Notepad++源码分析(二)

来源:互联网 发布:centos 7 syslog 编辑:程序博客网 时间:2024/04/30 15:08

这次介绍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->_nTabDraggedNMHDR 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 sourceTCITEM 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信息

Determines which tab, if any, is at a specified screen position. You can send this message explicitly or by using the TabCtrl_HitTest macrolResult = 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()函数中的处理方式了.这里也省略了.下次说吧.