]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/frame.cpp
don't use negative windows sizes
[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 if (ww < 0)
388 ww = 0;
389 int hh = m_menuBarHeight;
390 if (m_menuBarDetached) hh = wxPLACE_HOLDER;
391 m_frameMenuBar->m_x = xx;
392 m_frameMenuBar->m_y = yy;
393 m_frameMenuBar->m_width = ww;
394 m_frameMenuBar->m_height = hh;
395 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
396 m_frameMenuBar->m_widget,
397 xx, yy, ww, hh );
398 client_area_y_offset += hh;
399 }
400 #endif // wxUSE_MENUS_NATIVE
401
402 #if wxUSE_TOOLBAR
403 if ((m_frameToolBar) && m_frameToolBar->IsShown() &&
404 (m_frameToolBar->m_widget->parent == m_mainWidget))
405 {
406 int xx = m_miniEdge;
407 int yy = m_miniEdge + m_miniTitle;
408 #if wxUSE_MENUS_NATIVE
409 if (m_frameMenuBar)
410 {
411 if (!m_menuBarDetached)
412 yy += m_menuBarHeight;
413 else
414 yy += wxPLACE_HOLDER;
415 }
416 #endif // wxUSE_MENUS_NATIVE
417
418 m_frameToolBar->m_x = xx;
419 m_frameToolBar->m_y = yy;
420
421 // don't change the toolbar's reported height/width
422 int ww, hh;
423 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL )
424 {
425 ww = m_toolBarDetached ? wxPLACE_HOLDER
426 : m_frameToolBar->m_width;
427 hh = m_height - 2*m_miniEdge;
428 if (hh < 0)
429 hh = 0;
430
431 client_area_x_offset += ww;
432 }
433 else
434 {
435 ww = m_width - 2*m_miniEdge;
436 hh = m_toolBarDetached ? wxPLACE_HOLDER
437 : m_frameToolBar->m_height;
438
439 client_area_y_offset += hh;
440 }
441
442 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
443 m_frameToolBar->m_widget,
444 xx, yy, ww, hh );
445 }
446 #endif // wxUSE_TOOLBAR
447
448 int client_x = client_area_x_offset + m_miniEdge;
449 int client_y = client_area_y_offset + m_miniEdge + m_miniTitle;
450 int client_w = m_width - client_area_x_offset - 2*m_miniEdge;
451 int client_h = m_height - client_area_y_offset- 2*m_miniEdge - m_miniTitle;
452 if (client_w < 0)
453 client_w = 0;
454 if (client_h < 0)
455 client_h = 0;
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 if (ww < 0)
473 ww = 0;
474 int hh = wxSTATUS_HEIGHT;
475 m_frameStatusBar->m_x = xx;
476 m_frameStatusBar->m_y = yy;
477 m_frameStatusBar->m_width = ww;
478 m_frameStatusBar->m_height = hh;
479 gtk_pizza_set_size( GTK_PIZZA(m_wxwindow),
480 m_frameStatusBar->m_widget,
481 xx, yy, ww, hh );
482 }
483 #endif // wxUSE_STATUSBAR
484
485 m_sizeSet = true;
486
487 // send size event to frame
488 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
489 event.SetEventObject( this );
490 GetEventHandler()->ProcessEvent( event );
491
492 #if wxUSE_STATUSBAR
493 // send size event to status bar
494 if (m_frameStatusBar)
495 {
496 wxSizeEvent event2( wxSize(m_frameStatusBar->m_width,m_frameStatusBar->m_height), m_frameStatusBar->GetId() );
497 event2.SetEventObject( m_frameStatusBar );
498 m_frameStatusBar->GetEventHandler()->ProcessEvent( event2 );
499 }
500 #endif // wxUSE_STATUSBAR
501
502 m_resizing = false;
503 }
504
505 void wxFrame::OnInternalIdle()
506 {
507 wxFrameBase::OnInternalIdle();
508
509 #if wxUSE_MENUS_NATIVE
510 if (m_frameMenuBar) m_frameMenuBar->OnInternalIdle();
511 #endif // wxUSE_MENUS_NATIVE
512 #if wxUSE_TOOLBAR
513 if (m_frameToolBar) m_frameToolBar->OnInternalIdle();
514 #endif
515 #if wxUSE_STATUSBAR
516 if (m_frameStatusBar)
517 {
518 m_frameStatusBar->OnInternalIdle();
519
520 // There may be controls in the status bar that
521 // need to be updated
522 for ( wxWindowList::compatibility_iterator node = m_frameStatusBar->GetChildren().GetFirst();
523 node;
524 node = node->GetNext() )
525 {
526 wxWindow *child = node->GetData();
527 child->OnInternalIdle();
528 }
529 }
530 #endif
531 }
532
533 // ----------------------------------------------------------------------------
534 // menu/tool/status bar stuff
535 // ----------------------------------------------------------------------------
536
537 #if wxUSE_MENUS_NATIVE
538
539 void wxFrame::DetachMenuBar()
540 {
541 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
542 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") );
543
544 if ( m_frameMenuBar )
545 {
546 m_frameMenuBar->UnsetInvokingWindow( this );
547
548 if (m_frameMenuBar->GetWindowStyle() & wxMB_DOCKABLE)
549 {
550 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
551 (gpointer) gtk_menu_attached_callback,
552 this);
553
554 g_signal_handlers_disconnect_by_func (m_frameMenuBar->m_widget,
555 (gpointer) gtk_menu_detached_callback,
556 this);
557 }
558
559 gtk_widget_ref( m_frameMenuBar->m_widget );
560
561 gtk_container_remove( GTK_CONTAINER(m_mainWidget), m_frameMenuBar->m_widget );
562 }
563
564 wxFrameBase::DetachMenuBar();
565 }
566
567 void wxFrame::AttachMenuBar( wxMenuBar *menuBar )
568 {
569 wxFrameBase::AttachMenuBar(menuBar);
570
571 if (m_frameMenuBar)
572 {
573 m_frameMenuBar->SetInvokingWindow( this );
574
575 m_frameMenuBar->SetParent(this);
576 gtk_pizza_put( GTK_PIZZA(m_mainWidget),
577 m_frameMenuBar->m_widget,
578 m_frameMenuBar->m_x,
579 m_frameMenuBar->m_y,
580 m_frameMenuBar->m_width,
581 m_frameMenuBar->m_height );
582
583 if (menuBar->GetWindowStyle() & wxMB_DOCKABLE)
584 {
585 g_signal_connect (menuBar->m_widget, "child_attached",
586 G_CALLBACK (gtk_menu_attached_callback),
587 this);
588 g_signal_connect (menuBar->m_widget, "child_detached",
589 G_CALLBACK (gtk_menu_detached_callback),
590 this);
591 }
592
593 gtk_widget_show( m_frameMenuBar->m_widget );
594
595 UpdateMenuBarSize();
596 }
597 else
598 {
599 m_menuBarHeight = 2;
600 GtkUpdateSize(); // resize window in OnInternalIdle
601 }
602 }
603
604 void wxFrame::UpdateMenuBarSize()
605 {
606 GtkRequisition req;
607
608 req.width = 2;
609 req.height = 2;
610
611 // this is called after Remove with a NULL m_frameMenuBar
612 if ( m_frameMenuBar )
613 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_frameMenuBar->m_widget) )->size_request )
614 (m_frameMenuBar->m_widget, &req );
615
616 m_menuBarHeight = req.height;
617
618 // resize window in OnInternalIdle
619
620 GtkUpdateSize();
621 }
622
623 #endif // wxUSE_MENUS_NATIVE
624
625 #if wxUSE_TOOLBAR
626
627 wxToolBar* wxFrame::CreateToolBar( long style, wxWindowID id, const wxString& name )
628 {
629 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
630
631 m_insertInClientArea = false;
632
633 m_frameToolBar = wxFrameBase::CreateToolBar( style, id, name );
634
635 m_insertInClientArea = true;
636
637 GtkUpdateSize();
638
639 return m_frameToolBar;
640 }
641
642 void wxFrame::SetToolBar(wxToolBar *toolbar)
643 {
644 bool hadTbar = m_frameToolBar != NULL;
645
646 wxFrameBase::SetToolBar(toolbar);
647
648 if ( m_frameToolBar )
649 {
650 // insert into toolbar area if not already there
651 if ((m_frameToolBar->m_widget->parent) &&
652 (m_frameToolBar->m_widget->parent != m_mainWidget))
653 {
654 GetChildren().DeleteObject( m_frameToolBar );
655
656 gtk_widget_reparent( m_frameToolBar->m_widget, m_mainWidget );
657 GtkUpdateSize();
658 }
659 }
660 else // toolbar unset
661 {
662 // still need to update size if it had been there before
663 if ( hadTbar )
664 {
665 GtkUpdateSize();
666 }
667 }
668 }
669
670 #endif // wxUSE_TOOLBAR
671
672 #if wxUSE_STATUSBAR
673
674 wxStatusBar* wxFrame::CreateStatusBar(int number,
675 long style,
676 wxWindowID id,
677 const wxString& name)
678 {
679 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
680
681 // because it will change when toolbar is added
682 GtkUpdateSize();
683
684 return wxFrameBase::CreateStatusBar( number, style, id, name );
685 }
686
687 void wxFrame::SetStatusBar(wxStatusBar *statbar)
688 {
689 bool hadStatBar = m_frameStatusBar != NULL;
690
691 wxFrameBase::SetStatusBar(statbar);
692
693 if (hadStatBar && !m_frameStatusBar)
694 GtkUpdateSize();
695 }
696
697 void wxFrame::PositionStatusBar()
698 {
699 if ( !m_frameStatusBar )
700 return;
701
702 GtkUpdateSize();
703 }
704 #endif // wxUSE_STATUSBAR