/*
 * Boopsi.c -- BOOPSI Objekte anlegen und entfernen
 *
 */


/*
   Gadgetklassen-Bezeichner. Mit diesem Namen wird die public BOOPSI Klasse
   gefunden. Wäre die Klasse privat, würde ein Pointer auf sie (statt Name)
   verwendet.
 */

#define NB_BUTTONCLASSNAME "button.gadget"
char *buttonClassName = NB_BUTTONCLASSNAME;

/*
   Entwurf einer BOOPSI-Objekt-Liste

   Node:

   BoopsiObject
   {
   Node      bo_node    --- Standard Exec Node mit Name, Type, Pri Feld

   --------------------- Nutzdaten -------------------------

   Object *  bo_object  --- zeigt auf Adresse der Objekt-INSTANZ,

   NULL: kein Objekt vorhanden
   !NULL: (gültiges?) Objekt ist vorhanden


   Begriffe: Instanz -- Klasse
   "Diese Instanz A wurde aus der Klasse X erzeugt."
   "Diese Klasse X hat die Instanzen A, B und C erzeugt."

   Instanz = konkretes, erzeugtes Objekt: A, B, C.
   Klasse  = X, "Schöpfer"(Ausdruck!) für Instanzen.


   USHORT    bo_type    --- Gadget/Image/Listview Item/Pseudoeintrag
   LONGBITS  bo_status  --- BITS
   active
   valid
   ..

   }

   2. eine Struktur KeyboardObject

   Problem: Tastatureingaben dynamisch auswerten

   Lösungsmöglichkeit:
   - spezielle Exec Liste aus KeyboardObjects.
   - eine spezielle Routine, die in der IDCMP Schleife ist:
   - empfängt Tastendruck (keyboard event)
   - geht List-Nodes durch und vergleicht pro Node:
   - ist
   IMsg.Code/.Qualifier == KeyboardObject.kb_code/.kb_qualifier

   -- wenn ja:
   --- löse aus Aktion
   ---- ist
   --- breche ab Node-Durchlauf

   (-- wenn nein:
   --- weiter mit nächstem Node bis Listenende)



   Node

   KeyboardObject
   {
   struct Node kb_node;
   ULONG(?)    kb_code;        -- wie IMsg-Feld: einfacher Tastendruck
   ULONG(?)    kb_qualifier;   -- wie IMsg-Feld: Zusatztaste z.B. Shift usw.
   ULONG       kb_bobject;     -- Adresse des zugehörigen BOOPSI Objekt*Node*!.
   !NULL: Bei Tastendruck bekommt Objekt IM_DRAW
   Messages Activated, Normal.
   --> immer vorab Test ob gültiger Node
   NULL: kein Boopsi Objekt ist dem Tastendruck
   zugeordnet.
   APTR        kb_userdata    --  wie in Gadget.UserData/Menu.User.Data:
   zur Zeit noch (APTR) actioncode.
   Siehe <src/Actioncode.h> für Infos über das 
   Actioncode-System
   später: (todo) Zeiger auf erweiterte
   Actioncode-Struktur, als Input für
   typenfreie(?) Parameterübergabe im ReadArgs
   Template.
   }

 */

/* todo Gadget IDs */
#define	GID_Naechste    0
#define	GID_Vorherige	1
#define	GID_Messages    2
#define GID_Gruppen	3
#define GID_CatchUp     4
#define	GID_Hilfe       5
#define	NUMGADS         6
struct Gadget *gads[NUMGADS];	/* Namenkonflikt mit BOOPSI ListView vorprogrammiert!!! */

/*****************************************************************************/

static void
creategads (struct Window *win)
{
  BOOL okay = FALSE;
  LONG x, y, w, h, wspace;
  struct RastPort *rp;
  STRPTR label;



  /* Erzeuge verknüpfte Boopsi-Objekte */
  prop_object = NewObject (NULL, "propgclass",
			   GA_ID, 2,
		GA_Top, Mywindow->BorderTop + 2 * gap_h + infobar_h + box_h,
               GA_RelHeight, -(Mywindow->BorderTop + 2 * Mywindow->BorderBottom + 4 * gap_h + infobar_h + box_h),
			   GA_RelRight, -(Mywindow->BorderRight - 5),
			   GA_Width, Mywindow->BorderRight - 8,
			   GA_RightBorder, TRUE,
			   ICA_MAP, prop2text,
			   ICA_TARGET, NULL,
			   PGA_NewLook, TRUE,
			   PGA_Borderless, TRUE,
			   PGA_Visible, 50,
			   PGA_Total, 50,
			   TAG_END);

  up_image = NewObject (NULL, "sysiclass",
			SYSIA_DrawInfo, dri,
			SYSIA_Which, UPIMAGE,
			IA_Width, Mywindow->BorderRight,
			IA_Height, Mywindow->BorderBottom,
			TAG_END);

  down_image = NewObject (NULL, "sysiclass",
			  SYSIA_DrawInfo, dri,
			  SYSIA_Which, DOWNIMAGE,
			  IA_Width, Mywindow->BorderRight,
			  IA_Height, Mywindow->BorderBottom,
			  TAG_END);

  up_object = NewObject (NULL, "buttongclass",
			 GA_RelBottom, -(3 * Mywindow->BorderBottom) - 1,
			 GA_RelRight, -(Mywindow->BorderRight - 1),
			 GA_Height, Mywindow->BorderBottom,
			 GA_Width, Mywindow->BorderRight,
			 GA_Image, up_image,
			 GA_RightBorder, TRUE,
			 GA_Previous, prop_object,
			 ICA_MAP, up2text,
			 TAG_END);

  down_object = NewObject (NULL, "buttongclass",
			   GA_RelBottom, -(2 * Mywindow->BorderBottom),
			   GA_RelRight, -(Mywindow->BorderRight - 1),
			   GA_Height, Mywindow->BorderBottom,
			   GA_Width, Mywindow->BorderRight,
			   GA_Image, down_image,
			   GA_RightBorder, TRUE,
			   GA_Previous, up_object,
			   ICA_MAP, down2text,
			   TAG_END);


/*
   Clipboard für Textfield öffnen.
   Die Funktion OpenClipboard() kommt aus der iffparse.library.
 */
  clip_handle = OpenClipboard (0);
  undo_handle = OpenClipboard (42);

/* Das Textfeld ... */

  txtfld = NewObject (TextFieldClass, NULL,
		      GA_ID, 1,
		GA_Top, Mywindow->BorderTop + 2 * gap_h + infobar_h + box_h,
		      GA_Left, Mywindow->BorderLeft + gap_w,
   GA_RelWidth, -(Mywindow->BorderLeft + Mywindow->BorderRight + 2 * gap_w),
		      GA_RelHeight, -(Mywindow->BorderTop + Mywindow->BorderBottom + 4 * gap_h + infobar_h + box_h),
		      GA_Previous, down_object,
		      GA_TabCycle, TRUE,
		      ICA_MAP, text2prop,
		      ICA_TARGET, prop_object,
		      TEXTFIELD_Text, (ULONG) initial_text,
		      TEXTFIELD_UserAlign, FALSE,
		      TEXTFIELD_ClipStream, clip_handle,
		      TEXTFIELD_UndoStream, undo_handle,
		      TEXTFIELD_Border, TEXTFIELD_BORDER_NONE,
		      TEXTFIELD_BlinkRate, 500000,
		      TEXTFIELD_Spacing, 2,
		      TEXTFIELD_PassCommand, TRUE,	/* Amiga-Keys weiterleiten an IMsgPort */
		      TEXTFIELD_ReadOnly, FALSE,
		      TAG_END);

/* Tim Stacks BOOPSI ListView - System */

  creategad_group ();
  creategad_message ();

  okay = FALSE;			/* wichtig */

  if (txtfld && prop_object && up_image &&
      down_image && up_object && down_object)	/* BOOPSI! */
    if (gad_group && gad_message)	/* BOOPSI Listview */
      {
	okay = TRUE;
      }

  if (okay)			/* d.h. BOOPSI ist 100% perfekt alloziert */
    {

      /* BOOPSI-Verknüpfungen bearbeiten, 2nd pass ... */
      /* Hinweis: "SetGadgetAttrsX" ist Kludge. Grund: fieser Maxon-Compiler-Bug in SetGadgetAttrs()! */

      /* checks nötig? */
      SetGadgetAttrsX (prop_object, Mywindow, NULL, ICA_TARGET, txtfld, TAG_DONE);
      SetGadgetAttrsX (up_object, Mywindow, NULL, ICA_TARGET, txtfld, TAG_DONE);
      SetGadgetAttrsX (down_object, Mywindow, NULL, ICA_TARGET, txtfld, TAG_DONE);

      /* BOOPSI Listview */

      SetGadgetAttrsX (gad_group, Mywindow, NULL, ILGA_Lock, TRUE, TAG_DONE);
      SetGadgetAttrsX (gad_message, Mywindow, NULL, ILGA_Lock, TRUE, TAG_DONE);

      AddGList (Mywindow, prop_object, -1, -1, NULL);
      RefreshGadgets (Mywindow->FirstGadget, Mywindow, NULL);

      /* Textfield aktivieren */
      ActivateGadget (txtfld, Mywindow, NULL);

      /* Bestimme Breite und Offset der Gadgets. */
      /* die Gadgetliste des aktuellen Fensters wird durchsucht ... */

      rp = win->RPort;

      x = 8;			/* falls kein CloseGadget vorhanden: defaultwert für x (s.u. for()) */
      w = 30;
      h = rp->TxHeight + 3;	/* Gadgethöhe: Texthöhe */

/* GadgetTop, von Window-Bottom aus gemesssen (in Pixel): */
      y = -(h + win->BorderBottom + 3);

/* todo Abstand RelBottom */

      label = "mnNM";
      wspace = TextLength (rp, label, strlen (label));

      label = "Nächste";
      w = wspace + TextLength (rp, label, strlen (label));

      gads[GID_Naechste] = NewObject (NULL, buttonClassName,
				      GA_RelBottom, y,	/* Abstand von unten */
				      GA_Left, x,
				      GA_Width, w,
				      GA_Height, h,
				      GA_BottomBorder, TRUE,
				      GA_Immediate, TRUE,
				      GA_RelVerify, TRUE,
				      GA_ID, GID_Naechste,
				      GA_Text, label,
				 GA_UserData, (APTR) (DISPLAY_NEXT_MESSAGE),
				      TAG_DONE);

      x += w + 2;
      label = "Vorherige";
      w = wspace + TextLength (rp, label, strlen (label));
      gads[GID_Vorherige] = NewObject (NULL, buttonClassName,
				       GA_Previous, gads[GID_Vorherige - 1],
				       GA_RelBottom, y,		/* Abstand von unten */
				       GA_Left, x,
				       GA_Width, w,
				       GA_Height, h,
				       GA_BottomBorder, TRUE,
				       GA_Immediate, TRUE,
				       GA_RelVerify, TRUE,
				       GA_ID, GID_Vorherige,
				       GA_Text, label,
				 GA_UserData, (APTR) (DISPLAY_PREV_MESSAGE),
				       TAG_DONE);

      x += w + 2;
      label = "Bugliste zeigen";
      w = wspace + TextLength (rp, label, strlen (label));
      gads[GID_Messages] = NewObject (NULL, buttonClassName,
				      GA_Previous, gads[GID_Messages - 1],
				      GA_RelBottom, y,	/* Abstand von unten */
				      GA_Left, x,
				      GA_Width, w,
				      GA_Height, h,
				      GA_BottomBorder, TRUE,
				      GA_Immediate, TRUE,
				      GA_RelVerify, TRUE,
				      GA_ID, GID_Messages,
				      GA_Text, label,
				      GA_UserData, (APTR) (DISPLAY_BUGS),
				      TAG_DONE);


      x += w + 2;
      label = "Messagebase scannen";
      w = wspace + TextLength (rp, label, strlen (label));

      gads[GID_Gruppen] = NewObject (NULL, buttonClassName,
				     GA_Previous, gads[GID_Gruppen - 1],
				     GA_RelBottom, y,	/* Abstand von unten */
				     GA_Left, x,
				     GA_Width, w,
				     GA_Height, h,
				     GA_BottomBorder, TRUE,
				     GA_Immediate, TRUE,
				     GA_RelVerify, TRUE,
				     GA_ID, GID_Gruppen,
				     GA_Text, label,
				     GA_UserData, (APTR) (GET_NEW_GROUP),
				     TAG_DONE);

      x += w + 2;
      label = "Catch Up";
      w = wspace + TextLength (rp, label, strlen (label));

      gads[GID_CatchUp] = NewObject (NULL, buttonClassName,
				     GA_Previous, gads[GID_CatchUp - 1],
				     GA_RelBottom, y,	/* Abstand von unten */
				     GA_Left, x,
				     GA_Width, w,
				     GA_Height, h,
				     GA_BottomBorder, TRUE,
				     GA_Immediate, TRUE,
				     GA_RelVerify, TRUE,
				     GA_ID, GID_CatchUp,
				     GA_Text, label,
				GA_UserData, (APTR) (CATCHUP_CURRENT_GROUP),
				     TAG_DONE);

      x += w + 2;
      label = "Guide Hilfe";
      w = wspace + TextLength (rp, label, strlen (label));

      gads[GID_Hilfe] = NewObject (NULL, buttonClassName,
				   GA_Previous, gads[GID_Hilfe - 1],
				   GA_RelBottom, y,	/* Abstand von unten */
				   GA_Left, x,
				   GA_Width, w,
				   GA_Height, h,
				   GA_BottomBorder, TRUE,
				   GA_Immediate, TRUE,
				   GA_RelVerify, TRUE,

				   GA_ID, GID_Hilfe,
				   GA_Text, label,
				   GA_UserData, (APTR) (OPEN_AMIGAGUIDE),
				   TAG_DONE);

      /* Add and refresh the gadget list */
      AddGList (win, gads[0], 0, -1, NULL);
      RefreshGList (gads[0], win, NULL, -1);
    }
}

/*
 * selectgad -- Gadget (z.B. nach Tastendruck) als selektiert markieren
 *
 */

static void
selectgad (struct Window *win, UWORD sel, BOOL leave)
{
  register int i;

  for (i = 0; i < NUMGADS; i++)
    {
      if ((i != sel) || ((i == sel) && leave))
	{
	  if (!((i == sel) && (gads[i]->Flags & GFLG_SELECTED)))
	    SetGadgetAttrsX (gads[i], win, NULL, GA_Selected, (i == sel), TAG_DONE);
	}
    }
}

/*
 * deletegads-- Gadgets entfernen
 */

static void
deletegads (struct Window *win)
{
  register int i;

  /* alle Gadgets entfernen */
  RemoveGList (Mywindow, Mywindow->FirstGadget, -1);	/* Speicherleck, todo: alle Gadgets entfernen! */

  /* BOOPSI Scroller und Textfield */
  DisposeObject (txtfld);	/* safe with Null? :-) */
  DisposeObject (down_object);
  DisposeObject (up_object);
  DisposeObject (down_image);
  DisposeObject (up_image);
  DisposeObject (prop_object);

  /* Listview-Gadgets */
  DisposeObject (gad_group);
  DisposeObject (gad_message);

  /* Clipboard Handler schließen */
  if (undo_handle)
    {
      CloseClipboard (undo_handle);
    }

  if (clip_handle)
    {
      CloseClipboard (clip_handle);
    }

  /* DrawInfo Struktur freimachen */
  if (dri)
    {
      FreeScreenDrawInfo (Mywindow->WScreen, dri);
    }

  /* BOOPSI Buttons freimachen */
  for (i = 0; i < NUMGADS; i++)
    {
      if (gads[i])
	{
	  RemoveGList (win, gads[i], 1);
	  DisposeObject (gads[i]);
	}
      else
	printf ("Fehler: Gadget = NULL abgefangen!");
    }
}

/*
 * creategad_group -- Gruppenlistview erzeugen
 */

static LONG itemlisttags[] =
{ILGA_Selected, 0,
 ILGA_NumSelectable, NS_ONE,
 ILGA_Top, 0,
 ILGA_NoCareShift, TRUE,
 ILGA_HitTest, TRUE,
 TAG_DONE};

#define LV_Width 220

void 
creategad_group (void)
{
  gad_group = NewObject (NULL, "compoundlistviewgadget",
			 GA_Top, Mywindow->BorderTop + gap_h + infobar_h,
			 GA_Left, Mywindow->BorderLeft + gap_w,
			 GA_Width, LV_Width,
			 GA_Height, box_h,
			 GA_ID, LV_GROUP,
			 GA_Immediate, TRUE,
			 GA_RelVerify, TRUE,
			 CLV_ILTags, &itemlisttags,
			 CLV_DrawInfo, dri,
			 TAG_DONE);
  if (gad_group)
    {
      AddGadget (Mywindow, gad_group, -1);
      RefreshGadgets (Mywindow->FirstGadget, Mywindow, NULL);
    }
  else
    ShowError ("gad_group nicht erzeugt");
}

void 
creategad_message (void)
{
  gad_message = NewObject (NULL, "compoundlistviewgadget",
			   GA_Top, Mywindow->BorderTop + gap_h + infobar_h,
			   GA_Left, Mywindow->BorderLeft + gap_w + ((gad_group) ? (gad_group->Width) : (LV_Width)) + gap_w,
			   GA_Width, Mywindow->Width -
			   (((gad_group) ? (gad_group->Width) : (LV_Width))
			    + Mywindow->BorderLeft
			    + Mywindow->BorderRight + 3 * gap_w),
			   GA_Height, box_h,
			   GA_ID, LV_MESSAGE,
			   GA_Immediate, TRUE,
			   GA_RelVerify, TRUE,
			   CLV_ILTags, &itemlisttags,
			   CLV_DrawInfo, dri,
			   TAG_DONE);

  if (gad_message)
    {
      AddGadget (Mywindow, gad_message, -1);
      RefreshGadgets (Mywindow->FirstGadget, Mywindow, NULL);
    }
  else
    ShowError ("gad_message nicht erzeugt");
}
