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