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