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