Fix for crash when the child has no menu
[wxWidgets.git] / src / gtk / mdi.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mdi.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "mdi.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #include "wx/mdi.h"
18 #include "wx/notebook.h"
19
20 #if wxUSE_MDI
21
22 #include "wx/dialog.h"
23 #include "wx/menu.h"
24 #include "wx/intl.h"
25 #include "wx/gtk/private.h"
26
27 #include <glib.h>
28 #include <gdk/gdk.h>
29 #include <gtk/gtk.h>
30 #include "wx/gtk/win_gtk.h"
31
32 //-----------------------------------------------------------------------------
33 // constants
34 //-----------------------------------------------------------------------------
35
36 const int wxMENU_HEIGHT = 27;
37
38 //-----------------------------------------------------------------------------
39 // idle system
40 //-----------------------------------------------------------------------------
41
42 extern void wxapp_install_idle_handler();
43 extern bool g_isIdle;
44
45 //-----------------------------------------------------------------------------
46 // globals
47 //-----------------------------------------------------------------------------
48
49 extern wxList wxPendingDelete;
50
51 //-----------------------------------------------------------------------------
52 // "switch_page"
53 //-----------------------------------------------------------------------------
54
55 static void
56 gtk_mdi_page_change_callback( GtkNotebook *WXUNUSED(widget),
57 GtkNotebookPage *page,
58 gint WXUNUSED(page_num),
59 wxMDIParentFrame *parent )
60 {
61 if (g_isIdle)
62 wxapp_install_idle_handler();
63
64 // send deactivate event to old child
65
66 wxMDIChildFrame *child = parent->GetActiveChild();
67 if (child)
68 {
69 wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
70 event1.SetEventObject( child);
71 child->GetEventHandler()->ProcessEvent( event1 );
72 }
73
74 // send activate event to new child
75
76 wxMDIClientWindow *client_window = parent->GetClientWindow();
77 if (!client_window)
78 return;
79
80 child = (wxMDIChildFrame*) NULL;
81
82 wxWindowList::compatibility_iterator node = client_window->GetChildren().GetFirst();
83 while (node)
84 {
85 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
86 // CE: we come here in the destructor with a null child_frame - I think because
87 // gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page", (see below)
88 // isn't deleted early enough
89 if (!child_frame)
90 return ;
91
92 if (child_frame->m_page == page)
93 {
94 child = child_frame;
95 break;
96 }
97 node = node->GetNext();
98 }
99
100 if (!child)
101 return;
102
103 wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
104 event2.SetEventObject( child);
105 child->GetEventHandler()->ProcessEvent( event2 );
106 }
107
108 //-----------------------------------------------------------------------------
109 // wxMDIParentFrame
110 //-----------------------------------------------------------------------------
111
112 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
113
114 void wxMDIParentFrame::Init()
115 {
116 m_justInserted = false;
117 m_clientWindow = (wxMDIClientWindow *) NULL;
118 }
119
120 wxMDIParentFrame::~wxMDIParentFrame()
121 {
122 }
123
124 bool wxMDIParentFrame::Create(wxWindow *parent,
125 wxWindowID id,
126 const wxString& title,
127 const wxPoint& pos,
128 const wxSize& size,
129 long style,
130 const wxString& name )
131 {
132 wxFrame::Create( parent, id, title, pos, size, style, name );
133
134 OnCreateClient();
135
136 return true;
137 }
138
139 void wxMDIParentFrame::GtkOnSize( int x, int y, int width, int height )
140 {
141 wxFrame::GtkOnSize( x, y, width, height );
142
143 wxMDIChildFrame *child_frame = GetActiveChild();
144 if (!child_frame) return;
145
146 wxMenuBar *menu_bar = child_frame->m_menuBar;
147 if (!menu_bar) return;
148 if (!menu_bar->m_widget) return;
149
150 menu_bar->m_x = 0;
151 menu_bar->m_y = 0;
152 menu_bar->m_width = m_width;
153 menu_bar->m_height = wxMENU_HEIGHT;
154 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
155 menu_bar->m_widget,
156 0, 0, m_width, wxMENU_HEIGHT );
157 }
158
159 void wxMDIParentFrame::OnInternalIdle()
160 {
161 /* if a an MDI child window has just been inserted
162 it has to be brought to the top in idle time. we
163 simply set the last notebook page active as new
164 pages can only be appended at the end */
165
166 if (m_justInserted)
167 {
168 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
169 gtk_notebook_set_page( notebook, g_list_length( notebook->children ) - 1 );
170
171 /* need to set the menubar of the child */
172 wxMDIChildFrame *active_child_frame = GetActiveChild();
173 wxMenuBar *menu_bar = active_child_frame->m_menuBar;
174 if (menu_bar)
175 {
176 menu_bar->m_width = m_width;
177 menu_bar->m_height = wxMENU_HEIGHT;
178 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
179 menu_bar->m_widget,
180 0, 0, m_width, wxMENU_HEIGHT );
181 menu_bar->SetInvokingWindow(active_child_frame);
182 }
183
184 m_justInserted = false;
185 return;
186 }
187
188 wxFrame::OnInternalIdle();
189
190 wxMDIChildFrame *active_child_frame = GetActiveChild();
191 bool visible_child_menu = false;
192
193 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
194 while (node)
195 {
196 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
197
198 if ( child_frame )
199 {
200 wxMenuBar *menu_bar = child_frame->m_menuBar;
201 if ( menu_bar )
202 {
203 if (child_frame == active_child_frame)
204 {
205 if (menu_bar->Show(true))
206 {
207 menu_bar->m_width = m_width;
208 menu_bar->m_height = wxMENU_HEIGHT;
209 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
210 menu_bar->m_widget,
211 0, 0, m_width, wxMENU_HEIGHT );
212 menu_bar->SetInvokingWindow( child_frame );
213 }
214 visible_child_menu = true;
215 }
216 else
217 {
218 if (menu_bar->Show(false))
219 {
220 menu_bar->UnsetInvokingWindow( child_frame );
221 }
222 }
223 }
224 }
225
226 node = node->GetNext();
227 }
228
229 /* show/hide parent menu bar as required */
230 if ((m_frameMenuBar) &&
231 (m_frameMenuBar->IsShown() == visible_child_menu))
232 {
233 if (visible_child_menu)
234 {
235 m_frameMenuBar->Show( false );
236 m_frameMenuBar->UnsetInvokingWindow( this );
237 }
238 else
239 {
240 m_frameMenuBar->Show( true );
241 m_frameMenuBar->SetInvokingWindow( this );
242
243 m_frameMenuBar->m_width = m_width;
244 m_frameMenuBar->m_height = wxMENU_HEIGHT;
245 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
246 m_frameMenuBar->m_widget,
247 0, 0, m_width, wxMENU_HEIGHT );
248 }
249 }
250 }
251
252 void wxMDIParentFrame::DoGetClientSize(int *width, int *height ) const
253 {
254 wxFrame::DoGetClientSize( width, height );
255 }
256
257 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
258 {
259 if (!m_clientWindow) return (wxMDIChildFrame*) NULL;
260
261 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
262 if (!notebook) return (wxMDIChildFrame*) NULL;
263
264 gint i = gtk_notebook_get_current_page( notebook );
265 if (i < 0) return (wxMDIChildFrame*) NULL;
266
267 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
268 if (!page) return (wxMDIChildFrame*) NULL;
269
270 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
271 while (node)
272 {
273 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
274
275 wxASSERT_MSG( child_frame, _T("child is not a wxMDIChildFrame") );
276
277 if (child_frame->m_page == page)
278 return child_frame;
279 node = node->GetNext();
280 }
281
282 return (wxMDIChildFrame*) NULL;
283 }
284
285 wxMDIClientWindow *wxMDIParentFrame::GetClientWindow() const
286 {
287 return m_clientWindow;
288 }
289
290 wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
291 {
292 m_clientWindow = new wxMDIClientWindow( this );
293 return m_clientWindow;
294 }
295
296 void wxMDIParentFrame::ActivateNext()
297 {
298 if (m_clientWindow)
299 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
300 }
301
302 void wxMDIParentFrame::ActivatePrevious()
303 {
304 if (m_clientWindow)
305 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
306 }
307
308 //-----------------------------------------------------------------------------
309 // wxMDIChildFrame
310 //-----------------------------------------------------------------------------
311
312 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
313
314 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
315 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
316 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
317 END_EVENT_TABLE()
318
319 wxMDIChildFrame::wxMDIChildFrame()
320 {
321 m_menuBar = (wxMenuBar *) NULL;
322 m_page = (GtkNotebookPage *) NULL;
323 }
324
325 wxMDIChildFrame::wxMDIChildFrame( wxMDIParentFrame *parent,
326 wxWindowID id, const wxString& title,
327 const wxPoint& WXUNUSED(pos), const wxSize& size,
328 long style, const wxString& name )
329 {
330 m_menuBar = (wxMenuBar *) NULL;
331 m_page = (GtkNotebookPage *) NULL;
332 Create( parent, id, title, wxDefaultPosition, size, style, name );
333 }
334
335 wxMDIChildFrame::~wxMDIChildFrame()
336 {
337 if (m_menuBar)
338 delete m_menuBar;
339 }
340
341 bool wxMDIChildFrame::Create( wxMDIParentFrame *parent,
342 wxWindowID id, const wxString& title,
343 const wxPoint& WXUNUSED(pos), const wxSize& size,
344 long style, const wxString& name )
345 {
346 m_title = title;
347
348 return wxWindow::Create( parent->GetClientWindow(), id, wxDefaultPosition, size, style, name );
349 }
350
351 void wxMDIChildFrame::DoSetSize( int x, int y, int width, int height, int sizeFlags )
352 {
353 wxWindow::DoSetSize( x, y, width, height, sizeFlags );
354 }
355
356 void wxMDIChildFrame::DoSetClientSize(int width, int height)
357 {
358 wxWindow::DoSetClientSize( width, height );
359 }
360
361 void wxMDIChildFrame::DoGetClientSize( int *width, int *height ) const
362 {
363 wxWindow::DoGetClientSize( width, height );
364 }
365
366 void wxMDIChildFrame::AddChild( wxWindowBase *child )
367 {
368 wxWindow::AddChild(child);
369 }
370
371 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
372 {
373 wxASSERT_MSG( m_menuBar == NULL, wxT("Only one menubar allowed") );
374
375 m_menuBar = menu_bar;
376
377 if (m_menuBar)
378 {
379 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
380
381 m_menuBar->SetParent( mdi_frame );
382
383 /* insert the invisible menu bar into the _parent_ mdi frame */
384 gtk_pizza_put( GTK_PIZZA(mdi_frame->m_mainWidget),
385 m_menuBar->m_widget,
386 0, 0, mdi_frame->m_width, wxMENU_HEIGHT );
387 }
388 }
389
390 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
391 {
392 return m_menuBar;
393 }
394
395 void wxMDIChildFrame::Activate()
396 {
397 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
398 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
399 gint pageno = gtk_notebook_page_num( notebook, m_widget );
400 gtk_notebook_set_page( notebook, pageno );
401 }
402
403 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
404 {
405 }
406
407 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
408 {
409 #if wxUSE_STATUSBAR
410 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
411 if ( !ShowMenuHelp(mdi_frame->GetStatusBar(), event.GetMenuId()) )
412 {
413 // we don't have any help text for this item, but may be the MDI frame
414 // does?
415 mdi_frame->OnMenuHighlight(event);
416 }
417 #endif // wxUSE_STATUSBAR
418 }
419
420 void wxMDIChildFrame::SetTitle( const wxString &title )
421 {
422 if ( title == m_title )
423 return;
424
425 m_title = title;
426
427 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
428 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
429 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
430 }
431
432 //-----------------------------------------------------------------------------
433 // "size_allocate"
434 //-----------------------------------------------------------------------------
435
436 static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
437 {
438 if (g_isIdle) wxapp_install_idle_handler();
439
440 if ((win->m_x == alloc->x) &&
441 (win->m_y == alloc->y) &&
442 (win->m_width == alloc->width) &&
443 (win->m_height == alloc->height) &&
444 (win->m_sizeSet))
445 {
446 return;
447 }
448
449 win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
450 }
451
452 //-----------------------------------------------------------------------------
453 // InsertChild callback for wxMDIClientWindow
454 //-----------------------------------------------------------------------------
455
456 static void wxInsertChildInMDI( wxMDIClientWindow* parent, wxMDIChildFrame* child )
457 {
458 wxString s = child->m_title;
459 if (s.IsNull()) s = _("MDI child");
460
461 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
462 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
463
464 gtk_signal_connect( GTK_OBJECT(child->m_widget), "size_allocate",
465 GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)child );
466
467 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
468
469 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
470
471 child->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
472
473 wxMDIParentFrame *parent_frame = (wxMDIParentFrame*) parent->GetParent();
474 parent_frame->m_justInserted = true;
475 }
476
477 //-----------------------------------------------------------------------------
478 // wxMDIClientWindow
479 //-----------------------------------------------------------------------------
480
481 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)
482
483 wxMDIClientWindow::wxMDIClientWindow()
484 {
485 }
486
487 wxMDIClientWindow::wxMDIClientWindow( wxMDIParentFrame *parent, long style )
488 {
489 CreateClient( parent, style );
490 }
491
492 wxMDIClientWindow::~wxMDIClientWindow()
493 {
494
495 }
496
497 bool wxMDIClientWindow::CreateClient( wxMDIParentFrame *parent, long style )
498 {
499 m_needParent = true;
500
501 m_insertCallback = (wxInsertChildFunction)wxInsertChildInMDI;
502
503 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
504 !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("wxMDIClientWindow") ))
505 {
506 wxFAIL_MSG( wxT("wxMDIClientWindow creation failed") );
507 return false;
508 }
509
510 m_widget = gtk_notebook_new();
511
512 gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
513 GTK_SIGNAL_FUNC(gtk_mdi_page_change_callback), (gpointer)parent );
514
515 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
516
517 m_parent->DoAddChild( this );
518
519 PostCreation();
520
521 Show( true );
522
523 return true;
524 }
525
526 #endif