		/*************************************
		 *                                   *
		 *            Gadgets v2.0           *
		 *   by Torsten Jürgeleit in 05/91   *
		 *                                   *
		 *          Routines - Part 1        *
		 *                                   *
		 *************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#include <functions.h>
#include <string.h>
#include "/render/render.h"
#include "/texts/texts.h"
#include "gadgets.h"
#include "imports.h"

	/* Defines */

#define QUALIFIER_SHIFT		(IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define QUALIFIER_ALT		(IEQUALIFIER_LALT | IEQUALIFIER_RALT)

	/* 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
};
	/* Create gadget list */

   struct GadgetList  *
create_gadgets(struct RenderInfo  *ri, struct GadgetData  *gd,
					       SHORT hoffset, SHORT voffset)
{
   struct GadgetList  *gl;
   BYTE   *gadget_buffer, *image_buffer = NULL;
   USHORT data_entries;
   ULONG  gl_size, gadget_buffer_size, image_buffer_size;

   if (ri && gd && (data_entries = count_gadget_data_entries(gd))) {
      gl_size = sizeof(struct GadgetList) + (data_entries - 1) *
					    sizeof(struct ExtendedGadget *);
      if (gl = AllocMem(gl_size, (LONG)MEMF_PUBLIC | MEMF_CLEAR)) {
	 gl->gl_RenderInfo  = ri;
	 gl->gl_Data        = gd;
	 gl->gl_DataEntries = data_entries;
	 if ((gadget_buffer_size = get_gadget_buffer_size(gl)) &&
			(gadget_buffer = AllocMem(gadget_buffer_size, (LONG)
					       MEMF_PUBLIC | MEMF_CLEAR))) {
	    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;
	       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) {
      if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
	 remove_gadgets(gl);
      }
      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)
{
   struct VisibleGadgetLists  *vg = &visible_gadget_lists;
   struct ExtendedGadget      *egad, *last_egad = NULL;
   USHORT i, data_entries = gl->gl_DataEntries;

   if (win && gl && !(gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED)) {
      gl->gl_Window = win;
      gl->gl_Flags |= GADGET_LIST_FLAG_DISPLAYED;
      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++) {
	 egad = gl->gl_Gadgets[i];
	 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)
{
   USHORT i, data_entries = gl->gl_DataEntries;

   if (gl && gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
      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)
{
   struct Window          *win = gl->gl_Window;
   struct RastPort        *rp;
   struct GadgetData      *gd;
   struct ExtendedGadget  *egad, *save_egad;
   struct Gadget          *gad = NULL;
   struct StringInfo      *sinfo;
   struct List            *list;
   struct Node            *node;
   struct MXData          *mx;
   struct SliderData      *sl;
   struct ScrollerData    *sc;
   struct CycleData       *cy;
   struct CountData       *co;
   struct ListViewData    *lv;
   struct PaletteData     *pd;
   VOID   *data;
   UBYTE  displayed;
   BYTE   *buffer, *input, **text;
   ULONG  min, max, num, value = 0;
   USHORT i, type;

   if (gl && data_entry < gl->gl_DataEntries) {
      /* get gadget data */
      gd   = gl->gl_Data + data_entry;
      type = gd->gd_Type;
      egad = save_egad = gl->gl_Gadgets[data_entry];
      gad  = &egad->eg_Gadget;
      data = (VOID *)(egad + 1);
      if (data1 != USE_CURRENT_VALUE || data2 != USE_CURRENT_VALUE ||
				       data3 != (VOID *)USE_CURRENT_VALUE) {
	 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 {
			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) {
		  max   = sinfo->MaxChars;
		  input = data3;
		  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) {
		  text = cy->cy_TextArray;
		  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;
	       value = lv->lv_TopEntry;
	       if ((ULONG)data3 != USE_CURRENT_VALUE) {
		  max  = 0;
		  num  = 0;
		  list = data3;
		  if (list) {
		     /* count list nodes */
		     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) {
		  list = lv->lv_List;
		  min  = lv->lv_VisibleEntries;
		  max  = lv->lv_ListEntries;
		  node = NULL;
		  /* search top list node */
		  if (max) {
		     if (data2 != USE_CURRENT_VALUE) {
			num = data2;
		     } else {
			num = lv->lv_TopEntry;
		     }
		     if (num && max > min) {
			if (num > max || (max - num) < min) {
			   num  = max - min;
			   node = list->lh_TailPred;
			   for (i = max - num - 1; i; i--) {
			      node = node->ln_Pred;
			   }
			} else {
			   node = list->lh_Head;
			   for (i = 0; i < num; i++) {
			      node = node->ln_Succ;
			   }
			}
		     } else {
			num  = 0;
			node = list->lh_Head;
		     }
		  }
		  /* init list view data */
		  lv->lv_TopEntry = num;
		  lv->lv_TopNode  = node;
		  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)) {
		     egad = egad->eg_NextGadget->eg_NextGadget->eg_NextGadget;   /* ptr to first entry gadget */
		     for (i = 0; i < min; i++, num++) {
			gad = &egad->eg_Gadget;
			if (num < max) {
			   gad->Flags = (gad->Flags & ~GADGHIGHBITS) |
								  GADGHCOMP;
			} else {
			   gad->Flags = (gad->Flags & ~GADGHIGHBITS) |
								  GADGHNONE;
			}
			egad = egad->eg_NextGadget;
		     }
		     egad = save_egad;
		  }
	       }
	       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))) {
	 if (displayed = gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
	    rp = win->RPort;
	    SetDrMd(rp, (LONG)JAM1);
	    SetAPen(rp, (LONG)gl->gl_RenderInfo->ri_BackPen);
	 }
	 do {
	    gad = &egad->eg_Gadget;
	    if (gad->Flags & GADGDISABLED) {
	       gad->Flags &= ~GADGDISABLED;
	       /* clear ghosted gadget select box */
	       if (displayed) {
		  RectFill(rp, (LONG)gad->LeftEdge, (LONG)gad->TopEdge,
			      (LONG)(gad->LeftEdge + gad->Width - 1), (LONG)
					  (gad->TopEdge + gad->Height - 1));
	       }
	    } else {
	       gad->Flags |= GADGDISABLED;
	    }
	 } while (egad = egad->eg_NextGadget);
	 refresh_gadget(save_egad);
      }
   }
   return(value);
}
	/* Activate string or integer imput gadget */

   VOID
activate_input_gadget(struct GadgetList  *gl, USHORT data_entry)
{
   struct GadgetData  *gd;
   struct Gadget      *gad;
   UBYTE type;

   if (data_entry < gl->gl_DataEntries) {
      gd   = gl->gl_Data + data_entry;
      type = gd->gd_Type;
      if (type == GADGET_DATA_TYPE_STRING ||
					 type == GADGET_DATA_TYPE_INTEGER) {
	 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;

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

   VOID
remove_gadgets(struct GadgetList  *gl)
{
   struct VisibleGadgetLists  *vg = &visible_gadget_lists;
   struct Window              *win = gl->gl_Window;
   struct ExtendedGadget      *egad;
   USHORT i, data_entries = gl->gl_DataEntries;

   if (gl && data_entries && gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
      ObtainSemaphore(&vg->vg_Semaphore);
      Remove((struct Node *)&gl->gl_MinNode);
      vg->vg_Count--;
      ReleaseSemaphore(&vg->vg_Semaphore);
      for (i = 0; i < data_entries; i++) {
	 egad = gl->gl_Gadgets[i];
	 do {
	    RemoveGList(win, &egad->eg_Gadget, 1L);
	 } while (egad = egad->eg_NextGadget);
      }
      gl->gl_Flags &= ~GADGET_LIST_FLAG_DISPLAYED;
   }
}
	/* Get intuition msg and perform any action to isup gadgets */

   struct IntuiMessage *
get_msg(struct MsgPort  *uport)
{
   struct IntuiMessage    *real_imsg, *imsg = NULL;
   struct GadgetList      *gl;
   struct ExtendedGadget  *egad;

   if (uport) {
      while (imsg == NULL && (real_imsg = (struct IntuiMessage *)
							   GetMsg(uport))) {
	 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)) {
		     /* 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)) {
		     /* check auto repeat delay counter */
		     gl = egad->eg_GadgetList;
		     if (gl->gl_AutoRepeatDelay) {
			gl->gl_AutoRepeatDelay--;
		     } else {
			imsg = perform_gadget_action(egad, real_imsg);
		     }
		  }
	       }
	       break;
	    case VANILLAKEY :
	       if (egad = get_hot_key_gadget(real_imsg->IDCMPWindow,
							 real_imsg->Code)) {
		  if (!(egad->eg_Gadget.Flags & GADGDISABLED)) {
		     imsg = perform_hot_key_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)
{
   struct IntuiMessage  *real_imsg;

   if (imsg) {
      if (imsg->Class == ISUP_ID) {
	 real_imsg = *(struct IntuiMessage **)(imsg + 1);   /* real IntuiMessage direct after ISUP intuition message */
	 FreeMem(imsg, (LONG)(sizeof(struct IntuiMessage) +
					    sizeof(struct IntuiMessage *)));
	 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 SliderData      *sl;
   struct ScrollerData    *sc;
   struct CycleData       *cy;
   struct ListViewData    *lv;
   struct PaletteData     *pd;
   struct Node            *node;
   VOID   *data = (VOID *)(real_egad + 1);
   ULONG  min, max, num, value = 0, class = real_imsg->Class,
	  qualifier = real_imsg->Qualifier;
   USHORT i, next_input, type = egad->eg_Type;

   switch (type) {
      case EXTENDED_GADGET_TYPE_BUTTON :
	 if ((gd->gd_Flags & GADGET_DATA_FLAG_BUTTON_TOGGLE) &&
						    gad->Flags & SELECTED) {
	    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)) {
	    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 {
	    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 {
	    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 {
	    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 {
	    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 :
	 cy  = data;
	 max = cy->cy_TextEntries;
	 num = cy->cy_ActiveEntry;
	 if (real_imsg->Qualifier & QUALIFIER_SHIFT) {
	    if (num > 0) {
	       num--;
	    } else {
	       num = max - 1;
	    }
	 } else {
	    if (num < (max - 1)) {
	       num++;
	    } else {
	       num = 0;
	    }
	 }
	 cy->cy_ActiveEntry = value = num;
	 change_gadget(real_egad);
	 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) {
	       node = lv->lv_TopNode;
	       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) {
	    value = num;
	 } 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 hot key gadget action */

   STATIC struct IntuiMessage *
perform_hot_key_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;
   struct Node            *node;
   VOID   *data = (VOID *)(real_egad + 1);
   USHORT draw_mode, displayed = gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED;
   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) {
	       gad->Flags &= ~SELECTED;
	    } else {
	       gad->Flags |= SELECTED;
	       value       = 1;
	    }
	 } else {
	    if (displayed) {
	       gad->Flags |= SELECTED;
	       change_gadget(real_egad);
	       timer_delay(0L, 100000L);
	       if ((gad->Flags & GADGHIGHBITS) == GADGHCOMP) {
		  /* if HIGHCOMP then invert gadget select box manually */
		  draw_mode = rp->DrawMode;
		  SetDrMd(win->RPort, (LONG)COMPLEMENT);
		  RectFill(win->RPort, (LONG)gad->LeftEdge, (LONG)
		       gad->TopEdge, (LONG)(gad->LeftEdge + gad->Width - 1),
				    (LONG)(gad->TopEdge + gad->Height - 1));
		  SetDrMd(win->RPort, (LONG)draw_mode);   /* restore old draw mode */
	       }
	       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;
	    }
	 }
	 cy->cy_ActiveEntry = value = num;
	 change_gadget(real_egad);
	 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_TopEntry;
	 node = lv->lv_TopNode;
	 if (qualifier) {
	    if (num-- > 0) {
	       node = node->ln_Pred;
	    } else {
	       real_imsg = NULL;
	    }
	 } else {
	    if (++num < max && (max - num) >= min) {
	       node = node->ln_Succ;
	    } else {
	       real_imsg = NULL;
	    }
	 }
	 if (real_imsg) {
	    lv->lv_TopNode  = node;
	    lv->lv_TopEntry = value = num;
	    change_gadget(real_egad);
	    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 {
      imsg->Class       = ISUP_ID;
      imsg->Code        = data_entry;
      imsg->IAddress    = (APTR)value;
      imsg->SpecialLink = (struct IntuiMessage *)gl;
      *(struct IntuiMessage **)(imsg + 1) = real_imsg;   /* real IntuiMessage direct after ISUP intuition message */
   }
   return(imsg);
}
	/* Get active gadget for given window */

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

   ObtainSemaphore(&vg->vg_Semaphore);
   if (count = vg->vg_Count) {
      gl = (struct GadgetList *)vg->vg_MinList.mlh_Head;
      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 hot key gadget for given window */

   STATIC struct ExtendedGadget *
get_hot_key_gadget(struct Window  *win, USHORT hot_key)
{
   struct VisibleGadgetLists  *vg = &visible_gadget_lists;
   struct GadgetList          *gl;
   struct ExtendedGadget      *egad, *result_egad = NULL;
   USHORT i, j, count, data_entries;
   BOOL   found = FALSE;

   ObtainSemaphore(&vg->vg_Semaphore);
   if (count = vg->vg_Count) {
      hot_key = toupper(hot_key);
      gl = (struct GadgetList *)vg->vg_MinList.mlh_Head;
      for (i = 0; i < count && found == FALSE; i++) {
	 if (gl->gl_Window == win) {
	    data_entries = gl->gl_DataEntries;
	    for (j = 0; j < data_entries && found == FALSE; j++) {
	       egad = gl->gl_Gadgets[j];
	       if (egad->eg_Flags & EXTENDED_GADGET_FLAG_HOT_KEY &&
				      toupper(egad->eg_Hotkey) == hot_key) {
		  result_egad = egad;
		  found       = TRUE;
	       }
	    }
	 }
	 gl = (struct GadgetList *)gl->gl_MinNode.mln_Succ;
      }
   }
   ReleaseSemaphore(&vg->vg_Semaphore);
   return(result_egad);
}
