/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1991 by Torsten Jürgeleit
 *
 *	Name .....: gadgets1.c
 *	Created ..: Thursday 19-Dec-91 20:52:50
 *	Revision .: 21
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	21-Oct-92   Torsten Jürgeleit      corrected autorepeat of arrow
 *					   gadgets under Kick 2.x too
 *	14-Sep-92   Torsten Jürgeleit      now set_gadget_attributes() with
 *					   data1-3 set to USE_CURRENT_VALUE
 *					   returns current setting
 *	12-Sep-92   Torsten Jürgeleit      forgot to copy original IntuiMessage
 *					   into ISUP IntuiMessage
 *	10-Aug-92   Torsten Jürgeleit      perform_gadget_action(): don't use
 *					   lv_SelectedEntry anymore
 *	23-Jul-92   Torsten Jürgeleit      perform_gadget_action(): report too
 *					   if same palette color entry selected
 *					   again
 *	23-Jul-92   Torsten Jürgeleit      perform_gadget_action(): only use
 *					   lv_SelectedEntry if GADGET_DATA_-
 *					   FLAG_LISTVIEW_SHOW_SELECTED set
 *	20-Jul-92   Torsten Jürgeleit      mark end of chained gadgets in
 *					   display_gadgets()
 *	19-Jul-92   Torsten Jürgeleit      corrected replying of IntuiMessages
 *					   in remove_gadgets()
 *	19-Jul-92   Torsten Jürgeleit      remove/display_visible_gadget_lists()
 *					   imported from requester/requester.c
 *	16-Jul-92   Torsten Jürgeleit      size of count gadget changeable
 *					   via modify_gadgets()
 *	16-Jul-92   Torsten Jürgeleit      lower and upper bound of count
 *					   gadget now changeable via
 *					   set_gadget_attributes()
 *	11-Jul-92   Torsten Jürgeleit      now RAWKEY instead of VANILLAKEY
 *                                         used for gadget hotkeys
 *	14-Apr-92   Torsten Jürgeleit      if flag GADGET_DATA_FLAG_NO_CLEAR
 *                                         set then don't clear area occupied
 *                                         by ISUP object before drawing
 *	30-Apr-92   Torsten Jürgeleit      area gadget occupies now cleared 
 *                                         before diplaying it
 *	28-Apr-92   Torsten Jürgeleit      correct disabling in
 *                                         set_gadget_attributes() if gadgets
 *                                         not displayed -> gl_Window was not
 *                                         defined
 *	20-Apr-92   Torsten Jürgeleit      disable refresh if same entry
 *                                         from list view selected
 *	01-Apr-92   Torsten Jürgeleit      any references to gadget data
 *                                         via gl_Data removed
 *	01-Apr-92   Torsten Jürgeleit      changed parameter size of
 *                                         modify_gadget() for new value of
 *                                         USE_CURRENT_VALUE (1L << 31)
 *	11-Mar-92   Torsten Jürgeleit      guru if toggeling DISABLED of NOT
 *                                         displayed ISUP object via
 *                                         set_gadget_attributes()
 *	24-Jan-92   Torsten Jürgeleit      new function modify_gadget()
 *	28-Dec-91   Torsten Jürgeleit      correct highliting of toggle
 *                                         select buttons
 *	19-Dec-91   Torsten Jürgeleit      Created this file!
 *
 ****************************************************************************
 *
 *	Gadget support routines - part 1
 *
 * $Revision Header ********************************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#ifdef AZTEC_C
#include <functions.h>   /* needed for Aztec C - prototypes and pragmas for all Amiga system functions */
#endif
#include <libraries/memwatch.h>   /* header file for memory debug link library (Fish 240) - AFTER functions.h */
#include <string.h>
#include "/render/render.h"
#include "/borders/borders.h"
#include "/texts/texts.h"
#include "/language/language.h"
#include "/pointer/pointer.h"
#include "gadgets.h"
#include "imports.h"

	/* Defines */

#define CONVERT_BUFFER_SIZE	10L

	/* Imports */

IMPORT struct IntuitionBase  *IntuitionBase;

	/* Static data */

STATIC struct VisibleGadgetLists  visible_gadget_lists = {
   {   /* struct SignalSemaphore  vg_Semaphore */
      {   /* struct Node  ss_Link */
	 NULL, NULL, NT_SEMAPHORE, 0, NULL
      }, 0,
      {   /* struct MinList  ss_WaitQueue */
	 (struct MinNode *)&visible_gadget_lists.vg_Semaphore.ss_WaitQueue.mlh_Tail,
	 NULL,
	 (struct MinNode *)&visible_gadget_lists.vg_Semaphore.ss_WaitQueue.mlh_Head
      },
      {   /* struct SemaphoreRequest  ss_MultipleLink */
	 {   /* struct MinNode  sr_Link */
	    NULL, NULL
	 }, NULL
      }, NULL, -1
   },
   {   /* struct MinList  vg_MinList */
      (struct MinNode *)&visible_gadget_lists.vg_MinList.mlh_Tail,
      NULL,
      (struct MinNode *)&visible_gadget_lists.vg_MinList.mlh_Head
   }, 0
};
BYTE hotkey_id[] = "_";

	/* Create gadget list */

   struct GadgetList *
create_gadgets(struct RenderInfo  *ri, struct GadgetData  *gd,
		   SHORT hoffset, SHORT voffset, BYTE **language_text_array)
{
   USHORT data_entries;

   if (ri && ri->ri_ID == ISUP_ID && gd &&
			   (data_entries = count_gadget_data_entries(gd))) {
      struct GadgetList  *gl;
      ULONG gl_size = sizeof(struct GadgetList) + (data_entries - 1) *
					    sizeof(struct ExtendedGadget *);
      if (gl = AllocMem(gl_size, (LONG)MEMF_PUBLIC | MEMF_CLEAR)) {
	 BYTE  *gadget_buffer;
	 ULONG gadget_buffer_size;

	 gl->gl_RenderInfo        = ri;
	 gl->gl_DataEntries       = data_entries;
	 gl->gl_LanguageTextArray = language_text_array;
	 if ((gadget_buffer_size = get_gadget_buffer_size(gl, gd)) &&
			       (gadget_buffer = AllocMem(gadget_buffer_size,
					 (LONG)MEMF_PUBLIC | MEMF_CLEAR))) {
	    BYTE  *image_buffer = NULL;
	    ULONG image_buffer_size;

	    if (image_buffer_size = get_image_buffer_size(gl, gd)) {
	       image_buffer = AllocMem(image_buffer_size,
					     (LONG)MEMF_PUBLIC | MEMF_CHIP);
	    }
	    if (!image_buffer_size || image_buffer) {

	       /* Init gadget list struct */
	       gl->gl_GadgetBuffer     = gadget_buffer;
	       gl->gl_GadgetBufferSize = gadget_buffer_size;
	       gl->gl_ImageBuffer      = image_buffer;
	       gl->gl_ImageBufferSize  = image_buffer_size;
	       gl->gl_ID               = ISUP_ID;
	       init_gadgets(gl, gd, hoffset, voffset);
	       return(gl);
	    }
	    FreeMem(gadget_buffer, gadget_buffer_size);
	 }
	 FreeMem(gl, gl_size);
      }
   }
   return(NULL);
}
	/* Free gadget list */

   VOID
free_gadgets(struct GadgetList  *gl)
{
   if (gl && gl->gl_ID == ISUP_ID) {
      USHORT i, data_entries = gl->gl_DataEntries;

      if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
	 remove_gadgets(gl);
      }

      /* Close fonts */
      for (i = 0; i < data_entries; i++) {
	 struct ExtendedGadget  *egad = gl->gl_Gadgets[i];

	 if (egad->eg_Flags & EXTENDED_GADGET_FLAG_CLOSE_FONT) {
	    CloseFont(egad->eg_TextFont);
	 }
      }

      /* Free buffers */
      FreeMem(gl->gl_GadgetBuffer, gl->gl_GadgetBufferSize);
      if (gl->gl_ImageBufferSize) {
	 FreeMem(gl->gl_ImageBuffer, gl->gl_ImageBufferSize);
      }
      FreeMem(gl, (LONG)(sizeof(struct GadgetList) +
	       (gl->gl_DataEntries - 1) * sizeof(struct ExtendedGadget *)));
   }
}
	/* Display gadget list */

   VOID
display_gadgets(struct Window  *win, struct GadgetList  *gl)
{
   if (win && gl && gl->gl_ID == ISUP_ID &&
			     !(gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED)) {
      struct RenderInfo          *ri = gl->gl_RenderInfo;
      struct VisibleGadgetLists  *vg = &visible_gadget_lists;
      struct ExtendedGadget      *last_egad;
      USHORT i, data_entries = gl->gl_DataEntries,
	     clear_flags = (ri->ri_Flags & RENDER_INFO_FLAG_INNER_WINDOW ?
					0 : CLEAR_WINDOW_FLAG_ABSOLUTE_POS);
      /* Mark gadget list as displayed */
      gl->gl_Window = win;
      gl->gl_Flags |= GADGET_LIST_FLAG_DISPLAYED;

      /* Increment visible gadget list counter */
      ObtainSemaphore(&vg->vg_Semaphore);
      AddTail((struct List *)&vg->vg_MinList, (struct Node *)&gl->gl_MinNode);
      vg->vg_Count++;
      ReleaseSemaphore(&vg->vg_Semaphore);

      /* Link gadget list */
      for (i = 0, last_egad = NULL; i < data_entries; i++) {
	 struct ExtendedGadget  *egad = gl->gl_Gadgets[i];

	 /* First clear area occupied by this ISUP object */
	 if (!(egad->eg_DataFlags & GADGET_DATA_FLAG_NO_CLEAR)) {
	    clear_window(ri, win, egad->eg_BorderLeftEdge,
			       egad->eg_BorderTopEdge, egad->eg_BorderWidth,
				      egad->eg_BorderHeight, clear_flags);
	 }

	 /* Move mouse pointer to this ISUP object */
	 if (egad->eg_DataFlags & GADGET_DATA_FLAG_MOVE_POINTER) {
	    struct Gadget  *gad = &egad->eg_Gadget;

	    move_mouse_pointer(win, gad->LeftEdge + gad->Width / 2,
				     gad->TopEdge + gad->Height / 2, FALSE);
	 }

	 /* Link gadgets of this ISUP object */
	 do {
	    if (last_egad) {
	       last_egad->eg_Gadget.NextGadget = &egad->eg_Gadget;
	    }
	    last_egad = egad;
	 } while (egad = egad->eg_NextGadget);
      }

      /* Mark end of gadget list */
      if (last_egad) {
	 last_egad->eg_Gadget.NextGadget = NULL;
      }

      /* Append new gadgets to window's gadget list and refresh them */
      AddGList(win, &gl->gl_Gadgets[0]->eg_Gadget, -1L, -1L, (LONG)NULL);
      refresh_gadgets(gl);
   }
}
	/* Refresh gadget list */

   VOID
refresh_gadgets(struct GadgetList  *gl)
{
   if (gl && gl->gl_ID == ISUP_ID &&
			      (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED)) {
      USHORT i, data_entries = gl->gl_DataEntries;

      for (i = 0; i < data_entries; i++) {
	 refresh_gadget(gl->gl_Gadgets[i]);
      }
   }
}
	/* Modify gadget position and dimension */

   VOID
modify_gadget(struct GadgetList  *gl, USHORT data_entry, LONG left_edge,
				   LONG top_edge, ULONG width, ULONG height)
{
   if (gl && gl->gl_ID == ISUP_ID && data_entry < gl->gl_DataEntries &&
	 (left_edge != USE_CURRENT_VALUE || top_edge != USE_CURRENT_VALUE ||
	       width != USE_CURRENT_VALUE || height != USE_CURRENT_VALUE)) {
      struct ExtendedGadget  *egad = gl->gl_Gadgets[data_entry];
      VOID  *data = egad + 1;
      SHORT dx, dy;

      /* Clear old gadget */
      if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
	 struct RenderInfo  *ri = gl->gl_RenderInfo;
	 struct RastPort    *rp = gl->gl_Window->RPort;
	 SHORT x, y;

	 SetAPen(rp, (LONG)ri->ri_BackPen);
	 SetDrMd(rp, (LONG)JAM1);

	 /* Clear gadget */
	 x = egad->eg_BorderLeftEdge;
	 y = egad->eg_BorderTopEdge;

	 /* Inner window position needed ? */
	 if (ri->ri_Flags & RENDER_INFO_FLAG_INNER_WINDOW) {
	    x += ri->ri_WindowBorderLeft;
	    y += ri->ri_WindowBorderTop;
	 }
	 RectFill(rp, (LONG)x, (LONG)y, (LONG)(x + egad->eg_BorderWidth - 1),
				     (LONG)(y + egad->eg_BorderHeight - 1));
	 /* Clear gadget text - don't forget underscore !!! */
	 if (egad->eg_Text) {
	    x = egad->eg_Gadget.LeftEdge + egad->eg_TextLeftEdge;
	    y = egad->eg_Gadget.TopEdge + egad->eg_TextTopEdge;
	    RectFill(rp, (LONG)x, (LONG)y, (LONG)(x + egad->eg_TextWidth - 1),
				   (LONG)(y + egad->eg_TextFont->tf_YSize));
	 }
      }

      /* Calc difference to old position */
      if (left_edge == USE_CURRENT_VALUE) {
	 dx = 0;
      } else {
	 dx = left_edge - egad->eg_BorderLeftEdge;
      }
      if (top_edge == USE_CURRENT_VALUE) {
	 dy = 0;
      } else {
	 dy = top_edge - egad->eg_BorderTopEdge;
      }

      /* Move gadget */
      if (dx || dy) {
	 struct ExtendedGadget  *eg = egad;

	 /* Move all extended gadgets belonging to this object */
	 do {
	    eg->eg_BorderLeftEdge  += dx;
	    eg->eg_BorderTopEdge   += dy;
	    eg->eg_Gadget.LeftEdge += dx;
	    eg->eg_Gadget.TopEdge  += dy;
         } while (eg = eg->eg_NextGadget);

	 /* Move special gadget data */
	 switch (egad->eg_Type) {
	    case EXTENDED_GADGET_TYPE_CYCLE :
	       ((struct CycleData *)data)->cy_LeftEdge += dx;
	       ((struct CycleData *)data)->cy_TopEdge  += dy;
	       break;

	    case EXTENDED_GADGET_TYPE_COUNT :
	       ((struct CountData *)data)->co_LeftEdge += dx;
	       ((struct CountData *)data)->co_TopEdge  += dy;
	       break;

	    case EXTENDED_GADGET_TYPE_LISTVIEW_PROP :
	       ((struct ListViewData *)data)->lv_LeftEdge += dx;
	       ((struct ListViewData *)data)->lv_TopEdge  += dy;
	       break;
	 }
      }

      /* Calc difference to old dimension */
      if (width == USE_CURRENT_VALUE) {
	 dx = 0;
      } else {
	 dx = width - egad->eg_BorderWidth;
      }
      if (height == USE_CURRENT_VALUE) {
	 dy = 0;
      } else {
	 dy = height - egad->eg_BorderHeight;
      }

      /* Resize only some gadgets */
      if (dx || dy) {
	 struct Border  *border1 = NULL, *border2 = NULL;
	 USHORT border1_type, border2_type;
	 ULONG  flags = egad->eg_DataFlags;
	 BOOL   resize = FALSE;

	 switch (egad->eg_Type) {
	    case EXTENDED_GADGET_TYPE_BUTTON :
	       if (!(flags & GADGET_DATA_FLAG_BUTTON_IMAGE)) {
		  border1_type = BORDER_DATA_TYPE_BOX1_OUT;
		  border1      = (struct Border *)
					       egad->eg_Gadget.GadgetRender;
		  if (!(flags & GADGET_DATA_FLAG_HIGH_COMP)) {
		     border2_type = BORDER_DATA_TYPE_BOX1_IN;
		     border2      = (struct Border *)
					       egad->eg_Gadget.SelectRender;
		  }
		  resize = TRUE;
	       }
	       break;

	    case EXTENDED_GADGET_TYPE_SLIDER_PROP :
	       border1_type = BORDER_DATA_TYPE_BOX1_OUT;
	       border1      = (struct Border *)egad->eg_Render;
	       resize       = TRUE;
	       break;

	    case EXTENDED_GADGET_TYPE_SCROLLER_PROP :
	       if (flags & GADGET_DATA_FLAG_SCROLLER_NO_ARROWS) {
		  border1_type = BORDER_DATA_TYPE_BOX1_OUT;
		  border1      = (struct Border *)egad->eg_Render;
		  resize       = TRUE;
	       }
	       break;

	    case EXTENDED_GADGET_TYPE_COUNT :
	       border1_type = BORDER_DATA_TYPE_BOX1_OUT;
	       border1      = (struct Border *)egad->eg_Gadget.GadgetRender;
	       resize       = TRUE;

	       /* Move area for current value */
	       ((struct CountData *)data)->co_LeftEdge =
		    egad->eg_Gadget.LeftEdge + (egad->eg_Gadget.Width + dx -
				  ((struct CountData *)data)->co_Width) / 2;
	       ((struct CountData *)data)->co_TopEdge  =
		    egad->eg_Gadget.TopEdge + (egad->eg_Gadget.Height + dy -
				 ((struct CountData *)data)->co_Height) / 2;
	       break;
	 }
	 if (resize == TRUE) {
	    
	    /* Resize gadget */
	    egad->eg_BorderWidth   += dx;
	    egad->eg_BorderHeight  += dy;
	    egad->eg_Gadget.Width  += dx;
	    egad->eg_Gadget.Height += dy;

	    /* Resize gadget text */
	    if (egad->eg_Text) {

	       /* Change horizontal pos */
	       if (flags & GADGET_DATA_FLAG_TEXT_RIGHT) {
		  egad->eg_TextLeftEdge += dx;
	       } else {
		  if (!(flags & GADGET_DATA_FLAG_TEXT_LEFT)) {
		     egad->eg_TextLeftEdge += dx / 2;
		  }
	       }

	       /* Change vertical pos */
	       if (flags & GADGET_DATA_FLAG_TEXT_BELOW) {
		  egad->eg_TextTopEdge += dy;
	       } else {
		  if (!(flags & GADGET_DATA_FLAG_TEXT_ABOVE)) {
		     egad->eg_TextTopEdge += dy / 2;
		  }
	       }
	    }

	    /* Resize border */
	    if (!(flags & GADGET_DATA_FLAG_NO_BORDER)) {
	       if (border1) {
		  resize_border(border1, egad->eg_BorderWidth,
				       egad->eg_BorderHeight, border1_type);
	       }
	       if (border2) {
		  resize_border(border2, egad->eg_BorderWidth,
				       egad->eg_BorderHeight, border2_type);
	       }
	    }
	 }
      }

      /* Finally refresh modified gadget */
      refresh_gadget(egad);
   }
}
	/* Set gadget attributes */

   ULONG
set_gadget_attributes(struct GadgetList  *gl, USHORT data_entry,
    ULONG flag_mask, ULONG flag_bits, ULONG data1, ULONG data2, VOID *data3)
{
   ULONG value = 0;

   if (gl && gl->gl_ID == ISUP_ID && data_entry < gl->gl_DataEntries) {
      struct ExtendedGadget  *egad = gl->gl_Gadgets[data_entry];
      struct Gadget          *gad = &egad->eg_Gadget;
      USHORT type = egad->eg_DataType;
      ULONG  flags = egad->eg_DataFlags;
      BOOL   refresh = FALSE;

      /* Change gadget data flags */
      if (flag_mask) {
	 ULONG  mask;
	 USHORT i;

	 /* Change gadget data flags according to flag mask */
	 for (i = 0, mask = 1; i < MAX_DATA_FLAG_BITS; i++, mask <<= 1) {

	    /* Check if flag bit set */
	    if (flag_mask & mask) {
	       BOOL change = FALSE;

	       /* Change flag only if changeable and valid for gadget type */
	       switch (mask) {
		  case GADGET_DATA_FLAG_DISABLED :

		     /* Toggle able state of gadget */
		     if ((flags & mask) != (flag_bits & mask)) {
			struct RastPort        *rp;   /* init later !!! */
			struct ExtendedGadget  *temp_egad = egad;
			UBYTE displayed = gl->gl_Flags &
						 GADGET_LIST_FLAG_DISPLAYED;
			/* Set draw mode and color for clearing enabled select box */
			if (displayed &&
				      (flags & GADGET_DATA_FLAG_DISABLED)) {
			   rp = gl->gl_Window->RPort;   /* ONLY if displayed then gl_Window is valid !!!*/
			   SetDrMd(rp, (LONG)JAM1);
			   SetAPen(rp, (LONG)gl->gl_RenderInfo->ri_BackPen);
			}

			/* Toggle able state of ALL gadgets belonging to ISUP object */
			do {
			   struct Gadget  *temp_gad = &temp_egad->eg_Gadget;

			   if (flags & GADGET_DATA_FLAG_DISABLED) {
			      temp_gad->Flags &= ~GADGDISABLED;

			      /* Clear ghosted gadget select box */
			      if (displayed) {
				 RectFill(rp, (LONG)temp_gad->LeftEdge,
						    (LONG)temp_gad->TopEdge,
						 (LONG)(temp_gad->LeftEdge +
						       temp_gad->Width - 1),
						  (LONG)(temp_gad->TopEdge +
						     temp_gad->Height - 1));
			      }
			   } else {
			      temp_gad->Flags |= GADGDISABLED;
			   }
			} while (temp_egad = temp_egad->eg_NextGadget);

			/* Refresh gadget before leaving function */
			refresh = TRUE;
			change  = TRUE;
		     }
		     break;

		  case GADGET_DATA_FLAG_INPUT_AUTO_ACTIVATE :
		     if (type == GADGET_DATA_TYPE_STRING ||
					 type == GADGET_DATA_TYPE_INTEGER) {
			change = TRUE;
		     }
		     break;

		  case GADGET_DATA_FLAG_STRING_UNSIGNED_DEC :
		     if (type == GADGET_DATA_TYPE_STRING) {
			change = TRUE;
		     }
		     break;

		  case GADGET_DATA_FLAG_STRING_SIGNED_DEC :
		     if (type == GADGET_DATA_TYPE_STRING) {
			change = TRUE;
		     }
		     break;

		  case GADGET_DATA_FLAG_STRING_HEX :
		     if (type == GADGET_DATA_TYPE_STRING) {
			change = TRUE;
		     }
		     break;

		  case GADGET_DATA_FLAG_STRING_BIN :
		     if (type == GADGET_DATA_TYPE_STRING) {
			change = TRUE;
		     }
		     break;

		  case GADGET_DATA_FLAG_MOVE_POINTER :
		     change = TRUE;
		     break;
	       }

	       /* Change flag bit if neccessary */
	       if (change == TRUE) {
		  flags &= ~mask;
		  flags |= flag_bits & mask;
	       }
	    }
	 }

	 /* Save changed gadget data flags */
	 egad->eg_DataFlags = flags;
      }

      /* Get current setting and change special gadget data */
      switch (type) {
	 case GADGET_DATA_TYPE_BUTTON :
	    if (gad->Activation & TOGGLESELECT) {
	       if (gad->Flags & SELECTED) {
		  value = 1;
	       }
	       if (data1 != USE_CURRENT_VALUE) {
		  if (data1) {
		     gad->Flags |= SELECTED;
		  } else {
		     if (gad->Flags & SELECTED) {

			/* Prepare gadget state manually (unselected) */
			change_gadget(egad);
		     }
		     gad->Flags &= ~SELECTED;
		  }
		  change_gadget(egad);
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_CHECK :
	    if (gad->Flags & SELECTED) {
	        value = 1;
	    }
	    if (data1 != USE_CURRENT_VALUE) {
	       if (data1) {
		  gad->Flags |= SELECTED;
	       } else {
		  gad->Flags &= ~SELECTED;
	       }
	       change_gadget(egad);
	    }
	    break;

	 case GADGET_DATA_TYPE_MX :
	    {
	       struct MXData  *mx = (VOID *)(egad + 1);

	       value = mx->mx_ActiveEntry;
	       if (data2 != USE_CURRENT_VALUE) {
		  ULONG entries = mx->mx_TextEntries, active = data2;

		  if (active >= entries) {
		     active = entries - 1;   /* activate last entry */
		  }
		  mx->mx_ActiveEntry = active;
		  change_gadget(egad);
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_STRING :
	 case GADGET_DATA_TYPE_INTEGER :
	    {
	       struct StringInfo  *sinfo = (struct StringInfo *)
							   gad->SpecialInfo;
	       BYTE *buffer = (BYTE *)sinfo->Buffer;

	       if (type == GADGET_DATA_TYPE_STRING) {
		  value = (ULONG)buffer;
	       } else {
		  value = sinfo->LongInt;
	       }
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  BYTE  *input = data3;
		  ULONG len, max_len = sinfo->MaxChars;

		  if (type == GADGET_DATA_TYPE_STRING) {

		     /* Convert number for string input gadget */
		     if (flags & GADGET_DATA_FLAG_STRING_UNSIGNED_DEC) {
			if ((LONG)input != -1L) {
			   len = convert_unsigned_dec((ULONG)input,
					     buffer, DEC_NUM_CONVERT_FLAGS);
			} else {
			   len = 0;
			}
		     } else {
			if (flags & GADGET_DATA_FLAG_STRING_SIGNED_DEC) {
			   if ((LONG)input != -1L) {
			      len = convert_signed_dec((ULONG)input, buffer,
						     DEC_NUM_CONVERT_FLAGS);
			   } else {
			      len = 0;
			   }
			} else {
			   if (flags & GADGET_DATA_FLAG_STRING_HEX) {
			      if ((LONG)input != -1L) {
				 len = convert_hex((ULONG)input, buffer,
						     HEX_NUM_CONVERT_FLAGS);
			      } else {
				 len = 0;
			      }
			   } else {
			      if (flags & GADGET_DATA_FLAG_STRING_BIN) {
				 if ((LONG)input != -1L) {
				    len = convert_bin((ULONG)input, buffer,
						     BIN_NUM_CONVERT_FLAGS);
				 } else {
				    len = 0;
				 }
			      } else {
				 if (input) {
				    if ((len = strlen(input)) > max_len) {
				       len = max_len;
				    }
				    strncpy(buffer, input, (size_t)len);
				 } else {
				    len = 0;
				 }
			      }
			   }
			}
		     }
		  } else {

		     /* Convert number for integer input gadget */
		     if ((LONG)input != -1L) {
			len = convert_unsigned_dec((ULONG)input, buffer, 0);
		     } else {
			len = 0;
		     }
		  }
		  *(buffer + len)  = '\0';   /* mark end of string */
		  sinfo->BufferPos = 0;
		  change_gadget(egad);
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_SLIDER :
	    {
	       struct SliderData  *sl = (VOID *)(egad + 1);

	       value = sl->sl_Level;
	       if (data1 != USE_CURRENT_VALUE || data2 != USE_CURRENT_VALUE
				     || (ULONG)data3 != USE_CURRENT_VALUE) {
		  ULONG min, max, level;

		  if (data1 != USE_CURRENT_VALUE) {
		     min = sl->sl_Min = data1;
		  } else {
		    min = sl->sl_Min;
		  }
		  if (data2 != USE_CURRENT_VALUE) {
		     max = sl->sl_Max = data2;
		  } else {
		     max = sl->sl_Max;
		  }
		  if ((ULONG)data3 != USE_CURRENT_VALUE) {
		     level = (ULONG)data3;
		  } else {
		     level = sl->sl_Level;
		  }
		  if ((LONG)level < (LONG)min) {
		     level = min;
		  } else {
		    if ((LONG)level > (LONG)max) {
		       level = max;
		    }
		  }
		  sl->sl_Level = level;
		  change_gadget(egad);
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_SCROLLER :
	    {
	       struct ScrollerData  *sc = (VOID *)(egad + 1);

	       value = sc->sc_Top;
	       if (data1 != USE_CURRENT_VALUE || data2 != USE_CURRENT_VALUE
				     || (ULONG)data3 != USE_CURRENT_VALUE) {
	          ULONG visible, total, top;

		  if (data1 != USE_CURRENT_VALUE) {
		     visible = sc->sc_Visible = data1;
		  } else {
		     visible = sc->sc_Visible;
		  }
		  if (data2 != USE_CURRENT_VALUE) {
		     total = sc->sc_Total = data2;
		  } else {
		     total = sc->sc_Total;
		  }
		  if ((ULONG)data3 != USE_CURRENT_VALUE) {
		     top = (ULONG)data3;
		  } else {
		     top = sc->sc_Top;
		  }
		  if (top >= total || (total - top) < visible) {
		     top = (total > visible ? total - visible :
							   visible - total);
		  }
		  sc->sc_Top = top;
		  change_gadget(egad);
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_CYCLE :
	    {
	       struct CycleData  *cy = (VOID *)(egad + 1);

	       value = cy->cy_ActiveEntry;
	       if (data2 != USE_CURRENT_VALUE) {
		  ULONG entries = cy->cy_TextEntries, active = data2;

		  if (active > entries) {
		     active = entries;   /* activate last entry */
		  }
		  cy->cy_ActiveEntry = active;
		  change_gadget(egad);
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_COUNT :
	    {
	       struct CountData  *co = (VOID *)(egad + 1);

	       value = co->co_Value;
	       if (data1 != USE_CURRENT_VALUE || data2 != USE_CURRENT_VALUE
				     || (ULONG)data3 != USE_CURRENT_VALUE) {
		  if ((ULONG)data1 != USE_CURRENT_VALUE ||
					(ULONG)data2 != USE_CURRENT_VALUE) {
		     struct IntuiText  itext;
		     USHORT min_len, max_len, width;
		     SHORT  left_edge;
		     ULONG  min, max;

		     /* Calc new left edge and width of area for count value */
		     if ((ULONG)data1 != USE_CURRENT_VALUE) {
			min = (ULONG)data1;
		     } else {
			min = co->co_Min;
		     }
		     if ((ULONG)data2 != USE_CURRENT_VALUE) {
			max = (ULONG)data2;
		     } else {
			max = co->co_Max;
		     }
		     if (flags & GADGET_DATA_FLAG_COUNT_SIGNED_DEC) {
			min_len = convert_signed_dec((LONG)min,
							   (BYTE *)NULL, 0);
			max_len = convert_signed_dec((LONG)max,
							   (BYTE *)NULL, 0);
		     } else {
			min_len = convert_unsigned_dec(min, (BYTE *)NULL, 0);
			max_len = convert_unsigned_dec(max, (BYTE *)NULL, 0);
		     }
		     itext.ITextFont = egad->eg_TextAttr;
		     itext.IText     = (UBYTE *)"8";
		     width           = IntuiTextLength(&itext) *
				    (min_len > max_len ? min_len : max_len);
		     left_edge       = gad->LeftEdge +
					    (SHORT)(gad->Width - width) / 2;
		     /* Clear old area */
		     if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
			print_count_text(egad, FALSE);
		     }

		     /* Change count data */
		     co->co_LeftEdge = left_edge;
		     co->co_Width    = width;
		     co->co_Min      = min;
		     co->co_Max      = max;

		     /* Now let's compare current value with new bounds */
		     if ((ULONG)data3 == USE_CURRENT_VALUE) {
			data3 = (VOID *)co->co_Value;
		     }
		  }
		  if ((ULONG)data3 != USE_CURRENT_VALUE) {
		     ULONG min = co->co_Min, max = co->co_Max,
			   num = (ULONG)data3;

		     /* Compare new current value with bounds */
		     if ((LONG)num < (LONG)min) {
			num = min;
		     } else {
			if ((LONG)num > (LONG)max) {
			   num = max;
			}
		     }
		     co->co_Value = num;
		     change_gadget(egad);
		  }
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_LISTVIEW :
	    {
	       struct ListViewData  *lv = (VOID *)(egad + 1);

	       if (flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
		  value = lv->lv_SelectedEntry;
	       } else {
		  value = lv->lv_TopEntry;
	       }
	       if (data2 != USE_CURRENT_VALUE ||
					(ULONG)data3 != USE_CURRENT_VALUE) {
		  ULONG visible, entries, top;

		  if ((ULONG)data3 != USE_CURRENT_VALUE) {
		     struct List  *list = data3;
		     struct Node  *node;

		     /* Count list nodes */
		     entries = 0;
		     if (list) {
			node = list->lh_Head;
			while (node = node->ln_Succ) {
			   entries++;
			}
		     }
		     lv->lv_List        = list;
		     lv->lv_ListEntries = entries;
		  }
		  if (data2 != USE_CURRENT_VALUE ||
					(ULONG)data3 != USE_CURRENT_VALUE) {
		     struct List  *list = data3;
		     struct Node  *node;
		     ULONG sel;

		     list    = lv->lv_List;
		     visible = lv->lv_VisibleEntries;
		     entries = lv->lv_ListEntries;

		     /* Search top list node */
		     if (!entries) {
			sel  = 0;
			top  = 0;
			node = NULL;
		     } else {
			if (flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {

			   /* Calc correct top entry for selected entry */
			   if (data2 != USE_CURRENT_VALUE) {
			      sel = data2;
			   } else {
			      sel = lv->lv_SelectedEntry;
			   }
			   if (sel >= entries) {
			      sel = entries - 1;
			   }
			   if (sel && entries > visible) {
			      if (sel > (entries - visible)) {
				 ULONG i;

				 top  = entries - visible;
				 node = list->lh_TailPred;
				 for (i = entries - top - 1; i; i--) {
				    node = node->ln_Pred;
				 }
			      } else {
				 node = list->lh_Head;
				 if (sel > (visible >> 1)) {
				    ULONG i;

				    top = sel - (visible >> 1);
				    for (i = 0; i < top; i++) {
				       node = node->ln_Succ;
				    }
				 } else {
				    top = 0;
				 }
			      }
			   } else {
			      top  = 0;
			      node = list->lh_Head;
			   }
			} else {

			   /* Calc top entry ignoring selected entry */
			   if (data2 != USE_CURRENT_VALUE) {
			      top = data2;
			   } else {
			      top = lv->lv_TopEntry;
			   }
			   if (top && entries > visible) {
			      if (top > entries ||
						(entries - top) < visible) {
				 ULONG i;

				 top  = entries - visible;
				 node = list->lh_TailPred;
				 for (i = entries - top - 1; i; i--) {
				    node = node->ln_Pred;
				 }
			      } else {
				 ULONG i;

				 node = list->lh_Head;
				 for (i = 0; i < top; i++) {
				    node = node->ln_Succ;
				 }
			      }
			   } else {
			      top  = 0;
			      node = list->lh_Head;
			   }
			   sel = top;
			}
		     }

		     /* Init list view data */
		     lv->lv_TopNode       = node;
		     lv->lv_TopEntry      = top;
		     lv->lv_SelectedEntry = sel;
		     change_gadget(egad);   /* before changing list entry gadget flags !!! */

		     /* Set GADGHNONE flag for all entry gadgets without list node */
		     if (!(flags & GADGET_DATA_FLAG_LISTVIEW_READ_ONLY)) {
			struct ExtendedGadget  *temp_egad;
			USHORT i;

			/* Get ptr to first entry gadget */
			temp_egad = egad->eg_NextGadget->eg_NextGadget->
							      eg_NextGadget;
			for (i = 0; i < visible; i++, top++) {
			   struct Gadget  *temp_gad = &temp_egad->eg_Gadget;

			   if (top < entries) {
			      temp_gad->Flags = (temp_gad->Flags &
						 ~GADGHIGHBITS) | GADGHCOMP;
			   } else {
			      temp_gad->Flags = (temp_gad->Flags &
						 ~GADGHIGHBITS) | GADGHNONE;
			   }
			   temp_egad = temp_egad->eg_NextGadget;
			}
		     }
		  }
	       }
	    }
	    break;

	 case GADGET_DATA_TYPE_PALETTE :
	    {
	       struct PaletteData  *pd = (VOID *)(egad + 1);

	       value = pd->pd_ActiveColor;
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  ULONG offset = pd->pd_ColorOffset, max = pd->pd_MaxColors,
			active = (ULONG)data3;

		  if (active < offset) {
		     active = offset;
		  } else {
		     if (active >= (max + offset)) {
			active = max + offset - 1;
		     }
		  }
		  pd->pd_ActiveColor = active;
		  change_gadget(egad);
	       }
	    }
	    break;
      }

      /* Finally refresh gadget if necessary */
      if (refresh == TRUE) {
	 refresh_gadget(egad);
      }
   }
   return(value);
}
	/* Activate string or integer input gadget */

   VOID
activate_input_gadget(struct GadgetList  *gl, USHORT data_entry)
{
   if (gl && gl->gl_ID == ISUP_ID && data_entry < gl->gl_DataEntries &&
			      (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED)) {
      struct ExtendedGadget  *egad = gl->gl_Gadgets[data_entry];
      USHORT type = egad->eg_DataType;

      if (type == GADGET_DATA_TYPE_STRING ||
					 type == GADGET_DATA_TYPE_INTEGER) {
	 struct Gadget  *gad = &egad->eg_Gadget;

	 /* If selected input gadget disabled then activate next one */
	 if (gad->Flags & GADGDISABLED) {
	    if (egad->eg_DataFlags & GADGET_DATA_FLAG_INPUT_AUTO_ACTIVATE) {
	       struct InputData  *id = (struct InputData  *)(egad + 1);

	       /* !!! Recurrence (in german: Rekursion) !!! */
	       activate_input_gadget(gl, id->id_ActivateNext);
	    }
	 } else {
	    struct Window  *win = gl->gl_Window;

	    /* First bring screen + window to front */
	    if (IntuitionBase->ActiveScreen != win->WScreen) {
	       ScreenToFront(win->WScreen);
	    }

	    /* Now activate window + input gadget */
	    if (IntuitionBase->ActiveWindow != win) {
	       WindowToFront(win);
	       ActivateWindow(win);
	       timer_delay(0L, 500000L);
	    }
	    ActivateGadget(gad, win, (LONG)NULL);
	 }
      }
   }
}
	/* Return gadget address */

   struct Gadget *
gadget_address(struct GadgetList  *gl, USHORT data_entry)
{
   struct Gadget  *gad = NULL;

   if (gl && gl->gl_ID == ISUP_ID && data_entry < gl->gl_DataEntries) {
      gad = &gl->gl_Gadgets[data_entry]->eg_Gadget;
   }
   return(gad);
}
	/* Remove gadget list from display */

   struct Window *
remove_gadgets(struct GadgetList  *gl)
{
   struct Window  *win = NULL;

   if (gl && gl->gl_ID == ISUP_ID &&
			      (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED)) {
      struct VisibleGadgetLists  *vg = &visible_gadget_lists;
      struct IntuiMessage        *succ, *msg;
      USHORT i, data_entries = gl->gl_DataEntries;

      /* Mark gadget list as not displayed */
      win           = gl->gl_Window;
      gl->gl_Window = NULL;
      gl->gl_Flags &= ~GADGET_LIST_FLAG_DISPLAYED;

      /* Decrement visible gadget list counter */
      ObtainSemaphore(&vg->vg_Semaphore);
      Remove((struct Node *)&gl->gl_MinNode);
      vg->vg_Count--;
      ReleaseSemaphore(&vg->vg_Semaphore);

      /* Remove gadgets from windows gadget list */
      for (i = 0; i < data_entries; i++) {
	 struct ExtendedGadget  *egad = gl->gl_Gadgets[i];

	 do {
	    RemoveGList(win, &egad->eg_Gadget, 1L);
	 } while (egad = egad->eg_NextGadget);
      }

      /* Reply all ISUP intuition messages belonging to this window */
      Forbid();
      msg = (struct IntuiMessage *)win->UserPort->mp_MsgList.lh_Head;
      while (succ = (struct IntuiMessage *)msg->ExecMessage.mn_Node.ln_Succ) {
	 if (msg->IDCMPWindow == win) {
	    switch (msg->Class) {
	       case GADGETDOWN :
	       case GADGETUP :
	       case MOUSEMOVE :
	       case INTUITICKS :
	       case VANILLAKEY :	/* for compatibility to < v4.2 only */
	       case RAWKEY :
		  Remove((struct Node *)msg);
		  ReplyMsg((struct Message *)msg);
		  break;
	    }
	 }
	 msg = succ;
      }
      Permit();
   }
   return(win);
}
	/* Get intuition msg and perform any action to isup gadgets */

   struct IntuiMessage *
get_msg(struct MsgPort  *uport)
{
   struct IntuiMessage  *imsg = NULL;

   if (uport) {
      struct IntuiMessage  *real_imsg;

      while (imsg == NULL && (real_imsg = (struct IntuiMessage *)
							   GetMsg(uport))) {
	 struct ExtendedGadget  *egad;

	 switch (real_imsg->Class) {
	    case GADGETDOWN :
	       egad = (struct ExtendedGadget *)real_imsg->IAddress;
	       if (egad->eg_Gadget.MutualExclude == ISUP_ID) {
		  USHORT activation = egad->eg_Gadget.Activation;

		  /* Perform gadget action and check activation flags */
		  imsg = perform_gadget_action(egad, real_imsg);
		  if ((activation & FOLLOWMOUSE) ||
			       (activation & (GADGIMMEDIATE | RELVERIFY)) ==
					      (GADGIMMEDIATE | RELVERIFY)) {
		     struct GadgetList  *gl = egad->eg_GadgetList;

		     /* Save active gadget and set auto repeat delay counter */
		     gl->gl_ActiveGadget    = egad;
		     gl->gl_AutoRepeatDelay = ARROW_AUTO_REPEAT_START_DELAY;
		  }
	       } else {
		  egad = NULL;   /* return imsg later */
	       }
	       break;

	    case GADGETUP :
	       egad = (struct ExtendedGadget *)real_imsg->IAddress;
	       if (egad->eg_Gadget.MutualExclude == ISUP_ID) {
		  imsg = perform_gadget_action(egad, real_imsg);

		  /* Reset saved active gadget */
		  egad->eg_GadgetList->gl_ActiveGadget = NULL;
 	       } else {
		  egad = NULL;   /* return imsg later */
	       }
	       break;

	    case MOUSEMOVE :
	       if (egad = get_active_gadget(real_imsg->IDCMPWindow)) {
		  imsg = perform_gadget_action(egad, real_imsg);
	       }
	       break;

	    case INTUITICKS :
	       if ((egad = get_active_gadget(real_imsg->IDCMPWindow))
				    && (egad->eg_Gadget.Flags &	SELECTED)) {
		  if ((egad->eg_Gadget.Activation & (GADGIMMEDIATE |
			       RELVERIFY)) == (GADGIMMEDIATE | RELVERIFY)) {
		     struct GadgetList  *gl = egad->eg_GadgetList;

		     /* Check and decrement auto repeat delay counter */
		     if (gl->gl_AutoRepeatDelay) {
			gl->gl_AutoRepeatDelay--;
		     } else {
			imsg = perform_gadget_action(egad, real_imsg);
		     }
		  }
	       }
	       break;

	    case VANILLAKEY :	/* for compatibility to < v4.3 only */
	       if (egad = get_hotkey_gadget(real_imsg->IDCMPWindow,
							 real_imsg->Code)) {
		  if (!(egad->eg_Gadget.Flags & GADGDISABLED)) {
		     imsg = perform_hotkey_action(egad, real_imsg);
		  }
	       }
	       break;

	    case RAWKEY :
	       if (egad = get_hotkey_gadget(real_imsg->IDCMPWindow,
				      convert_rawkey_to_ascii(real_imsg))) {
		  if (!(egad->eg_Gadget.Flags & GADGDISABLED)) {
		     imsg = perform_hotkey_action(egad, real_imsg);
		  }
	       }
	       break;

	    default :
	       if (egad = get_active_gadget(real_imsg->IDCMPWindow)) {
		  egad->eg_GadgetList->gl_ActiveGadget = NULL;
		  egad = NULL;   /* return imsg later */
	       }
	       break;
	 }
	 if (!imsg) {

	    /* If isup msg then reply it, else return it */
	    if (egad) {
	       ReplyMsg((struct Message *)real_imsg);
	    } else {
	       imsg = real_imsg;
	    }
	 }
      }
   }
   return(imsg);
}
	/* Reply intuition message and free ISUP message if any */

   VOID
reply_msg(struct IntuiMessage  *imsg)
{
   if (imsg) {
      if (imsg->Class == ISUP_ID) {
	 struct IntuiMessage  *real_imsg = *(struct IntuiMessage **)
								 (imsg + 1);
	 /* Free ISUP msg */
	 FreeMem(imsg, (LONG)(sizeof(struct IntuiMessage) +
					    sizeof(struct IntuiMessage *)));
	 /* Return real intui msg (ptr direct after ISUP msg) */
	 imsg = real_imsg;
      }
      ReplyMsg((struct Message *)imsg);
   }
}
	/* Perform ISUP gadget action */

   STATIC struct IntuiMessage *
perform_gadget_action(struct ExtendedGadget  *egad,
					    struct IntuiMessage  *real_imsg)
{
   struct IntuiMessage    *imsg = NULL;
   struct Gadget          *gad = &egad->eg_Gadget;
   struct GadgetList      *gl = egad->eg_GadgetList;
   USHORT data_entry = egad->eg_DataEntry;
   struct ExtendedGadget  *real_egad = gl->gl_Gadgets[data_entry];
   struct MXData          *mx;
   struct ListViewData    *lv;
   struct PaletteData     *pd;
   VOID   *data = (VOID *)(real_egad + 1);
   USHORT type = egad->eg_Type;
   ULONG  min, max, num, flags = real_egad->eg_DataFlags, value = 0,
	  class = real_imsg->Class, qualifier = real_imsg->Qualifier;

   switch (type) {
      case EXTENDED_GADGET_TYPE_BUTTON :
	 if ((flags & GADGET_DATA_FLAG_BUTTON_TOGGLE) &&
						  (gad->Flags & SELECTED)) {
	    /* Prepare gadget state manually (selected) */
	    change_gadget(real_egad);
	    value = 1;
	 }
	 change_gadget(real_egad);
	 break;

      case EXTENDED_GADGET_TYPE_CHECK :
	 if (gad->Flags & SELECTED) {
	    value = 1;
	 }
	 break;

      case EXTENDED_GADGET_TYPE_MX :
	 mx = data;
	 if (mx->mx_ActiveEntry != egad->eg_Gadget.GadgetID) {
	    mx->mx_ActiveEntry = value = egad->eg_Gadget.GadgetID;
	 } else {
	    real_imsg = NULL;
	 }
	 change_gadget(real_egad);
	 break;

      case EXTENDED_GADGET_TYPE_STRING :
      case EXTENDED_GADGET_TYPE_INTEGER :
	 if (!(qualifier & QUALIFIER_ALT) &&
			   (flags & GADGET_DATA_FLAG_INPUT_AUTO_ACTIVATE)) {
	    struct InputData  *id = data;
	    USHORT next_input;

	    if (qualifier & QUALIFIER_SHIFT) {
	       next_input = id->id_ActivatePrev;
	    } else {
	       next_input = id->id_ActivateNext;
	    }
	    activate_input_gadget(gl, next_input);
	 }
	 if (type == EXTENDED_GADGET_TYPE_STRING) {
	    value = (ULONG)((struct StringInfo *)gad->SpecialInfo)->Buffer;
	 } else {
	    value = (ULONG)((struct StringInfo *)gad->SpecialInfo)->LongInt;
	 }
	 break;

      case EXTENDED_GADGET_TYPE_SLIDER_PROP :
	 if (class == GADGETUP) {
	    change_gadget(real_egad);
	    real_imsg = NULL;
	 } else {
	    struct SliderData  *sl = data;
	    struct PropInfo    *pi = (struct PropInfo *)gad->SpecialInfo;

	    min = sl->sl_Min;
	    max = (LONG)sl->sl_Max - (LONG)min;
	    if (flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	       num = max - ((LONG)get_prop_pos(max + 1, 1L, pi->VertPot) -
								 (LONG)min);
	    } else {
	       num = (LONG)get_prop_pos(max + 1, 1L, pi->HorizPot) +
								  (LONG)min;
	    }
	    if (sl->sl_Level != num) {
	       sl->sl_Level = value = num;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 break;

      case EXTENDED_GADGET_TYPE_SCROLLER_PROP :
	 if (class == GADGETUP) {
	    change_gadget(real_egad);
	    real_imsg = NULL;
	 } else {
	    struct ScrollerData  *sc = data;
	    struct PropInfo      *pi = (struct PropInfo *)gad->SpecialInfo;

	    min = sc->sc_Visible;
	    max = sc->sc_Total;
	    if (flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	       num = get_prop_pos(max, min, pi->VertPot);
	    } else {
	       num = get_prop_pos(max, min, pi->HorizPot);
	    }
	    if (sc->sc_Top != num) {
	       sc->sc_Top = value = num;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 break;

      case EXTENDED_GADGET_TYPE_SCROLLER_LEFT :
      case EXTENDED_GADGET_TYPE_SCROLLER_DOWN :
	 if (class == GADGETUP) {
	    real_imsg = NULL;
	 } else {
	    struct ScrollerData  *sc = data;

	    num = sc->sc_Top;
	    if (num > 0) {
	       sc->sc_Top = value = num - 1;
	       change_gadget(real_egad);
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 break;

      case EXTENDED_GADGET_TYPE_SCROLLER_RIGHT :
      case EXTENDED_GADGET_TYPE_SCROLLER_UP :
	 if (class == GADGETUP) {
	    real_imsg = NULL;
	 } else {
	    struct ScrollerData  *sc = data;

	    num = sc->sc_Top;
	    min = sc->sc_Visible;
	    max = sc->sc_Total;
	    num++;
	    if (num < max && (max - num) >= min) {
	       sc->sc_Top = value = num;
	       change_gadget(real_egad);
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 break;

      case EXTENDED_GADGET_TYPE_CYCLE :
	 if (change_cycle_gadget(real_egad, qualifier) == TRUE) {
	    value = ((struct CycleData *)data)->cy_ActiveEntry;
	 } else {
	    real_imsg = NULL;
	 }
	 break;

      case EXTENDED_GADGET_TYPE_COUNT :
	 change_count_gadget(real_egad);
	 value = ((struct CountData *)data)->co_Value;
	 break;

      case EXTENDED_GADGET_TYPE_LISTVIEW_PROP :
	 if (class == GADGETUP) {
	    change_gadget(real_egad);
	 } else {
	    lv  = data;
	    min = lv->lv_VisibleEntries;
	    max = lv->lv_ListEntries;
	    num = get_prop_pos(max, min, ((struct PropInfo *)
						gad->SpecialInfo)->VertPot);
	    if ((min = lv->lv_TopEntry) != num) {
	       struct Node  *node = lv->lv_TopNode;
	       USHORT i;

	       if (min > num) {
		  for (i = min - num; i > 0; i--) {
		     node = node->ln_Pred;
		  }
	       } else {
		  for (i = num - min; i > 0; i--) {
		     node = node->ln_Succ;
		  }
	       }
	       lv->lv_TopNode  = node;
	       lv->lv_TopEntry = num;
	       if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
		  print_list_view_text(egad);
	       }
	    }
	 }
	 real_imsg = NULL;
	 break;

      case EXTENDED_GADGET_TYPE_LISTVIEW_UP :
	 if (class != GADGETUP) {
	    lv  = data;
	    num = lv->lv_TopEntry;
	    if (num > 0) {
	       lv->lv_TopNode  = lv->lv_TopNode->ln_Pred;
	       lv->lv_TopEntry = value = num - 1;
	       change_gadget(real_egad);
	    }
	 }
	 real_imsg = NULL;
	 break;

      case EXTENDED_GADGET_TYPE_LISTVIEW_DOWN :
	 if (class != GADGETUP) {
	    lv  = data;
	    min = lv->lv_VisibleEntries;
	    max = lv->lv_ListEntries;
	    num = lv->lv_TopEntry + 1;
	    if (num < max && (max - num) >= min) {
	       lv->lv_TopNode  = lv->lv_TopNode->ln_Succ;
	       lv->lv_TopEntry = value = num;
	       change_gadget(real_egad);
	    }
	 }
	 real_imsg = NULL;
	 break;

      case EXTENDED_GADGET_TYPE_LISTVIEW_ENTRY :
	 lv  = data;
	 max = lv->lv_ListEntries;
	 num = lv->lv_TopEntry + egad->eg_Gadget.GadgetID;
	 if (num < max) {
	    lv->lv_SelectedEntry = value = num;
	    if (flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
	       change_gadget(real_egad);
	    }
	 } else {
	    real_imsg = NULL;
	 }
	 break;

      case EXTENDED_GADGET_TYPE_PALETTE_INDICATOR :
	 real_imsg = NULL;
	 break;

      case EXTENDED_GADGET_TYPE_PALETTE_COLOR :
	 pd                 = data;
	 pd->pd_ActiveColor = value = egad->eg_Gadget.GadgetID;
	 change_gadget(real_egad);
	 break;
   }
   if (real_imsg) {
      imsg = create_intui_message(real_imsg, gl, data_entry, value);
   }
   return(imsg);
}
	/* Perform hotkey gadget action */

   STATIC struct IntuiMessage *
perform_hotkey_action(struct ExtendedGadget  *egad,
					    struct IntuiMessage  *real_imsg)
{
   struct IntuiMessage    *imsg = NULL;
   struct Gadget          *gad = &egad->eg_Gadget;
   struct GadgetList      *gl = egad->eg_GadgetList;
   struct Window          *win = gl->gl_Window;
   struct RastPort        *rp = win->RPort;
   USHORT data_entry = egad->eg_DataEntry;
   struct ExtendedGadget  *real_egad = gl->gl_Gadgets[data_entry];
   struct MXData          *mx;
   struct SliderData      *sl;
   struct ScrollerData    *sc;
   struct CycleData       *cy;
   struct CountData       *co;
   struct ListViewData    *lv;
   struct PaletteData     *pd;
   VOID  *data = (VOID *)(real_egad + 1);
   ULONG min, max, num, flags = real_egad->eg_DataFlags, value = 0,
	 qualifier = real_imsg->Qualifier & QUALIFIER_SHIFT;

   switch (egad->eg_Type) {
      case EXTENDED_GADGET_TYPE_BUTTON :
	 if (flags & GADGET_DATA_FLAG_BUTTON_TOGGLE) {
	    if (gad->Flags & SELECTED) {

	       /* Prepare gadget state manually (selected) */
	       change_gadget(real_egad);
	       gad->Flags &= ~SELECTED;
	    } else {
	       gad->Flags |= SELECTED;
	       value       = 1;
	    }
	 } else {
	    if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
	       gad->Flags |= SELECTED;
	       change_gadget(real_egad);
	       timer_delay(0L, 100000L);

	       /* Prepare gadget state manually (unselected) */
	       change_gadget(real_egad);
	       gad->Flags &= ~SELECTED;
	    }
	 }
	 change_gadget(real_egad);
	 break;

      case EXTENDED_GADGET_TYPE_CHECK :
	 if (gad->Flags & SELECTED) {
	    gad->Flags &= ~SELECTED;
	 } else {
	    gad->Flags |= SELECTED;
	    value       = 1;
	 }
	 change_gadget(real_egad);
	 break;

      case EXTENDED_GADGET_TYPE_MX :
	 mx  = data;
	 max = mx->mx_TextEntries;
	 num = mx->mx_ActiveEntry;
	 if (qualifier) {
	    if (num > 0) {
	       num--;
	    } else {
	       num = max - 1;
	    }
	 } else {
	    if (num < (max - 1)) {
	       num++;
	    } else {
	       num = 0;
	    }
	 }
	 mx->mx_ActiveEntry = value = num;
	 change_gadget(real_egad);
	 break;

      case EXTENDED_GADGET_TYPE_STRING :
      case EXTENDED_GADGET_TYPE_INTEGER :
	 activate_input_gadget(gl, data_entry);
	 real_imsg = NULL;
	 break;

      case EXTENDED_GADGET_TYPE_SLIDER_PROP :
	 sl  = data;
	 min = sl->sl_Min;
	 max = sl->sl_Max;
	 num = sl->sl_Level;
	 if (qualifier) {
	    if ((LONG)num > (LONG)min) {
	       num--;
	    } else {
	       real_imsg = NULL;
	    }
	 } else {
	    if ((LONG)num < (LONG)max) {
	       num++;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 if (real_imsg) {
	    sl->sl_Level = value = num;
	    change_gadget(real_egad);
	 }
	 break;

      case EXTENDED_GADGET_TYPE_SCROLLER_PROP :
	 sc  = data;
	 min = sc->sc_Visible;
	 max = sc->sc_Total;
	 num = sc->sc_Top;
	 if (qualifier) {
	    if (num > 0) {
	       num--;
	    } else {
	       real_imsg = NULL;
	    }
	 } else {
	    if (num < (max - min)) {
	       num++;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 if (real_imsg) {
	    sc->sc_Top = value = num;
	    change_gadget(real_egad);
	 }
	 break;

      case EXTENDED_GADGET_TYPE_CYCLE :
	 cy  = data;
	 max = cy->cy_TextEntries;
	 num = cy->cy_ActiveEntry;
	 if (qualifier) {
	    if (num > 0) {
	       num--;
	    } else {
	       num = max - 1;
	    }
	 } else {
	    if (num < (max - 1)) {
	       num++;
	    } else {
	       num = 0;
	    }
	 }
	 if (cy->cy_ActiveEntry != num) {
	    cy->cy_ActiveEntry = value = num;
	    change_gadget(real_egad);
	 } else {
	    real_imsg = NULL;
	 }
	 break;

      case EXTENDED_GADGET_TYPE_COUNT :
	 co  = data;
	 min = co->co_Min;
	 max = co->co_Max;
	 num = co->co_Value;
	 if (qualifier) {
	    if ((LONG)num > (LONG)min) {
	       num--;
	    } else {
	       real_imsg = NULL;
	    }
	 } else {
	    if ((LONG)num < (LONG)max) {
	       num++;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 if (real_imsg) {
	    co->co_Value = value = num;
	    change_gadget(real_egad);
	 }
	 break;

      case EXTENDED_GADGET_TYPE_LISTVIEW_PROP :
	 lv  = data;
	 min = lv->lv_VisibleEntries;
	 max = lv->lv_ListEntries;
	 if (flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
	    num = lv->lv_SelectedEntry;
	 } else {
	    num = lv->lv_TopEntry;
	 }
	 if (qualifier) {
	    if (num > 0) {
	       num--;
	    } else {
	       real_imsg = NULL;
	    }
	 } else {
	    if (flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
	       if (++num >= max) {
		  real_imsg = NULL;
	       }
	    } else {
	       if (++num >= max || (max - num) < min) {
		  real_imsg = NULL;
	       }
	    }
	 }
	 if (real_imsg) {
	    set_gadget_attributes(gl, data_entry, 0L, 0L, USE_CURRENT_VALUE,
					    num, (VOID *)USE_CURRENT_VALUE);
	    if (flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
	       value = lv->lv_SelectedEntry;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 break;

      case EXTENDED_GADGET_TYPE_PALETTE_INDICATOR :
      case EXTENDED_GADGET_TYPE_PALETTE_COLOR :
	 pd  = data;
	 min = pd->pd_ColorOffset;
	 max = pd->pd_MaxColors;
	 num = pd->pd_ActiveColor;
	 if (qualifier) {
	    if ((LONG)num > (LONG)min) {
	       num--;
	    } else {
	       num = min + max - 1;
	    }
	 } else {
	    if ((LONG)num < (LONG)(min + max - 1)) {
	       num++;
	    } else {
	       num = min;
	    }
	 }
	 pd->pd_ActiveColor = value = num;
	 change_gadget(real_egad);
	 break;
   }
   if (real_imsg) {
      imsg = create_intui_message(real_imsg, gl, data_entry, value);
   }
   return(imsg);
}
	/* Create special ISUP intui message */

   STATIC struct IntuiMessage *
create_intui_message(struct IntuiMessage  *real_imsg, struct GadgetList  *gl,
					     USHORT data_entry,	ULONG value)
{
   struct IntuiMessage  *imsg = NULL;

   if (!(imsg = AllocMem((LONG)(sizeof(struct IntuiMessage) +
			    sizeof(struct IntuiMessage *)), MEMF_PUBLIC))) {
      imsg = real_imsg;
   } else {

      /* Init ISUP intui message */
      CopyMem((BYTE *)real_imsg, (BYTE *)imsg, sizeof(struct IntuiMessage));
      imsg->Class       = ISUP_ID;
      imsg->Code        = data_entry;
      imsg->IAddress    = (APTR)value;
      imsg->SpecialLink = (struct IntuiMessage *)gl;

      /* Put real IntuiMessage ptr direct after ISUP intuition message */
      *(struct IntuiMessage **)(imsg + 1) = real_imsg;
   }
   return(imsg);
}
	/* Get active gadget for given window */

   STATIC struct ExtendedGadget *
get_active_gadget(struct Window  *win)
{
   struct VisibleGadgetLists  *vg = &visible_gadget_lists;
   struct ExtendedGadget      *egad = NULL;
   USHORT count;

   ObtainSemaphore(&vg->vg_Semaphore);
   if (count = vg->vg_Count) {
      struct GadgetList  *gl = (struct GadgetList *)vg->vg_MinList.mlh_Head;
      USHORT i;

      for (i = 0; i < count; i++) {
	 if (gl->gl_Window == win && gl->gl_ActiveGadget) {
	    egad = gl->gl_ActiveGadget;
	    break;

	 }
	 gl = (struct GadgetList *)gl->gl_MinNode.mln_Succ;
      }
   }
   ReleaseSemaphore(&vg->vg_Semaphore);
   return(egad);
}
	/* Get hotkey gadget for given window */

   STATIC struct ExtendedGadget *
get_hotkey_gadget(struct Window  *win, USHORT hotkey)
{
   struct ExtendedGadget  *result_egad = NULL;

   /* First check for valid hotkey (printable character) */
   hotkey = tolower(hotkey);
   if (hotkey >= ' ' && hotkey <= '~') {
      struct VisibleGadgetLists  *vg = &visible_gadget_lists;
      USHORT count;

      /* Now check if any visible gadget list exists */
      ObtainSemaphore(&vg->vg_Semaphore);
      if (count = vg->vg_Count) {
	 struct GadgetList  *gl = (struct GadgetList *)
						    vg->vg_MinList.mlh_Head;
	 USHORT i;
	 BOOL   found = FALSE;

	 /* Search all gadget lists displayed on given window */
	 for (i = 0; i < count && found == FALSE; i++) {
	    if (gl->gl_Window == win) {
	       USHORT j, data_entries = gl->gl_DataEntries;

	       /* Search gadget with given hotkey */
	       for (j = 0; j < data_entries && found == FALSE; j++) {
		  struct ExtendedGadget  *egad = gl->gl_Gadgets[j];

		  if (egad->eg_Flags & EXTENDED_GADGET_FLAG_HOTKEY &&
						egad->eg_Hotkey == hotkey) {
		     result_egad = egad;
		     found       = TRUE;
		  }
	       }
	    }
	    gl = (struct GadgetList *)gl->gl_MinNode.mln_Succ;
	 }
      }
      ReleaseSemaphore(&vg->vg_Semaphore);
   }
   return(result_egad);
}
	/* Remove visible gadget lists from given window */

   USHORT
remove_visible_gadget_lists(struct Window  *win, struct MinList  *list)
{
   struct GadgetList  *gl;
   USHORT count = 0;

   NewList((struct List *)list);
   while (gl = get_first_gadget_list(win)) {
      remove_gadgets(gl);
      AddHead((struct List *)list, (struct Node *)&gl->gl_MinNode);
      count++;
   }
   return(count);
}
	/* Find first visible gadget list for given window */

   STATIC struct GadgetList  *
get_first_gadget_list(struct Window  *win)
{
   struct VisibleGadgetLists  *vg = &visible_gadget_lists;
   struct GadgetList          *gl, *gl_return = NULL;

   ObtainSemaphore(&vg->vg_Semaphore);
   gl = (struct GadgetList *)&vg->vg_MinList.mlh_Head;
   while (gl->gl_MinNode.mln_Succ) {
      if (gl->gl_Window == win) {
	 gl_return = gl;
	 break;
      }
      gl = (struct GadgetList *)gl->gl_MinNode.mln_Succ;
   }
   ReleaseSemaphore(&vg->vg_Semaphore);
   return(gl_return);
}
	/* Display visible gadget lists on given window */

   VOID
display_visible_gadget_lists(struct Window  *win, struct MinList  *list)
{
   struct GadgetList  *gl;

   while (gl = (struct GadgetList *)RemHead((struct List *)list)) {
      display_gadgets(win, gl);
   }
}
	/* Convert given RAWKEY IntuiMessage to ASCII */

   UBYTE
convert_rawkey_to_ascii(struct IntuiMessage  *im)
{
   UBYTE key = 0;

   /* First check for RAWKEY event */
   if (im->Class == RAWKEY) {
      struct InputEvent  input_event, *ie = &input_event;
      BYTE buffer[CONVERT_BUFFER_SIZE];

      /* Build InputEvent for console device */
      ie->ie_NextEvent    = NULL;
      ie->ie_Class        = IECLASS_RAWKEY;
      ie->ie_SubClass     = IECLASS_NULL;
      ie->ie_Code         = im->Code;
      ie->ie_Qualifier    = im->Qualifier;
      ie->ie_EventAddress = *((APTR *)im->IAddress);

      /* Convert it and check number of returned keys */
      if (RawKeyConvert(ie, &buffer[0], CONVERT_BUFFER_SIZE, NULL) == 1) {
	 key = (UBYTE)buffer[0];
      }
   }
   return(key);
}
