/*
 * 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 <glib/gi18n.h>
#include <gtk/gtk.h>
#include "sgtk-util.h"

/*** cpp *********************************************************************/

#define COUNT_KEY		"sgtk-tree-model-count"
#define COUNTABLE_KEY		"sgtk-tree-model-countable"
#define MENU_KEY		"sgtk-widget-menu"
#define NEXT_WIDGET_KEY		"sgtk-entry-next-widget"

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

typedef struct
{
  int *width;
  int *height;
} LinkWindowInfo;

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

static void sgtk_entry_activate_h (GtkEntry *entry, gpointer data);

static gboolean sgtk_widget_popup_menu_h (GtkWidget *widget,
					  gpointer user_data);
static gboolean sgtk_widget_button_press_event_h (GtkWidget *widget,
						  GdkEventButton *event,
						  gpointer user_data);

static void sgtk_tree_model_set_count (GtkTreeModel *model, unsigned int count);

static void sgtk_tree_model_row_inserted_h (GtkTreeModel *model,
					    GtkTreePath *arg1,
					    GtkTreeIter *arg2,
					    gpointer user_data);
static void sgtk_tree_model_row_deleted_h (GtkTreeModel *model,
					   GtkTreePath *arg1,
					   gpointer user_data);

static gboolean sgtk_window_link_size_event_configure_h (GtkWidget *widget,
							 GdkEventConfigure *event,
							 gpointer user_data);

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

void
sgtk_flush (void)
{
  while (gtk_events_pending())
    gtk_main_iteration();
}

char *
sgtk_translate_func (const char *path, gpointer data)
{
  return _(path);
}

GdkPixbuf *
sgtk_pixbuf_scale (GdkPixbuf *pixbuf, GtkIconSize size)
{
  int width;
  int height;
  gboolean status;

  g_return_val_if_fail(GDK_IS_PIXBUF(pixbuf), NULL);

  status = gtk_icon_size_lookup(size, &width, &height);
  g_return_val_if_fail(status == TRUE, NULL);

  return gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
}

void
sgtk_entry_set_next_widget (GtkEntry *entry, GtkWidget *next)
{
  g_return_if_fail(GTK_IS_ENTRY(entry));
  g_return_if_fail(GTK_IS_WIDGET(next));
  g_return_if_fail((GtkWidget *) entry != next);

  g_object_set_data(G_OBJECT(entry), NEXT_WIDGET_KEY, next);
  g_signal_connect(entry, "activate", G_CALLBACK(sgtk_entry_activate_h), next);
}

static void
sgtk_entry_activate_h (GtkEntry *entry, gpointer user_data)
{
  GtkWidget *next = user_data;

  while (! GTK_WIDGET_IS_SENSITIVE(next))
    {
      next = g_object_get_data(G_OBJECT(next), NEXT_WIDGET_KEY);

      if (! next || next == (GtkWidget *) entry)
	return;
    }

  gtk_widget_grab_focus(next);
}

void
sgtk_widget_set_popup_menu (GtkWidget *widget, GtkMenu *menu)
{
  GtkMenu *old_menu;

  g_return_if_fail(GTK_IS_WIDGET(widget));

  old_menu = g_object_get_data(G_OBJECT(widget), MENU_KEY);
  if (old_menu)
    g_object_disconnect(widget,
			"any-signal", sgtk_widget_popup_menu_h, old_menu,
			"any-signal", sgtk_widget_button_press_event_h, old_menu,
			NULL);

  g_object_set_data(G_OBJECT(widget), MENU_KEY, menu);
  if (menu)
    g_object_connect(widget,
		     "signal::popup-menu", sgtk_widget_popup_menu_h, menu,
		     "signal::button-press-event", sgtk_widget_button_press_event_h, menu,
		     NULL);
}

static gboolean
sgtk_widget_popup_menu_h (GtkWidget *widget, gpointer user_data)
{
  GtkMenu *menu = user_data;

  gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
  
  return TRUE;			/* a menu was activated */
}

static gboolean
sgtk_widget_button_press_event_h (GtkWidget *widget,
				  GdkEventButton *event,
				  gpointer user_data)
{
  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
    {
      GtkMenu *menu = user_data;

      gtk_menu_popup(menu,
		     NULL,
		     NULL,
		     NULL,
		     NULL,
		     event->button,
		     event->time);
    }

  return FALSE;			/* propagate event */
}

void
sgtk_tree_model_make_countable (GtkTreeModel *model)
{
  g_return_if_fail(GTK_IS_TREE_MODEL(model));
  
  g_object_set_data(G_OBJECT(model), COUNTABLE_KEY, GINT_TO_POINTER(TRUE));

  g_object_connect(model,
		   "signal::row-inserted", sgtk_tree_model_row_inserted_h, NULL,
		   "signal::row-deleted", sgtk_tree_model_row_deleted_h, NULL,
		   NULL);
}

gboolean
sgtk_tree_model_is_countable (GtkTreeModel *model)
{
  g_return_val_if_fail(GTK_IS_TREE_MODEL(model), FALSE);

  return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), COUNTABLE_KEY));
}

static void
sgtk_tree_model_set_count (GtkTreeModel *model, unsigned int count)
{
  g_return_if_fail(GTK_IS_TREE_MODEL(model));

  g_object_set_data(G_OBJECT(model), COUNT_KEY, GUINT_TO_POINTER(count));
}

unsigned int
sgtk_tree_model_get_count (GtkTreeModel *model)
{
  g_return_val_if_fail(GTK_IS_TREE_MODEL(model), 0);
  g_return_val_if_fail(sgtk_tree_model_is_countable(model), 0);

  return GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(model), COUNT_KEY));
}

static void
sgtk_tree_model_row_inserted_h (GtkTreeModel *model,
				GtkTreePath *arg1,
				GtkTreeIter *arg2,
				gpointer user_data)
{
  sgtk_tree_model_set_count(model, sgtk_tree_model_get_count(model) + 1);
}

static void
sgtk_tree_model_row_deleted_h (GtkTreeModel *model,
			       GtkTreePath *arg1,
			       gpointer user_data)
{
  sgtk_tree_model_set_count(model, sgtk_tree_model_get_count(model) - 1);
}

gboolean
sgtk_tree_model_get_iter_last (GtkTreeModel *model, GtkTreeIter *iter)
{
  gboolean status;
  gboolean valid;
  GtkTreeIter tmp_iter;

  g_return_val_if_fail(GTK_IS_TREE_MODEL(model), FALSE);
  g_return_val_if_fail(iter != NULL, FALSE);

  status = valid = gtk_tree_model_get_iter_first(model, &tmp_iter);
  while (valid)
    {
      *iter = tmp_iter;
      valid = gtk_tree_model_iter_next(model, &tmp_iter);
    }

  return status;
}

gboolean
sgtk_tree_model_iter_prev (GtkTreeModel *model, GtkTreeIter *iter)
{
  GtkTreePath *path;
  gboolean status;

  g_return_val_if_fail(GTK_IS_TREE_MODEL(model), FALSE);
  g_return_val_if_fail(iter != NULL, FALSE);
  
  path = gtk_tree_model_get_path(model, iter);
  status = gtk_tree_path_prev(path);
  if (status)
    {
      status = gtk_tree_model_get_iter(model, iter, path);
      g_return_val_if_fail(status == TRUE, FALSE);
    }
  gtk_tree_path_free(path);

  return status;
}

gboolean
sgtk_tree_model_iter_move (GtkTreeModel *model,
			   GtkTreeIter *iter,
			   sGtkDirection direction)
{
  g_return_val_if_fail(GTK_IS_TREE_MODEL(model), FALSE);
  g_return_val_if_fail(iter != NULL, FALSE);
  
  switch (direction)
    {
    case SGTK_PREVIOUS:
      return sgtk_tree_model_iter_prev(model, iter);

    case SGTK_NEXT:
      return gtk_tree_model_iter_next(model, iter);

    default:
      g_return_val_if_reached(FALSE);
    }
}

void
sgtk_accel_map_add_entry (const char *accel_path, const char *accelerator)
{
  unsigned int key;
  GdkModifierType mods;

  g_return_if_fail(accel_path != NULL);
  g_return_if_fail(accelerator != NULL);

  gtk_accelerator_parse(accelerator, &key, &mods);
  gtk_accel_map_add_entry(accel_path, key, mods);
}

gboolean
sgtk_accel_map_change_entry (const char *accel_path,
			     const char *accelerator,
			     gboolean replace)
{
  unsigned int key;
  GdkModifierType mods;

  g_return_val_if_fail(accel_path != NULL, FALSE);
  g_return_val_if_fail(accelerator != NULL, FALSE);

  gtk_accelerator_parse(accelerator, &key, &mods);
  return gtk_accel_map_change_entry(accel_path, key, mods, replace);
}

void
sgtk_window_link_size (GtkWindow *window, int *width, int *height)
{
  LinkWindowInfo *info;

  g_return_if_fail(GTK_IS_WINDOW(window));

  gtk_window_set_default_size(GTK_WINDOW(window),
			      width ? *width : -1,
			      height ? *height : -1);

  info = g_new(LinkWindowInfo, 1);
  info->width = width;
  info->height = height;

  g_signal_connect_data(window,
			"configure-event",
			G_CALLBACK(sgtk_window_link_size_event_configure_h),
			info,
			(GClosureNotify) g_free,
			0);
}

static gboolean
sgtk_window_link_size_event_configure_h (GtkWidget *widget,
					 GdkEventConfigure *event,
					 gpointer user_data)
{
  LinkWindowInfo *info = user_data;

  if (info->width)
    *info->width = event->width;
  if (info->height)
    *info->height = event->height;

  return FALSE;			/* propagate event */
}
