From ce58b424bdeec3616943f9ea8cd257f66700c3c0 Mon Sep 17 00:00:00 2001 From: Don Ho Date: Tue, 29 Oct 2024 01:18:13 +0100 Subject: [PATCH] Add Pin tab feature Fix #5786, close #15750 --- PowerEditor/installer/nativeLang/english.xml | 1 + PowerEditor/installer/nativeLang/french.xml | 1 + .../nativeLang/taiwaneseMandarin.xml | 4 +- PowerEditor/src/Notepad_plus.cpp | 17 +- PowerEditor/src/Notepad_plus.h | 1 + PowerEditor/src/Notepad_plus.rc | 9 + PowerEditor/src/NppBigSwitch.cpp | 53 +++ PowerEditor/src/NppCommands.cpp | 40 ++- PowerEditor/src/NppIO.cpp | 19 + PowerEditor/src/NppNotification.cpp | 32 +- PowerEditor/src/Parameters.cpp | 22 +- PowerEditor/src/Parameters.h | 38 +- PowerEditor/src/ScintillaComponent/Buffer.h | 4 + .../WinControls/ColourPicker/WordStyleDlg.rc | 12 +- .../src/WinControls/Preference/preference.rc | 56 ++- .../WinControls/Preference/preferenceDlg.cpp | 6 + .../WinControls/Preference/preference_rc.h | 2 +- PowerEditor/src/WinControls/TabBar/TabBar.cpp | 337 +++++++++++++++--- PowerEditor/src/WinControls/TabBar/TabBar.h | 52 ++- .../src/icons/dark/tabbar/pinTabButton.ico | Bin 0 -> 2809 bytes .../icons/dark/tabbar/pinTabButton_hover.ico | Bin 0 -> 1592 bytes .../icons/dark/tabbar/pinTabButton_pinned.ico | Bin 0 -> 1592 bytes .../dark/tabbar/pinTabButton_pinnedHover.ico | Bin 0 -> 2809 bytes .../icons/standard/tabbar/pinTabButton.ico | Bin 0 -> 1150 bytes .../standard/tabbar/pinTabButton_hover.ico | Bin 0 -> 1150 bytes .../standard/tabbar/pinTabButton_pinned.ico | Bin 0 -> 1150 bytes .../tabbar/pinTabButton_pinnedHover.ico | Bin 0 -> 1150 bytes PowerEditor/src/resource.h | 11 + 28 files changed, 592 insertions(+), 125 deletions(-) create mode 100644 PowerEditor/src/icons/dark/tabbar/pinTabButton.ico create mode 100644 PowerEditor/src/icons/dark/tabbar/pinTabButton_hover.ico create mode 100644 PowerEditor/src/icons/dark/tabbar/pinTabButton_pinned.ico create mode 100644 PowerEditor/src/icons/dark/tabbar/pinTabButton_pinnedHover.ico create mode 100644 PowerEditor/src/icons/standard/tabbar/pinTabButton.ico create mode 100644 PowerEditor/src/icons/standard/tabbar/pinTabButton_hover.ico create mode 100644 PowerEditor/src/icons/standard/tabbar/pinTabButton_pinned.ico create mode 100644 PowerEditor/src/icons/standard/tabbar/pinTabButton_pinnedHover.ico diff --git a/PowerEditor/installer/nativeLang/english.xml b/PowerEditor/installer/nativeLang/english.xml index 760310051..6a3deb8d1 100644 --- a/PowerEditor/installer/nativeLang/english.xml +++ b/PowerEditor/installer/nativeLang/english.xml @@ -958,6 +958,7 @@ Translation note: + diff --git a/PowerEditor/installer/nativeLang/french.xml b/PowerEditor/installer/nativeLang/french.xml index af7c235ef..4404be224 100644 --- a/PowerEditor/installer/nativeLang/french.xml +++ b/PowerEditor/installer/nativeLang/french.xml @@ -957,6 +957,7 @@ Translation note: + diff --git a/PowerEditor/installer/nativeLang/taiwaneseMandarin.xml b/PowerEditor/installer/nativeLang/taiwaneseMandarin.xml index 2f01e3343..c3a425c94 100644 --- a/PowerEditor/installer/nativeLang/taiwaneseMandarin.xml +++ b/PowerEditor/installer/nativeLang/taiwaneseMandarin.xml @@ -910,13 +910,15 @@ - + + + diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index a1e943c3b..919e4c0d0 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -405,6 +405,7 @@ LRESULT Notepad_plus::init(HWND hwnd) TabBarPlus::setDrawTopBar((tabBarStatus & TAB_DRAWTOPBAR) != 0, &_mainDocTab); TabBarPlus::setDrawInactiveTab((tabBarStatus & TAB_DRAWINACTIVETAB) != 0, &_mainDocTab); TabBarPlus::setDrawTabCloseButton((tabBarStatus & TAB_CLOSEBUTTON) != 0, &_mainDocTab); + TabBarPlus::setDrawTabPinButton((tabBarStatus & TAB_PINBUTTON) != 0, &_mainDocTab); TabBarPlus::setDbClk2Close((tabBarStatus & TAB_DBCLK2CLOSE) != 0); TabBarPlus::setVertical((tabBarStatus & TAB_VERTICAL) != 0); drawTabbarColoursFromStylerArray(); @@ -918,6 +919,7 @@ bool Notepad_plus::saveGUIParams() (TabBarPlus::drawInactiveTab() ? TAB_DRAWINACTIVETAB : 0) | \ (TabBarPlus::isReduced() ? TAB_REDUCE : 0) | \ (TabBarPlus::drawTabCloseButton() ? TAB_CLOSEBUTTON : 0) | \ + (TabBarPlus::drawTabPinButton() ? TAB_PINBUTTON : 0) | \ (TabBarPlus::isDbClk2Close() ? TAB_DBCLK2CLOSE : 0) | \ (TabBarPlus::isVertical() ? TAB_VERTICAL : 0) | \ (TabBarPlus::isMultiLine() ? TAB_MULTILINE : 0) |\ @@ -4909,6 +4911,8 @@ void Notepad_plus::docGotoAnotherEditView(FileTransferMode mode) //First put the doc in the other view if not present (if it is, activate it). //Then if needed close in the original tab BufferID current = _pEditView->getCurrentBufferID(); + Buffer* buf = MainFileManager.getBufferByID(current); + int viewToGo = otherView(); int indexFound = _pNonDocTab->getIndexByBuffer(current); if (indexFound != -1) //activate it @@ -4929,7 +4933,7 @@ void Notepad_plus::docGotoAnotherEditView(FileTransferMode mode) } loadBufferIntoView(current, viewToGo); - Buffer *buf = MainFileManager.getBufferByID(current); + _pEditView->saveCurrentPos(); //allow copying of position buf->setPosition(buf->getPosition(_pEditView), _pNonEditView); _pNonEditView->restoreCurrentPosPreStep(); //set position @@ -4948,7 +4952,6 @@ void Notepad_plus::docGotoAnotherEditView(FileTransferMode mode) //Close the document if we transfered the document instead of cloning it if (mode == TransferMove) { - Buffer *buf = MainFileManager.getBufferByID(current); monitoringWasOn = buf->isMonitoringOn(); //just close the activate document, since thats the one we moved (no search) @@ -4958,6 +4961,13 @@ void Notepad_plus::docGotoAnotherEditView(FileTransferMode mode) //Activate the other view since thats where the document went switchEditViewTo(viewToGo); + if (buf->isPinned()) + { + buf->setPinned(false); + _pDocTab->tabToStart(); + buf->setPinned(true); + } + if (monitoringWasOn) { command(IDM_VIEW_MONITORING); @@ -4965,7 +4975,6 @@ void Notepad_plus::docGotoAnotherEditView(FileTransferMode mode) if (_pDocumentListPanel != nullptr) { - Buffer* buf = MainFileManager.getBufferByID(current); _pDocumentListPanel->setItemColor(buf); } } @@ -6333,7 +6342,7 @@ void Notepad_plus::getCurrentOpenedFiles(Session & session, bool includUntitledD } const wchar_t* langName = languageName.c_str(); - sessionFileInfo sfi(buf->getFullPathName(), langName, buf->getEncoding(), buf->getUserReadOnly(), buf->getPosition(editView), buf->getBackupFileName().c_str(), buf->getLastModifiedTimestamp(), buf->getMapPosition()); + sessionFileInfo sfi(buf->getFullPathName(), langName, buf->getEncoding(), buf->getUserReadOnly(), buf->isPinned(), buf->getPosition(editView), buf->getBackupFileName().c_str(), buf->getLastModifiedTimestamp(), buf->getMapPosition()); sfi._isMonitoring = buf->isMonitoringOn(); sfi._individualTabColour = docTab[k]->getIndividualTabColourId(static_cast(i)); diff --git a/PowerEditor/src/Notepad_plus.h b/PowerEditor/src/Notepad_plus.h index 0ad06397c..2b26c49a3 100644 --- a/PowerEditor/src/Notepad_plus.h +++ b/PowerEditor/src/Notepad_plus.h @@ -192,6 +192,7 @@ public: bool fileRename(BufferID id = BUFFER_INVALID); bool fileRenameUntitledPluginAPI(BufferID id, const wchar_t* tabNewName); + void unPinnedForAllBuffers(); bool switchToFile(BufferID buffer); //find buffer in active view then in other view. //@} diff --git a/PowerEditor/src/Notepad_plus.rc b/PowerEditor/src/Notepad_plus.rc index 0347c78cd..12be73eb5 100644 --- a/PowerEditor/src/Notepad_plus.rc +++ b/PowerEditor/src/Notepad_plus.rc @@ -335,6 +335,15 @@ IDR_CLOSETAB_INACT_DM ICON "icons/dark/tabbar/closeTabButton_in IDR_CLOSETAB_HOVER_DM ICON "icons/dark/tabbar/closeTabButton_hover.ico" IDR_CLOSETAB_PUSH_DM ICON "icons/dark/tabbar/closeTabButton_push.ico" +IDR_PINTAB ICON "icons/standard/tabbar/pinTabButton.ico" +IDR_PINTAB_HOVER ICON "icons/standard/tabbar/pinTabButton_pinned.ico" +IDR_PINTAB_PINNED ICON "icons/standard/tabbar/pinTabButton_pinned.ico" +IDR_PINTAB_PINNEDHOVER ICON "icons/standard/tabbar/pinTabButton.ico" +IDR_PINTAB_DM ICON "icons/dark/tabbar/pinTabButton.ico" +IDR_PINTAB_HOVER_DM ICON "icons/dark/tabbar/pinTabButton_hover.ico" +IDR_PINTAB_PINNED_DM ICON "icons/dark/tabbar/pinTabButton_pinned.ico" +IDR_PINTAB_PINNEDHOVER_DM ICON "icons/dark/tabbar/pinTabButton_pinnedHover.ico" + IDR_DOCMAP_ICO ICON "icons/standard/panels/tabbar/docMap.ico" IDR_DOCMAP_ICO2 ICON "icons/light/panels/tabbar/docMap.ico" IDR_DOCMAP_ICO_DM ICON "icons/dark/panels/tabbar/docMap.ico" diff --git a/PowerEditor/src/NppBigSwitch.cpp b/PowerEditor/src/NppBigSwitch.cpp index a47fd2395..0d3cc1e1e 100644 --- a/PowerEditor/src/NppBigSwitch.cpp +++ b/PowerEditor/src/NppBigSwitch.cpp @@ -3693,6 +3693,8 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa _subDocTab.resizeIconsDpi(); _mainDocTab.setCloseBtnImageList(); _subDocTab.setCloseBtnImageList(); + _mainDocTab.setPinBtnImageList(); + _subDocTab.setPinBtnImageList(); ::SendMessage(_pPublicInterface->getHSelf(), WM_COMMAND, IDM_VIEW_REDUCETABBAR, 0); changeDocumentListIconSet(false); @@ -3779,6 +3781,57 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa return fileName.length(); } + case NPPM_INTERNAL_DRAWTABBARPINBOTTUN: + { + TabBarPlus::setDrawTabPinButton(!TabBarPlus::drawTabPinButton(), &_mainDocTab); + + bool drawTabPinButton = TabBarPlus::drawTabPinButton(); + bool drawTabCloseButton = TabBarPlus::drawTabCloseButton(); + + if (!drawTabPinButton) + { + unPinnedForAllBuffers(); + } + + if (drawTabCloseButton && drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(0); + _mainDocTab.setTabPinButtonOrder(1); + _subDocTab.setTabCloseButtonOrder(0); + _subDocTab.setTabPinButtonOrder(1); + } + else if (!drawTabCloseButton && drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(-1); + _mainDocTab.setTabPinButtonOrder(0); + _subDocTab.setTabCloseButtonOrder(-1); + _subDocTab.setTabPinButtonOrder(0); + } + else if (drawTabCloseButton && !drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(0); + _mainDocTab.setTabPinButtonOrder(-1); + _subDocTab.setTabCloseButtonOrder(0); + _subDocTab.setTabPinButtonOrder(-1); + } + else //if (!drawTabCloseButton && !drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(-1); + _mainDocTab.setTabPinButtonOrder(-1); + _subDocTab.setTabCloseButtonOrder(-1); + _subDocTab.setTabPinButtonOrder(-1); + } + + // This part is just for updating (redraw) the tabs + int tabDpiDynamicalHeight = _mainDocTab.dpiManager().scale(TabBarPlus::isReduced() ? g_TabHeight : g_TabHeightLarge); + int tabDpiDynamicalWidth = _mainDocTab.dpiManager().scale(TabBarPlus::drawTabPinButton() ? g_TabWidthCloseBtn : g_TabWidth); + TabCtrl_SetItemSize(_mainDocTab.getHSelf(), tabDpiDynamicalWidth, tabDpiDynamicalHeight); + TabCtrl_SetItemSize(_subDocTab.getHSelf(), tabDpiDynamicalWidth, tabDpiDynamicalHeight); + + ::SendMessage(_pPublicInterface->getHSelf(), WM_SIZE, 0, 0); + return TRUE; + } + default: { if (message == WDN_NOTIFY) diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index 342049bfc..38d71d0e8 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -2323,7 +2323,9 @@ void Notepad_plus::command(int id) case IDM_VIEW_REFRESHTABAR : { - ::SendMessage(_pPublicInterface->getHSelf(), WM_SIZE, 0, 0); + ::InvalidateRect(_mainDocTab.getHSelf(), NULL, TRUE); + ::InvalidateRect(_subDocTab.getHSelf(), NULL, TRUE); + break; } case IDM_VIEW_LOCKTABBAR: @@ -2349,6 +2351,38 @@ void Notepad_plus::command(int id) { TabBarPlus::setDrawTabCloseButton(!TabBarPlus::drawTabCloseButton(), &_mainDocTab); + bool drawTabPinButton = TabBarPlus::drawTabPinButton(); + bool drawTabCloseButton = TabBarPlus::drawTabCloseButton(); + + if (drawTabCloseButton && drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(0); + _mainDocTab.setTabPinButtonOrder(1); + _subDocTab.setTabCloseButtonOrder(0); + _subDocTab.setTabPinButtonOrder(1); + } + else if (!drawTabCloseButton && drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(-1); + _mainDocTab.setTabPinButtonOrder(0); + _subDocTab.setTabCloseButtonOrder(-1); + _subDocTab.setTabPinButtonOrder(0); + } + else if (drawTabCloseButton && !drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(0); + _mainDocTab.setTabPinButtonOrder(-1); + _subDocTab.setTabCloseButtonOrder(0); + _subDocTab.setTabPinButtonOrder(-1); + } + else //if (!drawTabCloseButton && !drawTabPinButton) + { + _mainDocTab.setTabCloseButtonOrder(-1); + _mainDocTab.setTabPinButtonOrder(-1); + _subDocTab.setTabCloseButtonOrder(-1); + _subDocTab.setTabPinButtonOrder(-1); + } + // This part is just for updating (redraw) the tabs int tabDpiDynamicalHeight = _mainDocTab.dpiManager().scale(TabBarPlus::isReduced() ? g_TabHeight : g_TabHeightLarge); int tabDpiDynamicalWidth = _mainDocTab.dpiManager().scale(TabBarPlus::drawTabCloseButton() ? g_TabWidthCloseBtn : g_TabWidth); @@ -3345,11 +3379,11 @@ void Notepad_plus::command(int id) } case IDM_VIEW_GOTO_START: - _pDocTab->currentTabToStart(); + _pDocTab->tabToStart(); break; case IDM_VIEW_GOTO_END: - _pDocTab->currentTabToEnd(); + _pDocTab->tabToEnd(); break; case IDM_VIEW_GOTO_ANOTHER_VIEW: diff --git a/PowerEditor/src/NppIO.cpp b/PowerEditor/src/NppIO.cpp index c893d9059..da669484b 100644 --- a/PowerEditor/src/NppIO.cpp +++ b/PowerEditor/src/NppIO.cpp @@ -1111,6 +1111,23 @@ bool Notepad_plus::fileClose(BufferID id, int curView) return true; } +void Notepad_plus::unPinnedForAllBuffers() +{ + for (size_t i = 0; i < _mainDocTab.nbItem(); ++i) + { + BufferID id = _mainDocTab.getBufferByIndex(i); + Buffer* buf = MainFileManager.getBufferByID(id); + buf->setPinned(false); + } + + for (size_t i = 0; i < _subDocTab.nbItem(); ++i) + { + BufferID id = _mainDocTab.getBufferByIndex(i); + Buffer* buf = MainFileManager.getBufferByID(id); + buf->setPinned(false); + } +} + bool Notepad_plus::fileCloseAll(bool doDeleteBackup, bool isSnapshotMode) { bool noSaveToAll = false; @@ -2407,6 +2424,7 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch buf->setEncoding(session._mainViewFiles[i]._encoding); buf->setUserReadOnly(session._mainViewFiles[i]._isUserReadOnly); + buf->setPinned(session._mainViewFiles[i]._isPinned); if (isSnapshotMode && !session._mainViewFiles[i]._backupFilePath.empty() && doesFileExist(session._mainViewFiles[i]._backupFilePath.c_str())) buf->setDirty(true); @@ -2539,6 +2557,7 @@ bool Notepad_plus::loadSession(Session & session, bool isSnapshotMode, const wch buf->setLangType(typeToSet, pLn); buf->setEncoding(session._subViewFiles[k]._encoding); buf->setUserReadOnly(session._subViewFiles[k]._isUserReadOnly); + buf->setPinned(session._subViewFiles[k]._isPinned); if (isSnapshotMode && !session._subViewFiles[k]._backupFilePath.empty() && doesFileExist(session._subViewFiles[k]._backupFilePath.c_str())) buf->setDirty(true); diff --git a/PowerEditor/src/NppNotification.cpp b/PowerEditor/src/NppNotification.cpp index 8503b6033..bd7bd1010 100644 --- a/PowerEditor/src/NppNotification.cpp +++ b/PowerEditor/src/NppNotification.cpp @@ -317,7 +317,7 @@ BOOL Notepad_plus::notify(SCNotification *notification) int index = tabNotification->_tabOrigin; BufferID bufferToClose = notifyDocTab->getBufferByIndex(index); Buffer * buf = MainFileManager.getBufferByID(bufferToClose); - int iView = isFromPrimary?MAIN_VIEW:SUB_VIEW; + int iView = isFromPrimary ? MAIN_VIEW : SUB_VIEW; if (buf->isDirty()) { activateBuffer(bufferToClose, iView); @@ -333,6 +333,36 @@ BOOL Notepad_plus::notify(SCNotification *notification) break; } + case TCN_TABPINNED: + { + int index = tabNotification->_tabOrigin; + BufferID bufferToBePinned = notifyDocTab->getBufferByIndex(index); + Buffer * buf = MainFileManager.getBufferByID(bufferToBePinned); + + bool isPinned = buf->isPinned(); + + if (_mainDocTab.getHSelf() == notification->nmhdr.hwndFrom) + { + if (!isPinned) + _mainDocTab.tabToStart(index); + else + _mainDocTab.tabToEnd(index); + } + else if (_subDocTab.getHSelf() == notification->nmhdr.hwndFrom) + { + if (!isPinned) + _subDocTab.tabToStart(index); + else + _subDocTab.tabToEnd(index); + } + else + return FALSE; + + buf->setPinned(!isPinned); + + break; + } + case TCN_SELCHANGE: { int iView = -1; diff --git a/PowerEditor/src/Parameters.cpp b/PowerEditor/src/Parameters.cpp index ff5d81bf9..5b65b9e33 100644 --- a/PowerEditor/src/Parameters.cpp +++ b/PowerEditor/src/Parameters.cpp @@ -2440,7 +2440,12 @@ bool NppParameters::getSessionFromXmlTree(TiXmlDocument *pSessionDoc, Session& s if (boolStrReadOnly) isUserReadOnly = _wcsicmp(L"yes", boolStrReadOnly) == 0; - sessionFileInfo sfi(fileName, langName, encStr ? encoding : -1, isUserReadOnly, position, backupFilePath, fileModifiedTimestamp, mapPosition); + bool isPinned = false; + const wchar_t* boolStrPinned = (childNode->ToElement())->Attribute(L"tabPinned"); + if (boolStrPinned) + isPinned = _wcsicmp(L"yes", boolStrPinned) == 0; + + sessionFileInfo sfi(fileName, langName, encStr ? encoding : -1, isUserReadOnly, isPinned, position, backupFilePath, fileModifiedTimestamp, mapPosition); const wchar_t* intStrTabColour = (childNode->ToElement())->Attribute(L"tabColourId"); if (intStrTabColour) @@ -3675,6 +3680,7 @@ void NppParameters::writeSession(const Session & session, const wchar_t *fileNam (fileNameNode->ToElement())->SetAttribute(L"originalFileLastModifTimestampHigh", static_cast(viewSessionFiles[i]._originalFileLastModifTimestamp.dwHighDateTime)); (fileNameNode->ToElement())->SetAttribute(L"tabColourId", static_cast(viewSessionFiles[i]._individualTabColour)); (fileNameNode->ToElement())->SetAttribute(L"RTL", viewSessionFiles[i]._isRTL ? L"yes" : L"no"); + (fileNameNode->ToElement())->SetAttribute(L"tabPinned", viewSessionFiles[i]._isPinned ? L"yes" : L"no"); // docMap (fileNameNode->ToElement())->SetAttribute(L"mapFirstVisibleDisplayLine", _i64tot(static_cast(viewSessionFiles[i]._mapPos._firstVisibleDisplayLine), szInt64, 10)); @@ -4908,6 +4914,17 @@ void NppParameters::feedGUIParameters(TiXmlNode *node) isFailed = true; } + val = element->Attribute(L"pinButton"); + if (val) + { + if (!lstrcmp(val, L"yes")) + _nppGUI._tabStatus |= TAB_PINBUTTON; + else if (!lstrcmp(val, L"no")) + _nppGUI._tabStatus |= 0; + else + isFailed = true; + } + val = element->Attribute(L"doubleClick2Close"); if (val) { @@ -7243,6 +7260,9 @@ void NppParameters::createXmlTreeFromGUIParams() pStr = (_nppGUI._tabStatus & TAB_CLOSEBUTTON) ? L"yes" : L"no"; GUIConfigElement->SetAttribute(L"closeButton", pStr); + pStr = (_nppGUI._tabStatus & TAB_PINBUTTON) ? L"yes" : L"no"; + GUIConfigElement->SetAttribute(L"pinButton", pStr); + pStr = (_nppGUI._tabStatus & TAB_DBCLK2CLOSE) ? L"yes" : L"no"; GUIConfigElement->SetAttribute(L"doubleClick2Close", pStr); diff --git a/PowerEditor/src/Parameters.h b/PowerEditor/src/Parameters.h index 12b31c869..c9cc0fcc5 100644 --- a/PowerEditor/src/Parameters.h +++ b/PowerEditor/src/Parameters.h @@ -64,17 +64,18 @@ const int UDD_DOCKED = 2; // 0000 0010 // 2 : 0000 0010 hide & docked // 3 : 0000 0011 show & docked -const int TAB_DRAWTOPBAR = 1; //0000 0000 0001 -const int TAB_DRAWINACTIVETAB = 2; //0000 0000 0010 -const int TAB_DRAGNDROP = 4; //0000 0000 0100 -const int TAB_REDUCE = 8; //0000 0000 1000 -const int TAB_CLOSEBUTTON = 16; //0000 0001 0000 -const int TAB_DBCLK2CLOSE = 32; //0000 0010 0000 -const int TAB_VERTICAL = 64; //0000 0100 0000 -const int TAB_MULTILINE = 128; //0000 1000 0000 -const int TAB_HIDE = 256; //0001 0000 0000 -const int TAB_QUITONEMPTY = 512; //0010 0000 0000 -const int TAB_ALTICONS = 1024; //0100 0000 0000 +const int TAB_DRAWTOPBAR = 1; //0000 0000 0001 +const int TAB_DRAWINACTIVETAB = 2; //0000 0000 0010 +const int TAB_DRAGNDROP = 4; //0000 0000 0100 +const int TAB_REDUCE = 8; //0000 0000 1000 +const int TAB_CLOSEBUTTON = 16; //0000 0001 0000 +const int TAB_DBCLK2CLOSE = 32; //0000 0010 0000 +const int TAB_VERTICAL = 64; //0000 0100 0000 +const int TAB_MULTILINE = 128; //0000 1000 0000 +const int TAB_HIDE = 256; //0001 0000 0000 +const int TAB_QUITONEMPTY = 512; //0010 0000 0000 +const int TAB_ALTICONS = 1024; //0100 0000 0000 +const int TAB_PINBUTTON = 2048; //1000 0000 0000 const bool activeText = true; const bool activeNumeric = false; @@ -216,8 +217,8 @@ public: struct sessionFileInfo : public Position { - sessionFileInfo(const wchar_t* fn, const wchar_t *ln, int encoding, bool userReadOnly, const Position& pos, const wchar_t *backupFilePath, FILETIME originalFileLastModifTimestamp, const MapPosition & mapPos) : - Position(pos), _encoding(encoding), _isUserReadOnly(userReadOnly), _originalFileLastModifTimestamp(originalFileLastModifTimestamp), _mapPos(mapPos) + sessionFileInfo(const wchar_t* fn, const wchar_t *ln, int encoding, bool userReadOnly,bool isPinned, const Position& pos, const wchar_t *backupFilePath, FILETIME originalFileLastModifTimestamp, const MapPosition & mapPos) : + Position(pos), _encoding(encoding), _isUserReadOnly(userReadOnly), _isPinned(isPinned), _originalFileLastModifTimestamp(originalFileLastModifTimestamp), _mapPos(mapPos) { if (fn) _fileName = fn; if (ln) _langName = ln; @@ -235,6 +236,7 @@ struct sessionFileInfo : public Position bool _isMonitoring = false; int _individualTabColour = -1; bool _isRTL = false; + bool _isPinned = false; std::wstring _backupFilePath; FILETIME _originalFileLastModifTimestamp {}; @@ -791,15 +793,7 @@ struct NppGUI final bool _statusBarShow = true; bool _menuBarShow = true; - // 1st bit : draw top bar; - // 2nd bit : draw inactive tabs - // 3rd bit : enable drag & drop - // 4th bit : reduce the height - // 5th bit : enable vertical - // 6th bit : enable multiline - - // 0:don't draw; 1:draw top bar 2:draw inactive tabs 3:draw both 7:draw both+drag&drop - int _tabStatus = (TAB_DRAWTOPBAR | TAB_DRAWINACTIVETAB | TAB_DRAGNDROP | TAB_REDUCE | TAB_CLOSEBUTTON); + int _tabStatus = (TAB_DRAWTOPBAR | TAB_DRAWINACTIVETAB | TAB_DRAGNDROP | TAB_REDUCE | TAB_CLOSEBUTTON | TAB_PINBUTTON); bool _splitterPos = POS_VERTICAL; int _userDefineDlgStatus = UDD_DOCKED; diff --git a/PowerEditor/src/ScintillaComponent/Buffer.h b/PowerEditor/src/ScintillaComponent/Buffer.h index cbb122f9b..e96ec3bec 100644 --- a/PowerEditor/src/ScintillaComponent/Buffer.h +++ b/PowerEditor/src/ScintillaComponent/Buffer.h @@ -362,6 +362,9 @@ public: bool isRTL() const { return _isRTL; }; void setRTL(bool isRTL) { _isRTL = isRTL; }; + bool isPinned() const { return _isPinned; }; + void setPinned(bool isPinned) { _isPinned = isPinned; }; + private: int indexOfReference(const ScintillaEditView * identifier) const; @@ -445,4 +448,5 @@ private: bool _isInaccessible = false; bool _isRTL = false; + bool _isPinned = false; }; diff --git a/PowerEditor/src/WinControls/ColourPicker/WordStyleDlg.rc b/PowerEditor/src/WinControls/ColourPicker/WordStyleDlg.rc index 441062816..13338964b 100644 --- a/PowerEditor/src/WinControls/ColourPicker/WordStyleDlg.rc +++ b/PowerEditor/src/WinControls/ColourPicker/WordStyleDlg.rc @@ -64,13 +64,13 @@ BEGIN LTEXT "User-defined keywords",IDC_USER_KEYWORDS_STATIC,350,135,126,8 EDITTEXT IDC_USER_KEYWORDS_EDIT,347,146,136,52,ES_MULTILINE | WS_VSCROLL - CONTROL "Force foreground color for all styles",IDC_GLOBAL_FG_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,191,134,132,10 - CONTROL "Force background color for all styles",IDC_GLOBAL_BG_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,191,149,132,10 + CONTROL "Force foreground color for all styles",IDC_GLOBAL_FG_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,191,134,153,10 + CONTROL "Force background color for all styles",IDC_GLOBAL_BG_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,191,149,153,10 CONTROL "Force font choice for all styles",IDC_GLOBAL_FONT_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,134,153,10 - CONTROL "Force font size choice for all styles",IDC_GLOBAL_FONTSIZE_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,149,135,10 - CONTROL "Force bold choice for all styles",IDC_GLOBAL_BOLD_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,164,135,10 - CONTROL "Force italic choice for all styles",IDC_GLOBAL_ITALIC_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,179,135,10 - CONTROL "Force underline choice for all styles",IDC_GLOBAL_UNDERLINE_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,194,135,10 + CONTROL "Force font size choice for all styles",IDC_GLOBAL_FONTSIZE_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,149, 153,10 + CONTROL "Force bold choice for all styles",IDC_GLOBAL_BOLD_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,164,153,10 + CONTROL "Force italic choice for all styles",IDC_GLOBAL_ITALIC_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,179,153,10 + CONTROL "Force underline choice for all styles",IDC_GLOBAL_UNDERLINE_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,327,194,153,10 LTEXT "Go to settings",IDC_GLOBAL_GOTOSETTINGS_LINK,210,153,131,10,WS_TABSTOP LTEXT "What is Global override?",IDC_GLOBAL_WHATISGLOBALOVERRIDE_LINK,191,173,132,10,WS_TABSTOP diff --git a/PowerEditor/src/WinControls/Preference/preference.rc b/PowerEditor/src/WinControls/Preference/preference.rc index 9ce09b03d..ef355800e 100644 --- a/PowerEditor/src/WinControls/Preference/preference.rc +++ b/PowerEditor/src/WinControls/Preference/preference.rc @@ -38,39 +38,35 @@ IDD_PREFERENCE_SUB_GENRAL DIALOGEX 115, 10, 460, 205 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN - GROUPBOX "Localization",IDC_LOCALIZATION_GB_STATIC,27,3,186,30,BS_CENTER + GROUPBOX "Localization",IDC_LOCALIZATION_GB_STATIC,27,0,186,36,BS_CENTER COMBOBOX IDC_COMBO_LOCALIZATION,60,14,125,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - - GROUPBOX "Status Bar",IDC_STATUSBAR_GB_STATIC,27,37,186,27,BS_CENTER - CONTROL "Hide",IDC_CHECK_HIDESTATUSBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,48,174,10 - - GROUPBOX "Toolbar",IDC_TOOLBAR_GB_STATIC,27,68,186,88,BS_CENTER - CONTROL "Hide",IDC_CHECK_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,76,100,10 - CONTROL "Fluent UI: small",IDC_RADIO_SMALLICON,"Button",BS_AUTORADIOBUTTON,33,91,174,10 - CONTROL "Fluent UI: large",IDC_RADIO_BIGICON,"Button",BS_AUTORADIOBUTTON,33,104,174,10 - CONTROL "Filled Fluent UI: small",IDC_RADIO_SMALLICON2,"Button",BS_AUTORADIOBUTTON,33,117,174,10 - CONTROL "Filled Fluent UI: large",IDC_RADIO_BIGICON2,"Button",BS_AUTORADIOBUTTON,33,130,174,10 - CONTROL "Standard icons: small",IDC_RADIO_STANDARD,"Button",BS_AUTORADIOBUTTON,33,143,174,10 - - GROUPBOX "Tab Bar",IDC_TABBAR_GB_STATIC,223,3,177,153,BS_CENTER - CONTROL "Hide",IDC_CHECK_TAB_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,11,100,10 - CONTROL "Multi-line",IDC_CHECK_TAB_MULTILINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,26,164,10 - CONTROL "Vertical",IDC_CHECK_TAB_VERTICAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,39,164,10 - CONTROL "Reduce",IDC_CHECK_REDUCE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,52,164,10 - CONTROL "Alternate icons",IDC_CHECK_TAB_ALTICONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,65,164,10 - CONTROL "Lock (no drag and drop)",IDC_CHECK_LOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,78,164,10 - CONTROL "Darken inactive tabs",IDC_CHECK_DRAWINACTIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,91,164,10 - CONTROL "Draw a coloured bar on active tab",IDC_CHECK_ORANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,104,164,10 - CONTROL "Show close button on each tab",IDC_CHECK_ENABLETABCLOSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,117,164,10 - CONTROL "Double click to close document",IDC_CHECK_DBCLICK2CLOSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,130,164,10 - CONTROL "Exit on close the last tab",IDC_CHECK_TAB_LAST_EXIT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,143,164,10 - - GROUPBOX "Menu",IDC_MENU_GB_STATIC,27,160,373,34,BS_CENTER - CONTROL "Hide menu bar (use Alt or F10 key to toggle)",IDC_CHECK_HIDEMENUBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,168,218,10 - CONTROL "Hide right shortcuts + ▼ ✕ from the menu bar (Need to restart Notepad++)",IDC_CHECK_HIDERIGHTSHORTCUTSOFMENUBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,181,350,10 + GROUPBOX "Status Bar",IDC_STATUSBAR_GB_STATIC,27,39,186,31,BS_CENTER + CONTROL "Hide",IDC_CHECK_HIDESTATUSBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,52,174,10 + GROUPBOX "Toolbar",IDC_TOOLBAR_GB_STATIC,27,75,186,91,BS_CENTER + CONTROL "Hide",IDC_CHECK_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,84,100,10 + CONTROL "Fluent UI: small",IDC_RADIO_SMALLICON,"Button",BS_AUTORADIOBUTTON,33,100,174,10 + CONTROL "Fluent UI: large",IDC_RADIO_BIGICON,"Button",BS_AUTORADIOBUTTON,33,113,174,10 + CONTROL "Filled Fluent UI: small",IDC_RADIO_SMALLICON2,"Button",BS_AUTORADIOBUTTON,33,126,174,10 + CONTROL "Filled Fluent UI: large",IDC_RADIO_BIGICON2,"Button",BS_AUTORADIOBUTTON,33,139,174,10 + CONTROL "Standard icons: small",IDC_RADIO_STANDARD,"Button",BS_AUTORADIOBUTTON,33,152,174,10 + GROUPBOX "Tab Bar",IDC_TABBAR_GB_STATIC,223,0,177,166,BS_CENTER + CONTROL "Hide",IDC_CHECK_TAB_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,8,100,10 + CONTROL "Multi-line",IDC_CHECK_TAB_MULTILINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,21,164,10 + CONTROL "Vertical",IDC_CHECK_TAB_VERTICAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,34,164,10 + CONTROL "Reduce",IDC_CHECK_REDUCE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,47,164,10 + CONTROL "Alternate icons",IDC_CHECK_TAB_ALTICONS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,60,164,10 + CONTROL "Lock (no drag and drop)",IDC_CHECK_LOCK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,73,164,10 + CONTROL "Darken inactive tabs",IDC_CHECK_DRAWINACTIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,86,164,10 + CONTROL "Draw a coloured bar on active tab",IDC_CHECK_ORANGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,99,164,10 + CONTROL "Show close button on each tab",IDC_CHECK_ENABLETABCLOSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,112,164,10 + CONTROL "Enable pin tab feature", IDC_CHECK_ENABLETABPIN,"Button", BS_AUTOCHECKBOX | WS_TABSTOP, 229, 125, 164, 10 + CONTROL "Double click to close document",IDC_CHECK_DBCLICK2CLOSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,138,164,10 + CONTROL "Exit on close the last tab",IDC_CHECK_TAB_LAST_EXIT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,151,164,10 + GROUPBOX "Menu",IDC_MENU_GB_STATIC,27,168,373,34,BS_CENTER + CONTROL "Hide menu bar (use Alt or F10 key to toggle)",IDC_CHECK_HIDEMENUBAR, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,176,218,10 + CONTROL "Hide right shortcuts + ? ? from the menu bar (Need to restart Notepad++)",IDC_CHECK_HIDERIGHTSHORTCUTSOFMENUBAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,189,350,10 END - IDD_PREFERENCE_SUB_EDITING DIALOGEX 115, 10, 460, 205 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD FONT 8, "MS Shell Dlg", 0, 0, 0x1 diff --git a/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp b/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp index ffd1bb21a..cedd39e8e 100644 --- a/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp +++ b/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp @@ -610,6 +610,7 @@ intptr_t CALLBACK GeneralSubDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM ::SendDlgItemMessage(_hSelf, IDC_CHECK_ORANGE, BM_SETCHECK, tabBarStatus & TAB_DRAWTOPBAR, 0); ::SendDlgItemMessage(_hSelf, IDC_CHECK_DRAWINACTIVE, BM_SETCHECK, tabBarStatus & TAB_DRAWINACTIVETAB, 0); ::SendDlgItemMessage(_hSelf, IDC_CHECK_ENABLETABCLOSE, BM_SETCHECK, tabBarStatus & TAB_CLOSEBUTTON, 0); + ::SendDlgItemMessage(_hSelf, IDC_CHECK_ENABLETABPIN, BM_SETCHECK, tabBarStatus & TAB_PINBUTTON, 0); ::SendDlgItemMessage(_hSelf, IDC_CHECK_DBCLICK2CLOSE, BM_SETCHECK, tabBarStatus & TAB_DBCLK2CLOSE, 0); ::SendDlgItemMessage(_hSelf, IDC_CHECK_TAB_VERTICAL, BM_SETCHECK, tabBarStatus & TAB_VERTICAL, 0); ::SendDlgItemMessage(_hSelf, IDC_CHECK_TAB_MULTILINE, BM_SETCHECK, tabBarStatus & TAB_MULTILINE, 0); @@ -699,6 +700,7 @@ intptr_t CALLBACK GeneralSubDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_ORANGE), !toBeHidden); ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_DRAWINACTIVE), !toBeHidden); ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_ENABLETABCLOSE), !toBeHidden); + ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_ENABLETABPIN), !toBeHidden); ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_DBCLICK2CLOSE), !toBeHidden); ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_TAB_LAST_EXIT), !toBeHidden); ::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_TAB_ALTICONS), !toBeHidden); @@ -757,6 +759,10 @@ intptr_t CALLBACK GeneralSubDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM ::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_DRAWTABBAR_CLOSEBOTTUN, 0); return TRUE; + case IDC_CHECK_ENABLETABPIN: + ::SendMessage(::GetParent(_hParent), NPPM_INTERNAL_DRAWTABBARPINBOTTUN, 0, 0); + return TRUE; + case IDC_CHECK_DBCLICK2CLOSE : ::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_DRAWTABBAR_DBCLK2CLOSE, 0); return TRUE; diff --git a/PowerEditor/src/WinControls/Preference/preference_rc.h b/PowerEditor/src/WinControls/Preference/preference_rc.h index e6f0d1353..0046dfcfe 100644 --- a/PowerEditor/src/WinControls/Preference/preference_rc.h +++ b/PowerEditor/src/WinControls/Preference/preference_rc.h @@ -40,7 +40,7 @@ #define IDC_CHECK_ENABLETABCLOSE (IDD_PREFERENCE_SUB_GENRAL + 12) #define IDC_CHECK_DBCLICK2CLOSE (IDD_PREFERENCE_SUB_GENRAL + 13) #define IDC_CHECK_ENABLEDOCSWITCHER (IDD_PREFERENCE_SUB_GENRAL + 14) - //#define IDC_CHECK_MAINTAININDENT (IDD_PREFERENCE_SUB_GENRAL + 15) + #define IDC_CHECK_ENABLETABPIN (IDD_PREFERENCE_SUB_GENRAL + 15) #define IDC_CHECK_KEEPINSAMEDIR (IDD_PREFERENCE_SUB_GENRAL + 16) #define IDC_CHECK_STYLEMRU (IDD_PREFERENCE_SUB_GENRAL + 17) #define IDC_CHECK_TAB_HIDE (IDD_PREFERENCE_SUB_GENRAL + 18) diff --git a/PowerEditor/src/WinControls/TabBar/TabBar.cpp b/PowerEditor/src/WinControls/TabBar/TabBar.cpp index 4f6e5f23a..87cd74812 100644 --- a/PowerEditor/src/WinControls/TabBar/TabBar.cpp +++ b/PowerEditor/src/WinControls/TabBar/TabBar.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see . #include +#include "Buffer.h" #include "TabBar.h" #include "Parameters.h" #include "DoubleBuffer/DoubleBuffer.h" @@ -28,7 +29,8 @@ bool TabBarPlus::_doDragNDrop = false; bool TabBarPlus::_drawTopBar = true; bool TabBarPlus::_drawInactiveTab = true; -bool TabBarPlus::_drawTabCloseButton = false; +bool TabBarPlus::_drawTabCloseButton = true; +bool TabBarPlus::_drawTabPinButton = true; bool TabBarPlus::_isDbClk2Close = false; bool TabBarPlus::_isCtrlVertical = false; bool TabBarPlus::_isCtrlMultiLine = false; @@ -277,6 +279,12 @@ void TabBarPlus::destroy() ::ImageList_Destroy(_hCloseBtnImgLst); _hCloseBtnImgLst = nullptr; } + + if (_hPinBtnImgLst != nullptr) + { + ::ImageList_Destroy(_hPinBtnImgLst); + _hPinBtnImgLst = nullptr; + } } @@ -285,7 +293,8 @@ void TabBarPlus::init(HINSTANCE hInst, HWND parent, bool isVertical, bool isMult Window::init(hInst, parent); const UINT dpi = DPIManagerV2::getDpiForWindow(_hParent); - _closeButtonZone.setParent(_hParent); + _closeButtonZone.init(_hParent, 0); + _pinButtonZone.init(_hParent, 1); _dpiManager.setDpi(dpi); int vertical = isVertical ? (TCS_VERTICAL | TCS_MULTILINE | TCS_RIGHTJUSTIFY) : 0; @@ -350,6 +359,7 @@ void TabBarPlus::init(HINSTANCE hInst, HWND parent, bool isVertical, bool isMult setFont(); setCloseBtnImageList(); + setPinBtnImageList(); } void TabBar::setFont() @@ -405,9 +415,21 @@ void TabBarPlus::doOwnerDrawTab(TabBarPlus* tbpObj) if (tbpObj) { - const int paddingSizeDynamicW = tbpObj->_dpiManager.scale(6); - const int paddingSizePlusClosebuttonDynamicW = tbpObj->_dpiManager.scale(10); - ::SendMessage(_hwndArray[i], TCM_SETPADDING, 0, MAKELPARAM(_drawTabCloseButton ? paddingSizePlusClosebuttonDynamicW : paddingSizeDynamicW, 0)); + int paddingSize = 0; + if (_drawTabCloseButton && _drawTabPinButton) // 2 buttons + { + paddingSize = 16; + } + else if (!_drawTabCloseButton && !_drawTabPinButton) // no button + { + paddingSize = 6; + } + else // only 1 button + { + paddingSize = 10; + } + const int paddingSizeDynamicW = tbpObj->_dpiManager.scale(paddingSize); + ::SendMessage(_hwndArray[i], TCM_SETPADDING, 0, MAKELPARAM(paddingSizeDynamicW, 0)); } } } @@ -439,27 +461,33 @@ void TabBarPlus::setColour(COLORREF colour2Set, tabColourIndex i, TabBarPlus* tb doOwnerDrawTab(tbpObj); } -void TabBarPlus::currentTabToStart() +void TabBarPlus::tabToStart(int index) { - int currentTabIndex = getCurrentTabIndex(); - if (currentTabIndex <= 0) + if (index < 0 || index >= static_cast(_nbItem)) + index = getCurrentTabIndex(); + + if (index <= 0) return; - for (int i = currentTabIndex, j = currentTabIndex - 1; j >= 0; --i, --j) + for (int i = index, j = index - 1; j >= 0; --i, --j) { - exchangeTabItemData(i, j); + if (!exchangeTabItemData(i, j)) + break; } } -void TabBarPlus::currentTabToEnd() +void TabBarPlus::tabToEnd(int index) { - int currentTabIndex = getCurrentTabIndex(); - if (currentTabIndex >= static_cast(_nbItem)) + if (index < 0 || index >= static_cast(_nbItem)) + index = getCurrentTabIndex(); + + if (index >= static_cast(_nbItem)) return; - for (int i = currentTabIndex, j = currentTabIndex + 1; j < static_cast(_nbItem); ++i, ++j) + for (int i = index, j = index + 1; j < static_cast(_nbItem); ++i, ++j) { - exchangeTabItemData(i, j); + if (!exchangeTabItemData(i, j)) + break; } } @@ -501,6 +529,45 @@ void TabBarPlus::setCloseBtnImageList() _closeButtonZone._height = btnSize; } + +void TabBarPlus::setPinBtnImageList() +{ + int iconSize = 0; + std::vector ids; + + if (NppDarkMode::isEnabled()) + { + iconSize = g_TabPinBtnSize_DM; + ids = { IDR_PINTAB_DM, IDR_PINTAB_HOVER_DM, IDR_PINTAB_PINNED_DM, IDR_PINTAB_PINNEDHOVER_DM }; + } + else + { + iconSize = g_TabPinBtnSize; + ids = { IDR_PINTAB, IDR_PINTAB_HOVER, IDR_PINTAB_PINNED, IDR_PINTAB_PINNEDHOVER }; + } + + if (_hPinBtnImgLst != nullptr) + { + ::ImageList_Destroy(_hPinBtnImgLst); + _hPinBtnImgLst = nullptr; + } + + const int btnSize = _dpiManager.scale(iconSize); + + _hPinBtnImgLst = ::ImageList_Create(btnSize, btnSize, ILC_COLOR32 | ILC_MASK, static_cast(ids.size()), 0); + + for (const auto& id : ids) + { + HICON hIcon = nullptr; + DPIManagerV2::loadIcon(_hInst, MAKEINTRESOURCE(id), btnSize, btnSize, &hIcon); + ::ImageList_AddIcon(_hPinBtnImgLst, hIcon); + ::DestroyIcon(hIcon); + } + + _pinButtonZone._width = btnSize; + _pinButtonZone._height = btnSize; +} + void TabBarPlus::doVertical() { for (int i = 0 ; i < _nbCtrl ; ++i) @@ -566,6 +633,7 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara { NppDarkMode::setDarkTooltips(hwnd, NppDarkMode::ToolTipsType::tabbar); setCloseBtnImageList(); + setPinBtnImageList(); return TRUE; } @@ -667,9 +735,10 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara // clear hover state of the close button, // WM_MOUSEMOVE won't handle this properly since the tab position will change - if (_isCloseHover) + if (_isCloseHover || _isPinHover) { _isCloseHover = false; + _isPinHover = false; ::InvalidateRect(_hSelf, &_currentHoverTabRect, false); } @@ -681,9 +750,12 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara case WM_LBUTTONDOWN : { + int xPos = LOWORD(lParam); + int yPos = HIWORD(lParam); + if (::GetWindowLongPtr(_hSelf, GWL_STYLE) & TCS_BUTTONS) { - int nTab = getTabIndexAt(LOWORD(lParam), HIWORD(lParam)); + int nTab = getTabIndexAt(xPos, yPos); if (nTab != -1 && nTab != static_cast(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0))) { setActiveTab(nTab); @@ -692,9 +764,6 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara if (_drawTabCloseButton) { - int xPos = LOWORD(lParam); - int yPos = HIWORD(lParam); - if (_closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) { _whichCloseClickDown = getTabIndexAt(xPos, yPos); @@ -703,6 +772,16 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara } } + if (_drawTabPinButton) + { + if (_pinButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) + { + _whichPinClickDown = getTabIndexAt(xPos, yPos); + ::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_REFRESHTABAR, 0); + return TRUE; + } + } + ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); int currentTabOn = static_cast(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0)); @@ -822,7 +901,7 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara RECT currentHoverTabRectOld = _currentHoverTabRect; bool isCloseHoverOld = _isCloseHover; - if (_currentHoverTabItem != -1) // is hovering + if (_currentHoverTabItem != -1) // tab item is being hovered { ::SendMessage(_hSelf, TCM_GETITEMRECT, _currentHoverTabItem, reinterpret_cast(&_currentHoverTabRect)); _isCloseHover = _closeButtonZone.isHit(p.x, p.y, _currentHoverTabRect, _isVertical); @@ -848,6 +927,39 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara trackMouseEvent(TME_LEAVE); } } + + if (_drawTabPinButton) + { + RECT currentHoverTabRectOld = _currentHoverTabRect; + bool isPinHoverOld = _isPinHover; + + if (_currentHoverTabItem != -1) // tab item is being hovered + { + ::SendMessage(_hSelf, TCM_GETITEMRECT, _currentHoverTabItem, reinterpret_cast(&_currentHoverTabRect)); + _isPinHover = _pinButtonZone.isHit(p.x, p.y, _currentHoverTabRect, _isVertical); + _isPinHover = _pinButtonZone.isHit(p.x, p.y, _currentHoverTabRect, _isVertical); + } + else + { + SetRectEmpty(&_currentHoverTabRect); + _isPinHover = false; + } + + if (isFromTabToTab || _isPinHover != isPinHoverOld) + { + if (isPinHoverOld && (isFromTabToTab || !_isPinHover)) + InvalidateRect(hwnd, ¤tHoverTabRectOld, FALSE); + + if (_isPinHover) + InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); + } + + if (_isPinHover) + { + // Mouse moves out from pin zone will send WM_MOUSELEAVE message + trackMouseEvent(TME_LEAVE); + } + } // Mouse moves out from tab zone will send WM_MOUSELEAVE message // but it doesn't track mouse moving from a tab to another trackMouseEvent(TME_LEAVE); @@ -858,13 +970,15 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara case WM_MOUSELEAVE: { - if (_isCloseHover) + if (_isCloseHover || _isPinHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); _currentHoverTabItem = -1; _whichCloseClickDown = -1; + _whichPinClickDown = -1; SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; + _isPinHover = false; notify(TCN_MOUSELEAVING, _currentHoverTabItem); break; @@ -915,6 +1029,28 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara _whichCloseClickDown = -1; } + if (_drawTabPinButton) + { + if ((_whichPinClickDown == currentTabOn) && _pinButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) + { + notify(TCN_TABPINNED, currentTabOn); + _whichPinClickDown = -1; + + // Get the next tab at same position + // If valid tab is found then + // update the current hover tab RECT (_currentHoverTabRect) + // update pin hover flag (_isPinHover), so that x will be highlighted or not based on new _currentHoverTabRect + int nextTab = getTabIndexAt(xPos, yPos); + if (nextTab != -1) + { + ::SendMessage(_hSelf, TCM_GETITEMRECT, nextTab, reinterpret_cast(&_currentHoverTabRect)); + _isPinHover = _pinButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical); + } + return TRUE; + } + _whichPinClickDown = -1; + } + break; } @@ -1144,30 +1280,23 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara return ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); } -void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct, bool isDarkMode) +void TabBarPlus::drawItem(DRAWITEMSTRUCT* pDrawItemStruct, bool isDarkMode) { RECT rect = pDrawItemStruct->rcItem; int nTab = pDrawItemStruct->itemID; - if (nTab < 0) - { - ::MessageBox(NULL, L"nTab < 0", L"", MB_OK); - } + assert(nTab >= 0); + bool isSelected = (nTab == ::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0)); wchar_t label[MAX_PATH] = { '\0' }; TCITEM tci{}; - tci.mask = TCIF_TEXT|TCIF_IMAGE; + tci.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM; tci.pszText = label; tci.cchTextMax = MAX_PATH-1; - if (!::SendMessage(_hSelf, TCM_GETITEM, nTab, reinterpret_cast(&tci))) - { - std::wstring errorMessageTitle = L"TabBarPlus::drawItem wrong: ! TCM_GETITEM"; - std::wstring errorMessage = GetLastErrorAsString(GetLastError()); - ::MessageBox(NULL, errorMessage.c_str(), errorMessageTitle.c_str(), MB_OK); - } - + ::SendMessage(_hSelf, TCM_GETITEM, nTab, reinterpret_cast(&tci)); + const COLORREF colorActiveBg = isDarkMode ? NppDarkMode::getSofterBackgroundColor() : ::GetSysColor(COLOR_BTNFACE); const COLORREF colorInactiveBgBase = isDarkMode ? NppDarkMode::getBackgroundColor() : ::GetSysColor(COLOR_BTNFACE); @@ -1322,22 +1451,22 @@ void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct, bool isDarkMode) { // 3 status for each inactive tab and selected tab close item : // normal / hover / pushed - int idxCloseImg = 0; // selected + int idxCloseImg = _closeTabIdx; // selected if (_isCloseHover && (_currentHoverTabItem == nTab)) { if (_whichCloseClickDown == -1) // hover { - idxCloseImg += 2; + idxCloseImg = _closeTabHoverIdx; } else if (_whichCloseClickDown == _currentHoverTabItem) // pushed { - idxCloseImg += 3; + idxCloseImg = _closeTabPushIdx; } } else if (!isSelected) // inactive { - idxCloseImg += 1; + idxCloseImg = _closeTabInactIdx; } RECT buttonRect = _closeButtonZone.getButtonRectFrom(rect, _isVertical); @@ -1345,6 +1474,82 @@ void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct, bool isDarkMode) ::ImageList_Draw(_hCloseBtnImgLst, idxCloseImg, hDC, buttonRect.left, buttonRect.top, ILD_TRANSPARENT); } + // draw pin button + if (_drawTabPinButton && _hPinBtnImgLst != nullptr) + { + // Each tab combined with the following stats : + // (active / inactive) | (pinned / unpinned) | (hover / not hover / pushed) + + + bool isPinned = reinterpret_cast(tci.lParam)->isPinned(); + int idxPinImg = _unpinnedIdx; // current: upinned as default + + + if (isPinned) + { + if (!isSelected) // inactive + { + if (_isPinHover && (_currentHoverTabItem == nTab)) + { + if (_whichPinClickDown == -1) // hover + { + idxPinImg = _pinnedHoverIdx; + } + else if (_whichPinClickDown == _currentHoverTabItem) // pushed + { + idxPinImg = _unpinnedIdx; + } + + } + else // pinned inactive + { + idxPinImg = _pinnedIdx; + } + } + else // current + { + if (_isPinHover && (_currentHoverTabItem == nTab)) // hover + idxPinImg = _pinnedHoverIdx; + else + idxPinImg = _pinnedIdx; + } + + } + else // unpinned + { + if (!isSelected) // inactive + { + if (_isPinHover && (_currentHoverTabItem == nTab)) + { + if (_whichPinClickDown == -1) // hover + { + idxPinImg = _unpinnedHoverIdx; + } + else if (_whichPinClickDown == _currentHoverTabItem) // pushed + { + idxPinImg = _pinnedIdx; + } + + } + else // unpinned inactive + { + idxPinImg = _unpinnedIdx; + } + } + else // current + { + if (_isPinHover && (_currentHoverTabItem == nTab)) // hover + idxPinImg = _unpinnedHoverIdx; + else + idxPinImg = _unpinnedIdx; + } + } + + RECT buttonRect = _pinButtonZone.getButtonRectFrom(rect, _isVertical); + + ::ImageList_Draw(_hPinBtnImgLst, idxPinImg, hDC, buttonRect.left, buttonRect.top, ILD_TRANSPARENT); + } + // draw image HIMAGELIST hImgLst = (HIMAGELIST)::SendMessage(_hSelf, TCM_GETIMAGELIST, 0, 0); @@ -1496,7 +1701,7 @@ void TabBarPlus::setActiveTab(int tabIndex) notify(TCN_SELCHANGE, tabIndex); } -void TabBarPlus::exchangeTabItemData(int oldTab, int newTab) +bool TabBarPlus::exchangeTabItemData(int oldTab, int newTab) { //1. shift their data, and insert the source TCITEM itemData_nDraggedTab{}, itemData_shift{}; @@ -1512,10 +1717,18 @@ void TabBarPlus::exchangeTabItemData(int oldTab, int newTab) itemData_shift.cchTextMax = (stringSize); ::SendMessage(_hSelf, TCM_GETITEM, oldTab, reinterpret_cast(&itemData_nDraggedTab)); + Buffer* chosenBuf = reinterpret_cast(itemData_nDraggedTab.lParam); + + ::SendMessage(_hSelf, TCM_GETITEM, newTab, reinterpret_cast(&itemData_shift)); + Buffer* shiftBuf = reinterpret_cast(itemData_shift.lParam); + + if (chosenBuf->isPinned() != shiftBuf->isPinned()) + return false; + int i = oldTab; if (oldTab > newTab) { - for (int i = oldTab; i > newTab; i--) + for (; i > newTab; i--) { ::SendMessage(_hSelf, TCM_GETITEM, i - 1, reinterpret_cast(&itemData_shift)); ::SendMessage(_hSelf, TCM_SETITEM, i, reinterpret_cast(&itemData_shift)); @@ -1523,12 +1736,13 @@ void TabBarPlus::exchangeTabItemData(int oldTab, int newTab) } else { - for (int i = oldTab; i < newTab; ++i) + for (; i < newTab; ++i) { ::SendMessage(_hSelf, TCM_GETITEM, i + 1, reinterpret_cast(&itemData_shift)); ::SendMessage(_hSelf, TCM_SETITEM, i, reinterpret_cast(&itemData_shift)); } } + ::SendMessage(_hSelf, TCM_SETITEM, newTab, reinterpret_cast(&itemData_nDraggedTab)); // Tell Notepad_plus to notifiy plugins that a D&D operation was done (so doc index has been changed) @@ -1536,6 +1750,8 @@ void TabBarPlus::exchangeTabItemData(int oldTab, int newTab) //2. set to focus setActiveTab(newTab); + + return true; } void TabBarPlus::exchangeItemData(POINT point) @@ -1556,9 +1772,11 @@ void TabBarPlus::exchangeItemData(POINT point) return; } - exchangeTabItemData(_nTabDragged, nTab); - _previousTabSwapped = _nTabDragged; - _nTabDragged = nTab; + if (exchangeTabItemData(_nTabDragged, nTab)) + { + _previousTabSwapped = _nTabDragged; + _nTabDragged = nTab; + } } else { @@ -1571,11 +1789,10 @@ void TabBarPlus::exchangeItemData(POINT point) _previousTabSwapped = -1; _isDraggingInside = false; } - } -bool CloseButtonZone::isHit(int x, int y, const RECT & tabRect, bool isVertical) const +bool TabButtonZone::isHit(int x, int y, const RECT & tabRect, bool isVertical) const { RECT buttonRect = getButtonRectFrom(tabRect, isVertical); @@ -1585,22 +1802,42 @@ bool CloseButtonZone::isHit(int x, int y, const RECT & tabRect, bool isVertical) return false; } -RECT CloseButtonZone::getButtonRectFrom(const RECT & tabRect, bool isVertical) const +RECT TabButtonZone::getButtonRectFrom(const RECT & tabRect, bool isVertical) const { RECT buttonRect{}; + const UINT dpi = DPIManagerV2::getDpiForWindow(_parent); + const int inBetween = DPIManagerV2::scale(NppDarkMode::isEnabled() ? 4 : 8, dpi); int fromBorder = 0; if (isVertical) { fromBorder = (tabRect.right - tabRect.left - _width + 1) / 2; + if (_order == 0) + { + buttonRect.top = tabRect.top + fromBorder; + } + else if (_order == 1) + { + buttonRect.top = tabRect.top + fromBorder + _height + inBetween; + } + buttonRect.left = tabRect.left + fromBorder; } else { fromBorder = (tabRect.bottom - tabRect.top - _height + 1) / 2; - buttonRect.left = tabRect.right - fromBorder - _width; + if (_order == 0) + { + buttonRect.left = tabRect.right - fromBorder - _width; + } + else if (_order == 1) + { + buttonRect.left = tabRect.right - fromBorder - _width * 2 - inBetween; + } + + buttonRect.top = tabRect.top + fromBorder; } - buttonRect.top = tabRect.top + fromBorder; + buttonRect.bottom = buttonRect.top + _height; buttonRect.right = buttonRect.left + _width; diff --git a/PowerEditor/src/WinControls/TabBar/TabBar.h b/PowerEditor/src/WinControls/TabBar/TabBar.h index 226a5734e..d0fe125ce 100644 --- a/PowerEditor/src/WinControls/TabBar/TabBar.h +++ b/PowerEditor/src/WinControls/TabBar/TabBar.h @@ -36,6 +36,7 @@ #define TCN_MOUSEHOVERING (TCN_FIRST - 13) #define TCN_MOUSELEAVING (TCN_FIRST - 14) #define TCN_MOUSEHOVERSWITCHING (TCN_FIRST - 15) +#define TCN_TABPINNED (TCN_FIRST - 16) #define WM_TABSETSTYLE (WM_APP + 0x024) @@ -65,7 +66,9 @@ constexpr int g_TabHeightLarge = 25; constexpr int g_TabWidth = 45; constexpr int g_TabWidthCloseBtn = 60; constexpr int g_TabCloseBtnSize = 11; +constexpr int g_TabPinBtnSize = 11; constexpr int g_TabCloseBtnSize_DM = 16; +constexpr int g_TabPinBtnSize_DM = 16; struct TBHDR { @@ -148,15 +151,21 @@ protected: }; -struct CloseButtonZone +struct TabButtonZone { + void init(HWND parent, int order) { + _parent = parent; + _order = order; + } + bool isHit(int x, int y, const RECT & tabRect, bool isVertical) const; RECT getButtonRectFrom(const RECT & tabRect, bool isVertical) const; - void setParent(HWND parent) { _parent = parent; } + void setOrder(int newOrder) { _order = newOrder; }; HWND _parent = nullptr; int _width = 0; int _height = 0; + int _order = -1; // from right to left: 0, 1 }; @@ -200,6 +209,7 @@ public : static bool drawTopBar() {return _drawTopBar;}; static bool drawInactiveTab() {return _drawInactiveTab;}; static bool drawTabCloseButton() {return _drawTabCloseButton;}; + static bool drawTabPinButton() {return _drawTabPinButton;}; static bool isDbClk2Close() {return _isDbClk2Close;}; static bool isVertical() { return _isCtrlVertical;}; static bool isMultiLine() { return _isCtrlMultiLine;}; @@ -220,6 +230,11 @@ public : doOwnerDrawTab(tbpObj); } + static void setDrawTabPinButton(bool b, TabBarPlus* tbpObj) { + _drawTabPinButton = b; + doOwnerDrawTab(tbpObj); + } + static void setDbClk2Close(bool b) { _isDbClk2Close = b; } @@ -241,10 +256,19 @@ public : static void setColour(COLORREF colour2Set, tabColourIndex i, TabBarPlus* tbpObj); virtual int getIndividualTabColourId(int tabIndex) = 0; - void currentTabToStart(); - void currentTabToEnd(); + void tabToStart(int index = -1); + void tabToEnd(int index = -1); void setCloseBtnImageList(); + void setPinBtnImageList(); + + void setTabPinButtonOrder(int newOrder) { + _pinButtonZone.setOrder(newOrder); + } + + void setTabCloseButtonOrder(int newOrder) { + _closeButtonZone.setOrder(newOrder); + } protected: // it's the boss to decide if we do the drag N drop @@ -263,10 +287,25 @@ protected: RECT _currentHoverTabRect{}; int _currentHoverTabItem = -1; // -1 : no mouse on any tab - CloseButtonZone _closeButtonZone; + TabButtonZone _closeButtonZone; + TabButtonZone _pinButtonZone; + HIMAGELIST _hCloseBtnImgLst = nullptr; + const int _closeTabIdx = 0; + const int _closeTabInactIdx = 1; + const int _closeTabHoverIdx = 2; + const int _closeTabPushIdx = 3; + + HIMAGELIST _hPinBtnImgLst = nullptr; + const int _unpinnedIdx = 0; + const int _unpinnedHoverIdx = 1; + const int _pinnedIdx = 2; + const int _pinnedHoverIdx = 3; + bool _isCloseHover = false; + bool _isPinHover = false; int _whichCloseClickDown = -1; + int _whichPinClickDown = -1; bool _lmbdHit = false; // Left Mouse Button Down Hit HWND _tooltips = nullptr; @@ -276,7 +315,7 @@ protected: return (((TabBarPlus *)(::GetWindowLongPtr(hwnd, GWLP_USERDATA)))->runProc(hwnd, Message, wParam, lParam)); }; void setActiveTab(int tabIndex); - void exchangeTabItemData(int oldTab, int newTab); + bool exchangeTabItemData(int oldTab, int newTab); void exchangeItemData(POINT point); @@ -284,6 +323,7 @@ protected: static bool _drawInactiveTab; static bool _drawTopBar; static bool _drawTabCloseButton; + static bool _drawTabPinButton; static bool _isDbClk2Close; static bool _isCtrlVertical; static bool _isCtrlMultiLine; diff --git a/PowerEditor/src/icons/dark/tabbar/pinTabButton.ico b/PowerEditor/src/icons/dark/tabbar/pinTabButton.ico new file mode 100644 index 0000000000000000000000000000000000000000..8078bf609d56f60f530dd3f1bded1a0376e17d55 GIT binary patch literal 2809 zcmbuBcTf|^9>+Hj=_MR30zxk0qlz?K4ini!+3nEv~Y zFb+26EVXfXh3Po`j2!|1;OqKskRIPBuFT@mKtuaLYhU+3+!cQ}fWzUGuK9QdV6OPN zDf#+)d7Z%%OW+Wa|fL83?ug~ z=vVuUg@FK0)clc}+{GPSlw$vM&IHCr_pXZ5ZjcW#rbt%lmqkfPrgq+7O z?8e}fqu*-WrLmwf%6VknYFhzNHxD>Y+!XtoV};j(^PD9uc$n(Ma;a%o(VMxGtZ1xG zu`r)gT?*0Gvg}&}UP72w{=mhJo%gC_B({hD=QLy;FVC%19qO}v8y9!;-L>i6`0ObY zIo;i3JZ2{js1^I-kt`}NQ#!(Q;(eF`0TaVtQB~($qW^=$-ysqDOXBgH5D!#RXeZHC7tgw8!wI8@WjzOa_6lTh^08h(c%?QOdJ;zrn) zYJ zo(+RRMK`WjkoKibe2!xBA!I+El|Zc*iv3&CQ*0oG{l(QAkDaOy?fnV9(iV%snv#hH zNs6rFo*KDQ83?~Y?+}Jm(mS@ARBkcMv}vO2JEej?xbH(2bM2rzQ9jL95J8pyao=BUd}olQIseBaUIM#c!~7LzeXut(kEP z(Hpe{JOgKP*12JEyxt^bzOE#HJL29v{5f6oL1Ti-QD7~f&ZZEvu`Oz78JSal6oUt6 zQ{PqFmZRAYg6CVWTwrYGltjRwyZy=tk5`A{o`)S($M!e_-&51l8xVVc1JCyx1^yR2 zvpC(a{|`JY7T@79jwhv?dkR{@W2xDi>6R`29&)$$Vu}b-ukSm`adLQ{i4L-p=%t)A z#eS5p{e6DWo+U{5T|5V=5Zh9tFhw46PC}tNVgrB2MyrKRT_`!JW`fSah3XA%L`bZ; zY2?0;?ShwTMOhoQ6_VOfbsR3|P}7bXq*gNdtM<}fviy^`*9E0DdU5M?76$Ustp6&Wc7k468d&H0*mM`H|3h z7l>A|$~>aR4JH|a5NQ%Y2sK~kJxPrd5J_%9BEj;bYiFN&@6sdN$UTGG;T9+ffzXmP z|3A9JXPgC+7i3HE@H^!<)b1VuQKK}O?8F#srEgN00+NcFk9hW8hNhm{M+Rq8D4v!3 ztPaUIus1$A*`nL)5Fd5E_&p?xW&N67iEX(9a0gx)ro?Ca-GUG_-LzV_ivDYI#U{c4 zMgedp^hqI#h~@p5{WhG$WBs}_sXd^bIJ|^2V%rq2nA241vjHB4B@g=-n!5lWxpzZn z$zxy+Rb*}xjPy4#49#|4s0*H~@~tK@t}}xiO3X z%?ros^qpY)*wsu6tb~n3kKVI0#pXJF=U9bR>gNmN@Cqm{dG-4)hP@56+{ll8F=qd$ zufJ$sh_|n$xYomaO%ykgCprYW@2L;nz37>2HSjyaHQ1)CgtXbv9XPX5Q?FXlGBMRX z6tmy4KD!;^+g`989+{O~S}M2Z@npB`0Du~tnkGlIu4(Bo@-sR`%es1OCND2W21>#eR z%$hAdbow-Lg!_5H%dF$3r51}HK1kAp^(A_$0eh5qgLmXe`E<}!xoN4%Yy)9lHJehp zm6I=ODP(9>Y1?yOrJALhmC(Dp6V^V>|CHWrCn_THKn$YdGqQE#P(z?=C zZ^@`t813nZ(%K4w66U@l*S+MwaMvJ_{SW3YZ|P7sk4r#1<}%#c*juwp2Dgp?o)fFT z&Dv|^?jtk%zntKzA(OKvXf!R(R$d-fM}4Ag`Ikm^vHLI0GTbUzELb0Vl4!!S+fJR# zE=~0E@%bS5$q2%#D2{RGj_su8y)x0QE$v!pkfiS8V5UaA)QC zmgIVP^pZ7e+uQStLrwOzw6wlWMY6%}yeGIhI1lSUJkj(;4|n$iS&@5ZNTYGrnm?rx z9v s1CD7QK^I{4HkORP*4ticn`NhBr7@e66_5DpTKO#fBk3v1eZAAiU0rr literal 0 HcmV?d00001 diff --git a/PowerEditor/src/icons/dark/tabbar/pinTabButton_hover.ico b/PowerEditor/src/icons/dark/tabbar/pinTabButton_hover.ico new file mode 100644 index 0000000000000000000000000000000000000000..7072beefb1dcad6a21eea6694ac88ed0d19e0491 GIT binary patch literal 1592 zcmZQzU}Run5D;Jh0uBaaMg|5qAWK99!p{ftD}h)-0>THeUobH+C@4Vq*Ma;*76yjS z06%wLE-5acD6gl7OAwF-nJB=)1|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-lYeY$K zep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt%^UT^vI!dXsrLI1(1HY*@hUGt0#D z)%*MVcmMnUe|@j(Usg6Yw*Ezn7EL!`R4Wm!|M%zPv4t}m8ygQcOq_U;Ig;TpgPYs2 z!#q4Z{|hBP{J+1y{`g0!fQX2S=ElZ{0({KhM8(DLAIfI9B6_({!az8dVKw6thBa(| zet*}OZLi`f<^Ev9Y-jmAZm|Qy(krnkNXc4%7{0vGq;s9IW~uBr~30_GbU$jSohk++x)Nd zMD3ZKFQWVIsxM-7J~bnJf+B(72Bm2UaBx2;xBWp$a5J)jg4?6>?bW?rM;s(tAAavX zmL^@j@#NnQPfangX+oY&(*-Xr`J$w>Zkkq=^2{)njT@z&Y~Fb1=V>Lgd1Zd*m=hD0 z=y=x|?_SrOIjihhrCdXC!qXMCt6r4`iiC%T*5+!Z=pB#|FQyd&&cKbHRq~ z!c5l$58O37P{;D%ZA*jMmUXMnDkaDqXMUi<(s3g5{`dBi4O_P{esf}&kRBPP&U_&9 zNX3TN%=412hkm_Ss;2q=paD;I!@AdfN&7kPE?WDU|Bvm1nlI+AE^VG%%)s0zRVk?KDupi?r^y zKH|P1K;W89S8K2DH^x7XVy*?VbJp)Y^lp>ct{28Efi53nHyxaRudZ~R?<&{Ho@$fQ z{H>qg`)qh7W}Qo*%WR{y%a|LxH9dtUFMGMOV-CaWzP zfNuk1Opn{*C(<)-%A-?o?Mi62wi@ly!X#~1&()n7}=T+ zYJZ3^jSZFm_4i-9xx>9TyR;e%pDnxc_?8aOVRePo6Xus$&7FRXVb|Stta-72m5k12 z&tg2WRP^uCA~ugnM;L6w_pg$BYI>F-C2|{Y&aPjL2ELcq9%ERuwbl5qd8bEauke8i zn>gW#b5}7qn9TIKwwH0uR@(&y%lb)=IE`WJzjsY`jhBu-zRwWy^@>EW^nq%jU#q^Fe_-D5D85Kw M!t*7-NP!n$0LG7U-2eap literal 0 HcmV?d00001 diff --git a/PowerEditor/src/icons/dark/tabbar/pinTabButton_pinned.ico b/PowerEditor/src/icons/dark/tabbar/pinTabButton_pinned.ico new file mode 100644 index 0000000000000000000000000000000000000000..7072beefb1dcad6a21eea6694ac88ed0d19e0491 GIT binary patch literal 1592 zcmZQzU}Run5D;Jh0uBaaMg|5qAWK99!p{ftD}h)-0>THeUobH+C@4Vq*Ma;*76yjS z06%wLE-5acD6gl7OAwF-nJB=)1|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-lYeY$K zep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt%^UT^vI!dXsrLI1(1HY*@hUGt0#D z)%*MVcmMnUe|@j(Usg6Yw*Ezn7EL!`R4Wm!|M%zPv4t}m8ygQcOq_U;Ig;TpgPYs2 z!#q4Z{|hBP{J+1y{`g0!fQX2S=ElZ{0({KhM8(DLAIfI9B6_({!az8dVKw6thBa(| zet*}OZLi`f<^Ev9Y-jmAZm|Qy(krnkNXc4%7{0vGq;s9IW~uBr~30_GbU$jSohk++x)Nd zMD3ZKFQWVIsxM-7J~bnJf+B(72Bm2UaBx2;xBWp$a5J)jg4?6>?bW?rM;s(tAAavX zmL^@j@#NnQPfangX+oY&(*-Xr`J$w>Zkkq=^2{)njT@z&Y~Fb1=V>Lgd1Zd*m=hD0 z=y=x|?_SrOIjihhrCdXC!qXMCt6r4`iiC%T*5+!Z=pB#|FQyd&&cKbHRq~ z!c5l$58O37P{;D%ZA*jMmUXMnDkaDqXMUi<(s3g5{`dBi4O_P{esf}&kRBPP&U_&9 zNX3TN%=412hkm_Ss;2q=paD;I!@AdfN&7kPE?WDU|Bvm1nlI+AE^VG%%)s0zRVk?KDupi?r^y zKH|P1K;W89S8K2DH^x7XVy*?VbJp)Y^lp>ct{28Efi53nHyxaRudZ~R?<&{Ho@$fQ z{H>qg`)qh7W}Qo*%WR{y%a|LxH9dtUFMGMOV-CaWzP zfNuk1Opn{*C(<)-%A-?o?Mi62wi@ly!X#~1&()n7}=T+ zYJZ3^jSZFm_4i-9xx>9TyR;e%pDnxc_?8aOVRePo6Xus$&7FRXVb|Stta-72m5k12 z&tg2WRP^uCA~ugnM;L6w_pg$BYI>F-C2|{Y&aPjL2ELcq9%ERuwbl5qd8bEauke8i zn>gW#b5}7qn9TIKwwH0uR@(&y%lb)=IE`WJzjsY`jhBu-zRwWy^@>EW^nq%jU#q^Fe_-D5D85Kw M!t*7-NP!n$0LG7U-2eap literal 0 HcmV?d00001 diff --git a/PowerEditor/src/icons/dark/tabbar/pinTabButton_pinnedHover.ico b/PowerEditor/src/icons/dark/tabbar/pinTabButton_pinnedHover.ico new file mode 100644 index 0000000000000000000000000000000000000000..8078bf609d56f60f530dd3f1bded1a0376e17d55 GIT binary patch literal 2809 zcmbuBcTf|^9>+Hj=_MR30zxk0qlz?K4ini!+3nEv~Y zFb+26EVXfXh3Po`j2!|1;OqKskRIPBuFT@mKtuaLYhU+3+!cQ}fWzUGuK9QdV6OPN zDf#+)d7Z%%OW+Wa|fL83?ug~ z=vVuUg@FK0)clc}+{GPSlw$vM&IHCr_pXZ5ZjcW#rbt%lmqkfPrgq+7O z?8e}fqu*-WrLmwf%6VknYFhzNHxD>Y+!XtoV};j(^PD9uc$n(Ma;a%o(VMxGtZ1xG zu`r)gT?*0Gvg}&}UP72w{=mhJo%gC_B({hD=QLy;FVC%19qO}v8y9!;-L>i6`0ObY zIo;i3JZ2{js1^I-kt`}NQ#!(Q;(eF`0TaVtQB~($qW^=$-ysqDOXBgH5D!#RXeZHC7tgw8!wI8@WjzOa_6lTh^08h(c%?QOdJ;zrn) zYJ zo(+RRMK`WjkoKibe2!xBA!I+El|Zc*iv3&CQ*0oG{l(QAkDaOy?fnV9(iV%snv#hH zNs6rFo*KDQ83?~Y?+}Jm(mS@ARBkcMv}vO2JEej?xbH(2bM2rzQ9jL95J8pyao=BUd}olQIseBaUIM#c!~7LzeXut(kEP z(Hpe{JOgKP*12JEyxt^bzOE#HJL29v{5f6oL1Ti-QD7~f&ZZEvu`Oz78JSal6oUt6 zQ{PqFmZRAYg6CVWTwrYGltjRwyZy=tk5`A{o`)S($M!e_-&51l8xVVc1JCyx1^yR2 zvpC(a{|`JY7T@79jwhv?dkR{@W2xDi>6R`29&)$$Vu}b-ukSm`adLQ{i4L-p=%t)A z#eS5p{e6DWo+U{5T|5V=5Zh9tFhw46PC}tNVgrB2MyrKRT_`!JW`fSah3XA%L`bZ; zY2?0;?ShwTMOhoQ6_VOfbsR3|P}7bXq*gNdtM<}fviy^`*9E0DdU5M?76$Ustp6&Wc7k468d&H0*mM`H|3h z7l>A|$~>aR4JH|a5NQ%Y2sK~kJxPrd5J_%9BEj;bYiFN&@6sdN$UTGG;T9+ffzXmP z|3A9JXPgC+7i3HE@H^!<)b1VuQKK}O?8F#srEgN00+NcFk9hW8hNhm{M+Rq8D4v!3 ztPaUIus1$A*`nL)5Fd5E_&p?xW&N67iEX(9a0gx)ro?Ca-GUG_-LzV_ivDYI#U{c4 zMgedp^hqI#h~@p5{WhG$WBs}_sXd^bIJ|^2V%rq2nA241vjHB4B@g=-n!5lWxpzZn z$zxy+Rb*}xjPy4#49#|4s0*H~@~tK@t}}xiO3X z%?ros^qpY)*wsu6tb~n3kKVI0#pXJF=U9bR>gNmN@Cqm{dG-4)hP@56+{ll8F=qd$ zufJ$sh_|n$xYomaO%ykgCprYW@2L;nz37>2HSjyaHQ1)CgtXbv9XPX5Q?FXlGBMRX z6tmy4KD!;^+g`989+{O~S}M2Z@npB`0Du~tnkGlIu4(Bo@-sR`%es1OCND2W21>#eR z%$hAdbow-Lg!_5H%dF$3r51}HK1kAp^(A_$0eh5qgLmXe`E<}!xoN4%Yy)9lHJehp zm6I=ODP(9>Y1?yOrJALhmC(Dp6V^V>|CHWrCn_THKn$YdGqQE#P(z?=C zZ^@`t813nZ(%K4w66U@l*S+MwaMvJ_{SW3YZ|P7sk4r#1<}%#c*juwp2Dgp?o)fFT z&Dv|^?jtk%zntKzA(OKvXf!R(R$d-fM}4Ag`Ikm^vHLI0GTbUzELb0Vl4!!S+fJR# zE=~0E@%bS5$q2%#D2{RGj_su8y)x0QE$v!pkfiS8V5UaA)QC zmgIVP^pZ7e+uQStLrwOzw6wlWMY6%}yeGIhI1lSUJkj(;4|n$iS&@5ZNTYGrnm?rx z9v s1CD7QK^I{4HkORP*4ticn`NhBr7@e66_5DpTKO#fBk3v1eZAAiU0rr literal 0 HcmV?d00001 diff --git a/PowerEditor/src/icons/standard/tabbar/pinTabButton.ico b/PowerEditor/src/icons/standard/tabbar/pinTabButton.ico new file mode 100644 index 0000000000000000000000000000000000000000..b92c30a4f218182c81df9d6f82b2df25c4d6508e GIT binary patch literal 1150 zcmbV~zl*{!9K~~RASyypIS~X8e_&nw2V8D*)e266>p6*wyEr%~w77`>fN)Y21<_5M z6dY8h=0lp6zG9-y<9jqgJc&GtK97ESF2jvh0Xw8U22rc`^DLSxhJtLbKVl=FBH* ztJR9HI~)#e-t+kkx7&?Yt7X$~GuG=hg25oB)2YzIzOmo$Mj`=0y<9HXY&K9;mFF}X4a>XR?Vza|a=9G5Uhl(wm}^F(QBluBV>X)! z>+yIT<#JhAGjnuZ=l5Z|-P(MKL;|H!$)=gi{TPeI@Us_QN~KaL7K@Hp2ITX3>+Yq~ mX%q?t$1LO7KMMo`NG6j|6y;AW<5|-*4V6meU+gvE_x>B8al0@8 literal 0 HcmV?d00001 diff --git a/PowerEditor/src/icons/standard/tabbar/pinTabButton_hover.ico b/PowerEditor/src/icons/standard/tabbar/pinTabButton_hover.ico new file mode 100644 index 0000000000000000000000000000000000000000..144175d953cb2fcf2668a42b6e3f1561b45394e5 GIT binary patch literal 1150 zcmbV~F$%&!5Jg9@71Ai!h;|-BY`lnqC$P7(w6eGG2=>;N;uSoBC}1vy(Mz`7g`N|A)40oAu0W?5C$Z^UZs(i9d^v)1sT-HiJCQr`|u0^L#(+lsaDL zZLPb?f%|coHg>?kvcAizU(2&zF}x2;U+OJ&9{1g-V2SVRx;SyA^q~47i-Q@~In-y& zss}kVlX#}&RYOc1almOl;l80h_sknlSs!w$)~~t3RynFz`Q)Hy)g-2{a_}ze72kx# ap}p6%uIh*1EM@vYhz#zuJz@P!dKp~(AL>x#lH~{5H0rTd~`;SjOG4iFQrFhLC zWd2am|9P6?e^M~IzrQYZ`cDc**AL@=+nVwpABL&JNB=xE;XgKvPY<#jHoYJ*WHtEM UBUwM8G=MMMh)sX^%pg?`0N!FBx&QzG literal 0 HcmV?d00001 diff --git a/PowerEditor/src/icons/standard/tabbar/pinTabButton_pinnedHover.ico b/PowerEditor/src/icons/standard/tabbar/pinTabButton_pinnedHover.ico new file mode 100644 index 0000000000000000000000000000000000000000..478b129fb0a0a18657dd8c523db2df1acb3fc9cc GIT binary patch literal 1150 zcmdUr!3lsc6hk98Ku?}MI)>x8{{s4p1VYJIOHWp6(loD>0#kgYApR?k8CU>pF(ZoP z&O@-rUgtPF`)$4QBWL`H=Uu;Q_Et~trDwN1*X(dj&2sLmx;wZ`8TGE~GG)}euFI5B h@47BiM!oC0Od0jA>oR5NYtMDFoLp6Q-c*?(e*q*xI!XWl literal 0 HcmV?d00001 diff --git a/PowerEditor/src/resource.h b/PowerEditor/src/resource.h index b6f038c19..fc9cbae79 100644 --- a/PowerEditor/src/resource.h +++ b/PowerEditor/src/resource.h @@ -405,6 +405,15 @@ #define IDR_FIND_RESULT_ICO2 1564 #define IDR_FIND_RESULT_ICO_DM 1565 +#define IDR_PINTAB 1566 +#define IDR_PINTAB_HOVER 1567 +#define IDR_PINTAB_PINNED 1568 +#define IDR_PINTAB_PINNEDHOVER 1569 +#define IDR_PINTAB_DM 1570 +#define IDR_PINTAB_HOVER_DM 1571 +#define IDR_PINTAB_PINNED_DM 1572 +#define IDR_PINTAB_PINNEDHOVER_DM 1573 + #define ID_MACRO 20000 // O . // C . @@ -698,6 +707,8 @@ #define NPPM_INTERNAL_LINECUTCOPYWITHOUTSELECTION (NOTEPADPLUS_USER_INTERNAL + 78) #define NPPM_INTERNAL_DOCMODIFIEDBYREPLACEALL (NOTEPADPLUS_USER_INTERNAL + 79) + #define NPPM_INTERNAL_DRAWTABBARPINBOTTUN (NOTEPADPLUS_USER_INTERNAL + 80) + // See Notepad_plus_msgs.h //#define NOTEPADPLUS_USER (WM_USER + 1000)