/**Palette.c*************************************************************
 *									*
 *   SeePix -- by Hank Schafer						*
 *									*
 *   SeePix is an IFF Picture Viewer.  It works with Lo-Res,		*
 *   Med-Res, Hi-Res, HAM, and EHB formats.  I'm working on support     *
 *   for X-Specs, and Anim5 formats.					*
 *									*
 *   SeePix is based on a program by Olaf Barthel, called		*
 *   'LoadImage'.  As released, LoadImage had a couple of bugs. 	*
 *   The Amiga-Key alternatives to menu use didn't all work.  That's	*
 *   been fixed.  There were two separate print functions in LoadImage	*
 *   which behaved exactly the same.  The redundancy was eliminated.	*
 *   LoadImage used the Topaz ROMFONT, and made no allowances for a	*
 *   different system default font (under 2.04).  I added a built-in	*
 *   font.  I've reworked the code considerably from the original       *
 *   release, to allow for optimizations based on time and space.	*
 *									*
 *   SeePix features a palette tool which allows "tweaking" of colors	*
 *   prior to printing, allowing you to modify the color printout to	*
 *   more closely resemble the original graphics (Blue is Blue, not	*
 *   Purple).  SeePix can be Iconified.  SeePix features an ARP 	*
 *   interface.  SeePix now uses the PathMaster File Selector.		*
 *									*
 ************************************************************************
 *                                                                      *
 *   SeePix Copyright © 1992 by Hank Schafer; all rights reserved.      *
 *                                                                      *
 *   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 1, 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:                         *
 *                 Free Software Foundation, Inc.                       *
 *                 675 Massachusetts Ave.                               *
 *                 Cambridge  MA  02139, USA                            *
 *                                                                      *
 **************** Special Function Copyright Notices ********************
 *									*
 ****LoadImage Copyright Notice:					*
 *									*
 *   LoadImage is © Copyright 1988, 1989, 1990 by MXM, all rights	*
 *   reserved, written by Olaf Barthel.  No guarantees of any kind are	*
 *   made that this program is 100% reliable.  Use this program on	*
 *   your own risk!							*
 *									*
 ****Iconify Copyright Notice:						*
 *									*
 *   Copyright 1987 by Leo L. Schwab.					*
 *   Permission is hereby granted for use in any and all programs,	*
 *   both Public Domain and commercial in nature, provided this 	*
 *   Copyright notice is left intact.					*
 *									*
 ****ColorWindow (Palette) Copyright Notice:				*
 *									*
 * ColorWindow Routine	--  Color Window Routines			*
 *     from Book 1 of the Amiga Programmers' Suite by RJ Mical          *
 *									*
 * Copyright (C) 1986, 1987, Robert J. Mical				*
 * All Rights Reserved. 						*
 *									*
 ****PathMaster Copyright Notice:					*
 *									*
 * -------------------------------------------------------------------- *
 *   Copyright © 1989 Justin V. McCormick.  All Rights Reserved.	*
 * -------------------------------------------------------------------- *
 *									*
 *    The PathMaster name is a trademark of Justin V. McCormick.	*
 *									*
 *   PathMaster File Selector source and documentation written by:	*
 *									*
 *			 Justin V. McCormick.				*
 *	       Copyright © 1989 by Justin V. McCormick. 		*
 *			 All Rights Reserved.				*
 *									*
 * -------------------------------------------------------------------- *
 ************************************************************************
 *									*
 *   Hope this is something you can use and enjoy.			*
 *									*
 ************************************************************************/

/*
 * DoCWind(); and support functions - the palette requester
 */

extern struct Gadget ColorTemplateGadgets[COLOR_GADGETS_COUNT];
extern struct Image ColorPropsImages[3];
extern struct PropInfo ColorPropsInfos[3];
extern struct Image ColorRGBImage;
extern struct Image ColorHSLImage;
extern struct Image SuperColorImages[32];
extern UWORD	RGBData[];
extern UWORD	HSLData[];

UBYTE	       *AllocRemember();
struct IntuiMessage *GetMsg();
struct Window  *OpenWindow();

/* ColorMode definitions */

#define COPYCOLOR	1
#define RANGE_FIRST  2
#define RANGE_SECOND 3

/* These are the dimensions of the color hit box */

#define COLOR_COLOR_ROWS	(4 * 10)
#define COLOR_COLOR_COLS	(8 * 15)
#define COLOR_COLOR_RIGHT   (COLOR_BOX_LEFT + COLOR_COLOR_COLS - 1)
#define COLOR_COLOR_BOTTOM  (COLOR_COLOR_TOP + COLOR_COLOR_ROWS - 1)

VOID		ResetColorProps();

struct NewWindow ColorNewWindow =
{
    20, 12,			/* LeftEdge, TopEdge */
    COLORWINDOW_WIDTH, COLORWINDOW_HEIGHT,	/* Width, Height */
    -1, -1,			/* FrontPen, BackPen */
    GADGETDOWN | GADGETUP | MOUSEBUTTONS | MENUPICK | MOUSEMOVE | ACTIVEWINDOW | INACTIVEWINDOW,	/* IDCMP Flags */
    WINDOWDRAG | SMART_REFRESH | NOCAREREFRESH | ACTIVATE,	/* Flags */
    &ColorTemplateGadgets[COLOR_GADGETS_COUNT - 1],	/* First Gadget */
    NULL,			/* CheckMark */
    (UBYTE *) "Print Palette:",	/* Title */
    NULL,			/* Screen */
    NULL,			/* BitMap */
    0, 0,			/* Minimum Width & Height */
    0, 0,			/* Maximum Width & Height */
    CUSTOMSCREEN,		/* Type */
};

UWORD		ColorMode;
UWORD		RangeFirst;
struct Window  *ColorWindow = NULL;
struct RastPort *ColorRPort;
struct ViewPort *ColorVPort;
WORD		RowCount, RowHeight, ColumnCount, ColumnWidth;
UWORD		SavePalette[32];

/* ======================================================================= */
/* === These Routines Open, Render, and Close the Color Window =========== */
/* ======================================================================= */

VOID
InitColorSizes(depth)
    WORD	    depth;

/*
 * This routine adjusts the row and column variables based on the number of
 * colors supported by the ColorWindow's screen.
 */

{
    RowCount = 1 << (depth >> 1);
    RowHeight = COLOR_COLOR_ROWS / RowCount;

    ColumnCount = 1 << ((depth + 1) >> 1);
    ColumnWidth = COLOR_COLOR_COLS / ColumnCount;
}

VOID
DrawBox(rp, left, top, right, bottom)
    struct RastPort *rp;
    WORD	    left, top, right, bottom;

/* A quick utility routine */

{
    WORD	    savepen;

    savepen = ColorRPort->FgPen;

    SetAPen(rp, 1);
    SetDrMd(rp, JAM2);

    Move(rp, left, top);
    Draw(rp, left, bottom);
    Draw(rp, right, bottom);
    Draw(rp, right, top);
    Draw(rp, left, top);

    SetAPen(ColorRPort, savepen);
}

VOID
ColorRectFill(pen)
    WORD	    pen;

/*
 * This routine sets the pen in the RastPort as the current pen selected by
 * the user, and then fills the color box with that pen color.
 */

{
    SetAPen(ColorRPort, pen);
    SetDrMd(ColorRPort, JAM1);
    RectFill(ColorRPort, COLOR_BOX_LEFT, COLOR_BOX_TOP, COLOR_BOX_RIGHT, COLOR_BOX_BOTTOM);
}

VOID
DrawColorWindow()
/* This routine fills in all the graphic details of the ColorWindow */

{
    WORD	    col, row, colstart, colend, rowstart;
    WORD	    savepen;

    savepen = ColorRPort->FgPen;

    InitColorSizes(ColorRPort->BitMap->Depth);

    ColorRectFill(ColorRPort->FgPen);

    DrawBox(ColorRPort, COLOR_BOX_LEFT - 2, COLOR_BOX_TOP - 2, COLOR_BOX_RIGHT + 2, COLOR_BOX_BOTTOM + 2);
    DrawBox(ColorRPort, COLOR_BOX_LEFT - 2, COLOR_COLOR_TOP - 2, COLOR_BOX_LEFT + (8 * 15) + 1, COLOR_COLOR_TOP + (4 * 10) + 1);

    colstart = COLOR_BOX_LEFT;
    colend = colstart + ColumnWidth - 1;
    for (col = 0; col < ColumnCount; col++) {
	rowstart = COLOR_COLOR_TOP;
	for (row = 0; row < RowCount; row++) {
	    SetAPen(ColorRPort, (row * ColumnCount) + col);
	    RectFill(ColorRPort, colstart, rowstart, colend, rowstart + RowHeight - 1);
	    rowstart += RowHeight;
	}
	colstart += ColumnWidth;
	colend += ColumnWidth;
    }

    SetAPen(ColorRPort, savepen);
}

struct Window  *
OpenColorWindow(screen, firstpen)
    struct Screen  *screen;
    WORD	    firstpen;
{
    WORD	    i;

    ColorNewWindow.Screen = screen;
    ColorNewWindow.Type = CUSTOMSCREEN;

    if ((ColorWindow = OpenWindow(&ColorNewWindow)) == 0)
	return (NULL);

    ColorVPort = &ColorWindow->WScreen->ViewPort;
    ColorRPort = ColorWindow->RPort;

    for (i = 0; i < 32; i++)
	SavePalette[i] = GetRGB4(ColorVPort->ColorMap, i);

    SetAPen(ColorRPort, firstpen);
    ResetColorProps();
    DrawColorWindow();

    ColorMode = NULL;

    return (ColorWindow);
}

VOID
CloseColorWindow(accept)
    BOOL	    accept;
{
    if (ColorWindow == NULL)
	return;

    if (NOT accept)
	LoadRGB4(ColorVPort, &SavePalette[0], 32);

    CloseWindow(ColorWindow);
    ColorWindow = NULL;
}

/* ======================================================================= */
/* === These Routines Manage the User Interaction ======================== */
/* ======================================================================= */

BOOL
ColorGadgetGotten(gadget)
    struct Gadget  *gadget;

/*
 * This routine manages the user's gadget selection.  If one of the end
 * gadgets, such as OK or CANCEL, was selected then this routine returns
 * FALSE, else it returns TRUE.
 */

{
    switch (gadget->GadgetID) {
    case COLOR_OK:
	CloseColorWindow(TRUE);
	PrintPix();
	return (FALSE);
	break;
    case COLOR_CANCEL:
	CloseColorWindow(FALSE);
	return (FALSE);
	break;
    case COLOR_COPY:
	ColorMode = COPYCOLOR;
	break;
    case COLOR_RANGE:
	ColorMode = RANGE_FIRST;
	break;
    case COLOR_HSL_RGB:
	ResetColorProps();
	break;
    }
    return (TRUE);
}

VOID
ColorRange(first, last)
    WORD	    first, last;

/* Create the color range from first to last */

{
    WORD	    i;
    WORD	    whole, redfraction, greenfraction, bluefraction, divisor;
    UWORD	    rgb;
    WORD	    firstred, firstgreen, firstblue;
    WORD	    lastred, lastgreen, lastblue;
    WORD	    workred, workgreen, workblue;

    /* If the pen numbers are out of order, swap */

    if (first > last) {
	i = first;
	first = last;
	last = i;
    }
    /*
     * I need to see a spread of at least two, where there's at least one
     * spot between the endpoints, else there's no work to do so I might as
     * well just return now.
     */

    if (first >= last - 1)
	return;

    rgb = GetRGB4(ColorVPort->ColorMap, first);
    firstred = (rgb >> 8) & 0xF;
    firstgreen = (rgb >> 4) & 0xF;
    firstblue = (rgb >> 0) & 0xF;

    rgb = GetRGB4(ColorVPort->ColorMap, last);
    lastred = (rgb >> 8) & 0xF;
    lastgreen = (rgb >> 4) & 0xF;
    lastblue = (rgb >> 0) & 0xF;


    divisor = last - first;

    /*
     * Do all math as fixed-point fractions where the low 8 bits are the
     * fraction.  This greatly lessens the effect of rounding errors.
     */

    whole = (lastred - firstred) << 8;
    redfraction = whole / divisor;

    whole = (lastgreen - firstgreen) << 8;
    greenfraction = whole / divisor;

    whole = (lastblue - firstblue) << 8;
    bluefraction = whole / divisor;

    for (i = first + 1; i < last; i++) {
	lastred = ((redfraction * (i - first)) + 0x0080) >> 8;
	workred = firstred + lastred;
	lastgreen = ((greenfraction * (i - first)) + 0x0080) >> 8;
	workgreen = firstgreen + lastgreen;
	lastblue = ((bluefraction * (i - first)) + 0x0080) >> 8;
	workblue = firstblue + lastblue;
	SetRGB4(ColorVPort, i, workred, workgreen, workblue);
    }
}

VOID
ColorWindowHit(x, y)
    WORD	    x, y;

/*
 * The color boxes at the bottom-right of the ColorWindow are not gadgets.
 * Instead, it's just graphics and this routine is used to detect whether the
 * user has selected one of the color boxes.
 */

{
    UWORD	    rgb, pen;

    /* Have we got a color specifier? */

    if ((x >= COLOR_BOX_LEFT) && (x <= COLOR_COLOR_RIGHT) && (y >= COLOR_COLOR_TOP) && (y <= COLOR_COLOR_BOTTOM)) {

	/* Yes, it's one of the color boxes.  Set this pen number */

	x = x - COLOR_BOX_LEFT;
	x = x / ColumnWidth;
	y = y - COLOR_COLOR_TOP;
	y = y / RowHeight;
	pen = (y * ColumnCount) + x;

	/* first, were we in COPY COLOR mode? */

	if (ColorMode == COPYCOLOR) {

	    /* ok, copy old color here first! */

	    rgb = GetRGB4(ColorVPort->ColorMap, ColorRPort->FgPen);
	    SetRGB4(ColorVPort, pen, rgb >> 8, rgb >> 4, rgb);
	    ColorMode = NULL;
	} else if (ColorMode == RANGE_FIRST) {
	    ColorMode = RANGE_SECOND;
	    RangeFirst = pen;
	} else if (ColorMode == RANGE_SECOND) {
	    ColorMode = NULL;
	    ColorRange(RangeFirst, pen);
	}
	ColorRectFill(pen);

	ResetColorProps();
    }
}

VOID
SetPropValueGrunt(value, y)
    WORD	    value, y;
{
    UBYTE	    text[16];

    if (value >= 0)
	sprintf(&text[0], "%ld ", value);
    else {
	text[0] = ' ';
	text[1] = ' ';
    }

    Move(ColorRPort, COLOR_VALUE_X, y);
    Text(ColorRPort, &text[0], 2);
}

VOID
SetPropValues(red, green, blue)
    WORD	    red, green, blue;

/* Sets the text to the right of the prop gadgets */

{
    WORD	    savepen;

    savepen = ColorRPort->FgPen;

    SetAPen(ColorRPort, 1);
    SetDrMd(ColorRPort, JAM2);

    SetPropValueGrunt(red, COLOR_VALUE_REDY);
    SetPropValueGrunt(green, COLOR_VALUE_GREENY);
    SetPropValueGrunt(blue, COLOR_VALUE_BLUEY);

    SetAPen(ColorRPort, savepen);
}

VOID
ModifyRGBColors()
{
    UWORD	    newred, newgreen, newblue;

    newred = ColorPropsInfos[0].HorizPot >> 12;
    newgreen = ColorPropsInfos[1].HorizPot >> 12;
    newblue = ColorPropsInfos[2].HorizPot >> 12;

    SetRGB4(ColorVPort, ColorRPort->FgPen, newred, newgreen, newblue);
    SetPropValues(newred, newgreen, newblue);
}

VOID
ModifyHSLColors()
{
    UWORD	    rgb;

    rgb = (UWORD) HSLToRGB(ColorPropsInfos[0].HorizPot, ColorPropsInfos[1].HorizPot, ColorPropsInfos[2].HorizPot);

    SetRGB4(ColorVPort, ColorRPort->FgPen, rgb >> 8, rgb >> 4, rgb);
    SetPropValues(-1, -1, -1);
}

VOID
ModifyColors()
/* This routine reacts to the user playing with one of the prop gadgets */

{
    if (ColorTemplateGadgets[COLOR_HSL_RGB].Flags & SELECTED)
	ModifyHSLColors();
    else
	ModifyRGBColors();
}

VOID
SetRGBProps(rgb)
    UWORD	    rgb;
{
    UWORD	    red, green, blue;

    red = (rgb >> 8) & 0xF;
    green = (rgb >> 4) & 0xF;
    blue = (rgb >> 0) & 0xF;

    ColorPropsInfos[0].HorizPot = (red << 12) | (red << 8) | (red << 4) | red;
    ColorPropsInfos[1].HorizPot = (green << 12) | (green << 8) | (green << 4) | green;
    ColorPropsInfos[2].HorizPot = (blue << 12) | (blue << 8) | (blue << 4) | blue;

    SetPropValues(red, green, blue);
}

VOID
SetHSLProps(rgb)
    UWORD	    rgb;
{
    RGBToHSL(rgb, &ColorPropsInfos[0].HorizPot, &ColorPropsInfos[1].HorizPot, &ColorPropsInfos[2].HorizPot);

    SetPropValues(-1, -1, -1);
}

VOID
ResetColorProps()
/*
 * This routine resets the proportional gadgets according to the current pen
 * number and the interaction technique
 */

{
    WORD	    bluepos;
    UWORD	    rgb;

    rgb = GetRGB4(ColorVPort->ColorMap, ColorRPort->FgPen);

    bluepos = RemoveGList(ColorWindow, &ColorTemplateGadgets[COLOR_BLUE], 3);

    if (ColorTemplateGadgets[COLOR_HSL_RGB].Flags & SELECTED)
	SetHSLProps(rgb);
    else
	SetRGBProps(rgb);

    AddGList(ColorWindow, &ColorTemplateGadgets[COLOR_BLUE], bluepos, 3, 0);
    RefreshGList(&ColorTemplateGadgets[COLOR_BLUE], ColorWindow, NULL, 3);
}

/* ======================================================================= */
/* === And finally, the Main Entry Point ================================= */
/* ======================================================================= */

BOOL
DoCWind(screen, left, top, firstpen, usergb)
    struct Screen  *screen;
    WORD	    left, top, firstpen;
    BOOL	    usergb;
{
    struct IntuiMessage *message;
    ULONG	    class;
    struct Gadget  *gadget;
    BOOL	    mousemoved;
    WORD	    x, y, code, i;
    struct Remember *key;
    UWORD	   *ptr;
    BOOL	    retvalue;

    key = NULL;
    retvalue = FALSE;

    if (ColorWindow)
	goto DONE;

    ColorNewWindow.LeftEdge = left;
    ColorNewWindow.TopEdge = top;

    if (usergb)
	ColorTemplateGadgets[COLOR_HSL_RGB].Flags &= ~SELECTED;
    else
	ColorTemplateGadgets[COLOR_HSL_RGB].Flags |= SELECTED;

    if ((ptr = (UWORD *) AllocRemember(&key, RGBHSL_SIZE * 2, MEMF_CHIP)) == NULL)
	goto DONE;
    ColorRGBImage.ImageData = ptr;
    for (i = 0; i < RGBHSL_SIZE; i++)
	*ptr++ = RGBData[i];
    if ((ptr = (UWORD *) AllocRemember(&key, RGBHSL_SIZE * 2, MEMF_CHIP)) == NULL)
	goto DONE;
    ColorHSLImage.ImageData = ptr;
    for (i = 0; i < RGBHSL_SIZE; i++)
	*ptr++ = HSLData[i];

    if (NOT OpenColorWindow(screen, firstpen & 0x3F))
	goto DONE;

    retvalue = TRUE;

    FOREVER
    {
	Wait(1 << ColorWindow->UserPort->mp_SigBit);

	mousemoved = FALSE;
	while (message = GetMsg(ColorWindow->UserPort)) {
	    class = message->Class;
	    code = message->Code;
	    gadget = (struct Gadget *) (message->IAddress);
	    x = message->MouseX;
	    y = message->MouseY;
	    ReplyMsg(message);

	    switch (class) {
	    case GADGETDOWN:
	    case GADGETUP:
		if (ColorGadgetGotten(gadget) == FALSE)
		    goto DONE;
		break;
	    case MOUSEMOVE:
		mousemoved = TRUE;
		break;
	    case MOUSEBUTTONS:
		if (code == SELECTDOWN)
		    ColorWindowHit(x, y);
		break;
	    }
	}
	if (mousemoved)
	    ModifyColors();
	SetPoint(ColorWindow);
    }

  DONE:
    FreeRemember(&key, TRUE);
    return (retvalue);
}
