This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
643 lines
17 KiB
C++
643 lines
17 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/generic/mdig.cpp
|
|
// Purpose: Generic MDI (Multiple Document Interface) classes
|
|
// Author: Hans Van Leemputten
|
|
// Modified by: 2008-10-31 Vadim Zeitlin: derive from the base classes
|
|
// Created: 29/07/2002
|
|
// Copyright: (c) 2002 Hans Van Leemputten
|
|
// (c) 2008 Vadim Zeitlin
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ===========================================================================
|
|
// declarations
|
|
// ===========================================================================
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// headers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_MDI
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/menu.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#endif //WX_PRECOMP
|
|
|
|
#include "wx/mdi.h"
|
|
#include "wx/generic/mdig.h"
|
|
#include "wx/notebook.h"
|
|
#include "wx/scopeguard.h"
|
|
|
|
#include "wx/stockitem.h"
|
|
|
|
enum MDI_MENU_ID
|
|
{
|
|
wxWINDOWCLOSE = 4001,
|
|
wxWINDOWCLOSEALL,
|
|
wxWINDOWNEXT,
|
|
wxWINDOWPREV
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGenericMDIParentFrame
|
|
//-----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIParentFrame, wxFrame)
|
|
|
|
BEGIN_EVENT_TABLE(wxGenericMDIParentFrame, wxFrame)
|
|
EVT_CLOSE(wxGenericMDIParentFrame::OnClose)
|
|
#if wxUSE_MENUS
|
|
EVT_MENU(wxID_ANY, wxGenericMDIParentFrame::OnWindowMenu)
|
|
#endif
|
|
END_EVENT_TABLE()
|
|
|
|
void wxGenericMDIParentFrame::Init()
|
|
{
|
|
#if wxUSE_MENUS
|
|
m_pMyMenuBar = NULL;
|
|
#endif // wxUSE_MENUS
|
|
}
|
|
|
|
wxGenericMDIParentFrame::~wxGenericMDIParentFrame()
|
|
{
|
|
// Make sure the client window is destructed before the menu bars are!
|
|
wxDELETE(m_clientWindow);
|
|
|
|
#if wxUSE_MENUS
|
|
wxDELETE(m_pMyMenuBar);
|
|
|
|
RemoveWindowMenu(GetMenuBar());
|
|
#endif // wxUSE_MENUS
|
|
}
|
|
|
|
bool wxGenericMDIParentFrame::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& title,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxString& name)
|
|
{
|
|
// this style can be used to prevent a window from having the standard MDI
|
|
// "Window" menu
|
|
if ( !(style & wxFRAME_NO_WINDOW_MENU) )
|
|
{
|
|
#if wxUSE_MENUS
|
|
m_windowMenu = new wxMenu;
|
|
|
|
m_windowMenu->Append(wxWINDOWCLOSE, _("Cl&ose"));
|
|
m_windowMenu->Append(wxWINDOWCLOSEALL, _("Close All"));
|
|
m_windowMenu->AppendSeparator();
|
|
m_windowMenu->Append(wxWINDOWNEXT, _("&Next"));
|
|
m_windowMenu->Append(wxWINDOWPREV, _("&Previous"));
|
|
#endif // wxUSE_MENUS
|
|
}
|
|
|
|
// the scrolling styles don't make sense neither for us nor for our client
|
|
// window (to which they're supposed to apply)
|
|
style &= ~(wxHSCROLL | wxVSCROLL);
|
|
|
|
if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
|
|
return false;
|
|
|
|
wxGenericMDIClientWindow * const client = OnCreateGenericClient();
|
|
if ( !client->CreateGenericClient(this) )
|
|
return false;
|
|
|
|
m_clientWindow = client;
|
|
|
|
return true;
|
|
}
|
|
|
|
wxGenericMDIClientWindow *wxGenericMDIParentFrame::OnCreateGenericClient()
|
|
{
|
|
return new wxGenericMDIClientWindow;
|
|
}
|
|
|
|
bool wxGenericMDIParentFrame::CloseAll()
|
|
{
|
|
wxGenericMDIClientWindow * const client = GetGenericClientWindow();
|
|
if ( !client )
|
|
return true; // none of the windows left
|
|
|
|
wxBookCtrlBase * const book = client->GetBookCtrl();
|
|
while ( book->GetPageCount() )
|
|
{
|
|
wxGenericMDIChildFrame * const child = client->GetChild(0);
|
|
if ( !child->Close() )
|
|
{
|
|
// it refused to close, don't close the remaining ones neither
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if wxUSE_MENUS
|
|
void wxGenericMDIParentFrame::SetWindowMenu(wxMenu* pMenu)
|
|
{
|
|
// Replace the window menu from the currently loaded menu bar.
|
|
wxMenuBar *pMenuBar = GetMenuBar();
|
|
|
|
if (m_windowMenu)
|
|
{
|
|
RemoveWindowMenu(pMenuBar);
|
|
|
|
wxDELETE(m_windowMenu);
|
|
}
|
|
|
|
if (pMenu)
|
|
{
|
|
m_windowMenu = pMenu;
|
|
|
|
AddWindowMenu(pMenuBar);
|
|
}
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::SetMenuBar(wxMenuBar *pMenuBar)
|
|
{
|
|
// Remove the Window menu from the old menu bar
|
|
RemoveWindowMenu(GetMenuBar());
|
|
// Add the Window menu to the new menu bar.
|
|
AddWindowMenu(pMenuBar);
|
|
|
|
wxFrame::SetMenuBar(pMenuBar);
|
|
}
|
|
#endif // wxUSE_MENUS
|
|
|
|
void wxGenericMDIParentFrame::WXSetChildMenuBar(wxGenericMDIChildFrame *pChild)
|
|
{
|
|
#if wxUSE_MENUS
|
|
if (pChild == NULL)
|
|
{
|
|
// No Child, set Our menu bar back.
|
|
SetMenuBar(m_pMyMenuBar);
|
|
|
|
// Make sure we know our menu bar is in use
|
|
m_pMyMenuBar = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (pChild->GetMenuBar() == NULL)
|
|
return;
|
|
|
|
// Do we need to save the current bar?
|
|
if (m_pMyMenuBar == NULL)
|
|
m_pMyMenuBar = GetMenuBar();
|
|
|
|
SetMenuBar(pChild->GetMenuBar());
|
|
}
|
|
#endif // wxUSE_MENUS
|
|
}
|
|
|
|
wxGenericMDIClientWindow *
|
|
wxGenericMDIParentFrame::GetGenericClientWindow() const
|
|
{
|
|
return static_cast<wxGenericMDIClientWindow *>(m_clientWindow);
|
|
}
|
|
|
|
wxBookCtrlBase *wxGenericMDIParentFrame::GetBookCtrl() const
|
|
{
|
|
wxGenericMDIClientWindow * const client = GetGenericClientWindow();
|
|
return client ? client->GetBookCtrl() : NULL;
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::AdvanceActive(bool forward)
|
|
{
|
|
wxBookCtrlBase * const book = GetBookCtrl();
|
|
if ( book )
|
|
book->AdvanceSelection(forward);
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::WXUpdateChildTitle(wxGenericMDIChildFrame *child)
|
|
{
|
|
wxGenericMDIClientWindow * const client = GetGenericClientWindow();
|
|
|
|
const int pos = client->FindChild(child);
|
|
if ( pos == wxNOT_FOUND )
|
|
return;
|
|
|
|
client->GetBookCtrl()->SetPageText(pos, child->GetTitle());
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::WXActivateChild(wxGenericMDIChildFrame *child)
|
|
{
|
|
wxGenericMDIClientWindow * const client = GetGenericClientWindow();
|
|
|
|
const int pos = client->FindChild(child);
|
|
if ( pos == wxNOT_FOUND )
|
|
return;
|
|
|
|
client->GetBookCtrl()->SetSelection(pos);
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::WXRemoveChild(wxGenericMDIChildFrame *child)
|
|
{
|
|
const bool removingActive = WXIsActiveChild(child);
|
|
if ( removingActive )
|
|
{
|
|
SetActiveChild(NULL);
|
|
WXSetChildMenuBar(NULL);
|
|
}
|
|
|
|
wxGenericMDIClientWindow * const client = GetGenericClientWindow();
|
|
wxCHECK_RET( client, "should have client window" );
|
|
|
|
wxBookCtrlBase * const book = client->GetBookCtrl();
|
|
|
|
// Remove page if still there
|
|
int pos = client->FindChild(child);
|
|
if ( pos != wxNOT_FOUND )
|
|
{
|
|
if ( book->RemovePage(pos) )
|
|
book->Refresh();
|
|
}
|
|
|
|
if ( removingActive )
|
|
{
|
|
// Set the new selection to a remaining page
|
|
const size_t count = book->GetPageCount();
|
|
if ( count > (size_t)pos )
|
|
{
|
|
book->SetSelection(pos);
|
|
}
|
|
else
|
|
{
|
|
if ( count > 0 )
|
|
book->SetSelection(count - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
wxGenericMDIParentFrame::WXIsActiveChild(wxGenericMDIChildFrame *child) const
|
|
{
|
|
return static_cast<wxMDIChildFrameBase *>(GetActiveChild()) == child;
|
|
}
|
|
|
|
#if wxUSE_MENUS
|
|
void wxGenericMDIParentFrame::RemoveWindowMenu(wxMenuBar *pMenuBar)
|
|
{
|
|
if (pMenuBar && m_windowMenu)
|
|
{
|
|
// Remove old window menu
|
|
int pos = pMenuBar->FindMenu(_("&Window"));
|
|
if (pos != wxNOT_FOUND)
|
|
{
|
|
wxASSERT(m_windowMenu == pMenuBar->GetMenu(pos)); // DBG:: We're going to delete the wrong menu!!!
|
|
pMenuBar->Remove(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::AddWindowMenu(wxMenuBar *pMenuBar)
|
|
{
|
|
if (pMenuBar && m_windowMenu)
|
|
{
|
|
int pos = pMenuBar->FindMenu(wxGetStockLabel(wxID_HELP,false));
|
|
if (pos == wxNOT_FOUND)
|
|
{
|
|
pMenuBar->Append(m_windowMenu, _("&Window"));
|
|
}
|
|
else
|
|
{
|
|
pMenuBar->Insert(pos, m_windowMenu, _("&Window"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxGenericMDIParentFrame::OnWindowMenu(wxCommandEvent &event)
|
|
{
|
|
switch ( event.GetId() )
|
|
{
|
|
case wxWINDOWCLOSE:
|
|
if ( m_currentChild )
|
|
m_currentChild->Close();
|
|
break;
|
|
|
|
case wxWINDOWCLOSEALL:
|
|
CloseAll();
|
|
break;
|
|
|
|
case wxWINDOWNEXT:
|
|
ActivateNext();
|
|
break;
|
|
|
|
case wxWINDOWPREV:
|
|
ActivatePrevious();
|
|
break;
|
|
|
|
default:
|
|
event.Skip();
|
|
}
|
|
}
|
|
#endif // wxUSE_MENUS
|
|
|
|
void wxGenericMDIParentFrame::OnClose(wxCloseEvent& event)
|
|
{
|
|
if ( !CloseAll() )
|
|
event.Veto();
|
|
else
|
|
event.Skip();
|
|
}
|
|
|
|
bool wxGenericMDIParentFrame::ProcessEvent(wxEvent& event)
|
|
{
|
|
if ( m_currentChild )
|
|
{
|
|
// the menu events should be given to the child as we show its menu bar
|
|
// as our own
|
|
const wxEventType eventType = event.GetEventType();
|
|
if ( eventType == wxEVT_MENU ||
|
|
eventType == wxEVT_UPDATE_UI )
|
|
{
|
|
// set the flag indicating that this event was forwarded to the
|
|
// child from the parent and so shouldn't be propagated upwards if
|
|
// not processed to avoid infinite loop
|
|
m_childHandler = m_currentChild;
|
|
wxON_BLOCK_EXIT_NULL(m_childHandler);
|
|
|
|
if ( m_currentChild->ProcessWindowEvent(event) )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return wxMDIParentFrameBase::ProcessEvent(event);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxGenericMDIChildFrame
|
|
// ----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame, wxFrame)
|
|
|
|
BEGIN_EVENT_TABLE(wxGenericMDIChildFrame, wxFrame)
|
|
EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight)
|
|
|
|
EVT_CLOSE(wxGenericMDIChildFrame::OnClose)
|
|
END_EVENT_TABLE()
|
|
|
|
void wxGenericMDIChildFrame::Init()
|
|
{
|
|
#if wxUSE_MENUS
|
|
m_pMenuBar = NULL;
|
|
#endif // wxUSE_MENUS
|
|
|
|
#if !wxUSE_GENERIC_MDI_AS_NATIVE
|
|
m_mdiParentGeneric = NULL;
|
|
#endif
|
|
}
|
|
|
|
wxGenericMDIChildFrame::~wxGenericMDIChildFrame()
|
|
{
|
|
wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
|
|
|
|
// it could happen that we don't have a valid parent if we hadn't been ever
|
|
// really created -- but in this case there is nothing else to do neither
|
|
if ( parent )
|
|
parent->WXRemoveChild(this);
|
|
|
|
#if wxUSE_MENUS
|
|
delete m_pMenuBar;
|
|
#endif // wxUSE_MENUS
|
|
}
|
|
|
|
bool wxGenericMDIChildFrame::Create(wxGenericMDIParentFrame *parent,
|
|
wxWindowID id,
|
|
const wxString& title,
|
|
const wxPoint& WXUNUSED(pos),
|
|
const wxSize& size,
|
|
long WXUNUSED(style),
|
|
const wxString& name)
|
|
{
|
|
// unfortunately we can't use the base class m_mdiParent field unless
|
|
// wxGenericMDIParentFrame is wxMDIParentFrame
|
|
#if wxUSE_GENERIC_MDI_AS_NATIVE
|
|
m_mdiParent = parent;
|
|
#else // generic != native
|
|
// leave m_mdiParent NULL, we don't have it
|
|
m_mdiParentGeneric = parent;
|
|
#endif
|
|
|
|
wxBookCtrlBase * const book = parent->GetBookCtrl();
|
|
|
|
wxASSERT_MSG( book, "Missing MDI client window." );
|
|
|
|
// note that we ignore the styles, none of the usual TLW styles apply to
|
|
// this (child) window
|
|
if ( !wxWindow::Create(book, id, wxDefaultPosition, size, 0, name) )
|
|
return false;
|
|
|
|
m_title = title;
|
|
book->AddPage(this, title, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if wxUSE_MENUS
|
|
void wxGenericMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
|
|
{
|
|
wxMenuBar *pOldMenuBar = m_pMenuBar;
|
|
m_pMenuBar = menu_bar;
|
|
|
|
if (m_pMenuBar)
|
|
{
|
|
wxGenericMDIParentFrame *parent = GetGenericMDIParent();
|
|
|
|
if ( parent )
|
|
{
|
|
m_pMenuBar->SetParent(parent);
|
|
|
|
if ( parent->WXIsActiveChild(this) )
|
|
{
|
|
// Replace current menu bars
|
|
if (pOldMenuBar)
|
|
parent->WXSetChildMenuBar(NULL);
|
|
parent->WXSetChildMenuBar(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wxMenuBar *wxGenericMDIChildFrame::GetMenuBar() const
|
|
{
|
|
return m_pMenuBar;
|
|
}
|
|
#endif // wxUSE_MENUS
|
|
|
|
void wxGenericMDIChildFrame::SetTitle(const wxString& title)
|
|
{
|
|
m_title = title;
|
|
|
|
wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
|
|
if ( parent )
|
|
parent->WXUpdateChildTitle(this);
|
|
//else: it's ok, we might be not created yet
|
|
}
|
|
|
|
void wxGenericMDIChildFrame::Activate()
|
|
{
|
|
wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
|
|
|
|
wxCHECK_RET( parent, "can't activate MDI child without parent" );
|
|
parent->WXActivateChild(this);
|
|
}
|
|
|
|
void wxGenericMDIChildFrame::OnMenuHighlight(wxMenuEvent& event)
|
|
{
|
|
wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
|
|
if ( parent)
|
|
{
|
|
// we don't have any help text for this item,
|
|
// but may be the MDI frame does?
|
|
parent->OnMenuHighlight(event);
|
|
}
|
|
}
|
|
|
|
void wxGenericMDIChildFrame::OnClose(wxCloseEvent& WXUNUSED(event))
|
|
{
|
|
// we're not a TLW so don't delay the destruction of this window
|
|
delete this;
|
|
}
|
|
|
|
bool wxGenericMDIChildFrame::TryAfter(wxEvent& event)
|
|
{
|
|
// we shouldn't propagate the event to the parent if we received it from it
|
|
// in the first place
|
|
wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
|
|
if ( parent && parent->WXIsInsideChildHandler(this) )
|
|
return false;
|
|
|
|
return wxTDIChildFrame::TryAfter(event);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxGenericMDIClientWindow
|
|
// ----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow, wxWindow)
|
|
|
|
bool
|
|
wxGenericMDIClientWindow::CreateGenericClient(wxWindow *parent)
|
|
{
|
|
if ( !wxWindow::Create(parent, wxID_ANY) )
|
|
return false;
|
|
|
|
m_notebook = new wxNotebook(this, wxID_ANY);
|
|
m_notebook->Connect
|
|
(
|
|
wxEVT_NOTEBOOK_PAGE_CHANGED,
|
|
wxNotebookEventHandler(
|
|
wxGenericMDIClientWindow::OnPageChanged),
|
|
NULL,
|
|
this
|
|
);
|
|
|
|
// now that we have a notebook to resize, hook up OnSize() too
|
|
Connect(wxEVT_SIZE, wxSizeEventHandler(wxGenericMDIClientWindow::OnSize));
|
|
|
|
return true;
|
|
}
|
|
|
|
wxBookCtrlBase *wxGenericMDIClientWindow::GetBookCtrl() const
|
|
{
|
|
return m_notebook;
|
|
}
|
|
|
|
wxGenericMDIChildFrame *wxGenericMDIClientWindow::GetChild(size_t pos) const
|
|
{
|
|
return static_cast<wxGenericMDIChildFrame *>(GetBookCtrl()->GetPage(pos));
|
|
}
|
|
|
|
int wxGenericMDIClientWindow::FindChild(wxGenericMDIChildFrame *child) const
|
|
{
|
|
wxBookCtrlBase * const book = GetBookCtrl();
|
|
const size_t count = book->GetPageCount();
|
|
for ( size_t pos = 0; pos < count; pos++ )
|
|
{
|
|
if ( book->GetPage(pos) == child )
|
|
return pos;
|
|
}
|
|
|
|
return wxNOT_FOUND;
|
|
}
|
|
|
|
void wxGenericMDIClientWindow::PageChanged(int oldSelection, int newSelection)
|
|
{
|
|
// Don't do anything if nothing changed
|
|
if (oldSelection == newSelection)
|
|
return;
|
|
|
|
// Again check if we really need to do this...
|
|
if (newSelection != -1)
|
|
{
|
|
wxGenericMDIChildFrame * const child = GetChild(newSelection);
|
|
|
|
if ( child->GetGenericMDIParent()->WXIsActiveChild(child) )
|
|
return;
|
|
}
|
|
|
|
// Notify old active child that it has been deactivated
|
|
if (oldSelection != -1)
|
|
{
|
|
wxGenericMDIChildFrame * const oldChild = GetChild(oldSelection);
|
|
if (oldChild)
|
|
{
|
|
wxActivateEvent event(wxEVT_ACTIVATE, false, oldChild->GetId());
|
|
event.SetEventObject( oldChild );
|
|
oldChild->GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
// Notify new active child that it has been activated
|
|
if (newSelection != -1)
|
|
{
|
|
wxGenericMDIChildFrame * const activeChild = GetChild(newSelection);
|
|
if ( activeChild )
|
|
{
|
|
wxActivateEvent event(wxEVT_ACTIVATE, true, activeChild->GetId());
|
|
event.SetEventObject( activeChild );
|
|
activeChild->GetEventHandler()->ProcessEvent(event);
|
|
|
|
wxGenericMDIParentFrame * const
|
|
parent = activeChild->GetGenericMDIParent();
|
|
|
|
if ( parent )
|
|
{
|
|
// this is a dirty hack as activeChild is not really a
|
|
// wxMDIChildFrame at all but we still want to store it in the
|
|
// base class m_currentChild field and this will work as long
|
|
// as we only use as wxMDIChildFrameBase pointer (which it is)
|
|
parent->SetActiveChild(
|
|
reinterpret_cast<wxMDIChildFrame *>(activeChild));
|
|
parent->WXSetChildMenuBar(activeChild);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent& event)
|
|
{
|
|
PageChanged(event.GetOldSelection(), event.GetSelection());
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxGenericMDIClientWindow::OnSize(wxSizeEvent& WXUNUSED(event))
|
|
{
|
|
m_notebook->SetSize(GetClientSize());
|
|
}
|
|
|
|
#endif // wxUSE_MDI
|
|
|