/* docindex.c - Creates the window used by the document index in go and gimp.
 *
 * Copyright (C) 1998 Chris Lahey.
 *
 * 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, 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.
 */
#include <string.h>
#include <stdlib.h>

#include <gtk/gtk.h>

#include "docindexif.h"
#include "docindex.h"
#include "gimpdnd.h"
#include "gimprc.h"

#include "config.h"
#include "libgimp/gimpenv.h"
#include "libgimp/gimpintl.h"

idea_manager *ideas = NULL;
static GList *idea_list = NULL;   /* of gchar *. */
static gint x = 0, y = 0, width = 0, height = 0;

static GtkTargetEntry drag_types[] =
{
  GIMP_TARGET_URI_LIST
};
static gint n_drag_types = sizeof (drag_types) / sizeof (drag_types[0]);

static void create_idea_list                       (void);
void        docindex_configure_drop_on_widget      (GtkWidget * widget);
void        docindex_cell_configure_drop_on_widget (GtkWidget * widget);


static void
docindex_dnd_filenames_dropped (GtkWidget        *widget,
				GdkDragContext   *context,
				gint              x,
				gint              y,
				GtkSelectionData *selection_data,
				guint             info,
				guint             time)
{
  gint len;
  gchar *data;
  gchar *end;

  switch (info)
    {
    case GIMP_DND_TYPE_URI_LIST:
      data = (gchar *) selection_data->data;
      len = selection_data->length;
      while (len > 0)
	{
	  end = strstr (data, "\x0D\x0A");
	  if (end != NULL)
	    *end = 0;
	  if (*data != '#')
	    {
	      gchar *filename = strchr (data, ':');
	      if (filename != NULL)
		filename ++;
	      else
		filename = data;
	      open_file_in_position (filename, -1);
	    }
	  if (end)
	    {
	      len -= end - data + 2;
	      data = end + 2;
	    }
	  else
	    len = 0;
	}
      break;
    }
  return;
}

void
docindex_configure_drop_on_widget (GtkWidget * widget)
{
  gtk_drag_dest_set (widget,
                     GTK_DEST_DEFAULT_MOTION |
                     GTK_DEST_DEFAULT_HIGHLIGHT |
                     GTK_DEST_DEFAULT_DROP,
                     drag_types, n_drag_types,
                     GDK_ACTION_COPY);

  gtk_signal_connect (GTK_OBJECT (widget), "drag_data_received",
		      GTK_SIGNAL_FUNC (docindex_dnd_filenames_dropped),
		      NULL);
}

static void
docindex_cell_dnd_filenames_dropped (GtkWidget        *widget,
				     GdkDragContext   *context,
				     gint              x,
				     gint              y,
				     GtkSelectionData *selection_data,
				     guint             info,
				     guint             time)
{
  gint len;
  gchar *data;
  gchar *end;
  gint position = g_list_index (GTK_TREE (ideas->tree)->children, widget);

  switch (info)
    {
    case GIMP_DND_TYPE_URI_LIST:
      data = (gchar *) selection_data->data;
      len = selection_data->length;
      while (len > 0)
	{
	  end = strstr (data, "\x0D\x0A");
	  if (end != NULL)
	    *end = 0;
	  if (*data != '#')
	    {
	      gchar *filename = strchr (data, ':');
	      if (filename != NULL)
		filename ++;
	      else
		filename = data;
	      open_file_in_position (filename, position);
	    }
	  if (end)
	    {
	      len -= end - data + 2;
	      data = end + 2;
	    }
	  else
	    len = 0;
	}
      break;
    }
  return;
}

void
docindex_cell_configure_drop_on_widget (GtkWidget *widget)
{
  gtk_drag_dest_set (widget,
                     GTK_DEST_DEFAULT_MOTION |
                     GTK_DEST_DEFAULT_HIGHLIGHT |
                     GTK_DEST_DEFAULT_DROP,
                     drag_types, n_drag_types,
                     GDK_ACTION_COPY);

  gtk_signal_connect (GTK_OBJECT (widget), "drag_data_received",
		      GTK_SIGNAL_FUNC (docindex_cell_dnd_filenames_dropped),
		      NULL);
}

gboolean
idea_window_delete_event_callback (GtkWidget *widget,
				   GdkEvent  *event,
				   gpointer   data)
{
  if (! exit_from_go ())
    {
      save_idea_manager (ideas);
      create_idea_list ();
      g_free (ideas);
      ideas = 0;
    }

  return FALSE;
}

void
idea_hide_callback (GtkWidget *widget,
		    gpointer   data)
{
  if (ideas || idea_list || width || height)
    save_idea_manager (ideas);

  /* False if exitting */
  if ((! exit_from_go ()) && ideas)
    {
      create_idea_list ();
      gtk_widget_destroy (ideas->window);
      g_free (ideas);
      ideas = 0;
    }
}

void
open_idea_window (void)
{
  make_idea_window (-1, -1);
}

static void
load_from_list (gpointer data,
		gpointer data_null)
{
  idea_add_in_position ((gchar *) data, -1);
}

FILE *
idea_manager_parse_init (/* RETURNS: */
			 gint *window_x,
			 gint *window_y,
			 gint *window_width,
			 gint *window_height)
{
  FILE *fp = NULL;
  gchar *desktopfile;

  desktopfile = gimp_personal_rc_file ("ideas");
  fp = fopen (desktopfile, "r");
  g_free (desktopfile);
      
  /* Read in persistant desktop information. */
  if (fp)
    {	  
      *window_x      = getinteger (fp);
      *window_y      = getinteger (fp);
      *window_width  = getinteger (fp);
      *window_height = getinteger (fp);
    }

  return fp;
}

gchar *
idea_manager_parse_line (FILE * fp)
{
  gint length;
  gchar *filename;

  length = getinteger (fp);

  if (!feof (fp) && !ferror (fp))
    {
      filename = g_malloc0 (length + 1);
      filename[fread (filename, 1, length, fp)] = 0;
      clear_white (fp);
      return filename;
    }

  return NULL;
}

void
load_idea_manager (idea_manager *ideas)
{
  FILE *fp = NULL;

  if ( ! idea_list )
    fp = idea_manager_parse_init (&x, &y, &width, &height);

  if (idea_list || fp)
    {
      gtk_widget_set_usize (ideas->window, width, height);
      gtk_widget_show (ideas->window);
      gtk_widget_set_uposition (ideas->window, x, y);
      gtk_idle_add (reset_usize, ideas->window);
      if (fp)
	{
	  gchar *title;
	  clear_white (fp);
	  
	  while ((title = idea_manager_parse_line (fp)))
	    {
	      idea_add_in_position (title, -1);
	      g_free (title);
	    }
	  fclose (fp);
	}
      else
	{
	  g_list_foreach (idea_list, load_from_list, NULL);
	  g_list_foreach (idea_list, (GFunc) g_free, NULL);
	  g_list_free (idea_list);
	  idea_list = 0;
	}
    }
  else
    gtk_widget_show (ideas->window);
}

static void
save_to_ideas (gpointer data,
	       gpointer user_data)
{
  gchar *title = GTK_LABEL (GTK_BIN ((GtkWidget *) data)->child)->label;

  fprintf ((FILE *) user_data, "%d %s\n", strlen (title), title);
}

static void
save_list_to_ideas (gpointer data,
		    gpointer user_data)
{
  gchar *title = (gchar *) data;

  fprintf ((FILE *) user_data, "%d %s\n", strlen (title), title);
}

void
save_idea_manager (idea_manager *ideas)
{
  FILE *fp;
  gchar *desktopfile;
    
  /* open persistant desktop file. */
  desktopfile = gimp_personal_rc_file ("ideas");
  fp = fopen (desktopfile, "w");
  g_free (desktopfile);

  if (fp)
    {
      if (ideas)
	{
	  gint x, y, width, height;
	  
	  gdk_window_get_geometry (ideas->window->window,
				   &x, &y, &width, &height, NULL);
	  gdk_window_get_origin (ideas->window->window, &x, &y);

	  fprintf (fp, "%d %d %d %d\n", x, y, width, height);

	  g_list_foreach (GTK_TREE (ideas->tree)->children, save_to_ideas, fp);
	}
      else
	{
	  if (idea_list)
	    {
	      fprintf (fp, "%d %d %d %d\n", x, y, width, height);

	      g_list_foreach (idea_list, save_list_to_ideas, fp);
	    }
	}

      fclose (fp);
    }
}

static void
save_to_list (gpointer data,
	      gpointer null_data)
{
  gchar *title = g_strdup (GTK_LABEL (GTK_BIN ((GtkWidget *) data)->child)->label);
  idea_list = g_list_append (idea_list, title);
}

static void
create_idea_list (void)
{
  gdk_window_get_geometry (ideas->window->window,
			   &x, &y, &width, &height, NULL);
  gdk_window_get_origin (ideas->window->window, &x, &y);

  if (idea_list)
    {
      g_list_foreach (idea_list, (GFunc) g_free, NULL);
      g_list_free (idea_list);
      idea_list = 0;
    }

  g_list_foreach (GTK_TREE (ideas->tree)->children, save_to_list, NULL);
}

static gint
open_or_raise_callback (GtkWidget      *widget,
			GdkEventButton *event,
			gpointer        func_data )
{
  if (GTK_IS_TREE_ITEM (widget) &&
      event->type == GDK_2BUTTON_PRESS)
    {
      open_or_raise (GTK_LABEL (GTK_BIN (widget)->child )->label);
    }

  return FALSE;
} 

void
raise_idea_callback (GtkWidget *widget,
		     gpointer   data)
{
  if (ideas)
    gdk_window_raise (ideas->window->window);
  else
    open_idea_window ();
}

static void
check_needed (gpointer data,
	      gpointer user_data)
{
  GtkWidget *widget = (GtkWidget *) data;
  struct bool_char_pair *pair = (struct bool_char_pair *) user_data;

  if (strcmp (pair->string, GTK_LABEL (GTK_BIN (widget)->child )->label) == 0)
    {
      pair->boole = TRUE;
    }
}

static void
check_needed_list (gpointer data,
		   gpointer user_data)
{
  struct bool_char_pair *pair = (struct bool_char_pair *) user_data;

  if (strcmp (pair->string, (gchar *) data ) == 0)
    {
      pair->boole = TRUE;
    }
}

static void
idea_add_in_position_with_select (gchar    *title,
				  gint      position,
				  gboolean  select )
{
  GtkWidget *treeitem;
  struct bool_char_pair pair;

  pair.boole = FALSE;
  pair.string = title;

  if (ideas)
    {
      g_list_foreach (GTK_TREE (ideas->tree)->children, check_needed, &pair);

      if (! pair.boole)
	{
	  treeitem = gtk_tree_item_new_with_label (title);
	  if (position < 0)
	    gtk_tree_append (GTK_TREE (ideas->tree), treeitem);
	  else
	    gtk_tree_insert (GTK_TREE (ideas->tree), treeitem, position);

	  gtk_signal_connect (GTK_OBJECT (treeitem),
			      "button_press_event",
			      GTK_SIGNAL_FUNC (open_or_raise_callback),
			      NULL);

	  docindex_cell_configure_drop_on_widget (treeitem);

	  gtk_widget_show (treeitem);

	  if (select)
	    gtk_tree_select_item (GTK_TREE (ideas->tree),
				  gtk_tree_child_position (GTK_TREE (ideas->tree ), treeitem));
	}
    }
  else
    {
      if (! idea_list)
	{
	  FILE *fp = NULL;
	  gchar *desktopfile;

	  /* open persistant desktop file. */
	  desktopfile = gimp_personal_rc_file ("ideas");
	  fp = fopen (desktopfile, "r");
	  g_free (desktopfile);

	  /* Read in persistant desktop information. */
	  if (fp)
	    {  
	      gchar *title;
	      gint length;

	      x      = getinteger (fp);
	      y      = getinteger (fp);
	      width  = getinteger (fp);
	      height = getinteger (fp);

	      clear_white (fp);

	      while (!feof (fp) && !ferror (fp))
		{
		  length = getinteger (fp);
		  title = g_malloc0 (length + 1);
		  title[fread (title, 1, length, fp)] = 0;
		  idea_list = g_list_append (idea_list, g_strdup (title));
		  g_free (title);
		  clear_white (fp);
		}
	      fclose (fp);
	    }
	}

      g_list_foreach (idea_list, check_needed_list, &pair);

      if (! pair.boole)
	{
	  if (position < 0)
	    idea_list = g_list_append (idea_list, g_strdup (title));
	  else
	    idea_list = g_list_insert (idea_list, g_strdup (title), position);
	}
    }
}

void
idea_add (gchar *title)
{
  idea_add_in_position (title, 0);
}

void
idea_add_in_position (gchar *title,
		      gint   position)
{
  idea_add_in_position_with_select (title, position, TRUE);
}

static gint
idea_move (GtkWidget *widget,
	   gint       distance,
	   gboolean   select)
{
  gint orig_position = g_list_index (GTK_TREE (ideas->tree)->children, widget);
  gint position = orig_position + distance;
  gchar *title;

  if (position < 0)
    position = 0;

  if (position >= g_list_length (GTK_TREE (ideas->tree)->children))
    position = g_list_length (GTK_TREE (ideas->tree)->children) - 1;

  if (position != orig_position)
    {
      title = g_strdup (GTK_LABEL (GTK_BIN (widget)->child )->label);
      gtk_container_remove (GTK_CONTAINER (ideas->tree), widget);
      idea_add_in_position_with_select (title, position, select);
      g_free (title); 
   }

  return position - orig_position;
}

static void
idea_remove (GtkWidget *widget)
{
  gint position = g_list_index (GTK_TREE (ideas->tree )->children, widget);

  gtk_container_remove (GTK_CONTAINER (ideas->tree), widget);

  if (g_list_length (GTK_TREE (ideas->tree)->children) - 1 < position)
    position = g_list_length (GTK_TREE (ideas->tree)->children) - 1;

  gtk_tree_select_item (GTK_TREE (ideas->tree), position);  
}

void
idea_up_callback (GtkWidget *widget,
		  gpointer   data)
{
  GtkWidget *selected;

  if (GTK_TREE (ideas->tree)->selection)
    {
      selected = GTK_TREE (ideas->tree)->selection->data;
      if (idea_move (selected, -1, TRUE) != -1)
	gtk_statusbar_push (GTK_STATUSBAR (ideas->status), ideas->contextid,
			    _("This file cannot be moved up."));
    }
  else
    gtk_statusbar_push (GTK_STATUSBAR (ideas->status), ideas->contextid,
			_("There's no selection to move up."));
}

void
idea_down_callback (GtkWidget *widget,
		    gpointer   data)
{
  GtkWidget *selected;

  if (GTK_TREE (ideas->tree)->selection)
    {
      selected = GTK_TREE (ideas->tree)->selection->data;
      if (idea_move (selected, 1, TRUE) != 1)
	gtk_statusbar_push (GTK_STATUSBAR (ideas->status), ideas->contextid,
			    _("This file cannot be moved down."));
    }
  else
    gtk_statusbar_push (GTK_STATUSBAR (ideas->status), ideas->contextid,
			_("There's no selection to move down."));
}

void
idea_remove_callback (GtkWidget *widget,
		      gpointer   data)
{
  GtkWidget *selected;

  if (GTK_TREE (ideas->tree)->selection)
    {
      selected = GTK_TREE (ideas->tree)->selection->data;
      idea_remove (selected);
    }
  else
    gtk_statusbar_push (GTK_STATUSBAR (ideas->status), ideas->contextid,
			_("There's no selection to remove."));
}

void
close_idea_window( void )
{
  idea_hide_callback (NULL, NULL);
}
