Review/simplify/cleanup MDI classes for all platforms and introduce base
[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 #endif
21
22 #include "wx/gtk/private.h"
23
24 //-----------------------------------------------------------------------------
25 // "switch_page"
26 //-----------------------------------------------------------------------------
27
28 extern "C" {
29 static void
30 gtk_mdi_page_change_callback( GtkNotebook *WXUNUSED(widget),
31 GtkNotebookPage *page,
32 gint WXUNUSED(page_num),
33 wxMDIParentFrame *parent )
34 {
35 // send deactivate event to old child
36
37 wxMDIChildFrame *child = parent->GetActiveChild();
38 if (child)
39 {
40 wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
41 event1.SetEventObject( child);
42 child->HandleWindowEvent( event1 );
43 }
44
45 // send activate event to new child
46
47 wxMDIClientWindowBase *client_window = parent->GetClientWindow();
48 if ( !client_window )
49 return;
50
51 child = NULL;
52
53 wxWindowList::compatibility_iterator node = client_window->GetChildren().GetFirst();
54 while ( node )
55 {
56 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
57
58 // child_frame can be NULL when this is called from dtor, probably
59 // because g_signal_connect (m_widget, "switch_page", (see below)
60 // isn't deleted early enough
61 if ( child_frame && child_frame->m_page == page )
62 {
63 child = child_frame;
64 break;
65 }
66 node = node->GetNext();
67 }
68
69 if (!child)
70 return;
71
72 wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
73 event2.SetEventObject( child);
74 child->HandleWindowEvent( event2 );
75 }
76 }
77
78 //-----------------------------------------------------------------------------
79 // wxMDIParentFrame
80 //-----------------------------------------------------------------------------
81
82 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
83
84 void wxMDIParentFrame::Init()
85 {
86 m_justInserted = false;
87 }
88
89 bool wxMDIParentFrame::Create(wxWindow *parent,
90 wxWindowID id,
91 const wxString& title,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxString& name )
96 {
97 if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
98 return false;
99
100 m_clientWindow = OnCreateClient();
101 if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
102 return false;
103
104 return true;
105 }
106
107 void wxMDIParentFrame::OnInternalIdle()
108 {
109 /* if a MDI child window has just been inserted
110 it has to be brought to the top in idle time. we
111 simply set the last notebook page active as new
112 pages can only be appended at the end */
113
114 if (m_justInserted)
115 {
116 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
117 gtk_notebook_set_current_page( notebook, g_list_length( notebook->children ) - 1 );
118
119 /* need to set the menubar of the child */
120 wxMDIChildFrame *active_child_frame = GetActiveChild();
121 if (active_child_frame != NULL)
122 {
123 wxMenuBar *menu_bar = active_child_frame->m_menuBar;
124 if (menu_bar)
125 {
126 menu_bar->SetInvokingWindow(active_child_frame);
127 }
128 }
129 m_justInserted = false;
130 return;
131 }
132
133 wxFrame::OnInternalIdle();
134
135 wxMDIChildFrame *active_child_frame = GetActiveChild();
136 bool visible_child_menu = false;
137
138 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
139 while (node)
140 {
141 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
142
143 if ( child_frame )
144 {
145 wxMenuBar *menu_bar = child_frame->m_menuBar;
146 if ( menu_bar )
147 {
148 if (child_frame == active_child_frame)
149 {
150 if (menu_bar->Show(true))
151 {
152 menu_bar->SetInvokingWindow( child_frame );
153 }
154 visible_child_menu = true;
155 }
156 else
157 {
158 if (menu_bar->Show(false))
159 {
160 menu_bar->UnsetInvokingWindow( child_frame );
161 }
162 }
163 }
164 }
165
166 node = node->GetNext();
167 }
168
169 /* show/hide parent menu bar as required */
170 if ((m_frameMenuBar) &&
171 (m_frameMenuBar->IsShown() == visible_child_menu))
172 {
173 if (visible_child_menu)
174 {
175 m_frameMenuBar->Show( false );
176 m_frameMenuBar->UnsetInvokingWindow( this );
177 }
178 else
179 {
180 m_frameMenuBar->Show( true );
181 m_frameMenuBar->SetInvokingWindow( this );
182 }
183 }
184 }
185
186 void wxMDIParentFrame::DoGetClientSize(int* width, int* height) const
187 {
188 wxFrame::DoGetClientSize(width, height);
189
190 if (height)
191 {
192 wxMDIChildFrame* active_child_frame = GetActiveChild();
193 if (active_child_frame)
194 {
195 wxMenuBar* menubar = active_child_frame->m_menuBar;
196 if (menubar && menubar->IsShown())
197 {
198 GtkRequisition req;
199 gtk_widget_size_request(menubar->m_widget, &req);
200 *height -= req.height;
201 if (*height < 0) *height = 0;
202 }
203 }
204 }
205 }
206
207 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
208 {
209 if (!m_clientWindow) return NULL;
210
211 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
212 if (!notebook) return NULL;
213
214 gint i = gtk_notebook_get_current_page( notebook );
215 if (i < 0) return NULL;
216
217 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
218 if (!page) return NULL;
219
220 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
221 while (node)
222 {
223 if ( wxPendingDelete.Member(node->GetData()) )
224 return NULL;
225
226 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
227
228 if (!child_frame)
229 return NULL;
230
231 if (child_frame->m_page == page)
232 return child_frame;
233
234 node = node->GetNext();
235 }
236
237 return NULL;
238 }
239
240 void wxMDIParentFrame::ActivateNext()
241 {
242 if (m_clientWindow)
243 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
244 }
245
246 void wxMDIParentFrame::ActivatePrevious()
247 {
248 if (m_clientWindow)
249 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
250 }
251
252 //-----------------------------------------------------------------------------
253 // wxMDIChildFrame
254 //-----------------------------------------------------------------------------
255
256 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
257
258 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
259 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
260 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
261 END_EVENT_TABLE()
262
263 void wxMDIChildFrame::Init()
264 {
265 m_menuBar = NULL;
266 m_page = NULL;
267 }
268
269 bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
270 wxWindowID id,
271 const wxString& title,
272 const wxPoint& WXUNUSED(pos),
273 const wxSize& size,
274 long style,
275 const wxString& name)
276 {
277 m_mdiParent = parent;
278 m_title = title;
279
280 return wxWindow::Create(parent->GetClientWindow(), id,
281 wxDefaultPosition, size,
282 style, name);
283 }
284
285 wxMDIChildFrame::~wxMDIChildFrame()
286 {
287 delete m_menuBar;
288
289 // wxMDIClientWindow does not get redrawn properly after last child is removed
290 if (m_parent && m_parent->GetChildren().size() <= 1)
291 gtk_widget_queue_draw(m_parent->m_widget);
292 }
293
294 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
295 {
296 wxASSERT_MSG( m_menuBar == NULL, "Only one menubar allowed" );
297
298 m_menuBar = menu_bar;
299
300 if (m_menuBar)
301 {
302 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
303
304 m_menuBar->SetParent( mdi_frame );
305
306 /* insert the invisible menu bar into the _parent_ mdi frame */
307 m_menuBar->Show(false);
308 gtk_box_pack_start(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, false, false, 0);
309 gtk_box_reorder_child(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, 0);
310
311 gulong handler_id = g_signal_handler_find(
312 m_menuBar->m_widget,
313 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
314 g_signal_lookup("size_request", GTK_TYPE_WIDGET),
315 0, NULL, NULL, m_menuBar);
316 if (handler_id != 0)
317 g_signal_handler_disconnect(m_menuBar->m_widget, handler_id);
318 gtk_widget_set_size_request(m_menuBar->m_widget, -1, -1);
319 }
320 }
321
322 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
323 {
324 return m_menuBar;
325 }
326
327 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
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
345 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
346 {
347 }
348
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
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
378 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
379
380 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
381 {
382 if ( !PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
383 !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
384 style, wxDefaultValidator, "wxMDIClientWindow" ))
385 {
386 wxFAIL_MSG( "wxMDIClientWindow creation failed" );
387 return false;
388 }
389
390 m_widget = gtk_notebook_new();
391 g_object_ref(m_widget);
392
393 g_signal_connect (m_widget, "switch_page",
394 G_CALLBACK (gtk_mdi_page_change_callback), parent);
395
396 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
397
398 m_parent->DoAddChild( this );
399
400 PostCreation();
401
402 Show( true );
403
404 return true;
405 }
406
407 void wxMDIClientWindow::AddChildGTK(wxWindowGTK* child)
408 {
409 wxMDIChildFrame* child_frame = static_cast<wxMDIChildFrame*>(child);
410 wxString s = child_frame->GetTitle();
411 if ( s.empty() )
412 s = _("MDI child");
413
414 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
415 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
416
417 GtkNotebook* notebook = GTK_NOTEBOOK(m_widget);
418
419 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
420
421 child_frame->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
422
423 wxMDIParentFrame* parent_frame = static_cast<wxMDIParentFrame*>(GetParent());
424 parent_frame->m_justInserted = true;
425 }
426
427 #endif // wxUSE_MDI