///////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1996-2002 Microsoft Corporation // // Module Name: // AtlLCPair.h // // Implementation File: // None. // // Description: // Definition of the CListCtrlPair dialog. // Derive from CDialogImpl<> or CPropertyPageImpl<>. // // Author: // David Potter (davidp) August 8, 1996 // // Revision History: // // Notes: // ///////////////////////////////////////////////////////////////////////////// #ifndef __ATLLCPAIR_H_ #define __ATLLCPAIR_H_ ///////////////////////////////////////////////////////////////////////////// // Forward Class Declarations ///////////////////////////////////////////////////////////////////////////// template < class T, class ObjT, class BaseT > class CListCtrlPair; ///////////////////////////////////////////////////////////////////////////// // External Class Declarations ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Include Files ///////////////////////////////////////////////////////////////////////////// #ifndef __ADMCOMMONRES_H_ #include "AdmCommonRes.h" // for ADMC_IDC_LCP_xxx #endif #ifndef __ATLUTIL_H_ #include "AtlUtil.h" // for DDX_xxx #endif ///////////////////////////////////////////////////////////////////////////// // Type Definitions ///////////////////////////////////////////////////////////////////////////// struct CLcpColumn { UINT m_idsText; int m_nWidth; }; #define LCPS_SHOW_IMAGES 0x1 #define LCPS_ALLOW_EMPTY 0x2 #define LCPS_CAN_BE_ORDERED 0x4 #define LCPS_ORDERED 0x8 #define LCPS_DONT_OUTPUT_RIGHT_LIST 0x10 #define LCPS_READ_ONLY 0x20 #define LCPS_PROPERTIES_BUTTON 0x40 #define LCPS_MAX 0x40 ///////////////////////////////////////////////////////////////////////////// //++ // // class CListCtrlPair // // Description: // Class to support dual list box. // // Inheritance: // CListCtrlPair< T, ObjT, BaseT > // // ... // CDialogImpl< T > // //-- ///////////////////////////////////////////////////////////////////////////// template < class T, class ObjT, class BaseT > class CListCtrlPair : public BaseT { // friend class CListCtrlPairDlg; // friend class CListCtrlPairPage; // friend class CListCtrlPairWizPage; typedef CListCtrlPair< T, ObjT, BaseT > thisClass; typedef std::list< ObjT * > _objptrlist; typedef typename std::list< ObjT * >::iterator _objptrlistit; protected: // Column structure and collection. typedef std::vector< CLcpColumn > CColumnArray; CColumnArray m_aColumns; // Sort information. struct SortInfo { int m_nDirection; int m_nColumn; }; public: // // Construction // // Default constructor CListCtrlPair( void ) { CommonConstruct(); } //*** CListCtrlPair() // Constructor with style specified CListCtrlPair( IN DWORD dwStyle, IN LPCTSTR lpszTitle = NULL ) : BaseT( lpszTitle ) { CommonConstruct(); m_dwStyle = dwStyle; } //*** CListCtrlPair( lpszTitle ) // Constructor with style specified CListCtrlPair( IN DWORD dwStyle, IN UINT nIDTitle ) : BaseT( nIDTitle ) { CommonConstruct(); m_dwStyle = dwStyle; } //*** CListCtrlPair( nIDTitle ) // Common object construction void CommonConstruct( void ) { m_dwStyle = LCPS_ALLOW_EMPTY; m_plvcFocusList = NULL; // Set the sort info. SiLeft().m_nDirection = -1; SiLeft().m_nColumn = -1; SiRight().m_nDirection = -1; SiRight().m_nColumn = -1; } //*** CommonConstruct() public: // // Functions that are required to be implemented by derived class. // // Return list of objects for right list control _objptrlist * PlpobjRight( void ) const { ATLTRACE( _T("PlpobjRight() - Define in derived class\n") ); ATLASSERT( 0 ); return NULL; } //*** PlpobjRight() // Return list of objects for left list control const _objptrlist * PlpobjLeft( void ) const { ATLTRACE( _T("PlpobjLeft() - Define in derived class\n") ); ATLASSERT( 0 ); return NULL; } //*** PlpobjLeft() // Get column text and image void GetColumnInfo( IN OUT ObjT * pobj, IN int iItem, IN int icol, OUT CString & rstr, OUT int * piimg ) { ATLTRACE( _T("GetColumnInfo() - Define in derived class\n") ); ATLASSERT( 0 ); } //*** GetColumnInfo() // Display properties for the object int BDisplayProperties( IN OUT ObjT * pobj ) { ATLTRACE( _T("BDisplayProperties() - Define in derived class\n") ); ATLASSERT( 0 ); return FALSE; } //*** BDisplayProperties() // Display an application-wide message box virtual int AppMessageBox( LPCWSTR lpszText, UINT fuStyle ) { ATLTRACE( _T("BDisplayProperties() - Define in derived class\n") ); ATLASSERT( 0 ); return MessageBox( lpszText, _T(""), fuStyle ); } //*** AppMessageBox() // Display an application-wide message box int AppMessageBox( UINT nID, UINT fuStyle ) { CString strMsg; strMsg.LoadString( nID ); return AppMessageBox( strMsg, fuStyle ); } //*** AppMessageBox() protected: // // List control pair style. // DWORD m_dwStyle; BOOL BIsStyleSet( IN DWORD dwStyle ) const { return (m_dwStyle & dwStyle) == dwStyle; } void ModifyStyle( IN DWORD dwRemove, IN DWORD dwAdd ) { ATLASSERT( (dwRemove & dwAdd) == 0 ); if ( dwRemove != 0 ) { m_dwStyle &= ~dwRemove; } // if: removing some styles if ( dwAdd != 0 ) { m_dwStyle |= dwAdd; } // if: adding some styles } //*** ModifyStyle() DWORD DwStyle( void ) const { return m_dwStyle; } BOOL BShowImages( void ) const { return BIsStyleSet( LCPS_SHOW_IMAGES ); } BOOL BAllowEmpty( void ) const { return BIsStyleSet( LCPS_ALLOW_EMPTY ); } BOOL BCanBeOrdered( void ) const { return BIsStyleSet( LCPS_CAN_BE_ORDERED ); } BOOL BOrdered( void ) const { return BIsStyleSet( LCPS_ORDERED ); } BOOL BReadOnly( void ) const { return BIsStyleSet( LCPS_READ_ONLY ); } BOOL BPropertiesButton( void ) const { return BIsStyleSet( LCPS_PROPERTIES_BUTTON ); } // Operations public: // Add column to list of columns displayed in each list control void AddColumn( IN UINT idsText, IN int nWidth ) { CLcpColumn col; ATLASSERT( idsText != 0 ); ATLASSERT( nWidth > 0 ); ATLASSERT( LpobjRight().empty() ); col.m_idsText = idsText; col.m_nWidth = nWidth; m_aColumns.insert( m_aColumns.end(), col ); } //*** AddColumn() // Insert an item in a list control int NInsertItemInListCtrl( IN int iitem, IN OUT ObjT * pobj, IN OUT CListViewCtrl & rlc ) { int iRetItem; CString strText; int iimg = 0; int icol; // Insert the first column. ((T *) this)->GetColumnInfo( pobj, iitem, 0, strText, &iimg ); iRetItem = rlc.InsertItem( LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM, // nMask iitem, // nItem strText, // lpszItem 0, // nState 0, // nStateMask iimg, // nImage (LPARAM) pobj // lParam ); ATLASSERT( iRetItem != -1 ); for ( icol = 1 ; icol < (int) m_aColumns.size() ; icol++ ) { ((T *) this)->GetColumnInfo( pobj, iRetItem, icol, strText, NULL ); rlc.SetItemText( iRetItem, icol, strText ); } // for: each column return iRetItem; } //*** NInsertItemInListCtrl() // Update data on or from the dialog BOOL UpdateData( IN BOOL bSaveAndValidate ) { BOOL bSuccess = TRUE; if ( bSaveAndValidate ) { // // Verify that the list is not empty. // if ( ! BAllowEmpty() && (m_lvcRight.GetItemCount() == 0) ) { CString strMsg; CString strLabel; TCHAR * pszLabel; DDX_GetText( m_hWnd, ADMC_IDC_LCP_RIGHT_LABEL, strLabel ); // // Remove ampersands (&) and colons (:). // pszLabel = strLabel.GetBuffer( 1 ); CleanupLabel( pszLabel ); strLabel.ReleaseBuffer(); // // Display an error message. // strMsg.FormatMessage( ADMC_IDS_EMPTY_RIGHT_LIST, pszLabel ); AppMessageBox( strMsg, MB_OK | MB_ICONWARNING ); bSuccess = FALSE; } // if: list is empty and isn't allowed to be } // if: saving data from the dialog else { } // else: setting data to the dialog return bSuccess; } //*** UpdateData() // Apply changes made on this dialog BOOL BApplyChanges( void ) { ATLASSERT( ! BIsStyleSet( LCPS_DONT_OUTPUT_RIGHT_LIST ) ); ATLASSERT( ! BReadOnly() ); T * pT = static_cast< T * >( this ); // // Copy the Nodes list. // *pT->PlpobjRight() = LpobjRight(); // // Call the base class method. // return BaseT::BApplyChanges(); } //*** BApplyChanges() // Implementation protected: _objptrlist m_lpobjRight; _objptrlist m_lpobjLeft; CListViewCtrl m_lvcRight; CListViewCtrl m_lvcLeft; CListViewCtrl * m_plvcFocusList; CButton m_pbAdd; CButton m_pbRemove; CButton m_pbMoveUp; CButton m_pbMoveDown; CButton m_pbProperties; public: // // Message map. // BEGIN_MSG_MAP( thisClass ) MESSAGE_HANDLER( WM_CONTEXTMENU, OnContextMenu ) COMMAND_HANDLER( ADMC_IDC_LCP_ADD, BN_CLICKED, OnAdd ) COMMAND_HANDLER( ADMC_IDC_LCP_REMOVE, BN_CLICKED, OnRemove ) COMMAND_HANDLER( ADMC_IDC_LCP_MOVE_UP, BN_CLICKED, OnMoveUp ) COMMAND_HANDLER( ADMC_IDC_LCP_MOVE_DOWN, BN_CLICKED, OnMoveDown ) COMMAND_HANDLER( ADMC_IDC_LCP_PROPERTIES, BN_CLICKED, OnProperties ) COMMAND_HANDLER( IDOK, BN_CLICKED, OnOK ) COMMAND_HANDLER( IDCANCEL, BN_CLICKED, OnCancel ) COMMAND_HANDLER( ADMC_ID_MENU_PROPERTIES, 0, OnProperties ) NOTIFY_HANDLER( ADMC_IDC_LCP_LEFT_LIST, NM_DBLCLK, OnDblClkList ) NOTIFY_HANDLER( ADMC_IDC_LCP_RIGHT_LIST, NM_DBLCLK, OnDblClkList ) NOTIFY_HANDLER( ADMC_IDC_LCP_LEFT_LIST, LVN_ITEMCHANGED, OnItemChangedList ) NOTIFY_HANDLER( ADMC_IDC_LCP_RIGHT_LIST, LVN_ITEMCHANGED, OnItemChangedList ) NOTIFY_HANDLER( ADMC_IDC_LCP_LEFT_LIST, LVN_COLUMNCLICK, OnColumnClickList ) NOTIFY_HANDLER( ADMC_IDC_LCP_RIGHT_LIST, LVN_COLUMNCLICK, OnColumnClickList ) CHAIN_MSG_MAP( BaseT ) END_MSG_MAP() // // Message handler functions. // // Handler for WM_CONTEXTMENU LRESULT OnContextMenu( IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam, IN OUT BOOL & bHandled ) { BOOL bDisplayed = FALSE; CMenu * pmenu = NULL; HWND hWnd = (HWND) wParam; WORD xPos = LOWORD( lParam ); WORD yPos = HIWORD( lParam ); CListViewCtrl * plvc; CString strMenuName; CWaitCursor wc; // // If focus is not in a list control, don't handle the message. // if ( hWnd == m_lvcLeft.m_hWnd ) { plvc = &m_lvcLeft; } // if: context menu on left list else if ( hWnd == m_lvcRight.m_hWnd ) { plvc = &m_lvcRight; } // else if: context menu on right list else { bHandled = FALSE; return 0; } // else: focus not in a list control ATLASSERT( plvc != NULL ); // // If the properties button is not enabled, don't display a menu. // if ( ! BPropertiesButton() ) { bHandled = FALSE; return 0; } // if: no properties button // // Create the menu to display. // pmenu = new CMenu; ATLASSERT( pmenu != NULL ); if ( pmenu == NULL ) { bHandled = FALSE; return 0; } // if: error allocating memory for the new menu if ( pmenu->CreatePopupMenu() ) { UINT nFlags = MF_STRING; // // If there are no items in the list, disable the menu item. // if ( plvc->GetItemCount() == 0 ) { nFlags |= MF_GRAYED; } // if: no items in the list // // Add the Properties item to the menu. // strMenuName.LoadString( ADMC_ID_MENU_PROPERTIES ); if ( pmenu->AppendMenu( nFlags, ADMC_ID_MENU_PROPERTIES, strMenuName ) ) { m_plvcFocusList = plvc; bDisplayed = TRUE; } // if: successfully added menu item } // if: menu created successfully if ( bDisplayed ) { // // Display the menu. // if ( ! pmenu->TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, xPos, yPos, m_hWnd ) ) { } // if: unsuccessfully displayed the menu } // if: there is a menu to display delete pmenu; return 0; } //*** OnContextMenu() // Handler for BN_CLICKED on ADMC_IDC_LCP_ADD LRESULT OnAdd( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { ATLASSERT( ! BReadOnly() ); // // Move selected items from the left list to the right list. // MoveItems( m_lvcRight, LpobjRight(), m_lvcLeft, LpobjLeft() ); return 0; } //*** OnAdd() // Handler for BN_CLICKED on ADMC_IDC_LCP_REMOVE LRESULT OnRemove( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { ATLASSERT( ! BReadOnly() ); // // Move selected items from the right list to the left list. // MoveItems( m_lvcLeft, LpobjLeft(), m_lvcRight, LpobjRight() ); return 0; } //*** OnRemove() // Handler for BN_CLICKED on ADMC_IDC_LCP_MOVE_UP LRESULT OnMoveUp( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { int nItem; ObjT * pobj; // // Find the index of the selected item. // nItem = m_lvcRight.GetNextItem( -1, LVNI_SELECTED ); ATLASSERT( nItem != -1 ); // // Get the item pointer. // pobj = (ObjT *) m_lvcRight.GetItemData( nItem ); ATLASSERT( pobj != NULL ); // Remove the selected item from the list and add it back in. { _objptrlistit itRemove; _objptrlistit itAdd; // Find the position of the item to be removed and the item before // which the item is to be inserted. itRemove = std::find( LpobjRight().begin(), LpobjRight().end(), pobj ); ATLASSERT( itRemove != LpobjRight().end() ); itAdd = itRemove--; LpobjRight().insert( itAdd, pobj ); LpobjRight().erase( itRemove ); } // Remove the selected item from the list and add it back in // Remove the selected item from the list control and add it back in. m_lvcRight.DeleteItem( nItem ); NInsertItemInListCtrl( nItem - 1, pobj, m_lvcRight ); m_lvcRight.SetItemState( nItem - 1, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); m_lvcRight.EnsureVisible( nItem - 1, FALSE /*bPartialOK*/ ); m_lvcRight.SetFocus(); return 0; } //*** OnMoveUp() // Handler for BN_CLICKED on ADMC_IDC_LCP_MOVE_DOWN LRESULT OnMoveDown( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { int nItem; ObjT * pobj; // // Find the index of the selected item. // nItem = m_lvcRight.GetNextItem( -1, LVNI_SELECTED ); ATLASSERT( nItem != -1 ); // // Get the item pointer. // pobj = (ObjT *) m_lvcRight.GetItemData( nItem ); ATLASSERT( pobj != NULL ); // Remove the selected item from the list and add it back in. { _objptrlistit itRemove; _objptrlistit itAdd; // Find the position of the item to be removed and the item after // which the item is to be inserted. itRemove = std::find( LpobjRight().begin(), LpobjRight().end(), pobj ); ATLASSERT( itRemove != LpobjRight().end() ); itAdd = itRemove++; LpobjRight().insert( itAdd, pobj ); LpobjRight().erase( itRemove ); } // Remove the selected item from the list and add it back in // Remove the selected item from the list control and add it back in. m_lvcRight.DeleteItem( nItem ); NInsertItemInListCtrl( nItem + 1, pobj, m_lvcRight ); m_lvcRight.SetItemState( nItem + 1, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); m_lvcRight.EnsureVisible( nItem + 1, FALSE /*bPartialOK*/ ); m_lvcRight.SetFocus(); return 0; } //*** OnMoveDown() // Handler for BN_CLICKED on ADMC_IDC_LCP_PROPERTIES LRESULT OnProperties( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { int iitem; ObjT * pobj; ATLASSERT( m_plvcFocusList != NULL ); // Get the index of the item with the focus. iitem = m_plvcFocusList->GetNextItem( -1, LVNI_FOCUSED ); ATLASSERT( iitem != -1 ); // Get a pointer to the selected item. pobj = (ObjT *) m_plvcFocusList->GetItemData( iitem ); ATLASSERT( pobj != NULL ); T * pT = static_cast< T * >( this ); if ( pT->BDisplayProperties( pobj ) ) { // Update this item. { CString strText; int iimg = 0; int icol; pT->GetColumnInfo( pobj, iitem, 0, strText, &iimg ); m_plvcFocusList->SetItem( iitem, 0, LVIF_TEXT /*| LVIF_IMAGE*/, strText, iimg, 0, 0, 0 ); for ( icol = 1 ; icol < (int) m_aColumns.size() ; icol++ ) { pT->GetColumnInfo( pobj, iitem, icol, strText, NULL ); m_plvcFocusList->SetItemText( iitem, icol, strText ); } // for: each column } // Update this item } // if: properties changed return 0; } //*** OnProperties() // Handler for BN_CLICKED on IDOK LRESULT OnOK( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { // // Save dialog data and exit the dialog. // if ( BSaveChanges() ) { EndDialog( IDOK ); } // if: dialgo data saved return 0; } //*** OnOK() // Handler for BN_CLICKED on IDCANCEL LRESULT OnCancel( IN WORD wNotifyCode, IN int idCtrl, IN HWND hwndCtrl, IN OUT BOOL & bHandled ) { // // Exit the dialog. // EndDialog( IDCANCEL ); return 0; } //*** OnCancel() // Handler for NM_DBLCLK on ADMC_IDC_LCP_LEFT_LIST & ADMC_IDC_LCP_RIGHT_LIST LRESULT OnDblClkList( IN int idCtrl, IN LPNMHDR pnmh, IN OUT BOOL & bHandled ) { ATLASSERT( ! BReadOnly() ); LRESULT lResult; if ( idCtrl == ADMC_IDC_LCP_LEFT_LIST ) { m_plvcFocusList = &m_lvcLeft; lResult = OnAdd( BN_CLICKED, idCtrl, pnmh->hwndFrom, bHandled ); } // if: double-clicked in left list else if ( idCtrl == ADMC_IDC_LCP_RIGHT_LIST ) { m_plvcFocusList = &m_lvcRight; lResult = OnRemove( BN_CLICKED, idCtrl, pnmh->hwndFrom, bHandled ); } // else if: double-clicked in right list else { ATLASSERT( 0 ); lResult = 0; } // else: double-clicked in an unknown location return lResult; } //*** OnDblClkList() // Handler for LVN_ITEMCHANGED on ADMC_IDC_LCP_LEFT_LIST & ADMC_IDC_LCP_RIGHT_LIST LRESULT OnItemChangedList( IN int idCtrl, IN LPNMHDR pnmh, IN OUT BOOL & bHandled ) { NM_LISTVIEW * pNMListView = (NM_LISTVIEW *) pnmh; BOOL bEnable; CButton * ppb; if ( idCtrl == ADMC_IDC_LCP_LEFT_LIST ) { m_plvcFocusList = &m_lvcLeft; ppb = &m_pbAdd; } // if: item changed in left list else if ( idCtrl == ADMC_IDC_LCP_RIGHT_LIST ) { m_plvcFocusList = &m_lvcRight; ppb = &m_pbRemove; } // else if: item changed in right list else { ATLASSERT( 0 ); bHandled = FALSE; return 0; } // else: unknown list ATLASSERT( ppb != NULL ); // If the selection changed, enable/disable the Add button. if ( (pNMListView->uChanged & LVIF_STATE) && ( (pNMListView->uOldState & LVIS_SELECTED) || (pNMListView->uNewState & LVIS_SELECTED) ) && ! BReadOnly() ) { UINT cSelected = m_plvcFocusList->GetSelectedCount(); // // If there is a selection, enable the Add or Remove button. // Otherwise disable it. // bEnable = (cSelected != 0); ppb->EnableWindow( bEnable ); if ( BPropertiesButton() ) { m_pbProperties.EnableWindow( (cSelected == 1) ? TRUE : FALSE ); } // if: dialog has Properties button // // If the right list is ordered, setup the state of the Up/Down buttons. // if ( BOrdered() ) { SetUpDownState(); } // if: right list is ordered } // if: selection changed return 0; } //*** OnItemChangedList() // Handler for LVN_COLUMNCLICK on ADMC_IDC_LCP_LEFT_LIST & ADMC_IDC_LCP_RIGHT_LIST LRESULT OnColumnClickList( IN int idCtrl, IN LPNMHDR pnmh, IN OUT BOOL & bHandled ) { NM_LISTVIEW * pNMListView = (NM_LISTVIEW *) pnmh; if ( idCtrl == ADMC_IDC_LCP_LEFT_LIST ) { m_plvcFocusList = &m_lvcLeft; m_psiCur = &SiLeft(); } // if: column clicked in left list else if ( idCtrl == ADMC_IDC_LCP_RIGHT_LIST ) { m_plvcFocusList = &m_lvcRight; m_psiCur = &SiRight(); } // else if: column clicked in right list else { ATLASSERT( 0 ); bHandled = FALSE; return 0; } // else: column clicked in unknown list // Save the current sort column and direction. if ( pNMListView->iSubItem == m_psiCur->m_nColumn ) { m_psiCur->m_nDirection ^= -1; } // if: sorting same column again else { m_psiCur->m_nColumn = pNMListView->iSubItem; m_psiCur->m_nDirection = 0; } // else: different column // Sort the list. if ( ! m_plvcFocusList->SortItems( CompareItems, (LPARAM) this ) ) { ATLASSERT( 0 ); } // if: error sorting items return 0; } //*** OnColumnClickList // // Message handler overrides. // // Handler for the WM_INITDIALOG message BOOL OnInitDialog( void ) { #if DBG T * pT = static_cast< T * >( this ); ATLASSERT( pT->PlpobjRight() != NULL ); ATLASSERT( pT->PlpobjLeft() != NULL ); #endif // DBG BOOL fReturn = FALSE; // // Attach the controls to control member variables. // AttachControl( m_lvcRight, ADMC_IDC_LCP_RIGHT_LIST ); AttachControl( m_lvcLeft, ADMC_IDC_LCP_LEFT_LIST ); AttachControl( m_pbAdd, ADMC_IDC_LCP_ADD ); AttachControl( m_pbRemove, ADMC_IDC_LCP_REMOVE ); if ( BPropertiesButton() ) { AttachControl( m_pbProperties, ADMC_IDC_LCP_PROPERTIES ); } // if: dialog has Properties button if ( BCanBeOrdered() ) { AttachControl( m_pbMoveUp, ADMC_IDC_LCP_MOVE_UP ); AttachControl( m_pbMoveDown, ADMC_IDC_LCP_MOVE_DOWN ); } // if: left list can be ordered // if ( BShowImages() ) // { // CClusterAdminApp * papp = GetClusterAdminApp(); // // m_lvcLeft.SetImageList( papp->PilSmallImages(), LVSIL_SMALL ); // m_lvcRight.SetImageList( papp->PilSmallImages(), LVSIL_SMALL ); // } // if: showing images // // Disable buttons by default. // m_pbAdd.EnableWindow( FALSE ); m_pbRemove.EnableWindow( FALSE ); if ( BPropertiesButton() ) { m_pbProperties.EnableWindow( FALSE ); } // if: dialog has Properties button // // Set the right list to sort if not ordered. Set both to show selection always. // if ( BOrdered() ) { m_lvcRight.ModifyStyle( 0, LVS_SHOWSELALWAYS, 0 ); } // if: right list is ordered else { m_lvcRight.ModifyStyle( 0, LVS_SHOWSELALWAYS | LVS_SORTASCENDING, 0 ); } // else: right list is not ordered m_lvcLeft.ModifyStyle( 0, LVS_SHOWSELALWAYS, 0 ); // // If this is an ordered list, show the Move buttons. // Otherwise, hide them. // if ( BCanBeOrdered() ) { SetUpDownState(); } // if: list can be ordered // // Change left list view control extended styles. // m_lvcLeft.SetExtendedListViewStyle( LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP ); // // Change right list view control extended styles. // m_lvcRight.SetExtendedListViewStyle( LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP ); // Duplicate lists. DuplicateLists(); // // Insert all the columns. // { int icol; int ncol; size_t nUpperBound = m_aColumns.size(); CString strColText; ATLASSERT( nUpperBound > 0 ); for ( icol = 0 ; icol < static_cast< int >( nUpperBound ) ; icol++ ) { strColText.LoadString( m_aColumns[icol].m_idsText ); ncol = m_lvcLeft.InsertColumn( icol, strColText, LVCFMT_LEFT, m_aColumns[icol].m_nWidth, 0 ); ATLASSERT( ncol == icol ); ncol = m_lvcRight.InsertColumn( icol, strColText, LVCFMT_LEFT, m_aColumns[icol].m_nWidth, 0 ); ATLASSERT( ncol == icol ); } // for: each column } // Insert all the columns // // Fill the list controls. // FillList( m_lvcRight, LpobjRight() ); FillList( m_lvcLeft, LpobjLeft() ); // // If read-only, set all controls to be either disabled or read-only. // if ( BReadOnly() ) { m_lvcRight.EnableWindow( FALSE ); m_lvcLeft.EnableWindow( FALSE ); } // if: sheet is read-only // // Call the base class method. // fReturn = static_cast< BOOL >( BaseT::OnInitDialog() ); return fReturn; } //*** OnInitDialog() // Handler for PSN_SETACTIVE BOOL OnSetActive( void ) { UINT nSelCount; // Set the focus to the left list. m_lvcLeft.SetFocus(); m_plvcFocusList = &m_lvcLeft; // Enable/disable the Properties button. nSelCount = m_lvcLeft.GetSelectedCount(); if ( BPropertiesButton() ) { m_pbProperties.EnableWindow( nSelCount == 1 ); } // if: dialog has Properties button // Enable or disable the other buttons. if ( ! BReadOnly() ) { m_pbAdd.EnableWindow( nSelCount > 0 ); nSelCount = m_lvcRight.GetSelectedCount(); m_pbRemove.EnableWindow( nSelCount > 0 ); SetUpDownState(); } // if: not read-only page return TRUE; } //*** OnSetActive() public: _objptrlist & LpobjRight( void ) { return m_lpobjRight; } _objptrlist & LpobjLeft( void ) { return m_lpobjLeft; } protected: void DuplicateLists( void ) { LpobjRight().erase( LpobjRight().begin(), LpobjRight().end() ); LpobjLeft().erase( LpobjLeft().begin(), LpobjLeft().end() ); T * pT = static_cast< T * >( this ); if ( (pT->PlpobjRight() == NULL) || (pT->PlpobjLeft() == NULL) ) { return; } // if: either list is empty // // Duplicate the lists. // LpobjRight() = *pT->PlpobjRight(); LpobjLeft() = *pT->PlpobjLeft(); // // Remove all the items that are in the right list from // the left list. // _objptrlistit itRight; _objptrlistit itLeft; for ( itRight = LpobjRight().begin() ; itRight != LpobjRight().end() ; itRight++ ) { // // Find the item in the left list. // itLeft = std::find( LpobjLeft().begin(), LpobjLeft().end(), *itRight ); if ( itLeft != LpobjLeft().end() ) { LpobjLeft().erase( itLeft ); } // if: object found in left list } // for: each item in the right list } //*** DuplicateLists() // Fill a list control void FillList( IN OUT CListViewCtrl & rlvc, IN const _objptrlist & rlpobj ) { _objptrlistit itpobj; ObjT * pobj; int iItem; // Initialize the control. if ( ! rlvc.DeleteAllItems() ) { ATLASSERT( 0 ); } // if: error deleting all items rlvc.SetItemCount( static_cast< int >( rlpobj.size() ) ); // Add the items to the list. itpobj = rlpobj.begin(); for ( iItem = 0 ; itpobj != rlpobj.end() ; iItem++, itpobj++ ) { pobj = *itpobj; NInsertItemInListCtrl( iItem, pobj, rlvc ); } // for: each string in the list // If there are any items, set the focus on the first one. if ( rlvc.GetItemCount() != 0) { rlvc.SetItemState( 0, LVIS_FOCUSED, LVIS_FOCUSED ); } // if: items were added to the list } //*** FillList() // Move items from one list to another void MoveItems( IN OUT CListViewCtrl & rlvcDst, IN OUT _objptrlist & rlpobjDst, IN OUT CListViewCtrl & rlvcSrc, IN OUT _objptrlist & rlpobjSrc ) { int iSrcItem; int iDstItem; int nItem = -1; ObjT * pobj; _objptrlistit itpobj; ATLASSERT( ! BReadOnly() ); iDstItem = rlvcDst.GetItemCount(); while ( (iSrcItem = rlvcSrc.GetNextItem( -1, LVNI_SELECTED )) != -1 ) { // Get the item pointer. pobj = (ObjT *) rlvcSrc.GetItemData( iSrcItem ); ATLASSERT( pobj ); // Remove the item from the source list. itpobj = std::find( rlpobjSrc.begin(), rlpobjSrc.end(), pobj ); ATLASSERT( itpobj != rlpobjSrc.end() ); rlpobjSrc.remove( *itpobj ); // Add the item to the destination list. rlpobjDst.insert( rlpobjDst.end(), pobj ); // Remove the item from the source list control and // add it to the destination list control. if ( ! rlvcSrc.DeleteItem( iSrcItem ) ) { ATLASSERT( 0 ); } // if: error deleting the item nItem = NInsertItemInListCtrl( iDstItem++, pobj, rlvcDst ); rlvcDst.SetItemState( nItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); } // while: more items ATLASSERT( nItem != -1 ); rlvcDst.EnsureVisible( nItem, FALSE /*bPartialOK*/ ); rlvcDst.SetFocus(); // Indicate that the data has changed. ::SendMessage( GetParent(), PSM_CHANGED, (WPARAM) m_hWnd, NULL ); } //*** MoveItems() BOOL BSaveChanges( void ) { ATLASSERT( ! BIsStyleSet( LCPS_DONT_OUTPUT_RIGHT_LIST ) ); ATLASSERT( ! BReadOnly() ); T * pT = static_cast< T * >( this ); // // Update the data first. // if ( ! pT->UpdateData( TRUE /*bSaveAndValidate*/ ) ) { return FALSE; } // if: error updating data // // Copy the object list. // *pT->PlpobjRight() = LpobjRight(); return TRUE; } //*** BSaveChanges() // Set the state of the Up/Down buttons based on the selection. void SetUpDownState( void ) { BOOL bEnableUp; BOOL bEnableDown; if ( BOrdered() && ! BReadOnly() && (m_lvcRight.GetSelectedCount() == 1) ) { int nItem; bEnableUp = TRUE; bEnableDown = TRUE; // // Find the index of the selected item. // nItem = m_lvcRight.GetNextItem( -1, LVNI_SELECTED ); ATLASSERT( nItem != -1 ); // // If the first item is selected, can't move up. // if ( nItem == 0 ) { bEnableUp = FALSE; } // if: first item is selected // // If the last item is selected, can't move down. // if ( nItem == m_lvcRight.GetItemCount() - 1 ) { bEnableDown = FALSE; } // if: last item is selected } // if: only one item selected else { bEnableUp = FALSE; bEnableDown = FALSE; } // else: zero or more than one item selected m_pbMoveUp.EnableWindow( bEnableUp ); m_pbMoveDown.EnableWindow( bEnableDown ); } //*** SetUpDownState() static int CALLBACK CompareItems( LPARAM lparam1, LPARAM lparam2, LPARAM lparamSort ) { ObjT * pobj1 = reinterpret_cast< ObjT * >( lparam1 ); ObjT * pobj2 = reinterpret_cast< ObjT * >( lparam2 ); T * plcp = reinterpret_cast< T * >( lparamSort ); SortInfo * psiCur = plcp->PsiCur(); int icol = psiCur->m_nColumn; int nResult; CString str1; CString str2; ATLASSERT( pobj1 != NULL ); ATLASSERT( pobj2 != NULL ); ATLASSERT( plcp != NULL ); ATLASSERT( psiCur->m_nColumn >= 0 ); ATLASSERT( icol >= 0 ); plcp->GetColumnInfo( pobj1, 0, icol, str1, NULL ); plcp->GetColumnInfo( pobj2, 0, icol, str2, NULL ); nResult = str1.Compare( str2 ); // Return the result based on the direction we are sorting. if ( psiCur->m_nDirection != 0 ) { nResult = -nResult; } // if: sorting in reverse direction return nResult; } //*** CompareItems() SortInfo m_siLeft; SortInfo m_siRight; SortInfo * m_psiCur; SortInfo & SiLeft( void ) { return m_siLeft; } SortInfo & SiRight( void ) { return m_siRight; } public: SortInfo * PsiCur( void ) const { return m_psiCur; } }; //*** class CListCtrlPair ///////////////////////////////////////////////////////////////////////////// #endif // __ATLLCPAIR_H_