home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / OWLSRC.PAK / GADGETWI.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  43.5 KB  |  1,645 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1992, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.46  $
  6. //
  7. // Implementation of class TGadgetWindow and TGadgetWindowFont.
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_GADGET_H)
  11. # include <owl/gadget.h>
  12. #endif
  13. #if !defined(OWL_GADGETWI_H)
  14. # include <owl/gadgetwi.h>
  15. #endif
  16. #if !defined(OWL_FRAMEWIN_H)
  17. # include <owl/framewin.h>
  18. #endif
  19. #if !defined(OWL_DECFRAME_H)
  20. # include <owl/decframe.h>
  21. #endif
  22. #if !defined(OWL_CELARRAY_H)
  23. # include <owl/celarray.h>
  24. #endif
  25. #if !defined(OWL_TOOLTIP_H)
  26. # include <owl/tooltip.h>
  27. #endif
  28. #if !defined(WINSYS_UIMETRIC_H)
  29. # include <winsys/uimetric.h>
  30. #endif
  31. #include <stdio.h>
  32.  
  33. //
  34. // Diagnostic group for gadgets & gadget windows
  35. //
  36. OWL_DIAGINFO;
  37. DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlGadget, 1, 0);
  38. DIAG_DECLARE_GROUP(OwlCmd);
  39.  
  40. const uint GadgetWindowTimerID = 0xBACA;
  41. const uint GadgetWindowTimeOut = 1000;
  42.  
  43. IMPLEMENT_CASTABLE1(TGadgetWindow, TWindow);
  44.  
  45. DEFINE_RESPONSE_TABLE1(TGadgetWindow, TWindow)
  46.   EV_WM_LBUTTONDOWN,
  47.   EV_WM_LBUTTONUP,
  48.   EV_WM_LBUTTONDBLCLK,
  49.   EV_WM_RBUTTONDOWN,
  50.   EV_WM_RBUTTONUP,
  51.   EV_WM_MOUSEMOVE,
  52.   EV_WM_WINDOWPOSCHANGING,
  53.   EV_WM_SYSCOLORCHANGE,
  54.   EV_WM_CREATETOOLTIP,
  55. END_RESPONSE_TABLE;
  56.  
  57. //
  58. // Calculate & return the height in pixels of a given number of points on
  59. // the screen.
  60. //
  61. static inline int heightInPixels(int pointSize)
  62. {
  63.   TScreenDC  dc;
  64.   return MulDiv(-pointSize, dc.GetDeviceCaps(LOGPIXELSY), 72);
  65. }
  66.  
  67. //
  68. // Construct a specialized gadget window font. Uses a specific font & size
  69. // appropriate for UI elements.
  70. //
  71. TGadgetWindowFont::TGadgetWindowFont(int pointSize, bool bold, bool italic)
  72. :
  73.   TFont(TDefaultGUIFont().GetFaceName().c_str(),
  74.         heightInPixels(pointSize),
  75.         0, 0, 0,
  76.         bold ? FW_BOLD : FW_NORMAL,
  77.         TDefaultGUIFont().GetPitchAndFamily(),
  78.         uint8(italic))
  79. /*
  80.   TFont("MS Sans Serif", heightInPixels(pointSize), 0, 0, 0,
  81.         bold ? FW_BOLD : FW_NORMAL,
  82.         uint8(VARIABLE_PITCH | FF_SWISS), uint8(italic))
  83. */        
  84. {
  85. }
  86.  
  87. //
  88. // Construct a specialized gadget window font. Base it on an existing font
  89. // object. 
  90. //
  91. TGadgetWindowFont::TGadgetWindowFont(const TFont& font)
  92. :
  93.   TFont(font)
  94. {
  95. }
  96.  
  97. //----------------------------------------------------------------------------
  98.  
  99. //
  100. // Construct a gadget window.
  101. //
  102. TGadgetWindow::TGadgetWindow(TWindow*       parent,
  103.                              TTileDirection direction,
  104.                              TFont*         font,
  105.                              TModule*       module)
  106.               :TimerID(0), WantTimer(false)
  107. {
  108.   // Initialize virtual base, in case the derived-most used default ctor
  109.   //
  110.   TWindow::Init(parent, 0, module);
  111.  
  112.   // Make sure we don't paint or erase over any child windows
  113.   //
  114.   Attr.Style |= WS_CLIPCHILDREN;
  115.  
  116.   // Older, non 3d UI uses a plain thin border. 3d UI relies on
  117.   // neighbor window edges.
  118.   //
  119.   if (!TSystem::Has3dUI())
  120.     Attr.Style |= WS_BORDER;
  121.  
  122.   Capture = 0;
  123.   AtMouse = 0;
  124.   Font = font ? font : new TGadgetWindowFont(6);
  125.  
  126.   Direction = direction;
  127.  
  128.   // Shrink wrap the narrow axis
  129.   //
  130.   ShrinkWrapWidth  = Direction == Vertical;  // As narrow as possible
  131.   ShrinkWrapHeight = Direction != Vertical;  // As short as possible
  132.  
  133.   HintMode = PressHints;
  134.  
  135.   WideAsPossible = 0;  // Number of gadgets with "WideAsPossible" set
  136.   DirtyLayout = true;
  137.   Gadgets = 0;
  138.   NumGadgets = 0;
  139.  
  140.   // Compute the font height
  141.   //
  142.   TEXTMETRIC  metrics;
  143.   Font->GetTextMetrics(metrics);
  144.   FontHeight = metrics.tmHeight + metrics.tmExternalLeading;
  145.  
  146.   // Choose a default height based on the height of the font plus room
  147.   // for a top and bottom border.
  148.   //
  149.   Attr.H = FontHeight;
  150.  
  151.   if (Attr.Style & WS_BORDER)
  152.     Attr.H += 2 * TUIMetric::CyBorder;
  153.  
  154.   SetBkgndColor(TColor::Sys3dFace);
  155.  
  156.   SharedCels = 0;
  157.   Tooltip = 0;
  158.   WantTooltip = false;
  159.  
  160.   // Rectangular layout members
  161.   //
  162.   RowWidth  = 0;  // Don't know yet? Set externally. Never let get smaller than widest gadget
  163.   RowMargin = 4;
  164. }
  165.  
  166. //
  167. // Destruct this gadget window. Deletes the gadgets, the font and the shared
  168. // cel array
  169. //
  170. TGadgetWindow::~TGadgetWindow()
  171. {
  172.   TGadget* gadget = Gadgets;
  173.  
  174.   while (gadget) {
  175.     TGadget*  tmp = gadget;
  176.     gadget = gadget->Next;
  177.     delete tmp;
  178.   }
  179.  
  180.   delete Font;
  181.   delete SharedCels;
  182. }
  183.  
  184. //
  185. // Respond to WM_SYSCOLORCHANGE to let the gadgets update their UI colors, and
  186. // to let this gadget window update its background color.
  187. //
  188. // OBSOLETE: retained for compatibility. New TWindow, TColor & other UI
  189. // support makes this unnecessary. SysColorChange() is still invoked on each
  190. // gadget.
  191. //
  192. void
  193. TGadgetWindow::EvSysColorChange()
  194. {
  195.   for (TGadget* g = Gadgets; g; g = g->NextGadget())
  196.     g->SysColorChange();
  197. }
  198.  
  199. //
  200. // During idle time, iterates over the Gadgets invoking their CommandEnable()
  201. // member function. Also detects lost mouse up for fly-over hint mode.
  202. //
  203. bool
  204. TGadgetWindow::IdleAction(long idleCount)
  205. {
  206.   if (idleCount == 0) {
  207.  
  208.     // See if we missed a mouse move & still need to send a MouseLeave to a
  209.     // gadget
  210.     //
  211.     if (AtMouse) {
  212.       TPoint crsPoint;
  213.       GetCursorPos(crsPoint);
  214.       if (WindowFromPoint(crsPoint) != GetHandle())
  215.         HandleMessage(WM_MOUSEMOVE, 0, MkParam2(-1,-1));  // nowhere
  216.     }
  217.   }
  218.  
  219.   // Let the gadgets do command enabling if they need to
  220.   //
  221.   for (TGadget* g = Gadgets; g; g = g->NextGadget())
  222.     g->IdleAction(idleCount);
  223.  
  224.   // Chain to base implementation
  225.   //
  226.   return TWindow::IdleAction(idleCount);
  227. }
  228.  
  229. //
  230. // Inform GadgetWindow to start a Timer notification.
  231. //
  232. // NOTE: TGadgetWindow does not catch the WM_TIMER event. The purpose of
  233. //       the timer message it to cause message dispatching which eventually
  234. //       leads to an 'IdleAction' call.
  235. //
  236. bool
  237. TGadgetWindow::EnableTimer()
  238. {
  239.   // Set flag and let 'SetupWindow' create timer
  240.   //
  241.   if (!GetHandle()) {
  242.     WantTimer = true;
  243.     return true;
  244.   }
  245.  
  246.   // Set a timer if one's not enabled already
  247.   //
  248.   if (!TimerID)
  249.     TimerID = SetTimer(GadgetWindowTimerID, GadgetWindowTimeOut);
  250.  
  251.   return TimerID != 0;
  252. }
  253.  
  254. //
  255. // Simple set accessor for the shrink wrap flags
  256. //
  257. void
  258. TGadgetWindow::SetShrinkWrap(bool shrinkWrapWidth, bool shrinkWrapHeight)
  259. {
  260.   ShrinkWrapWidth = shrinkWrapWidth;
  261.   ShrinkWrapHeight = shrinkWrapHeight;
  262. }
  263.  
  264. //
  265. // Called by a gadget that wants to capture the mouse
  266. //
  267. // Gadgets always get notified when a left button down occurs within their
  268. // bounding rectangle. If you want mouse drags and a mouse up then you
  269. // need to capture the mouse
  270. //
  271. // Fails if already captured
  272. //
  273. bool
  274. TGadgetWindow::GadgetSetCapture(TGadget& gadget)
  275. {
  276.   if (Capture) {
  277.     return false;
  278.   }
  279.   else {
  280.     Capture = &gadget;
  281.     SetCapture();
  282.     return true;
  283.   }
  284. }
  285.  
  286. //
  287. // Called by a gadget that wants to release the mouse capture
  288. //
  289. // Ignores other gadgets
  290. //
  291. void
  292. TGadgetWindow::GadgetReleaseCapture(TGadget& gadget)
  293. {
  294.   if (&gadget == Capture) {
  295.     Capture = 0;
  296.     ReleaseCapture();
  297.   }
  298. }
  299.  
  300. //
  301. // Simulate menu select & idle messages to our parent so that it can display
  302. // hint text the same way it does for menu commands
  303. //
  304. void
  305. TGadgetWindow::SetHintCommand(int id)
  306. {
  307.   // Find our ancestor decorated frame [which potentially has a statusbar
  308.   // to show hint messages]
  309.   //
  310.   TWindow* parent= GetParentO();
  311.   TDecoratedFrame* frame= parent ?
  312.                           TYPESAFE_DOWNCAST(parent, TDecoratedFrame) : 0;
  313.   while (parent && !frame) {
  314.     parent = parent->GetParentO();
  315.     if (parent)
  316.       frame = TYPESAFE_DOWNCAST(parent, TDecoratedFrame);
  317.   }
  318.  
  319.   // Forward command id to frame via fake WM_MENUSELECT messages
  320.   //
  321.   if (frame) {
  322.     if (id > 0) {
  323.       frame->HandleMessage(WM_MENUSELECT, id, 0);
  324.     }
  325.     else {
  326.       // Send a menuselect w/ flags==0xFFFF and hMenu==0 to indicate end
  327.       //
  328. #if defined(BI_PLAT_WIN32)
  329.       frame->HandleMessage(WM_MENUSELECT, 0xFFFF0000, 0); // flags+item, hmenu
  330. #else
  331.       frame->HandleMessage(WM_MENUSELECT, 0, 0x0000FFFFL);// item, hmenu+flags
  332. #endif
  333.     }
  334.     frame->HandleMessage(WM_ENTERIDLE, MSGF_MENU);
  335.   }
  336. }
  337.  
  338. //
  339. // Setter for the margins property. Initiates a layout session automatically
  340. //
  341. void
  342. TGadgetWindow::SetMargins(TMargins& margins)
  343. {
  344.   Margins = margins;
  345.   LayoutSession();
  346. }
  347.  
  348. //
  349. // Convert layout units to pixels using this gadget window's Font
  350. //
  351. // A layout unit is defined by dividing the window font "em" into 8
  352. // vertical and 8 horizontal segments
  353. //
  354. int
  355. TGadgetWindow::LayoutUnitsToPixels(int units)
  356. {
  357.   const long  unitsPerEM = 8;
  358.  
  359.   return int((long(units) * FontHeight + unitsPerEM / 2) / unitsPerEM);
  360. }
  361.  
  362. //
  363. // Perform a gadget layout session, possibly changing the size of this window.
  364. //
  365. // Default behavior is to tile the Gadgets & invalidate the area changed.
  366. //
  367. // Typically initiated by a change in margins, adding/deleting Gadgets, or a
  368. // Gadget changing size
  369. //
  370. void
  371. TGadgetWindow::LayoutSession()
  372. {
  373.   if (GetHandle())
  374.     InvalidateRect(TileGadgets());
  375. }
  376.  
  377. //
  378. // Get the desired size for this gadget window by performing a trial layout of
  379. // the gadgets without touching them.
  380. //
  381. // If shrink wrapping was requested returns the size needed to accomodate
  382. // the borders, margins, and the widest/highest Gadget; otherwise the
  383. // current width/height are returned
  384. //
  385. // For Rectangular layout it is assumed that the EndOfRow gadgets are already
  386. // Set.
  387. //
  388. // Override this member function if you want to leave extra room for a
  389. // specific look (e.g. separator line, raised, ...)
  390. //
  391. // If you override GetDesiredSize() you may also need to override
  392. // GetInnerRect()
  393. //
  394. void
  395. TGadgetWindow::GetDesiredSize(TSize& size)
  396. {
  397.   TLayoutInfo layout(NumGadgets);
  398.   if (ShrinkWrapWidth || ShrinkWrapHeight)
  399.     LayoutGadgets(Direction, layout);
  400.  
  401.   // Calculate the X dimension for both shrinkWrap and externally set
  402.   //
  403.   if (ShrinkWrapWidth) {
  404.     size.cx = layout.DesiredSize.cx;
  405.   }
  406.   else {
  407.     size.cx = Attr.W;  // Not shrink wrapped, just use set size
  408.   }
  409.  
  410.   // Calculate the Y dimension for both shrinkWrap and externally set
  411.   // Always shrinkwrap rectangular layout vertically
  412.   //
  413.   if (ShrinkWrapHeight) {
  414.     if (NumGadgets == 0) {
  415.       size.cy = 0;
  416.     }
  417.     else {
  418.       size.cy = layout.DesiredSize.cy;
  419.     }
  420.   }
  421.   else {
  422.     size.cy = Attr.H;  // Not shrink wrapped, just use set size
  423.   }
  424. }
  425.  
  426. //
  427. // Update the Size in Attr.W and Attr.H to match that obtained using
  428. // GetDesireSize for each dimension that is shrunk wrapped
  429. //
  430. void
  431. TGadgetWindow::UseDesiredSize()
  432. {
  433.   if (ShrinkWrapWidth || ShrinkWrapHeight) {
  434.     TSize  size;
  435.     GetDesiredSize(size);
  436.  
  437.     if (ShrinkWrapWidth)
  438.       Attr.W = size.cx;
  439.     if (ShrinkWrapHeight)
  440.       Attr.H = size.cy;
  441.   }
  442. }
  443.  
  444. //
  445. // Override TWindow member function and choose initial size if shrink
  446. // wrapping was requested
  447. //
  448. bool
  449. TGadgetWindow::Create()
  450. {
  451.   UseDesiredSize();
  452.   return TWindow::Create();
  453. }
  454.  
  455. //
  456. // Set direction, & default shrink wrap flags. If already created, then also
  457. // adjust shrink wrap dimension & re-layout. Will change this window's size
  458. //
  459. void
  460. TGadgetWindow::SetDirection(TTileDirection direction)
  461. {
  462.   if (Direction != direction || DirtyLayout) {
  463.  
  464.     // Swap margin's and ShrinkWrap's X & Y axis
  465.     //
  466.     if (Direction != direction) {
  467.       // Margin swapping seems to not be used in the latest UIs
  468.       //
  469. #if 0
  470.       int t = Margins.Left; Margins.Left = Margins.Top; Margins.Top = t;
  471.       t = Margins.Right; Margins.Right = Margins.Bottom; Margins.Bottom = t;
  472. #endif
  473.       // NOTE: a limitation. Passing thru Rectangular will mess this swap up
  474.       //
  475.       bool sww = ShrinkWrapWidth;
  476.       ShrinkWrapWidth = ShrinkWrapHeight;
  477.       ShrinkWrapHeight = sww;
  478.  
  479.       Direction = direction;
  480.       DirtyLayout = true;
  481.     }
  482.  
  483.     // Get & use the new size & relayout if created
  484.     //
  485.     UseDesiredSize();
  486.     LayoutSession();
  487.   }
  488. }
  489.  
  490. //
  491. // Set the maximum width for each row used for rectangular layout.
  492. // LayoutSession or SetDirection must be called for changes to take effect
  493. //
  494. void
  495. TGadgetWindow::SetRectangularDimensions(int width, int /*height*/, int rowMargin)
  496. {
  497.   if (width != RowWidth || rowMargin >= 0 && rowMargin != RowMargin) {
  498.     RowWidth = width;
  499.     if (rowMargin >= 0)
  500.       RowMargin = rowMargin;
  501.     if (Direction == Rectangular)
  502.       DirtyLayout = true;
  503.   }
  504. }
  505.  
  506. //
  507. // Compute the area inside of the borders and margins
  508. //
  509. // Override this if you want to leave extra room for a specific look
  510. // (e.g. separator line, raised, ...)
  511. //
  512. // If you override GetInnerRect() you will probably also need to override
  513. // GetDesiredSize()
  514. //
  515. void
  516. TGadgetWindow::GetInnerRect(TRect& rect)
  517. {
  518.   int left, right, top, bottom;
  519.   GetMargins(Margins, left, right, top, bottom);
  520.  
  521.   if (Attr.W != 0) {
  522.     rect.left = left;
  523.     rect.right = Attr.W - right;
  524.     if (Attr.Style & WS_BORDER)
  525.       rect.right -= 2 * TUIMetric::CyBorder;
  526.   }
  527.   else {
  528.     rect.left = 0;
  529.     rect.right = 0;
  530.   }
  531.   if (Attr.H != 0) {
  532.     rect.top = top;
  533.     rect.bottom = Attr.H - bottom;
  534.     if (Attr.Style & WS_BORDER)
  535.       rect.bottom -= 2 * TUIMetric::CxBorder;
  536.   }
  537.   else {
  538.     rect.top = 0;
  539.     rect.bottom = 0;
  540.   }
  541. }
  542.  
  543. //
  544. // Virtual called by TileGadgets() for each gadget in order to give derived
  545. // classes a chance to modify the gadget positioning. Default is to overlap
  546. // adjacent plain-bordered gadgets
  547. //
  548. void
  549. TGadgetWindow::PositionGadget(TGadget* previous, TGadget* next, TPoint& origin)
  550. {
  551.   // Overlap the button borders
  552.   //
  553.   if (previous->GetBorderStyle() == TGadget::Plain &&
  554.       next->GetBorderStyle() == TGadget::Plain)
  555.     if (Direction == Horizontal)
  556.       origin.x -= TUIMetric::CxBorder;
  557.  
  558.     else
  559.       origin.y -= TUIMetric::CyBorder;
  560. }
  561.  
  562. //
  563. // Layout & then tile the Gadgets in the current direction
  564. //
  565. // Calls member function PositionGadget() for each gadget to give derived
  566. // classes an opportunity to adjust the spacing between Gadgets
  567. //
  568. TRect
  569. TGadgetWindow::TileGadgets()
  570. {
  571.   TLayoutInfo layout(NumGadgets);
  572.   LayoutGadgets(Direction, layout);
  573.  
  574.   TRect invalidRect(0,0,0,0);
  575.   int   i = 0;
  576.   for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next, i++) {
  577.     TRect  originalBounds = gadget->GetBounds();
  578.     TRect  bounds = layout.GadgetBounds[i];
  579.     if (originalBounds != bounds) {
  580.       gadget->SetBounds(bounds);
  581.       if (originalBounds.TopLeft() != TPoint(0, 0))
  582.         invalidRect |= originalBounds;
  583.       invalidRect |= bounds;
  584.     }
  585.   }
  586.   DirtyLayout = false;
  587.   return invalidRect;
  588. }
  589.  
  590. //
  591. // Helper for LayoutGadgets() to calculate horizontal tiling
  592. //
  593. void
  594. TGadgetWindow::LayoutHorizontally(TLayoutInfo& layout)
  595. {
  596.   TRect  innerRect;
  597.   GetInnerRect(innerRect);
  598.  
  599.   int leftM, rightM, topM, bottomM;
  600.   GetMargins(Margins, leftM, rightM, topM, bottomM);
  601.  
  602.   layout.DesiredSize = TSize(0,0);
  603.   layout.GadgetBounds = new TRect[NumGadgets];
  604.  
  605.   // First pass tally the width of all gadgets that don't have "WideAsPossible"
  606.   // set if any have it seet so that we can divide up the extra width
  607.   //
  608.   // NOTE: we must also take into account any adjustments to the gadget spacing
  609.   //
  610.   int  wideAsPossibleWidth;
  611.   if (WideAsPossible) {
  612.     int  width = 0;
  613.     for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) {
  614.       if (!gadget->WideAsPossible) {
  615.         TSize desiredSize(0, 0);
  616.  
  617.         gadget->GetDesiredSize(desiredSize);
  618.         width += desiredSize.cx;
  619.       }
  620.  
  621.       if (gadget->Next) {
  622.         TPoint spacing(0, 0);
  623.  
  624.         PositionGadget(gadget, gadget->Next, spacing);
  625.         width += spacing.x;
  626.       }
  627.     }
  628.     wideAsPossibleWidth = max((innerRect.Width() - width) / WideAsPossible, 0U);
  629.   }
  630.   else
  631.     wideAsPossibleWidth = 0;
  632.  
  633.   // Now tile all the gadgets
  634.   //
  635.   int x = leftM;
  636.   int y = topM;
  637.   int h = 0;
  638.   int i = 0;
  639.  
  640.  
  641.   // Pass 1: calculate the gadget sizes and row height
  642.   //
  643.   TGadget* gadget;
  644.   for (gadget = Gadgets; gadget; gadget = gadget->Next, i++) {
  645.     TSize  desiredSize(0, 0);
  646.     gadget->GetDesiredSize(desiredSize);
  647.  
  648.     // Stash away the desiredSize for the next pass
  649.     //
  650.     layout.GadgetBounds[i].left = desiredSize.cx;
  651.     layout.GadgetBounds[i].top = desiredSize.cy;
  652.     h = max(h, (int)desiredSize.cy);
  653.   }
  654.  
  655.   // Pass 2: place the gadget in the row, centered vertically
  656.   //
  657.   i = 0;
  658.   for (gadget = Gadgets; gadget; gadget = gadget->Next, i++) {
  659.     TRect  bounds = gadget->GetBounds();
  660.     TRect  originalBounds(bounds);
  661.  
  662.     // Recover desired size from where we stashed it
  663.     //
  664.     TSize desiredSize(layout.GadgetBounds[i].left, layout.GadgetBounds[i].top);
  665.  
  666.     bounds.left = x;
  667.     bounds.top = y + (h - desiredSize.cy) / 2;   // Center vertically
  668.     if (gadget->WideAsPossible)
  669.       bounds.right = bounds.left + max(wideAsPossibleWidth, (int)desiredSize.cx);
  670.     else
  671.       bounds.right = bounds.left + desiredSize.cx;
  672.     bounds.bottom = bounds.top + desiredSize.cy;
  673.  
  674.     layout.GadgetBounds[i] = bounds;
  675.     x += bounds.Width();
  676.  
  677.     if (gadget->Next) {
  678.       TPoint  origin(x, 0);
  679.  
  680.       PositionGadget(gadget, gadget->Next, origin);
  681.       x = origin.x;
  682.     }
  683.  
  684.     // Update gadget window's cumulative desired size
  685.     //
  686.     layout.DesiredSize.cx = max(bounds.right+rightM, layout.DesiredSize.cx);
  687.     layout.DesiredSize.cy = max(bounds.bottom+bottomM, layout.DesiredSize.cy);
  688.   }
  689. }
  690.  
  691. //
  692. // Helper for LayoutGadgets() to calculate vertical tiling
  693. //
  694. void
  695. TGadgetWindow::LayoutVertically(TLayoutInfo& layout)
  696. {
  697.   TRect  innerRect;
  698.   GetInnerRect(innerRect);
  699.  
  700.   int leftM, rightM, topM, bottomM;
  701.   GetMargins(Margins, leftM, rightM, topM, bottomM);
  702.  
  703.   layout.DesiredSize = TSize(0,0);
  704.   layout.GadgetBounds = new TRect[NumGadgets];
  705.  
  706.   // Now tile all the gadgets
  707.   //
  708.   int y = topM;
  709.   int x = leftM;
  710.   int w = 0;
  711.   int i = 0;
  712.  
  713.   // Pass 1: calculate gadget sizes and column width
  714.   //
  715.   TGadget* gadget;
  716.   for (gadget = Gadgets; gadget; gadget = gadget->Next, i++) {
  717.     TSize  desiredSize(0, 0);
  718.     gadget->GetDesiredSize(desiredSize);
  719.  
  720.     // Stash away the DesiredSize for the next pass
  721.     //
  722.     layout.GadgetBounds[i].left = desiredSize.cx;
  723.     layout.GadgetBounds[i].top = desiredSize.cy;
  724.     w = max(w, (int)desiredSize.cx);
  725.   }
  726.  
  727.   // Pass 2: place the gadget in the column, centered horizontally
  728.   //
  729.   i = 0;
  730.   for (gadget = Gadgets; gadget; gadget = gadget->Next, i++) {
  731.     TRect  bounds = gadget->GetBounds();
  732.     TRect  originalBounds(bounds);
  733.  
  734.     // Recover desired size from where we stashed it
  735.     //
  736.     TSize desiredSize(layout.GadgetBounds[i].left, layout.GadgetBounds[i].top);
  737.  
  738.     bounds.top = y;
  739.     bounds.bottom = bounds.top + desiredSize.cy;
  740.     bounds.left = x + (w - desiredSize.cx) / 2;   // Center horizontally
  741.     bounds.right = bounds.left + desiredSize.cx;
  742.  
  743.     layout.GadgetBounds[i] = bounds;
  744.  
  745.     y += bounds.Height();
  746.  
  747.     if (gadget->Next) {
  748.       TPoint  origin(0, y);
  749.  
  750.       PositionGadget(gadget, gadget->Next, origin);
  751.       y = origin.y;
  752.     }
  753.  
  754.     // Update gadget window's desired size
  755.     //
  756.     layout.DesiredSize.cx = max(bounds.right+rightM, layout.DesiredSize.cx);
  757.     layout.DesiredSize.cy = max(bounds.bottom+bottomM, layout.DesiredSize.cy);
  758.   }
  759. }
  760.  
  761. //
  762. // Assign top & bottoms of all controls on this row (i.e. from rowStart to
  763. // lastBreak). If !lastBreak, go to the end of the list.
  764. //
  765. void TGadgetWindow::FinishRow(int istart, TGadget* rowStart, TGadget* lastBreak,
  766.                               int rowTop, TLayoutInfo& layout, int& rowHeight)
  767. {
  768.    int j;
  769.    TGadget* g;
  770.    rowHeight = 0;
  771.  
  772.    for (j = istart, g = rowStart; g; g = g->Next, j++) {
  773.      rowHeight = max(rowHeight, (int)layout.GadgetBounds[j].bottom);
  774.      if (g == lastBreak)
  775.        break;
  776.    }
  777.    for (j = istart, g = rowStart; g; g = g->Next, j++) {
  778.      // bounds.bottom has DesiredSize.cy
  779.      layout.GadgetBounds[j].top = rowTop + (rowHeight - layout.GadgetBounds[j].bottom) / 2;
  780.      layout.GadgetBounds[j].bottom = layout.GadgetBounds[j].top + layout.GadgetBounds[j].bottom;
  781.      layout.DesiredSize.cy = max(layout.GadgetBounds[j].bottom, layout.DesiredSize.cy);
  782.      if (g == lastBreak)
  783.        break;
  784.    }
  785. }
  786.  
  787. //
  788. // Helper for LayoutGadgets() to calculate rectangular 2D tiling
  789. //
  790. void
  791. TGadgetWindow::LayoutRectangularly(TLayoutInfo& layout)
  792. {
  793.   TRect  innerRect;
  794.   GetInnerRect(innerRect);
  795.  
  796.   layout.DesiredSize = TSize(0,0);
  797.   layout.GadgetBounds = new TRect[NumGadgets];
  798.  
  799.   int       leftM, rightM, topM, bottomM;
  800.   GetMargins(Margins, leftM, rightM, topM, bottomM);
  801.  
  802.   // Now tile all the gadgets. Assume no wide-as-possibles.
  803.   //
  804.   int x = leftM;
  805.   int y = topM;
  806.   int right = RowWidth - rightM;  // Base right margin limit on RowWidth
  807.  
  808.   // If any controls are wider than right margin, push out the right margin.
  809.   //
  810.   TGadget* gadget;
  811.   for (gadget = Gadgets; gadget; gadget = gadget->Next) {
  812.     TSize  desiredSize;
  813.     gadget->GetDesiredSize(desiredSize);
  814.     right = max(right, (int)(x + desiredSize.cx + rightM));
  815.   }
  816.  
  817.   // Scan gadgets, positioning & placing all of the EndOfRow flags
  818.   //
  819.   TGadget* rowStart;        // Tracks the first gadget in the row
  820.   TGadget* lastBreak;       // Tracks the last gadget in the row
  821.   bool     contRow = false; // Working on a group continuation row
  822.   bool     contBreak = false; // Finished group on continuation row
  823.   int i = 0;
  824.   int istart = 0;
  825.   int iend = 0;             // Tracks the last visible gadget in the row
  826.   int ibreak = 0;
  827.   int rowHeight;
  828.  
  829.   rowStart = Gadgets;
  830.   lastBreak = Gadgets;
  831.   for (gadget =  Gadgets; gadget; gadget = gadget->Next, i++) {
  832.     gadget->SetEndOfRow(false);
  833.  
  834.     //
  835.     TSize  desiredSize;
  836.     gadget->GetDesiredSize(desiredSize);
  837.  
  838.     TRect  bounds = gadget->GetBounds();
  839.     TRect  originalBounds(bounds);
  840.  
  841.     // Do the horizontal layout of this control
  842.     //
  843.     bounds.left = x;
  844.     bounds.right = bounds.left + desiredSize.cx;
  845.  
  846.     // Store gadget's height in bottom, so we can calculate the row height
  847.     // later
  848.     //
  849.     bounds.top = 0;
  850.     bounds.bottom = desiredSize.cy;
  851.  
  852.     // If too big or a new group on a group continue row, (& is not the first
  853.     // & is visible) then back up to iend & reset to lastBreak+1
  854.     //
  855.     if ((bounds.right > right || contBreak) &&
  856.         gadget != rowStart && gadget->IsVisible()) {
  857.  
  858.       lastBreak->SetEndOfRow(true);
  859.  
  860.       // Update gadget window's desired size
  861.       //
  862.       layout.DesiredSize.cx =
  863.         max(layout.GadgetBounds[iend].right+rightM, layout.DesiredSize.cx);
  864.  
  865.       // Do the vertical layout of this row
  866.       //
  867.       FinishRow(istart, rowStart, lastBreak, y, layout, rowHeight);
  868.  
  869.       contRow = lastBreak->IsVisible();  // Next row is a group continuation
  870.       x = leftM;
  871.       y += rowHeight;
  872.       if (!contRow)
  873.         y += RowMargin;
  874.  
  875.       gadget = lastBreak;       // will get bumped to Next by for incr
  876.       i = ibreak;               // so will this
  877.  
  878.       rowStart = lastBreak = gadget->Next;  // Begin next row
  879.       istart = i+1;
  880.       contBreak = false;
  881.       continue;
  882.     }
  883.  
  884.     layout.GadgetBounds[i] = bounds;
  885.  
  886.     // Advance the break and end cursors.
  887.     //
  888.     if (gadget->IsVisible()) {
  889.       if (lastBreak->IsVisible()) {  // advance both if in the first group
  890.         iend = i;
  891.         lastBreak = gadget;
  892.         ibreak = i;
  893.       }
  894.       else if (!gadget->Next || !gadget->Next->IsVisible()) {
  895.         // advance end if next is a break.
  896.         iend = i;
  897.       }
  898.     }
  899.     else { // advance last break if this gadget is a break
  900.       lastBreak = gadget;
  901.       ibreak = i;
  902.       if (contRow)          // This invisible gadget signifies end of group
  903.         contBreak = true;
  904.     }
  905.  
  906.     x += bounds.Width();
  907.  
  908.     if (gadget->Next) {
  909.       TPoint  origin(x, 0);
  910.  
  911.       PositionGadget(gadget, gadget->Next, origin);
  912.       x = origin.x;
  913.     }
  914.   }
  915.  
  916.   // Update gadget window's desired size
  917.   //
  918.   layout.DesiredSize.cx =
  919.     max(layout.GadgetBounds[iend].right+rightM, layout.DesiredSize.cx);
  920.  
  921.   // Do the vertical layout of the last row & add in bottom margin
  922.   //
  923.   FinishRow(istart, rowStart, 0, y, layout, rowHeight);
  924.   layout.DesiredSize.cy += bottomM;
  925. }
  926.  
  927. //
  928. // Calculate the layout of the Gadgets in a given direction
  929. //
  930. void
  931. TGadgetWindow::LayoutGadgets(TTileDirection dir, TLayoutInfo& layout)
  932. {
  933.   switch (dir) {
  934.     case Horizontal:
  935.       LayoutHorizontally(layout);
  936.       break;
  937.     case Vertical:
  938.       LayoutVertically(layout);
  939.       break;
  940.     default:
  941.     //case Rectangular:
  942.       CHECK(dir == Rectangular);
  943.       LayoutRectangularly(layout);
  944.       break;
  945.   }
  946. }
  947.  
  948. //
  949. // Respond to a gadget notifying us that it has changed size, by re-laying out
  950. // the gadgets
  951. //
  952. void
  953. TGadgetWindow::GadgetChangedSize(TGadget&)
  954. {
  955.   LayoutSession();
  956. }
  957.  
  958. //
  959. // Insert a Gadget.
  960. //
  961. // Caller needs to also call LayoutSession() after inserting gadgets if
  962. // this window has already been created
  963. //
  964. void
  965. TGadgetWindow::Insert(TGadget& gadget, TPlacement placement, TGadget* sibling)
  966. {
  967.   TGadgetList::Insert(gadget, placement, sibling);
  968.  
  969.   // Compute the maxWidth and maxHeight of the gadgetwindow. Needed early when
  970.   // not created if gadget window is being positioned within another window.
  971.   //
  972.   //
  973.   UseDesiredSize();
  974. }
  975.  
  976. //
  977. // Insert a list of Gadgets.
  978. //
  979. // Caller needs to also call LayoutSession() after inserting gadgets if
  980. // this window has already been created
  981. //
  982. void
  983. TGadgetWindow::InsertFrom(TGadgetList& list, TPlacement placement, TGadget* sibling)
  984. {
  985.   TGadgetList::InsertFrom(list, placement, sibling);
  986.  
  987.   // Compute the maxWidth and maxHeight of the gadgetwindow. Needed early when
  988.   // not created if gadget window is being positioned within another window.
  989.   //
  990.   //
  991.   UseDesiredSize();
  992. }
  993.  
  994. //
  995. // A gadget has been inserted into this gadget window
  996. //
  997. void
  998. TGadgetWindow::Inserted(TGadget& gadget)
  999. {
  1000.   // Let gadget know that it is now in this window
  1001.   //
  1002.   gadget.Window = this;
  1003.   gadget.Inserted();
  1004.  
  1005.   // If the gadgetwindow was already created, inform gadget
  1006.   // so it may perform initialization that requires an HWND
  1007.   //
  1008.   if (GetHandle())
  1009.     gadget.Created();
  1010.  
  1011.   if (gadget.WideAsPossible)
  1012.     WideAsPossible++;
  1013. }
  1014.  
  1015. //
  1016. // Remove (unlink) a gadget from this gadget window. The gadget is
  1017. // returned but not destroyed. Returns 0 if gadget is not in this window
  1018. //
  1019. // Caller needs to also call LayoutSession() after inserting/removing gadgets
  1020. // if this gadget window has already been created.
  1021. //
  1022. TGadget*
  1023. TGadgetWindow::Remove(TGadget& gadget)
  1024. {
  1025.   if (gadget.Window != this)
  1026.     return 0;
  1027.  
  1028.   // Perform actual removal from list
  1029.   //
  1030.   return TGadgetList::Remove(gadget);
  1031. }
  1032.  
  1033. //
  1034. // A gadget has been removed from this gadget window
  1035. //
  1036. void
  1037. TGadgetWindow::Removed(TGadget& gadget)
  1038. {
  1039.   // Re-adjust gadget now that it doesn't live in a window
  1040.   //
  1041.   if (gadget.WideAsPossible)
  1042.     WideAsPossible--;
  1043.  
  1044.   // Clear last know gadget as mouse location
  1045.   //
  1046.   if (&gadget == AtMouse)
  1047.     AtMouse = 0;
  1048.  
  1049.   // Release caption if it has/had it
  1050.   //
  1051.   GadgetReleaseCapture(gadget);
  1052.  
  1053.   // Notify gadget and reset/adjust variables to reflect new state of gadget
  1054.   //
  1055.   gadget.Removed();
  1056.   gadget.Window = 0;
  1057.   gadget.Next = 0;
  1058.   gadget.GetBounds() -= gadget.GetBounds().TopLeft();
  1059. }
  1060.  
  1061. //
  1062. // Set a new Shared CelArray for this gadget window. Allows a predefined array
  1063. // of images to be shared by the gadgets.
  1064. //
  1065. // This GadgetWindow assumes ownership of the passed CelArray ptr.
  1066. //
  1067. void
  1068. TGadgetWindow::SetCelArray(TCelArray* sharedCels)
  1069. {
  1070.   delete SharedCels;
  1071.   SharedCels = sharedCels;
  1072. }
  1073.  
  1074. //
  1075. // Get the Shared CelArray for this gadget window. Makes an empty one on the
  1076. // fly if needed.
  1077. //
  1078. TCelArray&
  1079. TGadgetWindow::GetCelArray(int minX, int minY)
  1080. {
  1081.   if (!SharedCels) {
  1082.     if (!minX)
  1083.       minX = 10;
  1084.     if (!minY)
  1085.       minY = 10;
  1086.     SharedCels = new TCelArray(TSize(minX,minY), ILC_MASK, 10, 5);
  1087.   }
  1088.  
  1089.   return *SharedCels;
  1090. }
  1091.  
  1092. //
  1093. // Override TWindow::SetupWindow.
  1094. //
  1095. void
  1096. TGadgetWindow::SetupWindow()
  1097. {
  1098.   TWindow::SetupWindow();
  1099.  
  1100.   // if 'WantTimer' is enabled, start a timer
  1101.   //
  1102.   if (WantTimer)
  1103.     EnableTimer();
  1104.  
  1105.   if (DirtyLayout)    
  1106.     LayoutSession();
  1107.  
  1108.   // Now that this window is created, see if any of the gadgets have changed
  1109.   // sizes (like control gadgets). If so, remember the size & relayout the
  1110.   // gadgets. Also adjust the size of this gadget window to match what we want.
  1111.   //
  1112.   TSize  size;
  1113.   GetDesiredSize(size);
  1114.  
  1115. //  if (DirtyLayout)  
  1116. //    LayoutSession();
  1117.  
  1118.   if (ShrinkWrapWidth  && Attr.W != size.cx ||
  1119.       ShrinkWrapHeight && Attr.H != size.cy)
  1120.   {
  1121.     if (ShrinkWrapWidth)
  1122.       Attr.W = size.cx;
  1123.     if (ShrinkWrapHeight)
  1124.       Attr.H = size.cy;
  1125.  
  1126.     LayoutSession();
  1127.     SetWindowPos(0, 0, 0, size.X(), size.Y(),
  1128.                  SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
  1129.   }
  1130.  
  1131.   // Create toolips after all other windows have been created.
  1132.   //
  1133.   PostMessage( WM_OWLCREATETTIP );
  1134. }
  1135.  
  1136. //
  1137. // Override TWindow's virtual to cleanup potential Timer
  1138. //
  1139. void
  1140. TGadgetWindow::CleanupWindow()
  1141. {
  1142.   // Cleanup pending timer
  1143.   //
  1144.   if (TimerID && KillTimer(TimerID)) {
  1145.     TimerID = 0;
  1146.   }
  1147.  
  1148.   // Chain to base class' version
  1149.   //
  1150.   TWindow::CleanupWindow();
  1151. }
  1152.  
  1153. //
  1154. // Relay 'interesting' messages to the tooltip window
  1155. //
  1156. bool
  1157. TGadgetWindow::PreProcessMsg(MSG& msg)
  1158. {
  1159.   // Check if this message might be worth relaying to the tooltip window
  1160.   //
  1161.   TTooltip* tooltip = GetTooltip();
  1162.   if (tooltip && tooltip->IsWindow()) {
  1163.     if (msg.hwnd == *this || IsChild(msg.hwnd)) {
  1164.       if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) {
  1165.         tooltip->RelayEvent(msg);
  1166.       }
  1167.     }
  1168.   }
  1169.  
  1170.   // Always let event go through.
  1171.   //
  1172.   return TWindow::PreProcessMsg(msg);
  1173. }
  1174.  
  1175. //
  1176. // Create Tooltips for GadgetWindow
  1177. //
  1178. void
  1179. TGadgetWindow::EvCreateTooltips()
  1180. {
  1181.   // If 'WantTooltip' is enabled, created the control
  1182.   //
  1183.   if (WantTooltip)
  1184.     EnableTooltip(true);
  1185.  
  1186.   // Inform each gadget that the gadgetwindow is now created. This allows
  1187.   // gadgets to perform initialization [eg. registering with the tooltip] which
  1188.   // requires the 'HWND' to be valid
  1189.   //
  1190.   for (TGadget* g = Gadgets; g; g = g->NextGadget())
  1191.     g->Created();
  1192.  
  1193. }
  1194.  
  1195.  
  1196.  
  1197. //
  1198. // Intercept window size changes to make sure that this gadget window follows
  1199. // is own sizing rules. Also gives it a chance to layout wide-as-possible
  1200. // gadgets
  1201. //
  1202. void
  1203. TGadgetWindow::EvWindowPosChanging(WINDOWPOS far& windowPos)
  1204. {
  1205.   TWindow::EvWindowPosChanging(windowPos);
  1206.  
  1207.   // Only process if it is a size change
  1208.   //
  1209.   if (!(windowPos.flags&SWP_NOSIZE))
  1210.   {
  1211.     // Knowing how big we might be, tile the gadgets to let them get positioned
  1212.     // the way they want.
  1213.     //
  1214.     Attr.W = windowPos.cx;
  1215.     Attr.H = windowPos.cy;
  1216.  
  1217.     // Only tile if either we are dirty or have some wide-as-possible gadgets
  1218.     // to adjust. Dont call LayoutSession() in order to avoid a loop. Derived
  1219.     // versions might trigger a resize in there.
  1220.     //
  1221.     if (DirtyLayout || WideAsPossible)
  1222.       InvalidateRect(TileGadgets());
  1223.  
  1224.     // Find out how big we really want to be if any shrink wrap, and enforce it
  1225.     //
  1226.     UseDesiredSize();
  1227.     if (ShrinkWrapWidth)
  1228.       windowPos.cx = Attr.W;
  1229.     if (ShrinkWrapHeight)
  1230.       windowPos.cy = Attr.H;
  1231.   }
  1232. }
  1233.  
  1234. //
  1235. // Iterate thru the gadget list and paint each one.
  1236. //
  1237. // Override this to implement a specific look (separator line, raised, etc)
  1238. //
  1239. void
  1240. TGadgetWindow::PaintGadgets(TDC& dc, bool, TRect& rect)
  1241. {
  1242.   TPoint viewport = dc.GetViewportOrg();
  1243.   for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) {
  1244.     if (gadget->GetBounds().Touches(rect)) {
  1245.       dc.SetViewportOrg((TPoint&)gadget->GetBounds());
  1246.  
  1247.       // If the gadget is a sloppy painter & needs clipping support, set the
  1248.       // clip rect before painting, otherwise just paint. Most gadgets don't
  1249.       // need this help
  1250.       //
  1251.       if (gadget->Clip) {
  1252.         TRegion savedClipRgn;
  1253.         dc.GetClipRgn(savedClipRgn);
  1254.         dc.IntersectClipRect(gadget->GetBounds());
  1255.  
  1256.         gadget->Paint(dc);
  1257.  
  1258.         dc.SelectClipRgn(savedClipRgn);
  1259.       }
  1260.       else
  1261.         gadget->Paint(dc);
  1262.     }
  1263.   }
  1264.   dc.SetViewportOrg(viewport);
  1265. }
  1266.  
  1267. //
  1268. // Respond to virtual TWindow Paint call. Call our own virtual PaintGadgets
  1269. // to actually perform the painting of the gadgets
  1270. //
  1271. void
  1272. TGadgetWindow::Paint(TDC& dc, bool erase, TRect& rect)
  1273. {
  1274.   dc.SelectObject(*Font);
  1275.   PaintGadgets(dc, erase, rect);
  1276.  
  1277. #if defined(__TRACE) || defined(__WARN)
  1278.   // Highlight the tools of this gadgetwindow for debugging purposes.
  1279.   // Especially useful for docking windows which undergo internal state
  1280.   // changes
  1281.   //
  1282.   if (Tooltip && Tooltip->IsWindow()) {
  1283.     uint count = Tooltip->GetToolCount();
  1284.     if (count) {
  1285.       TPen redPen(TColor(0xff, 0, 0));
  1286.       dc.SelectObject(redPen);
  1287.  
  1288.       TToolInfo ti;
  1289.       while (count) {
  1290.         if (Tooltip->EnumTools(--count, ti)) {
  1291.           dc.MoveTo(ti.rect.left, ti.rect.top);
  1292.           dc.LineTo(ti.rect.right,ti.rect.top);
  1293.           dc.LineTo(ti.rect.right,ti.rect.bottom);
  1294.           dc.LineTo(ti.rect.left, ti.rect.bottom);
  1295.           dc.LineTo(ti.rect.left, ti.rect.top);
  1296.         }
  1297.       }
  1298.       dc.RestorePen();
  1299.     }
  1300.   }
  1301. #endif
  1302. }
  1303.  
  1304. //
  1305. // Determine if the mouse down is in an enabled gadget, & if so pass it to
  1306. // the gadget.
  1307. //
  1308. void
  1309. TGadgetWindow::EvLButtonDown(uint modKeys, TPoint& point)
  1310. {
  1311.   TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
  1312.  
  1313.   if (gadget && (gadget->GetEnabled() || Capture)) {
  1314.     TPoint gadgetPoint = point - *(TSize*)&gadget->GetBounds().TopLeft();
  1315.     gadget->LButtonDown(modKeys, gadgetPoint);
  1316.   }
  1317.  
  1318.   TWindow::EvLButtonDown(modKeys, point);
  1319. }
  1320.  
  1321. //
  1322. // Forward mouse-ups to the gadget that has captures the mouse, if any, or
  1323. // to the gadget at the mouse pos
  1324. //
  1325. void
  1326. TGadgetWindow::EvLButtonUp(uint modKeys, TPoint& point)
  1327. {
  1328.   TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
  1329.  
  1330.   if (gadget && (gadget->GetEnabled() || Capture))
  1331.     gadget->LButtonUp(modKeys, TPoint(point.x - gadget->GetBounds().left,
  1332.                                       point.y - gadget->GetBounds().top));
  1333.   TWindow::EvLButtonUp(modKeys, point);
  1334. }
  1335.  
  1336. //
  1337. // Pass double clicks thru as if they were just a second click, finish the
  1338. // first click, & begin the second. Dn + Dbl + Up -> Dn + Up+Dn + Up
  1339. //
  1340. void
  1341. TGadgetWindow::EvLButtonDblClk(uint modKeys, TPoint& point)
  1342. {
  1343.   EvLButtonUp(modKeys, point);
  1344.   EvLButtonDown(modKeys, point);
  1345. }
  1346.  
  1347. //
  1348. // Pass RButton messages to the gadget at the mouse location or the one
  1349. // with Capture
  1350. //
  1351. // NOTE: The default right-mouse down handler of TGadget does
  1352. //       *NOT* set capture
  1353. //
  1354. void
  1355. TGadgetWindow::EvRButtonDown(uint modKeys, TPoint& point)
  1356. {
  1357.   TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
  1358.  
  1359.   if (gadget && (gadget->GetEnabled() || Capture)) {
  1360.     TPoint gadgetPoint = point - *(TSize*)&gadget->GetBounds().TopLeft();
  1361.     gadget->RButtonDown(modKeys, gadgetPoint);
  1362.   }
  1363.  
  1364.   TWindow::EvRButtonDown(modKeys, point);
  1365. }
  1366.  
  1367. //
  1368. // Pass RButton messages to the gadget at the mouse location or the one with
  1369. // capture.
  1370. //
  1371. // NOTE: The default right-mouse down handler of TGadget does
  1372. //       *NOT* set capture
  1373. //
  1374. void
  1375. TGadgetWindow::EvRButtonUp(uint modKeys, TPoint& point)
  1376. {
  1377.   TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
  1378.  
  1379.   if (gadget && (gadget->GetEnabled() || Capture))
  1380.     gadget->RButtonUp(modKeys, TPoint(point.x - gadget->GetBounds().left,
  1381.                                       point.y - gadget->GetBounds().top));
  1382.   TWindow::EvRButtonUp(modKeys, point);
  1383. }
  1384.  
  1385. //
  1386. // Forward mouse moves to the gadget that has captures the mouse, if any.
  1387. // Otherwise checks for mouse entering & leaving gadgets
  1388. //
  1389. // Could enhance this by delaying mouse enter messages until mouse has been
  1390. // in the same area for a while, or looking ahead in queue for mouse msgs.
  1391. //
  1392. void
  1393. TGadgetWindow::EvMouseMove(uint modKeys, TPoint& point)
  1394. {
  1395.   if (Capture) {
  1396.     Capture->MouseMove(modKeys, TPoint(point.x - Capture->GetBounds().left,
  1397.                                        point.y - Capture->GetBounds().top));
  1398.   }
  1399.   else {
  1400.     TGadget* gadget = GadgetFromPoint(point);
  1401.     if (gadget != AtMouse) {
  1402.       if (AtMouse)
  1403.         AtMouse->MouseLeave(modKeys, TPoint(point.x - AtMouse->GetBounds().left,
  1404.                                             point.y - AtMouse->GetBounds().top));
  1405.       AtMouse = gadget;
  1406.       if (AtMouse)
  1407.         AtMouse->MouseEnter(modKeys, TPoint(point.x - AtMouse->GetBounds().left,
  1408.                                             point.y - AtMouse->GetBounds().top));
  1409.     }
  1410.   }
  1411.   TWindow::EvMouseMove(modKeys, point);
  1412. }
  1413.  
  1414. //
  1415. //
  1416. //
  1417. TTooltip*
  1418. TGadgetWindow::GetTooltip() const
  1419. {
  1420.   return Tooltip;
  1421. }
  1422.  
  1423. //
  1424. //
  1425. //
  1426. void
  1427. TGadgetWindow::EnableTooltip(bool enable)
  1428. {
  1429.   if (!Tooltip) {
  1430.  
  1431.     // Find a parent for the tooltip: It's attractive to make the
  1432.     // gadgetwindow the owner of the tooltip, a popup window. However, this
  1433.     // will typically fail since the gadget window is invariably a child
  1434.     // window. Windows seems to accomodate this situation by simply making
  1435.     // the tooltip's owner the gadgetwindow's owner. This works fine until
  1436.     // the owner of the gadgetwindow is destroyed before the gadgetwindow
  1437.     // is destroyed, such as in the case of a gadgetwindow initally created
  1438.     // as a floating docking toolbar; When it's docked, the floating slip,
  1439.     // it's original owner, is destroyed and the gadget window is reparented
  1440.     // to an edge slip. In this scenario, the tooltip is silently destroyed
  1441.     // along with the floating slip [it's real owner!] and the docked
  1442.     // gadgetwindow no longer provide tool tips!
  1443.     //
  1444.  
  1445.     // To circumvent this scenario, we'll look for a window which is fairly
  1446.     // stable/rooted as owner of the tooltip. Ideally, we'll get the
  1447.     // application's main window.
  1448.     //
  1449.     TWindow* tipParent = this;
  1450.     while (tipParent->GetParentO()) {
  1451.       tipParent = tipParent->GetParentO();
  1452.       if (tipParent->IsFlagSet(wfMainWindow))
  1453.         break;
  1454.     }
  1455.  
  1456.     // Create and initialize tooltip
  1457.     //
  1458.     SetTooltip(new TTooltip(tipParent));
  1459.   }
  1460.   else {
  1461.     if (Tooltip->GetHandle())
  1462.       Tooltip->Activate(enable);
  1463.   }
  1464. }
  1465.  
  1466. //
  1467. // Set a specific tooltip for this window. Assume we now own the ToolTip
  1468. //
  1469. void
  1470. TGadgetWindow::SetTooltip(TTooltip* tooltip)
  1471. {
  1472.   // Cleanup; via Condemned list if tooltip was created
  1473.   //
  1474.   if (Tooltip) {
  1475.     if (Tooltip->GetHandle())
  1476.       Tooltip->SendMessage(WM_CLOSE);
  1477.     else
  1478.       delete Tooltip;
  1479.   }
  1480.  
  1481.   // Store new tooltip and create if necessary
  1482.   //
  1483.   Tooltip = tooltip;
  1484.   if (Tooltip) {
  1485.     if (!Tooltip->GetHandle()) {
  1486.  
  1487.       // Make sure tooltip is disabled so it does not take input focus
  1488.       //
  1489.       Tooltip->Attr.Style |= WS_DISABLED;
  1490.       Tooltip->Create();
  1491.     }
  1492.   }
  1493. }
  1494.  
  1495. //
  1496. // When the gadget window receives a WM_COMMAND message, it is likely
  1497. // from a gadget or control within a TControlGadget. Reroute to the command
  1498. // target.
  1499. //
  1500. TResult
  1501. TGadgetWindow::EvCommand(uint id, HWND hWndCtl, uint notifyCode)
  1502. {
  1503.   TRACEX(OwlCmd, 1, "TGadgetWindow::EvCommand - id(" << id << "), ctl(" <<\
  1504.                      hex << uint(hWndCtl) << "), code(" << notifyCode  << ")");
  1505.  
  1506.   // First allow any derived class that wants to handle the command
  1507.   // NOTE: This search only caters for menu-style WM_COMMANDs (not those
  1508.   //       sent by controls)
  1509.   //
  1510.   TEventInfo  eventInfo(0, id);
  1511.   if (Find(eventInfo)) {
  1512.     Dispatch(eventInfo, id);
  1513.     return 0;
  1514.   }
  1515.  
  1516.  
  1517. #if 0
  1518.   // Prior versions of TGadgetWindow relied on TWindow's EvCommand for
  1519.   // dispatching WM_COMMAND events. This required that one derives from
  1520.   // a decoration class (eg. TControlbar, TToolbox) to handle control
  1521.   // notifications. The current version uses a more generalized logic
  1522.   // involving the CommandTarget and a frame ancestor class. This allows
  1523.   // a client window to handle notifications of a control in a toolbar
  1524.   // without using a TControlbar-derived class.
  1525.   // However, if you need to previous behaviour, simply invoke TWindow's
  1526.   // EvCommand from this handler.
  1527.  
  1528.   return TWindow::EvCommand(id, hWndCtl, notifyCode);
  1529. #endif
  1530.  
  1531.   TWindow* target;
  1532.   TFrameWindow* frame;
  1533.  
  1534.   // Find the frame who is our latest ancestor and make it our command target
  1535.   //
  1536.   for (target = GetParentO(); target; target = target->GetParentO()) {
  1537.     frame = TYPESAFE_DOWNCAST(target, TFrameWindow);
  1538.     if (frame || !target->GetParentO())
  1539.       break;
  1540.   }
  1541.  
  1542.   // Make sure the frame doesn't think we are its command target, or a BAD
  1543.   // loop will happen
  1544.   //
  1545.   if (target && (!frame || frame->GetCommandTarget() != GetHandle())) {
  1546.     CHECK(target->IsWindow());
  1547.     return target->EvCommand(id, hWndCtl, notifyCode);
  1548.   }
  1549.  
  1550.   // If all command routing fails, go back to basic dispatching of TWindow
  1551.   //
  1552.   return TWindow::EvCommand(id, hWndCtl, notifyCode);
  1553. }
  1554.  
  1555. //
  1556. // When the gadget window receives a WM_COMMAND_ENABLE message, it is likely
  1557. // from a gadget or control within a TControlGadget. Reroute to the command
  1558. // target.
  1559. //
  1560. void
  1561. TGadgetWindow::EvCommandEnable(TCommandEnabler& ce)
  1562. {
  1563.   // If someone derived from TGadgetWindow and handles the command there,
  1564.   // give these handlers the first crack.
  1565.   //
  1566.   TEventInfo eventInfo(WM_COMMAND_ENABLE, ce.Id);
  1567.   if (Find(eventInfo)) {
  1568.     Dispatch(eventInfo, 0, TParam2(&ce));
  1569.     return;
  1570.   }
  1571.  
  1572.   TWindow* target = GetParentO();
  1573.  
  1574.   // Forward to command target if the enabler was really destined for us, and
  1575.   // not a routing from the frame.
  1576.   //
  1577.   if (target && ce.IsReceiver(*this)) {
  1578.     CHECK(target->IsWindow());
  1579.     ce.SetReceiver(*target);
  1580.     target->EvCommandEnable(ce);
  1581.     if( ce.GetHandled() )
  1582.       return;
  1583.   }
  1584.  
  1585.   // Default to TWindow's implementation if the above routing fails
  1586.   //
  1587.   TWindow::EvCommandEnable(ce);
  1588. }
  1589.  
  1590. //
  1591. // Catch tooltip requests for text and forward them to the parent instead
  1592. //
  1593. TResult
  1594. TGadgetWindow::EvNotify(uint id, TNotify far& notifyInfo)
  1595. {
  1596.   // Intercept requests for tooltip texts and turn the request into
  1597.   // a 'CommandEnabler'. This mechanism allows use the route the request
  1598.   // the same way commands are routed in OWL. Therefore, the object that
  1599.   // handles a command is the object that get's first crack at providing
  1600.   // the tip text for that command.
  1601.   //
  1602.   if (notifyInfo.code == TTN_NEEDTEXT) {
  1603.     TTooltipText& ttText = *(TTooltipText*)¬ifyInfo;
  1604.     TTooltipEnabler enabler(ttText, *this);
  1605.  
  1606. #if defined(__TRACE) || defined(__WARN)
  1607.     char text[50];
  1608.     sprintf(text, (ttText.uFlags & TTF_IDISHWND) ? "Tip for 0x%X not found" :
  1609.                                                    "Text for %d not found",
  1610.                                                     enabler.Id);
  1611.     enabler.SetText(text);
  1612. #endif
  1613.     HandleMessage(WM_COMMAND_ENABLE, 0, TParam2(&enabler));
  1614.     return 0;
  1615.   }
  1616.  
  1617.   // Chain to TWindow's dispatch mechanism
  1618.   //
  1619.   return TWindow::EvNotify(id, notifyInfo);
  1620. }
  1621.  
  1622. //----------------------------------------------------------------------------
  1623.  
  1624. //
  1625. //
  1626. //
  1627. TGadgetControl::TGadgetControl(TWindow*  parent,
  1628.                                TGadget*  soleGadget,
  1629.                                TFont*    font,
  1630.                                TModule*  module)
  1631. :
  1632.   TGadgetWindow(parent, Horizontal, font, module)
  1633. {
  1634.   // Let Attr rect override
  1635.   //
  1636.   SetShrinkWrap(false, false);
  1637.  
  1638.   // Let inner gadget paint everything
  1639.   //
  1640.   SetMargins(TMargins(TMargins::Pixels, 0, 0, 0, 0));
  1641.  
  1642.   if (soleGadget)
  1643.     Insert(*soleGadget);
  1644. }
  1645.