- 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>
1183 lines
33 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|