1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/mdi.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #if wxUSE_MDI
13 
14 #include "wx/mdi.h"
15 
16 #ifndef WX_PRECOMP
17     #include "wx/intl.h"
18     #include "wx/menu.h"
19 #endif
20 
21 #include "wx/gtk/private.h"
22 
23 //-----------------------------------------------------------------------------
24 // "switch_page"
25 //-----------------------------------------------------------------------------
26 
27 extern "C" {
28 static void
switch_page(GtkNotebook * widget,GtkNotebookPage *,guint page_num,wxMDIParentFrame * parent)29 switch_page(GtkNotebook* widget, GtkNotebookPage*, guint page_num, wxMDIParentFrame* parent)
30 {
31     // send deactivate event to old child
32 
33     wxMDIChildFrame *child = parent->GetActiveChild();
34     if (child)
35     {
36         wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
37         event1.SetEventObject( child);
38         child->HandleWindowEvent( event1 );
39     }
40 
41     // send activate event to new child
42 
43     wxMDIClientWindowBase *client_window = parent->GetClientWindow();
44     if ( !client_window )
45         return;
46 
47     child = NULL;
48     GtkWidget* page = gtk_notebook_get_nth_page(widget, page_num);
49 
50     wxWindowList::compatibility_iterator node = client_window->GetChildren().GetFirst();
51     while ( node )
52     {
53         wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
54 
55         // child_frame can be NULL when this is called from dtor, probably
56         // because g_signal_connect (m_widget, "switch_page", (see below)
57         // isn't deleted early enough
58         if (child_frame && child_frame->m_widget == page)
59         {
60             child = child_frame;
61             break;
62         }
63         node = node->GetNext();
64     }
65 
66     if (!child)
67          return;
68 
69     wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
70     event2.SetEventObject( child);
71     child->HandleWindowEvent( event2 );
72 }
73 }
74 
75 //-----------------------------------------------------------------------------
76 // wxMDIParentFrame
77 //-----------------------------------------------------------------------------
78 
IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)79 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
80 
81 void wxMDIParentFrame::Init()
82 {
83     m_justInserted = false;
84 }
85 
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,long style,const wxString & name)86 bool wxMDIParentFrame::Create(wxWindow *parent,
87                               wxWindowID id,
88                               const wxString& title,
89                               const wxPoint& pos,
90                               const wxSize& size,
91                               long style,
92                               const wxString& name )
93 {
94     if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
95         return false;
96 
97     m_clientWindow = OnCreateClient();
98     if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
99         return false;
100 
101     return true;
102 }
103 
OnInternalIdle()104 void wxMDIParentFrame::OnInternalIdle()
105 {
106     /* if a MDI child window has just been inserted
107        it has to be brought to the top in idle time. we
108        simply set the last notebook page active as new
109        pages can only be appended at the end */
110 
111     if (m_justInserted)
112     {
113         GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
114         gtk_notebook_set_current_page(notebook, -1);
115 
116         /* need to set the menubar of the child */
117         wxMDIChildFrame *active_child_frame = GetActiveChild();
118         if (active_child_frame != NULL)
119         {
120             wxMenuBar *menu_bar = active_child_frame->m_menuBar;
121             if (menu_bar)
122             {
123                 menu_bar->Attach(active_child_frame);
124             }
125         }
126         m_justInserted = false;
127         return;
128     }
129 
130     wxFrame::OnInternalIdle();
131 
132     wxMDIChildFrame *active_child_frame = GetActiveChild();
133     bool visible_child_menu = false;
134 
135     wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
136     while (node)
137     {
138         wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
139 
140         if ( child_frame )
141         {
142             wxMenuBar *menu_bar = child_frame->m_menuBar;
143             if ( menu_bar )
144             {
145                 if (child_frame == active_child_frame)
146                 {
147                     if (menu_bar->Show(true))
148                     {
149                         // Attach() asserts if we call it for an already
150                         // attached menu bar so don't do it if we're already
151                         // associated with this frame (it would be nice to get
152                         // rid of this check and ensure that this doesn't
153                         // happen...)
154                         if ( menu_bar->GetFrame() != child_frame )
155                             menu_bar->Attach( child_frame );
156                     }
157                     visible_child_menu = true;
158                 }
159                 else
160                 {
161                     if (menu_bar->Show(false))
162                     {
163                         menu_bar->Detach();
164                     }
165                 }
166             }
167         }
168 
169         node = node->GetNext();
170     }
171 
172     /* show/hide parent menu bar as required */
173     if ((m_frameMenuBar) &&
174         (m_frameMenuBar->IsShown() == visible_child_menu))
175     {
176         if (visible_child_menu)
177         {
178             m_frameMenuBar->Show( false );
179             m_frameMenuBar->Detach();
180         }
181         else
182         {
183             m_frameMenuBar->Show( true );
184             m_frameMenuBar->Attach( this );
185         }
186     }
187 }
188 
DoGetClientSize(int * width,int * height) const189 void wxMDIParentFrame::DoGetClientSize(int* width, int* height) const
190 {
191     wxFrame::DoGetClientSize(width, height);
192 
193     if (!m_useCachedClientSize && height)
194     {
195         wxMDIChildFrame* active_child_frame = GetActiveChild();
196         if (active_child_frame)
197         {
198             wxMenuBar* menubar = active_child_frame->m_menuBar;
199             if (menubar && menubar->IsShown())
200             {
201                 GtkRequisition req;
202                 gtk_widget_get_preferred_height(menubar->m_widget, NULL, &req.height);
203                 *height -= req.height;
204                 if (*height < 0) *height = 0;
205             }
206         }
207     }
208 }
209 
GetActiveChild() const210 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
211 {
212     if (!m_clientWindow) return NULL;
213 
214     GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
215     if (!notebook) return NULL;
216 
217     gint i = gtk_notebook_get_current_page( notebook );
218     if (i < 0) return NULL;
219 
220     GtkWidget* page = gtk_notebook_get_nth_page(notebook, i);
221     if (!page) return NULL;
222 
223     wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
224     while (node)
225     {
226         if ( wxPendingDelete.Member(node->GetData()) )
227             return NULL;
228 
229         wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
230 
231         if (!child_frame)
232             return NULL;
233 
234         if (child_frame->m_widget == page)
235             return child_frame;
236 
237         node = node->GetNext();
238     }
239 
240     return NULL;
241 }
242 
ActivateNext()243 void wxMDIParentFrame::ActivateNext()
244 {
245     if (m_clientWindow)
246       gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
247 }
248 
ActivatePrevious()249 void wxMDIParentFrame::ActivatePrevious()
250 {
251     if (m_clientWindow)
252       gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
253 }
254 
255 //-----------------------------------------------------------------------------
256 // wxMDIChildFrame
257 //-----------------------------------------------------------------------------
258 
IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)259 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
260 
261 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
262     EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
263     EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
264 END_EVENT_TABLE()
265 
266 void wxMDIChildFrame::Init()
267 {
268     m_menuBar = NULL;
269 }
270 
Create(wxMDIParentFrame * parent,wxWindowID id,const wxString & title,const wxPoint & WXUNUSED (pos),const wxSize & size,long style,const wxString & name)271 bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
272                              wxWindowID id,
273                              const wxString& title,
274                              const wxPoint& WXUNUSED(pos),
275                              const wxSize& size,
276                              long style,
277                              const wxString& name)
278 {
279     m_mdiParent = parent;
280     m_title = title;
281 
282     return wxWindow::Create(parent->GetClientWindow(), id,
283                             wxDefaultPosition, size,
284                             style, name);
285 }
286 
~wxMDIChildFrame()287 wxMDIChildFrame::~wxMDIChildFrame()
288 {
289     delete m_menuBar;
290 
291     // wxMDIClientWindow does not get redrawn properly after last child is removed
292     if (m_parent && m_parent->GetChildren().size() <= 1)
293         gtk_widget_queue_draw(m_parent->m_widget);
294 }
295 
GTKHandleRealized()296 void wxMDIChildFrame::GTKHandleRealized()
297 {
298     // since m_widget is not a GtkWindow, must bypass wxTopLevelWindowGTK
299     wxTopLevelWindowBase::GTKHandleRealized();
300 }
301 
SetMenuBar(wxMenuBar * menu_bar)302 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
303 {
304     wxASSERT_MSG( m_menuBar == NULL, "Only one menubar allowed" );
305 
306     m_menuBar = menu_bar;
307 
308     if (m_menuBar)
309     {
310         wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
311 
312         m_menuBar->SetParent( mdi_frame );
313 
314         /* insert the invisible menu bar into the _parent_ mdi frame */
315         m_menuBar->Show(false);
316         gtk_box_pack_start(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, false, false, 0);
317         gtk_box_reorder_child(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, 0);
318         gtk_widget_set_size_request(m_menuBar->m_widget, -1, -1);
319     }
320 }
321 
GetMenuBar() const322 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
323 {
324     return m_menuBar;
325 }
326 
GTKGetNotebook() const327 GtkNotebook *wxMDIChildFrame::GTKGetNotebook() const
328 {
329     wxMDIClientWindow * const
330         client = wxStaticCast(GetParent(), wxMDIClientWindow);
331     wxCHECK( client, NULL );
332 
333     return GTK_NOTEBOOK(client->m_widget);
334 }
335 
Activate()336 void wxMDIChildFrame::Activate()
337 {
338     GtkNotebook * const notebook = GTKGetNotebook();
339     wxCHECK_RET( notebook, "no parent notebook?" );
340 
341     gint pageno = gtk_notebook_page_num( notebook, m_widget );
342     gtk_notebook_set_current_page( notebook, pageno );
343 }
344 
OnActivate(wxActivateEvent & WXUNUSED (event))345 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
346 {
347 }
348 
OnMenuHighlight(wxMenuEvent & event)349 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
350 {
351 #if wxUSE_STATUSBAR
352     wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
353     if ( !ShowMenuHelp(event.GetMenuId()) )
354     {
355         // we don't have any help text for this item, but may be the MDI frame
356         // does?
357         mdi_frame->OnMenuHighlight(event);
358     }
359 #endif // wxUSE_STATUSBAR
360 }
361 
SetTitle(const wxString & title)362 void wxMDIChildFrame::SetTitle( const wxString &title )
363 {
364     if ( title == m_title )
365         return;
366 
367     m_title = title;
368 
369     GtkNotebook * const notebook = GTKGetNotebook();
370     wxCHECK_RET( notebook, "no parent notebook?" );
371     gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
372 }
373 
374 //-----------------------------------------------------------------------------
375 // wxMDIClientWindow
376 //-----------------------------------------------------------------------------
377 
IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)378 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
379 
380 wxMDIClientWindow::~wxMDIClientWindow()
381 {
382     // disconnect our handler because our ~wxWindow (which is going to be called
383     // after this dtor) will call DestroyChildren(); in turns our children
384     // ~wxWindow dtors will call wxWindow::Show(false) and this will generate
385     // a call to gtk_mdi_page_change_callback with an invalid parent
386     // (because gtk_mdi_page_change_callback expects a wxMDIClientWindow but
387     //  at that point of the dtor chain we are a simple wxWindow!)
388     g_signal_handlers_disconnect_by_func(m_widget, (void*)switch_page, GetParent());
389 }
390 
CreateClient(wxMDIParentFrame * parent,long style)391 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
392 {
393     if ( !PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
394          !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
395                        style, wxDefaultValidator, "wxMDIClientWindow" ))
396     {
397         wxFAIL_MSG( "wxMDIClientWindow creation failed" );
398         return false;
399     }
400 
401     m_widget = gtk_notebook_new();
402     g_object_ref(m_widget);
403 
404     g_signal_connect(m_widget, "switch_page", G_CALLBACK(switch_page), parent);
405 
406     gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
407 
408     m_parent->DoAddChild( this );
409 
410     PostCreation();
411 
412     Show( true );
413 
414     return true;
415 }
416 
AddChildGTK(wxWindowGTK * child)417 void wxMDIClientWindow::AddChildGTK(wxWindowGTK* child)
418 {
419     wxMDIChildFrame* child_frame = static_cast<wxMDIChildFrame*>(child);
420     wxString s = child_frame->GetTitle();
421     if ( s.empty() )
422         s = _("MDI child");
423 
424     GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
425     gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
426 
427     GtkNotebook* notebook = GTK_NOTEBOOK(m_widget);
428 
429     gtk_notebook_append_page( notebook, child->m_widget, label_widget );
430 
431     wxMDIParentFrame* parent_frame = static_cast<wxMDIParentFrame*>(GetParent());
432     parent_frame->m_justInserted = true;
433 }
434 
435 #endif // wxUSE_MDI
436