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