1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/mdi.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id: mdi.cpp 51758 2008-02-13 16:57:44Z PC $
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
gtk_mdi_page_change_callback(GtkNotebook * WXUNUSED (widget),GtkNotebookPage * page,gint WXUNUSED (page_num),wxMDIParentFrame * parent)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
IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)102 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
103
104 void wxMDIParentFrame::Init()
105 {
106 m_justInserted = false;
107 m_clientWindow = (wxMDIClientWindow *) NULL;
108 }
109
~wxMDIParentFrame()110 wxMDIParentFrame::~wxMDIParentFrame()
111 {
112 }
113
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,long style,const wxString & name)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
GtkOnSize()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
OnInternalIdle()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
GetActiveChild() const244 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 if ( wxPendingDelete.Member(node->GetData()) )
261 return (wxMDIChildFrame*) NULL;
262
263 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
264
265 if (!child_frame)
266 return (wxMDIChildFrame*) NULL;
267
268 if (child_frame->m_page == page)
269 return child_frame;
270
271 node = node->GetNext();
272 }
273
274 return (wxMDIChildFrame*) NULL;
275 }
276
GetClientWindow() const277 wxMDIClientWindow *wxMDIParentFrame::GetClientWindow() const
278 {
279 return m_clientWindow;
280 }
281
OnCreateClient()282 wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
283 {
284 m_clientWindow = new wxMDIClientWindow( this );
285 return m_clientWindow;
286 }
287
ActivateNext()288 void wxMDIParentFrame::ActivateNext()
289 {
290 if (m_clientWindow)
291 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
292 }
293
ActivatePrevious()294 void wxMDIParentFrame::ActivatePrevious()
295 {
296 if (m_clientWindow)
297 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
298 }
299
300 //-----------------------------------------------------------------------------
301 // wxMDIChildFrame
302 //-----------------------------------------------------------------------------
303
IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)304 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
305
306 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
307 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
308 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
309 END_EVENT_TABLE()
310
311 wxMDIChildFrame::wxMDIChildFrame()
312 {
313 m_menuBar = (wxMenuBar *) NULL;
314 m_page = (GtkNotebookPage *) NULL;
315 }
316
wxMDIChildFrame(wxMDIParentFrame * parent,wxWindowID id,const wxString & title,const wxPoint & WXUNUSED (pos),const wxSize & size,long style,const wxString & name)317 wxMDIChildFrame::wxMDIChildFrame( wxMDIParentFrame *parent,
318 wxWindowID id, const wxString& title,
319 const wxPoint& WXUNUSED(pos), const wxSize& size,
320 long style, const wxString& name )
321 {
322 m_menuBar = (wxMenuBar *) NULL;
323 m_page = (GtkNotebookPage *) NULL;
324 Create( parent, id, title, wxDefaultPosition, size, style, name );
325 }
326
~wxMDIChildFrame()327 wxMDIChildFrame::~wxMDIChildFrame()
328 {
329 if (m_menuBar)
330 delete m_menuBar;
331
332 // wxMDIClientWindow does not get redrawn properly after last child is removed
333 if (m_parent && m_parent->GetChildren().size() <= 1)
334 gtk_widget_queue_draw(m_parent->m_widget);
335 }
336
Create(wxMDIParentFrame * parent,wxWindowID id,const wxString & title,const wxPoint & WXUNUSED (pos),const wxSize & size,long style,const wxString & name)337 bool wxMDIChildFrame::Create( wxMDIParentFrame *parent,
338 wxWindowID id, const wxString& title,
339 const wxPoint& WXUNUSED(pos), const wxSize& size,
340 long style, const wxString& name )
341 {
342 m_title = title;
343
344 return wxWindow::Create( parent->GetClientWindow(), id, wxDefaultPosition, size, style, name );
345 }
346
Destroy()347 bool wxMDIChildFrame::Destroy()
348 {
349 // delayed destruction: the frame will be deleted during
350 // the next idle loop iteration.
351 // I'm not sure if delayed destruction really makes so
352 // much sense for MDI child frames, actually, but hiding
353 // it doesn't make any sense.
354 if ( !wxPendingDelete.Member(this) )
355 wxPendingDelete.Append(this);
356
357 return true;
358 }
359
DoSetSize(int x,int y,int width,int height,int sizeFlags)360 void wxMDIChildFrame::DoSetSize( int x, int y, int width, int height, int sizeFlags )
361 {
362 wxWindow::DoSetSize( x, y, width, height, sizeFlags );
363 }
364
DoSetClientSize(int width,int height)365 void wxMDIChildFrame::DoSetClientSize(int width, int height)
366 {
367 wxWindow::DoSetClientSize( width, height );
368 }
369
DoGetClientSize(int * width,int * height) const370 void wxMDIChildFrame::DoGetClientSize( int *width, int *height ) const
371 {
372 wxWindow::DoGetClientSize( width, height );
373 }
374
AddChild(wxWindowBase * child)375 void wxMDIChildFrame::AddChild( wxWindowBase *child )
376 {
377 wxWindow::AddChild(child);
378 }
379
SetMenuBar(wxMenuBar * menu_bar)380 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
381 {
382 wxASSERT_MSG( m_menuBar == NULL, wxT("Only one menubar allowed") );
383
384 m_menuBar = menu_bar;
385
386 if (m_menuBar)
387 {
388 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
389
390 m_menuBar->SetParent( mdi_frame );
391
392 /* insert the invisible menu bar into the _parent_ mdi frame */
393 gtk_pizza_put( GTK_PIZZA(mdi_frame->m_mainWidget),
394 m_menuBar->m_widget,
395 0, 0, mdi_frame->m_width, wxMENU_HEIGHT );
396 }
397 }
398
GetMenuBar() const399 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
400 {
401 return m_menuBar;
402 }
403
Activate()404 void wxMDIChildFrame::Activate()
405 {
406 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
407 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
408 gint pageno = gtk_notebook_page_num( notebook, m_widget );
409 gtk_notebook_set_current_page( notebook, pageno );
410 }
411
OnActivate(wxActivateEvent & WXUNUSED (event))412 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
413 {
414 }
415
OnMenuHighlight(wxMenuEvent & event)416 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
417 {
418 #if wxUSE_STATUSBAR
419 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
420 if ( !ShowMenuHelp(mdi_frame->GetStatusBar(), event.GetMenuId()) )
421 {
422 // we don't have any help text for this item, but may be the MDI frame
423 // does?
424 mdi_frame->OnMenuHighlight(event);
425 }
426 #endif // wxUSE_STATUSBAR
427 }
428
SetTitle(const wxString & title)429 void wxMDIChildFrame::SetTitle( const wxString &title )
430 {
431 if ( title == m_title )
432 return;
433
434 m_title = title;
435
436 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
437 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
438 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
439 }
440
441 //-----------------------------------------------------------------------------
442 // "size_allocate"
443 //-----------------------------------------------------------------------------
444
445 extern "C" {
gtk_page_size_callback(GtkWidget * WXUNUSED (widget),GtkAllocation * alloc,wxWindow * win)446 static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
447 {
448 if (g_isIdle) wxapp_install_idle_handler();
449
450 if ((win->m_x == alloc->x) &&
451 (win->m_y == alloc->y) &&
452 (win->m_width == alloc->width) &&
453 (win->m_height == alloc->height) &&
454 (win->m_sizeSet))
455 {
456 return;
457 }
458
459 win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
460 }
461 }
462
463 //-----------------------------------------------------------------------------
464 // InsertChild callback for wxMDIClientWindow
465 //-----------------------------------------------------------------------------
466
wxInsertChildInMDI(wxMDIClientWindow * parent,wxMDIChildFrame * child)467 static void wxInsertChildInMDI( wxMDIClientWindow* parent, wxMDIChildFrame* child )
468 {
469 wxString s = child->GetTitle();
470 if (s.IsNull()) s = _("MDI child");
471
472 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
473 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
474
475 g_signal_connect (child->m_widget, "size_allocate",
476 G_CALLBACK (gtk_page_size_callback), child);
477
478 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
479
480 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
481
482 child->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
483
484 wxMDIParentFrame *parent_frame = (wxMDIParentFrame*) parent->GetParent();
485 parent_frame->m_justInserted = true;
486 }
487
488 //-----------------------------------------------------------------------------
489 // wxMDIClientWindow
490 //-----------------------------------------------------------------------------
491
IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)492 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)
493
494 wxMDIClientWindow::wxMDIClientWindow()
495 {
496 }
497
wxMDIClientWindow(wxMDIParentFrame * parent,long style)498 wxMDIClientWindow::wxMDIClientWindow( wxMDIParentFrame *parent, long style )
499 {
500 CreateClient( parent, style );
501 }
502
~wxMDIClientWindow()503 wxMDIClientWindow::~wxMDIClientWindow()
504 {
505
506 }
507
CreateClient(wxMDIParentFrame * parent,long style)508 bool wxMDIClientWindow::CreateClient( wxMDIParentFrame *parent, long style )
509 {
510 m_needParent = true;
511
512 m_insertCallback = (wxInsertChildFunction)wxInsertChildInMDI;
513
514 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
515 !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("wxMDIClientWindow") ))
516 {
517 wxFAIL_MSG( wxT("wxMDIClientWindow creation failed") );
518 return false;
519 }
520
521 m_widget = gtk_notebook_new();
522
523 g_signal_connect (m_widget, "switch_page",
524 G_CALLBACK (gtk_mdi_page_change_callback), parent);
525
526 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
527
528 m_parent->DoAddChild( this );
529
530 PostCreation();
531
532 Show( true );
533
534 return true;
535 }
536
537 #endif
538