1///////////////////////////////////////////////////////////////////////////// 2// Name: src/gtk1/frame.cpp 3// Purpose: 4// Author: Robert Roebling 5// Id: $Id: frame.cpp 39646 2006-06-09 09:51:39Z ABX $ 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// ============================================================================ 14// declarations 15// ============================================================================ 16 17// ---------------------------------------------------------------------------- 18// headers 19// ---------------------------------------------------------------------------- 20 21#include "wx/frame.h" 22 23#ifndef WX_PRECOMP 24 #include "wx/app.h" 25 #include "wx/dcclient.h" 26 #include "wx/menu.h" 27 #include "wx/dialog.h" 28 #include "wx/control.h" 29 #include "wx/toolbar.h" 30 #include "wx/statusbr.h" 31#endif // WX_PRECOMP 32 33#include <glib.h> 34#include "wx/gtk1/private.h" 35 36#include <gdk/gdkkeysyms.h> 37#include <gdk/gdkx.h> 38 39#include "wx/gtk1/win_gtk.h" 40 41// ---------------------------------------------------------------------------- 42// constants 43// ---------------------------------------------------------------------------- 44 45const int wxSTATUS_HEIGHT = 25; 46const int wxPLACE_HOLDER = 0; 47 48// ---------------------------------------------------------------------------- 49// idle system 50// ---------------------------------------------------------------------------- 51 52extern void wxapp_install_idle_handler(); 53extern bool g_isIdle; 54 55// ---------------------------------------------------------------------------- 56// event tables 57// ---------------------------------------------------------------------------- 58 59IMPLEMENT_DYNAMIC_CLASS(wxFrame, wxTopLevelWindow) 60 61// ============================================================================ 62// implementation 63// ============================================================================ 64 65// ---------------------------------------------------------------------------- 66// GTK callbacks 67// ---------------------------------------------------------------------------- 68 69#if wxUSE_MENUS_NATIVE 70 71//----------------------------------------------------------------------------- 72// "child_attached" of menu bar 73//----------------------------------------------------------------------------- 74 75extern "C" { 76static void gtk_menu_attached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win ) 77{ 78 if (!win->m_hasVMT) return; 79 80 win->m_menuBarDetached = false; 81 win->GtkUpdateSize(); 82} 83} 84 85//----------------------------------------------------------------------------- 86// "child_detached" of menu bar 87//----------------------------------------------------------------------------- 88 89extern "C" { 90static void gtk_menu_detached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win ) 91{ 92 if (g_isIdle) 93 wxapp_install_idle_handler(); 94 95 if (!win->m_hasVMT) return; 96 97 // Raise the client area area 98 gdk_window_raise( win->m_wxwindow->window ); 99 100 win->m_menuBarDetached = true; 101 win->GtkUpdateSize(); 102} 103} 104 105#endif // wxUSE_MENUS_NATIVE 106 107#if wxUSE_TOOLBAR 108//----------------------------------------------------------------------------- 109// "child_attached" of tool bar 110//----------------------------------------------------------------------------- 111 112extern "C" { 113static void gtk_toolbar_attached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win ) 114{ 115 if (!win->m_hasVMT) return; 116 117 win->m_toolBarDetached = false; 118 win->GtkUpdateSize(); 119} 120} 121 122//----------------------------------------------------------------------------- 123// "child_detached" of tool bar 124//----------------------------------------------------------------------------- 125 126extern "C" { 127static void gtk_toolbar_detached_callback( GtkWidget *WXUNUSED(widget), GtkWidget *WXUNUSED(child), wxFrame *win ) 128{ 129 if (g_isIdle) 130 wxapp_install_idle_handler(); 131 132 if (!win->m_hasVMT) return; 133 134 // Raise the client area area 135 gdk_window_raise( win->m_wxwindow->window ); 136 137 win->m_toolBarDetached = true; 138 win->GtkUpdateSize(); 139} 140} 141#endif // wxUSE_TOOLBAR 142 143 144// ---------------------------------------------------------------------------- 145// wxFrame itself 146// ---------------------------------------------------------------------------- 147 148//----------------------------------------------------------------------------- 149// InsertChild for wxFrame 150//----------------------------------------------------------------------------- 151 152/* Callback for wxFrame. This very strange beast has to be used because 153 * C++ has no virtual methods in a constructor. We have to emulate a 154 * virtual function here as wxWidgets requires different ways to insert 155 * a child in container classes. */ 156 157static void wxInsertChildInFrame( wxFrame* parent, wxWindow* child ) 158{ 159 wxASSERT( GTK_IS_WIDGET(child->m_widget) ); 160 161 if (!parent->m_insertInClientArea) 162 { 163 // These are outside the client area 164 wxFrame* frame = (wxFrame*) parent; 165 gtk_pizza_put( GTK_PIZZA(frame->m_mainWidget), 166 GTK_WIDGET(child->m_widget), 167 child->m_x, 168 child->m_y, 169 child->m_width, 170 child->m_height ); 171 172#if wxUSE_TOOLBAR_NATIVE 173 // We connect to these events for recalculating the client area 174 // space when the toolbar is floating 175 if (wxIS_KIND_OF(child,wxToolBar)) 176 { 177 wxToolBar *toolBar = (wxToolBar*) child; 178 if (toolBar->GetWindowStyle() & wxTB_DOCKABLE) 179 { 180 gtk_signal_connect( GTK_OBJECT(toolBar->m_widget), "child_attached", 181 GTK_SIGNAL_FUNC(gtk_toolbar_attached_callback), (gpointer)parent ); 182 183 gtk_signal_connect( GTK_OBJECT(toolBar->m_widget), "child_detached", 184 GTK_SIGNAL_FUNC(gtk_toolbar_detached_callback), (gpointer)parent ); 185 } 186 } 187#endif // wxUSE_TOOLBAR 188 } 189 else 190 { 191 // These are inside the client area 192 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow), 193 GTK_WIDGET(child->m_widget), 194 child->m_x, 195 child->m_y, 196 child->m_width, 197 child->m_height ); 198 } 199 200 // Resize on OnInternalIdle 201 parent->GtkUpdateSize(); 202} 203 204// ---------------------------------------------------------------------------- 205// wxFrame creation 206// ---------------------------------------------------------------------------- 207 208void wxFrame::Init() 209{ 210 m_menuBarDetached = false; 211 m_toolBarDetached = false; 212 m_menuBarHeight = 2; 213} 214 215bool wxFrame::Create( wxWindow *parent, 216 wxWindowID id, 217 const wxString& title, 218 const wxPoint& pos, 219 const wxSize& sizeOrig, 220 long style, 221 const wxString &name ) 222{ 223 bool rt = wxTopLevelWindow::Create(parent, id, title, pos, sizeOrig, 224 style, name); 225 m_insertCallback = (wxInsertChildFunction) wxInsertChildInFrame; 226 227 return rt; 228} 229 230wxFrame::~wxFrame() 231{ 232 m_isBeingDeleted = true; 233 DeleteAllBars(); 234} 235 236// ---------------------------------------------------------------------------- 237// overridden wxWindow methods 238// ---------------------------------------------------------------------------- 239 240void wxFrame::DoGetClientSize( int *width, int *height ) const 241{ 242 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") ); 243 244 wxTopLevelWindow::DoGetClientSize( width, height ); 245 246 if (height) 247 { 248#if wxUSE_MENUS_NATIVE 249 // menu bar 250 if (m_frameMenuBar) 251 { 252 if (!m_menuBarDetached) 253 (*height) -= m_menuBarHeight; 254 else 255 (*height) -= wxPLACE_HOLDER; 256 } 257#endif // wxUSE_MENUS_NATIVE 258 259#if wxUSE_STATUSBAR 260 // status bar 261 if (m_frameStatusBar && m_frameStatusBar->IsShown()) 262 (*height) -= wxSTATUS_HEIGHT; 263#endif // wxUSE_STATUSBAR 264 265#if wxUSE_TOOLBAR 266 // tool bar 267 if (m_frameToolBar && m_frameToolBar->IsShown()) 268 { 269 if (m_toolBarDetached) 270 { 271 *height -= wxPLACE_HOLDER; 272 } 273 else 274 { 275 int x, y; 276 m_frameToolBar->GetSize( &x, &y ); 277 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL ) 278 { 279 *width -= x; 280 } 281 else 282 { 283 *height -= y; 284 } 285 } 286 } 287#endif // wxUSE_TOOLBAR 288 } 289} 290 291void wxFrame::DoSetClientSize( int width, int height ) 292{ 293 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") ); 294 295#if wxUSE_MENUS_NATIVE 296 // menu bar 297 if (m_frameMenuBar) 298 { 299 if (!m_menuBarDetached) 300 height += m_menuBarHeight; 301 else 302 height += wxPLACE_HOLDER; 303 } 304#endif // wxUSE_MENUS_NATIVE 305 306#if wxUSE_STATUSBAR 307 // status bar 308 if (m_frameStatusBar && m_frameStatusBar->IsShown()) height += wxSTATUS_HEIGHT; 309#endif 310 311#if wxUSE_TOOLBAR 312 // tool bar 313 if (m_frameToolBar && m_frameToolBar->IsShown()) 314 { 315 if (m_toolBarDetached) 316 { 317 height += wxPLACE_HOLDER; 318 } 319 else 320 { 321 int x, y; 322 m_frameToolBar->GetSize( &x, &y ); 323 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL ) 324 { 325 width += x; 326 } 327 else 328 { 329 height += y; 330 } 331 } 332 } 333#endif 334 335 wxTopLevelWindow::DoSetClientSize( width, height ); 336} 337 338void wxFrame::GtkOnSize( int WXUNUSED(x), int WXUNUSED(y), 339 int width, int height ) 340{ 341 // due to a bug in gtk, x,y are always 0 342 // m_x = x; 343 // m_y = y; 344 345 // avoid recursions 346 if (m_resizing) return; 347 m_resizing = true; 348 349 // this shouldn't happen: wxFrame, wxMDIParentFrame and wxMDIChildFrame have m_wxwindow 350 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") ); 351 352 m_width = width; 353 m_height = height; 354 355 // space occupied by m_frameToolBar and m_frameMenuBar 356 int client_area_x_offset = 0, 357 client_area_y_offset = 0; 358 359 /* wxMDIChildFrame derives from wxFrame but it _is_ a wxWindow as it uses 360 wxWindow::Create to create it's GTK equivalent. m_mainWidget is only 361 set in wxFrame::Create so it is used to check what kind of frame we 362 have here. if m_mainWidget is NULL it is a wxMDIChildFrame and so we 363 skip the part which handles m_frameMenuBar, m_frameToolBar and (most 364 importantly) m_mainWidget */ 365 366 int minWidth = GetMinWidth(), 367 minHeight = GetMinHeight(), 368 maxWidth = GetMaxWidth(), 369 maxHeight = GetMaxHeight(); 370 371 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth; 372 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight; 373 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth; 374 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight; 375 376 if (m_mainWidget) 377 { 378 // set size hints 379 gint flag = 0; // GDK_HINT_POS; 380 if ((minWidth != -1) || (minHeight != -1)) flag |= GDK_HINT_MIN_SIZE; 381 if ((maxWidth != -1) || (maxHeight != -1)) flag |= GDK_HINT_MAX_SIZE; 382 GdkGeometry geom; 383 geom.min_width = minWidth; 384 geom.min_height = minHeight; 385 geom.max_width = maxWidth; 386 geom.max_height = maxHeight; 387 gtk_window_set_geometry_hints( GTK_WINDOW(m_widget), 388 (GtkWidget*) NULL, 389 &geom, 390 (GdkWindowHints) flag ); 391 392 // I revert back to wxGTK's original behaviour. m_mainWidget holds 393 // the menubar, the toolbar and the client area, which is represented 394 // by m_wxwindow. 395 // This hurts in the eye, but I don't want to call SetSize() 396 // because I don't want to call any non-native functions here. 397 398#if wxUSE_MENUS_NATIVE 399 if (m_frameMenuBar) 400 { 401 int xx = m_miniEdge; 402 int yy = m_miniEdge + m_miniTitle; 403 int ww = m_width - 2*m_miniEdge; 404 int hh = m_menuBarHeight; 405 if (m_menuBarDetached) hh = wxPLACE_HOLDER; 406 m_frameMenuBar->m_x = xx; 407 m_frameMenuBar->m_y = yy; 408 m_frameMenuBar->m_width = ww; 409 m_frameMenuBar->m_height = hh; 410 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget), 411 m_frameMenuBar->m_widget, 412 xx, yy, ww, hh ); 413 client_area_y_offset += hh; 414 } 415#endif // wxUSE_MENUS_NATIVE 416 417#if wxUSE_TOOLBAR 418 if ((m_frameToolBar) && m_frameToolBar->IsShown() && 419 (m_frameToolBar->m_widget->parent == m_mainWidget)) 420 { 421 int xx = m_miniEdge; 422 int yy = m_miniEdge + m_miniTitle; 423#if wxUSE_MENUS_NATIVE 424 if (m_frameMenuBar) 425 { 426 if (!m_menuBarDetached) 427 yy += m_menuBarHeight; 428 else 429 yy += wxPLACE_HOLDER; 430 } 431#endif // wxUSE_MENUS_NATIVE 432 433 m_frameToolBar->m_x = xx; 434 m_frameToolBar->m_y = yy; 435 436 // don't change the toolbar's reported height/width 437 int ww, hh; 438 if ( m_frameToolBar->GetWindowStyle() & wxTB_VERTICAL ) 439 { 440 ww = m_toolBarDetached ? wxPLACE_HOLDER 441 : m_frameToolBar->m_width; 442 hh = m_height - 2*m_miniEdge; 443 444 client_area_x_offset += ww; 445 } 446 else 447 { 448 ww = m_width - 2*m_miniEdge; 449 hh = m_toolBarDetached ? wxPLACE_HOLDER 450 : m_frameToolBar->m_height; 451 452 client_area_y_offset += hh; 453 } 454 455 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget), 456 m_frameToolBar->m_widget, 457 xx, yy, ww, hh ); 458 } 459#endif // wxUSE_TOOLBAR 460 461 int client_x = client_area_x_offset + m_miniEdge; 462 int client_y = client_area_y_offset + m_miniEdge + m_miniTitle; 463 int client_w = m_width - client_area_x_offset - 2*m_miniEdge; 464 int client_h = m_height - client_area_y_offset- 2*m_miniEdge - m_miniTitle; 465 gtk_pizza_set_size( GTK_PIZZA(m_mainWidget), 466 m_wxwindow, 467 client_x, client_y, client_w, client_h ); 468 } 469 else 470 { 471 // If there is no m_mainWidget between m_widget and m_wxwindow there 472 // is no need to set the size or position of m_wxwindow. 473 } 474 475#if wxUSE_STATUSBAR 476 if (m_frameStatusBar && m_frameStatusBar->IsShown()) 477 { 478 int xx = 0 + m_miniEdge; 479 int yy = m_height - wxSTATUS_HEIGHT - m_miniEdge - client_area_y_offset; 480 int ww = m_width - 2*m_miniEdge; 481 int hh = wxSTATUS_HEIGHT; 482 m_frameStatusBar->m_x = xx; 483 m_frameStatusBar->m_y = yy; 484 m_frameStatusBar->m_width = ww; 485 m_frameStatusBar->m_height = hh; 486 gtk_pizza_set_size( GTK_PIZZA(m_wxwindow), 487 m_frameStatusBar->m_widget, 488 xx, yy, ww, hh ); 489 gtk_widget_draw( m_frameStatusBar->m_widget, (GdkRectangle*) NULL ); 490 } 491#endif // wxUSE_STATUSBAR 492 493 m_sizeSet = true; 494 495 // send size event to frame 496 wxSizeEvent event( wxSize(m_width,m_height), GetId() ); 497 event.SetEventObject( this ); 498 GetEventHandler()->ProcessEvent( event ); 499 500#if wxUSE_STATUSBAR 501 // send size event to status bar 502 if (m_frameStatusBar) 503 { 504 wxSizeEvent event2( wxSize(m_frameStatusBar->m_width,m_frameStatusBar->m_height), m_frameStatusBar->GetId() ); 505 event2.SetEventObject( m_frameStatusBar ); 506 m_frameStatusBar->GetEventHandler()->ProcessEvent( event2 ); 507 } 508#endif // wxUSE_STATUSBAR 509 510 m_resizing = false; 511} 512 513void wxFrame::OnInternalIdle() 514{ 515 wxFrameBase::OnInternalIdle(); 516 517#if wxUSE_MENUS_NATIVE 518 if (m_frameMenuBar) m_frameMenuBar->OnInternalIdle(); 519#endif // wxUSE_MENUS_NATIVE 520#if wxUSE_TOOLBAR 521 if (m_frameToolBar) m_frameToolBar->OnInternalIdle(); 522#endif 523#if wxUSE_STATUSBAR 524 if (m_frameStatusBar) 525 { 526 m_frameStatusBar->OnInternalIdle(); 527 528 // There may be controls in the status bar that 529 // need to be updated 530 for ( wxWindowList::compatibility_iterator node = m_frameStatusBar->GetChildren().GetFirst(); 531 node; 532 node = node->GetNext() ) 533 { 534 wxWindow *child = node->GetData(); 535 child->OnInternalIdle(); 536 } 537 } 538#endif 539} 540 541// ---------------------------------------------------------------------------- 542// menu/tool/status bar stuff 543// ---------------------------------------------------------------------------- 544 545#if wxUSE_MENUS_NATIVE 546 547void wxFrame::DetachMenuBar() 548{ 549 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") ); 550 wxASSERT_MSG( (m_wxwindow != NULL), wxT("invalid frame") ); 551 552 if ( m_frameMenuBar ) 553 { 554 m_frameMenuBar->UnsetInvokingWindow( this ); 555 556 if (m_frameMenuBar->GetWindowStyle() & wxMB_DOCKABLE) 557 { 558 gtk_signal_disconnect_by_func( GTK_OBJECT(m_frameMenuBar->m_widget), 559 GTK_SIGNAL_FUNC(gtk_menu_attached_callback), (gpointer)this ); 560 561 gtk_signal_disconnect_by_func( GTK_OBJECT(m_frameMenuBar->m_widget), 562 GTK_SIGNAL_FUNC(gtk_menu_detached_callback), (gpointer)this ); 563 } 564 565 gtk_widget_ref( m_frameMenuBar->m_widget ); 566 567 gtk_container_remove( GTK_CONTAINER(m_mainWidget), m_frameMenuBar->m_widget ); 568 } 569 570 wxFrameBase::DetachMenuBar(); 571} 572 573void wxFrame::AttachMenuBar( wxMenuBar *menuBar ) 574{ 575 wxFrameBase::AttachMenuBar(menuBar); 576 577 if (m_frameMenuBar) 578 { 579 m_frameMenuBar->SetInvokingWindow( this ); 580 581 m_frameMenuBar->SetParent(this); 582 gtk_pizza_put( GTK_PIZZA(m_mainWidget), 583 m_frameMenuBar->m_widget, 584 m_frameMenuBar->m_x, 585 m_frameMenuBar->m_y, 586 m_frameMenuBar->m_width, 587 m_frameMenuBar->m_height ); 588 589 if (menuBar->GetWindowStyle() & wxMB_DOCKABLE) 590 { 591 gtk_signal_connect( GTK_OBJECT(menuBar->m_widget), "child_attached", 592 GTK_SIGNAL_FUNC(gtk_menu_attached_callback), (gpointer)this ); 593 594 gtk_signal_connect( GTK_OBJECT(menuBar->m_widget), "child_detached", 595 GTK_SIGNAL_FUNC(gtk_menu_detached_callback), (gpointer)this ); 596 } 597 598 gtk_widget_show( m_frameMenuBar->m_widget ); 599 600 UpdateMenuBarSize(); 601 } 602 else 603 { 604 m_menuBarHeight = 2; 605 GtkUpdateSize(); // resize window in OnInternalIdle 606 } 607} 608 609void wxFrame::UpdateMenuBarSize() 610{ 611 GtkRequisition req; 612 613 req.width = 2; 614 req.height = 2; 615 616 // this is called after Remove with a NULL m_frameMenuBar 617 if ( m_frameMenuBar ) 618 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(m_frameMenuBar->m_widget) )->size_request ) 619 (m_frameMenuBar->m_widget, &req ); 620 621 m_menuBarHeight = req.height; 622 623 // resize window in OnInternalIdle 624 625 GtkUpdateSize(); 626} 627 628#endif // wxUSE_MENUS_NATIVE 629 630#if wxUSE_TOOLBAR 631 632wxToolBar* wxFrame::CreateToolBar( long style, wxWindowID id, const wxString& name ) 633{ 634 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") ); 635 636 m_insertInClientArea = false; 637 638 m_frameToolBar = wxFrameBase::CreateToolBar( style, id, name ); 639 640 m_insertInClientArea = true; 641 642 GtkUpdateSize(); 643 644 return m_frameToolBar; 645} 646 647void wxFrame::SetToolBar(wxToolBar *toolbar) 648{ 649 bool hadTbar = m_frameToolBar != NULL; 650 651 wxFrameBase::SetToolBar(toolbar); 652 653 if ( m_frameToolBar ) 654 { 655 // insert into toolbar area if not already there 656 if ((m_frameToolBar->m_widget->parent) && 657 (m_frameToolBar->m_widget->parent != m_mainWidget)) 658 { 659 GetChildren().DeleteObject( m_frameToolBar ); 660 661 gtk_widget_reparent( m_frameToolBar->m_widget, m_mainWidget ); 662 GtkUpdateSize(); 663 } 664 } 665 else // toolbar unset 666 { 667 // still need to update size if it had been there before 668 if ( hadTbar ) 669 { 670 GtkUpdateSize(); 671 } 672 } 673} 674 675#endif // wxUSE_TOOLBAR 676 677#if wxUSE_STATUSBAR 678 679wxStatusBar* wxFrame::CreateStatusBar(int number, 680 long style, 681 wxWindowID id, 682 const wxString& name) 683{ 684 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") ); 685 686 // because it will change when toolbar is added 687 GtkUpdateSize(); 688 689 return wxFrameBase::CreateStatusBar( number, style, id, name ); 690} 691 692void wxFrame::SetStatusBar(wxStatusBar *statbar) 693{ 694 bool hadStatBar = m_frameStatusBar != NULL; 695 696 wxFrameBase::SetStatusBar(statbar); 697 698 if (hadStatBar && !m_frameStatusBar) 699 GtkUpdateSize(); 700} 701 702void wxFrame::PositionStatusBar() 703{ 704 if ( !m_frameStatusBar ) 705 return; 706 707 GtkUpdateSize(); 708} 709#endif // wxUSE_STATUSBAR 710