1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta-shell.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-shell
23  * @title: AnjutaShell
24  * @short_description: Application shell interface
25  * @see_also:
26  * @stability: Unstable
27  * @include: libanjuta/anjuta-shell.h
28  *
29  * Shell is the playground where plugins are loaded and their UI
30  * widgets shown. It is also a place where plugins export objects for letting
31  * other pluings to use. Plugins are loaded into shell on demand, but some
32  * plugins are loaded on startup (such as help and text editor plugin).
33  * Demand to load a plugin can be made by requesting for a primary inferface
34  * using anjuta_shell_get_interface() or anjuta_shell_get_object().
35  *
36  * Plugins can add widgets in shell with
37  * anjuta_shell_add_widget() and remove with anjuta_shell_remove_widget()
38  * functions.
39  *
40  * In Anjuta, shell is implemented using an advanced widget docking system,
41  * allowing plugin widgets to dock, undock and layout in any fashion. Dock
42  * layout is also maintained internally and is transparent to plugin
43  * implementations.
44  *
45  * #AnjutaShell allows plugins to export arbitrary objects as <emphasis>
46  * values</emphasis> in its <emphasis>Values System</emphasis>. "value_added"
47  * and "value_removed" signals are emitted when a value is added to or
48  * removed from the <emphasis>Values System</emphasis>, hence notifying
49  * plugins of its state. However, plugins should really not connect directly
50  * to these signals, because they are emitted for all values
51  * and not just for the values the plugin is interested in. Instead,
52  * to monitor specific <emphasis>Values</emphasis>, plugins should
53  * setup watches using anjuta_plugin_add_watch().
54  *
55  * <emphasis>Values</emphasis> are added, get or removed with
56  * anjuta_shell_add_value() and anjuta_shell_get_value() or
57  * anjuta_shell_remove_value(). There multi-valued equivalent functions
58  * can be used to manipulate multiple values at once.
59  *
60  * <emphasis>Values</emphasis> are identified with names. Since <emphasis>
61  * Values</emphasis> are effectively variables, their names should follow
62  * the standard GNOME variable naming convention and should be as descriptive
63  * as possible (e.g project_root_directory, project_name etc.). It is also
64  * essential that meaningful prefix be given to names so that <emphasis>
65  * Values</emphasis> are easily grouped (e.g all values exported by a
66  * project manager should start with project_ prefix).
67  *
68  * Plugins can find other plugins with anjuta_shell_get_object() or
69  * anjuta_shell_get_interface() based on their primary interfaces.
70  */
71 
72 #include <config.h>
73 #include <string.h>
74 #include <gobject/gvaluecollector.h>
75 #include "anjuta-shell.h"
76 #include "anjuta-marshal.h"
77 #include "anjuta-debug.h"
78 
79 typedef struct {
80 	GtkWidget *widget;
81 	gchar *name;
82 	gchar *title;
83 	gchar *stock_id;
84 	AnjutaShellPlacement placement;
85 	gboolean locked;
86 } WidgetQueueData;
87 
88 static void
on_widget_data_free(WidgetQueueData * data)89 on_widget_data_free (WidgetQueueData *data)
90 {
91 	g_object_unref (data->widget);
92 	g_free (data->name);
93 	g_free (data->title);
94 	g_free (data->stock_id);
95 	g_free (data);
96 }
97 
98 static void
on_widget_data_add(WidgetQueueData * data,AnjutaShell * shell)99 on_widget_data_add (WidgetQueueData *data, AnjutaShell *shell)
100 {
101 	ANJUTA_SHELL_GET_IFACE (shell)->add_widget_full (shell, data->widget,
102 												data->name,
103 												data->title,
104 												data->stock_id,
105 												data->placement,
106 												data->locked,
107 												NULL);
108 }
109 
110 static void
on_destroy_widget_queue(gpointer data)111 on_destroy_widget_queue (gpointer data)
112 {
113 	GQueue *queue = (GQueue*)data;
114 	g_queue_foreach (queue, (GFunc)on_widget_data_free, NULL);
115 	g_queue_free (queue);
116 }
117 
118 GQuark
anjuta_shell_error_quark(void)119 anjuta_shell_error_quark (void)
120 {
121 	static GQuark quark = 0;
122 
123 	if (quark == 0) {
124 		quark = g_quark_from_static_string ("anjuta-shell-quark");
125 	}
126 
127 	return quark;
128 }
129 
130 /**
131  * anjuta_shell_freeze:
132  * @shell: A #AnjutaShell interface.
133  * @error: Error propagation object.
134  *
135  * Freezes addition of any UI elements (widgets) in the shell. All widget
136  * additions are queued for later additions when freeze count reaches 0.
137  * Any number of this function can be called and each call will increase
138  * the freeze count. anjuta_shell_thaw() will reduce the freeze count by
139  * 1 and real thawing happens when the count reaches 0.
140  */
141 void
anjuta_shell_freeze(AnjutaShell * shell,GError ** error)142 anjuta_shell_freeze (AnjutaShell *shell, GError **error)
143 {
144 	gint freeze_count;
145 
146 	g_return_if_fail (shell != NULL);
147 	freeze_count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (shell),
148 													   "__freeze_count"));
149 	freeze_count++;
150 	g_object_set_data (G_OBJECT (shell), "__freeze_count",
151 					   GINT_TO_POINTER (freeze_count));
152 }
153 
154 /**
155  * anjuta_shell_thaw:
156  * @shell: A #AnjutaShell interface.
157  * @error: Error propagation object.
158  *
159  * Reduces the freeze count by one and performs pending widget additions
160  * when the count reaches 0.
161  */
162 void
anjuta_shell_thaw(AnjutaShell * shell,GError ** error)163 anjuta_shell_thaw (AnjutaShell *shell, GError **error)
164 {
165 	gint freeze_count;
166 
167 	g_return_if_fail (shell != NULL);
168 	freeze_count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (shell),
169 													   "__freeze_count"));
170 	freeze_count--;
171 	if (freeze_count < 0)
172 		freeze_count = 0;
173 	g_object_set_data (G_OBJECT (shell), "__freeze_count",
174 					   GINT_TO_POINTER (freeze_count));
175 
176 	if (freeze_count <= 0)
177 	{
178 		/* Add all pending widgets */
179 		/* DEBUG_PRINT ("%s", "Thawing shell ..."); */
180 
181 		GQueue *queue;
182 		queue = g_object_get_data (G_OBJECT (shell), "__widget_queue");
183 		if (queue)
184 		{
185 			g_queue_reverse (queue);
186 			g_queue_foreach (queue, (GFunc)on_widget_data_add, shell);
187 			g_object_set_data (G_OBJECT (shell), "__widget_queue", NULL);
188 		}
189 	}
190 }
191 
192 /**
193  * anjuta_shell_add_widget:
194  * @shell: A #AnjutaShell interface.
195  * @widget: Then widget to add
196  * @name: Name of the widget. None translated string used to identify it in
197  * the shell.
198  * @stock_id: Icon stock ID. Could be null.
199  * @title: Translated string which is displayed along side the widget when
200  * required (eg. as window title or notebook tab label).
201  * @placement: Placement of the widget in shell.
202  * @error: Error propagation object.
203  *
204  * Adds @widget in the shell. The @placement tells where the widget should
205  * appear, but generally it will be overridden by the container
206  * (dock, notebook, GtkContainer etc.) saved layout.
207  */
208 void
anjuta_shell_add_widget(AnjutaShell * shell,GtkWidget * widget,const char * name,const char * title,const char * stock_id,AnjutaShellPlacement placement,GError ** error)209 anjuta_shell_add_widget (AnjutaShell *shell,
210 			 GtkWidget *widget,
211 			 const char *name,
212 			 const char *title,
213 			 const char *stock_id,
214 			 AnjutaShellPlacement placement,
215 			 GError **error)
216 {
217 	anjuta_shell_add_widget_full(shell, widget, name, title,
218 							stock_id, placement, FALSE, error);
219 }
220 
221 /**
222  * anjuta_shell_add_widget_full:
223  * @shell: A #AnjutaShell interface.
224  * @widget: Then widget to add
225  * @name: Name of the widget. None translated string used to identify it in
226  * the shell.
227  * @stock_id: Icon stock ID. Could be null.
228  * @title: Translated string which is displayed along side the widget when
229  * required (eg. as window title or notebook tab label).
230  * @placement: Placement of the widget in shell.
231  * @locked: Whether to lock that widget (do not use this, it's only
232  * 			useful to some stock plugins
233  * @error: Error propagation object.
234  *
235  * Adds @widget in the shell. The @placement tells where the widget should
236  * appear, but generally it will be overridden by the container
237  * (dock, notebook, GtkContainer etc.) saved layout.
238  *
239  * Normally just use anjuta_shell_add_widget() because you do not
240  * use locking.
241  */
242 void
anjuta_shell_add_widget_full(AnjutaShell * shell,GtkWidget * widget,const char * name,const char * title,const char * stock_id,AnjutaShellPlacement placement,gboolean locked,GError ** error)243 anjuta_shell_add_widget_full (AnjutaShell *shell,
244 			 GtkWidget *widget,
245 			 const char *name,
246 			 const char *title,
247 			 const char *stock_id,
248 			 AnjutaShellPlacement placement,
249 			 gboolean locked,
250 			 GError **error)
251 {
252 	GQueue *widget_queue;
253 	gint freeze_count;
254 
255 	g_return_if_fail (shell != NULL);
256 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
257 	g_return_if_fail (widget != NULL);
258 	g_return_if_fail (GTK_IS_WIDGET (widget));
259 	g_return_if_fail (name != NULL);
260 	g_return_if_fail (title != NULL);
261 
262 	freeze_count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (shell),
263 													   "__freeze_count"));
264 	if (freeze_count <= 0)
265 	{
266 		ANJUTA_SHELL_GET_IFACE (shell)->add_widget_full (shell, widget, name,
267 													title, stock_id,
268 													placement, locked, error);
269 	}
270 	else
271 	{
272 		/* Queue the operation */
273 		WidgetQueueData *qd;
274 
275 		widget_queue = g_object_get_data (G_OBJECT (shell), "__widget_queue");
276 		if (!widget_queue)
277 		{
278 			widget_queue = g_queue_new ();
279 			g_object_set_data_full (G_OBJECT (shell), "__widget_queue",
280 									widget_queue, on_destroy_widget_queue);
281 		}
282 		qd = g_new0(WidgetQueueData, 1);
283 		g_object_ref (G_OBJECT (widget));
284 		qd->widget = widget;
285 		qd->name = g_strdup (name);
286 		qd->title = g_strdup (title);
287 		qd->locked = locked;
288 		if (stock_id)
289 			qd->stock_id = g_strdup (stock_id);
290 		qd->placement = placement;
291 
292 		g_queue_push_head (widget_queue, qd);
293 	}
294 }
295 
296 /**
297  * anjuta_shell_add_widget_custom:
298  * @shell: A #AnjutaShell interface.
299  * @widget: Then widget to add
300  * @name: Name of the widget. None translated string used to identify it in
301  * the shell.
302  * @title: title of the widget (translated)
303  * @stock_id: Icon stock ID. Could be null.
304  * @label: Label widget to use
305  * @placement: Placement of the widget in shell.
306  * @error: Error propagation object.
307  *
308  * Adds @widget in the shell. The @placement tells where the widget should
309  * appear, but generally it will be overridden by the container
310  * (dock, notebook, GtkContainer etc.) saved layout.
311  *
312  * Using this method you can pass a custom widget as label.
313  */
anjuta_shell_add_widget_custom(AnjutaShell * shell,GtkWidget * widget,const char * name,const char * title,const char * stock_id,GtkWidget * label,AnjutaShellPlacement placement,GError ** error)314 void   anjuta_shell_add_widget_custom (AnjutaShell   *shell,
315                                        GtkWidget     *widget,
316                                        const char      *name,
317                                        const char      *title,
318                                        const char      *stock_id,
319                                        GtkWidget     *label,
320                                        AnjutaShellPlacement placement,
321                                        GError        **error)
322 {
323 	ANJUTA_SHELL_GET_IFACE (shell)->add_widget_custom (shell, widget, name, title, stock_id, label,
324 	                                                   placement, error);
325 }
326 
327 /**
328  * anjuta_shell_remove_widget:
329  * @shell: A #AnjutaShell interface
330  * @widget: The widget to remove
331  * @error: Error propagation object
332  *
333  * Removes the widget from shell. The widget should have been added before
334  * with #anjuta_shell_add_widget.
335  */
336 void
anjuta_shell_remove_widget(AnjutaShell * shell,GtkWidget * widget,GError ** error)337 anjuta_shell_remove_widget (AnjutaShell *shell,
338 							GtkWidget *widget,
339 							GError **error)
340 {
341 	GQueue *queue;
342 	gboolean found_in_queue;
343 
344 	g_return_if_fail (shell != NULL);
345 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
346 	g_return_if_fail (widget != NULL);
347 	g_return_if_fail (GTK_IS_WIDGET (widget));
348 
349 	/* If there is a queue, remove widgets from it */
350 	found_in_queue = FALSE;
351 	queue = g_object_get_data (G_OBJECT (shell), "__widget_queue");
352 	if (queue)
353 	{
354 		gint i;
355 		for (i = g_queue_get_length(queue) - 1; i >= 0; i--)
356 		{
357 			WidgetQueueData *qd;
358 
359 			qd = g_queue_peek_nth (queue, i);
360 			if (qd->widget == widget)
361 			{
362 				g_queue_remove (queue, qd);
363 				on_widget_data_free (qd);
364 				found_in_queue = TRUE;
365 				break;
366 			}
367 		}
368 	}
369 	if (!found_in_queue)
370 		ANJUTA_SHELL_GET_IFACE (shell)->remove_widget (shell, widget, error);
371 }
372 
373 /**
374  * anjuta_shell_present_widget:
375  * @shell: A #AnjutaShell interface
376  * @widget: The widget to present
377  * @error: Error propagation object
378  *
379  * Make sure the widget is visible to user. If the widget is hidden, it will
380  * be shown. If it is not visible to user, it will be made visible.
381  */
382 void
anjuta_shell_present_widget(AnjutaShell * shell,GtkWidget * widget,GError ** error)383 anjuta_shell_present_widget (AnjutaShell *shell,
384 							 GtkWidget *widget,
385 							 GError **error)
386 {
387 	GQueue *queue;
388 	gboolean found_in_queue;
389 
390 	g_return_if_fail (shell != NULL);
391 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
392 	g_return_if_fail (widget != NULL);
393 	g_return_if_fail (GTK_IS_WIDGET (widget));
394 
395 	/* If there is a queue and the widget is in the queue, there is no
396 	 * way we can 'present' the widget */
397 	found_in_queue = FALSE;
398 	queue = g_object_get_data (G_OBJECT (shell), "__widget_queue");
399 	if (queue)
400 	{
401 		gint i;
402 		for (i = g_queue_get_length(queue) - 1; i >= 0; i--)
403 		{
404 			WidgetQueueData *qd;
405 
406 			qd = g_queue_peek_nth (queue, i);
407 			if (qd->widget == widget)
408 			{
409 				found_in_queue = TRUE;
410 				break;
411 			}
412 		}
413 	}
414 	if (!found_in_queue)
415 		ANJUTA_SHELL_GET_IFACE (shell)->present_widget (shell, widget, error);
416 }
417 
418 /**
419  * anjuta_shell_iconify_dockable_widget:
420  * @shell: A #AnjutaShell interface.
421  * @widget: a #GtkWidget to iconify.
422  * @error: Error propagation object.
423  *
424  * If the widget is dockable, it iconifies it.
425  */
anjuta_shell_iconify_dockable_widget(AnjutaShell * shell,GtkWidget * widget,GError ** error)426 void anjuta_shell_iconify_dockable_widget (AnjutaShell *shell,
427                                      GtkWidget   *widget,
428                                      GError      **error)
429 {
430 	ANJUTA_SHELL_GET_IFACE (shell)->iconify_dockable_widget (shell, widget, error);
431 }
432 
433 /**
434  * anjuta_shell_hide_dockable_widget:
435  * @shell: A #AnjutaShell interface.
436  * @widget: a #GtkWidget to hide.
437  * @error: Error propagation object.
438  *
439  * If the widget is dockable, it hides it.
440  */
anjuta_shell_hide_dockable_widget(AnjutaShell * shell,GtkWidget * widget,GError ** error)441 void anjuta_shell_hide_dockable_widget (AnjutaShell *shell,
442                                      GtkWidget   *widget,
443                                      GError      **error)
444 {
445 	ANJUTA_SHELL_GET_IFACE (shell)->hide_dockable_widget (shell, widget, error);
446 }
447 
448 /**
449  * anjuta_shell_show_dockable_widget:
450  * @shell: A #AnjutaShell interface.
451  * @widget: a #GtkWidget to show.
452  * @error: Error propagation object.
453  *
454  * If the widget was hidden or iconified, it will make it visible.
455  */
anjuta_shell_show_dockable_widget(AnjutaShell * shell,GtkWidget * widget,GError ** error)456 void anjuta_shell_show_dockable_widget (AnjutaShell *shell,
457                                      GtkWidget   *widget,
458                                      GError      **error)
459 {
460 	ANJUTA_SHELL_GET_IFACE (shell)->show_dockable_widget (shell, widget, error);
461 }
462 
463 /**
464  * anjuta_shell_maximize_widget:
465  * @shell: A #AnjutaShell interface.
466  * @widget_name: Name of the widget to be maximized.
467  * @error: Error propagation object.
468  *
469  * Maximizes a widget so it will occupy all the possible space.
470  */
anjuta_shell_maximize_widget(AnjutaShell * shell,const char * widget_name,GError ** error)471 void anjuta_shell_maximize_widget   (AnjutaShell *shell,
472                                      const char  *widget_name,
473                                      GError      **error)
474 {
475 	ANJUTA_SHELL_GET_IFACE (shell)->maximize_widget (shell, widget_name, error);
476 }
477 
478 /**
479  * anjuta_shell_unmaximize:
480  * @shell: A #AnjutaShell interface.
481  * @error: Error propagation object.
482  *
483  * Unmaximizes the UI which was previously maximized by
484  * #anjuta_shell_maximize_widget
485  */
anjuta_shell_unmaximize(AnjutaShell * shell,GError ** error)486 void anjuta_shell_unmaximize (AnjutaShell *shell,
487                               GError      **error)
488 {
489 	ANJUTA_SHELL_GET_IFACE (shell)->unmaximize (shell, error);
490 }
491 
492 /**
493  * anjuta_shell_create_window:
494  * @shell: A #AnjutaShell interface
495  * @error: Error propagation object.
496  *
497  * Create a new main window in the same process.
498  */
499 AnjutaShell*
anjuta_shell_create_window(AnjutaShell * shell,GError ** error)500 anjuta_shell_create_window (AnjutaShell  *shell,
501                          GError		   **error)
502 {
503 	return ANJUTA_SHELL_GET_IFACE (shell)->create_window (shell, error);
504 }
505 
506 /**
507  * anjuta_shell_add_value:
508  * @shell: A #AnjutaShell interface
509  * @name: Name of the value
510  * @value: Value to add
511  * @error: Error propagation object
512  *
513  * Sets a value in the shell with the given name. Any previous value will
514  * be overridden. "value_added" signal will be emitted. Objects connecting
515  * to this signal can then update their data according to the new value.
516  */
517 void
anjuta_shell_add_value(AnjutaShell * shell,const char * name,const GValue * value,GError ** error)518 anjuta_shell_add_value (AnjutaShell *shell,
519 			const char *name,
520 			const GValue *value,
521 			GError **error)
522 {
523 	g_return_if_fail (shell != NULL);
524 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
525 	g_return_if_fail (name != NULL);
526 	g_return_if_fail (value != NULL);
527 
528 	ANJUTA_SHELL_GET_IFACE (shell)->add_value (shell, name, value, error);
529 }
530 
531 /**
532  * anjuta_shell_add_valist:
533  * @shell: A #AnjutaShell interface
534  * @first_name: First value name
535  * @first_type: First value type
536  * @var_args: First value, Second value name, Second value type ....
537  *
538  * Adds a valist of values in the shell. The valist should be in the order -
539  * value1, name2, type2, value2,... "value_added" signal will be emitted
540  * for each of the value.
541  */
542 void
anjuta_shell_add_valist(AnjutaShell * shell,const char * first_name,GType first_type,va_list var_args)543 anjuta_shell_add_valist (AnjutaShell *shell,
544 			 const char *first_name,
545 			 GType first_type,
546 			 va_list var_args)
547 {
548 	const char *name;
549 	GType type;
550 
551 	g_return_if_fail (shell != NULL);
552 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
553 	g_return_if_fail (first_name != NULL);
554 
555 	name = first_name;
556 	type = first_type;
557 
558 	while (name) {
559 		GValue value = {0, };
560 		GError *err = NULL;
561 		char *error;
562 
563 		g_value_init (&value, type);
564 
565 		G_VALUE_COLLECT (&value, var_args, 0, &error);
566 
567 		if (error){
568 			g_warning ("%s: %s", G_STRLOC, error);
569 			g_free (error);
570 			break;
571 		}
572 
573 		anjuta_shell_add_value (shell, name, &value, &err);
574 
575 		g_value_unset (&value);
576 
577 		if (err) {
578 			g_warning ("Could not set value: %s\n", err->message);
579 			g_error_free (err);
580 			break;
581 		}
582 
583 		name = va_arg (var_args, char *);
584 		if (name) {
585 			type = va_arg (var_args, GType);
586 		}
587 	}
588 }
589 
590 /**
591  * anjuta_shell_add:
592  * @shell: A #AnjutaShell interface
593  * @first_name: First value name
594  * @first_type: First value type
595  * @...: First value, Second value name, Second value type .... %NULL
596  *
597  * Adds a list of values in the shell. The list should be %NULL terminated
598  * and should be in the order - name1, type1, value1, name2, type2, value2,
599  * ..., %NULL. "value_added" signal will be emitted for each of the value.
600  */
601 void
anjuta_shell_add(AnjutaShell * shell,const char * first_name,GType first_type,...)602 anjuta_shell_add (AnjutaShell  *shell,
603 		  const char *first_name,
604 		  GType first_type,
605 		  ...)
606 {
607 	va_list var_args;
608 
609 	g_return_if_fail (shell != NULL);
610 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
611 	g_return_if_fail (first_name != NULL);
612 
613 	va_start (var_args, first_type);
614 	anjuta_shell_add_valist (shell, first_name, first_type, var_args);
615 	va_end (var_args);
616 }
617 
618 /**
619  * anjuta_shell_get_value:
620  * @shell: A #AnjutaShell interface
621  * @name: Name of the value to get
622  * @value: Value to get
623  * @error: Error propagation object
624  *
625  * Gets a value from the shell with the given name. The value will be set
626  * in the passed value pointer.
627  */
628 void
anjuta_shell_get_value(AnjutaShell * shell,const char * name,GValue * value,GError ** error)629 anjuta_shell_get_value (AnjutaShell *shell,
630 			const char *name,
631 			GValue *value,
632 			GError **error)
633 {
634 	g_return_if_fail (shell != NULL);
635 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
636 	g_return_if_fail (name != NULL);
637 	g_return_if_fail (value != NULL);
638 
639 	ANJUTA_SHELL_GET_IFACE (shell)->get_value (shell, name, value, error);
640 }
641 
642 /**
643  * anjuta_shell_get_valist:
644  * @shell: A #AnjutaShell interface
645  * @first_name: First value name
646  * @first_type: First value type
647  * @var_args: First value holder, Second value name, Second value type ....
648  *
649  * Gets a valist of values from the shell. The valist should be in the order -
650  * value1, name2, type2, value2,...
651  */
652 void
anjuta_shell_get_valist(AnjutaShell * shell,const char * first_name,GType first_type,va_list var_args)653 anjuta_shell_get_valist (AnjutaShell *shell,
654 			 const char *first_name,
655 			 GType first_type,
656 			 va_list var_args)
657 {
658 	const char *name;
659 	GType type;
660 
661 	g_return_if_fail (shell != NULL);
662 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
663 	g_return_if_fail (first_name != NULL);
664 
665 	name = first_name;
666 	type = first_type;
667 
668 	while (name) {
669 		GValue value = {0, };
670 		GError *err = NULL;
671 		char *error;
672 
673 		g_value_init (&value, type);
674 
675 		anjuta_shell_get_value (shell, name, &value, &err);
676 
677 		if (err) {
678 			g_warning ("Could not get value: %s", err->message);
679 			g_error_free (err);
680 			break;
681 		}
682 
683 		G_VALUE_LCOPY (&value, var_args, 0, &error);
684 
685 		if (error){
686 			g_warning ("%s: %s", G_STRLOC, error);
687 			g_free (error);
688 			break;
689 		}
690 
691 		g_value_unset (&value);
692 
693 		name = va_arg (var_args, char *);
694 		if (name) {
695 			type = va_arg (var_args, GType);
696 		}
697 	}
698 }
699 
700 /**
701  * anjuta_shell_get:
702  * @shell: A #AnjutaShell interface
703  * @first_name: First value name
704  * @first_type: First value type
705  * @...: First value holder, Second value name, Second value type .... %NULL
706  *
707  * Gets a list of values in the shell. The list should be %NULL terminated
708  * and should be in the order - name1, type1, value1, name2, type2, value2,
709  * ..., %NULL.
710  */
711 void
anjuta_shell_get(AnjutaShell * shell,const char * first_name,GType first_type,...)712 anjuta_shell_get (AnjutaShell  *shell,
713 		  const char *first_name,
714 		  GType first_type,
715 		  ...)
716 {
717 	va_list var_args;
718 
719 	g_return_if_fail (shell != NULL);
720 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
721 	g_return_if_fail (first_name != NULL);
722 
723 	va_start (var_args, first_type);
724 	anjuta_shell_get_valist (shell, first_name, first_type, var_args);
725 	va_end (var_args);
726 }
727 
728 /**
729  * anjuta_shell_remove_value:
730  * @shell: A #AnjutaShell interface
731  * @name: Name of the value to remove
732  * @error: Error propagation object
733  *
734  * Removes a value from the shell with the given name. "value_removed" signal
735  * will be emitted. Objects connecting to this signal can then update their
736  * data/internal-state accordingly.
737  */
738 void
anjuta_shell_remove_value(AnjutaShell * shell,const char * name,GError ** error)739 anjuta_shell_remove_value (AnjutaShell *shell,
740 						   const char *name,
741 						   GError **error)
742 {
743 	g_return_if_fail (shell != NULL);
744 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
745 	g_return_if_fail (name != NULL);
746 
747 	ANJUTA_SHELL_GET_IFACE (shell)->remove_value (shell, name, error);
748 }
749 
750 /**
751  * anjuta_shell_get_object:
752  * @shell: A #AnjutaShell interface
753  * @iface_name: The interface implemented by the object to be found
754  * @error: Error propagation.
755  *
756  * Searches the currently available plugins to find the one which
757  * implements the given interface as primary interface and returns it. If
758  * the plugin is not yet loaded, it will be loaded and activated.
759  * The returned object is garanteed to be an implementor of the
760  * interface (as exported by the plugin metafile). It only searches
761  * from the pool of plugin objects loaded in this shell and can only search
762  * by primary interface. If there are more objects implementing this primary
763  * interface, user might be prompted to select one from them (and might give
764  * the option to use it as default for future queries). A typical usage of this
765  * function is:
766  * <programlisting>
767  * GObject *docman =
768  *     anjuta_plugins_get_object (shell, "IAnjutaDocumentManager", error);
769  * </programlisting>
770  * Notice that this function takes the interface name string as string, unlike
771  * anjuta_plugins_get_interface() which takes the type directly.
772  *
773  * Return value: (transfer none): A plugin object implementing the primary
774  * interface or %NULL.
775  */
776 GObject*
anjuta_shell_get_object(AnjutaShell * shell,const gchar * iface_name,GError ** error)777 anjuta_shell_get_object (AnjutaShell *shell, const gchar *iface_name,
778 						 GError **error)
779 {
780 	g_return_val_if_fail (shell != NULL, NULL);
781 	g_return_val_if_fail (iface_name != NULL, NULL);
782 	g_return_val_if_fail (ANJUTA_IS_SHELL (shell), NULL);
783 
784 	return ANJUTA_SHELL_GET_IFACE (shell)->get_object (shell, iface_name, error);
785 }
786 
787 /**
788  * anjuta_shell_get_status:
789  * @shell: A #AnjutaShell interface
790  * @error: Error propagation object
791  *
792  * Retrieves the #AnjutaStatus object associated with the shell.
793  *
794  * Return value: (transfer none): The #AnjutaStatus object.
795  */
796 AnjutaStatus*
anjuta_shell_get_status(AnjutaShell * shell,GError ** error)797 anjuta_shell_get_status (AnjutaShell *shell, GError **error)
798 {
799 	g_return_val_if_fail (shell != NULL, NULL);
800 	g_return_val_if_fail (ANJUTA_IS_SHELL (shell), NULL);
801 
802 	return ANJUTA_SHELL_GET_IFACE (shell)->get_status (shell, error);
803 }
804 
805 /**
806  * anjuta_shell_get_ui:
807  * @shell: A #AnjutaShell interface
808  * @error: Error propagation object
809  *
810  * Retrieves the #AnjutaUI object associated with the shell.
811  *
812  * Return value: (transfer none): The #AnjutaUI object.
813  */
814 AnjutaUI*
anjuta_shell_get_ui(AnjutaShell * shell,GError ** error)815 anjuta_shell_get_ui (AnjutaShell *shell, GError **error)
816 {
817 	g_return_val_if_fail (shell != NULL, NULL);
818 	g_return_val_if_fail (ANJUTA_IS_SHELL (shell), NULL);
819 
820 	return ANJUTA_SHELL_GET_IFACE (shell)->get_ui (shell, error);
821 }
822 
823 /**
824  * anjuta_shell_get_preferences:
825  * @shell: A #AnjutaShell interface
826  * @error: Error propagation object
827  *
828  * Retrieves the #AnjutaPreferences object associated with the shell.
829  *
830  * Return value: (transfer none): The #AnjutaPreferences object.
831  */
832 AnjutaPreferences*
anjuta_shell_get_preferences(AnjutaShell * shell,GError ** error)833 anjuta_shell_get_preferences (AnjutaShell *shell, GError **error)
834 {
835 	g_return_val_if_fail (shell != NULL, NULL);
836 	g_return_val_if_fail (ANJUTA_IS_SHELL (shell), NULL);
837 
838 	return ANJUTA_SHELL_GET_IFACE (shell)->get_preferences (shell, error);
839 }
840 
841 /**
842  * anjuta_shell_get_plugin_manager:
843  * @shell: A #AnjutaShell interface
844  * @error: Error propagation object
845  *
846  * Retrieves the #AnjutaPluginManager object associated with the shell.
847  *
848  * Return value: (transfer none): The #AnjutaPluginManager object.
849  */
850 AnjutaPluginManager*
anjuta_shell_get_plugin_manager(AnjutaShell * shell,GError ** error)851 anjuta_shell_get_plugin_manager (AnjutaShell *shell, GError **error)
852 {
853 	g_return_val_if_fail (shell != NULL, NULL);
854 	g_return_val_if_fail (ANJUTA_IS_SHELL (shell), NULL);
855 
856 	return ANJUTA_SHELL_GET_IFACE (shell)->get_plugin_manager (shell, error);
857 }
858 
859 /**
860  * anjuta_shell_get_profile_manager:
861  * @shell: A #AnjutaShell interface
862  * @error: Error propagation object
863  *
864  * Retrieves the #AnjutaProfileManager object associated with the shell.
865  *
866  * Return value: (transfer none): The #AnjutaProfileManager object.
867  */
868 AnjutaProfileManager*
anjuta_shell_get_profile_manager(AnjutaShell * shell,GError ** error)869 anjuta_shell_get_profile_manager (AnjutaShell *shell, GError **error)
870 {
871 	g_return_val_if_fail (shell != NULL, NULL);
872 	g_return_val_if_fail (ANJUTA_IS_SHELL (shell), NULL);
873 
874 	return ANJUTA_SHELL_GET_IFACE (shell)->get_profile_manager (shell, error);
875 }
876 
877 /**
878  * anjuta_shell_saving_push:
879  * @shell: A #AnjutaShell interface
880  *
881  * Increase the count of files that need to be saved
882  *
883  */
anjuta_shell_saving_push(AnjutaShell * shell)884 void anjuta_shell_saving_push	    (AnjutaShell* shell)
885 {
886 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
887 
888 	ANJUTA_SHELL_GET_IFACE (shell)->saving_push (shell);
889 }
890 
891 /**
892  * anjuta_shell_saving_pop:
893  * @shell: A #AnjutaShell interface
894  *
895  * Decrease the count of files that need to be saved
896  *
897  */
anjuta_shell_saving_pop(AnjutaShell * shell)898 void anjuta_shell_saving_pop	    (AnjutaShell* shell)
899 {
900 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
901 
902 	ANJUTA_SHELL_GET_IFACE (shell)->saving_pop (shell);
903 }
904 
905 void
anjuta_shell_session_save(AnjutaShell * shell,const gchar * session_directory,GError ** error)906 anjuta_shell_session_save (AnjutaShell *shell, const gchar *session_directory,
907 						   GError **error)
908 {
909 	AnjutaSession *session;
910 
911 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
912 	g_return_if_fail (session_directory != NULL);
913 
914 	session = anjuta_session_new (session_directory);
915 	anjuta_session_clear (session);
916 	g_signal_emit_by_name (G_OBJECT (shell), "save_session",
917 						   ANJUTA_SESSION_PHASE_FIRST, session);
918 	g_signal_emit_by_name (G_OBJECT (shell), "save_session",
919 						   ANJUTA_SESSION_PHASE_NORMAL, session);
920 	g_signal_emit_by_name (G_OBJECT (shell), "save_session",
921 						   ANJUTA_SESSION_PHASE_LAST, session);
922 	anjuta_session_sync (session);
923 	g_object_unref (session);
924 }
925 
926 void
anjuta_shell_session_load(AnjutaShell * shell,const gchar * session_directory,GError ** error)927 anjuta_shell_session_load (AnjutaShell *shell, const gchar *session_directory,
928 						   GError **error)
929 {
930 	AnjutaSession *session;
931 	AnjutaSession *child;
932 
933 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
934 	g_return_if_fail (session_directory != NULL);
935 
936 	/* It is possible that loading a session triggers the load on another
937 	 * session. It happens when restoring an user session includes a project.
938 	 * The project session is loaded while the user session is still loading. */
939 	session = anjuta_session_new (session_directory);
940 
941 	child = g_object_get_data (G_OBJECT (shell), "__session_loading");
942 	g_object_set_data (G_OBJECT (shell), "__session_loading", session);
943 	if (child != NULL)
944 	{
945 		/* There is already a session loading.
946 		 * Replace it with our own session. The previous session will be
947 		 * aborted and our session will be loaded afterward. */
948 		g_object_unref (G_OBJECT (child));
949 
950 		return;
951 	}
952 
953 	/* This is the top session. This code emits all signals and check after
954 	 * each phase that the session is still the same. If it is not the case
955 	 * the session is aborted and the new session is loaded. */
956 	for (;;)
957 	{
958 		if (child != NULL)
959 		{
960 			/* Abort previous session */
961 			g_signal_emit_by_name (G_OBJECT (shell), "load_session",
962 								   ANJUTA_SESSION_PHASE_END, session);
963 			g_object_unref (session);
964 			/* Reread session in case PHASE_END has triggered another session */
965 			session = g_object_get_data (G_OBJECT (shell), "__session_loading");
966 		}
967 		g_object_ref (session);
968 
969 		g_signal_emit_by_name (G_OBJECT (shell), "load_session",
970 							   ANJUTA_SESSION_PHASE_START, session);
971 		child = g_object_get_data (G_OBJECT (shell), "__session_loading");
972 		if (child != session) continue;
973 		g_signal_emit_by_name (G_OBJECT (shell), "load_session",
974 							   ANJUTA_SESSION_PHASE_FIRST, session);
975 		child = g_object_get_data (G_OBJECT (shell), "__session_loading");
976 		if (child != session) continue;
977 		g_signal_emit_by_name (G_OBJECT (shell), "load_session",
978 							   ANJUTA_SESSION_PHASE_NORMAL, session);
979 		child = g_object_get_data (G_OBJECT (shell), "__session_loading");
980 		if (child != session) continue;
981 		g_signal_emit_by_name (G_OBJECT (shell), "load_session",
982  							   ANJUTA_SESSION_PHASE_LAST, session);
983 		child = g_object_get_data (G_OBJECT (shell), "__session_loading");
984 		if (child != session) continue;
985 		g_signal_emit_by_name (G_OBJECT (shell), "load_session",
986 							   ANJUTA_SESSION_PHASE_END, session);
987 		child = g_object_get_data (G_OBJECT (shell), "__session_loading");
988 		g_object_unref (session);
989 		if (child == session) break;
990 		session = child;
991 		child = NULL;
992 	}
993 	g_object_set_data (G_OBJECT (shell), "__session_loading", NULL);
994 }
995 
996 void
anjuta_shell_save_prompt(AnjutaShell * shell,AnjutaSavePrompt * save_prompt,GError ** error)997 anjuta_shell_save_prompt (AnjutaShell *shell,
998 						  AnjutaSavePrompt *save_prompt,
999 						  GError **error)
1000 {
1001 	g_return_if_fail (ANJUTA_IS_SHELL (shell));
1002 	g_return_if_fail (ANJUTA_IS_SAVE_PROMPT (save_prompt));
1003 	g_signal_emit_by_name (shell, "save-prompt", save_prompt);
1004 }
1005 
1006 static void
anjuta_shell_base_init(gpointer gclass)1007 anjuta_shell_base_init (gpointer gclass)
1008 {
1009 	static gboolean initialized = FALSE;
1010 
1011 	if (!initialized) {
1012 		g_signal_new ("value-added",
1013 			      ANJUTA_TYPE_SHELL,
1014 			      G_SIGNAL_RUN_LAST,
1015 			      G_STRUCT_OFFSET (AnjutaShellIface, value_added),
1016 			      NULL, NULL,
1017 			      anjuta_cclosure_marshal_VOID__STRING_BOXED,
1018 			      G_TYPE_NONE, 2,
1019 			      G_TYPE_STRING, G_TYPE_VALUE);
1020 
1021 		g_signal_new ("value-removed",
1022 			      ANJUTA_TYPE_SHELL,
1023 			      G_SIGNAL_RUN_LAST,
1024 			      G_STRUCT_OFFSET (AnjutaShellIface, value_removed),
1025 			      NULL, NULL,
1026 			      anjuta_cclosure_marshal_VOID__STRING,
1027 			      G_TYPE_NONE, 1,
1028 			      G_TYPE_STRING);
1029 		g_signal_new ("save-session",
1030 			      ANJUTA_TYPE_SHELL,
1031 			      G_SIGNAL_RUN_LAST,
1032 			      G_STRUCT_OFFSET (AnjutaShellIface, save_session),
1033 			      NULL, NULL,
1034 			      anjuta_cclosure_marshal_VOID__INT_OBJECT,
1035 			      G_TYPE_NONE, 2,
1036 				  G_TYPE_INT,
1037 			      G_TYPE_OBJECT);
1038 		g_signal_new ("load-session",
1039 			      ANJUTA_TYPE_SHELL,
1040 			      G_SIGNAL_RUN_LAST,
1041 			      G_STRUCT_OFFSET (AnjutaShellIface, load_session),
1042 			      NULL, NULL,
1043 			      anjuta_cclosure_marshal_VOID__INT_OBJECT,
1044 			      G_TYPE_NONE, 2,
1045 				  G_TYPE_INT,
1046 			      G_TYPE_OBJECT);
1047 		g_signal_new ("save-prompt",
1048 			      ANJUTA_TYPE_SHELL,
1049 			      G_SIGNAL_RUN_LAST,
1050 			      G_STRUCT_OFFSET (AnjutaShellIface, save_prompt),
1051 			      NULL, NULL,
1052 			      anjuta_cclosure_marshal_VOID__OBJECT,
1053 			      G_TYPE_NONE, 1,
1054 			      G_TYPE_OBJECT);
1055 		initialized = TRUE;
1056 	}
1057 }
1058 
1059 GType
anjuta_shell_get_type(void)1060 anjuta_shell_get_type (void)
1061 {
1062 	static GType type = 0;
1063 
1064 	if (!type) {
1065 		static const GTypeInfo info = {
1066 			sizeof (AnjutaShellIface),
1067 			anjuta_shell_base_init,
1068 			NULL,
1069 			NULL,
1070 			NULL,
1071 			NULL,
1072 			0,
1073 			0,
1074 			NULL
1075 		};
1076 
1077 		type = g_type_register_static (G_TYPE_INTERFACE,
1078 					       "AnjutaShell",
1079 					       &info,
1080 					       0);
1081 
1082 		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
1083 	}
1084 
1085 	return type;
1086 }
1087