simplify DoSetClientSize
[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 wxTopLevelWindow::DoGetClientSize( width, height );
213
214 if (height)
215 {
216 #if wxUSE_MENUS_NATIVE
217 // menu bar
218 if (m_frameMenuBar && !(m_fsIsShowing && (m_fsSaveFlag & wxFULLSCREEN_NOMENUBAR) != 0))
219 {
220 if (!m_menuBarDetached)
221 (*height) -= m_menuBarHeight;
222 else
223 (*height) -= wxPLACE_HOLDER;
224 }
225 #endif // wxUSE_MENUS_NATIVE
226
227 #if wxUSE_STATUSBAR
228 // status bar
229 if (m_frameStatusBar && m_frameStatusBar->IsShown() &&
230 !(m_fsIsShowing && (m_fsSaveFlag & wxFULLSCREEN_NOSTATUSBAR) != 0))
231 (*height) -= wxSTATUS_HEIGHT;
232 #endif // wxUSE_STATUSBAR
233 }
234
235 #if wxUSE_TOOLBAR
236 // tool bar
237 if (m_frameToolBar && m_frameToolBar->IsShown())
238 {
239 if (m_toolBarDetached)
240 {
241 if (height != NULL)
242 *height -= wxPLACE_HOLDER;
243 }
244 else
245 {
246 int x, y;
247 m_frameToolBar->GetSize( &x, &y );
248 if ( m_frameToolBar->IsVertical() )
249 {
250 if (width != NULL)
251 *width -= x;
252 }
253 else
254 {
255 if (height != NULL)
256 *height -= y;
257 }
258 }
259 }
260 #endif // wxUSE_TOOLBAR
261
262 if (width != NULL && *width < 0)
263 *width = 0;
264 if (height != NULL && *height < 0)
265 *height = 0;
266 }
267
268 void wxFrame::GtkOnSize()
269 {
270 // avoid recursions
271 if (m_resizing) return;
272 m_resizing = true;
273
274 // this shouldn't happen: wxFrame, wxMDIParentFrame and wxMDIChildFrame have m_wxwindow
275 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
276
277 // space occupied by m_frameToolBar and m_frameMenuBar
278 int client_area_x_offset = 0,
279 client_area_y_offset = 0;
280
281 /* wxMDIChildFrame derives from wxFrame but it _is_ a wxWindow as it uses
282 wxWindow::Create to create it's GTK equivalent. m_mainWidget is only
283 set in wxFrame::Create so it is used to check what kind of frame we
284 have here. if m_mainWidget is NULL it is a wxMDIChildFrame and so we
285 skip the part which handles m_frameMenuBar, m_frameToolBar and (most
286 importantly) m_mainWidget */
287
288 ConstrainSize();
289
290 if (m_mainWidget)
291 {
292 // TODO
293 // Rewrite this terrible code to using GtkVBox
294
295 // m_mainWidget holds the menubar, the toolbar and the client
296 // area, which is represented by m_wxwindow.
297
298 #if wxUSE_MENUS_NATIVE
299 if (m_frameMenuBar && !(m_fsIsShowing && (m_fsSaveFlag & wxFULLSCREEN_NOMENUBAR) != 0))
300 {
301 if (!GTK_WIDGET_VISIBLE(m_frameMenuBar->m_widget))
302 gtk_widget_show( m_frameMenuBar->m_widget );
303 int xx = m_miniEdge;
304 int yy = m_miniEdge + m_miniTitle;
305 int ww = m_width - 2*m_miniEdge;
306 if (ww < 0)
307 ww = 0;
308 int hh = m_menuBarHeight;
309 if (m_menuBarDetached) hh = wxPLACE_HOLDER;
310 m_frameMenuBar->m_x = xx;
311 m_frameMenuBar->m_y = yy;
312 m_frameMenuBar->m_width = ww;
313 m_frameMenuBar->m_height = hh;
314 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
315 m_frameMenuBar->m_widget,
316 xx, yy, ww, hh );
317 client_area_y_offset += hh;
318 }
319 else
320 {
321 if (m_frameMenuBar)
322 {
323 if (GTK_WIDGET_VISIBLE(m_frameMenuBar->m_widget))
324 gtk_widget_hide( m_frameMenuBar->m_widget );
325 }
326 }
327 #endif // wxUSE_MENUS_NATIVE
328
329 #if wxUSE_TOOLBAR
330 if ((m_frameToolBar) && m_frameToolBar->IsShown() &&
331 (m_frameToolBar->m_widget->parent == m_mainWidget))
332 {
333 int xx = m_miniEdge;
334 int yy = m_miniEdge + m_miniTitle;
335 #if wxUSE_MENUS_NATIVE
336 if (m_frameMenuBar)
337 {
338 if (!m_menuBarDetached)
339 yy += m_menuBarHeight;
340 else
341 yy += wxPLACE_HOLDER;
342 }
343 #endif // wxUSE_MENUS_NATIVE
344
345 m_frameToolBar->m_x = xx;
346 m_frameToolBar->m_y = yy;
347
348 // don't change the toolbar's reported height/width
349 int ww, hh;
350 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL )
351 {
352 ww = m_toolBarDetached ? wxPLACE_HOLDER
353 : m_frameToolBar->m_width;
354 hh = m_height - 2*m_miniEdge;
355
356 client_area_x_offset += ww;
357 }
358 else if( m_frameToolBar->HasFlag(wxTB_RIGHT) )
359 {
360 yy += 2;
361 ww = m_toolBarDetached ? wxPLACE_HOLDER
362 : m_frameToolBar->m_width;
363 xx = GetClientSize().x - 1;
364 hh = m_height - 2*m_miniEdge;
365 if( hh < 0 )
366 hh = 0;
367
368 }
369 else if( m_frameToolBar->GetWindowStyle() & wxTB_BOTTOM )
370 {
371 xx = m_miniEdge;
372 yy = GetClientSize().y;
373 #if wxUSE_MENUS_NATIVE
374 yy += m_menuBarHeight;
375 #endif // wxUSE_MENU_NATIVE
376 m_frameToolBar->m_x = xx;
377 m_frameToolBar->m_y = yy;
378 ww = m_width - 2*m_miniEdge;
379 hh = m_toolBarDetached ? wxPLACE_HOLDER
380 : m_frameToolBar->m_height;
381 }
382 else
383 {
384 ww = m_width - 2*m_miniEdge;
385 hh = m_toolBarDetached ? wxPLACE_HOLDER
386 : m_frameToolBar->m_height;
387
388 client_area_y_offset += hh;
389 }
390
391 if (ww < 0)
392 ww = 0;
393 if (hh < 0)
394 hh = 0;
395 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
396 m_frameToolBar->m_widget,
397 xx, yy, ww, hh );
398 }
399 #endif // wxUSE_TOOLBAR
400
401 int client_x = client_area_x_offset + m_miniEdge;
402 int client_y = client_area_y_offset + m_miniEdge + m_miniTitle;
403 int client_w = m_width - client_area_x_offset - 2*m_miniEdge;
404 int client_h = m_height - client_area_y_offset- 2*m_miniEdge - m_miniTitle;
405 if (client_w < 0)
406 client_w = 0;
407 if (client_h < 0)
408 client_h = 0;
409 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
410 m_wxwindow,
411 client_x, client_y, client_w, client_h );
412 }
413 else
414 {
415 // If there is no m_mainWidget between m_widget and m_wxwindow there
416 // is no need to set the size or position of m_wxwindow.
417 }
418
419 #if wxUSE_STATUSBAR
420 if (m_frameStatusBar && m_frameStatusBar->IsShown() &&
421 !(m_fsIsShowing && (m_fsSaveFlag & wxFULLSCREEN_NOSTATUSBAR) != 0))
422 {
423 if (!GTK_WIDGET_VISIBLE(m_frameStatusBar->m_widget))
424 gtk_widget_show( m_frameStatusBar->m_widget );
425
426 int xx = 0 + m_miniEdge;
427 int yy = m_height - wxSTATUS_HEIGHT - m_miniEdge - client_area_y_offset;
428 int ww = m_width - 2*m_miniEdge;
429 if (ww < 0)
430 ww = 0;
431 int hh = wxSTATUS_HEIGHT;
432 m_frameStatusBar->m_x = xx;
433 m_frameStatusBar->m_y = yy;
434 m_frameStatusBar->m_width = ww;
435 m_frameStatusBar->m_height = hh;
436 gtk_pizza_set_size( GTK_PIZZA(m_wxwindow),
437 m_frameStatusBar->m_widget,
438 xx, yy, ww, hh );
439 }
440 else
441 {
442 if (m_frameStatusBar)
443 {
444 if (GTK_WIDGET_VISIBLE(m_frameStatusBar->m_widget))
445 gtk_widget_hide( m_frameStatusBar->m_widget );
446 }
447 }
448 #endif // wxUSE_STATUSBAR
449
450 m_sizeSet = true;
451
452 // send size event to frame
453 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
454 event.SetEventObject( this );
455 GetEventHandler()->ProcessEvent( event );
456
457 #if wxUSE_STATUSBAR
458 // send size event to status bar
459 if (m_frameStatusBar)
460 {
461 wxSizeEvent event2( wxSize(m_frameStatusBar->m_width,m_frameStatusBar->m_height), m_frameStatusBar->GetId() );
462 event2.SetEventObject( m_frameStatusBar );
463 m_frameStatusBar->GetEventHandler()->ProcessEvent( event2 );
464 }
465 #endif // wxUSE_STATUSBAR
466
467 m_resizing = false;
468 }
469
470 void wxFrame::OnInternalIdle()
471 {
472 wxFrameBase::OnInternalIdle();
473
474 #if wxUSE_MENUS_NATIVE
475 if (m_frameMenuBar) m_frameMenuBar->OnInternalIdle();
476 #endif // wxUSE_MENUS_NATIVE
477 #if wxUSE_TOOLBAR
478 if (m_frameToolBar) m_frameToolBar->OnInternalIdle();
479 #endif
480 #if wxUSE_STATUSBAR
481 if (m_frameStatusBar)
482 {
483 m_frameStatusBar->OnInternalIdle();
484
485 // There may be controls in the status bar that
486 // need to be updated
487 for ( wxWindowList::compatibility_iterator node = m_frameStatusBar->GetChildren().GetFirst();
488 node;
489 node = node->GetNext() )
490 {
491 wxWindow *child = node->GetData();
492 child->OnInternalIdle();
493 }
494 }
495 #endif
496 }
497
498 // ----------------------------------------------------------------------------
499 // menu/tool/status bar stuff
500 // ----------------------------------------------------------------------------
501
502 #if wxUSE_MENUS_NATIVE
503
504 void wxFrame::DetachMenuBar()
505 {
506 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
507 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
508
509 if ( m_frameMenuBar )
510 {
511 m_frameMenuBar->UnsetInvokingWindow( this );
512
513 if (m_frameMenuBar->GetWindowStyle() & wxMB_DOCKABLE)
514 {
515 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
516 (gpointer) gtk_menu_attached_callback,
517 this);
518
519 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
520 (gpointer) gtk_menu_detached_callback,
521 this);
522 }
523
524 gtk_widget_ref( m_frameMenuBar->m_widget );
525
526 gtk_container_remove( GTK_CONTAINER(m_mainWidget), m_frameMenuBar->m_widget );
527 }
528
529 wxFrameBase::DetachMenuBar();
530 }
531
532 void wxFrame::AttachMenuBar( wxMenuBar *menuBar )
533 {
534 wxFrameBase::AttachMenuBar(menuBar);
535
536 if (m_frameMenuBar)
537 {
538 m_frameMenuBar->SetInvokingWindow( this );
539
540 m_frameMenuBar->SetParent(this);
541 gtk_pizza_put( GTK_PIZZA(m_mainWidget),
542 m_frameMenuBar->m_widget,
543 m_frameMenuBar->m_x,
544 m_frameMenuBar->m_y,
545 m_frameMenuBar->m_width,
546 m_frameMenuBar->m_height );
547
548 if (menuBar->GetWindowStyle() & wxMB_DOCKABLE)
549 {
550 g_signal_connect (menuBar->m_widget, "child_attached",
551 G_CALLBACK (gtk_menu_attached_callback),
552 this);
553 g_signal_connect (menuBar->m_widget, "child_detached",
554 G_CALLBACK (gtk_menu_detached_callback),
555 this);
556 }
557
558 gtk_widget_show( m_frameMenuBar->m_widget );
559
560 UpdateMenuBarSize();
561 }
562 else
563 {
564 m_menuBarHeight = 2;
565 GtkUpdateSize(); // resize window in OnInternalIdle
566 }
567 }
568
569 void wxFrame::UpdateMenuBarSize()
570 {
571 m_menuBarHeight = 2;
572
573 // this is called after Remove with a NULL m_frameMenuBar
574 if ( m_frameMenuBar )
575 {
576 GtkRequisition req;
577 gtk_widget_ensure_style(m_frameMenuBar->m_widget);
578 // have to call class method directly because
579 // "size_request" signal is overridden by wx
580 GTK_WIDGET_GET_CLASS(m_frameMenuBar->m_widget)->size_request(
581 m_frameMenuBar->m_widget, &req);
582
583 m_menuBarHeight = req.height;
584 }
585
586 // resize window in OnInternalIdle
587 GtkUpdateSize();
588 }
589
590 #endif // wxUSE_MENUS_NATIVE
591
592 #if wxUSE_TOOLBAR
593
594 wxToolBar* wxFrame::CreateToolBar( long style, wxWindowID id, const wxString& name )
595 {
596 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
597
598 m_insertInClientArea = false;
599
600 m_frameToolBar = wxFrameBase::CreateToolBar( style, id, name );
601
602 m_insertInClientArea = true;
603
604 GtkUpdateSize();
605
606 return m_frameToolBar;
607 }
608
609 void wxFrame::SetToolBar(wxToolBar *toolbar)
610 {
611 bool hadTbar = m_frameToolBar != NULL;
612
613 wxFrameBase::SetToolBar(toolbar);
614
615 if ( m_frameToolBar )
616 {
617 // insert into toolbar area if not already there
618 if ((m_frameToolBar->m_widget->parent) &&
619 (m_frameToolBar->m_widget->parent != m_mainWidget))
620 {
621 GetChildren().DeleteObject( m_frameToolBar );
622
623 gtk_widget_reparent( m_frameToolBar->m_widget, m_mainWidget );
624 GtkUpdateSize();
625 }
626 }
627 else // toolbar unset
628 {
629 // still need to update size if it had been there before
630 if ( hadTbar )
631 {
632 GtkUpdateSize();
633 }
634 }
635 }
636
637 #endif // wxUSE_TOOLBAR
638
639 #if wxUSE_STATUSBAR
640
641 wxStatusBar* wxFrame::CreateStatusBar(int number,
642 long style,
643 wxWindowID id,
644 const wxString& name)
645 {
646 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
647
648 // because it will change when toolbar is added
649 GtkUpdateSize();
650
651 return wxFrameBase::CreateStatusBar( number, style, id, name );
652 }
653
654 void wxFrame::SetStatusBar(wxStatusBar *statbar)
655 {
656 bool hadStatBar = m_frameStatusBar != NULL;
657
658 wxFrameBase::SetStatusBar(statbar);
659
660 if (hadStatBar && !m_frameStatusBar)
661 GtkUpdateSize();
662 }
663
664 void wxFrame::PositionStatusBar()
665 {
666 if ( !m_frameStatusBar )
667 return;
668
669 GtkUpdateSize();
670 }
671 #endif // wxUSE_STATUSBAR