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