/* FvwmIconBox Module --- Copyright 1994, 1995 Nobutaka Suzuki. 
 * 
 * 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. 
 *
 * The functions based on part of GoodStuff, FvwmScroll, FvwmWinList
 * and Fvwm are noted by a small copyright atop that function.
 * Full original copyright is described in COPYRIGHT.
 */

#define TRUE 1
#define FALSE 0

#define NONE 0
#define VERTICAL 1
#define HORIZONTAL 2

#include "../../configure.h"

#ifdef ISC
#include <sys/bsdtypes.h> /* Saul */
#endif

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#if defined ___AIX || defined _AIX || defined __QNX__ || defined ___AIXV3 || defined AIXV3 || defined _SEQUENT_
#include <sys/select.h>
#endif
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include "../../fvwm/module.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif /* SHAPE */

/* just as same as wild.c */
#ifndef TRUE                    
#define TRUE    1
#define FALSE   0
#endif

#include "FvwmIconBox.h"
#include "../../version.h"

char *MyName;

XFontStruct *font;

Display *dpy;			/* which display are we talking to */
int x_fd,fd_width;

Window Root;
int screen;
int d_depth;

char *Back = "#5f9ea0";
char *Fore = "#82dfe3";
char *IconBack = "#cfcfcf";
char *IconFore = "black";
char *ActIconBack = "white";
char *ActIconFore = "black";
char *font_string = "fixed";

/* same strings as in misc.c */
char NoClass[] = "NoClass"; 
char NoResource[] = "NoResource"; 

Pixel fore_pix, hilite_pix, back_pix, shadow_pix;
Pixel icon_fore_pix, icon_back_pix, icon_hilite_pix, icon_shadow_pix;
Pixel act_icon_fore_pix, act_icon_back_pix, act_icon_hilite_pix, act_icon_shadow_pix; 

GC  NormalGC,ShadowGC,ReliefGC,IconShadowGC,IconReliefGC;
Window main_win;
Window holder_win;
Window icon_win;
Window h_scroll_bar;
Window v_scroll_bar;
Window l_button, r_button, t_button, b_button;
Window Pressed = None;

long CurrentDesk;

int Width, Height;
int UWidth, UHeight;

#define MW_EVENTS   (KeyPressMask| ExposureMask | StructureNotifyMask|\
		     ButtonReleaseMask | ButtonPressMask )
#define SCROLL_EVENTS (ExposureMask | StructureNotifyMask|\
		       ButtonReleaseMask |ButtonPressMask | PointerMotionMask)
#define BUTTON_EVENTS (ExposureMask | StructureNotifyMask|\
		       ButtonReleaseMask | ButtonPressMask |\
		       LeaveWindowMask | PointerMotionMask) 

unsigned long m_mask = M_CONFIGURE_WINDOW|M_ADD_WINDOW|M_DESTROY_WINDOW|
		 M_END_WINDOWLIST| M_ICONIFY|M_DEICONIFY|M_ICON_NAME|
		 M_RES_NAME|M_RES_CLASS|M_WINDOW_NAME|M_ICON_FILE|
		 M_DEFAULTICON|M_CONFIG_INFO|M_END_CONFIG_INFO;

struct icon_info *Hilite;
int main_width, main_height;
int num_icons = 0;
int num_rows = 1;
int num_columns = 6;
int Lines = 6;
int max_icon_width = 48,max_icon_height = 48;
int ButtonWidth,ButtonHeight;
int x= -100000,y= -100000,w= -1,h= -1,gravity = NorthWestGravity;
int icon_win_x = 0, icon_win_y = 0, icon_win_width = 100,
icon_win_height = 100;
int interval = 8;
int motion = NONE;
int primary = LEFT, secondary = BOTTOM;
int xneg = 0, yneg = 0;
int ClickTime = 150;
unsigned int bar_width = 9;
int redraw_flag = 3;
int icon_relief = 4;
int margin1 = 8;
int margin2 = 6;

Pixmap IconwinPixmap = None;
char *IconwinPixmapFile = NULL;

int h_margin;
int v_margin;

int fd[2];

struct icon_info *Head = NULL;
struct icon_info *Tail = NULL;
struct iconfile *IconListHead = NULL;
struct iconfile *IconListTail = NULL;
struct iconfile *DefaultIcon = NULL;
struct mousefunc *MouseActions = NULL;
struct keyfunc *KeyActions = NULL;
char *iconPath = NULL;
char *pixmapPath = NULL;
char *FvwmDefaultIcon = NULL;

static Atom wm_del_win;
Atom _XA_WM_PROTOCOLS;
Atom _XA_WM_NAME;

int ready = 0;
unsigned long local_flags = 0;
int sortby = UNSORT;

/************************************************************************
  Main 
  Based on main() from GoodStuff:
  	Copyright 1993, Robert Nation.
************************************************************************/
void main(int argc, char **argv)
{
  char *display_name = NULL;
  char *temp, *s;
  XIconSize* size;

  temp = argv[0];

  s=strrchr(argv[0], '/');
  if (s != NULL)
    temp = s + 1;


  MyName = safemalloc(strlen(temp)+1);
  strcpy(MyName, temp);

  signal (SIGPIPE, DeadPipe);  
  
  if((argc != 6)&&(argc != 7))
    {
      fprintf(stderr,"%s Version %s should only be executed by fvwm!\n",MyName,
	      VERSION);
      exit(1);
    }

  fd[0] = atoi(argv[1]);
  fd[1] = atoi(argv[2]);
  
  if (!(dpy = XOpenDisplay(display_name))) 
    {
      fprintf(stderr,"%s: can't open display %s", MyName,
	      XDisplayName(display_name));
      exit (1);
    }
  x_fd = XConnectionNumber(dpy);

  fd_width = GetFdWidth();

  screen= DefaultScreen(dpy);
  Root = RootWindow(dpy, screen);
  if(Root == None) 
    {
      fprintf(stderr,"%s: Screen %d is not valid ", MyName, screen);
      exit(1);
    }
  d_depth = DefaultDepth(dpy, screen);

  XSetErrorHandler((XErrorHandler)myErrorHandler);

  ParseOptions();
  
  SetMessageMask(fd, m_mask);   

  if ((local_flags & SETWMICONSIZE) && (size = XAllocIconSize()) != NULL){
    size->max_width  = size->min_width  = max_icon_width + icon_relief;
    size->max_height = size->min_height = max_icon_height + icon_relief;
    size->width_inc  = size->height_inc = 0;
    XSetIconSizes(dpy, Root, size, 1);
    XFree(size);
  }

  CreateWindow();

  SendFvwmPipe(fd,"Send_WindowList",0);

  Loop();
}

/************************************************************************
  Loop
  Based on Loop() from GoodStuff:
      Copyright 1993, Robert Nation.
************************************************************************/
void Loop(void)
{
  Window root;
  struct icon_info *tmp, *exhilite;
  int x,y,border_width,depth;
  int i, hr = icon_relief/2;
  XEvent Event;
  int tw,th;
  int diffx, diffy;
  int oldw, oldh;

  while(1)
    {
      if(My_XNextEvent(dpy,&Event))
	{
	  switch(Event.type)
	    {
	    case Expose:
	      if(Event.xexpose.count == 0){
		if (Event.xany.window == main_win){
		  RedrawWindow();
		}else{
		  tmp = Head;
		  while(tmp != NULL){
		    if (Event.xany.window == tmp->icon_pixmap_w){
		      RedrawIcon(tmp, 1);
		      break;
		    }else if(Event.xany.window == tmp->IconWin){
		      RedrawIcon(tmp, 2);
		      break;
		    }
		    tmp = tmp->next;
		  }
		}
	      }
	      break;
	      
	    case ConfigureNotify:
	      XGetGeometry(dpy,main_win,&root,&x,&y,
			   (unsigned int *)&tw,(unsigned int *)&th,
			   (unsigned int *)&border_width,
			   (unsigned int *)&depth);
	      if (ready && (tw != main_width || th != main_height)){
		main_width = tw; 
		main_height= th;
		oldw = Width;
		oldh = Height;
		num_columns = (tw - h_margin - interval + 1) / UWidth;	 
		num_rows = (th - v_margin - interval + 1) / UHeight;
		Width = UWidth * num_columns + interval - 1; 
		Height = UHeight * num_rows + interval -1; 
		XMoveResizeWindow(dpy, holder_win, margin1+2,
				  margin1+2,   
				  tw - h_margin, th - v_margin);
		if (!(local_flags & HIDE_H))
		  XResizeWindow(dpy, h_scroll_bar, 
				Width - bar_width*2, bar_width);
		if (!(local_flags & HIDE_V))
		  XResizeWindow(dpy ,v_scroll_bar, 
				bar_width, Height - bar_width*2);
		GetIconwinSize(&diffx, &diffy);
		if (primary == BOTTOM || secondary == BOTTOM)
		  icon_win_y -= Height - oldh;
		if (primary == RIGHT || secondary == RIGHT)
		  icon_win_x -= Width - oldw;
		if (icon_win_x < 0)
		  icon_win_x = 0;
		if (icon_win_y < 0)
		  icon_win_y = 0;
		if (icon_win_x + Width > icon_win_width)
		  icon_win_x = icon_win_width - Width;
		if (icon_win_y + Height > icon_win_height)
		  icon_win_y = icon_win_height - Height;
		XMoveResizeWindow(dpy, icon_win, -icon_win_x,
				  -icon_win_y, icon_win_width,
				  icon_win_height);   
		AdjustIconWindows();
		XClearWindow(dpy,main_win);
		RedrawWindow();
	      }
	      break;
	    case KeyPress:
	      ExecuteKey(Event);
	      break;
	    case ButtonPress:
	      if (!(local_flags & HIDE_H)){
		if (Event.xbutton.window == h_scroll_bar)
		  motion = HORIZONTAL;
		else if (Event.xbutton.window == l_button){
		  Pressed = l_button;
		  RedrawLeftButton(ShadowGC, ReliefGC);
		}
		else if (Event.xbutton.window == r_button){
		  Pressed = r_button;
		  RedrawRightButton(ShadowGC, ReliefGC);
		}
	      }
	      if (!(local_flags & HIDE_V)){
		if (Event.xbutton.window == v_scroll_bar)
		  motion = VERTICAL;
		else if (Event.xbutton.window == t_button){
		  Pressed = t_button;
		  RedrawTopButton(ShadowGC, ReliefGC);
		}
		else if (Event.xbutton.window == b_button){
		  Pressed = b_button;
		  RedrawBottomButton(ShadowGC, ReliefGC);
		}
	      }
	      if ((tmp = Search(Event.xbutton.window)) != NULL)
		ExecuteAction(Event.xbutton.x, Event.xbutton.y, tmp);
	      break;

	    case ButtonRelease:
	      if (!(local_flags & HIDE_H)){
		if (Event.xbutton.window == h_scroll_bar && motion ==
		    HORIZONTAL)
		  HScroll(Event.xbutton.x * icon_win_width / Width);
		else if (Event.xbutton.window == l_button && Pressed ==
			 l_button){ 
		  Pressed = None;
		  RedrawLeftButton(ReliefGC, ShadowGC);
		  HScroll(icon_win_x - UWidth); 
		}
		else if (Event.xbutton.window == r_button && Pressed ==
			 r_button){ 
		  Pressed = None;
		  RedrawRightButton(ReliefGC, ShadowGC);
		  HScroll(icon_win_x + UWidth);
		}
	      }
	      if (!(local_flags & HIDE_V)){
		if (Event.xbutton.window == v_scroll_bar && motion
		    == VERTICAL)
		  VScroll(Event.xbutton.y * icon_win_height / Height);
		else if (Event.xbutton.window == t_button && Pressed ==
			 t_button){ 
		  Pressed = None;
		  RedrawTopButton(ReliefGC, ShadowGC);
		  VScroll(icon_win_y - UHeight);
		}
		else if (Event.xbutton.window == b_button && Pressed ==
			 b_button){ 
		  Pressed = None;
		  RedrawBottomButton(ReliefGC, ShadowGC);
		  VScroll(icon_win_y + UHeight);
		}
	      }
	      motion = NONE;
	      break;

	    case MotionNotify:
	      if (motion == VERTICAL){
		VScroll(Event.xbutton.y * icon_win_height / Height);
	      }else if (motion == HORIZONTAL){
		HScroll(Event.xbutton.x * icon_win_width / Width);
	      }
	      break;
	    case EnterNotify:
	      if ((tmp = Search(Event.xcrossing.window)) != NULL)
		if ((exhilite = Hilite) != tmp){
		  Hilite = tmp;
		  if (exhilite != NULL)
		    RedrawIcon(exhilite, redraw_flag);
		  RedrawIcon(tmp, redraw_flag);
		}
	      break;

	    case LeaveNotify:
	      if ((tmp = Search(Event.xcrossing.window)) != NULL &&
		  tmp == Hilite){
		Hilite = NULL;
		RedrawIcon(tmp, redraw_flag);
	      }
	      if (!(local_flags & HIDE_H) && Event.xbutton.window ==
		  l_button && Pressed == l_button){ 
		Pressed = None;
		RedrawLeftButton(ReliefGC, ShadowGC);
	      }
	      else if (!(local_flags & HIDE_H) && Event.xbutton.window ==
		       r_button && Pressed == r_button){ 
		Pressed = None;
		RedrawRightButton(ReliefGC, ShadowGC);
	      }
	      else if (!(local_flags & HIDE_V) && Event.xbutton.window ==
		       t_button && Pressed == t_button){ 
		Pressed = None;
		RedrawTopButton(ReliefGC, ShadowGC);
	      }
	      else if (!(local_flags & HIDE_V) && Event.xbutton.window ==
		       b_button && Pressed == b_button){ 
		Pressed = None;
		RedrawBottomButton(ReliefGC, ShadowGC);
	      }
	      break;
	    case ClientMessage:
	      if ((Event.xclient.format==32) && 
		  (Event.xclient.data.l[0]==wm_del_win))
		DeadPipe(1);
	      break;
	    case PropertyNotify:
	      switch (Event.xproperty.atom){
	      case XA_WM_HINTS:
		if (Event.xproperty.state == PropertyDelete)
		  break;
		tmp = Head; 
		i=0;
		while(tmp != NULL){
		  if (Event.xproperty.window == tmp->id)
		    break;
		  tmp = tmp->next;
		  ++i;
		}
		if (tmp == NULL || tmp->wmhints == NULL || !(tmp->extra_flags & DEFAULTICON)) 
		  break;
		if (tmp->wmhints)
		  XFree (tmp->wmhints);
		tmp->wmhints = XGetWMHints(dpy, tmp->id);
		if (tmp->wmhints && (tmp->wmhints->flags & IconPixmapHint)){
#ifdef SHAPE
		/* turn off "old" shape mask */
		if (tmp->icon_maskPixmap != None)
		  XShapeCombineMask(dpy, tmp->icon_pixmap_w,
				    ShapeBounding, 0, 0, None, ShapeSet);
#endif
		  if (tmp->iconPixmap != None)
		    XFreePixmap(dpy, tmp->iconPixmap);
		  GetIconBitmap(tmp);
#ifdef SHAPE
		  if (tmp->icon_maskPixmap != None)
		    XShapeCombineMask(dpy, tmp->icon_pixmap_w,
				      ShapeBounding, hr, hr, 
				      tmp->icon_maskPixmap, ShapeSet);
#endif
		  AdjustIconWindow(tmp, i);
		  if (max_icon_height != 0)
		  RedrawIcon(tmp, 1);
		}
		break;
	      }
	      break;
	      
	    default:
	      break;
	    }
	}
    }
  return;
}

void HScroll(int x)
{
  int oldx = icon_win_x;

  if (x + Width > icon_win_width)
    x = icon_win_width - Width;
  if (x < 0)
    x = 0;
  if (oldx != x){
    icon_win_x = x;
    XMoveWindow(dpy,icon_win, -icon_win_x, -icon_win_y);
    if (!(local_flags & HIDE_H))
      RedrawHScrollbar();
  }
}

void VScroll(int y)
{
  int oldy = icon_win_y;

  if (y + Height > icon_win_height)
    y = icon_win_height - Height;
  if (y < 0)
    y = 0;
  if (oldy != y){
    icon_win_y = y;
    XMoveWindow(dpy,icon_win, -icon_win_x, -icon_win_y);
    if (!(local_flags & HIDE_V))
      RedrawVScrollbar();
  }
}

struct icon_info *Search(Window w)
{
  struct icon_info *tmp;

  tmp = Head;
  while (tmp != NULL){
    if (tmp->IconWin == w || tmp->icon_pixmap_w == w)
      return tmp;
    tmp = tmp->next;
  }
  return NULL;
}

/************************************************************************
 *
 * Draw the window 
 *
 ***********************************************************************/
void RedrawWindow(void)
{
  XEvent dummy;

  while (XCheckTypedWindowEvent (dpy, main_win, Expose, &dummy));

  RelieveWindow(main_win, margin1, margin1, Width + 4, 
		Height + 4, ShadowGC,ReliefGC);   
  if (!(local_flags & HIDE_H))
    RelieveWindow(main_win, margin1, margin1 + 4 + Height + margin2, 
		  Width + 4, bar_width+4, ShadowGC,ReliefGC);   
  if (!(local_flags & HIDE_V))
    RelieveWindow(main_win, margin1 + 4 + Width + margin2, margin1, 
		  bar_width+4, Height + 4, ShadowGC,ReliefGC);
  RelieveWindow(main_win, 0, 0, Width + h_margin, Height + v_margin, 
		ReliefGC, ShadowGC);   

  /* scroll bar */
  if (!(local_flags & HIDE_H))
    RedrawHScrollbar();
  if (!(local_flags & HIDE_V))
    RedrawVScrollbar();

  /* buttons */
  if (!(local_flags & HIDE_H)){
    RedrawLeftButton(ReliefGC, ShadowGC);
    RedrawRightButton(ReliefGC, ShadowGC);
  }
  if (!(local_flags & HIDE_V)){
    RedrawTopButton(ReliefGC, ShadowGC);
    RedrawBottomButton(ReliefGC, ShadowGC);
  }

  /* icons */
  RedrawIcons();
}

void RedrawIcons(void)
{
  struct icon_info *tmp;

  tmp = Head;
  while(tmp != NULL){
    if (desk_cond(tmp))
      RedrawIcon(tmp, redraw_flag);
    tmp = tmp->next;
  }
}

void RedrawIcon(struct icon_info *item, int f)
{
  unsigned long plane = 1;
  int hr, len;
  int diff, lm ,w, h, tw;
  char label[256];

  hr = icon_relief/2;

  if (Hilite == item){	
    XSetForeground(dpy, NormalGC, act_icon_fore_pix);
    XSetBackground(dpy, NormalGC, act_icon_back_pix);
    XSetForeground(dpy, IconReliefGC, act_icon_hilite_pix);
    XSetForeground(dpy, IconShadowGC, act_icon_shadow_pix);

    if (max_icon_height != 0 && (item->flags & ICON_OURS))
      XSetWindowBackground(dpy, item->icon_pixmap_w, act_icon_back_pix);
    XSetWindowBackground(dpy, item->IconWin, act_icon_back_pix);
  }

  /* icon pixmap */
  if ((f & 1) && (item->flags & ICON_OURS)){
    if (item->iconPixmap != None && item->icon_pixmap_w != None){
      if (item->icon_depth != d_depth)
	XCopyPlane(dpy, item->iconPixmap, item->icon_pixmap_w, NormalGC,
		   0, 0, item->icon_w, item->icon_h, 
		   hr, hr, plane);
      else 
	XCopyArea(dpy, item->iconPixmap, item->icon_pixmap_w, NormalGC,
		  0, 0, item->icon_w, item->icon_h, hr, hr);
      }
    if (!(item->flags & SHAPED_ICON)){
      if (item->icon_w > 0 && item->icon_h > 0)
	RelieveWindow(item->icon_pixmap_w, 0, 0, item->icon_w
		      +icon_relief,  
		      item->icon_h + icon_relief, IconReliefGC,
		      IconShadowGC);   
      else 
	RelieveWindow(item->icon_pixmap_w, 0, 0, max_icon_width
		    +icon_relief,  
		    max_icon_height + icon_relief, IconReliefGC,
		    IconShadowGC);   
    }
  }
  
  /* label */
  if (f & 2){
    w = max_icon_width + icon_relief;
    h = max_icon_height + icon_relief;
    
    if (item->flags & ICONIFIED){
      sprintf(label, "(%s)", item->name);
    }else
      strcpy(label, item->name);
    
    len = strlen(label);
    tw = XTextWidth(font, label, len);
    diff = max_icon_width + icon_relief - tw;
    lm = diff/2;
    lm = lm > 4 ? lm : 4;

    if (Hilite == item){	
      XRaiseWindow(dpy, item->IconWin);
      XMoveResizeWindow(dpy, item->IconWin, 
			item->x + min(0, (diff - 8))/2, 
			item->y + h,
			max(tw + 8, w), 6 + font->ascent +
			font->descent); 
      XClearWindow(dpy, item->IconWin);
      XDrawString(dpy, item->IconWin, NormalGC, lm, 3 + font->ascent,
		  label, len); 
      RelieveWindow(item->IconWin, 0, 0,
		    max(tw + 8, w), 6 + font->ascent +
		    font->descent, IconReliefGC, IconShadowGC);  
    }else{
      XMoveResizeWindow(dpy, item->IconWin, item->x, item->y + h,
			w, 6 + font->ascent + font->descent);
      XClearWindow(dpy, item->IconWin);
      XDrawString(dpy, item->IconWin, NormalGC, lm, 3 + font->ascent,
		  label, len); 
      RelieveWindow(item->IconWin, 0, 0,
		    w, 6 + font->ascent + font->descent, 
		    IconReliefGC, IconShadowGC);  
    }
  }
  
  if (Hilite == item){	
    XSetForeground(dpy, NormalGC, icon_fore_pix);
    XSetBackground(dpy, NormalGC, icon_back_pix);
    XSetForeground(dpy, IconReliefGC, icon_hilite_pix);
    XSetForeground(dpy, IconShadowGC, icon_shadow_pix);

    if (max_icon_height != 0 && (item->flags & ICON_OURS))
      XSetWindowBackground(dpy, item->icon_pixmap_w, icon_back_pix);
    XSetWindowBackground(dpy, item->IconWin, icon_back_pix);
  }
}

/***********************************************************************
 * RedrawHScrollbar
 * 	Based on part of Loop() of GrabWindow.c in FvwmScroll: 
 *  		Copyright 1994, Robert Nation.
 ***********************************************************************/
void RedrawHScrollbar(void)
{
  int x,width;
  
  x = (Width - bar_width*2) * icon_win_x / icon_win_width;
  width = (Width - bar_width*2) * Width / icon_win_width;
  XClearArea(dpy, h_scroll_bar, 0, 0, Width, bar_width,False);
  RelieveWindow(h_scroll_bar, x, 0, width, bar_width, ReliefGC, ShadowGC);   
}

/***********************************************************************
 * RedrawVScrollbar
 * 	Based on part of Loop() of GrabWindow.c in FvwmScroll:
 *  		Copyright 1994, Robert Nation.
 ***********************************************************************/
void RedrawVScrollbar(void)
{
  int y, height;

  y = (Height - bar_width*2) * icon_win_y / icon_win_height;
  height = (Height - bar_width*2)*  Height / icon_win_height;
  XClearArea(dpy, v_scroll_bar, 0, 0, bar_width, Height,False);
  RelieveWindow(v_scroll_bar, 0, y, bar_width, height, ReliefGC, ShadowGC);   
}
  
void RedrawLeftButton(GC rgc, GC sgc)
{
  XSegment seg[4];  
  int i=0;
  
  seg[i].x1 = 1;		seg[i].y1 = bar_width/2;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = 1;

  seg[i].x1 = 0;		seg[i].y1 = bar_width/2;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = 0;
  XDrawSegments(dpy, l_button, rgc, seg, i);

  i = 0;
  seg[i].x1 = 1;		seg[i].y1 = bar_width/2;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = bar_width - 2;

  seg[i].x1 = 0;		seg[i].y1 = bar_width/2;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = bar_width - 1;

  seg[i].x1 = bar_width - 2;	seg[i].y1 = 1;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = bar_width - 2;

  seg[i].x1 = bar_width - 1;	seg[i].y1 = 0;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = bar_width - 1;
  XDrawSegments(dpy, l_button, sgc, seg, i);
}

void RedrawRightButton(GC rgc, GC sgc)
{
  XSegment seg[4];  
  int i=0;

  seg[i].x1 = 1;		seg[i].y1 = 1;
  seg[i].x2 = 1;		seg[i++].y2 = bar_width - 2;

  seg[i].x1 = 0;		seg[i].y1 = 0;
  seg[i].x2 = 0;		seg[i++].y2 = bar_width - 1;

  seg[i].x1 = 1;		seg[i].y1 = 1;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = bar_width/2;

  seg[i].x1 = 0;		seg[i].y1 = 0;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = bar_width/2;

  XDrawSegments(dpy, r_button, rgc, seg, i);

  i = 0;
  seg[i].x1 = 1;		seg[i].y1 = bar_width - 2;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = bar_width/2;

  seg[i].x1 = 0;		seg[i].y1 = bar_width - 1;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = bar_width/2;
  XDrawSegments(dpy, r_button, sgc, seg, i);
}

void RedrawTopButton(GC rgc, GC sgc)
{
  XSegment seg[4];  
  int i=0;

  seg[i].x1 = bar_width/2;	seg[i].y1 = 1;
  seg[i].x2 = 1;		seg[i++].y2 = bar_width - 2;

  seg[i].x1 = bar_width/2;	seg[i].y1 = 0;
  seg[i].x2 = 0;		seg[i++].y2 = bar_width - 1;
  XDrawSegments(dpy, t_button, rgc, seg, i);

  i = 0;
  seg[i].x1 = bar_width/2;	seg[i].y1 = 1;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = bar_width - 2;

  seg[i].x1 = bar_width/2;	seg[i].y1 = 0;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = bar_width - 1;

  seg[i].x1 = 1;		seg[i].y1 = bar_width - 2;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = bar_width - 2;

  seg[i].x1 = 0;		seg[i].y1 = bar_width - 1;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = bar_width - 1;
  XDrawSegments(dpy, t_button, sgc, seg, i);
}

void RedrawBottomButton(GC rgc, GC sgc)
{
  XSegment seg[4];  
  int i=0;

  seg[i].x1 = 1;		seg[i].y1 = 1;
  seg[i].x2 = bar_width/2;	seg[i++].y2 = bar_width - 2;

  seg[i].x1 = 0;		seg[i].y1 = 0;
  seg[i].x2 = bar_width/2;	seg[i++].y2 = bar_width - 1;

  seg[i].x1 = 1;		seg[i].y1 = 1;
  seg[i].x2 = bar_width - 2;	seg[i++].y2 = 1;

  seg[i].x1 = 0;		seg[i].y1 = 0;
  seg[i].x2 = bar_width - 1;	seg[i++].y2 = 0;
  XDrawSegments(dpy, b_button, rgc, seg, i);

  i = 0;
  seg[i].x1 = bar_width - 2;	seg[i].y1 = 1;
  seg[i].x2 = bar_width/2;	seg[i++].y2 = bar_width - 2;

  seg[i].x1 = bar_width - 1;	seg[i].y1 = 0;
  seg[i].x2 = bar_width/2;	seg[i++].y2 = bar_width - 1;
  XDrawSegments(dpy, b_button, sgc, seg, i);
}
  
/************************************************************************
  RelieveWindow
    Original work from GoodStuff:
      Copyright 1993, Robert Nation.
************************************************************************/
FVWM_INLINE void RelieveWindow(Window win,int x,int y,int w,int h,
		   GC rgc,GC sgc)
{
  XSegment seg[4];
  int i;

  i=0;
  seg[i].x1 = x;        seg[i].y1   = y;
  seg[i].x2 = w+x-1;    seg[i++].y2 = y;

  seg[i].x1 = x;        seg[i].y1   = y;
  seg[i].x2 = x;        seg[i++].y2 = h+y-1;

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

  seg[i].x1 = x+1;      seg[i].y1   = y+1;
  seg[i].x2 = x+1;      seg[i++].y2 = y+h-2;
  XDrawSegments(dpy, win, rgc, seg, i);

  i=0;
  seg[i].x1 = x;        seg[i].y1   = y+h-1;
  seg[i].x2 = w+x-1;    seg[i++].y2 = y+h-1;

  seg[i].x1 = x+w-1;    seg[i].y1   = y;
  seg[i].x2 = x+w-1;    seg[i++].y2 = y+h-1;
  if(d_depth<2)
    XDrawSegments(dpy, win, ShadowGC, seg, i);
  else
    XDrawSegments(dpy, win, sgc, seg, i);

  i=0;
  seg[i].x1 = x+1;      seg[i].y1   = y+h-2;
  seg[i].x2 = x+w-2;    seg[i++].y2 = y+h-2;

  seg[i].x1 = x+w-2;    seg[i].y1   = y+1;
  seg[i].x2 = x+w-2;    seg[i++].y2 = y+h-2;

  XDrawSegments(dpy, win, sgc, seg, i);
}

/************************************************************************
 * CreateWindow --Sizes and creates the window 
 * 	Based on CreateWindow() from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
void CreateWindow(void)
{
  XGCValues gcv;
  unsigned long gcm;
  unsigned long mask;
  char *list[2];
  XSetWindowAttributes attributes;
  XSizeHints mysizehints;
  XTextProperty name;
  XClassHint class_hints;

  h_margin = margin1*2 + bar_width + margin2 + 8;
  v_margin = margin1*2 + bar_width + margin2 + 8;

  wm_del_win = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
  _XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);

  /* load the font */
  if ((font = XLoadQueryFont(dpy, font_string)) == NULL)
    {
      if ((font = XLoadQueryFont(dpy, "fixed")) == NULL)
	{
	  fprintf(stderr,"%s: No fonts available\n",MyName);
	  exit(1);
	}
    };

  if ((local_flags & HIDE_H))
    v_margin -= bar_width + margin2 + 4;
  if ((local_flags & HIDE_V))
    h_margin -= bar_width + margin2 + 4;

  UWidth = max_icon_width + icon_relief + interval;
  UHeight = font->ascent + font->descent + max_icon_height +
    icon_relief + 6 + interval;
  Width = UWidth * num_columns + interval -1;
  Height = UHeight * num_rows + interval -1;

  mysizehints.flags = PWinGravity| PResizeInc | PMinSize;

  /* subtract one for the right/bottom border */
  mysizehints.min_width = UWidth + interval - 1 + h_margin; 
  main_width = mysizehints.width = Width + h_margin;  
  mysizehints.min_height = UHeight + interval - 1 + v_margin;
  main_height = mysizehints.height = Height + v_margin;  
  mysizehints.width_inc = UWidth;
  mysizehints.height_inc = UHeight;

  if(x > -100000)
    {
      if (xneg)
	{
	  mysizehints.x = DisplayWidth(dpy,screen) + x - mysizehints.width-2;
	  gravity = NorthEastGravity;
	}
      else 
	mysizehints.x = x;
      if (yneg)
	{
	  mysizehints.y = DisplayHeight(dpy,screen) + y - mysizehints.height-2;
	  gravity = SouthWestGravity;
	}
      else 
	mysizehints.y = y;

      if((xneg) && (yneg))
	gravity = SouthEastGravity;	

      mysizehints.flags |= USPosition;
    }

  mysizehints.win_gravity = gravity;
#define BW 1
  if(d_depth < 2)
    {
      back_pix = icon_back_pix = act_icon_fore_pix = GetColor("white");
      fore_pix = icon_fore_pix = act_icon_back_pix = GetColor("black");
      hilite_pix = icon_hilite_pix = act_icon_shadow_pix = icon_back_pix;
      shadow_pix = icon_shadow_pix = act_icon_hilite_pix = icon_fore_pix;
    }
  else
    {
      fore_pix = GetColor(Fore);
      back_pix = GetColor(Back);
      icon_back_pix = GetColor(IconBack);
      icon_fore_pix = GetColor(IconFore);
      icon_hilite_pix = GetHilite(icon_back_pix);
      icon_shadow_pix = GetShadow(icon_back_pix);
      act_icon_back_pix = GetColor(ActIconBack);
      act_icon_fore_pix = GetColor(ActIconFore);
      act_icon_hilite_pix = GetHilite(act_icon_back_pix);
      act_icon_shadow_pix = GetShadow(act_icon_back_pix);
      hilite_pix = GetHilite(back_pix);
      shadow_pix = GetShadow(back_pix);
    }

  main_win = XCreateSimpleWindow(dpy,Root,mysizehints.x,mysizehints.y,
				 mysizehints.width,mysizehints.height,  
				 BW,fore_pix,back_pix);
  XSetWMProtocols(dpy,main_win,&wm_del_win,1);
  XSelectInput(dpy,main_win,MW_EVENTS);

  /* set normal_hits, wm_hints, and, class_hints */
  list[0]=MyName;
  list[1]=NULL;
  if (XStringListToTextProperty(list,1,&name) == 0) 
    {
      fprintf(stderr,"%s: cannot allocate window name",MyName);
      return;
    }
  class_hints.res_name = MyName;
  class_hints.res_class = "FvwmIconBox";
  XSetWMProperties(dpy,main_win,&name,&name,
		   NULL,0,&mysizehints,NULL,&class_hints);
  XFree(name.value);

  mysizehints.width -= h_margin;
  mysizehints.height -= v_margin;
  holder_win = XCreateSimpleWindow(dpy,main_win,margin1+2,
				   margin1+2,   
				   mysizehints.width,
				   mysizehints.height, 
                                   0,fore_pix,back_pix);

  icon_win = XCreateSimpleWindow(dpy,holder_win,-icon_win_x,-icon_win_y,
                                   icon_win_width,
                                   icon_win_height,
                                   0,fore_pix,back_pix);
  
  gcm = GCForeground|GCBackground;
  gcv.foreground = hilite_pix;
  gcv.background = back_pix;
  ReliefGC = XCreateGC(dpy, Root, gcm, &gcv);  

  gcm = GCForeground|GCBackground;
  gcv.foreground = shadow_pix;
  gcv.background = back_pix;
  ShadowGC = XCreateGC(dpy, Root, gcm, &gcv);  

  gcm = GCForeground|GCBackground;
  gcv.foreground = icon_hilite_pix;
  gcv.background = icon_back_pix;
  IconReliefGC = XCreateGC(dpy, Root, gcm, &gcv);  

  gcm = GCForeground|GCBackground;
  gcv.foreground = icon_shadow_pix;
  gcv.background = icon_back_pix;
  IconShadowGC = XCreateGC(dpy, Root, gcm, &gcv);  

  gcm = GCForeground|GCBackground|GCFont;
  gcv.foreground = fore_pix;
  gcv.background = back_pix;
  gcv.font =  font->fid;
  NormalGC = XCreateGC(dpy, Root, gcm, &gcv);  


  /* icon_win's background */
  if (GetBackPixmap() == True){
    XSetWindowBackgroundPixmap(dpy, icon_win, IconwinPixmap);
    /*  special thanks to Dave Goldberg <dsg@mitre.org> 
	for his helpful information */
    XFreePixmap(dpy, IconwinPixmap);
  }

  XSetForeground(dpy, NormalGC, icon_fore_pix);
  XSetBackground(dpy, NormalGC, icon_back_pix);

  /* scroll bars */
  mask = CWWinGravity | CWBackPixel;
  attributes.background_pixel = back_pix;
  if (!(local_flags & HIDE_H)){
    attributes.win_gravity = SouthWestGravity;
    h_scroll_bar = XCreateWindow(dpy ,main_win, margin1 + 2 +
				 bar_width, 
				 margin1 + 6 + Height + margin2,
				 Width - bar_width*2, bar_width,
				 0, CopyFromParent,
				 InputOutput, CopyFromParent, 
				 mask, &attributes);
  XSelectInput(dpy,h_scroll_bar,SCROLL_EVENTS);
  }
  if (!(local_flags & HIDE_V)){
    attributes.win_gravity = NorthEastGravity;
    v_scroll_bar = XCreateWindow(dpy ,main_win, margin1 + 6 +
				 Width + margin2, 
				 margin1 + 2 + bar_width,
				 bar_width, Height - bar_width*2, 
				 0, CopyFromParent, 
				 InputOutput, CopyFromParent,
				 mask, &attributes);
  XSelectInput(dpy,v_scroll_bar,SCROLL_EVENTS);
  }

  /* buttons */
  if (!(local_flags & HIDE_H)){
    attributes.win_gravity = SouthWestGravity;
    l_button = XCreateWindow(dpy, main_win, margin1 + 2,
			     margin1 + 6 + Height + margin2,
			     bar_width, bar_width,
			     0, CopyFromParent, 
			     InputOutput, CopyFromParent,
			     mask, &attributes);
    attributes.win_gravity = SouthEastGravity;
    r_button = XCreateWindow(dpy, main_win, margin1 + 2 + Width -
			     bar_width, 
			     margin1 + 6 + Height + margin2,
			     bar_width, bar_width,
			     0, CopyFromParent, 
			     InputOutput, CopyFromParent,
			     mask, &attributes);
  XSelectInput(dpy,l_button,BUTTON_EVENTS);
  XSelectInput(dpy,r_button,BUTTON_EVENTS);
  }
  if (!(local_flags & HIDE_V)){
    attributes.win_gravity = NorthEastGravity;
    t_button = XCreateWindow(dpy, main_win, margin1 + 6 +
			     Width + margin2, margin1 + 2,
			     bar_width, bar_width,
			     0, CopyFromParent, 
			     InputOutput, CopyFromParent,
			     mask, &attributes);
    attributes.win_gravity = SouthEastGravity;
    b_button = XCreateWindow(dpy, main_win, margin1 + 6 +
			     Width + margin2,
			     margin1 + 2 + Height - bar_width,
			     bar_width, bar_width,
			     0, CopyFromParent, 
			     InputOutput, CopyFromParent,
			     mask, &attributes);
  XSelectInput(dpy,t_button,BUTTON_EVENTS);
  XSelectInput(dpy,b_button,BUTTON_EVENTS);
  }
}

void GetIconwinSize(int *dx, int *dy)
{
  *dx = icon_win_width;
  *dy = icon_win_height;

  if (primary == LEFT || primary == RIGHT){
    icon_win_width = max(Width, UWidth * Lines + interval - 1); 
    icon_win_height = max(Height, UHeight * (max(0,
						 (num_icons-1))/Lines
					     + 1) - 1 + interval); 
  }else{
    icon_win_width = max(Width, UWidth * (max(0,num_icons-1) / Lines +
					  1) + interval - 1);  
    icon_win_height = max(Height, UHeight * Lines - 1 + interval);  
  }
  *dx = icon_win_width - *dx;
  *dy = icon_win_height - *dy;
}

/************************************************************************
 * GetShadow 
 * 	Original work from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
Pixel GetShadow(Pixel background) 
{
  XColor bg_color;
  XWindowAttributes attributes;
  
  XGetWindowAttributes(dpy,Root,&attributes);
  
  bg_color.pixel = background;
  XQueryColor(dpy,attributes.colormap,&bg_color);
  
  bg_color.red = (unsigned short)((bg_color.red*50)/100);
  bg_color.green = (unsigned short)((bg_color.green*50)/100);
  bg_color.blue = (unsigned short)((bg_color.blue*50)/100);
  
  if(!XAllocColor(dpy,attributes.colormap,&bg_color))
    nocolor("alloc shadow","");
  
  return bg_color.pixel;
}

/************************************************************************
 * GetHilite 
 * 	Original work from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
Pixel GetHilite(Pixel background) 
{
  XColor bg_color, white_p;
  XWindowAttributes attributes;
  
  XGetWindowAttributes(dpy,Root,&attributes);
  
  bg_color.pixel = background;
  XQueryColor(dpy,attributes.colormap,&bg_color);

  white_p.pixel = GetColor("white");
  XQueryColor(dpy,attributes.colormap,&white_p);
  

  bg_color.red = max((white_p.red/5), bg_color.red);
  bg_color.green = max((white_p.green/5), bg_color.green);
  bg_color.blue = max((white_p.blue/5), bg_color.blue);
  
  bg_color.red = min(white_p.red, (bg_color.red*140)/100);
  bg_color.green = min(white_p.green, (bg_color.green*140)/100);
  bg_color.blue = min(white_p.blue, (bg_color.blue*140)/100);
  
  if(!XAllocColor(dpy,attributes.colormap,&bg_color))
    nocolor("alloc hilight","");
  
  return bg_color.pixel;
}


/************************************************************************
 * nocolor
 * 	Original work from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
void nocolor(char *a, char *b)
{
 fprintf(stderr,"%s: can't %s %s\n", MyName, a,b);
}


/************************************************************************
 * GetColor
 * 	Original work from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
Pixel GetColor(char *name)
{
  XColor color;
  XWindowAttributes attributes;

  XGetWindowAttributes(dpy,Root,&attributes);
  color.pixel = 0;
   if (!XParseColor (dpy, attributes.colormap, name, &color)) 
     {
       nocolor("parse",name);
     }
   else if(!XAllocColor (dpy, attributes.colormap, &color)) 
     {
       nocolor("alloc",name);
     }
  return color.pixel;
}

/************************************************************************
  SendFvwmPipe - Send a message back to fvwm 
    Original work from FvwmWinList:
      Copyright 1994, Mike Finger.
************************************************************************/
void SendFvwmPipe(int *fd, char *message,unsigned long window)
{
  int w;
  char *hold,*temp,*temp_msg;
  hold=message;

  while(1) {
    temp=strchr(hold,',');
    if (temp!=NULL) {
      temp_msg=malloc(temp-hold+1);
      strncpy(temp_msg,hold,(temp-hold));
      temp_msg[(temp-hold)]='\0';
      hold=temp+1;
    } else temp_msg=hold;

    if (!ExecIconBoxFunction(temp_msg)){
      write(fd[0],&window, sizeof(unsigned long));

      w=strlen(temp_msg);
      write(fd[0],&w,sizeof(int));
      write(fd[0],temp_msg,w);

      /* keep going */
      w=1;
      write(fd[0],&w,sizeof(int));

    }
    if(temp_msg!=hold) free(temp_msg);
    	else break;
  }
}

Bool ExecIconBoxFunction(char *msg)
{
  if (mystrncasecmp(msg, "Next", 4) == 0){
    Next();
    return True;
  }else if (mystrncasecmp(msg, "Prev", 4) == 0){
    Prev();
    return True;
  }else if (mystrncasecmp(msg, "Left", 4) == 0){
    HScroll(icon_win_x - UWidth); 
    return True;
  }else if (mystrncasecmp(msg, "Right", 5) == 0){
    HScroll(icon_win_x + UWidth);
    return True;
  }else if (mystrncasecmp(msg, "Up", 2) == 0){
    VScroll(icon_win_y - UHeight);
    return True;
  }else if (mystrncasecmp(msg, "Down", 4) == 0){
    VScroll(icon_win_y + UHeight);
    return True;
  }
  return False;
}

void Next(void)
{
  struct icon_info *new, *old;
  int i;

  old = new = Hilite;

  if (new != NULL)
    new = new->next;

  if (new != NULL)
    Hilite = new;
  else
    new = Hilite = Head;

  if (new == NULL)
    return;

  if (old != NULL)
    RedrawIcon(old, redraw_flag);
  if (new != NULL)
    RedrawIcon(new, redraw_flag);

  i=0;
  if (new->x < icon_win_x){
    while (new->x + UWidth * ++i < icon_win_x)
      ;
    HScroll(icon_win_x - UWidth*i); 
  }else if (new->x > icon_win_x + Width){
    while (new->x - UWidth * ++i > icon_win_x + Width)
      ;
    HScroll(icon_win_x + UWidth*i); 
  }

  i=0;
  if (new->y < icon_win_y){
    while (new->y + UHeight * ++i < icon_win_y)
      ;
    VScroll(icon_win_y - UHeight*i);
  }else if (new->y > icon_win_y + Height){
    while (new->y - UHeight * ++i > icon_win_y + Height)
      ;
    VScroll(icon_win_y + UHeight*i);
  }
}

void Prev(void)
{
  struct icon_info *new, *old;
  int i;
  
  old = new = Hilite;

  if (new != NULL)
    new = new->prev;

  if (new != NULL)
    Hilite = new;
  else
    new = Hilite = Tail;

  if (new == NULL)
    return;

  if (old != NULL)
    RedrawIcon(old, redraw_flag);
  if (new != NULL)
    RedrawIcon(new, redraw_flag);

  i=0;
  if (new->x < icon_win_x){
    while (new->x + UWidth * ++i < icon_win_x)
      ;
    HScroll(icon_win_x - UWidth*i); 
  }else if (new->x > icon_win_x + Width){
    while (new->x - UWidth * ++i > icon_win_x + Width)
      ;
    HScroll(icon_win_x + UWidth*i); 
  }

  i=0;
  if (new->y < icon_win_y){
    while (new->y + UHeight * ++i < icon_win_y)
      ;
    VScroll(icon_win_y - UHeight*i);
  }else if (new->y > icon_win_y + Height){
    while (new->y - UHeight * ++i > icon_win_y + Height)
      ;
    VScroll(icon_win_y + UHeight*i);
  }
}

/************************************************************************
 * DeadPipe --Dead pipe handler
 * 	Based on DeadPipe() from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
void DeadPipe(int nonsense)
{
  struct icon_info *tmpi, *tmpi2;
  struct mousefunc *tmpm, *tmpm2;
  struct keyfunc *tmpk, *tmpk2;
  struct iconfile *tmpf, *tmpf2;

  if (FvwmDefaultIcon != NULL)
    free(FvwmDefaultIcon);

  tmpm = MouseActions;
  while(tmpm != NULL){
    tmpm2 = tmpm;
    tmpm = tmpm->next;
    free(tmpm2->action);
    free(tmpm2);
  }

  tmpk = KeyActions;
  while(tmpk != NULL){
    tmpk2 = tmpk;
    tmpk = tmpk->next;
    free(tmpk2->action);
    free(tmpk2->name);
    free(tmpk2);
  }

  tmpf = IconListHead;
  while(tmpf != NULL){
    tmpf2 = tmpf;
    tmpf = tmpf->next;
    free(tmpf2->name);
    free(tmpf2->iconfile);
    free(tmpf2);
  }

  tmpi = Head;
  while(tmpi != NULL){
    tmpi2 = tmpi;
    tmpi = tmpi->next;
    freeitem(tmpi2, 0);
  }
  if ((local_flags & SETWMICONSIZE))
    XDeleteProperty(dpy, Root, XA_WM_ICON_SIZE); 
  XSync(dpy,0);
  exit(0);
}


/************************************************************************
 * ParseOptions
 * 	Based on ParseConfig() from FvwmWinList:
 *		Copyright 1994, Mike Finger.
 ***********************************************************************/
void ParseOptions(void)
{
  char *tline= NULL,*tmp;
  int Clength;

  Clength = strlen(MyName);

  GetConfigLine(fd,&tline);
  
  while(tline != NULL)
    {
      int g_x, g_y, flags;
      unsigned width,height;

      if(strlen(&tline[0])>1){
	if (mystrncasecmp(tline,CatString3("*", MyName,
					  "Geometry"),Clength+9)==0){
	  tmp = &tline[Clength+9];
	  while(((isspace(*tmp))&&(*tmp != '\n'))&&(*tmp != 0))
	    tmp++;
	  tmp[strlen(tmp)-1] = 0;
	  flags = XParseGeometry(tmp,&g_x,&g_y,&width,&height);
	  if (flags & WidthValue) 
	    num_columns = width;
	  if (flags & HeightValue) 
	    num_rows = height;
	  if (flags & XValue)
	    x = g_x;
	  if (flags & YValue) 
	    y = g_y;
	  if (flags & XNegative)
	    xneg = 1;
	  if (flags & YNegative)
	    yneg = 1;
	} else if (mystrncasecmp(tline,CatString3("*", MyName,
						"MaxIconSize"),Clength+12)==0){
	  tmp = &tline[Clength+12];
	  while(((isspace(*tmp))&&(*tmp != '\n'))&&(*tmp != 0))
	    tmp++;
	  tmp[strlen(tmp)-1] = 0;

	  flags = XParseGeometry(tmp,&g_x,&g_y,&width,&height);
	  if (flags & WidthValue) 
	    max_icon_width = width;
	  if (flags & HeightValue) 
	    max_icon_height = height;
	  if (height == 0){
	    icon_relief = 0;
	    redraw_flag = 2;
	    max_icon_width += 4;
	  }
	}else if
	  (mystrncasecmp(tline,CatString3("*",MyName,"Font"),Clength+5)==0)
	  CopyString(&font_string,&tline[Clength+5]);
	else if
	  (mystrncasecmp(tline,CatString3("*",MyName,"IconFore"),Clength+9)==0)
	  CopyString(&IconFore,&tline[Clength+9]);
	else if
	  (mystrncasecmp(tline,CatString3("*",MyName,"IconBack"),Clength+9)==0)
	    CopyString(&IconBack,&tline[Clength+9]);
	else if
	  (mystrncasecmp(tline,CatString3("*",MyName,"IconHiFore"),Clength+11)==0)
	  CopyString(&ActIconFore,&tline[Clength+11]);
	else if
	  (mystrncasecmp(tline,CatString3("*",MyName,"IconHiBack"),Clength+11)==0)
	  CopyString(&ActIconBack,&tline[Clength+11]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Fore"),Clength+5)==0) 
	  CopyString(&Fore,&tline[Clength+5]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Back"),Clength+5)==0) 
	  CopyString(&Back,&tline[Clength+5]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Pixmap"),Clength+7)==0) 
	  CopyString(&IconwinPixmapFile,&tline[Clength+7]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Padding"),Clength+8)==0) 
	  interval = max(0,atoi(&tline[Clength+8]));
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "FrameWidth"),Clength+11)==0){ 
	  sscanf(&tline[Clength+11], "%d %d", &margin1, &margin2);
	  margin1 = max(0, margin1);
	  margin2 = max(0, margin2);
	}else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Lines"),Clength+6)==0)
	  Lines = max(1,atoi(&tline[Clength+6]));
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "SBWidth"),Clength+8)==0) 
	  bar_width = max(5,atoi(&tline[Clength+8]));
	else if (mystrncasecmp(tline,CatString3("*",MyName,
						"Placement"),Clength+10)==0)
	  parseplacement(&tline[Clength+10]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "SetWMIconSize"),Clength+14)==0)
	  local_flags |= SETWMICONSIZE;
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "HilightFocusWin"),Clength+16)==0)
	  m_mask |= M_FOCUS_CHANGE;
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Resolution"),Clength+11)==0){
	  tmp = &tline[Clength+11];
	  while(((isspace(*tmp))&&(*tmp != '\n'))&&(*tmp != 0))
	    tmp++;
	  if (mystrncasecmp(tmp, "Desk", 4) == 0){
	    m_mask |= M_NEW_DESK;
	    local_flags |= CURRENT_ONLY;
	  }
	}else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Mouse"),Clength+6)==0)
	  parsemouse(&tline[Clength + 6]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "Key"),Clength+4)==0)
	  parsekey(&tline[Clength + 4]);
	else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "SortIcons"),Clength+10)==0){
	  tmp = &tline[Clength+10];
	  while(((isspace(*tmp))&&(*tmp != '\n'))&&(*tmp != 0))
	    tmp++;
	  if (strlen(tmp) == 0){ /* the case where no argument is given */
	    sortby = ICONNAME;
	    return;
	  }
	  if (mystrncasecmp(tmp, "WindowName", 10) == 0)
	    sortby = WINDOWNAME;
	  else if (mystrncasecmp(tmp, "IconName", 8) == 0)
	    sortby = ICONNAME;
	  else if (mystrncasecmp(tmp, "ResClass", 8) == 0)
	    sortby = RESCLASS;
	  else if (mystrncasecmp(tmp, "ResName", 7) == 0)
	    sortby = RESNAME;
	}else if (mystrncasecmp(tline,CatString3("*",MyName,
					      "HideSC"),Clength+7)==0){
	  tmp = &tline[Clength+7];
	  while(((isspace(*tmp))&&(*tmp != '\n'))&&(*tmp != 0))
	    tmp++;
	  if (mystrncasecmp(tmp, "Horizontal", 10) == 0)
	    local_flags |= HIDE_H;
	  else if (mystrncasecmp(tmp, "Vertical", 8) == 0)
	    local_flags |= HIDE_V;
	}else if (mystrncasecmp(tline,CatString3("*",MyName,
					      ""),Clength+1)==0)
	  parseicon(&tline[Clength + 1]);
	else if (mystrncasecmp(tline,"IconPath",8)==0)
	  CopyString(&iconPath,&tline[8]);
	else if (mystrncasecmp(tline,"PixmapPath",10)==0)
	  CopyString(&pixmapPath,&tline[10]);
	else if (mystrncasecmp(tline,"ClickTime",9)==0)
	  ClickTime = atoi(&tline[9]);
      }
      GetConfigLine(fd,&tline);
    }
  
  return;
}

void parseicon(char *tline)
{
  int len;
  struct iconfile *tmp;
  char *ptr, *start, *end;

  tmp = (struct iconfile *)safemalloc(sizeof(struct iconfile));

 
   /* windowname */
   tmp->name = stripcpy2(tline);
   if(tmp->name == NULL){
     free(tmp);
     return;
   }
 
   /* skip windowname, based on strpcpy3 of configure.c */
   while((*tline != '"')&&(tline != NULL))
     tline++;
    if(*tline != 0)
      tline++;
   while((*tline != '"')&&(tline != NULL))
     tline++;
   if(*tline == 0){
     free(tmp);
     return;
   }
   tline++;
 
   /* file */
   /* skip spaces */
   while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
     tline++;
   start = tline;
   end = tline;
   while(!isspace(*end)&&(*end != '\n')&&(*end != 0))
     end++;
   len = end - start;
   ptr = safemalloc(len+1);
   strncpy(ptr, start, len);
   ptr[len] = 0;
   tmp->iconfile = ptr;

  if (strcmp(tmp->name, "*") == 0)
    DefaultIcon = tmp;
  
  tmp->next = NULL;

  if (IconListHead == NULL)
    IconListHead = IconListTail = tmp;
  else{
    IconListTail->next = tmp;
    IconListTail = tmp;
  }
}

void parseplacement(char *tline)
{
  char p[240], s[240];

  sscanf(tline, "%s %s", p, s);
  
  if (mystrncasecmp(p, "Left", 4) == 0)
    primary = LEFT;
  else if (mystrncasecmp(p, "Right", 5) == 0)
    primary = RIGHT;	
  else if (mystrncasecmp(p, "Top", 3) == 0)
    primary = TOP;
  else if (mystrncasecmp(p, "Bottom", 6) == 0)
    primary = BOTTOM;

  if (mystrncasecmp(s, "Left", 4) == 0)
    secondary = LEFT;
  else if (mystrncasecmp(s, "Right", 5) == 0)
    secondary = RIGHT;	
  else if (mystrncasecmp(s, "Top", 3) == 0)
    secondary = TOP;
  else if (mystrncasecmp(s, "Bottom", 6) == 0)
    secondary = BOTTOM;
}

void parsemouse(char *tline)
{
  struct mousefunc *f = NULL;
  int len;
  char *ptr,*start,*end,*tmp;

  f = (struct mousefunc *)safemalloc(sizeof(struct mousefunc));
  f->next = NULL;
  f->mouse = 0;

  /* skip spaces */
  while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
    tline++;
  start = tline;
  end = tline;
  while((!isspace(*end))&&(*end!='\n')&&(*end!=0))
    end++;
  if (mystrncasecmp(start, "1", 1) == 0)
    f->mouse = Button1;
  else if (mystrncasecmp(start, "2", 1) == 0)
    f->mouse = Button2;
  else if (mystrncasecmp(start, "3", 1) == 0)
    f->mouse = Button3;
  /* click or doubleclick */
  tline = end;
  /* skip spaces */
  while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
    tline++;
  start = tline;
  end = tline;
  while((!isspace(*end))&&(*end!='\n')&&(*end!=0))
    end++;
  if (mystrncasecmp(start, "Click", 5) == 0)
    f->type = CLICK;
  else if (mystrncasecmp(start, "DoubleClick", 11) == 0)
    f->type = DOUBLE_CLICK;

  /* actions */
  tline = end;
  /* skip spaces */
  while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
    tline++;
  start = tline;
  end = tline;
  tmp = tline;
  while((*tmp!='\n')&&(*tmp!=0)){
    if (!isspace(*tmp))
      end = tmp;
    tmp++;
  }
  end++;
  len = end - start;
  ptr = safemalloc(len+1);
  strncpy(ptr, start, len);
  ptr[len] = 0;
  f->action = ptr;
  f->next = MouseActions;
  MouseActions = f;
}  

/***********************************************************************
  parsekey
 	Based on part of AddFunckey() of configure.c in Fvwm.
       	Copyright 1988, Evans and Sutherland Computer Corporation,
       	Copyright 1989, Massachusetts Institute of Technology,
       	Copyright 1993, Robert Nation.
 ***********************************************************************/
void parsekey(char *tline)
{
  struct keyfunc *k;
  int nlen, alen;
  char *nptr, *aptr, *start, *end, *tmp;
  int i, kmin, kmax;
  KeySym keysym;

  /* skip spaces */
  while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
    tline++;
  start = tline;
  end = tline;
  while((!isspace(*end))&&(*end!='\n')&&(*end!=0))
    end++;
  nlen = end - start;
  nptr = safemalloc(nlen+1);
  strncpy(nptr, start, nlen);
  nptr[nlen] = 0;
  
  /* actions */
  tline = end;
  /* skip spaces */
  while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
    tline++;
  start = tline;
  end = tline;
  tmp = tline;
  while((*tmp!='\n')&&(*tmp!=0)){
    if (!isspace(*tmp))
      end = tmp;
    tmp++;
  }
  end++;
  alen = end - start;
  aptr = safemalloc(alen+1);
  strncpy(aptr, start, alen);
  aptr[alen] = 0;

  if ((keysym = XStringToKeysym(nptr)) == NoSymbol ||
      XKeysymToKeycode(dpy, keysym) == 0){
    free(nptr);
    free(aptr);
    return;
  }

  XDisplayKeycodes(dpy, &kmin, &kmax);
  for (i=kmin; i<=kmax; i++)
    if (XKeycodeToKeysym(dpy, i, 0) == keysym)
      {
	k = (struct keyfunc *)safemalloc(sizeof(struct keyfunc));
	k->name = nptr;
        k->keycode = i;
	k->action = aptr;
	k->next = KeyActions;
	KeyActions = k;
      }
}  

/***********************************************************************
 * change_window_name
 * 	Original work from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
void change_window_name(char *str)
{
  XTextProperty name;
  
  if (XStringListToTextProperty(&str,1,&name) == 0) 
    {
      fprintf(stderr,"%s: cannot allocate window name",MyName);
      return;
    }
  XSetWMName(dpy,main_win,&name);
  XSetWMIconName(dpy,main_win,&name);
  XFree(name.value);
}

/***********************************************************************
 * My_XNextEvent
 * 	Original work from GoodStuff:
 *		Copyright 1993, Robert Nation.
 ***********************************************************************/
int My_XNextEvent(Display *dpy, XEvent *event)
{
  fd_set in_fdset;
  unsigned long header[HEADER_SIZE];
  int body_length;
  int count,count2 = 0;
  static int miss_counter = 0;
  unsigned long *body;
  int total;
  char *cbody;

  if(XPending(dpy))
    {
      XNextEvent(dpy,event);
      return 1;
    }

  FD_ZERO(&in_fdset);
  FD_SET(x_fd,&in_fdset);
  FD_SET(fd[1],&in_fdset);

#ifdef __hpux
  select(fd_width,(int *)&in_fdset, 0, 0, NULL);
#else
  select(fd_width,&in_fdset, 0, 0, NULL);
#endif


  if(FD_ISSET(x_fd, &in_fdset))
    {
      if(XPending(dpy))
	{
	  XNextEvent(dpy,event);
	  miss_counter = 0;
	  return 1;
	}
      else
	miss_counter++;
      if(miss_counter > 100)
	DeadPipe(0);
    }
  
  if(FD_ISSET(fd[1], &in_fdset))
    {
      if(count = ReadFvwmPacket(fd[1],header,&body) > 0)
	{
	  process_message(header[1],body);
	  free(body);
	}
    }
  return 0;
}

/**************************************************************************
 * process_message
 * 	Based on ProcessMassage() from FvwmWinList:
 *		Copyright 1994, Mike Finger.
 *************************************************************************/
int diffx, diffy;
void process_message(unsigned long type, unsigned long *body)
{
  struct icon_info *tmp, *old;
  char *str;
  long olddesk;

  switch(type){
  case M_CONFIGURE_WINDOW:
    if (ready){
      if (!(local_flags & CURRENT_ONLY)) break;
      tmp = Head;
      while(tmp != NULL){
	if (tmp->id == body[0]){
	  if ((tmp->desk != body[7]) && !(tmp->flags & STICKY)){
	    olddesk = tmp->desk;
	    tmp->desk = body[7];
	    if (olddesk == CurrentDesk || tmp->desk == CurrentDesk){
	      if (tmp->desk == CurrentDesk && sortby != UNSORT)
		SortItem(NULL);
	      num_icons = AdjustIconWindows();
	      GetIconwinSize(&diffx, &diffy);
	      if (diffy && (primary == BOTTOM || secondary == BOTTOM))
		icon_win_y += diffy;
	      if (diffx && (primary == RIGHT || secondary == RIGHT))
		icon_win_x += diffx;
	      if (icon_win_y < 0)
		icon_win_y = 0;
	      if (icon_win_x < 0)
		icon_win_x = 0;
	      if (icon_win_x + Width > icon_win_width)
		icon_win_x = icon_win_width - Width;
	      if (icon_win_y + Height > icon_win_height)
		icon_win_y = icon_win_height - Height;
	      XMoveResizeWindow(dpy, icon_win, -icon_win_x, -icon_win_y,
				icon_win_width, icon_win_height); 
	      if (tmp->desk == CurrentDesk){
		XMapWindow(dpy, tmp->IconWin);
		if (max_icon_height != 0)
		  XMapWindow(dpy, tmp->icon_pixmap_w);
	      }else{
		XUnmapWindow(dpy, tmp->IconWin);
		if (max_icon_height != 0)
		  XUnmapWindow(dpy, tmp->icon_pixmap_w);
	      }		  
	      if (!(local_flags & HIDE_H) && diffx)
		RedrawHScrollbar();
	      if (!(local_flags & HIDE_V) && diffy)
		RedrawVScrollbar();
	    }
	  }else if ((body[8] & STICKY) && !(tmp->flags & STICKY)) /* stick */
	    tmp->flags |= STICKY;
	  else if (!(body[8] & STICKY) && (tmp->flags & STICKY)){ /* unstick */
	    tmp->flags &= ~STICKY;
	    tmp->desk = body[7];
	  }
	  return;
	}
	tmp = tmp->next;
      }
      break;
    }
  case M_ADD_WINDOW:
    if (AddItem(body[0], body[7], body[8]) == True && ready){
      GetIconwinSize(&diffx, &diffy);
      if (diffy && (primary == BOTTOM || secondary == BOTTOM))
	icon_win_y += diffy;
      if (diffx && (primary == RIGHT || secondary == RIGHT))
	icon_win_x += diffx;
      XMoveResizeWindow(dpy, icon_win, -icon_win_x, -icon_win_y,
			icon_win_width, icon_win_height); 
    }
    break;
  case M_DESTROY_WINDOW:
    if (DeleteItem(body[0]) && ready){
      GetIconwinSize(&diffx, &diffy);
      if (diffy && (primary == BOTTOM || secondary == BOTTOM))
	icon_win_y += diffy;
      if (diffx && (primary == RIGHT || secondary == RIGHT))
	icon_win_x += diffx;
      if (icon_win_y < 0)
	icon_win_y = 0;
      if (icon_win_x < 0)
	icon_win_x = 0;
      if (icon_win_x + Width > icon_win_width)
	icon_win_x = icon_win_width - Width;
      if (icon_win_y + Height > icon_win_height)
	icon_win_y = icon_win_height - Height;
      XMoveResizeWindow(dpy, icon_win, -icon_win_x, -icon_win_y,
			icon_win_width, icon_win_height);  
      AdjustIconWindows();
      if (!(local_flags & HIDE_H) && diffx)
	RedrawHScrollbar();
      if (!(local_flags & HIDE_V) && diffy)
	RedrawVScrollbar();
    }
    break;
  case M_ICON_FILE:
  case M_RES_CLASS:
    UpdateItem(type, body[0], (char *)&body[3]);
    break;
  case M_WINDOW_NAME:
    tmp = UpdateItem(type, body[0], (char *)&body[3]);
    if (!ready || tmp == NULL) 
      break;
    if (sortby == WINDOWNAME && tmp->IconWin != None 
	&& desk_cond(tmp) && SortItem(tmp) == True)
      AdjustIconWindows();
    break;
  case M_RES_NAME:
    if ((tmp = UpdateItem(type, body[0], (char *)&body[3])) == NULL)
      break;
    if (LookInList(tmp) && ready){
      if (sortby != UNSORT)
	SortItem(tmp);
      CreateIconWindow(tmp);
      ConfigureIconWindow(tmp);
      AdjustIconWindows();
      if (desk_cond(tmp)){
	if (max_icon_height != 0)
      XMapWindow(dpy, tmp->icon_pixmap_w);       
      XMapWindow(dpy, tmp->IconWin);       
	if (!(local_flags & HIDE_H))
	RedrawHScrollbar();
	if (!(local_flags & HIDE_V))
	RedrawVScrollbar();
    }
    }
    break;	
  case M_ICON_NAME:
    tmp = UpdateItem(type, body[0], (char *)&body[3]);
    if (!ready || tmp == NULL) 
    break;
    if (sortby != UNSORT && tmp->IconWin != None 
	&& desk_cond(tmp) && SortItem(tmp) == True)
      AdjustIconWindows();
    if (tmp->IconWin != None && desk_cond(tmp))
      RedrawIcon(tmp, 2);
    break;
  case M_DEFAULTICON:
    str = (char *)safemalloc(strlen((char *)&body[3])+1);
    strcpy(str, (char *)&body[3]);
    FvwmDefaultIcon = str;
    break;
  case M_ICONIFY:
  case M_DEICONIFY:
    if (ready && (tmp = SetFlag(body[0], type)) != NULL)
      RedrawIcon(tmp, 2);
    break;
  case M_FOCUS_CHANGE:
    if (!ready)
      break;
    tmp = Head;
    while(tmp != NULL){
      if (tmp->id == body[0]) break;
      tmp = tmp->next;
    }
    old = Hilite;
    Hilite = tmp;
    if (old != NULL)
      RedrawIcon(old, redraw_flag);
    if (tmp != NULL)
      RedrawIcon(tmp, redraw_flag);
    break;
  case M_NEW_DESK:
    if (CurrentDesk != body[0]){
      CurrentDesk = body[0];
      if (body[0] != 10000 && ready){ /* 10000 is a "magic" number used in FvwmPager */
	if (sortby != UNSORT)
	SortItem(NULL);
	num_icons = AdjustIconWindows();
	GetIconwinSize(&diffx, &diffy);
	icon_win_x = icon_win_y = 0;
	if (primary == BOTTOM || secondary == BOTTOM)
	  icon_win_y = icon_win_height - Height;
	if (primary == RIGHT || secondary == RIGHT)
	  icon_win_x = icon_win_width - Width;
	XMoveResizeWindow(dpy, icon_win, -icon_win_x, -icon_win_y,
			  icon_win_width, icon_win_height); 
	XUnmapSubwindows(dpy, icon_win); 
	mapicons(); 
	if (!(local_flags & HIDE_H))
	  RedrawHScrollbar();
	if (!(local_flags & HIDE_V))
	  RedrawVScrollbar();
      }
    }
    break;
  case M_END_WINDOWLIST:
    GetIconwinSize(&diffx, &diffy);
    tmp = Head;
    while(tmp != NULL){
      CreateIconWindow(tmp);
      ConfigureIconWindow(tmp);
      tmp = tmp->next;
    }
    if (sortby != UNSORT)
      SortItem(NULL);
    if (primary == BOTTOM || secondary == BOTTOM)
      icon_win_y = icon_win_height - Height;
    if (primary == RIGHT || secondary == RIGHT)
      icon_win_x = icon_win_width - Width;
    XMoveResizeWindow(dpy, icon_win, -icon_win_x, -icon_win_y,
		      icon_win_width, icon_win_height); 
    AdjustIconWindows();
    XMapWindow(dpy,main_win);
    XMapSubwindows(dpy, main_win); 
    XMapWindow(dpy, icon_win); 
    mapicons(); 
    ready = 1;
    break;
  default:
    break;
  }
}

struct icon_info *SetFlag(unsigned long id, int t)
{
  struct icon_info *tmp;
  tmp = Head;

  while(tmp != NULL){
    if (tmp->id == id){
      if (t == M_ICONIFY)
	tmp->flags |= ICONIFIED;
       else
	tmp->flags ^= ICONIFIED;
      return tmp;
    }
    tmp = tmp->next;
  }
  return NULL;
}
    
void mapicons(void)
{
  struct icon_info *tmp;
  tmp = Head;

  while(tmp != NULL){
    if (desk_cond(tmp)){
      XMapWindow(dpy, tmp->IconWin);
      if (max_icon_height != 0)
	XMapWindow(dpy, tmp->icon_pixmap_w);
    }
    tmp = tmp->next;
  }
}

int AdjustIconWindows(void)
{
  struct icon_info *tmp;
  int i = 0;
  tmp = Head;

  while(tmp != NULL){
    if (desk_cond(tmp) && tmp->IconWin != None)
      AdjustIconWindow(tmp, i++);
    tmp = tmp->next;
  }
  return i;
}

int desk_cond(struct icon_info *item)
{
  if (!(local_flags & CURRENT_ONLY) || 
      (item->flags & STICKY) || (item->desk == CurrentDesk))
    return 1;

  return 0;
}

/************************************************************************
 * AddItem
 *	Skeleton based on AddItem() from FvwmWinList:
 *		Copyright 1994, Mike Finger.
 ***********************************************************************/
Bool AddItem(unsigned long id, long desk, unsigned long flags)
{
  struct icon_info *new, *tmp;
  tmp = Head;

  if (id == main_win || (flags & TRANSIENT) || !(flags & SUPPRESSICON))
    return False;

  while (tmp != NULL){
    if (tmp->id == id || 
	(tmp->wmhints && (tmp->wmhints->flags & IconWindowHint) &&
	 tmp->wmhints->icon_window == id))
      return False;
    tmp = tmp->next;
  }
  
  new = (struct icon_info *)safemalloc(sizeof(struct icon_info));
  new->name = NULL;
  new->window_name = NULL;
  new->res_class = NULL;
  new->res_name = NULL;
  new->action = NULL;
  new->icon_file = NULL;
  new->icon_w = 0;
  new->icon_h = 0;
  new->IconWin = None;
  new->iconPixmap = None;
  new->icon_maskPixmap = None;
  new->icon_pixmap_w = None;  
  new->icon_depth = 0;
  new->desk = desk;
  new->id = id;
  new->extra_flags = DEFAULTICON;
  new->flags = flags | ICON_OURS;
  new->wmhints = NULL;

/* add new item to the head of the list

  new->prev = NULL;
  new->next = Head;
  if (Head != NULL)
    Head->prev = new;
  else
    Tail = new;
  Head = new; */

/* add new item to the tail of the list */
  new->prev = Tail;
  new->next = NULL;
  if (Tail != NULL)
    Tail->next = new;
  else
  Head = new;
  Tail = new;
  
  if (desk_cond(new)) num_icons++;

  return True;
}

/************************************************************************
 * Deletetem
 *	Skeleton based on DeleteItem() from FvwmWinList:
 *		Copyright 1994, Mike Finger.
 ***********************************************************************/
Bool DeleteItem(unsigned long id)
{
  struct icon_info *tmp = Head;
  
  while(tmp != NULL){
  if (tmp->id == id){
      if (desk_cond(tmp)) 
	num_icons--;
    if (Hilite == tmp)
      Hilite = NULL;
    if ((tmp->icon_pixmap_w != None) && (tmp->flags & ICON_OURS))
      XDestroyWindow(dpy, tmp->icon_pixmap_w);
    if (tmp->IconWin != None)
      XDestroyWindow(dpy, tmp->IconWin);
      if (tmp == Head){
    Head = tmp->next;
    if (Head != NULL)
      Head->prev = NULL;
    else
      Tail = NULL;
      }else {
      if (Tail == tmp)
	Tail = tmp->prev;
      tmp->prev->next = tmp->next;
      if (tmp->next != NULL)
	tmp->next->prev = tmp->prev;
      }
      freeitem(tmp, 1);
      return True;
    }
    tmp = tmp->next;
  }
  return False;
} 

/************************************************************************
 * UpdateItem
 *	Skeleton based on UpdateItem() from FvwmWinList:
 *		Copyright 1994, Mike Finger.
 ***********************************************************************/
struct icon_info *UpdateItem(unsigned long type, unsigned long id, char *item)
{
  struct icon_info *tmp;
  char *str;
  XClassHint classhint;
  int ret;

  tmp = Head;
  while (tmp != NULL){
    if (tmp->id == id){
      str = (char *)safemalloc(strlen(item)+1);
      strcpy(str, item);

      switch (type){
      case M_ICON_NAME:
	if (tmp->name != NULL)
	  free(tmp->name);
	tmp->name = str;
	return tmp;
      case M_ICON_FILE:
	tmp->icon_file = str;
	tmp->extra_flags &= ~DEFAULTICON;
	return tmp;
      case M_WINDOW_NAME:
	if (tmp->window_name != NULL)
	  free(tmp->window_name);
	tmp->window_name = str;
	return tmp;
      case M_RES_CLASS:
	tmp->res_class = str;
	ret = 0;
	if (sortby == RESCLASS && strcmp(NoClass, str) == 0 
	    && !(ret = XGetClassHint(dpy, tmp->id, &classhint))){
	  tmp->extra_flags |= NOCLASS;
	}
	if (ret){
	  if (classhint.res_class != NULL)
	    XFree(classhint.res_class);
	  if (classhint.res_name != NULL)
	    XFree(classhint.res_name);
	}
	return tmp;
      case M_RES_NAME:
	tmp->res_name = str;
	ret = 0;
	if (sortby == RESNAME && strcmp(NoResource, str) == 0 
	    && !(ret = XGetClassHint(dpy, tmp->id, &classhint)))
	  tmp->extra_flags |= NONAME;
	if (ret){
	  if (classhint.res_class != NULL)
	    XFree(classhint.res_class);
	  if (classhint.res_name != NULL)
	    XFree(classhint.res_name);
	}
	return tmp;
      }
    }
    tmp = tmp->next;
  }
  return NULL;
}

Bool SortItem(struct icon_info *item)
{
  struct icon_info *tmp1=Head, *a, *b, *tmp2;

  if (tmp1 == NULL)
    return False;

  if (item != NULL && 
      ((itemcmp(item->prev, item) <= 0) &&  
       (itemcmp(item->next, item) >= 0)))
    return False;

  while (tmp1->next != NULL){
    tmp2 = MinItem(tmp1);
    if (tmp1 == tmp2){
      tmp1 = tmp1->next;
      continue;
    }
    if (tmp1 == Head)
      Head = tmp2;
    a = tmp1->prev;
    b = tmp1->next;
    if (tmp1->prev != NULL)
      tmp1->prev->next = tmp2;
    if (b != tmp2)
      tmp1->next->prev = tmp2;
    if (b != tmp2)
      tmp2->prev->next = tmp1;
    if (tmp2->next != NULL)
      tmp2->next->prev = tmp1;
    if (b == tmp2){
      tmp1->prev = tmp2;
      tmp1->next = tmp2->next;
      tmp2->prev = a;
      tmp2->next = tmp1;
    }else{
      tmp1->prev = tmp2->prev;
      tmp1->next = tmp2->next;
      tmp2->prev = a;
      tmp2->next = b;
    }
    tmp1 = b;
  }
  Tail = tmp1;

  return True;
}

struct icon_info *MinItem(struct icon_info *head)
{
  struct icon_info *tmp, *i_min;

  if (head == NULL)
    return NULL;

  i_min = head;
  tmp = head->next;
  while (tmp != NULL){
    if (itemcmp(i_min, tmp) > 0)
      i_min = tmp;
    tmp = tmp->next;
  }
  return i_min;
} 

int itemcmp(struct icon_info *item1, struct icon_info *item2)
{
  int ret1, ret2;

  if (item1 == NULL){
    if (item2 == NULL)
      return 0;
    else
      return -1;
  } else if (item2 == NULL)
    return 1;

  /* skip items not on the current desk */
  ret1 = desk_cond(item1);
  ret2 = desk_cond(item2);
  if (!ret1 || !ret2)
    return (ret1 - ret2);

  ret1 = 0;
  ret2 = strcmp(item1->name, item2->name);

  switch(sortby){
  case WINDOWNAME:
    ret1 = strcmp(item1->window_name, item2->window_name);
    break;
  case RESCLASS:
    if ((item1->extra_flags & NOCLASS)){
      if ((item2->extra_flags & NOCLASS))
        ret1 = 0;
      else if (!(item2->extra_flags & NOCLASS))
        ret1 = -1;
    }else if ((item2->extra_flags & NOCLASS))
      ret1 = 1;
    else 
      ret1 = strcmp(item1->res_class, item2->res_class);
    break;
  case RESNAME:
    if ((item1->extra_flags & NOCLASS)){
      if ((item2->extra_flags & NOCLASS))
        ret1 = 0;
      else if (!(item2->extra_flags & NOCLASS))
        ret1 = -1;
    }else if ((item2->extra_flags & NOCLASS))
      ret1 = 1;
    else 
      ret1 = strcmp(item1->res_name, item2->res_name);
    break;
  default:
    break;
  }
  return (ret1 != 0 ? ret1 : ret2);
}

/*
void ShowItem(struct icon_info *head)
{
  struct icon_info *tmp;

  fprintf(stderr, "The contents of item are as follows:\n");
  tmp = head;
  while (tmp != NULL){
    fprintf(stderr, "id:%x  name:%s resname:%s class%s iconfile:%s\n",
	    tmp->id, 
	    tmp->name == NULL ? "NULL" : tmp->name, tmp->res_name,
	    tmp->res_class, tmp->icon_file);  
    tmp = tmp->next;
  }
}

void ShowAction(void)
{
  struct mousefunc *tmp;

  tmp = MouseActions;
  while (tmp != NULL){
    fprintf(stderr, "mouse:%d type %d action:%s\n", tmp->mouse,
	    tmp->type, tmp->action);  
    tmp = tmp->next;
  }
}  

void ShowKAction(void)
{
  struct keyfunc *tmp;

  tmp = KeyActions;
  while (tmp != NULL){
    fprintf(stderr, "key:%s keycode:%d action:%s\n", tmp->name,
	    tmp->keycode, tmp->action);  
    tmp = tmp->next;
  }
}  
*/

void freeitem(struct icon_info *item, int d)
{
  if (item == NULL)
    return;

  if (!(item->flags & ICON_OURS)){ 
    if (max_icon_height != 0)
    XUnmapWindow(dpy, item->icon_pixmap_w);
    if (d == 0)
      XReparentWindow(dpy, item->icon_pixmap_w, Root, 0, 0);
  }

  if (item->name != NULL)
    free(item->name);
  if (item->window_name != NULL)
    free(item->window_name);
  if (item->res_name != NULL)
    free(item->res_name);
  if (item->res_class != NULL)
    free(item->res_class);
  if (item->wmhints != NULL)
    XFree(item->wmhints);
  if (item->iconPixmap != None)
    XFreePixmap(dpy, item->iconPixmap);
  if (item->icon_maskPixmap != None && 
      (item->wmhints == NULL || 
       !(item->wmhints->flags & (IconPixmapHint|IconWindowHint))))
    XFreePixmap(dpy, item->icon_maskPixmap);

  free(item);
}


/************************************************************************
  IsClick	
  	Based on functions.c from Fvwm:
	Copyright 1988, Evans and Sutherland Computer Corporation,
       	Copyright 1989, Massachusetts Institute of Technology,
       	Copyright 1993, Robert Nation.
 ***********************************************************************/
Bool IsClick(int x,int y,unsigned EndMask, XEvent *d)
{
  int xcurrent,ycurrent,total = 0;

  xcurrent = x;
  ycurrent = y;
  while((total < ClickTime)&&
        (x - xcurrent < 5)&&(x - xcurrent > -5)&&
        (y - ycurrent < 5)&&(y - ycurrent > -5))
    {
      sleep_a_little(10000);
      total+=10;
      if(XCheckMaskEvent (dpy,EndMask, d))
	return True;
      if(XCheckMaskEvent (dpy,ButtonMotionMask|PointerMotionMask, d))
        {
          xcurrent = d->xmotion.x_root;
          ycurrent = d->xmotion.y_root;
        }
    }
  return False;
}

/************************************************************************
 * ExecuteAction
* 	Based on part of ComplexFunction() of functions.c from fvwm:
	Copyright 1988, Evans and Sutherland Computer Corporation,
       	Copyright 1989, Massachusetts Institute of Technology,
       	Copyright 1993, Robert Nation.
 ***********************************************************************/
void ExecuteAction(int x, int y, struct icon_info *item)
{
  int type = NO_CLICK;
  XEvent *ev;
  XEvent d;
  struct mousefunc *tmp;

   /* Wait and see if we have a click, or a move */
  /* wait 100 msec, see if the used releases the button */
  if(IsClick(x,y,ButtonReleaseMask,&d))
    {
      type = CLICK;
      ev = &d;
    }

  /* If it was a click, wait to see if its a double click */
  if((type == CLICK) && (IsClick(x,y,ButtonPressMask, &d)))
    {
      type = ONE_AND_A_HALF_CLICKS;
      ev = &d;
    }
  if((type == ONE_AND_A_HALF_CLICKS) &&
     (IsClick(x,y,ButtonReleaseMask, &d))) 
    {
      type = DOUBLE_CLICK;
      ev = &d;
    }
  tmp = MouseActions;

  while (tmp != NULL){
    if (tmp->mouse == d.xbutton.button && tmp->type == type){
      SendFvwmPipe(fd, tmp->action, item->id);
      return;
    }
    tmp = tmp->next;
  }
}

void ExecuteKey(XEvent event)
{
  struct icon_info *item;
  struct keyfunc *tmp;

  if ((item = Hilite) == NULL)
     if ((item = Head) == NULL)
       return;

  tmp = KeyActions;
  event.xkey.keycode = 
    XKeysymToKeycode(dpy,XKeycodeToKeysym(dpy,event.xkey.keycode,0));
  while (tmp != NULL){
    if (tmp->keycode == event.xkey.keycode){
      SendFvwmPipe(fd, tmp->action, item->id);
      return;
    }
    tmp = tmp->next;
  }
}

/***********************************************************************
 LookInList
	Based on part of LookInList() of add_window.c from fvwm:
	Copyright 1988, Evans and Sutherland Computer Corporation,
       	Copyright 1989, Massachusetts Institute of Technology,
       	Copyright 1993, Robert Nation.
 ***********************************************************************/
int LookInList(struct icon_info *item)
{
  int isdefault=1;
  char *value=NULL;
  struct iconfile *nptr;

  if (IconListHead == NULL) {
    if ((item->extra_flags & DEFAULTICON) && (FvwmDefaultIcon != NULL))
      item->icon_file = FvwmDefaultIcon;
    return 1;
  }


  for (nptr = IconListHead; nptr != NULL; nptr = nptr->next){
    if (nptr == DefaultIcon)
      isdefault = 1;

    if (matchWildcards(nptr->name, item->res_class) == TRUE){
      value = nptr->iconfile;            
      if (nptr != DefaultIcon)
	isdefault = 0;
    }

    if (matchWildcards(nptr->name, item->res_name) == TRUE){
      value = nptr->iconfile;
      if (nptr != DefaultIcon)
	isdefault = 0;
    }

    if (matchWildcards(nptr->name, item->window_name) == TRUE){
      value = nptr->iconfile;
      if (nptr != DefaultIcon)
	isdefault = 0;
    }
  }

  if (!isdefault){
    item->icon_file = value;
    item->extra_flags &= ~DEFAULTICON;
  }else if ((item->extra_flags & DEFAULTICON)){ 
    if (DefaultIcon != NULL)
      item->icon_file = DefaultIcon->iconfile;
    else if (FvwmDefaultIcon != NULL)
      item->icon_file = FvwmDefaultIcon;
  }
   
  /* Icon is not shown if "-" is specified */
  if (item->icon_file != NULL && (strcmp(item->icon_file, "-") == 0)){
    DeleteItem(item->id);
    return 0;
  }
  return 1;
}

/***********************************************************************
 strcpy
	Based on stripcpy2() of configure.c from Fvwm:
	Copyright 1988, Evans and Sutherland Computer Corporation,
       	Copyright 1989, Massachusetts Institute of Technology,
       	Copyright 1993, Robert Nation.
 ***********************************************************************/
char *stripcpy2(char *source)
{
  char *ptr;
  int count = 0;
  while((*source != '"')&&(*source != 0))
    source++;
  if(*source == 0)
    return 0;

  source++;
  ptr = source;
  while((*ptr!='"')&&(*ptr != 0)){
    ptr++;  
    count++;
  }
  ptr = safemalloc(count+1);
  strncpy(ptr,source,count);
  ptr[count]=0;
  return ptr;
}

/***********************************************************************
 Error handler
 ***********************************************************************/
XErrorHandler myErrorHandler(Display *dpy, XErrorEvent *event)
{
  char msg[256];

  if (event->error_code == BadWindow)
    return;

  XGetErrorText(dpy, event->error_code, msg, 256);

  fprintf(stderr, "Error in %s:  %s \n", MyName, msg);
  fprintf(stderr, "Major opcode of failed request:  %d \n", event->request_code);
  fprintf(stderr, "Resource id of failed request:  0x%x \n", event->resourceid);
}
