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

1183 lines
33 KiB
C++

//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1992, 1994 by Borland International, All Rights Reserved
//
// Implementation of class TLayoutWindow.
//----------------------------------------------------------------------------
#include <owl/owlpch.h>
#include <owl/layoutwi.h>
DEFINE_RESPONSE_TABLE1(TLayoutWindow, TWindow)
EV_WM_SIZE,
END_RESPONSE_TABLE;
IMPLEMENT_CASTABLE(TLayoutWindow);
//
// class TFixed
// ----- ------
//
// simple fixed point class that maintains numbers as 16.16
//
class TFixed {
public:
long Value;
TFixed(int s = 0) {Value = long(s) << 16;}
TFixed(int num, int denom) {Value = long(num) * 65536L / denom;}
//
// unary negation operator
//
TFixed operator -() {return -Value;}
//
// postfix increment/decrement operators
//
void operator ++(int) {Value += 1L << 16;}
void operator --(int) {Value -= 1L << 16;}
//
// bitwise logical operators
//
TFixed operator <<(unsigned n) {return Value << char(n);}
TFixed operator >>(unsigned n) {return Value >> char(n);}
//
// assignment operators
//
TFixed& operator <<=(unsigned n) {Value <<= n; return *this;}
TFixed& operator >>=(unsigned n) {Value >>= n; return *this;}
TFixed& operator +=(const TFixed& f) {Value += f.Value; return *this;}
TFixed& operator -=(const TFixed& f) {Value -= f.Value; return *this;}
TFixed& operator *=(int s) {Value *= s; return *this;}
TFixed& operator *=(const TFixed& f) {Value = (Value >> 8) * (f.Value >> 8);
return *this;}
TFixed& operator /=(int s) {Value /= s; return *this;}
TFixed& operator /=(const TFixed& f) {Value /= f.Value >> 8; Value <<= 8;
return *this;}
//
// binary arithmetic operators
//
friend TFixed operator +(const TFixed& l,
const TFixed& r) {return l.Value + r.Value;}
friend TFixed operator +(int l,
const TFixed& r) {return TFixed(l) += r.Value;}
friend TFixed operator +(const TFixed& l,
int r) {return r + l;}
friend TFixed operator -(const TFixed& l,
const TFixed& r) {return l.Value - r.Value;}
friend TFixed operator -(int l,
const TFixed& r) {return TFixed(l) -= r.Value;}
friend TFixed operator -(const TFixed& l,
int r) {return l - TFixed(r);}
friend TFixed operator *(const TFixed& l,
const TFixed& r) {return (l.Value >> 8) * (r.Value >> 8);}
friend TFixed operator *(int l,
const TFixed& r) {return l * r.Value;}
friend TFixed operator *(const TFixed& l,
int r) {return l.Value * r;}
friend TFixed operator /(const TFixed& l,
const TFixed& r) {return (l.Value /(r.Value >> 8)) << 8;}
friend TFixed operator /(int l,
const TFixed& r) {return (long(l) << 16) / r.Value;}
friend TFixed operator /(const TFixed& l,
int r) {return l.Value / r;}
//
// equality operators
//
friend bool operator ==(const TFixed& l,
const TFixed& r) {return l.Value == r.Value;}
friend bool operator !=(const TFixed& l,
const TFixed& r) {return l.Value != r.Value;}
//
// conversion operator to int
//
operator int() {return int(Value >> 16);}
private:
TFixed(long v) {Value = v;}
};
//----------------------------------------------------------------------------
struct TVariable;
//
// constraints can have up to three input variables
//
// the method for solving the constraint is represented as an ordered linear
// combination of the inputs and the constant with the constant expressed last
//
struct TConstraint {
TVariable* Inputs[3];
TVariable* Output;
TFixed OrderedCombination[4];
TConstraint* Next;
TConstraint();
bool IsResolved(); // iff its inputs have been resolved
int Evaluate();
int NumActualInputs();
};
struct TVariable {
int Value;
TConstraint* DeterminedBy; // 0 if variable is constant
bool Resolved;
TVariable() {Value = 0; DeterminedBy = 0;}
};
//
// the layout metrics represent four equations. for equations that are
// "absolute" or "as is" we don't add a constraint and just set the variable
// value directly(and mark the variable as constant); otherwise we produce an
// ordered linear combination from the equation and add a constraint
//
struct TChildMetrics {
public:
bool GeneratedConstraints;
TWindow* Child;
TLayoutMetrics Metrics;
TVariable Variables[4]; // x => 0, y => 1, right => 2, bottom => 3
TChildMetrics* Next;
TChildMetrics(TWindow& child, TLayoutMetrics& metrics);
};
TChildMetrics::TChildMetrics(TWindow& child,
TLayoutMetrics& metrics)
: Child(&child), Metrics(metrics)
{
GeneratedConstraints = false;
Next = 0;
}
TConstraint::TConstraint()
{
Inputs[0] = Inputs[1] = Inputs[2] = 0;
OrderedCombination[0] = OrderedCombination[1] = OrderedCombination[2] = 1;
//
// NOTE: OrderedCombination[3] was initialized to 0 by the TFixed ctor
//
Output = 0;
}
bool
TConstraint::IsResolved()
{
return (!Inputs[0] || Inputs[0]->Resolved) &&
(!Inputs[1] || Inputs[1]->Resolved) &&
(!Inputs[2] || Inputs[2]->Resolved);
}
int
TConstraint::Evaluate()
{
TFixed value = OrderedCombination[3]; // initialize to constant part
if (Inputs[0])
value += OrderedCombination[0] * Inputs[0]->Value;
if (Inputs[1])
value += OrderedCombination[1] * Inputs[1]->Value;
if (Inputs[2])
value += OrderedCombination[2] * Inputs[2]->Value;
return value;
}
int
TConstraint::NumActualInputs()
{
for (int i = 0; i < 3; i++)
if (!Inputs[i])
break;
return i;
}
//----------------------------------------------------------------------------
TLayoutMetrics::TLayoutMetrics()
{
X.RelWin = 0;
X.MyEdge = X.OtherEdge = lmLeft;
X.Relationship = lmAsIs;
X.Units = lmLayoutUnits;
X.Value = 0;
Y.RelWin = 0;
Y.MyEdge = Y.OtherEdge = lmTop;
Y.Relationship = lmAsIs;
Y.Units = lmLayoutUnits;
Y.Value = 0;
Width.RelWin = 0;
Width.MyEdge = Width.OtherEdge = lmWidth;
Width.Relationship = lmAsIs;
Width.Units = lmLayoutUnits;
Width.Value = 0;
Height.RelWin = 0;
Height.MyEdge = Height.OtherEdge = lmHeight;
Height.Relationship = lmAsIs;
Height.Units = lmLayoutUnits;
Height.Value = 0;
}
void
TLayoutMetrics::SetMeasurementUnits(TMeasurementUnits units)
{
X.Units = Y.Units = Width.Units = Height.Units = units;
}
//----------------------------------------------------------------------------
TLayoutWindow::TLayoutWindow(TWindow* parent,
const char far* title,
TModule* module)
:
TWindow(parent, title, module)
{
//
// Initialize virtual bases, in case the derived-most used default ctor
//
TWindow::Init(parent, title, module);
NumChildMetrics = 0;
ChildMetrics = 0;
Constraints = 0;
Plan = 0;
PlanIsDirty = false;
ClientSize.cx = ClientSize.cy = 0;
//
// allocate variables for the parent's left, top, right, and bottom and
// mark them as resolved
//
Variables = new TVariable[4];
Variables[0].Resolved = true;
Variables[1].Resolved = true;
Variables[2].Resolved = true;
Variables[3].Resolved = true;
}
TLayoutWindow::~TLayoutWindow()
{
delete [] Variables;
//
// free the child metrics
//
for (TChildMetrics* childMetrics = ChildMetrics; childMetrics;) {
TChildMetrics* tmp = childMetrics;
childMetrics = childMetrics->Next;
delete tmp;
}
//
// free the constraints
//
ClearPlan();
for (TConstraint* c = Constraints; c;) {
TConstraint* tmp = c;
c = c->Next;
delete tmp;
}
}
void
TLayoutWindow::EvSize(uint sizeType, TSize& size)
{
TWindow::EvSize(sizeType, size);
if (sizeType != SIZE_MINIMIZED && size != ClientSize) {
ClientSize = size;
Layout();
}
}
void
TLayoutWindow::SetChildLayoutMetrics(TWindow& child, TLayoutMetrics& metrics)
{
TChildMetrics* childMetrics;
PlanIsDirty = true;
if (ChildMetrics) {
//
// see if we already have metrics for the child
//
for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next)
if (childMetrics->Child == &child) {
childMetrics->Child = &child;
childMetrics->Metrics = metrics;
//
// get rid of the old constraints
//
RemoveConstraints(*childMetrics);
return;
}
}
childMetrics = new TChildMetrics(child, metrics);
childMetrics->Next = ChildMetrics;
ChildMetrics = childMetrics;
NumChildMetrics++;
}
//
// Remove child (layout) metrics for a given child (if found) and update
// other children as necessary
//
bool
TLayoutWindow::RemoveChildLayoutMetrics(TWindow& child)
{
TChildMetrics** childMetrics;
for (childMetrics = &ChildMetrics; *childMetrics; childMetrics = &(*childMetrics)->Next)
if ((*childMetrics)->Child == &child) {
//
// unlink target metrics from list & clean up a bit
//
TChildMetrics* tmp = *childMetrics;
*childMetrics = tmp->Next;
RemoveConstraints(*tmp);
NumChildMetrics--;
//
// Update other child metrics now that removed metric is gone
// Check for case where new relWin is lmParent and adjust other edge
// to be what removed window was using. If an 'edge' is really a size,
// then give up & just leave it asis. If the removed window had an edge
// that was really a size, then use the other constraint in that
// dimension (X or Y)
//
for (TChildMetrics* cm = ChildMetrics; cm; cm = cm->Next) {
if (cm->Metrics.X.RelWin == &child) {
RemoveConstraints(*cm);
cm->Metrics.X.RelWin = tmp->Metrics.X.RelWin;
if (cm->Metrics.X.RelWin == lmParent)
cm->Metrics.X.OtherEdge = tmp->Metrics.X.OtherEdge;
}
if (cm->Metrics.Y.RelWin == &child) {
RemoveConstraints(*cm);
cm->Metrics.Y.RelWin = tmp->Metrics.Y.RelWin;
if (cm->Metrics.Y.RelWin == lmParent)
cm->Metrics.Y.OtherEdge = tmp->Metrics.Y.OtherEdge;
}
if (cm->Metrics.Width.RelWin == &child) {
RemoveConstraints(*cm);
if (cm->Metrics.Width.MyEdge == lmWidth)
cm->Metrics.Width.Relationship = lmAsIs;
else {
if (tmp->Metrics.Width.MyEdge == lmWidth) {
cm->Metrics.Width.RelWin = tmp->Metrics.X.RelWin;
if (cm->Metrics.Width.RelWin == lmParent)
cm->Metrics.Width.OtherEdge = tmp->Metrics.X.OtherEdge;
}
else {
cm->Metrics.Width.RelWin = tmp->Metrics.Width.RelWin;
if (cm->Metrics.Width.RelWin == lmParent)
cm->Metrics.Width.OtherEdge = tmp->Metrics.Width.OtherEdge;
}
}
}
if (cm->Metrics.Height.RelWin == &child) {
RemoveConstraints(*cm);
if (cm->Metrics.Height.MyEdge == lmHeight)
cm->Metrics.Height.Relationship = lmAsIs;
else {
if (tmp->Metrics.Height.MyEdge == lmHeight) {
cm->Metrics.Height.RelWin = tmp->Metrics.Y.RelWin;
if (cm->Metrics.Height.RelWin == lmParent)
cm->Metrics.Height.OtherEdge = tmp->Metrics.Y.OtherEdge;
}
else {
cm->Metrics.Height.RelWin = tmp->Metrics.Height.RelWin;
if (cm->Metrics.Height.RelWin == lmParent)
cm->Metrics.Height.OtherEdge = tmp->Metrics.Height.OtherEdge;
}
}
}
}
//
// finaly, delete target metrics
//
delete tmp;
return true;
}
return false;
}
TChildMetrics*
TLayoutWindow::GetChildMetrics(TWindow& child)
{
TChildMetrics* childMetrics;
for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next)
if (childMetrics->Child == &child)
return childMetrics;
return false;
}
bool
TLayoutWindow::GetChildLayoutMetrics(TWindow& child, TLayoutMetrics& metrics)
{
TChildMetrics* childMetrics;
for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next)
if (childMetrics->Child == &child) {
metrics = childMetrics->Metrics;
return true;
}
return false;
}
void
TLayoutWindow::RemoveChild(TWindow* child)
{
TWindow::RemoveChild(child);
RemoveChildLayoutMetrics(*child);
}
int
TLayoutWindow::LayoutUnitsToPixels(int value)
{
const long UnitsPerEM = 8;
return int((long(value) * FontHeight + UnitsPerEM / 2) / UnitsPerEM);
}
static
bool
HasBorder(TWindow* win)
{
//
// we consider it to have a border unless it is a pop-up or child window
// without WS_BORDER set
//
if ((win->Attr.Style & (WS_CHILD|WS_POPUP)) && !(win->Attr.Style & WS_BORDER))
return false;
else
return true;
}
void
TLayoutWindow::ExecutePlan()
{
for (TConstraint* c = Plan; c; c = c->Next)
c->Output->Value = c->Evaluate();
}
void
TLayoutWindow::ClearPlan()
{
if (Plan) {
//
// move all constraints that were in the plan back to the list of
// constraints
//
if (!Constraints)
Constraints = Plan;
else {
for (TConstraint* c = Constraints; c->Next; c = c->Next)
;
c->Next = Plan;
}
Plan = 0;
}
}
void
TLayoutWindow::BuildPlan()
{
TChildMetrics* childMetrics;
TConstraint* lastInPlan = 0;
ClearPlan();
//
// mark all variables that aren't determined by a constraint as resolved
//
for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
TVariable* variable = childMetrics->Variables;
variable->Resolved = variable->DeterminedBy ? false : true;
variable++;
variable->Resolved = variable->DeterminedBy ? false : true;
variable++;
variable->Resolved = variable->DeterminedBy ? false : true;
variable++;
variable->Resolved = variable->DeterminedBy ? false : true;
}
//
// uses local propagation as much as possible (because it's fast)
//
// if cycles exist then we will end up with constraints that haven't been
// added to the plan. we convert the remaining constraints into simultaneous
// linear equations which we solve using Gaussian elimination
//
// look for constraints that have all their input variables resolved and
// append them to the plan
//
for (bool foundOne = true; foundOne;) {
TConstraint* c = Constraints;
TConstraint* previous = 0;
foundOne = false;
while (c) {
if (c->IsResolved()) {
TConstraint* resolved = c;
c->Output->Resolved = true;
foundOne = true;
//
// extract the constraint from the list of constraints
//
if (previous)
previous->Next = c->Next;
else
Constraints = c->Next;
c = c->Next;
//
// append the constraint to the plan
//
if (lastInPlan)
lastInPlan->Next = resolved;
else
Plan = resolved;
lastInPlan = resolved;
}
else {
previous = c;
c = c->Next;
}
}
}
//
// Gaussian elimination not currently supported--give up
//
if (Constraints)
THROW( TXWindow(this, IDS_LAYOUTINCOMPLETE) );
}
static int
FindInput(TConstraint* simplify, TVariable* input)
{
for (int i = 0; i < 3; i++)
if (simplify->Inputs[i] == input)
return i;
return -1;
}
//
// simplify constraint "simplify" by substituting constraint "_using"
//
// we do this when the two constraints are defined in terms of each other
// 1. the output of "simplify" is an input of "_using"
// 2. the output of "_using" is an input of "simplify"
//
// we do this to avoid a layout cycle
//
// "output" is the output variable for constraint "_using"
//
static
void
Simplify(TConstraint* simplify, TVariable* output, TConstraint* _using)
{
if (!simplify)
return;
int outputOfSimplify = FindInput(_using, simplify->Output); // check #1
int target = FindInput(simplify, output); // check #2
if (outputOfSimplify != -1 && target != -1) {
int commonInputs[3];
int numInputsOfUsing = _using->NumActualInputs();
//
// count how many inputs are common between "simplify" and "_using"
//
for (int i = 0; i < numInputsOfUsing; i++)
commonInputs[i] = FindInput(simplify, _using->Inputs[i]);
//
// since constraints only have room for 3 inputs we can not simplify if the
// total number of the existing inputs minus the input we are going to back
// substitute for plus the number of inputs added by "_using" (i.e. inputs
// not common between the two constraints) exceeds 3
//
int numInputsOfSimplify = simplify->NumActualInputs() - 1;
int newInputs = 0;
//
// compute the number of additional inputs contributed by "_using"
//
for (i = 0; i < numInputsOfUsing; i++)
if (commonInputs[i] == -1 && i != outputOfSimplify)
newInputs++;
if (numInputsOfSimplify + newInputs > 3)
return;
TFixed m = simplify->OrderedCombination[target];
//
// adjust the constant part
//
simplify->OrderedCombination[3] += m * _using->OrderedCombination[3];
//
// merge the common inputs
//
for (i = 0; i < numInputsOfUsing; i++)
if (commonInputs[i] != -1)
simplify->OrderedCombination[commonInputs[i]] +=
m * _using->OrderedCombination[i];
simplify->Inputs[target] = 0; // input has been back substituted out
//
// if necessary shift the inputs following "output" (and their associated
// mutiplier) left by one...
//
for (i = target + 1; i < 3; i++)
if (simplify->Inputs[i]) {
simplify->Inputs[i - 1] = simplify->Inputs[i];
simplify->Inputs[i] = 0;
simplify->OrderedCombination[i - 1] = simplify->OrderedCombination[i];
}
//
// add the new inputs
//
for (i = 0; i < numInputsOfUsing; i++)
if (commonInputs[i] == -1 && i != outputOfSimplify) {
simplify->Inputs[numInputsOfSimplify] = _using->Inputs[i];
simplify->OrderedCombination[numInputsOfSimplify] =
m * _using->OrderedCombination[i];
numInputsOfSimplify++;
}
//
// now scale things back so that the output of "simplify" is 1
//
TFixed f = 1 - m;
simplify->OrderedCombination[3] /= f;
for (i = 0; i < numInputsOfSimplify; i++)
simplify->OrderedCombination[i] /= f;
}
}
void
TLayoutWindow::AddConstraint(TChildMetrics& metrics,
TLayoutConstraint* c,
TWhichConstraint whichConstraint)
{
int index;
TVariable* output;
TConstraint* result = new TConstraint;
//
// set the output variable
//
if (whichConstraint == XConstraint && metrics.Metrics.X.MyEdge == lmRight)
output = &metrics.Variables[2];
else if (whichConstraint == YConstraint && metrics.Metrics.Y.MyEdge == lmBottom)
output = &metrics.Variables[3];
else
output = &metrics.Variables[whichConstraint];
//
// set the inputs based on the edge
//
if (c->Relationship != lmAbsolute && c->Relationship != lmAsIs) {
TVariable* variables;
if (c->RelWin == lmParent)
variables = Variables;
else {
TChildMetrics* relWinMetrics = GetChildMetrics(*c->RelWin);
if (!relWinMetrics) {
delete result;
THROW( TXWindow(this, IDS_LAYOUTBADRELWIN) );
}
variables = relWinMetrics->Variables;
}
switch (c->OtherEdge) {
case lmLeft:
case lmTop:
case lmRight:
case lmBottom:
result->Inputs[0] = &variables[c->OtherEdge];
break;
case lmWidth:
case lmHeight:
//
// width => right - left + 1
// height => bottom - top + 1
//
result->Inputs[0] = &variables[c->OtherEdge - lmWidth+lmRight];
result->Inputs[1] = &variables[c->OtherEdge - lmWidth+lmLeft];
result->OrderedCombination[1] = -1;
result->OrderedCombination[3] = 1;
break;
case lmCenter:
switch (whichConstraint) {
case XConstraint:
case WidthConstraint:
//
// center => (left + right) / 2
//
result->Inputs[0] = &variables[0];
result->Inputs[1] = &variables[2];
break;
case YConstraint:
case HeightConstraint:
//
// center => (top + bottom) / 2
//
result->Inputs[0] = &variables[1];
result->Inputs[1] = &variables[3];
break;
}
result->OrderedCombination[0] = result->OrderedCombination[1] = TFixed(1,2);
break;
}
}
//
// now store the constant term as the last of the ordered linear combination
//
// we must do this after setting the inputs
//
// NOTE: we cannot assume that the constant part is 0, because it might have
// been set above
//
switch (c->Relationship) {
case lmAsIs:
result->OrderedCombination[3] += whichConstraint == WidthConstraint ?
metrics.Child->Attr.W :
metrics.Child->Attr.H;
break;
case lmAbsolute:
case lmSameAs:
case lmBelow:
case lmAbove: {
int value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
if (c->Relationship == lmAbove)
value = -value - 1;
else if (c->Relationship == lmBelow)
value++;
result->OrderedCombination[3] += value;
break;
}
case lmPercentOf:
TFixed percent = c->Percent;
percent /= 100;
result->OrderedCombination[0] *= percent;
result->OrderedCombination[3] *= percent;
switch (c->OtherEdge) {
case lmWidth:
case lmHeight:
case lmCenter:
result->OrderedCombination[1] *= percent;
break;
}
break;
}
//
// now handle cases where the left hand side is width, height, or center
//
// this must be done last...
//
if (result->Inputs[0])
index = result->Inputs[1] ? 2 : 1;
else
index = 0;
switch (c->MyEdge) {
case lmWidth:
if (whichConstraint == XConstraint || metrics.Metrics.X.MyEdge == lmRight) {
//
// rewrite "right - left + 1 = " as "left = right - (...) + 1"
//
for (int i = 0; i < index; i++)
result->OrderedCombination[i] = -result->OrderedCombination[i];
result->OrderedCombination[3] = -result->OrderedCombination[3];
result->OrderedCombination[3]++;
result->Inputs[index] = &metrics.Variables[2];
if (whichConstraint == WidthConstraint)
output = &metrics.Variables[XConstraint];
}
else {
//
// rewrite "right - left + 1 = " as "right = left + ... - 1"
//
result->Inputs[index] = &metrics.Variables[0];
result->OrderedCombination[3]--;
Simplify(metrics.Variables[0].DeterminedBy, output, result);
}
break;
case lmHeight:
if (whichConstraint == YConstraint || metrics.Metrics.Y.MyEdge == lmBottom) {
//
// rewrite "bottom - top + 1 = " as "top = bottom - (...) + 1"
//
for (int i = 0; i < index; i++)
result->OrderedCombination[i] = -result->OrderedCombination[i];
result->OrderedCombination[3] = -result->OrderedCombination[3];
result->OrderedCombination[3]++;
result->Inputs[index] = &metrics.Variables[3];
if (whichConstraint == HeightConstraint)
output = &metrics.Variables[YConstraint];
}
else {
//
// rewrite "bottom - top + 1 = " as "bottom = top + ... - 1"
//
result->Inputs[index] = &metrics.Variables[1];
result->OrderedCombination[3]--;
Simplify(metrics.Variables[1].DeterminedBy, output, result);
}
break;
case lmCenter:
TVariable* input = &metrics.Variables[0]; // left
switch (whichConstraint) {
case XConstraint:
//
// rewrite "(left + right) / 2 = " as "left = -right + 2 * (...)"
//
input += 2; // right
break;
case YConstraint:
//
// rewrite "(top + bottom) / 2 = " as "top = -bottom + 2 * (...)"
//
input += 3; // bottom
break;
case WidthConstraint:
//
// rewrite "(left + right) / 2 = " as "right = -left + 2 * (...)" or
// "left = -right + 2 * (...)" depending on whether the "x" constraint
// is left or right
//
if (metrics.Metrics.X.MyEdge == lmRight) {
input += 2; // right
output = &metrics.Variables[XConstraint];
}
break;
case HeightConstraint:
//
// rewrite "(top + bottom) / 2 = " as "bottom = -top + 2 * (...)" or
// "top = -bottom + 2 * (...)" depending on whether the "y" constraint
// is top or bottom
//
if (metrics.Metrics.Y.MyEdge != lmBottom)
input++; // top
else {
input += 3; // bottom
output = &metrics.Variables[XConstraint];
}
break;
}
result->Inputs[index] = input;
for (int i = 0; i < index; i++)
result->OrderedCombination[i] <<= 1;
result->OrderedCombination[3] <<= 1;
result->OrderedCombination[index] = -1;
break;
}
//
// now set the constraint output
//
output->DeterminedBy = result;
result->Output = output;
//
// add the constraint to the list of constraints
//
result->Next = Constraints;
Constraints = result;
}
void
TLayoutWindow::RemoveConstraints(TChildMetrics& childMetrics)
{
TVariable* variable = childMetrics.Variables;
PlanIsDirty = true;
ClearPlan();
childMetrics.GeneratedConstraints = false;
for (int i = 0; i < 4; i++) {
TConstraint* constraint = variable->DeterminedBy;
variable->Value = 0;
if (constraint) {
//
// remove the constraint from the list of constraints
//
if (Constraints == constraint)
Constraints = constraint->Next;
else
for (TConstraint* c = Constraints; c->Next; c = c->Next)
if (c->Next == constraint) {
c->Next = constraint->Next;
break;
}
delete constraint;
variable->DeterminedBy = 0;
}
variable++;
}
}
void
TLayoutWindow::BuildConstraints(TChildMetrics& childMetrics)
{
//
// NOTE: to get uniformity we consider the window edges to sit on pixels
// and not between pixels. so our idea of right is left + width - 1
// and not left + width
//
if (!childMetrics.GeneratedConstraints) {
TLayoutConstraint* c = &childMetrics.Metrics.X;
childMetrics.GeneratedConstraints = true;
//
// "x" can be one of: left, right, center
//
if (c->Relationship == lmAsIs)
if (c->MyEdge == lmLeft)
childMetrics.Variables[0].Value = childMetrics.Child->Attr.X;
else
childMetrics.Variables[2].Value = childMetrics.Child->Attr.X +
childMetrics.Child->Attr.W - 1;
else if (c->Relationship == lmAbsolute && c->MyEdge != lmCenter) {
int value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
childMetrics.Variables[c->MyEdge == lmLeft ? 0 : 2].Value = value;
}
else {
AddConstraint(childMetrics, c, XConstraint);
}
//
// "y" can be one of: top, bottom, center
//
c = &childMetrics.Metrics.Y;
if (c->Relationship == lmAsIs)
if (c->MyEdge == lmTop)
childMetrics.Variables[1].Value = childMetrics.Child->Attr.Y;
else
childMetrics.Variables[3].Value = childMetrics.Child->Attr.Y +
childMetrics.Child->Attr.H - 1;
else if (c->Relationship == lmAbsolute && c->MyEdge != lmCenter) {
int value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
childMetrics.Variables[c->MyEdge == lmTop ? 1 : 3].Value = value;
}
else {
AddConstraint(childMetrics, c, YConstraint);
}
//
// "width" can be one of: width, right, center
//
c = &childMetrics.Metrics.Width;
if (c->MyEdge == lmRight && (c->Relationship == lmAsIs || c->Relationship == lmAbsolute))
childMetrics.Variables[2].Value = c->Relationship == lmAsIs ?
childMetrics.Child->Attr.X +
childMetrics.Child->Attr.W - 1 :
c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
else
AddConstraint(childMetrics, c, WidthConstraint);
//
// "height" can be one of: height, bottom, center
//
c = &childMetrics.Metrics.Height;
if (c->MyEdge == lmBottom && (c->Relationship == lmAsIs || c->Relationship == lmAbsolute))
childMetrics.Variables[3].Value = c->Relationship == lmAsIs ?
childMetrics.Child->Attr.Y +
childMetrics.Child->Attr.H - 1 :
c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
else
AddConstraint(childMetrics, c, HeightConstraint);
}
}
void
TLayoutWindow::GetFontHeight()
{
HDC hDC = ::GetDC(0);
HFONT hFont = (HFONT)HandleMessage(WM_GETFONT);
HGDIOBJ hOldFont;
TEXTMETRIC tm;
if (hFont)
hOldFont = ::SelectObject(hDC, hFont);
::GetTextMetrics(hDC, &tm);
FontHeight = tm.tmHeight;
if (hFont)
::SelectObject(hDC, hOldFont);
::ReleaseDC(0, hDC);
}
void
TLayoutWindow::Layout()
{
if (ChildMetrics) {
TChildMetrics* childMetrics;
GetFontHeight();
//
// initialize the parent's variables
//
Variables[2].Value = ClientSize.cx - 1;
Variables[3].Value = ClientSize.cy - 1;
if (HasBorder(this)) {
int cxBorder = GetSystemMetrics(SM_CXBORDER);
int cyBorder = GetSystemMetrics(SM_CYBORDER);
Variables[0].Value = -cxBorder;
Variables[1].Value = -cyBorder;
Variables[2].Value += cxBorder;
Variables[3].Value += cyBorder;
}
else {
Variables[0].Value = 0;
Variables[1].Value = 0;
}
//
// Rebuild layout plan if necessary
//
if (PlanIsDirty) {
PlanIsDirty = false;
for (childMetrics = ChildMetrics; childMetrics;
childMetrics = childMetrics->Next)
BuildConstraints(*childMetrics);
BuildPlan();
}
//
// Use the plan to calculate actual child window position values
//
ExecutePlan();
//
// now do the actual resizing of the windows
//
for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
TWindow* win = childMetrics->Child;
TVariable* variables = childMetrics->Variables;
if (win->HWindow) {
win->SetWindowPos(
0,
variables[0].Value,
variables[1].Value,
variables[2].Value - variables[0].Value + 1,
variables[3].Value - variables[1].Value + 1,
SWP_NOZORDER | SWP_NOACTIVATE
);
}
else {
win->Attr.X = variables[0].Value;
win->Attr.Y = variables[1].Value;
win->Attr.W = variables[2].Value - variables[0].Value + 1;
win->Attr.H = variables[3].Value - variables[1].Value + 1;
}
}
}
}