
static char DynamicLabel_id[] = "$Header: /usr/tiburon/unix/gui/tmacs/RCS/DynamicLabel.cc,v 1.8 1997/10/03 09:30:00 oreilly Exp $";

/*
$Log: DynamicLabel.cc,v $
Revision 1.8  1997/10/03 09:30:00  oreilly
*** empty log message ***

Revision 1.7  97/08/12  14:42:41  14:42:41  oreilly (Thomas C. O'Reilly)
Added addXtEventHandler() method

Revision 1.6  97/05/13  07:59:46  07:59:46  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.5  97/05/08  09:31:31  09:31:31  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.3.1.1  97/05/08  09:23:41  09:23:41  oreilly (Thomas C. O'Reilly)
Grow pixmap/widget as needed. Never shrink (to minimize server requests)

Revision 1.3  97/04/30  19:34:06  19:34:06  oreilly (Thomas C. O'Reilly)
Improved performance

Revision 1.1  97/03/20  12:29:20  12:29:20  oreilly (Thomas C. O'Reilly)
Initial revision

*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <mbari/param.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Vk/VkApp.h>
#include "DynamicLabel.h"

#define dprintf if (debug) fprintf

GC DynamicLabel::_gc;

DynamicLabel::DynamicLabel(const char *name, Widget parent,
			   unsigned char alignment)
  : VkComponent(name)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();

  _windowIsReady = False;
  _pixmap = NULL;
  _text = NULL;
  _widgetWidth = _widgetHeight = 0;
  _fontInfo = NULL;
  _label = NULL;  

  getFont();

  Dimension height = _fontInfo->ascent + _fontInfo->descent;
  
  _label = 
    XtVaCreateManagedWidget("label", xmLabelWidgetClass, _baseWidget, 
			    XmNheight, height,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNrightAttachment, XmATTACH_FORM,
			    XmNlabelType, XmPIXMAP,
			    NULL);

  XtAddEventHandler(_label, 
		    ExposureMask,
		    False,
		    (XtEventHandler )&DynamicLabel::exposeCallback,
		    (XtPointer )this);

  XtAddEventHandler(_label, 
		    StructureNotifyMask,
		    False,
		    (XtEventHandler )&DynamicLabel::structureNotifyCallback,
		    (XtPointer )this);
  
  _alignment = alignment;

  _pixmapWidth = 1;
  _pixmapHeight = 1;

  Pixel foreground, background;
  
  XtVaGetValues(_label,
		XmNbackground, &background,
		XmNforeground, &foreground,
		NULL);

  int screenNum = DefaultScreen(theApplication->display());
  XGCValues gcValues;
  gcValues.foreground = foreground;
  gcValues.background = background;
  
  _gc = XCreateGC(theApplication->display(),
		  RootWindow(theApplication->display(), screenNum),
		  GCForeground | GCBackground, &gcValues);
  
  _nTextLines = 0;
  _lineSpacing = 3;

  setColors(foreground, background);

  /* Initialize text to zero-length string. Calling setText() ensures
     that pixmap and other objects are initialized. */
  setText("");
}


DynamicLabel::~DynamicLabel()
{
  if (_pixmap)
    XFreePixmap(theApplication->display(), _pixmap);
  
  if (_text)
    free(_text);
}


void DynamicLabel::setColors(Pixel fg, Pixel bg)
{
  _foreground = fg;
  _background = bg;
  
  Colormap colormap;
  XtVaGetValues(_baseWidget, XmNcolormap, &colormap, NULL);
  Pixel borderColor, topColor, bottomColor, armColor;
 
  XmGetColors(XtScreen(_baseWidget), colormap, bg, &borderColor,
	      &topColor, &bottomColor, &armColor);
  
  XtVaSetValues(_label, 
		XmNforeground, fg, 
		XmNbackground, bg, 
		NULL);

  setText(_text);
}


Boolean DynamicLabel::setText(const char *text)
{
  Boolean debug = False;

  if (!text)
    return False;
  
  if (!_text || strcmp(text, _text))
  {
    if (_text)
    {
      free(_text);
      _text = NULL;
    }

    if (text)
      _text = strdup(text);
  }

  draw();

  return True;
}


void DynamicLabel::set(const char *text, Pixel foreground, Pixel background)
{
  setColors(foreground, background);
  setText(text);
}


void DynamicLabel::draw()
{
  Boolean debug = False;
  dprintf(stderr, "DynamicLabel::draw()\n");
  
  if (!_fontInfo) 
    getFont();

  if (!_fontInfo)
  {
    fprintf(stderr, "DynamicLabel::draw() - couldn't get font!\n");
    return;
  }
  
  int oldWidth = _pixmapWidth;
  int oldHeight = _pixmapHeight;

  _nTextLines = 1;
  int textWidth = -1;

  if (_text)
  {
    char *ptr = _text;
    char *newlinePtr; 
    while (newlinePtr = strchr(ptr, '\n'))
    {
      textWidth = 
	MAX(textWidth, XTextWidth(_fontInfo, ptr, newlinePtr - ptr));

      _nTextLines++;
      ptr = newlinePtr + 1;
    }

    textWidth = MAX(textWidth, XTextWidth(_fontInfo, ptr, strlen(ptr)));
  
    
    textWidth +=  (_fontInfo->max_bounds.lbearing + 
		   _fontInfo->max_bounds.rbearing);
    
    _pixmapWidth = MAX(textWidth, _pixmapWidth);
  }
  else
    _pixmapWidth = 1;

  _pixmapHeight = _nTextLines * 
    (_fontInfo->ascent + _fontInfo->descent + _lineSpacing);

  if (_pixmap && (_pixmapWidth > oldWidth || _pixmapHeight > oldHeight))
  {
    dprintf(stderr, "draw() - XFreePixmap()\n");
    XFreePixmap(theApplication->display(), _pixmap);
    _pixmap = NULL;
  }

  if (!_pixmap)
  {
    int screen = DefaultScreen(theApplication->display());
    int depth = XDisplayPlanes(theApplication->display(), screen);

    dprintf(stderr, "draw() - XCreatePixmap(); w=%d, h=%d\n",
	    _pixmapWidth, _pixmapHeight);
    
    _pixmap = 
      XCreatePixmap(theApplication->display(), 
		    RootWindow(theApplication->display(), screen),
		    _pixmapWidth, _pixmapHeight,
		    depth);
  }

  resize(_widgetWidth, _widgetHeight);
  
  Pixel fg, bg;
  
  if (_fontInfo)
    XSetFont(XtDisplay(_label), _gc, _fontInfo->fid);

  XSetForeground(theApplication->display(), _gc, _background);
  XFillRectangle(theApplication->display(), _pixmap, _gc, 0, 0, 
		 _pixmapWidth, _pixmapHeight);

  XSetForeground(theApplication->display(), _gc, _foreground);
 
  if (_text)
  {
    char *text = strdup(_text);
    
    int textHeight = _fontInfo->ascent + _fontInfo->descent;
    char *ptr = text;
    char *token;

    int y = _pixmapHeight / 2 - 
      _nTextLines * (textHeight + _lineSpacing) / 2 +
	textHeight;

    // TEST TEST TEST
    // y = _pixmapHeight / 2;
    

    while (token = strtok(ptr, "\n"))
    {
      int x = _fontInfo->max_bounds.lbearing + 1;
      
      XDrawString(theApplication->display(), _pixmap, _gc, x, y,
		  token, strlen(token));

      y += (textHeight + _lineSpacing);
      
      ptr = NULL;
    }

    free(text);
  }
  
  if (_windowIsReady)
    updateWindow();
}



void DynamicLabel::updateWindow()
{
  if (!_text || !_pixmap)
  {
    return;
  }
  
  Boolean debug = False;
  
  int border = 0;
  int dstWidth = _widgetWidth - 2 * border;
  int srcWidth = _pixmapWidth;
  int srcX, srcY, dstX, dstY;

  srcX = 0;
  srcWidth = MIN(_pixmapWidth, dstWidth);
    
  switch (_alignment)
  {
    case XmALIGNMENT_BEGINNING:
    dstX = border;
    break;
      
    case XmALIGNMENT_CENTER:
    dstX = MAX(dstWidth / 2 - _pixmapWidth / 2, 0);  
    break;
      
    case XmALIGNMENT_END:
    dstX = MAX(dstWidth - _pixmapWidth, border);
    break;
  }
    
  int srcHeight = _pixmapHeight;
  int dstHeight = _widgetHeight - 2 * border;
  if (dstHeight > _pixmapHeight)
  {
    srcY = 0;
    dstY = (dstHeight - _pixmapHeight) / 2 + border;
  }
  else
  {
    srcY = (_pixmapHeight - dstHeight) / 2;
    srcHeight = dstHeight;
    dstY = border;
  }

  dprintf(stderr, "XCopyArea(): _name=%s, _pixmap=%d\n", _name, _pixmap);
  
  XCopyArea(XtDisplay(_label), _pixmap, XtWindow(_label),
	    _gc, srcX, srcY, srcWidth, srcHeight, 
	    dstX, dstY);

  dprintf(stderr, "done\n");
  
//  XmUpdateDisplay(_baseWidget);
}


void DynamicLabel::exposeCallback(Widget w, XtPointer clientData,
				  XEvent *event)
{
  XExposeEvent *exposeEvent = (XExposeEvent *)event;

  if (exposeEvent->count != 0)
    // More expose events coming
    return;
  
  Boolean debug = False;
  dprintf(stderr, "DynamicLabel::exposeCallback()\n"); 

  DynamicLabel *obj = (DynamicLabel *)clientData;

  
  if (!obj->_windowIsReady)
  {
    // Ok to draw following first exposure event  
    obj->_windowIsReady = True;
  }

  obj->updateWindow();
}


void DynamicLabel::structureNotifyCallback(Widget w, XtPointer clientData,
					   XEvent *event)
{
  Boolean debug = False;
  
  XConfigureEvent *configEvent = (XConfigureEvent *)event;

  dprintf(stderr, 
	  "** DynamicLabel::structureNotifyCallback(); "
	  "type=%d, width=%d, height=%d\n",
	  configEvent->type, configEvent->width, configEvent->height);

  if (configEvent->type != ConfigureNotify)
    return;

  dprintf(stderr, 
	  "** DynamicLabel::structureNotifyCallback(); width=%d, height=%d\n",
	  configEvent->width, configEvent->height);
  
  DynamicLabel *obj = (DynamicLabel *)clientData;

  obj->resize(configEvent->width, configEvent->height);
}


void DynamicLabel::resize(Dimension widgetWidth, Dimension widgetHeight)
{
  Boolean debug = False;
  
  if (widgetWidth < _pixmapWidth || widgetHeight < _pixmapHeight)
  {
    // Try to set widget to minumum required size
    dprintf(stderr, "DynamicLabel::resize(); w=%d, %h=%d\n",
	    _pixmapWidth, _pixmapHeight);
    
    XtVaSetValues(_baseWidget, 
		  XmNwidth, _pixmapWidth, 
		  XmNheight, _pixmapHeight, 
		  NULL);

    _widgetWidth = _pixmapWidth;
    _widgetHeight = _pixmapHeight;
  }
}


void DynamicLabel::afterRealizeHook()
{
  Boolean debug = False;
  Dimension width, height;
  
  XtVaGetValues(_baseWidget, XmNwidth, &width, XmNheight, &height, NULL);

  if (width != _widgetWidth || height != _widgetHeight)
  {
    _widgetWidth = width;
    _widgetHeight = height;
    draw();
  }
  
#ifdef 0
  dprintf(stderr, 
	  "DynamicLabel::afterRealizeHook(): width=%d, height=%d\n",
	  width, height);

  XtVaGetValues(XtParent(_baseWidget), 
		XmNwidth, &width, XmNheight, &height, NULL);

  dprintf(stderr, 
	  "parent width=%d, parent height=%d\n",
	  width, height);
#endif
}


void DynamicLabel::getFont()
{
//  if (!_fontInfo && XtIsRealized(_label))
  if (!_fontInfo)
  {
    XmFontList fontList;
    XtVaGetValues(_baseWidget, XmNlabelFontList, &fontList, NULL);
    
    XmFontContext fontContext;
    if (!XmFontListInitFontContext(&fontContext, fontList))
    {
      fprintf(stderr, 
	      "DynamicLabel::getFont() - XmFontListInitFontContext() "
	      "failed\n");
      exit(1);
    }
    char *charSet;
  
    if (!XmFontListGetNextFont(fontContext, &charSet, &_fontInfo))
    {
      fprintf(stderr, 
	      "DynamicLabel::getFont() - XmFontListGetNextFont() failed\n");
      exit(1);
    }

    free(charSet);
    XmFontListFreeFontContext(fontContext);
  }
}


void DynamicLabel::addXtEventHandler(EventMask eventMask,
				     Boolean nonMaskable,
				     XtEventHandler handler,
				     XtPointer clientData)
{
  XtAddEventHandler(_label, 
		    eventMask,
		    nonMaskable,
		    handler,
		    clientData);
}
