/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.

** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Jerzy Kaczorowski <kaczoroj@hotmail.com> --- December 2001
 */

// SmartComboBox.cpp : implementation file
//

#include "stdafx.h"
#include "SmartComboBox.h"

#ifdef WIN32
#	include "resource.h"
#endif /* WIN32 */

#include "MultiString.h"
#include "CvsAlert.h"

#ifdef WIN32

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#endif /* WIN32 */

/// Constructor
USmartCombo::USmartCombo(int feature)
	: m_feature(feature)
{
	m_pstrItems = NULL;
}

/// Destructor
USmartCombo::~USmartCombo()
{
}

/*! \brief Set the features.
 *	
 * Reflect the read-only state apropriate.
 */
void USmartCombo::SetFeature(int feature)
{
	m_feature = feature;
	SetReadOnly(0 != (m_feature & USmartCombo::ReadOnly));
}

/*! \brief Test if the feature is supported.
 *	
 * Return true if the feature is set, false otherwise
 */
bool USmartCombo::HasFeature(int feature) const
{
	return (m_feature & feature) != 0;
}

/*! \brief Set the items persistent collection.
 *	
 * Set the items persistent collection as a pointer to CMString class object so that it can be 
 * removed with Del key when the droplist is dropped down.
 * It will also set the feature member apropriate: if pstrItems is NULL it will remove 
 * and if it's valid then it will add the USmartCombo::RemoveItems feature.
 */
void USmartCombo::SetItems(CMString* pstrItems)
{
	m_pstrItems = pstrItems;
	if( m_pstrItems )
	{
		m_feature |= USmartCombo::RemoveItems;
	}
	else
	{
		m_feature &= ~USmartCombo::RemoveItems;
	}
}

/*! \brief Get the items persistent collection.
 *	
 * Get the items persistent collection as a pointer to CMString class object so that it can be 
 * used to populate combo box in DDX rutine etc.
 * \note the return can be NULL
 */
CMString* USmartCombo::GetItems()
{
	return m_pstrItems;
}


/*! \brief Calculate the width of the dropped items list to fit all items.
 *	
 * \note The default implementation does nothing, you should override that method
 * to perform the real calculation.
 */
int USmartCombo::CalcDroppedWidth()
{
	return 0;
}

/*! \brief Set the read-only feature.
 *	
 * \note Call the base class first when you override USmartCombo::SetReadOnly.
 */
void USmartCombo::SetReadOnly(bool readOnly /*= true*/)
{
	if( readOnly )
	{
		m_feature |= USmartCombo::ReadOnly;
	}
	else
	{
		m_feature &= ~USmartCombo::ReadOnly;
	}
}

/*! \brief Remove the item from the list.
 *	
 * Prompt the user to remove the item from the list and remove it.
 * \return true if item remove, false otherwise.
 */
bool USmartCombo::RemoveItem(const char* strItem)
{
	UStr prompt("Do you want to remove that item from the list?\n");
	prompt << "Item: ";
	prompt << strItem;

	bool res = CvsAlert(prompt, "Yes", "No");
	if( res )
	{
		m_pstrItems->Remove(strItem);
	}

	return res;
}

#ifdef WIN32
/////////////////////////////////////////////////////////////////////////////
// CSmartComboBox

CSmartComboBox::CSmartComboBox(int feature /*= USmartCombo::DefaultFeature*/)
	: USmartCombo(feature)
{
}

CSmartComboBox::~CSmartComboBox()
{
}


BEGIN_MESSAGE_MAP(CSmartComboBox, CComboBox)
	//{{AFX_MSG_MAP(CSmartComboBox)
	ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropdown)
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_SMCB_SETFEATURE, OnSetFeature)
	ON_MESSAGE(WM_SMCB_SETITEMS, OnSetItems)
	ON_MESSAGE(WM_SMCB_SETREADONLY, OnSetReadOnly)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSmartComboBox message handlers

/*! \brief CBN_DROPDOWN notification message handler.
 * 
 * Calculates the width of the droplist if the USmartCombo::AutoDropWidth feature is set.
 */
void CSmartComboBox::OnDropdown() 
{
	if( HasFeature(USmartCombo::AutoDropWidth) )
	{
		SetDroppedWidth(CalcDroppedWidth());
	}
}

/*! \brief PreTranslateMessage virtual override.
 * 
 * If the USmartCombo::RemoveItems feature is set perform the key test to determine whether to delete item.
 */
BOOL CSmartComboBox::PreTranslateMessage(MSG* pMsg) 
{
	if( HasFeature(USmartCombo::RemoveItems) )
	{
		if( DelKeyTest(pMsg) )
		{
			return TRUE;
		}
	}

	return CComboBox::PreTranslateMessage(pMsg);
}

/*! \brief Calculate droplist width.
 * 
 * Calculate the proper width for droplist so the items can fit into it. 
 * Make sure it doesn't go off-screen as well.
 */
int CSmartComboBox::CalcDroppedWidth()
{
	int maxSize = 0;

	CClientDC ClientDC(this);
	int savedDC = ClientDC.SaveDC();
    ClientDC.SelectObject(GetFont());
	
	CString strLBText;
	for( int nIndex = 0; nIndex < GetCount(); nIndex++ )
	{
		GetLBText( nIndex, strLBText );
		CSize size = ClientDC.GetTextExtent(strLBText);
		
		if( size.cx > maxSize )
		{
			maxSize = size.cx;
		}
	}
	ClientDC.RestoreDC(savedDC);

	maxSize += ::GetSystemMetrics(SM_CXVSCROLL)	+ 3*::GetSystemMetrics(SM_CXEDGE);
	
	CRect rect;
	GetWindowRect(rect);
	maxSize = min(ClientDC.GetDeviceCaps(HORZRES) - rect.TopLeft().x, maxSize);

	return maxSize;
}

/*! \brief Test if the Del key is pressed and remove the item if the droplist is dropped down.
 *	
 * Analizes the message and removes the item if the Del key is pressed while droplist is dropped down.
 * \attention Make sure the items collection is set earlier (call USmartCombo::SetItems to set the collection).
 * \return TRUE if the droplist was dropped and Del key was pressed.
 */
BOOL CSmartComboBox::DelKeyTest(MSG* pMsg)
{
	CMString* pstrItems = GetItems();
	ASSERT(pstrItems != NULL);

	BOOL bRes = FALSE;

	if( WM_KEYDOWN == pMsg->message && 
		VK_DELETE == (int)pMsg->wParam && 
		pstrItems && 
		GetDroppedState() )
	{
		int sel = GetCurSel();
		if( CB_ERR < sel )
		{
			CString strItem;
			GetLBText(sel, strItem);

			if( RemoveItem(strItem) )
			{
				DeleteString(sel);
			}
			
			//we have to prevent it going to another window
			bRes = TRUE;
		}
	}

	return bRes;
}

/*! \brief PreSubclassWindow virtual override.
 * 
 * Sets the read-only state if the USmartCombo::ReadOnly feature is set.
 */
void CSmartComboBox::PreSubclassWindow() 
{
	if( HasFeature(USmartCombo::ReadOnly) )
	{
		SetReadOnly();
	}
	
	CComboBox::PreSubclassWindow();
}

/*! \brief Set the read-only state.
 * 
 * Set the read-only state of the edit part of combo box.
 * \note It can be used at runtime, but it's better to set read-only
 * using constructor (CSmartComboBox::CSmartComboBox)
 */
void CSmartComboBox::SetReadOnly(bool readOnly /*= true*/)
{
	USmartCombo::SetReadOnly(readOnly);

	if( IsWindow(m_hWnd) )
	{
		CEdit* pEdit = (CEdit*)GetDlgItem(1001);
		if( pEdit && IsWindow(pEdit->m_hWnd) )
		{
			pEdit->SetReadOnly(readOnly);
		}
	}
}

/*! \brief WM_SMCB_SETFEATURE private message handler.
 * 
 * \arg \c wParam value or a combinations of values enumerated under USmartCombo::Feature
 * \arg \c lParam unused
 */
LRESULT CSmartComboBox::OnSetFeature(WPARAM wParam, LPARAM lParam)
{
	SetFeature((int)wParam);
	return 0;
}

/*! \brief WM_SMCB_SETITEMS private message handler.
 * 
 * \arg \c wParam pointer to persistent collection of items
 * \arg \c lParam unused
 */
LRESULT CSmartComboBox::OnSetItems(WPARAM wParam, LPARAM lParam)
{
	SetItems((CMString*)wParam);
	return 0;
}

/*! \brief WM_SMCB_SETREADONLY private message handler.
 * 
 * \arg \c wParam boolean value to set the read-only state
 * \arg \c lParam unused
 */
LRESULT CSmartComboBox::OnSetReadOnly(WPARAM wParam, LPARAM lParam)
{
	SetReadOnly(0 != wParam);
	return 0;
}

#endif /* WIN32 */
