\input wbuildmac.tex
\Class{XfwfAnimator}
 The animator widget cycles through a series of images (XImage
structures). Each image is displayed for a certain number of
milliseconds. After the last image, the animation can either start
over or stop with the last image displayed.

The animation must be started with {\tt XfwfStartAnimation} and it may be
stopped with {\tt XfwfStopAnimation}.

The widget uses the location resources (see XfwfBoard(3)) for
positioning only; the preferred size of the widget will be the size of
the largest image, plus room for the frame.



\Publicvars
\Table{XfwfAnimator}
XtNimages&XtCImages&ImageList&NULL \cr
XtNintervals&XtCIntervals&CardinalList&NULL \cr
XtNdefaultInterval&XtCDefaultInterval&Cardinal &500 \cr
XtNcycle&XtCCycle&Boolean &True \cr
\endTable
\Section
\Publicvar{XtNimages}
 The list of images can be specified in the resource file as a series
of filenames of files in XPM format. If it is set from an application,
the list must end with a {\tt NULL}. Every element is a pointer to an
{\tt XImage} structure.



\HCode
\Code
{\langle}ImageList{\rangle} XImage ** images = NULL \endCode
\endHCode


\Section
\Publicvar{XtNintervals}
 The time that each image will be displayed can be set with
{\tt intervals}. It is a list of cardinals, each representing a number of
milliseconds. The list must end with a zero. If there are insufficient
numbers in the list, the last number will be used as the interval for
any remaining images. If the list is empty (NULL), the interval will
be {\tt defaultInterval} (normally 500).



\HCode
\Code
{\langle}CardinalList{\rangle} Cardinal * intervals = NULL \endCode
\endHCode


\Section
\Publicvar{XtNdefaultInterval}
 If the list of intervals is empty, the interval will be
{\tt defaultInterval} milliseconds.



\HCode
\Code
Cardinal  defaultInterval = 500 \endCode
\endHCode


\Section
\Publicvar{XtNcycle}
 The animation repeats indefinitely by default. When the {\tt cycle}
resource is set to {\tt False} the animation sequence is shown once only,
for every call to {\tt XfwfStartAnimation}.



\HCode
\Code
Boolean  cycle = True \endCode
\endHCode


\Section
\Publicvar{XtNframeType}
 The default type of frame is set to `sunken'.



\HCode
\Code
 frameType = XfwfSunken \endCode
\endHCode


\Section
\Publicvar{XtNrel{\underline}width}
 If there is no image to display, the widget has no obvious size. The
following defaults for the location resources create a 7 by 7 pixel
widget.



\HCode
\Code
 rel{\underline}width = {\langle}String{\rangle}"0.0"\endCode
\endHCode


\Section
\Publicvar{XtNabs{\underline}width}


\HCode
\Code
 abs{\underline}width = 7 \endCode
\endHCode


\Section
\Publicvar{XtNrel{\underline}height}


\HCode
\Code
 rel{\underline}height = {\langle}String{\rangle}"0.0"\endCode
\endHCode


\Section
\Publicvar{XtNabs{\underline}height}


\HCode
\Code
 abs{\underline}height = 7 \endCode
\endHCode


\End\Table{XfwfBoard}
XtNcursor&XtCCursor&Cursor &None \cr
XtNabs{\underline}x&XtCAbs{\underline}x&Int &0 \cr
XtNrel{\underline}x&XtCRel{\underline}x&Float &"0.0"\cr
XtNabs{\underline}y&XtCAbs{\underline}y&Int &0 \cr
XtNrel{\underline}y&XtCRel{\underline}y&Float &"0.0"\cr
XtNabs{\underline}width&XtCAbs{\underline}width&Int &0 \cr
XtNrel{\underline}width&XtCRel{\underline}width&Float &"1.0"\cr
XtNabs{\underline}height&XtCAbs{\underline}height&Int &0 \cr
XtNrel{\underline}height&XtCRel{\underline}height&Float &"1.0"\cr
XtNhunit&XtCHunit&Int &1 \cr
XtNvunit&XtCVunit&Int &1 \cr
XtNlocation&XtCLocation&String &NULL \cr
\endTable
\Table{XfwfFrame}
XtNframeType&XtCFrameType&FrameType &XfwfRaised \cr
XtNframeWidth&XtCFrameWidth&Dimension &0 \cr
XtNframeOffset&XtCFrameOffset&Dimension &0 \cr
XtNshadowScheme&XtCShadowScheme&ShadowScheme &XfwfAuto \cr
XtNlightColor&XtCLightColor&Pixel &XtDefaultBackground \cr
XtNdarkColor&XtCDarkColor&Pixel &XtDefaultForeground \cr
XtNlightStipple&XtCLightStipple&Bitmap &NULL \cr
XtNdarkStipple&XtCDarkStipple&Bitmap &NULL \cr
\endTable
\Table{XfwfCommon}
XtNhighlighted&XtCHighlighted&Boolean &False \cr
XtNselected&XtCSelected&Boolean &False \cr
XtNtext&XtCText&String &NULL \cr
XtNhandlers&XtCHandlers&Program *&NULL \cr
\endTable
\Table{Composite}
XtNchildren&XtCChildren&WidgetList &NULL \cr
insertPosition&XtCInsertPosition&XTOrderProc &NULL \cr
numChildren&XtCNumChildren&Cardinal &0 \cr
\endTable
\Table{Core}
XtNx&XtCX&Position &0 \cr
XtNy&XtCY&Position &0 \cr
XtNwidth&XtCWidth&Dimension &0 \cr
XtNheight&XtCHeight&Dimension &0 \cr
borderWidth&XtCBorderWidth&Dimension &0 \cr
XtNcolormap&XtCColormap&Colormap &NULL \cr
XtNdepth&XtCDepth&Int &0 \cr
destroyCallback&XtCDestroyCallback&XTCallbackList &NULL \cr
XtNsensitive&XtCSensitive&Boolean &True \cr
XtNtranslations&XtCTranslations&XTTranslations &NULL \cr
ancestorSensitive&XtCAncestorSensitive&Boolean &False \cr
accelerators&XtCAccelerators&XTTranslations &NULL \cr
borderColor&XtCBorderColor&Pixel &0 \cr
borderPixmap&XtCBorderPixmap&Pixmap &NULL \cr
background&XtCBackground&Pixel &0 \cr
backgroundPixmap&XtCBackgroundPixmap&Pixmap &NULL \cr
mappedWhenManaged&XtCMappedWhenManaged&Boolean &True \cr
XtNscreen&XtCScreen&Screen *&NULL \cr
\endTable
\Exports
\Section
 To start the animation an application should call
{\tt XfwfStartAnimation}. If the animation is already running, it will be
restarted from the beginning.



\Code
XfwfStartAnimation( {\dollar})\endCode
\HCode
{\lbrace}
    if (! XtIsSubclass({\dollar}, xfwfAnimatorWidgetClass))
	XtWarning("Illegal widget type in XfwfStartAnimation");
    else
	{\dollar}start{\underline}animation({\dollar});
{\rbrace}\endHCode


\Section
 The animation can be stopped with {\tt XfwfStopAnimation}.



\Code
XfwfStopAnimation( {\dollar})\endCode
\HCode
{\lbrace}
    if (! XtIsSubclass({\dollar}, xfwfAnimatorWidgetClass))
	XtWarning("Illegal widget type in XfwfStopAnimation");
    else
	{\dollar}stop{\underline}animation({\dollar});
{\rbrace}\endHCode


\End\Imports
\Section


\Code
{\incl} {\langle}X11/xpm.h{\rangle}\endCode


\End\Privatevars
\Section
 The images will be changed by a time-out routine. The timer for that
routine is stored in {\tt timer}.



\Code
XtIntervalId  timer\endCode


\Section
 The current image.



\Code
int  cur{\underline}image\endCode


\Section
 The current interval, if {\tt intervals} is not {\tt NULL}.



\Code
int  cur{\underline}interval\endCode


\Section
 The GC for drawing the image.



\Code
GC  image{\underline}gc\endCode


\End\Methods
\Section
 {\tt class{\underline}initialize} installs the type converters for a list of images
and a list of cardinal numbers.



\Code
class{\underline}initialize()
{\lbrace}
    XtSetTypeConverter(XtRString, XtRImageList, cvtStringToImageList,
		       NULL, 0, XtCacheNone, NULL);
    XtSetTypeConverter(XtRString, XtRCardinalList, cvtStringToCardinalList,
		       NULL, 0, XtCacheNone, NULL);
{\rbrace}\endCode


\Section
 The {\tt initialize} sets the (desired and actual) size of the widget to
the size of the largest image. The GC is initialized to all default
values.



\Code
initialize(Widget  request, {\dollar}, ArgList  args, Cardinal * num{\underline}args)
{\lbrace}
    Dimension wd, ht, dummy1, dummy2;
    Position x, y;
    XImage **p;
    XtGCMask value{\underline}mask = 0;
    XGCValues values;

    {\dollar}timer = NULL;
    {\dollar}image{\underline}gc = XtGetGC({\dollar}, value{\underline}mask, {\ampersand}values);
    if ({\dollar}images != NULL) {\lbrace}
	{\dollar}compute{\underline}inside({\dollar}, {\ampersand}x, {\ampersand}y, {\ampersand}dummy1, {\ampersand}dummy2);
	wd = 1; ht = 1;
	for (p = {\dollar}images; *p; p++) {\lbrace}
	    if ((*p)-{\rangle}width {\rangle} wd) wd = (*p)-{\rangle}width;
	    if ((*p)-{\rangle}height {\rangle} ht) ht = (*p)-{\rangle}height;
	{\rbrace}
	XtVaSetValues({\dollar}, XtNwidth, wd + 2*x, XtNheight, ht + 2*y, NULL);
    {\rbrace}
{\rbrace}\endCode


\Section
 The {\tt start{\underline}animation} method is usually called by the
{\tt XfwfStartAnimation} routine.  If an animation is already running, the
time-out function must be removed first. To draw the first image, the
routine calls the {\tt expose} method with the {\tt event} and {\tt region}
parameters set to {\tt NULL}.



\Code
start{\underline}animation({\dollar})
{\lbrace}
    Cardinal delay;

    if (! {\dollar}images) return;
    if ({\dollar}timer) XtRemoveTimeOut({\dollar}timer);
    {\dollar}cur{\underline}image = 0;
    {\dollar}cur{\underline}interval = 0;
    if (! {\dollar}intervals {\bar}{\bar} {\dollar}intervals[0] == 0)
	delay = {\dollar}defaultInterval;
    else
	delay = {\dollar}intervals[{\dollar}cur{\underline}interval];
    {\dollar}expose({\dollar}, NULL, NULL);
    {\dollar}timer = XtAppAddTimeOut(XtWidgetToApplicationContext({\dollar}), delay,
			     next{\underline}image, {\dollar});
{\rbrace}\endCode


\Section
 The {\tt stop{\underline}animation} method simply removes the time-out function. It
leaves the current image undisturbed.



\Code
stop{\underline}animation({\dollar})
{\lbrace}
    if ({\dollar}timer) {\lbrace}
	XtRemoveTimeOut({\dollar}timer);
	{\dollar}timer = NULL;
    {\rbrace}
{\rbrace}\endCode


\Section
 The {\tt expose} method simply draws the current image (if any) against
the top left corner of the widget. It then calls the superclass's
{\tt expose} method to draw the frame.



\Code
expose({\dollar}, XEvent * event, Region  region)
{\lbrace}
    Dimension wd, ht;
    Position x, y;

    if (! XtIsRealized({\dollar})) return;
    if (region != NULL)
	XSetRegion(XtDisplay({\dollar}), {\dollar}image{\underline}gc, region);
    {\dollar}compute{\underline}inside({\dollar}, {\ampersand}x, {\ampersand}y, {\ampersand}wd, {\ampersand}ht);
    if ({\dollar}images) XPutImage(XtDisplay({\dollar}), XtWindow({\dollar}), {\dollar}image{\underline}gc,
			   {\dollar}images[{\dollar}cur{\underline}image], 0, 0, x, y,
			   {\dollar}images[{\dollar}cur{\underline}image]-{\rangle}width,
			   {\dollar}images[{\dollar}cur{\underline}image]-{\rangle}height);
    if (region != NULL)
	XSetClipMask(XtDisplay({\dollar}), {\dollar}image{\underline}gc, None);
    {\hashmark}expose({\dollar}, event, region);
{\rbrace}\endCode


\Section


\Code
Boolean  set{\underline}values(Widget  old, Widget  request, {\dollar}, ArgList  args, Cardinal * num{\underline}args)
{\lbrace}
    Dimension wd, ht, dummy1, dummy2;
    Position x, y;
    XImage **p;
    Boolean restart = False;

    if ({\dollar}old{\dollar}images != {\dollar}images) {\lbrace}
	if ({\dollar}timer) restart = True;
	{\dollar}stop{\underline}animation({\dollar});
	{\dollar}compute{\underline}inside({\dollar}, {\ampersand}x, {\ampersand}y, {\ampersand}dummy1, {\ampersand}dummy2);
	wd = 1; ht = 1;
	for (p = {\dollar}images; *p; p++) {\lbrace}
	    if ((*p)-{\rangle}width {\rangle} wd) wd = (*p)-{\rangle}width;
	    if ((*p)-{\rangle}height {\rangle} ht) ht = (*p)-{\rangle}height;
	{\rbrace}
	XtVaSetValues({\dollar}, XtNwidth, wd + 2*x, XtNheight, ht + 2*y, NULL);
    {\rbrace}
    if ({\dollar}old{\dollar}intervals != {\dollar}intervals) {\lbrace}
	if ({\dollar}timer) restart = True;
	{\dollar}stop{\underline}animation({\dollar});
    {\rbrace}
    if (restart) {\dollar}start{\underline}animation({\dollar});
    return False;
{\rbrace}\endCode


\End\Utilities
\Section
 The timer callback routine checks if there is another image to
display, either the next image or the first image, if {\tt cycle} is
{\tt True}. It determines the correct interval for the new image, which
can be the next number in {\tt intervals}, or the previous number, if
there is no next, or the {\tt defaultInterval}, if there is no {\tt interval}
at all. It then re-installs itself as a time-out routine.



\Code
next{\underline}image(XtPointer  client{\underline}data, XtIntervalId * timer)
{\lbrace}
    Widget {\dollar} = (Widget) client{\underline}data;
    Cardinal delay;

    if ({\dollar}images[{\dollar}cur{\underline}image+1] == NULL) {\lbrace}
	if (! {\dollar}cycle) return;			/* Nothing more */
	{\dollar}cur{\underline}image = 0;
	{\dollar}cur{\underline}interval = 0;
	if ({\dollar}intervals == NULL {\bar}{\bar} {\dollar}intervals[0] == 0)
	    delay = {\dollar}defaultInterval;
	else
	    delay = {\dollar}intervals[0];
    {\rbrace} else {\lbrace}
	{\dollar}cur{\underline}image++;
	if ({\dollar}intervals == NULL)
	    delay = {\dollar}defaultInterval;
	else if ({\dollar}intervals[{\dollar}cur{\underline}interval+1] == 0)
	    delay = {\dollar}intervals[{\dollar}cur{\underline}interval];
	else {\lbrace}
	    {\dollar}cur{\underline}interval++;
	    delay = {\dollar}intervals[{\dollar}cur{\underline}interval];
	{\rbrace}
    {\rbrace}
    {\dollar}expose({\dollar}, NULL, NULL);
    {\dollar}timer = XtAppAddTimeOut(XtWidgetToApplicationContext({\dollar}), delay,
			     next{\underline}image, {\dollar});
{\rbrace}\endCode


\Section
 The converter from a string to a list of images splits the string in
words separated by spaces and/or commas. Each word must be the name of
a file containing a pixmap in XPM format. The pixmap is read into an
{\tt XImage}.



\Macro
done(type, value) =
do {\lbrace}
      if (to-{\rangle}addr != NULL) {\lbrace}
	  if (to-{\rangle}size {\langle} sizeof(type)) {\lbrace}
	      to-{\rangle}size = sizeof(type);
	      return False;
	  {\rbrace}
	  *(type*)(to-{\rangle}addr) = (value);
      {\rbrace} else {\lbrace}
	  static type static{\underline}val;
	  static{\underline}val = (value);
	  to-{\rangle}addr = (XtPointer){\ampersand}static{\underline}val;
      {\rbrace}
      to-{\rangle}size = sizeof(type);
      return True;
  {\rbrace}while(0 )\endMacro


\Section


\Code
Boolean  cvtStringToImageList(Display * display, XrmValuePtr  args, Cardinal * num{\underline}args, XrmValuePtr  from, XrmValuePtr  to, XtPointer * converter{\underline}data)
{\lbrace}
    String p;
    XImage *im, *shape, **list = NULL;
    int status, n = 0;
    Cardinal one = 1;

    if (*num{\underline}args != 0)
	XtAppErrorMsg
	    (XtDisplayToApplicationContext(display),
	     "cvtStringToImageList", "wrongParameters",
	     "XtToolkitError",
	     "String to image list type conversion needs no arguments", 
	     (String*) NULL, (Cardinal*) NULL);

    p = strtok((String) from-{\rangle}addr, " {\backslash}t{\backslash}n,");
    while (p) {\lbrace}
	status = XpmReadFileToImage(display, p, {\ampersand}im, {\ampersand}shape, NULL);
	switch (status) {\lbrace}
	case XpmOpenFailed:
	case XpmFileInvalid:
	case XpmNoMemory:
	    XtAppWarningMsg
		(XtDisplayToApplicationContext(display),
		 "cvtStringToImageList", "fileError",
		 "XtToolkitError",
		 "Failed to read image from {\backslash}"{\percent}s{\backslash}"",
		 {\ampersand}p, {\ampersand}one);
	    break;	    
	case XpmColorError:
	case XpmColorFailed:
	    XtAppWarningMsg
		(XtDisplayToApplicationContext(display),
		 "cvtStringToImageList", "allocColor",
		 "XtToolkitError",
		 "Could not get (all) colors for image {\backslash}"{\percent}s{\backslash}"",
		 {\ampersand}p, {\ampersand}one);
	case XpmSuccess:
	    n++;
	    list = (XImage**) XtRealloc((char*) list, n * sizeof(im));
	    list[n-1] = im;
	{\rbrace}
	p = strtok(NULL, " {\backslash}t{\backslash}n,");
    {\rbrace}
    if (n {\rangle} 0) {\lbrace}
	n++;
	list = (XImage**) XtRealloc((char*) list, n * sizeof(im));
	list[n-1] = NULL;
    {\rbrace}
    done(XImage**, list);
{\rbrace}\endCode


\Section


\Code
Boolean  cvtStringToCardinalList(Display * display, XrmValuePtr  args, Cardinal * num{\underline}args, XrmValuePtr  from, XrmValuePtr  to, XtPointer * converter{\underline}data)
{\lbrace}
    String p;
    Cardinal *list = NULL;
    int n = 0;

    if (*num{\underline}args != 0)
	XtAppErrorMsg
	    (XtDisplayToApplicationContext(display),
	     "cvtStringToCardinalList", "wrongParameters",
	     "XtToolkitError",
	     "String to cardinal list type conversion needs no arguments", 
	     (String*) NULL, (Cardinal*) NULL);

    p = (char *) from-{\rangle}addr;
    do {\lbrace}
	n++;
	list = (Cardinal*) XtRealloc((char*) list, n * sizeof(Cardinal));
	list[n-1] = strtoul(p, {\ampersand}p, 0);
    {\rbrace} while (list[n-1] != 0);
    done(Cardinal *, list);
{\rbrace}\endCode


\End\bye
