]> git.saurik.com Git - wxWidgets.git/blame - src/msw/combo.cpp
don't recompute nonbreakable space character every time a text fragment is added...
[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"
2835f3cf 34 #include "wx/stopwatch.h"
a340b80d
VZ
35#endif
36
37#include "wx/dcbuffer.h"
a340b80d
VZ
38#include "wx/combo.h"
39
974a12f8 40#include "wx/msw/registry.h"
f100b1c3 41#if wxUSE_UXTHEME
a340b80d 42#include "wx/msw/uxtheme.h"
f100b1c3 43#endif
a340b80d
VZ
44
45// Change to #if 1 to include tmschema.h for easier testing of theme
46// parameters.
47#if 0
48 #include <tmschema.h>
c905c0d6 49 #include <VSStyle.h>
a340b80d
VZ
50#else
51 //----------------------------------
52 #define EP_EDITTEXT 1
53 #define ETS_NORMAL 1
54 #define ETS_HOT 2
55 #define ETS_SELECTED 3
56 #define ETS_DISABLED 4
57 #define ETS_FOCUSED 5
58 #define ETS_READONLY 6
59 #define ETS_ASSIST 7
60 #define TMT_FILLCOLOR 3802
61 #define TMT_TEXTCOLOR 3803
62 #define TMT_BORDERCOLOR 3801
63 #define TMT_EDGEFILLCOLOR 3808
c905c0d6
VZ
64 #define TMT_BGTYPE 4001
65
66 #define BT_IMAGEFILE 0
67 #define BT_BORDERFILL 1
68
69 #define CP_DROPDOWNBUTTON 1
70 #define CP_BACKGROUND 2 // This and above are Vista and later only
71 #define CP_TRANSPARENTBACKGROUND 3
72 #define CP_BORDER 4
73 #define CP_READONLY 5
74 #define CP_DROPDOWNBUTTONRIGHT 6
75 #define CP_DROPDOWNBUTTONLEFT 7
76 #define CP_CUEBANNER 8
77
78 #define CBXS_NORMAL 1
79 #define CBXS_HOT 2
80 #define CBXS_PRESSED 3
81 #define CBXS_DISABLED 4
82
83 #define CBXSR_NORMAL 1
84 #define CBXSR_HOT 2
85 #define CBXSR_PRESSED 3
86 #define CBXSR_DISABLED 4
87
88 #define CBXSL_NORMAL 1
89 #define CBXSL_HOT 2
90 #define CBXSL_PRESSED 3
91 #define CBXSL_DISABLED 4
92
93 #define CBTBS_NORMAL 1
94 #define CBTBS_HOT 2
95 #define CBTBS_DISABLED 3
96 #define CBTBS_FOCUSED 4
97
98 #define CBB_NORMAL 1
99 #define CBB_HOT 2
100 #define CBB_FOCUSED 3
101 #define CBB_DISABLED 4
102
103 #define CBRO_NORMAL 1
104 #define CBRO_HOT 2
105 #define CBRO_PRESSED 3
106 #define CBRO_DISABLED 4
107
108 #define CBCB_NORMAL 1
109 #define CBCB_HOT 2
110 #define CBCB_PRESSED 3
111 #define CBCB_DISABLED 4
112
a340b80d
VZ
113#endif
114
115
116#define NATIVE_TEXT_INDENT_XP 4
117#define NATIVE_TEXT_INDENT_CLASSIC 2
118
119#define TEXTCTRLXADJUST_XP 1
120#define TEXTCTRLYADJUST_XP 3
121#define TEXTCTRLXADJUST_CLASSIC 1
c905c0d6 122#define TEXTCTRLYADJUST_CLASSIC 3
a340b80d 123
30be036c
RR
124#define COMBOBOX_ANIMATION_RESOLUTION 10
125
974a12f8 126#define COMBOBOX_ANIMATION_DURATION 200 // In milliseconds
a340b80d 127
53a64063 128#define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM (1<<2)
30be036c
RR
129
130
a340b80d
VZ
131// ============================================================================
132// implementation
133// ============================================================================
134
135
a57d600f
VZ
136BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
137 EVT_PAINT(wxComboCtrl::OnPaintEvent)
138 EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
53a64063 139#if wxUSE_COMBOCTRL_POPUP_ANIMATION
30be036c 140 EVT_TIMER(wxID_ANY, wxComboCtrl::OnTimerEvent)
53a64063 141#endif
a340b80d
VZ
142END_EVENT_TABLE()
143
144
a57d600f 145IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxComboCtrlBase)
a340b80d 146
a57d600f 147void wxComboCtrl::Init()
a340b80d
VZ
148{
149}
150
a57d600f 151bool wxComboCtrl::Create(wxWindow *parent,
a340b80d
VZ
152 wxWindowID id,
153 const wxString& value,
154 const wxPoint& pos,
155 const wxSize& size,
156 long style,
157 const wxValidator& validator,
158 const wxString& name)
159{
160
161 // Set border
162 long border = style & wxBORDER_MASK;
163
f100b1c3 164#if wxUSE_UXTHEME
a340b80d 165 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
f100b1c3 166#endif
a340b80d
VZ
167
168 if ( !border )
169 {
f100b1c3 170#if wxUSE_UXTHEME
a340b80d
VZ
171 if ( theme )
172 {
c905c0d6 173 // For XP, have 1-width custom border, for older version use sunken
a340b80d
VZ
174 border = wxBORDER_NONE;
175 m_widthCustomBorder = 1;
176 }
177 else
f100b1c3 178#endif
a340b80d
VZ
179 border = wxBORDER_SUNKEN;
180
181 style = (style & ~(wxBORDER_MASK)) | border;
182 }
183
184 // create main window
a57d600f 185 if ( !wxComboCtrlBase::Create(parent,
93f7f8be
WS
186 id,
187 value,
188 pos,
189 size,
190 style | wxFULL_REPAINT_ON_RESIZE,
191 wxDefaultValidator,
192 name) )
a340b80d
VZ
193 return false;
194
f100b1c3 195#if wxUSE_UXTHEME
c905c0d6
VZ
196 if ( theme )
197 {
198 if ( ::wxGetWinVersion() >= wxWinVersion_Vista )
199 m_iFlags |= wxCC_BUTTON_STAYS_DOWN |wxCC_BUTTON_COVERS_BORDER;
200 }
f100b1c3 201#endif
c905c0d6 202
a340b80d
VZ
203 if ( style & wxCC_STD_BUTTON )
204 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
205
206 // Create textctrl, if necessary
207 CreateTextCtrl( wxNO_BORDER, validator );
208
209 // Add keyboard input handlers for main control and textctrl
b445b6a7 210 InstallInputHandlers();
a340b80d
VZ
211
212 // Prepare background for double-buffering
213 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
214
170acdc9
RD
215 // SetInitialSize should be called last
216 SetInitialSize(size);
a340b80d
VZ
217
218 return true;
219}
220
a57d600f 221wxComboCtrl::~wxComboCtrl()
a340b80d
VZ
222{
223}
224
a57d600f 225void wxComboCtrl::OnThemeChange()
a340b80d 226{
73049b0d
VZ
227 // there doesn't seem to be any way to get the text colour using themes
228 // API: TMT_TEXTCOLOR doesn't work neither for EDIT nor COMBOBOX
229 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
230
f100b1c3 231#if wxUSE_UXTHEME
73049b0d 232 wxUxThemeEngine * const theme = wxUxThemeEngine::GetIfActive();
a340b80d
VZ
233 if ( theme )
234 {
73049b0d 235 // NB: use EDIT, not COMBOBOX (the latter works in XP but not Vista)
721d1838 236 wxUxThemeHandle hTheme(this, L"EDIT");
a340b80d 237 COLORREF col;
73049b0d
VZ
238 HRESULT hr = theme->GetThemeColor
239 (
240 hTheme,
241 EP_EDITTEXT,
242 ETS_NORMAL,
243 TMT_FILLCOLOR,
244 &col
245 );
246 if ( SUCCEEDED(hr) )
247 {
721d1838
VZ
248 SetBackgroundColour(wxRGBToColour(col));
249
73049b0d
VZ
250 // skip the call below
251 return;
252 }
253
254 wxLogApiError(_T("GetThemeColor(EDIT, ETS_NORMAL, TMT_FILLCOLOR)"), hr);
a340b80d 255 }
f100b1c3 256#endif
73049b0d
VZ
257
258 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
a340b80d
VZ
259}
260
a57d600f 261void wxComboCtrl::OnResize()
a340b80d
VZ
262{
263 //
264 // Recalculates button and textctrl areas
265
266 int textCtrlXAdjust;
267 int textCtrlYAdjust;
268
f100b1c3 269#if wxUSE_UXTHEME
a340b80d
VZ
270 if ( wxUxThemeEngine::GetIfActive() )
271 {
272 textCtrlXAdjust = TEXTCTRLXADJUST_XP;
273 textCtrlYAdjust = TEXTCTRLYADJUST_XP;
274 }
275 else
f100b1c3 276#endif
a340b80d
VZ
277 {
278 textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
279 textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
280 }
281
282 // Technically Classic Windows style combo has more narrow button,
283 // but the native renderer doesn't paint it well like that.
284 int btnWidth = 17;
285 CalculateAreas(btnWidth);
286
287 // Position textctrl using standard routine
288 PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
289}
290
291// Draws non-XP GUI dotted line around the focus area
292static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
293{
294#if !defined(__WXWINCE__)
295 /*
296 RECT mswRect;
297 mswRect.left = rect.x;
298 mswRect.top = rect.y;
299 mswRect.right = rect.x + rect.width;
300 mswRect.bottom = rect.y + rect.height;
301 HDC hdc = (HDC) dc.GetHDC();
302 SetMapMode(hdc,MM_TEXT); // Just in case...
303 DrawFocusRect(hdc,&mswRect);
304 */
305 // FIXME: Use DrawFocusRect code above (currently it draws solid line
306 // for caption focus but works ok for other stuff).
307 // Also, this code below may not work in future wx versions, since
308 // it employs wxCAP_BUTT hack to have line of width 1.
309 dc.SetLogicalFunction(wxINVERT);
310
311 wxPen pen(*wxBLACK,1,wxDOT);
312 pen.SetCap(wxCAP_BUTT);
313 dc.SetPen(pen);
314 dc.SetBrush(*wxTRANSPARENT_BRUSH);
315
316 dc.DrawRectangle(rect);
317
318 dc.SetLogicalFunction(wxCOPY);
319#else
320 dc.SetLogicalFunction(wxINVERT);
321
322 dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
323 dc.SetBrush(*wxTRANSPARENT_BRUSH);
324
325 dc.DrawRectangle(rect);
326
327 dc.SetLogicalFunction(wxCOPY);
328#endif
329}
330
331// draw focus background on area in a way typical on platform
0d203f87
VZ
332void
333wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
a340b80d 334{
f100b1c3 335#if wxUSE_UXTHEME
a340b80d 336 wxUxThemeHandle hTheme(this, L"COMBOBOX");
f100b1c3 337#endif
a340b80d
VZ
338
339 wxSize sz = GetClientSize();
340 bool isEnabled;
c905c0d6 341 bool doDrawFocusRect; // also selected
a340b80d
VZ
342
343 // For smaller size control (and for disabled background) use less spacing
344 int focusSpacingX;
345 int focusSpacingY;
346
347 if ( !(flags & wxCONTROL_ISSUBMENU) )
348 {
349 // Drawing control
350 isEnabled = IsEnabled();
c905c0d6 351 doDrawFocusRect = ShouldDrawFocus();
a340b80d 352
f100b1c3 353#if wxUSE_UXTHEME
a340b80d
VZ
354 // Windows-style: for smaller size control (and for disabled background) use less spacing
355 if ( hTheme )
356 {
357 // WinXP Theme
358 focusSpacingX = isEnabled ? 2 : 1;
359 focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
360 }
361 else
f100b1c3 362#endif
a340b80d
VZ
363 {
364 // Classic Theme
365 if ( isEnabled )
366 {
367 focusSpacingX = 1;
368 focusSpacingY = 1;
369 }
370 else
371 {
372 focusSpacingX = 0;
373 focusSpacingY = 0;
374 }
375 }
376 }
377 else
378 {
379 // Drawing a list item
380 isEnabled = true; // they are never disabled
c905c0d6 381 doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
a340b80d
VZ
382
383 focusSpacingX = 0;
384 focusSpacingY = 0;
385 }
386
387 // Set the background sub-rectangle for selection, disabled etc
388 wxRect selRect(rect);
389 selRect.y += focusSpacingY;
390 selRect.height -= (focusSpacingY*2);
8e5ec129
WS
391
392 int wcp = 0;
393
394 if ( !(flags & wxCONTROL_ISSUBMENU) )
395 wcp += m_widthCustomPaint;
396
397 selRect.x += wcp + focusSpacingX;
398 selRect.width -= wcp + (focusSpacingX*2);
a340b80d 399
0d203f87
VZ
400 //wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
401 //if ( hTheme )
402 // theme = wxUxThemeEngine::GetIfActive();
a340b80d
VZ
403
404 wxColour bgCol;
c905c0d6
VZ
405 bool doDrawDottedEdge = false;
406 bool doDrawSelRect = true;
407
408 // TODO: doDrawDottedEdge = true when focus has arrived to control via tab.
409 // (and other cases which are not that apparent).
a340b80d
VZ
410
411 if ( isEnabled )
412 {
413 // If popup is hidden and this control is focused,
414 // then draw the focus-indicator (selbgcolor background etc.).
c905c0d6 415 if ( doDrawFocusRect )
a340b80d 416 {
c905c0d6
VZ
417 // NB: We can't really use XP visual styles to get TMT_TEXTCOLOR since
418 // it is not properly defined for combo boxes. Instead, they expect
419 // you to use DrawThemeText.
420 //
421 // Here is, however, sample code how to get theme colours:
422 //
423 // COLORREF cref;
424 // theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
425 // dc.SetTextForeground( wxRGBToColour(cref) );
426 if ( (m_iFlags & wxCC_FULL_BUTTON) && !(flags & wxCONTROL_ISSUBMENU) )
a340b80d 427 {
c905c0d6
VZ
428 // Vista style read-only combo
429 doDrawSelRect = false;
430 doDrawDottedEdge = true;
a340b80d
VZ
431 }
432 else
a340b80d
VZ
433 {
434 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
435 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
a340b80d
VZ
436 }
437 }
438 else
439 {
c905c0d6
VZ
440 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
441 bgCol = GetBackgroundColour();
442 doDrawSelRect = false;
a340b80d
VZ
443 }
444 }
445 else
446 {
c905c0d6
VZ
447 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
448 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
a340b80d
VZ
449 }
450
451 dc.SetBrush(bgCol);
c905c0d6
VZ
452 if ( doDrawSelRect )
453 {
454 dc.SetPen(bgCol);
455 dc.DrawRectangle(selRect);
456 }
457
458 if ( doDrawDottedEdge )
459 wxMSWDrawFocusRect(dc, selRect);
a340b80d 460
118f5fbd
RR
461 // Don't clip exactly to the selection rectangle so we can draw
462 // to the non-selected area in front of it.
463 wxRect clipRect(rect.x,rect.y,
464 (selRect.x+selRect.width)-rect.x-1,rect.height);
465 dc.SetClippingRegion(clipRect);
a340b80d
VZ
466}
467
a57d600f 468void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
a340b80d
VZ
469{
470 // TODO: Convert drawing in this function to Windows API Code
471
472 wxSize sz = GetClientSize();
2e992e06 473 wxAutoBufferedPaintDC dc(this);
a340b80d 474
c905c0d6
VZ
475 const wxRect& rectButton = m_btnArea;
476 wxRect rectTextField = m_tcArea;
a340b80d 477 wxColour bgCol = GetBackgroundColour();
c905c0d6 478
d6f2a891
VZ
479#if wxUSE_UXTHEME
480 const bool isEnabled = IsEnabled();
481
c905c0d6
VZ
482 HDC hDc = GetHdcOf(dc);
483 HWND hWnd = GetHwndOf(this);
a340b80d
VZ
484
485 wxUxThemeEngine* theme = NULL;
486 wxUxThemeHandle hTheme(this, L"COMBOBOX");
a340b80d 487
c905c0d6
VZ
488 if ( hTheme )
489 theme = wxUxThemeEngine::GetIfActive();
d6f2a891 490#endif // wxUSE_UXTHEME
c905c0d6
VZ
491
492 wxRect borderRect(0,0,sz.x,sz.y);
493
a340b80d
VZ
494 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
495 {
c905c0d6
VZ
496 borderRect = m_tcArea;
497 borderRect.Inflate(1);
a340b80d
VZ
498 }
499
c905c0d6
VZ
500 int drawButFlags = 0;
501
f100b1c3 502#if wxUSE_UXTHEME
a340b80d
VZ
503 if ( hTheme )
504 {
c905c0d6
VZ
505 const bool useVistaComboBox = ::wxGetWinVersion() >= wxWinVersion_Vista;
506
507 RECT rFull;
508 wxCopyRectToRECT(borderRect, rFull);
509
510 RECT rButton;
511 wxCopyRectToRECT(rectButton, rButton);
512
513 RECT rBorder;
514 wxCopyRectToRECT(borderRect, rBorder);
515
516 bool isNonStdButton = (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) ||
517 (m_iFlags & wxCC_IFLAG_HAS_NONSTANDARD_BUTTON);
518
519 //
520 // Get some states for themed drawing
521 int butState;
a340b80d 522
a340b80d 523 if ( !isEnabled )
c905c0d6
VZ
524 {
525 butState = CBXS_DISABLED;
526 }
527 // Vista will display the drop-button as depressed always
528 // when the popup window is visilbe
529 else if ( (m_btnState & wxCONTROL_PRESSED) ||
530 (useVistaComboBox && !IsPopupWindowState(Hidden)) )
531 {
532 butState = CBXS_PRESSED;
533 }
534 else if ( m_btnState & wxCONTROL_CURRENT )
535 {
536 butState = CBXS_HOT;
537 }
a340b80d 538 else
c905c0d6
VZ
539 {
540 butState = CBXS_NORMAL;
541 }
542
543 int comboBoxPart = 0; // For XP, use the 'default' part
544 RECT* rUseForBg = &rBorder;
545
546 bool drawFullButton = false;
547 int bgState = butState;
548 const bool isFocused = (FindFocus() == GetMainWindowOfCompositeControl()) ? true : false;
a340b80d 549
c905c0d6 550 if ( useVistaComboBox )
a340b80d 551 {
c905c0d6
VZ
552 // FIXME: Either SetBackgroundColour or GetBackgroundColour
553 // doesn't work under Vista, so here's a temporary
554 // workaround.
555 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
a340b80d 556
c905c0d6
VZ
557 // Draw the entire control as a single button?
558 if ( !isNonStdButton )
559 {
560 if ( HasFlag(wxCB_READONLY) )
561 drawFullButton = true;
562 }
563
564 if ( drawFullButton )
565 {
566 comboBoxPart = CP_READONLY;
567 rUseForBg = &rFull;
568
569 // It should be safe enough to update this flag here.
570 m_iFlags |= wxCC_FULL_BUTTON;
571 }
572 else
573 {
574 comboBoxPart = CP_BORDER;
575 m_iFlags &= ~wxCC_FULL_BUTTON;
a340b80d 576
c905c0d6
VZ
577 if ( isFocused )
578 bgState = CBB_FOCUSED;
579 else
580 bgState = CBB_NORMAL;
581 }
a340b80d
VZ
582 }
583
c905c0d6
VZ
584 //
585 // Draw parent's background, if necessary
586 RECT* rUseForTb = NULL;
a340b80d 587
c905c0d6
VZ
588 if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) )
589 rUseForTb = &rFull;
590 else if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
591 rUseForTb = &rButton;
a340b80d 592
c905c0d6
VZ
593 if ( rUseForTb )
594 theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb );
a340b80d 595
c905c0d6
VZ
596 //
597 // Draw the control background (including the border)
598 if ( m_widthCustomBorder > 0 )
599 {
600 theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL );
601 }
602 else
603 {
604 // No border. We can't use theme, since it cannot be relied on
605 // to deliver borderless drawing, even with DrawThemeBackgroundEx.
606 dc.SetBrush(bgCol);
607 dc.SetPen(bgCol);
608 dc.DrawRectangle(borderRect);
609 }
a340b80d 610
c905c0d6
VZ
611 //
612 // Draw the drop-button
613 if ( !isNonStdButton )
614 {
615 drawButFlags = Button_BitmapOnly;
a340b80d 616
c905c0d6
VZ
617 int butPart = CP_DROPDOWNBUTTON;
618
619 if ( useVistaComboBox )
620 {
621 if ( drawFullButton )
622 {
623 // We need to alter the button style slightly before
624 // drawing the actual button (but it was good above
625 // when background etc was done).
626 if ( butState == CBXS_HOT || butState == CBXS_PRESSED )
627 butState = CBXS_NORMAL;
628 }
629
630 if ( m_btnSide == wxRIGHT )
631 butPart = CP_DROPDOWNBUTTONRIGHT;
632 else
633 butPart = CP_DROPDOWNBUTTONLEFT;
634
635 }
636 theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL );
637 }
638 else if ( useVistaComboBox &&
639 (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) )
640 {
641 // We'll do this, because DrawThemeParentBackground
642 // doesn't seem to be reliable on Vista.
643 drawButFlags |= Button_PaintBackground;
644 }
645 }
646 else
f100b1c3 647#endif
c905c0d6
VZ
648 {
649 // Windows 2000 and earlier
650 drawButFlags = Button_PaintBackground;
a340b80d 651
c905c0d6
VZ
652 dc.SetBrush(bgCol);
653 dc.SetPen(bgCol);
654 dc.DrawRectangle(borderRect);
93f7f8be 655 }
a340b80d 656
c905c0d6
VZ
657 // Button rendering (may only do the bitmap on button, depending on the flags)
658 DrawButton( dc, rectButton, drawButFlags );
a340b80d 659
c905c0d6 660 // Paint required portion of the custom image on the control
6d0ce565 661 if ( (!m_text || m_widthCustomPaint) )
a340b80d
VZ
662 {
663 wxASSERT( m_widthCustomPaint >= 0 );
664
665 // this is intentionally here to allow drawed rectangle's
666 // right edge to be hidden
667 if ( m_text )
c905c0d6 668 rectTextField.width = m_widthCustomPaint;
a340b80d
VZ
669
670 dc.SetFont( GetFont() );
671
c905c0d6 672 dc.SetClippingRegion(rectTextField);
6d0ce565 673 if ( m_popupInterface )
c905c0d6 674 m_popupInterface->PaintComboControl(dc,rectTextField);
6d0ce565 675 else
c905c0d6 676 wxComboPopup::DefaultPaintComboControl(this,dc,rectTextField);
a340b80d
VZ
677 }
678}
679
a57d600f 680void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
a340b80d 681{
1efad474
RR
682 int mx = event.m_x;
683 bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y);
a340b80d
VZ
684 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
685
a340b80d
VZ
686 if ( PreprocessMouseEvent(event,isOnButtonArea) )
687 return;
688
689 if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
690 {
691 // if no textctrl and no special double-click, then the entire control acts
692 // as a button
693 handlerFlags |= wxCC_MF_ON_BUTTON;
694 if ( HandleButtonMouseEvent(event,handlerFlags) )
695 return;
696 }
697 else
698 {
1efad474
RR
699 if ( isOnButtonArea || HasCapture() ||
700 (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) )
a340b80d 701 {
1efad474
RR
702 handlerFlags |= wxCC_MF_ON_CLICK_AREA;
703
a340b80d
VZ
704 if ( HandleButtonMouseEvent(event,handlerFlags) )
705 return;
706 }
707 else if ( m_btnState )
708 {
709 // otherwise need to clear the hover status
710 m_btnState = 0;
711 RefreshRect(m_btnArea);
712 }
713 }
714
715 //
716 // This will handle left_down and left_dclick events outside button in a Windows-like manner.
717 // See header file for further information on this method.
718 HandleNormalMouseEvent(event);
719
720}
721
30be036c 722#if wxUSE_COMBOCTRL_POPUP_ANIMATION
974a12f8
RR
723static wxUint32 GetUserPreferencesMask()
724{
725 static wxUint32 userPreferencesMask = 0;
726 static bool valueSet = false;
727
728 if ( valueSet )
729 return userPreferencesMask;
730
53a64063
JS
731 wxRegKey* pKey = NULL;
732 wxRegKey key1(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Control Panel"));
733 wxRegKey key2(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Windows\\Control Panel"));
734 wxRegKey key3(wxRegKey::HKCU, wxT("Control Panel\\Desktop"));
735
736 if ( key1.Exists() )
737 pKey = &key1;
738 else if ( key2.Exists() )
739 pKey = &key2;
740 else if ( key3.Exists() )
741 pKey = &key3;
742
743 if ( pKey && pKey->Open(wxRegKey::Read) )
974a12f8
RR
744 {
745 wxMemoryBuffer buf;
53a64063
JS
746 if ( pKey->HasValue(wxT("UserPreferencesMask")) &&
747 pKey->QueryValue(wxT("UserPreferencesMask"), buf) )
974a12f8
RR
748 {
749 if ( buf.GetDataLen() >= 4 )
750 {
53a64063
JS
751 wxUint32* p = (wxUint32*) buf.GetData();
752 userPreferencesMask = *p;
974a12f8
RR
753 }
754 }
755 }
756
757 valueSet = true;
758
759 return userPreferencesMask;
760}
761#endif
762
30be036c
RR
763#if wxUSE_COMBOCTRL_POPUP_ANIMATION
764void wxComboCtrl::OnTimerEvent( wxTimerEvent& WXUNUSED(event) )
974a12f8 765{
30be036c 766 bool stopTimer = false;
974a12f8 767
30be036c 768 wxWindow* popup = GetPopupControl()->GetControl();
974a12f8 769
30be036c
RR
770 // Popup was hidden before it was fully shown?
771 if ( IsPopupWindowState(Hidden) )
772 {
773 stopTimer = true;
774 }
775 else
776 {
777 wxLongLong t = ::wxGetLocalTimeMillis();
778 const wxRect& rect = m_animRect;
974a12f8 779 wxWindow* win = GetPopupWindow();
974a12f8 780
30be036c
RR
781 int pos = (int) (t-m_animStart).GetLo();
782 if ( pos < COMBOBOX_ANIMATION_DURATION )
974a12f8 783 {
30be036c
RR
784 int height = rect.height;
785 //int h0 = rect.height;
786 int h = (((pos*256)/COMBOBOX_ANIMATION_DURATION)*height)/256;
787 int y = (height - h);
974a12f8
RR
788 if ( y < 0 )
789 y = 0;
790
30be036c 791 if ( m_animFlags & ShowAbove )
974a12f8 792 {
30be036c 793 win->SetSize( rect.x, rect.y + height - h, rect.width, h );
974a12f8
RR
794 }
795 else
796 {
797 popup->Move( 0, -y );
798 win->SetSize( rect.x, rect.y, rect.width, h );
799 }
974a12f8 800 }
30be036c
RR
801 else
802 {
803 stopTimer = true;
804 }
805 }
974a12f8 806
30be036c
RR
807 if ( stopTimer )
808 {
974a12f8 809 popup->Move( 0, 0 );
30be036c
RR
810 m_animTimer.Stop();
811 DoShowPopup( m_animRect, m_animFlags );
974a12f8 812 }
30be036c 813}
974a12f8
RR
814#endif
815
30be036c
RR
816#if wxUSE_COMBOCTRL_POPUP_ANIMATION
817bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
818{
819 if ( GetUserPreferencesMask() & wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM )
820 {
821 m_animStart = ::wxGetLocalTimeMillis();
822 m_animRect = rect;
823 m_animFlags = flags;
824
825 wxWindow* win = GetPopupWindow();
826 win->SetSize( rect.x, rect.y, rect.width, 0 );
827 win->Show();
828
829 m_animTimer.SetOwner( this, wxID_ANY );
830 m_animTimer.Start( COMBOBOX_ANIMATION_RESOLUTION, wxTIMER_CONTINUOUS );
831
832 OnTimerEvent(*((wxTimerEvent*)NULL)); // Event is never used, so we can give NULL
833
834 return false;
835 }
836
974a12f8
RR
837 return true;
838}
30be036c 839#endif
974a12f8 840
a57d600f 841wxCoord wxComboCtrl::GetNativeTextIndent() const
a340b80d 842{
f100b1c3 843#if wxUSE_UXTHEME
a340b80d
VZ
844 if ( wxUxThemeEngine::GetIfActive() )
845 return NATIVE_TEXT_INDENT_XP;
f100b1c3 846#endif
a340b80d
VZ
847 return NATIVE_TEXT_INDENT_CLASSIC;
848}
849
b445b6a7
VZ
850bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
851{
9e68717c 852 const bool isPopupShown = IsPopupShown();
b445b6a7 853
9e68717c 854 switch ( event.GetKeyCode() )
b445b6a7 855 {
9e68717c
VZ
856 case WXK_F4:
857 // F4 toggles the popup in the native comboboxes, so emulate them
858 if ( !event.AltDown() )
859 return true;
860 break;
861
862 case WXK_ESCAPE:
863 if ( isPopupShown )
864 return true;
865 break;
866
867 case WXK_DOWN:
868 case WXK_UP:
869 // On XP or with writable combo in Classic, arrows don't open the
870 // popup but Alt-arrow does
871 if ( event.AltDown() ||
872 ( !isPopupShown &&
f100b1c3
JS
873 HasFlag(wxCB_READONLY)
874#if wxUSE_UXTHEME
875 && !wxUxThemeEngine::GetIfActive()
876#endif
9e68717c
VZ
877 ) )
878 {
879 return true;
880 }
881 break;
b445b6a7
VZ
882 }
883
884 return false;
885}
a340b80d 886
a57d600f 887#endif // wxUSE_COMBOCTRL