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);
554 GdkModifierType mods, gpointer user_data)
558 g_signal_emit_by_name(shortcut->
widget, shortcut->
signal);
564 GdkModifierType mods, gpointer user_data)
570 gtk_widget_grab_focus(shortcut->
widget);
574 GdkKeymapKey *keys = NULL;
576 GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
577 if(gdk_keymap_get_entries_for_keyval(keymap, shortcut->
key, &keys, &
n))
579 if(
n > 0) keycode = keys[0].keycode;
584 GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS);
585 ev->key.window = g_object_ref(gtk_widget_get_window(shortcut->
widget));
586 ev->key.send_event =
TRUE;
587 ev->key.time = GDK_CURRENT_TIME;
588 ev->key.state = shortcut->
mods;
589 ev->key.keyval = shortcut->
key;
590 ev->key.hardware_keycode = keycode;
592 ev->key.is_modifier =
FALSE;
595 gtk_widget_event(shortcut->
widget, ev);
603 GtkWidget *widget, guint key_val, GdkModifierType accel_mods)
610 if(shortcut && shortcut->
widget == widget)
620 shortcut->
widget = widget;
622 shortcut->
path = g_strdup(accel_path);
624 shortcut->
key = key_val;
625 shortcut->
mods = accel_mods;
629 shortcut->
description = _(
"Contextual interaction on focus");
630 shortcut->
accels = accels;
638 gboolean (*action_callback)(GtkAccelGroup *group,
639 GObject *acceleratable, guint keyval,
640 GdkModifierType mods, gpointer user_data),
641 gpointer data, GtkAccelGroup *accel_group,
const gchar *action_scope,
642 const gchar *action_name)
657 shortcut->
path = g_strdup(accel_path);
665 shortcut->
accels = accels;
678 GtkAccelGroup *accel_group,
const gchar *accel_path, guint key_val,
679 GdkModifierType accel_mods,
const gboolean
lock)
686 if(shortcut && shortcut->
widget == widget)
695 GtkAccelKey
key = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
697 shortcut->
widget = widget;
706 shortcut->
widget = widget;
708 shortcut->
path = g_strdup(accel_path);
709 shortcut->
signal = signal;
710 shortcut->
key = key_val;
711 shortcut->
mods = accel_mods;
716 shortcut->
accels = accels;
734 gboolean (*action_callback)(GtkAccelGroup *group, GObject *acceleratable,
735 guint keyval, GdkModifierType mods,
737 gpointer data, GtkAccelGroup *accel_group,
const gchar *action_scope,
738 const gchar *action_name, guint key_val, GdkModifierType accel_mods,
750 if(closure && closure->data == data)
771 shortcut->
path = g_strdup(accel_path);
773 shortcut->
key = key_val;
774 shortcut->
mods = accel_mods;
779 shortcut->
accels = accels;
799 GtkAccelKey
key = { 0 };
802 const gboolean is_known = gtk_accel_map_lookup_entry(shortcut->
path, &
key);
803 if(!is_known)
return;
806 const GtkAccelKey oldkey = { .accel_key = shortcut->
key, .accel_mods = shortcut->
mods, .accel_flags = 0 };
814 const gboolean needs_cleanup = changed && oldkey.accel_key > 0 && oldtype !=
DT_SHORTCUT_UNSET;
817 const gboolean needs_init = changed &&
key.accel_key > 0;
855 if(g_strrstr(shortcut->
path, params->path) != NULL)
906 if(strncmp(scope,
"<Ansel>/", strlen(
"<Ansel>/")) == 0)
907 return g_strdup_printf(
"%s/%s", scope, feature);
909 return g_strdup_printf(
"<Ansel>/%s/%s", scope, feature);
917 gdk_event_get_state(event, mods);
923 GdkModifierType consumed;
924 gdk_keymap_translate_keyboard_state(accels->
keymap, event->key.hardware_keycode, event->key.state,
926 keyval, NULL, NULL, &consumed);
930 gchar *accel_name = gtk_accelerator_name(*keyval, *mods);
932 (event->type == GDK_KEY_PRESS) ?
"Key pressed" :
"Key released", accel_name);
939 if(gdk_keyval_to_lower(*keyval) == gdk_keyval_to_upper(*keyval))
947 if(*keyval == GDK_KEY_ISO_Left_Tab)
949 *keyval = GDK_KEY_Tab;
950 *mods |= GDK_SHIFT_MASK;
970 return gdk_keyval_to_lower(keyval);
977 const gchar *path = (
const gchar *)
key;
986 && shortcut_key == result_key
989 if(!g_strcmp0(path, shortcut->
path))
996 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1013 GList *item = g_list_first(result.
results);
1014 if(item) path = (
char *)item->data;
1024 const gchar *path = (
const gchar *)
key;
1033 && shortcut_key == result_key
1037 if(!g_strcmp0(path, shortcut->
path))
1044 fprintf(stderr,
"[shortcuts] ERROR: the shortcut path '%s' is known under the key '%s' in hashtable\n", shortcut->
path, path);
1050 GdkModifierType mods)
1059 GList *item = g_list_first(result.
results);
1072 gchar *accel_name = gtk_accelerator_name(keyval, mods);
1105 if(!(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE || event->type == GDK_SCROLL))
1109 if(event->type == GDK_SCROLL)
1118 GdkModifierType mods;
1136 if(event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
1138 GtkWidget *focused = gtk_window_get_focus(GTK_WINDOW(w));
1139 if(!
IS_NULL_PTR(focused) && (GTK_IS_EDITABLE(focused) || GTK_IS_TEXT_VIEW(focused)))
1147 if(event->type == GDK_KEY_PRESS &&
1155 else if(event->type == GDK_KEY_RELEASE)
1207 GtkTreeIter *iter, gpointer data)
1211 g_object_set(renderer,
1214 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
1219 GtkTreeIter *iter, gpointer data)
1223 g_object_set (renderer,
1224 "icon-name", (!
IS_NULL_PTR(shortcut) && !shortcut->
locked) ?
"edit-delete-symbolic" :
"lock",
1237 if(!gdk_keymap_get_entries_for_keycode(accels->
keymap, hardware_keycode, &keys, &keyvals, &n_keys))
1240 for(
int i = 0;
i < n_keys; ++
i)
1242 if(keyvals[
i] == keyval)
1244 int group = keys[
i].group;
1257 guint hardware_key, gpointer user_data)
1261 GtkTreeModel *filter = GTK_TREE_MODEL(user_data);
1262 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
1265 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1271 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(filter), &f_iter, path))
1272 gtk_tree_model_get(GTK_TREE_MODEL(filter), &f_iter,
COL_SHORTCUT, &shortcut, -1);
1274 const char *shortcut_path = NULL;
1280 if(keyval == GDK_KEY_VoidSymbol
1281 || (mods == 0 && (keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace)))
1290 if(keyval != 0 || mods != 0)
1292 GdkDisplay *display = gdk_display_get_default();
1293 GdkSeat *seat = gdk_display_get_default_seat(display);
1294 GdkDevice *pointer = gdk_seat_get_pointer(seat);
1295 GdkModifierType
state;
1296 gdk_device_get_state(pointer, gdk_get_default_root_window(), NULL, &
state);
1300 GdkEventKey
event = { 0 };
1301 event.type = GDK_KEY_PRESS;
1302 event.state = mods |
state;
1303 event.keyval = keyval;
1304 event.hardware_keycode = hardware_key;
1312 if(!(keyval == 0 && mods == 0))
1316 if(
IS_NULL_PTR(shortcut_path) && gtk_accel_map_change_entry(shortcut->
path, keyval, mods,
FALSE))
1326 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter), &s_iter, &f_iter);
1335 char *new_text = gtk_accelerator_name(keyval, mods);
1337 = 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",
1338 _(
"The shortcut for"), shortcut_path,
1339 _(
"is already using the key combination"), new_text,
1340 _(
"Delete it first."));
1341 gtk_dialog_run(GTK_DIALOG(dlg));
1342 gtk_widget_destroy(dlg);
1346 gtk_tree_path_free(path);
1349static void _shortcut_cleared(GtkCellRendererAccel *renderer,
const gchar *path_string, gpointer user_data)
1351 _shortcut_edited(GTK_CELL_RENDERER(renderer), path_string, 0, 0, 0, user_data);
1356 GdkRectangle *background, GdkRectangle *cell_area, GtkCellRendererState
flags,
1368 gtk_tree_store_set(
store, iter,
1382 const gchar *path = (
const gchar *)
key;
1389 GtkTreeIter *parent = NULL;
1390 GtkTreeIter *iter = NULL;
1397 gchar **parts = g_strsplit(path,
"/", -1);
1398 gchar *accum = g_strdup(
"<Ansel>");
1402 const size_t len_ansel = strlen(accum);
1403 for(
int i = 1; parts[
i]; ++
i)
1406 gchar *tmp = g_strconcat(accum,
"/", parts[
i], NULL);
1412 iter = g_hash_table_lookup(node_cache, accum);
1419 GtkTreeIter new_iter;
1420 gtk_tree_store_append(
store, &new_iter, parent);
1423 iter = g_new(GtkTreeIter, 1);
1425 g_hash_table_insert(node_cache, g_strdup(accum), iter);
1429 gchar *label = g_strdup(parts[
i]);
1433 if(!g_strcmp0(accum, path))
1451 gchar **parts = g_strsplit(path,
"/", -1);
1452 const gint len = g_strv_length(parts);
1455 tail = g_strjoinv(
"/", parts + 2);
1457 tail = g_strdup(parts[1]);
1459 tail = g_strdup(parts[0]);
1469 const gchar *path = (
const gchar *)
key;
1471 GtkListStore *
store = (GtkListStore *)user_data;
1483 gchar **tail_parts = g_strsplit(tail,
"/", -1);
1484 const gint tail_len = g_strv_length(tail_parts);
1485 const gchar *leaf = tail;
1486 if(tail_len > 0 && !
IS_NULL_PTR(tail_parts[tail_len - 1]) && tail_parts[tail_len - 1][0] !=
'\0')
1487 leaf = tail_parts[tail_len - 1];
1490 gtk_list_store_append(
store, &iter);
1491 gtk_list_store_set(
store, &iter,
1500 g_strfreev(tail_parts);
1509 gchar *pa = NULL, *pb = NULL;
1510 gtk_tree_model_get(
model, a, 2, &ka, 0, &pa, -1);
1511 gtk_tree_model_get(
model, b, 2, &kb, 0, &pb, -1);
1523 gchar *pa_ci = g_utf8_casefold(pa, -1);
1524 gchar *pb_ci = g_utf8_casefold(pb, -1);
1525 gchar **pa_parts = g_strsplit(pa_ci,
"/", -1);
1526 gchar **pb_parts = g_strsplit(pb_ci,
"/", -1);
1528 for(gint
i = 0;;
i++)
1530 const gchar *pa_part = pa_parts[
i];
1531 const gchar *pb_part = pb_parts[
i];
1548 ret = g_utf8_collate(pa_part, pb_part);
1552 g_strfreev(pb_parts);
1553 g_strfreev(pa_parts);
1566 gtk_tree_model_get(
model, a, GPOINTER_TO_INT(data), &ka, -1);
1567 gtk_tree_model_get(
model, b, GPOINTER_TO_INT(data), &kb, -1);
1573 gchar *ka_ci = g_utf8_casefold(ka, -1);
1574 gchar *kb_ci = g_utf8_casefold(kb, -1);
1577 res = g_utf8_collate(ka_ci, kb_ci);
1601 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(params->path_search));
1602 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(params->keys_search));
1604 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1605 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1608 gboolean show =
TRUE;
1613 if(needle_path && needle_path[0])
1615 if(path && path[0] !=
'\0')
1617 gchar *needle_ci = g_utf8_casefold(needle_path, -1);
1618 gchar *haystack_ci = g_utf8_casefold(path, -1);
1619 show &= (g_strrstr(haystack_ci, needle_ci) != NULL);
1631 if(needle_keys && needle_keys[0] !=
'\0')
1633 guint search_keyval = 0;
1634 GdkModifierType search_mods = 0;
1635 gtk_accelerator_parse(needle_keys, &search_keyval, &search_mods);
1636 if(search_keyval || search_mods)
1639 GdkModifierType mods = 0;
1646 if(search_keyval && search_mods)
1647 show &= (keyval == search_keyval && mods == search_mods);
1649 show &= ((keyval && keyval == search_keyval) || (mods && mods == search_mods));
1658 if(show)
return TRUE;
1661 if(gtk_tree_model_iter_has_child(
model, iter))
1664 if(gtk_tree_model_iter_children(
model, &child, iter))
1670 }
while(gtk_tree_model_iter_next(
model, &child));
1680 GtkTreeView *tree_view = GTK_TREE_VIEW(params->tree_view);
1681 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(tree_view)));
1684 const gchar *needle_path = gtk_entry_get_text(GTK_ENTRY(params->path_search));
1685 const gchar *needle_keys = gtk_entry_get_text(GTK_ENTRY(params->keys_search));
1687 if((
IS_NULL_PTR(needle_path) || needle_path[0] ==
'\0') &&
1688 (
IS_NULL_PTR(needle_keys) || needle_keys[0] ==
'\0'))
1689 gtk_tree_view_collapse_all(GTK_TREE_VIEW(params->tree_view));
1691 gtk_tree_view_expand_all(GTK_TREE_VIEW(params->tree_view));
1701 params->keys_search = gtk_search_entry_new();
1702 params->path_search = gtk_search_entry_new();
1703 GtkWidget *tree_view = params->tree_view = gtk_tree_view_new();
1709 = { {
"Primary>", N_(
"<Primary> - Decoded as <Control> on Windows/Linux or <Meta> on Mac OS") },
1710 {
"Control>", N_(
"<Control>") },
1711 {
"Shift>", N_(
"<Shift>") },
1712 {
"Alt>", N_(
"<Alt>") },
1713 {
"Super>", N_(
"<Super> - The Windows key on PC") },
1714 {
"Hyper>", N_(
"<Hyper>") },
1715 {
"Meta>", N_(
"<Meta> - Decoded as <Command> on Mac OS") },
1718 gtk_widget_set_tooltip_text(params->keys_search, _(
"Look for keys and modifiers codes, as `<Modifier>Key`.\n"
1719 "Type `<` to start the auto-completion"));
1721 gtk_widget_set_tooltip_text(params->path_search, _(
"Case-insensitive search for keywords of full pathes.\n"
1722 "Ex: `darkroom/controls/sliders`"));
1726 gtk_window_set_title(GTK_WINDOW(dialog), _(
"Ansel - Keyboard shortcuts"));
1728#ifdef GDK_WINDOWING_QUARTZ
1730 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1733 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1734 gtk_window_set_modal(GTK_WINDOW(dialog),
TRUE);
1735 gtk_window_set_transient_for(GTK_WINDOW(dialog), main_window);
1736 gtk_window_set_default_size(GTK_WINDOW(dialog), 1100, 900);
1739 GtkTreeStore *
store = gtk_tree_store_new(
NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1740 G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT);
1746 g_hash_table_destroy(node_cache);
1752 GINT_TO_POINTER(
i), NULL);
1753 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store),
i, GTK_SORT_ASCENDING);
1757 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
1758 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
filter_callback, params, NULL);
1761 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), filter_model);
1762 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view),
COL_PATH);
1763 gtk_widget_set_hexpand(tree_view,
TRUE);
1764 gtk_widget_set_vexpand(tree_view,
TRUE);
1765 gtk_widget_set_halign(tree_view, GTK_ALIGN_FILL);
1766 gtk_widget_set_valign(tree_view, GTK_ALIGN_FILL);
1768 g_signal_connect(G_OBJECT(params->path_search),
"changed", G_CALLBACK(
search_changed), params);
1769 g_signal_connect(G_OBJECT(params->keys_search),
"changed", G_CALLBACK(
search_changed), params);
1772 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_(
"View / Scope / Feature / Control"), gtk_cell_renderer_text_new(),
"text",
COL_NAME, NULL);
1773 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1775 GtkCellRenderer *renderer = gtk_cell_renderer_accel_new();
1776 column = gtk_tree_view_column_new_with_attributes(_(
"Keys"), renderer,
"accel-key",
COL_KEYVAL,
"accel-mods",
1779 g_signal_connect(renderer,
"accel-edited", G_CALLBACK(
_shortcut_edited), filter_model);
1780 g_signal_connect(renderer,
"accel-cleared", G_CALLBACK(
_shortcut_cleared), filter_model);
1781 gtk_tree_view_column_set_min_width(column, 100);
1782 gtk_tree_view_column_set_resizable(column,
TRUE);
1783 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1786 g_object_set(renderer,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
1787 column = gtk_tree_view_column_new_with_attributes(_(
"Clear"), renderer,
"pixbuf",
COL_CLEAR, NULL);
1789 g_signal_connect(renderer,
"activate", G_CALLBACK(
_icon_activate), filter_model);
1790 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1792 column = gtk_tree_view_column_new_with_attributes(_(
"Description"), gtk_cell_renderer_text_new(),
"text",
COL_DESCRIPTION, NULL);
1793 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
1797 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), box,
TRUE,
TRUE, 0);
1800 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by feature : ")),
FALSE,
FALSE, 0);
1801 gtk_box_pack_start(GTK_BOX(hbox), params->path_search,
TRUE,
TRUE, 0);
1802 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(
"Search by keys : ")),
FALSE,
FALSE, 0);
1803 gtk_box_pack_start(GTK_BOX(hbox), params->keys_search,
TRUE,
TRUE, 0);
1804 gtk_box_pack_start(GTK_BOX(box), hbox,
FALSE,
FALSE, 0);
1806 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1808 gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
1809 gtk_box_pack_start(GTK_BOX(box), scrolled_window,
TRUE,
TRUE, 0);
1811 gtk_widget_set_visible(tree_view,
TRUE);
1812 gtk_widget_show_all(dialog);
1814 gtk_dialog_run(GTK_DIALOG(dialog));
1815 gtk_widget_destroy(dialog);
1816 g_object_unref(filter_model);
1817 g_object_unref(
store);
1829 if(
IS_NULL_PTR(needle) || needle[0] ==
'\0')
return 0;
1833 gtk_tree_model_get(
model, iter, 0, &label, -1);
1841 gchar *label_ci = g_utf8_casefold(label, -1);
1843 gchar **parts = g_strsplit(label_ci,
"/", -1);
1844 gchar *needle_copy = g_strdup(needle);
1845 gchar **tokens = g_strsplit_set(needle_copy,
" \t\r\n", -1);
1847 gboolean has_token =
FALSE;
1848 gboolean all_matched =
TRUE;
1851 if(tokens[
t][0] ==
'\0')
continue;
1853 int best_token_rank = INT_MAX;
1858 const char *match = g_strstr_len(parts[
i], -1, tokens[
t]);
1861 const int cell_rank =
i * 10000;
1862 const int in_cell_rank = match - parts[
i];
1863 const int token_rank = cell_rank + in_cell_rank;
1864 if(token_rank < best_token_rank) best_token_rank = token_rank;
1867 if(best_token_rank == INT_MAX)
1869 all_matched =
FALSE;
1872 rank_sum += best_token_rank;
1875 if(all_matched) ret = has_token ? rank_sum : 0;
1889 const gchar *needle = gtk_entry_get_text(GTK_ENTRY(
search_entry));
1890 gchar *needle_query = g_strdup(!
IS_NULL_PTR(needle) ? needle :
"");
1893 g_strstrip(needle_query);
1894 gchar *needle_ci = g_utf8_casefold(needle_query, -1);
1898 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1899 GTK_SORT_ASCENDING);
1902 if(gtk_tree_model_get_iter_first(
model, &iter))
1907 gtk_list_store_set(GTK_LIST_STORE(
model), &iter, 2, rank, -1);
1909 }
while(gtk_tree_model_iter_next(
model, &iter));
1916 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), 2, GTK_SORT_ASCENDING);
1917 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
model));
1938#define DT_ACCEL_SEARCH_RECENT_KEY "plugins/accel_search/recent_entries"
1939#define DT_ACCEL_SEARCH_RECENT_MAX 20
1944 gtk_list_store_clear(
store);
1963 if(entry[0] ==
'\0')
1971 gchar **parts = g_strsplit(entry,
"\t", 3);
1973 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
1980 const gchar *query = parts[0];
1981 const gchar *command = parts[1];
1986 gtk_list_store_append(
store, &iter);
1987 gtk_list_store_set(
store, &iter,
2004 const gchar *search_text =
"";
2007 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2008 if(!
IS_NULL_PTR(entry_text)) search_text = entry_text;
2011 gchar *query_text = g_strdup(search_text);
2014 g_strstrip(query_text);
2015 gchar *query_ci = g_utf8_casefold(query_text, -1);
2016 const gboolean has_query = query_ci[0] !=
'\0';
2018 gchar *a_query = NULL, *a_command = NULL;
2019 gchar *b_query = NULL, *b_command = NULL;
2020 gint a_recent = G_MAXINT, b_recent = G_MAXINT;
2021 gtk_tree_model_get(
model, a, 0, &a_query, 1, &a_command, 4, &a_recent, -1);
2022 gtk_tree_model_get(
model, b, 0, &b_query, 1, &b_command, 4, &b_recent, -1);
2024 gchar *a_query_ci = g_utf8_casefold(!
IS_NULL_PTR(a_query) ? a_query :
"", -1);
2025 gchar *a_command_ci = g_utf8_casefold(!
IS_NULL_PTR(a_command) ? a_command :
"", -1);
2026 gchar *b_query_ci = g_utf8_casefold(!
IS_NULL_PTR(b_query) ? b_query :
"", -1);
2027 gchar *b_command_ci = g_utf8_casefold(!
IS_NULL_PTR(b_command) ? b_command :
"", -1);
2029 gint a_rank = 400000, b_rank = 400000;
2032 if(g_str_has_prefix(a_query_ci, query_ci))
2034 else if(g_str_has_prefix(a_command_ci, query_ci))
2038 const gchar *a_match_query = g_strstr_len(a_query_ci, -1, query_ci);
2040 a_rank = 200000 + (a_match_query - a_query_ci);
2043 const gchar *a_match_command = g_strstr_len(a_command_ci, -1, query_ci);
2045 a_rank = 300000 + (a_match_command - a_command_ci);
2049 if(g_str_has_prefix(b_query_ci, query_ci))
2051 else if(g_str_has_prefix(b_command_ci, query_ci))
2055 const gchar *b_match_query = g_strstr_len(b_query_ci, -1, query_ci);
2057 b_rank = 200000 + (b_match_query - b_query_ci);
2060 const gchar *b_match_command = g_strstr_len(b_command_ci, -1, query_ci);
2062 b_rank = 300000 + (b_match_command - b_command_ci);
2067 gint ret = a_rank - b_rank;
2068 if(ret == 0) ret = a_recent - b_recent;
2069 if(ret == 0) ret = g_utf8_collate(a_query_ci, b_query_ci);
2070 if(ret == 0) ret = g_utf8_collate(a_command_ci, b_command_ci);
2090 gchar *trimmed = g_strdup(query);
2091 g_strstrip(trimmed);
2092 if(trimmed[0] ==
'\0')
2098 gchar *command = g_strdup(shortcut->
path);
2099 g_strdelimit(trimmed,
"\t\r\n",
' ');
2100 g_strdelimit(command,
"\t\r\n",
' ');
2102 GPtrArray *
entries = g_ptr_array_new_with_free_func(g_free);
2103 GPtrArray *entries_ci = g_ptr_array_new_with_free_func(g_free);
2104 g_ptr_array_add(
entries, g_strdup_printf(
"%s\t%s", trimmed, command));
2105 g_ptr_array_add(entries_ci, g_utf8_casefold(trimmed, -1));
2118 if(
IS_NULL_PTR(candidate) || candidate[0] ==
'\0')
2123 g_strstrip(candidate);
2124 if(candidate[0] ==
'\0')
2130 gchar **parts = g_strsplit(candidate,
"\t", 3);
2132 || parts[0][0] ==
'\0' || parts[1][0] ==
'\0')
2139 const gchar *candidate_query = parts[0];
2140 const gchar *candidate_command = parts[1];
2142 gchar *candidate_ci = g_utf8_casefold(candidate_query, -1);
2143 gboolean found =
FALSE;
2144 for(guint
k = 0;
k < entries_ci->len;
k++)
2146 const char *kept_ci = g_ptr_array_index(entries_ci,
k);
2147 if(!g_strcmp0(kept_ci, candidate_ci))
2160 g_ptr_array_add(
entries, g_strdup_printf(
"%s\t%s", candidate_query, candidate_command));
2161 g_ptr_array_add(entries_ci, candidate_ci);
2169 const gchar *entry_value = (
i <
entries->len) ? (
const gchar *)g_ptr_array_index(
entries,
i) :
"";
2175 g_ptr_array_free(entries_ci,
TRUE);
2200 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(
state->recent_entries));
2202 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(
state->filter_model));
2204 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2205 gtk_tree_selection_unselect_all(selection);
2208 gboolean has_iter = gtk_tree_model_get_iter_first(
state->filter_model, &iter);
2209 GtkTreeIter fallback_iter = iter;
2210 GtkTreeIter selected_iter = iter;
2211 gboolean found_preferred =
FALSE;
2217 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2219 && !g_strcmp0(shortcut->
path,
state->preferred_command))
2221 selected_iter = iter;
2222 found_preferred =
TRUE;
2225 }
while(gtk_tree_model_iter_next(
state->filter_model, &iter));
2230 GtkTreeIter *target_iter = found_preferred ? &selected_iter : &fallback_iter;
2231 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, target_iter);
2233 gtk_tree_selection_select_iter(selection, target_iter);
2234 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2235 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2236 gtk_tree_path_free(path);
2238 gtk_tree_model_get(
state->filter_model, target_iter, 1, &
state->selected, -1);
2243 GtkTreeIter *iter, gpointer user_data)
2246 gchar *query = NULL;
2247 gchar *command = NULL;
2248 gtk_tree_model_get(
model, iter, 0, &query, 1, &command, -1);
2253 state->preferred_command = NULL;
2256 state->preferred_command = g_strdup(command);
2260 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
2261 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2273 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2274 if(
state->suppress_inline_once)
2280 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2282 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2284 const gchar *last = g_utf8_find_prev_char(entry_text, entry_text + strlen(entry_text));
2285 const gunichar last_char = !
IS_NULL_PTR(last) ? g_utf8_get_char(last) : 0;
2286 if(last_char != 0 && !g_unichar_isalnum(last_char))
2289 gchar *query = g_strdup(!
IS_NULL_PTR(entry_text) ? entry_text :
"");
2293 if(query[0] ==
'\0')
2299 gchar *query_ci = g_utf8_casefold(query, -1);
2300 if(query_ci[0] ==
'\0')
2307 gint best_rank = G_MAXINT;
2308 gint best_recent = G_MAXINT;
2309 gchar *best_command = NULL;
2310 gchar *best_display = NULL;
2311 gchar *best_query_ci = NULL;
2312 gchar *best_command_ci = NULL;
2313 const glong query_len = g_utf8_strlen(query_ci, -1);
2316 if(gtk_tree_model_get_iter_first(
model, &iter))
2320 gchar *row_query = NULL;
2321 gchar *row_command = NULL;
2322 gchar *row_display = NULL;
2323 gint row_recent = G_MAXINT;
2324 gtk_tree_model_get(
model, &iter, 0, &row_query, 1, &row_command, 3, &row_display, 4, &row_recent, -1);
2333 gchar *row_query_ci = g_utf8_casefold(row_query, -1);
2334 gchar *row_command_ci = g_utf8_casefold(!
IS_NULL_PTR(row_command) ? row_command :
"", -1);
2335 gint row_rank = G_MAXINT;
2336 if(g_str_has_prefix(row_query_ci, query_ci))
2339 const glong row_query_len = g_utf8_strlen(row_query_ci, -1);
2340 row_rank =
MAX((gint)(row_query_len - query_len), 0);
2342 else if(g_str_has_prefix(row_command_ci, query_ci))
2344 const glong row_command_len = g_utf8_strlen(row_command_ci, -1);
2345 row_rank = 100000 +
MAX((gint)(row_command_len - query_len), 0);
2349 const gchar *match_query = g_strstr_len(row_query_ci, -1, query_ci);
2351 row_rank = 200000 + (match_query - row_query_ci);
2354 const gchar *match_command = g_strstr_len(row_command_ci, -1, query_ci);
2356 row_rank = 300000 + (match_command - row_command_ci);
2360 if(row_rank < best_rank
2361 || (row_rank == best_rank && row_recent < best_recent)
2362 || (row_rank == best_rank && row_recent == best_recent
2363 && (!
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) < 0))
2364 || (row_rank == best_rank && row_recent == best_recent
2365 && !
IS_NULL_PTR(best_query_ci) && g_utf8_collate(row_query_ci, best_query_ci) == 0
2366 && (!
IS_NULL_PTR(best_command_ci) && g_utf8_collate(row_command_ci, best_command_ci) < 0)))
2368 best_rank = row_rank;
2369 best_recent = row_recent;
2374 best_command = g_strdup(!
IS_NULL_PTR(row_command) ? row_command :
"");
2375 best_display = g_strdup(!
IS_NULL_PTR(row_display) ? row_display : row_query);
2376 best_query_ci = g_strdup(row_query_ci);
2377 best_command_ci = g_strdup(row_command_ci);
2385 }
while(gtk_tree_model_iter_next(
model, &iter));
2388 if(!
IS_NULL_PTR(best_command) && best_command[0] !=
'\0' && best_rank < G_MAXINT)
2393 state->preferred_command = NULL;
2395 state->preferred_command = g_strdup(best_command);
2397 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2398 GtkTreeIter filter_iter;
2399 if(gtk_tree_model_get_iter_first(
state->filter_model, &filter_iter))
2404 gchar *shortcut_path_display = NULL;
2405 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &shortcut, 0, &shortcut_path_display, -1);
2406 gchar *shortcut_trimmed = NULL;
2410 && (!g_strcmp0(shortcut->
path, best_command)
2411 || (!
IS_NULL_PTR(shortcut_trimmed) && !g_strcmp0(shortcut_trimmed, best_command))
2412 || (!
IS_NULL_PTR(shortcut_path_display) && !g_strcmp0(shortcut_path_display, best_command))))
2414 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &filter_iter);
2418 dt_free(shortcut_path_display);
2421 gtk_tree_selection_select_iter(selection, &filter_iter);
2422 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2423 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2424 gtk_tree_path_free(path);
2425 gtk_tree_model_get(
state->filter_model, &filter_iter, 1, &
state->selected, -1);
2427 dt_free(shortcut_path_display);
2431 dt_free(shortcut_path_display);
2432 }
while(gtk_tree_model_iter_next(
state->filter_model, &filter_iter));
2435 if(!
IS_NULL_PTR(best_display) && best_display[0] !=
'\0')
2437 const gchar *current = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2438 if(g_strcmp0(current, best_display))
2440 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), best_display);
2442 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1));
2443 gtk_editable_select_region(GTK_EDITABLE(
state->search_entry), g_utf8_strlen(query, -1), -1);
2471 GValue params[4] = { G_VALUE_INIT };
2473 g_value_init(¶ms[0], G_TYPE_POINTER);
2474 g_value_set_pointer(¶ms[0], shortcut->
accel_group);
2476 g_value_init(¶ms[1], G_TYPE_POINTER);
2477 g_value_set_pointer(¶ms[1], G_OBJECT(main_window));
2479 g_value_init(¶ms[2], G_TYPE_UINT);
2480 g_value_set_uint(¶ms[2], shortcut->
key);
2482 g_value_init(¶ms[3], G_TYPE_UINT);
2483 g_value_set_uint(¶ms[3], shortcut->
mods);
2485 GValue ret = G_VALUE_INIT;
2486 g_value_init (&ret, G_TYPE_BOOLEAN);
2491 for(
int k = 0;
k < 4;
k++) g_value_unset(¶ms[
k]);
2492 g_value_unset(&ret);
2496 g_closure_invoke(active_closure, &ret, 4, params, NULL);
2497 const gboolean handled = g_value_get_boolean(&ret);
2499 for(
int k = 0;
k < 4;
k++) g_value_unset(¶ms[
k]);
2500 g_value_unset(&ret);
2527 for(GList *item = g_list_last(shortcut->
closure); item; item = g_list_previous(item))
2531 if(GTK_IS_WIDGET(candidate->
base->data))
2533 GtkWidget *candidate_widget = GTK_WIDGET(candidate->
base->data);
2535 || gtk_widget_is_ancestor(candidate_widget, GTK_WIDGET(
state->main_window));
2536 if(in_main_window &&
IS_NULL_PTR(payload_in_main_window))
2537 payload_in_main_window = candidate;
2538 if(in_main_window && gtk_widget_get_visible(candidate_widget) && gtk_widget_get_mapped(candidate_widget))
2540 payload = candidate;
2546 if(
IS_NULL_PTR(payload)) payload = payload_in_main_window;
2551 target_widget = GTK_WIDGET(payload->
base->data);
2553 target_widget = shortcut->
widget;
2571 g_object_add_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2573 g_object_add_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2580 "[accel_search] dispatch closure target='%s' description='%s' handled=%d\n",
2581 path, desc, handled);
2585 const gboolean activated = gtk_widget_activate(shortcut_widget);
2587 "[accel_search] dispatch widget target='%s' description='%s' activated=%d widget=%s\n",
2588 path, desc, activated,
2589 !
IS_NULL_PTR(shortcut_widget) ? gtk_widget_get_name(shortcut_widget) :
"<destroyed>");
2594 "[accel_search] dispatch failed: no callable target for '%s' description='%s'\n",
2603 focused_widget = gtk_window_get_focus(
state->main_window);
2606 gboolean target_focused_gtk =
FALSE;
2607 gboolean target_focused_scroll =
FALSE;
2612 target_focused_gtk = focused_widget == target_widget
2613 || gtk_widget_is_ancestor(focused_widget, target_widget)
2614 || gtk_widget_is_ancestor(target_widget, focused_widget);
2618 target_focused_scroll = scroll_focused_widget == target_widget
2619 || gtk_widget_is_ancestor(scroll_focused_widget, target_widget)
2620 || gtk_widget_is_ancestor(target_widget, scroll_focused_widget);
2623 const gboolean target_focused = target_focused_gtk || target_focused_scroll;
2626 "[accel_search] focus check (pre-idle) target='%s' target_widget=%s(%p) gtk_focus=%s(%p)"
2627 " scroll_focus=%s(%p) target_focused_gtk=%d target_focused_scroll=%d target_focused=%d\n",
2629 !
IS_NULL_PTR(target_widget) ? gtk_widget_get_name(target_widget) :
"<null>",
2630 (
void *)target_widget,
2631 !
IS_NULL_PTR(focused_widget) ? gtk_widget_get_name(focused_widget) :
"<null>",
2632 (
void *)focused_widget,
2633 !
IS_NULL_PTR(scroll_focused_widget) ? gtk_widget_get_name(scroll_focused_widget) :
"<null>",
2634 (
void *)scroll_focused_widget,
2635 target_focused_gtk, target_focused_scroll, target_focused);
2640 && !g_strcmp0(G_OBJECT_TYPE_NAME(target_widget),
"DtBauhausWidget"))
2643 retry->
path = g_strdup(path);
2648 "[accel_search] dispatch retry scheduled target='%s' retry=%u\n",
2657 g_object_remove_weak_pointer(G_OBJECT(target_widget), (gpointer *)&target_widget);
2659 g_object_remove_weak_pointer(G_OBJECT(shortcut_widget), (gpointer *)&shortcut_widget);
2667 g_free(
state->path);
2669 return G_SOURCE_REMOVE;
2675 gtk_tree_model_get(
model, iter, 2, &rank, -1);
2680 GtkTreeIter *iter, gpointer user_data)
2684 gchar *key_query = g_strdup(
key);
2687 g_strstrip(key_query);
2688 if(key_query[0] ==
'\0')
2694 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2701 gchar *query = NULL;
2702 gtk_tree_model_get(
model, iter, 0, &query, -1);
2709 gchar *key_ci = g_utf8_casefold(key_query, -1);
2710 gchar *query_ci = g_utf8_casefold(query, -1);
2711 const gboolean match = !
IS_NULL_PTR(g_strrstr(query_ci, key_ci));
2723 const gchar *query_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2724 gchar *query = g_strdup(!
IS_NULL_PTR(query_text) ? query_text :
"");
2729 "[accel_search] validate query='%s' shortcut='%s' description='%s'\n",
2735 state->selected = shortcut;
2736 state->response = GTK_RESPONSE_ACCEPT;
2737 gtk_widget_destroy(
window);
2745 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
2747 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2751 state->selected = NULL;
2756 GtkTreeViewColumn *column, gpointer user_data)
2760 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
return FALSE;
2763 gtk_tree_model_get(
state->filter_model, &iter, 1, &shortcut, -1);
2769 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
state->tree_view));
2771 if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
2773 if(!gtk_tree_model_get_iter_first(
state->filter_model, &iter))
return TRUE;
2777 if(!gtk_tree_model_iter_next(
state->filter_model, &iter))
return TRUE;
2781 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2783 if(!gtk_tree_path_prev(path))
2785 gtk_tree_path_free(path);
2789 if(!gtk_tree_model_get_iter(
state->filter_model, &iter, path))
2791 gtk_tree_path_free(path);
2794 gtk_tree_path_free(path);
2797 GtkTreePath *path = gtk_tree_model_get_path(
state->filter_model, &iter);
2799 gtk_tree_selection_select_iter(selection, &iter);
2800 gtk_tree_view_set_cursor(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE);
2801 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(
state->tree_view), path, NULL,
FALSE, 0.f, 0.f);
2802 gtk_tree_path_free(path);
2803 gtk_tree_model_get(
state->filter_model, &iter, 1, &
state->selected, -1);
2813 return G_SOURCE_REMOVE;
2815 gchar *with_space = g_strconcat(
state->pending_space_query,
" ", NULL);
2816 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), with_space);
2817 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2820 state->pending_space_query = NULL;
2821 return G_SOURCE_REMOVE;
2825 GdkEventKey *event, gpointer user_data)
2830 if(
key == GDK_KEY_Escape)
2832 state->response = GTK_RESPONSE_CANCEL;
2833 gtk_widget_destroy(
state->window);
2837 if(
key == GDK_KEY_Down)
2839 if(
key == GDK_KEY_Up)
2841 if(
key == GDK_KEY_Return)
2844 gchar *command = NULL;
2847 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2848 if(!
IS_NULL_PTR(entry_text) && entry_text[0] !=
'\0')
2853 : g_strdup(entry_text);
2858 g_strstrip(command);
2859 if(command[0] !=
'\0')
2862 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(
state->store), &iter))
2867 gchar *candidate_path = NULL;
2868 gtk_tree_model_get(GTK_TREE_MODEL(
state->store), &iter, 1, &candidate, 0, &candidate_path, -1);
2869 gchar *candidate_display_path = NULL;
2873 && (!g_strcmp0(command, candidate->
path)
2874 || !g_strcmp0(command, candidate_path)
2876 && !g_strcmp0(command, candidate_display_path))))
2878 shortcut = candidate;
2880 dt_free(candidate_display_path);
2884 dt_free(candidate_display_path);
2885 }
while(gtk_tree_model_iter_next(GTK_TREE_MODEL(
state->store), &iter));
2897 if(
key == GDK_KEY_space)
2903 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2906 gchar *query_only = NULL;
2907 gint sel_start = 0, sel_end = 0;
2908 const gboolean
has_selection = gtk_editable_get_selection_bounds(GTK_EDITABLE(
state->search_entry),
2909 &sel_start, &sel_end);
2911 : gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
2912 const gint text_chars = g_utf8_strlen(entry_text, -1);
2913 if(cursor_chars >= 0 && cursor_chars <= text_chars)
2914 query_only = g_utf8_substring(entry_text, 0, cursor_chars);
2916 query_only = g_strdup(entry_text);
2920 *((gchar *)sep) =
'\0';
2923 state->pending_space_query = query_only;
2926 if(
state->pending_space_idle_id != 0) g_source_remove(
state->pending_space_idle_id);
2931 gunichar key_char = gdk_keyval_to_unicode(event->keyval);
2932 const gboolean is_alnum = key_char != 0 && g_unichar_isalnum(key_char);
2939 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2945 const gint
position = gtk_editable_get_position(GTK_EDITABLE(
state->search_entry));
2946 const gsize query_len = sep - entry_text;
2947 gchar *query_only = g_strndup(entry_text, query_len);
2949 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query_only);
2950 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry),
MIN(
position, (gint)query_len));
2962 if(event->button != 1)
return FALSE;
2965 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(
state->search_entry));
2971 const gsize query_len = sep - entry_text;
2972 gchar *query = g_strndup(entry_text, query_len);
2974 gtk_entry_set_text(GTK_ENTRY(
state->search_entry), query);
2975 gtk_editable_set_position(GTK_EDITABLE(
state->search_entry), -1);
2988 GtkAllocation allocation = { 0 };
2989 gtk_widget_get_allocation(widget, &allocation);
2991 gboolean click_inside =
FALSE;
2992 GdkWindow *win = gtk_widget_get_window(widget);
2995 gint wx = 0, wy = 0;
2996 gdk_window_get_origin(win, &wx, &wy);
2997 const gdouble x0 = (gdouble)wx;
2998 const gdouble y0 = (gdouble)wy;
2999 const gdouble x1 = x0 + (gdouble)allocation.width;
3000 const gdouble y1 = y0 + (gdouble)allocation.height;
3001 click_inside = (
event->x_root >= x0 &&
event->x_root < x1
3002 &&
event->y_root >= y0 &&
event->y_root < y1);
3006 click_inside = (
event->x >= 0.0 &&
event->x < allocation.width
3007 &&
event->y >= 0.0 &&
event->y < allocation.height);
3010 if(click_inside)
return FALSE;
3012 state->response = GTK_RESPONSE_CANCEL;
3013 gtk_widget_destroy(widget);
3020 if(
state->pending_space_idle_id != 0)
3022 g_source_remove(
state->pending_space_idle_id);
3023 state->pending_space_idle_id = 0;
3028 state->pending_space_query = NULL;
3030 gtk_grab_remove(widget);
3031 if(
state->window == widget)
state->window = NULL;
3038 gtk_window_set_title(GTK_WINDOW(
window), _(
"Ansel - Search accelerators"));
3040#ifdef GDK_WINDOWING_QUARTZ
3044 const int dialog_width = 800;
3045 const int dialog_height = 0;
3047 gtk_window_set_decorated(GTK_WINDOW(
window),
FALSE);
3049 gtk_window_set_transient_for(GTK_WINDOW(
window), main_window);
3050 gtk_window_set_attached_to(GTK_WINDOW(
window), GTK_WIDGET(main_window));
3051 gtk_window_set_resizable(GTK_WINDOW(
window),
FALSE);
3052 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(
window),
TRUE);
3053 gtk_window_set_skip_pager_hint(GTK_WINDOW(
window),
TRUE);
3054 gtk_window_set_accept_focus(GTK_WINDOW(
window),
TRUE);
3055 gtk_window_set_focus_on_map(GTK_WINDOW(
window),
TRUE);
3056 gtk_window_set_default_size(GTK_WINDOW(
window), dialog_width, dialog_height);
3057 gtk_widget_set_name(
window,
"shortcut-search-dialog");
3058 gtk_widget_add_events(
window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3061 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);
3064 GMainLoop *loop = g_main_loop_new(NULL,
FALSE);
3067 .filter_model = NULL,
3068 .recent_entries = NULL,
3069 .main_window = main_window,
3070 .search_entry = NULL,
3074 .response = GTK_RESPONSE_CANCEL,
3076 .preferred_command = NULL,
3077 .suppress_inline_once =
FALSE,
3078 .pending_space_query = NULL,
3079 .pending_space_idle_id = 0
3083 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(
store), 2,
3085 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), 2, GTK_SORT_ASCENDING);
3090 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3091 gtk_container_add(GTK_CONTAINER(
window), box);
3092 GtkWidget *search_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
3093 gtk_box_pack_start(GTK_BOX(box), search_row,
TRUE,
TRUE, 0);
3096 GtkTreeModel *filter_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(
store), NULL);
3097 state.filter_model = filter_model;
3098 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter_model),
3101 GtkEntryCompletion *completion = gtk_entry_completion_new();
3102 GtkListStore *recent_entries = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
3104 state.recent_entries = recent_entries;
3106 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(recent_entries), 4,
3108 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(recent_entries), 4, GTK_SORT_ASCENDING);
3109 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(recent_entries));
3110 gtk_entry_completion_set_text_column(completion, 3);
3111 gtk_entry_completion_set_inline_completion(completion,
TRUE);
3112 gtk_entry_completion_set_inline_selection(completion,
TRUE);
3113 gtk_entry_completion_set_popup_completion(completion,
FALSE);
3115 gtk_entry_set_completion(GTK_ENTRY(
search_entry), completion);
3116 g_object_unref(completion);
3119 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(
scrolled),
3120 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3121 gtk_widget_set_size_request(
scrolled, dialog_width, 320);
3125 GtkWidget *tree_view = gtk_tree_view_new_with_model(filter_model);
3126 state.tree_view = tree_view;
3127 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view),
FALSE);
3128 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree_view),
FALSE);
3129 gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(tree_view),
FALSE);
3130 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(tree_view),
TRUE);
3131 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tree_view), 0);
3132 gtk_widget_set_hexpand(tree_view,
TRUE);
3133 gtk_widget_set_vexpand(tree_view,
TRUE);
3134 gtk_container_add(GTK_CONTAINER(
scrolled), tree_view);
3136 GtkCellRenderer *txt = gtk_cell_renderer_text_new();
3137 g_object_set(txt,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE,
"max-width-chars", 70, NULL);
3138 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, txt,
"text", 0, NULL);
3139 gtk_tree_view_column_set_expand(column,
FALSE);
3140 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3141 gtk_tree_view_column_set_min_width(column, 360);
3142 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3144 GtkCellRenderer *accel = gtk_cell_renderer_accel_new();
3145 g_object_set(accel,
"editable",
FALSE,
"accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER, NULL);
3146 column = gtk_tree_view_column_new_with_attributes(NULL, accel,
"accel-key", 5,
"accel-mods", 6, NULL);
3147 gtk_tree_view_column_set_min_width(column, 140);
3148 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3150 GtkCellRenderer *
description = gtk_cell_renderer_text_new();
3151 g_object_set(
description,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set",
TRUE, NULL);
3152 column = gtk_tree_view_column_new_with_attributes(NULL,
description,
"text", 3, NULL);
3153 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3154 gtk_tree_view_column_set_min_width(column, 280);
3155 gtk_tree_view_column_set_expand(column,
TRUE);
3156 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
3158 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
3159 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
3177 GtkAllocation main_alloc = { 0 };
3178 gtk_widget_get_allocation(GTK_WIDGET(main_window), &main_alloc);
3179 gint main_x = 0, main_y = 0;
3180 GdkWindow *main_gdk_window = gtk_widget_get_window(GTK_WIDGET(main_window));
3182 gdk_window_get_origin(main_gdk_window, &main_x, &main_y);
3184 gtk_window_get_position(main_window, &main_x, &main_y);
3186 gint top_panel_height = 0;
3190 if(!
IS_NULL_PTR(top_panel) && gtk_widget_get_visible(top_panel))
3191 top_panel_height = gtk_widget_get_allocated_height(top_panel);
3194 const gint window_x = main_x +
MAX((main_alloc.width - dialog_width) / 2, 0);
3195 const gint window_y = main_y +
MAX(top_panel_height, 0);
3196 gtk_window_move(GTK_WINDOW(
window), window_x, window_y);
3198 gtk_widget_realize(
window);
3199 gtk_widget_show_all(
window);
3201 gdk_window_focus(gtk_widget_get_window(
window), GDK_CURRENT_TIME);
3205 g_main_loop_run(loop);
3206 g_main_loop_unref(loop);
3210 dispatch->
path = g_strdup(
state.selected->path);
3212 ?
state.selected->accels
3220 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)
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
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_capitalize_label(gchar *text)
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]