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