
/*
 * Copyright 1989 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/**********************************************************************
 *
 * $XConsortium: icons.c,v 1.22 91/07/12 09:58:38 dave Exp $
 *
 * Icon releated routines
 *
 * 10-Apr-89 Tom LaStrange        Initial Version.
 *
 **********************************************************************/

#include <stdio.h>
#include <string.h>
#include "twm.h"
#include "screen.h"
#include "regions.h"
#include "list.h"
#include "gram.h"
#include "parse.h"
#include "image_formats.h"
#include "util.h"
#include "prototypes.h"

extern void splitRegionEntry(RegionEntry * re, int grav1, int grav2, int w, int h);
extern int roundEntryUp(int v, int multiple);
extern RegionEntry *prevRegionEntry(RegionEntry * re, RootRegion * rr);
extern void mergeRegionEntries(RegionEntry * old, RegionEntry * re);
extern void downRegionEntry(RootRegion * rr, RegionEntry * re);
extern RootRegion *AddRegion(char *geom, int grav1, int grav2, int stepx, int stepy);
static void PlaceIcon(TwmWindow * tmp_win, int def_x, int def_y, int *final_x, int *final_y);
static Image *GetIconImage(char *name, Pixel background, unsigned int *numcolors);


#define iconWidth(w)	(Scr->IconBorderWidth * 2 + w->icon_w_width)
#define iconHeight(w)	(Scr->IconBorderWidth * 2 + w->icon_w_height)

static void
PlaceIcon(TwmWindow * tmp_win, int def_x, int def_y, int *final_x, int *final_y)
{
  RootRegion *rr;
  RegionEntry *re;
  int w, h;

  re = 0;
  for (rr = Scr->FirstIconRegion; rr; rr = rr->next)
  {
    w = roundEntryUp(iconWidth(tmp_win), rr->stepx);
    h = roundEntryUp(iconHeight(tmp_win), rr->stepy);
    for (re = rr->entries; re; re = re->next)
    {
      if (re->usedby)
	continue;
      if (re->w >= iconWidth(tmp_win) && re->h >= iconHeight(tmp_win))
	break;
    }
    if (re)
      break;
  }
  if (re)
  {
    splitRegionEntry(re, rr->grav1, rr->grav2, w, h);
    re->usedby = USEDBY_TWIN;
    re->u.twm_win = tmp_win;

    *final_x = re->x;
    *final_y = re->y;

    /* adjust for region gravity - djhjr 4/26/99 */
    if (rr->grav2 == D_EAST)
      *final_x += re->w - iconWidth(tmp_win);
    if (rr->grav1 == D_SOUTH)
      *final_y += re->h - iconHeight(tmp_win);

  }
  else
  {
    *final_x = def_x;
    *final_y = def_y;
  }
  return;
}

static RegionEntry *
FindIconEntry(TwmWindow * tmp_win, RootRegion ** rrp)
{
  RootRegion *rr;
  RegionEntry *re;

  for (rr = Scr->FirstIconRegion; rr; rr = rr->next)
  {
    for (re = rr->entries; re; re = re->next)
      if (re->u.twm_win == tmp_win)
      {
	if (rrp)
	  *rrp = rr;
	return re;
      }
  }
  return 0;
}

void
IconUp(TwmWindow * tmp_win)
{
  int x, y;
  int defx, defy;
  struct RootRegion *rr;

  /*
   * If the client specified a particular location, let's use it (this might
   * want to be an option at some point).  Otherwise, try to fit within the
   * icon region.
   */
  if (tmp_win->wmhints && (tmp_win->wmhints->flags & IconPositionHint))
    return;

  if (tmp_win->icon_moved)
  {
    if (!XGetGeometry(dpy, tmp_win->icon_w.win, &JunkRoot, &defx, &defy, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth))
      return;

    x = defx;
    y = defy;

    for (rr = Scr->FirstIconRegion; rr; rr = rr->next)
    {
      if (x >= rr->x && x < (rr->x + rr->w) && y >= rr->y && y < (rr->y + rr->h))
	break;
    }
    if (!rr)
      return;			/* outside icon regions, leave alone */
  }

  defx = -100;
  defy = -100;
  PlaceIcon(tmp_win, defx, defy, &x, &y);
  if (x != defx || y != defy)
  {
    XMoveWindow(dpy, tmp_win->icon_w.win, x, y);
    tmp_win->icon_moved = FALSE;	/* since we've restored it */
  }
}

void
IconDown(TwmWindow * tmp_win)
{
  RegionEntry *re;
  RootRegion *rr;

  re = FindIconEntry(tmp_win, &rr);
  if (re)
    downRegionEntry(rr, re);
}

void
AddIconRegion(char *geom, int grav1, int grav2, int stepx, int stepy)
{
  RootRegion *rr;

  rr = AddRegion(geom, grav1, grav2, stepx, stepy);

  if (Scr->LastIconRegion)
    Scr->LastIconRegion->next = rr;
  Scr->LastIconRegion = rr;
  if (!Scr->FirstIconRegion)
    Scr->FirstIconRegion = rr;
}

static Image *
GetIconImage(char *name, Pixel background, unsigned int *numcolors)
{
  Image *iconimage;
  Pixmap bm;
  unsigned int bitmap_height, bitmap_width;

  iconimage = (Image *) LookInNameList(Scr->Icons, name);
  if (iconimage == NULL)
  {
    bm = FindBitmap(name, &bitmap_width, &bitmap_height);
    if (bm != None)
    {
      iconimage = (Image *) malloc(sizeof(Image));
      iconimage->height = bitmap_height;
      iconimage->width = bitmap_width;
      iconimage->pixmap = XCreatePixmap(dpy, Scr->Root, bitmap_width, bitmap_height, Scr->d_depth);

      XGetGeometry(dpy, bm, &JunkRoot, &JunkX, &JunkY, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth);

      /*
       * XCopyArea() seems to be necessary for some apps that change
       * their icons - djhjr - rem'd 8/23/98, re-instated 11/15/98
       */
      if (JunkDepth == Scr->d_depth)
      {
	XCopyArea(dpy, bm, iconimage->pixmap, Scr->NormalGC, 0, 0, iconimage->width, iconimage->height, 0, 0);
	iconimage->mask = None;
      }
      else
      {
	XCopyPlane(dpy, bm, iconimage->pixmap, Scr->NormalGC, 0, 0, iconimage->width, iconimage->height, 0, 0, 1);
	iconimage->mask = XCreatePixmap(dpy, Scr->Root, iconimage->width, iconimage->height, 1);
	if (iconimage->mask != None)
	  XCopyPlane(dpy, bm, iconimage->mask, Scr->BitGC, 0, 0, iconimage->width, iconimage->height, 0, 0, 1);
      }

      XFreePixmap(dpy, bm);
    }
    else
    {
      iconimage = FindImage(name, background);
    }

    if (iconimage != NULL)
      AddToList(&Scr->Icons, name, LTYPE_EXACT_NAME, (char *)iconimage);
  }

  *numcolors = 0;
#ifndef NO_XPM_SUPPORT
  if (iconimage != NULL)
    *numcolors = SetPixmapsBackground(iconimage, Scr->Root, background);
#endif

  return (iconimage);
}

void
CreateIconWindow(TwmWindow * tmp_win, int def_x, int def_y)
{
  unsigned long event_mask;
  unsigned long valuemask;	/* mask for create windows */
  XSetWindowAttributes attributes;	/* attributes for create windows */
  Pixmap pm;			/* tmp pixmap variable */
  Image *iconimage;
  char *icon_name;
  int x, final_x, final_y;
  unsigned int pm_numcolors = 0;

  GetColorFromList(Scr->IconBorderColorL, tmp_win->full_name, &tmp_win->class, &tmp_win->icon_border);
  GetColorFromList(Scr->IconForegroundL, tmp_win->full_name, &tmp_win->class, &tmp_win->iconc.fore);
  GetColorFromList(Scr->IconBackgroundL, tmp_win->full_name, &tmp_win->class, &tmp_win->iconc.back);

  if (Scr->IconBevelWidth > 0 && !Scr->BeNiceToColormap)
    GetShadeColors(&tmp_win->iconc);

  FB(Scr, tmp_win->iconc.fore, tmp_win->iconc.back);

  tmp_win->forced = FALSE;
  tmp_win->icon_not_ours = FALSE;
  iconimage = NULL;
  pm = None;

  /*
   * now go through the steps to get an icon window,  if ForceIcon is
   * set, then no matter what else is defined, the bitmap from the
   * .vtwmrc file is used
   */
  if (Scr->ForceIcon)
  {
    icon_name = LookInNameList(Scr->IconNames, tmp_win->full_name);
    if (icon_name == NULL)
      icon_name = LookInList(Scr->IconNames, tmp_win->full_name, &tmp_win->class);

    if (icon_name != NULL)
      iconimage = GetIconImage(icon_name, tmp_win->iconc.back, &pm_numcolors);

    if (iconimage != NULL)
    {
      tmp_win->icon_width = iconimage->width;
      tmp_win->icon_height = iconimage->height;

      pm = iconimage->pixmap;
      tmp_win->forced = TRUE;
    }
  }

  /*
   * if the pixmap is still NULL, we didn't get one from the above code,
   * that could mean that ForceIcon was not set, or that the window
   * was not in the Icons list, now check the WM hints for an icon
   */

  if (pm == None && tmp_win->wmhints && tmp_win->wmhints->flags & IconPixmapHint)
  {
    XGetGeometry(dpy, tmp_win->wmhints->icon_pixmap, &JunkRoot, &JunkX, &JunkY, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth);

    pm_numcolors = 3;

    iconimage = (Image *) malloc(sizeof(Image)); /* it looks like as if we leak this memory */
    iconimage->mask = None;
    iconimage->width = JunkWidth;
    iconimage->height = JunkHeight;
    iconimage->pixmap = XCreatePixmap(dpy, Scr->Root, iconimage->width, iconimage->height, Scr->d_depth);

    if (JunkDepth == Scr->d_depth)
      XCopyArea(dpy, tmp_win->wmhints->icon_pixmap, iconimage->pixmap,
		Scr->NormalGC, 0, 0, iconimage->width, iconimage->height, 0, 0);
    else
      XCopyPlane(dpy, tmp_win->wmhints->icon_pixmap, iconimage->pixmap,
		 Scr->NormalGC, 0, 0, iconimage->width, iconimage->height, 0, 0, 1);

    if ((tmp_win->wmhints->flags & IconMaskHint) &&
	XGetGeometry(dpy, tmp_win->wmhints->icon_mask,
		     &JunkRoot, &JunkX, &JunkY, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) && JunkDepth == 1)
    {
      iconimage->mask = XCreatePixmap(dpy, Scr->Root, JunkWidth, JunkHeight, 1);
      if (iconimage->mask != None)
	  XCopyPlane(dpy, tmp_win->wmhints->icon_mask, iconimage->mask, Scr->BitGC, 0, 0, JunkWidth, JunkHeight, 0, 0, 1);
    }

    if (iconimage != NULL)
    {
      tmp_win->icon_width = iconimage->width;
      tmp_win->icon_height = iconimage->height;

      pm = iconimage->pixmap;
    }
  }

  /*
   * if we still haven't got an icon, let's look in the Icon list
   * if ForceIcon is not set
   */
  if ((pm == None) && (!Scr->ForceIcon))
  {
    icon_name = LookInNameList(Scr->IconNames, tmp_win->full_name);
    if (icon_name == NULL)
      icon_name = LookInList(Scr->IconNames, tmp_win->full_name, &tmp_win->class);

    if (icon_name != NULL)
      iconimage = GetIconImage(icon_name, tmp_win->iconc.back, &pm_numcolors);

    if (iconimage != NULL)
    {
      tmp_win->icon_width = iconimage->width;
      tmp_win->icon_height = iconimage->height;

      pm = iconimage->pixmap;
    }
  }

  if (pm == None && Scr->unknownName != NULL)
  {
    iconimage = GetIconImage(Scr->unknownName, tmp_win->iconc.back, &pm_numcolors);

    if (iconimage != NULL)
    {
      tmp_win->icon_width = iconimage->width;
      tmp_win->icon_height = iconimage->height;

      pm = iconimage->pixmap;
    }
  }

  if (pm == None)
  {
    tmp_win->icon_height = 0;
    tmp_win->icon_width = 0;
    valuemask = 0;
  }
  else
  {
    valuemask = CWBackPixmap;
    attributes.background_pixmap = pm;

    if ((iconimage->type == IMAGE_TYPE_XPM) && (pm_numcolors <= 2))	/* not a pixmap */
    {
      valuemask |= CWBackPixel;
      attributes.background_pixel = tmp_win->iconc.fore;
    }
  }

  tmp_win->icon_w_width = MyFont_TextWidth(&Scr->IconFont, tmp_win->icon_name, strlen(tmp_win->icon_name));

#ifdef TWM_USE_SPACING
  tmp_win->icon_w_width += Scr->IconFont.height;	/*approx. '1ex' on both sides */
#else
  tmp_win->icon_w_width += 8;
#endif
  if (tmp_win->icon_w_width < tmp_win->icon_width + 8)
  {
    tmp_win->icon_x = (((tmp_win->icon_width + 8) - tmp_win->icon_w_width) / 2) + 4;
    tmp_win->icon_w_width = tmp_win->icon_width + 8;
  }
  else
#ifdef TWM_USE_SPACING
    tmp_win->icon_x = Scr->IconFont.height / 2;
#else
    tmp_win->icon_x = 4;
#endif

#ifdef TWM_USE_SPACING
  /* icon label height := 1.44 times font height: */
  tmp_win->icon_w_height = tmp_win->icon_height + 144 * Scr->IconFont.height / 100;
  tmp_win->icon_y = tmp_win->icon_height + Scr->IconFont.y + 44 * Scr->IconFont.height / 200;
#else
  tmp_win->icon_w_height = tmp_win->icon_height + Scr->IconFont.height + 8;
  tmp_win->icon_y = tmp_win->icon_height + Scr->IconFont.height + 2;
#endif

  event_mask = 0;
  if (tmp_win->wmhints && tmp_win->wmhints->flags & IconWindowHint)
  {
    tmp_win->icon_w.win = tmp_win->wmhints->icon_window;
    if (tmp_win->forced ||
	XGetGeometry(dpy, tmp_win->icon_w.win, &JunkRoot, &JunkX, &JunkY,
		     (unsigned int *)&tmp_win->icon_w_width, (unsigned int *)&tmp_win->icon_w_height, &JunkBW, &JunkDepth) == 0)
    {
      tmp_win->icon_w.win = None;
      tmp_win->wmhints->flags &= ~IconWindowHint;
    }
    else
    {
      tmp_win->icon_not_ours = TRUE;
      event_mask = EnterWindowMask | LeaveWindowMask;
    }
  }
  else
  {
    tmp_win->icon_w.win = None;
  }

  if (Scr->IconBevelWidth > 0)
  {
    tmp_win->icon_w_width += 2 * Scr->IconBevelWidth;
    tmp_win->icon_w_height += 2 * Scr->IconBevelWidth;

    tmp_win->icon_x += Scr->IconBevelWidth;
    tmp_win->icon_y += Scr->IconBevelWidth;
  }

  if (tmp_win->icon_w.win == None)
  {
    tmp_win->icon_w.win = XCreateSimpleWindow(dpy, Scr->Root,
					      0, 0,
					      tmp_win->icon_w_width, tmp_win->icon_w_height,
					      Scr->IconBorderWidth, tmp_win->icon_border, tmp_win->iconc.back);
    event_mask = ExposureMask;
#ifdef TWM_USE_XFT
    if (Scr->use_xft > 0)
      tmp_win->icon_w.xft = MyXftDrawCreate(tmp_win->icon_w.win);
#endif
#ifdef TWM_USE_OPACITY
    SetWindowOpacity(tmp_win->icon_w.win, Scr->IconOpacity);
#endif
  }

  XSelectInput(dpy, tmp_win->icon_w.win, KeyPressMask | ButtonPressMask | ButtonReleaseMask | event_mask);

  tmp_win->icon_bm_w = None;
  if (pm != None && (!(tmp_win->wmhints && tmp_win->wmhints->flags & IconWindowHint)))
  {
    int y;

    y = 4;

#ifdef TWM_USE_SPACING
    tmp_win->icon_w_height += y;
    tmp_win->icon_y += y;
    XResizeWindow(dpy, tmp_win->icon_w.win, tmp_win->icon_w_width, tmp_win->icon_w_height);
#endif

    if (tmp_win->icon_w_width == tmp_win->icon_width)
      x = 0;
    else
      x = (tmp_win->icon_w_width - tmp_win->icon_width) / 2;

    if (Scr->IconBevelWidth > 0)
      y += Scr->IconBevelWidth;

    tmp_win->icon_bm_w = XCreateWindow(dpy, tmp_win->icon_w.win, x, y,
				       (unsigned int)tmp_win->icon_width,
				       (unsigned int)tmp_win->icon_height,
				       (unsigned int)0, Scr->d_depth,
				       (unsigned int)CopyFromParent, Scr->d_visual, valuemask, &attributes);

    if (HasShape)
      if (tmp_win->icon_not_ours != TRUE)
	if (iconimage != NULL && iconimage->mask != None)
	  XShapeCombineMask(dpy, tmp_win->icon_bm_w, ShapeBounding, 0, 0, iconimage->mask, ShapeSet);
  }

  /* I need to figure out where to put the icon window now, because
   * getting here means that I am going to make the icon visible
   */
  if (tmp_win->wmhints && tmp_win->wmhints->flags & IconPositionHint)
  {
    final_x = tmp_win->wmhints->icon_x;
    final_y = tmp_win->wmhints->icon_y;
  }
  else
  {
    PlaceIcon(tmp_win, def_x, def_y, &final_x, &final_y);
  }

  if (final_x > Scr->MyDisplayWidth)
    final_x = Scr->MyDisplayWidth - tmp_win->icon_w_width - (2 * Scr->IconBorderWidth);

  if (final_y > Scr->MyDisplayHeight)
    final_y = Scr->MyDisplayHeight - tmp_win->icon_height - Scr->IconFont.height - 4 - (2 * Scr->IconBorderWidth);

  XMoveWindow(dpy, tmp_win->icon_w.win, final_x, final_y);
  tmp_win->iconified = TRUE;

  XMapSubwindows(dpy, tmp_win->icon_w.win);
  XSaveContext(dpy, tmp_win->icon_w.win, TwmContext, (caddr_t) tmp_win);
  XSaveContext(dpy, tmp_win->icon_w.win, ScreenContext, (caddr_t) Scr);
  XDefineCursor(dpy, tmp_win->icon_w.win, Scr->IconCursor);

  return;
}


/*
  Local Variables:
  mode:c
  c-file-style:"GNU"
  c-file-offsets:((substatement-open 0)(brace-list-open 0)(c-hanging-comment-ender-p . nil)(c-hanging-comment-beginner-p . nil)(comment-start . "// ")(comment-end . "")(comment-column . 48))
  End:
*/
/* vim: sw=2
*/
