Blame telepathy-account-widgets/tp-account-widgets/tpaw-live-search.c

Packit 79f644
/*
Packit 79f644
 * Copyright (C) 2010 Collabora Ltd.
Packit 79f644
 * Copyright (C) 2007-2010 Nokia Corporation.
Packit 79f644
 *
Packit 79f644
 * This library is free software; you can redistribute it and/or
Packit 79f644
 * modify it under the terms of the GNU Lesser General Public
Packit 79f644
 * License as published by the Free Software Foundation; either
Packit 79f644
 * version 2.1 of the License, or (at your option) any later version.
Packit 79f644
 *
Packit 79f644
 * This library is distributed in the hope that it will be useful,
Packit 79f644
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 79f644
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 79f644
 * Lesser General Public License for more details.
Packit 79f644
 *
Packit 79f644
 * You should have received a copy of the GNU Lesser General Public
Packit 79f644
 * License along with this library; if not, write to the Free Software
Packit 79f644
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit 79f644
 *
Packit 79f644
 * Authors: Felix Kaser <felix.kaser@collabora.co.uk>
Packit 79f644
 *          Xavier Claessens <xavier.claessens@collabora.co.uk>
Packit 79f644
 *          Claudio Saavedra <csaavedra@igalia.com>
Packit 79f644
 */
Packit 79f644
Packit 79f644
#include "config.h"
Packit 79f644
#include "tpaw-live-search.h"
Packit 79f644
Packit 79f644
#include "tpaw-utils.h"
Packit 79f644
Packit 79f644
G_DEFINE_TYPE (TpawLiveSearch, tpaw_live_search, GTK_TYPE_BOX)
Packit 79f644
Packit 79f644
struct _TpawLiveSearchPriv
Packit 79f644
{
Packit 79f644
  GtkWidget *search_entry;
Packit 79f644
  GtkWidget *hook_widget;
Packit 79f644
Packit 79f644
  GPtrArray *stripped_words;
Packit 79f644
};
Packit 79f644
Packit 79f644
enum
Packit 79f644
{
Packit 79f644
  PROP_0,
Packit 79f644
  PROP_HOOK_WIDGET,
Packit 79f644
  PROP_TEXT
Packit 79f644
};
Packit 79f644
Packit 79f644
enum
Packit 79f644
{
Packit 79f644
  ACTIVATE,
Packit 79f644
  KEYNAV,
Packit 79f644
  LAST_SIGNAL
Packit 79f644
};
Packit 79f644
Packit 79f644
static guint signals[LAST_SIGNAL];
Packit 79f644
Packit 79f644
static void live_search_hook_widget_destroy_cb (GtkWidget *object,
Packit 79f644
    gpointer user_data);
Packit 79f644
Packit 79f644
/**
Packit 79f644
 * stripped_char:
Packit 79f644
 *
Packit 79f644
 * Returns a stripped version of @ch, removing any case, accentuation
Packit 79f644
 * mark, or any special mark on it.
Packit 79f644
 **/
Packit 79f644
static gunichar
Packit 79f644
stripped_char (gunichar ch)
Packit 79f644
{
Packit 79f644
  gunichar retval = 0;
Packit 79f644
  GUnicodeType utype;
Packit 79f644
Packit 79f644
  utype = g_unichar_type (ch);
Packit 79f644
Packit 79f644
  switch (utype)
Packit 79f644
    {
Packit 79f644
    case G_UNICODE_CONTROL:
Packit 79f644
    case G_UNICODE_FORMAT:
Packit 79f644
    case G_UNICODE_UNASSIGNED:
Packit 79f644
    case G_UNICODE_NON_SPACING_MARK:
Packit 79f644
    case G_UNICODE_COMBINING_MARK:
Packit 79f644
    case G_UNICODE_ENCLOSING_MARK:
Packit 79f644
      /* Ignore those */
Packit 79f644
      break;
Packit 79f644
    case G_UNICODE_PRIVATE_USE:
Packit 79f644
    case G_UNICODE_SURROGATE:
Packit 79f644
    case G_UNICODE_LOWERCASE_LETTER:
Packit 79f644
    case G_UNICODE_MODIFIER_LETTER:
Packit 79f644
    case G_UNICODE_OTHER_LETTER:
Packit 79f644
    case G_UNICODE_TITLECASE_LETTER:
Packit 79f644
    case G_UNICODE_UPPERCASE_LETTER:
Packit 79f644
    case G_UNICODE_DECIMAL_NUMBER:
Packit 79f644
    case G_UNICODE_LETTER_NUMBER:
Packit 79f644
    case G_UNICODE_OTHER_NUMBER:
Packit 79f644
    case G_UNICODE_CONNECT_PUNCTUATION:
Packit 79f644
    case G_UNICODE_DASH_PUNCTUATION:
Packit 79f644
    case G_UNICODE_CLOSE_PUNCTUATION:
Packit 79f644
    case G_UNICODE_FINAL_PUNCTUATION:
Packit 79f644
    case G_UNICODE_INITIAL_PUNCTUATION:
Packit 79f644
    case G_UNICODE_OTHER_PUNCTUATION:
Packit 79f644
    case G_UNICODE_OPEN_PUNCTUATION:
Packit 79f644
    case G_UNICODE_CURRENCY_SYMBOL:
Packit 79f644
    case G_UNICODE_MODIFIER_SYMBOL:
Packit 79f644
    case G_UNICODE_MATH_SYMBOL:
Packit 79f644
    case G_UNICODE_OTHER_SYMBOL:
Packit 79f644
    case G_UNICODE_LINE_SEPARATOR:
Packit 79f644
    case G_UNICODE_PARAGRAPH_SEPARATOR:
Packit 79f644
    case G_UNICODE_SPACE_SEPARATOR:
Packit 79f644
    default:
Packit 79f644
      ch = g_unichar_tolower (ch);
Packit 79f644
      g_unichar_fully_decompose (ch, FALSE, &retval, 1);
Packit 79f644
    }
Packit 79f644
Packit 79f644
  return retval;
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
append_word (GPtrArray **word_array,
Packit 79f644
    GString **word)
Packit 79f644
{
Packit 79f644
  if (*word != NULL)
Packit 79f644
    {
Packit 79f644
      if (*word_array == NULL)
Packit 79f644
        *word_array = g_ptr_array_new_with_free_func (g_free);
Packit 79f644
      g_ptr_array_add (*word_array, g_string_free (*word, FALSE));
Packit 79f644
      *word = NULL;
Packit 79f644
    }
Packit 79f644
}
Packit 79f644
Packit 79f644
GPtrArray *
Packit 79f644
tpaw_live_search_strip_utf8_string (const gchar *string)
Packit 79f644
{
Packit 79f644
  GPtrArray *word_array = NULL;
Packit 79f644
  GString *word = NULL;
Packit 79f644
  const gchar *p;
Packit 79f644
Packit 79f644
  if (TPAW_STR_EMPTY (string))
Packit 79f644
    return NULL;
Packit 79f644
Packit 79f644
  for (p = string; *p != '\0'; p = g_utf8_next_char (p))
Packit 79f644
    {
Packit 79f644
      gunichar sc;
Packit 79f644
Packit 79f644
      /* Make the char lower-case, remove its accentuation marks, and ignore it
Packit 79f644
       * if it is just unicode marks */
Packit 79f644
      sc = stripped_char (g_utf8_get_char (p));
Packit 79f644
      if (sc == 0)
Packit 79f644
        continue;
Packit 79f644
Packit 79f644
      /* If it is not alpha-num, it is separator between words */
Packit 79f644
      if (!g_unichar_isalnum (sc))
Packit 79f644
        {
Packit 79f644
          append_word (&word_array, &word);
Packit 79f644
          continue;
Packit 79f644
        }
Packit 79f644
Packit 79f644
      /* It is alpha-num, append this char to current word, or start new word */
Packit 79f644
      if (word == NULL)
Packit 79f644
        word = g_string_new (NULL);
Packit 79f644
      g_string_append_unichar (word, sc);
Packit 79f644
    }
Packit 79f644
Packit 79f644
  append_word (&word_array, &word);
Packit 79f644
Packit 79f644
  return word_array;
Packit 79f644
}
Packit 79f644
Packit 79f644
static gboolean
Packit 79f644
live_search_match_prefix (const gchar *string,
Packit 79f644
    const gchar *prefix)
Packit 79f644
{
Packit 79f644
  const gchar *p;
Packit 79f644
  const gchar *prefix_p;
Packit 79f644
  gboolean next_word = FALSE;
Packit 79f644
Packit 79f644
  if (prefix == NULL || prefix[0] == 0)
Packit 79f644
    return TRUE;
Packit 79f644
Packit 79f644
  if (TPAW_STR_EMPTY (string))
Packit 79f644
    return FALSE;
Packit 79f644
Packit 79f644
  prefix_p = prefix;
Packit 79f644
  for (p = string; *p != '\0'; p = g_utf8_next_char (p))
Packit 79f644
    {
Packit 79f644
      gunichar sc;
Packit 79f644
Packit 79f644
      /* Make the char lower-case, remove its accentuation marks, and ignore it
Packit 79f644
       * if it is just unicode marks */
Packit 79f644
      sc = stripped_char (g_utf8_get_char (p));
Packit 79f644
      if (sc == 0)
Packit 79f644
        continue;
Packit 79f644
Packit 79f644
      /* If we want to go to next word, ignore alpha-num chars */
Packit 79f644
      if (next_word && g_unichar_isalnum (sc))
Packit 79f644
        continue;
Packit 79f644
      next_word = FALSE;
Packit 79f644
Packit 79f644
      /* Ignore word separators */
Packit 79f644
      if (!g_unichar_isalnum (sc))
Packit 79f644
        continue;
Packit 79f644
Packit 79f644
      /* If this char does not match prefix_p, go to next word and start again
Packit 79f644
       * from the beginning of prefix */
Packit 79f644
      if (sc != g_utf8_get_char (prefix_p))
Packit 79f644
        {
Packit 79f644
          next_word = TRUE;
Packit 79f644
          prefix_p = prefix;
Packit 79f644
          continue;
Packit 79f644
        }
Packit 79f644
Packit 79f644
      /* prefix_p match, verify to next char. If this was the last of prefix,
Packit 79f644
       * it means it completely machted and we are done. */
Packit 79f644
      prefix_p = g_utf8_next_char (prefix_p);
Packit 79f644
      if (*prefix_p == '\0')
Packit 79f644
        return TRUE;
Packit 79f644
    }
Packit 79f644
Packit 79f644
  return FALSE;
Packit 79f644
}
Packit 79f644
Packit 79f644
gboolean
Packit 79f644
tpaw_live_search_match_words (const gchar *string,
Packit 79f644
    GPtrArray *words)
Packit 79f644
{
Packit 79f644
  guint i;
Packit 79f644
Packit 79f644
  if (words == NULL)
Packit 79f644
    return TRUE;
Packit 79f644
Packit 79f644
  for (i = 0; i < words->len; i++)
Packit 79f644
    if (!live_search_match_prefix (string, g_ptr_array_index (words, i)))
Packit 79f644
      return FALSE;
Packit 79f644
Packit 79f644
  return TRUE;
Packit 79f644
}
Packit 79f644
Packit 79f644
static gboolean
Packit 79f644
fire_key_navigation_sig (TpawLiveSearch *self,
Packit 79f644
    GdkEventKey *event)
Packit 79f644
{
Packit 79f644
  gboolean ret;
Packit 79f644
Packit 79f644
  g_signal_emit (self, signals[KEYNAV], 0, event, &ret;;
Packit 79f644
  return ret;
Packit 79f644
}
Packit 79f644
Packit 79f644
static gboolean
Packit 79f644
live_search_entry_key_pressed_cb (GtkEntry *entry,
Packit 79f644
    GdkEventKey *event,
Packit 79f644
    gpointer user_data)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (user_data);
Packit 79f644
Packit 79f644
  /* if esc key pressed, hide the search */
Packit 79f644
  if (event->keyval == GDK_KEY_Escape)
Packit 79f644
    {
Packit 79f644
      gtk_widget_hide (GTK_WIDGET (self));
Packit 79f644
      return TRUE;
Packit 79f644
    }
Packit 79f644
Packit 79f644
  /* emit key navigation signal, so other widgets can respond to it properly */
Packit 79f644
  if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down
Packit 79f644
      || event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Page_Down
Packit 79f644
      || event->keyval == GDK_KEY_Menu)
Packit 79f644
     {
Packit 79f644
       return fire_key_navigation_sig (self, event);
Packit 79f644
     }
Packit 79f644
Packit 79f644
  if (event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_End ||
Packit 79f644
      event->keyval == GDK_KEY_space)
Packit 79f644
    {
Packit 79f644
      /* If the live search is visible, the entry should catch the Home/End
Packit 79f644
       * and space events */
Packit 79f644
      if (!gtk_widget_get_visible (GTK_WIDGET (self)))
Packit 79f644
        {
Packit 79f644
          return fire_key_navigation_sig (self, event);
Packit 79f644
        }
Packit 79f644
    }
Packit 79f644
Packit 79f644
  return FALSE;
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_text_changed (GtkEntry *entry,
Packit 79f644
    gpointer user_data)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (user_data);
Packit 79f644
  const gchar *text;
Packit 79f644
Packit 79f644
  text = gtk_entry_get_text (entry);
Packit 79f644
Packit 79f644
  if (TPAW_STR_EMPTY (text))
Packit 79f644
    gtk_widget_hide (GTK_WIDGET (self));
Packit 79f644
  else
Packit 79f644
    gtk_widget_show (GTK_WIDGET (self));
Packit 79f644
Packit 79f644
  if (self->priv->stripped_words != NULL)
Packit 79f644
    g_ptr_array_unref (self->priv->stripped_words);
Packit 79f644
Packit 79f644
  self->priv->stripped_words = tpaw_live_search_strip_utf8_string (text);
Packit 79f644
Packit 79f644
  g_object_notify (G_OBJECT (self), "text");
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_close_pressed (GtkEntry *entry,
Packit 79f644
    GtkEntryIconPosition icon_pos,
Packit 79f644
    GdkEvent *event,
Packit 79f644
    gpointer user_data)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (user_data);
Packit 79f644
Packit 79f644
  gtk_widget_hide (GTK_WIDGET (self));
Packit 79f644
}
Packit 79f644
Packit 79f644
static gboolean
Packit 79f644
live_search_key_press_event_cb (GtkWidget *widget,
Packit 79f644
    GdkEventKey *event,
Packit 79f644
    gpointer user_data)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (user_data);
Packit 79f644
  GdkEvent *new_event;
Packit 79f644
  gboolean ret;
Packit 79f644
Packit 79f644
  /* dont forward this event to the entry, else the event is consumed by the
Packit 79f644
   * entry and does not close the window */
Packit 79f644
  if (!gtk_widget_get_visible (GTK_WIDGET (self)) &&
Packit 79f644
      event->keyval == GDK_KEY_Escape)
Packit 79f644
    return FALSE;
Packit 79f644
Packit 79f644
  /* do not show the search if CTRL and/or ALT are pressed with a key
Packit 79f644
   * this is needed, because otherwise the CTRL + F accel would not work,
Packit 79f644
   * because the entry consumes it */
Packit 79f644
  if (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK) ||
Packit 79f644
      event->keyval == GDK_KEY_Control_L ||
Packit 79f644
      event->keyval == GDK_KEY_Control_R)
Packit 79f644
    return FALSE;
Packit 79f644
Packit 79f644
  /* dont forward the up/down and Page Up/Down arrow keys to the entry,
Packit 79f644
   * they are needed for navigation in the treeview and are not needed in
Packit 79f644
   * the search entry */
Packit 79f644
   if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down ||
Packit 79f644
       event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Page_Down ||
Packit 79f644
       event->keyval == GDK_KEY_Menu)
Packit 79f644
     return FALSE;
Packit 79f644
Packit 79f644
   if (event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_End ||
Packit 79f644
       event->keyval == GDK_KEY_space)
Packit 79f644
     {
Packit 79f644
       /* Home/End and space keys have to be forwarded to the entry only if
Packit 79f644
        * the live search is visible (to move the cursor inside the entry). */
Packit 79f644
       if (!gtk_widget_get_visible (GTK_WIDGET (self)))
Packit 79f644
         return FALSE;
Packit 79f644
     }
Packit 79f644
Packit 79f644
   /* Don't forward shift keys events as focusing the search entry would
Packit 79f644
    * cancel an in-progress editing on a cell renderer (like when renaming a
Packit 79f644
    * group). There is no point focusing it anyway as we don't display the
Packit 79f644
    * search entry when only a shift key is pressed. */
Packit 79f644
   if (event->keyval == GDK_KEY_Shift_L ||
Packit 79f644
       event->keyval == GDK_KEY_Shift_R)
Packit 79f644
       return FALSE;
Packit 79f644
Packit 79f644
  /* realize the widget if it is not realized yet */
Packit 79f644
  gtk_widget_realize (self->priv->search_entry);
Packit 79f644
  if (!gtk_widget_has_focus (self->priv->search_entry))
Packit 79f644
    {
Packit 79f644
      gtk_widget_grab_focus (self->priv->search_entry);
Packit 79f644
      gtk_editable_set_position (GTK_EDITABLE (self->priv->search_entry), -1);
Packit 79f644
    }
Packit 79f644
Packit 79f644
  /* forward the event to the search entry */
Packit 79f644
  new_event = gdk_event_copy ((GdkEvent *) event);
Packit 79f644
  ret = gtk_widget_event (self->priv->search_entry, new_event);
Packit 79f644
  gdk_event_free (new_event);
Packit 79f644
Packit 79f644
  return ret;
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_entry_activate_cb (GtkEntry *entry,
Packit 79f644
    TpawLiveSearch *self)
Packit 79f644
{
Packit 79f644
  g_signal_emit (self, signals[ACTIVATE], 0);
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_release_hook_widget (TpawLiveSearch *self)
Packit 79f644
{
Packit 79f644
  /* remove old handlers if old source was not null */
Packit 79f644
  if (self->priv->hook_widget != NULL)
Packit 79f644
    {
Packit 79f644
      g_signal_handlers_disconnect_by_func (self->priv->hook_widget,
Packit 79f644
          live_search_key_press_event_cb, self);
Packit 79f644
      g_signal_handlers_disconnect_by_func (self->priv->hook_widget,
Packit 79f644
          live_search_hook_widget_destroy_cb, self);
Packit 79f644
      g_object_unref (self->priv->hook_widget);
Packit 79f644
      self->priv->hook_widget = NULL;
Packit 79f644
    }
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_hook_widget_destroy_cb (GtkWidget *object,
Packit 79f644
    gpointer user_data)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (user_data);
Packit 79f644
Packit 79f644
  /* unref the hook widget and hide search */
Packit 79f644
  gtk_widget_hide (GTK_WIDGET (self));
Packit 79f644
  live_search_release_hook_widget (self);
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_dispose (GObject *obj)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (obj);
Packit 79f644
Packit 79f644
  live_search_release_hook_widget (self);
Packit 79f644
Packit 79f644
  if (G_OBJECT_CLASS (tpaw_live_search_parent_class)->dispose != NULL)
Packit 79f644
    G_OBJECT_CLASS (tpaw_live_search_parent_class)->dispose (obj);
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_finalize (GObject *obj)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (obj);
Packit 79f644
Packit 79f644
  if (self->priv->stripped_words != NULL)
Packit 79f644
    g_ptr_array_unref (self->priv->stripped_words);
Packit 79f644
Packit 79f644
  if (G_OBJECT_CLASS (tpaw_live_search_parent_class)->finalize != NULL)
Packit 79f644
    G_OBJECT_CLASS (tpaw_live_search_parent_class)->finalize (obj);
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_get_property (GObject *object,
Packit 79f644
    guint param_id,
Packit 79f644
    GValue *value,
Packit 79f644
    GParamSpec *pspec)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (object);
Packit 79f644
Packit 79f644
  switch (param_id)
Packit 79f644
    {
Packit 79f644
    case PROP_HOOK_WIDGET:
Packit 79f644
      g_value_set_object (value, tpaw_live_search_get_hook_widget (self));
Packit 79f644
      break;
Packit 79f644
    case PROP_TEXT:
Packit 79f644
      g_value_set_string (value, tpaw_live_search_get_text (self));
Packit 79f644
      break;
Packit 79f644
    default:
Packit 79f644
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
Packit 79f644
      break;
Packit 79f644
    }
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_set_property (GObject *object,
Packit 79f644
    guint param_id,
Packit 79f644
    const GValue *value,
Packit 79f644
    GParamSpec *pspec)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (object);
Packit 79f644
Packit 79f644
  switch (param_id) {
Packit 79f644
  case PROP_HOOK_WIDGET:
Packit 79f644
    tpaw_live_search_set_hook_widget (self, g_value_get_object (value));
Packit 79f644
    break;
Packit 79f644
  case PROP_TEXT:
Packit 79f644
    tpaw_live_search_set_text (self, g_value_get_string (value));
Packit 79f644
    break;
Packit 79f644
  default:
Packit 79f644
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
Packit 79f644
    break;
Packit 79f644
  };
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_unmap (GtkWidget *widget)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (widget);
Packit 79f644
Packit 79f644
  GTK_WIDGET_CLASS (tpaw_live_search_parent_class)->unmap (widget);
Packit 79f644
Packit 79f644
  /* unmap can happen if a parent gets hidden, in that case we want to hide
Packit 79f644
   * the live search as well, so when it gets mapped again, the live search
Packit 79f644
   * won't be shown. */
Packit 79f644
  gtk_widget_hide (widget);
Packit 79f644
Packit 79f644
  gtk_entry_set_text (GTK_ENTRY (self->priv->search_entry), "");
Packit 79f644
Packit 79f644
  if (self->priv->hook_widget != NULL)
Packit 79f644
    gtk_widget_grab_focus (self->priv->hook_widget);
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_show (GtkWidget *widget)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (widget);
Packit 79f644
Packit 79f644
  if (!gtk_widget_has_focus (self->priv->search_entry))
Packit 79f644
    gtk_widget_grab_focus (self->priv->search_entry);
Packit 79f644
Packit 79f644
  GTK_WIDGET_CLASS (tpaw_live_search_parent_class)->show (widget);
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
live_search_grab_focus (GtkWidget *widget)
Packit 79f644
{
Packit 79f644
  TpawLiveSearch *self = TPAW_LIVE_SEARCH (widget);
Packit 79f644
Packit 79f644
  if (!gtk_widget_has_focus (self->priv->search_entry))
Packit 79f644
    {
Packit 79f644
      gtk_widget_grab_focus (self->priv->search_entry);
Packit 79f644
      gtk_editable_set_position (GTK_EDITABLE (self->priv->search_entry), -1);
Packit 79f644
    }
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
tpaw_live_search_class_init (TpawLiveSearchClass *klass)
Packit 79f644
{
Packit 79f644
  GObjectClass *object_class = (GObjectClass *) klass;
Packit 79f644
  GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
Packit 79f644
  GParamSpec *param_spec;
Packit 79f644
Packit 79f644
  object_class->finalize = live_search_finalize;
Packit 79f644
  object_class->dispose = live_search_dispose;
Packit 79f644
  object_class->get_property = live_search_get_property;
Packit 79f644
  object_class->set_property = live_search_set_property;
Packit 79f644
Packit 79f644
  widget_class->unmap = live_search_unmap;
Packit 79f644
  widget_class->show = live_search_show;
Packit 79f644
  widget_class->grab_focus = live_search_grab_focus;
Packit 79f644
Packit 79f644
  signals[ACTIVATE] = g_signal_new ("activate",
Packit 79f644
      G_TYPE_FROM_CLASS (object_class),
Packit 79f644
      G_SIGNAL_RUN_LAST,
Packit 79f644
      0,
Packit 79f644
      NULL, NULL,
Packit 79f644
      g_cclosure_marshal_generic,
Packit 79f644
      G_TYPE_NONE, 0);
Packit 79f644
Packit 79f644
  signals[KEYNAV] = g_signal_new ("key-navigation",
Packit 79f644
      G_TYPE_FROM_CLASS (object_class),
Packit 79f644
      G_SIGNAL_RUN_LAST,
Packit 79f644
      0,
Packit 79f644
      g_signal_accumulator_true_handled, NULL,
Packit 79f644
      g_cclosure_marshal_generic,
Packit 79f644
      G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
Packit 79f644
Packit 79f644
  param_spec = g_param_spec_object ("hook-widget", "Live Search Hook Widget",
Packit 79f644
      "The live search catches key-press-events on this widget",
Packit 79f644
      GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
Packit 79f644
  g_object_class_install_property (object_class, PROP_HOOK_WIDGET,
Packit 79f644
      param_spec);
Packit 79f644
Packit 79f644
  param_spec = g_param_spec_string ("text", "Live Search Text",
Packit 79f644
      "The text of the live search entry",
Packit 79f644
      "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
Packit 79f644
  g_object_class_install_property (object_class, PROP_TEXT, param_spec);
Packit 79f644
Packit 79f644
  g_type_class_add_private (klass, sizeof (TpawLiveSearchPriv));
Packit 79f644
}
Packit 79f644
Packit 79f644
static void
Packit 79f644
tpaw_live_search_init (TpawLiveSearch *self)
Packit 79f644
{
Packit 79f644
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TPAW_TYPE_LIVE_SEARCH,
Packit 79f644
        TpawLiveSearchPriv);
Packit 79f644
Packit 79f644
  gtk_widget_set_no_show_all (GTK_WIDGET (self), TRUE);
Packit 79f644
Packit 79f644
  self->priv->search_entry = gtk_entry_new ();
Packit 79f644
  gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->search_entry),
Packit 79f644
      GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLOSE);
Packit 79f644
  gtk_entry_set_icon_activatable (GTK_ENTRY (self->priv->search_entry),
Packit 79f644
      GTK_ENTRY_ICON_SECONDARY, TRUE);
Packit 79f644
  gtk_entry_set_icon_sensitive (GTK_ENTRY (self->priv->search_entry),
Packit 79f644
      GTK_ENTRY_ICON_SECONDARY, TRUE);
Packit 79f644
  gtk_widget_show (self->priv->search_entry);
Packit 79f644
Packit 79f644
  gtk_box_pack_start (GTK_BOX (self), self->priv->search_entry, TRUE, TRUE, 0);
Packit 79f644
Packit 79f644
  g_signal_connect (self->priv->search_entry, "icon_release",
Packit 79f644
      G_CALLBACK (live_search_close_pressed), self);
Packit 79f644
  g_signal_connect (self->priv->search_entry, "changed",
Packit 79f644
      G_CALLBACK (live_search_text_changed), self);
Packit 79f644
  g_signal_connect (self->priv->search_entry, "key-press-event",
Packit 79f644
      G_CALLBACK (live_search_entry_key_pressed_cb), self);
Packit 79f644
  g_signal_connect (self->priv->search_entry, "activate",
Packit 79f644
      G_CALLBACK (live_search_entry_activate_cb), self);
Packit 79f644
Packit 79f644
  self->priv->hook_widget = NULL;
Packit 79f644
}
Packit 79f644
Packit 79f644
GtkWidget *
Packit 79f644
tpaw_live_search_new (GtkWidget *hook)
Packit 79f644
{
Packit 79f644
  g_return_val_if_fail (hook == NULL || GTK_IS_WIDGET (hook), NULL);
Packit 79f644
Packit 79f644
  return g_object_new (TPAW_TYPE_LIVE_SEARCH,
Packit 79f644
      "orientation", GTK_ORIENTATION_HORIZONTAL,
Packit 79f644
      "hook-widget", hook,
Packit 79f644
      NULL);
Packit 79f644
}
Packit 79f644
Packit 79f644
/* public methods */
Packit 79f644
Packit 79f644
GtkWidget *
Packit 79f644
tpaw_live_search_get_hook_widget (TpawLiveSearch *self)
Packit 79f644
{
Packit 79f644
  g_return_val_if_fail (TPAW_IS_LIVE_SEARCH (self), NULL);
Packit 79f644
Packit 79f644
  return self->priv->hook_widget;
Packit 79f644
}
Packit 79f644
Packit 79f644
void
Packit 79f644
tpaw_live_search_set_hook_widget (TpawLiveSearch *self,
Packit 79f644
    GtkWidget *hook)
Packit 79f644
{
Packit 79f644
  g_return_if_fail (TPAW_IS_LIVE_SEARCH (self));
Packit 79f644
  g_return_if_fail (hook == NULL || GTK_IS_WIDGET (hook));
Packit 79f644
Packit 79f644
  /* release the actual widget */
Packit 79f644
  live_search_release_hook_widget (self);
Packit 79f644
Packit 79f644
  /* connect handlers if new source is not null */
Packit 79f644
  if (hook != NULL)
Packit 79f644
    {
Packit 79f644
      self->priv->hook_widget = g_object_ref (hook);
Packit 79f644
      g_signal_connect (self->priv->hook_widget, "key-press-event",
Packit 79f644
          G_CALLBACK (live_search_key_press_event_cb),
Packit 79f644
          self);
Packit 79f644
      g_signal_connect (self->priv->hook_widget, "destroy",
Packit 79f644
          G_CALLBACK (live_search_hook_widget_destroy_cb),
Packit 79f644
          self);
Packit 79f644
    }
Packit 79f644
}
Packit 79f644
Packit 79f644
const gchar *
Packit 79f644
tpaw_live_search_get_text (TpawLiveSearch *self)
Packit 79f644
{
Packit 79f644
  g_return_val_if_fail (TPAW_IS_LIVE_SEARCH (self), NULL);
Packit 79f644
Packit 79f644
  return gtk_entry_get_text (GTK_ENTRY (self->priv->search_entry));
Packit 79f644
}
Packit 79f644
Packit 79f644
void
Packit 79f644
tpaw_live_search_set_text (TpawLiveSearch *self,
Packit 79f644
    const gchar *text)
Packit 79f644
{
Packit 79f644
  g_return_if_fail (TPAW_IS_LIVE_SEARCH (self));
Packit 79f644
  g_return_if_fail (text != NULL);
Packit 79f644
Packit 79f644
  gtk_entry_set_text (GTK_ENTRY (self->priv->search_entry), text);
Packit 79f644
}
Packit 79f644
Packit 79f644
/**
Packit 79f644
 * tpaw_live_search_match:
Packit 79f644
 * @self: a #TpawLiveSearch
Packit 79f644
 * @string: a string where to search, must be valid UTF-8.
Packit 79f644
 *
Packit 79f644
 * Search if one of the words in @string string starts with the current text
Packit 79f644
 * of @self.
Packit 79f644
 *
Packit 79f644
 * Searching for "aba" in "Abasto" will match, searching in "Moraba" will not,
Packit 79f644
 * and searching in "A tool (abacus)" will do.
Packit 79f644
 *
Packit 79f644
 * The match is not case-sensitive, and regardless of the accentuation marks.
Packit 79f644
 *
Packit 79f644
 * Returns: %TRUE if a match is found, %FALSE otherwise.
Packit 79f644
 *
Packit 79f644
 **/
Packit 79f644
gboolean
Packit 79f644
tpaw_live_search_match (TpawLiveSearch *self,
Packit 79f644
    const gchar *string)
Packit 79f644
{
Packit 79f644
  g_return_val_if_fail (TPAW_IS_LIVE_SEARCH (self), FALSE);
Packit 79f644
Packit 79f644
  return tpaw_live_search_match_words (string, self->priv->stripped_words);
Packit 79f644
}
Packit 79f644
Packit 79f644
gboolean
Packit 79f644
tpaw_live_search_match_string (const gchar *string,
Packit 79f644
    const gchar *prefix)
Packit 79f644
{
Packit 79f644
  GPtrArray *words;
Packit 79f644
  gboolean match;
Packit 79f644
Packit 79f644
  words = tpaw_live_search_strip_utf8_string (prefix);
Packit 79f644
  match = tpaw_live_search_match_words (string, words);
Packit 79f644
  if (words != NULL)
Packit 79f644
    g_ptr_array_unref (words);
Packit 79f644
Packit 79f644
  return match;
Packit 79f644
}
Packit 79f644
Packit 79f644
GPtrArray *
Packit 79f644
tpaw_live_search_get_words (TpawLiveSearch *self)
Packit 79f644
{
Packit 79f644
  return self->priv->stripped_words;
Packit 79f644
}