Beispiel #1
0
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);
}
Beispiel #2
0
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, &currentHoverTabRectOld, 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);
}