/////////////////////////////////////////////////////////////////////////////
//
// MRUCombo.cpp: Implementation file for the CNGRecentItemComboBox class.
//
// Written by Michael Dunn <mdunn at inreach dot com>
//
// Modified by Anna-Jayne Metcalfe to use CNGRecentItemList instead of CRecentFileList
//
/////////////////////////////////////////////////////////////////////////////
//
// Revision history:
//
// 9/9/1998: First release.
//
/////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "NGRecentItemComboBox.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define MRUC_DEFAULT_MRU_SIZE 10
/////////////////////////////////////////////////////////////////////////////
// CNGRecentItemComboBox constructor & destructor
//////////////////////////////////////////////////////////////////////////
//
// Function: CNGRecentItemComboBox()
//
// Description:
// Class constructor.
//
// Notes:
// Calls base class constructor and initializes member variables.
//
//////////////////////////////////////////////////////////////////////////
CNGRecentItemComboBox::CNGRecentItemComboBox() : CComboBox(),
m_bRefreshAfterAdd ( FALSE ),
m_bSaveAfterAdd ( FALSE ),
m_bSaveOnDestroy ( TRUE ),
m_nMaxMRUSize ( MRUC_DEFAULT_MRU_SIZE ),
m_pMRU ( NULL ),
m_bParamsChanged ( FALSE )
{
}
//////////////////////////////////////////////////////////////////////////
//
// Function: CNGRecentItemComboBox()
//
// Description:
// Class destructor.
//
//////////////////////////////////////////////////////////////////////////
CNGRecentItemComboBox::~CNGRecentItemComboBox(void)
{ // Save the MRU if we need to.
if ( m_bSaveOnDestroy )
{
if ( !SaveMRU() )
{
TRACE( _T("CNGRecentItemComboBox -- Warning - SaveMRU() in destructor failed. MRU was not saved.\n") );
}
}
// Free up the CRecentFileList object.
if ( NULL != m_pMRU )
{
delete m_pMRU;
}
}
BEGIN_MESSAGE_MAP(CNGRecentItemComboBox, CComboBox)
//{{AFX_MSG_MAP(CNGRecentItemComboBox)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNGRecentItemComboBox MRU operations
//////////////////////////////////////////////////////////////////////////
//
// Function: AddToMRU()
//
// Description:
// Adds a string to the MRU list.
//
// Input:
// szNewItem: [in] The string to add.
//
// Returns:
// TRUE if successful, FALSE if not.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::AddToMRU ( LPCTSTR szNewItem )
{
// String can't be a null pointer!
ASSERT ( NULL != szNewItem );
// Allocate a new CRecentFileList
// if necessary.
if ( NULL == m_pMRU )
{
if ( !AllocNewMRU() )
{
TRACE( _T("CNGRecentItemComboBox -- AllocNewMRU() failed in AddToMRU().\n") );
return FALSE;
}
}
m_pMRU->Add ( szNewItem ); // Add it to the MRU list.
// Automagically refresh the combobox?
if ( m_bRefreshAfterAdd )
{
RefreshCtrl();
}
// Automagically save the MRU?
if ( m_bSaveAfterAdd )
{
SaveMRU();
}
return TRUE;
}
BOOL CNGRecentItemComboBox::RemoveFromMRU(int nItem)
{
if ( (NULL != m_pMRU) && (nItem >= 0) && (nItem < GetCount() ) )
{
DeleteString(nItem);
ASSERT( (*m_pMRU)[nItem].GetLength() > 0 );
m_pMRU->Remove(nItem);
AllocNewMRU();
if (m_bSaveAfterAdd)
{
SaveMRU();
}
RefreshCtrl();
return TRUE;
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: EmptyMRU()
//
// Description:
// Removes all strings from the MRU list.
//
// Input:
// Nothing.
//
// Returns:
// Nothing.
//
//////////////////////////////////////////////////////////////////////////
void CNGRecentItemComboBox::EmptyMRU()
{
int i = 0;
if ( NULL != m_pMRU )
{
// Remove all strings from the MRU list.
// Go in reverse order to keep the
// strings from changing positions
// while we're doing our dirty work.
for ( i = m_pMRU->GetSize() - 1; i >= 0; i-- )
{
m_pMRU->Remove(i);
}
}
}
//////////////////////////////////////////////////////////////////////////
//
// Function: LoadMRU()
//
// Description:
// Loads an MRU from the registry or the app's INI file.
//
// Input:
// Nothing.
//
// Returns:
// TRUE if successful, FALSE if not.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::LoadMRU()
{
// We always allocate a new
// CRecentFileList object when loading.
if ( !AllocNewMRU() )
{
TRACE( _T("CNGRecentItemComboBox -- AllocNewMRU() failed in LoadMRU().\n") );
return FALSE;
}
m_pMRU->ReadList();
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: SaveMRU()
//
// Description:
// Writes the MRU to the registry or app's INI file.
//
// Input:
// Nothing.
//
// Returns:
// TRUE if successful, FALSE if not.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::SaveMRU()
{
// If we haven't created a
// CRecentFileList yet, then there's
// nothing to save.
if ( NULL == m_pMRU )
{
TRACE( _T("CNGRecentItemComboBox -- SaveMRU failed - no CRecentFileList created.\n") );
return FALSE;
}
// Check that the CRecentFileList
// parameters are kosher.
if ( !VerifyMRUParams() )
{
TRACE( _T("CNGRecentItemComboBox -- SaveMRU() failed - params not set.\n") );
return FALSE;
}
// If the registry key/value strings
// have been changed, we need to make
// a new CRecentFileList.
if ( m_bParamsChanged )
{
if ( !AllocNewMRU() )
{
TRACE( _T("CNGRecentItemComboBox -- SaveMRU failed - couldn't reallocate CRecentFileList with new MRU params.\n") );
return FALSE;
}
}
m_pMRU->WriteList();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CNGRecentItemComboBox combobox control operations
//////////////////////////////////////////////////////////////////////////
//
// Function: RefreshCtrl()
//
// Description:
// Sets the contents of the combobox according to the MRU list.
//
// Input:
// Nothing.
//
// Returns:
// Nothing.
//
//////////////////////////////////////////////////////////////////////////
void CNGRecentItemComboBox::RefreshCtrl()
{
CString cstrComboText;
// Save the contents of the edit
// portion of the combobox.
GetWindowText ( cstrComboText );
ResetContent();
for ( int i = 0; i < m_pMRU->GetSize(); i++ )
{
// Don't add empty strings to the combobox.
if ( (*m_pMRU)[i].GetLength() > 0 )
{
if ( AddString ( (*m_pMRU)[i] ) < 0 )
{
TRACE( _T("CNGRecentItemComboBox -- Warning - RefreshCtrl() couldn't add MRU item %d to combobox.\n"),
i );
}
}
}
// Restore the editbox text.
int nItem = (int)SendMessage(CB_FINDSTRINGEXACT,
(WPARAM)-1,
(LPARAM)(LPCTSTR)cstrComboText);
if (nItem >= 0)
{
// select it
SetCurSel(nItem);
}
else
{
// just set the edit text (this will be ignored if this is a drop list)
SetWindowText(cstrComboText);
}
}
/////////////////////////////////////////////////////////////////////////////
// CNGRecentItemComboBox data accessor functions
//////////////////////////////////////////////////////////////////////////
//
// Function: SetMRURegKey()
//
// Description:
// Sets the registry key (or INI file section) in which the MRU will be
// saved.
//
// Input:
// szRegKey: [in] The key/section name.
//
// Returns:
// Nothing.
//
//////////////////////////////////////////////////////////////////////////
void CNGRecentItemComboBox::SetMRURegKey ( LPCTSTR szRegKey )
{
// The key name can't be a null string.
ASSERT ( NULL != szRegKey );
try
{
// Store the reg key name & set the
// changed flag.
m_cstrRegKey = szRegKey;
m_bParamsChanged = TRUE;
}
catch (CMemoryException* e)
{
TRACE( _T("CNGRecentItemComboBox -- Memory exception in CNGRecentItemComboBox::SetMRURegKey()!\n") );
e->Delete();
throw;
}
}
//////////////////////////////////////////////////////////////////////////
//
// Function: GetMRURegKey
//
// Description:
// Returns the current registry key or INI file section in which the MRU
// will be saved.
//
// Input:
// Nothing.
//
// Returns:
// The key name.
//
//////////////////////////////////////////////////////////////////////////
const CString& CNGRecentItemComboBox::GetMRURegKey() const
{
return m_cstrRegKey;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: SetMRUValueFormat()
//
// Description:
// Sets the format to be used for writing MRU items to the registry or
// the app's INI file.
//
// Input:
// szValueFormat: [in] The format to use.
//
// Returns:
// TRUE if the format is acceptable (i.e., contains "%d"), FALSE if not.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::SetMRUValueFormat ( LPCTSTR szValueFormat )
{
BOOL bRetVal = FALSE;
// The key name can't be a null string.
ASSERT ( NULL != szValueFormat );
// Check that the format strings
// contains "%d"
if ( NULL == _tcsstr ( szValueFormat, _T("%d") ) )
{
TRACE( _T("CNGRecentItemComboBox -- SetMRUValueFormat() returning FALSE - argument didn't contain \"%d\"\n") );
return FALSE;
}
else
{
try
{
// Save the format string and set the
// changed flag.
m_cstrRegValueFormat = szValueFormat;
m_bParamsChanged = TRUE;
bRetVal = TRUE;
}
catch (CMemoryException* e)
{
TRACE( _T("CNGRecentItemComboBox -- Memory exception in CNGRecentItemComboBox::SetMRUValueFormat()!\n") );
e->Delete();
throw;
}
}
return bRetVal;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: GetMRUValueFormat
//
// Description:
// Returns the current registry value format string.
//
// Input:
// Nothing.
//
// Returns:
// The format string.
//
//////////////////////////////////////////////////////////////////////////
const CString& CNGRecentItemComboBox::GetMRUValueFormat() const
{
return m_cstrRegValueFormat;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: SetMaxMRUSize()
//
// Description:
// Sets the max number of entries that the MRU can hold.
//
// Input:
// nMaxSize: [in] The new MRU size.
//
// Returns:
// The previous MRU size, or 0 if there was no previous MRU allocated, or
// -1 on error.
//
// Notes:
// This function always reallocates a CRecentFileList so that the size
// change (and registry string changes, if any) takes place immediately.
//
//////////////////////////////////////////////////////////////////////////
int CNGRecentItemComboBox::SetMaxMRUSize ( int nMaxSize )
{
int nRetVal = m_nMaxMRUSize;
// New size needs to be a positive
// number.
ASSERT ( nMaxSize >= 1 );
if ( nMaxSize <= 0 )
return -1;
m_nMaxMRUSize = nMaxSize;
if ( NULL == m_pMRU )
{
nRetVal = 0; // no previous size
}
if ( !AllocNewMRU() )
{
nRetVal = -1; // error!!
TRACE( _T("CNGRecentItemComboBox -- SetMaxMRUSize() failed - couldn't allocate new CRecentFileList.\n") );
}
return nRetVal;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: GetMaxMRUSize
//
// Description:
// Returns the current max MRU size.
//
// Input:
// Nothing.
//
// Returns:
// The current max size.
//
//////////////////////////////////////////////////////////////////////////
int CNGRecentItemComboBox::GetMaxMRUSize() const
{
return m_nMaxMRUSize;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: SetAutoSaveOnDestroy()
//
// Description:
// Sets whether the CNGRecentItemComboBox will automatically save the MRU when
// the object is destroyed.
//
// Input:
// bAutoSave: [in] Flag: enable auto-saving?
//
// Returns:
// The previous value of this setting.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::SetAutoSaveOnDestroy ( BOOL bAutoSave )
{
BOOL bRetVal = m_bSaveOnDestroy;
m_bSaveOnDestroy = bAutoSave;
return bRetVal;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: SetAutoSaveAfterAdd()
//
// Description:
// Sets whether the CNGRecentItemComboBox will automatically save the MRU after
// an item is added successfully.
//
// Input:
// bAutoSave: [in] Flag: enable auto-saving?
//
// Returns:
// The previous value of this setting.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::SetAutoSaveAfterAdd ( BOOL bAutoSave )
{
BOOL bRetVal = m_bSaveAfterAdd;
m_bSaveAfterAdd = bAutoSave;
return bRetVal;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: SetAutoRefreshAfterAdd()
//
// Description:
// Sets whether the CNGRecentItemComboBox will automatically refresh the combobox
// control after an item is added successfully.
//
// Input:
// bAutoSave: [in] Flag: enable auto-refresh?
//
// Returns:
// The previous value of this setting.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::SetAutoRefreshAfterAdd ( BOOL bAutoSave )
{
BOOL bRetVal = m_bRefreshAfterAdd;
m_bRefreshAfterAdd = bAutoSave;
return bRetVal;
}
/////////////////////////////////////////////////////////////////////////////
// CNGRecentItemComboBox misc. functions
//////////////////////////////////////////////////////////////////////////
//
// Function: VerifyMRUParams()
//
// Description:
// Checks the registry and size parameters and makes sure they're valid
// for CRecentFileList.
//
// Input:
// Nothing. (uses member variables)
//
// Returns:
// TRUE if the params are OK, FALSE if not.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::VerifyMRUParams() const
{
BOOL bRetVal = TRUE;
// 1. The registry key string must be
// non-empty.
if ( m_cstrRegKey.IsEmpty() || 0 == m_cstrRegKey.GetLength() )
{
TRACE( _T("CNGRecentItemComboBox -- VerifyMRUParams() - registry key name not set.\n") );
bRetVal = FALSE;
}
// 2. The reg value must be non-empty
// and contain "%d"
if ( m_cstrRegValueFormat.IsEmpty() ||
0 == m_cstrRegValueFormat.GetLength() )
{
TRACE( _T("CNGRecentItemComboBox -- VerifyMRUParams() - registry value format not set.\n") );
bRetVal = FALSE;
}
else if ( -1 == m_cstrRegValueFormat.Find ( _T("%d") ) )
{
TRACE( _T("CNGRecentItemComboBox -- VerifyMRUParams() - registry value format doesn't contain \"%d\"\n") );
bRetVal = FALSE;
}
// 3. The Max MRU size must be > 0.
if ( m_nMaxMRUSize <= 0 )
{
TRACE( _T("CNGRecentItemComboBox -- VerifyMRUParams() - max MRU size is set to <= 0\n") );
bRetVal = FALSE;
}
return bRetVal;
}
//////////////////////////////////////////////////////////////////////////
//
// Function: AllocNewMRU()
//
// Description:
// Allocates a new CRecentFileList, and copies the contents of the previous
// MRU (if any) to the new one.
//
// Input:
// Nothing.
//
// Returns:
// TRUE if successful, FALSE if not.
//
//////////////////////////////////////////////////////////////////////////
BOOL CNGRecentItemComboBox::AllocNewMRU()
{
CString* acstrOldList = NULL;
int nItemsToCopy;
int i;
// Make sure the MRU params are OK.
if ( !VerifyMRUParams() )
{
TRACE( _T("CNGRecentItemComboBox -- AllocNewMRU() returning FALSE - MRU list params invalid or not set.\n") );
return FALSE;
}
try
{
// Figuring out how many strings to
// copy: The lesser of the new MRU
// size and the previous MRU's size.
// Of course, if there was no previous
// MRU, then nothing will be copied.
nItemsToCopy = m_nMaxMRUSize;
if ( NULL != m_pMRU )
{
nItemsToCopy = __min ( m_nMaxMRUSize, m_pMRU->GetSize() );
// Save the contents of the old MRU list.
acstrOldList = new CString [ nItemsToCopy ];
for ( i = 0; i < nItemsToCopy; i++ )
{
acstrOldList[i] = (*m_pMRU)[i];
}
// Nuke the old CRecentItemList object...
delete m_pMRU;
}
// and make a new one!
m_pMRU = new CNGRecentItemList( 1,
m_cstrRegKey,
m_cstrRegValueFormat,
m_nMaxMRUSize );
// Copy the MRU strings if there was a previous MRU. We add
// the strings in reverse numerical order so they end up in the same
// order as they were in the old MRU.
if ( NULL != acstrOldList )
{
for ( i = nItemsToCopy - 1; i >= 0; i-- )
{
if (!acstrOldList[i].IsEmpty() )
{
m_pMRU->Add ( acstrOldList[i] );
}
}
delete [] acstrOldList;
}
}
catch (CMemoryException* e)
{
TRACE( _T("CNGRecentItemComboBox -- Memory exception in AllocNewMRU()!\n") );
e->Delete();
if ( NULL != m_pMRU )
{
delete m_pMRU;
m_pMRU = NULL;
}
throw;
}
// Reset the changed flag.
m_bParamsChanged = FALSE;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CNGRecentFileComboBox
CNGRecentFileComboBox::CNGRecentFileComboBox(void)
{
}
CNGRecentFileComboBox::~CNGRecentFileComboBox(void)
{
}
BEGIN_MESSAGE_MAP(CNGRecentFileComboBox, CNGRecentItemComboBox)
//{{AFX_MSG_MAP(CNGRecentFileComboBox)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNGRecentFileComboBox message handlers
void CNGRecentFileComboBox::PreSubclassWindow(void)
{
ASSERT( (GetStyle() & (CBS_OWNERDRAWFIXED | CBS_HASSTRINGS)) == (CBS_OWNERDRAWFIXED | CBS_HASSTRINGS) );
CNGRecentItemComboBox::PreSubclassWindow();
}
void CNGRecentFileComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
if (((LONG)(lpDrawItemStruct->itemID) >= 0) &&
(lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)))
{
BOOL bDisabled = !IsWindowEnabled ();
COLORREF newTextColor = bDisabled ?
RGB(0x80, 0x80, 0x80) : GetSysColor (COLOR_WINDOWTEXT); // light gray
COLORREF oldTextColor = pDC->SetTextColor (newTextColor);
COLORREF newBkColor = GetSysColor (COLOR_WINDOW);
COLORREF oldBkColor = pDC->SetBkColor (newBkColor);
if (newTextColor == newBkColor)
newTextColor = RGB(0xC0, 0xC0, 0xC0); // dark gray
if (!bDisabled && ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0))
{
pDC->SetTextColor (GetSysColor (COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor (GetSysColor (COLOR_HIGHLIGHT));
}
CString sText;
GetLBText(lpDrawItemStruct->itemID, sText);
const RECT &rc=lpDrawItemStruct->rcItem;
CString sCompactedPath = sText;
LPTSTR pszCompactedPath = sCompactedPath.GetBuffer(_MAX_PATH);
::PathCompactPath( pDC->GetSafeHdc(),
pszCompactedPath,
rc.right - rc.left);
sCompactedPath.ReleaseBuffer();
pDC->ExtTextOut(rc.left + 2,
rc.top + 2,// + max(0, (cyItem - m_cyText) / 2),
ETO_OPAQUE, &rc,
sCompactedPath, sCompactedPath.GetLength (), NULL);
pDC->SetTextColor (oldTextColor);
pDC->SetBkColor (oldBkColor);
}
else if ((LONG)(lpDrawItemStruct->itemID)<0) // drawing edit text
{
COLORREF newTextColor = GetSysColor (COLOR_WINDOWTEXT); // light gray
COLORREF oldTextColor = pDC->SetTextColor (newTextColor);
COLORREF newBkColor = IsWindowEnabled() ? ::GetSysColor(COLOR_WINDOW) : ::GetSysColor(COLOR_3DFACE);
COLORREF oldBkColor = pDC->SetBkColor (newBkColor);
if ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0)
{
pDC->SetTextColor (GetSysColor (COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor (GetSysColor (COLOR_HIGHLIGHT));
}
CString sText;
GetWindowText(sText);
if (!sText.IsEmpty() )
{
const RECT& rc = lpDrawItemStruct->rcItem;
CString sCompactedPath = sText;
LPTSTR pszCompactedPath = sCompactedPath.GetBuffer(_MAX_PATH);
::PathCompactPath( pDC->GetSafeHdc(),
pszCompactedPath,
rc.right - rc.left);
sCompactedPath.ReleaseBuffer();
pDC->ExtTextOut(rc.left + 2,
rc.top + 2,// + max(0, (cyItem - m_cyText) / 2),
ETO_OPAQUE, &rc,
sCompactedPath, sCompactedPath.GetLength (), NULL);
pDC->SetTextColor (oldTextColor);
pDC->SetBkColor (oldBkColor);
}
}
if ((lpDrawItemStruct->itemAction & ODA_FOCUS) != 0)
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
}
void CNGRecentFileComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
// TODO: Add your code to determine the size of specified item
UNREFERENCED_PARAMETER(lpMeasureItemStruct);
}