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