Use a GtkVBox to do TLW layout. Rework some of the remaining sizing code.
[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->GetEventHandler()->ProcessEvent( event1 );
43 }
44
45 // send activate event to new child
46
47 wxMDIClientWindow *client_window = parent->GetClientWindow();
48 if (!client_window)
49 return;
50
51 child = (wxMDIChildFrame*) 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->GetEventHandler()->ProcessEvent( 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 m_clientWindow = (wxMDIClientWindow *) NULL;
88 }
89
90 wxMDIParentFrame::~wxMDIParentFrame()
91 {
92 }
93
94 bool wxMDIParentFrame::Create(wxWindow *parent,
95 wxWindowID id,
96 const wxString& title,
97 const wxPoint& pos,
98 const wxSize& size,
99 long style,
100 const wxString& name )
101 {
102 wxFrame::Create( parent, id, title, pos, size, style, name );
103
104 OnCreateClient();
105
106 return true;
107 }
108
109 void wxMDIParentFrame::OnInternalIdle()
110 {
111 /* if a an MDI child window has just been inserted
112 it has to be brought to the top in idle time. we
113 simply set the last notebook page active as new
114 pages can only be appended at the end */
115
116 if (m_justInserted)
117 {
118 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
119 gtk_notebook_set_current_page( notebook, g_list_length( notebook->children ) - 1 );
120
121 /* need to set the menubar of the child */
122 wxMDIChildFrame *active_child_frame = GetActiveChild();
123 if (active_child_frame != NULL)
124 {
125 wxMenuBar *menu_bar = active_child_frame->m_menuBar;
126 if (menu_bar)
127 {
128 menu_bar->SetInvokingWindow(active_child_frame);
129 }
130 }
131 m_justInserted = false;
132 return;
133 }
134
135 wxFrame::OnInternalIdle();
136
137 wxMDIChildFrame *active_child_frame = GetActiveChild();
138 bool visible_child_menu = false;
139
140 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
141 while (node)
142 {
143 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
144
145 if ( child_frame )
146 {
147 wxMenuBar *menu_bar = child_frame->m_menuBar;
148 if ( menu_bar )
149 {
150 if (child_frame == active_child_frame)
151 {
152 if (menu_bar->Show(true))
153 {
154 menu_bar->SetInvokingWindow( child_frame );
155 }
156 visible_child_menu = true;
157 }
158 else
159 {
160 if (menu_bar->Show(false))
161 {
162 menu_bar->UnsetInvokingWindow( child_frame );
163 }
164 }
165 }
166 }
167
168 node = node->GetNext();
169 }
170
171 /* show/hide parent menu bar as required */
172 if ((m_frameMenuBar) &&
173 (m_frameMenuBar->IsShown() == visible_child_menu))
174 {
175 if (visible_child_menu)
176 {
177 m_frameMenuBar->Show( false );
178 m_frameMenuBar->UnsetInvokingWindow( this );
179 }
180 else
181 {
182 m_frameMenuBar->Show( true );
183 m_frameMenuBar->SetInvokingWindow( this );
184 }
185 }
186 }
187
188 void wxMDIParentFrame::DoGetClientSize(int* width, int* height) const
189 {
190 wxFrame::DoGetClientSize(width, height);
191
192 if (height)
193 {
194 wxMDIChildFrame* active_child_frame = GetActiveChild();
195 if (active_child_frame)
196 {
197 wxMenuBar* menubar = active_child_frame->m_menuBar;
198 if (menubar && menubar->IsShown())
199 {
200 GtkRequisition req;
201 gtk_widget_size_request(menubar->m_widget, &req);
202 *height -= req.height;
203 if (*height < 0) *height = 0;
204 }
205 }
206 }
207 }
208
209 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
210 {
211 if (!m_clientWindow) return (wxMDIChildFrame*) NULL;
212
213 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
214 if (!notebook) return (wxMDIChildFrame*) NULL;
215
216 gint i = gtk_notebook_get_current_page( notebook );
217 if (i < 0) return (wxMDIChildFrame*) NULL;
218
219 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
220 if (!page) return (wxMDIChildFrame*) NULL;
221
222 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
223 while (node)
224 {
225 if ( wxPendingDelete.Member(node->GetData()) )
226 return (wxMDIChildFrame*) NULL;
227
228 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
229
230 if (!child_frame)
231 return (wxMDIChildFrame*) NULL;
232
233 if (child_frame->m_page == page)
234 return child_frame;
235
236 node = node->GetNext();
237 }
238
239 return (wxMDIChildFrame*) NULL;
240 }
241
242 wxMDIClientWindow *wxMDIParentFrame::GetClientWindow() const
243 {
244 return m_clientWindow;
245 }
246
247 wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
248 {
249 m_clientWindow = new wxMDIClientWindow( this );
250 return m_clientWindow;
251 }
252
253 void wxMDIParentFrame::ActivateNext()
254 {
255 if (m_clientWindow)
256 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
257 }
258
259 void wxMDIParentFrame::ActivatePrevious()
260 {
261 if (m_clientWindow)
262 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
263 }
264
265 //-----------------------------------------------------------------------------
266 // wxMDIChildFrame
267 //-----------------------------------------------------------------------------
268
269 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
270
271 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
272 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
273 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
274 END_EVENT_TABLE()
275
276 wxMDIChildFrame::wxMDIChildFrame()
277 {
278 m_menuBar = (wxMenuBar *) NULL;
279 m_page = (GtkNotebookPage *) NULL;
280 }
281
282 wxMDIChildFrame::wxMDIChildFrame( wxMDIParentFrame *parent,
283 wxWindowID id, const wxString& title,
284 const wxPoint& WXUNUSED(pos), const wxSize& size,
285 long style, const wxString& name )
286 {
287 m_menuBar = (wxMenuBar *) NULL;
288 m_page = (GtkNotebookPage *) NULL;
289 Create( parent, id, title, wxDefaultPosition, size, style, name );
290 }
291
292 wxMDIChildFrame::~wxMDIChildFrame()
293 {
294 if (m_menuBar)
295 delete m_menuBar;
296 }
297
298 bool wxMDIChildFrame::Create( wxMDIParentFrame *parent,
299 wxWindowID id, const wxString& title,
300 const wxPoint& WXUNUSED(pos), const wxSize& size,
301 long style, const wxString& name )
302 {
303 m_title = title;
304
305 return wxWindow::Create( parent->GetClientWindow(), id, wxDefaultPosition, size, style, name );
306 }
307
308 bool wxMDIChildFrame::Destroy()
309 {
310 // delayed destruction: the frame will be deleted during
311 // the next idle loop iteration.
312 // I'm not sure if delayed destruction really makes so
313 // much sense for MDI child frames, actually, but hiding
314 // it doesn't make any sense.
315 if ( !wxPendingDelete.Member(this) )
316 wxPendingDelete.Append(this);
317
318 return true;
319 }
320
321 void wxMDIChildFrame::DoSetSize( int x, int y, int width, int height, int sizeFlags )
322 {
323 wxWindow::DoSetSize( x, y, width, height, sizeFlags );
324 }
325
326 void wxMDIChildFrame::AddChild( wxWindowBase *child )
327 {
328 wxWindow::AddChild(child);
329 }
330
331 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
332 {
333 wxASSERT_MSG( m_menuBar == NULL, wxT("Only one menubar allowed") );
334
335 m_menuBar = menu_bar;
336
337 if (m_menuBar)
338 {
339 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
340
341 m_menuBar->SetParent( mdi_frame );
342
343 /* insert the invisible menu bar into the _parent_ mdi frame */
344 m_menuBar->Show(false);
345 gtk_box_pack_start(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, false, false, 0);
346 gtk_box_reorder_child(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, 0);
347
348 gulong handler_id = g_signal_handler_find(
349 m_menuBar->m_widget,
350 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
351 g_signal_lookup("size_request", GTK_TYPE_WIDGET),
352 0, NULL, NULL, m_menuBar);
353 if (handler_id != 0)
354 g_signal_handler_disconnect(m_menuBar->m_widget, handler_id);
355 gtk_widget_set_size_request(m_menuBar->m_widget, -1, -1);
356 }
357 }
358
359 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
360 {
361 return m_menuBar;
362 }
363
364 void wxMDIChildFrame::Activate()
365 {
366 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
367 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
368 gint pageno = gtk_notebook_page_num( notebook, m_widget );
369 gtk_notebook_set_current_page( notebook, pageno );
370 }
371
372 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
373 {
374 }
375
376 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
377 {
378 #if wxUSE_STATUSBAR
379 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
380 if ( !ShowMenuHelp(event.GetMenuId()) )
381 {
382 // we don't have any help text for this item, but may be the MDI frame
383 // does?
384 mdi_frame->OnMenuHighlight(event);
385 }
386 #endif // wxUSE_STATUSBAR
387 }
388
389 void wxMDIChildFrame::SetTitle( const wxString &title )
390 {
391 if ( title == m_title )
392 return;
393
394 m_title = title;
395
396 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
397 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
398 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
399 }
400
401 //-----------------------------------------------------------------------------
402 // InsertChild callback for wxMDIClientWindow
403 //-----------------------------------------------------------------------------
404
405 static void wxInsertChildInMDI(wxWindow* parent, wxWindow* child)
406 {
407 wxMDIChildFrame* child_frame = wx_static_cast(wxMDIChildFrame*, child);
408 wxString s = child_frame->GetTitle();
409 if (s.IsNull()) s = _("MDI child");
410
411 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
412 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
413
414 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
415
416 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
417
418 child_frame->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
419
420 wxMDIParentFrame *parent_frame = wx_static_cast(wxMDIParentFrame*, parent->GetParent());
421 parent_frame->m_justInserted = true;
422 }
423
424 //-----------------------------------------------------------------------------
425 // wxMDIClientWindow
426 //-----------------------------------------------------------------------------
427
428 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)
429
430 wxMDIClientWindow::wxMDIClientWindow()
431 {
432 }
433
434 wxMDIClientWindow::wxMDIClientWindow( wxMDIParentFrame *parent, long style )
435 {
436 CreateClient( parent, style );
437 }
438
439 wxMDIClientWindow::~wxMDIClientWindow()
440 {
441
442 }
443
444 bool wxMDIClientWindow::CreateClient( wxMDIParentFrame *parent, long style )
445 {
446 m_insertCallback = wxInsertChildInMDI;
447
448 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
449 !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("wxMDIClientWindow") ))
450 {
451 wxFAIL_MSG( wxT("wxMDIClientWindow creation failed") );
452 return false;
453 }
454
455 m_widget = gtk_notebook_new();
456
457 g_signal_connect (m_widget, "switch_page",
458 G_CALLBACK (gtk_mdi_page_change_callback), parent);
459
460 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
461
462 m_parent->DoAddChild( this );
463
464 PostCreation();
465
466 Show( true );
467
468 return true;
469 }
470
471 #endif // wxUSE_MDI