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()函数中的处理方式了.这里也省略了.下次说吧.
- Notepad++源码分析(二)
- Notepad++源码分析(三)
- Notepad源码分析
- Notepad++源码分析(一)
- Notepad++源码分析(一)
- android中notePad示例项目源码分析
- Mangos源码分析(二)
- ConcurrentHashMap 源码分析 (二)
- gSOAP 源码分析(二)
- shopqi源码分析二
- VLC源码分析(二)
- mongodb源码分析(二)
- VLC源码分析(二)
- tomcat源码分析二
- Struts2源码分析二
- gSOAP 源码分析(二)
- TreeMap源码分析二
- jeecms源码分析(二)
- zoj 2001
- ExtJS Web应用程序开发指南(第2版)(针对Ext JS 4.0更新)
- JavaScript中prototype理解
- JDeveloper中的External Tools的使用
- 收藏~~
- Notepad++源码分析(二)
- Android核心分析(22)-----Android应用框架之Activity
- JavaScript中prototype的理解
- linux内核参数修改
- GDAL/OGR
- Android核心分析(23)-----Andoird GDI之基本原理及其总体框架
- Oracle用户配置文件概述
- [小练习]grep练习
- Android核心分析(24)-----Android GDI之显示缓冲管理