/**************************************************************************\
 ibtk (Insomnia's Basic ToolKit)

  By Insomnia (Steaphan Greene)
  (insomnia@core.binghamton.edu)

  Copyright (C) 1999 Steaphan Greene

  This program 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.

\**************************************************************************/

#include <string.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#include "ibox.h"

IBox::~IBox()  {
  Win->RemoveClaim(this);
  }

void IBox::Resize(int x, int y)  {
  Init(text, Win, xpos, ypos, x, y);
  }

IBox::IBox(char *txt, IWindow *w, int xp, int yp, int xs, int ys)  {
  Init(txt, w, xp, yp, xs, ys);
  }

void IBox::Init(char *txt, IWindow *w, int xp, int yp, int xs, int ys)  {
  xpos = xp; ypos = yp; xsize = xs; ysize = ys;
  Win = w;
  Window wind = w->GetWindowBuffer();
  Display *disp = w->GetDisplay();
  unsigned int p, b, g, gl, gd;
  Img[0] = XCreatePixmap(disp, wind, xs, ys, DefaultDepth(disp, 0));
  Img[1] = XCreatePixmap(disp, wind, xs, ys, DefaultDepth(disp, 0));
  GC gc = XCreateGC(disp, wind, None, NULL);

//  printf("Depth = %d\n", DefaultDepth(disp, 0));

  p = w->GetPaperColor();
  b = w->GetFGColor();
  g = w->GetBGColor();
  gl = w->GetLBGColor();
  gd = w->GetDBGColor();

  XSetBackground(disp, gc, p);
  XSetForeground(disp, gc, gl);
  XFillRectangle(disp, Img[0], gc, 0, 0, xs, ys);
  XFillRectangle(disp, Img[1], gc, 0, 0, xs, ys);

  XSetForeground(disp, gc, p);
  XFillRectangle(disp, Img[0], gc, 2, 2, xs-4, ys-4);

  XSetForeground(disp, gc, g);
  XFillRectangle(disp, Img[1], gc, 2, 2, xs-4, ys-4);

  XSetForeground(disp, gc, gd);
  XFillRectangle(disp, Img[0], gc, 0, 0, xs-1, 2);
  XFillRectangle(disp, Img[0], gc, 0, 2, 2, ys-3);
  XDrawPoint(disp, Img[0], gc, 0, ys-1);
  XDrawPoint(disp, Img[0], gc, xs-1, 0);
  XFillRectangle(disp, Img[1], gc, 0, 0, xs-1, 2);
  XFillRectangle(disp, Img[1], gc, 0, 2, 2, ys-3);
  XDrawPoint(disp, Img[1], gc, 0, ys-1);
  XDrawPoint(disp, Img[1], gc, xs-1, 0);

  disabled = 0;
  changecallback = NULL;
  parent=NULL;
  nextfocus=NULL;
  Wrk[0] = None;
  Wrk[1] = None;
  text[0] = 0;

  xscroll = 0;
  tcp = 0;
  tcp2 = 0;
  gcp = 0;
  gcp2 = 0;
  SetText(txt);

  w->AddClaim(this, xp, yp, xs, ys);

  XFreeGC(disp, gc);
  Rebuild();
  }

void IBox::SetText(char *txt)  {
  Window wind = Win->GetWindowBuffer();
  Display *disp = Win->GetDisplay();
  GC gc = Win->GetGC();

  unsigned int p, b, g, gl, gd, s;
  p = Win->GetPaperColor();
  b = Win->GetFGColor();
  g = Win->GetBGColor();
  gl = Win->GetLBGColor();
  gd = Win->GetDBGColor();
  s = Win->GetSelectColor();

  sprintf(text, "%s%c", txt, 0);
  if(tcp > (long)strlen(text)) { tcp = strlen(text); tcp2 = tcp; }
  if(tcp2 < tcp) tcp2 = tcp;
  gcp = 0;

  XFontStruct *fs;
  XCharStruct cs;
  int dir, as, des;
  fs = Win->GetFontStruct();
//  Wlen = XTextWidth(fs, text, strlen(text));
  XTextExtents(fs, text, strlen(text), &dir, &as, &des, &cs);
//  printf("dir = %d, as = %d, des = %d\n", dir, as, des);
  Wlen = cs.width+4;
//  int hgt = cs.
  if(Wrk[0] != None) XFreePixmap(disp, Wrk[0]);
  if(Wrk[1] != None) XFreePixmap(disp, Wrk[1]);
  Wrk[0] = XCreatePixmap(disp, wind, Wlen, ysize-4, DefaultDepth(disp, 0));
  Wrk[1] = XCreatePixmap(disp, wind, Wlen, ysize-4, DefaultDepth(disp, 0));
  XSetForeground(disp, gc, p);
  XFillRectangle(disp, Wrk[0], gc, 0, 0, Wlen, ysize-4);
  XSetForeground(disp, gc, g);
  XFillRectangle(disp, Wrk[1], gc, 0, 0, Wlen, ysize-4);
  XSetBackground(disp, gc, p);
  XSetForeground(disp, gc, b);
  XDrawImageString(disp, Wrk[0], gc, 2, ((ysize+ysize-(as+des-2))>>1)-2,
	text, strlen(text));
  XSetBackground(disp, gc, g);
  XSetForeground(disp, gc, gl);
  XDrawImageString(disp, Wrk[1], gc, 3, ((ysize+ysize-(as+des-2))>>1)-1,
	text, strlen(text));
  XSetForeground(disp, gc, gd);
  XDrawString(disp, Wrk[1], gc, 2, ((ysize+ysize-(as+des-2))>>1)-2,
	text, strlen(text));
  if(tcp > 0) gcp = XTextWidth(fs, text, tcp);
  if(tcp2 > 0) gcp2 = XTextWidth(fs, text, tcp2);
  if(Win->GetFocus() == this && (!disabled) && tcp2>tcp) {
    Pixmap tmppm;
    tmppm = XCreatePixmap(disp, wind, Wlen, ysize-4, DefaultDepth(disp, 0));
    XSetForeground(disp, gc, s);
    XFillRectangle(disp, tmppm, gc, 0, 0, Wlen, ysize-4);
    XSetBackground(disp, gc, s);
    XSetForeground(disp, gc, p);
    XDrawImageString(disp, tmppm, gc, 0, ((ysize+ysize-(as+des-2))>>1)-4,
	&text[tcp], strlen(&text[tcp]));
    XCopyArea(disp, tmppm, Wrk[disabled], Win->GetGC(),
	0, 0, gcp2-gcp, ysize-4, 2+gcp, 2);
    }
  else if(Win->GetFocus() == this && (!disabled))  {
    XDrawLine(disp, Wrk[0], gc, gcp+2, 1, gcp+2, ysize-6);
    XDrawLine(disp, Wrk[0], gc, gcp+1, 1, gcp+3, 1);
    XDrawLine(disp, Wrk[0], gc, gcp+1, ysize-6, gcp+3, ysize-6);
    }
  Rebuild();
  if(changecallback != NULL)  (*changecallback)(parent, this);
  }

void IBox::Redraw()  {
  if(hidden) { Win->Rebuild(xpos, ypos, xsize, ysize); return; }
  XCopyArea(Win->GetDisplay(), Img[disabled], Win->GetWindowBuffer(), Win->GetGC(),
	0, 0, xsize, ysize, xpos, ypos);
  Win->Redraw(xpos, ypos, xsize, ysize);
  }

void IBox::Rebuild()  {
  if(xscroll < gcp+8-xsize) xscroll = gcp+8-xsize;
  if(xscroll > gcp) xscroll = gcp;
  unsigned int p = Win->GetPaperColor();
  unsigned int g = Win->GetBGColor();
  XSetForeground(Win->GetDisplay(), Win->GetGC(), p);
  XFillRectangle(Win->GetDisplay(), Img[0], Win->GetGC(), 2, 3,xsize-4,ysize-4);
  XSetForeground(Win->GetDisplay(), Win->GetGC(), g);
  XFillRectangle(Win->GetDisplay(), Img[1], Win->GetGC(), 2, 3,xsize-4,ysize-4);
  if(Wrk[disabled] != None)  {
    XCopyArea(Win->GetDisplay(), Wrk[disabled], Img[disabled], Win->GetGC(),
	xscroll, 0, Wlen<?(xsize-4), ysize-4, 2, 2);
    }
  Redraw();
  if(!hidden) Win->Redraw(xpos, ypos, xsize, ysize);
  }

void IBox::Enable()  {
  disabled = 0;
  Rebuild();
  }

void IBox::Disable()  {
  disabled = 1;
  Rebuild();
  }

int IBox::KeyDown(XKeyEvent ev)  {
  KeySym key;
  if(disabled) return 0;
  char buf[256];
  int len = XLookupString(&ev,buf,sizeof(buf),&key,NULL) ;
  buf[len] = 0;
  if(key == XK_Return || key == XK_KP_Enter) {
    if(nextfocus)  {
      Win->TakeFocus(nextfocus);
      }
    else Win->TakeFocus(NULL);
    }
  else if(key == XK_Delete)  {
    if(strlen(text) > 0)  {
      char *tmpc;
      for(tmpc=&text[tcp]; *tmpc != 0; tmpc++)  *tmpc = tmpc[1];
      SetText(text);
      Rebuild();
      }
    }
  else if(key == XK_BackSpace)  {
    if(tcp > 0)  {
      tcp--;
      char *tmpc;
      for(tmpc=&text[tcp]; tmpc[tcp2-(tcp+1)] != 0; tmpc++)
	*tmpc = tmpc[tcp2-tcp];
      tcp2=tcp;
      SetText(text);
      Rebuild();
      }
    else if(tcp2>tcp) {
      char *tmpc;
      for(tmpc=&text[tcp]; tmpc[tcp2-(tcp+1)] != 0; tmpc++)
	*tmpc = tmpc[tcp2-tcp];
      tcp2=tcp;
      SetText(text);
      Rebuild();
      }
    }
  else if(key == XK_Left)  {
    if(tcp>0)  {
      tcp--; tcp2=tcp;
      SetText(text);
      Rebuild();
      }
    }
  else if(key == XK_Right)  {
    if(tcp<(int)strlen(text))  {
      tcp++; tcp2=tcp;
      SetText(text);
      Rebuild();
      }
    }
  else if(key == XK_Escape || key == XK_Tab)  {
    return 0;
    }
  else if(buf[0] != 0 && buf[1] == 0)  {
    char *tmpt = new char[strlen(text)+3];
    sprintf(tmpt, "%s", text);
    sprintf(&tmpt[tcp], "%c%s%c", buf[0], &text[tcp2], 0);
    tcp++; tcp2=tcp;
    SetText(tmpt);
    Rebuild();
    delete tmpt;
    }
  else {
    return 0;
    //printf("Got Unhandled Key = \"%s\"\n", key);
    }
  return 1;
  }

int IBox::KeyUp(XKeyEvent ev)  {
//  char *key = XKeysymToString(XKeycodeToKeysym(Win->GetDisplay(), k, 0));
//  printf("Got \"%s\"\n", key);
  return 1;
  }

int IBox::Press(int b, int x, int y)  {
  if(Win->GetFocus() == this && (!disabled)) {
    tcp=x/6; tcp2=tcp;
    SetText(text);
    Rebuild();
    }
  return 1;
  }

int IBox::Release(int b, int x, int y)  {
  if((!disabled) && b != 0)   Win->TakeFocus(this);
  return 1;
  }

void IBox::GotFocus()  {
  SetText(text);
  Rebuild();
  }

void IBox::LostFocus()  {
  SetText(text);
  Rebuild();
  }

void IBox::SetChangeCallback(void (*cb)(IDoDad *, IDoDad *))  {
  changecallback = cb;
  }

void IBox::SetCurPos(int p) {
  SetCurPos(p, p);
  }

void IBox::SetCurPos(int p, int p2) {
  tcp=p; tcp2=p2;
  SetText(text);
  Rebuild();
  }

