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