/*
 * dockobj.c  V2.0
 *
 * TMObject, Type: Dock
 *
 * (c) 1990-1992 Stefan Becker
 */

#include "ToolManagerLib.h"

/* node for one tool */
struct DockTool {
                 struct DockTool    *dt_Next;
                 char               *dt_ExecName;
                 struct TMLink      *dt_ExecLink;
                 struct TMLinkImage *dt_ImageLink;
                 struct TMLink      *dt_SoundLink;
                };

/* extended TMObject structure for TMOBJTYPE_Dock objects */
struct TMObjectDock {
                     struct TMObject  do_Object;
                     ULONG            do_Flags;
                     ULONG            do_Columns;
                     struct TextAttr *do_Font;   /* User supplied font */
                     char            *do_HotKey;
                     ULONG            do_LeftEdge;
                     char            *do_PubScreen;
                     char            *do_Title;
                     struct DockTool *do_Tools;
                     ULONG            do_TopEdge;
                     CxObj           *do_CxObj;
                     struct TMLink    do_Link;
                     UWORD            do_Left;   /* Drawing start position */
                     UWORD            do_Top;
                     UWORD            do_Width;  /* Inner Window Width/Height */
                     UWORD            do_Height;
                     UWORD            do_XSize;  /* Image/Gadget box size */
                     UWORD            do_YSize;
                     struct Window   *do_Window;
                     void            *do_AppWindow;
                     struct Gadget    do_DragGadget;
                     UWORD            do_FillPen;
                     UWORD            do_BackPen;
                     struct TextAttr  do_TextAttr;   /* Font for gadgets */
                     struct TextFont *do_TextFont;   /* Screen font (open) */
                     struct TextFont *do_GadgetFont; /* Gadget font (open) */
                     void            *do_VisualInfo;
                     struct Menu     *do_Menu;
                     struct Gadget   *do_GadToolsGadgets;
                     struct DockTool *do_Selected;
                     UWORD            do_SelectX;
                     UWORD            do_SelectY;
                     ULONG            do_Seconds;
                     ULONG            do_Micros;
                    };

/* do_Flags */
#define DO_Activated (1L<<0)
#define DO_Centered  (1L<<1)
#define DO_FrontMost (1L<<2)
#define DO_Menu      (1L<<3)
#define DO_Pattern   (1L<<4)
#define DO_PopUp     (1L<<5)
#define DO_Text      (1L<<6)
#define DO_Vertical  (1L<<7)

/* Tag to Flag mapping table for PackBoolTags */
static struct TagItem flagmap[]={
                                 TMOP_Activated, DO_Activated,
                                 TMOP_Centered,  DO_Centered,
                                 TMOP_FrontMost, DO_FrontMost,
                                 TMOP_Menu,      DO_Menu,
                                 TMOP_Pattern,   DO_Pattern,
                                 TMOP_PopUp,     DO_PopUp,
                                 TMOP_Text,      DO_Text,
                                 TMOP_Vertical,  DO_Vertical,
                                 TAG_DONE};

/* Library bases */
struct Library *DiskfontBase;

/* Close a window safely */
static void SafeCloseWindow(struct Window *w)
{
 struct IntuiMessage *msg;

 DEBUG_PRINTF("SafeCloseWindow: 0x%08lx\n",w);

 /* we forbid here to keep out of race conditions with Intuition */
 Forbid();

 /* Remove all messsages for this window */
 msg=GetHead(&w->UserPort->mp_MsgList);
 while (msg)
  /* Does this message point to the window? */
  if (msg->IDCMPWindow==w) {
   struct IntuiMessage *nextmsg=GetSucc(msg);

   /* Yes. Remove it from port */
   Remove((struct Node *) msg);

   /* Reply it */
   ReplyMsg((struct Message *) msg);

   /* Get pointer to next message */
   msg=nextmsg;
  }
   /* No. Get pointer to next message */
  else msg=GetSucc(msg);

 /* clear UserPort so Intuition will not free it */
 w->UserPort=NULL;

 /* tell Intuition to stop sending more messages */
 ModifyIDCMP(w,0);

 /* turn multitasking back on */
 Permit();

 DEBUG_PRINTF("Closing window\n");

 /* and really close the window */
 CloseWindow(w);

 DEBUG_PRINTF("Window closed\n");
}

/* Pattern */
static UWORD BackgroundPattern[]={0xaaaa,0x5555};

/* Open Dock Window */
static void OpenDockWindow(struct TMObjectDock *tmobj)
{
 struct Screen *pubsc;

 /* Open on frontmost public screen? */
 if (tmobj->do_Flags & DO_FrontMost) {
  ULONG lock;

  /* Avoid race conditions */
  Forbid();

  /* Lock IntuitionBase */
  lock=LockIBase(0);

  /* Scan List */
  pubsc=IntuitionBase->FirstScreen;
  while (pubsc) {
   ULONG type=pubsc->Flags & SCREENTYPE;

   DEBUG_PRINTF("Screen flags: 0x%08lx",pubsc->Flags);
   DEBUG_PRINTF(" Screen type: 0x%08lx\n",type);

   /* Found a public screen? */
   if ((type==WBENCHSCREEN) || (type==PUBLICSCREEN)) break;

   /* Get pointer to next screen */
   pubsc=pubsc->NextScreen;
  }

  /* Unlock IntuitionBase */
  UnlockIBase(lock);

  /* Lock public screen */
  if (pubsc) {
   struct List *pubsclist;

   /* Get a pointer to the public screen list */
   if (pubsclist=LockPubScreenList()) {
    struct PubScreenNode *pubscnode=GetHead(pubsclist);
    UBYTE namebuf[MAXPUBSCREENNAME+1];

    /* Scan list */
    while (pubscnode) {
     /* Does this node point to our screen? */
     if (pubscnode->psn_Screen==pubsc) {
      /* Yes. Copy screen name */
      strcpy(namebuf,pubscnode->psn_Node.ln_Name);
      break;
     }

     /* get a pointer to next node */
     pubscnode=GetSucc(pubscnode);
    }

    /* Release public screen list */
    UnlockPubScreenList();

    /* Lock screen */
    if (pubscnode)
     pubsc=LockPubScreen(namebuf);
    else
     pubsc=NULL;
   }
   else pubsc=NULL; /* No public screens??? */
  }

  /* OK, we have a screen now */
  Permit();
 } else
  /* Lock public screen */
  pubsc=LockPubScreen(tmobj->do_PubScreen);

 DEBUG_PRINTF("PubScreen: 0x%08lx\n",pubsc);

 /* Got a locked public screen? */
 if (pubsc) {
  struct DrawInfo *dri;

  /* Get screen draw info */
  if (dri=GetScreenDrawInfo(pubsc)) {
   void *vi;

   /* Get screen visual info */
   if (vi=GetVisualInfo(pubsc,TAG_DONE)) {
    struct TextFont *tf;

    /* Open screen font */
    if (tf=OpenFont(pubsc->Font)) {
     struct Window *w;
     struct DockTool *dt=tmobj->do_Tools;
     struct TextFont *gadfont=NULL;
     UWORD wx=0,wy=0;
     BOOL borderless=tmobj->do_Title==NULL;
     BOOL text=(tmobj->do_Flags & DO_Text)!=0;

     /* Got ANY tools??? */
     if (dt) {
      /* Calculate box sizes */
      ULONG maxx=0,maxy=0,count=0;
      ULONG fheight;
      struct RastPort tmprp;

      /* Init RastPort for text writing */
      InitRastPort(&tmprp);

      /* Set default font */
      SetFont(&tmprp,tf);
      fheight=tf->tf_YSize+INTERHEIGHT+1;

      /* User specified font? */
      if (tmobj->do_Font &&
          (DiskfontBase=OpenLibrary("diskfont.library",36))) {
       /* OK, load font now */
       if (gadfont=OpenDiskFont(tmobj->do_Font)) {
        /* Set font */
        SetFont(&tmprp,gadfont);
        fheight=gadfont->tf_YSize+INTERHEIGHT+1;
       }

       /* Close library again */
       CloseLibrary(DiskfontBase);
      }

      DEBUG_PRINTF("TextAttr: 0x%08lx",tmobj->do_Font);
      DEBUG_PRINTF(" Font: 0x%08lx\n",gadfont);

      /* Scan tool list */
      while (dt) {
       ULONG valx=0,valy=0;

       if (text) {
        /* Gadgets */
        char *s=dt->dt_ExecName;

        /* Link and name valid? */
        if (dt->dt_ExecLink && s) {
         valx=TextLength(&tmprp,s,strlen(s))+2*INTERWIDTH+2;
         valy=fheight;
        }
       } else {
        /* Images */
        struct TMLinkImage *tmli=dt->dt_ImageLink;

        /* Link to image valid? */
        if (tmli) {
         /* max(width) */
         valx=tmli->tmli_Width;
         valy=tmli->tmli_Height-1;
        }
       }

       /* Check maxima */
       if (valx>maxx) maxx=valx;
       if (valy>maxy) maxy=valy;

       /* Increment counter & Get pointer to next tool */
       count++;
       dt=dt->dt_Next;
      }

      /* Calculate window sizes */
      tmobj->do_Width=tmobj->do_Columns*maxx;
      tmobj->do_Height=((count+tmobj->do_Columns-1)/tmobj->do_Columns)*maxy;
      tmobj->do_XSize=maxx;
      tmobj->do_YSize=maxy;
     }

     /* Got valid window sizes? */
     if (((LONG) tmobj->do_XSize>1) && ((LONG) tmobj->do_YSize>1)) {
      BOOL menu=(tmobj->do_Flags & DO_Menu)!=0;

      /* Correct window sizes */
      if (borderless) {
       /* Borderless window */
       struct Gadget *g=&tmobj->do_DragGadget;

       /* Gadget window */
       if (text) {
        tmobj->do_Width--;
        tmobj->do_Height--;
       }

       /* Calculate drag gadget dimensions */
       if (tmobj->do_Flags & DO_Vertical) {
        /* Vertical drag gadget */
        struct RastPort tmprp;
        UWORD gwidth;

        /* Init RastPort for text writing */
        InitRastPort(&tmprp);
        SetFont(&tmprp,tf);
        gwidth=TextLength(&tmprp,"A",1);

        tmobj->do_Left=gwidth;
        tmobj->do_Top=0;
        tmobj->do_Width+=gwidth;
        g->Width=gwidth;
        g->Height=tmobj->do_Height;
       } else {
        /* Horizontal drag gadget */
        UWORD gheight=(tf->tf_YSize+8)/2;

        tmobj->do_Left=0;
        tmobj->do_Top=gheight;
        tmobj->do_Height+=gheight;
        g->Width=tmobj->do_Width;
        g->Height=gheight;
       }

       /* Init drag gadget structure */
       g->Flags=GFLG_GADGHNONE;
       g->Activation=GACT_IMMEDIATE;
       g->GadgetType=GTYP_SYSGADGET|GTYP_WDRAGGING;

       /* Get pens */
       tmobj->do_FillPen=dri->dri_Pens[FILLPEN];
       tmobj->do_BackPen=dri->dri_Pens[BACKGROUNDPEN];
      } else {
       /* Normal window */
       tmobj->do_Left=pubsc->WBorLeft+1;
       tmobj->do_Top=pubsc->WBorTop+tf->tf_YSize+2;
       if (text) {
        tmobj->do_Width++;
        tmobj->do_Height++;
       } else {
        tmobj->do_Width+=2;
        tmobj->do_Height+=2;
       }
      }

      /* Centered window? */
      if (tmobj->do_Flags & DO_Centered) {
       /* Centered */
       /* Borderless window: X = MouseX - (width-left)/2 - left */
       /* Normal window    : X = MouseX -        width/2 - left */
       wx=tmobj->do_Width;
       wy=tmobj->do_Height;

       /* Borderless window? */
       if (borderless) {
        wx-=tmobj->do_Left;
        wy-=tmobj->do_Top;
       }

       wx=pubsc->MouseX-wx/2-tmobj->do_Left;
       wy=pubsc->MouseY-wy/2-tmobj->do_Top;
      } else {
       /* Not centered */
       wx=tmobj->do_LeftEdge;
       wy=tmobj->do_TopEdge;
      }

      /* Dock with menu? */
      if (menu &&
          (tmobj->do_Menu=CreateMenus(DockMenu,GTMN_FullMenu,TRUE,TAG_DONE)) &&
          !LayoutMenus(tmobj->do_Menu,vi,TAG_DONE))
       tmobj->do_Menu=NULL;

      DEBUG_PRINTF("Boxes: X %ld",tmobj->do_XSize);
      DEBUG_PRINTF(" Y %ld",tmobj->do_YSize);
      DEBUG_PRINTF(" Window: Width %ld",tmobj->do_Width);
      DEBUG_PRINTF(" Height %ld\n",tmobj->do_Height);
      DEBUG_PRINTF("menu %ld",menu);
      DEBUG_PRINTF(" Menu 0x%08lx\n",tmobj->do_Menu);

      /* Open window */
      if ((!menu || tmobj->do_Menu) &&
          (w=OpenWindowTags(NULL,WA_Left,        wx,
                                 WA_Top,         wy,
                                 WA_InnerWidth,  tmobj->do_Width,
                                 WA_InnerHeight, tmobj->do_Height,
                                 WA_Title,       tmobj->do_Title,
                                 WA_DragBar,     !borderless,
                                 WA_CloseGadget, !borderless,
                                 WA_DepthGadget, !borderless,
                                 WA_Borderless,  borderless,
                                 WA_PubScreen,   pubsc,
                                 WA_RMBTrap,     !menu,
                                 WA_AutoAdjust,  TRUE,
                                 WA_IDCMP,       0,
                                 TAG_DONE))) {
       /* Set window pointers */
       w->UserPort=IDCMPPort;
       w->UserData=tmobj;

       DEBUG_PRINTF("Window: 0x%08lx\n",w);

       /* Set IDCMP flags */
       if (ModifyIDCMP(w,IDCMP_ACTIVEWINDOW|IDCMP_INACTIVEWINDOW|IDCMP_GADGETUP|
                         IDCMP_MOUSEBUTTONS|IDCMP_CLOSEWINDOW|IDCMP_MENUPICK|
                         IDCMP_REFRESHWINDOW)) {
        BOOL wbscreen=(pubsc->Flags & SCREENTYPE)==WBENCHSCREEN;
        BOOL wbopen=FALSE;

        /* Try to add the window as application window */
        tmobj->do_AppWindow=NULL;
        if (wbscreen && (wbopen=GetWorkbench()))
         tmobj->do_AppWindow=AddAppWindowA((ULONG) &tmobj->do_Link,NULL,w,
                                           AppMsgPort,NULL);

        /* Not on Workbench screen, no Workbench or AddAppWindow successful? */
        if (!wbscreen || !wbopen || tmobj->do_AppWindow) {
         struct Gadget *g;

         /* Draw drag gadget */
         if (borderless) {
          struct RastPort *rp=w->RPort;
          UWORD width=tmobj->do_DragGadget.Width-1;
          UWORD height=tmobj->do_DragGadget.Height-1;

          /* Draw lighted side */
          SetAPen(rp,dri->dri_Pens[SHINEPEN]);
          Move(rp,0,height);
          Draw(rp,0,0);
          Draw(rp,width,0);

          /* Draw shadowed side */
          SetAPen(rp,dri->dri_Pens[SHADOWPEN]);
          Move(rp,width,1);
          Draw(rp,width,height);
          Draw(rp,0,height);

          AddGadget(w,&tmobj->do_DragGadget,(UWORD) -1);
          RefreshGList(&tmobj->do_DragGadget,w,NULL,1);
         }

         /* Draw initial window state */
         {
          struct RastPort *rp=w->RPort;
          ULONG col=0,lin=0;
          UWORD x=tmobj->do_Left;
          UWORD y=tmobj->do_Top;
          UWORD dw=tmobj->do_XSize;
          UWORD dh=tmobj->do_YSize;
          BOOL pattern=((tmobj->do_Flags & DO_Pattern)!=0 && !text);
          struct NewGadget ng;

          /* Set pattern */
          if (pattern) {
           SetAPen(rp,dri->dri_Pens[SHADOWPEN]);
           SetDrMd(rp,JAM2);
           SetAfPt(rp,BackgroundPattern,1);
          }

          /* Set pointer to begin of tool list */
          dt=tmobj->do_Tools;

          /* Init TextAttr & NewGadget struct, create GadTools context */
          if (text) {
           /* Font set? */
           if (tmobj->do_Font)
            tmobj->do_TextAttr=*(tmobj->do_Font);
           else {
            /* No. Set screen font */
            tmobj->do_TextAttr.ta_Name=tf->tf_Message.mn_Node.ln_Name;
            tmobj->do_TextAttr.ta_YSize=tf->tf_YSize;
            tmobj->do_TextAttr.ta_Style=tf->tf_Style;
            tmobj->do_TextAttr.ta_Flags=tf->tf_Flags;
           }

           DEBUG_PRINTF("Font '%s'",tmobj->do_TextAttr.ta_Name);
           DEBUG_PRINTF(" Size %ld\n",tmobj->do_TextAttr.ta_YSize);

           ng.ng_Width=dw-1;
           ng.ng_Height=dh-1;
           ng.ng_TextAttr=&tmobj->do_TextAttr;
           ng.ng_Flags=PLACETEXT_IN;
           ng.ng_VisualInfo=vi;
           ng.ng_UserData=0;
           tmobj->do_GadToolsGadgets=NULL;
           if (!(g=CreateContext(&tmobj->do_GadToolsGadgets)))
            dt=NULL; /* Error!!! */
          }

          /* Scan tool list */
          while (dt) {
           if (text) {
            /* Gadgets. Link valid? */
            if (dt->dt_ExecLink) {
             /* Init NewGadget values */
             ng.ng_LeftEdge=x;
             ng.ng_TopEdge=y;
             ng.ng_GadgetText=dt->dt_ExecName;
             ng.ng_UserData=dt;

             /* Create button gadget */
             if (!(g=CreateGadget(BUTTON_KIND,g,&ng,TAG_DONE)))
              dt=NULL; /* Error!!!! */
            }
           } else {
            /* Images */
            struct TMLinkImage *tmli=dt->dt_ImageLink;

            /* Draw pattern? */
            if (pattern) {
             /* Set Pattern color */
             SetBPen(rp,(col+lin)&1 ? dri->dri_Pens[BACKGROUNDPEN] :
                                      dri->dri_Pens[FILLPEN]);

             /* Draw pattern */
             RectFill(rp,x,y,x+dw-1,y+dh-1);
            }

            /* Link valid? */
            if (tmli) {
             /* Yes. Set Parameters */
             tmli->tmli_LeftEdge=x+(dw - tmli->tmli_Width)/2;
             tmli->tmli_TopEdge=y+(dh + 1 - tmli->tmli_Height)/2;
             tmli->tmli_RastPort=w->RPort;

             /* Draw deactivated state */
             CallActivateTMObject((struct TMLink *) tmli,(void *) IOC_DEACTIVE);
            }
           }

           /* Next column */
           col++;

           /* Increment x,y. Last Column? */
           if (col==tmobj->do_Columns) {
            /* Yes */
            y+=dh;
            x=tmobj->do_Left;
            col=0;
            lin++;
           } else
            /* No */
            x+=dw;

           /* Get pointer to next node */
           if (dt) dt=dt->dt_Next;
          }

          /* Reset Areafill pattern */
          if (pattern) SetAfPt(rp,NULL,0);
         }

         /* All OK. */
         if (!text || g) {
          /* Add GadTools gadgets */
          if (text) {
           AddGList(w,tmobj->do_GadToolsGadgets,(UWORD) -1,(UWORD) -1,NULL);
           RefreshGList(tmobj->do_GadToolsGadgets,w,NULL,(UWORD) -1);
           GT_RefreshWindow(w,NULL);
          }

          /* Add menu */
          if (menu) SetMenuStrip(w,tmobj->do_Menu);

          /* Free resources */
          FreeScreenDrawInfo(pubsc,dri);
          UnlockPubScreen(NULL,pubsc);

          /* Save pointers */
          tmobj->do_VisualInfo=vi;
          tmobj->do_TextFont=tf;
          tmobj->do_GadgetFont=gadfont;
          tmobj->do_Window=w;
          return;
          /* NOT REACHED */
         }

         /* Remove drag Gadget */
         if (borderless) RemoveGadget(w,&tmobj->do_DragGadget);

         /* Free GadTools gadgets */
         if (text) {
          FreeGadgets(tmobj->do_GadToolsGadgets);
          tmobj->do_GadToolsGadgets=NULL;
         }

         /* Remove AppWindow */
         if (tmobj->do_AppWindow)
          SafeRemoveAppWindow(tmobj->do_AppWindow,&tmobj->do_Link);
        }
        if (wbopen) FreeWorkbench();
       }
       SafeCloseWindow(w);
      }
      if (tmobj->do_Menu) {
       FreeMenus(tmobj->do_Menu);
       tmobj->do_Menu=NULL;
      }
     }
     if (gadfont) CloseFont(gadfont);
     CloseFont(tf);
    }
    FreeVisualInfo(vi);
   }
   FreeScreenDrawInfo(pubsc,dri);
  }
  UnlockPubScreen(NULL,pubsc);
 }
 /* Dock couldn't be opened! */
 DisplayBeep(NULL);
}

/* Close Dock window */
static void CloseDockWindow(struct TMObjectDock *tmobj)
{
 struct DockTool *dt=tmobj->do_Tools;
 struct Window *w=tmobj->do_Window;

 DEBUG_PRINTF("CloseDockWindow: 0x%08lx\n",tmobj);

 /* Deactivate all Image objects */
 while (dt) {
  /* Image object active? */
  if (dt->dt_ImageLink && dt->dt_ImageLink->tmli_Link.tml_Active)
   /* Yes. Deactivate it first */
   CallActivateTMObject((struct TMLink *) dt->dt_ImageLink,
                        (void *) IOC_DEACTIVE);

  /* Get pointer to next node */
  dt=dt->dt_Next;
 }

 /* Remove menu */
 if (tmobj->do_Menu) {
  ClearMenuStrip(w);
  tmobj->do_Menu=NULL;
 }

 /* Remove drag gadget */
 if (!tmobj->do_Title) RemoveGadget(w,&tmobj->do_DragGadget);

 /* Remove gadtools gadgets */
 if (tmobj->do_GadToolsGadgets) {
  RemoveGList(w,tmobj->do_GadToolsGadgets,(UWORD) -1);
  FreeGadgets(tmobj->do_GadToolsGadgets);
  tmobj->do_GadToolsGadgets=NULL;
 }

 /* Remove AppWindow */
 if (tmobj->do_AppWindow) {
  SafeRemoveAppWindow(tmobj->do_AppWindow,&tmobj->do_Link);
  FreeWorkbench();
 }

 /* Remember last window position */
 tmobj->do_LeftEdge=w->LeftEdge;
 tmobj->do_TopEdge=w->TopEdge;

 /* Close Window */
 SafeCloseWindow(w);
 tmobj->do_Window=NULL;

 /* Free resources */
 if (tmobj->do_GadgetFont) CloseFont(tmobj->do_GadgetFont);
 CloseFont(tmobj->do_TextFont);
 FreeVisualInfo(tmobj->do_VisualInfo);
}

/* Free tools list */
static void FreeTools(struct TMObjectDock *tmobj)
{
 struct DockTool *dt,*nextdt=tmobj->do_Tools;

 /* Scan list */
 while (dt=nextdt) {
  /* Get pointer to next node */
  nextdt=dt->dt_Next;

  /* Remove links */
  if (dt->dt_ExecLink) RemLinkTMObject(dt->dt_ExecLink);
  if (dt->dt_ImageLink) RemLinkTMObject((struct TMLink *) dt->dt_ImageLink);
  if (dt->dt_SoundLink) RemLinkTMObject(dt->dt_SoundLink);

  /* Free Node */
  FreeMem(dt,sizeof(struct DockTool));
 }
}

/* Create a Dock object */
struct TMObject *CreateTMObjectDock(struct TMHandle *handle, char *name,
                                    struct TagItem *tags)
{
 struct TMObjectDock *tmobj;

 /* allocate memory for object */
 if (tmobj=AllocateTMObject(sizeof(struct TMObjectDock))) {
  struct TagItem *ti,*tstate;
  struct DockTool *dt=NULL;

  /* Set defaults */
  tmobj->do_Columns=1;

  /* Scan tag list */
  tstate=tags;
  while (ti=NextTagItem(&tstate)) {

   DEBUG_PRINTF("Got Tag (0x%08lx)\n",ti->ti_Tag);

   switch (ti->ti_Tag) {
    case TMOP_Columns:   if (ti->ti_Data) tmobj->do_Columns=ti->ti_Data;
                         break;
    case TMOP_Font:      {
                          struct TextAttr *ta=(struct TextAttr *) ti->ti_Data;

                          if (ta && ta->ta_Name) tmobj->do_Font=ta;
                         }
                         break;
    case TMOP_HotKey:    tmobj->do_HotKey=(char *) ti->ti_Data;
                         break;
    case TMOP_LeftEdge:  tmobj->do_LeftEdge=ti->ti_Data;
                         break;
    case TMOP_PubScreen: tmobj->do_PubScreen=(char *) ti->ti_Data;
                         break;
    case TMOP_Title:     tmobj->do_Title=(char *) ti->ti_Data;
                         break;
    case TMOP_Tool:      if (ti->ti_Data) {
                          struct DockTool *newdt;

                          /* Alloc DockTool structure */
                          if (newdt=AllocMem(sizeof(struct DockTool),
                                             MEMF_CLEAR)) {
                           char **names=(char **) ti->ti_Data;

                           /* Add link to Exec object */
                           if (names[0] &&
                               (newdt->dt_ExecLink=AddLinkTMObject(handle,
                                                                  names[0],
                                                                TMOBJTYPE_EXEC,
                                                            (struct TMObject *)
                                                                  tmobj)))
                            /* Get name of exec object. We use names[0]
                               instead of object name, because the link could
                               be deleted while window is still active. */
                            newdt->dt_ExecName=names[0];

                           /* Add link to Image object */
                           if (names[1])
                            newdt->dt_ImageLink=AddLinkTMObject(handle,names[1],
                                                                TMOBJTYPE_IMAGE,
                                                            (struct TMObject *)
                                                                tmobj);

                           /* Add link to Sound object */
                           if (names[2])
                            newdt->dt_SoundLink=AddLinkTMObject(handle,names[2],
                                                                TMOBJTYPE_SOUND,
                                                            (struct TMObject *)
                                                                tmobj);

                           /* Add node to list. Head of list? */
                           if (dt) {
                            /* No. Add to tail */
                            dt->dt_Next=newdt;
                            dt=newdt;
                           } else {
                            /* Yes. Init list anchor */
                            tmobj->do_Tools=newdt;
                            dt=newdt;
                           }
                          }
                         }
                         break;
    case TMOP_TopEdge:   tmobj->do_TopEdge=ti->ti_Data;
                         break;
   }
  }

  {
   BOOL hotkey=tmobj->do_HotKey!=NULL;

   /* No HotKey or cannot create CxObject from HotKey description? */
   if (hotkey && (tmobj->do_CxObj=HotKey(tmobj->do_HotKey,BrokerPort,
                                         (ULONG) &tmobj->do_Link))) {

    DEBUG_PRINTF("Created CxObj (0x%08lx)\n",tmobj->do_CxObj);

    /* Attach object to broker */
    AttachCxObj(Broker,tmobj->do_CxObj);
   }

   /* No HotKey or Commodities error? */
   if (!hotkey || (tmobj->do_CxObj && !CxObjError(Broker))) {
    /* Set flags */
    tmobj->do_Flags=PackBoolTags(DO_Activated,tags,flagmap);

    /* Initialize rest of structure */
    tmobj->do_Link.tml_Linked=tmobj;

    /* Active flag set? */
    if (tmobj->do_Flags & DO_Activated)
     /* Yes. Open dock window */
     OpenDockWindow(tmobj);

    /* All OK */
    return(tmobj);
   }

   /* Free resources */
   if (tmobj->do_CxObj) SafeDeleteCxObjAll(tmobj->do_CxObj,&tmobj->do_Link);
  }
  FreeTools(tmobj);
 }

 /* call failed */
 return(NULL);
}

/* Delete a Dock object */
BOOL DeleteTMObjectDock(struct TMObjectDock *tmobj)
{
 DEBUG_PRINTF("Delete/Dock (0x%08lx)\n",tmobj);

 /* Free resources */
 if (tmobj->do_CxObj) SafeDeleteCxObjAll(tmobj->do_CxObj,&tmobj->do_Link);

 /* Close window and free its resources */
 if (tmobj->do_Window) CloseDockWindow(tmobj);

 /* Free tool nodes */
 FreeTools(tmobj);

 /* Remove object from list */
 Remove((struct Node *) tmobj);

 /* Free object */
 FreeMem(tmobj,sizeof(struct TMObjectDock));

 /* All OK. */
 return(TRUE);
}

/* Change a Dock object */
struct TMObject *ChangeTMObjectDock(struct TMHandle *handle,
                                    struct TMObjectDock *tmobj,
                                    struct TagItem *tags)
{
 struct TagItem *ti,*tstate;
 struct DockTool *dt;

 /* Search last dock tool */
 dt=tmobj->do_Tools;
 if (dt)
  while (dt->dt_Next) dt=dt->dt_Next;

 /* Close dock */
 if (tmobj->do_Window) CloseDockWindow(tmobj);

 /* Scan tag list */
 tstate=tags;
 while (ti=NextTagItem(&tstate)) {

  DEBUG_PRINTF("Got Tag (0x%08lx)\n",ti->ti_Tag);

  switch (ti->ti_Tag) {
   case TMOP_Columns:   if (ti->ti_Data) tmobj->do_Columns=ti->ti_Data;
                        break;
   case TMOP_Font:      {
                         struct TextAttr *ta=(struct TextAttr *) ti->ti_Data;

                         if (ta && ta->ta_Name) tmobj->do_Font=ta;
                        }
                        break;
   case TMOP_HotKey:    if (tmobj->do_CxObj) {
                         SafeDeleteCxObjAll(tmobj->do_CxObj,&tmobj->do_Link);
                         tmobj->do_CxObj=NULL;
                        }

                        tmobj->do_HotKey=(char *) ti->ti_Data;
                        break;
   case TMOP_LeftEdge:  tmobj->do_LeftEdge=ti->ti_Data;
                        break;
   case TMOP_PubScreen: tmobj->do_PubScreen=(char *) ti->ti_Data;
                        break;
   case TMOP_Title:     tmobj->do_Title=(char *) ti->ti_Data;
                        break;
   case TMOP_Tool:      if (ti->ti_Data) {
                         struct DockTool *newdt;

                         /* Alloc DockTool structure */
                         if (newdt=AllocMem(sizeof(struct DockTool),
                                            MEMF_CLEAR)) {
                          char **names=(char **) ti->ti_Data;

                          /* Add link to Exec object */
                          if (names[0] &&
                              (newdt->dt_ExecLink=AddLinkTMObject(handle,
                                                                  names[0],
                                                                TMOBJTYPE_EXEC,
                                                            (struct TMObject *)
                                                                  tmobj)))
                           /* Get name of exec object. We use names[0]
                              instead of object name, because the link could
                              be deleted while window is still active. */
                           newdt->dt_ExecName=names[0];

                          /* Add link to Image object */
                          if (names[1])
                           newdt->dt_ImageLink=AddLinkTMObject(handle,names[1],
                                                               TMOBJTYPE_IMAGE,
                                                           (struct TMObject *)
                                                               tmobj);

                          /* Add link to Sound object */
                          if (names[2])
                           newdt->dt_SoundLink=AddLinkTMObject(handle,names[2],
                                                               TMOBJTYPE_SOUND,
                                                           (struct TMObject *)
                                                               tmobj);

                          /* Add node to list. Head of list? */
                          if (dt) {
                           /* No. Add to tail */
                           dt->dt_Next=newdt;
                           dt=newdt;
                          } else {
                           /* Yes. Init list anchor */
                           tmobj->do_Tools=newdt;
                           dt=newdt;
                          }
                         }
                        }
                        break;
   case TMOP_TopEdge:   tmobj->do_TopEdge=ti->ti_Data;
                        break;
  }
 }

 /* Set new flags */
 tmobj->do_Flags=PackBoolTags(tmobj->do_Flags,tags,flagmap);

 /* HotKey set? Create Commodities object from HotKey description */
 if (tmobj->do_HotKey && !tmobj->do_CxObj &&
     (tmobj->do_CxObj=HotKey(tmobj->do_HotKey,BrokerPort,
                             (ULONG) &tmobj->do_Link))) {

  DEBUG_PRINTF("Created CxObj (0x%08lx)\n",tmobj->do_CxObj);

  /* Attach object to broker */
  AttachCxObj(Broker,tmobj->do_CxObj);

  /* Commodities error? */
  if (CxObjError(Broker)) {
   SafeDeleteCxObjAll(tmobj->do_CxObj,&tmobj->do_Link);
   tmobj->do_CxObj=NULL;
  }
 }

 /* Active flag set? */
 if (tmobj->do_Flags & DO_Activated)
  /* Yes. Open dock window */
  OpenDockWindow(tmobj);

 /* All OK. */
 return(TRUE);
}

/* Update link structures */
void DeleteLinkTMObjectDock(struct TMLink *tml)
{
 struct TMObjectDock *tmobj=tml->tml_LinkedTo;
 struct DockTool *dt=tmobj->do_Tools;

 /* Scan tool list */
 while (dt) {
  /* Link to Exec object? */
  if (tml==dt->dt_ExecLink) {
   dt->dt_ExecLink=NULL;
   dt->dt_ExecName=NULL;
   break;
  }
  /* Link to Image object? */
  else if (tml==(struct TMLink *) dt->dt_ImageLink) {
   /* Image object active? */
   if (tml->tml_Active)
    /* Yes. Deactivate it */
    CallActivateTMObject((struct TMLink *) tml,(void *) IOC_DEACTIVE);

   dt->dt_ImageLink=NULL;
   break;
  }
  /* Link to Sound object? */
  else if (tml==dt->dt_SoundLink) {
   dt->dt_SoundLink=NULL;
   break;
  }

  /* Get pointer to next tool */
  dt=dt->dt_Next;
 }
}

/* Find dock tool that corresponds to X,Y position */
static struct DockTool *FindDockTool(struct TMObjectDock *tmobj, ULONG x,
                                     ULONG y)
{
 struct DockTool *dt;
 UWORD dx=tmobj->do_Left;
 UWORD dy=tmobj->do_Top;
 UWORD w,h;
 ULONG col,cols;

 /* Out of bounds? */
 if ((x<dx) || (y<dy)) return(NULL); /* Yes! */

 /* Init variables */
 dt=tmobj->do_Tools;
 w=tmobj->do_XSize;
 h=tmobj->do_YSize;
 cols=tmobj->do_Columns;
 col=0;

 /* Scan dock tool list */
 while (dt)
  if ((x>=dx) && (y>=dy) && (x<dx+w) && (y<dy+h)) {
   /* Tool found */
   tmobj->do_Selected=dt;
   tmobj->do_SelectX=dx;
   tmobj->do_SelectY=dy;
   return(dt);
  } else {
   /* Increment column counter */
   col++;

   /* Last column? */
   if (col==cols) {
    /* Yes */
    dx=tmobj->do_Left;
    dy+=h;
    col=0;
   } else
    /* No */
    dx+=w;

   /* Get pointer to next dock tool */
   dt=dt->dt_Next;
  }

 /* Dock tool not found */
 return(NULL);
}

/* Activate a Dock object */
void ActivateTMObjectDock(struct TMLink *tml, struct AppMessage *msg)
{
 struct TMObjectDock *tmobj=tml->tml_Linked;

 DEBUG_PRINTF("Activate/Dock (0x%08lx)\n",msg);

 /* Got arguments? */
 if (msg) {
  /* Yes. Icons were dropped on the dock */
  struct DockTool *dt;

  /* Find Tool */
  if (dt=FindDockTool(tmobj,msg->am_MouseX,msg->am_MouseY)) {
   /* Activate Sound object */
   if (dt->dt_SoundLink) CallActivateTMObject(dt->dt_SoundLink,NULL);

   /* Activate Image object */
   if (!(tmobj->do_Flags & DO_Text) && dt->dt_ImageLink)
    CallActivateTMObject((struct TMLink *) dt->dt_ImageLink,
                         (void *) IOC_FULLANIM);

   /* Activate Exec object */
   if (dt->dt_ExecLink) CallActivateTMObject(dt->dt_ExecLink,msg);

   /* Clear pointer */
   tmobj->do_Selected=NULL;

   /* PopUp dock? Yes, close window */
   if (tmobj->do_Flags & DO_PopUp) CloseDockWindow(tmobj);
  }
 }
 /* No. User pressed HotKey. Window open? */
 else if (tmobj->do_Window)
  /* Close window and free its resources */
  CloseDockWindow(tmobj);
 else {
  /* Window not open. Open it. */
  OpenDockWindow(tmobj);

  /* If DO_FrontMost is not set, move screen to front */
  if (!(tmobj->do_Flags & DO_FrontMost) && tmobj->do_Window)
   ScreenToFront(tmobj->do_Window->WScreen);
 }
}

/* Handle IDCMP events */
void HandleIDCMPEvents(void)
{
 struct IntuiMessage *msg;

 /* Scan IDCMP message port */
 while (msg=GT_GetIMsg(IDCMPPort)) {
  struct TMObjectDock *tmobj=msg->IDCMPWindow->UserData;
  BOOL closewindow=FALSE;

  DEBUG_PRINTF("IDCMP Class: 0x%08lx\n",msg->Class);

  switch (msg->Class) {
   case IDCMP_CLOSEWINDOW:    /* Set close flag */
                              closewindow=TRUE;
                              break;
   case IDCMP_REFRESHWINDOW:  {
                               struct Window *w=tmobj->do_Window;

                               GT_BeginRefresh(w);
                               GT_EndRefresh(w,TRUE);
                              }
                              break;
   case IDCMP_MOUSEBUTTONS:   /* Only docks with images need button events */
                              if (!(tmobj->do_Flags & DO_Text))
                               switch (msg->Code) {
                                case SELECTDOWN: /* Select button pressed */
                                 {
                                  struct DockTool *dt;

                                  if (dt=FindDockTool(tmobj,msg->MouseX,
                                                            msg->MouseY)) {
                                   struct TMLinkImage *tmli=dt->dt_ImageLink;

                                   DEBUG_PRINTF("Dock selected: 0x%08lx\n",dt);

                                   /* Got a dock tool with image object? */
                                   if (tmli)
                                    /* Image object with active anim? */
                                    if (tmli->tmli_Link.tml_Active)
                                     /* Yes, clear pointer */
                                     tmobj->do_Selected=NULL;
                                    else
                                     /* No, select it */
                                     CallActivateTMObject((struct TMLink *)
                                                          tmli,
                                                          (void *) IOC_ACTIVE);
                                  }
                                 }
                                 break;
                                case SELECTUP:   /* Select button released */
                                 {
                                  struct DockTool *dt=tmobj->do_Selected;

                                  /* Got a selected dock tool? */
                                  if (dt) {
                                   struct TMLinkImage *tmli=dt->dt_ImageLink;

                                   /* Dock tool with image object? */
                                   if (tmli) {
                                    UWORD x=msg->MouseX;
                                    UWORD y=msg->MouseY;
                                    UWORD dx=tmobj->do_SelectX;
                                    UWORD dy=tmobj->do_SelectY;

                                    /* Mouse still on dock tool and no */
                                    /* double click?                   */
                                    if ((x>=dx) && (y>=dy) &&
                                        (x<dx+tmobj->do_XSize) &&
                                        (y<dy+tmobj->do_YSize) &&
                                        !DoubleClick(tmobj->do_Seconds,
                                                     tmobj->do_Micros,
                                                     msg->Seconds,
                                                     msg->Micros)) {
                                     /* Yes. Activate objects */
                                     /* Activate Sound object */
                                     if (dt->dt_SoundLink)
                                      CallActivateTMObject(dt->dt_SoundLink,
                                                           NULL);

                                     /* Activate Image object */
                                     CallActivateTMObject((struct TMLink *)
                                                          tmli,
                                                          (void *) IOC_CONTANIM
                                                          );

                                     /* Activate Exec object */
                                     if (dt->dt_ExecLink)
                                      CallActivateTMObject(dt->dt_ExecLink,
                                                           NULL);

                                     /* Save time stamp */
                                     tmobj->do_Seconds=msg->Seconds;
                                     tmobj->do_Micros=msg->Micros;

                                     /* PopUp dock? */
                                     if (tmobj->do_Flags & DO_PopUp)
                                      /* Yes. Close window */
                                      closewindow=TRUE;
                                    }
                                    else
                                     /* No, de-select image */
                                     CallActivateTMObject((struct TMLink *)
                                                          dt->dt_ImageLink,
                                                          (void *) IOC_DEACTIVE
                                                         );
                                   }

                                   /* Clear pointer */
                                   tmobj->do_Selected=NULL;
                                  }
                                 }
                                 break;
                               }
                              break;
   case IDCMP_GADGETUP:       /* Prevent double clicks */
                              if (!DoubleClick(tmobj->do_Seconds,
                                               tmobj->do_Micros,
                                               msg->Seconds,msg->Micros)) {
                               struct DockTool *dt=(struct DockTool *)
                                ((struct Gadget *) msg->IAddress)->UserData;

                               /* Activate sound object */
                               if (dt->dt_SoundLink)
                                CallActivateTMObject(dt->dt_SoundLink,NULL);

                               /* Activate Exec object */
                               if (dt->dt_ExecLink)
                                CallActivateTMObject(dt->dt_ExecLink,NULL);

                               /* Save time stamp */
                               tmobj->do_Seconds=msg->Seconds;
                               tmobj->do_Micros=msg->Micros;

                               /* PopUp dock? */
                               if (tmobj->do_Flags & DO_PopUp)
                                /* Yes. Close window */
                                closewindow=TRUE;
                              }
                              break;
   case IDCMP_MENUPICK:       {
                               USHORT menunum=msg->Code;

                               /* Scan all menu events */
                               while (menunum!=MENUNULL) {
                                struct MenuItem *menuitem=
                                 ItemAddress(tmobj->do_Menu,menunum);

                                /* Which menu selected? */
                                switch(GTMENUITEM_USERDATA(menuitem)) {
                                 case MENU_CLOSE: closewindow=TRUE;
                                                  break;
                                 case MENU_QUIT:  Closing=TRUE;
                                                  break;
                                }

                                /* Get next menu event */
                                menunum=menuitem->NextSelect;
                               }
                              }
                              break;
   case IDCMP_ACTIVEWINDOW:   if (!tmobj->do_Title) {
                               /* Borderless window */
                               struct RastPort *rp=tmobj->do_Window->RPort;
                               struct Gadget *g=&tmobj->do_DragGadget;

                               SetDrMd(rp,JAM1);
                               SetAPen(rp,tmobj->do_FillPen);
                               RectFill(rp,1,1,g->Width-2,g->Height-2);
                              }
                              break;
   case IDCMP_INACTIVEWINDOW: {
                               struct DockTool *dt=tmobj->do_Selected;

                               if (!tmobj->do_Title) {
                                /* Borderless window */
                                struct RastPort *rp=tmobj->do_Window->RPort;
                                struct Gadget *g=&tmobj->do_DragGadget;

                                SetDrMd(rp,JAM1);
                                SetAPen(rp,tmobj->do_BackPen);
                                RectFill(rp,1,1,g->Width-2,g->Height-2);
                               }

                               /* Missed a selectup??? */
                               if (dt) {
                                struct TMLinkImage *tmli=dt->dt_ImageLink;

                                /* Got non-ANIM image object? */
                                if (tmli && !tmli->tmli_Link.tml_Active)
                                 CallActivateTMObject((struct TMLink *) tmli,
                                                      (void *) IOC_DEACTIVE);

                                tmobj->do_Selected=NULL;
                               }
                              }
                              break;
  }

  /* Reply message */
  GT_ReplyIMsg(msg);

  /* Close window? */
  if (closewindow) CloseDockWindow(tmobj);
 }
}
