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.
6761 lines
191 KiB
6761 lines
191 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
|
|
{
|
|
public:
|
|
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;
|
|
// NOTE: do not use GetListCtrl().SetItemState - it uses SetItem which is not supported for virtual lists
|
|
|
|
// (wparam = -1) => Message applies to all items.
|
|
// This gives better performance than iterating through all items
|
|
// sending a separate LVM_SETITEMSTATE for each. RAID# 595783
|
|
if (!GetListCtrl().SendMessage( LVM_SETITEMSTATE, WPARAM(-1), (LPARAM)(LV_ITEM FAR *)&lvi))
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
HWND hListView = GetListViewHWND();
|
|
if(hListView == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
ASSERT(::IsWindow(hListView));
|
|
|
|
// 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(hListView, 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(hListView, 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.
|
|
*/
|
|
|
|
CAMCHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
|
|
if ((nNewValue & LVS_NOSORTHEADER) ^ (curStyle & LVS_NOSORTHEADER) && pHeaderCtrl)
|
|
{
|
|
if (nNewValue & LVS_NOSORTHEADER)
|
|
pHeaderCtrl->ModifyStyle (HDS_BUTTONS, 0); // Add the style
|
|
else
|
|
pHeaderCtrl->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)
|
|
{
|
|
HWND hListView = GetListViewHWND();
|
|
if (hListView == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
ASSERT(::IsWindow(hListView));
|
|
|
|
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(hListView, GWL_STYLE) ^ LVS_OWNERDATA;
|
|
long curStyleEx = ::GetWindowLong(hListView, GWL_EXSTYLE);
|
|
|
|
long curHdrStyle = 0;
|
|
|
|
CAMCHeaderCtrl* pHeaderCtrl = NULL;
|
|
if ((pHeaderCtrl = GetHeaderCtrl()))
|
|
curHdrStyle = pHeaderCtrl->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;
|
|
|
|
hListView = GetListViewHWND(); // Get new list view handle
|
|
if (hListView == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
// Set current styles on new control
|
|
::SetWindowLong(hListView, GWL_STYLE, curStyle);
|
|
::SetWindowLong(hListView, GWL_EXSTYLE, curStyleEx);
|
|
|
|
// Note we have switched to the other control by now so this is getting the
|
|
// header of the new list
|
|
|
|
pHeaderCtrl = GetHeaderCtrl();
|
|
if (pHeaderCtrl)
|
|
::SetWindowLong(pHeaderCtrl->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
|
|
|
|
CAMCHeaderCtrl* pHeaderCtrl = NULL;
|
|
if ((pHeaderCtrl = GetHeaderCtrl()))
|
|
pHeaderCtrl->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
|
|
{
|
|
// PERF: turn of redraw while all the items are deleted. This has a huge impact.
|
|
lc.SetRedraw(false);
|
|
|
|
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)
|
|
break; // don't return here because lc.SetRedraw(true) must be called
|
|
}
|
|
|
|
else
|
|
sc = E_FAIL;
|
|
}
|
|
}
|
|
|
|
// reenable the list control's drawing ability
|
|
lc.SetRedraw(true);
|
|
|
|
// ensure that the listview redraws itself
|
|
lc.Invalidate();
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
CAMCHeaderCtrl* pHeaderCtrl = NULL;
|
|
if ((pHeaderCtrl = GetHeaderCtrl()) == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
BOOL bRet = pHeaderCtrl->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
|
|
pHeaderCtrl->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;
|
|
|
|
CAMCHeaderCtrl* pHeaderCtrl = NULL;
|
|
if ((pHeaderCtrl = GetHeaderCtrl()))
|
|
bStat = ::SendMessage(pHeaderCtrl->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
|
|
{
|
|
CAMCHeaderCtrl* pHeaderCtrl = NULL;
|
|
if ((pHeaderCtrl = 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 (!pHeaderCtrl->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
|
|
{
|
|
CAMCHeaderCtrl* pHeaderCtrl = NULL;
|
|
if ((pHeaderCtrl = 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 = pHeaderCtrl->GetItem(nCol, &item);
|
|
if (!bStat)
|
|
hr = E_FAIL;
|
|
|
|
// NOTE: pHeaderCtrl->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 )
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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 )
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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 = false;
|
|
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)
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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)
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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)
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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)
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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 )
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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)
|
|
{
|
|
MMC_COM_MANAGE_STATE();
|
|
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;
|
|
}
|