 /*
    * Copyright (c) 1998 Rafal Wierzbicki <rafal@mcss.mcmaster.ca>
    * Copyright (c) 1998 Michal Vitecek <M.Vitecek@sh.cvut.cz>
    * Copyright (C) 1998 Sasha Vasko <sashav@sprintmail.com>
    * Copyright (c) 1998 Makoto Kato <m_kato@ga2.so-net.ne.jp>
    * Copyright (c) 1998 Rene Fichter <ceezaer@cyberspace.org>
    * Copyright (c) 1997 Guylhem Aznar <guylhem@oeil.qc.ca>
    * Copyright (c) 1994 Mike Finger <mfinger@mermaid.micro.umn.edu>
    *                    or <Mike_Finger@atk.com>
    * Copyright (c) 1994 Robert Nation
    * Copyright (c) 1994 Nobutaka Suzuki
    *
    * 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.
  */

#define TRUE 1
#define FALSE 0

#define NO_CONSOLE

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


#define YES "Yes"
#define NO  "No"

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

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.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
#ifdef ISC			/* Saul */
#include <sys/bsdtypes.h>	/* Saul */
#endif /* Saul */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/cursorfont.h>
#ifdef I18N
#include <X11/Xlocale.h>
#endif

#include "../../include/module.h"
#include "../../include/loadimg.h"
#include "WinList.h"
#include "ButtonArray.h"
#include "List.h"
#include "Colors.h"
#include "Mallocs.h"

#define GRAB_EVENTS (ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|EnterWindowMask|LeaveWindowMask)

#define SomeButtonDown(a) ((a)&Button1Mask||(a)&Button2Mask||(a)&Button3Mask)

/* Window and look related variables */

Window window;
int w_height = 5;
int w_width = 5;
XFontStruct *font;
#ifdef I18N
XFontSet fontset;
#endif
GC relief_gc;
GC shadow_gc;
GC fore_gc;
char *back_pixmap;
char *pixmap_path;
char *icon_path;
char *grad_from;
char *grad_to;
int grad_type;
int text_style;
int Balloons = 0;
int AutoHide = 0;
/* done */

/* X stuff */

Display *dpy;
int screen;			/* for balloons.c */
ScreenInfo Scr;

/*Window Root; */

int s_height;
int s_width;
char *MyName = NULL;
int hidden = 0;

/* File type information */

FILE *console;
int fd_width;
int fd[2];
int x_fd;

int fontheight;
static Atom wm_del_win;
Atom MwmAtom = None;

/* Module related information */

int WindowIsUp = 0;
int win_grav, win_x = 0, win_y = 0, win_title, win_border;
int Transient = 0, Pressed = 0, ButPressed, Checked = 0;
ButtonArray buttons;
List windows;

char *ClickAction[3] =
{"Iconify -1,Raise", "Iconify", "Lower"};
char *EnterAction, *BackColor = "white", *ForeColor = "black", *geometry = "";
char *font_string = "fixed";
int UseSkipList = 0, Anchor = 1, UseIconNames = 0, OrientVert = TRUE;
int LeftJustify = 0, TruncateLeft = 1;
unsigned int MaxWidth = 0;

/******************************************************************************
  Main - Setup the XConnection,request the window list and loop forever
    Based on main() from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
int
main (int argc, char **argv)
{
  char *s;
  char configfile[255];
  char *realconfigfile;

  /* Save the program name for error messages and config parsing */
  s = strrchr (argv[0], '/');
  if (s != NULL)
    s = s + 1;
  else
    s = argv[0];

  /* Setup my name */
  w_height = 5;
  w_width = 5;
  MyName = s;
  OpenConsole ();

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

#ifdef I18N
  if (setlocale (LC_CTYPE, "") == NULL)
    fprintf (stderr, "Cannot set locale!\n");
#endif

  if ((argc == 7) && (!mystrcasecmp (argv[6], "Transient")))
    Transient = 1;

  fd[0] = atoi (argv[1]);
  fd[1] = atoi (argv[2]);

  signal (SIGPIPE, DeadPipe);

  /* X stuff */

  if (!(dpy = XOpenDisplay (NULL)))
    {
      fprintf (stderr, "%s: can't open display %s", MyName,
	       XDisplayName (NULL));
      exit (1);
    }

  x_fd = XConnectionNumber (dpy);
  screen = Scr.screen = DefaultScreen (dpy);
  Scr.Root = RootWindow (dpy, Scr.screen);
  Scr.d_depth = DefaultDepth (dpy, Scr.screen);
  s_height = DisplayHeight (dpy, Scr.screen);
  s_width = DisplayWidth (dpy, Scr.screen);
  w_width = 5;
  w_height = 5;
  /* done */

  if (strstr (argv[3], "steprc") != NULL)
    ParseOptions (argv[3]);
  else
    {
      /* Parse the config file */
      sprintf (configfile, "%s/winlist", AFTER_DIR);
      realconfigfile = PutHome (configfile);

      if ((CheckFile (realconfigfile)) == -1)
	{
	  free (realconfigfile);
	  sprintf (configfile, "%s/winlist", AFTER_SHAREDIR);
	  realconfigfile = PutHome (configfile);
	}
      ParseOptions (realconfigfile);
      free (realconfigfile);
    }

  /* Rafal's additions */

  ParseBaseOptions (argv[3]);

  if (Balloons)
    balloon_setup (dpy);

  /* done */

  /* Setup the XConnection */
  StartMeUp ();
  InitArray (&buttons, 0, 0, w_width, fontheight + 6);
  InitList (&windows);


  fd_width = GetFdWidth ();

  /* Request a list of all windows,
   * wait for ConfigureWindow packets */
  SendASPipe ("Send_WindowList", 0);

  /* Recieve all messages from AfterStep */
  EndLessLoop ();

  return 0;
}

/******************************************************************************
  EndLessLoop -  Read and redraw until we get killed, blocking when can't read
******************************************************************************/
void
EndLessLoop ()
{
  fd_set readset;
  struct timeval tv;

  while (1)
    {
      FD_ZERO (&readset);
      FD_SET (fd[1], &readset);
      FD_SET (x_fd, &readset);
      tv.tv_sec = 0;
      tv.tv_usec = 0;
#ifdef __hpux
      if (!select (fd_width, (int *) &readset, NULL, NULL, &tv))
	{
	  XPending (dpy);
	  FD_ZERO (&readset);
	  FD_SET (fd[1], &readset);
	  FD_SET (x_fd, &readset);
	  select (fd_width, (int *) &readset, NULL, NULL, NULL);
	}
#else
      if (!select (fd_width, &readset, NULL, NULL, &tv))
	{
	  XPending (dpy);
	  FD_ZERO (&readset);
	  FD_SET (fd[1], &readset);
	  FD_SET (x_fd, &readset);
	  select (fd_width, &readset, NULL, NULL, NULL);
	}
#endif
      if (FD_ISSET (x_fd, &readset))
	LoopOnEvents ();
      if (!FD_ISSET (fd[1], &readset))
	continue;
      ReadASPipe ();
    }
}

/******************************************************************************
  ReadASPipe - Read a single message from the pipe from AfterStep
    Originally Loop() from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void
ReadASPipe ()
{
  unsigned long header[3], *body;
  if (ReadASPacket (fd[1], header, &body) > 0)
    {
      ProcessMessage (header[1], body);
      free (body);
    }
}

/******************************************************************************
  ProcessMessage - Process the message coming from AfterStep
    Skeleton based on processmessage() from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void
ProcessMessage (unsigned long type, unsigned long *body)
{
  int redraw = 0, i;
  long flags;
  char *name, *string;

  switch (type)
    {
    case M_ADD_WINDOW:
    case M_CONFIGURE_WINDOW:
      if (body[0] == window)
	{
	  win_x = (int) body[3];
	  win_y = (int) body[4];
	  win_title = (int) body[9];
	  win_border = (int) body[10];
	  if (win_border != 0)
	    win_border++;
	}
      if (FindItem (&windows, body[0]) != -1)
	break;
      if (!(body[8] & WINDOWLISTSKIP) || !UseSkipList)
	AddItem (&windows, body[0], body[8]);
      break;
    case M_DESTROY_WINDOW:
      if ((i = DeleteItem (&windows, body[0])) == -1)
	break;
      RemoveButton (&buttons, i);
      if (WindowIsUp)
	AdjustWindow ();
      redraw = 1;
      break;
    case M_WINDOW_NAME:
    case M_ICON_NAME:
      if ((type == M_ICON_NAME && !UseIconNames) ||
	  (type == M_WINDOW_NAME && UseIconNames))
	break;
      if ((i = UpdateItemName (&windows, body[0], (char *) &body[3])) == -1)
	break;
      string = (char *) &body[3];
      name = makename (string, ItemFlags (&windows, body[0]));
      if (UpdateButton (&buttons, i, name, -1) == -1)
	{
	  AddButton (&buttons, name, 1);
	}
      free (name);
      if (WindowIsUp)
	AdjustWindow ();
      redraw = 1;
      break;
    case M_DEICONIFY:
    case M_ICONIFY:
      if ((i = FindItem (&windows, body[0])) == -1)
	break;
      flags = ItemFlags (&windows, body[0]);
      if (type == M_DEICONIFY && !(flags & ICONIFIED))
	break;
      if (type == M_ICONIFY && flags & ICONIFIED)
	break;
      flags ^= ICONIFIED;
      UpdateItemFlags (&windows, body[0], flags);
      string = ItemName (&windows, i);
      name = makename (string, flags);
      if (UpdateButton (&buttons, i, name, -1) != -1)
	redraw = 1;
      free (name);
      break;
    case M_END_WINDOWLIST:
      if (!WindowIsUp)
	MakeMeWindow ();
      redraw = 1;
      break;
    case M_NEW_DESK:
      break;
    case M_NEW_PAGE:
      break;
    }

  if (redraw && WindowIsUp == 1)
    RedrawWindow (0);
}

/******************************************************************************
  SendASPipe - Send a message back to AfterStep 
    Based on SendInfo() from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void
SendASPipe (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;

      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;
    }
}

/***********************************************************************
  Detected a broken pipe - time to exit 
    Based on DeadPipe() from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
 **********************************************************************/
void
DeadPipe (int nonsense)
{
  ShutMeDown (1);
}

/******************************************************************************
  WaitForExpose - Used to wait for expose event so we don't draw too early
******************************************************************************/
void
WaitForExpose (void)
{
  XEvent Event;
  while (1)
    {
      XNextEvent (dpy, &Event);
      if (Event.type == Expose)
	{
	  if (Event.xexpose.count == 0)
	    break;
	}
    }
}

/******************************************************************************
  RedrawWindow - Update the needed lines and erase any old ones
******************************************************************************/
void
RedrawWindow (int force)
{
  DrawButtonArray (&buttons, force);
  if (XQLength (dpy) && !force)
    LoopOnEvents ();
}

/******************************************************************************
  ConsoleMessage - Print a message on the console.  Works like printf.
******************************************************************************/
void
ConsoleMessage (char *fmt,...)
{
#ifndef NO_CONSOLE
  va_list args;
  FILE *filep;
  if (console == NULL)
    filep = stderr;
  else
    filep = console;
  va_start (args, fmt);
  vfprintf (filep, fmt, args);
  va_end (args);
#endif
}

/******************************************************************************
  OpenConsole - Open the console as a way of sending messages
******************************************************************************/
int
OpenConsole ()
{
  return 1;
#ifndef NO_CONSOLE
  if ((console = fopen ("/dev/console", "w")) == NULL)
    {
      fprintf (stderr, "%s: cannot open console\n", MyName);
      return 0;
    }
#endif
  return 1;
}

/******************************************************************************
  ParseOptions - Parse the configuration file AfterStep to us to use
    Based on part of main() from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
      Changed by Michal Vitecek, 1998
      Additions by Rafal Wierzbicki, 1998
******************************************************************************/
void
ParseOptions (char *file)
{
  char *line, *tline;
  FILE *ptr;
  int len;

  if ((ptr = fopen (file, "r")) == NULL)
    {
      fprintf (stderr, "%s: can\'t open config file %s", MyName, file);
      exit (1);
    }

  line = (char *) safemalloc (MAXLINELENGTH);
  len = strlen (MyName);

  while ((tline = fgets (line, MAXLINELENGTH, ptr)) != NULL)
    {
      while (isspace (*tline))
	tline++;
      if ((*tline == '*') && (!mystrncasecmp (tline + 1, MyName, len)))
	{
	  tline += len + 1;
	  if (balloon_parse (tline, ptr))
	    {
	      Balloons = 1;
	      continue;
	    }
	  else if (!mystrncasecmp (tline, "Font", 4))
	    CopyString (&font_string, tline + 4);
	  else if (!mystrncasecmp (tline, "Fore", 4))
	    CopyString (&ForeColor, tline + 4);
	  else if (!mystrncasecmp (tline, "Geometry", 8))
	    CopyString (&geometry, tline + 8);
	  else if (!mystrncasecmp (tline, "Back", 4))
	    CopyString (&BackColor, tline + 4);
	  else if (!mystrncasecmp (tline, "NoAnchor", 8))
	    Anchor = 0;
	  else if (!mystrncasecmp (tline, "Action", 6))
	    LinkAction (tline + 6);
	  else if (!mystrncasecmp (tline, "UseSkipList", 11))
	    UseSkipList = 1;
	  else if (!mystrncasecmp (tline, "UseIconNames", 12))
	    UseIconNames = 1;
	  else if (!mystrncasecmp (tline, "LeftJustify", 11))
	    LeftJustify = 1;
	  else if (!mystrncasecmp (tline, "MaxWidth", 8))
	    {
	      MaxWidth = atoi (tline + 8);
	      if (MaxWidth <= 1)
		MaxWidth = 0;
	    }
	  else if (!mystrncasecmp (tline, "Orientation", 11))
	    {
	      if (!mystrncasecmp (tline + 12, "across", 6))
		OrientVert = FALSE;
	    }
	  /* Rafal's additions, 
	   * Options:
	   * WinListPixmap - background pixmap for WinList
	   * TruncateLeft - display the left part of the title
	   * TextStyle - 3d text, 1 and 2 for 3d text, 0 for normal
	   * GradType - gradients for WinList, 0 - normal, 1 relief
	   *            2 - vgradient, 3 hgradient
	   * GradFrom - starting colour for gradient
	   * GradTo - ending colour for gradient
	   */

	  else if (!mystrncasecmp (tline, "GradType", 8))
	    {
	      grad_type = atoi (tline + 8);
	    }
	  else if (!mystrncasecmp (tline, "TextStyle", 9))
	    {
	      text_style = atoi (tline + 9);
	    }
	  else if (!mystrncasecmp (tline, "Pixmap", 6))
	    {
	      CopyString (&back_pixmap, tline + 6);
	    }
	  else if (!mystrncasecmp (tline, "GradTo", 6))
	    {
	      CopyString (&grad_to, tline + 6);
	    }
	  else if (!mystrncasecmp (tline, "GradFrom", 8))
	    {
	      CopyString (&grad_from, tline + 8);
	    }
	  else if (!mystrncasecmp (tline, "AutoHide", 8))
	    {
	      AutoHide = atoi (tline + 8);
	    }
	}
    }
  free (line);
  fclose (ptr);
}

/* ParseBaseOptions - parse the appropriate base.xxbpp file
 * sets pixmap and icon paths to appropriate values
 * based on ParseOptions, Rafal Wierzbicki 1998.
 */
void
ParseBaseOptions (char *file)
{
  char line[MAXLINELENGTH];
  char *tline;
  FILE *ptr;

  if ((ptr = fopen (file, "r")) == NULL)
    {
      fprintf (stderr, "%s: can\'t open config file %s", MyName, file);
      exit (1);
    }

  tline = fgets (line, sizeof (line), ptr);
  while (tline != NULL)
    {
      while (isspace (*tline))
	tline++;
      if (strlen (tline) > 1)
	{
	  if (!mystrncasecmp (tline, "IconPath", 8))
	    CopyString (&icon_path, tline + 8);
	  else if (!mystrncasecmp (tline, "PixmapPath", 10))
	    CopyString (&pixmap_path, tline + 10);
	}
      tline = fgets (line, sizeof (line), ptr);
    }
  fclose (ptr);
}

/******************************************************************************
  LoopOnEvents - Process all the X events we get
******************************************************************************/
void
LoopOnEvents ()
{
  int num;
  char buffer[10];
  XEvent Event;
  Window dummyroot, dummychild;
  int x, x1, y, y1;
  unsigned int dummy1;

  if (Transient && !Checked)
    {
      XQueryPointer (dpy, window, &dummyroot, &dummychild, &x1, &y1,
		     &x, &y, &dummy1);
      num = WhichButton (&buttons, x, y);
      if (num != -1)
	{
	  Pressed = 1;
	  ButPressed = num;
	  SwitchButton (&buttons, num);
	}
      else
	Pressed = 0;
      Checked = 1;
    }

  while (XPending (dpy))
    {
      XNextEvent (dpy, &Event);
      if (Balloons && balloon_handle_event (&Event))
	{
	  /* balloon_handle_event showed a balloon but we still have to watch
	     * for these 3 events
	   */

	  switch (Event.type)
	    {
	    case EnterNotify:
	      if (SomeButtonDown (Event.xcrossing.state))
		{
		  num = WhichButton (&buttons, Event.xcrossing.x, Event.xcrossing.y);
		  if (num != -1)
		    {
		      SwitchButton (&buttons, num);
		      ButPressed = num;
		    }
		  else
		    ButPressed = -1;
		  Pressed = 1;
		}
	      if (AutoHide && hidden)
		UnHideWinList ();
	      break;
	    case LeaveNotify:
	      if (SomeButtonDown (Event.xcrossing.state))
		if (ButPressed != -1)
		  SwitchButton (&buttons, ButPressed);
	      Pressed = 0;
	      if (AutoHide && !hidden)
		HideWinList ();
	      break;
	    case MotionNotify:
	      if (!Pressed)
		{
		  break;
		}
	      num = WhichButton (&buttons, Event.xmotion.x, Event.xmotion.y);
	      if (num == ButPressed)
		break;
	      if (ButPressed != -1)
		SwitchButton (&buttons, ButPressed);
	      if (num != -1)
		{
		  SwitchButton (&buttons, num);
		  ButPressed = num;
		}
	      else
		ButPressed = -1;
	      break;
	    }
	  continue;
	}			/*balloon_handle_event */
      switch (Event.type)
	{
	case ButtonRelease:
	  if (Pressed)
	    {
	      num = WhichButton (&buttons, Event.xbutton.x, Event.xbutton.y);
	      if (num != -1)
		{
		  SendASPipe (ClickAction[(Transient) ? 0 :
					  Event.xbutton.button - 1],
			      ItemID (&windows, num));
		  SwitchButton (&buttons, num);
		}
	    }
	  if (Transient)
	    ShutMeDown (0);
	  Pressed = 0;
	  ButPressed = -1;
	  break;
	case ButtonPress:
	  num = WhichButton (&buttons, Event.xbutton.x, Event.xbutton.y);
	  if (num != -1)
	    {
	      SwitchButton (&buttons, num);
	      ButPressed = num;
	    }
	  else
	    ButPressed = -1;
	  Pressed = 1;
	  break;
	case Expose:
	  if (Event.xexpose.count == 0)
	    RedrawWindow (1);
	  break;
	case KeyPress:
	  num = XLookupString (&Event.xkey, buffer, 10, NULL, 0);
	  if (num == 1)
	    {
	      if (buffer[0] == 'q' || buffer[0] == 'Q')
		ShutMeDown (0);
	      else if (buffer[0] == 'i' || buffer[0] == 'I')
		PrintList (&windows);
	      else if (buffer[0] == 'b' || buffer[0] == 'B')
		PrintButtons (&buttons);
	    }
	  break;
	case ClientMessage:
	  if ((Event.xclient.format == 32) &&
	      (Event.xclient.data.l[0] == wm_del_win))
	    ShutMeDown (0);
	case EnterNotify:
	  if (!SomeButtonDown (Event.xcrossing.state))
	    break;
	  num = WhichButton (&buttons, Event.xcrossing.x, Event.xcrossing.y);
	  if (num != -1)
	    {
	      SwitchButton (&buttons, num);
	      ButPressed = num;
	    }
	  else
	    ButPressed = -1;
	  Pressed = 1;
	  break;
	case LeaveNotify:
	  if (!SomeButtonDown (Event.xcrossing.state))
	    break;
	  if (ButPressed != -1)
	    SwitchButton (&buttons, ButPressed);
	  Pressed = 0;
	  break;
	case MotionNotify:
	  if (!Pressed)
	    {
	      break;
	    }
	  num = WhichButton (&buttons, Event.xmotion.x, Event.xmotion.y);
	  if (num == ButPressed)
	    break;
	  if (ButPressed != -1)
	    SwitchButton (&buttons, ButPressed);
	  if (num != -1)
	    {
	      SwitchButton (&buttons, num);
	      ButPressed = num;
	    }
	  else
	    ButPressed = -1;
	  break;
	}
    }
}

/******************************************************************************
  AdjustWindow - Resize the window according to maxwidth by number of buttons
******************************************************************************/
void
AdjustWindow ()
{
  int new_width = 0, new_height = 0, tw, i, total;
  char *temp;

  total = ItemCount (&windows);
  if (!total)
    {
      if (WindowIsUp == 1)
	{
	  XUnmapWindow (dpy, window);
	  WindowIsUp = 2;
	}
      return;
    }
  for (i = 0; i < total; i++)
    {
      temp = ItemName (&windows, i);
      if (temp != NULL)
	{
	  tw = 10 + XTextWidth (font, temp, strlen (temp));
	  tw += XTextWidth (font, "()", 2);
	  new_width = max (new_width, tw);
	}
    }
  new_height = (total * (fontheight + 6 + 1) - 1);
  if (!OrientVert || (new_width > MaxWidth && MaxWidth != 0))
    new_width = MaxWidth;
  if (!OrientVert)
    new_height = (fontheight + 6 + 1) - 1;
  if (WindowIsUp && (new_height != w_height || new_width != w_width))
    {
      if (Anchor)
	{
	  if (win_grav == SouthEastGravity || win_grav == NorthEastGravity)
	    win_x -= (new_width - w_width);
	  if (win_grav == SouthEastGravity || win_grav == SouthWestGravity)
	    win_y -= (new_height - w_height);
	  XMoveResizeWindow (dpy, window, win_x + win_border,
			     win_y + win_title + win_border,
			     new_width, new_height);
	}
      else
	XResizeWindow (dpy, window, new_width, new_height);
    }
  UpdateArray (&buttons, -1, -1, new_width, -1);
  if (new_height > 0)
    w_height = new_height;
  if (new_width > 0)
    w_width = new_width;
  if (WindowIsUp == 2)
    {
      XMapWindow (dpy, window);
      WindowIsUp = 1;
      WaitForExpose ();
    }
}

/******************************************************************************
  makename - Based on the flags return me '(name)' or 'name'
******************************************************************************/
char *
makename (char *string, long flags)
{
  char *ptr;
  ptr = safemalloc (strlen (string) + 3);
  if (flags & ICONIFIED)
    strcpy (ptr, "(");
  else
    strcpy (ptr, "");
  strcat (ptr, string);
  if (flags & ICONIFIED)
    strcat (ptr, ")");
  return ptr;
}

/******************************************************************************
  LinkAction - Link an response to a users action
******************************************************************************/
void
LinkAction (char *string)
{
  char *temp;
  temp = string;
  while (isspace (*temp))
    temp++;
  if (mystrncasecmp (temp, "Click1", 6) == 0)
    CopyString (&ClickAction[0], &temp[6]);
  else if (mystrncasecmp (temp, "Click2", 6) == 0)
    CopyString (&ClickAction[1], &temp[6]);
  else if (mystrncasecmp (temp, "Click3", 6) == 0)
    CopyString (&ClickAction[2], &temp[6]);
  else if (mystrncasecmp (temp, "Enter", 5) == 0)
    CopyString (&EnterAction, &temp[5]);
}

/* SetBackPixmap - Rafal Wierzbicki, 1998
 * sets the background pixmap in WinList
 * returns 0 if failed, 1 otherwise.
 */
int
SetBackPixmap ()
{
  char *path = NULL;
  Pixmap pixmap = None;

  path = findIconFile (back_pixmap, pixmap_path, R_OK);
  if (path)
    {
      pixmap = LoadImage (dpy, window, Scr.d_depth, path);
      if (!pixmap)
	return 0;
      XSetWindowBackgroundPixmap (dpy, window, pixmap);
      XFreePixmap (dpy, pixmap);
      free (path);
      return 1;
    }
  return (0);
}

/* SetBackGradient - Rafal Wierzbicki 1998
 * sets WindowList gradient
 * returns 0 if failed, 1 otherwise
 */
int
SetBackGradient ()
{
  Pixmap pixmap = None;
  int from[3] =
  {0, 0, 0};
  int to[3] =
  {0, 0, 0};
  XColor tmp;

  if (XParseColor (dpy, DefaultColormap (dpy, screen),
		   grad_to, &tmp))
    {
      to[0] = tmp.red;
      to[2] = tmp.blue;
      to[1] = tmp.green;
    }
  else
    {
      fprintf (stderr, "No such colour: %s\n", grad_to);
      return (0);
    }

  if (XParseColor (dpy, DefaultColormap (dpy, screen),
		   grad_from, &tmp))
    {
      from[0] = tmp.red;
      from[2] = tmp.blue;
      from[1] = tmp.green;
    }
  else
    {
      fprintf (stderr, "No such colour: %s\n", grad_from);
      return (0);
    }

  pixmap = XCreatePixmap (dpy, Scr.Root, w_width, w_height,
			  Scr.d_depth);
  if (!pixmap)
    {
      fprintf (stderr, "SetBackGradient: could not create pixmap\n");
      return (0);
    }

  switch (grad_type)
    {
    case 1:
      if (!DrawDegradeRelief (dpy, pixmap, 0, 0, w_width, w_height,
			      from, to, 0, 128))
	{
	  fprintf (stderr, "SetBackGradient: DrawDegradeRelief failed\n");
	  XFreePixmap (dpy, pixmap);
	  return (0);
	}
      break;
    case 2:
      if (!DrawHGradient (dpy, pixmap, 0, 0, w_width, w_height,
			  from, to, 0, 128, 0))
	{
	  fprintf (stderr, "SetBackGradient: DrawHGradient failed\n");
	  XFreePixmap (dpy, pixmap);
	  return (0);
	}
      break;
    case 3:
      if (!DrawVGradient (dpy, pixmap, 0, 0, w_width, w_height,
			  from, to, 0, 128, 0))
	{
	  fprintf (stderr, "SetBackGradient: DrawVGradient failed\n");
	  XFreePixmap (dpy, pixmap);
	  return (0);
	}
      break;
    default:
      XFreePixmap (dpy, pixmap);
      return (0);
    }

  XSetWindowBackgroundPixmap (dpy, window, pixmap);
  XClearWindow (dpy, window);
  XFreePixmap (dpy, pixmap);
  return 1;
}

/* DrawStyledText - Rafal Wierzbicki 1998
 * based on src/afterstep/mystyle.c
 * draws 2 styles of 3d text or normal text
 * depending on the TextType global variable
 */
void
DrawStyledText (Window w, char *string, int posx, int posy)
{
  int len;

  len = strlen (string);

  switch (text_style)
    {
    case 1:
      XDrawString (dpy, window, relief_gc, posx - 2, posy - 2,
		   string, len);
      XDrawString (dpy, window, relief_gc, posx - 1, posy - 2,
		   string, len);
      XDrawString (dpy, window, relief_gc, posx + 0, posy - 2,
		   string, len);
      XDrawString (dpy, window, relief_gc, posx + 1, posy - 2,
		   string, len);
      XDrawString (dpy, window, relief_gc, posx + 2, posy - 2,
		   string, len);

      XDrawString (dpy, window, relief_gc, posx - 2,
		   posy - 1, string, len);
      XDrawString (dpy, window, relief_gc, posx - 2,
		   posy + 0, string, len);
      XDrawString (dpy, window, relief_gc, posx - 2,
		   posy + 1, string, len);
      XDrawString (dpy, window, relief_gc, posx - 2,
		   posy + 2, string, len);

      XDrawString (dpy, window, shadow_gc, posx + 2,
		   posy + 0, string, len);
      XDrawString (dpy, window, shadow_gc, posx + 2,
		   posy + 1, string, len);
      XDrawString (dpy, window, shadow_gc, posx + 2,
		   posy + 2, string, len);
      XDrawString (dpy, window, shadow_gc, posx + 1,
		   posy + 2, string, len);
      XDrawString (dpy, window, shadow_gc, posx + 0,
		   posy + 2, string, len);

      XDrawString (dpy, window, fore_gc, posx, posy,
		   string, len);
      break;

    case 2:
      XDrawString (dpy, window, shadow_gc, posx - 1,
		   posy - 1, string, len);
      XDrawString (dpy, window, relief_gc, posx, posy,
		   string, len);
      XDrawString (dpy, window, fore_gc, posx, posy,
		   string, len);
      break;

    default:
      XDrawString (dpy, window, fore_gc, posx, posy,
		   string, len);
      break;
    }
}

void 
UnHideWinList (void)
{
  XResizeWindow (dpy, window, w_width, w_height);
  hidden = 0;
}
void
HideWinList (void)
{
  XResizeWindow (dpy, window, w_width, 2);
  hidden = 1;
}


/******************************************************************************
  MakeMeWindow - Create and setup the window we will need
******************************************************************************/
void
MakeMeWindow (void)
{
  XSizeHints hints;
  XGCValues gcval;
  unsigned long gcmask;
  unsigned int dummy1, dummy2;
  int x, y, ret, count;
  Window dummyroot, dummychild;
  Pixel fore, back;

  if ((count = ItemCount (&windows)) == 0 && Transient)
    ShutMeDown (0);
  AdjustWindow ();

  hints.width = w_width;
  hints.height = w_height;
  hints.win_gravity = NorthWestGravity;
  hints.flags = PSize | PWinGravity | PResizeInc;
  hints.width_inc = 0;
  hints.height_inc = 0;

  if (geometry != NULL)
    {
      ret = XParseGeometry (geometry, &x, &y, &dummy1, &dummy2);

      if (ret & XValue && ret & YValue)
	{
	  hints.x = x;
	  if (ret & XNegative)
	    hints.x += XDisplayWidth (dpy, screen) - w_width;
	  hints.y = y;
	  if (ret & YNegative)
	    hints.y += XDisplayHeight (dpy, screen) - w_height;
	  hints.flags |= USPosition;
	}

      if (ret & XNegative)
	{
	  if (ret & YNegative)
	    hints.win_gravity = SouthEastGravity;
	  else
	    hints.win_gravity = NorthEastGravity;
	}
      else
	{
	  if (ret & YNegative)
	    hints.win_gravity = SouthWestGravity;
	  else
	    hints.win_gravity = NorthWestGravity;
	}

    }

  if (Transient)
    {
      XQueryPointer (dpy, Scr.Root, &dummyroot, &dummychild,
		     &hints.x, &hints.y, &x, &y, &dummy1);
      hints.win_gravity = NorthWestGravity;
      hints.flags |= USPosition;
    }
  win_grav = hints.win_gravity;
  win_x = hints.x;
  win_y = hints.y;

  if (Scr.d_depth < 2)
    {
      back = GetColor ("white");
      fore = GetColor ("black");
    }
  else
    {
      back = GetColor (BackColor);
      fore = GetColor (ForeColor);
    }

  window = XCreateSimpleWindow (dpy, Scr.Root, hints.x, hints.y,
				hints.width, hints.height, 1, fore, back);

  /* Rafal's additions, this could probably be done a lot better */
  if (back_pixmap)
    {
      if (!SetBackPixmap ())
	fprintf (stderr, "failed to set %s pixmap - %s\n", MyName,
		 back_pixmap);
    }
  else if (grad_from)
    {
      if (!SetBackGradient ())
	fprintf (stderr, "failed to set %s gradient - %s %s\n",
		 MyName, grad_from, grad_to);
    }
  /* done */

  wm_del_win = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
  XSetWMProtocols (dpy, window, &wm_del_win, 1);

  XSetWMNormalHints (dpy, window, &hints);

  if (!Transient)
    {
      XGrabButton (dpy, 1, AnyModifier, window, True, GRAB_EVENTS,
		   GrabModeAsync, GrabModeAsync, None, None);
      XGrabButton (dpy, 2, AnyModifier, window, True, GRAB_EVENTS,
		   GrabModeAsync, GrabModeAsync, None, None);
      XGrabButton (dpy, 3, AnyModifier, window, True, GRAB_EVENTS,
		   GrabModeAsync, GrabModeAsync, None, None);
      SetMwmHints (MWM_DECOR_ALL | MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE |
		   MWM_DECOR_MINIMIZE, MWM_FUNC_ALL | MWM_FUNC_RESIZE |
		 MWM_FUNC_MAXIMIZE | MWM_FUNC_MINIMIZE, MWM_INPUT_MODELESS);
    }
  else
    {
      SetMwmHints (0, MWM_FUNC_ALL, MWM_INPUT_MODELESS);
    }

  gcval.foreground = fore;
  gcval.background = back;
  gcval.font = font->fid;
  gcmask = GCForeground | GCBackground | GCFont;
  fore_gc = XCreateGC (dpy, Scr.Root, gcmask, &gcval);

  if (Scr.d_depth < 2)
    gcval.foreground = GetShadow (fore);
  else
    gcval.foreground = GetShadow (back);
  gcval.background = back;

  /* GC's modified to include fonts */

  if (text_style != 0)
    gcmask = GCForeground | GCBackground | GCFont;
  else
    gcmask = GCForeground | GCBackground;
  shadow_gc = XCreateGC (dpy, Scr.Root, gcmask, &gcval);

  gcval.foreground = GetHilite (back);
  gcval.background = back;

  if (text_style != 0)
    gcmask = GCForeground | GCBackground | GCFont;
  else
    gcmask = GCForeground | GCBackground;
  relief_gc = XCreateGC (dpy, Scr.Root, gcmask, &gcval);

  XSelectInput (dpy, window, (ExposureMask | KeyPressMask |
			      EnterWindowMask | LeaveWindowMask |
			      PointerMotionMask));

  ChangeWindowName (MyName);

  if (ItemCount (&windows) > 0)
    {
      XMapRaised (dpy, window);
      WaitForExpose ();
      WindowIsUp = 1;
      if (AutoHide)
	HideWinList ();
    }
  else
    WindowIsUp = 2;

  if (Transient)
    {
      if (XGrabPointer (dpy, window, True, GRAB_EVENTS, GrabModeAsync,
			GrabModeAsync,
			None, None, CurrentTime) != GrabSuccess)
	ShutMeDown (1);
      XQueryPointer (dpy, Scr.Root, &dummyroot, &dummychild,
		     &hints.x, &hints.y, &x, &y, &dummy1);
      if (!SomeButtonDown (dummy1))
	ShutMeDown (0);
    }

}

/******************************************************************************
  StartMeUp - Do X initialization things
******************************************************************************/
void
StartMeUp ()
{

#ifdef I18N
  XFontStruct **fs_list;
  char **ml;
  int mc;
  char *ds;
  char *fn_tmp;
  int fn_tmp_length;
#endif

#ifdef I18N
  if ((fontset = XCreateFontSet (dpy, font_string,
				 &ml, &mc, &ds)) == NULL)
    {
      fprintf (stderr, "Cannot get font %s\n", font_string);
      fn_tmp_length = strlen (font_string) + strlen (",-*--10-*");
      fn_tmp = malloc (fn_tmp_length + 1);
      strcpy (fn_tmp, font_string);
      strcat (fn_tmp, ",-*--10-*");
      fprintf (stderr, "Trying...%s\n", fn_tmp);
      if ((fontset = XCreateFontSet (dpy, fn_tmp,
				     &ml, &mc, &ds)) == NULL)
	{
	  fprintf (stderr, "Cannot get font %s\n", fn_tmp);
	  fprintf (stderr, "Trying...fixed\n");
	  if ((fontset = XCreateFontSet (dpy,
					 "fixed,-*--10-*",
					 &ml, &mc, &ds)) == NULL)
	    {
	      fprintf (stderr, "No fontset available\n");
	      free (fn_tmp);
	      exit (1);
	    }
	}
      free (fn_tmp);
    }
  XFontsOfFontSet (fontset, &fs_list, &ml);
  font = fs_list[0];
#else
  if ((font = XLoadQueryFont (dpy, font_string)) == NULL)
    {
      if ((font = XLoadQueryFont (dpy, "fixed")) == NULL)
	exit (1);
    }
#endif

  fontheight = font->ascent + font->descent;
  w_width = XTextWidth (font, "XXXXXXXXXXXXXXX", 10);
}

/******************************************************************************
  ShutMeDown - Do X client cleanup
******************************************************************************/
void
ShutMeDown (int exitstat)
{
  FreeList (&windows);
  FreeAllButtons (&buttons);
  XFreeGC (dpy, fore_gc);
  XFreeGC (dpy, shadow_gc);
  XFreeGC (dpy, relief_gc);

  if (WindowIsUp)
    XDestroyWindow (dpy, window);
  XCloseDisplay (dpy);
  exit (exitstat);
}

/******************************************************************************
  ChangeWindowName - Self explanitory
    Original work from Ident:
      Copyright 1994, Robert Nation and Nobutaka Suzuki.
******************************************************************************/
void
ChangeWindowName (char *str)
{
  XTextProperty name;
  if (XStringListToTextProperty (&str, 1, &name) == 0)
    {
      fprintf (stderr, "%s: cannot allocate window name.\n", MyName);
      return;
    }
  XSetWMName (dpy, window, &name);
  XSetWMIconName (dpy, window, &name);
  XFree (name.value);
}

/**************************************************************************
 *
 * Sets mwm hints 
 *
 *************************************************************************/
/* 
 *  Now, if we (hopefully) have MWW - compatible window manager ,
 *  say, mwm, ncdwm, or else, we will set useful decoration style.
 *  Never check for MWM_RUNNING property.May be considered bad.
 */
void
SetMwmHints (unsigned int value, unsigned int funcs, unsigned int input)
{
  PropMwmHints prop;

  if (MwmAtom == None)
    {
      MwmAtom = XInternAtom (dpy, "_MOTIF_WM_HINTS", False);
    }
  if (MwmAtom != None)
    {
      /* sh->mwm.decorations contains OR of the MWM_DECOR_XXXXX */
      prop.decorations = value;
      prop.functions = funcs;
      prop.inputMode = input;
      prop.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS |
	MWM_HINTS_INPUT_MODE;

      /* HOP - LA! */
      XChangeProperty (dpy, window,
		       MwmAtom, MwmAtom,
		       32, PropModeReplace,
		       (unsigned char *) &prop,
		       PROP_MWM_HINTS_ELEMENTS);
    }
}
