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