/*
 * Copyright (c) 1998 Rene Fichter <ceezaer@cyberspace.org>
 * Copyright (c) 1997 Guylhem Aznar <guylhem@danmark.linux.eu.org>
 * Copyright (c) 1994 Mike Finger <mfinger@mermaid.micro.umn.edu>
 *                             or <Mike_Finger@atk.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include "ButtonArray.h"
#include "Mallocs.h"

#ifndef min
#define min(a,b) (((a)<(b)) ? (a) : (b))
#define max(a,b) (((a)>(b)) ? (a) : (b))
#endif

extern XFontStruct *ButtonFont;
extern Display *dpy;
extern Window win;
extern GC shadow,hilite,graph;

extern int LeftJustify;
extern int TruncateLeft;
extern int OrientVert;

/******************************************************************************
  InitArray - Initialize the arrary of buttons
******************************************************************************/
void InitArray(ButtonArray *array,int x,int y,int w,int h)
{
  array->count=0;
  array->head=array->tail=NULL;
  array->x=x;
  array->y=y;
  array->w=w;
  array->h=h;
}

/******************************************************************************
  UpdateArray - Update the array specifics.  x,y, width, height
******************************************************************************/
void UpdateArray(ButtonArray *array,int x,int y,int w, int h)
{
Button *temp;
  if (x!=-1) array->x=x;
  if (y!=-1) array->y=y;
  if (w!=-1) array->w=w;
  if (h!=-1) array->h=h;
  for(temp=array->head;temp!=NULL;temp=temp->next) temp->needsupdate=1;
}

/******************************************************************************
  AddButton - Allocate space for and add the button to the bottom
******************************************************************************/
int AddButton(ButtonArray *array, char *title, int up)
{
Button *new;
  new=(Button *)safemalloc(sizeof(Button));
  new->title=safemalloc(strlen(title)+1);
  strcpy(new->title,title);
  new->up=up;
  new->tw=XTextWidth(ButtonFont,title,strlen(title));
  new->truncatewidth=0;
  new->next=NULL;
  new->needsupdate=1;
  new->set=0;

  if (array->head==NULL) array->head=array->tail=new;
  else {
    array->tail->next=new;
    array->tail=new;
  }
  array->count++;
  return (array->count-1);
}

/******************************************************************************
  UpdateButton - Change the name/state of a button
******************************************************************************/
int UpdateButton(ButtonArray *array, int butnum, char *title, int up)
{
Button *temp;
  temp=find_n(array,butnum);
  if (temp!=NULL) {
    if (title!=NULL) {
      temp->title=(char *)realloc(temp->title,strlen(title)+1);
      strcpy(temp->title,title);
      temp->tw=XTextWidth(ButtonFont,title,strlen(title));
      temp->truncatewidth = 0;
    }
    if (up!=-1) temp->up=up;
  } else return -1;
  temp->needsupdate=1;
  return 1;
}

/******************************************************************************
  RemoveButton - Delete a button from the list
******************************************************************************/
void RemoveButton(ButtonArray *array, int butnum)
{
Button *temp,*temp2;
  if (butnum==0) {
    temp2=array->head;
    temp=array->head=array->head->next;
  } else {
    temp=find_n(array,butnum-1);
    if (temp==NULL) return;
    temp2=temp->next;
    temp->next=temp2->next;
   }

  if (array->count > 0)
    array->count--;

  if (array->tail==temp2) array->tail=temp;

  FreeButton(temp2);

  if (temp!=array->head) temp=temp->next;
  for(temp;temp!=NULL;temp=temp->next) temp->needsupdate=1;
}

/******************************************************************************
  find_n - Find the nth button in the list (Use internally)
******************************************************************************/
Button *find_n(ButtonArray *array, int n)
{
Button *temp; 
int i;
  temp=array->head;
  for(i=0;i<n && temp!=NULL;i++,temp=temp->next);
  return temp;
}

/******************************************************************************
  FreeButton - Free space allocated to a button
******************************************************************************/
void FreeButton(Button *ptr)
{
  if (ptr != NULL) {
    if (ptr->title!=NULL) free(ptr->title);
    free(ptr);
  }
}

/******************************************************************************
  FreeAllButtons - Free the whole array of buttons
******************************************************************************/
void FreeAllButtons(ButtonArray *array)
{
Button *temp,*temp2;
  for(temp=array->head;temp!=NULL;) {
    temp2=temp;
    temp=temp->next;
    FreeButton(temp2);
  }
}

/******************************************************************************
  DoButton - Draw the specified button.  (Used internally)
******************************************************************************/
void DoButton(Button *ptr, int x, int y, int w, int h)
{
int up,Fontheight,newx,set;
char *string;

  up=ptr->up;

  Fontheight=ButtonFont->ascent+ButtonFont->descent;

  XClearArea(dpy,win,x,y,w,h,False);
  XDrawLine(dpy,win,(up) ? hilite : shadow,x,y,x+w-1,y);
  XDrawLine(dpy,win,(up) ? hilite : shadow,x,y+1,x+w-2,y+1);

  XDrawLine(dpy,win,(up) ? hilite : shadow,x,y,x,y+h-1);
  XDrawLine(dpy,win,(up) ? hilite : shadow,x+1,y,x+1,y+h-2);
  
  XDrawLine(dpy,win,(up) ? shadow : hilite,x,y+h,x+w,y+h);
  XDrawLine(dpy,win,(up) ? shadow : hilite,x+1,y+h-1,x+w,y+h-1);

  XDrawLine(dpy,win,(up) ? shadow : hilite,x+w,y+h,x+w,y);
  XDrawLine(dpy,win,(up) ? shadow : hilite,x+w-1,y+h,x+w-1,y+1);



  string=ptr->title;
  if (LeftJustify)
    newx=4;
  else
  if (TruncateLeft && (w-ptr->tw)/2 < 4) {
    if (ptr->truncatewidth == w)
      string=ptr->truncate_title;
    else {
      string=ptr->title;
      while(*string && (w-XTextWidth(ButtonFont,string,strlen(string)))/2 < 4)
        string++;
      ptr->truncatewidth = w;
      ptr->truncate_title=string;
    }
  }
  XDrawString(dpy,win,graph,x+newx+(up?0:1),y+3+ButtonFont->ascent+(up?0:1),
	      string,strlen(string));
  ptr->needsupdate=0;
}

/******************************************************************************
  DrawButtonArray - Draw the whole array (all=1), or only those that need.
******************************************************************************/
void DrawButtonArray(ButtonArray *array,int all)
{
Button *temp;
int i;
  for(temp=array->head,i=0;temp!=NULL;temp=temp->next,i++) 
    if (temp->needsupdate || all) {
      if (OrientVert)
	DoButton(temp,array->x,array->y+(i*(array->h+1)),array->w,array->h);
      else
	DoButton(temp,array->x+(i*((float)array->w/array->count)),array->y,
		 array->w/array->count,array->h);
    }
}

/******************************************************************************
  SwitchButton - Alternate the state of a button
******************************************************************************/
void SwitchButton(ButtonArray *array,int butnum)
{
Button *temp;
  temp=find_n(array,butnum);
  temp->up=!temp->up;
  temp->needsupdate=1;
  DrawButtonArray(array,0);
}

/******************************************************************************
  WhichButton - Based on x,y which button was pressed
******************************************************************************/
int WhichButton(ButtonArray *array,int x, int y)
{
int num;
  if (OrientVert)
    num=y/(array->h+1);
  else
    num=x/((float)array->w/array->count);

  if (x<array->x || x>array->x+array->w || num<0 || num>array->count-1) num=-1;

  return(num);
}

/******************************************************************************
  ButtonName - Return the name of the button
******************************************************************************/
char *ButtonName(ButtonArray *array, int butnum)
{
Button *temp;
  temp=find_n(array,butnum);
  return temp->title;
}

/******************************************************************************
  PrintButtons - Print the array of button names to the console. (Debugging)
******************************************************************************/
void PrintButtons(ButtonArray *array)
{
Button *temp;
  ConsoleMessage("List of Buttons:\n");
  for(temp=array->head;temp!=NULL;temp=temp->next)
    ConsoleMessage("   %s is %s\n",temp->title,(temp->up) ? "Up":"Down");
}

/******************************************************************************
  ButtonArrayMaxWidth - Calculate the width needed for the widest title
******************************************************************************/
int ButtonArrayMaxWidth(ButtonArray *array)
{
Button *temp;
int x=0;
  for(temp=array->head;temp!=NULL;temp=temp->next)
    x=max(temp->tw,x);
  return x;
}
