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.
1883 lines
52 KiB
1883 lines
52 KiB
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// comctrls.cpp
|
|
//
|
|
//
|
|
// NOTES: an FWORD is a short int
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include <gpdparse.h>
|
|
#include "comctrls.H"
|
|
#include <stdlib.h>
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// The functions defined below for CEditControlEditBox and CEditControlListBox
|
|
// are used to implement a lighter weight, more general purpose Edit control
|
|
// than the UFM Editor specific classes that are defined above. (A normal
|
|
// Edit Box is part of this Edit Control, too.)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CEditControlEditBox - Manages the Edit Box part of the Edit Control that
|
|
// is used to hold the field name.
|
|
|
|
CEditControlEditBox::CEditControlEditBox(CEditControlListBox* pceclb)
|
|
{
|
|
// Save a pointer to the corresponding list box.
|
|
|
|
m_pceclb = pceclb ;
|
|
}
|
|
|
|
CEditControlEditBox::~CEditControlEditBox()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CEditControlEditBox, CEdit)
|
|
//{{AFX_MSG_MAP(CEditControlEditBox)
|
|
ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillfocus)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CEditControlEditBox message handlers
|
|
|
|
void CEditControlEditBox::OnKillfocus()
|
|
{
|
|
// Make sure the value is saved back into the list box.
|
|
|
|
m_pceclb->SaveValue() ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CEditControlListBox
|
|
|
|
CEditControlListBox::CEditControlListBox(CEdit* pce,
|
|
CEditControlEditBox* pceceb)
|
|
{
|
|
// Save pointers to the other 2 controls that make up the Edit Control
|
|
|
|
m_pceName = pce ;
|
|
m_pcecebValue = pceceb ;
|
|
|
|
m_bReady = false ; // Not ready for operations yet
|
|
m_nCurSelIdx = -1 ; // Nothing is selected yet
|
|
}
|
|
|
|
|
|
CEditControlListBox::~CEditControlListBox()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CEditControlListBox, CListBox)
|
|
//{{AFX_MSG_MAP(CEditControlListBox)
|
|
ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelchange)
|
|
ON_CONTROL_REFLECT(LBN_DBLCLK, OnDblclk)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
bool CEditControlListBox::Init(CStringArray& csamodels, CStringArray& csafiles,
|
|
int ntabstop)
|
|
{
|
|
int n ; // Loop counter and temp var
|
|
|
|
// Make sure the list box is empty.
|
|
|
|
ResetContent() ;
|
|
|
|
// Set the position of the list box's tabstop. This value is passed in
|
|
// because it is a dialog box specific value.
|
|
|
|
SetTabStops(1, &ntabstop) ;
|
|
|
|
// Now combine the model names and file names and add them to the listbox.
|
|
|
|
//csafiles[0] = "MMMMMMMM" ;
|
|
CString cs ;
|
|
int nummodels = (int)csamodels.GetSize() ;
|
|
for (n = 0 ; n < nummodels ; n++) {
|
|
cs = csamodels[n] + _T("\t") + csafiles[n] ;
|
|
AddString(cs) ;
|
|
} ;
|
|
|
|
// Set the file name length limit.
|
|
|
|
m_pcecebValue->SetLimitText(8) ;
|
|
|
|
// Initialize (zap) the contents of the edit boxes
|
|
|
|
m_pceName->SetWindowText(_T("")) ;
|
|
m_pcecebValue->SetWindowText(_T("")) ;
|
|
|
|
// Reset the currently selected entry index
|
|
|
|
m_nCurSelIdx = -1 ;
|
|
|
|
// Make sure the list box has the focus.
|
|
|
|
SetFocus() ;
|
|
|
|
// Everything is ready to go now
|
|
|
|
m_bReady = true ;
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CEditControlListBox::GetGPDInfo
|
|
|
|
Load the provided array(s) with the field values and, optionally, the field
|
|
names.
|
|
|
|
******************************************************************************/
|
|
|
|
bool CEditControlListBox::GetGPDInfo(CStringArray& csavalues,
|
|
CStringArray* pcsanames /*= NULL*/)
|
|
{
|
|
// First, make sure that the last value changed in the edit control
|
|
// is saved.
|
|
|
|
SaveValue() ;
|
|
|
|
// Size the array(s) based on the number of entries in the list box.
|
|
// Note: An empty list box is not an error.
|
|
|
|
int numents = GetCount() ;
|
|
if (numents <= 0)
|
|
return true ;
|
|
csavalues.SetSize(numents) ;
|
|
if (pcsanames)
|
|
pcsanames->SetSize(numents) ;
|
|
|
|
// Loop through each entry in the list box, separate the entries into name
|
|
// and value parts, and save the appropriate parts of the entry.
|
|
|
|
CString csentry, csname, csvalue ;
|
|
int npos ;
|
|
for (int n = 0 ; n < numents ; n++) {
|
|
GetText(n, csentry) ;
|
|
npos = csentry.Find(_T('\t')) ;
|
|
csvalue = csentry.Mid(npos + 1) ;
|
|
csavalues[n] = csvalue ;
|
|
if (pcsanames) {
|
|
csname = (npos > 0) ? csentry.Left(npos) : _T("") ;
|
|
pcsanames->SetAt(n, csname) ;
|
|
} ;
|
|
} ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
void CEditControlListBox::SelectLBEntry(int nidx, bool bsave /*=false*/)
|
|
{
|
|
// Select the specified entry
|
|
|
|
SetCurSel(nidx) ;
|
|
|
|
// If the caller doesn't want to save the previous selection, clear the
|
|
// current selection index.
|
|
|
|
if (!bsave)
|
|
m_nCurSelIdx = -1 ;
|
|
|
|
// Update the edit control.
|
|
|
|
OnSelchange() ;
|
|
} ;
|
|
|
|
|
|
void CEditControlListBox::SaveValue(void)
|
|
{
|
|
// Do nothing if the edit control is not ready or nothing is loaded into
|
|
// the edit boxes.
|
|
|
|
if (!m_bReady || m_nCurSelIdx == -1)
|
|
return ;
|
|
|
|
// Get the string from the value edit box and from the selected entry in
|
|
// the list box.
|
|
|
|
CString csvalue, csentry ;
|
|
m_pcecebValue->GetWindowText(csvalue) ;
|
|
GetText(m_nCurSelIdx, csentry) ;
|
|
|
|
// Replace the value in the entry with the value from the edit box and put
|
|
// the new entry back into the list box.
|
|
|
|
int npos = csentry.Find(_T('\t')) ;
|
|
csentry = csentry.Left(npos + 1) + csvalue ;
|
|
DeleteString(m_nCurSelIdx) ;
|
|
InsertString(m_nCurSelIdx, csentry) ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CEditControlListBox message handlers
|
|
|
|
void CEditControlListBox::OnSelchange()
|
|
{
|
|
// Do nothing if the edit control isn't ready, yet.
|
|
|
|
if (!m_bReady)
|
|
return ;
|
|
|
|
// Do nothing if the selection didn't really change
|
|
|
|
int nidx = GetCurSel() ;
|
|
if (nidx == m_nCurSelIdx)
|
|
return ;
|
|
|
|
// Save the current value
|
|
|
|
SaveValue() ;
|
|
|
|
// Get the index of the currently selected list box entry. Return without
|
|
// doing anything else if no entry is selected.
|
|
|
|
if (nidx == LB_ERR)
|
|
return ;
|
|
|
|
// Get the listbox entry and split it into name and value components.
|
|
|
|
CString csentry, csname, csvalue ;
|
|
GetText(nidx, csentry) ;
|
|
int npos = csentry.Find(_T('\t')) ;
|
|
csname = (npos > 0) ? csentry.Left(npos) : _T("") ;
|
|
csvalue = csentry.Mid(npos + 1) ;
|
|
|
|
// Load the name into the name edit box and the value into the value edit
|
|
// box.
|
|
|
|
m_pceName->SetWindowText(csname) ;
|
|
m_pcecebValue->SetWindowText(csvalue) ;
|
|
|
|
// Save the index of the currently selected entry
|
|
|
|
m_nCurSelIdx = nidx ;
|
|
}
|
|
|
|
|
|
void CEditControlListBox::OnDblclk()
|
|
{
|
|
// Do nothing if the edit control isn't ready, yet.
|
|
|
|
if (!m_bReady)
|
|
return ;
|
|
|
|
// Do nothing if no item is selected in the list box.
|
|
|
|
if (GetCurSel() == LB_ERR)
|
|
return ;
|
|
|
|
// Load the edit boxes
|
|
|
|
OnSelchange() ;
|
|
|
|
// Set the focus to the value control
|
|
|
|
m_pcecebValue->SetFocus() ;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// The functions implement below the CFullEditListCtrl and CFELCEditBox
|
|
// classes. Together, they implement support a List Control in Report View
|
|
// in which subitems can be editted too, complete rows can be selected, and
|
|
// the data can be sorted by numeric or text columns. CFELCEditBox is a
|
|
// helper class that is only used by CFullEditListCtrl.
|
|
//
|
|
|
|
CFELCEditBox::CFELCEditBox()
|
|
{
|
|
}
|
|
|
|
|
|
CFELCEditBox::~CFELCEditBox()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CFELCEditBox, CEdit)
|
|
//{{AFX_MSG_MAP(CFELCEditBox)
|
|
ON_WM_KILLFOCUS()
|
|
ON_WM_KEYDOWN()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFELCEditBox message handlers
|
|
|
|
/******************************************************************************
|
|
|
|
CFELCEditBox::OnKeyDown
|
|
|
|
Handle the Escape (cancel) and Return (save) keys.
|
|
|
|
******************************************************************************/
|
|
|
|
void CFELCEditBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
|
{
|
|
// Get a pointer to the owning list control
|
|
|
|
CFullEditListCtrl* plist = (CFullEditListCtrl*) GetParent() ;
|
|
ASSERT (plist);
|
|
|
|
// Handle the interesting keys
|
|
|
|
switch (nChar) {
|
|
// End editing without saving
|
|
|
|
case VK_ESCAPE:
|
|
DefWindowProc(WM_KEYDOWN, 0, 0) ; // Is this right???
|
|
plist->EndEditing(false) ;
|
|
break ;
|
|
|
|
// Save contents and end editing
|
|
|
|
case VK_RETURN:
|
|
DefWindowProc(WM_KEYDOWN, 0, 0) ; // Is this right???
|
|
plist->EndEditing(true) ;
|
|
break ;
|
|
|
|
// Save contents, end editing, and begin editing next cell
|
|
|
|
case VK_TAB:
|
|
DefWindowProc (WM_KEYDOWN, 0, 0) ; // Is this right???
|
|
if (!(plist->EndEditing(true)))
|
|
return ;
|
|
plist->EditCurRowSpecCol(plist->GetCurCol() + 1) ;
|
|
break ;
|
|
|
|
// Process the key normally
|
|
|
|
default:
|
|
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
|
|
} ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFELCEditBox::OnKillFocus
|
|
|
|
Just hide the edit box when it looses the focus. The contents are "lost".
|
|
|
|
******************************************************************************/
|
|
|
|
void CFELCEditBox::OnKillFocus(CWnd* pNewWnd)
|
|
{
|
|
// Save whatever is in the edit box first.
|
|
|
|
CFullEditListCtrl* pList = (CFullEditListCtrl*)GetParent ();
|
|
ASSERT (pList);
|
|
pList->SaveValue() ;
|
|
|
|
CEdit::OnKillFocus(pNewWnd);
|
|
|
|
ShowWindow (SW_HIDE);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFullEditListCtrl
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Message map
|
|
|
|
BEGIN_MESSAGE_MAP(CFullEditListCtrl, CListCtrl)
|
|
//{{AFX_MSG_MAP(CFullEditListCtrl)
|
|
ON_WM_CREATE()
|
|
ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
|
|
ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
|
|
ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown)
|
|
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
|
|
ON_WM_VSCROLL()
|
|
ON_WM_HSCROLL()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFullEditListCtrl::CFullEditListCtrl - Constructor
|
|
|
|
CFullEditListCtrl::CFullEditListCtrl()
|
|
{
|
|
// Initialize member variable(s)
|
|
|
|
m_pciColInfo = NULL ;
|
|
m_nRow = m_nColumn = m_nNumColumns = -1 ;
|
|
m_nNextItemData = 1 ;
|
|
m_pcoOwner = NULL ;
|
|
m_dwCustEditFlags = m_dwToggleFlags = m_dwMiscFlags = 0 ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFullEditListCtrl::~CFullEditListCtrl - Destructor
|
|
|
|
CFullEditListCtrl::~CFullEditListCtrl()
|
|
{
|
|
// Free memory used
|
|
|
|
if (m_pciColInfo != NULL)
|
|
delete m_pciColInfo ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFullEditListCtrl message handlers
|
|
|
|
int CFullEditListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
// I don't think anything else needs to be done here.
|
|
|
|
return (CWnd::OnCreate(lpCreateStruct));
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFullEditListCtrl::InitControl
|
|
|
|
Handles control "global" initialization for CFullEditListCtrl.
|
|
|
|
Args:
|
|
dwaddlexstyles Extend styles for list control
|
|
numrows Number of rows of data that will be loaded into the control
|
|
numcols Number of columns of data that will be loaded into control
|
|
dwtoggleflags Flags describing if/how a column in the list can be toggled
|
|
(See TF_XXXX definitions in comctrls.cpp.)
|
|
neditlen Optional max length of edittable column data strings
|
|
dwmiscflags Miscellaneous flags for controlling the list control
|
|
(See MF_XXXX definitions in comctrls.cpp.)
|
|
|
|
Note:
|
|
The two main initialization routines, InitControl() for global
|
|
initialization and InitLoadColumn() for column specific initialization,
|
|
require a lot of arguments already and I don't want to clutter them with
|
|
more. Be that as it may, this control is still being enhanced so I have
|
|
added extra initialization routines when needed. They are called
|
|
ExtraInit_XXXX(). Read the comment header for each routine to find out
|
|
what they do and when/if they should be called. These routines may handle
|
|
a mixture of list global and/or per column initialization.
|
|
|
|
******************************************************************************/
|
|
|
|
void CFullEditListCtrl::InitControl(DWORD dwaddlexstyles, int numrows,
|
|
int numcols, DWORD dwtoggleflags/*=0*/,
|
|
int neditlen/*=0*/, int dwmiscflags /*=0*/)
|
|
{
|
|
// Add any additional, extended styles to the list control.
|
|
|
|
if (dwaddlexstyles != 0) {
|
|
DWORD dwExStyle = (DWORD)SendMessage (LVM_GETEXTENDEDLISTVIEWSTYLE);
|
|
dwExStyle |= dwaddlexstyles;
|
|
SendMessage (LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwExStyle);
|
|
} ;
|
|
|
|
// Set the number of rows in the list control.
|
|
|
|
SetItemCount(numrows) ;
|
|
|
|
// Allocate the structures used to track info on each column.
|
|
|
|
if (m_pciColInfo != NULL)
|
|
delete m_pciColInfo ;
|
|
m_pciColInfo = new COLINFO[numcols] ;
|
|
m_nNumColumns = numcols ;
|
|
|
|
// Create and initialize the edit control used for subitem editing.
|
|
|
|
VERIFY(m_edit.Create(ES_AUTOHSCROLL | WS_BORDER | WS_CHILD,
|
|
CRect(0,0,0,0), this, 2));
|
|
m_edit.SetFont(GetFont()) ;
|
|
if (neditlen > 0)
|
|
m_edit.LimitText(neditlen) ;
|
|
|
|
// Save the next item data number to use when and if new rows are inserted
|
|
// after the columns are initially loaded.
|
|
|
|
m_nNextItemData = numrows + 1 ;
|
|
|
|
// Save the toggle amd miscellaneous flags
|
|
|
|
m_dwToggleFlags = dwtoggleflags ;
|
|
m_dwMiscFlags = dwmiscflags ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFullEditListCtrl::InitLoadColumn
|
|
|
|
Initialize and load a specific CFullEditListCtrl column.
|
|
|
|
Args:
|
|
ncolnum Column number to initialize/load
|
|
strlabel Column label
|
|
nwidth Width of column or flag specifying how to compute width
|
|
nwidthpad + or - adjustment to column width
|
|
beditable True iff the column is editable
|
|
bsortable True iff the rows can be sorted on this column
|
|
cdtdatatype The type of data in the column
|
|
pcoadata Pointer to array containing the columns data
|
|
lpctstrtoggle If toggle-able column, the column's toggle string
|
|
|
|
Note:
|
|
The two main initialization routines, InitControl() for global
|
|
initialization and InitLoadColumn() for column specific initialization,
|
|
require a lot of arguments already and I don't want to clutter them with
|
|
more. Be that as it may, this control is still being enhanced so I have
|
|
added extra initialization routines when needed. They are called
|
|
ExtraInit_XXXX(). Read the comment header for each routine to find out
|
|
what they do and when/if they should be called. These routines may handle
|
|
a mixture of list global and/or per column initialization.
|
|
|
|
******************************************************************************/
|
|
|
|
int CFullEditListCtrl::InitLoadColumn(int ncolnum, LPCSTR strlabel, int nwidth,
|
|
int nwidthpad, bool beditable,
|
|
bool bsortable, COLDATTYPE cdtdatatype,
|
|
CObArray* pcoadata,
|
|
LPCTSTR lpctstrtoggle/*= NULL*/)
|
|
{
|
|
// Insert the column. Everything except numeric data is left justified.
|
|
// Numeric data is right justified.
|
|
|
|
int nfmt ;
|
|
switch (cdtdatatype) {
|
|
case COLDATTYPE_STRING:
|
|
case COLDATTYPE_TOGGLE:
|
|
case COLDATTYPE_CUSTEDIT:
|
|
nfmt = LVCFMT_LEFT ;
|
|
break ;
|
|
case COLDATTYPE_INT:
|
|
case COLDATTYPE_FLOAT:
|
|
nfmt = LVCFMT_RIGHT ;
|
|
break ;
|
|
default:
|
|
nfmt = LVCFMT_LEFT; //raid 116584 prefix
|
|
ASSERT(0) ;
|
|
}
|
|
VERIFY(InsertColumn(ncolnum, strlabel, nfmt, -1, ncolnum - 1) >= 0) ;
|
|
|
|
// Set flags based on how the column width should be set and initialize it
|
|
// when necessary.
|
|
|
|
bool bcompwidth, bwidthremainder ;
|
|
if (bcompwidth = (nwidth == COMPUTECOLWIDTH)) {
|
|
nwidth = GetStringWidth(strlabel) + 4 ; // Start with width of label
|
|
bwidthremainder = false ;
|
|
} else
|
|
bwidthremainder = (nwidth == SETWIDTHTOREMAINDER) ;
|
|
|
|
// Get the number (sub)items to be loaded into the column
|
|
|
|
int numitems = (int)pcoadata->GetSize() ;
|
|
|
|
// Load the data into the column. If the data aren't strings, they are
|
|
// converted into strings first. If some of the data may be editted with
|
|
// a custom edit routine, add elipses to those strings. The width is
|
|
// checked when necessary.
|
|
|
|
CString csitem ;
|
|
for (int n = 0 ; n < numitems ; n++) {
|
|
// Get the string to load.
|
|
|
|
switch (cdtdatatype) {
|
|
case COLDATTYPE_INT:
|
|
csitem.Format("%d", pcoadata->GetAt(n)) ;
|
|
break ;
|
|
case COLDATTYPE_STRING:
|
|
case COLDATTYPE_TOGGLE:
|
|
csitem = ((CStringArray*) pcoadata)->GetAt(n) ;
|
|
break ;
|
|
case COLDATTYPE_CUSTEDIT:
|
|
csitem = ((CStringArray*) pcoadata)->GetAt(n) ;
|
|
ASSERT(m_cuiaCustEditRows.GetSize()) ;
|
|
if (m_cuiaCustEditRows[n])
|
|
csitem += _T(" ...") ;
|
|
break ;
|
|
default:
|
|
ASSERT(0) ;
|
|
} ;
|
|
|
|
// Save the width of the current item if it is the longest found so far
|
|
// and the width needs to be computed.
|
|
|
|
if (bcompwidth)
|
|
if (nwidth < GetStringWidth(csitem))
|
|
nwidth = GetStringWidth(csitem) ;
|
|
|
|
// Load the item into the appropriate row and column
|
|
|
|
if (ncolnum == 0) {
|
|
VERIFY(InsertItem(n, csitem) != -1) ;
|
|
SetItemData(n, n);
|
|
} else
|
|
VERIFY(SetItem(n, ncolnum, LVIF_TEXT, csitem, -1, 0, 0, n)) ;
|
|
} ;
|
|
|
|
// Determine the column width when the remainder is required and then set it.
|
|
|
|
if (bwidthremainder) {
|
|
CRect cr ;
|
|
GetWindowRect(cr) ;
|
|
nwidth = cr.Width() - 4 ;
|
|
for (n = 0 ; n < ncolnum ; n++)
|
|
nwidth -= (m_pciColInfo + n)->nwidth ;
|
|
} ;
|
|
SetColumnWidth(ncolnum, nwidth + nwidthpad) ;
|
|
|
|
// Save info about the column
|
|
|
|
PCOLINFO pci = (m_pciColInfo + ncolnum) ;
|
|
pci->nwidth = nwidth ;
|
|
pci->beditable = beditable ;
|
|
pci->cdttype = cdtdatatype ;
|
|
pci->bsortable = bsortable ;
|
|
pci->basc = false ;
|
|
pci->lpctstrtoggle = lpctstrtoggle ;
|
|
|
|
// Return the width of the column
|
|
|
|
return nwidth ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFullEditListCtrl::ExtraInit_CustEditCol
|
|
|
|
This routine is called when *ONE* column in the list control contains some
|
|
cells whose contents are editted by a custom edit routine. When such a
|
|
cell is selected, one of the list's owner's member functions is called to
|
|
manage the work (show dlg, etc) needed to edit the cell's contents. Some of
|
|
the column's cells are editted normally. The rest are edited via a
|
|
custom edit routine. Cuiacusteditrows contains data that indicates which
|
|
cells are which.
|
|
|
|
Args:
|
|
ncolnum Custom edit column number
|
|
pcoowner Pointer to this class instance's owner
|
|
dwcusteditflags Custom edit flags
|
|
cuiacusteditrows Rows in ncolnum that require custom editting
|
|
|
|
Note:
|
|
When needed, this routine should be called once; after InitControl() and
|
|
before the InitLoadColumn() call for the custom edit column.
|
|
|
|
InitLoadColumn() should be called for this column with COLDATTYPE_CUSTEDIT
|
|
as one of its parameters.
|
|
|
|
******************************************************************************/
|
|
|
|
bool CFullEditListCtrl::ExtraInit_CustEditCol(int ncolnum, CObject* pcoowner,
|
|
DWORD dwcusteditflags,
|
|
CUIntArray& cuiacusteditrows,
|
|
LPCELLEDITPROC lpcelleditproc)
|
|
{
|
|
// Fail if the column number is invalid
|
|
|
|
if (ncolnum < 0 || ncolnum >= m_nNumColumns)
|
|
return false ;
|
|
|
|
// Save copies of the input parameters for later use.
|
|
|
|
m_pcoOwner = pcoowner ;
|
|
m_dwCustEditFlags = dwcusteditflags ;
|
|
m_cuiaCustEditRows.Copy(cuiacusteditrows) ;
|
|
m_lpCellEditProc = lpcelleditproc ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
BOOL CFullEditListCtrl::GetPointRowCol(LPPOINT lpPoint, int& iRow, int& iCol,
|
|
CRect& rect)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
|
|
// Get row number
|
|
|
|
iRow = HitTest (CPoint (*lpPoint));
|
|
if (-1 == iRow)
|
|
return bFound;
|
|
|
|
// Get the column number and the cell dimensions
|
|
|
|
return (GetColCellRect(lpPoint, iRow, iCol, rect)) ;
|
|
}
|
|
|
|
|
|
BOOL CFullEditListCtrl::GetColCellRect(LPPOINT lpPoint, int& iRow, int& iCol,
|
|
CRect& rect)
|
|
{
|
|
BOOL bFound = FALSE ;
|
|
|
|
// Get the dimensions for the entire row.
|
|
|
|
VERIFY(GetItemRect(iRow, rect, LVIR_BOUNDS)) ;
|
|
|
|
// Prepare to get the width of each column in the row.
|
|
|
|
int iCntr = 0 ;
|
|
LV_COLUMN lvc ;
|
|
ZeroMemory(&lvc, sizeof (LV_COLUMN)) ;
|
|
lvc.mask = LVCF_WIDTH ;
|
|
|
|
// Get the dimensions of each column until the one containing the point is
|
|
// found.
|
|
|
|
while (GetColumn(iCntr, &lvc)) {
|
|
rect.right = rect.left + lvc.cx ;
|
|
if (rect.PtInRect (*lpPoint)) {
|
|
bFound = TRUE ;
|
|
iCol = iCntr ;
|
|
break ;
|
|
} ;
|
|
rect.left = rect.right ;
|
|
iCntr++ ;
|
|
ZeroMemory (&lvc, sizeof (LV_COLUMN)) ;
|
|
lvc.mask = LVCF_WIDTH ;
|
|
} ;
|
|
|
|
// Return TRUE if the point is found in a cell. Otherwise, FALSE.
|
|
|
|
return bFound ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
*pResult = 0 ; // Result always 0
|
|
|
|
// Find out what point on the list control was clicked
|
|
|
|
CPoint point ;
|
|
VERIFY (::GetCursorPos(&point)) ;
|
|
//TRACE("***OnDblclk: GetCursorPos x = %d y = %d", point.x, point.y) ;
|
|
ScreenToClient(&point) ;
|
|
//TRACE(" --- ScreenToClient x = %d y = %d\n", point.x, point.y) ;
|
|
|
|
// Exit if the "click point" can't be mapped to a cell (item) on the list
|
|
// control.
|
|
|
|
int iRow = -1, iCol = -1 ;
|
|
CRect rect ;
|
|
if (!GetPointRowCol(&point, iRow, iCol, rect))
|
|
return ;
|
|
//TRACE("***OnDblclk: Passed GetPointRowCol() call.\n") ;
|
|
|
|
// Exit if the cell cannot be made completely visible. Then get the cell's
|
|
// position and dimensions again because it might have moved.
|
|
|
|
if (!EnsureVisible(iRow, false))
|
|
return ;
|
|
CRect rect2 ;
|
|
VERIFY(GetItemRect(iRow, rect2, LVIR_BOUNDS)) ;
|
|
rect.top = rect2.top ;
|
|
rect.bottom = rect2.bottom ;
|
|
|
|
// If the column is not editable (editability takes precedence), check for
|
|
// and - when appropriate - handle togglability. Then exit.
|
|
|
|
PCOLINFO pci = m_pciColInfo + iCol ;
|
|
if (!pci->beditable) {
|
|
if (!CheckHandleToggleColumns(iRow, iCol, pci))
|
|
CheckHandleCustEditColumn(iRow, iCol, pci) ;
|
|
return ;
|
|
} ;
|
|
|
|
// If the row/column contains a "subordinate dialog" cell that can be
|
|
// handled, do it and return.
|
|
|
|
if (CheckHandleCustEditColumn(iRow, iCol, pci))
|
|
return ;
|
|
|
|
// It is ok to edit the cell normally so fire up, position, and load the
|
|
// edit box.
|
|
|
|
m_nRow = iRow ;
|
|
m_nColumn = iCol ;
|
|
CString strTemp = GetItemText(iRow, iCol) ;
|
|
m_edit.SetWindowText(strTemp) ;
|
|
m_edit.MoveWindow(rect) ;
|
|
m_edit.SetSel(0, -1) ;
|
|
m_edit.ShowWindow(SW_SHOW) ;
|
|
m_edit.SetFocus() ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
// Save any value that was being edited and hide the edit box to end
|
|
// editing.
|
|
|
|
SaveValue() ;
|
|
m_edit.ShowWindow (SW_HIDE);
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
|
|
BOOL CFullEditListCtrl::PreTranslateMessage(MSG* pMsg)
|
|
{
|
|
switch (pMsg->message)
|
|
{
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
if (this == GetFocus())
|
|
{
|
|
switch (pMsg->wParam)
|
|
{
|
|
case VK_END:
|
|
case VK_HOME:
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
case VK_INSERT:
|
|
case VK_RETURN:
|
|
SendMessage (pMsg->message, pMsg->wParam, pMsg->lParam);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (&m_edit == GetFocus ())
|
|
{
|
|
switch (pMsg->wParam)
|
|
{
|
|
case VK_END:
|
|
case VK_HOME:
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
case VK_RETURN:
|
|
case VK_ESCAPE:
|
|
case VK_TAB:
|
|
m_edit.SendMessage (pMsg->message, pMsg->wParam, pMsg->lParam);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return CWnd::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
// Prepare to check the key
|
|
|
|
LV_KEYDOWN* pKeyDown = (LV_KEYDOWN*) pNMHDR ;
|
|
*pResult = 0 ;
|
|
CString strPrompt ;
|
|
UINT iSelCount ;
|
|
|
|
// Process the keys we're interested in.
|
|
|
|
switch (pKeyDown->wVKey) {
|
|
// Edit the first column of the first selected row.
|
|
|
|
case VK_RETURN:
|
|
EditCurRowSpecCol(0) ;
|
|
break ;
|
|
|
|
// Delete all of the selected rows. (Don't do anything if the
|
|
// MF_IGNOREDELETE miscellaneous flag is set.)
|
|
|
|
case VK_DELETE:
|
|
if (m_dwMiscFlags & MF_IGNOREDELETE)
|
|
return ;
|
|
iSelCount = GetSelectedCount ();
|
|
if (0 == iSelCount)
|
|
return;
|
|
strPrompt.LoadString(IDS_Delete2ItemQuery) ;
|
|
if (IDYES == MessageBox (strPrompt, NULL, MB_YESNO | MB_ICONQUESTION))
|
|
{
|
|
int iSelIndex = GetNextItem (-1, LVNI_SELECTED);
|
|
int nrow = iSelIndex ;
|
|
while (iSelIndex != -1)
|
|
{
|
|
VERIFY (DeleteItem (iSelIndex));
|
|
iSelIndex = GetNextItem (-1, LVNI_SELECTED);
|
|
}
|
|
if (nrow > -1)
|
|
SendChangeNotification(nrow, 0) ;
|
|
}
|
|
SetFocus ();
|
|
break ;
|
|
|
|
// Insert a row and begin editing it. (Don't do anything if the
|
|
// MF_IGNOREINSERT miscellaneous flag is set.)
|
|
|
|
case VK_INSERT:
|
|
if (m_dwMiscFlags & MF_IGNOREINSERT)
|
|
return ;
|
|
// Add a new row above the selected row or at the bottom of the
|
|
// list if nothing is selected.
|
|
|
|
int iIndex = GetNextItem (-1, LVNI_SELECTED);
|
|
if (-1 == iIndex)
|
|
iIndex = GetItemCount ();
|
|
ASSERT (-1 != iIndex);
|
|
InsertItem (iIndex, "");
|
|
SetItemData(iIndex, m_nNextItemData++) ;
|
|
|
|
// Make sure that the new row is visible and the only one selected.
|
|
|
|
SingleSelect(iIndex) ;
|
|
|
|
// Get the size and position of column 0 in the new row.
|
|
|
|
CRect rect;
|
|
GetItemRect (iIndex, rect, LVIR_BOUNDS);
|
|
rect.right = rect.left + GetColumnWidth (0);
|
|
|
|
// Start editing column 0.
|
|
|
|
m_nRow = iIndex ;
|
|
m_nColumn = 0 ;
|
|
m_edit.MoveWindow (rect);
|
|
m_edit.SetWindowText ("");
|
|
m_edit.ShowWindow (SW_SHOW);
|
|
m_edit.SetFocus ();
|
|
break ;
|
|
} ;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::SaveValue()
|
|
{
|
|
//TRACE("In SaveValue()\n") ;
|
|
|
|
// Just return false if the edit box isn't visible/editing.
|
|
|
|
if (!m_edit.IsWindowVisible()) {
|
|
//TRACE("Leaving SaveValue() because edit box not visible.\n") ;
|
|
return false ;
|
|
} ;
|
|
|
|
// Get the position and size of the edit box now that we know it is
|
|
// visible.
|
|
|
|
CRect rect ;
|
|
m_edit.GetWindowRect(rect) ;
|
|
ScreenToClient(rect) ;
|
|
|
|
// Prepare to find out what row/column is being edited.
|
|
|
|
POINT pt ;
|
|
pt.x = rect.left ;
|
|
pt.y = rect.top ;
|
|
int iRow = -1, iCol = -1 ;
|
|
|
|
// If the list box cell can be determined, save the edit box's contents in
|
|
// that cell.
|
|
|
|
if (GetPointRowCol(&pt, iRow, iCol, rect)) {
|
|
CString strTemp ;
|
|
m_edit.GetWindowText(strTemp) ;
|
|
VERIFY(SetItemText(iRow, iCol, strTemp)) ;
|
|
SendChangeNotification(iRow, iCol) ;
|
|
} ;
|
|
|
|
// The text in the edit box was saved in the appropriate list box cell
|
|
// when it can be so return true to indicate this.
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::HideEditBox()
|
|
{
|
|
m_edit.ShowWindow(SW_HIDE);
|
|
SetFocus() ;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::GetColumnData(CObArray* pcoadata, int ncolnum)
|
|
{
|
|
// Fail if the column number is bad.
|
|
|
|
if (ncolnum < 0 || ncolnum >= m_nNumColumns)
|
|
return false ;
|
|
|
|
// Clear and then initialize the array
|
|
|
|
pcoadata->RemoveAll() ;
|
|
int numitems = GetItemCount() ;
|
|
PCOLINFO pci = m_pciColInfo + ncolnum ;
|
|
switch (pci->cdttype) {
|
|
case COLDATTYPE_STRING:
|
|
case COLDATTYPE_TOGGLE:
|
|
case COLDATTYPE_CUSTEDIT:
|
|
// Extra initialization needed for string arrays.
|
|
((CStringArray*) pcoadata)->SetSize(numitems) ;
|
|
break ;
|
|
default:
|
|
pcoadata->SetSize(numitems) ;
|
|
} ;
|
|
|
|
// Declare and initialize the Item structure
|
|
|
|
LV_ITEM lvi ;
|
|
char acitemtext[4096] ;
|
|
lvi.mask = LVIF_TEXT ;
|
|
lvi.iSubItem = ncolnum ;
|
|
lvi.pszText = acitemtext ;
|
|
lvi.cchTextMax = 4095 ;
|
|
int npos ;
|
|
CString cscell ;
|
|
|
|
// Load the column's data into the array in a way based on the data type.
|
|
|
|
for (int n = 0 ; n < numitems ; n++) {
|
|
lvi.iItem = n ;
|
|
VERIFY(GetItem(&lvi)) ;
|
|
switch ((m_pciColInfo + ncolnum)->cdttype) {
|
|
case COLDATTYPE_INT:
|
|
((CUIntArray*) pcoadata)->SetAt(n, atoi(acitemtext)) ;
|
|
//TRACE("Set col %d sub %d to %d\n", ncolnum, n, ((CUIntArray*) pcoadata)->GetAt(n)) ;
|
|
break ;
|
|
case COLDATTYPE_STRING:
|
|
case COLDATTYPE_TOGGLE:
|
|
((CStringArray*) pcoadata)->SetAt(n, acitemtext) ;
|
|
//TRACE("Set col %d sub %d to '%s'\n", ncolnum, n, ((CStringArray*) pcoadata)->GetAt(n)) ;
|
|
break ;
|
|
case COLDATTYPE_CUSTEDIT:
|
|
cscell = acitemtext ;
|
|
if ((npos = cscell.Find(_T(" ..."))) >= 0)
|
|
cscell = cscell.Left(npos) ;
|
|
((CStringArray*) pcoadata)->SetAt(n, cscell) ;
|
|
//TRACE("Set col %d sub %d to '%s'\n", ncolnum, n, ((CStringArray*) pcoadata)->GetAt(n)) ;
|
|
break ;
|
|
default:
|
|
ASSERT(0) ;
|
|
} ;
|
|
} ;
|
|
|
|
// Return true because the columns data was saved.
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::SetColumnData(CObArray* pcoadata, int ncolnum)
|
|
{
|
|
// Get the number (sub)items to be loaded into the column. If there are
|
|
// more (sub)items than there are rows, add extra rows.
|
|
|
|
int numitems = (int)pcoadata->GetSize() ;
|
|
int noldnumitems = GetItemCount() ;
|
|
if (numitems > noldnumitems) {
|
|
SetItemCount(numitems) ;
|
|
m_nNextItemData = numitems + 1 ;
|
|
} ;
|
|
|
|
// Load the data into the column. If the data aren't strings, they are
|
|
// converted into strings first. The width is checked when necessary.
|
|
|
|
CString csitem ;
|
|
COLDATTYPE cdtdatatype = (m_pciColInfo + ncolnum)->cdttype ;
|
|
for (int n = 0 ; n < numitems ; n++) {
|
|
// Get the string to load.
|
|
|
|
switch (cdtdatatype) {
|
|
case COLDATTYPE_INT:
|
|
csitem.Format("%d", pcoadata->GetAt(n)) ;
|
|
break ;
|
|
case COLDATTYPE_STRING:
|
|
case COLDATTYPE_TOGGLE:
|
|
case COLDATTYPE_CUSTEDIT:
|
|
csitem = ((CStringArray*) pcoadata)->GetAt(n) ;
|
|
break ;
|
|
default:
|
|
ASSERT(0) ;
|
|
} ;
|
|
|
|
// Load the item into the appropriate row and column
|
|
|
|
if (n >= noldnumitems && ncolnum == 0) {
|
|
VERIFY(InsertItem(n, csitem) != -1) ;
|
|
SetItemData(n, n);
|
|
} else
|
|
VERIFY(SetItem(n, ncolnum, LVIF_TEXT, csitem, -1, 0, 0, n)) ;
|
|
} ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::SetCurRow(int nrow)
|
|
{
|
|
// First remove the focus and selection from any rows that have it now.
|
|
|
|
int nr = -1 ;
|
|
for ( ; (nr = GetNextItem(nr, LVNI_SELECTED)) != -1 ; )
|
|
SetItem(nr,0,LVIF_STATE,NULL,-1,LVIS_SELECTED,0,nr) ;
|
|
|
|
// Now set the new row.
|
|
|
|
SetItem(nrow, 0, LVIF_STATE, NULL, -1, LVIS_FOCUSED+LVIS_SELECTED,
|
|
LVIS_FOCUSED+LVIS_SELECTED, nrow) ;
|
|
m_nRow = nrow ;
|
|
}
|
|
|
|
|
|
int CALLBACK CFullEditListCtrl::SortListData(LPARAM lp1, LPARAM lp2, LPARAM lp3)
|
|
{
|
|
// Get pointer to associated class instance
|
|
|
|
CFullEditListCtrl* pcfelc = (CFullEditListCtrl*) lp3 ;
|
|
|
|
// Try to find the item indexes. This should not fail.
|
|
|
|
LV_FINDINFO lvfi ;
|
|
lvfi.flags = LVFI_PARAM ;
|
|
lvfi.lParam = lp1 ;
|
|
int nitem1, nitem2 ;
|
|
VERIFY((nitem1 = pcfelc->FindItem(&lvfi)) != -1) ;
|
|
lvfi.lParam = lp2 ;
|
|
VERIFY((nitem2 = pcfelc->FindItem(&lvfi)) != -1) ;
|
|
|
|
// Now get the item data. Again, this should not fail.
|
|
|
|
LV_ITEM lvi1, lvi2 ;
|
|
char acitemtext1[4096], acitemtext2[4096] ;
|
|
lvi1.mask = lvi2.mask = LVIF_TEXT ;
|
|
lvi1.iItem = nitem1 ;
|
|
lvi2.iItem = nitem2 ;
|
|
lvi1.iSubItem = lvi2.iSubItem = pcfelc->m_nSortColumn ;
|
|
lvi1.pszText = acitemtext1 ;
|
|
lvi2.pszText = acitemtext2 ;
|
|
lvi1.cchTextMax = lvi2.cchTextMax = 4095 ;
|
|
VERIFY(pcfelc->GetItem(&lvi1)) ;
|
|
VERIFY(pcfelc->GetItem(&lvi2)) ;
|
|
|
|
// Convert the item text when necessary and compare the items.
|
|
|
|
int ncompresult, inum1, inum2 ;
|
|
PCOLINFO pci = pcfelc->m_pciColInfo + pcfelc->m_nSortColumn ;
|
|
switch (pci->cdttype) {
|
|
case COLDATTYPE_INT:
|
|
inum1 = atoi(acitemtext1) ;
|
|
inum2 = atoi(acitemtext2) ;
|
|
ncompresult = inum1 - inum2 ;
|
|
break ;
|
|
case COLDATTYPE_STRING:
|
|
case COLDATTYPE_TOGGLE:
|
|
case COLDATTYPE_CUSTEDIT:
|
|
ncompresult = _stricmp(acitemtext1, acitemtext2) ;
|
|
break ;
|
|
default:
|
|
ASSERT(0) ;
|
|
} ;
|
|
|
|
// Return the result of the comparison. Reverse it before returning if
|
|
// sorting in descending order.
|
|
|
|
return ((pci->basc) ? ncompresult : 0 - ncompresult) ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult)
|
|
{
|
|
// Save any in progress editing and shut it down before doing anything else.
|
|
|
|
EndEditing(true) ;
|
|
|
|
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
|
|
|
|
// Get the sort column and send it to the sort control routine.
|
|
|
|
SortControl(pNMListView->iSubItem) ;
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::SortControl(int nsortcolumn)
|
|
{
|
|
CWaitCursor cwc ;
|
|
|
|
// Save any in progress editing and shut it down before doing anything else.
|
|
|
|
EndEditing(true) ;
|
|
|
|
// Get a pointer to the column info structure
|
|
|
|
PCOLINFO pci = m_pciColInfo + nsortcolumn ;
|
|
|
|
// If the column is not sortable, just return false.
|
|
|
|
if (!pci->bsortable)
|
|
return false ;
|
|
|
|
// Save the sort column, reverse the sort flag, and then sort the data.
|
|
|
|
m_nSortColumn = nsortcolumn ;
|
|
pci->basc = !pci->basc ;
|
|
SortItems(SortListData, (LPARAM) this) ;
|
|
|
|
// Data should have been sorted so...
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::SingleSelect(int nitem)
|
|
{
|
|
int nselitem ; // Selected item index
|
|
|
|
// First, deselect all of the currently selected rows.
|
|
|
|
nselitem = -1 ;
|
|
while ((nselitem = GetNextItem(nselitem, LVNI_SELECTED)) != -1)
|
|
SetItemState(nselitem, 0, LVIS_SELECTED | LVIS_FOCUSED) ;
|
|
|
|
// Now select the requested row (item) and make it visible.
|
|
|
|
SetItemState(nitem, LVIS_SELECTED | LVIS_FOCUSED,
|
|
LVIS_SELECTED | LVIS_FOCUSED) ;
|
|
EnsureVisible(nitem, false) ;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::GetRowData(int nrow, CStringArray& csafields)
|
|
{
|
|
// Fail if the requested row number is too large.
|
|
|
|
if (nrow > GetItemCount())
|
|
return false ;
|
|
|
|
// Make sure that the string array is large enough to hold all of the
|
|
// strings in a row. IE, the row's item text and all of the subitems'
|
|
// text.
|
|
|
|
if (m_nNumColumns > csafields.GetSize())
|
|
csafields.SetSize(m_nNumColumns) ;
|
|
|
|
// Declare and initialize the Item structure
|
|
|
|
LV_ITEM lvi ;
|
|
char acitemtext[4096] ;
|
|
lvi.iItem = nrow ;
|
|
lvi.mask = LVIF_TEXT ;
|
|
lvi.pszText = acitemtext ;
|
|
lvi.cchTextMax = 4095 ;
|
|
|
|
// Get each field in the row and copy it into the fields array.
|
|
|
|
for (int n = 0 ; n < m_nNumColumns ; n++) {
|
|
lvi.iSubItem = n ;
|
|
VERIFY(GetItem(&lvi)) ;
|
|
csafields[n] = acitemtext ;
|
|
} ;
|
|
|
|
// All went well so...
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::EndEditing(bool bsave)
|
|
{
|
|
// Save the new value first if requested. Return if there is nothing to
|
|
// save or - when not saving - if nothing is being edited.
|
|
|
|
if (bsave) {
|
|
if (!SaveValue())
|
|
return false ;
|
|
} else
|
|
if (!m_edit.IsWindowVisible())
|
|
return false ;
|
|
|
|
// Now hide the edit box and give the focus back to the list control.
|
|
|
|
m_edit.ShowWindow(SW_HIDE) ;
|
|
SetFocus() ;
|
|
|
|
// Mission accomplished so...
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
bool CFullEditListCtrl::EditCurRowSpecCol(int ncolumn)
|
|
{
|
|
// Save the specified column number. If it is too big, reset it to 0.
|
|
// If the column isn't editable, try the next one. Return false if an
|
|
// editable column can't be found.
|
|
|
|
for (int n = 0 ; n < m_nNumColumns ; n++, ncolumn++) {
|
|
if (ncolumn >= m_nNumColumns)
|
|
ncolumn = 0 ;
|
|
if ((m_pciColInfo + ncolumn)->beditable)
|
|
break ;
|
|
} ;
|
|
if (n < m_nNumColumns)
|
|
m_nColumn = ncolumn ;
|
|
else
|
|
return false ;
|
|
|
|
// Get the first currently selected row number. Use row number 0 if no
|
|
// row is selected.
|
|
|
|
if ((m_nRow = GetNextItem(-1, LVNI_SELECTED)) == -1)
|
|
m_nRow = 0 ;
|
|
|
|
// Make the current row, the only selected row and make sure it is visible.
|
|
|
|
SingleSelect(m_nRow) ;
|
|
|
|
// Now determine the dimensions of the specified column. Begin this by
|
|
// getting the dimensions of the entire row...
|
|
|
|
CRect rect ;
|
|
VERIFY(GetItemRect(m_nRow, rect, LVIR_BOUNDS)) ;
|
|
|
|
// ...Finish by looping through all of the columns getting their dimensions
|
|
// and using those dimensions to adjust the left side of the rectangle
|
|
// until the current column is reached. Then adjust the right side of the
|
|
// rectangle.
|
|
|
|
LV_COLUMN lvc ;
|
|
for (int ncol = 0 ; ncol <= m_nColumn ; ncol++) {
|
|
ZeroMemory(&lvc, sizeof(LV_COLUMN)) ;
|
|
lvc.mask = LVCF_WIDTH ;
|
|
GetColumn(ncol, &lvc) ;
|
|
if (ncol < m_nColumn)
|
|
rect.left += lvc.cx ;
|
|
} ;
|
|
rect.right = rect.left + lvc.cx ;
|
|
|
|
// Load, position, size, and show the edit box.
|
|
|
|
CString strTemp = GetItemText(m_nRow, m_nColumn) ;
|
|
m_edit.SetWindowText(strTemp) ;
|
|
m_edit.MoveWindow(rect) ;
|
|
m_edit.SetSel(0, -1) ;
|
|
m_edit.ShowWindow(SW_SHOW) ;
|
|
m_edit.SetFocus() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::OnVScroll(UINT nSBCode, UINT nPos,
|
|
CScrollBar* pScrollBar)
|
|
{
|
|
// Things get screwy if the list control scrolls while an item is being
|
|
// edited so end editing before allowing scrolling to take place.
|
|
|
|
EndEditing(true) ;
|
|
|
|
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::OnHScroll(UINT nSBCode, UINT nPos,
|
|
CScrollBar* pScrollBar)
|
|
{
|
|
// Things get screwy if the list control scrolls while an item is being
|
|
// edited so end editing before allowing scrolling to take place.
|
|
|
|
EndEditing(true) ;
|
|
|
|
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFullEditListCtrl::CheckHandleToggleColumns
|
|
|
|
First check to see if the row or column has togglable. If not, just return.
|
|
If the row is togglable, find the togglable column (cell). Then either set
|
|
its contents if it is empty or clear it if it is not empty.
|
|
|
|
Return true if a toggle-able column was found and toggled. Otherwise, return
|
|
false.
|
|
|
|
******************************************************************************/
|
|
|
|
bool CFullEditListCtrl::CheckHandleToggleColumns(int nrow, int ncol,
|
|
PCOLINFO pci)
|
|
{
|
|
// Return if there is no togglable column in this list.
|
|
|
|
if (!(m_dwToggleFlags & TF_HASTOGGLECOLUMNS))
|
|
return false ;
|
|
|
|
// If a specific togglable column must be clicked and that column was not
|
|
// clicked, just return.
|
|
|
|
if (m_dwToggleFlags & TF_CLICKONCOLUMN) {
|
|
if (pci->cdttype != COLDATTYPE_TOGGLE)
|
|
return false ;
|
|
|
|
// If any part of the row can be double-clicked to toggle the togglable
|
|
// column, find that column. Return if there is no such column.
|
|
|
|
} else {
|
|
pci = m_pciColInfo ;
|
|
for (ncol = 0 ; ncol < m_nNumColumns ; ncol++, pci++) {
|
|
if (pci->cdttype == COLDATTYPE_TOGGLE)
|
|
break ;
|
|
} ;
|
|
if (ncol >= m_nNumColumns)
|
|
return false ;
|
|
} ;
|
|
|
|
// Get the contents of the specified cell.
|
|
|
|
CString strcell = GetItemText(nrow, ncol) ;
|
|
|
|
// If the cell is empty, load it with the toggle string. If the cell is
|
|
// not empty, clear its contents.
|
|
|
|
if (strcell.IsEmpty())
|
|
VERIFY(SetItemText(nrow, ncol, pci->lpctstrtoggle)) ;
|
|
else
|
|
VERIFY(SetItemText(nrow, ncol, _T(""))) ;
|
|
SendChangeNotification(nrow, ncol) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFullEditListCtrl::CheckHandleCustEditColumn
|
|
|
|
First check to see if the row or column may contain a cell may be one that is
|
|
only editable with a custom edit routine. If not, just return. Next,
|
|
make sure that the cell not only might be of this type but actually is of this
|
|
type. If not, just return.
|
|
|
|
Now, get the contents of the selected cell and remove the ellipses from the
|
|
end of it. (The ellipses will be replaced before the new string is put back
|
|
into the cell.)
|
|
|
|
Next, comes the tricky part. Use the owner class pointer to call the routine
|
|
in that class which will manage the work of editting the cell's contents.
|
|
Although CFullEditListCtrl is supposed to be a generic class, this routine
|
|
must know what the owner class is so that the management routine can be
|
|
called. This means that extra, owner class specific, code must be added to
|
|
this routine each time a new owner class is added that uses this feature in
|
|
CFullEditListCtrl.
|
|
|
|
If the edit request was handled via a subordinate dialog box, return true.
|
|
Otherwise, return false.
|
|
|
|
******************************************************************************/
|
|
|
|
bool CFullEditListCtrl::CheckHandleCustEditColumn(int nrow, int ncol,
|
|
PCOLINFO pci)
|
|
{
|
|
// Return if there is no custom edit column in this list.
|
|
|
|
if (!(m_dwCustEditFlags & CEF_HASTOGGLECOLUMNS))
|
|
return false ;
|
|
|
|
// Return if the selected row does not contain a custom edit cell.
|
|
|
|
int n = (int)m_cuiaCustEditRows.GetSize() ;
|
|
ASSERT(m_cuiaCustEditRows.GetSize()) ;
|
|
if (nrow >= m_cuiaCustEditRows.GetSize() || m_cuiaCustEditRows[nrow] == 0)
|
|
return false ;
|
|
|
|
// If a specific custom edit column must be clicked and that column was not
|
|
// clicked, just return.
|
|
|
|
if (m_dwCustEditFlags & CEF_CLICKONCOLUMN) {
|
|
if (pci->cdttype != COLDATTYPE_CUSTEDIT)
|
|
return false ;
|
|
|
|
// If any part of the row can be double-clicked to edit the custom edit
|
|
// column, find that column. Return if there is no such column.
|
|
|
|
} else {
|
|
pci = m_pciColInfo ;
|
|
for (ncol = 0 ; ncol < m_nNumColumns ; ncol++, pci++) {
|
|
if (pci->cdttype == COLDATTYPE_CUSTEDIT)
|
|
break ;
|
|
} ;
|
|
if (ncol >= m_nNumColumns)
|
|
return false ;
|
|
} ;
|
|
|
|
// Get the contents of the specified cell and remove the ellipses from the
|
|
// end of it.
|
|
|
|
CString strcell = GetItemText(nrow, ncol) ;
|
|
int npos ;
|
|
if ((npos = strcell.Find(_T(" ..."))) >= 0)
|
|
strcell = strcell.Left(npos) ;
|
|
|
|
// Find the class for the owner and use that information to call the
|
|
// management routine. If this routine "fails" or is cancelled, return
|
|
// true without updating the cell.
|
|
|
|
bool brc ;
|
|
brc = (*m_lpCellEditProc)(m_pcoOwner, nrow, ncol, &strcell) ;
|
|
if (!brc)
|
|
return true ;
|
|
|
|
// Add the ellipses back on to the cell's new contents and update the cell.
|
|
|
|
strcell += _T(" ...") ;
|
|
VERIFY(SetItemText(nrow, ncol, strcell)) ;
|
|
SendChangeNotification(nrow, ncol) ;
|
|
SetFocus() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
void CFullEditListCtrl::SendChangeNotification(int nrow, int ncol)
|
|
{
|
|
// Do nothing if the owner does not want to be notified of a change in one
|
|
// of the cells in the list control.
|
|
|
|
if (!(m_dwMiscFlags & MF_SENDCHANGEMESSAGE))
|
|
return ;
|
|
|
|
// Send the message
|
|
|
|
::PostMessage(GetParent()->m_hWnd, WM_LISTCELLCHANGED, nrow, ncol) ;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFlagsListBox
|
|
|
|
CFlagsListBox::CFlagsListBox()
|
|
{
|
|
m_bReady = false ;
|
|
m_nGrpCnt = 0 ;
|
|
}
|
|
|
|
CFlagsListBox::~CFlagsListBox()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CFlagsListBox, CListBox)
|
|
//{{AFX_MSG_MAP(CFlagsListBox)
|
|
ON_CONTROL_REFLECT(LBN_DBLCLK, OnDblclk)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFlagsListBox::Init
|
|
|
|
Save the parameters that are used to control the behaviour of the list box.
|
|
Set the list box's tab stop. Then build the list box items based on the
|
|
flag names and settings and load them into the list box.
|
|
|
|
Args:
|
|
csafieldnames Flag field names
|
|
dwsettings Current flag setting(s)
|
|
cuiaflaggroupings Indexes for related groups of flags
|
|
ngrpcnt Number of flag groups
|
|
lptstrsetstring String to display when a flag is set
|
|
ntabstop List box tab stop, sets column widths
|
|
bnoclear True iff dbl-clk doesn't clear a flag that is set
|
|
nocleargrp -1 or only 0-based group number that bnoclear applies to
|
|
|
|
*** IMPORTANT NOTE ***
|
|
See OnDblclk() to find out how the data in m_cuiaFlagGroupings and m_nGrpCnt
|
|
are interpreted.
|
|
|
|
******************************************************************************/
|
|
|
|
bool CFlagsListBox::Init(CStringArray& csafieldnames, DWORD dwsettings,
|
|
CUIntArray& cuiaflaggroupings, int ngrpcnt,
|
|
LPTSTR lptstrsetstring, int ntabstop,
|
|
bool bnoclear /*=false*/, int nocleargrp /*=-1*/)
|
|
{
|
|
// Check parameters
|
|
|
|
ASSERT(csafieldnames.GetSize()) ;
|
|
ASSERT(cuiaflaggroupings.GetSize() >= (ngrpcnt * 2)) ;
|
|
ASSERT(lptstrsetstring) ;
|
|
ASSERT(ntabstop > 0) ;
|
|
|
|
// Copy parameters needed later
|
|
|
|
m_cuiaFlagGroupings.Copy(cuiaflaggroupings) ;
|
|
m_nGrpCnt = ngrpcnt ;
|
|
m_csSetString = lptstrsetstring ;
|
|
m_nNumFields = (int)csafieldnames.GetSize() ;
|
|
m_bNoClear = bnoclear ;
|
|
m_nNoClearGrp = nocleargrp ;
|
|
|
|
// Make sure the list box is empty.
|
|
|
|
ResetContent() ;
|
|
|
|
// Set the position of the list box's tabstop. This value is passed in
|
|
// because it is a dialog box specific value.
|
|
|
|
SetTabStops(1, &ntabstop) ;
|
|
|
|
// Now combine the field names and settings and add them to the listbox.
|
|
|
|
CString cs ;
|
|
int nbit = 1 ;
|
|
for (int n = 0 ; n < m_nNumFields ; n++, nbit <<= 1) {
|
|
cs = (dwsettings & nbit) ? m_csSetString : _T("") ;
|
|
cs = csafieldnames[n] + _T("\t") + cs ;
|
|
AddString(cs) ;
|
|
} ;
|
|
|
|
// Make sure the list box has the focus.
|
|
|
|
SetFocus() ;
|
|
|
|
// All went well so...
|
|
|
|
m_bReady = true ;
|
|
return true ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFlagsListBox::Init2
|
|
|
|
The flag settings in pcssettings are in the form of a hex number represented
|
|
as a string that may begin with "0x". For example, "0xA23" and "A23". Turn
|
|
this string into a DWORD and pass this DWORD plus the rest of the parameters
|
|
to the other form of this function that takes the settings parameter as a
|
|
DWORD. It does the rest of the work.
|
|
|
|
Args:
|
|
csafieldnames Flag field names
|
|
pcssettings Current flag setting(s)
|
|
cuiaflaggroupings Indexes for related groups of flags
|
|
ngrpcnt Number of flag groups
|
|
lptstrsetstring String to display when a flag is set
|
|
ntabstop List box tab stop, sets column widths
|
|
bnoclear True iff dbl-clk doesn't clear a flag that is set
|
|
nocleargrp -1 or only 0-based group number that bnoclear applies to
|
|
|
|
*** IMPORTANT NOTE ***
|
|
See OnDblclk() to find out how the data in m_cuiaFlagGroupings and m_nGrpCnt
|
|
are interpreted.
|
|
|
|
******************************************************************************/
|
|
|
|
bool CFlagsListBox::Init2(CStringArray& csafieldnames, CString* pcssettings,
|
|
CUIntArray& cuiaflaggroupings, int ngrpcnt,
|
|
LPTSTR lptstrsetstring, int ntabstop,
|
|
bool bnoclear /*=false*/, int nocleargrp /*=-1*/)
|
|
{
|
|
// Turn pcssettings into a dword. (Skip passed "0x" if it is on the
|
|
// beginning of the string.)
|
|
|
|
LPTSTR lptstr, lptstr2 ;
|
|
lptstr = pcssettings->GetBuffer(16) ;
|
|
int n = pcssettings->GetLength() ;
|
|
*(lptstr + n) = 0 ;
|
|
if (*(lptstr + 1) == 'x')
|
|
lptstr += 2 ;
|
|
DWORD dwsettings = strtoul(lptstr, &lptstr2, 16) ;
|
|
|
|
// The other Init() finishes the job.
|
|
|
|
return (Init(csafieldnames, dwsettings, cuiaflaggroupings, ngrpcnt,
|
|
lptstrsetstring, ntabstop, bnoclear, nocleargrp)) ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFlagsListBox::GetNewFlagDWord()
|
|
|
|
Read the settings of the flags in the list box to determine the new flag
|
|
dword. Then return it.
|
|
|
|
******************************************************************************/
|
|
|
|
DWORD CFlagsListBox::GetNewFlagDWord()
|
|
{
|
|
DWORD dwflags = 0 ; // Flag dword built here
|
|
DWORD dwbit = 1 ; // Used to set bits that are on
|
|
|
|
// Loop through each flag in the list box
|
|
|
|
CString csentry, csvalue ;
|
|
int npos ;
|
|
for (int n = 0 ; n < m_nNumFields ; n++, dwbit <<= 1) {
|
|
// Get the current flag and isolate its setting.
|
|
|
|
GetText(n, csentry) ;
|
|
npos = csentry.Find(_T('\t')) ;
|
|
csvalue = csentry.Mid(npos + 1) ;
|
|
|
|
// If the current flag is set, turn on its bit in dwflags.
|
|
|
|
if (csvalue.GetLength())
|
|
dwflags |= dwbit ;
|
|
} ;
|
|
|
|
// Return the flag DWORD.
|
|
|
|
return dwflags ;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
CFlagsListBox::GetNewFlagString()
|
|
|
|
Use the other version of this routine to get the DWORD version of the flags.
|
|
Then convert it to a string a save it in csflags. "0x" may or may not be
|
|
prepended to the string.
|
|
|
|
******************************************************************************/
|
|
|
|
void CFlagsListBox::GetNewFlagString(CString* pcsflags, bool badd0x /*=true*/)
|
|
{
|
|
// Get the DWORD version of the flag.
|
|
|
|
DWORD dwflags = GetNewFlagDWord() ;
|
|
|
|
// Format the flags as a string and prepend "0x" when requested.
|
|
|
|
if (badd0x)
|
|
pcsflags->Format("0x%x", dwflags) ;
|
|
else
|
|
pcsflags->Format("%x", dwflags) ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFlagsListBox message handlers
|
|
|
|
/******************************************************************************
|
|
|
|
CFlagsListBox::OnDblclk()
|
|
|
|
First, toggle the setting of the selected item. At least, that is what is
|
|
usually done first. If m_bNoClear is set and the user has asked to clear a
|
|
flag, don't do this or anything else. Just return. Use m_bNoClear when at
|
|
least one flag must always be set. See #4 for more information on m_bNoClear.
|
|
|
|
Next, process the rest of the flags in the items flag group. The following
|
|
things can happen based on the group:
|
|
1. If the item is not in a group, do nothing else. This is useful when
|
|
any combination of flags can be set. The most efficient way to do this
|
|
is by setting m_nGrpCnt to 0.
|
|
2. The item indexes in m_cuiaFlagGroupings that contain the selected item
|
|
are positive. Only one of the specified flags can be set. If the
|
|
selected item was set, clear the rest of the flags in the group.
|
|
3. The item indexes in m_cuiaFlagGroupings that contain the selected item
|
|
are negative. (The absolute value of the indexes is used for tests.)
|
|
Change the state of the other flags in the group, too. Generally, this
|
|
only makes sense in a two flag group where one and only one of the flags
|
|
MUST be selected.
|
|
4. If m_bNoClear is set and m_nNoClearGrp = -1, m_bNoClear affects all
|
|
flags. If m_nNoClearGrp is >= 0, it refers to the single group that
|
|
m_bNoClear affects.
|
|
|
|
******************************************************************************/
|
|
|
|
void CFlagsListBox::OnDblclk()
|
|
{
|
|
// Do nothing if the edit control isn't ready, yet.
|
|
|
|
if (!m_bReady)
|
|
return ;
|
|
|
|
// Do nothing if no item is selected in the list box.
|
|
|
|
int nselitem ;
|
|
if ((nselitem = GetCurSel()) == LB_ERR)
|
|
return ;
|
|
|
|
// Get the listbox entry and split it into name and value components.
|
|
|
|
CString csentry, csname, csvalue ;
|
|
GetText(nselitem, csentry) ;
|
|
int npos = csentry.Find(_T('\t')) ;
|
|
csname = (npos > 0) ? csentry.Left(npos) : _T("") ;
|
|
csvalue = csentry.Mid(npos + 1) ;
|
|
|
|
// Find the flag grouping for the selected flag.
|
|
|
|
int n, nidx ;
|
|
for (n = nidx = 0 ; n < m_nGrpCnt ; n++, nidx += 2)
|
|
if (abs(m_cuiaFlagGroupings[nidx]) <= nselitem
|
|
&& abs(m_cuiaFlagGroupings[nidx+1]) >= nselitem)
|
|
break ;
|
|
|
|
// Determine the flags current state. If it is set and m_bNoClear == true,
|
|
// don't do anything based on the value of m_nNoClearGrp. Just return.
|
|
|
|
bool boldstate = !csvalue.IsEmpty() ;
|
|
if (m_bNoClear && boldstate && (m_nNoClearGrp == -1 || m_nNoClearGrp == n))
|
|
return ;
|
|
|
|
// Change the state of the selected flag.
|
|
|
|
csvalue = (boldstate) ? _T("") : m_csSetString ;
|
|
csentry = csname + _T('\t') + csvalue ;
|
|
DeleteString(nselitem) ;
|
|
InsertString(nselitem, csentry) ;
|
|
|
|
// Do nothing else if the selected flag is not in a flag grouping.
|
|
|
|
if (n >= m_nGrpCnt) {
|
|
SetCurSel(nselitem) ;
|
|
return ;
|
|
} ;
|
|
|
|
// Find out if this group has to have a flag set. If it doesn't and the
|
|
// selected flag was cleared, there is nothing left to do.
|
|
|
|
bool bmustset = ((int) m_cuiaFlagGroupings[nidx+1]) < 0 ;
|
|
if (!bmustset && boldstate) {
|
|
SetCurSel(nselitem) ;
|
|
return ;
|
|
} ;
|
|
|
|
// Loop through all of the flags in the current group. Change the setting
|
|
// of all flags in the group -- EXCEPT the selected flag -- to the old value
|
|
// of the selected flag.
|
|
|
|
n = abs(m_cuiaFlagGroupings[nidx+1]) ;
|
|
csvalue = (boldstate) ? m_csSetString : _T("") ;
|
|
for (nidx = abs(m_cuiaFlagGroupings[nidx]) ; nidx <= n ; nidx++) {
|
|
if (nidx == nselitem)
|
|
continue ;
|
|
GetText(nidx, csentry) ;
|
|
npos = csentry.Find(_T('\t')) ;
|
|
csname = (npos > 0) ? csentry.Left(npos + 1) : _T("") ;
|
|
csentry = csname + csvalue ;
|
|
DeleteString(nidx) ;
|
|
InsertString(nidx, csentry) ;
|
|
} ;
|
|
|
|
// Make sure that the flag selected by the user is still selected and
|
|
// return.
|
|
|
|
SetCurSel(nselitem) ;
|
|
return ;
|
|
}
|
|
|