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