/****************************************************************************
 * This module is mostly all new
 * by Rob Nation 
 * A little of it is borrowed from ctwm.
 * Copyright 1993 Robert Nation. No restrictions are placed on this code,
 * as long as the copyright notice is preserved
 ****************************************************************************/
/***********************************************************************
 *
 * fvwm icon code
 *
 ***********************************************************************/

#include <FVWMconfig.h>

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#ifdef NeXT
#include <fcntl.h>
#endif

#include <X11/Intrinsic.h>
#ifdef XPM
#include <X11/xpm.h>
#endif /* XPM */
#include "fvwm.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#include "module.h"

#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif /* SHAPE */


void GrabIconButtons(FvwmWindow *, Window);
void GrabIconKeys(FvwmWindow *, Window);

/****************************************************************************
 *
 * Creates an icon window as needed
 *
 ****************************************************************************/
void CreateIconWindow(FvwmWindow *tmp_win, int def_x, int def_y)
{
  int final_x, final_y;
  unsigned long valuemask;		/* mask for create windows */
  XSetWindowAttributes attributes;	/* attributes for create windows */

  tmp_win->flags |= ICON_OURS;
  tmp_win->flags &= ~PIXMAP_OURS;
  tmp_win->flags &= ~SHAPED_ICON;
  tmp_win->icon_pixmap_w = None;
  tmp_win->iconPixmap = None;
  tmp_win->iconDepth = 0;
  
  if(tmp_win->flags & SUPPRESSICON)
    return;

  /* First, see if it was specified in the .fvwmrc */
  tmp_win->icon_p_height = 0;
  tmp_win->icon_p_width = 0;

  /* First, check for a monochrome bitmap */
  if(tmp_win->icon_bitmap_file != NULL)
    GetBitmapFile(tmp_win);

  /* Next, check for a color pixmap */
  if((tmp_win->icon_bitmap_file != NULL)&&
     (tmp_win->icon_p_height == 0)&&(tmp_win->icon_p_width == 0))
    GetXPMFile(tmp_win);

  /* Next, See if the app supplies its own icon window */
  if((tmp_win->icon_p_height == 0)&&(tmp_win->icon_p_width == 0)&&
     (tmp_win->wmhints) && (tmp_win->wmhints->flags & IconWindowHint))
    GetIconWindow(tmp_win);

  /* Finally, try to get icon bitmap from the application */
  if((tmp_win->icon_p_height == 0)&&(tmp_win->icon_p_width == 0)&&
	  (tmp_win->wmhints)&&(tmp_win->wmhints->flags & IconPixmapHint))
    GetIconBitmap(tmp_win);

  /* figure out the icon window size */
  if (!(tmp_win->flags & NOICON_TITLE)||(tmp_win->icon_p_height == 0))
    {
      if (tmp_win->icon_name == NULL)
        tmp_win->icon_name = tmp_win->name;
                 
      tmp_win->icon_t_width = XTextWidth(Scr.IconFont.font,
					 tmp_win->icon_name, 
                                         strlen(tmp_win->icon_name));
      tmp_win->icon_w_height = ICON_HEIGHT;
    }
  else
    {
      tmp_win->icon_t_width = 0;
      tmp_win->icon_w_height = 0;
    }
  if((tmp_win->flags & ICON_OURS)&&(tmp_win->icon_p_height >0))
    {
      tmp_win->icon_p_width += 4;
      tmp_win->icon_p_height +=4;
    }

  if(tmp_win->icon_p_width == 0)
    tmp_win->icon_p_width = tmp_win->icon_t_width+6;
  tmp_win->icon_w_width = tmp_win->icon_p_width;

  final_x = def_x;
  final_y = def_y;
  if(final_x <0)
    final_x = 0;
  if(final_y <0)
    final_y = 0;
  
  if(final_x + tmp_win->icon_w_width >=Scr.MyDisplayWidth)
    final_x = Scr.MyDisplayWidth - tmp_win->icon_w_width-1;
  if(final_y + tmp_win->icon_w_height >=Scr.MyDisplayHeight)
    final_y = Scr.MyDisplayHeight - tmp_win->icon_w_height-1;

  tmp_win->icon_x_loc = final_x;
  tmp_win->icon_xl_loc = final_x;
  tmp_win->icon_y_loc = final_y;

  /* clip to fit on screen */
  attributes.background_pixel = Scr.WinColors.back;
  valuemask =  CWBorderPixel | CWCursor | CWEventMask | CWBackPixel;
  attributes.border_pixel = Scr.WinColors.fore;
  attributes.cursor = Scr.FvwmCursors[DEFAULT];
  attributes.event_mask = (ButtonPressMask | ButtonReleaseMask | 
			   VisibilityChangeMask |
			   ExposureMask | KeyPressMask|EnterWindowMask |
			   FocusChangeMask );
  if (!(tmp_win->flags & NOICON_TITLE)||(tmp_win->icon_p_height == 0))
    tmp_win->icon_w = 
      XCreateWindow(dpy, Scr.Root, final_x, final_y+tmp_win->icon_p_height,
                    tmp_win->icon_w_width, tmp_win->icon_w_height,0,
                    CopyFromParent, 
                    CopyFromParent,CopyFromParent,valuemask,&attributes);
  
  if((tmp_win->flags & ICON_OURS)&&(tmp_win->icon_p_width>0)&&
     (tmp_win->icon_p_height>0))
    {
      tmp_win->icon_pixmap_w = 
	XCreateWindow(dpy, Scr.Root, final_x, final_y, tmp_win->icon_p_width,
		      tmp_win->icon_p_height, 0, CopyFromParent,
		      CopyFromParent,CopyFromParent,valuemask,&attributes);
    }
  else
    {
      attributes.event_mask = (ButtonPressMask | ButtonReleaseMask | 
			       VisibilityChangeMask |
			       KeyPressMask|EnterWindowMask |
			       FocusChangeMask | LeaveWindowMask );

      valuemask = CWEventMask;
      XChangeWindowAttributes(dpy,tmp_win->icon_pixmap_w,
			      valuemask,&attributes);
    }


#ifdef XPM
#ifdef SHAPE
  if (ShapesSupported && (tmp_win->flags & SHAPED_ICON))
    {
      XShapeCombineMask(dpy, tmp_win->icon_pixmap_w, ShapeBounding,2, 2,
			tmp_win->icon_maskPixmap, ShapeSet);
    }
#endif
#endif

  if(tmp_win->icon_w != None)
    {
      XSaveContext(dpy, tmp_win->icon_w, FvwmContext, (caddr_t)tmp_win);
      XDefineCursor(dpy, tmp_win->icon_w, Scr.FvwmCursors[DEFAULT]);
      GrabIconButtons(tmp_win,tmp_win->icon_w);
      GrabIconKeys(tmp_win,tmp_win->icon_w);
    }
  if(tmp_win->icon_pixmap_w != None)
    {
      XSaveContext(dpy, tmp_win->icon_pixmap_w, FvwmContext, (caddr_t)tmp_win);
      XDefineCursor(dpy, tmp_win->icon_pixmap_w, Scr.FvwmCursors[DEFAULT]);
      GrabIconButtons(tmp_win,tmp_win->icon_pixmap_w);
      GrabIconKeys(tmp_win,tmp_win->icon_pixmap_w);
    }
  return;
}

/****************************************************************************
 *
 * Draws the icon window
 *
 ****************************************************************************/
void DrawIconWindow(FvwmWindow *Tmp_win)
{
  GC Shadow, Relief;
  Pixel TextColor,BackColor;
  int x ;

  if(Tmp_win->flags & SUPPRESSICON)
    return;

  if(Tmp_win->icon_w != None)
    flush_expose (Tmp_win->icon_w);
  if(Tmp_win->icon_pixmap_w != None)
    flush_expose (Tmp_win->icon_pixmap_w);

  if(Scr.Hilite == Tmp_win)
    {
      if(Scr.d_depth < 2)
	Relief = Scr.WinShadowGC; /*was HiShadow!*/
      else
	Relief = Scr.WinReliefGC; /*was HiRelief!*/
      Shadow = Scr.WinShadowGC;
      /* resize the icon name window */
      if(Tmp_win->icon_w != None)
        {
          Tmp_win->icon_w_width = Tmp_win->icon_t_width+6;
          if(Tmp_win->icon_w_width < Tmp_win->icon_p_width)
            Tmp_win->icon_w_width = Tmp_win->icon_p_width;
          Tmp_win->icon_xl_loc = Tmp_win->icon_x_loc - 
            (Tmp_win->icon_w_width - Tmp_win->icon_p_width)/2;
        }
      TextColor = Scr.ActiveTitleColors.fore;
      BackColor = Scr.ActiveTitleColors.back;
    }
  else
    {
      if(Scr.d_depth < 2)
	{
	  Relief = Scr.WinReliefGC;
	  Shadow = Scr.WinShadowGC;
	}
      else
	{
	  Globalgcv.foreground = Tmp_win->TextPixel; /* ->ReliefPixel */
	  Globalgcm = GCForeground;
	  XChangeGC(dpy,Scr.ScratchGC1,Globalgcm,&Globalgcv); 
	  Relief = Scr.ScratchGC1;
	  
	  Globalgcv.foreground = Tmp_win->TextPixel; /* ->ShadowPixel */
	  XChangeGC(dpy,Scr.ScratchGC2,Globalgcm,&Globalgcv); 
	  Shadow = Scr.ScratchGC2;
	}
      /* resize the icon name window */
      if(Tmp_win->icon_w != None)
        {
          Tmp_win->icon_w_width = Tmp_win->icon_p_width;
          Tmp_win->icon_xl_loc = Tmp_win->icon_x_loc;
        }
      TextColor = Tmp_win->TextPixel;
      BackColor = Tmp_win->BackPixel;
	  
    }
  if((Tmp_win->flags & ICON_OURS)&&(Tmp_win->icon_pixmap_w != None))
    XSetWindowBackground(dpy,Tmp_win->icon_pixmap_w,
			 BackColor);
  if(Tmp_win->icon_w != None)
    XSetWindowBackground(dpy,Tmp_win->icon_w,BackColor);

  /* write the icon label */
  NewFontAndColor(Scr.IconFont.font->fid,TextColor,BackColor);

  if(Tmp_win->icon_pixmap_w != None)
    XMoveWindow(dpy,Tmp_win->icon_pixmap_w,Tmp_win->icon_x_loc,
                Tmp_win->icon_y_loc);
  if(Tmp_win->icon_w != None)
    {
      Tmp_win->icon_w_height = ICON_HEIGHT;
      XMoveResizeWindow(dpy, Tmp_win->icon_w, Tmp_win->icon_xl_loc,
                        Tmp_win->icon_y_loc+Tmp_win->icon_p_height,
                        Tmp_win->icon_w_width,ICON_HEIGHT);
      
      XClearWindow(dpy,Tmp_win->icon_w);
    }
  
  if((Tmp_win->iconPixmap != None)&&(!(Tmp_win->flags & SHAPED_ICON)))
    RelieveWindow(Tmp_win,Tmp_win->icon_pixmap_w,0,0,
		  Tmp_win->icon_p_width, Tmp_win->icon_p_height, 
		  Relief,Shadow, FULL_HILITE);  

  /* need to locate the icon pixmap */
  if(Tmp_win->iconPixmap != None)
    {
      if(Tmp_win->iconDepth == Scr.d_depth)
	{
	  XCopyArea(dpy,Tmp_win->iconPixmap,Tmp_win->icon_pixmap_w,Scr.ScratchGC3,
		    0,0,Tmp_win->icon_p_width-4, Tmp_win->icon_p_height-4,2,2);
	}
      else
	XCopyPlane(dpy,Tmp_win->iconPixmap,Tmp_win->icon_pixmap_w,Scr.ScratchGC3,0,
		   0,Tmp_win->icon_p_width-4, Tmp_win->icon_p_height-4,2,2,1);  
    }      

  if(Tmp_win->icon_w != None)
    {
      /* text position */
      x = (Tmp_win->icon_w_width - Tmp_win->icon_t_width)/2;
      if(x<3)x=3;
      
#ifdef I18N
#undef FONTSET
#define FONTSET Scr.IconFont.fontset
#endif

      XDrawString (dpy, Tmp_win->icon_w, Scr.ScratchGC3, x,
                   Tmp_win->icon_w_height-Scr.IconFont.height+
		   Scr.IconFont.y-3,
                   Tmp_win->icon_name, strlen(Tmp_win->icon_name));
      RelieveWindow(Tmp_win,Tmp_win->icon_w,0,0,Tmp_win->icon_w_width,      
                    ICON_HEIGHT,Relief,Shadow, FULL_HILITE);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	RedoIconName - procedure to re-position the icon window and name
 *
 ************************************************************************/
void RedoIconName(FvwmWindow *Tmp_win)
{

  if(Tmp_win->flags & SUPPRESSICON)
    return;

  if (Tmp_win->icon_w == (int)NULL)
    return;

  if (Tmp_win->icon_name == NULL)
   Tmp_win->icon_name = Tmp_win->name;
  
  Tmp_win->icon_t_width = XTextWidth(Scr.IconFont.font,Tmp_win->icon_name, 
				     strlen(Tmp_win->icon_name));
  /* clear the icon window, and trigger a re-draw via an expose event */
  if (Tmp_win->flags & ICONIFIED)
    XClearArea(dpy, Tmp_win->icon_w, 0, 0, 0, 0, True);
  return;
}



  
/***********************************************************************
 *
 *  Procedure:
 *	AutoPlace - Find a home for an icon
 *
 ************************************************************************/
void AutoPlace(FvwmWindow *t)
{
  int test_x=0, test_y=0,tw,th,tx,ty,temp_h,temp_w;
  int base_x, base_y;
  int width,height;
  FvwmWindow *test_window;
  Bool loc_ok;
  int real_x=10, real_y=10;
  int new_x, new_y;

  /* New! Put icon in same page as the center of the window */
  /* Not a good idea for StickyIcons */
  if((t->flags & StickyIcon)||(t->flags & STICKY))
    {
      base_x = 0;
      base_y = 0;
      /*Also, if its a stickyWindow, put it on the current page! */
      new_x = t->frame_x % Scr.MyDisplayWidth;
      new_y = t->frame_y % Scr.MyDisplayHeight;
      if(new_x < 0)new_x += Scr.MyDisplayWidth;
      if(new_y < 0)new_y += Scr.MyDisplayHeight;
      SetupFrame(t,new_x,new_y,
		 t->frame_width,t->frame_height,False);
      t->Desk = Scr.CurrentDesk;
    }
  else
    {
      base_x=((t->frame_x+Scr.Vx+(t->frame_width>>1))/Scr.MyDisplayWidth)*
	Scr.MyDisplayWidth - Scr.Vx;
      base_y=((t->frame_y+Scr.Vy+(t->frame_height>>1))/Scr.MyDisplayHeight)*
	Scr.MyDisplayHeight - Scr.Vy;
    }
  if(t->flags & ICON_MOVED)
    {
      /* just make sure the icon is on this screen */
      t->icon_x_loc = t->icon_x_loc % Scr.MyDisplayWidth + base_x;
      t->icon_y_loc = t->icon_y_loc % Scr.MyDisplayHeight + base_y;
      if(t->icon_x_loc < 0)
	t->icon_x_loc += Scr.MyDisplayWidth;
      if(t->icon_y_loc < 0)
	t->icon_y_loc += Scr.MyDisplayHeight;
    }
  else if (t->wmhints && t->wmhints->flags & IconPositionHint)
    {
      t->icon_x_loc = t->wmhints->icon_x;
      t->icon_y_loc = t->wmhints->icon_y;
    }
  else if(t->IconBox[0] >= 0)
    {
      width = t->icon_p_width;
      height = t->icon_w_height + t->icon_p_height;
      loc_ok = False;  
      
      /* check all boxes in order */
      /* In each IconBox, start at the upper left, travel right, then
       * down */
      test_y = t->IconBox[1]+base_y;
      
      temp_h = height;
      temp_w = width;
      
      /* OK second try at this.
       * If the window is taller than the icon box, ignore the icon height
       * when figuring where to put it. Same goes for the width */
      /* This should permit reasonably graceful handling of big icons. */
      if(width >= (t->IconBox[2] - t->IconBox[0]))
	    temp_w = 0;
	  if(height >= (t->IconBox[3] - t->IconBox[1]))
	    temp_h = 0;
	  
	  while(((test_y + temp_h) < (t->IconBox[3]+base_y))&&(!loc_ok))
	    {
	      test_x = t->IconBox[0]+base_x;
	      while(((test_x + temp_w) < (t->IconBox[2]+base_x))&&
		    (!loc_ok))
		{
		  real_x = test_x;
		  real_y = test_y;
		  
		  if(test_x + width > (Scr.MyDisplayWidth-2+base_x))
		    real_x = Scr.MyDisplayWidth - width -2 + base_x;
		  if(test_y + height > (Scr.MyDisplayHeight-2+base_y))
		    real_y = Scr.MyDisplayHeight - height -2+base_y;
		  if(test_x <base_x)
		    real_x = base_x;
		  if(test_y < base_y)
		    real_y = base_y;
		  loc_ok = True;
		  test_window = Scr.FvwmRoot.next;
		  while((test_window != (FvwmWindow *)0)&&(loc_ok == True))
		    {
		      if(test_window->Desk == t->Desk)
			{
			  if((test_window->flags&ICONIFIED)&&
                             (test_window->icon_w||test_window->icon_pixmap_w)&&
			     (test_window != t))
			    {
			      tw=test_window->icon_p_width;
			      th=test_window->icon_p_height+
				test_window->icon_w_height;
			      tx = test_window->icon_x_loc;
			      ty = test_window->icon_y_loc;
			  
			      if((tx<(real_x+width+3))&&((tx+tw+3) > real_x)&&
				 (ty<(real_y+height+3))&&((ty+th + 3)>real_y))
				{
				  loc_ok = False;
				}
			    }
			}
		      test_window = test_window->next;
		    }
		  test_x +=3;
		}
	      test_y +=3;
	    }
      if(loc_ok == False)
	return;
      t->icon_x_loc = real_x;
      t->icon_y_loc = real_y;
      
      if(t->icon_pixmap_w)
	XMoveWindow(dpy,t->icon_pixmap_w,t->icon_x_loc, t->icon_y_loc);
  
      t->icon_w_width = t->icon_p_width;
      t->icon_xl_loc = t->icon_x_loc;

      if (t->icon_w != None)
	XMoveResizeWindow(dpy, t->icon_w, t->icon_xl_loc,
			  t->icon_y_loc+t->icon_p_height, 
			  t->icon_w_width,ICON_HEIGHT);
      Broadcast(M_ICON_LOCATION,7,t->w,t->frame,
		(unsigned long)t,
		t->icon_x_loc,t->icon_y_loc,
		t->icon_w_width, t->icon_w_height+ t->icon_p_height);
    }
  
}

/***********************************************************************
 *
 *  Procedure:
 *	GrabIconButtons - grab needed buttons for the icon window
 *
 *  Inputs:
 *	tmp_win - the fvwm window structure to use
 *
 ***********************************************************************/
void GrabIconButtons(FvwmWindow *tmp_win, Window w)
{
  Binding *MouseEntry;

  MouseEntry = Scr.AllBindings;
  while(MouseEntry != (Binding *)0)
    {
      if((MouseEntry->Action != NULL)&&(MouseEntry->Context & C_ICON)&&
	 (MouseEntry->IsMouse == 1))
	{
	  if(MouseEntry->Button_Key >0)
	    XGrabButton(dpy, MouseEntry->Button_Key, MouseEntry->Modifier, w, 
			True, ButtonPressMask | ButtonReleaseMask,
			GrabModeAsync, GrabModeAsync, None, 
			Scr.FvwmCursors[DEFAULT]);
	  else
	    {
	      XGrabButton(dpy, 1, MouseEntry->Modifier, w,
			  True, ButtonPressMask | ButtonReleaseMask,
			  GrabModeAsync, GrabModeAsync, None, 
			  Scr.FvwmCursors[DEFAULT]);
	      XGrabButton(dpy, 2, MouseEntry->Modifier, w,
			  True, ButtonPressMask | ButtonReleaseMask,
			  GrabModeAsync, GrabModeAsync, None, 
			  Scr.FvwmCursors[DEFAULT]);
	      XGrabButton(dpy, 3, MouseEntry->Modifier, w,
			  True, ButtonPressMask | ButtonReleaseMask,
			  GrabModeAsync, GrabModeAsync, None, 
			  Scr.FvwmCursors[DEFAULT]);
	    }
	}
      
      MouseEntry = MouseEntry->NextBinding;
    }
  return;
}



/***********************************************************************
 *
 *  Procedure:
 *	GrabIconKeys - grab needed keys for the icon window
 *
 *  Inputs:
 *	tmp_win - the fvwm window structure to use
 *
 ***********************************************************************/
void GrabIconKeys(FvwmWindow *tmp_win,Window w)
{
  Binding *tmp;
  for (tmp = Scr.AllBindings; tmp != NULL; tmp = tmp->NextBinding)
    {
      if ((tmp->Context & C_ICON)&&(tmp->IsMouse == 0))
	XGrabKey(dpy, tmp->Button_Key, tmp->Modifier, w, True,
		 GrabModeAsync, GrabModeAsync);
    }
  return;
}


/****************************************************************************
 *
 * Looks for a monochrome icon bitmap file
 *
 ****************************************************************************/
void GetBitmapFile(FvwmWindow *tmp_win)
{
  char *path = NULL;
  int HotX,HotY;
  extern char *IconPath;

  path = findIconFile(tmp_win->icon_bitmap_file, IconPath,R_OK);

  if(path == NULL)return;
  if(XReadBitmapFile (dpy, Scr.Root,path,
		      (unsigned int *)&tmp_win->icon_p_width, 
		      (unsigned int *)&tmp_win->icon_p_height, 
		      &tmp_win->iconPixmap,
		      &HotX, &HotY) != BitmapSuccess)
    {
      tmp_win->icon_p_width = 0;
      tmp_win->icon_p_height = 0;
    }
    
  free(path);
}

/****************************************************************************
 *
 * Looks for a color XPM icon file
 *
 ****************************************************************************/
void GetXPMFile(FvwmWindow *tmp_win)
{
#ifdef XPM
  XWindowAttributes root_attr;
  XpmAttributes xpm_attributes;
  extern char *PixmapPath;
  char *path = NULL;

  path = findIconFile(tmp_win->icon_bitmap_file, PixmapPath,R_OK);
  if(path == NULL)return;  

  XGetWindowAttributes(dpy,Scr.Root,&root_attr);
  xpm_attributes.colormap = root_attr.colormap;
  xpm_attributes.closeness = 40000; /* Allow for "similar" colors */
  xpm_attributes.valuemask = XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness;

  if(XpmReadFileToPixmap(dpy, Scr.Root, path,
			 &tmp_win->iconPixmap, 
			 &tmp_win->icon_maskPixmap, 
			 &xpm_attributes) == XpmSuccess) 
    { 
      tmp_win->icon_p_width = xpm_attributes.width;
      tmp_win->icon_p_height = xpm_attributes.height;
      tmp_win->flags |= PIXMAP_OURS;
      tmp_win->iconDepth = Scr.d_depth;
#ifdef SHAPE
      if (ShapesSupported && tmp_win->icon_maskPixmap)
	tmp_win->flags |= SHAPED_ICON;
#endif
    } 
  free(path);
#endif /* XPM */
}

/****************************************************************************
 *
 * Looks for an application supplied icon window
 *
 ****************************************************************************/
void GetIconWindow(FvwmWindow *tmp_win)
{
  /* We are guaranteed that wmhints is non-null when calling this
   * routine */
  if(XGetGeometry(dpy,   tmp_win->wmhints->icon_window, &JunkRoot, 
		  &JunkX, &JunkY,(unsigned int *)&tmp_win->icon_p_width, 
		  (unsigned int *)&tmp_win->icon_p_height, 
		  &JunkBW, &JunkDepth)==0)
    {
      fvwm_msg(ERR,"GetIconWindow","Help! Bad Icon Window!");
    }
  tmp_win->icon_p_width += JunkBW<<1;
  tmp_win->icon_p_height += JunkBW<<1;
  /*
   * Now make the new window the icon window for this window,
   * and set it up to work as such (select for key presses
   * and button presses/releases, set up the contexts for it,
   * and define the cursor for it).
   */
  tmp_win->icon_pixmap_w = tmp_win->wmhints->icon_window;
#ifdef SHAPE
  if (ShapesSupported && (tmp_win->wmhints->flags & IconMaskHint))
    {
      tmp_win->flags |= SHAPED_ICON;
      tmp_win->icon_maskPixmap = tmp_win->wmhints->icon_mask;
    }
#endif
  /* Make sure that the window is a child of the root window ! */
  /* Olwais screws this up, maybe others do too! */
  XReparentWindow(dpy, tmp_win->icon_pixmap_w, Scr.Root, 0,0);
  tmp_win->flags &= ~ICON_OURS;
}


/****************************************************************************
 *
 * Looks for an application supplied bitmap or pixmap
 *
 ****************************************************************************/
void GetIconBitmap(FvwmWindow *tmp_win)
{
  /* We are guaranteed that wmhints is non-null when calling this
   * routine */
  XGetGeometry(dpy, tmp_win->wmhints->icon_pixmap, &JunkRoot, &JunkX, &JunkY,
	       (unsigned int *)&tmp_win->icon_p_width, 
	       (unsigned int *)&tmp_win->icon_p_height, &JunkBW, &JunkDepth);
  tmp_win->iconPixmap = tmp_win->wmhints->icon_pixmap;
  tmp_win->iconDepth = JunkDepth;
#ifdef SHAPE
  if (ShapesSupported && (tmp_win->wmhints->flags & IconMaskHint))
    {
      tmp_win->flags |= SHAPED_ICON;
      tmp_win->icon_maskPixmap = tmp_win->wmhints->icon_mask;
    }
#endif
}



/***********************************************************************
 *
 *  Procedure:
 *	DeIconify a window
 *
 ***********************************************************************/
void DeIconify(FvwmWindow *tmp_win)
{
  FvwmWindow *t,*tmp;

  if(!tmp_win)
    return;

  RaiseWindow(tmp_win);
  /* now de-iconify transients */
  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
    {
      if ((t == tmp_win)|| 
	  ((t->flags & TRANSIENT) &&(t->transientfor == tmp_win->w)))
	{
	  t->flags |= MAPPED;
	  if(Scr.Hilite == t)
	    SetBorder (t, False,True,True,None);

	  XMapWindow(dpy, t->w);
	  if(t->Desk == Scr.CurrentDesk)
	    {
	      XMapWindow(dpy, t->frame);
	      t->flags |= MAP_PENDING;
	    }
	  XMapWindow(dpy, t->Parent);
	  SetMapStateProp(t, NormalState);
	  t->flags &= ~ICONIFIED;
	  t->flags &= ~ICON_UNMAPPED;
	  /* Need to make sure the border is colored correctly,
	   * in case it was stuck or unstuck while iconified. */
	  tmp = Scr.Hilite;
	  Scr.Hilite = t;
	  SetBorder(t,False,True,True,None);
	  Scr.Hilite = tmp;
	  XRaiseWindow(dpy,t->w);
	  if (t->icon_w) 
	    XUnmapWindow(dpy, t->icon_w);
	  if (t->icon_pixmap_w) 
	    XUnmapWindow(dpy, t->icon_pixmap_w);
	  Broadcast(M_DEICONIFY,3,t->w,t->frame,(unsigned long)t,0,0,0,0);
	}
    }

  if(tmp_win->flags & ClickToFocus)
    FocusOn(tmp_win, 1, 1);

  KeepOnTop();

  return;
}


/****************************************************************************
 *
 * Iconifies the selected window
 *
 ****************************************************************************/
void Iconify(FvwmWindow *tmp_win, int def_x, int def_y)
{
  FvwmWindow *t;
  XWindowAttributes winattrs;
  unsigned long eventMask;

  if(!tmp_win)
    return;
  XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
  eventMask = winattrs.your_event_mask;

  if((tmp_win)&&(tmp_win == Scr.Hilite)&&
     (tmp_win->flags & ClickToFocus)&&(tmp_win->next))
    {
      SetFocus(tmp_win->next->w,tmp_win->next,1);
    }


  /* iconify transients first */
  for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
    {
      if ((t==tmp_win)||
	  ((t->flags & TRANSIENT) && (t->transientfor == tmp_win->w)))
	{
	  /*
	   * Prevent the receipt of an UnmapNotify, since that would
	   * cause a transition to the Withdrawn state.
	   */
	  t->flags &= ~MAPPED;
	  XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask);
          XUnmapWindow(dpy, t->w);
          XSelectInput(dpy, t->w, eventMask);
          XUnmapWindow(dpy, t->frame);
	  t->DeIconifyDesk = t->Desk;
	  if (t->icon_w)
	    XUnmapWindow(dpy, t->icon_w);
	  if (t->icon_pixmap_w)
	    XUnmapWindow(dpy, t->icon_pixmap_w);

	  SetMapStateProp(t, IconicState);
	  SetBorder (t, False,False,False,None);
	  if(t != tmp_win)
	    {
	      t->flags |= ICONIFIED|ICON_UNMAPPED;

	      Broadcast(M_ICONIFY,7,t->w,t->frame,
			(unsigned long)t,
			-10000, -10000,
			t->icon_w_width, 
			t->icon_w_height+t->icon_p_height);
	      BroadcastConfig(M_CONFIGURE_WINDOW,t);
	    }
	}
    } 
  if (tmp_win->icon_w == None)
    if(tmp_win->flags & ICON_MOVED)
      CreateIconWindow(tmp_win,tmp_win->icon_x_loc,tmp_win->icon_y_loc);
    else
      CreateIconWindow(tmp_win, def_x, def_y);

  AutoPlace(tmp_win);
  tmp_win->flags |= ICONIFIED;
  tmp_win->flags &= ~ICON_UNMAPPED;
  Broadcast(M_ICONIFY,7,tmp_win->w,tmp_win->frame,
	    (unsigned long)tmp_win,
	    tmp_win->icon_x_loc,tmp_win->icon_y_loc,
	    tmp_win->icon_w_width, 
	    tmp_win->icon_w_height+tmp_win->icon_p_height);
  BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win);

  LowerWindow(tmp_win);
  if(tmp_win->Desk == Scr.CurrentDesk)
    {
      if (tmp_win->icon_w != None)
        XMapWindow(dpy, tmp_win->icon_w);
      
      if(tmp_win->icon_pixmap_w != None)
	XMapWindow(dpy, tmp_win->icon_pixmap_w);
      KeepOnTop();
    }
  if((tmp_win->flags & ClickToFocus)||(tmp_win->flags & SloppyFocus))
    {
      if (tmp_win == Scr.Focus)
	{
	  if(Scr.PreviousFocus == Scr.Focus)
	    Scr.PreviousFocus = NULL;
	  if((tmp_win->flags & ClickToFocus)&&(tmp_win->next))
	    SetFocus(tmp_win->next->w, tmp_win->next,1);
	  else
	    {
	      SetFocus(Scr.NoFocusWin, NULL,1);
	    }
	}
    }
  return;
}



/****************************************************************************
 *
 * This is used to tell applications which windows on the screen are
 * top level appication windows, and which windows are the icon windows
 * that go with them.
 *
 ****************************************************************************/
void SetMapStateProp(FvwmWindow *tmp_win, int state)
{
  unsigned long data[2];		/* "suggested" by ICCCM version 1 */
  
  data[0] = (unsigned long) state;
  data[1] = (unsigned long) tmp_win->icon_w;
/*  data[2] = (unsigned long) tmp_win->icon_pixmap_w;*/
  
  XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32, 
		   PropModeReplace, (unsigned char *) data, 2);
  return;
}


syntax highlighted by Code2HTML, v. 0.9.1