Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1684 lines
50 KiB

/////////////////////////////////////////////////////////////////////////////
//
// 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()