1441 lines
82 KiB
C++
1441 lines
82 KiB
C++
// ==========================================================================
|
||
// Class Implementation : COXEditList
|
||
// ==========================================================================
|
||
|
||
// Source file : editlist.cpp
|
||
|
||
// Version: 9.3
|
||
|
||
// This software along with its related components, documentation and files ("The Libraries")
|
||
// is ?1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
||
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
||
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
||
// to obtain this file, or directly from our office. For a copy of the license governing
|
||
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
||
|
||
// //////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "stdafx.h" // standard MFC include
|
||
#include "OXEditList.h" // class specification
|
||
//#include "OXEditListRes.h" // For toolbar
|
||
#include "commctrl.h" // For toolbar text handler
|
||
#include <afxpriv.h> // for WM_IDLEUPDATECMDUI
|
||
#include "UTB64Bit.h"
|
||
|
||
#ifdef _DEBUG
|
||
#undef THIS_FILE
|
||
static char BASED_CODE THIS_FILE[] =__FILE__;
|
||
static char BASED_CODE _FILE_NAME_[]="OXEditList";
|
||
#else
|
||
#define _FILE_NAME_ __FILE__
|
||
#endif
|
||
|
||
#define new DEBUG_NEW
|
||
|
||
// Determine number of elements in an array (not bytes)
|
||
#ifndef _countof
|
||
#define _countof(array) (sizeof(array)/sizeof(array[0]))
|
||
#endif // _countof
|
||
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// COXEditListHeader
|
||
|
||
COXEditListHeader::COXEditListHeader()
|
||
{
|
||
// set default values
|
||
VERIFY(SetTextColor());
|
||
VERIFY(SetBorderColors());
|
||
SetVertOriented(FALSE);
|
||
m_sText.Empty();
|
||
}
|
||
|
||
COXEditListHeader::~COXEditListHeader()
|
||
{
|
||
}
|
||
|
||
|
||
BEGIN_MESSAGE_MAP(COXEditListHeader, CStatic)
|
||
//{{AFX_MSG_MAP(COXEditListHeader)
|
||
ON_WM_CREATE()
|
||
ON_WM_PAINT()
|
||
ON_MESSAGE(WM_SETTEXT,OnSetText)
|
||
ON_MESSAGE(WM_GETTEXT,OnGetText)
|
||
ON_MESSAGE(WM_GETTEXTLENGTH,OnGetTextLength)
|
||
ON_WM_ERASEBKGND()
|
||
ON_WM_SIZE()
|
||
//}}AFX_MSG_MAP
|
||
END_MESSAGE_MAP()
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// COXEditListHeader message handlers
|
||
|
||
void COXEditListHeader::PreSubclassWindow()
|
||
{
|
||
// TODO: Add your specialized code here and/or call the base class
|
||
|
||
// make sure there wasn't any border styles specified,
|
||
// we don't support them
|
||
ModifyStyle(WS_BORDER,0);
|
||
ModifyStyleEx(WS_EX_CLIENTEDGE|WS_EX_STATICEDGE|
|
||
WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME,0,SWP_FRAMECHANGED);
|
||
/////////////////////////////////
|
||
|
||
// save the window text
|
||
GetWindowText(m_sText);
|
||
|
||
CStatic::PreSubclassWindow();
|
||
}
|
||
|
||
int COXEditListHeader::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
||
{
|
||
// make sure there wasn't any border styles specified,
|
||
// we don't support them
|
||
ASSERT((lpCreateStruct->style&WS_BORDER)==0);
|
||
ASSERT((lpCreateStruct->dwExStyle&(WS_EX_CLIENTEDGE|WS_EX_STATICEDGE|
|
||
WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME))==0);
|
||
lpCreateStruct->style&=~WS_BORDER;
|
||
lpCreateStruct->dwExStyle&=~(WS_EX_CLIENTEDGE|WS_EX_STATICEDGE|
|
||
WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME);
|
||
/////////////////////////////////
|
||
|
||
if(CStatic::OnCreate(lpCreateStruct)==-1)
|
||
return -1;
|
||
|
||
// save the window text
|
||
m_sText=lpCreateStruct->lpszName;
|
||
|
||
return 0;
|
||
}
|
||
|
||
void COXEditListHeader::OnPaint()
|
||
{
|
||
CPaintDC dc(this); // device context for painting
|
||
|
||
// TODO: Add your message handler code here
|
||
|
||
CRect rectClient;
|
||
GetClientRect(rectClient);
|
||
|
||
// draw border
|
||
dc.Draw3dRect(rectClient,m_clrTopLeft,m_clrBottomRight);
|
||
|
||
// draw text
|
||
//
|
||
|
||
if(!m_sText.IsEmpty())
|
||
{
|
||
// setup environment
|
||
dc.SetBkMode(TRANSPARENT);
|
||
dc.SetTextColor(m_clrText);
|
||
|
||
CFont* pOldFont=NULL;
|
||
CFont fontVert;
|
||
CFont* pFont=GetFont();
|
||
if(pFont)
|
||
{
|
||
if(m_bVertOriented)
|
||
{
|
||
// setup font for vertically oriented mode
|
||
LOGFONT lf;
|
||
VERIFY(pFont->GetLogFont(&lf));
|
||
lf.lfEscapement=900;
|
||
lf.lfOrientation=900;
|
||
VERIFY(fontVert.CreateFontIndirect(&lf));
|
||
pOldFont=dc.SelectObject(&fontVert);
|
||
}
|
||
else
|
||
pOldFont=dc.SelectObject(pFont);
|
||
}
|
||
|
||
CRect rectText(0,0,0,0);
|
||
|
||
rectClient.DeflateRect(2,2);
|
||
dc.IntersectClipRect(rectClient);
|
||
|
||
rectText=rectClient;
|
||
CRect rectHelper;
|
||
if(m_bVertOriented)
|
||
{
|
||
// adjust rectangle to display verical text
|
||
rectHelper=rectText;
|
||
rectText.top=rectHelper.left;
|
||
rectText.bottom=rectHelper.right;
|
||
rectText.left=rectHelper.top;
|
||
rectText.right=rectHelper.bottom;
|
||
}
|
||
|
||
// calculate the rect to display text in
|
||
UINT nFormat=DT_LEFT|DT_SINGLELINE;
|
||
dc.DrawText(m_sText,&rectText,nFormat|DT_CALCRECT);
|
||
rectHelper=rectText;
|
||
if(m_bVertOriented)
|
||
{
|
||
rectText.top=rectHelper.left;
|
||
rectText.bottom=rectHelper.right;
|
||
rectText.left=rectHelper.top;
|
||
rectText.right=rectHelper.bottom;
|
||
rectHelper=rectText;
|
||
}
|
||
|
||
// adjust coordinates
|
||
if(m_bVertOriented)
|
||
{
|
||
rectText.left+=(rectClient.Width()-rectHelper.Width())/2+
|
||
(rectClient.Width()-rectHelper.Width())%2;
|
||
rectText.right=rectText.left+rectHelper.Width();
|
||
}
|
||
else
|
||
{
|
||
rectText.top+=(rectClient.Height()-rectHelper.Height())/2+
|
||
(rectClient.Height()-rectHelper.Height())%2;
|
||
rectText.bottom=rectText.top+rectHelper.Height();
|
||
}
|
||
|
||
// adjust the text box depending on text alignment style
|
||
DWORD dwStyle=GetStyle()&0x0000000f;
|
||
switch(dwStyle)
|
||
{
|
||
// SS_CENTER
|
||
case 1:
|
||
if(m_bVertOriented)
|
||
{
|
||
rectText.top+=(rectClient.Height()-rectHelper.Height())/2+
|
||
(rectClient.Height()-rectHelper.Height())%2;
|
||
rectText.bottom=rectText.top+rectHelper.Height();
|
||
}
|
||
else
|
||
{
|
||
rectText.left+=(rectClient.Width()-rectHelper.Width())/2;
|
||
rectText.right=rectText.left+rectHelper.Width();
|
||
}
|
||
break;
|
||
// SS_RIGHT
|
||
case 2:
|
||
if(m_bVertOriented)
|
||
{
|
||
rectText.bottom=rectClient.bottom;
|
||
rectText.top=rectText.bottom-rectHelper.Height();
|
||
}
|
||
else
|
||
{
|
||
rectText.right=rectClient.right;
|
||
rectText.left=rectText.right-rectHelper.Width();
|
||
}
|
||
break;
|
||
// SS_LEFT
|
||
default:
|
||
if(m_bVertOriented)
|
||
{
|
||
rectText.top=rectClient.top;
|
||
rectText.bottom=rectText.top+rectHelper.Height();
|
||
}
|
||
else
|
||
{
|
||
rectText.left=rectClient.left;
|
||
rectText.right=rectText.left+rectHelper.Width();
|
||
}
|
||
}
|
||
|
||
rectHelper=rectText;
|
||
if(m_bVertOriented)
|
||
{
|
||
rectHelper.bottom+=rectText.Width();
|
||
rectHelper.right+=rectText.Height();
|
||
}
|
||
// draw text
|
||
dc.DrawText(m_sText,&rectHelper,DT_BOTTOM|DT_LEFT|DT_SINGLELINE);
|
||
|
||
if(pOldFont!=NULL)
|
||
dc.SelectObject(pOldFont);
|
||
}
|
||
|
||
// Do not call CStatic::OnPaint() for painting messages
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG_PTR
|
||
LRESULT COXEditListHeader::OnSetText(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
|
||
// save the window text
|
||
m_sText=(LPCTSTR)lParam;
|
||
|
||
RedrawWindow();
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG
|
||
LRESULT COXEditListHeader::OnGetText(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
if(wParam>0)
|
||
{
|
||
wParam=wParam>(UINT)m_sText.GetLength()+1 ?
|
||
m_sText.GetLength()+1 : wParam;
|
||
VERIFY(lstrcpyn((LPTSTR)(LONG_PTR)lParam,
|
||
m_sText,wParam-1)!=NULL);
|
||
}
|
||
return (LONG)wParam;
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG
|
||
LRESULT COXEditListHeader::OnGetTextLength(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
return (LONG)m_sText.GetLength();
|
||
}
|
||
|
||
|
||
BOOL COXEditListHeader::OnEraseBkgnd(CDC* pDC)
|
||
{
|
||
// TODO: Add your message handler code here and/or call default
|
||
|
||
// provide background filling (we don't want it to be transparent!!!)
|
||
CRect rectClient;
|
||
GetClientRect(rectClient);
|
||
pDC->FillSolidRect(rectClient,::GetSysColor(COLOR_BTNFACE));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void COXEditListHeader::OnSize(UINT nType, int cx, int cy)
|
||
{
|
||
CStatic::OnSize(nType, cx, cy);
|
||
|
||
// TODO: Add your message handler code here
|
||
|
||
// redraw the control while resizing it
|
||
RedrawWindow();
|
||
}
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// Definition of static members
|
||
COXEditList::EToolbarPosition COXEditList::TBP_FIRST=COXEditList::TBPNone;
|
||
COXEditList::EToolbarPosition COXEditList::TBP_LAST=COXEditList::TBPVerticalRightBottom;
|
||
|
||
///////////////////////////////////////////////////////////////////
|
||
// Data members -------------------------------------------------------------
|
||
// protected:
|
||
// EToolbarPosition m_eToolbarPosition;
|
||
// --- The current position of the toolbar seen from this control
|
||
|
||
// BOOL m_bAllowDuplicates;
|
||
// --- Whether entries with the same name are allowed in the list
|
||
// (When not the user will be warned by a message box when he
|
||
// tries to add an already existing name
|
||
|
||
// BOOL m_bOrderedList;
|
||
// --- Whether the user can move an item up and down (TRUE) or not (FALSE)
|
||
|
||
// BOOL m_bIsDeleting
|
||
// --- Whether the control is in a delete process or not
|
||
|
||
// CToolBar m_toolbar;
|
||
// --- The toolbar window
|
||
// This window iss a sibling of this control, but this control is its owner
|
||
// This way it receives important messages from the toolbar
|
||
|
||
// BOOL m_bPostInitialized;
|
||
// --- Whether the function PostInit has been executed
|
||
|
||
// CString m_sOriginalEditText;
|
||
// --- The content of the label when the user started editing it
|
||
|
||
// CString m_sDuplicateErrorMsg;
|
||
// --- The error text that should be shown when a duplicate name is
|
||
// detected and is not allowed (m_bAllowDuplicates==FALSE)
|
||
|
||
// int m_nNewImageItem;
|
||
// --- The image index that will be assgined to a new item
|
||
|
||
// private:
|
||
|
||
// Member functions ---------------------------------------------------------
|
||
// public:
|
||
|
||
BEGIN_MESSAGE_MAP(COXEditList, COXGridList)
|
||
//{{AFX_MSG_MAP(COXEditList)
|
||
ON_NOTIFY_REFLECT_EX(LVN_ITEMCHANGED, OnItemChanged)
|
||
ON_WM_KEYDOWN()
|
||
ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginlabeledit)
|
||
ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndlabeledit)
|
||
ON_COMMAND(ID_OX_EDITLIST_NEW, OnNewItem)
|
||
ON_COMMAND(ID_OX_EDITLIST_DELETE, OnDeleteItem)
|
||
ON_UPDATE_COMMAND_UI(ID_OX_EDITLIST_DELETE, OnUpdateDeleteItem)
|
||
ON_COMMAND(ID_OX_EDITLIST_UP, OnMoveItemUp)
|
||
ON_UPDATE_COMMAND_UI(ID_OX_EDITLIST_UP, OnUpdateMoveItemUp)
|
||
ON_COMMAND(ID_OX_EDITLIST_DOWN, OnMoveItemDown)
|
||
ON_UPDATE_COMMAND_UI(ID_OX_EDITLIST_DOWN, OnUpdateMoveItemDown)
|
||
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
|
||
ON_WM_SYSKEYDOWN()
|
||
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, OnToolTipText)
|
||
ON_WM_WINDOWPOSCHANGED()
|
||
//}}AFX_MSG_MAP
|
||
ON_MESSAGE(LVM_INSERTITEM, OnInsertItem)
|
||
ON_MESSAGE(LVM_DELETEITEM, OnDeleteItem)
|
||
ON_MESSAGE(LVM_DELETEALLITEMS, OnDeleteAllItems)
|
||
ON_MESSAGE(WM_POST_INIT, OnPostInit)
|
||
END_MESSAGE_MAP()
|
||
|
||
COXEditList::COXEditList(EToolbarPosition eToolbarPosition /*=TBPHorizontalTopRight*/,
|
||
BOOL bAllowDuplicates /*=FALSE*/, BOOL bOrderedList /*=TRUE*/)
|
||
:
|
||
m_eToolbarPosition(eToolbarPosition),
|
||
m_bAllowDuplicates(bAllowDuplicates),
|
||
m_bOrderedList(bOrderedList),
|
||
m_bPostInitialized(FALSE),
|
||
m_sOriginalEditText(),
|
||
m_nNewImageItem(1),
|
||
m_sHeaderText(_T("")),
|
||
m_bEditable(TRUE)
|
||
{
|
||
// ... eToolbarPosition must be valid
|
||
ASSERT(TBP_FIRST <= eToolbarPosition);
|
||
ASSERT(eToolbarPosition <= TBP_LAST);
|
||
// ... Use a default error message
|
||
VERIFY(m_sDuplicateErrorMsg.LoadString(IDS_OX_EDITLISTDUPLERROR)); //"This item is already part of the list, please use another name"
|
||
ASSERT_VALID(this);
|
||
}
|
||
|
||
void COXEditList::InitGrid()
|
||
{
|
||
// First call bass class implementation
|
||
COXGridList::InitGrid();
|
||
|
||
// Create a header as sibling of this control
|
||
VERIFY(m_header.Create(NULL,WS_CHILD,CRect(0,0,0,0),GetParent()));
|
||
static CFont fontVertEnabled;
|
||
if((HFONT)fontVertEnabled==NULL)
|
||
VERIFY(fontVertEnabled.CreatePointFont(100,_T("Arial")));
|
||
m_header.SetFont(&fontVertEnabled);
|
||
|
||
|
||
// Create a toolbar as sibling of this control
|
||
// ... Invisible by default, we will show it when the correct dimensions are knwon
|
||
// in PositionToolbar
|
||
VERIFY(m_toolbar.Create(&m_header,WS_CHILD|CBRS_TOOLTIPS));
|
||
// ... Must find the resource
|
||
// (Make sure OXEdit.rc is included in your resource file)
|
||
ASSERT(AfxFindResourceHandle(MAKEINTRESOURCE(IDR_OX_EDITLIST_TOOLS), RT_TOOLBAR) != NULL);
|
||
VERIFY(m_toolbar.LoadToolBar(IDR_OX_EDITLIST_TOOLS));
|
||
m_toolbar.SetOwner(this);
|
||
if (!m_bOrderedList)
|
||
{
|
||
// Not an ordered list, remove the move up and down buttons
|
||
SetButtonCount(m_toolbar, 2);
|
||
}
|
||
#if _MFC_VER>0x0421
|
||
m_toolbar.SetBorders(0,0,0,0);
|
||
#else
|
||
m_toolbar.m_cxLeftBorder=m_toolbar.m_cxRightBorder=
|
||
m_toolbar.m_cyTopBorder=m_toolbar.m_cyBottomBorder=0;
|
||
#endif
|
||
// Move the toolbar to the right position
|
||
PositionToolbar(m_eToolbarPosition);
|
||
|
||
// Because subclassing is not finished yet
|
||
// and so our message handlers are not functional
|
||
// we defer some initialization to later
|
||
VERIFY(m_bPostInitialized==FALSE);
|
||
PostMessage(WM_POST_INIT);
|
||
}
|
||
|
||
void COXEditList::DrawItem(LPDRAWITEMSTRUCT lpDIS)
|
||
{
|
||
ASSERT_VALID(this);
|
||
|
||
// First call bass class implementation
|
||
COXGridList::DrawItem(lpDIS);
|
||
|
||
// Draw an extra recatangle on top of the last (empty) item
|
||
if((int)lpDIS->itemID==GetItemCount()-1 && IsWindowEnabled())
|
||
{
|
||
// We draw a rectangle of half the width of the item
|
||
CRect rect(lpDIS->rcItem);
|
||
if (m_nImageColumn==0)
|
||
// ... Images in first column : move rectangle
|
||
rect.left += GetImagesWidth();
|
||
rect.right=rect.left + rect.Width() / 2;
|
||
rect.InflateRect(-1, -1);
|
||
CDC* pDC=CDC::FromHandle(lpDIS->hDC);
|
||
pDC->DrawFocusRect(rect);
|
||
}
|
||
}
|
||
|
||
void COXEditList::SetDuplicateErrorMsg(LPCTSTR pszDuplicateErrorMsg)
|
||
{
|
||
m_sDuplicateErrorMsg=pszDuplicateErrorMsg;
|
||
}
|
||
|
||
BOOL COXEditList::EditNewItem()
|
||
{
|
||
int nLastItemIndex=GetItemCount() - 1;
|
||
// ... Last item should have an empty label
|
||
ASSERT(GetItemText(nLastItemIndex, 0).IsEmpty());
|
||
|
||
// Just start editing the last item
|
||
SetFocus();
|
||
EnsureVisible(nLastItemIndex, 0, FALSE);
|
||
return (EditLabel(nLastItemIndex, 0) != NULL);
|
||
}
|
||
|
||
BOOL COXEditList::CanDelete() const
|
||
{
|
||
// Check whether any item is selected (apart from the last one)
|
||
int nSelectedIndex=GetCurSel();
|
||
return (0 <= nSelectedIndex) && (nSelectedIndex < GetItemCount() - 1);
|
||
}
|
||
|
||
BOOL COXEditList::DeleteSelectedItems()
|
||
{
|
||
// Should not call this function when it is of no use
|
||
// GUI should be disabled when necessary
|
||
if (!CanDelete())
|
||
{
|
||
TRACE0("COXEditList::DeleteSelectedItems called when NOT CanDelete()\n");
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL bSuccess=TRUE;
|
||
// ... Store current focus item
|
||
int nFocusItem=GetCurFocus();
|
||
|
||
// Delete all the selected items
|
||
// This might also include the empty (last) item, which deletion will fail
|
||
int nItemIndex=-1;
|
||
// while calling deleteItem an OnChangeState is called, this function is not permitted
|
||
// to set the focus again a that time, so exclude this behaviour
|
||
m_bIsDeleting=TRUE;
|
||
while (bSuccess &&
|
||
(nItemIndex=GetNextItem(nItemIndex, LVNI_SELECTED)) != -1)
|
||
{
|
||
// ... Delete may fail for the empty item
|
||
bSuccess=DeleteItem(nItemIndex);
|
||
// ... Requery this index again,
|
||
// because now this index is used by the next item
|
||
nItemIndex--;
|
||
}
|
||
// Reset the default OnChangeState behaviour
|
||
m_bIsDeleting=FALSE;
|
||
|
||
// ... Restore current focus item (by first removing the focus, like this we are
|
||
// sure that OnChangeState will be called to set the selection also)
|
||
if (SetCurFocus(nFocusItem, FALSE))
|
||
SetCurFocus(nFocusItem);
|
||
else
|
||
{
|
||
// ... If it failed put focus on the last item
|
||
// Remove focus
|
||
SetCurFocus(GetItemCount() - 1, FALSE);
|
||
// Set focus
|
||
SetCurFocus(GetItemCount() - 1);
|
||
}
|
||
|
||
// If we tried to delete the last item, everything succeeded
|
||
// otherwise bSuccess contains the correct value
|
||
return (nItemIndex==GetItemCount() - 2 ? TRUE : bSuccess);
|
||
}
|
||
|
||
BOOL COXEditList::CanMoveUp() const
|
||
{
|
||
// prevent user's ability to move items while in edit mode
|
||
if ( GetEditControl() != NULL )
|
||
return FALSE;
|
||
|
||
// Check whether selected item is not the first item and not the last (empty) item
|
||
int nFocusIndex=GetCurFocus();
|
||
return m_bOrderedList && (0 < nFocusIndex) && (nFocusIndex < GetItemCount() - 1);
|
||
}
|
||
|
||
BOOL COXEditList::MoveItemUp()
|
||
{
|
||
// Should not call this function when it is of no use
|
||
// GUI should be disabled when necessary
|
||
if (!CanMoveUp())
|
||
{
|
||
TRACE0("COXEditList::MoveItemUp called when NOT CanMoveUp()\n");
|
||
return FALSE;
|
||
}
|
||
|
||
int nFocusIndex=GetCurFocus();
|
||
if (SwapItems(nFocusIndex - 1, nFocusIndex))
|
||
{
|
||
EnsureVisible(nFocusIndex - 1, 0, TRUE);
|
||
return TRUE;
|
||
}
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL COXEditList::CanMoveDown() const
|
||
{
|
||
// prevent user's ability to move items while in edit mode
|
||
if ( GetEditControl() != NULL )
|
||
return FALSE;
|
||
|
||
// Check whether selected item is not the last (empty) item or that before the last
|
||
int nFocusIndex=GetCurFocus();
|
||
return m_bOrderedList && (0 <= nFocusIndex) &&
|
||
(nFocusIndex < GetItemCount() - 2);
|
||
}
|
||
|
||
BOOL COXEditList::MoveItemDown()
|
||
{
|
||
// Should not call this function when it is of no use
|
||
// GUI should be disabled when necessary
|
||
if (!CanMoveDown())
|
||
{
|
||
TRACE0("COXEditList::MoveItemDown called when NOT CanMoveDown()\n");
|
||
return FALSE;
|
||
}
|
||
|
||
int nFocusIndex=GetCurFocus();
|
||
if (SwapItems(nFocusIndex, nFocusIndex + 1))
|
||
{
|
||
EnsureVisible(nFocusIndex + 1, 0, TRUE);
|
||
return TRUE;
|
||
}
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL COXEditList::SwapItems(int nIndex1, int nIndex2)
|
||
{
|
||
LV_ITEM lvi1;
|
||
LV_ITEM lvi2;
|
||
memset(&lvi1, 0, sizeof(lvi1));
|
||
memset(&lvi2, 0, sizeof(lvi2));
|
||
|
||
// Initialize the requested struct for both items
|
||
// ... Get everything apart from the text (which we will get seperately)
|
||
lvi1.mask=LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
|
||
lvi1.iItem=nIndex1;
|
||
lvi1.iSubItem=0;
|
||
lvi1.stateMask=0xFFFFFFFF;
|
||
|
||
// ... Get everything apart from the text (which we will get seperately)
|
||
lvi2.mask=LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
|
||
lvi2.iItem=nIndex2;
|
||
lvi2.iSubItem=0;
|
||
lvi2.stateMask=0xFFFFFFFF;
|
||
|
||
if (!GetItem(&lvi1) || !GetItem(&lvi2))
|
||
{
|
||
TRACE2("COXEditList::SwapItems : Failed to get the item %i or %i\n", nIndex1, nIndex2);
|
||
return FALSE;
|
||
}
|
||
|
||
// We got the data, now lets swap them
|
||
lvi1.iItem=nIndex2;
|
||
lvi2.iItem=nIndex1;
|
||
if (!SetItem(&lvi1) || !SetItem(&lvi2))
|
||
{
|
||
TRACE2("COXEditList::SwapItems : Failed to set the item %i or %i\n", nIndex1, nIndex2);
|
||
return FALSE;
|
||
}
|
||
|
||
// Now we will swap the text of the label and of each subitem
|
||
CString sText1;
|
||
CString sText2;
|
||
for (int nSubIndex=0; nSubIndex < GetNumCols(); nSubIndex++)
|
||
{
|
||
sText1=GetItemText(nIndex1, nSubIndex);
|
||
sText2=GetItemText(nIndex2, nSubIndex);
|
||
|
||
if (!SetItemText(nIndex2, nSubIndex, sText1) || !SetItemText(nIndex1, nSubIndex, sText2))
|
||
{
|
||
TRACE2("COXEditList::SwapItems : Failed to set the item text of %i or %i\n", nIndex1, nIndex2);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
// If we arrive here everything has gone OK
|
||
return TRUE;
|
||
}
|
||
|
||
void COXEditList::PositionToolbar(EToolbarPosition eToolbarPosition/*=TBPHorizontalTopRight*/)
|
||
{
|
||
ASSERT(TBP_FIRST <= eToolbarPosition);
|
||
ASSERT(eToolbarPosition <= TBP_LAST);
|
||
|
||
// Store new setting
|
||
m_eToolbarPosition=eToolbarPosition;
|
||
|
||
m_header.ShowWindow((eToolbarPosition==TBPNone) ? SW_HIDE : SW_SHOW);
|
||
m_toolbar.ShowWindow((eToolbarPosition==TBPNone) ? SW_HIDE : SW_SHOW);
|
||
if(eToolbarPosition==TBPNone)
|
||
{
|
||
// Invisible toolbar : nothing more to do
|
||
ASSERT_VALID(this);
|
||
return;
|
||
}
|
||
|
||
CRect rect;
|
||
CRect rectToolbar(0,0,0,0);
|
||
CRect rectHeader(0,0,0,0);
|
||
CSize toolSize;
|
||
CSize offsetHeader;
|
||
CSize offsetToolbar;
|
||
GetWindowRect(rect);
|
||
GetParent()->ScreenToClient(rect);
|
||
|
||
// First calculate the size of the toolbar
|
||
BOOL bHorizontal=(eToolbarPosition==TBPHorizontalTopLeft) ||
|
||
(eToolbarPosition==TBPHorizontalTopCenter) ||
|
||
(eToolbarPosition==TBPHorizontalTopRight) ||
|
||
(eToolbarPosition==TBPHorizontalBottomLeft) ||
|
||
(eToolbarPosition==TBPHorizontalBottomCenter) ||
|
||
(eToolbarPosition==TBPHorizontalBottomRight);
|
||
|
||
// set the orientation of header control
|
||
m_header.SetVertOriented(!bHorizontal);
|
||
switch(eToolbarPosition)
|
||
{
|
||
// Horizontal
|
||
case TBPHorizontalTopLeft:
|
||
case TBPHorizontalBottomLeft:
|
||
case TBPVerticalLeftTop:
|
||
case TBPVerticalRightTop:
|
||
m_header.ModifyStyle(NULL,SS_RIGHT);
|
||
break;
|
||
|
||
case TBPHorizontalTopCenter:
|
||
case TBPHorizontalTopRight:
|
||
case TBPHorizontalBottomCenter:
|
||
case TBPHorizontalBottomRight:
|
||
case TBPVerticalLeftCenter:
|
||
case TBPVerticalLeftBottom:
|
||
case TBPVerticalRightCenter:
|
||
case TBPVerticalRightBottom:
|
||
m_header.ModifyStyle(SS_RIGHT,NULL);
|
||
break;
|
||
|
||
default:
|
||
TRACE1("COXEditList::PositionToolbar : Unexpected case in switch (%i)\n", eToolbarPosition);
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
// ... Change bar style to reflect the correct orientation
|
||
DWORD nOldStyle=m_toolbar.GetBarStyle();
|
||
m_toolbar.SetBarStyle(bHorizontal ? (nOldStyle | CBRS_ORIENT_HORZ) :
|
||
(nOldStyle & ~CBRS_ORIENT_HORZ));
|
||
// ... Get the bar size
|
||
toolSize=m_toolbar.CalcFixedLayout(FALSE,bHorizontal);
|
||
// adjust toolbar rect
|
||
rectToolbar.right=rectToolbar.left + toolSize.cx;
|
||
rectToolbar.bottom=rectToolbar.top + toolSize.cy;
|
||
|
||
// ... Get the border size of the toolbar
|
||
// If the bar is vertical m_cxLeftBorder is used to adjust the top (cy) and
|
||
// m_cxRightBorder is used to adjust the bottom (cy) !
|
||
int nLeftBorder=1;
|
||
int nRightBorder=1;
|
||
int nTopBorder=1;
|
||
int nBottomBorder=1;
|
||
|
||
// adjust header rect
|
||
if(bHorizontal)
|
||
{
|
||
rectHeader.top-=nTopBorder;
|
||
rectHeader.right=rectHeader.left+rect.Width();
|
||
rectHeader.bottom=rectHeader.top+toolSize.cy+nTopBorder+nBottomBorder;
|
||
}
|
||
else
|
||
{
|
||
rectHeader.left-=nTopBorder;
|
||
rectHeader.bottom=rectHeader.left+rect.Height();
|
||
rectHeader.right=rectHeader.left+toolSize.cx+nLeftBorder+nRightBorder;
|
||
}
|
||
|
||
// Then adjust the position of the header and toolbar
|
||
|
||
// ... Handle all possible cases for header
|
||
switch(eToolbarPosition)
|
||
{
|
||
// Horizontal
|
||
case TBPHorizontalTopLeft:
|
||
case TBPHorizontalTopCenter:
|
||
case TBPHorizontalTopRight:
|
||
offsetHeader.cx=rect.left;
|
||
offsetHeader.cy=rect.top - rectHeader.Height();
|
||
break;
|
||
case TBPHorizontalBottomLeft:
|
||
case TBPHorizontalBottomCenter:
|
||
case TBPHorizontalBottomRight:
|
||
offsetHeader.cx=rect.left;
|
||
offsetHeader.cy=rect.bottom+nTopBorder+nBottomBorder;
|
||
break;
|
||
// Vertical
|
||
case TBPVerticalLeftTop:
|
||
case TBPVerticalLeftCenter:
|
||
case TBPVerticalLeftBottom:
|
||
offsetHeader.cx=rect.left-rectHeader.Width();
|
||
offsetHeader.cy=rect.top;
|
||
break;
|
||
case TBPVerticalRightTop:
|
||
case TBPVerticalRightCenter:
|
||
case TBPVerticalRightBottom:
|
||
// ... Right (Horizontal)
|
||
offsetHeader.cx=rect.right+nLeftBorder+nRightBorder;
|
||
offsetHeader.cy=rect.top;
|
||
break;
|
||
default:
|
||
TRACE1("COXEditList::PositionToolbar : Unexpected case in switch (%i)\n", eToolbarPosition);
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
// ... Handle all possible cases for toolbar
|
||
switch (eToolbarPosition)
|
||
{
|
||
// Horizontal
|
||
case TBPHorizontalTopLeft:
|
||
case TBPHorizontalBottomLeft:
|
||
offsetToolbar.cx=nLeftBorder;
|
||
offsetToolbar.cy=nTopBorder;
|
||
break;
|
||
case TBPHorizontalTopCenter:
|
||
case TBPHorizontalBottomCenter:
|
||
offsetToolbar.cx=((rectHeader.Width()>rectToolbar.Width()+
|
||
nLeftBorder+nRightBorder) ?
|
||
(rectHeader.Width()-rectToolbar.Width())/2 : nLeftBorder);
|
||
offsetToolbar.cy=nTopBorder;
|
||
break;
|
||
case TBPHorizontalTopRight:
|
||
case TBPHorizontalBottomRight:
|
||
offsetToolbar.cx=rectHeader.Width()-rectToolbar.Width()-nRightBorder;
|
||
offsetToolbar.cy=nTopBorder;
|
||
break;
|
||
|
||
// Vertical
|
||
case TBPVerticalLeftTop:
|
||
case TBPVerticalRightTop:
|
||
offsetToolbar.cy=nTopBorder;
|
||
offsetToolbar.cx=nLeftBorder;
|
||
break;
|
||
case TBPVerticalLeftCenter:
|
||
case TBPVerticalRightCenter:
|
||
offsetToolbar.cy=((rectHeader.Height()>rectToolbar.Height()+
|
||
nTopBorder+nBottomBorder) ?
|
||
(rectHeader.Height()-rectToolbar.Height())/2 : nTopBorder);
|
||
offsetToolbar.cx=nLeftBorder;
|
||
break;
|
||
case TBPVerticalLeftBottom:
|
||
case TBPVerticalRightBottom:
|
||
offsetToolbar.cy=rectHeader.Height()-rectToolbar.Height()-nBottomBorder;
|
||
offsetToolbar.cx=nLeftBorder;
|
||
break;
|
||
default:
|
||
TRACE1("COXEditList::PositionToolbar : Unexpected case in switch (%i)\n", eToolbarPosition);
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
rectHeader+=offsetHeader;
|
||
rectToolbar+=offsetToolbar;
|
||
|
||
// Reposition the header
|
||
m_header.MoveWindow(rectHeader);
|
||
m_header.Invalidate();
|
||
// Reposition the toolbar
|
||
m_toolbar.MoveWindow(rectToolbar);
|
||
// ... If already visible : redraw
|
||
m_toolbar.Invalidate();
|
||
|
||
ASSERT_VALID(this);
|
||
}
|
||
|
||
COXEditList::EToolbarPosition COXEditList::GetToolbarPosition() const
|
||
{
|
||
ASSERT_VALID(this);
|
||
return m_eToolbarPosition;
|
||
}
|
||
|
||
BOOL COXEditList::OnIdle(LONG lCount /*=0 */)
|
||
{
|
||
// Update the state of the GUI now
|
||
// ... We only have to update the first time the idle loop is executed
|
||
if (lCount==0)
|
||
{
|
||
SendMessage(WM_IDLEUPDATECMDUI, (WPARAM)TRUE /* bDisableIfNoHandler */, 0);
|
||
}
|
||
|
||
// ... We do not need more idle processing
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL COXEditList::SortColumn(int nColumn /*=0 */)
|
||
{
|
||
ASSERT((0 <= nColumn) && (nColumn < GetNumCols()));
|
||
|
||
COXGridListSortInfo gridListSortInfo(this, nColumn);
|
||
return SortItems(EditListCompareProc, (LPARAM)(&gridListSortInfo));
|
||
}
|
||
|
||
#ifdef _DEBUG
|
||
void COXEditList::AssertValid() const
|
||
{
|
||
COXGridList::AssertValid();
|
||
ASSERT(TBP_FIRST <= m_eToolbarPosition);
|
||
ASSERT(m_eToolbarPosition <= TBP_LAST);
|
||
}
|
||
|
||
void COXEditList::Dump(CDumpContext& dc) const
|
||
{
|
||
COXGridList::Dump(dc);
|
||
dc << "\nm_eToolbarPosition : " << (LONG)m_eToolbarPosition;
|
||
dc << "\nm_bAllowDuplicates : " << m_bAllowDuplicates;
|
||
dc << "\nm_bOrderedList : " << m_bOrderedList;
|
||
dc << "\nm_toolbar : " << m_toolbar;
|
||
dc << "\nm_bPostInitialized : " << m_bPostInitialized;
|
||
dc << "\nm_sOriginalEditText : " << m_sOriginalEditText;
|
||
dc << "\nm_sDuplicateErrorMsg : " << m_sDuplicateErrorMsg;
|
||
dc << "\nm_nNewImageItem : " << m_nNewImageItem;
|
||
dc << "\n";
|
||
}
|
||
#endif //_DEBUG
|
||
|
||
COXEditList::~COXEditList()
|
||
{
|
||
ASSERT_VALID(this);
|
||
}
|
||
|
||
BOOL COXEditList::IsFrameWnd() const
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns : Whether this window is a frame window
|
||
// --- Effect :
|
||
{
|
||
// Mimic a frame window to get the idle Update CmdUI messages from the toolbar
|
||
return TRUE;
|
||
}
|
||
|
||
int COXEditList::GetImagesWidth()
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns : The width in pixels of the images in the first column
|
||
// (small image and state image)
|
||
// --- Effect :
|
||
{
|
||
if (GetImageColumn() != 0)
|
||
// First column does not contain any images
|
||
return 0;
|
||
|
||
// Only small images can be shown in report mode
|
||
CImageList* pSmallImageList=GetImageList(LVSIL_SMALL);
|
||
CImageList* pStateImageList=GetImageList(LVSIL_STATE);
|
||
|
||
LV_ITEM lvI;
|
||
lvI.mask=LVIF_IMAGE | LVIF_STATE;
|
||
lvI.iItem=0;
|
||
lvI.iSubItem =0;
|
||
lvI.state=0;
|
||
lvI.stateMask=ALLSTATEIMAGEMASKS;
|
||
|
||
CSize stateImageSize(0,0);
|
||
CSize smallImageSize(0,0);
|
||
|
||
// Query the image and state this item is linked with
|
||
if (GetItem(&lvI))
|
||
{
|
||
// We need the width of the images. It will be used to determine from
|
||
// where we can begin writing the text
|
||
IMAGEINFO imageInfo;
|
||
|
||
// First get the size of the small image and the state image
|
||
// ... Convert from one-based to zero-based index
|
||
int nStateIndex=STATEIMAGEMASKTOINDEX(lvI.state) - 1;
|
||
if ((pStateImageList != NULL) && (0 <= nStateIndex))
|
||
{
|
||
pStateImageList->GetImageInfo(nStateIndex, &imageInfo);
|
||
stateImageSize=CRect(imageInfo.rcImage).Size();
|
||
}
|
||
if (pSmallImageList != NULL)
|
||
{
|
||
pSmallImageList->GetImageInfo(lvI.iImage, &imageInfo);
|
||
smallImageSize=CRect(imageInfo.rcImage).Size();
|
||
}
|
||
}
|
||
|
||
return stateImageSize.cx + smallImageSize.cx;
|
||
}
|
||
|
||
void COXEditList::AdjustEmptyChanged()
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Adjust the control after the the last (empty) item has
|
||
// been filled out
|
||
{
|
||
int nLastItemIndex=GetItemCount() - 1;
|
||
ASSERT(!GetItemText(nLastItemIndex, 0).IsEmpty());
|
||
|
||
// Change the image of this item from 0 (none) to m_nNewImageItem if that image exists
|
||
IMAGEINFO imageInfo;
|
||
CImageList* pSmallImageList=GetImageList(LVSIL_SMALL);
|
||
if ((pSmallImageList != NULL) && (pSmallImageList->GetImageInfo(m_nNewImageItem, &imageInfo)))
|
||
{
|
||
LV_ITEM lvi;
|
||
memset(&lvi, 0, sizeof(lvi));
|
||
lvi.iItem=nLastItemIndex;
|
||
lvi.mask=LVIF_IMAGE;
|
||
lvi.iImage=m_nNewImageItem;
|
||
VERIFY(SetItem(&lvi));
|
||
}
|
||
|
||
// ... Remove selection of item
|
||
VERIFY(SetItemState(nLastItemIndex, 0, LVIS_SELECTED));
|
||
// Last item has been filled out, add a new empty one
|
||
VERIFY(InsertItem(nLastItemIndex + 1, _T(""), 0)==nLastItemIndex + 1);
|
||
// ... Give it focus
|
||
VERIFY(SetItemState(nLastItemIndex + 1, LVIS_FOCUSED, LVIS_FOCUSED));
|
||
|
||
// If the new item is not visible, make it visible
|
||
if (GetTopIndex() + GetCountPerPage() - 1 < nLastItemIndex + 1)
|
||
{
|
||
VERIFY(EnsureVisible(nLastItemIndex + 1, 0, FALSE));
|
||
|
||
// Because the window scrolled up by making the new item visible
|
||
// we need to invalidate an extra region above the edit box
|
||
// (bacause part of edit box has scrolled up as well)
|
||
CRect editRect;
|
||
VERIFY(GetItemRect(nLastItemIndex + 1,&editRect,LVIR_BOUNDS));
|
||
//ASSERT_VALID(GetEditControl());
|
||
//GetEditControl()->GetWindowRect(editRect);
|
||
ScreenToClient(editRect);
|
||
editRect.top -= editRect.Height();
|
||
InvalidateRect(editRect);
|
||
}
|
||
}
|
||
|
||
BOOL COXEditList::SetButtonCount(CToolBar& toolbar, int nCount)
|
||
// --- In : toolbar : The toolbar to use
|
||
// nCount : The number of buttons that have to remain
|
||
// --- Out :
|
||
// --- Returns : Whether it succeeded or not
|
||
// --- Effect : Removes some of the buttons of the toolbar
|
||
// This function will never add new buttons
|
||
{
|
||
if (toolbar.GetCount() < nCount)
|
||
{
|
||
TRACE0("COXEditList::SetButtonCount : Cannot be used to add new buttons\n");
|
||
return FALSE;
|
||
}
|
||
|
||
// Get the present buttons
|
||
UINT* rgID=new UINT[nCount];
|
||
// ... We are not interested in the style and image, but we have to provide a parameter
|
||
UINT nStyle;
|
||
int nImage;
|
||
for (int nIndex=0; nIndex < nCount; nIndex++)
|
||
{
|
||
toolbar.GetButtonInfo(nIndex, rgID[nIndex], nStyle, nImage);
|
||
}
|
||
|
||
// Set the new buttons
|
||
BOOL bSuccess=toolbar.SetButtons(rgID, nCount);
|
||
if (!bSuccess)
|
||
TRACE0("COXEditList::SetButtonCount : Failed to set the new buttons\n");
|
||
|
||
// ... Clean up array of IDs
|
||
delete[] rgID;
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
int CALLBACK COXEditList::EditListCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
||
// --- In : lParam1 : lParam of the first item to compare
|
||
// lParam2 : lParam of the second item to compare
|
||
// lParamSort : parem of the sort object (COXGridListSortInfo)
|
||
// --- Out :
|
||
// --- Returns : A negative value if the first item should precede the second,
|
||
// a positive value if the first item should follow the second,
|
||
// or zero if the two items are equivalent.
|
||
// --- Effect : Compares two items of the control
|
||
// The COXGridListSortInfo contains information about which subitem to use
|
||
{
|
||
#if defined (_WINDLL)
|
||
#if defined (_AFXDLL)
|
||
AFX_MANAGE_STATE(AfxGetAppModuleState());
|
||
#else
|
||
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
#endif
|
||
#endif
|
||
|
||
COXGridListSortInfo* pGridListSortInfo=(COXGridListSortInfo*)lParamSort;
|
||
ASSERT(pGridListSortInfo != NULL);
|
||
ASSERT(AfxIsValidAddress(pGridListSortInfo, sizeof(COXGridListSortInfo)));
|
||
|
||
int nIndex1, nIndex2;
|
||
CString sItemText1, sItemText2;
|
||
|
||
// Lookup the first and second Item
|
||
nIndex1= PtrToInt(pGridListSortInfo->m_pThis->FindOriginalItemData(lParam1));
|
||
nIndex2= PtrToInt(pGridListSortInfo->m_pThis->FindOriginalItemData(lParam2));
|
||
|
||
// Make sure the last (empty) row stays always last
|
||
int iResult;
|
||
int nLastItem=pGridListSortInfo->m_pThis->GetItemCount() - 1;
|
||
if (nIndex1==nLastItem)
|
||
iResult=1;
|
||
else if (nIndex2==nLastItem)
|
||
iResult=-1;
|
||
else
|
||
{
|
||
// query the text
|
||
sItemText1=pGridListSortInfo->m_pThis->GetItemText(nIndex1, pGridListSortInfo->m_nSubIndex);
|
||
sItemText2=pGridListSortInfo->m_pThis->GetItemText(nIndex2, pGridListSortInfo->m_nSubIndex);
|
||
// Compare both string on a No Case basis
|
||
iResult=sItemText1.CompareNoCase(sItemText2);
|
||
}
|
||
|
||
return(iResult);
|
||
}
|
||
|
||
BOOL COXEditList::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
|
||
{
|
||
NM_LISTVIEW* pNMListView=(NM_LISTVIEW*)pNMHDR;
|
||
*pResult=0;
|
||
|
||
// If this item changed state and has focus and is not yet selected, we select it
|
||
if (!m_bIsDeleting &&
|
||
((pNMListView->uChanged & LVIF_STATE)==LVIF_STATE) &&
|
||
((pNMListView->uNewState & LVIS_FOCUSED)==LVIS_FOCUSED) &&
|
||
((pNMListView->uNewState & LVIS_SELECTED) != LVIS_SELECTED) )
|
||
{
|
||
SetCurSel(pNMListView->iItem);
|
||
}
|
||
|
||
if ((pNMListView->uChanged & LVIF_TEXT)==LVIF_TEXT)
|
||
{
|
||
if (pNMListView->iItem < GetItemCount() - 1)
|
||
{
|
||
if (GetItemText(pNMListView->iItem, 0).IsEmpty())
|
||
{
|
||
// If the text of this item changed to an empty text and
|
||
// it is not the last item : delete it
|
||
VERIFY(DeleteItem(pNMListView->iItem));
|
||
VERIFY(SetItemState(pNMListView->iItem, LVIS_FOCUSED, LVIS_FOCUSED));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!GetItemText(pNMListView->iItem, 0).IsEmpty())
|
||
{
|
||
// If the text of the last item was filled out, add a new empty item
|
||
ASSERT(pNMListView->iItem==GetItemCount() - 1);
|
||
// ... SSet image of new item and add a new empty item
|
||
AdjustEmptyChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Call the base class implementation
|
||
return COXGridList::OnListCtrlNotify(pNMHDR, pResult);
|
||
}
|
||
|
||
void COXEditList::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
{
|
||
COXGridList::OnKeyDown(nChar, nRepCnt, nFlags);
|
||
|
||
if((nChar==VK_DELETE) && CanDelete())
|
||
{
|
||
DeleteSelectedItems();
|
||
}
|
||
|
||
}
|
||
|
||
void COXEditList::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
{
|
||
// ... Call base class implementation
|
||
COXGridList::OnSysKeyDown(nChar, nRepCnt, nFlags);
|
||
|
||
// Move item when Alt + arrow Up/Down was pressed
|
||
if((nChar==VK_UP) && CanMoveUp())
|
||
{
|
||
MoveItemUp();
|
||
}
|
||
else if((nChar==VK_DOWN) && CanMoveDown())
|
||
{
|
||
MoveItemDown();
|
||
}
|
||
}
|
||
|
||
BOOL COXEditList::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
|
||
{
|
||
// First call the base class implementation (TRUE=eaten)
|
||
if (COXGridList::OnBeginlabeledit(pNMHDR, pResult))
|
||
return TRUE;
|
||
|
||
LV_DISPINFO* pDispInfo=(LV_DISPINFO*)pNMHDR;
|
||
// ... Never eat this message
|
||
BOOL bEaten=FALSE;
|
||
int nItem=pDispInfo->item.iItem;
|
||
|
||
// Store the old text of the item so we can restore it if necessary
|
||
m_sOriginalEditText=GetItemText(nItem,0);
|
||
|
||
// set internal edit control to use whole cient area
|
||
COXGridEdit* pGridEdit=GetGridEditCtrl();
|
||
ASSERT(pGridEdit!=NULL);
|
||
pGridEdit->SetFitToClient(TRUE);
|
||
|
||
|
||
// Do not change the result (already filled out by the base class)
|
||
// *pResult;
|
||
return bEaten;
|
||
}
|
||
|
||
BOOL COXEditList::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
|
||
{
|
||
// First call the base class implementation (TRUE=eaten)
|
||
if (COXGridList::OnEndlabeledit(pNMHDR, pResult))
|
||
return TRUE;
|
||
|
||
LV_DISPINFO* pDispInfo=(LV_DISPINFO*)pNMHDR;
|
||
// ... Never eat this message
|
||
BOOL bEaten=FALSE;
|
||
|
||
// Check whether the user cancelled editing
|
||
if ((pDispInfo->item.pszText==NULL) || (pDispInfo->item.iItem==-1))
|
||
return bEaten;
|
||
|
||
int nItem=pDispInfo->item.iItem;
|
||
CString sNewText=pDispInfo->item.pszText;
|
||
// ... Only check for duplicates in column 0
|
||
if ((0==m_nEditSubItem) && !sNewText.IsEmpty())
|
||
{
|
||
// Check for duplicate names
|
||
LV_FINDINFO lvfi;
|
||
memset(&lvfi, 0, sizeof(lvfi));
|
||
lvfi.flags=LVFI_STRING | LVFI_WRAP;
|
||
lvfi.psz=pDispInfo->item.pszText;
|
||
if (!m_bAllowDuplicates && (FindItem(&lvfi, nItem) != nItem))
|
||
{
|
||
// Duplicate names detected, restore old name
|
||
AfxMessageBox(m_sDuplicateErrorMsg, MB_ICONEXCLAMATION);
|
||
SetItemText(nItem, 0, m_sOriginalEditText);
|
||
}
|
||
}
|
||
|
||
// Do not change the result (already filled out by the base class)
|
||
// *pResult;
|
||
return bEaten;
|
||
}
|
||
|
||
void COXEditList::OnNewItem()
|
||
{
|
||
EditNewItem();
|
||
}
|
||
|
||
void COXEditList::OnDeleteItem()
|
||
{
|
||
DeleteSelectedItems();
|
||
}
|
||
|
||
void COXEditList::OnUpdateDeleteItem(CCmdUI* pCmdUI)
|
||
{
|
||
pCmdUI->Enable(CanDelete());
|
||
}
|
||
|
||
void COXEditList::OnMoveItemUp()
|
||
{
|
||
MoveItemUp();
|
||
}
|
||
|
||
void COXEditList::OnUpdateMoveItemUp(CCmdUI* pCmdUI)
|
||
{
|
||
pCmdUI->Enable(CanMoveUp());
|
||
}
|
||
|
||
void COXEditList::OnMoveItemDown()
|
||
{
|
||
MoveItemDown();
|
||
}
|
||
|
||
void COXEditList::OnUpdateMoveItemDown(CCmdUI* pCmdUI)
|
||
{
|
||
pCmdUI->Enable(CanMoveDown());
|
||
}
|
||
|
||
LRESULT COXEditList::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
// Pass on to the toolbar
|
||
m_toolbar.SendMessage(WM_IDLEUPDATECMDUI, wParam, lParam);
|
||
return 0L;
|
||
}
|
||
|
||
void COXEditList::OnWindowPosChanged(WINDOWPOS* lpwndpos)
|
||
{
|
||
if (m_bPostInitialized)
|
||
{
|
||
// Make the column of the list as wide as possible but
|
||
// leave space for scroll bar
|
||
CRect listRect;
|
||
GetClientRect(listRect);
|
||
SetColumnWidth(0, listRect.Width());
|
||
|
||
EnsureVisible(GetCurFocus(),0,FALSE);
|
||
}
|
||
|
||
PositionToolbar(m_eToolbarPosition);
|
||
|
||
COXGridList::OnWindowPosChanged(lpwndpos);
|
||
}
|
||
|
||
BOOL COXEditList::OnToolTipText(UINT /* nControlID */, NMHDR* pNMHDR, LRESULT* pResult)
|
||
{
|
||
ASSERT_VALID(this);
|
||
// This code is almost the same as in CFrameWnd::OnToolTipText
|
||
// see WinFrm.cpp
|
||
ASSERT(pNMHDR->code==TTN_NEEDTEXTA || pNMHDR->code==TTN_NEEDTEXTW);
|
||
|
||
// need to handle both ANSI and UNICODE versions of the message
|
||
TOOLTIPTEXTA* pTTTA=(TOOLTIPTEXTA*)pNMHDR;
|
||
TOOLTIPTEXTW* pTTTW=(TOOLTIPTEXTW*)pNMHDR;
|
||
TCHAR szFullText[256];
|
||
CString strTipText;
|
||
UINT_PTR nID=pNMHDR->idFrom;
|
||
if (pNMHDR->code==TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
|
||
pNMHDR->code==TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
|
||
{
|
||
// ... idFrom is actually the HWND of the tool
|
||
nID=((UINT_PTR)(WORD)::GetDlgCtrlID((HWND)nID));
|
||
}
|
||
|
||
// ... nID will be zero on a separator
|
||
if (nID != 0)
|
||
{
|
||
// AfxLoadString is an undocumented function, it still takes a UINT in the latest SDK.
|
||
AfxLoadString((UINT)nID, szFullText);
|
||
// ... this is the command id, not the button index
|
||
AfxExtractSubString(strTipText, szFullText, 1, '\n');
|
||
}
|
||
#ifndef _UNICODE
|
||
if (pNMHDR->code==TTN_NEEDTEXTA)
|
||
lstrcpyn(pTTTA->szText, strTipText, _countof(pTTTA->szText));
|
||
else
|
||
_mbstowcsz(pTTTW->szText, strTipText, _countof(pTTTW->szText));
|
||
#else
|
||
if (pNMHDR->code==TTN_NEEDTEXTA)
|
||
_wcstombsz(pTTTA->szText, strTipText, _countof(pTTTA->szText));
|
||
else
|
||
lstrcpyn(pTTTW->szText, strTipText, _countof(pTTTW->szText));
|
||
#endif
|
||
|
||
// Bring the tooltip window above other popup windows
|
||
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
|
||
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE);
|
||
|
||
// Message was handled
|
||
// ... Message does not use result
|
||
*pResult=0;
|
||
return TRUE;
|
||
}
|
||
|
||
LRESULT COXEditList::OnPostInit(WPARAM /* wParam */, LPARAM /* lParam */)
|
||
{
|
||
// ... Window must be valid
|
||
ASSERT(m_hWnd != NULL);
|
||
ASSERT(::IsWindow(m_hWnd));
|
||
|
||
if (m_bPostInitialized)
|
||
// Already initialized
|
||
return TRUE;
|
||
|
||
// Mark that PostInitialize has been entered
|
||
m_bPostInitialized=TRUE;
|
||
|
||
// Set initial values
|
||
InsertColumn(0, _T(""));
|
||
SetEditable(m_bEditable);
|
||
SetAutoEdit();
|
||
SetMultipleSelection();
|
||
SetBkColor(::GetSysColor(COLOR_WINDOW));
|
||
|
||
// Make the column of the list as wide as possible but leave space for scroll bar
|
||
CRect listRect;
|
||
GetClientRect(listRect);
|
||
SetColumnWidth(0, listRect.Width());
|
||
|
||
// .. Add one extra (empty column)
|
||
InsertItem(GetItemCount(), _T(""), 0);
|
||
Invalidate();
|
||
|
||
// ... Set the focus and selection to the first item
|
||
SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
LRESULT COXEditList::OnDeleteItem(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
// Do not delete the last item
|
||
int nItem=(int)wParam;
|
||
if(nItem==GetItemCount()-1)
|
||
{
|
||
return (LRESULT)FALSE;
|
||
}
|
||
|
||
// Pass the message to the base class
|
||
return COXGridList::OnDeleteItem(wParam, lParam);
|
||
}
|
||
|
||
LRESULT COXEditList::OnDeleteAllItems(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
// Do not delete the last item
|
||
int nCount=GetItemCount();
|
||
for(int nItem=0; nItem<nCount-1; nItem++)
|
||
{
|
||
DeleteItem(0);
|
||
}
|
||
|
||
return (LRESULT)TRUE;
|
||
}
|
||
|
||
LRESULT COXEditList::OnInsertItem(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
const LV_ITEM* pLviOriginal=(const LV_ITEM*)lParam;
|
||
BOOL bUseCopy=FALSE;
|
||
LV_ITEM lviCopy;
|
||
|
||
// Do not insert a non-empty item after last (empty) item
|
||
int nLastItemIndex=GetItemCount();
|
||
if (m_bPostInitialized && (nLastItemIndex <= pLviOriginal->iItem) &&
|
||
( ((pLviOriginal->mask & LVIF_TEXT) != LVIF_TEXT) ||
|
||
!CString(pLviOriginal->pszText).IsEmpty()))
|
||
{
|
||
ASSERT(AfxIsValidAddress(pLviOriginal, sizeof(LV_ITEM)));
|
||
memcpy(&lviCopy, pLviOriginal, sizeof(LV_ITEM));
|
||
bUseCopy=TRUE;
|
||
lviCopy.iItem=nLastItemIndex - 1;
|
||
}
|
||
|
||
// Pass the message to the base class
|
||
LRESULT result=COXGridList::OnInsertItem(wParam, bUseCopy ?
|
||
(LPARAM)&lviCopy : lParam);
|
||
return result;
|
||
}
|
||
|
||
|
||
void COXEditList::SetEditable(BOOL bEdit/*=TRUE*/, int nColumn/*=-1*/)
|
||
{
|
||
if(m_bPostInitialized)
|
||
{
|
||
COXGridList::SetEditable(bEdit,nColumn);
|
||
}
|
||
else
|
||
{
|
||
m_bEditable=bEdit;
|
||
}
|
||
}
|
||
|