LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { // Custom window message to change tab control style on the fly case WM_TABSETSTYLE: { DWORD style; //::SendMessage(upDownWnd, UDM_SETBUDDY, NULL, 0); style = ::GetWindowLongPtr(hwnd, GWL_STYLE); if (wParam > 0) style |= lParam; else style &= ~lParam; _isVertical = ((style & TCS_VERTICAL) != 0); _isMultiLine = ((style & TCS_MULTILINE) != 0); ::SetWindowLongPtr(hwnd, GWL_STYLE, style); ::InvalidateRect(hwnd, NULL, TRUE); return TRUE; } case WM_LBUTTONDOWN : { if (_drawTabCloseButton) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); if (_closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect)) { _whichCloseClickDown = getTabIndexAt(xPos, yPos); ::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_REFRESHTABAR, 0); return TRUE; } } ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); int currentTabOn = ::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0); if (wParam == 2) return TRUE; if (_doDragNDrop) { _nSrcTab = _nTabDragged = currentTabOn; POINT point; point.x = LOWORD(lParam); point.y = HIWORD(lParam); ::ClientToScreen(hwnd, &point); if(::DragDetect(hwnd, point)) { // Yes, we're beginning to drag, so capture the mouse... _isDragging = true; ::SetCapture(hwnd); } } TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = NM_CLICK; nmhdr.hdr.idFrom = reinterpret_cast<unsigned int>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); return TRUE; } case WM_RBUTTONDOWN : //rightclick selects tab aswell { ::CallWindowProc(_tabBarDefaultProc, hwnd, WM_LBUTTONDOWN, wParam, lParam); return TRUE; } //#define NPPM_INTERNAL_ISDRAGGING 40926 case WM_MOUSEMOVE : { if (_isDragging) { POINT p; p.x = LOWORD(lParam); p.y = HIWORD(lParam); exchangeItemData(p); // Get cursor position of "Screen" // For using the function "WindowFromPoint" afterward!!! ::GetCursorPos(&_draggingPoint); draggingCursor(_draggingPoint); return TRUE; } if (_drawTabCloseButton) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int _currentHoverTabItemOld = _currentHoverTabItem; RECT _currentHoverTabRectOld = _currentHoverTabRect; bool _isCloseHoverOld = _isCloseHover; _currentHoverTabItem = getTabIndexAt(xPos, yPos); if (_currentHoverTabItem != -1) { ::SendMessage(_hSelf, TCM_GETITEMRECT, _currentHoverTabItem, (LPARAM)&_currentHoverTabRect); _isCloseHover = _closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect); } else { SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; } if (_currentHoverTabItem != _currentHoverTabItemOld || _isCloseHover != _isCloseHoverOld) { if (_isCloseHoverOld && (_currentHoverTabItem != _currentHoverTabItemOld || !_isCloseHover)) InvalidateRect(hwnd, &_currentHoverTabRectOld, FALSE); if (_isCloseHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); } if (_isCloseHover) { TRACKMOUSEEVENT tme = {}; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; TrackMouseEvent(&tme); } } break; } case WM_MOUSELEAVE: if (_isCloseHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); _currentHoverTabItem = -1; SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; break; case WM_LBUTTONUP : { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); if (_isDragging) { if(::GetCapture() == _hSelf) ::ReleaseCapture(); // Send a notification message to the parent with wParam = 0, lParam = 0 // nmhdr.idFrom = this // destIndex = this->_nSrcTab // scrIndex = this->_nTabDragged TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = _isDraggingInside?TCN_TABDROPPED:TCN_TABDROPPEDOUTSIDE; nmhdr.hdr.idFrom = reinterpret_cast<unsigned int>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); return TRUE; } if (_drawTabCloseButton) { if ((_whichCloseClickDown == currentTabOn) && _closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect)) { TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = TCN_TABDELETE; nmhdr.hdr.idFrom = reinterpret_cast<unsigned int>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); _whichCloseClickDown = -1; return TRUE; } _whichCloseClickDown = -1; } break; } case WM_CAPTURECHANGED : { if (_isDragging) { _isDragging = false; return TRUE; } break; } case WM_DRAWITEM : { drawItem((DRAWITEMSTRUCT *)lParam); return TRUE; } case WM_KEYDOWN : { if (wParam == VK_LCONTROL) ::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_PLUS_TAB))); return TRUE; } case WM_MBUTTONUP: { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = TCN_TABDELETE; nmhdr.hdr.idFrom = reinterpret_cast<unsigned int>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); return TRUE; } case WM_LBUTTONDBLCLK : { if (_isDbClk2Close) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = TCN_TABDELETE; nmhdr.hdr.idFrom = reinterpret_cast<unsigned int>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); } return TRUE; } } return ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); }
LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { // Custom window message to change tab control style on the fly case WM_TABSETSTYLE: { LONG_PTR style = ::GetWindowLongPtr(hwnd, GWL_STYLE); if (wParam > 0) style |= lParam; else style &= ~lParam; _isVertical = ((style & TCS_VERTICAL) != 0); _isMultiLine = ((style & TCS_MULTILINE) != 0); ::SetWindowLongPtr(hwnd, GWL_STYLE, style); ::InvalidateRect(hwnd, NULL, TRUE); return TRUE; } case WM_MOUSEWHEEL: { // .............................................................................. // MOUSEWHEEL: // will scroll the tab bar area (similar to Firefox's tab scrolling), // it only happens if not in multi-line mode and at least one tab is hidden // .............................................................................. // CTRL + MOUSEWHEEL: // will do previous/next tab WITH scroll wrapping (endless loop) // .............................................................................. // SHIFT + MOUSEWHEEL: // will do previous/next tab WITHOUT scroll wrapping (stops at first/last tab) // .............................................................................. // CTRL + SHIFT + MOUSEWHEEL: // will switch to the first/last tab // .............................................................................. if (_isDragging) return TRUE; const bool isForward = ((short)HIWORD(wParam)) < 0; // wheel rotation towards the user will be considered as forward direction const LRESULT lastTabIndex = ::SendMessage(_hSelf, TCM_GETITEMCOUNT, 0, 0) - 1; if ((wParam & MK_CONTROL) && (wParam & MK_SHIFT)) { ::SendMessage(_hSelf, TCM_SETCURFOCUS, (isForward ? lastTabIndex : 0), 0); } else if (wParam & (MK_CONTROL | MK_SHIFT)) { LRESULT tabIndex = ::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0) + (isForward ? 1 : -1); if (tabIndex < 0) { if (wParam & MK_CONTROL) tabIndex = lastTabIndex; // wrap scrolling else return TRUE; } else if (tabIndex > lastTabIndex) { if (wParam & MK_CONTROL) tabIndex = 0; // wrap scrolling else return TRUE; } ::SendMessage(_hSelf, TCM_SETCURFOCUS, tabIndex, 0); } else if (not _isMultiLine) // don't scroll if in multi-line mode { RECT rcTabCtrl, rcLastTab; ::SendMessage(_hSelf, TCM_GETITEMRECT, lastTabIndex, reinterpret_cast<LPARAM>(&rcLastTab)); ::GetClientRect(_hSelf, &rcTabCtrl); // get index of the first visible tab TC_HITTESTINFO hti; LONG xy = NppParameters::getInstance()->_dpiManager.scaleX(12); // an arbitrary coordinate inside the first visible tab hti.pt = { xy, xy }; LRESULT scrollTabIndex = ::SendMessage(_hSelf, TCM_HITTEST, 0, reinterpret_cast<LPARAM>(&hti)); if (scrollTabIndex < 1 && (_isVertical ? rcLastTab.bottom < rcTabCtrl.bottom : rcLastTab.right < rcTabCtrl.right)) // nothing to scroll return TRUE; // maximal width/height of the msctls_updown32 class (arrow box in the tab bar), // this area may hide parts of the last tab and needs to be excluded LONG maxLengthUpDownCtrl = NppParameters::getInstance()->_dpiManager.scaleX(44); // sufficient static value // scroll forward as long as the last tab is hidden; scroll backward till the first tab if ((_isVertical ? ((rcTabCtrl.bottom - rcLastTab.bottom) < maxLengthUpDownCtrl) : ((rcTabCtrl.right - rcLastTab.right) < maxLengthUpDownCtrl)) || not isForward) { if (isForward) ++scrollTabIndex; else --scrollTabIndex; if (scrollTabIndex < 0 || scrollTabIndex > lastTabIndex) return TRUE; // clear hover state of the close button, // WM_MOUSEMOVE won't handle this properly since the tab position will change if (_isCloseHover) { _isCloseHover = false; ::InvalidateRect(_hSelf, &_currentHoverTabRect, false); } ::SendMessage(_hSelf, WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, scrollTabIndex), 0); } } return TRUE; } case WM_LBUTTONDOWN : { if (_drawTabCloseButton) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); if (_closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) { _whichCloseClickDown = getTabIndexAt(xPos, yPos); ::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_REFRESHTABAR, 0); return TRUE; } } ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); int currentTabOn = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0)); if (wParam == 2) return TRUE; if (_doDragNDrop) { _nSrcTab = _nTabDragged = currentTabOn; POINT point; point.x = LOWORD(lParam); point.y = HIWORD(lParam); ::ClientToScreen(hwnd, &point); if(::DragDetect(hwnd, point)) { // Yes, we're beginning to drag, so capture the mouse... _isDragging = true; ::SetCapture(hwnd); } } TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = NM_CLICK; nmhdr.hdr.idFrom = reinterpret_cast<UINT_PTR>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); return TRUE; } case WM_RBUTTONDOWN : //rightclick selects tab aswell { ::CallWindowProc(_tabBarDefaultProc, hwnd, WM_LBUTTONDOWN, wParam, lParam); return TRUE; } //#define NPPM_INTERNAL_ISDRAGGING 40926 case WM_MOUSEMOVE : { if (_isDragging) { POINT p; p.x = LOWORD(lParam); p.y = HIWORD(lParam); exchangeItemData(p); // Get cursor position of "Screen" // For using the function "WindowFromPoint" afterward!!! ::GetCursorPos(&_draggingPoint); draggingCursor(_draggingPoint); return TRUE; } if (_drawTabCloseButton) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int _currentHoverTabItemOld = _currentHoverTabItem; RECT _currentHoverTabRectOld = _currentHoverTabRect; bool _isCloseHoverOld = _isCloseHover; _currentHoverTabItem = getTabIndexAt(xPos, yPos); if (_currentHoverTabItem != -1) { ::SendMessage(_hSelf, TCM_GETITEMRECT, _currentHoverTabItem, reinterpret_cast<LPARAM>(&_currentHoverTabRect)); _isCloseHover = _closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical); } else { SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; } if (_currentHoverTabItem != _currentHoverTabItemOld || _isCloseHover != _isCloseHoverOld) { if (_isCloseHoverOld && (_currentHoverTabItem != _currentHoverTabItemOld || !_isCloseHover)) InvalidateRect(hwnd, &_currentHoverTabRectOld, FALSE); if (_isCloseHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); } if (_isCloseHover) { TRACKMOUSEEVENT tme = {}; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; TrackMouseEvent(&tme); } } break; } case WM_MOUSELEAVE: { if (_isCloseHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); _currentHoverTabItem = -1; SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; break; } case WM_LBUTTONUP : { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); if (_isDragging) { if(::GetCapture() == _hSelf) ::ReleaseCapture(); // Send a notification message to the parent with wParam = 0, lParam = 0 // nmhdr.idFrom = this // destIndex = this->_nSrcTab // scrIndex = this->_nTabDragged TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = _isDraggingInside?TCN_TABDROPPED:TCN_TABDROPPEDOUTSIDE; nmhdr.hdr.idFrom = reinterpret_cast<UINT_PTR>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); return TRUE; } if (_drawTabCloseButton) { if ((_whichCloseClickDown == currentTabOn) && _closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) { TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = TCN_TABDELETE; nmhdr.hdr.idFrom = reinterpret_cast<UINT_PTR>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); _whichCloseClickDown = -1; return TRUE; } _whichCloseClickDown = -1; } break; } case WM_CAPTURECHANGED : { if (_isDragging) { _isDragging = false; return TRUE; } break; } case WM_DRAWITEM : { drawItem((DRAWITEMSTRUCT *)lParam); return TRUE; } case WM_KEYDOWN : { if (wParam == VK_LCONTROL) ::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_PLUS_TAB))); return TRUE; } case WM_MBUTTONUP: { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = TCN_TABDELETE; nmhdr.hdr.idFrom = reinterpret_cast<UINT_PTR>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); return TRUE; } case WM_LBUTTONDBLCLK: { if (_isDbClk2Close) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); TBHDR nmhdr; nmhdr.hdr.hwndFrom = _hSelf; nmhdr.hdr.code = TCN_TABDELETE; nmhdr.hdr.idFrom = reinterpret_cast<UINT_PTR>(this); nmhdr.tabOrigin = currentTabOn; ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr)); } return TRUE; } } return ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); }
LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { // Custom window message to change tab control style on the fly case WM_TABSETSTYLE: { LONG_PTR style = ::GetWindowLongPtr(hwnd, GWL_STYLE); if (wParam > 0) style |= lParam; else style &= ~lParam; _isVertical = ((style & TCS_VERTICAL) != 0); _isMultiLine = ((style & TCS_MULTILINE) != 0); ::SetWindowLongPtr(hwnd, GWL_STYLE, style); ::InvalidateRect(hwnd, NULL, TRUE); return TRUE; } case WM_MOUSEWHEEL: { // .............................................................................. // MOUSEWHEEL: // will scroll the tab bar area (similar to Firefox's tab scrolling), // it only happens if not in multi-line mode and at least one tab is hidden // .............................................................................. // CTRL + MOUSEWHEEL: // will do previous/next tab WITH scroll wrapping (endless loop) // .............................................................................. // SHIFT + MOUSEWHEEL: // if _doDragNDrop is enabled, then moves the tab, otherwise switches // to previous/next tab WITHOUT scroll wrapping (stops at first/last tab) // .............................................................................. // CTRL + SHIFT + MOUSEWHEEL: // will switch to the first/last tab // .............................................................................. if (_isDragging) return TRUE; const bool isForward = ((short)HIWORD(wParam)) < 0; // wheel rotation towards the user will be considered as forward direction const int lastTabIndex = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETITEMCOUNT, 0, 0) - 1); if ((wParam & MK_CONTROL) && (wParam & MK_SHIFT)) { setActiveTab((isForward ? lastTabIndex : 0)); } else if ((wParam & MK_SHIFT) && _doDragNDrop) { int oldTabIndex = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0)); int newTabIndex = oldTabIndex + (isForward ? 1 : -1); if (newTabIndex < 0) { newTabIndex = lastTabIndex; // wrap scrolling } else if (newTabIndex > lastTabIndex) { newTabIndex = 0; // wrap scrolling } if (oldTabIndex != newTabIndex) { exchangeTabItemData(oldTabIndex, newTabIndex); } } else if (wParam & (MK_CONTROL | MK_SHIFT)) { int tabIndex = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0) + (isForward ? 1 : -1)); if (tabIndex < 0) { if (wParam & MK_CONTROL) tabIndex = lastTabIndex; // wrap scrolling else return TRUE; } else if (tabIndex > lastTabIndex) { if (wParam & MK_CONTROL) tabIndex = 0; // wrap scrolling else return TRUE; } setActiveTab(tabIndex); } else if (not _isMultiLine) // don't scroll if in multi-line mode { RECT rcTabCtrl, rcLastTab; ::SendMessage(_hSelf, TCM_GETITEMRECT, lastTabIndex, reinterpret_cast<LPARAM>(&rcLastTab)); ::GetClientRect(_hSelf, &rcTabCtrl); // get index of the first visible tab TC_HITTESTINFO hti; LONG xy = NppParameters::getInstance()->_dpiManager.scaleX(12); // an arbitrary coordinate inside the first visible tab hti.pt = { xy, xy }; int scrollTabIndex = static_cast<int32_t>(::SendMessage(_hSelf, TCM_HITTEST, 0, reinterpret_cast<LPARAM>(&hti))); if (scrollTabIndex < 1 && (_isVertical ? rcLastTab.bottom < rcTabCtrl.bottom : rcLastTab.right < rcTabCtrl.right)) // nothing to scroll return TRUE; // maximal width/height of the msctls_updown32 class (arrow box in the tab bar), // this area may hide parts of the last tab and needs to be excluded LONG maxLengthUpDownCtrl = NppParameters::getInstance()->_dpiManager.scaleX(44); // sufficient static value // scroll forward as long as the last tab is hidden; scroll backward till the first tab if ((_isVertical ? ((rcTabCtrl.bottom - rcLastTab.bottom) < maxLengthUpDownCtrl) : ((rcTabCtrl.right - rcLastTab.right) < maxLengthUpDownCtrl)) || not isForward) { if (isForward) ++scrollTabIndex; else --scrollTabIndex; if (scrollTabIndex < 0 || scrollTabIndex > lastTabIndex) return TRUE; // clear hover state of the close button, // WM_MOUSEMOVE won't handle this properly since the tab position will change if (_isCloseHover) { _isCloseHover = false; ::InvalidateRect(_hSelf, &_currentHoverTabRect, false); } ::SendMessage(_hSelf, WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, scrollTabIndex), 0); } } return TRUE; } case WM_LBUTTONDOWN : { if (_drawTabCloseButton) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); if (_closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) { _whichCloseClickDown = getTabIndexAt(xPos, yPos); ::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_REFRESHTABAR, 0); return TRUE; } } ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); int currentTabOn = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0)); if (wParam == 2) return TRUE; if (_doDragNDrop) { _mightBeDragging = true; } notify(NM_CLICK, currentTabOn); return TRUE; } case WM_RBUTTONDOWN : //rightclick selects tab aswell { // TCS_BUTTONS doesn't select the tab if (::GetWindowLongPtr(_hSelf, GWL_STYLE) & TCS_BUTTONS) { int nTab = getTabIndexAt(LOWORD(lParam), HIWORD(lParam)); if (nTab != -1 && nTab != static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0))) { setActiveTab(nTab); } } ::CallWindowProc(_tabBarDefaultProc, hwnd, WM_LBUTTONDOWN, wParam, lParam); return TRUE; } case WM_MOUSEMOVE : { if (_mightBeDragging && !_isDragging) { // Grrr! Who has stolen focus and eaten the WM_LBUTTONUP?! if (GetKeyState(VK_LBUTTON) >= 0) { _mightBeDragging = false; _dragCount = 0; } else if (++_dragCount > 2) { int tabFocused = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURFOCUS, 0, 0)); int tabSelected = static_cast<int32_t>(::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0)); // make sure the tab they are moving is active. if (tabFocused != tabSelected) { setActiveTab(tabFocused); } _nSrcTab = _nTabDragged = tabFocused; _isDragging = true; // ::SetCapture is required for normal non-TLS_BUTTONS. if (!(::GetWindowLongPtr(_hSelf, GWL_STYLE) & TCS_BUTTONS)) { ::SetCapture(hwnd); } } } POINT p; p.x = LOWORD(lParam); p.y = HIWORD(lParam); if (_isDragging) { exchangeItemData(p); // Get cursor position of "Screen" // For using the function "WindowFromPoint" afterward!!! ::GetCursorPos(&_draggingPoint); draggingCursor(_draggingPoint); return TRUE; } else { bool isFromTabToTab = false; int iTabNow = getTabIndexAt(p.x, p.y); // _currentHoverTabItem keeps previous value, and it need to be updated if (_currentHoverTabItem == iTabNow && _currentHoverTabItem != -1) // mouse moves arround in the same tab { // do nothing } else if (iTabNow == -1 && _currentHoverTabItem != -1) // mouse is no more on any tab, set hover -1 { _currentHoverTabItem = -1; // send mouse leave notif notify(TCN_MOUSELEAVING, -1); } else if (iTabNow != -1 && _currentHoverTabItem == -1) // mouse is just entered in a tab zone { _currentHoverTabItem = iTabNow; notify(TCN_MOUSEHOVERING, _currentHoverTabItem); } else if (iTabNow != -1 && _currentHoverTabItem != -1 && _currentHoverTabItem != iTabNow) // mouse is being moved from a tab and entering into another tab { isFromTabToTab = true; // set current hovered _currentHoverTabItem = iTabNow; // send mouse enter notif notify(TCN_MOUSEHOVERSWITCHING, _currentHoverTabItem); } else if (iTabNow == -1 && _currentHoverTabItem == -1) // mouse is already outside { // do nothing } if (_drawTabCloseButton) { RECT currentHoverTabRectOld = _currentHoverTabRect; bool isCloseHoverOld = _isCloseHover; if (_currentHoverTabItem != -1) // is hovering { ::SendMessage(_hSelf, TCM_GETITEMRECT, _currentHoverTabItem, reinterpret_cast<LPARAM>(&_currentHoverTabRect)); _isCloseHover = _closeButtonZone.isHit(p.x, p.y, _currentHoverTabRect, _isVertical); } else { SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; } if (isFromTabToTab || _isCloseHover != isCloseHoverOld) { if (isCloseHoverOld && (isFromTabToTab || !_isCloseHover)) InvalidateRect(hwnd, ¤tHoverTabRectOld, FALSE); if (_isCloseHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); } if (_isCloseHover) { // Mouse moves out from close 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); } break; } case WM_MOUSELEAVE: { if (_isCloseHover) InvalidateRect(hwnd, &_currentHoverTabRect, FALSE); _currentHoverTabItem = -1; SetRectEmpty(&_currentHoverTabRect); _isCloseHover = false; notify(TCN_MOUSELEAVING, _currentHoverTabItem); break; } case WM_LBUTTONUP : { _mightBeDragging = false; _dragCount = 0; int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); if (_isDragging) { if(::GetCapture() == _hSelf) ::ReleaseCapture(); notify(_isDraggingInside?TCN_TABDROPPED:TCN_TABDROPPEDOUTSIDE, currentTabOn); return TRUE; } if (_drawTabCloseButton) { if ((_whichCloseClickDown == currentTabOn) && _closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical)) { notify(TCN_TABDELETE, currentTabOn); _whichCloseClickDown = -1; // Get the next tab at same position // If valid tab is found then // update the current hover tab RECT (_currentHoverTabRect) // update close hover flag (_isCloseHover), 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<LPARAM>(&_currentHoverTabRect)); _isCloseHover = _closeButtonZone.isHit(xPos, yPos, _currentHoverTabRect, _isVertical); } return TRUE; } _whichCloseClickDown = -1; } break; } case WM_CAPTURECHANGED : { if (_isDragging) { _isDragging = false; return TRUE; } break; } case WM_DRAWITEM : { drawItem((DRAWITEMSTRUCT *)lParam); return TRUE; } case WM_KEYDOWN : { if (wParam == VK_LCONTROL) ::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_PLUS_TAB))); return TRUE; } case WM_MBUTTONUP: { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); notify(TCN_TABDELETE, currentTabOn); return TRUE; } case WM_LBUTTONDBLCLK: { if (_isDbClk2Close) { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); int currentTabOn = getTabIndexAt(xPos, yPos); notify(TCN_TABDELETE, currentTabOn); } return TRUE; } } return ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam); }