1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright 2000 Dave Camp, Naba Kumar <naba@gnome.org>
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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 * SECTION:anjuta-plugin
24 * @short_description: Anjuta plugin base class from which all plugins are
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.
51 * Register UI Actions: Use anjuta_ui_add_action_group_entries() or
52 * anjuta_ui_add_toggle_action_group_entries() to add your action
58 * Merge UI definition file: Use anjuta_ui_merge() to merge a UI
59 * file. See #AnjutaUI for more detail.
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().
71 * Setup value watches with anjuta_plugin_add_watch().
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:
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.
113 * GError *err = NULL;
114 * IAnjutaDocumentManager *docman;
115 * IAnjutaEditor *editor;
117 * docman = anjuta_shell_get_interface (ANJUTA_PLUGIN(plugin)->shell,
118 * IAnjutaDocumentManager, &err);
121 * g_warning ("Error encountered: %s", err->message);
122 * g_error_free (err);
126 * editor = ianjuta_document_manager_get_current_editor (docman, &err);
129 * g_warning ("Error encountered: %s", err->message);
130 * g_error_free (err);
134 * ianjuta_editor_goto_line (editor, 200);
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:
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, &err);
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
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.
175 #include <libanjuta/anjuta-marshal.h>
176 #include <libanjuta/interfaces/ianjuta-preferences.h>
177 #include "anjuta-plugin.h"
183 AnjutaPluginValueAdded added
;
184 AnjutaPluginValueRemoved removed
;
185 gboolean need_remove
;
189 struct _AnjutaPluginPrivate
{
193 int removed_signal_id
;
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 GNOME_CLASS_BOILERPLATE (AnjutaPlugin
, anjuta_plugin
, GObject
, G_TYPE_OBJECT
);
220 destroy_watch (Watch
*watch
)
222 g_free (watch
->name
);
227 anjuta_plugin_dispose (GObject
*object
)
229 AnjutaPlugin
*plugin
= ANJUTA_PLUGIN (object
);
231 if (plugin
->priv
->watches
) {
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
,
243 destroy_watch (watch
);
245 g_list_free (plugin
->priv
->watches
);
246 plugin
->priv
->watches
= NULL
;
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
;
264 anjuta_plugin_finalize (GObject
*object
)
266 AnjutaPlugin
*plugin
= ANJUTA_PLUGIN (object
);
267 g_free (plugin
->priv
);
271 anjuta_plugin_get_property (GObject
*object
,
276 AnjutaPlugin
*plugin
= ANJUTA_PLUGIN (object
);
280 g_value_set_object (value
, plugin
->shell
);
283 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
289 anjuta_plugin_set_property (GObject
*object
,
294 AnjutaPlugin
*plugin
= ANJUTA_PLUGIN (object
);
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");
304 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
310 anjuta_plugin_class_init (AnjutaPluginClass
*class)
312 GObjectClass
*object_class
= (GObjectClass
*) class;
313 parent_class
= g_type_class_peek_parent (class);
315 object_class
->dispose
= anjuta_plugin_dispose
;
316 object_class
->finalize
= anjuta_plugin_finalize
;
317 object_class
->get_property
= anjuta_plugin_get_property
;
318 object_class
->set_property
= anjuta_plugin_set_property
;
320 class->activate
= NULL
;
321 class->deactivate
= NULL
;
323 g_object_class_install_property
326 g_param_spec_object ("shell",
328 _("Anjuta shell that will contain the plugin"),
330 G_PARAM_READWRITE
/* | G_PARAM_CONSTRUCT_ONLY*/)); /* Construct only does not work with libanjutamm */
331 plugin_signals
[ACTIVATED_SIGNAL
] =
332 g_signal_new ("activated",
333 G_TYPE_FROM_CLASS (object_class
),
335 G_STRUCT_OFFSET (AnjutaPluginClass
,
338 anjuta_cclosure_marshal_VOID__VOID
,
341 plugin_signals
[ACTIVATED_SIGNAL
] =
342 g_signal_new ("deactivated",
343 G_TYPE_FROM_CLASS (object_class
),
345 G_STRUCT_OFFSET (AnjutaPluginClass
,
348 anjuta_cclosure_marshal_VOID__VOID
,
353 anjuta_plugin_instance_init (AnjutaPlugin
*plugin
)
355 plugin
->priv
= g_new0 (AnjutaPluginPrivate
, 1);
359 value_added_cb (AnjutaShell
*shell
,
364 AnjutaPlugin
*plugin
= ANJUTA_PLUGIN (user_data
);
367 for (l
= plugin
->priv
->watches
; l
!= NULL
; l
= l
->next
) {
368 Watch
*watch
= (Watch
*)l
->data
;
369 if (!strcmp (watch
->name
, name
)) {
371 watch
->added (plugin
,
377 watch
->need_remove
= TRUE
;
383 value_removed_cb (AnjutaShell
*shell
,
387 AnjutaPlugin
*plugin
= ANJUTA_PLUGIN (user_data
);
390 for (l
= plugin
->priv
->watches
; l
!= NULL
; l
= l
->next
) {
391 Watch
*watch
= (Watch
*)l
->data
;
392 if (!strcmp (watch
->name
, name
)) {
393 if (watch
->removed
) {
394 watch
->removed (plugin
, name
, watch
->user_data
);
396 if (!watch
->need_remove
) {
397 g_warning ("watch->need_remove FALSE when it should be TRUE");
400 watch
->need_remove
= FALSE
;
406 * anjuta_plugin_add_watch:
407 * @plugin: a #AnjutaPlugin derived class object.
408 * @name: Name of the value to watch.
409 * @added: Callback to call when the value is added.
410 * @removed: Callback to call when the value is removed.
411 * @user_data: User data to pass to callbacks.
413 * Adds a watch for @name value. When the value is added in shell, the @added
414 * callback will be called and when it is removed, the @removed callback will
415 * be called. The returned ID is used to remove the watch later.
417 * Return value: Watch ID.
420 anjuta_plugin_add_watch (AnjutaPlugin
*plugin
,
422 AnjutaPluginValueAdded added
,
423 AnjutaPluginValueRemoved removed
,
427 GValue value
= {0, };
428 GError
*error
= NULL
;
430 g_return_val_if_fail (plugin
!= NULL
, -1);
431 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin
), -1);
432 g_return_val_if_fail (name
!= NULL
, -1);
434 watch
= g_new0 (Watch
, 1);
436 watch
->id
= ++plugin
->priv
->watch_num
;
437 watch
->name
= g_strdup (name
);
438 watch
->added
= added
;
439 watch
->removed
= removed
;
440 watch
->need_remove
= FALSE
;
441 watch
->user_data
= user_data
;
443 plugin
->priv
->watches
= g_list_prepend (plugin
->priv
->watches
, watch
);
445 anjuta_shell_get_value (plugin
->shell
, name
, &value
, &error
);
448 watch
->added (plugin
, name
, &value
, user_data
);
449 g_value_unset (&value
);
451 watch
->need_remove
= TRUE
;
453 /* g_warning ("Error in getting value '%s': %s", name, error->message); */
454 g_error_free (error
);
457 if (!plugin
->priv
->added_signal_id
) {
458 plugin
->priv
->added_signal_id
=
459 g_signal_connect (plugin
->shell
,
461 G_CALLBACK (value_added_cb
),
464 plugin
->priv
->removed_signal_id
=
465 g_signal_connect (plugin
->shell
,
467 G_CALLBACK (value_removed_cb
),
475 * anjuta_plugin_remove_watch:
476 * @plugin: a #AnjutaPlugin derived class object.
477 * @id: Watch id to remove.
478 * @send_remove: If true, calls value_removed callback.
480 * Removes the watch represented by @id (which was returned by
481 * anjuta_plugin_add_watch()).
484 anjuta_plugin_remove_watch (AnjutaPlugin
*plugin
, guint id
,
485 gboolean send_remove
)
490 g_return_if_fail (plugin
!= NULL
);
491 g_return_if_fail (ANJUTA_IS_PLUGIN (plugin
));
493 for (l
= plugin
->priv
->watches
; l
!= NULL
; l
= l
->next
) {
496 if (watch
->id
== id
) {
502 g_warning ("Attempted to remove non-existant watch %d\n", id
);
506 if (send_remove
&& watch
->need_remove
&& watch
->removed
) {
507 watch
->removed (plugin
, watch
->name
, watch
->user_data
);
510 plugin
->priv
->watches
= g_list_remove (plugin
->priv
->watches
, watch
);
511 destroy_watch (watch
);
515 * anjuta_plugin_activate:
516 * @plugin: a #AnjutaPlugin derived class object.
518 * Activates the plugin by calling activate() virtual method. All plugins
519 * should derive their classes from this virtual class and implement this
521 * If the plugin implements IAnjutaPreferences, it is prompted to install
524 * Return value: TRUE if sucessfully activated, FALSE otherwise.
527 anjuta_plugin_activate (AnjutaPlugin
*plugin
)
529 AnjutaPluginClass
*klass
;
531 g_return_val_if_fail (plugin
!= NULL
, FALSE
);
532 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin
), FALSE
);
533 g_return_val_if_fail (plugin
->priv
->activated
== FALSE
, FALSE
);
535 klass
= ANJUTA_PLUGIN_GET_CLASS(plugin
);
536 g_return_val_if_fail (klass
->activate
!= NULL
, FALSE
);
538 plugin
->priv
->activated
= klass
->activate(plugin
);
540 if (plugin
->priv
->activated
)
542 if (IANJUTA_IS_PREFERENCES(plugin
))
543 ianjuta_preferences_merge(IANJUTA_PREFERENCES(plugin
),
544 ANJUTA_PREFERENCES(anjuta_shell_get_preferences(plugin
->shell
,
546 g_signal_emit_by_name (G_OBJECT (plugin
), "activated");
548 return plugin
->priv
->activated
;
552 * anjuta_plugin_deactivate:
553 * @plugin: a #AnjutaPlugin derived class object.
555 * Deactivates the plugin by calling deactivate() virtual method. All plugins
556 * should derive their classes from this virtual class and implement this
559 * Return value: TRUE if sucessfully activated, FALSE otherwise.
562 anjuta_plugin_deactivate (AnjutaPlugin
*plugin
)
564 AnjutaPluginClass
*klass
;
567 g_return_val_if_fail (plugin
!= NULL
, FALSE
);
568 g_return_val_if_fail (ANJUTA_IS_PLUGIN (plugin
), FALSE
);
569 g_return_val_if_fail (plugin
->priv
->activated
== TRUE
, FALSE
);
571 klass
= ANJUTA_PLUGIN_GET_CLASS(plugin
);
572 g_return_val_if_fail (klass
->deactivate
!= NULL
, FALSE
);
574 if (IANJUTA_IS_PREFERENCES(plugin
))
575 ianjuta_preferences_unmerge(IANJUTA_PREFERENCES(plugin
),
576 ANJUTA_PREFERENCES(anjuta_shell_get_preferences(plugin
->shell
, NULL
)), NULL
);
578 success
= klass
->deactivate(plugin
);
579 plugin
->priv
->activated
= !success
;
580 if (!plugin
->priv
->activated
)
581 g_signal_emit_by_name (G_OBJECT (plugin
), "deactivated");
586 * anjuta_plugin_is_active:
587 * @plugin: a #AnjutaPlugin derived class object.
589 * Returns TRUE if the plugin has been activated.
591 * Return value: TRUE if activated, FALSE otherwise.
594 anjuta_plugin_is_active (AnjutaPlugin
*plugin
)
596 return plugin
->priv
->activated
;