/* FvwmWinList Module for Fvwm. 
 *
 *  Copyright 1994,  Mike Finger (mfinger@mermaid.micro.umn.edu or
 *                               Mike_Finger@atk.com)
 *
 * The functions in this source file that are the original work of Mike Finger.
 * 
 * No guarantees or warantees or anything are provided or implied in any way
 * whatsoever. Use this program at your own risk. Permission to use this
 * program for any purpose is given, as long as the copyright is kept intact.
 *
 *  Things to do:  Convert to C++  (In Progress)
 */

#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>

#include <fvwm/fvwmlib.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[MAX_COLOUR_SETS],hilite[MAX_COLOUR_SETS];
extern GC graph[MAX_COLOUR_SETS],background[MAX_COLOUR_SETS];
extern int LeftJustify;
extern int TruncateLeft;

extern long CurrentDesk;
extern int ShowCurrentDesk;


/*************************************************************************
 *                                                                       *
 *  Button handling functions and procedures                             *
 *                                                                       *
 *************************************************************************/

/* -------------------------------------------------------------------------
   ButtonNew - Allocates and fills a new button structure
   ------------------------------------------------------------------------- */
Button *ButtonNew(char *title, Picture *p, int up)
{
  int updateneeded = 0;
  Button *new, *temp;

  new = (Button *)safemalloc(sizeof(Button));
  new->title = safemalloc(strlen(title)+1);
  strcpy(new->title, title);
  if (p != NULL)
  {
    new->p.picture = p->picture;
    new->p.mask = p->mask;
    new->p.width = p->width;
    new->p.height = p->height;
    new->p.depth = p->depth;
  }
  else

  new->p.picture = 0;

  new->up = up;
  new->next = NULL;
  new->needsupdate = 1;

  return new;
}

/******************************************************************************
  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, Picture *p, int up)
{
  Button *new;

  new = ButtonNew(title, p, up);
  if (array->head == NULL)
  {
    array->head = array->tail = new;
  }
  else
  {
    array->tail->next = new;
    array->tail = new;
  }
  array->count++;

/* in Taskbar this replaces below  ArrangeButtonArray (array);
*/

  new->tw=XTextWidth(ButtonFont,title,strlen(title));
  new->truncatewidth=0;
  new->next=NULL;
  new->needsupdate=1;
  new->set=0;

  return (array->count-1);
}

/******************************************************************************
  UpdateButton - Change the name/stae 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;
}

/* -------------------------------------------------------------------------
   UpdateButtonPicture - Change the picture of a button
   ------------------------------------------------------------------------- */
int UpdateButtonPicture(ButtonArray *array, int butnum, Picture *p)
{
  Button *temp;
  temp=find_n(array,butnum);
  if (temp == NULL) return -1;
  if (temp->p.picture != p->picture || temp->p.mask != p->mask)
  {
    temp->p.picture = p->picture;
    temp->p.mask = p->mask;
    temp->p.width = p->width;
    temp->p.height = p->height;
    temp->p.depth = p->depth;
    temp->needsupdate = 1;
  }
  return 1;
}

/******************************************************************************
  UpdateButtonSet - Change colour set of a button
******************************************************************************/
int UpdateButtonSet(ButtonArray *array, int butnum, int set)
{
  Button *btn;

  btn=find_n(array, butnum);
  if (btn != NULL)
  {
    if (btn->set != set)
    {
      btn->set = set;
      btn->needsupdate = 1;
    }
  } else return -1;
  return 1;
}

/******************************************************************************
  UpdateButtonDesk - Change desk of a button
******************************************************************************/
int UpdateButtonDesk(ButtonArray *array, int butnum, long desk )
{
  Button *btn;

  btn = find_n(array, butnum);
  if (btn != NULL)
  {
    btn->desk = desk;
  } else return -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->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 *button, int x, int y, int w, int h)
{
  int up,Fontheight,newx,set;
  GC topgc;
  GC bottomgc;
  char *string;
  XGCValues gcv;
  unsigned long gcm;
  XFontStruct *font;

  up=button->up;
  set=button->set;
  topgc = up ? hilite[set] : shadow[set];
  bottomgc = up ? shadow[set] : hilite[set];
  font = ButtonFont;

  gcm = GCFont;
  gcv.font = font->fid;
  XChangeGC(dpy, graph[set], gcm, &gcv);


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

 /*? XClearArea(dpy,win,x,y,w,h,False);*/
  XFillRectangle(dpy,win,background[set],x,y,w,h);
  XDrawLine(dpy,win,topgc,x,y,x+w-1,y);
  XDrawLine(dpy,win,topgc,x,y+1,x+w-2,y+1);

  XDrawLine(dpy,win,topgc,x,y,x,y+h-1);
  XDrawLine(dpy,win,topgc,x+1,y,x+1,y+h-2);
  
  XDrawLine(dpy,win,bottomgc,x,y+h,x+w,y+h);
  XDrawLine(dpy,win,bottomgc,x+1,y+h-1,x+w,y+h-1);

  XDrawLine(dpy,win,bottomgc,x+w,y+h,x+w,y);
  XDrawLine(dpy,win,bottomgc,x+w-1,y+h,x+w-1,y+1);

  newx = 2;

  if ((button->p.picture != 0)/* &&
      (w + button->p.width + w3p + 3 > MIN_BUTTON_SIZE)*/) {

    gcm = GCClipMask|GCClipXOrigin|GCClipYOrigin;
    gcv.clip_mask = button->p.mask;
    gcv.clip_x_origin = x + 3;
    gcv.clip_y_origin = y + ((h-button->p.height) >> 1);
    XChangeGC(dpy, hilite[set], gcm, &gcv);
    XCopyArea(dpy, button->p.picture, win, hilite[set], 0, 0,
                   button->p.width, button->p.height,
                   gcv.clip_x_origin, gcv.clip_y_origin);
    gcm = GCClipMask;
    gcv.clip_mask = None;
    XChangeGC(dpy, hilite[set], gcm, &gcv);

    newx += button->p.width+4;
    }
	else
	{
		newx = 4;
	}

  string=button->title;
  if (LeftJustify) {
/* newx set by pixmap    newx=4;
*/
  } else {
/* set by pixmap    newx=max((w-button->tw)/2,4);
*/
    if (TruncateLeft && (w-button->tw)/2 < 4) {
      if (button->truncatewidth == w)
	string=button->truncate_title;
      else {
	string=button->title;
	while(*string && (w-XTextWidth(ButtonFont,string,strlen(string)))/2 < 4)
	    string++;
	button->truncatewidth = w;
	button->truncate_title=string;
      }
    }
  }
  XDrawString(dpy,win,graph[set],x+newx,y+3+ButtonFont->ascent,string,strlen(string));
  button->needsupdate=0;
}

/******************************************************************************
  DrawButtonArray - Draw the whole array (all=1), or only those that need.
******************************************************************************/
void DrawButtonArray(ButtonArray *barray, int all)
{
  Button *btn;
  int i = 0;		/* buttons displayed */

  for(btn = barray->head; btn != NULL; btn = btn->next) 
  {
    if((!ShowCurrentDesk) || ( btn->desk == CurrentDesk ) )
    {
      if (btn->needsupdate || all)
      {
        DoButton
        (
  		btn,barray->x,
  		barray->y+(i*(barray->h+1)),
  		barray->w,barray->h
    	);
      }
      i++;
    }
  }
}

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

/* -------------------------------------------------------------------------
   RadioButton - Enable button i and verify all others are disabled
   ------------------------------------------------------------------------- */
void RadioButton(ButtonArray *array, int butnum)
{
  Button *temp;
  int i;
  
  for(temp=array->head,i=0; temp!=NULL; temp=temp->next,i++)
  {
    if (i == butnum)
    {
      temp->up = 0;
      temp->needsupdate=1;
    }
    else
    {
      if (temp->up == 0)
      {
        temp->up = 1;
        temp->needsupdate = 1;
      }
    }
  }
}

/******************************************************************************
  WhichButton - Based on x,y which button was pressed
******************************************************************************/
int WhichButton(ButtonArray *array,int x, int y)
{
  int num;

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

  /* Current Desk Hack */

  if(ShowCurrentDesk)
  {
    Button *temp;
    int i, n;

    temp=array->head;
    for(i=0, n = 0;n < (num + 1) && temp != NULL;temp=temp->next, i++)
    {
       if(temp->desk == CurrentDesk)
         n++;
    }
    num = i-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;
}
