added wxSystemSettings::HasFrameDecorations (anybody knows a better name?)
[wxWidgets.git] / src / univ / topluniv.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: topluniv.cpp
3 // Author: Vaclav Slavik
4 // Id: $Id$
5 // Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // ============================================================================
10 // declarations
11 // ============================================================================
12
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16
17 #ifdef __GNUG__
18 #pragma implementation "univtoplevel.h"
19 #endif
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #include "wx/defs.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #endif
34
35 #include "wx/toplevel.h"
36 #include "wx/univ/renderer.h"
37 #include "wx/bitmap.h"
38 #include "wx/image.h"
39 #include "wx/cshelp.h"
40
41
42 // ----------------------------------------------------------------------------
43 // event tables
44 // ----------------------------------------------------------------------------
45
46 BEGIN_EVENT_TABLE(wxTopLevelWindow, wxTopLevelWindowNative)
47 WX_EVENT_TABLE_INPUT_CONSUMER(wxTopLevelWindow)
48 EVT_NC_PAINT(wxTopLevelWindow::OnNcPaint)
49 END_EVENT_TABLE()
50
51 WX_FORWARD_TO_INPUT_CONSUMER(wxTopLevelWindow)
52
53 // ============================================================================
54 // implementation
55 // ============================================================================
56
57 int wxTopLevelWindow::ms_drawDecorations = -1;
58
59 void wxTopLevelWindow::Init()
60 {
61 m_isActive = FALSE;
62 m_windowStyle = 0;
63 m_pressedButton = 0;
64 }
65
66 bool wxTopLevelWindow::Create(wxWindow *parent,
67 wxWindowID id,
68 const wxString& title,
69 const wxPoint& pos,
70 const wxSize& size,
71 long style,
72 const wxString &name)
73 {
74 // init them to avoid compiler warnings
75 long styleOrig = 0,
76 exstyleOrig = 0;
77
78 if ( ms_drawDecorations == -1 )
79 ms_drawDecorations = !wxSystemSettings::HasFrameDecorations();
80 // FIXME -- wxUniv should provide a way to force non-native decorations!
81
82 if ( ms_drawDecorations )
83 {
84 CreateInputHandler(wxINP_HANDLER_TOPLEVEL);
85
86 styleOrig = style;
87 exstyleOrig = GetExtraStyle();
88 style &= ~(wxCAPTION | wxMINIMIZE_BOX | wxMAXIMIZE_BOX |
89 wxSYSTEM_MENU | wxRESIZE_BORDER | wxFRAME_TOOL_WINDOW |
90 wxTHICK_FRAME);
91 style = wxSIMPLE_BORDER;
92 SetExtraStyle(exstyleOrig &
93 ~(wxFRAME_EX_CONTEXTHELP | wxDIALOG_EX_CONTEXTHELP));
94 }
95
96 if ( !wxTopLevelWindowNative::Create(parent, id, title, pos,
97 size, style, name) )
98 return FALSE;
99
100 // FIXME: to be removed as soon as wxTLW/wxFrame/wxDialog creation code in
101 // wxMSW is rationalized
102 #ifdef __WXMSW__
103 extern const wxChar *wxFrameClassName;
104 if ( !MSWCreate(id, NULL, wxFrameClassName, this, title,
105 pos.x, pos.y, size.x, size.y, style) )
106 return FALSE;
107 #endif // __WXMSW__
108
109 if ( ms_drawDecorations )
110 {
111 m_windowStyle = styleOrig;
112 m_exStyle = exstyleOrig;
113 }
114
115 return TRUE;
116 }
117
118 bool wxTopLevelWindow::ShowFullScreen(bool show, long style)
119 {
120 if ( show == IsFullScreen() ) return FALSE;
121
122 if ( ms_drawDecorations )
123 {
124 if ( show )
125 {
126 m_fsSavedStyle = m_windowStyle;
127 if ( style & wxFULLSCREEN_NOBORDER )
128 m_windowStyle |= wxSIMPLE_BORDER;
129 if ( style & wxFULLSCREEN_NOCAPTION )
130 m_windowStyle &= ~wxCAPTION;
131 }
132 else
133 {
134 m_windowStyle = m_fsSavedStyle;
135 }
136 }
137
138 return wxTopLevelWindowNative::ShowFullScreen(show, style);
139 }
140
141 long wxTopLevelWindow::GetDecorationsStyle() const
142 {
143 long style = 0;
144
145 if ( m_windowStyle & wxCAPTION )
146 {
147 style |= wxTOPLEVEL_TITLEBAR | wxTOPLEVEL_BUTTON_CLOSE;
148 if ( m_windowStyle & wxMINIMIZE_BOX )
149 style |= wxTOPLEVEL_BUTTON_ICONIZE;
150 if ( m_windowStyle & wxMAXIMIZE_BOX )
151 style |= wxTOPLEVEL_BUTTON_MAXIMIZE;
152 #if wxUSE_HELP
153 if ( m_exStyle & (wxFRAME_EX_CONTEXTHELP | wxDIALOG_EX_CONTEXTHELP))
154 style |= wxTOPLEVEL_BUTTON_HELP;
155 #endif
156 }
157 if ( (m_windowStyle & (wxSIMPLE_BORDER | wxNO_BORDER)) == 0 )
158 style |= wxTOPLEVEL_BORDER;
159 if ( m_windowStyle & (wxRESIZE_BORDER | wxTHICK_FRAME) )
160 style |= wxTOPLEVEL_RESIZEABLE;
161
162 if ( IsMaximized() )
163 style |= wxTOPLEVEL_MAXIMIZED;
164 if ( GetIcon().Ok() )
165 style |= wxTOPLEVEL_ICON;
166 if ( m_isActive )
167 style |= wxTOPLEVEL_ACTIVE;
168
169 return style;
170 }
171
172 void wxTopLevelWindow::RefreshTitleBar()
173 {
174 wxNcPaintEvent event(GetId());
175 event.SetEventObject(this);
176 GetEventHandler()->ProcessEvent(event);
177 }
178
179 // ----------------------------------------------------------------------------
180 // client area handling
181 // ----------------------------------------------------------------------------
182
183 wxPoint wxTopLevelWindow::GetClientAreaOrigin() const
184 {
185 if ( ms_drawDecorations )
186 {
187 int w, h;
188 wxTopLevelWindowNative::DoGetClientSize(&w, &h);
189 wxRect rect = wxRect(wxTopLevelWindowNative::GetClientAreaOrigin(),
190 wxSize(w, h));
191 rect = m_renderer->GetFrameClientArea(rect,
192 GetDecorationsStyle());
193 return rect.GetPosition();
194 }
195 else
196 {
197 return wxTopLevelWindowNative::GetClientAreaOrigin();
198 }
199 }
200
201 void wxTopLevelWindow::DoGetClientSize(int *width, int *height) const
202 {
203 if ( ms_drawDecorations )
204 {
205 int w, h;
206 wxTopLevelWindowNative::DoGetClientSize(&w, &h);
207 wxRect rect = wxRect(wxTopLevelWindowNative::GetClientAreaOrigin(),
208 wxSize(w, h));
209 rect = m_renderer->GetFrameClientArea(rect,
210 GetDecorationsStyle());
211 if ( width )
212 *width = rect.width;
213 if ( height )
214 *height = rect.height;
215 }
216 else
217 wxTopLevelWindowNative::DoGetClientSize(width, height);
218 }
219
220 void wxTopLevelWindow::DoSetClientSize(int width, int height)
221 {
222 if ( ms_drawDecorations )
223 {
224 wxSize size = m_renderer->GetFrameTotalSize(wxSize(width, height),
225 GetDecorationsStyle());
226 wxTopLevelWindowNative::DoSetClientSize(size.x, size.y);
227 }
228 else
229 wxTopLevelWindowNative::DoSetClientSize(width, height);
230 }
231
232 void wxTopLevelWindow::OnNcPaint(wxPaintEvent& event)
233 {
234 if ( !ms_drawDecorations || !m_renderer )
235 event.Skip();
236 else
237 {
238 // get the window rect
239 wxRect rect;
240 wxSize size = GetSize();
241 rect.x =
242 rect.y = 0;
243 rect.width = size.x;
244 rect.height = size.y;
245
246 wxWindowDC dc(this);
247 m_renderer->DrawFrameTitleBar(dc, rect,
248 GetTitle(), m_titlebarIcon,
249 GetDecorationsStyle(),
250 m_pressedButton,
251 wxCONTROL_PRESSED);
252 }
253 }
254
255 long wxTopLevelWindow::HitTest(const wxPoint& pt) const
256 {
257 int w, h;
258 wxTopLevelWindowNative::DoGetClientSize(&w, &h);
259 wxRect rect(wxTopLevelWindowNative::GetClientAreaOrigin(), wxSize(w, h));
260
261 return m_renderer->HitTestFrame(rect, pt, GetDecorationsStyle());
262 }
263
264 // ----------------------------------------------------------------------------
265 // icons
266 // ----------------------------------------------------------------------------
267
268 void wxTopLevelWindow::SetIcon(const wxIcon& icon)
269 {
270 wxTopLevelWindowNative::SetIcon(icon);
271 if ( !m_renderer ) return;
272
273 wxSize size = m_renderer->GetFrameIconSize();
274
275 if ( !icon.Ok() || size.x == -1 )
276 m_titlebarIcon = icon;
277 else
278 {
279 wxBitmap bmp1;
280 bmp1.CopyFromIcon(icon);
281 if ( !bmp1.Ok() )
282 m_titlebarIcon = wxNullIcon;
283 else if ( bmp1.GetWidth() == size.x && bmp1.GetHeight() == size.y )
284 m_titlebarIcon = icon;
285 else
286 {
287 wxImage img = bmp1.ConvertToImage();
288 img.Rescale(size.x, size.y);
289 m_titlebarIcon.CopyFromBitmap(wxBitmap(img));
290 }
291 }
292 }
293
294 // ----------------------------------------------------------------------------
295 // actions
296 // ----------------------------------------------------------------------------
297
298 void wxTopLevelWindow::ClickTitleBarButton(long button)
299 {
300 switch ( button )
301 {
302 case wxTOPLEVEL_BUTTON_CLOSE:
303 Close();
304 break;
305
306 case wxTOPLEVEL_BUTTON_ICONIZE:
307 Iconize();
308 break;
309
310 case wxTOPLEVEL_BUTTON_MAXIMIZE:
311 Maximize();
312 break;
313
314 case wxTOPLEVEL_BUTTON_RESTORE:
315 Restore();
316 break;
317
318 case wxTOPLEVEL_BUTTON_HELP:
319 #if wxUSE_HELP
320 {
321 wxContextHelp contextHelp(this);
322 }
323 #endif
324 break;
325
326 default:
327 wxFAIL_MSG(wxT("incorrect button specification"));
328 }
329 }
330
331 bool wxTopLevelWindow::PerformAction(const wxControlAction& action,
332 long numArg,
333 const wxString& strArg)
334 {
335 bool isActive = numArg != 0;
336
337 if ( action == wxACTION_TOPLEVEL_ACTIVATE )
338 {
339 if ( m_isActive != isActive )
340 {
341 m_isActive = isActive;
342 RefreshTitleBar();
343 }
344 return TRUE;
345 }
346
347 else if ( action == wxACTION_TOPLEVEL_BUTTON_PRESS )
348 {
349 m_pressedButton = numArg;
350 RefreshTitleBar();
351 return TRUE;
352 }
353
354 else if ( action == wxACTION_TOPLEVEL_BUTTON_RELEASE )
355 {
356 m_pressedButton = 0;
357 RefreshTitleBar();
358 return TRUE;
359 }
360
361 else if ( action == wxACTION_TOPLEVEL_BUTTON_CLICK )
362 {
363 m_pressedButton = 0;
364 RefreshTitleBar();
365 ClickTitleBarButton(numArg);
366 return TRUE;
367 }
368
369 else if ( action == wxACTION_TOPLEVEL_MOVE )
370 {
371 InteractiveMove(wxINTERACTIVE_MOVE);
372 return TRUE;
373 }
374
375 else if ( action == wxACTION_TOPLEVEL_RESIZE )
376 {
377 int flags = wxINTERACTIVE_RESIZE;
378 if ( numArg & wxHT_TOPLEVEL_BORDER_N )
379 flags |= wxINTERACTIVE_RESIZE_N;
380 if ( numArg & wxHT_TOPLEVEL_BORDER_S )
381 flags |= wxINTERACTIVE_RESIZE_S;
382 if ( numArg & wxHT_TOPLEVEL_BORDER_W )
383 flags |= wxINTERACTIVE_RESIZE_W;
384 if ( numArg & wxHT_TOPLEVEL_BORDER_E )
385 flags |= wxINTERACTIVE_RESIZE_E;
386 InteractiveMove(flags);
387 return TRUE;
388 }
389
390 else
391 return FALSE;
392 }
393
394
395 // ============================================================================
396 // wxStdFrameInputHandler: handles focus, resizing and titlebar buttons clicks
397 // ============================================================================
398
399 wxStdFrameInputHandler::wxStdFrameInputHandler(wxInputHandler *inphand)
400 : wxStdInputHandler(inphand)
401 {
402 m_winCapture = NULL;
403 m_winHitTest = 0;
404 m_winPressed = 0;
405 m_borderCursorOn = FALSE;
406 }
407
408 bool wxStdFrameInputHandler::HandleMouse(wxInputConsumer *consumer,
409 const wxMouseEvent& event)
410 {
411 // the button has 2 states: pressed and normal with the following
412 // transitions between them:
413 //
414 // normal -> left down -> capture mouse and go to pressed state
415 // pressed -> left up inside -> generate click -> go to normal
416 // outside ------------------>
417 //
418 // the other mouse buttons are ignored
419 if ( event.Button(1) )
420 {
421 if ( event.ButtonDown(1) )
422 {
423 wxTopLevelWindow *w = wxStaticCast(consumer->GetInputWindow(), wxTopLevelWindow);
424 long hit = w->HitTest(event.GetPosition());
425
426 if ( hit & wxHT_TOPLEVEL_ANY_BUTTON )
427 {
428 m_winCapture = w;
429 m_winCapture->CaptureMouse();
430 m_winHitTest = hit;
431 m_winPressed = hit;
432 consumer->PerformAction(wxACTION_TOPLEVEL_BUTTON_PRESS, m_winPressed);
433 return TRUE;
434 }
435 else if ( hit & wxHT_TOPLEVEL_TITLEBAR )
436 {
437 consumer->PerformAction(wxACTION_TOPLEVEL_MOVE);
438 return TRUE;
439 }
440 else if ( (consumer->GetInputWindow()->GetWindowStyle() & wxRESIZE_BORDER)
441 && (hit & wxHT_TOPLEVEL_ANY_BORDER) )
442 {
443 consumer->PerformAction(wxACTION_TOPLEVEL_RESIZE, hit);
444 return TRUE;
445 }
446 }
447
448 else // up
449 {
450 if ( m_winCapture )
451 {
452 m_winCapture->ReleaseMouse();
453 m_winCapture = NULL;
454
455 if ( m_winHitTest == m_winPressed )
456 {
457 consumer->PerformAction(wxACTION_TOPLEVEL_BUTTON_CLICK, m_winPressed);
458 return TRUE;
459 }
460 }
461 //else: the mouse was released outside the window, this doesn't
462 // count as a click
463 }
464 }
465
466 return wxStdInputHandler::HandleMouse(consumer, event);
467 }
468
469 bool wxStdFrameInputHandler::HandleMouseMove(wxInputConsumer *consumer,
470 const wxMouseEvent& event)
471 {
472 if ( event.GetEventObject() == m_winCapture )
473 {
474 long hit = m_winCapture->HitTest(event.GetPosition());
475
476 if ( hit != m_winHitTest )
477 {
478 if ( hit != m_winPressed )
479 consumer->PerformAction(wxACTION_TOPLEVEL_BUTTON_RELEASE, m_winPressed);
480 else
481 consumer->PerformAction(wxACTION_TOPLEVEL_BUTTON_PRESS, m_winPressed);
482
483 m_winHitTest = hit;
484 return TRUE;
485 }
486 }
487 else if ( consumer->GetInputWindow()->GetWindowStyle() & wxRESIZE_BORDER )
488 {
489 wxTopLevelWindow *win = wxStaticCast(consumer->GetInputWindow(),
490 wxTopLevelWindow);
491 long hit = win->HitTest(event.GetPosition());
492
493 if ( hit != m_winHitTest )
494 {
495 m_winHitTest = hit;
496
497 if ( m_borderCursorOn )
498 {
499 m_borderCursorOn = FALSE;
500 win->SetCursor(m_origCursor);
501 }
502
503 if ( hit & wxHT_TOPLEVEL_ANY_BORDER )
504 {
505 m_borderCursorOn = TRUE;
506 wxCursor cur;
507
508 switch (hit)
509 {
510 case wxHT_TOPLEVEL_BORDER_N:
511 case wxHT_TOPLEVEL_BORDER_S:
512 cur = wxCursor(wxCURSOR_SIZENS);
513 break;
514 case wxHT_TOPLEVEL_BORDER_W:
515 case wxHT_TOPLEVEL_BORDER_E:
516 cur = wxCursor(wxCURSOR_SIZEWE);
517 break;
518 case wxHT_TOPLEVEL_BORDER_NE:
519 case wxHT_TOPLEVEL_BORDER_SW:
520 cur = wxCursor(wxCURSOR_SIZENESW);
521 break;
522 case wxHT_TOPLEVEL_BORDER_NW:
523 case wxHT_TOPLEVEL_BORDER_SE:
524 cur = wxCursor(wxCURSOR_SIZENWSE);
525 break;
526 default:
527 m_borderCursorOn = FALSE;
528 break;
529 }
530 if ( m_borderCursorOn )
531 {
532 m_origCursor = win->GetCursor();
533 win->SetCursor(cur);
534 }
535 }
536 }
537 }
538
539 return wxStdInputHandler::HandleMouseMove(consumer, event);
540 }
541
542 bool wxStdFrameInputHandler::HandleActivation(wxInputConsumer *consumer,
543 bool activated)
544 {
545 if ( m_borderCursorOn )
546 {
547 consumer->GetInputWindow()->SetCursor(m_origCursor);
548 m_borderCursorOn = FALSE;
549 }
550 consumer->PerformAction(wxACTION_TOPLEVEL_ACTIVATE, activated);
551 return FALSE;
552 }