/* workspace.c- Workspace management
 * 
 *  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>
#include <ctype.h>

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "wcore.h"
#include "framewin.h"
#include "window.h"
#include "icon.h"
#include "funcs.h"
#include "menu.h"
#include "application.h"
#include "dock.h"
#include "actions.h"
#include "workspace.h"
#include "appicon.h"
#ifdef PAGER
#include "pager.h"
#endif

#include <proplist.h>


extern WPreferences wPreferences;
extern XContext wWinContext;



void
wWorkspaceMake(WScreen *scr, int count)
{
    while (count>0) {
	wWorkspaceNew(scr);
	count--;
    }
}


int
wWorkspaceNew(WScreen *scr)
{
    WWorkspace *wspace, **list;
    int i;
    
    if (scr->workspace_count < MAX_WORKSPACES) {
	scr->workspace_count++;
	
	wspace = wmalloc(sizeof(WWorkspace));

	wspace->name = wmalloc(strlen(_("Workspace %i"))+8);
	sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);

#ifdef PAGER
	wspace->view = wPagerAddWorkspace(scr->pager);
#endif
	
	list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
	
	for (i=0; i<scr->workspace_count-1; i++) {
	    list[i] = scr->workspaces[i];
	}
	list[i] = wspace;
	free(scr->workspaces);
	scr->workspaces = list;

	wWorkspaceMenuUpdate(scr, scr->workspace_menu);
	
	return scr->workspace_count-1;
    }
    return -1;
}



void
wWorkspaceDelete(WScreen *scr, int workspace)
{
    WWindow *tmp;
    /*
    WWorkspace **list;
    int i, j;
     */

    if (workspace<=0)
	return;

    /* verify if workspace is in use by some window */
    tmp = scr->focused_window;
    while (tmp) {
	if (tmp->frame->workspace==workspace)
	    return;
	tmp = tmp->prev;
    }
    
#if 0
    if (workspace>0)
	wWorkspaceChange(scr, workspace-1);
    else
	wWorkspaceChange(scr, 1);
    
    list = wmalloc(sizeof(WWorkspace*)*(scr->workspace_count-1));
    j = 0;
    for (i=0; i<scr->workspace_count; i++) {
	if (i!=workspace)
	    list[j++] = scr->workspaces[i];
	else {
	    if (scr->workspaces[i]->name)
		free(scr->workspaces[i]->name);
	    free(scr->workspaces[i]);
	}
    }
    free(scr->workspaces);
    scr->workspaces = list;
#endif
    scr->workspace_count--;
#ifdef PAGER_
    wPagerRemoveWorkspace(scr->pager, workspace);
#endif


    /* update menu */
    if (scr->workspace_menu) {
	WMenu *menu = scr->workspace_menu;
	
	wMenuRemoveItem(menu, menu->entry_no-1);
	/* don't let user destroy current workspace */
	if (scr->current_workspace == scr->workspace_count-1) {
	    wMenuSetEnabled(menu, 1, False);
	} else {
	    wMenuSetEnabled(menu, 1, True);
	}
	wMenuRealize(menu);
    }
}


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

    if (workspace >= MAX_WORKSPACES || workspace < 0)
	return;

    if (workspace > scr->workspace_count-1) {
	wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
    }
    
    if (scr->workspace_menu) {
	WMenu *menu = scr->workspace_menu;
	
	menu->entries[scr->current_workspace+2]->flags.indicator_on = 0;
	menu->entries[workspace+2]->flags.indicator_on = 1;

	/* don't let user destroy current workspace */
	if (workspace == scr->workspace_count-1) {
	    wMenuSetEnabled(menu, 1, False);
	} else {
	    wMenuSetEnabled(menu, 1, True);
	}
	wMenuPaint(menu);
    }
    
    scr->current_workspace = workspace;

    tmp = scr->focused_window;
    if ((tmp = scr->focused_window)!= NULL) {	
	while (tmp) {
	    if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
		/* unmap windows not on this workspace */
		if ((tmp->flags.mapped||tmp->flags.shaded)
		    && !tmp->window_flags.omnipresent
		    && !tmp->flags.changing_workspace) {
		    XUnmapWindow(dpy, tmp->frame->core->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->core->window);
		    if (!foc)
			foc = tmp;
		    if (!tmp->flags.shaded)
			tmp->flags.mapped = 1;
		}
	    }
	    tmp = tmp->prev;
	}

	if (scr->focused_window->flags.mapped)
	    foc = scr->focused_window;

	if (wPreferences.focus_mode == WKF_CLICK) {
	    wSetFocusTo(scr, foc);
	} else {
	    wSetFocusTo(scr, NULL);
	}
    }
#ifdef DOCK
    if (scr->dock)
	wAppIconPaint(scr->dock->icon_array[0]);
#endif
}


static void
switchWSCommand(WMenu *menu, WMenuEntry *entry)
{
    wWorkspaceChange(menu->frame->screen_ptr, (int)entry->clientdata);
}



static void
deleteWSCommand(WMenu *menu, WMenuEntry *entry)
{
    wWorkspaceDelete(menu->frame->screen_ptr, 
		     menu->frame->screen_ptr->workspace_count-1);
}



static void
newWSCommand(WMenu *menu, WMenuEntry *foo)
{
    int ws;

    ws = wWorkspaceNew(menu->frame->screen_ptr);
    /* autochange workspace*/
    if (ws>=0)
	wWorkspaceChange(menu->frame->screen_ptr, ws);
    
    
    /*
    if (ws<9) {
	int kcode;
	if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
	    kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
	    entry->rtext = 
	      strdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
	}
    }*/
}


static char*
cropline(char *line)
{
    char *start, *end;
    
    if (strlen(line)==0)
	return line;
    
    start = line;
    end = &(line[strlen(line)])-1;
    while (isspace(*line) && *line!=0) line++;
    while (isspace(*end) && end!=line) {
	*end=0;
	end--;
    }
    return line;
}


/* callback for when menu entry is edited */
static void
onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
{
    int wspace = (int)entry->clientdata;
    WScreen *scr = menu->frame->screen_ptr;
    char buf[MAX_WORKSPACENAME_WIDTH+1];
    char *tmp;

    /* trim white spaces */
    tmp = cropline(entry->text);
    
    if (strlen(tmp)==0) {
	sprintf(buf, _("Workspace %i"), wspace+1);
	free(entry->text);
    } else {
	strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
	free(entry->text);
    }
    buf[MAX_WORKSPACENAME_WIDTH] = 0;
    entry->text = strdup(buf);
    
    /* update workspace */
    free(scr->workspaces[wspace]->name);
    scr->workspaces[wspace]->name = strdup(entry->text);
   
    UpdateSwitchMenuWorkspace(scr, wspace);
}


WMenu*
wWorkspaceMenuMake(WScreen *scr)
{
    WMenu *wsmenu;

    wsmenu = wMenuCreate(scr, _("Workspaces"), False);
    if (!wsmenu) {
	wWarning(_("could not create Workspace menu"));
	return NULL;
    }
    
    /* callback to be called when an entry is edited */
    wsmenu->on_edit = onMenuEntryEdited;
    
    wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
    wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);

    return wsmenu;
}



void
wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
{
    int i, ws;
    char title[MAX_WORKSPACENAME_WIDTH];
    WMenuEntry *entry;

    if (!menu)
	return;

    if (menu->entry_no < scr->workspace_count+2) {
	i = scr->workspace_count-(menu->entry_no-2);
	ws = menu->entry_no - 2;
	while (i>0) {
	    strcpy(title, scr->workspaces[ws]->name);

	    entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
	    entry->flags.indicator = 1;
	    entry->flags.editable = 1;

	    i--;
	    ws++;
	}
    }
    wMenuRealize(menu);
    
    for (i=0; i<scr->workspace_count; i++) {	
	menu->entries[i+2]->flags.indicator_on = 0;
    }
    menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;

    /* don't let user destroy current workspace */
    if (scr->current_workspace == scr->workspace_count-1) {
	wMenuSetEnabled(menu, 1, False);
    } else {
	wMenuSetEnabled(menu, 1, True);
    }
    
    wMenuPaint(menu);
}


void
wWorkspaceSaveState(WScreen *scr)
{
    proplist_t parr, pstr, pkey;
    int i;
    
    
    parr = PLMakeArrayFromElements(NULL);
    for (i=0; i < scr->workspace_count; i++) {
	pstr = PLMakeString(scr->workspaces[i]->name);
	PLAppendArrayElement(parr, pstr);
    }
    
    pkey = PLMakeString("Workspaces");
    
    PLInsertDictionaryEntry(scr->session_state, pkey, parr);
}


void
wWorkspaceRestoreState(WScreen *scr)
{
    proplist_t parr, pstr, pkey;
    int i, wscount;

    pkey = PLMakeString("Workspaces");
    
    parr = PLGetDictionaryEntry(scr->session_state, pkey);
    PLRelease(pkey);

    if (!parr)
	return;
    
    wscount = scr->workspace_count;
    for (i=0; i < PLGetNumberOfElements(parr); i++) {
	pstr = PLGetArrayElement(parr, i);
	if (i >= scr->workspace_count)
	    wWorkspaceNew(scr);
	if (scr->workspace_menu) {
	    free(scr->workspace_menu->entries[i+2]->text);
	    scr->workspace_menu->entries[i+2]->text = strdup(PLGetString(pstr));
	    scr->workspace_menu->flags.realized = 0;
	}
	free(scr->workspaces[i]->name);
	scr->workspaces[i]->name = strdup(PLGetString(pstr));
    }
}

