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