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