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

    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/OXButton.h>
#include <xclass/OXClient.h>
#include <xclass/OString.h>

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


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

OXGroupFrame::OXGroupFrame(const OXWindow *p, OString *title,
                           unsigned int options,
                           GC norm, XFontStruct *font, unsigned long back) :
  OXCompositeFrame(p, 1, 1, options, back) {

    _text = title;  
    _fontStruct = font;
    _normGC = norm;

    _bw = _fontStruct->max_bounds.ascent +
          _fontStruct->max_bounds.descent + 1;
}

OXGroupFrame::~OXGroupFrame() {
  delete _text;
}

void OXGroupFrame::DrawBorder() {
  int x, y, tw, l, t, r, b, gl, gr, sep;

  tw = XTextWidth(_fontStruct, _text->GetString(), _text->GetLength());

  l = 0;
  t = (_fontStruct->max_bounds.ascent + _fontStruct->max_bounds.descent + 2) >> 1;
  r = _w-1;
  b = _h-1;

  sep = 3;
  gl = 5 + sep;
  gr = gl + tw + (sep << 1);

  XDrawLine(GetDisplay(), _id, _shadowGC,  l,   t,   gl,  t);
  XDrawLine(GetDisplay(), _id, _hilightGC, l+1, t+1, gl,  t+1);
  XDrawLine(GetDisplay(), _id, _shadowGC,  gr,  t,   r-1, t);
  XDrawLine(GetDisplay(), _id, _hilightGC, gr,  t+1, r-2, t+1);

  XDrawLine(GetDisplay(), _id, _shadowGC,  r-1, t,   r-1, b-1);
  XDrawLine(GetDisplay(), _id, _hilightGC, r,   t,   r,   b);

  XDrawLine(GetDisplay(), _id, _shadowGC,  r-1, b-1, l,   b-1);
  XDrawLine(GetDisplay(), _id, _hilightGC, r,   b,   l,   b);

  XDrawLine(GetDisplay(), _id, _shadowGC,  l,   b-1, l,   t);
  XDrawLine(GetDisplay(), _id, _hilightGC, l+1, b-2, l+1, t+1);

  x = gl + sep;
  y = 1;

  _text->Draw(GetDisplay(), _id, _normGC, x, y + _fontStruct->max_bounds.ascent);
}


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

OXDDPopup::OXDDPopup(const OXWindow *p, int w, int h,
                     unsigned int options, unsigned long back) :
  OXCompositeFrame (p, w, h, options, back) {
    XSetWindowAttributes wattr;
    unsigned long mask;

    mask = CWOverrideRedirect | CWSaveUnder | CWBorderPixel;
    wattr.override_redirect = True;
    wattr.save_under = True;
    wattr.border_pixel = _blackPixel;
    XChangeWindowAttributes(GetDisplay(), _id, mask, &wattr);
    XSetWindowBorderWidth(GetDisplay(), _id, 1);

    XSelectInput(GetDisplay(), _id, StructureNotifyMask);
}

int OXDDPopup::HandleButton(XButtonEvent *event) {
  if (event->type == ButtonRelease) EndPopup();
  return True;
}

void OXDDPopup::EndPopup() {
  XUngrabPointer(GetDisplay(), CurrentTime);
  UnmapWindow();
}

void OXDDPopup::PlacePopup(int x, int y, int w, int h) {
  int rx, ry;
  unsigned int rw, rh;
  unsigned int dummy;
  Window wdummy;

  // Parent is root window for the popup:
  XGetGeometry(GetDisplay(), _parent->GetId(), &wdummy,
               &rx, &ry, &rw, &rh, &dummy, &dummy);

  if (x < 0) x = 0;
  if (x + _w > rw) x = rw - _w;
  if (y < 0) y = 0;
  if (y + _h > rh) y = rh - _h;

  MoveResize(x, y, w, h);
  MapSubwindows();
  Layout();
  MapRaised();

  XGrabPointer(GetDisplay(), _id, True,
               ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
               GrabModeAsync, GrabModeAsync, None, _defaultCursor,
               CurrentTime);

  _client->WaitForUnmap(this);
  EndPopup();
}

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

OXDDListBox::OXDDListBox(const OXWindow *p, int ID,
                         unsigned int options, unsigned long back) :
  OXCompositeFrame (p, 10, 10, options | OWN_BKGND, back) {

    _ID = ID;
    _msgWindow = p;

    if (!_bpic) FatalError("OXDDListBox: missing required pixmap");

    _selentry = new OXTextLBEntry(this, new OString(""), 0);
    _ddbutton = new OXScrollBarElt(this, _bpic, SB_WIDTH, SB_WIDTH,
                                   RAISED_FRAME);

    _selentry->ChangeOptions(_selentry->GetOptions() | OWN_BKGND);

    AddFrame(_selentry, _lhs = new OLayoutHints(LHINTS_LEFT |
                                   LHINTS_EXPAND_Y | LHINTS_EXPAND_X));
                                   //0, 0, 1, 0));
    AddFrame(_ddbutton, _lhb = new OLayoutHints(LHINTS_RIGHT |
                                   LHINTS_EXPAND_Y));

    _ddframe = new OXDDPopup(_client->GetRoot(), 100, 100, VERTICAL_FRAME);

    _lb = new OXListBox(_ddframe, _ID, CHILD_FRAME);
    _lb->Resize(100, 100);
    _lb->Associate(this);

    _ddframe->AddFrame(_lb, _lhdd = new OLayoutHints(LHINTS_EXPAND_X |
                                        LHINTS_EXPAND_Y));
    _ddframe->MapSubwindows();
    _ddframe->Resize(_ddframe->GetDefaultSize());

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

OXDDListBox::~OXDDListBox() {
  delete _ddbutton;
  delete _selentry;
  delete _lb;
  delete _ddframe;
  delete _lhs;
  delete _lhb;
  delete _lhdd;
}

void OXDDListBox::DrawBorder() {
  switch (_options & (SUNKEN_FRAME | RAISED_FRAME | DOUBLE_BORDER)) {
  case SUNKEN_FRAME | DOUBLE_BORDER:
    XDrawLine (GetDisplay(), _id, _shadowGC, 0, 0, _w-2, 0);
    XDrawLine (GetDisplay(), _id, _shadowGC, 0, 0, 0, _h-2);
    XDrawLine (GetDisplay(), _id, _blackGC, 1, 1, _w-3, 1);
    XDrawLine (GetDisplay(), _id, _blackGC, 1, 1, 1, _h-3);

    XDrawLine (GetDisplay(), _id, _hilightGC, 0, _h-1, _w-1, _h-1);
    XDrawLine (GetDisplay(), _id, _hilightGC, _w-1, _h-1, _w-1, 0);
    XDrawLine (GetDisplay(), _id, _bckgndGC,  1, _h-2, _w-2, _h-2);
    XDrawLine (GetDisplay(), _id, _bckgndGC,  _w-2, 1, _w-2, _h-2);
    break;

  default:
    OXCompositeFrame::DrawBorder();
    break;
  }
}

void OXDDListBox::SetTopEntry(OXLBEntry *e, OLayoutHints *lh) {
  RemoveFrame(_selentry);
  _selentry->DestroyWindow();
  delete _selentry;
  delete _lhs;
  _selentry = e;
  _lhs = lh;
  AddFrame(_selentry, _lhs);
  Layout();
}

void OXDDListBox::Select(int ID) {
  OXLBEntry *e;

  e = _lb->Select(ID);
  if (e) {
    _selentry->UpdateEntry(e);
    Layout();
  }
}

int OXDDListBox::HandleButton(XButtonEvent *event) {
  if (event->type == ButtonPress) {
    if (event->subwindow == _ddbutton->GetId()) 
      _ddbutton->SetState(BUTTON_DOWN);
  } else {
    int ax, ay;
    Window wdummy;

    _ddbutton->SetState(BUTTON_UP);
    XTranslateCoordinates(GetDisplay(), _id, (_ddframe->GetParent())->GetId(),
                          0, _h, &ax, &ay, &wdummy);

    _ddframe->PlacePopup(ax, ay, _w-2, _ddframe->GetDefaultHeight());
  }
  return True;
}

int OXDDListBox::ProcessMessage(long msg, long parm1, long parm2) {
  OXLBEntry *e;

  switch(GET_MSG(msg)) {
    case XC_COMMAND:
      switch(GET_SUBMSG(msg)) {
        case CM_LISTBOX:
          e = _lb->GetSelectedEntry();
          _selentry->UpdateEntry(e);
          Layout();
          _ddframe->EndPopup();
          SendMessage(_msgWindow, MK_MSG(XC_COMMAND, CM_DDLISTBOX),
                                  _ID, parm2);
          break;
      }
      break;
  }

  return True;
}
