Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

6716 lines
183 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 1999
//
// File: cclvctl.cpp
//
//--------------------------------------------------------------------------
// cclvctl.cpp : implementation file
//
#include "stdafx.h"
#include "cclvctl.h"
#include <malloc.h>
#include <wtypes.h>
#include "amcdoc.h"
#include "amcview.h"
#include "mmcres.h"
#include "treectrl.h"
#include "util.h"
#include "amcpriv.h"
#include "rsltitem.h"
#include "columninfo.h"
#include "bitmap.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//############################################################################
//############################################################################
//
// Traces
//
//############################################################################
//############################################################################
#ifdef DBG
CTraceTag tagList(TEXT("List View"), TEXT("List View"));
CTraceTag tagListImages(_T("Images"), _T("List view (draw when changed)"));
CTraceTag tagColumn(TEXT("Columns"), TEXT("Columns"));
#endif //DBG
DEBUG_DECLARE_INSTANCE_COUNTER(CAMCListView);
//############################################################################
//############################################################################
//
// Implementation of class CColumnsBase
//
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
* class CColumnsBase
*
*
* PURPOSE: Implements the Columns automation interface.
*
*+-------------------------------------------------------------------------*/
class CColumnsBase :
public CMMCIDispatchImpl<Columns>,
public CTiedComObject<CCCListViewCtrl>,
public CTiedObject // this is for enumerators
{
protected:
typedef CCCListViewCtrl CMyTiedObject;
public:
BEGIN_MMC_COM_MAP(CColumnsBase)
END_MMC_COM_MAP()
// Columns interface
public:
MMC_METHOD2(Item, long /*Index*/, PPCOLUMN /*ppColumn*/);
// properties
MMC_METHOD1(get_Count, PLONG /*pCount*/);
};
// this typedefs the real CColumns class. Implements get__NewEnum using CMMCEnumerator
typedef CMMCNewEnumImpl<CColumnsBase, int> CColumns;
/*+-------------------------------------------------------------------------*
* class CColumn
*
*
* PURPOSE: Implements the Node automation interface, for a result node
*
*+-------------------------------------------------------------------------*/
class CColumn :
public CMMCIDispatchImpl<Column>,
public CTiedComObject<CCCListViewCtrl>,
public CListViewObserver
{
protected:
typedef CCCListViewCtrl CMyTiedObject;
public:
BEGIN_MMC_COM_MAP(CColumn)
END_MMC_COM_MAP()
// Column methods
public:
MMC_METHOD1_PARAM( Name, /*[out, retval]*/ BSTR* /*Name*/ , m_iIndex);
MMC_METHOD1_PARAM( get_Width, /*[out, retval]*/ PLONG /*Width*/, m_iIndex);
MMC_METHOD1_PARAM( put_Width, /*[in]*/ long /*Width*/, m_iIndex);
MMC_METHOD1_PARAM( get_DisplayPosition, /*[out, retval]*/ PLONG /*DisplayPosition*/, m_iIndex);
MMC_METHOD1_PARAM( put_DisplayPosition, /*[in]*/ long /*Index*/, m_iIndex);
MMC_METHOD1_PARAM( get_Hidden, /*[out, retval]*/ PBOOL /*Hidden*/, m_iIndex );
MMC_METHOD1_PARAM( put_Hidden, /*[in]*/ BOOL /*Hidden*/ , m_iIndex );
MMC_METHOD1_PARAM( SetAsSortColumn, /*[in]*/ ColumnSortOrder /*SortOrder*/, m_iIndex);
MMC_METHOD1_PARAM( IsSortColumn, PBOOL /*IsSortColumn*/, m_iIndex);
CColumn() : m_iIndex(-1) { }
void SetIndex(int iIndex) { m_iIndex = iIndex; }
// observed events
// called when column is inserted to listview
virtual ::SC ScOnListViewColumnInserted (int nIndex);
// called when column is deleted from listview
virtual ::SC ScOnListViewColumnDeleted (int nIndex);
private: // implementation
int m_iIndex;
};
/////////////////////////////////////////////////////////////////////////////
// CAMCHeaderCtrl
// This class is defined just to intercept the header's set focus
BEGIN_MESSAGE_MAP(CAMCHeaderCtrl, CHeaderCtrl)
ON_WM_SETFOCUS()
ON_WM_SETCURSOR()
END_MESSAGE_MAP()
void CAMCHeaderCtrl::OnSetFocus(CWnd *pOldWnd)
{
// Make sure list view is made active, but don't steal focus from header
CAMCListView* pwndParent = dynamic_cast<CAMCListView*>(GetParent());
ASSERT(pwndParent != NULL);
pwndParent->GetParentFrame()->SetActiveView(pwndParent, FALSE);
CHeaderCtrl::OnSetFocus(pOldWnd);
}
//+-------------------------------------------------------------------
//
// Member: CAMCHeaderCtrl::OnSetCursor
//
// Synopsis: If the cursor is on hidden column do not show the divider
// cursor. WM_SETCURSOR handler.
//
// Arguments: [pWnd] - window which generated the message.
// [nHitTest] - hittest flag.
// [message] -
//
// Returns: BOOL, TRUE stop processing further.
//
//--------------------------------------------------------------------
BOOL CAMCHeaderCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// 1. If the mouse is on the header window.
if ( (nHitTest == HTCLIENT) && (pWnd == this) )
{
// 2. Get its position.
CPoint pt (GetMessagePos());
ScreenToClient (&pt);
// 3. Do a hit test
HDHITTESTINFO hitinfo;
ZeroMemory(&hitinfo, sizeof(hitinfo));
hitinfo.pt = pt;
if (SendMessage(HDM_HITTEST, 0, reinterpret_cast<LPARAM>(&hitinfo) ) != -1)
{
// 4. If the mouse is on a column of zero width and it is hidden do not
// process the message further.
// 4.a) HHT_ONDIVOPEN : pt is on the divider of an item that has a width of zero.
// b) HHT_ONDIVIDER : pt is on the divider between two header items.
if ( ( (HHT_ONDIVOPEN | HHT_ONDIVIDER) & hitinfo.flags) &&
(IsColumnHidden(hitinfo.iItem /*column index*/)) )
{
// Set default arrow cursor.
::SetCursor(::LoadCursor(NULL, IDC_ARROW) );
return TRUE;
}
}
}
return CHeaderCtrl::OnSetCursor(pWnd, nHitTest, message);
}
//+-------------------------------------------------------------------
//
// Member: CAMCHeaderCtrl::IsColumnHidden
//
// Synopsis: Is the given column hidden?
//
// Arguments: [iCol] - given column
//
// Returns: bool
//
//--------------------------------------------------------------------
bool CAMCHeaderCtrl::IsColumnHidden(int iCol)
{
// Get param to determine if column is hidden.
HDITEM hdItem;
ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_LPARAM;
if (GetItem(iCol, &hdItem))
{
CHiddenColumnInfo hci (hdItem.lParam);
if (hci.fHidden)
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////
// CAMCListView
const UINT CAMCListView::m_nColumnPersistedDataChangedMsg = ::RegisterWindowMessage (_T("CAMCListView::OnColumnPersistedDataChanged"));
BEGIN_MESSAGE_MAP(CAMCListView, CListView)
//{{AFX_MSG_MAP(CAMCListView)
ON_WM_CREATE()
ON_WM_KEYUP()
ON_WM_KEYDOWN()
ON_WM_SYSKEYDOWN()
ON_WM_SYSCHAR()
ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
ON_NOTIFY_REFLECT(LVN_BEGINRDRAG, OnBeginRDrag)
ON_WM_MOUSEACTIVATE()
ON_WM_SETFOCUS()
ON_WM_PAINT()
ON_WM_SIZE()
ON_NOTIFY(HDN_BEGINTRACK, 0, OnBeginTrack)
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE (m_nColumnPersistedDataChangedMsg, OnColumnPersistedDataChanged)
END_MESSAGE_MAP()
BOOL CAMCListView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= WS_BORDER |
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN |
LVS_SHAREIMAGELISTS |
LVS_SINGLESEL |
LVS_EDITLABELS |
LVS_SHOWSELALWAYS |
LVS_REPORT;
return CListView::PreCreateWindow(cs);
}
int CAMCListView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
DECLARE_SC(sc, TEXT("CAMCListView::OnCreate"));
if (CListView::OnCreate(lpCreateStruct) == -1)
return -1;
// Get parent's CWnd for command routing
m_pAMCView = ::GetAMCView (this);
/*
* add extended list view styles (these can't be handled in PreCreateWindow)
*/
SetExtendedListViewStyle (LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP |
LVS_EX_LABELTIP);
sc = ScRegisterAsDropTarget(m_hWnd);
if (sc)
return (-1);
AddObserver(static_cast<CListViewActivationObserver &>(*m_pAMCView));
return 0;
}
void CAMCListView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if ((VK_CONTROL == nChar) || (VK_SHIFT == nChar))
{
ASSERT (m_pAMCView != NULL);
m_pAMCView->SendMessage (WM_KEYUP, nChar, MAKELPARAM (nRepCnt, nFlags));
return;
}
CListView::OnKeyUp(nChar, nRepCnt, nFlags);
}
//+-------------------------------------------------------------------
//
// Member: OnKeyDown
//
// Synopsis: Handle any non-system keys (Without ALT).
// (Below handle Ctrl+A to multi-select
// all items in list view).
//
// Arguments:
//
// Returns: None.
//
//
//--------------------------------------------------------------------
void CAMCListView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case 'A':
{
// Check if LV is multi-sel enabled.
if (GetStyle() & LVS_SINGLESEL)
break;
SHORT nKeyState = GetKeyState(VK_CONTROL);
// Key is down if higher order bits are set in nKeyState.
nKeyState = nKeyState >> sizeof(SHORT) * 4;
if (nKeyState == 0)
break;
// Ctrl+A --> Select all items in list view.
LV_ITEM lvi;
lvi.stateMask = lvi.state = LVIS_SELECTED;
for (int i = 0; i < GetListCtrl().GetItemCount(); ++i)
{
// NOTE: do not use GetListCtrl().SetItemState - it uses SetItem which is not supported for virtual lists
if (!GetListCtrl().SendMessage( LVM_SETITEMSTATE, WPARAM(i), (LPARAM)(LV_ITEM FAR *)&lvi))
return;
}
return;
}
default:
break;
}
CListView::OnKeyDown(nChar, nRepCnt, nFlags);
}
/*+-------------------------------------------------------------------------*
*
* CAMCListView::OnSysKeyDown
*
* PURPOSE: Handle the WM_SYSCHAR message.
*
* PARAMETERS:
* UINT nChar :
* UINT nRepCnt :
* UINT nFlags :
*
* RETURNS:
* void
*
*+-------------------------------------------------------------------------*/
void CAMCListView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if ((VK_LEFT == nChar) || (VK_RIGHT == nChar))
{
ASSERT (m_pAMCView != NULL);
m_pAMCView->SendMessage (WM_SYSKEYDOWN, nChar, MAKELPARAM (nRepCnt, nFlags));
return;
}
CListView::OnSysKeyDown(nChar, nRepCnt, nFlags);
}
void CAMCListView::OnSysChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (VK_RETURN == nChar)
{
return; // don't call base class, otherwise a beep occurs. Handled by LVN_KEYDOWN
}
CListView::OnSysChar(nChar, nRepCnt, nFlags);
}
/*+-------------------------------------------------------------------------*
*
* CAMCListView::OnPaint
*
* PURPOSE: Displays a default message when no items are present in the list.
*
* RETURNS:
* void
*
*+-------------------------------------------------------------------------*/
void
CAMCListView::OnPaint()
{
Default();
if (NeedsCustomPaint())
{
COLORREF clrText = ::GetSysColor(COLOR_WINDOWTEXT);
COLORREF clrTextBk = ::GetSysColor(COLOR_WINDOW);
CClientDC dc(this);
// Save dc state
int nSavedDC = dc.SaveDC();
CRect rc;
GetClientRect(&rc);
CHeaderCtrl* pHC = GetHeaderCtrl();
if (pHC != NULL && ((GetListCtrl().GetStyle() & (LVS_REPORT | LVS_LIST | LVS_SMALLICON | LVS_ICON)) ==LVS_REPORT) ) // make sure that the style is report
{
CRect rcH;
pHC->GetItemRect(0, &rcH);
rc.top += rcH.bottom;
}
rc.top += 10;
CString strText;
strText.LoadString(IDS_EMPTY_LIST_MESSAGE); // The message
// create the font - we do not cache it.
LOGFONT lf;
CFont font;
SystemParametersInfo (SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, false);
font.CreateFontIndirect(&lf);
dc.SelectObject(&font); // select the font
dc.SetTextColor(clrText);
dc.SetBkColor(clrTextBk);
dc.FillRect(rc, &CBrush(clrTextBk));
dc.DrawText(strText, -1, rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP);
// Restore dc
dc.RestoreDC(nSavedDC);
}
// Do not call CListCtrl::OnPaint() for painting messages (Default was called above)
}
/*+-------------------------------------------------------------------------*
* CAMCListView::OnSize
*
* WM_SIZE handler for CAMCListView.
*--------------------------------------------------------------------------*/
void CAMCListView::OnSize(UINT nType, int cx, int cy)
{
CListView::OnSize(nType, cx, cy);
/*
* if we're custom painting, we need to redraw the list
* because we need to keep the text horizontally centered
*/
if (NeedsCustomPaint())
Invalidate ();
}
/*+-------------------------------------------------------------------------*
* CAMCListView::NeedsCustomPaint
*
* Determines whether we want to draw "There are no items..." in the list
* view.
*--------------------------------------------------------------------------*/
bool CAMCListView::NeedsCustomPaint()
{
CHeaderCtrl* pHC = GetHeaderCtrl();
// we check the column counts because there is a transition state when no columns are
// present during which we shouldn't draw anything.
return (GetListCtrl().GetItemCount() <= 0 && (pHC != NULL) && pHC->GetItemCount()>0);
}
BOOL CAMCListView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo )
{
// Do normal command routing
if (CListView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// if view did't handle it, give parent view a chance
if (m_pAMCView != NULL)
{
// OnCmdMsg is public in CCmdTarget, but protected in CView
// cast around it (arghhh!)
return ((CWnd*)m_pAMCView)->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
return FALSE;
}
void CAMCListView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
if (lHint == VIEW_UPDATE_DELETE_EMPTY_VIEW)
m_pAMCView->OnDeleteEmptyView();
}
int CAMCListView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
// see CAMCTreeView::OnMouseActivate for an explanation of focus churn
// avoidance.
return (MA_ACTIVATE);
}
void CAMCListView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
DECLARE_SC(sc, TEXT("CAMCListView::OnActivateView"));
#ifdef DBG
Trace(tagList, _T("ListView::OnActivateView (%s, pAct=0x%08x, pDeact=0x%08x))\n"),
(bActivate) ? _T("true") : _T("false"), pActivateView, pDeactiveView);
#endif
if ( (pActivateView != pDeactiveView) &&
(bActivate) )
{
sc = ScFireEvent(CListViewActivationObserver::ScOnListViewActivated);
if (sc)
sc.TraceAndClear();
}
CListView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}
void CAMCListView::OnSetFocus(CWnd* pOldWnd)
{
/*
* if this view has the focus, it should be the active view
*/
CFrameWnd *pParentFrame = GetParentFrame();
if(pParentFrame != NULL)
pParentFrame->SetActiveView (this);
CListView::OnSetFocus(pOldWnd);
// If we are currently reparented, then we need to send a setfocus notify
// to our current parent. This is needed because the listview control caches its
// parent window on creation and continues to sends all notifications to it.
if (dynamic_cast<CAMCView*>(GetParent()) == NULL)
{
NMHDR nmhdr;
nmhdr.hwndFrom = m_hWnd;
nmhdr.idFrom = GetDlgCtrlID();
nmhdr.code = NM_SETFOCUS;
::SendMessage(GetParent()->m_hWnd, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
}
}
/*+-------------------------------------------------------------------------*
*
* CAMCListView::OnKeyboardFocus
*
* PURPOSE: Whenever the user switches focus using the TAB keys ONLY, to the
* list view control, make sure that at least one item is highlighted.
*
* RETURNS:
* void
*
*+-------------------------------------------------------------------------*/
void
CAMCListView::OnKeyboardFocus(UINT nState, UINT nStateMask)
{
CListCtrl &lc = GetListCtrl();
// Make sure an item has the focus (unless the list is empty)
if (lc.GetNextItem(-1,LVNI_FOCUSED) == -1 && lc.GetItemCount() > 0)
{
/*
* It would be convenient to use
*
* CListCtrl::SetItemState (int nIndex, UINT nState, UINT nMask)
*
* here, but MFC uses LVM_SETITEM for that overload, which
* doesn't work for virtual lists. For the overload we use
* here, MFC uses LVM_SETITEMSTATE, which works fine for
* virtual lists.
*/
LV_ITEM lvi;
lvi.stateMask = nStateMask;
lvi.state = nState;
lc.SetItemState (0, &lvi);
}
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::OnBeginTrack
//
// Synopsis: HDN_BEGINTRACK handler, due to our improper message routing
// (handling all messages to CAMCView) this message gets lost
// (CAMCListView::OnCmdMsg passes it to underlying view which
// handles it & so we handle it here separately.
//
// Arguments: [pNotifyStruct] -
// [result] -
//
//--------------------------------------------------------------------
void CAMCListView::OnBeginTrack(NMHDR * pNotifyStruct, LRESULT * result)
{
if (!pNotifyStruct || !result)
return;
*result = FALSE;
NMHEADER* nmh = (NMHEADER*)pNotifyStruct;
SC sc = ScOnColumnsAttributeChanged(nmh, HDN_BEGINTRACK);
if (sc)
{
sc.TraceAndClear();
return;
}
// S_FALSE : dont allow the change
if (sc == SC(S_FALSE))
*result = TRUE;
return;
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::IsColumnHidden
//
// Synopsis: Get the LPARAM and check if given column is hidden.
//
// Arguments: [iCol] -
//
// Returns: bool
//
//--------------------------------------------------------------------
bool CAMCListView::IsColumnHidden(int iCol) const
{
CAMCHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
if (pHeaderCtrl)
return pHeaderCtrl->IsColumnHidden(iCol);
return false;
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScGetColumnInfoList
//
// Synopsis: Get the CColumnInfoList from current list-view.
//
// Arguments: [pColumnsList] - [out param], ptr to CColumnsInfoList*
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCListView::ScGetColumnInfoList (CColumnInfoList *pColumnsList)
{
DECLARE_SC(sc, _T("CAMCListView::ScGetColumnInfoList"));
sc = ScCheckPointers(pColumnsList);
if (sc)
return sc;
pColumnsList->clear();
CAMCHeaderCtrl *pHeader = GetHeaderCtrl();
sc = ScCheckPointers(pHeader, E_UNEXPECTED);
if (sc)
return sc;
int cColumns = pHeader->GetItemCount();
typedef std::auto_ptr<int> IntArray;
IntArray spColOrder = IntArray(new int[cColumns]);
int *pColOrder = spColOrder.get(); // Use a non-smart ptr for ease of use.
sc = ScCheckPointers(pColOrder, E_OUTOFMEMORY);
if (sc)
return sc;
sc = pHeader->GetOrderArray(pColOrder, cColumns) ? S_OK : E_FAIL;
if (sc)
return sc;
for (int i = 0; i < cColumns; i++)
{
// Get the data from header control.
HDITEM hdItem;
ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_WIDTH | HDI_LPARAM;
sc = pHeader->GetItem(pColOrder[i], &hdItem) ? S_OK : E_FAIL;
if (sc)
return sc;
// Save the visual index of the ith col
CColumnInfo colInfo;
colInfo.SetColIndex(pColOrder[i]);
// Save the width
CHiddenColumnInfo hci (hdItem.lParam);
if (hci.fHidden)
{
colInfo.SetColHidden();
colInfo.SetColWidth(hci.cx);
}
else
colInfo.SetColWidth(hdItem.cxy);
pColumnsList->push_back(colInfo);
}
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScModifyColumns
//
// Synopsis: Modify the header-control columns with given CColumnsInfoList.
//
// Arguments: [colInfoList] -
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCListView::ScModifyColumns (const CColumnInfoList& colInfoList)
{
DECLARE_SC(sc, _T("CAMCListView::ScModifyColumns"));
CAMCHeaderCtrl *pHeader = GetHeaderCtrl();
CAMCView *pAMCView = GetAMCView();
sc = ScCheckPointers(pHeader, pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// This method is called due to following conditions:
// 1. Just before inserting first item into list-view.
// 2. If there are no items in list-view then it is empty LV,
// in that case this method is called during OnPaint.
// 3. The IHeaderCtrlPrivate on CNodeInitObject can also call this method.
// Once a node is selected & result-pane is setup, case1 or case2 above
// should happen only once. To avoid multiple calls we use below flag which
// says that we have attempted to restore the columns from given data.
// case1 & case2 use this to determine whether to call this method.
SetColumnsNeedToBeRestored();
// Check for column consistency. If persisted # of cols & actual # of cols
// inserted are different then ask column-data to be deleted.
int cColumns = pHeader->GetItemCount();
if (colInfoList.size() != cColumns)
{
sc = pAMCView->ScDeletePersistedColumnData();
return sc;
}
typedef std::auto_ptr<int> IntArray;
IntArray spColOrder = IntArray(new int[cColumns]);
int *pColOrder = spColOrder.get(); // Use a non-smart ptr for ease of use.
sc = ScCheckPointers(pColOrder, E_OUTOFMEMORY);
if (sc)
return sc;
// Now restore the headers.
{
m_bColumnsBeingRestored = true; // should set this false before leaving this funcion.
CColumnInfoList::iterator itColInfo;
int i = 0;
// Get width/order of each column.
for (itColInfo = colInfoList.begin(), i = 0;
itColInfo != colInfoList.end();
++itColInfo, i++)
{
pColOrder[i] = itColInfo->GetColIndex();
// First set/reset the lparam
HDITEM hdItem;
ZeroMemory(&hdItem, sizeof(hdItem));
if (itColInfo->IsColHidden())
{
// We set the width first and then LPARAM because
// If we set lparam first then when we set width
// CAMCView::Notify HDN_ITEMCHANGING. Now we
// examine the lparam of the item to see if it is hidden.
// So setting lparam first and then setting width
// for hiding columns will not work.
hdItem.mask = HDI_WIDTH;
hdItem.cxy = 0;
sc = pHeader->SetItem(pColOrder[i], &hdItem) ? S_OK : E_FAIL;
if (sc)
goto Error;
CHiddenColumnInfo hci (itColInfo->GetColWidth(), true);
hdItem.mask = HDI_LPARAM;
hdItem.lParam = hci.lParam;
sc = pHeader->SetItem(pColOrder[i], &hdItem) ? S_OK : E_FAIL;
if (sc)
goto Error;
}
else
{
CHiddenColumnInfo hci (itColInfo->GetColWidth(), false);
// Here we need to clear the hidden flag in lParam
// before changing width So that hidden columns will be made visible.
hdItem.mask = HDI_LPARAM;
hdItem.lParam = hci.lParam;
sc = pHeader->SetItem(pColOrder[i], &hdItem) ? S_OK : E_FAIL;
if (sc)
goto Error;
if ( AUTO_WIDTH == itColInfo->GetColWidth())
{
// If the column is hidden and made visible we do not know its width.
// With ListView_SetColumnWidth passing AUTO_WIDTH for width calculates
// width automatically. Header_SetItem cannot do this.
sc = ListView_SetColumnWidth(GetSafeHwnd(),
pColOrder[i],
LVSCW_AUTOSIZE_USEHEADER) ? S_OK : E_FAIL;
if (sc)
goto Error;
}
else
{
hdItem.mask = HDI_WIDTH;
hdItem.cxy = itColInfo->GetColWidth();
sc = pHeader->SetItem(pColOrder[i], &hdItem) ? S_OK : E_FAIL;
if (sc)
goto Error;
}
}
}
// Set the order
sc = pHeader->SetOrderArray(cColumns, pColOrder) ? S_OK : E_FAIL;
if (sc)
goto Error;
// Now redraw the list view
InvalidateRect(NULL, TRUE);
}
Cleanup:
m_bColumnsBeingRestored = false;
return (sc);
Error:
goto Cleanup;
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScGetDefaultColumnInfoList
//
// Synopsis: Get the default column settings.
//
// Arguments: [columnInfoList] - [out]
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCListView::ScGetDefaultColumnInfoList (CColumnInfoList& columnInfoList)
{
DECLARE_SC(sc, _T("CAMCListView::ScRestoreDefaultColumnSettings"));
if (m_defaultColumnInfoList.size() <= 0)
return (sc = E_UNEXPECTED);
columnInfoList = m_defaultColumnInfoList;
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScSaveColumnInfoList
//
// Synopsis:
//
// Arguments:
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCListView::ScSaveColumnInfoList ()
{
DECLARE_SC(sc, _T("CAMCListView::ScSaveColumnInfoList"));
CAMCHeaderCtrl *pHeader = GetHeaderCtrl();
CAMCView *pAMCView = GetAMCView();
sc = ScCheckPointers(pHeader, pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// Get the column data & give it to CAMCView so that it can
// inform NodeMgr (thro NodeCallback) about new data.
CColumnInfoList colInfoList;
sc = ScGetColumnInfoList (&colInfoList);
if (sc)
return sc;
sc = pAMCView->ScColumnInfoListChanged(colInfoList);
if (sc)
return sc;
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScOnColumnsAttributeChanged
//
// Synopsis: Column width/order has changed so get the column data
// and ask the nodemgr to persist it.
//
// Arguments: NMHEADER* - the header change information.
// code - the HDN_* notification.
//
// Returns: SC, S_OK - allow the change.
// S_FALSE - dont allow the change.
//
//--------------------------------------------------------------------
SC CAMCListView::ScOnColumnsAttributeChanged (NMHEADER *pNMHeader, UINT code)
{
DECLARE_SC(sc, _T("CAMCListView::ScOnColumnsAttributeChanged"));
Trace (tagColumn, _T("CAMCListView::ScOnColumnsAttributeChanged"));
// If we are applying persisted column data to the header control
// so allow those changes.
if (m_bColumnsBeingRestored)
return sc;
sc = ScCheckPointers(pNMHeader, pNMHeader->pitem);
if (sc)
return sc;
// User is trying to drag a column make sure it is not a hidden column.
if ( (code == HDN_BEGINTRACK) && (pNMHeader->pitem->mask & HDI_WIDTH) )
{
sc = IsColumnHidden(pNMHeader->iItem) ? S_FALSE : S_OK;
return sc;
}
/*
* At this point the code is HDN_ENDTRACK (width change completed) or
* during HDN_ENDDRAG (order changing but not completed).
* During both these messages header-control has not updated internal
* data, so we post a message & save on message handler.
*/
if ((code == HDN_ENDDRAG) || (code == HDN_ENDTRACK))
{
PostMessage(m_nColumnPersistedDataChangedMsg);
return sc;
}
// Too risky to return error instead at this point, enable this for Blackcomb Beta1.
// sc = E_FAIL;
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::OnColumnPersistedDataChanged
//
// Synopsis: CAMCListView::m_nColumnDataChangedMsg registered message handler.
// Column width/order has changed so get the column data
// and ask the nodemgr to persist it.
//
// Returns: LRESULT
//
//--------------------------------------------------------------------
LRESULT CAMCListView::OnColumnPersistedDataChanged (WPARAM, LPARAM)
{
DECLARE_SC(sc, _T("CAMCListView::OnColumnPersistedDataChanged"));
Trace (tagColumn, _T("CAMCListView::OnColumnPersistedDataChanged"));
if (m_bColumnsBeingRestored)
return 0;
sc = ScSaveColumnInfoList();
if (sc)
return 0;
return (0);
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScRestoreColumnsFromPersistedData
//
// Synopsis: Get the persisted data for current list-view headers
// and apply them.
//
// Arguments:
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCListView::ScRestoreColumnsFromPersistedData ()
{
DECLARE_SC(sc, _T("CAMCListView::ScRestoreColumnsFromPersistedData"));
Trace (tagColumn, _T("CAMCListView::ScRestoreColumnsFromPersistedData"));
if (! AreColumnsNeedToBeRestored())
return sc;
/*
* When a node is selected the snapin initially inserts columns with
* some initial settings which is the default settings. At this point
* the list view has the default settings, save it before applying
* the persisted data.
*/
sc = ScGetColumnInfoList(&m_defaultColumnInfoList);
if (sc)
return sc;
CAMCHeaderCtrl *pHeader = GetHeaderCtrl();
CAMCView *pAMCView = GetAMCView();
sc = ScCheckPointers(pHeader, pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// Get the column data.
CColumnInfoList colInfoList;
sc = pAMCView->ScGetPersistedColumnInfoList(&colInfoList);
// Whether there is data or not we tried to restore columns.
SetColumnsNeedToBeRestored();
if (sc.IsError() || (sc == SC(S_FALSE)) )
return sc;
// Modify headers.
sc = ScModifyColumns(colInfoList);
if (sc)
return sc;
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: CAMCListView::ScResetColumnStatusData
//
// Synopsis: Reset the data used to keep track of hidden column state,
// columns-restored state.
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CAMCListView::ScResetColumnStatusData ()
{
DECLARE_SC(sc, _T("CAMCListView::ScResetColumnStatusData"));
SetColumnsNeedToBeRestored(true);
m_defaultColumnInfoList.clear();
return (sc);
}
BOOL CAMCListView::ChangePane(AMCNavDir eDir)
{
/*
* NOTE: We need to get the header control before we get the focus window.
*
* The first time GetHeaderCtrl is called, it will subclass the non-MFC
* header window with an MFC class. Doing this will put an entry for the
* header in the permanent window map. Before GetHeaderCtrl is called,
* MFC will have never seen the header before, so GetFocus will put an
* entry in the temporary map. Temporary CWnd pointers will never match
* permanent CWnd pointers, even though they wrap the same HWND, so we
* need to make sure the header is in the permanent map before we do
* any comparisons.
*/
CWnd* pwndHeader = GetHeaderCtrl();
CWnd* pwndFocus = GetFocus();
bool fFocusOnList = (pwndFocus == this);
bool fFocusOnHeader = (pwndFocus == pwndHeader);
/*
* It can't be that both the list and the focus have the focus,
* although it is possible that neither has the focus.
*/
ASSERT (!(fFocusOnList && fFocusOnHeader));
/*
* If either the list or the header has the focus, then this had
* better be the active view; if not, it had better not.
*/
if(!fFocusOnList && !fFocusOnHeader)
return FALSE;
/*
* Set the focus to the header if:
*
* 1. the focus is currently on the list, and
* 2. we're moving forward (Tab), and
* 3. we're in filter mode
*/
if (fFocusOnList && (eDir == AMCNAV_NEXT) && IsInFilteredReportMode())
{
GetHeaderCtrl()->SetFocus();
return TRUE;
}
/*
* Otherwise, set the focus to the list if:
*
* 1. the focus is not currently on the list, and
* 2. we're moving backward (Shift+Tab)
*/
// if focus not on list and we're moving backward
else if (!fFocusOnList && (eDir == AMCNAV_PREV))
{
ActivateSelf();
return TRUE;
}
/*
* didn't change the focus
*/
return FALSE;
}
BOOL CAMCListView::TakeFocus(AMCNavDir eDir)
{
if ((eDir == AMCNAV_PREV) && IsInFilteredReportMode())
GetHeaderCtrl()->SetFocus();
else
ActivateSelf();
ASSERT (GetParentFrame()->GetActiveView() == this);
return TRUE;
}
/*+-------------------------------------------------------------------------*
* CAMCListView::ActivateSelf
*
* If this isn't currently the active view, then this function makes it the
* active view; the focus will be set to the list implicitly.
*
* If it's already the active view, calling SetActiveView won't set the
* focus, because it shorts out if the active view isn't changing. In
* that case, we have to set the focus ourselves.
*
* This function returns true if the list view was made the active view,
* false if it was already the active view.
*--------------------------------------------------------------------------*/
bool CAMCListView::ActivateSelf (bool fNotify /* =true */)
{
CFrameWnd* pwndFrame = GetParentFrame();
ASSERT (pwndFrame != NULL);
bool fChangeActiveView = (pwndFrame->GetActiveView() != this);
if (fChangeActiveView)
pwndFrame->SetActiveView (this, fNotify);
else
SetFocus();
return (fChangeActiveView);
}
CAMCHeaderCtrl* CAMCListView::GetHeaderCtrl() const
{
// Is there a header ?
if (m_header.m_hWnd)
return (&m_header);
// if not, try getting it now
HWND hwndHdr = reinterpret_cast<HWND>(::SendMessage (m_hWnd, LVM_GETHEADER, 0, 0));
if (hwndHdr != NULL)
{
m_header.SubclassWindow(hwndHdr);
return (&m_header);
}
return (NULL);
}
void CAMCListView::SelectDropTarget(int iDropTarget)
{
if (m_iDropTarget == iDropTarget)
return;
CListCtrl& lc = GetListCtrl();
if (m_iDropTarget != -1)
{
// remove hiliting from all items
// do not use m_iDropTarget - item order and count may be changed already
int iIndex = -1;
while ( 0 <= ( iIndex = ListView_GetNextItem(lc, iIndex, LVIS_DROPHILITED) ) )
ListView_SetItemState(lc, iIndex, 0, LVIS_DROPHILITED);
}
if (iDropTarget != -1)
ListView_SetItemState(lc, iDropTarget, LVIS_DROPHILITED, LVIS_DROPHILITED);
m_iDropTarget = iDropTarget;
}
/////////////////////////////////////////////////////////////////////////////
// CCCListViewCtrl
DEBUG_DECLARE_INSTANCE_COUNTER(CCCListViewCtrl);
CCCListViewCtrl::CCCListViewCtrl() :
m_itemCount(0),
m_nScopeItems(0),
m_colCount(0),
m_headerIL (AfxGetResourceHandle(), IDB_SORT),
m_FontLinker (this)
{
DEBUG_INCREMENT_INSTANCE_COUNTER(CCCListViewCtrl);
// Sort Stuff
m_sortParams.bAscending = TRUE;
m_sortParams.nCol = 0;
m_sortParams.lpListView = this;
m_sortParams.spResultCompare = NULL;
m_sortParams.spResultCompareEx = NULL;
m_sortParams.lpUserParam = NULL;
m_sortParams.bLexicalSort = FALSE;
m_sortParams.hSelectedNode = NULL;
// Start as standard list
m_bVirtual = FALSE;
m_bFiltered = FALSE;
m_pStandardList = new CAMCListView;
m_pVirtualList = NULL;
m_bEnsureFocusVisible = FALSE;
m_bLoading = FALSE;
m_bDeferredSort = FALSE;
m_SavedHWND = NULL;
ZeroMemory (&m_wp, sizeof(WINDOWPLACEMENT));
m_pListView = m_pStandardList;
}
CCCListViewCtrl::~CCCListViewCtrl()
{
DEBUG_DECREMENT_INSTANCE_COUNTER(CCCListViewCtrl);
if (m_SavedHWND != NULL) {
// change back
::SetParent (m_pListView->m_hWnd, m_SavedHWND);
if (m_wp.length != 0)
::SetWindowPlacement (m_pListView->m_hWnd, &m_wp);
// clear saved window
m_SavedHWND = NULL;
}
}
/*+-------------------------------------------------------------------------*
*
* CCCListViewCtrl::ScInitialize
*
* PURPOSE:
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CCCListViewCtrl::ScInitialize()
{
DECLARE_SC(sc, _T("CCCListViewCtrl::ScInitialize"));
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers(pAMCView, E_FAIL);
if (sc)
return sc;
AddObserver(static_cast<CListViewObserver&>(*pAMCView));
return sc;
}
//---------------------------------------------------- Utility functions
void CCCListViewCtrl::CutSelectedItems(BOOL bCut)
{
CListCtrl& lc = GetListCtrl();
int nSearchFlags = (bCut) ? LVNI_SELECTED : LVNI_CUT;
int nNewState = (bCut) ? LVIS_CUT : 0;
int nItem = -1;
while ((nItem = lc.GetNextItem (nItem, nSearchFlags)) >= 0)
{
lc.SetItemState (nItem, nNewState, LVIS_CUT);
}
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::IndexToResultItem
*
* Returns the CResultItem pointer for a given index.
*--------------------------------------------------------------------------*/
CResultItem* CCCListViewCtrl::IndexToResultItem (int nItem)
{
HRESULTITEM hri = GetListCtrl().GetItemData (nItem);
if (IS_SPECIAL_LVDATA (hri))
return (NULL);
return (CResultItem::FromHandle (hri));
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::ResultItemToIndex
*
* Returns the index of an item given its CResultItem pointer. This does a
* linear search. If the speed of this function needs to be improved,
* we'll need a separate CResultItem-to-index map.
*--------------------------------------------------------------------------*/
int CCCListViewCtrl::ResultItemToIndex (CResultItem* pri) const
{
/*
* if this is a virtual list, the CResultItem "pointer" is actually
* the item index, so convert it. Note that CResultItem::ToHandle is
* safe to call with a NULL pointer.
*/
if (IsVirtual())
return (CResultItem::ToHandle(pri));
/*
* No items have NULL CResultItem pointers, don't bother looking.
*/
if (pri == NULL)
return (-1);
/*
* Let the list find the matching item.
*/
LV_FINDINFO lvfi;
lvfi.flags = LVFI_PARAM;
lvfi.lParam = CResultItem::ToHandle(pri);
return (GetListCtrl().FindItem (&lvfi, -1));
}
/////////////////////////////////////////////////////////////////////////////
// CCCListViewCtrl message handlers
HRESULT CCCListViewCtrl::InsertItem(
LPOLESTR str,
long iconNdx,
LPARAM lParam,
long state,
COMPONENTID ownerID,
long itemIndex,
CResultItem*& priInserted)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::InsertItem"));
/*
* init the output parameter
*/
priInserted = NULL;
if (IsVirtual())
return (sc = E_UNEXPECTED).ToHr();
if (str != MMC_TEXTCALLBACK)
return (sc = E_INVALIDARG).ToHr();
// Ask the CAMCListViewCtrl to setup headers.
sc = ScCheckPointers(m_pListView, E_UNEXPECTED);
if (! sc.IsError())
sc = m_pListView->ScRestoreColumnsFromPersistedData();
if (sc)
sc.TraceAndClear();
USES_CONVERSION;
LV_ITEM lvi;
lvi.iSubItem = 0;
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
lvi.pszText = LPSTR_TEXTCALLBACK;
// If the user has specified an icon index, map it and put it in the LV_ITEM struct
int nMapping = 0;
if ((iconNdx != MMCLV_NOICON) &&
m_resultIM.Lookup(&CImageIndexMapKey(ownerID,iconNdx), nMapping))
{
lvi.iImage = nMapping;
}
else
{
lvi.iImage = MMCLV_NOICON;
iconNdx = MMCLV_NOICON;
}
/*
* allocate and initialize a CResultItem for this item
*/
sc = ScAllocResultItem (priInserted, ownerID, lParam, iconNdx);
if (sc)
return (sc.ToHr());
sc = ScCheckPointers (priInserted, E_UNEXPECTED);
if (sc)
return (sc.ToHr());
// If the user has specified a state, put it in the LV_ITEM struct
if (state != MMCLV_NOPARAM)
{
lvi.mask |= LVIF_STATE;
lvi.state = state;
lvi.stateMask = 0xFFFFFFFF;
}
// if scope item
if (priInserted->IsScopeItem())
{
// if no index provided add to end of unsorted items
lvi.iItem = (itemIndex == -1) ? m_nScopeItems : itemIndex;
// if decending sort, offset from end instead of start
if (!m_sortParams.bAscending)
lvi.iItem += (m_itemCount - m_nScopeItems);
}
else
{
// Add sorted items to end of list (or before unsorted items, if reverse sorting)
lvi.iItem = m_sortParams.bAscending ? m_itemCount : m_itemCount - m_nScopeItems;
}
lvi.lParam = CResultItem::ToHandle(priInserted);
int nIndex = GetListCtrl().InsertItem (&lvi);
#if (defined(DBG) && defined(DEBUG_LIST_INSERTIONS))
static int cInserted = 0;
TRACE3 ("%4d:Inserted item: index=%d, lParam=0x%08x\n", ++cInserted, nIndex, lvi.lParam);
#endif
if (nIndex == -1 )
{
sc = E_FAIL;
ScFreeResultItem (priInserted); // ignore failures
priInserted = NULL;
}
else
{
// The insert succeeded, increase the internal item counts
m_itemCount++;
// we invalidate the rectangle when transitioning from zero to one item because otherwise the
// empty list message is not erased completely.
if(m_itemCount == 1)
GetListCtrl().InvalidateRect(NULL);
if (priInserted->IsScopeItem())
m_nScopeItems++;
// if ensure focus visible style and focus set, force item into view
if (m_bEnsureFocusVisible && state != MMCLV_NOPARAM && (state & LVIS_FOCUSED))
GetListCtrl().EnsureVisible(nIndex, FALSE);
}
if (sc)
return sc.ToHr();
// we have inserted an Item! - broadcast the good message to observers
sc = ScFireEvent(CListViewObserver::ScOnListViewItemInserted, nIndex);
if (sc)
return sc.ToHr();
return sc.ToHr();
}
HRESULT CCCListViewCtrl::DeleteItem(HRESULTITEM itemID, long nCol)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::DeleteItem"));
if (nCol != 0)
return E_INVALIDARG;
CListCtrl& lc = GetListCtrl();
int nItem = IsVirtual() ? static_cast<int>(itemID)
: ResultItemToIndex( CResultItem::FromHandle(itemID) );
#if (defined(DBG) && defined(DEBUG_LIST_INSERTIONS))
static int cDeletes = 0;
TRACE3 ("%4d:Deleted item: index=%d, lParam=0x%08x", ++cDeletes, nItem, priDelete);
#endif
if (nItem < 0 || nItem >= m_itemCount)
{
ASSERT(FALSE);
#if (defined(DEBUG_LIST_INSERTIONS))
TRACE0 (" (failed)\n");
#endif
return E_INVALIDARG;
}
#if (defined(DEBUG_LIST_INSERTIONS))
TRACE0 ("\n");
#endif
if (!lc.DeleteItem (nItem))
{
sc = E_FAIL;
}
else
{
// Delete was successful, decrement the ItemCount
ASSERT(m_itemCount > 0);
m_itemCount--;
if (!IsVirtual())
{
CResultItem *priDelete = CResultItem::FromHandle(itemID);
sc = ScCheckPointers (priDelete, E_UNEXPECTED);
if (sc)
return (sc.ToHr());
if (priDelete->IsScopeItem())
m_nScopeItems--;
sc = ScFreeResultItem (priDelete);
if (sc)
return (sc.ToHr());
}
}
if (sc)
return sc.ToHr();
// select the focused item ( this will save a lot of snapins from the confusion
// since they are not prepared to handle 'no items selected' scenario.
// Note: we only guard single selection lists - for multiple selection the situation
// must be handled by snapin, since user can easily unselect the item.
if ( (::GetFocus() == lc.m_hWnd) && (lc.GetStyle() & LVS_SINGLESEL) )
{
// check if focused item is selected
int iMarkedItem = lc.GetSelectionMark();
if ( (iMarkedItem >= 0) && !( lc.GetItemState( iMarkedItem, LVIS_SELECTED ) & LVIS_SELECTED ) )
{
// NOTE: do not use lc.SetItemState - it uses SetItem which is not supported for virtual lists
LV_ITEM lvi;
lvi.stateMask = lvi.state = LVIS_SELECTED;
if (!lc.SendMessage( LVM_SETITEMSTATE, WPARAM(iMarkedItem), (LPARAM)(LV_ITEM FAR *)&lvi))
(sc = E_FAIL).TraceAndClear(); // trace is enough - ignore and continue
}
}
// we have deleted an Item! - broadcast the message to observers
sc = ScFireEvent(CListViewObserver::ScOnListViewItemDeleted, nItem);
if (sc)
return sc.ToHr();
return sc.ToHr();
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::UpdateItem
//
// Synopsis: Update the given item.
//
// Arguments: [itemID] -
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
HRESULT CCCListViewCtrl::UpdateItem(HRESULTITEM itemID)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::UpdateItem"));
int nIndex = -1;
sc = ScGetItemIndexFromHRESULTITEM(itemID, nIndex);
if (sc)
return sc.ToHr();
if(nIndex < 0 || nIndex >= m_itemCount)
return (sc = E_INVALIDARG).ToHr();
CListCtrl& lc = GetListCtrl();
/*
* Since Common Control does not hold any data about virtual list view
* items they would not know what to invalidate. So we need to invalidate
* for virtual list views.
*/
if (IsVirtual())
{
RECT rc;
lc.GetItemRect(nIndex, &rc, LVIR_BOUNDS);
lc.InvalidateRect(&rc);
}
else
{
sc = ScRedrawItem(nIndex);
if (sc)
return (sc.ToHr());
}
lc.UpdateWindow();
// we have updated an item - broadcast the message to observers
sc = ScFireEvent(CListViewObserver::ScOnListViewItemUpdated, nIndex);
if (sc)
return sc.ToHr();
return sc.ToHr();
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::ScGetItemIndexFromHRESULTITEM
//
// Synopsis: Given HRESULTITEM get the index of that item.
// For virtual listview the itemid is the index.
//
// Arguments: [itemID] - [in param]
// [nIndex] - [out param]
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CCCListViewCtrl::ScGetItemIndexFromHRESULTITEM (const HRESULTITEM& itemID, int& nIndex)
{
DECLARE_SC(sc, _T("CCCListViewCtrl::ScGetItemIndexFromHRESULTITEM"));
nIndex = -1;
if (IsVirtual())
{
nIndex = itemID;
return sc;
}
CResultItem *pri = CResultItem::FromHandle(itemID);
sc = ScCheckPointers(pri, E_UNEXPECTED);
if (sc)
return sc;
nIndex = ResultItemToIndex(pri);
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::ScRedrawItem
//
// Synopsis: Redraw the given item in listview.
//
// Arguments: [nIndex] -
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CCCListViewCtrl::ScRedrawItem(int nIndex)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::RedrawItem"));
if(nIndex < 0 || nIndex >= m_itemCount)
return (sc = E_INVALIDARG);
if (!GetListCtrl().RedrawItems (nIndex, nIndex))
return (sc = E_FAIL);
return (sc);
}
//+-------------------------------------------------------------------
//
// Member: Sort
//
// Synopsis: Sort the list view with given data.
//
// Arguments: [lpUserParam] - Snapin supplied user param.
// [lParms] - ptr to CCLVSortParams struct.
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
HRESULT CCCListViewCtrl::Sort(LPARAM lUserParam, long* lParms)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Sort"));
if (IsVirtual())
{
sc = E_UNEXPECTED;
return sc.ToHr();
}
BOOL bResult = FALSE;
CCLVSortParams* lpParams = reinterpret_cast<CCLVSortParams*>(lParms);
ASSERT(lpParams != NULL);
// Note: the hwnd should only be initialize in ::Create
m_sortParams.bAscending = lpParams->bAscending;
m_sortParams.nCol = lpParams->nCol;
m_sortParams.spResultCompare = lpParams->lpResultCompare;
m_sortParams.spResultCompareEx = lpParams->lpResultCompareEx;
m_sortParams.lpUserParam = lUserParam;
// Do not sort on hidden columns.
if (IsColumnHidden(m_sortParams.nCol))
return (sc.ToHr());
{
// Check view options for scope item sorting
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers(pAMCView, E_FAIL);
if (sc)
return (sc.ToHr());
SViewData* pViewData = pAMCView->GetViewData();
sc = ScCheckPointers(pViewData, E_FAIL);
if (sc)
return (sc.ToHr());
m_sortParams.bLexicalSort = ((pViewData->GetListOptions() & RVTI_LIST_OPTIONS_LEXICAL_SORT) != 0);
LPNODECALLBACK pNodeCallback = pAMCView->GetNodeCallback();
sc = ScCheckPointers(pNodeCallback, E_FAIL);
if (sc)
return (sc.ToHr());
// Do not need to refcount this, because it is being passed to a method that returns.
m_sortParams.lpNodeCallback = pNodeCallback;
// Get component ID of node that owns the result view
HNODE hnodeOwner = pAMCView->GetSelectedNode();
sc = ScCheckPointers((LPVOID)hnodeOwner, E_FAIL);
if (sc)
return (sc.ToHr());
m_sortParams.hSelectedNode = hnodeOwner;
sc = pNodeCallback->GetNodeOwnerID(hnodeOwner, &m_sortParams.OwnerID);
if (sc)
return (sc.ToHr());
if (m_bLoading)
{
bResult = TRUE;
m_bDeferredSort = TRUE;
}
else
{
/*
* the sort could take awhile, so show a wait cursor
*/
CWaitCursor wait;
// It is lexical sort if
// 1. LV option specifies lexical sort option or
// 2. Snapin does not implement IResultDataCompare
// or IResultDataCompareEx interfaces.
BOOL bLexicalSort = ( m_sortParams.bLexicalSort ||
( (NULL == m_sortParams.spResultCompare) &&
(NULL == m_sortParams.spResultCompareEx) ) );
if (bLexicalSort)
{
bResult = GetListCtrl().SortItems (DefaultCompare, (DWORD_PTR)&m_sortParams);
}
else
{
bResult = GetListCtrl().SortItems (SortCompareFunc, (DWORD_PTR)&m_sortParams);
}
}
sc = (bResult == TRUE) ? S_OK : E_FAIL;
if (sc)
return (sc.ToHr());
// we have sorted Items! - cannot keep track of them currently
sc = ScFireEvent(CListViewObserver::ScOnListViewIndexesReset);
if (sc)
return (sc.ToHr());
}
return sc.ToHr();
}
HRESULT CCCListViewCtrl::FindItemByLParam(COMPONENTID ownerID, LPARAM lParam, CResultItem*& priFound)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::FindItemByLParam"));
/*
* init output parameter
*/
priFound = NULL;
if (IsVirtual())
return (sc = E_UNEXPECTED).ToHr();
/*
* find a CResultItem that matches the given owner and lParam.
*/
for (int i = GetListCtrl().GetItemCount()-1; i >= 0; i--)
{
CResultItem* pri = IndexToResultItem (i);
if ((pri != NULL) &&
(pri->GetOwnerID() == ownerID) &&
(pri->GetSnapinData() == lParam))
{
priFound = pri;
break;
}
}
if (priFound == NULL)
return ((sc = E_FAIL).ToHr());
return sc.ToHr();
}
HRESULT CCCListViewCtrl::GetListStyle()
{
LONG result;
ASSERT(::IsWindow(GetListViewHWND()));
// return the Style masked by the List View Style mask.
result = ::GetWindowLong(GetListViewHWND(),GWL_STYLE) & 0xffff;
return result;
}
HRESULT CCCListViewCtrl::SetListStyle(long nNewValue)
{
ASSERT(::IsWindow(GetListViewHWND()));
// Protect style bits that shouldn't be changed
// Use SetViewMode to change the mode, so filtering is properly updated
const long PRESERVE_MASK = LVS_OWNERDATA | LVS_SHAREIMAGELISTS | 0xffff0000;
DWORD curStyle = ::GetWindowLong(GetListViewHWND(), GWL_STYLE);
DWORD newStyle = (curStyle & PRESERVE_MASK) | (nNewValue & ~PRESERVE_MASK);
// Verify not changing the view mode
ASSERT( ((curStyle ^ newStyle) & LVS_TYPEMASK) == 0);
// verify OWNERDATA style is what we think it is
ASSERT((curStyle & LVS_OWNERDATA) && m_bVirtual || !(curStyle & LVS_OWNERDATA) && !m_bVirtual);
// Save state of MMC defined "ensure focus visible" syle
m_bEnsureFocusVisible = (nNewValue & MMC_LVS_ENSUREFOCUSVISIBLE) != 0;
if (curStyle != newStyle)
{
// Apply style changes
::SetWindowLong(GetListViewHWND(), GWL_STYLE, newStyle);
/*
* The list control does not pass changes to the LVS_NOSORTHEADER flag on to the
* HeaderCtrl. This section directly accesses the underlying HeaderCtrl and
* changes the HDS_BUTTONS flag which is the equivalent.
*/
if ((nNewValue & LVS_NOSORTHEADER) ^ (curStyle & LVS_NOSORTHEADER) && GetHeaderCtrl())
{
if (nNewValue & LVS_NOSORTHEADER)
GetHeaderCtrl()->ModifyStyle (HDS_BUTTONS, 0); // Add the style
else
GetHeaderCtrl()->ModifyStyle (0, HDS_BUTTONS); // Remove the style
}
}
return S_OK;
}
HRESULT CCCListViewCtrl::GetViewMode()
{
ASSERT(::IsWindow(GetListViewHWND()));
long nViewMode;
if (m_bFiltered)
nViewMode = MMCLV_VIEWSTYLE_FILTERED;
else
nViewMode = ::GetWindowLong(GetListViewHWND(), GWL_STYLE) & LVS_TYPEMASK;
return nViewMode;
}
#include "histlist.h"
HRESULT CCCListViewCtrl::SetViewMode(long nViewMode)
{
ASSERT(nViewMode >= 0 && nViewMode <= MMCLV_VIEWSTYLE_FILTERED);
CListCtrl& lc = GetListCtrl();
if (nViewMode < 0 && nViewMode > MMCLV_VIEWSTYLE_FILTERED)
return E_INVALIDARG;
CAMCView* pAMCView = dynamic_cast<CAMCView*>(m_pParentWnd);
if (pAMCView)
pAMCView->GetHistoryList()->SetCurrentViewMode (nViewMode);
BOOL bFiltered = FALSE;
if (nViewMode == MMCLV_VIEWSTYLE_FILTERED)
{
bFiltered = TRUE;
nViewMode = LVS_REPORT;
}
lc.ModifyStyle (LVS_TYPEMASK, nViewMode);
HRESULT hr = S_OK;
// set filter style
CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
ASSERT(NULL != pHeaderCtrl);
if (bFiltered != m_bFiltered && pHeaderCtrl)
{
if (bFiltered)
pHeaderCtrl->ModifyStyle (0, HDS_FILTERBAR);
else
pHeaderCtrl->ModifyStyle (HDS_FILTERBAR, 0);
m_bFiltered = bFiltered;
// The header size has changed with the addition/removal of filter.
// We hide and show the header which will force the list
// control to recalculate the size, position of new header
// and list view and display it.
lc.ModifyStyle(0, LVS_NOCOLUMNHEADER, 0);
lc.ModifyStyle(LVS_NOCOLUMNHEADER, 0, 0);
}
return S_OK;
}
HRESULT CCCListViewCtrl::SetVirtualMode(BOOL bVirtual)
{
ASSERT(::IsWindow(GetListViewHWND()));
HRESULT hr = S_OK;
// force param to TRUE or FALSE
bVirtual = bVirtual ? TRUE : FALSE;
if (bVirtual != m_bVirtual)
{
do // false loop
{
// list must be empty to switch
if (m_itemCount != 0)
{
ASSERT(FALSE);
hr = E_FAIL;
break;
}
// get styles to copy to new control
long curStyle = ::GetWindowLong(GetListViewHWND(), GWL_STYLE) ^ LVS_OWNERDATA;
long curStyleEx = ::GetWindowLong(GetListViewHWND(), GWL_EXSTYLE);
long curHdrStyle = 0;
if (GetHeaderCtrl())
curHdrStyle = GetHeaderCtrl()->GetStyle();
if (bVirtual && !m_pVirtualList)
{
m_pVirtualList = new CAMCListView;
m_pVirtualList->SetVirtual();
}
CAMCListView* pNewList = bVirtual ? m_pVirtualList : m_pStandardList;
CAMCListView* pOldList = m_pListView;
// Make sure new control has been created
if (pNewList->m_hWnd == NULL)
{
/*
* MFC will issue a warning about creating a pane with
* no document. That's OK, since CAMCView::AttachListView-
* AsResultPane will patch thing up later.
*/
ASSERT (pOldList != NULL);
if (!Create(curStyle, g_rectEmpty, m_pParentWnd, pOldList->GetDlgCtrlID()))
{
ASSERT(FALSE);
hr = E_FAIL;
break;
}
}
// update member variables (this switches to the new control)
m_bVirtual = bVirtual;
m_pListView = bVirtual ? m_pVirtualList : m_pStandardList;
// Set current styles on new control
::SetWindowLong(GetListViewHWND(), GWL_STYLE, curStyle);
::SetWindowLong(GetListViewHWND(), GWL_EXSTYLE, curStyleEx);
// Note we have switched to the other control by now so this is getting the
// header of the new list
if (GetHeaderCtrl())
::SetWindowLong(GetHeaderCtrl()->m_hWnd, GWL_STYLE, curHdrStyle);
// hide the old list control and show the new one
::ShowWindow(pOldList->m_hWnd, SW_HIDE);
::ShowWindow(m_pListView->m_hWnd, SW_SHOWNA);
}
while (0);
}
return hr;
}
HRESULT CCCListViewCtrl::InsertColumn(int nCol, LPCOLESTR str, long nFormat, long width)
{
// Cannot change a column that is not in the list.
if(!str || !*str)
return E_INVALIDARG;
HRESULT hr = S_OK;
LV_COLUMN newCol;
void* pvoid = &newCol;
// Cannot insert a column with any items in the list.
if(m_itemCount)
{
hr = E_FAIL;
}
else
{
newCol.mask=0;
USES_CONVERSION;
// if the user specified a string, put it in the struct.
if(str!=MMCLV_NOPTR)
{
newCol.mask|=LVCF_TEXT;
newCol.pszText=OLE2T((LPOLESTR)str);
}
// if the user specified a format, put it in the struct.
if(nFormat!=MMCLV_NOPARAM)
{
newCol.mask|=LVCF_FMT;
newCol.fmt=nFormat;
}
// if the user specified a width, put it in the struct.
if(width!=MMCLV_NOPARAM)
{
newCol.mask|=LVCF_WIDTH;
// if the user requested auto-width, calculate the width.
// else just store the passed width.
if(width==MMCLV_AUTO)
{
// if the user did pass a string, calculate the width based off the string.
// else the width is 0.
if(str!=MMCLV_NOPTR)
{
CSize sz(0,0);
CClientDC dc( m_pListView );
dc.SelectObject( m_pListView->GetFont());
sz=dc.GetTextExtent(OLE2CT((LPOLESTR)str),_tcslen(OLE2T((LPOLESTR)
str)));
newCol.cx=sz.cx+CCLV_HEADERPAD;
}
else
{
newCol.cx=0;
}
}
else
{
newCol.cx=width;
}
}
int nRet = GetListCtrl().InsertColumn (nCol, &newCol);
if (-1 == nRet)
hr = E_FAIL;
else
{
// set lparam (HDI_HIDDEN flag) if the width is HIDE_COLUMN
if (HIDE_COLUMN == width)
{
CHiddenColumnInfo hci (0, true);
HDITEM hdItem;
::ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_LPARAM;
hdItem.lParam = hci.lParam;
// We do not care if this call fails
if (GetHeaderCtrl())
GetHeaderCtrl()->SetItem(nRet, &hdItem);
}
else
{
CHiddenColumnInfo hci (newCol.cx, false);
// set lparam with the width.
HDITEM hdItem;
::ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_LPARAM;
hdItem.lParam = hci.lParam;
// We do not care if this call fails
if (GetHeaderCtrl())
GetHeaderCtrl()->SetItem(nRet, &hdItem);
}
// insert was successful, increment the column count.
m_colCount++;
}
}
// we have inserted a column! - broadcast the message to observers
if (SUCCEEDED(hr))
{
SC sc = ScFireEvent(CListViewObserver::ScOnListViewColumnInserted, nCol);
if (sc)
return sc.ToHr();
}
return hr;
}
HRESULT CCCListViewCtrl::DeleteColumn(int nCol)
{
if (nCol < 0 || nCol >= m_colCount)
return E_INVALIDARG;
HRESULT hr = S_OK;
// Cannot delete a column if there are items in the list.
if(m_itemCount)
{
hr = E_FAIL;
}
else
{
if (!GetListCtrl().DeleteColumn (nCol))
hr = E_FAIL;
else
// Successful delete, decrement the column count.
m_colCount--;
}
// we have deleteded a column! - broadcast the message to observers
if (SUCCEEDED(hr))
{
SC sc = ScFireEvent(CListViewObserver::ScOnListViewColumnDeleted, nCol);
if (sc)
return sc.ToHr();
}
return hr;
}
HRESULT CCCListViewCtrl::GetColumnCount(int* pnColCnt)
{
*pnColCnt = m_colCount;
return S_OK;
}
HRESULT CCCListViewCtrl::DeleteAllItems(COMPONENTID ownerID)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::DeleteAllItems"));
CListCtrl& lc = GetListCtrl();
const bool bHasItemsToDelete = (m_itemCount > 0);
// Nothing in the list -> nothing to do.
if (bHasItemsToDelete)
{
if (IsVirtual())
{
if (lc.DeleteAllItems ())
m_itemCount = 0;
else
sc = E_FAIL;
}
else if (ownerID == TVOWNED_MAGICWORD)
{
/*
* free all of the CResultItem objects
*/
for (int i = m_itemCount - 1; i >= 0; i--)
{
CResultItem* pri = IndexToResultItem (i);
if (pri != NULL)
{
sc = ScFreeResultItem(pri);
if (sc)
return (sc.ToHr());
}
}
if (lc.DeleteAllItems ())
{
// Delete all succeded, ItemCount is now 0;
m_itemCount = 0;
m_nScopeItems = 0;
}
else
sc = E_FAIL;
}
else
{
for(int i = m_itemCount - 1; i >= 0; i--)
{
CResultItem* pri = IndexToResultItem (i);
if ((pri != NULL) && (pri->GetOwnerID() == ownerID))
{
if (lc.DeleteItem (i))
{
m_itemCount--;
sc = ScFreeResultItem(pri);
if (sc)
return (sc.ToHr());
}
else
sc = E_FAIL;
}
}
}
}
if (sc)
return sc.ToHr();
if (bHasItemsToDelete)
{
// we have deleted all Items! - broadcast the message to observers
sc = ScFireEvent(CListViewObserver::ScOnListViewIndexesReset);
if (sc)
return sc.ToHr();
}
return sc.ToHr();
}
HRESULT CCCListViewCtrl::SetColumn(long nCol, LPCOLESTR str, long nFormat, long width)
{
// Cannot change a column that is not in the list.
if((nCol + 1) > m_colCount)
return E_INVALIDARG;
HRESULT hr = S_OK;
LV_COLUMN newCol;
newCol.mask=0;
USES_CONVERSION;
// if the user specified a string, put it in the struct.
if(str!=MMCLV_NOPTR)
{
newCol.mask|=LVCF_TEXT;
newCol.pszText=OLE2T((LPOLESTR)str);
}
// if the user specified a format, put it in the struct.
if(nFormat!=MMCLV_NOPARAM)
{
newCol.mask|=LVCF_FMT;
newCol.fmt=nFormat;
}
// if the user specified a width, put it in the struct.
if(width!=MMCLV_NOPARAM)
{
newCol.mask|=LVCF_WIDTH;
// if the user requested auto-width, calculate the width.
// else just store the passed width.
if(width==MMCLV_AUTO)
{
// if the user did pass a string, calculate the width based off the string.
// else the width is 0.
if(str!=MMCLV_NOPTR)
{
CSize sz(0,0);
CClientDC dc( m_pListView );
dc.SelectObject( m_pListView->GetFont() );
sz=dc.GetTextExtent(OLE2T((LPOLESTR)str),_tcslen(OLE2T((LPOLESTR)str)));
newCol.cx=sz.cx+15;
}
else
{
newCol.cx=0;
}
}
else
{
newCol.cx=width;
}
// Get the lParam to see if this is a hidden column.
HDITEM hdItem;
::ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_LPARAM;
ASSERT(GetHeaderCtrl());
BOOL bRet = GetHeaderCtrl()->GetItem(nCol, &hdItem);
ASSERT(bRet);
CHiddenColumnInfo hciOld (hdItem.lParam);
CHiddenColumnInfo hci (0);
::ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_LPARAM;
// If the column is to be hidden then
// remember the (Old width) and (HIDDEN_FLAG).
if (HIDE_COLUMN == newCol.cx)
{
hci.cx = hciOld.cx;
hci.fHidden = true;
}
// If the column was hidden then
// remember the (New width supplied) and (HIDDEN_FLAG).
if (hciOld.fHidden)
{
hci.cx = newCol.cx;
hci.fHidden = true;
}
hdItem.lParam = hci.lParam;
// We do not care if this call fails
GetHeaderCtrl()->SetItem(nCol, &hdItem);
// Common control does not know anything about hidden
// columns, so if the column is hidden clear the
// width mask.
if (hci.fHidden)
{
newCol.mask = newCol.mask & (~LVCF_WIDTH);
}
}
if (!GetListCtrl().SetColumn (nCol, &newCol))
hr = E_FAIL;
return hr;
}
/*+-------------------------------------------------------------------------*
*
* CCCListViewCtrl::GetColumn
*
* PURPOSE: Returns information about the nCol'th column
*
* PARAMETERS:
* long nCol : the column index
* LPOLESTR* str : if non-NULL, points to column name on exit
* LPLONG nFormat : [out] the column format
* int * width: [out] the width of the column
*
* RETURNS:
* HRESULT
*
*+-------------------------------------------------------------------------*/
HRESULT
CCCListViewCtrl::GetColumn(long nCol, LPOLESTR* str, LPLONG nFormat, int FAR *width)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::GetColumn"));
#ifdef DBG
if((nCol+1)>m_colCount)
return E_INVALIDARG;
#endif
LV_COLUMN col;
UINT cBufferSize = 25; // grows as needed. The size here is actually half the initially allocated size
CAutoArrayPtr<TCHAR> buffer; // we use CAutoArrayPtr because the destructor calls delete[]
// Set up the mask to select the values we are interested in.
UINT mask = (nFormat!=MMCLV_NOPTR?LVCF_FMT:0)|(width!=MMCLV_NOPTR?LVCF_WIDTH:0);
do
{
// If the user requested a string, reflect this in the struct.
if(str!=NULL)
{
buffer.Delete(); // get rid of the old buffer, if any
cBufferSize *= 2; // twice the previous size.
buffer.Attach(new TCHAR[cBufferSize]);
if(buffer==NULL)
return(sc = E_OUTOFMEMORY).ToHr();
mask|=LVCF_TEXT;
col.cchTextMax=cBufferSize;
col.pszText=buffer;
}
col.mask = mask;
sc = GetListCtrl().GetColumn (nCol, &col) ? S_OK : E_FAIL;
if(sc)
return sc.ToHr();
} while(str!=NULL && (cBufferSize == _tcslen(buffer) + 1) ); //loop if the string filled up the buffer.
// This is conservative - even if the buffer was just big enough, we loop again.
// Success! fill in the requested args and return.
USES_CONVERSION;
if(str!=MMCLV_NOPTR)
*str = ::CoTaskDupString(T2OLE(buffer));
if(nFormat!=MMCLV_NOPTR)
*nFormat=col.fmt;
if(width!=MMCLV_NOPTR)
*width=col.cx;
return sc.ToHr();
}
HRESULT CCCListViewCtrl::SetItem(int nItem,
CResultItem* pri,
long nCol,
LPOLESTR str,
long nImage,
LPARAM lParam,
long nState,
COMPONENTID ownerID)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::SetItem"));
if (IsVirtual())
return (sc = E_UNEXPECTED).ToHr();
ASSERT(pri != NULL || nItem >= 0);
// if this is a debug build, perform validity checks on the args. else leave it to the user.
if (nCol<0 || nCol >= m_colCount || (str != MMCLV_NOPTR && str != MMC_TEXTCALLBACK))
return (sc = E_INVALIDARG).ToHr();
if (pri != NULL)
{
nItem = ResultItemToIndex(pri);
if (nItem == -1)
return (sc = E_INVALIDARG).ToHr();
}
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(lvi));
lvi.mask=0;
lvi.iItem = nItem;
USES_CONVERSION;
lvi.mask|=LVIF_TEXT;
lvi.pszText=LPSTR_TEXTCALLBACK;
// If the user has specified an icon index, put it in the LV_ITEM struct
if((nImage!=MMCLV_NOICON)&&(m_resultIM.Lookup(&CImageIndexMapKey((COMPONENTID)ownerID,nImage), lvi.iImage)))
lvi.mask|=LVIF_IMAGE;
// If the user requested a state. put it in the LV_ITEM struct.
if(nState!=MMCLV_NOPARAM)
{
lvi.mask|=LVIF_STATE;
lvi.stateMask=0xFFFFFFFF;
lvi.state=nState;
}
lvi.iSubItem=nCol;
CListCtrl& lc = GetListCtrl();
if (!lc.SetItem (&lvi))
sc = E_FAIL;
// If the user has specified an lParam or image, and the Set was succesful,
// put the lParam and the image's back index in the mapping.
if (!sc.IsError())
{
if ((pri == NULL) && ((pri = IndexToResultItem (nItem)) == NULL))
sc = E_FAIL;
if (!sc.IsError())
{
if (lParam != MMCLV_NOPARAM)
pri->SetSnapinData (lParam);
if (nImage != MMCLV_NOICON)
pri->SetImageIndex (nImage);
}
// if ensure focus visible style and focus set, force item into view
if (m_bEnsureFocusVisible && nState != MMCLV_NOPARAM && (nState & LVIS_FOCUSED))
lc.EnsureVisible(nItem, FALSE);
}
return (sc.ToHr());
}
HRESULT CCCListViewCtrl::GetNextItem(COMPONENTID ownerID, long nIndex,
UINT nState, CResultItem*& priNextItem, long& nIndexNextItem)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::GetNextItem"));
CListCtrl& lc = GetListCtrl();
priNextItem = 0;
nIndexNextItem = -1;
while (1)
{
nIndex = lc.GetNextItem (nIndex, nState);
if (nIndex == -1)
break;
if (IsVirtual())
{
nIndexNextItem = nIndex;
break;
}
CResultItem* pri = IndexToResultItem (nIndex);
if ((pri != NULL) && ((pri->GetOwnerID() == ownerID) || (pri->IsScopeItem())))
{
priNextItem = pri;
nIndexNextItem = nIndex;
break;
}
}
return (sc = (nIndexNextItem != -1) ? S_OK : S_FALSE).ToHr();
}
HRESULT CCCListViewCtrl::GetItem(
int nItem,
CResultItem*& pri,
long nCol,
LPOLESTR* str,
int* pnImage,
LPARAM* pLParam,
UINT* pnState,
BOOL* pbScopeItem)
{
USES_CONVERSION;
if ((nCol < 0) || (nCol >= m_colCount))
return E_INVALIDARG;
HRESULT hr = S_OK;
CListCtrl& lc = GetListCtrl();
if (IsVirtual())
{
//Virtual list can only be queried for state
if ((pri != NULL) || (nItem < 0) || (nItem >= m_itemCount) ||
(str != MMCLV_NOPTR) || (pnImage != MMCLV_NOPTR) || (pLParam != MMCLV_NOPTR))
{
ASSERT(FALSE);
hr = E_INVALIDARG;
}
else if (pnState != MMCLV_NOPTR)
{
*pnState = lc.GetItemState (nItem, 0xFFFFFFFF);
// for virtual list, it's never a scope item
if (pbScopeItem != NULL)
*pbScopeItem = FALSE;
}
}
else
{
if (pri != 0)
nItem = ResultItemToIndex(pri);
if (nItem < 0 || nItem >= m_itemCount)
hr = E_INVALIDARG;
else
{
pri = IndexToResultItem (nItem);
if ( pri == NULL )
return E_UNEXPECTED;
// if the text was requested, get that seperatly so that we can use GETITEMTEXT to
// dynamically size the buffer.
if (str != MMCLV_NOPTR)
{
CString strText = lc.GetItemText (nItem, nCol);
*str = ::CoTaskDupString (T2COLE (strText));
}
// get the state if requested
if (pnState != MMCLV_NOPTR)
*pnState = lc.GetItemState (nItem, 0xFFFFFFFF);
// Nodemgr will unravel pri & get required data (lparam & image index).
if (pri->IsScopeItem())
return hr;
// get the image, pLParam, or scope item, if requested
if ((pnImage != MMCLV_NOPTR) ||
(pLParam != MMCLV_NOPTR) ||
(pbScopeItem != NULL))
{
if (pri != NULL)
{
if (pnImage != MMCLV_NOPTR)
*pnImage = pri->GetImageIndex();
if (pLParam != MMCLV_NOPTR)
*pLParam = pri->GetSnapinData();
// set the scope item flag
if (pbScopeItem != NULL)
*pbScopeItem = pri->IsScopeItem();
}
else
hr = E_FAIL;
}
}
}
return hr;
}
HRESULT CCCListViewCtrl::GetLParam(long nItem, CResultItem*& pri)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::GetLParam"));
if (IsVirtual())
return (sc = E_UNEXPECTED).ToHr();
pri = IndexToResultItem (nItem);
if (pri == NULL)
sc = E_FAIL;
return (sc.ToHr());
}
HRESULT CCCListViewCtrl::ModifyItemState(long nItem, CResultItem* pri,
UINT add, UINT remove)
{
ASSERT(((pri != 0) && !IsVirtual()) || (nItem >= 0));
// Can only set focus and selected states for virtual item
if (IsVirtual() && ((add | remove) & ~(LVIS_FOCUSED | LVIS_SELECTED)))
{
ASSERT(FALSE);
return E_FAIL;
}
HRESULT hr = E_FAIL;
if (pri != 0 && !IsVirtual())
nItem = ResultItemToIndex(pri);
if (nItem >= 0)
{
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(lvi));
lvi.iItem = nItem;
lvi.mask = LVIF_STATE;
lvi.stateMask = add | remove;
lvi.state = add;
hr = (GetListCtrl().SetItemState (nItem, &lvi)) ? S_OK : E_FAIL;
// if ensure focus visible style and focus set, force item into view
if (m_bEnsureFocusVisible && (add & LVIS_FOCUSED))
GetListCtrl().EnsureVisible(nItem, FALSE);
}
return hr;
}
HRESULT CCCListViewCtrl::SetIcon(long ownerID, HICON hIcon, long nLoc)
{
ASSERT (m_smallIL.GetImageCount() == m_largeIL.GetImageCount());
/*
* pick the flags out of nLoc
*/
bool fChangeLargeIcon = nLoc & ILSIF_LEAVE_SMALL_ICON;
bool fChangeSmallIcon = nLoc & ILSIF_LEAVE_LARGE_ICON;
nLoc &= ~ILSIF_LEAVE_MASK;
/*
* make sure the XOR below will work
*/
ASSERT ((fChangeLargeIcon == 0) || (fChangeLargeIcon == 1));
ASSERT ((fChangeSmallIcon == 0) || (fChangeSmallIcon == 1));
CImageIndexMapKey searchKey((COMPONENTID)ownerID, nLoc);
int nNdx1;
int nNdx2;
HRESULT hr = S_OK;
BOOL fExists = m_resultIM.Lookup(&searchKey, nNdx1);
/*
* are we changing the large or small icon only?
*/
if (fChangeSmallIcon ^ fChangeLargeIcon)
{
/*
* there must be an icon at nLoc already
*/
if (!fExists)
hr = E_INVALIDARG;
/*
* changing the large icon?
*/
else if (fChangeLargeIcon)
{
if (m_largeIL.Replace(nNdx1, hIcon) != nNdx1)
hr = E_FAIL;
}
/*
* otherwise, changing the small icon?
*/
else
{
if (m_smallIL.Replace(nNdx1, hIcon) != nNdx1)
hr = E_FAIL;
}
}
else if (fExists)
{
nNdx2 = m_smallIL.Replace(nNdx1, hIcon);
if (nNdx2 == -1)
{
hr = E_FAIL;
}
else
{
if(nNdx2 != nNdx1)
{
hr = E_UNEXPECTED;
}
else
{
nNdx2 = m_largeIL.Replace(nNdx1, hIcon);
if(nNdx2 != nNdx1)
hr = E_UNEXPECTED;
}
}
}
else
{
// Insert items and store indices in large and small
nNdx1 = m_smallIL.Add(hIcon);
if (nNdx1 != -1)
nNdx2 = m_largeIL.Add(hIcon);
if (nNdx1 == -1)
{
hr = E_FAIL;
}
else if (nNdx2 == -1)
{
m_smallIL.Remove (nNdx1);
hr = E_FAIL;
}
else if(nNdx1 != nNdx2)
{
m_smallIL.Remove (nNdx1);
m_largeIL.Remove (nNdx2);
hr = E_UNEXPECTED;
}
else
{
// Generate a new key and store the values in the maps
PImageIndexMapKey pKey = new CImageIndexMapKey((COMPONENTID)ownerID, nLoc);
m_resultIM[pKey] = nNdx1;
}
}
#ifdef DBG
if (tagListImages.FAny())
{
DrawOnDesktop (m_smallIL, 0, 0);
DrawOnDesktop (m_largeIL, 0, 32);
}
#endif
ASSERT (m_smallIL.GetImageCount() == m_largeIL.GetImageCount());
return hr;
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::SetImageStrip
*
* Adds one or more images to the imagelist. Bitmaps are owned (and
* released) by the caller.
*--------------------------------------------------------------------------*/
HRESULT CCCListViewCtrl::SetImageStrip (
long ownerID,
HBITMAP hbmSmall,
HBITMAP hbmLarge,
long nStartLoc,
long cMask)
{
DECLARE_SC (sc, _T("CCCListViewCtrl::SetImageStrip"));
ASSERT (m_smallIL.GetImageCount() == m_largeIL.GetImageCount());
/*
* valid start index?
*/
if (nStartLoc < 0)
return ((sc = E_INVALIDARG).ToHr());
/*
* valid bitmaps?
*/
sc = ScCheckPointers (hbmSmall, hbmLarge);
if (sc)
return (sc.ToHr());
BITMAP bmSmall;
if (!GetObject (hbmSmall, sizeof(BITMAP), &bmSmall))
return (sc.FromLastError().ToHr());
BITMAP bmLarge;
if (!GetObject (hbmLarge, sizeof(BITMAP), &bmLarge))
return (sc.FromLastError().ToHr());
/*
* are the small and large bitmaps of the integral dimensions,
* and do they have the same number of images?
*/
if ( (bmSmall.bmHeight != 16) || (bmLarge.bmHeight != 32) ||
(bmSmall.bmWidth % 16) || (bmLarge.bmWidth % 32) ||
((bmSmall.bmWidth / 16) != (bmLarge.bmWidth / 32)))
{
return ((sc = E_INVALIDARG).ToHr());
}
const int cEntries = bmSmall.bmWidth / 16;
/*
* make copies of the input bitmaps because CImageList::Add (which calls
* ImageList_AddMasked) will screw up the background color
*/
CBitmap bmpSmall, bmpLarge;
bmpSmall.Attach (CopyBitmap (hbmSmall));
bmpLarge.Attach (CopyBitmap (hbmLarge));
if ((bmpSmall.GetSafeHandle() == NULL) || (bmpLarge.GetSafeHandle() == NULL))
return (sc.FromLastError().ToHr());
/*
* add the small image
*/
const int nFirstNewIndexSmall = m_smallIL.Add (&bmpSmall, cMask);
if (nFirstNewIndexSmall == -1)
return (sc.FromLastError().ToHr());
/*
* add the large image
*/
const int nFirstNewIndexLarge = m_largeIL.Add (&bmpLarge, cMask);
if (nFirstNewIndexLarge == -1)
{
/*
* Images can be added many at a time, but only removed one at
* a time. Remove each entry we added.
*/
for (int i = 0; i < cEntries; i++)
m_smallIL.Remove (nFirstNewIndexSmall);
ASSERT (m_smallIL.GetImageCount() == m_largeIL.GetImageCount());
return (sc.FromLastError().ToHr());
}
/*
* if the starting indices of the large and small images aren't
* the same, we screwed
*/
if (nFirstNewIndexSmall != nFirstNewIndexLarge)
{
/*
* Images can be added many at a time, but only removed one at
* a time. Remove each entry we added.
*/
for (int i = 0; i < cEntries; i++)
{
m_smallIL.Remove (nFirstNewIndexSmall);
m_largeIL.Remove (nFirstNewIndexLarge);
}
ASSERT (m_smallIL.GetImageCount() == m_largeIL.GetImageCount());
return ((sc = E_UNEXPECTED).ToHr());
}
// Keep the map updated for each newly inserted image.
for(int i=0; i < cEntries; i++)
{
CImageIndexMapKey searchKey((COMPONENTID)ownerID, nStartLoc+i);
// if the item exists in the map, replace the value, else create a new
// key and set the value.
int nIndex = nFirstNewIndexSmall;
// use copy of nFirstNewIndexSmall as Lookup modifies nIndex.
if(m_resultIM.Lookup(&searchKey, nIndex))
m_resultIM[&searchKey] = nFirstNewIndexSmall+i;
else
m_resultIM[new CImageIndexMapKey((COMPONENTID)ownerID, nStartLoc+i)] = nFirstNewIndexSmall+i;
}
#ifdef DBG
if (tagListImages.FAny())
{
DrawOnDesktop (m_smallIL, 0, 0);
DrawOnDesktop (m_largeIL, 0, 32);
}
#endif
ASSERT (m_smallIL.GetImageCount() == m_largeIL.GetImageCount());
return (sc.ToHr());
}
HRESULT CCCListViewCtrl::MapImage(long ownerID, long nLoc, int far *pResult)
{
CImageIndexMapKey searchKey((COMPONENTID)ownerID, nLoc);
HRESULT hr = S_OK;
ASSERT(pResult);
if(!(m_resultIM.Lookup(&searchKey, *((int *)pResult))))
hr = E_FAIL;
return hr;
}
HRESULT CCCListViewCtrl::Reset()
{
DECLARE_SC (sc, _T("CCCListViewCtrl::Reset"));
// Note: we must call this->DeleteAllItems(TVOWNED_MAGICWORD) & not
// GetListCtrl().DeleteAllItems() to ensure that all internal data
// is cleaned up.
DeleteAllItems(TVOWNED_MAGICWORD);
ASSERT(GetListCtrl().GetItemCount() == 0);
ASSERT(m_itemCount == 0);
ASSERT(m_nScopeItems == 0);
m_resultIM.RemoveAll();
m_smallIL.DeleteImageList();
m_largeIL.DeleteImageList();
sc = ScSetImageLists();
if (sc)
return (sc.ToHr());
// Delete all columns
while (SUCCEEDED (DeleteColumn(0))) {};
if (m_pListView)
sc = m_pListView->ScResetColumnStatusData();
if (sc)
sc.TraceAndClear();
// reset lexical sorting until Sort is called again
m_sortParams.bLexicalSort = FALSE;
// release the snap-in's compare interfaces
m_sortParams.spResultCompare = NULL;
m_sortParams.spResultCompareEx = NULL;
return (sc.ToHr());
}
//+-------------------------------------------------------------------
//
// Member: SortCompareFunc
//
// Synopsis: Compare two items, called by list control sort.
//
// Arguments: [lParam1] - Item1's lparam.
// [lParam2] - Item2's lparam.
// [pSortParams_] - ptr to SortParams.
//
// Note: If snapin wants lexical sort do default-compare.
// Else if snapin has IResultDataCompare[Ex] then call it
// Else do default-compare.
//
// Returns: -1 : item1 < item2
// 0 : item1 == item2
// +1 : item1 > item2
//
//--------------------------------------------------------------------
int CALLBACK CCCListViewCtrl::SortCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM pSortParams_)
{
SortParams* pSortParams = reinterpret_cast<SortParams*>(pSortParams_);
ASSERT (pSortParams != NULL);
CCCListViewCtrl* pListView = reinterpret_cast<CCCListViewCtrl*>(pSortParams->lpListView);
ASSERT (pListView != NULL);
CResultItem* pri1 = CResultItem::FromHandle (lParam1);
CResultItem* pri2 = CResultItem::FromHandle (lParam2);
if (pri1 == NULL || pri2 == NULL)
{
ASSERT(FALSE);
return 0;
}
BOOL bScope1 = pri1->IsScopeItem();
BOOL bScope2 = pri2->IsScopeItem();
int iResult;
// if snap-in provides extended compare method
if (pSortParams->spResultCompareEx != NULL)
{
ASSERT(pSortParams->lpNodeCallback);
if (NULL == pSortParams->lpNodeCallback)
return 0; // Error
COMPONENTID ItemID;
BOOL bOwned1 = !bScope1 ||
((pSortParams->lpNodeCallback->GetNodeOwnerID(pri1->GetScopeNode(), &ItemID) == S_OK) &&
(ItemID == pSortParams->OwnerID));
BOOL bOwned2 = !bScope2 ||
((pSortParams->lpNodeCallback->GetNodeOwnerID(pri2->GetScopeNode(), &ItemID) == S_OK) &&
(ItemID == pSortParams->OwnerID));
// let snap-in order all items that it owns (scope and result)
// put rest of items items after owned items
if (bOwned1 && bOwned2)
iResult = SnapinCompareEx(pSortParams, pri1, pri2);
else if (bOwned1 || bOwned2)
iResult = bOwned1 ? -1 : 1;
else
// DefaultCompare flips results depending on ascending or descending.
return DefaultCompare(lParam1, lParam2, pSortParams_);
}
// do default sorting
else
{
// pass result items to original compare method if provided, else to default sort
if (!bScope1 && !bScope2)
{
if (pSortParams->spResultCompare != NULL)
iResult = SnapinCompare(pSortParams, pri1, pri2);
else
// DefaultCompare flips results depending on ascending or descending.
return DefaultCompare(lParam1, lParam2, pSortParams_);
}
// do not order scope items, just put them ahead of result items
else
{
iResult = (bScope1 && bScope2) ? 0 : (bScope1 ? -1 : 1);
}
}
// flip order for descending sort
return pSortParams->bAscending ? iResult : -iResult;
}
//+-------------------------------------------------------------------
//
// Member: DefaultCompare
//
// Synopsis: Compare two items, called by list control sort.
// This is used if snapin wants default compare or
// if it does not implement IResultDataCompare or
// IResultDataCompareEx interfaces
//
// Arguments: [lParam1] - Item1's lparam.
// [lParam2] - Item2's lparam.
// [pSortParams] - ptr to SortParams.
//
// Note: If one is scope item and other is result item
// place scope item before result item.
// Else get the text for both items and do string compare.
//
// Returns: -1 : item1 < item2
// 0 : item1 == item2
// +1 : item1 > item2
//
//--------------------------------------------------------------------
int CALLBACK CCCListViewCtrl::DefaultCompare(LPARAM lParam1, LPARAM lParam2, LPARAM pSortParams_)
{
SortParams* pSortParams = reinterpret_cast<SortParams*>(pSortParams_);
ASSERT(NULL != pSortParams);
if (NULL == pSortParams)
return 0;
CResultItem* pri1 = CResultItem::FromHandle (lParam1);
CResultItem* pri2 = CResultItem::FromHandle (lParam2);
ASSERT( (NULL != pri1) && (NULL != pri2));
if ( (NULL == pri1) || (NULL == pri2) )
return 0;
bool bScope1 = pri1->IsScopeItem();
bool bScope2 = pri2->IsScopeItem();
// If one of the item is scope pane item
// scope item goes before result item.
if (bScope1 != bScope2)
{
int iResult = bScope1 ? -1 : 1;
return pSortParams->bAscending ? iResult : -iResult;
}
LPNODECALLBACK lpNodeCallback = pSortParams->lpNodeCallback;
ASSERT(lpNodeCallback);
if (NULL == lpNodeCallback)
return 0;
HRESULT hr = E_FAIL;
CString strText1;
CString strText2;
if (bScope1)
{
// Both scope items, get the text for each item.
HNODE hNode1 = pri1->GetScopeNode();
HNODE hNode2 = pri2->GetScopeNode();
USES_CONVERSION;
tstring strName;
// GetDisplayName uses a static array to return name so no need to free it.
hr = lpNodeCallback->GetDisplayName(hNode1, strName);
ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr))
strText1 = strName.data();
hr = lpNodeCallback->GetDisplayName(hNode2, strName);
ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr))
strText2 = strName.data();
}
else // both items are result items.
{
ASSERT(!bScope1 && ! bScope2);
CCCListViewCtrl* pListView = reinterpret_cast<CCCListViewCtrl*>(pSortParams->lpListView);
ASSERT (pListView != NULL);
ASSERT(pListView->IsVirtual() == FALSE); // Virtual list sort should not come here.
LV_ITEMW lvi;
ZeroMemory(&lvi, sizeof(LV_ITEMW));
lvi.mask = LVIF_TEXT;
lvi.iSubItem = pSortParams->nCol;
lvi.cchTextMax = MAX_PATH;
WCHAR szTemp[MAX_PATH+1];
lvi.pszText = szTemp;
ASSERT(NULL != pSortParams->hSelectedNode);
if (NULL != pSortParams->hSelectedNode)
{
lvi.lParam = lParam1;
hr = lpNodeCallback->GetDispInfo(pSortParams->hSelectedNode, &lvi);
ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr))
strText1 = lvi.pszText;
lvi.lParam = lParam2;
hr = lpNodeCallback->GetDispInfo(pSortParams->hSelectedNode, &lvi);
ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr))
strText2 = lvi.pszText;
}
}
if (strText1.IsEmpty() && strText2.IsEmpty())
return (0);
int rc = 0;
/*
* Bug 9595: Do locale-sensitive, case-insensitive comparison
*/
switch (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE, strText1, -1, strText2, -1))
{
case CSTR_LESS_THAN:
rc = -1;
break;
case CSTR_EQUAL:
rc = 0;
break;
case CSTR_GREATER_THAN:
rc = 1;
break;
default:
/*
* if an error occurred, fall back to locale-insensitive,
* case-insensitive comparison
*/
rc = _tcsicmp (strText1, strText2);
break;
}
return pSortParams->bAscending ? rc: -rc;
}
int CCCListViewCtrl::SnapinCompare(SortParams* pSortParams, CResultItem* pri1, CResultItem* pri2)
{
ASSERT(pSortParams->spResultCompare != NULL);
// Set nResult to the current column
int nResult = pSortParams->nCol;
HRESULT hr = pSortParams->spResultCompare->Compare(pSortParams->lpUserParam, pri1->GetSnapinData(), pri2->GetSnapinData(), &nResult);
return SUCCEEDED(hr) ? nResult : 0;
}
int CCCListViewCtrl::SnapinCompareEx(SortParams* pSortParams, CResultItem* pri1, CResultItem* pri2)
{
ASSERT(pSortParams->spResultCompareEx != NULL);
RDITEMHDR rdch1;
RDITEMHDR rdch2;
if (pri1->IsScopeItem())
{
rdch1.dwFlags = RDCI_ScopeItem;
pSortParams->lpNodeCallback->GetNodeCookie(pri1->GetScopeNode(), &rdch1.cookie);
}
else
{
rdch1.dwFlags = 0;
rdch1.cookie = pri1->GetSnapinData();
}
if (pri2->IsScopeItem())
{
rdch2.dwFlags = RDCI_ScopeItem;
pSortParams->lpNodeCallback->GetNodeCookie(pri2->GetScopeNode(), &rdch2.cookie);
}
else
{
rdch2.dwFlags = 0;
rdch2.cookie = pri2->GetSnapinData();
}
rdch1.lpReserved = 0;
rdch2.lpReserved = 0;
RDCOMPARE rdc;
rdc.cbSize = sizeof(rdc);
rdc.dwFlags = 0;
rdc.nColumn = pSortParams->nCol;
rdc.lUserParam = pSortParams->lpUserParam;
rdc.prdch1 = &rdch1;
rdc.prdch2 = &rdch2;
int nResult = 0;
HRESULT hr = pSortParams->spResultCompareEx->Compare(&rdc, &nResult);
return SUCCEEDED(hr) ? nResult : 0;
}
HRESULT CCCListViewCtrl::Arrange(long style)
{
return ((GetListCtrl().Arrange (style)) ? S_OK : S_FALSE);
}
HRESULT CCCListViewCtrl::Repaint(BOOL bErase)
{
m_pListView->Invalidate(bErase);
return S_OK;
}
HRESULT CCCListViewCtrl::SetItemCount(int iItemCount, DWORD dwOptions)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::SetItemCount"));
ASSERT(iItemCount >= 0);
ASSERT((dwOptions & ~(LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL)) == 0);
// Ask the CAMCListViewCtrl to setup headers & set the flag.
sc = ScCheckPointers(m_pListView, E_UNEXPECTED);
if (! sc.IsError())
sc = m_pListView->ScRestoreColumnsFromPersistedData();
if (sc)
sc.TraceAndClear();
int iTop = ListView_GetTopIndex(GetListCtrl());
if (ListView_SetItemCountEx (GetListCtrl(), iItemCount, dwOptions))
{
// if virtual list, update the item count
// if not virtual, SetItemCount just reserves space for new items
if (IsVirtual())
m_itemCount = iItemCount;
}
else
{
ASSERT(FALSE);
sc = E_FAIL;
}
iTop = ListView_GetTopIndex(GetListCtrl());
if (sc)
return sc.ToHr();
// we cannot track any items any more - broadcast the message to observers
sc = ScFireEvent(CListViewObserver::ScOnListViewIndexesReset);
if (sc)
return sc.ToHr();
return sc.ToHr();
}
HRESULT CCCListViewCtrl::SetChangeTimeOut(ULONG lTimeout)
{
BOOL bStat = FALSE;
if (GetHeaderCtrl())
bStat = ::SendMessage(GetHeaderCtrl()->m_hWnd, HDM_SETFILTERCHANGETIMEOUT, 0, (LPARAM)lTimeout);
return (bStat ? S_OK : E_FAIL);
}
HRESULT CCCListViewCtrl::SetColumnFilter(int nCol, DWORD dwType, MMC_FILTERDATA* pFilterData)
{
HRESULT hr = S_OK;
USES_CONVERSION;
HD_ITEM item;
do // not a loop
{
if (GetHeaderCtrl() == NULL)
{
hr = E_FAIL;
break;
}
DWORD dwTypeOnly = dwType & ~MMC_FILTER_NOVALUE;
BOOL bHasValue = !(dwType & MMC_FILTER_NOVALUE);
// Validate filter type
ASSERT(dwTypeOnly == MMC_INT_FILTER || dwTypeOnly == MMC_STRING_FILTER);
if (!(dwTypeOnly == MMC_INT_FILTER || dwTypeOnly == MMC_STRING_FILTER))
{
hr = E_INVALIDARG;
break;
}
// Check for non-null filterdata and pszText
if ( ((dwType == MMC_STRING_FILTER || bHasValue) && pFilterData == NULL) ||
(dwType == MMC_STRING_FILTER && bHasValue && pFilterData->pszText == NULL) )
{
ASSERT(FALSE);
hr = E_POINTER;
break;
}
ZeroMemory(&item, sizeof(item));
item.mask = HDI_FILTER;
item.type = dwType;
HD_TEXTFILTER textFilter;
switch (dwTypeOnly)
{
case MMC_INT_FILTER:
item.pvFilter = &pFilterData->lValue;
break;
case MMC_STRING_FILTER:
{
item.pvFilter = &textFilter;
textFilter.cchTextMax = pFilterData->cchTextMax;
if (bHasValue)
textFilter.pszText = OLE2T(pFilterData->pszText);
break;
}
default:
ASSERT(FALSE);
}
if (!GetHeaderCtrl()->SetItem(nCol, &item))
{
ASSERT(FALSE);
hr = E_FAIL;
}
}
while(0);
return hr;
}
HRESULT CCCListViewCtrl::GetColumnFilter(int nCol, DWORD* pdwType, MMC_FILTERDATA* pFilterData)
{
HRESULT hr = S_OK;
USES_CONVERSION;
HD_ITEM item;
do // not a loop
{
if (GetHeaderCtrl() == NULL)
{
hr = E_FAIL;
break;
}
ASSERT(pdwType != NULL);
if (pdwType == NULL)
{
hr = E_POINTER;
break;
}
ASSERT(*pdwType == MMC_INT_FILTER || *pdwType == MMC_STRING_FILTER);
if (!(*pdwType == MMC_INT_FILTER || *pdwType == MMC_STRING_FILTER))
{
hr = E_INVALIDARG;
break;
}
ASSERT(!(*pdwType == MMC_STRING_FILTER && pFilterData != NULL && pFilterData->pszText == NULL));
if ((*pdwType == MMC_STRING_FILTER && pFilterData != NULL && pFilterData->pszText == NULL))
{
hr = E_INVALIDARG;
break;
}
ZeroMemory(&item, sizeof(item));
item.mask = HDI_FILTER;
item.type = *pdwType;
HD_TEXTFILTER textFilter;
if (pFilterData != 0)
{
switch (*pdwType)
{
case MMC_INT_FILTER:
item.pvFilter = &pFilterData->lValue;
break;
case MMC_STRING_FILTER:
{
item.pvFilter = &textFilter;
textFilter.pszText = (LPTSTR)alloca((pFilterData->cchTextMax + 1) * sizeof(TCHAR));
textFilter.pszText[0] = 0;
textFilter.cchTextMax = pFilterData->cchTextMax;
break;
}
default:
ASSERT(FALSE);
}
}
BOOL bStat = GetHeaderCtrl()->GetItem(nCol, &item);
if (!bStat)
hr = E_FAIL;
// NOTE: GetHeaderCtrl()->GetItem() fails when a string filter is empty
// Until this is fixed, assume that the error is caused by this
// and fake an empty string result
if (hr == E_FAIL && item.type == MMC_STRING_FILTER)
{
item.type |= HDFT_HASNOVALUE;
hr = S_OK;
}
// if requested string filter value, convert to caller's buffer
if (hr == S_OK && item.type == MMC_STRING_FILTER && pFilterData != NULL)
{
ocscpy(pFilterData->pszText, T2OLE(textFilter.pszText));
}
*pdwType = item.type;
}
while(0);
return hr;
}
//+-------------------------------------------------------------------
//
// Member: SetColumnSortIcon
//
// Synopsis: Set sort arrow if needed.
//
// Arguments: [nNewCol] - The column for which arrow should be set.
// [nOldCol] - The previous column, remove sort arrow.
// [bAscending] - Ascending/Descending.
// [bSetSortIcon] - If arrow is needed or not.
//
// Returns: S_OK.
//
// History: 04-01-1998 AnandhaG Created
//
//--------------------------------------------------------------------
HRESULT CCCListViewCtrl::SetColumnSortIcon(int nNewCol, int nOldCol,
BOOL bAscending, BOOL bSetSortIcon)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::SetColumnSortIcon"));
LVCOLUMN lvcol, lvOriginalCol;
ZeroMemory(&lvcol, sizeof(lvcol));
lvcol.mask = LVCF_FMT | LVCF_IMAGE;
ZeroMemory(&lvOriginalCol, sizeof(lvOriginalCol));
lvOriginalCol.mask = LVCF_FMT;
// update the old column
if ( nOldCol >= 0 )
{
// retrieve old frmt settings
if ( !GetListCtrl().GetColumn(nOldCol, &lvOriginalCol) )
return (sc = E_FAIL).ToHr();
// make the new format settings
// transfer old values such as LVCFMT_CENTER, which we do not want to change
// see windows bugs (ntbug09) #153029 10/09/00
lvcol.fmt = lvOriginalCol.fmt & ~(LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT);
// Reset the previous column's sort icon with blank icon.
lvcol.iImage = -1;
if ( !GetListCtrl().SetColumn(nOldCol, &lvcol) )
return (sc = E_FAIL).ToHr();
}
// We have to add sort icon only if LV items can be sorted.
// This is possible only if any of following condition is true.
// a. there are any result items in result pane OR
// b. snapin supports IResultDataCompare OR
// c. snapin supports IResultDataCompareEx OR
// d. snapin wants default lexical sort OR
// e. snapin has virtual list
BOOL bCanSortListView = (0 != (m_itemCount - m_nScopeItems)) ||
(NULL != m_sortParams.spResultCompare) ||
(NULL != m_sortParams.spResultCompareEx) ||
(TRUE == m_sortParams.bLexicalSort) ||
(IsVirtual());
if ( bCanSortListView && bSetSortIcon)
{
// retrieve old frmt settings
if ( !GetListCtrl().GetColumn(nNewCol, &lvOriginalCol) )
return (sc = E_FAIL).ToHr();
// make the new format settings
// transfer old values such as LVCFMT_CENTER, which we do not want to change
// see windows bugs (ntbug09) #153029 10/09/00
lvcol.fmt = lvOriginalCol.fmt | LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT;
// Set the sort icon for new column.
lvcol.iImage = (bAscending) ? 0 : 1;
if ( !GetListCtrl().SetColumn(nNewCol, &lvcol) )
return (sc = E_FAIL).ToHr();
}
// De-select all the items in virtual list.
if (IsVirtual())
{
int nItem = -1;
LV_ITEM lvi;
lvi.stateMask = LVIS_SELECTED;
lvi.state = ~LVIS_SELECTED;
while ( (nItem = GetListCtrl().GetNextItem(nItem, LVNI_SELECTED)) != -1)
{
GetListCtrl().SetItemState(nItem, &lvi);
}
}
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::ScRedrawHeader
//
// Synopsis: Need to send WM_SETREDRAW to headers to reduce flicker
// when persisted column data is applied.
// Turn it off before sending MMCN_SHOW to snapins and turn
// it on after MMCN_SHOW returns.
//
// Arguments: [bRedraw] -
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CCCListViewCtrl::ScRedrawHeader (bool bRedraw)
{
DECLARE_SC(sc, _T("CCCListViewCtrl::ScRedrawHeader"));
CAMCHeaderCtrl* pHeader = GetHeaderCtrl();
sc = ScCheckPointers(pHeader, E_UNEXPECTED);
if (sc)
return sc;
int nViewMode = GetViewMode();
// Turn off/on the header only if it is report or filtered mode.
// If turned on in other modes comctl does not take care of different
// mode and will show headers (eg: in large icon mode).
if ( (nViewMode != MMCLV_VIEWSTYLE_REPORT) && (nViewMode != MMCLV_VIEWSTYLE_FILTERED) )
return sc;
pHeader->SetRedraw(bRedraw);
// If redraw is true then repaint the control.
if (bRedraw)
pHeader->InvalidateRect(NULL);
return (sc);
}
/*+-------------------------------------------------------------------------*
*
* CCCListViewCtrl::SetLoadMode
*
* PURPOSE: Turn on/off redraw on list control & header control when
* persisted list view settings (columns...) are applied.
*
* PARAMETERS:
* BOOL bState - load state, true -> turn-off redraw, false -> turn-on redraw
*
* RETURNS:
* HRESULT
*
*+-------------------------------------------------------------------------*/
HRESULT CCCListViewCtrl::SetLoadMode(BOOL bState)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::SetLoadMode"));
if (bState == m_bLoading)
return (sc.ToHr());
if (bState)
{
// turn off drawing during loading
// 1. Turn off header.
sc = ScRedrawHeader(false);
if (sc)
sc.TraceAndClear();
// 2. Turn off the listcontrol.
GetListCtrl().SetRedraw(false);
}
else
{
sc = ScCheckPointers(m_pListView, E_UNEXPECTED);
if (sc)
sc.TraceAndClear();
else
{
sc = m_pListView->ScRestoreColumnsFromPersistedData();
if (sc)
sc.TraceAndClear();
}
// if sort requested while loading, sort the loaded items now
if (m_bDeferredSort)
{
/*
* the sort could take awhile, so show a wait cursor
*/
CWaitCursor wait;
GetListCtrl().SortItems (SortCompareFunc, (DWORD_PTR)&m_sortParams);
m_bDeferredSort = FALSE;
}
// 1. Important, first turn on list and then header else header will not be redrawn.
GetListCtrl().SetRedraw(true);
// 2. Turn on the header.
sc = ScRedrawHeader(true);
if (sc)
sc.TraceAndClear();
}
m_bLoading = bState;
return (sc.ToHr());
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::GetColumnInfoList
//
// Synopsis: Get the current column settings.
//
// Arguments: [pColumnsList] - [out param], ptr to CColumnsInfoList.
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CCCListViewCtrl::GetColumnInfoList (CColumnInfoList *pColumnsList)
{
DECLARE_SC(sc, _T("CCCListViewCtrl::GetColumnInfoList"));
sc = ScCheckPointers(pColumnsList);
if (sc)
return sc.ToHr();
CAMCListView *pAMCListView = GetListViewPtr();
sc = ScCheckPointers(pAMCListView, E_UNEXPECTED);
if (sc)
return sc.ToHr();
sc = pAMCListView->ScGetColumnInfoList(pColumnsList);
if (sc)
return sc.ToHr();
return (sc.ToHr());
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::ModifyColumns
//
// Synopsis: Modify the columns with given data.
//
// Arguments: [columnsList] -
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CCCListViewCtrl::ModifyColumns (const CColumnInfoList& columnsList)
{
DECLARE_SC(sc, _T("CCCListViewCtrl::ModifyColumns"));
CAMCListView *pAMCListView = GetListViewPtr();
sc = ScCheckPointers(pAMCListView, E_UNEXPECTED);
if (sc)
return sc.ToHr();
sc = pAMCListView->ScModifyColumns(columnsList);
if (sc)
return sc.ToHr();
return (sc.ToHr());
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::GetDefaultColumnInfoList
//
// Synopsis: Get the default column settings
//
// Arguments: [columnInfoList] - [out]
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CCCListViewCtrl::GetDefaultColumnInfoList (CColumnInfoList& columnInfoList)
{
DECLARE_SC(sc, _T("CNodeInitObject::GetDefaultColumnInfoList"));
CAMCListView *pAMCListView = GetListViewPtr();
sc = ScCheckPointers(pAMCListView, E_UNEXPECTED);
if (sc)
return sc.ToHr();
sc = pAMCListView->ScGetDefaultColumnInfoList(columnInfoList);
if (sc)
return sc.ToHr();
return (sc.ToHr());
}
/*+-------------------------------------------------------------------------*
*
* CCCListViewCtrl::RenameItem
*
* PURPOSE: Puts the specified result item into rename mode.
*
* PARAMETERS:
* CResultItem* pri :
*
* RETURNS:
* STDMETHODIMP
*
*+-------------------------------------------------------------------------*/
STDMETHODIMP
CCCListViewCtrl::RenameItem(HRESULTITEM itemID)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::RenameItem"));
int nIndex = -1;
sc = ScGetItemIndexFromHRESULTITEM(itemID, nIndex);
if (sc)
return sc.ToHr();
if(nIndex < 0 || nIndex >= m_itemCount)
return (sc = E_INVALIDARG).ToHr();
// must have the focus to rename
if (::GetFocus()!= GetListCtrl())
SetFocus(GetListCtrl());
// if the rename failed, E_FAIL is returned.
if(NULL==GetListCtrl().EditLabel(nIndex))
return (sc=E_FAIL).ToHr();
return sc.ToHr();
}
HRESULT CCCListViewCtrl::OnModifyItem(CResultItem* pri)
{
HRESULT hr = S_OK;
int nItem = ResultItemToIndex(pri);
if(nItem < 0 || nItem >= m_itemCount)
{
ASSERT(FALSE);
return E_INVALIDARG;
}
LV_ITEM lvi;
ZeroMemory(&lvi, sizeof(lvi));
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.iItem = nItem;
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.iImage = I_IMAGECALLBACK;
GetListCtrl().SetItem( &lvi );
if (!GetListCtrl().RedrawItems (nItem, nItem))
hr = E_FAIL;
CHECK_HRESULT(hr);
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CCCListViewCtrl::ScSelectAll
//
// Synopsis: Select all the items in the list view.
//
// Arguments: None
//
// Returns: SC
//
//--------------------------------------------------------------------
SC CCCListViewCtrl::ScSelectAll ()
{
DECLARE_SC(sc, _T("CCCListViewCtrl::ScSelectAll"));
LV_ITEM lvi;
lvi.stateMask = lvi.state = LVIS_SELECTED;
for (int i = 0; i < GetListCtrl().GetItemCount(); ++i)
{
// NOTE: do not use GetListCtrl().SetItemState - it uses SetItem which is not supported for virtual lists
if (!GetListCtrl().SendMessage( LVM_SETITEMSTATE, WPARAM(i), (LPARAM)(LV_ITEM FAR *)&lvi))
return (sc = E_FAIL);
}
return (sc);
}
//-------------------------------------------------- Windows Hooks
BOOL CCCListViewCtrl::Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext /*=NULL*/ )
{
DECLARE_SC (sc, _T("CCCListViewCtrl::Create"));
ASSERT(pParentWnd != NULL && IsWindow(pParentWnd->m_hWnd));
BOOL bRet = FALSE;
// standard or virtual ?
CAMCListView* pListView = (dwStyle & LVS_OWNERDATA) ? m_pVirtualList : m_pStandardList;
ASSERT(pListView->m_hWnd == NULL);
if (pListView->Create(NULL, NULL, dwStyle, rect, pParentWnd, nID, pContext))
{
// Attach image lists
sc = ScSetImageLists ();
if (sc)
{
pListView->DestroyWindow();
return (false);
}
// update member variables
m_bVirtual = (dwStyle & LVS_OWNERDATA) ? TRUE : FALSE;
m_pListView = pListView;
m_pParentWnd = pParentWnd;
bRet = TRUE;
}
int iTop = ListView_GetTopIndex(GetListCtrl());
return bRet;
}
SC CCCListViewCtrl::ScSetImageLists ()
{
DECLARE_SC (sc, _T("CCCListViewCtrl::ScSetImageLists"));
CListCtrl& lc = GetListCtrl();
/*
* if we need to create one list, we should need to create both
*/
ASSERT ((m_smallIL.GetSafeHandle() == NULL) == (m_largeIL.GetSafeHandle() == NULL));
/*
* create the image lists, if necessary
*/
if (m_smallIL.GetSafeHandle() == NULL)
{
if (!m_smallIL.Create(16, 16, ILC_COLORDDB | ILC_MASK, 20, 10) ||
!m_largeIL.Create(32, 32, ILC_COLORDDB | ILC_MASK, 20, 10))
{
goto Error;
}
// Add standard MMC bitmaps
CBitmap bmSmall;
CBitmap bmLarge;
if (!bmSmall.LoadBitmap(IDB_AMC_NODES16) || !bmLarge.LoadBitmap(IDB_AMC_NODES32))
goto Error;
sc = SetImageStrip (TVOWNED_MAGICWORD, bmSmall, bmLarge, 0, RGB(255,0,255));
if (sc)
goto Error;
}
/*
* attach them to the list control
*/
lc.SetImageList (&m_smallIL, LVSIL_SMALL);
lc.SetImageList (&m_largeIL, LVSIL_NORMAL);
/*
* setting the small image list for the list control overwrites
* the image list for the header control; fix it up
*/
{
CWnd* pwndHeader = GetHeaderCtrl();
if (pwndHeader != NULL)
Header_SetImageList (*pwndHeader, (HIMAGELIST) m_headerIL);
}
return (sc);
Error:
/*
* DeleteImageList is safe to call on uncreated CImageLists
*/
m_smallIL.DeleteImageList();
m_largeIL.DeleteImageList();
/*
* If we haven't filled in the SC with an error code, try last error.
* Some (many) APIs fail without setting the last error, so if we still
* don't have an error code, give it a generic E_FAIL.
*/
if (!sc.IsError())
{
sc.FromLastError();
if (!sc.IsError())
sc = E_FAIL;
}
return (sc);
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::OnSysColorChange
*
* WM_SYSCOLORCHANGE handler for CCCListViewCtrl.
*--------------------------------------------------------------------------*/
void CCCListViewCtrl::OnSysColorChange()
{
m_headerIL.OnSysColorChange();
CWnd* pwndHeader = GetHeaderCtrl();
if (pwndHeader != NULL)
Header_SetImageList (*pwndHeader, (HIMAGELIST) m_headerIL);
}
/*+-------------------------------------------------------------------------*
*
* CCCListViewCtrl::ScAttachToListPad
*
* PURPOSE: Attaches/Detaches the list view to the listpad window. The listpad
* window is an IE frame. Attaching occurs by reparenting the list view.
*
* PARAMETERS:
* HWND hwnd : The new parent window, or NULL to detach
* HWND* phwnd : 1) non-NULL phwnd: The list view window handle is returned as
* an out parameter
* 2) NULL phwnd: Detaches the list view control from the list pad.
*
* RETURNS:
* HRESULT
*
*+-------------------------------------------------------------------------*/
SC
CCCListViewCtrl::ScAttachToListPad (HWND hwnd, HWND* phwnd)
{
DECLARE_SC (sc, TEXT("CCCListViewCtrl::ScAttachToListPad"));
CAMCView* pAMCView = dynamic_cast<CAMCView*>(m_pParentWnd); // pointer is checked before usage, no need to test here.
if (phwnd)
{
// attaching
// are we still attached?
if (m_SavedHWND)
{
//Attaching to ListPad when already attached - just make window exactly the same size as (new) parent
RECT r;
::GetWindowRect (hwnd, &r);
m_pListView->SetWindowPos (NULL, 0, 0,
r.right-r.left,
r.bottom-r.top, SWP_NOZORDER | SWP_NOACTIVATE);
return sc;
}
else
{
// save current parent hwnd and its state
m_SavedHWND = ::GetParent (m_pListView->m_hWnd);
m_wp.length = sizeof(WINDOWPLACEMENT);
::GetWindowPlacement (m_pListView->m_hWnd, &m_wp);
// switch to new one
::SetParent (m_pListView->m_hWnd, hwnd);
m_pListView->ShowWindow (SW_SHOW);
// make window exactly the same size as (new) parent
RECT r;
::GetWindowRect (hwnd, &r);
m_pListView->SetWindowPos (NULL, 0, 0,
r.right-r.left,
r.bottom-r.top, SWP_NOZORDER);
// return back my window
*phwnd = m_pListView->m_hWnd;
// notify snapin of attach
if (pAMCView)
pAMCView->NotifyListPad (phwnd != NULL);
}
}
else
{
// detaching
if (m_SavedHWND == NULL) // this may get called repeatedly...
return S_OK;
// notify snapin of detach
if (pAMCView)
pAMCView->NotifyListPad (phwnd != NULL);
// change back parent window and its state
HWND hWndNewParent = m_pListView->m_hWnd;
::SetParent (m_pListView->m_hWnd, m_SavedHWND);
if (m_wp.length != 0)
{
m_wp.showCmd = SW_HIDE;
::SetWindowPlacement (m_pListView->m_hWnd, &m_wp);
}
// clear saved window and state
m_SavedHWND = NULL;
ZeroMemory (&m_wp, sizeof(WINDOWPLACEMENT));
Reset();
}
return sc;
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::OnCustomDraw
*
* NM_CUSTOMDRAW handler for CCCListViewCtrl.
*--------------------------------------------------------------------------*/
LRESULT CCCListViewCtrl::OnCustomDraw (NMLVCUSTOMDRAW* plvcd)
{
ASSERT (CWnd::FromHandle (plvcd->nmcd.hdr.hwndFrom) == m_pListView);
return (m_FontLinker.OnCustomDraw (&plvcd->nmcd));
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::UseFontLinking
*
*
*--------------------------------------------------------------------------*/
bool CCCListViewCtrl::UseFontLinking () const
{
CAMCView* pAMCView = m_pListView->GetAMCView();
ASSERT (pAMCView != NULL);
DWORD dwListOptions = pAMCView->GetViewData()->GetListOptions();
return (dwListOptions & RVTI_LIST_OPTIONS_USEFONTLINKING);
}
/*+-------------------------------------------------------------------------*
* CListFontLinker::GetItemText
*
*
*--------------------------------------------------------------------------*/
std::wstring CListFontLinker::GetItemText (NMCUSTOMDRAW* pnmcd) const
{
NMLVCUSTOMDRAW* plvcd = reinterpret_cast<NMLVCUSTOMDRAW *>(pnmcd);
int iItem = pnmcd->dwItemSpec;
int iSubItem = (pnmcd->dwDrawStage & CDDS_SUBITEM) ? plvcd->iSubItem : 0;
CListCtrl& lc = m_pListCtrl->GetListViewPtr()->GetListCtrl();
USES_CONVERSION;
return (std::wstring (T2CW (lc.GetItemText (iItem, iSubItem))));
}
/*+-------------------------------------------------------------------------*
* CListFontLinker::IsAnyItemLocalizable
*
*
*--------------------------------------------------------------------------*/
bool CListFontLinker::IsAnyItemLocalizable () const
{
return (m_pListCtrl->UseFontLinking ());
}
//############################################################################
//############################################################################
class CMMCResultNode;
/*+-------------------------------------------------------------------------*
* class CNodes
*
*
* PURPOSE: base class for Nodes collections. Implements most of required methods
*
*+-------------------------------------------------------------------------*/
class CNodes :
public CMMCIDispatchImpl<Nodes>,
public CTiedComObject<CCCListViewCtrl>, // is tied to CCCListViewCtrl
public CTiedObject, // enumerators are tied to it
public CListViewObserver
{
protected:
typedef CCCListViewCtrl CMyTiedObject; // tied to CCCListViewCtrl
private:
// define collection type for cached Nodes
typedef std::pair<int /*index*/, CMMCResultNode * /*pNode*/> col_entry_t;
typedef std::vector<col_entry_t> col_t;
// define comparison functor for b-search in a collection
struct index_less : public std::binary_function<col_entry_t, int, bool>
{
bool operator()(const col_entry_t& x, const int& y) const { return (x.first < y); }
};
public:
BEGIN_MMC_COM_MAP(CNodes)
END_MMC_COM_MAP()
// returning self as tied object
// class implements enumerator methods itself, but it is used as a base
// we need to tell all node we are going down
virtual ~CNodes() { InvalidateConnectedNodes(); }
// Nodes interface
public:
// methods forwarded to the list control
STDMETHODIMP get_Count( PLONG pCount );
STDMETHODIMP Item( long Index, PPNODE ppNode );
public:
// observed events
virtual ::SC ScOnListViewIndexesReset();
virtual ::SC ScOnListViewItemInserted(int iIndex);
virtual ::SC ScOnListViewItemDeleted (int iIndex);
// Nodes enumeration impl
::SC ScEnumReset (int &pos);
::SC ScEnumNext (int &pos, PDISPATCH & pDispatch);
::SC ScEnumSkip (unsigned long celt, unsigned long& celtSkipped, int &pos);
// node object helpers
::SC ScGetDisplayName(int iItem, CComBSTR& bstrName);
::SC ScUnadviseNodeObj(CMMCResultNode *node); // called from ~CMMCResultNode()
// asked by ListControl [forwarded by Node] to check if Node belongs to it.
// false if unconnected, else tries to match the owner
bool IsTiedToThisList(CCCListViewCtrl *pvc);
// returns Node representing the item (may reuse/create/forward-to-scope-tree)
::SC ScGetNode (int iItem, PPNODE ppNode );
::SC ScGetListCtrl(CCCListViewCtrl **ppListCtrl); // returns the tied list control
private: // implementation helpers
::SC ScAdvancePosition( int& pos, unsigned nItems ); // returns S_FALSE if adv. less than req.
// initializators
void SetSelectedItemsOnly(bool bSelOnly) { m_bSelectedItemsOnly = bSelOnly; }
// breaks connection between Nodes and any Node object
void InvalidateConnectedNodes();
// data members
bool m_bSelectedItemsOnly;
col_t m_Nodes;
friend class CCCListViewCtrl;
};
// this typedefs the CNodesEnum class. Implements get__NewEnum using CMMCEnumerator and a _Positon object
typedef CMMCNewEnumImpl<CNodes, int, CNodes> CNodesEnum;
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
* class CMMCResultNode
*
*
* PURPOSE: Implements the Node automation interface, for a result node
*
*+-------------------------------------------------------------------------*/
class CMMCResultNode :
public CMMCIDispatchImpl<Node>
{
public:
BEGIN_MMC_COM_MAP(CMMCResultNode)
END_MMC_COM_MAP()
// Node methods
public:
STDMETHODIMP get_Name( PBSTR pbstrName);
STDMETHODIMP get_Property( BSTR PropertyName, PBSTR PropertyValue);
STDMETHODIMP get_Bookmark( PBSTR pbstrBookmark);
STDMETHODIMP IsScopeNode(PBOOL pbIsScopeNode);
STDMETHODIMP get_Nodetype(PBSTR Nodetype);
CMMCResultNode();
~CMMCResultNode();
SC ScGetListCtrl(CCCListViewCtrl **ppListCtrl);
int GetIndex() { return m_iIndex; }
// asked by ListControl to check if Node belongs to it. false if orphan,
// forwarded to Nodes else
bool IsTiedToThisList(CCCListViewCtrl *pvc) { return (m_pNodes != NULL && m_pNodes->IsTiedToThisList(pvc)); }
private: // implementation
::SC ScGetAMCView(CAMCView **ppAMCView);
void Invalidate() { m_iIndex = -1; m_pNodes = NULL; }
int m_iIndex;
CNodes *m_pNodes;
friend class CNodes;
};
//############################################################################
//############################################################################
//
// CCCListViewCtrl methods managing Node & Nodes objects
//
//############################################################################
//############################################################################
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScFindResultItem
*
* PURPOSE: finds the index in ListView for item identified by Node [helper]
*
* PARAMETERS:
* PNODE pNode - node to examine
* int &iItem - storage for resulting index
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScFindResultItem( PNODE pNode, int &iItem )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScSelect"));
// parameter check
sc = ScCheckPointers(pNode);
if (sc)
return sc;
iItem = -1;
// what type of node do we have
BOOL bScopeNode = FALSE;
sc = pNode->IsScopeNode(&bScopeNode);
if (sc)
return sc;
if (bScopeNode) // scope node
{
// we do not have scope items in virtual lists
if (IsVirtual())
return sc = ScFromMMC(MMC_E_RESULT_ITEM_NOT_FOUND);
// find the result item (with the help of the owner class)
// check for view
sc = ScCheckPointers( m_pListView, E_UNEXPECTED);
if (sc)
return sc;
// get AMCView
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers( pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// forward the request
HRESULTITEM itm;
sc = pAMCView->ScFindResultItemForScopeNode( pNode, itm );
if (sc)
return sc;
// get the index of item
iItem = ResultItemToIndex(CResultItem::FromHandle(itm));
if (iItem < 0)
return sc = E_UNEXPECTED; // shouldn't be so
}
else // result node
{
// convert the pointer
CMMCResultNode *pResNode = dynamic_cast<CMMCResultNode *>(pNode);
sc = ScCheckPointers(pResNode); // invalid param. isn't it ?
if (sc)
return sc;
// now check if it's actually comming from this list
if (!pResNode->IsTiedToThisList(this))
return sc = E_INVALIDARG;
iItem = pResNode->GetIndex();
}
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScSelect
*
* PURPOSE: selects item identified by node [implements View.Select()]
*
* PARAMETERS:
* PNODE pNode - node to select
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScSelect( PNODE pNode )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScSelect"));
// parameter check
sc = ScCheckPointers(pNode);
if (sc)
return sc;
// find the result item
int nIdxToSelect = -1;
sc = ScFindResultItem( pNode, nIdxToSelect );
if (sc)
return sc;
// perform the action on list control
// NOTE: do not use GetListCtrl().SetItemState - it uses SetItem which is not supported for virtual lists
LV_ITEM lvi;
lvi.stateMask = lvi.state = LVIS_SELECTED;
if (!GetListCtrl().SendMessage( LVM_SETITEMSTATE, WPARAM(nIdxToSelect), (LPARAM)(LV_ITEM FAR *)&lvi))
return sc = E_FAIL;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScDeselect
*
* PURPOSE: deselects item identified by node [implements View.Deselect()]
*
* PARAMETERS:
* PNODE pNode - node to deselect
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScDeselect( PNODE pNode)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScDeselect"));
// parameter check
sc = ScCheckPointers(pNode);
if (sc)
return sc;
// find the result item
int nIdxToSelect = -1;
sc = ScFindResultItem( pNode, nIdxToSelect );
if (sc)
return sc;
// perform the action on list control
// NOTE: do not use GetListCtrl().SetItemState - it uses SetItem which is not supported for virtual lists
LV_ITEM lvi;
lvi.stateMask = LVIS_SELECTED;
lvi.state = 0;
if (!GetListCtrl().SendMessage( LVM_SETITEMSTATE, WPARAM(nIdxToSelect), (LPARAM)(LV_ITEM FAR *)&lvi))
return sc = E_FAIL;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScIsSelected
*
* PURPOSE: checks the status of item identified by node [implements View.IsSelected]
*
* PARAMETERS:
* PNODE pNode - node to examine
* PBOOL pIsSelected - storage for result
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScIsSelected( PNODE pNode, PBOOL pIsSelected)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScIsSelected"));
// parameter check
sc = ScCheckPointers(pNode, pIsSelected);
if (sc)
return sc;
*pIsSelected = FALSE;
// find the result item
int nIdxToSelect = -1;
sc = ScFindResultItem( pNode, nIdxToSelect );
if (sc)
return sc;
// perform the action on list control
if ( 0 != (GetListCtrl().GetItemState( nIdxToSelect, LVIS_SELECTED ) & LVIS_SELECTED ))
*pIsSelected = TRUE;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_ListItems
*
* PURPOSE: returns Nodes enumeration including all list items
*
* PARAMETERS:
* PPNODES ppNodes - storage for result
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_ListItems( PPNODES ppNodes )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_ListItems"));
// get proper enumeration
const bool bSelectedItemsOnly = false;
sc = ScGetNodesEnum(bSelectedItemsOnly, ppNodes);
if (sc)
return sc;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_SelectedItems
*
* PURPOSE: returns Nodes enumeration including selected list items
*
* PARAMETERS:
* PPNODES ppNodes - storage for result
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_SelectedItems( PPNODES ppNodes)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_SelectedItems"));
// get proper enumeration
const bool bSelectedItemsOnly = true;
sc = ScGetNodesEnum(bSelectedItemsOnly, ppNodes);
if (sc)
return sc;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScValidateItem
*
* PURPOSE: helper function inspecting the index validity and node type
*
* PARAMETERS:
* int iItem - item to inspect
* bool &bScopeNode - result: is it a scope node
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScValidateItem( int iItem, bool &bScopeNode )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScValidateItem"));
// check the index
if (iItem < 0 || iItem >= GetListCtrl().GetItemCount())
return sc = E_INVALIDARG;
bScopeNode = false; // its always false for virtual lists
if (!IsVirtual())
{
// now try to guess what kind of result item we have
CResultItem* pri = IndexToResultItem (iItem);
sc = ScCheckPointers(pri, E_UNEXPECTED);
if (sc)
return sc;
bScopeNode = pri->IsScopeItem();
}
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScGetNodesEnum
*
* PURPOSE: returns [creates if needed] com object Nodes
*
* PARAMETERS:
* EnumType enumType - type of enumeration requested [all items/selected items]
* PPNODES ppNodes - storage for the result (addref'ed for the caller)
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScGetNodesEnum(bool bSelectedItemsOnly, PPNODES ppNodes)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScGetNodesEnum"));
// parameter check
sc = ScCheckPointers ( ppNodes );
if (sc)
return sc;
// result initialization
*ppNodes = NULL;
// get a reference to proper variable
NodesPtr& rspNodes = bSelectedItemsOnly ? m_spSelNodes : m_spAllNodes;
if (rspNodes == NULL) // don't we have it ready?
{
// create a CNodesEnum object
sc = CTiedComObjectCreator<CNodesEnum>::ScCreateAndConnect(*this, rspNodes);
if (sc)
return (sc);
// get the actual object
typedef CComObject<CNodesEnum> CNodesEnumObj;
CNodesEnumObj *pNodesEnum = dynamic_cast<CNodesEnumObj*>(rspNodes.GetInterfacePtr());
// check the pointer
sc = ScCheckPointers( pNodesEnum, E_UNEXPECTED );
if(sc)
return sc;
// update internal data
pNodesEnum->SetSelectedItemsOnly(bSelectedItemsOnly);
// add new object as an observer to the view
AddObserver(static_cast<CListViewObserver&>(*pNodesEnum));
}
// addref and return the pointer for the client.
*ppNodes = rspNodes;
(*ppNodes)->AddRef();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScGetScopeNodeForItem
*
* PURPOSE: transit point for Scope Node request - comes from enumeration, forwarded
* to AMCView and further to ScopeTree
*
* PARAMETERS:
* int iItem - node index to retrieve
* PPNODE ppNode - result storage
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScGetScopeNodeForItem( int iItem, PPNODE ppNode )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScGetScopeNodeForItem"));
// check the parameters
sc = ScCheckPointers(ppNode);
if (sc)
return sc;
// initialize the result
*ppNode = NULL;
// now try to guess what kind of result item we have
CResultItem* pri = IndexToResultItem(iItem);
sc = ScCheckPointers(pri, E_UNEXPECTED);
if (sc)
return sc;
// get the hNode
HNODE hNode = pri->GetScopeNode();
// check for view
sc = ScCheckPointers( m_pListView, E_UNEXPECTED);
if (sc)
return sc;
// get AMCView
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers( pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// forward the request
sc = pAMCView->ScGetScopeNode( hNode, ppNode );
if (sc)
return sc;
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CCCListViewCtrl::ScGetAMCView
*
* PURPOSE: Returns a pointer to the parent CAMCView
*
* PARAMETERS:
* CAMCView ** ppAMCView :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CCCListViewCtrl::ScGetAMCView(CAMCView **ppAMCView)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScGetAMCView"));
sc = ScCheckPointers(ppAMCView);
if(sc)
return sc;
*ppAMCView = NULL;
// check for view
sc = ScCheckPointers( m_pListView, E_UNEXPECTED);
if (sc)
return sc;
// get AMCView
*ppAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers(*ppAMCView, E_UNEXPECTED);
return sc;
}
//############################################################################
//############################################################################
//
// Implementation of class CNodes
//
//############################################################################
//############################################################################
/***************************************************************************\
*
* METHOD: CNodes::ScEnumReset
*
* PURPOSE: resets position for Nodes enumeration
*
* PARAMETERS:
* int &pos - position to reset
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScEnumReset(int &pos)
{
DECLARE_SC(sc, TEXT("CNodes::ScEnumReset"));
pos = -1;
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CNodes::ScGetListCtrl
*
* PURPOSE: Returns a pointer to the list control
*
* GUARANTEE: if the function succeeds, the list control pointer is valid.
*
* PARAMETERS:
* CCCListViewCtrl ** ppListCtrl :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CNodes::ScGetListCtrl(CCCListViewCtrl **ppListCtrl)
{
DECLARE_SC(sc, TEXT("CNodes::ScGetListCtrl"));
sc = ScCheckPointers(ppListCtrl, E_UNEXPECTED);
if(sc)
return sc;
*ppListCtrl = NULL;
sc = ScGetTiedObject(*ppListCtrl);
if(sc)
return sc;
// recheck the pointer
sc = ScCheckPointers(*ppListCtrl, E_UNEXPECTED);
if(sc)
return sc;
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScAdvancePosition
*
* PURPOSE: advances position (index) of item depending on collection type
*
* PARAMETERS:
* int& pos - position to update
* int nItems - count of items to skip
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScAdvancePosition( int& pos, unsigned nItems )
{
DECLARE_SC(sc, TEXT("CNodes::ScAdvancePosition"));
// get the tied object
CCCListViewCtrl *pListCtrl = NULL;
sc = ScGetTiedObject(pListCtrl);
if(sc)
return sc;
// recheck the pointer
sc = ScCheckPointers(pListCtrl, E_UNEXPECTED);
if(sc)
return sc;
// inspect if we aren't behind the end
int nCount = pListCtrl->GetListCtrl().GetItemCount();
if (pos >= nCount)
return sc = E_FAIL; // we did not got a valid position
// advance depending on collection type
if (m_bSelectedItemsOnly)
{
// we only can do it by iterating
for (int i = 0; i < nItems; i++)
{
int iItem = pListCtrl->GetListCtrl().GetNextItem( pos, LVNI_SELECTED );
if (iItem < 0)
return sc = S_FALSE; // we didn't advance as much as requested
pos = iItem;
}
}
else // all_items selection
{
pos += nItems;
if (pos >= nCount)
{
pos = nCount - 1;
return sc = S_FALSE; // we didn't advance as much as requested
}
}
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScEnumNext
*
* PURPOSE: Retrieves next item from enumeration [ Implements Nodes.Next ]
*
* PARAMETERS:
* int &pos - position to start from
* IDispatch * &pDispatch - resulting item
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScEnumNext(int &pos, IDispatch * &pDispatch)
{
DECLARE_SC(sc, TEXT("CNodes::ScEnumNext"));
// get the index of next item
sc = ScAdvancePosition( pos, 1 /*nItems*/ );
if (sc.IsError() || sc == SC(S_FALSE))
return sc;
// get the result node for the index
PNODE pNode = NULL;
sc = ScGetNode( pos, &pNode );
if (sc)
return sc;
// assign to result
pDispatch = pNode;
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScEnumSkip
*
* PURPOSE: skips items in enumeration [implements Nodes.Skip method]
*
* PARAMETERS:
* unsigned long celt - positions to skip
* unsigned long &celtSkipped - result: positions skiped
* int &pos - position to update
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScEnumSkip(unsigned long celt,unsigned long &celtSkipped, int &pos)
{
DECLARE_SC(sc, TEXT("CNodes::ScEnumSkip"));
// init val.
celtSkipped = 0;
// save the position for evaluation
int org_pos = pos;
// advance the position
sc = ScAdvancePosition( pos, celt );
if (sc)
return sc;
// calculate items skipped
celtSkipped = pos - org_pos;
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::get_Count
*
* PURPOSE: returns count of object in enumeration [Implements Nodes.Count]
*
* PARAMETERS:
* PLONG pCount - storage for result
*
* RETURNS:
* HRESULT - result code
*
\***************************************************************************/
STDMETHODIMP CNodes::get_Count( PLONG pCount )
{
DECLARE_SC(sc, TEXT("CNodes::get_Count"));
// parameter check
sc = ScCheckPointers ( pCount );
if (sc)
return sc.ToHr();
// get the tied object
CCCListViewCtrl *pListCtrl = NULL;
sc = ScGetTiedObject(pListCtrl);
if(sc)
return sc.ToHr();
// recheck the pointer
sc = ScCheckPointers(pListCtrl, E_UNEXPECTED);
if(sc)
return sc.ToHr();
// get count from the control
if (m_bSelectedItemsOnly)
*pCount = pListCtrl->GetListCtrl().GetSelectedCount();
else
*pCount = pListCtrl->GetListCtrl().GetItemCount();
return sc.ToHr();
}
/***************************************************************************\
*
* METHOD: CNodes::Item
*
* PURPOSE: - returns Item from enumeration [Implements Nodes.Item]
*
* PARAMETERS:
* long Index - Index of item to retrieve
* PPNODE ppNode - storage for resulting node ptr
*
* RETURNS:
* HRESULT - result code
*
\***************************************************************************/
STDMETHODIMP CNodes::Item( long Index, PPNODE ppNode )
{
DECLARE_SC(sc, TEXT("CNodes::Item"));
// parameter check
sc = ScCheckPointers ( ppNode );
if (sc)
return sc.ToHr();
*ppNode = NULL;
// check the index
if (Index <= 0)
{
sc = E_INVALIDARG;
return sc.ToHr();
}
int iPos = -1; // just before the first item at start
// get to the right item
sc = ScAdvancePosition(iPos, Index);
if (sc == SC(S_FALSE)) // didn't get far enough?
sc = E_INVALIDARG; // means we've got the wrong Index
if (sc) // got an error?
return sc.ToHr();
// now get the node
sc = ScGetNode( iPos, ppNode );
if (sc)
return sc.ToHr();
return sc.ToHr();
}
/***************************************************************************\
*
* METHOD: CNodes::ScGetNode
*
* PURPOSE: Retrieves cached or creates new Node object
*
* PARAMETERS:
* int iIndex - index of result item
* PPNODE ppNode - storage for resulting node
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScGetNode(int iItem, PPNODE ppNode)
{
DECLARE_SC(sc, TEXT("CNodes::ScGetNode"));
// parameter check
sc = ScCheckPointers(ppNode);
if (sc)
return sc;
// initial return value
*ppNode = NULL;
// get the tied object
CCCListViewCtrl *pListCtrl = NULL;
sc = ScGetTiedObject(pListCtrl);
if(sc)
return sc;
// recheck the pointer
sc = ScCheckPointers(pListCtrl, E_UNEXPECTED);
if(sc)
return sc;
// inspect what kind of item we have
bool bScopeNode;
sc = pListCtrl->ScValidateItem( iItem, bScopeNode );
if (sc)
return sc;
if (bScopeNode)
{
// do not handle this - pass to the owner
sc = pListCtrl->ScGetScopeNodeForItem ( iItem, ppNode );
if (sc)
return sc;
return sc;
}
// "normal" list item (not a scope node) - we can handle ourselves
// find either the entry or the place to insert
col_t::iterator it = std::lower_bound(m_Nodes.begin(), m_Nodes.end(), iItem, index_less());
if (it!= m_Nodes.end() && it->first == iItem)
{
// we already have it!!! , recheck!
sc = ScCheckPointers(it->second, E_UNEXPECTED);
if (sc)
return sc;
// return it!
*ppNode = it->second;
(*ppNode)->AddRef();
return sc;
}
// doesn't exist - need to create one
typedef CComObject<CMMCResultNode> CResultNode;
CResultNode *pResultNode = NULL;
CResultNode::CreateInstance(&pResultNode);
sc = ScCheckPointers(pResultNode, E_OUTOFMEMORY);
if(sc)
return sc;
// update the information on the node
pResultNode->m_iIndex = iItem;
pResultNode->m_pNodes = this;
m_Nodes.insert(it, col_entry_t(iItem, pResultNode));
// return it!
*ppNode = pResultNode;
(*ppNode)->AddRef();
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScGetDisplayName
*
* PURPOSE: retrievs the display name of the Node
*
* PARAMETERS:
* int iIndex - index of the result item
* CComBSTR& bstrName - storage for resulting text
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScGetDisplayName(int iItem, CComBSTR& bstrName)
{
DECLARE_SC(sc, TEXT("CNodes::ScGetDisplayName"));
// get the tied object
CCCListViewCtrl *pListCtrl = NULL;
sc = ScGetTiedObject(pListCtrl);
if(sc)
return sc;
// recheck the pointer
sc = ScCheckPointers(pListCtrl, E_UNEXPECTED);
if(sc)
return sc;
// check the index
if (iItem < 0 || iItem >= pListCtrl->GetListCtrl().GetItemCount())
return sc = E_INVALIDARG;
// get the text
bstrName = pListCtrl->GetListCtrl().GetItemText( iItem, 0 /*column*/);
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScUnadviseNodeObj
*
* PURPOSE: Remove Node from collection. called from ~CMMCResultNode()
*
* PARAMETERS:
* CMMCResultNode *node - node to remove
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScUnadviseNodeObj(CMMCResultNode *node)
{
DECLARE_SC(sc, TEXT("CNodes::ScUnadviseNodeObj"));
// parameter check
sc = ScCheckPointers(node);
if (sc)
return sc;
// index of the node
int iIndex = node->m_iIndex;
// find the entry by index
col_t::iterator it = std::lower_bound(m_Nodes.begin(), m_Nodes.end(), iIndex, index_less());
if (it== m_Nodes.end() || it->first != iIndex)
{
// we do not have it ???
sc = E_UNEXPECTED;
return sc;
}
// remove from collection
m_Nodes.erase(it);
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScOnListViewIndexesReset
*
* PURPOSE: Fired Event handler. Wipes the cached info
*
* PARAMETERS:
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScOnListViewIndexesReset()
{
DECLARE_SC(sc, TEXT("CNodes::ScOnListViewIndexesReset"));
InvalidateConnectedNodes();
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::InvalidateConnectedNodes
*
* PURPOSE: wipes out the cache. invalidates alive nodes
*
* PARAMETERS:
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
void CNodes::InvalidateConnectedNodes()
{
DECLARE_SC(sc, TEXT("CNodes::ScOnListViewIndexesReset"));
// orphan all alive nodes - we do not keep pointers
col_t::iterator it;
for (it = m_Nodes.begin(); it != m_Nodes.end(); ++it)
{
// get the pointer to com object
CMMCResultNode * const pNode = it->second;
sc = ScCheckPointers(pNode, E_POINTER);
if (sc)
{
sc.TraceAndClear();
continue;
}
// reset the container info
pNode->Invalidate();
}
// clear the collection;
m_Nodes.clear();
}
/***************************************************************************\
*
* METHOD: CNodes::ScOnListViewItemInserted
*
* PURPOSE: Fired Event handler. shifts index info
*
* PARAMETERS:
* int iIndex - index of newly inserted item
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScOnListViewItemInserted(int iIndex)
{
DECLARE_SC(sc, TEXT("CNodes::ScOnListViewItemInserted"));
// find the insertion point
col_t::iterator it = std::lower_bound(m_Nodes.begin(), m_Nodes.end(), iIndex, index_less());
// increment all the entries following it
while (it != m_Nodes.end())
{
// increment index in own collection
++(it->first);
// get the pointer to com object
CMMCResultNode * const pNode = it->second;
sc = ScCheckPointers(pNode, E_UNEXPECTED);
if (sc)
return sc;
// increment member on com object
++(pNode->m_iIndex);
// get to the next item
++it;
}
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::ScOnListViewItemDeleted
*
* PURPOSE: Fired Event handler. shifts index info
*
* PARAMETERS:
* int iIndex - index of removed item
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CNodes::ScOnListViewItemDeleted (int iIndex)
{
DECLARE_SC(sc, TEXT("CNodes::ScOnlistViewItemDeleted"));
// find the insertion point
col_t::iterator it = std::lower_bound(m_Nodes.begin(), m_Nodes.end(), iIndex, index_less());
// if we have an object at the position, get rid of it!
if (it != m_Nodes.end() && it->first == iIndex)
{
// get pointer to the object
CMMCResultNode * const pNode = it->second;
sc = ScCheckPointers(pNode, E_UNEXPECTED);
if (sc)
return sc;
// reset the container info
pNode->Invalidate();
it = m_Nodes.erase(it);
}
// decrement all the entries following it
while (it != m_Nodes.end())
{
// decrement index in own collection
--(it->first);
// get the pointer to com object
CMMCResultNode * const pNode = it->second;
sc = ScCheckPointers(pNode, E_UNEXPECTED);
if (sc)
return sc;
// decrement member on com object
--(pNode->m_iIndex);
// get to the next item
++it;
}
return sc;
}
/***************************************************************************\
*
* METHOD: CNodes::IsTiedToThisList
*
* PURPOSE: this method is called from node which needs to know if it
* belongs to the the this list (as a result item on it)
*
* PARAMETERS:
* CCCListViewCtrl *pvc
*
* RETURNS:
* false if unconnected or belongs to other list
*
\***************************************************************************/
bool CNodes::IsTiedToThisList(CCCListViewCtrl *pvc)
{
DECLARE_SC(sc, TEXT("CNodes::IsTiedToThisList"));
// get the tied object
CCCListViewCtrl *pListCtrl = NULL;
sc = ScGetTiedObject(pListCtrl);
if(sc)
return false;
// recheck the pointer
sc = ScCheckPointers(pListCtrl, E_UNEXPECTED);
if(sc)
return false;
return (pListCtrl == pvc);
}
//############################################################################
//############################################################################
//
// Implementation of class CMMCResultNode
//
//############################################################################
//############################################################################
/***************************************************************************\
*
* METHOD: CMMCResultNode::CMMCResultNode
*
* PURPOSE: default constructor
*
* PARAMETERS:
*
\***************************************************************************/
CMMCResultNode::CMMCResultNode() : m_pNodes(NULL), m_iIndex(-1)
{
DECLARE_SC(sc, TEXT("CMMCResultNode::CMMCResultNode"));
Invalidate();
}
/***************************************************************************\
*
* METHOD: CMMCResultNode::~CMMCResultNode
*
* PURPOSE: Destructor
*
* PARAMETERS:
*
\***************************************************************************/
CMMCResultNode::~CMMCResultNode()
{
DECLARE_SC(sc, TEXT("CMMCResultNode::~CMMCResultNode"));
// informing container about desruction
if (m_pNodes)
{
sc = m_pNodes->ScUnadviseNodeObj(this);
if (sc)
sc.TraceAndClear();
}
}
/***************************************************************************\
*
* METHOD: CMMCResultNode::get_Name
*
* PURPOSE: Returns the display name of the node.
*
* PARAMETERS:
* PBSTR pbstrName - result (name)
*
* RETURNS:
* HRESULT - result code
*
\***************************************************************************/
STDMETHODIMP
CMMCResultNode::get_Name( PBSTR pbstrName)
{
DECLARE_SC(sc, TEXT("CMMCResultNode::get_Name"));
// check the parameters
sc = ScCheckPointers( pbstrName );
if (sc)
return sc.ToHr();
// initialize output
*pbstrName = NULL;
// check the container
sc = ScCheckPointers( m_pNodes, E_FAIL );
if (sc)
return sc.ToHr();
// ask owner about the name
CComBSTR bstrResult;
sc = m_pNodes->ScGetDisplayName(m_iIndex, bstrResult);
if (sc)
return sc.ToHr();
// return the result result
*pbstrName = bstrResult.Detach();
// recheck pointer before return
sc = ScCheckPointers( *pbstrName, E_UNEXPECTED );
if (sc)
return sc.ToHr();
return sc.ToHr();
}
/*+-------------------------------------------------------------------------*
*
* CMMCResultNode::ScGetAMCView
*
* PURPOSE: Returns a pointer to the view.
*
* PARAMETERS:
* CAMCView ** ppAMCView :
*
* RETURNS:
* SC
*
*+-------------------------------------------------------------------------*/
SC
CMMCResultNode::ScGetAMCView(CAMCView **ppAMCView)
{
DECLARE_SC(sc, TEXT("CMMCResultNode::ScGetAMCView"));
// check parameters
sc = ScCheckPointers(ppAMCView);
if(sc)
return sc;
// init out parameter
*ppAMCView = NULL;
// check the container
sc = ScCheckPointers( m_pNodes, E_FAIL );
if (sc)
return sc;
CCCListViewCtrl *pListCtrl = NULL;
sc = m_pNodes->ScGetListCtrl(& pListCtrl);
if(sc)
return sc;
sc = ScCheckPointers(pListCtrl, E_UNEXPECTED);
if(sc)
return sc;
sc = pListCtrl->ScGetAMCView(ppAMCView);
return sc;
}
/*+-------------------------------------------------------------------------*
*
* CMMCResultNode::get_Property
*
* PURPOSE: Returns the specified clipboard format data (must be a text
* format) for the node.
*
* PARAMETERS:
* BSTR PropertyName :
* PBSTR PropertyValue :
*
* RETURNS:
* STDMETHODIMP
*
*+-------------------------------------------------------------------------*/
STDMETHODIMP
CMMCResultNode::get_Property( BSTR PropertyName, PBSTR PropertyValue)
{
DECLARE_SC(sc, TEXT("CMMCResultNode::get_Property"));
CAMCView *pAMCView = NULL;
sc = ScGetAMCView(&pAMCView);
if(sc)
return sc.ToHr();
sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
if(sc)
return sc.ToHr();
sc = pAMCView->ScGetProperty(m_iIndex, PropertyName, PropertyValue);
return sc.ToHr();
}
/*+-------------------------------------------------------------------------*
*
* CMMCResultNode::get_Nodetype
*
* PURPOSE: Returns the GUID nodetype identifier for the node.
*
* PARAMETERS:
* PBSTR Nodetype : [out] the nodetype identifier.
*
* RETURNS:
* STDMETHODIMP
*
*+-------------------------------------------------------------------------*/
STDMETHODIMP
CMMCResultNode::get_Nodetype(PBSTR Nodetype)
{
DECLARE_SC(sc, TEXT("CMMCResultNode::get_Nodetype"));
CAMCView *pAMCView = NULL;
sc = ScGetAMCView(&pAMCView);
if(sc)
return sc.ToHr();
sc = ScCheckPointers(pAMCView, E_UNEXPECTED);
if(sc)
return sc.ToHr();
sc = pAMCView->ScGetNodetype(m_iIndex, Nodetype);
return sc.ToHr();
}
/***************************************************************************\
*
* METHOD: CMMCResultNode::get_Bookmark
*
* PURPOSE: Returns error always - not valid for result items
*
* PARAMETERS:
* PBSTR pbstrBookmark
*
* RETURNS:
* HRESULT - result code
*
\***************************************************************************/
STDMETHODIMP
CMMCResultNode::get_Bookmark( PBSTR pbstrBookmark )
{
DECLARE_SC(sc, TEXT("CMMCResultNode::get_Bookmark"));
// check the parameters
sc = ScCheckPointers( pbstrBookmark );
if (sc)
return sc.ToHr();
// initialize output
*pbstrBookmark = NULL;
// report the error - always
sc = ScFromMMC( MMC_E_NO_BOOKMARK );
return sc.ToHr();
}
/***************************************************************************\
*
* METHOD: CMMCResultNode::IsScopeNode
*
* PURPOSE: returns TRUE if it's a scope node ( i.e. always returns FALSE)
*
* PARAMETERS:
* PBOOL pbIsScopeNode
*
* RETURNS:
* HRESULT - result code
*
\***************************************************************************/
STDMETHODIMP
CMMCResultNode::IsScopeNode(PBOOL pbIsScopeNode)
{
DECLARE_SC(sc, TEXT("CMMCResultNode::IsScopeNode"));
sc = ScCheckPointers( pbIsScopeNode );
if(sc)
return sc.ToHr();
// if it's here it's for sure not a scope node
*pbIsScopeNode = FALSE;
return sc.ToHr();
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::ScAllocResultItem
*
* Allocates a CResultItem for a list control item.
*--------------------------------------------------------------------------*/
SC CCCListViewCtrl::ScAllocResultItem (
CResultItem*& pri, /* O:new list item */
COMPONENTID id, /* I:owning component ID */
LPARAM lSnapinData, /* I:snap-in's data for this item */
int nImage) /* I:image index */
{
DECLARE_SC (sc, _T("CCCListViewCtrl::ScAllocResultItem"));
pri = new CResultItem (id, lSnapinData, nImage);
sc = ScCheckPointers (pri, E_OUTOFMEMORY);
if (sc)
return (sc);
return (sc);
}
/*+-------------------------------------------------------------------------*
* CCCListViewCtrl::ScFreeResultItem
*
* Frees a CResultItem that was allocated with ScAllocResultItem.
*--------------------------------------------------------------------------*/
SC CCCListViewCtrl::ScFreeResultItem (
CResultItem* priFree) /* I:list item to free */
{
DECLARE_SC (sc, _T("CCCListViewCtrl::ScFreeResultItem"));
sc = ScCheckPointers (priFree, E_UNEXPECTED);
if (sc)
return (sc);
delete priFree;
return (sc);
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_Columns
*
* PURPOSE: returns pointer to Columns object; create one if required
*
* PARAMETERS:
* PPCOLUMNS ppColumns - resulting pointer
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_Columns(PPCOLUMNS ppColumns)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_Columns"));
// Check received parameters
sc = ScCheckPointers(ppColumns);
if (sc)
return sc;
// initialize
*ppColumns = NULL;
// create the object when required
sc = CTiedComObjectCreator<CColumns>::ScCreateAndConnect(*this, m_spColumns);
if (sc)
return sc;
// recheck the pointer
sc = ScCheckPointers(m_spColumns, E_UNEXPECTED);
if (sc)
return sc;
// return the pointer
*ppColumns = m_spColumns;
(*ppColumns)->AddRef();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_Count
*
* PURPOSE: returns the count of columns in LV
*
* PARAMETERS:
* PLONG pCount
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_Count( PLONG pCount )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_Count"));
// Check received parameters
sc = ScCheckPointers(pCount);
if (sc)
return sc;
// return the result
*pCount = GetColCount();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScEnumNext
*
* PURPOSE: advances position pointer for Columns enum. returns the object at the pos
*
* PARAMETERS:
* int &iZeroBasedPos - position to modify
* PDISPATCH & pDispatch - resulting pointer
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScEnumNext(int &iZeroBasedPos, PDISPATCH & pDispatch)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScEnumNext"));
// initialize
pDispatch = NULL;
// advance;
iZeroBasedPos++;
// recheck the pos
if (iZeroBasedPos < 0)
return sc = E_UNEXPECTED;
// no more columns?
if (iZeroBasedPos >= GetColCount())
return sc = S_FALSE;
// retrieve the column
ColumnPtr spColumn;
// ScItem accepts 1-based index
sc = ScItem( iZeroBasedPos + 1, &spColumn );
if (sc)
return sc;
//return the interface.
pDispatch = spColumn.Detach();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScEnumSkip
*
* PURPOSE: skips several items for Columns enum
*
* PARAMETERS:
* unsigned long celt
* unsigned long& celtSkipped
* int &iZeroBasedPos
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScEnumSkip(unsigned long celt, unsigned long& celtSkipped, int &iZeroBasedPos)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScEnumSkip"));
// init [out] param
celtSkipped = 0;
// recheck the pos
if (iZeroBasedPos < -1)
return sc = E_UNEXPECTED;
// already past the end?
if (iZeroBasedPos >= GetColCount())
return sc = S_FALSE;
// how far can we advance ?;
celtSkipped = GetColCount() - iZeroBasedPos;
if (celtSkipped > celt)
celtSkipped = celt;
// advance
iZeroBasedPos += celtSkipped;
return sc = ((celtSkipped == celt) ? S_OK : S_FALSE);
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScEnumReset
*
* PURPOSE: resets position index for Columns enum
*
* PARAMETERS:
* int &iZeroBasedPos position to modify
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScEnumReset(int &iZeroBasedPos)
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScEnumReset"));
iZeroBasedPos = -1;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScItem
*
* PURPOSE: returns Column com object from Columns collection
*
* PARAMETERS:
* long lOneBasedIndex - [in] - column index (1 based)
* PPCOLUMN ppColumn - [out] - resulting object
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScItem( long lOneBasedIndex, PPCOLUMN ppColumn )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScItem"));
// Check received parameters
sc = ScCheckPointers(ppColumn);
if (sc)
return sc;
// initialize output
*ppColumn = NULL;
// check the index
int iZeroBasedIndex = lOneBasedIndex - 1;
if (iZeroBasedIndex < 0 || iZeroBasedIndex >= GetColCount())
return sc = ::ScFromMMC(MMC_E_INVALID_COLUMN_INDEX);
// construct the object
typedef CComObject<CColumn> CComColumn;
ColumnPtr /*CComPtr<CComColumn>*/ spColumn;
// create the object when required
sc = CTiedComObjectCreator<CColumn>::ScCreateAndConnect(*this, spColumn);
if (sc)
return sc;
// get 'raw' pointer
CComColumn *pColumn = dynamic_cast<CComColumn *>(spColumn.GetInterfacePtr());
// recheck the pointer
sc = ScCheckPointers(pColumn, E_UNEXPECTED);
if (sc)
return sc;
// initialize the object
pColumn->SetIndex(iZeroBasedIndex);
// let it observe what's going on and manage its own index
AddObserver(static_cast<CListViewObserver&>(*pColumn));
// return the pointer
*ppColumn = spColumn;
(*ppColumn)->AddRef();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScName
*
* PURPOSE: Returns column name. Implements Column.Name property
*
* PARAMETERS:
* BSTR *Name - the returned name
* int iZeroBasedColIndex - index of column of interest
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScName( /*[out, retval]*/ BSTR *Name, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScName"));
// Check received parameters
sc = ScCheckPointers(Name);
if (sc)
return sc;
// initialize output
*Name = NULL;
// recheck the column index
// (it's not something the script sent - it's internal data)
if (iZeroBasedColIndex < 0 || iZeroBasedColIndex >= GetColCount())
return sc = E_UNEXPECTED;
LPOLESTR pstrName = NULL;
sc = GetColumn(iZeroBasedColIndex, &pstrName, MMCLV_NOPTR, MMCLV_NOPTR);
if (sc)
return sc;
// recheck the pointer
sc = ScCheckPointers(pstrName, E_UNEXPECTED);
if (sc)
return sc;
*Name = ::SysAllocString(pstrName);
::CoTaskMemFree(pstrName);
// recheck the result
sc = ScCheckPointers(*Name, E_OUTOFMEMORY);
if (sc)
return sc;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScGetColumnData
*
* PURPOSE: helper for Column com object implementation
* - checks column index
* - retrieves information about the column
*
* PARAMETERS:
* int iZeroBasedColIndex - column index
* ColumnData *pColData - storage for resulting data
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScGetColumnData( int iZeroBasedColIndex, ColumnData *pColData )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScGetColumnData"));
// Check received parameters (its internal function, so it's unexpected if the params are bad)
sc = ScCheckPointers(pColData, E_UNEXPECTED);
if (sc)
return sc;
// init the structure;
pColData->Init();
// recheck the column index
// (it's not something the script sent - it's internal data)
if (iZeroBasedColIndex < 0 || iZeroBasedColIndex >= GetColCount())
return sc = E_UNEXPECTED;
// need to get the header control for managing columns
CAMCHeaderCtrl* pHeader = GetHeaderCtrl();
// check!
sc = ScCheckPointers(pHeader, E_UNEXPECTED);
if (sc)
return sc;
// get the lParam
HDITEM hdItem;
::ZeroMemory(&hdItem, sizeof(hdItem));
hdItem.mask = HDI_LPARAM | HDI_WIDTH | HDI_ORDER;
BOOL bRet = pHeader->GetItem(iZeroBasedColIndex, &hdItem);
if (!bRet)
return sc = E_FAIL;
CHiddenColumnInfo hci (hdItem.lParam);
pColData->iColumnOrder = hdItem.iOrder;
pColData->bIsHidden = hci.fHidden;
pColData->iColumnWidth = hdItem.cxy;
if (pColData->bIsHidden) // special case for hidden columns
pColData->iColumnWidth = hci.cx;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScSetColumnData
*
* PURPOSE: helper for Column com object implementation
* - modifies column
* NOTE - not to be used for SHOW/HIDE operations
*
* PARAMETERS:
* int iZeroBasedColIndex - column index
* const ColumnData& rColData - modified column data
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScSetColumnData( int iZeroBasedColIndex, const ColumnData& rColData )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScSetColumnData"));
ColumnData oldColData;
sc = ScGetColumnData( iZeroBasedColIndex, &oldColData );
if (sc)
return sc;
// check if anything has changed
if (oldColData == rColData)
return sc;
// the visibility of column cannot be changed directly
// the snapin has be notified about that.
// the request should never come here
if (rColData.bIsHidden != oldColData.bIsHidden)
return sc = E_UNEXPECTED;
// need to get the header control for managing columns
CAMCHeaderCtrl* pHeader = GetHeaderCtrl();
// check!
sc = ScCheckPointers(pHeader, E_UNEXPECTED);
if (sc)
return sc;
// now set the new data
HDITEM hdItem;
::ZeroMemory(&hdItem, sizeof(hdItem));
// set the width properly (no mater if colum is hidden)
if (rColData.bIsHidden)
{
CHiddenColumnInfo hci (rColData.iColumnWidth, true);
hdItem.lParam = hci.lParam;
hdItem.mask = HDI_LPARAM;
}
else
{
hdItem.mask = HDI_WIDTH;
hdItem.cxy = rColData.iColumnWidth;
}
// set the order info
hdItem.mask |= HDI_ORDER;
hdItem.iOrder = rColData.iColumnOrder;
// set the column data
BOOL bRet = pHeader->SetItem(iZeroBasedColIndex, &hdItem);
if (!bRet)
return sc = E_FAIL;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_Width
*
* PURPOSE: Returns width of column. Implements get for Column.Width
*
* PARAMETERS:
* PLONG Width - resulting width
* int iZeroBasedColIndex - index of column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_Width( /*[out, retval]*/ PLONG Width, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_Width"));
// Check received parameters
sc = ScCheckPointers(Width);
if (sc)
return sc;
// initialize output
*Width = 0;
// retrieve data for column (this also checks the index)
ColumnData strColData;
sc = ScGetColumnData( iZeroBasedColIndex, &strColData );
if (sc)
return sc;
*Width = strColData.iColumnWidth;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scput_Width
*
* PURPOSE: Sets width of column. Implements put for Column.Width
*
* PARAMETERS:
* LONG Width - new width
* int iZeroBasedColIndex - index of column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scput_Width( /*[in]*/ long Width, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scput_Width"));
ColumnData strColData;
// retrieve current column data
sc = ScGetColumnData( iZeroBasedColIndex, &strColData );
if (sc)
return sc;
// change the width
strColData.iColumnWidth = Width;
// set modified column data
sc = ScSetColumnData( iZeroBasedColIndex, strColData );
if (sc)
return sc;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_DisplayPosition
*
* PURPOSE: Returns display position of column. Imnplements get for Column.DisplayPosition
*
* PARAMETERS:
* PLONG DisplayPosition - display position ( 1 based )
* int iZeroBasedColIndex - index of column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_DisplayPosition( /*[out, retval]*/ PLONG DisplayPosition, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_DisplayPosition"));
// Check received parameters
sc = ScCheckPointers(DisplayPosition);
if (sc)
return sc;
// initialize
*DisplayPosition = 0;
// retrieve data for column (this also checks the index)
ColumnData strColData;
sc = ScGetColumnData( iZeroBasedColIndex, &strColData );
if (sc)
return sc;
// it's not legal for hidden columns ( or unfair at least :-)
if (strColData.bIsHidden)
return sc = E_UNEXPECTED;
int iColumnOrder = strColData.iColumnOrder;
int iDisplayPosition = iColumnOrder + 1;
// that would be it. BUT we may have hidden columns with smaller order numbers
// we need ti iteratre to find it out
int nColumnCount = GetColCount();
for (int i = 0; i <nColumnCount; i++)
{
// retrieve data for column (this also checks the index)
sc = ScGetColumnData( i, &strColData );
if (sc)
return sc;
// we will not take into account any hidden column
if (strColData.iColumnOrder < iColumnOrder && strColData.bIsHidden)
{
// decrement position, since hidden columns do not affect visual position
iDisplayPosition --;
}
}
// return the display position
*DisplayPosition = iDisplayPosition;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scput_DisplayPosition
*
* PURPOSE: Moves column (visually) to specified position
* Implements put for Column.DisplayPosition
*
* PARAMETERS:
* long lVisualPosition - Position to move to (1 based)
* int iZeroBasedColIndex - column index to move
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scput_DisplayPosition( /*[in]*/ long lVisualPosition, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scput_DisplayPosition"));
// check the params
if (lVisualPosition < 1)
return sc = E_INVALIDARG;
// retrieve current column data (this will check the index)
ColumnData strColData;
sc = ScGetColumnData( iZeroBasedColIndex, &strColData );
if (sc)
return sc;
// we need to iterate and see what index and iOrder represents the requested position
int nColumnCount = GetColCount();
// will create a map from iOrder to visual status to make life easier
// and benefit from the fact that items get sorted when inserting
std::map<int,bool> mapColumnsByDisplayPos;
for (int i = 0; i <nColumnCount; i++)
{
ColumnData strCurrColData;
sc = ScGetColumnData( i, &strCurrColData );
if (sc)
return sc;
// insert to the map
mapColumnsByDisplayPos[strCurrColData.iColumnOrder] = strCurrColData.bIsHidden;
}
// now find out the right iOrder for the new position
std::map<int,bool>::iterator it;
int iNewOrder = 1;
int nCurrPos = 0;
for (it = mapColumnsByDisplayPos.begin(); it != mapColumnsByDisplayPos.end(); ++it)
{
iNewOrder = it->first;
bool bHidden = it->second;
// olny visible items are counted when it comes to display position
if (!bHidden)
++nCurrPos;
if (nCurrPos == lVisualPosition)
break; // we've found the good place to move in
}
// note - if position is not found - iNewOrder will mark the last column.
// good - that's a reasonable place for insertion to default to.
// that means column will go to the end if given index was bigger than count
// of visible columns
strColData.iColumnOrder = iNewOrder;
sc = ScSetColumnData( iZeroBasedColIndex, strColData );
if (sc)
return sc;
// Now redraw the list view
InvalidateRect(GetListCtrl(), NULL, TRUE);
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scget_Hidden
*
* PURPOSE: Returns hidden status for collumn. Implements get for Column.Hidden
*
* PARAMETERS:
* PBOOL Hidden - resulting status
* int iZeroBasedColIndex - index of the column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scget_Hidden( /*[out, retval]*/ PBOOL pHidden, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scget_Hidden"));
// check parameters
sc = ScCheckPointers(pHidden);
if (sc)
return sc;
// initialize
*pHidden = FALSE;
// retrieve current column data (this will check the index)
ColumnData strColData;
sc = ScGetColumnData( iZeroBasedColIndex, &strColData );
if (sc)
return sc;
// return the hidden status
*pHidden = strColData.bIsHidden;
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::Scput_Hidden
*
* PURPOSE: Hides or shows the column. Implements put for Column.Hidden
*
* PARAMETERS:
* BOOL Hidden - new status for column
* int iZeroBasedColIndex - index of column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::Scput_Hidden( /*[in]*/ BOOL Hidden , int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::Scput_Hidden"));
// retrieve current column data (this will check the index)
ColumnData strColData;
sc = ScGetColumnData( iZeroBasedColIndex, &strColData );
if (sc)
return sc;
// check if we do have a change in status
if (strColData.bIsHidden == (bool)Hidden)
return sc;
// will never hide the 0 column!!!
if (Hidden && iZeroBasedColIndex == 0)
return sc = ::ScFromMMC(MMC_E_ZERO_COLUMN_INVISIBLE);
// check for view
sc = ScCheckPointers( m_pListView, E_UNEXPECTED);
if (sc)
return sc;
// get AMCView
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers( pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// Get component node that owns the result view
HNODE hnodeOwner = pAMCView->GetSelectedNode();
sc = ScCheckPointers((LPVOID)hnodeOwner, E_FAIL);
if (sc)
return sc.ToHr();
LPNODECALLBACK pNodeCallback = pAMCView->GetNodeCallback();
sc = ScCheckPointers(pNodeCallback, E_UNEXPECTED);
if (sc)
return sc.ToHr();
// forward the request to CEnumeratedNode - owner of the view
sc = pNodeCallback->ShowColumn(hnodeOwner, iZeroBasedColIndex, !Hidden);
if (sc)
return sc.ToHr();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScSetAsSortColumn
*
* PURPOSE: Sorts LV by specified column. Implements Column.SetAsSortColumn
*
* PARAMETERS:
* ColumnSortOrder SortOrder - sort order requested
* int iZeroBasedColIndex - index of sort column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScSetAsSortColumn( /*[in]*/ ColumnSortOrder SortOrder, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScSetAsSortColumn"));
// recheck the column index
// (it's not something the script sent - it's internal data)
if (iZeroBasedColIndex < 0 || iZeroBasedColIndex >= GetColCount())
return sc = E_UNEXPECTED;
// get AMCView
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers( pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// Get component node that owns the result view
HNODE hnodeOwner = pAMCView->GetSelectedNode();
sc = ScCheckPointers((LPVOID)hnodeOwner, E_FAIL);
if (sc)
return sc.ToHr();
LPNODECALLBACK pNodeCallback = pAMCView->GetNodeCallback();
sc = ScCheckPointers(pNodeCallback, E_UNEXPECTED);
if (sc)
return sc.ToHr();
// forward the request to CEnumeratedNode - owner of the view
sc = pNodeCallback->SetSortColumn(hnodeOwner, iZeroBasedColIndex, SortOrder == SortOrder_Ascending);
if (sc)
return sc.ToHr();
return sc;
}
/***************************************************************************\
*
* METHOD: CCCListViewCtrl::ScIsSortColumn
*
* PURPOSE: Checks if the column is the one LV is sorted by
* Implements Column.IsSortColumn
*
* PARAMETERS:
* PBOOL IsSortColumn - result (TRUE/FALSE)
* int iZeroBasedColIndex - column index
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CCCListViewCtrl::ScIsSortColumn( PBOOL IsSortColumn, int iZeroBasedColIndex )
{
DECLARE_SC(sc, TEXT("CCCListViewCtrl::ScIsSortColumn"));
// check the param
sc = ScCheckPointers(IsSortColumn);
if (sc)
return sc;
// initialize
*IsSortColumn = FALSE;
// recheck the column index
// (it's not something the script sent - it's internal data)
if (iZeroBasedColIndex < 0 || iZeroBasedColIndex >= GetColCount())
return sc = E_UNEXPECTED;
// get AMCView
CAMCView* pAMCView = m_pListView->GetAMCView();
sc = ScCheckPointers( pAMCView, E_UNEXPECTED);
if (sc)
return sc;
// Get component node that owns the result view
HNODE hnodeOwner = pAMCView->GetSelectedNode();
sc = ScCheckPointers((LPVOID)hnodeOwner, E_FAIL);
if (sc)
return sc.ToHr();
LPNODECALLBACK pNodeCallback = pAMCView->GetNodeCallback();
sc = ScCheckPointers(pNodeCallback, E_UNEXPECTED);
if (sc)
return sc.ToHr();
// forward the request to CEnumeratedNode - owner of the view
int iSortCol = -1;
sc = pNodeCallback->GetSortColumn(hnodeOwner, &iSortCol);
if (sc)
return sc.ToHr();
// see if this column is the one LV is sorted by
*IsSortColumn = (iSortCol == iZeroBasedColIndex);
return sc;
}
/***************************************************************************\
*
* METHOD: CColumn::ScOnListViewColumnInserted
*
* PURPOSE: Handler for event fired by LV, informing that new column was inserted
*
* PARAMETERS:
* int nIndex - index of newly inserted column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CColumn::ScOnListViewColumnInserted (int nIndex)
{
DECLARE_SC(sc, TEXT("CColumn::ScOnListViewColumnInserted "));
// increment own index if column inserted ahead
if (m_iIndex >= nIndex)
m_iIndex++;
return sc;
}
/***************************************************************************\
*
* METHOD: CColumn::ScOnListViewColumnDeleted
*
* PURPOSE: Handler for event fired by LV, informing that new column was deleted
*
* PARAMETERS:
* int nIndex - index of deleted column
*
* RETURNS:
* SC - result code
*
\***************************************************************************/
SC CColumn::ScOnListViewColumnDeleted (int nIndex)
{
DECLARE_SC(sc, TEXT("CColumn::ScOnListViewColumnDeleted "));
// decrement own index if column deleted ahead
if (m_iIndex > nIndex)
m_iIndex--;
// disconnect from world if it's me who just died
else if (m_iIndex == nIndex)
{
// I'm hit. I'm hit. I'm hit.
m_iIndex = -1;
// disconnect from the tied object
if (IsTied())
{
CMyTiedObject *pTied = NULL;
sc = ScGetTiedObject(pTied);
if (sc)
return sc;
sc = ScCheckPointers(pTied, E_UNEXPECTED);
if (sc)
return sc;
// break the connection
pTied->RemoveFromList(this);
Unadvise();
}
}
return sc;
}