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