]> git.saurik.com Git - wxWidgets.git/blame - src/msw/combo.cpp
Commiting re-baked files after wxListCtrl OS X additions.
[wxWidgets.git] / src / msw / combo.cpp
CommitLineData
a340b80d 1/////////////////////////////////////////////////////////////////////////////
93f7f8be 2// Name: src/msw/combo.cpp
a57d600f 3// Purpose: wxMSW wxComboCtrl
a340b80d
VZ
4// Author: Jaakko Salli
5// Modified by:
6// Created: Apr-30-2006
7// RCS-ID: $Id$
8// Copyright: (c) 2005 Jaakko Salli
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
a57d600f 26#if wxUSE_COMBOCTRL
a340b80d
VZ
27
28#ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/combobox.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/dialog.h"
34#endif
35
36#include "wx/dcbuffer.h"
37
38#include "wx/combo.h"
39
40
41#include "wx/msw/uxtheme.h"
42
43// Change to #if 1 to include tmschema.h for easier testing of theme
44// parameters.
45#if 0
46 #include <tmschema.h>
47#else
48 //----------------------------------
49 #define EP_EDITTEXT 1
50 #define ETS_NORMAL 1
51 #define ETS_HOT 2
52 #define ETS_SELECTED 3
53 #define ETS_DISABLED 4
54 #define ETS_FOCUSED 5
55 #define ETS_READONLY 6
56 #define ETS_ASSIST 7
57 #define TMT_FILLCOLOR 3802
58 #define TMT_TEXTCOLOR 3803
59 #define TMT_BORDERCOLOR 3801
60 #define TMT_EDGEFILLCOLOR 3808
61 //----------------------------------
62#endif
63
64
65#define NATIVE_TEXT_INDENT_XP 4
66#define NATIVE_TEXT_INDENT_CLASSIC 2
67
68#define TEXTCTRLXADJUST_XP 1
69#define TEXTCTRLYADJUST_XP 3
70#define TEXTCTRLXADJUST_CLASSIC 1
71#define TEXTCTRLYADJUST_CLASSIC 2
72
73
74// ============================================================================
75// implementation
76// ============================================================================
77
78
a57d600f
VZ
79BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
80 EVT_PAINT(wxComboCtrl::OnPaintEvent)
81 EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
a340b80d
VZ
82END_EVENT_TABLE()
83
84
a57d600f 85IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxComboCtrlBase)
a340b80d 86
a57d600f 87void wxComboCtrl::Init()
a340b80d
VZ
88{
89}
90
a57d600f 91bool wxComboCtrl::Create(wxWindow *parent,
a340b80d
VZ
92 wxWindowID id,
93 const wxString& value,
94 const wxPoint& pos,
95 const wxSize& size,
96 long style,
97 const wxValidator& validator,
98 const wxString& name)
99{
100
101 // Set border
102 long border = style & wxBORDER_MASK;
103
104 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
105
106 if ( !border )
107 {
108 // For XP, have 1-width custom border, for older version use sunken
109 if ( theme )
110 {
111 border = wxBORDER_NONE;
112 m_widthCustomBorder = 1;
113 }
114 else
115 border = wxBORDER_SUNKEN;
116
117 style = (style & ~(wxBORDER_MASK)) | border;
118 }
119
120 // create main window
a57d600f 121 if ( !wxComboCtrlBase::Create(parent,
93f7f8be
WS
122 id,
123 value,
124 pos,
125 size,
126 style | wxFULL_REPAINT_ON_RESIZE,
127 wxDefaultValidator,
128 name) )
a340b80d
VZ
129 return false;
130
131 if ( style & wxCC_STD_BUTTON )
132 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
133
134 // Create textctrl, if necessary
135 CreateTextCtrl( wxNO_BORDER, validator );
136
137 // Add keyboard input handlers for main control and textctrl
b445b6a7 138 InstallInputHandlers();
a340b80d
VZ
139
140 // Prepare background for double-buffering
141 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
142
93f7f8be
WS
143 // SetBestSize should be called last
144 SetBestSize(size);
a340b80d
VZ
145
146 return true;
147}
148
a57d600f 149wxComboCtrl::~wxComboCtrl()
a340b80d
VZ
150{
151}
152
a57d600f 153void wxComboCtrl::OnThemeChange()
a340b80d
VZ
154{
155 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
156 if ( theme )
157 {
158 wxUxThemeHandle hTheme(this, L"COMBOBOX");
159
160 COLORREF col;
161 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col);
162 SetBackgroundColour(wxRGBToColour(col));
163 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col);
164 SetForegroundColour(wxRGBToColour(col));
165 }
166 else
167 {
168 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
169 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
170 }
171}
172
a57d600f 173void wxComboCtrl::OnResize()
a340b80d
VZ
174{
175 //
176 // Recalculates button and textctrl areas
177
178 int textCtrlXAdjust;
179 int textCtrlYAdjust;
180
181 if ( wxUxThemeEngine::GetIfActive() )
182 {
183 textCtrlXAdjust = TEXTCTRLXADJUST_XP;
184 textCtrlYAdjust = TEXTCTRLYADJUST_XP;
185 }
186 else
187 {
188 textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
189 textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
190 }
191
192 // Technically Classic Windows style combo has more narrow button,
193 // but the native renderer doesn't paint it well like that.
194 int btnWidth = 17;
195 CalculateAreas(btnWidth);
196
197 // Position textctrl using standard routine
198 PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
199}
200
201// Draws non-XP GUI dotted line around the focus area
202static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
203{
204#if !defined(__WXWINCE__)
205 /*
206 RECT mswRect;
207 mswRect.left = rect.x;
208 mswRect.top = rect.y;
209 mswRect.right = rect.x + rect.width;
210 mswRect.bottom = rect.y + rect.height;
211 HDC hdc = (HDC) dc.GetHDC();
212 SetMapMode(hdc,MM_TEXT); // Just in case...
213 DrawFocusRect(hdc,&mswRect);
214 */
215 // FIXME: Use DrawFocusRect code above (currently it draws solid line
216 // for caption focus but works ok for other stuff).
217 // Also, this code below may not work in future wx versions, since
218 // it employs wxCAP_BUTT hack to have line of width 1.
219 dc.SetLogicalFunction(wxINVERT);
220
221 wxPen pen(*wxBLACK,1,wxDOT);
222 pen.SetCap(wxCAP_BUTT);
223 dc.SetPen(pen);
224 dc.SetBrush(*wxTRANSPARENT_BRUSH);
225
226 dc.DrawRectangle(rect);
227
228 dc.SetLogicalFunction(wxCOPY);
229#else
230 dc.SetLogicalFunction(wxINVERT);
231
232 dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
233 dc.SetBrush(*wxTRANSPARENT_BRUSH);
234
235 dc.DrawRectangle(rect);
236
237 dc.SetLogicalFunction(wxCOPY);
238#endif
239}
240
241// draw focus background on area in a way typical on platform
118f5fbd 242void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
a340b80d
VZ
243{
244 wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
40b26d75
WS
245
246 // Constructor only calls GetHWND() const, so it should be safe
247 // to cast "this" to const.
a340b80d
VZ
248 wxUxThemeHandle hTheme(this, L"COMBOBOX");
249 //COLORREF cref;
250
251 wxSize sz = GetClientSize();
252 bool isEnabled;
253 bool isFocused; // also selected
254
255 // For smaller size control (and for disabled background) use less spacing
256 int focusSpacingX;
257 int focusSpacingY;
258
259 if ( !(flags & wxCONTROL_ISSUBMENU) )
260 {
261 // Drawing control
262 isEnabled = IsEnabled();
263 isFocused = ShouldDrawFocus();
264
265 // Windows-style: for smaller size control (and for disabled background) use less spacing
266 if ( hTheme )
267 {
268 // WinXP Theme
269 focusSpacingX = isEnabled ? 2 : 1;
270 focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
271 }
272 else
273 {
274 // Classic Theme
275 if ( isEnabled )
276 {
277 focusSpacingX = 1;
278 focusSpacingY = 1;
279 }
280 else
281 {
282 focusSpacingX = 0;
283 focusSpacingY = 0;
284 }
285 }
286 }
287 else
288 {
289 // Drawing a list item
290 isEnabled = true; // they are never disabled
291 isFocused = flags & wxCONTROL_SELECTED ? true : false;
292
293 focusSpacingX = 0;
294 focusSpacingY = 0;
295 }
296
297 // Set the background sub-rectangle for selection, disabled etc
298 wxRect selRect(rect);
299 selRect.y += focusSpacingY;
300 selRect.height -= (focusSpacingY*2);
8e5ec129
WS
301
302 int wcp = 0;
303
304 if ( !(flags & wxCONTROL_ISSUBMENU) )
305 wcp += m_widthCustomPaint;
306
307 selRect.x += wcp + focusSpacingX;
308 selRect.width -= wcp + (focusSpacingX*2);
a340b80d
VZ
309
310 if ( hTheme )
311 theme = wxUxThemeEngine::GetIfActive();
312
313 wxColour bgCol;
314 bool drawDottedEdge = false;
315
316 if ( isEnabled )
317 {
318 // If popup is hidden and this control is focused,
319 // then draw the focus-indicator (selbgcolor background etc.).
320 if ( isFocused )
321 {
322 #if 0
323 // TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
324 // those below don't work)
325 if ( hTheme )
326 {
327 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref);
328 dc.SetTextForeground( wxRGBToColour(cref) );
329 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref);
330 bgCol = wxRGBToColour(cref);
331 }
332 else
333 #endif
334 {
335 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
336 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
337 if ( m_windowStyle & wxCB_READONLY )
338 drawDottedEdge = true;
339 }
340 }
341 else
342 {
343 /*if ( hTheme )
344 {
345 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
346 dc.SetTextForeground( wxRGBToColour(cref) );
347 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&cref);
348 bgCol = wxRGBToColour(cref);
349 }
350 else
351 {*/
352 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
353 bgCol = GetBackgroundColour();
354 //}
355 }
356 }
357 else
358 {
359 /*if ( hTheme )
360 {
361 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_TEXTCOLOR,&cref);
362 dc.SetTextForeground( wxRGBToColour(cref) );
363 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_EDGEFILLCOLOR,&cref);
364 bgCol = wxRGBToColour(cref);
365 }
366 else
367 {*/
368 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
369 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
370 //}
371 }
372
373 dc.SetBrush(bgCol);
374 dc.SetPen(bgCol);
375 dc.DrawRectangle(selRect);
376 if ( drawDottedEdge )
377 wxMSWDrawFocusRect(dc,selRect);
378
118f5fbd
RR
379 // Don't clip exactly to the selection rectangle so we can draw
380 // to the non-selected area in front of it.
381 wxRect clipRect(rect.x,rect.y,
382 (selRect.x+selRect.width)-rect.x-1,rect.height);
383 dc.SetClippingRegion(clipRect);
a340b80d
VZ
384}
385
a57d600f 386void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
a340b80d
VZ
387{
388 // TODO: Convert drawing in this function to Windows API Code
389
390 wxSize sz = GetClientSize();
391 wxBufferedPaintDC dc(this,GetBufferBitmap(sz));
392
393 const wxRect& rectb = m_btnArea;
394 wxRect rect = m_tcArea;
395 bool isEnabled = IsEnabled();
396 wxColour bgCol = GetBackgroundColour();
397 wxColour fgCol;
398
399 wxUxThemeEngine* theme = NULL;
400 wxUxThemeHandle hTheme(this, L"COMBOBOX");
401 int etsState;
402
403 // area around both controls
404 wxRect rect2(0,0,sz.x,sz.y);
405 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
406 {
407 rect2 = m_tcArea;
408 rect2.Inflate(1);
409 }
410
411 // Use theme to draw border on XP
412 if ( hTheme )
413 {
414 theme = wxUxThemeEngine::GetIfActive();
415 COLORREF cref;
416
417 // Select correct border colour
418 if ( !isEnabled )
419 etsState = ETS_DISABLED;
420 else
421 etsState = ETS_NORMAL;
422
423 if ( m_widthCustomBorder )
424 {
425 theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref);
426
427 // Set border colour
428 dc.SetPen( wxRGBToColour(cref) );
429
430 dc.SetBrush( *wxTRANSPARENT_BRUSH );
431 dc.DrawRectangle(rect2);
432 }
433
434 theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_TEXTCOLOR,&cref);
435 fgCol = wxRGBToColour(cref);
436 }
437 else
438 {
439 // draw regular background
440 fgCol = GetForegroundColour();
441 }
442
443 rect2.Deflate(m_widthCustomBorder);
444
445 dc.SetBrush(bgCol);
446 dc.SetPen(bgCol);
447
448 // clear main background
449 dc.DrawRectangle(rect);
450
451 // Button background with theme?
452 bool drawButBg = true;
453 if ( hTheme && m_blankButtonBg )
454 {
455 RECT r;
456 wxCopyRectToRECT(rectb, r);
457
458 // Draw parent background if needed (since button looks like its out of
459 // the combo, this is preferred).
460 theme->DrawThemeParentBackground(GetHwndOf(this),
461 GetHdcOf(dc),
462 &r);
463
464 drawButBg = false;
93f7f8be 465 }
a340b80d
VZ
466
467 // Standard button rendering
468 DrawButton(dc,rectb,drawButBg);
469
470 // paint required portion on the control
6d0ce565 471 if ( (!m_text || m_widthCustomPaint) )
a340b80d
VZ
472 {
473 wxASSERT( m_widthCustomPaint >= 0 );
474
475 // this is intentionally here to allow drawed rectangle's
476 // right edge to be hidden
477 if ( m_text )
478 rect.width = m_widthCustomPaint;
479
480 dc.SetFont( GetFont() );
481
482 dc.SetClippingRegion(rect);
6d0ce565
VZ
483 if ( m_popupInterface )
484 m_popupInterface->PaintComboControl(dc,rect);
485 else
486 wxComboPopup::DefaultPaintComboControl(this,dc,rect);
a340b80d
VZ
487 }
488}
489
a57d600f 490void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
a340b80d 491{
22a35096 492 bool isOnButtonArea = m_btnArea.Contains(event.m_x,event.m_y);
a340b80d
VZ
493 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
494
495 // Preprocessing fabricates double-clicks and prevents
496 // (it may also do other common things in future)
497 if ( PreprocessMouseEvent(event,isOnButtonArea) )
498 return;
499
500 if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
501 {
502 // if no textctrl and no special double-click, then the entire control acts
503 // as a button
504 handlerFlags |= wxCC_MF_ON_BUTTON;
505 if ( HandleButtonMouseEvent(event,handlerFlags) )
506 return;
507 }
508 else
509 {
510 if ( isOnButtonArea || HasCapture() )
511 {
512 if ( HandleButtonMouseEvent(event,handlerFlags) )
513 return;
514 }
515 else if ( m_btnState )
516 {
517 // otherwise need to clear the hover status
518 m_btnState = 0;
519 RefreshRect(m_btnArea);
520 }
521 }
522
523 //
524 // This will handle left_down and left_dclick events outside button in a Windows-like manner.
525 // See header file for further information on this method.
526 HandleNormalMouseEvent(event);
527
528}
529
a57d600f 530wxCoord wxComboCtrl::GetNativeTextIndent() const
a340b80d
VZ
531{
532 if ( wxUxThemeEngine::GetIfActive() )
533 return NATIVE_TEXT_INDENT_XP;
534 return NATIVE_TEXT_INDENT_CLASSIC;
535}
536
b445b6a7
VZ
537bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
538{
9e68717c 539 const bool isPopupShown = IsPopupShown();
b445b6a7 540
9e68717c 541 switch ( event.GetKeyCode() )
b445b6a7 542 {
9e68717c
VZ
543 case WXK_F4:
544 // F4 toggles the popup in the native comboboxes, so emulate them
545 if ( !event.AltDown() )
546 return true;
547 break;
548
549 case WXK_ESCAPE:
550 if ( isPopupShown )
551 return true;
552 break;
553
554 case WXK_DOWN:
555 case WXK_UP:
556 // On XP or with writable combo in Classic, arrows don't open the
557 // popup but Alt-arrow does
558 if ( event.AltDown() ||
559 ( !isPopupShown &&
560 HasFlag(wxCB_READONLY) &&
561 !wxUxThemeEngine::GetIfActive()
562 ) )
563 {
564 return true;
565 }
566 break;
b445b6a7
VZ
567 }
568
569 return false;
570}
a340b80d 571
a57d600f 572#endif // wxUSE_COMBOCTRL