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