47#include <glib/gi18n.h>
92#ifdef GDK_WINDOWING_QUARTZ
100#define DT_ACCEL_SEARCH_INLINE_SEPARATOR " > "
101#define DT_ACCEL_SEARCH_DISPATCH_RETRY_DELAY_MS 50
130 if(pc->
base) g_closure_unref(pc->
base);
133 if(pc->
widget) g_object_remove_weak_pointer(G_OBJECT(pc->
widget), (gpointer *)&pc->
widget);
142 gtk_widget_set_has_tooltip(widget,
TRUE);
146 const GValue *param_values, gpointer data)
150 if(n_param_values < 5)
return TRUE;
152 GtkWidget *widget = g_value_get_object(¶m_values[0]);
155 if(!gtk_widget_get_has_tooltip(widget))
return TRUE;
158 const char *base_markup = g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-markup");
159 const char *base_text = base_markup ? NULL : g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-text");
160 const gboolean base_none = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-none"));
164 gchar *current_markup = gtk_widget_get_tooltip_markup(widget);
165 if(current_markup && current_markup[0])
167 g_object_set_data_full(G_OBJECT(widget),
"dt-accel-tooltip-base-markup", current_markup, g_free);
168 base_markup = current_markup;
173 gchar *current_text = gtk_widget_get_tooltip_text(widget);
174 if(current_text && current_text[0])
176 g_object_set_data_full(G_OBJECT(widget),
"dt-accel-tooltip-base-text", current_text, g_free);
177 base_text = current_text;
182 g_object_set_data(G_OBJECT(widget),
"dt-accel-tooltip-base-none", GINT_TO_POINTER(1));
190 const char *accel_path = g_object_get_data(G_OBJECT(widget),
"accel-path");
203 gtk_widget_set_tooltip_markup(widget, base_markup);
205 gtk_widget_set_tooltip_text(widget, base_text);
210 if(
IS_NULL_PTR(shortcut_label) || !shortcut_label[0])
217 if(base_markup && base_markup[0])
219 gchar *esc_label = g_markup_escape_text(shortcut_label, -1);
220 gchar *esc_desc = g_markup_escape_text(shortcut_desc, -1);
221 gchar *new_markup = g_strdup_printf(
"%s\n<small>%s: %s</small>", base_markup, esc_desc, esc_label);
222 gtk_widget_set_tooltip_markup(widget, new_markup);
227 else if(base_text && base_text[0])
229 gchar *new_text = g_strdup_printf(
"%s\n%s: %s", base_text, shortcut_desc, shortcut_label);
230 gtk_widget_set_tooltip_text(widget, new_text);
235 gchar *new_text = g_strdup_printf(
"%s: %s", shortcut_desc, shortcut_label);
236 gtk_widget_set_tooltip_text(widget, new_text);
247 static gulong hook_id = 0;
248 if(hook_id != 0)
return;
250 const guint signal_id = g_signal_lookup(
"query-tooltip", GTK_TYPE_WIDGET);
251 if(signal_id == 0)
return;
270 GList *link = g_list_last(shortcut->
closure);
301 for(link = g_list_first(shortcut->
closure); link; link = g_list_next(link))
304 if(closure->base->data == data || closure->parent_data == data)
313 link = g_list_last(shortcut->
closure);
341 gchar **child_parts = g_strsplit(child_shortcut->
path,
"/", -1);
342 guint
n = g_strv_length(child_parts);
344 gchar *parent_path = g_strjoinv (
"/", child_parts);
345 g_strfreev(child_parts);
348 if(!g_strcmp0(parent_shortcut->
path, parent_path))
352 if(parent_closure && child_closure)
377 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
378 guint keyval, GdkModifierType mods, gpointer user_data),
382 pc->
base = g_cclosure_new(G_CALLBACK(action_callback), data, NULL);
388 if(pc->
widget) g_object_add_weak_pointer(G_OBJECT(pc->
widget), (gpointer *)&pc->
widget);
390 g_closure_set_marshal(pc->
base, g_cclosure_marshal_generic);
391 g_closure_ref(pc->
base);
392 g_closure_sink(pc->
base);
412 accels->
keymap = gdk_keymap_get_for_display(gdk_display_get_default());
472 else if(!g_strcmp0(group,
"map") && accels->
map_accels)
477 else if(!g_strcmp0(group,
"print") && accels->
print_accels)
489 fprintf(stderr,
"[dt_accels_connect_active_group] INFO: unknown value: `%s'\n", group);
516 gboolean changed =
FALSE;
527 key->accel_key = shortcut->
key;
528 key->accel_mods = shortcut->
mods;
529 gtk_accel_map_change_entry(shortcut->
path, shortcut->
key, shortcut->
mods,
TRUE);
532 else if(
key->accel_key == shortcut->
key &&
key->accel_mods == shortcut->
mods)
544 shortcut->
key =
key->accel_key;
545 shortcut->
mods =
key->accel_mods;
552 else if(shortcut->
locked && (
key->accel_key != shortcut->
key ||
key->accel_mods != shortcut->
mods))
555 key->accel_key = shortcut->
key;
556 key->accel_mods = shortcut->
mods;
557 gtk_accel_map_change_entry(shortcut->
path, shortcut->
key, shortcut->
mods,
TRUE);
561 else if(
key->accel_key != shortcut->
key ||
key->accel_mods != shortcut->
mods)
563 shortcut->
key =
key->accel_key;
564 shortcut->
mods =
key->accel_mods;
584 if(shortcut->
key != alt_char)
592 gtk_widget_remove_accelerator(shortcut->
widget, shortcut->
accel_group, old_key->accel_key, old_key->accel_mods);
596 if(old_key->accel_key != alt_char)
597 gtk_widget_remove_accelerator(shortcut->
widget, shortcut->
accel_group, alt_char, old_key->accel_mods);
608 gtk_accel_group_disconnect(shortcut->
accel_group, cl);
617 gtk_accel_group_connect(shortcut->
accel_group, shortcut->
key, shortcut->
mods,
flags | GTK_ACCEL_VISIBLE, closure);
651 gtk_accel_map_add_entry(shortcut->
path, shortcut->
key, shortcut->
mods);
674 GdkModifierType mods, gpointer user_data)
678 g_signal_emit_by_name(shortcut->
widget, shortcut->
signal);
684 GdkModifierType mods, gpointer user_data)
690 gtk_widget_grab_focus(shortcut->
widget);
694 GdkKeymapKey *keys = NULL;
696 GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
697 if(gdk_keymap_get_entries_for_keyval(keymap, shortcut->
key, &keys, &
n))
699 if(
n > 0) keycode = keys[0].keycode;
704 GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS);
705 ev->key.window = g_object_ref(gtk_widget_get_window(shortcut->
widget));
706 ev->key.send_event =
TRUE;
707 ev->key.time = GDK_CURRENT_TIME;
708 ev->key.state = shortcut->
mods;
709 ev->key.keyval = shortcut->
key;
710 ev->key.hardware_keycode = keycode;
712 ev->key.is_modifier =
FALSE;
715 gtk_widget_event(shortcut->
widget, ev);
723 GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
730 if(shortcut && shortcut->
widget == widget)
740 shortcut->
widget = widget;
742 shortcut->
path = g_strdup(accel_path);
744 shortcut->
key = key_val;
745 shortcut->
mods = accel_mods;
749 shortcut->
description = _(
"Contextual interaction on focus");
750 shortcut->
accels = accels;
758 gboolean (*action_callback)(GtkAccelGroup *group,
759 GObject *acceleratable, guint keyval,
760 GdkModifierType mods, gpointer user_data),
761 gpointer data, GtkAccelGroup *accel_group,
const gchar *action_scope,
762 const gchar *action_name)
777 shortcut->
path = g_strdup(accel_path);
785 shortcut->
accels = accels;
798 GtkAccelGroup *accel_group,
const gchar *accel_path, guint key_val,
799 GdkModifierType accel_mods,
const gboolean
lock)
806 if(shortcut && shortcut->
widget == widget)
815 GtkAccelKey
key = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
817 shortcut->
widget = widget;
826 shortcut->
widget = widget;
828 shortcut->
path = g_strdup(accel_path);
829 shortcut->
signal = signal;
830 shortcut->
key = key_val;
831 shortcut->
mods = accel_mods;
836 shortcut->
accels = accels;
854 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
855 guint keyval, GdkModifierType mods,
857 gpointer data,
GtkWidget *target_widget, GtkAccelGroup *accel_group,
858 const gchar *action_scope,
const gchar *action_name, guint key_val,
859 GdkModifierType accel_mods,
const gboolean
lock,
const char *
description)
870 if(closure && closure->data == data)
891 shortcut->
path = g_strdup(accel_path);
893 shortcut->
key = key_val;
894 shortcut->
mods = accel_mods;
899 shortcut->
accels = accels;
919 GtkAccelKey
key = { 0 };
922 const gboolean is_known = gtk_accel_map_lookup_entry(shortcut->
path, &
key);
923 if(!is_known)
return;
926 const GtkAccelKey oldkey = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
934 const gboolean needs_cleanup = changed && oldkey.accel_key > 0 && oldtype !=
DT_SHORTCUT_UNSET;
937 const gboolean needs_init = changed &&
key.accel_key > 0;
975 if(g_strrstr(shortcut->
path,
params->path) != NULL)
1028 gchar *level = g_strdelimit(g_strdup(feature),
"/",
'-');
1029 gchar *path = (strncmp(scope,
"<Ansel>/", strlen(
"<Ansel>/")) == 0)
1030 ? g_strdup_printf(
"%s/%s", scope, level)
1031 : g_strdup_printf(
"<Ansel>/%s/%s", scope, level);
1041 gdk_event_get_state(event, mods);
1046#ifdef GDK_WINDOWING_QUARTZ
1055 if((*mods & GDK_MOD2_MASK) && (*mods & GDK_META_MASK))
1056 *mods &= ~GDK_META_MASK;
1060 GdkModifierType consumed;
1061 gdk_keymap_translate_keyboard_state(accels->
keymap, event->key.hardware_keycode, event->key.state,
1063 keyval, NULL, NULL, &consumed);
1067 gchar *accel_name = gtk_accelerator_name(*keyval, *mods);
1069 (event->type == GDK_KEY_PRESS) ?
"Key pressed" :
"Key released", accel_name);
1076 if(gdk_keyval_to_lower(*keyval) == gdk_keyval_to_upper(*keyval))
1084 if(*keyval == GDK_KEY_ISO_Left_Tab)
1086 *keyval = GDK_KEY_Tab;
1087 *mods |= GDK_SHIFT_MASK;
1107 return gdk_keyval_to_lower(keyval);
1114 const gchar *path = (
const gchar *)
key;
1123 && shortcut_key == result_key
1126 if(!g_strcmp0(path, shortcut->
path))
1129 dt_widget_log(
"[shortcuts] Found accel %s for typed keys\n", path);
1133 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1150 GList *item = g_list_first(result.
results);
1151 if(item) path = (
char *)item->data;
1161 const gchar *path = (
const gchar *)
key;
1170 && shortcut_key == result_key
1174 if(!g_strcmp0(path, shortcut->
path))
1177 dt_widget_log(
"[shortcuts] Found accel %s for typed keys\n", path);
1181 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1187 GdkModifierType mods)
1196 GList *item = g_list_first(result.
results);
1209 gchar *accel_name = gtk_accelerator_name(keyval, mods);
1210 dt_widget_log(
"[shortcuts] Combination of keys decoded: %s\n", accel_name);
1217 dt_widget_log(
"[shortcuts] Active group action executed\n");
1225 dt_widget_log(
"[shortcuts] Global group action executed\n");
1241 if(event->type == GDK_FOCUS_CHANGE)
1243 dt_widget_log(
"[shortcuts] Focus %s, held key forgotten\n", event->focus_change.in ?
"in" :
"out");
1254 if(!(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE || event->type == GDK_SCROLL))
1258 if(event->type == GDK_SCROLL)
1267 GdkModifierType mods;
1285 if(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
1287 GtkWidget *focused = gtk_window_get_focus(GTK_WINDOW(w));
1288 if(!
IS_NULL_PTR(focused) && (GTK_IS_EDITABLE(focused) || GTK_IS_TEXT_VIEW(focused)))
1296 if(event->type == GDK_KEY_PRESS &&
1304 else if(event->type == GDK_KEY_RELEASE)
1361 GtkTreeIter *iter, gpointer data)
1365 g_object_set(renderer,
1368 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
1373 GtkTreeIter *iter, gpointer data)
1379 g_object_set (renderer,
1380 "icon-name", (!
IS_NULL_PTR(shortcut) && !shortcut->
locked) ?
"edit-delete-symbolic"
1381 :
"changes-prevent-symbolic",
1394 if(!gdk_keymap_get_entries_for_keycode(accels->
keymap, hardware_keycode, &keys, &keyvals, &n_keys))
1397 for(
int i = 0;
i < n_keys; ++
i)
1399 if(keyvals[
i] == keyval)
1401 int group = keys[
i].group;
1414 guint hardware_key, gpointer user_data)
1418 GtkTreeModel *filter = GTK_TREE_MODEL(user_data);
1419 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
1422 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1428 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(filter), &f_iter, path))
1429 gtk_tree_model_get(GTK_TREE_MODEL(filter), &f_iter,
COL_SHORTCUT, &shortcut, -1);
1431 const char *shortcut_path = NULL;
1437 if(keyval == GDK_KEY_VoidSymbol
1438 || (mods == 0 && (keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace)))
1447 if(keyval != 0 || mods != 0)
1449 GdkDisplay *display = gdk_display_get_default();
1450 GdkSeat *seat = gdk_display_get_default_seat(display);
1451 GdkDevice *pointer = gdk_seat_get_pointer(seat);
1452 GdkModifierType
state;
1453 gdk_device_get_state(pointer, gdk_get_default_root_window(), NULL, &
state);
1457 GdkEventKey
event = { 0 };
1458 event.type = GDK_KEY_PRESS;
1459 event.state = mods |
state;
1460 event.keyval = keyval;
1461 event.hardware_keycode = hardware_key;
1469 if(!(keyval == 0 && mods == 0))
1473 if(
IS_NULL_PTR(shortcut_path) && gtk_accel_map_change_entry(shortcut->
path, keyval, mods,
FALSE))
1483 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter), &s_iter, &f_iter);
1497 = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
"%s <tt>%s</tt>\n%s <tt>%s</tt>.\n%s",
1498 _(
"The shortcut for"), shortcut_path,
1499 _(
"is already using the key combination"), new_text,
1500 _(
"Delete it first."));
1501 gtk_dialog_run(GTK_DIALOG(dlg));
1502 gtk_widget_destroy(dlg);
1506 gtk_tree_path_free(path);
1509static void _shortcut_cleared(GtkCellRendererAccel *renderer,
const gchar *path_string, gpointer user_data)
1511 _shortcut_edited(GTK_CELL_RENDERER(renderer), path_string, 0, 0, 0, user_data);
1516 GdkRectangle *background, GdkRectangle *cell_area, GtkCellRendererState
flags,
1528 gtk_tree_store_set(
store, iter,
1543 const gchar *path = (
const gchar *)
key;
1550 GtkTreeIter *parent = NULL;
1551 GtkTreeIter *iter = NULL;
1558 gchar **parts = g_strsplit(path,
"/", -1);
1559 gchar *accum = g_strdup(
"<Ansel>");
1563 const size_t len_ansel = strlen(accum);
1564 for(
int i = 1; parts[
i]; ++
i)
1567 gchar *tmp = g_strconcat(accum,
"/", parts[
i], NULL);
1573 iter = g_hash_table_lookup(node_cache, accum);
1580 GtkTreeIter new_iter;
1581 gtk_tree_store_append(
store, &new_iter, parent);
1584 iter = g_new(GtkTreeIter, 1);
1586 g_hash_table_insert(node_cache, g_strdup(accum), iter);
1590 gchar *label = g_strdup(parts[
i]);
1594 if(!g_strcmp0(accum, path))
1612 gchar **parts = g_strsplit(path,
"/", -1);
1613 const gint len = g_strv_length(parts);
1616 tail = g_strjoinv(
"/", parts + 2);
1618 tail = g_strdup(parts[1]);
1620 tail = g_strdup(parts[0]);
1630 const gchar *path = (
const gchar *)
key;
1632 GtkListStore *
store = (GtkListStore *)user_data;
1644 gchar **tail_parts = g_strsplit(tail,
"/", -1);
1645 const gint tail_len = g_strv_length(tail_parts);
1646 const gchar *leaf = tail;
1647 if(tail_len > 0 && !
IS_NULL_PTR(tail_parts[tail_len - 1]) && tail_parts[tail_len - 1][0] !=
'\0')
1648 leaf = tail_parts[tail_len - 1];
1651 gtk_list_store_append(
store, &iter);
1652 gtk_list_store_set(
store, &iter,
1661 g_strfreev(tail_parts);
1670 gchar *pa = NULL, *pb = NULL;
1671 gtk_tree_model_get(
model, a, 2, &ka, 0, &pa, -1);
1672 gtk_tree_model_get(
model, b, 2, &kb, 0, &pb, -1);
1684 gchar *pa_ci = g_utf8_casefold(pa, -1);
1685 gchar *pb_ci = g_utf8_casefold(pb, -1);
1686 gchar **pa_parts = g_strsplit(pa_ci,
"/", -1);
1687 gchar **pb_parts = g_strsplit(pb_ci,
"/", -1);
1689 for(gint
i = 0;;
i++)
1691 const gchar *pa_part = pa_parts[
i];
1692 const gchar *pb_part = pb_parts[
i];
1709 ret = g_utf8_collate(pa_part, pb_part);
1713 g_strfreev(pb_parts);
1714 g_strfreev(pa_parts);
1727 gtk_tree_model_get(
model, a, GPOINTER_TO_INT(data), &ka, -1);
1728 gtk_tree_model_get(
model, b, GPOINTER_TO_INT(data), &kb, -1);
1734 gchar *ka_ci = g_utf8_casefold(ka, -1);
1735 gchar *kb_ci = g_utf8_casefold(kb, -1);
1738 res = g_utf8_collate(ka_ci, kb_ci);
1762 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(
params->path_search));
1763 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(
params->keys_search));
1765 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1766 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1769 gboolean show =
TRUE;
1774 if(needle_path && needle_path[0])
1776 if(path && path[0] !=
'\0')
1778 gchar *needle_ci = g_utf8_casefold(needle_path, -1);
1779 gchar *haystack_ci = g_utf8_casefold(path, -1);
1780 show &= (g_strrstr(haystack_ci, needle_ci) != NULL);
1792 if(needle_keys && needle_keys[0] !=
'\0')
1794 guint search_keyval = 0;
1795 GdkModifierType search_mods = 0;
1796 gtk_accelerator_parse(needle_keys, &search_keyval, &search_mods);
1797 if(search_keyval || search_mods)
1800 GdkModifierType mods = 0;
1807 if(search_keyval && search_mods)
1808 show &= (keyval == search_keyval && mods == search_mods);
1810 show &= ((keyval && keyval == search_keyval) || (mods && mods == search_mods));
1819 if(show)
return TRUE;
1822 if(gtk_tree_model_iter_has_child(
model, iter))
1825 if(gtk_tree_model_iter_children(
model, &child, iter))
1831 }
while(gtk_tree_model_iter_next(
model, &child));
1841 GtkTreeView *tree_view = GTK_TREE_VIEW(
params->tree_view);
1842 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(tree_view)));
1845 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(
params->path_search));
1846 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(
params->keys_search));
1848 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1849 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1850 gtk_tree_view_collapse_all(GTK_TREE_VIEW(
params->tree_view));
1852 gtk_tree_view_expand_all(GTK_TREE_VIEW(
params->tree_view));
1862 params->keys_search = gtk_search_entry_new();
1863 params->path_search = gtk_search_entry_new();
1870 = { {
"Primary>", N_(
"<Primary> - Decoded as <Control> on Windows/Linux or <Meta> on Mac OS") },
1871 {
"Control>", N_(
"<Control>") },
1872 {
"Shift>", N_(
"<Shift>") },
1873 {
"Alt>", N_(
"<Alt>") },
1874 {
"Super>", N_(
"<Super> - The Windows key on PC") },
1875 {
"Hyper>", N_(
"<Hyper>") },
1876 {
"Meta>", N_(
"<Meta> - Decoded as <Command> on Mac OS") },
1879 gtk_widget_set_tooltip_text(
params->keys_search, _(
"Look for keys and modifiers codes, as `<Modifier>Key`.\n"
1880 "Type `<` to start the auto-completion"));
1882 gtk_widget_set_tooltip_text(
params->path_search, _(
"Case-insensitive search for keywords of full pathes.\n"
1883 "Ex: `darkroom/controls/sliders`"));
1887 gtk_window_set_title(GTK_WINDOW(dialog), _(
"Ansel - Keyboard shortcuts"));
1889#ifdef GDK_WINDOWING_QUARTZ
1891 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1894 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1895 gtk_window_set_modal(GTK_WINDOW(dialog),
TRUE);
1896 gtk_window_set_transient_for(GTK_WINDOW(dialog), main_window);
1897 gtk_window_set_default_size(GTK_WINDOW(dialog), 1100, 900);
1900 GtkTreeStore *
store = gtk_tree_store_new(
NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1901 G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
1907 g_hash_table_destroy(node_cache);
1913 GINT_TO_POINTER(
i), NULL);
1914 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store),
i, GTK_SORT_ASCENDING);
1918 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
1919 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
filter_callback,
params, NULL);
1922 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), filter_model);
1923 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view),
COL_PATH);
1924 gtk_widget_set_hexpand(tree_view,
TRUE);
1925 gtk_widget_set_vexpand(tree_view,
TRUE);
1926 gtk_widget_set_halign(tree_view, GTK_ALIGN_FILL);
1927 gtk_widget_set_valign(tree_view, GTK_ALIGN_FILL);
1933 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_(
"View / Scope / Feature / Control"), gtk_cell_renderer_text_new(),
"text",
COL_NAME, NULL);
1934 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1936 GtkCellRenderer *renderer = gtk_cell_renderer_accel_new();
1937 column = gtk_tree_view_column_new_with_attributes(_(
"Keys"), renderer,
"accel-key",
COL_KEYVAL,
"accel-mods",
1940 g_signal_connect(renderer,
"accel-edited", G_CALLBACK(
_shortcut_edited), filter_model);
1941 g_signal_connect(renderer,
"accel-cleared", G_CALLBACK(
_shortcut_cleared), filter_model);
1942 gtk_tree_view_column_set_min_width(column, 100);
1943 gtk_tree_view_column_set_resizable(column,
TRUE);
1944 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1947 g_object_set(renderer,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
1948 column = gtk_tree_view_column_new_with_attributes(_(
"Clear"), renderer,
"pixbuf",
COL_CLEAR, NULL);
1950 g_signal_connect(renderer,
"activate", G_CALLBACK(
_icon_activate), filter_model);
1951 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1953 column = gtk_tree_view_column_new_with_attributes(_(
"Description"), gtk_cell_renderer_text_new(),
"text",
COL_DESCRIPTION, NULL);
1954 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1958 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), box,
TRUE,
TRUE, 0);
1961 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by feature : ")),
FALSE,
FALSE, 0);
1962 gtk_box_pack_start(GTK_BOX(hbox),
params->path_search,
TRUE,
TRUE, 0);
1963 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by keys : ")),
FALSE,
FALSE, 0);
1964 gtk_box_pack_start(GTK_BOX(hbox),
params->keys_search,
TRUE,
TRUE, 0);
1965 gtk_box_pack_start(GTK_BOX(box), hbox,
FALSE,
FALSE, 0);
1967 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1969 gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
1970 gtk_box_pack_start(GTK_BOX(box), scrolled_window,
TRUE,
TRUE, 0);
1972 gtk_widget_set_visible(tree_view,
TRUE);
1973 gtk_widget_show_all(dialog);
1975 gtk_dialog_run(GTK_DIALOG(dialog));
1976 gtk_widget_destroy(dialog);
1977 g_object_unref(filter_model);
1978 g_object_unref(
store);
1990 if(
IS_NULL_PTR(needle) || needle[0] ==
'\0')
return 0;
1994 gtk_tree_model_get(
model, iter, 0, &label, -1);
2002 gchar *label_ci = g_utf8_casefold(label, -1);
2004 gchar **parts = g_strsplit(label_ci,
"/", -1);
2005 gchar *needle_copy = g_strdup(needle);
2006 gchar **tokens = g_strsplit_set(needle_copy,
" \t\r\n", -1);
2008 gboolean has_token =
FALSE;
2009 gboolean all_matched =
TRUE;
2012 if(tokens[
t][0] ==
'\0')
continue;
2014 int best_token_rank = INT_MAX;
2019 const char *match = g_strstr_len(parts[
i], -1, tokens[
t]);
2022 const int cell_rank =
i * 10000;
2023 const int in_cell_rank = match - parts[
i];
2024 const int token_rank = cell_rank + in_cell_rank;
2025 if(token_rank < best_token_rank) best_token_rank = token_rank;
2028 if(best_token_rank == INT_MAX)
2030 all_matched =
FALSE;
2033 rank_sum += best_token_rank;
2036 if(all_matched) ret = has_token ? rank_sum : 0;
2050 const gchar *needle = gtk_entry_get_text(GTK_ENTRY(
search_entry));
2051 gchar *needle_query = g_strdup(!
IS_NULL_PTR(needle) ? needle :
"");
2054 g_strstrip(needle_query);
2055 gchar *needle_ci = g_utf8_casefold(needle_query, -1);
2059 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
2060 GTK_SORT_ASCENDING);
2063 if(gtk_tree_model_get_iter_first(
model, &iter))
2068 gtk_list_store_set(GTK_LIST_STORE(
model), &iter, 2, rank, -1);
2070 }
while(gtk_tree_model_iter_next(
model, &iter));
2077 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), 2, GTK_SORT_ASCENDING);
2078 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
model));
2099#define DT_ACCEL_SEARCH_RECENT_KEY "plugins/accel_search/recent_entries"
2100#define DT_ACCEL_SEARCH_RECENT_MAX 20
2105 gtk_list_store_clear(
store);
2116 if(entry[0] ==
'\0')
2124 gchar **parts = g_strsplit(entry,
"\t", 3);
2126 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
2133 const gchar *query = parts[0];
2134 const gchar *command = parts[1];
2139 gtk_list_store_append(
store, &iter);
2140 gtk_list_store_set(
store, &iter,
2157 const gchar *search_text =
"";
2160 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2161 if(!
IS_NULL_PTR(entry_text)) search_text = entry_text;
2164 gchar *query_text = g_strdup(search_text);
2167 g_strstrip(query_text);
2168 gchar *query_ci = g_utf8_casefold(query_text, -1);
2169 const gboolean has_query = query_ci[0] !=
'\0';
2171 gchar *a_query = NULL, *a_command = NULL;
2172 gchar *b_query = NULL, *b_command = NULL;
2173 gint a_recent = G_MAXINT, b_recent = G_MAXINT;
2174 gtk_tree_model_get(
model, a, 0, &a_query, 1, &a_command, 4, &a_recent, -1);
2175 gtk_tree_model_get(
model, b, 0, &b_query, 1, &b_command, 4, &b_recent, -1);
2177 gchar *a_query_ci = g_utf8_casefold(!
IS_NULL_PTR(a_query) ? a_query :
"", -1);
2178 gchar *a_command_ci = g_utf8_casefold(!
IS_NULL_PTR(a_command) ? a_command :
"", -1);
2179 gchar *b_query_ci = g_utf8_casefold(!
IS_NULL_PTR(b_query) ? b_query :
"", -1);
2180 gchar *b_command_ci = g_utf8_casefold(!
IS_NULL_PTR(b_command) ? b_command :
"", -1);
2182 gint a_rank = 400000, b_rank = 400000;
2185 if(g_str_has_prefix(a_query_ci, query_ci))
2187 else if(g_str_has_prefix(a_command_ci, query_ci))
2191 const gchar *a_match_query = g_strstr_len(a_query_ci, -1, query_ci);
2193 a_rank = 200000 + (a_match_query - a_query_ci);
2196 const gchar *a_match_command = g_strstr_len(a_command_ci, -1, query_ci);
2198 a_rank = 300000 + (a_match_command - a_command_ci);
2202 if(g_str_has_prefix(b_query_ci, query_ci))
2204 else if(g_str_has_prefix(b_command_ci, query_ci))
2208 const gchar *b_match_query = g_strstr_len(b_query_ci, -1, query_ci);
2210 b_rank = 200000 + (b_match_query - b_query_ci);
2213 const gchar *b_match_command = g_strstr_len(b_command_ci, -1, query_ci);
2215 b_rank = 300000 + (b_match_command - b_command_ci);
2220 gint ret = a_rank - b_rank;
2221 if(ret == 0) ret = a_recent - b_recent;
2222 if(ret == 0) ret = g_utf8_collate(a_query_ci, b_query_ci);
2223 if(ret == 0) ret = g_utf8_collate(a_command_ci, b_command_ci);
2243 gchar *trimmed = g_strdup(query);
2244 g_strstrip(trimmed);
2245 if(trimmed[0] ==
'\0')
2251 gchar *command = g_strdup(shortcut->
path);
2252 g_strdelimit(trimmed,
"\t\r\n",
' ');
2253 g_strdelimit(command,
"\t\r\n",
' ');
2255 GPtrArray *
entries = g_ptr_array_new_with_free_func(g_free);
2256 GPtrArray *entries_ci = g_ptr_array_new_with_free_func(g_free);
2257 g_ptr_array_add(
entries, g_strdup_printf(
"%s\t%s", trimmed, command));
2258 g_ptr_array_add(entries_ci, g_utf8_casefold(trimmed, -1));
2263 if(
IS_NULL_PTR(candidate) || candidate[0] ==
'\0')
2268 g_strstrip(candidate);
2269 if(candidate[0] ==
'\0')
2275 gchar **parts = g_strsplit(candidate,
"\t", 3);
2277 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
2284 const gchar *candidate_query = parts[0];
2285 const gchar *candidate_command = parts[1];
2287 gchar *candidate_ci = g_utf8_casefold(candidate_query, -1);
2288 gboolean found =
FALSE;
2289 for(guint
k = 0;
k < entries_ci->len;
k++)
2291 const char *kept_ci = g_ptr_array_index(entries_ci,
k);
2292 if(!g_strcmp0(kept_ci, candidate_ci))
2305 g_ptr_array_add(
entries, g_strdup_printf(
"%s\t%s", candidate_query, candidate_command));
2306 g_ptr_array_add(entries_ci, candidate_ci);
2313 const gchar *entry_value = (
i <
entries->len) ? (
const gchar *)g_ptr_array_index(
entries,
i) :
"";
2317 g_ptr_array_free(entries_ci,
TRUE);
2342 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
state->recent_entries));
2344 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(
state->filter_model));
2346 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2347 gtk_tree_selection_unselect_all(selection);
2350 gboolean has_iter = gtk_tree_model_get_iter_first(
state->filter_model, &iter);
2351 GtkTreeIter fallback_iter = iter;
2352 GtkTreeIter selected_iter = iter;
2353 gboolean found_preferred =
FALSE;
2359 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2361 && !g_strcmp0(shortcut->
path,
state->preferred_command))
2363 selected_iter = iter;
2364 found_preferred =
TRUE;
2367 }
while(gtk_tree_model_iter_next(
state->filter_model, &iter));
2372 GtkTreeIter *target_iter = found_preferred ? &selected_iter : &fallback_iter;
2373 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, target_iter);
2375 gtk_tree_selection_select_iter(selection, target_iter);
2376 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2377 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2378 gtk_tree_path_free(path);
2380 gtk_tree_model_get(
state->filter_model, target_iter, 1, &
state->selected, -1);
2385 GtkTreeIter *iter, gpointer user_data)
2388 gchar *query = NULL;
2389 gchar *command = NULL;
2390 gtk_tree_model_get(
model, iter, 0, &query, 1, &command, -1);
2395 state->preferred_command = NULL;
2398 state->preferred_command = g_strdup(command);
2402 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
2403 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2415 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2416 if(
state->suppress_inline_once)
2422 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2424 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2426 const gchar *last = g_utf8_find_prev_char(entry_text, entry_text + strlen(entry_text));
2427 const gunichar last_char = !
IS_NULL_PTR(last) ? g_utf8_get_char(last) : 0;
2428 if(last_char != 0 && !g_unichar_isalnum(last_char))
2431 gchar *query = g_strdup(!
IS_NULL_PTR(entry_text) ? entry_text :
"");
2435 if(query[0] ==
'\0')
2441 gchar *query_ci = g_utf8_casefold(query, -1);
2442 if(query_ci[0] ==
'\0')
2449 gint best_rank = G_MAXINT;
2450 gint best_recent = G_MAXINT;
2451 gchar *best_command = NULL;
2452 gchar *best_display = NULL;
2453 gchar *best_query_ci = NULL;
2454 gchar *best_command_ci = NULL;
2455 const glong query_len = g_utf8_strlen(query_ci, -1);
2458 if(gtk_tree_model_get_iter_first(
model, &iter))
2462 gchar *row_query = NULL;
2463 gchar *row_command = NULL;
2464 gchar *row_display = NULL;
2465 gint row_recent = G_MAXINT;
2466 gtk_tree_model_get(
model, &iter, 0, &row_query, 1, &row_command, 3, &row_display, 4, &row_recent, -1);
2475 gchar *row_query_ci = g_utf8_casefold(row_query, -1);
2476 gchar *row_command_ci = g_utf8_casefold(!
IS_NULL_PTR(row_command) ? row_command :
"", -1);
2477 gint row_rank = G_MAXINT;
2478 if(g_str_has_prefix(row_query_ci, query_ci))
2481 const glong row_query_len = g_utf8_strlen(row_query_ci, -1);
2482 row_rank =
MAX((gint)(row_query_len - query_len), 0);
2484 else if(g_str_has_prefix(row_command_ci, query_ci))
2486 const glong row_command_len = g_utf8_strlen(row_command_ci, -1);
2487 row_rank = 100000 +
MAX((gint)(row_command_len - query_len), 0);
2491 const gchar *match_query = g_strstr_len(row_query_ci, -1, query_ci);
2493 row_rank = 200000 + (match_query - row_query_ci);
2496 const gchar *match_command = g_strstr_len(row_command_ci, -1, query_ci);
2498 row_rank = 300000 + (match_command - row_command_ci);
2502 if(row_rank < best_rank
2503 || (row_rank == best_rank && row_recent < best_recent)
2504 || (row_rank == best_rank && row_recent == best_recent
2505 && (!
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) < 0))
2506 || (row_rank == best_rank && row_recent == best_recent
2507 && !
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) == 0
2508 && (!
IS_NULL_PTR(best_command_ci) && g_utf8_collate(row_command_ci, best_command_ci) < 0)))
2510 best_rank = row_rank;
2511 best_recent = row_recent;
2516 best_command = g_strdup(!
IS_NULL_PTR(row_command) ? row_command :
"");
2517 best_display = g_strdup(!
IS_NULL_PTR(row_display) ? row_display : row_query);
2518 best_query_ci = g_strdup(row_query_ci);
2519 best_command_ci = g_strdup(row_command_ci);
2527 }
while(gtk_tree_model_iter_next(
model, &iter));
2530 if(!
IS_NULL_PTR(best_command) && best_command[0] !=
'\0' && best_rank < G_MAXINT)
2535 state->preferred_command = NULL;
2537 state->preferred_command = g_strdup(best_command);
2539 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2540 GtkTreeIter filter_iter;
2541 if(gtk_tree_model_get_iter_first(
state->filter_model, &filter_iter))
2546 gchar *shortcut_path_display = NULL;
2547 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &shortcut, 0, &shortcut_path_display, -1);
2548 gchar *shortcut_trimmed = NULL;
2552 && (!g_strcmp0(shortcut->
path, best_command)
2553 || (!
IS_NULL_PTR(shortcut_trimmed) && !g_strcmp0(shortcut_trimmed, best_command))
2554 || (!
IS_NULL_PTR(shortcut_path_display) && !g_strcmp0(shortcut_path_display, best_command))))
2556 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &filter_iter);
2560 dt_free(shortcut_path_display);
2563 gtk_tree_selection_select_iter(selection, &filter_iter);
2564 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2565 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2566 gtk_tree_path_free(path);
2567 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &
state->selected, -1);
2569 dt_free(shortcut_path_display);
2573 dt_free(shortcut_path_display);
2574 }
while(gtk_tree_model_iter_next(
state->filter_model, &filter_iter));
2577 if(!
IS_NULL_PTR(best_display) && best_display[0] !=
'\0')
2579 const gchar *current = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2580 if(g_strcmp0(current, best_display))
2582 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), best_display);
2584 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1));
2585 gtk_editable_select_region(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1), -1);
2613 GValue
params[4] = { G_VALUE_INIT };
2615 g_value_init(&
params[0], G_TYPE_POINTER);
2618 g_value_init(&
params[1], G_TYPE_POINTER);
2619 g_value_set_pointer(&
params[1], G_OBJECT(main_window));
2621 g_value_init(&
params[2], G_TYPE_UINT);
2622 g_value_set_uint(&
params[2], shortcut->
key);
2624 g_value_init(&
params[3], G_TYPE_UINT);
2625 g_value_set_uint(&
params[3], shortcut->
mods);
2627 GValue ret = G_VALUE_INIT;
2628 g_value_init (&ret, G_TYPE_BOOLEAN);
2633 for(
int k = 0;
k < 4;
k++) g_value_unset(&
params[
k]);
2634 g_value_unset(&ret);
2638 g_closure_invoke(active_closure, &ret, 4,
params, NULL);
2639 const gboolean handled = g_value_get_boolean(&ret);
2641 for(
int k = 0;
k < 4;
k++) g_value_unset(&
params[
k]);
2642 g_value_unset(&ret);
2660 dt_widget_log(
"[accel_search] dispatch skipped: shortcut '%s' no longer exists\n",
2669 for(GList *item = g_list_last(shortcut->
closure); item; item = g_list_previous(item))
2680 || gtk_widget_is_ancestor(candidate_widget, GTK_WIDGET(
state->main_window));
2681 if(in_main_window &&
IS_NULL_PTR(payload_in_main_window))
2682 payload_in_main_window = candidate;
2683 if(in_main_window && gtk_widget_get_visible(candidate_widget) && gtk_widget_get_mapped(candidate_widget))
2685 payload = candidate;
2691 if(
IS_NULL_PTR(payload)) payload = payload_in_main_window;
2696 target_widget = payload->
widget;
2698 target_widget = shortcut->
widget;
2716 g_object_add_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2718 g_object_add_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2724 dt_widget_log(
"[accel_search] dispatch closure target='%s' description='%s' handled=%d\n",
2725 path, desc, handled);
2729 const gboolean activated = gtk_widget_activate(shortcut_widget);
2730 dt_widget_log(
"[accel_search] dispatch widget target='%s' description='%s' activated=%d widget=%s\n",
2731 path, desc, activated,
2732 !
IS_NULL_PTR(shortcut_widget) ? gtk_widget_get_name(shortcut_widget) :
"<destroyed>");
2736 dt_widget_log(
"[accel_search] dispatch failed: no callable target for '%s' description='%s'\n",
2745 focused_widget = gtk_window_get_focus(
state->main_window);
2749 gboolean target_focused_gtk =
FALSE;
2750 gboolean target_focused_scroll =
FALSE;
2755 target_focused_gtk = focused_widget == target_widget
2756 || gtk_widget_is_ancestor(focused_widget, target_widget)
2757 || gtk_widget_is_ancestor(target_widget, focused_widget);
2761 target_focused_scroll = scroll_focused_widget == target_widget
2762 || gtk_widget_is_ancestor(scroll_focused_widget, target_widget)
2763 || gtk_widget_is_ancestor(target_widget, scroll_focused_widget);
2766 const gboolean target_focused = target_focused_gtk || target_focused_scroll;
2768 dt_widget_log(
"[accel_search] focus check (pre-idle) target='%s' target_widget=%s(%p) gtk_focus=%s(%p)"
2769 " scroll_focus=%s(%p) target_focused_gtk=%d target_focused_scroll=%d target_focused=%d\n",
2771 !
IS_NULL_PTR(target_widget) ? gtk_widget_get_name(target_widget) :
"<null>",
2772 (
void *)target_widget,
2773 !
IS_NULL_PTR(focused_widget) ? gtk_widget_get_name(focused_widget) :
"<null>",
2774 (
void *)focused_widget,
2775 !
IS_NULL_PTR(scroll_focused_widget) ? gtk_widget_get_name(scroll_focused_widget) :
"<null>",
2776 (
void *)scroll_focused_widget,
2777 target_focused_gtk, target_focused_scroll, target_focused);
2782 && !g_strcmp0(G_OBJECT_TYPE_NAME(target_widget),
"DtBauhausWidget"))
2785 retry->
path = g_strdup(path);
2789 dt_widget_log(
"[accel_search] dispatch retry scheduled target='%s' retry=%u\n",
2798 g_object_remove_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2800 g_object_remove_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2808 g_free(
state->path);
2810 return G_SOURCE_REMOVE;
2816 gtk_tree_model_get(
model, iter, 2, &rank, -1);
2821 GtkTreeIter *iter, gpointer user_data)
2825 gchar *key_query = g_strdup(
key);
2828 g_strstrip(key_query);
2829 if(key_query[0] ==
'\0')
2835 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2842 gchar *query = NULL;
2843 gtk_tree_model_get(
model, iter, 0, &query, -1);
2850 gchar *key_ci = g_utf8_casefold(key_query, -1);
2851 gchar *query_ci = g_utf8_casefold(query, -1);
2852 const gboolean match = !
IS_NULL_PTR(g_strrstr(query_ci, key_ci));
2864 const gchar *query_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2865 gchar *query = g_strdup(!
IS_NULL_PTR(query_text) ? query_text :
"");
2869 dt_widget_log(
"[accel_search] validate query='%s' shortcut='%s' description='%s'\n",
2875 state->selected = shortcut;
2876 state->response = GTK_RESPONSE_ACCEPT;
2877 gtk_widget_destroy(
window);
2885 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
2887 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2891 state->selected = NULL;
2896 GtkTreeViewColumn *column, gpointer user_data)
2900 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
return FALSE;
2903 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2909 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2911 if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
2913 if(!gtk_tree_model_get_iter_first(
state->filter_model, &iter))
return TRUE;
2917 if(!gtk_tree_model_iter_next(
state->filter_model, &iter))
return TRUE;
2921 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2923 if(!gtk_tree_path_prev(path))
2925 gtk_tree_path_free(path);
2929 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
2931 gtk_tree_path_free(path);
2934 gtk_tree_path_free(path);
2937 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2939 gtk_tree_selection_select_iter(selection, &iter);
2940 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2941 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2942 gtk_tree_path_free(path);
2943 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2953 return G_SOURCE_REMOVE;
2955 gchar *with_space = g_strconcat(
state->pending_space_query,
" ", NULL);
2956 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), with_space);
2957 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2960 state->pending_space_query = NULL;
2961 return G_SOURCE_REMOVE;
2965 GdkEventKey *event, gpointer user_data)
2970 if(
key == GDK_KEY_Escape)
2972 state->response = GTK_RESPONSE_CANCEL;
2973 gtk_widget_destroy(
state->window);
2977 if(
key == GDK_KEY_Down)
2979 if(
key == GDK_KEY_Up)
2981 if(
key == GDK_KEY_Return)
2984 gchar *command = NULL;
2987 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2988 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2993 : g_strdup(entry_text);
2998 g_strstrip(command);
2999 if(command[0] !=
'\0')
3002 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(
state->store), &iter))
3007 gchar *candidate_path = NULL;
3008 gtk_tree_model_get(GTK_TREE_MODEL(
state->store), &iter, 1, &candidate, 0, &candidate_path, -1);
3009 gchar *candidate_display_path = NULL;
3013 && (!g_strcmp0(command, candidate->
path)
3014 || !g_strcmp0(command, candidate_path)
3016 && !g_strcmp0(command, candidate_display_path))))
3018 shortcut = candidate;
3020 dt_free(candidate_display_path);
3024 dt_free(candidate_display_path);
3025 }
while(gtk_tree_model_iter_next(GTK_TREE_MODEL(
state->store), &iter));
3037 if(
key == GDK_KEY_space)
3043 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
3046 gchar *query_only = NULL;
3047 gint sel_start = 0, sel_end = 0;
3048 const gboolean
has_selection = gtk_editable_get_selection_bounds(GTK_EDITABLE(
state->search_entry),
3049 &sel_start, &sel_end);
3051 : gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
3052 const gint text_chars = g_utf8_strlen(entry_text, -1);
3053 if(cursor_chars >= 0 && cursor_chars <= text_chars)
3054 query_only = g_utf8_substring(entry_text, 0, cursor_chars);
3056 query_only = g_strdup(entry_text);
3060 *((gchar *)sep) =
'\0';
3063 state->pending_space_query = query_only;
3066 if(
state->pending_space_idle_id != 0) g_source_remove(
state->pending_space_idle_id);
3071 gunichar key_char = gdk_keyval_to_unicode(event->keyval);
3072 const gboolean is_alnum = key_char != 0 && g_unichar_isalnum(key_char);
3079 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
3085 const gint
position = gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
3086 const gsize query_len = sep - entry_text;
3087 gchar *query_only = g_strndup(entry_text, query_len);
3089 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query_only);
3090 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry),
MIN(
position, (gint)query_len));
3102 if(event->button != 1)
return FALSE;
3105 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
3111 const gsize query_len = sep - entry_text;
3112 gchar *query = g_strndup(entry_text, query_len);
3114 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
3115 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
3128 GtkAllocation allocation = { 0 };
3129 gtk_widget_get_allocation(widget, &allocation);
3131 gboolean click_inside =
FALSE;
3132 GdkWindow *win = gtk_widget_get_window(widget);
3135 gint wx = 0, wy = 0;
3136 gdk_window_get_origin(win, &wx, &wy);
3137 const gdouble x0 = (gdouble)wx;
3138 const gdouble y0 = (gdouble)wy;
3139 const gdouble x1 = x0 + (gdouble)allocation.width;
3140 const gdouble y1 = y0 + (gdouble)allocation.height;
3141 click_inside = (
event->x_root >= x0 &&
event->x_root < x1
3142 &&
event->y_root >= y0 &&
event->y_root < y1);
3146 click_inside = (
event->x >= 0.0 &&
event->x < allocation.width
3147 &&
event->y >= 0.0 &&
event->y < allocation.height);
3150 if(click_inside)
return FALSE;
3152 state->response = GTK_RESPONSE_CANCEL;
3153 gtk_widget_destroy(widget);
3160 if(
state->pending_space_idle_id != 0)
3162 g_source_remove(
state->pending_space_idle_id);
3163 state->pending_space_idle_id = 0;
3168 state->pending_space_query = NULL;
3170 gtk_grab_remove(widget);
3171 if(
state->window == widget)
state->window = NULL;
3178 gtk_window_set_title(GTK_WINDOW(
window), _(
"Ansel - Search accelerators"));
3180#ifdef GDK_WINDOWING_QUARTZ
3184 const int dialog_width = 800;
3185 const int dialog_height = 0;
3187 gtk_window_set_decorated(GTK_WINDOW(
window),
FALSE);
3189 gtk_window_set_transient_for(GTK_WINDOW(
window), main_window);
3190 gtk_window_set_attached_to(GTK_WINDOW(
window), GTK_WIDGET(main_window));
3191 gtk_window_set_resizable(GTK_WINDOW(
window),
FALSE);
3192 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(
window),
TRUE);
3193 gtk_window_set_skip_pager_hint(GTK_WINDOW(
window),
TRUE);
3194 gtk_window_set_accept_focus(GTK_WINDOW(
window),
TRUE);
3195 gtk_window_set_focus_on_map(GTK_WINDOW(
window),
TRUE);
3196 gtk_window_set_default_size(GTK_WINDOW(
window), dialog_width, dialog_height);
3197 gtk_widget_set_name(
window,
"shortcut-search-dialog");
3198 gtk_widget_add_events(
window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3201 GtkListStore *
store = gtk_list_store_new(7, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT);
3204 GMainLoop *loop = g_main_loop_new(NULL,
FALSE);
3207 .filter_model = NULL,
3208 .recent_entries = NULL,
3209 .main_window = main_window,
3210 .search_entry = NULL,
3214 .response = GTK_RESPONSE_CANCEL,
3216 .preferred_command = NULL,
3217 .suppress_inline_once =
FALSE,
3218 .pending_space_query = NULL,
3219 .pending_space_idle_id = 0
3223 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(
store), 2,
3225 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), 2, GTK_SORT_ASCENDING);
3230 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3231 gtk_container_add(GTK_CONTAINER(
window), box);
3232 GtkWidget *search_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
3233 gtk_box_pack_start(GTK_BOX(box), search_row,
TRUE,
TRUE, 0);
3236 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
3237 state.filter_model = filter_model;
3238 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
3241 GtkEntryCompletion *completion = gtk_entry_completion_new();
3242 GtkListStore *recent_entries = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
3244 state.recent_entries = recent_entries;
3246 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(recent_entries), 4,
3248 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(recent_entries), 4, GTK_SORT_ASCENDING);
3249 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(recent_entries));
3250 gtk_entry_completion_set_text_column(completion, 3);
3251 gtk_entry_completion_set_inline_completion(completion,
TRUE);
3252 gtk_entry_completion_set_inline_selection(completion,
TRUE);
3253 gtk_entry_completion_set_popup_completion(completion,
FALSE);
3255 gtk_entry_set_completion(GTK_ENTRY(
search_entry), completion);
3256 g_object_unref(completion);
3259 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(
scrolled),
3260 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3261 gtk_widget_set_size_request(
scrolled, dialog_width, 320);
3265 GtkWidget *tree_view = gtk_tree_view_new_with_model(filter_model);
3266 state.tree_view = tree_view;
3267 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view),
FALSE);
3268 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree_view),
FALSE);
3269 gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(tree_view),
FALSE);
3270 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(tree_view),
TRUE);
3271 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view), 0);
3272 gtk_widget_set_hexpand(tree_view,
TRUE);
3273 gtk_widget_set_vexpand(tree_view,
TRUE);
3274 gtk_container_add(GTK_CONTAINER(
scrolled), tree_view);
3276 GtkCellRenderer *txt = gtk_cell_renderer_text_new();
3277 g_object_set(txt,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE,
"max-width-chars", 70, NULL);
3278 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, txt,
"text", 0, NULL);
3279 gtk_tree_view_column_set_expand(column,
FALSE);
3280 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3281 gtk_tree_view_column_set_min_width(column, 360);
3282 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3284 GtkCellRenderer *accel = gtk_cell_renderer_accel_new();
3285 g_object_set(accel,
"editable",
FALSE,
"accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER, NULL);
3286 column = gtk_tree_view_column_new_with_attributes(NULL, accel,
"accel-key", 5,
"accel-mods", 6, NULL);
3287 gtk_tree_view_column_set_min_width(column, 140);
3288 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3290 GtkCellRenderer *
description = gtk_cell_renderer_text_new();
3291 g_object_set(
description,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE, NULL);
3292 column = gtk_tree_view_column_new_with_attributes(NULL,
description,
"text", 3, NULL);
3293 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3294 gtk_tree_view_column_set_min_width(column, 280);
3295 gtk_tree_view_column_set_expand(column,
TRUE);
3296 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3298 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
3299 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
3317 GtkAllocation main_alloc = { 0 };
3318 gtk_widget_get_allocation(GTK_WIDGET(main_window), &main_alloc);
3319 gint main_x = 0, main_y = 0;
3320 GdkWindow *main_gdk_window = gtk_widget_get_window(GTK_WIDGET(main_window));
3322 gdk_window_get_origin(main_gdk_window, &main_x, &main_y);
3324 gtk_window_get_position(main_window, &main_x, &main_y);
3329 const gint window_x = main_x +
MAX((main_alloc.width - dialog_width) / 2, 0);
3330 const gint window_y = main_y +
MAX(top_panel_height, 0);
3331 gtk_window_move(GTK_WINDOW(
window), window_x, window_y);
3333 gtk_widget_realize(
window);
3334 gtk_widget_show_all(
window);
3336 gdk_window_focus(gtk_widget_get_window(
window), GDK_CURRENT_TIME);
3340 g_main_loop_run(loop);
3341 g_main_loop_unref(loop);
3345 dispatch->
path = g_strdup(
state.selected->path);
3347 ?
state.selected->accels
3355 g_object_unref(
store);
3373 if(event->keyval == GDK_KEY_Escape)
3383 gtk_widget_add_events(widget, GDK_FOCUS_CHANGE_MASK);
static dt_shortcut_t * _find_non_virtual_shortcut(dt_accels_t *accels, GtkAccelGroup *group, guint keyval, GdkModifierType mods)
static dt_accels_t * accels_global_ref
void dt_shortcut_remove_closure(dt_shortcut_t *shortcut, gpointer data)
static void _shortcut_search_load_recent_entries(GtkListStore *store)
static gboolean _search_entry_restore_space_idle(gpointer user_data)
static void _remove_generic_accel(dt_shortcut_t *shortcut)
void dt_accels_connect_accels(dt_accels_t *accels)
Actually enable accelerators after having loaded user config.
void dt_accels_set_top_offset_handler(dt_accels_top_offset_handler_t handler)
static gboolean _text_entry_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
static void _shortcut_set_widget_data(GtkWidget *widget, dt_shortcut_t *shortcut)
static gchar * _shortcut_search_trim_display_path(const gchar *path)
static void _add_generic_accel(dt_shortcut_t *shortcut, GtkAccelFlags flags)
static void _for_each_non_virtual_accel(gpointer key, gpointer value, gpointer user_data)
void dt_accels_connect_active_group(dt_accels_t *accels, const gchar *group)
Connect the contextual active accels group to the window. Views can declare their own set of contextu...
static void _make_column_editable(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
static gboolean _shortcut_search_visible(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
void dt_accels_disconnect_active_group(dt_accels_t *accels)
Disconnect the contextual active accels group from the window.
static dt_accels_recent_get_handler_t _recent_get
void _for_each_path_create_treeview_row(gpointer key, gpointer value, gpointer user_data)
static gboolean _virtual_shortcut_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
gboolean dt_accels_dispatch(GtkWidget *w, GdkEvent *event, gpointer user_data)
Force our listener for all key strokes to bypass reserved Gtk keys.
static void _shortcut_search_selection_changed(GtkTreeSelection *selection, gpointer user_data)
#define DT_ACCEL_SEARCH_RECENT_MAX
static void _insert_accel(dt_accels_t *accels, dt_shortcut_t *shortcut)
static gboolean _shortcut_search_recent_insert_prefix(GtkEntryCompletion *completion, gchar *prefix, gpointer user_data)
void dt_accels_remove_shortcut(dt_accels_t *accels, const char *path)
Remove the shortcut object identified by path and all its accels.
PayloadClosure * dt_shortcut_get_payload_closure(dt_shortcut_t *shortcut)
static gboolean _shortcut_search_move_selection(dt_accels_search_state_t *state, const gboolean forward)
static void _insert_parent_data_into_children(dt_shortcut_t *shortcut)
static void _shortcut_cleared(GtkCellRendererAccel *renderer, const gchar *path_string, gpointer user_data)
static dt_accels_top_offset_handler_t _top_offset_handler
dt_accels_t * dt_accels_init(char *config_file, GtkAccelFlags flags)
static gint _sort_model_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
static dt_accels_refocus_handler_t _refocus_handler
void dt_accels_set_refocus_handler(dt_accels_refocus_handler_t handler)
static void _dispatch_selected_shortcut(dt_accels_dispatch_state_t *state)
static gint _shortcut_search_recent_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
static guint _normalize_keyval(const guint keyval)
static gboolean _shortcut_search_recent_match_selected(GtkEntryCompletion *completion, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static void _accels_keys_decode(dt_accels_t *accels, GdkEvent *event, guint *keyval, GdkModifierType *mods)
static gboolean _search_entry_button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static dt_accels_recent_set_handler_t _recent_set
static void _find_and_rank_matches(GtkTreeModel *model, GtkWidget *search_entry)
static const char * _find_path_for_keys(dt_accels_t *accels, guint key, GdkModifierType modifier, GtkAccelGroup *group)
static gboolean _update_shortcut_state(dt_shortcut_t *shortcut, GtkAccelKey *key, gboolean init)
static void _remove_widget_accel(dt_shortcut_t *shortcut, const GtkAccelKey *old_key)
static void _connect_accel(dt_shortcut_t *shortcut)
static gboolean _icon_activate(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *treeview, const gchar *path_str, GdkRectangle *background, GdkRectangle *cell_area, GtkCellRendererState flags, gpointer user_data)
static void _g_list_closure_unref(gpointer data)
static void _create_main_row(GtkTreeStore *store, GtkTreeIter *iter, const char *label, const char *path, dt_shortcut_t *shortcut)
static gboolean _shortcut_search_recent_completion_match(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data)
static gboolean _call_shortcut_cclosure(dt_shortcut_t *shortcut, GtkWindow *main_window, GClosure *closure)
static void _shortcut_search_save_recent_entry(const char *query, const dt_shortcut_t *shortcut)
static int _match_text(GtkTreeModel *model, GtkTreeIter *iter, const char *needle)
static void _make_column_clearable(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
static void _remove_accel_hashtable(gpointer _key, gpointer value, gpointer user_data)
static void _find_parent_hashtable(gpointer _key, gpointer value, gpointer user_data)
void _for_each_accel_create_treeview_row(gpointer key, gpointer value, gpointer user_data)
dt_accels_t * dt_accels_get_global(void)
static void _shortcut_search_destroy(GtkWidget *widget, gpointer user_data)
#define DT_ACCEL_SEARCH_INLINE_SEPARATOR
void dt_accels_cleanup(dt_accels_t *accels)
static void search_changed(GtkEntry *entry, gpointer user_data)
static gboolean _dispatch_selected_shortcut_idle(gpointer data)
static void _for_each_accel(gpointer key, gpointer value, gpointer user_data)
static gint _sort_model_by_relevance_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
static gboolean _accels_tooltip_query_hook(GSignalInvocationHint *hint, guint n_param_values, const GValue *param_values, gpointer data)
void dt_accels_search(dt_accels_t *accels, GtkWindow *main_window, GtkWidget *anchor)
static gboolean _search_entry_key_pressed(GtkWidget *widget __attribute__((unused)), GdkEventKey *event, gpointer user_data)
static int guess_key_group(dt_accels_t *accels, guint keyval, guint hardware_keycode)
void dt_accels_window(dt_accels_t *accels, GtkWindow *main_window)
Show the modal dialog listing all available keyboard shortcuts and letting user to set them.
static gboolean filter_callback(GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static gboolean _widget_shortcut_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
void dt_accels_new_virtual_shortcut(dt_accels_t *accels, GtkAccelGroup *accel_group, const gchar *accel_path, GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
Add a new virtual shortcut. Virtual shortcuts are immutable, read-only and don't trigger any action....
#define DT_ACCEL_SEARCH_DISPATCH_RETRY_DELAY_MS
void dt_accels_set_recent_handlers(dt_accels_recent_get_handler_t get, dt_accels_recent_set_handler_t set)
void dt_accels_set_global(dt_accels_t *accels)
static void _shortcut_edited(GtkCellRenderer *cell, const gchar *path_string, guint key, GdkModifierType mods, guint hardware_key, gpointer user_data)
static void _accels_install_tooltip_hook(void)
void dt_accels_new_virtual_instance_shortcut(dt_accels_t *accels, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gpointer data, GtkAccelGroup *accel_group, const gchar *action_scope, const gchar *action_name)
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
Build the accel path of feature under scope.
GClosure * dt_shortcut_get_closure(dt_shortcut_t *shortcut)
static gboolean _shortcut_search_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
void dt_accels_new_action_shortcut(dt_accels_t *accels, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gpointer data, GtkWidget *target_widget, GtkAccelGroup *accel_group, const gchar *action_scope, const gchar *action_name, guint key_val, GdkModifierType accel_mods, const gboolean lock, const char *description)
Register a new shortcut for a generic action, setting up its path, default keys and accel group....
static gboolean _text_entry_focus_out_event(GtkWidget *self, GdkEventFocus event, gpointer user_data)
void dt_accels_remove_accel(dt_accels_t *accels, const char *path, gpointer data)
Recursively remove all accels for all shortcuts containing path. This is unneeded for accels attached...
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
void dt_accels_load_user_config(dt_accels_t *accels)
Loads keyboardrc.lang from config dir. This needs to run after we inited the accel map from widgets c...
static gboolean _shortcut_search_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
void dt_accels_new_widget_shortcut(dt_accels_t *accels, GtkWidget *widget, const gchar *signal, GtkAccelGroup *accel_group, const gchar *accel_path, guint key_val, GdkModifierType accel_mods, const gboolean lock)
Register a new shortcut for a widget, setting up its path, default keys and accel group....
static gboolean _text_entry_focus_in_event(GtkWidget *self, GdkEventFocus event, gpointer user_data)
static gboolean _shortcut_search_window_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
static void _connect_accel_hashtable(gpointer _key, gpointer value, gpointer user_data)
static void _clean_shortcut(gpointer data)
static dt_accels_t * _accels_global
void dt_shortcut_set_closure(dt_shortcut_t *shortcut, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gpointer data, GtkWidget *widget)
static void _search_entry_changed(GtkWidget *widget, gpointer user_data)
void dt_accels_attach_scroll_handler(dt_accels_t *accels, gboolean(*callback)(GdkEventScroll event, void *data), void *data)
Attach a new global scroll event callback. So far this is used in darkroom to redirect scroll events ...
static gboolean _queue_action_from_shortcut(dt_shortcut_t *shortcut, GtkWidget *window, dt_accels_search_state_t *state)
static void _add_widget_accel(dt_shortcut_t *shortcut, GtkAccelFlags flags)
static gboolean _key_pressed(GtkWidget *w, GdkEvent *event, dt_accels_t *accels, guint keyval, GdkModifierType mods)
void dt_accels_detach_scroll_handler(dt_accels_t *accels)
Handle default and user-set shortcuts (accelerators)
void(* dt_accels_recent_set_handler_t)(int index, const char *value)
static void dt_accels_disable(dt_accels_t *accels, gboolean state)
void(* dt_accels_refocus_handler_t)(void)
#define DT_ACCELS_WIDGET_SHORTCUT_KEY
gchar *(* dt_accels_recent_get_handler_t)(int index)
gint(* dt_accels_top_offset_handler_t)(void)
#define DT_ACCELS_WIDGET_TOOLTIP_DISABLED_KEY
const char ** description(struct dt_iop_module_t *self)
int scrolled(struct dt_iop_module_t *self, double x, double y, int up, uint32_t state)
void init(dt_imageio_module_format_t *self)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
GtkTreeStore * store
its model, owned by the view
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
static G_BEGIN_DECLS guint dt_keys_numpad_alternatives(const guint key_val)
Find the numpad equivalent key of any given key. Use this to define/handle alternative shortcuts.
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
G_BEGIN_DECLS struct completion_spec dt_gtkentry_completion_spec
GtkCellRenderer * dtgtk_cell_renderer_button_new(void)
static const dt_iop_drawlayer_params_t * params
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
dt_mipmap_buffer_dsc_flags flags
void dt_osx_disallow_fullscreen(GtkWidget *widget)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
GtkTreeModel * filter_model
guint pending_space_idle_id
gchar * preferred_command
gboolean suppress_inline_once
GtkListStore * recent_entries
gchar * pending_space_query
struct dt_accels_t::scroll scroll
GtkAccelGroup * slideshow_accels
GtkAccelGroup * map_accels
GtkAccelGroup * global_accels
GtkAccelGroup * print_accels
GtkAccelGroup * lighttable_accels
GdkModifierType default_mod_mask
GHashTable * acceleratables
GtkAccelGroup * active_group
GtkAccelGroup * darkroom_accels
GtkAccelGroup * accel_group
gboolean virtual_shortcut