//---------------------------------------------------------------------------- // ObjectWindows // (C) Copyright 1992, 1994 by Borland International, All Rights Reserved // // Implementation of class TLayoutWindow. //---------------------------------------------------------------------------- #include #include 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; } } } }