/*
 * Copyright (c) 2003, 2004 Jean-Yves Lefort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "sg-util.h"
#include "sgtk-cell-renderer-pixbuf.h"
#include "st-category-view.h"
#include "st-stock.h"
#include "st-category-bag.h"
#include "st-handler.h"
#include "st-category-store.h"

/*** type definitions ********************************************************/

enum {
  SELECTION_CHANGED,
  LAST_SIGNAL
};
    
struct _STCategoryViewPrivate
{
  unsigned int		selection_changed_hid;
};

/*** variable declarations ***************************************************/

static GObjectClass *parent_class = NULL;
static unsigned int category_view_signals[LAST_SIGNAL] = { 0 };

/*** function declarations ***************************************************/

static void st_category_view_init	(STCategoryView		*view);
static void st_category_view_class_init	(STCategoryViewClass	*class);

static void st_category_view_row_expanded_h	(GtkTreeView	*treeview,
						 GtkTreeIter	*iter,
						 GtkTreePath	*path,
						 gpointer	user_data);
static void st_category_view_row_collapsed_h	(GtkTreeView	*treeview,
						 GtkTreeIter	*iter,
						 GtkTreePath	*path,
						 gpointer	user_data);
static void st_category_view_selection_changed_h (GtkTreeSelection *selection,
						  gpointer	   data);

static gboolean st_category_view_new_cb (GtkTreeModel *model,
					 GtkTreePath *path,
					 GtkTreeIter *iter,
					 gpointer data);
static void st_category_view_model_row_has_child_toggled_h (GtkTreeModel *model,
							    GtkTreePath *arg1,
							    GtkTreeIter *arg2,
							    gpointer user_data);
static void st_category_view_model_category_appended_h (STCategoryStore *store,
							STCategoryBag *bag,
							gpointer user_data);

/*** implementation **********************************************************/

GType
st_category_view_get_type (void)
{
  static GType category_view_type = 0;
  
  if (! category_view_type)
    {
      static const GTypeInfo category_view_info = {
	sizeof(STCategoryViewClass),
	NULL,
	NULL,
	(GClassInitFunc) st_category_view_class_init,
	NULL,
	NULL,
	sizeof(STCategoryView),
	0,
	(GInstanceInitFunc) st_category_view_init,
      };
      
      category_view_type = g_type_register_static(GTK_TYPE_TREE_VIEW,
						  "STCategoryView",
						  &category_view_info,
						  0);
    }

  return category_view_type;
}

static void
st_category_view_class_init (STCategoryViewClass *class)
{
  parent_class = g_type_class_peek_parent(class);

  g_type_class_add_private(class, sizeof(STCategoryViewPrivate));

  category_view_signals[SELECTION_CHANGED] = g_signal_new("selection-changed",
							  ST_TYPE_CATEGORY_VIEW,
							  G_SIGNAL_RUN_LAST,
							  G_STRUCT_OFFSET(STCategoryViewClass, selection_changed),
							  NULL,
							  NULL,
							  g_cclosure_marshal_VOID__VOID,
							  G_TYPE_NONE,
							  0);
}

static void
st_category_view_init (STCategoryView *view)
{
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;
  GtkTreeSelection *selection;

  view->priv = G_TYPE_INSTANCE_GET_PRIVATE(view, ST_TYPE_CATEGORY_VIEW, STCategoryViewPrivate);

  /* configure the view */

  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);

  gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), TRUE);
  gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(view),
				      st_category_store_search_equal_func,
				      NULL,
				      NULL);

  /* create the columns */

  column = gtk_tree_view_column_new();

  renderer = sgtk_cell_renderer_pixbuf_new();

  gtk_tree_view_column_pack_start(column, renderer, FALSE);
  gtk_tree_view_column_set_attributes(column, renderer,
				      "stock-id", ST_CATEGORY_STORE_COLUMN_STOCK_ID,
				      "stock-id-closed", ST_CATEGORY_STORE_COLUMN_STOCK_ID_CLOSED,
				      "stock-id-open", ST_CATEGORY_STORE_COLUMN_STOCK_ID_OPEN,
				      NULL);

  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_attributes(column, renderer,
				      "text", ST_CATEGORY_STORE_COLUMN_LABEL,
				      NULL);
  
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);

  /* connect to signals */

  g_object_connect(view,
		   "signal::row-expanded", st_category_view_row_expanded_h, NULL,
		   "signal::row-collapsed", st_category_view_row_collapsed_h, NULL,
		   NULL);

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  view->priv->selection_changed_hid = g_signal_connect(selection,
						       "changed",
						       G_CALLBACK(st_category_view_selection_changed_h),
						       view);
}

static void
st_category_view_selection_changed_h (GtkTreeSelection *selection,
				      gpointer data)
{
  STCategoryView *view = data;
  GtkTreeIter iter;
  GtkTreeModel *model;

  g_return_if_fail(ST_IS_CATEGORY_VIEW(view));
  
  if (gtk_tree_selection_get_selected(selection, &model, &iter))
    {
      STCategoryBag *bag;

      gtk_tree_model_get(model, &iter, ST_CATEGORY_STORE_COLUMN_BAG, &bag, -1);
      st_category_bag_select(bag);
      g_object_unref(bag);

      g_signal_emit(view, category_view_signals[SELECTION_CHANGED], 0);
    }
}

static void
st_category_view_row_expanded_h (GtkTreeView *treeview,
				 GtkTreeIter *iter,
				 GtkTreePath *path,
				 gpointer user_data)
{
  STCategoryBag *bag;

  gtk_tree_model_get(gtk_tree_view_get_model(treeview), iter, ST_CATEGORY_STORE_COLUMN_BAG, &bag, -1);
  SG_SET_FLAGS(bag->flags, ST_CATEGORY_BAG_EXPANDED);
  g_object_unref(bag);
}

static void
st_category_view_row_collapsed_h (GtkTreeView *treeview,
				  GtkTreeIter *iter,
				  GtkTreePath *path,
				  gpointer user_data)
{
  STCategoryBag *bag;

  gtk_tree_model_get(gtk_tree_view_get_model(treeview), iter, ST_CATEGORY_STORE_COLUMN_BAG, &bag, -1);
  SG_UNSET_FLAGS(bag->flags, ST_CATEGORY_BAG_EXPANDED);
  g_object_unref(bag);
}

GtkWidget *
st_category_view_new (STCategoryStore *store)
{
  GtkWidget *view;

  g_return_val_if_fail(ST_IS_CATEGORY_STORE(store), NULL);
  
  view = g_object_new(ST_TYPE_CATEGORY_VIEW, NULL);

  gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
  gtk_tree_model_foreach(GTK_TREE_MODEL(store), st_category_view_new_cb, view);
  
  g_object_connect(store,
		   "signal::row-has-child-toggled", st_category_view_model_row_has_child_toggled_h, view,
		   "signal::category-appended", st_category_view_model_category_appended_h, view,
		   NULL);

  return view;
}

static gboolean
st_category_view_new_cb (GtkTreeModel *model,
			 GtkTreePath *path,
			 GtkTreeIter *iter,
			 gpointer data)
{
  STCategoryView *view = data;
  STCategoryBag *bag;

  gtk_tree_model_get(model, iter, ST_CATEGORY_STORE_COLUMN_BAG, &bag, -1);

  if (ST_CATEGORY_BAG_IS_EXPANDED(bag))
    gtk_tree_view_expand_row(GTK_TREE_VIEW(view), path, FALSE);

  if (ST_CATEGORY_BAG_IS_SELECTED(bag))
    {
      GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));

      g_signal_handler_block(selection, view->priv->selection_changed_hid);
      gtk_tree_selection_select_iter(selection, iter);
      g_signal_handler_unblock(selection, view->priv->selection_changed_hid);
    }

  g_object_unref(bag);

  return FALSE;			/* continue */
}

static void
st_category_view_model_row_has_child_toggled_h (GtkTreeModel *model,
						GtkTreePath *arg1,
						GtkTreeIter *arg2,
						gpointer user_data)
{
  STCategoryView *view = user_data;
  STCategoryBag *bag;

  gtk_tree_model_get(model, arg2, ST_CATEGORY_STORE_COLUMN_BAG, &bag, -1);
  if (ST_CATEGORY_BAG_IS_EXPANDED(bag))
    gtk_tree_view_expand_row(GTK_TREE_VIEW(view), arg1, FALSE);
  g_object_unref(bag);
}

static void
st_category_view_model_category_appended_h (STCategoryStore *store,
					    STCategoryBag *bag,
					    gpointer user_data)
{
  if (ST_CATEGORY_BAG_IS_SELECTED(bag))
    {
      STCategoryView *view = user_data;
      GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));

      g_signal_handler_block(selection, view->priv->selection_changed_hid);
      gtk_tree_selection_select_iter(selection, &bag->iter);
      g_signal_handler_unblock(selection, view->priv->selection_changed_hid);
    }
}

void
st_category_view_select_category (STCategoryView *view, STCategoryBag *bag)
{
  GtkTreeSelection *selection;

  g_return_if_fail(ST_IS_CATEGORY_VIEW(view));
  g_return_if_fail(ST_IS_CATEGORY_BAG(bag));

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  gtk_tree_selection_select_iter(selection, &bag->iter);
}
