这次介绍NotePad++中多标签页下的鼠标拖动标签页位置的功能.
在TabBar.cpp文件中的类处理函数定义如下:
- LRESULT TabBar::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
- {
- switch (Message)
- {
- case WM_LBUTTONDOWN :
- {
- ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam);
-
- if (_doDragNDrop)
- {
- _nSrcTab = _nTabDragged = ::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0);
-
- POINT point;
- point.x = LOWORD(lParam);
- point.y = HIWORD(lParam);
- if(::DragDetect(hwnd, point))
- {
- // Yes, we‘re beginning to drag, so capture the mouse...
- _isDragging = true;
- ::SetCapture(hwnd);
- return TRUE;
- }
- break;
- }
- else
- return TRUE;
- }
-
- 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;
- }
- break;
- }
-
- case WM_LBUTTONUP :
- {
- 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
- NMHDR nmhdr;
- nmhdr.hwndFrom = _hSelf;
- nmhdr.code = _isDraggingInside?TCN_TABDROPPED:TCN_TABDROPPEDOUTSIDE;
- nmhdr.idFrom = reinterpret_cast<unsigned int>(this);
-
- ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&nmhdr));
- return TRUE;
- }
- 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;
- }
- }
- return ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam);
- }
其中WM_LBUTTONDOWN消息的处理方法中,
a. 调用::CallwindowProc函数来用_tabBarDefaultProc这个函数处理WM_LBUTTONDOWN消息(这里的_tabBarDefaultProc的作用就是作为默认的消息处理函数,这个是怎么来的,稍后解释).
b. 如果鼠标处于拖拽动作状态,则调用::SendMessage(_hSelf, TCM_GETCURSEL, 0, 0);获取当前拖拽标签的编号,这里的_hSelf为TabBar实例窗口的句柄.在Notepad++很多窗口对象源码中都有类似的设计实现.言归正 传,向TabBar窗口发送TCM_GETCURSEL消息会返回当前被选择标签(tab)的标记.
- Returns the index of the selected tab if successful, or -1 if no tab is selected.
-
- lResult = SendMessage( // returns LRESULT in lResult
- (HWND) hWndControl, // handle to destination control
- (UINT) TCM_GETCURSEL, // message ID
- (WPARAM) wParam, // = 0; not used, must be zero
- (LPARAM) lParam // = 0; not used, must be zero
- );
c. 将鼠标点击的坐标作为::DragDetect函数的参数传给该函数.::DragDetect函数的作用就是为了判断鼠标点击左键后是否有拖拽的动作.如果对这个函数还有什么疑问可以参阅http://baike.baidu.com/view/1080200.htm.
d. ::DragDetect函数返回为真,则将_isDragging标记为true,表示鼠标处于拖拽动作状态.
e. ::SetCapture(hwnd)函数表示为hwnd表示的窗口绑定鼠标操作.即便绑定后的鼠标光标离开了该窗口,鼠标动作也由该窗口捕获,如有什么疑问可以参阅http://baike.baidu.com/view/1080215.html?fromTaglist
剩下的工作有WM_MOUSEMOVE,WM_LBUTTONUP消息的处理函数来处理.
在WM_MOUSEMOVE消息的处理函数中,会做一下工作
a. 判断鼠标是否处于拖拽动作状态
b. 如果鼠标处于拖拽状态则执行exchangeItemData(p)函数其中p为鼠标在屏幕上的坐标(POINT).exchangeItemData(p)实现如下:
- void TabBar::exchangeItemData(POINT point)
- {
- TCHITTESTINFO hitinfo;
- hitinfo.pt.x = point.x;
- hitinfo.pt.y = point.y;
-
- // Find the destination tab...
- unsigned int nTab = ::SendMessage(_hSelf, TCM_HITTEST, 0, (LPARAM)&hitinfo);
-
- // The position is over a tab.
- if (hitinfo.flags != TCHT_NOWHERE)
- {
-
- _isDraggingInside = true;
-
- if (nTab != _nTabDragged)
- {
- //1. set to focus
- ::SendMessage(_hSelf, TCM_SETCURSEL, nTab, 0);
-
- //2. shift their data, and insert the source
- TCITEM itemData_nDraggedTab, itemData_shift;
- itemData_nDraggedTab.mask = itemData_shift.mask = TCIF_IMAGE | TCIF_TEXT;
- char str1[256];
- char str2[256];
-
- itemData_nDraggedTab.pszText = str1;
- itemData_nDraggedTab.cchTextMax = (sizeof(str1));
-
- itemData_shift.pszText = str2;
- itemData_shift.cchTextMax = (sizeof(str2));
-
- ::SendMessage(_hSelf, TCM_GETITEM, _nTabDragged, reinterpret_cast<LPARAM>(&itemData_nDraggedTab));
-
- if (_nTabDragged > nTab)
- {
- for (int i = _nTabDragged ; i > nTab ; i--)
- {
- ::SendMessage(_hSelf, TCM_GETITEM, i-1, reinterpret_cast<LPARAM>(&itemData_shift));
- ::SendMessage(_hSelf, TCM_SETITEM, i, reinterpret_cast<LPARAM>(&itemData_shift));
- }
- }
- else
- {
- for (int i = _nTabDragged ; i < nTab ; i++)
- {
- ::SendMessage(_hSelf, TCM_GETITEM, i+1, reinterpret_cast<LPARAM>(&itemData_shift));
- ::SendMessage(_hSelf, TCM_SETITEM, i, reinterpret_cast<LPARAM>(&itemData_shift));
- }
- }
- //
- ::SendMessage(_hSelf, TCM_SETITEM, nTab, reinterpret_cast<LPARAM>(&itemData_nDraggedTab));
-
- //3. update the current index
- _nTabDragged = nTab;
-
- }
- }
- else
- {
- //::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_TAB)));
- _isDraggingInside = false;
- }
-
- }
函 数中首先在TCHITTESTINFO结构体中将鼠标坐标参数传入pt.x,pt.y.具体TCHITTESTINFO结构体还是很有意思的,并且通过发 送TCM_HITTEST消息带上这个结构体作为参数.这个用法更有意思.在MSDN上的解释如下,得到指定屏幕位置处得Tab信息
- <pre class="cpp" name="code">Determines which tab, if any, is at a specified screen position. You can send this message explicitly or by using the TabCtrl_HitTest macro
- lResult = SendMessage( // returns LRESULT in lResult
- (HWND) hWndControl, // handle to destination control
- (UINT) TCM_HITTEST, // message ID
- (WPARAM) wParam, // = 0; not used, must be zero
- (LPARAM) lParam // = (LPARAM) (LPTCHITTESTINFO) pinfo;
- );
要想知道具体能够得到标签(tab)的什么状态,必须了解这个TCHITTESTINFO结构体了
- typedef struct tagTCHITTESTINFO {
- POINT pt;
- UINT flags;
- } TCHITTESTINFO, *LPTCHITTESTINFO;
- pt Position to hit test, in client coordinates. flags Variable that receives the results of a hit test. The tab control sets this member to one of the following values:
- TCHT_NOWHERE The position is not over a tab.
- TCHT_ONITEM The position is over a tab but not over its icon or its text. For owner-drawn tab controls, this value is specified if the position is anywhere over a tab.
- TCHT_ONITEMICON The position is over a tab‘s icon.
- TCHT_ONITEMLABEL The position is over a tab‘s text.
- TCHT_ONITEM is a bitwise-OR operation on TCHT_ONITEMICON and TCHT_ONITEMLABEL.
了解了结构体以及消息的作用之后,理解if( hitinfo.flags != TCHT_NOWHERE)这一句的意思就很easy了.
c. 如果鼠标拖拽行为将tab拖动到其他tab位置时,将_isDraggingInside置为true表示启动了鼠标拖拽标签(tab)插入.然后将通过 sendmessage获得鼠标坐标处标签的编号nTab与处理WM_LBUTTONDOWN消息时鼠标左键点击的标签标号_nTabDragged对 比.注意这两个标号的获取方式是不同的.具体怎么不一样,回顾一下前文就晓得了.然后将tabbar空间当前现则的标签标号标记为nTab.
d. 新建两个TCITEM结构体.首先将结构体的mask成员变量设置为TCIF_IMAGE|TCIF_TEXT.即在获得数据,填充这个结构体时,只填充pszText,iImage两个成员变量即可.具体的说法鉴于篇幅的关系可以参考MSDN.
e.然后就是根据_nTabDragged以及nTab之间的关系,采用TCM_GETITEM,TCM_SETITEM来类似于插入排序的方式,将鼠标拖拽的标签插入的指定的位置上.
f.为了方便插入操作的循环进行将成员变量_nTabDragged = nTab.
到这里只是实现了标签中TEXT和Image的替换,剩下的工作就是根据鼠标的位置,给光标附上不同的cursor图标,关键函数为draggingCursor(_draggingPoing).定义如下:
- void TabBar::draggingCursor(POINT screenPoint)
- {
- HWND hWin = ::WindowFromPoint(screenPoint);
- if (_hSelf == hWin)
- ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
- else
- {
- char className[256];
- ::GetClassName(hWin, className, 256);
- if ((!strcmp(className, "Scintilla")) || (!strcmp(className, WC_TABCONTROL)))
- {
- if (::GetKeyState(VK_LCONTROL) & 0x80000000)
- ::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_PLUS_TAB)));
- else
- ::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_TAB)));
- }
- else
- ::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_DRAG_INTERDIT_TAB)));
- }
- }
在这里我觉得比较有价值的知识点就是,::WindowFromPoint(screenPoint);他可以根据屏幕坐标返回改位置处窗口控件句柄.(类似于spy++的功能了).剩下的代码我不说你应该看的懂吧.哈哈~~
最后WM_LBUTTONUP消息的处理函数就更好理解了.
a. 取消拖拽状态时绑定鼠标动作的控件
b. 通知父窗口此处的鼠标拖拽的细节.这里的处理方式就是Notepad_plus.cpp中notify()函数中的处理方式了.这里也省略了.下次说吧.
Notepad++源码分析(2)(转载),布布扣,bubuko.com
Notepad++源码分析(2)(转载)
原文:http://www.cnblogs.com/hxb316/p/3851632.html