/* action.c- misc. window commands (miniaturize, hide etc.)
 * 
 *  WindowMaker window manager
 * 
 *  Copyright (c) 1997 Alfredo K. Kojima
 * 
 *  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.
 */
#include "wconfig.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#ifdef TWIST
#include <math.h>
#endif

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "wcore.h"
#include "window.h"
#include "client.h"
#include "icon.h"
#include "funcs.h"
#include "application.h"
#include "actions.h"
#include "stacking.h"
#include "appicon.h"
#include "dock.h"
#include "appmenu.h"

/****** Global Variables ******/
extern Time LastTimestamp;

extern Cursor wCursor[WCUR_LAST];

extern WPreferences wPreferences;

extern Atom _XA_WM_TAKE_FOCUS;

/******* Local Variables *******/
static int ignoreTimestamp=0;


#ifdef ANIMATIONS
static void
processEvents(int event_count)
{
    XEvent event;

    /*
     * This is a hack. When animations are enabled, processing of 
     * events ocurred during a animation are delayed until it's end.
     * Calls that consider the TimeStamp, like XSetInputFocus(), will
     * fail because the TimeStamp is too old. Then, for example, if
     * the user changes window focus while a miniaturize animation is
     * in course, the window will not get focus properly after the end
     * of the animation. This tries to workaround it by passing CurrentTime
     * as the TimeStamp for XSetInputFocus() for all events ocurred during
     * the animation.
     */
    ignoreTimestamp=1;
    while (event_count--) {
	XNextEvent(dpy, &event);
	DispatchEvent(&event);
    }
    ignoreTimestamp=0;
}
#endif /* ANIMATIONS */


/*
 *---------------------------------------------------------------------- 
 * wSetFocusTo--
 * 	Changes the window focus to the one passed as argument.
 * If the window to focus is not already focused, it will be brought
 * to the head of the list of windows. Previously focused window is
 * unfocused. 
 * 
 * Side effects:
 * 	Window list may be reordered and the window focus is changed.
 * 
 *---------------------------------------------------------------------- 
 */
void
wSetFocusTo(WScreen *scr, WWindow  *wwin)
{
    WWindow *focused=scr->focused_window;
    WWindow *owner = NULL;
    int timestamp=LastTimestamp;
    WApplication *oapp=NULL, *napp=NULL;
    int wasfocused;

    if (ignoreTimestamp)
      timestamp=CurrentTime;

    if (focused)
      oapp = wApplicationOf(focused->main_window);

    if (wwin==NULL) {
	XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, timestamp);
	if (focused) {
	    wWindowUnfocus(focused);
	    wWindowPaint(focused, BORDER_ALL, False);
	}
	if (oapp) {
	    wAppMenuUnmap(oapp->menu);
#ifdef NEWAPPICON
	    wApplicationDeactivate(oapp);
#endif
	}
	return;
    }
    wasfocused = wwin->flags.focused;
    napp = wApplicationOf(wwin->main_window);

    if (!wwin->window_flags.focusable) {
	return;
    }
    if (wwin->flags.mapped) {
	/* install colormap if colormap mode is lock mode */
	if (!wPreferences.colormap_follows_mouse)
	  InstallWindowColormaps(wwin);
	
	/* set input focus */
	switch (wwin->focus_mode) {
	 case WFM_NO_INPUT:
	    XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, timestamp);
	    break;
	    
	 case WFM_PASSIVE:
	 case WFM_LOCALLY_ACTIVE:
	    XSetInputFocus(dpy, wwin->client_win, RevertToParent, timestamp);
	    break;
	    
	 case WFM_GLOBALLY_ACTIVE:
	    break;
	}
    } else {
	XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, timestamp);
    }
    /* if this is not the focused window focus it */
    if (focused!=wwin) {
	int foo=0;
	
	if (wwin->client_win == focused->transient_for)
	  wwin->flags.semi_focused = 0;
	else if (wwin->transient_for == focused->client_win)
	  focused->flags.semi_focused = 1;
	
	if (wwin->transient_for 
	    && wwin->transient_for == focused->transient_for) {
	    owner = wWindowFor(wwin->transient_for);
	    if (owner && owner->flags.semi_focused) {
		foo=1;
		/* this is to override the unfocusing of the mainwindow 
		 * and avoid flickering */
		owner->flags.semi_focused = 0;
	    }
	}
	/* unfocus previous window */
	wWindowUnfocus(focused);
	if (foo) {
	    owner->flags.semi_focused = 1;
	}
	/* change the focus window list order */
	if (wwin->prev)
	  wwin->prev->next=wwin->next;
	if (wwin->next)
	  wwin->next->prev=wwin->prev;

	wwin->prev = focused;
	focused->next = wwin;
	wwin->next = NULL;
	scr->focused_window = wwin;

	if (oapp && oapp != napp) {
	    wAppMenuUnmap(oapp->menu);
#ifdef NEWAPPICON
	    wApplicationDeactivate(oapp);
#endif
	}
    }
    if ((owner=wWindowFor(wwin->transient_for)) 
	&& !owner->flags.semi_focused) {
	owner->flags.semi_focused = 1;
	wWindowUnfocus(owner);
	wWindowPaint(owner, BORDER_ALL, False);
    }
    wWindowFocus(wwin);
    if (focused!=wwin) {
	wWindowPaint(focused, BORDER_ALL, False);
    }
    wWindowPaint(wwin, BORDER_ALL, False);
    if (wwin->protocols.TAKE_FOCUS) {
	wClientSendProtocol(wwin, _XA_WM_TAKE_FOCUS, timestamp);
    }
    if (napp && !wasfocused) {
	wAppMenuMap(napp->menu);
#ifdef NEWAPPICON
	wApplicationActivate(napp);
#endif
    }
    XFlush(dpy);
}


void
wShadeWindow(WWindow  *wwin)
{
    XWindowAttributes attribs;
  
#ifdef ANIMATIONS    
    int y, s, w, h;

    if (!wwin->screen_ptr->flags.startup && !wwin->flags.skip_next_animation
	&& wPreferences.animations) {
	/* do the shading animation */
	XLowerWindow(dpy, wwin->client_win);    
	h = wwin->frame->height;
	s = h/SHADE_ANIMATION_STEPS;
	w = wwin->frame->width;
	y = wwin->top_width;
	while (h>wwin->top_width+1) {
	    XMoveWindow(dpy, wwin->client_win, 0, y);
	    XResizeWindow(dpy, wwin->frame->window, w, h);
	    XSync(dpy, 0);
# if (SHADE_ANIMATION_DELAY > 0)	
	    wmsleep(SHADE_ANIMATION_DELAY);
# endif
	    h-=s;
	    y-=s;
	}
	XMoveWindow(dpy, wwin->client_win, 0, wwin->top_width);
    }
#endif  /* ANIMATIONS */
    XGetWindowAttributes(dpy, wwin->client_win, &attribs);    
    wwin->flags.shaded=1;
    wwin->flags.mapped=0;
    /* prevent window withdrawal when getting UnmapNotify */
    XSelectInput(dpy, wwin->client_win, 
		 attribs.your_event_mask & ~StructureNotifyMask);
    XUnmapWindow(dpy, wwin->client_win);
    XSelectInput(dpy, wwin->client_win, attribs.your_event_mask);
    
    /* for the client it's just like iconification */
    wCoreConfigure(wwin->frame, wwin->frame_x, wwin->frame_y,
		   0,wwin->top_width-1);
    wClientSetState(wwin, IconicState, None);

#ifdef ANIMATIONS
    if (!wwin->screen_ptr->flags.startup) {
    	XSync(dpy, 0);
    	processEvents(XPending(dpy));
    }
#endif
}


void
wUnshadeWindow(WWindow  *wwin)
{
#ifdef ANIMATIONS
    int y, s, w, h;    
#endif /* ANIMATIONS */
    wwin->flags.shaded=0;
    wwin->flags.mapped=1;
    XMapWindow(dpy, wwin->client_win);

#ifdef ANIMATIONS
    if (wPreferences.animations && !wwin->flags.skip_next_animation) {
	/* do the shading animation */
	XMoveWindow(dpy, wwin->client_win, 0, -wwin->client.height);
	h = wwin->top_width;
	y = wwin->client.height + wwin->top_width + wwin->bottom_width;
	s = y/SHADE_ANIMATION_STEPS;
	w = wwin->frame->width;
	y = -wwin->client.height + wwin->top_width;
	while (h<wwin->client.height + wwin->top_width + wwin->bottom_width) {
	    XResizeWindow(dpy, wwin->frame->window, w, h);
	    XMoveWindow(dpy, wwin->client_win, 0, y);
	    XSync(dpy, 0);
# if (SHADE_ANIMATION_DELAY > 0)	
	    wmsleep(SHADE_ANIMATION_DELAY*2/3);
# endif
	    h+=s;
	    y+=s;
	}
	XMoveWindow(dpy, wwin->client_win, 0, wwin->top_width);
    }
#endif /* ANIMATIONS */
    wwin->flags.skip_next_animation = 0;
    wCoreConfigure(wwin->frame, wwin->frame_x, wwin->frame_y,
		   wwin->frame->width, 
		   wwin->top_width+wwin->client.height+wwin->bottom_width);
    wClientSetState(wwin, NormalState, None);
    /* if the window is focused, set the focus again as it was disabled during
     * shading */
    if (wwin->flags.focused)
      wSetFocusTo(wwin->screen_ptr, wwin);
}


void
wMaximizeWindow(WWindow *wwin, int full_maximize)
{
    int new_width, new_height, new_x, new_y;

    if (wwin->flags.shaded) {
	wwin->flags.skip_next_animation = 1;
	wUnshadeWindow(wwin);
    }
    wwin->flags.maximized = 1;
    wwin->old_geometry.width = wwin->frame->width;
    wwin->old_geometry.height = wwin->frame->height;
    wwin->old_geometry.x = wwin->frame_x;
    wwin->old_geometry.y = wwin->frame_y;
    if (full_maximize) {
	new_x = 0;
	new_width = wwin->screen_ptr->scr_width-FRAME_BORDER_WIDTH*2;
#if 0
	if (wwin->screen_ptr->dock) {
	    new_width -= ICON_WIDTH + DOCK_EXTRA_SPACE;
	    if (!wwin->screen_ptr->dock->on_right_side) 
		new_x += ICON_WIDTH + DOCK_EXTRA_SPACE;
	}
#endif
    } else {
	new_x = wwin->frame_x;
	new_width = wwin->frame->width;
    }
    new_y = 0;
    new_height = wwin->screen_ptr->scr_height-wwin->top_width
      -wwin->bottom_width;
    wWindowConstrainSize(wwin, &new_width, &new_height);
    new_height += wwin->top_width + wwin->bottom_width;
    wWindowConfigure(wwin, new_x, new_y, new_width, new_height);
}


void
wUnmaximizeWindow(WWindow *wwin)
{
    if (wwin->flags.shaded) {
	wwin->flags.skip_next_animation = 1;
	wUnshadeWindow(wwin);
    }
    wwin->flags.maximized = 0;
    wWindowConfigure(wwin, wwin->old_geometry.x, wwin->old_geometry.y, 
		     wwin->old_geometry.width, wwin->old_geometry.height);
}

#ifdef ANIMATIONS
#ifdef TWIST
static void 
animateResize(WScreen *scr, int x, int y, int w, int h, 
	      int fx, int fy, int fw, int fh)
{
#define FRAMES (MINIATURIZE_ANIMATION_FRAMES)
    float cx, cy, cw, ch;
    float xstep, ystep, wstep, hstep;
    XPoint points[5]; 
    float angle, angle_finite, a, d;

    x += w/2;
    y += h/2;
    fx += fw/2;
    fy += fh/2;

    xstep = (float)(fx-x)/MINIATURIZE_ANIMATION_STEPS;
    ystep = (float)(fy-y)/MINIATURIZE_ANIMATION_STEPS;
    wstep = (float)(fw-w)/MINIATURIZE_ANIMATION_STEPS;
    hstep = (float)(fh-h)/MINIATURIZE_ANIMATION_STEPS;
     
    cx = (float)x;
    cy = (float)y;
    cw = (float)w;
    ch = (float)h;
    a = atan(ch/cw);
    d = sqrt((cw/2)*(cw/2)+(ch/2)*(ch/2));

    angle_finite =2*WM_PI*0.5;
    for (angle=0;; angle+=(float)(2*WM_PI*MINIATURIZE_ANIMATION_TWIST/FRAMES)) {
        if (angle > angle_finite)
	  angle = angle_finite;
        points[0].x = cx+cos(angle-a)*d;
        points[0].y = cy+sin(angle-a)*d;
        points[1].x = cx+cos(angle+a)*d;
        points[1].y = cy+sin(angle+a)*d;
        points[2].x = cx+cos(angle-a+WM_PI)*d;
        points[2].y = cy+sin(angle-a+WM_PI)*d;
        points[3].x = cx+cos(angle+a+WM_PI)*d;
        points[3].y = cy+sin(angle+a+WM_PI)*d;
        points[4].x = cx+cos(angle-a)*d;
        points[4].y = cy+sin(angle-a)*d;
        XGrabServer(dpy);
        XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
	XFlush(dpy);
#if (MINIATURIZE_ANIMATION_DELAY > 0)	
 	wmsleep(MINIATURIZE_ANIMATION_DELAY);
#endif

	XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
	XUngrabServer(dpy);
	cx+=xstep;
	cy+=ystep;
	cw+=wstep;
	ch+=hstep;
	a = atan(ch/cw);
	d = sqrt((cw/2)*(cw/2)+(ch/2)*(ch/2));
	if (angle >= angle_finite)
	  break;
 
    }
    XFlush(dpy);
}

#else /* !TWIST */

static void 
animateResize(WScreen *scr, int x, int y, int w, int h, 
	      int fx, int fy, int fw, int fh)
{
#define FRAMES (MINIATURIZE_ANIMATION_FRAMES)
    float cx[FRAMES], cy[FRAMES], cw[FRAMES], ch[FRAMES];
    float xstep, ystep, wstep, hstep;
    int i, j;
        
    xstep = (float)(fx-x)/MINIATURIZE_ANIMATION_STEPS;
    ystep = (float)(fy-y)/MINIATURIZE_ANIMATION_STEPS;
    wstep = (float)(fw-w)/MINIATURIZE_ANIMATION_STEPS;
    hstep = (float)(fh-h)/MINIATURIZE_ANIMATION_STEPS;
    
    for (j=0; j<FRAMES; j++) {
	cx[j] = (float)x;
	cy[j] = (float)y;
	cw[j] = (float)w;
	ch[j] = (float)h;
    }
    XGrabServer(dpy);
    for (i=0; i<MINIATURIZE_ANIMATION_STEPS; i++) {
	for (j=0; j<FRAMES; j++) {
	    XDrawRectangle(dpy, scr->root_win, scr->frame_gc, 
			   (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
	}
	XFlush(dpy);
#if (MINIATURIZE_ANIMATION_DELAY > 0)	
	wmsleep(MINIATURIZE_ANIMATION_DELAY);
#endif
	for (j=0; j<FRAMES; j++) {
	    XDrawRectangle(dpy, scr->root_win, scr->frame_gc, 
			   (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
	    if (j<FRAMES-1) {
		cx[j]=cx[j+1];
		cy[j]=cy[j+1];
		cw[j]=cw[j+1];
		ch[j]=ch[j+1];
	    } else {
		cx[j]+=xstep;
		cy[j]+=ystep;
		cw[j]+=wstep;
		ch[j]+=hstep;
	    }
	}
    }
    XUngrabServer(dpy);
}
#undef FRAMES
#endif /* !TWIST */
#endif /* ANIMATIONS */

static void
flushExpose()
{
    XEvent tmpev;
    
    while (XCheckTypedEvent(dpy, Expose, &tmpev))
      DispatchEvent(&tmpev);
    XSync(dpy, 0);
}

static void
unmapTransientsFor(WWindow *wwin)
{
    WWindow *tmp;
    XWindowAttributes attribs;
    

    tmp = wwin->screen_ptr->focused_window;
    while (tmp) {
	/* unmap the transients for this transient */
	if (tmp!=wwin && tmp->transient_for == wwin->client_win
	    && (tmp->flags.mapped || wwin->screen_ptr->flags.startup)) {
	    unmapTransientsFor(tmp);
	    XGetWindowAttributes(dpy, tmp->client_win, &attribs);
	    tmp->flags.miniaturized=1;
	    tmp->flags.mapped=0;
	    XSelectInput(dpy, tmp->client_win, 
			 attribs.your_event_mask & ~StructureNotifyMask);
	    XUnmapWindow(dpy, tmp->client_win);
	    XSelectInput(dpy, tmp->client_win, attribs.your_event_mask);
	    XUnmapWindow(dpy, tmp->frame->window);
	    wClientSetState(tmp, IconicState, None);
	}
	tmp = tmp->prev;
    }
}


static void
mapTransientsFor(WWindow *wwin)
{
    WWindow *tmp;

    tmp = wwin->screen_ptr->focused_window;
    while (tmp) {
	/* recursively map the transients for this transient */
	if (tmp!=wwin && tmp->transient_for == wwin->client_win
	    && !tmp->flags.mapped && tmp->icon==NULL) {
	    mapTransientsFor(tmp);
	    tmp->flags.miniaturized=0;
	    tmp->flags.mapped=1;
	    XMapWindow(dpy, tmp->client_win);
	    XMapWindow(dpy, tmp->frame->window);
	    wClientSetState(tmp, NormalState, None);
	}
	tmp = tmp->prev;
    }
}


static void
setupIconGrabs(WIcon *icon)
{
    /* setup passive grabs on the icon */
    XGrabButton(dpy, Button1, AnyModifier, icon->core->window, True,
		ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
    XGrabButton(dpy, Button2, AnyModifier, icon->core->window, True,
		ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
    XGrabButton(dpy, Button3, AnyModifier, icon->core->window, True,
		ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
    XSync(dpy, 0);
}

static void
removeIconGrabs(WIcon *icon)
{
    /* remove passive grabs on the icon */
    XUngrabButton(dpy, Button1, AnyModifier, icon->core->window);
    XUngrabButton(dpy, Button2, AnyModifier, icon->core->window);
    XUngrabButton(dpy, Button3, AnyModifier, icon->core->window);    
    XSync(dpy, 0);
}


void
wIconifyWindow(WWindow *wwin)
{
    XWindowAttributes attribs;
    
    if (!XGetWindowAttributes(dpy, wwin->client_win, &attribs)) {
	/* the window doesn't exist anymore */
	return;
    }
    /* icon creation may take a while */
    XGrabPointer(dpy, wwin->screen_ptr->root_win, False, 
		 ButtonMotionMask|ButtonReleaseMask, GrabModeAsync, 
		 GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);

    if (!wwin->flags.icon_moved) {
	PlaceIcon(wwin->screen_ptr, &wwin->icon_x, &wwin->icon_y);
    }
    wwin->icon = wIconCreate(wwin);

    wwin->flags.miniaturized=1;
    wwin->flags.mapped=0;

    /* unmap transients */
    
    unmapTransientsFor(wwin);

    XUngrabPointer(dpy, CurrentTime);
    /* prevent window withdrawal when getting UnmapNotify */
    XSelectInput(dpy, wwin->client_win, 
		 attribs.your_event_mask & ~StructureNotifyMask);
    XUnmapWindow(dpy, wwin->client_win);
    XSelectInput(dpy, wwin->client_win, attribs.your_event_mask);
    XUnmapWindow(dpy, wwin->frame->window);
    /* let all Expose events arrive so that we can repaint
     * something before the animation starts (and the server is grabbed) */
    XSync(dpy, 0);
    wClientSetState(wwin, IconicState, wwin->icon->icon_win);
    flushExpose();
#ifdef ANIMATIONS
    if (!wwin->screen_ptr->flags.startup && !wwin->flags.skip_next_animation
	&& wPreferences.animations) {
	animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y, 
		      wwin->frame->width, wwin->frame->height,
		      wwin->icon_x, wwin->icon_y,
		      wwin->icon->core->width, wwin->icon->core->height);
    }
    wwin->flags.skip_next_animation = 0;
#endif
    XMapWindow(dpy, wwin->icon->core->window);
    AddToStackList(wwin->icon->core);

    setupIconGrabs(wwin->icon);
    if (wwin->flags.focused && wPreferences.focus_mode==WKF_CLICK) {
	WWindow *tmp;

	tmp = wwin->prev;
	while (tmp) {
	    if (tmp->window_flags.focusable
		&& !(tmp->flags.hidden||tmp->flags.miniaturized))
	      break;
	    tmp = tmp->prev;
	}
	wSetFocusTo(wwin->screen_ptr, tmp);
    } else if (wPreferences.focus_mode!=WKF_CLICK) {
	wSetFocusTo(wwin->screen_ptr, NULL);
    }
#ifdef ANIMATIONS
    if (!wwin->screen_ptr->flags.startup) {
    	XSync(dpy, 0);
    	processEvents(XPending(dpy));
    }
#endif
}


void
wDeiconifyWindow(WWindow  *wwin)
{
    wWindowChangeWorkspace(wwin, wwin->screen_ptr->workspace);

    wwin->flags.miniaturized=0;
    wwin->flags.mapped=1;

    XUnmapWindow(dpy, wwin->icon->core->window);
#ifdef ANIMATIONS
    if (wPreferences.animations && !wwin->flags.skip_next_animation) {
	animateResize(wwin->screen_ptr, wwin->icon_x, wwin->icon_y,
		      wwin->icon->core->width, wwin->icon->core->height,
		      wwin->frame_x, wwin->frame_y, 
		      wwin->frame->width, wwin->frame->height);
    }
    wwin->flags.skip_next_animation = 0;
#endif /* ANIMATIONS */
    XGrabServer(dpy);
    XMapWindow(dpy, wwin->client_win);
    XMapWindow(dpy, wwin->frame->window);
    wRaiseFrame(wwin->frame);
    wClientSetState(wwin, NormalState, None);

    mapTransientsFor(wwin);
    RemoveFromStackList(wwin->icon->core);
    removeIconGrabs(wwin->icon);
    wIconDestroy(wwin->icon);
    wwin->icon = NULL;
    XUngrabServer(dpy);
    if (wPreferences.focus_mode==WKF_CLICK)
      wSetFocusTo(wwin->screen_ptr, wwin);
#ifdef ANIMATIONS
    if (!wwin->screen_ptr->flags.startup) {
    	XSync(dpy, 0);
    	processEvents(XPending(dpy));
    }
#endif
}



static void
hideWindow(WIcon *icon, int icon_x, int icon_y, WWindow *wwin)
{
    XWindowAttributes attribs;
    
    XGetWindowAttributes(dpy, wwin->client_win, &attribs);
    
    wwin->flags.hidden = 1;
    wwin->flags.mapped = 0;

    /* prevent window withdrawal when getting UnmapNotify */
    XSelectInput(dpy, wwin->client_win, 
		 attribs.your_event_mask & ~StructureNotifyMask);
    XUnmapWindow(dpy, wwin->client_win);
    XSelectInput(dpy, wwin->client_win, attribs.your_event_mask);

    XUnmapWindow(dpy, wwin->frame->window);
    wClientSetState(wwin, IconicState, icon->icon_win);
    flushExpose();
#ifdef ANIMATIONS_
    if (wPreferences.animations) {
	animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y, 
		      wwin->frame->width, wwin->frame->height,
		      icon_x, icon_y, icon->core->width, icon->core->height);
    }
#endif
}



void
wHideOtherApplications(WWindow *awin)
{
    WWindow *wwin;
    WApplication *tapp;

    if (!awin)
	return;
    wwin = awin->screen_ptr->focused_window;

    while (wwin) {
	if (wwin!=awin && wwin->workspace == awin->workspace
	    && !(wwin->flags.miniaturized||wwin->flags.hidden)
	    && !wwin->window_flags.ignore_hide_others) {
	    if (wwin->main_window==None) {
		wwin->flags.skip_next_animation = 1;
		wIconifyWindow(wwin);
	    } else if (wwin->main_window!=None
		       && awin->main_window != wwin->main_window) {
		tapp = wApplicationOf(wwin->main_window);
		if (tapp) {
		    wHideApplication(tapp);
		} else {
		    wwin->flags.skip_next_animation = 1;
		    wIconifyWindow(wwin);
		}
	    }
	}
	wwin = wwin->prev;
    }
    wSetFocusTo(awin->screen_ptr, awin);
}



void
wHideApplication(WApplication *wapp)
{
    WScreen *scr;
    WWindow *wlist;
    int hadfocus;
    
    if (!wapp) {
	wWarning("trying to hide a non grouped window");
	return;
    }
    if (!wapp->main_window_desc) {
	wWarning("group leader not found for window group");
	return;
    }
    scr = wapp->main_window_desc->screen_ptr;
    hadfocus = 0;
    wlist = scr->focused_window;
    if (wlist->main_window == wapp->main_window)
      wapp->last_focused = wlist;
    else
      wapp->last_focused = NULL;
    while (wlist) {
	if (wlist->main_window == wapp->main_window) {
	    if (wlist->flags.focused) {
		hadfocus = 1;
	    }
	    hideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
		       wapp->app_icon->y_pos, wlist);
	}
	wlist = wlist->prev;
    }

    if (hadfocus) {
	if (wPreferences.focus_mode==WKF_CLICK) {
	    wlist = scr->focused_window;
	    while (wlist) {
		if (wlist->window_flags.focusable
		    && (wlist->flags.mapped || wlist->flags.shaded))
		  break;
		wlist = wlist->prev;
	    }
	    wSetFocusTo(scr, wlist);
	} else {
	    wSetFocusTo(scr, NULL);
	}
    }
}




static void
unhideWindow(WIcon *icon, int icon_x, int icon_y, WWindow *wwin)
{
    wWindowChangeWorkspace(wwin, wwin->screen_ptr->workspace);

    wwin->flags.hidden=0;
    wwin->flags.mapped=1;
#ifdef ANIMATIONS_
    if (wPreferences.animations) {
	animateResize(wwin->screen_ptr, icon_x, icon_y,
		      icon->core->width, icon->core->height,
		      wwin->frame_x, wwin->frame_y, 
		      wwin->frame->width, wwin->frame->height);
    }
#endif
    XMapWindow(dpy, wwin->client_win);
    XMapWindow(dpy, wwin->frame->window);
    wClientSetState(wwin, NormalState, None);
    wRaiseFrame(wwin->frame);
}


void
wUnhideApplication(WApplication *wapp)
{
    WScreen *scr;
    WWindow *wlist;
    WWindow *focused=NULL;

    if (!wapp) {
	return;
    }
    scr = wapp->main_window_desc->screen_ptr;

    wlist = scr->focused_window;
    /* goto beginning of list */
    while (wlist->prev)
      wlist = wlist->prev;
    
    while (wlist) {
	if (wlist->main_window == wapp->main_window) {
	    if (wlist->flags.focused)
	      focused = wlist;
	    else if (!focused || !focused->flags.focused)
	      focused = wlist;
	    if (wlist->flags.hidden) {
		unhideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
			     wapp->app_icon->y_pos, wlist);
	    } else if (wlist->flags.miniaturized) {
		wDeiconifyWindow(wlist);
	    } else {
		if (wlist->workspace!=scr->workspace) {
		    wlist->workspace = scr->workspace;
		    XMapWindow(dpy, wlist->frame->window);
		    wlist->flags.mapped = 1;
		}
		wRaiseFrame(wlist->frame);
	    }
	}
	wlist = wlist->next;
    }
    if (wPreferences.focus_mode==WKF_CLICK) {
	if (focused)
	  wSetFocusTo(scr, focused);
	else if (wapp->last_focused)
	  wSetFocusTo(scr, wapp->last_focused);
    } else
      wSetFocusTo(scr, NULL);
    wapp->last_focused = NULL;
}

void
wShowAllWindows(WScreen *scr)
{
    WWindow *wwin, *old_foc;
    WApplication *wapp;

    old_foc = wwin = scr->focused_window;
    while (wwin) {
	if (scr->workspace == wwin->workspace 
	    || wwin->window_flags.omnipresent) {
	    if (wwin->flags.miniaturized) {
		wwin->flags.skip_next_animation = 1;
		wDeiconifyWindow(wwin);
	    } else if (wwin->flags.hidden) {
		wapp = wApplicationOf(wwin->main_window);
		if (wapp) {
		    wUnhideApplication(wapp);
		} else {
		    wwin->flags.skip_next_animation = 1;
		    wDeiconifyWindow(wwin);
		}
	    }
	}
	wwin = wwin->prev;
    }
    wSetFocusTo(scr, old_foc);
}

void
wRefreshDesktop(WScreen *scr)
{
    Window win;
    XSetWindowAttributes attr;

    attr.backing_store = NotUseful;
    attr.save_under = False;
    win = XCreateWindow(dpy, scr->root_win, 0, 0, scr->scr_width, 
			scr->scr_height, 0, CopyFromParent, CopyFromParent,
			(Visual *) CopyFromParent, CWBackingStore|CWSaveUnder,
			&attr);
    XMapWindow(dpy, win);
    XDestroyWindow(dpy, win);
    XFlush(dpy);
}


int
wNewWorkspace(WScreen *scr)
{
    if (scr->last_workspace < MAX_WORKSPACES) {
	scr->last_workspace++;
	wChangeWorkspace(scr, scr->last_workspace);
	return scr->last_workspace;
    }
    return -1;
}



void
wDeleteLastWorkspace(WScreen *scr)
{
    WWindow *tmp;

    /* verify if workspace is in use */
    tmp = scr->focused_window;
    while (tmp) {
	if (tmp->workspace==scr->last_workspace)
	  return;
	tmp = tmp->prev;
    }
    scr->last_workspace--;
}


void
wChangeWorkspace(WScreen *scr, short workspace)
{
    WWindow *tmp, *foc=NULL;
    int change_focus;

    if (workspace>scr->last_workspace || workspace < 0 
	|| workspace==scr->workspace)
      return;
    
    scr->workspace = workspace;
    
    tmp = scr->focused_window;
    if ((tmp = scr->focused_window)!= NULL) {
	change_focus = !tmp->window_flags.omnipresent;
	while (tmp) {
	    if (tmp->workspace!=workspace && !tmp->flags.selected) {
		/* unmap windows not on this workspace */
		if ((tmp->flags.mapped||tmp->flags.shaded)
		    && !tmp->window_flags.omnipresent) {
		    XUnmapWindow(dpy, tmp->frame->window);
		    tmp->flags.mapped = 0;
		}
	    } else {
		/* change selected windows' workspace */
		if (tmp->flags.selected) {
		    wWindowChangeWorkspace(tmp, workspace);
		    foc = tmp;
		} else if (!(tmp->flags.mapped || tmp->flags.hidden 
			     || tmp->flags.miniaturized)) {
		    /* remap windows that are on this workspace */
		    XMapWindow(dpy, tmp->frame->window);
		if (!foc)
		      foc = tmp;
		    if (!tmp->flags.shaded)
		      tmp->flags.mapped = 1;
		}
	    }
	    tmp = tmp->prev;
	}
	if (wPreferences.focus_mode == WKF_CLICK && change_focus) {
	    wSetFocusTo(scr, foc);
	}
    }
}



void
wArrangeIcons(WScreen *scr)
{
    WWindow *wwin = scr->focused_window;
    WCoreWindow *wcore;
    int left_margin, right_margin;
    int x, y;

    
    if (scr->dock) {
	if (scr->dock->on_right_side) {
	    left_margin = 0;
	    right_margin = scr->scr_width - ICON_WIDTH;
	} else {
	    left_margin = ICON_WIDTH;
	    right_margin = scr->scr_width;
	}
    } else {
	left_margin = 0;
	right_margin = scr->scr_width;
    }
    scr->icon_aux++;
    /* arrange icons putting the most recently focused window
     * as the last icon */
    x = left_margin;
    y = scr->scr_height - ICON_HEIGHT;

    
    
    /* arrange application icons */
    wcore = scr->stacking_list[0];
    while (wcore) {
	if (wcore->descriptor.parent_type == WCLASS_APPICON) {
	    WAppIcon *aicon = (WAppIcon*)wcore->descriptor.parent;
	    
	    if (aicon->x_pos != x || aicon->y_pos != y) {
#ifdef ANIMATIONS
		if (wPreferences.animations) {
		    SlideWindow(wcore->window, aicon->x_pos,
				aicon->y_pos, x, y, 
				ARRANGE_ICONS_ANIMATION_STEPS,
				ARRANGE_ICONS_ANIMATION_DELAY);
		} else {
		    XMoveWindow(dpy, wcore->window, x, y);
		}
#else
		XMoveWindow(dpy, wcore->window, x, y);
#endif /* ANIMATIONS */
	    }
	    aicon->x_pos = x;
	    aicon->y_pos = y;
	    x += ICON_WIDTH;
	}
        wcore = wcore->stacking->under;
	if (x >= right_margin) {
	    x = left_margin;
	    y -= ICON_HEIGHT;
	}
    }
    
    /* arrange miniwindows */
    
    wwin = scr->focused_window;
    while (wwin) { 
	if (wwin->icon && wwin->flags.miniaturized) {
	    if (wwin->icon_x != x || wwin->icon_y != y) {
#ifdef ANIMATIONS
		if (wPreferences.animations) {
		    SlideWindow(wwin->icon->core->window, wwin->icon_x, 
				wwin->icon_y, x, y, 
				ARRANGE_ICONS_ANIMATION_STEPS,
				ARRANGE_ICONS_ANIMATION_DELAY);
		} else {
		    XMoveWindow(dpy, wwin->icon->core->window, x, y);
		}
#else
		XMoveWindow(dpy, wwin->icon->core->window, x, y);
#endif /* ANIMATIONS */
	    }
	    wwin->icon_x = x;
	    wwin->icon_y = y;
	    wwin->flags.icon_moved = 0;
	    x += ICON_WIDTH;
	}
	wwin = wwin->prev;
	if (x >= right_margin) {
	    x = left_margin;
	    y -= ICON_HEIGHT;
	}
    }
}


