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