<commit_before>/*
 * This file is a part of hildon
 *
 * Copyright (C) 2007-2009 Nokia Corporation.
 *
 * Based in OssoABookLiveSearch, OSSO Address Book.
 * Author: <NAME> <<EMAIL>>
 * Hildon version: <NAME> <<EMAIL>>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; version 2 of the license.
 *
 * 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 Lesser Public License for more details.
 *
 */

/**
 * SECTION:hildon-live-search
 * @short_description: A widget for manipulating #GtkTreeModelFilter
 * instances.
 *
 * This widget provides a user interface for manipulating
 * #GtkTreeModelFilter instances.
 *
 * To set a #GtkTreeFilterModel to filter with, use
 * hildon_live_search_set_filter(). By default, #HildonLiveSearch
 * filters on the child model of the filter model set using a case
 * sensitive prefix comparison on the model's column specified by
 * #HildonLiveSearch:text-column. If a more refined filtering is
 * necessary, you can use hildon_live_search_set_visible_func() to
 * specify a #HildonLiveSearchVisibleFunc to use.
 *
 */

#include                                        "hildon-live-search.h"

#include                                        <hildon/hildon.h>
#include                                        <string.h>

G_DEFINE_TYPE (HildonLiveSearch, hildon_live_search, GTK_TYPE_TOOLBAR);

#define                                         GET_PRIVATE(o)                     \
                                                (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
                                                HILDON_TYPE_LIVE_SEARCH,           \
                                                HildonLiveSearchPrivate))

struct _HildonLiveSearchPrivate
{
    GtkTreeModelFilter *filter;

    GtkTreeView *treeview;

    GtkWidget *entry;
    GtkWidget *event_widget;
    GHashTable *selection_map;

    gulong key_press_id;
    gulong event_widget_destroy_id;
    gulong treeview_destroy_id;

    gchar *prefix;
    gint text_column;

    HildonLiveSearchVisibleFunc visible_func;
    gpointer visible_data;
    GDestroyNotify visible_destroy;
    gboolean visible_func_set;
};

enum
{
    PROP_0,

    PROP_FILTER,
    PROP_WIDGET,
    PROP_TEXT_COLUMN
};

enum
{
  REFILTER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

static gboolean
visible_func                                    (GtkTreeModel *model,
                                                 GtkTreeIter  *iter,
                                                 gpointer      data);

/* Private implementation */

static guint
hash_func                                       (gconstpointer key)
{
    GtkTreePath *path;
    gchar *path_str;
    guint val;

    path = (GtkTreePath *) key;
    path_str = gtk_tree_path_to_string (path);
    val = g_str_hash (path_str);
    g_free (path_str);

    return val;
}

static gboolean
key_equal_func                                  (gconstpointer v1,
                                                 gconstpointer v2)
{
    return gtk_tree_path_compare ((GtkTreePath *)v1,
                                  (GtkTreePath *)v2) == 0;
}


static void
refilter (HildonLiveSearch *livesearch)
{
    HildonLiveSearchPrivate *priv = livesearch->priv;
    gboolean handled = FALSE;

    g_signal_emit (livesearch, signals[REFILTER], 0, &handled);
    if (!handled)
        gtk_tree_model_filter_refilter (priv->filter);
}

/**
 * selection_map_create:
 * @priv: The private pimpl
 *
 * Adds a selection map which is useful when merging selected rows in
 * the treeview, when the live search widget is used.
 **/
static void
selection_map_create                            (HildonLiveSearchPrivate *priv)
{
    gboolean working;
    GtkTreeModel *base_model;
    GtkTreeIter iter;
    GtkTreePath *path;

    g_assert (priv->selection_map == NULL);

    base_model = gtk_tree_model_filter_get_model (priv->filter);

    priv->selection_map = g_hash_table_new_full
        (hash_func, key_equal_func, (GDestroyNotify) gtk_tree_path_free, NULL);

    for (working = gtk_tree_model_get_iter_first (base_model, &iter);
         working;
         working = gtk_tree_model_iter_next (base_model, &iter)) {
        path = gtk_tree_model_get_path (base_model, &iter);
        g_hash_table_insert (priv->selection_map,
                             path, GINT_TO_POINTER (FALSE));
    }
}

/**
 * selection_map_destroy:
 * @priv: The private pimpl
 *
 * Destroys resources associated with the selection map.
 **/
static void
selection_map_destroy                           (HildonLiveSearchPrivate *priv)
{
    if (priv->selection_map != NULL) {
        g_hash_table_destroy (priv->selection_map);
        priv->selection_map = NULL;
    }
}

/**
 * selection_map_update_map_from_selection:
 * @priv: The private pimpl
 *
 * Find out which rows are visible in filter, and mark them as selected
 * or unselected from treeview to selection map.
 **/
static void
selection_map_update_map_from_selection         (HildonLiveSearchPrivate *priv)
{
    gboolean working;
    GtkTreeModel *base_model;
    GtkTreeSelection *selection;
    GtkTreeIter iter;

    base_model = gtk_tree_model_filter_get_model (priv->filter);
    selection = gtk_tree_view_get_selection (priv->treeview);

    for (working = gtk_tree_model_get_iter_first (base_model, &iter);
         working;
         working = gtk_tree_model_iter_next (base_model, &iter)) {
        GtkTreePath *path, *filter_path;
        path = gtk_tree_model_get_path (base_model, &iter);
        filter_path = gtk_tree_model_filter_convert_child_path_to_path (priv->filter, path);

        if (filter_path) {
            if (gtk_tree_selection_path_is_selected
                (selection, filter_path)) {
                g_hash_table_replace
                    (priv->selection_map,
                     path,
                     GINT_TO_POINTER (TRUE));
            } else {
                g_hash_table_replace
                    (priv->selection_map,
                     path,
                     GINT_TO_POINTER (FALSE));
            }
            gtk_tree_path_free (filter_path);
        } else {
            gtk_tree_path_free (path);
        }
    }
}

/**
 * selection_map_update_selection_from_map:
 * @priv: The private pimpl
 *
 * For currently visible rows in filter, set selection from selection
 * map to treeview.
 **/
static void
selection_map_update_selection_from_map         (HildonLiveSearchPrivate *priv)
{
    gboolean working;
    GtkTreeModel *base_model;
    GtkTreeSelection *selection;
    GtkTreeIter iter;

    base_model = gtk_tree_model_filter_get_model (priv->filter);
    selection = gtk_tree_view_get_selection (priv->treeview);

    for (working = gtk_tree_model_get_iter_first (base_model, &iter);
         working;
         working = gtk_tree_model_iter_next (base_model, &iter)) {
        GtkTreePath *path, *filter_path;
        path = gtk_tree_model_get_path (base_model, &iter);
        filter_path = gtk_tree_model_filter_convert_child_path_to_path (priv->filter, path);

        if (filter_path) {
            gboolean selected;

            selected = GPOINTER_TO_INT
                (g_hash_table_lookup
                 (priv->selection_map, path));

            if (selected) {
                gtk_tree_selection_select_path
                    (selection, filter_path);
            } else {
                gtk_tree_selection_unselect_path
                    (selection, filter_path);
            }
            gtk_tree_path_free (filter_path);
        }
        gtk_tree_path_free (path);
    }
}

static void
on_entry_changed                                (GtkEntry *entry,
                                                 gpointer  user_data)
{
    HildonLiveSearch *livesearch = user_data;
    HildonLiveSearchPrivate *priv;
    const char *text;
    glong len;
    char *old_prefix;

    g_return_if_fail (HILDON_IS_LIVE_SEARCH (livesearch));
    priv = livesearch->priv;

    text = gtk_entry_get_text (GTK_ENTRY (entry));
    len = g_utf8_strlen (text, -1);

    old_prefix = priv->prefix;

    if (len < 1) {
        text = NULL;
    } else {
        if ((old_prefix == NULL) || (strlen (old_prefix) == 0)) {
            selection_map_create (priv);
        }
    }

    selection_map_update_map_from_selection (priv);
    priv->prefix = g_strdup (text);
    refilter (livesearch);
    selection_map_update_selection_from_map (priv);

    if (len < 1) {
        selection_map_destroy (priv);
        gtk_widget_hide (GTK_WIDGET (livesearch));
    } else {
        gtk_widget_show (GTK_WIDGET (livesearch));
    }

    g_free (old_prefix);
}

/**
 * hildon_live_search_append_text:
 * @livesearch: An #HildonLiveSearch widget
 * @text: Text to append. @text is copied internally
 * can be freed later by the caller.
 *
 * Appends a string to the entry text in the live search widget.
 *
 * Since: 2.2.4
 **/
void
hildon_live_search_append_text                  (HildonLiveSearch *livesearch,
                                                 const char       *text)
{
    GtkEditable *editable;
    int pos, start, end;

    g_return_if_fail (HILDON_IS_LIVE_SEARCH (livesearch));
    g_return_if_fail (NULL != text);

    editable = GTK_EDITABLE (livesearch->priv->entry);

    if (gtk_editable_get_selection_bounds (editable, &start, &end)) {
        gtk_editable_delete_text (editable, start, end);
    }
    pos = gtk_editable_get_position (editable);
    gtk_editable_insert_text (editable, text, strlen (text), &pos);
    gtk_editable_set_position (editable, pos + 1);
}

/**
 * hildon_live_search_get_text:
 * @livesearch: An #HildonLiveSearch widget
 *
 * Retrieves the text contents of the #HildonLiveSearch widget.
 *
 * Returns: a pointer to the text contents of the widget as a
 * string. This string should not be freed, modified, or stored.
 *
 * Since: 2.2.4
 **/
const char *
hildon_live_search_get_text                     (HildonLiveSearch *livesearch)
{
    g_return_val_if_fail (HILDON_IS_LIVE_SEARCH (livesearch), NULL);

    return gtk_entry_get_text (GTK_ENTRY (livesearch->priv->entry));
}

/*
 * Key press handler. This takes key presses from the source widget and
 * manipulate the entry. This manipulation then calls #on_entry_changed.
 */
static gboolean
on_key_press_event                              (GtkWidget        *widget,
                                                 GdkEventKey      *event,
                                                 HildonLiveSearch *live_search)
{
    HildonLiveSearchPrivate *priv;
    GdkEvent *new_event;
    gboolean handled;

    g_return_val_if_fail (HILDON_IS_LIVE_SEARCH (live_search), FALSE);
    priv = live_search->priv;

    /* If the entry is realized and has focus, it is enough to catch events.
     * This assume that the toolbar is a child of the hook widget. */
    gtk_widget_realize (priv->entry);
    gtk_widget_grab_focus (priv->entry);

    new_event = gdk_event_copy ((GdkEvent *)event);
    handled = gtk_widget_event (priv->entry, new_event);
    gdk_event_free (new_event);

    return handled;
}<commit_msg>Make keyboard shortcuts work with HildonLiveSearch<commit_after>/*
 * This file is a part of hildon
 *
 * Copyright (C) 2007-2009 Nokia Corporation.
 *
 * Based in OssoABookLiveSearch, OSSO Address Book.
 * Author: <NAME> <<EMAIL>>
 * Hildon version: <NAME> <<EMAIL>>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; version 2 of the license.
 *
 * 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 Lesser Public License for more details.
 *
 */

/**
 * SECTION:hildon-live-search
 * @short_description: A widget for manipulating #GtkTreeModelFilter
 * instances.
 *
 * This widget provides a user interface for manipulating
 * #GtkTreeModelFilter instances.
 *
 * To set a #GtkTreeFilterModel to filter with, use
 * hildon_live_search_set_filter(). By default, #HildonLiveSearch
 * filters on the child model of the filter model set using a case
 * sensitive prefix comparison on the model's column specified by
 * #HildonLiveSearch:text-column. If a more refined filtering is
 * necessary, you can use hildon_live_search_set_visible_func() to
 * specify a #HildonLiveSearchVisibleFunc to use.
 *
 */

#include                                        "hildon-live-search.h"

#include                                        <hildon/hildon.h>
#include                                        <string.h>

G_DEFINE_TYPE (HildonLiveSearch, hildon_live_search, GTK_TYPE_TOOLBAR);

#define                                         GET_PRIVATE(o)                     \
                                                (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
                                                HILDON_TYPE_LIVE_SEARCH,           \
                                                HildonLiveSearchPrivate))

struct _HildonLiveSearchPrivate
{
    GtkTreeModelFilter *filter;

    GtkTreeView *treeview;

    GtkWidget *entry;
    GtkWidget *event_widget;
    GHashTable *selection_map;

    gulong key_press_id;
    gulong event_widget_destroy_id;
    gulong treeview_destroy_id;

    gchar *prefix;
    gint text_column;

    HildonLiveSearchVisibleFunc visible_func;
    gpointer visible_data;
    GDestroyNotify visible_destroy;
    gboolean visible_func_set;
};

enum
{
    PROP_0,

    PROP_FILTER,
    PROP_WIDGET,
    PROP_TEXT_COLUMN
};

enum
{
  REFILTER,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

static gboolean
visible_func                                    (GtkTreeModel *model,
                                                 GtkTreeIter  *iter,
                                                 gpointer      data);

/* Private implementation */

static guint
hash_func                                       (gconstpointer key)
{
    GtkTreePath *path;
    gchar *path_str;
    guint val;

    path = (GtkTreePath *) key;
    path_str = gtk_tree_path_to_string (path);
    val = g_str_hash (path_str);
    g_free (path_str);

    return val;
}

static gboolean
key_equal_func                                  (gconstpointer v1,
                                                 gconstpointer v2)
{
    return gtk_tree_path_compare ((GtkTreePath *)v1,
                                  (GtkTreePath *)v2) == 0;
}


static void
refilter (HildonLiveSearch *livesearch)
{
    HildonLiveSearchPrivate *priv = livesearch->priv;
    gboolean handled = FALSE;

    g_signal_emit (livesearch, signals[REFILTER], 0, &handled);
    if (!handled)
        gtk_tree_model_filter_refilter (priv->filter);
}

/**
 * selection_map_create:
 * @priv: The private pimpl
 *
 * Adds a selection map which is useful when merging selected rows in
 * the treeview, when the live search widget is used.
 **/
static void
selection_map_create                            (HildonLiveSearchPrivate *priv)
{
    gboolean working;
    GtkTreeModel *base_model;
    GtkTreeIter iter;
    GtkTreePath *path;

    g_assert (priv->selection_map == NULL);

    base_model = gtk_tree_model_filter_get_model (priv->filter);

    priv->selection_map = g_hash_table_new_full
        (hash_func, key_equal_func, (GDestroyNotify) gtk_tree_path_free, NULL);

    for (working = gtk_tree_model_get_iter_first (base_model, &iter);
         working;
         working = gtk_tree_model_iter_next (base_model, &iter)) {
        path = gtk_tree_model_get_path (base_model, &iter);
        g_hash_table_insert (priv->selection_map,
                             path, GINT_TO_POINTER (FALSE));
    }
}

/**
 * selection_map_destroy:
 * @priv: The private pimpl
 *
 * Destroys resources associated with the selection map.
 **/
static void
selection_map_destroy                           (HildonLiveSearchPrivate *priv)
{
    if (priv->selection_map != NULL) {
        g_hash_table_destroy (priv->selection_map);
        priv->selection_map = NULL;
    }
}

/**
 * selection_map_update_map_from_selection:
 * @priv: The private pimpl
 *
 * Find out which rows are visible in filter, and mark them as selected
 * or unselected from treeview to selection map.
 **/
static void
selection_map_update_map_from_selection         (HildonLiveSearchPrivate *priv)
{
    gboolean working;
    GtkTreeModel *base_model;
    GtkTreeSelection *selection;
    GtkTreeIter iter;

    base_model = gtk_tree_model_filter_get_model (priv->filter);
    selection = gtk_tree_view_get_selection (priv->treeview);

    for (working = gtk_tree_model_get_iter_first (base_model, &iter);
         working;
         working = gtk_tree_model_iter_next (base_model, &iter)) {
        GtkTreePath *path, *filter_path;
        path = gtk_tree_model_get_path (base_model, &iter);
        filter_path = gtk_tree_model_filter_convert_child_path_to_path (priv->filter, path);

        if (filter_path) {
            if (gtk_tree_selection_path_is_selected
                (selection, filter_path)) {
                g_hash_table_replace
                    (priv->selection_map,
                     path,
                     GINT_TO_POINTER (TRUE));
            } else {
                g_hash_table_replace
                    (priv->selection_map,
                     path,
                     GINT_TO_POINTER (FALSE));
            }
            gtk_tree_path_free (filter_path);
        } else {
            gtk_tree_path_free (path);
        }
    }
}

/**
 * selection_map_update_selection_from_map:
 * @priv: The private pimpl
 *
 * For currently visible rows in filter, set selection from selection
 * map to treeview.
 **/
static void
selection_map_update_selection_from_map         (HildonLiveSearchPrivate *priv)
{
    gboolean working;
    GtkTreeModel *base_model;
    GtkTreeSelection *selection;
    GtkTreeIter iter;

    base_model = gtk_tree_model_filter_get_model (priv->filter);
    selection = gtk_tree_view_get_selection (priv->treeview);

    for (working = gtk_tree_model_get_iter_first (base_model, &iter);
         working;
         working = gtk_tree_model_iter_next (base_model, &iter)) {
        GtkTreePath *path, *filter_path;
        path = gtk_tree_model_get_path (base_model, &iter);
        filter_path = gtk_tree_model_filter_convert_child_path_to_path (priv->filter, path);

        if (filter_path) {
            gboolean selected;

            selected = GPOINTER_TO_INT
                (g_hash_table_lookup
                 (priv->selection_map, path));

            if (selected) {
                gtk_tree_selection_select_path
                    (selection, filter_path);
            } else {
                gtk_tree_selection_unselect_path
                    (selection, filter_path);
            }
            gtk_tree_path_free (filter_path);
        }
        gtk_tree_path_free (path);
    }
}

static void
on_entry_changed                                (GtkEntry *entry,
                                                 gpointer  user_data)
{
    HildonLiveSearch *livesearch = user_data;
    HildonLiveSearchPrivate *priv;
    const char *text;
    glong len;
    char *old_prefix;

    g_return_if_fail (HILDON_IS_LIVE_SEARCH (livesearch));
    priv = livesearch->priv;

    text = gtk_entry_get_text (GTK_ENTRY (entry));
    len = g_utf8_strlen (text, -1);

    old_prefix = priv->prefix;

    if (len < 1) {
        text = NULL;
    } else {
        if ((old_prefix == NULL) || (strlen (old_prefix) == 0)) {
            selection_map_create (priv);
        }
    }

    selection_map_update_map_from_selection (priv);
    priv->prefix = g_strdup (text);
    refilter (livesearch);
    selection_map_update_selection_from_map (priv);

    if (len < 1) {
        selection_map_destroy (priv);
        gtk_widget_hide (GTK_WIDGET (livesearch));
    } else {
        gtk_widget_show (GTK_WIDGET (livesearch));
    }

    g_free (old_prefix);
}

/**
 * hildon_live_search_append_text:
 * @livesearch: An #HildonLiveSearch widget
 * @text: Text to append. @text is copied internally
 * can be freed later by the caller.
 *
 * Appends a string to the entry text in the live search widget.
 *
 * Since: 2.2.4
 **/
void
hildon_live_search_append_text                  (HildonLiveSearch *livesearch,
                                                 const char       *text)
{
    GtkEditable *editable;
    int pos, start, end;

    g_return_if_fail (HILDON_IS_LIVE_SEARCH (livesearch));
    g_return_if_fail (NULL != text);

    editable = GTK_EDITABLE (livesearch->priv->entry);

    if (gtk_editable_get_selection_bounds (editable, &start, &end)) {
        gtk_editable_delete_text (editable, start, end);
    }
    pos = gtk_editable_get_position (editable);
    gtk_editable_insert_text (editable, text, strlen (text), &pos);
    gtk_editable_set_position (editable, pos + 1);
}

/**
 * hildon_live_search_get_text:
 * @livesearch: An #HildonLiveSearch widget
 *
 * Retrieves the text contents of the #HildonLiveSearch widget.
 *
 * Returns: a pointer to the text contents of the widget as a
 * string. This string should not be freed, modified, or stored.
 *
 * Since: 2.2.4
 **/
const char *
hildon_live_search_get_text                     (HildonLiveSearch *livesearch)
{
    g_return_val_if_fail (HILDON_IS_LIVE_SEARCH (livesearch), NULL);

    return gtk_entry_get_text (GTK_ENTRY (livesearch->priv->entry));
}

/*
 * Key press handler. This takes key presses from the source widget and
 * manipulate the entry. This manipulation then calls #on_entry_changed.
 */
static gboolean
on_key_press_event                              (GtkWidget        *widget,
                                                 GdkEventKey      *event,
                                                 HildonLiveSearch *live_search)
{
    HildonLiveSearchPrivate *priv;
    gboolean handled = FALSE;

    g_return_val_if_fail (HILDON_IS_LIVE_SEARCH (live_search), FALSE);
    priv = live_search->priv;

    if (GTK_WIDGET_VISIBLE (priv->treeview)) {
        /* If the live search is hidden, Ctrl+whatever is always
         * passed to the treeview */
        if (GTK_WIDGET_VISIBLE (live_search) || !(event->state & GDK_CONTROL_MASK)) {
            GdkEvent *new_event;

            /* If the entry is realized and has focus, it is enough to catch events.
             * This assumes that the toolbar is a child of the hook widget. */
            gtk_widget_realize (priv->entry);
            if (!GTK_WIDGET_HAS_FOCUS (priv->entry))
                gtk_widget_grab_focus (priv->entry);

            new_event = gdk_event_copy ((GdkEvent *)event);
            handled = gtk_widget_event (priv->entry, new_event);
            gdk_event_free (new_event);
        } else if (!GTK_WIDGET_HAS_FOCUS (priv->treeview)) {
            gtk_widget_grab_focus (GTK_WIDGET (priv->treeview));
        }
    }

    return handled;
}
{'fraction_non_alphanumeric': 0.0803706097823745, 'fraction_numerical': 0.0024994613229907345, 'mean_word_length': 2.307111301125837, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 82, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 58, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2459335', 'n_tokens_mistral': 7151, 'n_tokens_neox': 6943, 'n_words': 1713}