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