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