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