842 lines
29 KiB
C
842 lines
29 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* pikasearchpopup.c
|
|
* Copyright (C) 2015 Jehan <jehan at girinstud.io>
|
|
*
|
|
* 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 3 of the License, 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, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
#include "libpikabase/pikabase.h"
|
|
#include "libpikawidgets/pikawidgets.h"
|
|
|
|
#include "widgets-types.h"
|
|
|
|
#include "core/pika.h"
|
|
|
|
#include "pikaaction.h"
|
|
#include "pikahelp-ids.h"
|
|
#include "pikapopup.h"
|
|
#include "pikasearchpopup.h"
|
|
#include "pikatoggleaction.h"
|
|
|
|
#include "pika-intl.h"
|
|
|
|
|
|
enum
|
|
{
|
|
COLUMN_ICON,
|
|
COLUMN_MARKUP,
|
|
COLUMN_TOOLTIP,
|
|
COLUMN_ACTION,
|
|
COLUMN_SENSITIVE,
|
|
COLUMN_SECTION,
|
|
N_COL
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_PIKA,
|
|
PROP_CALLBACK,
|
|
PROP_CALLBACK_DATA
|
|
};
|
|
|
|
|
|
struct _PikaSearchPopupPrivate
|
|
{
|
|
Pika *pika;
|
|
GtkWidget *keyword_entry;
|
|
GtkWidget *results_list;
|
|
GtkWidget *list_view;
|
|
|
|
PikaSearchPopupCallback build_results;
|
|
gpointer build_results_data;
|
|
};
|
|
|
|
|
|
static void pika_search_popup_constructed (GObject *object);
|
|
static void pika_search_popup_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void pika_search_popup_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void pika_search_popup_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
|
|
static void pika_search_popup_confirm (PikaPopup *popup);
|
|
|
|
/* Signal handlers on the search entry */
|
|
static void keyword_entry_icon_press (GtkEntry *entry,
|
|
GtkEntryIconPosition icon_pos,
|
|
GdkEvent *event,
|
|
PikaSearchPopup *popup);
|
|
static gboolean keyword_entry_key_press_event (GtkWidget *widget,
|
|
GdkEventKey *event,
|
|
PikaSearchPopup *popup);
|
|
static gboolean keyword_entry_key_release_event (GtkWidget *widget,
|
|
GdkEventKey *event,
|
|
PikaSearchPopup *popup);
|
|
|
|
/* Signal handlers on the results list */
|
|
static gboolean results_list_key_press_event (GtkWidget *widget,
|
|
GdkEventKey *kevent,
|
|
PikaSearchPopup *popup);
|
|
static void results_list_row_activated (GtkTreeView *treeview,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *col,
|
|
PikaSearchPopup *popup);
|
|
|
|
/* Utils */
|
|
static void pika_search_popup_run_selected (PikaSearchPopup *popup);
|
|
static void pika_search_popup_setup_results (GtkWidget **results_list,
|
|
GtkWidget **list_view);
|
|
static void pika_search_popup_help (PikaSearchPopup *popup);
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (PikaSearchPopup, pika_search_popup, PIKA_TYPE_POPUP)
|
|
|
|
#define parent_class pika_search_popup_parent_class
|
|
|
|
static gint window_height = 0;
|
|
|
|
|
|
static void
|
|
pika_search_popup_class_init (PikaSearchPopupClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
PikaPopupClass *popup_class = PIKA_POPUP_CLASS (klass);
|
|
|
|
object_class->constructed = pika_search_popup_constructed;
|
|
object_class->set_property = pika_search_popup_set_property;
|
|
object_class->get_property = pika_search_popup_get_property;
|
|
|
|
widget_class->size_allocate = pika_search_popup_size_allocate;
|
|
|
|
popup_class->confirm = pika_search_popup_confirm;
|
|
|
|
/**
|
|
* PikaSearchPopup:pika:
|
|
*
|
|
* The #Pika object.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_PIKA,
|
|
g_param_spec_object ("pika",
|
|
NULL, NULL,
|
|
G_TYPE_OBJECT,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY));
|
|
/**
|
|
* PikaSearchPopup:callback:
|
|
*
|
|
* The #PikaSearchPopupCallback used to fill in results.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_CALLBACK,
|
|
g_param_spec_pointer ("callback", NULL, NULL,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY));
|
|
/**
|
|
* PikaSearchPopup:callback-data:
|
|
*
|
|
* The #GPointer fed as last parameter to the #PikaSearchPopupCallback.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_CALLBACK_DATA,
|
|
g_param_spec_pointer ("callback-data", NULL, NULL,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY));
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_init (PikaSearchPopup *search_popup)
|
|
{
|
|
search_popup->priv = pika_search_popup_get_instance_private (search_popup);
|
|
}
|
|
|
|
/************ Public Functions ****************/
|
|
|
|
/**
|
|
* pika_search_popup_new:
|
|
* @pika: #Pika object.
|
|
* @role: the role to give to the #GtkWindow.
|
|
* @title: the #GtkWindow title.
|
|
* @callback: the #PikaSearchPopupCallback used to fill in results.
|
|
* @callback_data: data fed to @callback.
|
|
*
|
|
* Returns: a new #PikaSearchPopup.
|
|
*/
|
|
GtkWidget *
|
|
pika_search_popup_new (Pika *pika,
|
|
const gchar *role,
|
|
const gchar *title,
|
|
PikaSearchPopupCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
widget = g_object_new (PIKA_TYPE_SEARCH_POPUP,
|
|
"type", GTK_WINDOW_TOPLEVEL,
|
|
"type-hint", GDK_WINDOW_TYPE_HINT_DIALOG,
|
|
"decorated", TRUE,
|
|
"modal", TRUE,
|
|
"role", role,
|
|
"title", title,
|
|
|
|
"pika", pika,
|
|
"callback", callback,
|
|
"callback-data", callback_data,
|
|
NULL);
|
|
gtk_window_set_modal (GTK_WINDOW (widget), FALSE);
|
|
|
|
|
|
return widget;
|
|
}
|
|
|
|
/**
|
|
* pika_search_popup_add_result:
|
|
* @popup: the #PikaSearchPopup.
|
|
* @action: a #PikaAction to add in results list.
|
|
* @section: the section to add @action.
|
|
*
|
|
* Adds @action in the @popup's results at @section.
|
|
* The section only indicates relative order. If you want some items
|
|
* to appear before other, simply use lower @section.
|
|
*/
|
|
void
|
|
pika_search_popup_add_result (PikaSearchPopup *popup,
|
|
PikaAction *action,
|
|
gint section)
|
|
{
|
|
gchar **accels;
|
|
GtkTreeIter iter;
|
|
GtkTreeIter next_section;
|
|
GtkListStore *store;
|
|
GtkTreeModel *model;
|
|
gchar *markup;
|
|
gchar *action_name;
|
|
gchar *label;
|
|
gchar *escaped_label = NULL;
|
|
const gchar *icon_name;
|
|
gchar *shortcut_helper = NULL;
|
|
const gchar *menu_path;
|
|
gchar *menu_path_helper = NULL;
|
|
const gchar *tooltip;
|
|
gchar *escaped_tooltip = NULL;
|
|
gboolean has_tooltip = FALSE;
|
|
gboolean sensitive = FALSE;
|
|
const gchar *sensitive_reason = NULL;
|
|
gchar *escaped_reason = NULL;
|
|
|
|
label = g_strstrip (pika_strip_uline (pika_action_get_label (action)));
|
|
|
|
if (! label || strlen (label) == 0)
|
|
{
|
|
g_free (label);
|
|
return;
|
|
}
|
|
|
|
escaped_label = g_markup_escape_text (label, -1);
|
|
|
|
if (PIKA_IS_TOGGLE_ACTION (action))
|
|
{
|
|
if (pika_toggle_action_get_active (PIKA_TOGGLE_ACTION (action)))
|
|
icon_name = "gtk-ok";
|
|
else
|
|
icon_name = "gtk-no";
|
|
}
|
|
else
|
|
{
|
|
icon_name = pika_action_get_icon_name (action);
|
|
}
|
|
|
|
accels = pika_action_get_display_accels (action);
|
|
if (accels && accels[0])
|
|
{
|
|
gchar *formatted_label;
|
|
gchar *formatted_value;
|
|
|
|
/* TRANSLATORS: a helper label indicating the shortcut for an action,
|
|
* it will be used within the generic "%s: %s" (also localized) string.
|
|
* e.g.: "shortcut: Ctrl+Alt+O"
|
|
*/
|
|
formatted_label = g_markup_printf_escaped ("<u>%s</u>", _("shortcut"));
|
|
formatted_value = g_markup_escape_text (accels[0], -1);
|
|
|
|
/* TRANSLATORS: generic "title: value" label which will be used in various ways. */
|
|
shortcut_helper = g_strdup_printf (_("%s: %s"), formatted_label, formatted_value);
|
|
|
|
g_free (formatted_label);
|
|
g_free (formatted_value);
|
|
}
|
|
g_strfreev (accels);
|
|
|
|
if ((menu_path = pika_action_get_menu_path (action)) != NULL)
|
|
{
|
|
if (strlen (menu_path) > 0)
|
|
{
|
|
gchar *formatted_label;
|
|
gchar *formatted_value;
|
|
|
|
/* TRANSLATORS: a helper label indicating the menu path for an action,
|
|
* it will be used within the generic "%s: %s" (also localized) string.
|
|
* e.g.: "Menu: Filters > Generic"
|
|
*/
|
|
formatted_label = g_markup_printf_escaped ("<u>%s</u>", _("menu"));
|
|
formatted_value = g_markup_escape_text (menu_path, -1);
|
|
|
|
/* TRANSLATORS: generic "title: value" label which will be used in various ways. */
|
|
menu_path_helper = g_strdup_printf (_("%s: %s"), formatted_label, formatted_value);
|
|
|
|
g_free (formatted_label);
|
|
g_free (formatted_value);
|
|
}
|
|
}
|
|
|
|
tooltip = pika_action_get_tooltip (action);
|
|
if (tooltip != NULL)
|
|
{
|
|
escaped_tooltip = g_markup_escape_text (tooltip, -1);
|
|
has_tooltip = TRUE;
|
|
}
|
|
|
|
sensitive = pika_action_is_sensitive (action, &sensitive_reason);
|
|
if (sensitive_reason != NULL)
|
|
{
|
|
escaped_reason = g_markup_escape_text (sensitive_reason, -1);
|
|
}
|
|
|
|
markup = g_strdup_printf ("%s" /* Label */
|
|
"<small>%s%s" /* Shortcut */
|
|
"%s%s" /* Menu path */
|
|
"%s<span weight='light'>%s</span>" /* Tooltip */
|
|
"%s<i><span weight='ultralight'>%s</span></i>" /* Inactive reason */
|
|
"</small>",
|
|
escaped_label,
|
|
|
|
shortcut_helper ? " | " : "",
|
|
shortcut_helper ? shortcut_helper : "",
|
|
|
|
menu_path_helper ? " | " : "",
|
|
menu_path_helper ? menu_path_helper : "",
|
|
|
|
has_tooltip ? "\n" : "",
|
|
has_tooltip ? escaped_tooltip : "",
|
|
escaped_reason ? "\n" : "",
|
|
escaped_reason ? escaped_reason : "");
|
|
|
|
action_name = g_markup_escape_text (pika_action_get_name (action), -1);
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (popup->priv->results_list));
|
|
store = GTK_LIST_STORE (model);
|
|
if (gtk_tree_model_get_iter_first (model, &next_section))
|
|
{
|
|
while (TRUE)
|
|
{
|
|
gint iter_section;
|
|
|
|
gtk_tree_model_get (model, &next_section,
|
|
COLUMN_SECTION, &iter_section, -1);
|
|
if (iter_section > section)
|
|
{
|
|
gtk_list_store_insert_before (store, &iter, &next_section);
|
|
break;
|
|
}
|
|
else if (! gtk_tree_model_iter_next (model, &next_section))
|
|
{
|
|
gtk_list_store_append (store, &iter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gtk_list_store_append (store, &iter);
|
|
}
|
|
|
|
gtk_list_store_set (store, &iter,
|
|
COLUMN_ICON, icon_name,
|
|
COLUMN_MARKUP, markup,
|
|
COLUMN_TOOLTIP, action_name,
|
|
COLUMN_ACTION, action,
|
|
COLUMN_SECTION, section,
|
|
COLUMN_SENSITIVE, sensitive,
|
|
-1);
|
|
|
|
g_free (markup);
|
|
g_free (action_name);
|
|
g_free (label);
|
|
g_free (escaped_label);
|
|
g_free (escaped_tooltip);
|
|
g_free (escaped_reason);
|
|
g_free (menu_path_helper);
|
|
g_free (shortcut_helper);
|
|
}
|
|
|
|
/************ Private Functions ****************/
|
|
|
|
static void
|
|
pika_search_popup_constructed (GObject *object)
|
|
{
|
|
PikaSearchPopup *popup = PIKA_SEARCH_POPUP (object);
|
|
GtkWidget *main_vbox;
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
|
gtk_container_add (GTK_CONTAINER (popup), main_vbox);
|
|
gtk_widget_show (main_vbox);
|
|
|
|
popup->priv->keyword_entry = gtk_entry_new ();
|
|
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (popup->priv->keyword_entry),
|
|
GTK_ENTRY_ICON_PRIMARY, "edit-find");
|
|
gtk_entry_set_icon_activatable (GTK_ENTRY (popup->priv->keyword_entry),
|
|
GTK_ENTRY_ICON_PRIMARY, FALSE);
|
|
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (popup->priv->keyword_entry),
|
|
GTK_ENTRY_ICON_SECONDARY, PIKA_ICON_HELP);
|
|
gtk_entry_set_icon_activatable (GTK_ENTRY (popup->priv->keyword_entry),
|
|
GTK_ENTRY_ICON_SECONDARY, TRUE);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox),
|
|
popup->priv->keyword_entry,
|
|
FALSE, FALSE, 0);
|
|
gtk_widget_show (popup->priv->keyword_entry);
|
|
|
|
pika_search_popup_setup_results (&popup->priv->results_list,
|
|
&popup->priv->list_view);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox),
|
|
popup->priv->list_view, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_set_events (GTK_WIDGET (object),
|
|
GDK_KEY_RELEASE_MASK |
|
|
GDK_KEY_PRESS_MASK |
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_SCROLL_MASK |
|
|
GDK_SMOOTH_SCROLL_MASK);
|
|
|
|
g_signal_connect (popup->priv->keyword_entry, "icon-press",
|
|
G_CALLBACK (keyword_entry_icon_press),
|
|
popup);
|
|
g_signal_connect (popup->priv->keyword_entry, "key-press-event",
|
|
G_CALLBACK (keyword_entry_key_press_event),
|
|
popup);
|
|
g_signal_connect (popup->priv->keyword_entry, "key-release-event",
|
|
G_CALLBACK (keyword_entry_key_release_event),
|
|
popup);
|
|
|
|
g_signal_connect (popup->priv->results_list, "key-press-event",
|
|
G_CALLBACK (results_list_key_press_event),
|
|
popup);
|
|
g_signal_connect (popup->priv->results_list, "row-activated",
|
|
G_CALLBACK (results_list_row_activated),
|
|
popup);
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaSearchPopup *search_popup = PIKA_SEARCH_POPUP (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_PIKA:
|
|
search_popup->priv->pika = g_value_get_object (value);
|
|
break;
|
|
case PROP_CALLBACK:
|
|
search_popup->priv->build_results = g_value_get_pointer (value);
|
|
break;
|
|
case PROP_CALLBACK_DATA:
|
|
search_popup->priv->build_results_data = g_value_get_pointer (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaSearchPopup *search_popup = PIKA_SEARCH_POPUP (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_PIKA:
|
|
g_value_set_object (value, search_popup->priv->pika);
|
|
break;
|
|
case PROP_CALLBACK:
|
|
g_value_set_pointer (value, search_popup->priv->build_results);
|
|
break;
|
|
case PROP_CALLBACK_DATA:
|
|
g_value_set_pointer (value, search_popup->priv->build_results_data);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
PikaSearchPopup *popup = PIKA_SEARCH_POPUP (widget);
|
|
GdkRectangle workarea;
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
|
|
|
|
gdk_monitor_get_workarea (pika_widget_get_monitor (widget),
|
|
&workarea);
|
|
|
|
if (window_height == 0)
|
|
{
|
|
/* Default to half the monitor */
|
|
window_height = workarea.height / 2;
|
|
}
|
|
|
|
if (gtk_widget_get_visible (widget) &&
|
|
gtk_widget_get_visible (popup->priv->list_view))
|
|
{
|
|
/* Save the window height when results are shown so that resizes
|
|
* by the user are saved across searches.
|
|
*/
|
|
window_height = MAX (workarea.height / 4, allocation->height);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_confirm (PikaPopup *popup)
|
|
{
|
|
PikaSearchPopup *search_popup = PIKA_SEARCH_POPUP (popup);
|
|
|
|
pika_search_popup_run_selected (search_popup);
|
|
}
|
|
|
|
static void
|
|
keyword_entry_icon_press (GtkEntry *entry,
|
|
GtkEntryIconPosition icon_pos,
|
|
GdkEvent *event,
|
|
PikaSearchPopup *popup)
|
|
{
|
|
if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
|
|
pika_search_popup_help (popup);
|
|
}
|
|
|
|
static gboolean
|
|
keyword_entry_key_press_event (GtkWidget *widget,
|
|
GdkEventKey *event,
|
|
PikaSearchPopup *popup)
|
|
{
|
|
gboolean event_processed = FALSE;
|
|
|
|
if (event->keyval == GDK_KEY_Down &&
|
|
gtk_widget_get_visible (popup->priv->list_view))
|
|
{
|
|
GtkTreeView *tree_view = GTK_TREE_VIEW (popup->priv->results_list);
|
|
GtkTreePath *path;
|
|
|
|
/* When hitting the down key while editing, select directly the
|
|
* second item, since the first could have run directly with
|
|
* Enter. */
|
|
path = gtk_tree_path_new_from_string ("1");
|
|
gtk_tree_selection_select_path (gtk_tree_view_get_selection (tree_view), path);
|
|
gtk_tree_path_free (path);
|
|
|
|
gtk_widget_grab_focus (GTK_WIDGET (popup->priv->results_list));
|
|
event_processed = TRUE;
|
|
}
|
|
|
|
return event_processed;
|
|
}
|
|
|
|
static gboolean
|
|
keyword_entry_key_release_event (GtkWidget *widget,
|
|
GdkEventKey *event,
|
|
PikaSearchPopup *popup)
|
|
{
|
|
GtkTreeView *tree_view = GTK_TREE_VIEW (popup->priv->results_list);
|
|
GtkTreePath *path;
|
|
gchar *entry_text;
|
|
gint width;
|
|
|
|
/* These keys are already managed by key bindings. */
|
|
if (event->keyval == GDK_KEY_Escape ||
|
|
event->keyval == GDK_KEY_Return ||
|
|
event->keyval == GDK_KEY_KP_Enter ||
|
|
event->keyval == GDK_KEY_ISO_Enter)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gtk_window_get_size (GTK_WINDOW (popup), &width, NULL);
|
|
entry_text = g_strstrip (gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1));
|
|
|
|
if (event->keyval == GDK_KEY_F1)
|
|
{
|
|
pika_search_popup_help (popup);
|
|
}
|
|
else if (strcmp (entry_text, "") != 0)
|
|
{
|
|
path = gtk_tree_path_new_from_string ("0");
|
|
gtk_window_resize (GTK_WINDOW (popup),
|
|
width, window_height);
|
|
gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (tree_view)));
|
|
gtk_widget_show_all (popup->priv->list_view);
|
|
popup->priv->build_results (popup, entry_text,
|
|
popup->priv->build_results_data);
|
|
gtk_tree_selection_select_path (gtk_tree_view_get_selection (tree_view), path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else if (strcmp (entry_text, "") == 0 && (event->keyval == GDK_KEY_Down))
|
|
{
|
|
path = gtk_tree_path_new_from_string ("0");
|
|
gtk_window_resize (GTK_WINDOW (popup),
|
|
width, window_height);
|
|
gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (tree_view)));
|
|
gtk_widget_show_all (popup->priv->list_view);
|
|
popup->priv->build_results (popup, NULL,
|
|
popup->priv->build_results_data);
|
|
gtk_tree_selection_select_path (gtk_tree_view_get_selection (tree_view), path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else
|
|
{
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
path = gtk_tree_model_get_path (model, &iter);
|
|
gtk_tree_selection_unselect_path (selection, path);
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
gtk_widget_hide (popup->priv->list_view);
|
|
gtk_window_resize (GTK_WINDOW (popup), width, 1);
|
|
}
|
|
|
|
g_free (entry_text);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
results_list_key_press_event (GtkWidget *widget,
|
|
GdkEventKey *kevent,
|
|
PikaSearchPopup *popup)
|
|
{
|
|
/* These keys are already managed by key bindings. */
|
|
g_return_val_if_fail (kevent->keyval != GDK_KEY_Escape &&
|
|
kevent->keyval != GDK_KEY_Return &&
|
|
kevent->keyval != GDK_KEY_KP_Enter &&
|
|
kevent->keyval != GDK_KEY_ISO_Enter,
|
|
FALSE);
|
|
|
|
switch (kevent->keyval)
|
|
{
|
|
case GDK_KEY_Up:
|
|
{
|
|
gboolean event_processed = FALSE;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (popup->priv->results_list));
|
|
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
|
|
gchar *path_str;
|
|
|
|
path_str = gtk_tree_path_to_string (path);
|
|
if (strcmp (path_str, "0") == 0)
|
|
{
|
|
gint start_pos;
|
|
gint end_pos;
|
|
|
|
gtk_editable_get_selection_bounds (GTK_EDITABLE (popup->priv->keyword_entry),
|
|
&start_pos, &end_pos);
|
|
gtk_widget_grab_focus ((GTK_WIDGET (popup->priv->keyword_entry)));
|
|
gtk_editable_select_region (GTK_EDITABLE (popup->priv->keyword_entry),
|
|
start_pos, end_pos);
|
|
|
|
event_processed = TRUE;
|
|
}
|
|
|
|
g_free (path_str);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
return event_processed;
|
|
}
|
|
case GDK_KEY_Down:
|
|
{
|
|
return FALSE;
|
|
}
|
|
default:
|
|
{
|
|
gint start_pos;
|
|
gint end_pos;
|
|
|
|
gtk_editable_get_selection_bounds (GTK_EDITABLE (popup->priv->keyword_entry),
|
|
&start_pos, &end_pos);
|
|
gtk_widget_grab_focus ((GTK_WIDGET (popup->priv->keyword_entry)));
|
|
gtk_editable_select_region (GTK_EDITABLE (popup->priv->keyword_entry),
|
|
start_pos, end_pos);
|
|
gtk_widget_event (GTK_WIDGET (popup->priv->keyword_entry),
|
|
(GdkEvent *) kevent);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
results_list_row_activated (GtkTreeView *treeview,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *col,
|
|
PikaSearchPopup *popup)
|
|
{
|
|
pika_search_popup_run_selected (popup);
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_run_selected (PikaSearchPopup *popup)
|
|
{
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (popup->priv->results_list));
|
|
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
PikaAction *action;
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
|
|
|
|
if (pika_action_is_sensitive (action, NULL))
|
|
{
|
|
/* Close the search popup on activation. */
|
|
PIKA_POPUP_CLASS (parent_class)->cancel (PIKA_POPUP (popup));
|
|
|
|
pika_action_activate (action);
|
|
}
|
|
|
|
g_object_unref (action);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_setup_results (GtkWidget **results_list,
|
|
GtkWidget **list_view)
|
|
{
|
|
gint wid1 = 100;
|
|
GtkListStore *store;
|
|
GtkCellRenderer *cell;
|
|
GtkTreeViewColumn *column;
|
|
|
|
*list_view = gtk_scrolled_window_new (NULL, NULL);
|
|
store = gtk_list_store_new (N_COL,
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING,
|
|
G_TYPE_STRING,
|
|
PIKA_TYPE_ACTION,
|
|
G_TYPE_BOOLEAN,
|
|
G_TYPE_INT);
|
|
*results_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
|
|
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (*results_list), FALSE);
|
|
#ifdef PIKA_UNSTABLE
|
|
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (*results_list),
|
|
COLUMN_TOOLTIP);
|
|
#endif
|
|
|
|
cell = gtk_cell_renderer_pixbuf_new ();
|
|
column = gtk_tree_view_column_new_with_attributes (NULL, cell,
|
|
"icon-name", COLUMN_ICON,
|
|
"sensitive", COLUMN_SENSITIVE,
|
|
NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (*results_list), column);
|
|
gtk_tree_view_column_set_min_width (column, 22);
|
|
|
|
cell = gtk_cell_renderer_text_new ();
|
|
column = gtk_tree_view_column_new_with_attributes (NULL, cell,
|
|
"markup", COLUMN_MARKUP,
|
|
"sensitive", COLUMN_SENSITIVE,
|
|
NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (*results_list), column);
|
|
gtk_tree_view_column_set_max_width (column, wid1);
|
|
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (*list_view),
|
|
GTK_POLICY_NEVER,
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
gtk_container_add (GTK_CONTAINER (*list_view), *results_list);
|
|
g_object_unref (G_OBJECT (store));
|
|
}
|
|
|
|
static void
|
|
pika_search_popup_help (PikaSearchPopup *popup)
|
|
{
|
|
GtkTreeView *tree_view;
|
|
const gchar *help_id = NULL;
|
|
PikaAction *action = NULL;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
tree_view = GTK_TREE_VIEW (popup->priv->results_list);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
|
|
help_id = pika_action_get_help_id (action);
|
|
}
|
|
|
|
if (help_id == NULL)
|
|
help_id = PIKA_HELP_ACTION_SEARCH_DIALOG;
|
|
|
|
pika_help (popup->priv->pika, NULL, NULL, help_id);
|
|
g_clear_object (&action);
|
|
}
|