Don't use invoking window in wxGTK wxMenuBar implementation.
[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 #if wxUSE_MDI
14
15 #include "wx/mdi.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/intl.h"
19 #include "wx/menu.h"
20 #endif
21
22 #include "wx/gtk/private.h"
23
24 //-----------------------------------------------------------------------------
25 // "switch_page"
26 //-----------------------------------------------------------------------------
27
28 extern "C" {
29 static void
30 gtk_mdi_page_change_callback( GtkNotebook *WXUNUSED(widget),
31 GtkNotebookPage *page,
32 gint WXUNUSED(page_num),
33 wxMDIParentFrame *parent )
34 {
35 // send deactivate event to old child
36
37 wxMDIChildFrame *child = parent->GetActiveChild();
38 if (child)
39 {
40 wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
41 event1.SetEventObject( child);
42 child->HandleWindowEvent( event1 );
43 }
44
45 // send activate event to new child
46
47 wxMDIClientWindowBase *client_window = parent->GetClientWindow();
48 if ( !client_window )
49 return;
50
51 child = NULL;
52
53 wxWindowList::compatibility_iterator node = client_window->GetChildren().GetFirst();
54 while ( node )
55 {
56 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
57
58 // child_frame can be NULL when this is called from dtor, probably
59 // because g_signal_connect (m_widget, "switch_page", (see below)
60 // isn't deleted early enough
61 if ( child_frame && child_frame->m_page == page )
62 {
63 child = child_frame;
64 break;
65 }
66 node = node->GetNext();
67 }
68
69 if (!child)
70 return;
71
72 wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
73 event2.SetEventObject( child);
74 child->HandleWindowEvent( event2 );
75 }
76 }
77
78 //-----------------------------------------------------------------------------
79 // wxMDIParentFrame
80 //-----------------------------------------------------------------------------
81
82 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
83
84 void wxMDIParentFrame::Init()
85 {
86 m_justInserted = false;
87 }
88
89 bool wxMDIParentFrame::Create(wxWindow *parent,
90 wxWindowID id,
91 const wxString& title,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxString& name )
96 {
97 if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
98 return false;
99
100 m_clientWindow = OnCreateClient();
101 if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
102 return false;
103
104 return true;
105 }
106
107 void wxMDIParentFrame::OnInternalIdle()
108 {
109 /* if a MDI child window has just been inserted
110 it has to be brought to the top in idle time. we
111 simply set the last notebook page active as new
112 pages can only be appended at the end */
113
114 if (m_justInserted)
115 {
116 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
117 gtk_notebook_set_current_page( notebook, g_list_length( notebook->children ) - 1 );
118
119 /* need to set the menubar of the child */
120 wxMDIChildFrame *active_child_frame = GetActiveChild();
121 if (active_child_frame != NULL)
122 {
123 wxMenuBar *menu_bar = active_child_frame->m_menuBar;
124 if (menu_bar)
125 {
126 menu_bar->Attach(active_child_frame);
127 }
128 }
129 m_justInserted = false;
130 return;
131 }
132
133 wxFrame::OnInternalIdle();
134
135 wxMDIChildFrame *active_child_frame = GetActiveChild();
136 bool visible_child_menu = false;
137
138 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
139 while (node)
140 {
141 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
142
143 if ( child_frame )
144 {
145 wxMenuBar *menu_bar = child_frame->m_menuBar;
146 if ( menu_bar )
147 {
148 if (child_frame == active_child_frame)
149 {
150 if (menu_bar->Show(true))
151 {
152 // Attach() asserts if we call it for an already
153 // attached menu bar so don't do it if we're already
154 // associated with this frame (it would be nice to get
155 // rid of this check and ensure that this doesn't
156 // happen...)
157 if ( menu_bar->GetFrame() != child_frame )
158 menu_bar->Attach( child_frame );
159 }
160 visible_child_menu = true;
161 }
162 else
163 {
164 if (menu_bar->Show(false))
165 {
166 menu_bar->Detach();
167 }
168 }
169 }
170 }
171
172 node = node->GetNext();
173 }
174
175 /* show/hide parent menu bar as required */
176 if ((m_frameMenuBar) &&
177 (m_frameMenuBar->IsShown() == visible_child_menu))
178 {
179 if (visible_child_menu)
180 {
181 m_frameMenuBar->Show( false );
182 m_frameMenuBar->Detach();
183 }
184 else
185 {
186 m_frameMenuBar->Show( true );
187 m_frameMenuBar->Attach( this );
188 }
189 }
190 }
191
192 void wxMDIParentFrame::DoGetClientSize(int* width, int* height) const
193 {
194 wxFrame::DoGetClientSize(width, height);
195
196 if (height)
197 {
198 wxMDIChildFrame* active_child_frame = GetActiveChild();
199 if (active_child_frame)
200 {
201 wxMenuBar* menubar = active_child_frame->m_menuBar;
202 if (menubar && menubar->IsShown())
203 {
204 GtkRequisition req;
205 gtk_widget_size_request(menubar->m_widget, &req);
206 *height -= req.height;
207 if (*height < 0) *height = 0;
208 }
209 }
210 }
211 }
212
213 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
214 {
215 if (!m_clientWindow) return NULL;
216
217 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
218 if (!notebook) return NULL;
219
220 gint i = gtk_notebook_get_current_page( notebook );
221 if (i < 0) return NULL;
222
223 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
224 if (!page) return NULL;
225
226 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
227 while (node)
228 {
229 if ( wxPendingDelete.Member(node->GetData()) )
230 return NULL;
231
232 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
233
234 if (!child_frame)
235 return NULL;
236
237 if (child_frame->m_page == page)
238 return child_frame;
239
240 node = node->GetNext();
241 }
242
243 return NULL;
244 }
245
246 void wxMDIParentFrame::ActivateNext()
247 {
248 if (m_clientWindow)
249 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
250 }
251
252 void wxMDIParentFrame::ActivatePrevious()
253 {
254 if (m_clientWindow)
255 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
256 }
257
258 //-----------------------------------------------------------------------------
259 // wxMDIChildFrame
260 //-----------------------------------------------------------------------------
261
262 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
263
264 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
265 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
266 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
267 END_EVENT_TABLE()
268
269 void wxMDIChildFrame::Init()
270 {
271 m_menuBar = NULL;
272 m_page = NULL;
273 }
274
275 bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
276 wxWindowID id,
277 const wxString& title,
278 const wxPoint& WXUNUSED(pos),
279 const wxSize& size,
280 long style,
281 const wxString& name)
282 {
283 m_mdiParent = parent;
284 m_title = title;
285
286 return wxWindow::Create(parent->GetClientWindow(), id,
287 wxDefaultPosition, size,
288 style, name);
289 }
290
291 wxMDIChildFrame::~wxMDIChildFrame()
292 {
293 delete m_menuBar;
294
295 // wxMDIClientWindow does not get redrawn properly after last child is removed
296 if (m_parent && m_parent->GetChildren().size() <= 1)
297 gtk_widget_queue_draw(m_parent->m_widget);
298 }
299
300 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
301 {
302 wxASSERT_MSG( m_menuBar == NULL, "Only one menubar allowed" );
303
304 m_menuBar = menu_bar;
305
306 if (m_menuBar)
307 {
308 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
309
310 m_menuBar->SetParent( mdi_frame );
311
312 /* insert the invisible menu bar into the _parent_ mdi frame */
313 m_menuBar->Show(false);
314 gtk_box_pack_start(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, false, false, 0);
315 gtk_box_reorder_child(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, 0);
316
317 gulong handler_id = g_signal_handler_find(
318 m_menuBar->m_widget,
319 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
320 g_signal_lookup("size_request", GTK_TYPE_WIDGET),
321 0, NULL, NULL, m_menuBar);
322 if (handler_id != 0)
323 g_signal_handler_disconnect(m_menuBar->m_widget, handler_id);
324 gtk_widget_set_size_request(m_menuBar->m_widget, -1, -1);
325 }
326 }
327
328 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
329 {
330 return m_menuBar;
331 }
332
333 GtkNotebook *wxMDIChildFrame::GTKGetNotebook() const
334 {
335 wxMDIClientWindow * const
336 client = wxStaticCast(GetParent(), wxMDIClientWindow);
337 wxCHECK( client, NULL );
338
339 return GTK_NOTEBOOK(client->m_widget);
340 }
341
342 void wxMDIChildFrame::Activate()
343 {
344 GtkNotebook * const notebook = GTKGetNotebook();
345 wxCHECK_RET( notebook, "no parent notebook?" );
346
347 gint pageno = gtk_notebook_page_num( notebook, m_widget );
348 gtk_notebook_set_current_page( notebook, pageno );
349 }
350
351 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
352 {
353 }
354
355 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
356 {
357 #if wxUSE_STATUSBAR
358 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
359 if ( !ShowMenuHelp(event.GetMenuId()) )
360 {
361 // we don't have any help text for this item, but may be the MDI frame
362 // does?
363 mdi_frame->OnMenuHighlight(event);
364 }
365 #endif // wxUSE_STATUSBAR
366 }
367
368 void wxMDIChildFrame::SetTitle( const wxString &title )
369 {
370 if ( title == m_title )
371 return;
372
373 m_title = title;
374
375 GtkNotebook * const notebook = GTKGetNotebook();
376 wxCHECK_RET( notebook, "no parent notebook?" );
377 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
378 }
379
380 //-----------------------------------------------------------------------------
381 // wxMDIClientWindow
382 //-----------------------------------------------------------------------------
383
384 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
385
386 wxMDIClientWindow::~wxMDIClientWindow()
387 {
388 // disconnect our handler because our ~wxWindow (which is going to be called
389 // after this dtor) will call DestroyChildren(); in turns our children
390 // ~wxWindow dtors will call wxWindow::Show(false) and this will generate
391 // a call to gtk_mdi_page_change_callback with an invalid parent
392 // (because gtk_mdi_page_change_callback expects a wxMDIClientWindow but
393 // at that point of the dtor chain we are a simple wxWindow!)
394 g_signal_handlers_disconnect_by_func(m_widget,
395 (gpointer)gtk_mdi_page_change_callback,
396 GetParent());
397 }
398
399 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
400 {
401 if ( !PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
402 !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
403 style, wxDefaultValidator, "wxMDIClientWindow" ))
404 {
405 wxFAIL_MSG( "wxMDIClientWindow creation failed" );
406 return false;
407 }
408
409 m_widget = gtk_notebook_new();
410 g_object_ref(m_widget);
411
412 g_signal_connect (m_widget, "switch_page",
413 G_CALLBACK (gtk_mdi_page_change_callback), parent);
414
415 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
416
417 m_parent->DoAddChild( this );
418
419 PostCreation();
420
421 Show( true );
422
423 return true;
424 }
425
426 void wxMDIClientWindow::AddChildGTK(wxWindowGTK* child)
427 {
428 wxMDIChildFrame* child_frame = static_cast<wxMDIChildFrame*>(child);
429 wxString s = child_frame->GetTitle();
430 if ( s.empty() )
431 s = _("MDI child");
432
433 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
434 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
435
436 GtkNotebook* notebook = GTK_NOTEBOOK(m_widget);
437
438 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
439
440 child_frame->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
441
442 wxMDIParentFrame* parent_frame = static_cast<wxMDIParentFrame*>(GetParent());
443 parent_frame->m_justInserted = true;
444 }
445
446 #endif // wxUSE_MDI