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