home *** CD-ROM | disk | FTP | other *** search
/ C Programming Starter Kit 2.0 / SamsPublishing-CProgrammingStarterKit-v2.0-Win31.iso / bc45 / owlsrc.pak / LAYOUTWI.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-24  |  32.8 KB  |  1,183 lines

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