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