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