[anjuta] snippet-manager: Added the plugin
- From: Johannes Schmid <jhs src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [anjuta] snippet-manager: Added the plugin
- Date: Mon, 16 Aug 2010 20:05:18 +0000 (UTC)
commit ad9a3ca1ce4326280be83031dccc250ddb2bc3d3
Author: Dragos Dena <dragos dena gmail com>
Date: Wed Aug 11 00:23:29 2010 +0300
snippet-manager: Added the plugin
plugins/snippets-manager/Makefile.am | 80 +
.../anjuta-snippets-manager.plugin.in | 6 +
.../snippets-manager/anjuta-snippets-manager.png | Bin 0 -> 6496 bytes
plugins/snippets-manager/plugin.c | 971 +++++++++
plugins/snippets-manager/plugin.h | 92 +
plugins/snippets-manager/snippet-variables-store.c | 719 +++++++
plugins/snippets-manager/snippet-variables-store.h | 110 +
plugins/snippets-manager/snippet.c | 1226 ++++++++++++
plugins/snippets-manager/snippet.h | 124 ++
plugins/snippets-manager/snippets-browser.c | 1190 +++++++++++
plugins/snippets-manager/snippets-browser.h | 81 +
plugins/snippets-manager/snippets-browser.ui | 120 ++
plugins/snippets-manager/snippets-db.c | 2111 ++++++++++++++++++++
plugins/snippets-manager/snippets-db.h | 164 ++
plugins/snippets-manager/snippets-editor.c | 2012 +++++++++++++++++++
plugins/snippets-manager/snippets-editor.h | 72 +
plugins/snippets-manager/snippets-editor.ui | 575 ++++++
.../snippets-manager/snippets-global-variables.xml | 14 +
plugins/snippets-manager/snippets-group.c | 286 +++
plugins/snippets-manager/snippets-group.h | 73 +
plugins/snippets-manager/snippets-import-export.c | 103 +
plugins/snippets-manager/snippets-import-export.h | 28 +
.../snippets-interaction-interpreter.c | 925 +++++++++
.../snippets-interaction-interpreter.h | 72 +
.../snippets-manager-preferences.ui | 106 +
plugins/snippets-manager/snippets-manager-ui.xml | 16 +
plugins/snippets-manager/snippets-provider.c | 739 +++++++
plugins/snippets-manager/snippets-provider.h | 70 +
plugins/snippets-manager/snippets-xml-parser.c | 840 ++++++++
plugins/snippets-manager/snippets-xml-parser.h | 36 +
plugins/snippets-manager/snippets.anjuta-snippets | 464 +++++
31 files changed, 13425 insertions(+), 0 deletions(-)
---
diff --git a/plugins/snippets-manager/Makefile.am b/plugins/snippets-manager/Makefile.am
new file mode 100644
index 0000000..41dc50e
--- /dev/null
+++ b/plugins/snippets-manager/Makefile.am
@@ -0,0 +1,80 @@
+# Plugin glade file
+# Snippet Browser and Snippet Editor UI files here
+snippets_manager_gladedir = $(anjuta_glade_dir)
+snippets_manager_glade_DATA = snippets-manager-preferences.ui \
+ snippets-browser.ui \
+ snippets-editor.ui
+
+
+# Plugin default data
+# The default installed plugins here.
+default_snippetsdir = $(anjuta_data_dir)
+default_snippets_DATA = \
+ snippets.anjuta-snippets \
+ snippets-global-variables.xml
+
+
+snippets_manager_pixmapsdir = $(anjuta_image_dir)
+snippets_manager_pixmaps_DATA = \
+ anjuta-snippets-manager.png
+
+# Plugin menu UI file
+snippets_manager_uidir = $(anjuta_ui_dir)
+snippets_manager_ui_DATA = \
+ snippets-manager-ui.xml
+
+plugin_in_files = anjuta-snippets-manager.plugin.in
+%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+snippets_manager_plugindir = $(anjuta_plugin_dir)
+snippets_manager_plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
+
+AM_CPPFLAGS= \
+ $(WARN_CFLAGS) \
+ $(DEPRECATED_FLAGS) \
+ $(LIBXML_CFLAGS) \
+ $(LIBANJUTA_CFLAGS) \
+ -DG_LOG_DOMAIN=\"libanjuta-snippets-manager\"
+
+plugindir = $(anjuta_plugin_dir)
+plugin_LTLIBRARIES = libanjuta-snippets-manager.la
+
+libanjuta_snippets_manager_la_LDFLAGS = $(ANJUTA_PLUGIN_LDFLAGS)
+
+libanjuta_snippets_manager_la_LIBADD = \
+ $(GIO_LIBS) \
+ $(LIBANJUTA_LIBS)
+
+libanjuta_snippets_manager_la_SOURCES = \
+ snippet.c\
+ snippet.h\
+ snippets-group.c\
+ snippets-group.h\
+ snippets-db.c\
+ snippets-db.h\
+ snippets-xml-parser.c\
+ snippets-xml-parser.h\
+ snippets-browser.c\
+ snippets-browser.h\
+ snippets-editor.c\
+ snippets-editor.h\
+ snippets-interaction-interpreter.c\
+ snippets-interaction-interpreter.h\
+ plugin.c\
+ plugin.h\
+ snippet-variables-store.c\
+ snippet-variables-store.h\
+ snippets-provider.c\
+ snippets-provider.h\
+ snippets-import-export.c\
+ snippets-import-export.h
+
+EXTRA_DIST = \
+ $(plugin_in_files) \
+ $(snippets_manager_pixmaps_DATA) \
+ $(snippets_manager_plugin_DATA) \
+ $(snippets_manager_glade_DATA) \
+ $(default_snippets_DATA) \
+ $(snippets_manager_ui_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/snippets-manager/anjuta-snippets-manager.plugin.in b/plugins/snippets-manager/anjuta-snippets-manager.plugin.in
new file mode 100644
index 0000000..5ca9869
--- /dev/null
+++ b/plugins/snippets-manager/anjuta-snippets-manager.plugin.in
@@ -0,0 +1,6 @@
+[Anjuta Plugin]
+_Name=Code Snippets
+_Description=Allows insertion of snippets into the editor.
+Location=anjuta-snippets-manager:SnippetsManagerPlugin
+Icon=anjuta-snippets-manager.png
+Interfaces=IAnjutaSnippetsManager
diff --git a/plugins/snippets-manager/anjuta-snippets-manager.png b/plugins/snippets-manager/anjuta-snippets-manager.png
new file mode 100644
index 0000000..072f9fa
Binary files /dev/null and b/plugins/snippets-manager/anjuta-snippets-manager.png differ
diff --git a/plugins/snippets-manager/plugin.c b/plugins/snippets-manager/plugin.c
new file mode 100644
index 0000000..5c678e6
--- /dev/null
+++ b/plugins/snippets-manager/plugin.c
@@ -0,0 +1,971 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ plugin.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+#include "plugin.h"
+#include "snippet.h"
+#include "snippets-import-export.h"
+#include <libanjuta/interfaces/ianjuta-snippets-manager.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-preferences.h>
+#include <libanjuta/interfaces/ianjuta-editor-language.h>
+#include <gio/gio.h>
+#include <libanjuta/anjuta-shell.h>
+#include <libanjuta/anjuta-debug.h>
+#include <libanjuta/anjuta-utils.h>
+
+#define ICON_FILE "anjuta-snippets-manager.png"
+#define PREFERENCES_UI PACKAGE_DATA_DIR"/glade/snippets-manager-preferences.ui"
+#define SNIPPETS_MANAGER_PREFERENCES_ROOT "snippets_preferences_root"
+#define MENU_UI PACKAGE_DATA_DIR"/ui/snippets-manager-ui.xml"
+
+#define GLOBAL_VAR_NEW_NAME "new_global_var_name"
+#define GLOBAL_VAR_NEW_VALUE "new_global_var_value"
+
+static gpointer parent_class;
+
+/* Menu callbacks and actions */
+
+static void on_menu_trigger_insert_snippet (GtkAction *action,
+ SnippetsManagerPlugin *plugin);
+static void on_menu_autocomplete_insert_snippet (GtkAction *action,
+ SnippetsManagerPlugin *plugin);
+static void on_menu_import_snippets (GtkAction *action,
+ SnippetsManagerPlugin *plugin);
+static void on_menu_export_snippets (GtkAction *action,
+ SnippetsManagerPlugin *plugin);
+
+static GtkActionEntry actions_snippets[] = {
+ {
+ "ActionMenuEditSnippetsManager",
+ NULL,
+ N_("Snippets"),
+ NULL,
+ NULL,
+ NULL},
+ {
+ "ActionEditTriggerInsert",
+ NULL,
+ N_("_Trigger insert"),
+ "<control>e",
+ N_("Insert a snippet using the trigger-key"),
+ G_CALLBACK (on_menu_trigger_insert_snippet)},
+ {
+ "ActionEditAutoCompleteInsert",
+ NULL,
+ N_("_Auto complete insert"),
+ "<control>r",
+ N_("Insert a snippet using auto-completion"),
+ G_CALLBACK (on_menu_autocomplete_insert_snippet)},
+ {
+ "ActionEditImportSnippets",
+ NULL,
+ N_("_Import snippets â?¦"),
+ NULL,
+ N_("Import snippets to the database"),
+ G_CALLBACK (on_menu_import_snippets)},
+ {
+ "ActionEditExportSnippets",
+ NULL,
+ N_("_Export snippets â?¦"),
+ NULL,
+ N_("Export snippets from the database"),
+ G_CALLBACK (on_menu_export_snippets)}
+};
+
+typedef struct _GlobalVariablesUpdateData
+{
+ SnippetsDB *snippets_db;
+ GtkTreeView *global_vars_view;
+} GlobalVariablesUpdateData;
+
+gboolean
+snippet_insert (SnippetsManagerPlugin * plugin,
+ const gchar *trigger)
+{
+ AnjutaSnippet *requested_snippet = NULL;
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin),
+ FALSE);
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (plugin);
+
+ /* Get the snippet from the database and check if it's not found */
+ requested_snippet = snippets_db_get_snippet (snippets_manager_plugin->snippets_db,
+ trigger,
+ NULL);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (requested_snippet), FALSE);
+
+ /* Get the default content of the snippet */
+ snippets_interaction_insert_snippet (snippets_manager_plugin->snippets_interaction,
+ snippets_manager_plugin->snippets_db,
+ requested_snippet);
+
+ return TRUE;
+}
+
+static void
+on_menu_trigger_insert_snippet (GtkAction *action,
+ SnippetsManagerPlugin *plugin)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (plugin->snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (plugin->snippets_interaction));
+
+ snippets_interaction_trigger_insert_request (plugin->snippets_interaction,
+ plugin->snippets_db);
+
+}
+
+static void
+on_menu_autocomplete_insert_snippet (GtkAction *action,
+ SnippetsManagerPlugin *plugin)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (plugin->snippets_provider));
+
+ snippets_provider_request (plugin->snippets_provider);
+}
+
+static void
+on_menu_import_snippets (GtkAction *action,
+ SnippetsManagerPlugin *plugin)
+{
+ AnjutaPlugin *p = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin));
+
+ p = ANJUTA_PLUGIN (plugin);
+ snippets_manager_import_snippets (plugin->snippets_db, p->shell);
+}
+
+static void
+on_menu_export_snippets (GtkAction *action,
+ SnippetsManagerPlugin *plugin)
+{
+ AnjutaPlugin *p = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin));
+
+ p = ANJUTA_PLUGIN (plugin);
+ snippets_manager_export_snippets (plugin->snippets_db, p->shell);
+}
+
+static void
+on_added_current_document (AnjutaPlugin *plugin,
+ const gchar *name,
+ const GValue *value,
+ gpointer data)
+{
+ GObject *cur_editor = NULL;
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin));
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (plugin);
+
+ /* Get the current document and test if it's an IAnjutaEditor */
+ cur_editor = g_value_get_object (value);
+ if (IANJUTA_IS_EDITOR (cur_editor))
+ snippets_interaction_set_editor (snippets_manager_plugin->snippets_interaction,
+ IANJUTA_EDITOR (cur_editor));
+ else
+ snippets_interaction_set_editor (snippets_manager_plugin->snippets_interaction,
+ NULL);
+
+ /* Refilter the snippets shown in the browser */
+ snippets_browser_refilter_snippets_view (snippets_manager_plugin->snippets_browser);
+
+ /* Load the provider if needed */
+ if (IANJUTA_IS_EDITOR_ASSIST (cur_editor))
+ snippets_provider_load (snippets_manager_plugin->snippets_provider,
+ IANJUTA_EDITOR_ASSIST (cur_editor));
+
+}
+
+static void
+on_removed_current_document (AnjutaPlugin *plugin,
+ const char *name,
+ gpointer data)
+{
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin));
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (plugin);
+
+ /* Unload the provider */
+ snippets_provider_unload (snippets_manager_plugin->snippets_provider);
+
+ snippets_interaction_set_editor (snippets_manager_plugin->snippets_interaction,
+ NULL);
+}
+
+static void
+on_snippets_browser_maximize_request (SnippetsBrowser *snippets_browser,
+ gpointer user_data)
+{
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (user_data));
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (user_data);
+
+ if (snippets_manager_plugin->browser_maximized)
+ return;
+
+ anjuta_shell_maximize_widget (ANJUTA_PLUGIN (snippets_manager_plugin)->shell,
+ "snippets_browser", NULL);
+ snippets_browser_show_editor (snippets_browser);
+
+ snippets_manager_plugin->browser_maximized = TRUE;
+}
+
+static void
+on_snippets_browser_unmaximize_request (SnippetsBrowser *snippets_browser,
+ gpointer user_data)
+{
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (user_data));
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (user_data);
+
+ if (!snippets_manager_plugin->browser_maximized)
+ return;
+
+ anjuta_shell_unmaximize (ANJUTA_PLUGIN (snippets_manager_plugin)->shell,
+ NULL);
+ snippets_browser_hide_editor (snippets_browser);
+
+ snippets_manager_plugin->browser_maximized = FALSE;
+}
+
+static gboolean
+snippets_manager_activate (AnjutaPlugin * plugin)
+{
+ SnippetsManagerPlugin *snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (plugin);
+ AnjutaUI *anjuta_ui = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (snippets_manager_plugin),
+ FALSE);
+
+ /* Link the AnjutaShell to the SnippetsDB and load the SnippetsDB*/
+ snippets_manager_plugin->snippets_db->anjuta_shell = plugin->shell;
+ snippets_db_load (snippets_manager_plugin->snippets_db);
+
+ /* Link the AnjutaShell to the SnippetsProvider and load if necessary */
+ snippets_manager_plugin->snippets_provider->anjuta_shell = plugin->shell;
+
+ /* Load the SnippetsBrowser with the snippets in the SnippetsDB */
+ snippets_manager_plugin->snippets_browser->anjuta_shell = plugin->shell;
+ snippets_browser_load (snippets_manager_plugin->snippets_browser,
+ snippets_manager_plugin->snippets_db,
+ snippets_manager_plugin->snippets_interaction);
+ anjuta_shell_add_widget (plugin->shell,
+ GTK_WIDGET (snippets_manager_plugin->snippets_browser),
+ "snippets_browser",
+ _("Snippets"),
+ GTK_STOCK_FILE,
+ ANJUTA_SHELL_PLACEMENT_LEFT,
+ NULL);
+ snippets_manager_plugin->browser_maximized = FALSE;
+
+ /* Initialize the Interaction Interpreter */
+ snippets_interaction_start (snippets_manager_plugin->snippets_interaction,
+ plugin->shell);
+
+ /* Add a watch for the current document */
+ snippets_manager_plugin->cur_editor_watch_id =
+ anjuta_plugin_add_watch (plugin,
+ IANJUTA_DOCUMENT_MANAGER_CURRENT_DOCUMENT,
+ on_added_current_document,
+ on_removed_current_document,
+ NULL);
+
+ /* Merge the Menu UI */
+ anjuta_ui = anjuta_shell_get_ui (plugin->shell, FALSE);
+
+ snippets_manager_plugin->action_group =
+ anjuta_ui_add_action_group_entries (anjuta_ui,
+ "ActionGroupSnippetsManager",
+ _("Snippets Manager actions"),
+ actions_snippets,
+ G_N_ELEMENTS (actions_snippets),
+ GETTEXT_PACKAGE,
+ TRUE,
+ snippets_manager_plugin);
+
+ snippets_manager_plugin->uiid = anjuta_ui_merge (anjuta_ui, MENU_UI);
+
+ DEBUG_PRINT ("%s", "SnippetsManager: Activating SnippetsManager plugin â?¦");
+
+ return TRUE;
+}
+
+static gboolean
+snippets_manager_deactivate (AnjutaPlugin *plugin)
+{
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+ AnjutaUI *anjuta_ui = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (plugin), FALSE);
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (plugin);
+
+ DEBUG_PRINT ("%s", "SnippetsManager: Deactivating SnippetsManager plugin â?¦");
+
+ anjuta_plugin_remove_watch (plugin,
+ snippets_manager_plugin->cur_editor_watch_id,
+ FALSE);
+
+ /* Remove the Menu UI */
+ anjuta_ui = anjuta_shell_get_ui (plugin->shell, NULL);
+ anjuta_ui_unmerge (anjuta_ui, snippets_manager_plugin->uiid);
+ anjuta_ui_remove_action_group (anjuta_ui, snippets_manager_plugin->action_group);
+
+ /* Unload the SnippetsBrowser */
+ if (snippets_manager_plugin->browser_maximized)
+ on_snippets_browser_unmaximize_request (snippets_manager_plugin->snippets_browser,
+ snippets_manager_plugin);
+ snippets_browser_unload (snippets_manager_plugin->snippets_browser);
+ g_object_ref (snippets_manager_plugin->snippets_browser);
+ anjuta_shell_remove_widget (plugin->shell,
+ GTK_WIDGET (snippets_manager_plugin->snippets_browser),
+ NULL);
+
+ /* Destroy the SnippetsDB */
+ snippets_db_close (snippets_manager_plugin->snippets_db);
+
+ /* Destroy the Interaction Interpreter */
+ snippets_interaction_destroy (snippets_manager_plugin->snippets_interaction);
+
+ /* Unload the SnippetsProvider */
+ snippets_provider_unload (snippets_manager_plugin->snippets_provider);
+
+ return TRUE;
+}
+
+static void
+snippets_manager_finalize (GObject * obj)
+{
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+snippets_manager_dispose (GObject * obj)
+{
+ SnippetsManagerPlugin *snippets_manager = ANJUTA_PLUGIN_SNIPPETS_MANAGER (obj);
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (snippets_manager));
+
+ if (ANJUTA_IS_SNIPPETS_DB (snippets_manager->snippets_db))
+ g_object_unref (snippets_manager->snippets_db);
+
+ if (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_manager->snippets_interaction))
+ g_object_unref (snippets_manager->snippets_interaction);
+
+ if (ANJUTA_IS_SNIPPETS_BROWSER (snippets_manager->snippets_browser))
+ g_object_unref (snippets_manager->snippets_browser);
+
+ if (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_manager->snippets_provider))
+ g_object_unref (snippets_manager->snippets_provider);
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+snippets_manager_plugin_instance_init (GObject * obj)
+{
+ SnippetsManagerPlugin *snippets_manager = ANJUTA_PLUGIN_SNIPPETS_MANAGER (obj);
+
+ snippets_manager->overwrite_on_conflict = FALSE;
+ snippets_manager->show_only_document_language_snippets = FALSE;
+
+ snippets_manager->cur_editor_watch_id = -1;
+
+ snippets_manager->action_group = NULL;
+ snippets_manager->uiid = -1;
+
+ snippets_manager->snippets_db = snippets_db_new ();
+ snippets_manager->snippets_interaction = snippets_interaction_new ();
+ snippets_manager->snippets_browser = snippets_browser_new ();
+ snippets_manager->snippets_provider =
+ snippets_provider_new (snippets_manager->snippets_db,
+ snippets_manager->snippets_interaction);
+
+ g_signal_connect (GTK_OBJECT (snippets_manager->snippets_browser),
+ "maximize-request",
+ GTK_SIGNAL_FUNC (on_snippets_browser_maximize_request),
+ snippets_manager);
+ g_signal_connect (GTK_OBJECT (snippets_manager->snippets_browser),
+ "unmaximize-request",
+ GTK_SIGNAL_FUNC (on_snippets_browser_unmaximize_request),
+ snippets_manager);
+
+}
+
+static void
+snippets_manager_plugin_class_init (GObjectClass * klass)
+{
+ AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ plugin_class->activate = snippets_manager_activate;
+ plugin_class->deactivate = snippets_manager_deactivate;
+ klass->dispose = snippets_manager_dispose;
+ klass->finalize = snippets_manager_finalize;
+}
+
+
+
+/* IAnjutaSnippetsManager interface */
+
+static gboolean
+isnippets_manager_iface_insert (IAnjutaSnippetsManager* snippets_manager, const gchar* key, GError** err)
+{
+ SnippetsManagerPlugin* plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (snippets_manager);
+ snippet_insert (plugin, key);
+ return TRUE;
+}
+
+static void
+isnippets_manager_iface_init (IAnjutaSnippetsManagerIface *iface)
+{
+ iface->insert = isnippets_manager_iface_insert;
+}
+
+
+
+/* IAnjutaPreferences interface */
+
+static void
+on_global_vars_name_changed (GtkCellRendererText *cell,
+ gchar *path_string,
+ gchar *new_text,
+ gpointer user_data)
+{
+ GtkTreeModel *global_vars_model = NULL;
+ SnippetsDB *snippets_db = NULL;
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ gchar *name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (user_data));
+ snippets_db = ANJUTA_SNIPPETS_DB (user_data);
+ global_vars_model = snippets_db_get_global_vars_model (snippets_db);
+ g_return_if_fail (GTK_IS_TREE_MODEL (global_vars_model));
+
+ /* Get the iter */
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (global_vars_model, &iter, path);
+
+ /* Get the current type and change it */
+ gtk_tree_model_get (global_vars_model, &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+ snippets_db_set_global_variable_name (snippets_db,
+ name,
+ new_text);
+ g_free (name);
+
+ /* Save the global variables */
+ snippets_db_save_global_vars (snippets_db);
+}
+
+static void
+global_vars_view_name_data_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *cell,
+ GtkTreeModel *global_vars_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *name = NULL;
+ gboolean is_internal = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (cell));
+
+ /* Get the name */
+ gtk_tree_model_get (global_vars_model, iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+
+ /* Check if it's internal */
+ gtk_tree_model_get (global_vars_model, iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+ if (is_internal)
+ {
+ gchar *temp = NULL;
+ temp = g_strconcat ("<b>", name, "</b> <i>(Internal)</i>", NULL);
+ g_free (name);
+ name = temp;
+ g_object_set (cell, "sensitive", FALSE, NULL);
+ g_object_set (cell, "editable", FALSE, NULL);
+ }
+ else
+ {
+ gchar *temp = NULL;
+ temp = g_strconcat ("<b>", name, "</b>", NULL);
+ g_free (name);
+ name = temp;
+ g_object_set (cell, "sensitive", TRUE, NULL);
+ g_object_set (cell, "editable", TRUE, NULL);
+ }
+
+ g_object_set (cell, "markup", name, NULL);
+ g_free (name);
+}
+
+static void
+on_global_vars_type_toggled (GtkCellRendererToggle *cell,
+ gchar *path_string,
+ gpointer user_data)
+{
+ GtkTreeModel *global_vars_model = NULL;
+ SnippetsDB *snippets_db = NULL;
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ gboolean is_command = FALSE;
+ gchar *name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (user_data));
+ snippets_db = ANJUTA_SNIPPETS_DB (user_data);
+ global_vars_model = snippets_db_get_global_vars_model (snippets_db);
+ g_return_if_fail (GTK_IS_TREE_MODEL (global_vars_model));
+
+ /* Get the iter */
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (global_vars_model, &iter, path);
+
+ /* Get the current type and change it */
+ gtk_tree_model_get (global_vars_model, &iter,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, &is_command,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+ snippets_db_set_global_variable_type (snippets_db,
+ name,
+ (is_command) ? FALSE : TRUE);
+
+ /* Save the global variables */
+ snippets_db_save_global_vars (snippets_db);
+
+ g_free (name);
+
+}
+
+static void
+global_vars_view_type_data_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *cell,
+ GtkTreeModel *global_vars_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gboolean is_command = FALSE, is_internal = TRUE;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (cell));
+
+
+ /* Check if it's internal */
+ gtk_tree_model_get (global_vars_model, iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+ if (is_internal)
+ {
+ g_object_set (cell, "sensitive", FALSE, NULL);
+ gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (cell), FALSE);
+ gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (cell), FALSE);
+ }
+ else
+ {
+ gtk_tree_model_get (global_vars_model, iter,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, &is_command,
+ -1);
+ g_object_set (cell, "sensitive", TRUE, NULL);
+ gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (cell), TRUE);
+ gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (cell), is_command);
+ }
+}
+
+static void
+on_global_vars_text_changed (GtkCellRendererText *cell,
+ gchar *path_string,
+ gchar *new_text,
+ gpointer user_data)
+{
+ GtkTreeModel *global_vars_model = NULL;
+ SnippetsDB *snippets_db = NULL;
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ gchar *name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (user_data));
+ snippets_db = ANJUTA_SNIPPETS_DB (user_data);
+ global_vars_model = snippets_db_get_global_vars_model (snippets_db);
+ g_return_if_fail (GTK_IS_TREE_MODEL (global_vars_model));
+
+ /* Get the iter */
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (global_vars_model, &iter, path);
+
+ /* Get the current type and change it */
+ gtk_tree_model_get (global_vars_model, &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+ snippets_db_set_global_variable_value (snippets_db,
+ name,
+ new_text);
+ g_free (name);
+
+ /* Save the global variables */
+ snippets_db_save_global_vars (snippets_db);
+}
+
+static void
+global_vars_view_text_data_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *cell,
+ GtkTreeModel *global_vars_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *name = NULL, *text = NULL;
+ SnippetsDB *snippets_db = NULL;
+ gboolean is_internal = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (cell));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (user_data));
+ snippets_db = ANJUTA_SNIPPETS_DB (user_data);
+
+ /* Get the name */
+ gtk_tree_model_get (global_vars_model, iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ if (is_internal)
+ {
+ g_object_set (cell, "editable", FALSE, NULL);
+ }
+ else
+ {
+ g_object_set (cell, "editable", TRUE, NULL);
+ }
+
+ text = snippets_db_get_global_variable_text (snippets_db, name);
+
+ g_object_set (cell, "text", text, NULL);
+ g_free (name);
+ g_free (text);
+}
+
+static void
+global_vars_view_value_data_func (GtkTreeViewColumn *col,
+ GtkCellRenderer *cell,
+ GtkTreeModel *global_vars_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *name = NULL, *value = NULL;
+ SnippetsDB *snippets_db = NULL;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (cell));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (user_data));
+ snippets_db = ANJUTA_SNIPPETS_DB (user_data);
+
+ /* Get the name */
+ gtk_tree_model_get (global_vars_model, iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+
+ value = snippets_db_get_global_variable (snippets_db, name);
+
+ g_object_set (cell, "text", value, NULL);
+ g_free (name);
+}
+
+static void
+set_up_global_variables_view (SnippetsManagerPlugin *snippets_manager_plugin,
+ GtkTreeView *global_vars_view)
+{
+ GtkCellRenderer *cell = NULL;
+ GtkTreeViewColumn *col = NULL;
+ GtkTreeModel *global_vars_model = NULL;
+
+ /* Assertions */
+ global_vars_model = snippets_db_get_global_vars_model (snippets_manager_plugin->snippets_db);
+ g_return_if_fail (GTK_IS_TREE_MODEL (global_vars_model));
+ g_return_if_fail (GTK_IS_TREE_VIEW (global_vars_view));
+
+ /* Set up the model */
+ gtk_tree_view_set_model (global_vars_view,
+ global_vars_model);
+
+ /* Set up the name cell */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (col, "Name");
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_column_pack_start (col, cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (col, cell,
+ global_vars_view_name_data_func,
+ NULL, NULL);
+ gtk_tree_view_append_column (global_vars_view, col);
+ g_signal_connect (GTK_OBJECT (cell),
+ "edited",
+ GTK_SIGNAL_FUNC (on_global_vars_name_changed),
+ snippets_manager_plugin->snippets_db);
+
+ /* Set up the type cell */
+ cell = gtk_cell_renderer_toggle_new ();
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (col, "Command?");
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_column_pack_start (col, cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (col, cell,
+ global_vars_view_type_data_func,
+ NULL, NULL);
+ gtk_tree_view_append_column (global_vars_view, col);
+ g_signal_connect (GTK_OBJECT (cell),
+ "toggled",
+ GTK_SIGNAL_FUNC (on_global_vars_type_toggled),
+ snippets_manager_plugin->snippets_db);
+
+ /* Set up the text cell */
+ cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (col, "Variable text");
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_column_pack_start (col, cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (col, cell,
+ global_vars_view_text_data_func,
+ snippets_manager_plugin->snippets_db,
+ NULL);
+ gtk_tree_view_append_column (global_vars_view, col);
+ g_signal_connect (GTK_OBJECT (cell),
+ "edited",
+ GTK_SIGNAL_FUNC (on_global_vars_text_changed),
+ snippets_manager_plugin->snippets_db);
+
+ /* Set up the instant value cell */
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "editable", FALSE, NULL);
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (col, "Instant value");
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_column_pack_start (col, cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (col, cell,
+ global_vars_view_value_data_func,
+ snippets_manager_plugin->snippets_db,
+ NULL);
+ gtk_tree_view_append_column (global_vars_view, col);
+
+}
+
+static void
+on_add_variable_b_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ GlobalVariablesUpdateData *update_data = (GlobalVariablesUpdateData *)user_data;
+ GtkTreeView *global_vars_view = NULL;
+ GtkTreeModel *global_vars_model = NULL;
+ SnippetsDB *snippets_db = NULL;
+ GtkTreeIter iter;
+ gboolean iter_has_next = TRUE;
+ gchar *name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (update_data->snippets_db));
+ g_return_if_fail (GTK_IS_TREE_VIEW (update_data->global_vars_view));
+ snippets_db = ANJUTA_SNIPPETS_DB (update_data->snippets_db);
+ global_vars_view = GTK_TREE_VIEW (update_data->global_vars_view);
+ global_vars_model = snippets_db_get_global_vars_model (snippets_db);
+
+ /* Insert it into the SnippetsDB */
+ snippets_db_add_global_variable (snippets_db,
+ GLOBAL_VAR_NEW_NAME,
+ GLOBAL_VAR_NEW_VALUE,
+ FALSE, FALSE);
+
+ /* Get to the new inserted variable */
+ iter_has_next = gtk_tree_model_get_iter_first (global_vars_model, &iter);
+ while (iter_has_next)
+ {
+ gtk_tree_model_get (global_vars_model, &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+ if (!g_strcmp0 (name, GLOBAL_VAR_NEW_NAME))
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (global_vars_model, &iter);
+
+ gtk_tree_view_set_cursor (global_vars_view,
+ path,
+ gtk_tree_view_get_column (global_vars_view, 0),
+ TRUE);
+
+ gtk_tree_path_free (path);
+ g_free (name);
+ return;
+ }
+
+ g_free (name);
+ iter_has_next = gtk_tree_model_iter_next (global_vars_model, &iter);
+ }
+
+ /* Save the global variables */
+ snippets_db_save_global_vars (snippets_db);
+}
+
+static void
+on_delete_variable_b_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ GlobalVariablesUpdateData *update_data = (GlobalVariablesUpdateData *)user_data;
+ GtkTreeView *global_vars_view = NULL;
+ GtkTreeModel *global_vars_model = NULL;
+ SnippetsDB *snippets_db = NULL;
+ GtkTreeSelection *global_vars_selection = NULL;
+ gchar *name = NULL;
+ gboolean has_selection = FALSE;
+ GtkTreeIter iter;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (update_data->snippets_db));
+ g_return_if_fail (GTK_IS_TREE_VIEW (update_data->global_vars_view));
+ snippets_db = ANJUTA_SNIPPETS_DB (update_data->snippets_db);
+ global_vars_view = GTK_TREE_VIEW (update_data->global_vars_view);
+ global_vars_model = snippets_db_get_global_vars_model (snippets_db);
+ global_vars_selection = gtk_tree_view_get_selection (global_vars_view);
+
+ /* Get the selected iter */
+ has_selection = gtk_tree_selection_get_selected (global_vars_selection,
+ &global_vars_model,
+ &iter);
+
+ /* If there is a selection delete the selected item */
+ if (has_selection)
+ {
+ gtk_tree_model_get (global_vars_model, &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ -1);
+ snippets_db_remove_global_variable (snippets_db, name);
+ g_free (name);
+
+ }
+
+ /* Save the global variables */
+ snippets_db_save_global_vars (snippets_db);
+}
+
+static void
+ipreferences_merge (IAnjutaPreferences* ipref,
+ AnjutaPreferences* prefs,
+ GError** e)
+{
+ GError* error = NULL;
+ GtkBuilder* bxml = gtk_builder_new ();
+ GtkTreeView *global_vars_view = NULL;
+ GtkButton *add_variable_b = NULL, *delete_variable_b = NULL;
+ SnippetsManagerPlugin *snippets_manager_plugin = NULL;
+ GlobalVariablesUpdateData *global_vars_update_data = NULL;
+
+ /* Assertions */
+ snippets_manager_plugin = ANJUTA_PLUGIN_SNIPPETS_MANAGER (ipref);
+ g_return_if_fail (ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER (snippets_manager_plugin));
+
+ if (!gtk_builder_add_from_file (bxml, PREFERENCES_UI, &error))
+ {
+ g_warning ("Couldn't load preferences ui file: %s", error->message);
+ g_error_free (error);
+ }
+ anjuta_preferences_add_from_builder (prefs, bxml, SNIPPETS_MANAGER_PREFERENCES_ROOT, _("Code Snippets"),
+ ICON_FILE);
+
+ /* Get the Gtk objects */
+ global_vars_view = GTK_TREE_VIEW (gtk_builder_get_object (bxml, "global_vars_view"));
+ add_variable_b = GTK_BUTTON (gtk_builder_get_object (bxml, "add_var_button"));
+ delete_variable_b = GTK_BUTTON (gtk_builder_get_object (bxml, "delete_var_button"));
+ g_return_if_fail (GTK_IS_TREE_VIEW (global_vars_view));
+ g_return_if_fail (GTK_IS_BUTTON (add_variable_b));
+ g_return_if_fail (GTK_IS_BUTTON (delete_variable_b));
+
+ /* Set up the Global Variables GtkTreeView */
+ set_up_global_variables_view (snippets_manager_plugin, global_vars_view);
+
+ /* Connect the addition/deletion buttons */
+ global_vars_update_data = g_malloc (sizeof (GlobalVariablesUpdateData));
+ global_vars_update_data->snippets_db = snippets_manager_plugin->snippets_db;
+ global_vars_update_data->global_vars_view = global_vars_view;
+
+ g_signal_connect (GTK_OBJECT (add_variable_b),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_add_variable_b_clicked),
+ global_vars_update_data);
+
+ g_signal_connect (GTK_OBJECT (delete_variable_b),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_delete_variable_b_clicked),
+ global_vars_update_data);
+
+ g_object_unref (bxml);
+}
+
+static void
+ipreferences_unmerge (IAnjutaPreferences* ipref,
+ AnjutaPreferences* prefs,
+ GError** e)
+{
+ anjuta_preferences_remove_page (prefs, _("Code Snippets"));
+}
+
+static void
+ipreferences_iface_init (IAnjutaPreferencesIface* iface)
+{
+ iface->merge = ipreferences_merge;
+ iface->unmerge = ipreferences_unmerge;
+}
+
+
+ANJUTA_PLUGIN_BEGIN (SnippetsManagerPlugin, snippets_manager_plugin);
+ANJUTA_PLUGIN_ADD_INTERFACE (isnippets_manager, IANJUTA_TYPE_SNIPPETS_MANAGER);
+ANJUTA_PLUGIN_ADD_INTERFACE (ipreferences, IANJUTA_TYPE_PREFERENCES);
+ANJUTA_PLUGIN_END
+
+ANJUTA_SIMPLE_PLUGIN (SnippetsManagerPlugin, snippets_manager_plugin);
diff --git a/plugins/snippets-manager/plugin.h b/plugins/snippets-manager/plugin.h
new file mode 100644
index 0000000..e55180e
--- /dev/null
+++ b/plugins/snippets-manager/plugin.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ plugin.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_MANAGER_PLUGIN_H__
+#define __SNIPPETS_MANAGER_PLUGIN_H__
+
+#include <config.h>
+#include <libanjuta/anjuta-plugin.h>
+#include <libanjuta/interfaces/ianjuta-snippets-manager.h>
+#include <libanjuta/interfaces/ianjuta-editor.h>
+
+#include "snippets-editor.h"
+#include "snippets-browser.h"
+#include "snippets-db.h"
+#include "snippets-interaction-interpreter.h"
+#include "snippets-provider.h"
+
+
+extern GType snippets_manager_plugin_get_type (GTypeModule *module);
+#define ANJUTA_TYPE_PLUGIN_SNIPPETS_MANAGER (snippets_manager_plugin_get_type (NULL))
+#define ANJUTA_PLUGIN_SNIPPETS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ANJUTA_TYPE_PLUGIN_SNIPPETS_MANAGER, SnippetsManagerPlugin))
+#define ANJUTA_PLUGIN_SNIPPETS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), ANJUTA_TYPE_PLUGIN_SNIPPETS_MANAGER, SnippetsManagerPluginClass))
+#define ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ANJUTA_TYPE_PLUGIN_SNIPPETS_MANAGER))
+#define ANJUTA_IS_PLUGIN_SNIPPETS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ANJUTA_TYPE_PLUGIN_SNIPPETS_MANAGER))
+#define ANJUTA_PLUGIN_SNIPPETS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ANJUTA_TYPE_PLUGIN_SNIPPETS_MANAGER, SnippetsManagerPluginClass))
+
+
+typedef struct _SnippetsManagerPlugin SnippetsManagerPlugin;
+typedef struct _SnippetsManagerPluginClass SnippetsManagerPluginClass;
+
+struct _SnippetsManagerPlugin
+{
+ AnjutaPlugin parent;
+
+ /* Snippet Database. This is where snippets are loaded into memory.
+ Provides search functions. */
+ SnippetsDB* snippets_db;
+
+ /* Snippets Interaction Interpreter. This takes care of interacting with
+ the editor for inserting and live editing the snippets. */
+ SnippetsInteraction* snippets_interaction;
+
+ /* GUI parts. */
+ SnippetsBrowser* snippets_browser;
+
+ SnippetsProvider *snippets_provider;
+
+ /* Plug-in settings */
+ gboolean overwrite_on_conflict;
+ gboolean show_only_document_language_snippets;
+
+ gint cur_editor_watch_id;
+
+ /* The Menu UI */
+ GtkActionGroup *action_group;
+ gint uiid;
+
+ gboolean browser_maximized;
+
+};
+
+
+struct _SnippetsManagerPluginClass
+{
+ AnjutaPluginClass parent_class;
+};
+
+
+/* To insert a snippet to the editor. */
+gboolean
+snippet_insert (SnippetsManagerPlugin * plugin, const gchar *keyword);
+
+
+#endif /* __SNIPPETS_MANAGER_PLUGIN_H__ */
diff --git a/plugins/snippets-manager/snippet-variables-store.c b/plugins/snippets-manager/snippet-variables-store.c
new file mode 100644
index 0000000..e86e989
--- /dev/null
+++ b/plugins/snippets-manager/snippet-variables-store.c
@@ -0,0 +1,719 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippet-variables-store.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippet-variables-store.h"
+
+#define ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_SNIPPET_VARS_STORE, SnippetVarsStorePrivate))
+
+struct _SnippetVarsStorePrivate
+{
+ SnippetsDB *snippets_db;
+ AnjutaSnippet *snippet;
+
+ /* Handler id's */
+ gulong row_inserted_handler_id;
+ gulong row_changed_handler_id;
+ gulong row_deleted_handler_id;
+
+};
+
+G_DEFINE_TYPE (SnippetVarsStore, snippet_vars_store, GTK_TYPE_LIST_STORE);
+
+static void
+snippet_vars_store_init (SnippetVarsStore *snippet_vars_store)
+{
+ SnippetVarsStorePrivate *priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (snippet_vars_store);
+
+ snippet_vars_store->priv = priv;
+
+ /* Initialize the private field */
+ priv->snippets_db = NULL;
+ priv->snippet = NULL;
+
+}
+
+static void
+snippet_vars_store_class_init (SnippetVarsStoreClass *snippet_vars_store_class)
+{
+ snippet_vars_store_parent_class = g_type_class_peek_parent (snippet_vars_store_class);
+
+ g_type_class_add_private (snippet_vars_store_class, sizeof (SnippetVarsStorePrivate));
+}
+
+/**
+ * snippet_vars_store_new:
+ *
+ * Returns: A new #SnippetVarsStore object with empty fields, but with columns
+ * initialized.
+ */
+SnippetVarsStore*
+snippet_vars_store_new ()
+{
+ GObject* obj = g_object_new (snippet_vars_store_get_type (), NULL);
+ SnippetVarsStore *vars_store = ANJUTA_SNIPPET_VARS_STORE (obj);
+ GType types[VARS_STORE_COL_N] = {G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN};
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (vars_store),
+ VARS_STORE_COL_N, types);
+
+ return vars_store;
+}
+
+static void
+add_snippet_variable (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ const gchar *default_value,
+ gboolean is_global)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ gchar *instant_value = NULL;
+ gboolean undefined = FALSE;
+ GtkTreeIter iter;
+ SnippetVariableType type;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (variable_name != NULL);
+ g_return_if_fail (default_value != NULL);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+
+ if (is_global)
+ {
+ instant_value = snippets_db_get_global_variable (priv->snippets_db, variable_name);
+ if (instant_value == NULL)
+ {
+ undefined = TRUE;
+ instant_value = g_strdup (default_value);
+ }
+
+ type = SNIPPET_VAR_TYPE_GLOBAL;
+ }
+ else
+ {
+ instant_value = g_strdup (default_value);
+ type = SNIPPET_VAR_TYPE_LOCAL;
+ }
+
+ gtk_list_store_append (GTK_LIST_STORE (vars_store), &iter);
+
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_NAME, variable_name,
+ VARS_STORE_COL_TYPE, type,
+ VARS_STORE_COL_DEFAULT_VALUE, default_value,
+ VARS_STORE_COL_INSTANT_VALUE, instant_value,
+ VARS_STORE_COL_IN_SNIPPET, TRUE,
+ VARS_STORE_COL_UNDEFINED, undefined,
+ -1);
+
+ g_free (instant_value);
+}
+
+static void
+add_global_variables (SnippetVarsStore *vars_store)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeIter iter, iter_to_add;
+ gchar *cur_var_name = NULL;
+ GtkTreeModel *global_vars_model = NULL;
+ gchar *instant_value = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+ global_vars_model = snippets_db_get_global_vars_model (priv->snippets_db);
+ g_return_if_fail (GTK_IS_TREE_MODEL (global_vars_model));
+
+ if (gtk_tree_model_get_iter_first (global_vars_model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get (global_vars_model, &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &cur_var_name,
+ -1);
+
+ /* If the snippet holds this global variable, it was already added to the
+ store.*/
+ if (snippet_has_variable (priv->snippet, cur_var_name))
+ {
+ g_free (cur_var_name);
+ continue;
+ }
+ instant_value = snippets_db_get_global_variable (priv->snippets_db, cur_var_name);
+
+ gtk_list_store_append (GTK_LIST_STORE (vars_store), &iter_to_add);
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter_to_add,
+ VARS_STORE_COL_NAME, cur_var_name,
+ VARS_STORE_COL_TYPE, SNIPPET_VAR_TYPE_GLOBAL,
+ VARS_STORE_COL_DEFAULT_VALUE, g_strdup (""),
+ VARS_STORE_COL_INSTANT_VALUE, instant_value,
+ VARS_STORE_COL_IN_SNIPPET, FALSE,
+ VARS_STORE_COL_UNDEFINED, FALSE,
+ -1);
+
+ g_free (cur_var_name);
+ g_free (instant_value);
+
+ } while (gtk_tree_model_iter_next (global_vars_model, &iter));
+ }
+}
+
+/**
+ * reload_vars_store:
+ * @vars_store: A #SnippetVarsStore object.
+ *
+ * Reloads the GtkListStore with the current values of the variables in the internal
+ * snippet and snippets-db. If priv->snippet or priv->snippets_db is NULL, it will clear the
+ * GtkListStore.
+ */
+static void
+reload_vars_store (SnippetVarsStore *vars_store)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GList *snippet_vars_names = NULL, *snippet_vars_defaults = NULL, *snippet_vars_globals = NULL,
+ *iter1 = NULL, *iter2 = NULL, *iter3 = NULL;
+ gchar *cur_var_name = NULL, *cur_var_default = NULL;
+ gboolean cur_var_global = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+
+ /* Clear the GtkListStore */
+ gtk_list_store_clear (GTK_LIST_STORE (vars_store));
+
+ /* Add new items */
+ if (ANJUTA_IS_SNIPPET (priv->snippet) && ANJUTA_IS_SNIPPETS_DB (priv->snippets_db))
+ {
+ /* Add the snippet variables to the store */
+ snippet_vars_names = snippet_get_variable_names_list (priv->snippet);
+ snippet_vars_defaults = snippet_get_variable_defaults_list (priv->snippet);
+ snippet_vars_globals = snippet_get_variable_globals_list (priv->snippet);
+
+ g_return_if_fail (g_list_length (snippet_vars_names) == g_list_length (snippet_vars_defaults));
+ g_return_if_fail (g_list_length (snippet_vars_names) == g_list_length (snippet_vars_globals));
+
+ iter1 = g_list_first (snippet_vars_names);
+ iter2 = g_list_first (snippet_vars_defaults);
+ iter3 = g_list_first (snippet_vars_globals);
+ while (iter1 != NULL && iter2 != NULL && iter3 != NULL)
+ {
+ cur_var_name = (gchar *)iter1->data;
+ cur_var_default = (gchar *)iter2->data;
+ cur_var_global = GPOINTER_TO_INT (iter3->data);
+ add_snippet_variable (vars_store, cur_var_name, cur_var_default, cur_var_global);
+
+ iter1 = g_list_next (iter1);
+ iter2 = g_list_next (iter2);
+ iter3 = g_list_next (iter3);
+ }
+ g_list_free (snippet_vars_names);
+ g_list_free (snippet_vars_defaults);
+ g_list_free (snippet_vars_globals);
+
+ /* Add the global variables to the store which aren't in the snippet */
+ add_global_variables (vars_store);
+
+ }
+
+}
+
+static void
+on_global_vars_model_row_changed (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (user_data));
+
+ reload_vars_store (ANJUTA_SNIPPET_VARS_STORE (user_data));
+}
+
+
+static void
+on_global_vars_model_row_inserted (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (user_data));
+
+ reload_vars_store (ANJUTA_SNIPPET_VARS_STORE (user_data));
+}
+
+static void
+on_global_vars_model_row_deleted (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (user_data));
+
+ reload_vars_store (ANJUTA_SNIPPET_VARS_STORE (user_data));
+}
+
+void
+snippet_vars_store_load (SnippetVarsStore *vars_store,
+ SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+
+ priv->snippets_db = snippets_db;
+ priv->snippet = snippet;
+
+ /* This will fill the GtkListStore with values of variables from snippets_db
+ and snippet */
+ reload_vars_store (vars_store);
+
+ /* We connect to the signals that change the GtkTreeModel of the global variables.
+ This is to make sure our store is synced with the global variables model. */
+ priv->row_inserted_handler_id =
+ g_signal_connect (G_OBJECT (snippets_db_get_global_vars_model (snippets_db)),
+ "row-inserted",
+ G_CALLBACK (on_global_vars_model_row_inserted),
+ vars_store);
+ priv->row_changed_handler_id =
+ g_signal_connect (G_OBJECT (snippets_db_get_global_vars_model (snippets_db)),
+ "row-changed",
+ G_CALLBACK (on_global_vars_model_row_changed),
+ vars_store);
+ priv->row_deleted_handler_id =
+ g_signal_connect (G_OBJECT (snippets_db_get_global_vars_model (snippets_db)),
+ "row-deleted",
+ G_CALLBACK (on_global_vars_model_row_deleted),
+ vars_store);
+}
+
+void
+snippet_vars_store_unload (SnippetVarsStore *vars_store)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeModel *global_vars_model = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+
+ /* If we don't have a snippet or a snippets-db we just return */
+ if (!ANJUTA_IS_SNIPPETS_DB (priv->snippets_db))
+ return;
+
+ global_vars_model = snippets_db_get_global_vars_model (priv->snippets_db);
+ g_return_if_fail (GTK_IS_TREE_MODEL (global_vars_model));
+
+ /* Disconnect the handlers */
+ g_signal_handler_disconnect (global_vars_model, priv->row_inserted_handler_id);
+ g_signal_handler_disconnect (global_vars_model, priv->row_changed_handler_id);
+ g_signal_handler_disconnect (global_vars_model, priv->row_deleted_handler_id);
+
+ priv->snippets_db = NULL;
+ priv->snippet = NULL;
+
+ /* This will clear the GtkListStore */
+ reload_vars_store (vars_store);
+}
+
+static gboolean
+get_iter_at_variable (SnippetVarsStore *vars_store,
+ GtkTreeIter *iter,
+ const gchar *variable_name,
+ SnippetVariableType type,
+ gboolean in_snippet_only)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ gchar *cur_var_name = NULL;
+ gboolean cur_var_in_snippet = FALSE;
+ SnippetVariableType cur_type = SNIPPET_VAR_TYPE_ANY;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store), FALSE);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (vars_store), iter))
+ return FALSE;
+
+ do
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (vars_store), iter,
+ VARS_STORE_COL_NAME, &cur_var_name,
+ VARS_STORE_COL_IN_SNIPPET, &cur_var_in_snippet,
+ VARS_STORE_COL_TYPE, &cur_type,
+ -1);
+
+ if (!g_strcmp0 (variable_name, cur_var_name))
+ {
+ g_free (cur_var_name);
+ if (type != SNIPPET_VAR_TYPE_ANY && cur_type != type)
+ continue;
+ if (in_snippet_only && !cur_var_in_snippet)
+ continue;
+
+ return TRUE;
+ }
+ g_free (cur_var_name);
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (vars_store), iter));
+
+ return FALSE;
+}
+
+/**
+ * snippet_vars_store_set_variable_name:
+ * @vars_store: A #SnippetVarsStore object.
+ * @old_variable_name: The name of the variable which should be renamed.
+ * @new_variable_name: The new name for the variable.
+ *
+ * Changes the name of a variable which is already added to the snippet (so it won't
+ * work for global variables that aren't added to the snippet). If the type of the
+ * variable is global, it will remove it from the snippet and add a new global variable
+ * with the @new_variable_name. If no global variable with the new name is found, it
+ * will be marked as undefined. If the type of the variable is local, it will still
+ * remove/add again with a new name, but won't do the check if it's defined.
+ */
+void
+snippet_vars_store_set_variable_name (SnippetVarsStore *vars_store,
+ const gchar *old_variable_name,
+ const gchar *new_variable_name)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeIter iter;
+ gchar *default_value = NULL, *instant_value = NULL;
+ SnippetVariableType type;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (old_variable_name != NULL);
+ g_return_if_fail (new_variable_name != NULL);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (priv->snippet));
+
+ /* We check that the new name isn't already in the snippet */
+ if (snippet_has_variable (priv->snippet, new_variable_name))
+ return;
+
+ /* We get the iter at the requested variable */
+ if (!get_iter_at_variable (vars_store, &iter, old_variable_name, SNIPPET_VAR_TYPE_ANY, TRUE))
+ return;
+
+ /* We get the type and default value (as we may need to change it) */
+ gtk_tree_model_get (GTK_TREE_MODEL (vars_store), &iter,
+ VARS_STORE_COL_DEFAULT_VALUE, &default_value,
+ VARS_STORE_COL_TYPE, &type,
+ -1);
+
+ /* Remove the old variable */
+ snippet_vars_store_remove_variable_from_snippet (vars_store, old_variable_name);
+ snippet_vars_store_add_variable_to_snippet (vars_store, new_variable_name,
+ type == SNIPPET_VAR_TYPE_GLOBAL);
+
+ /* Get the iter at the newly added variable */
+ if (!get_iter_at_variable (vars_store, &iter, new_variable_name, type, TRUE))
+ g_return_if_reached ();
+
+ /* We compute the instant_value:
+ * if it's global and defined we get the value from the snippets_db
+ * if it's global but undefined or if it's local, we save the default_value
+ */
+ if (type == SNIPPET_VAR_TYPE_GLOBAL)
+ {
+ instant_value = snippets_db_get_global_variable (priv->snippets_db, new_variable_name);
+ }
+ if (instant_value == NULL)
+ {
+ instant_value = g_strdup (default_value);
+ }
+
+ /* Save the the list store the changes */
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_DEFAULT_VALUE, default_value,
+ VARS_STORE_COL_INSTANT_VALUE, instant_value,
+ -1);
+
+ /* Save the change to the snippet */
+ snippet_set_variable_name (priv->snippet, old_variable_name, new_variable_name);
+ snippet_set_variable_default_value (priv->snippet, new_variable_name, default_value);
+ snippet_set_variable_global (priv->snippet, new_variable_name, type == SNIPPET_VAR_TYPE_GLOBAL);
+
+ g_free (default_value);
+ g_free (instant_value);
+
+}
+
+/**
+ * snippet_vars_store_set_variable_type:
+ * @vars_store: A #SnippetVarsStore object.
+ * @variable_name: The name of the variable to have it's type changed.
+ * @new_type: The new type of the variable.
+ *
+ * Sets a new type for a varible that already is in the snippet (so you can't set
+ * types for global variables that aren't inserted in the snippet). This should be used
+ * for changing already inserted variables types. Changing to global will try to get the
+ * global variable with the given name and if not found it will be marked as undefined.
+ * Changing to local, should actually add a new local variable with the given name and
+ * remove the global variable from the snippet without deleting it it's defined, but
+ * deleting it if it's undefined.
+ */
+void
+snippet_vars_store_set_variable_type (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ SnippetVariableType new_type)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeIter iter;
+ gchar *default_value = NULL;
+ SnippetVariableType old_type;
+ gboolean undefined = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (variable_name != NULL);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (priv->snippet));
+
+ old_type = (new_type == SNIPPET_VAR_TYPE_LOCAL) ? SNIPPET_VAR_TYPE_GLOBAL : SNIPPET_VAR_TYPE_LOCAL;
+
+ /* We get the iter at the requested variable */
+ if (!get_iter_at_variable (vars_store, &iter, variable_name, old_type, TRUE))
+ return;
+
+ /* Get the default value as it was saved by the user */
+ gtk_tree_model_get (GTK_TREE_MODEL (vars_store), &iter,
+ VARS_STORE_COL_DEFAULT_VALUE, &default_value,
+ -1);
+
+ /* We remove from the snippet the old variable entry and we add a new "clean" one*/
+ snippet_vars_store_remove_variable_from_snippet (vars_store, variable_name);
+ snippet_vars_store_add_variable_to_snippet (vars_store, variable_name,
+ new_type == SNIPPET_VAR_TYPE_GLOBAL);
+
+ /* Get a iter at the new variable */
+ if (!get_iter_at_variable (vars_store, &iter, variable_name, new_type, TRUE))
+ g_return_if_reached ();
+
+ /* Change it's default value to what the user used to have for it */
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_DEFAULT_VALUE, default_value,
+ -1);
+
+ snippet_set_variable_global (priv->snippet, variable_name, new_type == SNIPPET_VAR_TYPE_GLOBAL);
+ snippet_set_variable_default_value (priv->snippet, variable_name, default_value);
+
+ /* If the newly added variable is local or undefined, we update it's instant value */
+ gtk_tree_model_get (GTK_TREE_MODEL (vars_store), &iter,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ -1);
+ if (new_type == SNIPPET_VAR_TYPE_LOCAL || undefined)
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_INSTANT_VALUE, default_value,
+ -1);
+
+ g_free (default_value);
+
+}
+
+/**
+ * snippet_vars_store_set_variable_default:
+ * @vars_store: A #SnippetVarsStore object.
+ * @variable_name: The name of the variable which will have it's default value changed.
+ * @default_value: The new default value.
+ *
+ * This will only work if the variable is in the snippet (so we can't set default values
+ * for global variables which aren't in the snippet).
+ */
+void
+snippet_vars_store_set_variable_default (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ const gchar *default_value)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeIter iter;
+ SnippetVariableType type;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (variable_name != NULL);
+ g_return_if_fail (default_value != NULL);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (priv->snippet));
+
+ /* We get the iter at the requested variable */
+ if (!get_iter_at_variable (vars_store, &iter, variable_name, SNIPPET_VAR_TYPE_ANY, TRUE))
+ return;
+
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_DEFAULT_VALUE, default_value,
+ -1);
+
+ /* If the variable is local, we also set the instant value */
+ gtk_tree_model_get (GTK_TREE_MODEL (vars_store), &iter,
+ VARS_STORE_COL_TYPE, &type,
+ -1);
+ if (type == SNIPPET_VAR_TYPE_LOCAL)
+ {
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_INSTANT_VALUE, default_value,
+ -1);
+ }
+
+ /* Save the changes to the snippet */
+ snippet_set_variable_default_value (priv->snippet, variable_name, default_value);
+}
+
+/**
+ * snippet_vars_store_add_variable_to_snippet:
+ * @vars_store: A #SnippetVarsStore object.
+ * @variable_name: The name of the variable to be added to the snippet.
+ * @get_global: If it should add the global variable with the given name.
+ *
+ * Adds a variable to the snippet. If @get_global is TRUE, but no global variable
+ * with the given name is found, the added variable will be marked as undefined.
+ * If it's FALSE it will add it as local.
+ */
+void
+snippet_vars_store_add_variable_to_snippet (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ gboolean get_global)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeIter iter;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (variable_name != NULL);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (priv->snippet));
+
+ /* We check to see if there is a variable with the same name in the snippet*/
+ if (snippet_has_variable (priv->snippet, variable_name))
+ return;
+
+ /* If we should get the global variable, we just change it's status to TRUE */
+ if (get_global)
+ {
+ if (!get_iter_at_variable (vars_store, &iter, variable_name, SNIPPET_VAR_TYPE_GLOBAL, FALSE))
+ {
+ /* If we didn't found a global variable with the given name, add one as
+ undefined. */
+ gtk_list_store_prepend (GTK_LIST_STORE (vars_store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_NAME, variable_name,
+ VARS_STORE_COL_TYPE, SNIPPET_VAR_TYPE_GLOBAL,
+ VARS_STORE_COL_DEFAULT_VALUE, "",
+ VARS_STORE_COL_INSTANT_VALUE, "",
+ VARS_STORE_COL_IN_SNIPPET, TRUE,
+ VARS_STORE_COL_UNDEFINED, TRUE,
+ -1);
+ }
+ else
+ {
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_IN_SNIPPET, TRUE,
+ -1);
+ }
+ }
+ /* If not, we just add a local variable */
+ else
+ {
+ gtk_list_store_prepend (GTK_LIST_STORE (vars_store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_NAME, variable_name,
+ VARS_STORE_COL_TYPE, SNIPPET_VAR_TYPE_LOCAL,
+ VARS_STORE_COL_DEFAULT_VALUE, "",
+ VARS_STORE_COL_INSTANT_VALUE, "",
+ VARS_STORE_COL_IN_SNIPPET, TRUE,
+ VARS_STORE_COL_UNDEFINED, FALSE,
+ -1);
+ }
+
+ snippet_add_variable (priv->snippet, variable_name, "", get_global);
+}
+
+/**
+ * snippet_vars_store_remove_variable_from_snippet:
+ * @vars_store: A #SnippetVarsStore object.
+ * @variable_name: The name of the variable to be removed from the snippet.
+ *
+ * If the variable to be removed is global, it's in_snippet field will just be set
+ * to FALSE. If the variable to be removed is local, we will also delete the row
+ * from the store. If the variable to be removed is global, but undefined, it will also
+ * delete the row.
+ */
+void
+snippet_vars_store_remove_variable_from_snippet (SnippetVarsStore *vars_store,
+ const gchar *variable_name)
+{
+ SnippetVarsStorePrivate *priv = NULL;
+ GtkTreeIter iter;
+ SnippetVariableType type;
+ gboolean undefined = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET_VARS_STORE (vars_store));
+ g_return_if_fail (variable_name != NULL);
+ priv = ANJUTA_SNIPPET_VARS_STORE_GET_PRIVATE (vars_store);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (priv->snippet));
+
+ /* We get the iter at the requested variable */
+ if (!get_iter_at_variable (vars_store, &iter, variable_name, SNIPPET_VAR_TYPE_ANY, TRUE))
+ return;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (vars_store), &iter,
+ VARS_STORE_COL_TYPE, &type,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ -1);
+
+ /* If it's local or global, but undefined, we remove the entry from the store */
+ if (type == SNIPPET_VAR_TYPE_LOCAL || undefined)
+ {
+ gtk_list_store_remove (GTK_LIST_STORE (vars_store), &iter);
+ }
+ /* If it's global we just set it's in_snippet field to FALSE */
+ else
+ {
+ g_return_if_fail (type == SNIPPET_VAR_TYPE_GLOBAL);
+
+ gtk_list_store_set (GTK_LIST_STORE (vars_store), &iter,
+ VARS_STORE_COL_IN_SNIPPET, FALSE,
+ VARS_STORE_COL_DEFAULT_VALUE, "",
+ -1);
+ }
+
+ /* Finally, remove the variable from the snippet. */
+ snippet_remove_variable (priv->snippet, variable_name);
+}
diff --git a/plugins/snippets-manager/snippet-variables-store.h b/plugins/snippets-manager/snippet-variables-store.h
new file mode 100644
index 0000000..94afbdf
--- /dev/null
+++ b/plugins/snippets-manager/snippet-variables-store.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippet-variables-store.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPET_VARIABLES_STORE_H__
+#define __SNIPPET_VARIABLES_STORE_H__
+
+#include "snippet.h"
+#include "snippets-db.h"
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SnippetVarsStore SnippetVarsStore;
+typedef struct _SnippetVarsStorePrivate SnippetVarsStorePrivate;
+typedef struct _SnippetVarsStoreClass SnippetVarsStoreClass;
+
+#define ANJUTA_TYPE_SNIPPET_VARS_STORE (snippet_vars_store_get_type ())
+#define ANJUTA_SNIPPET_VARS_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPET_VARS_STORE, SnippetVarsStore))
+#define ANJUTA_SNIPPET_VARS_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPET_VARS_STORE, SnippetsVarsStoreClass))
+#define ANJUTA_IS_SNIPPET_VARS_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPET_VARS_STORE))
+#define ANJUTA_IS_SNIPPET_VARS_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPET_VARS_STORE))
+
+typedef enum
+{
+ SNIPPET_VAR_TYPE_LOCAL = 0,
+ SNIPPET_VAR_TYPE_GLOBAL,
+ SNIPPET_VAR_TYPE_ANY
+} SnippetVariableType;
+
+/**
+ * @VARS_STORE_COL_NAME: A #gchar * representing the name of the variable
+ * @VARS_STORE_COL_TYPE: If the variable is global or local. See #SnippetVariableType.
+ * @VARS_STORE_COL_DEFAULT_VALUE: The default value for a local or inserted global variable,
+ * or an empty string.
+ * @VARS_STORE_COL_INSTANT_VALUE: The instant value for a global variable or the default value
+ * for a local variable.
+ * @VARS_STORE_COL_IN_SNIPPET: A #gboolean. If TRUE then the variable is inserted in the snippet.
+ * This is always TRUE for local variables.
+ * @VARS_STORE_COL_UNDEFINED: If the variable type is global and the database doesn't store an entry
+ * for it, this field is TRUE.
+ */
+enum
+{
+ VARS_STORE_COL_NAME = 0,
+ VARS_STORE_COL_TYPE,
+ VARS_STORE_COL_DEFAULT_VALUE,
+ VARS_STORE_COL_INSTANT_VALUE,
+ VARS_STORE_COL_IN_SNIPPET,
+ VARS_STORE_COL_UNDEFINED,
+ VARS_STORE_COL_N
+};
+
+struct _SnippetVarsStore
+{
+ GtkListStore parent;
+
+ /*< private >*/
+ SnippetVarsStorePrivate *priv;
+};
+
+struct _SnippetVarsStoreClass
+{
+ GtkListStoreClass parent_class;
+
+};
+
+GType snippet_vars_store_get_type (void) G_GNUC_CONST;
+SnippetVarsStore * snippet_vars_store_new (void);
+
+void snippet_vars_store_load (SnippetVarsStore *vars_store,
+ SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet);
+void snippet_vars_store_unload (SnippetVarsStore *vars_store);
+void snippet_vars_store_set_variable_name (SnippetVarsStore *vars_store,
+ const gchar *old_variable_name,
+ const gchar *new_variable_name);
+void snippet_vars_store_set_variable_type (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ SnippetVariableType new_type);
+void snippet_vars_store_set_variable_default (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ const gchar *default_value);
+void snippet_vars_store_add_variable_to_snippet (SnippetVarsStore *vars_store,
+ const gchar *variable_name,
+ gboolean get_global);
+void snippet_vars_store_remove_variable_from_snippet (SnippetVarsStore *vars_store,
+ const gchar *variable_name);
+G_END_DECLS
+
+#endif /* __SNIPPET_VARIABLES_STORE_H__ */
diff --git a/plugins/snippets-manager/snippet.c b/plugins/snippets-manager/snippet.c
new file mode 100644
index 0000000..1e53dde
--- /dev/null
+++ b/plugins/snippets-manager/snippet.c
@@ -0,0 +1,1226 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippet.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+#include "snippet.h"
+#include "snippets-db.h"
+#include <gio/gio.h>
+#include <libanjuta/anjuta-debug.h>
+
+#define SNIPPET_VARIABLE_START(text, index) (text[index] == '$' && text[index + 1] == '{')
+#define SNIPPET_VARIABLE_END(text, index) (text[index] == '}')
+
+#define STRING_CUR_POSITION(string) string->len
+
+#define END_CURSOR_VARIABLE_NAME "END_CURSOR_POSITION"
+#define LANGUAGE_SEPARATOR '/'
+
+/**
+ * SnippetVariable:
+ * @variable_name: the name of the variable.
+ * @default_value: the default value as it will be inserted in the code.
+ * @is_global: if the variable is global accross the SnippetDB. Eg: username or email.
+ * @cur_valaue_len: If the snippet was computed recently, it represents the length of the current variable
+ * (default or global variable)
+ * @relative_positions: the relative positions from the start of the snippet code each instance of
+ * this variable has.
+ *
+ * The snippet variable structure.
+ *
+ **/
+typedef struct _AnjutaSnippetVariable
+{
+ gchar* variable_name;
+ gchar* default_value;
+ gboolean is_global;
+
+ gint cur_value_len;
+ GPtrArray* relative_positions;
+
+} AnjutaSnippetVariable;
+
+
+#define ANJUTA_SNIPPET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_SNIPPET, AnjutaSnippetPrivate))
+
+struct _AnjutaSnippetPrivate
+{
+ gchar* trigger_key;
+ GList* snippet_languages;
+ gchar* snippet_name;
+
+ gchar* snippet_content;
+
+ GList* variables;
+ GList* keywords;
+
+ gint cur_value_end_position;
+
+ gboolean default_computed;
+};
+
+
+G_DEFINE_TYPE (AnjutaSnippet, snippet, G_TYPE_OBJECT);
+
+static void
+snippet_dispose (GObject* snippet)
+{
+ AnjutaSnippet* anjuta_snippet = ANJUTA_SNIPPET (snippet);
+ GList* iter = NULL;
+ gpointer p;
+ AnjutaSnippetVariable* cur_snippet_var;
+
+ /* Delete the trigger_key, snippet_language, snippet_name and snippet_content fields */
+ g_free (anjuta_snippet->priv->trigger_key);
+ anjuta_snippet->priv->trigger_key = NULL;
+ g_free (anjuta_snippet->priv->snippet_name);
+ anjuta_snippet->priv->snippet_name = NULL;
+ g_free (anjuta_snippet->priv->snippet_content);
+ anjuta_snippet->priv->snippet_content = NULL;
+
+ /* Delete the languages */
+ for (iter = g_list_first (anjuta_snippet->priv->snippet_languages); iter != NULL; iter = g_list_next (iter))
+ {
+ p = iter->data;
+ g_free (p);
+ }
+ g_list_free (anjuta_snippet->priv->snippet_languages);
+ anjuta_snippet->priv->snippet_languages = NULL;
+
+ /* Delete the keywords */
+ for (iter = g_list_first (anjuta_snippet->priv->keywords); iter != NULL; iter = g_list_next (iter))
+ {
+ p = iter->data;
+ g_free (p);
+ }
+ g_list_free (anjuta_snippet->priv->keywords);
+ anjuta_snippet->priv->keywords = NULL;
+
+ /* Delete the snippet variables */
+ for (iter = g_list_first (anjuta_snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet_var = (AnjutaSnippetVariable *)iter->data;
+
+ g_free (cur_snippet_var->variable_name);
+ g_free (cur_snippet_var->default_value);
+ g_ptr_array_unref (cur_snippet_var->relative_positions);
+
+ g_free (cur_snippet_var);
+ }
+ g_list_free (anjuta_snippet->priv->variables);
+
+ G_OBJECT_CLASS (snippet_parent_class)->dispose (snippet);
+}
+
+static void
+snippet_finalize (GObject* snippet)
+{
+ G_OBJECT_CLASS (snippet_parent_class)->finalize (snippet);
+}
+
+static void
+snippet_class_init (AnjutaSnippetClass* klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ snippet_parent_class = g_type_class_peek_parent (klass);
+ object_class->dispose = snippet_dispose;
+ object_class->finalize = snippet_finalize;
+
+ g_type_class_add_private (klass, sizeof (AnjutaSnippetPrivate));
+}
+
+static void
+snippet_init (AnjutaSnippet* snippet)
+{
+ AnjutaSnippetPrivate* priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ snippet->priv = priv;
+ snippet->parent_snippets_group = NULL;
+
+ /* Initialize the private field */
+ snippet->priv->trigger_key = NULL;
+ snippet->priv->snippet_languages = NULL;
+ snippet->priv->snippet_name = NULL;
+ snippet->priv->snippet_content = NULL;
+ snippet->priv->variables = NULL;
+ snippet->priv->keywords = NULL;
+
+ snippet->priv->cur_value_end_position = -1;
+ snippet->priv->default_computed = FALSE;
+}
+
+/**
+ * snippet_new:
+ * @trigger_key: The trigger key of the snippet.
+ * @snippet_languages: A list with the languages for which the snippet is meant.
+ * @snippet_name: A short, intuitive name of the snippet.
+ * @snippet_content: The actual content of the snippet.
+ * @variables_names: A #GList with the variable names.
+ * @variables_default_values: A #GList with the default values of the variables.
+ * @variable_globals: A #GList with gboolean's that state if the variable is global or not.
+ * @keywords: A #GList with the keywords of the snippet.
+ *
+ * Creates a new snippet object. All the data given to this function will be copied.
+ *
+ * Returns: The new #AnjutaSnippet or NULL on failure.
+ **/
+AnjutaSnippet*
+snippet_new (const gchar* trigger_key,
+ GList* snippet_languages,
+ const gchar* snippet_name,
+ const gchar* snippet_content,
+ GList* variable_names,
+ GList* variable_default_values,
+ GList* variable_globals,
+ GList* keywords)
+{
+ AnjutaSnippet* snippet = NULL;
+ GList *iter1 = NULL, *iter2 = NULL, *iter3 = NULL;
+ gchar* temporary_string_holder = NULL;
+ AnjutaSnippetVariable* cur_snippet_var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (trigger_key != NULL, NULL);
+ g_return_val_if_fail (snippet_name != NULL, NULL);
+ g_return_val_if_fail (snippet_content != NULL, NULL);
+ g_return_val_if_fail (g_list_length (variable_names) == g_list_length (variable_default_values),
+ NULL);
+ g_return_val_if_fail (g_list_length (variable_names) == g_list_length (variable_globals),
+ NULL);
+
+ /* Initialize the object */
+ snippet = ANJUTA_SNIPPET (g_object_new (snippet_get_type (), NULL));
+
+ /* Make a copy of the given strings */
+ snippet->priv->trigger_key = g_strdup (trigger_key);
+ snippet->priv->snippet_name = g_strdup (snippet_name);
+ snippet->priv->snippet_content = g_strdup (snippet_content);
+
+ /* Copy all the snippet languages to a new list */
+ snippet->priv->snippet_languages = NULL;
+ for (iter1 = g_list_first (snippet_languages); iter1 != NULL; iter1 = g_list_next (iter1))
+ {
+ temporary_string_holder = g_strdup ((const gchar *)iter1->data);
+ snippet->priv->snippet_languages = g_list_append (snippet->priv->snippet_languages,
+ temporary_string_holder);
+ }
+
+ /* Copy all the keywords to a new list */
+ snippet->priv->keywords = NULL;
+ for (iter1 = g_list_first (keywords); iter1 != NULL; iter1 = g_list_next (iter1))
+ {
+ temporary_string_holder = g_strdup ((gchar *)iter1->data);
+ snippet->priv->keywords = g_list_append (snippet->priv->keywords, temporary_string_holder);
+ }
+
+ /* Make a list of variables */
+ snippet->priv->variables = NULL;
+ iter1 = g_list_first (variable_names);
+ iter2 = g_list_first (variable_default_values);
+ iter3 = g_list_first (variable_globals);
+ while (iter1 != NULL && iter2 != NULL && iter3 != NULL)
+ {
+ cur_snippet_var = g_malloc (sizeof (AnjutaSnippetVariable));
+
+ cur_snippet_var->variable_name = g_strdup ((gchar*)iter1->data);
+ cur_snippet_var->default_value = g_strdup ((gchar*)iter2->data);
+ cur_snippet_var->is_global = GPOINTER_TO_INT (iter3->data);
+
+ cur_snippet_var->cur_value_len = 0;
+ cur_snippet_var->relative_positions = g_ptr_array_new ();
+
+ snippet->priv->variables = g_list_append (snippet->priv->variables, cur_snippet_var);
+
+ iter1 = g_list_next (iter1);
+ iter2 = g_list_next (iter2);
+ iter3 = g_list_next (iter3);
+ }
+ DEBUG_PRINT ("Snippet %s created.\n", snippet_name);
+ return snippet;
+}
+
+AnjutaSnippet *
+snippet_copy (AnjutaSnippet *snippet)
+{
+ GList *languages = NULL, *keywords = NULL, *variable_names = NULL, *variable_defaults = NULL,
+ *variable_globals = NULL;
+ const gchar *trigger = NULL, *name = NULL, *content = NULL;
+ AnjutaSnippet *copied_snippet = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ trigger = snippet_get_trigger_key (snippet);
+ name = snippet_get_name (snippet);
+ content = snippet_get_content (snippet);
+
+ keywords = snippet_get_keywords_list (snippet);
+ languages = (GList *)snippet_get_languages (snippet);
+ variable_names = snippet_get_variable_names_list (snippet);
+ variable_defaults = snippet_get_variable_defaults_list (snippet);
+ variable_globals = snippet_get_variable_globals_list (snippet);
+
+ copied_snippet = snippet_new (trigger, languages, name, content,
+ variable_names, variable_defaults, variable_globals,
+ keywords);
+
+ g_list_free (keywords);
+ g_list_free (variable_names);
+ g_list_free (variable_defaults);
+ g_list_free (variable_globals);
+
+ copied_snippet->parent_snippets_group = snippet->parent_snippets_group;
+
+ return copied_snippet;
+}
+
+/**
+ * snippet_get_trigger_key:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * Gets the trigger-key of the snippet.
+ *
+ * Returns: The snippet-key or NULL if @snippet is invalid.
+ **/
+const gchar*
+snippet_get_trigger_key (AnjutaSnippet* snippet)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ return priv->trigger_key;
+}
+
+void
+snippet_set_trigger_key (AnjutaSnippet *snippet,
+ const gchar *new_trigger_key)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (new_trigger_key != NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ g_free (priv->trigger_key);
+ priv->trigger_key = g_strdup (new_trigger_key);
+}
+
+/**
+ * snippet_get_language:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * Gets the supported languages of the snippet.
+ *
+ * Returns: A GList with strings representing the supported languages or NULL if @snippet is invalid.
+ **/
+const GList *
+snippet_get_languages (AnjutaSnippet* snippet)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ return snippet->priv->snippet_languages;
+}
+
+/**
+ * snippet_get_languages_string:
+ * @snippet: A #AnjutaSnippets object.
+ *
+ * Formats a string with the supported languages. For example, if the snippet
+ * is supported by C, C++ and Java, the returned string should look like:
+ * "C/C++/Java".
+ *
+ * Returns: The above mentioned string or NULL on error. The memory should be free'd.
+ */
+gchar*
+snippet_get_languages_string (AnjutaSnippet *snippet)
+{
+ GString *languages_string = NULL;
+ GList *languages = NULL, *iter = NULL;
+ gchar *cur_language = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ g_return_val_if_fail (snippet->priv != NULL, NULL);
+ languages = snippet->priv->snippet_languages;
+ languages_string = g_string_new ("");
+
+ for (iter = g_list_first (languages); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_language = (gchar *)iter->data;
+ languages_string = g_string_append (languages_string, cur_language);
+ languages_string = g_string_append_c (languages_string, LANGUAGE_SEPARATOR);
+ }
+ /* We delete the last '/' */
+ languages_string = g_string_set_size (languages_string, languages_string->len - 1);
+
+ return g_string_free (languages_string, FALSE);
+}
+
+/**
+ * snippet_has_language:
+ * @snippet: A #AnjutaSnippet object.
+ * @language: The language for which the query is done.
+ *
+ * Tests if the snippet is meant for the given language.
+ *
+ * Returns: TRUE if the snippet is meant for the given language.
+ */
+gboolean
+snippet_has_language (AnjutaSnippet *snippet,
+ const gchar *language)
+{
+ GList *languages = NULL, *iter = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+ g_return_val_if_fail (snippet->priv != NULL, FALSE);
+ g_return_val_if_fail (language != NULL, FALSE);
+ languages = snippet->priv->snippet_languages;
+
+ for (iter = g_list_first (languages); iter != NULL; iter = g_list_next (iter))
+ {
+ if (!g_strcmp0 ((gchar *)iter->data, language))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippet_get_any_language:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * Returns any of the languages that is supported by the given snippet. This should
+ * be useful for stuff like removing a snippet from a snippets-group.
+ *
+ * Returns: Any of the languages supported by the snippet (or NULL if an error occured).
+ */
+const gchar*
+snippet_get_any_language (AnjutaSnippet *snippet)
+{
+ GList *first_language_node = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ g_return_val_if_fail (snippet->priv != NULL, NULL);
+
+ first_language_node = g_list_first (snippet->priv->snippet_languages);
+ if (first_language_node == NULL)
+ return NULL;
+
+ return (const gchar *)first_language_node->data;
+}
+
+/**
+ * snippet_add_language:
+ * @snippet: A #AnjutaSnippet object
+ * @language: The language for which we want to add support to the snippet.
+ *
+ * By adding a language to a snippet, we mean that now the snippet can be used in that
+ * programming language.
+ */
+void
+snippet_add_language (AnjutaSnippet *snippet,
+ const gchar *language)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (snippet->priv != NULL);
+
+ if (snippet_has_language (snippet, language))
+ return;
+
+ snippet->priv->snippet_languages = g_list_append (snippet->priv->snippet_languages,
+ g_strdup (language));
+}
+
+
+/**
+ * snippet_remove_language:
+ * @snippet: A #AnjutaSnippet object
+ * @language: The language for which we want to remove support from the snippet.
+ *
+ * The snippet won't be able to be used for the removed programming language.
+ */
+void
+snippet_remove_language (AnjutaSnippet *snippet,
+ const gchar *language)
+{
+ GList *iter = NULL;
+ gpointer p = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (snippet->priv != NULL);
+ g_return_if_fail (language != NULL);
+
+ for (iter = g_list_first (snippet->priv->snippet_languages); iter != NULL; iter = g_list_next (iter))
+ if (!g_strcmp0 ((gchar *)iter->data, language))
+ {
+ p = iter->data;
+ snippet->priv->snippet_languages = g_list_remove (snippet->priv->snippet_languages,
+ iter->data);
+ g_free (p);
+ }
+
+}
+
+/**
+ * snippet_get_name:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * Gets the name of the snippet.
+ *
+ * Returns: The snippet name or NULL if @snippet is invalid.
+ **/
+const gchar*
+snippet_get_name (AnjutaSnippet* snippet)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ return priv->snippet_name;
+}
+
+/**
+ * snippet_set_name:
+ * @snippet: An #AnjutaSnippet object
+ * @new_name: The new name assigned to the snippet.
+ */
+void
+snippet_set_name (AnjutaSnippet *snippet,
+ const gchar *new_name)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (new_name != NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ priv->snippet_name = g_strdup (new_name);
+}
+
+/**
+ * snippet_get_keywords_list:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * Gets the list with the keywords of the snippet. The GList* should be free'd, but not
+ * the containing data.
+ *
+ * Returns: A #GList of #gchar* with the keywords of the snippet or NULL if
+ * @snippet is invalid.
+ **/
+GList*
+snippet_get_keywords_list (AnjutaSnippet* snippet)
+{
+ GList *iter = NULL;
+ GList *keywords_copy = NULL;
+ gchar *cur_keyword;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ for (iter = g_list_first (snippet->priv->keywords); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_keyword = (gchar *)iter->data;
+ keywords_copy = g_list_append (keywords_copy, cur_keyword);
+ }
+
+ return keywords_copy;
+}
+
+/**
+ * snippet_set_keywords_list:
+ * @snippet: An #AnjutaSnippet object.
+ * @keywords_list: The new list of keywords for the snippet.
+ *
+ * Sets a new keywords list.
+ */
+void
+snippet_set_keywords_list (AnjutaSnippet *snippet,
+ const GList *keywords_list)
+{
+ GList *iter = NULL;
+ AnjutaSnippetPrivate *priv = NULL;
+ gchar *cur_keyword = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ /* Delete the old keywords list */
+ for (iter = g_list_first (priv->keywords); iter != NULL; iter = g_list_next (iter))
+ {
+ g_free (iter->data);
+ }
+ g_list_free (g_list_first (priv->keywords));
+ priv->keywords = NULL;
+
+ /* Copy over the new list */
+ for (iter = g_list_first ((GList *)keywords_list); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_keyword = g_strdup ((const gchar *)iter->data);
+ priv->keywords = g_list_append (priv->keywords, cur_keyword);
+ }
+
+}
+
+/**
+ * snippet_get_variable_names_list:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * A list with the variables names, in the order they should be edited. The GList*
+ * returned should be freed, but not the containing data.
+ *
+ * Returns: The variable names list or NULL if the @snippet is invalid.
+ **/
+GList*
+snippet_get_variable_names_list (AnjutaSnippet* snippet)
+{
+ GList *iter = NULL;
+ GList *variable_names = NULL;
+ AnjutaSnippetVariable *cur_snippet_var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet_var = (AnjutaSnippetVariable *)iter->data;
+ variable_names = g_list_append (variable_names, cur_snippet_var->variable_name);
+ }
+
+ return variable_names;
+}
+
+/**
+ * snippet_get_variable_defaults_list:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * A list with the variables default values, in the order they should be edited.
+ * The GList* returned should be freed, but not the containing data.
+ *
+ * Returns: The variables default values #GList or NULL if @snippet is invalid.
+ **/
+GList*
+snippet_get_variable_defaults_list (AnjutaSnippet* snippet)
+{
+ GList *iter = NULL;
+ GList *variable_defaults = NULL;
+ AnjutaSnippetVariable *cur_snippet_var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet_var = (AnjutaSnippetVariable *)iter->data;
+ variable_defaults = g_list_append (variable_defaults, cur_snippet_var->default_value);
+ }
+
+ return variable_defaults;
+}
+
+/**
+ * snippet_get_variable_globals_list:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * A list with the variables global truth value, in the order they should be edited.
+ * The GList* returned should be freed.
+ *
+ * Returns: The variables global truth values #GList or NULL if @snippet is invalid.
+ **/
+GList*
+snippet_get_variable_globals_list (AnjutaSnippet* snippet)
+{
+ GList *iter = NULL;
+ GList *variable_globals = NULL;
+ gboolean temp_holder = FALSE;
+ AnjutaSnippetVariable *cur_snippet_var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet_var = (AnjutaSnippetVariable *)iter->data;
+ temp_holder = cur_snippet_var->is_global;
+ variable_globals = g_list_append (variable_globals, GINT_TO_POINTER (temp_holder));
+ }
+
+ return variable_globals;
+}
+
+static AnjutaSnippetVariable *
+get_snippet_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name)
+{
+ GList *iter = NULL;
+ AnjutaSnippetPrivate *priv = NULL;
+ AnjutaSnippetVariable *cur_var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ for (iter = g_list_first (priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_var = (AnjutaSnippetVariable *)iter->data;
+ g_return_val_if_fail (cur_var != NULL, NULL);
+
+ if (!g_strcmp0 (cur_var->variable_name, variable_name))
+ return cur_var;
+ }
+
+ return NULL;
+}
+
+gboolean
+snippet_has_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name)
+{
+ return (get_snippet_variable (snippet, variable_name) != NULL);
+}
+
+/**
+ * snippet_add_variable:
+ * @snippet: An #AnjutaSnippet object.
+ * @variable_name: The added variable name.
+ * @default_value: The default value of the added snippet.
+ * @is_global: If the added variable is global.
+ *
+ * If there is a variable with the same name, it won't add the variable.
+ */
+void
+snippet_add_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ const gchar *default_value,
+ gboolean is_global)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+ AnjutaSnippetVariable *added_var = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (variable_name != NULL);
+ g_return_if_fail (default_value != NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ /* Check if we have a variable with the same name */
+ if (snippet_has_variable (snippet, variable_name))
+ return;
+
+ /* Create a new variable */
+ added_var = g_malloc (sizeof (AnjutaSnippetVariable));
+ added_var->variable_name = g_strdup (variable_name);
+ added_var->default_value = g_strdup (default_value);
+ added_var->is_global = is_global;
+ added_var->cur_value_len = 0;
+ added_var->relative_positions = g_ptr_array_new ();
+
+ priv->variables = g_list_prepend (priv->variables, added_var);
+}
+
+void
+snippet_remove_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+ AnjutaSnippetVariable *cur_var = NULL;
+ GList *iter = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (variable_name != NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ for (iter = g_list_first (priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_var = (AnjutaSnippetVariable *)iter->data;
+ g_return_if_fail (cur_var != NULL);
+
+ if (!g_strcmp0 (cur_var->variable_name, variable_name))
+ {
+ g_free (cur_var->variable_name);
+ g_free (cur_var->default_value);
+ g_ptr_array_free (cur_var->relative_positions, TRUE);
+
+ priv->variables = g_list_remove_link (priv->variables, iter);
+
+ g_free (cur_var);
+ return;
+ }
+ }
+
+}
+
+void
+snippet_set_variable_name (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ const gchar *new_variable_name)
+{
+ AnjutaSnippetVariable *var = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (variable_name != NULL);
+ g_return_if_fail (new_variable_name != NULL);
+
+ var = get_snippet_variable (snippet, variable_name);
+ if (var == NULL)
+ return;
+
+ g_free (var->variable_name);
+ var->variable_name = g_strdup (new_variable_name);
+}
+
+const gchar*
+snippet_get_variable_default_value (AnjutaSnippet *snippet,
+ const gchar *variable_name)
+{
+ AnjutaSnippetVariable *var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ g_return_val_if_fail (variable_name != NULL, NULL);
+
+ var = get_snippet_variable (snippet, variable_name);
+ g_return_val_if_fail (var != NULL, NULL);
+
+ return var->default_value;
+}
+
+void
+snippet_set_variable_default_value (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ const gchar *default_value)
+{
+ AnjutaSnippetVariable *var = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (variable_name != NULL);
+ g_return_if_fail (default_value != NULL);
+
+ var = get_snippet_variable (snippet, variable_name);
+ g_return_if_fail (var != NULL);
+
+ g_free (var->default_value);
+ var->default_value = g_strdup (default_value);
+}
+
+gboolean
+snippet_get_variable_global (AnjutaSnippet *snippet,
+ const gchar *variable_name)
+{
+ AnjutaSnippetVariable *var = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+ g_return_val_if_fail (variable_name != NULL, FALSE);
+
+ var = get_snippet_variable (snippet, variable_name);
+ g_return_val_if_fail (var != NULL, FALSE);
+
+ return var->is_global;
+}
+
+void
+snippet_set_variable_global (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ gboolean global)
+{
+ AnjutaSnippetVariable *var = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (variable_name != NULL);
+
+ var = get_snippet_variable (snippet, variable_name);
+ g_return_if_fail (var != NULL);
+
+ var->is_global = global;
+}
+
+/**
+ * snippet_get_content:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * The content of the snippet without it being proccesed.
+ *
+ * Returns: The content of the snippet or NULL if @snippet is invalid.
+ **/
+const gchar*
+snippet_get_content (AnjutaSnippet* snippet)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ return snippet->priv->snippet_content;
+}
+
+
+void
+snippet_set_content (AnjutaSnippet *snippet,
+ const gchar *new_content)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (new_content != NULL);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ g_free (priv->snippet_content);
+ priv->snippet_content = g_strdup (new_content);
+}
+
+static void
+reset_variables (AnjutaSnippet *snippet)
+{
+ GList *iter = NULL;
+ AnjutaSnippetVariable *cur_var = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (snippet->priv != NULL);
+
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_var = (AnjutaSnippetVariable *)iter->data;
+
+ cur_var->cur_value_len = 0;
+ if (cur_var->relative_positions->len > 0)
+ g_ptr_array_remove_range (cur_var->relative_positions,
+ 0, cur_var->relative_positions->len);
+ }
+
+ snippet->priv->cur_value_end_position = -1;
+}
+
+static gchar *
+expand_global_and_default_variables (AnjutaSnippet *snippet,
+ gchar *snippet_text,
+ SnippetsDB *snippets_db)
+{
+ gint snippet_text_size = 0, i = 0, j = 0;
+ GString *buffer = NULL;
+ GList *iter = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ /* We get the snippet_text_size (for iterating) and init the buffer */
+ snippet_text_size = strlen (snippet_text);
+ buffer = g_string_new ("");
+
+ /* We reset the variable */
+ reset_variables (snippet);
+
+ /* We expand the variables to the default value or if they are global variables
+ we query the database for their value. If the database can't answer to our
+ request, we fill them also with their default values */
+ for (i = 0; i < snippet_text_size; i ++)
+ {
+ /* If it's the start of a variable name, we look up the end, get the name
+ and evaluate it. */
+ if (SNIPPET_VARIABLE_START (snippet_text, i))
+ {
+ GString *cur_var_name = NULL;
+ gchar *cur_var_value = NULL;
+ gint cur_var_value_size = 0;
+ AnjutaSnippetVariable *cur_var = NULL;
+
+ /* We search for the variable end */
+ cur_var_name = g_string_new ("");
+ for (j = i + 2; j < snippet_text_size && !SNIPPET_VARIABLE_END (snippet_text, j); j ++)
+ cur_var_name = g_string_append_c (cur_var_name, snippet_text[j]);
+
+ /* We first see if it's the END_CURSOR_POSITION variable */
+ if (!g_strcmp0 (cur_var_name->str, END_CURSOR_VARIABLE_NAME))
+ {
+ snippet->priv->cur_value_end_position = STRING_CUR_POSITION (buffer);
+ g_string_free (cur_var_name, TRUE);
+
+ i = j;
+ continue;
+ }
+
+ /* Look up the variable */
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_var = (AnjutaSnippetVariable *)iter->data;
+ if (!g_strcmp0 (cur_var->variable_name, cur_var_name->str))
+ break;
+ }
+
+ /* If iter is NULL, then we didn't found the variable name */
+ if (!iter)
+ {
+ //cur_var_value = g_strconcat ("${", cur_var_name->str, "}", NULL);
+ buffer = g_string_append_c (buffer, snippet_text[i]);
+
+ g_string_free (cur_var_name, TRUE);
+ continue;
+ }
+ else
+ {
+ /* If it's a global variable, we query the database */
+ if (cur_var->is_global)
+ cur_var_value = snippets_db_get_global_variable (snippets_db,
+ cur_var_name->str);
+
+ /* If we didn't got an answer from the database or if the variable is not
+ global, we get the default value. */
+ if (cur_var_value == NULL)
+ cur_var_value = g_strdup (cur_var->default_value);
+
+ i = j;
+ }
+
+ /* Update the variable data */
+ cur_var_value_size = strlen (cur_var_value);
+ cur_var->cur_value_len = cur_var_value_size;
+ g_ptr_array_add (cur_var->relative_positions,
+ GINT_TO_POINTER (STRING_CUR_POSITION (buffer)));
+
+ /* Append the variable value to the buffer */
+ buffer = g_string_append (buffer, cur_var_value);
+
+ g_free (cur_var_value);
+ g_string_free (cur_var_name, TRUE);
+ }
+ else
+ {
+ buffer = g_string_append_c (buffer, snippet_text[i]);
+ }
+ }
+
+ return g_string_free (buffer, FALSE);
+}
+
+static gchar *
+get_text_with_indentation (const gchar *text,
+ const gchar *indent)
+{
+ gint text_size = 0, i = 0;
+ GString *text_with_indentation = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (indent != NULL, NULL);
+
+ /* Init the text_with_indentation string */
+ text_with_indentation = g_string_new ("");
+
+ text_size = strlen (text);
+ for (i = 0; i < text_size; i ++)
+ {
+ text_with_indentation = g_string_append_c (text_with_indentation, text[i]);
+
+ /* If we go to a new line, we also add the indentation */
+ if (text[i] == '\n')
+ text_with_indentation = g_string_append (text_with_indentation, indent);
+ }
+
+ return g_string_free (text_with_indentation, FALSE);
+}
+
+/**
+ * snippet_get_default_content:
+ * @snippet: A #AnjutaSnippet object.
+ * @snippets_db: A #SnippetsDB object. This is required for filling the global variables.
+ * This can be NULL if the snippet is independent of a #SnippetsDB or if
+ * it doesn't have global variables.
+ * @indent: The indentation of the line where the snippet will be inserted.
+ *
+ * The content of the snippet filled with the default values of the variables.
+ * Every '\n' character will be replaced with a string obtained by concatanating
+ * "\n" with indent.
+ *
+ * Returns: The default content of the snippet or NULL if @snippet is invalid.
+ **/
+gchar*
+snippet_get_default_content (AnjutaSnippet *snippet,
+ GObject *snippets_db_obj,
+ const gchar *indent)
+{
+ gchar* buffer = NULL, *temp = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ /* Get the with indentation */
+ temp = get_text_with_indentation (snippet->priv->snippet_content, indent);
+
+ /* If we should expand the global variables */
+ if (snippets_db_obj && ANJUTA_IS_SNIPPETS_DB (snippets_db_obj))
+ {
+ /* Expand the global variables */
+ buffer = expand_global_and_default_variables (snippet,
+ temp,
+ ANJUTA_SNIPPETS_DB (snippets_db_obj));
+ g_free (temp);
+ }
+ else
+ {
+ buffer = temp;
+ }
+
+ snippet->priv->default_computed = TRUE;
+
+ return buffer;
+}
+
+/**
+ * snippet_get_variable_relative_positions:
+ * @snippet: A #AnjutaSnippet object.
+ *
+ * A GList* of GPtrArray* with the relative positions of all snippet variables instances
+ * from the start of the last computed default content.
+ *
+ * For example, the second element of the GPtrArray found in the first GList node has
+ * the meaning of: the relative position of the second instance of the first variable
+ * in the last computed default content.
+ *
+ * The values in the GPTrArray are gint's.
+ *
+ * If the default content was never computed, the method will return NULL.
+ *
+ * The GList should be free'd, but each GPtrArray should be just unrefed!
+ *
+ * Returns: A #GList with the positions or NULL on failure.
+ **/
+GList*
+snippet_get_variable_relative_positions (AnjutaSnippet* snippet)
+{
+ GList *relative_positions_list = NULL, *iter = NULL;
+ AnjutaSnippetVariable *cur_variable = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ g_return_val_if_fail (snippet->priv != NULL, NULL);
+ g_return_val_if_fail (snippet->priv->default_computed, NULL);
+
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_variable = (AnjutaSnippetVariable *)iter->data;
+
+ relative_positions_list = g_list_append (relative_positions_list,
+ cur_variable->relative_positions);
+ g_ptr_array_ref (cur_variable->relative_positions);
+ }
+
+ return relative_positions_list;
+}
+
+/**
+ * snippet_get_variable_cur_values_len:
+ * @snippet: A #AnjutaSnippet object
+ *
+ * Get's a #GList with the length (#gint) of each of the variables last computed
+ * default value. The order is the same as for #snippet_get_variable_relative_positions.
+ *
+ * The #GList should be free'd.
+ *
+ * Returns: The requested #GList or NULL on failure.
+ */
+GList*
+snippet_get_variable_cur_values_len (AnjutaSnippet *snippet)
+{
+ GList *cur_values_len_list = NULL, *iter = NULL;
+ AnjutaSnippetVariable *cur_variable = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+ g_return_val_if_fail (snippet->priv != NULL, NULL);
+
+ for (iter = g_list_first (snippet->priv->variables); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_variable = (AnjutaSnippetVariable *)iter->data;
+
+ cur_values_len_list = g_list_append (cur_values_len_list,
+ GINT_TO_POINTER (cur_variable->cur_value_len));
+
+ }
+
+ return cur_values_len_list;
+}
+
+gint
+snippet_get_cur_value_end_position (AnjutaSnippet *snippet)
+{
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), -1);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ return priv->cur_value_end_position;
+}
+
+/**
+ * snippet_is_equal:
+ * @snippet: A #AnjutaSnippet object.
+ * @snippet2: A #AnjutaSnippet object.
+ *
+ * Compares @snippet with @snippet2 and returns TRUE if they have the same identifier.
+ *
+ * Returns: TRUE if the snippets have at least one common identifier.
+ */
+gboolean
+snippet_is_equal (AnjutaSnippet *snippet,
+ AnjutaSnippet *snippet2)
+{
+ const gchar *trigger = NULL, *trigger2 = NULL, *cur_lang = NULL;
+ GList *iter = NULL, *languages = NULL;
+ AnjutaSnippetPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+ priv = ANJUTA_SNIPPET_GET_PRIVATE (snippet);
+
+ trigger = snippet_get_trigger_key (snippet);
+ trigger2 = snippet_get_trigger_key (snippet2);
+ languages = (GList *)snippet_get_languages (snippet);
+
+ if (!g_strcmp0 (trigger, trigger2))
+ {
+ for (iter = g_list_first (languages); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_lang = (gchar *)iter->data;
+ if (snippet_has_language (snippet2, cur_lang))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/plugins/snippets-manager/snippet.h b/plugins/snippets-manager/snippet.h
new file mode 100644
index 0000000..f8c12df
--- /dev/null
+++ b/plugins/snippets-manager/snippet.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippet.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __ANJUTA_SNIPPET_H__
+#define __ANJUTA_SNIPPET_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct _AnjutaSnippet AnjutaSnippet;
+typedef struct _AnjutaSnippetPrivate AnjutaSnippetPrivate;
+typedef struct _AnjutaSnippetClass AnjutaSnippetClass;
+
+#define ANJUTA_TYPE_SNIPPET (snippet_get_type ())
+#define ANJUTA_SNIPPET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPET, AnjutaSnippet))
+#define ANJUTA_SNIPPET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPET, AnjutaSnippetClass))
+#define ANJUTA_IS_SNIPPET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPET))
+#define ANJUTA_IS_SNIPPET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPET))
+
+struct _AnjutaSnippet
+{
+ GObject parent_instance;
+
+ /* A pointer to an AnjutaSnippetsGroup object. */
+ GObject *parent_snippets_group;
+
+ /*< private >*/
+ AnjutaSnippetPrivate *priv;
+};
+
+struct _AnjutaSnippetClass
+{
+ GObjectClass parent_class;
+
+};
+
+GType snippet_get_type (void) G_GNUC_CONST;
+AnjutaSnippet* snippet_new (const gchar *trigger_key,
+ GList *snippet_language,
+ const gchar *snippet_name,
+ const gchar *snippet_content,
+ GList *variable_names,
+ GList *variable_default_values,
+ GList *variable_globals,
+ GList *keywords);
+AnjutaSnippet* snippet_copy (AnjutaSnippet *snippet);
+const gchar* snippet_get_trigger_key (AnjutaSnippet *snippet);
+void snippet_set_trigger_key (AnjutaSnippet *snippet,
+ const gchar *new_trigger_key);
+const GList* snippet_get_languages (AnjutaSnippet *snippet);
+gchar* snippet_get_languages_string (AnjutaSnippet *snippet);
+const gchar* snippet_get_any_language (AnjutaSnippet *snippet);
+gboolean snippet_has_language (AnjutaSnippet *snippet,
+ const gchar *language);
+void snippet_add_language (AnjutaSnippet *snippet,
+ const gchar *language);
+void snippet_remove_language (AnjutaSnippet *snippet,
+ const gchar *language);
+const gchar* snippet_get_name (AnjutaSnippet *snippet);
+void snippet_set_name (AnjutaSnippet *snippet,
+ const gchar *new_name);
+GList* snippet_get_keywords_list (AnjutaSnippet *snippet);
+void snippet_set_keywords_list (AnjutaSnippet *snippet,
+ const GList *keywords_list);
+GList* snippet_get_variable_names_list (AnjutaSnippet *snippet);
+GList* snippet_get_variable_defaults_list (AnjutaSnippet *snippet);
+GList* snippet_get_variable_globals_list (AnjutaSnippet *snippet);
+gboolean snippet_has_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name);
+void snippet_add_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ const gchar *default_value,
+ gboolean is_global);
+void snippet_remove_variable (AnjutaSnippet *snippet,
+ const gchar *variable_name);
+void snippet_set_variable_name (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ const gchar *new_variable_name);
+const gchar* snippet_get_variable_default_value (AnjutaSnippet *snippet,
+ const gchar *variable_name);
+void snippet_set_variable_default_value (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ const gchar *default_value);
+gboolean snippet_get_variable_global (AnjutaSnippet *snippet,
+ const gchar *variable_name);
+void snippet_set_variable_global (AnjutaSnippet *snippet,
+ const gchar *variable_name,
+ gboolean global);
+const gchar* snippet_get_content (AnjutaSnippet *snippet);
+void snippet_set_content (AnjutaSnippet *snippet,
+ const gchar *new_content);
+gchar* snippet_get_default_content (AnjutaSnippet *snippet,
+ GObject *snippets_db_obj,
+ const gchar *indent);
+GList* snippet_get_variable_relative_positions (AnjutaSnippet *snippet);
+GList* snippet_get_variable_cur_values_len (AnjutaSnippet *snippet);
+gint snippet_get_cur_value_end_position (AnjutaSnippet *snippet);
+gboolean snippet_is_equal (AnjutaSnippet *snippet,
+ AnjutaSnippet *snippet2);
+
+G_END_DECLS
+
+#endif /* __ANJUTA_SNIPPET_H__ */
diff --git a/plugins/snippets-manager/snippets-browser.c b/plugins/snippets-manager/snippets-browser.c
new file mode 100644
index 0000000..69518cc
--- /dev/null
+++ b/plugins/snippets-manager/snippets-browser.c
@@ -0,0 +1,1190 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-browser.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-browser.h"
+#include "snippets-group.h"
+#include "snippets-editor.h"
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-document.h>
+#include <libanjuta/interfaces/ianjuta-editor.h>
+#include <libanjuta/interfaces/ianjuta-editor-language.h>
+#include <libanjuta/interfaces/ianjuta-language.h>
+
+#define BROWSER_UI PACKAGE_DATA_DIR"/glade/snippets-browser.ui"
+#define TOOLTIP_SIZE 200
+#define NEW_SNIPPETS_GROUP_NAME "New Snippets Group"
+
+#define ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_SNIPPETS_BROWSER, SnippetsBrowserPrivate))
+
+struct _SnippetsBrowserPrivate
+{
+ SnippetsEditor *snippets_editor;
+ GtkTreeView *snippets_view;
+
+ SnippetsDB *snippets_db;
+
+ GtkButton *add_button;
+ GtkButton *delete_button;
+ GtkButton *insert_button;
+ GtkToggleButton *edit_button;
+
+ GtkVBox *snippets_view_vbox;
+ GtkScrolledWindow *snippets_view_cont;
+
+ GtkWidget *browser_hpaned;
+
+ GtkTreeModel *filter;
+
+ gboolean maximized;
+
+ SnippetsInteraction *snippets_interaction;
+
+};
+
+enum
+{
+ SNIPPETS_VIEW_COL_NAME = 0,
+ SNIPPETS_VIEW_COL_TRIGGER,
+ SNIPPETS_VIEW_COL_LANGUAGES
+};
+
+
+G_DEFINE_TYPE (SnippetsBrowser, snippets_browser, GTK_TYPE_HBOX);
+
+static void
+snippets_browser_class_init (SnippetsBrowserClass* klass)
+{
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER_CLASS (klass));
+
+ /* The SnippetsBrowser asks for a maximize. If a maximize is possible,
+ the snippets_browser_show_editor should be called. */
+ g_signal_new ("maximize-request",
+ ANJUTA_TYPE_SNIPPETS_BROWSER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SnippetsBrowserClass, maximize_request),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ NULL);
+
+ /* Like above, the SnippetsBrowser asks for a unmaximize. */
+ g_signal_new ("unmaximize-request",
+ ANJUTA_TYPE_SNIPPETS_BROWSER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SnippetsBrowserClass, unmaximize_request),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ NULL);
+
+ g_type_class_add_private (klass, sizeof (SnippetsBrowserPrivate));
+}
+
+static void
+snippets_browser_init (SnippetsBrowser* snippets_browser)
+{
+ SnippetsBrowserPrivate* priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ snippets_browser->priv = priv;
+ snippets_browser->show_only_document_language_snippets = TRUE;
+ snippets_browser->anjuta_shell = NULL;
+
+ /* Initialize the private field */
+ priv->snippets_view = NULL;
+ priv->snippets_db = NULL;
+ priv->add_button = NULL;
+ priv->delete_button = NULL;
+ priv->insert_button = NULL;
+ priv->edit_button = NULL;
+ priv->snippets_view_cont = NULL;
+ priv->snippets_view_vbox = NULL;
+ priv->browser_hpaned = NULL;
+ priv->filter = NULL;
+ priv->maximized = FALSE;
+ priv->snippets_interaction = NULL;
+
+}
+
+/* Handlers */
+
+static void on_add_button_clicked (GtkButton *add_button,
+ gpointer user_data);
+static void on_delete_button_clicked (GtkButton *delete_button,
+ gpointer user_data);
+static void on_insert_button_clicked (GtkButton *insert_button,
+ gpointer user_data);
+static void on_edit_button_toggled (GtkToggleButton *edit_button,
+ gpointer user_data);
+static void on_snippets_view_row_activated (GtkTreeView *snippets_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer user_data);
+static gboolean on_snippets_view_query_tooltip (GtkWidget *snippets_view,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data);
+static void on_name_changed (GtkCellRendererText *renderer,
+ gchar *path_string,
+ gchar *new_text,
+ gpointer user_data);
+static void on_add_snippet_menu_item_activated (GtkMenuItem *menu_item,
+ gpointer user_data);
+static void on_add_snippets_group_menu_item_activated (GtkMenuItem *menu_item,
+ gpointer user_data);
+static void on_snippets_view_selection_changed (GtkTreeSelection *tree_selection,
+ gpointer user_data);
+static void on_snippets_editor_snippet_saved (SnippetsEditor *snippets_editor,
+ GObject *snippet,
+ gpointer user_data);
+static void on_snippets_editor_close_request (SnippetsEditor *snippets_editor,
+ gpointer user_data);
+
+/* Private methods */
+
+static void
+init_browser_layout (SnippetsBrowser *snippets_browser)
+{
+ GError *error = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkBuilder *bxml = NULL;
+ GObject *window = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ /* Load the UI file */
+ bxml = gtk_builder_new ();
+ if (!gtk_builder_add_from_file (bxml, BROWSER_UI, &error))
+ {
+ g_warning ("Couldn't load browser ui file: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Get the Gtk objects */
+ priv->add_button = GTK_BUTTON (gtk_builder_get_object (bxml, "add_button"));
+ priv->delete_button = GTK_BUTTON (gtk_builder_get_object (bxml, "delete_button"));
+ priv->insert_button = GTK_BUTTON (gtk_builder_get_object (bxml, "insert_button"));
+ priv->edit_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (bxml, "edit_button"));
+ priv->snippets_view_cont = GTK_SCROLLED_WINDOW (gtk_builder_get_object (bxml, "snippets_view_cont"));
+ priv->snippets_view_vbox = GTK_VBOX (gtk_builder_get_object (bxml, "snippets_view_vbox"));
+
+ /* Assert the objects */
+ g_return_if_fail (GTK_IS_BUTTON (priv->add_button));
+ g_return_if_fail (GTK_IS_BUTTON (priv->delete_button));
+ g_return_if_fail (GTK_IS_BUTTON (priv->insert_button));
+ g_return_if_fail (GTK_IS_TOGGLE_BUTTON (priv->edit_button));
+ g_return_if_fail (GTK_IS_SCROLLED_WINDOW (priv->snippets_view_cont));
+ g_return_if_fail (GTK_IS_VBOX (priv->snippets_view_vbox));
+
+ /* Add the Snippets View to the scrolled window */
+ gtk_container_add (GTK_CONTAINER (priv->snippets_view_cont),
+ GTK_WIDGET (priv->snippets_view));
+
+ /* Add the hbox as the child of the snippets browser */
+ window = gtk_builder_get_object (bxml, "builder_window");
+ g_object_ref (priv->snippets_view_vbox);
+ gtk_container_remove (GTK_CONTAINER (window), GTK_WIDGET (priv->snippets_view_vbox));
+ gtk_box_pack_start (GTK_BOX (snippets_browser),
+ GTK_WIDGET (priv->snippets_view_vbox),
+ TRUE,
+ TRUE,
+ 0);
+ g_object_unref (priv->snippets_view_vbox);
+
+ /* Initialize the snippets editor */
+ priv->snippets_editor = snippets_editor_new (priv->snippets_db);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (priv->snippets_editor));
+
+ /* Add the editor to the HPaned */
+ priv->browser_hpaned = gtk_hpaned_new ();
+ gtk_paned_pack2 (GTK_PANED (priv->browser_hpaned),
+ GTK_WIDGET (priv->snippets_editor),
+ TRUE, FALSE);
+ g_object_ref_sink (priv->browser_hpaned);
+
+ g_object_unref (bxml);
+}
+
+static void
+init_browser_handlers (SnippetsBrowser *snippets_browser)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ g_signal_connect (GTK_OBJECT (priv->snippets_view),
+ "row-activated",
+ GTK_SIGNAL_FUNC (on_snippets_view_row_activated),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->snippets_view),
+ "query-tooltip",
+ GTK_SIGNAL_FUNC (on_snippets_view_query_tooltip),
+ snippets_browser);
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (priv->snippets_view)),
+ "changed",
+ G_CALLBACK (on_snippets_view_selection_changed),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->add_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_add_button_clicked),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->delete_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_delete_button_clicked),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->insert_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_insert_button_clicked),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->edit_button),
+ "toggled",
+ GTK_SIGNAL_FUNC (on_edit_button_toggled),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->snippets_editor),
+ "snippet-saved",
+ GTK_SIGNAL_FUNC (on_snippets_editor_snippet_saved),
+ snippets_browser);
+ g_signal_connect (GTK_OBJECT (priv->snippets_editor),
+ "close-request",
+ GTK_SIGNAL_FUNC (on_snippets_editor_close_request),
+ snippets_browser);
+
+ /* Set the has-tooltip property for the query-tooltip signal */
+ g_object_set (priv->snippets_view, "has-tooltip", TRUE, NULL);
+}
+
+static void
+snippets_view_name_text_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *name = NULL;
+ GObject *cur_object = NULL;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer));
+ g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
+
+ /* Get the name */
+ gtk_tree_model_get (tree_model, iter,
+ SNIPPETS_DB_MODEL_COL_NAME, &name,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ g_object_set (renderer, "text", name, NULL);
+ g_free (name);
+
+ if (ANJUTA_IS_SNIPPETS_GROUP (cur_object))
+ {
+ g_object_set (renderer, "editable", TRUE, NULL);
+ }
+ else
+ {
+ g_return_if_fail (ANJUTA_IS_SNIPPET (cur_object));
+ g_object_set (renderer, "editable", FALSE, NULL);
+ }
+
+ g_object_unref (cur_object);
+
+}
+
+static void
+snippets_view_name_pixbuf_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GObject *cur_object = NULL;
+ gchar *stock_id = NULL;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_PIXBUF (renderer));
+ g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
+
+ gtk_tree_model_get (tree_model, iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ {
+ stock_id = GTK_STOCK_FILE;
+ }
+ else
+ {
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (cur_object));
+ stock_id = GTK_STOCK_DIRECTORY;
+ }
+
+ g_object_unref (cur_object);
+ g_object_set (renderer, "stock-id", stock_id, NULL);
+}
+
+static void
+snippets_view_trigger_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *trigger = NULL, *trigger_markup = NULL;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer));
+ g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
+
+ gtk_tree_model_get (tree_model, iter,
+ SNIPPETS_DB_MODEL_COL_TRIGGER, &trigger,
+ -1);
+ trigger_markup = g_strconcat ("<b>", trigger, "</b>", NULL);
+ g_object_set (renderer, "markup", trigger_markup, NULL);
+
+ g_free (trigger);
+ g_free (trigger_markup);
+}
+
+static void
+snippets_view_languages_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *languages = NULL;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer));
+ g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
+
+ gtk_tree_model_get (tree_model, iter,
+ SNIPPETS_DB_MODEL_COL_LANGUAGES, &languages,
+ -1);
+
+ g_object_set (renderer, "text", languages, NULL);
+
+ g_free (languages);
+}
+
+static gboolean
+snippets_db_language_filter_func (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ IAnjutaDocumentManager *docman = NULL;
+ IAnjutaDocument *doc = NULL;
+ IAnjutaLanguage *ilanguage = NULL;
+ const gchar *language = NULL;
+ GObject *cur_object = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ gboolean has_language = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data), FALSE);
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ /* If we have the setting to show all snippets or if we are editing,
+ we just return TRUE */
+ if (!snippets_browser->show_only_document_language_snippets ||
+ priv->maximized)
+ {
+ return TRUE;
+ }
+
+ /* Get the current object */
+ gtk_tree_model_get (tree_model, iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ /* If it's a SnippetsGroup we render it */
+ if (ANJUTA_IS_SNIPPETS_GROUP (cur_object))
+ {
+ g_object_unref (cur_object);
+ return TRUE;
+ }
+ else
+ if (!ANJUTA_IS_SNIPPET (cur_object))
+ {
+ g_return_val_if_reached (FALSE);
+ }
+
+ /* Get the current document manager. If it doesn't exist we show all snippets */
+ docman = anjuta_shell_get_interface (snippets_browser->anjuta_shell,
+ IAnjutaDocumentManager,
+ NULL);
+ if (!IANJUTA_IS_DOCUMENT_MANAGER (docman))
+ {
+ g_object_unref (cur_object);
+ return TRUE;
+ }
+
+ ilanguage = anjuta_shell_get_interface (snippets_browser->anjuta_shell,
+ IAnjutaLanguage,
+ NULL);
+ if (!IANJUTA_IS_LANGUAGE (ilanguage))
+ {
+ g_object_unref (cur_object);
+ return TRUE;
+ }
+
+ /* Get the current doc and if it isn't an editor show all snippets */
+ doc = ianjuta_document_manager_get_current_document (docman, NULL);
+ if (!IANJUTA_IS_EDITOR (doc))
+ {
+ g_object_unref (cur_object);
+ return TRUE;
+ }
+
+ /* Get the language */
+ language = ianjuta_language_get_name_from_editor (ilanguage,
+ IANJUTA_EDITOR_LANGUAGE (doc),
+ NULL);
+ if (language == NULL)
+ {
+ g_object_unref (cur_object);
+ return TRUE;
+ }
+
+ has_language = snippet_has_language (ANJUTA_SNIPPET (cur_object), language);
+
+ g_object_unref (cur_object);
+ return has_language;
+}
+
+static void
+init_snippets_view (SnippetsBrowser *snippets_browser)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkCellRenderer *text_renderer = NULL, *pixbuf_renderer = NULL;
+ GtkTreeViewColumn *column = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+ g_return_if_fail (GTK_IS_TREE_VIEW (priv->snippets_view));
+ g_return_if_fail (GTK_IS_TREE_MODEL (priv->snippets_db));
+
+ /* Set up the filter */
+ priv->filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->snippets_db), NULL);
+ gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter),
+ snippets_db_language_filter_func,
+ snippets_browser,
+ NULL);
+ gtk_tree_view_set_model (priv->snippets_view, priv->filter);
+
+
+ /* Column 1 - Name */
+ column = gtk_tree_view_column_new ();
+ text_renderer = gtk_cell_renderer_text_new ();
+ pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_set_title (column, _("Name"));
+ gtk_tree_view_column_pack_start (column, pixbuf_renderer, FALSE);
+ gtk_tree_view_column_pack_end (column, text_renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func (column, pixbuf_renderer,
+ snippets_view_name_pixbuf_data_func,
+ snippets_browser, NULL);
+ gtk_tree_view_column_set_cell_data_func (column, text_renderer,
+ snippets_view_name_text_data_func,
+ snippets_browser, NULL);
+ g_signal_connect (GTK_OBJECT (text_renderer),
+ "edited",
+ GTK_SIGNAL_FUNC (on_name_changed),
+ snippets_browser);
+ g_object_set (G_OBJECT (column), "resizable", TRUE, NULL);
+ gtk_tree_view_insert_column (priv->snippets_view, column, -1);
+
+ /* Column 2 - Trigger */
+ column = gtk_tree_view_column_new ();
+ text_renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_set_title (column, _("Trigger"));
+ gtk_tree_view_column_pack_start (column, text_renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func (column, text_renderer,
+ snippets_view_trigger_data_func,
+ snippets_browser, NULL);
+ g_object_set (G_OBJECT (column), "resizable", TRUE, NULL);
+ gtk_tree_view_insert_column (priv->snippets_view, column, -1);
+
+ /* Column 3- Languages */
+ column = gtk_tree_view_column_new ();
+ text_renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_set_title (column, _("Languages"));
+ gtk_tree_view_column_pack_start (column, text_renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func (column, text_renderer,
+ snippets_view_languages_data_func,
+ snippets_browser, NULL);
+ g_object_set (G_OBJECT (column), "resizable", TRUE, NULL);
+ g_object_set (G_OBJECT (column), "visible", FALSE, NULL);
+ gtk_tree_view_insert_column (priv->snippets_view, column, -1);
+
+}
+
+
+/* Public methods */
+
+/**
+ * snippets_browser_new:
+ *
+ * Returns: A new #SnippetsBrowser object.
+ */
+SnippetsBrowser*
+snippets_browser_new (void)
+{
+ return ANJUTA_SNIPPETS_BROWSER (g_object_new (snippets_browser_get_type (), NULL));
+}
+
+/**
+ * snippets_browser_load:
+ * @snippets_browser: A #SnippetsBrowser object.
+ * @snippets_db: A #SnippetsDB object from which the browser should be loaded.
+ * @snippets_interaction: A #SnippetsInteraction object which is used for interacting with the editor.
+ *
+ * Loads the #SnippetsBrowser with snippets that are found in the given database.
+ */
+void
+snippets_browser_load (SnippetsBrowser *snippets_browser,
+ SnippetsDB *snippets_db,
+ SnippetsInteraction *snippets_interaction)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ priv->snippets_db = snippets_db;
+ priv->snippets_interaction = snippets_interaction;
+ g_object_ref (priv->snippets_db);
+ g_object_ref (priv->snippets_interaction);
+
+ /* Set up the Snippets View */
+ priv->snippets_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+ init_snippets_view (snippets_browser);
+
+ /* Set up the layout */
+ init_browser_layout (snippets_browser);
+
+ /* Initialize the snippet handlers */
+ init_browser_handlers (snippets_browser);
+
+ priv->maximized = FALSE;
+}
+
+/**
+ * snippets_browser_unload:
+ * @snippets_browser: A #SnippetsBrowser object.
+ *
+ * Unloads the current #SnippetsBrowser object.
+ */
+void
+snippets_browser_unload (SnippetsBrowser *snippets_browser)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ g_object_unref (priv->snippets_db);
+ g_object_unref (priv->snippets_interaction);
+ priv->snippets_db = NULL;
+ priv->snippets_interaction = NULL;
+
+ if (priv->maximized)
+ {
+ gtk_container_remove (GTK_CONTAINER (snippets_browser),
+ GTK_WIDGET (priv->browser_hpaned));
+ }
+ else
+ {
+ gtk_container_remove (GTK_CONTAINER (snippets_browser),
+ GTK_WIDGET (priv->snippets_view_vbox));
+ g_object_unref (priv->browser_hpaned);
+ }
+
+ g_object_unref (priv->filter);
+}
+
+/**
+ * snippets_browser_show_editor:
+ * @snippets_browser: A #SnippetsBrowser object.
+ *
+ * Shows the editor attached to the browser. Warning: This will take up considerably
+ * more space than just having the browser shown.
+ */
+void
+snippets_browser_show_editor (SnippetsBrowser *snippets_browser)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreeSelection *selection = NULL;
+ GtkTreeViewColumn *col = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ if (priv->maximized)
+ return;
+
+ /* Unparent the SnippetsView from the SnippetsBrowser */
+ g_object_ref (priv->snippets_view_vbox);
+ gtk_container_remove (GTK_CONTAINER (snippets_browser),
+ GTK_WIDGET (priv->snippets_view_vbox));
+
+ /* Add the SnippetsView to the HPaned */
+ gtk_paned_pack1 (GTK_PANED (priv->browser_hpaned),
+ GTK_WIDGET (priv->snippets_view_vbox),
+ TRUE, FALSE);
+ g_object_unref (priv->snippets_view_vbox);
+
+ /* Add the HPaned in the SnippetsBrowser */
+ gtk_box_pack_start (GTK_BOX (snippets_browser),
+ priv->browser_hpaned,
+ TRUE,
+ TRUE,
+ 0);
+
+ /* Show the editor widgets */
+ gtk_widget_show (priv->browser_hpaned);
+ gtk_widget_show (GTK_WIDGET (priv->snippets_editor));
+
+ priv->maximized = TRUE;
+
+ snippets_browser_refilter_snippets_view (snippets_browser);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->insert_button), FALSE);
+
+ /* Set the current snippet for the editor */
+ selection = gtk_tree_view_get_selection (priv->snippets_view);
+ on_snippets_view_selection_changed (selection, snippets_browser);
+
+ col = gtk_tree_view_get_column (priv->snippets_view, SNIPPETS_VIEW_COL_LANGUAGES);
+ g_object_set (col, "visible", TRUE, NULL);
+
+}
+
+/**
+ * snippets_browser_hide_editor:
+ * @snippets_browser: A #SnippetsBrowser object.
+ *
+ * Hides the editor attached to the browser.
+ */
+void
+snippets_browser_hide_editor (SnippetsBrowser *snippets_browser)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreeViewColumn *col = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ if (!priv->maximized)
+ return;
+
+ /* Hide the editor widgets */
+ gtk_widget_hide (GTK_WIDGET (priv->snippets_editor));
+ gtk_widget_hide (priv->browser_hpaned);
+
+ /* Remove the SnippetsView from the HPaned */
+ g_object_ref (priv->snippets_view_vbox);
+ gtk_container_remove (GTK_CONTAINER (priv->browser_hpaned),
+ GTK_WIDGET (priv->snippets_view_vbox));
+
+ /* Remove the HPaned from the SnippetsBrowser */
+ g_object_ref (priv->browser_hpaned);
+ gtk_container_remove (GTK_CONTAINER (snippets_browser),
+ GTK_WIDGET (priv->browser_hpaned));
+ g_object_unref (priv->browser_hpaned);
+
+ /* Add the SnippetsView to the SnippetsBrowser */
+ gtk_box_pack_start (GTK_BOX (snippets_browser),
+ GTK_WIDGET (priv->snippets_view_vbox),
+ TRUE,
+ TRUE,
+ 0);
+ g_object_unref (priv->snippets_view_vbox);
+
+ priv->maximized = FALSE;
+
+ snippets_browser_refilter_snippets_view (snippets_browser);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (priv->insert_button), TRUE);
+
+ col = gtk_tree_view_get_column (priv->snippets_view, SNIPPETS_VIEW_COL_LANGUAGES);
+ g_object_set (col, "visible", FALSE, NULL);
+}
+
+/**
+ * snippets_browser_refilter_snippets_view:
+ * @snippets_browser: A #SnippetsBrowser object.
+ *
+ * Refilters the Snippets View (if snippets_browser->show_only_document_language_snippets
+ * is TRUE), showing just the snippets of the current document. If the Snippets Browser
+ * has the editor shown, it will show all the snippets, regardless of the option.
+ */
+void
+snippets_browser_refilter_snippets_view (SnippetsBrowser *snippets_browser)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (snippets_browser));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+}
+
+/* Handlers definitions */
+
+static void
+on_add_button_clicked (GtkButton *add_button,
+ gpointer user_data)
+{
+ GtkWidget *menu = NULL, *add_snippet_menu_item = NULL,
+ *add_snippets_group_menu_item = NULL;
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ menu = gtk_menu_new ();
+
+ /* Insert the Add Snippet menu item */
+ add_snippet_menu_item = gtk_menu_item_new_with_label (_("Add Snippet â?¦"));
+ g_signal_connect (GTK_OBJECT (add_snippet_menu_item),
+ "activate",
+ GTK_SIGNAL_FUNC (on_add_snippet_menu_item_activated),
+ snippets_browser);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ GTK_WIDGET (add_snippet_menu_item));
+ gtk_widget_show (GTK_WIDGET (add_snippet_menu_item));
+
+ /* Insert the Add Snippets Group menu item */
+ add_snippets_group_menu_item = gtk_menu_item_new_with_label (_("Add Snippets Group â?¦"));
+ g_signal_connect (GTK_OBJECT (add_snippets_group_menu_item),
+ "activate",
+ GTK_SIGNAL_FUNC (on_add_snippets_group_menu_item_activated),
+ snippets_browser);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ GTK_WIDGET (add_snippets_group_menu_item));
+ gtk_widget_show (GTK_WIDGET (add_snippets_group_menu_item));
+
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time ());
+
+}
+
+static void
+on_delete_button_clicked (GtkButton *delete_button,
+ gpointer user_data)
+{
+ GtkTreeIter iter;
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ gboolean has_selection = FALSE;
+ GObject *cur_object = NULL;
+ GtkTreeSelection *selection = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+ g_return_if_fail (GTK_IS_TREE_MODEL (priv->filter));
+
+ selection = gtk_tree_view_get_selection (priv->snippets_view);
+ g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+ has_selection = gtk_tree_selection_get_selected (selection,
+ &priv->filter,
+ &iter);
+ if (has_selection)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->filter), &iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ {
+ const gchar *trigger_key = NULL, *language = NULL;
+
+ trigger_key = snippet_get_trigger_key (ANJUTA_SNIPPET (cur_object));
+ language = snippet_get_any_language (ANJUTA_SNIPPET (cur_object));
+ g_return_if_fail (trigger_key != NULL);
+
+ snippets_db_remove_snippet (priv->snippets_db, trigger_key, language, TRUE);
+ }
+ else
+ {
+ /* It's a SnippetsGroup object */
+ const gchar *name = NULL;
+
+ name = snippets_group_get_name (ANJUTA_SNIPPETS_GROUP (cur_object));
+ g_return_if_fail (name != NULL);
+ snippets_db_remove_snippets_group (priv->snippets_db, name);
+ }
+
+ g_object_unref (cur_object);
+ }
+
+ snippets_db_save_snippets (priv->snippets_db);
+
+}
+
+static void
+on_insert_button_clicked (GtkButton *insert_button,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreeSelection *selection = NULL;
+ GObject *cur_object = NULL;
+ GtkTreeIter iter;
+ gboolean has_selection = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (priv->snippets_interaction));
+
+ selection = gtk_tree_view_get_selection (priv->snippets_view);
+ has_selection = gtk_tree_selection_get_selected (selection,
+ &priv->filter,
+ &iter);
+ if (has_selection)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->filter), &iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ if (!ANJUTA_IS_SNIPPET (cur_object))
+ return;
+
+ snippets_interaction_insert_snippet (priv->snippets_interaction,
+ priv->snippets_db,
+ ANJUTA_SNIPPET (cur_object));
+ }
+
+}
+
+static void
+on_edit_button_toggled (GtkToggleButton *edit_button,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ /* Request a maximize/unmaximize (which should be caught by the plugin) */
+ if (!priv->maximized)
+ g_signal_emit_by_name (G_OBJECT (snippets_browser),
+ "maximize-request");
+ else
+ g_signal_emit_by_name (G_OBJECT (snippets_browser),
+ "unmaximize-request");
+}
+
+static void
+on_snippets_view_row_activated (GtkTreeView *snippets_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer user_data)
+{
+ GtkTreeIter iter;
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GObject *cur_object = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (priv->snippets_interaction));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+
+ gtk_tree_model_get_iter (priv->filter, &iter, path);
+ gtk_tree_model_get (priv->filter, &iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ snippets_interaction_insert_snippet (priv->snippets_interaction,
+ priv->snippets_db,
+ ANJUTA_SNIPPET (cur_object));
+
+ g_object_unref (cur_object);
+}
+
+
+static gboolean
+on_snippets_view_query_tooltip (GtkWidget *snippets_view,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreeIter iter;
+ GObject *cur_object = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (snippets_view), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data), FALSE);
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (user_data);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db), FALSE);
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (priv->filter), FALSE);
+
+ /* Get the object at the current row */
+ if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (snippets_view),
+ &x, &y, keyboard_mode,
+ NULL, NULL,
+ &iter))
+ return FALSE;
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->filter), &iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ {
+ gchar *default_content = NULL, *default_content_preview = NULL, *tooltip_text = NULL;
+
+ default_content = snippet_get_default_content (ANJUTA_SNIPPET (cur_object),
+ G_OBJECT (priv->snippets_db),
+ "");
+
+ default_content_preview = g_strndup (default_content, TOOLTIP_SIZE);
+ tooltip_text = g_strconcat (default_content_preview, " â?¦", NULL);
+ gtk_tooltip_set_text (tooltip, tooltip_text);
+
+ g_free (default_content);
+ g_free (default_content_preview);
+ g_free (tooltip_text);
+ g_object_unref (cur_object);
+
+ return TRUE;
+ }
+
+ g_object_unref (cur_object);
+ return FALSE;
+}
+
+
+static void
+on_name_changed (GtkCellRendererText *renderer,
+ gchar *path_string,
+ gchar *new_text,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ gchar *old_name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (priv->filter, &iter, path);
+ gtk_tree_model_get (priv->filter, &iter,
+ SNIPPETS_DB_MODEL_COL_NAME, &old_name,
+ -1);
+
+ snippets_db_set_snippets_group_name (priv->snippets_db, old_name, new_text);
+ snippets_browser_refilter_snippets_view (snippets_browser);
+
+ snippets_db_save_snippets (priv->snippets_db);
+
+ gtk_tree_path_free (path);
+ g_free (old_name);
+}
+
+
+static void
+on_add_snippet_menu_item_activated (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (user_data);
+
+ /* Request a maximize/unmaximize (which should be caught by the plugin) */
+ if (!priv->maximized)
+ g_signal_emit_by_name (G_OBJECT (user_data),
+ "maximize-request");
+
+ snippets_editor_set_snippet_new (priv->snippets_editor);
+}
+
+static void
+on_add_snippets_group_menu_item_activated (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreeIter iter;
+ AnjutaSnippetsGroup *snippets_group = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+
+ snippets_group = snippets_group_new (NEW_SNIPPETS_GROUP_NAME);
+ snippets_db_add_snippets_group (priv->snippets_db, snippets_group, FALSE);
+
+ /* The snippets database shouldn't be empty here */
+ if (!gtk_tree_model_get_iter_first (priv->filter, &iter))
+ g_return_if_reached ();
+
+ do
+ {
+ gchar *name = NULL;
+
+ gtk_tree_model_get (priv->filter, &iter,
+ SNIPPETS_DB_MODEL_COL_NAME, &name,
+ -1);
+ if (!g_strcmp0 (name, NEW_SNIPPETS_GROUP_NAME))
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (priv->filter, &iter);
+ GtkTreeViewColumn *col = gtk_tree_view_get_column (priv->snippets_view,
+ SNIPPETS_VIEW_COL_NAME);
+ gtk_tree_view_set_cursor (priv->snippets_view, path, col, TRUE);
+
+ /* We save the database after the cursor was set. */
+ snippets_db_save_snippets (priv->snippets_db);
+
+ gtk_tree_path_free (path);
+ g_free (name);
+ return;
+ }
+
+ g_free (name);
+
+ } while (gtk_tree_model_iter_next (priv->filter, &iter));
+
+ /* We should have found the newly added group */
+ g_return_if_reached ();
+}
+
+
+static void
+on_snippets_view_selection_changed (GtkTreeSelection *tree_selection,
+ gpointer user_data)
+{
+ SnippetsBrowser *snippets_browser = NULL;
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreeIter iter;
+ GObject *cur_object = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ snippets_browser = ANJUTA_SNIPPETS_BROWSER (user_data);
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (snippets_browser);
+ g_return_if_fail (GTK_IS_TREE_MODEL (priv->filter));
+
+ if (!gtk_tree_selection_get_selected (tree_selection, &priv->filter, &iter))
+ {
+ snippets_editor_set_snippet (priv->snippets_editor, NULL);
+ return;
+ }
+
+ gtk_tree_model_get (priv->filter, &iter,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+
+ /* We only change the snippet of the editor if the browser has the editor shown */
+ if (ANJUTA_IS_SNIPPET (cur_object) && priv->maximized)
+ snippets_editor_set_snippet (priv->snippets_editor,
+ ANJUTA_SNIPPET (cur_object));
+ else if (priv->maximized)
+ snippets_editor_set_snippet (priv->snippets_editor, NULL);
+
+ g_object_unref (cur_object);
+}
+
+
+static void
+on_snippets_editor_snippet_saved (SnippetsEditor *snippets_editor,
+ GObject *snippet,
+ gpointer user_data)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+ GtkTreePath *path = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (user_data);
+
+ /* Focus on the newly inserted snippet (the path is valid because when the editor
+ is shown, all snippets are visibile.)*/
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+ path = snippets_db_get_path_at_object (priv->snippets_db, snippet);
+ gtk_tree_view_set_cursor (priv->snippets_view, path, NULL, FALSE);
+
+ /* We save the database after we set the cursor */
+ snippets_db_save_snippets (priv->snippets_db);
+}
+
+
+static void
+on_snippets_editor_close_request (SnippetsEditor *snippets_editor,
+ gpointer user_data)
+{
+ SnippetsBrowserPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_BROWSER (user_data));
+ priv = ANJUTA_SNIPPETS_BROWSER_GET_PRIVATE (user_data);
+
+ gtk_toggle_button_set_active (priv->edit_button, FALSE);
+}
diff --git a/plugins/snippets-manager/snippets-browser.h b/plugins/snippets-manager/snippets-browser.h
new file mode 100644
index 0000000..78c01ba
--- /dev/null
+++ b/plugins/snippets-manager/snippets-browser.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-browser.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_BROWSER_H__
+#define __SNIPPETS_BROWSER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "snippets-db.h"
+#include "snippet.h"
+#include "snippets-interaction-interpreter.h"
+#include <libanjuta/anjuta-shell.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SnippetsBrowser SnippetsBrowser;
+typedef struct _SnippetsBrowserPrivate SnippetsBrowserPrivate;
+typedef struct _SnippetsBrowserClass SnippetsBrowserClass;
+
+#define ANJUTA_TYPE_SNIPPETS_BROWSER (snippets_browser_get_type ())
+#define ANJUTA_SNIPPETS_BROWSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPETS_BROWSER, SnippetsBrowser))
+#define ANJUTA_SNIPPETS_BROWSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPETS_BROWSER, SnippetsBrowserClass))
+#define ANJUTA_IS_SNIPPETS_BROWSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPETS_BROWSER))
+#define ANJUTA_IS_SNIPPETS_BROWSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPETS_BROWSER))
+
+struct _SnippetsBrowser
+{
+ GtkHBox parent;
+
+ gboolean show_only_document_language_snippets;
+ AnjutaShell *anjuta_shell;
+
+ /*< private >*/
+ SnippetsBrowserPrivate *priv;
+};
+
+struct _SnippetsBrowserClass
+{
+ GtkHBoxClass parent_class;
+
+ /* Signals */
+ void (*maximize_request) (SnippetsBrowser *snippets_browser);
+ void (*unmaximize_request) (SnippetsBrowser *snippets_browser);
+};
+
+G_END_DECLS
+
+
+GType snippets_browser_get_type (void) G_GNUC_CONST;
+SnippetsBrowser* snippets_browser_new (void);
+
+void snippets_browser_load (SnippetsBrowser *snippets_browser,
+ SnippetsDB *snippets_db,
+ SnippetsInteraction *snippets_interaction);
+void snippets_browser_unload (SnippetsBrowser *snippets_browser);
+
+void snippets_browser_show_editor (SnippetsBrowser *snippets_browser);
+void snippets_browser_hide_editor (SnippetsBrowser *snippets_browser);
+void snippets_browser_refilter_snippets_view (SnippetsBrowser *snippets_browser);
+
+#endif /* __SNIPPETS_BROWSER_H__ */
+
diff --git a/plugins/snippets-manager/snippets-browser.ui b/plugins/snippets-manager/snippets-browser.ui
new file mode 100644
index 0000000..90efe98
--- /dev/null
+++ b/plugins/snippets-manager/snippets-browser.ui
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkWindow" id="builder_window">
+ <child>
+ <object class="GtkVBox" id="snippets_view_vbox">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="snippets_view_cont">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="height_request">26</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="add_button">
+ <property name="label" translatable="yes">Add</property>
+ <property name="width_request">60</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete_button">
+ <property name="label" translatable="yes">Delete</property>
+ <property name="width_request">60</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="insert_button">
+ <property name="label" translatable="yes">Insert</property>
+ <property name="width_request">60</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="yalign">0.46000000834465027</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVSeparator" id="vseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="edit_button">
+ <property name="width_request">36</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-edit</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">5</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plugins/snippets-manager/snippets-db.c b/plugins/snippets-manager/snippets-db.c
new file mode 100644
index 0000000..77127a7
--- /dev/null
+++ b/plugins/snippets-manager/snippets-db.c
@@ -0,0 +1,2111 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-db.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-db.h"
+#include "snippets-xml-parser.h"
+#include <libanjuta/anjuta-utils.h>
+#include <libanjuta/anjuta-debug.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-document.h>
+#include <libanjuta/interfaces/ianjuta-editor.h>
+#include <libanjuta/interfaces/ianjuta-editor-language.h>
+#include <libanjuta/interfaces/ianjuta-language.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#define DEFAULT_SNIPPETS_FILE "snippets.anjuta-snippets"
+#define DEFAULT_GLOBAL_VARS_FILE "snippets-global-variables.xml"
+#define USER_SNIPPETS_DB_DIR "snippets-database"
+
+#define SNIPPETS_DB_MODEL_DEPTH 2
+
+/* Internal global variables */
+#define GLOBAL_VAR_FILE_NAME "filename"
+#define GLOBAL_VAR_USER_NAME "username"
+#define GLOBAL_VAR_USER_FULL_NAME "userfullname"
+#define GLOBAL_VAR_HOST_NAME "hostname"
+
+static gchar *default_files[] = {
+ DEFAULT_SNIPPETS_FILE,
+ DEFAULT_GLOBAL_VARS_FILE
+};
+
+
+#define ANJUTA_SNIPPETS_DB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_SNIPPETS_DB, SnippetsDBPrivate))
+
+/**
+ * SnippetsDBPrivate:
+ * @snippets_groups: A #GList where the #AnjutaSnippetsGroup objects are loaded.
+ * @snippet_keys_map: A #GHashTable with strings representing the snippet-key as keys and pointers
+ * to #AnjutaSnippet objects as values.
+ * Important: One should not try to delete anything. The #GHashTable was
+ * constructed with destroy functions passed to the #GHashTable that will
+ * free the memory.
+ * @global_variables: A #GtkListStore where the static and command-based global variables are stored.
+ * See snippets-db.h for details about columns.
+ * Important: Only static and command-based global variables are stored here!
+ * The internal global variables are computed when #snippets_db_get_global_variable
+ * is called.
+ *
+ * The private field for the SnippetsDB object.
+ */
+struct _SnippetsDBPrivate
+{
+ GList* snippets_groups;
+
+ GHashTable* snippet_keys_map;
+
+ GtkListStore* global_variables;
+};
+
+
+/* GObject methods declaration */
+static void snippets_db_dispose (GObject* obj);
+
+static void snippets_db_finalize (GObject* obj);
+
+static void snippets_db_class_init (SnippetsDBClass* klass);
+
+static void snippets_db_init (SnippetsDB *snippets_db);
+
+
+/* GtkTreeModel methods declaration */
+static void snippets_db_tree_model_init (GtkTreeModelIface *iface);
+
+static GtkTreeModelFlags snippets_db_get_flags (GtkTreeModel *tree_model);
+
+static gint snippets_db_get_n_columns (GtkTreeModel *tree_model);
+
+static GType snippets_db_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+
+static gboolean snippets_db_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+
+static GtkTreePath* snippets_db_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+
+static void snippets_db_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+
+static gboolean snippets_db_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+
+static gboolean snippets_db_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+
+static gboolean snippets_db_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+
+static gint snippets_db_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+
+static gboolean snippets_db_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+
+static gboolean snippets_db_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+
+
+G_DEFINE_TYPE_WITH_CODE (SnippetsDB, snippets_db, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+ snippets_db_tree_model_init));
+
+/* SnippetsDB private methods */
+
+static GtkTreePath *
+get_tree_path_for_snippets_group (SnippetsDB *snippets_db,
+ AnjutaSnippetsGroup *snippets_group);
+static GtkTreePath *
+get_tree_path_for_snippet (SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet);
+
+static gchar *
+get_snippet_key_from_trigger_and_language (const gchar *trigger_key,
+ const gchar *language)
+{
+ gchar *snippet_key = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (trigger_key != NULL, NULL);
+
+ snippet_key = g_strconcat (trigger_key, ".", language, NULL);
+
+ return snippet_key;
+}
+
+static void
+add_snippet_to_hash_table (SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet)
+{
+ GList *iter = NULL, *languages = NULL;
+ gchar *snippet_key = NULL;
+ const gchar *trigger_key = NULL, *lang = NULL;
+ SnippetsDBPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ languages = (GList *)snippet_get_languages (snippet);
+ trigger_key = snippet_get_trigger_key (snippet);
+
+ for (iter = g_list_first (languages); iter != NULL; iter = g_list_next (iter))
+ {
+ lang = (const gchar *)iter->data;
+
+ snippet_key = get_snippet_key_from_trigger_and_language (trigger_key, lang);
+
+ g_hash_table_insert (priv->snippet_keys_map, snippet_key, snippet);
+
+ }
+
+}
+
+static void
+remove_snippet_from_hash_table (SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet)
+{
+ GList *languages = NULL, *iter = NULL;
+ gchar *cur_language = NULL, *cur_snippet_key = NULL, *trigger_key = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+
+ languages = (GList *)snippet_get_languages (snippet);
+ trigger_key = (gchar *)snippet_get_trigger_key (snippet);
+
+ for (iter = g_list_first (languages); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_language = (gchar *)iter->data;
+ cur_snippet_key = get_snippet_key_from_trigger_and_language (trigger_key, cur_language);
+
+ if (cur_snippet_key == NULL)
+ continue;
+
+ g_hash_table_remove (snippets_db->priv->snippet_keys_map, cur_snippet_key);
+ }
+}
+
+static void
+remove_snippets_group_from_hash_table (SnippetsDB *snippets_db,
+ AnjutaSnippetsGroup *snippets_group)
+{
+ GList *snippets = NULL, *iter = NULL;
+ AnjutaSnippet *cur_snippet = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group));
+
+ snippets = (GList *)snippets_group_get_snippets_list (snippets_group);
+
+ /* Iterate over all the snippets in the group, and remove all the snippet keys
+ a snippet has stored. */
+ for (iter = g_list_first (snippets); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet = (AnjutaSnippet *)iter->data;
+ g_return_if_fail (ANJUTA_IS_SNIPPET (cur_snippet));
+
+ remove_snippet_from_hash_table (snippets_db, cur_snippet);
+ }
+}
+
+static void
+copy_default_files_to_user_folder (SnippetsDB *snippets_db)
+{
+ /* In this function we should copy the default snippets file and the global variables
+ files in the user folder if there aren't already files with the same name in that
+ folder */
+ gchar *cur_user_path = NULL, *cur_installation_path = NULL,
+ *user_snippets_db_path = NULL;
+ GFile *cur_user_file = NULL, *cur_installation_file = NULL;
+ gboolean cur_file_exists = FALSE, copy_success = FALSE;
+ gint i = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ /* Compute the user_snippets_db file paths */
+ user_snippets_db_path = anjuta_util_get_user_data_file_path (USER_SNIPPETS_DB_DIR, "/", NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (default_files); i ++)
+ {
+ cur_user_path = g_strconcat (user_snippets_db_path, "/", default_files[i], NULL);
+ cur_installation_path = g_strconcat (PACKAGE_DATA_DIR, "/", default_files[i], NULL);
+
+ cur_file_exists = g_file_test (cur_user_path, G_FILE_TEST_EXISTS);
+ if (!cur_file_exists)
+ {
+ cur_installation_file = g_file_new_for_path (cur_installation_path);
+ cur_user_file = g_file_new_for_path (cur_user_path);
+
+ copy_success = g_file_copy (cur_installation_file, cur_user_file,
+ G_FILE_COPY_NONE,
+ NULL, NULL, NULL, NULL);
+
+ if (!copy_success)
+ DEBUG_PRINT ("Copying of %s failed.", default_files[i]);
+
+ g_object_unref (cur_installation_file);
+ g_object_unref (cur_user_file);
+ }
+
+ g_free (cur_user_path);
+ g_free (cur_installation_path);
+ }
+
+ g_free (user_snippets_db_path);
+
+}
+
+static void
+load_internal_global_variables (SnippetsDB *snippets_db)
+{
+ GtkTreeIter iter_added;
+ GtkListStore *global_vars_store = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (snippets_db->priv != NULL);
+ g_return_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables));
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Add the filename global variable */
+ gtk_list_store_prepend (global_vars_store, &iter_added);
+ gtk_list_store_set (global_vars_store, &iter_added,
+ GLOBAL_VARS_MODEL_COL_NAME, GLOBAL_VAR_FILE_NAME,
+ GLOBAL_VARS_MODEL_COL_VALUE, "",
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, FALSE,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, TRUE,
+ -1);
+
+ /* Add the username global variable */
+ gtk_list_store_prepend (global_vars_store, &iter_added);
+ gtk_list_store_set (global_vars_store, &iter_added,
+ GLOBAL_VARS_MODEL_COL_NAME, GLOBAL_VAR_USER_NAME,
+ GLOBAL_VARS_MODEL_COL_VALUE, "",
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, FALSE,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, TRUE,
+ -1);
+
+ /* Add the userfullname global variable */
+ gtk_list_store_prepend (global_vars_store, &iter_added);
+ gtk_list_store_set (global_vars_store, &iter_added,
+ GLOBAL_VARS_MODEL_COL_NAME, GLOBAL_VAR_USER_FULL_NAME,
+ GLOBAL_VARS_MODEL_COL_VALUE, "",
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, FALSE,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, TRUE,
+ -1);
+
+ /* Add the hostname global variable*/
+ gtk_list_store_prepend (global_vars_store, &iter_added);
+ gtk_list_store_set (global_vars_store, &iter_added,
+ GLOBAL_VARS_MODEL_COL_NAME, GLOBAL_VAR_HOST_NAME,
+ GLOBAL_VARS_MODEL_COL_VALUE, "",
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, FALSE,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, TRUE,
+ -1);
+}
+
+static void
+load_global_variables (SnippetsDB *snippets_db)
+{
+ gchar *global_vars_user_path = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ /* Load the internal global variables */
+ load_internal_global_variables (snippets_db);
+
+ global_vars_user_path =
+ anjuta_util_get_user_data_file_path (USER_SNIPPETS_DB_DIR, "/",
+ DEFAULT_GLOBAL_VARS_FILE, NULL);
+ snippets_manager_parse_variables_xml_file (global_vars_user_path, snippets_db);
+
+ g_free (global_vars_user_path);
+}
+
+static void
+load_snippets (SnippetsDB *snippets_db)
+{
+ gchar *user_snippets_path;
+ GList *snippets_groups = NULL, *iter = NULL;
+ AnjutaSnippetsGroup *snippets_group = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ user_snippets_path =
+ anjuta_util_get_user_data_file_path (USER_SNIPPETS_DB_DIR, "/",
+ DEFAULT_SNIPPETS_FILE, NULL);
+
+ snippets_groups = snippets_manager_parse_snippets_xml_file (user_snippets_path, NATIVE_FORMAT);
+
+ for (iter = g_list_first (snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ snippets_group = ANJUTA_SNIPPETS_GROUP (iter->data);
+ if (!ANJUTA_IS_SNIPPETS_GROUP (snippets_group))
+ continue;
+
+ snippets_db_add_snippets_group (snippets_db, snippets_group, TRUE);
+ }
+
+ g_free (user_snippets_path);
+}
+
+static gchar*
+get_internal_global_variable_value (AnjutaShell *shell,
+ const gchar* variable_name)
+{
+ /* Assertions */
+ g_return_val_if_fail (variable_name != NULL, NULL);
+
+ /* Check manually what variable is requested */
+ if (!g_strcmp0 (variable_name, GLOBAL_VAR_FILE_NAME))
+ {
+ IAnjutaDocumentManager *anjuta_docman = NULL;
+ IAnjutaDocument *anjuta_cur_doc = NULL;
+
+ anjuta_docman = anjuta_shell_get_interface (shell,
+ IAnjutaDocumentManager,
+ NULL);
+ if (anjuta_docman)
+ {
+ anjuta_cur_doc = ianjuta_document_manager_get_current_document (anjuta_docman, NULL);
+ if (!anjuta_cur_doc)
+ return g_strdup ("");
+
+ return g_strdup (ianjuta_document_get_filename (anjuta_cur_doc, NULL));
+ }
+ else
+ return g_strdup ("");
+ }
+
+ if (!g_strcmp0 (variable_name, GLOBAL_VAR_USER_NAME))
+ return g_strdup (g_get_user_name ());
+
+ if (!g_strcmp0 (variable_name, GLOBAL_VAR_USER_FULL_NAME))
+ return g_strdup (g_get_real_name ());
+
+ if (!g_strcmp0 (variable_name, GLOBAL_VAR_HOST_NAME))
+ return g_strdup (g_get_host_name ());
+
+ return NULL;
+}
+
+static GtkTreeIter*
+get_iter_at_global_variable_name (GtkListStore *global_vars_store,
+ const gchar *variable_name)
+{
+ GtkTreeIter iter;
+ gchar *stored_name = NULL;
+ gboolean iter_is_set = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (GTK_IS_LIST_STORE (global_vars_store), NULL);
+
+ iter_is_set = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (global_vars_store), &iter);
+ while (iter_is_set)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &stored_name,
+ -1);
+
+ /* If we found the name in the database */
+ if (!g_strcmp0 (stored_name, variable_name) )
+ {
+ g_free (stored_name);
+ return gtk_tree_iter_copy (&iter);
+ }
+
+ iter_is_set = gtk_tree_model_iter_next (GTK_TREE_MODEL (global_vars_store), &iter);
+ g_free (stored_name);
+ }
+
+ return NULL;
+}
+
+static gint
+compare_snippets_groups_by_name (gconstpointer a,
+ gconstpointer b)
+{
+ AnjutaSnippetsGroup *group1 = (AnjutaSnippetsGroup *)a;
+ AnjutaSnippetsGroup *group2 = (AnjutaSnippetsGroup *)b;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (group1), 0);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (group2), 0);
+
+ return g_utf8_collate (snippets_group_get_name (group1),
+ snippets_group_get_name (group2));
+}
+
+static void
+snippets_db_dispose (GObject* obj)
+{
+ /* Important: This does not free the memory in the internal structures. You first
+ must use snippets_db_close before disposing the snippets-database. */
+ SnippetsDB *snippets_db = NULL;
+
+ DEBUG_PRINT ("%s", "Disposing SnippetsDB â?¦");
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ snippets_db = ANJUTA_SNIPPETS_DB (obj);
+ g_return_if_fail (snippets_db->priv != NULL);
+
+ g_list_free (snippets_db->priv->snippets_groups);
+ g_hash_table_destroy (snippets_db->priv->snippet_keys_map);
+
+ snippets_db->priv->snippets_groups = NULL;
+ snippets_db->priv->snippet_keys_map = NULL;
+
+ G_OBJECT_CLASS (snippets_db_parent_class)->dispose (obj);
+}
+
+static void
+snippets_db_finalize (GObject* obj)
+{
+ DEBUG_PRINT ("%s", "Finalizing SnippetsDB â?¦");
+
+ G_OBJECT_CLASS (snippets_db_parent_class)->finalize (obj);
+}
+
+static void
+snippets_db_class_init (SnippetsDBClass* klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ snippets_db_parent_class = g_type_class_peek_parent (klass);
+ object_class->dispose = snippets_db_dispose;
+ object_class->finalize = snippets_db_finalize;
+ g_type_class_add_private (klass, sizeof (SnippetsDBPrivate));
+}
+
+static void
+snippets_db_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = snippets_db_get_flags;
+ iface->get_n_columns = snippets_db_get_n_columns;
+ iface->get_column_type = snippets_db_get_column_type;
+ iface->get_iter = snippets_db_get_iter;
+ iface->get_path = snippets_db_get_path;
+ iface->get_value = snippets_db_get_value;
+ iface->iter_next = snippets_db_iter_next;
+ iface->iter_children = snippets_db_iter_children;
+ iface->iter_has_child = snippets_db_iter_has_child;
+ iface->iter_n_children = snippets_db_iter_n_children;
+ iface->iter_nth_child = snippets_db_iter_nth_child;
+ iface->iter_parent = snippets_db_iter_parent;
+}
+
+static void
+snippets_db_init (SnippetsDB *snippets_db)
+{
+ SnippetsDBPrivate* priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+ snippets_db->priv = priv;
+ snippets_db->anjuta_shell = NULL;
+ snippets_db->stamp = g_random_int ();
+
+ /* Initialize the private fields */
+ snippets_db->priv->snippets_groups = NULL;
+ snippets_db->priv->snippet_keys_map = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ snippets_db->priv->global_variables = gtk_list_store_new (GLOBAL_VARS_MODEL_COL_N,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+}
+
+/* SnippetsDB public methods */
+
+/**
+ * snippets_db_new:
+ *
+ * A new #SnippetDB object.
+ *
+ * Returns: A new #SnippetsDB object.
+ **/
+SnippetsDB*
+snippets_db_new ()
+{
+ return ANJUTA_SNIPPETS_DB (g_object_new (snippets_db_get_type (), NULL));
+}
+
+/**
+ * snippets_db_load:
+ * @snippets_db: A #SnippetsDB object
+ *
+ * Loads the given @snippets_db with snippets/global-variables loaded from the default
+ * folder.
+ */
+void
+snippets_db_load (SnippetsDB *snippets_db)
+{
+ gchar *user_snippets_path = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ /* Make sure we have the user folder */
+ user_snippets_path = anjuta_util_get_user_data_file_path (USER_SNIPPETS_DB_DIR, "/", NULL);
+ g_mkdir_with_parents (user_snippets_path, 0755);
+
+ /* Check if the default snippets file is in the user directory and copy them
+ over from the installation folder if they aren't*/
+ copy_default_files_to_user_folder (snippets_db);
+
+ /* Load the snippets and global variables */
+ load_global_variables (snippets_db);
+ load_snippets (snippets_db);
+}
+
+/**
+ * snippets_db_close:
+ * @snippets_db: A #SnippetsDB object.
+ *
+ * Saves the snippets and free's the loaded data from the internal structures (not the
+ * internal structures themselvs, so after calling snippets_db_load, the snippets_db
+ * will be functional).
+ */
+void
+snippets_db_close (SnippetsDB *snippets_db)
+{
+ GList *iter = NULL;
+ AnjutaSnippetsGroup *cur_snippets_group = NULL;
+ SnippetsDBPrivate *priv = NULL;
+ GtkTreePath *path = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ g_return_if_fail (snippets_db->priv != NULL);
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ /* Free the memory for the snippets-groups in the SnippetsDB */
+ for (iter = g_list_first (priv->snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippets_group = (AnjutaSnippetsGroup *)iter->data;
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (cur_snippets_group));
+
+ /* Emit the signal that the snippet was deleted */
+ path = get_tree_path_for_snippets_group (snippets_db, cur_snippets_group);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (snippets_db), path);
+ gtk_tree_path_free (path);
+
+ g_object_unref (cur_snippets_group);
+
+ }
+ g_list_free (priv->snippets_groups);
+ priv->snippets_groups = NULL;
+
+ /* Unload the global variables */
+ gtk_list_store_clear (priv->global_variables);
+
+ /* Free the hash-table memory */
+ g_hash_table_ref (priv->snippet_keys_map);
+ g_hash_table_destroy (priv->snippet_keys_map);
+
+}
+
+void
+snippets_db_debug (SnippetsDB *snippets_db)
+{
+ SnippetsDBPrivate *priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+ GList *iter = NULL, *iter2 = NULL;
+
+ for (iter = g_list_first (priv->snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ if (ANJUTA_IS_SNIPPETS_GROUP (iter->data))
+ {
+ AnjutaSnippetsGroup *group = ANJUTA_SNIPPETS_GROUP (iter->data);
+ printf ("%s\n", snippets_group_get_name (group));
+ for (iter2 = g_list_first (snippets_group_get_snippets_list (group)); iter2 != NULL; iter2 = g_list_next (iter2))
+ {
+ if (ANJUTA_IS_SNIPPET (iter2->data))
+ {
+ AnjutaSnippet *s = ANJUTA_SNIPPET (iter2->data);
+ printf ("\t[%s | %s | %s]\n", snippet_get_name (s), snippet_get_trigger_key (s), snippet_get_languages_string (s));
+ }
+ else
+ printf ("\t(Invalid snippet)\n");
+ }
+ }
+ else
+ printf ("(Invalid Snippets Group)\n");
+ }
+}
+
+/**
+ * snippets_db_get_path_at_object:
+ * @snippets_db: A #SnippetsDB object.
+ * @obj: An #AnjutaSnippetsGroup or #AnjutaSnippet object.
+ *
+ * Gets the GtkTreePath at given snippet or snippets group.
+ *
+ * Returns: The GtkTreePath or NULL if the given object was not found.
+ */
+GtkTreePath *
+snippets_db_get_path_at_object (SnippetsDB *snippets_db,
+ GObject *obj)
+{
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+
+ if (ANJUTA_IS_SNIPPET (obj))
+ return get_tree_path_for_snippet (snippets_db, ANJUTA_SNIPPET (obj));
+
+ if (ANJUTA_IS_SNIPPETS_GROUP (obj))
+ return get_tree_path_for_snippets_group (snippets_db, ANJUTA_SNIPPETS_GROUP (obj));
+
+ g_return_val_if_reached (NULL);
+}
+
+
+void
+snippets_db_save_snippets (SnippetsDB *snippets_db)
+{
+ SnippetsDBPrivate *priv = NULL;
+ gchar *user_file_path = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ user_file_path =
+ anjuta_util_get_user_data_file_path (USER_SNIPPETS_DB_DIR, "/",
+ DEFAULT_SNIPPETS_FILE, NULL);
+
+ snippets_manager_save_snippets_xml_file (NATIVE_FORMAT, priv->snippets_groups, user_file_path);
+
+ g_free (user_file_path);
+}
+
+void
+snippets_db_save_global_vars (SnippetsDB *snippets_db)
+{
+ SnippetsDBPrivate *priv = NULL;
+ gchar *user_file_path = NULL;
+ GList *vars_names = NULL, *vars_values = NULL, *vars_comm = NULL, *l_iter = NULL;
+ GtkTreeIter iter;
+ gchar *name = NULL, *value = NULL;
+ gboolean is_command = FALSE, is_internal = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ user_file_path =
+ anjuta_util_get_user_data_file_path (USER_SNIPPETS_DB_DIR, "/",
+ DEFAULT_GLOBAL_VARS_FILE, NULL);
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->global_variables), &iter))
+ return;
+
+ do
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->global_variables), &iter,
+ GLOBAL_VARS_MODEL_COL_NAME, &name,
+ GLOBAL_VARS_MODEL_COL_VALUE, &value,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, &is_command,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ if (!is_internal)
+ {
+ vars_names = g_list_append (vars_names, name);
+ vars_values = g_list_append (vars_values, value);
+ vars_comm = g_list_append (vars_comm, GINT_TO_POINTER (is_command));
+ }
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->global_variables), &iter));
+
+ snippets_manager_save_variables_xml_file (user_file_path, vars_names, vars_values, vars_comm);
+
+ /* Free the data */
+ for (l_iter = g_list_first (vars_names); l_iter != NULL; l_iter = g_list_next (l_iter))
+ g_free (l_iter->data);
+ g_list_free (vars_names);
+ for (l_iter = g_list_first (vars_values); l_iter != NULL; l_iter = g_list_next (l_iter))
+ g_free (l_iter->data);
+ g_list_free (vars_values);
+ g_list_free (vars_comm);
+ g_free (user_file_path);
+}
+
+/**
+ * snippets_db_add_snippet:
+ * @snippets_db: A #SnippetsDB object
+ * @added_snippet: A #Snippet object
+ * @group_name: The name of the group where the snippet should be added.
+ *
+ * Adds the @added_snippet to the @snippets_db. If the user is conflicting, it will
+ * fail (or if the group wasn't found).
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+snippets_db_add_snippet (SnippetsDB* snippets_db,
+ AnjutaSnippet* added_snippet,
+ const gchar* group_name)
+{
+ GList *iter = NULL;
+ AnjutaSnippetsGroup *cur_snippets_group = NULL;
+ const gchar *cur_snippets_group_name = NULL;
+ GtkTreePath *path;
+ GtkTreeIter tree_iter;
+ SnippetsDBPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (added_snippet), FALSE);
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ /* Check that the snippet is not conflicting */
+ if (snippets_db_has_snippet (snippets_db, added_snippet))
+ return FALSE;
+
+ /* Lookup the AnjutaSnippetsGroup with the given group_name */
+ for (iter = g_list_first (priv->snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippets_group = ANJUTA_SNIPPETS_GROUP (iter->data);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (cur_snippets_group), FALSE);
+ cur_snippets_group_name = snippets_group_get_name (cur_snippets_group);
+
+ /* We found the group */
+ if (!g_strcmp0 (cur_snippets_group_name, group_name))
+ {
+ /* Add the snippet to the group */
+ snippets_group_add_snippet (cur_snippets_group, added_snippet);
+
+ /* Add to the Hashtable */
+ add_snippet_to_hash_table (snippets_db, added_snippet);
+
+ /* Emit the signal that the database was changed */
+ path = get_tree_path_for_snippet (snippets_db, added_snippet);
+ snippets_db_get_iter (GTK_TREE_MODEL (snippets_db), &tree_iter, path);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (snippets_db), path, &tree_iter);
+ gtk_tree_path_free (path);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+gboolean
+snippets_db_has_snippet (SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet)
+{
+ GtkTreePath *path = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+
+ path = get_tree_path_for_snippet (snippets_db, snippet);
+ if (path != NULL)
+ {
+ gtk_tree_path_free (path);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_db_get_snippet:
+ * @snippets_db: A #SnippetsDB object
+ * @trigger_key: The trigger-key of the requested snippet
+ * @language: The snippet language. NULL for auto-detection.
+ *
+ * Gets a snippet from the database for the given trigger-key. If language is NULL,
+ * it will get the snippet for the current editor language.
+ *
+ * Returns: The requested snippet (not a copy, should not be freed) or NULL if not found.
+ **/
+AnjutaSnippet*
+snippets_db_get_snippet (SnippetsDB* snippets_db,
+ const gchar* trigger_key,
+ const gchar* language)
+{
+ AnjutaSnippet *snippet = NULL;
+ gchar *snippet_key = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (trigger_key != NULL, NULL);
+
+ /* Get the editor language if not provided */
+ if (language == NULL)
+ {
+ IAnjutaDocumentManager *docman = NULL;
+ IAnjutaDocument *doc = NULL;
+ IAnjutaEditorLanguage *ieditor_language = NULL;
+ IAnjutaLanguage *ilanguage = NULL;
+
+ docman = anjuta_shell_get_interface (snippets_db->anjuta_shell,
+ IAnjutaDocumentManager,
+ NULL);
+ ilanguage = anjuta_shell_get_interface (snippets_db->anjuta_shell,
+ IAnjutaLanguage,
+ NULL);
+ g_return_val_if_fail (IANJUTA_IS_DOCUMENT_MANAGER (docman), NULL);
+ g_return_val_if_fail (IANJUTA_IS_LANGUAGE (ilanguage), NULL);
+
+ /* Get the current doc and make sure it's an editor */
+ doc = ianjuta_document_manager_get_current_document (docman, NULL);
+ if (!IANJUTA_IS_EDITOR_LANGUAGE (doc))
+ return NULL;
+ ieditor_language = IANJUTA_EDITOR_LANGUAGE (doc);
+
+
+ language = ianjuta_language_get_name_from_editor (ilanguage, ieditor_language, NULL);
+ }
+
+ /* Calculate the snippet-key */
+ snippet_key = get_snippet_key_from_trigger_and_language (trigger_key, language);
+ if (snippet_key == NULL)
+ return NULL;
+
+ /* Look up the the snippet in the hashtable */
+ snippet = g_hash_table_lookup (snippets_db->priv->snippet_keys_map, snippet_key);
+
+ g_free (snippet_key);
+ return snippet;
+}
+
+/**
+ * snippets_db_remove_snippet:
+ * @snippets_db: A #SnippetsDB object.
+ * @trigger-key: The snippet to be removed trigger-key.
+ * @language: The language of the snippet. This must not be NULL, as it won't take
+ * the document language.
+ * @remove_all_languages_support: If this is FALSE, it won't actually remove the snippet,
+ * but remove the given language support for the snippet.
+ *
+ * Removes a snippet from the #SnippetDB (or removes it's language support).
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+snippets_db_remove_snippet (SnippetsDB* snippets_db,
+ const gchar* trigger_key,
+ const gchar* language,
+ gboolean remove_all_languages_support)
+{
+ AnjutaSnippet *deleted_snippet = NULL;
+ AnjutaSnippetsGroup *deleted_snippet_group = NULL;
+ gchar *snippet_key = NULL;
+ GtkTreePath *path = NULL;
+ SnippetsDBPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ /* Get the snippet to be deleted */
+ snippet_key = get_snippet_key_from_trigger_and_language (trigger_key, language);
+ if (snippet_key == NULL)
+ return FALSE;
+
+ deleted_snippet = g_hash_table_lookup (priv->snippet_keys_map, snippet_key);
+ g_free (snippet_key);
+ if (!ANJUTA_IS_SNIPPET (deleted_snippet))
+ return FALSE;
+
+ if (remove_all_languages_support)
+ {
+ remove_snippet_from_hash_table (snippets_db, deleted_snippet);
+ }
+ else
+ {
+ /* We remove just the current language support from the database */
+ g_hash_table_remove (priv->snippet_keys_map, snippet_key);
+ }
+
+ /* Emit the signal that the snippet was deleted */
+ path = get_tree_path_for_snippet (snippets_db, deleted_snippet);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (snippets_db), path);
+ gtk_tree_path_free (path);
+
+ /* Remove it from the snippets group */
+ deleted_snippet_group = ANJUTA_SNIPPETS_GROUP (deleted_snippet->parent_snippets_group);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (deleted_snippet_group), FALSE);
+ snippets_group_remove_snippet (deleted_snippet_group,
+ trigger_key,
+ language,
+ remove_all_languages_support);
+
+ return TRUE;
+}
+
+/**
+ * snippets_db_add_snippets_group:
+ * @snippets_db: A #SnippetsDB object
+ * @snippets_group: A #AnjutaSnippetsGroup object
+ * @overwrite_group: If a #AnjutaSnippetsGroup with the same name exists it will
+ * be overwriten.
+ *
+ * Adds an #AnjutaSnippetsGroup to the database, checking for conflicts. The @snippets_group
+ * passed as argument will have it's reference increased by one. The snippets which are conflicting
+ * won't be added.
+ *
+ * Returns: TRUE on success
+ **/
+gboolean
+snippets_db_add_snippets_group (SnippetsDB* snippets_db,
+ AnjutaSnippetsGroup* snippets_group,
+ gboolean overwrite_group)
+{
+ AnjutaSnippet *cur_snippet = NULL;
+ GList *iter = NULL, *snippets_list = NULL;
+ SnippetsDBPrivate *priv = NULL;
+ GtkTreeIter tree_iter;
+ GtkTreePath *path;
+ const gchar *group_name = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), FALSE);
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ /* If we should overwrite an existing group, we remove it. */
+ group_name = snippets_group_get_name (snippets_group);
+ if (overwrite_group)
+ snippets_db_remove_snippets_group (snippets_db, group_name);
+ else
+ if (snippets_db_has_snippets_group_name (snippets_db, group_name))
+ return FALSE;
+
+ /* Check for conflicts */
+ snippets_list = snippets_group_get_snippets_list (snippets_group);
+ for (iter = g_list_first (snippets_list); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet = ANJUTA_SNIPPET (iter->data);
+ if (!ANJUTA_IS_SNIPPET (cur_snippet))
+ continue;
+
+ if (snippets_db_has_snippet (snippets_db, cur_snippet))
+ {
+ snippets_group_remove_snippet (snippets_group,
+ snippet_get_trigger_key (cur_snippet),
+ snippet_get_any_language (cur_snippet),
+ TRUE);
+ }
+ else
+ {
+ add_snippet_to_hash_table (snippets_db, cur_snippet);
+ }
+ }
+
+ /* Add the snippets_group to the database keeping sorted the list by the
+ group name. */
+ priv->snippets_groups = g_list_insert_sorted (priv->snippets_groups,
+ snippets_group,
+ compare_snippets_groups_by_name);
+ g_object_ref (snippets_group);
+
+ /* Emit the signal that the database was changed */
+ path = get_tree_path_for_snippets_group (snippets_db, snippets_group);
+ snippets_db_get_iter (GTK_TREE_MODEL (snippets_db), &tree_iter, path);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (snippets_db), path, &tree_iter);
+ gtk_tree_path_free (path);
+
+ return TRUE;
+}
+
+/**
+ * snippets_db_remove_snippets_group:
+ * @snippets_db: A #SnippetsDB object.
+ * @group_name: The name of the group to be removed.
+ *
+ * Removes a snippet group (and it's containing snippets) from the #SnippetsDB
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+snippets_db_remove_snippets_group (SnippetsDB* snippets_db,
+ const gchar* group_name)
+{
+ GList *iter = NULL;
+ AnjutaSnippetsGroup *snippets_group = NULL;
+ GtkTreePath *path = NULL;
+ SnippetsDBPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (group_name != NULL, FALSE);
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ for (iter = g_list_first (priv->snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ snippets_group = ANJUTA_SNIPPETS_GROUP (iter->data);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), FALSE);
+
+ if (!g_strcmp0 (group_name, snippets_group_get_name (snippets_group)))
+ {
+ /* Remove the snippets in the group from the hash-table */
+ remove_snippets_group_from_hash_table (snippets_db, snippets_group);
+
+ /* Emit the signal that it was deleted */
+ path = get_tree_path_for_snippets_group (snippets_db, snippets_group);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (snippets_db), path);
+ gtk_tree_path_free (path);
+
+ /* Destroy the snippets-group object */
+ g_object_unref (snippets_group);
+
+ /* Delete the list node */
+ iter->data = NULL;
+ priv->snippets_groups = g_list_delete_link (priv->snippets_groups, iter);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_db_get_snippets_group:
+ * @snippets_db: A #SnippetsDB object.
+ * @group_name: The name of the #AnjutaSnippetsGroup requested object.
+ *
+ * Returns: The requested #AnjutaSnippetsGroup object or NULL on failure.
+ */
+AnjutaSnippetsGroup*
+snippets_db_get_snippets_group (SnippetsDB* snippets_db,
+ const gchar* group_name)
+{
+ AnjutaSnippetsGroup *snippets_group = NULL;
+ SnippetsDBPrivate *priv = NULL;
+ GList *iter = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+
+ /* Look up the AnjutaSnippetsGroup object with the name being group_name */
+ priv = snippets_db->priv;
+ for (iter = g_list_first (priv->snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ snippets_group = (AnjutaSnippetsGroup *)iter->data;
+ if (!g_strcmp0 (snippets_group_get_name (snippets_group), group_name))
+ {
+ return snippets_group;
+ }
+ }
+
+ return NULL;
+}
+
+
+void
+snippets_db_set_snippets_group_name (SnippetsDB *snippets_db,
+ const gchar *old_group_name,
+ const gchar *new_group_name)
+{
+ AnjutaSnippetsGroup *snippets_group = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ /* Make sure the new name doesen't cause a conflict */
+ if (snippets_db_has_snippets_group_name (snippets_db, new_group_name))
+ return;
+
+ snippets_group = snippets_db_get_snippets_group (snippets_db, old_group_name);
+ if (!ANJUTA_IS_SNIPPETS_GROUP (snippets_group))
+ return;
+
+ /* Remove the group, but don't destroy it and add it with the new name */
+ g_object_ref (snippets_group);
+ snippets_db_remove_snippets_group (snippets_db, old_group_name);
+ snippets_group_set_name (snippets_group, new_group_name);
+ snippets_db_add_snippets_group (snippets_db, snippets_group, TRUE);
+ g_object_unref (snippets_group);
+}
+
+gboolean
+snippets_db_has_snippets_group_name (SnippetsDB *snippets_db,
+ const gchar *group_name)
+{
+ return ANJUTA_IS_SNIPPETS_GROUP (snippets_db_get_snippets_group (snippets_db, group_name));
+}
+
+/**
+ * snippets_db_get_global_variable_text:
+ * @snippets_db: A #SnippetsDB object.
+ * @variable_name: The variable name.
+ *
+ * Gets the actual text of the variable. If it's a command it will return the command,
+ * not the output. If it's static it will have the same result as #snippets_db_get_global_variable.
+ * If it's internal it will return an empty string.
+ *
+ * Returns: The actual text of the global variable.
+ */
+gchar*
+snippets_db_get_global_variable_text (SnippetsDB* snippets_db,
+ const gchar* variable_name)
+{
+ GtkTreeIter *iter = NULL;
+ GtkListStore *global_vars_store = NULL;
+ gboolean is_internal = FALSE;
+ gchar *value = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (snippets_db->priv != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), NULL);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Search for the variable */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ if (iter)
+ {
+ /* Check if it's internal or not */
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ /* If it's internal we return an empty string */
+ if (is_internal)
+ {
+ return g_strdup("");
+ }
+ else
+ {
+ /* If it's a command we launch that command and return the output */
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_VALUE, &value,
+ -1);
+ return value;
+ }
+ }
+
+ return NULL;
+
+}
+
+/**
+ * snippets_db_get_global_variable:
+ * @snippets_db: A #SnippetsDB object.
+ * @variable_name: The name of the global variable.
+ *
+ * Gets the value of a global variable. A global variable value can be static,the output of a
+ * command or internal.
+ *
+ * Returns: The value of the global variable, or NULL if the variable wasn't found.
+ */
+gchar*
+snippets_db_get_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name)
+{
+ GtkTreeIter *iter = NULL;
+ GtkListStore *global_vars_store = NULL;
+ gboolean is_command = FALSE, is_internal = FALSE, command_success = FALSE;
+ gchar *value = NULL, *command_line = NULL, *command_output = NULL, *command_error = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (snippets_db->priv != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), NULL);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Search for the variable */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ if (iter)
+ {
+ /* Check if it's a command/internal or not */
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, &is_command,
+ -1);
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ /* If it's internal we call a function defined above to compute the value */
+ if (is_internal)
+ {
+ return get_internal_global_variable_value (snippets_db->anjuta_shell,
+ variable_name);
+ }
+ /* If it's a command we launch that command and return the output */
+ else if (is_command)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_VALUE, &command_line,
+ -1);
+ command_success = g_spawn_command_line_sync (command_line,
+ &command_output,
+ &command_error,
+ NULL,
+ NULL);
+ g_free (command_line);
+ g_free (command_error);
+ if (command_success)
+ {
+ /* If the last character is a newline we eliminate it */
+ gint command_output_size = 0;
+ while (command_output[command_output_size] != 0)
+ command_output_size ++;
+ if (command_output[command_output_size - 1] == '\n')
+ command_output[command_output_size - 1] = 0;
+
+ return command_output;
+ }
+ }
+ /* If it's static just return the value stored */
+ else
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_VALUE, &value,
+ -1);
+ return value;
+
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * snippets_db_has_global_variable:
+ * @snippets_db: A #SnippetsDB object.
+ * @variable_name: A variable name.
+ *
+ * Checks if the Snippet Database has an entry with a variable name as requested.
+ *
+ * Returns: TRUE if the global variable exists.
+ */
+gboolean
+snippets_db_has_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name)
+{
+ GtkListStore *global_vars_store = NULL;
+ GtkTreeIter *iter = NULL;
+ gboolean found = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (snippets_db->priv != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), FALSE);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Locate the variable in the GtkListStore */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ found = (iter != NULL);
+ if (iter)
+ gtk_tree_iter_free (iter);
+
+ return found;
+}
+
+/**
+ * snippets_db_add_global_variable:
+ * @snippets_db: A #SnippetsDB object.
+ * @variable_name: A variable name.
+ * @variable_value: The global variable value.
+ * @variable_is_command: If the variable is the output of a command.
+ * @overwrite: If a global variable with the same name exists, it should be overwriten.
+ *
+ * Adds a global variable to the Snippets Database.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_db_add_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name,
+ const gchar* variable_value,
+ gboolean variable_is_command,
+ gboolean overwrite)
+{
+ GtkTreeIter *iter = NULL, iter_to_add;
+ GtkListStore *global_vars_store = NULL;
+ gboolean is_internal = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (snippets_db->priv != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), FALSE);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Check to see if there is a global variable with the same name in the database */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ if (iter)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ /* If it's internal it can't be overwriten */
+ if (overwrite && !is_internal)
+ {
+ gtk_list_store_set (global_vars_store, iter,
+ GLOBAL_VARS_MODEL_COL_NAME, variable_name,
+ GLOBAL_VARS_MODEL_COL_VALUE, variable_value,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, variable_is_command,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, FALSE,
+ -1);
+ gtk_tree_iter_free (iter);
+ return TRUE;
+ }
+ else
+ {
+ gtk_tree_iter_free (iter);
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* Add the the global_vars_store */
+ gtk_list_store_append (global_vars_store, &iter_to_add);
+ gtk_list_store_set (global_vars_store, &iter_to_add,
+ GLOBAL_VARS_MODEL_COL_NAME, variable_name,
+ GLOBAL_VARS_MODEL_COL_VALUE, variable_value,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, variable_is_command,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, FALSE,
+ -1);
+ }
+ return TRUE;
+}
+
+/**
+ * snippets_db_set_global_variable_name:
+ * @snippets_db: A #SnippetsDB object.
+ * @variable_old_name: The old name of the variable
+ * @variable_new_name: The name with which it should be changed.
+ *
+ * Changed the Global Variable name.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_db_set_global_variable_name (SnippetsDB* snippets_db,
+ const gchar* variable_old_name,
+ const gchar* variable_new_name)
+{
+ GtkListStore *global_vars_store = NULL;
+ GtkTreeIter *iter = NULL;
+ gboolean is_internal = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (snippets_db->priv != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), FALSE);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Test if the variable_new_name is already in the database */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_new_name);
+ if (iter)
+ {
+ gtk_tree_iter_free (iter);
+ return FALSE;
+ }
+
+ /* Get a GtkTreeIter pointing at the global variable to be updated */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_old_name);
+ if (iter)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ if (!is_internal)
+ {
+ gtk_list_store_set (global_vars_store, iter,
+ GLOBAL_VARS_MODEL_COL_NAME, variable_new_name,
+ -1);
+ gtk_tree_iter_free (iter);
+ return TRUE;
+ }
+ else
+ {
+ gtk_tree_iter_free (iter);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_db_set_global_variable_value:
+ * @snippets_db: A #SnippetsDB value.
+ * @variable_name: The name of the global variable to be updated.
+ * @variable_new_value: The new value to be set to the variable.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_db_set_global_variable_value (SnippetsDB* snippets_db,
+ const gchar* variable_name,
+ const gchar* variable_new_value)
+{
+ GtkListStore *global_vars_store = NULL;
+ GtkTreeIter *iter = NULL;
+ gboolean is_internal = FALSE;
+ gchar *stored_value = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (snippets_db->priv != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), FALSE);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Get a GtkTreeIter pointing at the global variable to be updated */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ if (iter)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_VALUE, &stored_value,
+ -1);
+
+ if (!is_internal)
+ {
+ gtk_list_store_set (global_vars_store, iter,
+ GLOBAL_VARS_MODEL_COL_VALUE, variable_new_value,
+ -1);
+
+ g_free (stored_value);
+ gtk_tree_iter_free (iter);
+
+ return TRUE;
+ }
+ else
+ {
+ g_free (stored_value);
+ gtk_tree_iter_free (iter);
+
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_db_set_global_variable_type:
+ * @snippets_db: A #SnippetsDB value.
+ * @variable_name: The name of the global variable to be updated.
+ * @is_command: TRUE if after the update the global variable should be considered a command.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_db_set_global_variable_type (SnippetsDB *snippets_db,
+ const gchar* variable_name,
+ gboolean is_command)
+{
+ GtkListStore *global_vars_store = NULL;
+ GtkTreeIter *iter = NULL;
+ gboolean is_internal = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (snippets_db->priv != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), FALSE);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Get a GtkTreeIter pointing at the global variable to be updated */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ if (iter)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ if (!is_internal)
+ {
+ gtk_list_store_set (global_vars_store, iter,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND, is_command,
+ -1);
+ gtk_tree_iter_free (iter);
+ return TRUE;
+ }
+ else
+ {
+ gtk_tree_iter_free (iter);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_db_remove_global_variable:
+ * @snippets_db: A #SnippetsDB object
+ * @group_name: The name of the global variable to be removed.
+ *
+ * Removes a global variable from the database. Only works for static or command
+ * based variables.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_db_remove_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name)
+{
+ GtkListStore *global_vars_store = NULL;
+ GtkTreeIter *iter = NULL;
+ gboolean is_internal = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ g_return_val_if_fail (snippets_db->priv != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), FALSE);
+ global_vars_store = snippets_db->priv->global_variables;
+
+ /* Get a GtkTreeIter pointing at the global variable to be removed */
+ iter = get_iter_at_global_variable_name (global_vars_store, variable_name);
+ if (iter)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (global_vars_store), iter,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL, &is_internal,
+ -1);
+
+ if (!is_internal)
+ {
+ gtk_list_store_remove (global_vars_store, iter);
+ gtk_tree_iter_free (iter);
+ return TRUE;
+ }
+ else
+ {
+ gtk_tree_iter_free (iter);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_db_get_global_vars_model:
+ * snippets_db: A #SnippetsDB object.
+ *
+ * The returned GtkTreeModel is basically a list with the global variables that
+ * should be used for displaying the variables. At the moment, it's used for
+ * displaying the variables in the preferences.
+ *
+ * Returns: The #GtkTreeModel of the global variables list.
+ * Warning: This isn't a copy, shouldn't be freed or unrefed.
+ */
+GtkTreeModel*
+snippets_db_get_global_vars_model (SnippetsDB* snippets_db)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (snippets_db->priv != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (snippets_db->priv->global_variables), NULL);
+
+ return GTK_TREE_MODEL (snippets_db->priv->global_variables);
+}
+
+/* GtkTreeModel methods definition */
+
+static GObject *
+iter_get_data (GtkTreeIter *iter)
+{
+ GList *cur_node = NULL;
+
+ g_return_val_if_fail (iter != NULL, NULL);
+ if (iter->user_data == NULL)
+ return NULL;
+ cur_node = (GList *)iter->user_data;
+ if (cur_node == NULL)
+ return NULL;
+ if (!G_IS_OBJECT (cur_node->data))
+ return NULL;
+
+ return G_OBJECT (cur_node->data);
+}
+
+static gboolean
+iter_is_snippets_group_node (GtkTreeIter *iter)
+{
+ GObject *data = iter_get_data (iter);
+
+ return ANJUTA_IS_SNIPPETS_GROUP (data);
+}
+
+static gboolean
+iter_is_snippet_node (GtkTreeIter *iter)
+{
+ GObject *data = iter_get_data (iter);
+
+ return ANJUTA_IS_SNIPPET (data);
+}
+
+static gboolean
+iter_get_first_snippets_db_node (GtkTreeIter *iter,
+ SnippetsDB *snippets_db)
+{
+ SnippetsDBPrivate *priv = NULL;
+
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+ priv = ANJUTA_SNIPPETS_DB_GET_PRIVATE (snippets_db);
+
+ iter->user_data = g_list_first (priv->snippets_groups);
+ iter->user_data2 = NULL;
+ iter->user_data3 = NULL;
+ iter->stamp = snippets_db->stamp;
+
+ return iter->user_data != NULL;
+}
+
+static gboolean
+iter_nth (GtkTreeIter *iter, gint n)
+{
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ iter->user_data = g_list_nth ((GList *)iter->user_data, n);
+
+ return (iter->user_data != NULL);
+}
+
+static GList *
+iter_get_list_node (GtkTreeIter *iter)
+{
+ g_return_val_if_fail (iter != NULL, NULL);
+
+ return iter->user_data;
+}
+
+static GtkTreeModelFlags
+snippets_db_get_flags (GtkTreeModel *tree_model)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), (GtkTreeModelFlags)0);
+
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint
+snippets_db_get_n_columns (GtkTreeModel *tree_model)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), 0);
+
+ return SNIPPETS_DB_MODEL_COL_N;
+}
+
+static GType
+snippets_db_get_column_type (GtkTreeModel *tree_model,
+ gint index)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), G_TYPE_INVALID);
+ g_return_val_if_fail (index >= 0 && index < SNIPPETS_DB_MODEL_COL_N, G_TYPE_INVALID);
+
+ if (index == 0)
+ return G_TYPE_OBJECT;
+ else
+ return G_TYPE_STRING;
+}
+
+static gboolean
+snippets_db_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ SnippetsDB *snippets_db = NULL;
+ gint *indices = NULL, depth = 0, db_count = 0, group_count = 0;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* Get the indices and depth of the path */
+ indices = gtk_tree_path_get_indices (path);
+ depth = gtk_tree_path_get_depth (path);
+ if (depth > SNIPPETS_DB_MODEL_DEPTH)
+ return FALSE;
+
+ db_count = indices[0];
+ if (depth == 2)
+ group_count = indices[1];
+
+ /* Get the top-level iter with the count being db_count */
+ iter_get_first_snippets_db_node (iter, snippets_db);
+ if (!iter_nth (iter, db_count))
+ return FALSE;
+
+ /* If depth is SNIPPETS_DB_MODEL_DEPTH, get the group_count'th child */
+ if (depth == SNIPPETS_DB_MODEL_DEPTH)
+ return snippets_db_iter_nth_child (tree_model, iter, iter, group_count);
+
+ return TRUE;
+}
+
+static GtkTreePath*
+snippets_db_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreePath *path = NULL;
+ GtkTreeIter *iter_copy = NULL;
+ SnippetsDB *snippets_db = NULL;
+ gint count = 0;
+ GList *l_iter = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), NULL);
+ g_return_val_if_fail (iter != NULL, NULL);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* Make a new GtkTreePath object */
+ path = gtk_tree_path_new ();
+
+ /* Get the first index */
+ l_iter = iter_get_list_node (iter);
+ while (l_iter != NULL)
+ {
+ count ++;
+ l_iter = g_list_previous (l_iter);
+ }
+ gtk_tree_path_append_index (path, count);
+
+ /* If it's a snippet node, then it has a parent */
+ count = 0;
+ if (iter_is_snippet_node (iter))
+ {
+ iter_copy = gtk_tree_iter_copy (iter);
+
+ snippets_db_iter_parent (tree_model, iter_copy, iter);
+ l_iter = iter_get_list_node (iter_copy);
+ while (l_iter != NULL)
+ {
+ count ++;
+ l_iter = g_list_previous (l_iter);
+ }
+ gtk_tree_iter_free (iter);
+ }
+
+ return path;
+}
+
+static void
+snippets_db_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ SnippetsDB *snippets_db = NULL;
+ GObject *cur_object = NULL;
+ gchar *cur_string = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (column >= 0 && column < SNIPPETS_DB_MODEL_COL_N);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* Initializations */
+ g_value_init (value, snippets_db_get_column_type (tree_model, column));
+ cur_object = iter_get_data (iter);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (cur_object) || ANJUTA_IS_SNIPPET (cur_object));
+
+ /* Get the data in the node */
+ switch (column)
+ {
+ case SNIPPETS_DB_MODEL_COL_CUR_OBJECT:
+ g_value_set_object (value, cur_object);
+ return;
+
+ case SNIPPETS_DB_MODEL_COL_NAME:
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ cur_string = g_strdup (snippet_get_name (ANJUTA_SNIPPET (cur_object)));
+ else
+ cur_string = g_strdup (snippets_group_get_name (ANJUTA_SNIPPETS_GROUP (cur_object)));
+ g_value_set_string (value, cur_string);
+ return;
+
+ case SNIPPETS_DB_MODEL_COL_TRIGGER:
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ cur_string = g_strdup (snippet_get_trigger_key (ANJUTA_SNIPPET (cur_object)));
+ else
+ cur_string = g_strdup ("");
+ g_value_set_string (value, cur_string);
+ return;
+
+ case SNIPPETS_DB_MODEL_COL_LANGUAGES:
+ if (ANJUTA_IS_SNIPPET (cur_object))
+ cur_string = g_strdup (snippet_get_languages_string (ANJUTA_SNIPPET (cur_object)));
+ else
+ cur_string = g_strdup ("");
+ g_value_set_string (value, cur_string);
+ }
+}
+
+static gboolean
+snippets_db_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ SnippetsDB* snippets_db = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* Check if the stamp is correct */
+ g_return_val_if_fail (snippets_db->stamp == iter->stamp,
+ FALSE);
+
+ /* Update the iter and return TRUE if it didn't reached the end*/
+ iter->user_data = g_list_next ((GList *)iter->user_data);
+
+ return (iter->user_data != NULL);
+}
+
+static gboolean
+snippets_db_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ return snippets_db_iter_nth_child (tree_model, iter, parent, 0);
+}
+
+static gboolean
+snippets_db_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GList *snippets_list = NULL;
+ AnjutaSnippetsGroup *snippets_group = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ /* If the parent field is NULL then it's the 1st level so it has children */
+ if (iter_is_snippets_group_node (iter))
+ {
+ snippets_group = ANJUTA_SNIPPETS_GROUP (iter_get_data (iter));
+ snippets_list = (GList *)snippets_group_get_snippets_list (snippets_group);
+ return (g_list_length (snippets_list) != 0);
+ }
+ else
+ return FALSE;
+}
+
+static gint
+snippets_db_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ const GList* snippets_list = NULL;
+ SnippetsDB *snippets_db = NULL;
+ AnjutaSnippetsGroup *snippets_group = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), -1);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* If a top-level count is requested */
+ if (iter == NULL)
+ {
+ return (gint)g_list_length (snippets_db->priv->snippets_groups);
+ }
+
+ /* If iter points to a SnippetsGroup node */
+ if (iter_is_snippets_group_node (iter))
+ {
+ /* Get the Snippets Group object and assert it */
+ snippets_group = ANJUTA_SNIPPETS_GROUP (iter_get_data (iter));
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group),
+ -1);
+
+ snippets_list = snippets_group_get_snippets_list (snippets_group);
+ return (gint)g_list_length ((GList *)snippets_list);
+ }
+
+ /* If iter points to a Snippet node than it has no children. */
+ return 0;
+}
+
+static gboolean
+snippets_db_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ SnippetsDB *snippets_db = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), FALSE);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* If it's a top level request */
+ if (parent == NULL)
+ {
+ iter_get_first_snippets_db_node (iter, snippets_db);
+ return iter_nth (iter, n);
+ }
+
+ if (iter_is_snippets_group_node (parent))
+ {
+ AnjutaSnippetsGroup *snippets_group = ANJUTA_SNIPPETS_GROUP (iter_get_data (parent));
+ GList *snippets_list = (GList *)snippets_group_get_snippets_list (snippets_group);
+
+ iter->user_data2 = parent->user_data;
+ iter->user_data = g_list_first (snippets_list);
+ iter->stamp = parent->stamp;
+
+ return iter_nth (iter, n);
+ }
+
+ /* If we got to this point, it's a snippet node, so it doesn't have a child*/
+ return FALSE;
+}
+
+static gboolean
+snippets_db_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ SnippetsDB *snippets_db = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (tree_model), FALSE);
+ g_return_val_if_fail (child != NULL, FALSE);
+ snippets_db = ANJUTA_SNIPPETS_DB (tree_model);
+
+ /* If it's a snippets group node, it doesn't have a parent */
+ if (iter_is_snippets_group_node (child))
+ return FALSE;
+
+ /* Fill the iter fields */
+ iter->user_data = child->user_data2;
+ iter->user_data2 = NULL;
+ iter->stamp = child->stamp;
+
+ return TRUE;
+}
+
+static GtkTreePath *
+get_tree_path_for_snippets_group (SnippetsDB *snippets_db,
+ AnjutaSnippetsGroup *snippets_group)
+{
+ GtkTreeIter iter;
+ gint index = 0;
+ const gchar *group_name = NULL;
+ AnjutaSnippetsGroup *cur_group = NULL;
+ GtkTreePath *path = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), NULL);
+
+ group_name = snippets_group_get_name (snippets_group);
+ path = gtk_tree_path_new ();
+
+ if (!iter_get_first_snippets_db_node (&iter, snippets_db))
+ return NULL;
+
+ do
+ {
+ cur_group = ANJUTA_SNIPPETS_GROUP (iter_get_data (&iter));
+ if (!ANJUTA_IS_SNIPPETS_GROUP (cur_group))
+ {
+ index ++;
+ continue;
+ }
+
+ if (!g_strcmp0 (snippets_group_get_name (cur_group), group_name))
+ {
+ gtk_tree_path_append_index (path, index);
+ return path;
+ }
+ index ++;
+
+ } while (snippets_db_iter_next (GTK_TREE_MODEL (snippets_db), &iter));
+
+ gtk_tree_path_free (path);
+ return NULL;
+}
+
+static GtkTreePath *
+get_tree_path_for_snippet (SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet)
+{
+ GtkTreePath *path = NULL;
+ gint index1 = 0, index2 = 0;
+ GtkTreeIter iter1, iter2;
+ AnjutaSnippet *cur_snippet = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ path = gtk_tree_path_new ();
+
+ if (!iter_get_first_snippets_db_node (&iter1, snippets_db))
+ return NULL;
+
+ do
+ {
+ index2 = 0;
+ snippets_db_iter_nth_child (GTK_TREE_MODEL (snippets_db), &iter2, &iter1, 0);
+
+ do
+ {
+ cur_snippet = ANJUTA_SNIPPET (iter_get_data (&iter2));
+ if (!ANJUTA_IS_SNIPPET (cur_snippet))
+ {
+ index2 ++;
+ continue;
+ }
+ if (snippet_is_equal (snippet, cur_snippet))
+ {
+ gtk_tree_path_append_index (path, index1);
+ gtk_tree_path_append_index (path, index2);
+ return path;
+ }
+
+ index2 ++;
+
+ } while (snippets_db_iter_next (GTK_TREE_MODEL (snippets_db), &iter2));
+
+ index1 ++;
+
+ } while (snippets_db_iter_next (GTK_TREE_MODEL (snippets_db), &iter1));
+
+ gtk_tree_path_free (path);
+ return NULL;
+}
diff --git a/plugins/snippets-manager/snippets-db.h b/plugins/snippets-manager/snippets-db.h
new file mode 100644
index 0000000..9be254a
--- /dev/null
+++ b/plugins/snippets-manager/snippets-db.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-db.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_DB_H__
+#define __SNIPPETS_DB_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "snippet.h"
+#include "snippets-group.h"
+#include <libanjuta/anjuta-shell.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SnippetsDB SnippetsDB;
+typedef struct _SnippetsDBPrivate SnippetsDBPrivate;
+typedef struct _SnippetsDBClass SnippetsDBClass;
+
+#define ANJUTA_TYPE_SNIPPETS_DB (snippets_db_get_type ())
+#define ANJUTA_SNIPPETS_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPETS_DB, SnippetsDB))
+#define ANJUTA_SNIPPETS_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPETS_DB, SnippetsDBClass))
+#define ANJUTA_IS_SNIPPETS_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPETS_DB))
+#define ANJUTA_IS_SNIPPETS_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPETS_DB))
+
+/**
+ * @SNIPPETS_DB_MODEL_COL_CUR_OBJECT: An #AnjutaSnippet or #AnjutaSnippetsGroup object.
+ * @SNIPPETS_DB_MODEL_COL_NAME: The name of the #AnjutaSnippet or #AnjutaSnippetsGroup.
+ * @SNIPPETS_DB_MODEL_COL_TRIGGER: The trigger of the #AnjutaSnippet or "".
+ * @SNIPPETS_DB_MODEL_COL_LANGUAGES: The supported languages of the #AnjutaSnippet or "".
+ *
+ * SnippetsDB Tree Model columns.
+ *
+ */
+enum
+{
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT = 0,
+ SNIPPETS_DB_MODEL_COL_NAME,
+ SNIPPETS_DB_MODEL_COL_TRIGGER,
+ SNIPPETS_DB_MODEL_COL_LANGUAGES,
+ SNIPPETS_DB_MODEL_COL_N
+};
+
+enum
+{
+ GLOBAL_VARS_MODEL_COL_NAME = 0,
+ GLOBAL_VARS_MODEL_COL_VALUE,
+ GLOBAL_VARS_MODEL_COL_IS_COMMAND,
+ GLOBAL_VARS_MODEL_COL_IS_INTERNAL,
+ GLOBAL_VARS_MODEL_COL_N
+};
+
+struct _SnippetsDB
+{
+ GObject object;
+
+ AnjutaShell* anjuta_shell;
+
+ gint stamp;
+
+ /*< private >*/
+ SnippetsDBPrivate* priv;
+};
+
+struct _SnippetsDBClass
+{
+ GObjectClass klass;
+
+};
+
+typedef enum
+{
+ NATIVE_FORMAT = 0,
+ GEDIT_FORMAT
+} FormatType;
+
+
+GType snippets_db_get_type (void) G_GNUC_CONST;
+SnippetsDB* snippets_db_new (void);
+
+void snippets_db_load (SnippetsDB *snippets_db);
+void snippets_db_close (SnippetsDB *snippets_db);
+
+GtkTreePath * snippets_db_get_path_at_object (SnippetsDB *snippets_db,
+ GObject *obj);
+void snippets_db_save_snippets (SnippetsDB *snippets_db);
+void snippets_db_save_global_vars (SnippetsDB *snippets_db);
+void snippets_db_debug (SnippetsDB *snippets_db);
+
+/* Snippet handling methods */
+gboolean snippets_db_add_snippet (SnippetsDB* snippets_db,
+ AnjutaSnippet* added_snippet,
+ const gchar* group_name);
+gboolean snippets_db_has_snippet (SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet);
+AnjutaSnippet* snippets_db_get_snippet (SnippetsDB* snippets_db,
+ const gchar* trigger_key,
+ const gchar* language);
+gboolean snippets_db_remove_snippet (SnippetsDB* snippets_db,
+ const gchar* trigger_key,
+ const gchar* language,
+ gboolean remove_all_languages_support);
+
+/* SnippetsGroup handling methods */
+gboolean snippets_db_add_snippets_group (SnippetsDB* snippets_db,
+ AnjutaSnippetsGroup* snippets_group,
+ gboolean overwrite_group);
+AnjutaSnippetsGroup* snippets_db_get_snippets_group (SnippetsDB* snippets_db,
+ const gchar* group_name);
+gboolean snippets_db_remove_snippets_group (SnippetsDB* snippets_db,
+ const gchar* group_name);
+void snippets_db_set_snippets_group_name (SnippetsDB *snippets_db,
+ const gchar *old_group_name,
+ const gchar *new_group_name);
+gboolean snippets_db_has_snippets_group_name (SnippetsDB *snippets_db,
+ const gchar *group_name);
+
+/* Global variables handling methods */
+gboolean snippets_db_add_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name,
+ const gchar* variable_value,
+ gboolean variable_is_command,
+ gboolean overwrite);
+gboolean snippets_db_set_global_variable_name (SnippetsDB* snippets_db,
+ const gchar* variable_old_name,
+ const gchar* variable_new_name);
+gboolean snippets_db_set_global_variable_value (SnippetsDB* snippets_db,
+ const gchar* variable_name,
+ const gchar* variable_new_value);
+gboolean snippets_db_set_global_variable_type (SnippetsDB *snippets_db,
+ const gchar* variable_name,
+ gboolean is_command);
+gchar* snippets_db_get_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name);
+gchar* snippets_db_get_global_variable_text (SnippetsDB* snippets_db,
+ const gchar* variable_name);
+gboolean snippets_db_remove_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name);
+gboolean snippets_db_has_global_variable (SnippetsDB* snippets_db,
+ const gchar* variable_name);
+GtkTreeModel* snippets_db_get_global_vars_model (SnippetsDB* snippes_db);
+
+G_END_DECLS
+
+#endif /* __SNIPPETS_DB_H__ */
diff --git a/plugins/snippets-manager/snippets-editor.c b/plugins/snippets-manager/snippets-editor.c
new file mode 100644
index 0000000..7456dc4
--- /dev/null
+++ b/plugins/snippets-manager/snippets-editor.c
@@ -0,0 +1,2012 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-editor.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-editor.h"
+#include "snippet-variables-store.h"
+#include <string.h>
+#include <libanjuta/interfaces/ianjuta-language.h>
+#include <libanjuta/interfaces/ianjuta-editor-language.h>
+
+#define EDITOR_UI PACKAGE_DATA_DIR"/glade/snippets-editor.ui"
+
+#define ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_SNIPPETS_EDITOR, SnippetsEditorPrivate))
+
+#define LOCAL_TYPE_STR "Snippet"
+#define GLOBAL_TYPE_STR "Anjuta"
+
+#define UNDEFINED_BG_COLOR "#ffbaba"
+
+#define NAME_COL_TITLE _("Name")
+#define TYPE_COL_TITLE _("Type")
+#define DEFAULT_COL_TITLE _("Default value")
+#define INSTANT_COL_TITLE _("Instant value")
+
+#define MIN_NAME_COL_WIDTH 120
+
+#define NEW_VAR_NAME "new_variable"
+
+#define GROUPS_COL_NAME 0
+
+#define ERROR_LANG_NULL _("<b>Error:</b> You must choose at least one language for the snippet!")
+#define ERROR_LANG_CONFLICT _("<b>Error:</b> The trigger key is already in use for one of the languages!")
+#define ERROR_TRIGGER_NOT_VALID _("<b>Error:</b> The trigger key can only contain alfanumeric characters and _ !")
+#define ERROR_TRIGGER_NULL _("<b>Error:</b> You haven't entered a trigger key for the snippet!")
+
+#define SNIPPET_VAR_START "${"
+#define SNIPPET_VAR_END "}"
+#define IS_SNIPPET_VAR_START(text, index) (text[index] == '$' && text[index + 1] == '{')
+#define IS_SNIPPET_VAR_END(text, index) (text[index] == '}')
+
+struct _SnippetsEditorPrivate
+{
+ SnippetsDB *snippets_db;
+ AnjutaSnippet *snippet;
+ AnjutaSnippet *backup_snippet;
+ GtkListStore *group_store;
+ GtkListStore *lang_store;
+
+ /* A tree model with 2 entries: LOCAL_TYPE_STR and GLOBAL_TYPE_STR to be used
+ by the variables view on the type column */
+ GtkListStore *type_model;
+
+ /* Snippet Content editor widgets */
+ GtkTextView *content_text_view; /* TODO - this should be changed later with GtkSourceView */
+ GtkToggleButton *preview_button;
+
+ /* Snippet properties widgets */
+ GtkEntry *name_entry;
+ GtkEntry *trigger_entry;
+ GtkEntry *keywords_entry;
+ GtkComboBox *languages_combo_box;
+ GtkComboBox *snippets_group_combo_box;
+ GtkImage *languages_notify;
+ GtkImage *group_notify;
+ GtkImage *trigger_notify;
+ GtkImage *name_notify;
+
+ /* If one of the following variables is TRUE, then we have an error and the Save Button
+ will be insensitive */
+ gboolean languages_error;
+ gboolean group_error;
+ gboolean trigger_error;
+
+ /* Variables widgets */
+ GtkTreeView *variables_view;
+ GtkButton *variable_add_button;
+ GtkButton *variable_remove_button;
+ GtkButton *variable_insert_button;
+
+ /* Variables tree model */
+ SnippetVarsStore *vars_store;
+ GtkTreeModel *vars_store_sorted;
+
+ /* Variables view cell renderers */
+ GtkCellRenderer *name_combo_cell;
+ GtkCellRenderer *type_combo_cell;
+ GtkCellRenderer *type_pixbuf_cell;
+ GtkCellRenderer *default_text_cell;
+ GtkCellRenderer *instant_text_cell;
+
+ /* Editor widgets */
+ GtkButton *save_button;
+ GtkButton *close_button;
+ GtkAlignment *editor_alignment;
+
+ /* So it will guard deleting the priv->snippet by calling snippets_editor_set_snippet
+ with snippet == NULL after deleting the backup_snippet in the saving handler. */
+ gboolean saving_snippet;
+
+};
+
+enum
+{
+ VARS_VIEW_COL_NAME = 0,
+ VARS_VIEW_COL_TYPE,
+ VARS_VIEW_COL_DEFAULT,
+ VARS_VIEW_COL_INSTANT
+};
+
+enum
+{
+ LANG_MODEL_COL_IN_SNIPPET = 0,
+ LANG_MODEL_COL_NAME,
+ LANG_MODEL_COL_N
+};
+
+/* Handlers */
+static void on_preview_button_toggled (GtkToggleButton *preview_button,
+ gpointer user_data);
+static void on_save_button_clicked (GtkButton *save_button,
+ gpointer user_data);
+static void on_close_button_clicked (GtkButton *close_button,
+ gpointer user_data);
+static void on_name_combo_cell_edited (GtkCellRendererText *cell,
+ gchar *path_string,
+ gchar *new_string,
+ gpointer user_data);
+static void on_type_combo_cell_changed (GtkCellRendererCombo *cell,
+ gchar *path_string,
+ gchar *new_string,
+ gpointer user_data);
+static void on_default_text_cell_edited (GtkCellRendererText *cell,
+ gchar *path_string,
+ gchar *new_string,
+ gpointer user_data);
+static void on_variables_view_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer user_data);
+static void on_variable_add_button_clicked (GtkButton *variable_add_button,
+ gpointer user_data);
+static void on_variable_remove_button_clicked (GtkButton *variable_remove_button,
+ gpointer user_data);
+static void on_variable_insert_button_clicked (GtkButton *variable_insert_button,
+ gpointer user_data);
+static void on_variables_view_selection_changed (GtkTreeSelection *selection,
+ gpointer user_data);
+static void on_snippets_group_combo_box_changed (GtkComboBox *combo_box,
+ gpointer user_data);
+static void on_languages_combo_box_changed (GtkComboBox *combo_box,
+ gpointer user_data);
+static void on_trigger_entry_text_changed (GObject *entry_obj,
+ GParamSpec *param_spec,
+ gpointer user_data);
+static void on_name_entry_text_changed (GObject *entry_obj,
+ GParamSpec *param_spec,
+ gpointer user_data);
+
+G_DEFINE_TYPE (SnippetsEditor, snippets_editor, GTK_TYPE_HBOX);
+
+
+static void
+snippets_editor_dispose (GObject *object)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (object));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (object);
+
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ g_object_unref (priv->snippet);
+
+ G_OBJECT_CLASS (snippets_editor_parent_class)->dispose (G_OBJECT (object));
+}
+
+static void
+snippets_editor_class_init (SnippetsEditorClass* klass)
+{
+ GObjectClass *g_object_class = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR_CLASS (klass));
+
+ g_object_class = G_OBJECT_CLASS (klass);
+ g_object_class->dispose = snippets_editor_dispose;
+
+ /* The SnippetsEditor saved the snippet. Basically, the SnippetsBrowser should
+ focus on the row where the snippet was saved. This is needed because when the
+ snippet is saved, the old one is deleted and the new one is inserted. The given
+ object is the newly inserted snippet. */
+ g_signal_new ("snippet-saved",
+ ANJUTA_TYPE_SNIPPETS_EDITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SnippetsEditorClass, snippet_saved),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_OBJECT,
+ NULL);
+
+ g_signal_new ("close-request",
+ ANJUTA_TYPE_SNIPPETS_EDITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SnippetsEditorClass, close_request),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ NULL);
+
+ g_type_class_add_private (klass, sizeof (SnippetsEditorPrivate));
+}
+
+static void
+snippets_editor_init (SnippetsEditor* snippets_editor)
+{
+ SnippetsEditorPrivate* priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ snippets_editor->priv = priv;
+
+ /* Initialize the private field */
+ priv->snippets_db = NULL;
+ priv->snippet = NULL;
+ priv->backup_snippet = NULL;
+ priv->group_store = NULL;
+ priv->lang_store = NULL;
+
+ priv->type_model = NULL;
+
+ priv->content_text_view = NULL;
+ priv->preview_button = NULL;
+
+ priv->name_entry = NULL;
+ priv->trigger_entry = NULL;
+ priv->languages_combo_box = NULL;
+ priv->snippets_group_combo_box = NULL;
+ priv->keywords_entry = NULL;
+ priv->languages_notify = NULL;
+ priv->group_notify = NULL;
+ priv->trigger_notify = NULL;
+
+ priv->variables_view = NULL;
+ priv->variable_add_button = NULL;
+ priv->variable_remove_button = NULL;
+ priv->variable_insert_button = NULL;
+
+ priv->vars_store = NULL;
+ priv->vars_store_sorted = NULL;
+
+ priv->name_combo_cell = NULL;
+ priv->type_combo_cell = NULL;
+ priv->type_pixbuf_cell = NULL;
+ priv->default_text_cell = NULL;
+ priv->instant_text_cell = NULL;
+
+ priv->save_button = NULL;
+ priv->close_button = NULL;
+ priv->editor_alignment = NULL;
+
+ priv->saving_snippet = FALSE;
+}
+
+static void
+load_snippets_editor_ui (SnippetsEditor *snippets_editor)
+{
+ GtkBuilder *bxml = NULL;
+ SnippetsEditorPrivate *priv = NULL;
+ GError *error = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Load the UI file */
+ bxml = gtk_builder_new ();
+ if (!gtk_builder_add_from_file (bxml, EDITOR_UI, &error))
+ {
+ g_warning ("Couldn't load editor ui file: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Edit content widgets */
+ priv->content_text_view = GTK_TEXT_VIEW (gtk_builder_get_object (bxml, "content_text_view"));
+ priv->preview_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (bxml, "preview_button"));
+ g_return_if_fail (GTK_IS_TEXT_VIEW (priv->content_text_view));
+ g_return_if_fail (GTK_IS_TOGGLE_BUTTON (priv->preview_button));
+
+ /* Edit properties widgets */
+ priv->name_entry = GTK_ENTRY (gtk_builder_get_object (bxml, "name_entry"));
+ priv->trigger_entry = GTK_ENTRY (gtk_builder_get_object (bxml, "trigger_entry"));
+ priv->languages_combo_box = GTK_COMBO_BOX (gtk_builder_get_object (bxml, "languages_combo_box"));
+ priv->snippets_group_combo_box = GTK_COMBO_BOX (gtk_builder_get_object (bxml, "snippets_group_combo_box"));
+ priv->languages_notify = GTK_IMAGE (gtk_builder_get_object (bxml, "languages_notify"));
+ priv->group_notify = GTK_IMAGE (gtk_builder_get_object (bxml, "group_notify"));
+ priv->trigger_notify = GTK_IMAGE (gtk_builder_get_object (bxml, "trigger_notify"));
+ priv->name_notify = GTK_IMAGE (gtk_builder_get_object (bxml, "name_notify"));
+ priv->keywords_entry = GTK_ENTRY (gtk_builder_get_object (bxml, "keywords_entry"));
+ g_return_if_fail (GTK_IS_ENTRY (priv->name_entry));
+ g_return_if_fail (GTK_IS_ENTRY (priv->trigger_entry));
+ g_return_if_fail (GTK_IS_COMBO_BOX (priv->languages_combo_box));
+ g_return_if_fail (GTK_IS_COMBO_BOX (priv->snippets_group_combo_box));
+ g_return_if_fail (GTK_IS_IMAGE (priv->languages_notify));
+ g_return_if_fail (GTK_IS_IMAGE (priv->group_notify));
+ g_return_if_fail (GTK_IS_ENTRY (priv->keywords_entry));
+
+ /* Edit variables widgets */
+ priv->variables_view = GTK_TREE_VIEW (gtk_builder_get_object (bxml, "variables_view"));
+ priv->variable_add_button = GTK_BUTTON (gtk_builder_get_object (bxml, "variable_add_button"));
+ priv->variable_remove_button = GTK_BUTTON (gtk_builder_get_object (bxml, "variable_remove_button"));
+ priv->variable_insert_button = GTK_BUTTON (gtk_builder_get_object (bxml, "variable_insert_button"));
+ g_return_if_fail (GTK_IS_TREE_VIEW (priv->variables_view));
+ g_return_if_fail (GTK_IS_BUTTON (priv->variable_add_button));
+ g_return_if_fail (GTK_IS_BUTTON (priv->variable_remove_button));
+ g_return_if_fail (GTK_IS_BUTTON (priv->variable_insert_button));
+
+ /* General widgets */
+ priv->save_button = GTK_BUTTON (gtk_builder_get_object (bxml, "save_button"));
+ priv->close_button = GTK_BUTTON (gtk_builder_get_object (bxml, "close_button"));
+ priv->editor_alignment = GTK_ALIGNMENT (gtk_builder_get_object (bxml, "editor_alignment"));
+ g_return_if_fail (GTK_IS_BUTTON (priv->save_button));
+ g_return_if_fail (GTK_IS_BUTTON (priv->close_button));
+ g_return_if_fail (GTK_IS_ALIGNMENT (priv->editor_alignment));
+
+ /* Add the gtk_alignment as the child of the snippets editor */
+ gtk_box_pack_start (GTK_BOX (snippets_editor),
+ GTK_WIDGET (priv->editor_alignment),
+ TRUE,
+ TRUE,
+ 0);
+
+ g_object_unref (bxml);
+}
+
+/* Variables View cell data functions and the sort function */
+
+static gint
+compare_var_in_snippet (gboolean in_snippet1, gboolean in_snippet2)
+{
+ /* Those that are in snippet go before those that aren't */
+ if ((in_snippet1 && in_snippet2) || (!in_snippet1 && !in_snippet2))
+ return 0;
+ if (in_snippet1)
+ return -1;
+ return 1;
+}
+
+static gint
+vars_store_sort_func (GtkTreeModel *vars_store,
+ GtkTreeIter *iter1,
+ GtkTreeIter *iter2,
+ gpointer user_data)
+{
+ gboolean in_snippet1 = FALSE, in_snippet2 = FALSE;
+ gchar *name1 = NULL, *name2 = NULL;
+ gint compare_value = 0;
+
+ /* Get the values from the model */
+ gtk_tree_model_get (vars_store, iter1,
+ VARS_STORE_COL_NAME, &name1,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet1,
+ -1);
+ gtk_tree_model_get (vars_store, iter2,
+ VARS_STORE_COL_NAME, &name2,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet2,
+ -1);
+
+ /* We first check if both variables are in the snippet */
+ compare_value = compare_var_in_snippet (in_snippet1, in_snippet2);
+
+ /* If we didn't got a compare_value until this point, we compare by name */
+ if (!compare_value)
+ compare_value = g_strcmp0 (name1, name2);
+
+ g_free (name1);
+ g_free (name2);
+
+ return compare_value;
+}
+
+static void
+set_cell_colors (GtkCellRenderer *cell,
+ SnippetVariableType type,
+ gboolean undefined)
+{
+ if (undefined && type == SNIPPET_VAR_TYPE_GLOBAL)
+ g_object_set (cell, "cell-background", UNDEFINED_BG_COLOR, NULL);
+ else
+ g_object_set (cell, "cell-background-set", FALSE, NULL);
+}
+
+static void
+focus_on_in_snippet_variable (GtkTreeView *vars_view,
+ GtkTreeModel *vars_model,
+ const gchar *var_name,
+ GtkTreeViewColumn *col,
+ gboolean start_editing)
+{
+ GtkTreeIter iter;
+ gchar *name = NULL;
+ gboolean in_snippet = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (GTK_IS_TREE_VIEW (vars_view));
+ g_return_if_fail (GTK_IS_TREE_MODEL (vars_model));
+
+ if (!gtk_tree_model_get_iter_first (vars_model, &iter))
+ return;
+
+ /* We search for a variable which is in the snippet with the given name. If we find
+ it, we focus the cursor on it. */
+ do
+ {
+ gtk_tree_model_get (vars_model, &iter,
+ VARS_STORE_COL_NAME, &name,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet,
+ -1);
+
+ if (!g_strcmp0 (var_name, name) && in_snippet)
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (vars_model, &iter);
+
+ gtk_tree_view_set_cursor (vars_view, path, col, start_editing);
+
+ gtk_tree_path_free (path);
+
+ g_free (name);
+ return;
+ }
+
+ g_free (name);
+
+ } while (gtk_tree_model_iter_next (vars_model, &iter));
+}
+
+static void
+change_snippet_variable_name_in_content (SnippetsEditor *snippets_editor,
+ const gchar *old_var_name,
+ const gchar *new_var_name)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ gchar *old_content = NULL;
+ GString *updated_content = NULL, *cur_var_name = NULL;
+ gint i = 0, j = 0, old_content_len = 0;
+ GtkTextBuffer *buffer = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ buffer = gtk_text_view_get_buffer (priv->content_text_view);
+
+ /* We should have a snippet loaded if we got in this function */
+ if (!ANJUTA_IS_SNIPPET (priv->snippet))
+ g_return_if_reached ();
+
+ /* Get the content depending on what is shown in the content editor right now */
+ if (gtk_toggle_button_get_active (priv->preview_button))
+ {
+ old_content = g_strdup (snippet_get_content (priv->snippet));
+ }
+ else
+ {
+ GtkTextIter start_iter, end_iter;
+
+ gtk_text_buffer_get_start_iter (buffer, &start_iter);
+ gtk_text_buffer_get_end_iter (buffer, &end_iter);
+ old_content = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
+ }
+
+ old_content_len = strlen (old_content);
+ updated_content = g_string_new ("");
+
+ for (i = 0; i < old_content_len; i ++)
+ {
+ if (IS_SNIPPET_VAR_START (old_content, i))
+ {
+ /* We add the snippet var start -- "${" and continue with the variable name */
+ //j = i;
+ i += strlen (SNIPPET_VAR_START) - 1;
+ j = i + 1;
+ g_string_append (updated_content, SNIPPET_VAR_START);
+ cur_var_name = g_string_new ("");
+
+ /* We add all the chars until we got to the mark of the variable end or
+ to the end of the text */
+ while (!IS_SNIPPET_VAR_END (old_content, j) && j < old_content_len)
+ g_string_append_c (cur_var_name, old_content[j ++]);
+
+ /* If we found a valid variable and it's the variable we want to replace */
+ if (IS_SNIPPET_VAR_END (old_content, j) &&
+ !g_strcmp0 (cur_var_name->str, old_var_name))
+ {
+ g_string_append (updated_content, new_var_name);
+ g_string_append (updated_content, SNIPPET_VAR_END);
+ i = j;
+ }
+
+ g_string_free (cur_var_name, TRUE);
+
+ }
+ else
+ {
+ g_string_append_c (updated_content, old_content[i]);
+ }
+ }
+
+ /* We update the content */
+ snippet_set_content (priv->snippet, updated_content->str);
+
+ /* And update the content text view if neccesary */
+ if (!gtk_toggle_button_get_active (priv->preview_button))
+ {
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (priv->content_text_view);
+ gtk_text_buffer_set_text (buffer, updated_content->str, -1);
+ }
+
+ g_string_free (updated_content, TRUE);
+ g_free (old_content);
+}
+
+static void
+variables_view_name_combo_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gboolean in_snippet = FALSE, undefined = FALSE;
+ gchar *name = NULL, *name_with_markup = NULL;
+ SnippetVariableType type;
+
+ gtk_tree_model_get (tree_model, iter,
+ VARS_STORE_COL_NAME, &name,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ VARS_STORE_COL_TYPE, &type,
+ -1);
+
+ if (in_snippet)
+ name_with_markup = g_strconcat ("<b>", name, "</b>", NULL);
+ else
+ name_with_markup = g_strdup (name);
+
+ g_object_set (cell, "editable", in_snippet, NULL);
+ g_object_set (cell, "markup", name_with_markup, NULL);
+
+ set_cell_colors (cell, type, undefined);
+
+ g_free (name);
+ g_free (name_with_markup);
+}
+
+static void
+variables_view_type_combo_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ SnippetVariableType type;
+ gboolean in_snippet = FALSE;
+ gboolean undefined = FALSE;
+
+ gtk_tree_model_get (tree_model, iter,
+ VARS_STORE_COL_TYPE, &type,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ -1);
+
+ if (type == SNIPPET_VAR_TYPE_LOCAL)
+ g_object_set (cell, "text", LOCAL_TYPE_STR, NULL);
+ else
+ if (type == SNIPPET_VAR_TYPE_GLOBAL)
+ g_object_set (cell, "text", GLOBAL_TYPE_STR, NULL);
+ else
+ g_return_if_reached ();
+
+ set_cell_colors (cell, type, undefined);
+
+ g_object_set (cell, "sensitive", in_snippet, NULL);
+ g_object_set (cell, "editable", in_snippet, NULL);
+}
+
+static void
+variables_view_type_pixbuf_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ SnippetVariableType type;
+ gboolean undefined = FALSE;
+
+ gtk_tree_model_get (tree_model, iter,
+ VARS_STORE_COL_TYPE, &type,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ -1);
+
+ if (type == SNIPPET_VAR_TYPE_GLOBAL && undefined)
+ g_object_set (cell, "visible", TRUE, NULL);
+ else
+ g_object_set (cell, "visible", FALSE, NULL);
+
+ set_cell_colors (cell, type, undefined);
+}
+
+static void
+variables_view_default_text_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *default_value = NULL;
+ gboolean in_snippet = FALSE, undefined = FALSE;
+ SnippetVariableType type;
+
+ gtk_tree_model_get (tree_model, iter,
+ VARS_STORE_COL_DEFAULT_VALUE, &default_value,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ VARS_STORE_COL_TYPE, &type,
+ -1);
+
+ g_object_set (cell, "text", default_value, NULL);
+ g_object_set (cell, "editable", in_snippet, NULL);
+
+ set_cell_colors (cell, type, undefined);
+
+ g_free (default_value);
+}
+
+static void
+variables_view_instant_text_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gboolean undefined = FALSE;
+ SnippetVariableType type;
+
+ gtk_tree_model_get (tree_model, iter,
+ VARS_STORE_COL_UNDEFINED, &undefined,
+ VARS_STORE_COL_TYPE, &type,
+ -1);
+
+ set_cell_colors (cell, type, undefined);
+
+}
+
+static void
+init_variables_view (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeViewColumn *col = NULL;
+ GtkTreeIter iter;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Initialize the type model */
+ priv->type_model = gtk_list_store_new (1, G_TYPE_STRING);
+ gtk_list_store_append (priv->type_model, &iter);
+ gtk_list_store_set (priv->type_model, &iter,
+ 0, LOCAL_TYPE_STR,
+ -1);
+ gtk_list_store_append (priv->type_model, &iter);
+ gtk_list_store_set (priv->type_model, &iter,
+ 0, GLOBAL_TYPE_STR,
+ -1);
+
+
+ /* Initialize the sorted model */
+ priv->vars_store_sorted = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (priv->vars_store));
+ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (priv->vars_store_sorted),
+ vars_store_sort_func,
+ NULL, NULL);
+ gtk_tree_view_set_model (priv->variables_view, GTK_TREE_MODEL (priv->vars_store_sorted));
+
+ /* Column 1 - Name */
+ col = gtk_tree_view_column_new ();
+ priv->name_combo_cell = gtk_cell_renderer_combo_new ();
+ gtk_tree_view_column_set_title (col, NAME_COL_TITLE);
+ gtk_tree_view_column_pack_start (col, priv->name_combo_cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (col, priv->name_combo_cell,
+ variables_view_name_combo_data_func,
+ snippets_editor, NULL);
+ g_object_set (col, "resizable", TRUE, NULL);
+ g_object_set (col, "min-width", MIN_NAME_COL_WIDTH, NULL);
+ g_object_set (priv->name_combo_cell, "has-entry", TRUE, NULL);
+ g_object_set (priv->name_combo_cell, "model",
+ snippets_db_get_global_vars_model (priv->snippets_db), NULL);
+ g_object_set (priv->name_combo_cell, "text-column",
+ GLOBAL_VARS_MODEL_COL_NAME, NULL);
+ gtk_tree_view_insert_column (priv->variables_view, col, -1);
+
+ /* Column 2 - Type */
+ col = gtk_tree_view_column_new ();
+ priv->type_combo_cell = gtk_cell_renderer_combo_new ();
+ priv->type_pixbuf_cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_set_title (col, TYPE_COL_TITLE);
+ gtk_tree_view_column_pack_start (col, priv->type_combo_cell, FALSE);
+ gtk_tree_view_column_pack_end (col, priv->type_pixbuf_cell, FALSE);
+ g_object_set (priv->type_combo_cell, "model", priv->type_model, NULL);
+ g_object_set (priv->type_combo_cell, "text-column", 0, NULL);
+ g_object_set (priv->type_combo_cell, "has-entry", FALSE, NULL);
+ gtk_tree_view_column_set_cell_data_func (col, priv->type_combo_cell,
+ variables_view_type_combo_data_func,
+ snippets_editor, NULL);
+ g_object_set (priv->type_pixbuf_cell, "stock-id", GTK_STOCK_DIALOG_WARNING, NULL);
+ gtk_tree_view_column_set_cell_data_func (col, priv->type_pixbuf_cell,
+ variables_view_type_pixbuf_data_func,
+ snippets_editor, NULL);
+ gtk_tree_view_insert_column (priv->variables_view, col, -1);
+
+ /* Column 3 - Default Value (just for those variables that are in the snippet) */
+ col = gtk_tree_view_column_new ();
+ priv->default_text_cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_set_title (col, DEFAULT_COL_TITLE);
+ gtk_tree_view_column_pack_start (col, priv->default_text_cell, FALSE);
+ gtk_tree_view_column_set_cell_data_func (col, priv->default_text_cell,
+ variables_view_default_text_data_func,
+ snippets_editor, NULL);
+ g_object_set (col, "resizable", TRUE, NULL);
+ gtk_tree_view_insert_column (priv->variables_view, col, -1);
+
+ /* Column 4 - Instant value */
+ priv->instant_text_cell = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (INSTANT_COL_TITLE,
+ priv->instant_text_cell,
+ "text", VARS_STORE_COL_INSTANT_VALUE,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (col, priv->instant_text_cell,
+ variables_view_instant_text_data_func,
+ snippets_editor, NULL);
+ g_object_set (col, "resizable", TRUE, NULL);
+ g_object_set (G_OBJECT (priv->instant_text_cell), "editable", FALSE, NULL);
+ gtk_tree_view_insert_column (priv->variables_view, col, -1);
+
+ /* Initialize the buttons as insensitive */
+ g_object_set (priv->variable_add_button, "sensitive", FALSE, NULL);
+ g_object_set (priv->variable_remove_button, "sensitive", FALSE, NULL);
+ g_object_set (priv->variable_insert_button, "sensitive", FALSE, NULL);
+
+}
+
+static void
+focus_snippets_group_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ AnjutaSnippetsGroup *parent_snippets_group = NULL;
+ const gchar *parent_snippets_group_name = NULL;
+ gchar *cur_group_name = NULL;
+ GtkTreeIter iter;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* We initialize it */
+ g_object_set (priv->snippets_group_combo_box, "active", -1, NULL);
+
+ /* If we have a snippet and it has a snippets group. */
+ if (ANJUTA_IS_SNIPPET (priv->snippet) &&
+ ANJUTA_IS_SNIPPETS_GROUP (priv->snippet->parent_snippets_group))
+ {
+ parent_snippets_group = ANJUTA_SNIPPETS_GROUP (priv->snippet->parent_snippets_group);
+ parent_snippets_group_name = snippets_group_get_name (parent_snippets_group);
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->group_store), &iter))
+ return;
+
+ do
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->group_store), &iter,
+ GROUPS_COL_NAME, &cur_group_name,
+ -1);
+
+ if (!g_strcmp0 (cur_group_name, parent_snippets_group_name))
+ {
+ gtk_combo_box_set_active_iter (priv->snippets_group_combo_box, &iter);
+
+ g_free (cur_group_name);
+ return;
+ }
+
+ g_free (cur_group_name);
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->group_store), &iter));
+ }
+
+}
+
+static void
+init_snippets_group_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkCellRenderer *cell = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Init the tree model */
+ priv->group_store = gtk_list_store_new (1, G_TYPE_STRING);
+
+ /* Set the tree model to the combo-box */
+ gtk_combo_box_set_model (priv->snippets_group_combo_box,
+ GTK_TREE_MODEL (priv->group_store));
+ g_object_unref (priv->group_store);
+
+ /* Add the cell renderer */
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->snippets_group_combo_box),
+ cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->snippets_group_combo_box),
+ cell, "text", GROUPS_COL_NAME, NULL);
+
+}
+
+static void
+reload_snippets_group_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ gchar *cur_group_name = NULL, *parent_group_name = NULL;
+ gint i = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Unref the old model if there is one */
+ gtk_list_store_clear (priv->group_store);
+
+ /* If we have a snippet loaded, we should re-check it in the combo-box */
+ if (ANJUTA_IS_SNIPPET (priv->snippet) &&
+ ANJUTA_IS_SNIPPETS_GROUP (priv->snippet->parent_snippets_group))
+ {
+ AnjutaSnippetsGroup *group = ANJUTA_SNIPPETS_GROUP (priv->snippet->parent_snippets_group);
+
+ parent_group_name = g_strdup (snippets_group_get_name (group));
+ }
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->snippets_db), &iter))
+ return;
+
+ /* Add the groups in the database to the list store */
+ do
+ {
+ /* Get the current group name from the database ... */
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->snippets_db), &iter,
+ SNIPPETS_DB_MODEL_COL_NAME, &cur_group_name,
+ -1);
+
+ gtk_combo_box_append_text (priv->snippets_group_combo_box, cur_group_name);
+
+ /* If we have a snippet loaded, we search for the row. */
+ if (parent_group_name != NULL)
+ {
+ if (!g_strcmp0 (parent_group_name, cur_group_name))
+ g_object_set (priv->snippets_group_combo_box, "active", i, NULL);
+
+ i ++;
+ }
+
+ g_free (cur_group_name);
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->snippets_db), &iter));
+
+}
+
+static void
+init_languages_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkCellRenderer *cell = NULL;
+ GtkTreeIter tree_iter;
+ GList *lang_ids = NULL, *iter = NULL;
+ gint cur_lang_id = 0;
+ const gchar *cur_lang = NULL;
+ IAnjutaLanguage *language = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Initialize the model which will be used for the combo-box */
+ priv->lang_store = gtk_list_store_new (LANG_MODEL_COL_N, G_TYPE_BOOLEAN, G_TYPE_STRING);
+
+ /* Add the items */
+ language = anjuta_shell_get_interface (priv->snippets_db->anjuta_shell,
+ IAnjutaLanguage, NULL);
+ lang_ids = ianjuta_language_get_languages (language, NULL);
+ for (iter = g_list_first (lang_ids); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_lang_id = GPOINTER_TO_INT (iter->data);
+ cur_lang = ianjuta_language_get_name (language, cur_lang_id, NULL);
+ gtk_list_store_append (priv->lang_store, &tree_iter);
+ gtk_list_store_set (priv->lang_store, &tree_iter,
+ LANG_MODEL_COL_IN_SNIPPET, FALSE,
+ LANG_MODEL_COL_NAME, cur_lang,
+ -1);
+ }
+ g_list_free (lang_ids);
+
+ /* Set it as the model of the combo-box */
+ gtk_combo_box_set_model (priv->languages_combo_box,
+ GTK_TREE_MODEL (priv->lang_store));
+ g_object_unref (priv->lang_store);
+
+ /* Add the cell renderers */
+ cell = gtk_cell_renderer_toggle_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->languages_combo_box),
+ cell, FALSE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->languages_combo_box),
+ cell, "active", LANG_MODEL_COL_IN_SNIPPET);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->languages_combo_box),
+ cell, FALSE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->languages_combo_box),
+ cell, "text", LANG_MODEL_COL_NAME);
+}
+
+static void
+load_languages_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ gchar *cur_lang = NULL;
+ gboolean has_language = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Add the new selection or clear it */
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->lang_store), &iter))
+ g_return_if_reached ();
+
+ do
+ {
+ /* Clear it */
+ gtk_list_store_set (priv->lang_store, &iter,
+ LANG_MODEL_COL_IN_SNIPPET, FALSE,
+ -1);
+
+ /* If we have a snippet loaded, we also populate the checklist */
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->lang_store), &iter,
+ LANG_MODEL_COL_NAME, &cur_lang,
+ -1);
+
+ has_language = snippet_has_language (priv->snippet, cur_lang);
+ gtk_list_store_set (priv->lang_store, &iter,
+ LANG_MODEL_COL_IN_SNIPPET, has_language,
+ -1);
+
+ g_free (cur_lang);
+ }
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->lang_store), &iter));
+
+ /* The combo box should be sensitive only if there is a snippet loaded */
+ g_object_set (priv->languages_combo_box, "sensitive",
+ ANJUTA_IS_SNIPPET (priv->snippet), NULL);
+}
+
+static void
+load_keywords_entry (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GList *keywords = NULL, *iter = NULL;
+ const gchar *cur_keyword = NULL;
+ GString *keywords_string = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Clear the text entry */
+ gtk_entry_set_text (priv->keywords_entry, "");
+
+ /* If there is a snippet loaded, add the keywords */
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ {
+ keywords = snippet_get_keywords_list (priv->snippet);
+ keywords_string = g_string_new ("");
+
+ for (iter = g_list_first (keywords); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_keyword = (const gchar *)iter->data;
+ g_string_append (keywords_string, cur_keyword);
+ g_string_append (keywords_string, " ");
+ }
+
+ gtk_entry_set_text (priv->keywords_entry, keywords_string->str);
+
+ g_string_free (keywords_string, TRUE);
+ g_list_free (keywords);
+ }
+}
+
+static void
+save_keywords_entry (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GList *keywords = NULL;
+ gchar **keywords_array = NULL;
+ gint i = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ keywords_array = g_strsplit (gtk_entry_get_text (priv->keywords_entry), " ", -1);
+
+ /* Add each item of the array to the list */
+ while (keywords_array[i])
+ {
+ /* In case the user entered more than one space between keywords */
+ if (g_strcmp0 (keywords_array[i], ""))
+ keywords = g_list_append (keywords, keywords_array[i]);
+
+ i ++;
+ }
+
+ snippet_set_keywords_list (priv->snippet, keywords);
+ g_strfreev (keywords_array);
+ g_list_free (keywords);
+
+}
+
+static gboolean
+check_trigger_entry (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ gboolean valid = TRUE;
+ guint16 i = 0, text_length = 0;
+ const gchar *text = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor), FALSE);
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Check the text is valid only if there is a snippet loaded */
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ {
+ text = gtk_entry_get_text (priv->trigger_entry);
+ text_length = gtk_entry_get_text_length (priv->trigger_entry);
+
+ for (i = 0; i < text_length; i ++)
+ if (!g_ascii_isalnum (text[i]) && text[i] != '_')
+ {
+ /* Set as invalid and set the according error message */
+ g_object_set (priv->trigger_notify, "tooltip-markup", ERROR_TRIGGER_NOT_VALID, NULL);
+ valid = FALSE;
+ break;
+ }
+
+ /* If there isn't a trigger-key entered, we also show an error message, but a
+ different one */
+ if (text_length == 0)
+ {
+ g_object_set (priv->trigger_notify, "tooltip-markup", ERROR_TRIGGER_NULL, NULL);
+ valid = FALSE;
+ }
+
+ }
+
+ /* Show or hide */
+ g_object_set (priv->trigger_notify, "visible", !valid, NULL);
+
+ return valid;
+}
+
+static gboolean
+check_languages_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ gchar *lang_name = NULL;
+ const gchar *trigger = NULL;
+ gboolean no_lang_selected = TRUE;
+ AnjutaSnippet *conflicting_snippet = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor), FALSE);
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ trigger = gtk_entry_get_text (priv->trigger_entry);
+
+ /* We should always have this tree model filled */
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->lang_store), &iter))
+ g_return_val_if_reached (FALSE);
+
+ /* We initialize the error image by hiding it */
+ g_object_set (priv->languages_notify, "visible", FALSE, NULL);
+
+ if (!ANJUTA_IS_SNIPPET (priv->snippet))
+ return TRUE;
+
+ /* We check each language to see if it doesen't cause a conflict */
+ do
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->lang_store), &iter,
+ LANG_MODEL_COL_NAME, &lang_name,
+ -1);
+
+ if (snippet_has_language (priv->snippet, lang_name))
+ {
+ conflicting_snippet = snippets_db_get_snippet (priv->snippets_db, trigger, lang_name);
+
+ no_lang_selected = FALSE;
+
+ /* If there is a conflicting snippet and it isn't the one we had backed-up,
+ we make visible the error icon. */
+ if (ANJUTA_IS_SNIPPET (conflicting_snippet) &&
+ priv->backup_snippet != conflicting_snippet)
+ {
+ g_object_set (priv->languages_notify, "tooltip-markup", ERROR_LANG_CONFLICT, NULL);
+ g_object_set (priv->languages_notify, "visible", TRUE, NULL);
+
+ g_free (lang_name);
+ return FALSE;
+ }
+
+ }
+
+ g_free (lang_name);
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->lang_store), &iter));
+
+ /* If the user doesn't have any language selected, we show a warning. */
+ if (no_lang_selected)
+ {
+ g_object_set (priv->languages_notify, "tooltip-markup", ERROR_LANG_NULL, NULL);
+ g_object_set (priv->languages_notify, "visible", TRUE, NULL);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_group_combo_box (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ gboolean has_selection = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor), FALSE);
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ has_selection = (gtk_combo_box_get_active (priv->snippets_group_combo_box) >= 0);
+ g_object_set (priv->group_notify, "visible",
+ !has_selection && ANJUTA_IS_SNIPPET (priv->snippet),
+ NULL);
+
+ return has_selection;
+}
+
+static void
+check_all_inputs (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ gboolean no_errors = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* We check we don't have any errors */
+ no_errors = (!priv->languages_error && !priv->group_error && !priv->trigger_error);
+
+ g_object_set (priv->save_button, "sensitive", no_errors, NULL);
+}
+
+static void
+check_name_entry (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ guint16 text_length = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Initialize the warning icon */
+ g_object_set (priv->name_notify, "visible", FALSE, NULL);
+
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ {
+ text_length = gtk_entry_get_text_length (priv->name_entry);
+ g_object_set (priv->name_notify, "visible", text_length == 0, NULL);
+
+ }
+}
+
+static void
+init_input_errors (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ g_object_set (priv->group_notify, "visible", FALSE, NULL);
+ g_object_set (priv->languages_notify, "visible", FALSE, NULL);
+ g_object_set (priv->trigger_notify, "visible", FALSE, NULL);
+
+ priv->group_error = !check_languages_combo_box (snippets_editor);
+ priv->languages_error = !check_group_combo_box (snippets_editor);
+ priv->trigger_error = !check_trigger_entry (snippets_editor);
+ check_name_entry (snippets_editor);
+
+ check_all_inputs (snippets_editor);
+}
+
+static void
+init_sensitivity (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ gboolean has_snippet = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ has_snippet = ANJUTA_IS_SNIPPET (priv->snippet);
+ g_object_set (priv->save_button, "sensitive", has_snippet, NULL);
+ g_object_set (priv->variable_add_button, "sensitive", has_snippet, NULL);
+ g_object_set (priv->languages_combo_box, "sensitive", has_snippet, NULL);
+ g_object_set (priv->snippets_group_combo_box, "sensitive", has_snippet, NULL);
+ g_object_set (priv->name_entry, "sensitive", has_snippet, NULL);
+ g_object_set (priv->trigger_entry, "sensitive", has_snippet, NULL);
+ g_object_set (priv->keywords_entry, "sensitive", has_snippet, NULL);
+ g_object_set (priv->content_text_view, "sensitive", has_snippet, NULL);
+
+}
+
+static void
+init_editor_handlers (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ g_signal_connect (GTK_OBJECT (priv->preview_button),
+ "toggled",
+ GTK_SIGNAL_FUNC (on_preview_button_toggled),
+ snippets_editor);
+ g_signal_connect (GTK_OBJECT (priv->save_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_save_button_clicked),
+ snippets_editor);
+ g_signal_connect (GTK_OBJECT (priv->close_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_close_button_clicked),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->name_combo_cell),
+ "edited",
+ G_CALLBACK (on_name_combo_cell_edited),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->type_combo_cell),
+ "changed",
+ G_CALLBACK (on_type_combo_cell_changed),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->default_text_cell),
+ "edited",
+ G_CALLBACK (on_default_text_cell_edited),
+ snippets_editor);
+ g_signal_connect (GTK_OBJECT (priv->variables_view),
+ "row-activated",
+ GTK_SIGNAL_FUNC (on_variables_view_row_activated),
+ snippets_editor);
+ g_signal_connect (GTK_OBJECT (priv->variable_add_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_variable_add_button_clicked),
+ snippets_editor);
+ g_signal_connect (GTK_OBJECT (priv->variable_remove_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_variable_remove_button_clicked),
+ snippets_editor);
+ g_signal_connect (GTK_OBJECT (priv->variable_insert_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (on_variable_insert_button_clicked),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (priv->variables_view)),
+ "changed",
+ G_CALLBACK (on_variables_view_selection_changed),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->snippets_group_combo_box),
+ "changed",
+ G_CALLBACK (on_snippets_group_combo_box_changed),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->languages_combo_box),
+ "changed",
+ G_CALLBACK (on_languages_combo_box_changed),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->trigger_entry),
+ "notify::text",
+ G_CALLBACK (on_trigger_entry_text_changed),
+ snippets_editor);
+ g_signal_connect (G_OBJECT (priv->name_entry),
+ "notify::text",
+ G_CALLBACK (on_name_entry_text_changed),
+ snippets_editor);
+}
+
+SnippetsEditor *
+snippets_editor_new (SnippetsDB *snippets_db)
+{
+ SnippetsEditor *snippets_editor =
+ ANJUTA_SNIPPETS_EDITOR (g_object_new (snippets_editor_get_type (), NULL));
+ SnippetsEditorPrivate *priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), snippets_editor);
+
+ priv->snippets_db = snippets_db;
+
+ /* Load the variables tree model */
+ priv->vars_store = snippet_vars_store_new ();
+
+ /* Load the UI for snippets-editor.ui */
+ load_snippets_editor_ui (snippets_editor);
+
+ /* Initialize the variables tree view */
+ init_variables_view (snippets_editor);
+
+ /* Initialize the snippets-group combo box */
+ init_snippets_group_combo_box (snippets_editor);
+ reload_snippets_group_combo_box (snippets_editor);
+
+ /* Initialize the languages combo box */
+ init_languages_combo_box (snippets_editor);
+
+ /* Connect the handlers */
+ init_editor_handlers (snippets_editor);
+
+ /* Initialize the buttons as insensitive */
+ g_object_set (priv->save_button, "sensitive", FALSE, NULL);
+ g_object_set (priv->languages_combo_box, "sensitive", FALSE, NULL);
+ g_object_set (priv->snippets_group_combo_box, "sensitive", FALSE, NULL);
+
+ return snippets_editor;
+}
+
+static void
+load_content_to_editor (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ gchar *text = NULL;
+ GtkTextBuffer *content_buffer = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* If we don't have a snippet loaded we don't do anything */
+ if (!ANJUTA_IS_SNIPPET (priv->snippet))
+ {
+ text = g_strdup ("");
+ }
+ else
+ if (gtk_toggle_button_get_active (priv->preview_button))
+ {
+ text = snippet_get_default_content (priv->snippet,
+ G_OBJECT (priv->snippets_db),
+ "");
+ }
+ else
+ {
+ text = g_strdup (snippet_get_content (priv->snippet));
+ }
+
+ content_buffer = gtk_text_view_get_buffer (priv->content_text_view);
+ gtk_text_buffer_set_text (content_buffer, text, -1);
+ g_free (text);
+}
+
+/* Warning: this will take the text as it is. It won't check if it's a preview. */
+static void
+save_content_from_editor (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTextIter start_iter, end_iter;
+ gchar *text = NULL;
+ GtkTextBuffer *content_buffer = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* If we don't have a snippet loaded, don't do anything */
+ if (!ANJUTA_IS_SNIPPET (priv->snippet))
+ return;
+
+ /* Get the text in the GtkTextBuffer */
+ content_buffer = gtk_text_view_get_buffer (priv->content_text_view);
+ gtk_text_buffer_get_start_iter (content_buffer, &start_iter);
+ gtk_text_buffer_get_end_iter (content_buffer, &end_iter);
+ text = gtk_text_buffer_get_text (content_buffer, &start_iter, &end_iter, FALSE);
+
+ /* Save it to the snippet */
+ snippet_set_content (priv->snippet, text);
+
+ g_free (text);
+}
+
+void
+snippets_editor_set_snippet (SnippetsEditor *snippets_editor,
+ AnjutaSnippet *snippet)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* If we are saving the snippet, we need to guard entering this method because
+ it will be called by the selection changed handler in the browser */
+ if (priv->saving_snippet)
+ return;
+
+ /* Delete the old snippet */
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ g_object_unref (priv->snippet);
+
+ /* Set the current snippet */
+ priv->backup_snippet = snippet;
+ if (ANJUTA_IS_SNIPPET (snippet))
+ priv->snippet = snippet_copy (snippet);
+ else
+ priv->snippet = NULL;
+
+ /* Set the sensitive property of the widgets */
+ init_sensitivity (snippets_editor);
+
+ /* Initialize the snippet content editor */
+ load_content_to_editor (snippets_editor);
+
+ /* Initialize the name property */
+ if (ANJUTA_IS_SNIPPET (snippet))
+ gtk_entry_set_text (priv->name_entry, snippet_get_name (snippet));
+ else
+ gtk_entry_set_text (priv->name_entry, "");
+
+ /* Initialize the trigger-key property */
+ if (ANJUTA_IS_SNIPPET (snippet))
+ gtk_entry_set_text (priv->trigger_entry, snippet_get_trigger_key (snippet));
+ else
+ gtk_entry_set_text (priv->trigger_entry, "");
+
+ /* Initialize the snippets group combo-box property */
+ reload_snippets_group_combo_box (snippets_editor);
+ focus_snippets_group_combo_box (snippets_editor);
+
+ /* Initialize the language combo-box property */
+ load_languages_combo_box (snippets_editor);
+
+ /* Initialize the keywords text-view property */
+ load_keywords_entry (snippets_editor);
+
+ /* Initialize the variables tree-view - load the variables tree model with the variables
+ from the current snippet*/
+ snippet_vars_store_unload (priv->vars_store);
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ snippet_vars_store_load (priv->vars_store, priv->snippets_db, priv->snippet);
+
+ /* We initialize the errors/warnings */
+ init_input_errors (snippets_editor);
+
+}
+
+void
+snippets_editor_set_snippet_new (SnippetsEditor *snippets_editor)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (snippets_editor));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* Delete the old snippet */
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ g_object_unref (priv->snippet);
+ priv->backup_snippet = NULL;
+
+ /* Initialize a new empty snippet */
+ priv->snippet = snippet_new ("", NULL, "", "", NULL, NULL, NULL, NULL);
+
+ init_sensitivity (snippets_editor);
+
+ /* Initialize the entries and content */
+ gtk_entry_set_text (priv->name_entry, "");
+ gtk_entry_set_text (priv->trigger_entry, "");
+ gtk_entry_set_text (priv->keywords_entry, "");
+ load_content_to_editor (snippets_editor);
+
+
+ reload_snippets_group_combo_box (snippets_editor);
+ focus_snippets_group_combo_box (snippets_editor);
+
+ load_languages_combo_box (snippets_editor);
+
+ snippet_vars_store_unload (priv->vars_store);
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ snippet_vars_store_load (priv->vars_store, priv->snippets_db, priv->snippet);
+
+ init_input_errors (snippets_editor);
+
+}
+
+static void
+on_preview_button_toggled (GtkToggleButton *preview_button,
+ gpointer user_data)
+{
+ SnippetsEditor *snippets_editor = NULL;
+ SnippetsEditorPrivate *priv = NULL;
+ gboolean preview_mode = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ snippets_editor = ANJUTA_SNIPPETS_EDITOR (user_data);
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+
+ /* If we go in the preview mode, we should save the content and disallow removing
+ and inserting variables */
+ preview_mode = gtk_toggle_button_get_active (preview_button);
+ if (preview_mode)
+ save_content_from_editor (snippets_editor);
+
+ g_object_set (priv->variable_insert_button, "sensitive", !preview_mode, NULL);
+ g_object_set (priv->content_text_view, "editable", !preview_mode, NULL);
+
+ load_content_to_editor (snippets_editor);
+}
+
+static void
+on_save_button_clicked (GtkButton *save_button,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ SnippetsEditor *snippets_editor = NULL;
+ AnjutaSnippetsGroup *parent_snippets_group = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ snippets_editor = ANJUTA_SNIPPETS_EDITOR (user_data);
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (snippets_editor);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+
+ /* If there isn't a snippet editing */
+ if (!ANJUTA_IS_SNIPPET (priv->snippet))
+ return;
+
+ /* The user should have a snippets group selected */
+ if (!ANJUTA_IS_SNIPPETS_GROUP (priv->snippet->parent_snippets_group))
+ return;
+
+ /* Copy over the name, trigger and keywords */
+ snippet_set_name (priv->snippet, gtk_entry_get_text (priv->name_entry));
+ snippet_set_trigger_key (priv->snippet, gtk_entry_get_text (priv->trigger_entry));
+ save_keywords_entry (snippets_editor);
+
+ /* Save the content */
+ if (!gtk_toggle_button_get_active (priv->preview_button))
+ save_content_from_editor (snippets_editor);
+
+ /* Delete the back-up snippet if there is one (we don't have one if it's a new snippet)*/
+ priv->saving_snippet = TRUE;
+ if (ANJUTA_IS_SNIPPET (priv->backup_snippet))
+ snippets_db_remove_snippet (priv->snippets_db,
+ snippet_get_trigger_key (priv->backup_snippet),
+ snippet_get_any_language (priv->backup_snippet),
+ TRUE);
+ /* Add the new snippet */
+ parent_snippets_group = ANJUTA_SNIPPETS_GROUP (priv->snippet->parent_snippets_group);
+ snippets_db_add_snippet (priv->snippets_db,
+ priv->snippet,
+ snippets_group_get_name (parent_snippets_group));
+
+ /* Move the new snippet as the back-up one */
+ priv->backup_snippet = priv->snippet;
+ priv->snippet = snippet_copy (priv->backup_snippet);
+
+ /* Emit the signal that the snippet was saved */
+ g_signal_emit_by_name (snippets_editor, "snippet-saved", priv->backup_snippet);
+
+ priv->saving_snippet = FALSE;
+}
+
+static void
+on_close_button_clicked (GtkButton *close_button,
+ gpointer user_data)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+
+ g_signal_emit_by_name (ANJUTA_SNIPPETS_EDITOR (user_data), "close-request");
+}
+
+static void
+on_name_combo_cell_edited (GtkCellRendererText *cell,
+ gchar *path_string,
+ gchar *new_string,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreePath *path = NULL;
+ gchar *old_name = NULL;
+ GtkTreeIter iter;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* We don't accept empty strings as variables names */
+ if (!g_strcmp0 (new_string, ""))
+ return;
+
+ /* Get the old name at the given path */
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->vars_store_sorted), &iter, path);
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->vars_store_sorted), &iter,
+ VARS_STORE_COL_NAME, &old_name,
+ -1);
+
+ /* If the name wasn't changed we don't do anything */
+ if (!g_strcmp0 (old_name, new_string))
+ {
+ g_free (old_name);
+ return;
+ }
+
+ /* Set the new name */
+ snippet_vars_store_set_variable_name (priv->vars_store, old_name, new_string);
+
+ /* If there is a global variable with the given name, treat it as global */
+ if (snippets_db_has_global_variable (priv->snippets_db, new_string))
+ snippet_vars_store_set_variable_type (priv->vars_store,
+ new_string,
+ SNIPPET_VAR_TYPE_GLOBAL);
+
+ focus_on_in_snippet_variable (priv->variables_view,
+ GTK_TREE_MODEL (priv->vars_store_sorted),
+ new_string,
+ NULL, FALSE);
+
+ change_snippet_variable_name_in_content (ANJUTA_SNIPPETS_EDITOR (user_data),
+ old_name, new_string);
+
+ g_free (old_name);
+}
+
+
+static void
+on_type_combo_cell_changed (GtkCellRendererCombo *cell,
+ gchar *path_string,
+ gchar *new_string,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreePath *path = NULL;
+ gchar *name = NULL;
+ GtkTreeIter iter;
+ SnippetVariableType type;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* Get the name at the given path */
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->vars_store_sorted), &iter, path);
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->vars_store_sorted), &iter,
+ VARS_STORE_COL_NAME, &name,
+ VARS_STORE_COL_TYPE, &type,
+ -1);
+
+ if (type == SNIPPET_VAR_TYPE_LOCAL)
+ snippet_vars_store_set_variable_type (priv->vars_store, name, SNIPPET_VAR_TYPE_GLOBAL);
+ else
+ snippet_vars_store_set_variable_type (priv->vars_store, name, SNIPPET_VAR_TYPE_LOCAL);
+
+ focus_on_in_snippet_variable (priv->variables_view,
+ GTK_TREE_MODEL (priv->vars_store_sorted),
+ name,
+ NULL, FALSE);
+
+ g_free (name);
+}
+
+static void
+on_default_text_cell_edited (GtkCellRendererText *cell,
+ gchar *path_string,
+ gchar *new_string,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreePath *path = NULL;
+ gchar *name = NULL;
+ GtkTreeIter iter;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* Get the name at the given path */
+ path = gtk_tree_path_new_from_string (path_string);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->vars_store_sorted), &iter, path);
+ gtk_tree_path_free (path);
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->vars_store_sorted), &iter,
+ VARS_STORE_COL_NAME, &name,
+ -1);
+
+ snippet_vars_store_set_variable_default (priv->vars_store, name, new_string);
+
+ g_free (name);
+
+}
+
+
+static void
+on_variables_view_row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* TODO -- still to be decided :) */
+
+}
+
+static void
+on_variable_add_button_clicked (GtkButton *variable_add_button,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeViewColumn *col = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* Add the variable to the vars_store */
+ snippet_vars_store_add_variable_to_snippet (priv->vars_store,
+ NEW_VAR_NAME, FALSE);
+
+ /* Focus on it */
+ col = gtk_tree_view_get_column (priv->variables_view, VARS_VIEW_COL_NAME);
+ focus_on_in_snippet_variable (priv->variables_view,
+ GTK_TREE_MODEL (priv->vars_store_sorted),
+ NEW_VAR_NAME,
+ col, TRUE);
+}
+
+static void
+on_variable_remove_button_clicked (GtkButton *variable_remove_button,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ gboolean has_selection = FALSE;
+ GtkTreeSelection *selection = NULL;
+ GtkTreeModel *model = NULL;
+ gchar *name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* Get the selected variable */
+ selection = gtk_tree_view_get_selection (priv->variables_view);
+ model = GTK_TREE_MODEL (priv->vars_store_sorted);
+ has_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
+
+ /* We should always have a selection if the remove button is sensitive */
+ g_return_if_fail (has_selection);
+
+ /* Remove the variable from the vars_store */
+ gtk_tree_model_get (model, &iter,
+ VARS_STORE_COL_NAME, &name,
+ -1);
+ snippet_vars_store_remove_variable_from_snippet (priv->vars_store, name);
+
+ g_free (name);
+}
+
+static void
+on_variable_insert_button_clicked (GtkButton *variable_insert_button,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTextBuffer *content_buffer = NULL;
+ gchar *var_name = NULL, *var_name_formated = NULL;
+ GtkTreeSelection *selection = NULL;
+ GtkTreeIter iter;
+ gboolean in_snippet = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* Get the name of the selected variable */
+ selection = gtk_tree_view_get_selection (priv->variables_view);
+ if (!gtk_tree_selection_get_selected (selection, &priv->vars_store_sorted, &iter))
+ g_return_if_reached ();
+
+ gtk_tree_model_get (priv->vars_store_sorted, &iter,
+ VARS_STORE_COL_NAME, &var_name,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet,
+ -1);
+
+ /* We insert the variable in the content text buffer and add it to the snippet
+ if necessary */
+ var_name_formated = g_strconcat (SNIPPET_VAR_START, var_name, SNIPPET_VAR_END, NULL);
+ content_buffer = gtk_text_view_get_buffer (priv->content_text_view);
+ gtk_text_buffer_insert_at_cursor (content_buffer, var_name_formated, -1);
+
+ if (!in_snippet)
+ {
+ snippet_vars_store_add_variable_to_snippet (priv->vars_store, var_name, TRUE);
+ g_object_set (priv->variable_remove_button, "sensitive", TRUE, NULL);
+ }
+
+ g_free (var_name_formated);
+ g_free (var_name);
+}
+
+
+static void
+on_variables_view_selection_changed (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *vars_store_sorted_model = NULL;
+ gboolean in_snippet = FALSE, has_selection = FALSE;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ vars_store_sorted_model = GTK_TREE_MODEL (priv->vars_store_sorted);
+ has_selection = gtk_tree_selection_get_selected (selection,
+ &vars_store_sorted_model,
+ &iter);
+
+ /* If there isn't a selection, the remove and insert button won't be sensitive */
+ g_object_set (priv->variable_remove_button, "sensitive", has_selection, NULL);
+ g_object_set (priv->variable_insert_button, "sensitive", has_selection, NULL);
+
+ if (!has_selection)
+ return;
+
+ /* Check if the selected variable is in the snippet. If not, the remove button
+ won't be sensitive. */
+ gtk_tree_model_get (vars_store_sorted_model, &iter,
+ VARS_STORE_COL_IN_SNIPPET, &in_snippet,
+ -1);
+ g_object_set (priv->variable_remove_button, "sensitive", in_snippet, NULL);
+
+}
+
+static void
+on_snippets_group_combo_box_changed (GtkComboBox *combo_box,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ gchar *group_name = NULL;
+ AnjutaSnippetsGroup *snippets_group = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* If we have a snippet loaded, we change his parent */
+ if (ANJUTA_IS_SNIPPET (priv->snippet))
+ {
+ if (!gtk_combo_box_get_active_iter (priv->snippets_group_combo_box, &iter))
+ {
+ priv->group_error = !check_group_combo_box (ANJUTA_SNIPPETS_EDITOR (user_data));
+ check_all_inputs (ANJUTA_SNIPPETS_EDITOR (user_data));
+ return;
+ }
+
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->group_store), &iter,
+ GROUPS_COL_NAME, &group_name,
+ -1);
+
+ snippets_group = snippets_db_get_snippets_group (priv->snippets_db, group_name);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group));
+
+ priv->snippet->parent_snippets_group = G_OBJECT (snippets_group);
+
+ g_free (group_name);
+ }
+
+ priv->group_error = !check_group_combo_box (ANJUTA_SNIPPETS_EDITOR (user_data));
+ check_all_inputs (ANJUTA_SNIPPETS_EDITOR (user_data));
+}
+
+
+static void
+on_languages_combo_box_changed (GtkComboBox *combo_box,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+ GtkTreeIter iter;
+ gboolean in_snippet = FALSE;
+ gchar *lang_name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ /* This will happen because we set at the end of this function -1 as active */
+ if (gtk_combo_box_get_active (combo_box) < 0)
+ return;
+
+ if (!gtk_combo_box_get_active_iter (combo_box, &iter))
+ g_return_if_reached ();
+
+ /* We reverse the setting */
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->lang_store), &iter,
+ LANG_MODEL_COL_IN_SNIPPET, &in_snippet,
+ LANG_MODEL_COL_NAME, &lang_name,
+ -1);
+ gtk_list_store_set (priv->lang_store, &iter,
+ LANG_MODEL_COL_IN_SNIPPET, !in_snippet,
+ -1);
+
+ if (!in_snippet)
+ snippet_add_language (priv->snippet, lang_name);
+ else
+ snippet_remove_language (priv->snippet, lang_name);
+
+ g_free (lang_name);
+
+ /* We don't want anything to show when there isn't a popup */
+ gtk_combo_box_set_active (combo_box, -1);
+
+ priv->languages_error = !check_languages_combo_box (ANJUTA_SNIPPETS_EDITOR (user_data));
+
+ check_all_inputs (ANJUTA_SNIPPETS_EDITOR (user_data));
+}
+
+static void
+on_trigger_entry_text_changed (GObject *entry_obj,
+ GParamSpec *param_spec,
+ gpointer user_data)
+{
+ SnippetsEditorPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+ priv = ANJUTA_SNIPPETS_EDITOR_GET_PRIVATE (user_data);
+
+ priv->trigger_error = !check_trigger_entry (ANJUTA_SNIPPETS_EDITOR (user_data));
+ priv->languages_error = !check_languages_combo_box (ANJUTA_SNIPPETS_EDITOR (user_data));
+
+ check_all_inputs (ANJUTA_SNIPPETS_EDITOR (user_data));
+}
+
+static void
+on_name_entry_text_changed (GObject *entry_obj,
+ GParamSpec *param_spec,
+ gpointer user_data)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_EDITOR (user_data));
+
+ check_name_entry (ANJUTA_SNIPPETS_EDITOR (user_data));
+}
diff --git a/plugins/snippets-manager/snippets-editor.h b/plugins/snippets-manager/snippets-editor.h
new file mode 100644
index 0000000..18dca7f
--- /dev/null
+++ b/plugins/snippets-manager/snippets-editor.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-editor.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_EDITOR_H__
+#define __SNIPPETS_EDITOR_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "snippet.h"
+#include "snippets-db.h"
+
+G_BEGIN_DECLS
+
+typedef struct _SnippetsEditor SnippetsEditor;
+typedef struct _SnippetsEditorPrivate SnippetsEditorPrivate;
+typedef struct _SnippetsEditorClass SnippetsEditorClass;
+
+#define ANJUTA_TYPE_SNIPPETS_EDITOR (snippets_editor_get_type ())
+#define ANJUTA_SNIPPETS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPETS_EDITOR, SnippetsEditor))
+#define ANJUTA_SNIPPETS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPETS_EDITOR, SnippetsEditorClass))
+#define ANJUTA_IS_SNIPPETS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPETS_EDITOR))
+#define ANJUTA_IS_SNIPPETS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPETS_EDITOR))
+
+struct _SnippetsEditor
+{
+ GtkHBox parent;
+
+ /*< private >*/
+ SnippetsEditorPrivate *priv;
+};
+
+struct _SnippetsEditorClass
+{
+ GtkHBoxClass parent_class;
+
+ /* Signals */
+ void (*snippet_saved) (SnippetsEditor *snippets_editor,
+ GObject *snippet);
+ void (*close_request) (SnippetsEditor *snippets_editor);
+};
+
+
+GType snippets_editor_get_type (void) G_GNUC_CONST;
+SnippetsEditor* snippets_editor_new (SnippetsDB *snippets_db);
+
+void snippets_editor_set_snippet (SnippetsEditor *snippets_editor,
+ AnjutaSnippet *snippet);
+void snippets_editor_set_snippet_new (SnippetsEditor *snippets_editor);
+
+G_END_DECLS
+
+#endif /* __SNIPPETS_EDITOR_H__ */
+
diff --git a/plugins/snippets-manager/snippets-editor.ui b/plugins/snippets-manager/snippets-editor.ui
new file mode 100644
index 0000000..71de686
--- /dev/null
+++ b/plugins/snippets-manager/snippets-editor.ui
@@ -0,0 +1,575 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkAlignment" id="editor_alignment">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">6</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="width_request">336</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="width_request">120</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Snippet Name:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="width_request">180</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Select the name of the Snippet.
+The role of the name is purely informative.</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="name_notify">
+ <property name="width_request">24</property>
+ <property name="visible">True</property>
+ <property name="tooltip_markup"><b>Warning:</b> You should choose a name for the snippet!</property>
+ <property name="stock">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="width_request">120</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Snippets Group:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="snippets_group_combo_box">
+ <property name="width_request">180</property>
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes">Select the Snippets Group to which this snippet belongs.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="group_notify">
+ <property name="width_request">24</property>
+ <property name="tooltip_markup"><b>Error:</b> The snippet must belong to a group!</property>
+ <property name="stock">gtk-dialog-error</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">24</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="width_request">336</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="width_request">120</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Trigger Key:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="trigger_entry">
+ <property name="width_request">180</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_markup">The trigger key is used for fast insertion of the snippet. Type the trigger key in the editor, followed by the "Fast Insert" shortcut to insert a snippet.
+<b>Warning: Must be unique per language. </b></property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="trigger_notify">
+ <property name="width_request">24</property>
+ <property name="stock">gtk-dialog-error</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="width_request">120</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Languages:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="languages_combo_box">
+ <property name="width_request">180</property>
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes">Select the languages for which you want to use this snippet.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="languages_notify">
+ <property name="width_request">24</property>
+ <property name="stock">gtk-dialog-error</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">24</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="width_request">120</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Keywords:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="keywords_entry">
+ <property name="width_request">546</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Keywords are used for better searching of the snippets. They should be relevant to the snippet content. Type them separated by a single space.</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Snippet Properties</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="top_padding">12</property>
+ <child>
+ <object class="GtkHPaned" id="hpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="right_padding">6</property>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="right_padding">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="content_text_view">
+ <property name="width_request">480</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="preview_button">
+ <property name="label" translatable="yes">Preview</property>
+ <property name="width_request">96</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Snippet Content</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="left_padding">6</property>
+ <child>
+ <object class="GtkFrame" id="frame3">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="right_padding">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="variables_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="variable_add_button">
+ <property name="label" translatable="yes">Add</property>
+ <property name="width_request">96</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="variable_remove_button">
+ <property name="label" translatable="yes">Remove</property>
+ <property name="width_request">96</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="variable_insert_button">
+ <property name="label" translatable="yes">Insert</property>
+ <property name="width_request">96</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Snippet Variables</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="label" translatable="yes">Close</property>
+ <property name="width_request">96</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="save_button">
+ <property name="label" translatable="yes">Save</property>
+ <property name="width_request">96</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plugins/snippets-manager/snippets-global-variables.xml b/plugins/snippets-manager/snippets-global-variables.xml
new file mode 100644
index 0000000..df0a80c
--- /dev/null
+++ b/plugins/snippets-manager/snippets-global-variables.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<anjuta-global-variables>
+
+ <global-variable name="date" is_command="true" ><![CDATA[date +"%B %e, %Y"]]></global-variable>
+
+ <global-variable name="time" is_command="true" ><![CDATA[date +%T]]></global-variable>
+
+ <global-variable name="date_time" is_command="true" ><![CDATA[date +"%a %B %d %T %Y"]]></global-variable>
+
+ <global-variable name="year" is_command="true" ><![CDATA[date +%Y]]></global-variable>
+
+ <global-variable name="email" is_command="false"><![CDATA[user host]]></global-variable>
+
+</anjuta-global-variables>
diff --git a/plugins/snippets-manager/snippets-group.c b/plugins/snippets-manager/snippets-group.c
new file mode 100644
index 0000000..5253d1d
--- /dev/null
+++ b/plugins/snippets-manager/snippets-group.c
@@ -0,0 +1,286 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-group.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-group.h"
+#include <gio/gio.h>
+
+
+#define ANJUTA_SNIPPETS_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_SNIPPETS_GROUP, AnjutaSnippetsGroupPrivate))
+
+struct _AnjutaSnippetsGroupPrivate
+{
+ gchar* name;
+
+ GList* snippets;
+};
+
+
+G_DEFINE_TYPE (AnjutaSnippetsGroup, snippets_group, G_TYPE_OBJECT);
+
+static void
+snippets_group_dispose (GObject* snippets_group)
+{
+ AnjutaSnippetsGroup *anjuta_snippets_group = ANJUTA_SNIPPETS_GROUP (snippets_group);
+ AnjutaSnippetsGroupPrivate *priv = ANJUTA_SNIPPETS_GROUP_GET_PRIVATE (snippets_group);
+ AnjutaSnippet *cur_snippet = NULL;
+ GList *iter = NULL;
+
+ /* Delete the name and description fields */
+ g_free (priv->name);
+ priv->name = NULL;
+
+ /* Delete the snippets in the group */
+ for (iter = g_list_first (priv->snippets); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet = (AnjutaSnippet *)iter->data;
+ g_object_unref (cur_snippet);
+ }
+ g_list_free (anjuta_snippets_group->priv->snippets);
+
+ G_OBJECT_CLASS (snippets_group_parent_class)->dispose (snippets_group);
+}
+
+static void
+snippets_group_finalize (GObject* snippets_group)
+{
+ G_OBJECT_CLASS (snippets_group_parent_class)->finalize (snippets_group);
+}
+
+static void
+snippets_group_class_init (AnjutaSnippetsGroupClass* klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ snippets_group_parent_class = g_type_class_peek_parent (klass);
+ object_class->dispose = snippets_group_dispose;
+ object_class->finalize = snippets_group_finalize;
+
+ g_type_class_add_private (klass, sizeof (AnjutaSnippetsGroupPrivate));
+}
+
+static void
+snippets_group_init (AnjutaSnippetsGroup* snippets_group)
+{
+ AnjutaSnippetsGroupPrivate* priv = ANJUTA_SNIPPETS_GROUP_GET_PRIVATE (snippets_group);
+
+ snippets_group->priv = priv;
+
+ /* Initialize the private field */
+ priv->name = NULL;
+ priv->snippets = NULL;
+}
+
+/**
+ * snippets_group_new:
+ * @snippets_group_name: A name for the group. It's unique.
+ *
+ * Makes a new #AnjutaSnippetsGroup object.
+ *
+ * Returns: A new #AnjutaSnippetsGroup object or NULL on failure.
+ **/
+AnjutaSnippetsGroup*
+snippets_group_new (const gchar* snippets_group_name)
+{
+ AnjutaSnippetsGroup* snippets_group = NULL;
+ AnjutaSnippetsGroupPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (snippets_group_name != NULL, NULL);
+
+ /* Initialize the object */
+ snippets_group = ANJUTA_SNIPPETS_GROUP (g_object_new (snippets_group_get_type (), NULL));
+ priv = ANJUTA_SNIPPETS_GROUP_GET_PRIVATE (snippets_group);
+
+ /* Copy the name, description and filename */
+ priv->name = g_strdup (snippets_group_name);
+
+ return snippets_group;
+}
+
+
+const gchar*
+snippets_group_get_name (AnjutaSnippetsGroup* snippets_group)
+{
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), NULL);
+
+ return snippets_group->priv->name;
+}
+
+void
+snippets_group_set_name (AnjutaSnippetsGroup* snippets_group,
+ const gchar* new_group_name)
+{
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group));
+
+ g_free (snippets_group->priv->name);
+ snippets_group->priv->name = g_strdup (new_group_name);
+}
+
+static gint
+compare_snippets_by_name (gconstpointer a,
+ gconstpointer b)
+{
+ AnjutaSnippet *snippet_a = (AnjutaSnippet *)a,
+ *snippet_b = (AnjutaSnippet *)b;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet_a), 0);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet_b), 0);
+
+ return g_utf8_collate (snippet_get_name (snippet_a),
+ snippet_get_name (snippet_b));
+}
+
+/**
+ * snippets_group_add_snippet:
+ * @snippets_group: A #AnjutaSnippetsGroup object.
+ * @snippet: A snippet to be added.
+ *
+ * Adds a new #AnjutaSnippet to the snippet group, checking for conflicts.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+snippets_group_add_snippet (AnjutaSnippetsGroup* snippets_group,
+ AnjutaSnippet* snippet)
+{
+ AnjutaSnippetsGroupPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+ priv = ANJUTA_SNIPPETS_GROUP_GET_PRIVATE (snippets_group);
+
+
+ /* Check if there is a snippet with the same key */
+ if (snippets_group_has_snippet (snippets_group, snippet))
+ return FALSE;
+
+ /* Add the new snippet to the group */
+ priv->snippets = g_list_insert_sorted (snippets_group->priv->snippets,
+ snippet,
+ compare_snippets_by_name);
+ snippet->parent_snippets_group = G_OBJECT (snippets_group);
+
+ return TRUE;
+}
+
+/**
+ * snippets_group_remove_snippet:
+ * @snippets_group: A #AnjutaSnippetsGroup object.
+ * @trigger_key: The trigger-key of the #AnjutaSnippet to be removed.
+ * @language: The language of the #AnjutaSnippet to be removed.
+ * @remove_all_languages_support: If it's FALSE it will remove just the support of the snippet for
+ * the language. If it's TRUE it will actually remove the snippet.
+ *
+ * If remove_all_languages_support is TRUE, this will remove the snippet from the snippet-group,
+ * if it's FALSE, it will just remove the language support for the given language.
+ **/
+void
+snippets_group_remove_snippet (AnjutaSnippetsGroup* snippets_group,
+ const gchar* trigger_key,
+ const gchar* language,
+ gboolean remove_all_languages_support)
+{
+
+ GList *iter = NULL;
+ AnjutaSnippet *to_be_deleted_snippet = NULL, *cur_snippet;
+ const gchar *cur_snippet_trigger = NULL;
+ AnjutaSnippetsGroupPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group));
+ g_return_if_fail (trigger_key != NULL);
+ priv = ANJUTA_SNIPPETS_GROUP_GET_PRIVATE (snippets_group);
+
+ /* Check if there is a snippet with the same key */
+ for (iter = g_list_first (priv->snippets); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_snippet = ANJUTA_SNIPPET (iter->data);
+ if (!ANJUTA_IS_SNIPPET (cur_snippet))
+ g_return_if_reached ();
+ cur_snippet_trigger = snippet_get_trigger_key (cur_snippet);
+
+ if (!g_strcmp0 (cur_snippet_trigger, trigger_key) &&
+ snippet_has_language (cur_snippet, language))
+ {
+ if (remove_all_languages_support)
+ {
+ to_be_deleted_snippet = cur_snippet;
+ break;
+ }
+ else
+ {
+ snippet_remove_language (cur_snippet, language);
+ return;
+ }
+ }
+
+ }
+
+ /* If we found a snippet that should be deleted we remove it from the list and unref it */
+ if (to_be_deleted_snippet)
+ {
+ priv->snippets = g_list_remove (priv->snippets, to_be_deleted_snippet);
+ g_object_unref (to_be_deleted_snippet);
+
+ }
+
+}
+
+gboolean
+snippets_group_has_snippet (AnjutaSnippetsGroup *snippets_group,
+ AnjutaSnippet *snippet)
+{
+ AnjutaSnippetsGroupPrivate *priv = NULL;
+ GList *iter = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+ priv = ANJUTA_SNIPPETS_GROUP_GET_PRIVATE (snippets_group);
+
+ for (iter = g_list_first (priv->snippets); iter != NULL; iter = g_list_next (iter))
+ {
+ if (!ANJUTA_IS_SNIPPET (iter->data))
+ continue;
+ if (snippet_is_equal (snippet, ANJUTA_SNIPPET (iter->data)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * snippets_group_get_snippets_list:
+ * @snippets_group: A #AnjutaSnippetsGroup object.
+ *
+ * Gets the snippets in this group.
+ * Important: This returns the actual list as saved in the #AnjutaSnippetsGroup.
+ *
+ * Returns: A #GList with entries of type #AnjutaSnippet.
+ **/
+GList*
+snippets_group_get_snippets_list (AnjutaSnippetsGroup* snippets_group)
+{
+ return snippets_group->priv->snippets;
+}
diff --git a/plugins/snippets-manager/snippets-group.h b/plugins/snippets-manager/snippets-group.h
new file mode 100644
index 0000000..5eef9ec
--- /dev/null
+++ b/plugins/snippets-manager/snippets-group.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-group.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_GROUP_H__
+#define __SNIPPETS_GROUP_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "snippet.h"
+
+G_BEGIN_DECLS
+
+#define ANJUTA_TYPE_SNIPPETS_GROUP (snippets_group_get_type ())
+#define ANJUTA_SNIPPETS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPETS_GROUP, AnjutaSnippetsGroup))
+#define ANJUTA_SNIPPETS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPETS_GROUP, AnjutaSnippetsGroupClass))
+#define ANJUTA_IS_SNIPPETS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPETS_GROUP))
+#define ANJUTA_IS_SNIPPETS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPETS_GROUP))
+
+typedef struct _AnjutaSnippetsGroup AnjutaSnippetsGroup;
+typedef struct _AnjutaSnippetsGroupPrivate AnjutaSnippetsGroupPrivate;
+typedef struct _AnjutaSnippetsGroupClass AnjutaSnippetsGroupClass;
+
+struct _AnjutaSnippetsGroup
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ AnjutaSnippetsGroupPrivate* priv;
+};
+
+struct _AnjutaSnippetsGroupClass
+{
+ GObjectClass parent_class;
+
+};
+
+GType snippets_group_get_type (void) G_GNUC_CONST;
+AnjutaSnippetsGroup* snippets_group_new (const gchar* snippets_group_name);
+const gchar* snippets_group_get_name (AnjutaSnippetsGroup* snippets_group);
+void snippets_group_set_name (AnjutaSnippetsGroup* snippets_group,
+ const gchar* new_group_name);
+gboolean snippets_group_add_snippet (AnjutaSnippetsGroup* snippets_group,
+ AnjutaSnippet* snippet);
+void snippets_group_remove_snippet (AnjutaSnippetsGroup* snippets_group,
+ const gchar* trigger_key,
+ const gchar* language,
+ gboolean remove_all_languages_support);
+gboolean snippets_group_has_snippet (AnjutaSnippetsGroup *snippets_group,
+ AnjutaSnippet *snippet);
+GList* snippets_group_get_snippets_list (AnjutaSnippetsGroup* snippets_group);
+
+G_END_DECLS
+
+#endif /* __SNIPPETS_GROUP_H__ */
+
diff --git a/plugins/snippets-manager/snippets-import-export.c b/plugins/snippets-manager/snippets-import-export.c
new file mode 100644
index 0000000..128a75c
--- /dev/null
+++ b/plugins/snippets-manager/snippets-import-export.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-import-export.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+
+#include "snippets-import-export.h"
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <libanjuta/anjuta-utils.h>
+
+static void
+add_native_snippets_at_path (SnippetsDB *snippets_db,
+ const gchar *path)
+{
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ if (path == NULL)
+ return;
+ /* TODO - import the snippets */
+}
+
+static void
+add_other_snippets_at_path (SnippetsDB *snippets_db,
+ const gchar *path)
+{
+
+ /* TODO - import the snippets. Should try every format known until it matches */
+}
+
+void
+snippets_manager_import_snippets (SnippetsDB *snippets_db,
+ AnjutaShell *anjuta_shell)
+{
+ GtkWidget *file_chooser = NULL;
+ GtkFileFilter *native_filter = NULL, *other_filter = NULL, *cur_filter = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+ file_chooser = gtk_file_chooser_dialog_new (_("Import Snippets"),
+ GTK_WINDOW (anjuta_shell),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ /* Set up the filters */
+ native_filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (native_filter, "Native format");
+ gtk_file_filter_add_pattern (native_filter, "*.anjuta-snippets");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_chooser), native_filter);
+
+ other_filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (other_filter, "Other formats");
+ gtk_file_filter_add_pattern (other_filter, "*");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_chooser), other_filter);
+
+ if (gtk_dialog_run (GTK_DIALOG (file_chooser)) == GTK_RESPONSE_ACCEPT)
+ {
+ gchar *uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (file_chooser)),
+ *path = anjuta_util_get_local_path_from_uri (uri);
+
+ cur_filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (file_chooser));
+ if (!g_strcmp0 ("Native format", gtk_file_filter_get_name (cur_filter)))
+ add_native_snippets_at_path (snippets_db, path);
+ else
+ add_other_snippets_at_path (snippets_db, path);
+
+ g_free (path);
+ g_free (uri);
+ }
+
+ gtk_widget_destroy (file_chooser);
+}
+
+void snippets_manager_export_snippets (SnippetsDB *snippets_db,
+ AnjutaShell *anjuta_shell)
+{
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+
+}
\ No newline at end of file
diff --git a/plugins/snippets-manager/snippets-import-export.h b/plugins/snippets-manager/snippets-import-export.h
new file mode 100644
index 0000000..b38c560
--- /dev/null
+++ b/plugins/snippets-manager/snippets-import-export.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-import-export.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-db.h"
+#include <libanjuta/anjuta-shell.h>
+
+void snippets_manager_import_snippets (SnippetsDB *snippets_db,
+ AnjutaShell *anjuta_shell);
+void snippets_manager_export_snippets (SnippetsDB *snippets_db,
+ AnjutaShell *anjuta_shell);
\ No newline at end of file
diff --git a/plugins/snippets-manager/snippets-interaction-interpreter.c b/plugins/snippets-manager/snippets-interaction-interpreter.c
new file mode 100644
index 0000000..48fc39a
--- /dev/null
+++ b/plugins/snippets-manager/snippets-interaction-interpreter.c
@@ -0,0 +1,925 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-interaction-interpreter.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-interaction-interpreter.h"
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-editor-language.h>
+#include <libanjuta/interfaces/ianjuta-editor-selection.h>
+#include <string.h>
+
+#define IN_WORD(c) (g_ascii_isalnum (c) || c == '_')
+
+#define ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
+ ANJUTA_TYPE_SNIPPETS_INTERACTION,\
+ SnippetsInteractionPrivate))
+
+typedef struct _SnippetEditingInfo SnippetEditingInfo;
+typedef struct _SnippetVariableInfo SnippetVariableInfo;
+
+struct _SnippetsInteractionPrivate
+{
+ AnjutaSnippet *cur_snippet;
+ gboolean editing;
+ SnippetEditingInfo *editing_info;
+
+ IAnjutaEditor *cur_editor;
+ gulong changed_handler_id;
+ gulong cursor_moved_handler_id;
+
+ gboolean selection_set_blocker;
+ gboolean changing_values_blocker;
+ IAnjutaIterable *cur_sel_start_iter;
+
+ AnjutaShell *shell;
+};
+
+struct _SnippetVariableInfo
+{
+ gint cur_value_length;
+
+ /* List of IAnjutaIterable objects */
+ GList *appearances;
+
+};
+
+struct _SnippetEditingInfo
+{
+ IAnjutaIterable *snippet_start;
+ IAnjutaIterable *snippet_end;
+ IAnjutaIterable *snippet_finish_position;
+
+ /* List of SnippetVariableInfo structures */
+ GList *snippet_vars_info;
+ GList *cur_var;
+
+};
+
+G_DEFINE_TYPE (SnippetsInteraction, snippets_interaction, G_TYPE_OBJECT);
+
+static void
+snippets_interaction_init (SnippetsInteraction *snippets_interaction)
+{
+ SnippetsInteractionPrivate* priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+
+ /* Initialize the private field */
+ priv->cur_snippet = NULL;
+ priv->editing = FALSE;
+ priv->editing_info = NULL;
+
+ priv->cur_editor = NULL;
+
+ priv->selection_set_blocker = FALSE;
+ priv->changing_values_blocker = FALSE;
+ priv->cur_sel_start_iter = NULL;
+
+ priv->shell = NULL;
+
+}
+
+static void
+snippets_interaction_class_init (SnippetsInteractionClass *snippets_interaction_class)
+{
+ snippets_interaction_parent_class = g_type_class_peek_parent (snippets_interaction_class);
+ g_type_class_add_private (snippets_interaction_class, sizeof (SnippetsInteractionPrivate));
+}
+
+/* Private */
+
+static gboolean focus_on_next_snippet_variable (SnippetsInteraction *snippets_interaction);
+static void update_snippet_positions (SnippetsInteraction *snippets_interaction,
+ IAnjutaIterable *start_position_iter,
+ gint modified_count);
+static gchar char_at_iterator (IAnjutaEditor *editor,
+ IAnjutaIterable *iter);
+static void on_cur_editor_changed (IAnjutaEditor *cur_editor,
+ GObject *position,
+ gboolean added,
+ gint length,
+ gint lines,
+ gchar *text,
+ gpointer user_data);
+static void on_cur_editor_cursor_moved (IAnjutaEditor *cur_editor,
+ gpointer user_data);
+static void delete_snippet_editing_info (SnippetsInteraction *snippets_interaction);
+static void start_snippet_editing_session (SnippetsInteraction *snippets_interaction,
+ IAnjutaIterable *start_pos,
+ gint len);
+static void stop_snippet_editing_session (SnippetsInteraction *snippets_interaction);
+
+
+static gboolean
+focus_on_next_snippet_variable (SnippetsInteraction *snippets_interaction)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ SnippetVariableInfo *var_info = NULL;
+ IAnjutaIterable *first_var_appearance = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction), FALSE);
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+ g_return_val_if_fail (IANJUTA_IS_EDITOR (priv->cur_editor), FALSE);
+
+ /* If we aren't editing we should just return */
+ if (!priv->editing)
+ return FALSE;
+ g_return_val_if_fail (priv->editing_info != NULL, FALSE);
+
+ /* If the current variable doesn't point to anything we stop editing */
+ if (priv->editing_info->cur_var == NULL)
+ {
+ if (IANJUTA_IS_ITERABLE (priv->editing_info->snippet_finish_position))
+ {
+ ianjuta_editor_goto_position (priv->cur_editor,
+ priv->editing_info->snippet_finish_position,
+ NULL);
+ }
+ stop_snippet_editing_session (snippets_interaction);
+
+ return FALSE;
+ }
+
+ /* We set the cursor to the current variable (the selection will be done in the
+ "move-cursor" signal handler) ... */
+ var_info = (SnippetVariableInfo *)priv->editing_info->cur_var->data;
+ if (var_info->appearances)
+ {
+ GList *first_elem = g_list_first (var_info->appearances);
+ first_var_appearance = IANJUTA_ITERABLE (first_elem->data);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (first_elem->data), FALSE);
+
+ ianjuta_editor_goto_position (priv->cur_editor,
+ first_var_appearance,
+ NULL);
+ }
+
+ /* ... and move to the next variable */
+ priv->editing_info->cur_var = g_list_next (priv->editing_info->cur_var);
+
+ return TRUE;
+}
+
+static gboolean
+update_editor_iter (IAnjutaIterable *iter,
+ IAnjutaIterable *start_position_iter,
+ gint modified_count,
+ SnippetsInteraction *snippets_interaction)
+{
+ gint iter_position = 0, start_position = 0;
+
+ /* Assertions */
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (iter), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction), FALSE);
+
+ /* We only return FALSE if a special case occured like deleting an iter */
+ if (!modified_count)
+ return TRUE;
+
+ iter_position = ianjuta_iterable_get_position (iter, NULL);
+ start_position = ianjuta_iterable_get_position (start_position_iter, NULL);
+
+ /* If the iter_position is less than the start_position, we don't modify
+ the iter. */
+ if (iter_position <= start_position)
+ return TRUE;
+
+ /* If we deleted this iterator, we should stop the editing session */
+ if (modified_count < 0 &&
+ start_position <= iter_position &&
+ start_position - modified_count >= iter_position)
+ {
+ return FALSE;
+ }
+
+ /* Update the iter position */
+ ianjuta_iterable_set_position (iter, iter_position + modified_count, NULL);
+
+ return TRUE;
+}
+
+static void
+update_snippet_positions (SnippetsInteraction *snippets_interaction,
+ IAnjutaIterable *start_position_iter,
+ gint modified_count)
+{
+ GList *iter = NULL, *iter2 = NULL;
+ SnippetsInteractionPrivate *priv = NULL;
+ SnippetVariableInfo *cur_var_info = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+ g_return_if_fail (priv->editing);
+ g_return_if_fail (priv->editing_info != NULL);
+
+ /* Update all the iter's if necessary */
+ if (!update_editor_iter (priv->editing_info->snippet_start,
+ start_position_iter,
+ modified_count,
+ snippets_interaction))
+ {
+ stop_snippet_editing_session (snippets_interaction);
+ return;
+ }
+
+ if (!update_editor_iter (priv->editing_info->snippet_end,
+ start_position_iter,
+ modified_count,
+ snippets_interaction))
+ {
+ stop_snippet_editing_session (snippets_interaction);
+ return;
+ }
+
+ if (priv->editing_info->snippet_finish_position != NULL)
+ if (!update_editor_iter (priv->editing_info->snippet_finish_position,
+ start_position_iter,
+ modified_count,
+ snippets_interaction))
+ {
+ stop_snippet_editing_session (snippets_interaction);
+ return;
+ }
+
+ for (iter = priv->editing_info->snippet_vars_info; iter != NULL; iter = g_list_next (iter))
+ {
+ cur_var_info = (SnippetVariableInfo *)iter->data;
+
+ for (iter2 = cur_var_info->appearances; iter2 != NULL; iter2 = g_list_next (iter2))
+ {
+ if (!update_editor_iter (IANJUTA_ITERABLE (iter2->data),
+ start_position_iter,
+ modified_count,
+ snippets_interaction))
+ {
+ stop_snippet_editing_session (snippets_interaction);
+ return;
+ }
+ }
+
+ }
+
+}
+
+static void
+update_variables_values (SnippetsInteraction *snippets_interaction,
+ IAnjutaIterable *iter,
+ gint modified_value,
+ gchar *text)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ GList *iter1 = NULL, *iter2 = NULL, *edited_app_node = NULL;
+ SnippetVariableInfo *var_info = NULL;
+ IAnjutaIterable *var_iter = NULL, *start_iter = NULL, *end_iter = NULL;
+ gboolean found = FALSE;
+ gint diff = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+ g_return_if_fail (priv->editing);
+ g_return_if_fail (priv->editing_info);
+ if (priv->changing_values_blocker)
+ return;
+ priv->changing_values_blocker = TRUE;
+
+ /* Search for any modified value of a variable */
+ for (iter1 = priv->editing_info->snippet_vars_info; iter1 != NULL; iter1 = g_list_next (iter1))
+ {
+ found = FALSE;
+ var_info = (SnippetVariableInfo *)iter1->data;
+
+ for (iter2 = g_list_first (var_info->appearances); iter2 != NULL; iter2 = g_list_next (iter2))
+ {
+ var_iter = IANJUTA_ITERABLE (iter2->data);
+ g_return_if_fail (IANJUTA_IS_ITERABLE (var_iter));
+
+ /* We found a variable value modified */
+ diff = ianjuta_iterable_diff (var_iter, iter, NULL);
+
+ if ((diff == 0) ||
+ (diff > 0 && diff <= var_info->cur_value_length))
+ {
+ edited_app_node = iter2;
+ found = TRUE;
+ var_info->cur_value_length += modified_value;
+
+ break;
+ }
+ }
+ if (found) break;
+
+ }
+
+ if (found)
+ {
+ g_return_if_fail (edited_app_node != NULL);
+
+ /* Modify the other appearances of the variables */
+ for (iter1 = g_list_first (edited_app_node); iter1 != NULL; iter1 = g_list_next (iter1))
+ {
+
+ /* Skipping the already visited appeareance */
+ if (iter1 == edited_app_node)
+ continue;
+
+ var_iter = IANJUTA_ITERABLE (iter1->data);
+ g_return_if_fail (IANJUTA_IS_ITERABLE (var_iter));
+
+ start_iter = ianjuta_iterable_clone (var_iter, NULL);
+ ianjuta_iterable_set_position (start_iter,
+ ianjuta_iterable_get_position (var_iter, NULL) + diff,
+ NULL);
+ end_iter = ianjuta_iterable_clone (var_iter, NULL);
+ ianjuta_iterable_set_position (end_iter,
+ ianjuta_iterable_get_position (var_iter, NULL) + diff - modified_value,
+ NULL);
+
+ if (modified_value > 0)
+ ianjuta_editor_insert (priv->cur_editor, start_iter, text, modified_value, NULL);
+ else
+ ianjuta_editor_erase (priv->cur_editor, start_iter, end_iter, NULL);
+
+ g_object_unref (start_iter);
+ g_object_unref (end_iter);
+ }
+ }
+
+ priv->changing_values_blocker = FALSE;
+}
+
+static gchar
+char_at_iterator (IAnjutaEditor *editor,
+ IAnjutaIterable *iter)
+{
+ IAnjutaIterable *next = NULL;
+ gchar *text = NULL, returned_char = 0;
+
+ /* Assertions */
+ g_return_val_if_fail (IANJUTA_IS_EDITOR (editor), 0);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (iter), 0);
+
+ next = ianjuta_iterable_clone (iter, NULL);
+ ianjuta_iterable_next (next, NULL);
+
+ text = ianjuta_editor_get_text (editor, iter, next, NULL);
+ if (text == NULL)
+ return 0;
+
+ returned_char = text[0];
+ g_free (text);
+ g_object_unref (next);
+
+ return returned_char;
+}
+
+static void
+on_cur_editor_changed (IAnjutaEditor *cur_editor,
+ GObject *position,
+ gboolean added,
+ gint length,
+ gint lines,
+ gchar *text,
+ gpointer user_data)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ gint sign = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (user_data));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (user_data);
+ g_return_if_fail (IANJUTA_IS_ITERABLE (position));
+
+ if (!priv->editing)
+ return;
+
+ sign = (added)? 1:-1;
+ update_snippet_positions (ANJUTA_SNIPPETS_INTERACTION (user_data),
+ IANJUTA_ITERABLE (position),
+ sign * length);
+
+ if (!priv->editing)
+ return;
+
+ update_variables_values (ANJUTA_SNIPPETS_INTERACTION (user_data),
+ IANJUTA_ITERABLE (position),
+ sign * length,
+ text);
+
+}
+
+static void
+on_cur_editor_cursor_moved (IAnjutaEditor *cur_editor,
+ gpointer user_data)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ IAnjutaIterable *cur_pos = NULL, *var_iter = NULL;
+ GList *iter = NULL, *iter2 = NULL;
+ SnippetVariableInfo *cur_var_info = NULL;
+ gboolean found = FALSE;
+ gint end_var_pos = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (user_data));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (user_data);
+ g_return_if_fail (IANJUTA_IS_EDITOR (priv->cur_editor));
+
+ if (!priv->editing)
+ return;
+ g_return_if_fail (priv->editing_info != NULL);
+ cur_pos = ianjuta_editor_get_position (priv->cur_editor, NULL);
+ if (!IANJUTA_IS_EDITOR_SELECTION (priv->cur_editor))
+ return;
+
+ if (priv->selection_set_blocker)
+ {
+ priv->selection_set_blocker = FALSE;
+ return;
+ }
+
+ /* We check to see if the current position is the start of a variable appearance
+ and select it if that happens */
+ for (iter = priv->editing_info->snippet_vars_info; iter != NULL; iter = g_list_next (iter))
+ {
+ found = FALSE;
+
+ cur_var_info = (SnippetVariableInfo *)iter->data;
+ for (iter2 = cur_var_info->appearances; iter2 != NULL; iter2 = g_list_next (iter2))
+ {
+
+ var_iter = IANJUTA_ITERABLE (iter2->data);
+ g_return_if_fail (IANJUTA_IS_ITERABLE (var_iter));
+
+ if (ianjuta_iterable_diff (cur_pos, var_iter, NULL) == 0)
+ {
+ found = TRUE;
+
+ if (IANJUTA_IS_ITERABLE (priv->cur_sel_start_iter))
+ {
+ if (ianjuta_iterable_diff (cur_pos, priv->cur_sel_start_iter, NULL) == 0)
+ {
+ g_object_unref (priv->cur_sel_start_iter);
+ priv->cur_sel_start_iter = NULL;
+ break;
+ }
+ g_object_unref (priv->cur_sel_start_iter);
+ }
+
+ IAnjutaIterable *end_iter = ianjuta_iterable_clone (var_iter, NULL);
+
+ end_var_pos = cur_var_info->cur_value_length +
+ ianjuta_iterable_get_position (var_iter, NULL);
+ ianjuta_iterable_set_position (end_iter, end_var_pos, NULL);
+
+ ianjuta_editor_selection_set (IANJUTA_EDITOR_SELECTION (priv->cur_editor),
+ cur_pos, end_iter, TRUE, NULL);
+ priv->cur_sel_start_iter = ianjuta_iterable_clone (cur_pos, NULL);
+
+ priv->selection_set_blocker = TRUE;
+ g_object_unref (end_iter);
+ break;
+ }
+ }
+ if (found)
+ break;
+
+ }
+#if 0
+ /* Check if we got out of the snippet bounds. */
+ if (ianjuta_iterable_diff (priv->editing_info->snippet_start, cur_pos, NULL) > 0 ||
+ ianjuta_iterable_diff (priv->editing_info->snippet_end, cur_pos, NULL) < 0)
+ {
+ stop_snippet_editing_session (ANJUTA_SNIPPETS_INTERACTION (user_data));
+ g_object_unref (cur_pos);
+ return;
+ }
+#endif
+
+ g_object_unref (cur_pos);
+
+}
+
+static void
+delete_snippet_editing_info (SnippetsInteraction *snippets_interaction)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ GList *iter = NULL, *iter2 = NULL;
+ SnippetVariableInfo *cur_var_info = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+
+ if (priv->editing_info == NULL)
+ return;
+
+ g_object_unref (priv->editing_info->snippet_start);
+ g_object_unref (priv->editing_info->snippet_end);
+ if (priv->editing_info->snippet_finish_position != NULL)
+ g_object_unref (priv->editing_info->snippet_finish_position);
+
+ for (iter = g_list_first (priv->editing_info->snippet_vars_info); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_var_info = (SnippetVariableInfo *)iter->data;
+
+ for (iter2 = g_list_first (cur_var_info->appearances); iter2 != NULL; iter2 = g_list_next (iter2))
+ {
+ if (!IANJUTA_IS_ITERABLE (iter2->data))
+ g_return_if_reached ();
+
+ g_object_unref (IANJUTA_ITERABLE (iter2->data));
+ }
+ g_list_free (cur_var_info->appearances);
+
+ g_free (cur_var_info);
+ }
+ g_list_first (priv->editing_info->snippet_vars_info);
+
+ priv->editing_info = NULL;
+}
+
+static gint
+sort_appearances (gconstpointer a,
+ gconstpointer b)
+{
+ IAnjutaIterable *ia = IANJUTA_ITERABLE (a),
+ *ib = IANJUTA_ITERABLE (b);
+
+ /* Assertions */
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (a), 0);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (b), 0);
+
+ return ianjuta_iterable_get_position (ia, NULL) - ianjuta_iterable_get_position (ib, NULL);
+}
+
+static gint
+sort_variables (gconstpointer a,
+ gconstpointer b)
+{
+ SnippetVariableInfo *var1 = (SnippetVariableInfo *)a,
+ *var2 = (SnippetVariableInfo *)b;
+ IAnjutaIterable *var1_min = NULL, *var2_min = NULL;
+
+ var1->appearances = g_list_sort (var1->appearances, sort_appearances);
+ var2->appearances = g_list_sort (var2->appearances, sort_appearances);
+
+ var1_min = IANJUTA_ITERABLE (var1->appearances->data);
+ var2_min = IANJUTA_ITERABLE (var2->appearances->data);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (var1_min), 0);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (var2_min), 0);
+
+ return ianjuta_iterable_get_position (var1_min, NULL) -
+ ianjuta_iterable_get_position (var2_min, NULL);
+}
+
+static void
+start_snippet_editing_session (SnippetsInteraction *snippets_interaction,
+ IAnjutaIterable *start_pos,
+ gint len)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ gint finish_position = -1, cur_var_length = -1, i = 0, cur_appearance_pos = 0;
+ GList *relative_positions = NULL, *variables_length = NULL,
+ *iter = NULL, *iter2 = NULL;
+ GPtrArray *cur_var_positions = NULL;
+ SnippetVariableInfo *cur_var_info = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+ g_return_if_fail (ANJUTA_IS_SNIPPET (priv->cur_snippet));
+ g_return_if_fail (IANJUTA_IS_EDITOR (priv->cur_editor));
+
+ /* Mark the editing session */
+ priv->editing = TRUE;
+
+ /* Clear the previous editing_info structure if needed */
+ delete_snippet_editing_info (snippets_interaction);
+
+ /* Create the editing_info structure */
+ priv->editing_info = g_new0 (SnippetEditingInfo, 1);
+ priv->editing_info->snippet_start = ianjuta_iterable_clone (start_pos, NULL);
+ priv->editing_info->snippet_end = ianjuta_iterable_clone (start_pos, NULL);
+ ianjuta_iterable_set_position (priv->editing_info->snippet_end,
+ ianjuta_iterable_get_position (start_pos, NULL) + len,
+ NULL);
+
+ finish_position = snippet_get_cur_value_end_position (priv->cur_snippet);
+ if (finish_position >= 0)
+ {
+ priv->editing_info->snippet_finish_position = ianjuta_iterable_clone (start_pos, NULL);
+ ianjuta_iterable_set_position (priv->editing_info->snippet_finish_position,
+ ianjuta_iterable_get_position (start_pos, NULL) + finish_position,
+ NULL);
+ }
+ else
+ {
+ priv->editing_info->snippet_finish_position = NULL;
+ }
+
+ /* Calculate positions of each variable appearance */
+ relative_positions = snippet_get_variable_relative_positions (priv->cur_snippet);
+ variables_length = snippet_get_variable_cur_values_len (priv->cur_snippet);
+
+ iter = g_list_first (relative_positions);
+ iter2 = g_list_first (variables_length);
+ while (iter != NULL && iter2 != NULL)
+ {
+ cur_var_positions = (GPtrArray *)iter->data;
+ cur_var_length = GPOINTER_TO_INT (iter2->data);
+
+ /* If the variable doesn't have any appearance, we don't add it */
+ if (!cur_var_positions->len)
+ {
+ iter = g_list_next (iter);
+ iter2 = g_list_next (iter2);
+ continue;
+ }
+
+ /* Initialize the current variable info */
+ cur_var_info = g_new0 (SnippetVariableInfo, 1);
+ cur_var_info->cur_value_length = cur_var_length;
+ cur_var_info->appearances = NULL;
+
+ /* Add each variable appearance relative positions */
+ for (i = 0; i < cur_var_positions->len; i ++)
+ {
+ IAnjutaIterable *new_iter = NULL;
+ cur_appearance_pos = GPOINTER_TO_INT (g_ptr_array_index (cur_var_positions, i));
+ new_iter = ianjuta_iterable_clone (start_pos, NULL);
+ ianjuta_iterable_set_position (new_iter,
+ ianjuta_iterable_get_position (new_iter, NULL) + cur_appearance_pos,
+ NULL);
+
+ cur_var_info->appearances = g_list_append (cur_var_info->appearances,
+ new_iter);
+ }
+
+ g_ptr_array_unref (cur_var_positions);
+ iter = g_list_next (iter);
+ iter2 = g_list_next (iter2);
+
+ priv->editing_info->snippet_vars_info = g_list_append (priv->editing_info->snippet_vars_info,
+ cur_var_info);
+
+ }
+ g_list_free (relative_positions);
+ g_list_free (variables_length);
+
+ /* Sort the list with appearances so the user will edit the ones that appear first
+ when the editing starts. */
+ priv->editing_info->snippet_vars_info =
+ g_list_sort (priv->editing_info->snippet_vars_info, sort_variables);
+
+ priv->editing_info->cur_var = g_list_first (priv->editing_info->snippet_vars_info);
+ focus_on_next_snippet_variable (snippets_interaction);
+
+}
+
+static void
+stop_snippet_editing_session (SnippetsInteraction *snippets_interaction)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+
+ if (!priv->editing)
+ return;
+
+ /* Mark the editing session */
+ priv->editing = FALSE;
+ priv->changing_values_blocker = FALSE;
+ priv->selection_set_blocker = FALSE;
+ if (IANJUTA_IS_ITERABLE (priv->cur_sel_start_iter))
+ g_object_unref (priv->cur_sel_start_iter);
+ priv->cur_sel_start_iter = NULL;
+
+ /* Clear the previous editing info structure if needed */
+ delete_snippet_editing_info (snippets_interaction);
+
+}
+
+/* Public methods */
+
+SnippetsInteraction*
+snippets_interaction_new ()
+{
+ return ANJUTA_SNIPPETS_INTERACTION (g_object_new (snippets_interaction_get_type (), NULL));
+}
+
+void
+snippets_interaction_start (SnippetsInteraction *snippets_interaction,
+ AnjutaShell *shell)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ g_return_if_fail (ANJUTA_IS_SHELL (shell));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+
+ priv->shell = shell;
+ priv->cur_editor = NULL;
+
+}
+
+void
+snippets_interaction_destroy (SnippetsInteraction *snippets_interaction)
+{
+ /* TODO */
+}
+
+
+void
+snippets_interaction_insert_snippet (SnippetsInteraction *snippets_interaction,
+ SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ gchar *indent = NULL, *cur_line = NULL, *snippet_default_content = NULL;
+ IAnjutaIterable *line_begin = NULL, *cur_pos = NULL;
+ gint cur_line_no = -1, i = 0;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+
+ /* Check we have an editor loaded */
+ if (!IANJUTA_IS_EDITOR (priv->cur_editor))
+ return;
+
+ /* Get the current line */
+ cur_line_no = ianjuta_editor_get_lineno (priv->cur_editor, NULL);
+ line_begin = ianjuta_editor_get_line_begin_position (priv->cur_editor, cur_line_no, NULL);
+ cur_pos = ianjuta_editor_get_position (priv->cur_editor, NULL);
+ cur_line = ianjuta_editor_get_text (priv->cur_editor, line_begin, cur_pos, NULL);
+
+ /* Calculate the current indentation */
+ indent = g_strdup (cur_line);
+ while (cur_line[i] == ' ' || cur_line[i] == '\t')
+ i ++;
+ indent[i] = 0;
+
+ /* Get the default content of the snippet */
+ snippet_default_content = snippet_get_default_content (snippet,
+ G_OBJECT (snippets_db),
+ indent);
+ g_return_if_fail (snippet_default_content != NULL);
+
+ /* Insert the default content into the editor */
+ ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT (priv->cur_editor), NULL);
+ ianjuta_editor_insert (priv->cur_editor,
+ cur_pos,
+ snippet_default_content,
+ -1,
+ NULL);
+ ianjuta_document_end_undo_action (IANJUTA_DOCUMENT (priv->cur_editor), NULL);
+ ianjuta_document_grab_focus (IANJUTA_DOCUMENT (priv->cur_editor), NULL);
+
+ priv->cur_snippet = snippet;
+ start_snippet_editing_session (snippets_interaction,
+ cur_pos,
+ strlen (snippet_default_content));
+
+ g_free (indent);
+ g_free (snippet_default_content);
+ g_object_unref (line_begin);
+ g_object_unref (cur_pos);
+
+}
+
+void
+snippets_interaction_trigger_insert_request (SnippetsInteraction *snippets_interaction,
+ SnippetsDB *snippets_db)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+ IAnjutaIterable *rewind_iter = NULL, *cur_pos = NULL;
+ gchar *trigger = NULL, cur_char = 0;
+ gboolean reached_start = FALSE;
+ AnjutaSnippet *snippet = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+ g_return_if_fail (ANJUTA_IS_SHELL (priv->shell));
+
+ /* Verify we have an editor */
+ if (!IANJUTA_IS_EDITOR (priv->cur_editor))
+ return;
+
+ /* We first try to focus on the next variable in case we are editing a snippet */
+ if (focus_on_next_snippet_variable (snippets_interaction))
+ return;
+
+ /* Initialize the iterators */
+ cur_pos = ianjuta_editor_get_position (priv->cur_editor, NULL);
+ rewind_iter = ianjuta_iterable_clone (cur_pos, NULL);
+
+ /* If we are inside a word we can't insert a snippet */
+ cur_char = char_at_iterator (priv->cur_editor, cur_pos);
+ if (IN_WORD (cur_char))
+ return;
+
+ /* If we can't decrement the cur_pos iterator, then we are at the start of the document,
+ so the trigger key is NULL */
+ if (!ianjuta_iterable_previous (rewind_iter, NULL))
+ return;
+ cur_char = char_at_iterator (priv->cur_editor, rewind_iter);
+
+ /* We iterate until we get to the start of the word or the start of the document */
+ while (IN_WORD (cur_char))
+ {
+ if (!ianjuta_iterable_previous (rewind_iter, NULL))
+ {
+ reached_start = TRUE;
+ break;
+ }
+
+ cur_char = char_at_iterator (priv->cur_editor, rewind_iter);
+ }
+
+ /* If we didn't reached the start of the document, we move the iterator forward one
+ step so we don't delete one extra char. */
+ if (!reached_start)
+ ianjuta_iterable_next (rewind_iter, NULL);
+
+ /* We compute the trigger-key */
+ trigger = ianjuta_editor_get_text (priv->cur_editor, rewind_iter, cur_pos, NULL);
+
+ /* If there is a snippet for our trigger-key we delete the trigger from the editor
+ and insert the snippet. */
+ snippet = snippets_db_get_snippet (snippets_db, trigger, NULL);
+
+ if (ANJUTA_IS_SNIPPET (snippet))
+ {
+ ianjuta_editor_erase (priv->cur_editor, rewind_iter, cur_pos, NULL);
+ snippets_interaction_insert_snippet (snippets_interaction, snippets_db, snippet);
+ }
+
+ g_free (trigger);
+ g_object_unref (rewind_iter);
+ g_object_unref (cur_pos);
+
+}
+
+void
+snippets_interaction_set_editor (SnippetsInteraction *snippets_interaction,
+ IAnjutaEditor *editor)
+{
+ SnippetsInteractionPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction));
+ priv = ANJUTA_SNIPPETS_INTERACTION_GET_PRIVATE (snippets_interaction);
+
+ /* Disconnect the handlers of the old editor */
+ if (IANJUTA_IS_EDITOR (priv->cur_editor))
+ {
+ g_signal_handler_disconnect (priv->cur_editor, priv->changed_handler_id);
+ g_signal_handler_disconnect (priv->cur_editor, priv->cursor_moved_handler_id);
+ }
+
+ /* Connect the handlers for the new editor */
+ if (IANJUTA_IS_EDITOR (editor))
+ {
+ priv->cur_editor = editor;
+
+ priv->changed_handler_id = g_signal_connect (G_OBJECT (priv->cur_editor),
+ "changed",
+ G_CALLBACK (on_cur_editor_changed),
+ snippets_interaction);
+ priv->cursor_moved_handler_id = g_signal_connect (G_OBJECT (priv->cur_editor),
+ "cursor-moved",
+ G_CALLBACK (on_cur_editor_cursor_moved),
+ snippets_interaction);
+ }
+ else
+ {
+ priv->cur_editor = NULL;
+ }
+
+ /* We end our editing session (if we had one) */
+ stop_snippet_editing_session (snippets_interaction);
+
+}
diff --git a/plugins/snippets-manager/snippets-interaction-interpreter.h b/plugins/snippets-manager/snippets-interaction-interpreter.h
new file mode 100644
index 0000000..104e00c
--- /dev/null
+++ b/plugins/snippets-manager/snippets-interaction-interpreter.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-interaction-interpreter.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_INTERACTION_H__
+#define __SNIPPETS_INTERACTION_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "snippet.h"
+#include "snippets-db.h"
+#include <libanjuta/anjuta-shell.h>
+#include <libanjuta/interfaces/ianjuta-editor.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SnippetsInteraction SnippetsInteraction;
+typedef struct _SnippetsInteractionPrivate SnippetsInteractionPrivate;
+typedef struct _SnippetsInteractionClass SnippetsInteractionClass;
+
+#define ANJUTA_TYPE_SNIPPETS_INTERACTION (snippets_interaction_get_type ())
+#define ANJUTA_SNIPPETS_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPETS_INTERACTION, SnippetsInteraction))
+#define ANJUTA_SNIPPETS_INTERACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPETS_INTERACTION, SnippetsInteractionClass))
+#define ANJUTA_IS_SNIPPETS_INTERACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPETS_INTERACTION))
+#define ANJUTA_IS_SNIPPETS_INTERACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPETS_INTERACTION))
+
+struct _SnippetsInteraction
+{
+ GObject object;
+
+};
+
+struct _SnippetsInteractionClass
+{
+ GObjectClass klass;
+
+};
+
+GType snippets_interaction_get_type (void) G_GNUC_CONST;
+SnippetsInteraction* snippets_interaction_new (void);
+
+void snippets_interaction_start (SnippetsInteraction *snippets_interaction,
+ AnjutaShell *shell);
+void snippets_interaction_destroy (SnippetsInteraction *snippets_interaction);
+void snippets_interaction_insert_snippet (SnippetsInteraction *snippets_interaction,
+ SnippetsDB *snippets_db,
+ AnjutaSnippet *snippet);
+void snippets_interaction_trigger_insert_request (SnippetsInteraction *snippets_interaction,
+ SnippetsDB *snippets_db);
+void snippets_interaction_set_editor (SnippetsInteraction *snippets_interaction,
+ IAnjutaEditor *editor);
+
+G_END_DECLS
+
+#endif /* __SNIPPETS_INTERACTION_H__ */
diff --git a/plugins/snippets-manager/snippets-manager-preferences.ui b/plugins/snippets-manager/snippets-manager-preferences.ui
new file mode 100644
index 0000000..227810f
--- /dev/null
+++ b/plugins/snippets-manager/snippets-manager-preferences.ui
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkWindow" id="_not_used">
+ <property name="title" translatable="yes">window1</property>
+ <child>
+ <object class="GtkFrame" id="snippets_preferences_root">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="global_vars_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="search_column">0</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="height_request">28</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="add_var_button">
+ <property name="width_request">48</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-add</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete_var_button">
+ <property name="width_request">48</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-delete</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="global_vars_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Anjuta variables</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plugins/snippets-manager/snippets-manager-ui.xml b/plugins/snippets-manager/snippets-manager-ui.xml
new file mode 100644
index 0000000..1487aac
--- /dev/null
+++ b/plugins/snippets-manager/snippets-manager-ui.xml
@@ -0,0 +1,16 @@
+<!--*- xml -*-->
+<ui>
+ <menubar name="MenuMain">
+ <menu name="MenuEdit" action="ActionMenuEdit">
+ <placeholder name="PlaceholderInsertMenus">
+ <menu name="SnippetsManagerMenu" action="ActionMenuEditSnippetsManager">
+ <menuitem name="TriggerInsertSnippetMenuItem" action="ActionEditTriggerInsert" />
+ <menuitem name="AutoCompleteInsertSnippetMenuItem" action="ActionEditAutoCompleteInsert" />
+ <separator />
+ <menuitem name="ImportSnippetMenuItem" action="ActionEditImportSnippets" />
+ <menuitem name="ExportSnippetMenuItem" action="ActionEditExportSnippets" />
+ </menu>
+ </placeholder>
+ </menu>
+ </menubar>
+</ui>
diff --git a/plugins/snippets-manager/snippets-provider.c b/plugins/snippets-manager/snippets-provider.c
new file mode 100644
index 0000000..09fd57a
--- /dev/null
+++ b/plugins/snippets-manager/snippets-provider.c
@@ -0,0 +1,739 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-provider.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include <libanjuta/interfaces/ianjuta-provider.h>
+#include <libanjuta/interfaces/ianjuta-editor.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-document.h>
+#include <libanjuta/interfaces/ianjuta-language.h>
+#include <libanjuta/interfaces/ianjuta-indicable.h>
+#include "snippets-provider.h"
+#include "snippet.h"
+#include "snippets-group.h"
+
+
+#define TRIGGER_RELEVANCE 1000
+#define NAME_RELEVANCE 1000
+#define FIRST_KEYWORD_RELEVANCE 100
+#define KEYWORD_RELEVANCE_DEC 5
+#define START_MATCH_BONUS 1.7
+
+#define RELEVANCE(search_str_len, key_len) ((gdouble)(search_str_len)/(key_len - search_str_len + 1))
+
+#define IS_SEPARATOR(c) ((c == ' ') || (c == '\n') || (c == '\t'))
+
+#define ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
+ ANJUTA_TYPE_SNIPPETS_PROVIDER,\
+ SnippetsProviderPrivate))
+
+struct _SnippetsProviderPrivate
+{
+ SnippetsDB *snippets_db;
+ SnippetsInteraction *snippets_interaction;
+
+ IAnjutaEditorAssist *editor_assist;
+
+ gboolean request;
+ gboolean listening;
+ IAnjutaIterable *start_iter;
+ GList *suggestions_list;
+
+};
+
+typedef struct _SnippetEntry
+{
+ AnjutaSnippet *snippet;
+ gdouble relevance;
+} SnippetEntry;
+
+/* IAnjutaProvider methods declaration */
+
+static void snippets_provider_iface_init (IAnjutaProviderIface* iface);
+static void snippets_provider_populate (IAnjutaProvider *self,
+ IAnjutaIterable *cursor,
+ GError **error);
+static IAnjutaIterable* snippets_provider_get_start_iter (IAnjutaProvider *self,
+ GError **error);
+static void snippets_provider_activate (IAnjutaProvider *self,
+ IAnjutaIterable *iter,
+ gpointer data,
+ GError **error);
+static const gchar* snippets_provider_get_name (IAnjutaProvider *self,
+ GError **error);
+
+G_DEFINE_TYPE_WITH_CODE (SnippetsProvider,
+ snippets_provider,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IANJUTA_TYPE_PROVIDER, snippets_provider_iface_init))
+
+
+static void
+snippets_provider_init (SnippetsProvider *obj)
+{
+ SnippetsProviderPrivate *priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (obj);
+
+ priv->snippets_db = NULL;
+ priv->snippets_interaction = NULL;
+
+ priv->editor_assist = NULL;
+
+ priv->request = FALSE;
+ priv->listening = FALSE;
+ priv->start_iter = NULL;
+ priv->suggestions_list = NULL;
+
+ obj->anjuta_shell = NULL;
+
+}
+
+static void
+snippets_provider_class_init (SnippetsProviderClass *klass)
+{
+ snippets_provider_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (SnippetsProviderPrivate));
+
+}
+
+static void
+snippets_provider_iface_init (IAnjutaProviderIface* iface)
+{
+ iface->populate = snippets_provider_populate;
+ iface->get_start_iter = snippets_provider_get_start_iter;
+ iface->activate = snippets_provider_activate;
+ iface->get_name = snippets_provider_get_name;
+
+}
+
+/* Private methods */
+
+static gdouble
+get_relevance_for_word (const gchar *search_word,
+ const gchar *key_word)
+{
+ gint i = 0, search_word_len = 0, key_word_len = 0;
+ gdouble relevance = 0.0, cur_relevance = 0.0;
+
+ search_word_len = strlen (search_word);
+ key_word_len = strlen (key_word);
+
+ for (i = 0; i < key_word_len - search_word_len + 1; i ++)
+ {
+ if (g_str_has_prefix (key_word + i, search_word))
+ {
+ cur_relevance = RELEVANCE (search_word_len, key_word_len);
+ if (i == 0)
+ cur_relevance *= START_MATCH_BONUS;
+
+ relevance += cur_relevance;
+ }
+ }
+
+ return relevance;
+}
+
+static gdouble
+get_relevance_for_snippet (AnjutaSnippet *snippet,
+ GList *words_list)
+{
+ gchar *cur_word = NULL, *name = NULL, *trigger = NULL, *cur_keyword = NULL;
+ gdouble relevance = 0.0, cur_relevance = 0.0, cur_keyword_relevance = 0.0;
+ GList *iter = NULL, *keywords = NULL, *keywords_down = NULL, *iter2 = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), 0.0);
+
+ /* If the user hasn't typed anything we just consider all snippets relevant */
+ if (words_list == NULL)
+ return 1.0;
+
+ /* Get the snippet data */
+ trigger = g_utf8_strdown (snippet_get_trigger_key (snippet), -1);
+ name = g_utf8_strdown (snippet_get_name (snippet), -1);
+ keywords = snippet_get_keywords_list (snippet);
+ for (iter = g_list_first (keywords); iter != NULL; iter = g_list_next (iter))
+ {
+ keywords_down = g_list_append (keywords_down,
+ g_utf8_strdown ((gchar *)iter->data, -1));
+ }
+ g_list_free (keywords);
+
+ /* We iterate over all the words */
+ for (iter = g_list_first (words_list); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_word = (gchar *)iter->data;
+
+ /* Check the trigger-key */
+ cur_relevance = get_relevance_for_word (cur_word, trigger);
+ cur_relevance *= TRIGGER_RELEVANCE;
+ relevance += cur_relevance;
+
+ /* Check the name */
+ cur_relevance = get_relevance_for_word (cur_word, name);
+ cur_relevance *= NAME_RELEVANCE;
+ relevance += cur_relevance;
+
+ /* Check each keyword */
+ cur_keyword_relevance = FIRST_KEYWORD_RELEVANCE;
+ for (iter2 = g_list_first (keywords_down); iter2 != NULL; iter2 = g_list_next (iter2))
+ {
+ /* If we have too many keywords */
+ if (cur_keyword_relevance < 0.0)
+ break;
+
+ cur_keyword = (gchar *)iter2->data;
+
+ cur_relevance = get_relevance_for_word (cur_word, cur_keyword);
+ cur_relevance *= cur_keyword_relevance;
+ relevance += cur_relevance;
+
+ cur_keyword_relevance -= KEYWORD_RELEVANCE_DEC;
+ }
+ }
+
+ return relevance;
+}
+
+static gint
+snippets_relevance_sort_func (gconstpointer a,
+ gconstpointer b)
+{
+ IAnjutaEditorAssistProposal *proposal1 = (IAnjutaEditorAssistProposal *)a,
+ *proposal2 = (IAnjutaEditorAssistProposal *)b;
+ SnippetEntry *entry1 = (SnippetEntry *)proposal1->data,
+ *entry2 = (SnippetEntry *)proposal2->data;
+
+ if (entry1->relevance - entry2->relevance == 0.0)
+ return g_strcmp0 (snippet_get_name (entry1->snippet),
+ snippet_get_name (entry2->snippet));
+
+ return (gint)(entry2->relevance - entry1->relevance);
+}
+
+static IAnjutaEditorAssistProposal*
+get_proposal_for_snippet (AnjutaSnippet *snippet,
+ SnippetsDB *snippets_db,
+ GList *words_list)
+{
+ IAnjutaEditorAssistProposal *proposal = NULL;
+ SnippetEntry *entry = NULL;
+ gchar *name_with_trigger = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), NULL);
+
+ /* Make a new proposal object */
+ proposal = g_new0 (IAnjutaEditorAssistProposal, 1);
+ entry = g_new0 (SnippetEntry, 1);
+
+ /* Fill the markup field */
+ name_with_trigger = g_strconcat (snippet_get_name (snippet), " (<b>",
+ snippet_get_trigger_key (snippet), "</b>)",
+ NULL);
+ proposal->markup = name_with_trigger;
+
+#if 0
+ /* Fill the info field */
+ proposal->info = snippet_get_default_content (snippet, G_OBJECT (snippets_db), "");
+#endif
+
+ /* Fill the data field */
+ entry->snippet = snippet;
+ entry->relevance = get_relevance_for_snippet (snippet, words_list);
+ proposal->data = entry;
+
+ return proposal;
+}
+
+static void
+clear_suggestions_list (SnippetsProvider *snippets_provider)
+{
+ SnippetsProviderPrivate *priv = NULL;
+ GList *iter = NULL;
+ IAnjutaEditorAssistProposal *cur_proposal = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+
+ for (iter = g_list_first (priv->suggestions_list); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_proposal = (IAnjutaEditorAssistProposal *)iter->data;
+
+ g_free (cur_proposal->markup);
+ g_free (cur_proposal->data);
+ g_free (cur_proposal);
+ }
+ g_list_free (priv->suggestions_list);
+ priv->suggestions_list = NULL;
+
+}
+
+static const gchar *
+get_current_editor_language (SnippetsProvider *snippets_provider)
+{
+
+ IAnjutaDocumentManager *docman = NULL;
+ IAnjutaDocument *doc = NULL;
+ IAnjutaLanguage *ilanguage = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider), NULL);
+ g_return_val_if_fail (ANJUTA_IS_SHELL (snippets_provider->anjuta_shell), NULL);
+
+ /* Get the document manager and assert it */
+ docman = anjuta_shell_get_interface (snippets_provider->anjuta_shell,
+ IAnjutaDocumentManager,
+ NULL);
+ g_return_val_if_fail (IANJUTA_IS_DOCUMENT_MANAGER (docman), NULL);
+
+ /* Get the language manager and assert it */
+ ilanguage = anjuta_shell_get_interface (snippets_provider->anjuta_shell,
+ IAnjutaLanguage,
+ NULL);
+ g_return_val_if_fail (IANJUTA_IS_LANGUAGE (ilanguage), NULL);
+
+ /* Get the current editor and assert it */
+ doc = ianjuta_document_manager_get_current_document (docman, NULL);
+ g_return_val_if_fail (IANJUTA_IS_EDITOR (doc), NULL);
+
+ return ianjuta_language_get_name_from_editor (ilanguage,
+ IANJUTA_EDITOR_LANGUAGE (doc),
+ NULL);
+}
+
+static void
+stop_listening (SnippetsProvider *snippets_provider)
+{
+ SnippetsProviderPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+
+ if (IANJUTA_IS_ITERABLE (priv->start_iter))
+ g_object_unref (priv->start_iter);
+ priv->start_iter = NULL;
+
+ priv->request = FALSE;
+ priv->listening = FALSE;
+
+ clear_suggestions_list (snippets_provider);
+
+}
+
+
+static void
+build_suggestions_list (SnippetsProvider *snippets_provider,
+ IAnjutaIterable *cur_cursor_position)
+{
+ SnippetsProviderPrivate *priv = NULL;
+ GtkTreeIter iter, iter2;
+ GObject *cur_object = NULL;
+ IAnjutaEditorAssistProposal *cur_proposal = NULL;
+ SnippetEntry *cur_entry = NULL;
+ gchar *search_string = NULL, **words = NULL;
+ gboolean show_all_languages = FALSE;
+ const gchar *language = NULL;
+ gint i = 0;
+ GList *words_list = NULL, *l_iter = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ g_return_if_fail (IANJUTA_IS_ITERABLE (cur_cursor_position));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+ g_return_if_fail (IANJUTA_IS_ITERABLE (cur_cursor_position));
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_DB (priv->snippets_db));
+ g_return_if_fail (priv->suggestions_list == NULL);
+
+ if (ianjuta_iterable_diff (priv->start_iter, cur_cursor_position, NULL) < 0)
+ {
+ stop_listening (snippets_provider);
+ return;
+ }
+
+ /* Get the language of the current editor */
+ language = get_current_editor_language (snippets_provider);
+ if (!language)
+ show_all_languages = TRUE;
+
+ /* Get the current searching string */
+ search_string = ianjuta_editor_get_text (IANJUTA_EDITOR (priv->editor_assist),
+ priv->start_iter,
+ cur_cursor_position,
+ NULL);
+ if (search_string == NULL)
+ search_string = g_strdup ("");
+
+ /* Split the search string into words and build the words list with non empty
+ words */
+ words = g_strsplit (search_string, " ", 0);
+ while (words[i])
+ {
+ if (g_strcmp0 (words[i], ""))
+ words_list = g_list_append (words_list, g_utf8_strdown (words[i], -1));
+
+ i ++;
+ }
+ g_strfreev (words);
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->snippets_db), &iter))
+ return;
+
+ do
+ {
+ /* Get the children for the current snippets group */
+ if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (priv->snippets_db), &iter2, &iter))
+ continue;
+
+ /* Iterate over the snippets */
+ do
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->snippets_db), &iter2,
+ SNIPPETS_DB_MODEL_COL_CUR_OBJECT, &cur_object,
+ -1);
+ g_object_unref (cur_object);
+
+ if (!ANJUTA_IS_SNIPPET (cur_object))
+ continue;
+
+ /* If the current snippet isn't meant for the current editor language, we ignore it */
+ if (!show_all_languages && !snippet_has_language (ANJUTA_SNIPPET (cur_object), language))
+ continue;
+
+ /* Build a proposal for the current snippet based on the words the user typed */
+ cur_proposal = get_proposal_for_snippet (ANJUTA_SNIPPET (cur_object),
+ priv->snippets_db,
+ words_list);
+
+ /* If the snippet isn't relevant for the typed text, we neglect this proposal */
+ cur_entry = (SnippetEntry *)cur_proposal->data;
+ if (cur_entry->relevance == 0.0)
+ {
+ g_free (cur_entry);
+ g_free (cur_proposal->markup);
+ g_free (cur_proposal);
+
+ continue;
+ }
+
+ /* We add the proposal to the suggestions list */
+ priv->suggestions_list = g_list_insert_sorted (priv->suggestions_list,
+ cur_proposal,
+ snippets_relevance_sort_func);
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->snippets_db), &iter2));
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->snippets_db), &iter));
+
+ /* Free the data */
+ g_free (search_string);
+ for (l_iter = g_list_first (words_list); l_iter != NULL; l_iter = g_list_next (l_iter))
+ g_free (l_iter->data);
+ g_list_free (words_list);
+
+}
+
+static gchar
+char_before_iterator (IAnjutaEditor *editor,
+ IAnjutaIterable *iter)
+{
+ IAnjutaIterable *prev = NULL;
+ gchar *text = NULL, returned_char = 0;
+
+ /* Assertions */
+ g_return_val_if_fail (IANJUTA_IS_EDITOR (editor), 0);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (iter), 0);
+
+ prev = ianjuta_iterable_clone (iter, NULL);
+ ianjuta_iterable_previous (prev, NULL);
+
+ text = ianjuta_editor_get_text (editor, prev, iter, NULL);
+ if (text == NULL)
+ return 0;
+
+ returned_char = text[0];
+ g_free (text);
+ g_object_unref (prev);
+
+ return returned_char;
+}
+
+static IAnjutaIterable *
+get_start_iter_for_cursor (IAnjutaEditor *editor,
+ IAnjutaIterable *cursor)
+{
+ IAnjutaIterable *iter = NULL;
+ gchar cur_char;
+
+ /* Assertions */
+ g_return_val_if_fail (IANJUTA_IS_EDITOR (editor), NULL);
+ g_return_val_if_fail (IANJUTA_IS_ITERABLE (cursor), NULL);
+
+ /* Go backwards in the text until we get to the start or find a separator */
+ iter = ianjuta_iterable_clone (cursor, NULL);
+ cur_char = char_before_iterator (editor, iter);
+ while (cur_char && !IS_SEPARATOR (cur_char))
+ {
+ if (!ianjuta_iterable_previous (iter, NULL))
+ break;
+
+ cur_char = char_before_iterator (editor, iter);
+ }
+
+ return iter;
+}
+
+/* Public methods */
+
+SnippetsProvider*
+snippets_provider_new (SnippetsDB *snippets_db,
+ SnippetsInteraction *snippets_interaction)
+{
+ SnippetsProvider *snippets_provider = NULL;
+ SnippetsProviderPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), NULL);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_INTERACTION (snippets_interaction), NULL);
+
+ snippets_provider = ANJUTA_SNIPPETS_PROVIDER (g_object_new (snippets_provider_get_type (), NULL));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+
+ priv->snippets_db = snippets_db;
+ priv->snippets_interaction = snippets_interaction;
+
+ return snippets_provider;
+}
+
+
+void
+snippets_provider_load (SnippetsProvider *snippets_provider,
+ IAnjutaEditorAssist *editor_assist)
+{
+ SnippetsProviderPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ g_return_if_fail (IANJUTA_IS_EDITOR_ASSIST (editor_assist));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+ /* Should be removed with unload */
+ g_return_if_fail (!IANJUTA_IS_EDITOR_ASSIST (priv->editor_assist));
+
+ /* Add the snippets_provider to the editor assist */
+ ianjuta_editor_assist_add (editor_assist,
+ IANJUTA_PROVIDER (snippets_provider),
+ NULL);
+ priv->editor_assist = editor_assist;
+
+ priv->request = FALSE;
+ priv->listening = FALSE;
+
+}
+
+void
+snippets_provider_unload (SnippetsProvider *snippets_provider)
+{
+ SnippetsProviderPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+
+ /* If we don't have anything to unload, we just return. This isn't an assertion
+ because it's possible to call this function if the previous document wasn't
+ an editor. */
+ if (!IANJUTA_IS_EDITOR_ASSIST (priv->editor_assist))
+ return;
+
+ /* Remove the snippets_provider from the editor assist and mark as NULL the one
+ saved internal. */
+ ianjuta_editor_assist_remove (priv->editor_assist,
+ IANJUTA_PROVIDER (snippets_provider),
+ NULL);
+ priv->editor_assist = NULL;
+
+ /* Stop listening if necessary */
+ stop_listening (snippets_provider);
+
+}
+
+void
+snippets_provider_request (SnippetsProvider *snippets_provider)
+{
+ SnippetsProviderPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+ g_return_if_fail (ANJUTA_IS_SHELL (snippets_provider->anjuta_shell));
+
+ /* If we don't have an editor assist loaded we just return. */
+ if (!IANJUTA_IS_EDITOR_ASSIST (priv->editor_assist))
+ return;
+
+ /* We just made the request and should listen with the populate method. */
+ priv->request = TRUE;
+ priv->listening = TRUE;
+
+ /* Clear the iter if need */
+ if (IANJUTA_IS_ITERABLE (priv->start_iter))
+ g_object_unref (priv->start_iter);
+ priv->start_iter = NULL;
+
+ /* Show the auto-complete widget */
+ ianjuta_editor_assist_invoke (priv->editor_assist,
+ IANJUTA_PROVIDER (snippets_provider),
+ NULL);
+
+}
+
+
+/* IAnjutaProvider methods declarations */
+
+static void
+snippets_provider_populate (IAnjutaProvider *self,
+ IAnjutaIterable *cursor,
+ GError **error)
+{
+ SnippetsProviderPrivate *priv = NULL;
+ SnippetsProvider *snippets_provider = NULL;
+
+ /* Assertions */
+ snippets_provider = ANJUTA_SNIPPETS_PROVIDER (self);
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (snippets_provider));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (snippets_provider);
+ g_return_if_fail (ANJUTA_IS_SHELL (snippets_provider->anjuta_shell));
+
+ /* If we aren't listening */
+ if (!priv->listening)
+ {
+ ianjuta_editor_assist_proposals (priv->editor_assist, self, NULL, TRUE, NULL);
+ return;
+ }
+
+ /* If the request just happened */
+ if (priv->request)
+ {
+ /* Save the new cursor as the starting one */
+/* priv->start_iter = get_start_iter_for_cursor (IANJUTA_EDITOR (priv->editor_assist),*/
+/* cursor);*/
+ /* TODO - seems to feel better if it starts at the current cursor position.
+ Keeping the old method also if it will be decided to use that method.
+ Note: get_start_iter_for_cursor goes back in the text until it finds a
+ separator. */
+ priv->start_iter = ianjuta_iterable_clone (cursor, NULL);
+ priv->request = FALSE;
+ }
+
+ clear_suggestions_list (snippets_provider);
+ build_suggestions_list (snippets_provider, cursor);
+
+ /* Clear the previous indicator */
+ if (IANJUTA_IS_INDICABLE (priv->editor_assist))
+ ianjuta_indicable_clear (IANJUTA_INDICABLE (priv->editor_assist), NULL);
+
+ if (priv->suggestions_list == NULL)
+ {
+ stop_listening (snippets_provider);
+ ianjuta_editor_assist_proposals (priv->editor_assist, self, NULL, TRUE, NULL);
+ return;
+ }
+
+ /* Highlight the search string */
+ if (IANJUTA_IS_INDICABLE (priv->editor_assist))
+ {
+ ianjuta_indicable_set (IANJUTA_INDICABLE (priv->editor_assist),
+ priv->start_iter,
+ cursor,
+ IANJUTA_INDICABLE_IMPORTANT,
+ NULL);
+ }
+
+ ianjuta_editor_assist_proposals (priv->editor_assist,
+ self,
+ priv->suggestions_list,
+ TRUE, NULL);
+
+}
+
+static IAnjutaIterable*
+snippets_provider_get_start_iter (IAnjutaProvider *self,
+ GError **error)
+{
+ SnippetsProviderPrivate *priv = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (self), NULL);
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (self);
+
+ return priv->start_iter;
+}
+
+static void
+snippets_provider_activate (IAnjutaProvider *self,
+ IAnjutaIterable *iter,
+ gpointer data,
+ GError **error)
+{
+ SnippetsProviderPrivate *priv = NULL;
+ AnjutaSnippet *snippet = NULL;
+
+ /* Assertions */
+ g_return_if_fail (ANJUTA_IS_SNIPPETS_PROVIDER (self));
+ g_return_if_fail (IANJUTA_IS_ITERABLE (iter));
+ priv = ANJUTA_SNIPPETS_PROVIDER_GET_PRIVATE (self);
+ g_return_if_fail (IANJUTA_IS_ITERABLE (priv->start_iter));
+ g_return_if_fail (IANJUTA_IS_EDITOR (priv->editor_assist));
+
+ /* Get the Snippet and assert it */
+ snippet = ((SnippetEntry *)data)->snippet;
+ g_return_if_fail (ANJUTA_IS_SNIPPET (snippet));
+
+ /* Erase the text */
+ ianjuta_editor_erase (IANJUTA_EDITOR (priv->editor_assist),
+ priv->start_iter,
+ iter,
+ NULL);
+
+ /* Set the cursor at the start iter */
+ ianjuta_editor_goto_position (IANJUTA_EDITOR (priv->editor_assist),
+ priv->start_iter,
+ NULL);
+
+ /* Clear the indicator */
+ if (IANJUTA_IS_INDICABLE (priv->editor_assist))
+ ianjuta_indicable_clear (IANJUTA_INDICABLE (priv->editor_assist), NULL);
+
+ /* Insert the snippet */
+ snippets_interaction_insert_snippet (priv->snippets_interaction,
+ priv->snippets_db,
+ snippet);
+
+ stop_listening (ANJUTA_SNIPPETS_PROVIDER (self));
+}
+
+static const gchar*
+snippets_provider_get_name (IAnjutaProvider *self,
+ GError **error)
+{
+ return _("Code Snippets");
+}
+
diff --git a/plugins/snippets-manager/snippets-provider.h b/plugins/snippets-manager/snippets-provider.h
new file mode 100644
index 0000000..678741a
--- /dev/null
+++ b/plugins/snippets-manager/snippets-provider.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-provider.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SNIPPETS_PROVIDER_H__
+#define __SNIPPETS_PROVIDER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libanjuta/anjuta-shell.h>
+#include <libanjuta/interfaces/ianjuta-editor-assist.h>
+#include "snippets-db.h"
+#include "snippets-interaction-interpreter.h"
+
+G_BEGIN_DECLS
+
+typedef struct _SnippetsProvider SnippetsProvider;
+typedef struct _SnippetsProviderPrivate SnippetsProviderPrivate;
+typedef struct _SnippetsProviderClass SnippetsProviderClass;
+
+#define ANJUTA_TYPE_SNIPPETS_PROVIDER (snippets_provider_get_type ())
+#define ANJUTA_SNIPPETS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_SNIPPETS_PROVIDER, SnippetsProvider))
+#define ANJUTA_SNIPPETS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_SNIPPETS_PROVIDER, SnippetsProviderClass))
+#define ANJUTA_IS_SNIPPETS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_SNIPPETS_PROVIDER))
+#define ANJUTA_IS_SNIPPETS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_SNIPPETS_PROVIDER))
+
+struct _SnippetsProvider
+{
+ GObject parent;
+
+ AnjutaShell *anjuta_shell;
+
+};
+
+struct _SnippetsProviderClass
+{
+ GObjectClass parent_class;
+
+};
+
+
+GType snippets_provider_get_type (void) G_GNUC_CONST;
+SnippetsProvider* snippets_provider_new (SnippetsDB *snippets_db,
+ SnippetsInteraction *snippets_interaction);
+void snippets_provider_load (SnippetsProvider *snippets_provider,
+ IAnjutaEditorAssist *editor_assist);
+void snippets_provider_unload (SnippetsProvider *snippets_provider);
+void snippets_provider_request (SnippetsProvider *snippets_provider);
+
+G_END_DECLS
+
+#endif /* __SNIPPETS_PROVIDER_H__ */
+
diff --git a/plugins/snippets-manager/snippets-xml-parser.c b/plugins/snippets-manager/snippets-xml-parser.c
new file mode 100644
index 0000000..ab9d473
--- /dev/null
+++ b/plugins/snippets-manager/snippets-xml-parser.c
@@ -0,0 +1,840 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-xml-parser.c
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include "snippets-xml-parser.h"
+#include "snippet.h"
+#include <libxml/parser.h>
+#include <libxml/xmlwriter.h>
+#include <string.h>
+
+#define NATIVE_XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+
+#define NATIVE_XML_ROOT "anjuta-snippets-packet"
+#define NATIVE_XML_GROUP_TAG "anjuta-snippets-group"
+#define NATIVE_XML_GROUP_NAME_TAG "name"
+#define NATIVE_XML_SNIPPETS_TAG "anjuta-snippets"
+#define NATIVE_XML_SNIPPET_TAG "anjuta-snippet"
+#define NATIVE_XML_LANGUAGES_TAG "languages"
+#define NATIVE_XML_VARIABLES_TAG "variables"
+#define NATIVE_XML_VARIABLE_TAG "variable"
+#define NATIVE_XML_CONTENT_TAG "snippet-content"
+#define NATIVE_XML_KEYWORDS_TAG "keywords"
+#define NATIVE_XML_NAME_PROP "name"
+#define NATIVE_XML_GLOBAL_PROP "is_global"
+#define NATIVE_XML_DEFAULT_PROP "default"
+#define NATIVE_XML_TRIGGER_PROP "trigger"
+#define NATIVE_XML_TRUE "true"
+#define NATIVE_XML_FALSE "false"
+
+#define GLOBAL_VARS_XML_ROOT "anjuta-global-variables"
+#define GLOBAL_VARS_XML_VAR_TAG "global-variable"
+#define GLOBAL_VARS_XML_NAME_PROP "name"
+#define GLOBAL_VARS_XML_COMMAND_PROP "is_command"
+#define GLOBAL_VARS_XML_TRUE "true"
+#define GLOBAL_VARS_XML_FALSE "false"
+
+#define CDATA_START "<![CDATA["
+#define CDATA_END "]]>"
+#define CDATA_MID "]]><![CDATA["
+#define IS_CDATA_END(text, i) (text[i - 1] == ']' && text[i] == ']' && text[i + 1] == '>')
+
+#define QUOTE_STR """
+
+
+static void
+write_simple_start_tag (GOutputStream *os,
+ const gchar *name)
+{
+ gchar *tag = g_strconcat ("<", name, ">\n", NULL);
+ g_output_stream_write (os, tag, strlen (tag), NULL, NULL);
+ g_free (tag);
+}
+
+static void
+write_simple_end_tag (GOutputStream *os,
+ const gchar *name)
+{
+ gchar *tag = g_strconcat ("</", name, ">\n", NULL);
+ g_output_stream_write (os, tag, strlen (tag), NULL, NULL);
+ g_free (tag);
+}
+
+static gchar*
+escape_text_cdata (const gchar *content)
+{
+ GString *formated_content = g_string_new (CDATA_START);
+ gint i = 0, content_len = 0;
+
+ content_len = strlen (content);
+ for (i = 0; i < content_len; i ++)
+ {
+ /* If we found the "]]>" string in the content we should escape it. */
+ if (i > 0 && IS_CDATA_END (content, i))
+ g_string_append (formated_content, CDATA_MID);
+
+ g_string_append_c (formated_content, content[i]);
+ }
+ g_string_append (formated_content, CDATA_END);
+
+ return g_string_free (formated_content, FALSE);
+
+}
+
+static gchar*
+escape_quotes (const gchar *text)
+{
+ GString *escaped_text = g_string_new ("");
+ gint i = 0, len = 0;
+
+ len = strlen (text);
+ for (i = 0; i < len; i += 1)
+ {
+ if (text[i] == '\"')
+ {
+ escaped_text = g_string_append (escaped_text, QUOTE_STR);
+ continue;
+ }
+
+ escaped_text = g_string_append_c (escaped_text, text[i]);
+ }
+
+ return g_string_free (escaped_text, FALSE);
+}
+
+static void
+write_start_end_tag_with_content (GOutputStream *os,
+ const gchar *tag_name,
+ const gchar *content)
+{
+ gchar *tag_with_content = NULL, *escaped_content = NULL;
+
+ escaped_content = escape_text_cdata (content);
+ tag_with_content = g_strconcat ("<", tag_name, ">", escaped_content, "</", tag_name, ">\n", NULL);
+
+ g_output_stream_write (os, tag_with_content, strlen (tag_with_content), NULL, NULL);
+ g_free (tag_with_content);
+ g_free (escaped_content);
+}
+
+static void
+write_start_end_tag_with_content_as_list (GOutputStream *os,
+ const gchar *tag_name,
+ GList *content_list)
+{
+ GList *iter = NULL;
+ GString *content = g_string_new ("");
+ gchar *cur_word = NULL;
+
+ for (iter = g_list_first (content_list); iter != NULL; iter = g_list_next (iter))
+ {
+ cur_word = (gchar *)iter->data;
+ g_string_append (content, cur_word);
+ g_string_append (content, " ");
+ }
+
+ write_start_end_tag_with_content (os, tag_name, content->str);
+ g_string_free (content, TRUE);
+
+}
+
+static void
+write_anjuta_snippet_tag (GOutputStream *os,
+ const gchar *trigger,
+ const gchar *name)
+{
+ gchar *tag = NULL, *escaped_name = NULL;
+
+ escaped_name = escape_quotes (name);
+ tag = g_strconcat ("<anjuta-snippet trigger=\"", trigger, "\" name=\"", escaped_name, "\">\n", NULL);
+ g_output_stream_write (os, tag, strlen (tag), NULL, NULL);
+ g_free (tag);
+ g_free (escaped_name);
+}
+
+static void
+write_variable_tag (GOutputStream *os,
+ const gchar *name,
+ const gchar *default_val,
+ gboolean is_global)
+{
+ const gchar *global_val = (is_global) ? NATIVE_XML_TRUE : NATIVE_XML_FALSE;
+ gchar *tag = NULL, *escaped_name = NULL, *escaped_default_val = NULL;
+
+ /* Escape the quotes */
+ escaped_name = escape_quotes (name);
+ escaped_default_val = escape_quotes (default_val);
+
+ /* Write the tag */
+ tag = g_strconcat ("<variable name=\"", escaped_name,
+ "\" default=\"", escaped_default_val,
+ "\" is_global=\"", global_val, "\" />\n", NULL);
+ g_output_stream_write (os, tag, strlen (tag), NULL, NULL);
+
+ /* Free the data */
+ g_free (tag);
+ g_free (escaped_name);
+ g_free (escaped_default_val);
+}
+
+static gboolean
+write_snippet (GOutputStream *os,
+ AnjutaSnippet *snippet)
+{
+ GList *iter = NULL, *iter2 = NULL, *iter3 = NULL, *keywords = NULL,
+ *vars_names = NULL, *vars_defaults = NULL, *vars_globals = NULL;
+ const gchar *content = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (os), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPET (snippet), FALSE);
+
+ write_anjuta_snippet_tag (os,
+ snippet_get_trigger_key (snippet),
+ snippet_get_name (snippet));
+
+ /* Write the languages */
+ write_start_end_tag_with_content_as_list (os,
+ NATIVE_XML_LANGUAGES_TAG,
+ (GList *)snippet_get_languages (snippet));
+
+ /* Write the variables */
+ write_simple_start_tag (os, NATIVE_XML_VARIABLES_TAG);
+ /* Write each variable */
+ vars_names = snippet_get_variable_names_list (snippet);
+ vars_defaults = snippet_get_variable_defaults_list (snippet);
+ vars_globals = snippet_get_variable_globals_list (snippet);
+ iter = g_list_first (vars_names);
+ iter2 = g_list_first (vars_defaults);
+ iter3 = g_list_first (vars_globals);
+ while (iter != NULL && iter2 != NULL && iter3 != NULL)
+ {
+ write_variable_tag (os,
+ (gchar *)iter->data,
+ (gchar *)iter2->data,
+ GPOINTER_TO_INT (iter3->data));
+
+ iter = g_list_next (iter);
+ iter2 = g_list_next (iter2);
+ iter3 = g_list_next (iter3);
+ }
+ g_list_free (vars_names);
+ g_list_free (vars_defaults);
+ g_list_free (vars_globals);
+ write_simple_end_tag (os, NATIVE_XML_VARIABLES_TAG);
+
+ /* Write the content */
+ content = snippet_get_content (snippet);
+ write_start_end_tag_with_content (os, NATIVE_XML_CONTENT_TAG, content);
+
+ /* Write the keywords */
+ keywords = snippet_get_keywords_list (snippet);
+ write_start_end_tag_with_content_as_list (os, NATIVE_XML_KEYWORDS_TAG, keywords);
+ g_list_free (keywords);
+ write_simple_end_tag (os, NATIVE_XML_SNIPPET_TAG);
+
+ return TRUE;
+}
+
+static gboolean
+write_snippets_group (GOutputStream *os,
+ AnjutaSnippetsGroup *snippets_group)
+{
+ GList *iter = NULL, *snippets = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (os), FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_GROUP (snippets_group), FALSE);
+
+ write_simple_start_tag (os, NATIVE_XML_GROUP_TAG);
+
+ /* Write the name tag */
+ write_start_end_tag_with_content (os,
+ NATIVE_XML_GROUP_NAME_TAG,
+ snippets_group_get_name (snippets_group));
+
+ write_simple_start_tag (os, NATIVE_XML_SNIPPETS_TAG);
+
+ /* Write the snippets */
+ snippets = snippets_group_get_snippets_list (snippets_group);
+ for (iter = g_list_first (snippets); iter != NULL; iter = g_list_next (iter))
+ {
+ if (!ANJUTA_IS_SNIPPET (iter->data))
+ continue;
+
+ write_snippet (os, ANJUTA_SNIPPET (iter->data));
+ }
+ write_simple_end_tag (os, NATIVE_XML_SNIPPETS_TAG);
+
+ write_simple_end_tag (os, NATIVE_XML_GROUP_TAG);
+
+ return TRUE;
+}
+
+static gboolean
+snippets_manager_save_native_xml_file (GList* snippets_groups,
+ const gchar *file_path)
+{
+ GFile *file = NULL;
+ GOutputStream *os = NULL;
+ GList *iter = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (file_path != NULL, FALSE);
+
+ /* Open the file */
+ file = g_file_new_for_path (file_path);
+ os = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL));
+ if (!G_IS_OUTPUT_STREAM (os))
+ {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ if (g_output_stream_write (os, NATIVE_XML_HEADER, strlen (NATIVE_XML_HEADER), NULL, NULL) < 0)
+ {
+ g_output_stream_close (os, NULL, NULL);
+ g_object_unref (os);
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ write_simple_start_tag (os, NATIVE_XML_ROOT);
+
+ for (iter = g_list_first (snippets_groups); iter != NULL; iter = g_list_next (iter))
+ {
+ if (!ANJUTA_IS_SNIPPETS_GROUP (iter->data))
+ continue;
+
+ write_snippets_group (os, ANJUTA_SNIPPETS_GROUP (iter->data));
+ }
+
+ write_simple_end_tag (os, NATIVE_XML_ROOT);
+
+ /* Close the file */
+ g_output_stream_close (os, NULL, NULL);
+ g_object_unref (os);
+ g_object_unref (file);
+
+ return TRUE;
+}
+
+static gboolean
+snippets_manager_save_gedit_xml_file (GList* snippets_group,
+ const gchar *file_path)
+{
+ /* TODO */
+ return FALSE;
+}
+
+static AnjutaSnippet*
+parse_snippet_node (xmlNodePtr snippet_node)
+{
+ AnjutaSnippet* snippet = NULL;
+ gchar *trigger_key = NULL, *snippet_name = NULL, *snippet_content = NULL, *cur_var_name = NULL,
+ *cur_var_default = NULL, *cur_var_global = NULL, *keywords_temp = NULL,
+ **keywords_temp_array = NULL, *keyword_temp = NULL, *languages_temp = NULL,
+ **languages_temp_array = NULL, *language_temp = NULL;
+ GList *variable_names = NULL, *variable_default_values = NULL,
+ *variable_globals = NULL, *keywords = NULL, *iter = NULL,
+ *snippet_languages = NULL;
+ xmlNodePtr cur_field_node = NULL, cur_variable_node = NULL;
+ gboolean cur_var_is_global = FALSE;
+ gint i = 0;
+
+ /* Assert that the snippet_node is indeed a anjuta-snippet tag */
+ g_return_val_if_fail (!g_strcmp0 ((gchar *)snippet_node->name, NATIVE_XML_SNIPPET_TAG), NULL);
+
+ /* Get the snippet name, trigger-key and language properties */
+ trigger_key = (gchar *)xmlGetProp (snippet_node, (const xmlChar *)NATIVE_XML_TRIGGER_PROP);
+ snippet_name = (gchar *)xmlGetProp (snippet_node, (const xmlChar *)NATIVE_XML_NAME_PROP);
+
+ /* Make sure we got all the above properties */
+ if (trigger_key == NULL || snippet_name == NULL)
+ {
+ g_free (trigger_key);
+ g_free (snippet_name);
+ return NULL;
+ }
+
+ /* Get the snippet fields (variables, content and keywords) in the following loop */
+ cur_field_node = snippet_node->xmlChildrenNode;
+ while (cur_field_node != NULL)
+ {
+ /* We will look in all the variables and save them */
+ if (!g_strcmp0 ((gchar*)cur_field_node->name, NATIVE_XML_VARIABLES_TAG))
+ {
+ cur_variable_node = cur_field_node->xmlChildrenNode;
+ while (cur_variable_node != NULL)
+ {
+ /* Check if it's a variable tag */
+ if (g_strcmp0 ((gchar *)cur_variable_node->name, NATIVE_XML_VARIABLE_TAG))
+ {
+ cur_variable_node = cur_variable_node->next;
+ continue;
+ }
+
+ /* Get the variable properties */
+ cur_var_name = (gchar*)xmlGetProp (cur_variable_node,\
+ (const xmlChar*)NATIVE_XML_NAME_PROP);
+ cur_var_default = (gchar*)xmlGetProp (cur_variable_node,\
+ (const xmlChar*)NATIVE_XML_DEFAULT_PROP);
+ cur_var_global = (gchar*)xmlGetProp (cur_variable_node,\
+ (const xmlChar*)NATIVE_XML_GLOBAL_PROP);
+ if (!g_strcmp0 (cur_var_global, NATIVE_XML_TRUE))
+ cur_var_is_global = TRUE;
+ else
+ cur_var_is_global = FALSE;
+ g_free (cur_var_global);
+
+ /* Add the properties to the lists */
+ variable_names = g_list_append (variable_names, cur_var_name);
+ variable_default_values = g_list_append (variable_default_values, cur_var_default);
+ variable_globals = g_list_append (variable_globals, GINT_TO_POINTER (cur_var_is_global));
+
+ cur_variable_node = cur_variable_node->next;
+ }
+ }
+
+ /* Get the actual snippet content */
+ if (!g_strcmp0 ((gchar*)cur_field_node->name, NATIVE_XML_CONTENT_TAG))
+ {
+ snippet_content = (gchar *)xmlNodeGetContent (cur_field_node);
+ }
+
+ /* Parse the keywords of the snippet */
+ if (!g_strcmp0 ((gchar*)cur_field_node->name, NATIVE_XML_KEYWORDS_TAG))
+ {
+ keywords_temp = (gchar *)xmlNodeGetContent (cur_field_node);
+ keywords_temp_array = g_strsplit (keywords_temp, " ", -1);
+
+ i = 0;
+ while (keywords_temp_array[i])
+ {
+ if (g_strcmp0 (keywords_temp_array[i], ""))
+ {
+ keyword_temp = g_strdup (keywords_temp_array[i]);
+ keywords = g_list_append (keywords, keyword_temp);
+ }
+ i ++;
+ }
+
+ g_free (keywords_temp);
+ g_strfreev (keywords_temp_array);
+ }
+
+ /* Parse the languages of the snippet */
+ if (!g_strcmp0 ((gchar *)cur_field_node->name, NATIVE_XML_LANGUAGES_TAG))
+ {
+ languages_temp = (gchar *)xmlNodeGetContent (cur_field_node);
+ languages_temp_array = g_strsplit (languages_temp, " ", -1);
+
+ i = 0;
+ while (languages_temp_array[i])
+ {
+ if (g_strcmp0 (languages_temp_array[i], ""))
+ {
+ language_temp = g_strdup (languages_temp_array[i]);
+ snippet_languages = g_list_append (snippet_languages, language_temp);
+ }
+ i ++;
+ }
+
+ g_free (languages_temp);
+ g_strfreev (languages_temp_array);
+ }
+
+ cur_field_node = cur_field_node->next;
+ }
+
+ /* Make a new AnjutaSnippet object */
+ snippet = snippet_new (trigger_key,
+ snippet_languages,
+ snippet_name,
+ snippet_content,
+ variable_names,
+ variable_default_values,
+ variable_globals,
+ keywords);
+
+ /* Free the memory (the data is copied on the snippet constructor) */
+ g_free (trigger_key);
+ g_free (snippet_name);
+ g_free (snippet_content);
+ for (iter = g_list_first (variable_names); iter != NULL; iter = g_list_next (iter))
+ {
+ g_free (iter->data);
+ }
+ for (iter = g_list_first (variable_default_values); iter != NULL; iter = g_list_next (iter))
+ {
+ g_free (iter->data);
+ }
+ g_list_free (variable_names);
+ g_list_free (variable_default_values);
+ g_list_free (variable_globals);
+ for (iter = g_list_first (snippet_languages); iter != NULL; iter = g_list_next (iter))
+ {
+ g_free (iter->data);
+ }
+ g_list_free (snippet_languages);
+ for (iter = g_list_first (keywords); iter != NULL; iter = g_list_next (iter))
+ {
+ g_free (iter->data);
+ }
+ g_list_free (keywords);
+
+ return snippet;
+}
+
+static AnjutaSnippetsGroup*
+parse_snippets_group_node (xmlNodePtr snippets_group_node)
+{
+ AnjutaSnippetsGroup *snippets_group = NULL;
+ AnjutaSnippet *cur_snippet = NULL;
+ xmlNodePtr cur_snippet_node = NULL, cur_node = NULL;
+ gchar *group_name = NULL;
+
+ /* Get the group name */
+ cur_node = snippets_group_node->xmlChildrenNode;
+ while (cur_node != NULL)
+ {
+ if (!g_strcmp0 ((gchar*)cur_node->name, NATIVE_XML_GROUP_NAME_TAG))
+ {
+ group_name = g_strdup ((gchar *)xmlNodeGetContent (cur_node));
+ break;
+ }
+
+ cur_node = cur_node->next;
+ }
+ if (group_name == NULL)
+ return NULL;
+
+ /* Initialize the snippets group */
+ snippets_group = snippets_group_new (group_name);
+
+ /* Get the snippets */
+ cur_node = snippets_group_node->xmlChildrenNode;
+ while (cur_node)
+ {
+ if (!g_strcmp0 ((gchar *)cur_node->name, NATIVE_XML_SNIPPETS_TAG))
+ {
+ cur_snippet_node = cur_node->xmlChildrenNode;
+
+ /* Look trough all the snippets */
+ while (cur_snippet_node)
+ {
+ /* Make sure it's a snippet node */
+ if (g_strcmp0 ((gchar *)cur_snippet_node->name, NATIVE_XML_SNIPPET_TAG))
+ {
+ cur_snippet_node = cur_snippet_node->next;
+ continue;
+ }
+
+ /* Get a new AnjutaSnippet object from the current node and add it to the group*/
+ cur_snippet = parse_snippet_node (cur_snippet_node);
+ if (ANJUTA_IS_SNIPPET (cur_snippet))
+ snippets_group_add_snippet (snippets_group, cur_snippet);
+
+ cur_snippet_node = cur_snippet_node->next;
+ }
+
+ break;
+ }
+
+ cur_node = cur_node->next;
+ }
+
+ return snippets_group;
+}
+
+static GList*
+snippets_manager_parse_native_xml_file (const gchar *snippet_packet_path)
+{
+ AnjutaSnippetsGroup* snippets_group = NULL;
+ GList* snippets_groups = NULL;
+ xmlDocPtr snippet_packet_doc = NULL;
+ xmlNodePtr cur_node = NULL;
+
+ /* Parse the XML file and load it into a xmlDoc */
+ snippet_packet_doc = xmlParseFile (snippet_packet_path);
+ if (snippet_packet_doc == NULL)
+ return NULL;
+
+ /* Get the root and assert it */
+ cur_node = xmlDocGetRootElement (snippet_packet_doc);
+ if (cur_node == NULL || g_strcmp0 ((gchar *)cur_node->name, NATIVE_XML_ROOT))
+ {
+ xmlFreeDoc (snippet_packet_doc);
+ return NULL;
+ }
+
+ /* Get the snippets groups */
+ cur_node = cur_node->xmlChildrenNode;
+ while (cur_node != NULL)
+ {
+ /* Get the current snippets group */
+ if (!g_strcmp0 ((gchar*)cur_node->name, NATIVE_XML_GROUP_TAG))
+ {
+ snippets_group = parse_snippets_group_node (cur_node);
+
+ if (ANJUTA_IS_SNIPPETS_GROUP (snippets_group))
+ snippets_groups = g_list_prepend (snippets_groups, snippets_group);
+ }
+
+ cur_node = cur_node->next;
+ }
+
+ xmlFreeDoc (snippet_packet_doc);
+
+ return snippets_groups;
+}
+
+static GList*
+snippets_manager_parse_gedit_xml_file (const gchar* snippet_packet_path)
+{
+ /* TODO */
+ return NULL;
+}
+
+
+/**
+ * snippets_manager_parse_xml_file:
+ * @snippet_packet_path: The path of the XML file describing the Snippet Group
+ * @format_Type: The type of the XML file (see snippets-db.h for the supported types)
+ *
+ * Parses the given XML file.
+ *
+ * Returns: A list of #AnjutaSnippetGroup objects.
+ **/
+GList*
+snippets_manager_parse_snippets_xml_file (const gchar* snippet_packet_path,
+ FormatType format_type)
+{
+ switch (format_type)
+ {
+ case NATIVE_FORMAT:
+ return snippets_manager_parse_native_xml_file (snippet_packet_path);
+
+ case GEDIT_FORMAT:
+ return snippets_manager_parse_gedit_xml_file (snippet_packet_path);
+
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * snippets_manager_parse_xml_file:
+ * @format_type: The type of the XML file (see snippets-db.h for the supported types)
+ * @snippets_group: A list #AnjutaSnippetsGroup objects.
+ *
+ * Saves the given snippets to a snippet-packet XML file.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+snippets_manager_save_snippets_xml_file (FormatType format_type,
+ GList* snippets_groups,
+ const gchar *file_path)
+{
+ switch (format_type)
+ {
+ case NATIVE_FORMAT:
+ return snippets_manager_save_native_xml_file (snippets_groups, file_path);
+
+ case GEDIT_FORMAT:
+ return snippets_manager_save_gedit_xml_file (snippets_groups, file_path);
+
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * snippets_manager_parse_variables_xml_file:
+ * @global_vars_path: A path with a XML file describing snippets global variables.
+ * @snippets_db: A #SnippetsDB object where the global variables should be loaded.
+ *
+ * Loads the global variables from the given XML file in the given #SnippetsDB object.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_manager_parse_variables_xml_file (const gchar* global_vars_path,
+ SnippetsDB* snippets_db)
+{
+ xmlDocPtr global_vars_doc = NULL;
+ xmlNodePtr cur_var_node = NULL;
+ gchar *cur_var_name = NULL, *cur_var_is_command = NULL, *cur_var_content = NULL;
+ gboolean cur_var_is_command_bool = FALSE;
+
+ /* Assertions */
+ g_return_val_if_fail (global_vars_path != NULL, FALSE);
+ g_return_val_if_fail (ANJUTA_IS_SNIPPETS_DB (snippets_db), FALSE);
+
+ /* Parse the XML file */
+ global_vars_doc = xmlParseFile (global_vars_path);
+ g_return_val_if_fail (global_vars_doc != NULL, FALSE);
+
+ /* Get the root and assert it */
+ cur_var_node = xmlDocGetRootElement (global_vars_doc);
+ if (cur_var_node == NULL ||\
+ g_strcmp0 ((gchar *)cur_var_node->name, GLOBAL_VARS_XML_ROOT))
+ {
+ xmlFreeDoc (global_vars_doc);
+ return FALSE;
+ }
+
+ /* Get the name and description fields */
+ cur_var_node = cur_var_node->xmlChildrenNode;
+ while (cur_var_node != NULL)
+ {
+ /* Get the current Snippet Global Variable */
+ if (!g_strcmp0 ((gchar*)cur_var_node->name, GLOBAL_VARS_XML_VAR_TAG))
+ {
+ /* Get the name, is_command properties and the content */
+ cur_var_name = (gchar*)xmlGetProp (cur_var_node,\
+ (const xmlChar*)GLOBAL_VARS_XML_NAME_PROP);
+ cur_var_is_command = (gchar*)xmlGetProp (cur_var_node,\
+ (const xmlChar*)GLOBAL_VARS_XML_COMMAND_PROP);
+ cur_var_content = g_strdup ((gchar *)xmlNodeGetContent (cur_var_node));
+ if (!g_strcmp0 (cur_var_is_command, GLOBAL_VARS_XML_TRUE))
+ cur_var_is_command_bool = TRUE;
+ else
+ cur_var_is_command_bool = FALSE;
+
+ /* Add the Global Variable to the Snippet Database */
+ snippets_db_add_global_variable (snippets_db,
+ cur_var_name,
+ cur_var_content,
+ cur_var_is_command_bool,
+ TRUE);
+
+ g_free (cur_var_content);
+ g_free (cur_var_name);
+ g_free (cur_var_is_command);
+ }
+
+ cur_var_node = cur_var_node->next;
+ }
+
+ return TRUE;
+}
+
+static void
+write_global_var_tags (GOutputStream *os,
+ const gchar *name,
+ const gchar *value,
+ gboolean is_command)
+{
+ gchar *command_string = NULL, *escaped_content = NULL, *line = NULL,
+ *escaped_name = NULL;
+
+ /* Assertions */
+ g_return_if_fail (G_IS_OUTPUT_STREAM (os));
+
+ /* Escape the text */
+ command_string = (is_command) ? GLOBAL_VARS_XML_TRUE : GLOBAL_VARS_XML_FALSE;
+ escaped_content = escape_text_cdata (value);
+ escaped_name = escape_quotes (name);
+
+ /* Write the tag */
+ line = g_strconcat ("<global-variable name=\"", escaped_name,
+ "\" is_command=\"", command_string, "\">",
+ escaped_content,
+ "</global-variable>\n",
+ NULL);
+ g_output_stream_write (os, line, strlen (line), NULL, NULL);
+
+ /* Free the memory */
+ g_free (line);
+ g_free (escaped_content);
+ g_free (escaped_name);
+}
+
+/**
+ * snippets_manager_save_variables_xml_file:
+ * @global_vars_path: A path with a XML file describing snippets global variables.
+ * @global_vars_name_list: A #GList with the name of the variables.
+ * @global_vars_value_list: A #GList with the values of the variables.
+ * @global_vars_is_command_list: A #Glist with #gboolean values showing if the value
+ * of the given variable is a command.
+ *
+ * Saves the given snippets global variables in a XML file at the given path.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+snippets_manager_save_variables_xml_file (const gchar* global_variables_path,
+ GList* global_vars_name_list,
+ GList* global_vars_value_list,
+ GList* global_vars_is_command_list)
+{
+ GList *iter = NULL, *iter2 = NULL, *iter3 = NULL;
+ GFile *file = NULL;
+ GOutputStream *os = NULL;
+
+ /* Assertions */
+ g_return_val_if_fail (global_variables_path != NULL, FALSE);
+
+ /* Open the file */
+ file = g_file_new_for_path (global_variables_path);
+ os = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL));
+ if (!G_IS_OUTPUT_STREAM (os))
+ {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ if (g_output_stream_write (os, NATIVE_XML_HEADER, strlen (NATIVE_XML_HEADER), NULL, NULL) < 0)
+ {
+ g_output_stream_close (os, NULL, NULL);
+ g_object_unref (os);
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ write_simple_start_tag (os, GLOBAL_VARS_XML_ROOT);
+
+ /* Write each of the global variable */
+ iter = g_list_first (global_vars_name_list);
+ iter2 = g_list_first (global_vars_value_list);
+ iter3 = g_list_first (global_vars_is_command_list);
+ while (iter != NULL && iter2 != NULL && iter3 != NULL)
+ {
+ write_global_var_tags (os,
+ (gchar *)iter->data,
+ (gchar *)iter2->data,
+ GPOINTER_TO_INT (iter3->data));
+
+ iter = g_list_next (iter);
+ iter2 = g_list_next (iter2);
+ iter3 = g_list_next (iter3);
+ }
+
+ write_simple_end_tag (os, GLOBAL_VARS_XML_ROOT);
+
+ /* Close the file */
+ g_output_stream_close (os, NULL, NULL);
+ g_object_unref (os);
+ g_object_unref (file);
+
+ return TRUE;
+}
diff --git a/plugins/snippets-manager/snippets-xml-parser.h b/plugins/snippets-manager/snippets-xml-parser.h
new file mode 100644
index 0000000..1b07941
--- /dev/null
+++ b/plugins/snippets-manager/snippets-xml-parser.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ snippets-xml-parser.h
+ Copyright (C) Dragos Dena 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include <glib.h>
+#include "snippets-group.h"
+#include "snippets-db.h"
+
+GList* snippets_manager_parse_snippets_xml_file (const gchar* snippet_packet_path,
+ FormatType format_type);
+gboolean snippets_manager_save_snippets_xml_file (FormatType format_type,
+ GList* snippets_groups,
+ const gchar *file_path);
+gboolean snippets_manager_parse_variables_xml_file (const gchar* global_vars_file_path,
+ SnippetsDB* snippets_db);
+gboolean snippets_manager_save_variables_xml_file (const gchar* global_variables_file_path,
+ GList* global_vars_name_list,
+ GList* global_vars_value_list,
+ GList* global_vars_is_command_list);
diff --git a/plugins/snippets-manager/snippets.anjuta-snippets b/plugins/snippets-manager/snippets.anjuta-snippets
new file mode 100644
index 0000000..1c5632d
--- /dev/null
+++ b/plugins/snippets-manager/snippets.anjuta-snippets
@@ -0,0 +1,464 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<anjuta-snippets-packet>
+<anjuta-snippets-group>
+<name><![CDATA[Comments and licenses]]></name>
+<anjuta-snippets>
+<anjuta-snippet trigger="bsd" name="BSD License">
+<languages><![CDATA[Python ]]></languages>
+<variables>
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of ${userfullname} nor the name of any other
+# contributor may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY ${userfullname} AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL ${userfullname} OR ANY OTHER
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+]]></snippet-content>
+<keywords><![CDATA[BSD license perl python sh shell comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="bsd" name="BSD License">
+<languages><![CDATA[C++ ]]></languages>
+<variables>
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of ${userfullname} nor the name of any other
+// contributor may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY ${userfullname} AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL ${userfullname} OR ANY OTHER
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+]]></snippet-content>
+<keywords><![CDATA[BSD license C++ comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="bsd" name="BSD License">
+<languages><![CDATA[C ]]></languages>
+<variables>
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of ${userfullname} nor the name of any other
+ * contributor may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ${userfullname} AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ${userfullname} OR ANY OTHER
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+]]></snippet-content>
+<keywords><![CDATA[BSD license C comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="c" name="Comment block">
+<languages><![CDATA[C++ Java C C# ]]></languages>
+<variables>
+</variables>
+<snippet-content><![CDATA[/* ${END_CURSOR_POSITION} */]]></snippet-content>
+<keywords><![CDATA[comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="co" name="Comment line">
+<languages><![CDATA[C# C++ C Java ]]></languages>
+<variables>
+</variables>
+<snippet-content><![CDATA[// ${END_CURSOR_POSITION}]]></snippet-content>
+<keywords><![CDATA[comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="gpl" name="GPL License">
+<languages><![CDATA[Python ]]></languages>
+<variables>
+<variable name="filename" default="file" is_global="true" />
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[# ${filename}
+#
+# Copyright (C) ${year} - ${userfullname}
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]></snippet-content>
+<keywords><![CDATA[GPL license Perl python shell sh comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="gpl" name="GPL License">
+<languages><![CDATA[C++ ]]></languages>
+<variables>
+<variable name="filename" default="file" is_global="true" />
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[// ${filename}
+//
+// Copyright (C) ${year} - ${userfullname}
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+]]></snippet-content>
+<keywords><![CDATA[GPL license C++ comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="gpl" name="GPL License">
+<languages><![CDATA[C ]]></languages>
+<variables>
+<variable name="filename" default="file" is_global="true" />
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[/*
+ * ${filename}
+ *
+ * Copyright (C) ${year} - ${userfullname}
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+]]></snippet-content>
+<keywords><![CDATA[GPL license C comment ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="header" name="Header guard">
+<languages><![CDATA[C C++ ]]></languages>
+<variables>
+<variable name="filename_prefix" default="FILE_NAME" is_global="false" />
+</variables>
+<snippet-content><![CDATA[#ifndef ${filename_prefix}_H
+#define ${filename_prefix}_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+${END_CURSOR_POSITION}
+
+G_END_DECLS
+
+#endif /* ${filename_prefix}_H */
+]]></snippet-content>
+<keywords><![CDATA[comment header copyright C C++ email date time ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="lgpl" name="LGPL License">
+<languages><![CDATA[Python ]]></languages>
+<variables>
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[# Copyright (C) ${year} ${userfullname}
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with main.c; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
+]]></snippet-content>
+<keywords><![CDATA[lgpl python sh perl comment header ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="lgpl" name="LGPL License">
+<languages><![CDATA[C++ ]]></languages>
+<variables>
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[// Copyright (C) ${year} ${userfullname}
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with main.c; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
+]]></snippet-content>
+<keywords><![CDATA[lgpl C++ cpp comment header ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="lgpl" name="LGPL License">
+<languages><![CDATA[C ]]></languages>
+<variables>
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+</variables>
+<snippet-content><![CDATA[/*
+ * Copyright (C) ${year} ${userfullname}
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with main.c; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
+ */
+]]></snippet-content>
+<keywords><![CDATA[lgpl C comment header ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="top_com" name="Top comment">
+<languages><![CDATA[Python ]]></languages>
+<variables>
+<variable name="filename" default="file" is_global="true" />
+<variable name="date_time" default="" is_global="true" />
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+<variable name="email" default="user domain org" is_global="true" />
+</variables>
+<snippet-content><![CDATA[
+# ${filename}
+# ${date_time}
+# Copyright ${year} ${userfullname}
+# <${email}>
+]]></snippet-content>
+<keywords><![CDATA[comment header copyright perl shell python sh license email ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="top_com" name="Top comment">
+<languages><![CDATA[C# ]]></languages>
+<variables>
+<variable name="copyright_file_name" default="copyright.txt" is_global="false" />
+<variable name="license_file_name" default="license.txt" is_global="false" />
+<variable name="userfullname" default="User Name" is_global="true" />
+<variable name="email" default="user domain org" is_global="true" />
+<variable name="version" default="1.0" is_global="false" />
+</variables>
+<snippet-content><![CDATA[// <file>
+// <copyright see="prj:///doc/${copyright_file_name}"/>
+// <license see="prj:///doc/${license_file_name}"/>
+// <owner name="${userfullname}" email="${email}"/>
+// <version value="${version}"/>
+// </file>
+]]></snippet-content>
+<keywords><![CDATA[comment header copyright C# csharp license date time email ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="top_com" name="Top Comment">
+<languages><![CDATA[C++ ]]></languages>
+<variables>
+<variable name="filename" default="file" is_global="true" />
+<variable name="date_time" default="" is_global="true" />
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+<variable name="email" default="user domain org" is_global="true" />
+</variables>
+<snippet-content><![CDATA[// ${filename}
+// ${date_time}
+// Copyright ${year} ${userfullname}
+// <${email}>
+]]></snippet-content>
+<keywords><![CDATA[comment header copyright C++ email date time ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="top_com" name="Top Comment">
+<languages><![CDATA[C ]]></languages>
+<variables>
+<variable name="filename" default="file" is_global="true" />
+<variable name="date_time" default="" is_global="true" />
+<variable name="year" default="2010" is_global="true" />
+<variable name="userfullname" default="User Name" is_global="true" />
+<variable name="email" default="user domain org" is_global="true" />
+</variables>
+<snippet-content><![CDATA[/***************************************************************************
+ * ${filename}
+ *
+ * ${date_time}
+ * Copyright ${year} ${userfullname}
+ * <${email}>
+ ****************************************************************************/
+]]></snippet-content>
+<keywords><![CDATA[comment header copyright C email date time ]]></keywords>
+</anjuta-snippet>
+</anjuta-snippets>
+</anjuta-snippets-group>
+<anjuta-snippets-group>
+<name><![CDATA[Control structures]]></name>
+<anjuta-snippets>
+<anjuta-snippet trigger="cond" name="Conditional assignment">
+<languages><![CDATA[C++ Java C C# ]]></languages>
+<variables>
+<variable name="false_case" default="/* False case */" is_global="false" />
+<variable name="true_case" default="/* True case */" is_global="false" />
+<variable name="condition" default="/* condition */" is_global="false" />
+</variables>
+<snippet-content><![CDATA[(${condition}) ? ${true_case} : ${false_case}${END_CURSOR_POSITION}]]></snippet-content>
+<keywords><![CDATA[cond ? if else elif statement ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="do" name="Do while loop">
+<languages><![CDATA[C++ Java C C# ]]></languages>
+<variables>
+<variable name="condition" default="/* condition */" is_global="false" />
+</variables>
+<snippet-content><![CDATA[do
+{
+ ${END_CURSOR_POSITION}
+
+} while (${condition});
+]]></snippet-content>
+<keywords><![CDATA[do loop while repeat for ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="else" name="Else">
+<languages><![CDATA[C C++ Java C# ]]></languages>
+<variables>
+</variables>
+<snippet-content><![CDATA[else
+{
+ ${END_CURSOR_POSITION}
+}]]></snippet-content>
+<keywords><![CDATA[else statement if elif ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="elif" name="Else if">
+<languages><![CDATA[C++ C Java C# ]]></languages>
+<variables>
+<variable name="condition" default="/* condition */" is_global="false" />
+</variables>
+<snippet-content><![CDATA[else if (${condition})
+{
+ ${END_CURSOR_POSITION}
+}]]></snippet-content>
+<keywords><![CDATA[else if elif statement ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="for" name="For loop">
+<languages><![CDATA[C++ Java C# ]]></languages>
+<variables>
+<variable name="type" default="int" is_global="false" />
+<variable name="iter" default="i" is_global="false" />
+<variable name="init" default="0" is_global="false" />
+<variable name="max" default="n" is_global="false" />
+<variable name="count" default="1" is_global="false" />
+</variables>
+<snippet-content><![CDATA[for (${type} ${iter} = ${init}; ${iter} < ${max}; ${iter} += ${count})
+{
+ ${END_CURSOR_POSITION}
+}
+]]></snippet-content>
+<keywords><![CDATA[for C loop statement iter ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="for" name="For loop">
+<languages><![CDATA[C ]]></languages>
+<variables>
+<variable name="iter" default="i" is_global="false" />
+<variable name="init" default="0" is_global="false" />
+<variable name="max" default="n" is_global="false" />
+<variable name="count" default="1" is_global="false" />
+</variables>
+<snippet-content><![CDATA[for (${iter} = ${init}; ${iter} < ${max}; ${iter} += ${count})
+{
+ ${END_CURSOR_POSITION}
+}
+]]></snippet-content>
+<keywords><![CDATA[for C loop statement iter ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="if" name="If">
+<languages><![CDATA[C C++ Java C# ]]></languages>
+<variables>
+<variable name="condition" default="/* condition */" is_global="false" />
+</variables>
+<snippet-content><![CDATA[if (${condition})
+{
+ ${END_CURSOR_POSITION}
+}
+]]></snippet-content>
+<keywords><![CDATA[if statement else ]]></keywords>
+</anjuta-snippet>
+<anjuta-snippet trigger="while" name="While loop">
+<languages><![CDATA[C++ C Java C# ]]></languages>
+<variables>
+<variable name="condition" default="/* condition */" is_global="false" />
+</variables>
+<snippet-content><![CDATA[while (${condition})
+{
+ ${END_CURSOR_POSITION}
+}]]></snippet-content>
+<keywords><![CDATA[while loop repeat do for ]]></keywords>
+</anjuta-snippet>
+</anjuta-snippets>
+</anjuta-snippets-group>
+</anjuta-snippets-packet>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]