- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1020 lines
26 KiB
C++
1020 lines
26 KiB
C++
//----------------------------------------------------------------------------
|
|
// ObjectWindows
|
|
// (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// Implementation of class TFrameWindow
|
|
//----------------------------------------------------------------------------
|
|
#pragma hdrignore SECTION
|
|
#include <owl/owlpch.h>
|
|
#include <owl/framewin.h>
|
|
#include <owl/applicat.h>
|
|
#include <owl/menu.h>
|
|
|
|
#if !defined(SECTION) || SECTION == 1
|
|
|
|
DEFINE_RESPONSE_TABLE1(TFrameWindow, TWindow)
|
|
EV_WM_PAINT,
|
|
EV_WM_ERASEBKGND,
|
|
EV_WM_QUERYDRAGICON,
|
|
EV_WM_INITMENUPOPUP,
|
|
EV_WM_SETFOCUS,
|
|
EV_WM_SIZE,
|
|
EV_WM_PARENTNOTIFY,
|
|
EV_WM_ENTERIDLE,
|
|
END_RESPONSE_TABLE;
|
|
|
|
//
|
|
// Enable/disable the command sender, in this case the menu item.
|
|
//
|
|
void
|
|
TMenuItemEnabler::Enable(bool enable)
|
|
{
|
|
TCommandEnabler::Enable(enable);
|
|
::EnableMenuItem(HMenu, Position,
|
|
MF_BYPOSITION | (enable ? MF_ENABLED : MF_GRAYED));
|
|
}
|
|
|
|
//
|
|
// Set the text for the command sender, in this case the menu item.
|
|
//
|
|
void
|
|
TMenuItemEnabler::SetText(const char far* str)
|
|
{
|
|
::ModifyMenu(HMenu, Position, MF_BYPOSITION | MF_STRING, Id, str);
|
|
}
|
|
|
|
//
|
|
// Set the check state of the command sender, in this case the menu item.
|
|
//
|
|
void
|
|
TMenuItemEnabler::SetCheck(int state)
|
|
{
|
|
::CheckMenuItem(HMenu, Position,
|
|
MF_BYPOSITION | (state == Checked ? MF_CHECKED : MF_UNCHECKED));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
// constructor for a TFrameWindow
|
|
//
|
|
TFrameWindow::TFrameWindow(TWindow* parent,
|
|
const char far* title,
|
|
TWindow* clientWnd,
|
|
bool shrinkToClient,
|
|
TModule* module)
|
|
{
|
|
//
|
|
// Initialize virtual base, in case the derived-most used default ctor
|
|
//
|
|
TWindow::Init(parent, title, module);
|
|
|
|
|
|
IconResId = 0; // remember that we still need to init
|
|
Init(clientWnd, shrinkToClient);
|
|
}
|
|
|
|
//
|
|
// constructor for a TFrameWindow. This ctor is generally not used by derived
|
|
// classes, only as generic alias to a framewindow-like HWND
|
|
//
|
|
TFrameWindow::TFrameWindow(HWND hWnd, TModule* module)
|
|
: TWindow(hWnd, module)
|
|
{
|
|
Init(0);
|
|
}
|
|
|
|
//
|
|
// Protected constructor for use by immediate virtually derived classes.
|
|
// Immediate derivitives must call Init() before constructions are done.
|
|
//
|
|
TFrameWindow::TFrameWindow()
|
|
{
|
|
IconResId = 0; // Zero this member to remember that we still need to init
|
|
}
|
|
|
|
//
|
|
// Normal initialization of a default constructed TFrameWindow. Is ignored
|
|
// if called more than once.
|
|
//
|
|
void
|
|
TFrameWindow::Init(TWindow* clientWnd, bool shrinkToClient)
|
|
{
|
|
if (!IconResId) {
|
|
Attr.Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
|
|
Attr.X = Attr.W = CW_USEDEFAULT;
|
|
|
|
if (clientWnd)
|
|
Attr.Style |= WS_CLIPCHILDREN;
|
|
|
|
if (shrinkToClient)
|
|
SetFlag(wfShrinkToClient);
|
|
|
|
Init(clientWnd);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Private initializer does a bulk of the common frame window initialization
|
|
//
|
|
void
|
|
TFrameWindow::Init(TWindow* clientWnd)
|
|
{
|
|
HWndRestoreFocus = 0;
|
|
KeyboardHandling = false;
|
|
ClientWnd = clientWnd;
|
|
|
|
MenuDescr = 0;
|
|
MergeModule = 0;
|
|
|
|
IconModule = ::Module;
|
|
IconResId = IDI_OWLAPP;
|
|
|
|
MinimizedPos = TPoint(-1,-1); // Windows convention for never minimized
|
|
|
|
if (ClientWnd) {
|
|
ClientWnd->SetParent(this);
|
|
ClientWnd->EnableAutoCreate(); // in case client is a dialog
|
|
SetBkgndColor(NoErase); // no need to erase client area
|
|
}
|
|
}
|
|
|
|
TFrameWindow::~TFrameWindow()
|
|
{
|
|
delete MenuDescr;
|
|
}
|
|
|
|
static uint32
|
|
GetMenuStateBits(HMENU hmenu, int count)
|
|
{
|
|
uint32 bit = 1;
|
|
uint32 result = 0;
|
|
|
|
for (int i=0; i<count; i++) {
|
|
uint state = GetMenuState(hmenu, i, MF_BYPOSITION);
|
|
if (state != (uint)-1) {
|
|
if (!(state & (MF_DISABLED | MF_GRAYED))) {
|
|
result |= bit;
|
|
}
|
|
}
|
|
bit <<= 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
TFrameWindow::EvInitMenuPopup(HMENU hPopupMenu, uint index, bool sysMenu)
|
|
{
|
|
if (!sysMenu && hPopupMenu) {
|
|
const int count = ::GetMenuItemCount(hPopupMenu);
|
|
|
|
// Save current state of visible top level menus
|
|
//
|
|
uint32 preState = 0;
|
|
if (hPopupMenu == GetMenu())
|
|
preState = GetMenuStateBits(hPopupMenu, count);
|
|
|
|
TWindow::EvInitMenuPopup(hPopupMenu, index, sysMenu);
|
|
|
|
// If the top level menu state changes, redraw the menu bar
|
|
//
|
|
if (hPopupMenu == GetMenu())
|
|
if (GetMenuStateBits(hPopupMenu, count) != preState)
|
|
DrawMenuBar();
|
|
}
|
|
}
|
|
|
|
static void
|
|
DoIdleAction(TWindow* win, void* idleCount)
|
|
{
|
|
win->IdleAction(*(long*)idleCount);
|
|
}
|
|
|
|
//
|
|
// TFrameWindow processes idle action occurs once per block of messages
|
|
//
|
|
bool
|
|
TFrameWindow::IdleAction(long idleCount)
|
|
{
|
|
if (idleCount == 0) {
|
|
// do command enabling for the menu bar if this is the active task
|
|
//
|
|
#if defined(BI_PLAT_WIN32)
|
|
if (GetFocus()) {
|
|
#else
|
|
HWND focus = GetFocus();
|
|
if (focus && ::GetWindowTask(focus) == GetCurrentTask()) {
|
|
#endif
|
|
long style = ::GetWindowLong(*this, GWL_STYLE);
|
|
if (!(style & WS_CHILD)) {
|
|
HMENU hMenu = ::GetMenu(*this);
|
|
if (IsMenu(hMenu))
|
|
HandleMessage(WM_INITMENUPOPUP, WPARAM(hMenu));
|
|
}
|
|
}
|
|
// give child windows an opportunity to do any idle processing
|
|
//
|
|
ForEach(DoIdleAction, &idleCount);
|
|
}
|
|
return false; // we don't need any more time for now
|
|
}
|
|
|
|
//
|
|
// Locate and return the child window that is the target of command and command
|
|
// enable messages.
|
|
//
|
|
HWND
|
|
TFrameWindow::GetCommandTarget()
|
|
{
|
|
// Start with the focus window
|
|
//
|
|
HWND hFocus = ::GetFocus();
|
|
|
|
// If its a child of ours, then return it.
|
|
//
|
|
if (IsChild(hFocus))
|
|
return hFocus;
|
|
|
|
// The focus is not our descendent, use our most recently active child
|
|
//
|
|
if (HWndRestoreFocus)
|
|
return HWndRestoreFocus;
|
|
|
|
// If we have a client, fall back on that
|
|
//
|
|
if (GetClientWindow())
|
|
return *GetClientWindow();
|
|
|
|
// Don't know who else to send it to, return ourselves
|
|
//
|
|
return *this;
|
|
}
|
|
|
|
//
|
|
// Handle WM_COMMAND to provide extra processing for commands: send the
|
|
// command down the command chain
|
|
//
|
|
LRESULT
|
|
TFrameWindow::EvCommand(uint id, HWND hWndCtl, uint notifyCode)
|
|
{
|
|
// walk the command chain from the focus child back up to us or until
|
|
// we hit a child that is an owl window. Delegate to owl-child or forward to
|
|
// our base if no child is found.
|
|
//
|
|
if (hWndCtl == 0) {
|
|
HWND hCmdTarget = GetCommandTarget();
|
|
|
|
// Check owl parentage too in case the hWnds were reparented
|
|
//
|
|
while (hCmdTarget && hCmdTarget != HWindow) {
|
|
TWindow* cmdTarget = GetWindowPtr(hCmdTarget);
|
|
|
|
if (cmdTarget)
|
|
return cmdTarget->EvCommand(id, hWndCtl, notifyCode);
|
|
|
|
hCmdTarget = ::GetParent(hCmdTarget);
|
|
}
|
|
}
|
|
|
|
return TWindow::EvCommand(id, hWndCtl, notifyCode);
|
|
}
|
|
|
|
//
|
|
// Handle WM_COMMAND_ENABLE to provide command enable distribution and default
|
|
// support for windows without command enable handlers.
|
|
//
|
|
void
|
|
TFrameWindow::EvCommandEnable(TCommandEnabler& commandEnabler)
|
|
{
|
|
// Don't process for windows out of our window tree (esp. other apps)
|
|
//
|
|
RouteCommandEnable(GetCommandTarget(), commandEnabler);
|
|
}
|
|
|
|
//
|
|
// Preprocess queued messages to handle translation of accelerators on a per
|
|
// window basis, and dialog-like keyboard navigation if enabled.
|
|
//
|
|
bool
|
|
TFrameWindow::PreProcessMsg(MSG& msg)
|
|
{
|
|
if (TWindow::PreProcessMsg(msg))
|
|
return true; // process accelerators
|
|
|
|
if (KeyboardHandling && msg.message >= WM_KEYFIRST &&
|
|
msg.message <= WM_KEYLAST) {
|
|
HWND parent = ::GetParent(msg.hwnd);
|
|
|
|
//
|
|
// Retrieve the COMBO handle if we're in the EDIT ctl
|
|
// parented to the combobox
|
|
//
|
|
char szClassName[0x10];
|
|
::GetClassName(parent, szClassName, sizeof(szClassName));
|
|
if (!strcmpi(szClassName, "COMBOBOX"))
|
|
parent = ::GetParent(parent);
|
|
|
|
if (parent && ::IsDialogMessage(parent, &msg));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Overrides TWindow's non-virtual SetMenu to make it virtual. This allows
|
|
// derived frame classes to implement this differently
|
|
// It also calls the application's PreProcessMenu() if it is the main window
|
|
// to let it make any changes just before setting.
|
|
//
|
|
bool
|
|
TFrameWindow::SetMenu(HMENU newMenu)
|
|
{
|
|
if (IsFlagSet(wfMainWindow))
|
|
GetApplication()->PreProcessMenu(newMenu);
|
|
|
|
return TWindow::SetMenu(newMenu);
|
|
}
|
|
|
|
//
|
|
// Perform a high-level menu assignment either before or after the HWND for the
|
|
// window has been created.
|
|
//
|
|
// returns true if successful; false otherwise
|
|
//
|
|
bool
|
|
TFrameWindow::AssignMenu(TResId menuResId)
|
|
{
|
|
if (menuResId != Attr.Menu) {
|
|
if (Attr.Menu.IsString())
|
|
delete (char far*)Attr.Menu;
|
|
|
|
Attr.Menu = menuResId.IsString() ? strnewdup(menuResId) : (char far*)menuResId;
|
|
}
|
|
|
|
// if the window has been created then load and set the new menu and destroy
|
|
// the old menu
|
|
//
|
|
if (!HWindow)
|
|
return true;
|
|
|
|
HMENU curMenu = GetMenu();
|
|
HMENU newMenu = GetModule()->LoadMenu(Attr.Menu);
|
|
|
|
if (!SetMenu(newMenu))
|
|
return false;
|
|
|
|
if (curMenu)
|
|
::DestroyMenu(curMenu);
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Set the Icon for use when this frame is minimized
|
|
//
|
|
bool
|
|
TFrameWindow::SetIcon(TModule* module, TResId resId)
|
|
{
|
|
IconModule = module;
|
|
IconResId = resId;
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Return the current client window as a TWindow*
|
|
//
|
|
TWindow*
|
|
TFrameWindow::GetClientWindow()
|
|
{
|
|
return ClientWnd;
|
|
}
|
|
|
|
//
|
|
// Remove the current client (if any) and set a new one.
|
|
// Assume clientWnd was parented to us.
|
|
//
|
|
TWindow*
|
|
TFrameWindow::SetClientWindow(TWindow* clientWnd)
|
|
{
|
|
TWindow* oldClientWnd = ClientWnd;
|
|
HWND oldHWnd = oldClientWnd ? oldClientWnd->HWindow : (HWND)0;
|
|
RemoveChild(ClientWnd);
|
|
|
|
if (HWndRestoreFocus == oldHWnd)
|
|
HWndRestoreFocus = 0;
|
|
|
|
ClientWnd = clientWnd;
|
|
|
|
if (ClientWnd) {
|
|
ClientWnd->SetParent(this);
|
|
if (!ClientWnd->HWindow)
|
|
ClientWnd->Create();
|
|
ClientWnd->Show(SW_NORMAL);
|
|
SetBkgndColor(NoErase); // no need to erase client area
|
|
ResizeClientWindow();
|
|
}
|
|
else
|
|
SetBkgndColor(NoColor); // will need to erase client area
|
|
|
|
if (ClientWnd && ClientWnd->HWindow)
|
|
ClientWnd->SetFocus();
|
|
else
|
|
SetFocus();
|
|
|
|
return oldClientWnd;
|
|
}
|
|
|
|
//
|
|
// If someone removes our client with a RemoveChild() call, update our client
|
|
// and restore focus ptrs.
|
|
//
|
|
void
|
|
TFrameWindow::RemoveChild(TWindow* child)
|
|
{
|
|
TWindow::RemoveChild(child);
|
|
if (child) {
|
|
if (child == ClientWnd)
|
|
ClientWnd = 0;
|
|
if (child->HWindow == HWndRestoreFocus)
|
|
HWndRestoreFocus = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the document style title for this frame window. Uses SetWindowText to
|
|
// modify the caption directly without modifying the Title data member.
|
|
// Generates a composite title based on Title if it exists, docname, and index
|
|
// if it is > 0.
|
|
// [<Title> - ]<docname>[:<index>]
|
|
//
|
|
bool
|
|
TFrameWindow::SetDocTitle(const char far* docname, int index)
|
|
{
|
|
if (index >= 0) {
|
|
string title;
|
|
|
|
if (Title && *Title) {
|
|
title = Title;
|
|
title += " - ";
|
|
}
|
|
title += docname;
|
|
if (index > 0) {
|
|
title += ':';
|
|
char num[10];
|
|
itoa(index, num, 10);
|
|
title += num;
|
|
}
|
|
|
|
SetWindowText(title.c_str());
|
|
|
|
} // else if index negative, simply acknowledge that title will display
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Obtain the real windows application icon. The IDI_APPLICATION icon is an
|
|
// ugly black & white box, but when a class is registered with this icon it
|
|
// gets substituted with a better windows icon. Worse case we end up with the
|
|
// ugly box icon.
|
|
//
|
|
static HICON
|
|
getAppIcon()
|
|
{
|
|
static HICON hRealAppIcon = 0;
|
|
if (!hRealAppIcon) {
|
|
WNDCLASS wndClass;
|
|
static char className[] = "IconSnarfer";
|
|
memset(&wndClass, 0, sizeof wndClass);
|
|
wndClass.hInstance = *::Module;
|
|
wndClass.hIcon = ::LoadIcon(0, IDI_APPLICATION);
|
|
wndClass.lpszClassName = className;
|
|
wndClass.lpfnWndProc = ::DefWindowProc;
|
|
::RegisterClass(&wndClass);
|
|
::GetClassInfo(*::Module, className, &wndClass);
|
|
hRealAppIcon = wndClass.hIcon;
|
|
::UnregisterClass(className, *::Module);
|
|
}
|
|
return hRealAppIcon ? hRealAppIcon : ::LoadIcon(0, IDI_APPLICATION);
|
|
}
|
|
|
|
//
|
|
// Response method for an incoming WM_PAINT message
|
|
//
|
|
// If iconic, and an icon has been defined then draw that.
|
|
// Or, if iconic & there is a client window, then call its paint function.
|
|
//
|
|
// If not iconic, forwards to TWindow for normal paint processing
|
|
//
|
|
void
|
|
TFrameWindow::EvPaint()
|
|
{
|
|
if (IsIconic() && (IconResId || ClientWnd)) {
|
|
TPaintDC dc(HWindow);
|
|
|
|
if (IconResId) {
|
|
HINSTANCE hInstance = IconModule ? HINSTANCE(*IconModule) : HINSTANCE(0);
|
|
HICON hIcon = ::LoadIcon(hInstance, IconResId);
|
|
::DrawIcon(dc, 0, 0, hIcon ? hIcon : getAppIcon());
|
|
}
|
|
else
|
|
ClientWnd->Paint(dc, dc.Ps.fErase, *(TRect*)&dc.Ps.rcPaint);
|
|
}
|
|
else
|
|
TWindow::EvPaint();
|
|
}
|
|
|
|
//
|
|
// Response method for an incoming WM_ERASEBKGND message
|
|
//
|
|
// If this frame window is iconic, and there is a client window, then give it
|
|
// a chance to erase the background since it may want to take over painting.
|
|
//
|
|
// If not iconic, forward to TWindow for normal erase background processing
|
|
//
|
|
bool
|
|
TFrameWindow::EvEraseBkgnd(HDC hDC)
|
|
{
|
|
if (IsIconic()) {
|
|
if (!IconResId && ClientWnd)
|
|
return (bool)ClientWnd->HandleMessage(WM_ERASEBKGND, WPARAM(hDC));
|
|
|
|
HandleMessage(WM_ICONERASEBKGND, WPARAM(hDC));
|
|
return true;
|
|
}
|
|
else
|
|
return TWindow::EvEraseBkgnd(hDC);
|
|
}
|
|
|
|
//
|
|
// Response method for an incoming WM_QUERYDRAGICON message
|
|
//
|
|
// If there is an icon set for this frame, then return it so that windows
|
|
// can make a nice cursor out of it when the user drags the icon
|
|
//
|
|
HANDLE
|
|
TFrameWindow::EvQueryDragIcon()
|
|
{
|
|
if (IconResId) {
|
|
HINSTANCE hInstance = IconModule ? HINSTANCE(*IconModule) : HINSTANCE(0);
|
|
HICON hIcon = ::LoadIcon(hInstance, IconResId);
|
|
return hIcon ? hIcon : getAppIcon();
|
|
}
|
|
else
|
|
return TWindow::EvQueryDragIcon();
|
|
}
|
|
|
|
static inline bool
|
|
IsEnabledVisibleChild(long style)
|
|
{
|
|
return (style & (WS_CHILD | WS_VISIBLE | WS_DISABLED)) == (WS_CHILD | WS_VISIBLE);
|
|
}
|
|
|
|
static TWindow*
|
|
SearchForChildWithTab(TWindow* win)
|
|
{
|
|
TWindow* firstChild = win->GetFirstChild();
|
|
|
|
if (firstChild) {
|
|
TWindow* child = firstChild;
|
|
|
|
do {
|
|
if (child->HWindow) {
|
|
long style = child->GetWindowLong(GWL_STYLE);
|
|
|
|
if (IsEnabledVisibleChild(style)) {
|
|
if (style & WS_TABSTOP)
|
|
return child;
|
|
|
|
else {
|
|
TWindow* result = SearchForChildWithTab(child);
|
|
if (result)
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
child = child->Next();
|
|
} while (child != firstChild);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
EnabledVisibleChild(TWindow* win, void*)
|
|
{
|
|
return win->HWindow ? IsEnabledVisibleChild(win->GetWindowLong(GWL_STYLE)) :
|
|
false;
|
|
}
|
|
|
|
//
|
|
// if the receiver doesn't have any children then returns 0. otherwise
|
|
// we search for the first child with WS_TABSTOP; if no child has WS_TABSTOP
|
|
// then we return the first enabled visible child
|
|
//
|
|
// does a depth-first search of nested child windows
|
|
//
|
|
// NOTE: we stop at the first child with WS_TABSTOP and do not search its
|
|
// children...
|
|
//
|
|
TWindow*
|
|
TFrameWindow::FirstChildWithTab()
|
|
{
|
|
TWindow* win = SearchForChildWithTab(this);
|
|
|
|
return win ? win : FirstThat(EnabledVisibleChild);
|
|
}
|
|
|
|
//
|
|
// Respond to a request to hold on to the handle of a child window that is
|
|
// losing focus, so that we can restore it again later (below).
|
|
//
|
|
// return true if caller can stop searching for a window to hold its handle.
|
|
//
|
|
bool
|
|
TFrameWindow::HoldFocusHWnd(HWND hWndLose, HWND hWndGain)
|
|
{
|
|
if (IsChild(hWndLose)) {
|
|
if (!hWndGain || !IsChild(hWndGain))
|
|
HWndRestoreFocus = hWndLose;
|
|
return true;
|
|
}
|
|
return hWndLose == HWindow;
|
|
}
|
|
|
|
//
|
|
// Handle WM_SETFOCUS to return focus back to the child that had it most
|
|
// recently, or find the best one to give it to otherwise.
|
|
//
|
|
void
|
|
TFrameWindow::EvSetFocus(HWND hWndLostFocus)
|
|
{
|
|
TWindow::EvSetFocus(hWndLostFocus);
|
|
|
|
if (HWndRestoreFocus) {
|
|
//
|
|
// Set focus to the saved HWND as long as it is still a valid window handle
|
|
//
|
|
if (::IsWindow(HWndRestoreFocus))
|
|
::SetFocus(HWndRestoreFocus);
|
|
|
|
HWndRestoreFocus = 0;
|
|
}
|
|
else {
|
|
TWindow* win = FirstChildWithTab();
|
|
|
|
if (win && win->HWindow != hWndLostFocus)
|
|
win->SetFocus();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close this window if the client is destroyed
|
|
//
|
|
// Clear the wfFullyCreated flag on any child that is destroyed
|
|
// Resize this frame if the client changes size & wfShrinkToClient is set
|
|
//
|
|
void
|
|
TFrameWindow::EvParentNotify(uint event,
|
|
uint childHandleOrX, uint /*childIDOrY*/)
|
|
{
|
|
if (event == WM_DESTROY) {
|
|
if (ClientWnd && ClientWnd->HWindow == HWND(childHandleOrX))
|
|
PostMessage(WM_CLOSE); // using ShutDownWindow() has side effects
|
|
|
|
TWindow* c = GetWindowPtr(HWND(childHandleOrX));
|
|
if (c)
|
|
c->ClearFlag(wfFullyCreated);
|
|
}
|
|
else if (event == WM_SIZE) {
|
|
if (IsFlagSet(wfShrinkToClient)
|
|
&& ClientWnd
|
|
&& ClientWnd->HWindow == HWND(childHandleOrX)
|
|
&& !IsIconic())
|
|
ResizeClientWindow();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Watch for WM_ENTERIDLE messages from idling modal dialog boxes so that
|
|
// background processing can continue when they are up.
|
|
//
|
|
void
|
|
TFrameWindow::EvEnterIdle(uint source, HWND hWndDlg)
|
|
{
|
|
if (source == MSGF_DIALOGBOX && IsFlagSet(wfMainWindow))
|
|
IdleAction(0); // could call GetApplication()->IdleAction(0)...
|
|
TWindow::EvEnterIdle(source, hWndDlg);
|
|
}
|
|
|
|
//
|
|
// Resize & reposition the client window to fit in this frames client area
|
|
// or resize the frame to fit around the client's client area if
|
|
// wfShrinkToClient
|
|
// Return true if a client was actualy resized.
|
|
// Adjust clients styles & make sure they get set.
|
|
//
|
|
bool
|
|
TFrameWindow::ResizeClientWindow(bool redraw)
|
|
{
|
|
if (!ClientWnd)
|
|
return false;
|
|
|
|
// prevent recursion during resize by ignore calls from EvParentNotify and
|
|
// EvSize when we have already been called
|
|
// do this by disabling notifications while resizing using the
|
|
// wfShrinkToClient flag as a semaphore on the client
|
|
//
|
|
if (ClientWnd->IsFlagSet(wfShrinkToClient))
|
|
return true;
|
|
ClientWnd->SetFlag(wfShrinkToClient);
|
|
|
|
bool clientResized = false;
|
|
TSize frameSize = GetClientRect().Size();
|
|
TSize childSize = ClientWnd->GetWindowRect().Size();
|
|
|
|
// first time through, strip client window of thick borders
|
|
// if shrink-to-client, then must measure the client size first
|
|
// if the client has scrolls bars, we must hide them to obtain the correct
|
|
// size
|
|
// Border style is left on & dealt with by hand below
|
|
//
|
|
const uint32 badClientStyles = WS_DLGFRAME | WS_THICKFRAME | // bad borders
|
|
WS_POPUP | WS_OVERLAPPED; // bad parenting
|
|
const uint32 badClientExStyles = WS_EX_DLGMODALFRAME;
|
|
if ((ClientWnd->Attr.Style & badClientStyles) ||
|
|
(ClientWnd->Attr.ExStyle & badClientExStyles)) {
|
|
if (IsFlagSet(wfShrinkToClient)) {
|
|
TSize tstSize = ClientWnd->GetClientRect().Size();
|
|
ClientWnd->ShowScrollBar(SB_BOTH, false);
|
|
childSize = ClientWnd->GetClientRect().Size();
|
|
if (childSize != tstSize) {
|
|
int restore = SB_BOTH;
|
|
if (childSize.cx == tstSize.cx)
|
|
restore = SB_HORZ;
|
|
if (childSize.cy == tstSize.cy)
|
|
restore = SB_VERT;
|
|
ClientWnd->ShowScrollBar(restore, true);
|
|
}
|
|
}
|
|
if (ClientWnd->Attr.Style & badClientStyles) {
|
|
bool reparent = (ClientWnd->Attr.Style & (WS_POPUP|WS_OVERLAPPED)) != 0;
|
|
ClientWnd->Attr.Style &= ~badClientStyles;
|
|
ClientWnd->Attr.Style |= WS_CHILD | WS_BORDER | WS_VISIBLE;
|
|
ClientWnd->SetWindowLong(GWL_STYLE, ClientWnd->Attr.Style);
|
|
if (reparent)
|
|
::SetParent(*ClientWnd, *this);
|
|
}
|
|
if (ClientWnd->Attr.ExStyle & badClientExStyles) {
|
|
ClientWnd->Attr.ExStyle &= ~badClientExStyles;
|
|
ClientWnd->SetWindowLong(GWL_EXSTYLE, ClientWnd->Attr.ExStyle);
|
|
}
|
|
}
|
|
if (ClientWnd->Attr.Style & WS_BORDER) {
|
|
childSize = ClientWnd->GetClientRect().Size();
|
|
}
|
|
if (childSize != frameSize) {
|
|
if (IsFlagSet(wfShrinkToClient)) {
|
|
TRect outside = GetWindowRect();
|
|
TPoint outsideOrg(0, 0);
|
|
if (Parent)
|
|
Parent->ClientToScreen(outsideOrg); // adjust our rect by our parent's
|
|
|
|
TSize border = outside.Size() - frameSize;
|
|
MoveWindow(outside.left - outsideOrg.x, outside.top - outsideOrg.y,
|
|
childSize.cx + border.cx, childSize.cy + border.cy, redraw);
|
|
frameSize = childSize; // must move client, will not cause an EvSize
|
|
}
|
|
else {
|
|
clientResized = true; // client will get resized
|
|
}
|
|
}
|
|
// If frame is sizeable, turn off flag so that user can then resize
|
|
// after initial setup
|
|
//
|
|
if (Attr.Style & WS_THICKFRAME)
|
|
ClearFlag(wfShrinkToClient);
|
|
|
|
// Handle border style by shoving the client's borders under the frame
|
|
//
|
|
if (ClientWnd->Attr.Style & WS_BORDER) {
|
|
int bx = ::GetSystemMetrics(SM_CXBORDER);
|
|
int by = ::GetSystemMetrics(SM_CYBORDER);
|
|
ClientWnd->MoveWindow(-bx, -by, frameSize.cx+bx+bx, frameSize.cy+by+by, redraw);
|
|
}
|
|
else
|
|
ClientWnd->MoveWindow(0, 0, frameSize.cx, frameSize.cy, redraw);
|
|
|
|
ClientWnd->ClearFlag(wfShrinkToClient);
|
|
return clientResized;
|
|
}
|
|
|
|
//
|
|
// called following a successful association between an MS-Windows interface
|
|
// element and a TFrameWindow
|
|
//
|
|
void
|
|
TFrameWindow::SetupWindow()
|
|
{
|
|
// create windows in child list (this includes the client window)
|
|
//
|
|
TWindow::SetupWindow();
|
|
|
|
ResizeClientWindow();
|
|
|
|
if (MinimizedPos != TPoint(-1,-1)) {
|
|
WINDOWPLACEMENT windata;
|
|
windata.length = sizeof(WINDOWPLACEMENT);
|
|
GetWindowPlacement(&windata);
|
|
windata.flags = WPF_SETMINPOSITION;
|
|
windata.showCmd = SW_SHOWNA;
|
|
windata.ptMinPosition = MinimizedPos;
|
|
SetWindowPlacement(&windata);
|
|
}
|
|
|
|
// if SetMenuDescr() was called before window created, update the menu now
|
|
//
|
|
if (IsFlagSet(wfMainWindow) && MenuDescr) {
|
|
HMENU curMenu = GetMenu();
|
|
TMenu newMenu(*MenuDescr, NoAutoDelete);
|
|
if (SetMenu(newMenu)) {
|
|
if (curMenu)
|
|
::DestroyMenu(curMenu);
|
|
}
|
|
else
|
|
::DestroyMenu(newMenu);
|
|
}
|
|
|
|
// if we haven't set HWndRestoreFocus then pick the first child with tabstop
|
|
//
|
|
if (!HWndRestoreFocus) {
|
|
TWindow* win = FirstChildWithTab();
|
|
|
|
HWndRestoreFocus = win ? win->HWindow : HWindow;
|
|
}
|
|
}
|
|
|
|
//
|
|
// response method for an incoming WM_SIZE message
|
|
//
|
|
// if not minimizing resizes the client window to be the same size as the
|
|
// client rectangle,
|
|
// if no WM_SIZE sent, forwards WM_SIZE message to client so it can recalc.
|
|
//
|
|
void
|
|
TFrameWindow::EvSize(uint sizeType, TSize& size)
|
|
{
|
|
TWindow::EvSize(sizeType, size);
|
|
|
|
if (ClientWnd) {
|
|
bool sizeSent = false;
|
|
if (sizeType != SIZE_MINIMIZED) {
|
|
sizeSent = ResizeClientWindow();
|
|
size = ClientWnd->GetClientRect().Size();
|
|
}
|
|
if (!sizeSent)
|
|
ClientWnd->ForwardMessage();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 2
|
|
|
|
//
|
|
// Set the menu descriptor for this frame window
|
|
//
|
|
void
|
|
TFrameWindow::SetMenuDescr(const TMenuDescr& menuDescr)
|
|
{
|
|
delete MenuDescr;
|
|
MenuDescr = new TMenuDescr(menuDescr);
|
|
|
|
if (IsFlagSet(wfMainWindow) && HWindow) {
|
|
HMENU curMenu = GetMenu();
|
|
TMenu newMenu(*MenuDescr, NoAutoDelete);
|
|
if (SetMenu(newMenu))
|
|
::DestroyMenu(curMenu);
|
|
else
|
|
::DestroyMenu(newMenu);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Merge another menu, given its menu descriptor, into our own using our menu
|
|
// descriptor.
|
|
// optionally use an existing HMENU to merge into & set
|
|
//
|
|
bool
|
|
TFrameWindow::MergeMenu(const TMenuDescr& childMenuDescr)
|
|
{
|
|
if (!MenuDescr || !HWindow)
|
|
return false;
|
|
|
|
MergeModule = childMenuDescr.GetModule();
|
|
TMenu curMenu(*this, NoAutoDelete);
|
|
TMenu newMenu(NoAutoDelete);
|
|
|
|
MenuDescr->Merge(childMenuDescr, newMenu);
|
|
|
|
if (IsFlagSet(wfMainWindow))
|
|
GetApplication()->PreProcessMenu(newMenu);
|
|
|
|
if (SetMenu(newMenu)) {
|
|
::DestroyMenu(curMenu);
|
|
return true;
|
|
|
|
}
|
|
else {
|
|
::DestroyMenu(newMenu);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Restore our menu to the one described by our menu descriptor
|
|
//
|
|
bool
|
|
TFrameWindow::RestoreMenu()
|
|
{
|
|
if (!MenuDescr)
|
|
return false;
|
|
|
|
HMENU curMenu = GetMenu();
|
|
TMenu newMenu(*MenuDescr, NoAutoDelete);
|
|
if (SetMenu(newMenu)) {
|
|
MergeModule = 0;
|
|
::DestroyMenu(curMenu);
|
|
}
|
|
else
|
|
::DestroyMenu(newMenu);
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 3
|
|
|
|
IMPLEMENT_STREAMABLE1(TFrameWindow, TWindow);
|
|
|
|
//
|
|
// reads data of the uninitialized TFrameWindow from the passed ipstream
|
|
//
|
|
void*
|
|
TFrameWindow::Streamer::Read(ipstream& is, uint32 /*version*/) const
|
|
{
|
|
TFrameWindow* o = GetObject();
|
|
ReadVirtualBase((TWindow*)o, is);
|
|
if (o->IsFlagSet(wfMainWindow))
|
|
return o;
|
|
|
|
is >> o->ClientWnd;
|
|
is >> o->KeyboardHandling;
|
|
o->HWndRestoreFocus = 0;
|
|
|
|
bool hasMenuDescr = is.readByte();
|
|
if (hasMenuDescr) {
|
|
o->MenuDescr = new TMenuDescr;
|
|
is >> *o->MenuDescr;
|
|
}
|
|
else
|
|
o->MenuDescr = 0;
|
|
|
|
is >> o->IconModule;
|
|
is >> o->IconResId;
|
|
is >> o->MergeModule;
|
|
is >> o->MinimizedPos;
|
|
|
|
return o;
|
|
}
|
|
|
|
//
|
|
// writes data of the TFrameWindow to the passed opstream
|
|
//
|
|
void
|
|
TFrameWindow::Streamer::Write(opstream& os) const
|
|
{
|
|
TFrameWindow* o = GetObject();
|
|
WriteVirtualBase((TWindow*)o, os);
|
|
if (o->IsFlagSet(wfMainWindow))
|
|
return;
|
|
|
|
os << o->ClientWnd;
|
|
os << o->KeyboardHandling;
|
|
|
|
os.writeByte(uint8(o->MenuDescr ? 1 : 0));
|
|
if (o->MenuDescr)
|
|
os << *o->MenuDescr;
|
|
|
|
os << o->IconModule;
|
|
os << o->IconResId;
|
|
os << o->MergeModule;
|
|
WINDOWPLACEMENT windata;
|
|
windata.length = sizeof(WINDOWPLACEMENT);
|
|
o->GetWindowPlacement(&windata);
|
|
os << TPoint(windata.ptMinPosition);
|
|
}
|
|
|
|
#endif // SECTION
|