/*
 *
 *  AM --- AmigaMail
 *  the code for the colorrequester is stolen 
 *  from ChangeColors V1.04, (C) 1991, 1992 by Stefan Sticht.
 *  Modified for use with AM by Christian Riede
 *
 *  AM is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY.  No author or distributor accepts responsibility to anyone
 *  for the consequences of using it or for whether it serves any
 *  particular purpose or works at all, unless he says so in writing.
 *  Refer to the GNU General Public License, Version 1, for full details.
 *  
 *  Everyone is granted permission to copy, modify and redistribute AM,
 *  but only under the conditions described in the GNU General Public
 *  License, Version 1.  A copy of this license is supposed to have been 
 *  given to you along with AM so you can know your rights and responsi-
 *  bilities.  It should be in a file named COPYING.  Among other things,
 *  the copyright notice and this notice must be preserved on all copies.
 *
 *  
 *
 */

/*
 *  original Copyright notice from ChangeColors:
 */

/*
 *  ChangeColors.c  ©1991, 1992 Stefan Sticht
 *
 *  Version history:
 *
 *  1.00: initial release
 *  1.01: now switching screen to front after gadgets and menus are build
 *  1.02: better calculation of TopEdge of first gadget
 *  1.03: added underscore feature of gadtools V37
 *  1.04: 
 */

#include "am.h"

#define RC_WARN         5l
#define RC_ERROR        10l
#define RC_FAIL         20l

#define RC_SIXBITPLANES 2l
#define RC_GOON         1l
#define RC_QUIT         0l
#define RC_RETRY       -1l
#define RC_NEXT        -2l

#define MAX(a, b) ((a)>=(b)?(a):(b))

#define RED(x)      ((x >> 8) & 0x000f)
#define GREEN(x)    ((x >> 4) & 0x000f)
#define BLUE(x)     (x & 0x000f)

#define DBL_INTERWIDTH  (INTERWIDTH << 1)
#define DBL_INTERHEIGHT (INTERHEIGHT << 1)
#define FOUR_INTERWIDTH (INTERWIDTH << 2)

#define GAD_PALETTE 1
#define GAD_RED     2
#define GAD_GREEN   3
#define GAD_BLUE    4
#define GAD_RESET   5
#define GAD_USE     6
#define GAD_CANCEL  7

#define INDICATOR_WIDTH 40
#define PALETTE_HEIGHT  20
#define PALETTE_WIDTH   208

UWORD colortable[32];

struct Gadget *gad_palette;
struct Gadget *gad_red;
struct Gadget *gad_green;
struct Gadget *gad_blue;
struct Gadget *gad_reset;
struct Gadget *gad_use;
struct Gadget *gad_cancel;

#define RETRY_GADGETS           "Retry|Cancel"
#define RESUME_GADGETS          "Resume"
#define OUTOFMEM                "Not enough free memory!"
#define MSG_LIBRARY_OPENERR     "%s (V%ld+) can't be opened!"
#define MSG_UNABLEOPENWIN       "Failed to open window!"

#define MSG_MENU_PROJECT        "Project"
#define MSG_MENU_PRO_NEXT       "Next public screen"
#define MSG_MENU_PRO_NEXT_SC    "N"
#define MSG_MENU_PRO_INFO       "About"
#define MSG_MENU_PRO_QUIT       "Quit"
#define MSG_MENU_PRO_QUIT_SC    "Q"

#define MSG_GAD_RED             "R_ed"
#define MSG_GAD_GREEN           "_Green"
#define MSG_GAD_BLUE            "_Blue"
#define MSG_GAD_RESET           "_Reset"
#define MSG_GAD_RESET_SC        'R'
#define MSG_GAD_USE             "_Use"
#define MSG_GAD_USE_SC          'U'
#define MSG_GAD_CANCEL          "_Cancel"
#define MSG_GAD_CANCEL_SC       'C'

#define ITEM_NEXT   7l
#define ITEM_INFO   8l
#define ITEM_QUIT   9l

struct NewMenu mynewmenu[] = {
    { NM_TITLE, (STRPTR)MSG_MENU_PROJECT, NULL, 0, 0l, 0l},
    {  NM_ITEM, (STRPTR)MSG_MENU_PRO_QUIT,  (STRPTR)MSG_MENU_PRO_QUIT_SC, 0, 0l, (APTR)ITEM_QUIT},

    {   NM_END, NULL, NULL, 0, 0l, 0l}
    };

WORD zoompos[] = {
    0, 0,
    0, 0
    };


#define P_WA_Left       0
#define P_WA_Top        1
#define P_WA_Width      2
#define P_WA_Height     3
#define P_WA_Title      6
#define P_WA_PubScreen  7
struct TagItem mywindowtag[] = {
    WA_Left,        0l,
    WA_Top,         12l,
    WA_Width,       400l,
    WA_Height,      150l,
    WA_IDCMP,       CLOSEWINDOW | SLIDERIDCMP | MENUPICK |
                    REFRESHWINDOW | VANILLAKEY,
    WA_Flags,       WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE |
                    SMART_REFRESH | ACTIVATE,
    WA_Title,       0l,
    WA_PubScreen,   0l,
    WA_Zoom,        /*(ULONG)&zoompos[0]*/ 0,
    TAG_END
    };

struct TagItem slidertags[] = {
    GTSL_Min, 0l,
    GTSL_Max, 15l,
    GTSL_LevelFormat,/* (ULONG)"%02ld  " */ 0,
    GTSL_MaxLevelLen, 4l,
    GTSL_LevelPlace, PLACETEXT_RIGHT,
    GT_Underscore, (ULONG)'_',
    TAG_END
    };

struct EasyStruct textreq = {
    sizeof (struct EasyStruct), /* ULONG es_StructSize      */
    0l,                         /* ULONG es_Flags           */
    NULL,                       /* UBYTE *es_Title          */
    NULL,                       /* UBYTE *es_TextFormat     */
    NULL,                       /* UBYTE *es_GadgetFormat   */
    };


/*
 *  Prototypes
 */
long colorreq(char *scrname, char *title, unsigned short initial_color);
long request(char *title, char *gadgets, char *text, ...);
unsigned long createallgadgets(struct Gadget **glist, APTR vi, struct Screen *scr);
short maxtextlength(struct RastPort *rp, char *t1, char *t2, char *t3);
void updatesliders(struct Window *win, UBYTE r, UBYTE g, UBYTE b);

/*
 *  request(): a glue routine to EasyRequest as simple as printf plus
 *             titlestring, gadgettexts
 *
 *  Input: char *title:         pointer to the title of the requester
 *         char *gadgets:       pointer to gadgettext
 *         char *text:          text displayed in requester
 *
 *  Result: same as EasyrequestArgs()
 *
 * !!! for more info see EasyRequestArgs() in Autodocs/intuition.doc !!!
 */
long request(char *title, char *gadgets, char *text, ...)
{
    struct Process *process;
    struct Window *win = NULL;
    va_list ap;
    long rc;

    va_start(ap, text);

    textreq.es_Title = (UBYTE *)title;
    textreq.es_TextFormat = (UBYTE *)text;
    textreq.es_GadgetFormat = (UBYTE *)gadgets;

    if (process = (struct Process *)FindTask(NULL)) win = process->pr_WindowPtr;

    /*
     *  win may be NULL
     */
    rc = EasyRequestArgs(win, &textreq, NULL, ap);

    va_end(ap);

    return(rc);
}

#define WORKBENCH "Workbench"

/*
 *  here is it: our colorrequester
 */
long colorreq(char *scrname, char *title, unsigned short initial_color)
{
    struct Screen *scr = NULL;
    struct Window *win = NULL;
    struct Menu *menu = NULL;
    struct Gadget *gadgetlist = NULL;
    struct IntuiMessage *msg;
    register struct ViewPort *vp;
    register struct ColorMap *cm;
    struct Gadget *gad;
    APTR  vi = NULL;
    unsigned long active_color = initial_color;
    unsigned long winwidth;
    long menuattached = FALSE;
    ULONG class;
    long rc = RC_GOON;
    long i, colors;
    unsigned short color;
    UWORD code;
    UBYTE r, g, b;

	mywindowtag[8].ti_Data = (ULONG)&zoompos[0];
	slidertags[2].ti_Data = (ULONG)"%02ld  ";

    /*
     *  get screen-pointer by locking it
     */
    if (!(scr = LockPubScreen((UBYTE *)scrname))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    if (scr->RastPort.BitMap->Depth > 5) {
        rc = RC_SIXBITPLANES;
        goto cleanup;
        }

    /*
     *  get visual info of screen for gadtools
     *
     *  perhaps some day GetVisualInfoA(scr, NULL) is allowed,
     *  this would safe the space for the stub GetVisualInfo()
     */
    if (!(vi = GetVisualInfo(scr, TAG_DONE))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    /*
     *  create menues
     *
     *  perhaps some day GetVisualInfoA(scr, NULL) is allowed,
     *  this would safe the space for the stub GetVisualInfo()
     */
    if (!(menu = CreateMenus(mynewmenu, TAG_DONE))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    /*
     *  layout menues
     */
    if (!(LayoutMenus(menu, vi, TAG_DONE))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    /*
     *  create all gadgets and get width of our window
     */
    if (!(winwidth = createallgadgets(&gadgetlist, vi, scr))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    /*
     *  get viewport and colormap for speed
     */
    vp = &scr->ViewPort;
    cm = vp->ColorMap;

    /*
     *  save colormap of screen
     *
     *  first: number of colors
     */
    colors = 2;
    for (r = scr->RastPort.BitMap->Depth; r > 1; r--) colors <<= 1;
    /*
     *  copy rgb values
     */
    for (i = 0; i < colors; i++) colortable[i] = GetRGB4(cm, i);

    /*
     *  set r, g, b to values of active_color
     */
    color = colortable[active_color];
    r = (UBYTE)RED(color);
    g = (UBYTE)GREEN(color);
    b = (UBYTE)BLUE(color);

    /*
     *  adjust window position and size
     */
    mywindowtag[P_WA_Width].ti_Data = winwidth;
    mywindowtag[P_WA_Height].ti_Data = (ULONG)(gad_use->TopEdge + gad_use->Height + INTERHEIGHT);
    mywindowtag[P_WA_Title].ti_Data = (ULONG)title;
    mywindowtag[P_WA_PubScreen].ti_Data = (ULONG)scr;
    zoompos[1] = mywindowtag[P_WA_Top].ti_Data = (ULONG)scr->BarHeight + 2l;
    zoompos[2] = TextLength(&scr->RastPort, (STRPTR)title, strlen(title)) + 100;
    zoompos[3] = (WORD)scr->BarHeight;

    if (!(win = OpenWindowTagList(NULL, &mywindowtag[0]))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    /*
     *  Unlock the screen
     */
    UnlockPubScreen(NULL, scr);
    scr = NULL;

    /*
     *  add gadgets to window
     */
    AddGList(win, gadgetlist, -1l, -1l, NULL);
    RefreshGList(gadgetlist, win, NULL, -1l);
    GT_RefreshWindow(win, NULL);
    updatesliders(win, r, g, b);

    /*
     *  add menus
     */
    if (!(menuattached = SetMenuStrip(win, menu))) {
        rc = RC_FAIL;
        goto cleanup;
        }

    /*
     *
     *  now watch our IDCMP port
     *
     */
    while (rc == RC_GOON) {

        Wait(1L << win->UserPort->mp_SigBit);

        while (msg = GT_GetIMsg(win->UserPort)) {

            /*
             *  save and free message
             */
            gad = (struct Gadget *)msg->IAddress;
            class = msg->Class;
            code  = msg->Code;

            GT_ReplyIMsg(msg);

            switch(class) {

                case CLOSEWINDOW:   rc = RC_QUIT;
                                    break;

                case MENUPICK:      if (code != MENUNULL) {

                                        struct MenuItem *item = ItemAddress(menu,(int)code);

                                        switch((int)GTMENUITEM_USERDATA(item)) {

                                            case ITEM_NEXT:
                                                rc = RC_NEXT;
                                                break;

                                            case ITEM_QUIT:
                                                rc = RC_QUIT;
                                                break;

                                            }

                                        }
                                    break;

                case VANILLAKEY:    switch (code) {

                                        case (MSG_GAD_USE_SC - ('A' - 'a')):
                                        case MSG_GAD_USE_SC:            rc = RC_QUIT;
                                                                        break;

                                        case 27:
                                        case (MSG_GAD_CANCEL_SC - ('A' - 'a')):
                                        case MSG_GAD_CANCEL_SC:         rc = RC_QUIT;

                                        case (MSG_GAD_RESET_SC - ('A' - 'a')):
                                        case MSG_GAD_RESET_SC:          LoadRGB4(vp, colortable, colors);
                                                                        color = GetRGB4(cm, active_color);
                                                                        r = (UBYTE)RED(color);
                                                                        g = (UBYTE)GREEN(color);
                                                                        b = (UBYTE)BLUE(color);
                                                                        updatesliders(win, r, g, b);
                                                                        break;

                                        }
                                    break;

                case GADGETUP:      switch(gad->GadgetID & GADTOOLMASK) {

                                        case GAD_PALETTE:
                                            if (code != active_color) {
                                                active_color = code;
                                                color = GetRGB4(cm, active_color);
                                                r = (UBYTE)RED(color);
                                                g = (UBYTE)GREEN(color);
                                                b = (UBYTE)BLUE(color);
                                                updatesliders(win, r, g, b);
                                                }
                                            break;

                                        case GAD_CANCEL:
                                            rc = RC_QUIT;

                                        case GAD_RESET:
                                            LoadRGB4(vp, colortable, colors);
                                            color = GetRGB4(cm, active_color);
                                            r = (UBYTE)RED(color);
                                            g = (UBYTE)GREEN(color);
                                            b = (UBYTE)BLUE(color);
                                            updatesliders(win, r, g, b);
                                            break;

                                        case GAD_USE:
                                            rc = RC_QUIT;

                                        }

                                    break;

                case GADGETDOWN:    switch(gad->GadgetID & GADTOOLMASK) {

                                        case GAD_PALETTE:
                                            color = GetRGB4(cm, active_color);
                                            r = (UBYTE)RED(color);
                                            g = (UBYTE)GREEN(color);
                                            b = (UBYTE)BLUE(color);
                                            break;

                                        }

                                    break;

                case MOUSEMOVE:     switch(gad->GadgetID & GADTOOLMASK) {

                                        case GAD_RED:
                                            r = code;
                                            SetRGB4(vp, active_color,(int)r,(int)g,(int)b);
                                            break;

                                        case GAD_GREEN:
                                            g = code;
                                            SetRGB4(vp, active_color,(int)r,(int)g,(int)b);
                                            break;

                                        case GAD_BLUE:
                                            b = code;
                                            SetRGB4(vp, active_color,(int)r,(int)g,(int)b);
                                            break;

                                        }

                                    break;

                case REFRESHWINDOW: GT_BeginRefresh(win);
                                    GT_EndRefresh(win, TRUE);
                                    break;

                } /* switch(class) */

            } /* while msg */

        } /* while rc == RC_GOON */

cleanup:
    if (menuattached) ClearMenuStrip(win);
    if (menu) FreeMenus(menu);
    if (win)            CloseWindow(win);
    if (gadgetlist)     FreeGadgets(gadgetlist);
    if (scr)            UnlockPubScreen(NULL, scr);
    if (vi)             FreeVisualInfo(vi);

    return(rc);
}

/*
 *  updatesliders(): set slider levels to r, g, b
 */
void updatesliders(struct Window *win, UBYTE r, UBYTE g, UBYTE b)
{
    GT_SetGadgetAttrs(gad_red, win, NULL, GTSL_Level, (ULONG)r, TAG_END);
    GT_SetGadgetAttrs(gad_green, win, NULL, GTSL_Level, (ULONG)g, TAG_END);
    GT_SetGadgetAttrs(gad_blue, win, NULL, GTSL_Level, (ULONG)b, TAG_END);
}

/*
 * maxtextlength(): determine the width of two or three strings in pixels
 *                  set t3 to NULL, to compare only two strings
 */
short maxtextlength(struct RastPort *rp, char *t1, char *t2, char *t3)
{
    /*
     *  TextLength() returns a WORD, so use shorts
     *  (I don't like these typedefs from types.h)
     */
    register short maxw = 0l;
    register short w;

    if (rp && t1 && t2) {

        /*
         *  get max(len(t1), len(t2))
         */
        maxw = TextLength(rp, (STRPTR)t1, strlen(t1));
        w    = TextLength(rp, (STRPTR)t2, strlen(t2));
        maxw = (maxw >= w) ? maxw : w;

        if (t3) {

            /*
             *  get max(max(len(t1), (t2)), len(t3))
             */
            w = TextLength(rp, (STRPTR)t3, strlen(t3));
            maxw = (maxw >= w) ? maxw : w;

            } /* if (t3) */

        } /* if (rp && t1 && t2) */

    return(maxw);
}

unsigned long createallgadgets(struct Gadget **glist, APTR vi, struct Screen *scr)
{
    static char msg_gad_red[]    = MSG_GAD_RED;
    static char msg_gad_green[]  = MSG_GAD_GREEN;
    static char msg_gad_blue[]   = MSG_GAD_BLUE;
    static char msg_gad_reset[]  = MSG_GAD_RESET;
    static char msg_gad_use[]    = MSG_GAD_USE;
    static char msg_gad_cancel[] = MSG_GAD_CANCEL;
    register struct Gadget *gad;
    struct RastPort *rp;
    struct NewGadget ng;
    unsigned long winwidth;
    short width;
    short button_dist;

    /*
     *  first get RastPort of screen to determine some TextLength()
     */
    rp = &scr->RastPort;

    /*
     *  determine maxtextlength(msg_gad_red, msg_gad_green, msg_gad_blue)
     */
    width = maxtextlength(rp, msg_gad_red + 1l, msg_gad_green + 1l, msg_gad_blue + 1l);

    /*
     * determine needed space left of Palette gadget / sliders
     *
     * first check if (width > INDICATOR_WIDTH)
     */
    width = (width >= INDICATOR_WIDTH) ? width : INDICATOR_WIDTH;

    /*
     *  now determine width of our window:
     *  PALETTE_WIDTH + 4 * INTERWIDTH + width + TextLength("00")
     */
    winwidth = PALETTE_WIDTH + TextLength(rp, (STRPTR)"00", 2l) +
               FOUR_INTERWIDTH + width;

    /*
     *  determine maximum width of the button gadgets
     *
     *  don't use width anymore as lvalue, needed below as button_width !!!
     */
    width = maxtextlength(rp, msg_gad_reset, msg_gad_use, msg_gad_cancel)
            + DBL_INTERWIDTH;

    /*
     *  get the distance between two button gadgets
     *
     *  button_dist = (winwidth - 3 * width) / 2
     */
    button_dist  = (winwidth - (width << 1) - width) >> 2;

    /*
     *  if (button_dist < INTERWIDTH) we have to widen our window:
     *
     *  winwidth = 4 * INTERWIDTH + 3 * width;
     */
    if (button_dist < INTERWIDTH) {

        button_dist = INTERWIDTH;
        winwidth = (INTERWIDTH << 2) + width + (width << 1);

        }

    /*
     *  now create the gadgets
     */
    gad = CreateContext(glist);

    /*
     *  Palette gadget
     */    
    ng.ng_TopEdge       = scr->WBorTop + rp->TxHeight + 1 + DBL_INTERHEIGHT;
    ng.ng_Width         = PALETTE_WIDTH;
    /*
     *  center the gadget in winwidth
     *  (width of gadget = PALETTE_WIDTH + INDICATOR_WIDTH !!!!)
     */
    ng.ng_LeftEdge      = (winwidth - PALETTE_WIDTH - INDICATOR_WIDTH) >> 1;
    ng.ng_Height        = PALETTE_HEIGHT;
    ng.ng_GadgetText    = NULL;
    ng.ng_TextAttr      = scr->Font;
    ng.ng_GadgetID      = GAD_PALETTE;
    ng.ng_Flags         = 0l;
    ng.ng_VisualInfo    = vi;

    gad_palette = gad = CreateGadget(PALETTE_KIND, gad, &ng,
                                     GTPA_Depth, (ULONG)rp->BitMap->Depth,
                                     GTPA_Color, 0l,
                                     GTPA_IndicatorWidth, INDICATOR_WIDTH,
                                     TAG_DONE);

    /*
     *  red slider
     */
    ng.ng_LeftEdge     += INDICATOR_WIDTH + INTERWIDTH;
    ng.ng_TopEdge      += PALETTE_HEIGHT + INTERHEIGHT;
    ng.ng_Height        = rp->TxHeight + 4;
    ng.ng_Width        -= (INDICATOR_WIDTH + INTERWIDTH);
    ng.ng_GadgetText    = (UBYTE *)msg_gad_red;
    ng.ng_GadgetID      = GAD_RED;
    ng.ng_Flags         = PLACETEXT_LEFT;

    gad_red = gad = CreateGadgetA(SLIDER_KIND, gad, &ng, slidertags);

    /*
     *  create green slider
     */
    ng.ng_TopEdge      += ng.ng_Height + INTERHEIGHT;
    ng.ng_GadgetText    = (UBYTE *)msg_gad_green;
    ng.ng_GadgetID      = GAD_GREEN;

    gad_green = gad = CreateGadgetA(SLIDER_KIND, gad, &ng, slidertags);

    /*
     *  create blue slider
     */
    ng.ng_TopEdge      += ng.ng_Height + INTERHEIGHT;
    ng.ng_GadgetText    = (UBYTE *)msg_gad_blue;
    ng.ng_GadgetID      = GAD_BLUE;

    gad_blue = gad = CreateGadgetA(SLIDER_KIND, gad, &ng, slidertags);

    /*
     *  create the three button gadgets
     *
     *  Reset-gadget:
     */
    ng.ng_LeftEdge      = button_dist;
    ng.ng_TopEdge      += ng.ng_Height + INTERHEIGHT;
    ng.ng_Width         = width;
    ng.ng_GadgetText    = (UBYTE *)msg_gad_reset;
    ng.ng_GadgetID      = GAD_RESET;
    ng.ng_Flags         = 0l;

    gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, (ULONG)'_', TAG_DONE);

    /*
     *  Use-gadget:
     */
    width += button_dist;       /* offset for next gadget */
    ng.ng_LeftEdge     += width;
    ng.ng_GadgetText    = (UBYTE *)msg_gad_use;
    ng.ng_GadgetID      = GAD_USE;

    gad_use = gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, (ULONG)'_', TAG_DONE);

    /*
     *  Cancel-gadget:
     */
    ng.ng_LeftEdge     += width;
    ng.ng_GadgetText    = (UBYTE *)msg_gad_cancel;
    ng.ng_GadgetID      = GAD_CANCEL;

    gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, (ULONG)'_', TAG_DONE);

    /*
     *  if gadgets couldn't be createt somehow, return winwidth = 0
     */
    if (!gad) winwidth = 0l;

    return(winwidth);
}
