1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta-ui.c
4  * Copyright (C) Naba Kumar  <naba@gnome.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 /**
22  * SECTION:anjuta-ui
23  * @short_description: User Interface manager
24  * @see_also: #GtkAction, #GtkActionEntry, #GtkToggleAction,
25  *   #GtkToggleActionEntry, #GtkRadioAction, #GtkRadioActionEntry,
26  *   #GtkActionGroup, #GtkUIManager
27  * @stability: Unstable
28  * @include: libanjuta/
29  *
30  * #AnjutaUI subclasses #GtkUIManager, so you should really read #GtkUIManager
31  * documentation first to know about Actions, UI merging and UI XML file
32  * format. This documentation will cover only the relevent	APIs.
33  *
34  * #AnjutaUI has its own methods for adding action groups, which is differnt
35  * from #GtkUIManager methods. All #AnjutaPlugin based classes should use
36  * these methods instead of #GtkUIManager methods. The reason is, in addition
37  * to adding the actions and groups to the UI manager, it also resgisters
38  * them for UI customization and accellerators editing. It also keeps
39  * record of all actions.
40  *
41  * An interesting side effect of this is that these
42  * actions could be conveniently accessed or activated with
43  * anjuta_ui_get_action() or anjuta_ui_activate_action_by_path(), without
44  * the need of original action group object. This makes it is possible for
45  * activating actions remotely from other plugins.
46  *
47  * anjuta_ui_get_accel_editor() will return a widget containing the
48  * UI customization and accellerators editor. All actions and action groups
49  * are organized into a tree view, which should be added to a visible
50  * container (e.g. a #GtkDialog based object) and displayed to users.
51  *
52  * <note>
53  * 	<para>
54  * 		Any actions additions/removals using #GtkUIManager are not
55  * 		registred with #AnjutaUI and hence their accellerators
56  * 		cannot be edited. Nor will they be listed in UI manager
57  * 		dialog. Hence, use #AnjutaUI methods whenever possible.
58  * 	</para>
59  * </note>
60  */
61 
62 #ifdef HAVE_CONFIG_H
63 #  include <config.h>
64 #endif
65 
66 #include <stdio.h>
67 #include <string.h>
68 #include <glib/gi18n.h>
69 #include <gtk/gtk.h>
70 
71 #include "resources.h"
72 #include "anjuta-ui.h"
73 #include "anjuta-utils.h"
74 #include "anjuta-debug.h"
75 
76 struct _AnjutaUIPrivate {
77 	GtkIconFactory *icon_factory;
78 	GtkTreeModel *name_model;
79 	GtkTreeModel *accel_model;
80 	GHashTable *customizable_actions_hash;
81 	GHashTable *uncustomizable_actions_hash;
82 };
83 
84 enum {
85 	COLUMN_PIXBUF,
86 	COLUMN_ACTION_LABEL,
87 	COLUMN_VISIBLE,
88 	COLUMN_SHOW_VISIBLE,
89 	COLUMN_SENSITIVE,
90 	COLUMN_ACTION,
91 	COLUMN_GROUP,
92 	N_COLUMNS
93 };
94 
95 static gchar*
get_action_label(GtkAction * action)96 get_action_label (GtkAction *action)
97 {
98 	gchar *action_label = NULL;
99 
100 	g_object_get (G_OBJECT (action), "label", &action_label, NULL);
101 	if (action_label && strlen (action_label))
102 	{
103 		gchar *s, *d;
104 		s = d = action_label;
105 		while (*s)
106 		{
107 			/* FIXME: May break with multibyte chars */
108 			if (*s == '_')
109 				s++;
110 			*d = *s; d++; s++;
111 		}
112 		*d = '\0';
113 	}
114 	else
115 		action_label = g_strdup (gtk_action_get_name (action));
116 	return action_label;
117 }
118 
119 /* Find action in tree */
120 static gboolean
find_action(GtkTreeModel * model,GtkTreeIter * iter,GtkAction * action)121 find_action (GtkTreeModel *model, GtkTreeIter *iter, GtkAction *action)
122 {
123 	GtkTreeIter group;
124 	gboolean valid;
125 
126 	for (valid = gtk_tree_model_get_iter_first (model, &group);
127 		valid;
128 		valid = gtk_tree_model_iter_next (model, &group))
129 	{
130 		for (valid = gtk_tree_model_iter_children (model, iter, &group);
131 			valid;
132 			valid = gtk_tree_model_iter_next (model, iter))
133 		{
134 			GtkAction *child_action;
135 
136 			gtk_tree_model_get (model, iter, COLUMN_ACTION, &child_action, -1);
137 			g_object_unref (child_action);
138 			if (action == child_action) return TRUE;
139 		}
140 	}
141 
142 	return FALSE;
143 }
144 
145 /* Find group in tree starting after sibling or from beginning if sibling is
146  * NULL */
147 static gboolean
find_group_after(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * sibling,const gchar * group_label)148 find_group_after (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *sibling, const gchar *group_label)
149 {
150 	gboolean valid;
151 
152 	if (sibling == NULL)
153 	{
154 		valid = gtk_tree_model_get_iter_first (model, iter);
155 	}
156 	else
157 	{
158 		*iter = *sibling;
159 		valid = gtk_tree_model_iter_next (model, iter);
160 	}
161 	while (valid)
162 	{
163 		gchar *label;
164 		gint comp;
165 
166 		gtk_tree_model_get (model, iter, COLUMN_ACTION_LABEL, &label, -1);
167 		comp = strcmp (label, group_label);
168 		g_free (label);
169 
170 		if (comp == 0) return TRUE;
171 
172 		valid = gtk_tree_model_iter_next (model, iter);
173 	}
174 
175 	return FALSE;
176 }
177 
178 /* Find position for new label */
179 static GtkTreeIter *
find_sorted_name(GtkTreeModel * model,GtkTreeIter * iter,const gchar * label)180 find_sorted_name (GtkTreeModel *model, GtkTreeIter *iter, const gchar *label)
181 {
182 	gboolean valid;
183 
184 	do
185 	{
186 		gchar *iter_label;
187 		gint comp;
188 
189 		gtk_tree_model_get (model, iter, COLUMN_ACTION_LABEL, &iter_label, -1);
190 
191 		comp = g_utf8_collate (label, iter_label);
192 		g_free (iter_label);
193 
194 		if (comp <= 0) return iter;
195 
196 		valid = gtk_tree_model_iter_next (model, iter);
197 	} while (valid);
198 
199 	return NULL;
200 }
201 
202 /* Find position for new accel */
203 static gboolean
find_sorted_accel(GtkTreeModel * model,GtkTreeIter * iter,GtkAction * action)204 find_sorted_accel (GtkTreeModel *model, GtkTreeIter *iter, GtkAction *action)
205 {
206 	GtkTreeIter group;
207 	gboolean valid;
208 	const gchar *accel_path;
209 	GtkAccelKey key;
210 
211 	accel_path = gtk_action_get_accel_path (action);
212 	if ((accel_path == NULL) ||
213 	!gtk_accel_map_lookup_entry (accel_path, &key) ||
214 	((key.accel_key == 0) && (key.accel_mods == 0)))
215 	{
216 		/* No accelerator */
217 		return FALSE;
218 	}
219 
220 	for (valid = gtk_tree_model_get_iter_first (model, &group);
221 		valid;
222 		valid = gtk_tree_model_iter_next (model, &group))
223 	{
224 		for (valid = gtk_tree_model_iter_children (model, iter, &group);
225 			valid;
226 			valid = gtk_tree_model_iter_next (model, iter))
227 		{
228 			GtkAction *child_action;
229 			GtkAccelKey child_key;
230 
231 			gtk_tree_model_get (model, iter, COLUMN_ACTION, &child_action, -1);
232 			accel_path = gtk_action_get_accel_path (child_action);
233 			g_object_unref (child_action);
234 			if ((accel_path == NULL) ||
235 			!gtk_accel_map_lookup_entry (accel_path, &child_key) ||
236 			((child_key.accel_key == 0) && (child_key.accel_mods == 0)))
237 			{
238 				/* No more accelerator and as accelerators are sorted, there is
239 				 * no need to go further */
240 				return TRUE;
241 			}
242 
243 			if ((child_key.accel_key > key.accel_key) ||
244 				((child_key.accel_key == key.accel_key) && (child_key.accel_mods >= key.accel_mods)))
245 			{
246 				/* Find next accelerator */
247 				return TRUE;
248 			}
249 		}
250 	}
251 
252 	return FALSE;
253 }
254 
255 static void
insert_sorted_by_accel(GtkTreeModel * model,GtkTreeIter * iter,const gchar * group_label,GtkAction * action)256 insert_sorted_by_accel (GtkTreeModel *model, GtkTreeIter *iter, const gchar *group_label, GtkAction *action)
257 {
258 	GtkTreeIter next;
259 	GtkTreeIter parent;
260 
261 	if (find_sorted_accel (model, &next, action))
262 	{
263 		gchar *label;
264 
265 		/* Try to set next action in an already existing parent */
266 		gtk_tree_model_iter_parent (model, &parent, &next);
267 		gtk_tree_model_get (model, &parent, COLUMN_ACTION_LABEL, &label, -1);
268 		if (strcmp (label, group_label) == 0)
269 		{
270 			/* Already the right group, just insert action */
271 			gtk_tree_store_insert_before (GTK_TREE_STORE (model), iter, &parent, &next);
272 		}
273 		else
274 		{
275 			/* Try to put in the previous group */
276 			GtkTreePath *path;
277 			gboolean prev;
278 
279 			path = gtk_tree_model_get_path (model, &next);
280 			prev = gtk_tree_path_prev (path);
281 			gtk_tree_path_free (path);
282 
283 			if (!prev)
284 			{
285 				path = gtk_tree_model_get_path (model, &parent);
286 				if (gtk_tree_path_prev (path))
287 				{
288 					g_free (label);
289 					gtk_tree_model_get_iter (model, &parent, path);
290 					gtk_tree_model_get (model, &parent, COLUMN_ACTION_LABEL, &label, -1);
291 					if (strcmp (label, group_label) != 0)
292 					{
293 						/* Create new parent */
294 						next = parent;
295 						gtk_tree_store_insert_after (GTK_TREE_STORE (model), &parent, NULL, &next);
296 					}
297 				}
298 				else
299 				{
300 					next = parent;
301 					gtk_tree_store_insert_before (GTK_TREE_STORE (model), &parent, NULL, &next);
302 				}
303 				gtk_tree_path_free (path);
304 				gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
305 									COLUMN_ACTION_LABEL, group_label,
306 									COLUMN_SHOW_VISIBLE, FALSE,
307 									-1);
308 
309 				/* Add action at the end */
310 				gtk_tree_store_append (GTK_TREE_STORE (model), iter, &parent);
311 			}
312 			else
313 			{
314 				/* Split parent and add new parent in the middle */
315 				GtkTreeIter split;
316 				gboolean valid;
317 
318 				gtk_tree_store_insert_after (GTK_TREE_STORE (model), &split, NULL, &parent);
319 				gtk_tree_store_set (GTK_TREE_STORE (model), &split,
320 									COLUMN_ACTION_LABEL, label,
321 									COLUMN_SHOW_VISIBLE, FALSE,
322 									-1);
323 
324 				do
325 				{
326 					GtkTreeIter child;
327 					GdkPixbuf *pixbuf;
328 					gchar *action_label;
329 					gboolean visible;
330 					gboolean sensitive;
331 					GtkAction *action;
332 					gpointer action_group;
333 
334 					gtk_tree_model_get (model, &next,
335 										COLUMN_PIXBUF, &pixbuf,
336 										COLUMN_ACTION_LABEL, &action_label,
337 										COLUMN_VISIBLE, &visible,
338 										COLUMN_SENSITIVE, &sensitive,
339 										COLUMN_ACTION, &action,
340 										COLUMN_GROUP, &action_group,
341 										-1);
342 					gtk_tree_store_append (GTK_TREE_STORE (model), &child, &split);
343 					gtk_tree_store_set (GTK_TREE_STORE (model), &child,
344 										COLUMN_PIXBUF, pixbuf,
345 										COLUMN_ACTION_LABEL, action_label,
346 										COLUMN_VISIBLE, visible,
347 										COLUMN_SHOW_VISIBLE, TRUE,
348 										COLUMN_SENSITIVE, sensitive,
349 										COLUMN_ACTION, action,
350 										COLUMN_GROUP, action_group,
351 										-1);
352 					if (pixbuf) g_object_unref (pixbuf);
353 					if (action) g_object_unref (action);
354 					g_free (action_label);
355 
356 					valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &next);
357 				} while (valid);
358 
359 				/* Add new parent */
360 				gtk_tree_store_insert_before (GTK_TREE_STORE (model), &parent, NULL, &split);
361 				gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
362 									COLUMN_ACTION_LABEL, group_label,
363 									COLUMN_SHOW_VISIBLE, FALSE,
364 									-1);
365 
366 				gtk_tree_store_append (GTK_TREE_STORE (model), iter, &parent);
367 			}
368 		}
369 		g_free (label);
370 	}
371 	else
372 	{
373 		if (find_group_after (model, &parent, NULL, group_label))
374 		{
375 			GtkTreeIter child;
376 			GtkAction *child_action;
377 			const gchar *accel_path;
378 			GtkAccelKey key;
379 
380 			/* Find last group */
381 			while (find_group_after (model, &next, &parent, group_label))
382 			{
383 				parent = next;
384 			}
385 
386 			gtk_tree_model_iter_children (model, &child, &parent);
387 			gtk_tree_model_get (model, &child, COLUMN_ACTION, &child_action, -1);
388 
389 			accel_path = gtk_action_get_accel_path (child_action);
390 			g_object_unref (child_action);
391 			if ((accel_path != NULL) &&
392 				gtk_accel_map_lookup_entry (accel_path, &key) &&
393 				((key.accel_key != 0) || (key.accel_mods != 0)))
394 			{
395 				/* Create new group */
396 				gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL);
397 				gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
398 									COLUMN_ACTION_LABEL, group_label,
399 									COLUMN_SHOW_VISIBLE, FALSE,
400 									-1);
401 
402 			}
403 		}
404 		else
405 		{
406 			/* Create new group */
407 			gtk_tree_store_append (GTK_TREE_STORE (model), &parent, NULL);
408 			gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
409 								COLUMN_ACTION_LABEL, group_label,
410 								COLUMN_SHOW_VISIBLE, FALSE,
411 								-1);
412 		}
413 
414 		/* Append action */
415 		gtk_tree_store_append (GTK_TREE_STORE (model), iter, &parent);
416 	}
417 }
418 
419 static void
insert_sorted_by_name(GtkTreeModel * model,GtkTreeIter * iter,const gchar * group_label,GtkAction * action)420 insert_sorted_by_name (GtkTreeModel *model, GtkTreeIter *iter, const gchar *group_label, GtkAction *action)
421 {
422 	GtkTreeIter parent;
423 	GtkTreeIter child;
424 	GtkTreeIter *sibling;
425 	gchar *label;
426 
427 	if (!find_group_after (model, &parent, NULL, group_label))
428 	{
429 		/* Insert group for label */
430 		if (gtk_tree_model_get_iter_first (model, &child))
431 		{
432 			sibling = find_sorted_name (model, &child, group_label);
433 		}
434 		else
435 		{
436 			sibling = NULL;
437 		}
438 		gtk_tree_store_insert_before (GTK_TREE_STORE (model), &parent, NULL, sibling);
439 		gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
440 							COLUMN_ACTION_LABEL, group_label,
441 							COLUMN_SHOW_VISIBLE, FALSE,
442 							-1);
443 	}
444 
445 	if (gtk_tree_model_iter_children (model, &child, &parent))
446 	{
447 		label = get_action_label (action);
448 		sibling = find_sorted_name (model, &child, label);
449 		g_free (label);
450 	}
451 	else
452 	{
453 		sibling = NULL;
454 	}
455 
456 	gtk_tree_store_insert_before (GTK_TREE_STORE (model), iter, &parent, sibling);
457 }
458 
459 static void
fill_action_data(GtkTreeModel * model,GtkTreeIter * iter,GtkAction * action,GtkActionGroup * group)460 fill_action_data (GtkTreeModel *model, GtkTreeIter *iter, GtkAction *action, GtkActionGroup *group)
461 {
462 	gchar *action_label;
463 	gchar *icon;
464 	GdkPixbuf *pixbuf = NULL;
465 	GtkWidget *dummy = NULL;
466 
467 	action_label = get_action_label (action);
468 	g_object_get (G_OBJECT (action), "stock-id", &icon, NULL);
469 	if (icon != NULL)
470 	{
471 		GtkWidget *dummy = gtk_label_new ("Dummy");
472 		g_object_ref_sink(G_OBJECT(dummy));
473 		pixbuf = gtk_widget_render_icon_pixbuf (dummy, icon, GTK_ICON_SIZE_MENU);
474 	}
475 	gtk_tree_store_set (GTK_TREE_STORE (model), iter,
476 						COLUMN_PIXBUF, pixbuf,
477 						COLUMN_ACTION_LABEL, action_label,
478 						COLUMN_VISIBLE, gtk_action_get_visible (action),
479 						COLUMN_SHOW_VISIBLE, TRUE,
480 						COLUMN_SENSITIVE, gtk_action_get_sensitive (action),
481 						COLUMN_ACTION, action,
482 						COLUMN_GROUP, group,
483 						-1);
484 	if (pixbuf != NULL) g_object_unref (G_OBJECT (pixbuf));
485 	if (dummy != NULL) g_object_unref (dummy);
486 	g_free (icon);
487 	g_free (action_label);
488 }
489 
490 /* Remove all actions in the action group group */
491 static void
remove_action_in_group(GtkTreeModel * model,GtkActionGroup * group)492 remove_action_in_group (GtkTreeModel *model, GtkActionGroup *group)
493 {
494 	GtkTreeIter parent;
495 	gboolean valid;
496 
497 	valid = gtk_tree_model_get_iter_first (model, &parent);
498 	while (valid)
499 	{
500 		/* Check each action, as a parent can contains actions from different
501 		 * groups */
502 		GtkTreeIter child;
503 
504 		valid = gtk_tree_model_iter_children (model, &child, &parent);
505 		while (valid)
506 		{
507 			gpointer child_group;
508 
509 			gtk_tree_model_get (model, &child, COLUMN_GROUP, &child_group, -1);
510 
511 			if (child_group == (gpointer)group)
512 			{
513 				valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
514 			}
515 			else
516 			{
517 				valid = gtk_tree_model_iter_next (model, &child);
518 			}
519 		}
520 
521 		/* if parent is now empty remove it */
522 		if (!gtk_tree_model_iter_has_child (model, &parent))
523 		{
524 			valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);
525 		}
526 		else
527 		{
528 			valid = gtk_tree_model_iter_next (model, &parent);
529 		}
530 	}
531 }
532 
533 #if 0
534 static void
535 sensitivity_toggled (GtkCellRendererToggle *cell,
536 					 const gchar *path_str, GtkTreeView *tree_view)
537 {
538 	GtkTreeModel *model;
539 	GtkTreeModel *other_model;
540 	GtkTreePath *path;
541 	GtkTreeIter iter;
542 	GtkAction *action;
543 	gboolean sensitive;
544 
545 	model = gtk_tree_view_get_model (tree_view);
546 	path = gtk_tree_path_new_from_string (path_str);
547 	gtk_tree_model_get_iter (model, &iter, path);
548 	gtk_tree_path_free (path);
549 
550 	gtk_tree_model_get (model, &iter,
551 						COLUMN_SENSITIVE, &sensitive,
552 						COLUMN_ACTION, &action, -1);
553 	g_object_set (G_OBJECT (action), "sensitive", !sensitive, NULL);
554 	g_object_unref (action);
555 
556 	gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
557 						COLUMN_SENSITIVE, !sensitive, -1);
558 
559 	/* Update other model */
560 	other_model = g_object_get_data (G_OBJECT (tree_view), "other_model");
561 	if (find_action (other_model, &iter, action))
562 	{
563 		gtk_tree_store_set (GTK_TREE_STORE (other_model), &iter,
564 							COLUMN_SENSITIVE, !sensitive, -1);
565 	}
566 }
567 #endif
568 
569 static void
visibility_toggled(GtkCellRendererToggle * cell,const gchar * path_str,GtkTreeView * tree_view)570 visibility_toggled (GtkCellRendererToggle *cell,
571 					const gchar *path_str, GtkTreeView *tree_view)
572 {
573 	GtkTreeModel *model;
574 	GtkTreeModel *other_model;
575 	GtkTreePath *path;
576 	GtkTreeIter iter;
577 	GtkAction *action;
578 	gboolean visible;
579 
580 	model = gtk_tree_view_get_model (tree_view);
581 	path = gtk_tree_path_new_from_string (path_str);
582 	gtk_tree_model_get_iter (model, &iter, path);
583 	gtk_tree_path_free (path);
584 
585 	gtk_tree_model_get (model, &iter,
586 						COLUMN_VISIBLE, &visible,
587 						COLUMN_ACTION, &action, -1);
588 	g_object_set (G_OBJECT (action), "visible", !visible, NULL);
589 	g_object_unref (action);
590 	gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
591 						COLUMN_VISIBLE, !visible, -1);
592 
593 	/* Update other model */
594 	other_model = g_object_get_data (G_OBJECT (tree_view), "other_model");
595 	if (find_action (other_model, &iter, action))
596 	{
597 		gtk_tree_store_set (GTK_TREE_STORE (other_model), &iter,
598 							COLUMN_VISIBLE, !visible, -1);
599 	}
600 }
601 
602 static void
accel_edited_callback(GtkCellRendererAccel * cell,const char * path_string,guint keyval,GdkModifierType mask,guint hardware_keycode,GtkTreeView * tree_view)603 accel_edited_callback (GtkCellRendererAccel *cell,
604                        const char          *path_string,
605                        guint                keyval,
606                        GdkModifierType      mask,
607                        guint                hardware_keycode,
608                        GtkTreeView          *tree_view)
609 {
610 	GtkTreeModel *model;
611 	GtkTreePath *path;
612 	GtkTreeIter iter;
613 	GtkAction *action;
614 	const gchar *accel_path;
615 
616 	model = gtk_tree_view_get_model (tree_view);
617 	path = gtk_tree_path_new_from_string (path_string);
618 	gtk_tree_model_get_iter (model, &iter, path);
619 	gtk_tree_path_free (path);
620 
621 	gtk_tree_model_get (model, &iter,
622 						COLUMN_ACTION, &action, -1);
623 
624 	/* sanity check */
625 	if (action == NULL)
626 		return;
627 
628 	accel_path = gtk_action_get_accel_path (action);
629 	g_object_unref (action);
630 	if (accel_path) {
631 		gtk_accel_map_change_entry (accel_path, keyval, mask, TRUE);
632 	}
633 }
634 
635 static void
accel_cleared_callback(GtkCellRendererAccel * cell,const char * path_string,GtkTreeView * tree_view)636 accel_cleared_callback (GtkCellRendererAccel *cell,
637 						const char *path_string,
638 						GtkTreeView *tree_view)
639 {
640 	GtkTreeModel *model;
641 	GtkTreePath *path;
642 	GtkTreeIter iter;
643 	GtkAction *action;
644 	const gchar *accel_path;
645 
646 
647 	model = gtk_tree_view_get_model (tree_view);
648 	path = gtk_tree_path_new_from_string (path_string);
649 	gtk_tree_model_get_iter (model, &iter, path);
650 	gtk_tree_path_free (path);
651 
652 	gtk_tree_model_get (model, &iter,
653 						COLUMN_ACTION, &action, -1);
654 
655 	/* sanity check */
656 	if (action == NULL)
657 		return;
658 
659 	accel_path = gtk_action_get_accel_path (action);
660 	g_object_unref (action);
661 	if (accel_path) {
662 		gtk_accel_map_change_entry (accel_path, 0, 0, TRUE);
663 	}
664 }
665 
666 /* Fill the model sorted by accelerator with data from default model sorted by
667  * name */
668 static void
fill_sort_by_accel_store(GtkTreeStore * store,GtkTreeModel * model)669 fill_sort_by_accel_store (GtkTreeStore *store, GtkTreeModel *model)
670 {
671 	gboolean valid;
672 	GtkTreeIter group;
673 
674 	for (valid = gtk_tree_model_get_iter_first (model, &group);
675 		valid;
676 		valid = gtk_tree_model_iter_next (model, &group))
677 	{
678 		GtkTreeIter iter;
679 		gchar *group_label;
680 
681 		gtk_tree_model_get (model, &group,
682 							COLUMN_ACTION_LABEL, &group_label,
683 							-1);
684 
685 		for (valid = gtk_tree_model_iter_children (model, &iter, &group);
686 			valid;
687 			valid = gtk_tree_model_iter_next (model, &iter))
688 		{
689 			GtkTreeIter child;
690 			GdkPixbuf *pixbuf;
691 			gchar *action_label;
692 			gboolean visible;
693 			gboolean sensitive;
694 			GtkAction *action;
695 			gpointer action_group;
696 
697 			gtk_tree_model_get (model, &iter,
698 								COLUMN_PIXBUF, &pixbuf,
699 								COLUMN_ACTION_LABEL, &action_label,
700 								COLUMN_VISIBLE, &visible,
701 								COLUMN_SENSITIVE, &sensitive,
702 								COLUMN_ACTION, &action,
703 								COLUMN_GROUP, &action_group,
704 								-1);
705 
706 			if (action != NULL)
707 			{
708 				insert_sorted_by_accel (GTK_TREE_MODEL (store), &child, group_label, action);
709 
710 				gtk_tree_store_set (store, &child,
711 									COLUMN_PIXBUF, pixbuf,
712 									COLUMN_ACTION_LABEL, action_label,
713 									COLUMN_VISIBLE, visible,
714 									COLUMN_SHOW_VISIBLE, TRUE,
715 									COLUMN_SENSITIVE, sensitive,
716 									COLUMN_ACTION, action,
717 									COLUMN_GROUP, action_group,
718 									-1);
719 
720 
721 			}
722 			if (pixbuf != NULL) g_object_unref (pixbuf);
723 			g_free (action_label);
724 			if (action != NULL) g_object_unref (action);
725 		}
726 		g_free (group_label);
727 	}
728 }
729 
730 /* Switch to the model sorted by name if use_name is true or sorted by
731  * accelerator if use_name is false */
732 static void
change_tree_model(GtkTreeView * tree_view,gboolean use_name)733 change_tree_model (GtkTreeView *tree_view, gboolean use_name)
734 {
735 	gboolean has_name;
736 
737 	has_name = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_view), "name_model"));
738 	if (has_name != use_name)
739 	{
740 		GtkTreeModel *model;
741 		GtkTreeModel *other_model;
742 		GtkTreeIter iter;
743 		GtkTreePath *path;
744 		GList *expanded = NULL;
745 		GtkAction *selected_action = NULL;
746 		gchar *selected_group = NULL;
747 		GtkTreeSelection *selection;
748 		GList *item;
749 
750 		/* Save selection */
751 		model = gtk_tree_view_get_model (tree_view);
752 		selection = gtk_tree_view_get_selection (tree_view);
753 		if (gtk_tree_selection_get_selected (selection, NULL, &iter))
754 		{
755 			gtk_tree_model_get (model, &iter,
756 								COLUMN_ACTION_LABEL, &selected_group,
757 								COLUMN_ACTION, &selected_action,
758 								-1);
759 		}
760 
761 		/* Save expanded groups */
762 		for (path = gtk_tree_path_new_first ();
763 			gtk_tree_model_get_iter (model, &iter, path);
764 			gtk_tree_path_next (path))
765 		{
766 			if (gtk_tree_view_row_expanded (tree_view, path))
767 			{
768 				gchar *label;
769 
770 				gtk_tree_model_get (model, &iter, COLUMN_ACTION_LABEL, &label, -1);
771 				expanded = g_list_prepend (expanded, label);
772 				g_message ("expanded %s", label);
773 			}
774 		}
775 		gtk_tree_path_free (path);
776 
777 		/* Swap model */
778 		other_model = g_object_get_data (G_OBJECT (tree_view), "other_model");
779 
780 		if (!gtk_tree_model_get_iter_first (other_model, &iter) && !use_name)
781 		{
782 			/* Accelerator model is empty fill it */
783 			fill_sort_by_accel_store (GTK_TREE_STORE (other_model), model);
784 		}
785 
786 		gtk_tree_view_set_model (tree_view, other_model);
787 		g_object_set_data (G_OBJECT (tree_view), "other_model", model);
788 		g_object_set_data (G_OBJECT (tree_view), "name_model", GINT_TO_POINTER (use_name));
789 
790 		/* Expand same group */
791 		for (item = g_list_first (expanded); item != NULL; item = g_list_next (item))
792 		{
793 			gchar *label = (gchar *)item->data;
794 			gboolean valid;
795 
796 			for (valid = find_group_after (other_model, &iter, NULL, label);
797 				valid;
798 				valid = find_group_after (other_model, &iter, &iter, label))
799 			{
800 				path = gtk_tree_model_get_path (other_model, &iter);
801 				gtk_tree_view_expand_row (tree_view, path, FALSE);
802 				gtk_tree_path_free (path);
803 			}
804 			g_free (label);
805 		}
806 		g_list_free (expanded);
807 
808 		/* Restore selection */
809 		if (selected_action != NULL)
810 		{
811 			find_action (other_model, &iter, selected_action);
812 			gtk_tree_selection_select_iter (selection, &iter);
813 			g_object_unref (selected_action);
814 		}
815 		else if (selected_group != NULL)
816 		{
817 			find_group_after (other_model, &iter, NULL, selected_group);
818 			gtk_tree_selection_select_iter (selection, &iter);
819 		}
820 		/* Display selected row */
821 		if ((selected_action != NULL) || (selected_group != NULL))
822 		{
823 			GtkTreePath *path;
824 
825 			path = gtk_tree_model_get_path (other_model, &iter);
826 			gtk_tree_view_scroll_to_cell (tree_view, path, NULL, TRUE, 0.5, 0);
827 			gtk_tree_path_free (path);
828 		}
829 		g_free (selected_group);
830 	}
831 }
832 
833 static void
accel_sort_by_accel_callback(GtkTreeViewColumn * column,gpointer data)834 accel_sort_by_accel_callback (GtkTreeViewColumn *column,
835 						gpointer data)
836 {
837 	GtkTreeView *tree_view = (GtkTreeView *)data;
838 
839 	change_tree_model (tree_view, FALSE);
840 }
841 
842 static void
accel_sort_by_name_callback(GtkTreeViewColumn * column,gpointer data)843 accel_sort_by_name_callback (GtkTreeViewColumn *column,
844 						gpointer data)
845 {
846 	GtkTreeView *tree_view = (GtkTreeView *)data;
847 
848 	change_tree_model (tree_view, TRUE);
849 }
850 
851 static void
accel_set_func(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)852 accel_set_func (GtkTreeViewColumn *tree_column,
853                 GtkCellRenderer   *cell,
854                 GtkTreeModel      *model,
855                 GtkTreeIter       *iter,
856                 gpointer           data)
857 {
858 	GtkAction *action;
859 	const gchar *accel_path;
860 	GtkAccelKey key;
861 
862 	gtk_tree_model_get (model, iter,
863 						COLUMN_ACTION, &action, -1);
864 	if (action == NULL)
865 		g_object_set (G_OBJECT (cell), "visible", FALSE, NULL);
866 	else
867 	{
868 		if ((accel_path = gtk_action_get_accel_path (action)))
869 		{
870 			if (gtk_accel_map_lookup_entry (accel_path, &key))
871 			{
872 				g_object_set (G_OBJECT (cell), "visible", TRUE,
873 							  "accel-key", key.accel_key,
874 							  "accel-mods", key.accel_mods, NULL);
875 			}
876 			else
877 				g_object_set (G_OBJECT (cell), "visible", TRUE,
878 							  "accel-key", 0,
879 							  "accel-mods", 0, NULL);
880 		}
881 		g_object_unref (action);
882 	}
883 }
884 
G_DEFINE_TYPE(AnjutaUI,anjuta_ui,GTK_TYPE_UI_MANAGER)885 G_DEFINE_TYPE(AnjutaUI, anjuta_ui, GTK_TYPE_UI_MANAGER)
886 
887 static void
888 anjuta_ui_dispose (GObject *obj)
889 {
890 	AnjutaUI *ui = ANJUTA_UI (obj);
891 
892 	if (ui->priv->name_model) {
893 		/* This will also release the refs on actions.
894 		 * Clear is necessary because following unref() might not actually
895 		 * finalize the model. It basically ensures all refs on actions
896 		 * are released irrespective of whether the model is finalized
897 		 * or not.
898 		 */
899 		gtk_tree_store_clear (GTK_TREE_STORE (ui->priv->name_model));
900 
901 		g_object_unref (G_OBJECT (ui->priv->name_model));
902 		ui->priv->name_model = NULL;
903 	}
904 	if (ui->priv->accel_model) {
905 		gtk_tree_store_clear (GTK_TREE_STORE (ui->priv->accel_model));
906 		g_object_unref (G_OBJECT (ui->priv->accel_model));
907 		ui->priv->accel_model = NULL;
908 	}
909 	if (ui->priv->customizable_actions_hash)
910 	{
911 		/* This will also release the refs on all action groups */
912 		g_hash_table_destroy (ui->priv->customizable_actions_hash);
913 		ui->priv->customizable_actions_hash = NULL;
914 	}
915 	if (ui->priv->uncustomizable_actions_hash)
916 	{
917 		/* This will also release the refs on all action groups */
918 		g_hash_table_destroy (ui->priv->uncustomizable_actions_hash);
919 		ui->priv->uncustomizable_actions_hash = NULL;
920 	}
921 	if (ui->priv->icon_factory) {
922 		g_object_unref (G_OBJECT (ui->priv->icon_factory));
923 		ui->priv->icon_factory = NULL;
924 	}
925 	G_OBJECT_CLASS (anjuta_ui_parent_class)->dispose (obj);
926 }
927 
928 static void
anjuta_ui_finalize(GObject * obj)929 anjuta_ui_finalize (GObject *obj)
930 {
931 	AnjutaUI *ui = ANJUTA_UI (obj);
932 	g_free (ui->priv);
933 	G_OBJECT_CLASS (anjuta_ui_parent_class)->finalize (obj);
934 }
935 
936 static void
anjuta_ui_class_init(AnjutaUIClass * class)937 anjuta_ui_class_init (AnjutaUIClass *class)
938 {
939 	GObjectClass *object_class = G_OBJECT_CLASS (class);
940 
941 	object_class->dispose = anjuta_ui_dispose;
942 	object_class->finalize = anjuta_ui_finalize;
943 }
944 
945 static void
anjuta_ui_init(AnjutaUI * ui)946 anjuta_ui_init (AnjutaUI *ui)
947 {
948 	GtkTreeStore *store;
949 
950 	/* Initialize member data */
951 	ui->priv = g_new0 (AnjutaUIPrivate, 1);
952 	ui->priv->customizable_actions_hash =
953 		g_hash_table_new_full (g_str_hash,
954 							   g_str_equal,
955 							   (GDestroyNotify) g_free,
956 							   NULL);
957 	ui->priv->uncustomizable_actions_hash =
958 		g_hash_table_new_full (g_str_hash,
959 							   g_str_equal,
960 							   (GDestroyNotify) g_free,
961 							   NULL);
962 	/* Create Icon factory */
963 	ui->priv->icon_factory = gtk_icon_factory_new ();
964 	gtk_icon_factory_add_default (ui->priv->icon_factory);
965 
966 	/* Create Accel editor sorted by name model */
967 	store = gtk_tree_store_new (N_COLUMNS,
968 								GDK_TYPE_PIXBUF,
969 								G_TYPE_STRING,
970 								G_TYPE_BOOLEAN,
971 								G_TYPE_BOOLEAN,
972 								G_TYPE_BOOLEAN,
973 								G_TYPE_OBJECT,
974 								G_TYPE_POINTER);
975 
976 	/* unreferenced in dispose() method. */
977 	ui->priv->name_model = GTK_TREE_MODEL (store);
978 
979 	/* Create Accel editor sorted by accelerator model */
980 	store = gtk_tree_store_new (N_COLUMNS,
981 								GDK_TYPE_PIXBUF,
982 								G_TYPE_STRING,
983 								G_TYPE_BOOLEAN,
984 								G_TYPE_BOOLEAN,
985 								G_TYPE_BOOLEAN,
986 								G_TYPE_OBJECT,
987 								G_TYPE_POINTER);
988 
989 	/* unreferenced in dispose() method. */
990 	ui->priv->accel_model = GTK_TREE_MODEL (store);
991 }
992 
993 /**
994  * anjuta_ui_new:
995  *
996  * Creates a new instance of #AnjutaUI.
997  *
998  * Return value: A #AnjutaUI object
999  */
1000 AnjutaUI *
anjuta_ui_new(void)1001 anjuta_ui_new (void)
1002 {
1003 	return g_object_new (ANJUTA_TYPE_UI, NULL);
1004 }
1005 
1006 /**
1007  * anjuta_ui_add_action_group_entries:
1008  * @ui: A #AnjutaUI object.
1009  * @action_group_name: Untranslated name of the action group.
1010  * @action_group_label: Translated label of the action group.
1011  * @entries: (array length=num_entries): An array of action entries.
1012  * @num_entries: Number of elements in the action entries array.
1013  * @can_customize: If true the actions are customizable by user.
1014  * @translation_domain: The translation domain used to translated the entries.
1015  * It is usually the GETTEXT_PACKAGE macro in a project.
1016  * @user_data: User data to pass to action objects. This is the data that
1017  * will come as user_data in "activate" signal of the actions.
1018  *
1019  * #GtkAction objects are created from the #GtkActionEntry structures and
1020  * added to the UI Manager. "activate" signal of #GtkAction is connected for
1021  * all the action objects using the callback in the entry structure and the
1022  * @user_data passed here.
1023  *
1024  * This group of actions are registered with the name @action_group_name
1025  * in #AnjutaUI. A #GtkAction object from this action group can be later
1026  * retrieved by anjuta_ui_get_action() using @action_group_name and action name.
1027  * @action_group_label is used as the display name for the action group in
1028  * UI manager dialog where action shortcuts are configured.
1029  *
1030  * Return value: (transfer none): A #GtkActionGroup object holding all the
1031  * action objects.
1032  */
1033 GtkActionGroup*
anjuta_ui_add_action_group_entries(AnjutaUI * ui,const gchar * action_group_name,const gchar * action_group_label,GtkActionEntry * entries,gint num_entries,const gchar * translation_domain,gboolean can_customize,gpointer user_data)1034 anjuta_ui_add_action_group_entries (AnjutaUI *ui,
1035 									const gchar *action_group_name,
1036 									const gchar *action_group_label,
1037 									GtkActionEntry *entries,
1038 									gint num_entries,
1039 									const gchar *translation_domain,
1040 									gboolean can_customize,
1041 									gpointer user_data)
1042 {
1043 	GtkActionGroup *action_group;
1044 
1045 	g_return_val_if_fail (ANJUTA_IS_UI (ui), NULL);
1046 	g_return_val_if_fail (action_group_name != NULL, NULL);
1047 	g_return_val_if_fail (action_group_name != NULL, NULL);
1048 
1049 	action_group = gtk_action_group_new (action_group_name);
1050 
1051 	gtk_action_group_set_translation_domain (action_group, translation_domain);
1052 	gtk_action_group_add_actions (action_group, entries, num_entries,
1053 								  user_data);
1054 	anjuta_ui_add_action_group (ui, action_group_name,
1055 								action_group_label, action_group,
1056 								can_customize);
1057 	return action_group;
1058 }
1059 
1060 /**
1061  * anjuta_ui_add_toggle_action_group_entries:
1062  * @ui: A #AnjutaUI object.
1063  * @action_group_name: Untranslated name of the action group.
1064  * @action_group_label: Translated label of the action group.
1065  * @entries: (array length=num_entries): An array of action entries.
1066  * @num_entries: Number of elements in the action entries array.
1067  * @translation_domain: The translation domain used to translated the entries.
1068  * It is usually the GETTEXT_PACKAGE macro in a project.
1069  * @user_data: User data to pass to action objects. This is the data that
1070  * will come as user_data in "activate" signal of the actions.
1071  *
1072  * This is similar to anjuta_ui_add_action_group_entries(), except that
1073  * it adds #GtkToggleAction objects after creating them from the @entries.
1074  *
1075  * Return value: (transfer none): A #GtkActionGroup object holding all the
1076  * action objects.
1077  */
1078 GtkActionGroup*
anjuta_ui_add_toggle_action_group_entries(AnjutaUI * ui,const gchar * action_group_name,const gchar * action_group_label,GtkToggleActionEntry * entries,gint num_entries,const gchar * translation_domain,gboolean can_customize,gpointer user_data)1079 anjuta_ui_add_toggle_action_group_entries (AnjutaUI *ui,
1080 									const gchar *action_group_name,
1081 									const gchar *action_group_label,
1082 									GtkToggleActionEntry *entries,
1083 									gint num_entries,
1084 									const gchar *translation_domain,
1085 									gboolean can_customize,
1086 									gpointer user_data)
1087 {
1088 	GtkActionGroup *action_group;
1089 
1090 	g_return_val_if_fail (ANJUTA_IS_UI (ui), NULL);
1091 	g_return_val_if_fail (action_group_name != NULL, NULL);
1092 	g_return_val_if_fail (action_group_name != NULL, NULL);
1093 
1094 	action_group = gtk_action_group_new (action_group_name);
1095 	gtk_action_group_set_translation_domain (action_group, translation_domain);
1096 	gtk_action_group_add_toggle_actions (action_group, entries, num_entries,
1097 										 user_data);
1098 	anjuta_ui_add_action_group (ui, action_group_name,
1099 								action_group_label, action_group,
1100 								can_customize);
1101 	return action_group;
1102 }
1103 
1104 /**
1105  * anjuta_ui_add_action_group:
1106  * @ui: A #AnjutaUI object.
1107  * @action_group_name: Untranslated name of the action group.
1108  * @action_group_label: Translated label of the action group.
1109  * @action_group: (transfer full): #GtkActionGroup object to add.
1110  *
1111  * This is similar to anjuta_ui_add_action_group_entries(), except that
1112  * it adds #GtkActionGroup object @action_group directly. All actions in this
1113  * group are automatically registered in #AnjutaUI and can be retrieved
1114  * normally with anjuta_ui_get_action().
1115  */
1116 void
anjuta_ui_add_action_group(AnjutaUI * ui,const gchar * action_group_name,const gchar * action_group_label,GtkActionGroup * action_group,gboolean can_customize)1117 anjuta_ui_add_action_group (AnjutaUI *ui,
1118 							const gchar *action_group_name,
1119 							const gchar *action_group_label,
1120 							GtkActionGroup *action_group,
1121 							gboolean can_customize)
1122 {
1123 	GList *actions, *l;
1124 
1125 	g_return_if_fail (ANJUTA_IS_UI (ui));
1126 	g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1127 	g_return_if_fail (action_group_name != NULL);
1128 	g_return_if_fail (action_group_name != NULL);
1129 
1130 	gtk_ui_manager_insert_action_group (GTK_UI_MANAGER (ui), action_group, 0);
1131 
1132 	if (can_customize)
1133 	{
1134 		g_hash_table_insert (ui->priv->customizable_actions_hash,
1135 							g_strdup (action_group_name), action_group);
1136 	}
1137 	else
1138 	{
1139 		g_hash_table_insert (ui->priv->uncustomizable_actions_hash,
1140 							g_strdup (action_group_name), action_group);
1141 	}
1142 
1143 	actions = gtk_action_group_list_actions (action_group);
1144 	for (l = actions; l; l = l->next)
1145 	{
1146 		guint signal_id;
1147 		gint n_handlers;
1148 		GtkTreeIter iter;
1149 		GtkAction *action = l->data;
1150 
1151 		if (!action)
1152 			continue;
1153 
1154 		signal_id = g_signal_lookup ("activate", GTK_TYPE_ACTION);
1155 		n_handlers = g_signal_has_handler_pending (action, signal_id,
1156 												   0, TRUE);
1157 		if (n_handlers == 0)
1158 			continue; /* The action element is not user configuration */
1159 
1160 		insert_sorted_by_name (ui->priv->name_model, &iter, action_group_label, action);
1161 		fill_action_data (ui->priv->name_model, &iter, action, action_group);
1162 
1163 		if (gtk_tree_model_get_iter_first (ui->priv->accel_model, &iter))
1164 		{
1165 			/* accel model is filled only if needed */
1166 			insert_sorted_by_accel (ui->priv->accel_model, &iter, action_group_label, action);
1167 			fill_action_data (ui->priv->accel_model, &iter, action, action_group);
1168 		}
1169 	}
1170 	g_list_free(actions);
1171 }
1172 
1173 static gboolean
on_action_group_remove_hash(gpointer key,gpointer value,gpointer data)1174 on_action_group_remove_hash (gpointer key, gpointer value, gpointer data)
1175 {
1176 	if (data == value)
1177 		return TRUE;
1178 	else
1179 		return FALSE;
1180 }
1181 
1182 /**
1183  * anjuta_ui_remove_action_group:
1184  * @ui: A #AnjutaUI object
1185  * @action_group: #GtkActionGroup object to remove.
1186  *
1187  * Removes a previous added action group. All actions in this group are
1188  * also unregistered from UI manager.
1189  */
1190 void
anjuta_ui_remove_action_group(AnjutaUI * ui,GtkActionGroup * action_group)1191 anjuta_ui_remove_action_group (AnjutaUI *ui, GtkActionGroup *action_group)
1192 {
1193 	g_return_if_fail (ANJUTA_IS_UI (ui));
1194 
1195 	remove_action_in_group (ui->priv->name_model, action_group);
1196 	remove_action_in_group (ui->priv->accel_model, action_group);
1197 
1198 	gtk_ui_manager_remove_action_group (GTK_UI_MANAGER (ui), action_group);
1199 
1200 	g_hash_table_foreach_remove (ui->priv->customizable_actions_hash,
1201 								 on_action_group_remove_hash, action_group);
1202 	g_hash_table_foreach_remove (ui->priv->uncustomizable_actions_hash,
1203 								 on_action_group_remove_hash, action_group);
1204 }
1205 
1206 /**
1207  * anjuta_ui_get_action:
1208  * @ui: This #AnjutaUI object
1209  * @action_group_name: Group name.
1210  * @action_name: Action name.
1211  *
1212  * Returns the action object with the name @action_name in @action_group_name.
1213  * Note that it will be only sucessully returned if the group has been added
1214  * using methods in #AnjutaUI.
1215  *
1216  * Returns: (transfer none): A #GtkAction object
1217  */
1218 GtkAction*
anjuta_ui_get_action(AnjutaUI * ui,const gchar * action_group_name,const gchar * action_name)1219 anjuta_ui_get_action (AnjutaUI *ui, const gchar *action_group_name,
1220 					  const gchar *action_name)
1221 {
1222 	GtkActionGroup *action_group;
1223 	GtkAction *action;
1224 
1225 	g_return_val_if_fail (ANJUTA_IS_UI (ui), NULL);
1226 
1227 	action_group = g_hash_table_lookup (ui->priv->customizable_actions_hash,
1228 										action_group_name);
1229 	if (!action_group)
1230 	{
1231 		action_group = g_hash_table_lookup (ui->priv->uncustomizable_actions_hash,
1232 											action_group_name);
1233 	}
1234 	if (GTK_IS_ACTION_GROUP (action_group) == FALSE)
1235 	{
1236 		g_warning ("Unable to find action group \"%s\"", action_group_name);
1237 		return NULL;
1238 	}
1239 	action = gtk_action_group_get_action (action_group, action_name);
1240 	if (GTK_IS_ACTION (action))
1241 		return action;
1242 	g_warning ("Unable to find action \"%s\" in group \"%s\"",
1243 			  action_name, action_group_name);
1244 	return NULL;
1245 }
1246 
1247 /**
1248  * anjuta_ui_activate_action_by_path:
1249  * @ui: This #AnjutaUI object
1250  * @action_path: Path of the action in the form "GroupName/ActionName"
1251  *
1252  * Activates the action represented by @action_path. The path is in the form
1253  * "ActionGroupName/ActionName". Note that it will only work if the group has
1254  * been added using methods in #AnjutaUI.
1255  */
1256 void
anjuta_ui_activate_action_by_path(AnjutaUI * ui,const gchar * action_path)1257 anjuta_ui_activate_action_by_path (AnjutaUI *ui, const gchar *action_path)
1258 {
1259 	const gchar *action_group_name;
1260 	const gchar *action_name;
1261 	GtkAction *action;
1262 	gchar **strv;
1263 
1264 	g_return_if_fail (ANJUTA_IS_UI (ui));
1265 	g_return_if_fail (action_path != NULL);
1266 
1267 	strv = g_strsplit (action_path, "/", 2);
1268 	action_group_name = strv[0];
1269 	action_name = strv[1];
1270 
1271 	g_return_if_fail (action_group_name != NULL && action_name != NULL);
1272 
1273 	action = anjuta_ui_get_action (ui, action_group_name, action_name);
1274 	if (action)
1275 		gtk_action_activate (action);
1276 	g_strfreev (strv);
1277 }
1278 
1279 /**
1280  * anjuta_ui_activate_action_by_group:
1281  * @ui: This #AnjutaUI object
1282  * @action_group: Action group.
1283  * @action_name: Action name.
1284  *
1285  * Activates the action @action_name in the #GtkActionGroup @action_group.
1286  * "ActionGroupName/ActionName". Note that it will only work if the group has
1287  * been added using methods in #AnjutaUI.
1288  */
1289 void
anjuta_ui_activate_action_by_group(AnjutaUI * ui,GtkActionGroup * action_group,const gchar * action_name)1290 anjuta_ui_activate_action_by_group (AnjutaUI *ui, GtkActionGroup *action_group,
1291 									const gchar *action_name)
1292 {
1293 	GtkAction *action;
1294 
1295 	g_return_if_fail (ANJUTA_IS_UI (ui));
1296 	g_return_if_fail (action_group != NULL && action_name != NULL);
1297 
1298 	action = gtk_action_group_get_action (action_group, action_name);
1299 	if (GTK_IS_ACTION (action))
1300 		gtk_action_activate (action);
1301 }
1302 
1303 /**
1304  * anjuta_ui_merge:
1305  * @ui: A #AnjutaUI object.
1306  * @ui_filename: UI file to merge into UI manager.
1307  *
1308  * Merges XML UI definition in @ui_filename. UI elements defined in the xml
1309  * are merged with existing UI elements in UI manager. The format of the
1310  * file content is the standard XML UI definition tree. For more detail,
1311  * read the documentation for #GtkUIManager.
1312  *
1313  * Return value: Integer merge ID
1314  */
1315 gint
anjuta_ui_merge(AnjutaUI * ui,const gchar * ui_filename)1316 anjuta_ui_merge (AnjutaUI *ui, const gchar *ui_filename)
1317 {
1318 	gint id;
1319 	GError *err = NULL;
1320 
1321 	g_return_val_if_fail (ANJUTA_IS_UI (ui), -1);
1322 	g_return_val_if_fail (ui_filename != NULL, -1);
1323 	id = gtk_ui_manager_add_ui_from_file(GTK_UI_MANAGER (ui),
1324 										 ui_filename, &err);
1325 #ifdef DEBUG
1326 	{
1327 		gchar *basename = g_path_get_basename (ui_filename);
1328 		DEBUG_PRINT ("merged [%d] %s", id, basename);
1329 		g_free(basename);
1330 	}
1331 #endif
1332 	if (err != NULL)
1333 		g_warning ("Could not merge [%s]: %s", ui_filename, err->message);
1334 	return id;
1335 }
1336 
1337 /**
1338  * anjuta_ui_unmerge:
1339  * @ui: A #AnjutaUI object.
1340  * @id: Merge ID returned by anjuta_ui_merge().
1341  *
1342  * Unmerges UI with the ID value @id (returned by anjuta_ui_merge() when
1343  * it was merged. For more detail, read the documentation for #GtkUIManager.
1344  */
1345 void
anjuta_ui_unmerge(AnjutaUI * ui,gint id)1346 anjuta_ui_unmerge (AnjutaUI *ui, gint id)
1347 {
1348 	/* DEBUG_PRINT ("Menu unmerging %d", id); */
1349 	g_return_if_fail (ANJUTA_IS_UI (ui));
1350 	gtk_ui_manager_remove_ui(GTK_UI_MANAGER (ui), id);
1351 }
1352 
1353 /**
1354  * anjuta_ui_get_accel_group:
1355  * @ui: A #AnjutaUI object.
1356  *
1357  * Returns the #GtkAccelGroup object associated with this UI manager.
1358  *
1359  * Returns: (transfer none): A #GtkAccelGroup object.
1360  */
1361 GtkAccelGroup*
anjuta_ui_get_accel_group(AnjutaUI * ui)1362 anjuta_ui_get_accel_group (AnjutaUI *ui)
1363 {
1364 	g_return_val_if_fail (ANJUTA_IS_UI (ui), NULL);
1365 	return gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (ui));
1366 }
1367 
1368 /**
1369  * anjuta_ui_get_accel_editor:
1370  * @ui: A #AnjutaUI object.
1371  *
1372  * Creates an accel editor widget and returns it. It should be added to
1373  * container and displayed to users.
1374  *
1375  * Returns: (transfer none): a #GtkWidget containing the editor.
1376  */
1377 GtkWidget *
anjuta_ui_get_accel_editor(AnjutaUI * ui)1378 anjuta_ui_get_accel_editor (AnjutaUI *ui)
1379 {
1380 	GtkWidget *tree_view, *sw;
1381 	GtkTreeStore *store;
1382 	GtkTreeViewColumn *column;
1383 	GtkCellRenderer *renderer;
1384 
1385 	store = GTK_TREE_STORE (ui->priv->name_model);
1386 
1387 	tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1388 	g_object_set_data (G_OBJECT (tree_view), "other_model", ui->priv->accel_model);
1389 	g_object_set_data (G_OBJECT (tree_view), "name_model", GINT_TO_POINTER (TRUE));
1390 	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
1391 
1392 	/* Columns */
1393 	column = gtk_tree_view_column_new ();
1394 	gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Action"));
1395 
1396 	renderer = gtk_cell_renderer_pixbuf_new ();
1397 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
1398 	gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
1399 										COLUMN_PIXBUF);
1400 
1401 	renderer = gtk_cell_renderer_text_new ();
1402 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
1403 	gtk_tree_view_column_add_attribute (column, renderer, "text",
1404 										COLUMN_ACTION_LABEL);
1405 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
1406 	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (tree_view), column);
1407 	gtk_tree_view_column_set_clickable (column, TRUE);
1408 	g_signal_connect (G_OBJECT (column), "clicked",
1409 					  G_CALLBACK (accel_sort_by_name_callback),
1410 					  tree_view);
1411 
1412 	renderer = gtk_cell_renderer_toggle_new ();
1413 	g_signal_connect (G_OBJECT (renderer), "toggled",
1414 					  G_CALLBACK (visibility_toggled), tree_view);
1415 	column = gtk_tree_view_column_new_with_attributes (dgettext (GETTEXT_PACKAGE, "Visible"),
1416 													   renderer,
1417 													   "active",
1418 													   COLUMN_VISIBLE,
1419 													   "visible",
1420 													   COLUMN_SHOW_VISIBLE,
1421 													   NULL);
1422 	gtk_tree_view_column_set_alignment (column, 0.5f);
1423 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
1424 #if 0
1425 	renderer = gtk_cell_renderer_toggle_new ();
1426 	g_signal_connect (G_OBJECT (renderer), "toggled",
1427 					  G_CALLBACK (sensitivity_toggled), tree_view);
1428 	column = gtk_tree_view_column_new_with_attributes (_("Sensitive"),
1429 													   renderer,
1430 													   "active",
1431 													   COLUMN_SENSITIVE,
1432 													   NULL);
1433 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
1434 #endif
1435 	column = gtk_tree_view_column_new ();
1436 	gtk_tree_view_column_set_title (column, dgettext (GETTEXT_PACKAGE, "Shortcut"));
1437 	renderer = g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL,
1438 							"editable", TRUE,
1439 							NULL);
1440 	g_signal_connect (G_OBJECT (renderer), "accel-edited",
1441 					  G_CALLBACK (accel_edited_callback),
1442 					  tree_view);
1443 	g_signal_connect (G_OBJECT (renderer), "accel-cleared",
1444 					  G_CALLBACK (accel_cleared_callback),
1445 					  tree_view);
1446 	g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
1447 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
1448 	gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
1449 	gtk_tree_view_column_set_clickable (column, TRUE);
1450 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
1451 	g_signal_connect (G_OBJECT (column), "clicked",
1452 					  G_CALLBACK (accel_sort_by_accel_callback),
1453 					  tree_view);
1454 
1455 	sw = gtk_scrolled_window_new (NULL, NULL);
1456 	gtk_container_set_border_width (GTK_CONTAINER (sw), 6);
1457 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1458 									GTK_POLICY_AUTOMATIC,
1459 									GTK_POLICY_AUTOMATIC);
1460 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
1461 	                                     GTK_SHADOW_IN);
1462 	gtk_container_add (GTK_CONTAINER (sw), tree_view);
1463 	gtk_widget_show_all (sw);
1464 	return sw;
1465 }
1466 
1467 /**
1468  * anjuta_ui_get_icon_factory:
1469  * @ui: A #AnjutaUI object
1470  *
1471  * This returns the IconFactory object. All icons should be registered using
1472  * this icon factory. Read the documentation for #GtkIconFactory on how to
1473  * use it.
1474  *
1475  * Return value: (transfer none): The #GtkIconFactory object used by it
1476  */
1477 GtkIconFactory*
anjuta_ui_get_icon_factory(AnjutaUI * ui)1478 anjuta_ui_get_icon_factory (AnjutaUI *ui)
1479 {
1480 	g_return_val_if_fail (ANJUTA_IS_UI (ui), NULL);
1481 	return ui->priv->icon_factory;
1482 }
1483 
1484 /**
1485  * anjuta_ui_dump_tree:
1486  * @ui: A #AnjutaUI object.
1487  *
1488  * Dumps the current UI XML tree in STDOUT. Useful for debugging.
1489  */
1490 void
anjuta_ui_dump_tree(AnjutaUI * ui)1491 anjuta_ui_dump_tree (AnjutaUI *ui)
1492 {
1493 	gchar *ui_str;
1494 
1495 	g_return_if_fail (ANJUTA_IS_UI(ui));
1496 
1497 	gtk_ui_manager_ensure_update (GTK_UI_MANAGER (ui));
1498 	ui_str = gtk_ui_manager_get_ui (GTK_UI_MANAGER (ui));
1499 	/* DEBUG_PRINT ("%s", ui_str); */
1500 	g_free (ui_str);
1501 }
1502 
1503 /*
1504  * Accels
1505  */
1506 static gchar *
anjuta_ui_get_accel_file(void)1507 anjuta_ui_get_accel_file (void)
1508 {
1509 	return anjuta_util_get_user_config_file_path ("anjuta-accels", NULL);
1510 }
1511 
1512 void
anjuta_ui_load_accels(const gchar * filename)1513 anjuta_ui_load_accels (const gchar *filename)
1514 {
1515 	if (filename)
1516 	{
1517 		gtk_accel_map_load (filename);
1518 	}
1519 	else
1520 	{
1521 		gchar *def_filename = anjuta_ui_get_accel_file ();
1522 		if (def_filename != NULL)
1523 		{
1524 			gtk_accel_map_load (def_filename);
1525 			g_free (def_filename);
1526 		}
1527 	}
1528 }
1529 
1530 void
anjuta_ui_save_accels(const gchar * filename)1531 anjuta_ui_save_accels (const gchar *filename)
1532 {
1533 	if (filename)
1534 	{
1535 		gtk_accel_map_save (filename);
1536 	}
1537 	else
1538 	{
1539         	gchar * def_filename = anjuta_ui_get_accel_file ();
1540 
1541 		if (def_filename != NULL)
1542 		{
1543 			gtk_accel_map_save (def_filename);
1544 			g_free (def_filename);
1545 		}
1546 	}
1547 }
1548 
anjuta_ui_remove_accel(AnjutaUI * ui,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)1549 static void anjuta_ui_remove_accel (AnjutaUI *ui,
1550      const gchar *accel_path, guint accel_key,
1551      GdkModifierType accel_mods, gboolean changed)
1552 {
1553     gtk_accel_group_disconnect_key (anjuta_ui_get_accel_group(ui), accel_key, accel_mods);
1554 }
1555 
1556 void
anjuta_ui_unload_accels(AnjutaUI * ui)1557 anjuta_ui_unload_accels (AnjutaUI *ui)
1558 {
1559     anjuta_ui_save_accels (NULL);
1560     gtk_accel_map_foreach_unfiltered (ui, (GtkAccelMapForeach) anjuta_ui_remove_accel);
1561 }
1562 
1563 
1564