- 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>
2538 lines
67 KiB
C++
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);
|
|
}
|