/*****************************************************************************

    Copyright (C) 1994,1997 Ivan A. Curtis.  All rights reserved.

This code must not be re-distributed without these copyright notices intact.

*******************************************************************************
*******************************************************************************

Filename:	~icurtis/src/mx/basic.c

Description:	

Update History:   (most recent first)
   I. Curtis   9-Apr-97 12:02 -- Updated
   I. Curtis  22-Mar-94 23:11 -- Created.

******************************************************************************/
#include <stdlib.h>
#include "X11/Xlib.h"
#include "X11/Xutil.h"
#include "X11/cursorfont.h"
#include "basic.h"

/***************************************************
 * Default Stipple Pattern for Disabled Menu Items *
 ***************************************************/
#define DEFAULT_PIX_WIDTH  8
#define DEFAULT_PIX_HEIGHT 8
static char mx_default_bits [] = {
  0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };

/*****************************
 * Default pixmap for button *
 *****************************/
#define BUTTON_PIX_WIDTH  8
#define BUTTON_PIX_HEIGHT 8
static char mx_button_bits [] = {
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c };

/***************************
 * Default pixmap for tick *
 ***************************/
#define TICK_PIX_WIDTH  8
#define TICK_PIX_HEIGHT 10
static char mx_tick_bits [] = {
  0xc0, 0xc0, 0xe0, 0x60, 0x70, 0x31, 0x3b, 0x1f, 0x1e, 0x0c };

/*********************
 * Create a new item *
 *********************/
mx_menu_item *mx_menu_item_new(char *text)
{
  mx_menu_item *mi;
  mi = (mx_menu_item *)malloc(sizeof(mx_menu_item));
  mi->length = strlen(text);
  mi->text = (char *)malloc(mi->length + 1);
  strcpy(mi->text, text);
  return mi;
}

/*************************************
 * Create a new appearance structure *
 *************************************/
mx_appearance *mx_appearance_create(Display *display, int screen,
				    Window window,
				    char *font_name,
				    int win_border, int item_border,
				    int pix_width, int pix_height,
				    char *pix_bits,
				    int (*expose_fun)(void))
{
  mx_appearance *app;
  XCharStruct xcs;
  int dir, asc, desc;

  app = (mx_appearance *)malloc(sizeof(mx_appearance));
  app->expose_fun = expose_fun;
  app->win_border = win_border;
  app->item_border = item_border;
				/* set up font */
  if (!(app->font = XLoadQueryFont(display, font_name))) {
    app->font = XLoadQueryFont(display, "fixed");
  }
  if (app->font == NULL) {	/* named font and "fixed" not available! */
    return NULL;
  }
				/* compute font dimensions */
  XTextExtents(app->font, "M", 1,
	       &dir, &asc, &desc, &xcs);
  app->em = xcs.lbearing + xcs.rbearing;
  app->ascent = xcs.ascent;
  app->descent = xcs.descent;
				/* make the flipping GC */
  app->gcf = XCreateGC(display, window, 0, 0);
  XSetState(display, app->gcf, 0xffffffff, 0, 
	    GXxor, BlackPixel(display, screen) ^ WhitePixel(display, screen));
  XSetLineAttributes(display, app->gcf, 0, LineSolid, CapButt, JoinMiter);
  XSetFont(display, app->gcf, app->font->fid);
				/* make the drawing GC */
  app->gcd = XCreateGC(display, window, 0, 0);
  XSetState(display, app->gcd, BlackPixel(display, screen),
	    WhitePixel(display, screen), GXcopy, 0xffffffff);
  XSetLineAttributes(display, app->gcd, 0, LineSolid, CapButt, JoinMiter);
  XSetFont(display, app->gcd, app->font->fid);
  
				/* make stipple pattern */
  if (pix_bits) {		/* user define stipple bitmap */
    app->stipple = XCreateBitmapFromData(display, window, 
					 pix_bits, pix_width, pix_height);
  }
  else {			/* use default stipple */
    app->stipple = XCreateBitmapFromData(display, window, 
					      mx_default_bits,
					      DEFAULT_PIX_WIDTH,
					      DEFAULT_PIX_HEIGHT);
  }
  app->button =  XCreateBitmapFromData(display, window, 
				       mx_tick_bits,
				       TICK_PIX_WIDTH,
				       TICK_PIX_HEIGHT);
  app->button_width = TICK_PIX_WIDTH;
  app->button_height = TICK_PIX_HEIGHT;
  return app;
}

/*********************************************
 * Create a panel structure from an array of *
 * menu items                                *
 *********************************************/
mx_panel *mx_panel_create(Display *display, mx_appearance *app,
			  int n_items, mx_menu_item *mi)
{
  mx_panel *panel;
  int i, dir, asc, desc;
  XCharStruct xcs;

  panel = (mx_panel *)malloc(sizeof(mx_panel));
  panel->app = app;
  panel->width = 0;
  panel->height = 0;
  panel->first_item = 0;
  panel->last_item = n_items - 1;
  panel->n_items = n_items;
  panel->item = mi;
  for (i = 0; i < n_items; i++) {
    mi[i].length = strlen(mi[i].text);
    XTextExtents(app->font, mi[i].text, mi[i].length,
		 &dir, &asc, &desc, &xcs);
    if (xcs.lbearing + xcs.rbearing > panel->width)
      panel->width = xcs.lbearing + xcs.rbearing;
    if (asc + desc > panel->height)
      panel->height = asc + desc;
  }
  return panel;
}

/************************************
 * Open a window on the root window *
 ************************************/
Window mx_window_open(Display *display, int screen, char *title,
		   int WindowXSize, int WindowYSize, int WindowBorder)
{
  Window new_window;
  XSizeHints hint;
				/*** Set hints for window manager ***/
  hint.x = 0; hint.y = 0;
  hint.width = WindowXSize; hint.height = WindowYSize;
  hint.flags = PPosition|PSize;
				/*** Create the top level window ***/
  new_window = XCreateSimpleWindow(display, DefaultRootWindow(display),
				   0, 0, WindowXSize, WindowYSize, 
				   WindowBorder,
				   BlackPixel(display,screen),
				   WhitePixel(display,screen));
				/*** Tell window manager about hints ***/
  XSetStandardProperties(display, new_window, title, title, None,
			 NULL, 0, &hint);
				/*** Initially Select only exposure events ***/
  XSelectInput(display, new_window, ExposureMask);
				/*** Show the window ***/
  XMapRaised(display, new_window);
  return new_window;
}

/**********************************
 * Open a transient window on the *
 * root window                    *
 **********************************/
Window mx_transient_window_open(Display *display, int screen, int border,
			     int xpos, int ypos, int xsize, int ysize)
{
  Window new_window;
  XSetWindowAttributes xswa;
  unsigned long valuemask;
  
  valuemask = 0;
  xswa.cursor = XCreateFontCursor(display, XC_left_ptr);
  valuemask |= CWCursor;
  xswa.background_pixel = WhitePixel(display,screen);
  valuemask |= CWBackPixel;
  xswa.override_redirect = True;
  valuemask |= CWOverrideRedirect;
				/*** Create the window ***/
  new_window = XCreateWindow(display, DefaultRootWindow(display),
			     xpos, ypos, xsize, ysize, border,
			     CopyFromParent, InputOutput, CopyFromParent,
			     valuemask, &xswa);
				/*** Initially select some events ***/
  XSelectInput(display, new_window,
	       ExposureMask | OwnerGrabButtonMask |
	       ButtonPressMask | ButtonReleaseMask |
	       EnterWindowMask | LeaveWindowMask);
				/*** Show the window ***/
  XMapRaised(display, new_window);
  return new_window;
}

/******************
 * Close a window *
 ******************/
void mx_window_close(Display *display, Window window)
{
  XDestroyWindow(display, window);
}

/*************************************
 * Draw the menu items in the window *
 *************************************/
void mx_items_draw(Display *display, Window window, mx_appearance *app,
		   mx_menu_item *item, int start_item, int max_items,
		   int width, int height,
		   int tlx, int tly)
{
  int i, x, dir, asc, desc;
  XCharStruct xcs;

  for (i = start_item; i < max_items + start_item; i++) {
    XTextExtents(app->font, item[i].text, item[i].length,
		 &dir, &asc, &desc, &xcs);
				/* determine x position given alignment */
    switch (item[i].flag & MXItemMask_Align) {
    case MXItemFlag_Center:
      x = (width - xcs.lbearing - xcs.rbearing) / 2;
      break;
    case MXItemFlag_Right:
      x = width - app->item_border - (xcs.lbearing + xcs.rbearing);
      break;
    case MXItemFlag_Left:
    default:			/* default is left alignment */
      x = app->item_border;
      break;
    }
    if (item[i].flag & MXItemFlag_Disabled) {
      XSetStipple(display, app->gcd, app->stipple);
      XSetFillStyle(display, app->gcd, FillStippled);
      XDrawString(display, window, app->gcd,
		  tlx + x, 
		  tly + (i - start_item) * height + app->item_border + app->ascent,
		  item[i].text, item[i].length);
      XSetFillStyle(display, app->gcd, FillSolid);
    }
    else
      XDrawString(display, window, app->gcd,
		  tlx + x,
		  tly + (i - start_item) * height + app->item_border + app->ascent,
		  item[i].text, item[i].length);
				/* possibly overline item */
    if ((item[i].flag & MXItemFlag_Overlined) && (i != start_item))
      XFillRectangle(display, window, app->gcd,
		     0, tly + (i - start_item) * height,
		     width, app->item_border);
				/* possible underline item */
    if ((item[i].flag & MXItemFlag_Underlined) &&
	(i != max_items + start_item - 1))
      XFillRectangle(display, window, app->gcd,
		     0, tly + (i - start_item) * height + app->item_border + app->ascent +
		     app->descent, width, app->item_border);
  }
}


/********************************************************
 * Adjust the top left coordinates 'x' and 'y' so that  *
 * a window positioned there with geometry 'width' and  *
 * 'height' will not be off the edge of the root window *
 ********************************************************/
void mx_adjust_xy(Display *display, int width, int height, int *x, int *y)
{
  Window root;
  int root_x, root_y, root_w, root_h, root_border, root_depth;
				/* determine size of root window */
  XGetGeometry(display, DefaultRootWindow(display),
	       &root, &root_x, &root_y, &root_w, &root_h,
	       &root_border, &root_depth);
				/* maybe adjust x coord */
  if (*x + width > root_x + root_w)
    *x = root_x + root_w - width;
				/* maybe adjust y coord */
  if (*y + height > root_y + root_h)
    *y = root_y + root_h - height;
}
