// ========================================================================== // Class Implementation : COXHistoryCombo // ========================================================================== // Source file : OXHistoryCombo.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXHistoryCombo.h" #include "commctrl.h" // For toolbar text handler #include // for WM_IDLEUPDATECMDUI #include "UTB64Bit.h" #include "UTBStrOp.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // determine number of elements in an array (not bytes) #ifndef _countof #define _countof(array) (sizeof(array)/sizeof(array[0])) #endif // _countof static const TCHAR szSoftware[] = _T("Software"); static const TCHAR szHistoryCombo[] = _T("HistoryCombo"); IMPLEMENT_DYNAMIC(COXHistoryCombo, CComboBox) ///////////////////////////////////////////////////////////////////////////// // Definition of static members COXHistoryCombo::EToolbarPosition COXHistoryCombo::TBP_FIRST = COXHistoryCombo::TBPNone; COXHistoryCombo::EToolbarPosition COXHistoryCombo::TBP_LAST = COXHistoryCombo::TBPHorizontalRightBottom; // Data members ------------------------------------------------------------- // protected: // EToolbarPosition m_eToolbarPosition; // --- The current position of the toolbar seen from this control // BOOL m_bUseGap; // --- Whether a gap should be shown between the combo box and the toolbar // int m_nMaxHistoryCount; // --- Maximum number of entries allowed in the list of the combo box // CToolBar* m_pToolbar; // --- The toolbar window // This window iss a sibling of this control, but this control is its owner // This way it receives important messages from the toolbar // BOOL m_rgbShowToolButton[OX_HISTORY_COMBO_MAX_TOOLBUTTONS]; // --- An array with one entry for each button // Whether the button is marked as visible or not // LPCTSTR m_lpszToolbarResource; // --- Resource name of the toolbar // BOOL m_bAutoPersistent; // --- Whether auto persistent mode is on (restore on creation, save on destruction) // CString m_sAutoPersistentCompany; // --- Company name for auto persistent // CString m_sAutoPersistentApplication; // --- Application name for auto persistent // CString m_sAutoPersistentValueName; // --- Value name for auto persistent // struct CFileOpenParams m_fileOpenParams; // --- Variable containing the information needed to popup a file open dialog // private: // Member functions --------------------------------------------------------- // public: BEGIN_MESSAGE_MAP(COXHistoryCombo, CComboBox) //{{AFX_MSG_MAP(COXHistoryCombo) ON_WM_CREATE() ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI) ON_WM_DESTROY() ON_COMMAND(ID_OX_HISTORY_COMBO_NEW, OnNew) ON_UPDATE_COMMAND_UI(ID_OX_HISTORY_COMBO_NEW, OnUpdateNew) ON_COMMAND(ID_OX_HISTORY_COMBO_DELETE, OnDelete) ON_UPDATE_COMMAND_UI(ID_OX_HISTORY_COMBO_DELETE, OnUpdateDelete) ON_COMMAND(ID_OX_HISTORY_COMBO_BROWSE, OnBrowse) ON_UPDATE_COMMAND_UI(ID_OX_HISTORY_COMBO_BROWSE, OnUpdateBrowse) ON_COMMAND(ID_OX_HISTORY_COMBO_RESERVED_1, OnReserved1) ON_UPDATE_COMMAND_UI(ID_OX_HISTORY_COMBO_RESERVED_1, OnUpdateReserved1) ON_COMMAND(ID_OX_HISTORY_COMBO_RESERVED_2, OnReserved2) ON_UPDATE_COMMAND_UI(ID_OX_HISTORY_COMBO_RESERVED_2, OnUpdateReserved2) ON_WM_WINDOWPOSCHANGED() ON_WM_ENABLE() //}}AFX_MSG_MAP ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText) ON_COMMAND_EX_RANGE(0, 0xFFFF, OnToolbarCommand) ON_UPDATE_COMMAND_UI_RANGE(0, 0xFFFF, OnToolbarUpdateUI) END_MESSAGE_MAP() COXHistoryCombo::COXHistoryCombo(EToolbarPosition eToolbarPosition /* = TBPHorizontalRightCenter */, BOOL bUseGap /* = FALSE */, LPCTSTR lpszToolbarResource /* = MAKEINTRESOURCE(IDR_HISTORY_COMBO_TOOLS) */) : m_eToolbarPosition(eToolbarPosition), m_bUseGap(bUseGap), m_nMaxHistoryCount(6), m_pToolbar(NULL), m_lpszToolbarResource(NULL), m_bAutoPersistent(FALSE) { // ... eToolbarPosition must be valid ASSERT(TBP_FIRST <= eToolbarPosition); ASSERT(eToolbarPosition <= TBP_LAST); // Initialize the visibility of the buttons // ... Set the non-reserved buttons to visible by default for (int nButtonIndex = 0; nButtonIndex < OX_HISTORY_COMBO_MAX_TOOLBUTTONS; nButtonIndex++) { // ... Hide the reserved buttons ( ID_HISTORY_COMBO_RESERVED_1 and // ID_HISTORY_COMBO_RESERVED_2 ) m_rgbShowToolButton[nButtonIndex] = (nButtonIndex<3); } ASSERT(5 <= OX_HISTORY_COMBO_MAX_TOOLBUTTONS); m_rgbShowToolButton[3] = FALSE; m_rgbShowToolButton[4] = FALSE; // ... Construct the toolbar (should exist at all times) m_pToolbar = new CToolBar(); // Set the toolbar resource SetToolbarResource(lpszToolbarResource); ASSERT_VALID(this); } COXHistoryCombo::~COXHistoryCombo() { ASSERT_VALID(this); if(::IsWindow(GetSafeHwnd())) { DestroyWindow(); } // ... Toolbar should have been constructed ASSERT(m_pToolbar!=NULL); if(::IsWindow(m_pToolbar->GetSafeHwnd())) { m_pToolbar->DestroyWindow(); } // We destruct the toolbar now delete m_pToolbar; m_pToolbar = NULL; } void COXHistoryCombo:: SetToolbarResource(UINT nIDToolbarResource/*=IDR_HISTORY_COMBO_TOOLS*/) { SetToolbarResource(MAKEINTRESOURCE(nIDToolbarResource)); } void COXHistoryCombo:: SetToolbarResource(LPCTSTR lpszToolbarResource/*=MAKEINTRESOURCE(IDR_HISTORY_COMBO_TOOLS)*/) { #ifdef _DEBUG if (AfxFindResourceHandle(lpszToolbarResource, RT_TOOLBAR) == NULL) { TRACE0("COXHistoryCombo::SetToolbarResource : Specified toolbar resource not found\n"); // ... Must find the resource // (If you are using the default IDR_HISTORY_COMBO_TOOLS, // make sure OXHistoryCombo.rc is included in your resource file) ASSERT(FALSE); } #endif // _DEBUG m_lpszToolbarResource = lpszToolbarResource; ASSERT_VALID(this); } void COXHistoryCombo::InitCombo(CWnd* pParentWnd /* = NULL */) { ASSERT_VALID(this); #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif // Create a toolbar as sibling of this control // ... Invisible by default, we will show it when the correct dimensions are knwon // in PositionToolbar // ... Get the parent window if none was specified if (pParentWnd == NULL) { pParentWnd = GetParent(); } ASSERT(pParentWnd != NULL); // ... Toolbar should have been constructed, but not created yet ASSERT(m_pToolbar!=NULL); ASSERT(m_pToolbar->m_hWnd==NULL); VERIFY(m_pToolbar->Create(pParentWnd, WS_CHILD | CBRS_TOOLTIPS)); m_pToolbar->SetWindowPos( &wndTop, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE ); SetWindowPos( &wndTop, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE ); // Set the recipient for notifications messages to this window // (This will not become the actual owner : see also COXHistoryCombo::OnDestroy) m_pToolbar->SetOwner(this); RefreshToolbar(); #ifdef _DEBUG // Check the styles of the control if ((GetStyle() & CBS_AUTOHSCROLL) != CBS_AUTOHSCROLL) TRACE0("COXHistoryCombo::InitCombo : Control does not have the CBS_AUTOHSCROLL style, text may be truncated\n"); if ((GetStyle() & CBS_SORT) == CBS_SORT) TRACE0("COXHistoryCombo::InitCombo : Control has the CBS_SORT style, new items will not be added at the top\n"); #endif // _DEBUG // Start auto persistence if necessary if (m_bAutoPersistent) { RestoreContents(m_sAutoPersistentValueName, m_sAutoPersistentCompany, m_sAutoPersistentApplication); } ASSERT_VALID(this); } void COXHistoryCombo::PositionToolbar(EToolbarPosition eToolbarPosition/*=TBPHorizontalRightCenter*/, BOOL bUseGap/*=TRUE*/) { ASSERT(TBP_FIRST <= eToolbarPosition); ASSERT(eToolbarPosition <= TBP_LAST); ASSERT_VALID(this); // Store new setting m_bUseGap = bUseGap; m_eToolbarPosition = eToolbarPosition; int nCount=GetVisibleButtonCount(); m_pToolbar->ShowWindow( (nCount == 0 || eToolbarPosition == TBPNone) ? SW_HIDE : SW_SHOW); if (nCount == 0 || eToolbarPosition == TBPNone) { // Invisible toolbar : nothing more to do ASSERT_VALID(this); return; } CRect rect; CRect toolRect(0,0,0,0); CSize toolSize; CSize offset; GetWindowRect(rect); GetParent()->ScreenToClient(rect); // ... Change bar style to reflect horizontal orientation DWORD nOldStyle = m_pToolbar->GetBarStyle(); m_pToolbar->SetBarStyle(nOldStyle | CBRS_ORIENT_HORZ); // ... Get the bar size toolSize = m_pToolbar->CalcFixedLayout(FALSE /* bStretch */, TRUE /* bHorz */); toolRect.right = toolRect.left + toolSize.cx; toolRect.bottom = toolRect.top + toolSize.cy; // Then adjust the position of the toolbar // ... Get the border size of the toolbar int cxToolbarLeftBorder = m_pToolbar->m_cxLeftBorder; int cxToolbarRightBorder = m_pToolbar->m_cxRightBorder; int cyToolbarTopBorder = m_pToolbar->m_cyTopBorder; int cyToolbarBottomBorder = m_pToolbar->m_cyBottomBorder; int nBorderFactor = m_bUseGap ? 0 : 1; // ... Handle all possible cases switch (eToolbarPosition) { case TBPHorizontalTopLeft: // ... Top (Horizontal) offset.cy = rect.top - toolRect.Height() + cyToolbarTopBorder * nBorderFactor; // ... Left (Horizontal) offset.cx = rect.left - cxToolbarLeftBorder; break; case TBPHorizontalTopCenter: // ... Top (Horizontal) offset.cy = rect.top - toolRect.Height() + cyToolbarTopBorder * nBorderFactor; // ... Center (Horizontal) offset.cx = rect.left + (rect.Width() - toolRect.Width()) / 2; break; case TBPHorizontalTopRight: // ... Top (Horizontal) offset.cy = rect.top - toolRect.Height() + cyToolbarTopBorder * nBorderFactor; // ... Right (Horizontal) offset.cx = rect.right - toolRect.Width() + cxToolbarRightBorder; break; case TBPHorizontalBottomLeft: // ... Bottom (Horizontal) offset.cy = rect.bottom - cyToolbarBottomBorder * nBorderFactor; // ... Left (Horizontal) offset.cx = rect.left - cxToolbarLeftBorder; break; case TBPHorizontalBottomCenter: // ... Bottom (Horizontal) offset.cy = rect.bottom - cyToolbarBottomBorder * nBorderFactor; // ... Center (Horizontal) offset.cx = rect.left + (rect.Width() - toolRect.Width()) / 2; break; case TBPHorizontalBottomRight: // ... Bottom (Horizontal) offset.cy = rect.bottom - cyToolbarBottomBorder * nBorderFactor; // ... Right (Horizontal) offset.cx = rect.right - toolRect.Width() + cxToolbarRightBorder; break; case TBPHorizontalLeftTop: // ... Top (Horizontal) offset.cy = rect.top - cyToolbarTopBorder; // ... Left (Horizontal) offset.cx = rect.left - toolRect.Width() + cxToolbarLeftBorder * nBorderFactor; break; case TBPHorizontalLeftCenter: // ... Center (Horizontal) offset.cy = rect.top + (rect.Height() - toolRect.Height()) / 2; // ... Left (Horizontal) offset.cx = rect.left - toolRect.Width() + cxToolbarLeftBorder * nBorderFactor; break; case TBPHorizontalLeftBottom: // ... Bottom (Horizontal) offset.cy = rect.top + (rect.Height() - toolRect.Height()) + cyToolbarBottomBorder; // ... Left (Horizontal) offset.cx = rect.left - toolRect.Width() + cxToolbarLeftBorder * nBorderFactor; break; case TBPHorizontalRightTop: // ... Top (Horizontal) offset.cy = rect.top - cyToolbarTopBorder; // ... Right (Horizontal) offset.cx = rect.right - cxToolbarRightBorder * nBorderFactor; break; case TBPHorizontalRightCenter: // ... Center (Horizontal) offset.cy = rect.top + (rect.Height() - toolRect.Height()) / 2; // ... Right (Horizontal) offset.cx = rect.right - cxToolbarRightBorder * nBorderFactor; break; case TBPHorizontalRightBottom: // ... Bottom (Horizontal) offset.cy = rect.top + (rect.Height() - toolRect.Height()) + cyToolbarBottomBorder; // ... Right (Horizontal) offset.cx = rect.right - cxToolbarRightBorder * nBorderFactor; break; default: TRACE1("COXHistoryCombo::PositionToolbar : Unexpected case in switch (%i)\n", eToolbarPosition); ASSERT(FALSE); break; } toolRect += offset; // Reposition the toolbar m_pToolbar->MoveWindow(toolRect); // ... If already visible : redraw m_pToolbar->Invalidate(); ASSERT_VALID(this); } COXHistoryCombo::EToolbarPosition COXHistoryCombo::GetToolbarPosition() const { ASSERT_VALID(this); return m_eToolbarPosition; } void COXHistoryCombo::ShowButton(int nButtonIndex, BOOL bShow /* = FALSE */) { ASSERT_VALID(this); ASSERT(nButtonIndex < OX_HISTORY_COMBO_MAX_TOOLBUTTONS); m_rgbShowToolButton[nButtonIndex] = bShow; ASSERT_VALID(this); } BOOL COXHistoryCombo::IsButtonShown(int nButtonIndex) const { ASSERT_VALID(this); ASSERT(nButtonIndex < OX_HISTORY_COMBO_MAX_TOOLBUTTONS); return m_rgbShowToolButton[nButtonIndex]; } void COXHistoryCombo::SetMaxHistoryCount(int nMaxHistoryCount) { ASSERT_VALID(this); m_nMaxHistoryCount = nMaxHistoryCount; // If the list contains more items than allowed, truncate the list // ... Cast to DWORD so that -1 becomes a very large number if ((DWORD)m_nMaxHistoryCount < (DWORD)GetCount()) { for (int nItemIndex = GetCount() - 1; m_nMaxHistoryCount <= nItemIndex; nItemIndex--) { DeleteString(nItemIndex); } } ASSERT_VALID(this); } int COXHistoryCombo::GetMaxHistoryCount() const { ASSERT_VALID(this); return m_nMaxHistoryCount; } void COXHistoryCombo::RefreshToolbar() { ASSERT_VALID(this); // First reload the toolbar ASSERT(m_pToolbar != NULL); ASSERT(m_pToolbar->m_hWnd != NULL); VERIFY(m_pToolbar->LoadToolBar(m_lpszToolbarResource)); // Remove buttons that are marked as invisible RemoveButtons(); // Reposition the toolbar (extra buttons may have become visible) PositionToolbar(GetToolbarPosition()); ASSERT_VALID(this); } BOOL COXHistoryCombo::AddNewItem(LPCTSTR pszItemText /* = NULL */) { ASSERT_VALID(this); CString sNewItem(pszItemText); if (sNewItem.IsEmpty()) // ... No text was specified, get the text from the edit control GetWindowText(sNewItem); if (sNewItem.IsEmpty()) // ... Do not add empty string to the list return FALSE; // Check whether the item is already in the list or not BOOL bAlreadyInList = FALSE; int nOldPos = FindStringExact(-1, sNewItem); if (nOldPos != CB_ERR) { // ... Already in the list, remove the old item DeleteString(nOldPos); bAlreadyInList = TRUE; } // ... Insert the new string at the top of the list and select it InsertString(0, sNewItem); SetCurSel(0); // If the list contains more items than allowed, truncate the list // ... Cast to DWORD so that -1 becomes a very large number if ((DWORD)m_nMaxHistoryCount < (DWORD)GetCount()) { for (int nItemIndex = GetCount() - 1; m_nMaxHistoryCount <= nItemIndex; nItemIndex--) { DeleteString(nItemIndex); } } // ... Select the entire string SetEditSel(0, -1); ASSERT_VALID(this); return !bAlreadyInList; } BOOL COXHistoryCombo::DeleteItem(int nItemIndex /* = -1 */) { ASSERT_VALID(this); if (nItemIndex == -1) // ... No index was specified, get the current selection nItemIndex = GetCurSel(); BOOL bSuccess = (DeleteString(nItemIndex) != CB_ERR); if (bSuccess) { // Select the next item if it exists if (nItemIndex < GetCount()) { // ... Items have shifted up, select the same index SetCurSel(nItemIndex); CWnd* pParentWnd=GetParent(); if(pParentWnd!=NULL) { pParentWnd->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(),CBN_SELCHANGE),(LPARAM)GetSafeHwnd()); } } else // ... Last item was deleted, select the current last one (may not exist if the list is empty) SetCurSel(nItemIndex - 1); } return bSuccess; } BOOL COXHistoryCombo::BrowseItem() { ASSERT_VALID(this); CFileDialog fileDlg( m_fileOpenParams.m_bOpenFileDialog, m_fileOpenParams.m_sDefExt.IsEmpty() ? NULL : (LPCTSTR)m_fileOpenParams.m_sDefExt, m_fileOpenParams.m_sFileName.IsEmpty() ? NULL : (LPCTSTR)m_fileOpenParams.m_sFileName, m_fileOpenParams.m_dwFlags, m_fileOpenParams.m_sFilter.IsEmpty() ? NULL : (LPCTSTR)m_fileOpenParams.m_sFilter, m_fileOpenParams.m_pParentWnd); //added 11/12/1999 fileDlg.m_ofn.lpstrInitialDir= m_fileOpenParams.m_sStartDir.IsEmpty() ? NULL : (LPCTSTR) m_fileOpenParams.m_sStartDir; BOOL bBrowseOK = FALSE; if (fileDlg.DoModal() == IDOK) { SetWindowText(fileDlg.GetPathName()); CWnd* pParentWnd=GetParent(); if(pParentWnd!=NULL) { pParentWnd->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(),CBN_EDITCHANGE),(LPARAM)GetSafeHwnd()); } bBrowseOK = TRUE; } ASSERT_VALID(this); return bBrowseOK; } BOOL COXHistoryCombo::CanAddNewItem() const { ASSERT_VALID(this); // ... Can add if text in edit control is not empty return (GetWindowTextLength() != 0); } BOOL COXHistoryCombo::CanDeleteItem() const { ASSERT_VALID(this); // ... Can only delete if one is selected return GetCurSel() != CB_ERR; } BOOL COXHistoryCombo::CanBrowseItem() const { ASSERT_VALID(this); // ... Can always browse by default return TRUE; } void COXHistoryCombo::SetFileDialogParams(BOOL bOpenFileDialog /* = TRUE */, LPCTSTR pszDefExt /* = NULL */, LPCTSTR pszFileName /* = NULL */, DWORD dwFlags /* = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT */, LPCTSTR pszFilter /* = NULL */, CWnd* pParentWnd /* = NULL */, LPCTSTR lpszInitDir/*=NULL*/) { ASSERT_VALID(this); m_fileOpenParams.m_bOpenFileDialog = bOpenFileDialog; m_fileOpenParams.m_sDefExt = pszDefExt; m_fileOpenParams.m_sFileName = pszFileName; m_fileOpenParams.m_dwFlags = dwFlags; m_fileOpenParams.m_sFilter = pszFilter; m_fileOpenParams.m_pParentWnd = pParentWnd; m_fileOpenParams.m_sStartDir=lpszInitDir;//added 11/12/1999 ASSERT_VALID(this); } BOOL COXHistoryCombo::OnIdle(LONG lCount /* = 0 */) { // Update the state of the GUI now // ... We only have to update the first time the idle loop is executed if(lCount==0) { SendMessage(WM_IDLEUPDATECMDUI, (WPARAM)TRUE /* bDisableIfNoHandler */, 0); } // ... We do not need more idle processing return FALSE; } BOOL COXHistoryCombo::SetAutoPersistent(LPCTSTR pszValueName, LPCTSTR pszCompany /* = NULL */, LPCTSTR pszApplication /* = NULL */) { ASSERT_VALID(this); BOOL bSuccess = TRUE; if (m_hWnd != NULL) { // Control is already created, restore persistent values now bSuccess = RestoreContents(pszValueName, pszCompany, pszApplication); } if (bSuccess) { // Only set the new values if the previous save succeeded m_bAutoPersistent = TRUE; m_sAutoPersistentCompany = pszCompany; m_sAutoPersistentApplication = pszApplication; m_sAutoPersistentValueName = pszValueName; } else { TRACE0("COXHistoryCombo::SetAutoPersistent : Failed to save the current contents, failing\n"); } ASSERT_VALID(this); return bSuccess; } void COXHistoryCombo::RemoveAutoPersistent() { ASSERT_VALID(this); m_bAutoPersistent = FALSE; ASSERT_VALID(this); } BOOL COXHistoryCombo::SaveContents(LPCTSTR pszValueName, LPCTSTR pszCompany /* = NULL */, LPCTSTR pszApplication /* = NULL*/ , HKEY hKeyRoot /* = HKEY_CURRENT_USER */, BOOL bAddNewItem /* = TRUE */) { ASSERT_VALID(this); CString sValueName(pszValueName); CString sCompany(pszCompany); CString sApplication(pszApplication); CString sContents; if (sValueName.IsEmpty()) { TRACE0("COXHistoryCombo::SaveContents : No value name provided, failing\n"); return FALSE; } if (sCompany.IsEmpty()) sCompany = AfxGetApp()->m_pszRegistryKey; if (sCompany.IsEmpty()) { TRACE0("COXHistoryCombo::SaveContents : No valid company name is provided, failing\n"); return FALSE; } if (sApplication.IsEmpty()) sApplication = AfxGetApp()->m_pszProfileName; // Add the current contents of the dit control to the list (if not yet one) if (bAddNewItem) AddNewItem(); // Get the contents from the list sContents = GetContents(); BOOL bSuccess = SaveContentsToRegistry(hKeyRoot, sCompany, sApplication, sValueName, sContents); ASSERT_VALID(this); return bSuccess; } BOOL COXHistoryCombo::RestoreContents(LPCTSTR pszValueName, LPCTSTR pszCompany /* = NULL */, LPCTSTR pszApplication /* = NULL*/ , HKEY hKeyRoot /* = HKEY_CURRENT_USER */, BOOL bRestoreFirstItem /* = TRUE */) { ASSERT_VALID(this); CString sValueName(pszValueName); CString sCompany(pszCompany); CString sApplication(pszApplication); CString sContents; if (sValueName.IsEmpty()) { TRACE0("COXHistoryCombo::SaveContents : No value name provided, failing\n"); return FALSE; } if (sCompany.IsEmpty()) sCompany = AfxGetApp()->m_pszRegistryKey; if (sCompany.IsEmpty()) { TRACE0("COXHistoryCombo::SaveContents : No valid company name is provided, failing\n"); return FALSE; } if (sApplication.IsEmpty()) sApplication = AfxGetApp()->m_pszProfileName; BOOL bSuccess = LoadContentsFromRegistry(hKeyRoot, sCompany, sApplication, sValueName, sContents); if (bSuccess) { // ... Set the contents in the list box SetContents(sContents); // Select the first item from the list if (bRestoreFirstItem) { SetCurSel(0); if (::IsWindow(GetSafeHwnd()) && GetFocus()==this && ::IsWindowEnabled(m_hWnd)) SetEditSel(0, -1); } } ASSERT_VALID(this); return bSuccess; } #ifdef _DEBUG void COXHistoryCombo::AssertValid() const { CComboBox::AssertValid(); // The toolbar object should exist at all times ASSERT(m_pToolbar != NULL); } void COXHistoryCombo::Dump(CDumpContext& dc) const { CComboBox::Dump(dc); dc << "\nTBP_FIRST : " << (int)TBP_FIRST; dc << "\nTBP_LAST : " << (int)TBP_LAST; dc << "\nm_eToolbarPosition : " << (int)m_eToolbarPosition; dc << "\nm_bUseGap : " << m_bUseGap; dc << "\nm_nMaxHistoryCount : " << m_nMaxHistoryCount; dc << "\nm_pToolbar : " << m_pToolbar; dc << "\nm_rgbShowToolButton : "; for (int nIndex = 0; nIndex < OX_HISTORY_COMBO_MAX_TOOLBUTTONS; nIndex++) dc << "\n[" << nIndex << "] : " << m_rgbShowToolButton[nIndex]; dc << "\nm_lpszToolbarResource : " << m_lpszToolbarResource; dc << "\nm_bAutoPersistent : " << m_bAutoPersistent; dc << "\nm_sAutoPersistentCompany : " << m_sAutoPersistentCompany; dc << "\nm_sAutoPersistentApplication : " << m_sAutoPersistentApplication; dc << "\nm_sAutoPersistentValueName : " << m_sAutoPersistentValueName; dc << "\nm_fileOpenParams : " << &m_fileOpenParams; } #endif //_DEBUG BOOL COXHistoryCombo::IsFrameWnd() const { // Mimic a frame window to get the idle Update CmdUI messages from the toolbar return TRUE; } void COXHistoryCombo::RemoveButtons() { ASSERT_VALID(this); ASSERT(AfxIsValidAddress(m_rgbShowToolButton, sizeof(BOOL)*OX_HISTORY_COMBO_MAX_TOOLBUTTONS)); // ... Toolbar should have been constructed ASSERT(m_pToolbar != NULL); // Compose a new button list int nCount=m_pToolbar->GetCount(); ASSERT(OX_HISTORY_COMBO_MAX_TOOLBUTTONS>=nCount); int nNewCount = 0; UINT* rgID = new UINT[nCount]; UINT* rgStyle = new UINT[nCount]; int* rgImage = new int[nCount]; // Iterate all the existing buttons and keep those we need for (int nIndex=0; nIndexGetButtonInfo(nIndex, rgID[nNewCount], rgStyle[nNewCount], rgImage[nNewCount]); nNewCount++; } } // Set the new buttons // ... We will only show the toolbar if at least one button is visible m_pToolbar->ShowWindow((nNewCount == 0) ? SW_HIDE : SW_SHOW); if (nNewCount != 0) { VERIFY(m_pToolbar->SetButtons(NULL, nNewCount)); for (int nNewIndex = 0; nNewIndex < nNewCount; nNewIndex++) { m_pToolbar->SetButtonInfo(nNewIndex, rgID[nNewIndex], rgStyle[nNewIndex], rgImage[nNewIndex]); } } //Clean up arrays delete[] rgID; delete[] rgStyle; delete[] rgImage; ASSERT_VALID(this); } CString COXHistoryCombo::GetContents() const { ASSERT_VALID(this); CString sContents; CString sLBItem; const int nItemCount = GetCount(); for (int nItemIndex = 0; nItemIndex < nItemCount; nItemIndex++) { GetLBText(nItemIndex, sLBItem); if (!sContents.IsEmpty()) sContents += ITEM_SEPERATOR; sContents += sLBItem; } ASSERT_VALID(this); return sContents; } void COXHistoryCombo::SetContents(LPCTSTR pszContents) { ASSERT_VALID(this); CString sContents(pszContents); // First remove everuthing from the combo ResetContent(); // Then add all new the items LPTSTR pszItem = sContents.GetBuffer(0); DWORD nIndex = 0; TCHAR * p; pszItem = UTBStr::tcstok(pszItem, ITEM_SEPERATOR, &p); // ... Cast to DWORD so that -1 becomes a very large number while ((nIndex < (DWORD)m_nMaxHistoryCount) && (pszItem != NULL)) { AddString(pszItem); pszItem = UTBStr::tcstok(NULL, ITEM_SEPERATOR, &p); } sContents.ReleaseBuffer(); ASSERT_VALID(this); } BOOL COXHistoryCombo::SaveContentsToRegistry(HKEY hKeyRoot, LPCTSTR pszCompany, LPCTSTR pszApplication, LPCTSTR pszValueName, LPCTSTR pszValue) { ASSERT(hKeyRoot != NULL); ASSERT((pszCompany != NULL) && (*pszCompany != _T('\0'))); ASSERT((pszApplication != NULL) && (*pszApplication != _T('\0'))); ASSERT((pszValueName != NULL) && (*pszValueName != _T('\0'))); // ... Value may be empty string ASSERT((pszValue != NULL)); // Open key for hKeyRoot\\\\ HKEY hSoftwareKey = NULL; HKEY hCompanyKey = NULL; HKEY hApplicationKey = NULL; HKEY hHistoryKey = NULL; if (::RegOpenKeyEx(hKeyRoot, szSoftware, 0, KEY_WRITE | KEY_READ, &hSoftwareKey) == ERROR_SUCCESS) { DWORD dw; if (::RegCreateKeyEx(hSoftwareKey, pszCompany, 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, &hCompanyKey, &dw) == ERROR_SUCCESS) { if (::RegCreateKeyEx(hCompanyKey, pszApplication, 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, &hApplicationKey, &dw) == ERROR_SUCCESS) { ::RegCreateKeyEx(hApplicationKey, szHistoryCombo, 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, &hHistoryKey, &dw); } } } if (hSoftwareKey != NULL) ::RegCloseKey(hSoftwareKey); if (hCompanyKey != NULL) ::RegCloseKey(hCompanyKey); if (hApplicationKey != NULL) ::RegCloseKey(hApplicationKey); if (hHistoryKey == NULL) { TRACE0("COXHistoryCombo::SaveContentsToRegistry : Failed to open history key\n"); return FALSE; } // Set value LONG nResult; if (*pszValue == _T('\0')) // ... Necessary to cast away const nResult = ::RegDeleteValue(hHistoryKey, (LPTSTR)pszValueName); else nResult = RegSetValueEx(hHistoryKey, pszValueName, NULL, REG_SZ, (LPBYTE)pszValue, PtrToLong((_tcslen(pszValue) + 1) * sizeof(TCHAR))); ::RegCloseKey(hHistoryKey); #ifdef _DEBUG if (nResult != ERROR_SUCCESS) TRACE0("COXHistoryCombo::SaveContentsToRegistry : Failed to set value\n"); #endif // _DEBUG return (nResult == ERROR_SUCCESS); } BOOL COXHistoryCombo::LoadContentsFromRegistry(HKEY hKeyRoot, LPCTSTR pszCompany, LPCTSTR pszApplication, LPCTSTR pszValueName, CString& sValue) { ASSERT(hKeyRoot != NULL); ASSERT((pszCompany != NULL) && (*pszCompany != _T('\0'))); ASSERT((pszApplication != NULL) && (*pszApplication != _T('\0'))); ASSERT((pszValueName != NULL) && (*pszValueName != _T('\0'))); // Open key for hKeyRoot\\\\ HKEY hSoftwareKey = NULL; HKEY hCompanyKey = NULL; HKEY hApplicationKey = NULL; HKEY hHistoryKey = NULL; if (::RegOpenKeyEx(hKeyRoot, szSoftware, 0, KEY_READ, &hSoftwareKey) == ERROR_SUCCESS) { if (::RegOpenKeyEx(hSoftwareKey, pszCompany, 0, KEY_READ, &hCompanyKey) == ERROR_SUCCESS) { if (::RegOpenKeyEx(hCompanyKey, pszApplication, 0, KEY_READ, &hApplicationKey) == ERROR_SUCCESS) { ::RegOpenKeyEx(hApplicationKey,szHistoryCombo,0,KEY_READ,&hHistoryKey); } } } if (hSoftwareKey != NULL) ::RegCloseKey(hSoftwareKey); if (hCompanyKey != NULL) ::RegCloseKey(hCompanyKey); if (hApplicationKey != NULL) ::RegCloseKey(hApplicationKey); if (hHistoryKey == NULL) { // Failed to open history key, return default (empty string) sValue.Empty(); return TRUE; } // Get value DWORD dwType, dwCount; LONG nResult = ::RegQueryValueEx(hHistoryKey, (LPTSTR)pszValueName, NULL, &dwType, NULL, &dwCount); if (nResult == ERROR_SUCCESS) { ASSERT(dwType == REG_SZ); nResult = ::RegQueryValueEx(hHistoryKey, (LPTSTR)pszValueName, NULL, &dwType, (LPBYTE)sValue.GetBuffer(dwCount / sizeof(TCHAR)), &dwCount); sValue.ReleaseBuffer(); ASSERT(dwType == REG_SZ); } ::RegCloseKey(hHistoryKey); if (nResult != ERROR_SUCCESS) // ... Could not get value, use default (empty string) sValue.Empty(); return TRUE; } void COXHistoryCombo::PreSubclassWindow() { ASSERT_VALID(this); // ... Attached window must be valid ASSERT(m_hWnd != NULL); ASSERT(::IsWindow(m_hWnd)); _AFX_THREAD_STATE* pThreadState=AfxGetThreadState(); // hook not already in progress if(pThreadState->m_pWndInit==NULL) { if(GetParent() != NULL) { // Initialize this control InitCombo(); } } // If GetParent() == NULL, this control is being created through Create() // and it is still to ealy to initialize, this will be done in OnCreate CComboBox::PreSubclassWindow(); ASSERT_VALID(this); } int COXHistoryCombo::OnCreate(LPCREATESTRUCT lpCreateStruct) { ASSERT_VALID(this); if (CComboBox::OnCreate(lpCreateStruct) == -1) return -1; // ... Window must be valid ASSERT(m_hWnd != NULL); ASSERT(::IsWindow(m_hWnd)); // Initialize this control // ... Pass on the window that will become the parent (GetParent() returns NULL at this point) InitCombo(CWnd::FromHandle(lpCreateStruct->hwndParent)); ASSERT_VALID(this); return 0; } LRESULT COXHistoryCombo::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam) { ASSERT_VALID(this); // ... Toolbar should have been constructed ASSERT(m_pToolbar != NULL); // Pass on to the toolbar m_pToolbar->SendMessage(WM_IDLEUPDATECMDUI, wParam, lParam); return 0L; } BOOL COXHistoryCombo::OnToolTipText(UINT /* nControlID */, NMHDR* pNMHDR, LRESULT* pResult) { ASSERT_VALID(this); // This code is almost the same as in CFrameWnd::OnToolTipText // see WinFrm.cpp ASSERT(pNMHDR->code == TTN_NEEDTEXT); // need to handle both ANSI and UNICODE versions of the message TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*) pNMHDR; TCHAR szFullText[256]; CString strTipText; UINT_PTR nID = pNMHDR->idFrom; if (pNMHDR->code == TTN_NEEDTEXT && (pTTT->uFlags & TTF_IDISHWND)) { // ... idFrom is actually the HWND of the tool nID = ((UINT)(WORD)::GetDlgCtrlID((HWND)nID)); } // ... nID will be zero on a separator if (nID != 0) { // don't handle the message if no string resource found if(AfxLoadString(PtrToUint(nID),szFullText)==0) { return FALSE; } // ... this is the command id, not the button index AfxExtractSubString(strTipText, szFullText, 1, '\n'); } UTBStr::tcscpy(pTTT->szText, 80, strTipText); // Bring the tooltip window above other popup windows ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE); // Message was handled // ... Message does not use result *pResult = 0; return TRUE; } void COXHistoryCombo::OnDestroy() { ASSERT_VALID(this); CComboBox::OnDestroy(); // ... Toolbar should have been constructed ASSERT(m_pToolbar != NULL); // Note : // A child window is confined to its parent window's client area. // A window is automatically destroyed when its owner is destroyed // Windows does not provide a method to change a window's owner. // See also MSDN : Window Owners and Parents (PSS ID Number: Q84190) // The toolbar is a window created with the WS_CHILD style, so does not have // an explicit owner; it is implicitly owned by its parent // Calling CToolbar::SetOwner will only send the TB_SETPARENT message and set the // CWnd data member m_hWndOwner // So the actual owner of the toolbar is still the parent window, // and thus the toolbar is only destroyed when the parent window is destroyed // We will explicitely destroy the toolbar here m_pToolbar->DestroyWindow(); // When the toolbar is destroyed but not destructed MFC does not reset the // protected data member CToolbar::m_hbmImageWell // Because of this all subsequent LoadToolBar calls will fail // To work around we actually destruct the toolbar and construct a new one. delete m_pToolbar; m_pToolbar = new CToolBar(); // Start auto persistence if necessary if (m_bAutoPersistent) { SaveContents(m_sAutoPersistentValueName,m_sAutoPersistentCompany, m_sAutoPersistentApplication); } ASSERT_VALID(this); } BOOL COXHistoryCombo::OnToolbarCommand(UINT nCommand) { ASSERT_VALID(this); // All WM_COMMANDs which we do not handle ourselves arrive here // Pass this command to our parent WPARAM wParam = (WPARAM)nCommand; LPARAM lParam = (LPARAM)m_pToolbar->m_hWnd; return (BOOL)GetParent()->SendMessage(WM_COMMAND, wParam, lParam); } void COXHistoryCombo::OnToolbarUpdateUI(CCmdUI* pCmdUI) { ASSERT_VALID(this); // All ON_UPDATE_COMMAND_UIs which we do not handle ourselves arrive here // Pass this command to our parent pCmdUI->DoUpdate(GetParent(), TRUE/* bDisableIfNoHndler */); } void COXHistoryCombo::OnNew() { // Add the string to the list box and clear the string AddNewItem(); } void COXHistoryCombo::OnUpdateNew(CCmdUI* pCmdUI) { pCmdUI->Enable(IsWindowEnabled()&CanAddNewItem()); } void COXHistoryCombo::OnDelete() { // Delete the item DeleteItem(); } void COXHistoryCombo::OnUpdateDelete(CCmdUI* pCmdUI) { pCmdUI->Enable(IsWindowEnabled()&CanDeleteItem()); } void COXHistoryCombo::OnBrowse() { // Open the file oppen dialog BrowseItem(); } void COXHistoryCombo::OnUpdateBrowse(CCmdUI* pCmdUI) { pCmdUI->Enable(IsWindowEnabled()&CanBrowseItem()); } void COXHistoryCombo::OnReserved1() { // This function should not be used (yet) ASSERT(FALSE); } void COXHistoryCombo::OnUpdateReserved1(CCmdUI* pCmdUI) { // Disable reserved commands by default pCmdUI->Enable(FALSE); } void COXHistoryCombo::OnReserved2() { // This function should not be used (yet) ASSERT(FALSE); } void COXHistoryCombo::OnUpdateReserved2(CCmdUI* pCmdUI) { // Disable reserved commands by default pCmdUI->Enable(FALSE); } void COXHistoryCombo::OnWindowPosChanged(WINDOWPOS* lpwndpos) { if(::IsWindow(GetToolBar()->GetSafeHwnd())) { PositionToolbar(m_eToolbarPosition); } CComboBox::OnWindowPosChanged(lpwndpos); } void COXHistoryCombo::OnEnable(BOOL bEnable) { CComboBox::OnEnable(bEnable); SetEditSel(0,0); ASSERT(m_pToolbar!=NULL); m_pToolbar->EnableWindow(bEnable); } int COXHistoryCombo::GetVisibleButtonCount() { ASSERT(m_pToolbar!=NULL); int nCount=m_pToolbar->GetCount(); ASSERT(OX_HISTORY_COMBO_MAX_TOOLBUTTONS>=nCount); int nNewCount=0; // Iterate all the existing buttons for (int nIndex=0; nIndex