no significant changes
[wxWidgets.git] / src / msw / toplevel.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/toplevel.cpp
3 // Purpose: implements wxTopLevelWindow for MSW
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 24.09.01
7 // RCS-ID: $Id$
8 // Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "toplevel.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/app.h"
33 #include "wx/toplevel.h"
34 #include "wx/string.h"
35 #include "wx/log.h"
36 #include "wx/intl.h"
37 #include "wx/frame.h"
38 #endif //WX_PRECOMP
39
40 #include "wx/msw/private.h"
41
42 #ifndef ICON_BIG
43 #define ICON_BIG 1
44 #endif
45
46 #ifndef ICON_SMALL
47 #define ICON_SMALL 0
48 #endif
49
50 // ----------------------------------------------------------------------------
51 // stubs for missing functions under MicroWindows
52 // ----------------------------------------------------------------------------
53
54 #ifdef __WXMICROWIN__
55
56 // static inline bool IsIconic(HWND WXUNUSED(hwnd)) { return FALSE; }
57 static inline bool IsZoomed(HWND WXUNUSED(hwnd)) { return FALSE; }
58
59 #endif // __WXMICROWIN__
60
61 // ----------------------------------------------------------------------------
62 // globals
63 // ----------------------------------------------------------------------------
64
65 // list of all frames and modeless dialogs
66 wxWindowList wxModelessWindows;
67
68 // the name of the default wxWindows class
69 extern const wxChar *wxCanvasClassName;
70
71 // ============================================================================
72 // wxTopLevelWindowMSW implementation
73 // ============================================================================
74
75 // ----------------------------------------------------------------------------
76 // wxDialog helpers
77 // ----------------------------------------------------------------------------
78
79 // Dialog window proc
80 LONG APIENTRY _EXPORT
81 wxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
82 {
83 switch ( message )
84 {
85 case WM_INITDIALOG:
86 // for this message, returning TRUE tells system to set focus to the
87 // first control in the dialog box
88 return TRUE;
89
90 default:
91 // for all the other ones, FALSE means that we didn't process the
92 // message
93 return FALSE;
94 }
95 }
96
97 // ----------------------------------------------------------------------------
98 // wxTopLevelWindowMSW creation
99 // ----------------------------------------------------------------------------
100
101 void wxTopLevelWindowMSW::Init()
102 {
103 m_iconized =
104 m_maximizeOnShow = FALSE;
105
106 // unlike (almost?) all other windows, frames are created hidden
107 m_isShown = FALSE;
108
109 // Data to save/restore when calling ShowFullScreen
110 m_fsStyle = 0;
111 m_fsOldWindowStyle = 0;
112 m_fsIsMaximized = FALSE;
113 m_fsIsShowing = FALSE;
114 }
115
116 WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const
117 {
118 // let the base class deal with the common styles but fix the ones which
119 // don't make sense for us (we also deal with the borders ourselves)
120 WXDWORD msflags = wxWindow::MSWGetStyle
121 (
122 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exflags
123 ) & ~WS_CHILD;
124
125 // first select the kind of window being created
126 //
127 // note that if we don't set WS_POPUP, Windows assumes WS_OVERLAPPED and
128 // creates a window with both caption and border, hence we also test it
129 // below in some other cases
130 if ( style & wxFRAME_TOOL_WINDOW )
131 msflags |= WS_POPUP;
132 else
133 msflags |= WS_OVERLAPPED;
134
135 // border and caption styles
136 if ( style & wxRESIZE_BORDER )
137 msflags |= WS_THICKFRAME;
138 else if ( !(style & wxBORDER_NONE) )
139 msflags |= WS_BORDER;
140 else
141 msflags |= WS_POPUP;
142
143 if ( style & wxCAPTION )
144 msflags |= WS_CAPTION;
145 else
146 msflags |= WS_POPUP;
147
148 // next translate the individual flags
149 if ( style & wxMINIMIZE_BOX )
150 msflags |= WS_MINIMIZEBOX;
151 if ( style & wxMAXIMIZE_BOX )
152 msflags |= WS_MAXIMIZEBOX;
153 if ( style & wxSYSTEM_MENU )
154 msflags |= WS_SYSMENU;
155 if ( style & wxMINIMIZE )
156 msflags |= WS_MINIMIZE;
157 if ( style & wxMAXIMIZE )
158 msflags |= WS_MAXIMIZE;
159
160 // Keep this here because it saves recoding this function in wxTinyFrame
161 #if wxUSE_ITSY_BITSY && !defined(__WIN32__)
162 if ( style & wxTINY_CAPTION_VERT )
163 msflags |= IBS_VERTCAPTION;
164 if ( style & wxTINY_CAPTION_HORIZ )
165 msflags |= IBS_HORZCAPTION;
166 #else
167 if ( style & (wxTINY_CAPTION_VERT | wxTINY_CAPTION_HORIZ) )
168 msflags |= WS_CAPTION;
169 #endif
170
171 if ( exflags )
172 {
173 #if !defined(__WIN16__) && !defined(__SC__)
174 if ( !(GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) )
175 {
176 // make all frames appear in the win9x shell taskbar unless
177 // wxFRAME_TOOL_WINDOW or wxFRAME_NO_TASKBAR is given - without
178 // giving them WS_EX_APPWINDOW style, the child (i.e. owned) frames
179 // wouldn't appear in it
180 if ( (style & wxFRAME_TOOL_WINDOW) || (style & wxFRAME_NO_TASKBAR) )
181 *exflags |= WS_EX_TOOLWINDOW;
182 else if ( !(style & wxFRAME_NO_TASKBAR) )
183 *exflags |= WS_EX_APPWINDOW;
184 }
185 #endif // !Win16
186
187 if ( style & wxSTAY_ON_TOP )
188 *exflags |= WS_EX_TOPMOST;
189
190 #ifdef __WIN32__
191 if ( GetExtraStyle() & wxFRAME_EX_CONTEXTHELP )
192 *exflags |= WS_EX_CONTEXTHELP;
193 #endif // __WIN32__
194 }
195
196 return msflags;
197 }
198
199 bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate,
200 const wxString& title,
201 const wxPoint& pos,
202 const wxSize& size)
203 {
204 #ifdef __WXMICROWIN__
205 // no dialogs support under MicroWin yet
206 return CreateFrame(title, pos, size);
207 #else // !__WXMICROWIN__
208 wxWindow *parent = GetParent();
209
210 // for the dialogs without wxDIALOG_NO_PARENT style, use the top level
211 // app window as parent - this avoids creating modal dialogs without
212 // parent
213 if ( !parent && !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT) )
214 {
215 parent = wxTheApp->GetTopWindow();
216
217 if ( parent )
218 {
219 // don't use transient windows as parents, this is dangerous as it
220 // can lead to a crash if the parent is destroyed before the child
221 //
222 // also don't use the window which is currently hidden as then the
223 // dialog would be hidden as well
224 if ( (parent->GetExtraStyle() & wxWS_EX_TRANSIENT) ||
225 !parent->IsShown() )
226 {
227 parent = NULL;
228 }
229 }
230 }
231
232 m_hWnd = (WXHWND)::CreateDialogIndirect
233 (
234 wxGetInstance(),
235 (DLGTEMPLATE*)dlgTemplate,
236 parent ? GetHwndOf(parent) : NULL,
237 (DLGPROC)wxDlgProc
238 );
239
240 if ( !m_hWnd )
241 {
242 wxFAIL_MSG(_("Failed to create dialog. Incorrect DLGTEMPLATE?"));
243
244 wxLogSysError(_("Can't create dialog using memory template"));
245
246 return FALSE;
247 }
248
249 WXDWORD exflags;
250 (void)MSWGetCreateWindowFlags(&exflags);
251
252 if ( exflags )
253 {
254 ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exflags);
255 ::SetWindowPos(GetHwnd(), NULL, 0, 0, 0, 0,
256 SWP_NOSIZE |
257 SWP_NOMOVE |
258 SWP_NOZORDER |
259 SWP_NOACTIVATE);
260 }
261
262 #if defined(__WIN95__)
263 // For some reason, the system menu is activated when we use the
264 // WS_EX_CONTEXTHELP style, so let's set a reasonable icon
265 if ( exflags & WS_EX_CONTEXTHELP )
266 {
267 wxFrame *winTop = wxDynamicCast(wxTheApp->GetTopWindow(), wxFrame);
268 if ( winTop )
269 {
270 wxIcon icon = winTop->GetIcon();
271 if ( icon.Ok() )
272 {
273 ::SendMessage(GetHwnd(), WM_SETICON,
274 (WPARAM)TRUE,
275 (LPARAM)GetHiconOf(icon));
276 }
277 }
278 }
279 #endif // __WIN95__
280
281 // move the dialog to its initial position without forcing repainting
282 int x, y, w, h;
283 if ( !MSWGetCreateWindowCoords(pos, size, x, y, w, h) )
284 {
285 x =
286 w = (int)CW_USEDEFAULT;
287 }
288
289 // we can't use CW_USEDEFAULT here as we're not calling CreateWindow()
290 // and passing CW_USEDEFAULT to MoveWindow() results in resizing the
291 // window to (0, 0) size which breaks quite a lot of things, e.g. the
292 // sizer calculation in wxSizer::Fit()
293 if ( w == (int)CW_USEDEFAULT )
294 {
295 // the exact number doesn't matter, the dialog will be resized
296 // again soon anyhow but it should be big enough to allow
297 // calculation relying on "totalSize - clientSize > 0" work, i.e.
298 // at least greater than the title bar height
299 w =
300 h = 100;
301 }
302
303 if ( x == (int)CW_USEDEFAULT )
304 {
305 // centre it on the screen - what else can we do?
306 wxSize sizeDpy = wxGetDisplaySize();
307
308 x = (sizeDpy.x - w) / 2;
309 y = (sizeDpy.y - h) / 2;
310 }
311
312 if ( !::MoveWindow(GetHwnd(), x, y, w, h, FALSE) )
313 {
314 wxLogLastError(wxT("MoveWindow"));
315 }
316
317 if ( !title.empty() )
318 {
319 ::SetWindowText(GetHwnd(), title);
320 }
321
322 SubclassWin(m_hWnd);
323
324 return TRUE;
325 #endif // __WXMICROWIN__/!__WXMICROWIN__
326 }
327
328 bool wxTopLevelWindowMSW::CreateFrame(const wxString& title,
329 const wxPoint& pos,
330 const wxSize& size)
331 {
332 WXDWORD exflags;
333 WXDWORD flags = MSWGetCreateWindowFlags(&exflags);
334
335 return MSWCreate(wxCanvasClassName, title, pos, size, flags, exflags);
336 }
337
338 bool wxTopLevelWindowMSW::Create(wxWindow *parent,
339 wxWindowID id,
340 const wxString& title,
341 const wxPoint& pos,
342 const wxSize& size,
343 long style,
344 const wxString& name)
345 {
346 // init our fields
347 Init();
348
349 m_windowStyle = style;
350
351 SetName(name);
352
353 m_windowId = id == -1 ? NewControlId() : id;
354
355 wxTopLevelWindows.Append(this);
356
357 if ( parent )
358 parent->AddChild(this);
359
360 if ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG )
361 {
362 // we have different dialog templates to allows creation of dialogs
363 // with & without captions under MSWindows, resizeable or not (but a
364 // resizeable dialog always has caption - otherwise it would look too
365 // strange)
366
367 // we need 3 additional WORDs for dialog menu, class and title (as we
368 // don't use DS_SETFONT we don't need the fourth WORD for the font)
369 static const int dlgsize = sizeof(DLGTEMPLATE) + (sizeof(WORD) * 3);
370 DLGTEMPLATE *dlgTemplate = (DLGTEMPLATE *)malloc(dlgsize);
371 memset(dlgTemplate, 0, dlgsize);
372
373 // these values are arbitrary, they won't be used normally anyhow
374 dlgTemplate->x = 34;
375 dlgTemplate->y = 22;
376 dlgTemplate->cx = 144;
377 dlgTemplate->cy = 75;
378
379 // reuse the code in MSWGetStyle() but correct the results slightly for
380 // the dialog
381 dlgTemplate->style = MSWGetStyle(style, NULL);
382
383 // all dialogs are popups
384 dlgTemplate->style |= WS_POPUP;
385
386 // force 3D-look if necessary, it looks impossibly ugly otherwise
387 if ( style & (wxRESIZE_BORDER | wxCAPTION) )
388 dlgTemplate->style |= DS_MODALFRAME;
389
390 bool ret = CreateDialog(dlgTemplate, title, pos, size);
391 free(dlgTemplate);
392
393 return ret;
394 }
395 else // !dialog
396 {
397 return CreateFrame(title, pos, size);
398 }
399 }
400
401 wxTopLevelWindowMSW::~wxTopLevelWindowMSW()
402 {
403 wxTopLevelWindows.DeleteObject(this);
404
405 if ( wxModelessWindows.Find(this) )
406 wxModelessWindows.DeleteObject(this);
407
408 // If this is the last top-level window, exit.
409 if ( wxTheApp && (wxTopLevelWindows.Number() == 0) )
410 {
411 wxTheApp->SetTopWindow(NULL);
412
413 if ( wxTheApp->GetExitOnFrameDelete() )
414 {
415 ::PostQuitMessage(0);
416 }
417 }
418 }
419
420 // ----------------------------------------------------------------------------
421 // wxTopLevelWindowMSW showing
422 // ----------------------------------------------------------------------------
423
424 void wxTopLevelWindowMSW::DoShowWindow(int nShowCmd)
425 {
426 ::ShowWindow(GetHwnd(), nShowCmd);
427
428 m_iconized = nShowCmd == SW_MINIMIZE;
429 }
430
431 bool wxTopLevelWindowMSW::Show(bool show)
432 {
433 // don't use wxWindow version as we want to call DoShowWindow() ourselves
434 if ( !wxWindowBase::Show(show) )
435 return FALSE;
436
437 int nShowCmd;
438 if ( show )
439 {
440 if ( m_maximizeOnShow )
441 {
442 // show and maximize
443 nShowCmd = SW_MAXIMIZE;
444
445 m_maximizeOnShow = FALSE;
446 }
447 else // just show
448 {
449 nShowCmd = SW_SHOW;
450 }
451 }
452 else // hide
453 {
454 nShowCmd = SW_HIDE;
455 }
456
457 DoShowWindow(nShowCmd);
458
459 if ( show )
460 {
461 ::BringWindowToTop(GetHwnd());
462
463 wxActivateEvent event(wxEVT_ACTIVATE, TRUE, m_windowId);
464 event.SetEventObject( this );
465 GetEventHandler()->ProcessEvent(event);
466 }
467 else // hide
468 {
469 // Try to highlight the correct window (the parent)
470 if ( GetParent() )
471 {
472 HWND hWndParent = GetHwndOf(GetParent());
473 if (hWndParent)
474 ::BringWindowToTop(hWndParent);
475 }
476 }
477
478 return TRUE;
479 }
480
481 // ----------------------------------------------------------------------------
482 // wxTopLevelWindowMSW maximize/minimize
483 // ----------------------------------------------------------------------------
484
485 void wxTopLevelWindowMSW::Maximize(bool maximize)
486 {
487 if ( IsShown() )
488 {
489 // just maximize it directly
490 DoShowWindow(maximize ? SW_MAXIMIZE : SW_RESTORE);
491 }
492 else // hidden
493 {
494 // we can't maximize the hidden frame because it shows it as well, so
495 // just remember that we should do it later in this case
496 m_maximizeOnShow = TRUE;
497 }
498 }
499
500 bool wxTopLevelWindowMSW::IsMaximized() const
501 {
502 return ::IsZoomed(GetHwnd()) != 0;
503 }
504
505 void wxTopLevelWindowMSW::Iconize(bool iconize)
506 {
507 DoShowWindow(iconize ? SW_MINIMIZE : SW_RESTORE);
508 }
509
510 bool wxTopLevelWindowMSW::IsIconized() const
511 {
512 // also update the current state
513 ((wxTopLevelWindowMSW *)this)->m_iconized = ::IsIconic(GetHwnd()) != 0;
514
515 return m_iconized;
516 }
517
518 void wxTopLevelWindowMSW::Restore()
519 {
520 DoShowWindow(SW_RESTORE);
521 }
522
523 // ----------------------------------------------------------------------------
524 // wxTopLevelWindowMSW fullscreen
525 // ----------------------------------------------------------------------------
526
527 bool wxTopLevelWindowMSW::ShowFullScreen(bool show, long style)
528 {
529 if (show)
530 {
531 if (IsFullScreen())
532 return FALSE;
533
534 m_fsIsShowing = TRUE;
535 m_fsStyle = style;
536
537 // zap the frame borders
538
539 // save the 'normal' window style
540 m_fsOldWindowStyle = GetWindowLong((HWND)GetHWND(), GWL_STYLE);
541
542 // save the old position, width & height, maximize state
543 m_fsOldSize = GetRect();
544 m_fsIsMaximized = IsMaximized();
545
546 // decide which window style flags to turn off
547 LONG newStyle = m_fsOldWindowStyle;
548 LONG offFlags = 0;
549
550 if (style & wxFULLSCREEN_NOBORDER)
551 offFlags |= WS_BORDER | WS_THICKFRAME;
552 if (style & wxFULLSCREEN_NOCAPTION)
553 offFlags |= (WS_CAPTION | WS_SYSMENU);
554
555 newStyle &= (~offFlags);
556
557 // change our window style to be compatible with full-screen mode
558 ::SetWindowLong((HWND)GetHWND(), GWL_STYLE, newStyle);
559
560 // resize to the size of the desktop
561 int width, height;
562
563 RECT rect = wxGetWindowRect(::GetDesktopWindow());
564 width = rect.right - rect.left;
565 height = rect.bottom - rect.top;
566
567 SetSize(width, height);
568
569 // now flush the window style cache and actually go full-screen
570 SetWindowPos((HWND)GetHWND(), HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED);
571
572 wxSizeEvent event(wxSize(width, height), GetId());
573 GetEventHandler()->ProcessEvent(event);
574
575 return TRUE;
576 }
577 else
578 {
579 if (!IsFullScreen())
580 return FALSE;
581
582 m_fsIsShowing = FALSE;
583
584 Maximize(m_fsIsMaximized);
585 SetWindowLong((HWND)GetHWND(),GWL_STYLE, m_fsOldWindowStyle);
586 SetWindowPos((HWND)GetHWND(),HWND_TOP,m_fsOldSize.x, m_fsOldSize.y,
587 m_fsOldSize.width, m_fsOldSize.height, SWP_FRAMECHANGED);
588
589 return TRUE;
590 }
591 }
592
593 // ----------------------------------------------------------------------------
594 // wxTopLevelWindowMSW misc
595 // ----------------------------------------------------------------------------
596
597 void wxTopLevelWindowMSW::SetIcon(const wxIcon& icon)
598 {
599 SetIcons( wxIconBundle( icon ) );
600 }
601
602 void wxTopLevelWindowMSW::SetIcons(const wxIconBundle& icons)
603 {
604 wxTopLevelWindowBase::SetIcons(icons);
605
606 #if defined(__WIN95__) && !defined(__WXMICROWIN__)
607 const wxIcon& sml = icons.GetIcon( wxSize( 16, 16 ) );
608 if( sml.Ok() && sml.GetWidth() == 16 && sml.GetHeight() == 16 )
609 {
610 ::SendMessage( GetHwndOf( this ), WM_SETICON, ICON_SMALL,
611 (LPARAM)GetHiconOf(sml) );
612 }
613
614 const wxIcon& big = icons.GetIcon( wxSize( 32, 32 ) );
615 if( big.Ok() && big.GetWidth() == 32 && big.GetHeight() == 32 )
616 {
617 ::SendMessage( GetHwndOf( this ), WM_SETICON, ICON_BIG,
618 (LPARAM)GetHiconOf(big) );
619 }
620 #endif // __WIN95__
621 }
622
623 bool wxTopLevelWindowMSW::EnableCloseButton(bool enable)
624 {
625 #ifndef __WXMICROWIN__
626 // get system (a.k.a. window) menu
627 HMENU hmenu = ::GetSystemMenu(GetHwnd(), FALSE /* get it */);
628 if ( !hmenu )
629 {
630 wxLogLastError(_T("GetSystemMenu"));
631
632 return FALSE;
633 }
634
635 // enabling/disabling the close item from it also automatically
636 // disables/enables the close title bar button
637 if ( ::EnableMenuItem(hmenu, SC_CLOSE,
638 MF_BYCOMMAND |
639 (enable ? MF_ENABLED : MF_GRAYED)) == -1 )
640 {
641 wxLogLastError(_T("EnableMenuItem(SC_CLOSE)"));
642
643 return FALSE;
644 }
645
646 // update appearance immediately
647 if ( !::DrawMenuBar(GetHwnd()) )
648 {
649 wxLogLastError(_T("DrawMenuBar"));
650 }
651 #endif // !__WXMICROWIN__
652
653 return TRUE;
654 }
655