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 BEGIN_MESSAGE_MAP(CTreeCtrlEx, CTreeCtrl) 00053 //{{AFX_MSG_MAP(CTreeCtrlEx) 00054 ON_WM_LBUTTONDOWN() 00055 ON_WM_LBUTTONUP() 00056 ON_WM_MOUSEMOVE() 00057 ON_WM_KEYDOWN() 00058 ON_NOTIFY_REFLECT_EX(TVN_ITEMEXPANDING, OnItemexpanding) 00059 ON_NOTIFY_REFLECT_EX(NM_SETFOCUS, OnSetfocus) 00060 ON_NOTIFY_REFLECT_EX(NM_KILLFOCUS, OnKillfocus) 00061 ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelchanged) 00062 ON_WM_RBUTTONDOWN() 00063 ON_WM_LBUTTONDBLCLK() 00064 ON_WM_TIMER() 00065 ON_WM_NCHITTEST() 00066 //}}AFX_MSG_MAP 00067 END_MESSAGE_MAP() 00068 00069 IMPLEMENT_DYNAMIC(CTreeCtrlEx, CTreeCtrl) 00070 00071 BOOL CTreeCtrlEx::Create(DWORD dwStyle, DWORD dwExStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) 00072 { 00073 #if _MFC_VER < 0x0700 00074 return CreateEx( dwExStyle, WC_TREEVIEW, NULL, dwStyle, 00075 rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 00076 pParentWnd->GetSafeHwnd(), (HMENU)nID ); 00077 #else 00078 return CTreeCtrl::CreateEx( dwExStyle, dwStyle, rect, pParentWnd, nID ); 00079 #endif 00080 } 00081 00082 BOOL CTreeCtrlEx::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) 00083 { 00084 return CTreeCtrl::Create(dwStyle, rect, pParentWnd, nID); 00085 } 00086 00087 00089 // CTreeCtrlEx message handlers 00090 00091 00093 // The tree control dosn't support multiple selection. However we can simulate 00094 // it by taking control of the left mouse click and arrow key press before the 00095 // control gets them, and setting/clearing the TVIS_SELECTED style on the items 00096 00097 void CTreeCtrlEx::OnLButtonDown( UINT nFlags, CPoint point ) 00098 { 00099 00100 UINT nHitFlags = 0; 00101 HTREEITEM hClickedItem = HitTest( point, &nHitFlags ); 00102 00103 // Must invoke label editing explicitly. The base class OnLButtonDown would normally 00104 // do this, but we can't call it here because of the multiple selection... 00105 if( !( nFlags&( MK_CONTROL|MK_SHIFT ) ) && ( GetStyle() & TVS_EDITLABELS ) && ( nHitFlags & TVHT_ONITEMLABEL ) ) 00106 if ( hClickedItem == GetSelectedItem() ) 00107 { 00108 // Clear multple selection before label editing 00109 ClearSelection(); 00110 SelectItem( hClickedItem ); 00111 00112 // Invoke label editing 00113 m_bEditLabelPending = TRUE; 00114 m_idTimer = SetTimer(TCEX_EDITLABEL, GetDoubleClickTime(), NULL); 00115 00116 return; 00117 } 00118 00119 m_bEditLabelPending = FALSE; 00120 00121 if( nHitFlags & TVHT_ONITEM ) 00122 { 00123 SetFocus(); 00124 00125 m_hClickedItem = hClickedItem; 00126 00127 // Is the clicked item already selected ? 00128 BOOL bIsClickedItemSelected = GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED; 00129 00130 if ( bIsClickedItemSelected ) 00131 { 00132 // Maybe user wants to drag/drop multiple items! 00133 // So, wait until OnLButtonUp() to do the selection stuff. 00134 m_bSelectPending=TRUE; 00135 } 00136 else 00137 { 00138 SelectMultiple( hClickedItem, nFlags, point ); 00139 m_bSelectPending=FALSE; 00140 } 00141 00142 m_ptClick=point; 00143 } 00144 else 00145 { 00146 // Modified: 01/29/2002 Tihamer Levendovszky//////// 00147 00148 if(nHitFlags&TVHT_NOWHERE || nHitFlags&TVHT_ONITEMRIGHT ) 00149 { 00150 ClearSelection(); 00151 } 00153 CTreeCtrl::OnLButtonDown( nFlags, point ); 00154 } 00155 } 00156 00157 void CTreeCtrlEx::OnLButtonUp( UINT nFlags, CPoint point ) 00158 { 00159 if ( m_bSelectPending ) 00160 { 00161 // A select has been waiting to be performed here 00162 SelectMultiple( m_hClickedItem, nFlags, point ); 00163 m_bSelectPending=FALSE; 00164 } 00165 00166 m_hClickedItem=NULL; 00167 00168 CTreeCtrl::OnLButtonUp( nFlags, point ); 00169 } 00170 00171 00172 void CTreeCtrlEx::OnMouseMove( UINT nFlags, CPoint point ) 00173 { 00174 // If there is a select pending, check if cursor has moved so much away from the 00175 // down-click point that we should cancel the pending select and initiate 00176 // a drag/drop operation instead! 00177 00178 if ( m_hClickedItem ) 00179 { 00180 CSize sizeMoved = m_ptClick-point; 00181 00182 if ( abs(sizeMoved.cx) > GetSystemMetrics( SM_CXDRAG ) || abs(sizeMoved.cy) > GetSystemMetrics( SM_CYDRAG ) ) 00183 { 00184 m_bSelectPending=FALSE; 00185 00186 // Notify parent that he may begin drag operation 00187 // Since we have taken over OnLButtonDown(), the default handler doesn't 00188 // do the normal work when clicking an item, so we must provide our own 00189 // TVN_BEGINDRAG notification for the parent! 00190 00191 CWnd* pWnd = GetParent(); 00192 if ( pWnd && !( GetStyle() & TVS_DISABLEDRAGDROP ) ) 00193 { 00194 NM_TREEVIEW tv; 00195 00196 tv.hdr.hwndFrom = GetSafeHwnd(); 00197 tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00198 tv.hdr.code = TVN_BEGINDRAG; 00199 00200 tv.itemNew.hItem = m_hClickedItem; 00201 tv.itemNew.state = GetItemState( m_hClickedItem, 0xffffffff ); 00202 tv.itemNew.lParam = GetItemData( m_hClickedItem ); 00203 00204 tv.ptDrag.x = point.x; 00205 tv.ptDrag.y = point.y; 00206 00207 pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); 00208 } 00209 00210 m_hClickedItem=NULL; 00211 } 00212 } 00213 00214 CTreeCtrl::OnMouseMove( nFlags, point ); 00215 } 00216 00217 00218 void CTreeCtrlEx::SelectMultiple( HTREEITEM hClickedItem, UINT nFlags, CPoint point ) 00219 { 00220 // Start preparing an NM_TREEVIEW struct to send a notification after selection is done 00221 NM_TREEVIEW tv; 00222 memset(&tv.itemOld, 0, sizeof(tv.itemOld)); 00223 00224 CWnd* pWnd = GetParent(); 00225 00226 HTREEITEM hOldItem = GetSelectedItem(); 00227 00228 if ( hOldItem ) 00229 { 00230 tv.itemOld.hItem = hOldItem; 00231 tv.itemOld.state = GetItemState( hOldItem, 0xffffffff ); 00232 tv.itemOld.lParam = GetItemData( hOldItem ); 00233 tv.itemOld.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00234 } 00235 00236 // Flag signaling that selection process is NOT complete. 00237 // (Will prohibit TVN_SELCHANGED from being sent to parent) 00238 m_bSelectionComplete = FALSE; 00239 00240 // Action depends on whether the user holds down the Shift or Ctrl key 00241 if ( nFlags & MK_SHIFT ) 00242 { 00243 // Select from first selected item to the clicked item 00244 if ( !m_hFirstSelectedItem ) 00245 m_hFirstSelectedItem = GetSelectedItem(); 00246 00247 SelectItems( m_hFirstSelectedItem, hClickedItem ); 00248 } 00249 else if ( nFlags & MK_CONTROL ) 00250 { 00251 // Find which item is currently selected 00252 HTREEITEM hSelectedItem = GetSelectedItem(); 00253 00254 // Is the clicked item already selected ? 00255 BOOL bIsClickedItemSelected = GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED; 00256 BOOL bIsSelectedItemSelected = FALSE; 00257 if ( hSelectedItem ) 00258 bIsSelectedItemSelected = GetItemState( hSelectedItem, TVIS_SELECTED ) & TVIS_SELECTED; 00259 00260 // Must synthesize a TVN_SELCHANGING notification 00261 if ( pWnd ) 00262 { 00263 tv.hdr.hwndFrom = GetSafeHwnd(); 00264 tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00265 tv.hdr.code = TVN_SELCHANGING; 00266 00267 tv.itemNew.hItem = hClickedItem; 00268 tv.itemNew.state = GetItemState( hClickedItem, 0xffffffff ); 00269 tv.itemNew.lParam = GetItemData( hClickedItem ); 00270 00271 tv.itemOld.hItem = NULL; 00272 tv.itemOld.mask = 0; 00273 00274 tv.action = TVC_BYMOUSE; 00275 00276 tv.ptDrag.x = point.x; 00277 tv.ptDrag.y = point.y; 00278 00279 pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); 00280 } 00281 00282 // If the previously selected item was selected, re-select it 00283 if ( bIsSelectedItemSelected ) 00284 SetItemState( hSelectedItem, TVIS_SELECTED, TVIS_SELECTED ); 00285 00286 // We want the newly selected item to toggle its selected state, 00287 // so unselect now if it was already selected before 00288 if ( bIsClickedItemSelected ) 00289 SetItemState( hClickedItem, 0, TVIS_SELECTED ); 00290 else 00291 { 00292 SelectItem(hClickedItem); 00293 SetItemState( hClickedItem, TVIS_SELECTED, TVIS_SELECTED ); 00294 } 00295 00296 // If the previously selected item was selected, re-select it 00297 if ( bIsSelectedItemSelected && hSelectedItem != hClickedItem ) 00298 SetItemState( hSelectedItem, TVIS_SELECTED, TVIS_SELECTED ); 00299 00300 // Store as first selected item (if not already stored) 00301 if ( m_hFirstSelectedItem==NULL ) 00302 m_hFirstSelectedItem = hClickedItem; 00303 } 00304 else 00305 { 00306 // Clear selection of all "multiple selected" items first 00307 ClearSelection(); 00308 00309 // Then select the clicked item 00310 SelectItem( hClickedItem ); 00311 SetItemState( hClickedItem, TVIS_SELECTED, TVIS_SELECTED ); 00312 00313 // Store as first selected item 00314 m_hFirstSelectedItem = hClickedItem; 00315 } 00316 00317 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED 00318 // notification provided by Windows' treectrl, we must now produce one ourselves, 00319 // so that our parent gets to know about the change of selection. 00320 m_bSelectionComplete = TRUE; 00321 00322 if ( pWnd ) 00323 { 00324 tv.hdr.hwndFrom = GetSafeHwnd(); 00325 tv.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00326 tv.hdr.code = TVN_SELCHANGED; 00327 00328 tv.itemNew.hItem = m_hClickedItem; 00329 tv.itemNew.state = GetItemState( m_hClickedItem, 0xffffffff ); 00330 tv.itemNew.lParam = GetItemData( m_hClickedItem ); 00331 tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00332 00333 tv.action = TVC_UNKNOWN; 00334 00335 pWnd->SendMessage( WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv ); 00336 } 00337 } 00338 00339 void CTreeCtrlEx::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ) 00340 { 00341 CWnd* pWnd = GetParent(); 00342 00343 if ( nChar==VK_NEXT || nChar==VK_PRIOR ) 00344 { 00345 if ( !( GetKeyState( VK_SHIFT )&0x8000 ) ) 00346 { 00347 // User pressed Pg key without holding 'Shift': 00348 // Clear multiple selection (if multiple) and let base class do 00349 // normal selection work! 00350 if ( GetSelectedCount()>1 ) 00351 ClearSelection( TRUE ); 00352 00353 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00354 m_hFirstSelectedItem = GetSelectedItem(); 00355 return; 00356 } 00357 00358 // Flag signaling that selection process is NOT complete. 00359 // (Will prohibit TVN_SELCHANGED from being sent to parent) 00360 m_bSelectionComplete = FALSE; 00361 00362 // Let base class select the item 00363 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00364 HTREEITEM hSelectedItem = GetSelectedItem(); 00365 00366 // Then select items in between 00367 SelectItems( m_hFirstSelectedItem, hSelectedItem ); 00368 00369 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED 00370 // notification provided by Windows' treectrl, we must now produce one ourselves, 00371 // so that our parent gets to know about the change of selection. 00372 m_bSelectionComplete = TRUE; 00373 00374 if (pWnd) 00375 { 00376 NM_TREEVIEW tv; 00377 memset(&tv.itemOld, 0, sizeof(tv.itemOld)); 00378 00379 tv.hdr.hwndFrom = GetSafeHwnd(); 00380 tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID); 00381 tv.hdr.code = TVN_SELCHANGED; 00382 00383 tv.itemNew.hItem = hSelectedItem; 00384 tv.itemNew.state = GetItemState(hSelectedItem, 0xffffffff); 00385 tv.itemNew.lParam = GetItemData(hSelectedItem); 00386 tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00387 00388 tv.action = TVC_UNKNOWN; 00389 00390 pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv); 00391 } 00392 } 00393 else if ( nChar==VK_UP || nChar==VK_DOWN ) 00394 { 00395 // Find which item is currently selected 00396 HTREEITEM hSelectedItem = GetSelectedItem(); 00397 00398 HTREEITEM hNextItem; 00399 if ( nChar==VK_UP ) 00400 hNextItem = GetPrevVisibleItem( hSelectedItem ); 00401 else 00402 hNextItem = GetNextVisibleItem( hSelectedItem ); 00403 00404 if ( !( GetKeyState( VK_SHIFT )&0x8000 ) ) 00405 { 00406 // User pressed arrow key without holding 'Shift': 00407 // Clear multiple selection (if multiple) and let base class do 00408 // normal selection work! 00409 if ( GetSelectedCount()>1 ) 00410 ClearSelection( TRUE ); 00411 00412 if ( hNextItem ) 00413 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00414 m_hFirstSelectedItem = GetSelectedItem(); 00415 return; 00416 } 00417 00418 if ( hNextItem ) 00419 { 00420 // Flag signaling that selection process is NOT complete. 00421 // (Will prohibit TVN_SELCHANGED from being sent to parent) 00422 m_bSelectionComplete = FALSE; 00423 00424 // If the next item is already selected, we assume user is 00425 // "moving back" in the selection, and thus we should clear 00426 // selection on the previous one 00427 BOOL bSelect = !( GetItemState( hNextItem, TVIS_SELECTED ) & TVIS_SELECTED ); 00428 00429 // Select the next item (this will also deselect the previous one!) 00430 SelectItem( hNextItem ); 00431 00432 // Now, re-select the previously selected item 00433 if ( bSelect || ( !( GetItemState( hSelectedItem, TVIS_SELECTED ) & TVIS_SELECTED ) ) ) 00434 SelectItems( m_hFirstSelectedItem, hNextItem ); 00435 00436 // Selection process is now complete. Since we have 'eaten' the TVN_SELCHANGED 00437 // notification provided by Windows' treectrl, we must now produce one ourselves, 00438 // so that our parent gets to know about the change of selection. 00439 m_bSelectionComplete = TRUE; 00440 00441 if (pWnd) 00442 { 00443 NM_TREEVIEW tv; 00444 memset(&tv.itemOld, 0, sizeof(tv.itemOld)); 00445 00446 tv.hdr.hwndFrom = GetSafeHwnd(); 00447 tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID); 00448 tv.hdr.code = TVN_SELCHANGED; 00449 00450 tv.itemNew.hItem = hNextItem; 00451 tv.itemNew.state = GetItemState(hNextItem, 0xffffffff); 00452 tv.itemNew.lParam = GetItemData(hNextItem); 00453 tv.itemNew.mask = TVIF_HANDLE|TVIF_STATE|TVIF_PARAM; 00454 00455 tv.action = TVC_UNKNOWN; 00456 00457 pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv); 00458 } 00459 } 00460 00461 // Since the base class' OnKeyDown() isn't called in this case, 00462 // we must provide our own TVN_KEYDOWN notification to the parent 00463 00464 CWnd* pWnd = GetParent(); 00465 if ( pWnd ) 00466 { 00467 NMTVKEYDOWN tvk; 00468 00469 tvk.hdr.hwndFrom = GetSafeHwnd(); 00470 tvk.hdr.idFrom = GetWindowLong( GetSafeHwnd(), GWL_ID ); 00471 tvk.hdr.code = TVN_KEYDOWN; 00472 00473 tvk.wVKey = nChar; 00474 tvk.flags = 0; 00475 00476 pWnd->SendMessage( WM_NOTIFY, tvk.hdr.idFrom, (LPARAM)&tvk ); 00477 } 00478 } 00479 else 00480 // Behave normally 00481 CTreeCtrl::OnKeyDown( nChar, nRepCnt, nFlags ); 00482 } 00483 00484 00486 // I want clicking on an item with the right mouse button to select the item, 00487 // but not if there is currently a multiple selection 00488 00489 void CTreeCtrlEx::OnRButtonDown( UINT nFlags, CPoint point ) 00490 { 00491 UINT nHitFlags = 0; 00492 HTREEITEM hClickedItem = HitTest( point, &nHitFlags ); 00493 00494 if( nHitFlags&TVHT_ONITEM ) 00495 { 00496 if ( GetSelectedCount()<2 ) 00497 { 00498 SelectItem( hClickedItem ); 00499 } 00500 // Modified: 01/26/2002 Tihamer Levendovszky 00501 else 00502 { 00503 if(!( GetItemState( hClickedItem, TVIS_SELECTED ) & TVIS_SELECTED )) 00504 { 00505 ClearSelection(); 00506 SelectItem( hClickedItem ); 00507 } 00508 } 00509 } 00510 // Modified: 01/26/2002 Tihamer Levendovszky 00511 else 00512 { 00513 ClearSelection(); 00514 } 00515 00516 // Modified: 01/26/2002 Tihamer Levendovszky 00517 /* CTreeCtrl::OnRButtonDown( nFlags, point ); */ 00518 } 00519 00520 00522 // Get number of selected items 00523 00524 UINT CTreeCtrlEx::GetSelectedCount() const 00525 { 00526 // Only visible items should be selected! 00527 UINT uCount=0; 00528 for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) ) 00529 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00530 uCount++; 00531 00532 return uCount; 00533 } 00534 00535 00537 // Overloaded to catch our own special code 00538 00539 HTREEITEM CTreeCtrlEx::GetNextItem(HTREEITEM hItem, UINT nCode) 00540 { 00541 if (nCode==TVGN_EX_ALL) 00542 { 00543 // This special code lets us iterate through ALL tree items regardless 00544 // of their parent/child relationship (very handy) 00545 HTREEITEM hNextItem; 00546 00547 // If it has a child node, this will be the next item 00548 hNextItem = GetChildItem( hItem ); 00549 if (hNextItem) 00550 return hNextItem; 00551 00552 // Otherwise, see if it has a next sibling item 00553 hNextItem = GetNextSiblingItem(hItem); 00554 if (hNextItem) 00555 return hNextItem; 00556 00557 // Finally, look for next sibling to the parent item 00558 HTREEITEM hParentItem=hItem; 00559 while (!hNextItem && hParentItem) 00560 { 00561 // No more children: Get next sibling to parent 00562 hParentItem = GetParentItem(hParentItem); 00563 hNextItem = GetNextSiblingItem(hParentItem); 00564 } 00565 00566 return hNextItem; // will return NULL if no more parents 00567 } 00568 else 00569 return CTreeCtrl::GetNextItem(hItem, nCode); // standard processing 00570 } 00571 00573 // Helpers to list out selected items. (Use similar to GetFirstVisibleItem(), 00574 // GetNextVisibleItem() and GetPrevVisibleItem()!) 00575 00576 HTREEITEM CTreeCtrlEx::GetFirstSelectedItem() 00577 { 00578 for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) ) 00579 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00580 return hItem; 00581 00582 return NULL; 00583 } 00584 00585 HTREEITEM CTreeCtrlEx::GetNextSelectedItem( HTREEITEM hItem ) 00586 { 00587 for ( hItem = GetNextVisibleItem( hItem ); hItem!=NULL; hItem = GetNextVisibleItem( hItem ) ) 00588 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00589 return hItem; 00590 00591 return NULL; 00592 } 00593 00594 HTREEITEM CTreeCtrlEx::GetPrevSelectedItem( HTREEITEM hItem ) 00595 { 00596 for ( hItem = GetPrevVisibleItem( hItem ); hItem!=NULL; hItem = GetPrevVisibleItem( hItem ) ) 00597 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00598 return hItem; 00599 00600 return NULL; 00601 } 00602 00603 00605 // Select/unselect item without unselecting other items 00606 00607 BOOL CTreeCtrlEx::SelectItemEx(HTREEITEM hItem, BOOL bSelect/*=TRUE*/) 00608 { 00609 HTREEITEM hSelItem = GetSelectedItem(); 00610 00611 if ( hItem==hSelItem ) 00612 { 00613 if ( !bSelect ) 00614 { 00615 SelectItem( NULL ); 00616 return TRUE; 00617 } 00618 00619 return FALSE; 00620 } 00621 00622 SelectItem( hItem ); 00623 m_hFirstSelectedItem=hItem; 00624 00625 // Reselect previous "real" selected item which was unselected byt SelectItem() 00626 if ( hSelItem ) 00627 SetItemState( hSelItem, TVIS_SELECTED, TVIS_SELECTED ); 00628 00629 return TRUE; 00630 } 00631 00633 // Select visible items between specified 'from' and 'to' item (including these!) 00634 // If the 'to' item is above the 'from' item, it traverses the tree in reverse 00635 // direction. Selection on other items is cleared! 00636 00637 BOOL CTreeCtrlEx::SelectItems( HTREEITEM hFromItem, HTREEITEM hToItem ) 00638 { 00639 // Determine direction of selection 00640 // (see what item comes first in the tree) 00641 HTREEITEM hItem = GetRootItem(); 00642 00643 while ( hItem && hItem!=hFromItem && hItem!=hToItem ) 00644 hItem = GetNextVisibleItem( hItem ); 00645 00646 if ( !hItem ) 00647 return FALSE; // Items not visible in tree 00648 00649 BOOL bReverse = hItem==hToItem; 00650 00651 // "Really" select the 'to' item (which will deselect 00652 // the previously selected item) 00653 00654 SelectItem( hToItem ); 00655 00656 // Go through all visible items again and select/unselect 00657 00658 hItem = GetRootItem(); 00659 BOOL bSelect = FALSE; 00660 00661 while ( hItem ) 00662 { 00663 if ( hItem == ( bReverse ? hToItem : hFromItem ) ) 00664 bSelect = TRUE; 00665 00666 if ( bSelect ) 00667 { 00668 if ( !( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) ) 00669 SetItemState( hItem, TVIS_SELECTED, TVIS_SELECTED ); 00670 } 00671 else 00672 { 00673 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00674 SetItemState( hItem, 0, TVIS_SELECTED ); 00675 } 00676 00677 if ( hItem == ( bReverse ? hFromItem : hToItem ) ) 00678 bSelect = FALSE; 00679 00680 hItem = GetNextVisibleItem( hItem ); 00681 } 00682 00683 return TRUE; 00684 } 00685 00686 00688 // Clear selected state on all visible items 00689 00690 void CTreeCtrlEx::ClearSelection(BOOL bMultiOnly/*=FALSE*/) 00691 { 00692 // if ( !bMultiOnly ) 00693 // SelectItem( NULL ); 00694 00695 for ( HTREEITEM hItem=GetRootItem(); hItem!=NULL; hItem=GetNextVisibleItem( hItem ) ) 00696 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00697 SetItemState( hItem, 0, TVIS_SELECTED ); 00698 } 00699 00700 00702 // If a node is collapsed, we should clear selections of its child items 00703 00704 BOOL CTreeCtrlEx::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult) 00705 { 00706 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; 00707 00708 if ( pNMTreeView->action == TVE_COLLAPSE ) 00709 { 00710 HTREEITEM hItem = GetChildItem( pNMTreeView->itemNew.hItem ); 00711 00712 while ( hItem ) 00713 { 00714 if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED ) 00715 SetItemState( hItem, 0, TVIS_SELECTED ); 00716 00717 // Get the next node: First see if current node has a child 00718 HTREEITEM hNextItem = GetChildItem( hItem ); 00719 if ( !hNextItem ) 00720 { 00721 // No child: Get next sibling item 00722 if ( !( hNextItem = GetNextSiblingItem( hItem ) ) ) 00723 { 00724 HTREEITEM hParentItem = hItem; 00725 while ( !hNextItem ) 00726 { 00727 // No more children: Get parent 00728 if ( !( hParentItem = GetParentItem( hParentItem ) ) ) 00729 break; 00730 00731 // Quit when parent is the collapsed node 00732 // (Don't do anything to siblings of this) 00733 if ( hParentItem == pNMTreeView->itemNew.hItem ) 00734 break; 00735 00736 // Get next sibling to parent 00737 hNextItem = GetNextSiblingItem( hParentItem ); 00738 } 00739 00740 // Quit when parent is the collapsed node 00741 if ( hParentItem == pNMTreeView->itemNew.hItem ) 00742 break; 00743 } 00744 } 00745 00746 hItem = hNextItem; 00747 } 00748 } 00749 00750 *pResult = 0; 00751 return FALSE; // Allow parent to handle this notification as well 00752 } 00753 00754 00756 // Intercept TVN_SELCHANGED and pass it only to the parent window of the 00757 // selection process is finished 00758 00759 BOOL CTreeCtrlEx::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 00760 { 00761 // Return TRUE if selection is not complete. This will prevent the 00762 // notification from being sent to parent. 00763 return !m_bSelectionComplete; 00764 } 00765 00766 00768 // Ensure the multiple selected items are drawn correctly when loosing/getting 00769 // the focus 00770 00771 BOOL CTreeCtrlEx::OnSetfocus(NMHDR* pNMHDR, LRESULT* pResult) 00772 { 00773 Invalidate(); 00774 *pResult = 0; 00775 return FALSE; 00776 } 00777 00778 BOOL CTreeCtrlEx::OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult) 00779 { 00780 Invalidate(); 00781 *pResult = 0; 00782 return FALSE; 00783 } 00784 00785 void CTreeCtrlEx::OnLButtonDblClk(UINT nFlags, CPoint point) 00786 { 00787 // We stop label editing. 00788 m_bEditLabelPending = FALSE; 00789 CTreeCtrl::OnLButtonDblClk(nFlags, point); 00790 } 00791 00792 void CTreeCtrlEx::OnTimer(UINT_PTR nIDEvent) 00793 { 00794 if (nIDEvent == TCEX_EDITLABEL) 00795 { 00796 // Stop the timer. 00797 KillTimer(m_idTimer); 00798 00799 // Invoke label editing. 00800 if (m_bEditLabelPending) 00801 EditLabel(GetSelectedItem()); 00802 00803 m_bEditLabelPending = FALSE; 00804 return; 00805 } 00806 00807 CTreeCtrl::OnTimer(nIDEvent); 00808 } 00809 00811 // Retrieves a sorted list of the item text and item handles 00812 // Returns the number of matched items 00813 // Tihamer Levendovszky 12/07/2001 00814 /*int CTreeCtrlEx::FindTextInItems(CString &strText, HTREEITEM hStartAtItem, 00815 CStringArrayEx &strResults, CArray<HTREEITEM, HTREEITEM>& hResults) 00816 { 00817 strResults.RemoveAll(); 00818 hResults.RemoveAll(); 00819 // Traverse all items in tree control 00820 HTREEITEM hItem; 00821 if ( hStartAtItem ) 00822 hItem = hStartAtItem; 00823 else 00824 hItem = GetRootItem(); 00825 00826 while ( hItem ) 00827 { 00828 CString strItemText = GetItemText( hItem ); 00829 if ( strItemText.Find(strText) == 0 ) // Item text starts with strText 00830 { 00831 // Insert at the same position the item handle and the string 00832 hResults.InsertAt(strResults.InsertAtOrder(strItemText), hItem); 00833 } 00834 00835 // Get first child node 00836 HTREEITEM hNextItem = GetChildItem( hItem ); 00837 00838 if ( !hNextItem ) 00839 { 00840 // Get next sibling child 00841 hNextItem = GetNextSiblingItem( hItem ); 00842 00843 if ( !hNextItem ) 00844 { 00845 HTREEITEM hParentItem=hItem; 00846 while ( !hNextItem && hParentItem ) 00847 { 00848 // No more children: Get next sibling to parent 00849 hParentItem = GetParentItem( hParentItem ); 00850 hNextItem = GetNextSiblingItem( hParentItem ); 00851 } 00852 } 00853 } 00854 00855 hItem = hNextItem; 00856 } 00857 00858 return strResults.GetSize(); 00859 00860 } 00861 00862 */ 00864 // Retreives a tree ctrl item given the item's data 00865 00866 HTREEITEM CTreeCtrlEx::ItemFromData(DWORD dwData, HTREEITEM hStartAtItem/*=NULL*/) const 00867 { 00868 // Traverse all items in tree control 00869 HTREEITEM hItem; 00870 if ( hStartAtItem ) 00871 hItem = hStartAtItem; 00872 else 00873 hItem = GetRootItem(); 00874 00875 while ( hItem ) 00876 { 00877 if ( dwData == (DWORD)GetItemData( hItem ) ) 00878 return hItem; 00879 00880 // Get first child node 00881 HTREEITEM hNextItem = GetChildItem( hItem ); 00882 00883 if ( !hNextItem ) 00884 { 00885 // Get next sibling child 00886 hNextItem = GetNextSiblingItem( hItem ); 00887 00888 if ( !hNextItem ) 00889 { 00890 HTREEITEM hParentItem=hItem; 00891 while ( !hNextItem && hParentItem ) 00892 { 00893 // No more children: Get next sibling to parent 00894 hParentItem = GetParentItem( hParentItem ); 00895 hNextItem = GetNextSiblingItem( hParentItem ); 00896 } 00897 } 00898 } 00899 00900 hItem = hNextItem; 00901 } 00902 00903 return NULL; 00904 } 00905 00906 00908 // Global function to retreive a HTREEITEM from a tree control, given the 00909 // item's itemdata. 00910 00911 HTREEITEM GetTreeItemFromData(CTreeCtrl& treeCtrl, DWORD dwData, HTREEITEM hStartAtItem /*=NULL*/) 00912 { 00913 // Traverse from given item (or all items if hFromItem is NULL) 00914 HTREEITEM hItem; 00915 if ( hStartAtItem ) 00916 hItem=hStartAtItem; 00917 else 00918 hItem = treeCtrl.GetRootItem(); 00919 00920 while ( hItem ) 00921 { 00922 if ( dwData == (DWORD)treeCtrl.GetItemData( hItem ) ) 00923 return hItem; 00924 00925 // Get first child node 00926 HTREEITEM hNextItem = treeCtrl.GetChildItem( hItem ); 00927 00928 if ( !hNextItem ) 00929 { 00930 // Get next sibling child 00931 hNextItem = treeCtrl.GetNextSiblingItem( hItem ); 00932 00933 if ( !hNextItem ) 00934 { 00935 HTREEITEM hParentItem=hItem; 00936 while ( !hNextItem && hParentItem ) 00937 { 00938 // No more children: Get next sibling to parent 00939 hParentItem = treeCtrl.GetParentItem( hParentItem ); 00940 hNextItem = treeCtrl.GetNextSiblingItem( hParentItem ); 00941 } 00942 } 00943 } 00944 hItem = hNextItem; 00945 } 00946 return NULL; 00947 } 00948 00949 00950 // Tihamer Levendovszky 02/25/02 00951 BOOL CTreeCtrlEx::CreateDragImageEx(CPoint ptDragPoint) 00952 { 00953 if (GetSelectedCount() <= 0) 00954 return FALSE; // no row selected 00955 00956 CRect rectSingle; 00957 CRect rectText; 00958 CRect rectComplete(0,0,0,0); 00959 00960 HTREEITEM hItem; 00961 00962 // Putting together the bounding rectangle 00963 for(hItem=GetFirstSelectedItem();hItem;hItem=GetNextSelectedItem(hItem)) 00964 { 00965 CImageList* pSingleImageList = CreateDragImage(hItem); 00966 IMAGEINFO ImageInfo; 00967 00968 // The image bounding rectangle is zero based - but has the correct size 00969 // GetItemRect correct in offset, but does not contain icon size 00970 pSingleImageList->GetImageInfo(0,&ImageInfo); 00971 GetItemRect(hItem,rectSingle,TRUE); 00972 rectSingle.left=rectSingle.right-ImageInfo.rcImage.right; 00973 00974 pSingleImageList->DeleteImageList(); 00975 delete pSingleImageList; 00976 00977 rectComplete.UnionRect(rectComplete, rectSingle); 00978 } 00979 00980 CClientDC dcClient(this); 00981 CDC dcMem; 00982 CBitmap Bitmap; 00983 00984 if (!dcMem.CreateCompatibleDC(&dcClient)) 00985 return FALSE; 00986 00987 if (!Bitmap.CreateCompatibleBitmap(&dcClient, 00988 rectComplete.Width(), 00989 rectComplete.Height())) 00990 return FALSE; 00991 00992 CBitmap *pOldMemDCBitmap = dcMem.SelectObject(&Bitmap); 00993 00994 COLORREF cMaskColor=RGB(0,255,0); 00995 dcMem.FillSolidRect(0, 0, 00996 rectComplete.Width(), 00997 rectComplete.Height(), 00998 cMaskColor); 00999 01000 // Paint each DragImage in the DC 01001 for(hItem=GetFirstSelectedItem();hItem;hItem=GetNextSelectedItem(hItem)) 01002 { 01003 CImageList* pSingleImageList = CreateDragImage(hItem); 01004 01005 if (pSingleImageList) 01006 { 01007 01008 // The image bounding rectangle is zero based - but has the correct size 01009 // GetItemRect correct in offset, but does not contain icon size 01010 IMAGEINFO ImageInfo; 01011 pSingleImageList->GetImageInfo(0,&ImageInfo); 01012 GetItemRect(hItem,rectSingle,TRUE); 01013 rectSingle.left=rectSingle.right-ImageInfo.rcImage.right; 01014 01015 pSingleImageList->Draw(&dcMem, 01016 0, 01017 CPoint(rectSingle.left - rectComplete.left, 01018 rectSingle.top - rectComplete.top), 01019 ILD_TRANSPARENT); 01020 01021 /* #ifdef _DEBUG 01022 CDC *pWndDC=GetDC(); 01023 pSingleImageList->Draw(pWndDC, 01024 0, 01025 CPoint(rectSingle.left - rectComplete.left, 01026 rectSingle.top - rectComplete.top), 01027 ILD_TRANSPARENT); 01028 ReleaseDC(pWndDC); 01029 #endif 01030 */ 01031 pSingleImageList->DeleteImageList(); 01032 delete pSingleImageList; 01033 } 01034 } 01035 /* 01036 #ifdef _DEBUG 01037 CDC *pWndDC=GetDC(); 01038 BitBlt(pWndDC->GetSafeHdc(),0,0,rectComplete.Width(),rectComplete.Height(),dcMem.GetSafeHdc(),0,0,SRCCOPY); 01039 ReleaseDC(pWndDC); 01040 #endif 01041 */ 01042 dcMem.SelectObject(pOldMemDCBitmap); 01043 01044 01045 if(m_CurrentDragImage.GetSafeHandle()!=NULL) 01046 { 01047 m_CurrentDragImage.DeleteImageList(); 01048 } 01049 01050 m_CurrentDragImage.Create(rectComplete.Width(), 01051 rectComplete.Height(), 01052 ILC_COLOR | ILC_MASK, 01053 0, 1); 01054 01055 // Green is used as mask color 01056 m_CurrentDragImage.Add(&Bitmap, cMaskColor); 01057 01058 01059 01060 Bitmap.DeleteObject(); 01061 01062 m_ptHotSpot.x = ptDragPoint.x-rectComplete.left; 01063 m_ptHotSpot.y = ptDragPoint.y-rectComplete.top; 01064 01065 01066 return TRUE; 01067 01068 } 01069 01070 // Tihamer Levendovszky 02/25/02 01071 void CTreeCtrlEx::DeleteDragImageEx() 01072 { 01073 m_CurrentDragImage.DeleteImageList(); 01074 } 01075 01076 // Tihamer Levendovszky 02/25/02 01077 void CTreeCtrlEx::ScrollUp() 01078 { 01079 01080 HTREEITEM hItem=GetFirstVisibleItem(); 01081 HTREEITEM hPrevSibling=GetPrevSiblingItem(hItem); 01082 HTREEITEM hParent=GetParentItem(hItem); 01083 01084 if(hItem) 01085 { 01086 if(hPrevSibling) 01087 { 01088 SelectSetFirstVisible(hPrevSibling); 01089 } 01090 else if(hParent) 01091 { 01092 SelectSetFirstVisible(hParent); 01093 } 01094 } 01095 01096 }