Fixes NULL pointer dereference when closing tabs.
[anjuta.git] / libanjuta / anjuta-plugin.c
blob44dd51add022dea4194d434e3433a15c7ce2e522
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * Anjuta
4 * Copyright 2000 Dave Camp, 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.
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.
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,
19 * Boston, MA 02110-1301, USA.
22 /**
23 * SECTION:anjuta-plugin
24 * @short_description: Anjuta plugin base class from which all plugins are
25 * derived.
26 * @see_also: #AnjutaPluginManager, #AnjutaProfileManager
27 * @stability: Unstable
28 * @include: libanjuta/anjuta-plugin.h
30 * Anjuta plugins are components which are loaded in Anjuta IDE shell
31 * either on startup or on demand to perform various subtasks. Plugins are
32 * specialized in doing only a very specific task and can let other plugins
33 * interract with it using interfaces.
35 * A plugin class is derived from #AnjutaPlugin class and will be used by
36 * shell to instanciate any number of plugin objects.
38 * When a plugin class is derived from #AnjutaPlugin, the virtual mehtods
39 * <emphasis>activate</emphasis> and <emphasis>deactivate</emphasis> must
40 * be implemented. The <emphasis>activate</emphasis> method is used to
41 * activate the plugin. Note that plugin activation is different from plugin
42 * instance initialization. Instance initialization is use to do internal
43 * initialization, while <emphasis>activate</emphasis> method is used to
44 * setup the plugin in shell, UI and preferences. Other plugins can also
45 * be queried in <emphasis>activate</emphasis> method.
47 * Following things should be done in <emphasis>activate</emphasis> method.
48 * <orderedlist>
49 * <listitem>
50 * <para>
51 * Register UI Actions: Use anjuta_ui_add_action_group_entries() or
52 * anjuta_ui_add_toggle_action_group_entries() to add your action
53 * groups.
54 * </para>
55 * </listitem>
56 * <listitem>
57 * <para>
58 * Merge UI definition file: Use anjuta_ui_merge() to merge a UI
59 * file. See #AnjutaUI for more detail.
60 * </para>
61 * </listitem>
62 * <listitem>
63 * <para>
64 * Add widgets to Anjuta Shell: If the plugin has one or more
65 * widgets as its User Interface, they can be added with
66 * anjuta_shell_add_widget().
67 * </para>
68 * </listitem>
69 * <listitem>
70 * <para>
71 * Setup value watches with anjuta_plugin_add_watch().
72 * </para>
73 * </listitem>
74 * </orderedlist>
76 * <emphasis>deactivate</emphasis> method undos all the above. That is, it
77 * removes widgets from the shell, unmerges UI and removes the action groups.
79 * Plugins interact with each other using interfaces. A plugin can expose an
80 * interface which will let other plugins find it. Any number of interfaces can
81 * be exposed by a plugin. These exposed interfaces are called
82 * <emphasis>Primary</emphasis> interfaces of the plugin. The condition for
83 * the interfaces to be primary is that they should be independent (i.e.
84 * an external entity requesting to use a primary interface should not require
85 * other primary interfaces). For example, an editor plugin can implement
86 * #IAnjutaEditor, #IAnjutaStream and #IAnjutaFile interfaces and expose them
87 * as primary interfaces, because they are independent.
88 * <emphasis>Primary</emphasis> interfaces exposed by a plugin are exported in
89 * its plugin meta-data file, so that plugin manager could register them.
91 * Any other interfaces implemented by the plugin are called
92 * <emphasis>Secondary</emphasis> interfaces and they generally depend on
93 * one or more primary interfaces.
94 * For example, #IAnjutaEditor is the primary interface of anjuta-editor plugin,
95 * but it also implements secondary interfaces #IAnjutaEditorGutter and
96 * #IAnjutaEditorBuffer. Notice that secondary interfaces #IAnjutaEditorGutter
97 * and #IAnjutaEditorBuffer depend on #IAnjutaEditor interface.
99 * The purpose of distinguishing between primary and
100 * secondary interfaces is only at plugin level. At code level, they behave
101 * just the same and there is no difference.
102 * So, a natural sequence for a plugin to communicate with another plugin is:
103 * <orderedlist>
104 * <listitem>
105 * <para>
106 * Query the shell for a plugin implemeting the primary interface
107 * using anjuta_shell_get_interface(). It will return an
108 * implemetation of the interface (or NULL if not found).
109 * Do not save this object for longer use, because the implementor
110 * plugin can change anytime and a different plugin implementing
111 * the same primary interface may be activated.
112 * <programlisting>
113 * GError *err = NULL;
114 * IAnjutaDocumentManager *docman;
115 * IAnjutaEditor *editor;
117 * docman = anjuta_shell_get_interface (ANJUTA_PLUGIN(plugin)->shell,
118 * IAnjutaDocumentManager, &amp;err);
119 * if (err)
121 * g_warning ("Error encountered: %s", err->message);
122 * g_error_free (err);
123 * return;
126 * editor = ianjuta_document_manager_get_current_editor (docman, &amp;err);
127 * if (err)
129 * g_warning ("Error encountered: %s", err->message);
130 * g_error_free (err);
131 * return;
134 * ianjuta_editor_goto_line (editor, 200);
135 * ...
136 * </programlisting>
137 * </para>
138 * </listitem>
139 * <listitem>
140 * <para>
141 * A primary interface of a plugin can be directly used, but
142 * to use a secondary interface, make sure to check if the plugin
143 * object implements it. For example, to check if editor plugin
144 * implements IAnjutaEditorGutter interface, do something like:
145 * <programlisting>
147 * if (IANJUTA_IS_EDITOR_GUTTER(editor))
149 * ianjuta_editor_gutter_set_marker(IANJUTA_EDITOR_GUTTER (editor),
150 * ANJUTA_EDITOR_MARKER_1,
151 * line_number, &amp;err);
153 * </programlisting>
154 * </para>
155 * </listitem>
156 * </orderedlist>
158 * Plugins can also communicate with outside using Shell's <emphasis>Values
159 * System</emphasis>. Values are objects exported by plugins to make them
160 * available to other plugins. Read #AnjutaShell documentation for more
161 * detail on <emphasis>Values System</emphasis>. A plugin can set up watches
162 * with anjuta_plugin_add_watch() to get notifications for values exported
163 * by other plugins.
165 * Values are very unreliable way of passing objects between plugins, but are
166 * nevertheless very useful (and quicker to code). It must be used with care.
167 * As a rule of thumb, a plugin should only watch values of other trusted
168 * plugins. For example, a group of plugins forming a subsystem can comfortably
169 * use values to pass objects and notifications. Use anjuta_plugin_add_watch()
170 * and anjuta_plugin_remove_watch() to add or remove value watches.
173 #include <config.h>
174 #include <string.h>
175 #include <libanjuta/anjuta-marshal.h>
176 #include <libanjuta/interfaces/ianjuta-preferences.h>
177 #include "anjuta-plugin.h"
179 typedef struct
181 guint id;
182 char *name;
183 AnjutaPluginValueAdded added;
184 AnjutaPluginValueRemoved removed;
185 gboolean need_remove;
186 gpointer user_data;
187 } Watch;
189 struct _AnjutaPluginPrivate {
190 guint watch_num;
192 int added_signal_id;
193 int removed_signal_id;
195 GList *watches;
197 gboolean activated;
200 enum {
201 PROP_0,
202 PROP_SHELL,
205 enum
207 ACTIVATED_SIGNAL,
208 DEACTIVATED_SIGNAL,
209 LAST_SIGNAL
212 static guint plugin_signals[LAST_SIGNAL] = { 0 };
214 static void anjuta_plugin_finalize (GObject *object);
215 static void anjuta_plugin_class_init (AnjutaPluginClass *class);
217 G_DEFINE_TYPE (AnjutaPlugin, anjuta_plugin, G_TYPE_OBJECT);
219 static void
220 destroy_watch (Watch *watch)
222 g_free (watch->name);
223 g_free (watch);
226 static void
227 anjuta_plugin_dispose (GObject *object)
229 AnjutaPlugin *plugin = ANJUTA_PLUGIN (object);
231 if (plugin->priv->watches) {
232 GList *l;
234 for (l = plugin->priv->watches; l != NULL; l = l->next) {
235 Watch *watch = (Watch *)l->data;
237 if (watch->removed && watch->need_remove) {
238 watch->removed (plugin,
239 watch->name,
240 watch->user_data);
243 destroy_watch (watch);
245 g_list_free (plugin->priv->watches);
246 plugin->priv->watches = NULL;
249 if (plugin->shell) {
250 if (plugin->priv->added_signal_id) {
251 g_signal_handler_disconnect (G_OBJECT (plugin->shell),
252 plugin->priv->added_signal_id);
253 g_signal_handler_disconnect (G_OBJECT (plugin->shell),
254 plugin->priv->removed_signal_id);
255 plugin->priv->added_signal_id = 0;
256 plugin->priv->removed_signal_id = 0;
258 g_object_unref (plugin->shell);
259 plugin->shell = NULL;
263 static void
264 anjuta_plugin_finalize (GObject *object)
266 AnjutaPlugin *plugin = ANJUTA_PLUGIN (object);
267 g_free (plugin->priv);
270 static void
271 anjuta_plugin_get_property (GObject *object,
272 guint param_id,
273 GValue *value,
274 GParamSpec *pspec)
276 AnjutaPlugin *plugin = ANJUTA_PLUGIN (object);
278 switch (param_id) {
279 case PROP_SHELL:
280 g_value_set_object (value, plugin->shell);
281 break;
282 default :
283 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
284 break;
288 static void
289 anjuta_plugin_set_property (GObject *object,
290 guint param_id,
291 const GValue *value,
292 GParamSpec *pspec)
294 AnjutaPlugin *plugin = ANJUTA_PLUGIN (object);
296 switch (param_id) {
297 case PROP_SHELL:
298 g_return_if_fail (plugin->shell == NULL);
299 plugin->shell = g_value_get_object (value);
300 g_object_ref (plugin->shell);
301 g_object_notify (object, "shell");
302 break;
303 default :
304 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
305 break;
309 static void
310 anjuta_plugin_class_init (AnjutaPluginClass *class)
312 GObjectClass *object_class = (GObjectClass*) class;
314 object_class->dispose = anjuta_plugin_dispose;
315 object_class->finalize = anjuta_plugin_finalize;
316 object_class->get_property = anjuta_plugin_get_property;
317 object_class->set_property = anjuta_plugin_set_property;
319 class->activate = NULL;
320 class->deactivate = NULL;
323 * AnjutaPlugin:shell:
325 * The #AnjutaShell object associated with this plugin
327 g_object_class_install_property
328 (object_class,
329 PROP_SHELL,
330 g_param_spec_object ("shell",
331 _("Anjuta Shell"),
332 _("Anjuta shell that will contain the plugin"),
333 ANJUTA_TYPE_SHELL,
334 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
335 plugin_signals[ACTIVATED_SIGNAL] =
336 g_signal_new ("activated",
337 G_TYPE_FROM_CLASS (object_class),
338 G_SIGNAL_RUN_FIRST,
339 G_STRUCT_OFFSET (AnjutaPluginClass,
340 activated),
341 NULL, NULL,
342 anjuta_cclosure_marshal_VOID__VOID,
343 G_TYPE_NONE, 0);
345 plugin_signals[ACTIVATED_SIGNAL] =
346 g_signal_new ("deactivated",
347 G_TYPE_FROM_CLASS (object_class),
348 G_SIGNAL_RUN_FIRST,
349 G_STRUCT_OFFSET (AnjutaPluginClass,
350 deactivated),
351 NULL, NULL,
352 anjuta_cclosure_marshal_VOID__VOID,
353 G_TYPE_NONE, 0);
356 static void
357 anjuta_plugin_init (AnjutaPlugin *plugin)
359 plugin->priv = g_new0 (AnjutaPluginPrivate, 1);
362 static void
363 value_added_cb (AnjutaShell *shell,
364 const char *name,
365 const GValue *value,
366 gpointer user_data)
368 AnjutaPlugin *plugin = ANJUTA_PLUGIN (user_data);
369 GList *l;
371 for (l = plugin->priv->watches; l != NULL; l = l->next) {
372 Watch *watch = (Watch *)l->data;
373 if (!strcmp (watch->name, name)) {
374 if (watch->added) {
375 watch->added (plugin,
376 name,
377 value,
378 watch->user_data);
381 watch->need_remove = TRUE;
386 static void
387 value_removed_cb (AnjutaShell *shell,
388 const char *name,
389 gpointer user_data)
391 AnjutaPlugin *plugin = ANJUTA_PLUGIN (user_data);
392 GList *l;
394 for (l = plugin->priv->watches; l != NULL; l = l->next) {
395 Watch *watch = (Watch *)l->data;
396 if (!strcmp (watch->name, name)) {
397 if (watch->removed) {
398 watch->removed (plugin, name, watch->user_data);
400 if (!watch->need_remove) {
401 g_warning ("watch->need_remove FALSE when it should be TRUE");
404 watch->need_remove = FALSE;
410 * anjuta_plugin_add_watch:
411 * @plugin: a #AnjutaPlugin derived class object.
412 * @name: Name of the value to watch.
413 * @added: (closure user_data) (scope call): Callback to call when the value is added.
414 * @removed: (closure user_data) (scope call): Callback to call when the value is removed.
415 * @user_data: User data to pass to callbacks.
417 * Adds a watch for @name value. When the value is added in shell, the @added
418 * callback will be called and when it is removed, the @removed callback will
419 * be called. The returned ID is used to remove the watch later.
421 * Return value: Watch ID.
423 guint
424 anjuta_plugin_add_watch (AnjutaPlugin *plugin,
425 const gchar *name,
426 AnjutaPluginValueAdded added,
427 AnjutaPluginValueRemoved removed,
428 gpointer user_data)
430 Watch *watch;
431 GValue value = {0, };
432 GError *error = NULL;
434 g_return_val_if_fail (plugin != NULL, -1);
435 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin), -1);
436 g_return_val_if_fail (name != NULL, -1);
438 watch = g_new0 (Watch, 1);
440 watch->id = ++plugin->priv->watch_num;
441 watch->name = g_strdup (name);
442 watch->added = added;
443 watch->removed = removed;
444 watch->need_remove = FALSE;
445 watch->user_data = user_data;
447 plugin->priv->watches = g_list_prepend (plugin->priv->watches, watch);
449 anjuta_shell_get_value (plugin->shell, name, &value, &error);
450 if (!error) {
451 if (added) {
452 watch->added (plugin, name, &value, user_data);
453 g_value_unset (&value);
455 watch->need_remove = TRUE;
456 } else {
457 /* g_warning ("Error in getting value '%s': %s", name, error->message); */
458 g_error_free (error);
461 if (!plugin->priv->added_signal_id) {
462 plugin->priv->added_signal_id =
463 g_signal_connect (plugin->shell,
464 "value_added",
465 G_CALLBACK (value_added_cb),
466 plugin);
468 plugin->priv->removed_signal_id =
469 g_signal_connect (plugin->shell,
470 "value_removed",
471 G_CALLBACK (value_removed_cb),
472 plugin);
475 return watch->id;
479 * anjuta_plugin_remove_watch:
480 * @plugin: a #AnjutaPlugin derived class object.
481 * @id: Watch id to remove.
482 * @send_remove: If true, calls value_removed callback.
484 * Removes the watch represented by @id (which was returned by
485 * anjuta_plugin_add_watch()).
487 void
488 anjuta_plugin_remove_watch (AnjutaPlugin *plugin, guint id,
489 gboolean send_remove)
491 GList *l;
492 Watch *watch = NULL;
494 g_return_if_fail (plugin != NULL);
495 g_return_if_fail (ANJUTA_IS_PLUGIN (plugin));
497 for (l = plugin->priv->watches; l != NULL; l = l->next) {
498 watch = l->data;
500 if (watch->id == id) {
501 break;
505 if (!watch) {
506 g_warning ("Attempted to remove non-existant watch %d\n", id);
507 return;
510 if (send_remove && watch->need_remove && watch->removed) {
511 watch->removed (plugin, watch->name, watch->user_data);
514 plugin->priv->watches = g_list_remove (plugin->priv->watches, watch);
515 destroy_watch (watch);
519 * anjuta_plugin_activate:
520 * @plugin: a #AnjutaPlugin derived class object.
522 * Activates the plugin by calling activate() virtual method. All plugins
523 * should derive their classes from this virtual class and implement this
524 * method.
525 * If the plugin implements IAnjutaPreferences, it is prompted to install
526 * it's preferences.
528 * Return value: TRUE if sucessfully activated, FALSE otherwise.
530 gboolean
531 anjuta_plugin_activate (AnjutaPlugin *plugin)
533 AnjutaPluginClass *klass;
535 g_return_val_if_fail (plugin != NULL, FALSE);
536 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin), FALSE);
537 g_return_val_if_fail (plugin->priv->activated == FALSE, FALSE);
539 klass = ANJUTA_PLUGIN_GET_CLASS(plugin);
540 g_return_val_if_fail (klass->activate != NULL, FALSE);
542 plugin->priv->activated = klass->activate(plugin);
544 if (plugin->priv->activated)
545 g_signal_emit_by_name (G_OBJECT (plugin), "activated");
547 return plugin->priv->activated;
551 * anjuta_plugin_deactivate:
552 * @plugin: a #AnjutaPlugin derived class object.
554 * Deactivates the plugin by calling deactivate() virtual method. All plugins
555 * should derive their classes from this virtual class and implement this
556 * method.
558 * Return value: TRUE if sucessfully activated, FALSE otherwise.
560 gboolean
561 anjuta_plugin_deactivate (AnjutaPlugin *plugin)
563 AnjutaPluginClass *klass;
564 gboolean success;
566 g_return_val_if_fail (plugin != NULL, FALSE);
567 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin), FALSE);
568 g_return_val_if_fail (plugin->priv->activated == TRUE, FALSE);
570 klass = ANJUTA_PLUGIN_GET_CLASS(plugin);
571 g_return_val_if_fail (klass->deactivate != NULL, FALSE);
573 success = klass->deactivate(plugin);
574 plugin->priv->activated = !success;
575 if (!plugin->priv->activated)
576 g_signal_emit_by_name (G_OBJECT (plugin), "deactivated");
577 return success;
581 * anjuta_plugin_is_active:
582 * @plugin: a #AnjutaPlugin derived class object.
584 * Returns TRUE if the plugin has been activated.
586 * Return value: TRUE if activated, FALSE otherwise.
588 gboolean
589 anjuta_plugin_is_active (AnjutaPlugin *plugin)
591 return plugin->priv->activated;
595 * anjuta_plugin_get_shell:
596 * @plugin: a #AnjutaPlugin
598 * Returns: The #AnjutaShell object associated with this plugin
600 AnjutaShell *
601 anjuta_plugin_get_shell (AnjutaPlugin* plugin)
603 return plugin->shell;