From fc051a1231065731205ff5e315684ce3bfb19033 Mon Sep 17 00:00:00 2001 From: Don Ho Date: Tue, 24 Sep 2024 20:00:34 +0200 Subject: [PATCH] Add tab created time tooltip for new opened untitled tab Note: while create the new tab (empty & clean), there will be a created time displayed. When the document is modified and period backup feature is enabled, a new created time will be assigned and displayed. However, the time of the first modification which makes empty document dirty will be remained as the tab creation time, even with several modification afterward. Fix #15563, close #15651 --- PowerEditor/src/Notepad_plus.h | 2 +- PowerEditor/src/NppBigSwitch.cpp | 2 +- PowerEditor/src/NppIO.cpp | 88 ++++++++++--------- PowerEditor/src/NppNotification.cpp | 15 +++- PowerEditor/src/ScintillaComponent/Buffer.cpp | 77 +++++++++------- PowerEditor/src/ScintillaComponent/Buffer.h | 17 ++++ 6 files changed, 124 insertions(+), 77 deletions(-) diff --git a/PowerEditor/src/Notepad_plus.h b/PowerEditor/src/Notepad_plus.h index 3d20484bd..0ad06397c 100644 --- a/PowerEditor/src/Notepad_plus.h +++ b/PowerEditor/src/Notepad_plus.h @@ -190,7 +190,7 @@ public: bool fileSaveAs(BufferID id = BUFFER_INVALID, bool isSaveCopy = false); bool fileDelete(BufferID id = BUFFER_INVALID); bool fileRename(BufferID id = BUFFER_INVALID); - bool fileRenameUntitled(BufferID id, const wchar_t* tabNewName); + bool fileRenameUntitledPluginAPI(BufferID id, const wchar_t* tabNewName); bool switchToFile(BufferID buffer); //find buffer in active view then in other view. //@} diff --git a/PowerEditor/src/NppBigSwitch.cpp b/PowerEditor/src/NppBigSwitch.cpp index 375ee79ab..4af757433 100644 --- a/PowerEditor/src/NppBigSwitch.cpp +++ b/PowerEditor/src/NppBigSwitch.cpp @@ -3150,7 +3150,7 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa case NPPM_SETUNTITLEDNAME: { if (!wParam || !lParam) return FALSE; - return fileRenameUntitled(reinterpret_cast(wParam), reinterpret_cast(lParam)); + return fileRenameUntitledPluginAPI(reinterpret_cast(wParam), reinterpret_cast(lParam)); } case NPPM_GETBOOKMARKID: diff --git a/PowerEditor/src/NppIO.cpp b/PowerEditor/src/NppIO.cpp index c296393d9..689b47615 100644 --- a/PowerEditor/src/NppIO.cpp +++ b/PowerEditor/src/NppIO.cpp @@ -1949,7 +1949,8 @@ bool Notepad_plus::fileRename(BufferID id) scnN.nmhdr.idFrom = (uptr_t)bufferID; bool success = false; - bool isFileExisting = doesFileExist(buf->getFullPathName()); + wstring oldFileNamePath = buf->getFullPathName(); + bool isFileExisting = doesFileExist(oldFileNamePath.c_str()); if (isFileExisting) { CustomFileDialog fDlg(_pPublicInterface->getHSelf()); @@ -1977,16 +1978,16 @@ bool Notepad_plus::fileRename(BufferID id) // We are just going to rename the tab nothing else // So just rename the tab and rename the backup file too if applicable - std::wstring staticName = _nativeLangSpeaker.getLocalizedStrFromID("tabrename-newname", L"New name"); + wstring staticName = _nativeLangSpeaker.getLocalizedStrFromID("tabrename-newname", L"New name"); StringDlg strDlg; - std::wstring title = _nativeLangSpeaker.getLocalizedStrFromID("tabrename-title", L"Rename Current Tab"); + wstring title = _nativeLangSpeaker.getLocalizedStrFromID("tabrename-title", L"Rename Current Tab"); strDlg.init(_pPublicInterface->getHinst(), _pPublicInterface->getHSelf(), title.c_str(), staticName.c_str(), buf->getFileName(), langNameLenMax - 1, filenameReservedChars.c_str(), true); wchar_t *tabNewName = reinterpret_cast(strDlg.doDialog()); if (tabNewName) { - std::wstring tabNewNameStr = tabNewName; + wstring tabNewNameStr = tabNewName; trim(tabNewNameStr); // No leading and tailing space allowed BufferID sameNamedBufferId = _pDocTab->findBufferByName(tabNewNameStr.c_str()); @@ -2023,18 +2024,21 @@ bool Notepad_plus::fileRename(BufferID id) bool isSnapshotMode = NppParameters::getInstance().getNppGUI().isSnapshotMode(); if (isSnapshotMode) { - std::wstring oldBackUpFile = buf->getBackupFileName(); + wstring oldBackUpFileName = buf->getBackupFileName(); + if (oldBackUpFileName.empty()) + return success; - // Change the backup file name and let MainFileManager decide the new filename - buf->setBackupFileName(L""); + wstring newBackUpFileName = oldBackUpFileName; - // Create new backup - buf->setModifiedStatus(true); - bool bRes = MainFileManager.backupCurrentBuffer(); + size_t index = newBackUpFileName.find_last_of(oldFileNamePath) - oldFileNamePath.length() + 1; + newBackUpFileName.replace(index, oldFileNamePath.length(), tabNewNameStr); - // Delete old backup - if (bRes) - ::DeleteFile(oldBackUpFile.c_str()); + if (doesFileExist(newBackUpFileName.c_str())) + ::ReplaceFile(newBackUpFileName.c_str(), oldBackUpFileName.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS, 0, 0); + else + ::MoveFileEx(oldBackUpFileName.c_str(), newBackUpFileName.c_str(), MOVEFILE_REPLACE_EXISTING); + + buf->setBackupFileName(newBackUpFileName); } } } @@ -2043,17 +2047,17 @@ bool Notepad_plus::fileRename(BufferID id) return success; } -bool Notepad_plus::fileRenameUntitled(BufferID id, const wchar_t* tabNewName) +bool Notepad_plus::fileRenameUntitledPluginAPI(BufferID id, const wchar_t* tabNewName) { BufferID bufferID = id; if (id == BUFFER_INVALID) { bufferID = _pEditView->getCurrentBufferID(); } + Buffer* buf = MainFileManager.getBufferByID(bufferID); - bool isFileExisting = doesFileExist(buf->getFullPathName()); - if (isFileExisting) return false; + if (!buf->isUntitled()) return false; // We are just going to rename the tab nothing else // So just rename the tab and rename the backup file too if applicable @@ -2076,37 +2080,39 @@ bool Notepad_plus::fileRenameUntitled(BufferID id, const wchar_t* tabNewName) sameNamedBufferId = _pNonDocTab->findBufferByName(tabNewNameStr.c_str()); } - if (sameNamedBufferId == BUFFER_INVALID) - { - SCNotification scnN{}; - scnN.nmhdr.code = NPPN_FILEBEFORERENAME; - scnN.nmhdr.hwndFrom = _pPublicInterface->getHSelf(); - scnN.nmhdr.idFrom = (uptr_t)bufferID; - _pluginsManager.notify(&scnN); + if (sameNamedBufferId != BUFFER_INVALID) return false; - buf->setFileName(tabNewNameStr.c_str()); - scnN.nmhdr.code = NPPN_FILERENAMED; - _pluginsManager.notify(&scnN); + SCNotification scnN{}; + scnN.nmhdr.code = NPPN_FILEBEFORERENAME; + scnN.nmhdr.hwndFrom = _pPublicInterface->getHSelf(); + scnN.nmhdr.idFrom = (uptr_t)bufferID; + _pluginsManager.notify(&scnN); - bool isSnapshotMode = NppParameters::getInstance().getNppGUI().isSnapshotMode(); - if (isSnapshotMode) - { - std::wstring oldBackUpFile = buf->getBackupFileName(); + buf->setFileName(tabNewNameStr.c_str()); + + scnN.nmhdr.code = NPPN_FILERENAMED; + _pluginsManager.notify(&scnN); + + bool isSnapshotMode = NppParameters::getInstance().getNppGUI().isSnapshotMode(); + if (isSnapshotMode) + { + wstring oldName = buf->getFullPathName(); + wstring oldBackUpFileName = buf->getBackupFileName(); + if (oldBackUpFileName.empty()) + return false; - // Change the backup file name and let MainFileManager decide the new filename - buf->setBackupFileName(L""); + wstring newBackUpFileName = oldBackUpFileName; - // Create new backup - buf->setModifiedStatus(true); - bool bRes = MainFileManager.backupCurrentBuffer(); + size_t index = newBackUpFileName.find_last_of(oldName) - oldName.length() + 1; + newBackUpFileName.replace(index, oldName.length(), tabNewNameStr); - // Delete old backup - if (bRes) - { - ::DeleteFile(oldBackUpFile.c_str()); - } - } + if (doesFileExist(newBackUpFileName.c_str())) + ::ReplaceFile(newBackUpFileName.c_str(), oldBackUpFileName.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS, 0, 0); + else + ::MoveFileEx(oldBackUpFileName.c_str(), newBackUpFileName.c_str(), MOVEFILE_REPLACE_EXISTING); + + buf->setBackupFileName(newBackUpFileName); } return true; diff --git a/PowerEditor/src/NppNotification.cpp b/PowerEditor/src/NppNotification.cpp index af855b978..50b5374fe 100644 --- a/PowerEditor/src/NppNotification.cpp +++ b/PowerEditor/src/NppNotification.cpp @@ -995,14 +995,27 @@ BOOL Notepad_plus::notify(SCNotification *notification) else return FALSE; - Buffer * buf = MainFileManager.getBufferByID(idd); + Buffer* buf = MainFileManager.getBufferByID(idd); if (buf == nullptr) return FALSE; tipTmp = buf->getFullPathName(); + wstring tabCreatedTime = buf->tabCreatedTimeString(); + if (!tabCreatedTime.empty()) + { + tipTmp += L"\r"; + tipTmp += tabCreatedTime; + SendMessage(lpttt->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, 200); + } + else + { + SendMessage(lpttt->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, -1); + } + if (tipTmp.length() >= tipMaxLen) return FALSE; + wcscpy_s(docTip, tipTmp.c_str()); lpttt->lpszText = docTip; return TRUE; diff --git a/PowerEditor/src/ScintillaComponent/Buffer.cpp b/PowerEditor/src/ScintillaComponent/Buffer.cpp index e3fee87d7..447414301 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.cpp +++ b/PowerEditor/src/ScintillaComponent/Buffer.cpp @@ -398,46 +398,51 @@ int64_t Buffer::getFileLength() const return -1; } - -wstring Buffer::getFileTime(fileTimeType ftt) const +wstring Buffer::getTimeString(FILETIME rawtime) const { wstring result; + SYSTEMTIME utcSystemTime, localSystemTime; + FileTimeToSystemTime(&rawtime, &utcSystemTime); + SystemTimeToTzSpecificLocalTime(nullptr, &utcSystemTime, &localSystemTime); - if (_currentStatus != DOC_UNNAMED) - { - WIN32_FILE_ATTRIBUTE_DATA attributes{}; - if (GetFileAttributesEx(_fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) - { - FILETIME rawtime; - switch (ftt) - { - case ft_created: - rawtime = attributes.ftCreationTime; - break; - case ft_modified: - rawtime = attributes.ftLastWriteTime; - break; - default: - rawtime = attributes.ftLastAccessTime; - break; - } + const size_t dateTimeStrLen = 256; + wchar_t bufDate[dateTimeStrLen] = { '\0' }; + GetDateFormat(LOCALE_USER_DEFAULT, 0, &localSystemTime, nullptr, bufDate, dateTimeStrLen); + result += bufDate; + result += ' '; - SYSTEMTIME utcSystemTime, localSystemTime; - FileTimeToSystemTime(&rawtime, &utcSystemTime); - SystemTimeToTzSpecificLocalTime(nullptr, &utcSystemTime, &localSystemTime); + wchar_t bufTime[dateTimeStrLen] = { '\0' }; + GetTimeFormat(LOCALE_USER_DEFAULT, 0, &localSystemTime, nullptr, bufTime, dateTimeStrLen); + result += bufTime; - const size_t dateTimeStrLen = 256; - wchar_t bufDate[dateTimeStrLen] = {'\0'}; - GetDateFormat(LOCALE_USER_DEFAULT, 0, &localSystemTime, nullptr, bufDate, dateTimeStrLen); - result += bufDate; - result += ' '; + return result; +} + +wstring Buffer::getFileTime(fileTimeType ftt) const +{ + wstring filePath; - wchar_t bufTime[dateTimeStrLen] = {'\0'}; - GetTimeFormat(LOCALE_USER_DEFAULT, 0, &localSystemTime, nullptr, bufTime, dateTimeStrLen); - result += bufTime; + WIN32_FILE_ATTRIBUTE_DATA attributes{}; + if (GetFileAttributesEx(_currentStatus == DOC_UNNAMED ? _backupFileName.c_str() : _fullPathName.c_str(), GetFileExInfoStandard, &attributes) != 0) + { + FILETIME rawtime; + switch (ftt) + { + case ft_created: + rawtime = attributes.ftCreationTime; + break; + case ft_modified: + rawtime = attributes.ftLastWriteTime; + break; + default: + rawtime = attributes.ftLastAccessTime; + break; } + + return getTimeString(rawtime); } - return result; + + return L""; } @@ -779,7 +784,10 @@ BufferID FileManager::loadFile(const wchar_t* filename, Document doc, int encodi { newBuf->_backupFileName = backupFileName; if (!doesFileExist(fullpath)) + { newBuf->_currentStatus = DOC_UNNAMED; + newBuf->setTabCreatedTimeStringFromBakFile(); + } } const FILETIME zeroTimeStamp = {}; @@ -1085,7 +1093,8 @@ bool FileManager::backupCurrentBuffer() ::MoveFileEx(fullpathTemp.c_str(), fullpath, MOVEFILE_REPLACE_EXISTING); } - buffer->setModifiedStatus(false); + buffer->setTabCreatedTimeStringFromBakFile(); + result = true; //all done } } @@ -1365,6 +1374,7 @@ BufferID FileManager::newEmptyDocument() BufferID id = newBuf; newBuf->_id = id; + newBuf->setTabCreatedTimeStringWithCurrentTime(); _buffers.push_back(newBuf); ++_nbBufs; ++_nextBufferID; @@ -1432,6 +1442,7 @@ BufferID FileManager::bufferFromDocument(Document doc, bool isMainEditZone) newBuf->_id = id; const NewDocDefaultSettings& ndds = (nppParamInst.getNppGUI()).getNewDocDefaultSettings(); newBuf->_lang = ndds._lang; + newBuf->setTabCreatedTimeStringWithCurrentTime(); _buffers.push_back(newBuf); ++_nbBufs; diff --git a/PowerEditor/src/ScintillaComponent/Buffer.h b/PowerEditor/src/ScintillaComponent/Buffer.h index b1a953e44..331c702b4 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.h +++ b/PowerEditor/src/ScintillaComponent/Buffer.h @@ -277,6 +277,20 @@ public: bool getNeedReload() const { return _needReloading; } void setNeedReload(bool reload) { _needReloading = reload; } + std::wstring tabCreatedTimeString() const { return _tabCreatedTimeString; } + void setTabCreatedTimeStringFromBakFile() { + if (_currentStatus == DOC_UNNAMED) + _tabCreatedTimeString = getFileTime(Buffer::ft_created); // while DOC_UNNAMED, getFileTime will retrieve time from backup file + } + void setTabCreatedTimeStringWithCurrentTime() { + if (_currentStatus == DOC_UNNAMED) + { + FILETIME now{}; + GetSystemTimeAsFileTime(&now); + _tabCreatedTimeString = getTimeString(now); + } + } + size_t docLength() const { assert(_pManager != nullptr); return _pManager->docLength(_id); @@ -286,6 +300,7 @@ public: enum fileTimeType { ft_created, ft_modified, ft_accessed }; std::wstring getFileTime(fileTimeType ftt) const; + std::wstring getTimeString(FILETIME rawtime) const; Lang * getCurrentLang() const; @@ -391,6 +406,8 @@ private: wchar_t * _fileName = nullptr; // points to filename part in _fullPathName bool _needReloading = false; // True if Buffer needs to be reloaded on activation + std::wstring _tabCreatedTimeString; + long _recentTag = -1; static long _recentTagCtr;