/*
 * dockwindow.c   V1.5
 *
 * dock window
 *
 * (c) 1991 by Stefan Becker
 *
 */
#include "ToolManager.h"

/* Structures for window */
static struct Window *w;
static struct AppWindow *aw;
static struct MsgPort *wp;
static struct RastPort *rp;
static struct Screen *pubsc; /* Workbench screen */
static void *vi;             /* Visual information is a *PRIVATE* data field! */
static WORD ww,wh;
UWORD DockXPos=0,DockYPos=0;
UWORD DockXSize=51,DockYSize=51;
UWORD XOff,YOff;
BOOL DockVertical=TRUE;
BOOL DockToBack=TRUE;
static BOOL DoDraw; /* Flag for SizeWindow() in progress */

/* Structures for window menu */
static struct Menu *wmn=NULL;
#define OTMENU_ID   1
#define CDMENU_ID   2
#define ABMENU_ID   3
extern struct EasyStruct AboutES;
#define QUMENU_ID   4
static struct NewMenu mdata[]={
      {NM_TITLE,"TM Dock Menu"  ,NULL,0,~0,NULL},
       {NM_ITEM,"Open TM Window",NULL,0,~0,OTMENU_ID},
       {NM_ITEM,"Close TM Dock" ,"C" ,0,~0,CDMENU_ID},
       {NM_ITEM,NM_BARLABEL     ,NULL,0,~0,NULL},
       {NM_ITEM,"About..."      ,NULL,0,~0,ABMENU_ID},
       {NM_ITEM,NM_BARLABEL     ,NULL,0,~0,NULL},
       {NM_ITEM,"Quit"          ,"Q" ,0,~0,QUMENU_ID},
      {NM_END,NULL,NULL,0,~0,NULL}};

/* Structures for dock list */
static struct List DockList;

/* Draw all images */
static void DrawDockImages(void)
{
 int x=XOff,y=YOff;
 struct Node *dn=GetHead(&DockList);
 struct Region *reg,*oreg;
 struct Rectangle rect;

 /* Get memory for region */
 if (!(reg=NewRegion())) goto die1;

 /* Init rectangle */
 if (DockVertical)
  {
   rect.MinX=XOff;
   rect.MaxX=XOff+DockXSize-2;
  }
 else
  {
   rect.MinY=YOff;
   rect.MaxY=YOff+DockYSize-2;
  }

 /* Clear Window */
 SetAPen(rp,0);
 SetDrMd(rp,JAM1);
 RectFill(rp,XOff-1,YOff-1,XOff+ww-2,YOff+wh-2);

 /* Draw all Docks */
 while (dn)
  {
   /* Create clipping rectangle and add it to our window */
   if (DockVertical)
    {
     rect.MinY=y;
     rect.MaxY=y+DockYSize-2;
    }
   else
    {
     rect.MinX=x;
     rect.MaxX=x+DockXSize-2;
    }
   if (!OrRectRegion(reg,&rect)) goto die2;
   oreg=InstallClipRegion(w->WLayer,reg);

   /* Draw Image */
   DrawImage(rp,((struct ToolNode *) dn->ln_Name)->tn_Dock->
                  do_Gadget.GadgetRender,x,y);

   /* Remove clipping region */
   InstallClipRegion(w->WLayer,oreg);
   ClearRegion(reg);

   /* Calculate next position */
   if (DockVertical)
    y+=DockYSize;
   else
    x+=DockXSize;

   /* Next dock */
   dn=GetSucc((struct ToolNode *) dn);
  }

 /* Something has gone wrong */
die2: DisposeRegion(reg);
die1: DoDraw=FALSE;       /* Drawing finished */
      return;
}

/* Invert a dock image */
void SelectDock(struct ToolNode *tn, WORD dockx, WORD docky, BOOL sel)
{
 WORD x,y;
 struct Gadget *g=&tn->tn_Dock->do_Gadget;

 if (DockVertical)
  {
   x=XOff;
   y=(docky-YOff)/DockYSize*DockYSize+YOff;
  }
 else
  {
   x=(dockx-XOff)/DockXSize*DockXSize+XOff;
   y=YOff;
  }

 /* Two image icon? */
 if ((g->Flags&GFLG_GADGHIGHBITS)==GFLG_GADGHIMAGE)
  {
   struct Region *reg;

   /* Alloc clip region */
   if (reg=NewRegion())
    {
     struct Rectangle rect;

     /* Build rectangle */
     rect.MinX=x;
     rect.MaxX=x+DockXSize-2;
     rect.MinY=y;
     rect.MaxY=y+DockYSize-2;

     /* Build clip region */
     if (OrRectRegion(reg,&rect))
      {
       struct Region *oreg;

       /* Install new clip region */
       oreg=InstallClipRegion(w->WLayer,reg);

       /* Clear region */
       SetDrMd(rp,JAM1);
       SetAPen(rp,0);
       RectFill(rp,x,y,rect.MaxX,rect.MaxY);

       /* Draw Image */
       DrawImage(rp,sel?g->SelectRender:g->GadgetRender,x,y);

       /* Remove clipping region */
       InstallClipRegion(w->WLayer,oreg);
      }

     /* Free region */
     DisposeRegion(reg);
    }
  }
 else
  {
   /* One image icon, only complement it. Set draw mode */
   SetDrMd(rp,COMPLEMENT);
   SetAPen(rp,0xff);
   RectFill(rp,x,y,x+DockXSize-2,y+DockYSize-2);
  }
}

/* Open dock window */
void OpenDockWindow(void)
{
 struct TextFont *f;

 /* Are any docks active or window already open? */
 if (!DockCount || dockwinsig) return; /* No, don't open window */

 if (!(pubsc=LockPubScreen(WBScreenName))) /* Lock Workbench screen */
  goto odw1;

 /* Get Offsets */
 if (!(f=OpenFont(pubsc->Font))) goto odw2;
 XOff=pubsc->WBorLeft+1;
 YOff=pubsc->WBorTop+f->tf_YSize+2;
 CloseFont(f);

 if (!(vi=GetVisualInfo(pubsc,TAG_DONE))) /* Get visual information */
  goto odw2;

 /* Create menus */
 if (!(wmn=CreateMenus(mdata,
                       GTMN_FullMenu,TRUE,
                       TAG_DONE))) goto odw3;

 /* Layout menus */
 if (!LayoutMenus(wmn,vi,TAG_DONE)) goto odw4;

 /* Calculate window size */
 if (DockVertical)
  {
   ww=DockXSize+1;
   wh=DockYSize*DockCount+1;
  }
 else
  {
   ww=DockXSize*DockCount+1;
   wh=DockYSize+1;
  }

 /* Open window */
 if (!(w=OpenWindowTags(NULL,WA_Left,DockXPos,
                             WA_Top,DockYPos,
                             WA_InnerWidth,ww,
                             WA_InnerHeight,wh,
                             WA_PubScreen,pubsc,
                             WA_AutoAdjust,TRUE,
                             WA_IDCMP,IDCMP_CLOSEWINDOW|IDCMP_MOUSEBUTTONS|
                                      IDCMP_CHANGEWINDOW|IDCMP_NEWSIZE|
                                      IDCMP_INACTIVEWINDOW|IDCMP_MENUPICK,
                             WA_Flags,WFLG_CLOSEGADGET|WFLG_DRAGBAR|
                                      WFLG_DEPTHGADGET|WFLG_SMART_REFRESH,
                             TAG_DONE)))
  goto odw4;
 if (DockToBack) WindowToBack(w);

 /* Add menu to window */
 if (!SetMenuStrip(w,wmn))
  goto odw5;

 /* Notify Workbench about window */
 if (!(aw=AddAppWindowA(DOCKWINAPPID,NULL,w,MyMP,NULL)))
  goto odw6;

 /* Dock window open */
 UnlockPubScreen(NULL,pubsc);
 rp=w->RPort;
 DrawDockImages();
 wp=w->UserPort;
 dockwinsig=1L<<wp->mp_SigBit;
 globalsigs|=dockwinsig;
 ShowDock=TRUE;
 StatWinDockState(TRUE);
 return;

 /* Something has gone wrong... */
odw6: ClearMenuStrip(w);
odw5: CloseWindow(w);
odw4: FreeMenus(wmn);
odw3: FreeVisualInfo(vi);
odw2: UnlockPubScreen(NULL,pubsc);
odw1: return;
}

/* Close dock window */
void CloseDockWindow(void)
{
 if (dockwinsig)
  {
   RemoveAppWindow(aw);
   ClearMenuStrip(w);
   CloseWindow(w);
   FreeMenus(wmn);
   FreeVisualInfo(vi);
   globalsigs&=~dockwinsig;
   dockwinsig=0;
   ShowDock=FALSE;
   StatWinDockState(FALSE);
  }
}

/* Find dock that correspondences to X,Y position */
struct ToolNode *FindDock(WORD x, WORD y)
{
 struct Node *dn;
 LONG i;

 /* Out of bounds? */
 if ((x<XOff) || (y<YOff)) return(NULL);

 /* Calculate ordinal number */
 if (DockVertical)
  {
   i=(y-YOff)/DockYSize;
   if (x>=(XOff+DockXSize)) return(NULL);
  }
 else
  {
   i=(x-XOff)/DockXSize;
   if (y>=(YOff+DockYSize)) return(NULL);
  }

 /* Out of bounds? */
 if (i<0) return(NULL);

 /* Search tool */
 dn=GetHead(&DockList);
 while (i && dn)
  {
   /* Next Dock */
   dn=GetSucc((struct ToolNode *) dn);
   i--;
  }

 /* Retreive ToolNode */
 if (dn)
  return(dn->ln_Name);
 else
  return(NULL);
}

/* Handle window events */
void HandleDockWinEvent(void)
{
 static struct ToolNode *otn=NULL;
 static WORD ox,oy;
 BOOL clwin=FALSE;
 struct IntuiMessage *msg;

 while (msg=GetMsg(wp))        /* Get Intuition messages */
  {
   switch (msg->Class)
    {
     case IDCMP_CLOSEWINDOW:   /* User selected the close window gadget */
      clwin=TRUE;
      break;
     case IDCMP_MOUSEBUTTONS:  /* User pressed mouse buttons */
      switch(msg->Code)
       {
        case SELECTDOWN:       /* User pressed select button */
         /* Save selected tool */
         ox=msg->MouseX;       /* Save coordinates */
         oy=msg->MouseY;
         if (otn=FindDock(ox,oy))     /* Find selected tool */
          SelectDock(otn,ox,oy,TRUE); /* If tool found, invert its image */
         break;
        case SELECTUP:         /* User released select button */
         struct ToolNode *tn;

         /* Save selected tool */
         tn=FindDock(msg->MouseX,msg->MouseY);

         /* Tool selected and same tool as button pressed? */
         if (tn && (otn==tn))
          StartTool(tn,NULL);  /* Yes, start tool with no args */

         /* Invert DockImage */
         if (otn)
          {
           SelectDock(otn,ox,oy,FALSE);
           otn=NULL;           /* invalidate pointer */
          }
       }
      break;
     case IDCMP_INACTIVEWINDOW: /* Window has gone inactive */
      /* Missed a SELECTUP???? */
      if (otn)
       {
        SelectDock(otn,ox,oy,FALSE); /* Yes. Invert dock again */
        otn=NULL;                     /* invalidate pointer */
       }
      break;
     case IDCMP_CHANGEWINDOW:   /* User has moved the window */
      DockXPos=w->LeftEdge;     /* Update window coordinates */
      DockYPos=w->TopEdge;
      break;
     case IDCMP_NEWSIZE:        /* Window has new size */
      /* Was a SizeWindow() in progress? Yes, it is completed now. */
      /* We can now draw the images safely. */
      if (DoDraw) DrawDockImages();
      break;
     case IDCMP_MENUPICK:       /* User selected a menu */
      USHORT mnum=msg->Code;

      while (mnum!=MENUNULL)    /* Scan all menu events */
       {
        struct MenuItem *mi=ItemAddress(wmn,mnum);

        switch(GTMENUITEM_USERDATA(mi))
         {
          case OTMENU_ID:         /* User selected open TM window menu item */
           OpenStatusWindow(TRUE);
           break;
          case CDMENU_ID:         /* User selected close TM dock menu item */
           clwin=TRUE;
           break;
          case ABMENU_ID:         /* User selected about menu item */
           EasyRequest(w,&AboutES,NULL,"");
           break;
          case QUMENU_ID:         /* User selected quit menu item */
           if (!clwin)
            {
             SetQuitFlag();
             if (!running) clwin=TRUE;
            }
           break;
         }

        /* Next selected menu */
        mnum=mi->NextSelect;
       }
      break;
    }
   ReplyMsg((struct Message *) msg); /* Reply message */
  }

 if (clwin)
  {
   otn=NULL;
   CloseDockWindow();
  }
}

/* Redraw dock window */
static void RefreshDockWindow(BOOL added)
{
 /* Not in initialization  phase? */
 if (ShowDock)
  /* Dock window open? */
  if (dockwinsig)
   /* Yes, change window contents */
   if (DockCount)
    { /* Dock added? */
     if (added)
      if (DockVertical)     /* Yes, enlarge window */
       {
        SizeWindow(w,0,DockYSize);
        wh+=DockYSize;
       }
      else
       {
        SizeWindow(w,DockXSize,0);
        ww+=DockXSize;
       }
     else
      if (DockVertical)     /* No, reduce window size */
       {
        SizeWindow(w,0,-DockYSize);
        wh-=DockYSize;
       }
      else
       {
        SizeWindow(w,-DockXSize,0);
        ww-=DockXSize;
       }

     /* SizeWindow() in progress */
     DoDraw=TRUE;
    }
   else CloseDockWindow(); /* No dock to show, so close the window */
  else if (added) OpenDockWindow(); /* No, open window */
}

/* Add a dock to the list */
BOOL AddDock(struct ToolNode *tn)
{
 struct Node *dn;

 /* Get memory for new dock node */
 if (!(dn=malloc(sizeof(struct Node)))) return(FALSE);

 /* Initialize dock list */
 if (DockCount==0) NewList(&DockList);

 /* Append dock node at the of list */
 dn->ln_Name=tn;
 AddTail(&DockList,dn);
 DockCount++;

 /* Refresh dock window */
 RefreshDockWindow(TRUE);

 return(TRUE);
}

/* Remove a dock from the list */
void RemDock(struct ToolNode *tn)
{
 struct Node *dn;

 /* Search node in dock list */
 dn=GetHead(&DockList);
 while (dn)
  {
   /* Node found? Yes --> Leave loop */
   if ((struct ToolNode *) dn->ln_Name==tn) break;

   /* Next node */
   dn=GetSucc((struct ToolNode *) dn);
  }

 /* Remove node */
 Remove(dn);
 DockCount--;

 /* Refresh dock window */
 RefreshDockWindow(FALSE);
}
