GnuCash  5.6-150-g038405b370+
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 
40 void
41 gnc_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 
88 static void
89 gnc_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 
117 static gboolean
118 gnc_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 
146 static gboolean
147 gnc_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 
164 void
165 gnc_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 
185 void
186 gnc_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 
235 gboolean
236 gnc_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 
257 void
258 gnc_style_context_get_background_color (GtkStyleContext *context,
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 
283 void
284 gnc_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 
301 static gpointer
302 find_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 
329 GtkWidget *
330 gnc_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 
341 void
342 gnc_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 
363 static void
364 accel_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 
390 static void
391 add_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 
430 void
431 gnc_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 
444 static gpointer
445 find_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 
499 GtkWidget *
500 gnc_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 
527 GtkWidget *
528 gnc_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 
547 static void
548 menu_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 
570 GList *
571 gnc_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 
589 static void
590 find_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 
612 GtkWidget *
613 gnc_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 
629 static void
630 extract_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 
705 static void
706 items_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 
744 gboolean
745 gnc_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 
777 GtkWidget *
778 gnc_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 
818 gboolean
819 gnc_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 
921 typedef struct
922 {
923  GMenuModel *model;
924  gint index;
925 } to_remove;
926 
927 static void
928 item_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 
948 static void
949 remove_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 
971 static void
972 remove_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 
986 void
987 gnc_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 
1001 static void
1002 statusbar_push (GtkWidget *statusbar, const gchar *text)
1003 {
1004  gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0,
1005  text ? text : " ");
1006 }
1007 
1008 static void
1009 statusbar_pop (GtkWidget *statusbar)
1010 {
1011  gtk_statusbar_pop (GTK_STATUSBAR(statusbar), 0);
1012 }
1013 
1014 static void
1015 menu_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 
1039 static void
1040 menu_item_deselect_cb (GtkWidget *menu_item, GtkWidget *statusbar)
1041 {
1042  statusbar_pop (statusbar);
1043 }
1044 
1052 void
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 
1074 static gboolean
1075 tool_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 
1085 static gboolean
1086 tool_item_leave_event (GtkWidget *button, GdkEvent *event,
1087  gpointer user_data)
1088 {
1089  statusbar_pop (user_data);
1090  return FALSE;
1091 }
1092 
1100 void
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 }
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.
Definition: gnc-gtk-utils.c:41
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...
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...
gtk helper routines.
gboolean gnc_menubar_model_find_item(GMenuModel *menu_model, GncMenuModelSearch *gsm)
Find a GtkMenu item from the action name.
gboolean gnc_is_dark_theme(GdkRGBA *fg_color)
Return whether the current gtk theme is a dark one.
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_style_context_get_background_color(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color)
Wrapper to get the background color of a widget for a given state.
GtkWidget * gnc_get_dialog_widget_from_id(GtkDialog *dialog, const gchar *id)
Find the Widget defined by &#39;id&#39; in the dialog.
GList * gnc_menu_get_items(GtkWidget *menu)
Return a list of GtkMenuItems.
GtkWidget * gnc_menubar_model_find_menu_item(GMenuModel *menu_model, GtkWidget *menu, const gchar *action_name)
Find a GtkMenu item from the action name.
GtkWidget * gnc_find_toolbar_item(GtkWidget *toolbar, const gchar *action_name)
Search the toolbar for the tool item based on the action name.
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.
void gnc_add_accelerator_keys_for_menu(GtkWidget *menu, GMenuModel *model, GtkAccelGroup *accel_group)
Add accelerator keys for menu item widgets.
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.
void gnc_disable_all_actions_in_group(GSimpleActionGroup *action_group)
Disable all the actions in a simple action group.
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_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...