Rewrite wxExecute() implementation under Unix.
[wxWidgets.git] / src / gtk1 / mdi.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/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 #include "wx/dialog.h"
21 #endif
22
23 #include "wx/notebook.h"
24 #include "wx/gtk1/private.h"
25
26 #include <glib.h>
27 #include <gdk/gdk.h>
28 #include <gtk/gtk.h>
29 #include "wx/gtk1/win_gtk.h"
30
31 //-----------------------------------------------------------------------------
32 // constants
33 //-----------------------------------------------------------------------------
34
35 const int wxMENU_HEIGHT = 27;
36
37 //-----------------------------------------------------------------------------
38 // idle system
39 //-----------------------------------------------------------------------------
40
41 extern void wxapp_install_idle_handler();
42 extern bool g_isIdle;
43
44 //-----------------------------------------------------------------------------
45 // globals
46 //-----------------------------------------------------------------------------
47
48 //-----------------------------------------------------------------------------
49 // "switch_page"
50 //-----------------------------------------------------------------------------
51
52 extern "C" {
53 static void
54 gtk_mdi_page_change_callback( GtkNotebook *WXUNUSED(widget),
55 GtkNotebookPage *page,
56 gint WXUNUSED(page_num),
57 wxMDIParentFrame *parent )
58 {
59 if (g_isIdle)
60 wxapp_install_idle_handler();
61
62 // send deactivate event to old child
63
64 wxMDIChildFrame *child = parent->GetActiveChild();
65 if (child)
66 {
67 wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
68 event1.SetEventObject( child);
69 child->HandleWindowEvent( event1 );
70 }
71
72 // send activate event to new child
73
74 wxMDIClientWindowBase * const client_window = parent->GetClientWindow();
75 if ( !client_window )
76 return;
77
78 child = NULL;
79
80 wxWindowList::compatibility_iterator node = client_window->GetChildren().GetFirst();
81 while (node)
82 {
83 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
84 // CE: we come here in the destructor with a null child_frame - I think because
85 // gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page", (see below)
86 // isn't deleted early enough
87 if (!child_frame)
88 return ;
89
90 if (child_frame->m_page == page)
91 {
92 child = child_frame;
93 break;
94 }
95 node = node->GetNext();
96 }
97
98 if (!child)
99 return;
100
101 wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
102 event2.SetEventObject( child);
103 child->HandleWindowEvent( event2 );
104 }
105 }
106
107 //-----------------------------------------------------------------------------
108 // wxMDIParentFrame
109 //-----------------------------------------------------------------------------
110
111 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
112
113 void wxMDIParentFrame::Init()
114 {
115 m_justInserted = false;
116 m_clientWindow = NULL;
117 }
118
119 bool wxMDIParentFrame::Create(wxWindow *parent,
120 wxWindowID id,
121 const wxString& title,
122 const wxPoint& pos,
123 const wxSize& size,
124 long style,
125 const wxString& name )
126 {
127 if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
128 return false;
129
130 m_clientWindow = OnCreateClient();
131 if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
132 return false;
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->Attach(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
213 // Attach() asserts if we call it for an already
214 // attached menu bar so don't do it if we're already
215 // associated with this frame (it would be nice to get
216 // rid of this check and ensure that this doesn't
217 // happen...)
218 if ( menu_bar->GetFrame() != child_frame )
219 menu_bar->Attach( child_frame );
220 }
221 visible_child_menu = true;
222 }
223 else
224 {
225 if (menu_bar->Show(false))
226 {
227 menu_bar->Detach();
228 }
229 }
230 }
231 }
232
233 node = node->GetNext();
234 }
235
236 /* show/hide parent menu bar as required */
237 if ((m_frameMenuBar) &&
238 (m_frameMenuBar->IsShown() == visible_child_menu))
239 {
240 if (visible_child_menu)
241 {
242 m_frameMenuBar->Show( false );
243 m_frameMenuBar->Detach();
244 }
245 else
246 {
247 m_frameMenuBar->Show( true );
248 m_frameMenuBar->Attach( this );
249
250 m_frameMenuBar->m_width = m_width;
251 m_frameMenuBar->m_height = wxMENU_HEIGHT;
252 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget),
253 m_frameMenuBar->m_widget,
254 0, 0, m_width, wxMENU_HEIGHT );
255 }
256 }
257 }
258
259 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
260 {
261 if (!m_clientWindow) return NULL;
262
263 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
264 if (!notebook) return NULL;
265
266 gint i = gtk_notebook_get_current_page( notebook );
267 if (i < 0) return NULL;
268
269 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
270 if (!page) return NULL;
271
272 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
273 while (node)
274 {
275 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
276
277 wxASSERT_MSG( child_frame, wxT("child is not a wxMDIChildFrame") );
278
279 if (child_frame->m_page == page)
280 return child_frame;
281 node = node->GetNext();
282 }
283
284 return NULL;
285 }
286
287 void wxMDIParentFrame::ActivateNext()
288 {
289 if (m_clientWindow)
290 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
291 }
292
293 void wxMDIParentFrame::ActivatePrevious()
294 {
295 if (m_clientWindow)
296 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
297 }
298
299 //-----------------------------------------------------------------------------
300 // wxMDIChildFrame
301 //-----------------------------------------------------------------------------
302
303 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
304
305 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
306 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
307 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
308 END_EVENT_TABLE()
309
310 void wxMDIChildFrame::Init()
311 {
312 m_menuBar = NULL;
313 m_page = NULL;
314 }
315
316 bool wxMDIChildFrame::Create( wxMDIParentFrame *parent,
317 wxWindowID id, const wxString& title,
318 const wxPoint& WXUNUSED(pos), const wxSize& size,
319 long style, const wxString& name )
320 {
321 m_title = title;
322
323 return wxWindow::Create( parent->GetClientWindow(), id, wxDefaultPosition, size, style, name );
324 }
325
326 wxMDIChildFrame::~wxMDIChildFrame()
327 {
328 delete m_menuBar;
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 gtk_pizza_put( GTK_PIZZA(mdi_frame->m_mainWidget),
345 m_menuBar->m_widget,
346 0, 0, mdi_frame->m_width, wxMENU_HEIGHT );
347 }
348 }
349
350 wxMenuBar *wxMDIChildFrame::GetMenuBar() const
351 {
352 return m_menuBar;
353 }
354
355 GtkNotebook *wxMDIChildFrame::GTKGetNotebook() const
356 {
357 wxMDIClientWindow * const
358 client = wxStaticCast(GetParent(), wxMDIClientWindow);
359 wxCHECK( client, NULL );
360
361 return GTK_NOTEBOOK(client->m_widget);
362 }
363
364 void wxMDIChildFrame::Activate()
365 {
366 GtkNotebook * const notebook = GTKGetNotebook();
367 wxCHECK_RET( notebook, "no parent notebook?" );
368
369 gint pageno = gtk_notebook_page_num( notebook, m_widget );
370 gtk_notebook_set_page( notebook, pageno );
371 }
372
373 void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
374 {
375 }
376
377 void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
378 {
379 #if wxUSE_STATUSBAR
380 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
381 if ( !ShowMenuHelp(event.GetMenuId()) )
382 {
383 // we don't have any help text for this item, but may be the MDI frame
384 // does?
385 mdi_frame->OnMenuHighlight(event);
386 }
387 #endif // wxUSE_STATUSBAR
388 }
389
390 void wxMDIChildFrame::SetTitle( const wxString &title )
391 {
392 if ( title == m_title )
393 return;
394
395 m_title = title;
396
397 GtkNotebook * const notebook = GTKGetNotebook();
398 wxCHECK_RET( notebook, "no parent notebook?" );
399
400 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
401 }
402
403 //-----------------------------------------------------------------------------
404 // "size_allocate"
405 //-----------------------------------------------------------------------------
406
407 extern "C" {
408 static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
409 {
410 if (g_isIdle) wxapp_install_idle_handler();
411
412 if ((win->m_x == alloc->x) &&
413 (win->m_y == alloc->y) &&
414 (win->m_width == alloc->width) &&
415 (win->m_height == alloc->height) &&
416 (win->m_sizeSet))
417 {
418 return;
419 }
420
421 win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
422 }
423 }
424
425 //-----------------------------------------------------------------------------
426 // InsertChild callback for wxMDIClientWindow
427 //-----------------------------------------------------------------------------
428
429 static void wxInsertChildInMDI( wxMDIClientWindow* parent, wxMDIChildFrame* child )
430 {
431 wxString s = child->GetTitle();
432 if ( s.empty() ) s = _("MDI child");
433
434 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
435 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
436
437 gtk_signal_connect( GTK_OBJECT(child->m_widget), "size_allocate",
438 GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)child );
439
440 GtkNotebook *notebook = GTK_NOTEBOOK(parent->m_widget);
441
442 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
443
444 child->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
445
446 wxMDIParentFrame *parent_frame = (wxMDIParentFrame*) parent->GetParent();
447 parent_frame->m_justInserted = true;
448 }
449
450 //-----------------------------------------------------------------------------
451 // wxMDIClientWindow
452 //-----------------------------------------------------------------------------
453
454 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow,wxWindow)
455
456 bool wxMDIClientWindow::CreateClient( wxMDIParentFrame *parent, long style )
457 {
458 m_needParent = true;
459
460 m_insertCallback = (wxInsertChildFunction)wxInsertChildInMDI;
461
462 if (!PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
463 !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("wxMDIClientWindow") ))
464 {
465 wxFAIL_MSG( wxT("wxMDIClientWindow creation failed") );
466 return false;
467 }
468
469 m_widget = gtk_notebook_new();
470
471 gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
472 GTK_SIGNAL_FUNC(gtk_mdi_page_change_callback), (gpointer)parent );
473
474 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
475
476 m_parent->DoAddChild( this );
477
478 PostCreation();
479
480 Show( true );
481
482 return true;
483 }
484
485 #endif