remove a few unnecessary queue_resize/queue_draw calls
[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()
330 {
331 // avoid recursions
332 if (m_resizing) return;
333 m_resizing = true;
334
335 // this shouldn't happen: wxFrame, wxMDIParentFrame and wxMDIChildFrame have m_wxwindow
336 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
337
338 // space occupied by m_frameToolBar and m_frameMenuBar
339 int client_area_x_offset = 0,
340 client_area_y_offset = 0;
341
342 /* wxMDIChildFrame derives from wxFrame but it _is_ a wxWindow as it uses
343 wxWindow::Create to create it's GTK equivalent. m_mainWidget is only
344 set in wxFrame::Create so it is used to check what kind of frame we
345 have here. if m_mainWidget is NULL it is a wxMDIChildFrame and so we
346 skip the part which handles m_frameMenuBar, m_frameToolBar and (most
347 importantly) m_mainWidget */
348
349 int minWidth = GetMinWidth(),
350 minHeight = GetMinHeight(),
351 maxWidth = GetMaxWidth(),
352 maxHeight = GetMaxHeight();
353
354 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
355 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
356 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
357 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
358
359 if (m_mainWidget)
360 {
361 // set size hints
362 gint flag = 0; // GDK_HINT_POS;
363 if ((minWidth != -1) || (minHeight != -1)) flag |= GDK_HINT_MIN_SIZE;
364 if ((maxWidth != -1) || (maxHeight != -1)) flag |= GDK_HINT_MAX_SIZE;
365 GdkGeometry geom;
366 geom.min_width = minWidth;
367 geom.min_height = minHeight;
368 geom.max_width = maxWidth;
369 geom.max_height = maxHeight;
370 gtk_window_set_geometry_hints( GTK_WINDOW(m_widget),
371 (GtkWidget*) NULL,
372 &geom,
373 (GdkWindowHints) flag );
374
375 // I revert back to wxGTK's original behaviour. m_mainWidget holds
376 // the menubar, the toolbar and the client area, which is represented
377 // by m_wxwindow.
378 // This hurts in the eye, but I don't want to call SetSize()
379 // because I don't want to call any non-native functions here.
380
381 #if wxUSE_MENUS_NATIVE
382 if (m_frameMenuBar)
383 {
384 int xx = m_miniEdge;
385 int yy = m_miniEdge + m_miniTitle;
386 int ww = m_width - 2*m_miniEdge;
387 int hh = m_menuBarHeight;
388 if (m_menuBarDetached) hh = wxPLACE_HOLDER;
389 m_frameMenuBar->m_x = xx;
390 m_frameMenuBar->m_y = yy;
391 m_frameMenuBar->m_width = ww;
392 m_frameMenuBar->m_height = hh;
393 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
394 m_frameMenuBar->m_widget,
395 xx, yy, ww, hh );
396 client_area_y_offset += hh;
397 }
398 #endif // wxUSE_MENUS_NATIVE
399
400 #if wxUSE_TOOLBAR
401 if ((m_frameToolBar) && m_frameToolBar->IsShown() &&
402 (m_frameToolBar->m_widget->parent == m_mainWidget))
403 {
404 int xx = m_miniEdge;
405 int yy = m_miniEdge + m_miniTitle;
406 #if wxUSE_MENUS_NATIVE
407 if (m_frameMenuBar)
408 {
409 if (!m_menuBarDetached)
410 yy += m_menuBarHeight;
411 else
412 yy += wxPLACE_HOLDER;
413 }
414 #endif // wxUSE_MENUS_NATIVE
415
416 m_frameToolBar->m_x = xx;
417 m_frameToolBar->m_y = yy;
418
419 // don't change the toolbar's reported height/width
420 int ww, hh;
421 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL )
422 {
423 ww = m_toolBarDetached ? wxPLACE_HOLDER
424 : m_frameToolBar->m_width;
425 hh = m_height - 2*m_miniEdge;
426
427 client_area_x_offset += ww;
428 }
429 else
430 {
431 ww = m_width - 2*m_miniEdge;
432 hh = m_toolBarDetached ? wxPLACE_HOLDER
433 : m_frameToolBar->m_height;
434
435 client_area_y_offset += hh;
436 }
437
438 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
439 m_frameToolBar->m_widget,
440 xx, yy, ww, hh );
441 }
442 #endif // wxUSE_TOOLBAR
443
444 int client_x = client_area_x_offset + m_miniEdge;
445 int client_y = client_area_y_offset + m_miniEdge + m_miniTitle;
446 int client_w = m_width - client_area_x_offset - 2*m_miniEdge;
447 int client_h = m_height - client_area_y_offset- 2*m_miniEdge - m_miniTitle;
448 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
449 m_wxwindow,
450 client_x, client_y, client_w, client_h );
451 }
452 else
453 {
454 // If there is no m_mainWidget between m_widget and m_wxwindow there
455 // is no need to set the size or position of m_wxwindow.
456 }
457
458 #if wxUSE_STATUSBAR
459 if (m_frameStatusBar && m_frameStatusBar->IsShown())
460 {
461 int xx = 0 + m_miniEdge;
462 int yy = m_height - wxSTATUS_HEIGHT - m_miniEdge - client_area_y_offset;
463 int ww = m_width - 2*m_miniEdge;
464 int hh = wxSTATUS_HEIGHT;
465 m_frameStatusBar->m_x = xx;
466 m_frameStatusBar->m_y = yy;
467 m_frameStatusBar->m_width = ww;
468 m_frameStatusBar->m_height = hh;
469 gtk_pizza_set_size( GTK_PIZZA(m_wxwindow),
470 m_frameStatusBar->m_widget,
471 xx, yy, ww, hh );
472 }
473 #endif // wxUSE_STATUSBAR
474
475 m_sizeSet = true;
476
477 // send size event to frame
478 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
479 event.SetEventObject( this );
480 GetEventHandler()->ProcessEvent( event );
481
482 #if wxUSE_STATUSBAR
483 // send size event to status bar
484 if (m_frameStatusBar)
485 {
486 wxSizeEvent event2( wxSize(m_frameStatusBar->m_width,m_frameStatusBar->m_height), m_frameStatusBar->GetId() );
487 event2.SetEventObject( m_frameStatusBar );
488 m_frameStatusBar->GetEventHandler()->ProcessEvent( event2 );
489 }
490 #endif // wxUSE_STATUSBAR
491
492 m_resizing = false;
493 }
494
495 void wxFrame::OnInternalIdle()
496 {
497 wxFrameBase::OnInternalIdle();
498
499 #if wxUSE_MENUS_NATIVE
500 if (m_frameMenuBar) m_frameMenuBar->OnInternalIdle();
501 #endif // wxUSE_MENUS_NATIVE
502 #if wxUSE_TOOLBAR
503 if (m_frameToolBar) m_frameToolBar->OnInternalIdle();
504 #endif
505 #if wxUSE_STATUSBAR
506 if (m_frameStatusBar)
507 {
508 m_frameStatusBar->OnInternalIdle();
509
510 // There may be controls in the status bar that
511 // need to be updated
512 for ( wxWindowList::compatibility_iterator node = m_frameStatusBar->GetChildren().GetFirst();
513 node;
514 node = node->GetNext() )
515 {
516 wxWindow *child = node->GetData();
517 child->OnInternalIdle();
518 }
519 }
520 #endif
521 }
522
523 // ----------------------------------------------------------------------------
524 // menu/tool/status bar stuff
525 // ----------------------------------------------------------------------------
526
527 #if wxUSE_MENUS_NATIVE
528
529 void wxFrame::DetachMenuBar()
530 {
531 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
532 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
533
534 if ( m_frameMenuBar )
535 {
536 m_frameMenuBar->UnsetInvokingWindow( this );
537
538 if (m_frameMenuBar->GetWindowStyle() & wxMB_DOCKABLE)
539 {
540 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
541 (gpointer) gtk_menu_attached_callback,
542 this);
543
544 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
545 (gpointer) gtk_menu_detached_callback,
546 this);
547 }
548
549 gtk_widget_ref( m_frameMenuBar->m_widget );
550
551 gtk_container_remove( GTK_CONTAINER(m_mainWidget), m_frameMenuBar->m_widget );
552 }
553
554 wxFrameBase::DetachMenuBar();
555 }
556
557 void wxFrame::AttachMenuBar( wxMenuBar *menuBar )
558 {
559 wxFrameBase::AttachMenuBar(menuBar);
560
561 if (m_frameMenuBar)
562 {
563 m_frameMenuBar->SetInvokingWindow( this );
564
565 m_frameMenuBar->SetParent(this);
566 gtk_pizza_put( GTK_PIZZA(m_mainWidget),
567 m_frameMenuBar->m_widget,
568 m_frameMenuBar->m_x,
569 m_frameMenuBar->m_y,
570 m_frameMenuBar->m_width,
571 m_frameMenuBar->m_height );
572
573 if (menuBar->GetWindowStyle() & wxMB_DOCKABLE)
574 {
575 g_signal_connect (menuBar->m_widget, "child_attached",
576 G_CALLBACK (gtk_menu_attached_callback),
577 this);
578 g_signal_connect (menuBar->m_widget, "child_detached",
579 G_CALLBACK (gtk_menu_detached_callback),
580 this);
581 }
582
583 gtk_widget_show( m_frameMenuBar->m_widget );
584
585 UpdateMenuBarSize();
586 }
587 else
588 {
589 m_menuBarHeight = 2;
590 GtkUpdateSize(); // resize window in OnInternalIdle
591 }
592 }
593
594 void wxFrame::UpdateMenuBarSize()
595 {
596 GtkRequisition req;
597
598 req.width = 2;
599 req.height = 2;
600
601 // this is called after Remove with a NULL m_frameMenuBar
602 if ( m_frameMenuBar )
603 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_frameMenuBar->m_widget) )->size_request )
604 (m_frameMenuBar->m_widget, &req );
605
606 m_menuBarHeight = req.height;
607
608 // resize window in OnInternalIdle
609
610 GtkUpdateSize();
611 }
612
613 #endif // wxUSE_MENUS_NATIVE
614
615 #if wxUSE_TOOLBAR
616
617 wxToolBar* wxFrame::CreateToolBar( long style, wxWindowID id, const wxString& name )
618 {
619 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
620
621 m_insertInClientArea = false;
622
623 m_frameToolBar = wxFrameBase::CreateToolBar( style, id, name );
624
625 m_insertInClientArea = true;
626
627 GtkUpdateSize();
628
629 return m_frameToolBar;
630 }
631
632 void wxFrame::SetToolBar(wxToolBar *toolbar)
633 {
634 bool hadTbar = m_frameToolBar != NULL;
635
636 wxFrameBase::SetToolBar(toolbar);
637
638 if ( m_frameToolBar )
639 {
640 // insert into toolbar area if not already there
641 if ((m_frameToolBar->m_widget->parent) &&
642 (m_frameToolBar->m_widget->parent != m_mainWidget))
643 {
644 GetChildren().DeleteObject( m_frameToolBar );
645
646 gtk_widget_reparent( m_frameToolBar->m_widget, m_mainWidget );
647 GtkUpdateSize();
648 }
649 }
650 else // toolbar unset
651 {
652 // still need to update size if it had been there before
653 if ( hadTbar )
654 {
655 GtkUpdateSize();
656 }
657 }
658 }
659
660 #endif // wxUSE_TOOLBAR
661
662 #if wxUSE_STATUSBAR
663
664 wxStatusBar* wxFrame::CreateStatusBar(int number,
665 long style,
666 wxWindowID id,
667 const wxString& name)
668 {
669 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
670
671 // because it will change when toolbar is added
672 GtkUpdateSize();
673
674 return wxFrameBase::CreateStatusBar( number, style, id, name );
675 }
676
677 void wxFrame::SetStatusBar(wxStatusBar *statbar)
678 {
679 bool hadStatBar = m_frameStatusBar != NULL;
680
681 wxFrameBase::SetStatusBar(statbar);
682
683 if (hadStatBar && !m_frameStatusBar)
684 GtkUpdateSize();
685 }
686
687 void wxFrame::PositionStatusBar()
688 {
689 if ( !m_frameStatusBar )
690 return;
691
692 GtkUpdateSize();
693 }
694 #endif // wxUSE_STATUSBAR