Files
TeslaRel410/BORLAND/BC45/SOURCE/OWL/WINDOW.CPP
T
CydandClaude Fable 5 63312e07f9 source410: literal 4.10 source reconstruction + BC++ 4.52 fleet toolchain archived
- 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>
2026-07-19 07:33:26 -05:00

2538 lines
67 KiB
C++

//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
//
// Implementation of TWindow. This defines the basic behavior of all
// Windows.
//----------------------------------------------------------------------------
#include <owl/owlpch.h>
#include <owl/window.h>
#include <owl/applicat.h>
#include <owl/appdict.h>
#include <owl/scroller.h>
#include <owl/gdiobjec.h>
#include <owl/menu.h>
#include <owl/framewin.h>
#include <stdlib.h>
DIAG_DECLARE_GROUP(OwlMsg); // diagnostic group for message tracing
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlWin, 1, 0); // diagnostic group for windows
//
// define to build registered Window's classname using the module name
//
//#define _OWL_BUILDUP_CLASSNAME
//
// define to use rtti to create unique window ids for the message cache
//
#if defined(__BORLANDC__) && !defined(BI_NO_RTTI)
# define OWL_RTTI_MSGCACHE
# define TYPE_UNIQUE_UINT32(t) reinterpret_cast<uint32>(typeid(t).tpp)
#endif
//
// Externs defined in owl.cpp
//
extern LRESULT CALLBACK __export InitWndProc(HWND, uint, WPARAM, LPARAM);
extern WNDPROC CreateInstanceThunk(TWindow*);
extern void FreeInstanceThunk(WNDPROC);
extern void _OWLFUNC SetCreationWindow(TWindow*);
extern uint _OWLDATA GetWindowPtrMsgId;
//$---------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE(TWindow)
EV_WM_SETCURSOR,
EV_WM_SIZE,
EV_WM_MOVE,
EV_WM_COMPAREITEM,
EV_WM_DELETEITEM,
EV_WM_DRAWITEM,
EV_WM_MEASUREITEM,
EV_WM_VSCROLL,
EV_WM_HSCROLL,
EV_WM_CHILDINVALID,
EV_WM_ERASEBKGND,
EV_WM_PAINT,
EV_WM_LBUTTONDOWN,
EV_WM_KILLFOCUS,
#if defined(BI_PLAT_WIN32)
EV_MESSAGE(WM_CTLCOLORMSGBOX, EvWin32CtlColor),
EV_MESSAGE(WM_CTLCOLOREDIT, EvWin32CtlColor),
EV_MESSAGE(WM_CTLCOLORLISTBOX, EvWin32CtlColor),
EV_MESSAGE(WM_CTLCOLORBTN, EvWin32CtlColor),
EV_MESSAGE(WM_CTLCOLORDLG, EvWin32CtlColor),
EV_MESSAGE(WM_CTLCOLORSCROLLBAR, EvWin32CtlColor),
EV_MESSAGE(WM_CTLCOLORSTATIC, EvWin32CtlColor),
#endif
EV_WM_CREATE,
EV_WM_CLOSE,
EV_WM_DESTROY,
EV_WM_NCDESTROY,
EV_COMMAND(CM_EXIT, CmExit),
EV_WM_QUERYENDSESSION,
EV_WM_SYSCOLORCHANGE,
EV_WM_INITMENUPOPUP,
END_RESPONSE_TABLE;
//$---------------------------------------------------------------------------
//
//
TCommandEnabler::TCommandEnabler(uint id, HWND hWndReceiver)
: Id(id), HWndReceiver(hWndReceiver)
{
Handled = false;
}
void
TCommandEnabler::Enable(bool)
{
Handled = true;
}
//$---------------------------------------------------------------------------
//
// constructor for a TWindow. if a parent window is passed, adds the TWindow
// to its parent's list of children
//
TWindow::TWindow(TWindow* parent,
const char far* title,
TModule* module)
{
Thunk = 0; // remember that we still need to initialize
Init(parent, title, module);
}
//$---------------------------------------------------------------------------
//
// constructor for a TWindow which is being used as an alias for a
// non-OWL window. This ctor is generally not used by derived classes
//
TWindow::TWindow(HWND hWnd, TModule* module)
{
// Perform preliminary initialization
//
HWindow = hWnd;
Title = 0;
Thunk = 0; // remember that we still need to initialize
// If the receiver's parent is an OWL window then pass the window to Init()
// so the receiver can be added to the parent's list of children
//
HWND hParent = GetParent();
TWindow* parent = hParent ? GetWindowPtr(hParent) : 0;
Init(parent, module);
// Thunk the window
//
SubclassWindowFunction();
// Copy over the title, styles, the coordinates & the id
//
GetWindowTextTitle();
GetHWndState();
// Keep track that this is an alias object & that it is already created
//
SetFlag(wfAlias | wfFullyCreated);
// Unique id may have been set inadvertantly to TWindow by the above
// GetWindowTextTitle, et. al. Reset it just in case
//
SetUniqueId();
}
//$---------------------------------------------------------------------------
//
// Protected constructor for use by immediate virtually derived classes.
// Immediate derivitives must call Init() before constructions are done.
//
TWindow::TWindow()
{
Thunk = 0; // remember that we still need to initialize
}
//
// Normal initialization of a default constructed TWindow. Is ignored
// if called more than once.
//
void
TWindow::Init(TWindow* parent,
const char far* title,
TModule* module)
{
if (!Thunk) {
Init(parent, module);
Title = strnewdup(title);
DefaultProc = 0;
HWindow = 0;
EnableAutoCreate();
// initialize creation attributes
//
Attr.Style = WS_CHILD | WS_VISIBLE;
Attr.X = Attr.Y = Attr.W = Attr.H = 0;
Attr.ExStyle = 0;
Attr.Id = 0;
}
}
//
// Private initializer function that does the bulk of the work
//
void
TWindow::Init(TWindow* parent, TModule* module)
{
ZOrder = 0;
ChildList = 0;
Flags = wfDeleteOnClose;
TransferBuffer = 0;
Parent = parent;
Attr.Param = 0;
Attr.Menu = 0;
Attr.AccelTable = 0;
hAccel = 0;
Thunk = ::CreateInstanceThunk(this);
Scroller = 0;
CursorModule = 0;
CursorResId = 0;
HCursor = 0;
BkgndColor = NoColor;
if (Parent)
Parent->AddChild(this);
else
SiblingList = 0;
// Use passed module if one, else get parent's. If no parent, use app
//
if (Parent) {
Application = Parent->GetApplication();
Module = module ? module : Parent->GetModule();
}
else {
Module = module ? module : 0;
Application = TYPESAFE_DOWNCAST(Module,TApplication);
if (!Application) {
Application = ::GetApplicationObject();
if (!Module)
Module = Application;
}
CHECK(Application);
}
SetUniqueId();
TRACEX(OwlWin, 1, "TWindow constructed @" << (void*)this);
}
//
// installs the thunk as the window function and saves the old window function
// in "DefaultProc"
//
void
TWindow::SubclassWindowFunction()
{
PRECONDITION(HWindow != 0);
#if defined(BI_PLAT_WIN32)
uint32 processId;
::GetWindowThreadProcessId(HWindow, &processId);
if (processId == ::GetCurrentProcessId()) {
#else
if (::GetWindowTask(HWindow) == ::GetCurrentTask()) {
#endif
DefaultProc = (WNDPROC)SetWindowLong(GWL_WNDPROC, uint32(GetThunk()));
CHECK(DefaultProc != GetThunk()); // can only be called once!
}
}
//
// Destructor for a TWindow: disposes of each window in its ChildList
// and removes itself from a non-0 parent's list of children
//
// Destroys a still-associated MS-Windows interface element and frees
// the instance thunk used for association of an MS-Windows element
// to the TWindow
//
// Disposes of its Scroller if the TScroller object was constructed
//
static void shutDown(TWindow* win, void*) {
win->Destroy();
delete win;
}
TWindow::~TWindow()
{
// Flush the cache so that messages dont get dispatched to a
// already-destructed derived class
//
void CacheFlush(uint32 id);
CacheFlush(UniqueId);
// ShutDown children first, so the Owl objects get a chance to clean up
//
ForEach(shutDown);
// If the HWindow is still around, then destroy it or unlink from it as
// appropriate.
//
if (HWindow) {
// for aliases:
// - we are destructing the C++ object but not destroying the MS window
// - restore the original window function
//
if (IsFlagSet(wfAlias)) {
// May want to check WNDPROC against Thunk to see if its still us
// and not restore if it's not us...
//
WARNX(OwlWin, GetWindowLong(GWL_WNDPROC) != (uint32)GetThunk(), 0,
"Restoring old WndProc after foreign subclass of:" << *this);
SetWindowLong(GWL_WNDPROC, uint32(DefaultProc));
}
// for non-aliases:
// - destroy the windows element
// this is not a normal condition and is a safety net only
//
else {
WARNX(OwlWin, HWindow, 0, "Destroying from TWindow::~TWindow: " << *this);
Destroy();
}
}
if (Parent)
Parent->RemoveChild(this);
if (IsFlagSet(wfMainWindow))
GetApplication()->MainWindow = 0;
if (Application)
Application->Uncondemn(this);
// Delete menu id string, scroller, title, cursor & thunk
//
if (Attr.Menu.IsString())
delete [] (char far*)Attr.Menu; // assume strnewdup was used
// delete scroller if there is one, by casting up to base and relying on
// virtual dtor to avoid pulling in TScroller object code.
//
if (Scroller) {
delete (TStreamableBase*)Scroller;
Scroller = 0;
}
if (HIWORD(Title))
delete [] Title;
if (HCursor && CursorModule)
SetCursor(0, 0);
::FreeInstanceThunk(Thunk);
TRACEX(OwlWin, 1, "TWindow destructed @" << (void*)this);
}
#if defined(BI_MULTI_THREAD)
//
// overrides TEventHandler::Dispatch() to handle multi-thread synchronization
//
LRESULT TWindow::Dispatch(TEventInfo& info, WPARAM wp, LPARAM lp)
{
TApplication::TAppLock Lock(*GetApplication());
return TEventHandler::Dispatch(info, wp, lp);
}
#endif
//
//
//
bool
TWindow::PreProcessMsg(MSG& msg)
{
return hAccel ? ::TranslateAccelerator(HWindow, hAccel, &msg) : false;
}
//
//
//
bool
TWindow::IdleAction(long /*idleCount*/)
{
return false; // we don't need any more time for now
}
//
// Respond to this message by broadcasting it to all children
//
void
TWindow::EvSysColorChange()
{
ChildBroadcastMessage(WM_SYSCOLORCHANGE);
}
//
// Removes the passed pointer to a child window from the linked list of
// sibling windows which the TWindow's ChildList points to
//
void
TWindow::RemoveChild(TWindow* child)
{
if (child && ChildList) {
TWindow* lastChild = ChildList;
TWindow* nextChild = lastChild;
while (nextChild->SiblingList != lastChild &&
nextChild->SiblingList != child) {
nextChild = nextChild->SiblingList;
}
if (nextChild->SiblingList == child) {
if (nextChild->SiblingList == nextChild)
ChildList = 0;
else {
if (nextChild->SiblingList == ChildList)
ChildList = nextChild;
nextChild->SiblingList = nextChild->SiblingList->SiblingList;
}
}
}
}
//
// Reparents this window to a new owl parent window. Also sets the windows
// parent (owner)
//
void
TWindow::SetParent(TWindow* newParent)
{
if (Parent != newParent) {
if (Parent)
Parent->RemoveChild(this);
SiblingList = 0;
Parent = newParent;
if (Parent)
Parent->AddChild(this);
}
// tell Windows about the change too, if appropriate
//
if (HWindow && Parent && GetParent() != Parent->HWindow) {
if (newParent) {
if (newParent->HWindow)
::SetParent(HWindow, newParent->HWindow);
}
else
::SetParent(HWindow, 0);
}
}
//
// Default behavior for updating document title is to pass it to parent frame
//
bool
TWindow::SetDocTitle(const char far* docname, int index)
{
return Parent ? Parent->SetDocTitle(docname, index) : false;
}
//
// Wildcard check used for child id notifications
//
static bool wildcardCheck(TGenericTableEntry __RTFAR& entry,
TEventHandler::TEventInfo& info) {
return entry.Id == info.Id && (entry.Msg == UINT_MAX || entry.Msg == info.Msg);
}
//
// handles default processing of events
//
// this includes continued processing of menu/accelerators and child Id
// notifications
//
LRESULT
TWindow::DefaultProcessing()
{
TCurrentEvent& currentEvent = GetCurrentEvent();
if (currentEvent.Message == WM_COMMAND_ENABLE) {
if (currentEvent.Win != this) {
TWindow* receiver = Parent ? Parent : currentEvent.Win;
TCommandEnabler& commandEnabler = *(TCommandEnabler*)currentEvent.LParam;
TEventInfo eventInfo(WM_COMMAND_ENABLE, commandEnabler.Id);
if (receiver->Find(eventInfo))
return receiver->Dispatch(eventInfo, 0, currentEvent.LParam);
}
return 0;
}
if (currentEvent.Message != WM_COMMAND)
return DefWindowProc(currentEvent.Message, currentEvent.WParam, currentEvent.LParam);
//
// currentEvent.Message == WM_COMMAND
//
#if defined(BI_PLAT_WIN16)
uint notifyCode = HIWORD(currentEvent.LParam);
#else
uint notifyCode = HIWORD(currentEvent.WParam);
#endif
uint id = LOWORD(currentEvent.WParam);
HWND hWndCtl = (HWND)(uint)currentEvent.LParam;
if (currentEvent.Win == this) {
//
// menu command originally destined for the receiver. defer to the app
//
if (hWndCtl == 0) {
TEventInfo eventInfo(0, id);
TApplication* app = GetApplication();
if (app->Find(eventInfo)) {
app->Dispatch(eventInfo, eventInfo.Id);
return 0;
}
WARNX(OwlMsg, notifyCode<=1, 0, "Unprocessed WM_COMMAND(id=" << id << ")");
}
//
// originally destined for the receiver and the receiver has called us so
// default processing can occur
//
// unpack the original parameters and call DefWindowProc()
//
return DefWindowProc(currentEvent.Message, currentEvent.WParam, currentEvent.LParam);
}
else {
TWindow* receiver;
uint wParam;
TEqualOperator equal = 0;
if (hWndCtl == 0) {
//
// menu/accelerator/push button
//
// either give the message to the receiver's parent or the original
// receiver of the message
//
receiver = Parent ? Parent : currentEvent.Win;
//
// "notifyCode" is either 0 or 1 depending on whether this is from an
// accelerator; however, we want to explicitly look for a 0...
//
notifyCode = 0;
wParam = id; // pass along "id" in case the receiver wants it
}
else {
//
// child id notification sent to the child and the child has called us
//
// offer the parent an opportunity to handle the notification
//
// NOTE: use equal function that will do a wildcard search
//
equal = wildcardCheck;
receiver = currentEvent.Win;
//
// the child window identifier shouldn't be 0, but if it is then it will
// look like we are searching for a WM_ message with value "notifyCode"
//
if (receiver->IsFlagSet(wfAlias) || id == 0)
return receiver->DefWindowProc(currentEvent.Message, currentEvent.WParam, currentEvent.LParam);
wParam = notifyCode; // pass notification code in case receiver wants it
}
TEventInfo eventInfo(notifyCode, id);
if (receiver->Find(eventInfo, equal))
return receiver->Dispatch(eventInfo, wParam);
else
return receiver->DefaultProcessing();
}
}
//
// processing for WM_COMMAND
//
LRESULT
TWindow::EvCommand(uint id, HWND hWndCtl, uint notifyCode)
{
TWindow* receiver = this;
uint wParam;
TEqualOperator equal = 0;
if (hWndCtl == 0) {
//
// menu/accelerator
//
// "notifyCode" is either 0 or 1 depending on whether this is from an
// accelerator; however, we want to explicitly look for a 0...
//
notifyCode = 0;
//
// pass the "id" along in case the user wants it...
//
wParam = id;
}
else {
//
// child id notification
//
TWindow* child = GetWindowPtr(hWndCtl);
//
// pass the "notifyCode" along in case the user wants it...
//
wParam = notifyCode;
if (child) {
//
// give the child first crack at the event
//
receiver = child;
id = UINT_MAX; // special designator for child Id notifications that are
// handled at the child
}
else {
//
// offer the parent an opportunity to handle the notification
//
// NOTE: use equal function that will do a wildcard search
//
equal = wildcardCheck;
//
// the child window identifier shouldn't be 0, but if it is then it will
// look like we are searching for a WM_ message with value "notifyCode"
//
if (id == 0)
return DefaultProcessing();
}
}
TEventInfo eventInfo(notifyCode, id);
if (receiver->Find(eventInfo, equal))
return receiver->Dispatch(eventInfo, wParam);
else
return receiver->DefaultProcessing();
}
//
//
//
void
TWindow::EvCommandEnable(TCommandEnabler& commandEnabler)
{
TEventInfo eventInfo(WM_COMMAND_ENABLE, commandEnabler.Id);
if (Find(eventInfo))
Dispatch(eventInfo, 0, (LPARAM)&commandEnabler);
}
//
// Don't process for windows out of our window tree (esp. other apps)
//
void
TWindow::RouteCommandEnable(HWND hInitCmdTarget, TCommandEnabler& commandEnabler)
{
// Extra processing for commands & commandEnablers: send the command down the
// command chain if we are the original receiver
//
if (commandEnabler.IsReceiver(*this)) {
HWND hCmdTarget = hInitCmdTarget;
while (hCmdTarget && hCmdTarget != *this) {
TWindow* cmdTarget = GetWindowPtr(hCmdTarget);
if (cmdTarget) {
cmdTarget->EvCommandEnable(commandEnabler);
if (commandEnabler.GetHandled())
return;
}
hCmdTarget = ::GetParent(hCmdTarget);
}
}
// Always call base handler
//
TWindow::EvCommandEnable(commandEnabler);
// No one explicitly enabled/disabled the command via the enabler, so run up
// the command chain checking for any one who is going to handle the command
// itself; if not then disable it...
// Don't do this for command senders that don't actually generate commands,
// like popup menu items.
//
if (commandEnabler.IsReceiver(*this) && !commandEnabler.GetHandled()
&& commandEnabler.SendsCommand()) {
bool enable = false;
TEventInfo eventInfo(0, commandEnabler.Id);
HWND hCmdTarget = hInitCmdTarget;
while (true) {
TWindow* cmdTarget = GetWindowPtr(hCmdTarget);
if (cmdTarget && cmdTarget->Find(eventInfo)) {
enable = true; // command will be handled, leave sender alone
break;
}
if (!hCmdTarget || hCmdTarget == *this)
break;
hCmdTarget = ::GetParent(hCmdTarget);
}
if (!enable) {
// check if the app wants to handle it
//
TEventInfo enableInfo(WM_COMMAND_ENABLE, commandEnabler.Id);
TApplication* app = GetApplication();
if (app->Find(enableInfo)) {
app->Dispatch(enableInfo, 0, (LPARAM)&commandEnabler);
if (commandEnabler.GetHandled())
return;
}
enable = app->Find(eventInfo);
}
commandEnabler.Enable(enable);
}
}
//
// specifies default processing for an incoming message
//
// calls original window proc that we subclassed, using ::CallWindowProc to
// make sure that registers get setup correctly.
//
LRESULT
TWindow::DefWindowProc(uint message, WPARAM wParam, LPARAM lParam)
{
CHECK(DefaultProc);
LRESULT result = ::CallWindowProc(DefaultProc, HWindow, message, wParam, lParam);
GetApplication()->ResumeThrow();
return result;
}
static const int msgCacheSize = 31;
struct TCacheEntry {
uint32 UniqueId;
TGenericTableEntry __RTFAR* Entry;
uint Msg;
int Delta; // adjustment to "this" pointer
void Set(uint32 uniqueId, uint msg, TGenericTableEntry __RTFAR* entry, int delta = 0) {
UniqueId = uniqueId;
Entry = entry;
Msg = msg;
Delta = delta;
}
bool Hit(uint msg, uint32 uniqueId) {return msg == Msg && uniqueId == UniqueId;}
static uint Key(uint32 id, uint msg) {return (uint(id) ^ msg) % msgCacheSize;}
};
uint32 TWindow::LastUniqueId = 0;
static TCacheEntry msgCache[msgCacheSize];
static bool cacheEnabled = true;
void CacheFlush(uint32 id) {
for (int i = 0; i < msgCacheSize; i++)
if (msgCache[i].UniqueId == id)
msgCache[i].Msg = 0;
}
//
// If rtti is available, then use it get an id for this window that is unique
// on a per-class basis.
//
// Without rtti, use a per-instance unique id. Less efficient, but safe without
// rtti.
//
void
TWindow::SetUniqueId()
{
#if defined(OWL_RTTI_MSGCACHE)
UniqueId = 0;
#else
if (++LastUniqueId == 0) {
//
// numbers wrapped around. disable the cache to be safe...
//
LastUniqueId = 1;
cacheEnabled = false;
}
UniqueId = LastUniqueId;
#endif
}
//
// if the message is WM_COMMAND calls EvCommand(); otherwise looks for a handler
// in the message response table
//
LRESULT
TWindow::WindowProc(uint msg, WPARAM wParam, LPARAM lParam)
{
// Handle WM_COMMAND_ENABLE command enable msgs by directly calling the
// virtual EvCommandEnable
//
if (msg == WM_COMMAND_ENABLE) {
EvCommandEnable(*(TCommandEnabler*)lParam);
return 0;
}
// Handle WM_COMMAND command msgs by directly calling the
// virtual EvCommand
//
else if (msg == WM_COMMAND) {
#if defined(BI_PLAT_WIN16)
return EvCommand(LOWORD(wParam), (HWND)(uint)lParam, HIWORD(lParam));
#else
return EvCommand(LOWORD(wParam), (HWND)(uint)lParam, HIWORD(wParam));
#endif
}
// Handle all other messages by looking up and dispatching using the
// response tables
//
else {
#if defined(OWL_RTTI_MSGCACHE)
if (!UniqueId)
UniqueId = TYPE_UNIQUE_UINT32(*this);
#endif
uint key = TCacheEntry::Key(UniqueId, msg);
TEventInfo eventInfo(msg);
// Check the cache. A cache hit may be an RT handler, or an RT miss.
//
if (cacheEnabled && msgCache[key].Hit(msg, UniqueId)) {
eventInfo.Entry = msgCache[key].Entry;
if (eventInfo.Entry) {
TRACEX(OwlMsg, 1, MsgName(msg) << "* => " << *this);
eventInfo.Object = (GENERIC*)(((char*)this) + msgCache[key].Delta);
return Dispatch(eventInfo, wParam, lParam);
} // else fall out & do default below
}
// Perform the lookup on this window.
//
else if (Find(eventInfo)) {
TRACEX(OwlMsg, 1, MsgName(msg) << " => " << *this);
msgCache[key].Set(UniqueId, msg, eventInfo.Entry, int(eventInfo.Object) - int(this));
return Dispatch(eventInfo, wParam, lParam);
}
else // not found
msgCache[key].Set(UniqueId, msg, 0); // Cache no-handler entries too
// Behavior for messages that have no handler. If this is the main window,
// then give the app a chance to handle the message. If not the main
// window, or if the app had no handler, just call DefWindowProc() to
// pass the message back to whomever we subclassed
//
if (IsFlagSet(wfMainWindow)) {
TEventInfo cmdEventInfo(msg, wParam);
if (GetApplication()->Find(cmdEventInfo))
return GetApplication()->Dispatch(cmdEventInfo, wParam, lParam);
if (GetApplication()->Find(eventInfo))
return GetApplication()->Dispatch(eventInfo, wParam, lParam);
}
return DefWindowProc(msg, wParam, lParam);
}
}
//
// Save current event and call this window's WindowProc. Handles exceptions,
// error checking, & GetWindowPtr messages.
//
LRESULT
TWindow::HandleMessage(uint msg, WPARAM wParam, LPARAM lParam)
{
// save event away in "CurrentEvent"
//
TCurrentEvent& currentEvent = GetCurrentEvent();
TCurrentEvent saveEvent = currentEvent; // for nested calls
currentEvent.Win = this;
currentEvent.Message = msg;
currentEvent.WParam = wParam;
currentEvent.LParam = lParam;
// call window object's WindowProc virtual function
//
LRESULT result;
result = WindowProc(msg, wParam, lParam);
currentEvent = saveEvent; // restore previous event to current event
return result;
}
//
// Window function called for normal, initialized, OWL windows via a
// per-window thunk. 'this' ptr is setup in register(s) by the thunk before
// StdWndProc is called.
//
#if defined(BI_PLAT_WIN16)
LRESULT
FAR PASCAL __export
StdWndProc(HWND, uint msg, WPARAM wParam, LPARAM lParam)
{
return ((TWindow*)MK_FP(_ES,_BX))->ReceiveMessage(msg, wParam, lParam);
}
#else
LRESULT
FAR PASCAL _stdcall
StdWndProc(HWND, uint msg, WPARAM wParam, LPARAM lParam)
{
return ((TWindow*)_EAX)->ReceiveMessage(msg, wParam, lParam);
}
#endif
//
// First member function that receives messages from windows thru the
// thunk & then StdWndProc. Deals with exception suspension for non-NT
// environments.
//
LRESULT
TWindow::ReceiveMessage(uint msg, WPARAM wParam, LPARAM lParam)
{
// if it's a "GetWindowPtr" message, then return pointer to this window
//
if (msg == ::GetWindowPtrMsgId && (!lParam || lParam == LPARAM(Application)))
return LRESULT(this);
#if defined(BI_PLAT_WIN32)
static bool exceptionOK =
!(::GetVersion()&0x80000000) || (::GetVersion()&0xFF) >= 4;
if (exceptionOK)
return HandleMessage(msg, wParam, lParam);
#endif
TRY {
return HandleMessage(msg, wParam, lParam);
}
CATCH( (TXOwl& x) {
GetApplication()->SuspendThrow(x);
})
CATCH( (xalloc& x) {
GetApplication()->SuspendThrow(x);
})
CATCH( (xmsg& x) {
GetApplication()->SuspendThrow(x);
})
CATCH( (Bad_cast& x) {
GetApplication()->SuspendThrow(TApplication::xsBadCast);
})
CATCH( (Bad_typeid& x) {
GetApplication()->SuspendThrow(TApplication::xsBadTypeid);
})
CATCH( (...) {
GetApplication()->SuspendThrow(TApplication::xsUnknown);
})
ENDCATCH
// When exceptions are disabled CATCH is defined as empty, so the only code
// is the TRY block, making this return unreachable
//
# pragma warn -rch
return msg == WM_CREATE ? -1 : 0; // default value returned when exception caught
# pragma warn .rch
}
//
// Determine the object pointer by sending a GetWindowPtrMsgId message to the
// window. When the first StdWndProc() in the subclass chain receives this
// message it returns a pointer to the object (via the thunking mechanism).
// If app is non-zero, then the process makes sure that the corresponding
// TWindow is returned.
//
TWindow* _OWLFUNC GetWindowPtr(HWND hWnd, const TApplication* app)
{
if (!hWnd /* && ::IsWindow(hWnd) */) // could also check handle validity
return 0;
#if defined(BI_DATA_NEAR)
LPARAM param2 = app ? LPARAM(app) : 0;
#else
LPARAM param2 = LPARAM(app);
#endif
// Avoid SendMessage to cutdown the overhead & message spy tool flooding
//
// Under Win16, need to fallback to SendMessage when the given hWnd is owned
// by another task. Using CallWindowProc on it would result in a bad SS
// setup when it received the message.
//
// Under Win32 don't even try if it is not our process. Some Win32's will
// return a wndProc that crashes.
//
#if defined(BI_PLAT_WIN16)
if (::GetWindowTask(hWnd) != ::GetCurrentTask())
return (TWindow*)::SendMessage(hWnd, GetWindowPtrMsgId, 0, param2);
#else
uint32 processId;
::GetWindowThreadProcessId(hWnd, &processId);
if (processId != ::GetCurrentProcessId())
return 0;
#endif
WNDPROC wndProc = (WNDPROC)::GetWindowLong(hWnd, GWL_WNDPROC);
if (!wndProc)
return 0;
return (TWindow*)::CallWindowProc(wndProc, hWnd, GetWindowPtrMsgId, 0, param2);
}
//
// response method for an incoming WM_CREATE message
//
// Call virtual SetupWindow to give derived classes a chance to set things up
// now that we are created & have an HWindow
//
int
TWindow::EvCreate(CREATESTRUCT far&)
{
SetupWindow();
SetFlag(wfFullyCreated);
return (int)DefaultProcessing();
}
//
// regular windows never hold focus child handles--just say no.
//
bool
TWindow::HoldFocusHWnd(HWND /*hWndLose*/, HWND /*hWndGain*/)
{
return false;
}
//
// handle WM_KILLFOCUS so that we can have a parent window hold onto our
// HWindow and possibly restore focus later.
//
void
TWindow::EvKillFocus(HWND getFocus)
{
// Follow the parent chain back until a window volunteers to hold our handle
//
if (IsFlagSet(wfFullyCreated)) {
TWindow* holdWin = Parent;
while (holdWin && !holdWin->HoldFocusHWnd(HWindow, getFocus))
holdWin = holdWin->Parent;
}
DefaultProcessing();
}
//
// response method for an incoming WM_SIZE message
//
// saves the normal size of the window in Attr
//
// also calls the SetPageSize() and SetBarRange() methods of the TWindow's
// scroller, if it has been constructed
//
void
TWindow::EvSize(uint sizeType, TSize&)
{
if (Scroller && sizeType != SIZE_MINIMIZED) {
Scroller->SetPageSize();
Scroller->SetSBarRange();
}
if (sizeType == SIZE_RESTORED) {
TRect wndRect;
GetWindowRect(wndRect);
Attr.W = wndRect.Width();
Attr.H = wndRect.Height();
}
// Added owl functionality: notify parent of a resize in case it wants to
// adust accordingly
//
if (Parent && !(GetWindowLong(GWL_EXSTYLE)&WS_EX_NOPARENTNOTIFY))
Parent->SendMessage(WM_PARENTNOTIFY, WM_SIZE, (uint)HWindow);
DefaultProcessing();
}
//
// save the normal position of the window
//
// if IsIconic() or IsZoomed() ignore the position since it does not reflect the
// normal state
//
void
TWindow::EvMove(TPoint&)
{
if (!IsIconic() && !IsZoomed()) {
TRect wndRect;
GetWindowRect(wndRect);
if ((GetWindowLong(GWL_STYLE) & WS_CHILD) == WS_CHILD && Parent &&
Parent->HWindow)
Parent->ScreenToClient(wndRect.TopLeft());
Attr.X = wndRect.left;
Attr.Y = wndRect.top;
}
DefaultProcessing();
}
//
// handles WM_COMPAREITEM message (for owner draw controls) by forwarding
// message to control itself
//
LRESULT
TWindow::EvCompareItem(uint /*ctrlId*/, COMPAREITEMSTRUCT far& compareInfo)
{
TWindow* win = GetWindowPtr(compareInfo.hwndItem);
if (win)
return win->ForwardMessage();
return DefaultProcessing();
}
//
// handles WM_DELETEITEM message (for owner draw controls) by forwarding
// message to control itself
//
void
TWindow::EvDeleteItem(uint /*ctrlId*/, DELETEITEMSTRUCT far& deleteInfo)
{
TWindow* win = GetWindowPtr(deleteInfo.hwndItem);
if (deleteInfo.hwndItem != HWindow && win)
win->ForwardMessage();
else
DefaultProcessing();
}
//
// handles WM_DRAWITEM message (for owner draw controls & menus) by forwarding
// message to control itself
//
void
TWindow::EvDrawItem(uint /*ctrlId*/, DRAWITEMSTRUCT far& drawInfo)
{
if (drawInfo.CtlType == ODT_MENU) {
// dispatch to menu...
// TMenu* menu = ...
// menu->DrawItem(drawInfo);
}
else {
TWindow* win = GetWindowPtr(drawInfo.hwndItem);
if (drawInfo.hwndItem != HWindow && win) {
win->ForwardMessage();
return;
}
}
DefaultProcessing();
}
//
// handles WM_MEASUREITEM message (for owner draw controls & menus) by
// forwarding message to control itself
//
void
TWindow::EvMeasureItem(uint ctrlId, MEASUREITEMSTRUCT far& measureInfo)
{
if (measureInfo.CtlType == ODT_MENU) {
// dispatch to menu...
// TMenu* menu = ...
// menu->MeasureItem(measureInfo);
}
else {
HWND hwndCtl = GetDlgItem(measureInfo.CtlID); // hWnd not in struct, get
TWindow* win = GetWindowPtr(hwndCtl);
//
// If the GetWindowPtr failed, & CreationWindow is non-zero, then this
// WM_MEASUREITEM is probably for the ctrl which is not yet thunked.
// route the message directly to creation window.
//
extern TWindow* _OWLDATA CreationWindow;
if (!win) { // sometimes this is send before Owl thunks ctl
if (CreationWindow)
win = CreationWindow;
else
win = ChildWithId(ctrlId);
}
if (win) {
win->HandleMessage(WM_MEASUREITEM, ctrlId, LPARAM(&measureInfo));
return;
}
}
DefaultProcessing();
}
//
//
#if defined(BI_PLAT_WIN32)
LRESULT
TWindow::EvWin32CtlColor(WPARAM wParam, LPARAM lParam)
{
int ctlType = GetCurrentEvent().Message - WM_CTLCOLORMSGBOX;
CHECK(ctlType >= CTLCOLOR_MSGBOX && ctlType <= CTLCOLOR_STATIC);
TEventInfo eventInfo(WM_CTLCOLOR);
if (!Find(eventInfo))
return DefWindowProc(GetCurrentEvent().Message, wParam, lParam);
else {
typedef HBRUSH(GENERIC::*CTL_COLOR_PMF)(HDC, HWND, uint);
CTL_COLOR_PMF& pmf = (CTL_COLOR_PMF&)(eventInfo.Entry->Pmf);
return (LRESULT)(eventInfo.Object->*pmf)((HDC)wParam, (HWND)lParam, ctlType);
}
}
#endif
//
// dispatches scroll messages as if they were Command messages.
//
void
TWindow::DispatchScroll(uint scrollCode, uint /*thumbPos*/, HWND hWndCtrl)
{
if (hWndCtrl) {
TWindow* win = GetWindowPtr(hWndCtrl);
if (win)
win->ForwardMessage();
//
// Adjust "CurrentEvent" to allow DefaultProcessing to work
//
uint16 id = (uint16)::GetDlgCtrlID(hWndCtrl);
TCurrentEvent& currentEvent = GetCurrentEvent();
currentEvent.Message = WM_COMMAND;
#if defined(BI_PLAT_WIN16)
currentEvent.WParam = id;
currentEvent.LParam = MAKELPARAM(hWndCtrl, scrollCode);
#else
currentEvent.WParam = MAKEWPARAM(id, scrollCode);
currentEvent.LParam = LPARAM(hWndCtrl);
#endif
EvCommand(id, hWndCtrl, scrollCode);
return;
}
DefaultProcessing();
}
//
// response method for an incoming WM_HSCROLL message
//
// if the message is from a scrollbar control, calls DispatchScroll()
// otherwise passes the message to the TWindow's scroller if it has been
// constructed, else calls DefaultProcessing()
//
// assumes, because of a Windows bug, that if the window has the scrollbar
// style, it will not have scrollbar controls
//
void
TWindow::EvHScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
{
if (!(GetWindowLong(GWL_STYLE) & WS_HSCROLL))
DispatchScroll(scrollCode, thumbPos, hWndCtl); // from scrollbar control
else if (Scroller)
Scroller->HScroll(scrollCode, thumbPos);
else
DefaultProcessing();
}
//
// response method for an incoming WM_VSCROLL message
//
// if the message is from a scrollbar control, calls DispatchScroll()
// otherwise passes the message to the TWindow's scroller if it has been
// constructed, else calls DefaultProcessing()
//
// assumes, because of a Windows bug, that if the window has the scrollbar
// style, it will not have scrollbar controls
//
void
TWindow::EvVScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
{
if (!(GetWindowLong(GWL_STYLE) & WS_VSCROLL))
DispatchScroll(scrollCode, thumbPos, hWndCtl);
else if (Scroller)
Scroller->VScroll(scrollCode, thumbPos);
else
DefaultProcessing();
}
//
// response method for an incoming WM_ERASEBKGND message
//
bool
TWindow::EvEraseBkgnd(HDC hDC)
{
// color set, we'll handle erasing (or doing nothing) here
//
if (BkgndColor != NoColor) {
// if a color is set, blt out a rectangle of it, else don't erase & let
// paint handle background
//
if (BkgndColor != NoErase) {
SetBkColor(hDC, BkgndColor);
ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &GetClientRect(), 0, 0, 0);
}
return true;
}
//
// no color set, use default class brush
//
return (bool)DefaultProcessing();
}
//
// response method for an incoming WM_PAINT message
//
// calls Paint(), performing Windows-required paint setup and cleanup before
// and after. if the TWindow has a TScroller, also calls its BeginView() and
// EndView() methods before and after call to Paint()
//
void
TWindow::EvPaint()
{
if (IsFlagSet(wfAlias))
DefaultProcessing(); // use application-defined wndproc
else {
TPaintDC dc(*this);
TRect& rect = *(TRect*)&dc.Ps.rcPaint;
if (Scroller)
Scroller->BeginView(dc, rect);
Paint(dc, dc.Ps.fErase, rect);
if (Scroller)
Scroller->EndView();
}
}
//
// redraws the contents of the TWindow after a WM_PAINT message
// is received
//
// placeholder for descendant object types to redefine
//
void
TWindow::Paint(TDC&, bool /*erase*/, TRect&)
{
}
//
// response method for an incoming WM_SETCURSOR message
//
// if a cursor has been set for this window, & the mouse is over the
// client area, set the cursor
//
bool
TWindow::EvSetCursor(HWND hWndCursor, uint hitTest, uint /*mouseMsg*/)
{
if (hWndCursor == HWindow && hitTest == HTCLIENT && HCursor) {
::SetCursor(HCursor);
return true;
}
return (bool)DefaultProcessing();
}
//
// response method for an incoming WM_LBUTTONDOWN message
//
// if the TWindow's Scroller has been constructed and if auto-scrolling
// has been requested, captures mouse input, loops until a WM_LBUTTONUP
// message comes in calling the Scroller's AutoScroll method, and then
// releases capture on mouse input
// will also break if a WM_MOUSEMOVE comes in without the left button down
// indicating a lost WM_LBUTTONUP
//
void
TWindow::EvLButtonDown(uint /*modKeys*/, TPoint& /*pt*/)
{
if (Scroller && Scroller->IsAutoMode()) {
MSG loopMsg;
loopMsg.message = 0;
SetCapture();
while (loopMsg.message != WM_LBUTTONUP &&
(loopMsg.message != WM_MOUSEMOVE || (loopMsg.wParam&MK_LBUTTON))) {
if (::PeekMessage(&loopMsg, 0, 0, 0, PM_REMOVE)) {
::TranslateMessage(&loopMsg);
::DispatchMessage(&loopMsg);
}
Scroller->AutoScroll();
}
ReleaseCapture();
GetApplication()->ResumeThrow();
}
DefaultProcessing();
}
//
void
DoEnableAutoCreate(TWindow* win, void* /*retVal*/)
{
if (win->HWindow)
win->EnableAutoCreate();
}
//
// Destroys an MS-Windows element associated with the TWindow.
// First, sets the wfAutoCreate flag to ON for each of the windows in the
// TWindow's ChildList to allow later re-creation.
//
void
TWindow::Destroy(int)
{
if (HWindow) {
ForEach(DoEnableAutoCreate, 0);
if (::DestroyWindow(HWindow))
HWindow = 0;
GetApplication()->ResumeThrow();
WARNX(OwlWin, HWindow, 0, "::DestroyWindow(" << (uint)HWindow << ") failed");
}
}
//
// specifies registration attributes for the MS-Windows window class
// of the TWindow, allowing instances of TWindow to be registered
//
// sets the fields of the passed WNDCLASS parameter to the default
// attributes appropriate for a TWindow
//
void
TWindow::GetWindowClass(WNDCLASS& wndClass)
{
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = *GetModule();
wndClass.hIcon = 0;
wndClass.hCursor = ::LoadCursor(0, IDC_ARROW);
wndClass.hbrBackground = HBRUSH(COLOR_WINDOW + 1);
wndClass.lpszMenuName = 0;
wndClass.lpszClassName = GetClassName();
wndClass.style = CS_DBLCLKS;
wndClass.lpfnWndProc = ::InitWndProc;
}
//
// Returns a classname for a generic owl window. Builds the name based on the
// name part of the current module's filename with "Window" appended.
//
char far*
TWindow::GetClassName()
{
#if defined(_OWL_BUILDUP_CLASSNAME)
static char classSuffix[] = "Window";
static char className[_MAX_FNAME+sizeof(classSuffix)];
if (!*className) {
char filePath[_MAX_PATH];
#if (BI_PLAT_WIN16)
int len = GetModule()->GetModuleFileName(filePath, sizeof(filePath));
#else
int len = ::GetModuleFileName(0, filePath, sizeof(filePath));
#endif
CHECK(len > 0);
_splitpath(filePath, 0, 0, className, 0);
strcat(className, classSuffix);
}
return className;
#else
return "OwlWindow"; // assume application private class
#endif
}
//
void
TWindow::LoadAcceleratorTable()
{
if (Attr.AccelTable) {
hAccel = GetModule()->LoadAccelerators(Attr.AccelTable);
WARNX(OwlWin, !hAccel, 0,
"Unable to load accelerators " << Attr.AccelTable
<< " from " << *GetModule());
}
}
//
// Perform MS Windows window creation
//
void
TWindow::PerformCreate(int menuOrId)
{
HWindow = ::CreateWindowEx(Attr.ExStyle,
GetClassName(),
Title,
Attr.Style,
Attr.X, Attr.Y, Attr.W, Attr.H,
Parent ? Parent->HWindow : 0,
(HMENU)menuOrId,
*GetModule(),
Attr.Param);
}
//
// associates an MS-Windows interface element with the TWindow object,
// after creating the interface element if not already created
//
// when creating an element, uses the creation attributes previously set in
// the Attr data field(simply associates the TWindow with an already
// created interface element if the "FromResource" flag is set)
//
// Since this member function now throw an exception on error, it always
// returns true.
//
bool
TWindow::Create()
{
if (HWindow)
return true;
int menuOrId = 0;
DisableAutoCreate();
if (IsFlagSet(wfFromResource))
HWindow = Parent ? Parent->GetDlgItem(Attr.Id) : 0; // windows already created it
else if (Register()) {
::SetCreationWindow(this);
LoadAcceleratorTable();
if (Attr.Menu) {
menuOrId = (int)GetModule()->LoadMenu(Attr.Menu);
WARNX(OwlWin, !menuOrId, 0, "Unable to load menu: " << Attr.Menu <<
" from " << *GetModule());
}
else
menuOrId = Attr.Id;
PerformCreate(menuOrId);
GetApplication()->ResumeThrow();
}
else
THROW( TXWindow(this, IDS_CLASSREGISTERFAIL) );
if (!HWindow) {
if (Attr.Menu) // && !IsFlagSet(wfFromResource) ?
DestroyMenu(HMENU(menuOrId));
THROW( TWindow::TXWindow(this, IDS_WINDOWCREATEFAIL) );
}
// predefined (non-owl) class--grab the state info that the class setup,
// install the thunk & zero the creation window since InitWndProc never got
// called, and set a flag
//
if (!GetWindowPtr(HWindow)) {
GetWindowTextTitle();
GetHWndState();
SubclassWindowFunction();
::SetCreationWindow(0);
SetFlag(wfPredefinedClass|wfFullyCreated);
SetupWindow();
}
return true;
}
//
// performs setup following creation of an associated MS-Windows window.
// Iterates through the TWindow's ChildList, attempting to create
// an associated MS-Windows interface element for each child window
// object in the list (a child's Create method is not called if its
// wfAutoCreate flag is not set)
//
// calls TransferData to transfer data for its children for whom data transfer
// is enabled
//
// if the receiver has a TScroller object, calls the scroller's SetBarRange()
// method
//
// can be redefined in derived classes to perform additional special
// initialization that requires an HWND
//
void
TWindow::SetupWindow()
{
if (Scroller)
Scroller->SetSBarRange();
if (!CreateChildren())
THROW( TXWindow(0, IDS_CHILDCREATEFAIL) ); // not us, an unspecified child
TransferData(tdSetData);
}
//
// always called just before HWindow goes away to give derived classes a
// chance to cleanup HWND related resources.
//
void
TWindow::CleanupWindow()
{
}
//
// transfer window 'data' to/from the passed data buffer. Used to initialize
// windows and get data in or out of them.
//
// the direction passed specifies whether data is to be read from or
// written to the passed buffer, or whether the data element size is simply
// to be returned
//
// the return value is the size (in bytes) of the transfer data. this method
// recursively calls transfer on all its children that have wfTransfer set.
//
struct TTransferIterInfo {
void* Data;
TTransferDirection Direction;
};
static void transferDatchild(TWindow* child, TTransferIterInfo* info) {
if (child->IsFlagSet(wfTransfer))
(char*)info->Data += child->Transfer(info->Data, info->Direction);
}
uint
TWindow::Transfer(void* buffer, TTransferDirection direction)
{
if (buffer) {
TTransferIterInfo info = { buffer, direction };
ForEach((TActionFunc)transferDatchild, &info);
return (char near*)info.Data - (char near*)buffer;
}
return 0;
}
//$---------------------------------------------------------------------------
//
// transfers data between the TWindow's data buffer and the child
// windows in its ChildList (data is not transfered between any child
// windows whose wfTransfer flag is not set)
//
void
TWindow::TransferData(TTransferDirection direction)
{
if (TransferBuffer)
Transfer(TransferBuffer, direction);
}
//
// registers the TWindow's MS-Windows, if not already registered
//
bool
TWindow::Register()
{
// Only check for globally registered classes if not in an NT WoW box,
// since WoW plays nasty games with class registration.
//
#if defined(BI_PLAT_WIN32)
static bool checkGlobal = ToBool(::GetVersion()&0x80000000); // not NT
#else
static bool checkGlobal = !ToBool(::GetWinFlags()&0x4000); // not WoW box
#endif
WNDCLASS windowClass;
bool gc;
if (checkGlobal)
gc = ToBool(::GetClassInfo(0, GetClassName(), &windowClass));
else
gc = false;
if (!gc && !GetModule()->GetClassInfo(GetClassName(), &windowClass)) {
GetWindowClass(windowClass);
return ::RegisterClass(&windowClass);
}
return true;
}
//
// returns a bool indicating whether or not it is Ok to close the TWindow
//
// iterates through the TWindow's ChildList, calling the CanClose()
// method of each
//
// returns false if any of the child windows returns false
//
static bool cannotClose(TWindow* win, void*) {
return win->HWindow && !win->CanClose();
}
bool
TWindow::CanClose()
{
return !FirstThat(cannotClose);
}
//
// destroys the associated MS-Windows interface element
// after determining that it is Ok to do so
//
// if the TWindow is the main window of the application, calls the CanClose()
// method of the application, else calls this->CanClose(), before calling
// Destroy()
//
void
TWindow::CloseWindow(int retVal)
{
bool willClose;
if (IsFlagSet(wfMainWindow))
willClose = GetApplication()->CanClose();
else
willClose = CanClose();
if (willClose)
Destroy(retVal);
}
//
// the default response to a WM_CLOSE message is to call CloseWindow()
// and then have the window deleted if the HWindow was really destroyed.
//
void
TWindow::EvClose()
{
if (IsFlagSet(wfAlias))
DefaultProcessing();
else {
CloseWindow();
if (!HWindow && IsFlagSet(wfDeleteOnClose))
GetApplication()->Condemn(this); // Assumes delete
}
}
//
// responds to an incoming WM_DESTROY message
//
// Calls CleanupWindow() to let derived classes cleanup
// Clears the wfFullyCreated flag since this window is no longer fully created
//
// if the TWindow is the application's main window posts a 'quit' message to
// end the application, unless already in ~TApplication() (MainWindow == 0)
//
void
TWindow::EvDestroy()
{
ClearFlag(wfFullyCreated);
CleanupWindow();
if (!IsFlagSet(wfAlias)) {
if (IsFlagSet(wfMainWindow) && GetApplication()->IsRunning())
::PostQuitMessage(GetApplication()->Status);
}
DefaultProcessing();
}
//
// responds to an incoming WM_NCDESTROY message, the last message
// sent to an MS-Windows interface element
//
// sets the HWindow data member of the TWindow to zero to indicate that an
// interface element is no longer associated with the object
//
void
TWindow::EvNCDestroy()
{
DefaultProcessing();
HWindow = 0;
}
//
// respond to Windows attempt to close down
//
bool
TWindow::EvQueryEndSession()
{
if (IsFlagSet(wfAlias))
return (bool)DefaultProcessing();
else if (IsFlagSet(wfMainWindow))
return GetApplication()->CanClose();
else
return CanClose();
}
//
// if the window receives an Exit menu choice, it will attempt
// to close down the window
//
void
TWindow::CmExit()
{
if (IsFlagSet(wfMainWindow))
CloseWindow();
else
DefaultProcessing();
}
//
// Handle message posted to us by a control needing assistance in dealing with
// invalid inputs
//
void
TWindow::EvChildInvalid(HWND hWnd)
{
::SendMessage(hWnd, WM_CHILDINVALID, 0, 0);
}
//----------------------------------------------------------------------------
// Non-virtuals
//----------------------------------------------------------------------------
//$---------------------------------------------------------------------------
unsigned
TWindow::NumChildren()
{
return IndexOf(ChildList) + 1;
}
//$---------------------------------------------------------------------------
//
//
void
TWindow::AssignZOrder()
{
TWindow* wnd;
HWND curWindow = HWindow;
if (curWindow) {
curWindow = ::GetWindow(curWindow, GW_CHILD);
if (curWindow) {
int i = 1;
for (curWindow = ::GetWindow(curWindow, GW_HWNDLAST);
curWindow;
curWindow = ::GetWindow(curWindow, GW_HWNDPREV))
{
wnd = GetWindowPtr(curWindow);
if (wnd)
wnd->ZOrder = (uint16)i++;
}
}
}
}
//$---------------------------------------------------------------------------
//
// The private field ZOrder is used to ensure the Z-order is
// consistent through read and write of the object.
//
// When the object is written, parent->AssignZOrder will fill in this value
//
// ZOrder ranges from 1 to N where N is the number of objects and the top one.
// A ZOrder value of 0 means that the Z-ordering has not be recoreded.
//
bool
TWindow::OrderIsI(TWindow* win, void* position)
{
return win->ZOrder == *(int*)position;
}
//$---------------------------------------------------------------------------
//
// returns true if the child was supposed to be created but couldn't be
//
static bool
cantCreate(TWindow* win, void*)
{
if (win->HWindow)
return false;
bool autoCreate = win->IsFlagSet(wfAutoCreate);
WARNX(OwlWin, !autoCreate, 0,
"Child window(Id=" << win->GetId() << ") not autocreated");
if (!autoCreate)
return false;
//
// this call will only fail if a user-defined Create() returns false. Owl's
// Creates always throw exceptions.
//
if (!win->Create())
return true;
if (win->IsIconic()) {
int textLen = ::GetWindowTextLength(win->HWindow);
char* text = new char[textLen + 1];
::GetWindowText(win->HWindow, text, textLen + 1);
::SetWindowText(win->HWindow, text);
delete [] text;
}
return false;
}
//
// create the children of the object. returns true if all the windows
// were successfully created
//
bool
TWindow::CreateChildren()
{
if (FirstThat(cantCreate)) // create children first to restore created order
return false;
//
// restore Z-ordering for children that have Z-ordering recorded
//
HWND above = HWND_TOP;
for (int top = NumChildren(); top; top--) {
TWindow* wnd = FirstThat(&TWindow::OrderIsI, &top);
if (wnd) {
wnd->SetWindowPos(above, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
above = wnd->HWindow;
}
}
return true;
}
//$---------------------------------------------------------------------------
//
// adds the passed pointer to a child window to the linked list
// of sibling windows which ChildList points to
//
void
TWindow::AddChild(TWindow* child)
{
if (child)
if (ChildList) {
child->SiblingList = ChildList->SiblingList;
ChildList->SiblingList = child;
ChildList = child;
}
else {
ChildList = child;
child->SiblingList = child;
}
}
//$---------------------------------------------------------------------------
//
// returns a pointer to the TWindow's previous sibling (the
// window previous to the TWindow in its parent's child window
// list)
//
// returns the sibling which points to the TWindow
//
// if the TWindow was the first child added to the list, returns
// a pointer to the last child added
//
TWindow*
TWindow::Previous()
{
if (!SiblingList)
return 0;
else {
TWindow* CurrentIndex = this;
while (CurrentIndex->Next() != this)
CurrentIndex = CurrentIndex->Next();
return CurrentIndex;
}
}
//$---------------------------------------------------------------------------
//
// returns a pointer to the first TWindow in the ChildList that meets some
// specified criteria
//
// if no child in the list meets the criteria, 0 is returned
//
// the Test parameter which defines the criteria, is a pointer to a
// function that takes a TWindow pointer & a pointer to void
//
// the TWindow* will be used as the pointer to the child window and
// the void* as a pointer to the Test function's additional parameters
//
// the Test function must return a Boolean value indicating whether the
// child passed meets the criteria
//
TWindow*
TWindow::FirstThat(TCondFunc test, void* paramList)
{
if (ChildList) {
TWindow* nextChild = ChildList->Next();
TWindow* curChild;
do {
curChild = nextChild;
nextChild = nextChild->Next();
//
// Test curChild for okay
//
if (test(curChild, paramList))
return curChild;
} while (curChild != ChildList && ChildList);
}
return 0;
}
//$---------------------------------------------------------------------------
//
// iterates over each child window in the TWindow's ChildList,
// calling the procedure whose pointer is passed as the Action to be
// performed for each child
//
// a pointer to a child is passed as the first parameter to the iteration
// procedure
//
void
TWindow::ForEach(TActionFunc action, void* paramList)
{
if (ChildList) {
TWindow* curChild;
TWindow* nextChild = ChildList->Next(); // allows ForEach to delete children
do {
curChild = nextChild;
nextChild = nextChild->Next();
action(curChild, paramList);
} while (curChild != ChildList && ChildList);
}
}
//$---------------------------------------------------------------------------
//
// returns a pointer to the first TWindow in the ChildList that
// meets some specified criteria
//
// if no child in the list meets the criteria, 0 is returned
//
// the Test parameter which defines the criteria, is a pointer to a member
// function (this is how it's different from FirstThat above) that takes a
// pointer to a TWindow & a pointer to void
//
// the TWindow pointer will be used as the pointer to the child window and the
// void pointer as a pointer to the Test function's additional parameters
//
// the Test function must return a Boolean value indicating whether the child
// passed meets the criteria
//
TWindow*
TWindow::FirstThat(TCondMemFunc test, void* paramList)
{
if (ChildList) {
TWindow* nextChild = ChildList->Next();
TWindow* curChild;
do {
curChild = nextChild;
nextChild = nextChild->Next();
if ((this->*test)(curChild, paramList))
return curChild;
} while (curChild != ChildList && ChildList);
}
return 0;
}
//$---------------------------------------------------------------------------
//
// iterates over each child window in the TWindow's ChildList,
// calling the member function (unlike ForEach above which takes pointer
// to non-member function) whose pointer is passed as the Action to
// be performed for each child
//
// a pointer to a child is passed as the first parameter to the iteration
// procedure
//
void
TWindow::ForEach(TActionMemFunc action, void* paramList)
{
if (ChildList) {
TWindow* nextChild = ChildList->Next();
TWindow* curChild;
do {
curChild = nextChild;
nextChild = nextChild->Next();
(this->*action)(curChild, paramList);
} while (curChild != ChildList && ChildList);
}
}
//$---------------------------------------------------------------------------
//
// returns the position at which the passed child window appears
// in the TWindow's ChildList
//
// if the child does not appear in the list, -1 is returned
//
// zero'th position is ChildList->Next
//
static int position;
static bool isItThisChild1(TWindow* win, void* child) {
++position;
return win == (TWindow*)child;
}
int
TWindow::IndexOf(TWindow* child)
{
position = -1;
return FirstThat(isItThisChild1, child) ? position : -1;
}
//$---------------------------------------------------------------------------
//
// returns the child at the passed position in the TWindow's
// ChildList
//
// the ChildList is circularly-referent so that passing a position
// larger than the number of children will cause the traversal of the
// list to wrap
//
// zero'th position is ChildList->Next
//
TWindow*
TWindow::At(int position)
{
if (position == -1)
return 0;
else {
TWindow* currentChild = ChildList;
if (currentChild) {
currentChild = ChildList->Next();
while (position > 0) {
currentChild = currentChild->Next();
position--;
}
}
return currentChild;
}
}
//$---------------------------------------------------------------------------
//
// returns a pointer to the window in the ChildList with the passed Id
//
// if no child in the list has the passed Id, 0 is returned
//
static bool isItThisChild2(TWindow* win, void* id) {
return win->GetId() == *(int*)id;
}
TWindow*
TWindow::ChildWithId(int id) const
{
return ((TWindow*)this)->FirstThat(isItThisChild2, &id);
}
//$---------------------------------------------------------------------------
//
LRESULT
TWindow::SendMessage(uint msg, WPARAM wParam, LPARAM lParam) {
LRESULT result = ::SendMessage(HWindow, msg, wParam, lParam);
GetApplication()->ResumeThrow();
return result;
}
//$---------------------------------------------------------------------------
//
LRESULT
TWindow::ForwardMessage(HWND hWnd, bool send)
{
if (!hWnd)
return 0;
TCurrentEvent& currentEvent = GetCurrentEvent();
if (send) {
LRESULT result = ::SendMessage(hWnd, currentEvent.Message,
currentEvent.WParam,
currentEvent.LParam);
GetApplication()->ResumeThrow();
return result;
}
else
return ::PostMessage(hWnd, currentEvent.Message,
currentEvent.WParam,
currentEvent.LParam);
}
//$---------------------------------------------------------------------------
//
// Forward a message to an Owl window. If send, then bypass windows directly
// and call the owl window's window proc.
//
LRESULT
TWindow::ForwardMessage(bool send)
{
TCurrentEvent& currentEvent = GetCurrentEvent();
if (send)
return HandleMessage(currentEvent.Message, currentEvent.WParam,
currentEvent.LParam);
return ForwardMessage(HWindow, send);
}
//$---------------------------------------------------------------------------
//
void
TWindow::ChildBroadcastMessage(uint msg, WPARAM wParam, LPARAM lParam)
{
for (HWND hWndChild = GetWindow(GW_CHILD); hWndChild; ) {
HWND hWndNext = ::GetWindow(hWndChild, GW_HWNDNEXT);
::SendMessage(hWndChild, msg, wParam, lParam);
GetApplication()->ResumeThrow();
hWndChild = hWndNext;
}
}
//$---------------------------------------------------------------------------
//
// destroys the associated MS-Windows interface element and deletes the C++
// object unconditionally (without calling CanClose())
//
// This function is static to avoid side effects of deleting 'this'.
//
void
TWindow::ShutDownWindow(TWindow* win, int retVal)
{
win->Destroy(retVal);
delete win;
}
//$---------------------------------------------------------------------------
//
// displays the TWindow, after checking that it has a valid handle
//
void
TWindow::Show(int cmdShow)
{
//
// if the window is being minimzed send a WM_SYSCOMMAND; this way the
// frame window focus saving works properly
//
if (HWindow)
if (cmdShow == SW_MINIMIZE)
HandleMessage(WM_SYSCOMMAND, SC_MINIMIZE);
else
::ShowWindow(HWindow, cmdShow);
}
//$---------------------------------------------------------------------------
//
// sets the Title and caption of the TWindow
//
void
TWindow::SetCaption(const char far* title)
{
if (Title != title) {
if (HIWORD(Title))
delete [] Title;
Title = strnewdup(title);
}
if (HWindow)
::SetWindowText(HWindow, Title);
}
//$---------------------------------------------------------------------------
//
// gets the Title member var from the current window caption or text
//
void
TWindow::GetWindowTextTitle()
{
if (LOWORD(Title) == 0xFFFF) // ignore "don't-change" titles
return;
if (HIWORD(Title))
delete [] Title;
int titleLength = GetWindowTextLength();
if (titleLength < 0)
Title = strnewdup((const char far*)"");
else {
Title = new far char[titleLength + 1];
Title[0] = 0;
Title[titleLength] = 0;
GetWindowText(Title, titleLength + 1);
}
}
//$---------------------------------------------------------------------------
//
// copy over the styles, the coordinates & the id from the existing HWnd into
// the owl TWindow members.
// Note: the title is not copied here
//
void
TWindow::GetHWndState()
{
//
// retrieve Attr.Style and Attr.ExStyle
//
// NOTE: some windows controls (e.g. EDIT) change the style bits
// (e.g. WS_BORDER) from their original values. if we always reset
// Attr.Style and Attr.ExStyle by extracting their values from
// Windows, we will lose some of the style bits we supplied
// in the CreateWindowEx call. in the case of the ResourceId
// constructors, of course, we must retrieve these values.
//
if (IsFlagSet(wfFromResource)) {
Attr.Style = GetWindowLong(GWL_STYLE);
Attr.ExStyle = GetWindowLong(GWL_EXSTYLE);
}
//
// retrieve Attr.X, Attr.Y, Attr.W and Attr.H
//
TRect wndRect;
GetWindowRect(wndRect);
Attr.H = wndRect.Height();
Attr.W = wndRect.Width();
HWND hParent = GetParent();
if ((Attr.Style & WS_CHILD) && hParent)
::ScreenToClient(hParent, &wndRect.TopLeft());
Attr.X = wndRect.left;
Attr.Y = wndRect.top;
#if defined(BI_PLAT_WIN16)
Attr.Id = GetWindowWord(GWW_ID);
#else
Attr.Id = GetWindowLong(GWL_ID);
#endif
}
//$---------------------------------------------------------------------------
//
// Set the cursor for this window given a TModule and a resId
// Updates the current cursor if it is over this window.
//
bool
TWindow::SetCursor(TModule* module, TResId resId)
{
if (module == CursorModule && resId == CursorResId)
return false;
HCURSOR hOldCursor = (HCursor && CursorModule) ? HCursor : 0;
CursorModule = module;
CursorResId = resId;
if (CursorResId)
if (CursorModule)
HCursor = CursorModule->LoadCursor(CursorResId);
else
HCursor = ::LoadCursor(0, CursorResId);
else
HCursor = 0;
//
// If the cursor is in our client window then set it now
//
if (HWindow) {
TPoint p;
GetCursorPos(p);
ScreenToClient(p);
if (GetClientRect().Contains(p))
::SetCursor(HCursor);
}
//
// Destroy old cursor if there was one & it was not loaded from USER
//
if (hOldCursor)
::DestroyCursor(hOldCursor);
return true;
}
//
// Handle WM_INITMENUPOPUP while embeded to generate command enable messages
// for our server menu items. Very similar to TFrameWindow::EvInitMenuPopup;
// could rearrange code to share better.
//
void
TWindow::EvInitMenuPopup(HMENU hPopupMenu, uint /*index*/, bool sysMenu)
{
if (!sysMenu && hPopupMenu) {
const int count = ::GetMenuItemCount(hPopupMenu);
for (int pos = 0; pos < count; pos++) {
uint id;
if (hPopupMenu == GetMenu()) // top level menu
id = ::GetMenuItemID(hPopupMenu, pos);
else {
// For second level and below menus, return the implied id for popup
// sub-menus. The implied id for a sub-menu is the id of the first item
// in the popup sub-menu less 1. If there are more than one level of
// popup menus, it will recursively look into those sub-menus as well.
//
TMenu popupMenu(hPopupMenu);
id = popupMenu.GetMenuItemID(pos);
}
// ignore separators
//
if (id == 0 || id == uint(-1))
continue;
// Skip the rest if it is the mdi child list, or system commands
//
if (id == IDW_FIRSTMDICHILD || id > 0xF000)
break;
EvCommandEnable(TMenuItemEnabler(hPopupMenu, id, HWindow, pos));
}
}
}
//$---------------------------------------------------------------------------
//
// not inline to avoid requiring gdiobjec.h by window.h
//
bool
TWindow::GetUpdateRgn(TRegion& region, bool erase) const
{
return ::GetUpdateRgn(HWindow, region, erase);
}
//$---------------------------------------------------------------------------
//
//
int
TWindow::MessageBox(const char far* text,
const char far* caption,
uint type)
{
if (GetApplication()->BWCCEnabled()) {
static bool FAR PASCAL(*bwccMessageBox)(HWND, LPCSTR, LPCSTR, uint);
if (!bwccMessageBox)
(FARPROC)bwccMessageBox =
GetApplication()->GetBWCCModule()->GetProcAddress("BWCCMessageBox");
if (bwccMessageBox)
return (*bwccMessageBox)(HWindow, text, caption, type);
return 0;
}
else {
GetApplication()->EnableCtl3dAutosubclass(true);
int retValue = ::MessageBox(HWindow, text, caption, type);
GetApplication()->EnableCtl3dAutosubclass(false);
return retValue;
}
}
#if defined(__TRACE) || defined(__WARN)
ostream& operator <<(ostream& os, const TWindow& w)
{
os << '(';
#if !defined(BI_NO_RTTI)
os << typeid(w).name() << ',';
#endif
os << "0x" << hex << (unsigned)w.HWindow << ',';
if (!w.Parent)
os << '\'' << TResId(w.Title) << '\'';
else
os << "id=" << w.GetId();
os << ')';
return os;
}
#endif
//$---------------------------------------------------------------------------
//
// static member function to construct base TXOwl object
//
string
TWindow::TXWindow::Msg(TWindow* win, uint resId)
{
bool found; // did we locate the string
string rscMsg = ResourceIdToString(&found, resId);
if (found) {
char buf[255];
//
// supply title of window if we have a valid window
//
wsprintf(buf, rscMsg.c_str(), win && HIWORD(win->Title) ? win->Title : "");
return string(buf);
}
else
return rscMsg;
}
//$---------------------------------------------------------------------------
//
// InvalidWindow exception constructor
//
TWindow::TXWindow::TXWindow(TWindow* win, uint resId)
: TXOwl(Msg(win, resId), resId)
{
Window = win;
}
TWindow::TXWindow::TXWindow(const TXWindow& src) : TXOwl(src),Window(src.Window)
{
}
int
TWindow::TXWindow::Unhandled(TModule* app, unsigned promptResId)
{
delete Window;
Window = 0;
return TXOwl::Unhandled(app, promptResId);
}
TXOwl*
TWindow::TXWindow::Clone()
{
return new TXWindow(*this);
}