]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/mdi.cpp
Fix Tab navigation when focused control is disabled.
[wxWidgets.git] / src / gtk / mdi.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
72c23f8e 2// Name: src/gtk/mdi.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
a81258be
RR
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
88a7a4e1
WS
13#if wxUSE_MDI
14
c801d85f 15#include "wx/mdi.h"
dcf924a3 16
88a7a4e1
WS
17#ifndef WX_PRECOMP
18 #include "wx/intl.h"
3b3dc801 19 #include "wx/menu.h"
88a7a4e1 20#endif
dcf924a3 21
fab591c5 22#include "wx/gtk/private.h"
716b7364 23
5e014a0c
RR
24//-----------------------------------------------------------------------------
25// "switch_page"
26//-----------------------------------------------------------------------------
27
865bb325 28extern "C" {
f6bcfd97 29static void
7941ba11 30gtk_mdi_page_change_callback( GtkNotebook *WXUNUSED(widget),
e90196a5 31 GtkNotebookPage *page,
f6bcfd97
BP
32 gint WXUNUSED(page_num),
33 wxMDIParentFrame *parent )
5e014a0c 34{
e90196a5
RR
35 // send deactivate event to old child
36
5e014a0c 37 wxMDIChildFrame *child = parent->GetActiveChild();
e90196a5
RR
38 if (child)
39 {
b1d4dd7a 40 wxActivateEvent event1( wxEVT_ACTIVATE, false, child->GetId() );
e90196a5 41 event1.SetEventObject( child);
937013e0 42 child->HandleWindowEvent( event1 );
e90196a5 43 }
f6bcfd97 44
e90196a5 45 // send activate event to new child
f6bcfd97 46
d2824cdb
VZ
47 wxMDIClientWindowBase *client_window = parent->GetClientWindow();
48 if ( !client_window )
e90196a5
RR
49 return;
50
d2824cdb 51 child = NULL;
e90196a5 52
222ed1d6 53 wxWindowList::compatibility_iterator node = client_window->GetChildren().GetFirst();
af6c0f1d 54 while ( node )
e90196a5 55 {
b1d4dd7a 56 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
b1d4dd7a 57
af6c0f1d
VZ
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 )
f6bcfd97 62 {
e90196a5 63 child = child_frame;
f6bcfd97
BP
64 break;
65 }
b1d4dd7a 66 node = node->GetNext();
e90196a5 67 }
f6bcfd97 68
e90196a5
RR
69 if (!child)
70 return;
f6bcfd97 71
b1d4dd7a 72 wxActivateEvent event2( wxEVT_ACTIVATE, true, child->GetId() );
e90196a5 73 event2.SetEventObject( child);
937013e0 74 child->HandleWindowEvent( event2 );
5e014a0c 75}
865bb325 76}
5e014a0c 77
6ca41e57
RR
78//-----------------------------------------------------------------------------
79// wxMDIParentFrame
33d0b396
RR
80//-----------------------------------------------------------------------------
81
c801d85f
KB
82IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)
83
f6bcfd97 84void wxMDIParentFrame::Init()
c801d85f 85{
b1d4dd7a 86 m_justInserted = false;
ff7b1510 87}
c801d85f 88
f6bcfd97
BP
89bool wxMDIParentFrame::Create(wxWindow *parent,
90 wxWindowID id,
91 const wxString& title,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxString& name )
c801d85f 96{
6e42617a
VZ
97 if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
98 return false;
a3622daa 99
6e42617a 100 m_clientWindow = OnCreateClient();
d2824cdb
VZ
101 if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
102 return false;
a3622daa 103
d2824cdb 104 return true;
ff7b1510 105}
c801d85f 106
ab2b3dd4
RR
107void wxMDIParentFrame::OnInternalIdle()
108{
d2824cdb 109 /* if a MDI child window has just been inserted
ab2b3dd4
RR
110 it has to be brought to the top in idle time. we
111 simply set the last notebook page active as new
112 pages can only be appended at the end */
113
114 if (m_justInserted)
83624f79 115 {
a2053b27 116 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
38f1df7c 117 gtk_notebook_set_current_page( notebook, g_list_length( notebook->children ) - 1 );
a0fdacee 118
147d6669 119 /* need to set the menubar of the child */
1d608029 120 wxMDIChildFrame *active_child_frame = GetActiveChild();
1b10056f 121 if (active_child_frame != NULL)
147d6669 122 {
1b10056f
RD
123 wxMenuBar *menu_bar = active_child_frame->m_menuBar;
124 if (menu_bar)
125 {
6abf7b63 126 menu_bar->Attach(active_child_frame);
1b10056f 127 }
147d6669 128 }
b1d4dd7a 129 m_justInserted = false;
a0fdacee 130 return;
83624f79 131 }
a0fdacee 132
ab2b3dd4 133 wxFrame::OnInternalIdle();
c626a8b7 134
ab2b3dd4 135 wxMDIChildFrame *active_child_frame = GetActiveChild();
b1d4dd7a 136 bool visible_child_menu = false;
a0fdacee 137
222ed1d6 138 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
ab2b3dd4 139 while (node)
83624f79 140 {
b1d4dd7a
RL
141 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
142
f6bcfd97 143 if ( child_frame )
a0fdacee 144 {
f6bcfd97
BP
145 wxMenuBar *menu_bar = child_frame->m_menuBar;
146 if ( menu_bar )
f03fc89f 147 {
f6bcfd97
BP
148 if (child_frame == active_child_frame)
149 {
b1d4dd7a 150 if (menu_bar->Show(true))
f6bcfd97 151 {
6abf7b63
VZ
152 // Attach() asserts if we call it for an already
153 // attached menu bar so don't do it if we're already
154 // associated with this frame (it would be nice to get
155 // rid of this check and ensure that this doesn't
156 // happen...)
157 if ( menu_bar->GetFrame() != child_frame )
158 menu_bar->Attach( child_frame );
f6bcfd97 159 }
b1d4dd7a 160 visible_child_menu = true;
f6bcfd97
BP
161 }
162 else
163 {
b1d4dd7a 164 if (menu_bar->Show(false))
f6bcfd97 165 {
6abf7b63 166 menu_bar->Detach();
f6bcfd97
BP
167 }
168 }
f03fc89f 169 }
a0fdacee 170 }
f6bcfd97 171
b1d4dd7a 172 node = node->GetNext();
83624f79 173 }
a0fdacee 174
e27ce4e9 175 /* show/hide parent menu bar as required */
5bd9e519
RR
176 if ((m_frameMenuBar) &&
177 (m_frameMenuBar->IsShown() == visible_child_menu))
178 {
179 if (visible_child_menu)
f6bcfd97 180 {
b1d4dd7a 181 m_frameMenuBar->Show( false );
6abf7b63 182 m_frameMenuBar->Detach();
f6bcfd97
BP
183 }
184 else
185 {
b1d4dd7a 186 m_frameMenuBar->Show( true );
6abf7b63 187 m_frameMenuBar->Attach( this );
cca410b3
PC
188 }
189 }
190}
f6bcfd97 191
cca410b3
PC
192void wxMDIParentFrame::DoGetClientSize(int* width, int* height) const
193{
194 wxFrame::DoGetClientSize(width, height);
195
196 if (height)
197 {
198 wxMDIChildFrame* active_child_frame = GetActiveChild();
199 if (active_child_frame)
200 {
201 wxMenuBar* menubar = active_child_frame->m_menuBar;
202 if (menubar && menubar->IsShown())
203 {
204 GtkRequisition req;
205 gtk_widget_size_request(menubar->m_widget, &req);
206 *height -= req.height;
207 if (*height < 0) *height = 0;
208 }
f6bcfd97 209 }
5bd9e519 210 }
ff7b1510 211}
716b7364 212
ab2b3dd4
RR
213wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
214{
d2824cdb 215 if (!m_clientWindow) return NULL;
a0fdacee 216
a2053b27 217 GtkNotebook *notebook = GTK_NOTEBOOK(m_clientWindow->m_widget);
d2824cdb 218 if (!notebook) return NULL;
a0fdacee 219
ab2b3dd4 220 gint i = gtk_notebook_get_current_page( notebook );
d2824cdb 221 if (i < 0) return NULL;
f4322df6 222
ab2b3dd4 223 GtkNotebookPage* page = (GtkNotebookPage*) (g_list_nth(notebook->children,i)->data);
d2824cdb 224 if (!page) return NULL;
a0fdacee 225
222ed1d6 226 wxWindowList::compatibility_iterator node = m_clientWindow->GetChildren().GetFirst();
ab2b3dd4
RR
227 while (node)
228 {
3811dacb 229 if ( wxPendingDelete.Member(node->GetData()) )
d2824cdb 230 return NULL;
f4322df6 231
b1d4dd7a
RL
232 wxMDIChildFrame *child_frame = wxDynamicCast( node->GetData(), wxMDIChildFrame );
233
3811dacb 234 if (!child_frame)
d2824cdb 235 return NULL;
b1d4dd7a 236
ab2b3dd4
RR
237 if (child_frame->m_page == page)
238 return child_frame;
f4322df6 239
b1d4dd7a 240 node = node->GetNext();
ab2b3dd4 241 }
a0fdacee 242
d2824cdb 243 return NULL;
ff7b1510 244}
c801d85f 245
ab2b3dd4 246void wxMDIParentFrame::ActivateNext()
c801d85f 247{
83624f79 248 if (m_clientWindow)
a2053b27 249 gtk_notebook_next_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
ff7b1510 250}
c801d85f 251
ab2b3dd4 252void wxMDIParentFrame::ActivatePrevious()
716b7364 253{
83624f79 254 if (m_clientWindow)
a2053b27 255 gtk_notebook_prev_page( GTK_NOTEBOOK(m_clientWindow->m_widget) );
ff7b1510 256}
716b7364 257
c801d85f
KB
258//-----------------------------------------------------------------------------
259// wxMDIChildFrame
260//-----------------------------------------------------------------------------
261
cf4219e7 262IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame,wxFrame)
c626a8b7 263
cf4219e7 264BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
83624f79 265 EVT_ACTIVATE(wxMDIChildFrame::OnActivate)
f6bcfd97 266 EVT_MENU_HIGHLIGHT_ALL(wxMDIChildFrame::OnMenuHighlight)
716b7364
RR
267END_EVENT_TABLE()
268
d2824cdb 269void wxMDIChildFrame::Init()
c801d85f 270{
d2824cdb
VZ
271 m_menuBar = NULL;
272 m_page = NULL;
ff7b1510 273}
c801d85f 274
d2824cdb
VZ
275bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
276 wxWindowID id,
277 const wxString& title,
278 const wxPoint& WXUNUSED(pos),
279 const wxSize& size,
280 long style,
281 const wxString& name)
c801d85f 282{
d2824cdb
VZ
283 m_mdiParent = parent;
284 m_title = title;
285
286 return wxWindow::Create(parent->GetClientWindow(), id,
287 wxDefaultPosition, size,
288 style, name);
ff7b1510 289}
c801d85f 290
ab2b3dd4 291wxMDIChildFrame::~wxMDIChildFrame()
c801d85f 292{
d2824cdb 293 delete m_menuBar;
002626c5
PC
294
295 // wxMDIClientWindow does not get redrawn properly after last child is removed
296 if (m_parent && m_parent->GetChildren().size() <= 1)
297 gtk_widget_queue_draw(m_parent->m_widget);
72c23f8e 298}
c801d85f 299
716b7364
RR
300void wxMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
301{
d2824cdb 302 wxASSERT_MSG( m_menuBar == NULL, "Only one menubar allowed" );
5bd9e519 303
83624f79 304 m_menuBar = menu_bar;
a3622daa 305
83624f79 306 if (m_menuBar)
716b7364 307 {
f03fc89f 308 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
83624f79 309
5bd9e519 310 m_menuBar->SetParent( mdi_frame );
c626a8b7 311
ab2b3dd4 312 /* insert the invisible menu bar into the _parent_ mdi frame */
cca410b3
PC
313 m_menuBar->Show(false);
314 gtk_box_pack_start(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, false, false, 0);
315 gtk_box_reorder_child(GTK_BOX(mdi_frame->m_mainWidget), m_menuBar->m_widget, 0);
316
317 gulong handler_id = g_signal_handler_find(
318 m_menuBar->m_widget,
319 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
320 g_signal_lookup("size_request", GTK_TYPE_WIDGET),
321 0, NULL, NULL, m_menuBar);
322 if (handler_id != 0)
323 g_signal_handler_disconnect(m_menuBar->m_widget, handler_id);
324 gtk_widget_set_size_request(m_menuBar->m_widget, -1, -1);
716b7364 325 }
ff7b1510 326}
cf4219e7 327
33b64e6f 328wxMenuBar *wxMDIChildFrame::GetMenuBar() const
cf4219e7 329{
83624f79 330 return m_menuBar;
ff7b1510 331}
c801d85f 332
d2824cdb
VZ
333GtkNotebook *wxMDIChildFrame::GTKGetNotebook() const
334{
335 wxMDIClientWindow * const
336 client = wxStaticCast(GetParent(), wxMDIClientWindow);
337 wxCHECK( client, NULL );
338
339 return GTK_NOTEBOOK(client->m_widget);
340}
341
ab2b3dd4 342void wxMDIChildFrame::Activate()
c801d85f 343{
d2824cdb
VZ
344 GtkNotebook * const notebook = GTKGetNotebook();
345 wxCHECK_RET( notebook, "no parent notebook?" );
346
9e691f46 347 gint pageno = gtk_notebook_page_num( notebook, m_widget );
38f1df7c 348 gtk_notebook_set_current_page( notebook, pageno );
ff7b1510 349}
c801d85f 350
f6bcfd97
BP
351void wxMDIChildFrame::OnActivate( wxActivateEvent& WXUNUSED(event) )
352{
353}
354
355void wxMDIChildFrame::OnMenuHighlight( wxMenuEvent& event )
9746a2ba 356{
f6bcfd97
BP
357#if wxUSE_STATUSBAR
358 wxMDIParentFrame *mdi_frame = (wxMDIParentFrame*)m_parent->GetParent();
722ed5be 359 if ( !ShowMenuHelp(event.GetMenuId()) )
f6bcfd97
BP
360 {
361 // we don't have any help text for this item, but may be the MDI frame
362 // does?
363 mdi_frame->OnMenuHighlight(event);
364 }
365#endif // wxUSE_STATUSBAR
366}
367
368void wxMDIChildFrame::SetTitle( const wxString &title )
369{
370 if ( title == m_title )
371 return;
372
373 m_title = title;
374
d2824cdb
VZ
375 GtkNotebook * const notebook = GTKGetNotebook();
376 wxCHECK_RET( notebook, "no parent notebook?" );
fab591c5 377 gtk_notebook_set_tab_label_text(notebook, m_widget, wxGTK_CONV( title ) );
ff7b1510 378}
9746a2ba 379
c801d85f
KB
380//-----------------------------------------------------------------------------
381// wxMDIClientWindow
382//-----------------------------------------------------------------------------
383
d2824cdb 384IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
c801d85f 385
5a0a15cf
FM
386wxMDIClientWindow::~wxMDIClientWindow()
387{
388 // disconnect our handler because our ~wxWindow (which is going to be called
389 // after this dtor) will call DestroyChildren(); in turns our children
390 // ~wxWindow dtors will call wxWindow::Show(false) and this will generate
391 // a call to gtk_mdi_page_change_callback with an invalid parent
392 // (because gtk_mdi_page_change_callback expects a wxMDIClientWindow but
393 // at that point of the dtor chain we are a simple wxWindow!)
03647350 394 g_signal_handlers_disconnect_by_func(m_widget,
5a0a15cf
FM
395 (gpointer)gtk_mdi_page_change_callback,
396 GetParent());
397}
398
d2824cdb 399bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
c801d85f 400{
d2824cdb
VZ
401 if ( !PreCreation( parent, wxDefaultPosition, wxDefaultSize ) ||
402 !CreateBase( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
403 style, wxDefaultValidator, "wxMDIClientWindow" ))
4dcaf11a 404 {
d2824cdb 405 wxFAIL_MSG( "wxMDIClientWindow creation failed" );
b1d4dd7a 406 return false;
4dcaf11a 407 }
c801d85f 408
83624f79 409 m_widget = gtk_notebook_new();
9ff9d30c 410 g_object_ref(m_widget);
a3622daa 411
9fa72bd2
MR
412 g_signal_connect (m_widget, "switch_page",
413 G_CALLBACK (gtk_mdi_page_change_callback), parent);
5e014a0c 414
83624f79 415 gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
a3622daa 416
f03fc89f 417 m_parent->DoAddChild( this );
c626a8b7 418
83624f79 419 PostCreation();
a3622daa 420
b1d4dd7a 421 Show( true );
a3622daa 422
b1d4dd7a 423 return true;
ff7b1510 424}
c801d85f 425
d2824cdb
VZ
426void wxMDIClientWindow::AddChildGTK(wxWindowGTK* child)
427{
428 wxMDIChildFrame* child_frame = static_cast<wxMDIChildFrame*>(child);
429 wxString s = child_frame->GetTitle();
430 if ( s.empty() )
431 s = _("MDI child");
432
433 GtkWidget *label_widget = gtk_label_new( s.mbc_str() );
434 gtk_misc_set_alignment( GTK_MISC(label_widget), 0.0, 0.5 );
435
436 GtkNotebook* notebook = GTK_NOTEBOOK(m_widget);
437
438 gtk_notebook_append_page( notebook, child->m_widget, label_widget );
439
440 child_frame->m_page = (GtkNotebookPage*) (g_list_last(notebook->children)->data);
441
442 wxMDIParentFrame* parent_frame = static_cast<wxMDIParentFrame*>(GetParent());
443 parent_frame->m_justInserted = true;
444}
445
e8375af8 446#endif // wxUSE_MDI