GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-gtk-utils.c
1/********************************************************************\
2 * gnc-gtk-utils.c -- utility functions based on glib functions *
3 * Copyright (C) 2006 David Hampton <hampton@employees.org> *
4 * *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License as *
7 * published by the Free Software Foundation; either version 2 of *
8 * the License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License*
16 * along with this program; if not, contact: *
17 * *
18 * Free Software Foundation Voice: +1-617-542-5942 *
19 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 * Boston, MA 02110-1301, USA gnu@gnu.org *
21 * *
22\********************************************************************/
23
24#include <config.h>
25
26#include "gnc-gtk-utils.h"
27
28#define LAST_INDEX "last_index"
29#define CHANGED_ID "changed_id"
30
31
40void
41gnc_cbwe_set_by_string(GtkComboBox *cbwe,
42 const gchar *text)
43{
44 GtkTreeModel *model;
45 GtkTreeIter iter;
46 gchar *tree_string;
47 gint column, index, id;
48 gboolean match;
49
50 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbwe));
51 if (!gtk_tree_model_get_iter_first(model, &iter))
52 {
53 /* empty tree */
54 gtk_combo_box_set_active(GTK_COMBO_BOX(cbwe), -1);
55 return;
56 }
57
58 column = gtk_combo_box_get_entry_text_column(cbwe);
59 do
60 {
61 gtk_tree_model_get(model, &iter, column, &tree_string, -1);
62 match = g_utf8_collate(text, tree_string) == 0;
63 g_free(tree_string);
64 if (!match)
65 continue;
66
67 /* Found a matching string */
68 id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cbwe), CHANGED_ID));
69 g_signal_handler_block(cbwe, id);
70 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cbwe), &iter);
71 g_signal_handler_unblock(cbwe, id);
72
73 index = gtk_combo_box_get_active(GTK_COMBO_BOX(cbwe));
74 g_object_set_data(G_OBJECT(cbwe), LAST_INDEX, GINT_TO_POINTER(index));
75 return;
76 }
77 while (gtk_tree_model_iter_next(model, &iter));
78}
79
80
88static void
89gnc_cbwe_changed_cb (GtkComboBox *widget,
90 GtkComboBox *cbwe)
91{
92 gint index;
93
94 index = gtk_combo_box_get_active(widget);
95 if (index == -1)
96 return;
97 g_object_set_data(G_OBJECT(cbwe), LAST_INDEX, GINT_TO_POINTER(index));
98}
99
100
117static gboolean
118gnc_cbwe_match_selected_cb (GtkEntryCompletion *completion,
119 GtkTreeModel *comp_model,
120 GtkTreeIter *comp_iter,
121 GtkComboBox *cbwe)
122{
123 gint column;
124 gchar *text;
125
126 column = gtk_combo_box_get_entry_text_column(cbwe);
127 gtk_tree_model_get(comp_model, comp_iter, column, &text, -1);
128 gnc_cbwe_set_by_string(cbwe, text);
129 g_free(text);
130 return FALSE;
131}
132
133
146static gboolean
147gnc_cbwe_focus_out_cb (GtkEntry *entry,
148 GdkEventFocus *event,
149 GtkComboBox *cbwe)
150{
151 const gchar *text;
152 gint index;
153
154 /* Make a final attempt to match the current text. */
155 text = gtk_entry_get_text(entry);
156 gnc_cbwe_set_by_string(cbwe, text);
157
158 /* Get the last known index (which may have just been set). */
159 index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cbwe), LAST_INDEX));
160 gtk_combo_box_set_active(GTK_COMBO_BOX(cbwe), index);
161 return FALSE;
162}
163
164void
165gnc_cbwe_add_completion (GtkComboBox *cbwe)
166{
167 GtkEntry *entry;
168 GtkEntryCompletion *completion;
169 GtkTreeModel *model;
170
171 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbwe)));
172 completion = gtk_entry_get_completion(entry);
173 if (completion)
174 return;
175
176 /* No completion yet? Set one up. */
177 completion = gtk_entry_completion_new();
178 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbwe));
179 gtk_entry_completion_set_model(completion, model);
180 gtk_entry_completion_set_text_column(completion, 0);
181 gtk_entry_set_completion(entry, completion);
182 g_object_unref(completion);
183}
184
185void
186gnc_cbwe_require_list_item (GtkComboBox *cbwe)
187{
188 GtkEntry *entry;
189 GtkEntryCompletion *completion;
190 GtkTreeModel *model;
191 GtkTreeIter iter;
192 gint index, id;
193
194 /* Ensure completion is set up. */
195 gnc_cbwe_add_completion(cbwe);
196
197 /* If an item in the combo box isn't already selected, then force
198 * select the first item. Take care, the combo box may not have been
199 * filled yet. */
200 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbwe)));
201 completion = gtk_entry_get_completion(entry);
202 index = gtk_combo_box_get_active(GTK_COMBO_BOX(cbwe));
203 if (index == -1)
204 {
205 model = gtk_entry_completion_get_model(completion);
206 if (gtk_tree_model_get_iter_first(model, &iter))
207 {
208 gtk_combo_box_set_active(GTK_COMBO_BOX(cbwe), 0);
209 index = 0;
210 }
211 }
212 g_object_set_data(G_OBJECT(cbwe), LAST_INDEX, GINT_TO_POINTER(index));
213
214 /* Now the signals to make sure the user can't leave the
215 widget without a valid match. */
216 id = g_signal_connect(cbwe, "changed",
217 G_CALLBACK(gnc_cbwe_changed_cb), cbwe);
218 g_signal_connect(completion, "match_selected",
219 G_CALLBACK(gnc_cbwe_match_selected_cb), cbwe);
220 g_signal_connect(entry, "focus-out-event",
221 G_CALLBACK(gnc_cbwe_focus_out_cb), cbwe);
222
223 g_object_set_data(G_OBJECT(cbwe), CHANGED_ID, GINT_TO_POINTER(id));
224}
225
235gboolean
236gnc_is_dark_theme (GdkRGBA *fg_color)
237{
238 gboolean is_dark = FALSE;
239
240 // Counting the perceptive luminance - human eye favors green color...
241 double lightness = (0.299 * fg_color->red + 0.587 * fg_color->green + 0.114 * fg_color->blue);
242
243 if (lightness > 0.5)
244 is_dark = TRUE;
245
246 return is_dark;
247}
248
257void
259 GtkStateFlags state,
260 GdkRGBA *color)
261{
262 GdkRGBA *c;
263
264 g_return_if_fail (color != NULL);
265 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
266
267 gtk_style_context_get (context,
268 state,
269 GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &c,
270 NULL);
271 *color = *c;
272 gdk_rgba_free (c);
273}
274
283void
284gnc_style_context_get_border_color (GtkStyleContext *context,
285 GtkStateFlags state,
286 GdkRGBA *color)
287{
288 GdkRGBA *c;
289
290 g_return_if_fail (color != NULL);
291 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
292
293 gtk_style_context_get (context,
294 state,
295 GTK_STYLE_PROPERTY_BORDER_COLOR, &c,
296 NULL);
297 *color = *c;
298 gdk_rgba_free (c);
299}
300
301static gpointer
302find_widget_func (GtkWidget *widget, const gchar *id)
303{
304 const gchar *name = gtk_buildable_get_name (GTK_BUILDABLE(widget));
305 GtkWidget *ret = NULL;
306
307 if (g_strcmp0 (name, id) == 0)
308 return widget;
309
310 if (GTK_IS_CONTAINER(widget))
311 {
312 GList *container_list = gtk_container_get_children (GTK_CONTAINER(widget));
313 for (GList *n = container_list; !ret && n; n = n->next)
314 ret = find_widget_func (n->data, id);
315 g_list_free (container_list);
316 }
317
318 return ret;
319}
320
329GtkWidget *
330gnc_get_dialog_widget_from_id (GtkDialog *dialog, const gchar *id)
331{
332 GtkWidget *content_area = gtk_dialog_get_content_area (dialog);
333 return find_widget_func (content_area, id);
334}
335
336
341void
342gnc_disable_all_actions_in_group (GSimpleActionGroup *action_group)
343{
344 gchar **actions;
345 gint num_actions;
346
347 g_return_if_fail (action_group != NULL);
348
349 actions = g_action_group_list_actions (G_ACTION_GROUP(action_group));
350 num_actions = g_strv_length (actions);
351
352 // Disable the actions
353 for (gint i = 0; i < num_actions; i++)
354 {
355 GAction *action = g_action_map_lookup_action (G_ACTION_MAP(action_group),
356 actions[i]);
357 g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
358 }
359 g_strfreev (actions);
360}
361
362
363static void
364accel_map_foreach_func (gpointer user_data, const gchar* accel_path, guint accel_key,
365 GdkModifierType accel_mods, gboolean changed)
366{
367 GMenuModel *menu_model = user_data;
368 gchar **accel_path_parts = NULL;
369 guint accel_size = 0;
370 gchar *target = NULL;
371 gchar *accel_name_tmp = gtk_accelerator_name (accel_key, accel_mods);
372 gchar *accel_name = g_strescape (accel_name_tmp, NULL);
373
374 accel_path_parts = g_strsplit (accel_path, "/", -1);
375 accel_size = g_strv_length (accel_path_parts);
376
377 if (accel_size == 4)
378 target = g_strdup (accel_path_parts[3]);
379
380 if (accel_size >=3)
381 gnc_menubar_model_update_item (menu_model, accel_path_parts[2],
382 target, NULL, accel_name, NULL);
383
384 g_strfreev (accel_path_parts);
385 g_free (target);
386 g_free (accel_name_tmp);
387 g_free (accel_name);
388}
389
390static void
391add_accel_for_menu_lookup (GtkWidget *widget, gpointer user_data)
392{
393 if (GTK_IS_MENU_ITEM(widget))
394 {
395 GtkMenuItem* menuItem = GTK_MENU_ITEM(widget);
396 GtkWidget* subMenu = gtk_menu_item_get_submenu (menuItem);
397 GtkWidget *accel_label = gtk_bin_get_child (GTK_BIN(widget));
398
399 if (accel_label)
400 {
401 gboolean added = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(menuItem),
402 "accel-added"));
403 guint key;
404 GdkModifierType mods;
405
406 gtk_accel_label_get_accel (GTK_ACCEL_LABEL(accel_label), &key, &mods);
407
408 if (key > 0 && !added)
409 {
410 g_object_set_data (G_OBJECT(menuItem), "accel-added", GINT_TO_POINTER(1));
411 gtk_widget_add_accelerator (GTK_WIDGET(widget), "activate",
412 GTK_ACCEL_GROUP(user_data),
413 key, mods, GTK_ACCEL_VISIBLE);
414 }
415 }
416 if (GTK_IS_CONTAINER(subMenu))
417 gtk_container_foreach (GTK_CONTAINER(subMenu),
418 add_accel_for_menu_lookup, user_data);
419 }
420}
421
430void
431gnc_add_accelerator_keys_for_menu (GtkWidget *menu, GMenuModel *model, GtkAccelGroup *accel_group)
432{
433 g_return_if_fail (GTK_IS_WIDGET(menu));
434 g_return_if_fail (model != NULL);
435 g_return_if_fail (accel_group != NULL);
436
437 // this updates the menu accelerators based on accelerator-map
438 gtk_accel_map_foreach (model, (GtkAccelMapForeach)accel_map_foreach_func);
439
440 gtk_container_foreach (GTK_CONTAINER(menu), add_accel_for_menu_lookup, accel_group);
441}
442
443
444static gpointer
445find_menu_item_func (GtkWidget *widget, const gchar *action_name, const gchar *action_label)
446{
447 GtkWidget *ret = NULL;
448
449 if (GTK_IS_MENU_ITEM(widget))
450 {
451 GtkWidget* subMenu;
452
453 if (action_name)
454 {
455 if (GTK_IS_ACTIONABLE(widget))
456 {
457 const gchar *a_name = gtk_actionable_get_action_name (GTK_ACTIONABLE(widget));
458
459 if (g_strcmp0 (a_name, action_name) == 0)
460 return widget;
461 }
462 }
463
464 if (action_label)
465 {
466 GtkWidget *accel_label = gtk_bin_get_child (GTK_BIN(widget));
467
468 if (accel_label)
469 {
470 // use gtk_label_get_text to get text with no underlines
471 const gchar *al_name = gtk_label_get_label (GTK_LABEL(accel_label));
472
473 if (g_strcmp0 (al_name, action_label) == 0)
474 return widget;
475 }
476 }
477
478 subMenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM(widget));
479
480 if (GTK_IS_CONTAINER(subMenu))
481 {
482 GList *container_list = gtk_container_get_children (GTK_CONTAINER(subMenu));
483 for (GList *n = container_list; !ret && n; n = n->next)
484 ret = find_menu_item_func (n->data, action_name, action_label);
485 g_list_free (container_list);
486 }
487 }
488 return ret;
489}
490
499GtkWidget *
500gnc_find_menu_item_by_action_name (GtkWidget *menu, const gchar *action_name)
501{
502 GtkWidget *ret = NULL;
503 const gchar *action_label = NULL;
504
505 g_return_val_if_fail (GTK_IS_WIDGET(menu), NULL);
506 g_return_val_if_fail (action_name != NULL, NULL);
507
508 if (GTK_IS_CONTAINER(menu))
509 {
510 GList *container_list = gtk_container_get_children (GTK_CONTAINER(menu));
511 for (GList *n = container_list; !ret && n; n = n->next)
512 ret = find_menu_item_func (n->data, action_name, action_label);
513 g_list_free (container_list);
514 }
515 return ret;
516}
517
518
527GtkWidget *
528gnc_find_menu_item_by_action_label (GtkWidget *menu, const gchar *action_label)
529{
530 GtkWidget *ret = NULL;
531 const gchar *action_name = NULL;
532
533 g_return_val_if_fail (GTK_IS_WIDGET(menu), NULL);
534 g_return_val_if_fail (action_label != NULL, NULL);
535
536 if (GTK_IS_CONTAINER(menu))
537 {
538 GList *container_list = gtk_container_get_children (GTK_CONTAINER(menu));
539 for (GList *n = container_list; !ret && n; n = n->next)
540 ret = find_menu_item_func (n->data, action_name, action_label);
541 g_list_free (container_list);
542 }
543 return ret;
544}
545
546
547static void
548menu_item_list (GtkWidget *widget, gpointer user_data)
549{
550 GList **list = user_data;
551
552 if (GTK_IS_MENU_ITEM(widget))
553 {
554 GtkWidget* subMenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM(widget));
555
556 *list = g_list_prepend (*list, widget);
557
558 if (GTK_IS_CONTAINER(subMenu))
559 gtk_container_foreach (GTK_CONTAINER(subMenu),
560 menu_item_list, user_data);
561 }
562}
563
570GList *
571gnc_menu_get_items (GtkWidget *menu)
572{
573 GList *list = NULL;
574
575 g_return_val_if_fail (GTK_IS_WIDGET(menu), NULL);
576
577 gtk_container_foreach (GTK_CONTAINER(menu), menu_item_list, &list);
578
579 return list;
580}
581
582
584{
585 GtkWidget *found_tool_item;
586 const gchar *action_name;
587};
588
589static void
590find_tool_action (GtkWidget *widget, gpointer user_data)
591{
592 struct find_tool_item_struct *ftis = user_data;
593
594 if (GTK_IS_ACTIONABLE(widget))
595 {
596 // this returns the full action name
597 const gchar *item_action_name = gtk_actionable_get_action_name (GTK_ACTIONABLE(widget));
598
599 if (g_str_has_suffix (item_action_name, ftis->action_name))
600 ftis->found_tool_item = GTK_WIDGET(widget);
601 }
602}
603
612GtkWidget *
613gnc_find_toolbar_item (GtkWidget *toolbar, const gchar *action_name)
614{
615 struct find_tool_item_struct ftis;
616
617 g_return_val_if_fail (GTK_IS_TOOLBAR(toolbar), NULL);
618 g_return_val_if_fail (action_name != NULL, NULL);
619
620 ftis.action_name = action_name;
621 ftis.found_tool_item = NULL;
622
623 gtk_container_foreach (GTK_CONTAINER(toolbar), find_tool_action, &ftis);
624
625 return ftis.found_tool_item;
626}
627
628
629static void
630extract_items_from_model (GMenuModel *model,
631 gint item,
632 gpointer user_data)
633{
634 GMenuAttributeIter *iter;
635 const gchar *key;
636 GVariant *value;
637 GncMenuModelSearch *gsm = user_data;
638 const gchar *action = NULL;
639 const gchar *label = NULL;
640 const gchar *tooltip = NULL;
641 const gchar *target_char = NULL;
642 gint target_int = -1;
643
644 iter = g_menu_model_iterate_item_attributes (model, item);
645 while (g_menu_attribute_iter_get_next (iter, &key, &value))
646 {
647 if (g_str_equal (key, GNC_MENU_ATTRIBUTE_TOOLTIP) &&
648 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
649 tooltip = g_variant_get_string (value, NULL);
650 else if (g_str_equal (key, G_MENU_ATTRIBUTE_LABEL) &&
651 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
652 label = g_variant_get_string (value, NULL);
653 else if (g_str_equal (key, G_MENU_ATTRIBUTE_ACTION) &&
654 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
655 action = g_variant_get_string (value, NULL);
656 else if (g_str_equal (key, G_MENU_ATTRIBUTE_TARGET) &&
657 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
658 target_char = g_variant_get_string (value, NULL);
659 else if (g_str_equal (key, G_MENU_ATTRIBUTE_TARGET) &&
660 g_variant_is_of_type (value, G_VARIANT_TYPE_INT32))
661 target_int = g_variant_get_int32 (value);
662 g_variant_unref (value);
663 }
664
665 if (gsm->search_action_target)
666 {
667 gboolean target_test = FALSE;
668
669 if (target_int != -1 && target_int == atoi (gsm->search_action_target))
670 target_test = TRUE;
671
672 if (target_char && g_strcmp0 (target_char, gsm->search_action_target) == 0)
673 target_test = TRUE;
674
675 if (!target_test)
676 {
677 g_object_unref (iter);
678 return;
679 }
680 }
681
682 if (action && gsm->search_action_name)
683 {
684 if (g_str_has_suffix (action, gsm->search_action_name))
685 {
686 gsm->model = model;
687 gsm->index = item;
688 gsm->tooltip = tooltip;
689 gsm->search_action_label = label;
690 }
691 }
692 if (label && gsm->search_action_label)
693 {
694 if (g_strcmp0 (label, gsm->search_action_label) == 0)
695 {
696 gsm->model = model;
697 gsm->index = item;
698 gsm->tooltip = tooltip;
699 gsm->search_action_name = action;
700 }
701 }
702 g_object_unref (iter);
703}
704
705static void
706items_from_model (GMenuModel *model,
707 gpointer user_data)
708{
709 GncMenuModelSearch *gsm = user_data;
710
711 for (gint i = 0; i < g_menu_model_get_n_items (model); i++)
712 {
713 GMenuLinkIter *iter;
714 GMenuModel *sub_model;
715
716 if (gsm->model)
717 return;
718
719 extract_items_from_model (model, i, user_data);
720
721 iter = g_menu_model_iterate_item_links (model, i);
722 while (g_menu_link_iter_get_next (iter, NULL, &sub_model))
723 {
724 items_from_model (sub_model, user_data);
725 g_object_unref (sub_model);
726 }
727 g_object_unref (iter);
728 }
729}
730
744gboolean
745gnc_menubar_model_find_item (GMenuModel *menu_model, GncMenuModelSearch *gsm)
746{
747
748 g_return_val_if_fail (menu_model != NULL, FALSE);
749 g_return_val_if_fail (gsm != NULL, FALSE);
750
751 gsm->model = NULL;
752
753 items_from_model (menu_model, gsm);
754
755 if (gsm->model)
756 return TRUE;
757
758 return FALSE;
759}
760
761
777GtkWidget *
778gnc_menubar_model_find_menu_item (GMenuModel *menu_model, GtkWidget *menu, const gchar *action_name)
779{
780 GncMenuModelSearch *gsm;
781 GtkWidget *menu_item = NULL;
782
783 g_return_val_if_fail (menu_model != NULL, NULL);
784 g_return_val_if_fail (menu != NULL, NULL);
785 g_return_val_if_fail (action_name != NULL, NULL);
786
787 gsm = g_new0 (GncMenuModelSearch, 1);
788
789 gsm->search_action_label = NULL;
790 gsm->search_action_name = action_name;
791 gsm->search_action_target = NULL;
792
793 if (gnc_menubar_model_find_item (menu_model, gsm))
794 menu_item = gnc_find_menu_item_by_action_label (menu, gsm->search_action_label);
795
796 g_free (gsm);
797 return menu_item;
798}
799
800
818gboolean
819gnc_menubar_model_update_item (GMenuModel *menu_model, const gchar *action_name,
820 const gchar *target, const gchar *label,
821 const gchar *accel_name, const gchar *tooltip)
822{
823 GncMenuModelSearch *gsm;
824 gboolean found = FALSE;
825
826 g_return_val_if_fail (menu_model != NULL, FALSE);
827 g_return_val_if_fail (action_name != NULL, FALSE);
828
829 gsm = g_new0 (GncMenuModelSearch, 1);
830
831 gsm->search_action_label = NULL;
832 gsm->search_action_name = action_name;
833 gsm->search_action_target = target;
834
835 if (gnc_menubar_model_find_item (menu_model, gsm))
836 {
837 GMenuAttributeIter *iter;
838 const gchar *key;
839 GVariant *value;
840 GVariant *old_target = NULL;
841 const gchar *old_action = NULL;
842 const gchar *old_temp = NULL;
843 const gchar *old_accel = NULL;
844 const gchar *old_tooltip = NULL;
845
846 iter = g_menu_model_iterate_item_attributes (gsm->model, gsm->index);
847 while (g_menu_attribute_iter_get_next (iter, &key, &value))
848 {
849 if (g_str_equal (key, GNC_MENU_ATTRIBUTE_TEMPORARY) &&
850 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
851 old_temp = g_variant_get_string (value, NULL);
852 else if (g_str_equal (key, G_MENU_ATTRIBUTE_ACTION) &&
853 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
854 old_action = g_variant_get_string (value, NULL);
855 else if (g_str_equal (key, GNC_MENU_ATTRIBUTE_ACCELERATOR) &&
856 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
857 old_accel = g_variant_get_string (value, NULL);
858 else if (g_str_equal (key, GNC_MENU_ATTRIBUTE_TOOLTIP) &&
859 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
860 old_tooltip = g_variant_get_string (value, NULL);
861 else if (g_str_equal (key, G_MENU_ATTRIBUTE_TARGET))
862 old_target = g_variant_ref (value);
863
864 g_variant_unref (value);
865 }
866 g_object_unref (iter);
867
868 if (!label && !gsm->search_action_label)
869 {
870 if (old_target)
871 g_variant_unref (old_target);
872
873 g_free (gsm);
874 return found;
875 }
876
877 if ((accel_name && g_strcmp0 (old_accel, accel_name) != 0) ||
878 (tooltip && g_strcmp0 (old_tooltip, tooltip) != 0) ||
879 (label && g_strcmp0 (gsm->search_action_label, label) != 0))
880 {
881 GMenuItem *item = NULL;
882
883 if (label)
884 item = g_menu_item_new (label, old_action);
885 else
886 item = g_menu_item_new (gsm->search_action_label, old_action);
887
888 if (tooltip)
889 g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_TOOLTIP, "s", tooltip);
890 else
891 {
892 if (old_tooltip)
893 g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_TOOLTIP, "s", old_tooltip);
894 }
895 if (accel_name)
896 g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_ACCELERATOR, "s", accel_name);
897 else
898 {
899 if (old_accel)
900 g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_ACCELERATOR, "s", old_accel);
901 }
902 if (old_temp)
903 g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_TEMPORARY, "s", old_temp);
904
905 if (old_target)
906 g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_TARGET, old_target);
907
908 g_menu_remove (G_MENU(gsm->model), gsm->index);
909 g_menu_insert_item (G_MENU(gsm->model), gsm->index, item);
910 g_object_unref (item);
911 found = TRUE;
912 }
913 if (old_target)
914 g_variant_unref (old_target);
915 }
916 g_free (gsm);
917 return found;
918}
919
920
921typedef struct
922{
923 GMenuModel *model;
924 gint index;
925} to_remove;
926
927static void
928item_to_remove_from_model (GMenuModel *model,
929 gint item,
930 GList **remove_list,
931 const gchar *attrib)
932{
933 GVariant *value = g_menu_model_get_item_attribute_value (model, item,
934 attrib, NULL);
935
936 if (value && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
937 {
938 to_remove *tr = g_new0 (to_remove, 1);
939 tr->model = model;
940 tr->index = item;
941
942 // to keep the order append
943 *remove_list = g_list_append (*remove_list, tr);
944 g_variant_unref (value);
945 }
946}
947
948static void
949remove_items_from_model (GMenuModel *model,
950 GList **remove_list,
951 const gchar *attrib)
952{
953 // Note: item high to low
954 for (gint i = g_menu_model_get_n_items (model) -1; i >= 0; i--)
955 {
956 GMenuLinkIter *iter;
957 GMenuModel *sub_model;
958
959 item_to_remove_from_model (model, i, remove_list, attrib);
960
961 iter = g_menu_model_iterate_item_links (model, i);
962 while (g_menu_link_iter_get_next (iter, NULL, &sub_model))
963 {
964 remove_items_from_model (sub_model, remove_list, attrib);
965 g_object_unref (sub_model);
966 }
967 g_object_unref (iter);
968 }
969}
970
971static void
972remove_items (gpointer data, gpointer user_data)
973{
974 to_remove *tr = (to_remove*)data;
975 g_menu_remove (G_MENU(tr->model), tr->index);
976 g_free (tr);
977}
978
986void
987gnc_menubar_model_remove_items_with_attrib (GMenuModel *menu_model, const gchar *attrib)
988{
989 GList *remove_list = NULL;
990
991 g_return_if_fail (menu_model != NULL);
992 g_return_if_fail (attrib != NULL);
993
994 remove_items_from_model (menu_model, &remove_list, attrib);
995
996 g_list_foreach (remove_list, (GFunc)remove_items, NULL);
997 g_list_free (remove_list);
998}
999
1000
1001static void
1002statusbar_push (GtkWidget *statusbar, const gchar *text)
1003{
1004 gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0,
1005 text ? text : " ");
1006}
1007
1008static void
1009statusbar_pop (GtkWidget *statusbar)
1010{
1011 gtk_statusbar_pop (GTK_STATUSBAR(statusbar), 0);
1012}
1013
1014static void
1015menu_item_select_cb (GtkWidget *menu_item, GtkWidget *statusbar)
1016{
1017 GtkWidget *accel_label = gtk_bin_get_child (GTK_BIN(menu_item));
1018 GMenuModel *menubar_model = g_object_get_data (G_OBJECT(statusbar), "menu-model");
1019
1020 if (!menubar_model)
1021 return;
1022
1023 if (accel_label)
1024 {
1025 GncMenuModelSearch *gsm = g_new0 (GncMenuModelSearch, 1);
1026
1027 gsm->search_action_label = gtk_label_get_label (GTK_LABEL(accel_label));
1028 gsm->search_action_name = NULL;
1029
1030 if (gnc_menubar_model_find_item (menubar_model, gsm))
1031 {
1032 if (gsm->model)
1033 statusbar_push (statusbar, gsm->tooltip);
1034 }
1035 g_free (gsm);
1036 }
1037}
1038
1039static void
1040menu_item_deselect_cb (GtkWidget *menu_item, GtkWidget *statusbar)
1041{
1042 statusbar_pop (statusbar);
1043}
1044
1052void
1054 GtkWidget *statusbar)
1055{
1056 g_return_if_fail (menu_item != NULL);
1057 g_return_if_fail (statusbar != NULL);
1058
1059 if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "added-callbacks")))
1060 return;
1061
1062 g_signal_connect (menu_item, "select",
1063 G_CALLBACK(menu_item_select_cb),
1064 statusbar);
1065 g_signal_connect (menu_item, "deselect",
1066 G_CALLBACK(menu_item_deselect_cb),
1067 statusbar);
1068 g_object_set (G_OBJECT(menu_item), "has-tooltip", FALSE, NULL);
1069
1070 g_object_set_data (G_OBJECT(menu_item), "added-callbacks", GINT_TO_POINTER(1));
1071}
1072
1073
1074static gboolean
1075tool_item_enter_event (GtkWidget *button, GdkEvent *event,
1076 gpointer user_data)
1077{
1078 GtkWidget *tool_item = gtk_widget_get_parent (button);
1079 gchar *tooltip = gtk_widget_get_tooltip_text (tool_item);
1080 statusbar_push (user_data, tooltip);
1081 g_free (tooltip);
1082 return FALSE;
1083}
1084
1085static gboolean
1086tool_item_leave_event (GtkWidget *button, GdkEvent *event,
1087 gpointer user_data)
1088{
1089 statusbar_pop (user_data);
1090 return FALSE;
1091}
1092
1100void
1102 GtkWidget *statusbar)
1103{
1104 GtkWidget *child;
1105
1106 g_return_if_fail (tool_item != NULL);
1107 g_return_if_fail (statusbar != NULL);
1108
1109 child = gtk_bin_get_child (GTK_BIN(tool_item));
1110
1111 gtk_widget_add_events (GTK_WIDGET(child),
1112 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
1113 | GDK_FOCUS_CHANGE_MASK);
1114
1115 g_signal_connect (child, "enter-notify-event",
1116 G_CALLBACK (tool_item_enter_event),
1117 statusbar);
1118
1119 g_signal_connect (child, "leave-notify-event",
1120 G_CALLBACK (tool_item_leave_event),
1121 statusbar);
1122
1123 g_object_set (G_OBJECT(tool_item), "has-tooltip", FALSE, NULL);
1124}
gtk helper routines.
void gnc_menu_item_setup_tooltip_to_statusbar_callback(GtkWidget *menu_item, GtkWidget *statusbar)
Setup the callbacks for menu bar items so the tooltip can be displayed in the status bar.
void gnc_disable_all_actions_in_group(GSimpleActionGroup *action_group)
Disable all the actions in a simple action group.
void gnc_menubar_model_remove_items_with_attrib(GMenuModel *menu_model, const gchar *attrib)
Remove GMenuModel entries based on having an attribute value equal to attrib, it does not matter what...
GtkWidget * gnc_find_toolbar_item(GtkWidget *toolbar, const gchar *action_name)
Search the toolbar for the tool item based on the action name.
void gnc_cbwe_set_by_string(GtkComboBox *cbwe, const gchar *text)
Find an entry in the GtkComboBox by its text value, and set the widget to that value.
GtkWidget * gnc_find_menu_item_by_action_label(GtkWidget *menu, const gchar *action_label)
Search the menu for the menu item based on the action label.
GtkWidget * gnc_get_dialog_widget_from_id(GtkDialog *dialog, const gchar *id)
Find the Widget defined by 'id' in the dialog.
gboolean gnc_menubar_model_find_item(GMenuModel *menu_model, GncMenuModelSearch *gsm)
Find a GtkMenu item from the action name.
GList * gnc_menu_get_items(GtkWidget *menu)
Return a list of GtkMenuItems.
GtkWidget * gnc_find_menu_item_by_action_name(GtkWidget *menu, const gchar *action_name)
Search the menu for the menu item based on action name.
gboolean gnc_menubar_model_update_item(GMenuModel *menu_model, const gchar *action_name, const gchar *target, const gchar *label, const gchar *accel_name, const gchar *tooltip)
Update the GMenuModel item based on the action name by copying existing item, removing it and inserti...
void gnc_style_context_get_background_color(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color)
Wrapper to get the background color of a widget for a given state.
gboolean gnc_is_dark_theme(GdkRGBA *fg_color)
Return whether the current gtk theme is a dark one.
void gnc_tool_item_setup_tooltip_to_statusbar_callback(GtkWidget *tool_item, GtkWidget *statusbar)
Setup the callbacks for tool bar items so the tooltip can be displayed in the status bar.
GtkWidget * gnc_menubar_model_find_menu_item(GMenuModel *menu_model, GtkWidget *menu, const gchar *action_name)
Find a GtkMenu item from the action name.
void gnc_style_context_get_border_color(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color)
Wrapper to get the border color of a widget for a given state.
void gnc_add_accelerator_keys_for_menu(GtkWidget *menu, GMenuModel *model, GtkAccelGroup *accel_group)
Add accelerator keys for menu item widgets.