GME
13
|
00001 00002 // 00003 // CTreeCtrlEx - Multiple selection tree control for MFC 00004 // 00005 // Copyright © 1997-2003 Bendik Engebretsen 00006 // bendik@techsoft.no 00007 // http://www.techsoft.no/bendik/ 00008 // 00009 // Oct 9, 1997 : Fixed problem with notification to parent (TVN_BEGINDRAG) 00010 // Oct 17, 1997 : Fixed bug with deselection when collapsing node with no sibling 00011 // Nov 5, 1997 : Fixed problem with label editing 00012 // Feb 17, 1998 : Fixed another notfication to parent (TVN_KEYDOWN) 00013 // Apr 27, 1998 : Added TVN_SELCHANGED notification to parent in SelectMultiple() 00014 // Dec 21, 1998 : Fixed incorrect use of LVHT_... constants. Now using TVHT_... 00015 // Mar 15, 1999 : Fixed problems when there is no selected item. 00016 // Fixed new problem with label editing 00017 // Apr 16, 1999 : Fixed problem with double TVN_SELCHANGED notifications 00018 // Many thanx to Roel Schroeven for providing the solution!! 00019 // Jul 2, 1999 : Added TVN_SELCHANGING notification when selecting/deselecting 00020 // selected item 00021 // Oct 11, 1999 : Fixed quirks with Shift+Arrow Key selection. Thanx to Yariv Elani ! 00022 // Also changed behaviour of Ctrl+Mouse click selection: The item is 00023 // unselected, but NOT 'focused' (i.e. same behaviour as a listctrl). 00024 // Nov 16, 1999 : Fixed another quirk with Ctrl+Mouse click selection. Thanks to 00025 // Yariv Ben-Tovim! 00026 // May 21, 2001 : Now supports Shift PgUp and PgDn selection. Thanks to Sergei 00027 // Antonov. Also fixed the too early TVN_SELCHANGED when key-selecting. 00028 // May 31, 2001 : Fixed bug with uninitialized m_ptClick in OnLButtonDown. Thanks to 00029 // Cristian Rodriguez! 00030 // Jun 20, 2001 : Fixed quirk with label editing/doubleclick. Once more, thanks 00031 // to Cristian Rodriguez. Also added treectrl wndclass registration. 00032 // Jan 24, 2003 : Fixed bug when treeview has the TVS_DISABLEDRAGDROP style. Thanks to 00033 // Fernanda Diniz Tavarez 00034 // Mar 20, 2003 : Fixed 'blinking' problem with TVS_SINGLEEXPAND style. Thanks to 00035 // H.-Joachim Riedel. 00036 // Jun 10, 2003 : Migrated to Visual C++.NET / MFC 7.X 00037 00038 #include "stdafx.h" 00039 #include "TreeCtrlEx.h" 00040 00041 #ifdef _DEBUG 00042 #define new DEBUG_NEW 00043 #undef THIS_FILE 00044 static char THIS_FILE[] = __FILE__; 00045 #endif 00046 00047 #define TCEX_EDITLABEL 1 // Edit label timer event 00048 00050 // CTreeCtrlEx 00051 00052 CTreeCtrlEx::CTreeCtrlEx() : m_bSelectPending(FALSE), m_hClickedItem(NULL), m_hFirstSelectedItem(NULL), m_bSelectionComplete(TRUE), m_bEditLabelPending(FALSE) 00053 { 00054 //m_pchTip = NULL; 00055 //m_pwchTip = NULL; 00056 } 00057 00058 CTreeCtrlEx::~CTreeCtrlEx() 00059 { 00060 /*if(m_pwchTip != NULL) 00061 delete m_pwchTip; 00062 00063 if(m_pchTip != NULL) 00064 delete m_pchTip;*/ 00065 } 00066 00067 BEGIN_MESSAGE_MAP(CTreeCtrlEx, CTreeCtrl) 00068 //{{AFX_MSG_MAP(CTreeCtrlEx) 00069 //ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText) 00070 //ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText) 00071 ON_WM_LBUTTONDOWN() 00072 ON_WM_LBUTTONUP() 00073 ON_WM_MOUSEMOVE() 00074 ON_WM_KEYDOWN() 00075 ON_NOTIFY_REFLECT_EX(TVN_ITEMEXPANDING, OnItemexpanding) 00076 ON_NOTIFY_REFLECT_EX(NM_SETFOCUS, OnSetfocus) 00077 ON_NOTIFY_REFLECT_EX(NM_KILLFOCUS, OnKillfocus) 00078 ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelchanged) 00079 ON_WM_RBUTTONDOWN() 00080 ON_WM_RBUTTONUP() 00081 ON_WM_LBUTTONDBLCLK() 00082 ON_WM_TIMER() 00083 ON_WM_NCHITTEST() 00084 //}}AFX_MSG_MAP 00085 END_MESSAGE_MAP() 00086 00087 IMPLEMENT_DYNAMIC(CTreeCtrlEx, CTreeCtrl) 00088 00089 BOOL CTreeCtrlEx::Create(DWORD dwStyle, DWORD dwExStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) 00090 { 00091 return CTreeCtrl::CreateEx( dwExStyle, dwStyle, rect, pParentWnd, nID ); 00092 } 00093 00094 BOOL CTreeCtrlEx::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) 00095 { 00096 return CTreeCtrl::Create(dwStyle, rect, pParentWnd, nID); 00097 } 00098 00099 00101 // CTreeCtrlEx message handlers 00102 00103 void CTreeCtrlEx::PreSubclassWindow() 00104 { 00105 CTreeCtrl::PreSubclassWindow(); 00106 //EnableToolTips(TRUE); 00107 } 00108 00109 /*int CTreeCtrlEx::OnToolHitTest(CPoint point, TOOLINFO * pTI) const 00110 { 00111 RECT rect; 00112 00113 UINT nFlags; 00114 HTREEITEM hitem = HitTest( point, &nFlags ); 00115 if( nFlags & TVHT_ONITEM) 00116 { 00117 GetItemRect( hitem, &rect, TRUE ); 00118 pTI->hwnd = m_hWnd; 00119 pTI->uId = (UINT)hitem; 00120 pTI->lpszText = LPSTR_TEXTCALLBACK; 00121 pTI->rect = rect; 00122 return pTI->uId; 00123 } 00124 00125 return -1; 00126 }*/ 00127 00128 /*BOOL CTreeCtrlEx::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ) 00129 { 00130 // need to handle both ANSI and UNICODE versions of the message 00131 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; 00132 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; 00133 CString strTipText; 00134 UINT nID = pNMHDR->idFrom; 00135 00136 // Do not process the message from built in tooltip 00137 if( nID == (UINT)m_hWnd && 00138 (( pNMHDR->code == TTN_NEEDTEXTA && pTTTA->uFlags & TTF_IDISHWND ) || 00139 ( pNMHDR->code == TTN_NEEDTEXTW && pTTTW->uFlags & TTF_IDISHWND ) ) ) 00140 return FALSE; 00141 00142 // Get the mouse position 00143 const MSG* pMessage; 00144 CPoint pt; 00145 pMessage = GetCurrentMessage(); 00146 ASSERT ( pMessage ); 00147 pt = pMessage->pt; 00148 ScreenToClient( &pt ); 00149 00150 UINT nFlags; 00151 HTREEITEM hitem = HitTest( pt, &nFlags ); 00152 if( nFlags & TVHT_ONITEM) 00153 { 00154 CString strTxt = GetItemText((HTREEITEM)nID); 00155 strTipText = strTxt; 00156 } 00157 00158 #ifndef _UNICODE 00159 if(pNMHDR->code == TTN_NEEDTEXTA) 00160 { 00161 if(m_pchTip != NULL) 00162 delete m_pchTip; 00163 00164 m_pchTip = new TCHAR[strTipText.GetLength()+1]; 00165 lstrcpyn(m_pchTip, strTipText, strTipText.GetLength()); 00166 m_pchTip[strTipText.GetLength()] = 0; 00167 pTTTW->lpszText = (WCHAR*)m_pchTip; 00168 } 00169 else 00170 { 00171 if(m_pwchTip != NULL) 00172 delete m_pwchTip; 00173 00174 m_pwchTip = new WCHAR[strTipText.GetLength()+1]; 00175 _mbstowcsz(m_pwchTip, strTipText, strTipText.GetLength()); 00176 m_pwchTip[strTipText.GetLength()] = 0; // end of text 00177 pTTTW->lpszText = (WCHAR*)m_pwchTip; 00178 } 00179 #else 00180 if(pNMHDR->code == TTN_NEEDTEXTA) 00181 { 00182 if(m_pchTip != NULL) 00183 delete m_pchTip; 00184 00185 m_pchTip = new TCHAR[strTipText.GetLength()+1]; 00186 _wcstombsz(m_pchTip, strTipText, strTipText.GetLength()); 00187 m_pchTip[strTipText.GetLength()] = 0; // end of text 00188 pTTTA->lpszText = (LPTSTR)m_pchTip; 00189 } 00190 else 00191 { 00192 if(m_pwchTip != NULL) 00193 delete m_pwchTip; 00194 00195 m_pwchTip = new WCHAR[strTipText.GetLength()+1]; 00196 lstrcpyn(m_pwchTip, strTipText, strTipText.GetLength()); 00197 m_pwchTip[strTipText.GetLength()] = 0; 00198 pTTTA->lpszText = (LPTSTR) m_pwchTip; 00199 } 00200 #endif 00201 00202 *pResult = 0; 00203 00204 return TRUE; // message was handled 00205 }*/ 00206 00208 // The tree control dosn't support multiple selection. However we can simulate 00209 // it by taking control of the left mouse click and arrow key press before the 00210 // control gets them, and setting/clearing the TVIS_SELECTED style on the items 00211 00212 void CTreeCtrlEx::OnLButtonDown( UINT nFlags, CPoint point ) 00213 { 00214 00215 UINT nHitFlags = 0; 00216 HTREEITEM hClickedItem = HitTest( point, &nHitFlags ); 00217 00218 // Must invoke label editing explicitly. The base class OnLButtonDown would normally 00219 // do this, but we can't call it here because of the multiple selection... 00220 if( !( nFlags&( MK_CONTROL|MK_SHIFT ) ) && ( GetStyle() & TVS_EDITLABELS ) && ( nHitFlags & TVHT_ONITEMLABEL ) ) 00221 if ( hClickedItem == GetSelectedItem() ) 00222 { 00223 // Clear multple selection before label editing 00224 ClearSelection(); 00225 SelectItem( hClickedItem ); 00226 00227 // Invoke label editing 00228 m_bEditLabelPending = TRUE; 00229 m_idTimer = SetTimer(TCEX_EDITLABEL, GetDoubleClickTime(), NULL); 00230 00231 return; 00232 } 00233 00234 m_bEditLabelPending = FALSE; 00235 00236 if( nHitFlags & TVHT_ONITEM ) 00237 { 00238 SetFocus(); 00239 00240 m_hClickedItem = hClickedItem; 00241 00242 // Is the clicked item already selected ? 00243 BOOL bIsClickedItemSelected = GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED; 00244 00245 if ( bIsClickedItemSelected ) 00246 { 00247 // Maybe user wants to drag/drop multiple items! 00248 // So, wait until OnLButtonUp() to do the selection stuff. 00249 m_bSelectPending=TRUE; 00250 } 00251 else 00252 { 00253 SelectMultiple( hClickedItem, nFlags, point ); 00254 m_bSelectPending=FALSE; 00255 } 00256 00257 m_ptClick=point; 00258 } 00259 else 00260 { 00261 // Modified: 01/29/2002 Tihamer Levendovszky//////// 00262 00263 if(nHitFlags&TVHT_NOWHERE || nHitFlags&TVHT_ONITEMRIGHT ) 00264 { 00265 ClearSelection(); 00266 } 00268 CTreeCtrl::OnLButtonDown( nFlags, point ); 00269 } 00270 } 00271 00272 void CTreeCtrlEx::OnLButtonUp( UINT nFlags, CPoint point ) 00273 { 00274 if ( m_bSelectPending ) 00275 { 00276 // A select has been waiting to be performed here 00277 SelectMultiple( m_hClickedItem, nFlags, point ); 00278 m_bSelectPending=FALSE; 00279 } 00280 00281 m_hClickedItem=NULL; 00282 00283 CTreeCtrl::OnLButtonUp( nFlags, point ); 00284 } 00285 00286 00287 void CTreeCtrlEx::OnMouseMove( UINT nFlags, CPoint point ) 00288 { 00289 // If there is a select pending, check if cursor has moved so much away from the 00290 // down-click point that we should cancel the pending select and initiate 00291 // a drag/drop operation instead! 00292 00293 // FIXME: need to look at NC too, since if the user moves fast enough, we won't get this message 00294 if ( m_hClickedItem ) 00295 { 00296 CSize sizeMoved = m_ptClick-point; 00297 00298 if ( abs(sizeMoved.cx) > GetSystemMetrics( SM_CXDRAG ) || abs(sizeMoved.cy) > GetSystemMetrics( SM_CYDRAG ) ) 00299 { 00300 m_bSelectPending=FALSE; 00301 00302 // Notify parent that he may begin drag operation 00303 // Since we have taken over OnLButtonDown(), the default handler doesn't 00304 // do the normal work when clicking an item, so we must provide our own 00305 // TVN_BEGINDRAG notification for the parent! 00306 00307 CWnd* pWnd = GetParent(); 00308 if ( pWnd && !( GetStyle() & TVS_DISABLEDRAGDROP ) ) 00309 { 00310 NM_TREEVIEW tv; 00311 00312 tv.hdr.hwndFrom = GetSafeHwnd(); 00313 tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00314 tv.hdr.code = TVN_BEGINDRAG; 00315 00316 tv.itemNew.hItem = m_hClickedItem; 00317 tv.itemNew.state = GetItemState( m_hClickedItem, 0xffffffff ); 00318 tv.itemNew.lParam = GetItemData( m_hClickedItem ); 00319 00320 tv.ptDrag.x = point.x; 00321 tv.ptDrag.y = point.y; 00322 00323 pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); 00324 } 00325 00326 m_hClickedItem=NULL; 00327 } 00328 } 00329 00330 CTreeCtrl::OnMouseMove( nFlags, point ); 00331 } 00332 00333 00334 void CTreeCtrlEx::SelectMultiple( HTREEITEM hClickedItem, UINT nFlags, CPoint point ) 00335 { 00336 // Start preparing an NM_TREEVIEW struct to send a notification after selection is done 00337 NM_TREEVIEW tv; 00338 memset(&tv.itemOld, 0, sizeof(tv.itemOld)); 00339 00340 CWnd* pWnd = GetParent(); 00341 00342 HTREEITEM hOldItem = GetSelectedItem(); 00343 00344 if ( hOldItem ) 00345 { 00346 tv.itemOld.hItem = hOldItem; 00347 tv.itemOld.state = GetItemState( hOldItem, 0xffffffff ); 00348 tv.itemOld.lParam = GetItemData( hOldItem ); 00349 tv.itemOld.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00350 } 00351 00352 // Flag signaling that selection process is NOT complete. 00353 // (Will prohibit TVN_SELCHANGED from being sent to parent) 00354 m_bSelectionComplete = FALSE; 00355 00356 // Action depends on whether the user holds down the Shift or Ctrl key 00357 if ( nFlags & MK_SHIFT ) 00358 { 00359 // Select from first selected item to the clicked item 00360 if ( !m_hFirstSelectedItem ) 00361 m_hFirstSelectedItem = GetSelectedItem(); 00362 00363 SelectItems( m_hFirstSelectedItem, hClickedItem ); 00364 } 00365 else if ( nFlags & MK_CONTROL ) 00366 { 00367 // Find which item is currently selected 00368 HTREEITEM hSelectedItem = GetSelectedItem(); 00369 00370 // Is the clicked item already selected ? 00371 BOOL bIsClickedItemSelected = GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED; 00372 BOOL bIsSelectedItemSelected = FALSE; 00373 if ( hSelectedItem ) 00374 bIsSelectedItemSelected = GetItemState( hSelectedItem, TVIS_SELECTED ) & TVIS_SELECTED; 00375 00376 // Must synthesize a TVN_SELCHANGING notification 00377 if ( pWnd ) 00378 { 00379 tv.hdr.hwndFrom = GetSafeHwnd(); 00380 tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00381 tv.hdr.code = TVN_SELCHANGING; 00382 00383 tv.itemNew.hItem = hClickedItem; 00384 tv.itemNew.state = GetItemState( hClickedItem, 0xffffffff ); 00385 tv.itemNew.lParam = GetItemData( hClickedItem ); 00386 00387 tv.itemOld.hItem = NULL; 00388 tv.itemOld.mask = 0; 00389 00390 tv.action = TVC_BYMOUSE; 00391 00392 tv.ptDrag.x = point.x; 00393 tv.ptDrag.y = point.y; 00394 00395 pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); 00396 } 00397 00398 // If the previously selected item was selected, re-select it 00399 if ( bIsSelectedItemSelected ) 00400 SetItemState( hSelectedItem, TVIS_SELECTED, TVIS_SELECTED ); 00401 00402 // We want the newly selected item to toggle its selected state, 00403 // so unselect now if it was already selected before 00404 if ( bIsClickedItemSelected ) 00405 SetItemState( hClickedItem, 0, TVIS_SELECTED ); 00406 else 00407 { 00408 SetItemState( hClickedItem, TVIS_SELECTED, TVIS_SELECTED ); 00409 } 00410 00411 // If the previously selected item was selected, re-select it 00412 if ( bIsSelectedItemSelected && hSelectedItem != hClickedItem ) 00413 SetItemState( hSelectedItem, TVIS_SELECTED, TVIS_SELECTED ); 00414 00415 // Store as first selected item (if not already stored) 00416 if ( m_hFirstSelectedItem==NULL ) 00417 m_hFirstSelectedItem = hClickedItem; 00418 } 00419 else 00420 { 00421 // Clear selection of all "multiple selected" items first 00422 ClearSelection(true); 00423 00424 // Then select the clicked item 00425 SelectItem( hClickedItem ); 00426 SetItemState( hClickedItem, TVIS_SELECTED, TVIS_SELECTED ); 00427 00428 // Store as first selected item 00429 m_hFirstSelectedItem = hClickedItem; 00430 } 00431 00432 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED 00433 // notification provided by Windows' treectrl, we must now produce one ourselves, 00434 // so that our parent gets to know about the change of selection. 00435 m_bSelectionComplete = TRUE; 00436 00437 if ( pWnd ) 00438 { 00439 tv.hdr.hwndFrom = GetSafeHwnd(); 00440 tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00441 tv.hdr.code = TVN_SELCHANGED; 00442 00443 tv.itemNew.hItem = m_hClickedItem; 00444 tv.itemNew.state = GetItemState( m_hClickedItem, 0xffffffff ); 00445 tv.itemNew.lParam = GetItemData( m_hClickedItem ); 00446 tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00447 00448 tv.action = TVC_UNKNOWN; 00449 00450 pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); 00451 } 00452 } 00453 00454 void CTreeCtrlEx::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ) 00455 { 00456 CWnd* pWnd = GetParent(); 00457 00458 if ( nChar==VK_NEXT || nChar==VK_PRIOR ) 00459 { 00460 if ( !( GetKeyState( VK_SHIFT )&0x8000 ) ) 00461 { 00462 // User pressed Pg key without holding 'Shift': 00463 // Clear multiple selection (if multiple) and let base class do 00464 // normal selection work! 00465 if ( GetSelectedCount()>1 ) 00466 ClearSelection( TRUE ); 00467 00468 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00469 m_hFirstSelectedItem = GetSelectedItem(); 00470 return; 00471 } 00472 00473 // Flag signaling that selection process is NOT complete. 00474 // (Will prohibit TVN_SELCHANGED from being sent to parent) 00475 m_bSelectionComplete = FALSE; 00476 00477 // Let base class select the item 00478 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00479 HTREEITEM hSelectedItem = GetSelectedItem(); 00480 00481 // Then select items in between 00482 SelectItems( m_hFirstSelectedItem, hSelectedItem ); 00483 00484 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED 00485 // notification provided by Windows' treectrl, we must now produce one ourselves, 00486 // so that our parent gets to know about the change of selection. 00487 m_bSelectionComplete = TRUE; 00488 00489 if (pWnd) 00490 { 00491 NM_TREEVIEW tv; 00492 memset(&tv.itemOld, 0, sizeof(tv.itemOld)); 00493 00494 tv.hdr.hwndFrom = GetSafeHwnd(); 00495 tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID); 00496 tv.hdr.code = TVN_SELCHANGED; 00497 00498 tv.itemNew.hItem = hSelectedItem; 00499 tv.itemNew.state = GetItemState(hSelectedItem, 0xffffffff); 00500 tv.itemNew.lParam = GetItemData(hSelectedItem); 00501 tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00502 00503 tv.action = TVC_UNKNOWN; 00504 00505 pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv); 00506 } 00507 } 00508 else if ( nChar==VK_UP || nChar==VK_DOWN ) 00509 { 00510 // Find which item is currently selected 00511 HTREEITEM hSelectedItem = GetSelectedItem(); 00512 00513 HTREEITEM hNextItem; 00514 if ( nChar==VK_UP ) 00515 hNextItem = GetPrevVisibleItem( hSelectedItem ); 00516 else 00517 hNextItem = GetNextVisibleItem( hSelectedItem ); 00518 00519 if ( !( GetKeyState( VK_SHIFT )&0x8000 ) ) 00520 { 00521 // User pressed arrow key without holding 'Shift': 00522 // Clear multiple selection (if multiple) and let base class do 00523 // normal selection work! 00524 if ( GetSelectedCount()>1 ) 00525 ClearSelection( TRUE ); 00526 00527 if ( hNextItem ) 00528 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00529 m_hFirstSelectedItem = GetSelectedItem(); 00530 return; 00531 } 00532 00533 if ( hNextItem ) 00534 { 00535 // Flag signaling that selection process is NOT complete. 00536 // (Will prohibit TVN_SELCHANGED from being sent to parent) 00537 m_bSelectionComplete = FALSE; 00538 00539 // If the next item is already selected, we assume user is 00540 // "moving back" in the selection, and thus we should clear 00541 // selection on the previous one 00542 BOOL bSelect = !( GetItemState( hNextItem, TVIS_SELECTED ) & TVIS_SELECTED ); 00543 00544 // Select the next item (this will also deselect the previous one!) 00545 SelectItem( hNextItem ); 00546 00547 // Now, re-select the previously selected item 00548 if ( bSelect || ( !( GetItemState( hSelectedItem, TVIS_SELECTED ) & TVIS_SELECTED ) ) ) 00549 SelectItems( m_hFirstSelectedItem, hNextItem ); 00550 00551 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED 00552 // notification provided by Windows' treectrl, we must now produce one ourselves, 00553 // so that our parent gets to know about the change of selection. 00554 m_bSelectionComplete = TRUE; 00555 00556 if (pWnd) 00557 { 00558 NM_TREEVIEW tv; 00559 memset(&tv.itemOld, 0, sizeof(tv.itemOld)); 00560 00561 tv.hdr.hwndFrom = GetSafeHwnd(); 00562 tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID); 00563 tv.hdr.code = TVN_SELCHANGED; 00564 00565 tv.itemNew.hItem = hNextItem; 00566 tv.itemNew.state = GetItemState(hNextItem, 0xffffffff); 00567 tv.itemNew.lParam = GetItemData(hNextItem); 00568 tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00569 00570 tv.action = TVC_UNKNOWN; 00571 00572 pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv); 00573 } 00574 } 00575 00576 // Since the base class' OnKeyDown() isn't called in this case, 00577 // we must provide our own TVN_KEYDOWN notification to the parent 00578 00579 CWnd* pWnd = GetParent(); 00580 if ( pWnd ) 00581 { 00582 NMTVKEYDOWN tvk; 00583 00584 tvk.hdr.hwndFrom = GetSafeHwnd(); 00585 tvk.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00586 tvk.hdr.code = TVN_KEYDOWN; 00587 00588 tvk.wVKey = nChar; 00589 tvk.flags = 0; 00590 00591 pWnd->SendMessage( WM_NOTIFY, tvk.hdr.idFrom, (LPARAM)&tvk ); 00592 } 00593 } 00594 else 00595 // Behave normally 00596 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00597 } 00598 00599 00601 // I want clicking on an item with the right mouse button to select the item, 00602 // but not if there is currently a multiple selection 00603 00604 void CTreeCtrlEx::OnRButtonDown( UINT nFlags, CPoint point ) 00605 { 00606 UINT nHitFlags = 0; 00607 HTREEITEM hClickedItem = HitTest( point, &nHitFlags ); 00608 00609 if( nHitFlags&TVHT_ONITEM ) 00610 { 00611 if ( GetSelectedCount()<2 ) 00612 { 00613 SelectItem( hClickedItem ); 00614 } 00615 // Modified: 01/26/2002 Tihamer Levendovszky 00616 else 00617 { 00618 if(!( GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED )) 00619 { 00620 ClearSelection(TRUE); 00621 SelectItem( hClickedItem ); 00622 } 00623 } 00624 m_hClickedItem = hClickedItem; 00625 m_ptClick = point; 00626 } 00627 // Modified: 01/26/2002 Tihamer Levendovszky 00628 else 00629 { 00630 ClearSelection(); 00631 } 00632 00633 // Modified: 01/26/2002 Tihamer Levendovszky 00634 /* CTreeCtrl::OnRButtonDown( nFlags, point ); */ 00635 } 00636 00637 void CTreeCtrlEx::OnRButtonUp(UINT nFlags, CPoint point) 00638 { 00639 m_hClickedItem = NULL; 00640 __super::OnRButtonUp(nFlags, point); 00641 } 00642 00643 00645 // Get number of selected items 00646 00647 UINT CTreeCtrlEx::GetSelectedCount() const 00648 { 00649 // Only visible items should be selected! 00650 UINT uCount=0; 00651 for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) ) 00652 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00653 uCount++; 00654 00655 return uCount; 00656 } 00657 00658 00660 // Overloaded to catch our own special code 00661 00662 HTREEITEM CTreeCtrlEx::GetNextItem(HTREEITEM hItem, UINT nCode) 00663 { 00664 if (nCode==TVGN_EX_ALL) 00665 { 00666 // This special code lets us iterate through ALL tree items regardless 00667 // of their parent/child relationship (very handy) 00668 HTREEITEM hNextItem; 00669 00670 // If it has a child node, this will be the next item 00671 hNextItem = GetChildItem( hItem ); 00672 if (hNextItem) 00673 return hNextItem; 00674 00675 // Otherwise, see if it has a next sibling item 00676 hNextItem = GetNextSiblingItem(hItem); 00677 if (hNextItem) 00678 return hNextItem; 00679 00680 // Finally, look for next sibling to the parent item 00681 HTREEITEM hParentItem=hItem; 00682 while (!hNextItem && hParentItem) 00683 { 00684 // No more children: Get next sibling to parent 00685 hParentItem = GetParentItem(hParentItem); 00686 hNextItem = GetNextSiblingItem(hParentItem); 00687 } 00688 00689 return hNextItem; // will return NULL if no more parents 00690 } 00691 else 00692 return CTreeCtrl::GetNextItem(hItem, nCode); // standard processing 00693 } 00694 00696 // Helpers to list out selected items. (Use similar to GetFirstVisibleItem(), 00697 // GetNextVisibleItem() and GetPrevVisibleItem()!) 00698 00699 HTREEITEM CTreeCtrlEx::GetFirstSelectedItem() 00700 { 00701 for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) ) 00702 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00703 return hItem; 00704 00705 return NULL; 00706 } 00707 00708 HTREEITEM CTreeCtrlEx::GetNextSelectedItem( HTREEITEM hItem ) 00709 { 00710 for ( hItem = GetNextVisibleItem( hItem ); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) ) 00711 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00712 return hItem; 00713 00714 return NULL; 00715 } 00716 00717 HTREEITEM CTreeCtrlEx::GetPrevSelectedItem( HTREEITEM hItem ) 00718 { 00719 for ( hItem = GetPrevVisibleItem( hItem ); hItem!=NULL; hItem = GetPrevVisibleItem( hItem ) ) 00720 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00721 return hItem; 00722 00723 return NULL; 00724 } 00725 00726 00728 // Select/unselect item without unselecting other items 00729 00730 BOOL CTreeCtrlEx::SelectItemEx(HTREEITEM hItem, BOOL bSelect/*=TRUE*/) 00731 { 00732 HTREEITEM hSelItem = GetSelectedItem(); 00733 00734 if ( hItem==hSelItem ) 00735 { 00736 if ( !bSelect ) 00737 { 00738 SelectItem( NULL ); 00739 return TRUE; 00740 } 00741 00742 return FALSE; 00743 } 00744 00745 SelectItem( hItem ); 00746 m_hFirstSelectedItem=hItem; 00747 00748 // Reselect previous "real" selected item which was unselected byt SelectItem() 00749 if ( hSelItem ) 00750 SetItemState( hSelItem, TVIS_SELECTED, TVIS_SELECTED ); 00751 00752 return TRUE; 00753 } 00754 00756 // Select visible items between specified 'from' and 'to' item (including these!) 00757 // If the 'to' item is above the 'from' item, it traverses the tree in reverse 00758 // direction. Selection on other items is cleared! 00759 00760 BOOL CTreeCtrlEx::SelectItems( HTREEITEM hFromItem, HTREEITEM hToItem ) 00761 { 00762 // Determine direction of selection 00763 // (see what item comes first in the tree) 00764 HTREEITEM hItem = GetRootItem(); 00765 00766 while ( hItem && hItem!=hFromItem && hItem!=hToItem ) 00767 hItem = GetNextVisibleItem( hItem ); 00768 00769 if ( !hItem ) 00770 return FALSE; // Items not visible in tree 00771 00772 BOOL bReverse = hItem==hToItem; 00773 00774 // "Really" select the 'to' item (which will deselect 00775 // the previously selected item) 00776 00777 SelectItem( hToItem ); 00778 00779 // Go through all visible items again and select/unselect 00780 00781 hItem = GetRootItem(); 00782 BOOL bSelect = FALSE; 00783 00784 while ( hItem ) 00785 { 00786 if ( hItem == ( bReverse ? hToItem : hFromItem ) ) 00787 bSelect = TRUE; 00788 00789 if ( bSelect ) 00790 { 00791 if ( !( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) ) 00792 SetItemState( hItem, TVIS_SELECTED, TVIS_SELECTED ); 00793 } 00794 else 00795 { 00796 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00797 SetItemState( hItem, 0, TVIS_SELECTED ); 00798 } 00799 00800 if ( hItem == ( bReverse ? hFromItem : hToItem ) ) 00801 bSelect = FALSE; 00802 00803 hItem = GetNextVisibleItem( hItem ); 00804 } 00805 00806 return TRUE; 00807 } 00808 00809 00811 // Clear selected state on all visible items 00812 00813 void CTreeCtrlEx::ClearSelection(BOOL bMultiOnly/*=FALSE*/) 00814 { 00815 // TZS - entropia decreased 00816 if ( !bMultiOnly ) 00817 return; 00818 // SelectItem( NULL ); 00819 00820 for ( HTREEITEM hItem=GetRootItem(); hItem!=NULL; hItem=GetNextVisibleItem( hItem ) ) 00821 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00822 SetItemState( hItem, 0, TVIS_SELECTED ); 00823 SelectItem( NULL ); 00824 } 00825 00826 00828 // If a node is collapsed, we should clear selections of its child items 00829 00830 BOOL CTreeCtrlEx::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult) 00831 { 00832 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; 00833 00834 if ( pNMTreeView->action == TVE_COLLAPSE ) 00835 { 00836 HTREEITEM hItem = GetChildItem( pNMTreeView->itemNew.hItem ); 00837 00838 while ( hItem ) 00839 { 00840 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00841 SetItemState( hItem, 0, TVIS_SELECTED ); 00842 00843 // Get the next node: First see if current node has a child 00844 HTREEITEM hNextItem = GetChildItem( hItem ); 00845 if ( !hNextItem ) 00846 { 00847 // No child: Get next sibling item 00848 if ( !( hNextItem = GetNextSiblingItem( hItem ) ) ) 00849 { 00850 HTREEITEM hParentItem = hItem; 00851 while ( !hNextItem ) 00852 { 00853 // No more children: Get parent 00854 if ( !( hParentItem = GetParentItem( hParentItem ) ) ) 00855 break; 00856 00857 // Quit when parent is the collapsed node 00858 // (Don't do anything to siblings of this) 00859 if ( hParentItem == pNMTreeView->itemNew.hItem ) 00860 break; 00861 00862 // Get next sibling to parent 00863 hNextItem = GetNextSiblingItem( hParentItem ); 00864 } 00865 00866 // Quit when parent is the collapsed node 00867 if ( hParentItem == pNMTreeView->itemNew.hItem ) 00868 break; 00869 } 00870 } 00871 00872 hItem = hNextItem; 00873 } 00874 } 00875 00876 *pResult = 0; 00877 return FALSE; // Allow parent to handle this notification as well 00878 } 00879 00880 00882 // Intercept TVN_SELCHANGED and pass it only to the parent window of the 00883 // selection process is finished 00884 00885 BOOL CTreeCtrlEx::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 00886 { 00887 // Return TRUE if selection is not complete. This will prevent the 00888 // notification from being sent to parent. 00889 return !m_bSelectionComplete; 00890 } 00891 00892 00894 // Ensure the multiple selected items are drawn correctly when loosing/getting 00895 // the focus 00896 00897 BOOL CTreeCtrlEx::OnSetfocus(NMHDR* pNMHDR, LRESULT* pResult) 00898 { 00899 Invalidate(); 00900 *pResult = 0; 00901 return FALSE; 00902 } 00903 00904 BOOL CTreeCtrlEx::OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult) 00905 { 00906 Invalidate(); 00907 *pResult = 0; 00908 return FALSE; 00909 } 00910 00911 void CTreeCtrlEx::OnLButtonDblClk(UINT nFlags, CPoint point) 00912 { 00913 // We stop label editing. 00914 m_bEditLabelPending = FALSE; 00915 CTreeCtrl::OnLButtonDblClk(nFlags, point); 00916 } 00917 00918 void CTreeCtrlEx::OnTimer(UINT_PTR nIDEvent) 00919 { 00920 if (nIDEvent == TCEX_EDITLABEL) 00921 { 00922 // Stop the timer. 00923 KillTimer(m_idTimer); 00924 00925 // Invoke label editing. 00926 if (m_bEditLabelPending) 00927 EditLabel(GetSelectedItem()); 00928 00929 m_bEditLabelPending = FALSE; 00930 return; 00931 } 00932 00933 CTreeCtrl::OnTimer(nIDEvent); 00934 } 00935 00937 // Retrieves a sorted list of the item text and item handles 00938 // Returns the number of matched items 00939 // Tihamer Levendovszky 12/07/2001 00940 int CTreeCtrlEx::FindTextInItems(CString &strText, HTREEITEM hStartAtItem, 00941 CStringArrayEx &strResults, CArray<HTREEITEM, HTREEITEM>& hResults) 00942 { 00943 strResults.RemoveAll(); 00944 hResults.RemoveAll(); 00945 // Traverse all items in tree control 00946 HTREEITEM hItem; 00947 if ( hStartAtItem ) 00948 hItem = hStartAtItem; 00949 else 00950 hItem = GetRootItem(); 00951 00952 while ( hItem ) 00953 { 00954 CString strItemText = GetItemText( hItem ); 00955 if ( strItemText.Find(strText) == 0 ) // Item text starts with strText 00956 { 00957 // Insert at the same position the item handle and the string 00958 hResults.InsertAt(strResults.InsertAtOrder(strItemText), hItem); 00959 } 00960 00961 // Get first child node 00962 HTREEITEM hNextItem = GetChildItem( hItem ); 00963 00964 if ( !hNextItem ) 00965 { 00966 // Get next sibling child 00967 hNextItem = GetNextSiblingItem( hItem ); 00968 00969 if ( !hNextItem ) 00970 { 00971 HTREEITEM hParentItem=hItem; 00972 while ( !hNextItem && hParentItem ) 00973 { 00974 // No more children: Get next sibling to parent 00975 hParentItem = GetParentItem( hParentItem ); 00976 hNextItem = GetNextSiblingItem( hParentItem ); 00977 } 00978 } 00979 } 00980 00981 hItem = hNextItem; 00982 } 00983 00984 return strResults.GetSize(); 00985 00986 } 00987 00988 00990 // Retreives a tree ctrl item given the item's data 00991 00992 HTREEITEM CTreeCtrlEx::ItemFromData(DWORD_PTR dwData, HTREEITEM hStartAtItem/*=NULL*/) const 00993 { 00994 // Traverse all items in tree control 00995 HTREEITEM hItem; 00996 if ( hStartAtItem ) 00997 hItem = hStartAtItem; 00998 else 00999 hItem = GetRootItem(); 01000 01001 while ( hItem ) 01002 { 01003 if ( dwData == GetItemData( hItem ) ) 01004 return hItem; 01005 01006 // Get first child node 01007 HTREEITEM hNextItem = GetChildItem( hItem ); 01008 01009 if ( !hNextItem ) 01010 { 01011 // Get next sibling child 01012 hNextItem = GetNextSiblingItem( hItem ); 01013 01014 if ( !hNextItem ) 01015 { 01016 HTREEITEM hParentItem=hItem; 01017 while ( !hNextItem && hParentItem ) 01018 { 01019 // No more children: Get next sibling to parent 01020 hParentItem = GetParentItem( hParentItem ); 01021 hNextItem = GetNextSiblingItem( hParentItem ); 01022 } 01023 } 01024 } 01025 01026 hItem = hNextItem; 01027 } 01028 01029 return NULL; 01030 } 01031 01032 01034 // Global function to retreive a HTREEITEM from a tree control, given the 01035 // item's itemdata. 01036 01037 HTREEITEM GetTreeItemFromData(CTreeCtrl& treeCtrl, DWORD_PTR dwData, HTREEITEM hStartAtItem /*=NULL*/) 01038 { 01039 // Traverse from given item (or all items if hFromItem is NULL) 01040 HTREEITEM hItem; 01041 if ( hStartAtItem ) 01042 hItem=hStartAtItem; 01043 else 01044 hItem = treeCtrl.GetRootItem(); 01045 01046 while ( hItem ) 01047 { 01048 if ( dwData == treeCtrl.GetItemData( hItem ) ) 01049 return hItem; 01050 01051 // Get first child node 01052 HTREEITEM hNextItem = treeCtrl.GetChildItem( hItem ); 01053 01054 if ( !hNextItem ) 01055 { 01056 // Get next sibling child 01057 hNextItem = treeCtrl.GetNextSiblingItem( hItem ); 01058 01059 if ( !hNextItem ) 01060 { 01061 HTREEITEM hParentItem=hItem; 01062 while ( !hNextItem && hParentItem ) 01063 { 01064 // No more children: Get next sibling to parent 01065 hParentItem = treeCtrl.GetParentItem( hParentItem ); 01066 hNextItem = treeCtrl.GetNextSiblingItem( hParentItem ); 01067 } 01068 } 01069 } 01070 hItem = hNextItem; 01071 } 01072 return NULL; 01073 } 01074 01075 01076 // Tihamer Levendovszky 02/25/02 01077 BOOL CTreeCtrlEx::CreateDragImageEx(CPoint ptDragPoint) 01078 { 01079 if (GetSelectedCount() <= 0) 01080 return FALSE; // no row selected 01081 01082 CRect rectSingle; 01083 CRect rectText; 01084 CRect rectComplete(0,0,0,0); 01085 01086 HTREEITEM hItem; 01087 01088 // Putting together the bounding rectangle 01089 for(hItem=GetFirstSelectedItem();hItem;hItem=GetNextSelectedItem(hItem)) 01090 { 01091 CImageList* pSingleImageList = CreateDragImage(hItem); 01092 IMAGEINFO ImageInfo; 01093 01094 // The image bounding rectangle is zero based - but has the correct size 01095 // GetItemRect correct in offset, but does not contain icon size 01096 pSingleImageList->GetImageInfo(0,&ImageInfo); 01097 GetItemRect(hItem,rectSingle,TRUE); 01098 rectSingle.left=rectSingle.right-ImageInfo.rcImage.right; 01099 01100 pSingleImageList->DeleteImageList(); 01101 delete pSingleImageList; 01102 01103 rectComplete.UnionRect(rectComplete, rectSingle); 01104 } 01105 01106 CClientDC dcClient(this); 01107 CDC dcMem; 01108 CBitmap Bitmap; 01109 01110 if (!dcMem.CreateCompatibleDC(&dcClient)) 01111 return FALSE; 01112 01113 if (!Bitmap.CreateCompatibleBitmap(&dcClient, 01114 rectComplete.Width(), 01115 rectComplete.Height())) 01116 return FALSE; 01117 01118 CBitmap *pOldMemDCBitmap = dcMem.SelectObject(&Bitmap); 01119 01120 COLORREF cMaskColor=RGB(0,255,0); 01121 dcMem.FillSolidRect(0, 0, 01122 rectComplete.Width(), 01123 rectComplete.Height(), 01124 cMaskColor); 01125 01126 // Paint each DragImage in the DC 01127 for(hItem=GetFirstSelectedItem();hItem;hItem=GetNextSelectedItem(hItem)) 01128 { 01129 CImageList* pSingleImageList = CreateDragImage(hItem); 01130 01131 if (pSingleImageList) 01132 { 01133 01134 // The image bounding rectangle is zero based - but has the correct size 01135 // GetItemRect correct in offset, but does not contain icon size 01136 IMAGEINFO ImageInfo; 01137 pSingleImageList->GetImageInfo(0,&ImageInfo); 01138 GetItemRect(hItem,rectSingle,TRUE); 01139 rectSingle.left=rectSingle.right-ImageInfo.rcImage.right; 01140 01141 pSingleImageList->Draw(&dcMem, 01142 0, 01143 CPoint(rectSingle.left - rectComplete.left, 01144 rectSingle.top - rectComplete.top), 01145 ILD_TRANSPARENT); 01146 01147 /* #ifdef _DEBUG 01148 CDC *pWndDC=GetDC(); 01149 pSingleImageList->Draw(pWndDC, 01150 0, 01151 CPoint(rectSingle.left - rectComplete.left, 01152 rectSingle.top - rectComplete.top), 01153 ILD_TRANSPARENT); 01154 ReleaseDC(pWndDC); 01155 #endif 01156 */ 01157 pSingleImageList->DeleteImageList(); 01158 delete pSingleImageList; 01159 } 01160 } 01161 /* 01162 #ifdef _DEBUG 01163 CDC *pWndDC=GetDC(); 01164 BitBlt(pWndDC->GetSafeHdc(),0,0,rectComplete.Width(),rectComplete.Height(),dcMem.GetSafeHdc(),0,0,SRCCOPY); 01165 ReleaseDC(pWndDC); 01166 #endif 01167 */ 01168 dcMem.SelectObject(pOldMemDCBitmap); 01169 01170 01171 if(m_CurrentDragImage.GetSafeHandle()!=NULL) 01172 { 01173 m_CurrentDragImage.DeleteImageList(); 01174 } 01175 01176 m_CurrentDragImage.Create(rectComplete.Width(), 01177 rectComplete.Height(), 01178 ILC_COLOR | ILC_MASK, 01179 0, 1); 01180 01181 // Green is used as mask color 01182 m_CurrentDragImage.Add(&Bitmap, cMaskColor); 01183 01184 01185 01186 Bitmap.DeleteObject(); 01187 01188 m_ptHotSpot.x = ptDragPoint.x-rectComplete.left; 01189 m_ptHotSpot.y = ptDragPoint.y-rectComplete.top; 01190 01191 01192 return TRUE; 01193 01194 } 01195 01196 // Tihamer Levendovszky 02/25/02 01197 void CTreeCtrlEx::DeleteDragImageEx() 01198 { 01199 m_CurrentDragImage.DeleteImageList(); 01200 } 01201 01202 // Tihamer Levendovszky 02/25/02 01203 void CTreeCtrlEx::ScrollUp() 01204 { 01205 01206 HTREEITEM hItem=GetFirstVisibleItem(); 01207 HTREEITEM hPrevSibling=GetPrevSiblingItem(hItem); 01208 HTREEITEM hParent=GetParentItem(hItem); 01209 01210 if(hItem) 01211 { 01212 if(hPrevSibling) 01213 { 01214 SelectSetFirstVisible(hPrevSibling); 01215 } 01216 else if(hParent) 01217 { 01218 SelectSetFirstVisible(hParent); 01219 } 01220 } 01221 01222 }