home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c083 / 11.ddi / OWLSRC.PAK / LAYOUTWI.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-02  |  32.8 KB  |  1,179 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows - (C) Copyright 1992, 1993 by Borland International
  3. //   source\owl\layoutwi.cpp
  4. //   Implementation of class TLayoutWindow.
  5. //----------------------------------------------------------------------------
  6. #include <owl\owlpch.h>
  7. #include <owl\layoutwi.h>
  8.  
  9. DEFINE_RESPONSE_TABLE1(TLayoutWindow, TWindow)
  10.   EV_WM_SIZE,
  11. END_RESPONSE_TABLE;
  12.  
  13. IMPLEMENT_CASTABLE(TLayoutWindow);
  14.  
  15. //
  16. //  class TFixed
  17. //  ----- ------
  18. //
  19. //  simple fixed point class that maintains numbers as 16.16
  20. //
  21. class TFixed {
  22.   public:
  23.     long  Value;
  24.  
  25.     TFixed(int s = 0) {Value = long(s) << 16;}
  26.     TFixed(int num, int denom) {Value = long(num) * 65536L / denom;}
  27.  
  28.     //
  29.     //  unary negation operator
  30.     //
  31.     TFixed         operator -() {return -Value;}
  32.  
  33.     //
  34.     //  postfix increment/decrement operators
  35.     //
  36.     void           operator ++(int) {Value += 1L << 16;}
  37.     void           operator --(int) {Value -= 1L << 16;}
  38.  
  39.     //
  40.     //  bitwise logical operators
  41.     //
  42.     TFixed         operator <<(unsigned n) {return Value << char(n);}
  43.     TFixed         operator >>(unsigned n) {return Value >> char(n);}
  44.  
  45.     //
  46.     // assignment operators
  47.     //
  48.     TFixed&        operator <<=(unsigned n) {Value <<= n; return *this;}
  49.     TFixed&        operator >>=(unsigned n) {Value >>= n; return *this;}
  50.  
  51.     TFixed&        operator +=(const TFixed& f) {Value += f.Value; return *this;}
  52.  
  53.     TFixed&        operator -=(const TFixed& f) {Value -= f.Value; return *this;}
  54.  
  55.     TFixed&        operator *=(int s) {Value *= s; return *this;}
  56.     TFixed&        operator *=(const TFixed& f) {Value = (Value >> 8) * (f.Value >> 8);
  57.                                                  return *this;}
  58.  
  59.     TFixed&        operator /=(int s) {Value /= s; return *this;}
  60.     TFixed&        operator /=(const TFixed& f) {Value /= f.Value >> 8; Value <<= 8;
  61.                                                  return *this;}
  62.  
  63.     //
  64.     // binary arithmetic operators
  65.     //
  66.     friend TFixed operator +(const TFixed& l,
  67.                              const TFixed& r) {return l.Value + r.Value;}
  68.     friend TFixed operator +(int           l,
  69.                              const TFixed& r) {return TFixed(l) += r.Value;}
  70.     friend TFixed operator +(const TFixed& l,
  71.                              int           r) {return r + l;}
  72.  
  73.     friend TFixed operator -(const TFixed& l,
  74.                              const TFixed& r) {return l.Value - r.Value;}
  75.     friend TFixed operator -(int           l,
  76.                              const TFixed& r) {return TFixed(l) -= r.Value;}
  77.     friend TFixed operator -(const TFixed& l,
  78.                              int           r) {return l - TFixed(r);}
  79.  
  80.     friend TFixed operator *(const TFixed& l,
  81.                              const TFixed& r) {return (l.Value >> 8) * (r.Value >> 8);}
  82.     friend TFixed operator *(int           l,
  83.                              const TFixed& r) {return l * r.Value;}
  84.     friend TFixed operator *(const TFixed& l,
  85.                              int           r) {return l.Value * r;}
  86.  
  87.     friend TFixed operator /(const TFixed& l,
  88.                              const TFixed& r) {return (l.Value /(r.Value >> 8)) << 8;}
  89.     friend TFixed operator /(int           l,
  90.                              const TFixed& r) {return (long(l) << 16) / r.Value;}
  91.     friend TFixed operator /(const TFixed& l,
  92.                              int           r) {return l.Value / r;}
  93.  
  94.     //
  95.     //  equality operators
  96.     //
  97.     friend BOOL   operator ==(const TFixed& l,
  98.                               const TFixed& r) {return l.Value == r.Value;}
  99.     friend BOOL   operator !=(const TFixed& l,
  100.                               const TFixed& r) {return l.Value != r.Value;}
  101.  
  102.     //
  103.     // conversion operator to int
  104.     //
  105.     operator int() {return int(Value >> 16);}
  106.  
  107.   private:
  108.     TFixed(long v) {Value = v;}
  109. };
  110.  
  111. //----------------------------------------------------------------------------
  112.  
  113. struct TVariable;
  114.  
  115. //
  116. // constraints can have up to three input variables
  117. //
  118. // the method for solving the constraint is represented as an ordered linear
  119. // combination of the inputs and the constant with the constant expressed last
  120. //
  121. struct TConstraint {
  122.   TVariable*    Inputs[3];
  123.   TVariable*    Output;
  124.   TFixed        OrderedCombination[4];
  125.   TConstraint*  Next;
  126.  
  127.   TConstraint();
  128.  
  129.   BOOL  IsResolved();  // iff its inputs have been resolved
  130.   int   Evaluate();
  131.   int   NumActualInputs();
  132. };
  133.  
  134. struct TVariable {
  135.   int           Value;
  136.   TConstraint*  DeterminedBy;  // 0 if variable is constant
  137.   BOOL          Resolved;
  138.  
  139.   TVariable() {Value = 0; DeterminedBy = 0;}
  140. };
  141.  
  142. //
  143. // the layout metrics represent four equations. for equations that are
  144. // "absolute" or "as is" we don't add a constraint and just set the variable
  145. // value directly(and mark the variable as constant); otherwise we produce an
  146. // ordered linear combination from the equation and add a constraint
  147. //
  148. struct TChildMetrics {
  149.   public:
  150.     BOOL            GeneratedConstraints;
  151.     TWindow*        Child;
  152.     TLayoutMetrics  Metrics;
  153.     TVariable       Variables[4];  // x => 0, y => 1, right => 2, bottom => 3
  154.     TChildMetrics*  Next;
  155.  
  156.     TChildMetrics(TWindow& child, TLayoutMetrics& metrics);
  157. };
  158.  
  159. TChildMetrics::TChildMetrics(TWindow&        child,
  160.                              TLayoutMetrics& metrics)
  161.   : Child(&child), Metrics(metrics)
  162. {
  163.   GeneratedConstraints = FALSE;
  164.   Next = 0;
  165. }
  166.  
  167. TConstraint::TConstraint()
  168. {
  169.   Inputs[0] = Inputs[1] = Inputs[2] = 0;
  170.   OrderedCombination[0] = OrderedCombination[1] = OrderedCombination[2] = 1;
  171.   //
  172.   // NOTE: OrderedCombination[3] was initialized to 0 by the TFixed ctor
  173.   //
  174.   Output = 0;
  175. }
  176.  
  177. BOOL
  178. TConstraint::IsResolved()
  179. {
  180.   return (!Inputs[0] || Inputs[0]->Resolved) &&
  181.          (!Inputs[1] || Inputs[1]->Resolved) &&
  182.          (!Inputs[2] || Inputs[2]->Resolved);
  183. }
  184.  
  185. int
  186. TConstraint::Evaluate()
  187. {
  188.   TFixed  value = OrderedCombination[3];  // initialize to constant part
  189.  
  190.   if (Inputs[0])
  191.     value += OrderedCombination[0] * Inputs[0]->Value;
  192.  
  193.   if (Inputs[1])
  194.     value += OrderedCombination[1] * Inputs[1]->Value;
  195.  
  196.   if (Inputs[2])
  197.     value += OrderedCombination[2] * Inputs[2]->Value;
  198.  
  199.   return value;
  200. }
  201.  
  202. int
  203. TConstraint::NumActualInputs()
  204. {
  205.   for (int i = 0; i < 3; i++)
  206.     if (!Inputs[i])
  207.       break;
  208.  
  209.   return i;
  210. }
  211.  
  212. //----------------------------------------------------------------------------
  213.  
  214. TLayoutMetrics::TLayoutMetrics()
  215. {
  216.   X.RelWin = 0;
  217.   X.MyEdge = X.OtherEdge = lmLeft;
  218.   X.Relationship = lmAsIs;
  219.   X.Units = lmLayoutUnits;
  220.   X.Value = 0;
  221.  
  222.   Y.RelWin = 0;
  223.   Y.MyEdge = Y.OtherEdge = lmTop;
  224.   Y.Relationship = lmAsIs;
  225.   Y.Units = lmLayoutUnits;
  226.   Y.Value = 0;
  227.  
  228.   Width.RelWin = 0;
  229.   Width.MyEdge = Width.OtherEdge = lmWidth;
  230.   Width.Relationship = lmAsIs;
  231.   Width.Units = lmLayoutUnits;
  232.   Width.Value = 0;
  233.  
  234.   Height.RelWin = 0;
  235.   Height.MyEdge = Height.OtherEdge = lmHeight;
  236.   Height.Relationship = lmAsIs;
  237.   Height.Units = lmLayoutUnits;
  238.   Height.Value = 0;
  239. }
  240.  
  241. void
  242. TLayoutMetrics::SetMeasurementUnits(TMeasurementUnits units)
  243. {
  244.   X.Units = Y.Units = Width.Units = Height.Units = units;
  245. }
  246.  
  247. //----------------------------------------------------------------------------
  248.  
  249. TLayoutWindow::TLayoutWindow(TWindow*        parent,
  250.                              const char far* title,
  251.                              TModule*        module)
  252.   : TWindow(parent, title, module)
  253. {
  254.   //
  255.   // Initialize virtual bases, in case the derived-most used default ctor
  256.   //
  257.   TWindow::Init(parent, title, module);
  258.  
  259.   NumChildMetrics = 0;
  260.   ChildMetrics = 0;
  261.   Constraints = 0;
  262.   Plan = 0;
  263.   PlanIsDirty = FALSE;
  264.   ClientSize.cx = ClientSize.cy = 0;
  265.  
  266.   //
  267.   // allocate variables for the parent's left, top, right, and bottom and
  268.   // mark them as resolved
  269.   //
  270.   Variables = new TVariable[4];
  271.   Variables[0].Resolved = TRUE;
  272.   Variables[1].Resolved = TRUE;
  273.   Variables[2].Resolved = TRUE;
  274.   Variables[3].Resolved = TRUE;
  275. }
  276.  
  277. TLayoutWindow::~TLayoutWindow()
  278. {
  279.   delete Variables;
  280.  
  281.   //
  282.   // free the child metrics
  283.   //
  284.   for (TChildMetrics* childMetrics = ChildMetrics; childMetrics;) {
  285.     TChildMetrics*  tmp = childMetrics;
  286.  
  287.     childMetrics = childMetrics->Next;
  288.     delete tmp;
  289.   }
  290.  
  291.   //
  292.   // free the constraints
  293.   //
  294.   ClearPlan();
  295.   for (TConstraint* c = Constraints; c;) {
  296.     TConstraint*  tmp = c;
  297.  
  298.     c = c->Next;
  299.     delete tmp;
  300.   }
  301. }
  302.  
  303. void
  304. TLayoutWindow::EvSize(UINT sizeType, TSize& size)
  305. {
  306.   TWindow::EvSize(sizeType, size);
  307.  
  308.   if (sizeType != SIZE_MINIMIZED && size != ClientSize) {
  309.     ClientSize = size;
  310.     Layout();
  311.   }
  312. }
  313.  
  314. void
  315. TLayoutWindow::SetChildLayoutMetrics(TWindow& child, TLayoutMetrics& metrics)
  316. {
  317.   TChildMetrics*  childMetrics;
  318.  
  319.   PlanIsDirty = TRUE;
  320.  
  321.   if (ChildMetrics) {
  322.     //
  323.     // see if we already have metrics for the child
  324.     //
  325.     for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next)
  326.       if (childMetrics->Child == &child) {
  327.         childMetrics->Child = &child;
  328.         childMetrics->Metrics = metrics;
  329.         //
  330.         // get rid of the old constraints
  331.         //
  332.         RemoveConstraints(*childMetrics);
  333.         return;
  334.       }
  335.   }
  336.  
  337.   childMetrics = new TChildMetrics(child, metrics);
  338.   childMetrics->Next = ChildMetrics;
  339.   ChildMetrics = childMetrics;
  340.   NumChildMetrics++;
  341. }
  342.  
  343. //
  344. // Remove child (layout) metrics for a given child (if found) and update
  345. // other children as necessary
  346. //
  347. BOOL
  348. TLayoutWindow::RemoveChildLayoutMetrics(TWindow& child)
  349. {
  350.   TChildMetrics**  childMetrics;
  351.  
  352.   for (childMetrics = &ChildMetrics; *childMetrics; childMetrics = &(*childMetrics)->Next)
  353.     if ((*childMetrics)->Child == &child) {
  354.       //
  355.       // unlink target metrics from list & clean up a bit
  356.       //
  357.       TChildMetrics*  tmp = *childMetrics;
  358.       *childMetrics = tmp->Next;
  359.       RemoveConstraints(*tmp);
  360.       NumChildMetrics--;
  361.       
  362.       //
  363.       // Update other child metrics now that removed metric is gone
  364.       // Check for case where new relWin is lmParent and adjust other edge
  365.       // to be what removed window was using. If an 'edge' is really a size,
  366.       // then give up & just leave it asis. If the removed window had an edge
  367.       // that was really a size, then use the other constraint in that
  368.       // dimension (X or Y)
  369.       //
  370.       for (TChildMetrics* cm = ChildMetrics; cm; cm = cm->Next) {
  371.         if (cm->Metrics.X.RelWin == &child) {
  372.           RemoveConstraints(*cm);
  373.           cm->Metrics.X.RelWin = tmp->Metrics.X.RelWin;
  374.           if (cm->Metrics.X.RelWin == lmParent)
  375.             cm->Metrics.X.OtherEdge = tmp->Metrics.X.OtherEdge;
  376.         }
  377.         if (cm->Metrics.Y.RelWin == &child) {
  378.           RemoveConstraints(*cm);
  379.           cm->Metrics.Y.RelWin = tmp->Metrics.Y.RelWin;
  380.           if (cm->Metrics.Y.RelWin == lmParent)
  381.             cm->Metrics.Y.OtherEdge = tmp->Metrics.Y.OtherEdge;
  382.         }
  383.         if (cm->Metrics.Width.RelWin == &child) {
  384.           RemoveConstraints(*cm);
  385.           if (cm->Metrics.Width.MyEdge == lmWidth)
  386.             cm->Metrics.Width.Relationship = lmAsIs;
  387.           else {
  388.             if (tmp->Metrics.Width.MyEdge == lmWidth) {
  389.               cm->Metrics.Width.RelWin = tmp->Metrics.X.RelWin;
  390.               if (cm->Metrics.Width.RelWin == lmParent)
  391.                 cm->Metrics.Width.OtherEdge = tmp->Metrics.X.OtherEdge;
  392.             } else {
  393.               cm->Metrics.Width.RelWin = tmp->Metrics.Width.RelWin;
  394.               if (cm->Metrics.Width.RelWin == lmParent)
  395.                 cm->Metrics.Width.OtherEdge = tmp->Metrics.Width.OtherEdge;
  396.             }
  397.           }
  398.         }
  399.         if (cm->Metrics.Height.RelWin == &child) {
  400.           RemoveConstraints(*cm);
  401.           if (cm->Metrics.Height.MyEdge == lmHeight)
  402.             cm->Metrics.Height.Relationship = lmAsIs;
  403.           else {
  404.             if (tmp->Metrics.Height.MyEdge == lmHeight) {
  405.               cm->Metrics.Height.RelWin = tmp->Metrics.Y.RelWin;
  406.               if (cm->Metrics.Height.RelWin == lmParent)
  407.                 cm->Metrics.Height.OtherEdge = tmp->Metrics.Y.OtherEdge;
  408.             } else {
  409.               cm->Metrics.Height.RelWin = tmp->Metrics.Height.RelWin;
  410.               if (cm->Metrics.Height.RelWin == lmParent)
  411.                 cm->Metrics.Height.OtherEdge = tmp->Metrics.Height.OtherEdge;
  412.             }
  413.           }
  414.         }
  415.       }
  416.       
  417.       //
  418.       // finaly, delete target metrics
  419.       //
  420.       delete tmp;
  421.       return TRUE;
  422.     }
  423.  
  424.   return FALSE;
  425. }
  426.  
  427. TChildMetrics*
  428. TLayoutWindow::GetChildMetrics(TWindow& child)
  429. {
  430.   TChildMetrics*  childMetrics;
  431.  
  432.   for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next)
  433.     if (childMetrics->Child == &child)
  434.       return childMetrics;
  435.  
  436.   return FALSE;
  437. }
  438.  
  439. BOOL
  440. TLayoutWindow::GetChildLayoutMetrics(TWindow& child, TLayoutMetrics& metrics)
  441. {
  442.   TChildMetrics*  childMetrics;
  443.  
  444.   for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next)
  445.     if (childMetrics->Child == &child) {
  446.       metrics = childMetrics->Metrics;
  447.       return TRUE;
  448.     }
  449.  
  450.   return FALSE;
  451. }
  452.  
  453. void
  454. TLayoutWindow::RemoveChild(TWindow* child)
  455. {
  456.   TWindow::RemoveChild(child);
  457.   RemoveChildLayoutMetrics(*child);
  458. }
  459.  
  460. int
  461. TLayoutWindow::LayoutUnitsToPixels(int value)
  462. {
  463.   const long  UnitsPerEM = 8;
  464.  
  465.   return int((long(value) * FontHeight + UnitsPerEM / 2) / UnitsPerEM);
  466. }
  467.  
  468. static
  469. BOOL
  470. HasBorder(TWindow* win)
  471. {
  472.   //
  473.   // we consider it to have a border unless it is a pop-up or child window
  474.   // without WS_BORDER set
  475.   //
  476.   if ((win->Attr.Style & (WS_CHILD|WS_POPUP)) && !(win->Attr.Style & WS_BORDER))
  477.     return FALSE;
  478.  
  479.   else
  480.     return TRUE;
  481. }
  482.  
  483. void
  484. TLayoutWindow::ExecutePlan()
  485. {
  486.   for (TConstraint* c = Plan; c; c = c->Next)
  487.     c->Output->Value = c->Evaluate();
  488. }
  489.  
  490. void
  491. TLayoutWindow::ClearPlan()
  492. {
  493.   if (Plan) {
  494.     //
  495.     // move all constraints that were in the plan back to the list of
  496.     // constraints
  497.     //
  498.     if (!Constraints)
  499.       Constraints = Plan;
  500.  
  501.     else {
  502.       for (TConstraint* c = Constraints; c->Next; c = c->Next)
  503.         ;
  504.  
  505.       c->Next = Plan;
  506.     }
  507.  
  508.     Plan = 0;
  509.   }
  510. }
  511.  
  512. void
  513. TLayoutWindow::BuildPlan()
  514. {
  515.   TChildMetrics*  childMetrics;
  516.   TConstraint*    lastInPlan = 0;
  517.  
  518.   ClearPlan();
  519.  
  520.   //
  521.   // mark all variables that aren't determined by a constraint as resolved
  522.   //
  523.   for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
  524.     TVariable*  variable = childMetrics->Variables;
  525.  
  526.     variable->Resolved = variable->DeterminedBy ? FALSE : TRUE;
  527.     variable++;
  528.     variable->Resolved = variable->DeterminedBy ? FALSE : TRUE;
  529.     variable++;
  530.     variable->Resolved = variable->DeterminedBy ? FALSE : TRUE;
  531.     variable++;
  532.     variable->Resolved = variable->DeterminedBy ? FALSE : TRUE;
  533.   }
  534.  
  535.   //
  536.   // uses local propagation as much as possible (because it's fast)
  537.   //
  538.   // if cycles exist then we will end up with constraints that haven't been
  539.   // added to the plan. we convert the remaining constraints into simultaneous
  540.   // linear equations which we solve using Gaussian elimination
  541.   //
  542.   // look for constraints that have all their input variables resolved and
  543.   // append them to the plan
  544.   //
  545.   for (BOOL foundOne = TRUE; foundOne;) {
  546.     TConstraint*  c = Constraints;
  547.     TConstraint*  previous = 0;
  548.  
  549.     foundOne = FALSE;
  550.  
  551.     while (c) {
  552.       if (c->IsResolved()) {
  553.         TConstraint*  resolved = c;
  554.  
  555.         c->Output->Resolved = TRUE;
  556.         foundOne = TRUE;
  557.         //
  558.         // extract the constraint from the list of constraints
  559.         //
  560.         if (previous)
  561.           previous->Next = c->Next;
  562.  
  563.         else
  564.           Constraints = c->Next;
  565.  
  566.         c = c->Next;
  567.  
  568.         //
  569.         // append the constraint to the plan
  570.         //
  571.         if (lastInPlan)
  572.           lastInPlan->Next = resolved;
  573.  
  574.         else
  575.           Plan = resolved;
  576.  
  577.         lastInPlan = resolved;
  578.  
  579.       } else {
  580.         previous = c;
  581.         c = c->Next;
  582.       }
  583.     }
  584.   }
  585.  
  586.   //
  587.   // Gaussian elimination not currently supported--give up
  588.   //
  589.   if (Constraints)
  590.     THROW( TXWindow(this, IDS_LAYOUTINCOMPLETE) );
  591. }
  592.  
  593. static int
  594. FindInput(TConstraint* simplify, TVariable* input)
  595. {
  596.   for (int i = 0; i < 3; i++)
  597.     if (simplify->Inputs[i] == input)
  598.       return i;
  599.  
  600.   return -1;
  601. }
  602.  
  603. //
  604. // simplify constraint "simplify" by substituting constraint "_using"
  605. //
  606. // we do this when the two constraints are defined in terms of each other
  607. //   1. the output of "simplify" is an input of "_using"
  608. //   2. the output of "_using" is an input of "simplify"
  609. //
  610. // we do this to avoid a layout cycle
  611. //
  612. // "output" is the output variable for constraint "_using"
  613. //
  614. static
  615. void
  616. Simplify(TConstraint* simplify, TVariable* output, TConstraint* _using)
  617. {
  618.   if (!simplify)
  619.     return;
  620.  
  621.   int  outputOfSimplify = FindInput(_using, simplify->Output);  // check #1
  622.   int  target = FindInput(simplify, output);  // check #2
  623.  
  624.   if (outputOfSimplify != -1 && target != -1) {
  625.     int  commonInputs[3];
  626.     int  numInputsOfUsing = _using->NumActualInputs();
  627.  
  628.     //
  629.     // count how many inputs are common between "simplify" and "_using"
  630.     //
  631.     for (int i = 0; i < numInputsOfUsing; i++)
  632.       commonInputs[i] = FindInput(simplify, _using->Inputs[i]);
  633.  
  634.     //
  635.     // since constraints only have room for 3 inputs we can not simplify if the
  636.     // total number of the existing inputs minus the input we are going to back
  637.     // substitute for plus the number of inputs added by "_using" (i.e. inputs
  638.     // not common between the two constraints) exceeds 3
  639.     //
  640.     int  numInputsOfSimplify = simplify->NumActualInputs() - 1;
  641.     int  newInputs = 0;
  642.  
  643.     //
  644.     // compute the number of additional inputs contributed by "_using"
  645.     //
  646.     for (i = 0; i < numInputsOfUsing; i++)
  647.       if (commonInputs[i] == -1 && i != outputOfSimplify)
  648.         newInputs++;
  649.  
  650.     if (numInputsOfSimplify + newInputs > 3)
  651.       return;
  652.  
  653.     TFixed  m = simplify->OrderedCombination[target];
  654.  
  655.     //
  656.     // adjust the constant part
  657.     //
  658.     simplify->OrderedCombination[3] += m * _using->OrderedCombination[3];
  659.  
  660.     //
  661.     // merge the common inputs
  662.     //
  663.     for (i = 0; i < numInputsOfUsing; i++)
  664.       if (commonInputs[i] != -1)
  665.         simplify->OrderedCombination[commonInputs[i]] +=
  666.           m * _using->OrderedCombination[i];
  667.  
  668.     simplify->Inputs[target] = 0;  // input has been back substituted out
  669.  
  670.     //
  671.     // if necessary shift the inputs following "output" (and their associated
  672.     // mutiplier) left by one...
  673.     //
  674.     for (i = target + 1; i < 3; i++)
  675.       if (simplify->Inputs[i]) {
  676.         simplify->Inputs[i - 1] = simplify->Inputs[i];
  677.         simplify->Inputs[i] = 0;
  678.         simplify->OrderedCombination[i - 1] = simplify->OrderedCombination[i];
  679.       }
  680.  
  681.     //
  682.     // add the new inputs
  683.     //
  684.     for (i = 0; i < numInputsOfUsing; i++)
  685.       if (commonInputs[i] == -1 && i != outputOfSimplify) {
  686.         simplify->Inputs[numInputsOfSimplify] = _using->Inputs[i];
  687.         simplify->OrderedCombination[numInputsOfSimplify] =
  688.           m * _using->OrderedCombination[i];
  689.         numInputsOfSimplify++;
  690.       }
  691.  
  692.     //
  693.     // now scale things back so that the output of "simplify" is 1
  694.     //
  695.     TFixed  f = 1 - m;
  696.  
  697.     simplify->OrderedCombination[3] /= f;
  698.     for (i = 0; i < numInputsOfSimplify; i++)
  699.       simplify->OrderedCombination[i] /= f;
  700.   }
  701. }
  702.  
  703. void
  704. TLayoutWindow::AddConstraint(TChildMetrics&     metrics,
  705.                              TLayoutConstraint* c,
  706.                              TWhichConstraint   whichConstraint)
  707. {
  708.   int           index;
  709.   TVariable*    output;
  710.   TConstraint*  result = new TConstraint;
  711.  
  712.   //
  713.   // set the output variable
  714.   //
  715.   if (whichConstraint == XConstraint && metrics.Metrics.X.MyEdge == lmRight)
  716.     output = &metrics.Variables[2];
  717.  
  718.   else if (whichConstraint == YConstraint && metrics.Metrics.Y.MyEdge == lmBottom)
  719.     output = &metrics.Variables[3];
  720.  
  721.   else
  722.     output = &metrics.Variables[whichConstraint];
  723.  
  724.   //
  725.   // set the inputs based on the edge
  726.   //
  727.   if (c->Relationship != lmAbsolute && c->Relationship != lmAsIs) {
  728.     TVariable*  variables;
  729.  
  730.     if (c->RelWin == lmParent)
  731.       variables = Variables;
  732.  
  733.     else {
  734.       TChildMetrics*  relWinMetrics = GetChildMetrics(*c->RelWin);
  735.       if (!relWinMetrics) {
  736.         delete result;
  737.         THROW( TXWindow(this, IDS_LAYOUTBADRELWIN) );
  738.       }
  739.       variables = relWinMetrics->Variables;
  740.     }
  741.  
  742.     switch (c->OtherEdge) {
  743.       case lmLeft:
  744.       case lmTop:
  745.       case lmRight:
  746.       case lmBottom:
  747.         result->Inputs[0] = &variables[c->OtherEdge];
  748.         break;
  749.  
  750.       case lmWidth:
  751.       case lmHeight:
  752.         //
  753.         // width => right - left + 1
  754.         // height => bottom - top + 1
  755.         //
  756.         result->Inputs[0] = &variables[c->OtherEdge - lmWidth+lmRight];
  757.         result->Inputs[1] = &variables[c->OtherEdge - lmWidth+lmLeft];
  758.         result->OrderedCombination[1] = -1;
  759.         result->OrderedCombination[3] = 1;
  760.         break;
  761.  
  762.       case lmCenter:
  763.         switch (whichConstraint) {
  764.           case XConstraint:
  765.           case WidthConstraint:
  766.             //
  767.             // center => (left + right) / 2
  768.             //
  769.             result->Inputs[0] = &variables[0];
  770.             result->Inputs[1] = &variables[2];
  771.             break;
  772.  
  773.           case YConstraint:
  774.           case HeightConstraint:
  775.             //
  776.             // center => (top + bottom) / 2
  777.             //
  778.             result->Inputs[0] = &variables[1];
  779.             result->Inputs[1] = &variables[3];
  780.             break;
  781.         }
  782.         result->OrderedCombination[0] = result->OrderedCombination[1] = TFixed(1,2);
  783.         break;
  784.     }
  785.   }
  786.  
  787.   //
  788.   // now store the constant term as the last of the ordered linear combination
  789.   //
  790.   // we must do this after setting the inputs
  791.   //
  792.   // NOTE: we cannot assume that the constant part is 0, because it might have
  793.   //       been set above
  794.   //
  795.   switch (c->Relationship) {
  796.     case lmAsIs:
  797.       result->OrderedCombination[3] += whichConstraint == WidthConstraint ?
  798.                                                           metrics.Child->Attr.W :
  799.                                                           metrics.Child->Attr.H;
  800.       break;
  801.  
  802.     case lmAbsolute:
  803.     case lmSameAs:
  804.     case lmBelow:
  805.     case lmAbove: {
  806.       int  value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  807.  
  808.       if (c->Relationship == lmAbove)
  809.         value = -value - 1;
  810.  
  811.       else if (c->Relationship == lmBelow)
  812.         value++;
  813.  
  814.       result->OrderedCombination[3] += value;
  815.       break;
  816.     }
  817.  
  818.     case lmPercentOf:
  819.       TFixed  percent = c->Percent;
  820.  
  821.       percent /= 100;
  822.       result->OrderedCombination[0] *= percent;
  823.       result->OrderedCombination[3] *= percent;
  824.  
  825.       switch (c->OtherEdge) {
  826.         case lmWidth:
  827.         case lmHeight:
  828.         case lmCenter:
  829.           result->OrderedCombination[1] *= percent;
  830.           break;
  831.       }
  832.       break;
  833.   }
  834.  
  835.   //
  836.   // now handle cases where the left hand side is width, height, or center
  837.   //
  838.   // this must be done last...
  839.   //
  840.   if (result->Inputs[0])
  841.     index = result->Inputs[1] ? 2 : 1;
  842.  
  843.   else
  844.     index = 0;
  845.  
  846.   switch (c->MyEdge) {
  847.     case lmWidth:
  848.       if (whichConstraint == XConstraint || metrics.Metrics.X.MyEdge == lmRight) {
  849.         //
  850.         // rewrite "right - left + 1 = " as "left = right - (...) + 1"
  851.         //
  852.         for (int i = 0; i < index; i++)
  853.           result->OrderedCombination[i] = -result->OrderedCombination[i];
  854.  
  855.         result->OrderedCombination[3] = -result->OrderedCombination[3];
  856.         result->OrderedCombination[3]++;
  857.         result->Inputs[index] = &metrics.Variables[2];
  858.  
  859.         if (whichConstraint == WidthConstraint)
  860.           output = &metrics.Variables[XConstraint];
  861.  
  862.       } else {
  863.         //
  864.         // rewrite "right - left + 1 = " as "right = left + ... - 1"
  865.         //
  866.         result->Inputs[index] = &metrics.Variables[0];
  867.         result->OrderedCombination[3]--;
  868.  
  869.         Simplify(metrics.Variables[0].DeterminedBy, output, result);
  870.       }
  871.       break;
  872.  
  873.     case lmHeight:
  874.       if (whichConstraint == YConstraint || metrics.Metrics.Y.MyEdge == lmBottom) {
  875.         //
  876.         // rewrite "bottom - top + 1 = " as "top = bottom - (...) + 1"
  877.         //
  878.         for (int i = 0; i < index; i++)
  879.           result->OrderedCombination[i] = -result->OrderedCombination[i];
  880.  
  881.         result->OrderedCombination[3] = -result->OrderedCombination[3];
  882.         result->OrderedCombination[3]++;
  883.         result->Inputs[index] = &metrics.Variables[3];
  884.  
  885.         if (whichConstraint == HeightConstraint)
  886.           output = &metrics.Variables[YConstraint];
  887.  
  888.       } else {
  889.         //
  890.         // rewrite "bottom - top + 1 = " as "bottom = top + ... - 1"
  891.         //
  892.         result->Inputs[index] = &metrics.Variables[1];
  893.         result->OrderedCombination[3]--;
  894.  
  895.         Simplify(metrics.Variables[1].DeterminedBy, output, result);
  896.       }
  897.       break;
  898.  
  899.     case lmCenter:
  900.       TVariable*  input = &metrics.Variables[0];  // left
  901.  
  902.       switch (whichConstraint) {
  903.         case XConstraint:
  904.           //
  905.           // rewrite "(left + right) / 2 = " as "left = -right + 2 * (...)"
  906.           //
  907.           input += 2;  // right
  908.           break;
  909.  
  910.         case YConstraint:
  911.           //
  912.           // rewrite "(top + bottom) / 2 = " as "top = -bottom + 2 * (...)"
  913.           //
  914.           input += 3;  // bottom
  915.           break;
  916.  
  917.         case WidthConstraint:
  918.           //
  919.           // rewrite "(left + right) / 2 = " as "right = -left + 2 * (...)" or
  920.           // "left = -right + 2 * (...)" depending on whether the "x" constraint
  921.           // is left or right
  922.           //
  923.           if (metrics.Metrics.X.MyEdge == lmRight) {
  924.             input += 2;  // right
  925.             output = &metrics.Variables[XConstraint];
  926.           }
  927.           break;
  928.  
  929.         case HeightConstraint:
  930.           //
  931.           // rewrite "(top + bottom) / 2 = " as "bottom = -top + 2 * (...)" or
  932.           // "top = -bottom + 2 * (...)" depending on whether the "y" constraint
  933.           // is top or bottom
  934.           //
  935.           if (metrics.Metrics.Y.MyEdge != lmBottom)
  936.             input++;  // top
  937.  
  938.           else {
  939.             input += 3;  // bottom
  940.             output = &metrics.Variables[XConstraint];
  941.           }
  942.           break;
  943.       }
  944.       result->Inputs[index] = input;
  945.       for (int i = 0; i < index; i++)
  946.         result->OrderedCombination[i] <<= 1;
  947.  
  948.       result->OrderedCombination[3] <<= 1;
  949.       result->OrderedCombination[index] = -1;
  950.       break;
  951.   }
  952.  
  953.   //
  954.   // now set the constraint output
  955.   //
  956.   output->DeterminedBy = result;
  957.   result->Output = output;
  958.  
  959.   //
  960.   // add the constraint to the list of constraints
  961.   //
  962.   result->Next = Constraints;
  963.   Constraints = result;
  964. }
  965.  
  966. void
  967. TLayoutWindow::RemoveConstraints(TChildMetrics& childMetrics)
  968. {
  969.   TVariable*  variable = childMetrics.Variables;
  970.  
  971.   PlanIsDirty = TRUE;
  972.   ClearPlan();
  973.   childMetrics.GeneratedConstraints = FALSE;
  974.  
  975.   for (int i = 0; i < 4; i++) {
  976.     TConstraint*  constraint = variable->DeterminedBy;
  977.  
  978.     variable->Value = 0;
  979.  
  980.     if (constraint) {
  981.       //
  982.       // remove the constraint from the list of constraints
  983.       //
  984.       if (Constraints == constraint)
  985.         Constraints = constraint->Next;
  986.  
  987.       else
  988.         for (TConstraint*  c = Constraints; c->Next; c = c->Next)
  989.           if (c->Next == constraint) {
  990.             c->Next = constraint->Next;
  991.             break;
  992.           }
  993.  
  994.       delete constraint;
  995.       variable->DeterminedBy = 0;
  996.     }
  997.  
  998.     variable++;
  999.   }
  1000. }
  1001.  
  1002. void
  1003. TLayoutWindow::BuildConstraints(TChildMetrics& childMetrics)
  1004. {
  1005.   //
  1006.   // NOTE: to get uniformity we consider the window edges to sit on pixels
  1007.   //       and not between pixels. so our idea of right is left + width - 1
  1008.   //       and not left + width
  1009.   //
  1010.   if (!childMetrics.GeneratedConstraints) {
  1011.     TLayoutConstraint*  c = &childMetrics.Metrics.X;
  1012.  
  1013.     childMetrics.GeneratedConstraints = TRUE;
  1014.  
  1015.     //
  1016.     // "x" can be one of: left, right, center
  1017.     //
  1018.     if (c->Relationship == lmAsIs)
  1019.       if (c->MyEdge == lmLeft)
  1020.         childMetrics.Variables[0].Value = childMetrics.Child->Attr.X;
  1021.  
  1022.       else
  1023.         childMetrics.Variables[2].Value = childMetrics.Child->Attr.X +
  1024.                                           childMetrics.Child->Attr.W - 1;
  1025.  
  1026.     else if (c->Relationship == lmAbsolute && c->MyEdge != lmCenter) {
  1027.       int  value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1028.  
  1029.       childMetrics.Variables[c->MyEdge == lmLeft ? 0 : 2].Value = value;
  1030.  
  1031.     } else {
  1032.       AddConstraint(childMetrics, c, XConstraint);
  1033.     }
  1034.  
  1035.     //
  1036.     // "y" can be one of: top, bottom, center
  1037.     //
  1038.     c = &childMetrics.Metrics.Y;
  1039.  
  1040.     if (c->Relationship == lmAsIs)
  1041.       if (c->MyEdge == lmTop)
  1042.         childMetrics.Variables[1].Value = childMetrics.Child->Attr.Y;
  1043.  
  1044.       else
  1045.         childMetrics.Variables[3].Value = childMetrics.Child->Attr.Y +
  1046.                                           childMetrics.Child->Attr.H - 1;
  1047.  
  1048.     else if (c->Relationship == lmAbsolute && c->MyEdge != lmCenter) {
  1049.       int  value = c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1050.  
  1051.       childMetrics.Variables[c->MyEdge == lmTop ? 1 : 3].Value = value;
  1052.  
  1053.     } else {
  1054.       AddConstraint(childMetrics, c, YConstraint);
  1055.     }
  1056.  
  1057.     //
  1058.     // "width" can be one of: width, right, center
  1059.     //
  1060.     c = &childMetrics.Metrics.Width;
  1061.  
  1062.     if (c->MyEdge == lmRight && (c->Relationship == lmAsIs || c->Relationship == lmAbsolute))
  1063.       childMetrics.Variables[2].Value = c->Relationship == lmAsIs ?
  1064.                                         childMetrics.Child->Attr.X +
  1065.                                         childMetrics.Child->Attr.W - 1 :
  1066.                                         c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1067.  
  1068.     else
  1069.       AddConstraint(childMetrics, c, WidthConstraint);
  1070.  
  1071.     //
  1072.     // "height" can be one of: height, bottom, center
  1073.     //
  1074.     c = &childMetrics.Metrics.Height;
  1075.  
  1076.     if (c->MyEdge == lmBottom && (c->Relationship == lmAsIs || c->Relationship == lmAbsolute))
  1077.       childMetrics.Variables[3].Value = c->Relationship == lmAsIs ?
  1078.                                         childMetrics.Child->Attr.Y +
  1079.                                         childMetrics.Child->Attr.H - 1 :
  1080.                                         c->Units == lmPixels ? c->Value : LayoutUnitsToPixels(c->Value);
  1081.  
  1082.     else
  1083.       AddConstraint(childMetrics, c, HeightConstraint);
  1084.   }
  1085. }
  1086.  
  1087. void
  1088. TLayoutWindow::GetFontHeight()
  1089. {
  1090.   HDC         hDC = ::GetDC(0);
  1091.   HFONT       hFont = (HFONT)HandleMessage(WM_GETFONT);
  1092.   HGDIOBJ     hOldFont;
  1093.   TEXTMETRIC  tm;
  1094.  
  1095.   if (hFont)
  1096.     hOldFont = ::SelectObject(hDC, hFont);
  1097.  
  1098.   ::GetTextMetrics(hDC, &tm);
  1099.   FontHeight = tm.tmHeight;
  1100.  
  1101.   if (hFont)
  1102.     ::SelectObject(hDC, hOldFont);
  1103.  
  1104.   ::ReleaseDC(0, hDC);
  1105. }
  1106.  
  1107. void
  1108. TLayoutWindow::Layout()
  1109. {
  1110.   if (ChildMetrics) {
  1111.     TChildMetrics*  childMetrics;
  1112.  
  1113.     GetFontHeight();
  1114.  
  1115.     //
  1116.     // initialize the parent's variables
  1117.     //
  1118.     Variables[2].Value = ClientSize.cx - 1;
  1119.     Variables[3].Value = ClientSize.cy - 1;
  1120.  
  1121.     if (HasBorder(this)) {
  1122.       int  cxBorder = GetSystemMetrics(SM_CXBORDER);
  1123.       int  cyBorder = GetSystemMetrics(SM_CYBORDER);
  1124.  
  1125.       Variables[0].Value = -cxBorder;
  1126.       Variables[1].Value = -cyBorder;
  1127.       Variables[2].Value += cxBorder;
  1128.       Variables[3].Value += cyBorder;
  1129.  
  1130.     } else {
  1131.       Variables[0].Value = 0;
  1132.       Variables[1].Value = 0;
  1133.     }
  1134.  
  1135.     //
  1136.     // Rebuild layout plan if necessary
  1137.     //
  1138.     if (PlanIsDirty) {
  1139.       PlanIsDirty = FALSE;
  1140.  
  1141.       for (childMetrics = ChildMetrics; childMetrics;
  1142.            childMetrics = childMetrics->Next)
  1143.         BuildConstraints(*childMetrics);
  1144.  
  1145.       BuildPlan();
  1146.     }
  1147.  
  1148.     //
  1149.     // Use the plan to calculate actual child window position values
  1150.     //
  1151.     ExecutePlan();
  1152.  
  1153.     //
  1154.     // now do the actual resizing of the windows
  1155.     //
  1156.     for (childMetrics = ChildMetrics; childMetrics; childMetrics = childMetrics->Next) {
  1157.       TWindow*    win = childMetrics->Child;
  1158.       TVariable*  variables = childMetrics->Variables;
  1159.  
  1160.       if (win->HWindow) {
  1161.         win->SetWindowPos(
  1162.           0,
  1163.           variables[0].Value,
  1164.           variables[1].Value,
  1165.           variables[2].Value - variables[0].Value + 1,
  1166.           variables[3].Value - variables[1].Value + 1,
  1167.           SWP_NOZORDER | SWP_NOACTIVATE
  1168.         );
  1169.  
  1170.       } else {
  1171.         win->Attr.X = variables[0].Value;
  1172.         win->Attr.Y = variables[1].Value;
  1173.         win->Attr.W = variables[2].Value - variables[0].Value + 1;
  1174.         win->Attr.H = variables[3].Value - variables[1].Value + 1;
  1175.       }
  1176.     }
  1177.   }
  1178. }
  1179.