//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of TGlyphButton
//----------------------------------------------------------------------------
#include <owl/pch.h>
 
#include <owl/glyphbtn.h>
#include <owl/uihelper.h>
#include <owl/celarray.h>
#include <owl/gdiobjec.h>
#include <owl/color.h>
#include <owl/system.h>
#include <owl/theme.h>
#include <owl/commctrl.h>
#include <owl/glyphbtn.rh>
 
#if defined(__BORLANDC__)
# pragma option -w-ccc // Disable "Condition is always true/false"
#endif
 
namespace owl {
 
const int  LayoutMargin         = 4;
const int  FaceToFocusRectDelta = 1;
const int  BUTTONSTATE_PUSHED   = 0x0004;
const int  BUTTONSTATE_FOCUS    = 0x0008;
const int  BUTTONSTATE_HOT      = 0x0010;
const uint32 BUTTONSTYLE_MASK = 0x000000FFL;
const long RopDSPDxax           = 0x00E20746L;
 
//
// Helper function - extracts the button style bits.
//
static inline uint32
ButtonStyle(uint32 style)
{
  return style & BUTTONSTYLE_MASK;
}
 
 
 
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlControl);        // General Controls diagnostic group
 
 
//
/// Loads the specified bitmap and updates the face color, if necessary.
/// If not specified, the default face color is assumed to be TColor::LtGray.
//
TBtnBitmap::TBtnBitmap(HINSTANCE hInstance, TResId resId,
                       const TColor& faceColor)
:
  TBitmap(hInstance, resId),
  FaceColor(faceColor)
{
  UpdateFaceColor();
}
 
 
//
/// Aliases the specified bitmap handle and updates the face color, if necessary.
/// If not specified, the default face color is assumed to be TColor::LtGray.
//
TBtnBitmap::TBtnBitmap(HBITMAP hBitmap, const TColor& faceColor,
                       TAutoDelete autoDelete)
:
  TBitmap(hBitmap, autoDelete),
  FaceColor(faceColor)
{
  UpdateFaceColor();
}
 
 
//
/// Updates the face color of the associated bitmap to the current system setting.
/// Does nothing if the current face color already matches the system color.
//
void
TBtnBitmap::UpdateFaceColor()
{
  if (FaceColor != TColor::Sys3dFace) {
    TBitmap copy(*this);
    TMemoryDC dstDC, srcDC;
    dstDC.SelectObject(*this);
    srcDC.SelectObject(copy);
    MapColor(dstDC, srcDC, TColor::Sys3dFace, FaceColor, Width(), Height());
    dstDC.RestoreBitmap();
    srcDC.RestoreBitmap();
    FaceColor = TColor::Sys3dFace;
  }
}
 
 
//
/// Helper routine used to map the face color of the underlying bitmap.
//
void
TBtnBitmap::MapColor(TDC& dc, TDC& srcDC, const TColor& toColor,
                     const TColor& fromColor, int width, int height)
{
  // Create a monochrome mask
  //
  TMemoryDC hMemDC(srcDC);
  TBitmap hMask(width, height);
  hMemDC.SelectObject(hMask);
 
  // Build a mask where the source color is zeroed
  //
  TColor crBack = srcDC.SetBkColor(fromColor);
  hMemDC.BitBlt(0, 0, width, height, srcDC, 0, 0, SRCCOPY);
  srcDC.SetBkColor(crBack);
 
  // Make a brush out of the destination color
  //
  TBrush brush(toColor);
  dc.SelectObject(brush);
 
  // Copy the mask to the destination
  //
  dc.BitBlt(0, 0, width, height, srcDC, 0, 0, SRCCOPY);
 
  // Copy the source bitmap to the destination by applying the
  // brush where pixels are zeroed
  //
  dc.BitBlt(0, 0, width, height, hMemDC, 0, 0, RopDSPDxax);
  dc.RestoreBrush();
}
 
 
namespace
{
 
  //
  // Returns `true` if themes are enabled for this application and
  // themed Common Controls (version 6 or later) are in use.
  //
  // Important: This function must be called after the creation of the main
  // window, otherwise it may always return `false`.
  //
  // Note that IsAppThemed will return `false` if either (a) themes have been
  // disabled for the application by selecting "Disable visual themes" in the
  // Compatibility tab in the Properties dialog for the executable, or (b)
  // themes have been deactivated by selecting the Windows Classic style in
  // the Windows XP/7/Vista Control Panel (not available in Windows 8).
  // Note that (b) may change at run-time.
  //
  // Note we do not have to use IsThemeActive here. This function only reports
  // the state of the Control Panel setting (Classic vs themed).
  //
  bool IsThemed()
  {
    static const auto v = GetCommCtrlVersion();
    return TThemeModule::GetInstance().IsAppThemed() && v >= 0x60000;
  }
 
} // namespace
 
//
/// Constructor of TGlyphButton - Use this constructor to create a GlyphBtn from
/// scratch.
//
TGlyphButton::TGlyphButton(TWindow* parent, int id, LPCTSTR text,
                           int X, int Y, int W, int H, TBtnType type,
                           bool isDefault, TModule* module)
:
  TButton(parent, id, text, X, Y, W, H, isDefault, module)
{
  InitVars();
  BtnType = type;
  if(type != btCustom)
    InitGlyp(type);
}
 
//
/// String-aware overload
//
TGlyphButton::TGlyphButton(
  TWindow* parent,
  int id,
  const tstring& text,
  int X, int Y, int W, int H,
  TBtnType type,
  bool isDefault,
  TModule* module
  )
  : TButton(parent, id, text, X, Y, W, H, isDefault, module)
{
  InitVars();
  BtnType = type;
  if(type != btCustom)
    InitGlyp(type);
}
 
 
//
/// Constructor of TGlyphButton - Use this constructor to alias a glyph button
/// control specified in a dialog template.
//
TGlyphButton::TGlyphButton(TWindow* parent, int resourceId,
                           TBtnType type, TModule* module)
:
  TButton(parent, resourceId, module)
{
  InitVars();
  BtnType = type;
  if(type != btCustom)
    InitGlyp(type);
}
 
 
//
/// Method used to initialized variables used by GlyphButton's implementation.
//
void
TGlyphButton::InitVars()
{
  CelArray = 0;
  BtnFont = 0;
  xText = yText = -1;
  xGlyph = yGlyph = -1;
  LayStyle = lsH_GST;
  BtnType = btCustom;
  Set(biShowText);
}
 
//
/// Destructor - Cleans up resources used by Glyph Button object.
//
TGlyphButton::~TGlyphButton()
{
  delete CelArray;
  delete BtnFont;
}
 
 
//
//init Glip
//
void TGlyphButton::InitGlyp(TBtnType type)
{
  uint ResIdUp = 0;
  switch(type){
    case btOk:
    case btYes:
    case btAbort:
      ResIdUp = IDB_BORBTNII_OK;
      break;
    case btCancel:
    case btClose:
    case btIgnore:
      ResIdUp = IDB_BORBTNII_CANCEL;
      break;
    case btNo:
      ResIdUp = IDB_BORBTNII_NO;
      break;
    case btHelp:
      ResIdUp = IDB_BORBTNII_HELP;
      break;
    case btApply:
      ResIdUp = IDB_BORBTNII_APPLY;
      break;
    case btBrowse:
      ResIdUp = IDB_BORBTNII_BROWSE;
      break;
    case btRevert:
      ResIdUp = IDB_BORBTNII_REVERT;
      break;
    case btAdd :
      ResIdUp = IDB_BORBTNII_ADD;
      break;
    case btDelete:
      ResIdUp = IDB_BORBTNII_DEL;
      break;
    case btEdit:
      ResIdUp = IDB_BORBTNII_EDIT;
      break;
    case btKey:
      ResIdUp = IDB_BORBTNII_KEY;
      break;
    case btSetup:
      ResIdUp = IDB_BORBTNII_SETUP;
      break;
    case btCustom:
    default:                 //JJH added empty default construct
      break;
  }
  if(ResIdUp){
    delete CelArray;
    CelArray = new TCelArray(new TBtnBitmap(GetGlobalModule(), ResIdUp, TColor::LtGray), 3); //CelsTotal);
    Set(biGtUp|biGtDown|biGtDisabled|biShowGlyph);
  }
  return;
}
 
 
//
// Response Table
//
DEFINE_RESPONSE_TABLE1(TGlyphButton, TButton)
  EV_WM_PAINT,
  EV_WM_ERASEBKGND,
  EV_WM_SETFOCUS,
  EV_WM_KILLFOCUS,
  EV_WM_GETFONT,
  EV_WM_SETFONT,
  EV_WM_GETDLGCODE,
  EV_WM_LBUTTONDOWN,
  EV_WM_LBUTTONDBLCLK,
  EV_WM_LBUTTONUP,
  EV_WM_MOUSEMOVE,
  EV_WM_KEYDOWN,
  EV_WM_KEYUP,
  EV_WM_ENABLE,
  EV_WM_CANCELMODE,
  EV_WM_SYSCOLORCHANGE,
  EV_MESSAGE(BM_SETSTATE, BmSetState),
  EV_MESSAGE(BM_GETSTATE, BmGetState),
  EV_MESSAGE(BM_SETSTYLE, BmSetStyle),
  EV_WM_MOUSELEAVE,
END_RESPONSE_TABLE;
 
//
// Window proc. of control to handle messages sent before OWL thunks
//
LRESULT CALLBACK
BButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg) {
    case WM_GETDLGCODE: {
      uint32 btnStyle = ButtonStyle(GetWindowLong(hwnd, GWL_STYLE));
      if (btnStyle == BS_DEFPUSHBUTTON)
        return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
      else
        return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}
 
//
/// Overriden virtual of TWindow - Fills out information about the Window class
/// associated with a glyph button.
/// \note The class information is based on the system's "BUTTON" class.
//
void
TGlyphButton::GetWindowClass(WNDCLASS& wndClass)
{
  // Grab a the attributes of the native "BUTTON" control
  //
  if (::GetClassInfo(0, _T("BUTTON"), &wndClass)) {
    wndClass.hInstance = *GetModule();
    wndClass.lpszClassName = GetWindowClassName().GetPointerRepresentation();
    wndClass.lpfnWndProc = BButtonProc;
  }
  else {
    TControl::GetWindowClass(wndClass);
    wndClass.style = CS_HREDRAW|CS_VREDRAW|CS_PARENTDC;
    wndClass.hbrBackground = HBRUSH(COLOR_BTNFACE+1);
  }
}
 
//
/// Returns name of window class associated with a glyph button control.
//
auto TGlyphButton::GetWindowClassName() -> TWindowClassName
{
  return TWindowClassName{OWL_GLYPHBTN};
}
 
//
/// Overriden virtual of TWindow - Updates internal flags based on style of
/// underlying window.
//
void
TGlyphButton::SetupWindow()
{
  TButton::SetupWindow();
 
  // Update state flags based on current style
  //
  if (ButtonStyle(GetStyle()) == BS_DEFPUSHBUTTON)
    Set(biDefault);
  if (GetStyle() & WS_DISABLED)
    Set(biDisabled);
 
  // Get Parent font -> dialog font
  //
  delete BtnFont;
  HFONT hFont = GetParentO()->GetWindowFont();
  BtnFont = hFont ? new TFont(hFont) : new TDefaultGuiFont();
}
 
//
/// Sets the bitmap to use for the specified button state (glyph type).
/// The 'bitmap' parameter can be `nullptr` to reset the glyph for the given state.
///
/// \note TColor::Sys3dFace is the 'face color' used to denote transparency.
/// If you want a different color to denote transparency, you must map the colors of the
/// bitmap before passing it. TBtnBitmap is made for this purpose.
//
void TGlyphButton::SetGlyph(TBitmap* bitmap, TGlyphType type, TAutoDelete autoDelete)
{
  PRECONDITION(!bitmap || bitmap->IsGDIObject());
  if(!CelArray && bitmap && type != gtCellArray)
    BuildCelArray(bitmap);
 
  switch (type) {
  case gtUp:
    if(bitmap){
      CelArray->Replace(0, *bitmap);
      if(autoDelete==AutoDelete)
        delete bitmap;
      Set(biGtUp);
    }
    else
      Clear(biGtUp);
    break;
 
  case gtDown:
    if(bitmap){
      CelArray->Replace(1, *bitmap);
      if(autoDelete==AutoDelete)
        delete bitmap;
      Set(biGtDown);
    }
    else
      Clear(biGtDown);
    break;
 
  case gtDisabled:
    if(bitmap){
      CelArray->Replace(2, *bitmap);
      if(autoDelete==AutoDelete)
        delete bitmap;
      Set(biGtDisabled);
    }
    else
      Clear(biGtDisabled);
    break;
 
  case gtCellArray:
    if(bitmap){
      delete CelArray;
      CelArray = new TCelArray((autoDelete==AutoDelete?
        bitmap:new TBitmap(*bitmap)),3);
      Set(biGtUp|biGtDown|biGtDisabled);
    }
    else
      Clear(biGtUp|biGtDown|biGtDisabled);
    break;
 
  default:
    break;
  }
 
  // Update status flag
  //
  if (IsSet(biGtUp))
    Set(biShowGlyph);
  else
    Clear(biShowGlyph);
}
 
//
/// Sets the icon to use for the specified button state (glyph type).
//
void TGlyphButton::SetGlyph(const TIcon& icon, TGlyphType type)
{
  const auto createBitmap = [](const TIcon& icon, TColor bkgndColor)
  {
    const auto i = icon.GetInfo();
    auto bmp = i.Color;
    TMemoryDC dc{bmp};
    const auto faceBrush = TBrush{bkgndColor};
    dc.SelectObject(faceBrush);
    const auto r = TRect{{0, 0}, bmp.Size()};
    dc.MaskBlt(r, dc, r.TopLeft(), i.Mask, r.TopLeft(), MAKEROP4(PATCOPY, SRCCOPY));
    dc.RestoreBitmap(); // Needed to commit changes to the bitmap.
    return bmp;
  };
  auto b = createBitmap(icon, TColor::Sys3dFace);
  SetGlyph(&b, type, TAutoDelete::NoAutoDelete);
}
 
//
/// Sets the bitmap to use for the specified button state (glyph type).
///
/// \note TColor::LtGray is the 'face color' used to denote transparency.
/// If you want a different color to denote transparency, then create a TBtnBitmap and use
/// the SetGlyph (TBitmap*, ...) overload instead.
//
void TGlyphButton::SetGlyph(HBITMAP hBitmap, TGlyphType type, TAutoDelete autoDelete)
{
  PRECONDITION(!hBitmap || ::GetObjectType(hBitmap) == OBJ_BITMAP);
 
  SetGlyph(hBitmap ? new TBtnBitmap(hBitmap, TColor::LtGray, autoDelete) : (TBitmap*)0, type);
}
 
//
/// Sets the bitmap resource to use for the specified button state (glyph type).
///
/// \note TColor::LtGray is the 'face color' used to denote transparency.
/// If you want a different color to denote transparency, then create a TBtnBitmap and use
/// the SetGlyph (TBitmap*, ...) overload instead.
//
void TGlyphButton::SetGlyph(TResId resId, TModule* module, TGlyphType type)
{
  PRECONDITION(module != 0 || GetModule());
  SetGlyph(new TBtnBitmap(module ? *module : *GetModule(), resId, TColor::LtGray), type,
            AutoDelete);
}
 
//
// Create CellArray with 3 elements
//
void
TGlyphButton::BuildCelArray(const TBitmap* cell)
{
  CelArray = new TCelArray(cell->Size(), 0, 3,2);
  CelArray->Add(*cell);
  CelArray->Add(*cell);
  CelArray->Add(*cell);
}
 
//
/// WM_PAINT handler - Invokes 'Paint' method to display glyph and/or text.
//
void
TGlyphButton::EvPaint()
{
  TPaintDC dc(*this);
  TRect& rect = *(TRect*)&dc.Ps.rcPaint;
  Paint(dc, dc.Ps.fErase, rect);
}
 
//
/// Renders the button in a flicker-free manner.
/// Creates a memory DC into which the button is rendered by calling PaintButton.
/// The rendered bitmap is then blitted to the given device context.
//
void
TGlyphButton::Paint(TDC& dc, bool /*erase*/, TRect&)
{
  const TRect r = GetClientRect();
  TMemoryDC mdc(dc);
  TBitmap b(dc, r.Width(), r.Height());
  mdc.SelectObject(b);
  PaintButton(mdc);
  dc.BitBlt(r, mdc, r.TopLeft());
  mdc.RestoreBitmap();
}
 
 
//
/// WM_ERASEBKGND handler - Returns true to prevent background from being erased.
/// (WM_PAINT handler paints whole client area).
//
bool
TGlyphButton::EvEraseBkgnd(HDC /*dc*/)
{
  return true;
}
 
//
/// WM_SETFOCUS handler - Updates internal flag and forces button to redraw.
//
void
TGlyphButton::EvSetFocus(THandle /*hWndLostFocus*/)
{
  Set(biFocus);
  Invalidate(true);
}
 
//
/// WM_KILLFOCUS handler - Updates internal flag and forces button to redraw.
//
void
TGlyphButton::EvKillFocus(THandle /*hWndGetFocus*/)
{
  Clear(biFocus);
  if (IsSet(biPushed))
    ClearCapture();
  else
    Invalidate(true);
}
 
//
/// WM_GETFONT handler - Returns font used by control if one was specified earlier.
/// Otherwise, returns 0.
//
HFONT
TGlyphButton::EvGetFont()
{
  PRECONDITION(!BtnFont || BtnFont->IsGDIObject());
  return BtnFont ? HFONT(*BtnFont) : HFONT(0);
}
 
//
/// WM_SETFONT Handler. Deletes any cached font and stores a copy of the new one.
//
void
TGlyphButton::EvSetFont(HFONT hFont, bool redraw)
{
  delete BtnFont;
  BtnFont = new TFont(hFont);
  if (redraw)
    Invalidate();
}
 
//
/// WM_GETDLGCODE handler. Informs dialog manager of a 'normal' push button or the
/// default push button according to the style.
//
uint
TGlyphButton::EvGetDlgCode(const MSG* /*msg*/)
{
  if (IsSet(biDefault))
    return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
  else
    return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
}
 
//
/// WM_LBUTTONDOWN handler. Grabs focus and updates button's state to be in 'pushed'
/// mode.
//
void
TGlyphButton::EvLButtonDown(uint /*modKeys*/, const TPoint& /*point*/)
{
  SetCapture();
  SendMessage(BM_SETSTATE, TRUE);
  if(!IsSet(biFocus))
    SetFocus();
}
 
//
/// WM_LBUTTONDBLCLK handler. Simply forwards to LBUTTONDOWN handler.
//
void
TGlyphButton::EvLButtonDblClk(uint modKeys, const TPoint& point)
{
  EvLButtonDown(modKeys, point);
}
 
//
/// WM_LBUTTONUP handler. Restores state of button and notifies parent with a
/// CLICKED message if necessary.
//
void
TGlyphButton::EvLButtonUp(uint /*modKeys*/, const TPoint& point)
{
  if (GetCapture() == *this) {
    ReleaseCapture();
 
    SendMessage(BM_SETSTATE, FALSE);
 
    TRect rect;
    GetClientRect(rect);
 
    if (rect.Contains(point)) {
      SendNotification(::GetParent(*this), GetDlgCtrlID(), BN_CLICKED, *this);
    }
  }
}
 
//
/// WM_MOUSEMOVE handler. Updates state of button if it is in 'capture' mode.
//
void
TGlyphButton::EvMouseMove(uint modKeys, const TPoint& point)
{
  TRect rect;
  GetClientRect(rect);
  if (modKeys & MK_LBUTTON && GetCapture() == *this) {
 
    if (rect.Contains(point))
      SendMessage(BM_SETSTATE, 1);
    else
      SendMessage(BM_SETSTATE, 0);
  }
  else if (IsThemed() && rect.Contains(point))
  {
    if (!IsSet(biHot))
    {
      SendMessage(BM_SETSTATE, 2); //HOT
      TrackMouseEvent(TME_LEAVE); // Order the WM_MOUSELEAVE message.
    }
  }
}
 
//
/// WM_KEYDOWN handler. Updates the state of the button upon detecting that the user
/// pressed the space bar.
//
void
TGlyphButton::EvKeyDown(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  if (key == VK_SPACE)
    SendMessage(BM_SETSTATE, TRUE);
}
 
//
/// WM_KEYUP handler. Restores state of button and notify parent.
//
void
TGlyphButton::EvKeyUp(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  if (IsSet(biPushed) && key == VK_SPACE) {
    SendMessage(BM_SETSTATE, FALSE);
 
    SendNotification(::GetParent(*this), GetDlgCtrlID(),
                      BN_CLICKED, *this);
  }
}
 
//
/// WM_ENABLE handler. Updates internal flags and invalidates control if  necessary.
//
void
TGlyphButton::EvEnable(bool enabled)
{
  if (enabled) {
    Clear(biDisabled);
  }
  else {
    ClearCapture();
    Set(biDisabled);
  }
  Invalidate(true);
}
 
//
/// WM_CANCELMODE handler. Releases capture if currently in capture mode and
/// terminates any internal operation on the button.
//
void
TGlyphButton::EvCancelMode()
{
  ClearCapture();
}
 
/// WM_SYSCOLORCHANGE handler � Reloads buttons.
void
TGlyphButton::EvSysColorChange()
{
  TButton::EvSysColorChange();
  if(BtnType != btCustom)
    InitGlyp(BtnType);
}
 
//
/// Cancels button Hot state.
//
void
TGlyphButton::EvMouseLeave()
{
  if (IsSet(biHot))
    SendMessage(BM_SETSTATE, 0);
}
 
//
/// BM_GETSTATE handler. Returns the current state of the window.
//
TResult
TGlyphButton::BmGetState(TParam1, TParam2)
{
  TResult result = 0;
  if (IsSet(biPushed))
    result |= BUTTONSTATE_PUSHED;
 
  if (IsSet(biFocus))
    result |= BUTTONSTATE_FOCUS;
 
  if (IsSet(biHot))
    result |= BUTTONSTATE_HOT;
 
  return result;
}
 
//
/// BM_SETSTATE handler. Updates internal state flags based on parameters and
/// redraws control if necessary.
//
TResult
TGlyphButton::BmSetState(TParam1 param1, TParam2)
{
  switch (param1)
  {
  case 1: // Needs hilight look
    if (!IsSet(biPushed))
    {
      Set(biPushed);
      PaintNow();
    }
    break;
 
  case 2: //New: Hot state
    if (!IsSet(biHot))
    {
      Set(biHot);
      PaintNow();
    }
    break;
 
  default: // Needs normal look
    if (IsSet(biPushed)||IsSet(biHot))
    {
      Clear(biPushed);
      Clear(biHot);
      PaintNow();
    }
    break;
  }
  return 0;
}
 
//
/// BM_SETSTYLE handler. Updates internal flags to match specified parameters and
/// invalidates the window if necessary.
//
TResult
TGlyphButton::BmSetStyle(TParam1 param1, TParam2)
{
  struct Local
  {
    static void SetButtonStyle(TGlyphButton* b, uint32 btnStyle)
    {
      using namespace owl; // Circumvents compiler issue.
 
      uint32 bs = btnStyle & BUTTONSTYLE_MASK;
      uint32 other = b->GetStyle() & ~BUTTONSTYLE_MASK;
      b->TWindow::SetStyle(bs | other);
    }
  };
 
  // Grab and splice the styles
  //
  uint32 btnStyle = ButtonStyle(GetStyle());
  uint32 newBtnStyle = ButtonStyle(static_cast<uint32>(param1));
 
  // Check against passed in parameter
  // NOTE: We only cater to PUSHBUTTON and DEFPUSHBUTTON
  //       The current definition so BS_PUSHBUTTON is 0L
  //
  if (newBtnStyle == BS_PUSHBUTTON && btnStyle != BS_PUSHBUTTON)
  {
    // Make 'simple' push button
    //
    Local::SetButtonStyle(this, newBtnStyle);
    Clear(biDefault);
    Invalidate(true);
  }
  else if (newBtnStyle == BS_DEFPUSHBUTTON && btnStyle != BS_DEFPUSHBUTTON)
  {
    // Make 'default' push button
    //
    Local::SetButtonStyle(this, newBtnStyle);
    Set(biDefault);
    Invalidate(true);
  }
 
#if defined(__DEBUG) || defined(__TRACE) || defined(__WARN)
  if (newBtnStyle != BS_PUSHBUTTON && newBtnStyle != BS_DEFPUSHBUTTON)
    TRACEX(OwlControl, 0, "BmSetStyle: Invalid style specified");
#endif
 
  return 0;
}
 
//
/// Releases caption if we are in 'capture' mode. Resets internal flags
/// appropriately.
//
void
TGlyphButton::ClearCapture()
{
  if (GetCapture() == *this)
    ReleaseCapture();
  Clear(biPushed);
  Invalidate(true);
}
 
//
/// Entry point for painting the button to the given device context.
//
void
TGlyphButton::PaintButton(TDC& dc)
{
  TRect rect;
  GetClientRect(rect);
 
  if (IsThemed())
  {
    // Draw themed background. This ignores all our border styles.
    // PBS_NORMAL = 1, PBS_HOT = 2, PBS_PRESSED = 3, PBS_DISABLED = 4, PBS_DEFAULTED = 5, PBS_DEFAULTED_ANIMATING = 6,
    int state = IsSet(biDisabled) ? PBS_DISABLED :
      IsSet(biPushed) ? PBS_PRESSED :
      IsSet(biHot) ? PBS_HOT :
      IsSet(biFocus) ? PBS_DEFAULTED :
      PBS_NORMAL;
    TThemePart part(GetHandle(), L"BUTTON", BP_PUSHBUTTON, state);
    part.DrawTransparentBackground(GetHandle(), dc, rect);
  }
  else
  {
    // Themes are not available. Draw classic style.
    //
    PaintDefaultRect(dc, rect);
    PaintFrame(dc, rect);
  }
  PaintFace(dc, rect);
}
 
//
/// Draws the border of the button.
//
void
TGlyphButton::PaintFrame(TDC& dc, TRect& rect)
{
  // Paint the border
  //
  TUIBorder uiBorder(rect, IsSet(biPushed) ? TUIBorder::ButtonDn :
                                             TUIBorder::ButtonUp);
  uiBorder.Paint(dc);
 
  // Shrink the rectangle to leave the face
  //
  rect = uiBorder.GetClientRect();
}
 
//
/// Draws a frame around the button if it's a default push button.
//
void
TGlyphButton::PaintDefaultRect(TDC& dc, TRect& rect)
{
  if (IsSet(biDefault)){
    if (rect.Width() > 2 && rect.Height() > 2) {
      TPen framePen(TColor::SysWindowFrame);
      dc.SelectObject(framePen);
      dc.SelectStockObject(NULL_BRUSH);
 
#if 0
      rect.Inflate(-1, -1);
      dc.Rectangle(rect);
#else
      dc.Rectangle(rect);
      rect.Inflate(-1, -1);
#endif
      dc.RestoreBrush();
      dc.RestorePen();
    }
  }
}
 
//
/// Draws the face of the button [i.e. text and glyph portions].
//
void
TGlyphButton::PaintFace(TDC& dc, const TRect& rect)
{
  const bool isThemed = IsThemed();
  if (!isThemed)
  {
    // Fill the background with the face color
    //
    TBrush brush(TColor::Sys3dFace);
    dc.FillRect(rect, brush);
  }
 
  // Grab the glyph and it's size
  //
  int index = -1;              // index into array
  TRect glyphRect;             // Size of glyph. (default constructor now zerroed all members) !!!!!!!!!1
  if(IsSet(biShowGlyph)){
 
    // Start with the up bitmap
    index = 0;
 
    // Switch to more appropriate bitmap if applicable
    //
    if (IsSet(biPushed) && IsSet(biGtDown))
      index = 1;
    else if (IsSet(biDisabled) && IsSet(biGtDisabled))
      index = 2;
 
    CHECK(CelArray);
    TSize cell = CelArray->CelSize();
    glyphRect = {{0, 0}, cell};
  }
 
  // Grab some information about the text/caption
  //
  int len = 0;                    // Length of Caption
  TRect textRect;                 // Size of text (default constructor now zerroed all members) !!!!!!
  TAPointer<tchar> text;          // Pointer to caption dynamic buffer
  TPointer<TFont>  tmpFnt;        // Object wrapping font handle
  TColor textColor;               // Color used for button's text
 
  if (IsSet(biShowText)) {
    len = GetWindowTextLength();
    if (len){
 
      // Select the font
      //
      if (!BtnFont)
      {
        HFONT parentFont = reinterpret_cast<HFONT>(::SendMessage(GetParentH(), WM_GETFONT, 0, 0));
        tmpFnt = parentFont ? new TFont(parentFont) : static_cast<TFont*>(new TDefaultGuiFont());
      }
      const TFont& f = BtnFont ? *BtnFont : *tmpFnt;
      CHECK(f.IsGDIObject());
      dc.SelectObject(f);
 
      text = new tchar[len+1];
      GetWindowText(text, len+1);
      textRect = {0, 0, rect.Width() - glyphRect.Width(), SHRT_MAX};
 
      textColor = IsSet(biDisabled) ? TColor::Sys3dShadow : TColor::SysBtnText;
      dc.DrawText(&text[0], len, textRect, DT_WORDBREAK|DT_CALCRECT);
    }
  }
 
  // If we have text and/or glyph, lay them out and paint them
  //
  if (!textRect.IsNull() || !glyphRect.IsNull()) {
 
    LayoutTextGlyph(rect, textRect, glyphRect);
 
    // Offset things to the lower right if we're in down
    //
    if (IsSet(biPushed)) {
      if (!glyphRect.IsNull() && index != 1)
        glyphRect.Offset(1, 1);
      if (!textRect.IsNull())
        textRect.Offset(1, 1);
    }
 
    // Paint the components of the button
    //
    if (!glyphRect.IsNull()) {
      PRECONDITION(CelArray);
 
      // Create a UI Face object for this button & let it paint the button face.
      //
      if (isThemed) {
        TRect srcRect(CelArray->CelRect(index));
        TUIFace face(rect, *CelArray);
        const TPoint dstPt = glyphRect.TopLeft();
        const TUIFace::TState state = TUIFace::Normal;
        const bool pressed = false;
        face.Paint(dc, srcRect, dstPt, state, pressed, false); // no fill (transparent)
      }
      else {
        // If not themed, painting like above will not center oversized images properly.
        // Hence we use the original non-themed code here.
        //
        CelArray->BitBlt(index, dc, glyphRect.left, glyphRect.top);
      }
    }
    if (!textRect.IsNull()) {
      // Paint disabled text in 3d style
      if (IsSet(biDisabled)) {
        TUIFace(textRect, &text[0], TColor::LtGray,
            DT_WORDBREAK|DT_VCENTER|DT_CENTER).Paint(dc, textRect.TopLeft(),
            TUIFace::Disabled, false, false);
      }
      else{
        int mode = dc.SetBkMode(TRANSPARENT);
        TColor oldColor = dc.SetTextColor(textColor);
        dc.DrawText(&text[0], len, textRect, DT_WORDBREAK|DT_VCENTER|DT_CENTER);
        dc.SetTextColor(oldColor);
        dc.SetBkMode(mode);
      }
    }
  }
 
  // Paint the focus rect [if necessary]
  //
  if (IsSet(biFocus))
  {
    if (isThemed || textRect.IsNull())
      PaintFocusRect(dc, rect.InflatedBy(-LayoutMargin, -LayoutMargin));
    else
      PaintFocusRect(dc, textRect);
  }
 
  // Restore font
  //
  if (len && (BtnFont || tmpFnt))
    dc.RestoreFont();
}
 
//
/// Virtual routine invoked to retrieve the placement of text and glyph when drawing
/// the button.
///
/// Override this routine to customize the layout logic and support
/// custom layout styles.
//
void
TGlyphButton::LayoutTextGlyph(const TRect& faceRect, TRect& textRect,
                              TRect& glyphRect)
{
  // Must have either text or a glyph
  //
  PRECONDITION(!textRect.IsNull() || !glyphRect.IsNull());
 
  // First check for lsNone and, if so, use specified coordinates.
  //
  if (LayStyle == lsNone)
  {
    if (!textRect.IsNull() && xText >= 0 && yText >= 0)
      textRect.Offset(xText, yText);
    if (!glyphRect.IsNull() && xGlyph >= 0 && yGlyph >= 0)
      glyphRect.Offset(xGlyph, yGlyph);
  }
 
  // Now check for the case where we've got either
  // text or glyph - but not both
  //
  else if (textRect.IsNull() || glyphRect.IsNull())
  {
    TRect& centerRect = textRect.IsNull() ? glyphRect : textRect;
 
    centerRect.Offset(faceRect.left, faceRect.top);
    if (centerRect.Width() < faceRect.Width())
      centerRect.Offset((faceRect.Width() - centerRect.Width())/2, 0);
    else
      centerRect.right = faceRect.right;
    if (centerRect.Height() < faceRect.Height())
      centerRect.Offset(0, (faceRect.Height() - centerRect.Height())/2);
    else
      centerRect.bottom = faceRect.bottom;
  }
 
  // Here we attempt to layout both the glyph and text
  //
  else {
    // Align upper left corners of face, text and glyph rectangles
    //
    glyphRect.Offset(faceRect.left, faceRect.top);
    textRect.Offset(faceRect.left, faceRect.top);
 
    // Compute amount of 'extra' space, if any, and how to partition it
    // between the two items
    //
    int hspace  = faceRect.Width() - glyphRect.Width()- textRect.Width() -
                 LayoutMargin*2;
    int vspace  = faceRect.Height() - glyphRect.Height()- textRect.Height() -
                 LayoutMargin*2;
    int gDelta=0;
    int tDelta=0;
 
    switch (LayStyle) {
      case lsH_SGST: {
           gDelta = hspace >= 0 ? LayoutMargin + hspace/3 :
                                 LayoutMargin + hspace/2;
          tDelta = hspace >= 0 ? gDelta + glyphRect.Width() + hspace/3 :
                                 gDelta + glyphRect.Width();
        }
        break;
      case lsH_GST: {
          gDelta = hspace >= 0 ? LayoutMargin : LayoutMargin + hspace/2;
          tDelta = hspace >= 0 ? gDelta + glyphRect.Width() + hspace/2 :
                                    gDelta + glyphRect.Width();
        }
        break;
       case lsH_STSG: {
           tDelta = hspace >= 0 ? LayoutMargin + hspace/3 :
                                 LayoutMargin + hspace/2;
          gDelta = hspace >= 0 ? tDelta + textRect.Width() + hspace/3 :
                                 tDelta + textRect.Width();
        }
        break;
      case lsH_TSGS: {
          tDelta = 0;
          gDelta = hspace >= 0 ? textRect.Width() + hspace/3 : textRect.Width();
          if(IsSet(biDefault))
            tDelta--;
        }
        break;
      case lsV_SGST: {
           gDelta = vspace >= 0 ? LayoutMargin + vspace/3 :
                                 LayoutMargin + vspace/2;
          tDelta = vspace >= 0 ? gDelta + glyphRect.Height() + vspace/3 :
                                 gDelta + glyphRect.Height();
        }
        break;
      case lsV_GST: {
          gDelta = vspace >= 0 ? LayoutMargin : LayoutMargin + vspace/2;
          tDelta = vspace >= 0 ? gDelta + glyphRect.Height() + vspace/2 :
                                    gDelta + glyphRect.Height();
        }
        break;
      case lsV_STSG: {
           tDelta = vspace >= 0 ? LayoutMargin + vspace/3 :
                                 LayoutMargin + vspace/2;
          gDelta = vspace >= 0 ? tDelta + textRect.Height() + vspace/3 :
                                 tDelta + textRect.Height();
        }
        break;
      case lsV_TSGS: {
          tDelta = 0;
          gDelta = vspace >= 0 ? textRect.Height() + vspace/3 : textRect.Height();
          if(IsSet(biDefault))
            tDelta--;
        }
        break;
       default:
        break;
    }
 
    int gx=0,gy=0;
    if (LayStyle == lsH_SGST || LayStyle == lsH_GST ||
        LayStyle == lsH_STSG || LayStyle == lsH_TSGS ) {
 
      // Center vertically
      //
      if (textRect.Height() < faceRect.Height())
        textRect.Offset(0, (faceRect.Height() - textRect.Height())/2);
      if (glyphRect.Height() < faceRect.Height()){
        gy = (faceRect.Height() - glyphRect.Height())/2;
        if(IsSet(biDefault)){
          gx--;
         if(LayStyle == lsH_STSG || LayStyle == lsH_TSGS)
           gx+=2;
        }
      }
      else{
        gy = (faceRect.Height() - glyphRect.Height())/2;
        if(IsSet(biDefault))
           gx--,gy--;
      }
 
      // Layout horizontally
      //
      glyphRect.Offset(gx+gDelta, gy);
      textRect.Offset(tDelta, 0);
    }
    else if (LayStyle == lsV_SGST || LayStyle == lsV_GST ||
             LayStyle == lsV_STSG || LayStyle == lsV_TSGS ) {
 
      // Or center horizontally
      if (textRect.Width() < faceRect.Width())
        textRect.Offset((faceRect.Width() - textRect.Width())/2, 0);
      if (glyphRect.Width() < faceRect.Width()){
        gx += (faceRect.Width() - glyphRect.Width())/2;
        if(IsSet(biDefault)){
          gy--;
         if(LayStyle == lsV_STSG || LayStyle == lsV_TSGS)
           gy+=2;
        }
      }
      else{
        gy = (faceRect.Height() - glyphRect.Height())/2;
        if(IsSet(biDefault))
          gx--,gy--;
      }
      // Layout vertically
      glyphRect.Offset(gx, gy+gDelta);
      textRect.Offset(0, tDelta);
    }
  }
}
 
//
/// Specifies a 'style' describing how text and glyph should be laid out.
/// Invalidates the window if necessary.
//
void
TGlyphButton::SetLayoutStyle(TLayoutStyle style)
{
  if (style != LayStyle) {
    LayStyle = style;
    if (GetHandle())
      Invalidate();
  }
}
 
 
//
/// Sets text coordinates and invalidates window if necessary.
//
void
TGlyphButton::SetTextOrigin(int x, int y)
{
  if (x != xText || y != yText) {
    xText = x;
    yText = y;
    if (GetHandle())
      Invalidate();
  }
}
 
 
//
/// Sets the upper left corner of glyphs and invalidates window if necessary.
//
void
TGlyphButton::SetGlyphOrigin(int x, int y)
{
  if (x != xGlyph || y != yGlyph) {
    xGlyph = x;
    yGlyph = y;
    if (GetHandle())
      Invalidate();
  }
}
 
 
//
/// Displays a focus rectangle.
//
void
TGlyphButton::PaintFocusRect(TDC& dc, const TRect& faceRect)
{
  PRECONDITION(IsSet(biFocus));
  TRect focusRect = faceRect;
  focusRect.Inflate(FaceToFocusRectDelta, FaceToFocusRectDelta);
  dc.DrawFocusRect(focusRect);
}
 
//
/// Repaints window right away by retrieving a client DC and invoking the 'Paint'
/// method.
//
void
TGlyphButton::PaintNow()
{
  TRect rect;
  GetClientRect(rect);
  TClientDC dc(*this);
  Paint(dc, false, rect);
}
 
} // OWL namespace
/* ========================================================================== */
 

V522 There might be dereferencing of a potential null pointer 'CelArray'.