/*
 * src/workset.c - working set management
 *
 * Copyright (C) 1999, 2000 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
 *
 * This 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, or (at your option)
 * any later version.
 * 
 * This software 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 package; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.  
 */

#if HAVE_CONFIG_H
# include <config.h>
#endif 

#include <string.h>

#include "display.h"
#include "workset.h"
#include "wimpwm.h"
#include "screen.h"
#include "events.h"

#define WORKSET_WINDOW_WIDTH  200
#define WORKSET_WINDOW_HEIGHT 30

/*
 * CURRENT_WORKSET is the number of the working set currently in use.
 * All new windows will be assigned to this working set.
 */
int current_workset;

/*
 * WORKSETS holds information about all available working sets.
 */
struct workset worksets[MAX_WORKSETS];
/*
 * WORKSET_COUNT holds the number of defined working sets in the arrat
 * WORKSETS.
 */
int workset_count;

/*
 * The window for displaying the working set name after a switch.
 */
Window workset_window = None;

/*
 * This is non-zero when WORKSET_WINDOW above is active and acts
 * as a counter until the window will be deleted.
 */
static int workset_window_active = 0;

/*
 * Draw the contents of the woking set indicator window.
 */ 
void
draw_workset_window (void)
{
  char buf[128];
  XPoint points0[3];

  if (workset_window != None)
    {
      XClearWindow (display, workset_window);

      sprintf (buf, "%s", worksets[current_workset].name);
      XDrawString(display, workset_window, active_gc,
		  (WORKSET_WINDOW_WIDTH / 2) -
		  XTextWidth (font, buf, strlen (buf)) / 2,
		  (WORKSET_WINDOW_HEIGHT / 2) +
		  font->ascent / 2, buf, strlen (buf));

      points0[0].x = 0;
      points0[0].y = WORKSET_WINDOW_HEIGHT - 1;
      points0[1].x = 0;
      points0[1].y = 0;
      points0[2].x = WORKSET_WINDOW_WIDTH;
      points0[2].y = 0;
      XDrawLines(display, workset_window,
		 highlight_gc, points0, 3, CoordModeOrigin);
      points0[0].x = WORKSET_WINDOW_WIDTH;
      points0[0].y = 1;
      points0[1].x = WORKSET_WINDOW_WIDTH;
      points0[1].y = WORKSET_WINDOW_HEIGHT - 1;
      points0[2].x = 0;
      points0[2].y = WORKSET_WINDOW_HEIGHT - 1;
      XDrawLines(display, workset_window, shadow_gc,
		 points0, 3, CoordModeOrigin);

    }
}

/*
 * Gets called every 10th of a second.  Delete the working set
 * indicator as soon as it times out.
 */
void
workset_tick (void)
{
  if (workset_window_active > 0)
    {
      if (--workset_window_active == 0)
	{
	  XUnmapWindow (display, workset_window);
	}
    }
}

/*
 * Select the working set with number NUMBER as the current working
 * set.
 */
result_t
workset_select (int number)
{
  client_t client, old_current;
  int old_workset;
  int width, height;

  if (number < 0 || number >= workset_count)
    return RESULT_INVALID;

  if (number == current_workset)
    return RESULT_SUCCESS;

  old_workset = current_workset;
  current_workset = number;

  for (client = client_root; client; client = client->next)
    if (client->workset == current_workset ||
	client->workset == -1)
      make_client_visible (client);
    else if (client->workset == old_workset)
      make_client_invisible (client);

  if (current_client && current_client->workset == old_workset)
    worksets[old_workset].last_focused = current_client->window;
  else
    worksets[old_workset].last_focused = None;

  if (worksets[current_workset].last_focused != None)
    {
      if ((client = find_client (worksets[current_workset].last_focused, 0)))
	{
	  old_current = current_client;
	  current_client = client;
	  transfer_focus (current_client, old_current);
	  ignore_enter_event = 1;
	}
      else
	current_client = NULL;
    }
  else
    {
      focus_any_client ();
    }
  width = WORKSET_WINDOW_WIDTH;
  height = WORKSET_WINDOW_HEIGHT;
  if (workset_window == None)
    {
      workset_window =
	XCreateSimpleWindow(display, root_window, 
			    DisplayWidth (display, default_screen) / 2
			    - width / 2,
			    DisplayHeight (display, default_screen) / 2
			    - height / 2,
			    width, 
			    height, 1,
			    BlackPixel(display, default_screen), 
			    background_color.pixel);
      if (!workset_window) 
	{
	  error ("cannot create working set indicator");
	  return RESULT_NOMEM;
	}

      XStoreName(display, workset_window, "Working Set");
      XSetIconName(display, workset_window, "Working Set");
      XSelectInput (display, workset_window, ExposureMask);

    }
  else
    {
      XMoveResizeWindow (display, workset_window,
			 DisplayWidth (display, default_screen) / 2
			 - width / 2,
			 DisplayHeight (display, default_screen) / 2
			 - height / 2,
			 width, 
			 height);
    }
  XMapRaised (display, workset_window);
  
  draw_workset_window ();

  workset_window_active = 10;

  return RESULT_SUCCESS;
}

/*
 * Navigate to the previous working set when DIRECTION is negative and
 * navigate to the next working set if DIRECTION is positive.
 * Behaviour is undefined when DIRECTION is zero.  Working sets wrap
 * around when the first or last working set is reached.
 */
result_t
workset_next (int direction)
{
  if (direction < 0)
    {
      if (current_workset == 0)
	return workset_select (workset_count - 1);
      else
	return workset_select (current_workset - 1);
    }
  else if (direction > 0)
    {
      if (current_workset == workset_count - 1)
	return workset_select (0);
      else
	return workset_select (current_workset + 1);
    }
  return RESULT_INVALID;
}

/*
 * Append a new working set to the end of the list of already defined
 * working sets.
 */
result_t
create_workset (char * name)
{
  if (workset_count >= MAX_WORKSETS)
    return RESULT_NOMEM;
  strncpy (worksets[workset_count].name, name, MAX_WORKSET_NAME_LEN);
  worksets[workset_count].name[MAX_WORKSET_NAME_LEN - 1] = '\0';
  worksets[workset_count].last_focused = None;
  workset_count++;
  
  return RESULT_SUCCESS;
}

result_t
setup_worksets (void)
{
  if (list_p (working_set_list))
    {
      zzz_scm_t c, s;

      c = working_set_list;
      while (cons_p (c))
	{
	  s = car (c);
	  if (string_p (s))
	    {
	      create_workset (string_val (s));
	    }
	  c = cdr (c);
	}
    }
  else
    {
      create_workset ("Default");
    }

  return RESULT_SUCCESS;
}
