#include <owl/pch.h>
#include "source/glyphdib.h"

// - TGlyphDib by Barmak Shemirani Feb,17th,1999
// - This class is almost the same thing as Borland's TGlyphButton
// - I have marked most of the differences to TGlyphButton by
//   three slashes "///"
// - TButtonEx is drived from this class.

// - A major difference to TGlyphButton is in using TDib instead
//   of TBtnBitmap because it is easier to map colors in TDib.
// - It is also easier to drive from this class.

// compared to TGlyphButton
// the following modifications has been made:

// changed UpBmp, DownBmp,...  to  dibUp, dibDown,... (no reason)

// Changed paint functions to virtual

// added:
// TGlyphType::gtNegative  and  dibNegative
// when mouse moves over the button above values will be used

// added:
// TDib*  dibOf(TGlyphType);        ->to access private TDib  from drived classes
// TFont* BtnFontOf();               ->to access private TFont from drived classes

// added:
// SendNotificationEx();
// this function sends a BN_CLICKED message to the parent.
// Drive classes can over-ride it to do pre-processing

// added:
// MapColors(TDib* dib, TGlyphType);
// This will map the colors to the proper system colors
// Drived classes can over-ride it
 

DEFINE_RESPONSE_TABLE1(TGlyphDib, 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_MESSAGE(BM_SETSTATE, BmSetState),
  EV_MESSAGE(BM_GETSTATE, BmGetState),
  EV_MESSAGE(BM_SETSTYLE, BmSetStyle),
END_RESPONSE_TABLE;

const int  LayoutMargin         = 4;
const int  FaceToFocusRectDelta = -1;
const int  BUTTONSTATE_PUSHED   = 0x0004;
const int  BUTTONSTATE_FOCUS    = 0x0008;
const int  BUTTONSTYLE_MASK     = 0x00FF;
const long RopDSPDxax           = 0x00E20746L;

OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlControl);        // General Controls diagnostic group

// Constructor of TGlyphDib - Use this constructor to create a GlyphBtn from scratch.
TGlyphDib::TGlyphDib(TWindow* parent, int id, const char far* text,
                           int X, int Y, int W, int H, bool isDefault,
                           TModule* module)
:
  TButton(parent, id, text, X, Y, W, H, isDefault, module)
{
  InitVars();
}

// Constructor of TGlyphDib - Use this constructor to alias a glyph button control specified in a dialog template.
TGlyphDib::TGlyphDib(TWindow* parent, int resourceId, TModule* module)
:
  TButton(parent, resourceId, module)
{
  InitVars();
}

// Method used to initialized variables used by GlyphButton's implementation
void
TGlyphDib::InitVars()
{
///
  dibUp = 0;
  dibDown = 0;
  dibFocus = 0;
  dibNegative = 0;
  BtnFont = new TDefaultGUIFont;
  xText = yText = -1;
  xGlyph = yGlyph = -1;
  LayStyle = lsH_GST;
  Set(biShowText);
}

// Destructor - Cleanup resources used by Glyph Button object
TGlyphDib::~TGlyphDib()
{
///
  delete dibUp;
  delete dibDown;
  delete dibFocus;
  delete dibNegative;
  delete BtnFont;
}

// Window proc. of control to handle messages sent before OWL thunks
LRESULT CALLBACK OWL_EXPORT16
BButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg) {
    case WM_GETDLGCODE: {
      uint32 style = GetWindowLong(hwnd, GWL_STYLE);
      uint16 btnStyle = uint16(LoUint16(style) & BUTTONSTYLE_MASK);
      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
TGlyphDib::GetWindowClass(WNDCLASS& wndClass)
{
  // Grab a the attributes of the native "BUTTON" control
  //
  if (::GetClassInfo(0, "BUTTON", &wndClass)) {
    wndClass.hInstance = *GetModule();
    wndClass.lpszClassName = GetClassName();
    wndClass.lpfnWndProc = BButtonProc;
  }
  else {
    TControl::GetWindowClass(wndClass);
    wndClass.style = CS_HREDRAW|CS_VREDRAW|CS_PARENTDC;
    wndClass.hbrBackground = HBRUSH(COLOR_BTNFACE+1);
  }
}

// Return name of window class associated with a glyph button control.
char far*
TGlyphDib::GetClassName()
{
  return OWL_GLYPHBTN_EX;
}

//
// Overriden virtual of TWindow - Updates internal flags based on style of
// underlying window.
//
void
TGlyphDib::SetupWindow()
{
  TButton::SetupWindow();

  // Update state flags based on current style
  //
  uint32 style = GetStyle();
  if (style & BS_DEFPUSHBUTTON)
    Set(biDefault);
  if (style & WS_DISABLED)
    Set(biDisabled);
}

// Specify the resource identifier of a dib to be used as glyph
void
TGlyphDib::SetGlyph(TResId resId, TGlyphType type, TModule* module)
{
  PRECONDITION(module != 0 || GetModule());
  SetGlyphDib(new TDib(module ? *module : *GetModule(), resId), type);
}

///you might need to over-ride this in the drived class
void TGlyphDib::MapColors(TDib* dib, TGlyphType)
{
  dib->MapUIColors(TDib::MapFace|TDib::MapText|TDib::MapShadow|TDib::MapHighlight);
}

// Specify a dib object to be used as glyph
// NOTE: The 'dib' parameter can be 0 to reset the glyph stored by the glyph button object.
void TGlyphDib::SetGlyphDib(TDib* dib, TGlyphType type)
{
  PRECONDITION(!dib || dib->IsGDIObject());
///
  MapColors(dib,type);

  switch (type) {
    case gtUp:
      delete dibUp;
      dibUp = dib;
      break;

    case gtDown:
      delete dibDown;
      dibDown = dib;
      break;

    case gtDisabled:
      delete dibDisabled;
      dibDisabled = dib;
      break;

    case gtFocus:
      delete dibFocus;
      dibFocus = dib;
      break;

    case gtNegative:
      delete dibNegative;
      dibNegative = dib;
      break;

    default:
      break;
  }

  // Update status flag
  //
  if (dibUp)
    Set(biShowGlyph);
  else
    Clear(biShowGlyph);
}

void TGlyphDib::EvPaint()
{
  TPaintDC dc(*this);
  TRect&   rect = *(TRect*)&dc.Ps.rcPaint;
  Paint(dc, dc.Ps.fErase, rect);
}

void TGlyphDib::Paint(TDC& dc, bool /*erase*/, TRect& /*rect*/)
{
  PaintButton(dc);
}
 

bool TGlyphDib::EvEraseBkgnd(HDC /*dc*/)
{
  return true;
}

void TGlyphDib::EvSetFocus(THandle /*hWndLostFocus*/)
{
  Set(biFocus);
  Invalidate(true);
}

void TGlyphDib::EvKillFocus(THandle /*hWndGetFocus*/)
{
  Clear(biFocus);
  if (IsSet(biPushed))
    ClearCapture();
  else
    Invalidate(true);
}

HFONT TGlyphDib::EvGetFont()
{
  PRECONDITION(!BtnFont || BtnFont->IsGDIObject());
  return BtnFont ? HFONT(*BtnFont) : HFONT(0);
}

void TGlyphDib::EvSetFont(HFONT hFont, bool redraw)
{
  delete BtnFont;
  BtnFont = new TFont(hFont);
  if (redraw)
    Invalidate();
}

uint TGlyphDib::EvGetDlgCode(MSG* /*msg*/)
{
  if (IsSet(biDefault))
    return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
  else
    return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
}

void TGlyphDib::EvLButtonDown(uint /*modKeys*/, TPoint& /*point*/)
{
  SetCapture();
  SendMessage(BM_SETSTATE, TRUE);
  if (!IsSet(biFocus))
    SetFocus();
}

void TGlyphDib::EvLButtonDblClk(uint modKeys, TPoint& point)
{
  EvLButtonDown(modKeys, point);
}

void TGlyphDib::EvLButtonUp(uint /*modKeys*/, TPoint& point)
{
  if (GetCapture() == *this) {
    ReleaseCapture();

    SendMessage(BM_SETSTATE, FALSE);

    TRect rect;
    GetClientRect(rect);

    if (rect.Contains(point))
      SendNotificationEx();
  }
}

void TGlyphDib::EvMouseMove(uint modKeys, TPoint& point)
{
  if (modKeys & MK_LBUTTON && GetCapture() == *this) {
    TRect rect;
    GetClientRect(rect);

    if (rect.Contains(point))
      SendMessage(BM_SETSTATE, TRUE);
    else
      SendMessage(BM_SETSTATE, FALSE);
  }
}

void TGlyphDib::EvKeyDown(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  if (key == VK_SPACE)
    SendMessage(BM_SETSTATE, TRUE);
}

//I might want to catch this, I use this instead of
//EV_NOTIFY_AT_CHILD(BN_CLICKED, BNClicked),
void TGlyphDib::SendNotificationEx()
{
  SendNotification(::GetParent(*this), GetDlgCtrlID(),BN_CLICKED, *this);
}

void TGlyphDib::EvKeyUp(uint key, uint /*repeatCount*/, uint /*flags*/)
{
  if (IsSet(biPushed) && key == VK_SPACE) {
    SendMessage(BM_SETSTATE, FALSE);
    SendNotificationEx();
  }
}

// WM_ENABLE handler. Update internal flags and invalidate control if necessary.
void TGlyphDib::EvEnable(bool enabled)
{
  if (enabled) {
    Clear(biDisabled);
  }
  else {
    ClearCapture();
    Set(biDisabled);
  }
  Invalidate(true);
}

void TGlyphDib::EvCancelMode()
{
  ClearCapture();
}

TResult TGlyphDib::BmGetState(TParam1 /*param1*/, TParam2 /*param2*/)
{
  TResult result = 0;
  if (IsSet(biPushed))
    result |= BUTTONSTATE_PUSHED;

  if (IsSet(biFocus))
    result |= BUTTONSTATE_FOCUS;

  return result;
}

TResult TGlyphDib::BmSetState(TParam1 param1, TParam2 /*param2*/)
{
  if (param1) {
    // Needs hilight look
    //
    if (!IsSet(biPushed)) {
      Set(biPushed);
      PaintNow();
    }
  }
  else {
    // Needs normal look
    //
    if (IsSet(biPushed)) {
      Clear(biPushed);
      PaintNow();
    }
  }
  return 0;
}

// BM_SETSTYLE handler. Update internal flags to match specified parameters and invalidate the window if necessary.
TResult TGlyphDib::BmSetStyle(TParam1 param1, TParam2 /*param2*/)
{
  // Grab and splice the styles
  //
  uint32 style = GetStyle();
  uint16 winStyle = HiUint16(style);
  uint16 btnStyle = uint16(LoUint16(style) & BUTTONSTYLE_MASK);

  // Check against passed in parameter
  // NOTE: We only cater to PUSHBUTTON and DEFPUSHBUTTON
  //       The current definition so BS_PUSHBUTTON is 0L
  //
  if (LOWORD(param1) == BS_PUSHBUTTON  &&  btnStyle != BS_PUSHBUTTON) {

    // Make 'simple' push button
    //
    TWindow::SetStyle(MAKELONG(param1, winStyle));
    Clear(biDefault);
    Invalidate(true);
  }
  else if (LOWORD(param1)==BS_DEFPUSHBUTTON && btnStyle != BS_DEFPUSHBUTTON){

    // Make 'default' push button
    //
    TWindow::SetStyle(MAKELONG(param1, winStyle));
    Set(biDefault);
    Invalidate(true);
  }

#if defined(__DEBUG) || defined(__TRACE) || defined(__WARN)
  if (LOWORD(param1) != BS_PUSHBUTTON &&
      LOWORD(param1) != BS_DEFPUSHBUTTON)
    TRACEX(OwlControl, 0, "BmSetStyle: Invalid style specified");
#endif

  return 0;
}

// Release caption if we are in 'capture' mode. Reset internal flags
// appropriately.
void TGlyphDib::ClearCapture()
{
  if (GetCapture() == *this)
    ReleaseCapture();
  Clear(biPushed);
  Invalidate(true);
}

// Paint the button into a memory DC and bitblt the final rendering to the
// specified 'dc'.
void TGlyphDib::PaintButton(TDC& dc)
{
  TRect rect;
  GetClientRect(rect);

  // Create compatible bitmap
  TBitmap memBmp(dc, rect.Width(), rect.Height());
  // Create compatible memory DC
  TMemoryDC memDC(dc);
  // Select and init memory bitmap
  memDC.SelectObject(memBmp);

  // Save the rectangle for bitblt'ing - 'rect' will be adjusted
  // as we move from defaultFrame, border and face...
  TRect bltRect = rect;
  // Paint the button into the memory DC
  PaintDefaultRect(memDC, rect);
  PaintFrame(memDC, rect);
  PaintFace(memDC, rect);

  // Bitblt the button to the output device
  dc.BitBlt(bltRect, memDC, TPoint(bltRect.left, bltRect.top));
  // Cleanup
  memDC.RestoreBitmap();
}

// Draw the border of the button
void TGlyphDib::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();
}

// Draw a frame around the button if it's a default push button.
void TGlyphDib::PaintDefaultRect(TDC& dc, TRect& rect)
{
  if (IsSet(biDefault)) {
    if (rect.Width() > 2 && rect.Height() > 2) {
      TPen framePen(TColor::SysWindowFrame);
      dc.SelectObject(framePen);
      TBrush nullBrush(HBRUSH(::GetStockObject(NULL_BRUSH)));
      dc.SelectObject(nullBrush);
      dc.Rectangle(rect);
      rect.Inflate(-1, -1);
      dc.RestoreBrush();
      dc.RestorePen();
    }
  }
}

// Draw the face of the button [i.e. text and glyph portions]
void TGlyphDib::PaintFace(TDC& dc, TRect& rect)
{
  // Fill the background with the face color
  //
  TBrush brush(TColor::Sys3dFace);
  dc.FillRect(rect, brush);

  // Grab the glyph and it's size
  //
  TDib* glyph = 0;             // Pointer to glyph
  TRect glyphRect(0, 0, 0, 0);    // Size of glyph
  if (IsSet(biShowGlyph)) {
    // Start with the up dib
    //
    glyph = dibUp;

    // Switch to more appropriate dib if applicable
    //
    if (IsSet(biPushed) && dibDown)
      glyph = dibDown;

    CHECK(glyph && glyph->IsGDIObject());
    glyphRect.Set(0, 0, glyph->Width(), glyph->Height());
  }

  // Grab some information about the text/caption
  //
  int len = 0;                    // Length of Caption
  TRect textRect(0, 0, 0, 0);     // Size of text
  TAPointer<char> 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 hFont = HFONT(::SendMessage(::GetParent(*this), WM_GETFONT,
                                          0, 0));
        if (!hFont)
          hFont = HFONT(GetStockObject(TSystem::Has3dUI() ? ANSI_VAR_FONT :
                                                            SYSTEM_FONT));
        if (hFont)
          tmpFnt = new TFont(hFont);
      }
      if (BtnFont) {
        CHECK(BtnFont->IsGDIObject());
        dc.SelectObject(*BtnFont);
      }
      else if (tmpFnt) {
        CHECK(((TFont&)tmpFnt).IsGDIObject());
        dc.SelectObject((TFont&)tmpFnt);
      }

      text = new char[len+1];
      GetWindowText(text, len+1);
      textRect.Set(0, 0, rect.Width() - glyphRect.Width(), SHRT_MAX);

      // Display text in proper color
      //
      // Under NT 3.51 and Window 3.1, the system inexplicably returns values
      // for gray text that are the same as either button text (so text never
      // looks grayed) or as button face (so grayed text is invisible).  The
      // extra if statement guards against those problems.
      if (IsSet(biDisabled)) {
        textColor = TColor::SysGrayText;
        if ((textColor == TColor::Sys3dFace.GetValue()) ||
            (textColor == TColor::SysBtnText.GetValue())) {
          textColor = TColor::Sys3dShadow;
        }
      } else {
        textColor = TColor::SysBtnText;
      }

      TColor oldColor = dc.SetTextColor(textColor);
      dc.DrawText(text, len, textRect, DT_WORDBREAK|DT_CALCRECT);
      dc.SetTextColor(oldColor);
    }
  }

  // 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() && glyph == dibUp)
        glyphRect.Offset(1, 1);
      if (!textRect.IsNull())
        textRect.Offset(1, 1);
    }

    // Paint the components of the button
    //
    if (!glyphRect.IsNull()) {
      PRECONDITION(glyph && glyph->IsGDIObject());

      if(IsSet(biDisabled))
      {
        TBitmap  bitmap(*glyph, &TPalette((HPALETTE)::GetStockObject(DEFAULT_PALETTE)));
        TUIFace uiFace(glyphRect, bitmap);
        uiFace.Paint(dc, TPoint(0, 0), IsSet(biDisabled)?TUIFace::Disabled:TUIFace::Normal, IsSet(biPushed));
      }
      else
      {
        dc.SetDIBitsToDevice(glyphRect,TPoint(0,0),*glyph);
      }

    }
    if (!textRect.IsNull()) {
      int mode = dc.SetBkMode(TRANSPARENT);
      TColor oldColor = dc.SetTextColor(textColor);
      dc.DrawText(text, len, textRect, DT_WORDBREAK);
      dc.SetTextColor(oldColor);
      dc.SetBkMode(mode);
    }
  }

  // Paint the focus rect [if necessary]
  //
  if (IsSet(biFocus))
    PaintFocusRect(dc, rect);

  // 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
TGlyphDib::LayoutTextGlyph(const TRect& faceRect, TRect& textRect,
                              TRect& glyphRect)
{
  // Must have either text or a glyph
  //
  PRECONDITION(!textRect.IsNull() || !glyphRect.IsNull());

  // First check for the case where we've got either
  // text or glyph - but not both
  //
  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 space  = faceRect.Width() - glyphRect.Width()- textRect.Width() -
                 LayoutMargin*2;
    int gDelta;
    int tDelta;

    switch (LayStyle) {

     case lsH_SGST: {
              gDelta = space >= 0 ? LayoutMargin + space/3 :
                                    LayoutMargin + space/2;
              tDelta = space >= 0 ? gDelta + glyphRect.Width() + space/3 :
                                    gDelta + glyphRect.Width();
            }
            break;

      case lsH_GST: {
              gDelta = space >= 0 ? LayoutMargin : LayoutMargin + space/2;
              tDelta = space >= 0 ? gDelta + glyphRect.Width() + space/2 :
                                    gDelta + glyphRect.Width();
            }
            break;

      default:
        break;
    }

    if (LayStyle == lsH_SGST || LayStyle == lsH_GST)
    {
      // Center vertically
      //
      if (textRect.Height() < faceRect.Height())
        textRect.Offset(0, (faceRect.Height() - textRect.Height())/2);
      if (glyphRect.Height() < faceRect.Height())
        glyphRect.Offset(0, (faceRect.Height() - glyphRect.Height())/2);

      // Layout horizontally
      //
      glyphRect.Offset(gDelta, 0);
      textRect.Offset(tDelta, 0);
    }
  }
}

//
// Specify a 'style' describing how text and glyph should be laid out.
// Invalidate the window if necessary.
//
void
TGlyphDib::SetLayoutStyle(TLayoutStyle style)
{
  if (style != LayStyle) {
    LayStyle = style;
    if (GetHandle())
      Invalidate();
  }
}

//
// Sets text coordinates and invalidates window if necessary
//
void
TGlyphDib::SetTextOrigin(int x, int y)
{
  if (x != xText || y != yText) {
    xText = x;
    yText = y;
    if (GetHandle())
      Invalidate();
  }
}

//
// Set the upper left corner of glyphs and invalidates window if necessary
//
void
TGlyphDib::SetGlyphOrigin(int x, int y)
{
  if (x != xGlyph || y != yGlyph) {
    xGlyph = x;
    yGlyph = y;
    if (GetHandle())
      Invalidate();
  }
}

//
// Display a focus rectangle
//
void
TGlyphDib::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
TGlyphDib::PaintNow()
{
  TRect rect;
  GetClientRect(rect);
  TClientDC dc(*this);
  Paint(dc, false, rect);
}
 

///added
TDib*  TGlyphDib::dibOf(TGlyphType gt)
{
  switch(gt){
    case gtUp:         return dibUp;
    case gtDisabled:   return dibDisabled;
    case gtDown:       return dibDown;
    case gtFocus:     return dibFocus;
    case gtNegative:  return dibNegative;
    default: break;
  }
  return NULL;
}

TFont* TGlyphDib::BtnFontOf()
{
  return BtnFont;
}