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

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1992, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.19  $
  6. //
  7. // Implementation of classes TGadget, TSeparatorGadget and TSizeGripGadget
  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_TOOLTIP_H)
  17. # include <owl/tooltip.h>
  18. #endif
  19. #if !defined(OWL_UIHELPER_H)
  20. # include <owl/uihelper.h>
  21. #endif
  22. #if !defined(WINSYS_UIMETRIC_H)
  23. # include <winsys/uimetric.h>
  24. #endif
  25.  
  26. OWL_DIAGINFO;
  27.  
  28. #if defined(BI_NO_RTTI)
  29.   IMPLEMENT_CASTABLE1(TGadget, TStreamableBase);
  30. #endif
  31.  
  32. //
  33. // Convert layout units to pixels using a given font height
  34. //
  35. // A layout unit is defined by dividing the font "em" into 8 vertical and 8
  36. // horizontal segments
  37. //
  38. static int
  39. layoutUnitsToPixels(int units, int fontHeight)
  40. {
  41.   const long  unitsPerEM = 8;
  42.  
  43.   return int((long(units) * fontHeight + unitsPerEM / 2) / unitsPerEM);
  44. }
  45.  
  46. //
  47. // Return true if the Id is a predefined gadget id.
  48. //
  49. static bool
  50. predefinedGadgetId(int id)
  51. {
  52.   if (id == 0 || id == -1 || (IDG_FIRST <= id && id < IDG_LAST)) {
  53.     return true;
  54.   }
  55.   return false;
  56. }
  57.  
  58. //
  59. // Retrieve the sizes of the 4 margins in pixels given a font height
  60. //
  61. void
  62. TMargins::GetPixels(int& left, int& right, int& top, int& bottom, int fontHeight) const
  63. {
  64.   switch (Units) {
  65.     case Pixels:
  66.       left = Left;
  67.       top = Top;
  68.       right = Right;
  69.       bottom = Bottom;
  70.       break;
  71.  
  72.     case LayoutUnits:
  73.       left = layoutUnitsToPixels(Left, fontHeight);
  74.       top = layoutUnitsToPixels(Top, fontHeight);
  75.       right = layoutUnitsToPixels(Right, fontHeight);
  76.       bottom = layoutUnitsToPixels(Bottom, fontHeight);
  77.       break;
  78.  
  79.     case BorderUnits:
  80.       int  cxBorder = TUIMetric::CxBorder;
  81.       int  cyBorder = TUIMetric::CyBorder;
  82.  
  83.       left = Left * cxBorder;
  84.       top = Top * cyBorder;
  85.       right = Right * cxBorder;
  86.       bottom = Bottom * cyBorder;
  87.       break;
  88.   }
  89. }
  90.  
  91. //
  92. // Construct a gadget with a given id and border style. Used by derived
  93. // classes.
  94. //
  95. TGadget::TGadget(int id, TBorderStyle borderStyle)
  96. {
  97.   Window = 0;
  98.   Bounds = TRect(0, 0, 0, 0);
  99.   Flags = Enabled | Visible;
  100.   TrackMouse = false;
  101.   Clip = false;
  102.   WideAsPossible = false;
  103.   ShrinkWrapWidth = ShrinkWrapHeight = true;
  104.   Next = 0;
  105.   Id = id;
  106.  
  107.   SetBorderStyle(borderStyle);
  108. }
  109.  
  110. //
  111. // Destruct a gadget and cleanup
  112. //
  113. TGadget::~TGadget()
  114. {
  115.   // If we're in a window, remove ourselves.
  116.   //
  117.   if (Window)
  118.     Window->Remove(*this);
  119. }
  120.  
  121. //
  122. // Called during idle time to allow the gadget to perform any idle actions.
  123. // TGadget performs command enabling on first call in each idle period
  124. //
  125. bool
  126. TGadget::IdleAction(long idleCount)
  127. {
  128.   if (idleCount == 0)
  129.     CommandEnable();
  130.   return false;
  131. }
  132.  
  133. //
  134. // Placeholder for virtual called for command enabling
  135. //
  136. void
  137. TGadget::CommandEnable()
  138. {
  139. }
  140.  
  141. //
  142. // Placeholder for virtual called when system colors change
  143. //
  144. void
  145. TGadget::SysColorChange()
  146. {
  147. }
  148.  
  149. //
  150. // Simple set accessor to set whether shrinkwrapping is performed horizontally
  151. // and/or vertically.
  152. // Call the gadget window's GadgetChangedSize() member function to affect a
  153. // change in size.
  154. //
  155. void
  156. TGadget::SetShrinkWrap(bool shrinkWrapWidth, bool shrinkWrapHeight)
  157. {
  158.   ShrinkWrapWidth = shrinkWrapWidth;
  159.   ShrinkWrapHeight = shrinkWrapHeight;
  160. }
  161.  
  162. //
  163. // Directly alter the size of the gadget. Call the gadget window's
  164. // GadgetChangedSize() member function for the size change to take affect
  165. //
  166. // Only use this member function if you turned off shrink wrapping in one or
  167. // both dimension; otherwise the GetDesiredSize() member function will return
  168. // the fitted size which will be different.
  169. //
  170. void
  171. TGadget::SetSize(TSize& size)
  172. {
  173.   Bounds.right = Bounds.left + size.cx;
  174.   Bounds.bottom = Bounds.top + size.cy;
  175.  
  176.   if (Window)
  177.     Window->GadgetChangedSize(*this);
  178. }
  179.  
  180. //
  181. // Enable or disable this gadget
  182. //
  183. void
  184. TGadget::SetEnabled(bool enabled)
  185. {
  186.   if (ToBool(Flags & Enabled) != enabled) {
  187.     if (enabled)
  188.       Flags |= Enabled;
  189.     else
  190.       Flags &= ~Enabled;
  191.     Invalidate(false);
  192.   }
  193. }
  194.  
  195. //
  196. // Called by the gadget window to inform the gadget of a change in its bounding
  197. // rectangle. Default behavior here just updates instance variable "Bounds" but
  198. // derived classes might override this method to update other internal state.
  199. //
  200. void
  201. TGadget::SetBounds(const TRect& rect)
  202. {
  203.   if (rect != Bounds) {
  204.     Bounds = rect;
  205.     Moved();
  206.   }
  207. }
  208.  
  209. //
  210. // Set the widths of the four outer borders. Notifies the owning Window of a
  211. // size change.
  212. //
  213. void
  214. TGadget::SetBorders(const TBorders& borders)
  215. {
  216.   Borders = borders;
  217.  
  218.   if (Window)
  219.     Window->GadgetChangedSize(*this);
  220. }
  221.  
  222. //
  223. // Set the widths of the four margins within the borders. Notifies the owning
  224. // Window of a size change.
  225. //
  226. void
  227. TGadget::SetMargins(const TMargins& margins)
  228. {
  229.   Margins = margins;
  230.  
  231.   if (Window)
  232.     Window->GadgetChangedSize(*this);
  233. }
  234.  
  235. //
  236. // Set the border style used by this gadget. Internal Border members are
  237. // updated and owning Window is notified of a size change.
  238. //
  239. void
  240. TGadget::SetBorderStyle(TBorderStyle borderStyle)
  241. {
  242.   BorderStyle = borderStyle;
  243.  
  244.   int  edgeThickness;
  245.   switch (BorderStyle) {
  246.     default:
  247.     case None:
  248.       edgeThickness = 0;
  249.       break;
  250.  
  251.     case Plain:
  252.     case Raised:
  253.     case Recessed:
  254.       edgeThickness = 1;
  255.       break;
  256.  
  257.     case Embossed:
  258. //      edgeThickness = 3;
  259.     case Grooved:
  260.     case ButtonUp:
  261.     case ButtonDn:
  262.     case WndRaised:
  263.     case WndRecessed:
  264.       edgeThickness = 2;
  265.       break;
  266.   }
  267.  
  268.   Borders.Left = Borders.Top = Borders.Right = Borders.Bottom = edgeThickness;
  269.  
  270.   if (Window)
  271.     Window->GadgetChangedSize(*this);
  272. }
  273.  
  274. //
  275. // Determine if a point is logically within this gadget. Return true if this
  276. // gadget is visible & the point is within this gadget's bounds rect
  277. //
  278. bool
  279. TGadget::PtIn(const TPoint& point)
  280. {
  281.   return IsVisible() &&
  282.          point.x >= 0 && point.y >= 0 &&
  283.          point.x < Bounds.Width() && point.y < Bounds.Height();
  284. }
  285.  
  286. //
  287. // Virtual called after the window holding a gadget has been created
  288. //
  289. void
  290. TGadget::Created()
  291. {
  292.   PRECONDITION(Window);
  293.   PRECONDITION(Window->GetHandle());
  294.  
  295.   // Register ourself with the tooltip window (if there's one)
  296.   //
  297.   TTooltip* tooltip = Window->GetTooltip();
  298.   if (tooltip && tooltip->IsWindow()) {
  299.  
  300.     // Don't register gadget's with Id's of 0 or -1.
  301.     // Typically, 0 is reserved by separators and -1 could
  302.     // be used for dumb text gadgets...
  303.     //
  304.     if (!predefinedGadgetId(Id)) {
  305.       TToolInfo toolInfo(Window->GetHandle(), Bounds, Id);
  306.       tooltip->AddTool(toolInfo);
  307.     }
  308.   }
  309. }
  310.  
  311. //
  312. // Virtual called after gadget is inserted into window
  313. //
  314. void
  315. TGadget::Inserted()
  316. {
  317. }
  318.  
  319. //
  320. // Virtual called before gadget is removed from window
  321. //
  322. void
  323. TGadget::Removed()
  324. {
  325.   // Unregister ourself with the tooltip window (if there's one)
  326.   //
  327.   if (Window && Window->GetHandle()) {
  328.     TTooltip* tooltip = Window->GetTooltip();
  329.     if (tooltip && tooltip->IsWindow()) {
  330.  
  331.       // Don't bother with gadgets with Id's of 0 or -1.
  332.       // Typically, 0 is reserved by separators and -1 could
  333.       // be used for dumb text gadgets...
  334.       //
  335.       if (!predefinedGadgetId(Id)) {
  336.         TToolInfo toolInfo(Window->GetHandle(), Bounds, Id);
  337.         tooltip->DeleteTool(toolInfo);
  338.       }
  339.     }
  340.   }
  341. }
  342.  
  343. //
  344. // Virtual called when a gadget is relocated
  345. //
  346. void
  347. TGadget::Moved()
  348. {
  349.   // Send tooltip window our updated location
  350.   //
  351.   if (Window && Window->GetHandle()) {
  352.     TTooltip* tooltip = Window->GetTooltip();
  353.     if (tooltip && tooltip->IsWindow()) {
  354.  
  355.       // Don't bother with gadgets with Id's of 0 or -1.
  356.       // Typically, 0 is reserved by separators and -1 could
  357.       // be used for dumb text gadgets...
  358.       //
  359.       if (!predefinedGadgetId(Id)) {
  360.  
  361.         // First retrieve information about the tool
  362.         //
  363.         TToolInfo ti(true);
  364.         ti.SetToolInfo(*Window, Id);
  365.         if (tooltip->GetToolInfo(ti)) {
  366.  
  367.           // Update the tooltip info if we've moved
  368.           //
  369.           if (TRect(ti.rect) != Bounds)
  370.             tooltip->NewToolRect(TToolInfo(Window->GetHandle(), Bounds, Id));
  371.         }
  372.  
  373. #if 0   // POSSIBLE ENHANCEMENT /////////////////////////////////////////////
  374.         // If the tool information could not be retrieved, should we just
  375.         // go ahead and add a brand new tool? We should really never get
  376.         // here unless the tooltip of the gadget window was manipulated
  377.         // behind our back or replaced with a new one...
  378.         //
  379.         else {
  380.           TToolInfo toolInfo(Window->GetHandle(), Bounds, Id);
  381.           tooltip->AddTool(toolInfo);
  382.         }
  383. #endif  /////////////////////////////////////////////////////////////////////
  384.       }
  385.     }
  386.   }
  387. }
  388.  
  389. //
  390. // Invalidate a rectangle in our containing window. Rectangle is specified
  391. // in gadget coordinates.
  392. //
  393. void
  394. TGadget::InvalidateRect(const TRect& rect, bool erase)
  395. {
  396.   if (Window && Window->GetHandle()) {
  397.     TRect  updateRect(rect.left + Bounds.left, rect.top + Bounds.top,
  398.                       rect.right + Bounds.left, rect.bottom + Bounds.top);
  399.  
  400.     Window->InvalidateRect(updateRect, erase);
  401.   }
  402. }
  403.  
  404. //
  405. // Invalidate the rectangle used by this gadget to cause it to repaint
  406. //
  407. void
  408. TGadget::Invalidate(bool erase)
  409. {
  410.   InvalidateRect(TRect(0, 0, Bounds.Width(), Bounds.Height()), erase);
  411. }
  412.  
  413. //
  414. // Cause owning window to paint now if possible
  415. //
  416. void
  417. TGadget::Update()
  418. {
  419.   if (Window && Window->GetHandle())
  420.     Window->UpdateWindow();
  421. }
  422.  
  423. //
  424. // Paint the border of the gadget based on the BorderStyle member
  425. //
  426. void
  427. TGadget::PaintBorder(TDC& dc)
  428. {
  429.   if (BorderStyle != None) {
  430.     int  xB = TUIMetric::CxBorder;
  431.     int  yB = TUIMetric::CyBorder;
  432.  
  433.     if (BorderStyle == Plain) {
  434.       TBrush winBr(TColor::SysWindowFrame);
  435.       dc.OWLFastWindowFrame(winBr,
  436.                             TRect(0, 0, Bounds.Width(), Bounds.Height()),
  437.                             xB, yB);
  438.     }
  439.     else {
  440.       // Use the 1:1 mapping of BorderStyle to TUIBorder::TStyle
  441.       //
  442.       TRect boundsRect(0,0,Bounds.Width(),Bounds.Height());
  443.       TUIBorder(boundsRect, TUIBorder::TStyle(BorderStyle)).Paint(dc);
  444.     }
  445.     dc.RestoreBrush();
  446.   }
  447. }
  448.  
  449. //
  450. // Default painting is just the border
  451. //
  452. void
  453. TGadget::Paint(TDC& dc)
  454. {
  455.   PaintBorder(dc);
  456. }
  457.  
  458. //
  459. // Request by the gadget window to query the gadget's desired size.
  460. // If shrink wrapping was requested just returns the size needed to
  461. // accomodate the borders and margins--derived classes add in the guts;
  462. // otherwise the current width/height are returned
  463. //
  464. // If this gadget is "WideAsPossible" then "size.cx" is ignored
  465. //
  466. void
  467. TGadget::GetDesiredSize(TSize& size)
  468. {
  469.   int  left, right, top, bottom;
  470.   GetOuterSizes(left, right, top, bottom);
  471.  
  472.   size.cx = ShrinkWrapWidth ? left+right : Bounds.Width();
  473.   size.cy = ShrinkWrapHeight ? top+bottom : Bounds.Height();
  474. }
  475.  
  476. //
  477. // Get the four total outer sizes in pixels which consists of the margins
  478. // plus the borders.
  479. //
  480. void
  481. TGadget::GetOuterSizes(int& left, int& right, int& top, int& bottom)
  482. {
  483.   if (Window) {
  484.     int  xBorder = TUIMetric::CxBorder;
  485.     int  yBorder = TUIMetric::CyBorder;
  486.  
  487.     Window->GetMargins(Margins, left, right, top, bottom);
  488.     left += Borders.Left * xBorder;
  489.     right += Borders.Right * xBorder;
  490.     top += Borders.Top * yBorder;
  491.     bottom += Borders.Bottom * yBorder;
  492.   }
  493. }
  494.  
  495. //
  496. // Get the inner working rectangle. Which is, by default, the Bounds minus
  497. // each of the outer sizes
  498. //
  499. void
  500. TGadget::GetInnerRect(TRect& innerRect)
  501. {
  502.   int  left, right, top, bottom;
  503.   GetOuterSizes(left, right, top, bottom);
  504.  
  505.   innerRect.left = left;
  506.   innerRect.right = Bounds.Width() - right;
  507.   innerRect.top = top;
  508.   innerRect.bottom = Bounds.Height() - bottom;
  509. }
  510.  
  511. //
  512. // Mouse response functions
  513. //
  514.  
  515. //
  516. // Mouse is entering this gadget. Called by gadget window if no other gadget
  517. // has capture
  518. //
  519. void
  520. TGadget::MouseEnter(uint /*modKeys*/, TPoint&)
  521. {
  522. }
  523.  
  524. //
  525. // Mouse is moving over this gadget. Called by gadget window only if this
  526. // gadget has captured the mouse
  527. //
  528. void
  529. TGadget::MouseMove(uint /*modKeys*/, TPoint&)
  530. {
  531. }
  532.  
  533. //
  534. // Mouse is leaving this gadget. Called by gadget window if no other gadget
  535. // has capture
  536. //
  537. void
  538. TGadget::MouseLeave(uint /*modKeys*/, TPoint&)
  539. {
  540.   PRECONDITION(Window);
  541.   PRECONDITION(Window->GetHandle());
  542.  
  543.   // Send a fake WM_MOUSEMOVE to the tooltip in case the user moved away
  544.   // from the gadget *very* quick without the window detecting mouse move
  545.   // messages.
  546.   //
  547.   TTooltip* tooltip = Window->GetTooltip();
  548.   if (tooltip && tooltip->IsWindow()) {
  549.  
  550.     TPoint crsPoint;
  551.     GetCursorPos(&crsPoint);
  552.     HWND hwnd = WindowFromPoint(crsPoint);
  553.     if (hwnd)
  554.       ::MapWindowPoints(HWND_DESKTOP, hwnd, LPPOINT(&crsPoint), 1);
  555.     else
  556.       hwnd = GetDesktopWindow();
  557.  
  558.     MSG msg;
  559.     msg.hwnd    = hwnd;
  560.     msg.message = WM_MOUSEMOVE;
  561.     msg.wParam  = 0;
  562.     msg.lParam  = MAKELPARAM(crsPoint.x, crsPoint.y);
  563.     tooltip->RelayEvent(msg);
  564.   }
  565. }
  566.  
  567. //
  568. // Mouse button down on this gadget, routed from parent.
  569. //
  570. void
  571. TGadget::LButtonDown(uint /*modKeys*/, TPoint&)
  572. {
  573.   if (TrackMouse)
  574.     Window->GadgetSetCapture(*this);
  575. }
  576.  
  577. //
  578. // Mouse button up on this gadget, routed from parent.
  579. //
  580. void
  581. TGadget::LButtonUp(uint /*modKeys*/, TPoint&)
  582. {
  583.   if (TrackMouse)
  584.     Window->GadgetReleaseCapture(*this);
  585. }
  586.  
  587. //
  588. //
  589. //
  590. void
  591. TGadget::RButtonDown(uint /*modKeys*/, TPoint&)
  592. {
  593.   // TGadget does nothing with right mouse messages.
  594.   // However, a derived gadgets may catch this event
  595. }
  596.  
  597. //
  598. //
  599. //
  600. void
  601. TGadget::RButtonUp(uint /*modKeys*/, TPoint&)
  602. {
  603.   // TGadget does nothing with right mouse messages.
  604.   // However, a derived gadgets may catch this event
  605. }
  606.  
  607. //----------------------------------------------------------------------------
  608.  
  609. //
  610. // Construct a separator gadget of a given size
  611. //
  612. TSeparatorGadget::TSeparatorGadget(int size, int id)
  613. :
  614.   TGadget(id)
  615. {
  616.   ShrinkWrapWidth = ShrinkWrapHeight = false;
  617.   SetEnabled(false);
  618.   SetVisible(false);
  619.  
  620.   // Default size to a sysMetric based value
  621.   //
  622.   if (!size)
  623.     size = TUIMetric::CxSizeFrame * 2;
  624.   Bounds.right = size; // * TUIMetric::CxBorder;
  625.   Bounds.bottom = size; // * TUIMetric::CyBorder;
  626. }
  627.  
  628. //
  629. // Overridden virtual called after gadget is inserted into window
  630. //
  631. void
  632. TSeparatorGadget::Inserted()
  633. {
  634.   BorderStyle = None;  // Prohibit our style from being changed
  635. }
  636.  
  637. //----------------------------------------------------------------------------
  638.  
  639. //
  640. // Construct a gadget that can be grabbed to resize the frame.
  641. //
  642. TSizeGripGadget::TSizeGripGadget(int id)
  643. :
  644.   TSeparatorGadget(TUIMetric::CxHScroll > TUIMetric::CyVScroll ?
  645.                    TUIMetric::CxHScroll :
  646.                    TUIMetric::CyVScroll, id)
  647. {
  648.   // Enable gadget so mouse hit testing handler will let cursor change
  649.   // when user moves over the size grip.
  650.   //
  651.   SetEnabled(true);
  652. }
  653.  
  654. //
  655. // Draw the resize gadget.
  656. //
  657. void
  658. TSizeGripGadget::Paint(TDC& dc)
  659. {
  660.   int  left, right, top, bottom;
  661.   GetOuterSizes(left, right, top, bottom);
  662.  
  663.   TRect innerRect;
  664.   innerRect.left = left;
  665.   innerRect.top = top;
  666.   innerRect.right = Bounds.Width() + 1;
  667.   innerRect.bottom = Bounds.Height() + 1;
  668.  
  669.   TUIPart part;
  670.   part.Paint(dc, innerRect, TUIPart::uiScroll, TUIPart::ScrollSizeGrip);
  671. }
  672.