53#ifdef GDK_WINDOWING_QUARTZ
61#define DT_ACCEL_SEARCH_INLINE_SEPARATOR " > "
62#define DT_ACCEL_SEARCH_DISPATCH_RETRY_DELAY_MS 50
79 if(pc->
base) g_closure_unref(pc->
base);
88 gtk_widget_set_has_tooltip(widget,
TRUE);
92 const GValue *param_values, gpointer data)
96 if(n_param_values < 5)
return TRUE;
98 GtkWidget *widget = g_value_get_object(¶m_values[0]);
101 if(!gtk_widget_get_has_tooltip(widget))
return TRUE;
104 const char *base_markup = g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-markup");
105 const char *base_text = base_markup ? NULL : g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-text");
106 const gboolean base_none = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"dt-accel-tooltip-base-none"));
110 gchar *current_markup = gtk_widget_get_tooltip_markup(widget);
111 if(current_markup && current_markup[0])
113 g_object_set_data_full(G_OBJECT(widget),
"dt-accel-tooltip-base-markup", current_markup, g_free);
114 base_markup = current_markup;
119 gchar *current_text = gtk_widget_get_tooltip_text(widget);
120 if(current_text && current_text[0])
122 g_object_set_data_full(G_OBJECT(widget),
"dt-accel-tooltip-base-text", current_text, g_free);
123 base_text = current_text;
128 g_object_set_data(G_OBJECT(widget),
"dt-accel-tooltip-base-none", GINT_TO_POINTER(1));
136 const char *accel_path = g_object_get_data(G_OBJECT(widget),
"accel-path");
149 gtk_widget_set_tooltip_markup(widget, base_markup);
151 gtk_widget_set_tooltip_text(widget, base_text);
155 gchar *shortcut_label = gtk_accelerator_get_label(shortcut->
key, shortcut->
mods);
156 if(
IS_NULL_PTR(shortcut_label) || !shortcut_label[0])
163 if(base_markup && base_markup[0])
165 gchar *esc_label = g_markup_escape_text(shortcut_label, -1);
166 gchar *esc_desc = g_markup_escape_text(shortcut_desc, -1);
167 gchar *new_markup = g_strdup_printf(
"%s\n<small>%s: %s</small>", base_markup, esc_desc, esc_label);
168 gtk_widget_set_tooltip_markup(widget, new_markup);
173 else if(base_text && base_text[0])
175 gchar *new_text = g_strdup_printf(
"%s\n%s: %s", base_text, shortcut_desc, shortcut_label);
176 gtk_widget_set_tooltip_text(widget, new_text);
181 gchar *new_text = g_strdup_printf(
"%s: %s", shortcut_desc, shortcut_label);
182 gtk_widget_set_tooltip_text(widget, new_text);
193 static gulong hook_id = 0;
194 if(hook_id != 0)
return;
196 const guint signal_id = g_signal_lookup(
"query-tooltip", GTK_TYPE_WIDGET);
197 if(signal_id == 0)
return;
216 GList *link = g_list_last(shortcut->
closure);
247 for(link = g_list_first(shortcut->
closure); link; link = g_list_next(link))
259 link = g_list_last(shortcut->
closure);
265 g_closure_unref(cl->
base);
279 gchar **child_parts = g_strsplit(child_shortcut->
path,
"/", -1);
280 guint
n = g_strv_length(child_parts);
282 gchar *parent_path = g_strjoinv (
"/", child_parts);
283 g_strfreev(child_parts);
286 if(!g_strcmp0(parent_shortcut->
path, parent_path))
290 if(parent_closure && child_closure)
315 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
316 guint keyval, GdkModifierType mods, gpointer user_data),
320 pc->
base = g_cclosure_new(G_CALLBACK(action_callback), data, NULL);
323 g_closure_set_marshal(pc->
base, g_cclosure_marshal_generic);
324 g_closure_ref(pc->
base);
325 g_closure_sink(pc->
base);
345 accels->
keymap = gdk_keymap_get_for_display(gdk_display_get_default());
405 else if(!g_strcmp0(group,
"map") && accels->
map_accels)
410 else if(!g_strcmp0(group,
"print") && accels->
print_accels)
422 fprintf(stderr,
"[dt_accels_connect_active_group] INFO: unknown value: `%s'\n", group);
437 gboolean changed =
FALSE;
448 key->accel_key = shortcut->
key;
449 key->accel_mods = shortcut->
mods;
450 gtk_accel_map_change_entry(shortcut->
path, shortcut->
key, shortcut->
mods,
TRUE);
453 else if(
key->accel_key == shortcut->
key &&
key->accel_mods == shortcut->
mods)
462 shortcut->
key =
key->accel_key;
463 shortcut->
mods =
key->accel_mods;
470 else if(shortcut->
locked && (
key->accel_key != shortcut->
key ||
key->accel_mods != shortcut->
mods))
473 key->accel_key = shortcut->
key;
474 key->accel_mods = shortcut->
mods;
475 gtk_accel_map_change_entry(shortcut->
path, shortcut->
key, shortcut->
mods,
TRUE);
479 else if(
key->accel_key != shortcut->
key ||
key->accel_mods != shortcut->
mods)
481 shortcut->
key =
key->accel_key;
482 shortcut->
mods =
key->accel_mods;
502 if(shortcut->
key != alt_char)
510 gtk_widget_remove_accelerator(shortcut->
widget, shortcut->
accel_group, old_key->accel_key, old_key->accel_mods);
514 if(old_key->accel_key != alt_char)
515 gtk_widget_remove_accelerator(shortcut->
widget, shortcut->
accel_group, alt_char, old_key->accel_mods);
526 gtk_accel_group_disconnect(shortcut->
accel_group, cl);
535 gtk_accel_group_connect(shortcut->
accel_group, shortcut->
key, shortcut->
mods,
flags | GTK_ACCEL_VISIBLE, closure);
542 gtk_accel_map_add_entry(shortcut->
path, 0, 0);
550 GdkModifierType mods, gpointer user_data)
556 gtk_widget_grab_focus(shortcut->
widget);
560 GdkKeymapKey *keys = NULL;
562 GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
563 if(gdk_keymap_get_entries_for_keyval(keymap, shortcut->
key, &keys, &
n))
565 if(
n > 0) keycode = keys[0].keycode;
570 GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS);
571 ev->key.window = g_object_ref(gtk_widget_get_window(shortcut->
widget));
572 ev->key.send_event =
TRUE;
573 ev->key.time = GDK_CURRENT_TIME;
574 ev->key.state = shortcut->
mods;
575 ev->key.keyval = shortcut->
key;
576 ev->key.hardware_keycode = keycode;
578 ev->key.is_modifier =
FALSE;
581 gtk_widget_event(shortcut->
widget, ev);
589 GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
596 if(shortcut && shortcut->
widget == widget)
606 shortcut->
widget = widget;
608 shortcut->
path = g_strdup(accel_path);
610 shortcut->
key = key_val;
611 shortcut->
mods = accel_mods;
615 shortcut->
description = _(
"Contextual interaction on focus");
616 shortcut->
accels = accels;
624 gboolean (*action_callback)(GtkAccelGroup *group,
625 GObject *acceleratable, guint keyval,
626 GdkModifierType mods, gpointer user_data),
627 gpointer data, GtkAccelGroup *accel_group,
const gchar *action_scope,
628 const gchar *action_name)
643 shortcut->
path = g_strdup(accel_path);
651 shortcut->
accels = accels;
664 GtkAccelGroup *accel_group,
const gchar *accel_path, guint key_val,
665 GdkModifierType accel_mods,
const gboolean lock)
672 if(shortcut && shortcut->
widget == widget)
681 GtkAccelKey
key = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
683 shortcut->
widget = widget;
692 shortcut->
widget = widget;
694 shortcut->
path = g_strdup(accel_path);
695 shortcut->
signal = signal;
696 shortcut->
key = key_val;
697 shortcut->
mods = accel_mods;
702 shortcut->
accels = accels;
719 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
720 guint keyval, GdkModifierType mods,
722 gpointer data, GtkAccelGroup *accel_group,
const gchar *action_scope,
723 const gchar *action_name, guint key_val, GdkModifierType accel_mods,
735 if(closure && closure->data == data)
756 shortcut->
path = g_strdup(accel_path);
758 shortcut->
key = key_val;
759 shortcut->
mods = accel_mods;
764 shortcut->
accels = accels;
784 GtkAccelKey
key = { 0 };
787 const gboolean is_known = gtk_accel_map_lookup_entry(shortcut->
path, &
key);
788 if(!is_known)
return;
791 const GtkAccelKey oldkey = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
799 const gboolean needs_cleanup = changed && oldkey.accel_key > 0 && oldtype !=
DT_SHORTCUT_UNSET;
802 const gboolean needs_init = changed &&
key.accel_key > 0;
840 if(g_strrstr(shortcut->
path, params->path) != NULL)
891 if(strncmp(scope,
"<Ansel>/", strlen(
"<Ansel>/")) == 0)
892 return g_strdup_printf(
"%s/%s", scope, feature);
894 return g_strdup_printf(
"<Ansel>/%s/%s", scope, feature);
902 gdk_event_get_state(event, mods);
908 GdkModifierType consumed;
909 gdk_keymap_translate_keyboard_state(accels->
keymap, event->key.hardware_keycode, event->key.state,
911 keyval, NULL, NULL, &consumed);
915 gchar *accel_name = gtk_accelerator_name(*keyval, *mods);
917 (event->type == GDK_KEY_PRESS) ?
"Key pressed" :
"Key released", accel_name);
924 if(gdk_keyval_to_lower(*keyval) == gdk_keyval_to_upper(*keyval))
932 if(*keyval == GDK_KEY_ISO_Left_Tab)
934 *keyval = GDK_KEY_Tab;
935 *mods |= GDK_SHIFT_MASK;
955 return gdk_keyval_to_lower(keyval);
962 const gchar *path = (
const gchar *)
key;
971 && shortcut_key == result_key
974 if(!g_strcmp0(path, shortcut->
path))
981 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
998 GList *item = g_list_first(result.
results);
999 if(item) path = (
char *)item->data;
1009 const gchar *path = (
const gchar *)
key;
1018 && shortcut_key == result_key
1022 if(!g_strcmp0(path, shortcut->
path))
1029 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1035 GdkModifierType mods)
1044 GList *item = g_list_first(result.
results);
1057 gchar *accel_name = gtk_accelerator_name(keyval, mods);
1090 if(!(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE || event->type == GDK_SCROLL))
1094 if(event->type == GDK_SCROLL)
1103 GdkModifierType mods;
1121 if(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
1123 GtkWidget *focused = gtk_window_get_focus(GTK_WINDOW(w));
1124 if(!
IS_NULL_PTR(focused) && (GTK_IS_EDITABLE(focused) || GTK_IS_TEXT_VIEW(focused)))
1132 if(event->type == GDK_KEY_PRESS &&
1140 else if(event->type == GDK_KEY_RELEASE)
1192 GtkTreeIter *iter, gpointer data)
1196 g_object_set(renderer,
1199 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
1204 GtkTreeIter *iter, gpointer data)
1208 g_object_set (renderer,
1209 "icon-name", (!
IS_NULL_PTR(shortcut) && !shortcut->
locked) ?
"edit-delete-symbolic" :
"lock",
1222 if(!gdk_keymap_get_entries_for_keycode(accels->
keymap, hardware_keycode, &keys, &keyvals, &n_keys))
1225 for(
int i = 0;
i < n_keys; ++
i)
1227 if(keyvals[
i] == keyval)
1229 int group = keys[
i].group;
1242 guint hardware_key, gpointer user_data)
1246 GtkTreeModel *filter = GTK_TREE_MODEL(user_data);
1247 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
1250 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1256 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(filter), &f_iter, path))
1257 gtk_tree_model_get(GTK_TREE_MODEL(filter), &f_iter,
COL_SHORTCUT, &shortcut, -1);
1259 const char *shortcut_path = NULL;
1265 if(keyval == GDK_KEY_VoidSymbol
1266 || (mods == 0 && (keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace)))
1275 if(keyval != 0 || mods != 0)
1277 GdkDisplay *display = gdk_display_get_default();
1278 GdkSeat *seat = gdk_display_get_default_seat(display);
1279 GdkDevice *pointer = gdk_seat_get_pointer(seat);
1280 GdkModifierType
state;
1281 gdk_device_get_state(pointer, gdk_get_default_root_window(), NULL, &
state);
1285 GdkEventKey
event = { 0 };
1286 event.type = GDK_KEY_PRESS;
1287 event.state = mods |
state;
1288 event.keyval = keyval;
1289 event.hardware_keycode = hardware_key;
1297 if(!(keyval == 0 && mods == 0))
1301 if(
IS_NULL_PTR(shortcut_path) && gtk_accel_map_change_entry(shortcut->
path, keyval, mods,
FALSE))
1311 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter), &s_iter, &f_iter);
1320 char *new_text = gtk_accelerator_name(keyval, mods);
1322 = 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",
1323 _(
"The shortcut for"), shortcut_path,
1324 _(
"is already using the key combination"), new_text,
1325 _(
"Delete it first."));
1326 gtk_dialog_run(GTK_DIALOG(dlg));
1327 gtk_widget_destroy(dlg);
1331 gtk_tree_path_free(path);
1334static void _shortcut_cleared(GtkCellRendererAccel *renderer,
const gchar *path_string, gpointer user_data)
1336 _shortcut_edited(GTK_CELL_RENDERER(renderer), path_string, 0, 0, 0, user_data);
1341 GdkRectangle *background, GdkRectangle *cell_area, GtkCellRendererState
flags,
1353 gtk_tree_store_set(
store, iter,
1367 const gchar *path = (
const gchar *)
key;
1374 GtkTreeIter *parent = NULL;
1375 GtkTreeIter *iter = NULL;
1382 gchar **parts = g_strsplit(path,
"/", -1);
1383 gchar *accum = g_strdup(
"<Ansel>");
1387 const size_t len_ansel = strlen(accum);
1388 for(
int i = 1; parts[
i]; ++
i)
1391 gchar *tmp = g_strconcat(accum,
"/", parts[
i], NULL);
1397 iter = g_hash_table_lookup(node_cache, accum);
1404 GtkTreeIter new_iter;
1405 gtk_tree_store_append(
store, &new_iter, parent);
1408 iter = g_new(GtkTreeIter, 1);
1410 g_hash_table_insert(node_cache, g_strdup(accum), iter);
1414 gchar *label = g_strdup(parts[
i]);
1415 label[0] = g_unichar_toupper(label[0]);
1418 if(!g_strcmp0(accum, path))
1436 gchar **parts = g_strsplit(path,
"/", -1);
1437 const gint len = g_strv_length(parts);
1440 tail = g_strjoinv(
"/", parts + 2);
1442 tail = g_strdup(parts[1]);
1444 tail = g_strdup(parts[0]);
1454 const gchar *path = (
const gchar *)
key;
1456 GtkListStore *
store = (GtkListStore *)user_data;
1468 gchar **tail_parts = g_strsplit(tail,
"/", -1);
1469 const gint tail_len = g_strv_length(tail_parts);
1470 const gchar *leaf = tail;
1471 if(tail_len > 0 && !
IS_NULL_PTR(tail_parts[tail_len - 1]) && tail_parts[tail_len - 1][0] !=
'\0')
1472 leaf = tail_parts[tail_len - 1];
1475 gtk_list_store_append(
store, &iter);
1476 gtk_list_store_set(
store, &iter,
1485 g_strfreev(tail_parts);
1494 gchar *pa = NULL, *pb = NULL;
1495 gtk_tree_model_get(
model, a, 2, &ka, 0, &pa, -1);
1496 gtk_tree_model_get(
model, b, 2, &kb, 0, &pb, -1);
1508 gchar *pa_ci = g_utf8_casefold(pa, -1);
1509 gchar *pb_ci = g_utf8_casefold(pb, -1);
1510 gchar **pa_parts = g_strsplit(pa_ci,
"/", -1);
1511 gchar **pb_parts = g_strsplit(pb_ci,
"/", -1);
1513 for(gint
i = 0;;
i++)
1515 const gchar *pa_part = pa_parts[
i];
1516 const gchar *pb_part = pb_parts[
i];
1533 ret = g_utf8_collate(pa_part, pb_part);
1537 g_strfreev(pb_parts);
1538 g_strfreev(pa_parts);
1551 gtk_tree_model_get(
model, a, GPOINTER_TO_INT(data), &ka, -1);
1552 gtk_tree_model_get(
model, b, GPOINTER_TO_INT(data), &kb, -1);
1558 gchar *ka_ci = g_utf8_casefold(ka, -1);
1559 gchar *kb_ci = g_utf8_casefold(kb, -1);
1562 res = g_utf8_collate(ka_ci, kb_ci);
1586 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(params->path_search));
1587 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(params->keys_search));
1589 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1590 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1593 gboolean show =
TRUE;
1598 if(needle_path && needle_path[0])
1600 if(path && path[0] !=
'\0')
1602 gchar *needle_ci = g_utf8_casefold(needle_path, -1);
1603 gchar *haystack_ci = g_utf8_casefold(path, -1);
1604 show &= (g_strrstr(haystack_ci, needle_ci) != NULL);
1616 if(needle_keys && needle_keys[0] !=
'\0')
1618 guint search_keyval = 0;
1619 GdkModifierType search_mods = 0;
1620 gtk_accelerator_parse(needle_keys, &search_keyval, &search_mods);
1621 if(search_keyval || search_mods)
1624 GdkModifierType mods = 0;
1631 if(search_keyval && search_mods)
1632 show &= (keyval == search_keyval && mods == search_mods);
1634 show &= ((keyval && keyval == search_keyval) || (mods && mods == search_mods));
1643 if(show)
return TRUE;
1646 if(gtk_tree_model_iter_has_child(
model, iter))
1649 if(gtk_tree_model_iter_children(
model, &child, iter))
1655 }
while(gtk_tree_model_iter_next(
model, &child));
1665 GtkTreeView *tree_view = GTK_TREE_VIEW(params->tree_view);
1666 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(tree_view)));
1669 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(params->path_search));
1670 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(params->keys_search));
1672 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1673 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1674 gtk_tree_view_collapse_all(GTK_TREE_VIEW(params->tree_view));
1676 gtk_tree_view_expand_all(GTK_TREE_VIEW(params->tree_view));
1686 params->keys_search = gtk_search_entry_new();
1687 params->path_search = gtk_search_entry_new();
1688 GtkWidget *tree_view = params->tree_view = gtk_tree_view_new();
1694 = { {
"Primary>", N_(
"<Primary> - Decoded as <Control> on Windows/Linux or <Meta> on Mac OS") },
1695 {
"Control>", N_(
"<Control>") },
1696 {
"Shift>", N_(
"<Shift>") },
1697 {
"Alt>", N_(
"<Alt>") },
1698 {
"Super>", N_(
"<Super> - The Windows key on PC") },
1699 {
"Hyper>", N_(
"<Hyper>") },
1700 {
"Meta>", N_(
"<Meta> - Decoded as <Command> on Mac OS") },
1703 gtk_widget_set_tooltip_text(params->keys_search, _(
"Look for keys and modifiers codes, as `<Modifier>Key`.\n"
1704 "Type `<` to start the auto-completion"));
1706 gtk_widget_set_tooltip_text(params->path_search, _(
"Case-insensitive search for keywords of full pathes.\n"
1707 "Ex: `darkroom/controls/sliders`"));
1711 gtk_window_set_title(GTK_WINDOW(dialog), _(
"Ansel - Keyboard shortcuts"));
1713#ifdef GDK_WINDOWING_QUARTZ
1715 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1718 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1719 gtk_window_set_modal(GTK_WINDOW(dialog),
TRUE);
1720 gtk_window_set_transient_for(GTK_WINDOW(dialog), main_window);
1721 gtk_window_set_default_size(GTK_WINDOW(dialog), 1100, 900);
1724 GtkTreeStore *
store = gtk_tree_store_new(
NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1725 G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT);
1731 g_hash_table_destroy(node_cache);
1737 GINT_TO_POINTER(
i), NULL);
1738 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store),
i, GTK_SORT_ASCENDING);
1742 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
1743 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
filter_callback, params, NULL);
1746 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), filter_model);
1747 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view),
COL_PATH);
1748 gtk_widget_set_hexpand(tree_view,
TRUE);
1749 gtk_widget_set_vexpand(tree_view,
TRUE);
1750 gtk_widget_set_halign(tree_view, GTK_ALIGN_FILL);
1751 gtk_widget_set_valign(tree_view, GTK_ALIGN_FILL);
1753 g_signal_connect(G_OBJECT(params->path_search),
"changed", G_CALLBACK(
search_changed), params);
1754 g_signal_connect(G_OBJECT(params->keys_search),
"changed", G_CALLBACK(
search_changed), params);
1757 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_(
"View / Scope / Feature / Control"), gtk_cell_renderer_text_new(),
"text",
COL_NAME, NULL);
1758 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1760 GtkCellRenderer *renderer = gtk_cell_renderer_accel_new();
1761 column = gtk_tree_view_column_new_with_attributes(_(
"Keys"), renderer,
"accel-key",
COL_KEYVAL,
"accel-mods",
1764 g_signal_connect(renderer,
"accel-edited", G_CALLBACK(
_shortcut_edited), filter_model);
1765 g_signal_connect(renderer,
"accel-cleared", G_CALLBACK(
_shortcut_cleared), filter_model);
1766 gtk_tree_view_column_set_min_width(column, 100);
1767 gtk_tree_view_column_set_resizable(column,
TRUE);
1768 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1771 g_object_set(renderer,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
1772 column = gtk_tree_view_column_new_with_attributes(_(
"Clear"), renderer,
"pixbuf",
COL_CLEAR, NULL);
1774 g_signal_connect(renderer,
"activate", G_CALLBACK(
_icon_activate), filter_model);
1775 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1777 column = gtk_tree_view_column_new_with_attributes(_(
"Description"), gtk_cell_renderer_text_new(),
"text",
COL_DESCRIPTION, NULL);
1778 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1782 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), box,
TRUE,
TRUE, 0);
1785 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by feature : ")),
FALSE,
FALSE, 0);
1786 gtk_box_pack_start(GTK_BOX(hbox), params->path_search,
TRUE,
TRUE, 0);
1787 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by keys : ")),
FALSE,
FALSE, 0);
1788 gtk_box_pack_start(GTK_BOX(hbox), params->keys_search,
TRUE,
TRUE, 0);
1789 gtk_box_pack_start(GTK_BOX(box), hbox,
FALSE,
FALSE, 0);
1791 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1793 gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
1794 gtk_box_pack_start(GTK_BOX(box), scrolled_window,
TRUE,
TRUE, 0);
1796 gtk_widget_set_visible(tree_view,
TRUE);
1797 gtk_widget_show_all(dialog);
1799 gtk_dialog_run(GTK_DIALOG(dialog));
1800 gtk_widget_destroy(dialog);
1801 g_object_unref(filter_model);
1802 g_object_unref(
store);
1814 if(
IS_NULL_PTR(needle) || needle[0] ==
'\0')
return 0;
1818 gtk_tree_model_get(
model, iter, 0, &label, -1);
1826 gchar *label_ci = g_utf8_casefold(label, -1);
1828 gchar **parts = g_strsplit(label_ci,
"/", -1);
1829 gchar *needle_copy = g_strdup(needle);
1830 gchar **tokens = g_strsplit_set(needle_copy,
" \t\r\n", -1);
1832 gboolean has_token =
FALSE;
1833 gboolean all_matched =
TRUE;
1836 if(tokens[
t][0] ==
'\0')
continue;
1838 int best_token_rank = INT_MAX;
1843 const char *match = g_strstr_len(parts[
i], -1, tokens[
t]);
1846 const int cell_rank =
i * 10000;
1847 const int in_cell_rank = match - parts[
i];
1848 const int token_rank = cell_rank + in_cell_rank;
1849 if(token_rank < best_token_rank) best_token_rank = token_rank;
1852 if(best_token_rank == INT_MAX)
1854 all_matched =
FALSE;
1857 rank_sum += best_token_rank;
1860 if(all_matched) ret = has_token ? rank_sum : 0;
1874 const gchar *needle = gtk_entry_get_text(GTK_ENTRY(search_entry));
1875 gchar *needle_query = g_strdup(!
IS_NULL_PTR(needle) ? needle :
"");
1878 g_strstrip(needle_query);
1879 gchar *needle_ci = g_utf8_casefold(needle_query, -1);
1883 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1884 GTK_SORT_ASCENDING);
1887 if(gtk_tree_model_get_iter_first(
model, &iter))
1892 gtk_list_store_set(GTK_LIST_STORE(
model), &iter, 2, rank, -1);
1894 }
while(gtk_tree_model_iter_next(
model, &iter));
1901 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), 2, GTK_SORT_ASCENDING);
1902 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
model));
1923#define DT_ACCEL_SEARCH_RECENT_KEY "plugins/accel_search/recent_entries"
1924#define DT_ACCEL_SEARCH_RECENT_MAX 20
1929 gtk_list_store_clear(
store);
1948 if(entry[0] ==
'\0')
1956 gchar **parts = g_strsplit(entry,
"\t", 3);
1958 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
1965 const gchar *query = parts[0];
1966 const gchar *command = parts[1];
1971 gtk_list_store_append(
store, &iter);
1972 gtk_list_store_set(
store, &iter,
1989 const gchar *search_text =
"";
1992 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
1993 if(!
IS_NULL_PTR(entry_text)) search_text = entry_text;
1996 gchar *query_text = g_strdup(search_text);
1999 g_strstrip(query_text);
2000 gchar *query_ci = g_utf8_casefold(query_text, -1);
2001 const gboolean has_query = query_ci[0] !=
'\0';
2003 gchar *a_query = NULL, *a_command = NULL;
2004 gchar *b_query = NULL, *b_command = NULL;
2005 gint a_recent = G_MAXINT, b_recent = G_MAXINT;
2006 gtk_tree_model_get(
model, a, 0, &a_query, 1, &a_command, 4, &a_recent, -1);
2007 gtk_tree_model_get(
model, b, 0, &b_query, 1, &b_command, 4, &b_recent, -1);
2009 gchar *a_query_ci = g_utf8_casefold(!
IS_NULL_PTR(a_query) ? a_query :
"", -1);
2010 gchar *a_command_ci = g_utf8_casefold(!
IS_NULL_PTR(a_command) ? a_command :
"", -1);
2011 gchar *b_query_ci = g_utf8_casefold(!
IS_NULL_PTR(b_query) ? b_query :
"", -1);
2012 gchar *b_command_ci = g_utf8_casefold(!
IS_NULL_PTR(b_command) ? b_command :
"", -1);
2014 gint a_rank = 400000, b_rank = 400000;
2017 if(g_str_has_prefix(a_query_ci, query_ci))
2019 else if(g_str_has_prefix(a_command_ci, query_ci))
2023 const gchar *a_match_query = g_strstr_len(a_query_ci, -1, query_ci);
2025 a_rank = 200000 + (a_match_query - a_query_ci);
2028 const gchar *a_match_command = g_strstr_len(a_command_ci, -1, query_ci);
2030 a_rank = 300000 + (a_match_command - a_command_ci);
2034 if(g_str_has_prefix(b_query_ci, query_ci))
2036 else if(g_str_has_prefix(b_command_ci, query_ci))
2040 const gchar *b_match_query = g_strstr_len(b_query_ci, -1, query_ci);
2042 b_rank = 200000 + (b_match_query - b_query_ci);
2045 const gchar *b_match_command = g_strstr_len(b_command_ci, -1, query_ci);
2047 b_rank = 300000 + (b_match_command - b_command_ci);
2052 gint ret = a_rank - b_rank;
2053 if(ret == 0) ret = a_recent - b_recent;
2054 if(ret == 0) ret = g_utf8_collate(a_query_ci, b_query_ci);
2055 if(ret == 0) ret = g_utf8_collate(a_command_ci, b_command_ci);
2075 gchar *trimmed = g_strdup(query);
2076 g_strstrip(trimmed);
2077 if(trimmed[0] ==
'\0')
2083 gchar *command = g_strdup(shortcut->
path);
2084 g_strdelimit(trimmed,
"\t\r\n",
' ');
2085 g_strdelimit(command,
"\t\r\n",
' ');
2087 GPtrArray *entries = g_ptr_array_new_with_free_func(g_free);
2088 GPtrArray *entries_ci = g_ptr_array_new_with_free_func(g_free);
2089 g_ptr_array_add(entries, g_strdup_printf(
"%s\t%s", trimmed, command));
2090 g_ptr_array_add(entries_ci, g_utf8_casefold(trimmed, -1));
2103 if(
IS_NULL_PTR(candidate) || candidate[0] ==
'\0')
2108 g_strstrip(candidate);
2109 if(candidate[0] ==
'\0')
2115 gchar **parts = g_strsplit(candidate,
"\t", 3);
2117 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
2124 const gchar *candidate_query = parts[0];
2125 const gchar *candidate_command = parts[1];
2127 gchar *candidate_ci = g_utf8_casefold(candidate_query, -1);
2128 gboolean found =
FALSE;
2129 for(guint
k = 0;
k < entries_ci->len;
k++)
2131 const char *kept_ci = g_ptr_array_index(entries_ci,
k);
2132 if(!g_strcmp0(kept_ci, candidate_ci))
2145 g_ptr_array_add(entries, g_strdup_printf(
"%s\t%s", candidate_query, candidate_command));
2146 g_ptr_array_add(entries_ci, candidate_ci);
2154 const gchar *entry_value = (
i < entries->len) ? (
const gchar *)g_ptr_array_index(entries,
i) :
"";
2160 g_ptr_array_free(entries_ci,
TRUE);
2161 g_ptr_array_free(entries,
TRUE);
2185 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
state->recent_entries));
2187 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(
state->filter_model));
2189 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2190 gtk_tree_selection_unselect_all(selection);
2193 gboolean has_iter = gtk_tree_model_get_iter_first(
state->filter_model, &iter);
2194 GtkTreeIter fallback_iter = iter;
2195 GtkTreeIter selected_iter = iter;
2196 gboolean found_preferred =
FALSE;
2202 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2204 && !g_strcmp0(shortcut->
path,
state->preferred_command))
2206 selected_iter = iter;
2207 found_preferred =
TRUE;
2210 }
while(gtk_tree_model_iter_next(
state->filter_model, &iter));
2215 GtkTreeIter *target_iter = found_preferred ? &selected_iter : &fallback_iter;
2216 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, target_iter);
2218 gtk_tree_selection_select_iter(selection, target_iter);
2219 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2220 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2221 gtk_tree_path_free(path);
2223 gtk_tree_model_get(
state->filter_model, target_iter, 1, &
state->selected, -1);
2228 GtkTreeIter *iter, gpointer user_data)
2231 gchar *query = NULL;
2232 gchar *command = NULL;
2233 gtk_tree_model_get(
model, iter, 0, &query, 1, &command, -1);
2238 state->preferred_command = NULL;
2241 state->preferred_command = g_strdup(command);
2245 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
2246 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2258 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2259 if(
state->suppress_inline_once)
2265 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2267 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2269 const gchar *last = g_utf8_find_prev_char(entry_text, entry_text + strlen(entry_text));
2270 const gunichar last_char = !
IS_NULL_PTR(last) ? g_utf8_get_char(last) : 0;
2271 if(last_char != 0 && !g_unichar_isalnum(last_char))
2274 gchar *query = g_strdup(!
IS_NULL_PTR(entry_text) ? entry_text :
"");
2278 if(query[0] ==
'\0')
2284 gchar *query_ci = g_utf8_casefold(query, -1);
2285 if(query_ci[0] ==
'\0')
2292 gint best_rank = G_MAXINT;
2293 gint best_recent = G_MAXINT;
2294 gchar *best_command = NULL;
2295 gchar *best_display = NULL;
2296 gchar *best_query_ci = NULL;
2297 gchar *best_command_ci = NULL;
2298 const glong query_len = g_utf8_strlen(query_ci, -1);
2301 if(gtk_tree_model_get_iter_first(
model, &iter))
2305 gchar *row_query = NULL;
2306 gchar *row_command = NULL;
2307 gchar *row_display = NULL;
2308 gint row_recent = G_MAXINT;
2309 gtk_tree_model_get(
model, &iter, 0, &row_query, 1, &row_command, 3, &row_display, 4, &row_recent, -1);
2318 gchar *row_query_ci = g_utf8_casefold(row_query, -1);
2319 gchar *row_command_ci = g_utf8_casefold(!
IS_NULL_PTR(row_command) ? row_command :
"", -1);
2320 gint row_rank = G_MAXINT;
2321 if(g_str_has_prefix(row_query_ci, query_ci))
2324 const glong row_query_len = g_utf8_strlen(row_query_ci, -1);
2325 row_rank =
MAX((gint)(row_query_len - query_len), 0);
2327 else if(g_str_has_prefix(row_command_ci, query_ci))
2329 const glong row_command_len = g_utf8_strlen(row_command_ci, -1);
2330 row_rank = 100000 +
MAX((gint)(row_command_len - query_len), 0);
2334 const gchar *match_query = g_strstr_len(row_query_ci, -1, query_ci);
2336 row_rank = 200000 + (match_query - row_query_ci);
2339 const gchar *match_command = g_strstr_len(row_command_ci, -1, query_ci);
2341 row_rank = 300000 + (match_command - row_command_ci);
2345 if(row_rank < best_rank
2346 || (row_rank == best_rank && row_recent < best_recent)
2347 || (row_rank == best_rank && row_recent == best_recent
2348 && (!
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) < 0))
2349 || (row_rank == best_rank && row_recent == best_recent
2350 && !
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) == 0
2351 && (!
IS_NULL_PTR(best_command_ci) && g_utf8_collate(row_command_ci, best_command_ci) < 0)))
2353 best_rank = row_rank;
2354 best_recent = row_recent;
2359 best_command = g_strdup(!
IS_NULL_PTR(row_command) ? row_command :
"");
2360 best_display = g_strdup(!
IS_NULL_PTR(row_display) ? row_display : row_query);
2361 best_query_ci = g_strdup(row_query_ci);
2362 best_command_ci = g_strdup(row_command_ci);
2370 }
while(gtk_tree_model_iter_next(
model, &iter));
2373 if(!
IS_NULL_PTR(best_command) && best_command[0] !=
'\0' && best_rank < G_MAXINT)
2378 state->preferred_command = NULL;
2380 state->preferred_command = g_strdup(best_command);
2382 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2383 GtkTreeIter filter_iter;
2384 if(gtk_tree_model_get_iter_first(
state->filter_model, &filter_iter))
2389 gchar *shortcut_path_display = NULL;
2390 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &shortcut, 0, &shortcut_path_display, -1);
2391 gchar *shortcut_trimmed = NULL;
2395 && (!g_strcmp0(shortcut->
path, best_command)
2396 || (!
IS_NULL_PTR(shortcut_trimmed) && !g_strcmp0(shortcut_trimmed, best_command))
2397 || (!
IS_NULL_PTR(shortcut_path_display) && !g_strcmp0(shortcut_path_display, best_command))))
2399 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &filter_iter);
2403 dt_free(shortcut_path_display);
2406 gtk_tree_selection_select_iter(selection, &filter_iter);
2407 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2408 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2409 gtk_tree_path_free(path);
2410 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &
state->selected, -1);
2412 dt_free(shortcut_path_display);
2416 dt_free(shortcut_path_display);
2417 }
while(gtk_tree_model_iter_next(
state->filter_model, &filter_iter));
2420 if(!
IS_NULL_PTR(best_display) && best_display[0] !=
'\0')
2422 const gchar *current = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2423 if(g_strcmp0(current, best_display))
2425 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), best_display);
2427 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1));
2428 gtk_editable_select_region(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1), -1);
2456 GValue params[4] = { G_VALUE_INIT };
2458 g_value_init(¶ms[0], G_TYPE_POINTER);
2459 g_value_set_pointer(¶ms[0], shortcut->
accel_group);
2461 g_value_init(¶ms[1], G_TYPE_POINTER);
2462 g_value_set_pointer(¶ms[1], G_OBJECT(main_window));
2464 g_value_init(¶ms[2], G_TYPE_UINT);
2465 g_value_set_uint(¶ms[2], shortcut->
key);
2467 g_value_init(¶ms[3], G_TYPE_UINT);
2468 g_value_set_uint(¶ms[3], shortcut->
mods);
2470 GValue ret = G_VALUE_INIT;
2471 g_value_init (&ret, G_TYPE_BOOLEAN);
2476 for(
int k = 0;
k < 4;
k++) g_value_unset(¶ms[
k]);
2477 g_value_unset(&ret);
2481 g_closure_invoke(active_closure, &ret, 4, params, NULL);
2482 const gboolean handled = g_value_get_boolean(&ret);
2484 for(
int k = 0;
k < 4;
k++) g_value_unset(¶ms[
k]);
2485 g_value_unset(&ret);
2512 for(GList *item = g_list_last(shortcut->
closure); item; item = g_list_previous(item))
2516 if(GTK_IS_WIDGET(candidate->
base->data))
2518 GtkWidget *candidate_widget = GTK_WIDGET(candidate->
base->data);
2520 || gtk_widget_is_ancestor(candidate_widget, GTK_WIDGET(
state->main_window));
2521 if(in_main_window &&
IS_NULL_PTR(payload_in_main_window))
2522 payload_in_main_window = candidate;
2523 if(in_main_window && gtk_widget_get_visible(candidate_widget) && gtk_widget_get_mapped(candidate_widget))
2525 payload = candidate;
2531 if(
IS_NULL_PTR(payload)) payload = payload_in_main_window;
2536 target_widget = GTK_WIDGET(payload->
base->data);
2538 target_widget = shortcut->
widget;
2556 g_object_add_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2558 g_object_add_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2565 "[accel_search] dispatch closure target='%s' description='%s' handled=%d\n",
2566 path, desc, handled);
2570 const gboolean activated = gtk_widget_activate(shortcut_widget);
2572 "[accel_search] dispatch widget target='%s' description='%s' activated=%d widget=%s\n",
2573 path, desc, activated,
2574 !
IS_NULL_PTR(shortcut_widget) ? gtk_widget_get_name(shortcut_widget) :
"<destroyed>");
2579 "[accel_search] dispatch failed: no callable target for '%s' description='%s'\n",
2588 focused_widget = gtk_window_get_focus(
state->main_window);
2591 gboolean target_focused_gtk =
FALSE;
2592 gboolean target_focused_scroll =
FALSE;
2597 target_focused_gtk = focused_widget == target_widget
2598 || gtk_widget_is_ancestor(focused_widget, target_widget)
2599 || gtk_widget_is_ancestor(target_widget, focused_widget);
2603 target_focused_scroll = scroll_focused_widget == target_widget
2604 || gtk_widget_is_ancestor(scroll_focused_widget, target_widget)
2605 || gtk_widget_is_ancestor(target_widget, scroll_focused_widget);
2608 const gboolean target_focused = target_focused_gtk || target_focused_scroll;
2611 "[accel_search] focus check (pre-idle) target='%s' target_widget=%s(%p) gtk_focus=%s(%p)"
2612 " scroll_focus=%s(%p) target_focused_gtk=%d target_focused_scroll=%d target_focused=%d\n",
2614 !
IS_NULL_PTR(target_widget) ? gtk_widget_get_name(target_widget) :
"<null>",
2615 (
void *)target_widget,
2616 !
IS_NULL_PTR(focused_widget) ? gtk_widget_get_name(focused_widget) :
"<null>",
2617 (
void *)focused_widget,
2618 !
IS_NULL_PTR(scroll_focused_widget) ? gtk_widget_get_name(scroll_focused_widget) :
"<null>",
2619 (
void *)scroll_focused_widget,
2620 target_focused_gtk, target_focused_scroll, target_focused);
2625 && !g_strcmp0(G_OBJECT_TYPE_NAME(target_widget),
"DtBauhausWidget"))
2628 retry->
path = g_strdup(path);
2633 "[accel_search] dispatch retry scheduled target='%s' retry=%u\n",
2642 g_object_remove_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2644 g_object_remove_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2652 g_free(
state->path);
2654 return G_SOURCE_REMOVE;
2660 gtk_tree_model_get(
model, iter, 2, &rank, -1);
2665 GtkTreeIter *iter, gpointer user_data)
2669 gchar *key_query = g_strdup(
key);
2672 g_strstrip(key_query);
2673 if(key_query[0] ==
'\0')
2679 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2686 gchar *query = NULL;
2687 gtk_tree_model_get(
model, iter, 0, &query, -1);
2694 gchar *key_ci = g_utf8_casefold(key_query, -1);
2695 gchar *query_ci = g_utf8_casefold(query, -1);
2696 const gboolean match = !
IS_NULL_PTR(g_strrstr(query_ci, key_ci));
2708 const gchar *query_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2709 gchar *query = g_strdup(!
IS_NULL_PTR(query_text) ? query_text :
"");
2714 "[accel_search] validate query='%s' shortcut='%s' description='%s'\n",
2720 state->selected = shortcut;
2721 state->response = GTK_RESPONSE_ACCEPT;
2722 gtk_widget_destroy(window);
2730 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
2732 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2736 state->selected = NULL;
2741 GtkTreeViewColumn *column, gpointer user_data)
2745 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
return FALSE;
2748 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2754 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2756 if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
2758 if(!gtk_tree_model_get_iter_first(
state->filter_model, &iter))
return TRUE;
2762 if(!gtk_tree_model_iter_next(
state->filter_model, &iter))
return TRUE;
2766 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2768 if(!gtk_tree_path_prev(path))
2770 gtk_tree_path_free(path);
2774 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
2776 gtk_tree_path_free(path);
2779 gtk_tree_path_free(path);
2782 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2784 gtk_tree_selection_select_iter(selection, &iter);
2785 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2786 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2787 gtk_tree_path_free(path);
2788 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2798 return G_SOURCE_REMOVE;
2800 gchar *with_space = g_strconcat(
state->pending_space_query,
" ", NULL);
2801 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), with_space);
2802 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2805 state->pending_space_query = NULL;
2806 return G_SOURCE_REMOVE;
2810 GdkEventKey *event, gpointer user_data)
2815 if(
key == GDK_KEY_Escape)
2817 state->response = GTK_RESPONSE_CANCEL;
2818 gtk_widget_destroy(
state->window);
2822 if(
key == GDK_KEY_Down)
2824 if(
key == GDK_KEY_Up)
2826 if(
key == GDK_KEY_Return)
2829 gchar *command = NULL;
2832 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2833 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2838 : g_strdup(entry_text);
2843 g_strstrip(command);
2844 if(command[0] !=
'\0')
2847 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(
state->store), &iter))
2852 gchar *candidate_path = NULL;
2853 gtk_tree_model_get(GTK_TREE_MODEL(
state->store), &iter, 1, &candidate, 0, &candidate_path, -1);
2854 gchar *candidate_display_path = NULL;
2858 && (!g_strcmp0(command, candidate->
path)
2859 || !g_strcmp0(command, candidate_path)
2861 && !g_strcmp0(command, candidate_display_path))))
2863 shortcut = candidate;
2865 dt_free(candidate_display_path);
2869 dt_free(candidate_display_path);
2870 }
while(gtk_tree_model_iter_next(GTK_TREE_MODEL(
state->store), &iter));
2882 if(
key == GDK_KEY_space)
2888 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2891 gchar *query_only = NULL;
2892 gint sel_start = 0, sel_end = 0;
2893 const gboolean
has_selection = gtk_editable_get_selection_bounds(GTK_EDITABLE(
state->search_entry),
2894 &sel_start, &sel_end);
2896 : gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
2897 const gint text_chars = g_utf8_strlen(entry_text, -1);
2898 if(cursor_chars >= 0 && cursor_chars <= text_chars)
2899 query_only = g_utf8_substring(entry_text, 0, cursor_chars);
2901 query_only = g_strdup(entry_text);
2905 *((gchar *)sep) =
'\0';
2908 state->pending_space_query = query_only;
2911 if(
state->pending_space_idle_id != 0) g_source_remove(
state->pending_space_idle_id);
2916 gunichar key_char = gdk_keyval_to_unicode(event->keyval);
2917 const gboolean is_alnum = key_char != 0 && g_unichar_isalnum(key_char);
2924 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2930 const gint
position = gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
2931 const gsize query_len = sep - entry_text;
2932 gchar *query_only = g_strndup(entry_text, query_len);
2934 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query_only);
2935 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry),
MIN(
position, (gint)query_len));
2947 if(event->button != 1)
return FALSE;
2950 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2956 const gsize query_len = sep - entry_text;
2957 gchar *query = g_strndup(entry_text, query_len);
2959 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
2960 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2973 GtkAllocation allocation = { 0 };
2974 gtk_widget_get_allocation(widget, &allocation);
2976 gboolean click_inside =
FALSE;
2977 GdkWindow *win = gtk_widget_get_window(widget);
2980 gint wx = 0, wy = 0;
2981 gdk_window_get_origin(win, &wx, &wy);
2982 const gdouble x0 = (gdouble)wx;
2983 const gdouble y0 = (gdouble)wy;
2984 const gdouble x1 = x0 + (gdouble)allocation.width;
2985 const gdouble y1 = y0 + (gdouble)allocation.height;
2986 click_inside = (
event->x_root >= x0 &&
event->x_root < x1
2987 &&
event->y_root >= y0 &&
event->y_root < y1);
2991 click_inside = (
event->x >= 0.0 &&
event->x < allocation.width
2992 &&
event->y >= 0.0 &&
event->y < allocation.height);
2995 if(click_inside)
return FALSE;
2997 state->response = GTK_RESPONSE_CANCEL;
2998 gtk_widget_destroy(widget);
3005 if(
state->pending_space_idle_id != 0)
3007 g_source_remove(
state->pending_space_idle_id);
3008 state->pending_space_idle_id = 0;
3013 state->pending_space_query = NULL;
3015 gtk_grab_remove(widget);
3016 if(
state->window == widget)
state->window = NULL;
3022 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3023 gtk_window_set_title(GTK_WINDOW(window), _(
"Ansel - Search accelerators"));
3025#ifdef GDK_WINDOWING_QUARTZ
3029 const int dialog_width = 800;
3030 const int dialog_height = 0;
3032 gtk_window_set_decorated(GTK_WINDOW(window),
FALSE);
3033 gtk_window_set_modal(GTK_WINDOW(window),
FALSE);
3034 gtk_window_set_transient_for(GTK_WINDOW(window), main_window);
3035 gtk_window_set_attached_to(GTK_WINDOW(window), GTK_WIDGET(main_window));
3036 gtk_window_set_resizable(GTK_WINDOW(window),
FALSE);
3037 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window),
TRUE);
3038 gtk_window_set_skip_pager_hint(GTK_WINDOW(window),
TRUE);
3039 gtk_window_set_accept_focus(GTK_WINDOW(window),
TRUE);
3040 gtk_window_set_focus_on_map(GTK_WINDOW(window),
TRUE);
3041 gtk_window_set_default_size(GTK_WINDOW(window), dialog_width, dialog_height);
3042 gtk_widget_set_name(window,
"shortcut-search-dialog");
3043 gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3046 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);
3049 GMainLoop *loop = g_main_loop_new(NULL,
FALSE);
3052 .filter_model = NULL,
3053 .recent_entries = NULL,
3054 .main_window = main_window,
3055 .search_entry = NULL,
3059 .response = GTK_RESPONSE_CANCEL,
3061 .preferred_command = NULL,
3062 .suppress_inline_once =
FALSE,
3063 .pending_space_query = NULL,
3064 .pending_space_idle_id = 0
3068 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(
store), 2,
3070 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), 2, GTK_SORT_ASCENDING);
3073 GtkWidget *search_entry = gtk_search_entry_new();
3074 state.search_entry = search_entry;
3075 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3076 gtk_container_add(GTK_CONTAINER(window), box);
3077 GtkWidget *search_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
3078 gtk_box_pack_start(GTK_BOX(box), search_row,
TRUE,
TRUE, 0);
3079 gtk_box_pack_start(GTK_BOX(search_row), search_entry,
TRUE,
TRUE, 0);
3081 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
3082 state.filter_model = filter_model;
3083 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
3086 GtkEntryCompletion *completion = gtk_entry_completion_new();
3087 GtkListStore *recent_entries = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
3089 state.recent_entries = recent_entries;
3091 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(recent_entries), 4,
3093 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(recent_entries), 4, GTK_SORT_ASCENDING);
3094 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(recent_entries));
3095 gtk_entry_completion_set_text_column(completion, 3);
3096 gtk_entry_completion_set_inline_completion(completion,
TRUE);
3097 gtk_entry_completion_set_inline_selection(completion,
TRUE);
3098 gtk_entry_completion_set_popup_completion(completion,
FALSE);
3100 gtk_entry_set_completion(GTK_ENTRY(search_entry), completion);
3101 g_object_unref(completion);
3104 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(
scrolled),
3105 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3106 gtk_widget_set_size_request(
scrolled, dialog_width, 320);
3110 GtkWidget *tree_view = gtk_tree_view_new_with_model(filter_model);
3111 state.tree_view = tree_view;
3112 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view),
FALSE);
3113 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree_view),
FALSE);
3114 gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(tree_view),
FALSE);
3115 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(tree_view),
TRUE);
3116 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view), 0);
3117 gtk_widget_set_hexpand(tree_view,
TRUE);
3118 gtk_widget_set_vexpand(tree_view,
TRUE);
3119 gtk_container_add(GTK_CONTAINER(
scrolled), tree_view);
3121 GtkCellRenderer *txt = gtk_cell_renderer_text_new();
3122 g_object_set(txt,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE,
"max-width-chars", 70, NULL);
3123 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, txt,
"text", 0, NULL);
3124 gtk_tree_view_column_set_expand(column,
FALSE);
3125 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3126 gtk_tree_view_column_set_min_width(column, 360);
3127 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3129 GtkCellRenderer *accel = gtk_cell_renderer_accel_new();
3130 g_object_set(accel,
"editable",
FALSE,
"accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER, NULL);
3131 column = gtk_tree_view_column_new_with_attributes(NULL, accel,
"accel-key", 5,
"accel-mods", 6, NULL);
3132 gtk_tree_view_column_set_min_width(column, 140);
3133 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3135 GtkCellRenderer *
description = gtk_cell_renderer_text_new();
3136 g_object_set(
description,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE, NULL);
3137 column = gtk_tree_view_column_new_with_attributes(NULL,
description,
"text", 3, NULL);
3138 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3139 gtk_tree_view_column_set_min_width(column, 280);
3140 gtk_tree_view_column_set_expand(column,
TRUE);
3141 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3143 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
3144 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
3162 GtkAllocation main_alloc = { 0 };
3163 gtk_widget_get_allocation(GTK_WIDGET(main_window), &main_alloc);
3164 gint main_x = 0, main_y = 0;
3165 GdkWindow *main_gdk_window = gtk_widget_get_window(GTK_WIDGET(main_window));
3167 gdk_window_get_origin(main_gdk_window, &main_x, &main_y);
3169 gtk_window_get_position(main_window, &main_x, &main_y);
3171 gint top_panel_height = 0;
3175 if(!
IS_NULL_PTR(top_panel) && gtk_widget_get_visible(top_panel))
3176 top_panel_height = gtk_widget_get_allocated_height(top_panel);
3179 const gint window_x = main_x +
MAX((main_alloc.width - dialog_width) / 2, 0);
3180 const gint window_y = main_y +
MAX(top_panel_height, 0);
3181 gtk_window_move(GTK_WINDOW(window), window_x, window_y);
3183 gtk_widget_realize(window);
3184 gtk_widget_show_all(window);
3185 gtk_grab_add(window);
3186 gdk_window_focus(gtk_widget_get_window(window), GDK_CURRENT_TIME);
3187 gtk_window_set_focus(GTK_WINDOW(window), search_entry);
3188 gtk_widget_grab_focus(search_entry);
3190 g_main_loop_run(loop);
3191 g_main_loop_unref(loop);
3195 dispatch->
path = g_strdup(
state.selected->path);
3197 ?
state.selected->accels
3205 g_object_unref(
store);
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_shortcut_set_closure(dt_shortcut_t *shortcut, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gpointer 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.
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)
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 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 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)
static void _shortcut_search_destroy(GtkWidget *widget, gpointer user_data)
#define DT_ACCEL_SEARCH_INLINE_SEPARATOR
void dt_accels_cleanup(dt_accels_t *accels)
#define DT_ACCEL_SEARCH_RECENT_KEY
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)
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
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_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_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....
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, 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 _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 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)
#define DT_ACCELS_WIDGET_SHORTCUT_KEY
#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))
int dt_conf_key_exists(const char *key)
gchar * dt_conf_get_string(const char *name)
void dt_conf_set_string(const char *name, const char *val)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
static void dt_free_gpointer(gpointer ptr)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
static const dt_aligned_pixel_simd_t value
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
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)
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 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_gui_refocus_center()
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
#define DT_GUI_BOX_SPACING
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
GtkCellRenderer * dtgtk_cell_renderer_button_new(void)
float *const restrict const size_t k
dt_mipmap_buffer_dsc_flags flags
void dt_osx_disallow_fullscreen(GtkWidget *widget)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
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
GtkWidget * has_scroll_focus
GtkAccelGroup * accel_group
gboolean virtual_shortcut
GtkWidget * panels[DT_UI_PANEL_SIZE]