Split this out from other changes to keep things sane..
[wxWidgets.git] / src / gtk / mdi.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mdi.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "mdi.h"
12 #endif
13
14 #include "wx/mdi.h"
15
16 #if wxUSE_MDI_ARCHITECTURE
17
18 #include "wx/dialog.h"
19 #include "wx/menu.h"
20 #include "wx/intl.h"
21 #include "wx/gtk/private.h"
22
23 #include <glib.h>
24 #include <gdk/gdk.h>
25 #include <gtk/gtk.h>
26 #include "wx/gtk/win_gtk.h"
27
28 //-----------------------------------------------------------------------------
29 // constants
30 //-----------------------------------------------------------------------------
31
32 const int wxMENU_HEIGHT = 27;
33
34 //-----------------------------------------------------------------------------
35 // idle system
36 //-----------------------------------------------------------------------------
37
38 extern void wxapp_install_idle_handler();
39 extern bool g_isIdle;
40
41 //-----------------------------------------------------------------------------
42 // globals
43 //-----------------------------------------------------------------------------
44
45 extern wxList wxPendingDelete;
46
47 //-----------------------------------------------------------------------------
48 // "switch_page"
49 //-----------------------------------------------------------------------------
50
51 static void
52 gtk_mdi_page_change_callback( GtkNotebook *WXUNUSED(widget),
53 GtkNotebookPage *page,
54 gint WXUNUSED(page_num),
55 wxMDIParentFrame *parent )
56 {
57 if (g_isIdle)
58 wxapp_install_idle_handler();
59
60 // send deactivate event to old child
61
62 wxMDIChildFrame *child = parent->GetActiveChild();
63 if (child)
64 {
65 wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
66 event1.SetEventObject( child);
67 child->GetEventHandler()->ProcessEvent( event1 );
68 }
69
70 // send activate event to new child
71
72 wxMDIClientWindow *client_window = parent->GetClientWindow();
73 if (!client_window)
74 return;
75
76 child = (wxMDIChildFrame*) NULL;
77
78 wxWindowList::Node *node = client_window->GetChildren().GetFirst();
79 while (node)
80 {
81 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
82
83 wxASSERT_MSG( child_frame, _T("child is not a wxMDIChildFrame") );
84
85 if (child_frame->m_page == page)
86 {
87 child = child_frame;
88 break;
89 }
90 node = node->GetNext();
91 }
92
93 if (!child)
94 return;
95
96 wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
97 event2.SetEventObject( child);
98 child->GetEventHandler()->ProcessEvent( event2 );
99 }
100
101 //-----------------------------------------------------------------------------
102 // wxMDIParentFrame
103 //-----------------------------------------------------------------------------
104
105 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
106
107 void wxMDIParentFrame::Init()
108 {
109 m_justInserted = false;
110 m_clientWindow = (wxMDIClientWindow *) NULL;
111 }
112
113 wxMDIParentFrame::~wxMDIParentFrame()
114 {
115 }
116
117 bool wxMDIParentFrame::Create(wxWindow *parent,
118 wxWindowID id,
119 const wxString& title,
120 const wxPoint& pos,
121 const wxSize& size,
122 long style,
123 const wxString& name )
124 {
125 wxFrame::Create( parent, id, title, pos, size, style, name );
126
127 OnCreateClient();
128
129 return true;
130 }
131
132 void wxMDIParentFrame::GtkOnSize( int x, int y, int width, int height )
133 {
134 wxFrame::GtkOnSize( x, y, width, height );
135
136 wxMDIChildFrame *child_frame = GetActiveChild();
137 if (!child_frame) return;
138
139 wxMenuBar *menu_bar = child_frame->m_menuBar;
140 if (!menu_bar) return;
141 if (!menu_bar->m_widget) return;
142
143 menu_bar->m_x = 0;
144 menu_bar->m_y = 0;
145 menu_bar->m_width = m_width;
146 menu_bar->m_height = wxMENU_HEIGHT;
147 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
148 menu_bar->m_widget,
149 0, 0, m_width, wxMENU_HEIGHT );
150 }
151
152 void wxMDIParentFrame::OnInternalIdle()
153 {
154 /* if a an MDI child window has just been inserted
155 it has to be brought to the top in idle time. we
156 simply set the last notebook page active as new
157 pages can only be appended at the end */
158
159 if (m_justInserted)
160 {
161 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
162 gtk_notebook_set_page( notebook, g_list_length( notebook->children ) - 1 );
163
164 m_justInserted = false;
165 return;
166 }
167
168 wxFrame::OnInternalIdle();
169
170 wxMDIChildFrame *active_child_frame = GetActiveChild();
171 bool visible_child_menu = false;
172
173 wxWindowList::Node *node = m_clientWindow->GetChildren().GetFirst();
174 while (node)
175 {
176 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
177
178 if ( child_frame )
179 {
180 wxMenuBar *menu_bar = child_frame->m_menuBar;
181 if ( menu_bar )
182 {
183 if (child_frame == active_child_frame)
184 {
185 if (menu_bar->Show(true))
186 {
187 menu_bar->m_width = m_width;
188 menu_bar->m_height = wxMENU_HEIGHT;
189 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
190 menu_bar->m_widget,
191 0, 0, m_width, wxMENU_HEIGHT );
192 menu_bar->SetInvokingWindow( child_frame );
193 }
194 visible_child_menu = true;
195 }
196 else
197 {
198 if (menu_bar->Show(false))
199 {
200 menu_bar->UnsetInvokingWindow( child_frame );
201 }
202 }
203 }
204 }
205
206 node = node->GetNext();
207 }
208
209 /* show/hide parent menu bar as required */
210 if ((m_frameMenuBar) &&
211 (m_frameMenuBar->IsShown() == visible_child_menu))
212 {
213 if (visible_child_menu)
214 {
215 m_frameMenuBar->Show( false );
216 m_frameMenuBar->UnsetInvokingWindow( this );
217 }
218 else
219 {
220 m_frameMenuBar->Show( true );
221 m_frameMenuBar->SetInvokingWindow( this );
222
223 m_frameMenuBar->m_width = m_width;
224 m_frameMenuBar->m_height = wxMENU_HEIGHT;
225 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
226 m_frameMenuBar->m_widget,
227 0, 0, m_width, wxMENU_HEIGHT );
228 }
229 }
230 }
231
232 void wxMDIParentFrame::DoGetClientSize(int *width, int *height ) const
233 {
234 wxFrame::DoGetClientSize( width, height );
235 }
236
237 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
238 {
239 if (!m_clientWindow) return (wxMDIChildFrame*) NULL;
240
241 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
242 if (!notebook) return (wxMDIChildFrame*) NULL;
243
244 gint i = gtk_notebook_get_current_page( notebook );
245 if (i < 0) return (wxMDIChildFrame*) NULL;
246
247 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
248 if (!page) return (wxMDIChildFrame*) NULL;
249
250 wxWindowList::Node *node = m_clientWindow->GetChildren().GetFirst();
251 while (node)
252 {
253 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
254
255 wxASSERT_MSG( child_frame, _T("child is not a wxMDIChildFrame") );
256
257 if (child_frame->m_page == page)
258 return child_frame;
259 node = node->GetNext();
260 }
261
262 return (wxMDIChildFrame*) NULL;
263 }
264
265 wxMDIClientWindow *wxMDIParentFrame::GetClientWindow() const
266 {
267 return m_clientWindow;
268 }
269
270 wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
271 {
272 m_clientWindow = new wxMDIClientWindow( this );
273 return m_clientWindow;
274 }
275
276 void wxMDIParentFrame::ActivateNext()
277 {
278 if (m_clientWindow)
279 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
280 }
281
282 void wxMDIParentFrame::ActivatePrevious()
283 {
284 if (m_clientWindow)
285 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
286 }
287
288 //-----------------------------------------------------------------------------
289 // wxMDIChildFrame
290 //-----------------------------------------------------------------------------
291
292 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
293
294 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
295 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
296 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
297 END_EVENT_TABLE()
298
299 wxMDIChildFrame::wxMDIChildFrame()
300 {
301 m_menuBar = (wxMenuBar *) NULL;
302 m_page = (GtkNotebookPage *) NULL;
303 }
304
305 wxMDIChildFrame::wxMDIChildFrame( wxMDIParentFrame *parent,
306 wxWindowID id, const wxString& title,
307 const wxPoint& WXUNUSED(pos), const wxSize& size,
308 long style, const wxString& name )
309 {
310 m_menuBar = (wxMenuBar *) NULL;
311 m_page = (GtkNotebookPage *) NULL;
312 Create( parent, id, title, wxDefaultPosition, size, style, name );
313 }
314
315 wxMDIChildFrame::~wxMDIChildFrame()
316 {
317 if (m_menuBar)
318 delete m_menuBar;
319 }
320
321 bool wxMDIChildFrame::Create( wxMDIParentFrame *parent,
322 wxWindowID id, const wxString& title,
323 const wxPoint& WXUNUSED(pos), const wxSize& size,
324 long style, const wxString& name )
325 {
326 m_title = title;
327
328 return wxWindow::Create( parent->GetClientWindow(), id, wxDefaultPosition, size, style, name );
329 }
330
331 void wxMDIChildFrame::DoSetSize( int x, int y, int width, int height, int sizeFlags )
332 {
333 wxWindow::DoSetSize( x, y, width, height, sizeFlags );
334 }
335
336 void wxMDIChildFrame::DoSetClientSize(int width, int height)
337 {
338 wxWindow::DoSetClientSize( width, height );
339 }
340
341 void wxMDIChildFrame::DoGetClientSize( int *width, int *height ) const
342 {
343 wxWindow::DoGetClientSize( width, height );
344 }
345
346 void wxMDIChildFrame::AddChild( wxWindowBase *child )
347 {
348 wxWindow::AddChild(child);
349 }
350
351 void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
352 {
353 wxASSERT_MSG( m_menuBar == NULL, wxT("Only one menubar allowed") );
354
355 m_menuBar = menu_bar;
356
357 if (m_menuBar)
358 {
359 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
360
361 m_menuBar->SetParent( mdi_frame );
362
363 /* insert the invisible menu bar into the _parent_ mdi frame */
364 gtk_pizza_put( GTK_PIZZA(mdi_frame->m_mainWidget),
365 m_menuBar->m_widget,
366 0, 0, mdi_frame->m_width, wxMENU_HEIGHT );
367 }
368 }
369
370 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
371 {
372 return m_menuBar;
373 }
374
375 void wxMDIChildFrame::Activate()
376 {
377 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
378 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
379 gint pageno = gtk_notebook_page_num( notebook, m_widget );
380 gtk_notebook_set_page( notebook, pageno );
381 }
382
383 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
384 {
385 }
386
387 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
388 {
389 #if wxUSE_STATUSBAR
390 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
391 if ( !ShowMenuHelp(mdi_frame->GetStatusBar(), event.GetMenuId()) )
392 {
393 // we don't have any help text for this item, but may be the MDI frame
394 // does?
395 mdi_frame->OnMenuHighlight(event);
396 }
397 #endif // wxUSE_STATUSBAR
398 }
399
400 void wxMDIChildFrame::SetTitle( const wxString &title )
401 {
402 if ( title == m_title )
403 return;
404
405 m_title = title;
406
407 wxMDIParentFrame* parent = (wxMDIParentFrame*) GetParent();
408 GtkNotebook* notebook = GTK_NOTEBOOK(parent->m_widget);
409 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
410 }
411
412 //-----------------------------------------------------------------------------
413 // "size_allocate"
414 //-----------------------------------------------------------------------------
415
416 static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
417 {
418 if (g_isIdle) wxapp_install_idle_handler();
419
420 if ((win->m_x == alloc->x) &&
421 (win->m_y == alloc->y) &&
422 (win->m_width == alloc->width) &&
423 (win->m_height == alloc->height) &&
424 (win->m_sizeSet))
425 {
426 return;
427 }
428
429 win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
430 }
431
432 //-----------------------------------------------------------------------------
433 // InsertChild callback for wxMDIClientWindow
434 //-----------------------------------------------------------------------------
435
436 static void wxInsertChildInMDI( wxMDIClientWindow* parent, wxMDIChildFrame* child )
437 {
438 wxString s = child->m_title;
439 if (s.IsNull()) s = _("MDI child");
440
441 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
442 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
443
444 gtk_signal_connect( GTK_OBJECT(child->m_widget), "size_allocate",
445 GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)child );
446
447 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
448
449 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
450
451 child->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
452
453 wxMDIParentFrame *parent_frame = (wxMDIParentFrame*) parent->GetParent();
454 parent_frame->m_justInserted = true;
455 }
456
457 //-----------------------------------------------------------------------------
458 // wxMDIClientWindow
459 //-----------------------------------------------------------------------------
460
461 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)
462
463 wxMDIClientWindow::wxMDIClientWindow()
464 {
465 }
466
467 wxMDIClientWindow::wxMDIClientWindow( wxMDIParentFrame *parent, long style )
468 {
469 CreateClient( parent, style );
470 }
471
472 wxMDIClientWindow::~wxMDIClientWindow()
473 {
474 }
475
476 bool wxMDIClientWindow::CreateClient( wxMDIParentFrame *parent, long style )
477 {
478 m_needParent = true;
479
480 m_insertCallback = (wxInsertChildFunction)wxInsertChildInMDI;
481
482 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
483 !CreateBase( parent, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("wxMDIClientWindow") ))
484 {
485 wxFAIL_MSG( wxT("wxMDIClientWindow creation failed") );
486 return false;
487 }
488
489 m_widget = gtk_notebook_new();
490
491 gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
492 GTK_SIGNAL_FUNC(gtk_mdi_page_change_callback), (gpointer)parent );
493
494 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
495
496 m_parent->DoAddChild( this );
497
498 PostCreation();
499
500 Show( true );
501
502 return true;
503 }
504
505 #endif