46#include <glib/gi18n.h>
91#ifdef GDK_WINDOWING_QUARTZ
99#define DT_ACCEL_SEARCH_INLINE_SEPARATOR " > "
100#define DT_ACCEL_SEARCH_DISPATCH_RETRY_DELAY_MS 50
129 if(pc->
base) g_closure_unref(pc->
base);
132 if(pc->
widget) g_object_remove_weak_pointer(G_OBJECT(pc->
widget), (gpointer *)&pc->
widget);
141 gtk_widget_set_has_tooltip(widget,
TRUE);
145 const GValue *param_values, gpointer data)
149 if(n_param_values < 5)
return TRUE;
151 GtkWidget *widget = g_value_get_object(¶m_values[0]);
154 if(!gtk_widget_get_has_tooltip(widget))
return TRUE;
157 const char *base_markup = g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-markup");
158 const char *base_text = base_markup ? NULL : g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-text");
159 const gboolean base_none = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-none"));
163 gchar *current_markup = gtk_widget_get_tooltip_markup(widget);
164 if(current_markup && current_markup[0])
166 g_object_set_data_full(G_OBJECT(widget),
"dt-accel-tooltip-base-markup", current_markup, g_free);
167 base_markup = current_markup;
172 gchar *current_text = gtk_widget_get_tooltip_text(widget);
173 if(current_text && current_text[0])
175 g_object_set_data_full(G_OBJECT(widget),
"dt-accel-tooltip-base-text", current_text, g_free);
176 base_text = current_text;
181 g_object_set_data(G_OBJECT(widget),
"dt-accel-tooltip-base-none", GINT_TO_POINTER(1));
189 const char *accel_path = g_object_get_data(G_OBJECT(widget),
"accel-path");
202 gtk_widget_set_tooltip_markup(widget, base_markup);
204 gtk_widget_set_tooltip_text(widget, base_text);
209 if(
IS_NULL_PTR(shortcut_label) || !shortcut_label[0])
216 if(base_markup && base_markup[0])
218 gchar *esc_label = g_markup_escape_text(shortcut_label, -1);
219 gchar *esc_desc = g_markup_escape_text(shortcut_desc, -1);
220 gchar *new_markup = g_strdup_printf(
"%s\n<small>%s: %s</small>", base_markup, esc_desc, esc_label);
221 gtk_widget_set_tooltip_markup(widget, new_markup);
226 else if(base_text && base_text[0])
228 gchar *new_text = g_strdup_printf(
"%s\n%s: %s", base_text, shortcut_desc, shortcut_label);
229 gtk_widget_set_tooltip_text(widget, new_text);
234 gchar *new_text = g_strdup_printf(
"%s: %s", shortcut_desc, shortcut_label);
235 gtk_widget_set_tooltip_text(widget, new_text);
246 static gulong hook_id = 0;
247 if(hook_id != 0)
return;
249 const guint signal_id = g_signal_lookup(
"query-tooltip", GTK_TYPE_WIDGET);
250 if(signal_id == 0)
return;
269 GList *link = g_list_last(shortcut->
closure);
300 for(link = g_list_first(shortcut->
closure); link; link = g_list_next(link))
303 if(closure->base->data == data || closure->parent_data == data)
312 link = g_list_last(shortcut->
closure);
340 gchar **child_parts = g_strsplit(child_shortcut->
path,
"/", -1);
341 guint
n = g_strv_length(child_parts);
343 gchar *parent_path = g_strjoinv (
"/", child_parts);
344 g_strfreev(child_parts);
347 if(!g_strcmp0(parent_shortcut->
path, parent_path))
351 if(parent_closure && child_closure)
376 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
377 guint keyval, GdkModifierType mods, gpointer user_data),
381 pc->
base = g_cclosure_new(G_CALLBACK(action_callback), data, NULL);
387 if(pc->
widget) g_object_add_weak_pointer(G_OBJECT(pc->
widget), (gpointer *)&pc->
widget);
389 g_closure_set_marshal(pc->
base, g_cclosure_marshal_generic);
390 g_closure_ref(pc->
base);
391 g_closure_sink(pc->
base);
411 accels->
keymap = gdk_keymap_get_for_display(gdk_display_get_default());
471 else if(!g_strcmp0(group,
"map") && accels->
map_accels)
476 else if(!g_strcmp0(group,
"print") && accels->
print_accels)
488 fprintf(stderr,
"[dt_accels_connect_active_group] INFO: unknown value: `%s'\n", group);
515 gboolean changed =
FALSE;
526 key->accel_key = shortcut->
key;
527 key->accel_mods = shortcut->
mods;
528 gtk_accel_map_change_entry(shortcut->
path, shortcut->
key, shortcut->
mods,
TRUE);
531 else if(
key->accel_key == shortcut->
key &&
key->accel_mods == shortcut->
mods)
543 shortcut->
key =
key->accel_key;
544 shortcut->
mods =
key->accel_mods;
551 else if(shortcut->
locked && (
key->accel_key != shortcut->
key ||
key->accel_mods != shortcut->
mods))
554 key->accel_key = shortcut->
key;
555 key->accel_mods = shortcut->
mods;
556 gtk_accel_map_change_entry(shortcut->
path, shortcut->
key, shortcut->
mods,
TRUE);
560 else if(
key->accel_key != shortcut->
key ||
key->accel_mods != shortcut->
mods)
562 shortcut->
key =
key->accel_key;
563 shortcut->
mods =
key->accel_mods;
583 if(shortcut->
key != alt_char)
591 gtk_widget_remove_accelerator(shortcut->
widget, shortcut->
accel_group, old_key->accel_key, old_key->accel_mods);
595 if(old_key->accel_key != alt_char)
596 gtk_widget_remove_accelerator(shortcut->
widget, shortcut->
accel_group, alt_char, old_key->accel_mods);
607 gtk_accel_group_disconnect(shortcut->
accel_group, cl);
616 gtk_accel_group_connect(shortcut->
accel_group, shortcut->
key, shortcut->
mods,
flags | GTK_ACCEL_VISIBLE, closure);
650 gtk_accel_map_add_entry(shortcut->
path, shortcut->
key, shortcut->
mods);
673 GdkModifierType mods, gpointer user_data)
677 g_signal_emit_by_name(shortcut->
widget, shortcut->
signal);
683 GdkModifierType mods, gpointer user_data)
689 gtk_widget_grab_focus(shortcut->
widget);
693 GdkKeymapKey *keys = NULL;
695 GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
696 if(gdk_keymap_get_entries_for_keyval(keymap, shortcut->
key, &keys, &
n))
698 if(
n > 0) keycode = keys[0].keycode;
703 GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS);
704 ev->key.window = g_object_ref(gtk_widget_get_window(shortcut->
widget));
705 ev->key.send_event =
TRUE;
706 ev->key.time = GDK_CURRENT_TIME;
707 ev->key.state = shortcut->
mods;
708 ev->key.keyval = shortcut->
key;
709 ev->key.hardware_keycode = keycode;
711 ev->key.is_modifier =
FALSE;
714 gtk_widget_event(shortcut->
widget, ev);
722 GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
729 if(shortcut && shortcut->
widget == widget)
739 shortcut->
widget = widget;
741 shortcut->
path = g_strdup(accel_path);
743 shortcut->
key = key_val;
744 shortcut->
mods = accel_mods;
748 shortcut->
description = _(
"Contextual interaction on focus");
749 shortcut->
accels = accels;
757 gboolean (*action_callback)(GtkAccelGroup *group,
758 GObject *acceleratable, guint keyval,
759 GdkModifierType mods, gpointer user_data),
760 gpointer data, GtkAccelGroup *accel_group,
const gchar *action_scope,
761 const gchar *action_name)
776 shortcut->
path = g_strdup(accel_path);
784 shortcut->
accels = accels;
797 GtkAccelGroup *accel_group,
const gchar *accel_path, guint key_val,
798 GdkModifierType accel_mods,
const gboolean
lock)
805 if(shortcut && shortcut->
widget == widget)
814 GtkAccelKey
key = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
816 shortcut->
widget = widget;
825 shortcut->
widget = widget;
827 shortcut->
path = g_strdup(accel_path);
828 shortcut->
signal = signal;
829 shortcut->
key = key_val;
830 shortcut->
mods = accel_mods;
835 shortcut->
accels = accels;
853 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
854 guint keyval, GdkModifierType mods,
856 gpointer data,
GtkWidget *target_widget, GtkAccelGroup *accel_group,
857 const gchar *action_scope,
const gchar *action_name, guint key_val,
858 GdkModifierType accel_mods,
const gboolean
lock,
const char *
description)
869 if(closure && closure->data == data)
890 shortcut->
path = g_strdup(accel_path);
892 shortcut->
key = key_val;
893 shortcut->
mods = accel_mods;
898 shortcut->
accels = accels;
918 GtkAccelKey
key = { 0 };
921 const gboolean is_known = gtk_accel_map_lookup_entry(shortcut->
path, &
key);
922 if(!is_known)
return;
925 const GtkAccelKey oldkey = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
933 const gboolean needs_cleanup = changed && oldkey.accel_key > 0 && oldtype !=
DT_SHORTCUT_UNSET;
936 const gboolean needs_init = changed &&
key.accel_key > 0;
974 if(g_strrstr(shortcut->
path, params->path) != NULL)
1005 params->path = path;
1006 params->data = data;
1025 if(strncmp(scope,
"<Ansel>/", strlen(
"<Ansel>/")) == 0)
1026 return g_strdup_printf(
"%s/%s", scope, feature);
1028 return g_strdup_printf(
"<Ansel>/%s/%s", scope, feature);
1036 gdk_event_get_state(event, mods);
1041#ifdef GDK_WINDOWING_QUARTZ
1050 if((*mods & GDK_MOD2_MASK) && (*mods & GDK_META_MASK))
1051 *mods &= ~GDK_META_MASK;
1055 GdkModifierType consumed;
1056 gdk_keymap_translate_keyboard_state(accels->
keymap, event->key.hardware_keycode, event->key.state,
1058 keyval, NULL, NULL, &consumed);
1062 gchar *accel_name = gtk_accelerator_name(*keyval, *mods);
1064 (event->type == GDK_KEY_PRESS) ?
"Key pressed" :
"Key released", accel_name);
1071 if(gdk_keyval_to_lower(*keyval) == gdk_keyval_to_upper(*keyval))
1079 if(*keyval == GDK_KEY_ISO_Left_Tab)
1081 *keyval = GDK_KEY_Tab;
1082 *mods |= GDK_SHIFT_MASK;
1102 return gdk_keyval_to_lower(keyval);
1109 const gchar *path = (
const gchar *)
key;
1118 && shortcut_key == result_key
1121 if(!g_strcmp0(path, shortcut->
path))
1124 dt_widget_log(
"[shortcuts] Found accel %s for typed keys\n", path);
1128 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1145 GList *item = g_list_first(result.
results);
1146 if(item) path = (
char *)item->data;
1156 const gchar *path = (
const gchar *)
key;
1165 && shortcut_key == result_key
1169 if(!g_strcmp0(path, shortcut->
path))
1172 dt_widget_log(
"[shortcuts] Found accel %s for typed keys\n", path);
1176 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1182 GdkModifierType mods)
1191 GList *item = g_list_first(result.
results);
1204 gchar *accel_name = gtk_accelerator_name(keyval, mods);
1205 dt_widget_log(
"[shortcuts] Combination of keys decoded: %s\n", accel_name);
1212 dt_widget_log(
"[shortcuts] Active group action executed\n");
1220 dt_widget_log(
"[shortcuts] Global group action executed\n");
1237 if(!(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE || event->type == GDK_SCROLL))
1241 if(event->type == GDK_SCROLL)
1250 GdkModifierType mods;
1268 if(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
1270 GtkWidget *focused = gtk_window_get_focus(GTK_WINDOW(w));
1271 if(!
IS_NULL_PTR(focused) && (GTK_IS_EDITABLE(focused) || GTK_IS_TEXT_VIEW(focused)))
1279 if(event->type == GDK_KEY_PRESS &&
1287 else if(event->type == GDK_KEY_RELEASE)
1344 GtkTreeIter *iter, gpointer data)
1348 g_object_set(renderer,
1351 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
1356 GtkTreeIter *iter, gpointer data)
1360 g_object_set (renderer,
1361 "icon-name", (!
IS_NULL_PTR(shortcut) && !shortcut->
locked) ?
"edit-delete-symbolic" :
"lock",
1374 if(!gdk_keymap_get_entries_for_keycode(accels->
keymap, hardware_keycode, &keys, &keyvals, &n_keys))
1377 for(
int i = 0;
i < n_keys; ++
i)
1379 if(keyvals[
i] == keyval)
1381 int group = keys[
i].group;
1394 guint hardware_key, gpointer user_data)
1398 GtkTreeModel *filter = GTK_TREE_MODEL(user_data);
1399 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
1402 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1408 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(filter), &f_iter, path))
1409 gtk_tree_model_get(GTK_TREE_MODEL(filter), &f_iter,
COL_SHORTCUT, &shortcut, -1);
1411 const char *shortcut_path = NULL;
1417 if(keyval == GDK_KEY_VoidSymbol
1418 || (mods == 0 && (keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace)))
1427 if(keyval != 0 || mods != 0)
1429 GdkDisplay *display = gdk_display_get_default();
1430 GdkSeat *seat = gdk_display_get_default_seat(display);
1431 GdkDevice *pointer = gdk_seat_get_pointer(seat);
1432 GdkModifierType
state;
1433 gdk_device_get_state(pointer, gdk_get_default_root_window(), NULL, &
state);
1437 GdkEventKey
event = { 0 };
1438 event.type = GDK_KEY_PRESS;
1439 event.state = mods |
state;
1440 event.keyval = keyval;
1441 event.hardware_keycode = hardware_key;
1449 if(!(keyval == 0 && mods == 0))
1453 if(
IS_NULL_PTR(shortcut_path) && gtk_accel_map_change_entry(shortcut->
path, keyval, mods,
FALSE))
1463 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter), &s_iter, &f_iter);
1477 = 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",
1478 _(
"The shortcut for"), shortcut_path,
1479 _(
"is already using the key combination"), new_text,
1480 _(
"Delete it first."));
1481 gtk_dialog_run(GTK_DIALOG(dlg));
1482 gtk_widget_destroy(dlg);
1486 gtk_tree_path_free(path);
1489static void _shortcut_cleared(GtkCellRendererAccel *renderer,
const gchar *path_string, gpointer user_data)
1491 _shortcut_edited(GTK_CELL_RENDERER(renderer), path_string, 0, 0, 0, user_data);
1496 GdkRectangle *background, GdkRectangle *cell_area, GtkCellRendererState
flags,
1508 gtk_tree_store_set(
store, iter,
1523 const gchar *path = (
const gchar *)
key;
1530 GtkTreeIter *parent = NULL;
1531 GtkTreeIter *iter = NULL;
1538 gchar **parts = g_strsplit(path,
"/", -1);
1539 gchar *accum = g_strdup(
"<Ansel>");
1543 const size_t len_ansel = strlen(accum);
1544 for(
int i = 1; parts[
i]; ++
i)
1547 gchar *tmp = g_strconcat(accum,
"/", parts[
i], NULL);
1553 iter = g_hash_table_lookup(node_cache, accum);
1560 GtkTreeIter new_iter;
1561 gtk_tree_store_append(
store, &new_iter, parent);
1564 iter = g_new(GtkTreeIter, 1);
1566 g_hash_table_insert(node_cache, g_strdup(accum), iter);
1570 gchar *label = g_strdup(parts[
i]);
1574 if(!g_strcmp0(accum, path))
1592 gchar **parts = g_strsplit(path,
"/", -1);
1593 const gint len = g_strv_length(parts);
1596 tail = g_strjoinv(
"/", parts + 2);
1598 tail = g_strdup(parts[1]);
1600 tail = g_strdup(parts[0]);
1610 const gchar *path = (
const gchar *)
key;
1612 GtkListStore *
store = (GtkListStore *)user_data;
1624 gchar **tail_parts = g_strsplit(tail,
"/", -1);
1625 const gint tail_len = g_strv_length(tail_parts);
1626 const gchar *leaf = tail;
1627 if(tail_len > 0 && !
IS_NULL_PTR(tail_parts[tail_len - 1]) && tail_parts[tail_len - 1][0] !=
'\0')
1628 leaf = tail_parts[tail_len - 1];
1631 gtk_list_store_append(
store, &iter);
1632 gtk_list_store_set(
store, &iter,
1641 g_strfreev(tail_parts);
1650 gchar *pa = NULL, *pb = NULL;
1651 gtk_tree_model_get(
model, a, 2, &ka, 0, &pa, -1);
1652 gtk_tree_model_get(
model, b, 2, &kb, 0, &pb, -1);
1664 gchar *pa_ci = g_utf8_casefold(pa, -1);
1665 gchar *pb_ci = g_utf8_casefold(pb, -1);
1666 gchar **pa_parts = g_strsplit(pa_ci,
"/", -1);
1667 gchar **pb_parts = g_strsplit(pb_ci,
"/", -1);
1669 for(gint
i = 0;;
i++)
1671 const gchar *pa_part = pa_parts[
i];
1672 const gchar *pb_part = pb_parts[
i];
1689 ret = g_utf8_collate(pa_part, pb_part);
1693 g_strfreev(pb_parts);
1694 g_strfreev(pa_parts);
1707 gtk_tree_model_get(
model, a, GPOINTER_TO_INT(data), &ka, -1);
1708 gtk_tree_model_get(
model, b, GPOINTER_TO_INT(data), &kb, -1);
1714 gchar *ka_ci = g_utf8_casefold(ka, -1);
1715 gchar *kb_ci = g_utf8_casefold(kb, -1);
1718 res = g_utf8_collate(ka_ci, kb_ci);
1742 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(params->path_search));
1743 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(params->keys_search));
1745 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1746 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1749 gboolean show =
TRUE;
1754 if(needle_path && needle_path[0])
1756 if(path && path[0] !=
'\0')
1758 gchar *needle_ci = g_utf8_casefold(needle_path, -1);
1759 gchar *haystack_ci = g_utf8_casefold(path, -1);
1760 show &= (g_strrstr(haystack_ci, needle_ci) != NULL);
1772 if(needle_keys && needle_keys[0] !=
'\0')
1774 guint search_keyval = 0;
1775 GdkModifierType search_mods = 0;
1776 gtk_accelerator_parse(needle_keys, &search_keyval, &search_mods);
1777 if(search_keyval || search_mods)
1780 GdkModifierType mods = 0;
1787 if(search_keyval && search_mods)
1788 show &= (keyval == search_keyval && mods == search_mods);
1790 show &= ((keyval && keyval == search_keyval) || (mods && mods == search_mods));
1799 if(show)
return TRUE;
1802 if(gtk_tree_model_iter_has_child(
model, iter))
1805 if(gtk_tree_model_iter_children(
model, &child, iter))
1811 }
while(gtk_tree_model_iter_next(
model, &child));
1821 GtkTreeView *tree_view = GTK_TREE_VIEW(params->tree_view);
1822 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(tree_view)));
1825 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(params->path_search));
1826 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(params->keys_search));
1828 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1829 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1830 gtk_tree_view_collapse_all(GTK_TREE_VIEW(params->tree_view));
1832 gtk_tree_view_expand_all(GTK_TREE_VIEW(params->tree_view));
1842 params->keys_search = gtk_search_entry_new();
1843 params->path_search = gtk_search_entry_new();
1844 GtkWidget *tree_view = params->tree_view = gtk_tree_view_new();
1850 = { {
"Primary>", N_(
"<Primary> - Decoded as <Control> on Windows/Linux or <Meta> on Mac OS") },
1851 {
"Control>", N_(
"<Control>") },
1852 {
"Shift>", N_(
"<Shift>") },
1853 {
"Alt>", N_(
"<Alt>") },
1854 {
"Super>", N_(
"<Super> - The Windows key on PC") },
1855 {
"Hyper>", N_(
"<Hyper>") },
1856 {
"Meta>", N_(
"<Meta> - Decoded as <Command> on Mac OS") },
1859 gtk_widget_set_tooltip_text(params->keys_search, _(
"Look for keys and modifiers codes, as `<Modifier>Key`.\n"
1860 "Type `<` to start the auto-completion"));
1862 gtk_widget_set_tooltip_text(params->path_search, _(
"Case-insensitive search for keywords of full pathes.\n"
1863 "Ex: `darkroom/controls/sliders`"));
1867 gtk_window_set_title(GTK_WINDOW(dialog), _(
"Ansel - Keyboard shortcuts"));
1869#ifdef GDK_WINDOWING_QUARTZ
1871 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1874 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1875 gtk_window_set_modal(GTK_WINDOW(dialog),
TRUE);
1876 gtk_window_set_transient_for(GTK_WINDOW(dialog), main_window);
1877 gtk_window_set_default_size(GTK_WINDOW(dialog), 1100, 900);
1880 GtkTreeStore *
store = gtk_tree_store_new(
NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1881 G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
1887 g_hash_table_destroy(node_cache);
1893 GINT_TO_POINTER(
i), NULL);
1894 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store),
i, GTK_SORT_ASCENDING);
1898 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
1899 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
filter_callback, params, NULL);
1902 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), filter_model);
1903 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view),
COL_PATH);
1904 gtk_widget_set_hexpand(tree_view,
TRUE);
1905 gtk_widget_set_vexpand(tree_view,
TRUE);
1906 gtk_widget_set_halign(tree_view, GTK_ALIGN_FILL);
1907 gtk_widget_set_valign(tree_view, GTK_ALIGN_FILL);
1909 g_signal_connect(G_OBJECT(params->path_search),
"changed", G_CALLBACK(
search_changed), params);
1910 g_signal_connect(G_OBJECT(params->keys_search),
"changed", G_CALLBACK(
search_changed), params);
1913 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_(
"View / Scope / Feature / Control"), gtk_cell_renderer_text_new(),
"text",
COL_NAME, NULL);
1914 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1916 GtkCellRenderer *renderer = gtk_cell_renderer_accel_new();
1917 column = gtk_tree_view_column_new_with_attributes(_(
"Keys"), renderer,
"accel-key",
COL_KEYVAL,
"accel-mods",
1920 g_signal_connect(renderer,
"accel-edited", G_CALLBACK(
_shortcut_edited), filter_model);
1921 g_signal_connect(renderer,
"accel-cleared", G_CALLBACK(
_shortcut_cleared), filter_model);
1922 gtk_tree_view_column_set_min_width(column, 100);
1923 gtk_tree_view_column_set_resizable(column,
TRUE);
1924 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1927 g_object_set(renderer,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
1928 column = gtk_tree_view_column_new_with_attributes(_(
"Clear"), renderer,
"pixbuf",
COL_CLEAR, NULL);
1930 g_signal_connect(renderer,
"activate", G_CALLBACK(
_icon_activate), filter_model);
1931 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1933 column = gtk_tree_view_column_new_with_attributes(_(
"Description"), gtk_cell_renderer_text_new(),
"text",
COL_DESCRIPTION, NULL);
1934 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1938 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), box,
TRUE,
TRUE, 0);
1941 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by feature : ")),
FALSE,
FALSE, 0);
1942 gtk_box_pack_start(GTK_BOX(hbox), params->path_search,
TRUE,
TRUE, 0);
1943 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by keys : ")),
FALSE,
FALSE, 0);
1944 gtk_box_pack_start(GTK_BOX(hbox), params->keys_search,
TRUE,
TRUE, 0);
1945 gtk_box_pack_start(GTK_BOX(box), hbox,
FALSE,
FALSE, 0);
1947 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1949 gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
1950 gtk_box_pack_start(GTK_BOX(box), scrolled_window,
TRUE,
TRUE, 0);
1952 gtk_widget_set_visible(tree_view,
TRUE);
1953 gtk_widget_show_all(dialog);
1955 gtk_dialog_run(GTK_DIALOG(dialog));
1956 gtk_widget_destroy(dialog);
1957 g_object_unref(filter_model);
1958 g_object_unref(
store);
1970 if(
IS_NULL_PTR(needle) || needle[0] ==
'\0')
return 0;
1974 gtk_tree_model_get(
model, iter, 0, &label, -1);
1982 gchar *label_ci = g_utf8_casefold(label, -1);
1984 gchar **parts = g_strsplit(label_ci,
"/", -1);
1985 gchar *needle_copy = g_strdup(needle);
1986 gchar **tokens = g_strsplit_set(needle_copy,
" \t\r\n", -1);
1988 gboolean has_token =
FALSE;
1989 gboolean all_matched =
TRUE;
1992 if(tokens[
t][0] ==
'\0')
continue;
1994 int best_token_rank = INT_MAX;
1999 const char *match = g_strstr_len(parts[
i], -1, tokens[
t]);
2002 const int cell_rank =
i * 10000;
2003 const int in_cell_rank = match - parts[
i];
2004 const int token_rank = cell_rank + in_cell_rank;
2005 if(token_rank < best_token_rank) best_token_rank = token_rank;
2008 if(best_token_rank == INT_MAX)
2010 all_matched =
FALSE;
2013 rank_sum += best_token_rank;
2016 if(all_matched) ret = has_token ? rank_sum : 0;
2030 const gchar *needle = gtk_entry_get_text(GTK_ENTRY(
search_entry));
2031 gchar *needle_query = g_strdup(!
IS_NULL_PTR(needle) ? needle :
"");
2034 g_strstrip(needle_query);
2035 gchar *needle_ci = g_utf8_casefold(needle_query, -1);
2039 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
2040 GTK_SORT_ASCENDING);
2043 if(gtk_tree_model_get_iter_first(
model, &iter))
2048 gtk_list_store_set(GTK_LIST_STORE(
model), &iter, 2, rank, -1);
2050 }
while(gtk_tree_model_iter_next(
model, &iter));
2057 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), 2, GTK_SORT_ASCENDING);
2058 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
model));
2079#define DT_ACCEL_SEARCH_RECENT_KEY "plugins/accel_search/recent_entries"
2080#define DT_ACCEL_SEARCH_RECENT_MAX 20
2085 gtk_list_store_clear(
store);
2096 if(entry[0] ==
'\0')
2104 gchar **parts = g_strsplit(entry,
"\t", 3);
2106 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
2113 const gchar *query = parts[0];
2114 const gchar *command = parts[1];
2119 gtk_list_store_append(
store, &iter);
2120 gtk_list_store_set(
store, &iter,
2137 const gchar *search_text =
"";
2140 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2141 if(!
IS_NULL_PTR(entry_text)) search_text = entry_text;
2144 gchar *query_text = g_strdup(search_text);
2147 g_strstrip(query_text);
2148 gchar *query_ci = g_utf8_casefold(query_text, -1);
2149 const gboolean has_query = query_ci[0] !=
'\0';
2151 gchar *a_query = NULL, *a_command = NULL;
2152 gchar *b_query = NULL, *b_command = NULL;
2153 gint a_recent = G_MAXINT, b_recent = G_MAXINT;
2154 gtk_tree_model_get(
model, a, 0, &a_query, 1, &a_command, 4, &a_recent, -1);
2155 gtk_tree_model_get(
model, b, 0, &b_query, 1, &b_command, 4, &b_recent, -1);
2157 gchar *a_query_ci = g_utf8_casefold(!
IS_NULL_PTR(a_query) ? a_query :
"", -1);
2158 gchar *a_command_ci = g_utf8_casefold(!
IS_NULL_PTR(a_command) ? a_command :
"", -1);
2159 gchar *b_query_ci = g_utf8_casefold(!
IS_NULL_PTR(b_query) ? b_query :
"", -1);
2160 gchar *b_command_ci = g_utf8_casefold(!
IS_NULL_PTR(b_command) ? b_command :
"", -1);
2162 gint a_rank = 400000, b_rank = 400000;
2165 if(g_str_has_prefix(a_query_ci, query_ci))
2167 else if(g_str_has_prefix(a_command_ci, query_ci))
2171 const gchar *a_match_query = g_strstr_len(a_query_ci, -1, query_ci);
2173 a_rank = 200000 + (a_match_query - a_query_ci);
2176 const gchar *a_match_command = g_strstr_len(a_command_ci, -1, query_ci);
2178 a_rank = 300000 + (a_match_command - a_command_ci);
2182 if(g_str_has_prefix(b_query_ci, query_ci))
2184 else if(g_str_has_prefix(b_command_ci, query_ci))
2188 const gchar *b_match_query = g_strstr_len(b_query_ci, -1, query_ci);
2190 b_rank = 200000 + (b_match_query - b_query_ci);
2193 const gchar *b_match_command = g_strstr_len(b_command_ci, -1, query_ci);
2195 b_rank = 300000 + (b_match_command - b_command_ci);
2200 gint ret = a_rank - b_rank;
2201 if(ret == 0) ret = a_recent - b_recent;
2202 if(ret == 0) ret = g_utf8_collate(a_query_ci, b_query_ci);
2203 if(ret == 0) ret = g_utf8_collate(a_command_ci, b_command_ci);
2223 gchar *trimmed = g_strdup(query);
2224 g_strstrip(trimmed);
2225 if(trimmed[0] ==
'\0')
2231 gchar *command = g_strdup(shortcut->
path);
2232 g_strdelimit(trimmed,
"\t\r\n",
' ');
2233 g_strdelimit(command,
"\t\r\n",
' ');
2235 GPtrArray *
entries = g_ptr_array_new_with_free_func(g_free);
2236 GPtrArray *entries_ci = g_ptr_array_new_with_free_func(g_free);
2237 g_ptr_array_add(
entries, g_strdup_printf(
"%s\t%s", trimmed, command));
2238 g_ptr_array_add(entries_ci, g_utf8_casefold(trimmed, -1));
2243 if(
IS_NULL_PTR(candidate) || candidate[0] ==
'\0')
2248 g_strstrip(candidate);
2249 if(candidate[0] ==
'\0')
2255 gchar **parts = g_strsplit(candidate,
"\t", 3);
2257 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
2264 const gchar *candidate_query = parts[0];
2265 const gchar *candidate_command = parts[1];
2267 gchar *candidate_ci = g_utf8_casefold(candidate_query, -1);
2268 gboolean found =
FALSE;
2269 for(guint
k = 0;
k < entries_ci->len;
k++)
2271 const char *kept_ci = g_ptr_array_index(entries_ci,
k);
2272 if(!g_strcmp0(kept_ci, candidate_ci))
2285 g_ptr_array_add(
entries, g_strdup_printf(
"%s\t%s", candidate_query, candidate_command));
2286 g_ptr_array_add(entries_ci, candidate_ci);
2293 const gchar *entry_value = (
i <
entries->len) ? (
const gchar *)g_ptr_array_index(
entries,
i) :
"";
2297 g_ptr_array_free(entries_ci,
TRUE);
2322 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
state->recent_entries));
2324 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(
state->filter_model));
2326 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2327 gtk_tree_selection_unselect_all(selection);
2330 gboolean has_iter = gtk_tree_model_get_iter_first(
state->filter_model, &iter);
2331 GtkTreeIter fallback_iter = iter;
2332 GtkTreeIter selected_iter = iter;
2333 gboolean found_preferred =
FALSE;
2339 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2341 && !g_strcmp0(shortcut->
path,
state->preferred_command))
2343 selected_iter = iter;
2344 found_preferred =
TRUE;
2347 }
while(gtk_tree_model_iter_next(
state->filter_model, &iter));
2352 GtkTreeIter *target_iter = found_preferred ? &selected_iter : &fallback_iter;
2353 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, target_iter);
2355 gtk_tree_selection_select_iter(selection, target_iter);
2356 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2357 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2358 gtk_tree_path_free(path);
2360 gtk_tree_model_get(
state->filter_model, target_iter, 1, &
state->selected, -1);
2365 GtkTreeIter *iter, gpointer user_data)
2368 gchar *query = NULL;
2369 gchar *command = NULL;
2370 gtk_tree_model_get(
model, iter, 0, &query, 1, &command, -1);
2375 state->preferred_command = NULL;
2378 state->preferred_command = g_strdup(command);
2382 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
2383 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2395 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2396 if(
state->suppress_inline_once)
2402 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2404 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2406 const gchar *last = g_utf8_find_prev_char(entry_text, entry_text + strlen(entry_text));
2407 const gunichar last_char = !
IS_NULL_PTR(last) ? g_utf8_get_char(last) : 0;
2408 if(last_char != 0 && !g_unichar_isalnum(last_char))
2411 gchar *query = g_strdup(!
IS_NULL_PTR(entry_text) ? entry_text :
"");
2415 if(query[0] ==
'\0')
2421 gchar *query_ci = g_utf8_casefold(query, -1);
2422 if(query_ci[0] ==
'\0')
2429 gint best_rank = G_MAXINT;
2430 gint best_recent = G_MAXINT;
2431 gchar *best_command = NULL;
2432 gchar *best_display = NULL;
2433 gchar *best_query_ci = NULL;
2434 gchar *best_command_ci = NULL;
2435 const glong query_len = g_utf8_strlen(query_ci, -1);
2438 if(gtk_tree_model_get_iter_first(
model, &iter))
2442 gchar *row_query = NULL;
2443 gchar *row_command = NULL;
2444 gchar *row_display = NULL;
2445 gint row_recent = G_MAXINT;
2446 gtk_tree_model_get(
model, &iter, 0, &row_query, 1, &row_command, 3, &row_display, 4, &row_recent, -1);
2455 gchar *row_query_ci = g_utf8_casefold(row_query, -1);
2456 gchar *row_command_ci = g_utf8_casefold(!
IS_NULL_PTR(row_command) ? row_command :
"", -1);
2457 gint row_rank = G_MAXINT;
2458 if(g_str_has_prefix(row_query_ci, query_ci))
2461 const glong row_query_len = g_utf8_strlen(row_query_ci, -1);
2462 row_rank =
MAX((gint)(row_query_len - query_len), 0);
2464 else if(g_str_has_prefix(row_command_ci, query_ci))
2466 const glong row_command_len = g_utf8_strlen(row_command_ci, -1);
2467 row_rank = 100000 +
MAX((gint)(row_command_len - query_len), 0);
2471 const gchar *match_query = g_strstr_len(row_query_ci, -1, query_ci);
2473 row_rank = 200000 + (match_query - row_query_ci);
2476 const gchar *match_command = g_strstr_len(row_command_ci, -1, query_ci);
2478 row_rank = 300000 + (match_command - row_command_ci);
2482 if(row_rank < best_rank
2483 || (row_rank == best_rank && row_recent < best_recent)
2484 || (row_rank == best_rank && row_recent == best_recent
2485 && (!
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) < 0))
2486 || (row_rank == best_rank && row_recent == best_recent
2487 && !
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) == 0
2488 && (!
IS_NULL_PTR(best_command_ci) && g_utf8_collate(row_command_ci, best_command_ci) < 0)))
2490 best_rank = row_rank;
2491 best_recent = row_recent;
2496 best_command = g_strdup(!
IS_NULL_PTR(row_command) ? row_command :
"");
2497 best_display = g_strdup(!
IS_NULL_PTR(row_display) ? row_display : row_query);
2498 best_query_ci = g_strdup(row_query_ci);
2499 best_command_ci = g_strdup(row_command_ci);
2507 }
while(gtk_tree_model_iter_next(
model, &iter));
2510 if(!
IS_NULL_PTR(best_command) && best_command[0] !=
'\0' && best_rank < G_MAXINT)
2515 state->preferred_command = NULL;
2517 state->preferred_command = g_strdup(best_command);
2519 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2520 GtkTreeIter filter_iter;
2521 if(gtk_tree_model_get_iter_first(
state->filter_model, &filter_iter))
2526 gchar *shortcut_path_display = NULL;
2527 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &shortcut, 0, &shortcut_path_display, -1);
2528 gchar *shortcut_trimmed = NULL;
2532 && (!g_strcmp0(shortcut->
path, best_command)
2533 || (!
IS_NULL_PTR(shortcut_trimmed) && !g_strcmp0(shortcut_trimmed, best_command))
2534 || (!
IS_NULL_PTR(shortcut_path_display) && !g_strcmp0(shortcut_path_display, best_command))))
2536 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &filter_iter);
2540 dt_free(shortcut_path_display);
2543 gtk_tree_selection_select_iter(selection, &filter_iter);
2544 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2545 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2546 gtk_tree_path_free(path);
2547 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &
state->selected, -1);
2549 dt_free(shortcut_path_display);
2553 dt_free(shortcut_path_display);
2554 }
while(gtk_tree_model_iter_next(
state->filter_model, &filter_iter));
2557 if(!
IS_NULL_PTR(best_display) && best_display[0] !=
'\0')
2559 const gchar *current = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2560 if(g_strcmp0(current, best_display))
2562 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), best_display);
2564 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1));
2565 gtk_editable_select_region(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1), -1);
2593 GValue params[4] = { G_VALUE_INIT };
2595 g_value_init(¶ms[0], G_TYPE_POINTER);
2596 g_value_set_pointer(¶ms[0], shortcut->
accel_group);
2598 g_value_init(¶ms[1], G_TYPE_POINTER);
2599 g_value_set_pointer(¶ms[1], G_OBJECT(main_window));
2601 g_value_init(¶ms[2], G_TYPE_UINT);
2602 g_value_set_uint(¶ms[2], shortcut->
key);
2604 g_value_init(¶ms[3], G_TYPE_UINT);
2605 g_value_set_uint(¶ms[3], shortcut->
mods);
2607 GValue ret = G_VALUE_INIT;
2608 g_value_init (&ret, G_TYPE_BOOLEAN);
2613 for(
int k = 0;
k < 4;
k++) g_value_unset(¶ms[
k]);
2614 g_value_unset(&ret);
2618 g_closure_invoke(active_closure, &ret, 4, params, NULL);
2619 const gboolean handled = g_value_get_boolean(&ret);
2621 for(
int k = 0;
k < 4;
k++) g_value_unset(¶ms[
k]);
2622 g_value_unset(&ret);
2640 dt_widget_log(
"[accel_search] dispatch skipped: shortcut '%s' no longer exists\n",
2649 for(GList *item = g_list_last(shortcut->
closure); item; item = g_list_previous(item))
2660 || gtk_widget_is_ancestor(candidate_widget, GTK_WIDGET(
state->main_window));
2661 if(in_main_window &&
IS_NULL_PTR(payload_in_main_window))
2662 payload_in_main_window = candidate;
2663 if(in_main_window && gtk_widget_get_visible(candidate_widget) && gtk_widget_get_mapped(candidate_widget))
2665 payload = candidate;
2671 if(
IS_NULL_PTR(payload)) payload = payload_in_main_window;
2676 target_widget = payload->
widget;
2678 target_widget = shortcut->
widget;
2696 g_object_add_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2698 g_object_add_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2704 dt_widget_log(
"[accel_search] dispatch closure target='%s' description='%s' handled=%d\n",
2705 path, desc, handled);
2709 const gboolean activated = gtk_widget_activate(shortcut_widget);
2710 dt_widget_log(
"[accel_search] dispatch widget target='%s' description='%s' activated=%d widget=%s\n",
2711 path, desc, activated,
2712 !
IS_NULL_PTR(shortcut_widget) ? gtk_widget_get_name(shortcut_widget) :
"<destroyed>");
2716 dt_widget_log(
"[accel_search] dispatch failed: no callable target for '%s' description='%s'\n",
2725 focused_widget = gtk_window_get_focus(
state->main_window);
2729 gboolean target_focused_gtk =
FALSE;
2730 gboolean target_focused_scroll =
FALSE;
2735 target_focused_gtk = focused_widget == target_widget
2736 || gtk_widget_is_ancestor(focused_widget, target_widget)
2737 || gtk_widget_is_ancestor(target_widget, focused_widget);
2741 target_focused_scroll = scroll_focused_widget == target_widget
2742 || gtk_widget_is_ancestor(scroll_focused_widget, target_widget)
2743 || gtk_widget_is_ancestor(target_widget, scroll_focused_widget);
2746 const gboolean target_focused = target_focused_gtk || target_focused_scroll;
2748 dt_widget_log(
"[accel_search] focus check (pre-idle) target='%s' target_widget=%s(%p) gtk_focus=%s(%p)"
2749 " scroll_focus=%s(%p) target_focused_gtk=%d target_focused_scroll=%d target_focused=%d\n",
2751 !
IS_NULL_PTR(target_widget) ? gtk_widget_get_name(target_widget) :
"<null>",
2752 (
void *)target_widget,
2753 !
IS_NULL_PTR(focused_widget) ? gtk_widget_get_name(focused_widget) :
"<null>",
2754 (
void *)focused_widget,
2755 !
IS_NULL_PTR(scroll_focused_widget) ? gtk_widget_get_name(scroll_focused_widget) :
"<null>",
2756 (
void *)scroll_focused_widget,
2757 target_focused_gtk, target_focused_scroll, target_focused);
2762 && !g_strcmp0(G_OBJECT_TYPE_NAME(target_widget),
"DtBauhausWidget"))
2765 retry->
path = g_strdup(path);
2769 dt_widget_log(
"[accel_search] dispatch retry scheduled target='%s' retry=%u\n",
2778 g_object_remove_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2780 g_object_remove_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2788 g_free(
state->path);
2790 return G_SOURCE_REMOVE;
2796 gtk_tree_model_get(
model, iter, 2, &rank, -1);
2801 GtkTreeIter *iter, gpointer user_data)
2805 gchar *key_query = g_strdup(
key);
2808 g_strstrip(key_query);
2809 if(key_query[0] ==
'\0')
2815 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2822 gchar *query = NULL;
2823 gtk_tree_model_get(
model, iter, 0, &query, -1);
2830 gchar *key_ci = g_utf8_casefold(key_query, -1);
2831 gchar *query_ci = g_utf8_casefold(query, -1);
2832 const gboolean match = !
IS_NULL_PTR(g_strrstr(query_ci, key_ci));
2844 const gchar *query_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2845 gchar *query = g_strdup(!
IS_NULL_PTR(query_text) ? query_text :
"");
2849 dt_widget_log(
"[accel_search] validate query='%s' shortcut='%s' description='%s'\n",
2855 state->selected = shortcut;
2856 state->response = GTK_RESPONSE_ACCEPT;
2857 gtk_widget_destroy(
window);
2865 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
2867 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2871 state->selected = NULL;
2876 GtkTreeViewColumn *column, gpointer user_data)
2880 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
return FALSE;
2883 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2889 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2891 if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
2893 if(!gtk_tree_model_get_iter_first(
state->filter_model, &iter))
return TRUE;
2897 if(!gtk_tree_model_iter_next(
state->filter_model, &iter))
return TRUE;
2901 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2903 if(!gtk_tree_path_prev(path))
2905 gtk_tree_path_free(path);
2909 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
2911 gtk_tree_path_free(path);
2914 gtk_tree_path_free(path);
2917 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2919 gtk_tree_selection_select_iter(selection, &iter);
2920 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2921 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2922 gtk_tree_path_free(path);
2923 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2933 return G_SOURCE_REMOVE;
2935 gchar *with_space = g_strconcat(
state->pending_space_query,
" ", NULL);
2936 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), with_space);
2937 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2940 state->pending_space_query = NULL;
2941 return G_SOURCE_REMOVE;
2945 GdkEventKey *event, gpointer user_data)
2950 if(
key == GDK_KEY_Escape)
2952 state->response = GTK_RESPONSE_CANCEL;
2953 gtk_widget_destroy(
state->window);
2957 if(
key == GDK_KEY_Down)
2959 if(
key == GDK_KEY_Up)
2961 if(
key == GDK_KEY_Return)
2964 gchar *command = NULL;
2967 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2968 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2973 : g_strdup(entry_text);
2978 g_strstrip(command);
2979 if(command[0] !=
'\0')
2982 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(
state->store), &iter))
2987 gchar *candidate_path = NULL;
2988 gtk_tree_model_get(GTK_TREE_MODEL(
state->store), &iter, 1, &candidate, 0, &candidate_path, -1);
2989 gchar *candidate_display_path = NULL;
2993 && (!g_strcmp0(command, candidate->
path)
2994 || !g_strcmp0(command, candidate_path)
2996 && !g_strcmp0(command, candidate_display_path))))
2998 shortcut = candidate;
3000 dt_free(candidate_display_path);
3004 dt_free(candidate_display_path);
3005 }
while(gtk_tree_model_iter_next(GTK_TREE_MODEL(
state->store), &iter));
3017 if(
key == GDK_KEY_space)
3023 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
3026 gchar *query_only = NULL;
3027 gint sel_start = 0, sel_end = 0;
3028 const gboolean
has_selection = gtk_editable_get_selection_bounds(GTK_EDITABLE(
state->search_entry),
3029 &sel_start, &sel_end);
3031 : gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
3032 const gint text_chars = g_utf8_strlen(entry_text, -1);
3033 if(cursor_chars >= 0 && cursor_chars <= text_chars)
3034 query_only = g_utf8_substring(entry_text, 0, cursor_chars);
3036 query_only = g_strdup(entry_text);
3040 *((gchar *)sep) =
'\0';
3043 state->pending_space_query = query_only;
3046 if(
state->pending_space_idle_id != 0) g_source_remove(
state->pending_space_idle_id);
3051 gunichar key_char = gdk_keyval_to_unicode(event->keyval);
3052 const gboolean is_alnum = key_char != 0 && g_unichar_isalnum(key_char);
3059 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
3065 const gint
position = gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
3066 const gsize query_len = sep - entry_text;
3067 gchar *query_only = g_strndup(entry_text, query_len);
3069 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query_only);
3070 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry),
MIN(
position, (gint)query_len));
3082 if(event->button != 1)
return FALSE;
3085 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
3091 const gsize query_len = sep - entry_text;
3092 gchar *query = g_strndup(entry_text, query_len);
3094 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
3095 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
3108 GtkAllocation allocation = { 0 };
3109 gtk_widget_get_allocation(widget, &allocation);
3111 gboolean click_inside =
FALSE;
3112 GdkWindow *win = gtk_widget_get_window(widget);
3115 gint wx = 0, wy = 0;
3116 gdk_window_get_origin(win, &wx, &wy);
3117 const gdouble x0 = (gdouble)wx;
3118 const gdouble y0 = (gdouble)wy;
3119 const gdouble x1 = x0 + (gdouble)allocation.width;
3120 const gdouble y1 = y0 + (gdouble)allocation.height;
3121 click_inside = (
event->x_root >= x0 &&
event->x_root < x1
3122 &&
event->y_root >= y0 &&
event->y_root < y1);
3126 click_inside = (
event->x >= 0.0 &&
event->x < allocation.width
3127 &&
event->y >= 0.0 &&
event->y < allocation.height);
3130 if(click_inside)
return FALSE;
3132 state->response = GTK_RESPONSE_CANCEL;
3133 gtk_widget_destroy(widget);
3140 if(
state->pending_space_idle_id != 0)
3142 g_source_remove(
state->pending_space_idle_id);
3143 state->pending_space_idle_id = 0;
3148 state->pending_space_query = NULL;
3150 gtk_grab_remove(widget);
3151 if(
state->window == widget)
state->window = NULL;
3158 gtk_window_set_title(GTK_WINDOW(
window), _(
"Ansel - Search accelerators"));
3160#ifdef GDK_WINDOWING_QUARTZ
3164 const int dialog_width = 800;
3165 const int dialog_height = 0;
3167 gtk_window_set_decorated(GTK_WINDOW(
window),
FALSE);
3169 gtk_window_set_transient_for(GTK_WINDOW(
window), main_window);
3170 gtk_window_set_attached_to(GTK_WINDOW(
window), GTK_WIDGET(main_window));
3171 gtk_window_set_resizable(GTK_WINDOW(
window),
FALSE);
3172 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(
window),
TRUE);
3173 gtk_window_set_skip_pager_hint(GTK_WINDOW(
window),
TRUE);
3174 gtk_window_set_accept_focus(GTK_WINDOW(
window),
TRUE);
3175 gtk_window_set_focus_on_map(GTK_WINDOW(
window),
TRUE);
3176 gtk_window_set_default_size(GTK_WINDOW(
window), dialog_width, dialog_height);
3177 gtk_widget_set_name(
window,
"shortcut-search-dialog");
3178 gtk_widget_add_events(
window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3181 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);
3184 GMainLoop *loop = g_main_loop_new(NULL,
FALSE);
3187 .filter_model = NULL,
3188 .recent_entries = NULL,
3189 .main_window = main_window,
3190 .search_entry = NULL,
3194 .response = GTK_RESPONSE_CANCEL,
3196 .preferred_command = NULL,
3197 .suppress_inline_once =
FALSE,
3198 .pending_space_query = NULL,
3199 .pending_space_idle_id = 0
3203 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(
store), 2,
3205 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), 2, GTK_SORT_ASCENDING);
3210 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3211 gtk_container_add(GTK_CONTAINER(
window), box);
3212 GtkWidget *search_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
3213 gtk_box_pack_start(GTK_BOX(box), search_row,
TRUE,
TRUE, 0);
3216 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
3217 state.filter_model = filter_model;
3218 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
3221 GtkEntryCompletion *completion = gtk_entry_completion_new();
3222 GtkListStore *recent_entries = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
3224 state.recent_entries = recent_entries;
3226 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(recent_entries), 4,
3228 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(recent_entries), 4, GTK_SORT_ASCENDING);
3229 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(recent_entries));
3230 gtk_entry_completion_set_text_column(completion, 3);
3231 gtk_entry_completion_set_inline_completion(completion,
TRUE);
3232 gtk_entry_completion_set_inline_selection(completion,
TRUE);
3233 gtk_entry_completion_set_popup_completion(completion,
FALSE);
3235 gtk_entry_set_completion(GTK_ENTRY(
search_entry), completion);
3236 g_object_unref(completion);
3239 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(
scrolled),
3240 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3241 gtk_widget_set_size_request(
scrolled, dialog_width, 320);
3245 GtkWidget *tree_view = gtk_tree_view_new_with_model(filter_model);
3246 state.tree_view = tree_view;
3247 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view),
FALSE);
3248 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree_view),
FALSE);
3249 gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(tree_view),
FALSE);
3250 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(tree_view),
TRUE);
3251 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view), 0);
3252 gtk_widget_set_hexpand(tree_view,
TRUE);
3253 gtk_widget_set_vexpand(tree_view,
TRUE);
3254 gtk_container_add(GTK_CONTAINER(
scrolled), tree_view);
3256 GtkCellRenderer *txt = gtk_cell_renderer_text_new();
3257 g_object_set(txt,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE,
"max-width-chars", 70, NULL);
3258 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, txt,
"text", 0, NULL);
3259 gtk_tree_view_column_set_expand(column,
FALSE);
3260 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3261 gtk_tree_view_column_set_min_width(column, 360);
3262 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3264 GtkCellRenderer *accel = gtk_cell_renderer_accel_new();
3265 g_object_set(accel,
"editable",
FALSE,
"accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER, NULL);
3266 column = gtk_tree_view_column_new_with_attributes(NULL, accel,
"accel-key", 5,
"accel-mods", 6, NULL);
3267 gtk_tree_view_column_set_min_width(column, 140);
3268 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3270 GtkCellRenderer *
description = gtk_cell_renderer_text_new();
3271 g_object_set(
description,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE, NULL);
3272 column = gtk_tree_view_column_new_with_attributes(NULL,
description,
"text", 3, NULL);
3273 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3274 gtk_tree_view_column_set_min_width(column, 280);
3275 gtk_tree_view_column_set_expand(column,
TRUE);
3276 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3278 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
3279 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
3297 GtkAllocation main_alloc = { 0 };
3298 gtk_widget_get_allocation(GTK_WIDGET(main_window), &main_alloc);
3299 gint main_x = 0, main_y = 0;
3300 GdkWindow *main_gdk_window = gtk_widget_get_window(GTK_WIDGET(main_window));
3302 gdk_window_get_origin(main_gdk_window, &main_x, &main_y);
3304 gtk_window_get_position(main_window, &main_x, &main_y);
3309 const gint window_x = main_x +
MAX((main_alloc.width - dialog_width) / 2, 0);
3310 const gint window_y = main_y +
MAX(top_panel_height, 0);
3311 gtk_window_move(GTK_WINDOW(
window), window_x, window_y);
3313 gtk_widget_realize(
window);
3314 gtk_widget_show_all(
window);
3316 gdk_window_focus(gtk_widget_get_window(
window), GDK_CURRENT_TIME);
3320 g_main_loop_run(loop);
3321 g_main_loop_unref(loop);
3325 dispatch->
path = g_strdup(
state.selected->path);
3327 ?
state.selected->accels
3335 g_object_unref(
store);
3353 if(event->keyval == GDK_KEY_Escape)
3363 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)
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)
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