simplify client size calculation
[wxWidgets.git] / src / gtk / frame.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/frame.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #include "wx/frame.h"
14
15 #ifndef WX_PRECOMP
16 #include "wx/menu.h"
17 #include "wx/toolbar.h"
18 #include "wx/statusbr.h"
19 #endif // WX_PRECOMP
20
21 #include <gtk/gtk.h>
22 #include "wx/gtk/win_gtk.h"
23
24 // ----------------------------------------------------------------------------
25 // constants
26 // ----------------------------------------------------------------------------
27
28 static const int wxSTATUS_HEIGHT = 25;
29 static const int wxPLACE_HOLDER = 0;
30
31 // ----------------------------------------------------------------------------
32 // event tables
33 // ----------------------------------------------------------------------------
34
35 IMPLEMENT_DYNAMIC_CLASS(wxFrame, wxTopLevelWindow)
36
37 // ============================================================================
38 // implementation
39 // ============================================================================
40
41 // ----------------------------------------------------------------------------
42 // GTK callbacks
43 // ----------------------------------------------------------------------------
44
45 #if wxUSE_MENUS_NATIVE
46
47 //-----------------------------------------------------------------------------
48 // "child_attached" of menu bar
49 //-----------------------------------------------------------------------------
50
51 extern "C" {
52 static void gtk_menu_attached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win )
53 {
54 if (!win->m_hasVMT) return;
55
56 win->m_menuBarDetached = false;
57 win->GtkUpdateSize();
58 }
59 }
60
61 //-----------------------------------------------------------------------------
62 // "child_detached" of menu bar
63 //-----------------------------------------------------------------------------
64
65 extern "C" {
66 static void gtk_menu_detached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win )
67 {
68 if (!win->m_hasVMT) return;
69
70 // Raise the client area area
71 gdk_window_raise( win->m_wxwindow->window );
72
73 win->m_menuBarDetached = true;
74 win->GtkUpdateSize();
75 }
76 }
77
78 #endif // wxUSE_MENUS_NATIVE
79
80 #if wxUSE_TOOLBAR
81 //-----------------------------------------------------------------------------
82 // "child_attached" of tool bar
83 //-----------------------------------------------------------------------------
84
85 extern "C" {
86 static void gtk_toolbar_attached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win )
87 {
88 if (!win->m_hasVMT) return;
89
90 win->m_toolBarDetached = false;
91 win->GtkUpdateSize();
92 }
93 }
94
95 //-----------------------------------------------------------------------------
96 // "child_detached" of tool bar
97 //-----------------------------------------------------------------------------
98
99 extern "C" {
100 static void gtk_toolbar_detached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win )
101 {
102 if (!win->m_hasVMT) return;
103
104 // Raise the client area area
105 gdk_window_raise( win->m_wxwindow->window );
106
107 win->m_toolBarDetached = true;
108 win->GtkUpdateSize();
109 }
110 }
111 #endif // wxUSE_TOOLBAR
112
113
114 // ----------------------------------------------------------------------------
115 // wxFrame itself
116 // ----------------------------------------------------------------------------
117
118 //-----------------------------------------------------------------------------
119 // InsertChild for wxFrame
120 //-----------------------------------------------------------------------------
121
122 /* Callback for wxFrame. This very strange beast has to be used because
123 * C++ has no virtual methods in a constructor. We have to emulate a
124 * virtual function here as wxWidgets requires different ways to insert
125 * a child in container classes. */
126
127 static void wxInsertChildInFrame( wxFrame* parent, wxWindow* child )
128 {
129 wxASSERT( GTK_IS_WIDGET(child->m_widget) );
130
131 if (!parent->m_insertInClientArea)
132 {
133 // These are outside the client area
134 wxFrame* frame = (wxFrame*) parent;
135 gtk_pizza_put( GTK_PIZZA(frame->m_mainWidget),
136 child->m_widget,
137 child->m_x,
138 child->m_y,
139 child->m_width,
140 child->m_height );
141
142 #if wxUSE_TOOLBAR_NATIVE
143 // We connect to these events for recalculating the client area
144 // space when the toolbar is floating
145 if (wxIS_KIND_OF(child,wxToolBar))
146 {
147 wxToolBar *toolBar = (wxToolBar*) child;
148 if (toolBar->GetWindowStyle() & wxTB_DOCKABLE)
149 {
150 g_signal_connect (toolBar->m_widget, "child_attached",
151 G_CALLBACK (gtk_toolbar_attached_callback),
152 parent);
153 g_signal_connect (toolBar->m_widget, "child_detached",
154 G_CALLBACK (gtk_toolbar_detached_callback),
155 parent);
156 }
157 }
158 #endif // wxUSE_TOOLBAR
159 }
160 else
161 {
162 // These are inside the client area
163 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
164 child->m_widget,
165 child->m_x,
166 child->m_y,
167 child->m_width,
168 child->m_height );
169 }
170 }
171
172 // ----------------------------------------------------------------------------
173 // wxFrame creation
174 // ----------------------------------------------------------------------------
175
176 void wxFrame::Init()
177 {
178 m_menuBarDetached = false;
179 m_toolBarDetached = false;
180 m_menuBarHeight = 2;
181 }
182
183 bool wxFrame::Create( wxWindow *parent,
184 wxWindowID id,
185 const wxString& title,
186 const wxPoint& pos,
187 const wxSize& sizeOrig,
188 long style,
189 const wxString &name )
190 {
191 bool rt = wxTopLevelWindow::Create(parent, id, title, pos, sizeOrig,
192 style, name);
193 m_insertCallback = (wxInsertChildFunction) wxInsertChildInFrame;
194
195 return rt;
196 }
197
198 wxFrame::~wxFrame()
199 {
200 m_isBeingDeleted = true;
201 DeleteAllBars();
202 }
203
204 // ----------------------------------------------------------------------------
205 // overridden wxWindow methods
206 // ----------------------------------------------------------------------------
207
208 void wxFrame::DoGetClientSize( int *width, int *height ) const
209 {
210 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
211
212 wxFrameBase::DoGetClientSize(width, height);
213
214 if (height)
215 {
216 #if wxUSE_MENUS_NATIVE
217 // menu bar
218 if (m_frameMenuBar &&
219 GTK_WIDGET_VISIBLE(m_frameMenuBar->m_widget) && !m_menuBarDetached)
220 {
221 *height -= m_menuBarHeight;
222 }
223 #endif // wxUSE_MENUS_NATIVE
224
225 #if wxUSE_STATUSBAR
226 // status bar
227 if (m_frameStatusBar && GTK_WIDGET_VISIBLE(m_frameStatusBar->m_widget))
228 *height -= wxSTATUS_HEIGHT;
229 #endif // wxUSE_STATUSBAR
230 }
231
232 #if wxUSE_TOOLBAR
233 // tool bar
234 if (m_frameToolBar &&
235 GTK_WIDGET_VISIBLE(m_frameToolBar->m_widget) && !m_toolBarDetached)
236 {
237 if (m_frameToolBar->IsVertical())
238 {
239 if (width)
240 *width -= m_frameToolBar->GetSize().x;
241 }
242 else
243 {
244 if (height)
245 *height -= m_frameToolBar->GetSize().y;
246 }
247 }
248 #endif // wxUSE_TOOLBAR
249
250 if (width != NULL && *width < 0)
251 *width = 0;
252 if (height != NULL && *height < 0)
253 *height = 0;
254 }
255
256 void wxFrame::GtkOnSize()
257 {
258 // avoid recursions
259 if (m_resizing) return;
260 m_resizing = true;
261
262 // this shouldn't happen: wxFrame, wxMDIParentFrame and wxMDIChildFrame have m_wxwindow
263 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
264
265 // space occupied by m_frameToolBar and m_frameMenuBar
266 int client_area_x_offset = 0,
267 client_area_y_offset = 0;
268
269 /* wxMDIChildFrame derives from wxFrame but it _is_ a wxWindow as it uses
270 wxWindow::Create to create it's GTK equivalent. m_mainWidget is only
271 set in wxFrame::Create so it is used to check what kind of frame we
272 have here. if m_mainWidget is NULL it is a wxMDIChildFrame and so we
273 skip the part which handles m_frameMenuBar, m_frameToolBar and (most
274 importantly) m_mainWidget */
275
276 ConstrainSize();
277
278 if (m_mainWidget)
279 {
280 // TODO
281 // Rewrite this terrible code to using GtkVBox
282
283 // m_mainWidget holds the menubar, the toolbar and the client
284 // area, which is represented by m_wxwindow.
285
286 #if wxUSE_MENUS_NATIVE
287 if (m_frameMenuBar && !(m_fsIsShowing && (m_fsSaveFlag & wxFULLSCREEN_NOMENUBAR) != 0))
288 {
289 if (!GTK_WIDGET_VISIBLE(m_frameMenuBar->m_widget))
290 gtk_widget_show( m_frameMenuBar->m_widget );
291 int xx = m_miniEdge;
292 int yy = m_miniEdge + m_miniTitle;
293 int ww = m_width - 2*m_miniEdge;
294 if (ww < 0)
295 ww = 0;
296 int hh = m_menuBarHeight;
297 if (m_menuBarDetached) hh = wxPLACE_HOLDER;
298 m_frameMenuBar->m_x = xx;
299 m_frameMenuBar->m_y = yy;
300 m_frameMenuBar->m_width = ww;
301 m_frameMenuBar->m_height = hh;
302 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
303 m_frameMenuBar->m_widget,
304 xx, yy, ww, hh );
305 client_area_y_offset += hh;
306 }
307 else
308 {
309 if (m_frameMenuBar)
310 {
311 if (GTK_WIDGET_VISIBLE(m_frameMenuBar->m_widget))
312 gtk_widget_hide( m_frameMenuBar->m_widget );
313 }
314 }
315 #endif // wxUSE_MENUS_NATIVE
316
317 #if wxUSE_TOOLBAR
318 if ((m_frameToolBar) && m_frameToolBar->IsShown() &&
319 (m_frameToolBar->m_widget->parent == m_mainWidget))
320 {
321 int xx = m_miniEdge;
322 int yy = m_miniEdge + m_miniTitle;
323 #if wxUSE_MENUS_NATIVE
324 if (m_frameMenuBar)
325 {
326 if (!m_menuBarDetached)
327 yy += m_menuBarHeight;
328 else
329 yy += wxPLACE_HOLDER;
330 }
331 #endif // wxUSE_MENUS_NATIVE
332
333 m_frameToolBar->m_x = xx;
334 m_frameToolBar->m_y = yy;
335
336 // don't change the toolbar's reported height/width
337 int ww, hh;
338 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL )
339 {
340 ww = m_toolBarDetached ? wxPLACE_HOLDER
341 : m_frameToolBar->m_width;
342 hh = m_height - 2*m_miniEdge;
343
344 client_area_x_offset += ww;
345 }
346 else if( m_frameToolBar->HasFlag(wxTB_RIGHT) )
347 {
348 yy += 2;
349 ww = m_toolBarDetached ? wxPLACE_HOLDER
350 : m_frameToolBar->m_width;
351 xx = GetClientSize().x - 1;
352 hh = m_height - 2*m_miniEdge;
353 if( hh < 0 )
354 hh = 0;
355
356 }
357 else if( m_frameToolBar->GetWindowStyle() & wxTB_BOTTOM )
358 {
359 xx = m_miniEdge;
360 yy = GetClientSize().y;
361 #if wxUSE_MENUS_NATIVE
362 yy += m_menuBarHeight;
363 #endif // wxUSE_MENU_NATIVE
364 m_frameToolBar->m_x = xx;
365 m_frameToolBar->m_y = yy;
366 ww = m_width - 2*m_miniEdge;
367 hh = m_toolBarDetached ? wxPLACE_HOLDER
368 : m_frameToolBar->m_height;
369 }
370 else
371 {
372 ww = m_width - 2*m_miniEdge;
373 hh = m_toolBarDetached ? wxPLACE_HOLDER
374 : m_frameToolBar->m_height;
375
376 client_area_y_offset += hh;
377 }
378
379 if (ww < 0)
380 ww = 0;
381 if (hh < 0)
382 hh = 0;
383 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
384 m_frameToolBar->m_widget,
385 xx, yy, ww, hh );
386 }
387 #endif // wxUSE_TOOLBAR
388
389 int client_x = client_area_x_offset + m_miniEdge;
390 int client_y = client_area_y_offset + m_miniEdge + m_miniTitle;
391 int client_w = m_width - client_area_x_offset - 2*m_miniEdge;
392 int client_h = m_height - client_area_y_offset- 2*m_miniEdge - m_miniTitle;
393 if (client_w < 0)
394 client_w = 0;
395 if (client_h < 0)
396 client_h = 0;
397 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
398 m_wxwindow,
399 client_x, client_y, client_w, client_h );
400 }
401 else
402 {
403 // If there is no m_mainWidget between m_widget and m_wxwindow there
404 // is no need to set the size or position of m_wxwindow.
405 }
406
407 #if wxUSE_STATUSBAR
408 if (m_frameStatusBar && m_frameStatusBar->IsShown() &&
409 !(m_fsIsShowing && (m_fsSaveFlag & wxFULLSCREEN_NOSTATUSBAR) != 0))
410 {
411 if (!GTK_WIDGET_VISIBLE(m_frameStatusBar->m_widget))
412 gtk_widget_show( m_frameStatusBar->m_widget );
413
414 int xx = 0 + m_miniEdge;
415 int yy = m_height - wxSTATUS_HEIGHT - m_miniEdge - client_area_y_offset;
416 int ww = m_width - 2*m_miniEdge;
417 if (ww < 0)
418 ww = 0;
419 int hh = wxSTATUS_HEIGHT;
420 m_frameStatusBar->m_x = xx;
421 m_frameStatusBar->m_y = yy;
422 m_frameStatusBar->m_width = ww;
423 m_frameStatusBar->m_height = hh;
424 gtk_pizza_set_size( GTK_PIZZA(m_wxwindow),
425 m_frameStatusBar->m_widget,
426 xx, yy, ww, hh );
427 }
428 else
429 {
430 if (m_frameStatusBar)
431 {
432 if (GTK_WIDGET_VISIBLE(m_frameStatusBar->m_widget))
433 gtk_widget_hide( m_frameStatusBar->m_widget );
434 }
435 }
436 #endif // wxUSE_STATUSBAR
437
438 m_sizeSet = true;
439
440 // send size event to frame
441 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
442 event.SetEventObject( this );
443 GetEventHandler()->ProcessEvent( event );
444
445 #if wxUSE_STATUSBAR
446 // send size event to status bar
447 if (m_frameStatusBar)
448 {
449 wxSizeEvent event2( wxSize(m_frameStatusBar->m_width,m_frameStatusBar->m_height), m_frameStatusBar->GetId() );
450 event2.SetEventObject( m_frameStatusBar );
451 m_frameStatusBar->GetEventHandler()->ProcessEvent( event2 );
452 }
453 #endif // wxUSE_STATUSBAR
454
455 m_resizing = false;
456 }
457
458 void wxFrame::OnInternalIdle()
459 {
460 wxFrameBase::OnInternalIdle();
461
462 #if wxUSE_MENUS_NATIVE
463 if (m_frameMenuBar) m_frameMenuBar->OnInternalIdle();
464 #endif // wxUSE_MENUS_NATIVE
465 #if wxUSE_TOOLBAR
466 if (m_frameToolBar) m_frameToolBar->OnInternalIdle();
467 #endif
468 #if wxUSE_STATUSBAR
469 if (m_frameStatusBar)
470 {
471 m_frameStatusBar->OnInternalIdle();
472
473 // There may be controls in the status bar that
474 // need to be updated
475 for ( wxWindowList::compatibility_iterator node = m_frameStatusBar->GetChildren().GetFirst();
476 node;
477 node = node->GetNext() )
478 {
479 wxWindow *child = node->GetData();
480 child->OnInternalIdle();
481 }
482 }
483 #endif
484 }
485
486 // ----------------------------------------------------------------------------
487 // menu/tool/status bar stuff
488 // ----------------------------------------------------------------------------
489
490 #if wxUSE_MENUS_NATIVE
491
492 void wxFrame::DetachMenuBar()
493 {
494 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
495 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
496
497 if ( m_frameMenuBar )
498 {
499 m_frameMenuBar->UnsetInvokingWindow( this );
500
501 if (m_frameMenuBar->GetWindowStyle() & wxMB_DOCKABLE)
502 {
503 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
504 (gpointer) gtk_menu_attached_callback,
505 this);
506
507 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
508 (gpointer) gtk_menu_detached_callback,
509 this);
510 }
511
512 gtk_widget_ref( m_frameMenuBar->m_widget );
513
514 gtk_container_remove( GTK_CONTAINER(m_mainWidget), m_frameMenuBar->m_widget );
515 }
516
517 wxFrameBase::DetachMenuBar();
518 }
519
520 void wxFrame::AttachMenuBar( wxMenuBar *menuBar )
521 {
522 wxFrameBase::AttachMenuBar(menuBar);
523
524 if (m_frameMenuBar)
525 {
526 m_frameMenuBar->SetInvokingWindow( this );
527
528 m_frameMenuBar->SetParent(this);
529 gtk_pizza_put( GTK_PIZZA(m_mainWidget),
530 m_frameMenuBar->m_widget,
531 m_frameMenuBar->m_x,
532 m_frameMenuBar->m_y,
533 m_frameMenuBar->m_width,
534 m_frameMenuBar->m_height );
535
536 if (menuBar->GetWindowStyle() & wxMB_DOCKABLE)
537 {
538 g_signal_connect (menuBar->m_widget, "child_attached",
539 G_CALLBACK (gtk_menu_attached_callback),
540 this);
541 g_signal_connect (menuBar->m_widget, "child_detached",
542 G_CALLBACK (gtk_menu_detached_callback),
543 this);
544 }
545
546 gtk_widget_show( m_frameMenuBar->m_widget );
547
548 UpdateMenuBarSize();
549 }
550 else
551 {
552 m_menuBarHeight = 2;
553 GtkUpdateSize(); // resize window in OnInternalIdle
554 }
555 }
556
557 void wxFrame::UpdateMenuBarSize()
558 {
559 m_menuBarHeight = 2;
560
561 // this is called after Remove with a NULL m_frameMenuBar
562 if ( m_frameMenuBar )
563 {
564 GtkRequisition req;
565 gtk_widget_ensure_style(m_frameMenuBar->m_widget);
566 // have to call class method directly because
567 // "size_request" signal is overridden by wx
568 GTK_WIDGET_GET_CLASS(m_frameMenuBar->m_widget)->size_request(
569 m_frameMenuBar->m_widget, &req);
570
571 m_menuBarHeight = req.height;
572 }
573
574 // resize window in OnInternalIdle
575 GtkUpdateSize();
576 }
577
578 #endif // wxUSE_MENUS_NATIVE
579
580 #if wxUSE_TOOLBAR
581
582 wxToolBar* wxFrame::CreateToolBar( long style, wxWindowID id, const wxString& name )
583 {
584 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
585
586 m_insertInClientArea = false;
587
588 m_frameToolBar = wxFrameBase::CreateToolBar( style, id, name );
589
590 m_insertInClientArea = true;
591
592 GtkUpdateSize();
593
594 return m_frameToolBar;
595 }
596
597 void wxFrame::SetToolBar(wxToolBar *toolbar)
598 {
599 bool hadTbar = m_frameToolBar != NULL;
600
601 wxFrameBase::SetToolBar(toolbar);
602
603 if ( m_frameToolBar )
604 {
605 // insert into toolbar area if not already there
606 if ((m_frameToolBar->m_widget->parent) &&
607 (m_frameToolBar->m_widget->parent != m_mainWidget))
608 {
609 GetChildren().DeleteObject( m_frameToolBar );
610
611 gtk_widget_reparent( m_frameToolBar->m_widget, m_mainWidget );
612 GtkUpdateSize();
613 }
614 }
615 else // toolbar unset
616 {
617 // still need to update size if it had been there before
618 if ( hadTbar )
619 {
620 GtkUpdateSize();
621 }
622 }
623 }
624
625 #endif // wxUSE_TOOLBAR
626
627 #if wxUSE_STATUSBAR
628
629 wxStatusBar* wxFrame::CreateStatusBar(int number,
630 long style,
631 wxWindowID id,
632 const wxString& name)
633 {
634 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
635
636 // because it will change when toolbar is added
637 GtkUpdateSize();
638
639 return wxFrameBase::CreateStatusBar( number, style, id, name );
640 }
641
642 void wxFrame::SetStatusBar(wxStatusBar *statbar)
643 {
644 bool hadStatBar = m_frameStatusBar != NULL;
645
646 wxFrameBase::SetStatusBar(statbar);
647
648 if (hadStatBar && !m_frameStatusBar)
649 GtkUpdateSize();
650 }
651
652 void wxFrame::PositionStatusBar()
653 {
654 if ( !m_frameStatusBar )
655 return;
656
657 GtkUpdateSize();
658 }
659 #endif // wxUSE_STATUSBAR