/*
        mindlib/libXmind.c

        Copyright (C) 1994-97 flux GbR (flux@berlin.snafu.de)

        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 2, or (at your option)
        any later version.

        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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/***************************************************************************
 ***************************************************************************
 *
 *  Filename:		libXmind.c
 *  
 *  Description:	Functions to create some special widgets, such as
 *			Bitmap-, Anim- and PixmapButtons. 
 *
 *
 ***************************************************************************
 *
 *  Function Export:
 *
 *
 *
 ***************************************************************************
 ***************************************************************************/

/* DEFINES */

#define XPM_YES
#define MAXINT INT_MAX

/* INCLUDES */


#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <float.h>
#include <X11/IntrinsicP.h>
#include <Xm/Xm.h>
#include <Xm/DrawnB.h>
#include <Xm/RowColumn.h>
#ifndef NOEASY
#include "../include/XMind.h"
#else
#include "../include/XMind.h"
#endif

#ifdef XPM_YES
#include <X11/xpm.h>
#endif

#ifdef LINUX
#ifndef strdup
char *strdup P((const char *s));
#endif
#endif

/*
#ifdef LINUX
#define GC_NULL 0
#else
#define GC_NULL NULL
#endif
*/
const int GC_NULL = 0;

#ifdef COMA
char *getpath();
#endif


/* PROTOTYPES */

static void AnimButtonCb(AnimData *data, XtIntervalId *id);

/* callback prototypes */
void DrawnButtonCb  (Widget, XtPointer, XmDrawnButtonCallbackStruct *cbs);
static void PixmapButtonCb (Widget, XtPointer, XtPointer);


/* STATIC GLOBALS */

static GC gc=NULL;					/* Default graphical context	*/

char *TstStr(char *s) {

  if(!s) return NULL;
  if(!*s) return NULL;
  return s;
}

/***************************************************************************
 *
 *  Funktionsname:	PixmapExposeCb
 *  
 *  Input:		Widget parent, char *name, 
 *			XmDrawnButtonCallbackStruct *cbs
 *
 *  Return:		void
 *
 *  Documentation:	When a DrawnButton is exposed, this function
 *			is called. It reads an image (in XWD-format), 
 *			convert it to a pixmap. 
 *
 *
 *  History:		10-07-93	Created by flux
 *			01-20-95	New ReadXWD-function added
 *
 *
 ***************************************************************************/

void PixmapExposeCb(Widget parent, char *name, XmDrawnButtonCallbackStruct *cbs)
{
  Pixmap pix;
  XImage *img;
  char buf[256];
  GC gc = XCreateGC(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)),(unsigned long)0,0);

  if (!TstStr(name)) return;

  sprintf(buf, "%s",name);
  if (access(buf, R_OK) != 0){
        sprintf(buf,"%s.0",name);
        if (access(buf, R_OK) != 0){
	  if (strcmp("(null)", buf)){ /* buf is not (null) */
	    printf("Unable to find pixmap: %s\n",name);
	  }
	  return;
	} else
	  name = strdup(buf);
  } 
 if(!(img = ReadXWD(name, parent))) return;
 pix = XCreatePixmap(XtDisplay(parent),XtWindow(parent), img->width, img->height, DefaultDepthOfScreen(XtScreen(parent)));
 if (pix){
        XPutImage(XtDisplay(parent), pix, gc, img, 0,0,0,0, img->width, img->height);
        /* This is the wrong place for this function prototype
           PixmapButtonCb(parent, pix, cbs); */
        XtAddCallback(parent, XmNexposeCallback, /* (XtCallbackProc) */ PixmapButtonCb,(XtPointer) &pix);
 	XtRemoveCallback(parent, XmNexposeCallback, (XtCallbackProc) PixmapExposeCb, name);
  }
}


/***************************************************************************
 *
 *  Functionname:	MCreateAnimButton
 *  
 *  Input:		Widget parent, 
 *			String name,
 *		        String pixmapName;
 *			Cardinal pixampNum,
 *			Cardinal loop,
 *			Cardinal time,
 * 			Arglist args,
 *			Cardinal argcount;
 * 			
 *
 *  Return:		Widget AnimButtonId;
 *
 *  Documentation:	MCreateAnimButton creates an instance of 
 *			XmDrawnButton and returns the associated
 *			widget ID. 
 *			It also adds the XtAddTimeOut procedures.
 *
 *
 *  History:		06-10-93	Created by flux
 *
 *
 ***************************************************************************/
 
Widget MCreateAnimButton(Widget parent, String name,String pixmapName, Cardinal pixmapNum,
		  Cardinal loop, unsigned long TheTime, Arg *args, Cardinal argcount)
{
  Pixel fg, bg;		  		/* foreground and background colors 	*/
  Pixmap *animPixmap=NULL;		/* Pointer to pixmap data		*/
  AnimData *data = NULL;		/* Struct AnimData (see typedefs)	*/
  Bool pixmapFlag = TRUE;		/* Flag for file-loading		*/
  Cardinal i;
  int stat;
  Widget DrawnButton;
  char buf[256];

  if (!pixmapNum) return (DrawnButton = XmCreateDrawnButton(parent, name, args, argcount));

  if (loop == 0) loop = (int)MAXINT/pixmapNum;

  animPixmap = (Pixmap *) XtMalloc(sizeof(Pixmap) * pixmapNum);
  data       = (AnimData *) XtMalloc(sizeof(AnimData));

  /* CHECK FILE PATH FOR PIXMAPS */

  sprintf(buf, "%s.0",pixmapName);
  if (access(buf, R_OK) != 0){
        if (access(buf, R_OK) != 0){
	  /* MAYBE XPM-FORMAT */
	  sprintf(buf, "%s_0",pixmapName);
          if (access(buf, R_OK) != 0){
 	       	printf("Unable to find animation: %s\n",pixmapName);
         		pixmapFlag = FALSE;
	  }
	} else pixmapName = strdup(buf); 
  }

    /* CHECK XPM-FORMAT PIXMAP */

    stat = XpmReadFileToPixmap(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)), 
					buf, &animPixmap[0], NULL, NULL);
    if (stat == XpmSuccess){

	/* TRY TO LOAD XPM-FORMAT PIXMAPS */

        for (i=0; i < pixmapNum; i++){ 
	    sprintf(buf, "%s_%d",pixmapName,i);
    	    stat = XpmReadFileToPixmap(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)), 
					buf, &animPixmap[i], NULL, NULL);
	    if (stat != XpmSuccess){
	    	printf("ERROR: Can't load file %s\n",buf);
		pixmapFlag = FALSE;
	    }
        }

    } else {

	/* TRY TO LOAD BMP-FORMAT PIXMAPS */
    	XtVaGetValues(parent, XmNforeground, &fg, XmNbackground, &bg, NULL);

        for (i=0; i < pixmapNum; i++){ 
	    sprintf(buf, "%s.%d",pixmapName,i);
  	    animPixmap[i] = XmGetPixmap(XtScreen(parent), buf, fg, bg);
	    if (!animPixmap[i]){
	    	printf("ERROR: Can't load file %s\n",buf);
		pixmapFlag = FALSE;
	    }
        }
    
  }
  DrawnButton = XmCreateDrawnButton(parent, name, args, argcount);

  if (pixmapFlag){
    data->counter = loop * pixmapNum;
    data->actualFrame = 0;
    data->maxFrame = pixmapNum;
    data->pixmap = animPixmap;
    data->w      = DrawnButton;
    data->time   = TheTime;

    XtAddCallback(DrawnButton, XmNexposeCallback, (XtCallbackProc) AnimButtonExposeCb, data);
  }

  return DrawnButton;
}


/***************************************************************************
 *
 *  Functionname:	MCreatePixmapButton
 *  
 *  Input:		Widget parent, 
 *			String name,
 *		        String pixmapName;
 * 			Arglist args,
 *			Cardinal argcount;
 * 			
 *
 *  Return:		Widget DrawnButtonId;
 *
 *  Documentation:	MCreatePixmapButton creates an instance of 
 *			XmDrawnButton and returns the associated
 *			widget ID. Also, it adds two Callbacks, for expose
 *			and resize, named PixmapButtonCB. Bitmaps are 
 *			Pixmaps with only one bit-mask. Color Pixmaps
 *			can be generated with tools like xpaint.
 *			  
 *
 *  History:		06-29-93	Created by flux
 *
 *
 ***************************************************************************/
 
Widget MCreatePixmapButton(Widget parent, String name,String pixmapName, Arg *args, Cardinal argcount)
{
  Widget PixmapButton;
  char *buf;

  XtSetArg(args[argcount], XmNrecomputeSize, FALSE); argcount++;
  PixmapButton = XmCreateDrawnButton(parent, name, args, argcount);

  if (TstStr(pixmapName)){
    buf = strdup(pixmapName);
    XtAddCallback(PixmapButton, XmNexposeCallback, (XtCallbackProc) PixmapExposeCb,(XtPointer) buf);
  }

  return PixmapButton;
}

/***************************************************************************
 *
 *  Functionname:	MCreateDrawnButton
 *  
 *  Input:		Widget parent, 
 *			String name,
 *		        String pixmapName;
 * 			Arglist args,
 *			Cardinal argcount;
 * 			
 *
 *  Return:		Widget DrawnButtonId;
 *
 *  Documentation:	MCreateDrawnButton creates an instance of 
 *			XmDrawnButton and returns the associated
 *			widget ID. Also, it adds two Callbacks, for expose
 *			and resize, named DrawnButtonCB.  
 *
 *
 *  History:		06-10-93	Created by flux
 *
 *
 ***************************************************************************/
 
Widget MCreateDrawnButton(Widget parent, String name,String pixmapName, Arg *args, Cardinal argcount)
{
  Widget DrawnButton;
  Pixel fg, bg;		/* foreground and background colors 	*/
  Pixmap pixmap;	/* Pixmap ID				*/ 
  char buf[256];

  sprintf(buf, "%s",pixmapName);
  if (access(buf, R_OK) != 0){
	  if(strcmp("(null)", buf)){ /* buf is not (null) */
#ifdef COMA
	    sprintf(buf, "%s/data/bitmaps/%s",getpath(), pixmapName);
	    printf("Looking for Pixmap %s\n",buf);
	    pixmapName = strdup(buf);
#else
	    printf("Unable to find bitmap: %s\n",pixmapName);
#endif
	} else
	  pixmapName = strdup(buf);
  }
  XtVaGetValues(parent, 
	XmNforeground, &fg,
	XmNbackground, &bg, NULL);

  pixmap = XmGetPixmap(XtScreen(parent), pixmapName, fg, bg);
  DrawnButton = XmCreateDrawnButton(parent, name, args, argcount);

  if ( pixmap == XmUNSPECIFIED_PIXMAP){
	/*XtWarning("Unable to load pixmap!");*/
  }
  else{

	XtAddCallback(DrawnButton, XmNexposeCallback, (XtCallbackProc) DrawnButtonCb, (XtPointer) pixmap);
	XtAddCallback(DrawnButton, XmNresizeCallback, (XtCallbackProc) DrawnButtonCb, (XtPointer) pixmap);
  }

  return DrawnButton;
}


/* CALLBACK FUNCTIONS */


/***************************************************************************
 *
 *  Funktionsname:	
 *  
 *  Input:		
 *
 *  Return:		
 *
 *  Documentation:	
 *
 *
 *  History:		
 *
 *
 ***************************************************************************/
 
void DrawnButtonCb(Widget parent, XtPointer call_data, XmDrawnButtonCallbackStruct *cbs)
{
  Pixmap pixmap = (Pixmap) call_data;
  Dimension width, height, shad;
  Window win;

  /* Does the default gc exist ? */

  if (!gc)
	gc = XCreateGC(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)), GC_NULL, 0);

  XtVaGetValues(parent,
                XmNwidth,       &width,
                XmNheight,      &height,
		XmNshadowThickness, &shad,
                NULL);
  
  if (!(win = cbs->window)) win = XtWindow(parent);
  if (!win) return;
  XCopyArea(XtDisplay(parent), pixmap, win, gc, 0, 0, width-(shad*4), height-(shad*4), shad*2, shad*2);
}


/***************************************************************************
 *
 *  Funktionsname:	PixmapButtonCb
 *  
 *  Input:		Widget parent, char *name, 
 *			XmDrawnButtonCallbackStruct *cbs
 *
 *  Return:		void
 *
 *  Documentation:	When a DrawnButton is exposed, this function
 *			is called. It copys the pixmap
 *			to the DrawnButton.
 *
 *
 *  History:		06-30-93	Created by flux
 * 			10-07-93	New optimized functions added.
 *
 *
 ***************************************************************************/
static void PixmapButtonCb(Widget parent, XtPointer client_data, XtPointer call_data)
{
  Pixmap pix = (Pixmap) client_data;
  XmDrawnButtonCallbackStruct * cbs = call_data;
  Dimension width, height, shadow;

  if (!XtIsRealized(parent)){
	XtWarning("PixmapButtonCb: Parent not realized");
	return;
  }
  if (!XtIsManaged(parent)){
	XtWarning("PixmapButtonCb: Parent is not managed");
	return;
  }

  /* Does the default gc exist ? */
  if (!gc)
	gc = XCreateGC(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)), GC_NULL, 0);

  XtVaGetValues(parent,
                XmNwidth,           &width,
                XmNheight,          &height,
                XmNshadowThickness, &shadow,
                NULL);

  if (pix)
      XCopyArea(XtDisplay(parent), pix, cbs->window, gc, 0, 0, width-(shadow*4), height-(shadow*4), shadow*2, shadow*2);
}

/***************************************************************************
 *
 *  Funktionsname:	
 *  
 *  Input:		
 *
 *  Return:		
 *
 *  Documentation:	
 *
 *
 *  History:		
 *
 *
 ***************************************************************************/
 

void AnimButtonExposeCb(Widget parent, AnimData *data, XmAnyCallbackStruct *cbs)
{

  XtRemoveCallback(data->w, XmNexposeCallback, (XtCallbackProc) AnimButtonExposeCb, data);
  data->id = (XtIntervalId)XtAppAddTimeOut(XtWidgetToApplicationContext(parent), data->time, (XtTimerCallbackProc)AnimButtonCb, data);

}

/***************************************************************************
 *
 *  Funktionsname:	
 *  
 *  Input:		
 *
 *  Return:		
 *
 *  Documentation:	
 *
 *
 *  History:		
 *
 *
 ***************************************************************************/
 

static void AnimButtonCb(AnimData *data, XtIntervalId *id)
{
  Dimension width, height, x, y, shad;
  Pixel fg,bg;

  /* Does the default gc exist ? */

  if (!data) return;

  if (!gc)
	gc = XCreateGC(XtDisplay(data->w), RootWindowOfScreen(XtScreen(data->w)), (int) GC_NULL, 0);

  if (!XtIsRealized(data->w)){
	XtRemoveTimeOut(data->id);
	XtFree((char *)data->pixmap);
        data = NULL;
	return;
  }
  if(!XtIsManaged(data->w)){
        XtRemoveTimeOut(data->id);
	XtFree((char *)data->pixmap);
        data = NULL;
	return;
  }

  XtVaGetValues(data->w,
                XmNwidth,       &width,
                XmNheight,      &height,
		XmNforeground, 	&fg,
		XmNbackground,	&bg,
		XmNshadowThickness, &shad,
                XmNx,           &x,
                XmNy,           &y, NULL);

  
  XCopyArea(XtDisplay(data->w), data->pixmap[data->actualFrame], 
	XtWindow(data->w), gc, 0, 0, width-(shad*4), height-(shad*4), shad*2, shad*2);
  
  if (data->actualFrame == (data->maxFrame-1))
  	data->actualFrame = 0;
  else
	data->actualFrame++;
  data->counter--;

  if (data->counter){
  	data->id = XtAppAddTimeOut(XtWidgetToApplicationContext(data->w),data->time, (XtTimerCallbackProc)AnimButtonCb, data);
  } else{
	XtRemoveTimeOut(data->id);
	XtFree((char *)data->pixmap);
  }
}


/* CALLBACK FUNCTIONS */


/***************************************************************************
 *
 *  Funktionsname:	
 *  
 *  Input:		
 *
 *  Return:		
 *
 *  Documentation:	
 *
 *
 *  History:		
 *
 *
 ***************************************************************************/
 
void MPostPopupMenu(Widget w, Widget popup, XButtonEvent *event)
{
  if (event->button != Button3)
        return;

  XmMenuPosition(popup, event);
  XtManageChild(popup);
}
 

/***************************************************************************
 *
 *  Funktionsname:	
 *  
 *  Input:		
 *
 *  Return:		
 *
 *  Documentation:	
 *
 *
 *  History:		
 *
 *
 ***************************************************************************/
 
void MNextCb(Widget wid, Widget data, XmAnyCallbackStruct *cbs)
{
  if (data)
        XmProcessTraversal(data, XmTRAVERSE_CURRENT);
}


/***************************************************************************
 *
 *  Funktionsname:	MGetHelp
 *  
 *  Input:		Widget w
 *
 *  Return:		char *
 *
 *  Documentation:	Retrieves the quickhelp of a specified widget from 
 *			resources.
 *
 *			....widgetClassName.help.quickhelp: It's a quickhelp
 *
 *
 *  History:		17.11.1995	created by flux
 *
 *
 ***************************************************************************/
 
char *MGetQuickHelp(Widget w)
{
  static char *returnValue = NULL;
  XtResource requestResources;

  requestResources.resource_name        = "quickhelp";
  requestResources.resource_class       = "Quickhelp";
  requestResources.resource_type        = XmRString;
  requestResources.resource_size        = sizeof (char *);
  requestResources.default_type         = XmRImmediate;
  requestResources.resource_offset      = 0;
  requestResources.default_addr         = (XtPointer) NULL;

  XtGetSubresources (w, (XtPointer) &returnValue, "help", "Help", &requestResources, 1, NULL, 0);

  if(returnValue) return (returnValue);
  else return strdup("");
}

