/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1991 by Torsten Jürgeleit
 *
 *	Name .....: gadgets3.c
 *	Created ..: Thursday 19-Dec-91 20:52:56
 *	Revision .: 5
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	19-Aug-92   Torsten Jürgeleit      save old clipping rectangles in
 *					   print_list_view_text()
 *	06-Aug-92   Torsten Jürgeleit      alternate entry text color in
 *					   print_list_view_text()
 *	01-Apr-92   Torsten Jürgeleit      any references to gadget data
 *					   via gl_Data removed
 *	06-Jan-92   Torsten Jürgeleit      added pop up cycle text list
 *	28-Dec-91   Torsten Jürgeleit      correct output of count gadget value
 *					   if ri_ScreenDepth == 1
 *	19-Dec-91   Torsten Jürgeleit      Created this file!
 *
 ****************************************************************************
 *
 *	Gadget support routines - part 3
 *
 * $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 "/texts/texts.h"
#include "/borders/borders.h"
#include "/language/language.h"
#include "gadgets.h"
#include "imports.h"

	/* Refresh full ISUP gadget */

   VOID
refresh_gadget(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;

   if (gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED) {
      struct RenderInfo      *ri = gl->gl_RenderInfo;
      struct Window          *win = gl->gl_Window;
      struct RastPort        *rp = win->RPort;
      struct ExtendedGadget  *first_egad = egad;

      /* Render object text - rendering first because of ghosting by GADGDISABLED */
      switch (egad->eg_DataType) {
	 case GADGET_DATA_TYPE_CYCLE :
	    print_cycle_text(egad);
	    break;

	 case GADGET_DATA_TYPE_COUNT :
	    print_count_text(egad, TRUE);
	    break;

	 case GADGET_DATA_TYPE_LISTVIEW :
	    print_list_view_text(egad);
	    break;
      }
      print_gadget_text(first_egad);

      /* Render all gadgets belonging to this ISUP object */
      do {
	 struct Gadget  *gad = &egad->eg_Gadget;
	 UBYTE flags = egad->eg_Flags;

	 /* Draw additional border or image if any */
	 if (flags & (EXTENDED_GADGET_FLAG_RENDER_BORDER |
				       EXTENDED_GADGET_FLAG_RENDER_IMAGE)) {
	    SHORT left_edge = gad->LeftEdge, top_edge = gad->TopEdge;

	    if (flags & EXTENDED_GADGET_FLAG_RENDER_BORDER) {
	       DrawBorder(rp, (struct Border *)egad->eg_Render,
					   (LONG)left_edge, (LONG)top_edge);
	    } else {
	       struct Image  *image = (struct Image *)egad->eg_Render;

	       SHORT xoffset = (first_egad->eg_DataFlags &
						GADGET_DATA_FLAG_NO_BORDER ?
			       IMAGE_HORIZ_OFFSET : IMAGE_HORIZ_OFFSET + 2),
		     width = gad->Width;

	       top_edge += (SHORT)(gad->Height - image->Height) / 2;
	       switch (egad->eg_Type) {
		  case EXTENDED_GADGET_TYPE_CYCLE :
		     left_edge += xoffset;
		     break;

		  case EXTENDED_GADGET_TYPE_COUNT :
		     DrawImage(rp, &ri->ri_Images[IMAGE_COUNT_RIGHT],
			 (LONG)(left_edge + width - image->Width - xoffset),
							    (LONG)top_edge);
		     left_edge += xoffset;
		     break;

		  default :
		     left_edge += (SHORT)(width - image->Width) / 2;
		     break;
	       }
	       DrawImage(rp, image, (LONG)left_edge, (LONG)top_edge);
	    }
	 }

	 /* Check if disabled selected toggle select gadget */
	 if ((gad->Activation & TOGGLESELECT) && (gad->Flags &
		  (GADGDISABLED | SELECTED)) == (GADGDISABLED | SELECTED)) {
	    APTR render = gad->GadgetRender;

	    /* Special refresh to force correct ghosting */
	    gad->GadgetRender = gad->SelectRender;
	    gad->Flags       &= ~SELECTED;
	    RefreshGList(gad, win, (LONG)NULL, 1L);
	    gad->Flags       |= SELECTED;
	    gad->GadgetRender = render;
	 } else {

	    /* Normal refresh */
	    RefreshGList(gad, win, (LONG)NULL, 1L);
	 }
      } while (egad = egad->eg_NextGadget);
   }
}
	/* Change gadget */

   VOID
change_gadget(struct ExtendedGadget  *egad)
{
   struct GadgetList    *gl = egad->eg_GadgetList;
   struct Window        *win = gl->gl_Window;
   struct Gadget        *gad = &egad->eg_Gadget;
   struct PropInfo      *pinfo;
   struct MXData        *mx;
   struct SliderData    *sl;
   struct CycleData     *cy;
   struct ScrollerData  *sc;
   struct ListViewData  *lv;
   struct PaletteData   *pd;
   VOID   *data = (VOID *)(egad + 1);
   ULONG  min, max, num, body, pot, flags = egad->eg_DataFlags;
   UBYTE  displayed = gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED;
   USHORT i;

   switch (egad->eg_DataType) {
      case GADGET_DATA_TYPE_BUTTON :
      case GADGET_DATA_TYPE_CHECK :
      case GADGET_DATA_TYPE_STRING :
      case GADGET_DATA_TYPE_INTEGER :
	 if (displayed) {
	    RefreshGList(gad, win, (LONG)NULL, 1L);
	 }
	 break;

      case GADGET_DATA_TYPE_MX :
	 mx  = data;
	 max = mx->mx_TextEntries;
	 num = mx->mx_ActiveEntry;
	 for (i = 0; i < max; i++) {
	    gad = &egad->eg_Gadget;
	    if (i == num) {
	       gad->Flags |= SELECTED;
	       if (displayed) {
		  RefreshGList(gad, win, (LONG)NULL, 1L);
	       }
	    } else {
	       if (gad->Flags & SELECTED) {
		  gad->Flags &= ~SELECTED;
		  if (displayed) {
		     RefreshGList(gad, win, (LONG)NULL, 1L);
		  }
	       }
	       
	    }
	    egad = egad->eg_NextGadget;
	 }
	 break;

      case GADGET_DATA_TYPE_SLIDER :
	 pinfo = (struct PropInfo *)gad->SpecialInfo;
	 sl    = data;
	 min   = sl->sl_Min;
	 max   = (LONG)sl->sl_Max - (LONG)min;
	 num   = (LONG)sl->sl_Level - (LONG)min;
	 if (flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	    num = max - num;   /* if vert slider then maximum is at top */
	 }
	 body = calc_prop_body(max + 1, 1L);
	 pot  = calc_prop_pot(max + 1, 1L, num);
	 if (flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, 0L,
							 pot, 0L, body, 1L);
	    } else {
	       pinfo->VertBody = body;
	       pinfo->VertPot  = pot;
	    }
	 } else {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, pot,
							  0L, body, 0L, 1L);
	    } else {
	       pinfo->HorizBody = body;
	       pinfo->HorizPot  = pot;
	    }
	 }
	 break;

      case GADGET_DATA_TYPE_SCROLLER :
	 pinfo = (struct PropInfo *)gad->SpecialInfo;
	 sc    = data;
	 min   = sc->sc_Visible;
	 max   = sc->sc_Total;
	 num   = sc->sc_Top;
	 body  = calc_prop_body(max, min);
	 pot   = calc_prop_pot(max, min, num);
	 if (flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, 0L,
							 pot, 0L, body, 1L);
	    } else {
	       pinfo->VertBody = body;
	       pinfo->VertPot  = pot;
	    }
	 } else {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, pot,
							  0L, body, 0L, 1L);
	    } else {
	       pinfo->HorizBody = body;
	       pinfo->HorizPot  = pot;
	    }
	 }
	 break;

      case GADGET_DATA_TYPE_CYCLE :
	 cy                = data;
	 cy->cy_ActiveText = cy->cy_TextArray[cy->cy_ActiveEntry];
	 if (displayed) {
	    print_cycle_text(egad);
	 }
	 break;

      case GADGET_DATA_TYPE_COUNT :
	 if (displayed) {
	    print_count_text(egad, TRUE);
	 }
	 break;

      case GADGET_DATA_TYPE_LISTVIEW :
	 pinfo = (struct PropInfo *)gad->SpecialInfo;
	 lv    = data;
	 min   = lv->lv_VisibleEntries;
	 max   = lv->lv_ListEntries;
	 num   = lv->lv_TopEntry;
	 body  = calc_prop_body(max, min);
	 pot   = calc_prop_pot(max, min, num);
	 if (displayed) {
	    NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, 0L, pot,
							      0L, body, 1L);
	    print_list_view_text(egad);
	 } else {
	    pinfo->VertBody = body;
	    pinfo->VertPot  = pot;
	 }
	 break;

      case GADGET_DATA_TYPE_PALETTE :
	 pd = data;
	 if (!(flags & GADGET_DATA_FLAG_PALETTE_NO_INDICATOR)) {
	    struct Image  *image = (struct Image *)egad->eg_Render;

	    image->PlaneOnOff = pd->pd_ActiveColor;
	    if (displayed) {
	       DrawImage(win->RPort, image, (LONG)(gad->LeftEdge + (SHORT)
		     (gad->Width - image->Width) / 2), (LONG)(gad->TopEdge +
				 (SHORT)(gad->Height - image->Height) / 2));
	    }
	 }
	 break;
   }
}
	/* Change cycle gadget text */

   BOOL
change_cycle_gadget(struct ExtendedGadget  *egad, ULONG qualifier)
{
   struct Window     *win = egad->eg_GadgetList->gl_Window;
   struct MsgPort    *up  = win->UserPort;
   struct Gadget     *gad = &egad->eg_Gadget;
   struct CycleData  *cy = (struct CycleData *)(egad + 1);
   LONG   entry, old_entry = cy->cy_ActiveEntry;
   ULONG  old_idcmp_flags = win->IDCMPFlags;
   USHORT delay = MAX_CYCLE_DELAY;
   BOOL   keepon = TRUE, changed = FALSE;

   /* Wait a while before using popup cycle text list */
   ModifyIDCMP(win, (LONG)(MOUSEBUTTONS | INTUITICKS));
   do {
      struct IntuiMessage  *msg;

      WaitPort(up);
      if (msg = (struct IntuiMessage *)GetMsg(up)) {
	 switch (msg->Class) {
	    case INTUITICKS :
	       if (!--delay) {
		  keepon = FALSE;
	       }
	       break;

	    case MOUSEBUTTONS :
	       if (msg->Code == SELECTUP) {
		  keepon = FALSE;
	       }
	       break;
	 }
	 ReplyMsg((struct Message *)msg);
      }
   } while (keepon == TRUE);
   if (delay) {
      LONG max_entry = cy->cy_TextEntries - 1;

      /* Get new entry entry by selection next or previous one */
      if (qualifier & QUALIFIER_SHIFT) {
	 if (old_entry > 0) {
	    entry = old_entry - 1;
	 } else {
	    entry = max_entry;
	 }
      } else {
	 if (old_entry < max_entry) {
	    entry = old_entry + 1;
	 } else {
	    entry = 0;
	 }
      }
   } else {

      /* Get new entry from pop up cycle text list */
      if ((entry = pop_up_cycle_list(egad)) == -1L) {
	 entry = old_entry;
      }
   }
   ModifyIDCMP(win, old_idcmp_flags);

   /* Restore normal cycle gadget state (unselected) */
   if ((gad->Flags & GADGHIGHBITS) == GADGHCOMP) {
      RefreshGList(gad, win, (LONG)NULL, 1L);   /* complement gadget select box */
   }
   gad->Flags &= ~SELECTED;
   RefreshGList(gad, win, (LONG)NULL, 1L);

   /* Print new cycle text */
   if (entry != old_entry) {
      cy->cy_ActiveEntry = entry;
      cy->cy_ActiveText  = cy->cy_TextArray[entry];
      print_cycle_text(egad);
      changed = TRUE;
   }
   return(changed);
}
	/* Select cycle entry from pop up cycle list */

   STATIC LONG
pop_up_cycle_list(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *backup_rp, *rp = win->RPort;
   struct CycleData   *cy = (struct CycleData *)(egad + 1);
   SHORT  yinc, left_edge, top_edge;
   USHORT width, height;
   LONG   entry = -1L;

   /* Calc pop up list dimension and position */
   width     = cy->cy_Width + 2 * CYCLE_POP_XOFFSET;
   height    = cy->cy_EntryHeight * cy->cy_TextEntries +
						      2 * CYCLE_POP_YOFFSET;
   left_edge = cy->cy_LeftEdge - CYCLE_POP_XOFFSET;
   top_edge  = cy->cy_TopEdge - CYCLE_POP_YOFFSET;

   /* If pop up list doesn't fit downwards then try it upwards */
   if ((top_edge + height) > win->Height) {
      top_edge -= height - (cy->cy_EntryHeight + 2 * CYCLE_POP_YOFFSET);
      yinc      = -cy->cy_EntryHeight;

      /* If it doesn't fit upwards too then give up */
      if (top_edge < 0) {
	 DisplayBeep(NULL);
      }
   } else {
      yinc = cy->cy_EntryHeight;
   }

   /* Create bitmap for saving pop up list background */
   if (top_edge >= 0 && (backup_rp = create_rast_port(width, height,
					      (USHORT)rp->BitMap->Depth))) {
      struct MsgPort     *up = win->UserPort;
      struct IntuiText  itext, *it = &itext;
      BYTE   **text = cy->cy_TextArray;
      USHORT xpos = left_edge + CYCLE_POP_XOFFSET,
	     ypos = top_edge + CYCLE_POP_YOFFSET,
	     detail_pen = win->DetailPen, block_pen = win->BlockPen;
      LONG   last_entry = -1L;
      BOOL   keepon = TRUE;

      /* Save old cycle list backgound and prepare new one */
      ClipBlit(rp, (LONG)left_edge, (LONG)top_edge, backup_rp, 0L, 0L,
					  (LONG)width, (LONG)height, 0xc0L);
      SetDrMd(rp, (LONG)JAM1);
      SetAPen(rp, (LONG)detail_pen);
      RectFill(rp, (LONG)left_edge, (LONG)top_edge,
	      (LONG)(left_edge + width - 1), (LONG)(top_edge + height - 1));
      SetAPen(rp, (LONG)block_pen);
      RectFill(rp, (LONG)(left_edge + 2), (LONG)(top_edge + 1),
	      (LONG)(left_edge + width - 3), (LONG)(top_edge + height - 2));
      SetDrMd(rp, (LONG)COMPLEMENT);

      /* Print cycle list text */
      it->FrontPen  = detail_pen;
      it->BackPen   = block_pen;
      it->DrawMode  = JAM1;
      it->LeftEdge  = xpos;
      it->TopEdge   = (yinc > 0 ? ypos : ypos + height -
			      (cy->cy_EntryHeight + 2 * CYCLE_POP_YOFFSET));
      it->ITextFont = egad->eg_TextAttr;
      it->NextText  = NULL;
      while (*text) {
	 it->IText = (UBYTE *)get_language_text(*text++,
						  gl->gl_LanguageTextArray);
	 PrintIText(rp, it, 0L, 0L);
	 it->TopEdge += yinc;
      }

      /* Wait for release of left mouse button */
      do {
	 struct IntuiMessage  *msg;

	 WaitPort(up);
	 while (msg = (struct IntuiMessage *)GetMsg(up)) {
	    SHORT mouse_x = win->MouseX, mouse_y = win->MouseY;

	    /* Check if mouse pointer is over pop up list entry */
	    if (mouse_x >= (left_edge + CYCLE_POP_XOFFSET) &&
		    mouse_x < (left_edge + width - 2 * CYCLE_POP_XOFFSET) &&
		    mouse_y >= (top_edge + CYCLE_POP_YOFFSET) &&
		    mouse_y < (top_edge + height - 2 * CYCLE_POP_YOFFSET)) {
	       LONG new_entry = (yinc > 0 ? (mouse_y - ypos) / yinc :
			 cy->cy_TextEntries - (mouse_y - ypos) / -yinc - 1);

	       if (new_entry != last_entry) {
		  USHORT y;

		  if (last_entry != -1L) {

		     /* Unmark last selected entry */
		     y = ypos + CYCLE_LIST_ENTRY_YPOS(last_entry);
		     RectFill(rp, (LONG)xpos, (LONG)y, (LONG)(xpos + width -
						 2 * CYCLE_POP_XOFFSET - 1),
					(LONG)(y + cy->cy_EntryHeight - 1));
		  }

		  /* Mark selected entry */
		  y = ypos + CYCLE_LIST_ENTRY_YPOS(new_entry);
		  RectFill(rp, (LONG)xpos, (LONG)y, (LONG)(xpos + width -
						 2 * CYCLE_POP_XOFFSET - 1),
					(LONG)(y + cy->cy_EntryHeight - 1));
		  last_entry = new_entry;
	       }
	    } else {
	       if (last_entry != -1L) {
		  USHORT y = ypos + CYCLE_LIST_ENTRY_YPOS(last_entry);

		  /* Unmark last selected entry */
		  RectFill(rp, (LONG)xpos, (LONG)y, (LONG)(xpos + width -
						 2 * CYCLE_POP_XOFFSET - 1),
					(LONG)(y + cy->cy_EntryHeight - 1));
		  last_entry = -1L;
	       }
	    }
	    if (msg->Class == MOUSEBUTTONS && msg->Code == SELECTUP) {
	       entry  = last_entry;
	       keepon = FALSE;
	    }
	    ReplyMsg((struct Message *)msg);
	 }
      } while (keepon == TRUE);
      ClipBlit(backup_rp, 0L, 0L, rp, (LONG)left_edge, (LONG)top_edge,
					  (LONG)width, (LONG)height, 0xc0L);
      free_rast_port(backup_rp, width, height);
   }
   return(entry);
}
	/* Build rast port for saving background of pop up cycle list */

   STATIC struct RastPort *
create_rast_port(USHORT width, USHORT height, USHORT depth)
{
   struct RastPort  *rp;

   if (rp = AllocMem((LONG)(sizeof(struct RastPort) + sizeof(struct BitMap)),
						       (LONG)MEMF_PUBLIC)) {
      struct BitMap  *bm = (struct BitMap *)(rp + 1);
      USHORT i;

      /* Init rast port */
      InitRastPort(rp);
      rp->BitMap = bm;

      /* Init bitmap structure and allocate bit planes */
      InitBitMap(bm, (LONG)depth, (LONG)width, (LONG)height);
      for (i = 0; i < depth; i++) {
	 if (!(bm->Planes[i] = (PLANEPTR)AllocRaster((LONG)width,
							   (LONG)height))) {
	    /* Free bitmap */
	    for ( ; i > 0; i--) {
	       FreeRaster(bm->Planes[i], (LONG)width, (LONG)height);
	    }
	    FreeMem(rp, (LONG)(sizeof(struct RastPort) +
						    sizeof(struct BitMap)));
	    rp = NULL;
	    break;
	 }
      }
   }
   return(rp);
}
	/* Free rast port for saving background of pop up cycle list */

   STATIC VOID
free_rast_port(struct RastPort  *rp, USHORT width, USHORT height)
{
   struct BitMap  *bm = rp->BitMap;
   USHORT i, depth = bm->Depth;

   for (i = 0; i < depth; i++) {
      FreeRaster(bm->Planes[i], (LONG)width, (LONG)height);
   }
   FreeMem(rp, (LONG)(sizeof(struct RastPort) + sizeof(struct BitMap)));
}
	/* Change count gadget value */

   VOID
change_count_gadget(struct ExtendedGadget  *egad)
{
   struct Window     *win = egad->eg_GadgetList->gl_Window;
   struct MsgPort    *up  = win->UserPort;
   struct Gadget     *gad = &egad->eg_Gadget;
   struct CountData  *co = (struct CountData *)(egad + 1);
   LONG   min = co->co_Min, max = co->co_Max, count = co->co_Value;
   ULONG  old_idcmp_flags = win->IDCMPFlags;
   USHORT max_delay = MAX_COUNT_DELAY, delay = MAX_COUNT_DELAY;
   SHORT  count_change, middle = gad->LeftEdge + gad->Width / 2;
   BOOL   keepon = TRUE;

   ModifyIDCMP(win, (LONG)(MOUSEBUTTONS | INTUITICKS));

   /* Calc new delay and change value with new mouse pos */
   if (win->MouseX > middle) {
      count_change = +1;
      if (count < max) {
	 count++;
      }
   } else {
      count_change = -1;
      if (count > min) {
	 count--;
      }
   }
   co->co_Value = count;
   print_count_text(egad, TRUE);

   /* Change count value depending on mouse position (busy loop) */
   do {
      struct IntuiMessage  *msg;
      SHORT  mouse_x = win->MouseX;
      USHORT distance, new_delay;

      /* Calc new delay and change value with new mouse pos */
      if (mouse_x > middle) {
	 distance     = mouse_x - middle;
	 count_change = +1;
      } else {
	 distance = middle - mouse_x;
	 count_change = -1;
      }
      if (distance > MAX_COUNT_DISTANCE) {
	 distance = MAX_COUNT_DISTANCE;
      }
      new_delay = MAX_COUNT_DELAY - distance / MAX_COUNT_STEP_SIZE;
      if (new_delay != max_delay) {
	 max_delay = delay = new_delay;
      }
      if (msg = (struct IntuiMessage *)GetMsg(up)) {
	 switch (msg->Class) {
	    case INTUITICKS :
	       if (max_delay) {

		  /* Delayed change of count value */
		  if (! --delay) {
		     if ((count + count_change) >= min &&
					    (count + count_change) <= max) {
			co->co_Value = count += count_change;
			print_count_text(egad, TRUE);
		     }
		     delay = max_delay;
		  }
	       }
	       break;

	    case MOUSEBUTTONS :
	       if (msg->Code == SELECTUP) {
		  keepon = FALSE;
	       }
	       break;
	 }
	 ReplyMsg((struct Message *)msg);
      }
      if (! max_delay) {

	 /* Undelayed change of count value */
	 if ((count + count_change) >= min && (count + count_change) <=
								      max) {
	    co->co_Value = count += count_change;
	    print_count_text(egad, TRUE);
	    timer_delay(0L, MIN_COUNT_DELAY_MICROS);
	 }
      }
   } while (keepon == TRUE);
   ModifyIDCMP(win, old_idcmp_flags);

   /* Restore normal count gadget state (unselected) */
   if ((gad->Flags & GADGHIGHBITS) == GADGHCOMP) {
      RefreshGList(gad, win, (LONG)NULL, 1L);   /* complement gadget select box */
   }
   gad->Flags &= ~SELECTED;
   RefreshGList(gad, win, (LONG)NULL, 1L);
}
	/* Delay function - can't use Delay() from DOS (no process) */

   VOID
timer_delay(ULONG seconds, ULONG micros)
{
   struct MsgPort      *port;
   struct timerequest  *timer;

   if (port = CreatePort(NULL, 0L)) {
      if (timer = (struct timerequest *)CreateExtIO(port, (LONG)
					      sizeof(struct timerequest))) {
	 if (!OpenDevice(TIMERNAME, (LONG)UNIT_VBLANK, (struct IORequest *)
							       timer, 0L)) {
	    timer->tr_node.io_Command = TR_ADDREQUEST;
	    timer->tr_time.tv_secs    = seconds;
	    timer->tr_time.tv_micro   = micros;
	    DoIO((struct IORequest *)timer);
	    CloseDevice((struct IORequest *)timer);
	 }
	 DeleteExtIO((struct IORequest *)timer);
      }
      DeletePort(port);
   }
}
	/* Print gadget text */

   VOID
print_gadget_text(struct ExtendedGadget  *egad)
{
   BYTE *text;

   if (text = egad->eg_Text) {
      struct GadgetList  *gl = egad->eg_GadgetList;
      struct RenderInfo  *ri = gl->gl_RenderInfo;
      struct Window      *win = gl->gl_Window;
      struct RastPort    *rp = win->RPort;
      struct Gadget      *gad = &egad->eg_Gadget;
      struct TextAttr    *ta = egad->eg_TextAttr;
      USHORT flags = (egad->eg_DataFlags &
		GADGET_DATA_FLAG_TEXT_COLOR2 ? TEXT_DATA_FLAG_ABSOLUTE_POS |
		       TEXT_DATA_FLAG_COLOR2 : TEXT_DATA_FLAG_ABSOLUTE_POS),
	     left_edge = gad->LeftEdge + egad->eg_TextLeftEdge,
	     top_edge = gad->TopEdge + egad->eg_TextTopEdge;

      if (egad->eg_Flags & EXTENDED_GADGET_FLAG_HOTKEY) {
	 BYTE   *buffer;
	 LONG   len = strlen(text) + 1;
	 USHORT pos = egad->eg_HotkeyPos;

	 if (pos < len && (buffer = AllocMem(len, (LONG)MEMF_PUBLIC))) {
	    BYTE   c;
	    USHORT save_left_edge = left_edge;

	    /* Print text part before hotkey */
	    strcpy(buffer, text);
	    if (pos > 1) {
	       *(buffer + pos - 1) = '\0';
	       left_edge          += print_text(ri, win, buffer, left_edge,
				  top_edge, TEXT_DATA_TYPE_TEXT, flags, ta);
	    }

	    /* Print and underline hotkey character */
	    if (pos < (len - 2)) {
	       c                   = *(buffer + pos + 1);
	       *(buffer + pos + 1) = '\0';
	    }
	    save_left_edge = left_edge;
	    left_edge     += print_text(ri, win, buffer + pos, left_edge,
				  top_edge, TEXT_DATA_TYPE_TEXT, flags, ta);
	    SetDrMd(rp, (LONG)JAM1);
	    SetAPen(rp, (LONG)(egad->eg_DataFlags &
			    GADGET_DATA_FLAG_TEXT_COLOR2 ? ri->ri_TextPen2 :
							  ri->ri_TextPen1));
	    RectFill(rp, (LONG)save_left_edge, (LONG)(top_edge +
			egad->eg_TextFont->tf_YSize), (LONG)(left_edge - 1),
			    (LONG)(top_edge + egad->eg_TextFont->tf_YSize));
	    /* Print text part after hotkey */
	    if (pos < (len - 2)) {
	       *(buffer + pos + 1) = c;
	       print_text(ri, win, buffer + pos + 1, left_edge, top_edge,
					    TEXT_DATA_TYPE_TEXT, flags, ta);
	    }
	    FreeMem(buffer, len);
	 } else {
	    print_text(ri, win, text, left_edge, top_edge,
					    TEXT_DATA_TYPE_TEXT, flags, ta);
	 }
      } else {
	 print_text(ri, win, text, left_edge, top_edge, TEXT_DATA_TYPE_TEXT,
								 flags, ta);
      }
   }
}
	/* Print cycle text */

   VOID
print_cycle_text(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct RenderInfo  *ri = gl->gl_RenderInfo;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *rp = win->RPort;
   struct CycleData   *cy = (struct CycleData *)
				   (gl->gl_Gadgets[egad->eg_DataEntry] + 1);
   struct TextAttr    *text_attr = egad->eg_TextAttr;
   struct IntuiText   itext;
   BYTE   *text = get_language_text(cy->cy_ActiveText,
						  gl->gl_LanguageTextArray);
   USHORT flags = (egad->eg_DataFlags &
		GADGET_DATA_FLAG_TEXT_COLOR2 ? TEXT_DATA_FLAG_ABSOLUTE_POS |
		       TEXT_DATA_FLAG_COLOR2 : TEXT_DATA_FLAG_ABSOLUTE_POS),
	  left_edge = cy->cy_LeftEdge, top_edge = cy->cy_TopEdge,
	  width = cy->cy_Width, height = cy->cy_Height;

   itext.IText     = (UBYTE *)text;
   itext.ITextFont = text_attr;
   SetDrMd(rp, (LONG)JAM1);
   SetAPen(rp, (LONG)ri->ri_BackPen);
   RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge + width -
					 1), (LONG)(top_edge + height - 1));
   print_text(ri, win, text, left_edge + (SHORT)(width -
		IntuiTextLength(&itext)) / 2, top_edge, TEXT_DATA_TYPE_TEXT,
							  flags, text_attr);
}
	/* Print count text */

   VOID
print_count_text(struct ExtendedGadget  *egad, BOOL print_new)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct RenderInfo  *ri = gl->gl_RenderInfo;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *rp = win->RPort;
   struct CountData   *co = (struct CountData *)
				   (gl->gl_Gadgets[egad->eg_DataEntry] + 1);
   struct TextAttr    *text_attr = egad->eg_TextAttr;
   USHORT type = (egad->eg_DataFlags & GADGET_DATA_FLAG_COUNT_SIGNED_DEC ?
					     TEXT_DATA_TYPE_NUM_SIGNED_DEC :
					   TEXT_DATA_TYPE_NUM_UNSIGNED_DEC),
	  gflags = egad->eg_Gadget.Flags,
	  tflags = (egad->eg_DataFlags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
		   TEXT_DATA_FLAG_ABSOLUTE_POS | TEXT_DATA_FLAG_PLACE_LEFT |
		       TEXT_DATA_FLAG_COLOR2 : TEXT_DATA_FLAG_ABSOLUTE_POS |
						 TEXT_DATA_FLAG_PLACE_LEFT),
	  left_edge = co->co_LeftEdge, top_edge = co->co_TopEdge,
	  width = co->co_Width, height = co->co_Height;

   /* Clear area for new value */
   if ((gflags & SELECTED) && (gflags & GADGHIGHBITS) == GADGHCOMP) {
      tflags |= TEXT_DATA_FLAG_COMPLEMENT | INTERNAL_TEXT_DATA_FLAG_COUNT;
      SetAPen(rp, (LONG)(ri->ri_BackPen ^ ((1 << ri->ri_ScreenDepth) - 1)));
   } else {
      SetAPen(rp, (LONG)ri->ri_BackPen);
   }
   SetDrMd(rp, (LONG)JAM1);
   RectFill(rp, (LONG)left_edge, (LONG)top_edge,
	      (LONG)(left_edge + width - 1), (LONG)(top_edge + height - 1));

   /* Print new value if requested */
   if (print_new) {
      print_text(ri, win, (BYTE *)co->co_Value, left_edge + width,
					 top_edge, type, tflags, text_attr);
   }
}
	/* Print list view text */

   VOID
print_list_view_text(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *rp = win->RPort;
   struct Layer       *layer = rp->Layer;
   struct Region      *region;

   /* Calc offsets for inner window */
   LockLayerRom(layer);
   if (region = NewRegion()) {
      struct RenderInfo    *ri = gl->gl_RenderInfo;
      struct Rectangle     rect;
      struct Region        *old_region;
      struct ClipRect      *old_cliprects;
      struct ListViewData  *lv = (struct ListViewData *)
				   (gl->gl_Gadgets[egad->eg_DataEntry] + 1);
      struct TextAttr      *ta = egad->eg_TextAttr;
      struct Node          *node = lv->lv_TopNode;
      ULONG  gflags = egad->eg_DataFlags;
      USHORT i, tflags = (gflags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
		       TEXT_DATA_FLAG_COLOR2 | TEXT_DATA_FLAG_ABSOLUTE_POS |
		    TEXT_DATA_FLAG_BACK_FILL : TEXT_DATA_FLAG_ABSOLUTE_POS |
						  TEXT_DATA_FLAG_BACK_FILL),
	     left_edge = lv->lv_LeftEdge, top_edge = lv->lv_TopEdge,
	     width = lv->lv_Width, height = lv->lv_Height,
	     entries = lv->lv_ListEntries, visible = lv->lv_VisibleEntries,
	     entry_height = lv->lv_EntryHeight, top = lv->lv_TopEntry;

      /* Create clipping region around list view entry area */
      rect.MinX = left_edge;
      rect.MaxX = left_edge + width - 1;
      rect.MinY = top_edge;
      rect.MaxY = top_edge + height - 1;
      OrRectRegion(region, &rect);

      /* Before installing clipping region save old clipping rectangles */
      old_cliprects     = layer->_cliprects;
      layer->_cliprects = NULL;
      old_region = InstallClipRegion(layer, region);

      /* Print list view entry text with clipping */
      SetDrMd(rp, (LONG)JAM1);
      SetAPen(rp, (LONG)ri->ri_BackPen);
      for (i = visible; i; i--, top++, top_edge += entry_height) {
	 if (top < entries) {
	    BYTE   *text = node->ln_Name;
	    USHORT tflags, text_width;

	    /* Check for alternate entry text color */
	    if ((gflags & GADGET_DATA_FLAG_LISTVIEW_ENTRY_COLOR) &&
							       *text == 1) {
	       text++;
	       tflags = (gflags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
		    TEXT_DATA_FLAG_ABSOLUTE_POS | TEXT_DATA_FLAG_BACK_FILL :
		       TEXT_DATA_FLAG_COLOR2 | TEXT_DATA_FLAG_ABSOLUTE_POS |
						  TEXT_DATA_FLAG_BACK_FILL);
	    } else {
	       tflags = (gflags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
		       TEXT_DATA_FLAG_COLOR2 | TEXT_DATA_FLAG_ABSOLUTE_POS |
		    TEXT_DATA_FLAG_BACK_FILL : TEXT_DATA_FLAG_ABSOLUTE_POS |
						  TEXT_DATA_FLAG_BACK_FILL);
	    }

	    /* Print entry text and clear rest of line */
	    text_width = print_text(ri, win, text, left_edge, top_edge,
					   TEXT_DATA_TYPE_TEXT, tflags, ta);
	    if (text_width < width) {
	       RectFill(rp, (LONG)(left_edge + text_width), (LONG)top_edge,
			    (LONG)(left_edge + width - 1), (LONG)(top_edge +
							 entry_height - 1));
	    }
	    node = node->ln_Succ;
	 } else {
	    break;
	 }
      }
      if (i) {
	 RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge +
		       width - 1), (LONG)(top_edge + i * entry_height - 1));
      }

      /* Highlite selected entry */
      if (gflags & GADGET_DATA_FLAG_LISTVIEW_SHOW_SELECTED) {
	 USHORT selected = lv->lv_SelectedEntry;

	 /* Check if visible */
	 top = lv->lv_TopEntry;
	 if (selected < entries && selected >= top &&
					       selected < (top + visible)) {
	    top_edge = lv->lv_TopEdge + (selected - top) * entry_height;
	    SetDrMd(rp, (LONG)COMPLEMENT);
	    RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge +
			   width - 1), (LONG)(top_edge + ta->ta_YSize - 1));
	 }
      }

      /* Remove clipping region and restore old clipping rectangles */
      InstallClipRegion(layer, old_region);
      layer->_cliprects = old_cliprects;
      DisposeRegion(region);
   }
   UnlockLayerRom(layer);
}
