/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
 
/***********************************************************************
 *
 *               Window Manager Hints
 *
 ***********************************************************************/
#define _EZ_WM_C_
#include "EZ_Widget.h"

void EZ_DeleteWidgetWMHints MY_ANSIARGS((EZ_Widget *widget));
void EZ_SetWidgetWMHints MY_ANSIARGS((EZ_Widget *wt, int opt, int i, int j, void *p));

void EZ_SetWMHintsAndSizeHints MY_ANSIARGS((EZ_Widget *widget,int flag));
void EZ_SetWMSizeHints MY_ANSIARGS((EZ_Widget *widget,int flag));
void EZ_SetWMHintForWindow MY_ANSIARGS((Window win, int x, int y, int w, int h));
int EZ_WidgetStartsAsIcon MY_ANSIARGS((EZ_Widget *w));
/***********************************************************************
 *
 *  If  EZ_WM_*** attributes are set on a toplevel window, a copy of
 *  the following data structure is allocated and attached the 
 *  corresponding widget.  This data structure is private to this
 *  file.
 */
typedef struct EZ_WMHint_ {
  /* name and the sort */
  char       *WM_Name;
  char       *WM_IconName;
  char       *WM_Command;
  /* geometry etc */
  int        initialState;
  int        sizeHintFlags;
  int        x,y, w, h, maxW, maxH, minW, minH;
  /* icon etc */
  int        iconX, iconY;
  EZ_Bitmap  *iconPixmap;  /* May not be an X Bitmap ! */
  Window     iconWindow;
} EZ_WMHints;
/*************************************************************************************/
static void iconEventHandler MY_ANSIARGS((EZ_Widget *w, void *d, int et, XEvent *event));
/*************************************************************************************/
static EZ_WMHints *EZ_AllocateWMHints()
{
  EZ_WMHints *hints = (EZ_WMHints *)my_malloc(sizeof(EZ_WMHints), _WM_HINTS_DATA_);
  if(hints == NULL) EZ_OutOfMemory("EZ_AllocateWMHints");
  memset(hints, 0, sizeof(EZ_WMHints));
  hints->initialState=NormalState;
  return(hints);
}
/***********************************************************************/
void EZ_DeleteWidgetWMHints(widget)
     EZ_Widget *widget;
{
  if(widget)
    {
      EZ_WMHints *hints = (EZ_WMHints *)EZ_WidgetWMHints(widget);
      if(hints)
	{
	  EZ_WidgetWMHints(widget) = NULL;

	  if(hints->WM_Name)     (void)my_free((char *)hints->WM_Name);
	  if(hints->WM_IconName) (void)my_free((char *)hints->WM_IconName);
	  if(hints->iconPixmap)  (void)EZ_FreeBitmap(hints->iconPixmap);
	  if(hints->iconWindow)
	    {
	      EZ_CallBackStruct **funcList=(EZ_CallBackStruct **)&EZ_NoWidgetEventHandlers;
	      EZ_RemoveCallBackWork(funcList, (EZ_CallBack)iconEventHandler, widget);
	      XDestroyWindow(EZ_Display, hints->iconWindow);
	    }
	  (void)my_free((char *)hints);
	}
    }
}
/***********************************************************************/
void EZ_SetWidgetWMHints(widget,option, i1,i2,p1)
     EZ_Widget *widget; int option, i1, i2; void *p1;
{
  if(widget && EZ_WidgetParent(widget) == NULL)
    {
      EZ_WMHints *hints = (EZ_WMHints *)EZ_WidgetWMHints(widget);
      if(hints == NULL)	
	{ 
	  hints = EZ_AllocateWMHints();
	  EZ_WidgetWMHints(widget) = (void *)hints;
	}

      switch(option)
	{
	case EZ_WM_INITIAL_STATE:
	  hints->initialState = i1;
	  break;
	case EZ_WMHINTS: 
	  hints->sizeHintFlags |= i1;
	  break;
	case EZ_WM_WINDOW_NAME:
	  if(p1) hints->WM_Name = EZ_ReallocCopyString(hints->WM_Name, p1);
	  break;
	case EZ_WM_ICON_NAME:
	  if(p1) hints->WM_IconName = EZ_ReallocCopyString(hints->WM_IconName, p1);
	  break;
	case EZ_WM_ICON_XPM_FILE:
	  if(p1)
	    {
	      char *file = (char *)p1;
	      EZ_Bitmap *bitmap = (file == (char *)NULL? (EZ_Bitmap *)NULL:
				   EZ_GetBitmapFromFile(file, 1));
	      if(!bitmap) bitmap =  EZ_GetImageFromFile(file, 1);
	      hints->iconPixmap = bitmap;
	    }
	  break;
	case EZ_WM_ICON_PIXMAP:
	  {
	    EZ_Bitmap *bitmap = (EZ_Bitmap *)p1;
	    if(p1) bitmap->ref++;
	    if(hints->iconPixmap) EZ_FreeBitmap(hints->iconPixmap);
	    hints->iconPixmap = bitmap;
	  }
	break;
	case EZ_WM_ICON_LOCATION:
	  hints->iconX = i1; hints->iconY = i2;
	  break;
	case EZ_WM_LOCATION_HINT:
	  hints->x = i1; hints->y = i2; 
	  hints->sizeHintFlags |= PPosition;
	  break;
	case EZ_WM_SIZE_HINT:
	  hints->w = i1; hints->h = i2;
	  hints->sizeHintFlags |= PSize;
	  break;	  
	case EZ_WM_MAX_SIZE_HINT:
	  hints->maxW = i1; hints->maxH = i2;
	  hints->sizeHintFlags |= PMaxSize;	  
	  break;
	case EZ_WM_MIN_SIZE_HINT:
	  hints->minW = i1; hints->minH = i2;
	  hints->sizeHintFlags |= PMinSize;	  
	  break;
	default:
	  break;
	}
      /* if widget window has been created, update immediately */
      if(EZ_WidgetWindow(widget) != None)
	EZ_SetWMHintsAndSizeHints(widget, 0);
    }
}
/***********************************************************************/
#ifndef ABSV 
#define ABSV(a) ((a) > 0? (a) : -(a))
#endif
void EZ_SetWMHintsAndSizeHints(widget, flag )
     EZ_Widget *widget;
     int flag;  /* whether or not to set the USPosition Hints */
{
  if(EZ_WindowExist(EZ_WidgetWindow(widget)))
    {
      XWMHints      *WMHints = XAllocWMHints();       /*  WM Hints. */
      EZ_WMHints    *hints = (EZ_WMHints *)EZ_WidgetWMHints(widget);
      char          *wmName = NULL, *iconName = NULL;
      XTextProperty textProp;

      /* initial state */
      if(flag & 256) WMHints->initial_state = IconicState; 
      else WMHints->initial_state = NormalState;
      /* always accept input */
      WMHints->input = True;
      /* dummyWindow is always the leader */
      WMHints->window_group = EZ_DummyWindow;

      WMHints->flags = StateHint  | InputHint | WindowGroupHint |StateHint;
      
      /* figure out icon hints */
      if(hints)
	{
	  if(WMHints->initial_state == NormalState && hints->initialState == IconicState)
	    WMHints->initial_state =  IconicState;
	  if(hints->WM_Name) wmName = hints->WM_Name;
	  if(hints->WM_IconName) iconName = hints->WM_IconName;	  
	  if(hints->iconX | hints->iconY)
	    {
	      WMHints->icon_x = hints->iconX;  WMHints->icon_y = hints->iconY;
	      WMHints->flags |= IconPositionHint;
	    }
	  if(hints->iconPixmap)
	    {
	      if(hints->iconWindow == None)
		{
		  int       w = hints->iconPixmap->width;
		  int       h = hints->iconPixmap->height;	      
		  int       status, i, count = 0;
		  int       w_set = w, h_set = h, diff=987654;
		  XIconSize *sizeList = NULL;
		  Display   *dpy = EZ_GetDisplay();
		  status = XGetIconSizes(dpy, DefaultRootWindow(dpy), &sizeList, &count);
		  if(status)
		    {
		      for(i = 0; i < count; i++)
			{
			  int mnw = sizeList[i].min_width;
			  int mnh = sizeList[i].min_height;
			  int mxw = sizeList[i].max_width;
			  int mxh = sizeList[i].max_height;
			  int winc = sizeList[i].width_inc;
			  int hinc = sizeList[i].height_inc;
			  int wtmp, htmp, dtmp;
			  if(w > mxw) wtmp = mxw;
			  else if(w < mnh) wtmp = mnh;
			  else
			    {
			      if(winc == 1) wtmp = w;
			      else wtmp = ((w - mnw + winc -1)/winc)*winc + mnw;
			    }
			  if(h > mxh) htmp = mxh;
			  else if(h < mnh) htmp = mnh;
			  else
			    {
			      if(hinc == 1) htmp = h;
			      else htmp = ((h - mnh + hinc -1)/hinc)*hinc + mnh;
			    }
			  dtmp = ABSV(w-wtmp) + ABSV(h-htmp);
			  if(dtmp < diff) {w_set = wtmp; h_set=htmp; diff = dtmp;}
			  if(dtmp == 0) break;
			}
		      if(sizeList) XFree((char *)sizeList);
		    }
		  /* now create icon window */
		  {
		    XSetWindowAttributes   setWAttr;
		    unsigned long          valuemask = 0;		    
		    setWAttr.colormap          = DefaultColormap(EZ_Display, EZ_ScreenNum);
		    setWAttr.background_pixel  = BlackPixel(EZ_Display, EZ_ScreenNum);
		    setWAttr.background_pixmap = None;
		    valuemask = CWBackPixel|CWColormap|CWBackPixmap;
		    
		    hints->iconWindow = 
		      XCreateWindow(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
				    hints->iconX, hints->iconY, w_set, h_set, /*geom */
				    0, DefaultDepth(EZ_Display, EZ_ScreenNum),
				    InputOutput,
				    DefaultVisual(EZ_Display, EZ_ScreenNum),
				    valuemask,
				    &setWAttr);
		    XSelectInput(EZ_Display, hints->iconWindow, ExposureMask);
		    if(EZ_XServerHasSHAPEExt)
		      {
			EZ_Bitmap *bitmap = hints->iconPixmap;
			if(bitmap->shape)
			  XShapeCombineMask(EZ_Display,  hints->iconWindow,
					    ShapeBounding, 0, 0,
					    bitmap->shape, ShapeSet);
		      }
		    /* add a NoWidget event handler */
		    {
		      EZ_CallBackStruct **funcList=(EZ_CallBackStruct **)&EZ_NoWidgetEventHandlers;
		      EZ_AddCallBackWork(funcList, (EZ_CallBack)iconEventHandler,(void *)widget, 0, 1);
		    }
		  }
		}
	    }
	  if(hints->iconWindow)
	    {
	      WMHints->icon_window = hints->iconWindow;
	      WMHints->flags |= IconWindowHint;
	    }
	}
      /* set WM_NAME */
      if(wmName == NULL) wmName = EZ_GetApplicationName();
      if( XStringListToTextProperty(&wmName, 1, &textProp))
	{
	  XSetWMName(EZ_Display, EZ_WidgetWindow(widget), &textProp);
	  XFree((char *)textProp.value);
        }
      /* set WM_ICON_NAME */
      if(iconName == NULL) iconName = wmName;
      XSetIconName(EZ_Display, EZ_WidgetWindow(widget), iconName);
      
      /* set normal hints */
      XSetWMHints(EZ_Display,EZ_WidgetWindow(widget),WMHints);
      XFree((char *)WMHints);
      /* set size hints */
      EZ_SetWMSizeHints(widget, flag);
    }
}
/************************************************************************/
void  EZ_SetWMSizeHints(widget, flag)
     EZ_Widget *widget; int flag;
{
  if(EZ_WindowExist(EZ_WidgetWindow(widget)))
    {
      XSizeHints   *sizeHints = XAllocSizeHints(); /* Size Hints.*/
      EZ_WMHints   *hints = (EZ_WMHints *)EZ_WidgetWMHints(widget);
      sizeHints->x = EZ_WidgetOriginX(widget);
      sizeHints->y = EZ_WidgetOriginY(widget);
      sizeHints->width = EZ_WidgetWidth(widget);
      sizeHints->height = EZ_WidgetHeight(widget);
      sizeHints->width_inc = 4;
      sizeHints->height_inc = 4;
      sizeHints->flags = PPosition|PSize|PResizeInc|USSize;
      if((flag & 1))  sizeHints->flags  |= USPosition;

      if(hints)
	 {
	   int flags = hints->sizeHintFlags;
	   if(flags &  PMinSize)
	     { 
	       if(hints->minW > 0)
		 {
		   sizeHints->min_width  = hints->minW;
		   sizeHints->min_height  = hints->minH;
		 }
	       else
		 {
		   sizeHints->min_width  = EZ_WidgetWidth(widget);
		   sizeHints->min_height = EZ_WidgetHeight(widget);
		 }
	     }
	   if(flags &  PMaxSize)
	     { 
	       if(hints->maxW > 0)
		 {
		   sizeHints->max_width  = hints->maxW;
		   sizeHints->max_height  = hints->maxH;
		 }
	       else
		 {
		   sizeHints->max_width  = EZ_WidgetWidth(widget);
		   sizeHints->max_height = EZ_WidgetHeight(widget);
		 }
	     }
	   if(flags &  PWinGravity) sizeHints->win_gravity  = CenterGravity;
	   sizeHints->flags |= flags;
	 }
      XSetWMNormalHints(EZ_Display,EZ_WidgetWindow(widget),sizeHints);
      XFree((char *)sizeHints);
    }
}
/***********************************************************************/
void  EZ_SetWMHintForWindow(window, x,y,w,h)
     Window window;
     int x,y,w,h;
{
  if(EZ_WindowExist(window))
    {
      XWMHints   *WMHints = XAllocWMHints();       /*  WM Hints. */
      XSizeHints   *sizeHints = XAllocSizeHints(); /* Size Hints.*/

      WMHints->initial_state = NormalState;
      WMHints->input = True;
      WMHints->flags = StateHint | InputHint ;
      
      XSetWMHints(EZ_Display,window,WMHints);
      XFree(WMHints);
	    
      sizeHints->x = x;
      sizeHints->y = y;
      sizeHints->width = w;
      sizeHints->height = h;
      sizeHints->width_inc = 4;
      sizeHints->height_inc = 4;
      sizeHints->flags = PPosition|PSize|PResizeInc|USSize|USPosition;
      XSetWMNormalHints(EZ_Display,window,sizeHints);
      XFree(sizeHints);
    }
}
/***********************************************************************/
static void iconEventHandler(nullW, data, zeroI, event)
     EZ_Widget *nullW;  /* always NULL */
     void      *data;   /* this is the widget */
     int       zeroI;   /* always 0 */
     XEvent    *event;
{
  if(event->type == Expose)
    {
      EZ_Widget *widget = (EZ_Widget *)data;
      Window win = event->xexpose.window;

      if(EZ_LookupWidgetFromAllHT(widget))
	{
	  EZ_WMHints *hints = (EZ_WMHints *)EZ_WidgetWMHints(widget);
	  if(hints && hints->iconWindow == win && hints->iconPixmap)
	    {
	      GC gc = DefaultGC(EZ_Display, EZ_ScreenNum);
	      EZ_Bitmap *bitmap = hints->iconPixmap;
	      
	      if(EZ_BitmapType(bitmap) == EZ_BITMAP_IS_BITMAP)
		XCopyPlane(EZ_Display, bitmap->pixmap, win, gc, 0, 0,
			   (unsigned int)(bitmap->width),
			   (unsigned int)(bitmap->height), 
			   0, 0, 1);
	      else
		XCopyArea(EZ_Display, bitmap->pixmap, win, gc, 0, 0,
			  (unsigned int)(bitmap->width),
			  (unsigned int)(bitmap->height),
			  0, 0);
	      return;
	    }
	}
    }
}
/***********************************************************************/
int EZ_WidgetStartsAsIcon(widget) EZ_Widget *widget;
{
  if(widget && EZ_WidgetParent(widget) == NULL)
    {
      EZ_WMHints *hints = (EZ_WMHints *)EZ_WidgetWMHints(widget);
      if(hints) return((hints->initialState == IconicState));
    }
  return(0);
}
/***********************************************************************/
#undef _EZ_WM_C_
