mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-12 22:56:52 +01:00

From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7. include/wx/defs.h -- __w64 warning disable patch by comex brought forward. include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code. src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux. src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue. src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text). Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
1633 lines
47 KiB
C++
1633 lines
47 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/mdi.cpp
|
|
// Purpose: MDI classes for wxMSW
|
|
// Author: Julian Smart
|
|
// Modified by: Vadim Zeitlin on 2008-11-04 to use the base classes
|
|
// Created: 04/01/98
|
|
// Copyright: (c) 1998 Julian Smart
|
|
// (c) 2008-2009 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 && !defined(__WXUNIVERSAL__)
|
|
|
|
#include "wx/mdi.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/frame.h"
|
|
#include "wx/menu.h"
|
|
#include "wx/app.h"
|
|
#include "wx/utils.h"
|
|
#include "wx/dialog.h"
|
|
#include "wx/statusbr.h"
|
|
#include "wx/settings.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#include "wx/sizer.h"
|
|
#include "wx/toolbar.h"
|
|
#endif
|
|
|
|
#include "wx/stockitem.h"
|
|
#include "wx/msw/private.h"
|
|
|
|
#include <string.h>
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// global variables
|
|
// ---------------------------------------------------------------------------
|
|
|
|
extern wxMenu *wxCurrentPopupMenu;
|
|
|
|
extern void wxRemoveHandleAssociation(wxWindow *win);
|
|
|
|
namespace
|
|
{
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// constants
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// First ID for the MDI child menu item in the "Window" menu.
|
|
const int wxFIRST_MDI_CHILD = 4100;
|
|
|
|
// There can be no more than 9 children in the "Window" menu as beginning with
|
|
// the tenth one they're not shown and "More windows..." menu item is used
|
|
// instead.
|
|
const int wxLAST_MDI_CHILD = wxFIRST_MDI_CHILD + 8;
|
|
|
|
// The ID of the "More windows..." menu item is the next one after the last
|
|
// child.
|
|
const int wxID_MDI_MORE_WINDOWS = wxLAST_MDI_CHILD + 1;
|
|
|
|
// The MDI "Window" menu label
|
|
const char *WINDOW_MENU_LABEL = gettext_noop("&Window");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// private functions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// set the MDI menus (by sending the WM_MDISETMENU message) and update the menu
|
|
// of the parent of win (which is supposed to be the MDI client window)
|
|
void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow);
|
|
|
|
// insert the window menu (subMenu) into menu just before "Help" submenu or at
|
|
// the very end if not found
|
|
void MDIInsertWindowMenu(wxWindow *win, WXHMENU hMenu, HMENU subMenu);
|
|
|
|
// Remove the window menu
|
|
void MDIRemoveWindowMenu(wxWindow *win, WXHMENU hMenu);
|
|
|
|
// unpack the parameters of WM_MDIACTIVATE message
|
|
void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
|
|
WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact);
|
|
|
|
// return the HMENU of the MDI menu
|
|
//
|
|
// this function works correctly even when we don't have a window menu and just
|
|
// returns 0 then
|
|
inline HMENU GetMDIWindowMenu(wxMDIParentFrame *frame)
|
|
{
|
|
wxMenu *menu = frame->GetWindowMenu();
|
|
return menu ? GetHmenuOf(menu) : 0;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
// ===========================================================================
|
|
// implementation
|
|
// ===========================================================================
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// wxWin macros
|
|
// ---------------------------------------------------------------------------
|
|
|
|
wxIMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame, wxFrame);
|
|
wxIMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame, wxFrame);
|
|
wxIMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow);
|
|
|
|
wxBEGIN_EVENT_TABLE(wxMDIParentFrame, wxFrame)
|
|
EVT_ACTIVATE(wxMDIParentFrame::OnActivate)
|
|
EVT_SIZE(wxMDIParentFrame::OnSize)
|
|
EVT_ICONIZE(wxMDIParentFrame::OnIconized)
|
|
EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged)
|
|
|
|
#if wxUSE_MENUS
|
|
EVT_MENU_RANGE(wxFIRST_MDI_CHILD, wxLAST_MDI_CHILD,
|
|
wxMDIParentFrame::OnMDIChild)
|
|
EVT_MENU_RANGE(wxID_MDI_WINDOW_FIRST, wxID_MDI_WINDOW_LAST,
|
|
wxMDIParentFrame::OnMDICommand)
|
|
#endif // wxUSE_MENUS
|
|
wxEND_EVENT_TABLE()
|
|
|
|
wxBEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
|
|
EVT_IDLE(wxMDIChildFrame::OnIdle)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
wxBEGIN_EVENT_TABLE(wxMDIClientWindow, wxWindow)
|
|
EVT_SCROLL(wxMDIClientWindow::OnScroll)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
// ===========================================================================
|
|
// wxMDIParentFrame: the frame which contains the client window which manages
|
|
// the children
|
|
// ===========================================================================
|
|
|
|
void wxMDIParentFrame::Init()
|
|
{
|
|
#if wxUSE_MENUS && wxUSE_ACCEL
|
|
// the default menu doesn't have any accelerators (even if we have it)
|
|
m_accelWindowMenu = NULL;
|
|
#endif // wxUSE_MENUS && wxUSE_ACCEL
|
|
|
|
m_activationNotHandled = false;
|
|
}
|
|
|
|
bool wxMDIParentFrame::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) )
|
|
{
|
|
// normal case: we have the window menu, so construct it
|
|
m_windowMenu = new wxMenu;
|
|
|
|
m_windowMenu->Append(wxID_MDI_WINDOW_CASCADE, _("&Cascade"));
|
|
m_windowMenu->Append(wxID_MDI_WINDOW_TILE_HORZ, _("Tile &Horizontally"));
|
|
m_windowMenu->Append(wxID_MDI_WINDOW_TILE_VERT, _("Tile &Vertically"));
|
|
m_windowMenu->AppendSeparator();
|
|
m_windowMenu->Append(wxID_MDI_WINDOW_ARRANGE_ICONS, _("&Arrange Icons"));
|
|
m_windowMenu->Append(wxID_MDI_WINDOW_NEXT, _("&Next"));
|
|
m_windowMenu->Append(wxID_MDI_WINDOW_PREV, _("&Previous"));
|
|
}
|
|
|
|
if (!parent)
|
|
wxTopLevelWindows.Append(this);
|
|
|
|
SetName(name);
|
|
m_windowStyle = style;
|
|
|
|
if ( parent )
|
|
parent->AddChild(this);
|
|
|
|
if ( id != wxID_ANY )
|
|
m_windowId = id;
|
|
else
|
|
m_windowId = NewControlId();
|
|
|
|
WXDWORD exflags;
|
|
WXDWORD msflags = MSWGetCreateWindowFlags(&exflags);
|
|
msflags &= ~WS_VSCROLL;
|
|
msflags &= ~WS_HSCROLL;
|
|
|
|
if ( !wxWindow::MSWCreate(wxApp::GetRegisteredClassName(wxT("wxMDIFrame")),
|
|
title.t_str(),
|
|
pos, size,
|
|
msflags,
|
|
exflags) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
|
|
|
|
// unlike (almost?) all other windows, frames are created hidden
|
|
m_isShown = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
wxMDIParentFrame::~wxMDIParentFrame()
|
|
{
|
|
// see comment in ~wxMDIChildFrame
|
|
#if wxUSE_TOOLBAR
|
|
m_frameToolBar = NULL;
|
|
#endif
|
|
#if wxUSE_STATUSBAR
|
|
m_frameStatusBar = NULL;
|
|
#endif // wxUSE_STATUSBAR
|
|
|
|
#if wxUSE_MENUS && wxUSE_ACCEL
|
|
delete m_accelWindowMenu;
|
|
#endif // wxUSE_MENUS && wxUSE_ACCEL
|
|
|
|
DestroyChildren();
|
|
|
|
// the MDI frame menubar is not automatically deleted by Windows unlike for
|
|
// the normal frames
|
|
if ( m_hMenu )
|
|
::DestroyMenu((HMENU)m_hMenu);
|
|
|
|
if ( m_clientWindow )
|
|
{
|
|
if ( m_clientWindow->MSWGetOldWndProc() )
|
|
m_clientWindow->UnsubclassWin();
|
|
|
|
m_clientWindow->SetHWND(0);
|
|
delete m_clientWindow;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxMDIParentFrame child management
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
|
|
{
|
|
HWND hWnd = (HWND)::SendMessage(GetWinHwnd(GetClientWindow()),
|
|
WM_MDIGETACTIVE, 0, 0L);
|
|
if ( !hWnd )
|
|
return NULL;
|
|
|
|
return static_cast<wxMDIChildFrame *>(wxFindWinFromHandle(hWnd));
|
|
}
|
|
|
|
int wxMDIParentFrame::GetChildFramesCount() const
|
|
{
|
|
int count = 0;
|
|
for ( wxWindowList::const_iterator i = GetChildren().begin();
|
|
i != GetChildren().end();
|
|
++i )
|
|
{
|
|
if ( wxDynamicCast(*i, wxMDIChildFrame) )
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
#if wxUSE_MENUS
|
|
|
|
void wxMDIParentFrame::AddMDIChild(wxMDIChildFrame * WXUNUSED(child))
|
|
{
|
|
switch ( GetChildFramesCount() )
|
|
{
|
|
case 1:
|
|
// first MDI child was just added, we need to insert the window
|
|
// menu now if we have it
|
|
AddWindowMenu();
|
|
|
|
// and disable the items which can't be used until we have more
|
|
// than one child
|
|
UpdateWindowMenu(false);
|
|
break;
|
|
|
|
case 2:
|
|
// second MDI child was added, enable the menu items which were
|
|
// disabled because they didn't make sense for a single window
|
|
UpdateWindowMenu(true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void wxMDIParentFrame::RemoveMDIChild(wxMDIChildFrame * WXUNUSED(child))
|
|
{
|
|
switch ( GetChildFramesCount() )
|
|
{
|
|
case 1:
|
|
// last MDI child is being removed, remove the now unnecessary
|
|
// window menu too
|
|
RemoveWindowMenu();
|
|
|
|
// there is no need to call UpdateWindowMenu(true) here so this is
|
|
// not quite symmetric to AddMDIChild() above
|
|
break;
|
|
|
|
case 2:
|
|
// only one MDI child is going to remain, disable the menu commands
|
|
// which don't make sense for a single child window
|
|
UpdateWindowMenu(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxMDIParentFrame window menu handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxMDIParentFrame::AddWindowMenu()
|
|
{
|
|
if ( m_windowMenu )
|
|
{
|
|
// For correct handling of the events from this menu we also must
|
|
// attach it to the menu bar.
|
|
m_windowMenu->Attach(GetMenuBar());
|
|
|
|
MDIInsertWindowMenu(GetClientWindow(), m_hMenu, GetMDIWindowMenu(this));
|
|
}
|
|
}
|
|
|
|
void wxMDIParentFrame::RemoveWindowMenu()
|
|
{
|
|
if ( m_windowMenu )
|
|
{
|
|
MDIRemoveWindowMenu(GetClientWindow(), m_hMenu);
|
|
|
|
m_windowMenu->Detach();
|
|
}
|
|
}
|
|
|
|
void wxMDIParentFrame::UpdateWindowMenu(bool enable)
|
|
{
|
|
if ( m_windowMenu )
|
|
{
|
|
m_windowMenu->Enable(wxID_MDI_WINDOW_NEXT, enable);
|
|
m_windowMenu->Enable(wxID_MDI_WINDOW_PREV, enable);
|
|
}
|
|
}
|
|
|
|
#if wxUSE_MENUS_NATIVE
|
|
|
|
void wxMDIParentFrame::InternalSetMenuBar()
|
|
{
|
|
if ( GetActiveChild() )
|
|
{
|
|
AddWindowMenu();
|
|
}
|
|
else // we don't have any MDI children yet
|
|
{
|
|
// wait until we do to add the window menu but do set the main menu for
|
|
// now (this is done by AddWindowMenu() as a side effect)
|
|
MDISetMenu(GetClientWindow(), (HMENU)m_hMenu, NULL);
|
|
}
|
|
}
|
|
|
|
#endif // wxUSE_MENUS_NATIVE
|
|
|
|
void wxMDIParentFrame::SetWindowMenu(wxMenu* menu)
|
|
{
|
|
if ( menu != m_windowMenu )
|
|
{
|
|
// We may not be showing the window menu currently if we don't have any
|
|
// children, and in this case we shouldn't remove/add it back right now.
|
|
const bool hasWindowMenu = GetActiveChild() != NULL;
|
|
|
|
if ( hasWindowMenu )
|
|
RemoveWindowMenu();
|
|
|
|
delete m_windowMenu;
|
|
|
|
m_windowMenu = menu;
|
|
|
|
if ( hasWindowMenu )
|
|
AddWindowMenu();
|
|
}
|
|
|
|
#if wxUSE_ACCEL
|
|
wxDELETE(m_accelWindowMenu);
|
|
|
|
if ( menu && menu->HasAccels() )
|
|
m_accelWindowMenu = menu->CreateAccelTable();
|
|
#endif // wxUSE_ACCEL
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxMDIParentFrame other menu-related stuff
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxMDIParentFrame::DoMenuUpdates(wxMenu* menu)
|
|
{
|
|
wxMDIChildFrame *child = GetActiveChild();
|
|
if ( child )
|
|
{
|
|
wxMenuBar* bar = child->GetMenuBar();
|
|
|
|
if (menu)
|
|
{
|
|
menu->UpdateUI();
|
|
}
|
|
else
|
|
{
|
|
if ( bar != NULL )
|
|
{
|
|
int nCount = bar->GetMenuCount();
|
|
for (int n = 0; n < nCount; n++)
|
|
bar->GetMenu(n)->UpdateUI();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxFrameBase::DoMenuUpdates(menu);
|
|
}
|
|
}
|
|
|
|
wxMenuItem *wxMDIParentFrame::FindItemInMenuBar(int menuId) const
|
|
{
|
|
// We must look in the child menu first: if it has an item with the same ID
|
|
// as in our own menu bar, the child item should be used to determine
|
|
// whether it's currently enabled.
|
|
wxMenuItem *item = GetActiveChild()
|
|
? GetActiveChild()->FindItemInMenuBar(menuId)
|
|
: NULL;
|
|
if ( !item )
|
|
item = wxFrame::FindItemInMenuBar(menuId);
|
|
|
|
if ( !item && m_windowMenu )
|
|
item = m_windowMenu->FindItem(menuId);
|
|
|
|
return item;
|
|
}
|
|
|
|
wxMenu* wxMDIParentFrame::MSWFindMenuFromHMENU(WXHMENU hMenu)
|
|
{
|
|
wxMenu* menu = GetActiveChild()
|
|
? GetActiveChild()->MSWFindMenuFromHMENU(hMenu)
|
|
: NULL;
|
|
if ( !menu )
|
|
menu = wxFrame::MSWFindMenuFromHMENU(hMenu);
|
|
|
|
if ( !menu && m_windowMenu && GetHmenuOf(m_windowMenu) == hMenu )
|
|
menu = m_windowMenu;
|
|
|
|
return menu;
|
|
}
|
|
|
|
WXHMENU wxMDIParentFrame::MSWGetActiveMenu() const
|
|
{
|
|
wxMDIChildFrame * const child = GetActiveChild();
|
|
if ( child )
|
|
{
|
|
const WXHMENU hmenu = child->MSWGetActiveMenu();
|
|
if ( hmenu )
|
|
return hmenu;
|
|
}
|
|
|
|
return wxFrame::MSWGetActiveMenu();
|
|
}
|
|
|
|
#endif // wxUSE_MENUS
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxMDIParentFrame event handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxMDIParentFrame::UpdateClientSize()
|
|
{
|
|
int width, height;
|
|
GetClientSize(&width, &height);
|
|
|
|
if ( wxSizer* sizer = GetSizer() )
|
|
{
|
|
sizer->SetDimension(0, 0, width, height);
|
|
}
|
|
else
|
|
{
|
|
if ( GetClientWindow() )
|
|
GetClientWindow()->SetSize(0, 0, width, height);
|
|
}
|
|
}
|
|
|
|
void wxMDIParentFrame::OnSize(wxSizeEvent& WXUNUSED(event))
|
|
{
|
|
UpdateClientSize();
|
|
|
|
// do not call event.Skip() here, it somehow messes up MDI client window
|
|
}
|
|
|
|
void wxMDIParentFrame::OnIconized(wxIconizeEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
if ( !event.IsIconized() )
|
|
UpdateClientSize();
|
|
}
|
|
|
|
// Responds to colour changes, and passes event on to children.
|
|
void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent& event)
|
|
{
|
|
if ( m_clientWindow )
|
|
{
|
|
m_clientWindow->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
|
|
m_clientWindow->Refresh();
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
WXHICON wxMDIParentFrame::GetDefaultIcon() const
|
|
{
|
|
// we don't have any standard icons (any more)
|
|
return (WXHICON)0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MDI operations
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void wxMDIParentFrame::Cascade()
|
|
{
|
|
::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDICASCADE, 0, 0);
|
|
}
|
|
|
|
void wxMDIParentFrame::Tile(wxOrientation orient)
|
|
{
|
|
wxASSERT_MSG( orient == wxHORIZONTAL || orient == wxVERTICAL,
|
|
wxT("invalid orientation value") );
|
|
|
|
::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE,
|
|
orient == wxHORIZONTAL ? MDITILE_HORIZONTAL
|
|
: MDITILE_VERTICAL, 0);
|
|
}
|
|
|
|
void wxMDIParentFrame::ArrangeIcons()
|
|
{
|
|
::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDIICONARRANGE, 0, 0);
|
|
}
|
|
|
|
void wxMDIParentFrame::ActivateNext()
|
|
{
|
|
::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 0);
|
|
}
|
|
|
|
void wxMDIParentFrame::ActivatePrevious()
|
|
{
|
|
::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 1);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// the MDI parent frame window proc
|
|
// ---------------------------------------------------------------------------
|
|
|
|
WXLRESULT wxMDIParentFrame::MSWWindowProc(WXUINT message,
|
|
WXWPARAM wParam,
|
|
WXLPARAM lParam)
|
|
{
|
|
WXLRESULT rc = 0;
|
|
bool processed = false;
|
|
|
|
switch ( message )
|
|
{
|
|
case WM_ACTIVATE:
|
|
{
|
|
WXWORD state, minimized;
|
|
WXHWND hwnd;
|
|
UnpackActivate(wParam, lParam, &state, &minimized, &hwnd);
|
|
|
|
processed = HandleActivate(state, minimized != 0, hwnd);
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
// system messages such as SC_CLOSE are sent as WM_COMMANDs to the
|
|
// parent MDI frame and we must let the DefFrameProc() have them
|
|
// for these commands to work (without it, closing the maximized
|
|
// MDI children doesn't work, for example)
|
|
{
|
|
WXWORD id, cmd;
|
|
WXHWND hwnd;
|
|
UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
|
|
|
|
if ( id == wxID_MDI_MORE_WINDOWS ||
|
|
(cmd == 0 /* menu */ &&
|
|
id >= SC_SIZE /* first system menu command */) )
|
|
{
|
|
MSWDefWindowProc(message, wParam, lParam);
|
|
processed = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CREATE:
|
|
m_clientWindow = OnCreateClient();
|
|
// Uses own style for client style
|
|
if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
|
|
{
|
|
wxLogMessage(_("Failed to create MDI parent frame."));
|
|
|
|
rc = -1;
|
|
}
|
|
|
|
processed = true;
|
|
break;
|
|
}
|
|
|
|
if ( !processed )
|
|
rc = wxFrame::MSWWindowProc(message, wParam, lParam);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void wxMDIParentFrame::OnActivate(wxActivateEvent& WXUNUSED(event))
|
|
{
|
|
// The base class version saves the current focus when we are being
|
|
// deactivated and restores it when the window is activated again, but this
|
|
// is not necessary here as DefWindowProc() for MDI parent frame already
|
|
// takes care of re-activating the MDI child that had been active the last
|
|
// time, and MDI children remember their own last focused child already,
|
|
// being subclasses of wxTLW.
|
|
//
|
|
// Moreover, in addition to being unnecessary, this can be actively harmful
|
|
// if we somehow don't have the focus any more at the moment of activation
|
|
// loss as happens when showing a standard file dialog under Windows 7, see
|
|
// #16635: in this case the base class just gives the focus to its first
|
|
// child, meaning that we can switch to a different MDI child, which is
|
|
// worse than losing focus inside the current child.
|
|
//
|
|
// So we don't let the base class have this event to prevent this from
|
|
// happening. But the event is not really processed, so we set a flag here
|
|
// which is used in HandleActivate() below to check if the event was really
|
|
// processed (and not skipped) in the user code or just reached this dummy
|
|
// handler.
|
|
m_activationNotHandled = true;
|
|
}
|
|
|
|
bool wxMDIParentFrame::HandleActivate(int state, bool minimized, WXHWND activate)
|
|
{
|
|
bool processed = false;
|
|
|
|
// Set the flag before testing it to ensure the only way for it to be true
|
|
// is to be set in our OnActivate() -- and not just remain set from the
|
|
// last time.
|
|
m_activationNotHandled = false;
|
|
|
|
if ( wxWindow::HandleActivate(state, minimized, activate) )
|
|
{
|
|
// already processed, unless we artificially marked the event as
|
|
// handled in our own handler without really processing it
|
|
processed = !m_activationNotHandled;
|
|
}
|
|
|
|
// Also generate the (de)activation event for the current child, if any, to
|
|
// allow updating its state and, in particular, remembering or restoring
|
|
// its last focused window.
|
|
if ( GetActiveChild() )
|
|
{
|
|
if ( GetActiveChild()->HandleActivate(state, minimized, activate) )
|
|
processed = true;
|
|
}
|
|
|
|
return processed;
|
|
}
|
|
|
|
#if wxUSE_MENUS
|
|
|
|
void wxMDIParentFrame::OnMDIChild(wxCommandEvent& event)
|
|
{
|
|
wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
|
|
while ( node )
|
|
{
|
|
wxWindow *child = node->GetData();
|
|
if ( child->GetHWND() )
|
|
{
|
|
int childId = wxGetWindowId(child->GetHWND());
|
|
if ( childId == event.GetId() )
|
|
{
|
|
wxStaticCast(child, wxMDIChildFrame)->Activate();
|
|
return;
|
|
}
|
|
}
|
|
|
|
node = node->GetNext();
|
|
}
|
|
|
|
wxFAIL_MSG( "unknown MDI child selected?" );
|
|
}
|
|
|
|
void wxMDIParentFrame::OnMDICommand(wxCommandEvent& event)
|
|
{
|
|
WXWPARAM wParam = 0;
|
|
WXLPARAM lParam = 0;
|
|
int msg;
|
|
switch ( event.GetId() )
|
|
{
|
|
case wxID_MDI_WINDOW_CASCADE:
|
|
msg = WM_MDICASCADE;
|
|
wParam = MDITILE_SKIPDISABLED;
|
|
break;
|
|
|
|
case wxID_MDI_WINDOW_TILE_HORZ:
|
|
wParam |= MDITILE_HORIZONTAL;
|
|
// fall through
|
|
|
|
case wxID_MDI_WINDOW_TILE_VERT:
|
|
if ( !wParam )
|
|
wParam = MDITILE_VERTICAL;
|
|
msg = WM_MDITILE;
|
|
wParam |= MDITILE_SKIPDISABLED;
|
|
break;
|
|
|
|
case wxID_MDI_WINDOW_ARRANGE_ICONS:
|
|
msg = WM_MDIICONARRANGE;
|
|
break;
|
|
|
|
case wxID_MDI_WINDOW_NEXT:
|
|
msg = WM_MDINEXT;
|
|
lParam = 0; // next child
|
|
break;
|
|
|
|
case wxID_MDI_WINDOW_PREV:
|
|
msg = WM_MDINEXT;
|
|
lParam = 1; // previous child
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( "unknown MDI command" );
|
|
return;
|
|
}
|
|
|
|
::SendMessage(GetWinHwnd(GetClientWindow()), msg, wParam, lParam);
|
|
}
|
|
|
|
#endif // wxUSE_MENUS
|
|
|
|
WXLRESULT wxMDIParentFrame::MSWDefWindowProc(WXUINT message,
|
|
WXWPARAM wParam,
|
|
WXLPARAM lParam)
|
|
{
|
|
WXHWND clientWnd;
|
|
if ( GetClientWindow() )
|
|
clientWnd = GetClientWindow()->GetHWND();
|
|
else
|
|
clientWnd = 0;
|
|
|
|
return DefFrameProc(GetHwnd(), (HWND)clientWnd, message, wParam, lParam);
|
|
}
|
|
|
|
bool wxMDIParentFrame::MSWTranslateMessage(WXMSG* msg)
|
|
{
|
|
MSG *pMsg = (MSG *)msg;
|
|
|
|
// first let the current child get it
|
|
wxMDIChildFrame * const child = GetActiveChild();
|
|
if ( child && child->MSWTranslateMessage(msg) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// then try out accelerator table (will also check the accelerators for the
|
|
// normal menu items)
|
|
if ( wxFrame::MSWTranslateMessage(msg) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#if wxUSE_MENUS && wxUSE_ACCEL
|
|
// but it doesn't check for the (custom) accelerators of the window menu
|
|
// items as it's not part of the menu bar as it's handled by Windows itself
|
|
// so we need to do this explicitly
|
|
if ( m_accelWindowMenu && m_accelWindowMenu->Translate(this, msg) )
|
|
return true;
|
|
#endif // wxUSE_MENUS && wxUSE_ACCEL
|
|
|
|
// finally, check for MDI specific built-in accelerators
|
|
if ( pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN )
|
|
{
|
|
if ( ::TranslateMDISysAccel(GetWinHwnd(GetClientWindow()), pMsg))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ===========================================================================
|
|
// wxMDIChildFrame
|
|
// ===========================================================================
|
|
|
|
void wxMDIChildFrame::Init()
|
|
{
|
|
m_needsResize = true;
|
|
}
|
|
|
|
bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
|
|
wxWindowID id,
|
|
const wxString& title,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxString& name)
|
|
{
|
|
m_mdiParent = parent;
|
|
|
|
SetName(name);
|
|
|
|
if ( id != wxID_ANY )
|
|
m_windowId = id;
|
|
else
|
|
m_windowId = NewControlId();
|
|
|
|
if ( parent )
|
|
{
|
|
parent->AddChild(this);
|
|
}
|
|
|
|
int x = pos.x;
|
|
int y = pos.y;
|
|
int width = size.x;
|
|
int height = size.y;
|
|
|
|
MDICREATESTRUCT mcs;
|
|
|
|
wxString className =
|
|
wxApp::GetRegisteredClassName(wxT("wxMDIChildFrame"), COLOR_WINDOW);
|
|
if ( !(style & wxFULL_REPAINT_ON_RESIZE) )
|
|
className += wxApp::GetNoRedrawClassSuffix();
|
|
|
|
mcs.szClass = className.t_str();
|
|
mcs.szTitle = title.t_str();
|
|
mcs.hOwner = wxGetInstance();
|
|
if (x != wxDefaultCoord)
|
|
mcs.x = x;
|
|
else
|
|
mcs.x = CW_USEDEFAULT;
|
|
|
|
if (y != wxDefaultCoord)
|
|
mcs.y = y;
|
|
else
|
|
mcs.y = CW_USEDEFAULT;
|
|
|
|
if (width != wxDefaultCoord)
|
|
mcs.cx = width;
|
|
else
|
|
mcs.cx = CW_USEDEFAULT;
|
|
|
|
if (height != wxDefaultCoord)
|
|
mcs.cy = height;
|
|
else
|
|
mcs.cy = CW_USEDEFAULT;
|
|
|
|
DWORD msflags = WS_OVERLAPPED | WS_CLIPCHILDREN;
|
|
if (style & wxMINIMIZE_BOX)
|
|
msflags |= WS_MINIMIZEBOX;
|
|
if (style & wxMAXIMIZE_BOX)
|
|
msflags |= WS_MAXIMIZEBOX;
|
|
if (style & wxRESIZE_BORDER)
|
|
msflags |= WS_THICKFRAME;
|
|
if (style & wxSYSTEM_MENU)
|
|
msflags |= WS_SYSMENU;
|
|
if ((style & wxMINIMIZE) || (style & wxICONIZE))
|
|
msflags |= WS_MINIMIZE;
|
|
if (style & wxMAXIMIZE)
|
|
msflags |= WS_MAXIMIZE;
|
|
if (style & wxCAPTION)
|
|
msflags |= WS_CAPTION;
|
|
|
|
mcs.style = msflags;
|
|
|
|
mcs.lParam = 0;
|
|
|
|
wxWindowCreationHook hook(this);
|
|
|
|
m_hWnd = (WXHWND)::SendMessage(GetWinHwnd(parent->GetClientWindow()),
|
|
WM_MDICREATE, 0, (LPARAM)&mcs);
|
|
|
|
if ( !m_hWnd )
|
|
{
|
|
wxLogLastError(wxT("WM_MDICREATE"));
|
|
return false;
|
|
}
|
|
|
|
SubclassWin(m_hWnd);
|
|
|
|
parent->AddMDIChild(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
wxMDIChildFrame::~wxMDIChildFrame()
|
|
{
|
|
// if we hadn't been created, there is nothing to destroy
|
|
if ( !m_hWnd )
|
|
return;
|
|
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
|
|
parent->RemoveMDIChild(this);
|
|
|
|
// will be destroyed by DestroyChildren() but reset them before calling it
|
|
// to avoid using dangling pointers if a callback comes in the meanwhile
|
|
#if wxUSE_TOOLBAR
|
|
m_frameToolBar = NULL;
|
|
#endif
|
|
#if wxUSE_STATUSBAR
|
|
m_frameStatusBar = NULL;
|
|
#endif // wxUSE_STATUSBAR
|
|
|
|
DestroyChildren();
|
|
|
|
MDIRemoveWindowMenu(NULL, m_hMenu);
|
|
|
|
// MDIRemoveWindowMenu() doesn't update the MDI menu when called with NULL
|
|
// window, so do it ourselves.
|
|
MDISetMenu(parent->GetClientWindow(),
|
|
(HMENU)parent->MSWGetActiveMenu(),
|
|
GetMDIWindowMenu(parent));
|
|
|
|
MSWDestroyWindow();
|
|
}
|
|
|
|
bool wxMDIChildFrame::Show(bool show)
|
|
{
|
|
if (!wxFrame::Show(show))
|
|
return false;
|
|
|
|
// KH: Without this call, new MDI children do not become active.
|
|
// This was added here after the same BringWindowToTop call was
|
|
// removed from wxTopLevelWindow::Show (November 2005)
|
|
if ( show )
|
|
::BringWindowToTop(GetHwnd());
|
|
|
|
// we need to refresh the MDI frame window menu to include (or exclude if
|
|
// we've been hidden) this frame
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
MDISetMenu(parent->GetClientWindow(), NULL, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
wxMDIChildFrame::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
|
{
|
|
// we need to disable client area origin adjustments used for the child
|
|
// windows for the frame itself
|
|
wxMDIChildFrameBase::DoSetSize(x, y, width, height, sizeFlags);
|
|
}
|
|
|
|
// Set the client size (i.e. leave the calculation of borders etc.
|
|
// to wxWidgets)
|
|
void wxMDIChildFrame::DoSetClientSize(int width, int height)
|
|
{
|
|
HWND hWnd = GetHwnd();
|
|
|
|
RECT rect;
|
|
::GetClientRect(hWnd, &rect);
|
|
|
|
RECT rect2;
|
|
GetWindowRect(hWnd, &rect2);
|
|
|
|
// Find the difference between the entire window (title bar and all)
|
|
// and the client area; add this to the new client size to move the
|
|
// window
|
|
int actual_width = rect2.right - rect2.left - rect.right + width;
|
|
int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
|
|
|
|
#if wxUSE_STATUSBAR
|
|
if (GetStatusBar() && GetStatusBar()->IsShown())
|
|
{
|
|
int sx, sy;
|
|
GetStatusBar()->GetSize(&sx, &sy);
|
|
actual_height += sy;
|
|
}
|
|
#endif // wxUSE_STATUSBAR
|
|
|
|
POINT point;
|
|
point.x = rect2.left;
|
|
point.y = rect2.top;
|
|
|
|
// If there's an MDI parent, must subtract the parent's top left corner
|
|
// since MoveWindow moves relative to the parent
|
|
wxMDIParentFrame * const mdiParent = GetMDIParent();
|
|
::ScreenToClient(GetHwndOf(mdiParent->GetClientWindow()), &point);
|
|
|
|
MoveWindow(hWnd, point.x, point.y, actual_width, actual_height, (BOOL)true);
|
|
|
|
wxSize size(width, height);
|
|
wxSizeEvent event(size, m_windowId);
|
|
event.SetEventObject( this );
|
|
HandleWindowEvent(event);
|
|
}
|
|
|
|
// Unlike other wxTopLevelWindowBase, the mdi child's "GetPosition" is not the
|
|
// same as its GetScreenPosition
|
|
void wxMDIChildFrame::DoGetScreenPosition(int *x, int *y) const
|
|
{
|
|
HWND hWnd = GetHwnd();
|
|
|
|
RECT rect;
|
|
::GetWindowRect(hWnd, &rect);
|
|
if (x)
|
|
*x = rect.left;
|
|
if (y)
|
|
*y = rect.top;
|
|
}
|
|
|
|
|
|
void wxMDIChildFrame::DoGetPosition(int *x, int *y) const
|
|
{
|
|
RECT rect;
|
|
GetWindowRect(GetHwnd(), &rect);
|
|
POINT point;
|
|
point.x = rect.left;
|
|
point.y = rect.top;
|
|
|
|
// Since we now have the absolute screen coords,
|
|
// if there's a parent we must subtract its top left corner
|
|
wxMDIParentFrame * const mdiParent = GetMDIParent();
|
|
::ScreenToClient(GetHwndOf(mdiParent->GetClientWindow()), &point);
|
|
|
|
if (x)
|
|
*x = point.x;
|
|
if (y)
|
|
*y = point.y;
|
|
}
|
|
|
|
void wxMDIChildFrame::InternalSetMenuBar()
|
|
{
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
|
|
MDIInsertWindowMenu(parent->GetClientWindow(),
|
|
m_hMenu, GetMDIWindowMenu(parent));
|
|
}
|
|
|
|
void wxMDIChildFrame::DetachMenuBar()
|
|
{
|
|
MDIRemoveWindowMenu(NULL, m_hMenu);
|
|
wxFrame::DetachMenuBar();
|
|
}
|
|
|
|
WXHICON wxMDIChildFrame::GetDefaultIcon() const
|
|
{
|
|
// we don't have any standard icons (any more)
|
|
return (WXHICON)0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MDI operations
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void wxMDIChildFrame::Maximize(bool maximize)
|
|
{
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
if ( parent && parent->GetClientWindow() )
|
|
{
|
|
if ( !IsShown() )
|
|
{
|
|
// Turn off redrawing in the MDI client window because otherwise
|
|
// maximizing it would also show it and we don't want this for
|
|
// hidden windows.
|
|
::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_SETREDRAW,
|
|
FALSE, 0L);
|
|
}
|
|
|
|
::SendMessage(GetWinHwnd(parent->GetClientWindow()),
|
|
maximize ? WM_MDIMAXIMIZE : WM_MDIRESTORE,
|
|
(WPARAM)GetHwnd(), 0);
|
|
|
|
if ( !IsShown() )
|
|
{
|
|
// Hide back the child window shown by maximizing it.
|
|
::ShowWindow(GetHwnd(), SW_HIDE);
|
|
|
|
// Turn redrawing in the MDI client back on.
|
|
::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_SETREDRAW,
|
|
TRUE, 0L);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxMDIChildFrame::Restore()
|
|
{
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
if ( parent && parent->GetClientWindow() )
|
|
{
|
|
::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIRESTORE,
|
|
(WPARAM) GetHwnd(), 0);
|
|
}
|
|
}
|
|
|
|
void wxMDIChildFrame::Activate()
|
|
{
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
if ( parent && parent->GetClientWindow() )
|
|
{
|
|
// Activating an iconized MDI frame doesn't do anything, so restore it
|
|
// first to really present it to the user.
|
|
if ( IsIconized() )
|
|
Restore();
|
|
|
|
::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIACTIVATE,
|
|
(WPARAM) GetHwnd(), 0);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MDI window proc and message handlers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
WXLRESULT wxMDIChildFrame::MSWWindowProc(WXUINT message,
|
|
WXWPARAM wParam,
|
|
WXLPARAM lParam)
|
|
{
|
|
WXLRESULT rc = 0;
|
|
bool processed = false;
|
|
|
|
switch ( message )
|
|
{
|
|
case WM_GETMINMAXINFO:
|
|
processed = HandleGetMinMaxInfo((MINMAXINFO *)lParam);
|
|
break;
|
|
|
|
case WM_MDIACTIVATE:
|
|
{
|
|
WXWORD act;
|
|
WXHWND hwndAct, hwndDeact;
|
|
UnpackMDIActivate(wParam, lParam, &act, &hwndAct, &hwndDeact);
|
|
|
|
processed = HandleMDIActivate(act, hwndAct, hwndDeact);
|
|
}
|
|
// fall through
|
|
|
|
case WM_MOVE:
|
|
// must pass WM_MOVE to DefMDIChildProc() to recalculate MDI client
|
|
// scrollbars if necessary
|
|
|
|
// fall through
|
|
|
|
case WM_SIZE:
|
|
// must pass WM_SIZE to DefMDIChildProc(), otherwise many weird
|
|
// things happen
|
|
MSWDefWindowProc(message, wParam, lParam);
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
processed = HandleWindowPosChanging((LPWINDOWPOS)lParam);
|
|
break;
|
|
}
|
|
|
|
if ( !processed )
|
|
rc = wxFrame::MSWWindowProc(message, wParam, lParam);
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate),
|
|
WXHWND hwndAct,
|
|
WXHWND hwndDeact)
|
|
{
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
|
|
WXHMENU hMenuToSet = 0;
|
|
|
|
bool activated;
|
|
|
|
if ( m_hWnd == hwndAct )
|
|
{
|
|
activated = true;
|
|
parent->SetActiveChild(this);
|
|
|
|
WXHMENU hMenuChild = m_hMenu;
|
|
if ( hMenuChild )
|
|
hMenuToSet = hMenuChild;
|
|
}
|
|
else if ( m_hWnd == hwndDeact )
|
|
{
|
|
wxASSERT_MSG( parent->GetActiveChild() == this,
|
|
wxT("can't deactivate MDI child which wasn't active!") );
|
|
|
|
activated = false;
|
|
parent->SetActiveChild(NULL);
|
|
|
|
WXHMENU hMenuParent = parent->m_hMenu;
|
|
|
|
// activate the parent menu only when there is no other child
|
|
// that has been activated
|
|
if ( hMenuParent && !hwndAct )
|
|
hMenuToSet = hMenuParent;
|
|
}
|
|
else
|
|
{
|
|
// we have nothing to do with it
|
|
return false;
|
|
}
|
|
|
|
if ( hMenuToSet )
|
|
{
|
|
MDISetMenu(parent->GetClientWindow(),
|
|
(HMENU)hMenuToSet, GetMDIWindowMenu(parent));
|
|
}
|
|
|
|
wxActivateEvent event(wxEVT_ACTIVATE, activated, m_windowId);
|
|
event.SetEventObject( this );
|
|
|
|
ResetWindowStyle(NULL);
|
|
|
|
return HandleWindowEvent(event);
|
|
}
|
|
|
|
bool wxMDIChildFrame::HandleWindowPosChanging(void *pos)
|
|
{
|
|
WINDOWPOS *lpPos = (WINDOWPOS *)pos;
|
|
|
|
if (!(lpPos->flags & SWP_NOSIZE))
|
|
{
|
|
RECT rectClient;
|
|
DWORD dwExStyle = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE);
|
|
DWORD dwStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE);
|
|
if (ResetWindowStyle((void *) & rectClient) && (dwStyle & WS_MAXIMIZE))
|
|
{
|
|
::AdjustWindowRectEx(&rectClient, dwStyle, false, dwExStyle);
|
|
lpPos->x = rectClient.left;
|
|
lpPos->y = rectClient.top;
|
|
lpPos->cx = rectClient.right - rectClient.left;
|
|
lpPos->cy = rectClient.bottom - rectClient.top;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool wxMDIChildFrame::HandleGetMinMaxInfo(void *mmInfo)
|
|
{
|
|
// Get the window max size from DefMDIChildProc() as it calculates it
|
|
// correctly from the size of the MDI parent frame.
|
|
MSWDefWindowProc(WM_GETMINMAXINFO, 0, (LPARAM)mmInfo);
|
|
|
|
// But then handle the message as usual at the base class level to allow
|
|
// overriding min/max frame size as for the normal frames.
|
|
return false;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MDI specific message translation/preprocessing
|
|
// ---------------------------------------------------------------------------
|
|
|
|
WXLRESULT wxMDIChildFrame::MSWDefWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
|
|
{
|
|
return DefMDIChildProc(GetHwnd(),
|
|
(UINT)message, (WPARAM)wParam, (LPARAM)lParam);
|
|
}
|
|
|
|
bool wxMDIChildFrame::MSWTranslateMessage(WXMSG* msg)
|
|
{
|
|
// we must pass the parent frame to ::TranslateAccelerator(), otherwise it
|
|
// doesn't do its job correctly for MDI child menus
|
|
return MSWDoTranslateMessage(GetMDIParent(), msg);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// misc
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void wxMDIChildFrame::MSWDestroyWindow()
|
|
{
|
|
wxMDIParentFrame * const parent = GetMDIParent();
|
|
|
|
// Must make sure this handle is invalidated (set to NULL) since all sorts
|
|
// of things could happen after the child client is destroyed, but before
|
|
// the wxFrame is destroyed.
|
|
|
|
HWND oldHandle = (HWND)GetHWND();
|
|
SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIDESTROY,
|
|
(WPARAM)oldHandle, 0);
|
|
|
|
if (parent->GetActiveChild() == NULL)
|
|
ResetWindowStyle(NULL);
|
|
|
|
if (m_hMenu)
|
|
{
|
|
::DestroyMenu((HMENU) m_hMenu);
|
|
m_hMenu = 0;
|
|
}
|
|
wxRemoveHandleAssociation(this);
|
|
m_hWnd = 0;
|
|
}
|
|
|
|
// Change the client window's extended style so we don't get a client edge
|
|
// style when a child is maximised (a double border looks silly.)
|
|
bool wxMDIChildFrame::ResetWindowStyle(void *vrect)
|
|
{
|
|
RECT *rect = (RECT *)vrect;
|
|
wxMDIParentFrame * const pFrameWnd = GetMDIParent();
|
|
wxMDIChildFrame* pChild = pFrameWnd->GetActiveChild();
|
|
|
|
if (!pChild || (pChild == this))
|
|
{
|
|
HWND hwndClient = GetWinHwnd(pFrameWnd->GetClientWindow());
|
|
DWORD dwStyle = ::GetWindowLong(hwndClient, GWL_EXSTYLE);
|
|
|
|
// we want to test whether there is a maximized child, so just set
|
|
// dwThisStyle to 0 if there is no child at all
|
|
DWORD dwThisStyle = pChild
|
|
? ::GetWindowLong(GetWinHwnd(pChild), GWL_STYLE) : 0;
|
|
DWORD dwNewStyle = dwStyle;
|
|
if ( dwThisStyle & WS_MAXIMIZE )
|
|
dwNewStyle &= ~(WS_EX_CLIENTEDGE);
|
|
else
|
|
dwNewStyle |= WS_EX_CLIENTEDGE;
|
|
|
|
if (dwStyle != dwNewStyle)
|
|
{
|
|
// force update of everything
|
|
::RedrawWindow(hwndClient, NULL, NULL,
|
|
RDW_INVALIDATE | RDW_ALLCHILDREN);
|
|
::SetWindowLong(hwndClient, GWL_EXSTYLE, dwNewStyle);
|
|
::SetWindowPos(hwndClient, NULL, 0, 0, 0, 0,
|
|
SWP_FRAMECHANGED | SWP_NOACTIVATE |
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
|
|
SWP_NOCOPYBITS);
|
|
if (rect)
|
|
::GetClientRect(hwndClient, rect);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ===========================================================================
|
|
// wxMDIClientWindow: the window of predefined (by Windows) class which
|
|
// contains the child frames
|
|
// ===========================================================================
|
|
|
|
bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
|
|
{
|
|
m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE);
|
|
|
|
CLIENTCREATESTRUCT ccs;
|
|
m_windowStyle = style;
|
|
m_parent = parent;
|
|
|
|
ccs.hWindowMenu = GetMDIWindowMenu(parent);
|
|
ccs.idFirstChild = wxFIRST_MDI_CHILD;
|
|
|
|
DWORD msStyle = MDIS_ALLCHILDSTYLES | WS_VISIBLE | WS_CHILD |
|
|
WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
|
|
if ( style & wxHSCROLL )
|
|
msStyle |= WS_HSCROLL;
|
|
if ( style & wxVSCROLL )
|
|
msStyle |= WS_VSCROLL;
|
|
|
|
DWORD exStyle = WS_EX_CLIENTEDGE;
|
|
|
|
wxWindowCreationHook hook(this);
|
|
m_hWnd = (WXHWND)::CreateWindowEx
|
|
(
|
|
exStyle,
|
|
wxT("MDICLIENT"),
|
|
NULL,
|
|
msStyle,
|
|
0, 0, 0, 0,
|
|
GetWinHwnd(parent),
|
|
NULL,
|
|
wxGetInstance(),
|
|
(LPSTR)(LPCLIENTCREATESTRUCT)&ccs);
|
|
if ( !m_hWnd )
|
|
{
|
|
wxLogLastError(wxT("CreateWindowEx(MDI client)"));
|
|
|
|
return false;
|
|
}
|
|
|
|
SubclassWin(m_hWnd);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Explicitly call default scroll behaviour
|
|
void wxMDIClientWindow::OnScroll(wxScrollEvent& event)
|
|
{
|
|
// Note: for client windows, the scroll position is not set in
|
|
// WM_HSCROLL, WM_VSCROLL, so we can't easily determine what
|
|
// scroll position we're at.
|
|
// This makes it hard to paint patterns or bitmaps in the background,
|
|
// and have the client area scrollable as well.
|
|
|
|
if ( event.GetOrientation() == wxHORIZONTAL )
|
|
m_scrollX = event.GetPosition(); // Always returns zero!
|
|
else
|
|
m_scrollY = event.GetPosition(); // Always returns zero!
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxMDIClientWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
|
{
|
|
// Try to fix a problem whereby if you show an MDI child frame, then reposition the
|
|
// client area, you can end up with a non-refreshed portion in the client window
|
|
// (see OGL studio sample). So check if the position is changed and if so,
|
|
// redraw the MDI child frames.
|
|
|
|
const wxPoint oldPos = GetPosition();
|
|
|
|
wxWindow::DoSetSize(x, y, width, height, sizeFlags | wxSIZE_FORCE);
|
|
|
|
const wxPoint newPos = GetPosition();
|
|
|
|
if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y))
|
|
{
|
|
if (GetParent())
|
|
{
|
|
wxWindowList::compatibility_iterator node = GetParent()->GetChildren().GetFirst();
|
|
while (node)
|
|
{
|
|
wxWindow *child = node->GetData();
|
|
if (wxDynamicCast(child, wxMDIChildFrame))
|
|
{
|
|
::RedrawWindow(GetHwndOf(child),
|
|
NULL,
|
|
NULL,
|
|
RDW_FRAME |
|
|
RDW_ALLCHILDREN |
|
|
RDW_INVALIDATE);
|
|
}
|
|
node = node->GetNext();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxMDIChildFrame::OnIdle(wxIdleEvent& event)
|
|
{
|
|
// MDI child frames get their WM_SIZE when they're constructed but at this
|
|
// moment they don't have any children yet so all child windows will be
|
|
// positioned incorrectly when they are added later - to fix this, we
|
|
// generate an artificial size event here
|
|
if ( m_needsResize )
|
|
{
|
|
m_needsResize = false; // avoid any possibility of recursion
|
|
|
|
SendSizeEvent();
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// private helper functions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
namespace
|
|
{
|
|
|
|
void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
|
|
{
|
|
if ( hmenuFrame || hmenuWindow )
|
|
{
|
|
// Under XP, the last error seems to be not reset by this function, so
|
|
// ensure we don't report spurious errors below when setting the menu
|
|
// initially.
|
|
::SetLastError(ERROR_SUCCESS);
|
|
|
|
if ( !::SendMessage(GetWinHwnd(win),
|
|
WM_MDISETMENU,
|
|
(WPARAM)hmenuFrame,
|
|
(LPARAM)hmenuWindow) )
|
|
{
|
|
const DWORD err = ::GetLastError();
|
|
if ( err != ERROR_SUCCESS )
|
|
{
|
|
wxLogApiError(wxT("SendMessage(WM_MDISETMENU)"), err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// update menu bar of the parent window
|
|
wxWindow *parent = win->GetParent();
|
|
wxCHECK_RET( parent, wxT("MDI client without parent frame? weird...") );
|
|
|
|
::SendMessage(GetWinHwnd(win), WM_MDIREFRESHMENU, 0, 0L);
|
|
|
|
::DrawMenuBar(GetWinHwnd(parent));
|
|
}
|
|
|
|
class MenuIterator
|
|
{
|
|
public:
|
|
explicit MenuIterator(HMENU hmenu)
|
|
: m_hmenu(hmenu),
|
|
m_numItems(::GetMenuItemCount(hmenu)),
|
|
m_pos(-1)
|
|
{
|
|
m_mii.fMask = MIIM_STRING;
|
|
m_mii.dwTypeData = m_buf;
|
|
}
|
|
|
|
bool GetNext(wxString& str)
|
|
{
|
|
// Loop until we get the label of the next menu item.
|
|
for ( m_pos++; m_pos < m_numItems; m_pos++ )
|
|
{
|
|
// As cch field is updated by GetMenuItemInfo(), it's important to
|
|
// reset it to the size of the buffer before each call.
|
|
m_mii.cch = WXSIZEOF(m_buf);
|
|
|
|
if ( !::GetMenuItemInfo(m_hmenu, m_pos, TRUE, &m_mii) )
|
|
{
|
|
wxLogLastError(wxString::Format("GetMenuItemInfo(%d)", m_pos));
|
|
continue;
|
|
}
|
|
|
|
if ( !m_mii.cch )
|
|
{
|
|
// This isn't a string menu at all.
|
|
continue;
|
|
}
|
|
|
|
str = m_buf;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int GetPos() const { return m_pos; }
|
|
|
|
private:
|
|
const HMENU m_hmenu;
|
|
const int m_numItems;
|
|
int m_pos;
|
|
|
|
wxChar m_buf[1024];
|
|
WinStruct<MENUITEMINFO> m_mii;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(MenuIterator);
|
|
};
|
|
|
|
void MDIInsertWindowMenu(wxWindow *win, WXHMENU hMenu, HMENU menuWin)
|
|
{
|
|
HMENU hmenu = (HMENU)hMenu;
|
|
|
|
if ( menuWin )
|
|
{
|
|
// Try to insert Window menu in front of Help, otherwise append it.
|
|
bool inserted = false;
|
|
wxString buf;
|
|
MenuIterator it(hmenu);
|
|
while ( it.GetNext(buf) )
|
|
{
|
|
const wxString label = wxStripMenuCodes(buf);
|
|
if ( label == wxGetStockLabel(wxID_HELP, wxSTOCK_NOFLAGS) )
|
|
{
|
|
inserted = true;
|
|
::InsertMenu(hmenu, it.GetPos(),
|
|
MF_BYPOSITION | MF_POPUP | MF_STRING,
|
|
(UINT_PTR)menuWin,
|
|
wxString(wxGetTranslation(WINDOW_MENU_LABEL)).t_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !inserted )
|
|
{
|
|
::AppendMenu(hmenu, MF_POPUP,
|
|
(UINT_PTR)menuWin,
|
|
wxString(wxGetTranslation(WINDOW_MENU_LABEL)).t_str());
|
|
}
|
|
}
|
|
|
|
MDISetMenu(win, hmenu, menuWin);
|
|
}
|
|
|
|
void MDIRemoveWindowMenu(wxWindow *win, WXHMENU hMenu)
|
|
{
|
|
HMENU hmenu = (HMENU)hMenu;
|
|
|
|
if ( hmenu )
|
|
{
|
|
wxString buf;
|
|
MenuIterator it(hmenu);
|
|
while ( it.GetNext(buf) )
|
|
{
|
|
if ( wxStrcmp(buf, wxGetTranslation(WINDOW_MENU_LABEL)) == 0 )
|
|
{
|
|
if ( !::RemoveMenu(hmenu, it.GetPos(), MF_BYPOSITION) )
|
|
{
|
|
wxLogLastError(wxT("RemoveMenu"));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( win )
|
|
{
|
|
// we don't change the windows menu, but we update the main one
|
|
MDISetMenu(win, hmenu, NULL);
|
|
}
|
|
}
|
|
|
|
void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
|
|
WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact)
|
|
{
|
|
*activate = true;
|
|
*hwndAct = (WXHWND)lParam;
|
|
*hwndDeact = (WXHWND)wParam;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
#endif // wxUSE_MDI && !defined(__WXUNIVERSAL__)
|