/* $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 .: 1
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	28-Dec-91   Torsten Jürgeleit      correct highliting of toggle 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>
#include <functions.h>
#include <libraries/memwatch.h>		/* AFTER functions.h */
#include <string.h>
#include "/render/render.h"
#include "/texts/texts.h"
#include "/language/language.h"
#include "/pointer/pointer.h"
#include "gadgets.h"
#include "imports.h"

	/* 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_Data              = gd;
	 gl->gl_DataEntries       = data_entries;
	 gl->gl_LanguageTextArray = language_text_array;
	 if ((gadget_buffer_size = get_gadget_buffer_size(gl)) &&
			       (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)) {
	       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, 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 VisibleGadgetLists  *vg = &visible_gadget_lists;
      struct ExtendedGadget      *last_egad = NULL;
      USHORT i, data_entries = gl->gl_DataEntries;

      /* 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; i < data_entries; i++) {
	 struct ExtendedGadget  *egad = gl->gl_Gadgets[i];

	 /* Move mouse pointer to this ISUP object */
	 if (egad->eg_Flags & EXTENDED_GADGET_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 ISUP object */
	 do {
	    if (last_egad) {
	       last_egad->eg_Gadget.NextGadget = &egad->eg_Gadget;
	    }
	    last_egad = egad;
	 } while (egad = egad->eg_NextGadget);
      }
      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]);
      }
   }
}
	/* Set gadget attributes */

   ULONG
set_gadget_attributes(struct GadgetList  *gl, USHORT data_entry,
			 ULONG flags, 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;

      /* Get gadget data */
      if (data1 != USE_CURRENT_VALUE || data2 != USE_CURRENT_VALUE ||
				       data3 != (VOID *)USE_CURRENT_VALUE) {
	 struct GadgetData    *gd = gl->gl_Data + data_entry;
	 struct MXData        *mx;
	 struct SliderData    *sl;
	 struct ScrollerData  *sc;
	 struct CycleData     *cy;
	 struct CountData     *co;
	 struct ListViewData  *lv;
	 struct PaletteData   *pd;
	 struct StringInfo    *sinfo;
	 VOID   *data = (VOID *)(egad + 1);
	 USHORT type = gd->gd_Type;
	 BYTE   *buffer;
	 ULONG  min, max, num;

	 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 :
	       mx    = data;
	       value = mx->mx_ActiveEntry;
	       if (data2 != USE_CURRENT_VALUE) {
		  max = mx->mx_TextEntries;
		  num = data2;
		  if (num >= max) {
		     num = max - 1;   /* activate last entry */
		  }
		  mx->mx_ActiveEntry = num;
		  change_gadget(egad);
	       }
	       break;

	    case GADGET_DATA_TYPE_STRING :
	    case GADGET_DATA_TYPE_INTEGER :
	       sinfo  = (struct StringInfo *)gad->SpecialInfo;
	       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;

		  max = sinfo->MaxChars;
		  if (type == GADGET_DATA_TYPE_STRING) {
		     if (gd->gd_Flags &
				     GADGET_DATA_FLAG_STRING_UNSIGNED_DEC) {
			if ((LONG)input != -1L) {
			   min = convert_unsigned_dec((ULONG)input, buffer);
			} else {
			   min = 0;
			}
		     } else {
			if (gd->gd_Flags &
				       GADGET_DATA_FLAG_STRING_SIGNED_DEC) {
			   if ((LONG)input != -1L) {
			      min = convert_signed_dec((ULONG)input, buffer);
			   } else {
			      min = 0;
			   }
			} else {
			   if (gd->gd_Flags & GADGET_DATA_FLAG_STRING_HEX) {
			      if ((LONG)input != -1L) {
				 min = convert_hex((ULONG)input, buffer);
			      } else {
				 min = 0;
			      }
			   } else {
			      if (gd->gd_Flags &
					      GADGET_DATA_FLAG_STRING_BIN) {
				 if ((LONG)input != -1L) {
				    min = convert_bin((ULONG)input, buffer);
				 } else {
				    min = 0;
				 }
			      } else {
				 if (input) {
				    if ((min = strlen(input)) > max) {
				       min = max;
				    }
				    strncpy(buffer, input, (size_t)min);
				 } else {
				    min = 0;
				 }
			      }
			   }
			}
		     }
		  } else {
		     if ((LONG)input != -1L) {
			min = convert_unsigned_dec((ULONG)input, buffer);
		     } else {
			min = 0;
		     }
		  }
		  *(buffer + min)  = '\0';   /* mark end of string */
		  sinfo->BufferPos = 0;
		  change_gadget(egad);
	       }
	       break;

	    case GADGET_DATA_TYPE_SLIDER :
	       sl    = data;
	       value = sl->sl_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) {
		  num = (ULONG)data3;
	       } else {
		  num = sl->sl_Level;
	       }
	       if ((LONG)num < (LONG)min) {
		  num = min;
	       } else {
		  if ((LONG)num > (LONG)max) {
		     num = max;
		  }
	       }
	       sl->sl_Level = num;
	       change_gadget(egad);
	       break;

	    case GADGET_DATA_TYPE_SCROLLER :
	       sc    = data;
	       value = sc->sc_Top;
	       if (data1 != USE_CURRENT_VALUE) {
		  min = sc->sc_Visible = data1;
	       } else {
		  min = sc->sc_Visible;
	       }
	       if (data2 != USE_CURRENT_VALUE) {
		  max = sc->sc_Total = data2;
	       } else {
		  max = sc->sc_Total;
	       }
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  num = (ULONG)data3;
	       } else {
		  num = sc->sc_Top;
	       }
	       if (num >= max || (max - num) < min) {
		  num = (max > min ? max - min : min - max);
	       }
	       sc->sc_Top = num;
	       change_gadget(egad);
	       break;

	    case GADGET_DATA_TYPE_CYCLE :
	       cy    = data;
	       value = cy->cy_ActiveEntry;
	       if (data2 != USE_CURRENT_VALUE) {
		  max = cy->cy_TextEntries;
		  num = data2;
		  if (num > max) {
		     num = max;   /* activate last entry */
		  }
		  cy->cy_ActiveEntry = num;
		  change_gadget(egad);
	       }
	       break;

	    case GADGET_DATA_TYPE_COUNT :
	       co    = data;
	       value = co->co_Value;
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  min = co->co_Min;
		  max = co->co_Max;
		  num = (ULONG)data3;
		  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 :
	       lv = data;
	       if (lv->lv_Flags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
		  value = lv->lv_SelectedEntry;
	       } else {
		  value = lv->lv_TopEntry;
	       }
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  struct List  *list = data3;
		  struct Node  *node;

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

		  list = lv->lv_List;
		  min  = lv->lv_VisibleEntries;
		  max  = lv->lv_ListEntries;

		  /* Search top list node */
		  if (!max) {
		     sel  = 0;
		     num  = 0;
		     node = NULL;
		  } else {
		     if (lv->lv_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 >= max) {
			   sel = max - 1;
			}
			if (sel && max > min) {
			   if (sel > (max - min)) {
			      USHORT i;

			      num  = max - min;
			      node = list->lh_TailPred;
			      for (i = max - num - 1; i; i--) {
				 node = node->ln_Pred;
			      }
			   } else {
			      node = list->lh_Head;
			      if (sel > (min >> 1)) {
				 USHORT i;

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

			/* Calc top entry ignoring selected entry */
			if (data2 != USE_CURRENT_VALUE) {
			   num = data2;
			} else {
			   num = lv->lv_TopEntry;
			}
			if (num && max > min) {
			   if (num > max || (max - num) < min) {
			      USHORT i;

			      num  = max - min;
			      node = list->lh_TailPred;
			      for (i = max - num - 1; i; i--) {
				 node = node->ln_Pred;
			      }
			   } else {
			      USHORT i;

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

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

		  /* Set GADGHNONE flag for all entry gadgets without list node */
		  if (!(lv->lv_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 < min; i++, num++) {
			struct Gadget  *temp_gad = &temp_egad->eg_Gadget;

			if (num < max) {
			   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 :
	       pd    = data;
	       value = pd->pd_ActiveColor;
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  min = pd->pd_ColorOffset;
		  max = pd->pd_MaxColors;
		  num = (ULONG)data3;
		  if (num < min) {
		     num = min;
		  } else {
		     if (num >= (max + min)) {
			num = max + min - 1;
		     }
		  }
		  pd->pd_ActiveColor = num;
		  change_gadget(egad);
	       }
	       break;
	 }
      }

      /* Toggle able state of gadget */
      if (((flags & GADGET_DATA_FLAG_DISABLED) &&
					    !(gad->Flags & GADGDISABLED)) ||
				    (!(flags & GADGET_DATA_FLAG_DISABLED) &&
					     (gad->Flags & GADGDISABLED))) {
	 struct RastPort        *rp = gl->gl_Window->RPort;
	 struct ExtendedGadget  *temp_egad = egad;
	 UBYTE displayed;

	 if (displayed = gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
	    SetDrMd(rp, (LONG)JAM1);
	    SetAPen(rp, (LONG)gl->gl_RenderInfo->ri_BackPen);
	 }
	 do {
	    struct Gadget  *temp_gad = &temp_egad->eg_Gadget;

	    if (temp_gad->Flags & GADGDISABLED) {
	       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(egad);
      }
   }
   return(value);
}
	/* Activate string or integer imput gadget */

   VOID
activate_input_gadget(struct GadgetList  *gl, USHORT data_entry)
{
   if (gl && gl->gl_ID == ISUP_ID && data_entry < gl->gl_DataEntries) {
      struct GadgetData  *gd = gl->gl_Data + data_entry;
      UBYTE type = gd->gd_Type;

      if (type == GADGET_DATA_TYPE_STRING ||
					 type == GADGET_DATA_TYPE_INTEGER) {
	 struct Gadget  *gad = &gl->gl_Gadgets[data_entry]->eg_Gadget;

	 if (gad->Flags & GADGDISABLED) {
	    if (gd->gd_Flags & GADGET_DATA_FLAG_INPUT_AUTO_ACTIVATE) {

	       /* !!! Recurrence (in german: Rekursion) !!! */
	       activate_input_gadget(gl, (USHORT)
		      gd->gd_SpecialData.gd_InputData.gd_InputActivateNext);
	    }
	 } else {
	    ActivateGadget(gad, gl->gl_Window, (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) {
      if (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 given gadget list */
      Forbid();
      msg = (struct IntuiMessage *)win->UserPort->mp_MsgList.lh_Head;
      while (succ = (struct IntuiMessage *)msg->ExecMessage.mn_Node.ln_Succ) {
	 if (msg->Class == ISUP_ID &&
			      msg->SpecialLink == (struct IntuiMessage *)gl) {
	    Remove((struct Node *)msg);
	    reply_msg(msg);
	 }
	 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) {
		  imsg = perform_gadget_action(egad, real_imsg);
		  if ((egad->eg_Gadget.Activation & FOLLOWMOUSE) ||
			      egad->eg_Gadget.Activation == (GADGIMMEDIATE |
							       RELVERIFY)) {
		     struct GadgetList  *gl;

		     /* Save active gadget and set auto repeat delay counter */
		     gl                     = egad->eg_GadgetList;
		     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)) {
		     struct GadgetList  *gl = egad->eg_GadgetList;

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

	    case VANILLAKEY :
	       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;

	    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 GadgetData      *gd = gl->gl_Data + data_entry;
   struct ExtendedGadget  *real_egad = gl->gl_Gadgets[data_entry];
   struct MXData          *mx;
   struct ListViewData    *lv;
   struct PaletteData     *pd;
   VOID   *data = (VOID *)(real_egad + 1);
   ULONG  min, max, num, value = 0, class = real_imsg->Class,
	  qualifier = real_imsg->Qualifier;
   USHORT type = egad->eg_Type;

   switch (type) {
      case EXTENDED_GADGET_TYPE_BUTTON :
	 if (gd->gd_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) && (gd->gd_Flags &
				    GADGET_DATA_FLAG_INPUT_AUTO_ACTIVATE)) {
	    USHORT next_input;

	    if (qualifier & QUALIFIER_SHIFT) {
	       next_input = gd->gd_SpecialData.gd_InputData.gd_InputActivatePrev;
	    } else {
	       next_input = gd->gd_SpecialData.gd_InputData.gd_InputActivateNext;
	    }
	    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;

	    min = sl->sl_Min;
	    max = (LONG)sl->sl_Max - (LONG)min;
	    if (sl->sl_Flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	       num = max - ((LONG)get_prop_pos(max + 1, 1L,
			   ((struct PropInfo *)gad->SpecialInfo)->VertPot) -
								 (LONG)min);
	    } else {
	       num = (LONG)get_prop_pos(max + 1, 1L, ((struct PropInfo *)
				   gad->SpecialInfo)->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;

	    min = sc->sc_Visible;
	    max = sc->sc_Total;
	    if (sc->sc_Flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	       num = get_prop_pos(max, min, ((struct PropInfo *)
						gad->SpecialInfo)->VertPot);
	    } else {
	       num = get_prop_pos(max, min, ((struct PropInfo *)
					       gad->SpecialInfo)->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 (lv->lv_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;
	 num = egad->eg_Gadget.GadgetID;
	 if (num != pd->pd_ActiveColor) {
	    pd->pd_ActiveColor = value = num;
	    change_gadget(real_egad);
	 } else {
	    real_imsg = NULL;
	 }
	 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 GadgetData      *gd = gl->gl_Data + data_entry;
   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, value = 0, qualifier = real_imsg->Qualifier &
							    QUALIFIER_SHIFT;
   switch (egad->eg_Type) {
      case EXTENDED_GADGET_TYPE_BUTTON :
	 if (gd->gd_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;
	 num = lv->lv_SelectedEntry;
	 if (qualifier) {
	    if (num > 0) {
	       num--;
	    } else {
	       real_imsg = NULL;
	    }
	 } else {
	    if (lv->lv_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, USE_CURRENT_VALUE,
					    num, (VOID *)USE_CURRENT_VALUE);
	    if (lv->lv_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 *)), (LONG)MEMF_PUBLIC | MEMF_CLEAR))) {
      imsg = real_imsg;
   } else {

      /* Init ISUP intui message */
      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 VisibleGadgetLists  *vg = &visible_gadget_lists;
   struct ExtendedGadget      *result_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;
      BOOL   found = FALSE;

      hotkey = toupper(hotkey);
      for (i = 0; i < count && found == FALSE; i++) {
	 if (gl->gl_Window == win) {
	    USHORT j, data_entries = gl->gl_DataEntries;

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

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

   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);
}
