/////////////////////////////////////////////////////////////////////////////
-// Name: combobox.cpp
+// Name: msw/combobox.cpp
// Purpose: wxComboBox class
// Author: Julian Smart
// Modified by:
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
#ifdef __GNUG__
#pragma implementation "combobox.h"
#endif
#endif
#include "wx/combobox.h"
+#include "wx/brush.h"
#include "wx/clipbrd.h"
#include "wx/msw/private.h"
+#if wxUSE_TOOLTIPS
+ #ifndef __GNUWIN32_OLD__
+ #include <commctrl.h>
+ #endif
+ #include "wx/tooltip.h"
+#endif // wxUSE_TOOLTIPS
+
+// ----------------------------------------------------------------------------
+// wxWin macros
+// ----------------------------------------------------------------------------
+
IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl)
-bool wxComboBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
+// ----------------------------------------------------------------------------
+// function prototypes
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam);
+
+// ---------------------------------------------------------------------------
+// global vars
+// ---------------------------------------------------------------------------
+
+// the pointer to standard radio button wnd proc
+static WXFARPROC gs_wndprocEdit = (WXFARPROC)NULL;
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wnd proc for subclassed edit control
+// ----------------------------------------------------------------------------
+
+LRESULT APIENTRY _EXPORT wxComboEditWndProc(HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
{
- if (param == CBN_SELCHANGE)
- {
- if (GetSelection() > -1)
+ HWND hwndCombo = ::GetParent(hWnd);
+ wxWindow *win = wxFindWinFromHandle((WXHWND)hwndCombo);
+
+ switch ( message )
{
- wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId);
- event.SetInt(GetSelection());
- event.SetEventObject(this);
- event.SetString(GetStringSelection());
- ProcessCommand(event);
+ // forward some messages to the combobox
+ case WM_KEYUP:
+ case WM_KEYDOWN:
+ case WM_CHAR:
+ {
+ wxComboBox *combo = wxDynamicCast(win, wxComboBox);
+ wxCHECK_MSG( combo, 0, _T("should have combo as parent") );
+
+ if ( combo->MSWProcessEditMsg(message, wParam, lParam) )
+ return 0;
+ }
+ break;
+
+#if 0
+ case WM_GETDLGCODE:
+ {
+ wxCHECK_MSG( win, 0, _T("should have a parent") );
+
+ if ( win->GetWindowStyle() & wxPROCESS_ENTER )
+ {
+ // need to return a custom dlg code or we'll never get it
+ return DLGC_WANTMESSAGE;
+ }
+ }
+ break;
+#endif // 0
+
+ // deal with tooltips here
+#if wxUSE_TOOLTIPS
+ case WM_NOTIFY:
+ {
+ wxCHECK_MSG( win, 0, _T("should have a parent") );
+
+ NMHDR* hdr = (NMHDR *)lParam;
+ if ( (int)hdr->code == TTN_NEEDTEXT )
+ {
+ wxToolTip *tooltip = win->GetToolTip();
+ if ( tooltip )
+ {
+ TOOLTIPTEXT *ttt = (TOOLTIPTEXT *)lParam;
+ ttt->lpszText = (wxChar *)tooltip->GetTip().c_str();
+ }
+
+ // processed
+ return 0;
+ }
+ }
+ break;
+#endif // wxUSE_TOOLTIPS
}
- return TRUE;
- }
- else if (param == CBN_EDITCHANGE)
- {
- wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_windowId);
- event.SetString(GetValue());
- event.SetEventObject(this);
- ProcessCommand(event);
-
- return TRUE;
- }
- else
- return FALSE;
+ return ::CallWindowProc(CASTWNDPROC gs_wndprocEdit, hWnd, message, wParam, lParam);
}
-bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
- const wxString& value,
- const wxPoint& pos,
- const wxSize& size,
- int n, const wxString choices[],
- long style,
- const wxValidator& validator,
- const wxString& name)
+WXHBRUSH wxComboBox::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor,
+ WXUINT message,
+ WXWPARAM wParam,
+ WXLPARAM lParam)
{
- SetName(name);
- SetValidator(validator);
- if (parent) parent->AddChild(this);
-// SetBackgroundColour(parent->GetBackgroundColour()) ;
+#if wxUSE_CTL3D
+ if ( m_useCtl3D )
+ {
+ HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
+ return (WXHBRUSH) hbrush;
+ }
+#endif // wxUSE_CTL3D
- // A choice/combobox normally has a white background (or other, depending
- // on global settings) rather than inheriting the parent's background colour.
- SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
+ HDC hdc = (HDC)pDC;
+ if (GetParent()->GetTransparentBackground())
+ SetBkMode(hdc, TRANSPARENT);
+ else
+ SetBkMode(hdc, OPAQUE);
- SetForegroundColour(parent->GetForegroundColour()) ;
+ wxColour colBack = GetBackgroundColour();
- m_windowStyle = style;
+ if (!IsEnabled())
+ colBack = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
- if ( id == -1 )
- m_windowId = (int)NewControlId();
- else
- m_windowId = id;
+ ::SetBkColor(hdc, wxColourToRGB(colBack));
+ ::SetTextColor(hdc, wxColourToRGB(GetForegroundColour()));
- int x = pos.x;
- int y = pos.y;
- int width = size.x;
- int height = size.y;
+ wxBrush *brush = wxTheBrushList->FindOrCreateBrush(colBack, wxSOLID);
- long msStyle = WS_CHILD | WS_TABSTOP | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
- CBS_NOINTEGRALHEIGHT;
+ return (WXHBRUSH)brush->GetResourceHandle();
+}
- if (m_windowStyle & wxCB_READONLY)
- msStyle |= CBS_DROPDOWNLIST;
- else if (m_windowStyle & wxCB_SIMPLE)
- msStyle |= CBS_SIMPLE; // A list (shown always) and edit control
- else
- msStyle |= CBS_DROPDOWN;
+// ----------------------------------------------------------------------------
+// wxComboBox
+// ----------------------------------------------------------------------------
- if (m_windowStyle & wxCB_SORT)
- msStyle |= CBS_SORT;
+bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ switch ( msg )
+ {
+ case WM_CHAR:
+ return HandleChar(wParam, lParam, TRUE /* isASCII */);
- bool want3D;
- WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D) ;
+ case WM_KEYDOWN:
+ return HandleKeyDown(wParam, lParam);
- // Even with extended styles, need to combine with WS_BORDER
- // for them to look right.
- if ( want3D || wxStyleHasBorder(m_windowStyle) )
- msStyle |= WS_BORDER;
+ case WM_KEYUP:
+ return HandleKeyUp(wParam, lParam);
+ }
- m_hWnd = (WXHWND)::CreateWindowEx(exStyle, wxT("COMBOBOX"), NULL,
- msStyle,
- 0, 0, 0, 0, (HWND) parent->GetHWND(), (HMENU)m_windowId,
- wxGetInstance(), NULL);
+ return FALSE;
+}
- wxCHECK_MSG( m_hWnd, FALSE, wxT("Failed to create combobox") );
+bool wxComboBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
+{
+ switch ( param )
+ {
+ case CBN_SELCHANGE:
+ if (GetSelection() > -1)
+ {
+ wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, GetId());
+ event.SetInt(GetSelection());
+ event.SetEventObject(this);
+ event.SetString(GetStringSelection());
+ ProcessCommand(event);
+ }
+ break;
+
+ case CBN_EDITCHANGE:
+ {
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
+ event.SetString(GetValue());
+ event.SetEventObject(this);
+ ProcessCommand(event);
+ }
+ break;
+ }
-/*
-#if wxUSE_CTL3D
- if (want3D)
- {
- Ctl3dSubclassCtl(wx_combo);
- m_useCtl3D = TRUE;
- }
-#endif
-*/
+ // there is no return value for the CBN_ notifications, so always return
+ // FALSE from here to pass the message to DefWindowProc()
+ return FALSE;
+}
- // Subclass again for purposes of dialog editing mode
- SubclassWin(m_hWnd);
+WXHWND wxComboBox::GetEditHWND() const
+{
+ // this function should not be called for wxCB_READONLY controls, it is
+ // the callers responsability to check this
+ wxASSERT_MSG( !(GetWindowStyle() & wxCB_READONLY),
+ _T("read-only combobox doesn't have any edit control") );
+
+ POINT pt;
+ pt.x = pt.y = 4;
+ HWND hwndEdit = ::ChildWindowFromPoint(GetHwnd(), pt);
+ if ( !hwndEdit || hwndEdit == GetHwnd() )
+ {
+ wxFAIL_MSG(_T("not read only combobox without edit control?"));
+ }
- SetFont(parent->GetFont());
- int i;
- for (i = 0; i < n; i++)
- {
- Append(choices[i]);
- }
+ return (WXHWND)hwndEdit;
+}
- SetSelection(i);
+bool wxComboBox::Create(wxWindow *parent, wxWindowID id,
+ const wxString& value,
+ const wxPoint& pos,
+ const wxSize& size,
+ int n, const wxString choices[],
+ long style,
+ const wxValidator& validator,
+ const wxString& name)
+{
+ // first create wxWin object
+ if ( !CreateControl(parent, id, pos, size, style, validator, name) )
+ return FALSE;
+
+ // get the right style
+ long msStyle = WS_TABSTOP | WS_VSCROLL | WS_HSCROLL |
+ CBS_AUTOHSCROLL | CBS_NOINTEGRALHEIGHT /* | WS_CLIPSIBLINGS */;
+ if ( style & wxCB_READONLY )
+ msStyle |= CBS_DROPDOWNLIST;
+ else if ( style & wxCB_SIMPLE )
+ msStyle |= CBS_SIMPLE; // A list (shown always) and edit control
+ else
+ msStyle |= CBS_DROPDOWN;
+
+ if ( style & wxCB_SORT )
+ msStyle |= CBS_SORT;
+
+ // and now create the MSW control
+ if ( !MSWCreateControl(_T("COMBOBOX"), msStyle) )
+ return FALSE;
+
+ SetSize(pos.x, pos.y, size.x, size.y);
+
+ // A choice/combobox normally has a white background (or other, depending
+ // on global settings) rather than inheriting the parent's background colour.
+ SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
+
+ for ( int i = 0; i < n; i++ )
+ {
+ Append(choices[i]);
+ }
- SetSize(x, y, width, height);
- if ( !value.IsEmpty() )
- {
- SetValue(value);
- }
+ if ( !value.IsEmpty() )
+ {
+ SetValue(value);
+ }
+
+ // a (not read only) combobox is, in fact, 2 controls: the combobox itself
+ // and an edit control inside it and if we want to catch events from this
+ // edit control, we must subclass it as well
+ if ( !(style & wxCB_READONLY) )
+ {
+ gs_wndprocEdit = (WXFARPROC)::SetWindowLong
+ (
+ (HWND)GetEditHWND(),
+ GWL_WNDPROC,
+ (LPARAM)wxComboEditWndProc
+ );
+ }
- return TRUE;
+ return TRUE;
}
+// TODO: update and clear all this horrible mess (VZ)
+
void wxComboBox::SetValue(const wxString& value)
{
// If newlines are denoted by just 10, must stick 13 in front.
void wxComboBox::SetInsertionPoint(long pos)
{
-/*
- HWND hWnd = GetHwnd();
#ifdef __WIN32__
- SendMessage(hWnd, EM_SETSEL, pos, pos);
- SendMessage(hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
-#else
- SendMessage(hWnd, EM_SETSEL, 0, MAKELPARAM(pos, pos));
+ HWND hWnd = GetHwnd();
+ SendMessage(hWnd, CB_SETEDITSEL, 0, MAKELPARAM(pos, pos));
+ HWND hEditWnd = (HWND) GetEditHWND() ;
+ if (hEditWnd)
+ {
+ // Scroll insertion point into view
+ SendMessage(hEditWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
+ // Why is this necessary? (Copied from wxTextCtrl::SetInsertionPoint)
+ static const wxChar *nothing = _T("");
+ SendMessage(hEditWnd, EM_REPLACESEL, 0, (LPARAM)nothing);
+ }
#endif
- char *nothing = "";
- SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)nothing);
-*/
}
void wxComboBox::SetInsertionPointEnd()
{
-/*
long pos = GetLastPosition();
SetInsertionPoint(pos);
-*/
}
long wxComboBox::GetInsertionPoint() const
{
-/*
- DWORD Pos=(DWORD)SendMessage(GetHwnd(), EM_GETSEL, 0, 0L);
- return Pos&0xFFFF;
-*/
- return 0;
+#ifdef __WIN32__
+ DWORD Pos=(DWORD)SendMessage(GetHwnd(), CB_GETEDITSEL, 0, 0L);
+ return Pos&0xFFFF;
+#else
+ return 0;
+#endif
}
long wxComboBox::GetLastPosition() const
{
-/*
- HWND hWnd = GetHwnd();
-
- // Will always return a number > 0 (according to docs)
- int noLines = (int)SendMessage(hWnd, EM_GETLINECOUNT, (WPARAM)0, (LPARAM)0L);
-
- // This gets the char index for the _beginning_ of the last line
- int charIndex = (int)SendMessage(hWnd, EM_LINEINDEX, (WPARAM)(noLines-1), (LPARAM)0L);
+ HWND hEditWnd = (HWND) GetEditHWND();
- // Get number of characters in the last line. We'll add this to the character
+ // Get number of characters in the last (only) line. We'll add this to the character
// index for the last line, 1st position.
- int lineLength = (int)SendMessage(hWnd, EM_LINELENGTH, (WPARAM)charIndex, (LPARAM)0L);
+ int lineLength = (int)SendMessage(hEditWnd, EM_LINELENGTH, (WPARAM) 0, (LPARAM)0L);
- return (long)(charIndex + lineLength);
-*/
- return 0;
+ return (long)(lineLength);
}
void wxComboBox::Replace(long from, long to, const wxString& value)
#endif
}
-void wxComboBox::DoSetSize(int x, int y,
- int width, int height,
- int sizeFlags)
+void wxComboBox::DoMoveWindow(int x, int y, int width, int height)
{
- wxControl::DoSetSize(x, y, width, height, sizeFlags);
+ // here is why this is necessary: if the width is negative, the combobox
+ // window proc makes the window of the size width*height instead of
+ // interpreting height in the usual manner (meaning the height of the drop
+ // down list - usually the height specified in the call to MoveWindow()
+ // will not change the height of combo box per se)
+ //
+ // this behaviour is not documented anywhere, but this is just how it is
+ // here (NT 4.4) and, anyhow, the check shouldn't hurt - however without
+ // the check, constraints/sizers using combos may break the height
+ // constraint will have not at all the same value as expected
+ if ( width < 0 )
+ return;
+
+ int cx, cy;
+ wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
+
+ // what should the height of the drop down list be? we choose 10 items by
+ // default and also 10 items max (if we always use n, the list will never
+ // have vertical scrollbar)
+ int n = GetCount();
+ if ( !n || (n > 10) )
+ n = 10;
+
+ height = (n + 1)* EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
+
+ wxControl::DoMoveWindow(x, y, width, height);
+}
+
+wxSize wxComboBox::DoGetBestSize() const
+{
+ // the choice calculates the horz size correctly, but not the vertical
+ // component: correct it
+ wxSize size = wxChoice::DoGetBestSize();
+
+ int cx, cy;
+ wxGetCharSize(GetHWND(), &cx, &cy, &GetFont());
+ size.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
+
+ return size;
}
#endif