/*****************************************************************************
 *****************************************************************************
 **
 **   File:         icon.c
 **
 **   Description:  Icon support functions for the CDE Drag & Drop Demo.
 **
 **  (c) Copyright 1993, 1994 Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
 **      Novell, Inc.
 **
 ****************************************************************************
 ************************************<+>*************************************/

#include <stdio.h>

#include <Xm/Screen.h>

#include <Dt/Dts.h>
#include <Dt/Dnd.h>

#include "icon.h"

 /*************************************************************************
 *
 *       Data Structures & Private Declarations For Icon Handling Functions
 *
 **************************************************************************/

#define ICON_TEXT_YGAP  2

#define ICON_PATH               "/usr/dt/appconfig/icons/C"
#define ICON_BITMAP_SUFFIX      "m.bm"
#define ICON_MASK_SUFFIX        "m_m.bm"

/*
 * Default Generic Icon Bitmap & Mask
 */

#define iconWidth 32
#define iconHeight 32
static unsigned char iconBits[] = {
   0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0x0f, 0x08, 0x00, 0x00, 0x08,
   0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x88, 0x9d, 0x6d, 0x08,
   0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x88, 0xf9, 0xed, 0x08,
   0x08, 0x00, 0x00, 0x08, 0xc8, 0x6e, 0x7d, 0x08, 0x08, 0x00, 0x00, 0x08,
   0xc8, 0x9d, 0xef, 0x09, 0x08, 0x00, 0x00, 0x08, 0xc8, 0xdd, 0xb3, 0x08,
   0x08, 0x00, 0x00, 0x08, 0xc8, 0xdf, 0xfd, 0x09, 0x08, 0x00, 0x00, 0x08,
   0xc8, 0xf6, 0xde, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08,
   0x88, 0xed, 0xd2, 0x08, 0x08, 0x00, 0x00, 0x08, 0xc8, 0xbb, 0xf3, 0x08,
   0x08, 0x00, 0x00, 0x08, 0x48, 0xdf, 0x5e, 0x08, 0x08, 0x00, 0x00, 0x08,
   0xc8, 0x36, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08,
   0x08, 0x00, 0x00, 0x08, 0xf8, 0xff, 0xff, 0x0f};
 
#define iconMaskWidth 32
#define iconMaskHeight 32
static unsigned char iconMaskBits[] = {
   0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x0f};
 
/*
 * Blank Bitmap
 */

#define blankWidth 16
#define blankHeight 16
static unsigned char blankBits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

/*
 * Private Icon Handling Function Declarations
 */

static void	iconClearBitmap(Display*, Window, GC, Pixmap, int, int);
static Pixmap	iconDefaultBitmap(Display*, Window, IconBitmapPart,
			unsigned short*, unsigned short*);
static void	iconFree(IconInfo*);
static Pixmap	iconGetBitmap(Widget, char*, IconBitmapPart, unsigned short*,
			unsigned short*);
static char	*iconGetIconPath(void*, int, IconTyping, IconBitmapPart);
static Pixmap	iconMergeBitmaps(Widget, Pixmap, int, int, Pixmap, int, int,
			int, int, Boolean);

 /*************************************************************************
 *
 *	Public Icon Handling Functions
 *
 **************************************************************************/

/*
 * IconCreateDouble
 *
 * Create a double icon from a single icon. The double icon has a bitmap
 * consisting of 2 images of the original bitmap offset from one another.
 */
void
IconCreateDouble(
	Widget		widget,
	IconInfo	*oldIcon,
	int		xOffset,
	int		yOffset,
	IconInfo	*newIcon,
	short		xx,
	short		yy)
{
	newIcon->bitmap = iconMergeBitmaps(widget,
		oldIcon->bitmap, oldIcon->icon.width, oldIcon->icon.height,
		oldIcon->bitmap, oldIcon->icon.width, oldIcon->icon.height,
		xOffset, yOffset, False);
	newIcon->mask = iconMergeBitmaps(widget,
		oldIcon->mask, oldIcon->icon.width, oldIcon->icon.height,
		oldIcon->mask, oldIcon->icon.width, oldIcon->icon.height,
		xOffset, yOffset, True);
	newIcon->icon.width = oldIcon->icon.width + 10;
	newIcon->icon.height = oldIcon->icon.height + 10;
	newIcon->name = XtNewString("Multiple");
	newIcon->icon.x = xx;
	newIcon->icon.y = yy;
	newIcon->dragIcon = DtDndCreateSourceIcon(widget,
		newIcon->bitmap, newIcon->mask);
}

/*
 * IconDelete
 *
 * Remove the icon from its list and deallocate memory used by the icon.
 */
void
IconDelete(
	Widget		drawArea,
	IconInfo	*iconPtr)
{
	IconInfo	*iconList;

	if (iconPtr == NULL || drawArea == NULL) {
		return;
	}

	if (iconPtr->next != NULL) { /* iconPtr is not the tail of the list */
		iconPtr->next->prev = iconPtr->prev;
	}

	if (iconPtr->prev != NULL) { /* iconPtr is not the head of the list */
		iconPtr->prev->next = iconPtr->next;
	}
	if (iconPtr->next == NULL) { /* iconPtr is the tail of the list */
		iconList = iconPtr->prev;
	} else {
		iconList = iconPtr->next;
	}

	while (iconList != NULL && iconList->prev != NULL) {
		iconList = iconList->prev;
	}
	iconFree(iconPtr);

	XtVaSetValues(drawArea, XmNuserData, iconList, NULL);
}

/*
 * IconDraw
 *
 * Render given icon in the window of the given widget.
 */
void
IconDraw(
	Widget		widget,
	IconInfo	*iconPtr)
{
	static GC	graphicsContext = NULL;
	static XFontStruct *fontStruct;
	Display		*display = XtDisplayOfObject(widget);
	Window		window = XtWindow(widget);
	int		screen = DefaultScreen(display);
	XGCValues	gcValues;

	if (graphicsContext == NULL) {
		gcValues.foreground = BlackPixel(display, screen);
                gcValues.background = WhitePixel(display, screen);
                graphicsContext = XCreateGC(display, window,
                                GCForeground | GCBackground, &gcValues);
	}

	/*
	 * Draw icon
	 */

	gcValues.clip_mask = iconPtr->mask;
	gcValues.clip_x_origin = iconPtr->icon.x;
	gcValues.clip_y_origin = iconPtr->icon.y;
	XChangeGC(display, graphicsContext,
		GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcValues);
	XCopyPlane(display, iconPtr->bitmap, window, graphicsContext, 0, 0,
		iconPtr->icon.width, iconPtr->icon.height,
		iconPtr->icon.x, iconPtr->icon.y, 1L);

	/*
	 * Draw icon name centered below icon
	 */

        if (iconPtr->name != NULL) {
                int             nameX, nameY;
                int             direction, ascent, decent;
                XCharStruct     overall;
                Widget          xmScreen;
 
                if (fontStruct == NULL) {
                        xmScreen = XmGetXmScreen(XtScreen(widget));
                        XtVaGetValues(xmScreen, XmNfont, &fontStruct, NULL);
                }
                XTextExtents(fontStruct, iconPtr->name, strlen(iconPtr->name),
                        &direction, &ascent, &decent, &overall);
 
                nameX = (iconPtr->icon.x + (iconPtr->icon.width/2)) -
                        (overall.width/2);
                nameY = iconPtr->icon.y + iconPtr->icon.height +
                        ICON_TEXT_YGAP + ascent;
 
                gcValues.font = fontStruct->fid;
                gcValues.clip_mask = None;
                XChangeGC(display, graphicsContext,
                        GCFont | GCClipMask, &gcValues);
                XDrawString(display, window, graphicsContext, nameX, nameY,
                        iconPtr->name, strlen(iconPtr->name));
        }
}

/*
 * IconInitialize
 *
 * Initialize given icon with given data. Get icon bitmap based on data
 * associated with the icon. This data may be a file name, a buffer or
 * a type name. Create source icon for dragging based on the bitmap and mask.
 */
void
IconInitialize(
	Widget		widget,
	IconInfo	*iconPtr,
	short		x,
	short		y,
	void		*data,
	int		dataLen,
	char		*name,
	IconTyping	typing)
{
	char		*iconPath;

	iconPath = iconGetIconPath(data, dataLen, typing, IconMask);
	iconPtr->mask = iconGetBitmap(widget, iconPath, IconMask,
		&(iconPtr->icon.width), &(iconPtr->icon.height));
	XtFree(iconPath);

	iconPath = iconGetIconPath(data, dataLen, typing, IconBitmap);
	iconPtr->bitmap = iconGetBitmap(widget, iconPath, IconBitmap,
		&(iconPtr->icon.width), &(iconPtr->icon.height));
	XtFree(iconPath);

        iconPtr->icon.x = x;
	iconPtr->icon.y = y;
        iconPtr->name = XtNewString(name);
        iconPtr->dragIcon = DtDndCreateSourceIcon(widget, 
                iconPtr->bitmap, iconPtr->mask); 
}

/*
 * IconNew
 *
 * Allocate memory for a new icon structure and clear.
 */
IconInfo*
IconNew()
{
	IconInfo	*iconPtr;

	iconPtr = (IconInfo*) XtMalloc(sizeof(IconInfo));
	memset(iconPtr, 0x00, sizeof(IconInfo));

	return iconPtr;
}

 /*************************************************************************
 *
 *	Private Icon Handling Functions
 *
 **************************************************************************/

/*
 * iconClearBitmap
 *
 * Clear a bitmap by filling it with zeros.
 */
static void
iconClearBitmap(
	Display		*display,
	Window		window,
	GC		graphicsContext,
	Pixmap		bitmap,
	int		width,
	int		height)
{
	int		xx, yy;
	static Pixmap	blankBitmap = NULL;

	if (blankBitmap == NULL) {
		blankBitmap = XCreateBitmapFromData(display, window,
			(char *)blankBits, blankWidth, blankHeight);
	}
	for (xx = 0; xx < width + blankWidth; xx += blankWidth) {
	        for (yy = 0; yy < height + blankHeight; yy += blankHeight) {
			XCopyArea(display, blankBitmap, bitmap,
				graphicsContext, 0, 0,
				blankWidth, blankHeight, xx, yy);
		}
	}
}

/*
 * iconDefaultBitmap
 *
 * Create default icon bitmap or mask and set width and height accordingly.
 */
static Pixmap
iconDefaultBitmap(
	Display		*display,
	Window		window,
	IconBitmapPart	iconPart,
	unsigned short	*width,
	unsigned short	*height)
{
	static Pixmap	bitmap;
	static Pixmap	mask;

	if (iconPart == IconMask) { /* create default mask */
		if (mask == NULL) {
			mask = XCreateBitmapFromData(display, window,
			   (char *)iconMaskBits, iconMaskWidth, iconMaskHeight);
		}
		*width = iconMaskWidth;
		*height = iconMaskHeight;

		return mask;

	} else { /* create default bitmap */
		if (bitmap == NULL) {
			bitmap = XCreateBitmapFromData(display, window,
				(char *)iconBits, iconWidth, iconHeight);
		}
		*width = iconWidth;
		*height = iconHeight;

		return bitmap;
	}
}

/*
 * iconFree
 *
 * Deallocate the icon and associated resources.
 */
static void
iconFree(
	IconInfo	*iconPtr)
{
	Display		*display = XtDisplayOfObject(iconPtr->dragIcon);

	if (iconPtr == NULL) {
		return;
	}

	XtFree(iconPtr->name);
	/*
	 * REMIND: This needs to free other things too...
	 * XFreePixmap(display, iconPtr->bitmap);
	 * XFreePixmap(display, iconPtr->mask);
	 * Free(iconPtr->dragIcon);
	 */
	XtFree((char *)iconPtr);
}

/*
 * iconGetBitmap
 *
 * Get a bitmap for the icon based on the path and name of the icon. 
 * If no bitmap file is found use a built-in default.
 */
static Pixmap
iconGetBitmap(
	Widget		widget,
	char		*iconPath,
	IconBitmapPart	iconPart,
	unsigned short	*returnWidth,
	unsigned short	*returnHeight)
{
	Display		*display = XtDisplayOfObject(widget);
	Window		window = DefaultRootWindow(display);
	int		status;
	int		xHot, yHot;
	Pixmap		bitmap;
	unsigned int	width, height;
	
	*returnWidth = *returnHeight = 0;

	if (iconPath != NULL) {

		status = XReadBitmapFile(display, window, iconPath,
			&width, &height, &bitmap, &xHot, &yHot);

		if (status == BitmapSuccess) {
			*returnWidth = (unsigned short) width;
			*returnHeight = (unsigned short) height;
		} else {
			printf(
			    "Unable to read icon from bitmap file \"%s\".\n"
			    "Using default icon bitmap.\n",
			    iconPath, NULL);
			bitmap = iconDefaultBitmap(display, window, iconPart,
				returnWidth, returnHeight);
		}
	} else {
		bitmap = iconDefaultBitmap(display, window, iconPart,
			returnWidth, returnHeight);
	}
	return bitmap;
}

/*
 * iconGetIconPath
 *
 * Get the file path and name for an icon based on the given data and
 * method of typing that data. This function assumes the data typing
 * database has been loaded with DtDtsLoadDataTypes().
 */
static char*
iconGetIconPath(
	void		*data,
	int		dataLen,
	IconTyping	typing,
	IconBitmapPart	iconPart)
{
	char		iconPath[MAXPATHLEN + 1],
			*iconName,
			*iconSuffix;

	switch (typing) {
	case IconByFile:
		iconName = DtDtsFileToAttributeValue(data, "ICON");
		break;
	case IconByData:
		iconName = DtDtsBufferToAttributeValue(data, dataLen,
			"ICON", NULL);
		break;
	case IconByType:
		iconName = DtDtsDataTypeToAttributeValue(data, "ICON", NULL);
		break;
	default:
		iconName = NULL;
	}
	if (iconName != NULL) {
		if (iconPart == IconMask) {
			iconSuffix = ICON_MASK_SUFFIX;
		} else {
			iconSuffix = ICON_BITMAP_SUFFIX;
		}
		sprintf(iconPath, "%s/%s.%s", ICON_PATH, iconName, iconSuffix);
		DtDtsFreeAttributeValue(iconName);
	}
	return XtNewString(iconPath);
}

/*
 * iconMergeBitmaps
 *
 * Create new bitmap consisting of the offset composition of the given bitmaps.
 */
static Pixmap
iconMergeBitmaps(
	Widget		widget,
	Pixmap		bitmap1,
	int		width1,
	int		height1,
	Pixmap		bitmap2,
	int		width2,
	int		height2,
	int		xOffset,
	int		yOffset,
	Boolean		clear)
{
	Display		*display = XtDisplayOfObject(widget);
	Window		window = DefaultRootWindow(display);
	int		screen = DefaultScreen(display);
	Pixmap		mergedBitmap, blankBitmap;
	int		extraX, extraY, width, height;
	static GC	graphicsContext = NULL;
	XGCValues	gcValues;

	extraX = width2 - width1 + xOffset + 1;
	if (extraX < 0) extraX = 0;
	width = width1 + extraX;

	extraY = height2 - height1 + yOffset + 1;
	if (extraY < 0) extraY = 0;
	height = height1 + extraY;

	mergedBitmap = XCreatePixmap(display, window, width, height, 1);

	if (graphicsContext == NULL) {
                graphicsContext = XCreateGC(display, mergedBitmap, 0L, NULL);
	}

	if (clear) {
		iconClearBitmap(display, window, graphicsContext, mergedBitmap,
			width, height);
	}

	XCopyArea(display, bitmap2, mergedBitmap, graphicsContext, 0, 0,
		width2, height2, xOffset, yOffset);

	XCopyArea(display, bitmap1, mergedBitmap, graphicsContext, 0, 0,
		width1, height1, 0, 0);

	return mergedBitmap;
}

