1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/window.cpp
3 // Purpose: wxWindowMSW
4 // Author: Julian Smart
5 // Modified by: VZ on 13.05.99: no more Default(), MSWOnXXX() reorganisation
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ===========================================================================
12 // declarations
13 // ===========================================================================
14
15 // ---------------------------------------------------------------------------
16 // headers
17 // ---------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22
23 #include "wx/window.h"
24
25 #ifndef WX_PRECOMP
26 #include "wx/msw/wrapwin.h"
27 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
28 #include "wx/msw/missing.h"
29 #include "wx/accel.h"
30 #include "wx/menu.h"
31 #include "wx/dc.h"
32 #include "wx/dcclient.h"
33 #include "wx/dcmemory.h"
34 #include "wx/dialog.h"
35 #include "wx/utils.h"
36 #include "wx/app.h"
37 #include "wx/layout.h"
38 #include "wx/dialog.h"
39 #include "wx/frame.h"
40 #include "wx/listbox.h"
41 #include "wx/button.h"
42 #include "wx/msgdlg.h"
43 #include "wx/settings.h"
44 #include "wx/statbox.h"
45 #include "wx/sizer.h"
46 #include "wx/intl.h"
47 #include "wx/log.h"
48 #include "wx/textctrl.h"
49 #include "wx/menuitem.h"
50 #include "wx/module.h"
51 #include "wx/vector.h"
52 #endif
53
54 #if wxUSE_OWNER_DRAWN && !defined(__WXUNIVERSAL__)
55 #include "wx/ownerdrw.h"
56 #endif
57
58 #include "wx/evtloop.h"
59 #include "wx/hashmap.h"
60 #include "wx/popupwin.h"
61 #include "wx/power.h"
62 #include "wx/scopeguard.h"
63 #include "wx/sysopt.h"
64
65 #if wxUSE_DRAG_AND_DROP
66 #include "wx/dnd.h"
67 #endif
68
69 #if wxUSE_ACCESSIBILITY
70 #include "wx/access.h"
71 #include <ole2.h>
72 #include <oleacc.h>
73 #ifndef OBJID_CLIENT
74 #define OBJID_CLIENT 0xFFFFFFFC
75 #endif
76 #endif
77
78 #include "wx/msw/private.h"
79 #include "wx/msw/private/keyboard.h"
80 #include "wx/msw/private/paint.h"
81 #include "wx/msw/private/winstyle.h"
82 #include "wx/msw/dcclient.h"
83 #include "wx/msw/seh.h"
84 #include "wx/private/textmeasure.h"
85
86 #if wxUSE_TOOLTIPS
87 #include "wx/tooltip.h"
88 #endif
89
90 #if wxUSE_CARET
91 #include "wx/caret.h"
92 #endif // wxUSE_CARET
93
94 #if wxUSE_RADIOBOX
95 #include "wx/radiobox.h"
96 #endif // wxUSE_RADIOBOX
97
98 #if wxUSE_SPINCTRL
99 #include "wx/spinctrl.h"
100 #endif // wxUSE_SPINCTRL
101
102 #include "wx/notebook.h"
103 #include "wx/listctrl.h"
104 #include "wx/dynlib.h"
105 #include "wx/display.h"
106
107 #include <string.h>
108
109 #include <shellapi.h>
110 #include <mmsystem.h>
111
112 #include <windowsx.h>
113
114 #if wxUSE_UXTHEME
115 #include "wx/msw/uxtheme.h"
116 #endif
117
118 #if wxUSE_DYNLIB_CLASS
119 #define HAVE_TRACKMOUSEEVENT
120 #endif // everything needed for TrackMouseEvent()
121
122 #ifndef MAPVK_VK_TO_CHAR
123 // Contrary to MS claims that this is present starting with Win2k, it is
124 // missing from the SDK released for Windows 5.2 build 3790, aka XP 64-bit
125 // and also from tdm32-gcc-5.1.0.
126 #define MAPVK_VK_TO_CHAR 2
127 #endif
128
129 // ---------------------------------------------------------------------------
130 // global variables
131 // ---------------------------------------------------------------------------
132
133 #if wxUSE_MENUS_NATIVE
134 extern wxMenu *wxCurrentPopupMenu;
135 #endif
136
137 #if wxUSE_POPUPWIN
138 extern wxPopupWindow* wxCurrentPopupWindow;
139 #endif // wxUSE_POPUPWIN
140
141 #if wxUSE_UXTHEME
142 // This is a hack used by the owner-drawn wxButton implementation to ensure
143 // that the brush used for erasing its background is correctly aligned with the
144 // control.
145 wxWindowMSW *wxWindowBeingErased = NULL;
146 #endif // wxUSE_UXTHEME
147
148 namespace
149 {
150
151 // true if we had already created the std colour map, used by
152 // wxGetStdColourMap() and wxWindow::OnSysColourChanged() (FIXME-MT)
153 bool gs_hasStdCmap = false;
154
155 // last mouse event information we need to filter out the duplicates
156 struct MouseEventInfoDummy
157 {
158 // mouse position (in screen coordinates)
159 wxPoint pos;
160
161 // last mouse event type
162 wxEventType type;
163 } gs_lastMouseEvent;
164
165 // hash containing the registered handlers for the custom messages
166 WX_DECLARE_HASH_MAP(int, wxWindow::MSWMessageHandler,
167 wxIntegerHash, wxIntegerEqual,
168 MSWMessageHandlers);
169
170 MSWMessageHandlers gs_messageHandlers;
171
172 // hash containing all our windows, it uses HWND keys and wxWindow* values
173 WX_DECLARE_HASH_MAP(HWND, wxWindow *,
174 wxPointerHash, wxPointerEqual,
175 WindowHandles);
176
177 WindowHandles gs_windowHandles;
178
179 #ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
180
181 // temporary override for WM_ERASEBKGND processing: we don't store this in
182 // wxWindow itself as we don't need it during most of the time so don't
183 // increase the size of all window objects unnecessarily
184 WX_DECLARE_HASH_MAP(wxWindow *, wxWindow *,
185 wxPointerHash, wxPointerEqual,
186 EraseBgHooks);
187
188 EraseBgHooks gs_eraseBgHooks;
189
190 #endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
191
192 // If this variable is strictly positive, EVT_CHAR_HOOK is not generated for
193 // Escape key presses as it can't be intercepted because it's needed by some
194 // currently shown window, e.g. IME entry.
195 //
196 // This is currently global as we allow using UI from the main thread only
197 // anyhow but could be replaced with a thread-specific value in the future if
198 // needed.
199 int gs_modalEntryWindowCount = 0;
200
201 // Indicates whether we are currently processing WM_CAPTURECHANGED message.
202 bool gs_insideCaptureChanged = false;
203
204 } // anonymous namespace
205
206 #ifdef WM_GESTURE
207
208 namespace
209 {
210
211 // Class used to dynamically load gestures related API functions.
212 class GestureFuncs
213 {
214 public:
215 // Must be called before using any other methods of this class (and they
216 // can't be used if this one returns false).
IsOk()217 static bool IsOk()
218 {
219 if ( !ms_gestureSymbolsLoaded )
220 {
221 ms_gestureSymbolsLoaded = true;
222 LoadGestureSymbols();
223 }
224
225 return ms_pfnGetGestureInfo &&
226 ms_pfnCloseGestureInfoHandle &&
227 ms_pfnSetGestureConfig;
228 }
229
230 typedef BOOL (WINAPI *GetGestureInfo_t)(HGESTUREINFO, PGESTUREINFO);
231
GetGestureInfo()232 static GetGestureInfo_t GetGestureInfo()
233 {
234 return ms_pfnGetGestureInfo;
235 }
236
237 typedef BOOL (WINAPI *CloseGestureInfoHandle_t)(HGESTUREINFO);
238
CloseGestureInfoHandle()239 static CloseGestureInfoHandle_t CloseGestureInfoHandle()
240 {
241 return ms_pfnCloseGestureInfoHandle;
242 }
243
244 typedef BOOL
245 (WINAPI *SetGestureConfig_t)(HWND, DWORD, UINT, PGESTURECONFIG, UINT);
246
SetGestureConfig()247 static SetGestureConfig_t SetGestureConfig()
248 {
249 return ms_pfnSetGestureConfig;
250 }
251
252 private:
LoadGestureSymbols()253 static void LoadGestureSymbols()
254 {
255 wxLoadedDLL dll(wxS("user32.dll"));
256
257 wxDL_INIT_FUNC(ms_pfn, GetGestureInfo, dll);
258 wxDL_INIT_FUNC(ms_pfn, CloseGestureInfoHandle, dll);
259 wxDL_INIT_FUNC(ms_pfn, SetGestureConfig, dll);
260 }
261
262 static GetGestureInfo_t ms_pfnGetGestureInfo;
263 static CloseGestureInfoHandle_t ms_pfnCloseGestureInfoHandle;
264 static SetGestureConfig_t ms_pfnSetGestureConfig;
265
266 static bool ms_gestureSymbolsLoaded;
267 };
268
269 GestureFuncs::GetGestureInfo_t
270 GestureFuncs::ms_pfnGetGestureInfo = NULL;
271 GestureFuncs::CloseGestureInfoHandle_t
272 GestureFuncs::ms_pfnCloseGestureInfoHandle = NULL;
273 GestureFuncs::SetGestureConfig_t
274 GestureFuncs::ms_pfnSetGestureConfig = NULL;
275
276 bool GestureFuncs::ms_gestureSymbolsLoaded = false;
277
278 } // anonymous namespace
279
280 #endif // WM_GESTURE
281
282 // ---------------------------------------------------------------------------
283 // private functions
284 // ---------------------------------------------------------------------------
285
286 // the window proc for all our windows
287 LRESULT WXDLLEXPORT APIENTRY
288 wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
289
290 #if wxDEBUG_LEVEL >= 2
291 const wxChar *wxGetMessageName(int message);
292
293 inline
wxTraceMSWMessage(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)294 void wxTraceMSWMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
295 {
296 // The casts to size_t allow to avoid having different code for Win32 and
297 // Win64 as size_t is of the right size in both cases, unlike int or long.
298 wxLogTrace("winmsg",
299 wxT("Processing %s(hWnd=%p, wParam=%zx, lParam=%zx)"),
300 wxGetMessageName(message),
301 hWnd,
302 static_cast<size_t>(wParam),
303 static_cast<size_t>(lParam));
304 }
305 #endif // wxDEBUG_LEVEL >= 2
306
307 void wxRemoveHandleAssociation(wxWindowMSW *win);
308 extern void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win);
309
310 // get the text metrics for the current font
311 static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win);
312
313 // wrapper around BringWindowToTop() API
wxBringWindowToTop(HWND hwnd)314 static inline void wxBringWindowToTop(HWND hwnd)
315 {
316 // raise top level parent to top of z order
317 if (!::SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE))
318 {
319 wxLogLastError(wxT("SetWindowPos"));
320 }
321 }
322
323 // ensure that all our parent windows have WS_EX_CONTROLPARENT style
EnsureParentHasControlParentStyle(wxWindow * parent)324 static void EnsureParentHasControlParentStyle(wxWindow *parent)
325 {
326 /*
327 If we have WS_EX_CONTROLPARENT flag we absolutely *must* set it for our
328 parent as well as otherwise several Win32 functions using
329 GetNextDlgTabItem() to iterate over all controls such as
330 IsDialogMessage() or DefDlgProc() would enter an infinite loop: indeed,
331 all of them iterate over all the controls starting from the currently
332 focused one and stop iterating when they get back to the focus but
333 unless all parents have WS_EX_CONTROLPARENT bit set, they would never
334 get back to the initial (focused) window: as we do have this style,
335 GetNextDlgTabItem() will leave this window and continue in its parent,
336 but if the parent doesn't have it, it wouldn't recurse inside it later
337 on and so wouldn't have a chance of getting back to this window either.
338 */
339 while ( parent && !parent->IsTopLevel() )
340 {
341 // force the parent to have this style
342 wxMSWWinExStyleUpdater(GetHwndOf(parent)).TurnOn(WS_EX_CONTROLPARENT);
343
344 parent = parent->GetParent();
345 }
346 }
347
348 // GetCursorPos can return an error, so use this function
349 // instead.
350 // Error observed when using Remote Desktop to connect to XP.
wxGetCursorPosMSW(POINT * pt)351 void wxGetCursorPosMSW(POINT* pt)
352 {
353 if (!GetCursorPos(pt))
354 {
355 DWORD pos = GetMessagePos();
356 // the coordinates may be negative in multi-monitor systems
357 pt->x = GET_X_LPARAM(pos);
358 pt->y = GET_Y_LPARAM(pos);
359 }
360 }
361
362 // ---------------------------------------------------------------------------
363 // event tables
364 // ---------------------------------------------------------------------------
365
366 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
367 // method
368 #ifdef __WXUNIVERSAL__
369 wxIMPLEMENT_ABSTRACT_CLASS(wxWindowMSW, wxWindowBase);
370 #endif // __WXUNIVERSAL__
371
wxBEGIN_EVENT_TABLE(wxWindowMSW,wxWindowBase)372 wxBEGIN_EVENT_TABLE(wxWindowMSW, wxWindowBase)
373 EVT_SYS_COLOUR_CHANGED(wxWindowMSW::OnSysColourChanged)
374 wxEND_EVENT_TABLE()
375
376 // ===========================================================================
377 // implementation
378 // ===========================================================================
379
380 // ---------------------------------------------------------------------------
381 // wxWindow utility functions
382 // ---------------------------------------------------------------------------
383
384 // Find an item given the MS Windows id
385 wxWindow *wxWindowMSW::FindItem(long id, WXHWND hWnd) const
386 {
387 // First check for the control itself and its Windows-level children which
388 // are mapped to the same wxWindow at wx level.
389 wxWindow *wnd = MSWFindItem(id, hWnd);
390 if ( wnd )
391 return wnd;
392
393 // Then check wx level children.
394 wxWindowList::compatibility_iterator current = GetChildren().GetFirst();
395 while (current)
396 {
397 wxWindow *childWin = current->GetData();
398
399 wnd = childWin->FindItem(id, hWnd);
400 if ( wnd )
401 return wnd;
402
403 current = current->GetNext();
404 }
405
406 return NULL;
407 }
408
409 // Find an item given the MS Windows handle
FindItemByHWND(WXHWND hWnd,bool controlOnly) const410 wxWindow *wxWindowMSW::FindItemByHWND(WXHWND hWnd, bool controlOnly) const
411 {
412 wxWindowList::compatibility_iterator current = GetChildren().GetFirst();
413 while (current)
414 {
415 wxWindow *parent = current->GetData();
416
417 // Do a recursive search.
418 wxWindow *wnd = parent->FindItemByHWND(hWnd);
419 if ( wnd )
420 return wnd;
421
422 if ( !controlOnly
423 #if wxUSE_CONTROLS
424 || wxDynamicCast(parent, wxControl)
425 #endif // wxUSE_CONTROLS
426 )
427 {
428 wxWindow *item = current->GetData();
429 if ( item->GetHWND() == hWnd )
430 return item;
431 else
432 {
433 if ( item->ContainsHWND(hWnd) )
434 return item;
435 }
436 }
437
438 current = current->GetNext();
439 }
440 return NULL;
441 }
442
443 // Default command handler
MSWCommand(WXUINT WXUNUSED (param),WXWORD WXUNUSED (id))444 bool wxWindowMSW::MSWCommand(WXUINT WXUNUSED(param), WXWORD WXUNUSED(id))
445 {
446 return false;
447 }
448
449 // ----------------------------------------------------------------------------
450 // constructors and such
451 // ----------------------------------------------------------------------------
452
Init()453 void wxWindowMSW::Init()
454 {
455 // MSW specific
456 m_oldWndProc = NULL;
457 m_mouseInWindow = false;
458 m_lastKeydownProcessed = false;
459
460 m_hWnd = 0;
461
462 m_xThumbSize = 0;
463 m_yThumbSize = 0;
464
465 #if wxUSE_DEFERRED_SIZING
466 m_hDWP = 0;
467 m_pendingPosition = wxDefaultPosition;
468 m_pendingSize = wxDefaultSize;
469 #endif // wxUSE_DEFERRED_SIZING
470
471 }
472
473 // Destructor
~wxWindowMSW()474 wxWindowMSW::~wxWindowMSW()
475 {
476 SendDestroyEvent();
477
478 // VS: destroy children first and _then_ detach *this from its parent.
479 // If we did it the other way around, children wouldn't be able
480 // find their parent frame.
481 DestroyChildren();
482
483 if ( m_hWnd )
484 {
485 // VZ: test temp removed to understand what really happens here
486 //if (::IsWindow(GetHwnd()))
487 {
488 if ( !::DestroyWindow(GetHwnd()) )
489 {
490 wxLogLastError(wxT("DestroyWindow"));
491 }
492 }
493
494 // remove hWnd <-> wxWindow association
495 wxRemoveHandleAssociation(this);
496 }
497
498 }
499
GetMSWClassName(long style)500 const wxChar *wxWindowMSW::GetMSWClassName(long style)
501 {
502 return wxApp::GetRegisteredClassName
503 (
504 wxT("wxWindow"),
505 COLOR_BTNFACE,
506 0, // no special extra style
507 (style & wxFULL_REPAINT_ON_RESIZE) ? wxApp::RegClass_Default
508 : wxApp::RegClass_ReturnNR
509 );
510 }
511
512 // real construction (Init() must have been called before!)
CreateUsingMSWClass(const wxChar * classname,wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)513 bool wxWindowMSW::CreateUsingMSWClass(const wxChar* classname,
514 wxWindow *parent,
515 wxWindowID id,
516 const wxPoint& pos,
517 const wxSize& size,
518 long style,
519 const wxString& name)
520 {
521 wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") );
522
523 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
524 return false;
525
526 parent->AddChild(this);
527
528 WXDWORD exstyle;
529 DWORD msflags = MSWGetCreateWindowFlags(&exstyle);
530
531 #ifdef __WXUNIVERSAL__
532 // no borders, we draw them ourselves
533 exstyle &= ~(WS_EX_DLGMODALFRAME |
534 WS_EX_STATICEDGE |
535 WS_EX_CLIENTEDGE |
536 WS_EX_WINDOWEDGE);
537 msflags &= ~WS_BORDER;
538 #endif // wxUniversal
539
540 if ( IsShown() )
541 {
542 msflags |= WS_VISIBLE;
543 }
544
545 if ( !MSWCreate(classname, NULL, pos, size, msflags, exstyle) )
546 return false;
547
548 InheritAttributes();
549
550 return true;
551 }
552
SetId(wxWindowID winid)553 void wxWindowMSW::SetId(wxWindowID winid)
554 {
555 wxWindowBase::SetId(winid);
556
557 // Also update the ID used at the Windows level to avoid nasty surprises
558 // when we can't find the control when handling messages for it after
559 // changing its ID because Windows still uses the old one.
560 if ( GetHwnd() )
561 {
562 ::SetLastError(0);
563
564 if ( !::SetWindowLong(GetHwnd(), GWL_ID, winid) )
565 {
566 const DWORD err = ::GetLastError();
567 if ( err )
568 wxLogApiError(wxT("SetWindowLong(GWL_ID)"), err);
569 }
570 }
571 }
572
573 // ---------------------------------------------------------------------------
574 // basic operations
575 // ---------------------------------------------------------------------------
576
SetFocus()577 void wxWindowMSW::SetFocus()
578 {
579 HWND hWnd = (HWND)MSWGetFocusHWND();
580 wxCHECK_RET( hWnd, wxT("can't set focus to invalid window") );
581
582 ::SetLastError(0);
583
584 if ( !::SetFocus(hWnd) )
585 {
586 // was there really an error?
587 DWORD dwRes = ::GetLastError();
588 if ( dwRes )
589 {
590 HWND hwndFocus = ::GetFocus();
591 if ( hwndFocus != hWnd )
592 {
593 wxLogApiError(wxT("SetFocus"), dwRes);
594 }
595 }
596 }
597 }
598
SetFocusFromKbd()599 void wxWindowMSW::SetFocusFromKbd()
600 {
601 HWND hWnd = (HWND)MSWGetFocusHWND();
602
603 // when the focus is given to the control with DLGC_HASSETSEL style from
604 // keyboard its contents should be entirely selected: this is what
605 // ::IsDialogMessage() does and so we should do it as well to provide the
606 // same LNF as the native programs
607 if ( ::SendMessage(hWnd, WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL )
608 {
609 ::SendMessage(hWnd, EM_SETSEL, 0, -1);
610 }
611
612 // do this after (maybe) setting the selection as like this when
613 // wxEVT_SET_FOCUS handler is called, the selection would have been already
614 // set correctly -- this may be important
615 wxWindowBase::SetFocusFromKbd();
616 }
617
618 // Get the window with the focus
DoFindFocus()619 wxWindow *wxWindowBase::DoFindFocus()
620 {
621 HWND hWnd = ::GetFocus();
622 if ( hWnd )
623 {
624 return wxGetWindowFromHWND((WXHWND)hWnd);
625 }
626
627 return NULL;
628 }
629
DoEnable(bool enable)630 void wxWindowMSW::DoEnable( bool enable )
631 {
632 MSWEnableHWND(GetHwnd(), enable);
633 }
634
MSWEnableHWND(WXHWND hWnd,bool enable)635 bool wxWindowMSW::MSWEnableHWND(WXHWND hWnd, bool enable)
636 {
637 if ( !hWnd )
638 return false;
639
640 // If disabling focused control, we move focus to the next one, as if the
641 // user pressed Tab. That's because we can't keep focus on a disabled
642 // control, Tab-navigation would stop working then.
643 if ( !enable && ::GetFocus() == hWnd )
644 Navigate();
645
646 return ::EnableWindow(hWnd, (BOOL)enable) != 0;
647 }
648
Show(bool show)649 bool wxWindowMSW::Show(bool show)
650 {
651 if ( !wxWindowBase::Show(show) )
652 return false;
653
654 HWND hWnd = GetHwnd();
655
656 // we could be called before the underlying window is created (this is
657 // actually useful to prevent it from being initially shown), e.g.
658 //
659 // wxFoo *foo = new wxFoo;
660 // foo->Hide();
661 // foo->Create(parent, ...);
662 //
663 // should work without errors
664 if ( hWnd )
665 {
666 ::ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
667 }
668
669 if ( IsFrozen() )
670 {
671 // DoFreeze/DoThaw don't do anything if the window is not shown, so
672 // we have to call them from here now
673 if ( show )
674 DoFreeze();
675 else
676 DoThaw();
677 }
678
679 return true;
680 }
681
682 bool
MSWShowWithEffect(bool show,wxShowEffect effect,unsigned timeout)683 wxWindowMSW::MSWShowWithEffect(bool show,
684 wxShowEffect effect,
685 unsigned timeout)
686 {
687 #if wxUSE_DYNLIB_CLASS
688 if ( effect == wxSHOW_EFFECT_NONE ||
689 (GetParent() && !GetParent()->IsShownOnScreen()) )
690 return Show(show);
691
692 if ( !wxWindowBase::Show(show) )
693 return false;
694
695 // Show() has a side effect of sending a WM_SIZE to the window, which helps
696 // ensuring that it's laid out correctly, but AnimateWindow() doesn't do
697 // this so send the event ourselves
698 SendSizeEvent();
699
700 // prepare to use AnimateWindow()
701
702 if ( !timeout )
703 timeout = 200; // this is the default animation timeout, per MSDN
704
705 DWORD dwFlags = show ? 0 : AW_HIDE;
706
707 switch ( effect )
708 {
709 case wxSHOW_EFFECT_ROLL_TO_LEFT:
710 dwFlags |= AW_HOR_NEGATIVE;
711 break;
712
713 case wxSHOW_EFFECT_ROLL_TO_RIGHT:
714 dwFlags |= AW_HOR_POSITIVE;
715 break;
716
717 case wxSHOW_EFFECT_ROLL_TO_TOP:
718 dwFlags |= AW_VER_NEGATIVE;
719 break;
720
721 case wxSHOW_EFFECT_ROLL_TO_BOTTOM:
722 dwFlags |= AW_VER_POSITIVE;
723 break;
724
725 case wxSHOW_EFFECT_SLIDE_TO_LEFT:
726 dwFlags |= AW_SLIDE | AW_HOR_NEGATIVE;
727 break;
728
729 case wxSHOW_EFFECT_SLIDE_TO_RIGHT:
730 dwFlags |= AW_SLIDE | AW_HOR_POSITIVE;
731 break;
732
733 case wxSHOW_EFFECT_SLIDE_TO_TOP:
734 dwFlags |= AW_SLIDE | AW_VER_NEGATIVE;
735 break;
736
737 case wxSHOW_EFFECT_SLIDE_TO_BOTTOM:
738 dwFlags |= AW_SLIDE | AW_VER_POSITIVE;
739 break;
740
741 case wxSHOW_EFFECT_BLEND:
742 dwFlags |= AW_BLEND;
743 break;
744
745 case wxSHOW_EFFECT_EXPAND:
746 dwFlags |= AW_CENTER;
747 break;
748
749
750 case wxSHOW_EFFECT_MAX:
751 wxFAIL_MSG( wxT("invalid window show effect") );
752 return false;
753
754 default:
755 wxFAIL_MSG( wxT("unknown window show effect") );
756 return false;
757 }
758
759 if ( !::AnimateWindow(GetHwnd(), timeout, dwFlags) )
760 {
761 wxLogLastError(wxT("AnimateWindow"));
762
763 return false;
764 }
765
766 return true;
767 #else // wxUSE_DYNLIB_CLASS
768 return Show(show);
769 #endif
770 }
771
772 // Raise the window to the top of the Z order
Raise()773 void wxWindowMSW::Raise()
774 {
775 wxBringWindowToTop(GetHwnd());
776 }
777
778 // Lower the window to the bottom of the Z order
Lower()779 void wxWindowMSW::Lower()
780 {
781 ::SetWindowPos(GetHwnd(), HWND_BOTTOM, 0, 0, 0, 0,
782 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
783 }
784
DoCaptureMouse()785 void wxWindowMSW::DoCaptureMouse()
786 {
787 HWND hWnd = GetHwnd();
788 if ( hWnd )
789 {
790 ::SetCapture(hWnd);
791 }
792 }
793
DoReleaseMouse()794 void wxWindowMSW::DoReleaseMouse()
795 {
796 if ( !::ReleaseCapture() )
797 {
798 wxLogLastError(wxT("ReleaseCapture"));
799 }
800 }
801
GetCapture()802 /* static */ wxWindow *wxWindowBase::GetCapture()
803 {
804 // When we receive WM_CAPTURECHANGED message, ::GetCapture() still returns
805 // the HWND that is losing the mouse capture. But as we must not release
806 // the capture for it (it's going to happen anyhow), pretend that there is
807 // no capture any more.
808 if ( gs_insideCaptureChanged )
809 return NULL;
810
811 HWND hwnd = ::GetCapture();
812 return hwnd ? wxFindWinFromHandle(hwnd) : NULL;
813 }
814
SetFont(const wxFont & font)815 bool wxWindowMSW::SetFont(const wxFont& font)
816 {
817 if ( !wxWindowBase::SetFont(font) )
818 {
819 // nothing to do
820 return false;
821 }
822
823 HWND hWnd = GetHwnd();
824 if ( hWnd != 0 )
825 {
826 // note the use of GetFont() instead of m_font: our own font could have
827 // just been reset and in this case we need to change the font used by
828 // the native window to the default for this class, i.e. exactly what
829 // GetFont() returns
830 wxSetWindowFont(hWnd, GetFont());
831 }
832
833 return true;
834 }
835
SetCursor(const wxCursor & cursor)836 bool wxWindowMSW::SetCursor(const wxCursor& cursor)
837 {
838 if ( !wxWindowBase::SetCursor(cursor) )
839 {
840 // no change
841 return false;
842 }
843
844 // don't "overwrite" busy cursor
845 if ( wxIsBusy() )
846 return true;
847
848 if ( m_cursor.IsOk() )
849 {
850 // normally we should change the cursor only if it's over this window
851 // but we should do it always if we capture the mouse currently
852 bool set = HasCapture();
853 if ( !set )
854 {
855 HWND hWnd = GetHwnd();
856
857 POINT point;
858 ::wxGetCursorPosMSW(&point);
859
860 RECT rect = wxGetWindowRect(hWnd);
861
862 set = ::PtInRect(&rect, point) != 0;
863 }
864
865 if ( set )
866 {
867 ::SetCursor(GetHcursorOf(m_cursor));
868 }
869 //else: will be set later when the mouse enters this window
870 }
871 else // Invalid cursor: this means reset to the default one.
872 {
873 // To revert to the correct cursor we need to find the window currently
874 // under the cursor and ask it to set its cursor itself as only it
875 // knows what it is.
876 POINT pt;
877 wxGetCursorPosMSW(&pt);
878
879 const wxWindowMSW* win = wxFindWindowAtPoint(wxPoint(pt.x, pt.y));
880 if ( !win )
881 win = this;
882
883 ::SendMessage(GetHwndOf(win), WM_SETCURSOR,
884 (WPARAM)GetHwndOf(win),
885 MAKELPARAM(HTCLIENT, WM_MOUSEMOVE));
886 }
887
888 return true;
889 }
890
WarpPointer(int x,int y)891 void wxWindowMSW::WarpPointer(int x, int y)
892 {
893 ClientToScreen(&x, &y);
894
895 if ( !::SetCursorPos(x, y) )
896 {
897 wxLogLastError(wxT("SetCursorPos"));
898 }
899 }
900
EnableTouchEvents(int eventsMask)901 bool wxWindowMSW::EnableTouchEvents(int eventsMask)
902 {
903 #ifdef WM_GESTURE
904 if ( GestureFuncs::IsOk() )
905 {
906 // Static struct used when we need to use just a single configuration.
907 GESTURECONFIG config = {0, 0, 0};
908
909 GESTURECONFIG* ptrConfigs = &config;
910 UINT numConfigs = 1;
911
912 // This is used only if we need to allocate the configurations
913 // dynamically.
914 wxVector<GESTURECONFIG> configs;
915
916 // There are two simple cases: enabling or disabling all gestures.
917 if ( eventsMask == wxTOUCH_NONE )
918 {
919 config.dwBlock = GC_ALLGESTURES;
920 }
921 else if ( eventsMask == wxTOUCH_ALL_GESTURES )
922 {
923 config.dwWant = GC_ALLGESTURES;
924 }
925 else // Need to enable the individual gestures
926 {
927 int wantedPan = 0;
928 switch ( eventsMask & wxTOUCH_PAN_GESTURES )
929 {
930 case wxTOUCH_VERTICAL_PAN_GESTURE:
931 wantedPan = GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
932 break;
933
934 case wxTOUCH_HORIZONTAL_PAN_GESTURE:
935 wantedPan = GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
936 break;
937
938 case wxTOUCH_PAN_GESTURES:
939 wantedPan = GC_PAN;
940 break;
941
942 case 0:
943 // This is the only other possibility and wantedPan is
944 // already initialized to 0 anyhow, so don't do anything,
945 // just list it for completeness.
946 break;
947 }
948
949 if ( wantedPan )
950 {
951 eventsMask &= ~wxTOUCH_PAN_GESTURES;
952
953 config.dwID = GID_PAN;
954 config.dwWant = wantedPan;
955 configs.push_back(config);
956 }
957
958 if ( eventsMask & wxTOUCH_ZOOM_GESTURE )
959 {
960 eventsMask &= ~wxTOUCH_ZOOM_GESTURE;
961
962 config.dwID = GID_ZOOM;
963 config.dwWant = GC_ZOOM;
964 configs.push_back(config);
965 }
966
967 if ( eventsMask & wxTOUCH_ROTATE_GESTURE )
968 {
969 eventsMask &= ~wxTOUCH_ROTATE_GESTURE;
970
971 config.dwID = GID_ROTATE;
972 config.dwWant = GC_ROTATE;
973 configs.push_back(config);
974 }
975
976 if ( eventsMask & wxTOUCH_PRESS_GESTURES )
977 {
978 eventsMask &= ~wxTOUCH_PRESS_GESTURES;
979
980 config.dwID = GID_TWOFINGERTAP;
981 config.dwWant = GC_TWOFINGERTAP;
982 configs.push_back(config);
983
984 config.dwID = GID_PRESSANDTAP;
985 config.dwWant = GC_PRESSANDTAP;
986 configs.push_back(config);
987 }
988
989 // As we clear all the known bits if they're set in the code above,
990 // there should be nothing left.
991 wxCHECK_MSG( eventsMask == 0, false,
992 wxS("Unknown touch event mask bit specified") );
993
994 ptrConfigs = &configs[0];
995 }
996
997 if ( !GestureFuncs::SetGestureConfig()
998 (
999 m_hWnd,
1000 wxRESERVED_PARAM,
1001 numConfigs, // Number of gesture configurations.
1002 ptrConfigs, // Pointer to the first one.
1003 sizeof(GESTURECONFIG) // Size of each configuration.
1004 )
1005 )
1006 {
1007 wxLogLastError("SetGestureConfig");
1008 return false;
1009 }
1010
1011 return true;
1012 }
1013 #endif // WM_GESTURE
1014
1015 return wxWindowBase::EnableTouchEvents(eventsMask);
1016 }
1017
MSWUpdateUIState(int action,int state)1018 void wxWindowMSW::MSWUpdateUIState(int action, int state)
1019 {
1020 // we send WM_CHANGEUISTATE so if nothing needs changing then the system
1021 // won't send WM_UPDATEUISTATE
1022 ::SendMessage(GetHwnd(), WM_CHANGEUISTATE, MAKEWPARAM(action, state), 0);
1023 }
1024
1025 // ---------------------------------------------------------------------------
1026 // scrolling stuff
1027 // ---------------------------------------------------------------------------
1028
1029 namespace
1030 {
1031
GetScrollPosition(HWND hWnd,int wOrient)1032 inline int GetScrollPosition(HWND hWnd, int wOrient)
1033 {
1034 WinStruct<SCROLLINFO> scrollInfo;
1035 scrollInfo.cbSize = sizeof(SCROLLINFO);
1036 scrollInfo.fMask = SIF_POS;
1037 ::GetScrollInfo(hWnd, wOrient, &scrollInfo );
1038
1039 return scrollInfo.nPos;
1040 }
1041
WXOrientToSB(int orient)1042 inline UINT WXOrientToSB(int orient)
1043 {
1044 return orient == wxHORIZONTAL ? SB_HORZ : SB_VERT;
1045 }
1046
1047 } // anonymous namespace
1048
GetScrollPos(int orient) const1049 int wxWindowMSW::GetScrollPos(int orient) const
1050 {
1051 HWND hWnd = GetHwnd();
1052 wxCHECK_MSG( hWnd, 0, wxT("no HWND in GetScrollPos") );
1053
1054 return GetScrollPosition(hWnd, WXOrientToSB(orient));
1055 }
1056
1057 // This now returns the whole range, not just the number
1058 // of positions that we can scroll.
GetScrollRange(int orient) const1059 int wxWindowMSW::GetScrollRange(int orient) const
1060 {
1061 int maxPos;
1062 HWND hWnd = GetHwnd();
1063 if ( !hWnd )
1064 return 0;
1065 WinStruct<SCROLLINFO> scrollInfo;
1066 scrollInfo.fMask = SIF_RANGE;
1067 if ( !::GetScrollInfo(hWnd, WXOrientToSB(orient), &scrollInfo) )
1068 {
1069 // Most of the time this is not really an error, since the return
1070 // value can also be zero when there is no scrollbar yet.
1071 // wxLogLastError(wxT("GetScrollInfo"));
1072 }
1073 maxPos = scrollInfo.nMax;
1074
1075 // undo "range - 1" done in SetScrollbar()
1076 return maxPos + 1;
1077 }
1078
GetScrollThumb(int orient) const1079 int wxWindowMSW::GetScrollThumb(int orient) const
1080 {
1081 return orient == wxHORIZONTAL ? m_xThumbSize : m_yThumbSize;
1082 }
1083
SetScrollPos(int orient,int pos,bool refresh)1084 void wxWindowMSW::SetScrollPos(int orient, int pos, bool refresh)
1085 {
1086 HWND hWnd = GetHwnd();
1087 wxCHECK_RET( hWnd, wxT("SetScrollPos: no HWND") );
1088
1089 WinStruct<SCROLLINFO> info;
1090 info.nPage = 0;
1091 info.nMin = 0;
1092 info.nPos = pos;
1093 info.fMask = SIF_POS;
1094 if ( HasFlag(wxALWAYS_SHOW_SB) )
1095 {
1096 // disable scrollbar instead of removing it then
1097 info.fMask |= SIF_DISABLENOSCROLL;
1098 }
1099
1100 ::SetScrollInfo(hWnd, WXOrientToSB(orient), &info, refresh);
1101 }
1102
1103 // New function that will replace some of the above.
SetScrollbar(int orient,int pos,int pageSize,int range,bool refresh)1104 void wxWindowMSW::SetScrollbar(int orient,
1105 int pos,
1106 int pageSize,
1107 int range,
1108 bool refresh)
1109 {
1110 #if wxUSE_DEFERRED_SIZING
1111 // Work around not documented, but reliably happening, at least under
1112 // Windows 7, but with changing the scrollbars in the middle of a deferred
1113 // positioning operation: the child windows of a window whose position is
1114 // deferred after changing the scrollbar get offset compared to their
1115 // correct position, somehow.
1116 //
1117 // Note that this scenario happens all the time with wxScrolledWindow as
1118 // its HandleOnSize(), which calls AdjustScrollbars() and hence this method
1119 // indirectly, gets called from WM_SIZE handler, which begins deferring
1120 // window positions, and calls Layout() which continues doing it after the
1121 // scrollbar update. So one way of reproducing the bug is to have a window
1122 // with children, such as wxStaticBox, inside a wxScrolledWindow whose size
1123 // gets changed.
1124 //
1125 // Fix this simply by "flushing" the pending windows positions and starting
1126 // a new deferring operation.
1127 if ( m_hDWP )
1128 {
1129 // Do reposition the children already moved.
1130 EndRepositioningChildren();
1131
1132 // And restart another deferred positioning operation as any currently
1133 // existing ChildrenRepositioningGuard objects would be confused if we
1134 // just removed the HDWP from under them.
1135 //
1136 // Unfortunately we have to ignore BeginRepositioningChildren() return
1137 // value here, there is not much that we can do if it fails (but this
1138 // should never happen anyhow).
1139 BeginRepositioningChildren();
1140 }
1141 #endif // wxUSE_DEFERRED_SIZING
1142
1143 // We have to set the variables here to make them valid in events
1144 // triggered by ::SetScrollInfo()
1145 *(orient == wxHORIZONTAL ? &m_xThumbSize : &m_yThumbSize) = pageSize;
1146
1147 HWND hwnd = GetHwnd();
1148 if ( !hwnd )
1149 return;
1150
1151 WinStruct<SCROLLINFO> info;
1152 if ( range != -1 )
1153 {
1154 info.nPage = pageSize;
1155 info.nMin = 0; // range is nMax - nMin + 1
1156 info.nMax = range - 1; // as both nMax and nMax are inclusive
1157 info.nPos = pos;
1158
1159 // We normally also reenable scrollbar in case it had been previously
1160 // disabled by specifying SIF_DISABLENOSCROLL below but we should only
1161 // do this if it has valid range, otherwise it would be enabled but not
1162 // do anything.
1163 if ( range >= pageSize )
1164 {
1165 ::EnableScrollBar(hwnd, WXOrientToSB(orient), ESB_ENABLE_BOTH);
1166 }
1167 }
1168 //else: leave all the fields to be 0
1169
1170 info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1171 if ( HasFlag(wxALWAYS_SHOW_SB) || range == -1 )
1172 {
1173 // disable scrollbar instead of removing it then
1174 info.fMask |= SIF_DISABLENOSCROLL;
1175 }
1176
1177 ::SetScrollInfo(hwnd, WXOrientToSB(orient), &info, refresh);
1178 }
1179
ScrollWindow(int dx,int dy,const wxRect * prect)1180 void wxWindowMSW::ScrollWindow(int dx, int dy, const wxRect *prect)
1181 {
1182 if ( !dx && !dy )
1183 return;
1184
1185 RECT rect;
1186 RECT *pr;
1187 if ( prect )
1188 {
1189 wxCopyRectToRECT(*prect, rect);
1190 pr = ▭
1191 }
1192 else
1193 {
1194 pr = NULL;
1195
1196 }
1197
1198 ::ScrollWindow(GetHwnd(), dx, dy, pr, pr);
1199 }
1200
ScrollVertically(HWND hwnd,int kind,int count)1201 static bool ScrollVertically(HWND hwnd, int kind, int count)
1202 {
1203 int posStart = GetScrollPosition(hwnd, SB_VERT);
1204
1205 int pos = posStart;
1206 for ( int n = 0; n < count; n++ )
1207 {
1208 ::SendMessage(hwnd, WM_VSCROLL, kind, 0);
1209
1210 int posNew = GetScrollPosition(hwnd, SB_VERT);
1211 if ( posNew == pos )
1212 {
1213 // don't bother to continue, we're already at top/bottom
1214 break;
1215 }
1216
1217 pos = posNew;
1218 }
1219
1220 return pos != posStart;
1221 }
1222
ScrollLines(int lines)1223 bool wxWindowMSW::ScrollLines(int lines)
1224 {
1225 bool down = lines > 0;
1226
1227 return ScrollVertically(GetHwnd(),
1228 down ? SB_LINEDOWN : SB_LINEUP,
1229 down ? lines : -lines);
1230 }
1231
ScrollPages(int pages)1232 bool wxWindowMSW::ScrollPages(int pages)
1233 {
1234 bool down = pages > 0;
1235
1236 return ScrollVertically(GetHwnd(),
1237 down ? SB_PAGEDOWN : SB_PAGEUP,
1238 down ? pages : -pages);
1239 }
1240
1241 // ----------------------------------------------------------------------------
1242 // RTL support
1243 // ----------------------------------------------------------------------------
1244
SetLayoutDirection(wxLayoutDirection dir)1245 void wxWindowMSW::SetLayoutDirection(wxLayoutDirection dir)
1246 {
1247 if ( wxUpdateLayoutDirection(GetHwnd(), dir) )
1248 {
1249 // Update layout: whether we have children or are drawing something, we
1250 // need to redo it with the new layout.
1251 SendSizeEvent();
1252 Refresh();
1253 }
1254 }
1255
GetLayoutDirection() const1256 wxLayoutDirection wxWindowMSW::GetLayoutDirection() const
1257 {
1258 wxCHECK_MSG( GetHwnd(), wxLayout_Default, wxT("invalid window") );
1259
1260 return wxHasWindowExStyle(this, WS_EX_LAYOUTRTL) ? wxLayout_RightToLeft
1261 : wxLayout_LeftToRight;
1262 }
1263
1264 wxCoord
AdjustForLayoutDirection(wxCoord x,wxCoord WXUNUSED (width),wxCoord WXUNUSED (widthTotal)) const1265 wxWindowMSW::AdjustForLayoutDirection(wxCoord x,
1266 wxCoord WXUNUSED(width),
1267 wxCoord WXUNUSED(widthTotal)) const
1268 {
1269 // Win32 mirrors the coordinates of RTL windows automatically, so don't
1270 // redo it ourselves
1271 return x;
1272 }
1273
1274 // ---------------------------------------------------------------------------
1275 // subclassing
1276 // ---------------------------------------------------------------------------
1277
SubclassWin(WXHWND hWnd)1278 void wxWindowMSW::SubclassWin(WXHWND hWnd)
1279 {
1280 wxASSERT_MSG( !m_oldWndProc, wxT("subclassing window twice?") );
1281
1282 HWND hwnd = (HWND)hWnd;
1283 wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in SubclassWin") );
1284
1285 SetHWND(hWnd);
1286
1287 wxAssociateWinWithHandle(hwnd, this);
1288
1289 m_oldWndProc = wxGetWindowProc((HWND)hWnd);
1290
1291 // we don't need to subclass the window of our own class (in the Windows
1292 // sense of the word)
1293 if ( !wxCheckWindowWndProc(hWnd) )
1294 {
1295 wxSetWindowProc(hwnd, wxWndProc);
1296
1297 // If the window didn't use our window proc during its creation, the
1298 // code in HandleCreate() hasn't been executed, so do it here.
1299 if ( wxHasWindowExStyle(this, WS_EX_CONTROLPARENT) )
1300 EnsureParentHasControlParentStyle(GetParent());
1301 }
1302 else
1303 {
1304 // don't bother restoring it either: this also makes it easy to
1305 // implement IsOfStandardClass() method which returns true for the
1306 // standard controls and false for the wxWidgets own windows as it can
1307 // simply check m_oldWndProc
1308 m_oldWndProc = NULL;
1309 }
1310
1311 // we're officially created now, send the event
1312 wxWindowCreateEvent event((wxWindow *)this);
1313 (void)HandleWindowEvent(event);
1314 }
1315
UnsubclassWin()1316 void wxWindowMSW::UnsubclassWin()
1317 {
1318 wxRemoveHandleAssociation(this);
1319
1320 // Restore old Window proc
1321 HWND hwnd = GetHwnd();
1322 if ( hwnd )
1323 {
1324 SetHWND(0);
1325
1326 wxCHECK_RET( ::IsWindow(hwnd), wxT("invalid HWND in UnsubclassWin") );
1327
1328 if ( m_oldWndProc )
1329 {
1330 if ( !wxCheckWindowWndProc((WXHWND)hwnd) )
1331 {
1332 wxSetWindowProc(hwnd, m_oldWndProc);
1333 }
1334
1335 m_oldWndProc = NULL;
1336 }
1337 }
1338 }
1339
AssociateHandle(WXWidget handle)1340 void wxWindowMSW::AssociateHandle(WXWidget handle)
1341 {
1342 if ( m_hWnd )
1343 {
1344 if ( !::DestroyWindow(GetHwnd()) )
1345 {
1346 wxLogLastError(wxT("DestroyWindow"));
1347 }
1348 }
1349
1350 WXHWND wxhwnd = (WXHWND)handle;
1351
1352 // this also calls SetHWND(wxhwnd)
1353 SubclassWin(wxhwnd);
1354 }
1355
DissociateHandle()1356 void wxWindowMSW::DissociateHandle()
1357 {
1358 // this also calls SetHWND(0) for us
1359 UnsubclassWin();
1360 }
1361
1362
wxCheckWindowWndProc(WXHWND hWnd,WXWNDPROC WXUNUSED (wndProc))1363 bool wxCheckWindowWndProc(WXHWND hWnd, WXWNDPROC WXUNUSED(wndProc))
1364 {
1365 const wxString str(wxGetWindowClass(hWnd));
1366
1367 // TODO: get rid of wxTLWHiddenParent special case (currently it's not
1368 // registered by wxApp but using ad hoc code in msw/toplevel.cpp);
1369 // there is also a hidden window class used by sockets &c
1370 return wxApp::IsRegisteredClassName(str) || str == wxT("wxTLWHiddenParent");
1371 }
1372
1373 // ----------------------------------------------------------------------------
1374 // Style handling
1375 // ----------------------------------------------------------------------------
1376
SetWindowStyleFlag(long flags)1377 void wxWindowMSW::SetWindowStyleFlag(long flags)
1378 {
1379 long flagsOld = GetWindowStyleFlag();
1380 if ( flags == flagsOld )
1381 return;
1382
1383 // update the internal variable
1384 wxWindowBase::SetWindowStyleFlag(flags);
1385
1386 // and the real window flags
1387 MSWUpdateStyle(flagsOld, GetExtraStyle());
1388 }
1389
SetExtraStyle(long exflags)1390 void wxWindowMSW::SetExtraStyle(long exflags)
1391 {
1392 long exflagsOld = GetExtraStyle();
1393 if ( exflags == exflagsOld )
1394 return;
1395
1396 // update the internal variable
1397 wxWindowBase::SetExtraStyle(exflags);
1398
1399 // and the real window flags
1400 MSWUpdateStyle(GetWindowStyleFlag(), exflagsOld);
1401 }
1402
MSWUpdateStyle(long flagsOld,long exflagsOld)1403 void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld)
1404 {
1405 // now update the Windows style as well if needed - and if the window had
1406 // been already created
1407 if ( !GetHwnd() )
1408 return;
1409
1410 // we may need to call SetWindowPos() when we change some styles
1411 bool callSWP = false;
1412
1413 WXDWORD exstyle;
1414 long style = MSWGetStyle(GetWindowStyleFlag(), &exstyle);
1415
1416 // this is quite a horrible hack but we need it because MSWGetStyle()
1417 // doesn't take exflags as parameter but uses GetExtraStyle() internally
1418 // and so we have to modify the window exflags temporarily to get the
1419 // correct exstyleOld
1420 long exflagsNew = GetExtraStyle();
1421 wxWindowBase::SetExtraStyle(exflagsOld);
1422
1423 WXDWORD exstyleOld;
1424 long styleOld = MSWGetStyle(flagsOld, &exstyleOld);
1425
1426 wxWindowBase::SetExtraStyle(exflagsNew);
1427
1428
1429 if ( style != styleOld )
1430 {
1431 // some flags (e.g. WS_VISIBLE or WS_DISABLED) should not be changed by
1432 // this function so instead of simply setting the style to the new
1433 // value we clear the bits which were set in styleOld but are set in
1434 // the new one and set the ones which were not set before
1435 wxMSWWinStyleUpdater updateStyle(GetHwnd());
1436 updateStyle.TurnOff(styleOld).TurnOn(style);
1437
1438 // we need to call SetWindowPos() if any of the styles affecting the
1439 // frame appearance have changed
1440 callSWP = ((styleOld ^ style ) & (WS_BORDER |
1441 WS_THICKFRAME |
1442 WS_CAPTION |
1443 WS_DLGFRAME |
1444 WS_MAXIMIZEBOX |
1445 WS_MINIMIZEBOX |
1446 WS_SYSMENU) ) != 0;
1447 }
1448
1449 // There is one extra complication with the extended style: we must never
1450 // reset WS_EX_CONTROLPARENT because it may break the invariant that the
1451 // parent of any window with this style bit set has it as well. We enforce
1452 // this invariant elsewhere and must not clear it here to avoid the fatal
1453 // problems (hangs) which happen if we break it, so ensure it is preserved.
1454 if ( exstyleOld & WS_EX_CONTROLPARENT )
1455 exstyle |= WS_EX_CONTROLPARENT;
1456
1457 wxMSWWinExStyleUpdater updateExStyle(GetHwnd());
1458 if ( updateExStyle.TurnOff(exstyleOld).TurnOn(exstyle).Apply() )
1459 {
1460 // ex style changes don't take effect without calling SetWindowPos
1461 callSWP = true;
1462 }
1463
1464 if ( callSWP )
1465 {
1466 // we must call SetWindowPos() to flush the cached extended style and
1467 // also to make the change to wxSTAY_ON_TOP style take effect: just
1468 // setting the style simply doesn't work
1469 if ( !::SetWindowPos(GetHwnd(),
1470 updateExStyle.IsOn(WS_EX_TOPMOST) ? HWND_TOPMOST
1471 : HWND_NOTOPMOST,
1472 0, 0, 0, 0,
1473 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
1474 SWP_FRAMECHANGED) )
1475 {
1476 wxLogLastError(wxT("SetWindowPos"));
1477 }
1478 }
1479 }
1480
GetDefaultBorderForControl() const1481 wxBorder wxWindowMSW::GetDefaultBorderForControl() const
1482 {
1483 return wxBORDER_THEME;
1484 }
1485
GetDefaultBorder() const1486 wxBorder wxWindowMSW::GetDefaultBorder() const
1487 {
1488 return wxWindowBase::GetDefaultBorder();
1489 }
1490
1491 // Translate wxBORDER_THEME (and other border styles if necessary) to the value
1492 // that makes most sense for this Windows environment
TranslateBorder(wxBorder border) const1493 wxBorder wxWindowMSW::TranslateBorder(wxBorder border) const
1494 {
1495 #if wxUSE_UXTHEME
1496 if (border == wxBORDER_THEME)
1497 {
1498 if (CanApplyThemeBorder())
1499 {
1500 if ( wxUxThemeIsActive() )
1501 return wxBORDER_THEME;
1502 }
1503 return wxBORDER_SUNKEN;
1504 }
1505 #endif
1506 return border;
1507 }
1508
1509
MSWGetStyle(long flags,WXDWORD * exstyle) const1510 WXDWORD wxWindowMSW::MSWGetStyle(long flags, WXDWORD *exstyle) const
1511 {
1512 // translate common wxWidgets styles to Windows ones
1513
1514 // most of windows are child ones, those which are not (such as
1515 // wxTopLevelWindow) should remove WS_CHILD in their MSWGetStyle()
1516 WXDWORD style = WS_CHILD;
1517
1518 if ( !IsThisEnabled() )
1519 style |= WS_DISABLED;
1520
1521 // using this flag results in very significant reduction in flicker,
1522 // especially with controls inside the static boxes (as the interior of the
1523 // box is not redrawn twice), but sometimes results in redraw problems, so
1524 // optionally allow the old code to continue to use it provided a special
1525 // system option is turned on
1526 if ( !wxSystemOptions::GetOptionInt(wxT("msw.window.no-clip-children"))
1527 || (flags & wxCLIP_CHILDREN) )
1528 style |= WS_CLIPCHILDREN;
1529
1530 // it doesn't seem useful to use WS_CLIPSIBLINGS here as we officially
1531 // don't support overlapping windows and it only makes sense for them and,
1532 // presumably, gives the system some extra work (to manage more clipping
1533 // regions), so avoid it altogether
1534
1535
1536 if ( flags & wxVSCROLL )
1537 style |= WS_VSCROLL;
1538
1539 if ( flags & wxHSCROLL )
1540 style |= WS_HSCROLL;
1541
1542 const wxBorder border = TranslateBorder(GetBorder(flags));
1543
1544 // After translation, border is now optimized for the specific version of Windows
1545 // and theme engine presence.
1546
1547 // WS_BORDER is only required for wxBORDER_SIMPLE
1548 if ( border == wxBORDER_SIMPLE )
1549 style |= WS_BORDER;
1550
1551 // now deal with ext style if the caller wants it
1552 if ( exstyle )
1553 {
1554 *exstyle = 0;
1555
1556 if ( flags & wxTRANSPARENT_WINDOW )
1557 *exstyle |= WS_EX_TRANSPARENT;
1558
1559 switch ( border )
1560 {
1561 default:
1562 case wxBORDER_DEFAULT:
1563 wxFAIL_MSG( wxT("unknown border style") );
1564 wxFALLTHROUGH;
1565
1566 case wxBORDER_NONE:
1567 case wxBORDER_SIMPLE:
1568 case wxBORDER_THEME:
1569 break;
1570
1571 case wxBORDER_STATIC:
1572 *exstyle |= WS_EX_STATICEDGE;
1573 break;
1574
1575 case wxBORDER_RAISED:
1576 *exstyle |= WS_EX_DLGMODALFRAME;
1577 break;
1578
1579 case wxBORDER_SUNKEN:
1580 *exstyle |= WS_EX_CLIENTEDGE;
1581 style &= ~WS_BORDER;
1582 break;
1583
1584 // case wxBORDER_DOUBLE:
1585 // *exstyle |= WS_EX_DLGMODALFRAME;
1586 // break;
1587 }
1588
1589 // wxUniv doesn't use Windows dialog navigation functions at all
1590 #if !defined(__WXUNIVERSAL__)
1591 // to make the dialog navigation work with the nested panels we must
1592 // use this style (top level windows such as dialogs don't need it)
1593 if ( (flags & wxTAB_TRAVERSAL) && !IsTopLevel() )
1594 {
1595 *exstyle |= WS_EX_CONTROLPARENT;
1596 }
1597 #endif // __WXUNIVERSAL__
1598 }
1599
1600 return style;
1601 }
1602
1603 // Setup background and foreground colours correctly
SetupColours()1604 void wxWindowMSW::SetupColours()
1605 {
1606 if ( GetParent() )
1607 SetBackgroundColour(GetParent()->GetBackgroundColour());
1608 }
1609
IsMouseInWindow() const1610 bool wxWindowMSW::IsMouseInWindow() const
1611 {
1612 // get the mouse position
1613 POINT pt;
1614 wxGetCursorPosMSW(&pt);
1615
1616 // find the window which currently has the cursor and go up the window
1617 // chain until we find this window - or exhaust it
1618 HWND hwnd = ::WindowFromPoint(pt);
1619 while ( hwnd && (hwnd != GetHwnd()) )
1620 hwnd = ::GetParent(hwnd);
1621
1622 return hwnd != NULL;
1623 }
1624
OnInternalIdle()1625 void wxWindowMSW::OnInternalIdle()
1626 {
1627 #ifndef HAVE_TRACKMOUSEEVENT
1628 // Check if we need to send a LEAVE event
1629 if ( m_mouseInWindow )
1630 {
1631 // note that we should generate the leave event whether the window has
1632 // or doesn't have mouse capture
1633 if ( !IsMouseInWindow() )
1634 {
1635 GenerateMouseLeave();
1636 }
1637 }
1638 #endif // !HAVE_TRACKMOUSEEVENT
1639
1640 wxWindowBase::OnInternalIdle();
1641 }
1642
1643 // Set this window to be the child of 'parent'.
Reparent(wxWindowBase * parent)1644 bool wxWindowMSW::Reparent(wxWindowBase *parent)
1645 {
1646 if ( !wxWindowBase::Reparent(parent) )
1647 return false;
1648
1649 HWND hWndChild = GetHwnd();
1650 HWND hWndParent = GetParent() ? GetWinHwnd(GetParent()) : (HWND)0;
1651
1652 ::SetParent(hWndChild, hWndParent);
1653
1654 if ( wxHasWindowExStyle(this, WS_EX_CONTROLPARENT) )
1655 {
1656 EnsureParentHasControlParentStyle(GetParent());
1657 }
1658
1659 return true;
1660 }
1661
SendSetRedraw(HWND hwnd,bool on)1662 static inline void SendSetRedraw(HWND hwnd, bool on)
1663 {
1664 ::SendMessage(hwnd, WM_SETREDRAW, (WPARAM)on, 0);
1665 }
1666
DoFreeze()1667 void wxWindowMSW::DoFreeze()
1668 {
1669 if ( !IsShown() )
1670 return; // no point in freezing hidden window
1671
1672 SendSetRedraw(GetHwnd(), false);
1673 }
1674
DoThaw()1675 void wxWindowMSW::DoThaw()
1676 {
1677 if ( !IsShown() )
1678 return; // hidden windows aren't frozen by DoFreeze
1679
1680 SendSetRedraw(GetHwnd(), true);
1681
1682 // we need to refresh everything or otherwise the invalidated area
1683 // is not going to be repainted
1684 Refresh();
1685 }
1686
Refresh(bool eraseBack,const wxRect * rect)1687 void wxWindowMSW::Refresh(bool eraseBack, const wxRect *rect)
1688 {
1689 HWND hWnd = GetHwnd();
1690 if ( hWnd )
1691 {
1692 RECT mswRect;
1693 const RECT *pRect;
1694 if ( rect )
1695 {
1696 wxCopyRectToRECT(*rect, mswRect);
1697 pRect = &mswRect;
1698 }
1699 else
1700 {
1701 pRect = NULL;
1702 }
1703
1704 UINT flags = RDW_INVALIDATE | RDW_ALLCHILDREN;
1705 if ( eraseBack )
1706 flags |= RDW_ERASE;
1707
1708 ::RedrawWindow(hWnd, pRect, NULL, flags);
1709 }
1710 }
1711
Update()1712 void wxWindowMSW::Update()
1713 {
1714 if ( !::UpdateWindow(GetHwnd()) )
1715 {
1716 wxLogLastError(wxT("UpdateWindow"));
1717 }
1718
1719 // just calling UpdateWindow() is not enough, what we did in our WM_PAINT
1720 // handler needs to be really drawn right now
1721 (void)::GdiFlush();
1722 }
1723
1724 // ---------------------------------------------------------------------------
1725 // drag and drop
1726 // ---------------------------------------------------------------------------
1727
1728 #if wxUSE_DRAG_AND_DROP
1729
1730 #if wxUSE_STATBOX
1731
1732 // we need to lower the sibling static boxes so controls contained within can be
1733 // a drop target
AdjustStaticBoxZOrder(wxWindow * parent)1734 static void AdjustStaticBoxZOrder(wxWindow *parent)
1735 {
1736 // no sibling static boxes if we have no parent (ie TLW)
1737 if ( !parent )
1738 return;
1739
1740 for ( wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst();
1741 node;
1742 node = node->GetNext() )
1743 {
1744 wxStaticBox *statbox = wxDynamicCast(node->GetData(), wxStaticBox);
1745 if ( statbox )
1746 {
1747 ::SetWindowPos(GetHwndOf(statbox), HWND_BOTTOM, 0, 0, 0, 0,
1748 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1749 }
1750 }
1751 }
1752
1753 #else // !wxUSE_STATBOX
1754
AdjustStaticBoxZOrder(wxWindow * WXUNUSED (parent))1755 static inline void AdjustStaticBoxZOrder(wxWindow * WXUNUSED(parent))
1756 {
1757 }
1758
1759 #endif // wxUSE_STATBOX/!wxUSE_STATBOX
1760
SetDropTarget(wxDropTarget * pDropTarget)1761 void wxWindowMSW::SetDropTarget(wxDropTarget *pDropTarget)
1762 {
1763 if ( m_dropTarget != 0 ) {
1764 m_dropTarget->Revoke(m_hWnd);
1765 delete m_dropTarget;
1766 }
1767
1768 m_dropTarget = pDropTarget;
1769 if ( m_dropTarget != 0 )
1770 {
1771 AdjustStaticBoxZOrder(GetParent());
1772 m_dropTarget->Register(m_hWnd);
1773 }
1774 }
1775
1776 // old-style file manager drag&drop support: we retain the old-style
1777 // DragAcceptFiles in parallel with SetDropTarget.
DragAcceptFiles(bool accept)1778 void wxWindowMSW::DragAcceptFiles(bool accept)
1779 {
1780 HWND hWnd = GetHwnd();
1781 if ( hWnd )
1782 {
1783 AdjustStaticBoxZOrder(GetParent());
1784 ::DragAcceptFiles(hWnd, (BOOL)accept);
1785 }
1786 }
1787 #endif // wxUSE_DRAG_AND_DROP
1788
1789 // ----------------------------------------------------------------------------
1790 // tooltips
1791 // ----------------------------------------------------------------------------
1792
1793 #if wxUSE_TOOLTIPS
1794
DoSetToolTip(wxToolTip * tooltip)1795 void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip)
1796 {
1797 wxWindowBase::DoSetToolTip(tooltip);
1798
1799 if ( m_tooltip )
1800 m_tooltip->SetWindow((wxWindow *)this);
1801 }
1802
1803 #endif // wxUSE_TOOLTIPS
1804
1805 // ---------------------------------------------------------------------------
1806 // moving and resizing
1807 // ---------------------------------------------------------------------------
1808
IsSizeDeferred() const1809 bool wxWindowMSW::IsSizeDeferred() const
1810 {
1811 #if wxUSE_DEFERRED_SIZING
1812 if ( m_pendingPosition != wxDefaultPosition ||
1813 m_pendingSize != wxDefaultSize )
1814 return true;
1815 #endif // wxUSE_DEFERRED_SIZING
1816
1817 return false;
1818 }
1819
1820 // Get total size
DoGetSize(int * x,int * y) const1821 void wxWindowMSW::DoGetSize(int *x, int *y) const
1822 {
1823 #if wxUSE_DEFERRED_SIZING
1824 // if SetSize() had been called at wx level but not realized at Windows
1825 // level yet (i.e. EndDeferWindowPos() not called), we still should return
1826 // the new and not the old position to the other wx code
1827 if ( m_pendingSize != wxDefaultSize )
1828 {
1829 if ( x )
1830 *x = m_pendingSize.x;
1831 if ( y )
1832 *y = m_pendingSize.y;
1833 }
1834 else // use current size
1835 #endif // wxUSE_DEFERRED_SIZING
1836 {
1837 RECT rect = wxGetWindowRect(GetHwnd());
1838
1839 if ( x )
1840 *x = rect.right - rect.left;
1841 if ( y )
1842 *y = rect.bottom - rect.top;
1843 }
1844 }
1845
1846 // Get size *available for subwindows* i.e. excluding menu bar etc.
DoGetClientSize(int * x,int * y) const1847 void wxWindowMSW::DoGetClientSize(int *x, int *y) const
1848 {
1849 #if wxUSE_DEFERRED_SIZING
1850 if ( m_pendingSize != wxDefaultSize )
1851 {
1852 // we need to calculate the client size corresponding to pending size
1853 //
1854 // FIXME: Unfortunately this doesn't work correctly for the maximized
1855 // top level windows, the returned values are too small (e.g.
1856 // under Windows 7 on a 1600*1200 screen with task bar on the
1857 // right the pending size for a maximized window is 1538*1200
1858 // and WM_NCCALCSIZE returns 1528*1172 even though the correct
1859 // client size of such window is 1538*1182). No idea how to fix
1860 // it though, setting WS_MAXIMIZE in GWL_STYLE before calling
1861 // WM_NCCALCSIZE doesn't help and AdjustWindowRectEx() doesn't
1862 // work in this direction neither. So we just have to live with
1863 // the slightly wrong results and relayout the window when it
1864 // gets finally shown in its maximized state (see #11762).
1865 RECT rect;
1866 rect.left = m_pendingPosition.x;
1867 rect.top = m_pendingPosition.y;
1868 rect.right = rect.left + m_pendingSize.x;
1869 rect.bottom = rect.top + m_pendingSize.y;
1870
1871 ::SendMessage(GetHwnd(), WM_NCCALCSIZE, FALSE, (LPARAM)&rect);
1872
1873 if ( x )
1874 *x = rect.right - rect.left;
1875 if ( y )
1876 *y = rect.bottom - rect.top;
1877 }
1878 else
1879 #endif // wxUSE_DEFERRED_SIZING
1880 {
1881 RECT rect = wxGetClientRect(GetHwnd());
1882
1883 if ( x )
1884 *x = rect.right;
1885 if ( y )
1886 *y = rect.bottom;
1887 }
1888
1889 // The size of the client window can't be negative but ::GetClientRect()
1890 // can return negative size for an extremely small (1x1) window with
1891 // borders so ensure that we correct it here as having negative sizes is
1892 // completely unexpected.
1893 if ( x && *x < 0 )
1894 *x = 0;
1895 if ( y && *y < 0 )
1896 *y = 0;
1897 }
1898
DoGetPosition(int * x,int * y) const1899 void wxWindowMSW::DoGetPosition(int *x, int *y) const
1900 {
1901 wxWindow * const parent = GetParent();
1902
1903 wxPoint pos;
1904 #if wxUSE_DEFERRED_SIZING
1905 if ( m_pendingPosition != wxDefaultPosition )
1906 {
1907 pos = m_pendingPosition;
1908 }
1909 else // use current position
1910 #endif // wxUSE_DEFERRED_SIZING
1911 {
1912 RECT rect = wxGetWindowRect(GetHwnd());
1913
1914 // we do the adjustments with respect to the parent only for the "real"
1915 // children, not for the dialogs/frames
1916 if ( !IsTopLevel() )
1917 {
1918 // In RTL mode, we want the logical left x-coordinate,
1919 // which would be the physical right x-coordinate.
1920 ::MapWindowPoints(NULL, parent ? GetHwndOf(parent) : HWND_DESKTOP,
1921 (LPPOINT)&rect, 2);
1922 }
1923
1924 pos.x = rect.left;
1925 pos.y = rect.top;
1926 }
1927
1928 // we also must adjust by the client area offset: a control which is just
1929 // under a toolbar could be at (0, 30) in Windows but at (0, 0) in wx
1930 if ( parent && !IsTopLevel() )
1931 {
1932 const wxPoint pt(parent->GetClientAreaOrigin());
1933 pos.x -= pt.x;
1934 pos.y -= pt.y;
1935 }
1936
1937 if ( x )
1938 *x = pos.x;
1939 if ( y )
1940 *y = pos.y;
1941 }
1942
1943 /* static */
MSWDoScreenToClient(WXHWND hWnd,int * x,int * y)1944 void wxWindowMSW::MSWDoScreenToClient(WXHWND hWnd, int *x, int *y)
1945 {
1946 POINT pt;
1947 if ( x )
1948 pt.x = *x;
1949 if ( y )
1950 pt.y = *y;
1951
1952 ::ScreenToClient(hWnd, &pt);
1953
1954 if ( x )
1955 *x = pt.x;
1956 if ( y )
1957 *y = pt.y;
1958 }
1959
1960 /* static */
MSWDoClientToScreen(WXHWND hWnd,int * x,int * y)1961 void wxWindowMSW::MSWDoClientToScreen(WXHWND hWnd, int *x, int *y)
1962 {
1963 POINT pt;
1964 if ( x )
1965 pt.x = *x;
1966 if ( y )
1967 pt.y = *y;
1968
1969 ::ClientToScreen(hWnd, &pt);
1970
1971 if ( x )
1972 *x = pt.x;
1973 if ( y )
1974 *y = pt.y;
1975 }
1976
DoScreenToClient(int * x,int * y) const1977 void wxWindowMSW::DoScreenToClient(int *x, int *y) const
1978 {
1979 MSWDoScreenToClient(GetHwnd(), x, y);
1980 }
1981
DoClientToScreen(int * x,int * y) const1982 void wxWindowMSW::DoClientToScreen(int *x, int *y) const
1983 {
1984 MSWDoClientToScreen(GetHwnd(), x, y);
1985 }
1986
1987 bool
DoMoveSibling(WXHWND hwnd,int x,int y,int width,int height)1988 wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height)
1989 {
1990 // toplevel window's coordinates are mirrored if the TLW is a child of another
1991 // RTL window and changing width without moving the position would enlarge the
1992 // window in the wrong direction, so we need to adjust for it
1993 if ( IsTopLevel() )
1994 {
1995 // note that this may be different from GetParent() for wxDialogs
1996 HWND tlwParent = ::GetParent((HWND)hwnd);
1997 if ( tlwParent && (::GetWindowLong(tlwParent, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0 )
1998 {
1999 RECT old;
2000 ::GetWindowRect((HWND) hwnd, &old);
2001 if ( old.left == x && old.right - old.left != width )
2002 {
2003 x -= width - (old.right - old.left);
2004 }
2005 // else: not a simple resize
2006 }
2007 }
2008
2009 #if wxUSE_DEFERRED_SIZING
2010 else if ( MSWIsPositionDirectlySupported(x, y) )
2011 {
2012 // if our parent had prepared a defer window handle for us, use it
2013 wxWindowMSW * const parent = GetParent();
2014
2015 HDWP hdwp = parent ? (HDWP)parent->m_hDWP : NULL;
2016 if ( hdwp )
2017 {
2018 hdwp = ::DeferWindowPos(hdwp, (HWND)hwnd, NULL, x, y, width, height,
2019 SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
2020 if ( !hdwp )
2021 {
2022 wxLogLastError(wxT("DeferWindowPos"));
2023 }
2024 }
2025
2026 if ( parent )
2027 {
2028 // hdwp must be updated as it may have been changed
2029 parent->m_hDWP = (WXHANDLE)hdwp;
2030 }
2031
2032 if ( hdwp )
2033 {
2034 // did deferred move, remember new coordinates of the window as they're
2035 // different from what Windows would return for it
2036 return true;
2037 }
2038
2039 // otherwise (or if deferring failed) move the window in place immediately
2040 }
2041 #endif // wxUSE_DEFERRED_SIZING
2042
2043 MSWMoveWindowToAnyPosition(hwnd, x, y, width, height, IsShown());
2044
2045 // if wxUSE_DEFERRED_SIZING, indicates that we didn't use deferred move,
2046 // ignored otherwise
2047 return false;
2048 }
2049
MSWMoveWindowToAnyPosition(WXHWND hwnd,int x,int y,int width,int height,bool bRepaint)2050 void wxWindowMSW::MSWMoveWindowToAnyPosition(WXHWND hwnd, int x, int y, int width, int height, bool bRepaint)
2051 {
2052 bool scroll = GetParent() && !MSWIsPositionDirectlySupported(x, y);
2053
2054 if ( scroll )
2055 {
2056 // scroll to the actual position (looks like there is no need to Freeze() the parent)
2057 ::ScrollWindow(GetHwndOf(GetParent()), -x, -y, NULL, NULL);
2058 }
2059
2060 // move to relative coordinates
2061 if ( !::MoveWindow(hwnd, (scroll ? 0 : x), (scroll ? 0 : y), width, height, bRepaint) )
2062 {
2063 wxLogLastError(wxT("MoveWindow"));
2064 }
2065
2066 if ( scroll )
2067 {
2068 // scroll back
2069 ::ScrollWindow(GetHwndOf(GetParent()), x, y, NULL, NULL);
2070 }
2071 }
2072
DoMoveWindow(int x,int y,int width,int height)2073 void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height)
2074 {
2075 // TODO: is this consistent with other platforms?
2076 // Still, negative width or height shouldn't be allowed
2077 if (width < 0)
2078 width = 0;
2079 if (height < 0)
2080 height = 0;
2081
2082 if ( DoMoveSibling(m_hWnd, x, y, width, height) )
2083 {
2084 #if wxUSE_DEFERRED_SIZING
2085 m_pendingPosition = wxPoint(x, y);
2086 m_pendingSize = wxSize(width, height);
2087 }
2088 else // window was moved immediately, without deferring it
2089 {
2090 m_pendingPosition = wxDefaultPosition;
2091 m_pendingSize = wxDefaultSize;
2092 #endif // wxUSE_DEFERRED_SIZING
2093 }
2094 }
2095
2096 // set the size of the window: if the dimensions are positive, just use them,
2097 // but if any of them is equal to -1, it means that we must find the value for
2098 // it ourselves (unless sizeFlags contains wxSIZE_ALLOW_MINUS_ONE flag, in
2099 // which case -1 is a valid value for x and y)
2100 //
2101 // If sizeFlags contains wxSIZE_AUTO_WIDTH/HEIGHT flags (default), we calculate
2102 // the width/height to best suit our contents, otherwise we reuse the current
2103 // width/height
DoSetSize(int x,int y,int width,int height,int sizeFlags)2104 void wxWindowMSW::DoSetSize(int x, int y, int width, int height, int sizeFlags)
2105 {
2106 // get the current size and position...
2107 int currentX, currentY;
2108 int currentW, currentH;
2109
2110 GetPosition(¤tX, ¤tY);
2111 GetSize(¤tW, ¤tH);
2112
2113 if ( x == wxDefaultCoord && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) )
2114 x = currentX;
2115 if ( y == wxDefaultCoord && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) )
2116 y = currentY;
2117
2118 wxSize size = wxDefaultSize;
2119 if ( width == wxDefaultCoord )
2120 {
2121 if ( sizeFlags & wxSIZE_AUTO_WIDTH )
2122 {
2123 size = GetBestSize();
2124 width = size.x;
2125 }
2126 else
2127 {
2128 // just take the current one
2129 width = currentW;
2130 }
2131 }
2132
2133 if ( height == wxDefaultCoord )
2134 {
2135 if ( sizeFlags & wxSIZE_AUTO_HEIGHT )
2136 {
2137 if ( size.x == wxDefaultCoord )
2138 {
2139 size = GetBestSize();
2140 }
2141 //else: already called GetBestSize() above
2142
2143 height = size.y;
2144 }
2145 else
2146 {
2147 // just take the current one
2148 height = currentH;
2149 }
2150 }
2151
2152 // ... and don't do anything (avoiding flicker) if it's already ok unless
2153 // we're forced to resize the window
2154 if ( !(sizeFlags & wxSIZE_FORCE) )
2155 {
2156 if ( width == currentW && height == currentH )
2157 {
2158 // We need to send wxSizeEvent ourselves because Windows won't do
2159 // it if the size doesn't change.
2160 if ( sizeFlags & wxSIZE_FORCE_EVENT )
2161 {
2162 wxSizeEvent event( wxSize(width,height), GetId() );
2163 event.SetEventObject( this );
2164 HandleWindowEvent( event );
2165 }
2166
2167 // Still call DoMoveWindow() below if we need to change the
2168 // position, otherwise we're done.
2169 if ( x == currentX && y == currentY )
2170 return;
2171 }
2172 }
2173
2174 AdjustForParentClientOrigin(x, y, sizeFlags);
2175
2176 DoMoveWindow(x, y, width, height);
2177 }
2178
DoSetClientSize(int width,int height)2179 void wxWindowMSW::DoSetClientSize(int width, int height)
2180 {
2181 // setting the client size is less obvious than it could have been
2182 // because in the result of changing the total size the window scrollbar
2183 // may [dis]appear and/or its menubar may [un]wrap (and AdjustWindowRect()
2184 // doesn't take neither into account) and so the client size will not be
2185 // correct as the difference between the total and client size changes --
2186 // so we keep changing it until we get it right
2187 //
2188 // normally this loop shouldn't take more than 3 iterations (usually 1 but
2189 // if scrollbars [dis]appear as the result of the first call, then 2 and it
2190 // may become 3 if the window had 0 size originally and so we didn't
2191 // calculate the scrollbar correction correctly during the first iteration)
2192 // but just to be on the safe side we check for it instead of making it an
2193 // "infinite" loop (i.e. leaving break inside as the only way to get out)
2194 for ( int i = 0; i < 4; i++ )
2195 {
2196 RECT rectClient;
2197 ::GetClientRect(GetHwnd(), &rectClient);
2198
2199 // if the size is already ok, stop here (NB: rectClient.left = top = 0)
2200 if ( (rectClient.right == width || width == wxDefaultCoord) &&
2201 (rectClient.bottom == height || height == wxDefaultCoord) )
2202 {
2203 break;
2204 }
2205
2206 // Find the difference between the entire window (title bar and all)
2207 // and the client area; add this to the new client size to move the
2208 // window
2209 RECT rectWin;
2210 ::GetWindowRect(GetHwnd(), &rectWin);
2211
2212 const int widthWin = rectWin.right - rectWin.left,
2213 heightWin = rectWin.bottom - rectWin.top;
2214
2215 if ( IsTopLevel() )
2216 {
2217 // toplevel window's coordinates are mirrored if the TLW is a child of another
2218 // RTL window and changing width without moving the position would enlarge the
2219 // window in the wrong direction, so we need to adjust for it
2220
2221 // note that this may be different from GetParent() for wxDialogs
2222 HWND tlwParent = ::GetParent(GetHwnd());
2223 if ( tlwParent && (::GetWindowLong(tlwParent, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0 )
2224 {
2225 const int diffWidth = width - (rectClient.right - rectClient.left);
2226 rectWin.left -= diffWidth;
2227 rectWin.right -= diffWidth;
2228 }
2229 }
2230 else
2231 {
2232 // MoveWindow positions the child windows relative to the parent, so
2233 // adjust if necessary
2234 wxWindow *parent = GetParent();
2235 if ( parent )
2236 {
2237 ::ScreenToClient(GetHwndOf(parent), (POINT *)&rectWin);
2238 }
2239 }
2240
2241 // don't call DoMoveWindow() because we want to move window immediately
2242 // and not defer it here as otherwise the value returned by
2243 // GetClient/WindowRect() wouldn't change as the window wouldn't be
2244 // really resized
2245 MSWMoveWindowToAnyPosition(GetHwnd(), rectWin.left, rectWin.top,
2246 width + widthWin - rectClient.right,
2247 height + heightWin - rectClient.bottom, true);
2248 }
2249 }
2250
DoGetBorderSize() const2251 wxSize wxWindowMSW::DoGetBorderSize() const
2252 {
2253 wxCoord border;
2254 switch ( GetBorder() )
2255 {
2256 case wxBORDER_STATIC:
2257 case wxBORDER_SIMPLE:
2258 border = 1;
2259 break;
2260
2261 case wxBORDER_SUNKEN:
2262 case wxBORDER_THEME:
2263 border = 2;
2264 break;
2265
2266 case wxBORDER_RAISED:
2267 border = 3;
2268 break;
2269
2270 default:
2271 wxFAIL_MSG( wxT("unknown border style") );
2272 wxFALLTHROUGH;
2273
2274 case wxBORDER_NONE:
2275 border = 0;
2276 }
2277
2278 return 2*wxSize(border, border);
2279 }
2280
2281 // ---------------------------------------------------------------------------
2282 // text metrics
2283 // ---------------------------------------------------------------------------
2284
GetCharHeight() const2285 int wxWindowMSW::GetCharHeight() const
2286 {
2287 return wxGetTextMetrics(this).tmHeight;
2288 }
2289
GetCharWidth() const2290 int wxWindowMSW::GetCharWidth() const
2291 {
2292 // +1 is needed because Windows apparently adds it when calculating the
2293 // dialog units size in pixels
2294 #if wxDIALOG_UNIT_COMPATIBILITY
2295 return wxGetTextMetrics(this).tmAveCharWidth;
2296 #else
2297 return wxGetTextMetrics(this).tmAveCharWidth + 1;
2298 #endif
2299 }
2300
DoGetTextExtent(const wxString & string,int * x,int * y,int * descent,int * externalLeading,const wxFont * fontToUse) const2301 void wxWindowMSW::DoGetTextExtent(const wxString& string,
2302 int *x, int *y,
2303 int *descent,
2304 int *externalLeading,
2305 const wxFont *fontToUse) const
2306 {
2307 // ensure we work with a valid font
2308 wxFont font;
2309 if ( !fontToUse || !fontToUse->IsOk() )
2310 font = GetFont();
2311 else
2312 font = *fontToUse;
2313
2314 wxCHECK_RET( font.IsOk(), wxT("invalid font in GetTextExtent()") );
2315
2316 const wxWindow* win = static_cast<const wxWindow*>(this);
2317 wxTextMeasure txm(win, &font);
2318 txm.GetTextExtent(string, x, y, descent, externalLeading);
2319 }
2320
2321 // ---------------------------------------------------------------------------
2322 // popup menu
2323 // ---------------------------------------------------------------------------
2324
2325 #if wxUSE_MENUS_NATIVE
2326
2327 // yield for WM_COMMAND events only, i.e. process all WM_COMMANDs in the queue
2328 // immediately, without waiting for the next event loop iteration
2329 //
2330 // NB: this function should probably be made public later as it can almost
2331 // surely replace wxYield() elsewhere as well
wxYieldForCommandsOnly()2332 static void wxYieldForCommandsOnly()
2333 {
2334 // peek all WM_COMMANDs (it will always return WM_QUIT too but we don't
2335 // want to process it here)
2336 MSG msg;
2337 while ( ::PeekMessage(&msg, (HWND)0, WM_COMMAND, WM_COMMAND, PM_REMOVE) )
2338 {
2339 if ( msg.message == WM_QUIT )
2340 {
2341 // if we retrieved a WM_QUIT, insert back into the message queue.
2342 ::PostQuitMessage(0);
2343 break;
2344 }
2345
2346 // luckily (as we don't have access to wxEventLoopImpl method from here
2347 // anyhow...) we don't need to pre process WM_COMMANDs so dispatch it
2348 // immediately
2349 ::TranslateMessage(&msg);
2350 ::DispatchMessage(&msg);
2351 }
2352 }
2353
DoPopupMenu(wxMenu * menu,int x,int y)2354 bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y)
2355 {
2356 wxPoint pt;
2357 if ( x == wxDefaultCoord && y == wxDefaultCoord )
2358 {
2359 pt = wxGetMousePosition();
2360 }
2361 else
2362 {
2363 pt = ClientToScreen(wxPoint(x, y));
2364 }
2365
2366 // using TPM_RECURSE allows us to show a popup menu while another menu
2367 // is opened which can be useful and is supported by the other
2368 // platforms, so allow it under Windows too
2369 UINT flags = TPM_RIGHTBUTTON | TPM_RECURSE;
2370
2371 ::TrackPopupMenu(GetHmenuOf(menu), flags, pt.x, pt.y, 0, GetHwnd(), NULL);
2372
2373 // we need to do it right now as otherwise the events are never going to be
2374 // sent to wxCurrentPopupMenu from HandleCommand()
2375 //
2376 // note that even eliminating (ugly) wxCurrentPopupMenu global wouldn't
2377 // help and we'd still need wxYieldForCommandsOnly() as the menu may be
2378 // destroyed as soon as we return (it can be a local variable in the caller
2379 // for example) and so we do need to process the event immediately
2380 wxYieldForCommandsOnly();
2381
2382 return true;
2383 }
2384
2385 #endif // wxUSE_MENUS_NATIVE
2386
2387 // ---------------------------------------------------------------------------
2388 // menu events
2389 // ---------------------------------------------------------------------------
2390
2391 #if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
2392
2393 bool
HandleMenuSelect(WXWORD nItem,WXWORD flags,WXHMENU hMenu)2394 wxWindowMSW::HandleMenuSelect(WXWORD nItem, WXWORD flags, WXHMENU hMenu)
2395 {
2396 // Ignore the special messages generated when the menu is closed (this is
2397 // the only case when the flags are set to -1), in particular don't clear
2398 // the help string in the status bar when this happens as it had just been
2399 // restored by the base class code.
2400 if ( !hMenu && flags == 0xffff )
2401 return false;
2402
2403 // sign extend to int from unsigned short we get from Windows
2404 int item = (signed short)nItem;
2405
2406 // WM_MENUSELECT is generated for both normal items and menus, including
2407 // the top level menus of the menu bar, which can't be represented using
2408 // any valid identifier in wxMenuEvent so use an otherwise unused value for
2409 // them
2410 if ( flags & (MF_POPUP | MF_SEPARATOR) )
2411 item = wxID_NONE;
2412
2413 wxMenu* menu = MSWFindMenuFromHMENU(hMenu);
2414 wxMenuEvent event(wxEVT_MENU_HIGHLIGHT, item, menu);
2415 if ( wxMenu::ProcessMenuEvent(menu, event, this) )
2416 return true;
2417
2418 // by default, i.e. if the event wasn't handled above, clear the status bar
2419 // text when an item which can't have any associated help string in wx API
2420 // is selected
2421 if ( item == wxID_NONE )
2422 {
2423 wxFrame *frame = wxDynamicCast(wxGetTopLevelParent(this), wxFrame);
2424 if ( frame )
2425 frame->DoGiveHelp(wxEmptyString, true);
2426 }
2427
2428 return false;
2429 }
2430
2431 bool
DoSendMenuOpenCloseEvent(wxEventType evtType,wxMenu * menu)2432 wxWindowMSW::DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu)
2433 {
2434 wxMenuEvent event(evtType, menu && !menu->IsAttached() ? wxID_ANY : 0, menu);
2435
2436 return wxMenu::ProcessMenuEvent(menu, event, this);
2437 }
2438
HandleMenuPopup(wxEventType evtType,WXHMENU hMenu)2439 bool wxWindowMSW::HandleMenuPopup(wxEventType evtType, WXHMENU hMenu)
2440 {
2441 wxMenu* const menu = MSWFindMenuFromHMENU(hMenu);
2442
2443 return DoSendMenuOpenCloseEvent(evtType, menu);
2444 }
2445
MSWFindMenuFromHMENU(WXHMENU hMenu)2446 wxMenu* wxWindowMSW::MSWFindMenuFromHMENU(WXHMENU hMenu)
2447 {
2448 if ( wxCurrentPopupMenu && wxCurrentPopupMenu->GetHMenu() == hMenu )
2449 return wxCurrentPopupMenu;
2450
2451 return NULL;
2452 }
2453
2454 #endif // wxUSE_MENUS && !defined(__WXUNIVERSAL__)
2455
2456 // ===========================================================================
2457 // pre/post message processing
2458 // ===========================================================================
2459
MSWDefWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)2460 WXLRESULT wxWindowMSW::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2461 {
2462 WXLRESULT rc;
2463 if ( m_oldWndProc )
2464 rc = ::CallWindowProc(m_oldWndProc, GetHwnd(), nMsg, wParam, lParam);
2465 else
2466 rc = ::DefWindowProc(GetHwnd(), nMsg, wParam, lParam);
2467
2468 // Special hack used by wxTextEntry auto-completion only: this event is
2469 // sent after the normal keyboard processing so that its handler could use
2470 // the updated contents of the text control, after taking the key that was
2471 // pressed into account.
2472 if ( nMsg == WM_CHAR )
2473 {
2474 wxKeyEvent event(CreateCharEvent(wxEVT_AFTER_CHAR, wParam, lParam));
2475 HandleWindowEvent(event);
2476 }
2477
2478 return rc;
2479 }
2480
MSWProcessMessage(WXMSG * pMsg)2481 bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg)
2482 {
2483 // wxUniversal implements tab traversal itself
2484 #ifndef __WXUNIVERSAL__
2485 // Notice that we check for both wxTAB_TRAVERSAL and WS_EX_CONTROLPARENT
2486 // being set here. While normally the latter should always be set if the
2487 // former is, doing it like this also works if there is ever a bug that
2488 // results in wxTAB_TRAVERSAL being set but not WS_EX_CONTROLPARENT as we
2489 // must not call IsDialogMessage() then, it would simply hang (see #15458).
2490 if ( m_hWnd &&
2491 HasFlag(wxTAB_TRAVERSAL) &&
2492 wxHasWindowExStyle(this, WS_EX_CONTROLPARENT) )
2493 {
2494 // intercept dialog navigation keys
2495 MSG *msg = (MSG *)pMsg;
2496
2497 // here we try to do all the job which ::IsDialogMessage() usually does
2498 // internally
2499 if ( msg->message == WM_KEYDOWN )
2500 {
2501 bool bCtrlDown = wxIsCtrlDown();
2502 bool bShiftDown = wxIsShiftDown();
2503
2504 // WM_GETDLGCODE: ask the control if it wants the key for itself,
2505 // don't process it if it's the case (except for Ctrl-Tab/Enter
2506 // combinations which are always processed)
2507 LONG lDlgCode = ::SendMessage(msg->hwnd, WM_GETDLGCODE, 0, 0);
2508
2509 // surprisingly, DLGC_WANTALLKEYS bit mask doesn't contain the
2510 // DLGC_WANTTAB nor DLGC_WANTARROWS bits although, logically,
2511 // it, of course, implies them
2512 if ( lDlgCode & DLGC_WANTALLKEYS )
2513 {
2514 lDlgCode |= DLGC_WANTTAB | DLGC_WANTARROWS;
2515 }
2516
2517 bool bForward = true,
2518 bWindowChange = false,
2519 bFromTab = false;
2520
2521 // should we process this message specially?
2522 bool bProcess = true;
2523 switch ( msg->wParam )
2524 {
2525 case VK_TAB:
2526 if ( (lDlgCode & DLGC_WANTTAB) && !bCtrlDown )
2527 {
2528 // let the control have the TAB
2529 bProcess = false;
2530 }
2531 else // use it for navigation
2532 {
2533 // Ctrl-Tab cycles thru notebook pages
2534 bWindowChange = bCtrlDown;
2535 bForward = !bShiftDown;
2536 bFromTab = true;
2537 }
2538 break;
2539
2540 case VK_UP:
2541 case VK_LEFT:
2542 if ( (lDlgCode & DLGC_WANTARROWS) || bCtrlDown )
2543 bProcess = false;
2544 else
2545 bForward = false;
2546 break;
2547
2548 case VK_DOWN:
2549 case VK_RIGHT:
2550 if ( (lDlgCode & DLGC_WANTARROWS) || bCtrlDown )
2551 bProcess = false;
2552 break;
2553
2554 case VK_PRIOR:
2555 bForward = false;
2556 wxFALLTHROUGH;
2557
2558 case VK_NEXT:
2559 // we treat PageUp/Dn as arrows because chances are that
2560 // a control which needs arrows also needs them for
2561 // navigation (e.g. wxTextCtrl, wxListCtrl, ...)
2562 if ( (lDlgCode & DLGC_WANTARROWS) && !bCtrlDown )
2563 bProcess = false;
2564 else // OTOH Ctrl-PageUp/Dn works as [Shift-]Ctrl-Tab
2565 bWindowChange = true;
2566 break;
2567
2568 case VK_RETURN:
2569 {
2570 #if wxUSE_BUTTON
2571 // currently active button should get enter press even
2572 // if there is a default button elsewhere so check if
2573 // this window is a button first
2574 wxButton *btn = NULL;
2575 if ( lDlgCode & DLGC_DEFPUSHBUTTON )
2576 {
2577 // let IsDialogMessage() handle this for all
2578 // buttons except the owner-drawn ones which it
2579 // just seems to ignore
2580 long style = ::GetWindowLong(msg->hwnd, GWL_STYLE);
2581 if ( (style & BS_OWNERDRAW) == BS_OWNERDRAW )
2582 {
2583 btn = wxDynamicCast
2584 (
2585 wxFindWinFromHandle(msg->hwnd),
2586 wxButton
2587 );
2588 }
2589 }
2590 else // not a button itself, do we have default button?
2591 {
2592 // check if this window or any of its ancestors
2593 // wants the message for itself (we always reserve
2594 // Ctrl-Enter for dialog navigation though)
2595 wxWindow *win = this;
2596 if ( !bCtrlDown )
2597 {
2598 // this will contain the dialog code of this
2599 // window and all of its parent windows in turn
2600 LONG lDlgCode2 = lDlgCode;
2601
2602 while ( win )
2603 {
2604 if ( lDlgCode2 & DLGC_WANTMESSAGE )
2605 {
2606 // as it wants to process Enter itself,
2607 // don't call IsDialogMessage() which
2608 // would consume it
2609 return false;
2610 }
2611
2612 // don't propagate keyboard messages beyond
2613 // the first top level window parent
2614 if ( win->IsTopLevel() )
2615 break;
2616
2617 win = win->GetParent();
2618
2619 lDlgCode2 = ::SendMessage
2620 (
2621 GetHwndOf(win),
2622 WM_GETDLGCODE,
2623 0,
2624 0
2625 );
2626 }
2627 }
2628
2629 btn = MSWGetDefaultButtonFor(win);
2630 }
2631
2632 if ( MSWClickButtonIfPossible(btn) )
2633 return true;
2634
2635 // This "Return" key press won't be actually used for
2636 // navigation so don't generate wxNavigationKeyEvent
2637 // for it but still pass it to IsDialogMessage() as it
2638 // may handle it in some other way (e.g. by playing the
2639 // default error sound).
2640 bProcess = false;
2641
2642 #endif // wxUSE_BUTTON
2643
2644 }
2645 break;
2646
2647 default:
2648 bProcess = false;
2649 }
2650
2651 if ( bProcess )
2652 {
2653 wxNavigationKeyEvent event;
2654 event.SetDirection(bForward);
2655 event.SetWindowChange(bWindowChange);
2656 event.SetFromTab(bFromTab);
2657 event.SetEventObject(this);
2658
2659 if ( HandleWindowEvent(event) )
2660 {
2661 // as we don't call IsDialogMessage(), which would take of
2662 // this by default, we need to manually send this message
2663 // so that controls can change their UI state if needed
2664 MSWUpdateUIState(UIS_CLEAR, UISF_HIDEFOCUS);
2665
2666 return true;
2667 }
2668 }
2669 }
2670
2671 if ( MSWSafeIsDialogMessage(msg) )
2672 {
2673 // IsDialogMessage() did something...
2674 return true;
2675 }
2676 }
2677 #else // __WXUNIVERSAL__
2678 wxUnusedVar(pMsg);
2679 #endif // !__WXUNIVERSAL__/__WXUNIVERSAL__
2680
2681 #if wxUSE_TOOLTIPS
2682 if ( m_tooltip )
2683 {
2684 // relay mouse move events to the tooltip control
2685 MSG *msg = (MSG *)pMsg;
2686 if ( msg->message == WM_MOUSEMOVE )
2687 wxToolTip::RelayEvent(pMsg);
2688 }
2689 #endif // wxUSE_TOOLTIPS
2690
2691 return false;
2692 }
2693
MSWTranslateMessage(WXMSG * pMsg)2694 bool wxWindowMSW::MSWTranslateMessage(WXMSG* pMsg)
2695 {
2696 #if wxUSE_ACCEL && !defined(__WXUNIVERSAL__)
2697 return m_acceleratorTable.Translate(this, pMsg);
2698 #else
2699 (void) pMsg;
2700 return false;
2701 #endif // wxUSE_ACCEL
2702 }
2703
MSWShouldPreProcessMessage(WXMSG * WXUNUSED (msg))2704 bool wxWindowMSW::MSWShouldPreProcessMessage(WXMSG* WXUNUSED(msg))
2705 {
2706 // We don't have any reason to not preprocess messages at this level.
2707 return true;
2708 }
2709
2710 #ifndef __WXUNIVERSAL__
2711
MSWSafeIsDialogMessage(WXMSG * msg)2712 bool wxWindowMSW::MSWSafeIsDialogMessage(WXMSG* msg)
2713 {
2714 // don't let IsDialogMessage() get VK_ESCAPE as it _always_ eats the
2715 // message even when there is no cancel button and when the message is
2716 // needed by the control itself: in particular, it prevents the tree in
2717 // place edit control from being closed with Escape in a dialog
2718 if ( msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE )
2719 {
2720 return false;
2721 }
2722
2723 // ::IsDialogMessage() is broken and may sometimes hang the application by
2724 // going into an infinite loop when it tries to find the control to give
2725 // focus to when Alt-<key> is pressed, so we try to detect [some of] the
2726 // situations when this may happen and not call it then
2727 if ( msg->message == WM_SYSCHAR )
2728 {
2729 HWND hwndFocus = ::GetFocus();
2730
2731 // if the currently focused window itself has WS_EX_CONTROLPARENT style,
2732 // ::IsDialogMessage() will also enter an infinite loop, because it will
2733 // recursively check the child windows but not the window itself and so if
2734 // none of the children accepts focus it loops forever (as it only stops
2735 // when it gets back to the window it started from)
2736 //
2737 // while it is very unusual that a window with WS_EX_CONTROLPARENT
2738 // style has the focus, it can happen. One such possibility is if
2739 // all windows are either toplevel, wxDialog, wxPanel or static
2740 // controls and no window can actually accept keyboard input.
2741 if ( ::GetWindowLong(hwndFocus, GWL_EXSTYLE) & WS_EX_CONTROLPARENT )
2742 {
2743 // pessimistic by default
2744 bool canSafelyCallIsDlgMsg = false;
2745 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
2746 node;
2747 node = node->GetNext() )
2748 {
2749 wxWindow * const win = node->GetData();
2750 if ( win->CanAcceptFocus() &&
2751 !wxHasWindowExStyle(win, WS_EX_CONTROLPARENT) )
2752 {
2753 // it shouldn't hang...
2754 canSafelyCallIsDlgMsg = true;
2755
2756 break;
2757 }
2758 }
2759
2760 if ( !canSafelyCallIsDlgMsg )
2761 return false;
2762 }
2763
2764 // ::IsDialogMessage() can enter in an infinite loop when the
2765 // currently focused window is disabled or hidden and its
2766 // parent has WS_EX_CONTROLPARENT style, so don't call it in
2767 // this case
2768 while ( hwndFocus )
2769 {
2770 if ( !::IsWindowEnabled(hwndFocus) ||
2771 !::IsWindowVisible(hwndFocus) )
2772 {
2773 // it would enter an infinite loop if we do this!
2774 return false;
2775 }
2776
2777 if ( !(::GetWindowLong(hwndFocus, GWL_STYLE) & WS_CHILD) )
2778 {
2779 // it's a top level window, don't go further -- e.g. even
2780 // if the parent of a dialog is disabled, this doesn't
2781 // break navigation inside the dialog
2782 break;
2783 }
2784
2785 hwndFocus = ::GetParent(hwndFocus);
2786 }
2787 }
2788
2789 return ::IsDialogMessage(GetHwnd(), msg) != 0;
2790 }
2791
2792 #endif // __WXUNIVERSAL__
2793
2794 /* static */
MSWGetDefaultButtonFor(wxWindow * win)2795 wxButton* wxWindowMSW::MSWGetDefaultButtonFor(wxWindow* win)
2796 {
2797 #if wxUSE_BUTTON
2798 win = wxGetTopLevelParent(win);
2799
2800 wxTopLevelWindow *const tlw = wxDynamicCast(win, wxTopLevelWindow);
2801 if ( tlw )
2802 return wxDynamicCast(tlw->GetDefaultItem(), wxButton);
2803 #endif // wxUSE_BUTTON
2804
2805 return NULL;
2806 }
2807
2808 /* static */
MSWClickButtonIfPossible(wxButton * btn)2809 bool wxWindowMSW::MSWClickButtonIfPossible(wxButton* btn)
2810 {
2811 #if wxUSE_BUTTON
2812 if ( btn && btn->IsEnabled() && btn->IsShownOnScreen() )
2813 {
2814 btn->MSWCommand(BN_CLICKED, 0 /* unused */);
2815 return true;
2816 }
2817 #endif // wxUSE_BUTTON
2818
2819 wxUnusedVar(btn);
2820
2821 return false;
2822 }
2823
2824 // ---------------------------------------------------------------------------
2825 // message params unpackers
2826 // ---------------------------------------------------------------------------
2827
UnpackCommand(WXWPARAM wParam,WXLPARAM lParam,WORD * id,WXHWND * hwnd,WORD * cmd)2828 void wxWindowMSW::UnpackCommand(WXWPARAM wParam, WXLPARAM lParam,
2829 WORD *id, WXHWND *hwnd, WORD *cmd)
2830 {
2831 *id = LOWORD(wParam);
2832 *hwnd = (WXHWND)lParam;
2833 *cmd = HIWORD(wParam);
2834 }
2835
UnpackActivate(WXWPARAM wParam,WXLPARAM lParam,WXWORD * state,WXWORD * minimized,WXHWND * hwnd)2836 void wxWindowMSW::UnpackActivate(WXWPARAM wParam, WXLPARAM lParam,
2837 WXWORD *state, WXWORD *minimized, WXHWND *hwnd)
2838 {
2839 *state = LOWORD(wParam);
2840 *minimized = HIWORD(wParam);
2841 *hwnd = (WXHWND)lParam;
2842 }
2843
UnpackScroll(WXWPARAM wParam,WXLPARAM lParam,WXWORD * code,WXWORD * pos,WXHWND * hwnd)2844 void wxWindowMSW::UnpackScroll(WXWPARAM wParam, WXLPARAM lParam,
2845 WXWORD *code, WXWORD *pos, WXHWND *hwnd)
2846 {
2847 *code = LOWORD(wParam);
2848 *pos = HIWORD(wParam);
2849 *hwnd = (WXHWND)lParam;
2850 }
2851
UnpackCtlColor(WXWPARAM wParam,WXLPARAM lParam,WXHDC * hdc,WXHWND * hwnd)2852 void wxWindowMSW::UnpackCtlColor(WXWPARAM wParam, WXLPARAM lParam,
2853 WXHDC *hdc, WXHWND *hwnd)
2854 {
2855 *hwnd = (WXHWND)lParam;
2856 *hdc = (WXHDC)wParam;
2857 }
2858
UnpackMenuSelect(WXWPARAM wParam,WXLPARAM lParam,WXWORD * item,WXWORD * flags,WXHMENU * hmenu)2859 void wxWindowMSW::UnpackMenuSelect(WXWPARAM wParam, WXLPARAM lParam,
2860 WXWORD *item, WXWORD *flags, WXHMENU *hmenu)
2861 {
2862 *item = (WXWORD)wParam;
2863 *flags = HIWORD(wParam);
2864 *hmenu = (WXHMENU)lParam;
2865 }
2866
2867 // ---------------------------------------------------------------------------
2868 // Main wxWidgets window proc and the window proc for wxWindow
2869 // ---------------------------------------------------------------------------
2870
2871 // Hook for new window just as it's being created, when the window isn't yet
2872 // associated with the handle
2873 static wxWindowMSW *gs_winBeingCreated = NULL;
2874
2875 // implementation of wxWindowCreationHook class: it just sets gs_winBeingCreated to the
2876 // window being created and insures that it's always unset back later
wxWindowCreationHook(wxWindowMSW * winBeingCreated)2877 wxWindowCreationHook::wxWindowCreationHook(wxWindowMSW *winBeingCreated)
2878 {
2879 gs_winBeingCreated = winBeingCreated;
2880 }
2881
~wxWindowCreationHook()2882 wxWindowCreationHook::~wxWindowCreationHook()
2883 {
2884 gs_winBeingCreated = NULL;
2885 }
2886
2887 // Main window proc
2888 LRESULT WXDLLEXPORT APIENTRY
wxWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)2889 wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2890 {
2891 // trace all messages: useful for the debugging but noticeably slows down
2892 // the code so don't do it by default
2893 #if wxDEBUG_LEVEL >= 2
2894 // We have to do this inside a helper function as wxLogTrace() constructs
2895 // an object internally, but objects can't be used in functions using __try
2896 // (expanded from wxSEH_TRY below) with MSVC.
2897 wxTraceMSWMessage(hWnd, message, wParam, lParam);
2898 #endif // wxDEBUG_LEVEL >= 2
2899
2900 wxWindowMSW *wnd = wxFindWinFromHandle(hWnd);
2901
2902 // when we get the first message for the HWND we just created, we associate
2903 // it with wxWindow stored in gs_winBeingCreated
2904 if ( !wnd && gs_winBeingCreated )
2905 {
2906 wxAssociateWinWithHandle(hWnd, gs_winBeingCreated);
2907 wnd = gs_winBeingCreated;
2908 gs_winBeingCreated = NULL;
2909 wnd->SetHWND((WXHWND)hWnd);
2910 }
2911
2912 LRESULT rc;
2913
2914 // We have to catch unhandled Win32 exceptions here because otherwise they
2915 // would be simply lost if we're called from a kernel callback (as it
2916 // happens when we process WM_PAINT, for example under WOW64: the 32 bit
2917 // exceptions can't pass through the 64 bit kernel in this case and so are
2918 // mostly just suppressed, although the exact behaviour differs across
2919 // Windows versions, see the "Remarks" section of WindowProc documentation
2920 // at https://msdn.microsoft.com/en-us/library/ms633573.aspx
2921 wxSEH_TRY
2922 {
2923 if ( wnd && wxGUIEventLoop::AllowProcessing(wnd) )
2924 rc = wnd->MSWWindowProc(message, wParam, lParam);
2925 else
2926 rc = ::DefWindowProc(hWnd, message, wParam, lParam);
2927 }
2928 wxSEH_HANDLE(0)
2929
2930 return rc;
2931 }
2932
2933 bool
MSWHandleMessage(WXLRESULT * result,WXUINT message,WXWPARAM wParam,WXLPARAM lParam)2934 wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
2935 WXUINT message,
2936 WXWPARAM wParam,
2937 WXLPARAM lParam)
2938 {
2939 // did we process the message?
2940 bool processed = false;
2941
2942 // the return value
2943 union
2944 {
2945 bool allow;
2946 WXLRESULT result;
2947 WXHBRUSH hBrush;
2948 } rc;
2949
2950 // for most messages we should return 0 when we do process the message
2951 rc.result = 0;
2952
2953 // Special hook for dismissing the current popup if it's active. It's a bit
2954 // ugly to have to do this here, but the only alternatives seem to be
2955 // installing a WH_CBT hook in wxPopupTransientWindow code, which is not
2956 // really much better.
2957 #if wxUSE_POPUPWIN
2958 // Note that we let the popup window, or its child, have the event if it
2959 // happens inside it -- it's supposed to react to it and we don't want to
2960 // dismiss it before it can do it.
2961 if ( wxCurrentPopupWindow && !wxCurrentPopupWindow->IsDescendant(this) )
2962 {
2963 switch ( message )
2964 {
2965 case WM_NCLBUTTONDOWN:
2966 case WM_NCRBUTTONDOWN:
2967 case WM_NCMBUTTONDOWN:
2968
2969 case WM_LBUTTONDOWN:
2970 case WM_RBUTTONDOWN:
2971 case WM_MBUTTONDOWN:
2972
2973 case WM_SETFOCUS:
2974 case WM_KILLFOCUS:
2975 wxCurrentPopupWindow->MSWDismissUnfocusedPopup();
2976 }
2977 }
2978 #endif // wxUSE_POPUPWIN
2979
2980 switch ( message )
2981 {
2982 case WM_CREATE:
2983 {
2984 bool mayCreate;
2985 processed = HandleCreate((WXLPCREATESTRUCT)lParam, &mayCreate);
2986 if ( processed )
2987 {
2988 // return 0 to allow window creation
2989 rc.result = mayCreate ? 0 : -1;
2990 }
2991 }
2992 break;
2993
2994 case WM_DESTROY:
2995 // never set processed to true and *always* pass WM_DESTROY to
2996 // DefWindowProc() as Windows may do some internal cleanup when
2997 // processing it and failing to pass the message along may cause
2998 // memory and resource leaks!
2999 (void)HandleDestroy();
3000 break;
3001
3002 case WM_SIZE:
3003 processed = HandleSize(LOWORD(lParam), HIWORD(lParam), wParam);
3004 break;
3005
3006 case WM_MOVE:
3007 processed = HandleMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3008 break;
3009
3010 case WM_MOVING:
3011 {
3012 LPRECT pRect = (LPRECT)lParam;
3013 wxRect rect;
3014 rect.SetLeft(pRect->left);
3015 rect.SetTop(pRect->top);
3016 rect.SetRight(pRect->right);
3017 rect.SetBottom(pRect->bottom);
3018 processed = HandleMoving(rect);
3019 if (processed) {
3020 pRect->left = rect.GetLeft();
3021 pRect->top = rect.GetTop();
3022 pRect->right = rect.GetRight();
3023 pRect->bottom = rect.GetBottom();
3024 }
3025 }
3026 break;
3027
3028 case WM_ENTERSIZEMOVE:
3029 {
3030 processed = HandleEnterSizeMove();
3031 }
3032 break;
3033
3034 case WM_EXITSIZEMOVE:
3035 {
3036 processed = HandleExitSizeMove();
3037 }
3038 break;
3039
3040 case WM_SIZING:
3041 {
3042 LPRECT pRect = (LPRECT)lParam;
3043 wxRect rect;
3044 rect.SetLeft(pRect->left);
3045 rect.SetTop(pRect->top);
3046 rect.SetRight(pRect->right);
3047 rect.SetBottom(pRect->bottom);
3048 processed = HandleSizing(rect);
3049 if (processed) {
3050 pRect->left = rect.GetLeft();
3051 pRect->top = rect.GetTop();
3052 pRect->right = rect.GetRight();
3053 pRect->bottom = rect.GetBottom();
3054 }
3055 }
3056 break;
3057
3058 case WM_ACTIVATEAPP:
3059 // This implicitly sends a wxEVT_ACTIVATE_APP event
3060 wxTheApp->SetActive(wParam != 0, FindFocus());
3061 break;
3062
3063 case WM_ACTIVATE:
3064 {
3065 WXWORD state, minimized;
3066 WXHWND hwnd;
3067 UnpackActivate(wParam, lParam, &state, &minimized, &hwnd);
3068
3069 processed = HandleActivate(state, minimized != 0, (WXHWND)hwnd);
3070 }
3071 break;
3072
3073 case WM_SETFOCUS:
3074 processed = HandleSetFocus((WXHWND)wParam);
3075 break;
3076
3077 case WM_KILLFOCUS:
3078 processed = HandleKillFocus((WXHWND)wParam);
3079 break;
3080
3081 case WM_PRINTCLIENT:
3082 processed = HandlePrintClient((WXHDC)wParam);
3083 break;
3084
3085 case WM_PAINT:
3086 if ( wParam )
3087 {
3088 wxPaintDCEx dc((wxWindow *)this, (WXHDC)wParam);
3089
3090 processed = HandlePaint();
3091 }
3092 else // no DC given
3093 {
3094 processed = HandlePaint();
3095 }
3096 break;
3097
3098 case WM_CLOSE:
3099 #ifdef __WXUNIVERSAL__
3100 // Universal uses its own wxFrame/wxDialog, so we don't receive
3101 // close events unless we have this.
3102 Close();
3103 #endif // __WXUNIVERSAL__
3104
3105 // don't let the DefWindowProc() destroy our window - we'll do it
3106 // ourselves in ~wxWindow
3107 processed = true;
3108 rc.result = TRUE;
3109 break;
3110
3111 case WM_SHOWWINDOW:
3112 processed = HandleShow(wParam != 0, (int)lParam);
3113 break;
3114
3115 case WM_MOUSEMOVE:
3116 processed = HandleMouseMove(GET_X_LPARAM(lParam),
3117 GET_Y_LPARAM(lParam),
3118 wParam);
3119 break;
3120
3121 #ifdef HAVE_TRACKMOUSEEVENT
3122 case WM_MOUSELEAVE:
3123 // filter out excess WM_MOUSELEAVE events sent after PopupMenu()
3124 // (on XP at least)
3125 if ( m_mouseInWindow )
3126 {
3127 GenerateMouseLeave();
3128 }
3129
3130 // always pass processed back as false, this allows the window
3131 // manager to process the message too. This is needed to
3132 // ensure windows XP themes work properly as the mouse moves
3133 // over widgets like buttons. So don't set processed to true here.
3134 break;
3135 #endif // HAVE_TRACKMOUSEEVENT
3136
3137 #if wxUSE_MOUSEWHEEL
3138 case WM_MOUSEWHEEL:
3139 processed = HandleMouseWheel(wxMOUSE_WHEEL_VERTICAL, wParam, lParam);
3140 break;
3141
3142 case WM_MOUSEHWHEEL:
3143 processed = HandleMouseWheel(wxMOUSE_WHEEL_HORIZONTAL, wParam, lParam);
3144 break;
3145 #endif // wxUSE_MOUSEWHEEL
3146
3147 case WM_LBUTTONDOWN:
3148 case WM_LBUTTONUP:
3149 case WM_LBUTTONDBLCLK:
3150 case WM_RBUTTONDOWN:
3151 case WM_RBUTTONUP:
3152 case WM_RBUTTONDBLCLK:
3153 case WM_MBUTTONDOWN:
3154 case WM_MBUTTONUP:
3155 case WM_MBUTTONDBLCLK:
3156 case WM_XBUTTONDOWN:
3157 case WM_XBUTTONUP:
3158 case WM_XBUTTONDBLCLK:
3159 {
3160 int x = GET_X_LPARAM(lParam),
3161 y = GET_Y_LPARAM(lParam);
3162
3163 wxWindowMSW *win = this;
3164
3165 processed = win->HandleMouseEvent(message, x, y, wParam);
3166
3167 // if the app didn't eat the event, handle it in the default
3168 // way, that is by giving this window the focus
3169 if ( !processed )
3170 {
3171 // for the standard classes their WndProc sets the focus to
3172 // them anyhow and doing it from here results in some weird
3173 // problems, so don't do it for them (unnecessary anyhow)
3174 if ( !win->IsOfStandardClass() )
3175 {
3176 if ( message == WM_LBUTTONDOWN && win->IsFocusable() )
3177 win->SetFocus();
3178 }
3179 }
3180 }
3181 break;
3182
3183
3184 case WM_COMMAND:
3185 {
3186 WORD id, cmd;
3187 WXHWND hwnd;
3188 UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
3189
3190 processed = HandleCommand(id, cmd, hwnd);
3191 }
3192 break;
3193
3194 case WM_NOTIFY:
3195 processed = HandleNotify((int)wParam, lParam, &rc.result);
3196 break;
3197
3198 // for these messages we must return true if process the message
3199 #ifdef WM_DRAWITEM
3200 case WM_DRAWITEM:
3201 processed = MSWOnDrawItem(wParam, (WXDRAWITEMSTRUCT *)lParam);
3202 if ( processed )
3203 rc.result = TRUE;
3204 break;
3205
3206 case WM_MEASUREITEM:
3207 processed = MSWOnMeasureItem(wParam, (WXMEASUREITEMSTRUCT *)lParam);
3208 if ( processed )
3209 rc.result = TRUE;
3210 break;
3211 #endif // defined(WM_DRAWITEM)
3212
3213 case WM_GETDLGCODE:
3214 if ( !IsOfStandardClass() || HasFlag(wxWANTS_CHARS) )
3215 {
3216 // Get current input processing flags to retain flags like DLGC_HASSETSEL, etc.
3217 rc.result = MSWDefWindowProc(WM_GETDLGCODE, 0, 0);
3218 // we always want to get the char events
3219 rc.result |= DLGC_WANTCHARS;
3220
3221 if ( HasFlag(wxWANTS_CHARS) )
3222 {
3223 // in fact, we want everything
3224 rc.result |= DLGC_WANTARROWS |
3225 DLGC_WANTTAB |
3226 DLGC_WANTALLKEYS;
3227 }
3228
3229 // Message is marked as processed so MSWDefWindowProc() will not be called once more.
3230 processed = true;
3231 }
3232 //else: get the dlg code from the DefWindowProc()
3233 break;
3234
3235 case WM_SYSKEYDOWN:
3236 case WM_KEYDOWN:
3237 // Generate the key down event in any case.
3238 m_lastKeydownProcessed = HandleKeyDown((WORD) wParam, lParam);
3239 if ( m_lastKeydownProcessed )
3240 {
3241 // If it was processed by an event handler, we stop here,
3242 // notably we intentionally don't generate char event then.
3243 processed = true;
3244 }
3245 else // key down event not handled
3246 {
3247 // Examine the event to decide whether we need to generate a
3248 // char event for it ourselves or let Windows do it. Window
3249 // mostly only does it for the keys which produce printable
3250 // characters (although there are exceptions, e.g. VK_ESCAPE or
3251 // VK_BACK (but not VK_DELETE)) while we do it for all keys
3252 // except the modifier ones (the wisdom of this is debatable
3253 // but by now this decision is enshrined forever due to
3254 // backwards compatibility).
3255 switch ( wParam )
3256 {
3257 // No wxEVT_CHAR events are generated for these keys at all.
3258 case VK_SHIFT:
3259 case VK_CONTROL:
3260 case VK_MENU:
3261 case VK_CAPITAL:
3262 case VK_NUMLOCK:
3263 case VK_SCROLL:
3264
3265 // Windows will send us WM_CHAR for these ones so we'll
3266 // generate wxEVT_CHAR for them later when we get it.
3267 case VK_ESCAPE:
3268 case VK_SPACE:
3269 case VK_RETURN:
3270 case VK_BACK:
3271 case VK_TAB:
3272 case VK_ADD:
3273 case VK_SUBTRACT:
3274 case VK_MULTIPLY:
3275 case VK_DIVIDE:
3276 case VK_DECIMAL:
3277 case VK_NUMPAD0:
3278 case VK_NUMPAD1:
3279 case VK_NUMPAD2:
3280 case VK_NUMPAD3:
3281 case VK_NUMPAD4:
3282 case VK_NUMPAD5:
3283 case VK_NUMPAD6:
3284 case VK_NUMPAD7:
3285 case VK_NUMPAD8:
3286 case VK_NUMPAD9:
3287 case VK_OEM_1:
3288 case VK_OEM_2:
3289 case VK_OEM_3:
3290 case VK_OEM_4:
3291 case VK_OEM_5:
3292 case VK_OEM_6:
3293 case VK_OEM_7:
3294 case VK_OEM_8:
3295 case VK_OEM_102:
3296 case VK_OEM_PLUS:
3297 case VK_OEM_COMMA:
3298 case VK_OEM_MINUS:
3299 case VK_OEM_PERIOD:
3300 break;
3301
3302 default:
3303 if ( (wParam >= '0' && wParam <= '9') ||
3304 (wParam >= 'A' && wParam <= 'Z') )
3305 {
3306 // We'll get WM_CHAR for those later too.
3307 break;
3308 }
3309
3310 // But for the rest we won't get WM_CHAR later so we do
3311 // need to generate the event right now.
3312 wxKeyEvent event(wxEVT_CHAR);
3313 InitAnyKeyEvent(event, wParam, lParam);
3314
3315 // Set the "extended" bit in lParam because we want to
3316 // generate CHAR events with WXK_HOME and not
3317 // WXK_NUMPAD_HOME even if the "Home" key on numpad was
3318 // pressed.
3319 event.m_keyCode = wxMSWKeyboard::VKToWX
3320 (
3321 wParam,
3322 lParam | (KF_EXTENDED << 16)
3323 );
3324
3325 // Don't produce events without any valid character
3326 // code (even if this shouldn't normally happen...).
3327 if ( event.m_keyCode != WXK_NONE )
3328 processed = HandleWindowEvent(event);
3329 }
3330 }
3331 break;
3332
3333 case WM_SYSKEYUP:
3334 case WM_KEYUP:
3335 processed = HandleKeyUp((WORD) wParam, lParam);
3336 break;
3337
3338 case WM_SYSCHAR:
3339 case WM_CHAR: // Always an ASCII character
3340 if ( m_lastKeydownProcessed )
3341 {
3342 // The key was handled in the EVT_KEY_DOWN and handling
3343 // a key in an EVT_KEY_DOWN handler is meant, by
3344 // design, to prevent EVT_CHARs from happening
3345 m_lastKeydownProcessed = false;
3346 processed = true;
3347 }
3348 else
3349 {
3350 processed = HandleChar((WORD)wParam, lParam);
3351 }
3352 break;
3353
3354 case WM_IME_STARTCOMPOSITION:
3355 // IME popup needs Escape as it should undo the changes in its
3356 // entry window instead of e.g. closing the dialog for which the
3357 // IME is used (and losing all the changes in the IME window).
3358 gs_modalEntryWindowCount++;
3359 break;
3360
3361 case WM_IME_ENDCOMPOSITION:
3362 gs_modalEntryWindowCount--;
3363 break;
3364
3365 #if wxUSE_HOTKEY
3366 case WM_HOTKEY:
3367 processed = HandleHotKey(wParam, lParam);
3368 break;
3369 #endif // wxUSE_HOTKEY
3370
3371 case WM_CUT:
3372 case WM_COPY:
3373 case WM_PASTE:
3374 processed = HandleClipboardEvent(message);
3375 break;
3376
3377 case WM_HSCROLL:
3378 case WM_VSCROLL:
3379 {
3380 WXWORD code, pos;
3381 WXHWND hwnd;
3382 UnpackScroll(wParam, lParam, &code, &pos, &hwnd);
3383
3384 processed = MSWOnScroll(message == WM_HSCROLL ? wxHORIZONTAL
3385 : wxVERTICAL,
3386 code, pos, hwnd);
3387 }
3388 break;
3389
3390 #ifdef WM_GESTURE
3391 case WM_GESTURE:
3392 {
3393 if ( !GestureFuncs::IsOk() )
3394 break;
3395
3396 HGESTUREINFO hGestureInfo = reinterpret_cast<HGESTUREINFO>(lParam);
3397
3398 WinStruct<GESTUREINFO> gestureInfo;
3399 if ( !GestureFuncs::GetGestureInfo()(hGestureInfo, &gestureInfo) )
3400 {
3401 wxLogLastError("GetGestureInfo");
3402 break;
3403 }
3404
3405 if ( gestureInfo.hwndTarget != GetHWND() )
3406 {
3407 wxLogDebug("This is Not the window targeted by this gesture!");
3408 }
3409
3410 const wxPoint pt = ScreenToClient
3411 (
3412 wxPoint(gestureInfo.ptsLocation.x,
3413 gestureInfo.ptsLocation.y)
3414 );
3415
3416 // dwID field is used to determine the type of gesture
3417 switch ( gestureInfo.dwID )
3418 {
3419 case GID_PAN:
3420 // Point contains the current position of the pan.
3421 processed = HandlePanGesture(pt, gestureInfo.dwFlags);
3422 break;
3423
3424 case GID_ZOOM:
3425 // Point is the mid-point of 2 fingers and ullArgument
3426 // contains the distance between the fingers in its lower
3427 // half
3428 processed = HandleZoomGesture
3429 (
3430 pt,
3431 static_cast<DWORD>(gestureInfo.ullArguments),
3432 gestureInfo.dwFlags
3433 );
3434 break;
3435
3436 case GID_ROTATE:
3437 // Point is the center point of rotation and ullArguments
3438 // contains the angle of rotation
3439 processed = HandleRotateGesture
3440 (
3441 pt,
3442 static_cast<DWORD>(gestureInfo.ullArguments),
3443 gestureInfo.dwFlags
3444 );
3445 break;
3446
3447 case GID_TWOFINGERTAP:
3448 processed = HandleTwoFingerTap(pt, gestureInfo.dwFlags);
3449 break;
3450
3451 case GID_PRESSANDTAP:
3452 processed = HandlePressAndTap(pt, gestureInfo.dwFlags);
3453 break;
3454 }
3455
3456 if ( processed )
3457 {
3458 // If processed, we must call this to avoid memory leaks
3459 if ( !GestureFuncs::CloseGestureInfoHandle()(hGestureInfo) )
3460 {
3461 wxLogLastError("CloseGestureInfoHandle");
3462 }
3463 }
3464 }
3465 break;
3466 #endif // WM_GESTURE
3467
3468 // CTLCOLOR messages are sent by children to query the parent for their
3469 // colors
3470 case WM_CTLCOLORMSGBOX:
3471 case WM_CTLCOLOREDIT:
3472 case WM_CTLCOLORLISTBOX:
3473 case WM_CTLCOLORBTN:
3474 case WM_CTLCOLORDLG:
3475 case WM_CTLCOLORSCROLLBAR:
3476 case WM_CTLCOLORSTATIC:
3477 {
3478 WXHDC hdc;
3479 WXHWND hwnd;
3480 UnpackCtlColor(wParam, lParam, &hdc, &hwnd);
3481
3482 processed = HandleCtlColor(&rc.hBrush, (WXHDC)hdc, (WXHWND)hwnd);
3483 }
3484 break;
3485
3486 case WM_SYSCOLORCHANGE:
3487 // the return value for this message is ignored
3488 processed = HandleSysColorChange();
3489 break;
3490
3491 case WM_DISPLAYCHANGE:
3492 processed = HandleDisplayChange();
3493 break;
3494
3495 case WM_PALETTECHANGED:
3496 processed = HandlePaletteChanged((WXHWND)wParam);
3497 break;
3498
3499 case WM_CAPTURECHANGED:
3500 processed = HandleCaptureChanged((WXHWND)lParam);
3501 break;
3502
3503 case WM_SETTINGCHANGE:
3504 processed = HandleSettingChange(wParam, lParam);
3505 break;
3506
3507 case WM_QUERYNEWPALETTE:
3508 processed = HandleQueryNewPalette();
3509 break;
3510
3511 case WM_ERASEBKGND:
3512 {
3513 #ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
3514 // check if an override was configured for this window
3515 EraseBgHooks::const_iterator it = gs_eraseBgHooks.find(this);
3516 if ( it != gs_eraseBgHooks.end() )
3517 processed = it->second->MSWEraseBgHook((WXHDC)wParam);
3518 else
3519 #endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
3520 processed = HandleEraseBkgnd((WXHDC)wParam);
3521 }
3522
3523 if ( processed )
3524 {
3525 // we processed the message, i.e. erased the background
3526 rc.result = TRUE;
3527 }
3528 break;
3529
3530 case WM_DROPFILES:
3531 processed = HandleDropFiles(wParam);
3532 break;
3533
3534 case WM_INITDIALOG:
3535 processed = HandleInitDialog((WXHWND)wParam);
3536
3537 if ( processed )
3538 {
3539 // we never set focus from here
3540 rc.result = FALSE;
3541 }
3542 break;
3543
3544 case WM_QUERYENDSESSION:
3545 processed = HandleQueryEndSession(lParam, &rc.allow);
3546 break;
3547
3548 case WM_ENDSESSION:
3549 processed = HandleEndSession(wParam != 0, lParam);
3550 break;
3551
3552 case WM_GETMINMAXINFO:
3553 processed = HandleGetMinMaxInfo((MINMAXINFO*)lParam);
3554 break;
3555
3556 case WM_SETCURSOR:
3557 processed = HandleSetCursor((WXHWND)wParam,
3558 LOWORD(lParam), // hit test
3559 HIWORD(lParam)); // mouse msg
3560
3561 if ( processed )
3562 {
3563 // returning TRUE stops the DefWindowProc() from further
3564 // processing this message - exactly what we need because we've
3565 // just set the cursor.
3566 rc.result = TRUE;
3567 }
3568 break;
3569
3570 #if wxUSE_ACCESSIBILITY
3571 case WM_GETOBJECT:
3572 {
3573 //WPARAM dwFlags = (WPARAM) (DWORD) wParam;
3574 LPARAM dwObjId = (LPARAM) (DWORD) lParam;
3575
3576 if (dwObjId == (LPARAM)OBJID_CLIENT && GetOrCreateAccessible())
3577 {
3578 processed = true;
3579 rc.result = LresultFromObject(IID_IAccessible, wParam, (IUnknown*) GetAccessible()->GetIAccessible());
3580 }
3581 break;
3582 }
3583 #endif
3584
3585 case WM_HELP:
3586 {
3587 // by default, WM_HELP is propagated by DefWindowProc() upwards
3588 // to the window parent but as we do it ourselves already
3589 // (wxHelpEvent is derived from wxCommandEvent), we don't want
3590 // to get the other events if we process this message at all
3591 processed = true;
3592
3593 // WM_HELP doesn't use lParam under CE
3594 HELPINFO* info = (HELPINFO*) lParam;
3595 if ( info->iContextType == HELPINFO_WINDOW )
3596 {
3597 wxHelpEvent helpEvent
3598 (
3599 wxEVT_HELP,
3600 GetId(),
3601 wxPoint(info->MousePos.x, info->MousePos.y)
3602 );
3603
3604 helpEvent.SetEventObject(this);
3605 HandleWindowEvent(helpEvent);
3606 }
3607 else if ( info->iContextType == HELPINFO_MENUITEM )
3608 {
3609 wxHelpEvent helpEvent(wxEVT_HELP, info->iCtrlId);
3610 helpEvent.SetEventObject(this);
3611 HandleWindowEvent(helpEvent);
3612
3613 }
3614 else // unknown help event?
3615 {
3616 processed = false;
3617 }
3618 }
3619 break;
3620
3621 case WM_CONTEXTMENU:
3622 {
3623 // As with WM_HELP above, we need to avoid duplicate events due
3624 // to wxContextMenuEvent being a (propagatable) wxCommandEvent
3625 // at wx level but WM_CONTEXTMENU also being propagated upwards
3626 // by DefWindowProc(). Unlike WM_HELP, we still need to pass
3627 // this one to DefWindowProc() as it sometimes does useful
3628 // things with it, e.g. displays the default context menu in
3629 // EDIT controls. So we do let the default processing to take
3630 // place but set this flag before calling into DefWindowProc()
3631 // and don't do anything if we're called from inside it.
3632 static bool s_propagatedByDefWndProc = false;
3633 if ( s_propagatedByDefWndProc )
3634 {
3635 // We could also return false from here, it shouldn't
3636 // matter, the important thing is to not send any events.
3637 // But returning true prevents the message from bubbling up
3638 // even further upwards and so seems to be better.
3639 processed = true;
3640 break;
3641 }
3642
3643 // we don't convert from screen to client coordinates as
3644 // the event may be handled by a parent window
3645 wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3646
3647 // we could have got an event from our child, reflect it back
3648 // to it if this is the case
3649 wxWindowMSW *win = NULL;
3650 WXHWND hWnd = (WXHWND)wParam;
3651 if ( hWnd != m_hWnd )
3652 {
3653 win = FindItemByHWND(hWnd);
3654 }
3655
3656 if ( !win )
3657 win = this;
3658
3659 processed = win->WXSendContextMenuEvent(pt);
3660
3661 if ( !processed )
3662 {
3663 // Temporarily set the flag before calling out.
3664 s_propagatedByDefWndProc = true;
3665 wxON_BLOCK_EXIT_SET(s_propagatedByDefWndProc, false);
3666
3667 // Now do whatever the default handling does, which could
3668 // be nothing at all -- but we can't know this, so we still
3669 // need to call it.
3670 win->MSWDefWindowProc(message, wParam, lParam);
3671
3672 // And finally pretend that we processed the message in any
3673 // case because otherwise DefWindowProc() that we're called
3674 // from would pass the message to our parent resulting in
3675 // duplicate events. As it is, we ensure that only one
3676 // wxWindow ever gets this message for any given click.
3677 processed = true;
3678 }
3679 }
3680 break;
3681
3682 #if wxUSE_MENUS && !defined(__WXUNIVERSAL__)
3683 case WM_MENUCHAR:
3684 // we're only interested in our own menus, not MF_SYSMENU
3685 if ( HIWORD(wParam) == MF_POPUP )
3686 {
3687 // handle menu chars for ownerdrawn menu items
3688 int i = HandleMenuChar(toupper(LOWORD(wParam)), lParam);
3689 if ( i != wxNOT_FOUND )
3690 {
3691 rc.result = MAKELRESULT(i, MNC_EXECUTE);
3692 processed = true;
3693 }
3694 }
3695 break;
3696
3697 case WM_INITMENUPOPUP:
3698 processed = HandleMenuPopup(wxEVT_MENU_OPEN, (WXHMENU)wParam);
3699 break;
3700
3701 case WM_MENUSELECT:
3702 {
3703 WXWORD item, flags;
3704 WXHMENU hmenu;
3705 UnpackMenuSelect(wParam, lParam, &item, &flags, &hmenu);
3706
3707 processed = HandleMenuSelect(item, flags, hmenu);
3708 }
3709 break;
3710
3711 case WM_UNINITMENUPOPUP:
3712 processed = HandleMenuPopup(wxEVT_MENU_CLOSE, (WXHMENU)wParam);
3713 break;
3714 #endif // wxUSE_MENUS && !defined(__WXUNIVERSAL__)
3715
3716 case WM_POWERBROADCAST:
3717 {
3718 bool vetoed;
3719 processed = HandlePower(wParam, lParam, &vetoed);
3720 rc.result = processed && vetoed ? BROADCAST_QUERY_DENY : TRUE;
3721 }
3722 break;
3723
3724 #if wxUSE_POPUPWIN
3725 case WM_NCACTIVATE:
3726 // When we're losing activation to our own popup window, we want to
3727 // retain the "active" appearance of the title bar, as dropping
3728 // down a combobox popup shouldn't deactivate the window containing
3729 // the combobox, for example. Explicitly calling DefWindowProc() to
3730 // draw the window as active seems to be the only way of achieving
3731 // this (thanks to Barmak Shemirani for suggesting it at
3732 // https://stackoverflow.com/a/52808753/15275).
3733 if ( !wParam &&
3734 wxCurrentPopupWindow &&
3735 wxCurrentPopupWindow->MSWGetOwner() == this )
3736 {
3737 rc.result = MSWDefWindowProc(message, TRUE, lParam);
3738 processed = true;
3739 }
3740 break;
3741 #endif
3742
3743 #if wxUSE_UXTHEME
3744 // If we want the default themed border then we need to draw it ourselves
3745 case WM_NCCALCSIZE:
3746 {
3747 const wxBorder border = TranslateBorder(GetBorder());
3748 if (wxUxThemeIsActive() && border == wxBORDER_THEME)
3749 {
3750 // first ask the widget to calculate the border size
3751 rc.result = MSWDefWindowProc(message, wParam, lParam);
3752 processed = true;
3753
3754 // now alter the client size making room for drawing a
3755 // themed border
3756 RECT *rect;
3757 NCCALCSIZE_PARAMS *csparam = NULL;
3758 if ( wParam )
3759 {
3760 csparam = (NCCALCSIZE_PARAMS *)lParam;
3761 rect = &csparam->rgrc[0];
3762 }
3763 else
3764 {
3765 rect = (RECT *)lParam;
3766 }
3767
3768 wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT");
3769
3770 // There is no need to initialize rcClient: either it will
3771 // be done by GetThemeBackgroundContentRect() or we'll do
3772 // it below if it fails.
3773 RECT rcClient;
3774
3775 wxClientDC dc((wxWindow *)this);
3776 wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
3777
3778 if ( ::GetThemeBackgroundContentRect
3779 (
3780 hTheme,
3781 GetHdcOf(*impl),
3782 EP_EDITTEXT,
3783 IsEnabled() ? ETS_NORMAL : ETS_DISABLED,
3784 rect,
3785 &rcClient) != S_OK )
3786 {
3787 // If GetThemeBackgroundContentRect() failed, as can
3788 // happen with at least some custom themes, just use
3789 // the original client rectangle.
3790 rcClient = *rect;
3791 }
3792
3793 InflateRect(&rcClient, -1, -1);
3794 if (wParam)
3795 csparam->rgrc[0] = rcClient;
3796 else
3797 *((RECT*)lParam) = rcClient;
3798
3799 // WVR_REDRAW triggers a bug whereby child windows are moved up and left,
3800 // so don't use.
3801 // rc.result = WVR_REDRAW;
3802 }
3803 }
3804 break;
3805
3806 case WM_NCPAINT:
3807 {
3808 const wxBorder border = TranslateBorder(GetBorder());
3809 if (wxUxThemeIsActive() && border == wxBORDER_THEME)
3810 {
3811 // first ask the widget to paint its non-client area, such as scrollbars, etc.
3812 rc.result = MSWDefWindowProc(message, wParam, lParam);
3813 processed = true;
3814
3815 wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT");
3816 wxWindowDC dc((wxWindow *)this);
3817 wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
3818
3819 // Clip the DC so that you only draw on the non-client area
3820 RECT rcBorder;
3821 wxCopyRectToRECT(GetSize(), rcBorder);
3822
3823 RECT rcClient;
3824
3825 const int nState = IsEnabled() ? ETS_NORMAL : ETS_DISABLED;
3826
3827 if ( ::GetThemeBackgroundContentRect
3828 (
3829 hTheme,
3830 GetHdcOf(*impl),
3831 EP_EDITTEXT,
3832 nState,
3833 &rcBorder,
3834 &rcClient
3835 ) != S_OK )
3836 {
3837 // As above in WM_NCCALCSIZE, fall back on something
3838 // reasonable for themes which don't implement this
3839 // function.
3840 rcClient = rcBorder;
3841 }
3842
3843 InflateRect(&rcClient, -1, -1);
3844
3845 ::ExcludeClipRect(GetHdcOf(*impl), rcClient.left, rcClient.top,
3846 rcClient.right, rcClient.bottom);
3847
3848 // Make sure the background is in a proper state
3849 if (::IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, nState))
3850 {
3851 ::DrawThemeParentBackground(GetHwnd(), GetHdcOf(*impl), &rcBorder);
3852 }
3853
3854 // Draw the border
3855 ::DrawThemeBackground(hTheme, GetHdcOf(*impl), EP_EDITTEXT, nState, &rcBorder, NULL);
3856 }
3857 }
3858 break;
3859
3860 #endif // wxUSE_UXTHEME
3861
3862 default:
3863 // try a custom message handler
3864 const MSWMessageHandlers::const_iterator
3865 i = gs_messageHandlers.find(message);
3866 if ( i != gs_messageHandlers.end() )
3867 {
3868 processed = (*i->second)(this, message, wParam, lParam);
3869 }
3870 }
3871
3872 if ( !processed )
3873 return false;
3874
3875 *result = rc.result;
3876
3877 return true;
3878 }
3879
MSWWindowProc(WXUINT message,WXWPARAM wParam,WXLPARAM lParam)3880 WXLRESULT wxWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
3881 {
3882 WXLRESULT result;
3883 if ( !MSWHandleMessage(&result, message, wParam, lParam) )
3884 {
3885 #if wxDEBUG_LEVEL >= 2
3886 wxLogTrace("winmsg", wxT("Forwarding %s to DefWindowProc."),
3887 wxGetMessageName(message));
3888 #endif // wxDEBUG_LEVEL >= 2
3889 result = MSWDefWindowProc(message, wParam, lParam);
3890 }
3891
3892 return result;
3893 }
3894
3895 // ----------------------------------------------------------------------------
3896 // wxWindow <-> HWND map
3897 // ----------------------------------------------------------------------------
3898
wxFindWinFromHandle(HWND hwnd)3899 wxWindow *wxFindWinFromHandle(HWND hwnd)
3900 {
3901 WindowHandles::const_iterator i = gs_windowHandles.find(hwnd);
3902 return i == gs_windowHandles.end() ? NULL : i->second;
3903 }
3904
wxAssociateWinWithHandle(HWND hwnd,wxWindowMSW * win)3905 void wxAssociateWinWithHandle(HWND hwnd, wxWindowMSW *win)
3906 {
3907 // adding NULL hwnd is (first) surely a result of an error and
3908 // (secondly) breaks menu command processing
3909 wxCHECK_RET( hwnd != (HWND)NULL,
3910 wxT("attempt to add a NULL hwnd to window list ignored") );
3911
3912 #if wxDEBUG_LEVEL
3913 WindowHandles::const_iterator i = gs_windowHandles.find(hwnd);
3914 if ( i != gs_windowHandles.end() )
3915 {
3916 if ( i->second != win )
3917 {
3918 wxFAIL_MSG(
3919 wxString::Format(
3920 wxT("HWND %p already associated with another window (%s)"),
3921 hwnd, win->GetClassInfo()->GetClassName()
3922 )
3923 );
3924 }
3925 //else: this actually happens currently because we associate the window
3926 // with its HWND during creation (if we create it) and also when
3927 // SubclassWin() is called later, this is ok
3928 }
3929 #endif // wxDEBUG_LEVEL
3930
3931 gs_windowHandles[hwnd] = (wxWindow *)win;
3932 }
3933
wxRemoveHandleAssociation(wxWindowMSW * win)3934 void wxRemoveHandleAssociation(wxWindowMSW *win)
3935 {
3936 gs_windowHandles.erase(GetHwndOf(win));
3937 }
3938
3939 // ----------------------------------------------------------------------------
3940 // various MSW speciic class dependent functions
3941 // ----------------------------------------------------------------------------
3942
3943 // Default destroyer - override if you destroy it in some other way
3944 // (e.g. with MDI child windows)
MSWDestroyWindow()3945 void wxWindowMSW::MSWDestroyWindow()
3946 {
3947 }
3948
MSWGetCreateWindowCoords(const wxPoint & pos,const wxSize & size,int & x,int & y,int & w,int & h) const3949 void wxWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos,
3950 const wxSize& size,
3951 int& x, int& y,
3952 int& w, int& h) const
3953 {
3954 // CW_USEDEFAULT can't be used for child windows so just position them at
3955 // the origin by default
3956 x = pos.x == wxDefaultCoord ? 0 : pos.x;
3957 y = pos.y == wxDefaultCoord ? 0 : pos.y;
3958
3959 AdjustForParentClientOrigin(x, y);
3960
3961 // We don't have any clearly good choice for the size by default neither
3962 // but we must use something non-zero.
3963 w = WidthDefault(size.x);
3964 h = HeightDefault(size.y);
3965
3966 /*
3967 NB: there used to be some code here which set the initial size of the
3968 window to the client size of the parent if no explicit size was
3969 specified. This was wrong because wxWidgets programs often assume
3970 that they get a WM_SIZE (EVT_SIZE) upon creation, however this broke
3971 it. To see why, you should understand that Windows sends WM_SIZE from
3972 inside ::CreateWindow() anyhow. However, ::CreateWindow() is called
3973 from some base class ctor and so this WM_SIZE is not processed in the
3974 real class' OnSize() (because it's not fully constructed yet and the
3975 event goes to some base class OnSize() instead). So the WM_SIZE we
3976 rely on is the one sent when the parent frame resizes its children
3977 but here is the problem: if the child already has just the right
3978 size, nothing will happen as both wxWidgets and Windows check for
3979 this and ignore any attempts to change the window size to the size it
3980 already has - so no WM_SIZE would be sent.
3981 */
3982 }
3983
MSWGetParent() const3984 WXHWND wxWindowMSW::MSWGetParent() const
3985 {
3986 return m_parent ? m_parent->GetHWND() : WXHWND(NULL);
3987 }
3988
MSWCreate(const wxChar * wclass,const wxChar * title,const wxPoint & pos,const wxSize & size,WXDWORD style,WXDWORD extendedStyle)3989 bool wxWindowMSW::MSWCreate(const wxChar *wclass,
3990 const wxChar *title,
3991 const wxPoint& pos,
3992 const wxSize& size,
3993 WXDWORD style,
3994 WXDWORD extendedStyle)
3995 {
3996 // check a common bug in the user code: if the window is created with a
3997 // non-default ctor and Create() is called too, we'd create 2 HWND for a
3998 // single wxWindow object and this results in all sorts of trouble,
3999 // especially for wxTLWs
4000 wxCHECK_MSG( !m_hWnd, true, "window can't be recreated" );
4001
4002 // this can happen if this function is called using the return value of
4003 // wxApp::GetRegisteredClassName() which failed
4004 wxCHECK_MSG( wclass, false, "failed to register window class?" );
4005
4006
4007 // choose the position/size for the new window
4008 int x, y, w, h;
4009 (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h);
4010
4011 // controlId is menu handle for the top level windows, so set it to 0
4012 // unless we're creating a child window
4013 int controlId = style & WS_CHILD ? GetId() : 0;
4014
4015 // do create the window
4016 wxWindowCreationHook hook(this);
4017
4018 m_hWnd = MSWCreateWindowAtAnyPosition
4019 (
4020 extendedStyle,
4021 wclass,
4022 title ? title : m_windowName.t_str(),
4023 style,
4024 x, y, w, h,
4025 MSWGetParent(),
4026 controlId
4027 );
4028
4029 if ( !m_hWnd )
4030 {
4031 return false;
4032 }
4033
4034 SubclassWin(m_hWnd);
4035
4036 return true;
4037 }
4038
MSWCreateWindowAtAnyPosition(WXDWORD exStyle,const wxChar * clName,const wxChar * title,WXDWORD style,int x,int y,int width,int height,WXHWND parent,wxWindowID id)4039 WXHWND wxWindowMSW::MSWCreateWindowAtAnyPosition(WXDWORD exStyle, const wxChar* clName,
4040 const wxChar* title, WXDWORD style,
4041 int x, int y, int width, int height,
4042 WXHWND parent, wxWindowID id)
4043 {
4044 WXHWND hWnd = ::CreateWindowEx(exStyle, clName, title, style, x, y, width, height,
4045 parent, (HMENU)wxUIntToPtr(id), wxGetInstance(),
4046 NULL); // no extra data
4047
4048 if ( !hWnd )
4049 {
4050 wxLogLastError(wxString::Format
4051 (
4052 wxT("CreateWindowEx(\"%s\", flags=%08lx, ex=%08lx)"),
4053 clName, style, exStyle
4054 ));
4055 }
4056 else if ( !IsTopLevel() && !MSWIsPositionDirectlySupported(x, y) )
4057 {
4058 // fix position if limited by Short range
4059 MSWMoveWindowToAnyPosition(hWnd, x, y, width, height, IsShown());
4060 }
4061
4062 return hWnd;
4063 }
4064
4065 // ===========================================================================
4066 // MSW message handlers
4067 // ===========================================================================
4068
4069 // ---------------------------------------------------------------------------
4070 // WM_NOTIFY
4071 // ---------------------------------------------------------------------------
4072
HandleNotify(int idCtrl,WXLPARAM lParam,WXLPARAM * result)4073 bool wxWindowMSW::HandleNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
4074 {
4075 LPNMHDR hdr = (LPNMHDR)lParam;
4076 HWND hWnd = hdr->hwndFrom;
4077 wxWindow *win = wxFindWinFromHandle(hWnd);
4078
4079 // if the control is one of our windows, let it handle the message itself
4080 if ( win )
4081 {
4082 return win->MSWOnNotify(idCtrl, lParam, result);
4083 }
4084
4085 // VZ: why did we do it? normally this is unnecessary and, besides, it
4086 // breaks the message processing for the toolbars because the tooltip
4087 // notifications were being forwarded to the toolbar child controls
4088 // (if it had any) before being passed to the toolbar itself, so in my
4089 // example the tooltip for the combobox was always shown instead of the
4090 // correct button tooltips
4091 #if 0
4092 // try all our children
4093 wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
4094 while ( node )
4095 {
4096 wxWindow *child = node->GetData();
4097 if ( child->MSWOnNotify(idCtrl, lParam, result) )
4098 {
4099 return true;
4100 }
4101
4102 node = node->GetNext();
4103 }
4104 #endif // 0
4105
4106 // by default, handle it ourselves
4107 return MSWOnNotify(idCtrl, lParam, result);
4108 }
4109
4110 #if wxUSE_TOOLTIPS
4111
HandleTooltipNotify(WXUINT code,WXLPARAM lParam,const wxString & ttip)4112 bool wxWindowMSW::HandleTooltipNotify(WXUINT code,
4113 WXLPARAM lParam,
4114 const wxString& ttip)
4115 {
4116 // I don't know why it happens, but the versions of comctl32.dll starting
4117 // from 4.70 sometimes send TTN_NEEDTEXTW even to ANSI programs (normally,
4118 // this message is supposed to be sent to Unicode programs only) -- hence
4119 // we need to handle it as well, otherwise no tooltips will be shown in
4120 // this case
4121 if ( !(code == (WXUINT) TTN_NEEDTEXTA || code == (WXUINT) TTN_NEEDTEXTW)
4122 || ttip.empty() )
4123 {
4124 // not a tooltip message or no tooltip to show anyhow
4125 return false;
4126 }
4127
4128 LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam;
4129
4130 // We don't want to use the szText buffer because it has a limit of 80
4131 // bytes and this is not enough, especially for Unicode build where it
4132 // limits the tooltip string length to only 40 characters
4133 //
4134 // The best would be, of course, to not impose any length limitations at
4135 // all but then the buffer would have to be dynamic and someone would have
4136 // to free it and we don't have the tooltip owner object here any more, so
4137 // for now use our own static buffer with a higher fixed max length.
4138 //
4139 // Note that using a static buffer should not be a problem as only a single
4140 // tooltip can be shown at the same time anyhow.
4141 #if !wxUSE_UNICODE
4142 if ( code == (WXUINT) TTN_NEEDTEXTW )
4143 {
4144 // We need to convert tooltip from multi byte to Unicode on the fly.
4145 static wchar_t buf[513];
4146
4147 // Truncate tooltip length if needed as otherwise we might not have
4148 // enough space for it in the buffer and MultiByteToWideChar() would
4149 // return an error
4150 size_t tipLength = wxMin(ttip.length(), WXSIZEOF(buf) - 1);
4151
4152 // Convert to WideChar without adding the NULL character. The NULL
4153 // character is added afterwards (this is more efficient).
4154 int len = ::MultiByteToWideChar
4155 (
4156 CP_ACP,
4157 0, // no flags
4158 ttip.t_str(),
4159 tipLength,
4160 buf,
4161 WXSIZEOF(buf) - 1
4162 );
4163
4164 if ( !len )
4165 {
4166 wxLogLastError(wxT("MultiByteToWideChar()"));
4167 }
4168
4169 buf[len] = L'\0';
4170 ttText->lpszText = (LPSTR) buf;
4171 }
4172 else // TTN_NEEDTEXTA
4173 #endif // !wxUSE_UNICODE
4174 {
4175 // we get here if we got TTN_NEEDTEXTA (only happens in ANSI build) or
4176 // if we got TTN_NEEDTEXTW in Unicode build: in this case we just have
4177 // to copy the string we have into the buffer
4178 static wxChar buf[513];
4179 wxStrlcpy(buf, ttip.c_str(), WXSIZEOF(buf));
4180 ttText->lpszText = buf;
4181 }
4182
4183 return true;
4184 }
4185
4186 #endif // wxUSE_TOOLTIPS
4187
MSWOnNotify(int WXUNUSED (idCtrl),WXLPARAM lParam,WXLPARAM * WXUNUSED (result))4188 bool wxWindowMSW::MSWOnNotify(int WXUNUSED(idCtrl),
4189 WXLPARAM lParam,
4190 WXLPARAM* WXUNUSED(result))
4191 {
4192 #if wxUSE_TOOLTIPS
4193 if ( m_tooltip )
4194 {
4195 NMHDR* hdr = (NMHDR *)lParam;
4196 if ( HandleTooltipNotify(hdr->code, lParam, m_tooltip->GetTip()))
4197 {
4198 // processed
4199 return true;
4200 }
4201 }
4202 #else
4203 wxUnusedVar(lParam);
4204 #endif // wxUSE_TOOLTIPS
4205
4206 return false;
4207 }
4208
4209 // ---------------------------------------------------------------------------
4210 // end session messages
4211 // ---------------------------------------------------------------------------
4212
HandleQueryEndSession(long logOff,bool * mayEnd)4213 bool wxWindowMSW::HandleQueryEndSession(long logOff, bool *mayEnd)
4214 {
4215 wxCloseEvent event(wxEVT_QUERY_END_SESSION, wxID_ANY);
4216 event.SetEventObject(wxTheApp);
4217 event.SetCanVeto(true);
4218 event.SetLoggingOff(logOff == (long)ENDSESSION_LOGOFF);
4219
4220 bool rc = wxTheApp->SafelyProcessEvent(event);
4221
4222 if ( rc )
4223 {
4224 // we may end only if the app didn't veto session closing (double
4225 // negation...)
4226 *mayEnd = !event.GetVeto();
4227 }
4228
4229 return rc;
4230 }
4231
HandleEndSession(bool endSession,long logOff)4232 bool wxWindowMSW::HandleEndSession(bool endSession, long logOff)
4233 {
4234 // do nothing if the session isn't ending
4235 if ( !endSession )
4236 return false;
4237
4238 // only send once
4239 if ( this != wxApp::GetMainTopWindow() )
4240 return false;
4241
4242 wxCloseEvent event(wxEVT_END_SESSION, wxID_ANY);
4243 event.SetEventObject(wxTheApp);
4244 event.SetCanVeto(false);
4245 event.SetLoggingOff((logOff & ENDSESSION_LOGOFF) != 0);
4246
4247 return wxTheApp->SafelyProcessEvent(event);
4248 }
4249
4250 // ---------------------------------------------------------------------------
4251 // window creation/destruction
4252 // ---------------------------------------------------------------------------
4253
HandleCreate(WXLPCREATESTRUCT cs,bool * mayCreate)4254 bool wxWindowMSW::HandleCreate(WXLPCREATESTRUCT cs,
4255 bool *mayCreate)
4256 {
4257 if ( ((CREATESTRUCT *)cs)->dwExStyle & WS_EX_CONTROLPARENT )
4258 EnsureParentHasControlParentStyle(GetParent());
4259
4260 *mayCreate = true;
4261
4262 return true;
4263 }
4264
HandleDestroy()4265 bool wxWindowMSW::HandleDestroy()
4266 {
4267 // delete our drop target if we've got one
4268 #if wxUSE_DRAG_AND_DROP
4269 if ( m_dropTarget != NULL )
4270 {
4271 m_dropTarget->Revoke(m_hWnd);
4272
4273 wxDELETE(m_dropTarget);
4274 }
4275 #endif // wxUSE_DRAG_AND_DROP
4276
4277 // WM_DESTROY handled
4278 return true;
4279 }
4280
4281 // ---------------------------------------------------------------------------
4282 // activation/focus
4283 // ---------------------------------------------------------------------------
4284
HandleActivate(int state,bool minimized,WXHWND WXUNUSED (activate))4285 bool wxWindowMSW::HandleActivate(int state,
4286 bool minimized,
4287 WXHWND WXUNUSED(activate))
4288 {
4289 if ( minimized )
4290 {
4291 // Getting activation event when the window is minimized, as happens
4292 // e.g. when the window task bar icon is clicked, is unexpected and
4293 // managed to even break the logic in wx itself (see #17128), so just
4294 // don't do it as there doesn't seem to be any need to be notified
4295 // about the activation of the window icon in the task bar in practice.
4296 return false;
4297 }
4298
4299 if ( m_isBeingDeleted )
4300 {
4301 // Same goes for activation events sent to an already half-destroyed
4302 // window: this doesn't happen always, but can happen for a TLW using a
4303 // (still existent) hidden parent, see #18970.
4304 return false;
4305 }
4306
4307 wxActivateEvent event(wxEVT_ACTIVATE,
4308 (state == WA_ACTIVE) || (state == WA_CLICKACTIVE),
4309 m_windowId,
4310 state == WA_CLICKACTIVE
4311 ? wxActivateEvent::Reason_Mouse
4312 : wxActivateEvent::Reason_Unknown);
4313 event.SetEventObject(this);
4314
4315 return HandleWindowEvent(event);
4316 }
4317
HandleSetFocus(WXHWND hwnd)4318 bool wxWindowMSW::HandleSetFocus(WXHWND hwnd)
4319 {
4320 // Strangly enough, some controls get set focus events when they are being
4321 // deleted, even if they already had focus before.
4322 if ( m_isBeingDeleted )
4323 {
4324 return false;
4325 }
4326
4327 if ( ContainsHWND(hwnd) )
4328 {
4329 // If another subwindow of this window already had focus before, this
4330 // window should already have focus at wx level, no need for another
4331 // event.
4332 return false;
4333 }
4334
4335 // notify the parent keeping track of focus for the kbd navigation
4336 // purposes that we got it
4337 wxChildFocusEvent eventFocus((wxWindow *)this);
4338 (void)HandleWindowEvent(eventFocus);
4339
4340 #if wxUSE_CARET
4341 // Deal with caret
4342 if ( m_caret )
4343 {
4344 m_caret->OnSetFocus();
4345 }
4346 #endif // wxUSE_CARET
4347
4348 wxFocusEvent event(wxEVT_SET_FOCUS, m_windowId);
4349 event.SetEventObject(this);
4350
4351 // wxFindWinFromHandle() may return NULL, it is ok
4352 event.SetWindow(wxFindWinFromHandle(hwnd));
4353
4354 return HandleWindowEvent(event);
4355 }
4356
HandleKillFocus(WXHWND hwnd)4357 bool wxWindowMSW::HandleKillFocus(WXHWND hwnd)
4358 {
4359 // Don't send the event when in the process of being deleted. This can
4360 // only cause problems if the event handler tries to access the object.
4361 if ( m_isBeingDeleted )
4362 {
4363 return false;
4364 }
4365
4366 if ( ContainsHWND(hwnd) )
4367 {
4368 // If the focus switches to another HWND which is part of the same
4369 // wxWindow, we must not generate a wxEVT_KILL_FOCUS.
4370 return false;
4371 }
4372
4373 #if wxUSE_CARET
4374 // Deal with caret
4375 if ( m_caret )
4376 {
4377 m_caret->OnKillFocus();
4378 }
4379 #endif // wxUSE_CARET
4380
4381 wxFocusEvent event(wxEVT_KILL_FOCUS, m_windowId);
4382 event.SetEventObject(this);
4383
4384 // wxFindWinFromHandle() may return NULL, it is ok
4385 event.SetWindow(wxFindWinFromHandle(hwnd));
4386
4387 return HandleWindowEvent(event);
4388 }
4389
4390 // ---------------------------------------------------------------------------
4391 // labels
4392 // ---------------------------------------------------------------------------
4393
SetLabel(const wxString & label)4394 void wxWindowMSW::SetLabel( const wxString& label)
4395 {
4396 SetWindowText(GetHwnd(), label.c_str());
4397 }
4398
GetLabel() const4399 wxString wxWindowMSW::GetLabel() const
4400 {
4401 return wxGetWindowText(GetHWND());
4402 }
4403
4404 // ---------------------------------------------------------------------------
4405 // miscellaneous
4406 // ---------------------------------------------------------------------------
4407
HandleShow(bool show,int WXUNUSED (status))4408 bool wxWindowMSW::HandleShow(bool show, int WXUNUSED(status))
4409 {
4410 wxShowEvent event(GetId(), show);
4411 event.SetEventObject(this);
4412
4413 return HandleWindowEvent(event);
4414 }
4415
HandleInitDialog(WXHWND WXUNUSED (hWndFocus))4416 bool wxWindowMSW::HandleInitDialog(WXHWND WXUNUSED(hWndFocus))
4417 {
4418 wxInitDialogEvent event(GetId());
4419 event.SetEventObject(this);
4420
4421 return HandleWindowEvent(event);
4422 }
4423
HandleDropFiles(WXWPARAM wParam)4424 bool wxWindowMSW::HandleDropFiles(WXWPARAM wParam)
4425 {
4426 HDROP hFilesInfo = (HDROP) wParam;
4427
4428 // Get the total number of files dropped
4429 UINT gwFilesDropped = ::DragQueryFile
4430 (
4431 (HDROP)hFilesInfo,
4432 (UINT)-1,
4433 (LPTSTR)0,
4434 (UINT)0
4435 );
4436
4437 wxString *files = new wxString[gwFilesDropped];
4438 for ( UINT wIndex = 0; wIndex < gwFilesDropped; wIndex++ )
4439 {
4440 // first get the needed buffer length (+1 for terminating NUL)
4441 size_t len = ::DragQueryFile(hFilesInfo, wIndex, NULL, 0) + 1;
4442
4443 // and now get the file name
4444 ::DragQueryFile(hFilesInfo, wIndex,
4445 wxStringBuffer(files[wIndex], len), len);
4446 }
4447
4448 wxDropFilesEvent event(wxEVT_DROP_FILES, gwFilesDropped, files);
4449 event.SetEventObject(this);
4450
4451 POINT dropPoint;
4452 DragQueryPoint(hFilesInfo, (LPPOINT) &dropPoint);
4453 event.m_pos.x = dropPoint.x;
4454 event.m_pos.y = dropPoint.y;
4455
4456 DragFinish(hFilesInfo);
4457
4458 return HandleWindowEvent(event);
4459 }
4460
4461
HandleSetCursor(WXHWND WXUNUSED (hWnd),short nHitTest,int WXUNUSED (mouseMsg))4462 bool wxWindowMSW::HandleSetCursor(WXHWND WXUNUSED(hWnd),
4463 short nHitTest,
4464 int WXUNUSED(mouseMsg))
4465 {
4466 // the logic is as follows:
4467 // 0. if we're busy, set the busy cursor (even for non client elements)
4468 // 1. don't set custom cursor for non client area of enabled windows
4469 // 2. ask user EVT_SET_CURSOR handler for the cursor
4470 // 3. if still no cursor but we're in a TLW, set the global cursor
4471
4472 HCURSOR hcursor = 0;
4473
4474 // Check for "business" is complicated by the fact that modal dialogs shown
4475 // while busy cursor is in effect shouldn't show it as they are active and
4476 // accept input from the user, unlike all the other windows.
4477 bool isBusy = false;
4478 if ( wxIsBusy() )
4479 {
4480 wxDialog* const
4481 dlg = wxDynamicCast(wxGetTopLevelParent((wxWindow *)this), wxDialog);
4482 if ( !dlg || !dlg->IsModal() )
4483 isBusy = true;
4484 }
4485
4486 if ( isBusy )
4487 {
4488 hcursor = wxGetCurrentBusyCursor();
4489 }
4490 else // not busy
4491 {
4492 if ( nHitTest != HTCLIENT )
4493 return false;
4494
4495 // first ask the user code - it may wish to set the cursor in some very
4496 // specific way (for example, depending on the current position)
4497 POINT pt;
4498 wxGetCursorPosMSW(&pt);
4499
4500 int x = pt.x,
4501 y = pt.y;
4502 ScreenToClient(&x, &y);
4503 wxSetCursorEvent event(x, y);
4504 event.SetId(GetId());
4505 event.SetEventObject(this);
4506
4507 bool processedEvtSetCursor = HandleWindowEvent(event);
4508 if ( processedEvtSetCursor && event.HasCursor() )
4509 {
4510 hcursor = GetHcursorOf(event.GetCursor());
4511 }
4512
4513 if ( !hcursor )
4514 {
4515 // the test for processedEvtSetCursor is here to prevent using
4516 // m_cursor if the user code caught EVT_SET_CURSOR() and returned
4517 // nothing from it - this is a way to say that our cursor shouldn't
4518 // be used for this point
4519 if ( !processedEvtSetCursor && m_cursor.IsOk() )
4520 {
4521 hcursor = GetHcursorOf(m_cursor);
4522 }
4523
4524 if ( !hcursor && !GetParent() )
4525 {
4526 const wxCursor *cursor = wxGetGlobalCursor();
4527 if ( cursor && cursor->IsOk() )
4528 {
4529 hcursor = GetHcursorOf(*cursor);
4530 }
4531 }
4532 }
4533 }
4534
4535
4536 if ( hcursor )
4537 {
4538 ::SetCursor(hcursor);
4539
4540 // cursor set, stop here
4541 return true;
4542 }
4543
4544 // pass up the window chain
4545 return false;
4546 }
4547
HandlePower(WXWPARAM wParam,WXLPARAM WXUNUSED (lParam),bool * vetoed)4548 bool wxWindowMSW::HandlePower(WXWPARAM wParam,
4549 WXLPARAM WXUNUSED(lParam),
4550 bool *vetoed)
4551 {
4552 wxEventType evtType;
4553 switch ( wParam )
4554 {
4555 case PBT_APMQUERYSUSPEND:
4556 evtType = wxEVT_POWER_SUSPENDING;
4557 break;
4558
4559 case PBT_APMQUERYSUSPENDFAILED:
4560 evtType = wxEVT_POWER_SUSPEND_CANCEL;
4561 break;
4562
4563 case PBT_APMSUSPEND:
4564 evtType = wxEVT_POWER_SUSPENDED;
4565 break;
4566
4567 case PBT_APMRESUMESUSPEND:
4568 evtType = wxEVT_POWER_RESUME;
4569 break;
4570
4571 default:
4572 wxLogDebug(wxT("Unknown WM_POWERBROADCAST(%zd) event"), wParam);
4573 wxFALLTHROUGH;
4574
4575 // these messages are currently not mapped to wx events
4576 case PBT_APMQUERYSTANDBY:
4577 case PBT_APMQUERYSTANDBYFAILED:
4578 case PBT_APMSTANDBY:
4579 case PBT_APMRESUMESTANDBY:
4580 case PBT_APMBATTERYLOW:
4581 case PBT_APMPOWERSTATUSCHANGE:
4582 case PBT_APMOEMEVENT:
4583 case PBT_APMRESUMECRITICAL:
4584 case PBT_APMRESUMEAUTOMATIC:
4585 evtType = wxEVT_NULL;
4586 break;
4587 }
4588
4589 // don't handle unknown messages
4590 if ( evtType == wxEVT_NULL )
4591 return false;
4592
4593 // TODO: notify about PBTF_APMRESUMEFROMFAILURE in case of resume events?
4594
4595 wxPowerEvent event(evtType);
4596 if ( !HandleWindowEvent(event) )
4597 return false;
4598
4599 *vetoed = event.IsVetoed();
4600
4601 return true;
4602 }
4603
IsDoubleBuffered() const4604 bool wxWindowMSW::IsDoubleBuffered() const
4605 {
4606 for ( const wxWindowMSW *win = this; win; win = win->GetParent() )
4607 {
4608 if ( wxHasWindowExStyle(win, WS_EX_COMPOSITED) )
4609 return true;
4610
4611 if ( win->IsTopLevel() )
4612 break;
4613 }
4614
4615 return false;
4616 }
4617
SetDoubleBuffered(bool on)4618 void wxWindowMSW::SetDoubleBuffered(bool on)
4619 {
4620 wxMSWWinExStyleUpdater(GetHwnd()).TurnOnOrOff(on, WS_EX_COMPOSITED);
4621 }
4622
4623 // ---------------------------------------------------------------------------
4624 // owner drawn stuff
4625 // ---------------------------------------------------------------------------
4626
4627 #if (wxUSE_OWNER_DRAWN && wxUSE_MENUS_NATIVE) || \
4628 (wxUSE_CONTROLS && !defined(__WXUNIVERSAL__))
4629 #define WXUNUSED_UNLESS_ODRAWN(param) param
4630 #else
4631 #define WXUNUSED_UNLESS_ODRAWN(param)
4632 #endif
4633
4634 bool
MSWOnDrawItem(int WXUNUSED_UNLESS_ODRAWN (id),WXDRAWITEMSTRUCT * WXUNUSED_UNLESS_ODRAWN (itemStruct))4635 wxWindowMSW::MSWOnDrawItem(int WXUNUSED_UNLESS_ODRAWN(id),
4636 WXDRAWITEMSTRUCT * WXUNUSED_UNLESS_ODRAWN(itemStruct))
4637 {
4638 #if wxUSE_OWNER_DRAWN
4639
4640 #if wxUSE_MENUS_NATIVE
4641 // is it a menu item?
4642 DRAWITEMSTRUCT *pDrawStruct = (DRAWITEMSTRUCT *)itemStruct;
4643 if ( id == 0 && pDrawStruct->CtlType == ODT_MENU )
4644 {
4645 wxMenuItem *pMenuItem = (wxMenuItem *)(pDrawStruct->itemData);
4646
4647 // see comment before the same test in MSWOnMeasureItem() below
4648 if ( !pMenuItem )
4649 return false;
4650
4651 wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem),
4652 false, wxT("MSWOnDrawItem: bad wxMenuItem pointer") );
4653
4654 // prepare to call OnDrawItem(): notice using of wxDCTemp to prevent
4655 // the DC from being released
4656 wxDCTemp dc((WXHDC)pDrawStruct->hDC);
4657 wxRect rect(pDrawStruct->rcItem.left, pDrawStruct->rcItem.top,
4658 pDrawStruct->rcItem.right - pDrawStruct->rcItem.left,
4659 pDrawStruct->rcItem.bottom - pDrawStruct->rcItem.top);
4660
4661 return pMenuItem->OnDrawItem
4662 (
4663 dc,
4664 rect,
4665 (wxOwnerDrawn::wxODAction)pDrawStruct->itemAction,
4666 (wxOwnerDrawn::wxODStatus)pDrawStruct->itemState
4667 );
4668 }
4669 #endif // wxUSE_MENUS_NATIVE
4670
4671 #endif // USE_OWNER_DRAWN
4672
4673 #if wxUSE_CONTROLS && !defined(__WXUNIVERSAL__)
4674
4675 #if wxUSE_OWNER_DRAWN
4676 wxControl *item = wxDynamicCast(FindItem(id), wxControl);
4677 #else // !wxUSE_OWNER_DRAWN
4678 // we may still have owner-drawn buttons internally because we have to make
4679 // them owner-drawn to support colour change
4680 wxControl *item =
4681 # if wxUSE_BUTTON
4682 wxDynamicCast(FindItem(id), wxButton)
4683 # else
4684 NULL
4685 # endif
4686 ;
4687 #endif // USE_OWNER_DRAWN
4688
4689 if ( item )
4690 {
4691 return item->MSWOnDraw(itemStruct);
4692 }
4693
4694 #endif // wxUSE_CONTROLS
4695
4696 return false;
4697 }
4698
4699 bool
MSWOnMeasureItem(int id,WXMEASUREITEMSTRUCT * itemStruct)4700 wxWindowMSW::MSWOnMeasureItem(int id, WXMEASUREITEMSTRUCT *itemStruct)
4701 {
4702 #if wxUSE_OWNER_DRAWN && wxUSE_MENUS_NATIVE
4703 // is it a menu item?
4704 MEASUREITEMSTRUCT *pMeasureStruct = (MEASUREITEMSTRUCT *)itemStruct;
4705 if ( id == 0 && pMeasureStruct->CtlType == ODT_MENU )
4706 {
4707 wxMenuItem *pMenuItem = (wxMenuItem *)(pMeasureStruct->itemData);
4708
4709 // according to Carsten Fuchs the pointer may be NULL under XP if an
4710 // MDI child frame is initially maximized, see this for more info:
4711 // http://article.gmane.org/gmane.comp.lib.wxwidgets.general/27745
4712 //
4713 // so silently ignore it instead of asserting
4714 if ( !pMenuItem )
4715 return false;
4716
4717 wxCHECK_MSG( wxDynamicCast(pMenuItem, wxMenuItem),
4718 false, wxT("MSWOnMeasureItem: bad wxMenuItem pointer") );
4719
4720 size_t w, h;
4721 bool rc = pMenuItem->OnMeasureItem(&w, &h);
4722
4723 pMeasureStruct->itemWidth = w;
4724 pMeasureStruct->itemHeight = h;
4725
4726 return rc;
4727 }
4728
4729 wxControl *item = wxDynamicCast(FindItem(id), wxControl);
4730 if ( item )
4731 {
4732 return item->MSWOnMeasure(itemStruct);
4733 }
4734 #else
4735 wxUnusedVar(id);
4736 wxUnusedVar(itemStruct);
4737 #endif // wxUSE_OWNER_DRAWN && wxUSE_MENUS_NATIVE
4738
4739 return false;
4740 }
4741
4742 // ---------------------------------------------------------------------------
4743 // DPI
4744 // ---------------------------------------------------------------------------
4745
4746 namespace
4747 {
4748
GetWindowDPI(HWND hwnd)4749 static wxSize GetWindowDPI(HWND hwnd)
4750 {
4751 #if wxUSE_DYNLIB_CLASS
4752 typedef UINT (WINAPI *GetDpiForWindow_t)(HWND hwnd);
4753 static GetDpiForWindow_t s_pfnGetDpiForWindow = NULL;
4754 static bool s_initDone = false;
4755
4756 if ( !s_initDone )
4757 {
4758 wxLoadedDLL dllUser32("user32.dll");
4759 wxDL_INIT_FUNC(s_pfn, GetDpiForWindow, dllUser32);
4760 s_initDone = true;
4761 }
4762
4763 if ( s_pfnGetDpiForWindow )
4764 {
4765 const int dpi = static_cast<int>(s_pfnGetDpiForWindow(hwnd));
4766 return wxSize(dpi, dpi);
4767 }
4768 #endif // wxUSE_DYNLIB_CLASS
4769
4770 return wxSize();
4771 }
4772
4773 }
4774
4775 /*extern*/
wxGetSystemMetrics(int nIndex,const wxWindow * window)4776 int wxGetSystemMetrics(int nIndex, const wxWindow* window)
4777 {
4778 #if wxUSE_DYNLIB_CLASS
4779 if ( !window )
4780 window = wxApp::GetMainTopWindow();
4781
4782 if ( window )
4783 {
4784 typedef int (WINAPI * GetSystemMetricsForDpi_t)(int nIndex, UINT dpi);
4785 static GetSystemMetricsForDpi_t s_pfnGetSystemMetricsForDpi = NULL;
4786 static bool s_initDone = false;
4787
4788 if ( !s_initDone )
4789 {
4790 wxLoadedDLL dllUser32("user32.dll");
4791 wxDL_INIT_FUNC(s_pfn, GetSystemMetricsForDpi, dllUser32);
4792 s_initDone = true;
4793 }
4794
4795 if ( s_pfnGetSystemMetricsForDpi )
4796 {
4797 const int dpi = window->GetDPI().y;
4798 return s_pfnGetSystemMetricsForDpi(nIndex, (UINT)dpi);
4799 }
4800 }
4801 #else
4802 wxUnusedVar(window);
4803 #endif // wxUSE_DYNLIB_CLASS
4804
4805 return ::GetSystemMetrics(nIndex);
4806 }
4807
4808 /*extern*/
wxSystemParametersInfo(UINT uiAction,UINT uiParam,PVOID pvParam,UINT fWinIni,const wxWindow * window)4809 bool wxSystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, const wxWindow* window)
4810 {
4811 // Note that we can't use SystemParametersInfoForDpi() in non-Unicode build
4812 // because it always works with wide strings and we'd have to check for all
4813 // uiAction values corresponding to strings and use a temporary wide buffer
4814 // for them, and convert the returned value to ANSI after the call. Instead
4815 // of doing all this, just don't use it at all in the deprecated ANSI build.
4816 #if wxUSE_DYNLIB_CLASS && wxUSE_UNICODE
4817 if ( !window )
4818 window = wxApp::GetMainTopWindow();
4819
4820 if ( window )
4821 {
4822 typedef int (WINAPI * SystemParametersInfoForDpi_t)(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi);
4823 static SystemParametersInfoForDpi_t s_pfnSystemParametersInfoForDpi = NULL;
4824 static bool s_initDone = false;
4825
4826 if ( !s_initDone )
4827 {
4828 wxLoadedDLL dllUser32("user32.dll");
4829 wxDL_INIT_FUNC(s_pfn, SystemParametersInfoForDpi, dllUser32);
4830 s_initDone = true;
4831 }
4832
4833 if ( s_pfnSystemParametersInfoForDpi )
4834 {
4835 const int dpi = window->GetDPI().y;
4836 if ( s_pfnSystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, (UINT)dpi) == TRUE )
4837 {
4838 return true;
4839 }
4840 }
4841 }
4842 #else
4843 wxUnusedVar(window);
4844 #endif // wxUSE_DYNLIB_CLASS
4845
4846 return ::SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni) == TRUE;
4847 }
4848
GetDPI() const4849 wxSize wxWindowMSW::GetDPI() const
4850 {
4851 HWND hwnd = GetHwnd();
4852
4853 if ( hwnd == NULL )
4854 {
4855 const wxWindow* topWin = wxGetTopLevelParent(const_cast<wxWindowMSW*>(this));
4856 if ( topWin )
4857 {
4858 hwnd = GetHwndOf(topWin);
4859 }
4860 }
4861
4862 wxSize dpi = GetWindowDPI(hwnd);
4863
4864 if ( !dpi.x || !dpi.y )
4865 {
4866 WindowHDC hdc(hwnd);
4867 dpi.x = ::GetDeviceCaps(hdc, LOGPIXELSX);
4868 dpi.y = ::GetDeviceCaps(hdc, LOGPIXELSY);
4869 }
4870
4871 return dpi;
4872 }
4873
GetDPIScaleFactor() const4874 double wxWindowMSW::GetDPIScaleFactor() const
4875 {
4876 return GetDPI().y / (double)wxDisplay::GetStdPPIValue();
4877 }
4878
WXAdjustFontToOwnPPI(wxFont & font) const4879 void wxWindowMSW::WXAdjustFontToOwnPPI(wxFont& font) const
4880 {
4881 font.WXAdjustToPPI(GetDPI());
4882 }
4883
MSWUpdateFontOnDPIChange(const wxSize & newDPI)4884 void wxWindowMSW::MSWUpdateFontOnDPIChange(const wxSize& newDPI)
4885 {
4886 if ( m_font.IsOk() )
4887 {
4888 m_font.WXAdjustToPPI(newDPI);
4889
4890 // WXAdjustToPPI() changes the HFONT, so reassociate it with the window.
4891 wxSetWindowFont(GetHwnd(), m_font);
4892 }
4893 }
4894
4895 // Helper function to update the given coordinate by the scaling factor if it
4896 // is set, i.e. different from wxDefaultCoord.
ScaleCoordIfSet(int & coord,float scaleFactor)4897 static void ScaleCoordIfSet(int& coord, float scaleFactor)
4898 {
4899 if ( coord != wxDefaultCoord )
4900 {
4901 const float coordScaled = coord * scaleFactor;
4902 coord = int(scaleFactor > 1 ? std::ceil(coordScaled) : std::floor(coordScaled));
4903 }
4904 }
4905
4906 // Called from MSWUpdateonDPIChange() to recursively update the window
4907 // sizer and any child sizers and spacers.
UpdateSizerOnDPIChange(wxSizer * sizer,float scaleFactor)4908 static void UpdateSizerOnDPIChange(wxSizer* sizer, float scaleFactor)
4909 {
4910 if ( !sizer )
4911 {
4912 return;
4913 }
4914
4915 for ( wxSizerItemList::compatibility_iterator
4916 node = sizer->GetChildren().GetFirst();
4917 node;
4918 node = node->GetNext() )
4919 {
4920 wxSizerItem* sizerItem = node->GetData();
4921
4922 int border = sizerItem->GetBorder();
4923 ScaleCoordIfSet(border, scaleFactor);
4924 sizerItem->SetBorder(border);
4925
4926 // only scale sizers and spacers, not windows
4927 if ( sizerItem->IsSizer() || sizerItem->IsSpacer() )
4928 {
4929 wxSize min = sizerItem->GetMinSize();
4930 ScaleCoordIfSet(min.x, scaleFactor);
4931 ScaleCoordIfSet(min.y, scaleFactor);
4932 sizerItem->SetMinSize(min);
4933
4934 if ( sizerItem->IsSpacer() )
4935 {
4936 wxSize size = sizerItem->GetSize();
4937 ScaleCoordIfSet(size.x, scaleFactor);
4938 ScaleCoordIfSet(size.y, scaleFactor);
4939 sizerItem->SetDimension(wxDefaultPosition, size);
4940 }
4941
4942 // Update any child sizers if this is a sizer
4943 UpdateSizerOnDPIChange(sizerItem->GetSizer(), scaleFactor);
4944 }
4945 }
4946 }
4947
4948 void
MSWUpdateOnDPIChange(const wxSize & oldDPI,const wxSize & newDPI)4949 wxWindowMSW::MSWUpdateOnDPIChange(const wxSize& oldDPI, const wxSize& newDPI)
4950 {
4951 // update min and max size if necessary
4952 const float scaleFactor = (float)newDPI.y / oldDPI.y;
4953
4954 ScaleCoordIfSet(m_minHeight, scaleFactor);
4955 ScaleCoordIfSet(m_minWidth, scaleFactor);
4956 ScaleCoordIfSet(m_maxHeight, scaleFactor);
4957 ScaleCoordIfSet(m_maxWidth, scaleFactor);
4958
4959 InvalidateBestSize();
4960
4961 // update font if necessary
4962 MSWUpdateFontOnDPIChange(newDPI);
4963
4964 // update sizers
4965 UpdateSizerOnDPIChange(GetSizer(), scaleFactor);
4966
4967 // update children
4968 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
4969 node;
4970 node = node->GetNext() )
4971 {
4972 wxWindow *childWin = node->GetData();
4973 // Update all children, except other top-level windows.
4974 // These could be on a different monitor and will get their own
4975 // dpi-changed event.
4976 if ( childWin && !childWin->IsTopLevel() )
4977 {
4978 childWin->MSWUpdateOnDPIChange(oldDPI, newDPI);
4979 }
4980 }
4981
4982 wxDPIChangedEvent event(oldDPI, newDPI);
4983 event.SetEventObject(this);
4984 HandleWindowEvent(event);
4985 }
4986
4987 // ---------------------------------------------------------------------------
4988 // colours and palettes
4989 // ---------------------------------------------------------------------------
4990
HandleSysColorChange()4991 bool wxWindowMSW::HandleSysColorChange()
4992 {
4993 wxSysColourChangedEvent event;
4994 event.SetEventObject(this);
4995
4996 (void)HandleWindowEvent(event);
4997
4998 // always let the system carry on the default processing to allow the
4999 // native controls to react to the colours update
5000 return false;
5001 }
5002
HandleDisplayChange()5003 bool wxWindowMSW::HandleDisplayChange()
5004 {
5005 wxDisplayChangedEvent event;
5006 event.SetEventObject(this);
5007
5008 return HandleWindowEvent(event);
5009 }
5010
HandleCtlColor(WXHBRUSH * brush,WXHDC hDC,WXHWND hWnd)5011 bool wxWindowMSW::HandleCtlColor(WXHBRUSH *brush, WXHDC hDC, WXHWND hWnd)
5012 {
5013 #if !wxUSE_CONTROLS || defined(__WXUNIVERSAL__)
5014 wxUnusedVar(hDC);
5015 wxUnusedVar(hWnd);
5016 #else
5017 wxControl *item = wxDynamicCast(FindItemByHWND(hWnd, true), wxControl);
5018
5019 if ( item )
5020 *brush = item->MSWControlColor(hDC, hWnd);
5021 else
5022 #endif // wxUSE_CONTROLS
5023 *brush = NULL;
5024
5025 return *brush != NULL;
5026 }
5027
HandlePaletteChanged(WXHWND hWndPalChange)5028 bool wxWindowMSW::HandlePaletteChanged(WXHWND hWndPalChange)
5029 {
5030 #if wxUSE_PALETTE
5031 // same as below except we don't respond to our own messages
5032 if ( hWndPalChange != GetHWND() )
5033 {
5034 // check to see if we our our parents have a custom palette
5035 wxWindowMSW *win = this;
5036 while ( win && !win->HasCustomPalette() )
5037 {
5038 win = win->GetParent();
5039 }
5040
5041 if ( win && win->HasCustomPalette() )
5042 {
5043 // realize the palette to see whether redrawing is needed
5044 HDC hdc = ::GetDC((HWND) hWndPalChange);
5045 win->m_palette.SetHPALETTE((WXHPALETTE)
5046 ::SelectPalette(hdc, GetHpaletteOf(win->m_palette), FALSE));
5047
5048 int result = ::RealizePalette(hdc);
5049
5050 // restore the palette (before releasing the DC)
5051 win->m_palette.SetHPALETTE((WXHPALETTE)
5052 ::SelectPalette(hdc, GetHpaletteOf(win->m_palette), FALSE));
5053 ::RealizePalette(hdc);
5054 ::ReleaseDC((HWND) hWndPalChange, hdc);
5055
5056 // now check for the need to redraw
5057 if (result > 0)
5058 ::InvalidateRect((HWND) hWndPalChange, NULL, TRUE);
5059 }
5060
5061 }
5062 #endif // wxUSE_PALETTE
5063
5064 wxPaletteChangedEvent event(GetId());
5065 event.SetEventObject(this);
5066 event.SetChangedWindow(wxFindWinFromHandle(hWndPalChange));
5067
5068 return HandleWindowEvent(event);
5069 }
5070
HandleCaptureChanged(WXHWND hWndGainedCapture)5071 bool wxWindowMSW::HandleCaptureChanged(WXHWND hWndGainedCapture)
5072 {
5073 // Ensure that wxWindow::GetCapture() returns NULL if called from the event
5074 // handlers invoked below. This is necessary to avoid wrongly calling
5075 // ReleaseMouse() when we're already losing the mouse capture anyhow.
5076 gs_insideCaptureChanged = true;
5077 wxON_BLOCK_EXIT_SET(gs_insideCaptureChanged, false);
5078
5079 // notify windows on the capture stack about lost capture
5080 // (see http://sourceforge.net/tracker/index.php?func=detail&aid=1153662&group_id=9863&atid=109863):
5081 wxWindowBase::NotifyCaptureLost();
5082
5083 wxWindow *win = wxFindWinFromHandle(hWndGainedCapture);
5084 wxMouseCaptureChangedEvent event(GetId(), win);
5085 event.SetEventObject(this);
5086 return HandleWindowEvent(event);
5087 }
5088
HandleSettingChange(WXWPARAM wParam,WXLPARAM lParam)5089 bool wxWindowMSW::HandleSettingChange(WXWPARAM wParam, WXLPARAM lParam)
5090 {
5091 // despite MSDN saying "(This message cannot be sent directly to a window.)"
5092 // we need to send this to child windows (it is only sent to top-level
5093 // windows) so {list,tree}ctrls can adjust their font size if necessary
5094 // this is exactly how explorer does it to enable the font size changes
5095
5096 wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
5097 while ( node )
5098 {
5099 // top-level windows already get this message from the system
5100 wxWindow *win = node->GetData();
5101 if ( !win->IsTopLevel() )
5102 {
5103 ::SendMessage(GetHwndOf(win), WM_SETTINGCHANGE, wParam, lParam);
5104 }
5105
5106 node = node->GetNext();
5107 }
5108
5109 // let the system handle it
5110 return false;
5111 }
5112
HandleQueryNewPalette()5113 bool wxWindowMSW::HandleQueryNewPalette()
5114 {
5115
5116 #if wxUSE_PALETTE
5117 // check to see if we our our parents have a custom palette
5118 wxWindowMSW *win = this;
5119 while (!win->HasCustomPalette() && win->GetParent()) win = win->GetParent();
5120 if (win->HasCustomPalette()) {
5121 /* realize the palette to see whether redrawing is needed */
5122 HDC hdc = ::GetDC((HWND) GetHWND());
5123 win->m_palette.SetHPALETTE( (WXHPALETTE)
5124 ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), FALSE) );
5125
5126 int result = ::RealizePalette(hdc);
5127 /* restore the palette (before releasing the DC) */
5128 win->m_palette.SetHPALETTE( (WXHPALETTE)
5129 ::SelectPalette(hdc, (HPALETTE) win->m_palette.GetHPALETTE(), TRUE) );
5130 ::RealizePalette(hdc);
5131 ::ReleaseDC((HWND) GetHWND(), hdc);
5132 /* now check for the need to redraw */
5133 if (result > 0)
5134 ::InvalidateRect((HWND) GetHWND(), NULL, TRUE);
5135 }
5136 #endif // wxUSE_PALETTE
5137
5138 wxQueryNewPaletteEvent event(GetId());
5139 event.SetEventObject(this);
5140
5141 return HandleWindowEvent(event) && event.GetPaletteRealized();
5142 }
5143
5144 // Responds to colour changes: passes event on to children.
OnSysColourChanged(wxSysColourChangedEvent & WXUNUSED (event))5145 void wxWindowMSW::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
5146 {
5147 // the top level window also reset the standard colour map as it might have
5148 // changed (there is no need to do it for the non top level windows as we
5149 // only have to do it once)
5150 if ( IsTopLevel() )
5151 {
5152 // FIXME-MT
5153 gs_hasStdCmap = false;
5154 }
5155 wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
5156 while ( node )
5157 {
5158 // Only propagate to non-top-level windows because Windows already
5159 // sends this event to all top-level ones
5160 wxWindow *win = node->GetData();
5161 if ( !win->IsTopLevel() )
5162 {
5163 // we need to send the real WM_SYSCOLORCHANGE and not just trigger
5164 // EVT_SYS_COLOUR_CHANGED call because the latter wouldn't work for
5165 // the standard controls
5166 ::SendMessage(GetHwndOf(win), WM_SYSCOLORCHANGE, 0, 0);
5167 }
5168
5169 node = node->GetNext();
5170 }
5171 }
5172
wxGetStdColourMap()5173 extern wxCOLORMAP *wxGetStdColourMap()
5174 {
5175 static COLORREF s_stdColours[wxSTD_COL_MAX];
5176 static wxCOLORMAP s_cmap[wxSTD_COL_MAX];
5177
5178 if ( !gs_hasStdCmap )
5179 {
5180 static bool s_coloursInit = false;
5181
5182 if ( !s_coloursInit )
5183 {
5184 // When a bitmap is loaded, the RGB values can change (apparently
5185 // because Windows adjusts them to care for the old programs always
5186 // using 0xc0c0c0 while the transparent colour for the new Windows
5187 // versions is different). But we do this adjustment ourselves so
5188 // we want to avoid Windows' "help" and for this we need to have a
5189 // reference bitmap which can tell us what the RGB values change
5190 // to.
5191 wxLogNull logNo; // suppress error if we couldn't load the bitmap
5192 wxBitmap stdColourBitmap(wxT("wxBITMAP_STD_COLOURS"));
5193 if ( stdColourBitmap.IsOk() )
5194 {
5195 // the pixels in the bitmap must correspond to wxSTD_COL_XXX!
5196 wxASSERT_MSG( stdColourBitmap.GetWidth() == wxSTD_COL_MAX,
5197 wxT("forgot to update wxBITMAP_STD_COLOURS!") );
5198
5199 wxMemoryDC memDC;
5200 memDC.SelectObject(stdColourBitmap);
5201
5202 wxColour colour;
5203 for ( size_t i = 0; i < WXSIZEOF(s_stdColours); i++ )
5204 {
5205 memDC.GetPixel(i, 0, &colour);
5206 s_stdColours[i] = wxColourToRGB(colour);
5207 }
5208 }
5209 else // wxBITMAP_STD_COLOURS couldn't be loaded
5210 {
5211 s_stdColours[0] = RGB(000,000,000); // black
5212 s_stdColours[1] = RGB(128,128,128); // dark grey
5213 s_stdColours[2] = RGB(192,192,192); // light grey
5214 s_stdColours[3] = RGB(255,255,255); // white
5215 //s_stdColours[4] = RGB(000,000,255); // blue
5216 //s_stdColours[5] = RGB(255,000,255); // magenta
5217 }
5218
5219 s_coloursInit = true;
5220 }
5221
5222 gs_hasStdCmap = true;
5223
5224 // create the colour map
5225 #define INIT_CMAP_ENTRY(col) \
5226 s_cmap[wxSTD_COL_##col].from = s_stdColours[wxSTD_COL_##col]; \
5227 s_cmap[wxSTD_COL_##col].to = ::GetSysColor(COLOR_##col)
5228
5229 INIT_CMAP_ENTRY(BTNTEXT);
5230 INIT_CMAP_ENTRY(BTNSHADOW);
5231 INIT_CMAP_ENTRY(BTNFACE);
5232 INIT_CMAP_ENTRY(BTNHIGHLIGHT);
5233
5234 #undef INIT_CMAP_ENTRY
5235 }
5236
5237 return s_cmap;
5238 }
5239
5240 #if wxUSE_UXTHEME && !defined(TMT_FILLCOLOR)
5241 #define TMT_FILLCOLOR 3802
5242 #define TMT_TEXTCOLOR 3803
5243 #define TMT_BORDERCOLOR 3801
5244 #endif
5245
MSWGetThemeColour(const wchar_t * themeName,int themePart,int themeState,MSWThemeColour themeColour,wxSystemColour fallback) const5246 wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName,
5247 int themePart,
5248 int themeState,
5249 MSWThemeColour themeColour,
5250 wxSystemColour fallback) const
5251 {
5252 #if wxUSE_UXTHEME
5253 if ( wxUxThemeIsActive() )
5254 {
5255 int themeProperty = 0;
5256
5257 // TODO: Convert this into a table? Sure would be faster.
5258 switch ( themeColour )
5259 {
5260 case ThemeColourBackground:
5261 themeProperty = TMT_FILLCOLOR;
5262 break;
5263 case ThemeColourText:
5264 themeProperty = TMT_TEXTCOLOR;
5265 break;
5266 case ThemeColourBorder:
5267 themeProperty = TMT_BORDERCOLOR;
5268 break;
5269 default:
5270 wxFAIL_MSG(wxT("unsupported theme colour"));
5271 }
5272
5273 wxUxThemeHandle hTheme((const wxWindow *)this, themeName);
5274 COLORREF col;
5275 HRESULT hr = ::GetThemeColor
5276 (
5277 hTheme,
5278 themePart,
5279 themeState,
5280 themeProperty,
5281 &col
5282 );
5283
5284 if ( SUCCEEDED(hr) )
5285 return wxRGBToColour(col);
5286
5287 wxLogApiError(
5288 wxString::Format(
5289 "GetThemeColor(%s, %i, %i, %i)",
5290 themeName, themePart, themeState, themeProperty),
5291 hr);
5292 }
5293 #else
5294 wxUnusedVar(themeName);
5295 wxUnusedVar(themePart);
5296 wxUnusedVar(themeState);
5297 wxUnusedVar(themeColour);
5298 #endif
5299 return wxSystemSettings::GetColour(fallback);
5300 }
5301
5302 // ---------------------------------------------------------------------------
5303 // painting
5304 // ---------------------------------------------------------------------------
5305
5306 // this variable is used to check that a paint event handler which processed
5307 // the event did create a wxPaintDC inside its code and called BeginPaint() to
5308 // validate the invalidated window area as otherwise we'd keep getting an
5309 // endless stream of WM_PAINT messages for this window resulting in a lot of
5310 // difficult to debug problems (e.g. impossibility to repaint other windows,
5311 // lack of timer and idle events and so on)
5312 wxStack<wxMSWImpl::PaintData> wxMSWImpl::paintStack;
5313
HandlePaint()5314 bool wxWindowMSW::HandlePaint()
5315 {
5316 HRGN hRegion = ::CreateRectRgn(0, 0, 0, 0); // Dummy call to get a handle
5317 if ( !hRegion )
5318 {
5319 wxLogLastError(wxT("CreateRectRgn"));
5320 }
5321 if ( ::GetUpdateRgn(GetHwnd(), hRegion, FALSE) == ERROR )
5322 {
5323 wxLogLastError(wxT("GetUpdateRgn"));
5324 }
5325
5326 m_updateRegion = wxRegion((WXHRGN) hRegion);
5327
5328 using namespace wxMSWImpl;
5329
5330 paintStack.push(PaintData(this));
5331
5332 wxPaintEvent event(this);
5333
5334 bool processed = HandleWindowEvent(event);
5335
5336 const bool createdPaintDC = paintStack.top().createdPaintDC;
5337 if ( createdPaintDC && !processed )
5338 {
5339 // Event handler did paint something as wxPaintDC object was created
5340 // but then it must have skipped the event to indicate that default
5341 // handling should still take place, so call MSWDefWindowProc() right
5342 // now. It's important to do it before EndPaint() call below as that
5343 // would validate the window and MSWDefWindowProc(WM_PAINT) wouldn't do
5344 // anything if called after it.
5345 OnPaint(event);
5346 }
5347
5348 // note that we must generate NC event after the normal one as otherwise
5349 // BeginPaint() will happily overwrite our decorations with the background
5350 // colour
5351 wxNcPaintEvent eventNc(this);
5352 HandleWindowEvent(eventNc);
5353
5354 // don't keep an HRGN we don't need any longer (GetUpdateRegion() can only
5355 // be called from inside the event handlers called above)
5356 m_updateRegion.Clear();
5357
5358 wxPaintDCImpl::EndPaint((wxWindow *)this);
5359
5360 paintStack.pop();
5361
5362 // It doesn't matter whether the event was actually processed or not here,
5363 // what matters is whether we already painted, and hence validated, the
5364 // window or not. If we did, either the event was processed or we called
5365 // OnPaint() above, so we should return true. If we did not, even the event
5366 // was processed, we must still call MSWDefWindowProc() to ensure that the
5367 // window is validated, i.e. to avoid the problem described in the comment
5368 // before paintStack definition above.
5369 return createdPaintDC;
5370 }
5371
5372 // Can be called from an application's OnPaint handler
OnPaint(wxPaintEvent & event)5373 void wxWindowMSW::OnPaint(wxPaintEvent& event)
5374 {
5375 #ifdef __WXUNIVERSAL__
5376 event.Skip();
5377 #else
5378 HDC hDC = (HDC) wxPaintDCImpl::FindDCInCache((wxWindow*) event.GetEventObject());
5379 if (hDC != 0)
5380 {
5381 MSWDefWindowProc(WM_PAINT, (WPARAM) hDC, 0);
5382 }
5383 #endif
5384 }
5385
HandleEraseBkgnd(WXHDC hdc)5386 bool wxWindowMSW::HandleEraseBkgnd(WXHDC hdc)
5387 {
5388 if ( IsBeingDeleted() )
5389 {
5390 // We can get WM_ERASEBKGND after starting the destruction of our top
5391 // level parent. Handling it in this case is unnecessary and can be
5392 // actually harmful as e.g. wxStaticBox::GetClientSize() doesn't work
5393 // without a valid TLW parent (because it uses dialog units internally
5394 // which use the dialog font), so just don't do anything then.
5395 return false;
5396 }
5397
5398 switch ( GetBackgroundStyle() )
5399 {
5400 case wxBG_STYLE_ERASE:
5401 case wxBG_STYLE_COLOUR:
5402 // we need to generate an erase background event
5403 {
5404 wxDCTemp dc(hdc, GetClientSize());
5405 wxDCTempImpl *impl = (wxDCTempImpl*) dc.GetImpl();
5406
5407 impl->SetHDC(hdc);
5408 impl->SetWindow((wxWindow *)this);
5409
5410 wxEraseEvent event(m_windowId, &dc);
5411 event.SetEventObject(this);
5412 bool rc = HandleWindowEvent(event);
5413
5414 // must be called manually as ~wxDC doesn't do anything for
5415 // wxDCTemp
5416 impl->SelectOldObjects(hdc);
5417
5418 if ( rc )
5419 {
5420 // background erased by the user-defined handler
5421 return true;
5422 }
5423 }
5424 wxFALLTHROUGH;
5425
5426 case wxBG_STYLE_SYSTEM:
5427 if ( !DoEraseBackground(hdc) )
5428 {
5429 // let the default processing to take place if we didn't erase
5430 // the background ourselves
5431 return false;
5432 }
5433 break;
5434
5435 case wxBG_STYLE_PAINT:
5436 case wxBG_STYLE_TRANSPARENT:
5437 // no need to do anything here at all, background will be entirely
5438 // redrawn in WM_PAINT handler
5439 break;
5440
5441 default:
5442 wxFAIL_MSG( "unknown background style" );
5443 }
5444
5445 return true;
5446 }
5447
5448 #ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
5449
MSWHasEraseBgHook() const5450 bool wxWindowMSW::MSWHasEraseBgHook() const
5451 {
5452 return gs_eraseBgHooks.find(const_cast<wxWindowMSW *>(this))
5453 != gs_eraseBgHooks.end();
5454 }
5455
MSWSetEraseBgHook(wxWindow * child)5456 void wxWindowMSW::MSWSetEraseBgHook(wxWindow *child)
5457 {
5458 if ( child )
5459 {
5460 if ( !gs_eraseBgHooks.insert(
5461 EraseBgHooks::value_type(this, child)).second )
5462 {
5463 wxFAIL_MSG( wxT("Setting erase background hook twice?") );
5464 }
5465 }
5466 else // reset the hook
5467 {
5468 if ( gs_eraseBgHooks.erase(this) != 1 )
5469 {
5470 wxFAIL_MSG( wxT("Resetting erase background which was not set?") );
5471 }
5472 }
5473 }
5474
5475 #endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
5476
DoEraseBackground(WXHDC hDC)5477 bool wxWindowMSW::DoEraseBackground(WXHDC hDC)
5478 {
5479 HBRUSH hbr = (HBRUSH)MSWGetBgBrush(hDC);
5480 if ( !hbr )
5481 return false;
5482
5483 // erase just the client area of the window, this is important for the
5484 // frames to avoid drawing over the toolbar part of the window (you might
5485 // think using WS_CLIPCHILDREN would prevent this from happening, but it
5486 // clearly doesn't)
5487 RECT rc;
5488 wxCopyRectToRECT(GetClientRect(), rc);
5489 ::FillRect((HDC)hDC, &rc, hbr);
5490
5491 return true;
5492 }
5493
5494 WXHBRUSH
MSWGetBgBrushForChild(WXHDC hDC,wxWindowMSW * child)5495 wxWindowMSW::MSWGetBgBrushForChild(WXHDC hDC, wxWindowMSW *child)
5496 {
5497 // Test for the custom background brush first.
5498 WXHBRUSH hbrush = MSWGetCustomBgBrush();
5499 if ( hbrush )
5500 {
5501 // We assume that this is either a stipple or hatched brush and not a
5502 // solid one as otherwise it would have been enough to set the
5503 // background colour and such brushes need to be positioned correctly
5504 // in order to align when different windows are painted, so do it here.
5505 RECT rc;
5506 ::GetWindowRect(GetHwndOf(child), &rc);
5507
5508 // It is important to pass both points to MapWindowPoints() as in
5509 // addition to converting them to our coordinate system, this function
5510 // will also exchange the left and right coordinates if this window
5511 // uses RTL layout, which is exactly what we need here as the child
5512 // window origin is its _right_ top corner in this case and not the
5513 // left one.
5514 ::MapWindowPoints(NULL, GetHwnd(), (POINT *)&rc, 2);
5515
5516 int x = rc.left,
5517 y = rc.top;
5518 MSWAdjustBrushOrg(&x, &y);
5519
5520 if ( !::SetBrushOrgEx((HDC)hDC, -x, -y, NULL) )
5521 {
5522 wxLogLastError(wxT("SetBrushOrgEx(bg brush)"));
5523 }
5524
5525 return hbrush;
5526 }
5527
5528 // Otherwise see if we have a custom background colour.
5529 if ( m_hasBgCol )
5530 {
5531 wxBrush *
5532 brush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour());
5533
5534 return (WXHBRUSH)GetHbrushOf(*brush);
5535 }
5536
5537 return 0;
5538 }
5539
MSWGetBgBrush(WXHDC hDC)5540 WXHBRUSH wxWindowMSW::MSWGetBgBrush(WXHDC hDC)
5541 {
5542 // Use the special wxWindowBeingErased variable if it is set as the child
5543 // being erased.
5544 wxWindowMSW * const child =
5545 #if wxUSE_UXTHEME
5546 wxWindowBeingErased ? wxWindowBeingErased :
5547 #endif
5548 this;
5549
5550 for ( wxWindowMSW *win = this; win; win = win->GetParent() )
5551 {
5552 WXHBRUSH hBrush = win->MSWGetBgBrushForChild(hDC, child);
5553 if ( hBrush )
5554 return hBrush;
5555
5556 // don't use the parent background if we're not transparent
5557 if ( !win->HasTransparentBackground() )
5558 break;
5559
5560 // background is not inherited beyond top level windows
5561 if ( win->IsTopLevel() )
5562 break;
5563 }
5564
5565 return 0;
5566 }
5567
HandlePrintClient(WXHDC hDC)5568 bool wxWindowMSW::HandlePrintClient(WXHDC hDC)
5569 {
5570 // we receive this message when DrawThemeParentBackground() is
5571 // called from def window proc of several controls under XP and we
5572 // must draw properly themed background here
5573 //
5574 // note that naively I'd expect filling the client rect with the
5575 // brush returned by MSWGetBgBrush() work -- but for some reason it
5576 // doesn't and we have to call parents MSWPrintChild() which is
5577 // supposed to call DrawThemeBackground() with appropriate params
5578 //
5579 // also note that in this case lParam == PRF_CLIENT but we're
5580 // clearly expected to paint the background and nothing else!
5581
5582 if ( IsTopLevel() || InheritsBackgroundColour() )
5583 return false;
5584
5585 // sometimes we don't want the parent to handle it at all, instead
5586 // return whatever value this window wants
5587 if ( !MSWShouldPropagatePrintChild() )
5588 return MSWPrintChild(hDC, (wxWindow *)this);
5589
5590 for ( wxWindow *win = GetParent(); win; win = win->GetParent() )
5591 {
5592 if ( win->MSWPrintChild(hDC, (wxWindow *)this) )
5593 return true;
5594
5595 if ( win->IsTopLevel() || win->InheritsBackgroundColour() )
5596 break;
5597 }
5598
5599 return false;
5600 }
5601
5602 // ---------------------------------------------------------------------------
5603 // moving and resizing
5604 // ---------------------------------------------------------------------------
5605
HandleMinimize()5606 bool wxWindowMSW::HandleMinimize()
5607 {
5608 wxIconizeEvent event(m_windowId);
5609 event.SetEventObject(this);
5610
5611 return HandleWindowEvent(event);
5612 }
5613
HandleMaximize()5614 bool wxWindowMSW::HandleMaximize()
5615 {
5616 wxMaximizeEvent event(m_windowId);
5617 event.SetEventObject(this);
5618
5619 return HandleWindowEvent(event);
5620 }
5621
HandleMove(int x,int y)5622 bool wxWindowMSW::HandleMove(int x, int y)
5623 {
5624 wxPoint point(x,y);
5625 wxMoveEvent event(point, m_windowId);
5626 event.SetEventObject(this);
5627
5628 return HandleWindowEvent(event);
5629 }
5630
HandleMoving(wxRect & rect)5631 bool wxWindowMSW::HandleMoving(wxRect& rect)
5632 {
5633 wxMoveEvent event(rect, m_windowId);
5634 event.SetEventObject(this);
5635
5636 bool rc = HandleWindowEvent(event);
5637 if (rc)
5638 rect = event.GetRect();
5639 return rc;
5640 }
5641
HandleEnterSizeMove()5642 bool wxWindowMSW::HandleEnterSizeMove()
5643 {
5644 wxMoveEvent event(wxPoint(0,0), m_windowId);
5645 event.SetEventType(wxEVT_MOVE_START);
5646 event.SetEventObject(this);
5647
5648 return HandleWindowEvent(event);
5649 }
5650
HandleExitSizeMove()5651 bool wxWindowMSW::HandleExitSizeMove()
5652 {
5653 wxMoveEvent event(wxPoint(0,0), m_windowId);
5654 event.SetEventType(wxEVT_MOVE_END);
5655 event.SetEventObject(this);
5656
5657 return HandleWindowEvent(event);
5658 }
5659
5660 #if wxUSE_DEFERRED_SIZING
5661
BeginRepositioningChildren()5662 bool wxWindowMSW::BeginRepositioningChildren()
5663 {
5664 int numChildren = 0;
5665 for ( HWND child = ::GetWindow(GetHwndOf(this), GW_CHILD);
5666 child;
5667 child = ::GetWindow(child, GW_HWNDNEXT) )
5668 {
5669 numChildren ++;
5670 }
5671
5672 // Nothing is gained by deferring the repositioning of a single child.
5673 if ( numChildren < 2 )
5674 return false;
5675
5676 // Protect against valid m_hDWP being overwritten
5677 if ( m_hDWP )
5678 return false;
5679
5680 m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren);
5681 if ( !m_hDWP )
5682 {
5683 wxLogLastError(wxT("BeginDeferWindowPos"));
5684 return false;
5685 }
5686
5687 // Return true to indicate that EndDeferWindowPos() should be called.
5688 return true;
5689 }
5690
EndRepositioningChildren()5691 void wxWindowMSW::EndRepositioningChildren()
5692 {
5693 wxASSERT_MSG( m_hDWP, wxS("Shouldn't be called") );
5694
5695 // reset m_hDWP to NULL so that child windows don't try to use our
5696 // m_hDWP after we call EndDeferWindowPos() on it (this shouldn't
5697 // happen anyhow normally but who knows what weird flow of control we
5698 // may have depending on what the users EVT_SIZE handler does...)
5699 HDWP hDWP = (HDWP)m_hDWP;
5700 m_hDWP = NULL;
5701
5702 // do put all child controls in place at once
5703 if ( !::EndDeferWindowPos(hDWP) )
5704 {
5705 wxLogLastError(wxT("EndDeferWindowPos"));
5706 }
5707
5708 // Reset our children's pending pos/size values.
5709 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
5710 node;
5711 node = node->GetNext() )
5712 {
5713 wxWindowMSW * const child = node->GetData();
5714 child->MSWEndDeferWindowPos();
5715 }
5716 }
5717
5718 #endif // wxUSE_DEFERRED_SIZING
5719
HandleSize(int WXUNUSED (w),int WXUNUSED (h),WXUINT wParam)5720 bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam)
5721 {
5722 // when we resize this window, its children are probably going to be
5723 // repositioned as well, prepare to use DeferWindowPos() for them
5724 ChildrenRepositioningGuard repositionGuard(this);
5725
5726 // update this window size
5727 bool processed = false;
5728 switch ( wParam )
5729 {
5730 default:
5731 wxFAIL_MSG( wxT("unexpected WM_SIZE parameter") );
5732 // fall through nevertheless
5733 wxFALLTHROUGH;
5734
5735 case SIZE_MAXHIDE:
5736 wxFALLTHROUGH;
5737 case SIZE_MAXSHOW:
5738 // we're not interested in these messages at all
5739 break;
5740
5741 case SIZE_MINIMIZED:
5742 processed = HandleMinimize();
5743 break;
5744
5745 case SIZE_MAXIMIZED:
5746 /* processed = */ HandleMaximize();
5747 // fall through to send a normal size event as well
5748 wxFALLTHROUGH;
5749
5750 case SIZE_RESTORED:
5751 // don't use w and h parameters as they specify the client size
5752 // while according to the docs EVT_SIZE handler is supposed to
5753 // receive the total size
5754 wxSizeEvent event(GetSize(), m_windowId);
5755 event.SetEventObject(this);
5756
5757 processed = HandleWindowEvent(event);
5758 break;
5759 }
5760
5761 return processed;
5762 }
5763
HandleSizing(wxRect & rect)5764 bool wxWindowMSW::HandleSizing(wxRect& rect)
5765 {
5766 wxSizeEvent event(rect, m_windowId);
5767 event.SetEventObject(this);
5768
5769 bool rc = HandleWindowEvent(event);
5770 if (rc)
5771 rect = event.GetRect();
5772 return rc;
5773 }
5774
HandleGetMinMaxInfo(void * mmInfo)5775 bool wxWindowMSW::HandleGetMinMaxInfo(void *mmInfo)
5776 {
5777 MINMAXINFO *info = (MINMAXINFO *)mmInfo;
5778
5779 bool rc = false;
5780
5781 int minWidth = GetMinWidth(),
5782 minHeight = GetMinHeight(),
5783 maxWidth = GetMaxWidth(),
5784 maxHeight = GetMaxHeight();
5785
5786 if ( minWidth != wxDefaultCoord )
5787 {
5788 info->ptMinTrackSize.x = minWidth;
5789 rc = true;
5790 }
5791
5792 if ( minHeight != wxDefaultCoord )
5793 {
5794 info->ptMinTrackSize.y = minHeight;
5795 rc = true;
5796 }
5797
5798 if ( maxWidth != wxDefaultCoord )
5799 {
5800 info->ptMaxTrackSize.x = maxWidth;
5801 rc = true;
5802 }
5803
5804 if ( maxHeight != wxDefaultCoord )
5805 {
5806 info->ptMaxTrackSize.y = maxHeight;
5807 rc = true;
5808 }
5809
5810 return rc;
5811 }
5812
5813 // ---------------------------------------------------------------------------
5814 // command messages
5815 // ---------------------------------------------------------------------------
5816
HandleCommand(WXWORD id_,WXWORD cmd,WXHWND control)5817 bool wxWindowMSW::HandleCommand(WXWORD id_, WXWORD cmd, WXHWND control)
5818 {
5819 // sign extend to int from short before comparing with the other int ids
5820 int id = (signed short)id_;
5821
5822 #if wxUSE_MENUS_NATIVE
5823 if ( !cmd && wxCurrentPopupMenu )
5824 {
5825 wxMenu *popupMenu = wxCurrentPopupMenu;
5826 wxCurrentPopupMenu = NULL;
5827
5828 return popupMenu->MSWCommand(cmd, id);
5829 }
5830 #endif // wxUSE_MENUS_NATIVE
5831
5832 wxWindow *win = NULL;
5833
5834 // first try to find it from HWND - this works even with the broken
5835 // programs using the same ids for different controls
5836 if ( control )
5837 {
5838 win = wxFindWinFromHandle(control);
5839 }
5840
5841 // try the id
5842 if ( !win )
5843 {
5844 win = FindItem(id, control);
5845 }
5846
5847 if ( win )
5848 {
5849 return win->MSWCommand(cmd, id);
5850 }
5851
5852 // the messages sent from the in-place edit control used by the treectrl
5853 // for label editing have id == 0, but they should _not_ be treated as menu
5854 // messages (they are EN_XXX ones, in fact) so don't translate anything
5855 // coming from a control to wxEVT_MENU
5856 if ( !control )
5857 {
5858 wxCommandEvent event(wxEVT_MENU, id);
5859 event.SetEventObject(this);
5860 event.SetInt(id);
5861
5862 return HandleWindowEvent(event);
5863 }
5864 else
5865 {
5866 #if wxUSE_SPINCTRL && !defined(__WXUNIVERSAL__)
5867 // the text ctrl which is logically part of wxSpinCtrl sends WM_COMMAND
5868 // notifications to its parent which we want to reflect back to
5869 // wxSpinCtrl
5870 wxSpinCtrl *spin = wxSpinCtrl::GetSpinForTextCtrl(control);
5871 if ( spin && spin->ProcessTextCommand(cmd, id) )
5872 return true;
5873 #endif // wxUSE_SPINCTRL
5874
5875 }
5876
5877 return false;
5878 }
5879
5880 // ---------------------------------------------------------------------------
5881 // mouse events
5882 // ---------------------------------------------------------------------------
5883
InitMouseEvent(wxMouseEvent & event,int x,int y,WXUINT flags)5884 void wxWindowMSW::InitMouseEvent(wxMouseEvent& event,
5885 int x, int y,
5886 WXUINT flags)
5887 {
5888 // our client coords are not quite the same as Windows ones
5889 wxPoint pt = GetClientAreaOrigin();
5890 event.m_x = x - pt.x;
5891 event.m_y = y - pt.y;
5892
5893 event.m_shiftDown = (flags & MK_SHIFT) != 0;
5894 event.m_controlDown = (flags & MK_CONTROL) != 0;
5895 event.m_leftDown = (flags & MK_LBUTTON) != 0;
5896 event.m_middleDown = (flags & MK_MBUTTON) != 0;
5897 event.m_rightDown = (flags & MK_RBUTTON) != 0;
5898 event.m_aux1Down = (flags & MK_XBUTTON1) != 0;
5899 event.m_aux2Down = (flags & MK_XBUTTON2) != 0;
5900 event.m_altDown = ::wxIsAltDown();
5901
5902 event.SetTimestamp(::GetMessageTime());
5903
5904 event.SetEventObject(this);
5905 event.SetId(GetId());
5906
5907 gs_lastMouseEvent.pos = ClientToScreen(wxPoint(x, y));
5908 gs_lastMouseEvent.type = event.GetEventType();
5909 }
5910
HandleMouseEvent(WXUINT msg,int x,int y,WXUINT flags)5911 bool wxWindowMSW::HandleMouseEvent(WXUINT msg, int x, int y, WXUINT flags)
5912 {
5913 // the mouse events take consecutive IDs from WM_MOUSEFIRST to
5914 // WM_MOUSELAST, so it's enough to subtract WM_MOUSEMOVE == WM_MOUSEFIRST
5915 // from the message id and take the value in the table to get wxWin event
5916 // id
5917 static const wxEventType eventsMouse[] =
5918 {
5919 wxEVT_MOTION,
5920 wxEVT_LEFT_DOWN,
5921 wxEVT_LEFT_UP,
5922 wxEVT_LEFT_DCLICK,
5923 wxEVT_RIGHT_DOWN,
5924 wxEVT_RIGHT_UP,
5925 wxEVT_RIGHT_DCLICK,
5926 wxEVT_MIDDLE_DOWN,
5927 wxEVT_MIDDLE_UP,
5928 wxEVT_MIDDLE_DCLICK,
5929 0, // this one is for wxEVT_MOTION which is not used here
5930 wxEVT_AUX1_DOWN,
5931 wxEVT_AUX1_UP,
5932 wxEVT_AUX1_DCLICK,
5933 wxEVT_AUX2_DOWN,
5934 wxEVT_AUX2_UP,
5935 wxEVT_AUX2_DCLICK
5936 };
5937
5938 // the same messages are used for both auxiliary mouse buttons so we need
5939 // to adjust the index manually
5940 switch ( msg )
5941 {
5942 case WM_XBUTTONDOWN:
5943 case WM_XBUTTONUP:
5944 case WM_XBUTTONDBLCLK:
5945 if (HIWORD(flags) == XBUTTON2)
5946 msg += wxEVT_AUX2_DOWN - wxEVT_AUX1_DOWN;
5947 }
5948
5949 wxMouseEvent event(eventsMouse[msg - WM_MOUSEMOVE]);
5950 InitMouseEvent(event, x, y, flags);
5951
5952 return HandleWindowEvent(event);
5953 }
5954
HandleMouseMove(int x,int y,WXUINT flags)5955 bool wxWindowMSW::HandleMouseMove(int x, int y, WXUINT flags)
5956 {
5957 if ( !m_mouseInWindow )
5958 {
5959 // it would be wrong to assume that just because we get a mouse move
5960 // event that the mouse is inside the window: although this is usually
5961 // true, it is not if we had captured the mouse, so we need to check
5962 // the mouse coordinates here
5963 if ( !HasCapture() || IsMouseInWindow() )
5964 {
5965 // Generate an ENTER event
5966 m_mouseInWindow = true;
5967
5968 #ifdef HAVE_TRACKMOUSEEVENT
5969 typedef BOOL (WINAPI *_TrackMouseEvent_t)(LPTRACKMOUSEEVENT);
5970 static _TrackMouseEvent_t s_pfn_TrackMouseEvent;
5971 static bool s_initDone = false;
5972 if ( !s_initDone )
5973 {
5974 // see comment in wxApp::GetComCtl32Version() explaining the
5975 // use of wxLoadedDLL
5976 wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
5977 if ( dllComCtl32.IsLoaded() )
5978 {
5979 s_pfn_TrackMouseEvent = (_TrackMouseEvent_t)
5980 dllComCtl32.RawGetSymbol(wxT("_TrackMouseEvent"));
5981 }
5982
5983 s_initDone = true;
5984 }
5985
5986 if ( s_pfn_TrackMouseEvent )
5987 {
5988 WinStruct<TRACKMOUSEEVENT> trackinfo;
5989
5990 trackinfo.dwFlags = TME_LEAVE;
5991 trackinfo.hwndTrack = GetHwnd();
5992
5993 (*s_pfn_TrackMouseEvent)(&trackinfo);
5994 }
5995 #endif // HAVE_TRACKMOUSEEVENT
5996
5997 wxMouseEvent event(wxEVT_ENTER_WINDOW);
5998 InitMouseEvent(event, x, y, flags);
5999
6000 (void)HandleWindowEvent(event);
6001 }
6002 }
6003 #ifdef HAVE_TRACKMOUSEEVENT
6004 else // mouse not in window
6005 {
6006 // Check if we need to send a LEAVE event
6007 // Windows doesn't send WM_MOUSELEAVE if the mouse has been captured so
6008 // send it here if we are using native mouse leave tracking
6009 if ( HasCapture() && !IsMouseInWindow() )
6010 {
6011 GenerateMouseLeave();
6012 }
6013 }
6014 #endif // HAVE_TRACKMOUSEEVENT
6015
6016 // Windows often generates mouse events even if mouse position hasn't
6017 // changed (http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/66576)
6018 //
6019 // Filter this out as it can result in unexpected behaviour compared to
6020 // other platforms
6021 if ( gs_lastMouseEvent.type == wxEVT_RIGHT_DOWN ||
6022 gs_lastMouseEvent.type == wxEVT_LEFT_DOWN ||
6023 gs_lastMouseEvent.type == wxEVT_MIDDLE_DOWN ||
6024 gs_lastMouseEvent.type == wxEVT_MOTION )
6025 {
6026 if ( ClientToScreen(wxPoint(x, y)) == gs_lastMouseEvent.pos )
6027 {
6028 gs_lastMouseEvent.type = wxEVT_MOTION;
6029
6030 return false;
6031 }
6032 }
6033
6034 return HandleMouseEvent(WM_MOUSEMOVE, x, y, flags);
6035 }
6036
6037
6038 bool
HandleMouseWheel(wxMouseWheelAxis axis,WXWPARAM wParam,WXLPARAM lParam)6039 wxWindowMSW::HandleMouseWheel(wxMouseWheelAxis axis,
6040 WXWPARAM wParam, WXLPARAM lParam)
6041 {
6042 #if wxUSE_MOUSEWHEEL
6043 // notice that WM_MOUSEWHEEL position is in screen coords (as it's
6044 // forwarded up to the parent by DefWindowProc()) and not in the client
6045 // ones as all the other messages, translate them to the client coords for
6046 // consistency -- but do it using Windows function and not our own one
6047 // because InitMouseEvent() expects coordinates in Windows client
6048 // coordinates and not wx ones (the difference being the height of the
6049 // toolbar, if any).
6050 POINT pt;
6051 pt.x = GET_X_LPARAM(lParam);
6052 pt.y = GET_Y_LPARAM(lParam);
6053 ::ScreenToClient(GetHwnd(), &pt);
6054
6055 wxMouseEvent event(wxEVT_MOUSEWHEEL);
6056 InitMouseEvent(event, pt.x, pt.y, LOWORD(wParam));
6057 event.m_wheelRotation = (short)HIWORD(wParam);
6058 event.m_wheelDelta = WHEEL_DELTA;
6059 event.m_wheelAxis = axis;
6060
6061 static int s_linesPerRotation = -1;
6062 if ( s_linesPerRotation == -1 )
6063 {
6064 if ( !::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
6065 &s_linesPerRotation, 0))
6066 {
6067 // this is not supposed to happen
6068 wxLogLastError(wxT("SystemParametersInfo(GETWHEELSCROLLLINES)"));
6069
6070 // the default is 3, so use it if SystemParametersInfo() failed
6071 s_linesPerRotation = 3;
6072 }
6073 }
6074
6075 static int s_columnsPerRotation = -1;
6076 if ( s_columnsPerRotation == -1 )
6077 {
6078 if ( !::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0,
6079 &s_columnsPerRotation, 0))
6080 {
6081 // this setting is not supported on Windows 2000/XP, so use the value of 1
6082 // http://msdn.microsoft.com/en-us/library/ms997498.aspx
6083 s_columnsPerRotation = 1;
6084 }
6085 }
6086
6087 event.m_linesPerAction = s_linesPerRotation;
6088 event.m_columnsPerAction = s_columnsPerRotation;
6089 return HandleWindowEvent(event);
6090
6091 #else // !wxUSE_MOUSEWHEEL
6092 wxUnusedVar(wParam);
6093 wxUnusedVar(lParam);
6094
6095 return false;
6096 #endif // wxUSE_MOUSEWHEEL/!wxUSE_MOUSEWHEEL
6097 }
6098
GenerateMouseLeave()6099 void wxWindowMSW::GenerateMouseLeave()
6100 {
6101 m_mouseInWindow = false;
6102
6103 int state = 0;
6104 if ( wxIsShiftDown() )
6105 state |= MK_SHIFT;
6106 if ( wxIsCtrlDown() )
6107 state |= MK_CONTROL;
6108
6109 // Only the high-order bit should be tested
6110 if ( GetKeyState( VK_LBUTTON ) & (1<<15) )
6111 state |= MK_LBUTTON;
6112 if ( GetKeyState( VK_MBUTTON ) & (1<<15) )
6113 state |= MK_MBUTTON;
6114 if ( GetKeyState( VK_RBUTTON ) & (1<<15) )
6115 state |= MK_RBUTTON;
6116
6117 POINT pt;
6118 wxGetCursorPosMSW(&pt);
6119
6120 // we need to have client coordinates here for symmetry with
6121 // wxEVT_ENTER_WINDOW
6122 RECT rect = wxGetWindowRect(GetHwnd());
6123 pt.x -= rect.left;
6124 pt.y -= rect.top;
6125
6126 wxMouseEvent event(wxEVT_LEAVE_WINDOW);
6127 InitMouseEvent(event, pt.x, pt.y, state);
6128
6129 (void)HandleWindowEvent(event);
6130 }
6131
6132 #ifdef WM_GESTURE
6133 // ---------------------------------------------------------------------------
6134 // Gesture events
6135 // ---------------------------------------------------------------------------
6136
InitGestureEvent(wxGestureEvent & event,const wxPoint & pt,WXDWORD flags)6137 bool wxWindowMSW::InitGestureEvent(wxGestureEvent& event,
6138 const wxPoint& pt,
6139 WXDWORD flags)
6140 {
6141 event.SetEventObject(this);
6142 event.SetTimestamp(::GetMessageTime());
6143 event.SetPosition(pt);
6144
6145 if ( flags & GF_BEGIN )
6146 event.SetGestureStart();
6147
6148 if ( flags & GF_END )
6149 event.SetGestureEnd();
6150
6151 return (flags & GF_BEGIN) != 0;
6152 }
6153
HandlePanGesture(const wxPoint & pt,WXDWORD flags)6154 bool wxWindowMSW::HandlePanGesture(const wxPoint& pt, WXDWORD flags)
6155 {
6156 // wxEVT_GESTURE_PAN
6157 wxPanGestureEvent event(GetId());
6158
6159 // This is used to calculate the pan delta.
6160 static wxPoint s_previousLocation;
6161
6162 // If the gesture has just started, store the current point to determine
6163 // the pan delta later on.
6164 if ( InitGestureEvent(event, pt, flags) )
6165 {
6166 s_previousLocation = pt;
6167 }
6168
6169 // Determine the horizontal and vertical changes
6170 event.SetDelta(pt - s_previousLocation);
6171
6172 // Update the last gesture event point
6173 s_previousLocation = pt;
6174
6175 return HandleWindowEvent(event);
6176 }
6177
HandleZoomGesture(const wxPoint & pt,WXDWORD fingerDistance,WXDWORD flags)6178 bool wxWindowMSW::HandleZoomGesture(const wxPoint& pt,
6179 WXDWORD fingerDistance,
6180 WXDWORD flags)
6181 {
6182 // wxEVT_GESTURE_ZOOM
6183 wxZoomGestureEvent event(GetId());
6184
6185 // These are used to calculate the center of the zoom and zoom factor
6186 static wxPoint s_previousLocation;
6187 static int s_intialFingerDistance;
6188
6189 // This flag indicates that the gesture has just started, store the current
6190 // point and distance between the fingers for future calculations.
6191 if ( InitGestureEvent(event, pt, flags) )
6192 {
6193 s_previousLocation = pt;
6194 s_intialFingerDistance = fingerDistance;
6195 }
6196
6197 // Calculate center point of the zoom. Human beings are not very good at
6198 // moving two fingers at exactly the same rate outwards/inwards and there
6199 // is usually some error, which can cause the center to shift slightly. So,
6200 // it is recommended to take the average of center of fingers in the
6201 // current and last positions.
6202 const wxPoint ptCenter = (s_previousLocation + pt)/2;
6203
6204 const double zoomFactor = (double) fingerDistance / s_intialFingerDistance;
6205
6206 event.SetZoomFactor(zoomFactor);
6207
6208 event.SetPosition(ptCenter);
6209
6210 // Update gesture event point
6211 s_previousLocation = pt;
6212
6213 return HandleWindowEvent(event);
6214 }
6215
HandleRotateGesture(const wxPoint & pt,WXDWORD angleArgument,WXDWORD flags)6216 bool wxWindowMSW::HandleRotateGesture(const wxPoint& pt,
6217 WXDWORD angleArgument,
6218 WXDWORD flags)
6219 {
6220 // wxEVT_GESTURE_ROTATE
6221 wxRotateGestureEvent event(GetId());
6222
6223 if ( InitGestureEvent(event, pt, flags) )
6224 {
6225 event.SetRotationAngle(angleArgument);
6226 }
6227 else // Not the first event.
6228 {
6229 // Use angleArgument to obtain the cumulative angle since the gesture
6230 // was first started. This angle is in radians and MSW returns negative
6231 // angle for clockwise rotation and positive otherwise, so, multiply
6232 // angle by -1 for positive angle for clockwise and negative in case of
6233 // counterclockwise.
6234 double angle = -GID_ROTATE_ANGLE_FROM_ARGUMENT(angleArgument);
6235
6236 // If the rotation is anti-clockwise convert the angle to its
6237 // corresponding positive value in a clockwise sense.
6238 if ( angle < 0 )
6239 {
6240 angle += 2 * M_PI;
6241 }
6242
6243 // Set the angle
6244 event.SetRotationAngle(angle);
6245 }
6246
6247 return HandleWindowEvent(event);
6248 }
6249
HandleTwoFingerTap(const wxPoint & pt,WXDWORD flags)6250 bool wxWindowMSW::HandleTwoFingerTap(const wxPoint& pt, WXDWORD flags)
6251 {
6252 // wxEVT_TWO_FINGER_TAP
6253 wxTwoFingerTapEvent event(GetId());
6254
6255 InitGestureEvent(event, pt, flags);
6256
6257 return HandleWindowEvent(event);
6258 }
6259
HandlePressAndTap(const wxPoint & pt,WXDWORD flags)6260 bool wxWindowMSW::HandlePressAndTap(const wxPoint& pt, WXDWORD flags)
6261 {
6262 wxPressAndTapEvent event(GetId());
6263
6264 InitGestureEvent(event, pt, flags);
6265
6266 return HandleWindowEvent(event);
6267 }
6268 #endif // WM_GESTURE
6269
6270 // ---------------------------------------------------------------------------
6271 // keyboard handling
6272 // ---------------------------------------------------------------------------
6273
6274 namespace
6275 {
6276
6277 // Implementation of InitAnyKeyEvent() which can also be used when there is no
6278 // associated window: this can happen for the wxEVT_CHAR_HOOK events created by
6279 // the global keyboard hook (e.g. the event might have happened in a non-wx
6280 // window).
6281 void
MSWInitAnyKeyEvent(wxKeyEvent & event,WXWPARAM wParam,WXLPARAM lParam,const wxWindowBase * win)6282 MSWInitAnyKeyEvent(wxKeyEvent& event,
6283 WXWPARAM wParam,
6284 WXLPARAM lParam,
6285 const wxWindowBase *win /* may be NULL */)
6286 {
6287 if ( win )
6288 {
6289 event.SetId(win->GetId());
6290 event.SetEventObject(const_cast<wxWindowBase *>(win));
6291 }
6292 else // No associated window.
6293 {
6294 // Use wxID_ANY for compatibility with the old code even if wxID_NONE
6295 // would arguably make more sense.
6296 event.SetId(wxID_ANY);
6297 }
6298
6299 event.m_shiftDown = wxIsShiftDown();
6300 event.m_controlDown = wxIsCtrlDown();
6301 event.m_altDown = (HIWORD(lParam) & KF_ALTDOWN) == KF_ALTDOWN;
6302
6303 event.m_rawCode = (wxUint32) wParam;
6304 event.m_rawFlags = (wxUint32) lParam;
6305 event.SetTimestamp(::GetMessageTime());
6306 }
6307
6308 } // anonymous namespace
6309
6310 void
InitAnyKeyEvent(wxKeyEvent & event,WXWPARAM wParam,WXLPARAM lParam) const6311 wxWindowMSW::InitAnyKeyEvent(wxKeyEvent& event,
6312 WXWPARAM wParam,
6313 WXLPARAM lParam) const
6314 {
6315 MSWInitAnyKeyEvent(event, wParam, lParam, this);
6316 }
6317
6318 wxKeyEvent
CreateKeyEvent(wxEventType evType,WXWPARAM wParam,WXLPARAM lParam) const6319 wxWindowMSW::CreateKeyEvent(wxEventType evType,
6320 WXWPARAM wParam,
6321 WXLPARAM lParam) const
6322 {
6323 // Catch any attempts to use this with WM_CHAR, it wouldn't work because
6324 // wParam is supposed to be a virtual key and not a character here.
6325 wxASSERT_MSG( evType != wxEVT_CHAR && evType != wxEVT_CHAR_HOOK,
6326 "CreateKeyEvent() can't be used for char events" );
6327
6328 wxKeyEvent event(evType);
6329 InitAnyKeyEvent(event, wParam, lParam);
6330
6331 event.m_keyCode = wxMSWKeyboard::VKToWX
6332 (
6333 wParam,
6334 lParam
6335 #if wxUSE_UNICODE
6336 , &event.m_uniChar
6337 #endif // wxUSE_UNICODE
6338 );
6339
6340 return event;
6341 }
6342
6343 wxKeyEvent
CreateCharEvent(wxEventType evType,WXWPARAM wParam,WXLPARAM lParam) const6344 wxWindowMSW::CreateCharEvent(wxEventType evType,
6345 WXWPARAM wParam,
6346 WXLPARAM lParam) const
6347 {
6348 wxKeyEvent event(evType);
6349 InitAnyKeyEvent(event, wParam, lParam);
6350
6351 #if wxUSE_UNICODE
6352 // TODO: wParam uses UTF-16 so this is incorrect for characters outside of
6353 // the BMP, we should use WM_UNICHAR to handle them.
6354 event.m_uniChar = wParam;
6355 #endif // wxUSE_UNICODE
6356
6357 // Set non-Unicode key code too for compatibility if possible.
6358 if ( wParam < 0x80 )
6359 {
6360 // It's an ASCII character, no need to translate it.
6361 event.m_keyCode = wParam;
6362 }
6363 else
6364 {
6365 // Check if this key can be represented (as a single character) in the
6366 // current locale.
6367 const wchar_t wc = wParam;
6368 char ch;
6369 if ( wxConvLibc.FromWChar(&ch, 1, &wc, 1) != wxCONV_FAILED )
6370 {
6371 // For compatibility continue to provide the key code in this field
6372 // even though using GetUnicodeKey() is recommended now.
6373 event.m_keyCode = static_cast<unsigned char>(ch);
6374 }
6375 //else: Key can't be represented in the current locale, leave m_keyCode
6376 // as WXK_NONE and use GetUnicodeKey() to access the character.
6377 }
6378
6379 // the alphanumeric keys produced by pressing AltGr+something on European
6380 // keyboards have both Ctrl and Alt modifiers which may confuse the user
6381 // code as, normally, keys with Ctrl and/or Alt don't result in anything
6382 // alphanumeric, so pretend that there are no modifiers at all (the
6383 // KEY_DOWN event would still have the correct modifiers if they're really
6384 // needed)
6385 if ( event.m_controlDown && event.m_altDown &&
6386 (event.m_keyCode >= 32 && event.m_keyCode < 256) )
6387 {
6388 event.m_controlDown =
6389 event.m_altDown = false;
6390 }
6391
6392 return event;
6393 }
6394
6395 // isASCII is true only when we're called from WM_CHAR handler and not from
6396 // WM_KEYDOWN one
HandleChar(WXWPARAM wParam,WXLPARAM lParam)6397 bool wxWindowMSW::HandleChar(WXWPARAM wParam, WXLPARAM lParam)
6398 {
6399 wxKeyEvent event(CreateCharEvent(wxEVT_CHAR, wParam, lParam));
6400 return HandleWindowEvent(event);
6401 }
6402
HandleKeyDown(WXWPARAM wParam,WXLPARAM lParam)6403 bool wxWindowMSW::HandleKeyDown(WXWPARAM wParam, WXLPARAM lParam)
6404 {
6405 wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam));
6406 return HandleWindowEvent(event);
6407 }
6408
HandleKeyUp(WXWPARAM wParam,WXLPARAM lParam)6409 bool wxWindowMSW::HandleKeyUp(WXWPARAM wParam, WXLPARAM lParam)
6410 {
6411 wxKeyEvent event(CreateKeyEvent(wxEVT_KEY_UP, wParam, lParam));
6412 return HandleWindowEvent(event);
6413 }
6414
6415 #if wxUSE_MENUS
HandleMenuChar(int chAccel,WXLPARAM lParam)6416 int wxWindowMSW::HandleMenuChar(int chAccel,
6417 WXLPARAM lParam)
6418 {
6419 const HMENU hmenu = (HMENU)lParam;
6420
6421 WinStruct<MENUITEMINFO> mii;
6422
6423 // use MIIM_FTYPE to know if the item is ownerdrawn or not
6424 mii.fMask = MIIM_FTYPE | MIIM_DATA;
6425
6426 // find if we have this letter in any owner drawn item
6427 const int count = ::GetMenuItemCount(hmenu);
6428 for ( int i = 0; i < count; i++ )
6429 {
6430 if ( ::GetMenuItemInfo(hmenu, i, TRUE, &mii) )
6431 {
6432 if ( mii.fType == MFT_OWNERDRAW )
6433 {
6434 // dwItemData member of the MENUITEMINFO is a
6435 // pointer to the associated wxMenuItem -- see the
6436 // menu creation code
6437 wxMenuItem *item = (wxMenuItem*)mii.dwItemData;
6438
6439 const wxString label(item->GetItemLabel());
6440 const wxChar *p = wxStrchr(label.t_str(), wxT('&'));
6441 while ( p++ )
6442 {
6443 if ( *p == wxT('&') )
6444 {
6445 // this is not the accel char, find the real one
6446 p = wxStrchr(p + 1, wxT('&'));
6447 }
6448 else // got the accel char
6449 {
6450 // FIXME-UNICODE: this comparison doesn't risk to work
6451 // for non ASCII accelerator characters I'm afraid, but
6452 // what can we do?
6453 if ( (wchar_t)wxToupper(*p) == (wchar_t)chAccel )
6454 {
6455 return i;
6456 }
6457 else
6458 {
6459 // this one doesn't match
6460 break;
6461 }
6462 }
6463 }
6464 }
6465 }
6466 else // failed to get the menu text?
6467 {
6468 // it's not fatal, so don't show error, but still log it
6469 wxLogLastError(wxT("GetMenuItemInfo"));
6470 }
6471 }
6472 return wxNOT_FOUND;
6473 }
6474
6475 #endif // wxUSE_MENUS
6476
HandleClipboardEvent(WXUINT nMsg)6477 bool wxWindowMSW::HandleClipboardEvent(WXUINT nMsg)
6478 {
6479 const wxEventType type = nMsg == WM_CUT ? wxEVT_TEXT_CUT
6480 : nMsg == WM_COPY ? wxEVT_TEXT_COPY
6481 : /* nMsg == WM_PASTE */ wxEVT_TEXT_PASTE;
6482 wxClipboardTextEvent evt(type, GetId());
6483
6484 evt.SetEventObject(this);
6485
6486 return HandleWindowEvent(evt);
6487 }
6488
6489
6490 // ---------------------------------------------------------------------------
6491 // scrolling
6492 // ---------------------------------------------------------------------------
6493
MSWOnScroll(int orientation,WXWORD wParam,WXWORD pos,WXHWND control)6494 bool wxWindowMSW::MSWOnScroll(int orientation, WXWORD wParam,
6495 WXWORD pos, WXHWND control)
6496 {
6497 if ( control && control != m_hWnd ) // Prevent infinite recursion
6498 {
6499 wxWindow *child = wxFindWinFromHandle(control);
6500 if ( child )
6501 return child->MSWOnScroll(orientation, wParam, pos, control);
6502 }
6503
6504 wxScrollWinEvent event;
6505 event.SetPosition(pos);
6506 event.SetOrientation(orientation);
6507 event.SetEventObject(this);
6508
6509 switch ( wParam )
6510 {
6511 case SB_TOP:
6512 event.SetEventType(wxEVT_SCROLLWIN_TOP);
6513 break;
6514
6515 case SB_BOTTOM:
6516 event.SetEventType(wxEVT_SCROLLWIN_BOTTOM);
6517 break;
6518
6519 case SB_LINEUP:
6520 event.SetEventType(wxEVT_SCROLLWIN_LINEUP);
6521 break;
6522
6523 case SB_LINEDOWN:
6524 event.SetEventType(wxEVT_SCROLLWIN_LINEDOWN);
6525 break;
6526
6527 case SB_PAGEUP:
6528 event.SetEventType(wxEVT_SCROLLWIN_PAGEUP);
6529 break;
6530
6531 case SB_PAGEDOWN:
6532 event.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN);
6533 break;
6534
6535 case SB_THUMBPOSITION:
6536 case SB_THUMBTRACK:
6537 // under Win32, the scrollbar range and position are 32 bit integers,
6538 // but WM_[HV]SCROLL only carry the low 16 bits of them, so we must
6539 // explicitly query the scrollbar for the correct position (this must
6540 // be done only for these two SB_ events as they are the only one
6541 // carrying the scrollbar position)
6542 {
6543 WinStruct<SCROLLINFO> scrollInfo;
6544 scrollInfo.fMask = SIF_TRACKPOS;
6545
6546 if ( !::GetScrollInfo(GetHwnd(),
6547 WXOrientToSB(orientation),
6548 &scrollInfo) )
6549 {
6550 // Not necessarily an error, if there are no scrollbars yet.
6551 // wxLogLastError(wxT("GetScrollInfo"));
6552 }
6553
6554 event.SetPosition(scrollInfo.nTrackPos);
6555 }
6556
6557 event.SetEventType( wParam == SB_THUMBPOSITION
6558 ? wxEVT_SCROLLWIN_THUMBRELEASE
6559 : wxEVT_SCROLLWIN_THUMBTRACK );
6560 break;
6561
6562 default:
6563 return false;
6564 }
6565
6566 return HandleWindowEvent(event);
6567 }
6568
6569 // ----------------------------------------------------------------------------
6570 // custom message handlers
6571 // ----------------------------------------------------------------------------
6572
6573 /* static */ bool
MSWRegisterMessageHandler(int msg,MSWMessageHandler handler)6574 wxWindowMSW::MSWRegisterMessageHandler(int msg, MSWMessageHandler handler)
6575 {
6576 wxCHECK_MSG( gs_messageHandlers.find(msg) == gs_messageHandlers.end(),
6577 false, wxT("registering handler for the same message twice") );
6578
6579 gs_messageHandlers[msg] = handler;
6580 return true;
6581 }
6582
6583 /* static */ void
MSWUnregisterMessageHandler(int msg,MSWMessageHandler handler)6584 wxWindowMSW::MSWUnregisterMessageHandler(int msg, MSWMessageHandler handler)
6585 {
6586 const MSWMessageHandlers::iterator i = gs_messageHandlers.find(msg);
6587 wxCHECK_RET( i != gs_messageHandlers.end() && i->second == handler,
6588 wxT("unregistering non-registered handler?") );
6589
6590 gs_messageHandlers.erase(i);
6591 }
6592
6593 // ===========================================================================
6594 // global functions
6595 // ===========================================================================
6596
wxGetCharSize(WXHWND wnd,int * x,int * y,const wxFont & the_font)6597 void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont& the_font)
6598 {
6599 TEXTMETRIC tm;
6600 HDC dc = ::GetDC((HWND) wnd);
6601 HFONT was = 0;
6602
6603 // the_font.UseResource();
6604 // the_font.RealizeResource();
6605 HFONT fnt = (HFONT)the_font.GetResourceHandle(); // const_cast
6606 if ( fnt )
6607 was = (HFONT) SelectObject(dc,fnt);
6608
6609 GetTextMetrics(dc, &tm);
6610 if ( fnt && was )
6611 {
6612 SelectObject(dc,was);
6613 }
6614 ReleaseDC((HWND)wnd, dc);
6615
6616 if ( x )
6617 *x = tm.tmAveCharWidth;
6618 if ( y )
6619 *y = tm.tmHeight + tm.tmExternalLeading;
6620
6621 // the_font.ReleaseResource();
6622 }
6623
6624 // ----------------------------------------------------------------------------
6625 // keyboard codes
6626 // ----------------------------------------------------------------------------
6627
6628 namespace wxMSWKeyboard
6629 {
6630
6631 namespace
6632 {
6633
6634 // use the "extended" bit of lParam to distinguish extended keys from normal
6635 // keys as the same virtual key code is sent for both by Windows
6636 inline
ChooseNormalOrExtended(int lParam,int keyNormal,int keyExtended)6637 int ChooseNormalOrExtended(int lParam, int keyNormal, int keyExtended)
6638 {
6639 // except that if lParam is 0, it means we don't have real lParam from
6640 // WM_KEYDOWN but are just translating just a VK constant (e.g. done from
6641 // msw/treectrl.cpp when processing TVN_KEYDOWN) -- then assume this is a
6642 // non-numpad (hence extended) key as this is a more common case
6643 return !lParam || (HIWORD(lParam) & KF_EXTENDED) ? keyExtended : keyNormal;
6644 }
6645
6646 // this array contains the Windows virtual key codes which map one to one to
6647 // WXK_xxx constants and is used in wxMSWKeyboard::VKToWX/WXToVK() below
6648 //
6649 // note that keys having a normal and numpad version (e.g. WXK_HOME and
6650 // WXK_NUMPAD_HOME) are not included in this table as the mapping is not 1-to-1
6651 const struct wxKeyMapping
6652 {
6653 int vk;
6654 wxKeyCode wxk;
6655 } gs_specialKeys[] =
6656 {
6657 { VK_CANCEL, WXK_CANCEL },
6658 { VK_BACK, WXK_BACK },
6659 { VK_TAB, WXK_TAB },
6660 { VK_CLEAR, WXK_CLEAR },
6661 { VK_SHIFT, WXK_SHIFT },
6662 { VK_CONTROL, WXK_CONTROL },
6663 { VK_MENU , WXK_ALT },
6664 { VK_PAUSE, WXK_PAUSE },
6665 { VK_CAPITAL, WXK_CAPITAL },
6666 { VK_SPACE, WXK_SPACE },
6667 { VK_ESCAPE, WXK_ESCAPE },
6668 { VK_SELECT, WXK_SELECT },
6669 { VK_PRINT, WXK_PRINT },
6670 { VK_EXECUTE, WXK_EXECUTE },
6671 { VK_SNAPSHOT, WXK_SNAPSHOT },
6672 { VK_HELP, WXK_HELP },
6673
6674 { VK_NUMPAD0, WXK_NUMPAD0 },
6675 { VK_NUMPAD1, WXK_NUMPAD1 },
6676 { VK_NUMPAD2, WXK_NUMPAD2 },
6677 { VK_NUMPAD3, WXK_NUMPAD3 },
6678 { VK_NUMPAD4, WXK_NUMPAD4 },
6679 { VK_NUMPAD5, WXK_NUMPAD5 },
6680 { VK_NUMPAD6, WXK_NUMPAD6 },
6681 { VK_NUMPAD7, WXK_NUMPAD7 },
6682 { VK_NUMPAD8, WXK_NUMPAD8 },
6683 { VK_NUMPAD9, WXK_NUMPAD9 },
6684 { VK_MULTIPLY, WXK_NUMPAD_MULTIPLY },
6685 { VK_ADD, WXK_NUMPAD_ADD },
6686 { VK_SUBTRACT, WXK_NUMPAD_SUBTRACT },
6687 { VK_DECIMAL, WXK_NUMPAD_DECIMAL },
6688 { VK_DIVIDE, WXK_NUMPAD_DIVIDE },
6689
6690 { VK_F1, WXK_F1 },
6691 { VK_F2, WXK_F2 },
6692 { VK_F3, WXK_F3 },
6693 { VK_F4, WXK_F4 },
6694 { VK_F5, WXK_F5 },
6695 { VK_F6, WXK_F6 },
6696 { VK_F7, WXK_F7 },
6697 { VK_F8, WXK_F8 },
6698 { VK_F9, WXK_F9 },
6699 { VK_F10, WXK_F10 },
6700 { VK_F11, WXK_F11 },
6701 { VK_F12, WXK_F12 },
6702 { VK_F13, WXK_F13 },
6703 { VK_F14, WXK_F14 },
6704 { VK_F15, WXK_F15 },
6705 { VK_F16, WXK_F16 },
6706 { VK_F17, WXK_F17 },
6707 { VK_F18, WXK_F18 },
6708 { VK_F19, WXK_F19 },
6709 { VK_F20, WXK_F20 },
6710 { VK_F21, WXK_F21 },
6711 { VK_F22, WXK_F22 },
6712 { VK_F23, WXK_F23 },
6713 { VK_F24, WXK_F24 },
6714
6715 { VK_NUMLOCK, WXK_NUMLOCK },
6716 { VK_SCROLL, WXK_SCROLL },
6717
6718 { VK_LWIN, WXK_WINDOWS_LEFT },
6719 { VK_RWIN, WXK_WINDOWS_RIGHT },
6720 { VK_APPS, WXK_WINDOWS_MENU },
6721
6722 { VK_BROWSER_BACK, WXK_BROWSER_BACK },
6723 { VK_BROWSER_FORWARD, WXK_BROWSER_FORWARD },
6724 { VK_BROWSER_REFRESH, WXK_BROWSER_REFRESH },
6725 { VK_BROWSER_STOP, WXK_BROWSER_STOP },
6726 { VK_BROWSER_SEARCH, WXK_BROWSER_SEARCH },
6727 { VK_BROWSER_FAVORITES, WXK_BROWSER_FAVORITES },
6728 { VK_BROWSER_HOME, WXK_BROWSER_HOME },
6729 { VK_VOLUME_MUTE, WXK_VOLUME_MUTE },
6730 { VK_VOLUME_DOWN, WXK_VOLUME_DOWN },
6731 { VK_VOLUME_UP, WXK_VOLUME_UP },
6732 { VK_MEDIA_NEXT_TRACK, WXK_MEDIA_NEXT_TRACK },
6733 { VK_MEDIA_PREV_TRACK, WXK_MEDIA_PREV_TRACK },
6734 { VK_MEDIA_STOP, WXK_MEDIA_STOP },
6735 { VK_MEDIA_PLAY_PAUSE, WXK_MEDIA_PLAY_PAUSE },
6736 { VK_LAUNCH_MAIL, WXK_LAUNCH_MAIL },
6737 { VK_LAUNCH_APP1, WXK_LAUNCH_APP1 },
6738 { VK_LAUNCH_APP2, WXK_LAUNCH_APP2 },
6739 };
6740
6741 } // anonymous namespace
6742
VKToWX(WXWORD vk,WXLPARAM lParam,wchar_t * uc)6743 int VKToWX(WXWORD vk, WXLPARAM lParam, wchar_t *uc)
6744 {
6745 int wxk;
6746
6747 // check the table first
6748 for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ )
6749 {
6750 if ( gs_specialKeys[n].vk == vk )
6751 {
6752 wxk = gs_specialKeys[n].wxk;
6753 if ( wxk < WXK_START )
6754 {
6755 // Unicode code for this key is the same as its ASCII code.
6756 if ( uc )
6757 *uc = wxk;
6758 }
6759
6760 return wxk;
6761 }
6762 }
6763
6764 // keys requiring special handling
6765 switch ( vk )
6766 {
6767 case VK_OEM_1:
6768 case VK_OEM_PLUS:
6769 case VK_OEM_COMMA:
6770 case VK_OEM_MINUS:
6771 case VK_OEM_PERIOD:
6772 case VK_OEM_2:
6773 case VK_OEM_3:
6774 case VK_OEM_4:
6775 case VK_OEM_5:
6776 case VK_OEM_6:
6777 case VK_OEM_7:
6778 case VK_OEM_8:
6779 case VK_OEM_102:
6780 // MapVirtualKey() returns 0 if it fails to convert the virtual
6781 // key which nicely corresponds to our WXK_NONE.
6782 wxk = ::MapVirtualKey(vk, MAPVK_VK_TO_CHAR);
6783
6784 if ( HIWORD(wxk) & 0x8000 )
6785 {
6786 // It's a dead key and we don't return anything at all for them
6787 // as we simply don't have any way to indicate the difference
6788 // between e.g. a normal "'" and "'" as a dead key -- and
6789 // generating the same events for them just doesn't seem like a
6790 // good idea.
6791 wxk = WXK_NONE;
6792 }
6793
6794 // In any case return this as a Unicode character value.
6795 if ( uc )
6796 *uc = wxk;
6797
6798 // For compatibility with the old non-Unicode code we continue
6799 // returning key codes for Latin-1 characters directly
6800 // (normally it would really only make sense to do it for the
6801 // ASCII characters, not Latin-1 ones).
6802 if ( wxk > 255 )
6803 {
6804 // But for anything beyond this we can only return the key
6805 // value as a real Unicode character, not a wxKeyCode
6806 // because this enum values clash with Unicode characters
6807 // (e.g. WXK_LBUTTON also happens to be U+012C a.k.a.
6808 // "LATIN CAPITAL LETTER I WITH BREVE").
6809 wxk = WXK_NONE;
6810 }
6811 break;
6812
6813 // handle extended keys
6814 case VK_PRIOR:
6815 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEUP, WXK_PAGEUP);
6816 break;
6817
6818 case VK_NEXT:
6819 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_PAGEDOWN, WXK_PAGEDOWN);
6820 break;
6821
6822 case VK_END:
6823 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_END, WXK_END);
6824 break;
6825
6826 case VK_HOME:
6827 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_HOME, WXK_HOME);
6828 break;
6829
6830 case VK_LEFT:
6831 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_LEFT, WXK_LEFT);
6832 break;
6833
6834 case VK_UP:
6835 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_UP, WXK_UP);
6836 break;
6837
6838 case VK_RIGHT:
6839 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_RIGHT, WXK_RIGHT);
6840 break;
6841
6842 case VK_DOWN:
6843 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DOWN, WXK_DOWN);
6844 break;
6845
6846 case VK_INSERT:
6847 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_INSERT, WXK_INSERT);
6848 break;
6849
6850 case VK_DELETE:
6851 wxk = ChooseNormalOrExtended(lParam, WXK_NUMPAD_DELETE, WXK_DELETE);
6852
6853 if ( uc )
6854 *uc = WXK_DELETE;
6855 break;
6856
6857 case VK_RETURN:
6858 // don't use ChooseNormalOrExtended() here as the keys are reversed
6859 // here: numpad enter is the extended one
6860 wxk = HIWORD(lParam) & KF_EXTENDED ? WXK_NUMPAD_ENTER : WXK_RETURN;
6861
6862 if ( uc )
6863 *uc = WXK_RETURN;
6864 break;
6865
6866 default:
6867 if ( (vk >= '0' && vk <= '9') || (vk >= 'A' && vk <= 'Z') )
6868 {
6869 // A simple alphanumeric key and the values of them coincide in
6870 // Windows and wx for both ASCII and Unicode codes.
6871 wxk = vk;
6872 }
6873 else // Something we simply don't know about at all.
6874 {
6875 wxk = WXK_NONE;
6876 }
6877
6878 if ( uc )
6879 *uc = vk;
6880 }
6881
6882 return wxk;
6883 }
6884
WXToVK(int wxk,bool * isExtended)6885 WXWORD WXToVK(int wxk, bool *isExtended)
6886 {
6887 // check the table first
6888 for ( size_t n = 0; n < WXSIZEOF(gs_specialKeys); n++ )
6889 {
6890 if ( gs_specialKeys[n].wxk == wxk )
6891 {
6892 // All extended keys (i.e. non-numpad versions of the keys that
6893 // exist both in the numpad and outside of it) are dealt with
6894 // below.
6895 if ( isExtended )
6896 *isExtended = false;
6897
6898 return gs_specialKeys[n].vk;
6899 }
6900 }
6901
6902 // and then check for special keys not included in the table
6903 bool extended = false;
6904 WXWORD vk;
6905 switch ( wxk )
6906 {
6907 case WXK_PAGEUP:
6908 extended = true;
6909 wxFALLTHROUGH;
6910 case WXK_NUMPAD_PAGEUP:
6911 vk = VK_PRIOR;
6912 break;
6913
6914 case WXK_PAGEDOWN:
6915 extended = true;
6916 wxFALLTHROUGH;
6917 case WXK_NUMPAD_PAGEDOWN:
6918 vk = VK_NEXT;
6919 break;
6920
6921 case WXK_END:
6922 extended = true;
6923 wxFALLTHROUGH;
6924 case WXK_NUMPAD_END:
6925 vk = VK_END;
6926 break;
6927
6928 case WXK_HOME:
6929 extended = true;
6930 wxFALLTHROUGH;
6931 case WXK_NUMPAD_HOME:
6932 vk = VK_HOME;
6933 break;
6934
6935 case WXK_LEFT:
6936 extended = true;
6937 wxFALLTHROUGH;
6938 case WXK_NUMPAD_LEFT:
6939 vk = VK_LEFT;
6940 break;
6941
6942 case WXK_UP:
6943 extended = true;
6944 wxFALLTHROUGH;
6945 case WXK_NUMPAD_UP:
6946 vk = VK_UP;
6947 break;
6948
6949 case WXK_RIGHT:
6950 extended = true;
6951 wxFALLTHROUGH;
6952 case WXK_NUMPAD_RIGHT:
6953 vk = VK_RIGHT;
6954 break;
6955
6956 case WXK_DOWN:
6957 extended = true;
6958 wxFALLTHROUGH;
6959 case WXK_NUMPAD_DOWN:
6960 vk = VK_DOWN;
6961 break;
6962
6963 case WXK_INSERT:
6964 extended = true;
6965 wxFALLTHROUGH;
6966 case WXK_NUMPAD_INSERT:
6967 vk = VK_INSERT;
6968 break;
6969
6970 case WXK_DELETE:
6971 extended = true;
6972 wxFALLTHROUGH;
6973 case WXK_NUMPAD_DELETE:
6974 vk = VK_DELETE;
6975 break;
6976
6977 default:
6978 // check to see if its one of the OEM key codes.
6979 BYTE vks = LOBYTE(VkKeyScan(wxk));
6980 if ( vks != 0xff )
6981 {
6982 vk = vks;
6983 }
6984 else
6985 {
6986 vk = (WXWORD)wxk;
6987 }
6988 break;
6989 }
6990
6991 if ( isExtended )
6992 *isExtended = extended;
6993
6994 return vk;
6995 }
6996
6997 } // namespace wxMSWKeyboard
6998
6999 // small helper for wxGetKeyState() and wxGetMouseState()
wxIsKeyDown(WXWORD vk)7000 static inline bool wxIsKeyDown(WXWORD vk)
7001 {
7002 if ( vk == VK_LBUTTON || vk == VK_RBUTTON )
7003 {
7004 if ( ::GetSystemMetrics(SM_SWAPBUTTON) )
7005 {
7006 if ( vk == VK_LBUTTON )
7007 vk = VK_RBUTTON;
7008 else // vk == VK_RBUTTON
7009 vk = VK_LBUTTON;
7010 }
7011 }
7012
7013 // the low order bit indicates whether the key was pressed since the last
7014 // call and the high order one indicates whether it is down right now and
7015 // we only want that one
7016 return (GetAsyncKeyState(vk) & (1<<15)) != 0;
7017 }
7018
wxGetKeyState(wxKeyCode key)7019 bool wxGetKeyState(wxKeyCode key)
7020 {
7021 // although this does work under Windows, it is not supported under other
7022 // platforms so don't allow it, you must use wxGetMouseState() instead
7023 wxASSERT_MSG( key != VK_LBUTTON &&
7024 key != VK_RBUTTON &&
7025 key != VK_MBUTTON,
7026 wxT("can't use wxGetKeyState() for mouse buttons") );
7027
7028 const WXWORD vk = wxMSWKeyboard::WXToVK(key);
7029
7030 // if the requested key is a LED key, return true if the led is pressed
7031 if ( key == WXK_NUMLOCK || key == WXK_CAPITAL || key == WXK_SCROLL )
7032 {
7033 // low order bit means LED is highlighted and high order one means the
7034 // key is down; for compatibility with the other ports return true if
7035 // either one is set
7036 return GetKeyState(vk) != 0;
7037
7038 }
7039 else // normal key
7040 {
7041 return wxIsKeyDown(vk);
7042 }
7043 }
7044
7045
wxGetMouseState()7046 wxMouseState wxGetMouseState()
7047 {
7048 wxMouseState ms;
7049 POINT pt;
7050 wxGetCursorPosMSW(&pt);
7051
7052 ms.SetX(pt.x);
7053 ms.SetY(pt.y);
7054 ms.SetLeftDown(wxIsKeyDown(VK_LBUTTON));
7055 ms.SetMiddleDown(wxIsKeyDown(VK_MBUTTON));
7056 ms.SetRightDown(wxIsKeyDown(VK_RBUTTON));
7057 ms.SetAux1Down(wxIsKeyDown(VK_XBUTTON1));
7058 ms.SetAux2Down(wxIsKeyDown(VK_XBUTTON2));
7059
7060 ms.SetControlDown(wxIsCtrlDown ());
7061 ms.SetShiftDown (wxIsShiftDown());
7062 ms.SetAltDown (wxIsAltDown ());
7063 // ms.SetMetaDown();
7064
7065 return ms;
7066 }
7067
7068
wxGetActiveWindow()7069 wxWindow *wxGetActiveWindow()
7070 {
7071 HWND hWnd = GetActiveWindow();
7072 if ( hWnd != 0 )
7073 {
7074 return wxFindWinFromHandle(hWnd);
7075 }
7076 return NULL;
7077 }
7078
wxGetWindowFromHWND(WXHWND hWnd)7079 extern wxWindow *wxGetWindowFromHWND(WXHWND hWnd)
7080 {
7081 HWND hwnd = (HWND)hWnd;
7082
7083 // For a radiobutton, we get the radiobox from GWL_USERDATA (which is set
7084 // by code in msw/radiobox.cpp), for all the others we just search up the
7085 // window hierarchy
7086 wxWindow *win = NULL;
7087 if ( hwnd )
7088 {
7089 win = wxFindWinFromHandle(hwnd);
7090 if ( !win )
7091 {
7092 #if wxUSE_RADIOBOX && !defined(__WXUNIVERSAL__)
7093 // native radiobuttons return DLGC_RADIOBUTTON here and for any
7094 // wxWindow class which overrides WM_GETDLGCODE processing to
7095 // do it as well, win would be already non NULL
7096 if ( ::SendMessage(hwnd, WM_GETDLGCODE, 0, 0) & DLGC_RADIOBUTTON )
7097 {
7098 win = wxRadioBox::GetFromRadioButtonHWND(hwnd);
7099 }
7100 //else: it's a wxRadioButton, not a radiobutton from wxRadioBox
7101 #endif // wxUSE_RADIOBOX
7102
7103 // spin control text buddy window should be mapped to spin ctrl
7104 // itself so try it too
7105 #if wxUSE_SPINCTRL && !defined(__WXUNIVERSAL__)
7106 if ( !win )
7107 {
7108 win = wxSpinCtrl::GetSpinForTextCtrl((WXHWND)hwnd);
7109 }
7110 #endif // wxUSE_SPINCTRL
7111 }
7112 }
7113
7114 while ( hwnd && !win )
7115 {
7116 // this is a really ugly hack needed to avoid mistakenly returning the
7117 // parent frame wxWindow for the find/replace modeless dialog HWND -
7118 // this, in turn, is needed to call IsDialogMessage() from
7119 // wxApp::ProcessMessage() as for this we must return NULL from here
7120 //
7121 // FIXME: this is clearly not the best way to do it but I think we'll
7122 // need to change HWND <-> wxWindow code more heavily than I can
7123 // do it now to fix it
7124 if ( ::GetWindow(hwnd, GW_OWNER) )
7125 {
7126 // it's a dialog box, don't go upwards
7127 break;
7128 }
7129
7130 hwnd = ::GetParent(hwnd);
7131 win = wxFindWinFromHandle(hwnd);
7132 }
7133
7134 return win;
7135 }
7136
7137 // Windows keyboard hook. Allows interception of e.g. F1, ESCAPE
7138 // in active frames and dialogs, regardless of where the focus is.
7139 static HHOOK wxTheKeyboardHook = 0;
7140
7141 LRESULT APIENTRY
wxKeyboardHook(int nCode,WXWPARAM wParam,WXLPARAM lParam)7142 wxKeyboardHook(int nCode, WXWPARAM wParam, WXLPARAM lParam)
7143 {
7144 DWORD hiWord = HIWORD(lParam);
7145 if ( nCode != HC_NOREMOVE && ((hiWord & KF_UP) == 0) )
7146 {
7147 wchar_t uc = 0;
7148 int id = wxMSWKeyboard::VKToWX(wParam, lParam, &uc);
7149
7150 // Don't intercept keyboard entry (notably Escape) if a modal window
7151 // (not managed by wx, e.g. IME one) is currently opened as more often
7152 // than not it needs all the keys for itself.
7153 //
7154 // Also don't catch it if a window currently captures the mouse as
7155 // Escape is normally used to release the mouse capture and if you
7156 // really need to catch all the keys in the window that has mouse
7157 // capture it can be easily done in its own EVT_CHAR handler as it is
7158 // certain to have focus while it has the capture.
7159 if ( !gs_modalEntryWindowCount && !::GetCapture() )
7160 {
7161 if ( id != WXK_NONE
7162 #if wxUSE_UNICODE
7163 || static_cast<int>(uc) != WXK_NONE
7164 #endif // wxUSE_UNICODE
7165 )
7166 {
7167 wxWindow const* win = wxWindow::DoFindFocus();
7168 if ( !win )
7169 {
7170 // Even if the focus got lost somehow, still send the event
7171 // to the top level parent to allow a wxDialog to always
7172 // close on Escape.
7173 win = wxGetActiveWindow();
7174 }
7175
7176 wxKeyEvent event(wxEVT_CHAR_HOOK);
7177 MSWInitAnyKeyEvent(event, wParam, lParam, win);
7178
7179 event.m_keyCode = id;
7180 #if wxUSE_UNICODE
7181 event.m_uniChar = uc;
7182 #endif // wxUSE_UNICODE
7183
7184 wxEvtHandler * const handler = win ? win->GetEventHandler()
7185 : wxTheApp;
7186
7187 // Do not let exceptions propagate out of the hook, it's a
7188 // module boundary.
7189 if ( handler && handler->SafelyProcessEvent(event) )
7190 {
7191 if ( !event.IsNextEventAllowed() )
7192 {
7193 // Stop processing of this event.
7194 return 1;
7195 }
7196 }
7197 }
7198 }
7199 }
7200
7201 return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam);
7202 }
7203
wxSetKeyboardHook(bool doIt)7204 void wxSetKeyboardHook(bool doIt)
7205 {
7206 if ( doIt )
7207 {
7208 wxTheKeyboardHook = ::SetWindowsHookEx
7209 (
7210 WH_KEYBOARD,
7211 wxKeyboardHook,
7212 NULL, // must be NULL for process hook
7213 ::GetCurrentThreadId()
7214 );
7215 if ( !wxTheKeyboardHook )
7216 {
7217 wxLogLastError(wxT("SetWindowsHookEx(wxKeyboardHook)"));
7218 }
7219 }
7220 else // uninstall
7221 {
7222 if ( wxTheKeyboardHook )
7223 ::UnhookWindowsHookEx(wxTheKeyboardHook);
7224 }
7225 }
7226
7227 #if wxDEBUG_LEVEL >= 2
wxGetMessageName(int message)7228 const wxChar *wxGetMessageName(int message)
7229 {
7230 switch ( message )
7231 {
7232 case 0x0000: return wxT("WM_NULL");
7233 case 0x0001: return wxT("WM_CREATE");
7234 case 0x0002: return wxT("WM_DESTROY");
7235 case 0x0003: return wxT("WM_MOVE");
7236 case 0x0005: return wxT("WM_SIZE");
7237 case 0x0006: return wxT("WM_ACTIVATE");
7238 case 0x0007: return wxT("WM_SETFOCUS");
7239 case 0x0008: return wxT("WM_KILLFOCUS");
7240 case 0x000A: return wxT("WM_ENABLE");
7241 case 0x000B: return wxT("WM_SETREDRAW");
7242 case 0x000C: return wxT("WM_SETTEXT");
7243 case 0x000D: return wxT("WM_GETTEXT");
7244 case 0x000E: return wxT("WM_GETTEXTLENGTH");
7245 case 0x000F: return wxT("WM_PAINT");
7246 case 0x0010: return wxT("WM_CLOSE");
7247 case 0x0011: return wxT("WM_QUERYENDSESSION");
7248 case 0x0012: return wxT("WM_QUIT");
7249 case 0x0013: return wxT("WM_QUERYOPEN");
7250 case 0x0014: return wxT("WM_ERASEBKGND");
7251 case 0x0015: return wxT("WM_SYSCOLORCHANGE");
7252 case 0x0016: return wxT("WM_ENDSESSION");
7253 case 0x0017: return wxT("WM_SYSTEMERROR");
7254 case 0x0018: return wxT("WM_SHOWWINDOW");
7255 case 0x0019: return wxT("WM_CTLCOLOR");
7256 case 0x001A: return wxT("WM_WININICHANGE");
7257 case 0x001B: return wxT("WM_DEVMODECHANGE");
7258 case 0x001C: return wxT("WM_ACTIVATEAPP");
7259 case 0x001D: return wxT("WM_FONTCHANGE");
7260 case 0x001E: return wxT("WM_TIMECHANGE");
7261 case 0x001F: return wxT("WM_CANCELMODE");
7262 case 0x0020: return wxT("WM_SETCURSOR");
7263 case 0x0021: return wxT("WM_MOUSEACTIVATE");
7264 case 0x0022: return wxT("WM_CHILDACTIVATE");
7265 case 0x0023: return wxT("WM_QUEUESYNC");
7266 case 0x0024: return wxT("WM_GETMINMAXINFO");
7267 case 0x0026: return wxT("WM_PAINTICON");
7268 case 0x0027: return wxT("WM_ICONERASEBKGND");
7269 case 0x0028: return wxT("WM_NEXTDLGCTL");
7270 case 0x002A: return wxT("WM_SPOOLERSTATUS");
7271 case 0x002B: return wxT("WM_DRAWITEM");
7272 case 0x002C: return wxT("WM_MEASUREITEM");
7273 case 0x002D: return wxT("WM_DELETEITEM");
7274 case 0x002E: return wxT("WM_VKEYTOITEM");
7275 case 0x002F: return wxT("WM_CHARTOITEM");
7276 case 0x0030: return wxT("WM_SETFONT");
7277 case 0x0031: return wxT("WM_GETFONT");
7278 case 0x0037: return wxT("WM_QUERYDRAGICON");
7279 case 0x0039: return wxT("WM_COMPAREITEM");
7280 case 0x0041: return wxT("WM_COMPACTING");
7281 case 0x0044: return wxT("WM_COMMNOTIFY");
7282 case 0x0046: return wxT("WM_WINDOWPOSCHANGING");
7283 case 0x0047: return wxT("WM_WINDOWPOSCHANGED");
7284 case 0x0048: return wxT("WM_POWER");
7285
7286 case 0x004A: return wxT("WM_COPYDATA");
7287 case 0x004B: return wxT("WM_CANCELJOURNAL");
7288 case 0x004E: return wxT("WM_NOTIFY");
7289 case 0x0050: return wxT("WM_INPUTLANGCHANGEREQUEST");
7290 case 0x0051: return wxT("WM_INPUTLANGCHANGE");
7291 case 0x0052: return wxT("WM_TCARD");
7292 case 0x0053: return wxT("WM_HELP");
7293 case 0x0054: return wxT("WM_USERCHANGED");
7294 case 0x0055: return wxT("WM_NOTIFYFORMAT");
7295 case 0x007B: return wxT("WM_CONTEXTMENU");
7296 case 0x007C: return wxT("WM_STYLECHANGING");
7297 case 0x007D: return wxT("WM_STYLECHANGED");
7298 case 0x007E: return wxT("WM_DISPLAYCHANGE");
7299 case 0x007F: return wxT("WM_GETICON");
7300 case 0x0080: return wxT("WM_SETICON");
7301
7302 case 0x0081: return wxT("WM_NCCREATE");
7303 case 0x0082: return wxT("WM_NCDESTROY");
7304 case 0x0083: return wxT("WM_NCCALCSIZE");
7305 case 0x0084: return wxT("WM_NCHITTEST");
7306 case 0x0085: return wxT("WM_NCPAINT");
7307 case 0x0086: return wxT("WM_NCACTIVATE");
7308 case 0x0087: return wxT("WM_GETDLGCODE");
7309 case 0x00A0: return wxT("WM_NCMOUSEMOVE");
7310 case 0x00A1: return wxT("WM_NCLBUTTONDOWN");
7311 case 0x00A2: return wxT("WM_NCLBUTTONUP");
7312 case 0x00A3: return wxT("WM_NCLBUTTONDBLCLK");
7313 case 0x00A4: return wxT("WM_NCRBUTTONDOWN");
7314 case 0x00A5: return wxT("WM_NCRBUTTONUP");
7315 case 0x00A6: return wxT("WM_NCRBUTTONDBLCLK");
7316 case 0x00A7: return wxT("WM_NCMBUTTONDOWN");
7317 case 0x00A8: return wxT("WM_NCMBUTTONUP");
7318 case 0x00A9: return wxT("WM_NCMBUTTONDBLCLK");
7319
7320 case 0x00B0: return wxT("EM_GETSEL");
7321 case 0x00B1: return wxT("EM_SETSEL");
7322 case 0x00B2: return wxT("EM_GETRECT");
7323 case 0x00B3: return wxT("EM_SETRECT");
7324 case 0x00B4: return wxT("EM_SETRECTNP");
7325 case 0x00B5: return wxT("EM_SCROLL");
7326 case 0x00B6: return wxT("EM_LINESCROLL");
7327 case 0x00B7: return wxT("EM_SCROLLCARET");
7328 case 0x00B8: return wxT("EM_GETMODIFY");
7329 case 0x00B9: return wxT("EM_SETMODIFY");
7330 case 0x00BA: return wxT("EM_GETLINECOUNT");
7331 case 0x00BB: return wxT("EM_LINEINDEX");
7332 case 0x00BC: return wxT("EM_SETHANDLE");
7333 case 0x00BD: return wxT("EM_GETHANDLE");
7334 case 0x00BE: return wxT("EM_GETTHUMB");
7335 case 0x00C1: return wxT("EM_LINELENGTH");
7336 case 0x00C2: return wxT("EM_REPLACESEL");
7337 case 0x00C4: return wxT("EM_GETLINE");
7338 case 0x00C5: return wxT("EM_LIMITTEXT/EM_SETLIMITTEXT"); /* ;win40 Name change */
7339 case 0x00C6: return wxT("EM_CANUNDO");
7340 case 0x00C7: return wxT("EM_UNDO");
7341 case 0x00C8: return wxT("EM_FMTLINES");
7342 case 0x00C9: return wxT("EM_LINEFROMCHAR");
7343 case 0x00CB: return wxT("EM_SETTABSTOPS");
7344 case 0x00CC: return wxT("EM_SETPASSWORDCHAR");
7345 case 0x00CD: return wxT("EM_EMPTYUNDOBUFFER");
7346 case 0x00CE: return wxT("EM_GETFIRSTVISIBLELINE");
7347 case 0x00CF: return wxT("EM_SETREADONLY");
7348 case 0x00D0: return wxT("EM_SETWORDBREAKPROC");
7349 case 0x00D1: return wxT("EM_GETWORDBREAKPROC");
7350 case 0x00D2: return wxT("EM_GETPASSWORDCHAR");
7351 case 0x00D3: return wxT("EM_SETMARGINS");
7352 case 0x00D4: return wxT("EM_GETMARGINS");
7353 case 0x00D5: return wxT("EM_GETLIMITTEXT");
7354 case 0x00D6: return wxT("EM_POSFROMCHAR");
7355 case 0x00D7: return wxT("EM_CHARFROMPOS");
7356 case 0x00D8: return wxT("EM_SETIMESTATUS");
7357 case 0x00D9: return wxT("EM_GETIMESTATUS");
7358
7359 case 0x0100: return wxT("WM_KEYDOWN");
7360 case 0x0101: return wxT("WM_KEYUP");
7361 case 0x0102: return wxT("WM_CHAR");
7362 case 0x0103: return wxT("WM_DEADCHAR");
7363 case 0x0104: return wxT("WM_SYSKEYDOWN");
7364 case 0x0105: return wxT("WM_SYSKEYUP");
7365 case 0x0106: return wxT("WM_SYSCHAR");
7366 case 0x0107: return wxT("WM_SYSDEADCHAR");
7367 case 0x0108: return wxT("WM_KEYLAST");
7368
7369 case 0x010D: return wxT("WM_IME_STARTCOMPOSITION");
7370 case 0x010E: return wxT("WM_IME_ENDCOMPOSITION");
7371 case 0x010F: return wxT("WM_IME_COMPOSITION");
7372
7373 case 0x0110: return wxT("WM_INITDIALOG");
7374 case 0x0111: return wxT("WM_COMMAND");
7375 case 0x0112: return wxT("WM_SYSCOMMAND");
7376 case 0x0113: return wxT("WM_TIMER");
7377 case 0x0114: return wxT("WM_HSCROLL");
7378 case 0x0115: return wxT("WM_VSCROLL");
7379 case 0x0116: return wxT("WM_INITMENU");
7380 case 0x0117: return wxT("WM_INITMENUPOPUP");
7381 case 0x011F: return wxT("WM_MENUSELECT");
7382 case 0x0120: return wxT("WM_MENUCHAR");
7383 case 0x0121: return wxT("WM_ENTERIDLE");
7384
7385 case 0x0127: return wxT("WM_CHANGEUISTATE");
7386 case 0x0128: return wxT("WM_UPDATEUISTATE");
7387 case 0x0129: return wxT("WM_QUERYUISTATE");
7388
7389 case 0x0132: return wxT("WM_CTLCOLORMSGBOX");
7390 case 0x0133: return wxT("WM_CTLCOLOREDIT");
7391 case 0x0134: return wxT("WM_CTLCOLORLISTBOX");
7392 case 0x0135: return wxT("WM_CTLCOLORBTN");
7393 case 0x0136: return wxT("WM_CTLCOLORDLG");
7394 case 0x0137: return wxT("WM_CTLCOLORSCROLLBAR");
7395 case 0x0138: return wxT("WM_CTLCOLORSTATIC");
7396 case 0x01E1: return wxT("MN_GETHMENU");
7397
7398 case 0x0200: return wxT("WM_MOUSEMOVE");
7399 case 0x0201: return wxT("WM_LBUTTONDOWN");
7400 case 0x0202: return wxT("WM_LBUTTONUP");
7401 case 0x0203: return wxT("WM_LBUTTONDBLCLK");
7402 case 0x0204: return wxT("WM_RBUTTONDOWN");
7403 case 0x0205: return wxT("WM_RBUTTONUP");
7404 case 0x0206: return wxT("WM_RBUTTONDBLCLK");
7405 case 0x0207: return wxT("WM_MBUTTONDOWN");
7406 case 0x0208: return wxT("WM_MBUTTONUP");
7407 case 0x0209: return wxT("WM_MBUTTONDBLCLK");
7408 case 0x020A: return wxT("WM_MOUSEWHEEL");
7409 case 0x020B: return wxT("WM_XBUTTONDOWN");
7410 case 0x020C: return wxT("WM_XBUTTONUP");
7411 case 0x020D: return wxT("WM_XBUTTONDBLCLK");
7412 case 0x0210: return wxT("WM_PARENTNOTIFY");
7413 case 0x0211: return wxT("WM_ENTERMENULOOP");
7414 case 0x0212: return wxT("WM_EXITMENULOOP");
7415
7416 case 0x0213: return wxT("WM_NEXTMENU");
7417 case 0x0214: return wxT("WM_SIZING");
7418 case 0x0215: return wxT("WM_CAPTURECHANGED");
7419 case 0x0216: return wxT("WM_MOVING");
7420 case 0x0218: return wxT("WM_POWERBROADCAST");
7421 case 0x0219: return wxT("WM_DEVICECHANGE");
7422
7423 case 0x0220: return wxT("WM_MDICREATE");
7424 case 0x0221: return wxT("WM_MDIDESTROY");
7425 case 0x0222: return wxT("WM_MDIACTIVATE");
7426 case 0x0223: return wxT("WM_MDIRESTORE");
7427 case 0x0224: return wxT("WM_MDINEXT");
7428 case 0x0225: return wxT("WM_MDIMAXIMIZE");
7429 case 0x0226: return wxT("WM_MDITILE");
7430 case 0x0227: return wxT("WM_MDICASCADE");
7431 case 0x0228: return wxT("WM_MDIICONARRANGE");
7432 case 0x0229: return wxT("WM_MDIGETACTIVE");
7433 case 0x0230: return wxT("WM_MDISETMENU");
7434 case 0x0233: return wxT("WM_DROPFILES");
7435
7436 case 0x0281: return wxT("WM_IME_SETCONTEXT");
7437 case 0x0282: return wxT("WM_IME_NOTIFY");
7438 case 0x0283: return wxT("WM_IME_CONTROL");
7439 case 0x0284: return wxT("WM_IME_COMPOSITIONFULL");
7440 case 0x0285: return wxT("WM_IME_SELECT");
7441 case 0x0286: return wxT("WM_IME_CHAR");
7442 case 0x0290: return wxT("WM_IME_KEYDOWN");
7443 case 0x0291: return wxT("WM_IME_KEYUP");
7444
7445 case 0x02A0: return wxT("WM_NCMOUSEHOVER");
7446 case 0x02A1: return wxT("WM_MOUSEHOVER");
7447 case 0x02A2: return wxT("WM_NCMOUSELEAVE");
7448 case 0x02A3: return wxT("WM_MOUSELEAVE");
7449
7450 case 0x0300: return wxT("WM_CUT");
7451 case 0x0301: return wxT("WM_COPY");
7452 case 0x0302: return wxT("WM_PASTE");
7453 case 0x0303: return wxT("WM_CLEAR");
7454 case 0x0304: return wxT("WM_UNDO");
7455 case 0x0305: return wxT("WM_RENDERFORMAT");
7456 case 0x0306: return wxT("WM_RENDERALLFORMATS");
7457 case 0x0307: return wxT("WM_DESTROYCLIPBOARD");
7458 case 0x0308: return wxT("WM_DRAWCLIPBOARD");
7459 case 0x0309: return wxT("WM_PAINTCLIPBOARD");
7460 case 0x030A: return wxT("WM_VSCROLLCLIPBOARD");
7461 case 0x030B: return wxT("WM_SIZECLIPBOARD");
7462 case 0x030C: return wxT("WM_ASKCBFORMATNAME");
7463 case 0x030D: return wxT("WM_CHANGECBCHAIN");
7464 case 0x030E: return wxT("WM_HSCROLLCLIPBOARD");
7465 case 0x030F: return wxT("WM_QUERYNEWPALETTE");
7466 case 0x0310: return wxT("WM_PALETTEISCHANGING");
7467 case 0x0311: return wxT("WM_PALETTECHANGED");
7468 case 0x0312: return wxT("WM_HOTKEY");
7469
7470 case 0x0317: return wxT("WM_PRINT");
7471 case 0x0318: return wxT("WM_PRINTCLIENT");
7472
7473 // common controls messages - although they're not strictly speaking
7474 // standard, it's nice to decode them nevertheless
7475
7476 // listview
7477 case 0x1000 + 0: return wxT("LVM_GETBKCOLOR");
7478 case 0x1000 + 1: return wxT("LVM_SETBKCOLOR");
7479 case 0x1000 + 2: return wxT("LVM_GETIMAGELIST");
7480 case 0x1000 + 3: return wxT("LVM_SETIMAGELIST");
7481 case 0x1000 + 4: return wxT("LVM_GETITEMCOUNT");
7482 case 0x1000 + 5: return wxT("LVM_GETITEMA");
7483 case 0x1000 + 75: return wxT("LVM_GETITEMW");
7484 case 0x1000 + 6: return wxT("LVM_SETITEMA");
7485 case 0x1000 + 76: return wxT("LVM_SETITEMW");
7486 case 0x1000 + 7: return wxT("LVM_INSERTITEMA");
7487 case 0x1000 + 77: return wxT("LVM_INSERTITEMW");
7488 case 0x1000 + 8: return wxT("LVM_DELETEITEM");
7489 case 0x1000 + 9: return wxT("LVM_DELETEALLITEMS");
7490 case 0x1000 + 10: return wxT("LVM_GETCALLBACKMASK");
7491 case 0x1000 + 11: return wxT("LVM_SETCALLBACKMASK");
7492 case 0x1000 + 12: return wxT("LVM_GETNEXTITEM");
7493 case 0x1000 + 13: return wxT("LVM_FINDITEMA");
7494 case 0x1000 + 83: return wxT("LVM_FINDITEMW");
7495 case 0x1000 + 14: return wxT("LVM_GETITEMRECT");
7496 case 0x1000 + 15: return wxT("LVM_SETITEMPOSITION");
7497 case 0x1000 + 16: return wxT("LVM_GETITEMPOSITION");
7498 case 0x1000 + 17: return wxT("LVM_GETSTRINGWIDTHA");
7499 case 0x1000 + 87: return wxT("LVM_GETSTRINGWIDTHW");
7500 case 0x1000 + 18: return wxT("LVM_HITTEST");
7501 case 0x1000 + 19: return wxT("LVM_ENSUREVISIBLE");
7502 case 0x1000 + 20: return wxT("LVM_SCROLL");
7503 case 0x1000 + 21: return wxT("LVM_REDRAWITEMS");
7504 case 0x1000 + 22: return wxT("LVM_ARRANGE");
7505 case 0x1000 + 23: return wxT("LVM_EDITLABELA");
7506 case 0x1000 + 118: return wxT("LVM_EDITLABELW");
7507 case 0x1000 + 24: return wxT("LVM_GETEDITCONTROL");
7508 case 0x1000 + 25: return wxT("LVM_GETCOLUMNA");
7509 case 0x1000 + 95: return wxT("LVM_GETCOLUMNW");
7510 case 0x1000 + 26: return wxT("LVM_SETCOLUMNA");
7511 case 0x1000 + 96: return wxT("LVM_SETCOLUMNW");
7512 case 0x1000 + 27: return wxT("LVM_INSERTCOLUMNA");
7513 case 0x1000 + 97: return wxT("LVM_INSERTCOLUMNW");
7514 case 0x1000 + 28: return wxT("LVM_DELETECOLUMN");
7515 case 0x1000 + 29: return wxT("LVM_GETCOLUMNWIDTH");
7516 case 0x1000 + 30: return wxT("LVM_SETCOLUMNWIDTH");
7517 case 0x1000 + 31: return wxT("LVM_GETHEADER");
7518 case 0x1000 + 33: return wxT("LVM_CREATEDRAGIMAGE");
7519 case 0x1000 + 34: return wxT("LVM_GETVIEWRECT");
7520 case 0x1000 + 35: return wxT("LVM_GETTEXTCOLOR");
7521 case 0x1000 + 36: return wxT("LVM_SETTEXTCOLOR");
7522 case 0x1000 + 37: return wxT("LVM_GETTEXTBKCOLOR");
7523 case 0x1000 + 38: return wxT("LVM_SETTEXTBKCOLOR");
7524 case 0x1000 + 39: return wxT("LVM_GETTOPINDEX");
7525 case 0x1000 + 40: return wxT("LVM_GETCOUNTPERPAGE");
7526 case 0x1000 + 41: return wxT("LVM_GETORIGIN");
7527 case 0x1000 + 42: return wxT("LVM_UPDATE");
7528 case 0x1000 + 43: return wxT("LVM_SETITEMSTATE");
7529 case 0x1000 + 44: return wxT("LVM_GETITEMSTATE");
7530 case 0x1000 + 45: return wxT("LVM_GETITEMTEXTA");
7531 case 0x1000 + 115: return wxT("LVM_GETITEMTEXTW");
7532 case 0x1000 + 46: return wxT("LVM_SETITEMTEXTA");
7533 case 0x1000 + 116: return wxT("LVM_SETITEMTEXTW");
7534 case 0x1000 + 47: return wxT("LVM_SETITEMCOUNT");
7535 case 0x1000 + 48: return wxT("LVM_SORTITEMS");
7536 case 0x1000 + 49: return wxT("LVM_SETITEMPOSITION32");
7537 case 0x1000 + 50: return wxT("LVM_GETSELECTEDCOUNT");
7538 case 0x1000 + 51: return wxT("LVM_GETITEMSPACING");
7539 case 0x1000 + 52: return wxT("LVM_GETISEARCHSTRINGA");
7540 case 0x1000 + 117: return wxT("LVM_GETISEARCHSTRINGW");
7541 case 0x1000 + 53: return wxT("LVM_SETICONSPACING");
7542 case 0x1000 + 54: return wxT("LVM_SETEXTENDEDLISTVIEWSTYLE");
7543 case 0x1000 + 55: return wxT("LVM_GETEXTENDEDLISTVIEWSTYLE");
7544 case 0x1000 + 56: return wxT("LVM_GETSUBITEMRECT");
7545 case 0x1000 + 57: return wxT("LVM_SUBITEMHITTEST");
7546 case 0x1000 + 58: return wxT("LVM_SETCOLUMNORDERARRAY");
7547 case 0x1000 + 59: return wxT("LVM_GETCOLUMNORDERARRAY");
7548 case 0x1000 + 60: return wxT("LVM_SETHOTITEM");
7549 case 0x1000 + 61: return wxT("LVM_GETHOTITEM");
7550 case 0x1000 + 62: return wxT("LVM_SETHOTCURSOR");
7551 case 0x1000 + 63: return wxT("LVM_GETHOTCURSOR");
7552 case 0x1000 + 64: return wxT("LVM_APPROXIMATEVIEWRECT");
7553 case 0x1000 + 65: return wxT("LVM_SETWORKAREA");
7554
7555 // tree view
7556 case 0x1100 + 0: return wxT("TVM_INSERTITEMA");
7557 case 0x1100 + 50: return wxT("TVM_INSERTITEMW");
7558 case 0x1100 + 1: return wxT("TVM_DELETEITEM");
7559 case 0x1100 + 2: return wxT("TVM_EXPAND");
7560 case 0x1100 + 4: return wxT("TVM_GETITEMRECT");
7561 case 0x1100 + 5: return wxT("TVM_GETCOUNT");
7562 case 0x1100 + 6: return wxT("TVM_GETINDENT");
7563 case 0x1100 + 7: return wxT("TVM_SETINDENT");
7564 case 0x1100 + 8: return wxT("TVM_GETIMAGELIST");
7565 case 0x1100 + 9: return wxT("TVM_SETIMAGELIST");
7566 case 0x1100 + 10: return wxT("TVM_GETNEXTITEM");
7567 case 0x1100 + 11: return wxT("TVM_SELECTITEM");
7568 case 0x1100 + 12: return wxT("TVM_GETITEMA");
7569 case 0x1100 + 62: return wxT("TVM_GETITEMW");
7570 case 0x1100 + 13: return wxT("TVM_SETITEMA");
7571 case 0x1100 + 63: return wxT("TVM_SETITEMW");
7572 case 0x1100 + 14: return wxT("TVM_EDITLABELA");
7573 case 0x1100 + 65: return wxT("TVM_EDITLABELW");
7574 case 0x1100 + 15: return wxT("TVM_GETEDITCONTROL");
7575 case 0x1100 + 16: return wxT("TVM_GETVISIBLECOUNT");
7576 case 0x1100 + 17: return wxT("TVM_HITTEST");
7577 case 0x1100 + 18: return wxT("TVM_CREATEDRAGIMAGE");
7578 case 0x1100 + 19: return wxT("TVM_SORTCHILDREN");
7579 case 0x1100 + 20: return wxT("TVM_ENSUREVISIBLE");
7580 case 0x1100 + 21: return wxT("TVM_SORTCHILDRENCB");
7581 case 0x1100 + 22: return wxT("TVM_ENDEDITLABELNOW");
7582 case 0x1100 + 23: return wxT("TVM_GETISEARCHSTRINGA");
7583 case 0x1100 + 64: return wxT("TVM_GETISEARCHSTRINGW");
7584 case 0x1100 + 24: return wxT("TVM_SETTOOLTIPS");
7585 case 0x1100 + 25: return wxT("TVM_GETTOOLTIPS");
7586
7587 // header
7588 case 0x1200 + 0: return wxT("HDM_GETITEMCOUNT");
7589 case 0x1200 + 1: return wxT("HDM_INSERTITEMA");
7590 case 0x1200 + 10: return wxT("HDM_INSERTITEMW");
7591 case 0x1200 + 2: return wxT("HDM_DELETEITEM");
7592 case 0x1200 + 3: return wxT("HDM_GETITEMA");
7593 case 0x1200 + 11: return wxT("HDM_GETITEMW");
7594 case 0x1200 + 4: return wxT("HDM_SETITEMA");
7595 case 0x1200 + 12: return wxT("HDM_SETITEMW");
7596 case 0x1200 + 5: return wxT("HDM_LAYOUT");
7597 case 0x1200 + 6: return wxT("HDM_HITTEST");
7598 case 0x1200 + 7: return wxT("HDM_GETITEMRECT");
7599 case 0x1200 + 8: return wxT("HDM_SETIMAGELIST");
7600 case 0x1200 + 9: return wxT("HDM_GETIMAGELIST");
7601 case 0x1200 + 15: return wxT("HDM_ORDERTOINDEX");
7602 case 0x1200 + 16: return wxT("HDM_CREATEDRAGIMAGE");
7603 case 0x1200 + 17: return wxT("HDM_GETORDERARRAY");
7604 case 0x1200 + 18: return wxT("HDM_SETORDERARRAY");
7605 case 0x1200 + 19: return wxT("HDM_SETHOTDIVIDER");
7606
7607 // tab control
7608 case 0x1300 + 2: return wxT("TCM_GETIMAGELIST");
7609 case 0x1300 + 3: return wxT("TCM_SETIMAGELIST");
7610 case 0x1300 + 4: return wxT("TCM_GETITEMCOUNT");
7611 case 0x1300 + 5: return wxT("TCM_GETITEMA");
7612 case 0x1300 + 60: return wxT("TCM_GETITEMW");
7613 case 0x1300 + 6: return wxT("TCM_SETITEMA");
7614 case 0x1300 + 61: return wxT("TCM_SETITEMW");
7615 case 0x1300 + 7: return wxT("TCM_INSERTITEMA");
7616 case 0x1300 + 62: return wxT("TCM_INSERTITEMW");
7617 case 0x1300 + 8: return wxT("TCM_DELETEITEM");
7618 case 0x1300 + 9: return wxT("TCM_DELETEALLITEMS");
7619 case 0x1300 + 10: return wxT("TCM_GETITEMRECT");
7620 case 0x1300 + 11: return wxT("TCM_GETCURSEL");
7621 case 0x1300 + 12: return wxT("TCM_SETCURSEL");
7622 case 0x1300 + 13: return wxT("TCM_HITTEST");
7623 case 0x1300 + 14: return wxT("TCM_SETITEMEXTRA");
7624 case 0x1300 + 40: return wxT("TCM_ADJUSTRECT");
7625 case 0x1300 + 41: return wxT("TCM_SETITEMSIZE");
7626 case 0x1300 + 42: return wxT("TCM_REMOVEIMAGE");
7627 case 0x1300 + 43: return wxT("TCM_SETPADDING");
7628 case 0x1300 + 44: return wxT("TCM_GETROWCOUNT");
7629 case 0x1300 + 45: return wxT("TCM_GETTOOLTIPS");
7630 case 0x1300 + 46: return wxT("TCM_SETTOOLTIPS");
7631 case 0x1300 + 47: return wxT("TCM_GETCURFOCUS");
7632 case 0x1300 + 48: return wxT("TCM_SETCURFOCUS");
7633 case 0x1300 + 49: return wxT("TCM_SETMINTABWIDTH");
7634 case 0x1300 + 50: return wxT("TCM_DESELECTALL");
7635
7636 // toolbar
7637 case WM_USER+1: return wxT("TB_ENABLEBUTTON");
7638 case WM_USER+2: return wxT("TB_CHECKBUTTON");
7639 case WM_USER+3: return wxT("TB_PRESSBUTTON");
7640 case WM_USER+4: return wxT("TB_HIDEBUTTON");
7641 case WM_USER+5: return wxT("TB_INDETERMINATE");
7642 case WM_USER+9: return wxT("TB_ISBUTTONENABLED");
7643 case WM_USER+10: return wxT("TB_ISBUTTONCHECKED");
7644 case WM_USER+11: return wxT("TB_ISBUTTONPRESSED");
7645 case WM_USER+12: return wxT("TB_ISBUTTONHIDDEN");
7646 case WM_USER+13: return wxT("TB_ISBUTTONINDETERMINATE");
7647 case WM_USER+17: return wxT("TB_SETSTATE");
7648 case WM_USER+18: return wxT("TB_GETSTATE");
7649 case WM_USER+19: return wxT("TB_ADDBITMAP");
7650 case WM_USER+20: return wxT("TB_ADDBUTTONS");
7651 case WM_USER+21: return wxT("TB_INSERTBUTTON");
7652 case WM_USER+22: return wxT("TB_DELETEBUTTON");
7653 case WM_USER+23: return wxT("TB_GETBUTTON");
7654 case WM_USER+24: return wxT("TB_BUTTONCOUNT");
7655 case WM_USER+25: return wxT("TB_COMMANDTOINDEX");
7656 case WM_USER+26: return wxT("TB_SAVERESTOREA");
7657 case WM_USER+76: return wxT("TB_SAVERESTOREW");
7658 case WM_USER+27: return wxT("TB_CUSTOMIZE");
7659 case WM_USER+28: return wxT("TB_ADDSTRINGA");
7660 case WM_USER+77: return wxT("TB_ADDSTRINGW");
7661 case WM_USER+29: return wxT("TB_GETITEMRECT");
7662 case WM_USER+30: return wxT("TB_BUTTONSTRUCTSIZE");
7663 case WM_USER+31: return wxT("TB_SETBUTTONSIZE");
7664 case WM_USER+32: return wxT("TB_SETBITMAPSIZE");
7665 case WM_USER+33: return wxT("TB_AUTOSIZE");
7666 case WM_USER+35: return wxT("TB_GETTOOLTIPS");
7667 case WM_USER+36: return wxT("TB_SETTOOLTIPS");
7668 case WM_USER+37: return wxT("TB_SETPARENT");
7669 case WM_USER+39: return wxT("TB_SETROWS");
7670 case WM_USER+40: return wxT("TB_GETROWS");
7671 case WM_USER+42: return wxT("TB_SETCMDID");
7672 case WM_USER+43: return wxT("TB_CHANGEBITMAP");
7673 case WM_USER+44: return wxT("TB_GETBITMAP");
7674 case WM_USER+45: return wxT("TB_GETBUTTONTEXTA");
7675 case WM_USER+75: return wxT("TB_GETBUTTONTEXTW");
7676 case WM_USER+46: return wxT("TB_REPLACEBITMAP");
7677 case WM_USER+47: return wxT("TB_SETINDENT");
7678 case WM_USER+48: return wxT("TB_SETIMAGELIST");
7679 case WM_USER+49: return wxT("TB_GETIMAGELIST");
7680 case WM_USER+50: return wxT("TB_LOADIMAGES");
7681 case WM_USER+51: return wxT("TB_GETRECT");
7682 case WM_USER+52: return wxT("TB_SETHOTIMAGELIST");
7683 case WM_USER+53: return wxT("TB_GETHOTIMAGELIST");
7684 case WM_USER+54: return wxT("TB_SETDISABLEDIMAGELIST");
7685 case WM_USER+55: return wxT("TB_GETDISABLEDIMAGELIST");
7686 case WM_USER+56: return wxT("TB_SETSTYLE");
7687 case WM_USER+57: return wxT("TB_GETSTYLE");
7688 case WM_USER+58: return wxT("TB_GETBUTTONSIZE");
7689 case WM_USER+59: return wxT("TB_SETBUTTONWIDTH");
7690 case WM_USER+60: return wxT("TB_SETMAXTEXTROWS");
7691 case WM_USER+61: return wxT("TB_GETTEXTROWS");
7692 case WM_USER+41: return wxT("TB_GETBITMAPFLAGS");
7693
7694 default:
7695 static wxString s_szBuf;
7696 s_szBuf.Printf(wxT("<unknown message = %d>"), message);
7697 return s_szBuf.c_str();
7698 }
7699 }
7700 #endif // wxDEBUG_LEVEL >= 2
7701
wxGetTextMetrics(const wxWindowMSW * win)7702 static TEXTMETRIC wxGetTextMetrics(const wxWindowMSW *win)
7703 {
7704 // prepare the DC
7705 TEXTMETRIC tm;
7706 HWND hwnd = GetHwndOf(win);
7707 HDC hdc = ::GetDC(hwnd);
7708
7709 #if !wxDIALOG_UNIT_COMPATIBILITY
7710 // and select the current font into it
7711
7712 // Note that it's important to extend the lifetime of the possibly
7713 // temporary wxFont returned by GetFont() to ensure that its HFONT remains
7714 // valid.
7715 const wxFont& f(win->GetFont());
7716 HFONT hfont = GetHfontOf(f);
7717 if ( hfont )
7718 {
7719 hfont = (HFONT)::SelectObject(hdc, hfont);
7720 }
7721 #endif
7722
7723 // finally retrieve the text metrics from it
7724 GetTextMetrics(hdc, &tm);
7725
7726 #if !wxDIALOG_UNIT_COMPATIBILITY
7727 // and clean up
7728 if ( hfont )
7729 {
7730 (void)::SelectObject(hdc, hfont);
7731 }
7732 #endif
7733
7734 ::ReleaseDC(hwnd, hdc);
7735
7736 return tm;
7737 }
7738
7739 // Find the wxWindow at the current mouse position, returning the mouse
7740 // position.
wxFindWindowAtPointer(wxPoint & pt)7741 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
7742 {
7743 pt = wxGetMousePosition();
7744 return wxFindWindowAtPoint(pt);
7745 }
7746
wxFindWindowAtPoint(const wxPoint & pt)7747 wxWindow* wxFindWindowAtPoint(const wxPoint& pt)
7748 {
7749 POINT pt2;
7750 pt2.x = pt.x;
7751 pt2.y = pt.y;
7752
7753 HWND hWnd = ::WindowFromPoint(pt2);
7754 if ( hWnd )
7755 {
7756 // WindowFromPoint() ignores the disabled children but we're supposed
7757 // to take them into account, so check if we have a child at this
7758 // coordinate using ChildWindowFromPointEx().
7759 for ( ;; )
7760 {
7761 pt2.x = pt.x;
7762 pt2.y = pt.y;
7763 ::ScreenToClient(hWnd, &pt2);
7764 HWND child = ::ChildWindowFromPointEx(hWnd, pt2, CWP_SKIPINVISIBLE);
7765 if ( child == hWnd || !child )
7766 break;
7767
7768 // ChildWindowFromPointEx() only examines the immediate children
7769 // but we want to get the deepest (top in Z-order) one, so continue
7770 // iterating for as long as it finds anything.
7771 hWnd = child;
7772 }
7773 }
7774
7775 return wxGetWindowFromHWND((WXHWND)hWnd);
7776 }
7777
7778 // Get the current mouse position.
wxGetMousePosition()7779 wxPoint wxGetMousePosition()
7780 {
7781 POINT pt;
7782 wxGetCursorPosMSW(&pt);
7783
7784 return wxPoint(pt.x, pt.y);
7785 }
7786
7787 #if wxUSE_HOTKEY
7788
RegisterHotKey(int hotkeyId,int modifiers,int keycode)7789 bool wxWindowMSW::RegisterHotKey(int hotkeyId, int modifiers, int keycode)
7790 {
7791 UINT win_modifiers=0;
7792 if ( modifiers & wxMOD_ALT )
7793 win_modifiers |= MOD_ALT;
7794 if ( modifiers & wxMOD_SHIFT )
7795 win_modifiers |= MOD_SHIFT;
7796 if ( modifiers & wxMOD_CONTROL )
7797 win_modifiers |= MOD_CONTROL;
7798 if ( modifiers & wxMOD_WIN )
7799 win_modifiers |= MOD_WIN;
7800
7801 // Special compatibility hack: the initial version of this function didn't
7802 // use wxMSWKeyboard::WXToVK() at all, which was wrong as the user code is
7803 // expected to use WXK_XXX constants and not VK_XXX ones, but as people had
7804 // no choice but to use the latter with the previous version of wxWidgets,
7805 // now we have to continue accepting VK_XXX here too. So we assume that the
7806 // argument is a VK constant and not a WXK one if it looks like this should
7807 // be the case based on its value. It helps that all of WXK constants
7808 // before WXK_START have the same value as the corresponding VK constants,
7809 // with the only exception of WXK_DELETE which is equal to VK_F16 -- and we
7810 // consider that the latter is unlikely to be used.
7811 if ( keycode >= WXK_START || keycode == WXK_DELETE )
7812 keycode = wxMSWKeyboard::WXToVK(keycode);
7813 //else: leave it unchanged because it looks like it's a VK constant (which
7814 // includes ASCII digits and upper case letters)
7815
7816 if ( !::RegisterHotKey(GetHwnd(), hotkeyId, win_modifiers, keycode) )
7817 {
7818 wxLogLastError(wxT("RegisterHotKey"));
7819
7820 return false;
7821 }
7822
7823 return true;
7824 }
7825
UnregisterHotKey(int hotkeyId)7826 bool wxWindowMSW::UnregisterHotKey(int hotkeyId)
7827 {
7828 if ( !::UnregisterHotKey(GetHwnd(), hotkeyId) )
7829 {
7830 wxLogLastError(wxT("UnregisterHotKey"));
7831
7832 return false;
7833 }
7834
7835 return true;
7836 }
7837
HandleHotKey(WXWPARAM wParam,WXLPARAM lParam)7838 bool wxWindowMSW::HandleHotKey(WXWPARAM wParam, WXLPARAM lParam)
7839 {
7840 int win_modifiers = LOWORD(lParam);
7841
7842 wxKeyEvent event(CreateKeyEvent(wxEVT_HOTKEY, HIWORD(lParam)));
7843 event.SetId(wParam);
7844 event.m_shiftDown = (win_modifiers & MOD_SHIFT) != 0;
7845 event.m_controlDown = (win_modifiers & MOD_CONTROL) != 0;
7846 event.m_altDown = (win_modifiers & MOD_ALT) != 0;
7847 event.m_metaDown = (win_modifiers & MOD_WIN) != 0;
7848
7849 return HandleWindowEvent(event);
7850 }
7851
7852 #endif // wxUSE_HOTKEY
7853
7854 // this class installs a message hook which really wakes up our idle processing
7855 // each time a message is handled, even if we're sitting inside a local modal
7856 // loop (e.g. a menu is opened or scrollbar is being dragged or even inside
7857 // ::MessageBox()) and so don't control message dispatching otherwise
7858 class wxIdleWakeUpModule : public wxModule
7859 {
7860 public:
OnInit()7861 virtual bool OnInit() wxOVERRIDE
7862 {
7863 ms_hMsgHookProc = ::SetWindowsHookEx
7864 (
7865 WH_GETMESSAGE,
7866 &wxIdleWakeUpModule::MsgHookProc,
7867 NULL,
7868 GetCurrentThreadId()
7869 );
7870
7871 if ( !ms_hMsgHookProc )
7872 {
7873 wxLogLastError(wxT("SetWindowsHookEx(WH_GETMESSAGE)"));
7874
7875 return false;
7876 }
7877
7878 return true;
7879 }
7880
OnExit()7881 virtual void OnExit() wxOVERRIDE
7882 {
7883 ::UnhookWindowsHookEx(wxIdleWakeUpModule::ms_hMsgHookProc);
7884 }
7885
MsgHookProc(int nCode,WPARAM wParam,LPARAM lParam)7886 static LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam)
7887 {
7888 // Don't process idle events unless the message is going to be really
7889 // handled, i.e. removed from the queue, as it seems wrong to do it
7890 // just because someone called PeekMessage(PM_NOREMOVE).
7891 if ( wParam == PM_REMOVE )
7892 wxTheApp->MSWProcessPendingEventsIfNeeded();
7893
7894 return CallNextHookEx(ms_hMsgHookProc, nCode, wParam, lParam);
7895 }
7896
7897 private:
7898 static HHOOK ms_hMsgHookProc;
7899
7900 wxDECLARE_DYNAMIC_CLASS(wxIdleWakeUpModule);
7901 };
7902
7903 HHOOK wxIdleWakeUpModule::ms_hMsgHookProc = 0;
7904
7905 wxIMPLEMENT_DYNAMIC_CLASS(wxIdleWakeUpModule, wxModule);
7906