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