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