/*
 * CMWmain.c --
 *
 * Procedures to interface the colormap editor with the window package
 * for the purposes of window creation, deletion, and modification.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/cmwind/CMWmain.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "database/database.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/main.h"
#include "commands/commands.h"
#include "cmwind/cmwind.h"
#include "graphics/graphicsInt.h"
#include "graphics/graphics.h"
#include "textio/textio.h"
#include "utils/utils.h"
#include "utils/styles.h"
#include "graphics/glyphs.h"
#include "utils/malloc.h"

global WindClient CMWclientID;

/* Forward and external declarations */

extern void cmwColor(MagWindow *w, TxCommand *cmd);
extern void cmwSave(MagWindow *w, TxCommand *cmd);
extern void cmwLoad(MagWindow *w, TxCommand *cmd);
extern void cmwPushbutton(MagWindow *w, TxCommand *cmd);
extern bool RGBxHSV(double r, double g, double b, double *h, double *s, double *v);
extern void CMWundoInit(void);

/* -------------------------------------------------------------------- */

/*
 * The following is the layout of a color map window:
 *
 * +========================================================================+
 * |                         +--------------------+			    |
 * |			     |                    |			    |
 * |                         +--------------------+			    |
 * |									    |
 * |               Red   		                 Hue                |
 * |  +---+ +----------------+ +---+        +---+ +----------------+ +---+  |
 * |  | - | |                | | + |        | - | |                | | + |  |
 * |  +---+ +----------------+ +---+        +---+ +----------------+ +---+  |
 * |									    |
 * |              Green                               Saturation            |
 * |  +---+ +----------------+ +---+        +---+ +----------------+ +---+  |
 * |  | - | |                | | + |        | - | |                | | + |  |
 * |  +---+ +----------------+ +---+        +---+ +----------------+ +---+  |
 * |									    |
 * |               Blue                                 Value
 * |  +---+ +----------------+ +---+        +---+ +----------------+ +---+  |
 * |  | - | |                | | + |        | - | |                | | + |  |
 * |  +---+ +----------------+ +---+        +---+ +----------------+ +---+  |
 * |									    |
 * +========================================================================+
 *
 * The top box displays the current color, and can be buttoned
 * to select a different color for editing, or to copy an existing
 * color into the current color.
 *
 * Each of the boxes Red, Green, Blue, etc, is referred to as a
 * "color bar".  The following table defines the location of each
 * color bar.  Each of the + and minus boxes as a "color pump",
 * which increments or decrements the particular value depending on
 * which pump is hit and which mouse button is used to hit it.
 */

const ColorBar colorBars[] =
{
    {"Red",	CB_RED,		STYLE_RED,	{{ 2000,	8000},	{10000,	 9000}},
						{{ 2000,	9500},	{10000,	10500}}},
    {"Green",	CB_GREEN,	STYLE_GREEN,	{{ 2000,	5000},	{10000,	 6000}},
						{{ 2000,	6500},	{10000,	 7500}}},
    {"Blue",	CB_BLUE,	STYLE_BLUE,	{{ 2000,	2000},	{10000,	 3000}},
						{{ 2000,	3500},	{10000,	 4500}}},
    {"Hue",	CB_HUE,		STYLE_YELLOW,	{{14000,	8000},	{22000,	 9000}},
						{{14000,	9500},	{22000,	10500}}},
    {"Saturation", CB_SAT,	STYLE_GRAY,	{{14000,	5000},	{22000,	 6000}},
						{{14000,	6500},	{22000,	 7500}}},
    {"Value",	CB_VALUE,	STYLE_BROWN1,	{{14000,	2000},	{22000,	 3000}},
						{{14000,	3500},	{22000,	 4500}}},
    {0}
};

const ColorPump colorPumps[] =
{
    {CB_RED,	-.0078,	{{  500, 8000},	{ 1500,	9000}}},
    {CB_RED,	 .0078,	{{10500, 8000},	{11500,	9000}}},
    {CB_GREEN,	-.0078,	{{  500, 5000},	{ 1500,	6000}}},
    {CB_GREEN,	 .0078,	{{10500, 5000},	{11500,	6000}}},
    {CB_BLUE,	-.0078,	{{  500, 2000},	{ 1500,	3000}}},
    {CB_BLUE,	 .0078,	{{10500, 2000},	{11500,	3000}}},
    {CB_HUE,	-.01,	{{12500, 8000},	{13500,	9000}}},
    {CB_HUE,	 .01,	{{22500, 8000},	{23500,	9000}}},
    {CB_SAT,	-.01,	{{12500, 5000},	{13500,	6000}}},
    {CB_SAT,	 .01,	{{22500, 5000},	{23500,	6000}}},
    {CB_VALUE,	-.01,	{{12500, 2000},	{13500,	3000}}},
    {CB_VALUE,	 .01,	{{22500, 2000},	{23500,	3000}}},
    {-1}
};

const Rect cmwCurrentColorArea = {{6000, 12000}, {18000, 15000}};
const Rect cmwCurrentColorTextBox = {{6000, 15500}, {18000, 16500}};
const char * const cmwCurrentColorText = "Color Being Edited";

/* Bounding rectangle for entire window */

const Rect colorWindowRect = {{0, 1500}, {24000, 17000}};

/*
 * ----------------------------------------------------------------------------
 *
 * CMWcreate --
 *
 * A new window has been created.  Create and initialize the needed
 * structures.
 *
 * Results:
 *	FALSE if we have too many windows, TRUE otherwise.
 *
 * Side effects:
 *	Initialize the window to be editing the background color.
 *
 * ----------------------------------------------------------------------------
 */

bool
CMWcreate(
    MagWindow *window,
    int argc,
    char *argv[])
{
    CMWclientRec *crec;
    unsigned int color;

    crec = (CMWclientRec *) mallocMagic(sizeof(CMWclientRec));
    window->w_clientData = (ClientData) crec;
    if (argc > 0) sscanf(argv[0], "%o", &color);
    else color = 0;
    color &= 0377;
    window->w_flags &= ~(WIND_SCROLLABLE | WIND_SCROLLBARS | WIND_CAPTION);
    window->w_frameArea.r_xtop = GrScreenRect.r_xtop;
    window->w_frameArea.r_xbot = GrScreenRect.r_xtop - 250;
    window->w_frameArea.r_ybot = 0;
    window->w_frameArea.r_ytop = 200;
    WindSetWindowAreas(window);
    CMWloadWindow(window, color);
    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CMWdelete --
 *
 * Clean up the data structures before deleting a window.
 *
 * Results:
 *	TRUE if we really want to delete the window, FALSE otherwise.
 *
 * Side effects:
 *	A CMWclientRec is freed.
 *
 * ----------------------------------------------------------------------------
 */

bool
CMWdelete(
    MagWindow *window)
{
    CMWclientRec *cr;

    cr = (CMWclientRec *) window->w_clientData;
    if (cr->cmw_cname)
	freeMagic(cr->cmw_cname);
    freeMagic((char *) cr);
    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CMWreposition --
 *
 * A window has moved -- center it.
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	May change the window's view.
 *
 * ----------------------------------------------------------------------------
 */

/*ARGSUSED*/
void
CMWreposition(
    MagWindow *window,
    Rect *newScreenArea,	/* UNUSED */
    bool final)
{
    if (final)
	WindMove(window, &colorWindowRect);
}


/*
 * ----------------------------------------------------------------------------
 *
 * CMWredisplay --
 *
 * Redisplay a portion of a window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Redisplay is done.
 * ----------------------------------------------------------------------------
 */

void
CMWredisplay(
    MagWindow *w,		/* The window containing the area. */
    Rect *rootArea,	/* Redisplay area in surface coordinates. */
    Rect *clipArea)	/* An area on the screen to clip to. */
{
    CMWclientRec *cr;
    const ColorBar *cb;
    const ColorPump *cp;
    Rect rect, screenR;
    Point screenP;
    double values[6], x;
    int r, g, b;
    char *string;

    GrLock(w, TRUE);

    cr = (CMWclientRec *) w->w_clientData;

    /* Erase previous information */

    WindSurfaceToScreen(w, rootArea, &screenR);
    GrClipBox(clipArea, STYLE_ERASEALL);

    /* Get current color map values */
    (void) GrGetColor(cr->cmw_color, &r, &g, &b);
    values[CB_RED] = ( ((double) r) + 0.5 ) / 255.0;
    values[CB_GREEN] = ( ((double) g) + 0.5 ) / 255.0;
    values[CB_BLUE] = ( ((double) b) + 0.5 ) / 255.0;
    RGBxHSV(values[CB_RED], values[CB_GREEN], values[CB_BLUE],
	   &values[CB_HUE], &values[CB_SAT], &values[CB_VALUE]);

    /* Redisplay each bar */
    for (cb = colorBars; cb->cb_name; cb++)
    {
	x = values[cb->cb_code];

	/* Display the color bar and its bounding box. */

	if (GEO_TOUCH(&cb->cb_rect, rootArea))
	{
	    rect.r_xbot = cb->cb_rect.r_xbot;
	    rect.r_ybot = cb->cb_rect.r_ybot;
	    rect.r_ytop = cb->cb_rect.r_ytop;
	    rect.r_xtop = cb->cb_rect.r_xbot +
		(int) (x * (double) (cb->cb_rect.r_xtop - cb->cb_rect.r_xbot));
	    WindSurfaceToScreen(w, &rect, &screenR);
	    GrClipBox(&screenR, cb->cb_style);

	    WindSurfaceToScreen(w, &cb->cb_rect, &screenR);
	    GrClipBox(&screenR, STYLE_BBOX);
	}

	/* Display bar name */
	if (GEO_TOUCH(&cb->cb_textRect, rootArea))
	{
	    WindSurfaceToScreen(w, &cb->cb_textRect, &screenR);
	    screenP.p_x = (screenR.r_xbot + screenR.r_xtop)/2;
	    screenP.p_y = (screenR.r_ybot + screenR.r_ytop)/2;
	    GeoClip(&screenR, &GrScreenRect);
	    GrPutText(cb->cb_name, STYLE_BBOX, &screenP,
		GEO_CENTER, GR_TEXT_LARGE,
		TRUE, &screenR, (Rect *) NULL);
	}
    }

    /* Redisplay each of the color pumps. */

    for (cp = colorPumps; cp->cp_code >= 0; cp++)
    {
	if (GEO_TOUCH(&cp->cp_rect, rootArea))
	{
	    WindSurfaceToScreen(w, &cp->cp_rect, &screenR);
	    GrClipBox(&screenR, STYLE_BBOX);
	    screenP.p_x = (screenR.r_xbot + screenR.r_xtop)/2;
	    screenP.p_y = (screenR.r_ybot + screenR.r_ytop)/2;
	    if (cp->cp_amount < 0) string = "-";
	    else string = "+";
	    GeoClip(&screenR, &GrScreenRect);
	    GrPutText(string, STYLE_BBOX, &screenP, GEO_CENTER, GR_TEXT_LARGE,
		    TRUE, &screenR, (Rect *) NULL);
	}
    }

    /* Redisplay the box at the top that shows the color being edited.
     * This is a bit tricky, since we don't know the style corresponding
     * to that color.  Instead, change the color in a particular style
     * reserved for our own use.
     */

    if (GEO_TOUCH(&cmwCurrentColorArea, rootArea))
    {
	GrStyleTable[STYLE_CMEDIT].color = cr->cmw_color;
	WindSurfaceToScreen(w, &cmwCurrentColorArea, &screenR);
	GrClipBox(&screenR, STYLE_CMEDIT);
	GrClipBox(&screenR, STYLE_BBOX);
    }
    if (GEO_TOUCH(&cmwCurrentColorTextBox, rootArea))
    {
	WindSurfaceToScreen(w, &cmwCurrentColorTextBox, &screenR);
	screenP.p_x = (screenR.r_xbot + screenR.r_xtop)/2;
	screenP.p_y = (screenR.r_ybot + screenR.r_ytop)/2;
	GeoClip(&screenR, &GrScreenRect);
	GrPutText(cmwCurrentColorText, STYLE_BBOX, &screenP,
	    GEO_CENTER, GR_TEXT_LARGE,
	    TRUE, &screenR, (Rect *) NULL);
    }

    GrUnlock(w);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CMWloadWindow --
 *
 * Load the window with a new color.
 * A color name of NULL causes us to edit the background color.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Loads a color into the window.
 *
 * ----------------------------------------------------------------------------
 */

void
CMWloadWindow(
    MagWindow *w,	/* Identifies window to which color is to be bound */
    int color)		/* New color to be bound to this window. */
{
    CMWclientRec *cr = (CMWclientRec *) w->w_clientData;
    char caption[40];

    cr->cmw_color = color;
    cr->cmw_cname = (char *) NULL;

    sprintf(caption, "COLOR = 0%o", cr->cmw_color);
    WindCaption(w, caption);
    WindAreaChanged(w, (Rect *) NULL);

    /* move the contents of the window so the color bars show */
    WindMove(w, &colorWindowRect);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CMWinit --
 *
 *	Add the color-map window client to the window module.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Add ourselves as a client to the window package.
 *
 * ----------------------------------------------------------------------------
 */

void
CMWinit(void)
{
    CMWclientID = WindAddClient("color", CMWcreate, CMWdelete,
			CMWredisplay, CMWcommand,
			(void(*)()) NULL,
			CMWCheckWritten,
			CMWreposition,
			(GrGlyph *) NULL);
    CMWundoInit();

    /* Register commands with the client */

    WindAddCommand(CMWclientID,
	"pushbutton button	invoke a button press in the color window",
	 cmwPushbutton, FALSE);
    WindAddCommand(CMWclientID,
       "color [color-#]	        specify color to edit, or print current intensities",
	cmwColor, FALSE);
    WindAddCommand(CMWclientID,
       "load [techStyle displayStyle monitorType]\n\
                        load new color map techStyle.displayStyle.monitorType",
	cmwLoad, FALSE);
    WindAddCommand(CMWclientID,
       "save [techStyle displayStyle monitorType]\n\
                        save color map to techStyle.displayStyle.monitorType",
	cmwSave, FALSE);
}
