//----------------------------------------------------------------------------//
// ObjectWindows 1998 Copyright by Yura Bidus                                 //
//                                                                            //
// Used code and ideas from Dieter Windau and Joseph Parrello                 //
//                                                                            //
// THIS CLASS AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF         //
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO        //
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A             //
// PARTICULAR PURPOSE.                                                        //
/// \file                                                                           //
/// Implementation of class TButtonTextGadget.                                 //
//----------------------------------------------------------------------------//
#include <owl/pch.h>
 
#include <owl/celarray.h>
#include <owl/gadgetwi.h>
#include <owl/uihelper.h>
#include <owl/btntextg.h>
#include <owl/decframe.h>
 
using namespace std;
 
namespace owl {
 
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlGadget);
 
//------------------------------------------------------------------------------
//
/// \class TButtonTextGadgetEnabler
/// Command enabler for button text gadget
//
 
#if defined(BI_COMP_BORLANDC)
# pragma warn -inl
#endif
 
class TButtonTextGadgetEnabler
  : public TCommandEnabler
{
public:
  TButtonTextGadgetEnabler(TWindow::THandle hReceiver, TButtonTextGadget* g)
    : TCommandEnabler(g->GetId(), hReceiver), Gadget(g)
  {}
 
  //
  // Override TCommandEnabler virtuals.
  //
  virtual void Enable(bool);
  virtual void SetText(LPCTSTR text);
  void SetText(const tstring& s) {SetText(s.c_str());}
  virtual void SetCheck(int);
 
 
protected:
  TButtonTextGadget*  Gadget;
};
 
#if defined(BI_COMP_BORLANDC)
# pragma warn .inl
#endif
 
//
/// Enable or disable the button gadget
//
void
TButtonTextGadgetEnabler::Enable(bool enable)
{
  TCommandEnabler::Enable(enable);
  Gadget->SetEnabled(enable);
}
 
//
/// Handle the SetText request for a button-text gadget.
//
void
TButtonTextGadgetEnabler::SetText(LPCTSTR text)
{
  if(Gadget->GetStyle()& TButtonTextGadget::sText)
    Gadget->SetText(text);
}
 
//
/// Set the check-state for the button gadget
//
void
TButtonTextGadgetEnabler::SetCheck(int state)
{
  Gadget->SetButtonState(TButtonTextGadget::TState(state));
}
 
//------------------------------------------------------------------------------
 
const int TextSpaceV = 0;
const int TextSpaceH = 0;
 
//
/// Constructs a TButtonTextGadget object using the specified bitmap ID, button
/// gadget ID, style, and type, with enabled set to false, in a button-up state, and
/// reserved number chars in text = 4. The button isn't enabled - its initial state
/// before command enabling occurs.
//
TButtonTextGadget::TButtonTextGadget(
  int id, TResId glyphResIdOrIndex,
  TStyle style, TType type,
  bool enabled, TState state,
  bool sharedGlyph, uint numChars)
:
  TButtonGadget(glyphResIdOrIndex, id, type, enabled, state, sharedGlyph),
  Text(),
  NumChars(numChars),
  Align(aCenter),
  Style(style),
  LayoutStyle(lTextBottom),
  Font(0)
{}
 
//
/// Destroys a TButtonTextGadget object.
//
TButtonTextGadget::~TButtonTextGadget()
{
  delete Font;
}
 
//
/// Initiates a command enable for this button gadget.
/// Enables the button-text gadget to capture messages. Calls SendMessage to send a
/// WM_COMMAND_ENABLE message to the gadget window's parent, passing a
/// TCommandEnable:EvCommandEnable message for this button.
//
void
TButtonTextGadget::CommandEnable()
{
  PRECONDITION(Window);
 
  // Must send, not post here, since a ptr to a temp is passed
  //
  // This might be called during idle processing before the
  // HWND has created.  Therefore, confirm handle exists.
  //
  if (GetGadgetWindow()->GetHandle()) {
    TButtonTextGadgetEnabler ge(*GetGadgetWindow(), this);
    GetGadgetWindow()->HandleMessage(WM_COMMAND_ENABLE, 0,TParam2(&ge));
  }
}
 
//
/// Sets the text of the gadget. If the given text is blank, then we attempt to
/// load the text from the menu or tooltip.
//
void
TButtonTextGadget::SetText(const tstring& text, bool repaint)
{
  if (text == Text) return;
  Text = text;
 
  if (Text.length() == 0 && (Style & sText) && GetGadgetWindow())
  {
    TWindow* parent = GetGadgetWindow()->GetParentO();
    TDecoratedFrame* frame= parent ? dynamic_cast<TDecoratedFrame*>(parent) : 0;
    while (parent && !frame){
      parent = parent->GetParentO();
      if (parent)
        frame = dynamic_cast<TDecoratedFrame*>(parent);
    }
    CHECK(frame);
    Text = frame->GetHintText(GetResId().GetInt(), htTooltip);
  }
 
  if (GetGadgetWindow() && repaint)
  {
    GetGadgetWindow()->GadgetChangedSize(*this);
    Invalidate();
  }
}
 
//
/// Returns the effective font used to render the text for this gadget.
/// If no font was passed to the constructor, the font of the gadget window is returned.
//
const TFont&
TButtonTextGadget::GetFont() const
{
  PRECONDITION(GetGadgetWindow());
  return Font ? *Font : GetGadgetWindow()->GetFont();
}
 
//
/// Sets the font to be used by the gadget. If repaint is true, calls
/// TGadgetWindow::GadgetChangedSize to recalculate the size of the gadget.
//
void
TButtonTextGadget::SetFont(const TFont& font, bool repaint)
{
  delete Font;
  Font = new TFont(font);
  if (GetGadgetWindow() && repaint)
    GetGadgetWindow()->GadgetChangedSize(*this);
}
 
//
/// If the style stored in Style is not the same as the new style, SetStyle sets
/// Style to the new style, and then if repaint is true calls
/// Window->GadgetChangedSize(*this) to recalculate size of gadget.
//
void
TButtonTextGadget::SetStyle(TStyle style, bool repaint)
{
  if(Style != style){
    Style = style;
    if (GetGadgetWindow() && repaint)
      GetGadgetWindow()->GadgetChangedSize(*this);
  }
}
 
//
/// If the align stored in Style is not the same as the new align, SetAlign sets
/// Align to the new align, and then if repaint is true calls Invalidate to
/// invalidate the rectangle.
//
void
TButtonTextGadget::SetAlign(const TAlign align, bool repaint)
{
  if(Align != align){
    Align = align;
    if(repaint)
      Invalidate();
  }
}
 
//
/// If the style stored in LayoutStyle is not the same as the new style,
/// SetLayoutStyle sets LayoutStyle to the new style, and then if repaint is true
/// calls Window->GadgetChangedSize(*this) to recalculate size of gadget.
//
void
TButtonTextGadget::SetLayoutStyle(const TLayoutStyle style, bool repaint)
{
  if(LayoutStyle != style){
    LayoutStyle = style;
    if (GetGadgetWindow() && repaint)
      GetGadgetWindow()->GadgetChangedSize(*this);
  }
}
 
 
//
/// Calls TButtonGadget::Created and if Text == 0 and (Style & sText) retrieves text
/// from menu or resource.
///
/// Virtual called after the window holding a gadget has been created
//
void
TButtonTextGadget::Created()
{
  PRECONDITION(Window);
  PRECONDITION(Window->GetHandle());
 
  TButtonGadget::Created();
 
  // If text style and text isn't set, then get it now from the menu/resource.
  //
  if ((Style & sText) && Text.length() == 0)
  {
    SetText(_T(""));
    Invalidate();
  }
}
 
//
/// Respond to a WM_SYSCOLORCHANGE, in this case to rebuild the CelArray with
/// possibly new 3d colors
///
/// SysColorChange responds to an EvSysColorChange message forwarded by the owning
/// TGadgetWindow by setting the dither brush to zero. If a style is Bitmap it
/// forwards the message to TButtonGadget, otherwise to TGadget.
//
void
TButtonTextGadget::SysColorChange()
{
  // Baypass TButtonGadget handler if CellArray = 0
  if(Style&sBitmap)
    TButtonGadget::SysColorChange();
  else
    TGadget::SysColorChange();
}
 
//
void
TButtonTextGadget::GetTextSize(TSize& size)
{
  TEXTMETRIC tm = GetFont().GetTextMetrics();
  size.cx += tm.tmAveCharWidth * NumChars;
  size.cy += tm.tmHeight + 2;
}
 
//
/// Calls TButtonGadget::GetDesiredSize  if (Style & sBitmap); calls
/// TGadget::GetDesiredSize and adds the size of the text region.
//
void
TButtonTextGadget::GetDesiredSize(TSize& size)
{
  PRECONDITION(Window);
  TRACEX(OwlGadget, 1, _T("TButtonTextGadget::GetDesiredSize() enter @") << this <<
    _T(" size ") << size);
 
  if(Style&sBitmap)
    TButtonGadget::GetDesiredSize(size);
  else
    TGadget::GetDesiredSize(size);
 
  // if paint text -> add text size
  if(Style&sText){
    TSize textSize;
    GetTextSize(textSize);
 
    TSize gsize;
    TGadget::GetDesiredSize(gsize);
    switch(LayoutStyle){
       case lTextLeft:
       case lTextRight:
         size.cx += textSize.cx + TextSpaceV;
         size.cy =  std::max(size.cy,textSize.cy+gsize.cy);
         break;
       case lTextTop:
       case lTextBottom:
         size.cx += std::max(size.cx,textSize.cx+gsize.cx);
         size.cy += textSize.cy + TextSpaceH;
         break;
    }
  }
  TRACEX(OwlGadget, 1, _T("TButtonTextGadget::GetDesiredSize() leave @") << this <<
    _T(" size ") << size);
}
 
//
/// If (Style & sBitmap) calls TButtonGadget::SetBounds; otherwise calls
/// TGadget::SetBounds to set the boundary of the rectangle, and it (Style &
/// sBitmap) calculates new BitmapOrigin.
//
void
TButtonTextGadget::SetBounds(const TRect& rect)
{
  PRECONDITION(Window);
 
  if(Style&sBitmap)
    TButtonGadget::SetBounds(rect);
  else
    TGadget::SetBounds(rect);
 
  if(Style&sBitmap && Style&sText){
    TRect  faceRect, textRect, btnRect;
    GetInnerRect(faceRect);
 
    Layout(faceRect, textRect, btnRect);
 
    TSize  bitmapSize = GetCelArray() ? GetCelArray()->CelSize() : GetGadgetWindow()->GetCelArray().CelSize();
    TPoint pt;
    pt.x = btnRect.left + (btnRect.Width() - bitmapSize.cx) / 2;
    pt.y = btnRect.top + (btnRect.Height() - bitmapSize.cy) / 2;
    SetBitmapOrigin(pt);
  }
}
 
//
/// Calls TGadget::PaintBorder to perform the actual painting of the border of the
/// control. Get Inner rectangle of control, layout Text and Bitmap and call
/// PaintText() and PaintFace().
//
void
TButtonTextGadget::Paint(TDC& dc)
{
  PRECONDITION(Window);
 
  PaintBorder(dc);
 
  TRect  faceRect, textRect, btnRect;
  GetInnerRect(faceRect);
 
  Layout(faceRect, textRect, btnRect);
 
  if(Style&sText)
    PaintText(dc, textRect, Text);
  if(Style&sBitmap)
    PaintFace(dc, btnRect);
}
 
//
/// Layout button and text in the control area.
//
void
TButtonTextGadget::Layout(TRect& faceRect, TRect& textRect, TRect& btnRect)
{
  if(Style == sBitmap)
    btnRect = faceRect;
  else if(Style == sText)
    textRect = faceRect;
  else{
    TSize textSize;
    if(Style&sText)
      GetTextSize(textSize);
 
    TSize btnSize;
    if(Style&sBitmap)
      TButtonGadget::GetDesiredSize(btnSize);
 
    switch(LayoutStyle){
 
       case lTextLeft:
        if(Style&sText)
          textRect = TRect(faceRect.TopLeft(), TSize(textSize.cx,faceRect.Height()));
        if(Style&sBitmap)
          btnRect = TRect(TPoint(faceRect.left+textSize.cx+TextSpaceV,faceRect.top),
                          faceRect.BottomRight());
         break;
 
       case lTextRight:
        if(Style&sBitmap)
          btnRect = TRect(faceRect.left, faceRect.top,
                          faceRect.left+btnSize.cx, faceRect.bottom);
        if(Style&sText)
          textRect = TRect(btnRect.right+TextSpaceV,faceRect.top,
                           faceRect.right, faceRect.bottom);
 
         break;
 
       case lTextTop:
        if(Style&sText)
          textRect = TRect(faceRect.TopLeft(), TSize(faceRect.Width(),textSize.cy));
        if(Style&sBitmap)
          btnRect = TRect(TPoint(faceRect.left,faceRect.top+textSize.cy+TextSpaceH),
                          TSize(faceRect.Width(), faceRect.Height()-textSize.cy));
         break;
 
       case lTextBottom:
        if(Style&sText)
          textRect = TRect(faceRect.left,faceRect.bottom-textSize.cy,
                           faceRect.right,faceRect.bottom);
        if(Style&sBitmap)
          btnRect = TRect(faceRect.TopLeft(),
                          TSize(faceRect.Width(), faceRect.Height()-textSize.cy));
         break;
    }
  }
}
 
//
/// Paint Text
//
void
TButtonTextGadget::PaintText(TDC& dc, TRect& rect, const tstring& text)
{
  dc.SelectObject(GetFont());
 
  TColor textColor = TColor::SysBtnText;
  if(!GetEnabled())
    textColor = TColor::Sys3dHilight;
  else if((GetGadgetWindow()->GetFlatStyle()&TGadgetWindow::FlatHotText) && IsHaveMouse())
     textColor = TColor::LtBlue;
 
   TColor oldTxColor  = dc.SetTextColor(textColor);
 
  uint format =  DT_SINGLELINE|DT_NOCLIP|DT_END_ELLIPSIS;
  switch(Align){
    case aLeft:
      format |= DT_LEFT;
      break;
    case aRight:
      format |= DT_RIGHT;
      break;
    case aCenter:
      format |= DT_CENTER;
      break;
  }
  switch(LayoutStyle){
    case lTextLeft:
    case lTextRight:
      format |= DT_VCENTER;
      break;
    case lTextTop:
      format |= DT_VCENTER;//DT_BOTTOM;
      break;
    case lTextBottom:
      format |= DT_VCENTER;//DT_TOP;
      break;
  }
 
  // Create a UI Face object for this button & let it paint the button face
  //
  TPoint  dstPt(rect.TopLeft());
 
  if (GetButtonState() == Down && GetEnabled() &&
      GetGadgetWindow()->GetFlatStyle()&TGadgetWindow::FlatStandard)
  {
    if(IsHaveMouse())
    {
      if(IsPressed())
      {
        const int dx = (format & DT_CENTER) ? 2 : 1;
        const int dy = (format & DT_VCENTER) ? 2 : 1;
        rect.Offset(dx, dy);
      }
      TUIFace(rect, text, TColor::Sys3dFace,format).Paint(dc, dstPt,
                TUIFace::Normal, true, true);
    }
    else
    {
      TUIFace face(rect, text, TColor::Sys3dFace,format);
      if(GetGadgetWindow()->GadgetGetCaptured()==this)
        face.Paint(dc, dstPt, TUIFace::Normal, true);
      else
        face.Paint(dc, dstPt, TUIFace::Down, IsPressed(), false);
    }
  }
  else
  {
    TUIFace face(rect, text, TColor::Sys3dFace,format);
    if (!GetEnabled())
      face.Paint(dc, dstPt, TUIFace::Disabled, false, false);
    else if (GetButtonState() == Indeterminate)
      face.Paint(dc, dstPt, TUIFace::Indeterm, IsPressed(), false);
    else if (GetButtonState() == Down) // Down and not flat
      face.Paint(dc, dstPt, TUIFace::Down, IsPressed(), false);
    else
      face.Paint(dc, dstPt, TUIFace::Normal, IsPressed(), false);
  }
 
  dc.SetTextColor(oldTxColor);
 
  dc.RestoreFont();
}
 
} // OWL namespace
 
//==============================================================================

V1004 The 'frame' pointer was used unsafely after it was verified against nullptr. Check lines: 167, 168.

V1037 Two or more case-branches perform the same actions. Check lines: 480, 483, 486