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