/* 
 * xvctrl.c - Control box handling functions
 *
 * callable functions:
 *
 *   CreateCtrl(geom)       -  creates the ctrlW window.  Doesn't map it.
 *   CtrlBox(vis)           -  random processing based on value of 'vis'
 *                             maps/unmaps window, etc.
 *   RedrawCtrl(x,y,w,h)    -  called by 'expose' events
 *   ClickCtrl(x,y)
 *   DrawCtrlStr()          -  called to redraw 'ISTR_INFO' string in ctrlW
 *   ScrollToCurrent()      -  called when 'curname' is changed 
 *
 *   LSCreate()             -  creates a listbox
 *   LSRedraw()             -  redraws 'namelist' box
 *   LSClick()              -  operates list box
 *   LSNewData()            -  called when strings or number of them change
 *   LSKey()                -  called to handle page up/down, arrows
 *
 */

/*
 * Copyright 1989, 1990, 1991, 1992 by John Bradley and
 *                       The University of Pennsylvania
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation. 
 *
 * The software may be modified for your own purposes, but modified versions
 * may not be distributed.
 *
 * This software is provided "as is" without any expressed or implied warranty.
 *
 * The author may be contacted via:
 *    US Mail:   John Bradley
 *               GRASP Lab, Room 301C
 *               3401 Walnut St.  
 *               Philadelphia, PA  19104
 *
 *    Phone:     (215) 898-8813
 *    EMail:     bradley@cis.upenn.edu       
 */



#include "xv.h"
#include "bitmaps.h"

#define DBLCLKTIME 500             /* double-click speed in milliseconds */

#define INACTIVE(lptr, item) ((lptr)->filetypes && (lptr)->dirsonly && \
			      (item) >= 0 && (item) < (lptr)->nstr && \
			      (lptr)->str[(item)][0] != C_DIR && \
			      (lptr)->str[(item)][0] != C_LNK)

#define NLINES 11                  /* # of lines in list control (keep odd) */
#define LISTW CTRL_LISTW

#define BUTTW   71   /* must be odd for 'half' buttons to work out */
#define BUTTH   24

static int    listh;               /* height of list/scrl controls */
static int    ptop;                /* y-coord of top of button area in ctrlW */

static Pixmap fifoPix, chrPix, dirPix, blkPix, lnkPix, sockPix, regPix;
static Pixmap rotlPix, rotrPix, fliphPix, flipvPix;

static XRectangle butrect;

static char *dispMList[] = { "Window", 
			     "Root: tiled",
			     "Root: integer tiled",
			     "Root: mirrored",
			     "Root: integer mirrored",
			     "Root: center tiled",
			     "Root: centered",
			     "Root: centered, warp",
			     "Root: centered, brick" };

static char *conv24MList[] = { "quick  (fastest)",
			       "slower (default)",
			       "best   (non-dithered)" };

#ifdef __STDC__
static void drawSel(LIST *, int);
static void RedrawNList(void);
#else
static void drawSel(), RedrawNList();
#endif


/***************************************************/
void CreateCtrl(geom)
char *geom;
{
  int i;
  double skip;
  XClassHint classh;

  ctrlW = CreateWindow("xv controls", geom, CTRLWIDE,CTRLHIGH,infofg,infobg);
  if (!ctrlW) FatalError("can't create controls window!");

  classh.res_name = "xv";
  classh.res_class = "XVcontrols";
  XSetClassHint(theDisp, ctrlW, &classh);
  StoreDeleteWindowProp(ctrlW);

  grayTile = XCreatePixmapFromBitmapData(theDisp, ctrlW, gray25_bits,
	     gray25_width, gray25_height, infofg, infobg, dispDEEP);

  grayStip = XCreatePixmapFromBitmapData(theDisp, ctrlW, gray50_bits,
	     gray50_width, gray50_height, 1, 0, 1);
  
  fifoPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_fifo_bits,
	     i_fifo_width, i_fifo_height, 1, 0, 1);
  
  chrPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_chr_bits,
	     i_chr_width, i_chr_height, 1,0,1);
  
  dirPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_dir_bits,
	     i_dir_width, i_dir_height, 1,0,1);
  
  blkPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_blk_bits,
	     i_blk_width, i_blk_height, 1,0,1);
  
  lnkPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_lnk_bits,
	     i_lnk_width, i_lnk_height, 1,0,1);
  
  sockPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_sock_bits,
	     i_sock_width, i_sock_height, 1,0,1);
  
  regPix  = XCreatePixmapFromBitmapData(theDisp, ctrlW, i_reg_bits,
	     i_reg_width, i_reg_height, 1,0,1);

  rotlPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, h_rotl_bits, 
					h_rotl_width, h_rotl_height, 1, 0, 1);

  rotrPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, h_rotr_bits, 
					h_rotr_width, h_rotr_height, 1, 0, 1);

  fliphPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fliph_bits, 
					fliph_width, fliph_height, 1, 0, 1);

  flipvPix = XCreatePixmapFromBitmapData(theDisp, ctrlW, flipv_bits, 
					flipv_width, flipv_height, 1, 0, 1);

  

  if (ctrlColor) XSetWindowBackground(theDisp, ctrlW, locol);
            else XSetWindowBackgroundPixmap(theDisp, ctrlW, grayTile);

  /* create doo-wahs */
  listh = LINEHIGH * NLINES;

  LSCreate(&nList, ctrlW, 10, 10+CHIGH+1, LISTW, listh, NLINES, dispnames, 
	   numnames, infofg, infobg, RedrawNList, 0, 0);
  nList.selected = 0;  /* default to first name selected */

  i = listh-BUTTH;

  skip =  ((double) (listh - 2*19 - 4*BUTTH)) / 5.0;

  BTCreate(&but[BNEXT], ctrlW, 298, nList.y + 2*19 + (int)(2*skip), 
	   60, BUTTH, "Next",     infofg, infobg);
  BTCreate(&but[BPREV], ctrlW, 298, nList.y + 2*19 + BUTTH + (int)(3*skip),
	   60, BUTTH, "Previous", infofg, infobg);
  BTCreate(&but[BINFO], ctrlW, 298, nList.y + 2*19 + 2*BUTTH + (int)(4*skip),
	   60, BUTTH, "Info", infofg, infobg);
  BTCreate(&but[BGAMMA], ctrlW,298, nList.y + 2*19 + 3*BUTTH + (int)(5*skip),
	   60, BUTTH, "ColEdit", infofg, infobg);

  BTCreate(&but[BLOAD],   ctrlW, 368, but[BNEXT].y,
	   60, BUTTH, "Load",     infofg, infobg);
  BTCreate(&but[BSAVE],   ctrlW, 368, but[BPREV].y,
	   60, BUTTH, "Save",     infofg, infobg);
  BTCreate(&but[BDELETE], ctrlW, 368, but[BINFO].y,
	   60, BUTTH, "Delete", infofg, infobg);
  BTCreate(&but[BQUIT],   ctrlW, 368, but[BGAMMA].y,
	   60, BUTTH, "Quit", infofg, infobg);


#define BXSPACE (BUTTW+1)
#define BYSPACE (BUTTH+1)

  ptop = CTRLHIGH - (3*BYSPACE + 5 + 4);

#define BX0 ((CTRLWIDE - (BXSPACE*6))/2)
#define BX1 (BX0 + BXSPACE)
#define BX2 (BX0 + BXSPACE*2)
#define BX3 (BX0 + BXSPACE*3)
#define BX4 (BX0 + BXSPACE*4)
#define BX5 (BX0 + BXSPACE*5)
#define BY0 (ptop+5)
#define BY1 (BY0 + BYSPACE)
#define BY2 (BY0 + BYSPACE*2)

  butrect.x = BX0-1;  butrect.y = BY0-1;
  butrect.width = 6*BXSPACE + 1;
  butrect.height = 3*BYSPACE + 1;

  BTCreate(&but[BCROP],   ctrlW,BX0,BY0,BUTTW,BUTTH,"Crop",     infofg,infobg);
  BTCreate(&but[BUNCROP], ctrlW,BX0,BY1,BUTTW,BUTTH,"UnCrop",   infofg,infobg);
  BTCreate(&but[BACROP],  ctrlW,BX0,BY2,BUTTW,BUTTH,"AutoCrop", infofg,infobg);

  BTCreate(&but[BNORM],   ctrlW,BX1,BY0,BUTTW,BUTTH,"Normal",   infofg,infobg);
  BTCreate(&but[BMAX],    ctrlW,BX1,BY1,BUTTW,BUTTH,"Max Size", infofg,infobg);
  BTCreate(&but[BMAXPECT],ctrlW,BX1,BY2,BUTTW,BUTTH,"Maxpect",  infofg,infobg);

  BTCreate(&but[BUP2],    ctrlW,BX2,BY0,BUTTW,BUTTH,"Dbl Size", infofg,infobg);
  BTCreate(&but[BDN2],    ctrlW,BX2,BY1,BUTTW,BUTTH,"Half Size",infofg,infobg);
  BTCreate(&but[B4BY3],   ctrlW,BX2,BY2,BUTTW,BUTTH,"4x3",      infofg,infobg);

  BTCreate(&but[BUP10],   ctrlW,BX3,BY0,BUTTW,BUTTH,"+10%",     infofg,infobg);
  BTCreate(&but[BDN10],   ctrlW,BX3,BY1,BUTTW,BUTTH,"-10%",     infofg,infobg);
  BTCreate(&but[BASPECT], ctrlW,BX3,BY2,BUTTW,BUTTH,"Aspect",   infofg,infobg);

  BTCreate(&but[BRAW],    ctrlW,BX4,BY0,BUTTW,BUTTH,"Raw",      infofg,infobg);
  BTCreate(&but[BDITH],   ctrlW,BX4,BY1,BUTTW,BUTTH,"Dither",   infofg,infobg);
  BTCreate(&but[BSMOOTH], ctrlW,BX4,BY2,BUTTW,BUTTH,"Smooth",   infofg,infobg);

  BTCreate(&but[BROTL],    ctrlW,BX5,BY0,BUTTW/2,BUTTH,
	   "", infofg,infobg);
  BTCreate(&but[BROTR],    ctrlW,BX5+BUTTW/2 + 1,BY0,BUTTW/2,BUTTH,
	   "", infofg,infobg);

  BTCreate(&but[BFLIPH],    ctrlW,BX5,BY1,BUTTW/2,BUTTH,
	   "", infofg,infobg);
  BTCreate(&but[BFLIPV],    ctrlW,BX5+BUTTW/2 + 1,BY1,BUTTW/2,BUTTH,
	   "", infofg,infobg);

  BTCreate(&but[BGRAB],   ctrlW,BX5,BY2,BUTTW,BUTTH,"Grab",infofg,infobg);


  but[BROTL].pix = rotlPix;
  but[BROTL].pw  = h_rotl_width;
  but[BROTL].ph  = h_rotl_height;

  but[BROTR].pix = rotrPix;
  but[BROTR].pw  = h_rotr_width;
  but[BROTR].ph  = h_rotr_height;

  but[BFLIPH].pix = fliphPix;
  but[BFLIPH].pw  = fliph_width;
  but[BFLIPH].ph  = fliph_height;

  but[BFLIPV].pix = flipvPix;
  but[BFLIPV].pw  = flipv_width;
  but[BFLIPV].ph  = flipv_height;

  if (numnames<1) BTSetActive(&but[BDELETE],0);

  XMapSubwindows(theDisp, ctrlW);

  /* have to create dispMB after XMapSubWindows, as we *don't* want the popup
     mapped */

  MBCreate(&dispMB, ctrlW, 298, nList.y, 128, 19,
	   "Display Modes", dispMList, RM_MAX+2, infofg, infobg);
  if (!useroot) dispMB.selected = 0;
           else dispMB.selected = rootMode + 1;


  MBCreate(&conv24MB, ctrlW, 298, nList.y + 19 + (int) skip, 128, 19,
	   "24-bit Conversion", conv24MList, 3, infofg, infobg);
  conv24MB.selected = conv24;
}
  

/***************************************************/
void CtrlBox(vis)
int vis;
{
  if (vis) XMapRaised(theDisp, ctrlW);  
  else     XUnmapWindow(theDisp, ctrlW);

  ctrlUp = vis;
}


/***************************************************/
void RedrawCtrl(x,y,w,h)
int x,y,w,h;
{
  char foo[40];
  int i;
  XRectangle xr;

  xr.x = x;  xr.y = y;  xr.width = w;  xr.height = h;
  XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);

  XSetForeground(theDisp, theGC, infofg);
  XSetBackground(theDisp, theGC, infobg);

  XDrawLine(theDisp, ctrlW, theGC, 0, ptop, CTRLWIDE, ptop);

  if (numnames != 1) sprintf(foo,"%d files",numnames);
  else strcpy(foo,"1 file");
    
  XSetForeground(theDisp, theGC, infobg);
  XFillRectangle(theDisp,ctrlW, theGC, 10+1,5+1,StringWidth(foo)+4,CHIGH+2);
  XSetForeground(theDisp,theGC,infofg);
  XDrawRectangle(theDisp,ctrlW, theGC, 10,5,StringWidth(foo)+5,CHIGH+3);
  XDrawString(theDisp, ctrlW, theGC, 10+3, 5+ASCENT+2,
	      foo, strlen(foo));

  XSetForeground(theDisp,theGC,infofg);
  XDrawRectangles(theDisp, ctrlW, theGC, &butrect, 1);

  for (i=0; i<NBUTTS; i++)
    BTRedraw(&but[i]);

  MBRedraw(&dispMB);
  MBRedraw(&conv24MB);

  DrawCtrlStr();

  XSetClipMask(theDisp, theGC, None);
}


/***************************************************/
void DrawCtrlStr()
{
  int   y;
  char *st,*st1;

  y = ptop - (CHIGH + 4)*2 - 2;
  st  = GetISTR(ISTR_INFO);
  st1 = GetISTR(ISTR_WARNING);

  XSetForeground(theDisp, theGC, infobg);
  XFillRectangle(theDisp, ctrlW, theGC, 0, y+1, CTRLWIDE, (CHIGH+4)*2+1);

  XSetForeground(theDisp, theGC, infofg);
  XDrawLine(theDisp, ctrlW, theGC, 0, y,   CTRLWIDE, y);
  XDrawLine(theDisp, ctrlW, theGC, 0, y+2, CTRLWIDE, y+2);
  XDrawLine(theDisp, ctrlW, theGC, 0, y+CHIGH+4, CTRLWIDE, y+CHIGH+4);
  XDrawLine(theDisp, ctrlW, theGC, 0, y+CHIGH+6, CTRLWIDE, y+CHIGH+6);
  XDrawLine(theDisp, ctrlW, theGC, 0, y+(CHIGH+4)*2, CTRLWIDE, y+(CHIGH+4)*2);

  XDrawString(theDisp, ctrlW, theGC, 10, y+ASCENT+3,       st, strlen(st));
  XDrawString(theDisp, ctrlW, theGC, 10, y+ASCENT+CHIGH+7, st1, strlen(st1));
}


/***************************************************/
int ClickCtrl(x,y)
int x,y;
{
  BUTT *bp;
  int   i;

  for (i=0; i<NBUTTS; i++) {
    bp = &but[i];
    if (PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) break;
  }

  if (i<NBUTTS) {                   /* found one */
    if (BTTrack(bp)) return (i);    /* and it was clicked */
  }

  return -1;
}



/***************************************************/
void ScrollToCurrent(lst)
LIST *lst;
{
  /* called when 'curname' is changed by anything (next/prev buttons,
     wait timeout, whatever.  IF curname is already visible, just redraws
     list to reflect changed selection.  If not, trys to adjust 'liststart' 
     so that curname will appear in the center of the list window */

  int halfway;

  if (lst->selected > lst->scrl.val && 
      lst->selected <  lst->scrl.val + lst->nlines-1) LSRedraw(lst);
  else {
    halfway = (lst->nlines)/2;   /* offset to the halfway pt. of the list */
    if (!SCSetVal(&lst->scrl, lst->selected - halfway)) LSRedraw(lst);
  }
}


/***************************************************/
static void RedrawNList()
{
  LSRedraw(&nList);
}




/***************** LIST STUFF *********************/

/***************************************************/
void LSCreate(lp, win, x, y, w, h, nlines, strlist, nstr, fg, bg, fptr, 
	      typ, donly)
LIST         *lp;
Window        win;
int           x,y,w,h,nlines,nstr,typ,donly;
unsigned long fg, bg;
char        **strlist;    /* a pointer to a list of strings */
void        (*fptr)();
{
  lp->win = XCreateSimpleWindow(theDisp,win,x,y,w,h,1,infofg,infobg);
  if (!lp->win) FatalError("can't create list window!");

  lp->x = x;    lp->y = y;   
  lp->w = w;    lp->h = h;
  lp->fg = fg;  lp->bg = bg;
  lp->str      = strlist;
  lp->nstr     = nstr;
  lp->selected = -1;   /* no initial selection */
  lp->nlines   = nlines;
  lp->filetypes= typ;
  lp->dirsonly = donly;

  XSelectInput(theDisp, lp->win, ExposureMask | ButtonPressMask);

  SCCreate(&lp->scrl, win, x+w, y, 1, h, 0, nstr-nlines, curname, nlines-1,
	   fg, bg, fptr);
}



/***************************************************/
void LSNewData(lp, strlist, nstr)
LIST         *lp;
char        **strlist;
int           nstr;
{
  lp->str = strlist;
  lp->nstr = nstr;
  lp->selected = -1;   /* no initial selection */
  SCSetRange(&lp->scrl, 0, nstr - lp->nlines, 0, lp->nlines-1);
}


/***************************************************/
static void drawSel(lp,j)
LIST *lp;
int j;
{
  int i, inactive;
  unsigned long fg, bg;

  inactive = INACTIVE(lp,j);

  i = j - lp->scrl.val;
  if (i<0 || i>=lp->nlines) return;  /* off screen */

  if (j == lp->selected && !inactive && j<lp->nstr) 
       { fg = lp->bg;  bg = lp->fg; }  /* invert */
  else { fg = lp->fg;  bg = lp->bg; }

  XSetForeground(theDisp, theGC, bg);
  XFillRectangle(theDisp, lp->win, theGC, 0,i*LINEHIGH, lp->w, LINEHIGH);

  if (j>=0 && j<lp->nstr) {   /* only draw string if valid */
    /* make non-dirs inactive, if dirsonly and filetypes */
    XSetForeground(theDisp, theGC, fg);
    XSetBackground(theDisp, theGC, bg);

    if (!lp->filetypes) 
      XDrawString(theDisp, lp->win, theGC, 3, i*LINEHIGH + ASCENT + 1, 
		  lp->str[j], strlen(lp->str[j]));
    else {
      int ypos = i*LINEHIGH + (LINEHIGH - i_fifo_height)/2;

      if (lp->str[j][0] == C_FIFO) 
	XCopyPlane(theDisp, fifoPix, lp->win, theGC, 0, 0,
		   i_fifo_width, i_fifo_height, 3, ypos, 1L);

      else if (lp->str[j][0] == C_CHR) 
	XCopyPlane(theDisp, chrPix, lp->win, theGC, 0, 0,
		   i_chr_width, i_chr_height, 3, ypos, 1L);

      else if (lp->str[j][0] == C_DIR) 
	XCopyPlane(theDisp, dirPix, lp->win, theGC, 0, 0,
		   i_dir_width, i_dir_height, 3, ypos, 1L);

      else if (lp->str[j][0] == C_BLK) 
	XCopyPlane(theDisp, blkPix, lp->win, theGC, 0, 0,
		   i_blk_width, i_blk_height, 3, ypos, 1L);

      else if (lp->str[j][0] == C_LNK) 
	XCopyPlane(theDisp, lnkPix, lp->win, theGC, 0, 0,
		   i_lnk_width, i_lnk_height, 3, ypos, 1L);

      else if (lp->str[j][0] == C_SOCK) 
	XCopyPlane(theDisp, sockPix, lp->win, theGC, 0, 0,
		   i_sock_width, i_sock_height, 3, ypos, 1L);

      else  /* lp->str[j][0] == C_REG */
	XCopyPlane(theDisp, regPix, lp->win, theGC, 0, 0,
		   i_reg_width, i_reg_height, 3, ypos, 1L);


      XDrawString(theDisp, lp->win, theGC, 3 + i_fifo_width + 3, 
		  i*LINEHIGH + ASCENT + 1, 
		  lp->str[j]+1, strlen(lp->str[j]+1));
    }
  }
}


/***************************************************/
void LSRedraw(lp)
LIST *lp;
{
  int  i;

  for (i = lp->scrl.val; i < lp->scrl.val + lp->nlines; i++) 
    drawSel(lp,i);
}


/***************************************************/
int LSClick(lp,ev)
LIST *lp;
XButtonEvent *ev;
{
  /* returns '-1' normally.  returns 0 -> numnames-1 for a goto */

  Window       rW, cW;
  int          rx, ry, x, y, sel, oldsel;
  unsigned int mask;
  static Time  lasttime=0;
  static int   lastsel = -1;

  x = ev->x;  y = ev->y;
  sel = lp->scrl.val + y/LINEHIGH;
  if (sel >= lp->nstr) sel = lp->selected;

  /* see if it's a double click */
  if (ev->time - lasttime < DBLCLKTIME && sel==lastsel 
      && (lp->scrl.val + y/LINEHIGH) < lp->nstr
      && !INACTIVE(lp,sel)) {
    return (sel);
  }

  lasttime = ev->time;  lastsel = sel;

  /* if not clicked on selected, turn off selected and select new one */
  if (sel != lp->selected) {
    oldsel = lp->selected;
    lp->selected = sel;
    drawSel(lp,sel);  drawSel(lp,oldsel);
    XFlush(theDisp);
  }

  while (XQueryPointer(theDisp,lp->win,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
    if (!(mask & Button1Mask)) break;    /* button released */

    if (y<0) { /* scroll up in list */ 
      if (lp->scrl.val > lp->scrl.min) {
	lp->selected = lp->scrl.val - 1;
	SCSetVal(&lp->scrl, lp->scrl.val - 1);
	Timer(100);
      }
    }

    else if (y > lp->h) { /* scroll down in list */
      if (lp->scrl.val < lp->scrl.max) {
	lp->selected = lp->scrl.val + lp->nlines;
	if (lp->selected >= lp->nstr) lp->selected = lp->nstr - 1;
	SCSetVal(&lp->scrl, lp->scrl.val + 1);
	Timer(100);
      }
    }

    else {
      sel = lp->scrl.val + y/LINEHIGH;
      if (sel >= lp->nstr) sel = lp->nstr - 1;

      if (sel != lp->selected && sel >= lp->scrl.val &&
	  sel < lp->scrl.val + lp->nlines) {  
	/* dragged to another on current page */
	oldsel = lp->selected;
	lp->selected = sel;
	drawSel(lp, sel);  drawSel(lp, oldsel);
	XFlush(theDisp);
      }
    }
  }

  return(-1);
}



/***************************************************/
void LSKey(lp, key)
LIST         *lp;
int           key;
{
  if      (key==LS_PAGEUP)   SCSetVal(&lp->scrl,lp->scrl.val - (lp->nlines-1));
  else if (key==LS_PAGEDOWN) SCSetVal(&lp->scrl,lp->scrl.val + (lp->nlines-1));
  else if (key==LS_HOME)     SCSetVal(&lp->scrl,lp->scrl.min);
  else if (key==LS_END)      SCSetVal(&lp->scrl,lp->scrl.max);

  else if (key==LS_LINEUP)   {
    /* if the selected item visible, but not the top line */
    if (lp->selected > lp->scrl.val && 
	lp->selected <= lp->scrl.val + lp->nlines - 1) {
      /* then just move it */
      lp->selected--;
      drawSel(lp, lp->selected);  drawSel(lp, lp->selected+1);
    }

    /* if it's the top line... */
    else if (lp->selected == lp->scrl.val) {
      if (lp->selected > 0) {
	lp->selected--;
	SCSetVal(&lp->scrl, lp->selected);
      }
    }

    /* if it's not visible, put it on the bottom line */
    else {
      lp->selected = lp->scrl.val + lp->nlines - 1;
      if (lp->selected >= lp->nstr) lp->selected = lp->nstr - 1;
      drawSel(lp, lp->selected);
    }
  }
    
  else if (key==LS_LINEDOWN)   {
    /* if the selected item visible, but not the bottom line */
    if (lp->selected >= lp->scrl.val && 
	lp->selected < lp->scrl.val + lp->nlines - 1) {
      if (lp->selected < lp->nstr-1) {
	/* then just move it */
	lp->selected++;
	drawSel(lp, lp->selected);  drawSel(lp, lp->selected-1);
      }
    }

    /* if it's the bottom line... */
    else if (lp->selected == lp->scrl.val + lp->nlines - 1) {
      if (lp->selected < lp->nstr-1) {
	lp->selected++;
	SCSetVal(&lp->scrl, lp->scrl.val+1);
      }
    }

    /* if it's not visible, put it on the top line */
    else {
      lp->selected = lp->scrl.val;
      drawSel(lp, lp->selected);
    }
  }
}


