(c)  Copyright 1989 Commodore-Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice, and 
is provided "as is" without warranty of any kind, either expressed or implied.  
The entire risk as to the use of this information is assumed by the user.




           Disabled Render - How to Supply an Alternate 
               Image for a Gadget Which Is Disabled

                        Adam Keith Levin   
                Commodore-Amiga Technical Support




Intuition allows boolean gadgets to be rendered in many different ways -
as plain text, as a simple line drawing or even as a custom bit mapped image.
Intuition also provides several ways to show when a boolean gadget has been
selected by the user.  Its rendering can be changed by complementing the 
colors, drawing a box around the gadget, replacing the border or image with 
a different border or image or by no change at all.

However, Intuition provides only one method for showing when a gadget is
disabled and that is by ghosting.  A gadget which is disabled is temporarily 
not available for use.  Intuition indicates this by ghosting the gadget's 
image.  Ghosting is accomplished by overlaying a fine grid of pixels onto 
the gadget.  This makes the image look "fuzzy".  When a disabled gadget is 
subsequently enabled, the ghosting is removed and the gadget is again 
available for use.

The program listed below shows an alternate method for indicating when a 
boolean gadget is disabled.  You can use this program to provide a whole new
custom image for the disabled gadget instead of using the ghosting feature
of Intuition.

The ghosting feature of Intuition is provided through two routines OffGadget
and OnGadget.  The program below provides two new routines, myOffGadget and
myOnGadget.  Use these carefully.  They are not meant to be replacements for 
OffGadget and OnGadget as they are not set up to handle more than one gadget 
at a time.  They also work only with boolean gadgets.

The routine myOffGadget will cause the given gadget to stop generating both
GADGETDOWN and GADGETUP events just as OffGadget will, but by a different
method.  The myOffGadget routine does not set the GADGDISABLED flag in the 
Flags field like OffGadget does.  This means that if your application is 
handling the disabling of multiple gadgets and you have used myOffGadget 
then you cannot check the status of the gadget by looking at the Flags field
as you normally would.

Instead, you can look at the UserData field of the gadget.  The myOffGadget 
routine must save a copy of the old GadgetRender and SelectRender pointers 
and 2 IDCMP flags since it changes these and must restore them later.  To
do this a pointer to a structure containing this information is placed in 
the gadget's UserData field.  So, if the UserData field is not NULL then 
myOffGadget has been called and the gadget is disabled - the UserData field 
contains a pointer to the information needed to re-enable the gadget.
If the UserData field is NULL, myOffGadget has not been called and the gadget
is enabled. 

The example below handles the disabled rendering of image gadgets, not text 
or border gadgets.  However, the method used is extensible to rendering these 
other forms as well.  The example also assumes that boolean gadgets of fixed
size are used as opposed to those which use GRELWIDTH, GRELHEIGHT, etc.

If the disabled rendering does not completely cover the original imagery,
some of the original gadget will still be visible.  The example code 
defines the disabled rendering as slightly larger than the gadget rendering, 
so this is not a problem.  For more details on how the program works, see 
the comments in the code.

Even though Intuition only provides one method for indicating to the user 
that a gadget is disabled, it is not difficult to extend Intuition to provide
an alternative method.  The DisabledRender program shows you how.


------------------- Lauren, code starts here -----------------------

/*

   DisabledRender

   A method for supplying a boolean gadget with alternate
   (ie non-ghosted) imagery when it is disabled.

   Adam Keith Levin   Commodore-Amiga Technical Support

   Created with Manx's Aztec C v3.6a
   For Manx, Compile with: cc DisabledRender.c +L
            and Link with: ln DisabledRender.o -lc32



    O V E R V I E W

   A window with three boolean gadgets is created.
      Two of the gadgets ("gadget1" and "gadget2") do nothing, but
      have both gadget and select imagery.  The third gadget ("control")
      alternately disables and enables the other two gadgets.

   Gadget1 is disabled and enabled normally, using OffGadget() and OnGadget().
   Gadget2 is disabled by
      1. Removing it from the gadget list.
      2. Setting its GadgetRender and SelectRender fields to point
         at a third "DisabledRender" Image structure.
      3. Clearing the GADGIMMEDIATE and RELVERIFY bits in its
         Activation flags field to prevent GADGETDOWN/GADGETUP events.
      4. Saving the displaced information from steps 2 and 3 in a
         special SavedGadgetData structure.
      5. Adding the gadget to the gadget list in its previous position.
      6. Calling RefreshGadgets() to display the "disabled" image.

   Gadget2 is enabled by
      1. Removing it from the gadget list.
      2. Restoring its previous GadgetRender and SelectRender fields.
      3. Restoring the previous GADGIMMEDIATE and RELVERIFY bits in its
         Activation flags field.  (The information for steps 2 and 3
         is retrieved from the SavedGadgetData structure).
      4. Adding it to the gadget list in its previous position.
      5. Calling RefreshGadgets() to display the gadget image.

   Messages announcing gadget events are written
      to the window via the console device.

*/


#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <stdio.h>

struct IntuitionBase *IntuitionBase;


/*  Image data for normal rendering of gadget1 and gadget2.  */
USHORT newGadgetRenderData[] =
{
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x0000,0x0000,0x0000,0x0000,0x0007,0xFF80,0x0007,0xFF80,
    0x0007,0xFF80,0x0007,0xFF80,0x0007,0xFF80,0x0007,0xFF80,
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
    0x0001,0xFE60,0x0001,0xFE60,0x0001,0xFE60,0x0001,0xFE60,
    0x0000,0x0060,0x0001,0xFFE0
};

/*  Image data for select rendering of gadget1 and gadget2.  */
USHORT newSelectRenderData[] =
{
    0xFFFF,0xFF80,0xFFFF,0xFF80,0xFFFF,0xFF80,0xFFFF,0xFF80,
    0xFFFF,0xFF80,0xFFFF,0xFF80,0xFFFF,0xFF80,0xFFFF,0xFF80,
    0xFFFF,0xFF80,0xFFFF,0xFF80,0xFFFF,0xFF80,0xFFFF,0xFF80,
    0x0000,0x0000,0x0000,0x0000,0x3FFF,0xFE60,0x3FFF,0xFE60,
    0x3FFF,0xFE60,0x3FFF,0xFE60,0x3FFF,0xFE60,0x3FFF,0xFE60,
    0x3FFF,0xFE60,0x3FFF,0xFE60,0x3FFF,0xFE60,0x3FFF,0xFE60,
    0x0000,0x0060,0x3FFF,0xFFE0
};

/*  Image data for disabled rendering of gadget2.  */
USHORT newDisabledRenderData[] =
{
    0x03FF,0x0000,0x1E01,0xE000,0x7380,0x3800,0xC0E0,0x0C00,
    0xC038,0x0C00,0xC00E,0x0C00,0x7003,0xB800,0x1E01,0xE000,
    0x03FF,0x0000,0x03FF,0x03FF,0x1E01,0xE3FF,0x7380,0x3BFF,
    0xC0E0,0x0FFF,0xC038,0x0FFF,0xC00E,0x0FFF,0x7003,0xBBFF,
    0x1E01,0xE3FF,0x03FF,0x03FF
};


/*
    Make the default RenderData the newRenderData arrays.
    If they are in chip RAM, they will be used as they are;
    if in fast RAM, they will be copied to chip RAM by chipImage().
*/
USHORT *gadgetRenderData = (USHORT *) &newGadgetRenderData,
          *selectRenderData = (USHORT *) &newSelectRenderData,
             *disabledRenderData = (USHORT *) &newDisabledRenderData;


/*  The ImageData fields will be filled by prepImage()
    once a chip RAM pointer is obtained from chipImage().
*/
struct Image gadgetRender = {0,0,27,13,2,NULL,0x0003,0x0000,NULL},
                selectRender = {0,0,27,13,2,NULL,0x0003,0x0000,NULL}, 
                   disabledRender = {5,4,22,9,2,NULL,0x0003,0x0000,NULL};


/*  Imagery for the control gadget.  */
SHORT vertices[] = { 0,0,75,0,75,12,0,12,0,0 };
struct Border border = { -1,-1,3,0,JAM1,5,vertices,NULL };
struct IntuiText intuiText = { 3,0,JAM2,8,2,NULL,(UBYTE *)"CONTROL",NULL };


/*  Constants for GadgetID.  */
#define GADGET1 0
#define GADGET2 1
#define CONTROL 2

struct Gadget control =
{
    NULL,
    129,38,
    74,11,
    NULL,
    RELVERIFY|TOGGLESELECT,
    BOOLGADGET,
    (APTR)&border,
    NULL,
    &intuiText,
    NULL,
    NULL,
    CONTROL,
    NULL
};

struct Gadget gadget2 =
{
    &control,
    178,18,
    27,13,
    GADGHIMAGE|GADGIMAGE,
    RELVERIFY,
    BOOLGADGET,
    (APTR)&gadgetRender,
    (APTR)&selectRender,
    NULL,
    NULL,
    NULL,
    GADGET2,
    NULL
};

struct Gadget gadget1 =
{
    &gadget2,
    128,18,
    27,13,
    GADGHIMAGE|GADGIMAGE,
    RELVERIFY,
    BOOLGADGET,
    (APTR)&gadgetRender,
    (APTR)&selectRender,
    NULL,
    NULL,
    NULL,
    GADGET1,
    NULL
};


/*  The return value from OpenDevice() is used to
    tell whether the console.device was opened or not.
*/
LONG consoleOpen = 1L;

struct IOStdReq *console;
struct MsgPort *msgPort;
struct Window *window;
struct NewWindow newWindow =
{
    148,47,
    340,105,
    0,1,
    GADGETUP|CLOSEWINDOW,
    WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|ACTIVATE|NOCAREREFRESH,
    NULL,  /*  The gadget list will be attached later.  */
    NULL,
    (UBYTE *)"DisabledRender",
    NULL,
    NULL,
    5,5,
    -1,-1,
    WBENCHSCREEN
};


/*  Anchor for AllocRemember.  */
struct Remember *remember = NULL;

/*  Console control sequence characters to set top offset
    of text and to turn window cursor off.
*/
char conPrep[] =
{
    0x9b, 0x35, 0x35, 0x79, 0x9b, 0x30, 0x20, 0x70, 0x00
};

/*  Indication of where this program started from.  */
BOOL fromCLI = FALSE;


main(argc, argv)
int argc;
char *argv[];
{
BOOL windowOpen=TRUE;    /*  FALSE once user has clicked close gadget.  */
ULONG class;    /*  IntuiMessage Class.  */
APTR iAddress;    /*  IntuiMessage IAddress.  */
struct IntuiMessage *intuiMessage;
BOOL prepImage();
void cPrint(), doGadget(), cleanExit();


    /*  If started from CLI, set global variable to TRUE.  */
    if (argc) fromCLI = TRUE;

    IntuitionBase =
        (struct IntuitionBase *) OpenLibrary("intuition.library", 0L);

    if (IntuitionBase == NULL)
        cleanExit("\nUnable to open \"intuition.library\".\n",
            ERROR_INVALID_RESIDENT_LIBRARY);

    /*  Ensure that gadget imagery is in chip RAM.  */
    if (prepImage() != TRUE)
        cleanExit("\nInsufficent chip RAM for gadget imagery.\n",
            ERROR_NO_FREE_STORE);

    window = (struct Window *) OpenWindow(&newWindow);
    if (window == NULL)
        cleanExit("\nUnable to open a window.\n", RETURN_FAIL);

    msgPort = (struct MsgPort *) CreatePort("DisabledRender", 0L);
    if (msgPort == NULL) 
        cleanExit("\nUnable to create a message port.\n", RETURN_FAIL);

    console = (struct IOStdReq *) CreateStdIO(msgPort);
    if (console == NULL)
        cleanExit("\nUnable to create a standard I/O requester.\n",
            RETURN_FAIL);

    console->io_Data = (APTR) window;
    console->io_Length = (LONG) sizeof(*window);

    consoleOpen = OpenDevice("console.device", 0L, console, 0L);
    if (consoleOpen != 0L)
        cleanExit("\nUnable to open \"console.device\".\n", RETURN_FAIL);

    /*  Send control sequences to set top offset and invisible cursor.  */
    cPrint(console, conPrep);

    /*  Print a welcome message.  */
    cPrint(console, "Gadget events are announced here.\n");

    /*  Add the gadget list and see that it is displayed.  */
    (void) AddGList(window, &gadget1, -1, -1, NULL);
    RefreshGadgets(&gadget1, window, NULL);

    /*  Intuition event processing loop.  */
    while (windowOpen)
    {
        (void) Wait(1<<window->UserPort->mp_SigBit);
        while (intuiMessage = (struct IntuiMessage *) GetMsg(window->UserPort))
        {
            class = intuiMessage->Class;
            iAddress = intuiMessage->IAddress;
            ReplyMsg(intuiMessage);

            switch (class)
            {
                case (CLOSEWINDOW):
                    windowOpen = FALSE;
                    cPrint(console, "\nCLOSEWINDOW.");
                    Delay(2L * TICKS_PER_SECOND);
                    break;
                case (GADGETUP):
                    cPrint(console, "\nGADGETUP:  ");
                    doGadget(window, ((struct Gadget *)iAddress)->GadgetID);
                    break;
                default:
                    break;
            } /*  end switch(class)  */
        } /*  end while(intuiMessage...  */
    } /*  end while(windowOpen)  */

    cleanExit(NULL, RETURN_OK);
}


/*  Return code from chipImage() to prepImage().  */
#define cI_ORIG    0    /*  Array was already in chip RAM.  */
#define cI_COPY    1    /*  Array was copied to chip RAM.  */
#define cI_FAIL    2    /*  Array could not be copied to chip RAM.  */

/*
    prepImage
    Ensures that the gadget imagery is in chip RAM.
*/
BOOL
prepImage()
{
    /*  Ensure that gadgetRenderData points to an array in chip RAM.  */
    if (chipImage(&gadgetRenderData, sizeof(newGadgetRenderData)) == cI_FAIL)
        return(FALSE);

    /*  Fill the ImageData field of the gadgetRender Image structure.  */
    gadgetRender.ImageData = gadgetRenderData;

    if (chipImage(&selectRenderData, sizeof(newSelectRenderData)) == cI_FAIL)
        return(FALSE);
    selectRender.ImageData = selectRenderData;

   if (chipImage(&disabledRenderData, sizeof(newDisabledRenderData)) == cI_FAIL)
        return(FALSE);
    disabledRender.ImageData = disabledRenderData;

    return(TRUE);
}


/*  chipImage
    Ensures that the array pointed to by imageData is in
    chip RAM.  It creates a chip RAM copy if necessary.
*/
int
chipImage(imageData, sizeOfImageData)
USHORT **imageData;
int sizeOfImageData;
{
USHORT *tmpImageData;

    /*  If it is already in CHIP RAM, return.  */
    if (TypeOfMem(*imageData) & MEMF_CHIP) return(cI_ORIG);

    /*  Otherwise create a copy in CHIP RAM and point imageData to it.  */
    else
    {
        tmpImageData = (USHORT *)
            AllocRemember(&remember, sizeOfImageData, MEMF_CHIP|MEMF_PUBLIC);

        if (tmpImageData != NULL)
        {
            CopyMem(*imageData, tmpImageData, sizeOfImageData);
            *imageData = tmpImageData;
            return(cI_COPY);
        }
    }
    return(cI_FAIL);
}


/*
    doGadget
    Handles the gadget events.  "Gadget1" and "gadget2" do nothing.
    "Control" alternately disables and enables gadget1 and gadget2.
*/
void
doGadget(window, gadgetID)
struct Window *window;
USHORT gadgetID;
{
static BOOL on=TRUE;
void cPrint(), myOffGadget(), myOnGadget();

    switch (gadgetID)
    {
        case (GADGET1):
            cPrint(console, "Left gadget.");
            break;
        case (GADGET2):
            cPrint(console, "Right gadget.");
            break;
        case (CONTROL):
            cPrint(console, "Control gadget.\nLeft and right gadgets are now ");
                if (on)
                {
                    /*  Normal method of disabling.  Image will be ghosted.  */ 
                    OffGadget(&gadget1, window, NULL);

                    /*  Alternate method of disabling.  "Disabled" image
                        will be displayed.
                    */
                    myOffGadget(&gadget2, window, NULL, &disabledRender);

                    on = FALSE;
                    cPrint(console, "disabled.");
                }
                else
                {
                    /*  Normal method of enabling.  */
                    OnGadget(&gadget1, window, NULL);

                    /*  Alternate method of enabling.  */
                    myOnGadget(&gadget2, window, NULL);

                    on = TRUE;
                    cPrint(console, "enabled.");
                }
                break;
            default:
                cPrint(console, "Unknown gadget.");
            break;
    }
}


/*  A mask to clear and restore the corresponding Activation flags.  */
#define GI_RV    (GADGIMMEDIATE|RELVERIFY)

/*  A structure to hold gadget information while the gadget is disabled.  */
struct SavedGadgetData
{
    USHORT Activation;
    APTR gadgetRender;
    APTR selectRender;
};

/*  Since there is only one gadget to be disabled with this example method,
    an instance of the SavedGadgetData structure is declared.
    If several gadgets will be disabled, it is advised that memory
    for each be allocated at runtime.
*/
struct SavedGadgetData savedGadgetData;


/*  myOffGadget
    Disables a boolean gadget by clearing the GADGIMMEDIATE and RELVERIFY
    Activation flags and swaping in a third "disabled" image for
    both the gadget and select images.  The removed information is saved 
    in a SavedGadgetData structure which is hung off of Gadget->UserID.
*/
void
myOffGadget(gadget, window, requester, image)
struct Gadget *gadget;
struct Window *window;
struct Requester *requester;
struct Image *image;
{
USHORT position;

    /*  Ensure that this gadget is not currently disabled by
        either the normal method or by the method in this example.
        This test should always fail, but the check is here nonetheless.
    */
    if ((gadget->Flags & GADGDISABLED) || (gadget->UserData != NULL))
        return;

    /*  Remove the gadget so changes may be made.  */
    position = RemoveGadget(window, gadget);

    /*  Save Gadget and Select renderings.  */
    savedGadgetData.gadgetRender = gadget->GadgetRender;
    savedGadgetData.selectRender = gadget->SelectRender;

    /*  Swap in disabledRender image.  */
    gadget->GadgetRender = gadget->SelectRender = image;

    /*  Save the current GI_RV state.  */
    savedGadgetData.Activation = gadget->Activation & GI_RV;

    /*  Keep gadget from causing IDCMP messages by clearing
        both the GADGIMMEDIATE and RELVERIFY Activation Flags.
    */
    gadget->Activation &= ~GI_RV;

    /*  Put the address of the savedGadgetData structure in the UserData
        field of the gadget, to be used when it is re-enabled.
    */
    gadget->UserData = (APTR) &savedGadgetData;

    (void) AddGadget(window, gadget, position);
    RefreshGadgets(gadget, window, requester);
}


/*  myOnGadget
    Enables a gadget by restoring the GADGIMMEDIATE and RELVERIFY
    Activation flags as well as the gadget and select images.
    The information is retrieved from the SavedGadgetData structure.
*/
void
myOnGadget(gadget, window, requester)
struct Gadget *gadget;
struct Window *window;
struct Requester *requester;
{
USHORT position;


    /*  Ensure that this gadget was disabled.  */
    if (gadget->UserData == NULL) return;

    position = RemoveGadget(window, gadget);

    /*  Restore the Gadget and Select rendering.  */
    gadget->GadgetRender = savedGadgetData.gadgetRender;
    gadget->SelectRender = savedGadgetData.selectRender;

    /*  Restore the gadget's ability to cause IDCMP messages
        by restoring the previous state of the
        GADGIMMEDIATE and RELVERIFY Activation Flags.
    */
    gadget->Activation |= savedGadgetData.Activation;

    /*  Clear the gadget's UserData field to indicate that it is enabled.  */
    gadget->UserData = NULL;

    (void) AddGadget(window, gadget, position);
    RefreshGadgets(gadget, window, requester);
}


/*
    cPrint
    Sends the given (NULL terminated) string to the given console window.
*/
void
cPrint(ioStdReq, string)
struct IOStdReq *ioStdReq;
char *string;
{
    if (*string)
    {
        ioStdReq->io_Data = (APTR) string;
        ioStdReq->io_Length = -1L;
        ioStdReq->io_Command = CMD_WRITE;

        (void) DoIO(ioStdReq);
    }
}


/*  cleanExit
    Prints an exit message.
    Replys to any pending console messages, closes the console.device,
        deletes the I/O request block and the message Port.
    Replys to any pending Intuition messages, closes the window.
    Frees any allocated memory.
    Closes the Intuition library.
    Exits with an exit value.
*/

void
cleanExit(exitString, exitValue)
char *exitString;
int exitValue;
{
struct FileHandle *fileHandle;
struct Message *message;
struct IntuiMessage *intuiMessage;

    if (*exitString)
    {
        fileHandle = (struct FileHandle *)
            Open("CON:138/100/358/50/DisabledRender Message", MODE_OLDFILE);
        if (fileHandle != NULL)
        {
            (void) Write(fileHandle, exitString, strlen(exitString));
            Delay(10L * TICKS_PER_SECOND);
            Close(fileHandle);
        }
        else
        {
            if (fromCLI)
                (void) fprintf(stderr, exitString);
       }
    }

    if (consoleOpen == 0) CloseDevice(console);

    if (console) DeleteStdIO(console);

    if (msgPort)
    {
        while (message = (struct Message *) GetMsg(msgPort))
            ReplyMsg(message);
        DeletePort(msgPort);
    }

    if (window)
    {
        while (intuiMessage = (struct IntuiMessage *) GetMsg(window->UserPort))
            ReplyMsg(intuiMessage);
        CloseWindow(window);
    }

    if (remember) FreeRemember(&remember, TRUE);

    if (IntuitionBase) CloseLibrary(IntuitionBase);

    exit(exitValue);
}



