///////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1996-2000 Microsoft Corporation // // Module Name: // ListView.cpp // // Abstract: // Implementation of the CListView class. // // Author: // David Potter (davidp) May 6, 1996 // // Revision History: // // Notes: // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "CluAdmin.h" #include "ConstDef.h" #include "ClusDoc.h" #include "ClusItem.h" #include "ListView.h" #include "ListItem.h" #include "ListItem.inl" #include "SplitFrm.h" #include "TreeItem.h" #include "TreeView.h" #include "ClusDoc.h" #include "TraceTag.h" #include "ExcOper.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Global Variables ///////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG CTraceTag g_tagListView(_T("UI"), _T("LIST VIEW"), 0); CTraceTag g_tagListDrag(_T("Drag&Drop"), _T("LIST VIEW DRAG"), 0); CTraceTag g_tagListDragMouse(_T("Drag&Drop"), _T("LIST VIEW DRAG MOUSE"), 0); #endif ///////////////////////////////////////////////////////////////////////////// // CClusterListView ///////////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNCREATE(CClusterListView, CListView) ///////////////////////////////////////////////////////////////////////////// // Message Maps BEGIN_MESSAGE_MAP(CClusterListView, CListView) //{{AFX_MSG_MAP(CClusterListView) ON_WM_DESTROY() ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemChanged) ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick) ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblClk) ON_COMMAND(ID_OPEN_ITEM, OpenItem) ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit) ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit) ON_UPDATE_COMMAND_UI(ID_FILE_PROPERTIES, OnUpdateProperties) ON_COMMAND(ID_FILE_PROPERTIES, OnCmdProperties) ON_COMMAND(ID_FILE_RENAME, OnCmdRename) ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag) ON_NOTIFY_REFLECT(LVN_BEGINRDRAG, OnBeginDrag) ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeyDown) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::CClusterListView // // Routine Description: // Default constructor. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// CClusterListView::CClusterListView(void) { m_ptiParent = NULL; m_nColumns = 0; m_nSortDirection = -1; m_pcoliSort = NULL; m_pframe = NULL; // Initialize label editing. m_pliBeingEdited = NULL; m_bShiftPressed = FALSE; m_bControlPressed = FALSE; m_bAltPressed = FALSE; // Initialize drag & drop. m_iliDrag = -1; m_pliDrag = NULL; m_iliDrop = -1; } //*** CClusterListView::CClusterListView() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::~CClusterListView // // Routine Description: // Destructor. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// CClusterListView::~CClusterListView(void) { if (m_ptiParent != NULL) m_ptiParent->Release(); } //*** CClusterListView::~CClusterListView() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::Create // // Routine Description: // Create the window. // // Arguments: // lpszClassName [IN] Name of the window class to create. // lpszWindowName [IN] Name of the window (used as the caption). // dwStyle [IN] Window styles. // rect [IN] Size and position of the window // pParentWnd [IN OUT ] Parent window. // nID [IN] ID of the window. // pContext [IN OUT] Create context of the window. // // Return Value: // 0 Successful. // !0 Unsuccessful. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterListView::Create( LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT & rect, CWnd * pParentWnd, UINT nID, CCreateContext * pContext ) { BOOL bSuccess; // Set default style bits. dwStyle |= LVS_SHAREIMAGELISTS | LVS_EDITLABELS | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ICON | LVS_REPORT ; bSuccess = CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext); if (bSuccess) { GetListCtrl().SetImageList(GetClusterAdminApp()->PilLargeImages(), LVSIL_NORMAL); GetListCtrl().SetImageList(GetClusterAdminApp()->PilSmallImages(), LVSIL_SMALL); // GetListCtrl().SetImageList(GetClusterAdminApp()->PilSmallImages(), LVSIL_STATE); // Change list view control extended styles. { DWORD dwExtendedStyle; dwExtendedStyle = (DWORD)GetListCtrl().SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE); GetListCtrl().SendMessage( LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwExtendedStyle | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP ); } // Change list view control extended styles } // if: window created successfully return bSuccess; } //*** CClusterListView::Create() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnInitialUpdate // // Routine Description: // Do one-time initialization. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnInitialUpdate() { CListView::OnInitialUpdate(); // Save the frame pointer. // ASSERT(m_pframe == NULL); m_pframe = (CSplitterFrame *) GetParentFrame(); ASSERT_VALID(m_pframe); ASSERT_KINDOF(CSplitterFrame, m_pframe); } //*** CClusterListView::OnInitialUpdate() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::Refresh // // Routine Description: // Refresh the view by reloading all the data. // // Arguments: // ptiSelected [IN OUT] Pointer to currently selected item in the tree control. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::Refresh(IN OUT CTreeItem * ptiSelected) { // Save columns. if (PtiParent() != NULL) SaveColumns(); // Clean up the control to start with. DeleteAllItems(); // Cleanup the previous parent tree item. if (m_ptiParent != NULL) m_ptiParent->Release(); m_ptiParent = ptiSelected; // Setup the new selection. if (m_ptiParent != NULL) { ASSERT_VALID(ptiSelected); CListCtrl & rListCtrl = GetListCtrl(); const CListItemList & rlpli = ptiSelected->LpliChildren(); m_ptiParent->AddRef(); // Add columns to the list control. AddColumns(); // Add items from the tree item's list to the list view. { POSITION pos; CListItem * pli; // Tell the list control how many items we will be adding. // This improves performance. rListCtrl.SetItemCount((int)rlpli.GetCount()); // Add the items to the list control. pos = rlpli.GetHeadPosition(); while (pos != NULL) { pli = rlpli.GetNext(pos); ASSERT_VALID(pli); pli->IliInsertInList(this); } // while: more items in the list } // Add items from the tree item's list to the list view // Give the focus to the first item in the list. if (rListCtrl.GetItemCount() != 0) rListCtrl.SetItem(0, 0, LVIF_STATE, NULL, 0, LVIS_FOCUSED, LVIS_FOCUSED, NULL); } // if: non-null selection // Set the sort column and direction. m_nSortDirection = -1; m_pcoliSort = NULL; } //*** CClusterListView::Refresh() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::DeleteAllItems // // Routine Description: // Delete all the list and column items. // // Arguments: // None. // // Return Value: // TRUE All items deleted successfully. // FALSE Not all items were deleted successfully. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterListView::DeleteAllItems(void) { BOOL bDeletedAllItems; BOOL bDeletedAllColumns = TRUE; int icol; // Remove all the items from our list. { CListItem * pli; int ili; int cli = GetListCtrl().GetItemCount(); // Get the index of the first item. for (ili = 0 ; ili < cli; ili++) { pli = (CListItem *) GetListCtrl().GetItemData(ili); ASSERT_VALID(pli); pli->PreRemoveFromList(this); } // for: each item in the list } // Remove all the items from the cluster item back pointer list // Delete the columns. { for (icol = m_nColumns - 1 ; icol >= 0 ; icol--) { // Delete the column from the view. if (!GetListCtrl().DeleteColumn(icol)) bDeletedAllColumns = FALSE; } // for: each column m_nColumns = 0; } // Delete the columns // Remove all the items from the list. bDeletedAllItems = GetListCtrl().DeleteAllItems(); return (bDeletedAllItems && bDeletedAllColumns); } //*** CClusterListView::DeleteAllItems() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::SaveColumns // // Routine Description: // Save the columns being displayed in the list view. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::SaveColumns(void) { int icol; DWORD * prgnColumnInfo; CListCtrl & rplc = GetListCtrl(); ASSERT_VALID(PtiParent()); // We can only save column information if we are in report view. if (GetView() & LVS_REPORT) { try { // Get the column info array for this view. prgnColumnInfo = PtiParent()->PrgnColumnInfo(this); // Save the widths of the columns. for (icol = m_nColumns - 1 ; icol >= 0 ; icol--) prgnColumnInfo[icol + 1] = rplc.GetColumnWidth(icol); // Save the position information in the array. rplc.SendMessage(LVM_GETCOLUMNORDERARRAY, m_nColumns, (LPARAM) &prgnColumnInfo[m_nColumns + 1]); } // try catch (CException * pe) { pe->Delete(); } // catch: CException } // if: we are in detail view } //*** CClusterListView::SaveColumns() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::AddColumns // // Routine Description: // Add columns to the list view. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::AddColumns(void) { POSITION pos; int cColumns; int icoli = 0; CColumnItem * pcoli; DWORD * prgnColumnInfo; CListCtrl & rplc = GetListCtrl(); ASSERT_VALID(PtiParent()); try { // Get the column info. cColumns = (int)PtiParent()->Lpcoli().GetCount(); prgnColumnInfo = PtiParent()->PrgnColumnInfo(this); pos = PtiParent()->Lpcoli().GetHeadPosition(); for (icoli = 0 ; pos != NULL ; icoli++) { // Get the next column item. pcoli = PtiParent()->Lpcoli().GetNext(pos); ASSERT(pcoli != NULL); // Insert the column item in the list. rplc.InsertColumn( icoli, // nCol pcoli->StrText(), // lpszColumnHeading LVCFMT_LEFT, // nFormat prgnColumnInfo[icoli + 1], // nWidth icoli // nSubItem ); } // while: more items in the list // Set column positions. rplc.SendMessage(LVM_SETCOLUMNORDERARRAY, cColumns, (LPARAM) &prgnColumnInfo[cColumns + 1]); } // try catch (CException * pe) { pe->ReportError(); pe->Delete(); } // catch: CException m_nColumns = icoli; } //*** CClusterListView::AddColumns() ///////////////////////////////////////////////////////////////////////////// // CClusterListView diagnostics ///////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG void CClusterListView::AssertValid(void) const { CListView::AssertValid(); } //*** CClusterListView::AssertValid() void CClusterListView::Dump(CDumpContext& dc) const { CListView::Dump(dc); } //*** CClusterListView::Dump() CClusterDoc * CClusterListView::GetDocument(void) // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CClusterDoc))); return (CClusterDoc *) m_pDocument; } //*** CClusterListView::GetDocument() #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::PliFocused // // Routine Description: // Get the list item that has the focus. // // Arguments: // None. // // Return Value: // pliSelected The item with the focus or NULL if no item has focus. // //-- ///////////////////////////////////////////////////////////////////////////// CListItem * CClusterListView::PliFocused(void) const { int iliFocused; CListItem * pliFocused; iliFocused = IliFocused(); if (iliFocused != -1) { pliFocused = (CListItem *) GetListCtrl().GetItemData(iliFocused); ASSERT_VALID(pliFocused); } // if: found an item with the focus else pliFocused = NULL; return pliFocused; } //*** CClusterListView::PliFocused() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnCmdMsg // // Routine Description: // Processes command messages. Attempts to pass them on to a selected // item first. // // Arguments: // nID [IN] Command ID. // nCode [IN] Notification code. // pExtra [IN OUT] Used according to the value of nCode. // pHandlerInfo [OUT] ??? // // Return Value: // TRUE Message has been handled. // FALSE Message has NOT been handled. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterListView::OnCmdMsg( UINT nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo ) { int ili; CListItem * pli; BOOL bHandled = FALSE; // If there is a current item selected, give it a chance // to handle the message. ili = GetListCtrl().GetNextItem(-1, LVNI_FOCUSED); if (ili != -1) { pli = (CListItem *) GetListCtrl().GetItemData(ili); ASSERT_VALID(pli); bHandled = pli->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } // if: an item is selected if (!bHandled) bHandled = CListView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); return bHandled; } //*** CClusterListView::OnCmdMsg() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::PmenuPopup // // Routine Description: // Returns a popup menu. // // Arguments: // rpointScreen [IN OUT] Position of the cursor, in screen coordinates. // rpci [OUT] Pointer in which to return associated cluster item. // // Return Value: // pmenu A popup menu for the item. // //-- ///////////////////////////////////////////////////////////////////////////// CMenu * CClusterListView::PmenuPopup( IN OUT CPoint & rpointScreen, OUT CClusterItem *& rpci ) { CListItem * pli = NULL; CMenu * pmenu = NULL; rpci = NULL; // If there are no coordinates (-1,-1), display a menu for the selected item. if ((rpointScreen.x == -1) && (rpointScreen.y == -1)) { CListItem * pliFocused = PliFocused(); CRect rect; if ((pliFocused != NULL) && GetListCtrl().GetItemRect(IliFocused(), &rect, LVIR_BOUNDS)) { pli = pliFocused; } // if: item with focus and it is visible else GetWindowRect(&rect); rpointScreen.x = (rect.right - rect.left) / 2; rpointScreen.y = (rect.bottom - rect.top) / 2; ClientToScreen(&rpointScreen); } // if: no coordinates else { CPoint pointClient; int ili; UINT uiFlags; // Get the coordinates of the point where the user clicked the right mouse // button. We need in both screen and client coordinates. pointClient = rpointScreen; ScreenToClient(&pointClient); // Get the item under the cursor and get its popup menu. ili = GetListCtrl().HitTest(pointClient, &uiFlags); if ((ili != -1) && ((uiFlags | LVHT_ONITEM) != 0)) { // Get the list item for the item under the cursor. pli = (CListItem *) GetListCtrl().GetItemData(ili); ASSERT_VALID(pli); } // if: on an item } // else: coordinates specified if (pli != NULL) { // Get a menu from the item. pmenu = pli->PmenuPopup(); rpci = pli->Pci(); } // if: item found return pmenu; } //*** CClusterListView::PmenuPopup() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnUpdateProperties // // Routine Description: // Determines whether menu items corresponding to ID_FILE_PROPERTIES // should be enabled or not. // // Arguments: // pCmdUI [IN OUT] Command routing object. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnUpdateProperties(CCmdUI * pCmdUI) { CListItem * pliFocused = PliFocused(); // If there is an item with the focus, pass this message on to it. if (pliFocused != NULL) { ASSERT_VALID(pliFocused->Pci()); pliFocused->Pci()->OnUpdateProperties(pCmdUI); } // if: there is an item with the focus } //*** CClusterListView::OnUpdateProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnCmdProperties // // Routine Description: // Processes the ID_FILE_PROPERTIES menu command. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnCmdProperties(void) { CListItem * pliFocused = PliFocused(); // If there is an item with the focus, pass this message on to it. if (pliFocused != NULL) { ASSERT_VALID(pliFocused->Pci()); pliFocused->Pci()->OnCmdProperties(); } // if: there is an item with the focus } //*** CClusterListView::OnCmdProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnItemChanged // // Routine Description: // Handler method for the LVN_ITEMCHANGED message. // // Arguments: // pNMHDR [IN OUT] WM_NOTIFY structure. // pResult [OUT] LRESULT in which to return the result of this operation. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnItemChanged(NMHDR * pNMHDR, LRESULT * pResult) { NM_LISTVIEW * pNMListView = (NM_LISTVIEW *) pNMHDR; CWnd * pwndFocus = GetFocus(); CListItem * pli; if (m_pDocument != NULL) // this happens on system shutdown { // If the item has just lost or received the focus, save it and set the menu. if ((pNMListView->uChanged & LVIF_STATE) && (pwndFocus == &GetListCtrl())) { ASSERT(pNMListView->iItem != -1); // Get the item whose state is changing. pli = (CListItem *) pNMListView->lParam; ASSERT_VALID(pli); if ((pNMListView->uOldState & LVIS_FOCUSED) && !(pNMListView->uNewState & LVIS_FOCUSED)) { Trace(g_tagListView, _T("OnItemChanged() - '%s' lost focus"), pli->Pci()->StrName()); // Tell the document of the new selection. GetDocument()->OnSelChanged(NULL); } // if: old item losing focus else if (!(pNMListView->uOldState & LVIS_FOCUSED) && (pNMListView->uNewState & LVIS_FOCUSED)) { Trace(g_tagListView, _T("OnItemChanged() - '%s' received focus"), pli->Pci()->StrName()); // Tell the document of the new selection. GetDocument()->OnSelChanged(pli->Pci()); } // else: new item receiving focus } // if: item received the focus } // if: document is available *pResult = 0; } //*** CClusterListView::OnItemChanged() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnActivateView // // Routine Description: // Called when the view is activated. // // Arguments: // bActivate [IN] Indicates whether the view being activated or deactivated. // pActivateView [IN OUT] Points to the view object that is being activated. // peactiveView [IN OUT] Points to the view object that is being deactivated. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnActivateView( BOOL bActivate, CView * pActivateView, CView * pDeactiveView ) { CListItem * pliFocused = PliFocused(); if (m_pDocument != NULL) // this happens on system shutdown { if (bActivate && (pliFocused != NULL)) { ASSERT_VALID(pliFocused->Pci()); Trace(g_tagListView, _T("OnActivateView() - '%s' received focus"), pliFocused->Pci()->StrName()); // Tell the document of the new selection. GetDocument()->OnSelChanged(pliFocused->Pci()); } // if: we are being activated } // if: document is available CListView::OnActivateView(bActivate, pActivateView, pDeactiveView); } //*** CClusterListView::OnActivateView() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnDestroy // // Routine Description: // Handler method for the WM_DESTROY message. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnDestroy(void) { // Save the columns. if (PtiParent() != NULL) SaveColumns(); // Clean up the control. DeleteAllItems(); CListView::OnDestroy(); } //*** CClusterListView::OnDestroy() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnColumnClick // // Routine Description: // Handler method for the LVN_COLUMNCLICK message. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnColumnClick(NMHDR * pNMHDR, LRESULT * pResult) { NM_LISTVIEW * pNMListView = (NM_LISTVIEW *) pNMHDR; int ili; CListItem * pli; POSITION pos; CColumnItem * pcoli; if (GetListCtrl().GetItemCount() != 0) { // Get the first item in the list. ili = GetListCtrl().GetNextItem(-1, LVNI_ALL); ASSERT(ili != -1); pli = (CListItem *) GetListCtrl().GetItemData(ili); ASSERT_VALID(pli); ASSERT_VALID(pli->PtiParent()); // Get the column item to sort by. pos = pli->PtiParent()->Lpcoli().FindIndex(pNMListView->iSubItem); ASSERT(pos != NULL); pcoli = pli->PtiParent()->Lpcoli().GetAt(pos); ASSERT_VALID(pcoli); // Save the current sort column and direction. if (pcoli == PcoliSort()) m_nSortDirection ^= -1; else { m_pcoliSort = pcoli; m_nSortDirection = 0; } // else: different column // Sort the list. GetListCtrl().SortItems(CompareItems, (LPARAM) this); } // if: there are items in the list *pResult = 0; } //*** CClusterListView::OnColumnClick() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::CompareItems [static] // // Routine Description: // Callback function for the CListCtrl::SortItems method. // // Arguments: // lparam1 First item to compare. // lparam2 Second item to compare. // lparamSort Sort parameter. // // Return Value: // -1 First parameter comes before second. // 0 First and second parameters are the same. // 1 First parameter comes after second. // //-- ///////////////////////////////////////////////////////////////////////////// int CALLBACK CClusterListView::CompareItems( LPARAM lparam1, LPARAM lparam2, LPARAM lparamSort ) { CListItem * pli1 = (CListItem *) lparam1; CListItem * pli2 = (CListItem *) lparam2; CClusterListView * pclv = (CClusterListView *) lparamSort; CString str1; CString str2; int nResult; ASSERT_VALID(pli1); ASSERT_VALID(pli2); ASSERT_VALID(pli1->Pci()); ASSERT_VALID(pli2->Pci()); ASSERT_VALID(pclv); ASSERT_VALID(pclv->PcoliSort()); // Get the strings from the list items. pli1->Pci()->BGetColumnData(pclv->PcoliSort()->Colid(), str1); pli2->Pci()->BGetColumnData(pclv->PcoliSort()->Colid(), str2); // Compare the two strings. // Use CompareString() so that it will sort properly on localized builds. nResult = CompareString( LOCALE_USER_DEFAULT, 0, str1, str1.GetLength(), str2, str2.GetLength() ); if ( nResult == CSTR_LESS_THAN ) { nResult = -1; } else if ( nResult == CSTR_EQUAL ) { nResult = 0; } else if ( nResult == CSTR_GREATER_THAN ) { nResult = 1; } else { // An error occurred. Ignore it. nResult = 0; } // Return the result based on the direction we are sorting. if (pclv->NSortDirection() != 0) nResult = -nResult; return nResult; } //*** CClusterListView::CompareItems() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnDblClk // // Routine Description: // Handler method for the NM_DBLCLK message. // // Arguments: // pNMHDR Notification message structure. // pResult Place in which to return the result. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnDblClk(NMHDR * pNMHDR, LRESULT * pResult) { OpenItem(); *pResult = 0; } //*** CClusterListView::OnDblClk() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OpenItem // // Routine Description: // Open the item with the focus. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OpenItem(void) { CListItem * pliFocused = PliFocused(); if (m_pliBeingEdited == NULL) { // If an item has focus, open it or show its properties. if (pliFocused != NULL) { CTreeItem * pti; ASSERT_VALID(pliFocused->PtiParent()); Trace(g_tagListView, _T("Opening item '%s'"), pliFocused->Pci()->StrName()); // Find the item tree item for the list item. pti = pliFocused->PtiParent()->PtiChildFromPci(pliFocused->Pci()); // If this item has a tree item, open it up. Otherwise show its // properties. if (pti != NULL) { CSplitterFrame * pframe; // Get the frame pointer so we can talk to the tree view. pframe = (CSplitterFrame *) GetParentFrame(); ASSERT_KINDOF(CSplitterFrame, pframe); pliFocused->PtiParent()->OpenChild(pti, pframe); } // if: item is openable else OnCmdProperties(); } // if: an item has focus } // if: label not being edited else { ASSERT_VALID(m_pliBeingEdited); Trace(g_tagListView, _T("Not opening item '%s'"), m_pliBeingEdited->Pci()->StrName()); } // else if: label being edited } //*** CClusterListView::OpenItem() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnBeginLabelEdit // // Routine Description: // Handler method for the LVN_BEGINLABELEDIT message. // // Arguments: // pNMHDR Notification message structure. // pResult Place in which to return the result. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnBeginLabelEdit(NMHDR * pNMHDR, LRESULT * pResult) { ASSERT(pNMHDR != NULL); LV_DISPINFO * pDispInfo = (LV_DISPINFO *) pNMHDR; CListItem * pli = (CListItem *) pDispInfo->item.lParam; ASSERT(m_pliBeingEdited == NULL); ASSERT_VALID(pli->Pci()); if (pli->Pci()->BCanBeEdited()) { pli->Pci()->OnBeginLabelEdit(GetListCtrl().GetEditControl()); m_pliBeingEdited = pli; *pResult = FALSE; } // if: object can be renamed else *pResult = TRUE; m_bShiftPressed = FALSE; m_bControlPressed = FALSE; m_bAltPressed = FALSE; } //*** CClusterListView::OnBeginLabelEdit() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnEndLabelEdit // // Routine Description: // Handler method for the LVN_ENDLABELEDIT message. // // Arguments: // pNMHDR Notification message structure. // pResult Place in which to return the result. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnEndLabelEdit(NMHDR * pNMHDR, LRESULT * pResult) { ASSERT(pNMHDR != NULL); LV_DISPINFO * pDispInfo = (LV_DISPINFO *) pNMHDR; CListItem * pli = (CListItem *) pDispInfo->item.lParam; ASSERT_VALID(pli); ASSERT(pli == m_pliBeingEdited); ASSERT_VALID(pli->Pci()); // If the edit wasn't cancelled, rename it. if (pDispInfo->item.mask & LVIF_TEXT) { ASSERT(pli->Pci()->BCanBeEdited()); ASSERT(pDispInfo->item.pszText != NULL); Trace(g_tagListView, _T("Ending edit of item '%s' (Saving as '%s')"), pli->Pci()->StrName(), pDispInfo->item.pszText); if ( pli->Pci()->BIsLabelEditValueValid( pDispInfo->item.pszText ) ) { try { pli->Pci()->Rename(pDispInfo->item.pszText); *pResult = TRUE; } // try catch (CException * pe) { pe->ReportError(); pe->Delete(); *pResult = FALSE; } // catch: CException } // if: name is valid else { *pResult = FALSE; } } // if: the edit wasn't cancelled else { Trace(g_tagListView, _T("Ending edit of item '%s' (Not Saving)"), pli->Pci()->StrName()); *pResult = TRUE; } // else: edit was cancelled m_pliBeingEdited = NULL; m_bShiftPressed = FALSE; m_bControlPressed = FALSE; m_bAltPressed = FALSE; } //*** CClusterListView::OnEndLabelEdit() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnBeginDrag // // Routine Description: // Handler method for the LVN_BEGINDRAG and LVN_BEGINRDRAG messages. // // Arguments: // pNMHDR Notification message structure. // pResult Place in which to return the result. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnBeginDrag(NMHDR * pNMHDR, LRESULT * pResult) { NM_LISTVIEW * pNMListView = (NM_LISTVIEW *) pNMHDR; CListCtrl & rlc = GetListCtrl(); CPoint ptAction; CClusterItem * pci = NULL; CImageList * pimagelist; ASSERT_VALID(Pframe()); // Get the item being dragged. { int ili; CListItem * pli; // Get the item being dragged. ili = pNMListView->iItem; pli = (CListItem *) rlc.GetItemData(ili); ASSERT_VALID(pli); ASSERT_KINDOF(CListItem, pli); ASSERT_VALID(pli->Pci()); // If the item can not be dragged, abort the operation. if (!pli->Pci()->BCanBeDragged()) return; // Deselect the item being dragged. rlc.SetItemState(ili, 0, LVIS_SELECTED); // Save info for later. m_iliDrag = ili; m_pliDrag = pli; m_iliDrop = -1; pci = pli->Pci(); } // Get the item being dragged // Create the image list and let the frame window initialize the drag operation. { CPoint ptImage; CPoint ptFrameItem; CPoint ptHotSpot; pimagelist = rlc.CreateDragImage(m_iliDrag, &ptImage); ASSERT(pimagelist != NULL); ptFrameItem = pNMListView->ptAction; Pframe()->ScreenToClient(&ptFrameItem); // Calculate the hot spot point. { long lStyle = rlc.GetStyle() & LVS_TYPEMASK; switch (lStyle) { case LVS_REPORT: case LVS_LIST: case LVS_SMALLICON: ptHotSpot.x = 0; ptHotSpot.y = -16; break; case LVS_ICON: ptHotSpot.x = 8; ptHotSpot.y = 8; break; } // switch: lStyle } // Calculate the hot spot point Trace(g_tagListDrag, _T("OnBeginDrag() - Dragging '%s' at (%d,%d)"), m_pliDrag->StrName(), ptFrameItem.x, ptFrameItem.y); Pframe()->BeginDrag(pimagelist, pci, ptFrameItem, ptHotSpot); pimagelist->SetDragCursorImage(0, CPoint(0, 0)); // define the hot spot for the new cursor image } // Create the image list and let the frame window initialize the drag operation *pResult = 0; } //*** CClusterListView::OnBeginDrag(pNMHDR, pResult) ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnMouseMoveForDrag // // Routine Description: // Handler method for the WM_MOUSEMOVE message during a drag operation. // This function is only responsible for providing view-specific // functionality, such as selecting the drop target if it is valid. // // Arguments: // nFlags Indicates whether various virtual keys are down. // point Specifies the x- and y-coordinate of the cursor in frame // coordinates. // pwndDrop Specifies the window under the cursor. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnMouseMoveForDrag( IN UINT nFlags, IN OUT CPoint point, IN const CWnd * pwndDrop ) { ASSERT(BDragging()); ASSERT_VALID(Pframe()); // If we are dragging, select the drop target. if (BDragging()) { int ili; UINT uFlags; CPoint ptView; CListCtrl & rlc = GetListCtrl(); // Convert the point to view coordinates. ptView = point; Pframe()->ClientToScreen(&ptView); rlc.ScreenToClient(&ptView); // If this window is the drop target, find the item under the cursor. if (pwndDrop == &rlc) { // If we are over a list item, highlight it. ili = rlc.HitTest(ptView, &uFlags); if (ili != -1) { CListItem * pli; // Get the item to be highlight. pli = (CListItem *) rlc.GetItemData(ili); ASSERT_VALID(pli); ASSERT_KINDOF(CListItem, pli); ASSERT_VALID(pli->Pci()); // If this is not a drop target, change the cursor. if (pli->Pci()->BCanBeDropTarget(Pframe()->PciDrag())) Pframe()->ChangeDragCursor(IDC_ARROW); else Pframe()->ChangeDragCursor(IDC_NO); } // if: over a list item } // if: this window is the drop target else ili = -1; // If the drop target is or was in this view, update the view. if ((ili != -1) || (m_iliDrop != -1)) { // Unlock window updates. VERIFY(Pimagelist()->DragShowNolock(FALSE /*bShow*/)); // Turn off highlight for the previous drop target. if (m_iliDrop != -1) { VERIFY(rlc.SetItemState(m_iliDrop, 0, LVIS_DROPHILITED)); VERIFY(rlc.RedrawItems(m_iliDrop, m_iliDrop)); } // if: there was a previous drop target // Highlight the new drop target. if (ili != -1) { VERIFY(rlc.SetItemState(ili, LVIS_DROPHILITED, LVIS_DROPHILITED)); VERIFY(rlc.RedrawItems(ili, ili)); } // if: over an item m_iliDrop = ili; rlc.UpdateWindow(); VERIFY(Pimagelist()->DragShowNolock(TRUE /*bShow*/)); } // if: new or old drop target } // if: list item is being dragged } //*** CClusterListView::OnMouseMoveForDrag() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnButtonUpForDrag // // Routine Description: // Called to handle a button up event during drag and drop. // // Arguments: // nFlags Indicates whether various virtual keys are down. // point Specifies the x- and y-coordinate of the cursor. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnButtonUpForDrag(IN UINT nFlags, IN CPoint point) { ASSERT(BDragging()); ASSERT_VALID(Pframe()); ASSERT_VALID(Pframe()->PciDrag()); // If we are dragging, process the drop. if (BDragging()) { int ili; UINT flags; CPoint ptView; CListCtrl & rlc = GetListCtrl(); Trace(g_tagListDrag, _T("OnButtonUpForDrag()")); // Convert the point to view coordinates. ptView = point; Pframe()->ClientToScreen(&ptView); rlc.ScreenToClient(&ptView); // If we are over a tree item, drop the item being dragged. ili = rlc.HitTest(ptView, &flags); if (ili != -1) { CListItem * pliDropTarget; // Get the item to drop on. pliDropTarget = (CListItem *) rlc.GetItemData(ili); ASSERT_VALID(pliDropTarget); ASSERT_KINDOF(CListItem, pliDropTarget); ASSERT_VALID(pliDropTarget->Pci()); if (pliDropTarget->Pci() != Pframe()->PciDrag()) pliDropTarget->Pci()->DropItem(Pframe()->PciDrag()); } // if: over a tree item } // if: tree item is being dragged } //*** CClusterListView::OnButtonUpForDrag() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::BeginDrag // // Routine Description: // Called by the frame to begin a drag operation. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::BeginDrag(void) { Trace(g_tagListDrag, _T("BeginDrag()")); } //*** CClusterListView::BeginDrag() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::EndDrag // // Routine Description: // Called by the frame to end a drag operation. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::EndDrag(void) { // Clear and reset highlights. The second one can fail, since the item // could be removed from the list by this time. if (m_iliDrop != -1) VERIFY(GetListCtrl().SetItemState(m_iliDrop, 0, LVIS_DROPHILITED)); if (m_iliDrag != -1) GetListCtrl().SetItemState(m_iliDrag, LVIS_SELECTED, LVIS_SELECTED); m_iliDrag = -1; m_pliDrag = NULL; m_iliDrop = -1; Trace(g_tagListDrag, _T("EndDrag()")); } //*** CClusterListView::EndDrag() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::PreTranslateMessage // // Routine Description: // Translate window messages before they are dispatched. // This is necessary for handling keystrokes properly while editing // the label on an item. // // Arguments: // pMsg Points to a MSG structure that contains the message to process. // // Return Value: // TRUE Message was handled. // FALSE Message was not handled. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusterListView::PreTranslateMessage(MSG * pMsg) { BOOL bForward = FALSE; if (m_pliBeingEdited != NULL) { CEdit * pedit = GetListCtrl().GetEditControl(); ASSERT(pedit != NULL); if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_SHIFT) m_bShiftPressed = TRUE; else if (pMsg->wParam == VK_CONTROL) { ::CopyMemory(&m_msgControl, pMsg, sizeof(m_msgControl)); m_bControlPressed = TRUE; } // else if: control key pressed else if ((pMsg->wParam == VK_RETURN) || (pMsg->wParam == VK_ESCAPE) || (pMsg->wParam == VK_INSERT) || (pMsg->wParam == VK_DELETE) || (pMsg->wParam == VK_F1) || (pMsg->wParam == VK_F5) || (pMsg->wParam == VK_F6) ) { Trace(g_tagListView, _T("PreTranslateMessage() - Forwarding WM_KEYDOWN - %d '%c', lparam = %08.8x"), pMsg->wParam, pMsg->wParam, pMsg->lParam); bForward = TRUE; if (m_bControlPressed) { if (pMsg->wParam == VK_RETURN) pedit->SendMessage(WM_KEYUP, m_msgControl.wParam, m_msgControl.lParam); } // if: control key pressed } // else if: editing key pressed else if ((pMsg->wParam == VK_TAB) || (m_bControlPressed && (_T('A') <= pMsg->wParam) && (pMsg->wParam <= _T('Y')) && (pMsg->wParam != _T('C')) && (pMsg->wParam != _T('H')) && (pMsg->wParam != _T('M')) && (pMsg->wParam != _T('V')) && (pMsg->wParam != _T('X')) ) ) { Trace(g_tagListView, _T("PreTranslateMessage() - Ignoring WM_KEYDOWN - %d '%c', lparam = %08.8x"), pMsg->wParam, pMsg->wParam, pMsg->lParam); MessageBeep(MB_ICONEXCLAMATION); return TRUE; } // else if: key pressed that should be ignored #ifdef NEVER else { Trace(g_tagListView, _T("PreTranslateMessage() - Not forwarding WM_KEYDOWN - %d '%c', lparam = %08.8x"), pMsg->wParam, pMsg->wParam, pMsg->lParam); } // else: not processing key #endif } // if: key pressed while editing label else if (pMsg->message == WM_SYSKEYDOWN) { if (pMsg->wParam == VK_MENU) m_bAltPressed = TRUE; else if ((pMsg->wParam == VK_RETURN) ) { Trace(g_tagListView, _T("PreTranslateMessage() - Forwarding WM_SYSKEYDOWN - %d '%c', lparam = %08.8x"), pMsg->wParam, pMsg->wParam, pMsg->lParam); bForward = TRUE; } // else if: editing key pressed #ifdef NEVER else { Trace(g_tagListView, _T("PreTranslateMessage() - Not forwarding WM_SYSKEYDOWN - %d '%c', lparam = %08.8x"), pMsg->wParam, pMsg->wParam, pMsg->lParam); } // else: not processing key #endif } // else if: system key pressed while editing label if (bForward) { pedit->SendMessage(pMsg->message, pMsg->wParam, pMsg->lParam); return TRUE; } // if: forwarding the message else if (pMsg->message == WM_KEYUP) { if (pMsg->wParam == VK_SHIFT) m_bShiftPressed = FALSE; else if (pMsg->wParam == VK_CONTROL) m_bControlPressed = FALSE; } // else if: key up else if (pMsg->message == WM_SYSKEYUP) { if (pMsg->wParam == VK_MENU) m_bAltPressed = FALSE; } // else if: system key up } // if: editing a label return CListView::PreTranslateMessage(pMsg); } //*** CClusterListView::PreTranslateMessage() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnCmdRename // // Routine Description: // Processes the ID_FILE_RENAME menu command. // // Arguments: // None. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnCmdRename(void) { CListItem * pliFocused = PliFocused(); // If an item has focus, begin label editing if (pliFocused != NULL) { ASSERT_VALID(pliFocused); pliFocused->EditLabel(this); } // if: an item has the focus } //*** CClusterListView::OnCmdRename() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::SetView // // Routine Description: // Set the current view of the list view control. // // Arguments: // dwView [IN] List view to set. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::SetView(IN DWORD dwView) { // Get the current window style. DWORD dwStyle = GetWindowLong(GetListCtrl().m_hWnd, GWL_STYLE); // Only set the window style if the view bits have changed. if ((dwStyle & LVS_TYPEMASK) != dwView) { // Save the column information before switching out of report view. if ((dwStyle & LVS_REPORT) && (PtiParent() != NULL)) SaveColumns(); // Set the new view. SetWindowLong(GetListCtrl().m_hWnd, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView); } // if: view has changed } //*** CClusterListView::SetView() ///////////////////////////////////////////////////////////////////////////// //++ // // CClusterListView::OnKeyDown // // Routine Description: // Handler method for the LVN_KEYDOWN message. // // Arguments: // pNMHDR Notification message structure. // pResult Place in which to return the result. // // Return Value: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CClusterListView::OnKeyDown(NMHDR * pNMHDR, LRESULT * pResult) { LV_KEYDOWN * pLVKeyDown = (LV_KEYDOWN *) pNMHDR; if (BDragging() && (pLVKeyDown->wVKey == VK_ESCAPE)) Pframe()->AbortDrag(); *pResult = 0; } //*** CClusterListView::OnKeyDown()