/*
**  EPIwm
**  Copyright (C) 1999 Julien Mulot, Thomas Poindessous, Stephane Rault
**
**  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** window.c
** 
** Made by (Julien Mulot)
** Login   <mulot_j@epita.fr>
** 
** Started on  Fri Jan  8 21:13:01 1999 Julien Mulot
 ** Last update Mon Oct 23 22:18:59 2000 Julien Mulot
*/

#include "epiwm.h"
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>

int		test_shape(Window	win)
{
  int		xws;
  int		yws;
  int		xbs;
  int		ybs;
  unsigned int	wws;
  unsigned int	hws;
  unsigned int	wbs;
  unsigned int	hbs;
  int		boundingShaped;
  int		clipShaped;
  
  XShapeSelectInput(gl_dpy, win, ShapeNotifyMask);
  XShapeQueryExtents(gl_dpy, win, &boundingShaped, &xws, &yws, &wws,
		     &hws, &clipShaped, &xbs, &ybs, &wbs, &hbs);
  return (boundingShaped);
}

void			add_shape(t_win	*ewin)
{
  XRectangle		rect[4];
  XWindowAttributes	attrib;
  unsigned short	nb_rect;
  
  nb_rect = 4;
  XGetWindowAttributes(gl_dpy, ewin->parent, &attrib);
  rect[0].x = 0;
  rect[0].y = 0;
  rect[0].width = attrib.width ;
  rect[0].height = ewin->border_size + ewin->titlebar_height +
    ewin->titlebar_border*2;
  rect[1].x = 0;
  rect[1].y = 0;
  rect[1].width = ewin->border_size + ewin->bf_border;
  rect[1].height = attrib.height;
  rect[2].x = attrib.width - ewin->border_size - ewin->bf_border;
  rect[2].y = 0;
  rect[2].width = ewin->border_size + ewin->bf_border;
  rect[2].height = attrib.height;
  rect[3].x = 0;
  rect[3].y = attrib.height - ewin->border_size - ewin->bf_border;
  rect[3].width = attrib.width;
  rect[3].height = ewin->border_size + ewin->bf_border;   
  XGetWindowAttributes(gl_dpy, ewin->win, &attrib);
  XShapeCombineShape(gl_dpy, ewin->parent, ShapeBounding,
		     ewin->border_size + attrib.border_width
		     + ewin->bf_border,
		     ewin->border_size + attrib.border_width
		     + ewin->titlebar_height
		     + 2*ewin->titlebar_border,
		     ewin->win, ShapeBounding, ShapeSet);
  if (ewin->border_size != 0 && ewin->titlebar_height != 0)
    XShapeCombineRectangles(gl_dpy, ewin->parent, ShapeBounding,
			    0, 0, rect, nb_rect,
			    ShapeUnion, Unsorted);     
}

t_win			*add_border(Window	win)
{     
  t_win			*ewin;
  XWindowAttributes	attrib;
  XSetWindowAttributes	attr;  
  char			*name;
  XWMHints		*wmhints;
  Window		transient;
  
  XGetWindowAttributes(gl_dpy, win, &attrib);
  if (!attrib.override_redirect)
    {
      ewin = (t_win *) xmalloc(sizeof (t_win));
      ewin->win = win;
      XSetWindowBorderWidth(gl_dpy, win, 0);
      XGetWindowAttributes(gl_dpy, win, &attrib);
      XAddToSaveSet(gl_dpy, win);
      XGrabServer(gl_dpy);
      XUnmapWindow(gl_dpy, win);
      XFetchName(gl_dpy, win, &name);
      check_app_prop(ewin, name);
      motif_check(ewin);
      check_properties(ewin);
      ewin->parent = add_parent(ewin, &attrib);
      ewin->border_frame = add_border_frame(ewin, ewin->parent);
      ewin->titlebar_win = add_titlebar_win(ewin, ewin->parent);
      XStoreName(gl_dpy, ewin->parent, name);
      if ((ewin->prop & NO_TITLE) == 0)
	XStoreName(gl_dpy, ewin->titlebar_win, name);
      free(name);
      XGetIconName(gl_dpy, win, &name);
      XSetIconName(gl_dpy, ewin->parent, name);
      free(name);
      if (gl_epiwin.align_btn_max)
	ewin->button_max = build_button(ewin,
					gl_epiwin.align_btn_max,
					gl_epiwin.pos_btn_max);
      if (gl_epiwin.align_btn_max_height)
	ewin->button_max_height = 
	  build_button(ewin, 
		       gl_epiwin.align_btn_max_height,
		       gl_epiwin.pos_btn_max_height);
      if (gl_epiwin.align_btn_max_width)
	ewin->button_max_width = build_button(ewin,
					      gl_epiwin.align_btn_max_width,
					      gl_epiwin.pos_btn_max_width);
      
      if (gl_epiwin.align_btn_iconify)
	ewin->button_iconify = build_button(ewin,
					    gl_epiwin.align_btn_iconify,
					    gl_epiwin.pos_btn_iconify);
      if (gl_epiwin.align_btn_shade)
	ewin->button_shade = build_button(ewin,
					  gl_epiwin.align_btn_shade,
					  gl_epiwin.pos_btn_shade);
      if (gl_epiwin.align_btn_close)
	ewin->button_close = build_button(ewin,
					  gl_epiwin.align_btn_close,
					  gl_epiwin.pos_btn_close);
      if (gl_epiwin.align_btn_destroy)
	ewin->button_destroy = build_button(ewin,
					    gl_epiwin.align_btn_destroy,
					    gl_epiwin.pos_btn_destroy);
      add_elt(ewin);
      attr.event_mask = PropertyChangeMask | ResizeRedirectMask
	| StructureNotifyMask | KeyPressMask | PointerMotionMask;
      attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
      XChangeWindowAttributes(gl_dpy, ewin->win,
			      CWEventMask | CWDontPropagate, &attr);
      XReparentWindow(gl_dpy, win, ewin->border_frame,
		      ewin->border_size, ewin->border_size);
      grab_all_shortcuts(win);
      wmhints = XGetWMHints(gl_dpy, win);
      if (gl_epiwin.prop & NEW_AUTO_FOCUS)
	window_state(ewin, "active");
      else
	window_state(ewin, "inactive");
      if (wmhints)
	{
	  if ((wmhints->flags & StateHint) &&
	      (wmhints->initial_state == IconicState))
	    win_iconify(ewin, 1, 0);
	  else if (attrib.map_state == IsViewable)
	    {
	      if ((gl_epiwin.prop & SMART_PLACE)
		  && (!(gl_epiwin.prop & FIRST_START)))
		placement_smart(ewin);
	      window_group_map(ewin);
	      if (gl_epiwin.prop & NEW_AUTO_FOCUS)
		window_state(ewin, "active");
	      else
		window_state(ewin, "inactive");
	      catch_win_mouse();
	    }
	  XFree(wmhints);
	}
      else if (attrib.map_state == IsViewable)
	{
	  if ((gl_epiwin.prop & SMART_PLACE)
	      && (!(gl_epiwin.prop & FIRST_START)))
	    placement_smart(ewin);
	  window_group_map(ewin);
	  if (gl_epiwin.prop & NEW_AUTO_FOCUS)
	    window_state(ewin, "active");
	  else
	    window_state(ewin, "inactive");
	  catch_win_mouse();
	}
      if (XGetTransientForHint(gl_dpy, ewin->win, &transient))
	{
	  if (transient != gl_scr_info.root_win)
	    map_request(transient);
	}
      XUngrabServer(gl_dpy);
      XSync(gl_dpy, 1);
      return (ewin);
    }
  else
    return (NULL);
}

void			handle_window(Window	win)
{
  XWindowAttributes	attrib;
  
  XGetWindowAttributes(gl_dpy, win, &attrib);
  if (attrib.map_state == IsViewable)
    add_border(win);
}

t_win		*create_win(Window	win)
{
  t_win		*ewin;
  
  ewin = add_border(win);
  return (ewin);
}

Window			add_parent(t_win		*ewin,
				   XWindowAttributes	*attrib)
{
  Window		temp;
  XSetWindowAttributes	attr;
  int			width;
  int			height;
  
  width = 0;
  height = 0;
  if (ewin->prop & NO_TITLE)
    {
      width = attrib->width + (ewin->border_size + ewin->bf_border) * 2;
      height = attrib->height + (ewin->border_size + ewin->bf_border) * 2;
    }
  else if (gl_epiwin.titlebar.position == TOP
	   || gl_epiwin.titlebar.position == BOTTOM)
    {
      width = attrib->width + (ewin->border_size + ewin->bf_border) * 2;
      height = attrib->height +	(ewin->border_size + ewin->titlebar_border)*2
	+ ewin->titlebar_height + ewin->bf_border;
    }
  else if (gl_epiwin.titlebar.position == LEFT
	   || gl_epiwin.titlebar.position == RIGHT)
    {
      width = attrib->width + ewin->titlebar_height + ewin->titlebar_border * 2
	+ ewin->border_size * 2 + ewin->bf_border;
      height = attrib->height +	(ewin->border_size + ewin->bf_border)*2;
    }
  temp = XCreateSimpleWindow(gl_dpy,
			     gl_scr_info.root_win,
			     attrib->x, attrib->y, width, height, 0,
			     BlackPixel(gl_dpy, gl_scr_info.screen),
			     WhitePixel(gl_dpy, gl_scr_info.screen));
  attr.event_mask = /*ExposureMask | */EnterWindowMask | LeaveWindowMask
    | ButtonPressMask | ButtonReleaseMask | SubstructureNotifyMask
    | PropertyChangeMask | SubstructureRedirectMask | KeyPressMask;
  XChangeWindowAttributes(gl_dpy, temp , CWEventMask, &attr);
  return (temp);
}

Window			build_button(t_win	*ewin,
				     char	align,
				     int	pos)
{
  t_window_btn		tmp;
  XSetWindowAttributes	attrib;  
  Window		w_tmp;
  
  if (ewin->prop & NO_TITLE)
    return (0);
  if ((gl_epiwin.titlebar.position == TOP) 
      || (gl_epiwin.titlebar.position == BOTTOM))
    {
      tmp.x = set_btn_position(ewin, align, pos);
      tmp.y = (ewin->titlebar_height - gl_epiwin.btn_height) / 2;
    }
  else
    {
      tmp.x = (ewin->titlebar_height - gl_epiwin.btn_width) / 2;
      tmp.y = set_btn_position(ewin, align, pos);
    }
  tmp.width = gl_epiwin.btn_width;
  tmp.height = gl_epiwin.btn_height ;
  tmp.bgcolor = BlackPixel(gl_dpy, gl_scr_info.screen);
  tmp.fgcolor = gl_epiwin.titlebar.color_active.pixel;
  tmp.parent = ewin->titlebar_win;
  tmp.cursor = gl_epiwin.normal_cursor;
  tmp.border = 0;
  w_tmp = add_btn(&tmp);
  attrib.event_mask = ButtonPressMask | ButtonReleaseMask
    /* | ExposureMask */;
  XChangeWindowAttributes(gl_dpy, w_tmp, CWEventMask, &attrib);
  return (w_tmp);
}

Window			add_border_frame(t_win	*ewin,
					 Window	parent)
{
  t_window_btn		tmp;
  XWindowAttributes	attrib;
  XSetWindowAttributes	attr;
  Window		w_tmp;
  int			width;
  int			height;
  int			x;
  int			y;
  
  x = 0;
  y = 0;
  width = 0;
  height = 0;
  XGetWindowAttributes(gl_dpy, parent, &attrib);
  if (ewin->prop & NO_TITLE)
    {
      width = attrib.width - ewin->bf_border * 2;
      height = attrib.height - ewin->bf_border * 2;
    }
  else if (gl_epiwin.titlebar.position == TOP)
    {
      x = 0;
      y = ewin->titlebar_height + ewin->titlebar_border * 2
	- ewin->bf_border;
      width = attrib.width - ewin->bf_border * 2;
      height =  attrib.height - ewin->titlebar_height
	- (ewin->titlebar_border * 2) - ewin->bf_border;
    }
  else if (gl_epiwin.titlebar.position == LEFT)
    {
      x = ewin->titlebar_height + ewin->titlebar_border * 2
	- ewin->bf_border;
      y = 0;
      width = attrib.width - ewin->bf_border - ewin->titlebar_height
	- (ewin->titlebar_border * 2);
      height = attrib.height - ewin->bf_border * 2;
    }
  else if (gl_epiwin.titlebar.position == RIGHT)
    {
      x = 0;
      y = 0;
      width = attrib.width - ewin->bf_border - ewin->titlebar_height
	- (ewin->titlebar_border * 2);
      height = attrib.height - ewin->bf_border * 2;
    }
  else
    {
      x = 0;
      y = 0;
      width = attrib.width - ewin->bf_border * 2;
      height =  attrib.height - ewin->titlebar_height
	- (ewin->titlebar_border * 2) - ewin->bf_border;
      /*
	width = attrib.width - ewin->bf_border * 2;
	height = attrib.height - ewin->bf_border * 2;
      */
    }
  tmp.bgcolor = BlackPixel(gl_dpy, gl_scr_info.screen);
  tmp.fgcolor = gl_epiwin.color_active.pixel;
  tmp.parent = parent;
  /*#warning Changer le cursor de border frame*/
  tmp.cursor = 0;
  tmp.border = ewin->bf_border;
  tmp.x = x;
  tmp.y = y;
  tmp.width = width;
  tmp.height = height;
  w_tmp = add_btn(&tmp);
  attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask
    | SubstructureRedirectMask | SubstructureNotifyMask | PointerMotionMask
    | KeyPressMask;
  XChangeWindowAttributes(gl_dpy, w_tmp, CWEventMask, &attr);
  return (w_tmp);
}

Window			add_titlebar_win(t_win	*ewin,
					 Window	parent)
{
  t_window_btn		tmp;
  XWindowAttributes	attrib;
  XSetWindowAttributes	attr;
  Window		w_tmp;
  int			width;
  int			height;
  int			x;
  int			y;
  
  if (ewin->prop & NO_TITLE)
    return (0);
  x = 0;
  y = 0;
  width = 0;
  height = 0;
  XGetWindowAttributes(gl_dpy, parent, &attrib);
  if (gl_epiwin.titlebar.position == TOP)
    {
      x = 0;
      y = 0;
      width = attrib.width - 2 * ewin->titlebar_border;
      height = ewin->titlebar_height;
    }
  else if (gl_epiwin.titlebar.position == LEFT)
    {
      x = 0;
      y = 0;
      width = ewin->titlebar_height;
      height = attrib.height - 2 * ewin->titlebar_border;
    }
  else if (gl_epiwin.titlebar.position == RIGHT)
    {
      x = attrib.width - ewin->titlebar_height - ewin->titlebar_border * 2;
      y = 0;
      width = ewin->titlebar_height;
      height = attrib.height - 2 * ewin->titlebar_border;
    }
  else
    {
      x = 0;
      y = attrib.height - ewin->titlebar_height
	- ewin->titlebar_border * 2;
      width = attrib.width - 2 * ewin->titlebar_border;
      height = ewin->titlebar_height;
    }
  tmp.x = x;
  tmp.y = y;
  tmp.bgcolor = BlackPixel(gl_dpy, gl_scr_info.screen);
  tmp.fgcolor = gl_epiwin.titlebar.color_active.pixel;
  tmp.parent = parent;
  tmp.cursor = 0;
  tmp.border = ewin->titlebar_border;
  tmp.width = width;
  tmp.height = height;
  w_tmp = add_btn(&tmp);
  attr.event_mask = ButtonPressMask | ButtonReleaseMask
    | ExposureMask | PointerMotionMask | KeyPressMask;
  XChangeWindowAttributes(gl_dpy, w_tmp, CWEventMask, &attr);
  return (w_tmp);
}


Window		add_btn(t_window_btn	*win_struct)
{
  Window	temp;
  
  temp = XCreateSimpleWindow(gl_dpy,
			     win_struct->parent,
			     win_struct->x,
			     win_struct->y,
			     win_struct->width,
			     win_struct->height,
			     win_struct->border,
			     win_struct->bgcolor,
			     win_struct->fgcolor);
  if (win_struct->cursor)
    XDefineCursor(gl_dpy, temp, win_struct->cursor);
  return (temp);
}

void	check_properties(t_win	*ewin)
{  
  check_size_hints(ewin);
  check_icon(ewin);
}

void		check_icon(t_win	*ewin)
{
  XWMHints	*wm_hints;
  
  if (gl_icon.showed)
    {
      wm_hints = XGetWMHints(gl_dpy, ewin->win);
      if (wm_hints)
	{      
	  if (wm_hints->flags & IconWindowHint)
	    ewin->iconify_win = wm_hints->icon_window;	  
	  else
	    ewin->iconify_win = 0;
	  if (wm_hints->flags & IconMaskHint)
	    create_pixmap_mask(ewin, &(wm_hints->icon_mask));
	  if (wm_hints->flags & IconPixmapHint)
	    create_icon_win_with_pixmap(ewin, &(wm_hints->icon_pixmap));
	  if (wm_hints->flags & IconPositionHint)
	    ;
	  /*	    printf("icon position\n");*/
	}
      else
	ewin->iconify_win = 0;
      XFree(wm_hints);
    }
  else
    ewin->iconify_win = 0;
}

void	create_icon_win_with_pixmap(t_win	*ewin,
				    Pixmap	*pixmap)
{
  int	width;
  int	height;
  
  if (pixmap != 0)
    {
      check_icon_size(pixmap, &width, &height);
      if (ewin->iconify_win == 0)
	ewin->iconify_win =
	  XCreateSimpleWindow(gl_dpy, gl_scr_info.root_win,
			      0, 0, width, height, 0,
			      BlackPixel(gl_dpy,
					 gl_scr_info.screen),
			      WhitePixel(gl_dpy,
					 gl_scr_info.screen));
      else
	XResizeWindow(gl_dpy, ewin->iconify_win, width, height);      
      XSetWindowBackgroundPixmap(gl_dpy, ewin->iconify_win, *pixmap);
      iconify_win_set_attrib(ewin);
    }  
}

void	create_pixmap_mask(t_win	*ewin,
			   Pixmap	*shape_pixmap)
{
  XShapeCombineMask(gl_dpy, ewin->iconify_win, ShapeBounding,
		    0, 0, *shape_pixmap, ShapeSet);
}

void			iconify_win_set_attrib(t_win	*ewin)
{
  XSetWindowAttributes	attr;
  
  attr.event_mask = ButtonPressMask | ButtonReleaseMask
    | EnterWindowMask | LeaveWindowMask | ExposureMask | PointerMotionMask;
  XChangeWindowAttributes(gl_dpy, ewin->iconify_win, CWEventMask, &attr);
}

void			check_icon_size(Pixmap	*pixmap,
					int	*width,
					int	*height)
{
  Window		dummy;
  int			dummy_int;
  
  XGetGeometry(gl_dpy, *pixmap, &dummy, &dummy_int, &dummy_int, width, 
	       height, &dummy_int, &dummy_int);
}

void		check_size_hints(t_win		*ewin)
{
  XSizeHints	size_hints;
  long		toto;
  
  XGetWMNormalHints(gl_dpy, ewin->win, &size_hints, &toto);
  if (size_hints.flags & PResizeInc)
    {
      if (size_hints.width_inc == 0)
	ewin->hints.width_inc = 1;
      else
	ewin->hints.width_inc = size_hints.width_inc;
      if (size_hints.height_inc == 0)
	ewin->hints.height_inc = 1;
      else
	ewin->hints.height_inc = size_hints.height_inc;
    }
  else
    {
      ewin->hints.width_inc = 1;
      ewin->hints.height_inc = 1;
    }
  if (size_hints.flags & PMinSize)
    {
      if (size_hints.min_width  == 0)
	ewin->hints.min_width = 1;
      else
	ewin->hints.min_width = size_hints.min_width;
      if (size_hints.min_height  == 0)
	ewin->hints.min_height = 1;
      else
	ewin->hints.min_height = size_hints.min_height;
    }
  else
    {
      ewin->hints.min_width = 1;
      ewin->hints.min_height = 1;
    }
  if (size_hints.flags & PMaxSize)
    {
      if (size_hints.max_width  == 0)
	ewin->hints.max_width = 1;
      else
	ewin->hints.max_width = size_hints.max_width;
      if (size_hints.max_height == 0)
	ewin->hints.max_height = 1;
      else
	ewin->hints.max_height = size_hints.max_height;
    }
  else
    {
      ewin->hints.max_width = -1;
      ewin->hints.max_height = -1;
    }
  verify_max_min(ewin);
}

void	verify_max_min(t_win	*ewin)
{
  int	tmp;
  
  if (ewin->hints.max_width != -1)
    if (ewin->hints.max_width < ewin->hints.min_width)
      {
	tmp = ewin->hints.max_width;
	ewin->hints.max_width = ewin->hints.min_width;
	ewin->hints.min_width = tmp;
      }
  if (ewin->hints.max_height != -1)
    if (ewin->hints.max_height < ewin->hints.min_height)
      {
	tmp = ewin->hints.max_height;
	ewin->hints.max_height = ewin->hints.min_height;
	ewin->hints.min_height = tmp;
      }
}


syntax highlighted by Code2HTML, v. 0.9.1