fix an mdi crash on exit
[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 menu_bar->m_width = m_width;
175 menu_bar->m_height = wxMENU_HEIGHT;
176 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
177 menu_bar->m_widget,
178 0, 0, m_width, wxMENU_HEIGHT );
179 menu_bar->SetInvokingWindow(active_child_frame);
180
181 m_justInserted = false;
182 return;
183 }
184
185 wxFrame::OnInternalIdle();
186
187 wxMDIChildFrame *active_child_frame = GetActiveChild();
188 bool visible_child_menu = false;
189
190 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
191 while (node)
192 {
193 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
194
195 if ( child_frame )
196 {
197 wxMenuBar *menu_bar = child_frame->m_menuBar;
198 if ( menu_bar )
199 {
200 if (child_frame == active_child_frame)
201 {
202 if (menu_bar->Show(true))
203 {
204 menu_bar->m_width = m_width;
205 menu_bar->m_height = wxMENU_HEIGHT;
206 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
207 menu_bar->m_widget,
208 0, 0, m_width, wxMENU_HEIGHT );
209 menu_bar->SetInvokingWindow( child_frame );
210 }
211 visible_child_menu = true;
212 }
213 else
214 {
215 if (menu_bar->Show(false))
216 {
217 menu_bar->UnsetInvokingWindow( child_frame );
218 }
219 }
220 }
221 }
222
223 node = node->GetNext();
224 }
225
226 /* show/hide parent menu bar as required */
227 if ((m_frameMenuBar) &&
228 (m_frameMenuBar->IsShown() == visible_child_menu))
229 {
230 if (visible_child_menu)
231 {
232 m_frameMenuBar->Show( false );
233 m_frameMenuBar->UnsetInvokingWindow( this );
234 }
235 else
236 {
237 m_frameMenuBar->Show( true );
238 m_frameMenuBar->SetInvokingWindow( this );
239
240 m_frameMenuBar->m_width = m_width;
241 m_frameMenuBar->m_height = wxMENU_HEIGHT;
242 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
243 m_frameMenuBar->m_widget,
244 0, 0, m_width, wxMENU_HEIGHT );
245 }
246 }
247 }
248
249 void wxMDIParentFrame::DoGetClientSize(int *width, int *height ) const
250 {
251 wxFrame::DoGetClientSize( width, height );
252 }
253
254 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
255 {
256 if (!m_clientWindow) return (wxMDIChildFrame*) NULL;
257
258 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
259 if (!notebook) return (wxMDIChildFrame*) NULL;
260
261 gint i = gtk_notebook_get_current_page( notebook );
262 if (i < 0) return (wxMDIChildFrame*) NULL;
263
264 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
265 if (!page) return (wxMDIChildFrame*) NULL;
266
267 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
268 while (node)
269 {
270 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
271
272 wxASSERT_MSG( child_frame, _T("child is not a wxMDIChildFrame") );
273
274 if (child_frame->m_page == page)
275 return child_frame;
276 node = node->GetNext();
277 }
278
279 return (wxMDIChildFrame*) NULL;
280 }
281
282 wxMDIClientWindow *wxMDIParentFrame::GetClientWindow() const
283 {
284 return m_clientWindow;
285 }
286
287 wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
288 {
289 m_clientWindow = new wxMDIClientWindow( this );
290 return m_clientWindow;
291 }
292
293 void wxMDIParentFrame::ActivateNext()
294 {
295 if (m_clientWindow)
296 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
297 }
298
299 void wxMDIParentFrame::ActivatePrevious()
300 {
301 if (m_clientWindow)
302 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
303 }
304
305 //-----------------------------------------------------------------------------
306 // wxMDIChildFrame
307 //-----------------------------------------------------------------------------
308
309 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
310
311 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
312 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
313 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
314 END_EVENT_TABLE()
315
316 wxMDIChildFrame::wxMDIChildFrame()
317 {
318 m_menuBar = (wxMenuBar *) NULL;
319 m_page = (GtkNotebookPage *) NULL;
320 }
321
322 wxMDIChildFrame::wxMDIChildFrame( wxMDIParentFrame *parent,
323 wxWindowID id, const wxString& title,
324 const wxPoint& WXUNUSED(pos), const wxSize& size,
325 long style, const wxString& name )
326 {
327 m_menuBar = (wxMenuBar *) NULL;
328 m_page = (GtkNotebookPage *) NULL;
329 Create( parent, id, title, wxDefaultPosition, size, style, name );
330 }
331
332 wxMDIChildFrame::~wxMDIChildFrame()
333 {
334 if (m_menuBar)
335 delete m_menuBar;
336 }
337
338 bool wxMDIChildFrame::Create( wxMDIParentFrame *parent,
339 wxWindowID id, const wxString& title,
340 const wxPoint& WXUNUSED(pos), const wxSize& size,
341 long style, const wxString& name )
342 {
343 m_title = title;
344
345 return wxWindow::Create( parent->GetClientWindow(), id, wxDefaultPosition, size, style, name );
346 }
347
348 void wxMDIChildFrame::DoSetSize( int x, int y, int width, int height, int sizeFlags )
349 {
350 wxWindow::DoSetSize( x, y, width, height, sizeFlags );
351 }
352
353 void wxMDIChildFrame::DoSetClientSize(int width, int height)
354 {
355 wxWindow::DoSetClientSize( width, height );
356 }
357
358 void wxMDIChildFrame::DoGetClientSize( int *width, int *height ) const
359 {
360 wxWindow::DoGetClientSize( width, height );
361 }
362
363 void wxMDIChildFrame::AddChild( wxWindowBase *child )
364 {
365 wxWindow::AddChild(child);
366 }
367
368 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
369 {
370 wxASSERT_MSG( m_menuBar == NULL, wxT("Only one menubar allowed") );
371
372 m_menuBar = menu_bar;
373
374 if (m_menuBar)
375 {
376 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
377
378 m_menuBar->SetParent( mdi_frame );
379
380 /* insert the invisible menu bar into the _parent_ mdi frame */
381 gtk_pizza_put( GTK_PIZZA(mdi_frame->m_mainWidget),
382 m_menuBar->m_widget,
383 0, 0, mdi_frame->m_width, wxMENU_HEIGHT );
384 }
385 }
386
387 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
388 {
389 return m_menuBar;
390 }
391
392 void wxMDIChildFrame::Activate()
393 {
394 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
395 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
396 gint pageno = gtk_notebook_page_num( notebook, m_widget );
397 gtk_notebook_set_page( notebook, pageno );
398 }
399
400 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
401 {
402 }
403
404 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
405 {
406 #if wxUSE_STATUSBAR
407 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
408 if ( !ShowMenuHelp(mdi_frame->GetStatusBar(), event.GetMenuId()) )
409 {
410 // we don't have any help text for this item, but may be the MDI frame
411 // does?
412 mdi_frame->OnMenuHighlight(event);
413 }
414 #endif // wxUSE_STATUSBAR
415 }
416
417 void wxMDIChildFrame::SetTitle( const wxString &title )
418 {
419 if ( title == m_title )
420 return;
421
422 m_title = title;
423
424 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
425 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
426 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
427 }
428
429 //-----------------------------------------------------------------------------
430 // "size_allocate"
431 //-----------------------------------------------------------------------------
432
433 static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
434 {
435 if (g_isIdle) wxapp_install_idle_handler();
436
437 if ((win->m_x == alloc->x) &&
438 (win->m_y == alloc->y) &&
439 (win->m_width == alloc->width) &&
440 (win->m_height == alloc->height) &&
441 (win->m_sizeSet))
442 {
443 return;
444 }
445
446 win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
447 }
448
449 //-----------------------------------------------------------------------------
450 // InsertChild callback for wxMDIClientWindow
451 //-----------------------------------------------------------------------------
452
453 static void wxInsertChildInMDI( wxMDIClientWindow* parent, wxMDIChildFrame* child )
454 {
455 wxString s = child->m_title;
456 if (s.IsNull()) s = _("MDI child");
457
458 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
459 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
460
461 gtk_signal_connect( GTK_OBJECT(child->m_widget), "size_allocate",
462 GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)child );
463
464 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
465
466 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
467
468 child->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
469
470 wxMDIParentFrame *parent_frame = (wxMDIParentFrame*) parent->GetParent();
471 parent_frame->m_justInserted = true;
472 }
473
474 //-----------------------------------------------------------------------------
475 // wxMDIClientWindow
476 //-----------------------------------------------------------------------------
477
478 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)
479
480 wxMDIClientWindow::wxMDIClientWindow()
481 {
482 }
483
484 wxMDIClientWindow::wxMDIClientWindow( wxMDIParentFrame *parent, long style )
485 {
486 CreateClient( parent, style );
487 }
488
489 wxMDIClientWindow::~wxMDIClientWindow()
490 {
491
492 }
493
494 bool wxMDIClientWindow::CreateClient( wxMDIParentFrame *parent, long style )
495 {
496 m_needParent = true;
497
498 m_insertCallback = (wxInsertChildFunction)wxInsertChildInMDI;
499
500 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
501 !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("wxMDIClientWindow") ))
502 {
503 wxFAIL_MSG( wxT("wxMDIClientWindow creation failed") );
504 return false;
505 }
506
507 m_widget = gtk_notebook_new();
508
509 gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
510 GTK_SIGNAL_FUNC(gtk_mdi_page_change_callback), (gpointer)parent );
511
512 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
513
514 m_parent->DoAddChild( this );
515
516 PostCreation();
517
518 Show( true );
519
520 return true;
521 }
522
523 #endif