
static char DynamicButton_id[] = "$Header: /usr/tiburon/unix/gui/tmacs/RCS/DynamicButton.cc,v 1.5 1997/12/12 17:52:26 oreilly Exp $";

/*
$Log: DynamicButton.cc,v $
Revision 1.5  1997/12/12 17:52:26  oreilly
*** empty log message ***

Revision 1.4  1997/11/20 08:21:10  oreilly
*** empty log message ***

Revision 1.3  97/08/12  14:41:50  14:41:50  oreilly (Thomas C. O'Reilly)
added getFont() method

Revision 1.2  97/03/20  12:29:16  12:29:16  oreilly (Thomas C. O'Reilly)
..

Revision 1.1  96/10/28  09:12:01  09:12:01  oreilly (Thomas C. O'Reilly)
Initial revision

*/
#include <stdio.h>
#include <malloc.h>
#include <Xm/Form.h>
#include <Xm/DrawnB.h>
#include <Vk/VkApp.h>
#include "DynamicButton.h"

#define dprintf if (debug) fprintf

GC DynamicButton::_gc;
Pixmap DynamicButton::_stipple;

DynamicButton::DynamicButton(const char *name, Widget parent,
			     int width, int height)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();

  _pixmap = NULL;
  _text = NULL;
  _fontInfo = NULL;
  
  _drawnB = 
    XtVaCreateManagedWidget("drawnB", xmDrawnButtonWidgetClass, _baseWidget, 
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNrightAttachment, XmATTACH_FORM,
			    XmNpushButtonEnabled, True,
			    XmNshadowType, XmSHADOW_OUT,
			    XmNwidth, width,
			    XmNheight, height,
			    NULL);

  XtAddCallback(_drawnB, XmNactivateCallback, 
		&DynamicButton::activateCallback, 
		(XtPointer )this);

  XtAddCallback(_drawnB, XmNarmCallback, 
		&DynamicButton::armCallback, 
		(XtPointer )this);

  XtAddCallback(_drawnB, XmNdisarmCallback, 
		&DynamicButton::disarmCallback, 
		(XtPointer )this);

  XtAddCallback(_drawnB, XmNexposeCallback, 
		&DynamicButton::exposeResizeCallback,
		(XtPointer )this);

  XtAddCallback(_drawnB, XmNresizeCallback, 
		&DynamicButton::exposeResizeCallback,
		(XtPointer )this);
  
  Pixel foreground, background;
  XtVaGetValues(_drawnB, 
		XmNbackground, &background, 
		XmNforeground, &foreground,
		XmNshadowThickness, &_shadowThickness,
		NULL);

  _pixmapWidth = width - 2 * _shadowThickness;
  _pixmapHeight = height - 2 * _shadowThickness;

  static unsigned char stippleBits[] = 
  {
    0125, 0252,
    0125, 0252
  };
  
  int stippleWidth = 8;
  int stippleHeight = 2;
  int screenNum = DefaultScreen(theApplication->display());
 
  _stipple = 
    XCreateBitmapFromData(theApplication->display(), 
			  RootWindow(theApplication->display(), screenNum),
			  stippleBits, stippleWidth, stippleHeight);
					 
  XGCValues gcValues;
  gcValues.foreground = foreground;
  gcValues.background = background;
  gcValues.stipple = _stipple;
  
  _gc = XCreateGC(theApplication->display(),
		  RootWindow(theApplication->display(), screenNum),
		  GCForeground | GCBackground | GCStipple, &gcValues);

  getFont();
  
//   _fontInfo = XQueryFont(theApplication->display(), XGContextFromGC(_gc));

  _nTextLines = 0;
  _lineSpacing = 3;

  setColors(foreground, background);
  setText(name);
}


DynamicButton::~DynamicButton()
{
  XFreeFont(theApplication->display(), _fontInfo);

  if (_pixmap)
    XFreePixmap(theApplication->display(), _pixmap);
  
  if (_text)
    free(_text);
}


void DynamicButton::setColors(Pixel fg, Pixel bg)
{
  _background = bg;
  
  Colormap colormap;
  XtVaGetValues(_baseWidget, XmNcolormap, &colormap, NULL);
  Pixel borderColor, topColor, bottomColor;
 
  XmGetColors(XtScreen(_baseWidget), colormap, bg, &borderColor,
	      &topColor, &bottomColor, &_armColor);
  
  XtVaSetValues(_drawnB, 
		XmNforeground, fg, 
		XmNbackground, bg, 
		XmNtopShadowColor, topColor,
		XmNbottomShadowColor, bottomColor,
		XmNborderColor, _armColor,
		NULL);

  draw();
}


void DynamicButton::setPixmap(Pixmap pixmap)
{
  XtVaSetValues(_drawnB,
		XmNlabelType, XmPIXMAP,
		XmNlabelPixmap, pixmap,
		NULL);
}


void DynamicButton::setText(const char *text, Boolean recomputeSize)
{
  Boolean debug = False;

  if (_text)
  {
    free(_text);
    _text = NULL;
  }

  if (text)
    _text = strdup(text);

  _nTextLines = 1;
  char *ptr = _text;
  char *newlinePtr; 
  while (newlinePtr = strchr(ptr, '\n'))
  {
    _nTextLines++;
    ptr = newlinePtr + 1;
  }
 
  if (recomputeSize)
  {
    if (_text)
      _pixmapWidth = XTextWidth(_fontInfo, _text, strlen(_text));
    else
      _pixmapWidth = 1;

    _pixmapHeight = _nTextLines * 
      (_fontInfo->ascent + _fontInfo->descent + _lineSpacing);

    _pixmap = NULL;
  }
  
  draw();
}


void DynamicButton::setSensitive(Boolean sensitive)
{
  if (sensitive)
    XtVaSetValues(_drawnB,
		  XmNsensitive, True, 
		  XmNpushButtonEnabled, True,
		  NULL);
  else
    XtVaSetValues(_drawnB,
		  XmNsensitive, False, 
		  XmNpushButtonEnabled, False,
		  NULL);
}


void DynamicButton::draw()
{
  Boolean debug = False;
  
  int screen = DefaultScreen(theApplication->display());
  int depth = XDisplayPlanes(theApplication->display(), screen);

  if (!_pixmap)
  {
    _pixmap = 
      XCreatePixmap(theApplication->display(), 
		    RootWindow(theApplication->display(), screen),
		    _pixmapWidth, _pixmapHeight,
		    depth);
  }
  
  Pixel fg, bg;
  Boolean sensitive;
  
  XtVaGetValues(_drawnB, 
		XmNforeground, &fg, XmNbackground, &bg, 
		XmNsensitive, &sensitive, 
		NULL);
    
  XSetForeground(theApplication->display(), _gc, bg);
  XFillRectangle(theApplication->display(), _pixmap, _gc, 0, 0, 
		 _pixmapWidth, _pixmapHeight);

  XSetForeground(theApplication->display(), _gc, fg);
 
  if (_text)
  {
    if (_fontInfo)
      XSetFont(XtDisplay(_drawnB), _gc, _fontInfo->fid);
    
    if (!sensitive)
    {
      // Apply stippling to text
      XSetFillStyle(theApplication->display(), _gc, FillStippled);
    }
    
    char *text = strdup(_text);
    
    int textHeight = _fontInfo->ascent + _fontInfo->descent;
    char *ptr = text;
    char *token;
    int y = _pixmapHeight / 2 - 
      _nTextLines * (textHeight + _lineSpacing) / 2 +
	textHeight;

    while (token = strtok(ptr, "\n"))
    {
      int textWidth = XTextWidth(_fontInfo, token, strlen(token));
      int x = _pixmapWidth / 2 - textWidth / 2;
      XDrawString(theApplication->display(), _pixmap, _gc, x, y,
		  token, strlen(token));

      y += (textHeight + _lineSpacing);
      
      ptr = NULL;
    }
    if (!sensitive)
    {
      // Go back to FillSolid style
      XSetFillStyle(theApplication->display(), _gc, FillSolid);
    }    

    free(text);
  }

  if (XtIsRealized(_drawnB))
  {
    Dimension borderWidth, width, height;
    short highlightThickness, shadowThickness;
    
    XtVaGetValues(_drawnB, 
		  XmNwidth, &width,
		  XmNheight, &height,
		  XmNborderWidth, &borderWidth,
		  XmNhighlightThickness, &highlightThickness,
		  XmNshadowThickness, &shadowThickness,
		  NULL);
    
    int border = borderWidth + highlightThickness + shadowThickness;
    int drawWidth = width - 2 * border;
    int srcWidth = _pixmapWidth;
    int srcX, srcY, dstX, dstY;
    if (drawWidth > _pixmapWidth)
    {
      srcX = 0;
      dstX = (drawWidth - _pixmapWidth) / 2 + border;
    }
    else
    {
      srcX = (_pixmapWidth - drawWidth) / 2;
      srcWidth = drawWidth;
      dstX = border;
    }

    int srcHeight = _pixmapHeight;
    int drawHeight = height - 2 * border;
    if (drawHeight > _pixmapHeight)
    {
      srcY = 0;
      dstY = (drawHeight - _pixmapHeight) / 2 + border;
    }
    else
    {
      srcY = (_pixmapHeight - drawHeight) / 2;
      srcHeight = drawHeight;
      dstY = border;
    }
    
    XCopyArea(XtDisplay(_drawnB), _pixmap, XtWindow(_drawnB),
	      _gc, srcX, srcY, srcWidth, srcHeight, 
	      dstX, dstY);
  }
}


void DynamicButton::drawArmed()
{
  Boolean debug = False;
  dprintf(stderr, "DynamicButton::drawArmed()\n");
  
//  reverseBorders();
  XtVaSetValues(_drawnB, XmNbackground, _armColor, NULL);
  draw();
}


void DynamicButton::drawDisarmed()
{
  Boolean debug = False;
  dprintf(stderr, "DynamicButton::drawDisarmed()\n");

//  reverseBorders();
  XtVaSetValues(_drawnB, XmNbackground, _background, NULL);
  draw();
}


void DynamicButton::reverseBorders()
{
  Pixel topColor, bottomColor;

  XtVaGetValues(_drawnB,
		XmNtopShadowColor, &topColor,
		XmNbottomShadowColor, &bottomColor,
		NULL);

  XtVaSetValues(_drawnB,
		XmNtopShadowColor, bottomColor,
		XmNbottomShadowColor, topColor,
		NULL);
}


void DynamicButton::getFont()
{
  XmFontList fontList;
  XtVaGetValues(_baseWidget, XmNlabelFontList, &fontList, NULL);
    
  XmFontContext fontContext;
  if (!XmFontListInitFontContext(&fontContext, fontList))
  {
    fprintf(stderr, 
	    "DynamicButton::getFont() - XmFontListInitFontContext() "
	    "failed\n");
    exit(1);
  }
  char *charSet;
  
  if (!XmFontListGetNextFont(fontContext, &charSet, &_fontInfo))
  {
    fprintf(stderr, 
	    "DynamicButton::getFont() - XmFontListGetNextFont() failed\n");
    exit(1);
  }

  free(charSet);
  XmFontListFreeFontContext(fontContext);
}


Widget DynamicButton::buttonWidget()
{
  return _drawnB;
}


void DynamicButton::activateCallback(Widget w, XtPointer clientData,
				     XtPointer callData)
{
  DynamicButton *obj = (DynamicButton *)clientData;
}


void DynamicButton::armCallback(Widget w, XtPointer clientData,
				XtPointer callData)
{
  DynamicButton *obj = (DynamicButton *)clientData;
  obj->drawArmed();
}


void DynamicButton::disarmCallback(Widget w, XtPointer clientData,
				   XtPointer callData)
{
  DynamicButton *obj = (DynamicButton *)clientData;
  obj->drawDisarmed();
}

void DynamicButton::exposeResizeCallback(Widget w, XtPointer clientData,
					 XtPointer callData)
{
  Boolean debug = False;
  dprintf(stderr, "DynamicButton::exposeResizeCallback()\n"); 
  DynamicButton *obj = (DynamicButton *)clientData;
  obj->draw();
}
