Metropoli BBS
VIEWER: wbtools.c MODE: TEXT (ASCII)
/***************************************************************************
 *		  Copyright (C) 1994  Charles P. Peterson                  *
 *	     4007 Enchanted Sun, San Antonio, Texas 78244-1254             *
 *              Email: Charles_P_Peterson@fcircus.sat.tx.us                *
 *                                                                         *
 *		  This is free software with NO WARRANTY.                  *
 *	      See gfft.c, or run program itself, for details.              *
 *		      Support is available for a fee.                      *
 ***************************************************************************
 *
 * Program:     gfft--General FFT analysis
 * File:        wbtools.c
 * Purpose:     workbench GUI toolkit for GFFT
 * Author:      Charles Peterson (CPP)
 * History:     19-Nov-1993 CPP; Created.
 *              4-Aug-94 CPP (1.04); Filenames may contain spaces
 *              14-Dec-94 CPP (1.16); Fix message indicator for >4 colors
 *              14-Dec-94 CPP (1.17); Fix button gadgets for >4 colors
 *               5-Jan-95 CPP (1.18); Use system default font if possible
 *              17-Jan-95 CPP (1.20); Size shells nicely
 *              26-Jan-95 CPP (1.26); Allow largest possible font size
 *              27-Jan-95 CPP (1.27); Report window open failure
 *              28-Jan-95 CPP (1.29); Labeled radio buttons
 *              28-Jan-95 CPP (1.30); Tall action buttons
 *               6-Feb-95 CPP (1.31); Progress requester gadgets
 *               6-Feb-95 CPP (1.32); Render to window while requester on
 *              10-Feb-95 CPP (1.34); Thicker requester borders
 *
 * Comments:    Workbench GUI.  Amiga Dependent!
 *              There may be other toolkits.  Mine is intended to be
 *              compatible with all releases of AmigaDOS, be fairly
 *              simple, and have a 3d-like look.  I've had to make
 *              some small compromises:  non-toggle gadgets only
 *              have their borders change when selected (the only good way
 *              to get around this is to have multiple images...with about
 *              an order of magnitude increase in complexity...or by using
 *              BOOPSI, which is fairly complicated itself and 2.0+
 *              dependent, or use MUI, which would not only necessitate
 *              a complete re-write but also would introduce some copyright
 *              inconsistencies.  So, my rationalization anyway is that
 *              there is no _perfect_ choice, and the only better choices
 *              would take much more of my time to implement.  I haven't
 *              made enough money on it so far to justify that.
 */

#ifdef AMIGA
#define GUI_DEBUG

#include <stdio.h>     /* sprintf() */
#include <string.h>
/*
 * Amiga includes
 */
#include <exec/types.h>
#include <exec/exec.h>
#include <workbench/workbench.h>
#include <workbench/icon.h>
#include <workbench/startup.h>
#include <intuition/intuition.h>
#include <intuition/sghooks.h>
#include <clib/intuition_protos.h>
#include <clib/exec_protos.h>
#include <proto/icon.h>
#include <proto/dos.h>
#include <dos/dos.h>
#include <libraries/asl.h>
#include <clib/asl_protos.h>
#include <graphics/text.h>
#include <clib/graphics_protos.h>
#include <graphics/gfxmacros.h>  /* BNDRYOFF */
#include <proto/graphics.h>    /* GfxBase */
#include <graphics/gfxbase.h>

#include "gfft.h"
#include "wbench.h"
#include "settings.h"

#define USE_MAX_ROWS_FOR_FONT_CHECK

/*
 * Dialogue Window parameters...pretty good guesses for 2.0 and 1.3
 * reset later (some require drawinfo to be available for reset)
 */
#define SHINE_PEN_20 2
#define SHADOW_PEN_20 1
#define SHINE_PEN_13 1
#define SHADOW_PEN_13 2
#define TEXT_PEN 1         /* Seems to work well under both 2.0 and 1.3 */
#define FILL_TEXT_PEN 2    /* ditto; hmmn, I like better than 2.0 default */
#define FILL_PEN 3
#define BACKGROUND_PEN 0   /* almost guaranteed now, but may be updated */

#define DEFAULT_SCREEN_DEPTH 2
#define DEFAULT_SCREEN_FONT_HEIGHT 8
#define DEFAULT_FONT_HEIGHT 8
#define DEFAULT_FONT_WIDTH 8

#define FONT_COLUMNS_REQUIRED 80
#define FONT_ROWS_REQUIRED 24

#define DEFAULT_SHELL_WIDTH 640
#define DEFAULT_SHELL_HEIGHT 200


#define WINDOW_LEFT 0         /* Same as right edge */
#define WINDOW_TOP 0          /* Allow for WB title & click area above */

#define FIRST_WINDOW_BUTTON_LEFT 12
#define FIRST_WINDOW_BUTTON_TOP 6

#define FIRST_REQUESTER_BUTTON_LEFT 12
#define FIRST_REQUESTER_BUTTON_TOP 6

#define BUTTON_SIDE_EDGE (Font_Width)

#define BUTTON_TOP_EDGE 3    /* Inside button to text; same as bottom edge*/
#define BUTTON_TOP_SPACE 2   /* Vertical space between buttons */

/*
 * Use the following when possible
 */

#define BUTTON_WIDTH(WIDTH) ((WIDTH) * Font_Width + 2 * BUTTON_SIDE_EDGE)

#define BUTTON_HEIGHT (Font_Height + 2 * BUTTON_TOP_EDGE)
#define BUTTON_SPACING (BUTTON_HEIGHT + BUTTON_TOP_SPACE)
#define BUTTON_TOP(row) (first_button_top + row * BUTTON_SPACING)

#define WINDOW_HEIGHT(max_rows) (BUTTON_TOP(max_rows) + \
  BUTTON_TOP_SPACE)  /* for the bottom edge */

struct TextAttr FontA = {
"topaz.font",
DEFAULT_FONT_HEIGHT,
NULL,
NULL
};

struct TextAttr *FontAp = &FontA;

struct TextFont *fontp = NULL; /* Set if system default font used */

static short shine_pen = SHINE_PEN_20;
static short shadow_pen = SHADOW_PEN_20;
static short text_pen = TEXT_PEN;
static short fill_pen = FILL_PEN;
static short fill_text_pen = FILL_TEXT_PEN;
static short background_pen = BACKGROUND_PEN;

short Screen_Font_Height = DEFAULT_SCREEN_FONT_HEIGHT;
short Font_Height = DEFAULT_FONT_HEIGHT;
short Font_Width = DEFAULT_FONT_WIDTH;
static short screen_depth = DEFAULT_SCREEN_DEPTH;

static short first_button_left = FIRST_WINDOW_BUTTON_LEFT;
static short first_button_top = FIRST_WINDOW_BUTTON_TOP + 
                                DEFAULT_FONT_HEIGHT;
static short max_rows = 0;
static int gadget_type_mode = NULL;
static struct IntuiText *next_textp = NULL;
static struct Gadget *next_gadgetp = NULL;
static struct Requester *current_requesterp = NULL;

#define REQUESTER_STACK_DEPTH 10
static struct Requester *requester_stack[REQUESTER_STACK_DEPTH] =
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static int requester_stack_index = 0;


struct Library *IntuitionBase = NULL;
BOOLEAN Old_Intuition = FALSE;
short Max_Columns = 0;

int Shell_Width = DEFAULT_SHELL_WIDTH;
int Shell_Height = DEFAULT_SHELL_HEIGHT;


extern struct Library *AslBase;   /* from filereq.c */
extern struct FileRequester *File_Requesterp;


static struct Gadget *text_button__new (char *text, short tcol, short trow);

void open_libraries (void)
{
/*
 * Intuition is currently the only library for which we need to know
 * version number, hence, we have to open it explicitly.
 */
    if (!IntuitionBase)
    {
	IntuitionBase = OpenLibrary ("intuition.library", 37);
	if (IntuitionBase == NULL) 
	{
	    Old_Intuition = TRUE;
	    IntuitionBase = OpenLibrary ("intuition.library", 0);
	    if (IntuitionBase == NULL)
	    {
		error_message (CANT_OPEN_INTUITION);
		gabort (EXIT_FAILURE);
	    }
	}
    }
}

void setup_window_defaults (int max_rows)
{
    struct Screen *screen;
    struct DrawInfo *draw_info;

    if (Old_Intuition)
    {
	shine_pen = SHINE_PEN_13;  /* These usually work for 1.3... */
	shadow_pen = SHADOW_PEN_13;  /* Should do this better someday */
	/* everything else is defaulted */
    }
    else  /* Ok, Rev 37 or greater, get pens and fonts */
    {
	if (screen = LockPubScreen (NULL))
	{
	    if (draw_info = GetScreenDrawInfo (screen))
	    {
		/* vars not used outside this block */
		char *system_default_font_name;
		short system_default_font_height;
		short system_default_font_width;
		short screen_width;
		short screen_height;
		short save_font_height;
		
		shine_pen = draw_info->dri_Pens[SHINEPEN];
		shadow_pen = draw_info->dri_Pens[SHADOWPEN];
		text_pen = draw_info->dri_Pens[TEXTPEN];
		fill_pen = draw_info->dri_Pens[FILLPEN];
		fill_text_pen = draw_info->dri_Pens[FILLTEXTPEN];
		background_pen = draw_info->dri_Pens[BACKGROUNDPEN];
		screen_depth = draw_info->dri_Depth;
		screen_width = screen->Width;
		screen_height = screen->Height;
		Screen_Font_Height = 
		  draw_info->dri_Font->tf_YSize;
		first_button_top = FIRST_WINDOW_BUTTON_TOP + 
		  Screen_Font_Height;
		/*
		 * Get system default font (always monospaced) info...
		 * (Maybe if I make some money on this, I'll rewrite to
		 * use the screen font (which might be proportional).
		 * Test to see that font permits max_rows and
		 * FONT_COLUMNS_REQUIRED
		 * before using (otherwise, we're defaulting to TOPAZ 8).
		 */
		system_default_font_name =
		  GfxBase->DefaultFont->tf_Message.mn_Node.ln_Name;
		system_default_font_height =
		  GfxBase->DefaultFont->tf_YSize;
		system_default_font_width =
		  GfxBase->DefaultFont->tf_XSize;
		
		save_font_height = Font_Height;
		Font_Height = system_default_font_height;
		
		if (!Topaz && 

#ifndef FORCE_FONT
#ifndef USE_MAX_ROWS_FOR_FONT_CHECK
		    (screen_height  / system_default_font_height >=
		     FONT_ROWS_REQUIRED) &&
#else
		    (WINDOW_HEIGHT(max_rows) <= screen_height) &&
#endif
#endif
		    (screen_width / system_default_font_width >=
		     FONT_COLUMNS_REQUIRED))
		{
		    FontA.ta_Name = system_default_font_name;
		    FontA.ta_YSize = system_default_font_height;
		    
		    Font_Height = system_default_font_height;
		    Font_Width = system_default_font_width;
		    
		    fontp = GfxBase->DefaultFont;
		    
/* Looks nicer without resetting shell height, upped later if needed  */
		    
		    Shell_Width = FONT_COLUMNS_REQUIRED * 
		      system_default_font_width;
		}
		else 
		{
		    Font_Height = save_font_height;
		    
		    Shell_Height = FONT_ROWS_REQUIRED *
		      system_default_font_height;
		    
		    Shell_Width = FONT_COLUMNS_REQUIRED * 
		      system_default_font_width;
		    
		    if (Shell_Height > screen_height)
		      Shell_Height = screen_height;
		    
		    if (Shell_Width > screen_width)
		      Shell_Width = screen_width;
		}
		FreeScreenDrawInfo (screen, draw_info);
	    }
	    UnlockPubScreen (NULL, screen);
	}
    }
}

void close_amiga_stuff (void)
{
    if (IntuitionBase)
    {
	CloseLibrary (IntuitionBase);
	IntuitionBase = NULL;
    }
    if (File_Requesterp)
    {
	FreeAslRequest(File_Requesterp);
	File_Requesterp = NULL;
    }
    if (AslBase)
    {
        CloseLibrary(AslBase);
	AslBase = NULL;
    }
}


void gadget__begin (int gadget_type_flag)
{
    gadget_type_mode = gadget_type_flag;
    Max_Columns = 0;
    max_rows = 0;
    next_textp = NULL;
    next_gadgetp = NULL;

    if (gadget_type_mode == GTYP_REQGADGET)
    {
	first_button_left = FIRST_REQUESTER_BUTTON_LEFT;
	first_button_top = FIRST_REQUESTER_BUTTON_TOP;
    }
    else
    {
	first_button_left = FIRST_WINDOW_BUTTON_LEFT;
	first_button_top = FIRST_WINDOW_BUTTON_TOP + Screen_Font_Height;
    }
}

struct Border *requester_border__new (short height, short width,
			    short top_pen, short bottom_pen)
{
    struct Border *rborder;
    struct Border *nborder;

    rborder = border__new (height-1, width-1, top_pen, bottom_pen);

    nborder = rborder->NextBorder->NextBorder = border__new 
      (height-3, width-3, top_pen, bottom_pen);

    nborder->LeftEdge += 1;
    nborder->TopEdge += 1;
    nborder->NextBorder->LeftEdge += 1;
    nborder->NextBorder->TopEdge += 1;

    nborder = nborder->NextBorder->NextBorder = border__new 
      (height-5, width-5, top_pen, bottom_pen);

    nborder->LeftEdge += 2;
    nborder->TopEdge += 2;
    nborder->NextBorder->LeftEdge += 2;
    nborder->NextBorder->TopEdge += 2;

    return rborder;
}


struct Border *button_border__new (short width, short top_pen,
				   short bottom_pen)
{

    struct Border *bborder;

    bborder = border__new (BUTTON_HEIGHT-1, width-1, top_pen, bottom_pen);
    return bborder;
}


struct Border *border__new (short height, short width, short top_pen,
			    short bottom_pen)
{
    short *xy_pairs_u, *xy_pairs_l;
    struct Border *border_u, *border_l;

    xy_pairs_u = gcalloc (6, sizeof (short), NOTHING_SPECIAL);
    xy_pairs_l = gcalloc (6, sizeof (short), NOTHING_SPECIAL);
    border_u = gcalloc (1, sizeof (struct Border), NOTHING_SPECIAL);
    border_l = gcalloc (1, sizeof (struct Border), NOTHING_SPECIAL);

    xy_pairs_u[0] = 0;
    xy_pairs_u[1] = height;
    xy_pairs_u[2] = 0;
    xy_pairs_u[3] = 0;
    xy_pairs_u[4] = width;
    xy_pairs_u[5] = 0;

    xy_pairs_l[0] = 1;        /* Looks a little nicer, I think, than 0 */
    xy_pairs_l[1] = height;
    xy_pairs_l[2] = width;
    xy_pairs_l[3] = height;
    xy_pairs_l[4] = width;
    xy_pairs_l[5] = 0;

    border_u->LeftEdge = 0;
    border_u->TopEdge = 0;
    border_u->XY = xy_pairs_u;
    border_u->FrontPen = top_pen;
    border_u->DrawMode = JAM1;
    border_u->NextBorder = border_l;
    border_u->Count = 3;

    border_l->LeftEdge = 0;
    border_l->TopEdge = 0;
    border_l->XY = xy_pairs_l;
    border_l->FrontPen = bottom_pen;
    border_l->DrawMode = JAM1;
    border_l->NextBorder = NULL;
    border_l->Count = 3;

    return border_u;
}
    

struct Gadget *progress_gadget__new (short length, short tcol, short trow)
{
    struct Gadget *gadgetp;

    gadgetp = message_gadget__new (length, tcol, trow);
    gadgetp->UserData = NULL;

    return gadgetp;
}

void progress_gadget__clear (struct Gadget *gadgetp, struct Window *windowp)
{
    gadgetp->UserData = NULL;
    if (gadgetp->GadgetText)
      message_gadget__write (gadgetp, NullString, windowp);
}

void progress_gadget__update (struct Gadget *gadgetp,
			      int percentage,
			      struct Window *windowp)
{
    struct RastPort *rport;
    UBYTE save_apen;
    struct Requester *requesterp;
    int last_percentage = (int) gadgetp->UserData;

    if (percentage == -1 || last_percentage == -1)
    {
	if (last_percentage == -1) return;
	message_gadget__write (gadgetp, "Unable to measure progress...",
			       windowp);
	gadgetp->UserData = (APTR) -1;
	return;
    }

    if (last_percentage >= percentage) return;
    if (percentage > 100 ) percentage = 100;
    gadgetp->UserData = (APTR) percentage;

    if (windowp)
    {
	short x1, y1, x2, y2;

	x1 = gadgetp->LeftEdge + 3;
	y1 = gadgetp->TopEdge + 2;

	x2 = x1 + (gadgetp->Width - 7) * (percentage/100.0);
	y2 = y1 + gadgetp->Height - 5;

	RemoveGadget (windowp, gadgetp);

	requesterp = 
	  (gadgetp->GadgetType & GTYP_REQGADGET) ?
	    current_requesterp :
	      NULL;

	rport = (requesterp) ? 
	  requesterp->ReqLayer->rp :
	    windowp->RPort;

	save_apen = rport->FgPen;

	SetAPen (rport, (UBYTE) fill_pen);

	RectFill (rport, x1, y1, x2, y2);

	SetAPen (rport, (UBYTE) save_apen);

	AddGList (windowp, gadgetp, -1, 1, requesterp);
	refresh_gadget (gadgetp, windowp);
    }
}


struct Gadget *message_gadget__new (short length, short tcol, short trow)
{
    struct Gadget *mgadgetp;

    mgadgetp = gadget__new (length, tcol, trow);
    mgadgetp->Flags = GFLG_GADGHNONE;
    mgadgetp->Activation = NULL;
    mgadgetp->GadgetRender = button_border__new (BUTTON_WIDTH (length),
						 shadow_pen, shine_pen);
    mgadgetp->GadgetText = NULL;
    return mgadgetp;
}

struct Gadget *labeled_radio_button__new 
  (char *label, char *text, short tcol, short trow)
{
    short button_offset = strlen (label) + 2;
    struct Gadget *gadgetp = radio_button__new (text, tcol + button_offset, 
						trow);
    gadgetp->GadgetText->NextText = button_text__new (label);
    gadgetp->GadgetText->NextText->LeftEdge -= BUTTON_SIDE_EDGE*2 + 
      strlen (label) * Font_Width;

    return gadgetp;
}

struct Gadget *radio_button__new (char *text, short tcol, short trow)
{
    struct Gadget *gadgetp;

    gadgetp = text_button__new (text, tcol, trow);

    gadgetp->SelectRender = button_border__new (BUTTON_WIDTH(strlen (text)),
                                                shadow_pen, shine_pen);
    gadgetp->Flags = GFLG_GADGHIMAGE;
    gadgetp->Activation &= ~GACT_RELVERIFY;
    gadgetp->Activation &= ~TOGGLESELECT;
    gadgetp->Activation |= GADGIMMEDIATE;
    return gadgetp;
}

struct Gadget *tall_action_button__new (char *text, short tcol, short trow)
{
    struct Gadget *gadgetp;
    struct Border *borderp;

    gadgetp = action_button__new (text, tcol, trow);

/*
 * Add extra borders to make button appear taller
 */
    if (screen_depth > 1)
    {
	borderp = (struct Border *) gadgetp->GadgetRender;
	borderp->NextBorder->NextBorder = button_border__new 
	  (BUTTON_WIDTH(strlen (text)), shine_pen, 
	   shadow_pen);
	
	borderp = borderp->NextBorder->NextBorder;
	borderp->XY[1] += 1;
#if FALSE
	borderp->XY[0] += 1;
	borderp->XY[2] += 1;
	borderp->XY[3] += 1;
	borderp->XY[4] += -1;
	borderp->XY[5] += 1;
#endif	

	borderp = borderp->NextBorder;
	borderp->XY[0] += 0;
	borderp->XY[1] += 1;
	borderp->XY[2] += 1;
	borderp->XY[3] += 1;
	borderp->XY[4] += 1;
	borderp->XY[5] += 1;
	
	borderp = (struct Border *) gadgetp->SelectRender;
	borderp->NextBorder->NextBorder = button_border__new 
	  (BUTTON_WIDTH(strlen (text)), shadow_pen, 
	   shine_pen);
	
	borderp = borderp->NextBorder->NextBorder;
	borderp->XY[1] += 1;
#if FALSE
	borderp->XY[0] += 1;
	borderp->XY[2] += 1;
	borderp->XY[3] += 1;
	borderp->XY[4] += -1;
	borderp->XY[5] += 1;
#endif
	
	borderp = borderp->NextBorder;
	borderp->XY[0] += 0;
	borderp->XY[1] += 1;
	borderp->XY[2] += 1;
	borderp->XY[3] += 1;
	borderp->XY[4] += 1;
	borderp->XY[5] += 1;

    }
    return gadgetp;
}
    


struct Gadget *action_button__new (char *text, short tcol, short trow)
{
    struct Gadget *gadgetp;

    gadgetp = text_button__new (text, tcol, trow);
    if (screen_depth > 1)
    {
	gadgetp->SelectRender = button_border__new 
	  (BUTTON_WIDTH(strlen (text)), shadow_pen, shine_pen);
	gadgetp->Flags = GFLG_GADGHIMAGE;
    }
    return gadgetp;
}

static struct Gadget *text_button__new (char *text, short tcol, short trow)
{
    struct Gadget *gadgetp;

    gadgetp = gadget__new (strlen (text), tcol, trow);
    gadgetp->GadgetText = button_text__new (text);
    return gadgetp;
}

struct Gadget *gadget__new (short length, short tcol, short trow)
{
    struct Gadget *gadgetp;
    short local_max_column = length + tcol;

    if (local_max_column > Max_Columns) Max_Columns = local_max_column;
    if (trow + 1 > max_rows) max_rows = trow + 1;

    gadgetp = gcalloc (1, sizeof (struct Gadget), NOTHING_SPECIAL);

    gadgetp->NextGadget = next_gadgetp;
    next_gadgetp = gadgetp;

    gadgetp->LeftEdge = first_button_left + tcol * Font_Width;
    gadgetp->TopEdge = first_button_top + trow * BUTTON_SPACING;
    gadgetp->Width = BUTTON_WIDTH (length);
    gadgetp->Height = BUTTON_HEIGHT;
    gadgetp->Activation = GACT_RELVERIFY;
    gadgetp->GadgetType = GTYP_BOOLGADGET | gadget_type_mode;
    gadgetp->GadgetRender = button_border__new (BUTTON_WIDTH (length),
						shine_pen, shadow_pen);
    gadgetp->SelectRender = NULL;
    gadgetp->Flags = GFLG_GADGHCOMP;
    gadgetp->GadgetText = NULL;
    gadgetp->MutualExclude = NULL;
    gadgetp->SpecialInfo = NULL;
    gadgetp->GadgetID = NULL;
    gadgetp->UserData = NULL;
    return gadgetp;
}


struct StringInfo *string_info__new (short max_chars, short vis_chars)
{
    struct StringInfo *string_info;

    string_info = gcalloc (1, sizeof (struct StringInfo), NOTHING_SPECIAL);

    string_info->Buffer = gcalloc (1, max_chars+1, NOTHING_SPECIAL);
    string_info->MaxChars = max_chars+1;
    string_info->UndoBuffer = NULL;
    string_info->BufferPos = 0;
    string_info->DispPos = 0;
    string_info->CTop = BUTTON_TOP_EDGE;
    string_info->CLeft = BUTTON_SIDE_EDGE;
    string_info->DispCount = vis_chars;


    if (fontp)
    {
	struct StringExtend *string_extendp = 
	  gcalloc (1, sizeof (struct StringExtend), NOTHING_SPECIAL);

	string_extendp->Font = fontp;
	string_extendp->Pens[0] = text_pen;
	string_extendp->Pens[1] = background_pen;
	string_extendp->ActivePens[0] = text_pen;
	string_extendp->ActivePens[1] = background_pen;

	string_info->Extension = string_extendp;
	
    }

    return string_info;
}


struct Gadget *string_gadget__new (char *prompt, char *initial_name,
					  short max_chars, 
					  short length, short tcol, 
					  short trow)
{
    struct Gadget *string_gadgetp;
    struct Border *borderp;
    struct Border *bottomp;
    short *bottom_xy;
    short right_text_offset, right_border_offset;
    short container_length, container_tcol;
    
/*
 * (Damn! Intuition doesn't allow user offset for string gadget container
 *  offsets within overall gadget area, so must do it manually.)
 */
    container_length = length - (strlen (prompt) + 2);
    container_tcol = tcol + (strlen (prompt) + 2);

    string_gadgetp = gadget__new (container_length, container_tcol, trow);

    string_gadgetp->GadgetType = GTYP_STRGADGET | gadget_type_mode;
    string_gadgetp->Activation |= GADGIMMEDIATE;

    if (fontp) string_gadgetp->Flags |= GFLG_STRINGEXTEND;

    string_gadgetp->SpecialInfo = string_info__new (max_chars, length-4);

    string_gadgetp->GadgetText = button_text__new (prompt);

    right_border_offset = BUTTON_SIDE_EDGE;
    right_text_offset = BUTTON_SIDE_EDGE*2 + strlen (prompt) * 
      Font_Width + right_border_offset;

    string_gadgetp->GadgetText->LeftEdge -= right_text_offset;

    string_gadgetp->LeftEdge += right_border_offset;
    borderp = (struct Border *) string_gadgetp->GadgetRender;
    borderp->XY[0] -= right_border_offset;
    borderp->XY[2] -= right_border_offset;
    borderp->XY[4] -= right_border_offset;
    borderp = borderp->NextBorder;
    borderp->XY[0] -= right_border_offset;
    borderp->XY[2] -= right_border_offset;
    borderp->XY[4] -= right_border_offset;
    string_gadgetp->Width -= right_border_offset + BUTTON_SIDE_EDGE;

    string_gadgetp->TopEdge += BUTTON_TOP_EDGE;
    string_gadgetp->GadgetText->TopEdge -= BUTTON_TOP_EDGE;
    borderp = (struct Border *) string_gadgetp->GadgetRender;
    borderp->XY[1] -= BUTTON_TOP_EDGE;
    borderp->XY[3] -= BUTTON_TOP_EDGE;
    borderp->XY[5] -= BUTTON_TOP_EDGE;
    borderp = borderp->NextBorder;
    borderp->XY[1] -= BUTTON_TOP_EDGE;
    borderp->XY[3] -= BUTTON_TOP_EDGE;
    borderp->XY[5] -= BUTTON_TOP_EDGE;
    string_gadgetp->Height -= BUTTON_TOP_EDGE * 2;
/*
 * Add "bottom line"
 */
    bottomp = gcalloc (1, sizeof (struct Border), NOTHING_SPECIAL);
    bottom_xy = gcalloc (4, sizeof (short), NOTHING_SPECIAL);

    bottom_xy[0] = 0;
    bottom_xy[1] = Font_Height;
    bottom_xy[2] = container_length * Font_Width;
    bottom_xy[3] = Font_Height;
    
    bottomp->LeftEdge = 0;
    bottomp->TopEdge = 0;
    bottomp->XY = bottom_xy;
    bottomp->FrontPen = shadow_pen;
    bottomp->DrawMode = JAM1;
    bottomp->NextBorder = NULL;
    bottomp->Count = 2;

    borderp->NextBorder = bottomp;

    if (initial_name)
    {
	struct StringInfo *string_info = string_gadgetp->SpecialInfo;
	strcpy (string_info->Buffer, initial_name);
    }

    return string_gadgetp;
}


struct IntuiText *requester_text__new (char *text, short tcol, short trow)
{
    struct IntuiText *textst;
    short local_max_column = tcol + strlen (text);

    if (local_max_column > Max_Columns) Max_Columns = local_max_column;
    if (trow + 1 > max_rows) max_rows = trow + 1;

    textst = button_text__new (text);
    textst->LeftEdge = first_button_left + BUTTON_SIDE_EDGE + 
      tcol * Font_Width;
    textst->TopEdge = BUTTON_TOP_EDGE + BUTTON_TOP(trow);
    textst->NextText = next_textp;
    next_textp = textst;

    return textst;
}

struct IntuiText *button_text__new (char *text)
{
    struct IntuiText *textst;

    textst = gcalloc (1, sizeof (struct IntuiText), NOTHING_SPECIAL);

    textst->FrontPen = text_pen;
    textst->BackPen = background_pen;
    textst->DrawMode = JAM1;
    textst->LeftEdge = BUTTON_SIDE_EDGE;
    textst->TopEdge = BUTTON_TOP_EDGE;
    textst->ITextFont = FontAp;
/*  textst->ITextFont = NULL; This gives screen font, may be prop font */
    textst->IText = text;
    textst->NextText = NULL;
    return textst;
}

void refresh_gadget (struct Gadget *gadgetp, struct Window *windowp)
{
    struct Requester *requesterp = 
      (gadgetp->GadgetType & GTYP_REQGADGET) ?
	current_requesterp :
	  NULL;

    RefreshGList (gadgetp, windowp, requesterp, 1);
}

void toggle_button__toggle (struct Gadget *gadgetp, 
		     char *(*on_function)(char *arg),
		     char *(*off_function)(char *arg),
		     struct Window *windowp)
{
    if (!(gadgetp->Flags & GFLG_SELECTED))
    {
	(*off_function) (NullString);
    }
    else
    {
	(*on_function) (NullString);
    }
}

void radio_button__toggle (struct Gadget *gadgetp, 
		     char *(*on_function)(char *arg),
		     char *(*off_function)(char *arg),
		     struct Window *windowp)
{
/*
 * Must do imagery here for radio buttons
 */
    if (gadgetp->Flags & GFLG_SELECTED)
    {
	(*off_function) (NullString);
	radio_button__out (gadgetp, windowp);
    }
    else
    {
	(*on_function) (NullString);
	radio_button__in (gadgetp, windowp);
    }
}

char *string_gadget__apply (struct Gadget *string_gadgetp,
			    char *(*apply_function)(char *arg))
{
    struct StringInfo *string_infop = 
      string_gadgetp->SpecialInfo;
    char *buffer = string_infop->Buffer;
    buffer[string_infop->NumChars] = '\0';
    (*apply_function) (buffer);
    return buffer;
}

/*
 * (1.04) Names in requesters may include spaces, so
 * must be quoted
 */
char *name_string_gadget__apply (struct Gadget *string_gadgetp,
				 char *(*apply_function)(char *arg))
{
    char *enquote_buffer;
    struct StringInfo *string_infop = 
      string_gadgetp->SpecialInfo;
    char *buffer = string_infop->Buffer;
    buffer[string_infop->NumChars] = '\0';

    if (!string_infop->NumChars)
    {
	(*apply_function) (buffer);  /* Null String; don't enquote */
    }
    else
    {
	enquote_buffer = gmalloc (string_infop->NumChars + 8, 
				  NOTHING_SPECIAL);
	sprintf (enquote_buffer, "\"%s\"", buffer);
	(*apply_function) (enquote_buffer);
	gfree (enquote_buffer);
    }
    return buffer;
}

void string_gadget__reset (struct Gadget *string_gadgetp,
			    char *new_string,
			    struct Window *windowp)
{
    struct StringInfo *string_infop = 
      string_gadgetp->SpecialInfo;
    char *buffer = string_infop->Buffer;
    int string_length = 0;
    if (!new_string || (string_length = strlen (new_string)) <= 0)
    {
	buffer[0] = '\0';
    }
    else
    {
	strcpy (buffer, new_string);
    }
    string_infop->NumChars = string_length;
    refresh_gadget (string_gadgetp, windowp);
}


void message_requester (struct Window *windowp, char *message_text)
{
    extern char *Got_It_S;
    struct IntuiText *mtextp = button_text__new (message_text);
    struct IntuiText *got_it_textp = button_text__new (Got_It_S);

    if (Old_Intuition)
    {
	mtextp->DrawMode = JAM2 | INVERSVID;
	got_it_textp->DrawMode = JAM2 | INVERSVID;
    }

    AutoRequest (windowp, mtextp, NULL, got_it_textp,
		     IDCMP_GADGETDOWN,
		     IDCMP_GADGETDOWN, 640, 50);

    gfree (mtextp);
    gfree (got_it_textp);
}

/*
 * I have decided NOT to follow the guidelines for 'mutually exclusive'
 * gadgets on page 140 of the Amiga Rom Kernel Reference Manual, Third
 * Edition, which specifically advise against this combination (mutually
 * exclusive gadgets using 'complementing'--though I don't really use
 * 'complementing' here anymore--I use explicit colors to be compatible with
 * larger numbers of colors).
 *
 * However, I have chosen instead to follow the guidelines for 'Updating
 * a Gadget's Imagery' on pages 129-130.  Before a radio_button is
 * refreshed in either selected or unselected state, the background
 * behind it is restored ("cleared") with RectFill.  I do this very
 * carefully as you will see.
 *
 * This concept has been extended to cover other features, such as
 * gadget disable and enable, which I couldn't get to work before.
 *
 * This should be acceptable and stable with future released of Intuition.
 * At least, there is some hope.  And it works with 1.2-2.1 for sure.
 *
 * It now even handles requester gadgets properly.
 */

void radio_button__in (struct Gadget *gadgetp, struct Window *windowp)
{
    struct RastPort *rport;
    short x1, y1, x2, y2;
    UBYTE save_apen;  /* I preserve this one, dunno if necessary */
    struct Requester *requesterp = 
      (gadgetp->GadgetType & GTYP_REQGADGET) ?
	current_requesterp :
	  NULL;

    if (windowp)
    {
/*	if (!(gadgetp->Flags & GFLG_SELECTED)) /* pushing my luck too far*/
	{
	    RemoveGadget (windowp, gadgetp); 
	    gadgetp->Flags |= GFLG_SELECTED;
	    gadgetp->GadgetText->FrontPen = fill_text_pen;
	    gadgetp->GadgetText->BackPen = fill_pen;

	    rport = (requesterp) ? 
	      requesterp->ReqLayer->rp :
		windowp->RPort;

	    save_apen = rport->FgPen;

	    SetAPen (rport, (UBYTE) fill_pen);
	    SetDrMd (rport, JAM1);
	    SetAfPt (rport, NULL, 0);
	    SetWrMsk (rport, (UBYTE) 0xff);
	    BNDRYOFF (rport);
	    x1 = gadgetp->LeftEdge;
	    y1 = gadgetp->TopEdge;
	    x2 = x1 + gadgetp->Width - 1;
	    y2 = y1 + gadgetp->Height - 1;
	    
	    RectFill (rport, x1, y1, x2, y2);
	    
	    SetAPen (rport, (UBYTE) save_apen);
	    
	    AddGList (windowp, gadgetp, -1, 1, requesterp);
	    refresh_gadget (gadgetp, windowp);
	}
    }
    else  /* if (!windowp) */
    {
	gadgetp->Flags |= GFLG_SELECTED;
    }
}
    
void radio_button__out (struct Gadget *gadgetp, struct Window *windowp)
{
    struct RastPort *rport;
    short x1, y1, x2, y2;
    UBYTE save_apen;  /* I preserve this one, dunno if necessary */
    struct Requester *requesterp = 
      (gadgetp->GadgetType & GTYP_REQGADGET) ?
	current_requesterp :
	  NULL;

    if (windowp)
    {
	if (gadgetp->Flags & GFLG_SELECTED)
	{
	    RemoveGadget (windowp, gadgetp);
	    gadgetp->Flags &= ~GFLG_SELECTED;
	    gadgetp->GadgetText->FrontPen = text_pen;
	    gadgetp->GadgetText->BackPen = background_pen;

	    rport = (requesterp) ? 
	      requesterp->ReqLayer->rp :
		windowp->RPort;

	    save_apen = rport->FgPen;

	    SetAPen (rport, (UBYTE) background_pen);
	    SetDrMd (rport, JAM1);
	    SetAfPt (rport, NULL, 0);
	    SetWrMsk (rport, (UBYTE) 0xff);
	    BNDRYOFF (rport);
	    x1 = gadgetp->LeftEdge;
	    y1 = gadgetp->TopEdge;
	    x2 = x1 + gadgetp->Width - 1;
	    y2 = y1 + gadgetp->Height - 1;

	    RectFill (rport, x1, y1, x2, y2);

	    SetAPen (rport, (UBYTE) save_apen);

	    AddGList (windowp, gadgetp, -1, 1, requesterp);
	    refresh_gadget (gadgetp, windowp);
	}
    }
    else /* if (!windowp) */
    {
	gadgetp->Flags &= ~GFLG_SELECTED;
    }
}

void gadget__disable (struct Gadget *gadgetp, struct Window *windowp)
{
    struct RastPort *rport;
    short x1, y1, x2, y2;
    UBYTE save_apen;  /* I preserve this one, dunno if necessary */
    struct Requester *requesterp = 
      (gadgetp->GadgetType & GTYP_REQGADGET) ?
	current_requesterp :
	  NULL;

    if (gadgetp->Flags & GFLG_DISABLED) return;

    if (!windowp)
    {
	gadgetp->Flags |= GFLG_DISABLED;
	return;
    }

    RemoveGadget (windowp, gadgetp);
    gadgetp->Flags |= GFLG_DISABLED;

    rport = (requesterp) ? 
      requesterp->ReqLayer->rp :
	windowp->RPort;

    save_apen = rport->FgPen;

    if (gadgetp->Flags & GFLG_SELECTED)
    {
	SetAPen (rport, (UBYTE) fill_pen);
    }
    else
    {
	SetAPen (rport, (UBYTE) background_pen);
    }
    SetDrMd (rport, JAM1);
    SetAfPt (rport, NULL, 0);
    SetWrMsk (rport, (UBYTE) 0xff);
    BNDRYOFF (rport);
    x1 = gadgetp->LeftEdge;
    y1 = gadgetp->TopEdge;
    x2 = x1 + gadgetp->Width - 1;
    y2 = y1 + gadgetp->Height - 1;
	
    RectFill (rport, x1, y1, x2, y2);
	
    SetAPen (rport, (UBYTE) save_apen);

    AddGList (windowp, gadgetp, -1, 1, requesterp);
    refresh_gadget (gadgetp, windowp);
}

void gadget__enable (struct Gadget *gadgetp, struct Window *windowp)
{
    struct RastPort *rport;
    short x1, y1, x2, y2;
    UBYTE save_apen;  /* I preserve this one, dunno if necessary */
    struct Requester *requesterp = 
      (gadgetp->GadgetType & GTYP_REQGADGET) ?
	current_requesterp :
	  NULL;

    if (!(gadgetp->Flags & GFLG_DISABLED)) return;

    if (!windowp)
    {
	gadgetp->Flags |= GFLG_DISABLED;
	return;
    }

/* Yes, of course, OnGadget isn't smart enough for border gadgets */

    RemoveGadget (windowp, gadgetp);
    gadgetp->Flags &= ~GFLG_DISABLED;

    rport = (requesterp) ? 
      requesterp->ReqLayer->rp :
	windowp->RPort;

    save_apen = rport->FgPen;

    if (gadgetp->Flags & GFLG_SELECTED)
    {
	SetAPen (rport, (UBYTE) fill_pen);
    }
    else
    {
	SetAPen (rport, (UBYTE) background_pen);
    }
    SetDrMd (rport, JAM1);
    SetAfPt (rport, NULL, 0);
    SetWrMsk (rport, (UBYTE) 0xff);
    BNDRYOFF (rport);
    x1 = gadgetp->LeftEdge;
    y1 = gadgetp->TopEdge;
    x2 = x1 + gadgetp->Width - 1;
    y2 = y1 + gadgetp->Height - 1;
	
    RectFill (rport, x1, y1, x2, y2);
	
    SetAPen (rport, (UBYTE) save_apen);

    AddGList (windowp, gadgetp, -1, 1, requesterp);
    refresh_gadget (gadgetp, windowp);
}


void message_gadget__write (struct Gadget *gadgetp, char *message, struct
			    Window *windowp)
{
    struct RastPort *rport;
    UBYTE save_apen;  /* I preserve this one, dunno if necessary */
    struct Requester *requesterp = 
      (gadgetp->GadgetType & GTYP_REQGADGET) ?
	current_requesterp :
	  NULL;

    if (windowp) RemoveGadget (windowp, gadgetp);

    if (gadgetp->GadgetText) gfree (gadgetp->GadgetText);
    if (!message || !strlen(message))
    {
	gadgetp->GadgetText = NULL;
    }
    else
    {
	gadgetp->GadgetText = button_text__new (message);
    }

    /*  gadgetp->GadgetText->FrontPen = fill_text_pen; */

    if (windowp)
    {
	short x1, y1, x2, y2;

	x1 = gadgetp->LeftEdge;
	y1 = gadgetp->TopEdge;
	x2 = x1 + gadgetp->Width - 1;
	y2 = y1 + gadgetp->Height - 1;

	rport = (requesterp) ? 
	  requesterp->ReqLayer->rp :
	    windowp->RPort;

	save_apen = rport->FgPen;

	SetAPen (rport, (UBYTE) background_pen);

	RectFill (rport, x1, y1, x2, y2);

	SetAPen (rport, (UBYTE) save_apen);

	AddGList (windowp, gadgetp, -1, 1, requesterp);
	refresh_gadget (gadgetp, windowp);
    }
}

struct Window *window__new (char *title)
{
    struct NewWindow new_window; /* parameter structure to create window */
    struct Window *windowp = NULL;
    short height = WINDOW_HEIGHT (max_rows);

    short width = (Max_Columns * Font_Width) + 
      (2 * (FIRST_WINDOW_BUTTON_LEFT + BUTTON_SIDE_EDGE));

    if (Shell_Height < height) Shell_Height = height;
    if (Shell_Width < width) Shell_Width = width;

    new_window.LeftEdge = WINDOW_LEFT;
    new_window.TopEdge = WINDOW_TOP;
    new_window.Height = height;
    new_window.Width = width;
      new_window.DetailPen = 0;
    new_window.BlockPen = 1;
    new_window.Title = title;
    new_window.Flags = SMART_REFRESH | ACTIVATE | WINDOWCLOSE | 
      WINDOWDRAG | WINDOWDEPTH | NOCAREREFRESH;
    new_window.IDCMPFlags = IDCMP_CLOSEWINDOW | IDCMP_GADGETUP |
      IDCMP_GADGETDOWN | IDCMP_RAWKEY;
    if (Old_Intuition)
    {
	new_window.Type = WBENCHSCREEN;
    }
    else
    {
	new_window.Type = PUBLICSCREEN;  /* It's the latest thing */
    }
    new_window.FirstGadget = next_gadgetp;
    new_window.Screen = NULL;
    new_window.BitMap = NULL;
    new_window.MinWidth = 0;
    new_window.MinHeight = 0;
    new_window.MaxWidth = 0;
    new_window.MaxHeight = 0;
	
/* Try to open the window.  Like the call to OpenLibrary(), if
 * the OpenWindow call is successful, it returns a pointer to
 * the structure for your new window.  If the OpenWindow() call
 * fails, it returns a zero.
 */
    if ((windowp = (struct Window *) OpenWindow (&new_window)) == NULL )
    {
	error_message (WINDOW_OPEN_FAILED);
	gabort (EXIT_FAILURE);
    }
    return windowp;
}

struct Requester *requester__new (void)
{
    struct Requester *requesterp;
    short height = (max_rows * (Font_Height + (2 * BUTTON_TOP_EDGE)))
                     + ((max_rows - 1) * BUTTON_TOP_SPACE) +
		     + (2 * FIRST_REQUESTER_BUTTON_TOP);
    short width = (Max_Columns * Font_Width) + 
      (2 * (FIRST_REQUESTER_BUTTON_LEFT + BUTTON_SIDE_EDGE));

    requesterp = gmalloc (sizeof (struct Requester), NOTHING_SPECIAL);
    InitRequester (requesterp);
    requesterp->BackFill = background_pen;
    requesterp->ReqBorder = requester_border__new (height, width, shine_pen,
						   shadow_pen);
    requesterp->Width = width;
    requesterp->Height = height;
    requesterp->RelLeft = 0;
    requesterp->RelTop = 0;
    requesterp->Flags = POINTREL;
    return requesterp;
}

BOOLEAN requester__display (struct Requester *rp, struct Window *wp)
{
    if (Request (rp, wp))
    {
	if (++requester_stack_index >= REQUESTER_STACK_DEPTH)
	{
	    error_message (REQUESTER_STACK_OVERFLOW);
	    EndRequest (rp, wp);
	    return FALSE;
	}
	requester_stack[requester_stack_index] = current_requesterp;
	current_requesterp = rp;
	return TRUE;
    }
    return FALSE;
}

void requester__remove (struct Requester *rp, struct Window *wp)
{
    current_requesterp = requester_stack[requester_stack_index];
    if (requester_stack_index) requester_stack_index--;

    EndRequest (rp, wp);
}

BOOLEAN ignore_tooltype (char *tooltype)
{
    if (!strncmp (tooltype, "TOOLPRI", 7)) return TRUE;
    if (!strncmp (tooltype, "STARTPRI", 8)) return TRUE;
    if (!strncmp (tooltype, "DONOTWAIT", 9)) return TRUE;
    if (!strncmp (tooltype, "FILETYPE", 8)) return TRUE;
    if (!strncmp (tooltype, "DUMMY", 5)) return TRUE;
    return FALSE;
}


#endif  /* ifdef AMIGA */

[ RETURN TO DIRECTORY ]