2025-11-27 16:46:48 +09:00

1275 lines
35 KiB
C++

// ==========================================================================
// 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 <afxpriv.h> // 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; nIndex<nCount; nIndex++)
{
if(m_rgbShowToolButton[nIndex])
{
ASSERT(nNewCount<nCount);
m_pToolbar->GetButtonInfo(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\<szSoftware>\<pszCompany>\<pszApplication>\<szHistoryCombo>
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\<szSoftware>\<pszCompany>\<pszApplication>\<szHistoryCombo>
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<OX_HISTORY_COMBO_MAX_TOOLBUTTONS; nIndex++)
{
if(m_rgbShowToolButton[nIndex])
{
nNewCount++;
}
}
return nNewCount;
}