project-wizard: Remove trailing spaces
[anjuta.git] / plugins / language-support-python / plugin.c
blobb2b7bdee63d415800cec89c1458d35eb3d1d49cb
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * plugin.c
4 * Copyright (C) Ishan Chattopadhyaya 2009 <ichattopadhyaya@gmail.com>
6 * plugin.c is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * plugin.c is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with plugin.c. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include <config.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <libanjuta/anjuta-shell.h>
29 #include <libanjuta/anjuta-debug.h>
30 #include <libanjuta/anjuta-launcher.h>
31 #include <libanjuta/anjuta-preferences.h>
32 #include <libanjuta/anjuta-utils.h>
33 #include <libanjuta/interfaces/ianjuta-iterable.h>
34 #include <libanjuta/interfaces/ianjuta-document.h>
35 #include <libanjuta/interfaces/ianjuta-document-manager.h>
36 #include <libanjuta/interfaces/ianjuta-editor.h>
37 #include <libanjuta/interfaces/ianjuta-editor-cell.h>
38 #include <libanjuta/interfaces/ianjuta-editor-language.h>
39 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
40 #include <libanjuta/interfaces/ianjuta-editor-assist.h>
41 #include <libanjuta/interfaces/ianjuta-editor-glade-signal.h>
42 #include <libanjuta/interfaces/ianjuta-preferences.h>
43 #include <libanjuta/interfaces/ianjuta-symbol.h>
44 #include <libanjuta/interfaces/ianjuta-language.h>
45 #include <libanjuta/interfaces/ianjuta-indenter.h>
47 #include "plugin.h"
48 #include "python-assist.h"
50 #define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-language-support-python.xml"
51 #define PROPERTIES_FILE_UI PACKAGE_DATA_DIR"/glade/anjuta-language-support-python.ui"
52 #define ICON_FILE "anjuta-language-support-python-plugin.png"
54 /* Preferences keys */
56 #define ANJUTA_PREF_SCHEMA_PREFIX "org.gnome.anjuta."
57 #define PREF_SCHEMA "org.gnome.anjuta.plugins.python"
60 #define PREF_NO_ROPE_WARNING "no-rope-warning"
61 #define PREF_INTERPRETER_PATH "interpreter-path"
63 static gpointer parent_class;
65 static void
66 on_check_finished (AnjutaLauncher* launcher,
67 int child_pid, int exit_status,
68 gulong time, gpointer user_data)
70 PythonPlugin* plugin = ANJUTA_PLUGIN_PYTHON (user_data);
71 if (exit_status != 0)
73 GtkWidget* dialog = gtk_dialog_new_with_buttons (_("Python support warning"),
74 NULL,
75 GTK_DIALOG_MODAL,
76 GTK_STOCK_OK,
77 GTK_RESPONSE_ACCEPT,
78 NULL);
79 GtkWidget* area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
81 GtkWidget* label = gtk_label_new (_("Either python path is wrong or python-rope (http://rope.sf.net) libraries\n"
82 "aren't installed. Both are required for autocompletion in python files.\n"
83 "Please install them and check the python path in the preferences."));
84 GtkWidget* check_button = gtk_check_button_new_with_label (_("Do not show that warning again"));
86 gtk_box_pack_start (GTK_BOX (area), label,
87 TRUE, TRUE, 5);
88 gtk_box_pack_start (GTK_BOX (area), check_button,
89 TRUE, TRUE, 5);
90 gtk_widget_show_all (dialog);
92 gtk_dialog_run (GTK_DIALOG(dialog));
94 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button)))
96 g_settings_get_boolean (plugin->settings,
97 PREF_NO_ROPE_WARNING);
99 gtk_widget_destroy (dialog);
101 g_object_unref (launcher);
104 static void
105 check_support (PythonPlugin *python_plugin)
107 if (!g_settings_get_boolean (python_plugin->settings,
108 PREF_NO_ROPE_WARNING))
110 AnjutaLauncher* launcher = anjuta_launcher_new ();
111 gchar* python_path = g_settings_get_string (python_plugin->settings,
112 PREF_INTERPRETER_PATH);
113 gchar* command = g_strdup_printf ("%s -c \"import rope\"", python_path);
115 g_signal_connect (launcher, "child-exited",
116 G_CALLBACK(on_check_finished), python_plugin);
117 anjuta_launcher_execute (launcher, command, NULL, NULL);
119 g_free (python_path);
120 g_free (command);
124 /* Glade support */
125 static gchar*
126 language_support_check_param_name (const gchar* name,
127 GList** names)
129 gint index = 0;
130 GString* real_name = g_string_new (name);
131 while (g_list_find_custom (*names, real_name->str, (GCompareFunc) strcmp))
133 g_string_free (real_name, TRUE);
134 real_name = g_string_new (name);
135 g_string_append_printf (real_name, "%d", ++index);
137 *names = g_list_append (*names, real_name->str);
138 return g_string_free (real_name, FALSE);
141 static const gchar*
142 language_support_get_signal_parameter (const gchar* type_name, GList** names)
144 const gchar* c;
145 const gchar* param_name = NULL;
146 GString* param_string;
147 gchar* real_name;
148 /* Search for the second upper character */
149 for (c = type_name + 1; *c != '\0'; c++)
151 if (g_ascii_isupper (*c))
153 param_name = c;
154 break;
157 if (param_name && strlen (param_name))
159 param_string = g_string_new (param_name);
160 g_string_down (param_string);
162 else
164 param_string = g_string_new ("arg");
166 real_name = language_support_check_param_name (g_string_free (param_string, FALSE), names);
168 return real_name;
171 static void
172 on_glade_drop (IAnjutaEditor* editor,
173 IAnjutaIterable* iterator,
174 const gchar* signal_data,
175 PythonPlugin* lang_plugin)
177 GSignalQuery query;
178 GType type;
179 guint id;
181 const gchar* widget;
182 const gchar* signal;
183 const gchar* handler;
184 GList* names = NULL;
185 GString* str = g_string_new (NULL);
186 int i;
187 IAnjutaIterable* start, * end;
189 GStrv data = g_strsplit(signal_data, ":", 5);
191 widget = data[0];
192 signal = data[1];
193 handler = data[2];
195 type = g_type_from_name (widget);
196 id = g_signal_lookup (signal, type);
198 g_signal_query (id, &query);
200 g_string_append_printf (str, "\ndef %s (self, %s", handler,
201 language_support_get_signal_parameter (widget,
202 &names));
203 for (i = 0; i < query.n_params; i++)
205 const gchar* type_name = g_type_name (query.param_types[i]);
206 const gchar* param_name = language_support_get_signal_parameter (type_name,
207 &names);
209 g_string_append_printf (str, ", %s", param_name);
211 g_string_append (str, "):\n");
213 ianjuta_editor_insert (editor, iterator,
214 str->str, -1, NULL);
216 /* Indent code correctly */
217 start = iterator;
218 end = ianjuta_iterable_clone (iterator, NULL);
219 ianjuta_iterable_set_position (end,
220 ianjuta_iterable_get_position (iterator, NULL)
221 + g_utf8_strlen (str->str, -1),
222 NULL);
223 ianjuta_indenter_indent (IANJUTA_INDENTER (lang_plugin),
224 start, end, NULL);
225 g_object_unref (end);
227 g_string_free (str, TRUE);
228 anjuta_util_glist_strings_free (names);
230 g_strfreev (data);
233 static void
234 install_support (PythonPlugin *lang_plugin)
236 IAnjutaLanguage* lang_manager =
237 anjuta_shell_get_interface (ANJUTA_PLUGIN (lang_plugin)->shell,
238 IAnjutaLanguage, NULL);
239 IAnjutaSymbolManager* sym_manager =
240 anjuta_shell_get_interface (ANJUTA_PLUGIN (lang_plugin)->shell,
241 IAnjutaSymbolManager,
242 NULL);
244 if (!lang_manager || !sym_manager)
245 return;
247 if (lang_plugin->support_installed)
248 return;
250 lang_plugin->current_language =
251 ianjuta_language_get_name_from_editor (lang_manager,
252 IANJUTA_EDITOR_LANGUAGE (lang_plugin->current_editor), NULL);
254 if (!(lang_plugin->current_language &&
255 (g_str_equal (lang_plugin->current_language, "Python"))))
256 return;
258 /* Disable editor intern auto-indent */
259 ianjuta_editor_set_auto_indent (IANJUTA_EDITOR(lang_plugin->current_editor),
260 FALSE, NULL);
262 if (IANJUTA_IS_EDITOR_ASSIST (lang_plugin->current_editor) )
264 AnjutaPlugin *plugin;
265 IAnjutaEditor* ieditor;
267 const gchar *project_root;
269 check_support (lang_plugin);
271 plugin = ANJUTA_PLUGIN (lang_plugin);
272 ieditor = IANJUTA_EDITOR (lang_plugin->current_editor);
274 g_assert (lang_plugin->assist == NULL);
276 project_root = ANJUTA_PLUGIN_PYTHON(plugin)->project_root_directory;
278 lang_plugin->assist = python_assist_new (ieditor,
279 sym_manager,
280 lang_plugin->settings,
281 plugin,
282 project_root);
285 if (IANJUTA_IS_EDITOR_GLADE_SIGNAL (lang_plugin->current_editor))
287 g_signal_connect (lang_plugin->current_editor,
288 "drop-possible", G_CALLBACK (gtk_true), NULL);
289 g_signal_connect (lang_plugin->current_editor,
290 "drop", G_CALLBACK (on_glade_drop),
291 lang_plugin);
294 lang_plugin->support_installed = TRUE;
297 static void
298 uninstall_support (PythonPlugin *lang_plugin)
300 if (!lang_plugin->support_installed)
301 return;
303 if (lang_plugin->assist)
305 g_object_unref (lang_plugin->assist);
306 lang_plugin->assist = NULL;
309 if (IANJUTA_IS_EDITOR_GLADE_SIGNAL (lang_plugin->current_editor))
311 g_signal_handlers_disconnect_by_func (lang_plugin->current_editor,
312 gtk_true, NULL);
313 g_signal_handlers_disconnect_by_func (lang_plugin->current_editor,
314 on_glade_drop, lang_plugin);
317 lang_plugin->support_installed = FALSE;
320 static void
321 on_editor_language_changed (IAnjutaEditor *editor,
322 const gchar *new_language,
323 PythonPlugin *plugin)
325 uninstall_support (plugin);
326 install_support (plugin);
329 static void
330 on_editor_added (AnjutaPlugin *plugin, const gchar *name,
331 const GValue *value, gpointer data)
333 PythonPlugin *lang_plugin;
334 IAnjutaDocument* doc = IANJUTA_DOCUMENT(g_value_get_object (value));
335 lang_plugin = ANJUTA_PLUGIN_PYTHON(plugin);
338 if (IANJUTA_IS_EDITOR(doc))
340 lang_plugin->current_editor = G_OBJECT(doc);
342 else
344 lang_plugin->current_editor = NULL;
345 return;
347 if (lang_plugin->current_editor)
349 install_support (lang_plugin);
350 g_signal_connect (lang_plugin->current_editor, "language-changed",
351 G_CALLBACK (on_editor_language_changed),
352 plugin);
356 static void
357 on_editor_removed (AnjutaPlugin *plugin, const gchar *name,
358 gpointer data)
360 PythonPlugin *lang_plugin;
361 lang_plugin = ANJUTA_PLUGIN_PYTHON (plugin);
363 if (lang_plugin->current_editor)
364 g_signal_handlers_disconnect_by_func (lang_plugin->current_editor,
365 G_CALLBACK (on_editor_language_changed),
366 plugin);
368 uninstall_support (lang_plugin);
370 lang_plugin->current_editor = NULL;
371 lang_plugin->current_language = NULL;
374 static GtkActionEntry actions[] = {
376 "ActionMenuEdit",
377 NULL, N_("_Edit"),
378 NULL, NULL, NULL
382 static void
383 on_project_root_added (AnjutaPlugin *plugin, const gchar *name,
384 const GValue *value, gpointer user_data)
386 PythonPlugin *python_plugin;
387 gchar *project_root_uri;
388 GFile *file;
390 python_plugin = ANJUTA_PLUGIN_PYTHON (plugin);
392 g_free (python_plugin->project_root_directory);
393 project_root_uri = g_value_dup_string (value);
394 file = g_file_new_for_uri (project_root_uri);
395 python_plugin->project_root_directory = g_file_get_path (file);
396 g_object_unref (file);
397 g_free (project_root_uri);
400 static void
401 on_project_root_removed (AnjutaPlugin *plugin, const gchar *name,
402 gpointer user_data)
404 PythonPlugin *python_plugin;
406 python_plugin = ANJUTA_PLUGIN_PYTHON (plugin);
408 g_free (python_plugin->project_root_directory);
409 python_plugin->project_root_directory = NULL;
412 static gboolean
413 python_plugin_activate (AnjutaPlugin *plugin)
415 AnjutaUI *ui;
417 PythonPlugin *python_plugin;
419 python_plugin = (PythonPlugin*) plugin;
421 python_plugin->prefs = anjuta_shell_get_preferences (plugin->shell, NULL);
423 /* Add all UI actions and merge UI */
424 ui = anjuta_shell_get_ui (plugin->shell, NULL);
426 python_plugin->action_group =
427 anjuta_ui_add_action_group_entries (ui, "ActionGroupPythonAssist",
428 _("Python Assistance"),
429 actions,
430 G_N_ELEMENTS (actions),
431 GETTEXT_PACKAGE, TRUE,
432 plugin);
433 python_plugin->uiid = anjuta_ui_merge (ui, UI_FILE);
435 /* Add watches */
436 python_plugin->project_root_watch_id = anjuta_plugin_add_watch (plugin,
437 IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
438 on_project_root_added,
439 on_project_root_removed,
440 NULL);
442 python_plugin->editor_watch_id = anjuta_plugin_add_watch (plugin,
443 IANJUTA_DOCUMENT_MANAGER_CURRENT_DOCUMENT,
444 on_editor_added,
445 on_editor_removed,
446 NULL);
447 return TRUE;
450 static gboolean
451 python_plugin_deactivate (AnjutaPlugin *plugin)
454 AnjutaUI *ui;
455 PythonPlugin *lang_plugin;
456 lang_plugin = (PythonPlugin*) (plugin);
458 anjuta_plugin_remove_watch (plugin,
459 lang_plugin->editor_watch_id,
460 TRUE);
461 anjuta_plugin_remove_watch (plugin,
462 lang_plugin->project_root_watch_id,
463 TRUE);
466 ui = anjuta_shell_get_ui (plugin->shell, NULL);
467 anjuta_ui_remove_action_group (ui, ANJUTA_PLUGIN_PYTHON(plugin)->action_group);
468 anjuta_ui_unmerge (ui, ANJUTA_PLUGIN_PYTHON(plugin)->uiid);
470 return TRUE;
473 static void
474 python_plugin_finalize (GObject *obj)
476 /* Finalization codes here */
477 G_OBJECT_CLASS (parent_class)->finalize (obj);
480 static void
481 python_plugin_dispose (GObject *obj)
483 /* Disposition codes */
484 PythonPlugin *plugin = (PythonPlugin*)obj;
486 if (plugin->settings)
487 g_object_unref (plugin->settings);
488 plugin->settings = NULL;
489 if (plugin->editor_settings)
490 g_object_unref (plugin->editor_settings);
491 plugin->editor_settings = NULL;
493 G_OBJECT_CLASS (parent_class)->dispose (obj);
496 static void
497 python_plugin_instance_init (GObject *obj)
499 PythonPlugin *plugin = (PythonPlugin*)obj;
500 plugin->action_group = NULL;
501 plugin->current_editor = NULL;
502 plugin->current_language = NULL;
503 plugin->editor_watch_id = 0;
504 plugin->uiid = 0;
505 plugin->assist = NULL;
506 plugin->settings = g_settings_new (PREF_SCHEMA);
507 plugin->editor_settings = g_settings_new (ANJUTA_PREF_SCHEMA_PREFIX IANJUTA_EDITOR_PREF_SCHEMA);
510 static void
511 python_plugin_class_init (GObjectClass *klass)
513 AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
515 parent_class = g_type_class_peek_parent (klass);
517 plugin_class->activate = python_plugin_activate;
518 plugin_class->deactivate = python_plugin_deactivate;
519 klass->finalize = python_plugin_finalize;
520 klass->dispose = python_plugin_dispose;
523 #define PREF_WIDGET_SPACE "preferences:completion-space-after-func"
524 #define PREF_WIDGET_BRACE "preferences:completion-brace-after-func"
525 #define PREF_WIDGET_CLOSEBRACE "preferences:completion-closebrace-after-func"
526 #define PREF_WIDGET_AUTO "preferences:completion-enable"
528 static void
529 on_autocompletion_toggled (GtkToggleButton* button,
530 PythonPlugin* plugin)
532 GtkWidget* widget;
533 gboolean sensitive = gtk_toggle_button_get_active (button);
535 widget = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_SPACE));
536 gtk_widget_set_sensitive (widget, sensitive);
537 widget = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_BRACE));
538 gtk_widget_set_sensitive (widget, sensitive);
539 widget = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_CLOSEBRACE));
540 gtk_widget_set_sensitive (widget, sensitive);
543 static void
544 ipreferences_merge (IAnjutaPreferences* ipref, AnjutaPreferences* prefs,
545 GError** e)
547 /* Add preferences */
548 GError* error = NULL;
549 PythonPlugin* plugin = ANJUTA_PLUGIN_PYTHON (ipref);
550 plugin->bxml = gtk_builder_new ();
551 GtkWidget* toggle;
553 if (!gtk_builder_add_from_file (plugin->bxml, PROPERTIES_FILE_UI, &error))
555 g_warning ("Couldn't load builder file: %s", error->message);
556 g_error_free (error);
558 anjuta_preferences_add_from_builder (prefs,
559 plugin->bxml,
560 plugin->settings,
561 "preferences", _("Python"),
562 ICON_FILE);
563 toggle = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_AUTO));
564 g_signal_connect (toggle, "toggled", G_CALLBACK (on_autocompletion_toggled),
565 plugin);
566 on_autocompletion_toggled (GTK_TOGGLE_BUTTON (toggle), plugin);
569 static void
570 ipreferences_unmerge (IAnjutaPreferences* ipref, AnjutaPreferences* prefs,
571 GError** e)
573 PythonPlugin* plugin = ANJUTA_PLUGIN_PYTHON (ipref);
574 anjuta_preferences_remove_page(prefs, _("Python"));
575 g_object_unref (plugin->bxml);
578 static void
579 ipreferences_iface_init (IAnjutaPreferencesIface* iface)
581 iface->merge = ipreferences_merge;
582 iface->unmerge = ipreferences_unmerge;
585 ANJUTA_PLUGIN_BEGIN (PythonPlugin, python_plugin);
586 ANJUTA_PLUGIN_ADD_INTERFACE(ipreferences, IANJUTA_TYPE_PREFERENCES);
587 ANJUTA_PLUGIN_END;
589 ANJUTA_SIMPLE_PLUGIN (PythonPlugin, python_plugin);