//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1992, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of classes TGadget, TSeparatorGadget and TSizeGripGadget
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/defs.h>
#include <owl/gadget.h>
#include <owl/gadgetwi.h>
#include <owl/framewin.h>
#include <owl/tooltip.h>
#include <owl/uihelper.h>
#include <owl/uimetric.h>
#include <owl/theme.h>
 
using namespace std;
 
namespace owl {
 
 
OWL_DIAGINFO;
 
//
// Convert layout units to pixels using a given font height
//
// A layout unit is defined by dividing the font "em" into 8 vertical and 8
// horizontal segments
//
static int
layoutUnitsToPixels(int units, int fontHeight)
{
  const long  unitsPerEM = 8;
 
  return int((long(units) * fontHeight + unitsPerEM / 2) / unitsPerEM);
}
 
//
// Return true if the Id is a predefined gadget id.
//
static bool
predefinedGadgetId(int id)
{
  if (id == 0 || id == -1 || (IDG_FIRST <= id && id < IDG_LAST)) {
    return true;
  }
  return false;
}
 
//
// Retrieve the sizes of the 4 margins in pixels given a font height
//
void
TMargins::GetPixels(int& left, int& right, int& top, int& bottom, int fontHeight) const
{
  switch (Units) {
    case Pixels:
      left = Left;
      top = Top;
      right = Right;
      bottom = Bottom;
      break;
 
    case LayoutUnits:
      left = layoutUnitsToPixels(Left, fontHeight);
      top = layoutUnitsToPixels(Top, fontHeight);
      right = layoutUnitsToPixels(Right, fontHeight);
      bottom = layoutUnitsToPixels(Bottom, fontHeight);
      break;
 
    case BorderUnits:
      int  cxBorder = TUIMetric::CxBorder;
      int  cyBorder = TUIMetric::CyBorder;
 
      left = Left * cxBorder;
      top = Top * cyBorder;
      right = Right * cxBorder;
      bottom = Bottom * cyBorder;
      break;
  }
}
 
//
/// Construct a gadget with a given id and border style. Used by derived
/// classes.
//
TGadget::TGadget(int id, TBorderStyle borderStyle)
{
  Window = 0;
  Bounds = TRect(0, 0, 0, 0);
  Flags = Enabled | Visible;
  TrackMouse = false;
  MouseInGadget = false;
  Clip = false;
  WideAsPossible = false;
  ShrinkWrapWidth = ShrinkWrapHeight = true;
  Next = 0;
  Id = id;
 
  SetBorderStyle(borderStyle);
}
 
//
/// Destroys a TGadget interface object and removes it from its associated window.
//
TGadget::~TGadget()
{
  // If we're in a window, remove ourselves.
  //
  if (Window)
    Window->Remove(*this);
}
 
//
/// Called during idle time to allow the gadget to perform any idle actions. TGadget
/// performs command enabling on first call in each idle period.
//
bool
TGadget::IdleAction(long idleCount)
{
  if (idleCount == 0)
    CommandEnable();
  return false;
}
 
//
/// Provided so that the gadget can perform command enabling (so it can handle an
/// incoming message if it's appropriate to do so).
//
void
TGadget::CommandEnable()
{
}
 
//
/// Called when the system colors have been changed so that gadgets can rebuild and
/// repaint, if necessary.
//
void
TGadget::SysColorChange()
{
}
 
//
// A way to detect whether a button is created and visible
//
bool
TGadget::IsWindowVisible() const
{
  if (Window)
    return Window->IsWindowVisible();
  else
    return false;
}
 
//
/// Simple set accessor to set whether shrinkwrapping is performed horizontally
/// and/or vertically.
///
/// Your derived class can call TGadgetWindow::GadgetChangedSize()
/// if you want to change the size of the gadget.
//
void
TGadget::SetShrinkWrap(bool shrinkWrapWidth, bool shrinkWrapHeight)
{
  ShrinkWrapWidth = shrinkWrapWidth;
  ShrinkWrapHeight = shrinkWrapHeight;
}
 
//
/// Alters the size of the gadget and then calls TGadgetWindow::GadgetChangedSize()
/// for the size change to take effect.
/// This function is needed only if you have turned off shrink-wrapping in one or
/// both dimensions; otherwise, use the GetDesiredSize() member function to return the
/// shrink-wrapped size.
//
void
TGadget::SetSize(const TSize& size)
{
  Bounds.right = Bounds.left + size.cx;
  Bounds.bottom = Bounds.top + size.cy;
 
  if (Window)
    Window->GadgetChangedSize(*this);
}
 
//
/// Enables or disables keyboard and mouse input for the gadget. By default, the
/// gadget is disabled when it is created and must be enabled before it can be
/// activated.
//
void
TGadget::SetEnabled(bool enabled)
{
  if (ToBool(Flags & Enabled) != enabled) {
    if (enabled)
      Flags |= Enabled;
    else
      Flags &= ~Enabled;
    Invalidate(true);
  }
}
 
//
/// Called by the gadget window to inform the gadget of a change in its bounding
/// rectangle. Default behavior here just updates instance variable "Bounds" but
/// derived classes might override this method to update other internal state.
//
void
TGadget::SetBounds(const TRect& rect)
{
  if (rect != Bounds) {
    Bounds = rect;
    Moved();
  }
}
 
//
/// Sets the borders for the gadget. If the borders are changed, SetBorders calls
/// TGadgetWindow::GadgetChangedSize() to notify the gadget window of the change.
//
void
TGadget::SetBorders(const TBorders& borders)
{
  Borders = borders;
 
  if (Window)
    Window->GadgetChangedSize(*this);
}
 
//
/// Sets the margins of the gadget. If the margins are changed, SetMargins calls
/// TGadgetWindow::GadgetChangedSize() to notify the gadget window.
//
void
TGadget::SetMargins(const TMargins& margins)
{
  Margins = margins;
 
  if (Window)
    Window->GadgetChangedSize(*this);
}
 
//
/// Set the border style used by this gadget. Internal Border members are
/// updated and owning Window is notified of a size change.
//
void
TGadget::SetBorderStyle(TBorderStyle borderStyle)
{
  BorderStyle = borderStyle;
 
  int  edgeThickness;
  switch (BorderStyle) {
    default:
    case None:
      edgeThickness = 0;
      break;
 
    case Plain:
    case Raised:
    case Recessed:
      edgeThickness = 1;
      break;
 
    case Embossed:
//      edgeThickness = 3;
    case Grooved:
    case ButtonUp:
    case ButtonDn:
    case WndRaised:
    case WndRecessed:
      edgeThickness = 2;
      break;
  }
 
  Borders.Left = Borders.Top = Borders.Right = Borders.Bottom = edgeThickness;
 
  if (Window)
    Window->GadgetChangedSize(*this);
}
 
//
/// Determines if the point is within the receiver's bounding rectangle and returns
/// true if this is the case; otherwise, returns false.
//
bool
TGadget::PtIn(const TPoint& point)
{
  return IsVisible() &&
         point.x >= 0 && point.y >= 0 &&
         point.x < Bounds.Width() && point.y < Bounds.Height();
}
 
//
/// This is the virtual called after the window holding a gadget has been created.
//
void
TGadget::Created()
{
  PRECONDITION(Window);
  PRECONDITION(Window->GetHandle());
 
  // Register ourself with the tooltip window (if there's one)
  //
  TTooltip* tooltip = Window->GetTooltip();
  if (tooltip && tooltip->IsWindow()) {
 
    // Don't register gadget's with Id's of 0 or -1.
    // Typically, 0 is reserved by separators and -1 could
    // be used for dumb text gadgets...
    //
    if (!predefinedGadgetId(Id))
      tooltip->AddTool({Window->GetHandle(), static_cast<uint>(Id)}, Bounds);
  }
}
 
//
/// Called after a gadget is inserted into a window.
//
void
TGadget::Inserted()
{
}
 
//
/// Virtual called after a gadget is removed from a window.
//
void
TGadget::Removed()
{
  // Unregister ourself with the tooltip window (if there's one)
  //
  if (Window && Window->GetHandle()) {
    TTooltip* tooltip = Window->GetTooltip();
    if (tooltip && tooltip->IsWindow()) {
 
      // Don't bother with gadgets with Id's of 0 or -1.
      // Typically, 0 is reserved by separators and -1 could
      // be used for dumb text gadgets...
      //
      if (!predefinedGadgetId(Id))
        tooltip->DelTool(TTooltip::TRectToolId{Window->GetHandle(), static_cast<uint>(Id)});
    }
  }
}
 
//
/// This is the virtual called when a gadget is relocated.
//
void
TGadget::Moved()
{
  // Send tooltip window our updated location
  //
  if (Window && Window->GetHandle()) {
    TTooltip* tooltip = Window->GetTooltip();
    if (tooltip && tooltip->IsWindow()) {
 
      // Don't bother with gadgets with Id's of 0 or -1.
      // Typically, 0 is reserved by separators and -1 could
      // be used for dumb text gadgets...
      //
      if (!predefinedGadgetId(Id))
      {
        const auto toolId = TTooltip::TRectToolId{*Window, static_cast<uint>(Id)};
        if (tooltip->GetTool(toolId)->GetBoundingBox() != Bounds)
          tooltip->NewToolRect(toolId, Bounds);
      }
    }
  }
}
 
//
/// Invalidate a rectangle in our containing window. Rectangle is specified
/// in gadget coordinates.
//
void
TGadget::InvalidateRect(const TRect& rect, bool erase)
{
  if (Window && Window->GetHandle()) {
    TRect  updateRect(rect.left + Bounds.left, rect.top + Bounds.top,
                      rect.right + Bounds.left, rect.bottom + Bounds.top);
 
    Window->InvalidateRect(updateRect, erase);
  }
}
 
//
/// Used to invalidate the active (usually nonborder) portion of the gadget,
/// Invalidate calls InvalidateRect and passes the boundary width and height of the
/// area to erase.
//
void
TGadget::Invalidate(bool erase)
{
  InvalidateRect(TRect(0, 0, Bounds.Width(), Bounds.Height()), erase);
}
 
//
/// Repaints the gadget if possible.
//
void
TGadget::Update()
{
  if (Window && Window->GetHandle())
    Window->UpdateWindow();
}
 
//
/// Used to paint the border, PaintBorder determines the width and height of the
/// gadget and uses the color returned by GetSyscolor to paint or highlight the area
/// with the specified brush. Depending on whether the border style is raised,
/// embossed, or recessed, PaintBorder paints the specified boundary. You can
/// override this function if you want to implement a border style that is not
/// supported by ObjectWindows' gadgets.
//
void
TGadget::PaintBorder(TDC& dc)
{
  if (BorderStyle != None) {
    int  xB = TUIMetric::CxBorder;
    int  yB = TUIMetric::CyBorder;
 
    TRect boundsRect(0,0,Bounds.Width(),Bounds.Height());
    if (BorderStyle == Plain) {
      TBrush winBru(TColor::SysWindowFrame);
      dc.OWLFastWindowFrame(winBru, boundsRect,xB, yB);
      dc.RestoreBrush();
    }
    else {
      if(Window->GetFlatStyle()&TGadgetWindow::FlatStandard){
        TUIBorder(boundsRect, (uint)BorderStyle == (uint)TUIBorder::Recessed ?
                  TUIBorder::SunkenOuter : TUIBorder::RaisedInner,
                  TUIBorder::Rect).Paint(dc);
      }
      else{
        // Use the 1:1 mapping of BorderStyle to TUIBorder::TStyle
        TUIBorder(boundsRect, TUIBorder::TStyle(BorderStyle)).Paint(dc);
      }
    }
  }
}
 
//
/// Calls PaintBorder() to paint the indicated device context.
//
void
TGadget::Paint(TDC& dc)
{
  PaintBorder(dc);
}
 
//
/// Request by the gadget window to query the gadget's desired size.
//
/// Determines how big the gadget can be. The gadget window sends this message to
/// query the gadget's size. If shrink-wrapping is requested, GetDesiredSize returns
/// the size needed to accommodate the borders and margins. If shrink-wrapping is
/// not requested, it returns the gadget's current width and height. TGadgetWindow
/// needs this information to determine how big the gadget needs to be, but it can
/// adjust these dimensions if necessary. If WideAsPossible is true, then the width
/// parameter (size.cx) is ignored.
//
void
TGadget::GetDesiredSize(TSize& size)
{
  int left  = 0;
  int right = 0;
  int top   = 0;
  int bottom = 0;
  GetOuterSizes(left, right, top, bottom);
 
  size.cx = ShrinkWrapWidth ? left+right : Bounds.Width();
  size.cy = ShrinkWrapHeight ? top+bottom : Bounds.Height();
}
 
//
/// Get the four total outer sizes in pixels which consists of the margins
/// plus the borders.
//
void
TGadget::GetOuterSizes(int& left, int& right, int& top, int& bottom)
{
  if (Window) {
    int  xBorder = TUIMetric::CxBorder;
    int  yBorder = TUIMetric::CyBorder;
 
    Window->GetMargins(Margins, left, right, top, bottom);
    left += Borders.Left * xBorder;
    right += Borders.Right * xBorder;
    top += Borders.Top * yBorder;
    bottom += Borders.Bottom * yBorder;
  }
}
 
//
/// Computes the area of the gadget's rectangle excluding the borders and margins.
//
void
TGadget::GetInnerRect(TRect& innerRect)
{
  int left  = 0;
  int right = 0;
  int top   = 0;
  int bottom = 0;
  GetOuterSizes(left, right, top, bottom);
 
  innerRect.left = left;
  innerRect.right = Bounds.Width() - right;
  innerRect.top = top;
  innerRect.bottom = Bounds.Height() - bottom;
}
 
//
// Mouse response functions
//
 
//
/// Mouse is entering this gadget. Called by gadget window if no other gadget
/// has capture.
///
/// For toolbars with the flat style we want to allow the gadget to redraw with
/// a slightly different style (e.g. border) when the mouse is hovering over
/// the gadget. We handle this here by setting a flag (MouseInGadget) and then
/// invalidating the gadget.
//
void
TGadget::MouseEnter(uint /*modKeys*/, const TPoint&)
{
  PRECONDITION(Window);
    MouseInGadget = true;
    if(Window->GetFlatStyle()&TGadgetWindow::FlatStandard)
      Invalidate(); // is flat style -> repaint gadget TWindow
}
 
//
/// Mouse is moving over this gadget. Called by gadget window only if this
/// gadget has captured the mouse
///
/// point is located in the receiver's coordinate system.
//
void
TGadget::MouseMove(uint /*modKeys*/, const TPoint&)
{
}
 
//
/// Mouse is leaving this gadget. Called by gadget window if no other gadget
/// has capture
//
void
TGadget::MouseLeave(uint /*modKeys*/, const TPoint&)
{
  PRECONDITION(Window);
  PRECONDITION(Window->GetHandle());
 
  // Send a fake WM_MOUSEMOVE to the tooltip in case the user moved away
  // from the gadget *very* quick without the window detecting mouse move
  // messages.
  //
  TTooltip* tooltip = Window->GetTooltip();
  if (tooltip && tooltip->IsWindow()) {
 
    TPoint crsPoint;
    GetCursorPos(&crsPoint);
    HWND hwnd = WindowFromPoint(crsPoint);
    if (hwnd)
      ::MapWindowPoints(HWND_DESKTOP, hwnd, LPPOINT(&crsPoint), 1);
    else
      hwnd = GetDesktopWindow();
 
    MSG msg;
    msg.hwnd    = hwnd;
    msg.message = WM_MOUSEMOVE;
    msg.wParam  = 0;
    msg.lParam  = MkUint32((uint16)crsPoint.x, (uint16)crsPoint.y);
    tooltip->RelayEvent(msg);
  }
  //
  // for flat a la IE 3.0 toolbar
  //
  if(MouseInGadget){
    MouseInGadget = false;
    if(Window->GetFlatStyle()&TGadgetWindow::FlatStandard)
      Invalidate(); // is flat style -> repaint gadget
  }
}
 
//
/// Captures the mouse if TrackMouse is set. point is located in the gadget's
/// coordinate system.
//
void
TGadget::LButtonDown(uint /*modKeys*/, const TPoint&)
{
  if (TrackMouse)
    Window->GadgetSetCapture(*this);
}
 
//
/// Releases the mouse capture if TrackMouse is set. point is located in the
/// gadget's coordinate system.
//
void
TGadget::LButtonUp(uint /*modKeys*/, const TPoint&)
{
  if (TrackMouse)
    Window->GadgetReleaseCapture(*this);
}
 
//
//
//
void
TGadget::RButtonDown(uint /*modKeys*/, const TPoint&)
{
  // TGadget does nothing with right mouse messages.
  // However, a derived gadgets may catch this event
}
 
//
//
//
void
TGadget::RButtonUp(uint /*modKeys*/, const TPoint&)
{
  // TGadget does nothing with right mouse messages.
  // However, a derived gadgets may catch this event
}
 
//----------------------------------------------------------------------------
const int DefaultGripSize = 6;
 
 
//
/// Used for both the width and the height of the separator, the default size is
/// TUIMetric::CxSizeFrame times two. id is the ID of the TGadget object.
//
TSeparatorGadget::TSeparatorGadget(int size, int id, bool showsep)
:
  TGadget(id),
  ShowSeparator(showsep)
{
  ShrinkWrapWidth = ShrinkWrapHeight = false;
  SetEnabled(false);
  SetVisible(false);
 
  // Default size to a sysMetric based value
  //
  if (!size)
    size = TUIMetric::CxSizeFrame * 2;
  Bounds.right  = size;
  Bounds.bottom = size;
}
 
//
/// This is an overridden virtual, called after a gadget is inserted into a window.
//
void
TSeparatorGadget::Inserted()
{
  BorderStyle = None;  // Prohibit our style from being changed
}
 
//
//
//
void
TSeparatorGadget::Paint(TDC& dc)
{
  TGadget::Paint(dc); // Does nothing since BorderStyle is None.
 
  bool flat = GetGadgetWindow()->GetFlatStyle() & TGadgetWindow::FlatStandard;
  if (flat && ShowSeparator)
  {
    const bool horizontal = GetGadgetWindow()->GetDirection() == TGadgetWindow::Horizontal;
 
    // Calculate border.
    //
    TSize border;
    bool xpstyle = GetGadgetWindow()->IsThemed();
    if (xpstyle)
      border = TSize(TUIMetric::CxBorder, TUIMetric::CyBorder);
    else
      border = TSize(TUIMetric::CxSizeFrame, TUIMetric::CySizeFrame);
 
    // Expand the bounds rectangle around the separator so that its
    // height (horizontal) or width (vertical) matches the gadget window.
    //
    TRect bounds = Bounds.MovedTo(0, 0);
    const TRect bar = GetGadgetWindow()->GetClientRect();
    bounds.Inflate
      (
      horizontal ? 0 : (bar.Width() - Bounds.Width())/2 - border.cx,
      !horizontal ? 0 : (bar.Height() - Bounds.Height())/2 - border.cy
      );
 
    // Draw the separator in the active style.
    //
    if (xpstyle)
    {
      int part_id = horizontal ? TP_SEPARATOR : TP_SEPARATORVERT;
      TThemePart part(GetGadgetWindow()->GetHandle(), (LPCWSTR) L"TOOLBAR", part_id, TS_NORMAL);
      part.DrawBackground(dc, bounds);
    }
    else // old flat style
    {
      // Narrow the drawing rectangle to 2 pixels in the center.
      //
      if (horizontal)
      {
        bounds.left = bounds.Width()/2-1;
        bounds.right = bounds.left+2;
      }
      else // vertical
      {
        bounds.top = bounds.Height()/2-1;
        bounds.bottom = bounds.top+2;
      }
      uint flags = horizontal ? TUIBorder::Right : TUIBorder::Top;
      TUIBorder::DrawEdge(dc, bounds, TUIBorder::Embossed, flags);
    }
  }
}
 
//
//------------------------------------------------------------------------------
//
TFlatHandleGadget::TFlatHandleGadget(int id)
:
  TSeparatorGadget(TUIMetric::CySizeFrame > TUIMetric::CxSizeFrame ?
                   TUIMetric::CySizeFrame :
                   TUIMetric::CxSizeFrame, id)
{
  // Enable gadget so mouse hit testing handler will let cursor change
  // when user moves over the size grip.
  //
  SetEnabled(true);
}
 
 
//
void
TFlatHandleGadget::GetDesiredSize(TSize& size)
{
  TSeparatorGadget::GetDesiredSize(size);
 
  if(GetGadgetWindow()->GetDirection() == TGadgetWindow::Rectangular)
    size.cx = size.cy = 0;
  else{
    TSize maxsize;
    for (TGadget* gadg = GetGadgetWindow()->FirstGadget(); gadg; gadg = GetGadgetWindow()->NextGadget(*gadg)){
      int id = gadg->GetId();
      if (id != IDG_FLATHANDLE && gadg != this){
        TSize gsize(0, 0);
        gadg->GetDesiredSize(gsize);
        maxsize.cx = std::max(gsize.cx,maxsize.cx);
         maxsize.cy = std::max(gsize.cy,maxsize.cy);
      }
    }
 
    if (Window->GetDirection()==TGadgetWindow::Horizontal){
      size.cx = DefaultGripSize;
      size.cy = maxsize.cy;
    }
    else if (Window->GetDirection()==TGadgetWindow::Vertical){
      size.cx = maxsize.cx;
      size.cy = DefaultGripSize+1;
    }
  }
}
 
// Draw the resize gadget.
//
void
TFlatHandleGadget::Paint(TDC& dc)
{
  if(Window->GetDirection()==TGadgetWindow::Horizontal){
    TRect boundsRect = Bounds;
    dc.DPtoLP((TPoint*)&boundsRect, 2);
    boundsRect.right = boundsRect.left+3;
    if(Window->GetFlatStyle()&TGadgetWindow::FlatSingleDiv){
      boundsRect.Offset(-2,0);
      TUIBorder::DrawEdge(dc, boundsRect, TUIBorder::RaisedInner, TUIBorder::Rect);
    }
    else{
      boundsRect.Offset(-3,0);
      TUIBorder::DrawEdge(dc, boundsRect, TUIBorder::RaisedInner, TUIBorder::Rect);
      boundsRect.Offset(3,0);
      TUIBorder::DrawEdge(dc, boundsRect, TUIBorder::RaisedInner, TUIBorder::Rect);
    }
  }
  else if(Window->GetDirection()==TGadgetWindow::Vertical){
    TRect boundsRect = Bounds;
    dc.DPtoLP((TPoint*)&boundsRect, 2);
    boundsRect.bottom = boundsRect.top+3;
    if(Window->GetFlatStyle()&TGadgetWindow::FlatSingleDiv)
      TUIBorder::DrawEdge(dc, boundsRect, TUIBorder::RaisedInner, TUIBorder::Rect);
    else{
      boundsRect.Offset(0,-1);
      TUIBorder::DrawEdge(dc, boundsRect, TUIBorder::RaisedInner, TUIBorder::Rect);
      boundsRect.Offset(0,3);
      TUIBorder::DrawEdge(dc, boundsRect, TUIBorder::RaisedInner, TUIBorder::Rect);
    }
  }
}
//------------------------------------------------------------------------------
 
//
/// Constructs a gadget that can be grabbed to resize the frame.
//
TSizeGripGadget::TSizeGripGadget(int id)
:
  TSeparatorGadget(TUIMetric::CxHScroll > TUIMetric::CyVScroll ?
                   TUIMetric::CxHScroll :
                   TUIMetric::CyVScroll, id)
{
  // Enable gadget so mouse hit testing handler will let cursor change
  // when user moves over the size grip.
  //
  SetEnabled(true);
}
 
//
/// Draws the resize gadget.
//
void
TSizeGripGadget::Paint(TDC& dc)
{
  int left  = 0;
  int right = 0;
  int top   = 0;
  int bottom = 0;
  GetOuterSizes(left, right, top, bottom);
 
  TRect innerRect;
  innerRect.left = left;
  innerRect.top = top;
  innerRect.right = Bounds.Width() + 1;
  innerRect.bottom = Bounds.Height() + 1;
 
  TUIPart part;
  part.Paint(dc, innerRect, TUIPart::uiScroll, TUIPart::ScrollSizeGrip);
}
 
} // OWL namespace
/* ========================================================================== */
 

V1027 Pointer to an object of the 'TRect' class is cast to unrelated 'TPoint' class.

V1027 Pointer to an object of the 'TRect' class is cast to unrelated 'TPoint' class.