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

1103 lines
32 KiB
C++

// ==========================================================================
// Class Implementation : COXMultiComboBox
// ==========================================================================
// Source file : OXMultiComboBox.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 "OXMultiComboBox.h"
#include "UTBStrOp.h"
#include "UTB64Bit.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNAMIC(COXMultiComboBox, CComboBox)
BEGIN_MESSAGE_MAP(COXMultiComboBox, CComboBox)
//{{AFX_MSG_MAP(COXMultiComboBox)
ON_WM_CTLCOLOR()
ON_WM_DELETEITEM()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_OX_LISTHSCROLL,OnListHScroll)
ON_MESSAGE(CB_GETITEMDATA,OnGetItemData)
ON_MESSAGE(CB_SETITEMDATA,OnSetItemData)
ON_MESSAGE(WM_COMPAREITEM,OnCompareItem)
ON_MESSAGE(WM_MEASUREITEM,OnMeasureItem)
ON_MESSAGE(WM_DRAWITEM,OnDrawItem)
ON_MESSAGE(CB_INSERTSTRING,OnInsertString)
ON_MESSAGE(CB_ADDSTRING,OnAddString)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Definition of static members
// Data members-------------------------------------------------------------
//Protected
// int m_nColumnCount;
// --- Numbers of columns
// int m_nMasterColumn
// --- Master column number (zero based index)
// CArray<int,int> m_aColumnWidth;
// --- Array used to store the width of each column
// enum {DEFAULT_COLUMN_WIDTH=40};
// --- Default column width
// CPen m_GridPen
// --- Pen to Draw the grid
// class COXComboLBox : public CWnd
// --- This CWnd class is used to subclass the listbox hadle of the combobox
// to handle its WM_HSCROLL message, which will be send to combobox.
// COXComboLBox m_ComboLBox;
// --- This Window object is used to subclass the window handle of listbox of
// the combobox.
// class COXRowData
// --- This private class is used to store the each items column strings and
// its data. All the members in this are self explainatory
//Private
// BOOL m_fSizeChanged
// --- This will be true when a column width is set and accordingly
// the scrollbasr are not adjusted.
// BOOL m_fMasterColumnChanging
// --- Flag used to avoid from deleting the itemdata when master column is
// changing.
// int m_nPageWidth;
// --- Amount of Width to scroll on SB_PAGELEFT and SB_PAGERIGHT
// int m_nLineWidth;
// --- Line width to scroll, on SB_LINELEFT and SB_LINERIGHT
// Member functions ---------------------------------------------------------
// public:
COXMultiComboBox::COXMultiComboBox()
{
// ... By default
m_nColumnCount = 1;
// ... By default
m_nMasterColumn = 0;
m_aColumnWidth.SetSize(1);
m_aColumnWidth[0] = DEFAULT_COLUMN_WIDTH;
m_fSizeChanged = TRUE;
m_ComboLBox.Init(this);
m_fMasterColumnChanging = FALSE;
m_bFitToSize=FALSE;
// Create pen with alternating dot - space
// (PS_DOT does not give a good result, so we use
// PS_COSMETIC | PS_ALTERNATE)
LOGBRUSH logBrush;
logBrush.lbColor = RGB(0,0,0);
logBrush.lbStyle = BS_SOLID;
logBrush.lbHatch = HS_HORIZONTAL;
if (!m_GridPen.CreatePen(PS_COSMETIC | PS_ALTERNATE, 1, &logBrush, 0,NULL))
if (!m_GridPen.CreatePen(PS_COSMETIC, 1, &logBrush, 0, NULL))
TRACE0("COXMultiComboBox::COXMultiComboBox : Error creating the grid lines Pen\n");
}
BOOL COXMultiComboBox::SetColumnCount(int nCount)
{
if(nCount == m_nColumnCount)
return TRUE;
// ... nCount should be more than or equal to Master column and -1
if ((nCount <= m_nMasterColumn) || (nCount < 0))
return FALSE;
m_aColumnWidth.SetSize(nCount);
// If size grows initailize the new columns width
for(int nColIndex = m_nColumnCount; nColIndex < nCount; nColIndex++)
m_aColumnWidth[nColIndex] = DEFAULT_COLUMN_WIDTH;
// Resizes the number of columns in each rowdata object
int nCurCount = GetCount();
for(int nIndex = 0; nIndex < nCurCount; nIndex++)
{
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
pRowData->SetColumnCount(nCount);
else
TRACE0("In COXMultiComboBox::SetColumnCount : GetRowData() returned NULL.\n");
}
m_nColumnCount = nCount;
AdjustToFitSize();
return TRUE;
}
BOOL COXMultiComboBox::SetMasterColumn(int nCol)
{
if(nCol == m_nMasterColumn)
return TRUE;
// Master Column index should be in between 0 and m_nColumnCount-1
if ((m_nColumnCount <= nCol) || (nCol < 0))
return FALSE;
int nPrevMasterColumn = m_nMasterColumn;
CString sPrevMasterColumnString;
int nCurSel = GetCurSel();
if(nCurSel!=-1)
GetLBText(GetCurSel(),sPrevMasterColumnString);
m_nMasterColumn = nCol;
// Resorts based on newly set master column
if(!ChangeMasterColumn(nCol))
{
m_nMasterColumn = nPrevMasterColumn;
return FALSE;
}
// Selects the string in newly set master column into editbox of combo
if(nCurSel!=-1)
if((SelectString(-1,nPrevMasterColumn,sPrevMasterColumnString.GetBuffer(0)))==CB_ERR)
return FALSE;
return TRUE;
}
BOOL COXMultiComboBox::SetColumnWidth(int nColIndex, int nWidth)
{
if(nColIndex >= m_nColumnCount || nColIndex < -1)
return FALSE;
if(nColIndex == -1)
{
// Set the width of all the columns
for(nColIndex =0; nColIndex<m_nColumnCount; nColIndex++)
m_aColumnWidth[nColIndex] = nWidth;
}
else
m_aColumnWidth[nColIndex] = nWidth;
AdjustToFitSize();
return TRUE;
}
int COXMultiComboBox::GetLBText(int nIndex,int nColIndex, LPTSTR lpszText) const
{
if(nIndex >= GetCount() || nColIndex >= m_nColumnCount || nColIndex < 0 || nIndex < 0)
return CB_ERR;
// ... copies the string into buffer
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
{
// ... returns the length
UTBStr::tcscpy(lpszText, pRowData->GetColumnString(nColIndex).GetLength()+1, pRowData->GetColumnString(nColIndex).GetBuffer(0));
return pRowData->GetColumnString(nColIndex).GetLength();
}
else
{
TRACE0("In COXMultiComboBox::GetLBText : GetRowData() returned NULL.\n");
return CB_ERR;
}
}
void COXMultiComboBox::GetLBText(int nIndex, int nColIndex, CString& rString) const
{
if(nIndex < GetCount() && nColIndex < m_nColumnCount && nIndex >= 0 && nColIndex >=0 )
{
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
{
rString = pRowData->GetColumnString(nColIndex);
return;
}
else
TRACE0("In COXMultiComboBox::GetLBText : GetRowData() returned NULL.\n");
}
rString.Empty();
}
int COXMultiComboBox::GetLBTextLen(int nIndex,int nColIndex) const
{
if(nIndex >= GetCount() || nColIndex >= m_nColumnCount || nColIndex < 0 || nIndex < 0)
return CB_ERR;
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
return pRowData->GetColumnString(nColIndex).GetLength();
else
{
TRACE0("In COXMultiComboBox::GetLBTextLen : GetRowData() returned NULL.\n");
return CB_ERR;
}
}
int COXMultiComboBox::FindStringExact(int nIndexStart, int nColIndex, LPCTSTR lpszFind,
BOOL bCaseSensitive/*=FALSE*/) const
{
if(nColIndex >= m_nColumnCount || nColIndex < 0)
return CB_ERR;
if(nIndexStart <= -1)
nIndexStart = 0;
int nItemCount = GetCount();
for(int nIndex = nIndexStart; nIndex < nItemCount; nIndex++)
{
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
{
if(bCaseSensitive)
{
if(pRowData->GetColumnString(nColIndex) == lpszFind)
return nIndex;
}
else
{
if(pRowData->GetColumnString(nColIndex).CompareNoCase(lpszFind) == 0)
return nIndex;
}
}
else
TRACE0("In COXMultiComboBox::FindStringExact : GetRowData() returned NULL.\n");
}
return CB_ERR;
}
int COXMultiComboBox::FindString(int nStartAfter, int nColIndex,
LPCTSTR lpszString,
BOOL bCaseSensitive/*=FALSE*/) const
{
if(nColIndex >= m_nColumnCount || nColIndex < 0)
return CB_ERR;
if(nStartAfter < -1)
nStartAfter = -1;
int nItemCount = GetCount();
int nStrLen = (int)_tcslen(lpszString);
if(nStrLen==0)
{
TRACE(_T("COXMultiComboBox::FindString : zero-length string was specified\n"));
return CB_ERR;
}
for(int nIndex = nStartAfter+1; nIndex < nItemCount; nIndex++)
{
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
{
if(bCaseSensitive)
{
if(pRowData->GetColumnString(nColIndex).Left(nStrLen) == lpszString)
return nIndex;
}
else
{
if(pRowData->GetColumnString(nColIndex).Left(nStrLen).
CompareNoCase(lpszString) == 0)
return nIndex;
}
}
else
TRACE0("In COXMultiComboBox::FindString : GetRowData() returned NULL.\n");
}
return CB_ERR;
}
int COXMultiComboBox::InsertString(int nIndex, LPCTSTR* lpszString, int nNumStrings)
{
int nRetVal;
// return CB_ERR if invalid number of strings
if(nNumStrings > m_nColumnCount || nNumStrings < 1)
return CB_ERR;
// Actually Inserts the item with master column text
if((nRetVal = CComboBox::InsertString(nIndex,lpszString[m_nMasterColumn])) != CB_ERR)
{
COXRowData* pRowData = new COXRowData(m_nColumnCount);
// Initialize the column strings
for(int nColIndex=0; nColIndex < nNumStrings; nColIndex++)
pRowData->SetColumnString(nColIndex,lpszString[nColIndex]);
SetRowData(nRetVal,pRowData);
AdjustToFitSize();
}
return nRetVal;
}
int COXMultiComboBox::AddString(LPCTSTR* lpszString, int nNumStrings)
{
int nRetVal;
// return CB_ERR if invalid number of strings
if(nNumStrings > m_nColumnCount || nNumStrings < 1)
return CB_ERR;
// Adds the item with master column text
if((nRetVal = CComboBox::AddString(lpszString[m_nMasterColumn])) != CB_ERR)
{
COXRowData* pRowData = new COXRowData(m_nColumnCount);
// Initialize the column strings
for(int nColIndex=0; nColIndex < nNumStrings; nColIndex++)
pRowData->SetColumnString(nColIndex,lpszString[nColIndex]);
SetRowData(nRetVal,pRowData);
AdjustToFitSize();
}
return nRetVal;
}
int COXMultiComboBox::SelectString(int nStartAfter, int nColIndex, LPCTSTR lpszString)
{
if(nColIndex >= m_nColumnCount || nColIndex < 0)
return CB_ERR;
int nIndex = FindStringExact(nStartAfter,nColIndex,lpszString);
if(nIndex != CB_ERR)
{
COXRowData* pRowData = GetRowData(nIndex);
if (pRowData != NULL)
nIndex = CComboBox::SelectString(nStartAfter,pRowData->GetColumnString(m_nMasterColumn));
else
{
TRACE0("In COXMultiComboBox::SelectString : GetRowData() returned NULL.\n");
nIndex = CB_ERR;
}
}
return nIndex;
}
int COXMultiComboBox::AddString(LPCTSTR lpszString)
{
int nRetVal;
if((nRetVal = CComboBox::AddString(lpszString)) != CB_ERR)
{
COXRowData* pRowData = new COXRowData(m_nColumnCount);
pRowData->SetColumnString(m_nMasterColumn,lpszString);
SetRowData(nRetVal,pRowData);
AdjustToFitSize();
}
return nRetVal;
}
int COXMultiComboBox::InsertString(int nIndex, LPCTSTR lpszString)
{
int nRetVal;
if((nRetVal = CComboBox::InsertString(nIndex,lpszString)) != CB_ERR)
{
COXRowData* pRowData = new COXRowData(m_nColumnCount);
pRowData->SetColumnString(m_nMasterColumn,lpszString);
SetRowData(nRetVal,pRowData);
AdjustToFitSize();
}
return nRetVal;
}
//Protected
// This is handled to divide the drawing into individual columns.
// Advised to handle 'DrawColumnItem' than handling this itself in derived
// classes unless you need to customize the lists more than just dividing
// into column drawings
void COXMultiComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
// effect : Number of calls to DrawColumnItems are made
{
ASSERT(lpDrawItemStruct->CtlType==ODT_COMBOBOX);
// In case of drawing the static Text area of dropdown-list combobox
if(lpDrawItemStruct->itemState & ODS_COMBOBOXEDIT)
{
DrawColumnItem(lpDrawItemStruct->hDC,GetCurSel(),m_nMasterColumn,lpDrawItemStruct->rcItem,lpDrawItemStruct->itemState);
return;
}
if(lpDrawItemStruct->itemID==-1 && GetCount() == 0)
{
DrawFocusRect(lpDrawItemStruct->hDC,&(lpDrawItemStruct->rcItem));
return;
}
// Draw columns one by one until last but one
CRect rectColumn = lpDrawItemStruct->rcItem;
int nColIndex=0;
for(nColIndex=0; nColIndex< m_nColumnCount-1; nColIndex++)
{
rectColumn.right = rectColumn.left + m_aColumnWidth[nColIndex]-1;
DrawColumnItem(lpDrawItemStruct->hDC,lpDrawItemStruct->itemID,
nColIndex,rectColumn,lpDrawItemStruct->itemState);
rectColumn.left = rectColumn.right+1;
}
// draws last column
rectColumn.right = __max(lpDrawItemStruct->rcItem.right,
rectColumn.left + m_aColumnWidth[nColIndex]-1);
DrawColumnItem(lpDrawItemStruct->hDC,lpDrawItemStruct->itemID,
nColIndex,rectColumn,lpDrawItemStruct->itemState);
}
//This virtual function is called to draw the individual columns. In the case of
// non-CBS_HASSTRINGS this has to be implimented in derived classes
void COXMultiComboBox::DrawColumnItem(HDC hDC, int nIndex, int nColIndex, const CRect& rectColumn, int ItemState)
// --- In : hDC : the DC into which to draw the column
// nIndex : The row index
// nColIndex : The Column Index
// rectColumn : The rectangle into which we can draw
// ItemState : Gives the state of the item(Selected or not)
// --- Out :
// --- Returns :
// --- Effect : The column text is drawn in the case of CBS_HASSTRINGS
{
ASSERT(GetStyle()&CBS_HASSTRINGS);
CDC dc;
dc.Attach(hDC);
// Sets the foreground color of the text based on its selection state
COLORREF clrOldForeground = dc.SetTextColor(::GetSysColor(IsWindowEnabled() ?
(ItemState&ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT) :
COLOR_GRAYTEXT));
COLORREF clrBackground = ::GetSysColor(IsWindowEnabled() ? (ItemState & ODS_SELECTED ?
COLOR_HIGHLIGHT : COLOR_WINDOW) : COLOR_BTNFACE);
// Sets the background color of text based on selection state
COLORREF clrOldBackground = dc.SetBkColor( clrBackground);
// Gets the column text to draw
CString strText;
if(nIndex!=-1)
GetLBText(nIndex,nColIndex,strText);
else
strText.Empty();
CRect cellRect(rectColumn);
dc.FillSolidRect(cellRect, clrBackground);
// give left margin
cellRect.left += LOWORD(GetDialogBaseUnits()) / 2;
// Draw text
dc.DrawText(strText, cellRect, DT_NOPREFIX | DT_SINGLELINE |
DT_VCENTER | DT_END_ELLIPSIS);
// Seperators are drawn in list boxe between columns and rows
DrawColumnBorder(&dc,rectColumn,ItemState);
// Reset the actual DC colors
dc.SetTextColor(clrOldForeground);
dc.SetBkColor(clrOldBackground);
dc.Detach();
}
//override this function to draw you own border
void COXMultiComboBox::DrawColumnBorder(CDC* pDC,const CRect& rectColumn,int ItemState)
// --- In : pDC : the pointer to the DC into which to draw the column border
// rectColumn : The rectangle into which we can draw
// ItemState : Gives the state of the item(Selected or not)
// --- Out :
// --- Returns :
// --- Effect : The column borderis drawn like ' _______| '
{
if(!(ItemState & ODS_COMBOBOXEDIT))
{
CPen* pOldPen = pDC->SelectObject(&m_GridPen);
CPoint ptOld = pDC->MoveTo(rectColumn.right,rectColumn.top);
pDC->LineTo(rectColumn.right,rectColumn.bottom-1);
pDC->LineTo(rectColumn.left,rectColumn.bottom-1);
pDC->MoveTo(ptOld);
pDC->SelectObject(pOldPen);
}
}
void COXMultiComboBox::MeasureItem(LPMEASUREITEMSTRUCT /* lpMeasureItemStruct */)
{
// In case of CBS_OWNERDRAWNVARIABLE,to be handle in derived classes
// to get the size of items
// The documentation of CComboBox::MeasureItem() states
// "If the combo box is created with the CBS_OWNERDRAWVARIABLE style,
// the framework calls this member function for each item in the list box.
// Otherwise, this member is called only once."
// So even with CBS_OWNERDRAWFIXED this function is called once.
// In this case we do nothing
return;
}
int COXMultiComboBox::CompareItem(LPCOMPAREITEMSTRUCT /* lpCompareItemStruct */)
{
// In case of non-CBS_HASSTRINGS, to be handle in derived classes
// to compare the items
ASSERT(FALSE);
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// COXMultiComboBox message handlers
//This is handle to delete rowdata.
void COXMultiComboBox::OnDeleteItem(int nIDCtl, LPDELETEITEMSTRUCT lpDeleteItemStruct)
{
if(!m_fMasterColumnChanging && lpDeleteItemStruct->CtlType == ODT_LISTBOX)
{
COXRowData* pRowData = GetRowData(lpDeleteItemStruct->itemID);
if (pRowData != NULL)
{
pRowData->SetColumnCount(0);
delete pRowData;
}
else
TRACE0("In COXMultiComboBox::OnDeleteItem : GetRowData() returned NULL.\n");
}
CComboBox::OnDeleteItem(nIDCtl, lpDeleteItemStruct);
}
// This message is handle to get the HWND of the listbox of the combobox
// as there is no otherway to get it(refer to Q65881, Q131845 in Knowledge Base)
// The listbox handle is needed to set the scrollbar to it if needed, and to
// handle its WM_HSCROLL message.
HBRUSH COXMultiComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
// --- In : pDC : device context (See standard help for detaled info)
// pWnd : Pointer Cwnd obejct of the window whose background
// has to be painted.
// nCtlColor : Type of the control
// --- Out :
// --- Returns : Brush to supply to paint the background
// --- Effect : The listbox's HWND is got and sunclassed to COXComboLBox
// window object. The listbox style is modified to
// have WS_HSCROLL. The horizontal extent of the listbox is
// set and scrollbars are shown are hided depending on the
// width and height. Scroll range for horizantal scrollbar
// is set and also m_NumCharsPerPage is set.
{
HBRUSH hbr = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
// Handle only if the type of control is Listbox only
if ((nCtlColor == CTLCOLOR_LISTBOX) && m_fSizeChanged)
{
// ... To stop this code run unneccessorily
m_fSizeChanged = FALSE;
if(m_ComboLBox.GetSafeHwnd()==NULL)
{
m_ComboLBox.SubclassWindow(pWnd->GetSafeHwnd());
m_ComboLBox.ModifyStyle(0,WS_HSCROLL);
TEXTMETRIC tm;
GetTextMetrics(pDC->m_hDC,&tm);
m_nLineWidth = tm.tmAveCharWidth;
}
int nTotalWidth = GetTotalWidth();
// ... Sets horizontal extent to total width
m_ComboLBox.SetHorizontalExtent(nTotalWidth);
CRect rcDrop;
GetDroppedControlRect(&rcDrop);
int iTotalItemHeight = 0;
for (int i = 0; i < m_ComboLBox.GetCount(); i++)
iTotalItemHeight += m_ComboLBox.GetItemHeight(i);
if (rcDrop.Height() < iTotalItemHeight || m_bFitToSize)
m_ComboLBox.AdjustHScroll();
if(GetStyle()&CBS_DROPDOWN)
{
CRect rcWnd;
m_ComboLBox.GetWindowRect(&rcWnd);
m_ComboLBox.MoveWindow(&rcWnd,TRUE);
}
if(m_ComboLBox.m_fHorzScrollVisible)
{
CRect rcClient;
m_ComboLBox.GetClientRect(&rcClient);
m_nPageWidth = rcClient.Width();
// setting sroll info
SCROLLINFO si;
si.cbSize = sizeof(si);
si.nPos = 0;
si.nMin = 0;
si.nMax = nTotalWidth;
si.nPage = m_nPageWidth;
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS ;
m_ComboLBox.SetScrollInfo(SB_HORZ,&si,TRUE);
m_ComboLBox.SendMessage(WM_HSCROLL,SB_TOP,0L);
}
m_ComboLBox.RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_FRAME);
}
return hbr;
}
BEGIN_MESSAGE_MAP(COXMultiComboBox::COXComboLBox, CListBox)
//{{AFX_MSG_MAP(COXMultiComboBox::COXComboLBox)
ON_MESSAGE(WM_HSCROLL,COXMultiComboBox::COXComboLBox::OnHScroll)
ON_MESSAGE(WM_WINDOWPOSCHANGING,COXMultiComboBox::COXComboLBox::OnWindowPosChanging)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COXComboLBox message handlers
LRESULT COXMultiComboBox::COXComboLBox::OnHScroll(WPARAM wParam, LPARAM lParam)
// --- In : wParam : Hiword contains scroll position and loward scroll code.
// lParam : the handle of scrollbar control
// --- Out :
// --- Returns : Has to return 0 to stop from processing further
// --- Effect : A WM_OX_LISTHSCROLL message is sent to Combobox Window
{
//Sends a message to combo box to notify it to change scroll position
//If the message is not processes in Combo it will be sent to window
//proc of listbox
if(!m_pWndCombo->SendMessage(WM_OX_LISTHSCROLL,wParam,lParam))
return DefWindowProc(WM_HSCROLL,wParam,lParam);
else
// ... to stop further processing
return 0l;
}
LRESULT COXMultiComboBox::OnListHScroll(WPARAM wParam,LPARAM /* lParam */)
// --- In : wParam : Hiword contains scroll position and loward scroll code.
// lParam : the handle of scrollbar control
// --- Out :
// --- Returns : Has to return 0 to stop from processing further
// --- Effect : The scrollbar thumb positions are set
{
int nSBCode = (int) LOWORD(wParam);
int nPos = (short int) HIWORD(wParam);
CRect Rect;
switch(nSBCode)
{
case SB_LINELEFT :
// Decrease the position by one char
m_ComboLBox.SetScrollPos(SB_HORZ,m_ComboLBox.GetScrollPos(SB_HORZ)-m_nLineWidth,TRUE);
return 0l;
case SB_LINERIGHT :
// Increase the position by one char
m_ComboLBox.SetScrollPos(SB_HORZ,m_ComboLBox.GetScrollPos(SB_HORZ)+m_nLineWidth,TRUE);
return 0l;
case SB_PAGELEFT :
// Decrease the position by 2/3rds of Number of chasr per page
m_ComboLBox.SetScrollPos(SB_HORZ,m_ComboLBox.GetScrollPos(SB_HORZ)-((2*m_nPageWidth)/3),TRUE);
return 0l;
case SB_PAGERIGHT :
// Increase the position by 2/3rds of Number of chasr per page
m_ComboLBox.SetScrollPos(SB_HORZ,m_ComboLBox.GetScrollPos(SB_HORZ)+((2*m_nPageWidth)/3),TRUE);
return 0l;
case SB_THUMBTRACK :
m_ComboLBox.SetScrollPos(SB_HORZ,nPos,TRUE);
return 0l;
}
return 0l;
}
//this message to handle to control the height of the window depending on
//the visibility of the horizontal scrollbar when ever it is shown
//Horz Scrollbar Visibility is decided by the difference between
//the WindowRect and ClentRect
LRESULT COXMultiComboBox::COXComboLBox::OnWindowPosChanging(WPARAM wParam,
LPARAM lParam)
{
LPWINDOWPOS lpWP = (LPWINDOWPOS) lParam;
if( (!(lpWP->flags & SWP_NOSIZE)))
{
int nTotalHeight = ((COXMultiComboBox*)m_pWndCombo)->GetTotalHeight();
int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
int nScrollbarHeight = GetSystemMetrics(SM_CYHSCROLL);
CRect rcDrop;
((COXMultiComboBox*)m_pWndCombo)->GetDroppedControlRect(&rcDrop);
//Check and see if listbox appears
//in a random position and if so, fix it - Nish, Feb 24, 2005
int orig_cy = lpWP->cy;
if(m_fHorzScrollVisible)
{
if(nTotalHeight+nScrollbarHeight+2*nBorderHeight<=rcDrop.Height())
lpWP->cy = nTotalHeight+nScrollbarHeight + 2*nBorderHeight;
else
lpWP->cy = GetMaxHeight();
}
else
{
if(nTotalHeight + 2*nBorderHeight <= rcDrop.Height())
lpWP->cy = nTotalHeight + 2*nBorderHeight;
else
lpWP->cy = GetMaxHeight();
}
//Fixing listbox position - Nish
if(orig_cy != lpWP->cy)
{
CRect rCbEdit;
m_pWndCombo->GetWindowRect(&rCbEdit);
lpWP->y = lpWP->y + (orig_cy - lpWP->cy);
if(lpWP->y > rCbEdit.bottom)
{
lpWP->y = rCbEdit.bottom;
}
else {
if(lpWP->y < rCbEdit.bottom) {
lpWP->y = rCbEdit.bottom;
}
}
}
}
// check if the right side of the combo list box is out of screen coordinates
int nScreenWidth=::GetSystemMetrics(SM_CXSCREEN);
if(lpWP->x+lpWP->cx>nScreenWidth)
{
lpWP->x=nScreenWidth-lpWP->cx;
lpWP->x=lpWP->x>0 ? lpWP->x : 0;
// remove SWP_NOMOVE flag
lpWP->flags&=~SWP_NOMOVE;
}
return DefWindowProc(WM_WINDOWPOSCHANGING,wParam,lParam);
}
//To on and off the scrollbars of the listbox
void COXMultiComboBox::COXComboLBox::AdjustHScroll()
{
int nTotalHeight = ((COXMultiComboBox*)m_pWndCombo)->GetTotalHeight();
int nTotalWidth = ((COXMultiComboBox*)m_pWndCombo)->GetTotalWidth();
int nBorderWidth = GetSystemMetrics(SM_CXBORDER);
int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
int nScrollbarWidth = GetSystemMetrics(SM_CXVSCROLL);
int nScrollbarHeight = GetSystemMetrics(SM_CYHSCROLL);
CRect rcDrop;
((COXMultiComboBox*)m_pWndCombo)->GetDroppedControlRect(&rcDrop);
if( (nTotalHeight > rcDrop.Height()-2*nBorderHeight) ||
((nTotalWidth > rcDrop.Width() - 2*nBorderWidth) &&
(nTotalHeight > rcDrop.Height()-nScrollbarHeight - 2*nBorderHeight)) )
m_fVertScrollVisible = TRUE;
else
m_fVertScrollVisible = FALSE;
int nMargin=nTotalWidth-rcDrop.Width()+2*nBorderWidth;
if(m_fVertScrollVisible)
nMargin+=nScrollbarWidth;
if( nMargin>0 )
m_fHorzScrollVisible = TRUE;
else
m_fHorzScrollVisible = FALSE;
if(m_fHorzScrollVisible)
ShowScrollBar(SB_BOTH,TRUE);
else
ShowScrollBar(SB_HORZ,FALSE);
ShowScrollBar(SB_VERT,m_fVertScrollVisible);
}
// The following five message handlers are defined to replace the Itemdata which we are using
// to store the COXRowData with the actual item data the user set
LRESULT COXMultiComboBox::OnGetItemData(WPARAM wParam,LPARAM lParam)
{
LRESULT result = DefWindowProc(CB_GETITEMDATA, wParam, lParam);
// Replace the value by the correct one (own data structure)
if (result != CB_ERR)
{
COXRowData* pRowData = (COXRowData*)result;
ASSERT(AfxIsValidAddress(pRowData, sizeof(COXRowData)));
result = pRowData->GetItemData();
}
return result;
}
LRESULT COXMultiComboBox::OnSetItemData(WPARAM wParam,LPARAM lParam)
{
LRESULT result = DefWindowProc(CB_GETITEMDATA, wParam, lParam);
// Replace the value by the correct one (own data structure)
if (result != CB_ERR)
{
COXRowData* pRowData = (COXRowData*)result;
ASSERT(AfxIsValidAddress(pRowData, sizeof(COXRowData)));
pRowData->SetItemData((DWORD)lParam);
}
return result;
}
LRESULT COXMultiComboBox::OnCompareItem(WPARAM wParam,LPARAM lParam)
{
LPCOMPAREITEMSTRUCT pCompareItemStruct = (LPCOMPAREITEMSTRUCT)lParam;
COXRowData* pRowData;
if(pCompareItemStruct->itemID1!=-1)
{
pRowData = (COXRowData*)pCompareItemStruct->itemData1;
ASSERT(AfxIsValidAddress(pRowData, sizeof(COXRowData)));
pCompareItemStruct->itemData1 = pRowData->GetItemData();
}
if(pCompareItemStruct->itemID2!=-1)
{
pRowData = (COXRowData*)pCompareItemStruct->itemData2;
ASSERT(AfxIsValidAddress(pRowData, sizeof(COXRowData)));
pCompareItemStruct->itemData2 = pRowData->GetItemData();
}
return DefWindowProc(WM_COMPAREITEM,wParam,lParam);
}
LRESULT COXMultiComboBox::OnMeasureItem(WPARAM wParam,LPARAM lParam)
{
LPMEASUREITEMSTRUCT pMeasureItemStruct = (LPMEASUREITEMSTRUCT)lParam;
// ... Ignore if CBS_OWNERDRAWFIXED
if( (pMeasureItemStruct->itemID!=-1) &&
((GetStyle() & CBS_OWNERDRAWFIXED) != CBS_OWNERDRAWFIXED) )
{
COXRowData* pRowData = (COXRowData*)(pMeasureItemStruct->itemData);
ASSERT(AfxIsValidAddress(pRowData, sizeof(COXRowData)));
pMeasureItemStruct->itemData = pRowData->GetItemData();
}
return DefWindowProc(WM_MEASUREITEM,wParam,lParam);
}
LRESULT COXMultiComboBox::OnDrawItem(WPARAM wParam,LPARAM lParam)
{
LPDRAWITEMSTRUCT pDrawItemStruct = (LPDRAWITEMSTRUCT)lParam;
if(pDrawItemStruct->itemID!=-1)
{
COXRowData* pRowData = (COXRowData*)pDrawItemStruct->itemData;
ASSERT(AfxIsValidAddress(pRowData, sizeof(COXRowData)));
pDrawItemStruct->itemData = pRowData->GetItemData();
}
return DefWindowProc(WM_DRAWITEM,wParam,lParam);
}
//CB_ADDSTRING and CB_INSERTSTRING are handled to notify that Size is changed
LRESULT COXMultiComboBox::OnAddString(WPARAM wParam,LPARAM lParam)
{
m_fSizeChanged = TRUE;
//if(::IsWindow(m_ComboLBox.m_hWnd))
// m_ComboLBox.InvalidateRect(NULL);
return DefWindowProc(CB_ADDSTRING,wParam,lParam);
}
LRESULT COXMultiComboBox::OnInsertString(WPARAM wParam,LPARAM lParam)
{
m_fSizeChanged = TRUE;
// if(::IsWindow(m_ComboLBox.m_hWnd))
// m_ComboLBox.InvalidateRect(NULL);
return DefWindowProc(CB_INSERTSTRING,wParam,lParam);
}
int COXMultiComboBox::GetTotalHeight()
// --- In :
// --- Out :
// --- Returns : returns the sum of hieghts of all the items in the combobox
// --- Effect :
{
int nTotalHeight =0;
int nCount = GetCount();
if(!(GetStyle()&CBS_OWNERDRAWVARIABLE))
nTotalHeight = nCount * GetItemHeight(0);
else
for(int nIndex=0; nIndex < nCount; nIndex++)
nTotalHeight+=GetItemHeight(nIndex);
return nTotalHeight;
}
int COXMultiComboBox::GetTotalWidth()
// --- In :
// --- Out :
// --- Returns : Returns the sum of widths of all columns
// --- Effect :
{
int nTotalWidth =0;
int nColumnCount = GetColumnCount();
for(int nColIndex=0; nColIndex < nColumnCount; nColIndex++)
nTotalWidth += GetColumnWidth(nColIndex);
return nTotalWidth;
}
BOOL COXMultiComboBox::SetRowData(int nIndex, COXRowData* pRowData)
// --- In : Index whose Rowdata has to be get
// pRowData : RowData to set to the item
// --- Out :
// --- Returns : TRUE if successfull
// --- Effect : to set the RowData to an Item
{
if(DefWindowProc(CB_SETITEMDATA,nIndex,(DWORD)PtrToUint(pRowData))==CB_ERR)
return FALSE;
else
return TRUE;
}
COXMultiComboBox::COXRowData* COXMultiComboBox::GetRowData(int nIndex) const
// --- In : Index whose Rowdata has to be get
// --- Out :
// --- Returns : Returns row data if pointer to sucessfull, otherwise NULL.
// --- Effect :
{
LRESULT lResult;
// casting is to convert this from 'const COXMultiComboBox*' to
// 'COXMultiComboBox*'
lResult = ((COXMultiComboBox*)this)->DefWindowProc(CB_GETITEMDATA, nIndex, 0l);
if(lResult!=CB_ERR)
return (COXRowData*)lResult;
else
{
TRACE0("In COXMultiComboBox::GetRowData : GetItemData() returned CB_ERR. Returning NULL.\n");
return NULL;
}
}
//Private
BOOL COXMultiComboBox::ChangeMasterColumn(int /* nCol */)
{
int nNumItems = GetCount();
CArray<COXRowData*,COXRowData*> aPtrsRowData;
aPtrsRowData.SetSize(nNumItems);
int nIndex=0;
for(nIndex=0; nIndex < nNumItems; nIndex++)
{
COXRowData* pRowData = GetRowData(nIndex);
aPtrsRowData[nIndex] = pRowData;
if (pRowData == NULL)
TRACE0("In COXMultiComboBox::ChangeMasterColumn : GetRowData() returned NULL.\n");
}
// ... To avoid deleting the Rowdata in DeleteItem
m_fMasterColumnChanging = TRUE;
// ... Delete all the Items
ResetContent();
// ... Resets the flag
m_fMasterColumnChanging = FALSE;
int nRetVal;
// Again add all the items. This deletion and addition is to effect
// the sorting order based on current master column
for(nIndex=0; nIndex < nNumItems; nIndex++)
{
if((nRetVal = CComboBox::AddString((aPtrsRowData[nIndex])->GetColumnString(m_nMasterColumn))) != CB_ERR)
SetRowData(nRetVal,aPtrsRowData[nIndex]);
else
return FALSE;
}
return TRUE;
}
int COXMultiComboBox::COXComboLBox::GetMaxHeight()
{
CRect rcDrop;
((COXMultiComboBox*)m_pWndCombo)->GetDroppedControlRect(&rcDrop);
int nScrollHeight = m_fHorzScrollVisible ? GetSystemMetrics(SM_CYHSCROLL) : 0;
int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
int nTmpHeight = rcDrop.Height()-2*nBorderHeight-nScrollHeight;
int nAbsHeight;
if(((COXMultiComboBox*)m_pWndCombo)->GetStyle() & CBS_OWNERDRAWVARIABLE)
{
nAbsHeight = 0;
int nCount = ((COXMultiComboBox*)m_pWndCombo)->GetCount();
int nItemHeight;
for(int nIndex=0; nIndex < nCount; nIndex++)
{
nItemHeight = ((COXMultiComboBox*)m_pWndCombo)->GetItemHeight(nIndex);
if(nAbsHeight + nItemHeight <= nTmpHeight)
nAbsHeight += nItemHeight;
else
break;
}
}
else
nAbsHeight = nTmpHeight - nTmpHeight%((COXMultiComboBox*)m_pWndCombo)->GetItemHeight(0);
return nAbsHeight + 2*nBorderHeight + nScrollHeight;
}
void COXMultiComboBox::SetFitToSize(BOOL bFitToSize)
{
m_bFitToSize=bFitToSize;
AdjustToFitSize();
}
int COXMultiComboBox::AdjustToFitSize()
{
int nResult;
if(m_bFitToSize)
{
int nTotalWidth=GetTotalWidth()+2*GetSystemMetrics(SM_CXBORDER);
int nTotalHeight=GetTotalHeight();
int nBorderHeight=GetSystemMetrics(SM_CYBORDER);
int nScrollbarHeight=GetSystemMetrics(SM_CYHSCROLL);
CRect rcDrop;
GetDroppedControlRect(&rcDrop);
if((nTotalHeight > rcDrop.Height()-2*nBorderHeight) ||
((nTotalWidth > rcDrop.Width()) &&
(nTotalHeight > rcDrop.Height()-nScrollbarHeight-2*nBorderHeight)))
{
nTotalWidth+=::GetSystemMetrics(SM_CXVSCROLL);
}
int nScreenWidth=::GetSystemMetrics(SM_CXSCREEN);
nTotalWidth=nTotalWidth>nScreenWidth ? nScreenWidth : nTotalWidth;
nResult=SetDroppedWidth(nTotalWidth);
}
else
nResult=SetDroppedWidth(1);
if(nResult!=CB_ERR)
{
// ... To indicate that the scrollbars has to be re-adjusted
m_fSizeChanged = TRUE;
// Redraws the listbox
if(::IsWindow(m_ComboLBox.m_hWnd))
{
m_ComboLBox.RedrawWindow(NULL,NULL,
RDW_ERASE|RDW_INVALIDATE|RDW_ERASENOW|RDW_UPDATENOW|RDW_FRAME);
}
}
return nResult;
}