Files
TeslaRel410/BORLAND/BC45/SOURCE/OWL/GADGETWI.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

745 lines
17 KiB
C++

//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1992, 1994 by Borland International, All Rights Reserved
//
// Implementation of class TGadgetWindow.
//----------------------------------------------------------------------------
#include <owl/owlpch.h>
#include <owl/gadget.h>
#include <owl/gadgetwi.h>
DEFINE_RESPONSE_TABLE1(TGadgetWindow, TWindow)
EV_WM_CTLCOLOR,
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONUP,
EV_WM_LBUTTONDBLCLK,
EV_WM_MOUSEMOVE,
EV_WM_SIZE,
EV_WM_SYSCOLORCHANGE,
END_RESPONSE_TABLE;
static inline int HeightInPixels(int pointSize)
{
TScreenDC dc;
return MulDiv(-pointSize, dc.GetDeviceCaps(LOGPIXELSY), 72);
}
TGadgetWindowFont::TGadgetWindowFont(int pointSize, bool bold, bool italic)
:
TFont("MS Sans Serif", HeightInPixels(pointSize), 0, 0, 0,
bold ? FW_BOLD : FW_NORMAL,
uint8(VARIABLE_PITCH | FF_SWISS), uint8(italic))
{
}
TGadgetWindow::TGadgetWindow(TWindow* parent,
TTileDirection direction,
TFont* font,
TModule* module)
:
TWindow(parent, 0, module)
{
PRECONDITION(font);
Attr.Style |= WS_BORDER;
Capture = 0;
AtMouse = 0;
Font = font;
Direction = direction;
if (Direction == Horizontal) {
ShrinkWrapWidth = false;
ShrinkWrapHeight = true;
}
else {
ShrinkWrapWidth = true;
ShrinkWrapHeight = false;
}
HintMode = PressHints;
WideAsPossible = 0; // number of gadgets with "WideAsPossible" set
DirtyLayout = true;
Gadgets = 0;
NumGadgets = 0;
//
// compute the font height
//
TScreenDC dc;
TEXTMETRIC metrics;
dc.SelectObject(*Font);
dc.GetTextMetrics(metrics);
FontHeight = metrics.tmHeight + metrics.tmExternalLeading;
//
// choose a default height based on the height of the font plus room
// for a top and bottom border
//
Attr.H = FontHeight;
if (Attr.Style & WS_BORDER)
Attr.H += 2 * GetSystemMetrics(SM_CYBORDER);
SetBkgndColor(GetSysColor(COLOR_BTNFACE));
BkgndBrush = new TBrush(GetSysColor(COLOR_BTNFACE));
}
TGadgetWindow::~TGadgetWindow()
{
TGadget* gadget = Gadgets;
while (gadget) {
TGadget* tmp = gadget;
gadget = gadget->Next;
delete tmp;
}
delete Font;
delete BkgndBrush;
}
void
TGadgetWindow::EvSysColorChange()
{
for (TGadget* g = Gadgets; g; g = g->NextGadget())
g->SysColorChange();
SetBkgndColor(GetSysColor(COLOR_BTNFACE));
delete BkgndBrush;
BkgndBrush = new TBrush(GetSysColor(COLOR_BTNFACE));
}
bool
TGadgetWindow::IdleAction(long idleCount)
{
if (idleCount == 0) {
// See if we missed a mouse move & still need to send a MouseLeave to a
// gadget
//
if (AtMouse) {
TPoint crsPoint;
GetCursorPos(crsPoint);
if (WindowFromPoint(crsPoint) != HWindow)
HandleMessage(WM_MOUSEMOVE, 0, MAKELPARAM(-1,-1)); // nowhere
}
// Let the gadgets do command enabling if they need to
//
for (TGadget* g = Gadgets; g; g = g->NextGadget())
g->CommandEnable();
}
return TWindow::IdleAction(idleCount);
}
TGadget*
TGadgetWindow::GadgetWithId(int id) const
{
for (TGadget* g = Gadgets; g; g = g->NextGadget())
if (g->GetId() == id)
return g;
return 0;
}
void
TGadgetWindow::SetShrinkWrap(bool shrinkWrapWidth, bool shrinkWrapHeight)
{
ShrinkWrapWidth = shrinkWrapWidth;
ShrinkWrapHeight = shrinkWrapHeight;
}
bool
TGadgetWindow::GadgetSetCapture(TGadget& gadget)
{
if (Capture) {
return false;
}
else {
Capture = &gadget;
SetCapture();
return true;
}
}
void
TGadgetWindow::GadgetReleaseCapture(TGadget& gadget)
{
if (&gadget == Capture) {
Capture = 0;
ReleaseCapture();
}
}
//
// Simulate menu select & idle messages to our parent so that it can display
// hint text the same way it does for menu commands
//
void
TGadgetWindow::SetHintCommand(int id)
{
if (Parent) {
if (id > 0)
Parent->HandleMessage(WM_MENUSELECT, id, 0);
else
// Send a menuselect w/ flags==0xFFFF and hMenu==0 to indicate end
//
#if defined(BI_PLAT_WIN32)
Parent->HandleMessage(WM_MENUSELECT, 0xFFFF0000, 0); // flags+item, hmenu
#else
Parent->HandleMessage(WM_MENUSELECT, 0, 0x0000FFFFL);// item, hmenu+flags
#endif
Parent->HandleMessage(WM_ENTERIDLE, MSGF_MENU);
}
}
void
TGadgetWindow::SetMargins(TMargins& margins)
{
Margins = margins;
LayoutSession();
}
int
TGadgetWindow::LayoutUnitsToPixels(int units)
{
const long UnitsPerEM = 8;
return int((long(units) * FontHeight + UnitsPerEM / 2) / UnitsPerEM);
}
void
TGadgetWindow::GetMargins(TMargins& margins,
int& left, int& right, int& top, int& bottom)
{
switch (margins.Units) {
case TMargins::Pixels:
left = margins.Left;
top = margins.Top;
right = margins.Right;
bottom = margins.Bottom;
break;
case TMargins::LayoutUnits:
left = LayoutUnitsToPixels(margins.Left);
top = LayoutUnitsToPixels(margins.Top);
right = LayoutUnitsToPixels(margins.Right);
bottom = LayoutUnitsToPixels(margins.Bottom);
break;
case TMargins::BorderUnits:
int cxBorder = GetSystemMetrics(SM_CXBORDER);
int cyBorder = GetSystemMetrics(SM_CYBORDER);
left = margins.Left * cxBorder;
top = margins.Top * cyBorder;
right = margins.Right * cxBorder;
bottom = margins.Bottom * cyBorder;
break;
}
}
void
TGadgetWindow::LayoutSession()
{
if (HWindow)
InvalidateRect(TileGadgets());
}
void
TGadgetWindow::GetDesiredSize(TSize& size)
{
int left, right, top, bottom;
int maxWidth = 0, maxHeight = 0;
TGadget* lastGadget = 0;
GetMargins(Margins, left, right, top, bottom);
if (ShrinkWrapWidth || ShrinkWrapHeight) {
for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) {
TSize size;
gadget->GetDesiredSize(size);
if (size.cx > maxWidth)
maxWidth = size.cx;
if (size.cy > maxHeight)
maxHeight = size.cy;
}
//
// in some cases we need to actually tile the gadgets in order to tell how
// much room is needed
//
if (Direction == Horizontal && ShrinkWrapWidth ||
Direction == Vertical && ShrinkWrapHeight) {
TileGadgets();
for (lastGadget = Gadgets; lastGadget && lastGadget->Next; lastGadget = lastGadget->Next)
;
}
}
if (!ShrinkWrapWidth)
size.cx = Attr.W;
else if (Direction == Horizontal) {
size.cx = right;
if (lastGadget)
size.cx += lastGadget->GetBounds().right;
}
else {
size.cx = left + right + maxWidth;
if (Attr.Style & WS_BORDER)
size.cx += 2 * GetSystemMetrics(SM_CXBORDER);
}
if (!ShrinkWrapHeight)
size.cy = Attr.H;
else if (Direction == Vertical) {
size.cx = bottom;
if (lastGadget)
size.cx += lastGadget->GetBounds().bottom;
}
else {
size.cy = top + bottom + maxHeight;
if (Attr.Style & WS_BORDER)
size.cy += 2 * GetSystemMetrics(SM_CYBORDER);
}
}
bool
TGadgetWindow::Create()
{
TSize size;
GetDesiredSize(size);
if (ShrinkWrapWidth)
Attr.W = size.cx;
if (ShrinkWrapHeight)
Attr.H = size.cy;
bool r = TWindow::Create();
GetDesiredSize(size);
if ((ShrinkWrapWidth && Attr.W != size.cx) || (ShrinkWrapHeight && Attr.H != size.cy)) {
if (ShrinkWrapWidth)
Attr.W = size.cx;
if (ShrinkWrapHeight)
Attr.H = size.cy;
LayoutSession();
}
return r;
}
//
// Set direction, & default shrink wrap flags. If already created, then also
// adjust shrink wrap dimension & re-layout
//
void
TGadgetWindow::SetDirection(TTileDirection direction)
{
if (Direction != direction) {
Direction = direction;
if (Direction == Horizontal) {
ShrinkWrapWidth = false;
ShrinkWrapHeight = true;
}
else {
ShrinkWrapWidth = true;
ShrinkWrapHeight = false;
}
//
// Swap margin's X & Y axis
//
int t = Margins.Left; Margins.Left = Margins.Top; Margins.Top = t;
t = Margins.Right; Margins.Right = Margins.Bottom; Margins.Bottom = t;
if (HWindow) {
TSize size;
GetDesiredSize(size);
if (ShrinkWrapWidth)
Attr.W = size.cx;
if (ShrinkWrapHeight)
Attr.H = size.cy;
LayoutSession();
}
}
}
void
TGadgetWindow::GetInnerRect(TRect& rect)
{
int left, right, top, bottom;
GetMargins(Margins, left, right, top, bottom);
rect.left = left;
rect.right = Attr.W - right;
rect.top = top;
rect.bottom = Attr.H - bottom;
if (Attr.Style & WS_BORDER) {
rect.right -= 2 * GetSystemMetrics(SM_CYBORDER);
rect.bottom -= 2 * GetSystemMetrics(SM_CXBORDER);
}
}
void
TGadgetWindow::PositionGadget(TGadget*, TGadget*, TPoint&)
{
}
TRect
TGadgetWindow::TileHorizontally()
{
TRect innerRect;
TRect invalidRect;
int width = 0;
int x;
GetInnerRect(innerRect);
invalidRect.SetEmpty();
//
// first pass tally the width of all gadgets that don't have "WideAsPossible"
// set
//
// NOTE: we must also take into account any adjustments to the gadget spacing
//
for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) {
if (!gadget->WideAsPossible) {
TSize desiredSize;
gadget->GetDesiredSize(desiredSize);
width += desiredSize.cx;
}
if (gadget->Next) {
TPoint spacing(0, 0);
PositionGadget(gadget, gadget->Next, spacing);
width += spacing.x;
}
}
//
// now tile all the gadgets
//
x = innerRect.left;
for (gadget = Gadgets; gadget; gadget = gadget->Next) {
TRect bounds = gadget->Bounds;
TRect originalBounds(bounds);
TSize desiredSize;
bounds.left = x;
gadget->GetDesiredSize(desiredSize);
bounds.top = innerRect.top + (innerRect.Height() - desiredSize.cy) / 2;
bounds.bottom = bounds.top + desiredSize.cy;
if (gadget->WideAsPossible)
bounds.right = bounds.left + (innerRect.Width() - width) / WideAsPossible;
else
bounds.right = bounds.left + desiredSize.cx;
if (bounds != originalBounds) {
gadget->SetBounds(bounds);
invalidRect |= bounds;
if (originalBounds.TopLeft() != TPoint(0, 0))
invalidRect |= originalBounds;
}
x += bounds.Width();
if (gadget->Next) {
TPoint origin(x, 0);
PositionGadget(gadget, gadget->Next, origin);
x = origin.x;
}
}
return invalidRect;
}
TRect
TGadgetWindow::TileVertically()
{
TRect innerRect;
TRect invalidRect;
int y;
GetInnerRect(innerRect);
invalidRect.SetEmpty();
//
// now tile all the gadgets
//
y = innerRect.top;
for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) {
TRect bounds = gadget->Bounds;
TRect originalBounds(bounds);
TSize desiredSize;
gadget->GetDesiredSize(desiredSize);
bounds.top = y;
bounds.bottom = bounds.top + desiredSize.cy;
bounds.left = innerRect.left + (innerRect.Width() - desiredSize.cx) / 2;
bounds.right = bounds.left + desiredSize.cx;
if (bounds != originalBounds) {
gadget->SetBounds(bounds);
invalidRect |= bounds;
if (originalBounds.TopLeft () != TPoint (0, 0))
invalidRect |= originalBounds;
}
y += bounds.Height();
if (gadget->Next) {
TPoint origin(0, y);
PositionGadget(gadget, gadget->Next, origin);
y = origin.y;
}
}
return invalidRect;
}
TRect
TGadgetWindow::TileGadgets()
{
return (Direction == Horizontal) ? TileHorizontally() : TileVertically();
}
void
TGadgetWindow::GadgetChangedSize(TGadget&)
{
LayoutSession();
}
void
TGadgetWindow::Insert(TGadget& gadget, TPlacement placement, TGadget* sibling)
{
PRECONDITION(!gadget.Window);
TGadget** g = &Gadgets;
if (sibling || placement == After) {
while (*g && *g != sibling)
g = &(*g)->Next;
CHECK(*g == sibling);
}
if (placement == After && *g)
g = &(*g)->Next;
gadget.Next = *g;
*g = &gadget;
gadget.Window = this;
NumGadgets++;
gadget.Inserted();
if (gadget.WideAsPossible)
WideAsPossible++;
}
//
// remove a gadget from the gadget window's list
//
TGadget*
TGadgetWindow::Remove(TGadget& gadget)
{
if (gadget.Window != this || !Gadgets)
return 0;
//
// handle head-of-list case
//
if (&gadget == Gadgets) {
Gadgets = Gadgets->Next;
}
//
// scan for gadget, looking one link ahead
//
else {
TGadget* g = Gadgets;
while (g->Next && g->Next != &gadget)
g = g->Next;
if (!g->Next) // not found
return 0;
g->Next = g->Next->Next;
}
NumGadgets--;
//
// re-adjust gadget now that it doesn't live in a window
//
if (gadget.WideAsPossible)
WideAsPossible--;
if (&gadget == AtMouse)
AtMouse = 0;
GadgetReleaseCapture(gadget);
gadget.Removed();
gadget.Window = 0;
gadget.Next = 0;
gadget.GetBounds() -= gadget.GetBounds().TopLeft();
return &gadget;
}
void
TGadgetWindow::EvSize(uint sizeType, TSize& size)
{
TWindow::EvSize(sizeType, size);
if (DirtyLayout || WideAsPossible > 0) {
DirtyLayout = false;
TileGadgets();
Invalidate();
}
}
//
// Iterate thru the gadget list and paint each one.
//
void
TGadgetWindow::PaintGadgets(TDC& dc, bool, TRect& rect)
{
TPoint viewport = dc.GetViewportOrg();
for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) {
if (gadget->Bounds.Touches(rect)) {
dc.SetViewportOrg((TPoint&)gadget->Bounds);
if (gadget->Clip) {
dc.SaveDC();
dc.IntersectClipRect(gadget->Bounds);
}
gadget->Paint(dc);
if (gadget->Clip)
dc.RestoreDC();
}
}
dc.SetViewportOrg(viewport);
}
void
TGadgetWindow::Paint(TDC& dc, bool erase, TRect& rect)
{
dc.SelectObject(*Font);
PaintGadgets(dc, erase, rect);
}
//
// Return gadget that a given window-relative point is in, 0 if none
//
TGadget*
TGadgetWindow::GadgetFromPoint(TPoint& point) const
{
for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next)
if (gadget->PtIn(point - *(TSize*)&gadget->Bounds.TopLeft()))
break;
return gadget;
}
HBRUSH
TGadgetWindow::EvCtlColor(HDC hDC, HWND /*hWndChild*/, uint /*ctlType*/)
{
::SetBkColor(hDC, BkgndColor);
return HBRUSH(*BkgndBrush);
}
//
// Determine if the mouse down is in an enabled gadget, & if so pass it to
// the gadget.
//
void
TGadgetWindow::EvLButtonDown(uint modKeys, TPoint& point)
{
TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
if (gadget && (gadget->GetEnabled() || Capture))
gadget->LButtonDown(modKeys, point - *(TSize*)&gadget->Bounds.TopLeft());
TWindow::EvLButtonDown(modKeys, point);
}
//
// Forward mouse ups to the gadget that has captures the mouse, if any, or
// to the gadget at the mouse pos
//
void
TGadgetWindow::EvLButtonUp(uint modKeys, TPoint& point)
{
TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
if (gadget && (gadget->GetEnabled() || Capture))
gadget->LButtonUp(modKeys, TPoint(point.x - gadget->Bounds.left,
point.y - gadget->Bounds.top));
TWindow::EvLButtonUp(modKeys, point);
}
//
// Pass double clicks thru as if they were just a second click, finish the
// first click, & begin the second. Dn + Dbl + Up -> Dn + Up+Dn + Up
//
void
TGadgetWindow::EvLButtonDblClk(uint modKeys, TPoint& point)
{
EvLButtonUp(modKeys, point);
EvLButtonDown(modKeys, point);
}
//
// Forward mouse moves to the gadget that has captures the mouse, if any.
// Otherwise checks for mouse entering & leaving gadgets
//
// Could enhance this by delaying mouse enter messages until mouse has been
// in the same area for a while, or looking ahead in queue for mouse msgs.
//
void
TGadgetWindow::EvMouseMove(uint modKeys, TPoint& point)
{
if (Capture) {
Capture->MouseMove(modKeys, TPoint(point.x - Capture->Bounds.left,
point.y - Capture->Bounds.top));
}
else {
TGadget* gadget = GadgetFromPoint(point);
if (gadget != AtMouse) {
if (AtMouse)
AtMouse->MouseLeave(modKeys, TPoint(point.x - AtMouse->Bounds.left,
point.y - AtMouse->Bounds.top));
AtMouse = gadget;
if (AtMouse)
AtMouse->MouseEnter(modKeys, TPoint(point.x - AtMouse->Bounds.left,
point.y - AtMouse->Bounds.top));
}
}
TWindow::EvMouseMove(modKeys, point);
}