/*--------------------------------*-C-*---------------------------------*
 * File:	sbar.c
 *
 * Copyright 1992 John Bovey, University of Kent at Canterbury.
 *
 * You can do what you like with this source code as long as you don't
 * try to make money out of it and you include an unaltered copy of this
 * message (including the copyright).
 *
 * This module has been heavily modified by R. Nation
 * (nation@rocket.sanders.lockheed.com).
 * No additional restrictions are applied
 *
 * Extensive modifications by mj olesen <olesen@me.QueensU.CA>
 * No additional restrictions are applied.
 *
 * As usual, the author accepts no responsibility for anything, nor does
 * he guarantee anything whatsoever.
 *----------------------------------------------------------------------*/
#include "rxvt.h"
#include <X11/Xlib.h>
#include "sbar.h"
#include "xsetup.h"

/*----------------------------------------------------------------------*
 * extern functions referenced
 */
/*----------------------------------------------------------------------*
 * extern variables referenced
 */

/*----------------------------------------------------------------------*
 * extern variables declared here
 */
/*----------------------------------------------------------------------*
 * local variables
 */
#ifdef NO_SCROLLBAR
void sbar_init (Window win, XGCValues *gcv) {}
#else	/* NO_SCROLLBAR */

# ifdef XTERM_SCROLLBAR		/* bitmap scrollbar */
static GC sbarGC;

#  define sb_width 12
#  define sb_height 2
static char sb_bits[] = {
   0x55, 0x05, 0xaa, 0x0a};

#  if (SB_WIDTH != sb_width)
Error, check scrollbar width
#  endif
# else
static GC topShadowGC, bottomShadowGC;
# endif	/* ! XTERM_SCROLLBAR */

/*----------------------------------------------------------------------*
 * local functions referenced
 */

/*----------------------------------------------------------------------*/

/*
 * Initialize scrollbar data structures - called just once,
 * gcv already contains foreground, background, font
 *
 * The calculations of topShadow/bottomShadow values are adapted from the
 * fvwm window manager.
 */
void
sbar_init (Window win, XGCValues *gcv)
{
# ifdef XTERM_SCROLLBAR
   gcv->fill_style = FillOpaqueStippled;
   gcv->stipple = XCreateBitmapFromData (Xdisplay, sbar.sb,
					 sb_bits, sb_width, sb_height);
   if (!gcv->stipple)
     {
	print_error ("can't create bitmap");
	exit (EXIT_FAILURE);
     }

   sbarGC = XCreateGC (Xdisplay, win,
		       GCForeground|GCBackground|GCFillStyle|GCStipple,
		       gcv);

   Options &= ~(Opt_sbArrows);
# else	/* XTERM_SCROLLBAR */

   XColor xcol;

   if (!(Options & Opt_scrollBar))
     return;

   if (!XParseColor (Xdisplay, Xcmap, rs_color [SCROLLCOLOR], &xcol) ||
       !XAllocColor (Xdisplay, Xcmap, &xcol))
     pixel_colors [SCROLLCOLOR] = pixel_colors [COLORBG];
   else
     pixel_colors [SCROLLCOLOR] = xcol.pixel;

   pixel_colors [BOTTOMSHADOWCOLOR] = pixel_colors [SCROLLCOLOR];
   pixel_colors [TOPSHADOWCOLOR] = pixel_colors [SCROLLCOLOR];
   if (Xdepth > 2)
     {
	XColor white;
	int okay;

	if (rs_color [BOTTOMSHADOWCOLOR] != NULL)
	  {
	     okay = (XParseColor (Xdisplay, Xcmap,
				  rs_color [BOTTOMSHADOWCOLOR], &xcol) &&
		     XAllocColor (Xdisplay, Xcmap, &xcol));
	  }
	else
	  {
	     /* find bottomShadow color from scrollColor */
	     /* pixel_colors [BOTTOMSHADOWCOLOR] = pixel_colors [SCROLLCOLOR]; */
	     xcol.pixel = pixel_colors [SCROLLCOLOR];
	     XQueryColor (Xdisplay, Xcmap, &xcol);

	     xcol.red   = ((xcol.red)   / 2);
	     xcol.green = ((xcol.green) / 2);
	     xcol.blue  = ((xcol.blue)  / 2);

	     okay = XAllocColor (Xdisplay, Xcmap, &xcol);
	  }

	if (okay)
	  {
	     pixel_colors [BOTTOMSHADOWCOLOR] = xcol.pixel;
	  }
	else
	  {
	     print_error ("can't allocate bottomShadow");
	     pixel_colors [BOTTOMSHADOWCOLOR] = pixel_colors [COLOR_BLACK];
	  }

	if (rs_color [TOPSHADOWCOLOR] != NULL)
	  {
	     okay = (XParseColor (Xdisplay, Xcmap, rs_color [TOPSHADOWCOLOR], &xcol) &&
		     XAllocColor (Xdisplay, Xcmap, &xcol));
	  }
	else
	  {
	     /* find topShadow color from scrollColor */
	     /* pixel_colors [TOPSHADOWCOLOR] = pixel_colors [SCROLLCOLOR]; */

	     xcol.pixel = pixel_colors [SCROLLCOLOR];
	     XQueryColor (Xdisplay, Xcmap, &xcol);

	     white.pixel = WhitePixel (Xdisplay, Xscreen);
	     XQueryColor (Xdisplay, Xcmap, &white);

#  ifndef min
#   define min(a,b) (((a)<(b)) ? (a) : (b))
#   define max(a,b) (((a)>(b)) ? (a) : (b))
#  endif
	     xcol.red   = max ((white.red   / 5), xcol.red);
	     xcol.green = max ((white.green / 5), xcol.green);
	     xcol.blue  = max ((white.blue  / 5), xcol.blue);

	     xcol.red   = min (white.red,   (xcol.red   * 7) / 5);
	     xcol.green = min (white.green, (xcol.green * 7) / 5);
	     xcol.blue  = min (white.blue,  (xcol.blue  * 7) / 5);

	     okay = XAllocColor (Xdisplay, Xcmap, &xcol);
	  }

	if (okay)
	  {
	     pixel_colors [TOPSHADOWCOLOR] = xcol.pixel;
	  }
	else
	  {
	     print_error ("can't allocate topShadow");
	     pixel_colors [TOPSHADOWCOLOR] = pixel_colors [COLOR_WHITE];
	  }
     }

   gcv->foreground = pixel_colors [TOPSHADOWCOLOR];
   gcv->background = pixel_colors [TOPSHADOWCOLOR];

   topShadowGC = XCreateGC (Xdisplay, win,
			    GCForeground|GCBackground,
			    gcv);

   gcv->foreground = pixel_colors [BOTTOMSHADOWCOLOR];
   gcv->background = pixel_colors [BOTTOMSHADOWCOLOR];
   bottomShadowGC = XCreateGC (Xdisplay, win,
			       GCForeground|GCBackground,
			       gcv);

   XSetWindowBackground (Xdisplay, sbar.sb, pixel_colors [SCROLLCOLOR]);
# endif	/* XTERM_SCROLLBAR */

   sbar_show (1);
}

# ifndef XTERM_SCROLLBAR
/*
 * draw bottomShadow/highlight along top/left sides of the window
 */
static void
Draw_tl (Window win, GC gc, int x, int y, int w, int h)
{
   int i;

   w += (x - 1);
   h += (y - 1);

   for (i = 0; i < SB_SHADOW; i++, x++, y++, w--, h--)
     {
	XDrawLine (Xdisplay, win, gc, x, y, w, y);
	XDrawLine (Xdisplay, win, gc, x, y, x, h);
     }
}

/*
 * draw bottomShadow/highlight along the bottom/right sides of the window
 */
static void
Draw_br (Window win, GC gc, int x, int y, int w, int h)
{
   int i;

   w += (x - 1);
   h += (y - 1);

   x++; y++;
   for (i = 0; i < SB_SHADOW; i++, x++, y++, w--, h--)
     {
	XDrawLine (Xdisplay, win, gc, w, h, w, y);
	XDrawLine (Xdisplay, win, gc, w, h, x, h);
     }
}

/* draw triangular up button with a shadow of 2 pixels */
static void
Draw_up_button (Window win, GC gc1, GC gc2, int x, int y, int size)
{
   XSegment seg [4];
   int n;

   n = 0;
   seg[n].x1 = x + size/2;	seg[n].y1 = y;
   seg[n].x2 = x;		seg[n].y2 = y + size - 1;
   n++;

   seg[n].x1 = x + size/2;	seg[n].y1 = y + 1;
   seg[n].x2 = x + 1;		seg[n].y2 = y + size - 2;
   n++;

   XDrawSegments (Xdisplay, win, gc1, seg, n);

   n = 0;
   seg[n].x1 = x + size/2 + 1;	seg[n].y1 = y + 1;
   seg[n].x2 = x + size - 1;	seg[n].y2 = y + size - 2;
   n++;

   seg[n].x1 = x + size/2 + 1;	seg[n].y1 = y + 2;
   seg[n].x2 = x + size - 2;	seg[n].y2 = y + size - 3;
   n++;

   seg[n].x1 = x + 1;		seg[n].y1 = y + size - 1;
   seg[n].x2 = x + size - 1;	seg[n].y2 = y + size - 1;
   n++;

   seg[n].x1 = x + 2;		seg[n].y1 = y + size - 2;
   seg[n].x2 = x + size - 1;	seg[n].y2 = y + size - 2;
   n++;

   XDrawSegments (Xdisplay, win, gc2, seg, n);
}

/* draw triangular down button with a shadow of 2 pixels */
static void
Draw_dn_button (Window win, GC gc1, GC gc2, int x, int y, int size)
{
   XSegment seg[4];
   int n;

   n = 0;
   seg[n].x1 = x + size - 1;	seg[n].y1 = y;
   seg[n].x2 = x + size/2;	seg[n].y2 = y + size - 1;
   n++;

   seg[n].x1 = x + size - 2;	seg[n].y1 = y + 1;
   seg[n].x2 = x + size/2;	seg[n].y2 = y + size - 2;
   n++;

   XDrawSegments (Xdisplay, win, gc2, seg, n);

   n = 0;
   seg[n].x1 = x;		seg[n].y1 = y;
   seg[n].x2 = x + size/2 - 1;	seg[n].y2 = y + size - 2;
   n++;

   seg[n].x1 = x + 1;		seg[n].y1 = y + 1;
   seg[n].x2 = x + size/2 - 1;	seg[n].y2 = y + size - 3;
   n++;

   seg[n].x1 = x;		seg[n].y1 = y;
   seg[n].x2 = x + size - 2;	seg[n].y2 = y;
   n++;

   seg[n].x1 = x;		seg[n].y1 = y + 1;
   seg[n].x2 = x + size - 3;	seg[n].y2 = y + 1;
   n++;

   XDrawSegments (Xdisplay, win, gc1, seg, n);
}
# endif	/* XTERM_SCROLLBAR */

/*
 * Redraw scrollbar
 */
int
sbar_show (int update)
{
   /* old, drawn values */
   static int last_top, last_bot, last_len;

   if (update)
     {
	int top, bot, len;
	top = (TermWin.hist_count - TermWin.hist_start);
	bot = top + (TermWin.nrow-1);
	len = (TermWin.hist_count + (TermWin.nrow-1));

	sbar.top = (top * (sbar.end - sbar.beg)) / len + sbar.beg;
	sbar.bot = (bot * (sbar.end - sbar.beg)) / len + sbar.beg;

	if (sbar.top < sbar.beg) sbar.top = sbar.beg;
	if (sbar.bot > sbar.end) sbar.top = sbar.end;
	sbar.len = (sbar.bot - sbar.top);
     }

   if (!(Options & Opt_scrollBar) ||
       (update &&
	(sbar.top == last_top) &&
	(sbar.bot == last_bot) &&
	(sbar.len == last_len)))
     return 0;			/* no change */

   XClearWindow (Xdisplay, sbar.sb);

   last_top = sbar.top;
   last_bot = sbar.bot;
   last_len = sbar.len;

# ifdef XTERM_SCROLLBAR
   XFillRectangle (Xdisplay, sbar.sb, sbarGC,
		   0, sbar.top,
		   sbar.width, sbar.len);
# else	/* XTERM_SCROLLBAR */

#  define Draw_shadow(win,tlGC,brGC,x,y,w,h)  do {\
Draw_tl (win, tlGC, x, y, w, h);\
Draw_br (win, brGC, x, y, w, h);\
} while (0)

   /*
    * draw shadow for entire scrollbar
    */
   Draw_shadow (sbar.parent, bottomShadowGC, topShadowGC,
		0, 0,
		(sbar.width + 2 * SB_SHADOW),
		((sbar.beg + sbar.end) + 2 * SB_SHADOW));

   /*
    * draw the actual scrollbar
    */
   Draw_shadow (sbar.sb, topShadowGC, bottomShadowGC,
		0, sbar.top,
		sbar.width, sbar.len);

   if (Options & Opt_sbArrows)
     {
	/*
	 * Redraw the scrollbar arrows
	 */
	Draw_up_button (sbar.sb,
			(sbar_isUp () ? bottomShadowGC : topShadowGC),
			(sbar_isUp () ? topShadowGC : bottomShadowGC),
			1, 1, sbar.width - 2);

	Draw_dn_button (sbar.sb,
			(sbar_isDn () ? bottomShadowGC : topShadowGC),
			(sbar_isDn () ? topShadowGC : bottomShadowGC),
			1, sbar.end + 1, sbar.width - 2);
     }

# endif	/* XTERM_SCROLLBAR */
   return 1;
}

#endif	/* NO_SCROLLBAR */
/*----------------------- end-of-file (C source) -----------------------*/
