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