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

1883 lines
52 KiB

  1. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3. //
  4. // comctrls.cpp
  5. //
  6. //
  7. // NOTES: an FWORD is a short int
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  11. #include "stdafx.h"
  12. #include <gpdparse.h>
  13. #include "comctrls.H"
  14. #include <stdlib.h>
  15. /////////////////////////////////////////////////////////////////////////////
  16. // The functions defined below for CEditControlEditBox and CEditControlListBox
  17. // are used to implement a lighter weight, more general purpose Edit control
  18. // than the UFM Editor specific classes that are defined above. (A normal
  19. // Edit Box is part of this Edit Control, too.)
  20. /////////////////////////////////////////////////////////////////////////////
  21. // CEditControlEditBox - Manages the Edit Box part of the Edit Control that
  22. // is used to hold the field name.
  23. CEditControlEditBox::CEditControlEditBox(CEditControlListBox* pceclb)
  24. {
  25. // Save a pointer to the corresponding list box.
  26. m_pceclb = pceclb ;
  27. }
  28. CEditControlEditBox::~CEditControlEditBox()
  29. {
  30. }
  31. BEGIN_MESSAGE_MAP(CEditControlEditBox, CEdit)
  32. //{{AFX_MSG_MAP(CEditControlEditBox)
  33. ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillfocus)
  34. //}}AFX_MSG_MAP
  35. END_MESSAGE_MAP()
  36. /////////////////////////////////////////////////////////////////////////////
  37. // CEditControlEditBox message handlers
  38. void CEditControlEditBox::OnKillfocus()
  39. {
  40. // Make sure the value is saved back into the list box.
  41. m_pceclb->SaveValue() ;
  42. }
  43. /////////////////////////////////////////////////////////////////////////////
  44. // CEditControlListBox
  45. CEditControlListBox::CEditControlListBox(CEdit* pce,
  46. CEditControlEditBox* pceceb)
  47. {
  48. // Save pointers to the other 2 controls that make up the Edit Control
  49. m_pceName = pce ;
  50. m_pcecebValue = pceceb ;
  51. m_bReady = false ; // Not ready for operations yet
  52. m_nCurSelIdx = -1 ; // Nothing is selected yet
  53. }
  54. CEditControlListBox::~CEditControlListBox()
  55. {
  56. }
  57. BEGIN_MESSAGE_MAP(CEditControlListBox, CListBox)
  58. //{{AFX_MSG_MAP(CEditControlListBox)
  59. ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelchange)
  60. ON_CONTROL_REFLECT(LBN_DBLCLK, OnDblclk)
  61. //}}AFX_MSG_MAP
  62. END_MESSAGE_MAP()
  63. bool CEditControlListBox::Init(CStringArray& csamodels, CStringArray& csafiles,
  64. int ntabstop)
  65. {
  66. int n ; // Loop counter and temp var
  67. // Make sure the list box is empty.
  68. ResetContent() ;
  69. // Set the position of the list box's tabstop. This value is passed in
  70. // because it is a dialog box specific value.
  71. SetTabStops(1, &ntabstop) ;
  72. // Now combine the model names and file names and add them to the listbox.
  73. //csafiles[0] = "MMMMMMMM" ;
  74. CString cs ;
  75. int nummodels = (int)csamodels.GetSize() ;
  76. for (n = 0 ; n < nummodels ; n++) {
  77. cs = csamodels[n] + _T("\t") + csafiles[n] ;
  78. AddString(cs) ;
  79. } ;
  80. // Set the file name length limit.
  81. m_pcecebValue->SetLimitText(8) ;
  82. // Initialize (zap) the contents of the edit boxes
  83. m_pceName->SetWindowText(_T("")) ;
  84. m_pcecebValue->SetWindowText(_T("")) ;
  85. // Reset the currently selected entry index
  86. m_nCurSelIdx = -1 ;
  87. // Make sure the list box has the focus.
  88. SetFocus() ;
  89. // Everything is ready to go now
  90. m_bReady = true ;
  91. return TRUE ;
  92. }
  93. /******************************************************************************
  94. CEditControlListBox::GetGPDInfo
  95. Load the provided array(s) with the field values and, optionally, the field
  96. names.
  97. ******************************************************************************/
  98. bool CEditControlListBox::GetGPDInfo(CStringArray& csavalues,
  99. CStringArray* pcsanames /*= NULL*/)
  100. {
  101. // First, make sure that the last value changed in the edit control
  102. // is saved.
  103. SaveValue() ;
  104. // Size the array(s) based on the number of entries in the list box.
  105. // Note: An empty list box is not an error.
  106. int numents = GetCount() ;
  107. if (numents <= 0)
  108. return true ;
  109. csavalues.SetSize(numents) ;
  110. if (pcsanames)
  111. pcsanames->SetSize(numents) ;
  112. // Loop through each entry in the list box, separate the entries into name
  113. // and value parts, and save the appropriate parts of the entry.
  114. CString csentry, csname, csvalue ;
  115. int npos ;
  116. for (int n = 0 ; n < numents ; n++) {
  117. GetText(n, csentry) ;
  118. npos = csentry.Find(_T('\t')) ;
  119. csvalue = csentry.Mid(npos + 1) ;
  120. csavalues[n] = csvalue ;
  121. if (pcsanames) {
  122. csname = (npos > 0) ? csentry.Left(npos) : _T("") ;
  123. pcsanames->SetAt(n, csname) ;
  124. } ;
  125. } ;
  126. return true ;
  127. }
  128. void CEditControlListBox::SelectLBEntry(int nidx, bool bsave /*=false*/)
  129. {
  130. // Select the specified entry
  131. SetCurSel(nidx) ;
  132. // If the caller doesn't want to save the previous selection, clear the
  133. // current selection index.
  134. if (!bsave)
  135. m_nCurSelIdx = -1 ;
  136. // Update the edit control.
  137. OnSelchange() ;
  138. } ;
  139. void CEditControlListBox::SaveValue(void)
  140. {
  141. // Do nothing if the edit control is not ready or nothing is loaded into
  142. // the edit boxes.
  143. if (!m_bReady || m_nCurSelIdx == -1)
  144. return ;
  145. // Get the string from the value edit box and from the selected entry in
  146. // the list box.
  147. CString csvalue, csentry ;
  148. m_pcecebValue->GetWindowText(csvalue) ;
  149. GetText(m_nCurSelIdx, csentry) ;
  150. // Replace the value in the entry with the value from the edit box and put
  151. // the new entry back into the list box.
  152. int npos = csentry.Find(_T('\t')) ;
  153. csentry = csentry.Left(npos + 1) + csvalue ;
  154. DeleteString(m_nCurSelIdx) ;
  155. InsertString(m_nCurSelIdx, csentry) ;
  156. }
  157. /////////////////////////////////////////////////////////////////////////////
  158. // CEditControlListBox message handlers
  159. void CEditControlListBox::OnSelchange()
  160. {
  161. // Do nothing if the edit control isn't ready, yet.
  162. if (!m_bReady)
  163. return ;
  164. // Do nothing if the selection didn't really change
  165. int nidx = GetCurSel() ;
  166. if (nidx == m_nCurSelIdx)
  167. return ;
  168. // Save the current value
  169. SaveValue() ;
  170. // Get the index of the currently selected list box entry. Return without
  171. // doing anything else if no entry is selected.
  172. if (nidx == LB_ERR)
  173. return ;
  174. // Get the listbox entry and split it into name and value components.
  175. CString csentry, csname, csvalue ;
  176. GetText(nidx, csentry) ;
  177. int npos = csentry.Find(_T('\t')) ;
  178. csname = (npos > 0) ? csentry.Left(npos) : _T("") ;
  179. csvalue = csentry.Mid(npos + 1) ;
  180. // Load the name into the name edit box and the value into the value edit
  181. // box.
  182. m_pceName->SetWindowText(csname) ;
  183. m_pcecebValue->SetWindowText(csvalue) ;
  184. // Save the index of the currently selected entry
  185. m_nCurSelIdx = nidx ;
  186. }
  187. void CEditControlListBox::OnDblclk()
  188. {
  189. // Do nothing if the edit control isn't ready, yet.
  190. if (!m_bReady)
  191. return ;
  192. // Do nothing if no item is selected in the list box.
  193. if (GetCurSel() == LB_ERR)
  194. return ;
  195. // Load the edit boxes
  196. OnSelchange() ;
  197. // Set the focus to the value control
  198. m_pcecebValue->SetFocus() ;
  199. }
  200. /////////////////////////////////////////////////////////////////////////////
  201. // The functions implement below the CFullEditListCtrl and CFELCEditBox
  202. // classes. Together, they implement support a List Control in Report View
  203. // in which subitems can be editted too, complete rows can be selected, and
  204. // the data can be sorted by numeric or text columns. CFELCEditBox is a
  205. // helper class that is only used by CFullEditListCtrl.
  206. //
  207. CFELCEditBox::CFELCEditBox()
  208. {
  209. }
  210. CFELCEditBox::~CFELCEditBox()
  211. {
  212. }
  213. BEGIN_MESSAGE_MAP(CFELCEditBox, CEdit)
  214. //{{AFX_MSG_MAP(CFELCEditBox)
  215. ON_WM_KILLFOCUS()
  216. ON_WM_KEYDOWN()
  217. //}}AFX_MSG_MAP
  218. END_MESSAGE_MAP()
  219. /////////////////////////////////////////////////////////////////////////////
  220. // CFELCEditBox message handlers
  221. /******************************************************************************
  222. CFELCEditBox::OnKeyDown
  223. Handle the Escape (cancel) and Return (save) keys.
  224. ******************************************************************************/
  225. void CFELCEditBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  226. {
  227. // Get a pointer to the owning list control
  228. CFullEditListCtrl* plist = (CFullEditListCtrl*) GetParent() ;
  229. ASSERT (plist);
  230. // Handle the interesting keys
  231. switch (nChar) {
  232. // End editing without saving
  233. case VK_ESCAPE:
  234. DefWindowProc(WM_KEYDOWN, 0, 0) ; // Is this right???
  235. plist->EndEditing(false) ;
  236. break ;
  237. // Save contents and end editing
  238. case VK_RETURN:
  239. DefWindowProc(WM_KEYDOWN, 0, 0) ; // Is this right???
  240. plist->EndEditing(true) ;
  241. break ;
  242. // Save contents, end editing, and begin editing next cell
  243. case VK_TAB:
  244. DefWindowProc (WM_KEYDOWN, 0, 0) ; // Is this right???
  245. if (!(plist->EndEditing(true)))
  246. return ;
  247. plist->EditCurRowSpecCol(plist->GetCurCol() + 1) ;
  248. break ;
  249. // Process the key normally
  250. default:
  251. CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
  252. } ;
  253. }
  254. /******************************************************************************
  255. CFELCEditBox::OnKillFocus
  256. Just hide the edit box when it looses the focus. The contents are "lost".
  257. ******************************************************************************/
  258. void CFELCEditBox::OnKillFocus(CWnd* pNewWnd)
  259. {
  260. // Save whatever is in the edit box first.
  261. CFullEditListCtrl* pList = (CFullEditListCtrl*)GetParent ();
  262. ASSERT (pList);
  263. pList->SaveValue() ;
  264. CEdit::OnKillFocus(pNewWnd);
  265. ShowWindow (SW_HIDE);
  266. }
  267. /////////////////////////////////////////////////////////////////////////////
  268. // CFullEditListCtrl
  269. /////////////////////////////////////////////////////////////////////////////
  270. // Message map
  271. BEGIN_MESSAGE_MAP(CFullEditListCtrl, CListCtrl)
  272. //{{AFX_MSG_MAP(CFullEditListCtrl)
  273. ON_WM_CREATE()
  274. ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
  275. ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
  276. ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown)
  277. ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
  278. ON_WM_VSCROLL()
  279. ON_WM_HSCROLL()
  280. //}}AFX_MSG_MAP
  281. END_MESSAGE_MAP()
  282. /////////////////////////////////////////////////////////////////////////////
  283. // CFullEditListCtrl::CFullEditListCtrl - Constructor
  284. CFullEditListCtrl::CFullEditListCtrl()
  285. {
  286. // Initialize member variable(s)
  287. m_pciColInfo = NULL ;
  288. m_nRow = m_nColumn = m_nNumColumns = -1 ;
  289. m_nNextItemData = 1 ;
  290. m_pcoOwner = NULL ;
  291. m_dwCustEditFlags = m_dwToggleFlags = m_dwMiscFlags = 0 ;
  292. }
  293. /////////////////////////////////////////////////////////////////////////////
  294. // CFullEditListCtrl::~CFullEditListCtrl - Destructor
  295. CFullEditListCtrl::~CFullEditListCtrl()
  296. {
  297. // Free memory used
  298. if (m_pciColInfo != NULL)
  299. delete m_pciColInfo ;
  300. }
  301. /////////////////////////////////////////////////////////////////////////////
  302. // CFullEditListCtrl message handlers
  303. int CFullEditListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
  304. {
  305. // I don't think anything else needs to be done here.
  306. return (CWnd::OnCreate(lpCreateStruct));
  307. }
  308. /******************************************************************************
  309. CFullEditListCtrl::InitControl
  310. Handles control "global" initialization for CFullEditListCtrl.
  311. Args:
  312. dwaddlexstyles Extend styles for list control
  313. numrows Number of rows of data that will be loaded into the control
  314. numcols Number of columns of data that will be loaded into control
  315. dwtoggleflags Flags describing if/how a column in the list can be toggled
  316. (See TF_XXXX definitions in comctrls.cpp.)
  317. neditlen Optional max length of edittable column data strings
  318. dwmiscflags Miscellaneous flags for controlling the list control
  319. (See MF_XXXX definitions in comctrls.cpp.)
  320. Note:
  321. The two main initialization routines, InitControl() for global
  322. initialization and InitLoadColumn() for column specific initialization,
  323. require a lot of arguments already and I don't want to clutter them with
  324. more. Be that as it may, this control is still being enhanced so I have
  325. added extra initialization routines when needed. They are called
  326. ExtraInit_XXXX(). Read the comment header for each routine to find out
  327. what they do and when/if they should be called. These routines may handle
  328. a mixture of list global and/or per column initialization.
  329. ******************************************************************************/
  330. void CFullEditListCtrl::InitControl(DWORD dwaddlexstyles, int numrows,
  331. int numcols, DWORD dwtoggleflags/*=0*/,
  332. int neditlen/*=0*/, int dwmiscflags /*=0*/)
  333. {
  334. // Add any additional, extended styles to the list control.
  335. if (dwaddlexstyles != 0) {
  336. DWORD dwExStyle = (DWORD)SendMessage (LVM_GETEXTENDEDLISTVIEWSTYLE);
  337. dwExStyle |= dwaddlexstyles;
  338. SendMessage (LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwExStyle);
  339. } ;
  340. // Set the number of rows in the list control.
  341. SetItemCount(numrows) ;
  342. // Allocate the structures used to track info on each column.
  343. if (m_pciColInfo != NULL)
  344. delete m_pciColInfo ;
  345. m_pciColInfo = new COLINFO[numcols] ;
  346. m_nNumColumns = numcols ;
  347. // Create and initialize the edit control used for subitem editing.
  348. VERIFY(m_edit.Create(ES_AUTOHSCROLL | WS_BORDER | WS_CHILD,
  349. CRect(0,0,0,0), this, 2));
  350. m_edit.SetFont(GetFont()) ;
  351. if (neditlen > 0)
  352. m_edit.LimitText(neditlen) ;
  353. // Save the next item data number to use when and if new rows are inserted
  354. // after the columns are initially loaded.
  355. m_nNextItemData = numrows + 1 ;
  356. // Save the toggle amd miscellaneous flags
  357. m_dwToggleFlags = dwtoggleflags ;
  358. m_dwMiscFlags = dwmiscflags ;
  359. }
  360. /******************************************************************************
  361. CFullEditListCtrl::InitLoadColumn
  362. Initialize and load a specific CFullEditListCtrl column.
  363. Args:
  364. ncolnum Column number to initialize/load
  365. strlabel Column label
  366. nwidth Width of column or flag specifying how to compute width
  367. nwidthpad + or - adjustment to column width
  368. beditable True iff the column is editable
  369. bsortable True iff the rows can be sorted on this column
  370. cdtdatatype The type of data in the column
  371. pcoadata Pointer to array containing the columns data
  372. lpctstrtoggle If toggle-able column, the column's toggle string
  373. Note:
  374. The two main initialization routines, InitControl() for global
  375. initialization and InitLoadColumn() for column specific initialization,
  376. require a lot of arguments already and I don't want to clutter them with
  377. more. Be that as it may, this control is still being enhanced so I have
  378. added extra initialization routines when needed. They are called
  379. ExtraInit_XXXX(). Read the comment header for each routine to find out
  380. what they do and when/if they should be called. These routines may handle
  381. a mixture of list global and/or per column initialization.
  382. ******************************************************************************/
  383. int CFullEditListCtrl::InitLoadColumn(int ncolnum, LPCSTR strlabel, int nwidth,
  384. int nwidthpad, bool beditable,
  385. bool bsortable, COLDATTYPE cdtdatatype,
  386. CObArray* pcoadata,
  387. LPCTSTR lpctstrtoggle/*= NULL*/)
  388. {
  389. // Insert the column. Everything except numeric data is left justified.
  390. // Numeric data is right justified.
  391. int nfmt ;
  392. switch (cdtdatatype) {
  393. case COLDATTYPE_STRING:
  394. case COLDATTYPE_TOGGLE:
  395. case COLDATTYPE_CUSTEDIT:
  396. nfmt = LVCFMT_LEFT ;
  397. break ;
  398. case COLDATTYPE_INT:
  399. case COLDATTYPE_FLOAT:
  400. nfmt = LVCFMT_RIGHT ;
  401. break ;
  402. default:
  403. nfmt = LVCFMT_LEFT; //raid 116584 prefix
  404. ASSERT(0) ;
  405. }
  406. VERIFY(InsertColumn(ncolnum, strlabel, nfmt, -1, ncolnum - 1) >= 0) ;
  407. // Set flags based on how the column width should be set and initialize it
  408. // when necessary.
  409. bool bcompwidth, bwidthremainder ;
  410. if (bcompwidth = (nwidth == COMPUTECOLWIDTH)) {
  411. nwidth = GetStringWidth(strlabel) + 4 ; // Start with width of label
  412. bwidthremainder = false ;
  413. } else
  414. bwidthremainder = (nwidth == SETWIDTHTOREMAINDER) ;
  415. // Get the number (sub)items to be loaded into the column
  416. int numitems = (int)pcoadata->GetSize() ;
  417. // Load the data into the column. If the data aren't strings, they are
  418. // converted into strings first. If some of the data may be editted with
  419. // a custom edit routine, add elipses to those strings. The width is
  420. // checked when necessary.
  421. CString csitem ;
  422. for (int n = 0 ; n < numitems ; n++) {
  423. // Get the string to load.
  424. switch (cdtdatatype) {
  425. case COLDATTYPE_INT:
  426. csitem.Format("%d", pcoadata->GetAt(n)) ;
  427. break ;
  428. case COLDATTYPE_STRING:
  429. case COLDATTYPE_TOGGLE:
  430. csitem = ((CStringArray*) pcoadata)->GetAt(n) ;
  431. break ;
  432. case COLDATTYPE_CUSTEDIT:
  433. csitem = ((CStringArray*) pcoadata)->GetAt(n) ;
  434. ASSERT(m_cuiaCustEditRows.GetSize()) ;
  435. if (m_cuiaCustEditRows[n])
  436. csitem += _T(" ...") ;
  437. break ;
  438. default:
  439. ASSERT(0) ;
  440. } ;
  441. // Save the width of the current item if it is the longest found so far
  442. // and the width needs to be computed.
  443. if (bcompwidth)
  444. if (nwidth < GetStringWidth(csitem))
  445. nwidth = GetStringWidth(csitem) ;
  446. // Load the item into the appropriate row and column
  447. if (ncolnum == 0) {
  448. VERIFY(InsertItem(n, csitem) != -1) ;
  449. SetItemData(n, n);
  450. } else
  451. VERIFY(SetItem(n, ncolnum, LVIF_TEXT, csitem, -1, 0, 0, n)) ;
  452. } ;
  453. // Determine the column width when the remainder is required and then set it.
  454. if (bwidthremainder) {
  455. CRect cr ;
  456. GetWindowRect(cr) ;
  457. nwidth = cr.Width() - 4 ;
  458. for (n = 0 ; n < ncolnum ; n++)
  459. nwidth -= (m_pciColInfo + n)->nwidth ;
  460. } ;
  461. SetColumnWidth(ncolnum, nwidth + nwidthpad) ;
  462. // Save info about the column
  463. PCOLINFO pci = (m_pciColInfo + ncolnum) ;
  464. pci->nwidth = nwidth ;
  465. pci->beditable = beditable ;
  466. pci->cdttype = cdtdatatype ;
  467. pci->bsortable = bsortable ;
  468. pci->basc = false ;
  469. pci->lpctstrtoggle = lpctstrtoggle ;
  470. // Return the width of the column
  471. return nwidth ;
  472. }
  473. /******************************************************************************
  474. CFullEditListCtrl::ExtraInit_CustEditCol
  475. This routine is called when *ONE* column in the list control contains some
  476. cells whose contents are editted by a custom edit routine. When such a
  477. cell is selected, one of the list's owner's member functions is called to
  478. manage the work (show dlg, etc) needed to edit the cell's contents. Some of
  479. the column's cells are editted normally. The rest are edited via a
  480. custom edit routine. Cuiacusteditrows contains data that indicates which
  481. cells are which.
  482. Args:
  483. ncolnum Custom edit column number
  484. pcoowner Pointer to this class instance's owner
  485. dwcusteditflags Custom edit flags
  486. cuiacusteditrows Rows in ncolnum that require custom editting
  487. Note:
  488. When needed, this routine should be called once; after InitControl() and
  489. before the InitLoadColumn() call for the custom edit column.
  490. InitLoadColumn() should be called for this column with COLDATTYPE_CUSTEDIT
  491. as one of its parameters.
  492. ******************************************************************************/
  493. bool CFullEditListCtrl::ExtraInit_CustEditCol(int ncolnum, CObject* pcoowner,
  494. DWORD dwcusteditflags,
  495. CUIntArray& cuiacusteditrows,
  496. LPCELLEDITPROC lpcelleditproc)
  497. {
  498. // Fail if the column number is invalid
  499. if (ncolnum < 0 || ncolnum >= m_nNumColumns)
  500. return false ;
  501. // Save copies of the input parameters for later use.
  502. m_pcoOwner = pcoowner ;
  503. m_dwCustEditFlags = dwcusteditflags ;
  504. m_cuiaCustEditRows.Copy(cuiacusteditrows) ;
  505. m_lpCellEditProc = lpcelleditproc ;
  506. return true ;
  507. }
  508. BOOL CFullEditListCtrl::GetPointRowCol(LPPOINT lpPoint, int& iRow, int& iCol,
  509. CRect& rect)
  510. {
  511. BOOL bFound = FALSE;
  512. // Get row number
  513. iRow = HitTest (CPoint (*lpPoint));
  514. if (-1 == iRow)
  515. return bFound;
  516. // Get the column number and the cell dimensions
  517. return (GetColCellRect(lpPoint, iRow, iCol, rect)) ;
  518. }
  519. BOOL CFullEditListCtrl::GetColCellRect(LPPOINT lpPoint, int& iRow, int& iCol,
  520. CRect& rect)
  521. {
  522. BOOL bFound = FALSE ;
  523. // Get the dimensions for the entire row.
  524. VERIFY(GetItemRect(iRow, rect, LVIR_BOUNDS)) ;
  525. // Prepare to get the width of each column in the row.
  526. int iCntr = 0 ;
  527. LV_COLUMN lvc ;
  528. ZeroMemory(&lvc, sizeof (LV_COLUMN)) ;
  529. lvc.mask = LVCF_WIDTH ;
  530. // Get the dimensions of each column until the one containing the point is
  531. // found.
  532. while (GetColumn(iCntr, &lvc)) {
  533. rect.right = rect.left + lvc.cx ;
  534. if (rect.PtInRect (*lpPoint)) {
  535. bFound = TRUE ;
  536. iCol = iCntr ;
  537. break ;
  538. } ;
  539. rect.left = rect.right ;
  540. iCntr++ ;
  541. ZeroMemory (&lvc, sizeof (LV_COLUMN)) ;
  542. lvc.mask = LVCF_WIDTH ;
  543. } ;
  544. // Return TRUE if the point is found in a cell. Otherwise, FALSE.
  545. return bFound ;
  546. }
  547. void CFullEditListCtrl::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
  548. {
  549. *pResult = 0 ; // Result always 0
  550. // Find out what point on the list control was clicked
  551. CPoint point ;
  552. VERIFY (::GetCursorPos(&point)) ;
  553. //TRACE("***OnDblclk: GetCursorPos x = %d y = %d", point.x, point.y) ;
  554. ScreenToClient(&point) ;
  555. //TRACE(" --- ScreenToClient x = %d y = %d\n", point.x, point.y) ;
  556. // Exit if the "click point" can't be mapped to a cell (item) on the list
  557. // control.
  558. int iRow = -1, iCol = -1 ;
  559. CRect rect ;
  560. if (!GetPointRowCol(&point, iRow, iCol, rect))
  561. return ;
  562. //TRACE("***OnDblclk: Passed GetPointRowCol() call.\n") ;
  563. // Exit if the cell cannot be made completely visible. Then get the cell's
  564. // position and dimensions again because it might have moved.
  565. if (!EnsureVisible(iRow, false))
  566. return ;
  567. CRect rect2 ;
  568. VERIFY(GetItemRect(iRow, rect2, LVIR_BOUNDS)) ;
  569. rect.top = rect2.top ;
  570. rect.bottom = rect2.bottom ;
  571. // If the column is not editable (editability takes precedence), check for
  572. // and - when appropriate - handle togglability. Then exit.
  573. PCOLINFO pci = m_pciColInfo + iCol ;
  574. if (!pci->beditable) {
  575. if (!CheckHandleToggleColumns(iRow, iCol, pci))
  576. CheckHandleCustEditColumn(iRow, iCol, pci) ;
  577. return ;
  578. } ;
  579. // If the row/column contains a "subordinate dialog" cell that can be
  580. // handled, do it and return.
  581. if (CheckHandleCustEditColumn(iRow, iCol, pci))
  582. return ;
  583. // It is ok to edit the cell normally so fire up, position, and load the
  584. // edit box.
  585. m_nRow = iRow ;
  586. m_nColumn = iCol ;
  587. CString strTemp = GetItemText(iRow, iCol) ;
  588. m_edit.SetWindowText(strTemp) ;
  589. m_edit.MoveWindow(rect) ;
  590. m_edit.SetSel(0, -1) ;
  591. m_edit.ShowWindow(SW_SHOW) ;
  592. m_edit.SetFocus() ;
  593. }
  594. void CFullEditListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
  595. {
  596. // Save any value that was being edited and hide the edit box to end
  597. // editing.
  598. SaveValue() ;
  599. m_edit.ShowWindow (SW_HIDE);
  600. *pResult = 0;
  601. }
  602. BOOL CFullEditListCtrl::PreTranslateMessage(MSG* pMsg)
  603. {
  604. switch (pMsg->message)
  605. {
  606. case WM_KEYDOWN:
  607. case WM_KEYUP:
  608. if (this == GetFocus())
  609. {
  610. switch (pMsg->wParam)
  611. {
  612. case VK_END:
  613. case VK_HOME:
  614. case VK_UP:
  615. case VK_DOWN:
  616. case VK_INSERT:
  617. case VK_RETURN:
  618. SendMessage (pMsg->message, pMsg->wParam, pMsg->lParam);
  619. return TRUE;
  620. }
  621. }
  622. else if (&m_edit == GetFocus ())
  623. {
  624. switch (pMsg->wParam)
  625. {
  626. case VK_END:
  627. case VK_HOME:
  628. case VK_LEFT:
  629. case VK_RIGHT:
  630. case VK_RETURN:
  631. case VK_ESCAPE:
  632. case VK_TAB:
  633. m_edit.SendMessage (pMsg->message, pMsg->wParam, pMsg->lParam);
  634. return TRUE;
  635. }
  636. }
  637. }
  638. return CWnd::PreTranslateMessage(pMsg);
  639. }
  640. void CFullEditListCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
  641. {
  642. // Prepare to check the key
  643. LV_KEYDOWN* pKeyDown = (LV_KEYDOWN*) pNMHDR ;
  644. *pResult = 0 ;
  645. CString strPrompt ;
  646. UINT iSelCount ;
  647. // Process the keys we're interested in.
  648. switch (pKeyDown->wVKey) {
  649. // Edit the first column of the first selected row.
  650. case VK_RETURN:
  651. EditCurRowSpecCol(0) ;
  652. break ;
  653. // Delete all of the selected rows. (Don't do anything if the
  654. // MF_IGNOREDELETE miscellaneous flag is set.)
  655. case VK_DELETE:
  656. if (m_dwMiscFlags & MF_IGNOREDELETE)
  657. return ;
  658. iSelCount = GetSelectedCount ();
  659. if (0 == iSelCount)
  660. return;
  661. strPrompt.LoadString(IDS_Delete2ItemQuery) ;
  662. if (IDYES == MessageBox (strPrompt, NULL, MB_YESNO | MB_ICONQUESTION))
  663. {
  664. int iSelIndex = GetNextItem (-1, LVNI_SELECTED);
  665. int nrow = iSelIndex ;
  666. while (iSelIndex != -1)
  667. {
  668. VERIFY (DeleteItem (iSelIndex));
  669. iSelIndex = GetNextItem (-1, LVNI_SELECTED);
  670. }
  671. if (nrow > -1)
  672. SendChangeNotification(nrow, 0) ;
  673. }
  674. SetFocus ();
  675. break ;
  676. // Insert a row and begin editing it. (Don't do anything if the
  677. // MF_IGNOREINSERT miscellaneous flag is set.)
  678. case VK_INSERT:
  679. if (m_dwMiscFlags & MF_IGNOREINSERT)
  680. return ;
  681. // Add a new row above the selected row or at the bottom of the
  682. // list if nothing is selected.
  683. int iIndex = GetNextItem (-1, LVNI_SELECTED);
  684. if (-1 == iIndex)
  685. iIndex = GetItemCount ();
  686. ASSERT (-1 != iIndex);
  687. InsertItem (iIndex, "");
  688. SetItemData(iIndex, m_nNextItemData++) ;
  689. // Make sure that the new row is visible and the only one selected.
  690. SingleSelect(iIndex) ;
  691. // Get the size and position of column 0 in the new row.
  692. CRect rect;
  693. GetItemRect (iIndex, rect, LVIR_BOUNDS);
  694. rect.right = rect.left + GetColumnWidth (0);
  695. // Start editing column 0.
  696. m_nRow = iIndex ;
  697. m_nColumn = 0 ;
  698. m_edit.MoveWindow (rect);
  699. m_edit.SetWindowText ("");
  700. m_edit.ShowWindow (SW_SHOW);
  701. m_edit.SetFocus ();
  702. break ;
  703. } ;
  704. }
  705. bool CFullEditListCtrl::SaveValue()
  706. {
  707. //TRACE("In SaveValue()\n") ;
  708. // Just return false if the edit box isn't visible/editing.
  709. if (!m_edit.IsWindowVisible()) {
  710. //TRACE("Leaving SaveValue() because edit box not visible.\n") ;
  711. return false ;
  712. } ;
  713. // Get the position and size of the edit box now that we know it is
  714. // visible.
  715. CRect rect ;
  716. m_edit.GetWindowRect(rect) ;
  717. ScreenToClient(rect) ;
  718. // Prepare to find out what row/column is being edited.
  719. POINT pt ;
  720. pt.x = rect.left ;
  721. pt.y = rect.top ;
  722. int iRow = -1, iCol = -1 ;
  723. // If the list box cell can be determined, save the edit box's contents in
  724. // that cell.
  725. if (GetPointRowCol(&pt, iRow, iCol, rect)) {
  726. CString strTemp ;
  727. m_edit.GetWindowText(strTemp) ;
  728. VERIFY(SetItemText(iRow, iCol, strTemp)) ;
  729. SendChangeNotification(iRow, iCol) ;
  730. } ;
  731. // The text in the edit box was saved in the appropriate list box cell
  732. // when it can be so return true to indicate this.
  733. return true ;
  734. }
  735. void CFullEditListCtrl::HideEditBox()
  736. {
  737. m_edit.ShowWindow(SW_HIDE);
  738. SetFocus() ;
  739. }
  740. bool CFullEditListCtrl::GetColumnData(CObArray* pcoadata, int ncolnum)
  741. {
  742. // Fail if the column number is bad.
  743. if (ncolnum < 0 || ncolnum >= m_nNumColumns)
  744. return false ;
  745. // Clear and then initialize the array
  746. pcoadata->RemoveAll() ;
  747. int numitems = GetItemCount() ;
  748. PCOLINFO pci = m_pciColInfo + ncolnum ;
  749. switch (pci->cdttype) {
  750. case COLDATTYPE_STRING:
  751. case COLDATTYPE_TOGGLE:
  752. case COLDATTYPE_CUSTEDIT:
  753. // Extra initialization needed for string arrays.
  754. ((CStringArray*) pcoadata)->SetSize(numitems) ;
  755. break ;
  756. default:
  757. pcoadata->SetSize(numitems) ;
  758. } ;
  759. // Declare and initialize the Item structure
  760. LV_ITEM lvi ;
  761. char acitemtext[4096] ;
  762. lvi.mask = LVIF_TEXT ;
  763. lvi.iSubItem = ncolnum ;
  764. lvi.pszText = acitemtext ;
  765. lvi.cchTextMax = 4095 ;
  766. int npos ;
  767. CString cscell ;
  768. // Load the column's data into the array in a way based on the data type.
  769. for (int n = 0 ; n < numitems ; n++) {
  770. lvi.iItem = n ;
  771. VERIFY(GetItem(&lvi)) ;
  772. switch ((m_pciColInfo + ncolnum)->cdttype) {
  773. case COLDATTYPE_INT:
  774. ((CUIntArray*) pcoadata)->SetAt(n, atoi(acitemtext)) ;
  775. //TRACE("Set col %d sub %d to %d\n", ncolnum, n, ((CUIntArray*) pcoadata)->GetAt(n)) ;
  776. break ;
  777. case COLDATTYPE_STRING:
  778. case COLDATTYPE_TOGGLE:
  779. ((CStringArray*) pcoadata)->SetAt(n, acitemtext) ;
  780. //TRACE("Set col %d sub %d to '%s'\n", ncolnum, n, ((CStringArray*) pcoadata)->GetAt(n)) ;
  781. break ;
  782. case COLDATTYPE_CUSTEDIT:
  783. cscell = acitemtext ;
  784. if ((npos = cscell.Find(_T(" ..."))) >= 0)
  785. cscell = cscell.Left(npos) ;
  786. ((CStringArray*) pcoadata)->SetAt(n, cscell) ;
  787. //TRACE("Set col %d sub %d to '%s'\n", ncolnum, n, ((CStringArray*) pcoadata)->GetAt(n)) ;
  788. break ;
  789. default:
  790. ASSERT(0) ;
  791. } ;
  792. } ;
  793. // Return true because the columns data was saved.
  794. return true ;
  795. }
  796. bool CFullEditListCtrl::SetColumnData(CObArray* pcoadata, int ncolnum)
  797. {
  798. // Get the number (sub)items to be loaded into the column. If there are
  799. // more (sub)items than there are rows, add extra rows.
  800. int numitems = (int)pcoadata->GetSize() ;
  801. int noldnumitems = GetItemCount() ;
  802. if (numitems > noldnumitems) {
  803. SetItemCount(numitems) ;
  804. m_nNextItemData = numitems + 1 ;
  805. } ;
  806. // Load the data into the column. If the data aren't strings, they are
  807. // converted into strings first. The width is checked when necessary.
  808. CString csitem ;
  809. COLDATTYPE cdtdatatype = (m_pciColInfo + ncolnum)->cdttype ;
  810. for (int n = 0 ; n < numitems ; n++) {
  811. // Get the string to load.
  812. switch (cdtdatatype) {
  813. case COLDATTYPE_INT:
  814. csitem.Format("%d", pcoadata->GetAt(n)) ;
  815. break ;
  816. case COLDATTYPE_STRING:
  817. case COLDATTYPE_TOGGLE:
  818. case COLDATTYPE_CUSTEDIT:
  819. csitem = ((CStringArray*) pcoadata)->GetAt(n) ;
  820. break ;
  821. default:
  822. ASSERT(0) ;
  823. } ;
  824. // Load the item into the appropriate row and column
  825. if (n >= noldnumitems && ncolnum == 0) {
  826. VERIFY(InsertItem(n, csitem) != -1) ;
  827. SetItemData(n, n);
  828. } else
  829. VERIFY(SetItem(n, ncolnum, LVIF_TEXT, csitem, -1, 0, 0, n)) ;
  830. } ;
  831. return true ;
  832. }
  833. void CFullEditListCtrl::SetCurRow(int nrow)
  834. {
  835. // First remove the focus and selection from any rows that have it now.
  836. int nr = -1 ;
  837. for ( ; (nr = GetNextItem(nr, LVNI_SELECTED)) != -1 ; )
  838. SetItem(nr,0,LVIF_STATE,NULL,-1,LVIS_SELECTED,0,nr) ;
  839. // Now set the new row.
  840. SetItem(nrow, 0, LVIF_STATE, NULL, -1, LVIS_FOCUSED+LVIS_SELECTED,
  841. LVIS_FOCUSED+LVIS_SELECTED, nrow) ;
  842. m_nRow = nrow ;
  843. }
  844. int CALLBACK CFullEditListCtrl::SortListData(LPARAM lp1, LPARAM lp2, LPARAM lp3)
  845. {
  846. // Get pointer to associated class instance
  847. CFullEditListCtrl* pcfelc = (CFullEditListCtrl*) lp3 ;
  848. // Try to find the item indexes. This should not fail.
  849. LV_FINDINFO lvfi ;
  850. lvfi.flags = LVFI_PARAM ;
  851. lvfi.lParam = lp1 ;
  852. int nitem1, nitem2 ;
  853. VERIFY((nitem1 = pcfelc->FindItem(&lvfi)) != -1) ;
  854. lvfi.lParam = lp2 ;
  855. VERIFY((nitem2 = pcfelc->FindItem(&lvfi)) != -1) ;
  856. // Now get the item data. Again, this should not fail.
  857. LV_ITEM lvi1, lvi2 ;
  858. char acitemtext1[4096], acitemtext2[4096] ;
  859. lvi1.mask = lvi2.mask = LVIF_TEXT ;
  860. lvi1.iItem = nitem1 ;
  861. lvi2.iItem = nitem2 ;
  862. lvi1.iSubItem = lvi2.iSubItem = pcfelc->m_nSortColumn ;
  863. lvi1.pszText = acitemtext1 ;
  864. lvi2.pszText = acitemtext2 ;
  865. lvi1.cchTextMax = lvi2.cchTextMax = 4095 ;
  866. VERIFY(pcfelc->GetItem(&lvi1)) ;
  867. VERIFY(pcfelc->GetItem(&lvi2)) ;
  868. // Convert the item text when necessary and compare the items.
  869. int ncompresult, inum1, inum2 ;
  870. PCOLINFO pci = pcfelc->m_pciColInfo + pcfelc->m_nSortColumn ;
  871. switch (pci->cdttype) {
  872. case COLDATTYPE_INT:
  873. inum1 = atoi(acitemtext1) ;
  874. inum2 = atoi(acitemtext2) ;
  875. ncompresult = inum1 - inum2 ;
  876. break ;
  877. case COLDATTYPE_STRING:
  878. case COLDATTYPE_TOGGLE:
  879. case COLDATTYPE_CUSTEDIT:
  880. ncompresult = _stricmp(acitemtext1, acitemtext2) ;
  881. break ;
  882. default:
  883. ASSERT(0) ;
  884. } ;
  885. // Return the result of the comparison. Reverse it before returning if
  886. // sorting in descending order.
  887. return ((pci->basc) ? ncompresult : 0 - ncompresult) ;
  888. }
  889. void CFullEditListCtrl::OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult)
  890. {
  891. // Save any in progress editing and shut it down before doing anything else.
  892. EndEditing(true) ;
  893. NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  894. // Get the sort column and send it to the sort control routine.
  895. SortControl(pNMListView->iSubItem) ;
  896. *pResult = 0;
  897. }
  898. bool CFullEditListCtrl::SortControl(int nsortcolumn)
  899. {
  900. CWaitCursor cwc ;
  901. // Save any in progress editing and shut it down before doing anything else.
  902. EndEditing(true) ;
  903. // Get a pointer to the column info structure
  904. PCOLINFO pci = m_pciColInfo + nsortcolumn ;
  905. // If the column is not sortable, just return false.
  906. if (!pci->bsortable)
  907. return false ;
  908. // Save the sort column, reverse the sort flag, and then sort the data.
  909. m_nSortColumn = nsortcolumn ;
  910. pci->basc = !pci->basc ;
  911. SortItems(SortListData, (LPARAM) this) ;
  912. // Data should have been sorted so...
  913. return true ;
  914. }
  915. void CFullEditListCtrl::SingleSelect(int nitem)
  916. {
  917. int nselitem ; // Selected item index
  918. // First, deselect all of the currently selected rows.
  919. nselitem = -1 ;
  920. while ((nselitem = GetNextItem(nselitem, LVNI_SELECTED)) != -1)
  921. SetItemState(nselitem, 0, LVIS_SELECTED | LVIS_FOCUSED) ;
  922. // Now select the requested row (item) and make it visible.
  923. SetItemState(nitem, LVIS_SELECTED | LVIS_FOCUSED,
  924. LVIS_SELECTED | LVIS_FOCUSED) ;
  925. EnsureVisible(nitem, false) ;
  926. }
  927. bool CFullEditListCtrl::GetRowData(int nrow, CStringArray& csafields)
  928. {
  929. // Fail if the requested row number is too large.
  930. if (nrow > GetItemCount())
  931. return false ;
  932. // Make sure that the string array is large enough to hold all of the
  933. // strings in a row. IE, the row's item text and all of the subitems'
  934. // text.
  935. if (m_nNumColumns > csafields.GetSize())
  936. csafields.SetSize(m_nNumColumns) ;
  937. // Declare and initialize the Item structure
  938. LV_ITEM lvi ;
  939. char acitemtext[4096] ;
  940. lvi.iItem = nrow ;
  941. lvi.mask = LVIF_TEXT ;
  942. lvi.pszText = acitemtext ;
  943. lvi.cchTextMax = 4095 ;
  944. // Get each field in the row and copy it into the fields array.
  945. for (int n = 0 ; n < m_nNumColumns ; n++) {
  946. lvi.iSubItem = n ;
  947. VERIFY(GetItem(&lvi)) ;
  948. csafields[n] = acitemtext ;
  949. } ;
  950. // All went well so...
  951. return true ;
  952. }
  953. bool CFullEditListCtrl::EndEditing(bool bsave)
  954. {
  955. // Save the new value first if requested. Return if there is nothing to
  956. // save or - when not saving - if nothing is being edited.
  957. if (bsave) {
  958. if (!SaveValue())
  959. return false ;
  960. } else
  961. if (!m_edit.IsWindowVisible())
  962. return false ;
  963. // Now hide the edit box and give the focus back to the list control.
  964. m_edit.ShowWindow(SW_HIDE) ;
  965. SetFocus() ;
  966. // Mission accomplished so...
  967. return true ;
  968. }
  969. bool CFullEditListCtrl::EditCurRowSpecCol(int ncolumn)
  970. {
  971. // Save the specified column number. If it is too big, reset it to 0.
  972. // If the column isn't editable, try the next one. Return false if an
  973. // editable column can't be found.
  974. for (int n = 0 ; n < m_nNumColumns ; n++, ncolumn++) {
  975. if (ncolumn >= m_nNumColumns)
  976. ncolumn = 0 ;
  977. if ((m_pciColInfo + ncolumn)->beditable)
  978. break ;
  979. } ;
  980. if (n < m_nNumColumns)
  981. m_nColumn = ncolumn ;
  982. else
  983. return false ;
  984. // Get the first currently selected row number. Use row number 0 if no
  985. // row is selected.
  986. if ((m_nRow = GetNextItem(-1, LVNI_SELECTED)) == -1)
  987. m_nRow = 0 ;
  988. // Make the current row, the only selected row and make sure it is visible.
  989. SingleSelect(m_nRow) ;
  990. // Now determine the dimensions of the specified column. Begin this by
  991. // getting the dimensions of the entire row...
  992. CRect rect ;
  993. VERIFY(GetItemRect(m_nRow, rect, LVIR_BOUNDS)) ;
  994. // ...Finish by looping through all of the columns getting their dimensions
  995. // and using those dimensions to adjust the left side of the rectangle
  996. // until the current column is reached. Then adjust the right side of the
  997. // rectangle.
  998. LV_COLUMN lvc ;
  999. for (int ncol = 0 ; ncol <= m_nColumn ; ncol++) {
  1000. ZeroMemory(&lvc, sizeof(LV_COLUMN)) ;
  1001. lvc.mask = LVCF_WIDTH ;
  1002. GetColumn(ncol, &lvc) ;
  1003. if (ncol < m_nColumn)
  1004. rect.left += lvc.cx ;
  1005. } ;
  1006. rect.right = rect.left + lvc.cx ;
  1007. // Load, position, size, and show the edit box.
  1008. CString strTemp = GetItemText(m_nRow, m_nColumn) ;
  1009. m_edit.SetWindowText(strTemp) ;
  1010. m_edit.MoveWindow(rect) ;
  1011. m_edit.SetSel(0, -1) ;
  1012. m_edit.ShowWindow(SW_SHOW) ;
  1013. m_edit.SetFocus() ;
  1014. return true ;
  1015. }
  1016. void CFullEditListCtrl::OnVScroll(UINT nSBCode, UINT nPos,
  1017. CScrollBar* pScrollBar)
  1018. {
  1019. // Things get screwy if the list control scrolls while an item is being
  1020. // edited so end editing before allowing scrolling to take place.
  1021. EndEditing(true) ;
  1022. CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
  1023. }
  1024. void CFullEditListCtrl::OnHScroll(UINT nSBCode, UINT nPos,
  1025. CScrollBar* pScrollBar)
  1026. {
  1027. // Things get screwy if the list control scrolls while an item is being
  1028. // edited so end editing before allowing scrolling to take place.
  1029. EndEditing(true) ;
  1030. CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
  1031. }
  1032. /******************************************************************************
  1033. CFullEditListCtrl::CheckHandleToggleColumns
  1034. First check to see if the row or column has togglable. If not, just return.
  1035. If the row is togglable, find the togglable column (cell). Then either set
  1036. its contents if it is empty or clear it if it is not empty.
  1037. Return true if a toggle-able column was found and toggled. Otherwise, return
  1038. false.
  1039. ******************************************************************************/
  1040. bool CFullEditListCtrl::CheckHandleToggleColumns(int nrow, int ncol,
  1041. PCOLINFO pci)
  1042. {
  1043. // Return if there is no togglable column in this list.
  1044. if (!(m_dwToggleFlags & TF_HASTOGGLECOLUMNS))
  1045. return false ;
  1046. // If a specific togglable column must be clicked and that column was not
  1047. // clicked, just return.
  1048. if (m_dwToggleFlags & TF_CLICKONCOLUMN) {
  1049. if (pci->cdttype != COLDATTYPE_TOGGLE)
  1050. return false ;
  1051. // If any part of the row can be double-clicked to toggle the togglable
  1052. // column, find that column. Return if there is no such column.
  1053. } else {
  1054. pci = m_pciColInfo ;
  1055. for (ncol = 0 ; ncol < m_nNumColumns ; ncol++, pci++) {
  1056. if (pci->cdttype == COLDATTYPE_TOGGLE)
  1057. break ;
  1058. } ;
  1059. if (ncol >= m_nNumColumns)
  1060. return false ;
  1061. } ;
  1062. // Get the contents of the specified cell.
  1063. CString strcell = GetItemText(nrow, ncol) ;
  1064. // If the cell is empty, load it with the toggle string. If the cell is
  1065. // not empty, clear its contents.
  1066. if (strcell.IsEmpty())
  1067. VERIFY(SetItemText(nrow, ncol, pci->lpctstrtoggle)) ;
  1068. else
  1069. VERIFY(SetItemText(nrow, ncol, _T(""))) ;
  1070. SendChangeNotification(nrow, ncol) ;
  1071. return true ;
  1072. }
  1073. /******************************************************************************
  1074. CFullEditListCtrl::CheckHandleCustEditColumn
  1075. First check to see if the row or column may contain a cell may be one that is
  1076. only editable with a custom edit routine. If not, just return. Next,
  1077. make sure that the cell not only might be of this type but actually is of this
  1078. type. If not, just return.
  1079. Now, get the contents of the selected cell and remove the ellipses from the
  1080. end of it. (The ellipses will be replaced before the new string is put back
  1081. into the cell.)
  1082. Next, comes the tricky part. Use the owner class pointer to call the routine
  1083. in that class which will manage the work of editting the cell's contents.
  1084. Although CFullEditListCtrl is supposed to be a generic class, this routine
  1085. must know what the owner class is so that the management routine can be
  1086. called. This means that extra, owner class specific, code must be added to
  1087. this routine each time a new owner class is added that uses this feature in
  1088. CFullEditListCtrl.
  1089. If the edit request was handled via a subordinate dialog box, return true.
  1090. Otherwise, return false.
  1091. ******************************************************************************/
  1092. bool CFullEditListCtrl::CheckHandleCustEditColumn(int nrow, int ncol,
  1093. PCOLINFO pci)
  1094. {
  1095. // Return if there is no custom edit column in this list.
  1096. if (!(m_dwCustEditFlags & CEF_HASTOGGLECOLUMNS))
  1097. return false ;
  1098. // Return if the selected row does not contain a custom edit cell.
  1099. int n = (int)m_cuiaCustEditRows.GetSize() ;
  1100. ASSERT(m_cuiaCustEditRows.GetSize()) ;
  1101. if (nrow >= m_cuiaCustEditRows.GetSize() || m_cuiaCustEditRows[nrow] == 0)
  1102. return false ;
  1103. // If a specific custom edit column must be clicked and that column was not
  1104. // clicked, just return.
  1105. if (m_dwCustEditFlags & CEF_CLICKONCOLUMN) {
  1106. if (pci->cdttype != COLDATTYPE_CUSTEDIT)
  1107. return false ;
  1108. // If any part of the row can be double-clicked to edit the custom edit
  1109. // column, find that column. Return if there is no such column.
  1110. } else {
  1111. pci = m_pciColInfo ;
  1112. for (ncol = 0 ; ncol < m_nNumColumns ; ncol++, pci++) {
  1113. if (pci->cdttype == COLDATTYPE_CUSTEDIT)
  1114. break ;
  1115. } ;
  1116. if (ncol >= m_nNumColumns)
  1117. return false ;
  1118. } ;
  1119. // Get the contents of the specified cell and remove the ellipses from the
  1120. // end of it.
  1121. CString strcell = GetItemText(nrow, ncol) ;
  1122. int npos ;
  1123. if ((npos = strcell.Find(_T(" ..."))) >= 0)
  1124. strcell = strcell.Left(npos) ;
  1125. // Find the class for the owner and use that information to call the
  1126. // management routine. If this routine "fails" or is cancelled, return
  1127. // true without updating the cell.
  1128. bool brc ;
  1129. brc = (*m_lpCellEditProc)(m_pcoOwner, nrow, ncol, &strcell) ;
  1130. if (!brc)
  1131. return true ;
  1132. // Add the ellipses back on to the cell's new contents and update the cell.
  1133. strcell += _T(" ...") ;
  1134. VERIFY(SetItemText(nrow, ncol, strcell)) ;
  1135. SendChangeNotification(nrow, ncol) ;
  1136. SetFocus() ;
  1137. return true ;
  1138. }
  1139. void CFullEditListCtrl::SendChangeNotification(int nrow, int ncol)
  1140. {
  1141. // Do nothing if the owner does not want to be notified of a change in one
  1142. // of the cells in the list control.
  1143. if (!(m_dwMiscFlags & MF_SENDCHANGEMESSAGE))
  1144. return ;
  1145. // Send the message
  1146. ::PostMessage(GetParent()->m_hWnd, WM_LISTCELLCHANGED, nrow, ncol) ;
  1147. }
  1148. /////////////////////////////////////////////////////////////////////////////
  1149. // CFlagsListBox
  1150. CFlagsListBox::CFlagsListBox()
  1151. {
  1152. m_bReady = false ;
  1153. m_nGrpCnt = 0 ;
  1154. }
  1155. CFlagsListBox::~CFlagsListBox()
  1156. {
  1157. }
  1158. BEGIN_MESSAGE_MAP(CFlagsListBox, CListBox)
  1159. //{{AFX_MSG_MAP(CFlagsListBox)
  1160. ON_CONTROL_REFLECT(LBN_DBLCLK, OnDblclk)
  1161. //}}AFX_MSG_MAP
  1162. END_MESSAGE_MAP()
  1163. /******************************************************************************
  1164. CFlagsListBox::Init
  1165. Save the parameters that are used to control the behaviour of the list box.
  1166. Set the list box's tab stop. Then build the list box items based on the
  1167. flag names and settings and load them into the list box.
  1168. Args:
  1169. csafieldnames Flag field names
  1170. dwsettings Current flag setting(s)
  1171. cuiaflaggroupings Indexes for related groups of flags
  1172. ngrpcnt Number of flag groups
  1173. lptstrsetstring String to display when a flag is set
  1174. ntabstop List box tab stop, sets column widths
  1175. bnoclear True iff dbl-clk doesn't clear a flag that is set
  1176. nocleargrp -1 or only 0-based group number that bnoclear applies to
  1177. *** IMPORTANT NOTE ***
  1178. See OnDblclk() to find out how the data in m_cuiaFlagGroupings and m_nGrpCnt
  1179. are interpreted.
  1180. ******************************************************************************/
  1181. bool CFlagsListBox::Init(CStringArray& csafieldnames, DWORD dwsettings,
  1182. CUIntArray& cuiaflaggroupings, int ngrpcnt,
  1183. LPTSTR lptstrsetstring, int ntabstop,
  1184. bool bnoclear /*=false*/, int nocleargrp /*=-1*/)
  1185. {
  1186. // Check parameters
  1187. ASSERT(csafieldnames.GetSize()) ;
  1188. ASSERT(cuiaflaggroupings.GetSize() >= (ngrpcnt * 2)) ;
  1189. ASSERT(lptstrsetstring) ;
  1190. ASSERT(ntabstop > 0) ;
  1191. // Copy parameters needed later
  1192. m_cuiaFlagGroupings.Copy(cuiaflaggroupings) ;
  1193. m_nGrpCnt = ngrpcnt ;
  1194. m_csSetString = lptstrsetstring ;
  1195. m_nNumFields = (int)csafieldnames.GetSize() ;
  1196. m_bNoClear = bnoclear ;
  1197. m_nNoClearGrp = nocleargrp ;
  1198. // Make sure the list box is empty.
  1199. ResetContent() ;
  1200. // Set the position of the list box's tabstop. This value is passed in
  1201. // because it is a dialog box specific value.
  1202. SetTabStops(1, &ntabstop) ;
  1203. // Now combine the field names and settings and add them to the listbox.
  1204. CString cs ;
  1205. int nbit = 1 ;
  1206. for (int n = 0 ; n < m_nNumFields ; n++, nbit <<= 1) {
  1207. cs = (dwsettings & nbit) ? m_csSetString : _T("") ;
  1208. cs = csafieldnames[n] + _T("\t") + cs ;
  1209. AddString(cs) ;
  1210. } ;
  1211. // Make sure the list box has the focus.
  1212. SetFocus() ;
  1213. // All went well so...
  1214. m_bReady = true ;
  1215. return true ;
  1216. }
  1217. /******************************************************************************
  1218. CFlagsListBox::Init2
  1219. The flag settings in pcssettings are in the form of a hex number represented
  1220. as a string that may begin with "0x". For example, "0xA23" and "A23". Turn
  1221. this string into a DWORD and pass this DWORD plus the rest of the parameters
  1222. to the other form of this function that takes the settings parameter as a
  1223. DWORD. It does the rest of the work.
  1224. Args:
  1225. csafieldnames Flag field names
  1226. pcssettings Current flag setting(s)
  1227. cuiaflaggroupings Indexes for related groups of flags
  1228. ngrpcnt Number of flag groups
  1229. lptstrsetstring String to display when a flag is set
  1230. ntabstop List box tab stop, sets column widths
  1231. bnoclear True iff dbl-clk doesn't clear a flag that is set
  1232. nocleargrp -1 or only 0-based group number that bnoclear applies to
  1233. *** IMPORTANT NOTE ***
  1234. See OnDblclk() to find out how the data in m_cuiaFlagGroupings and m_nGrpCnt
  1235. are interpreted.
  1236. ******************************************************************************/
  1237. bool CFlagsListBox::Init2(CStringArray& csafieldnames, CString* pcssettings,
  1238. CUIntArray& cuiaflaggroupings, int ngrpcnt,
  1239. LPTSTR lptstrsetstring, int ntabstop,
  1240. bool bnoclear /*=false*/, int nocleargrp /*=-1*/)
  1241. {
  1242. // Turn pcssettings into a dword. (Skip passed "0x" if it is on the
  1243. // beginning of the string.)
  1244. LPTSTR lptstr, lptstr2 ;
  1245. lptstr = pcssettings->GetBuffer(16) ;
  1246. int n = pcssettings->GetLength() ;
  1247. *(lptstr + n) = 0 ;
  1248. if (*(lptstr + 1) == 'x')
  1249. lptstr += 2 ;
  1250. DWORD dwsettings = strtoul(lptstr, &lptstr2, 16) ;
  1251. // The other Init() finishes the job.
  1252. return (Init(csafieldnames, dwsettings, cuiaflaggroupings, ngrpcnt,
  1253. lptstrsetstring, ntabstop, bnoclear, nocleargrp)) ;
  1254. }
  1255. /******************************************************************************
  1256. CFlagsListBox::GetNewFlagDWord()
  1257. Read the settings of the flags in the list box to determine the new flag
  1258. dword. Then return it.
  1259. ******************************************************************************/
  1260. DWORD CFlagsListBox::GetNewFlagDWord()
  1261. {
  1262. DWORD dwflags = 0 ; // Flag dword built here
  1263. DWORD dwbit = 1 ; // Used to set bits that are on
  1264. // Loop through each flag in the list box
  1265. CString csentry, csvalue ;
  1266. int npos ;
  1267. for (int n = 0 ; n < m_nNumFields ; n++, dwbit <<= 1) {
  1268. // Get the current flag and isolate its setting.
  1269. GetText(n, csentry) ;
  1270. npos = csentry.Find(_T('\t')) ;
  1271. csvalue = csentry.Mid(npos + 1) ;
  1272. // If the current flag is set, turn on its bit in dwflags.
  1273. if (csvalue.GetLength())
  1274. dwflags |= dwbit ;
  1275. } ;
  1276. // Return the flag DWORD.
  1277. return dwflags ;
  1278. }
  1279. /******************************************************************************
  1280. CFlagsListBox::GetNewFlagString()
  1281. Use the other version of this routine to get the DWORD version of the flags.
  1282. Then convert it to a string a save it in csflags. "0x" may or may not be
  1283. prepended to the string.
  1284. ******************************************************************************/
  1285. void CFlagsListBox::GetNewFlagString(CString* pcsflags, bool badd0x /*=true*/)
  1286. {
  1287. // Get the DWORD version of the flag.
  1288. DWORD dwflags = GetNewFlagDWord() ;
  1289. // Format the flags as a string and prepend "0x" when requested.
  1290. if (badd0x)
  1291. pcsflags->Format("0x%x", dwflags) ;
  1292. else
  1293. pcsflags->Format("%x", dwflags) ;
  1294. }
  1295. /////////////////////////////////////////////////////////////////////////////
  1296. // CFlagsListBox message handlers
  1297. /******************************************************************************
  1298. CFlagsListBox::OnDblclk()
  1299. First, toggle the setting of the selected item. At least, that is what is
  1300. usually done first. If m_bNoClear is set and the user has asked to clear a
  1301. flag, don't do this or anything else. Just return. Use m_bNoClear when at
  1302. least one flag must always be set. See #4 for more information on m_bNoClear.
  1303. Next, process the rest of the flags in the items flag group. The following
  1304. things can happen based on the group:
  1305. 1. If the item is not in a group, do nothing else. This is useful when
  1306. any combination of flags can be set. The most efficient way to do this
  1307. is by setting m_nGrpCnt to 0.
  1308. 2. The item indexes in m_cuiaFlagGroupings that contain the selected item
  1309. are positive. Only one of the specified flags can be set. If the
  1310. selected item was set, clear the rest of the flags in the group.
  1311. 3. The item indexes in m_cuiaFlagGroupings that contain the selected item
  1312. are negative. (The absolute value of the indexes is used for tests.)
  1313. Change the state of the other flags in the group, too. Generally, this
  1314. only makes sense in a two flag group where one and only one of the flags
  1315. MUST be selected.
  1316. 4. If m_bNoClear is set and m_nNoClearGrp = -1, m_bNoClear affects all
  1317. flags. If m_nNoClearGrp is >= 0, it refers to the single group that
  1318. m_bNoClear affects.
  1319. ******************************************************************************/
  1320. void CFlagsListBox::OnDblclk()
  1321. {
  1322. // Do nothing if the edit control isn't ready, yet.
  1323. if (!m_bReady)
  1324. return ;
  1325. // Do nothing if no item is selected in the list box.
  1326. int nselitem ;
  1327. if ((nselitem = GetCurSel()) == LB_ERR)
  1328. return ;
  1329. // Get the listbox entry and split it into name and value components.
  1330. CString csentry, csname, csvalue ;
  1331. GetText(nselitem, csentry) ;
  1332. int npos = csentry.Find(_T('\t')) ;
  1333. csname = (npos > 0) ? csentry.Left(npos) : _T("") ;
  1334. csvalue = csentry.Mid(npos + 1) ;
  1335. // Find the flag grouping for the selected flag.
  1336. int n, nidx ;
  1337. for (n = nidx = 0 ; n < m_nGrpCnt ; n++, nidx += 2)
  1338. if (abs(m_cuiaFlagGroupings[nidx]) <= nselitem
  1339. && abs(m_cuiaFlagGroupings[nidx+1]) >= nselitem)
  1340. break ;
  1341. // Determine the flags current state. If it is set and m_bNoClear == true,
  1342. // don't do anything based on the value of m_nNoClearGrp. Just return.
  1343. bool boldstate = !csvalue.IsEmpty() ;
  1344. if (m_bNoClear && boldstate && (m_nNoClearGrp == -1 || m_nNoClearGrp == n))
  1345. return ;
  1346. // Change the state of the selected flag.
  1347. csvalue = (boldstate) ? _T("") : m_csSetString ;
  1348. csentry = csname + _T('\t') + csvalue ;
  1349. DeleteString(nselitem) ;
  1350. InsertString(nselitem, csentry) ;
  1351. // Do nothing else if the selected flag is not in a flag grouping.
  1352. if (n >= m_nGrpCnt) {
  1353. SetCurSel(nselitem) ;
  1354. return ;
  1355. } ;
  1356. // Find out if this group has to have a flag set. If it doesn't and the
  1357. // selected flag was cleared, there is nothing left to do.
  1358. bool bmustset = ((int) m_cuiaFlagGroupings[nidx+1]) < 0 ;
  1359. if (!bmustset && boldstate) {
  1360. SetCurSel(nselitem) ;
  1361. return ;
  1362. } ;
  1363. // Loop through all of the flags in the current group. Change the setting
  1364. // of all flags in the group -- EXCEPT the selected flag -- to the old value
  1365. // of the selected flag.
  1366. n = abs(m_cuiaFlagGroupings[nidx+1]) ;
  1367. csvalue = (boldstate) ? m_csSetString : _T("") ;
  1368. for (nidx = abs(m_cuiaFlagGroupings[nidx]) ; nidx <= n ; nidx++) {
  1369. if (nidx == nselitem)
  1370. continue ;
  1371. GetText(nidx, csentry) ;
  1372. npos = csentry.Find(_T('\t')) ;
  1373. csname = (npos > 0) ? csentry.Left(npos + 1) : _T("") ;
  1374. csentry = csname + csvalue ;
  1375. DeleteString(nidx) ;
  1376. InsertString(nidx, csentry) ;
  1377. } ;
  1378. // Make sure that the flag selected by the user is still selected and
  1379. // return.
  1380. SetCurSel(nselitem) ;
  1381. return ;
  1382. }