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