diff --git a/src/BowPad.cpp b/src/BowPad.cpp index 01af57a4..22717d6a 100644 --- a/src/BowPad.cpp +++ b/src/BowPad.cpp @@ -1,6 +1,6 @@ // This file is part of BowPad. // -// Copyright (C) 2013-2018, 2020-2023 - Stefan Kueng +// Copyright (C) 2013-2018, 2020-2024 - Stefan Kueng // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -364,7 +364,7 @@ static void ParseCommandLine(CCmdLineParser& parser, CMainWindow* mainWindow) if (parser.HasKey(L"tabmove") && parser.HasKey(L"savepath")) { std::wstring title = parser.HasVal(L"title") ? parser.GetVal(L"title") : L""; - mainWindow->SetTabMove(parser.GetVal(L"path"), parser.GetVal(L"savepath"), !!parser.HasKey(L"modified"), static_cast(line), title); + mainWindow->SetTabMove(parser.GetVal(L"path"), parser.GetVal(L"savepath"), !!parser.HasKey(L"modified"), static_cast(line), title, parser.GetVal(L"posinfopath")); mainWindow->SetFileOpenMRU(false); } } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index b331593f..f5cb35f1 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1,6 +1,6 @@ // This file is part of BowPad. // -// Copyright (C) 2013-2023 - Stefan Kueng +// Copyright (C) 2013-2024 - Stefan Kueng // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -54,6 +54,7 @@ #include #include #include +#include IUIFramework* g_pFramework = nullptr; // Reference to the Ribbon framework. @@ -1845,10 +1846,11 @@ void CMainWindow::HandleAfterInit() if (!m_tabMovePath.empty()) { - TabMove(m_tabMovePath, m_tabMoveSavePath, m_tabMoveMod, m_initLine, m_tabMoveTitle); + TabMove(m_tabMovePath, m_tabMoveSavePath, m_tabMoveMod, m_initLine, m_tabMoveTitle, m_tabMovePosInfoPath); m_tabMovePath.clear(); m_tabMoveSavePath.clear(); m_tabMoveTitle.clear(); + m_tabMovePosInfoPath.clear(); } EnsureAtLeastOneTab(); @@ -2059,7 +2061,7 @@ bool CMainWindow::SaveDoc(DocID docID, const std::wstring& path) // Happens when the user drags a tab out and drops it over a BowPad window. // path is the temporary file that contains the latest document. // savePath is the file we want to save the temporary file over and then use. -void CMainWindow::TabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title) +void CMainWindow::TabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title, const std::wstring& posInfoPath) { std::wstring filepath = CPathUtils::GetLongPathname(path); @@ -2095,19 +2097,70 @@ void CMainWindow::TabMove(const std::wstring& path, const std::wstring& savePath GoToLine(line); + if (!posInfoPath.empty()) + { + std::wifstream file(posInfoPath); + if (file.is_open()) + { + std::wstring posString; + file >> posString; + file.close(); + + std::vector dataVec; + stringtok(dataVec, posString, false, L"*"); + + size_t vecIdx = 0; + CPosData pos; + pos.m_nFirstVisibleLine = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nWrapLineOffset = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nStartPos = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nEndPos = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_xOffset = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nSelMode = static_cast(_wtoi(dataVec[vecIdx++].c_str())); + pos.m_nScrollWidth = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_lastStyleLine = _wtoi(dataVec[vecIdx++].c_str()); + size_t nLineStates = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_lineStateVector.resize(nLineStates); + for (size_t i = 0; i < nLineStates; ++i) + { + pos.m_lineStateVector.push_back(_wtoi(dataVec[vecIdx++].c_str())); + } + size_t nUndoActions = _wtoi(dataVec[vecIdx++].c_str()); + if (nUndoActions) + { + pos.m_undoData.m_currentAction = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_undoData.m_savePoint = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_undoData.m_tentative = _wtoi(dataVec[vecIdx++].c_str()); + for (size_t i = 0; i < nUndoActions; ++i) + { + CUndoAction a; + a.m_type = _wtoi(dataVec[vecIdx++].c_str()); + a.m_position = _wtoi(dataVec[vecIdx++].c_str()); + a.m_text = CStringUtils::base64_decode(CUnicodeUtils::StdGetUTF8(dataVec[vecIdx++])); + pos.m_undoData.m_actions.push_back(a); + } + } + if (vecIdx == dataVec.size()) + { + m_editor.RestoreCurrentPos(pos); + } + } + } + m_fileTree.SetPath(CPathUtils::GetParentDirectory(savePath), false); ResizeChildWindows(); DeleteFile(filepath.c_str()); } -void CMainWindow::SetTabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title) +void CMainWindow::SetTabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title, const std::wstring& posInfoPath) { - m_tabMovePath = path; - m_tabMoveSavePath = savePath; - m_tabMoveMod = bMod; - m_initLine = line; - m_tabMoveTitle = title; + m_tabMovePath = path; + m_tabMoveSavePath = savePath; + m_tabMoveMod = bMod; + m_initLine = line; + m_tabMoveTitle = title; + m_tabMovePosInfoPath = posInfoPath; } void CMainWindow::SetElevatedSave(const std::wstring& path, const std::wstring& savePath, long line) @@ -3814,15 +3867,15 @@ bool CMainWindow::OpenFileAs(const std::wstring& tempPath, const std::wstring& r return false; } - std::wstring filepath = CPathUtils::GetLongPathname(tempPath); + std::wstring filepath = CPathUtils::GetLongPathname(tempPath); // Get the id for the document we just loaded, // it'll currently have a temporary name. - auto docID = m_docManager.GetIdForPath(filepath); - auto& doc = m_docManager.GetModDocumentFromID(docID); - doc.m_path = CPathUtils::GetLongPathname(realpath); - doc.m_bIsDirty = bModified; - doc.m_bNeedsSaving = bModified; + auto docID = m_docManager.GetIdForPath(filepath); + auto& doc = m_docManager.GetModDocumentFromID(docID); + doc.m_path = CPathUtils::GetLongPathname(realpath); + doc.m_bIsDirty = bModified; + doc.m_bNeedsSaving = bModified; m_docManager.UpdateFileTime(doc, true); std::wstring sFileName = CPathUtils::GetFileName(doc.m_path); const auto& lang = CLexStyles::Instance().GetLanguageForDocument(doc, m_scratchEditor); @@ -4104,19 +4157,66 @@ bool CMainWindow::HandleCopyDataMoveTab(const COPYDATASTRUCT& cds) std::wstring paths = std::wstring(static_cast(cds.lpData), cds.cbData / sizeof(wchar_t)); std::vector dataVec; stringtok(dataVec, paths, false, L"*"); - // Be a little untrusting of the clipboard data and if it's - // a mistake by the caller let them know so they - // don't throw away something we can't open. - if (dataVec.size() != 4) + if (dataVec.size() < 11) { - APPVERIFY(false); // Assume a bug if testing. + APPVERIFY(false); return false; } - std::wstring realPath = dataVec[0]; - std::wstring tempPath = dataVec[1]; - bool bMod = _wtoi(dataVec[2].c_str()) != 0; - int line = _wtoi(dataVec[3].c_str()); +#define DATAVECCHECK \ + if (dataVec.size() <= vecIdx) \ + { \ + APPVERIFY(false); \ + return false; \ + } + + size_t vecIdx = 0; + std::wstring realPath = dataVec[vecIdx++]; + std::wstring tempPath = dataVec[vecIdx++]; + bool bMod = _wtoi(dataVec[vecIdx++].c_str()) != 0; + + CPosData pos; + pos.m_nFirstVisibleLine = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nWrapLineOffset = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nStartPos = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nEndPos = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_xOffset = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_nSelMode = static_cast(_wtoi(dataVec[vecIdx++].c_str())); + pos.m_nScrollWidth = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_lastStyleLine = _wtoi(dataVec[vecIdx++].c_str()); + size_t nLineStates = _wtoi(dataVec[vecIdx++].c_str()); + pos.m_lineStateVector.resize(nLineStates); + for (size_t i = 0; i < nLineStates; ++i) + { + DATAVECCHECK; + pos.m_lineStateVector.push_back(_wtoi(dataVec[vecIdx++].c_str())); + } + size_t nUndoActions = _wtoi(dataVec[vecIdx++].c_str()); + if (nUndoActions) + { + DATAVECCHECK; + pos.m_undoData.m_currentAction = _wtoi(dataVec[vecIdx++].c_str()); + DATAVECCHECK; + pos.m_undoData.m_savePoint = _wtoi(dataVec[vecIdx++].c_str()); + DATAVECCHECK; + pos.m_undoData.m_tentative = _wtoi(dataVec[vecIdx++].c_str()); + for (size_t i = 0; i < nUndoActions; ++i) + { + CUndoAction a; + DATAVECCHECK; + a.m_type = _wtoi(dataVec[vecIdx++].c_str()); + DATAVECCHECK; + a.m_position = _wtoi(dataVec[vecIdx++].c_str()); + DATAVECCHECK; + a.m_text = CStringUtils::base64_decode(CUnicodeUtils::StdGetUTF8(dataVec[vecIdx++])); + pos.m_undoData.m_actions.push_back(a); + } + } + if (vecIdx != dataVec.size()) + { + APPVERIFY(false); + return false; + } // Unsaved files / New tabs have an empty real path. if (!realPath.empty()) // If this is a saved file @@ -4138,7 +4238,9 @@ bool CMainWindow::HandleCopyDataMoveTab(const COPYDATASTRUCT& cds) } bool opened = OpenFileAs(tempPath, realPath, bMod); if (opened) - GoToLine(line); + { + m_editor.RestoreCurrentPos(pos); + } return opened; } @@ -4183,10 +4285,43 @@ void CMainWindow::HandleTabDroppedOutside(int tab, POINT pt) // Start a new instance of BowPad with this dropped tab, or add this tab to // the BowPad window the drop was done on. Then close this tab. // First save the file to a temp location to ensure all unsaved mods are saved. - std::wstring tempPath = CTempFiles::Instance().GetTempFilePath(true); - auto docID = m_tabBar.GetIDFromIndex(tab); - auto& doc = m_docManager.GetModDocumentFromID(docID); - HWND hDroppedWnd = WindowFromPoint(pt); + std::wstring tempPath = CTempFiles::Instance().GetTempFilePath(true); + auto docID = m_tabBar.GetIDFromIndex(tab); + auto& doc = m_docManager.GetModDocumentFromID(docID); + HWND hDroppedWnd = WindowFromPoint(pt); + + auto getPosString = [&]() { + CPosData pos; + m_editor.SaveCurrentPos(pos); + std::wstring posString; + + posString += std::to_wstring(pos.m_nFirstVisibleLine) + L"*"; + posString += std::to_wstring(pos.m_nWrapLineOffset) + L"*"; + posString += std::to_wstring(pos.m_nStartPos) + L"*"; + posString += std::to_wstring(pos.m_nEndPos) + L"*"; + posString += std::to_wstring(pos.m_xOffset) + L"*"; + posString += std::to_wstring(static_cast(pos.m_nSelMode)) + L"*"; + posString += std::to_wstring(pos.m_nScrollWidth) + L"*"; + posString += std::to_wstring(pos.m_lastStyleLine) + L"*"; + posString += std::to_wstring(pos.m_lineStateVector.size()) + L"*"; + for (size_t i = 0; i < pos.m_lineStateVector.size(); ++i) + posString += std::to_wstring(pos.m_lineStateVector[i]) + L"*"; + posString += std::to_wstring(pos.m_undoData.m_actions.size()) + L"*"; + if (!pos.m_undoData.m_actions.empty()) + { + posString += std::to_wstring(pos.m_undoData.m_currentAction) + L"*"; + posString += std::to_wstring(pos.m_undoData.m_savePoint) + L"*"; + posString += std::to_wstring(pos.m_undoData.m_tentative) + L"*"; + for (size_t i = 0; i < pos.m_undoData.m_actions.size(); ++i) + { + posString += std::to_wstring(pos.m_undoData.m_actions[i].m_type) + L"*"; + posString += std::to_wstring(pos.m_undoData.m_actions[i].m_position) + L"*"; + posString += CUnicodeUtils::StdGetUnicode(CStringUtils::base64_encode((pos.m_undoData.m_actions[i].m_text))) + L"*"; + } + } + return posString; + }; + if (hDroppedWnd) { // If the drop target identifies a specific instance of BowPad found @@ -4211,7 +4346,8 @@ void CMainWindow::HandleTabDroppedOutside(int tab, POINT pt) cpd.dwData = CD_COMMAND_MOVETAB; std::wstring cpData = doc.m_path + L"*" + tempPath + L"*"; cpData += bModified ? L"1*" : L"0*"; - cpData += std::to_wstring(m_editor.GetCurrentLineNumber() + 1); + cpData += getPosString(); + cpd.lpData = static_cast(const_cast(cpData.c_str())); cpd.cbData = static_cast(cpData.size() * sizeof(wchar_t)); @@ -4282,12 +4418,17 @@ void CMainWindow::HandleTabDroppedOutside(int tab, POINT pt) // before we start the new process! CCommandHandler::Instance().OnDocumentClose(docID); + auto posTempPath = CTempFiles::Instance().GetTempFilePath(true); + std::wofstream ofs(posTempPath); + ofs << getPosString(); + ofs.close(); + // Start a new instance and open the tab there. std::wstring modPath = CPathUtils::GetModulePath(); - std::wstring cmdLine = CStringUtils::Format(L"/multiple /tabmove /savepath:\"%s\" /path:\"%s\" /line:%Id /title:\"%s\"", + std::wstring cmdLine = CStringUtils::Format(L"/multiple /tabmove /savepath:\"%s\" /path:\"%s\" /line:%Id /title:\"%s\" /posinfopath:\"%s\"", doc.m_path.c_str(), tempPath.c_str(), m_editor.GetCurrentLineNumber() + 1, - m_tabBar.GetTitle(tab).c_str()); + m_tabBar.GetTitle(tab).c_str(), posTempPath.c_str()); if (bModified) cmdLine += L" /modified"; SHELLEXECUTEINFO shExecInfo = {}; diff --git a/src/MainWindow.h b/src/MainWindow.h index 1813ee19..d283b3ba 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -1,6 +1,6 @@ // This file is part of BowPad. // -// Copyright (C) 2013-2018, 2020-2023 - Stefan Kueng +// Copyright (C) 2013-2018, 2020-2024 - Stefan Kueng // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -91,8 +91,8 @@ class CMainWindow : public CWindow void SetFileOpenMRU(bool bUseMRU) { m_bPathsToOpenMRU = bUseMRU; } void SetElevatedSave(const std::wstring& path, const std::wstring& savePath, long line); void ElevatedSave(const std::wstring& path, const std::wstring& savePath, long line); - void TabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title); - void SetTabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title); + void TabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title, const std::wstring& posInfoPath); + void SetTabMove(const std::wstring& path, const std::wstring& savePath, bool bMod, long line, const std::wstring& title, const std::wstring& posInfoPath); void SetInsertionIndex(int index) { m_insertionIndex = index; } std::wstring GetNewTabName(); void ShowFileTree(bool bShow); @@ -233,6 +233,7 @@ class CMainWindow : public CWindow std::wstring m_tabMovePath; std::wstring m_tabMoveSavePath; std::wstring m_tabMoveTitle; + std::wstring m_tabMovePosInfoPath; bool m_tabMoveMod; bool m_bIgnoreFileChanges; long m_initLine;