Logo Search packages:      
Sourcecode: kazehakase version File versions

kz-history-action.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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.
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "kz-history-action.h"
#include "kazehakase.h"
#include "migemo.h"

#include <string.h>
#include <glib/gi18n.h>

#include "gobject-utils.h"


enum {
      HISTORY_CHANGED_SIGNAL,
      LAST_SIGNAL
};

enum {
      COLUMN_TERMINATOR = -1,
      COLUMN_FAVICON,
      COLUMN_URI,
      COLUMN_TIRLE,
      N_COLUMN
};


static void       kz_history_action_class_init       (KzHistoryActionClass *klass);
static void       kz_history_action_init             (KzHistoryAction *action);
static void       kz_history_action_dispose          (GObject *object);

static void       kz_history_action_activate         (GtkAction *action);
static GtkWidget *kz_history_action_create_menu_item (GtkAction *action);
static GtkWidget *kz_history_action_create_tool_item (GtkAction *action);

static GtkComboBox  *kz_history_action_real_get_combo_widget
                                         (KzHistoryAction *action,
                                          GtkWidget        *proxy);
static void       kz_history_action_real_set_history (KzHistoryAction *action,
                                          GList            *list);

static void       kz_history_action_connect_proxy    (GtkAction        *action,
                                          GtkWidget        *proxy);

static KzEntryActionClass *parent_class = NULL;

static gint history_action_signals[LAST_SIGNAL] = { 0 };

KZ_OBJECT_GET_TYPE(kz_history_action, "KzHistoryAction", KzHistoryAction,
               kz_history_action_class_init, kz_history_action_init,
               KZ_TYPE_ENTRY_ACTION)

static void
kz_history_action_class_init (KzHistoryActionClass *klass)
{
      GObjectClass *object_class;
      GtkActionClass *action_class;
      KzEntryActionClass *entry_action_class;

      parent_class = g_type_class_peek_parent(klass);
      object_class = G_OBJECT_CLASS(klass);
      action_class = GTK_ACTION_CLASS(klass);
      entry_action_class = KZ_ENTRY_ACTION_CLASS(klass);

      object_class->dispose      = kz_history_action_dispose;
      /*
      object_class->set_property = kz_history_action_set_property;
      object_class->get_property = kz_history_action_get_property;
      */

      action_class->activate         = kz_history_action_activate;
      action_class->create_menu_item = kz_history_action_create_menu_item;
      action_class->create_tool_item = kz_history_action_create_tool_item;
      action_class->connect_proxy    = kz_history_action_connect_proxy;

      action_class->toolbar_item_type = GTK_TYPE_TOOL_ITEM;

      klass->get_combo_widget = kz_history_action_real_get_combo_widget;
      klass->set_history      = kz_history_action_real_set_history;
      klass->history_changed  = NULL;

      history_action_signals[HISTORY_CHANGED_SIGNAL] =
            g_signal_new("history-changed",
                       G_OBJECT_CLASS_TYPE(klass),
                       G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
                       G_STRUCT_OFFSET(KzHistoryActionClass, history_changed),
                       NULL,
                       NULL,
                       g_cclosure_marshal_VOID__VOID,
                       G_TYPE_NONE, 0);
}


static void
kz_history_action_init (KzHistoryAction *action)
{
      action->max_history = 32;
      action->list_store = gtk_list_store_new(N_COLUMN,
                                    GDK_TYPE_PIXBUF,
                                    G_TYPE_STRING,
                                    G_TYPE_STRING);
      action->completion = NULL;
      action->completion_egg_regex = NULL;
      action->completion_previous_key = NULL;
}


static void
kz_history_action_dispose (GObject *object)
{
      KzHistoryAction *action = KZ_HISTORY_ACTION(object);

      if (action->list_store)
      {
            g_object_unref(action->list_store);
            action->list_store = NULL;
      }

      if (action->completion)
      {
            g_object_unref(action->completion);
            action->completion = NULL;
      }

      if (action->completion_egg_regex)
      {
            egg_regex_free(action->completion_egg_regex);
            action->completion_egg_regex = NULL;
      }

      if(action->completion_previous_key)
      {
            g_free(action->completion_previous_key);
            action->completion_previous_key = NULL;
      }

      if (G_OBJECT_CLASS (parent_class)->dispose)
            G_OBJECT_CLASS (parent_class)->dispose(object);
}


typedef struct _FindRow
{
      const gchar *uri;
      GtkTreePath *path;
} FindRow;


static gboolean
find_row_func (GtkTreeModel *model,
             GtkTreePath *path, GtkTreeIter *iter,
             gpointer data)
{
      FindRow *findrow = data;
      gchar *uri; 

      gtk_tree_model_get(model, iter,
                     COLUMN_URI, &uri,
                     COLUMN_TERMINATOR);

      if (!strcmp(findrow->uri, uri))
      {
            findrow->path = gtk_tree_path_copy(path);
            g_free(uri);
            return TRUE;
      }

      g_free(uri);
      return FALSE;
}


static GtkTreePath *
find_row (GtkTreeModel *model, const gchar *uri)
{
      FindRow findrow;

      g_return_val_if_fail(GTK_IS_TREE_MODEL(model), NULL);

      findrow.uri = uri;
      findrow.path = NULL;
      gtk_tree_model_foreach(model, find_row_func, &findrow);

      return findrow.path;
}


static void
kz_history_action_activate (GtkAction *action)
{
      KzHistoryAction *hist_act = KZ_HISTORY_ACTION(action);
      const gchar *text;

      text = kz_entry_action_get_text(KZ_ENTRY_ACTION(action));

      if (text && *text)
      {
            GtkTreeIter iter;
            GtkTreePath *path;

            /* append, or reorder history */
            path = find_row(GTK_TREE_MODEL(hist_act->list_store), text);
            if (path)
            {     
                  gtk_tree_model_get_iter(GTK_TREE_MODEL(hist_act->list_store),
                                    &iter, path);
                  gtk_list_store_move_after(hist_act->list_store,
                                      &iter, NULL);
                  gtk_tree_path_free(path);
            }
            else
            {
                  gint n_row;
                  gtk_list_store_prepend(hist_act->list_store,
                                     &iter);
                  gtk_list_store_set (hist_act->list_store, &iter,
                                  COLUMN_URI, text,
                                  COLUMN_TERMINATOR);
                  n_row = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(hist_act->list_store),
                                                NULL);
                  while (n_row > hist_act->max_history)
                  {
                        gboolean ret;
                        ret = gtk_tree_model_iter_nth_child(
                                    GTK_TREE_MODEL(hist_act->list_store),
                                    &iter, NULL, n_row);
                        if (ret)
                        {
                              gtk_list_store_remove(hist_act->list_store,
                                                &iter);
                        }
                        n_row--;
                  }
            }
      }

      g_signal_emit(action,
                  history_action_signals[HISTORY_CHANGED_SIGNAL], 0);

      /* not needed */
      if (GTK_ACTION_CLASS (parent_class)->activate)
            GTK_ACTION_CLASS (parent_class)->activate(action);
}


#if 0
static gboolean
cb_menu_item_combo_button_press (GtkWidget *widget)
{
      /* don't propagate to parent */
      return TRUE;
}


static gboolean
cb_menu_item_combo_button_release (GtkWidget *widget)
{
      /* don't propagate to parent */
      return TRUE;
}


static gboolean
cb_menu_item_combo_enter (GtkWidget *widget, GdkEventCrossing *event)
{
      gtk_grab_add(GTK_BIN(widget)->child);
      gtk_widget_grab_focus(GTK_BIN(widget)->child);

      return TRUE;
}


static gboolean
cb_menu_item_combo_leave (GtkWidget *widget, GdkEventCrossing *event)
{
      gtk_grab_remove(GTK_BIN(widget)->child);

      return TRUE;
}
#endif

static gboolean
cb_match_selected (GtkEntryCompletion *completion,
               GtkTreeModel *model,
               GtkTreeIter *iter,
               GtkEntry *entry)
{
      char *item = NULL;

      gtk_tree_model_get (model, iter, 
                      COLUMN_URI, &item,
                      COLUMN_TERMINATOR);

      gtk_entry_set_text(entry, item);

      g_signal_emit_by_name (entry, "activate");

      g_free (item);

      return TRUE;
}

static gboolean
completion_func (GtkEntryCompletion *completion,
             const char *key,
             GtkTreeIter *iter,
             gpointer data)
{
      char *item = NULL;
      gboolean ret = FALSE;
      GtkTreeModel *model;
      GError *e = NULL;
      KzHistoryAction *action = KZ_HISTORY_ACTION(data);

      model = gtk_entry_completion_get_model (completion);

      gtk_tree_model_get (model, iter,
                      COLUMN_URI, &item,
                      COLUMN_TERMINATOR);

      if (!item) return ret;

      if (action->completion_previous_key && 
          strcmp(action->completion_previous_key, key))
      {
            egg_regex_free(action->completion_egg_regex);
            action->completion_egg_regex = NULL;

            g_free(action->completion_previous_key);
            action->completion_previous_key = NULL;
      }

      if(!action->completion_egg_regex)
      {
#if USE_MIGEMO
            gboolean use_migemo;

            KZ_CONF_GET("Global", "use_migemo", use_migemo, BOOL);
            if (use_migemo)
            {
                  gchar* regex = migemo_get_regex(key);
                  if (regex)
                  {
                        action->completion_egg_regex = egg_regex_new(regex,
                                                     EGG_REGEX_MULTILINE |
                                                     EGG_REGEX_EXTENDED,
                                                     0, &e);
                        if (e)
                        {
                              g_warning("%s", e->message);
                              g_error_free(e);
                              e = NULL;
                        }
                        g_free(regex);
                  }
            }
            else
#endif /* USE_MIGEMO */
            {
                  action->completion_egg_regex = egg_regex_new(key,
                                               EGG_REGEX_MULTILINE |
                                               EGG_REGEX_EXTENDED,
                                               0, &e);

                  if (e)
                  {
                        g_warning("%s", e->message);
                        g_error_free(e);
                        e = NULL;
                  }
            }

                /* reference */
            if(action->completion_egg_regex)
            {
                  action->completion_previous_key = g_strdup(key);
            }
            else
            {
                  goto END;
            }
      }

      if (egg_regex_match(action->completion_egg_regex, item, -1, 0) > 0)
      {
            ret = TRUE;
      }
 END:
      g_free (item);
      return ret;
}

static GtkWidget *
kz_history_action_create_menu_item (GtkAction *action)
{
      GtkWidget *widget, *combo;

#warning FIXME! implement as our original widget and force blink cursor.
      widget = gtk_menu_item_new();
      combo = gtk_combo_box_entry_new_with_model(
                        GTK_TREE_MODEL(KZ_HISTORY_ACTION(action)->list_store),
                        COLUMN_URI);
      gtk_widget_show(combo);
      gtk_container_add(GTK_CONTAINER(widget), combo);
      gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
#if 0
      g_signal_connect_after(widget, "button-press-event",
                         G_CALLBACK(cb_menu_item_combo_button_press),
                         NULL);
      g_signal_connect_after(widget, "button-release-event",
                         G_CALLBACK(cb_menu_item_combo_button_release),
                         NULL);

      g_signal_connect(widget, "enter-notify-event",
                   G_CALLBACK(cb_menu_item_combo_enter), NULL);
      g_signal_connect(widget, "leave-notify-event",
                   G_CALLBACK(cb_menu_item_combo_leave), NULL);
#endif
      return widget;
}


static GtkWidget *
kz_history_action_create_tool_item (GtkAction *action)
{
      GType toolbar_item_type;
      GtkWidget *widget, *combo;

      toolbar_item_type = GTK_ACTION_GET_CLASS(action)->toolbar_item_type;
      widget = GTK_WIDGET(g_object_new(toolbar_item_type, NULL));
      gtk_tool_item_set_expand (GTK_TOOL_ITEM(widget), TRUE);

      combo = gtk_combo_box_entry_new_with_model(
                        GTK_TREE_MODEL(KZ_HISTORY_ACTION(action)->list_store),
                        COLUMN_URI);
      gtk_container_add(GTK_CONTAINER(widget), combo);
      gtk_container_set_border_width(GTK_CONTAINER(widget), 4);
      gtk_widget_show(combo);

      return widget;
}


static void
kz_history_action_connect_proxy (GtkAction *action, GtkWidget *proxy)
{
      GtkComboBox *combo;

      g_return_if_fail(proxy);


      combo = kz_history_action_get_combo_widget(KZ_HISTORY_ACTION(action),
                                       proxy);
      if (GTK_IS_COMBO_BOX(combo))
      {
            gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), "");
      }
      GTK_ACTION_CLASS(parent_class)->connect_proxy(action, proxy);
}


static GtkComboBox *
kz_history_action_real_get_combo_widget (KzHistoryAction *action, GtkWidget *proxy)
{
      GtkComboBox *combo = NULL;

      g_return_val_if_fail(proxy, NULL);

      if (GTK_IS_COMBO_BOX(combo))
            return combo;

      return NULL;
}


GtkComboBox *
kz_history_action_get_combo_widget (KzHistoryAction *action, GtkWidget *proxy)
{
      KzHistoryActionClass *klass;

      g_return_val_if_fail(KZ_IS_ENTRY_ACTION(action), NULL);

      klass = KZ_HISTORY_ACTION_GET_CLASS(action);

      if (klass->get_combo_widget)
            return klass->get_combo_widget(action, proxy);

      return NULL;
}


static void
kz_history_action_real_set_history (KzHistoryAction *action, GList *list)
{
      GList *node;
      gint num = 0;

      g_return_if_fail(KZ_IS_ENTRY_ACTION(action));

      /* remove old list store for combobox */
      gtk_list_store_clear(action->list_store);
      
      /* dup the list */
      for (node = list, num = 0;
           node && num < action->max_history;
           node = g_list_next(node), num++)
      {
            GtkTreeIter iter;
            const gchar *text = node->data;

            if (text && *text)
            {
                  gtk_list_store_append (action->list_store, &iter);
                  gtk_list_store_set (action->list_store, &iter,
                                  COLUMN_URI, text,
                                  COLUMN_TERMINATOR);
            }
      }

      g_signal_emit(action,
                  history_action_signals[HISTORY_CHANGED_SIGNAL], 0);
}


void
kz_history_action_set_history (KzHistoryAction *action, GList *list)
{
      KzHistoryActionClass *klass;

      g_return_if_fail(KZ_IS_ENTRY_ACTION(action));

      klass = KZ_HISTORY_ACTION_GET_CLASS(action);

      if (klass->set_history)
            return klass->set_history(action, list);
}


GList *
kz_history_action_get_history (KzHistoryAction *action)
{
      GList *history = NULL;
      gchar *uri;
      GtkTreeIter iter;
      GtkTreeModel *model;

      g_return_val_if_fail(KZ_IS_ENTRY_ACTION(action), NULL);

      model = GTK_TREE_MODEL(action->list_store);

      if (!gtk_tree_model_get_iter_first(model, &iter))
            return NULL;

      while (1)
      {
            gtk_tree_model_get(model, &iter,
                           COLUMN_URI, &uri,
                           COLUMN_TERMINATOR);
      
            history = g_list_append (history, uri);
            if (!gtk_tree_model_iter_next(model, &iter))
                  break;
      }

      return history;
}


void
kz_history_action_set_max_history (KzHistoryAction *action, guint max_history)
{
      g_return_if_fail(KZ_IS_ENTRY_ACTION(action));

      action->max_history = max_history;
}


guint
kz_history_action_get_max_history (KzHistoryAction *action)
{
      g_return_val_if_fail(KZ_IS_ENTRY_ACTION(action), 0);

      return action->max_history;
}

void
kz_history_set_completion (KzHistoryAction *action, 
                     GtkEntry *entry)
{
      GtkCellRenderer *cell;
      GtkTreeModel *model;

      action->completion = gtk_entry_completion_new ();
      model = GTK_TREE_MODEL(action->list_store);

      gtk_entry_completion_set_model (action->completion, model);
      gtk_entry_completion_set_match_func (action->completion, completion_func, action, NULL);
      g_signal_connect (action->completion, "match_selected",
                    G_CALLBACK (cb_match_selected), entry);

      cell = gtk_cell_renderer_text_new ();
      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (action->completion),
                            cell, TRUE);
      gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (action->completion),
                               cell, "text", COLUMN_URI);

      gtk_entry_set_completion (entry, action->completion);
}

void
kz_history_unset_completion (KzHistoryAction *action, 
                       GtkEntry *entry)
{
      action->completion = gtk_entry_completion_new ();

      gtk_entry_completion_set_model (action->completion, NULL);
      g_signal_handlers_disconnect_by_func
                  (action->completion,
                   G_CALLBACK(cb_match_selected),
                   entry);

      g_object_unref(action->completion);
      action->completion = NULL;

      gtk_entry_set_completion (entry, NULL);
}


Generated by  Doxygen 1.6.0   Back to index