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