/* appicon.c- icon for applications (not mini-window)
 *
 *  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 <string.h>

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "wcore.h"
#include "window.h"
#include "pixmap.h"
#include "icon.h"
#include "appicon.h"
#include "actions.h"
#include "resource.h"
#include "stacking.h"
#include "dock.h"
#include "funcs.h"

/* 
 * icon_file for the dock is got from the preferences file by
 * using the classname/instancename
 */

/**** Global variables ****/
extern Cursor wCursor[WCUR_LAST];
extern WPreferences wPreferences;

static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
static void iconDblClick(WObjDescriptor *desc, XEvent *event);

WMethodTable wAppIconMethods = {
    (WEventHandler)iconMouseDown,
    NULL,
    (WEventHandler)iconDblClick,
    NULL
};


/*
 *---------------------------------------------------------------------- 
 * wAppIconCreateFromIcon--
 * 	Create an icon for use in the Dock, by "converting" a normal
 * icon to a dock icon. Tries to guess how the app is launched and
 * if it can't, fail.
 * 
 * Returns:
 * 	A WAppIcon structure or NULL, if the icon can't be docked.
 * 
 *---------------------------------------------------------------------- 
 */
WAppIcon*
wAppIconCreateFromIcon(WIcon *icon)
{
    WAppIcon *dicon;
    char **argv;
    int argc;

    dicon = wmalloc(sizeof(WAppIcon));
    memset(dicon, 0, sizeof(WAppIcon));
    dicon->icon = icon;

    /* check WM_COMMAND */
    if (!wApplicationOf(icon->owner->main_window)) {
	dicon->main_window = icon->owner->client_win;
    } else {
	dicon->main_window = icon->owner->main_window;
    }
    if (!XGetCommand(dpy, dicon->main_window, &argv, &argc)) {
	wfree(dicon);
	return NULL;
    }
    dicon->argv = argv;
    dicon->argc = argc;
    dicon->wm_class = strdup(icon->owner->wm_class);
    dicon->wm_instance = strdup(icon->owner->wm_instance);
    
    return dicon;
}


WAppIcon*
wAppIconCreateForCommand(WScreen *scr, char **argv, int argc, char *wm_class,
			 char *wm_instance)
{
    WAppIcon *dicon;
    WResource *res;
    char *image, *mask, *itext=NULL;
    
    dicon = wmalloc(sizeof(WAppIcon));
    memset(dicon, 0, sizeof(WAppIcon));

    if (argv) {
	dicon->argv = argv;
	dicon->argc = argc;
    }
    res = wResourceGet(NULL, wm_instance, wm_class);
    if (!wm_class && !wm_instance) {
	image = wPreferences.logo_file;
	mask = NULL;
    } else if (!res) {
	image = NULL;
	mask = NULL;
    } else {
	image = res->icon_file;
	itext = res->icon_text;
    }
    if (wm_class)
      dicon->wm_class = strdup(wm_class);
    if (wm_instance)
      dicon->wm_instance = strdup(wm_instance);

    dicon->icon = wIconCreateWithImage(scr, image);
    dicon->icon->icon_text = itext;
    if (itext) {
	dicon->icon->force_paint = 1;
	wIconUpdate(dicon->icon);
    }
    /* will be overriden by dock */
    dicon->icon->core->descriptor.methods = &wAppIconMethods;
    dicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
    dicon->icon->core->descriptor.parent = dicon;
    AddToStackList(dicon->icon->core);
    
    return dicon;
}



WAppIcon*
wAppIconCreate(WWindow *leader_win)
{
    WAppIcon *aicon;

    aicon = wmalloc(sizeof(WAppIcon));
    memset(aicon, 0, sizeof(WAppIcon));

    aicon->icon = wIconCreate(leader_win);
    /* will be overriden if docked */
    aicon->icon->core->descriptor.methods = &wAppIconMethods;
    aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
    aicon->icon->core->descriptor.parent = aicon;
    AddToStackList(aicon->icon->core);
    aicon->icon->show_title = 0;
    wIconUpdate(aicon->icon);

    return aicon;
}


void
wAppIconDestroy(WAppIcon *aicon)
{
    RemoveFromStackList(aicon->icon->core);
    wIconDestroy(aicon->icon);
    if (aicon->argv)
      XFreeStringList(aicon->argv);
    if (aicon->wm_instance)
      XFree(aicon->wm_instance);
    if (aicon->wm_class)
      XFree(aicon->wm_class);

    wfree(aicon);
}

#ifdef NEWAPPICON
static void
drawCorner(WIcon *icon, WWindow *wwin, int active)
{
    WScreen *scr = wwin->screen_ptr;
    XPoint points[3];
    GC gc;

    points[0].x = 2;
    points[0].y = 2;
    points[1].x = 12;
    points[1].y = 2;
    points[2].x = 2;
    points[2].y = 12;
    if (active) {
	gc=scr->focused_texture->any.gc;
    } else {
	gc=scr->unfocused_texture->any.gc;
    }
    XFillPolygon(dpy, icon->core->window, gc, points, 3,
		 Convex, CoordModeOrigin);
}
#endif /* NEWAPPICON */

void
wAppIconPaint(WAppIcon *aicon)
{
    WScreen *scr = aicon->icon->core->screen_ptr;

    wIconPaint(aicon->icon);

    if (scr->dock_dots && aicon->docked && !aicon->running) {
	XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
	XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
	XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window, 
		  scr->copy_gc, 0, 0, scr->dock_dots->width,
		  scr->dock_dots->height, 0, 0);
    }
#ifdef AUTOLAUNCH_INDICATOR
    if (scr->dock_tbolt && aicon->docked && aicon->auto_launch) {
	XSetClipMask(dpy, scr->copy_gc, scr->dock_tbolt->mask);
	XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
	XCopyArea(dpy, scr->dock_tbolt->image, aicon->icon->core->window, 
		  scr->copy_gc, 0, 0, scr->dock_tbolt->width,
		  scr->dock_tbolt->height, 0, 0);
    }
#endif /* AUTOLAUNCH_INDICATOR */
#ifdef NEWAPPICON
    if (!wPreferences.strict_ns && aicon->icon->owner!=NULL) {
	int active=0;
	if (aicon->main_window==None) {
	    active = (aicon->icon->owner->flags.focused ? 1 : 0);
	} else {
	    WApplication *wapp;

	    wapp = wApplicationOf(aicon->main_window);
	    if (!wapp) {
		active = aicon->icon->owner->flags.focused;
		wWarning("error in wAppIconPaint(). Please report it");
	    } else {
		active = wapp->main_window_desc->flags.focused;
		if (wapp->main_window_desc->flags.hidden
		    || !wapp->main_window_desc->flags.mapped)
		  active = -1;
	    }
	}
	if (active>=0)
	  drawCorner(aicon->icon, aicon->icon->owner, active);
    }
#endif /* NEWAPPICON */
    XSetClipMask(dpy, scr->copy_gc, None);
    if (aicon->launching) {
	XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc, 
		       0, 0, ICON_WIDTH, ICON_HEIGHT);
    }
}


static int
canBeDocked(WWindow *wwin)
{
    if (wwin && (wwin->wm_class || wwin->wm_instance)) {
	char **argv;
	int argc;
	  
	if (!XGetCommand(dpy, wwin->client_win, &argv, &argc)) {
	    return False;
	}
	XFreeStringList(argv);
	return True;
    }
    return False;
}


/******************************************************************/
static void 
iconDblClick(WObjDescriptor *desc, XEvent *event)
{
    WAppIcon *aicon = desc->parent;
    WApplication *wapp;

    if (event->xbutton.button == Button3) {
	WWindow *wwin = aicon->icon->owner;
	if (wwin->wm_instance && wwin->wm_class) {
	    printf("%s.%s\n", wwin->wm_instance, wwin->wm_class);
	} else if (wwin->wm_class) {
	    printf("%s\n", wwin->wm_class);
	} else if (wwin->wm_instance) {
	    printf("%s\n", wwin->wm_instance);
	} else {
	    printf("instance/class name not available for window %x\n",
		   (unsigned)wwin->client_win);
	}
    } else {
	
	if (!aicon->icon->owner)
	{
	  puts("OWNER NOT SET");
	    return;
	}
	
	wapp = wApplicationOf(aicon->icon->owner->main_window);
	/* aproveita que ta aqui pra atualizar */
	wapp->main_window = aicon->icon->owner->main_window;
#ifdef DEBUG0
	if (!wapp) {
	    wWarning("could not find application descriptor for app icon!!");
	    return;
	}
#endif
	wUnhideApplication(wapp);
	if (event->xbutton.state & Mod1Mask) {
	    wHideOtherApplications(aicon->icon->owner);
	}
    }
}


static void 
iconMouseDown(WObjDescriptor *desc, XEvent *event)
{
    WAppIcon *aicon = desc->parent;
    WIcon *icon = aicon->icon;
    WScreen *scr = icon->core->screen_ptr;
    XEvent ev;
    int x=aicon->x_pos, y=aicon->y_pos;
    int dx=event->xbutton.x, dy=event->xbutton.y;
    int grabbed=0;
    int done=0;
    int shad_x, shad_y, docking=0, dockable;

#ifdef DEBUG
    puts("Moving icon");
#endif
    if (event->xbutton.state & Mod1Mask)
      wLowerFrame(icon->core);
    else
      wRaiseFrame(icon->core);

    
    /* TODO: remove this send event and make event be passed directly
     * to the client */
    if (icon->owner && icon->client_window)
      XSendEvent(dpy, icon->icon_win, False, ButtonPressMask, event);

    if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
		     |ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None,
		     None, CurrentTime) !=GrabSuccess) {
	wWarning("pointer grab failed for appicon move");
    }
    if (wPreferences.flags.nodock)
      dockable = 0;
    else
      dockable = canBeDocked(icon->owner);
    while (!done) {
	XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonMotionMask
		   |ExposureMask, &ev);
	switch (ev.type) {
	 case Expose:
	    DispatchEvent(&ev);
	    break;

	 case MotionNotify:
	    if (!grabbed) {
		if (abs(dx-ev.xmotion.x)>=MOVE_THRESHOLD
		    || abs(dy-ev.xmotion.y)>=MOVE_THRESHOLD) {
		    XChangeActivePointerGrab(dpy, ButtonMotionMask
					     |ButtonReleaseMask, 
					     wCursor[WCUR_MOVE], CurrentTime);
		    grabbed=1;
		} else {
		    break;
		}
	    }
	    x = ev.xmotion.x_root - dx;
	    y = ev.xmotion.y_root - dy;
	    XMoveWindow(dpy, icon->core->window, x, y);
	    if (dockable 
		&& ((scr->dock->on_right_side 
		     && x > scr->scr_width - ICON_WIDTH*2)
		    || (x < ICON_WIDTH && !scr->dock->on_right_side))) {
		if (wDockSnapIcon(scr->dock, aicon, x, y, &shad_x, &shad_y,
				  False)) {
		    XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
		    if (!docking) {
			Window wins[2];

			wins[0] = icon->core->window;
			wins[1] = scr->dock_shadow;
			XRestackWindows(dpy, wins, 2);
			XMapWindow(dpy, scr->dock_shadow);
		    }
		    docking = 1;
		}
	    } else if (docking) {
		XUnmapWindow(dpy, scr->dock_shadow);
		docking = 0;
	    }
	    break;

	 case ButtonRelease:
	    if (docking) {
		/* icon was docked */
		SlideWindow(icon->core->window, x, y, shad_x, shad_y,
			    DOCK_SPRING_STEPS, DOCK_SPRING_DELAY);
		XUnmapWindow(dpy, scr->dock_shadow);
		aicon->x_pos = shad_x;
		aicon->y_pos = shad_y;
		
		wDockAttachIcon(scr->dock, aicon);
	    } else {
		XMoveWindow(dpy, icon->core->window, x, y);
		aicon->x_pos = x;
		aicon->y_pos = y;
	    }
	    done = 1;
	    break;
	}
    }
#ifdef DEBUG
    puts("End icon move");
#endif
    XUngrabPointer(dpy, CurrentTime);	    
}
