/* started off as a interface to g3vga viewer, now a generic frontend */
/* g3vga - simple g3 FAX fiewer -
   Mitch 30th May 1995 (m.dsouza@mrc-apu.cam.ac.uk) */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <malloc.h>
#include "vgaui.h"
#include "fontutils.h"
#include "miscutils.h"
#include "loadim.h"
#include "menu.h"
#ifdef MOUSE_SUPPORT
#include <vgamouse.h>
#include <vgakeyboard.h>
#endif

#define CTRL_F		6
#define ESC		27
#define CTRL_P		16
#define HOME		49
#define END		52
#define PAGE_UP		53
#define PAGE_DOWN	54
#define ARROW_UP	65
#define ARROW_DOWN	66
#define ARROW_RIGHT	67
#define ARROW_LEFT	68

#define sqr(x) ((x)*(x))
/*
   Mode    1: 320x200, 16 colors (4 bitplanes)
   Mode    2: 640x200, 16 colors (4 bitplanes)
   Mode    3: 640x350, 16 colors (4 bitplanes)
   Mode    4: 640x480, 16 colors (4 bitplanes)
   Mode    5: 320x200, 256 colors (linear)
   Mode    6: 320x240, 256 colors (Mode X)
   Mode    7: 320x400, 256 colors (Mode X)
   Mode    8: 360x480, 256 colors (Mode X)

 */

/* Everyone should have this one at least these days */
#define DEFMODE 4

#define TITLE_WIDTH 17

#ifdef MOUSE_SUPPORT
#define COMMAND_BAR_WIDTH 17
#define BORDER_WIDTH 0
#else
#define COMMAND_BAR_WIDTH 0
#define BORDER_WIDTH 8
#endif

short int xmax,
  ymax;
short int bw = BORDER_WIDTH;
static char *background;
static char force,
 *curfile;
static int rgbsave[256*3];
static short int vga_x = 0, vga_y = 0;
static int arrow_step, page_step;
static font_t *tfont, *pfont, *ifont, *mfont;
static int curopt, poptno;
static char *newname = NULL;
static char **pargv;

#define force_vga_display(x,y) force=1;vga_display(x,y)
#define beep() printf("\007"), fflush(stdout);

#ifndef MOUSE_SUPPORT
#define sbwidth 8
static struct sb {
  char empty[sbwidth];
  char fill[sbwidth];
} sb;
#endif

static int done() {
  vga_setpalvec(0,256,rgbsave);
  vga_setmode (TEXT);
  exit (0);
}

static vga_modeinfo *modeinfo;
static short vp_x,
  vp_y,
  vp_wid,
  vp_ht;

vga_image image = {
	NULL,	/* The image data */
	0,	/* The width of the image */
	0,	/* The height of the image */
	NULL,	/* The rgb colormap */
	0.0,	/* The scale of the image (0-1.0)*/
	NULL	/* The filename of the image */
};

char *progname="vgagui";
char *version="0.4";
char (*vga_key_callback)(char)=NULL;
extern int vga_release_menu();
extern int page_selector();
extern int file_selector();
extern int show_help();
extern int increase_res();
extern int decrease_res();
extern int bottom_page();
extern int top_page();
extern int invert_page();
extern int print_file();
extern int next_page();
extern int previous_page();

#ifdef MOUSE_SUPPORT
static vga_menu_t left_menu[] = {
	"Quit",		done,
	NULL,		NULL,
};

static vga_menu_t file_menu[] = {
	"Open",		file_selector,
	"Goto page",	page_selector,
	"",		NULL,
	"Print",	print_file,
	"",		NULL,
	"Help",		show_help,
	NULL,		NULL,
};

static vga_menu_t image_menu[] = {
	"Top",		top_page,
	"Bottom",	bottom_page,
	"",		NULL,
	"Invert",	invert_page,
	"",		NULL,
	"Increase res",	increase_res,
	"Decrease res",	decrease_res,
	NULL,		NULL,
};

static char *menu_bar_labels[] = {
	"File", "Image", NULL
};
static vga_menu_t *menu_bar_menus[] = {
	file_menu, image_menu, NULL
};

#define PERM_HOTSPOTS 6
#define TEMP_HOTSPOTS 6
static mouse_hs_t perm_hotspot[PERM_HOTSPOTS];
static mouse_hs_t temp_hotspot[TEMP_HOTSPOTS];
static vga_win cur_win = { 0, 0, 0, 0, 0 };

static vga_win *menu_is_up = NULL;

static vga_win cur_item = { 0, 0, 0, 0, 0 };


static inline int
mouse_in (mouse_hs_t *mhs) {
	int mx = mouse_getx();
	int my = mouse_gety();

	return (mx >= mhs->x1 && mx <= mhs->x2 &&
		my >= mhs->y1 && my <= mhs->y2);

}

int vga_release_menu() {
	int i;

	if (cur_item.data) {
		vga_restore_win(&cur_item);
		cur_item.data=NULL;
	}
	if (!menu_is_up)
		return;
	for (i = 0; i<TEMP_HOTSPOTS; i++)
		temp_hotspot[i].func=NULL;
	vga_restore_win(menu_is_up);
	menu_is_up = NULL;
}

static int menubarcallback() {
	int i;

	if (cur_item.data || menu_is_up)
		return;
	for (i = 2; i<4; i++)
		if (mouse_in(&perm_hotspot[i])) {
			cur_item.x = perm_hotspot[i].x1;
			cur_item.y = perm_hotspot[i].y1;
			cur_item.wid = perm_hotspot[i].x2 - perm_hotspot[i].x1;
			cur_item.ht = perm_hotspot[i].y2 - perm_hotspot[i].y1-1;
		vga_save_win(&cur_item);
#if 0
		vga_box(cur_item.x,
			cur_item.y,
			cur_item.wid,
			cur_item.ht,
			LIGHTRED, WHITE, HOLLOW);
#else
		vga_reverse_win(&cur_item, BLACK, LIGHTGRAY);
#endif
		vga_popupmenu(menu_bar_menus[i-2],
			      perm_hotspot[i].x1,
      			      perm_hotspot[i].y2,
			      BLACK, LIGHTGRAY, mfont,
			      &temp_hotspot[0], menu_is_up = &cur_win);
		}
}

static int leftbuttonmenucallback() {
	int i;

	if (!menu_is_up) {
		cur_item.x = perm_hotspot[0].x1;
		cur_item.y = perm_hotspot[0].y1;
		cur_item.wid = perm_hotspot[0].x2 - perm_hotspot[0].x1;
		cur_item.ht = perm_hotspot[0].y2 - perm_hotspot[0].y1-1;
		vga_save_win(&cur_item);
		vga_reverse_win(&cur_item, BLACK, LIGHTGRAY);
		vga_popupmenu(left_menu, bw, bw+TITLE_WIDTH,
			      BLACK, LIGHTGRAY, mfont,
			      &temp_hotspot[0], menu_is_up = &cur_win);
	}
}

void
draw_menu_bar(font_t *font, char *labels[]) {
int i;
int px;

  vga_setegacolor (LIGHTGRAY);
  for (i = bw+TITLE_WIDTH + 1; i < bw+TITLE_WIDTH+COMMAND_BAR_WIDTH; i++)
    vga_drawline (bw, i, bw + vp_wid, i);

  for (px = 8, i = 0; labels[i]; i++) {
	vga_write (labels[i],
		   perm_hotspot[i+2].x1 = px,
		   perm_hotspot[i+2].y1 = bw + TITLE_WIDTH +1,
	     font, BLACK, LIGHTGRAY, ALIGN_LEFT);

	perm_hotspot[i+2].x2 = perm_hotspot[i+2].x1 + 8*strlen(labels[i]);
	perm_hotspot[i+2].y2 = perm_hotspot[i+2].y1 + font->font_height;
	perm_hotspot[i+2].func = menubarcallback;
	px += 8 + perm_hotspot[i+2].x2;
  }
	vga_tile(&prev_bm,
		 perm_hotspot[4].x1 = bw+vp_wid-next_bm.wid-prev_bm.wid,
		 perm_hotspot[4].y1 = bw + TITLE_WIDTH +1,
		 prev_bm.wid,
		 prev_bm.ht);

		perm_hotspot[4].x2 = perm_hotspot[4].x1 + prev_bm.wid;
		perm_hotspot[4].y2 = perm_hotspot[4].y1 + prev_bm.ht;
		perm_hotspot[4].func = previous_page;

	vga_tile(&next_bm,
		 perm_hotspot[5].x1 = bw+vp_wid-next_bm.wid,
		 perm_hotspot[5].y1 = bw + TITLE_WIDTH +1,
		 next_bm.wid,
		 next_bm.ht);

		perm_hotspot[5].x2 = perm_hotspot[5].x1 + next_bm.wid;
		perm_hotspot[5].y2 = perm_hotspot[5].y1 + next_bm.ht;
		perm_hotspot[5].func = next_page;
}
#endif

void
vga_display (short int x, short int y)
{
  int i,
    j,
    y1;
  static short int old_x = -1,
    old_y = -1;
#ifndef MOUSE_SUPPORT
  short int sb_start,
    sb_len;
#endif

  if ((x < 0 || y < 0 || x > image.wid || y > image.ht) ||
      (!force && x == old_x && y == old_y)) {
    beep ();
    return;
  }
  force = 0;
#ifndef MOUSE_SUPPORT
  sb_start = vp_y + (y * vp_ht) / image.ht;
  sb_len = TITLE_WIDTH + COMMAND_BAR_WIDTH + sqr (vp_ht) / image.ht;
#endif
  if (image.rgb)
	vga_setpalvec(0,256,image.rgb);
  y1 = y;

  for (j = vp_y, i = y1; j < vp_y + vp_ht && i < image.ht; i++, j++) {

    memset (background, PAGE_COLOR, xmax);
    if (image.data[i] && x < image.wid)
#ifndef MOUSE_SUPPORT
      bcopy (image.data[i] + x, background + sbwidth, min (image.wid - x, vp_wid - sbwidth));
#else
      bcopy (image.data[i] + x, background, min (image.wid - x, vp_wid));
#endif
#ifndef MOUSE_SUPPORT
    bcopy (j >= sb_start && j <= sb_start + sb_len +vp_y ? sb.fill : sb.empty,
	   background, sbwidth);
#endif
    vga_drawscansegment (background, vp_x, j, vp_wid);
  }

  memset (background, PAGE_COLOR, xmax);
#ifndef MOUSE_SUPPORT
  bcopy (sb.fill,background, sbwidth);
#endif
  for (i=j; i<vp_y+vp_ht; i++)
    vga_drawscansegment (background, vp_x, i, vp_wid);

  old_x = x;
  old_y = y;
}

void
update_title (font_t * font, int xpos, int drawbuttons)
{
  char title[1024],
   *p,
   *q;
  int i;

  p = (p = strrchr (progname, '/')) ? ++p : progname;
  q = (q = strrchr (curfile, '/')) ? ++q : curfile;

  vga_setegacolor (TITLE_BG_COLOR);
  for (i = bw; i < bw + TITLE_WIDTH; i++) {
    vga_drawline (bw+left_bm.wid, i, bw + vp_wid-right_bm.wid, i);
  }
  if (drawbuttons) {
	vga_tile(&left_bm,
#ifdef MOUSE_SUPPORT
		 perm_hotspot[0].x1 =
#endif
		 bw,
#ifdef MOUSE_SUPPORT
		 perm_hotspot[0].y1 =
#endif
		 bw,
		 left_bm.wid,
		 left_bm.ht);
#ifdef MOUSE_SUPPORT
		perm_hotspot[0].x2 = perm_hotspot[0].x1 + left_bm.wid;
		perm_hotspot[0].y2 = perm_hotspot[0].y1 + left_bm.ht;
		perm_hotspot[0].func = leftbuttonmenucallback;
#endif
	vga_tile(&right_bm,
#ifdef MOUSE_SUPPORT
		 perm_hotspot[1].x1 =
#endif
			bw+vp_wid-right_bm.wid,
#ifdef MOUSE_SUPPORT
		 perm_hotspot[1].y1 =
#endif
			bw,
		 right_bm.wid,
		 right_bm.ht);
#ifdef MOUSE_SUPPORT
		perm_hotspot[1].x2 = perm_hotspot[1].x1 + right_bm.wid;
		perm_hotspot[1].y2 = perm_hotspot[1].y1 + right_bm.ht;
		perm_hotspot[1].func = done;
#endif
  }
  sprintf (title, "%s (%s) - %s", p, version, q);
  vga_write (title, xpos, bw + 1,
	     font, TITLE_FG_COLOR, TITLE_BG_COLOR, ALIGN_CENTER);
}

void
display (char **argv, int optno)
{
  int i=0,
    j;
  char key;
  int MODE;
#ifdef MOUSE_SUPPORT
  int mouse_x, mouse_y, old_mouse_x, old_mouse_y;
  char mouse_save[8*8], mouse_save_old[8*8];
  char mouse_bm[] = {
			 1, 1, 1, 1, 1, 0, 0, 0,
			 1, 1, 1, 1, 0, 0, 0, 0,
			 1, 1, 1, 1, 0, 0, 0, 0,
			 1, 1, 1, 1, 1, 0, 0, 0,
			 1, 0, 0, 1, 1, 1, 0, 0,
			 0, 0, 0, 0, 1, 1, 1, 0,
			 0, 0, 0, 0, 0, 1, 1, 1,
			 0, 0, 0, 0, 0, 0, 1, 0,
  };
  int mousebutton;

  vga_setmousesupport(1);
#endif

  vga_xbmsetbg(LIGHTGRAY);
  vga_xbmsetfg(BLACK);
  vga_loaddefaultbitmaps();

  MODE = vga_getdefaultmode ();
  if (MODE == -1)
    MODE = DEFMODE;

#ifndef MOUSE_SUPPORT
  memset (sb.empty, BLACK, sbwidth);
  memset (sb.fill, DARKGRAY, sbwidth);
  sb.empty[sbwidth - 1] = sb.fill[sbwidth - 1] = DARKGRAY;
#endif
  vga_setmode (MODE);
  vga_getpalvec(0,256,rgbsave);
  modeinfo = vga_getmodeinfo (MODE);

  pargv = argv;
  poptno = optno;
  curopt = optno;
  curfile = image.filename;
  xmax = vga_getxdim ();
  ymax = vga_getydim ();

  /* Viewport dimensions */
  vp_x = bw;
  vp_y = bw + TITLE_WIDTH + COMMAND_BAR_WIDTH;
  vp_wid = xmax - bw * 2;
  vp_ht = ymax - bw * 2 - (TITLE_WIDTH + COMMAND_BAR_WIDTH);
#ifdef DEBUG
  fprintf (stderr, "vp_x = %d, vp_y = %d, vp_wid = %d, vp_ht = %d\n", vp_x, vp_y, vp_wid, vp_ht);
#endif
  /* Page keys scroll by this much */
  page_step = vp_ht;

  /* Arrow keys scroll by this much - i.e. half a page step */
  arrow_step = vp_ht >> 1;

#ifdef DEBUG
  fprintf (stderr, "arrow_step = %d, xmax = %d, ymax = %d\n", arrow_step, xmax, ymax);
#endif
  /* This is used for backgrounding unused fax lines to the page color */
  background = malloc (xmax);

  tfont = malloc (sizeof (font_t));		/* The Title font */
  vga_initfont (TITLE_FONT, tfont, 1, 1);	/* See the Makefile for info */

  pfont = malloc (sizeof (font_t));		/* The Prompt Font */
  vga_initfont (PROMPT_FONT, pfont, 1, 1);	/* See the Makefile for info */

  ifont = malloc (sizeof (font_t));		/* The usr Input Font */
  vga_initfont (INPUT_FONT, ifont, 1, 1);	/* See the Makefile for info */

  mfont = malloc (sizeof (font_t));		/* The menu  Font */
  vga_initfont (MENU_FONT, mfont, 1, 1);	/* See the Makefile for info */

#ifndef MOUSE_SUPPORT
  vga_setegacolor (BORDER_COLOR);
  for (i = 0; i < bw; i++) {
    vga_drawline (0, i, xmax, i);
    vga_drawline (0, i + ymax - bw, xmax, i + ymax - bw);
    vga_drawline (i, 0, i, ymax);
    vga_drawline (i + bw + vp_wid, 0, i + bw + vp_wid, ymax);
  }

#define ticks (bw+10)
  vga_setcolor (LIGHTGRAY);
  vga_drawline (0, 0, xmax-1, 0);
  vga_drawline (xmax-1, 0, xmax-1, ymax-1);
  vga_drawline (bw-1, bw, bw-1, ymax-bw);
  vga_drawline (bw-1, ymax-bw, xmax-bw-1, ymax-bw);

  vga_drawline (0, ticks, bw-1, ticks);
  vga_drawline (xmax - bw-1, ticks, xmax-1, ticks);
  vga_drawline (0, ymax - ticks, bw-1, ymax - ticks);
  vga_drawline (xmax - bw, ymax - ticks, xmax, ymax - ticks);
  vga_drawline (ticks, 0, ticks, bw-1);
  vga_drawline (xmax - ticks, 0, xmax - ticks, bw-1);
  vga_drawline (ticks, ymax - bw, ticks, ymax);
  vga_drawline (xmax - ticks, ymax - bw, xmax - ticks, ymax);

  vga_setcolor (DARKGRAY);
  vga_drawline (xmax-1, ymax-1, 0, ymax-1);
  vga_drawline (0, ymax-1, 0, 0);
  vga_drawline (bw-1, bw-1, xmax-bw-1, bw-1);
  vga_drawline (xmax-bw, bw-1, xmax-bw, ymax-bw);

  vga_drawline (0, ticks - 1, bw-1, ticks - 1);
  vga_drawline (xmax - bw-1, ticks - 1, xmax-1, ticks - 1);
  vga_drawline (0, ymax - ticks - 1, bw-1, ymax - ticks - 1);
  vga_drawline (xmax - bw, ymax - ticks - 1, xmax, ymax - ticks - 1);
  vga_drawline (ticks - 1, 0, ticks - 1, bw-1);
  vga_drawline (xmax - ticks - 1, 0, xmax - ticks - 1, bw-1);
  vga_drawline (ticks - 1, ymax - bw, ticks - 1, ymax);
  vga_drawline (xmax - ticks - 1, ymax - bw, xmax - ticks - 1, ymax);
#endif
  update_title (tfont, xmax >> 1, 1);
  vga_display (vga_x, vga_y);
#ifdef MOUSE_SUPPORT
  draw_menu_bar(tfont, menu_bar_labels);
  mouse_setposition (xmax>>1,ymax>>1);
  old_mouse_x = mouse_x = mouse_getx();
  old_mouse_y = mouse_y = mouse_gety();

  for (i=0; i < 8; i++)
	vga_getscansegment(mouse_save_old+(i*8), mouse_x, mouse_y+i, 8);
#endif
  /* We loop forever until 'q' */
  for (;;) {
#ifdef MOUSE_SUPPORT
    int scrolled = 0;

    if (old_mouse_x == 0) {
	scrolled = 1;
	for (i=0; i < 8; i++)
		vga_drawscansegment(mouse_save_old+(i*8), old_mouse_x , old_mouse_y+i, 8);
	mouse_setposition (old_mouse_x=8,old_mouse_y);
	vga_x = vga_x - MOUSE_SCROLL_STEP < 0 ?  0 : vga_x - MOUSE_SCROLL_STEP;
	vga_display (vga_x, vga_y);
    }
    if (old_mouse_x == xmax-1) {
	scrolled = 1;
	for (i=0; i < 8; i++)
		vga_drawscansegment(mouse_save_old+(i*8), old_mouse_x , old_mouse_y+i, 8);
	mouse_setposition (old_mouse_x=xmax-8,old_mouse_y);
	vga_x = vga_x + MOUSE_SCROLL_STEP;
	if (vga_x + xmax - 2 * bw > image.wid)
		vga_x = image.wid - xmax - 2 * bw;
	vga_display (vga_x, vga_y);
    }
    if (old_mouse_y == 0) {
	scrolled = 1;
	for (i=0; i < 8; i++)
		vga_drawscansegment(mouse_save_old+(i*8), old_mouse_x , old_mouse_y+i, 8);
	mouse_setposition (old_mouse_x,old_mouse_y=8);
	vga_y = vga_y - MOUSE_SCROLL_STEP < 0 ? 0 : vga_y - MOUSE_SCROLL_STEP;
	vga_display (vga_x, vga_y);
    }
    if (old_mouse_y == ymax-1) {
	scrolled = 1;
	for (i=0; i < 8; i++)
		vga_drawscansegment(mouse_save_old+(i*8), old_mouse_x , old_mouse_y+i, 8);
	mouse_setposition (old_mouse_x,old_mouse_y=ymax-8);
	vga_y = vga_y + MOUSE_SCROLL_STEP > image.ht ? vga_y : vga_y + MOUSE_SCROLL_STEP;
	if (vga_y + ymax - 2 * bw > image.ht)
		vga_y = image.ht - ymax - 2 * bw;
	vga_display (vga_x, vga_y);
    }
    if (scrolled)
	for (i=0; i < 8; i++)
	     vga_getscansegment(mouse_save_old+(i*8), old_mouse_x, old_mouse_y+i, 8);
	
    mouse_update();
    if ((mouse_x = mouse_getx()) != old_mouse_x ||
	(mouse_y = mouse_gety()) != old_mouse_y) {

	for (i=0; i < 8; i++)
		vga_drawscansegment(mouse_save_old+(i*8), old_mouse_x , old_mouse_y+i, 8);

	for (i=0; i < 8; i++) {
		vga_getscansegment(mouse_save+(i*8), mouse_x, mouse_y+i, 8);
		bcopy(mouse_save+(i*8), mouse_save_old+(i*8), 8);
		for (j = 0; j<8; j++)
			if (mouse_bm[(i*8)+j])
				mouse_save[(i*8)+j] = MOUSE_POINTER_COLOR;
		vga_drawscansegment(mouse_save+(i*8), mouse_x , mouse_y+i, 8);
	}

    old_mouse_x = mouse_x;
    old_mouse_y = mouse_y;
    }
    mousebutton = mouse_getbutton();

    if (mousebutton & MOUSE_LEFTBUTTON || mousebutton & MOUSE_RIGHTBUTTON) {
	int inhs = 0;

	for (i=0; i < 8; i++)
		vga_drawscansegment(mouse_save_old+(i*8), old_mouse_x , old_mouse_y+i, 8);

	for (i=0; i<TEMP_HOTSPOTS; i++)
		if (mouse_in(&temp_hotspot[i])) {
			inhs = 1;
			if (temp_hotspot[i].func) {
			int (*func)() = temp_hotspot[i].func;
			vga_release_menu();
			func(mouse_x, mouse_y);
			}
		}
	if (!menu_is_up)
	for (i=0; i<PERM_HOTSPOTS; i++)
		if (mouse_in(&perm_hotspot[i])) {
			inhs = 1;
			if (perm_hotspot[i].func) {
			int (*func)() = perm_hotspot[i].func;
			vga_release_menu();
			func(mouse_x, mouse_y);
			}
		}

	if (!inhs && !menu_is_up)
		vga_release_menu();

	for (i=0; i < 8; i++) {
		vga_getscansegment(mouse_save+(i*8), mouse_x, mouse_y+i, 8);
		bcopy(mouse_save+(i*8), mouse_save_old+(i*8), 8);
		for (j = 0; j<8; j++)
			if (mouse_bm[(i*8)+j])
				mouse_save[(i*8)+j] = MOUSE_POINTER_COLOR;
				
		vga_drawscansegment(mouse_save+(i*8), mouse_x , mouse_y+i, 8);
	}
    }
#else
    key = getchar();
    switch (key) {

    case '+':
      increase_res();
      break;
    case '-':
      decrease_res();
      break;
    case 'h':
    case 'H':
      show_help();
      break;
    case 'i':
    case 'I':
      invert_page();
      break;
    case 'q':
    case 'Q':
      done();
      break;
    case CTRL_F:
      file_selector();
      break;
    case CTRL_P:
      page_selector();
      break;
    case ' ':
    case 'n':
    case 'N':
      next_page();
      break;
    case 'p':
    case 'P':
      previous_page();
      break;      
    case ESC:
      {
	char extkey = getchar ();

	if (extkey == 91 || extkey == 79) {
	  switch (getchar ()) {
	  case ARROW_LEFT:
	    vga_x = vga_x - arrow_step < 0 ?
	      0 : vga_x - arrow_step;
	    vga_display (vga_x, vga_y);
	    break;
	  case ARROW_RIGHT:
	    vga_x = vga_x + arrow_step;
	    if (vga_x + xmax - 2 * bw > image.wid)
	      vga_x = image.wid - xmax - 2 * bw;
	    vga_display (vga_x, vga_y);
	    break;
	  case ARROW_UP:
	    vga_y = vga_y - arrow_step < 0 ?
	      0 : vga_y - arrow_step;
	    vga_display (vga_x, vga_y);
	    break;
	  case ARROW_DOWN:
	    vga_y = vga_y + arrow_step > image.ht ?
	      vga_y : vga_y + arrow_step;
	    if (vga_y + ymax - 2 * bw > image.ht)
	      vga_y = image.ht - ymax - 2 * bw;
	    vga_display (vga_x, vga_y);
	    break;
	  case PAGE_UP:
	    getchar ();
	    vga_y = vga_y - page_step < 0 ?
	      0 : vga_y - page_step;
	    vga_display (vga_x, vga_y);
	    break;
	  case PAGE_DOWN:
	    getchar ();
	    vga_y = vga_y + page_step > image.ht ?
	      vga_y : vga_y + page_step;
	    if (vga_y + ymax - 2 * bw > image.ht)
	      vga_y = image.ht - ymax - 2 * bw;
	    vga_display (vga_x, vga_y);
	    break;
	  case HOME:
	    getchar ();
	    top_page();
	    break;
	  case END:
	    getchar ();
	    bottom_page();
	    break;
	  }
	}
      }
    }
#endif
  }
}

top_page() {
    vga_display (vga_x, vga_y = 0);
}

bottom_page() {
	vga_display (vga_x, vga_y = image.ht - ymax - 2 * bw);
}

file_selector () {
	short pwid = 200,
	  pht = 32;
	char *str;
	int i;

	if ((str = vga_prompt ((xmax + 1 - pwid) >> 1,
			       (ymax + 1 - pht) >> 1,
			       pwid,
			       pht,
			       "Load file: ",
			       pfont,
			       ifont,
			       YELLOW,
			       LIGHTRED,
			       BLACK,
			       PROMPT_SCROLLABLE)) == NULL ||
	    access (str, F_OK | R_OK)) {
	  beep ();
	  return;
	}
	for (i = 0; i < image.ht; i++)
	  if (image.data[i])
	    free (image.data[i]);
	if (newname)
	  free (newname);

	vga_message ((xmax + 1 - 200) >> 1,
		     (ymax + 1 - 32) >> 1,
		     200,
		     32,
		     "Decoding...",
		     pfont,
		     WHITE,
		     RED,
		     0);	/* Return immediately - no bg restoration */

	decode (curfile = newname = str);
	update_title (tfont, xmax >> 1,0);
	force_vga_display (vga_x = 0, vga_y = 0);
}

page_selector () {
	char *str,
	 *p,
	 *f=NULL;
	short pwid = 100,
	  pht = 32;
	int i;

	if ((str = vga_prompt ((xmax + 1 - pwid) >> 1,
			       (ymax + 1 - pht) >> 1,
			       pwid,
			       pht,
			       "Page: ",
			       pfont,
			       ifont,
			       YELLOW,
			       LIGHTRED,
			       BLACK,
			       PROMPT_SCROLLABLE)) == NULL) {
	  beep ();
	  return;
	}
	if (rindex (curfile, '.')) {
	  p = strdup (curfile);
	  *rindex (p, '.') = '\0';
	  f = malloc (PATH_MAX);
	  sprintf (f, NAME_TEMPLATE, p, atoi (str));
	  free (p);
	  if (!access (f, F_OK | R_OK)) {
	    for (i = 0; i < image.ht; i++)
	      if (image.data[i])
		free (image.data[i]);
	    if (newname)
	      free (newname);
	    vga_message ((xmax + 1 - 200) >> 1,
			 (ymax + 1 - 32) >> 1,
			 200,
			 32,
			 "Decoding...",
			 pfont,
			 WHITE,
			 RED,
			 0);	/* Return immediately - no time delay */
	    pargv[atoi(str)] ? curopt = atoi(str): curopt;
	    decode (curfile = newname = f);
	    update_title (tfont, xmax >> 1,0);
	    force_vga_display (vga_x = 0, vga_y = 0);
	  }
	} else {
	  if (f)
	    free (f);
	  beep ();
	}
	free (str);
}

invert_page () {
	int i,j;

	vga_message ((xmax + 1 - 200) >> 1,
		     (ymax + 1 - 32) >> 1,
		     200,
		     32,
		     "Inverting...",
		     pfont,
		     WHITE,
		     RED,
		     0);	/* Return immediately - no bg restoration */

      for (i = 0; i < (image.ht>>1); i++) {
	char *p = image.data[i];
	image.data[i]=image.data[image.ht-i];
	image.data[image.ht-i]=p;
      }
      for (i = 0; i < image.ht; i++) {
	if (!image.data[i])
		continue;

	for (j = 0; j < (image.wid>>1); j++) {
		char p = image.data[i][j];
		image.data[i][j]=image.data[i][image.wid-j];
		image.data[i][image.wid-j]=p;
	}
      }
      force_vga_display (vga_x = 0, vga_y = 0);
}

increase_res () {
      if (vga_key_callback && vga_key_callback('+')) {
	decode(curfile);
	force_vga_display (vga_x = 0, vga_y = 0);
      } else
	beep ();
}
decrease_res () {
      if (vga_key_callback && vga_key_callback('-')) {
	decode(curfile);
	force_vga_display (vga_x = 0, vga_y = 0);
      } else
	beep ();
}
next_page () {
      int i;

      if (pargv[curopt + 1] == NULL) {
	beep ();
	return;
      }
      for (i = 0; i < image.ht; i++)
	if (image.data[i])
	  free (image.data[i]);
      decode (curfile = pargv[++curopt]);
      update_title (tfont, xmax >> 1,0);
      force_vga_display (vga_x = 0, vga_y = 0);
}
previous_page () {
      int i;
      if (curopt == poptno || pargv[curopt - 1] == NULL) {
	beep ();
	return;
      }
      for (i = 0; i < image.ht; i++)
	if (image.data[i])
	  free (image.data[i]);
      decode (curfile = pargv[--curopt]);
      update_title (tfont, xmax >> 1,0);
      force_vga_display (vga_x = 0, vga_y = 0);
}

show_help() {
      if (vga_key_callback && !vga_key_callback('h'))
	beep ();
}

print_file() {
	char command[512];
	sprintf(command,PRINT_COMMAND,curfile);
	if (system(command) < 0)
		printf("print command '%s' failed (%s)\n",command,strerror(errno));
}
