/******************  gadgets.c  ******************
 *                                               *
 *                                               *
 *                     hyper                     *
 *                                               *
 *                  © by Koessi                  *
 *                                               *
 *              Sunday, 23 Aug 1992              *
 *                                               *
 *                                               *
 *************************************************/


#include "defs.h"

Prototype short HandleHlpGads(short, short);
Prototype void  DoLinkGad(short mx, short my);
Prototype short InitGads(short);
Prototype void  DrawText(struct mynode *);
Prototype void  scroll(short);
Prototype void  AllocMap(struct BitMap *, UBYTE, short, short);
Prototype void  ClrMap(struct BitMap *);    /*  reset bitmap structure  */
Prototype void  setlimits(void);
Prototype short setvars(void);


Prototype struct BitMap       hlp_map;
Prototype struct VisualInfo  *VisInfo;
Prototype struct Font        *gadsfont;
Prototype struct Font        *txtfont;
Prototype struct Gadget      *hlp_glist;
Prototype struct Gadget      *Gad_scroller;
Prototype struct Screen      *Scr;

Prototype short xstart;
Prototype short ystart;
Prototype short xend  ;
Prototype short yend  ;
Prototype short ylen  ;

Prototype short scrollpos;
Prototype short scrollmin;
Prototype short scrollmax;
Prototype short scrollvis;
Prototype short lineheight;

Prototype short width;
Prototype short height;

struct BitMap       hlp_map;
struct RastPort     hlp_port;
struct Gadget      *Gad_scroller;
struct Gadget      *hlp_glist    = NULL;

struct Library  *DiskfontBase = NULL;
struct TextFont *gadsfont     = NULL;
struct TextFont *txtfont      = NULL;
struct Screen   *Scr          = NULL;

struct VisualInfo *VisInfo    = NULL;

struct TextAttr opal  = {
  opalname,
  12,
  FS_NORMAL,  /*  FSF_BOLD, */
  FPF_DISKFONT | FPF_PROPORTIONAL
};

struct TextAttr topaz = {
  topazname,
  8,
  FS_NORMAL,
  FPF_ROMFONT
};

struct TextAttr pearl = {
  pearlname,
  8,
  FS_NORMAL,
  FPF_DISKFONT
};

char *gadtxt[] =
{
  str_return,
  str_loaddoc,
  str_firstpage,
  str_prevpage,
  str_nextpage,
  str_sleep
};

short gadid[] =
{
  GD_Gad_return,
  GD_Gad_load,
  GD_Gad_firstpage,
  GD_Gad_prevpage,
  GD_Gad_nextpage,
  GD_Gad_sleep
};

short width   = 77 * 8;
short height  = 18 * LINEHEIGHT;

short xstart;
short ystart;
short xend  ;
short yend  ;
short ylen  ;

short scrollpos  =  0;
short scrollmin  =  0;
short scrollmax  = 18;
short scrollvis  = 18;
short lineheight = LINEHEIGHT;

/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: HandleHlpGads                    *
 *                                               *
 *                                               *
 *    INPUT:    short id                         *
 *              short code                       *
 *                                               *
 *    OUTPUT:   void                             *
 *                                               *
 *    NOTE:                                      *
 *                                               *
 *************************************************/


short
HandleHlpGads(short id, short code)
{
  switch (id)
  {
  case GD_cursor:         /*  _scroll       */
    if (code == scrollpos)
      break;
    GT_SetGadgetAttrs(Gad_scroller, hlp_win, NULL,
                      GTSC_Top,   code,
                      TAG_DONE );

  case GD_scroller:       /*  _scroll       */
    short dl = code - scrollpos;
    if (dl != 0)
    {
      struct Window *w = hlp_win;
      scroll(dl);
      scrollpos = code;
    }
    break;

  case GD_Gad_return:     /*  _return       */
    code = DoRequest(reallyquit, str_quit, FilePart(taskname));
    if (code < 2)
      return(code == FALSE);

  case GD_Gad_sleep:      /*  _sleep        */
    ClrWindow(FALSE);
    appmenuitem = AddAppMenuItem(0L, 0L, hypertofront, app_port, TAG_DONE);
    backmode    = TRUE;
    return(FALSE);
    break;

  case GD_Gad_prevpage:   /*  _prevpage     */
    struct mynode *hn;
    if (hn = hlp_node)
    {
      struct mynode *mn;
      struct List *list = hlp_nodelist;
      if (hn == (struct mynode *)list->lh_Head)
      {
        if (*(hn->mn_prev))
        {
#ifdef DEBUG
          Printf("\nprev: %s\n", hn->mn_prev);
#endif
          if (!hn->mn_hasprev)
            NewNode(hn->mn_prev);   /*  olddoc  */
        }
        break;
      }
      if (*(hn->mn_prev))
      {
#ifdef DEBUG
        Printf("\nprev: %s\n", hn->mn_prev);
#endif
        mn = FindName(list, hn->mn_prev);
        if (!hn->mn_hasprev)
          hn->mn_prev = NULL;
      }
      else
      {
        if (hn->mn_hastoc)
          mn = FindName(list, hn->mn_toc);
        else
          mn = NULL;
      }
      if (mn == NULL)
        mn = hn->mn_node.ln_Pred;

      if (mn)
        DrawText(mn);
    }
    break;

  case GD_Gad_firstpage:  /*  _firstpage    */
    DrawText(hlp_nodelist->lh_Head);
    break;

  case GD_Gad_load:       /*  _loaddoc      */
    if (LoadDoc())
      NewNode(charbuffer);
    break;

  case GD_Gad_nextpage:   /*  _nextpage     */
    struct mynode *hn;
    if (hn = hlp_node)
    {
      struct List *list = hlp_nodelist;
      struct mynode *mn;
      if (hn->mn_hasnext && (mn = FindName(list, hn->mn_next)))
      {
        if (!mn->mn_hasprev)
          mn->mn_prev = hn->mn_node.ln_Name;
      }
      else
      {
        mn = hn->mn_node.ln_Succ;
        if (mn == (struct mynode *)&(list->lh_Tail))
          break;
      }
      if (mn)
        DrawText(mn);
    }

  default:
    break;
  }
  return(TRUE);
}

/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: DrawText                         *
 *                                               *
 *                                               *
 *    INPUT:    struct mynode *mn                *
 *                                               *
 *    OUTPUT:   void                             *
 *                                               *
 *    NOTE:                                      *
 *                                               *
 *************************************************/


void
DrawText(struct mynode *mn)
{
  LockWin();
  hlp_node = mn;
  char *s  = mn->mn_node.ln_Name;
  char *d  = nodename;
  while(*d = toupper(*s))
  {
    ++s; ++d;
  }
  struct RastPort *hport = &hlp_port;
  struct BitMap   *bmap  = &hlp_map;

  int    linum    =   mn->mn_linum;
  char **txtline  =   mn->mn_txtline;
  char  *str      =  (mn->mn_headline) ? *txtline++ : mn->mn_node.ln_Name;
  int    xl       =   width;
  int    lh       =   lineheight;
  int    yl       =  (linum + 1) * lh;

  AllocMap(bmap, 2, xl, yl + ylen);
  InitRastPort(hport);
  hport->BitMap = bmap;
  SetFont(hport, txtfont);

  short len  = strlen(str);                             /*  headline */
  short offs = (xl - TextLength(hport, str, len)) >> 1; /*  center   */

  SetDrMd(hport, JAM1);
  SetAPen(hport, 1);
  Move(   hport, offs++, lh + 1);
  Text(   hport, str, len);
  SetAPen(hport, 2);
  Move(   hport, offs,   lh);
  Text(   hport, str, len);

#ifdef DEBUG
  Printf("node: %-20s * linum=%ld\n", str, linum);
#endif

  offs  = scrollvis - linum + 2;
  offs  = (offs > 3) ? offs >>= 1 : 1;
  offs += 1;

  mn->mn_offset = (int)offs;
  linum += offs;

#ifdef DEBUG
  Printf("scrollvis=%ld * offs=%ld * linum=%ld\n", scrollvis, offs, linum);
#endif
  struct linkgad *lg = ((struct List *)&(mn->mn_linkgads))->lh_Head;

  for (; offs < linum; ++offs, ++txtline)
  {
    char *ptr;
    if (ptr = *txtline)
    {
      short tl = 0;
      Move(hport, 0, offs * lh);
      short notlink = TRUE;

      while (*ptr)
      {
        SetAPen(hport, 1);
        SetBPen(hport, 0);
        SetDrMd(hport, JAM1);

        len = 0;
        str = ptr;
        while (TRUE)
        {
          if (*ptr == '\0')
          {
            tl += TextLength(hport, str, len);
            break;
          }
          else
          {
            if (*ptr++ == (BYTE)'\x9B')
            {
              tl += TextLength(hport, str, len);
              if (notlink = (notlink == FALSE))
              {
                lg->lg_xe = tl;
                lg = lg->lg_node.ln_Succ;
                SetAPen(hport, 2);
                SetBPen(hport, 3);
                SetDrMd(hport, JAM2);
              }
              else
              {
                lg->lg_xs = tl;
              }
              break;
            }
            ++len;
          }
        }

        if (tl > xl)
        {
          tl -= TextLength(hport, str, len);
          --len;
          while (tl + TextLength(hport, str, len) > xl)
            --len;
          Text(hport, str, len);
          break;                /*  next line */
        }
        Text(hport, str, len);
      }
    }
  }
  BltBitMapRastPort(&hlp_map,
                    0, 2,
                    hlp_win->RPort,
                    xstart,   ystart,
                    xl,       ylen,
                    0xC0);

  GT_SetGadgetAttrs(Gad_scroller, hlp_win, NULL,
                    GTSC_Visible, scrollvis,
                    GTSC_Top,     scrollpos = 0,      /*  top line  */
                    GTSC_Total,   scrollmax = linum,  /*  all lines */
                    TAG_DONE );
  UnLockWin();
}


/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: scroll                           *
 *                                               *
 *                                               *
 *    INPUT:    short dl                         *
 *                                               *
 *    OUTPUT:   void                             *
 *                                               *
 *    NOTE:                                      *
 *                                               *
 *************************************************/


void
scroll(short dl)
{
  register short xs = xstart;
  register short ys = ystart;
  register short xw = width;
  register short yl = ylen;
  register short lh = lineheight;
  register short xe = xs + xw - 1;
  register short ye = ys + yl - 1;
  register short yy = scrollpos * lh + 2;

  register struct RastPort *rport =  hlp_win->RPort;
  register struct BitMap   *bmap  = &hlp_map;

  Forbid();
  if (dl > 0)
  {
    yy += yl;               /*  shift up from bottom */
    ys += 1;
    yl  = ye - lh + 1;
    while (dl-- > 0)
    {
      ScrollRaster(rport, 0, lh, xs, ys, xe, ye);

      BltBitMapRastPort( bmap, 0, yy, rport, xs, yl, xw, lh, 0xC0);
      yy += lh;
    }
  }
  else
  {                         /*  shift down from top */
    yy -= lh;
    while (dl++ < 0)
    {
      ScrollRaster(rport, 0, -lh, xs, ys, xe, ye);

      BltBitMapRastPort( bmap, 0, yy, rport, xs, ys, xw, lh, 0xC0);
      yy -= lh;
    }
  }
  Permit();
}
/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: DoLinkGad                        *
 *                                               *
 *                                               *
 *    INPUT:    short mx                         *
 *              short my                         *
 *                                               *
 *    OUTPUT:   void                             *
 *                                               *
 *    NOTE:                                      *
 *                                               *
 *************************************************/



void
DoLinkGad(short mx, short my)
{
  struct linkgad  *lg;
  struct mynode   *mn   = hlp_node;
  struct List     *list = &(mn->mn_linkgads);
  int    offset         = mn->mn_offset;

#ifdef DEBUG
  Printf("\n******\n"
         "mx %3ld, my %3ld\n", mx, my);
#endif

  for ( lg  = list->lh_Head;
        lg != (struct linkgad *)&(list->lh_Tail);
        lg  = lg->lg_node.ln_Succ)
  {
#ifdef DEBUG
    Printf("\n%-16s - ", lg->lg_linknode);
#endif
    int pos = offset + lg->lg_line - scrollpos - 1;

#ifdef DEBUG
    Printf("line=%2ld->%2ld, ", lg->lg_line, pos);
#endif

    if (pos < 0 || pos > scrollvis)         /*  not visible */
      continue;

    pos *= lineheight;
    pos += ystart;                          /*  *lh + offset */

#ifdef DEBUG
    Printf("ys=%3ld, ", pos);
#endif

    if (my < pos || my > (pos + lineheight))  /*  not hit */
      continue;

    pos = xstart + lg->lg_xs;               /*  tlen + offset */

#ifdef DEBUG
    Printf("xs=%3ld, ", pos);
#endif

    if (mx < pos)                           /*  not hit */
      continue;

    pos = xstart + lg->lg_xe;               /*  txtlen of linkname */

#ifdef DEBUG
    Printf("xe=%3ld", pos);
#endif

    if (mx > pos)                           /*  not hit */
      continue;

    NewNode(lg->lg_linknode);               /*  parse 'doc/node' */
    break;
  }
#ifdef DEBUG
  PutStr("\n");
#endif
}

/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: AllocMap                         *
 *                                               *
 *                                               *
 *    INPUT:    struct BitMap *bmap              *
 *              UBYTE depth                      *
 *              short wid                        *
 *              short hei                        *
 *                                               *
 *    OUTPUT:   void                             *
 *                                               *
 *    NOTE:     always                           *
 *                                               *
 *************************************************/


void
AllocMap(struct BitMap *bmap, UBYTE depth, short wid, short hei)
{
  ClrMap(bmap);

  InitBitMap(bmap, depth, wid, hei);
  ULONG  RastSize  = (ULONG)(bmap->BytesPerRow * bmap->Rows);
  for (BYTE i = 0; i < depth; i++)
  {
    if (!(bmap->Planes[i] = (PLANEPTR)AllocMem(RastSize, MEMF_CHIP | MEMF_CLEAR)))
    {
      cleanup();
      exit(103);
    }
  }
}


/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: ClrMap                           *
 *                                               *
 *                                               *
 *    INPUT:    struct BitMap *bmap              *
 *                                               *
 *    OUTPUT:   void                             *
 *                                               *
 *    NOTE:     opposite to AllocMap             *
 *                                               *
 *************************************************/


void
ClrMap(struct BitMap *bmap)
{
  BYTE depth;
  if (depth = bmap->Depth)
  {
    ULONG  RastSize  = bmap->BytesPerRow * bmap->Rows;
    for (BYTE i = 0; i < depth; i++)
    {
      if (bmap->Planes[i])
      {
        FreeMem(bmap->Planes[i], RastSize);
        bmap->Planes[i] = NULL;
      }
    }
    bmap->Depth = 0;
  }
}

/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: InitGads                         *
 *                                               *
 *                                               *
 *    INPUT:    short clear                      *
 *                                               *
 *    OUTPUT:   short                            *
 *                                               *
 *    NOTE:                                      *
 *                                               *
 *************************************************/


short
InitGads(short clear)
{
  struct NewGadget   ng;
  struct NewGadget  *p = &ng;

  if (txtfont == NULL && !setvars())
    return(FALSE);

  struct Window *w = hlp_win;
  short w_width  = w->Width;              /*  Window data */
  short w_height = w->Height;
  short w_btop   = w->BorderTop;
  short w_bleft  = w->BorderLeft;
  short w_bright = w->BorderRight;
  short w_bbot   = w->BorderBottom;
  short minwidth = w_bleft + w_bright  + SCROLLERWIDTH + 4 + width;
  short xoffset  = (w_width - minwidth) >> 1;    /*  calc x-offset */

  xstart    = w_bleft + 2 + ((xoffset > 0) ? xoffset : 0);
  width     = w_width - (xstart + w_bright + SCROLLERWIDTH + 2);   /*  use rest of field */
  ystart    = w_btop  + 1;
  yend      = w_height - (w_bbot + BUTTONHEIGHT  + 1);
  scrollvis = (yend - ystart) / lineheight;
  ylen      = scrollvis       * lineheight;
  yend      = ystart + ylen;

  if (clear)
  {
    struct RastPort *rport = w->RPort;
    SetAPen(rport, 0);
    RectFill(rport, w_bleft, w_btop,
                    w_width  - w_bright,
                    w_height - w_bbot);
    RefreshWindowFrame(w);
  }
  else
  {
    WindowLimits(w, minwidth,
                    w_btop + w_bbot + BUTTONHEIGHT + 2 + height,
                    Scr->Width,
                    Scr->Height);
  }

  struct Gadget *g;
  if (hlp_glist == NULL)
  {
    if (g =  CreateContext(&hlp_glist))
    {
      p->ng_LeftEdge   = w_width - w_bright - SCROLLERWIDTH;
      p->ng_TopEdge    = w_btop;
      p->ng_Width      = SCROLLERWIDTH;
      p->ng_Height     = w_height - w_bbot - w_btop - BUTTONHEIGHT;
      p->ng_GadgetText = "";
      p->ng_TextAttr   = (gadsfont) ? &opal : &topaz;
      p->ng_GadgetID   = GD_scroller;
      p->ng_Flags      = PLACETEXT_IN;
      p->ng_VisualInfo = VisInfo;

      Gad_scroller = g = CreateGadget( SCROLLER_KIND, g, p,
                         GTSC_Top,     0,             /*  top line  */
                         GTSC_Total,   10,            /*  all lines */
                         GTSC_Visible, 10,            /*  txtlines */
                         PGA_Freedom,  LORIENT_VERT,
                         GA_Immediate, TRUE,
                         GA_RelVerify, TRUE,
                         TAG_DONE );

      short i_width   = w_width - w_bleft - w_bright;
      short g_width   = i_width / 6;                    /*  width of 1 but  */

      p->ng_Width     = i_width  - g_width * 5;
      p->ng_TopEdge   = w_height - w_bbot - BUTTONHEIGHT;
      p->ng_LeftEdge  = w_bleft;
      p->ng_Height    = BUTTONHEIGHT;

      short *id = gadid;
      char **tx = gadtxt;

      for (short i = 0; g && i < 6; ++i)
      {
        p->ng_GadgetText = *tx++;
        p->ng_GadgetID   = *id++;
        g = CreateGadget( BUTTON_KIND, g, p, GT_Underscore, '_', TAG_DONE );
        p->ng_LeftEdge  +=  p->ng_Width;
        p->ng_Width      =  g_width;
      }
    }
    if (!g)
      return(FALSE);
  }

  DrawBevelBox( w->RPort,
                w_bleft, w_btop,
                w_width  - w_bleft - w_bright - SCROLLERWIDTH,
                w_height - w_bbot  - w_btop   - BUTTONHEIGHT,
                GT_VisualInfo, VisInfo,
                TAG_END);

  g = hlp_glist;
  AddGList(w, g, -1, -1, NULL);
  RefreshGadgets(g, w, NULL);
  GT_RefreshWindow(w, NULL);
  GT_BeginRefresh(w);
  GT_EndRefresh(w, TRUE);

  return(TRUE);
}

/*************************************************
 *                                               *
 *                                               *
 *    FUNCTION: setvars                          *
 *                                               *
 *                                               *
 *    INPUT:    void                             *
 *                                               *
 *    OUTPUT:   int                              *
 *                                               *
 *    NOTE:                                      *
 *                                               *
 *************************************************/


short
setvars(void)
{
  if (Scr = LockPubScreen(NULL))
  {
    if (VisInfo = GetVisualInfo(Scr, TAG_END))
    {
      if (DiskfontBase = OpenLibrary(diskfontname, 0))
      {
        void *f;
        if (f = (void *)OpenDiskFont(&pearl))
        {
          lineheight = pearl.ta_YSize + 1;
        }
        else
        {
          f = (void *)OpenDiskFont(&topaz);
          lineheight = topaz.ta_YSize + 1;
        }
        txtfont = f;
        if (f = (void *)OpenDiskFont(&opal))
          gadsfont = f;
        else
          gadsfont = txtfont;
        CloseLibrary(DiskfontBase);
      }
    }
  }
  return(gadsfont != NULL);
}

