/**************************************************************************

    This file is part of Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Hector Peraza.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************/

#include <xclass/utils.h>
#include <xclass/OXWidgets.h>
#include <xclass/OXClient.h>
#include <xclass/OString.h>

#include <X11/cursorfont.h>
#include <X11/keysym.h>


//-----------------------------------------------------------------

OXButton::OXButton(const OXWindow *p, int ID, GC norm, unsigned int options)
  : OXFrame(p, 1, 1, options) {
    
    _widgetID = ID;
    _widgetType = "OXButton";
    _widgetFlags = WIDGET_WANT_FOCUS;
    _msgWindow = p;

    _normGC = norm;
    _prevstate = _state = BUTTON_UP;
    _click = False;
    _type = BUTTON_NORMAL;

    XGrabButton(GetDisplay(), Button1, AnyModifier, _id, True,
		ButtonPressMask | ButtonReleaseMask |
		EnterWindowMask | LeaveWindowMask,
		GrabModeAsync, GrabModeAsync, None, None);
}

void OXButton::SetState(int state) {

  if (state != _state) {
    switch(state) {
    case BUTTON_ENGAGED:
    case BUTTON_DOWN:
              _options &= ~RAISED_FRAME;
              _options |= SUNKEN_FRAME;
              break;

    case BUTTON_DISABLED:
    case BUTTON_UP:
              _options &= ~SUNKEN_FRAME;
              _options |= RAISED_FRAME;
              break;
    }
    _state = state;
    _client->NeedRedraw(this);
  }
}

int OXButton::HandleButton(XButtonEvent *event) {
  
  OXFrame::HandleButton(event);
  if (_state == BUTTON_DISABLED) return True;

  switch(_type) {
  default:
  case BUTTON_NORMAL:
    if (event->type == ButtonPress) {
      _click = True;
      _prevstate = _state;
      SetState(BUTTON_DOWN);
    } else {
      _click = False;
      if (_state != _prevstate) {
        if (_state == BUTTON_DOWN) {
          SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON),
                      _widgetID, 0L);
          SetState(BUTTON_UP);
        }
      }
    }
    break;

  case BUTTON_STAYDOWN:
    if (event->type == ButtonPress) {
      _click = True;
      _prevstate = _state;
      if (_state == BUTTON_UP) SetState(BUTTON_DOWN);
    } else {
      _click = False;
      if (_state != _prevstate) {
        if (_state == BUTTON_DOWN) {
          SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON),
                      _widgetID, 0L);
          SetState(BUTTON_ENGAGED);
        }
      }
    }
    break;

  case BUTTON_ONOFF:
    if (event->type == ButtonPress) {
      _click = True;
      _prevstate = _state;
      SetState(BUTTON_DOWN);
    } else {
      _click = False;
      if (_state != _prevstate) {
        if (_prevstate == BUTTON_UP) {
          SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON),
                      _widgetID, 0L);
          SetState(BUTTON_ENGAGED);
        } else if (_prevstate == BUTTON_ENGAGED) {
          SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON),
                      _widgetID, 0L);
          SetState(BUTTON_UP);
        }
      }
    }
    break;
  }

  return True;
}

int OXButton::HandleCrossing(XCrossingEvent *event) {

  OXFrame::HandleCrossing(event);
  if (_state == BUTTON_DISABLED) return True;
  if (!_click) return True;

  switch(_type) {
  default:
  case BUTTON_NORMAL:
    if (event->type == LeaveNotify) {
      SetState(_prevstate);
    } else {
      SetState(BUTTON_DOWN);
    }
    break;

  case BUTTON_STAYDOWN:
  case BUTTON_ONOFF:
    if (event->type == LeaveNotify) {
      SetState(_prevstate);
    } else {
      if (_prevstate != BUTTON_ENGAGED) SetState(BUTTON_DOWN);
    }
    break;
  }

  return True;
}

//-----------------------------------------------------------------

OXTextButton::OXTextButton(const OXWindow *p, OHotString *s, int ID,
                   GC norm, XFontStruct *font, unsigned int options)
  : OXButton(p, ID, norm, options) {
    int hotchar;

    _widgetType = "OXTextButton";

    _label = s;
    _fontStruct = font;
    _tmode = TEXT_CENTER_X | TEXT_CENTER_Y;
    _hkeycode = 0;

    _tw = XTextWidth(_fontStruct, _label->GetString(), _label->GetLength());
    _th = _fontStruct->max_bounds.ascent + _fontStruct->max_bounds.descent;

    Resize(_tw + 8, _th + 7);

    if ((hotchar = s->GetHotChar()) != 0) {
      if ((_hkeycode = XKeysymToKeycode(GetDisplay(), hotchar)) != 0) {
        const OXMainFrame *main = (OXMainFrame *) _GetMainFrame();
        main->BindKey(this, _hkeycode, Mod1Mask);
      }
    }

}

OXTextButton::~OXTextButton() {
  if (_hkeycode) {
    const OXMainFrame *main = (OXMainFrame *) _GetMainFrame();
    main->RemoveBind(this, _hkeycode, Mod1Mask);
  }
  if (_label) delete _label;
}

void OXTextButton::SetText(OHotString *new_label) {

  if (_label) delete _label;
  _label = new_label;

  _tw = XTextWidth(_fontStruct, _label->GetString(), _label->GetLength());
  _th = _fontStruct->max_bounds.ascent + _fontStruct->max_bounds.descent;

//  Resize(_tw + 8, _th + 7);

  _client->NeedRedraw(this);
}

void OXTextButton::_DoRedraw() {
  int x, y;
  
  OXFrame::_DoRedraw();

  if (_tmode & TEXT_LEFT)
    x = 4;
  else if (_tmode & TEXT_RIGHT)
    x = _w - _tw - 4;
  else
    x = (_w - _tw) >> 1;

  if (_tmode & TEXT_TOP)
    y = 3;
  else if (_tmode & TEXT_BOTTOM)
    y = _h - _th -3;
  else
    y = (_h - _th) >> 1;

  if (_state == BUTTON_DOWN || _state == BUTTON_ENGAGED) { ++x; ++y; }
  if (_state == BUTTON_ENGAGED) {
    XFillRectangle (GetDisplay(), _id, _hibckgndGC, 2, 2, _w-4, _h-4);
    XDrawLine (GetDisplay(), _id, _hilightGC, 2, 2, _w-3, 2);
  }
  if (_state == BUTTON_DISABLED) {
    _label->Draw(GetDisplay(), _id, _hilightGC, x+1, y+1 + _fontStruct->max_bounds.ascent);
    _label->Draw(GetDisplay(), _id, _shadowGC, x, y + _fontStruct->max_bounds.ascent);
  } else {
    _label->Draw(GetDisplay(), _id, _normGC, x, y + _fontStruct->max_bounds.ascent);
  }
}

int OXTextButton::HandleKey(XKeyEvent *event) {
  int click = False;

  if (_state == BUTTON_DISABLED) return True;

  // We don't need to check the key number as XGrabKey will
  // only allow _hotchar events
  if (event->type == KeyPress) {
    if (_state == BUTTON_ENGAGED) return True;
    SetState(BUTTON_DOWN);
  } else { // KeyRelease
    if (_state == BUTTON_ENGAGED /*&& !allowRelease*/) return True;
    click = (_state == BUTTON_DOWN);
    if (click && _type)
      SetState(BUTTON_ENGAGED);
    else
      SetState(BUTTON_UP);
  }
  if (click)
    SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_BUTTON),
                _widgetID, 0L);

  return True;
}

//-----------------------------------------------------------------

OXPictureButton::OXPictureButton(const OXWindow *p, const OPicture *pic, int ID,
                                 GC norm, unsigned int option)
  : OXButton(p, ID, norm, option) {

    _widgetType = "OXPictureButton";

    _pic = pic;

    _tw = _pic->GetWidth();
    _th = _pic->GetHeight();

    Resize(_tw  + (_bw << 1) + _bw + 1,
           _th + (_bw << 1) + _bw); // *3
  }

void OXPictureButton::_DoRedraw() {

  int x = (_w - _tw) >> 1;
  int y = (_h - _th) >> 1;

  OXFrame::_DoRedraw();
  if (_state == BUTTON_DOWN || _state == BUTTON_ENGAGED) { ++x; ++y; }
  if (_state == BUTTON_ENGAGED) {
    XFillRectangle (GetDisplay(), _id, _hibckgndGC, 2, 2, _w-4, _h-4);
    XDrawLine (GetDisplay(), _id, _hilightGC, 2, 2, _w-3, 2);
  }
  _pic->Draw(_client->GetDisplay(), _id, _normGC, x, y);
}


//-----------------------------------------------------------------

OXCheckButton::OXCheckButton(const OXWindow *p, OHotString *s, int ID,
                             GC norm, XFontStruct *font, unsigned int option)
  : OXButton(p, ID, norm, option) {

    _widgetType = "OXCheckButton";

    _label = s;
    _fontStruct = font;

    _tw = XTextWidth(_fontStruct, _label->GetString(), _label->GetLength());
    _th = _fontStruct->max_bounds.ascent + _fontStruct->max_bounds.descent;

    Resize(_tw + 22, _th + 2);

    XGrabButton(GetDisplay(), Button1, AnyModifier, _id, True,
		ButtonPressMask | ButtonReleaseMask |
		EnterWindowMask | LeaveWindowMask,
		GrabModeAsync, GrabModeAsync, None, None);
    
}

OXCheckButton::~OXCheckButton() {
  if (_label) delete _label;
}

void OXCheckButton::_SetState(int state) {
  if (state != _state) {
    _state = state;
    _client->NeedRedraw(this);
  }
}

int OXCheckButton::HandleButton(XButtonEvent *event) {
  int click = False;

  if (_state == BUTTON_DISABLED) return True;

  // We don't need to check the button number as XGrabButton will
  // only allow button1 events
  if (event->type == ButtonPress) {
    _SetState((_prevstate == BUTTON_UP) ? BUTTON_DOWN : BUTTON_UP);
  } else { // ButtonRelease
    click = (_state != _prevstate);
    _prevstate = _state;
  }
  if (click)
    SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_CHECKBUTTON),
                _widgetID, 0L);
  return True;
}

int OXCheckButton::HandleCrossing(XCrossingEvent *event) {

  if (_state == BUTTON_DISABLED) return True;
  if (event->state != Button1Mask) return True;

  if (event->type == EnterNotify) {
    _SetState((_prevstate == BUTTON_UP) ? BUTTON_DOWN : BUTTON_UP);
  } else {
    _SetState(_prevstate);
  }
  return True;
}

void OXCheckButton::_DoRedraw() {
  int x, y, y0, cw;
  
  OXFrame::_DoRedraw();

  cw = 13;
  y0 = (_h - cw) >> 1;

  XDrawLine (GetDisplay(), _id, _shadowGC, 0, y0, cw-2, y0);
  XDrawLine (GetDisplay(), _id, _shadowGC, 0, y0, 0, y0+cw-2);
  XDrawLine (GetDisplay(), _id, _blackGC, 1, y0+1, cw-3, y0+1);
  XDrawLine (GetDisplay(), _id, _blackGC, 1, y0+1, 1, y0+cw-3);

  XDrawLine (GetDisplay(), _id, _hilightGC, 0, y0+cw-1, cw-1, y0+cw-1);
  XDrawLine (GetDisplay(), _id, _hilightGC, cw-1, y0+cw-1, cw-1, y0);
  XDrawLine (GetDisplay(), _id, _bckgndGC,  2, y0+cw-2, cw-2, y0+cw-2);
  XDrawLine (GetDisplay(), _id, _bckgndGC,  cw-2, y0+2, cw-2, y0+cw-2);

  XFillRectangle (GetDisplay(), _id, _whiteGC, 2, y0+2, cw-4, cw-4);

  if (_state == BUTTON_DOWN) {
    XSegment seg[6];

    int l = 2;
    int t = y0+2; 

    seg[0].x1 = 1+l; seg[0].y1 = 3+t; seg[0].x2 = 3+l; seg[0].y2 = 5+t;
    seg[1].x1 = 1+l; seg[1].y1 = 4+t; seg[1].x2 = 3+l; seg[1].y2 = 6+t;
    seg[2].x1 = 1+l; seg[2].y1 = 5+t; seg[2].x2 = 3+l; seg[2].y2 = 7+t;
    seg[3].x1 = 3+l; seg[3].y1 = 5+t; seg[3].x2 = 7+l; seg[3].y2 = 1+t;
    seg[4].x1 = 3+l; seg[4].y1 = 6+t; seg[4].x2 = 7+l; seg[4].y2 = 2+t;
    seg[5].x1 = 3+l; seg[5].y1 = 7+t; seg[5].x2 = 7+l; seg[5].y2 = 3+t;

    XDrawSegments(GetDisplay(), _id, _blackGC, seg, 6);
  }

  x = 20;
  y = 1;

  if (_state == BUTTON_DISABLED) {
    _label->Draw(GetDisplay(), _id, _hilightGC, x+1, y+1 + _fontStruct->max_bounds.ascent);
    _label->Draw(GetDisplay(), _id, _shadowGC, x, y + _fontStruct->max_bounds.ascent);
  } else {
    _label->Draw(GetDisplay(), _id, _normGC, x, y + _fontStruct->max_bounds.ascent);
  }
}

//-----------------------------------------------------------------

OXRadioButton::OXRadioButton(const OXWindow *p, OHotString *s, int ID,
                             GC norm, XFontStruct *font, unsigned int option)
  : OXButton(p, ID, norm, option) {

    _widgetType = "OXRadioButton";

    _label = s;
    _fontStruct = font;

    if (!_on || !_off) FatalError("OXRadioButton: missing required pixmaps");

    _tw = XTextWidth(_fontStruct, _label->GetString(), _label->GetLength());
    _th = _fontStruct->max_bounds.ascent + _fontStruct->max_bounds.descent;

    Resize(_tw + 22, _th + 2);

    XGrabButton(GetDisplay(), Button1, AnyModifier, _id, True,
		ButtonPressMask | ButtonReleaseMask |
		EnterWindowMask | LeaveWindowMask,
		GrabModeAsync, GrabModeAsync, None, None);
    
}

OXRadioButton::~OXRadioButton() {
  if (_label) delete _label;
}

void OXRadioButton::_SetState(int state) {
  if (state != _state) {
    _prevstate = _state = state;
    _client->NeedRedraw(this);
  }
}

int OXRadioButton::HandleButton(XButtonEvent *event) {
  int click = False;

  if (_state == BUTTON_DISABLED) return True;

  // We don't need to check the button number as XGrabButton will
  // only allow button1 events
  if (event->type == ButtonPress) {
    _SetState(BUTTON_DOWN);
SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_RADIOBUTTON),
            _widgetID, 0L);
  } else { // ButtonRelease
    click = (_state != _prevstate);
    _prevstate = _state;
  }
//  if (click)
//    SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_RADIOBUTTON),
//                _widgetID, 0L);
  return True;
}

int OXRadioButton::HandleCrossing(XCrossingEvent *event) {

  if (_state == BUTTON_DISABLED) return True;
  if (event->state != Button1Mask) return True;

  if (event->type == EnterNotify) {
    _SetState(BUTTON_DOWN);
  } else {
    _SetState(_prevstate);
  }
  return True;
}

void OXRadioButton::_DoRedraw() {
  int nlines, tx, ty, y0, pw;

  OXFrame::_DoRedraw();

  tx = 20;
  nlines = _label->GetLines(_fontStruct, _w-tx-1);
  ty = (_h - _th*nlines) >> 1;

  pw = 12;
  y0 = ty + ((_th - pw) >> 1);

  if (_state == BUTTON_DOWN)
    _on->Draw(GetDisplay(), _id, _normGC, 0, y0);
  else
    _off->Draw(GetDisplay(), _id, _normGC, 0, y0);

//  ty = _fontStruct->max_bounds.ascent + 1;

  ty += _fontStruct->max_bounds.ascent;

//  _label->Draw(GetDisplay(), _id, _normGC, tx, ty);
  if (_state == BUTTON_DISABLED) {
    _label->DrawWrapped(GetDisplay(), _id, _hilightGC, tx+1, ty+1, _w-tx-1, _fontStruct);
    _label->DrawWrapped(GetDisplay(), _id, _shadowGC, tx, ty, _w-tx-1, _fontStruct);
  } else {
    _label->DrawWrapped(GetDisplay(), _id, _normGC, tx, ty, _w-tx-1, _fontStruct);
  }
}
