Logo Search packages:      
Sourcecode: galeon version File versions

galeon-autocompletion-window.c

/*
 *  Copyright (C) 2002  Ricardo Fernández Pascual
 *
 *  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.
 */

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

#include <glib/gi18n.h>
#include "galeon-autocompletion-window.h"
#include "gul-string.h"
#include "galeon-marshal.h"
#include "eel-gconf-extensions.h"
#include "prefs-strings.h"
#include "galeon-debug.h"

#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkmain.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtkframe.h>

/* This is copied from gtkscrollbarwindow.c */
#define DEFAULT_SCROLLBAR_SPACING  3

#define SCROLLBAR_SPACING(w)                                                            \
  (GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing >= 0 ?                          \
   GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing : DEFAULT_SCROLLBAR_SPACING)


/**
 * these allow tunning for better interactive response 
 */
#define MAX_ALTERNATIVES 300
#define MAX_CHUNK_ALTERNATIVES 25
#define ADD_MATCHES_DELAY 400
#undef DISABLE_PROGRESSIVE_FILLING

/**
 * Private data
 */
#define GALEON_AUTOCOMPLETION_WINDOW_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
                               GALEON_TYPE_AUTOCOMPLETION_WINDOW, GaleonAutocompletionWindowPrivate))


struct _GaleonAutocompletionWindowPrivate {
      GaleonAutocompletion *autocompletion;
      GtkWidget *parent;
      
      GtkWidget *window;
      GtkScrolledWindow *scrolled_window;
      GtkTreeView *tree_view;
      GtkTreeViewColumn *col1;
      GtkTreeViewColumn *col2;
      
      GSList *selected;
      
      GtkListStore *list_store;
      guint last_added_match;
      guint add_matches_timeout;
      
      gboolean shown;
};

/**
 * Private functions, only availble from this file
 */
static void galeon_autocompletion_window_finalize_impl      (GObject *o);
static void galeon_autocompletion_window_init_widgets (GaleonAutocompletionWindow *aw);
static void galeon_autocompletion_window_selection_changed_cb (GtkTreeSelection *treeselection, 
                                                   GaleonAutocompletionWindow *aw);
static void galeon_autocompletion_window_selection_add_selected (GtkTreeModel *model, GtkTreePath *path, 
                                                     GtkTreeIter *iter, gpointer data);
static gboolean   galeon_autocompletion_window_button_press_event_cb (GtkWidget *widget,
                                                    GdkEventButton *event,
                                                    GaleonAutocompletionWindow *aw);
static gboolean   galeon_autocompletion_window_key_press_cb (GtkWidget *widget,
                                                 GdkEventKey *event,
                                                 GaleonAutocompletionWindow *aw);
static gboolean   galeon_autocompletion_window_key_press_hack     (GaleonAutocompletionWindow *aw, 
                                                 GdkEventKey *event);
static void       hack_tree_view_move_selection                   (GtkTreeView *tv, int dir);
static void galeon_autocompletion_window_event_after_cb     (GtkWidget *wid, GdkEvent *event,
                                                 GaleonAutocompletionWindow *aw);
static gboolean   galeon_autocompletion_window_fill_store_chunk   (GaleonAutocompletionWindow *aw);
static gboolean galeon_autocompletion_window_add_matches_to (GaleonAutocompletionWindow *aw);
static void galeon_autocompletion_window_autocompletion_sources_changed_cb (GaleonAutocompletion *ac, 
                                                            GaleonAutocompletionWindow *aw);




enum GaleonAutocompletionWindowSignalsEnum {
      GALEON_AUTOCOMPLETION_WINDOW_URL_ACTIVATED,
      GALEON_AUTOCOMPLETION_WINDOW_HIDDEN,
      GALEON_AUTOCOMPLETION_WINDOW_LAST_SIGNAL
};
static gint GaleonAutocompletionWindowSignals[GALEON_AUTOCOMPLETION_WINDOW_LAST_SIGNAL];

/**
 * AutocompletionWindow object
 */

G_DEFINE_TYPE (GaleonAutocompletionWindow, galeon_autocompletion_window, G_TYPE_OBJECT);

static void
galeon_autocompletion_window_class_init (GaleonAutocompletionWindowClass *klass)
{
      G_OBJECT_CLASS (klass)->finalize = galeon_autocompletion_window_finalize_impl;
      
      GaleonAutocompletionWindowSignals[GALEON_AUTOCOMPLETION_WINDOW_URL_ACTIVATED] = g_signal_new (
            "url-activated", G_OBJECT_CLASS_TYPE (klass),  
            G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
                G_STRUCT_OFFSET (GaleonAutocompletionWindowClass, url_activated), 
            NULL, NULL, 
            galeon_marshal_VOID__STRING_INT,
            G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);

      GaleonAutocompletionWindowSignals[GALEON_AUTOCOMPLETION_WINDOW_HIDDEN] = g_signal_new (
            "hidden", G_OBJECT_CLASS_TYPE (klass),  
            G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
                G_STRUCT_OFFSET (GaleonAutocompletionWindowClass, hidden), 
            NULL, NULL, 
            galeon_marshal_VOID__VOID,
            G_TYPE_NONE, 0);

      g_type_class_add_private (klass, sizeof (GaleonAutocompletionWindowPrivate));
}

static void 
galeon_autocompletion_window_init (GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = GALEON_AUTOCOMPLETION_WINDOW_GET_PRIVATE (aw);
      GtkTreeSelection *s;
      
      aw->priv = p;
      
      galeon_autocompletion_window_init_widgets (aw);
      
      s = gtk_tree_view_get_selection (p->tree_view);
      /* I would like to use GTK_SELECTION_SINGLE, but it seems to require that one 
         item is selected always */
      gtk_tree_selection_set_mode (s, GTK_SELECTION_MULTIPLE);
      
      g_signal_connect (s, "changed", G_CALLBACK (galeon_autocompletion_window_selection_changed_cb), aw);

      g_signal_connect (p->window, "button-press-event", 
                    G_CALLBACK (galeon_autocompletion_window_button_press_event_cb),
                    aw);
      g_signal_connect (p->window, "key-press-event",
                    G_CALLBACK (galeon_autocompletion_window_key_press_cb),
                    aw);
      g_signal_connect (p->tree_view, "event-after", 
                    G_CALLBACK (galeon_autocompletion_window_event_after_cb),
                    aw);
}

static void
galeon_autocompletion_window_finalize_impl (GObject *o)
{
      GaleonAutocompletionWindow *aw = GALEON_AUTOCOMPLETION_WINDOW (o);
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      
      if (p->add_matches_timeout)
      {
            g_source_remove (p->add_matches_timeout);
            p->add_matches_timeout = 0;
      }

      if (p->list_store) g_object_unref (p->list_store);
      if (p->parent) g_object_unref (p->parent);
      if (p->window) gtk_widget_destroy (p->window);

      if (p->autocompletion) 
      {
            g_signal_handlers_disconnect_matched (p->autocompletion, G_SIGNAL_MATCH_DATA, 0, 0, 
                                          NULL, NULL, aw);
            g_object_unref (p->autocompletion);
      }

      
      g_slist_foreach (p->selected, (GFunc) g_free, NULL);
      g_slist_free (p->selected);
      
      gdk_pointer_ungrab (GDK_CURRENT_TIME);
      gdk_keyboard_ungrab (GDK_CURRENT_TIME);
      
      G_OBJECT_CLASS (galeon_autocompletion_window_parent_class)->finalize (o);
}

static void
galeon_autocompletion_window_init_widgets (GaleonAutocompletionWindow *aw)
{
      gboolean show_titles = eel_gconf_get_boolean (CONF_COMPLETION_SHOW_TITLES);
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      GtkWidget *sw;
      GtkCellRenderer *renderer;
      GtkWidget *frame;
      
      p->window = gtk_window_new (GTK_WINDOW_POPUP);
      gtk_window_set_resizable (GTK_WINDOW (p->window), FALSE);
      
      frame = gtk_frame_new (NULL);
      gtk_frame_set_shadow_type (GTK_FRAME (frame), 
                                   GTK_SHADOW_OUT);
      gtk_container_add (GTK_CONTAINER (p->window), frame); 
      gtk_widget_show (frame);
      
      sw = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_shadow_type 
            (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER (frame), sw);
      p->scrolled_window = GTK_SCROLLED_WINDOW (sw);
      gtk_widget_show (sw);
      
      p->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
      gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (p->tree_view));
      
      renderer = gtk_cell_renderer_text_new ();
      p->col1 = gtk_tree_view_column_new ();
      gtk_tree_view_column_pack_start (p->col1, renderer, TRUE);
      gtk_tree_view_column_set_sizing (p->col1, GTK_TREE_VIEW_COLUMN_FIXED);
      gtk_tree_view_column_set_attributes (p->col1, renderer,
                                             "text", 0, 
                                   NULL);
      gtk_tree_view_append_column (p->tree_view, p->col1);
      
      if (show_titles)
      {
            GdkColor *color;
            GValue v = { 0 };
            g_value_init (&v, GDK_TYPE_COLOR);
            renderer = gtk_cell_renderer_text_new ();
            g_object_get_property (G_OBJECT (renderer), "foreground-gdk", &v);
            color = g_value_peek_pointer (&v);
            
            /* this is very hacky, but maybe it works with dark themes (maybe) */
            color->red ^= 0x8000;
            color->green ^= 0x8000;
            color->blue ^= 0x8000;
            
            g_object_set_property (G_OBJECT (renderer), "foreground-gdk", &v);
            
            p->col2 = gtk_tree_view_column_new ();
            gtk_tree_view_column_pack_start (p->col2, renderer, TRUE);
            gtk_tree_view_column_set_sizing (p->col2, GTK_TREE_VIEW_COLUMN_FIXED);
            gtk_tree_view_column_set_attributes (p->col2, renderer,
                                         "text", 1, 
                                         NULL);
            gtk_tree_view_append_column (p->tree_view, p->col2);
      }
      
      gtk_tree_view_set_headers_visible (p->tree_view, FALSE);
      gtk_widget_show (GTK_WIDGET(p->tree_view));
}

GaleonAutocompletionWindow *
galeon_autocompletion_window_new (GaleonAutocompletion *ac, GtkWidget *w)
{
      GaleonAutocompletionWindow *ret = g_object_new (GALEON_TYPE_AUTOCOMPLETION_WINDOW, NULL);
      galeon_autocompletion_window_set_parent_widget (ret, w);
      galeon_autocompletion_window_set_autocompletion (ret, ac);
      return ret;
}

void
galeon_autocompletion_window_set_parent_widget (GaleonAutocompletionWindow *aw, GtkWidget *w)
{
      if (aw->priv->parent) g_object_unref (aw->priv->parent);
      aw->priv->parent = g_object_ref (w);
}

void
galeon_autocompletion_window_set_autocompletion (GaleonAutocompletionWindow *aw, 
                                     GaleonAutocompletion *ac)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;

      if (p->autocompletion) 
      {
            if (p->add_matches_timeout)
            {
                  g_source_remove (p->add_matches_timeout);
                  p->add_matches_timeout = 0;
            }

            g_signal_handlers_disconnect_matched (p->autocompletion, G_SIGNAL_MATCH_DATA, 0, 0, 
                                          NULL, NULL, aw);

            g_object_unref (p->autocompletion);

      }
      p->autocompletion = g_object_ref (ac);
      g_signal_connect (p->autocompletion, "sources-changed", 
                    G_CALLBACK (galeon_autocompletion_window_autocompletion_sources_changed_cb), aw);
      
}

static void
galeon_autocompletion_window_selection_changed_cb (GtkTreeSelection *treeselection, 
                                       GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      
      g_slist_foreach (p->selected, (GFunc) g_free, NULL);
      g_slist_free (p->selected);
      aw->priv->selected = NULL;
      gtk_tree_selection_selected_foreach (treeselection, 
                                   galeon_autocompletion_window_selection_add_selected, aw);
      
}

static void
galeon_autocompletion_window_selection_add_selected (GtkTreeModel *model, GtkTreePath *path, 
                                         GtkTreeIter *iter, gpointer data)
{
      GaleonAutocompletionWindow *aw = data;
      gchar *item;
      
      if (aw->priv->list_store)
      {
            gtk_tree_model_get (GTK_TREE_MODEL (aw->priv->list_store), iter, 0, &item, -1);
            aw->priv->selected = g_slist_prepend (aw->priv->selected, item);
      }
}

static void
galeon_autocompletion_window_get_dimensions (GaleonAutocompletionWindow *aw,
                                   int *x, int *y, int *width, int *height)
{
      GtkBin *popwin;
      GtkWidget *widget;
      GtkScrolledWindow *popup;
      gint real_height;
      GtkRequisition list_requisition;
      gboolean show_hscroll = FALSE;
      gboolean show_vscroll = FALSE;
      gint avail_height;
      gint min_height;
      gint alloc_width;
      gint work_height;
      gint old_height;
      gint old_width;
      
      widget = GTK_WIDGET (aw->priv->parent);
      popup  = GTK_SCROLLED_WINDOW (aw->priv->scrolled_window);
      popwin = GTK_BIN (aw->priv->window);
      
      gdk_window_get_origin (widget->window, x, y);
      real_height = MIN (widget->requisition.height, 
                           widget->allocation.height);
      *y += real_height;
      avail_height = gdk_screen_height () - *y;
      
      gtk_widget_size_request (GTK_WIDGET(aw->priv->tree_view), 
                         &list_requisition);
      min_height = MIN (list_requisition.height, 
                          popup->vscrollbar->requisition.height);
      
      alloc_width = (widget->allocation.width -
                   2 * popwin->child->style->xthickness -
                   2 * GTK_CONTAINER (popwin->child)->border_width -
                   2 * GTK_CONTAINER (popup)->border_width -
                   2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width - 
                   2 * GTK_BIN (popup)->child->style->xthickness);
      
      work_height = (2 * popwin->child->style->ythickness +
                   2 * GTK_CONTAINER (popwin->child)->border_width +
                   2 * GTK_CONTAINER (popup)->border_width +
                   2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width +
                   2 * GTK_BIN (popup)->child->style->xthickness);
      
      do 
      {
                  old_width = alloc_width;
                  old_height = work_height;
            
                  if (!show_hscroll &&
                alloc_width < list_requisition.width)
            {
                  GtkRequisition req;
                  gtk_widget_size_request (popup->hscrollbar, &req);
                  work_height += req.height;
                        work_height += SCROLLBAR_SPACING (popup);
                  show_hscroll = TRUE;
            }
                  
            if (!show_vscroll && 
                work_height + list_requisition.height > avail_height)
            {
                  if (work_height + min_height > avail_height && 
                      *y - real_height > avail_height)
                  {
                        *y -= (work_height + list_requisition.height + real_height);
                        break;
                  }
                  alloc_width -= (popup->vscrollbar->requisition.width +
                              SCROLLBAR_SPACING (popup));
                  show_vscroll = TRUE;
            }
      } while (old_width != alloc_width || old_height != work_height);
      
      *width = widget->allocation.width;
      
      if (show_vscroll)
            *height = avail_height;
      else
            *height = work_height + list_requisition.height;
      
      if (*x < 0) *x = 0;
}


static gboolean 
galeon_autocompletion_window_add_matches_to (GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      gboolean more;

      more = galeon_autocompletion_window_fill_store_chunk (aw);

      if (more)
      {
            return TRUE;
      }
      else
      {
            p->add_matches_timeout = 0;
            return FALSE;
      }
}

static gboolean
galeon_autocompletion_window_fill_store_chunk (GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      const GaleonAutocompletionMatch *matches;
      guint i;
      guint nmatches;
      guint last = p->last_added_match;

      LOG ("ACW: Filling the list from %d", last);
      
      START_PROFILER ("filling liststore");

      nmatches = galeon_autocompletion_get_num_matches (p->autocompletion);
      matches = galeon_autocompletion_get_matches_sorted_by_score (p->autocompletion);

      for (i = 0; last < nmatches && i < MAX_CHUNK_ALTERNATIVES; i++, last++)
      {
            const GaleonAutocompletionMatch *m = &matches[last];
            GtkTreeIter iter;
            gtk_list_store_append (p->list_store, &iter);
            gtk_list_store_set (p->list_store, &iter, 0, m->match, 1, m->title, -1);
      }

      p->last_added_match = last;

      STOP_PROFILER ("filling liststore");

      if (i == MAX_CHUNK_ALTERNATIVES && p->last_added_match < MAX_ALTERNATIVES)
      {
            LOG ("ACW: will continue...");
            return TRUE;
      }
      else
      {
            return FALSE;
      }
}

void
galeon_autocompletion_window_show (GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      gint x, y, height, width;
      gboolean show_titles = eel_gconf_get_boolean (CONF_COMPLETION_SHOW_TITLES);
      guint nmatches;

      g_return_if_fail (p->window);
      g_return_if_fail (p->autocompletion);
      
      nmatches = galeon_autocompletion_get_num_matches (p->autocompletion);
      if (nmatches <= 0)
      {
            galeon_autocompletion_window_hide (aw);
            return;
      }

      LOG ("ACW: showing window.");

      START_PROFILER ("showing window");
      
      START_PROFILER ("creating liststore");

      if (p->list_store) g_object_unref (p->list_store);
      p->list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
      
      if (p->add_matches_timeout)
      {
            g_source_remove (p->add_matches_timeout);
            p->add_matches_timeout = 0;
      }
      p->last_added_match = 0;
      if (galeon_autocompletion_window_fill_store_chunk (aw)
#ifdef DISABLE_PROGRESSIVE_FILLING         
          && FALSE /* using this instead of ifdefing the if body to avoid a warning */
#endif
            )
      {
            p->add_matches_timeout = g_timeout_add
                  (ADD_MATCHES_DELAY,
                   (GSourceFunc) galeon_autocompletion_window_add_matches_to, aw);
      }

      STOP_PROFILER ("creating liststore");
      
      gtk_tree_view_set_model (p->tree_view, GTK_TREE_MODEL (p->list_store));
      
      START_PROFILER ("calculating dimensions");
      
      galeon_autocompletion_window_get_dimensions (aw, &x, &y, &width, &height);
      
      STOP_PROFILER ("calculating dimensions");
      
      if (show_titles)
      {
            gtk_tree_view_column_set_fixed_width (p->col1, width * 2 / 3);
      }

      if (height > 350) height = 350;
      
      gtk_widget_set_size_request (GTK_WIDGET (p->window), width, 
                             height);
      gtk_window_move (GTK_WINDOW (p->window), x, y);

      if (!p->shown)
      {
            gtk_widget_show (p->window);
            
            gdk_pointer_grab (p->parent->window, TRUE,
                          GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
                          GDK_BUTTON_RELEASE_MASK,
                          NULL, NULL, GDK_CURRENT_TIME);
            gdk_keyboard_grab (p->parent->window, TRUE, GDK_CURRENT_TIME);
            gtk_grab_add (p->window);
            p->shown = TRUE;
      }

      gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (p->tree_view), 0, 0);

      gtk_widget_grab_focus (GTK_WIDGET (p->tree_view));

      STOP_PROFILER ("showing window");
}

static gboolean
galeon_autocompletion_window_button_press_event_cb (GtkWidget *widget,
                                        GdkEventButton *event,
                                        GaleonAutocompletionWindow *aw)
{
      GtkWidget *event_widget;
      
      event_widget = gtk_get_event_widget ((GdkEvent *) event);
      
      /* Check to see if button press happened inside the alternatives
         window.  If not, destroy the window. */
      if (event_widget != aw->priv->window)
      {
            while (event_widget)
            {
                  if (event_widget == aw->priv->window)
                        return FALSE;
                  event_widget = event_widget->parent;
            }
      }
      galeon_autocompletion_window_hide (aw);

      return TRUE;
}

static gboolean
galeon_autocompletion_window_key_press_cb (GtkWidget *widget,
                                 GdkEventKey *event,
                                 GaleonAutocompletionWindow *aw)
{
      GdkEventKey tmp_event;
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      GtkWidget *dest_widget;
      
      /* allow keyboard navigation in the alternatives clist */
      if (event->keyval == GDK_Up || event->keyval == GDK_Down 
          || event->keyval == GDK_Page_Up ||  event->keyval == GDK_Page_Down
          || ((event->keyval == GDK_space || event->keyval == GDK_Return 
             || event->keyval == GDK_KP_Enter)
            && p->selected))
      {
            return galeon_autocompletion_window_key_press_hack (aw, event);
      }
      else
      {
            dest_widget = p->parent;
      }
      
      if (dest_widget != widget)
      {
            LOG ("Resending event");
            
            tmp_event = *event;
            gtk_widget_event (dest_widget, (GdkEvent *)&tmp_event);
            
            return TRUE;
      }
      else
      {
            if (widget == GTK_WIDGET (p->tree_view))
            {
                  LOG ("on the tree view");
            }
            LOG ("Ignoring event");
            return FALSE;
      }
      
}

static gboolean
galeon_autocompletion_window_key_press_hack (GaleonAutocompletionWindow *aw, 
                                   GdkEventKey *event)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      switch (event->keyval)
      {
      case GDK_Up:
            hack_tree_view_move_selection (p->tree_view, -1);
            break;
      case GDK_Down:
            hack_tree_view_move_selection (p->tree_view, +1);
            break;
      case GDK_Page_Down:
            hack_tree_view_move_selection (p->tree_view, +5);
            break;
      case GDK_Page_Up:
            hack_tree_view_move_selection (p->tree_view, -5);
            break;
      case GDK_Return:
      case GDK_KP_Enter:
      case GDK_space:
            if (p->selected)
            {
                  gboolean new_tab_or_window = event->state & GDK_CONTROL_MASK;

                  g_signal_emit (aw, GaleonAutocompletionWindowSignals
                               [GALEON_AUTOCOMPLETION_WINDOW_URL_ACTIVATED], 0, 
                               p->selected->data, new_tab_or_window);
            }
            break;
      default:
            g_warning ("Unexpected keyval");
            break;
      }
      return TRUE;
}

void
galeon_autocompletion_window_hide (GaleonAutocompletionWindow *aw)
{
      if (aw->priv->window)
      {
            gtk_widget_hide (aw->priv->window);
            gtk_grab_remove (aw->priv->window);
            gdk_pointer_ungrab (GDK_CURRENT_TIME);
            gdk_keyboard_ungrab (GDK_CURRENT_TIME);
            galeon_autocompletion_window_unselect (aw);
            g_signal_emit (aw, GaleonAutocompletionWindowSignals[GALEON_AUTOCOMPLETION_WINDOW_HIDDEN], 0);
      }
      aw->priv->shown = FALSE;
}

static void 
hack_tree_view_move_selection_aux (GtkTreeModel      *model,
                           GtkTreePath       *path,
                           GtkTreeIter       *iter,
                           gpointer           data)
{
      GSList **l = data;
      *l = g_slist_prepend (*l, gtk_tree_path_copy (path));
}

static void
hack_tree_view_move_selection (GtkTreeView *tv, int dir)
{
      GtkTreeSelection *ts = gtk_tree_view_get_selection (tv);
      GSList *selected = NULL;
      gtk_tree_selection_selected_foreach (ts, hack_tree_view_move_selection_aux, &selected);
      
      gtk_tree_selection_unselect_all (ts);
      
      if (!selected || selected->next)
      {
            /* none (or more than one, should not happen) items selected 
               select the first one */
            GtkTreePath *p = gtk_tree_path_new_first ();
            gtk_tree_selection_select_path (ts, p);
            gtk_tree_view_scroll_to_cell (tv, p, NULL, FALSE, 0, 0);
            gtk_tree_path_free (p);
      }
      else
      {
            GtkTreePath *p = selected->data;
            int i;
            if (dir > 0)
            {
                  for (i = 0; i < dir; ++i)
                  {
                        gtk_tree_path_next (p);
                  }
            }
            else
            {
                  for (i = 0; i > dir; --i)
                  {
                        gtk_tree_path_prev (p);
                  }
            }
            gtk_tree_selection_select_path (ts, p);
            gtk_tree_view_scroll_to_cell (tv, p, NULL, FALSE, 0, 0);
      }
      
      g_slist_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
      g_slist_free (selected);
}


void
galeon_autocompletion_window_unselect (GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      GtkTreeSelection *ts = gtk_tree_view_get_selection (p->tree_view);
      gtk_tree_selection_unselect_all (ts);
}

static void
galeon_autocompletion_window_event_after_cb (GtkWidget *wid, GdkEvent *event,
                                   GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;
      if (event->type == GDK_BUTTON_PRESS
          && ((GdkEventButton *) event)->button == 1)
      {
            if (p->selected)
            {
                  g_signal_emit (aw, GaleonAutocompletionWindowSignals
                               [GALEON_AUTOCOMPLETION_WINDOW_URL_ACTIVATED], 0, 
                               p->selected->data, FALSE);
            }
      }
}

static void
galeon_autocompletion_window_autocompletion_sources_changed_cb (GaleonAutocompletion *ac, 
                                                GaleonAutocompletionWindow *aw)
{
      GaleonAutocompletionWindowPrivate *p = aw->priv;

      if (p->add_matches_timeout)
      {
            g_source_remove (p->add_matches_timeout);
            p->add_matches_timeout = 0;
      }
}

Generated by  Doxygen 1.6.0   Back to index