[anjuta] Add new quick-open plugin.
- From: Sebastien Granjoux <sgranjoux src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [anjuta] Add new quick-open plugin.
- Date: Fri, 18 Jan 2013 22:01:46 +0000 (UTC)
commit 7a59983044cd4f767ffd14da243515a8ee00166a
Author: Carl-Anton Ingmarsson <ca ingmarsson gmail com>
Date: Sat Jan 5 17:51:31 2013 +0100
Add new quick-open plugin.
The quick-open plugin lets you quickly open project files and switch to
already open files.
https://bugzilla.gnome.org/show_bug.cgi?id=691445
quick-open: disconnect watch on IANJUTA_PROJECT_MANAGER_CURRENT_PROJECT.
quick-open: focus search entry on show.
Also make the tree view non focusable.
quick-open: don't use anjuta_shell_get_interface() during deactivation.
It may cause plugins to get activated again.
quick-open: Only use GtkSearchEntry if gtk+ version is atleast 3.6
configure.ac | 1 +
plugins/Makefile.am | 1 +
plugins/quick-open/Makefile.am | 47 ++
plugins/quick-open/anjuta-quick-open-48.png | Bin 0 -> 2190 bytes
plugins/quick-open/anjuta-quick-open.plugin.in | 5 +
plugins/quick-open/anjuta-quick-open.ui | 121 ++++
plugins/quick-open/anjuta-quick-open.xml | 10 +
plugins/quick-open/plugin.c | 332 +++++++++
plugins/quick-open/plugin.h | 60 ++
plugins/quick-open/quick-open-dialog.c | 881 ++++++++++++++++++++++++
plugins/quick-open/quick-open-dialog.h | 79 +++
11 files changed, 1537 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 2c7ace3..7280dae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -848,6 +848,7 @@ plugins/parser-cxx/Makefile
plugins/parser-cxx/cxxparser/Makefile
plugins/python-loader/Makefile
plugins/jhbuild/Makefile
+plugins/quick-open/Makefile
anjuta.desktop.in
manuals/Makefile
manuals/reference/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 11f14ed..1ab55b0 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -33,6 +33,7 @@ SUBDIRS = . \
project-manager \
project-wizard \
python-loader \
+ quick-open \
run-program \
snippets-manager \
sourceview \
diff --git a/plugins/quick-open/Makefile.am b/plugins/quick-open/Makefile.am
new file mode 100755
index 0000000..f64b133
--- /dev/null
+++ b/plugins/quick-open/Makefile.am
@@ -0,0 +1,47 @@
+# Plugin glade file
+quick_open_gladedir = $(anjuta_glade_dir)
+quick_open_glade_DATA = anjuta-quick-open.ui
+
+# Plugin UI file
+quick_open_uidir = $(anjuta_ui_dir)
+quick_open_ui_DATA = anjuta-quick-open.xml
+
+# Plugin Icon file
+quick_open_pixmapsdir = $(anjuta_image_dir)
+quick_open_pixmaps_DATA = anjuta-quick-open-48.png
+
+# Plugin description file
+plugin_in_files = anjuta-quick-open.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
+
+anjuta_plugindir = $(anjuta_plugin_dir)
+anjuta_plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
+
+plugindir = $(anjuta_plugin_dir)
+plugin_LTLIBRARIES = libanjuta-quick-open.la
+
+AM_CPPFLAGS= \
+ $(WARN_CFLAGS) \
+ $(DEPRECATED_FLAGS) \
+ $(LIBANJUTA_CFLAGS)
+
+libanjuta_quick_open_la_LDFLAGS = $(ANJUTA_PLUGIN_LDFLAGS)
+
+libanjuta_quick_open_la_LIBADD = \
+ $(LIBANJUTA_LIBS)
+
+libanjuta_quick_open_la_SOURCES = \
+ plugin.c \
+ plugin.h \
+ quick-open-dialog.c \
+ quick-open-dialog.h
+
+EXTRA_DIST = \
+ $(plugin_in_files) \
+ $(anjuta_pixmaps_DATA) \
+ $(anjuta_plugin_DATA) \
+ $(anjuta_ui_DATA) \
+ $(anjuta_glade_DATA) \
+ $(schema_in_files)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/quick-open/anjuta-quick-open-48.png b/plugins/quick-open/anjuta-quick-open-48.png
new file mode 100644
index 0000000..73ee06c
Binary files /dev/null and b/plugins/quick-open/anjuta-quick-open-48.png differ
diff --git a/plugins/quick-open/anjuta-quick-open.plugin.in b/plugins/quick-open/anjuta-quick-open.plugin.in
new file mode 100755
index 0000000..8709ed8
--- /dev/null
+++ b/plugins/quick-open/anjuta-quick-open.plugin.in
@@ -0,0 +1,5 @@
+[Anjuta Plugin]
+_Name=Quick Open
+_Description=Quickly open project files.
+Location=anjuta-quick-open:QuickOpenPlugin
+Icon=anjuta-quick-open-48.png
diff --git a/plugins/quick-open/anjuta-quick-open.ui b/plugins/quick-open/anjuta-quick-open.ui
new file mode 100644
index 0000000..270110c
--- /dev/null
+++ b/plugins/quick-open/anjuta-quick-open.ui
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkGrid" id="grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkNotebook" id="treeview_notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_tabs">False</property>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkSpinner" id="spinner1">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes"><big><b>Loading files...</b></big></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <object class="GtkListStore" id="liststore">
+ <columns>
+ <!-- column-name is_separator -->
+ <column type="gboolean"/>
+ <!-- column-name title -->
+ <column type="gchararray"/>
+ <!-- column-name is_document -->
+ <column type="gboolean"/>
+ <!-- column-name object -->
+ <column type="GObject"/>
+ </columns>
+ </object>
+</interface>
diff --git a/plugins/quick-open/anjuta-quick-open.xml b/plugins/quick-open/anjuta-quick-open.xml
new file mode 100644
index 0000000..625af1f
--- /dev/null
+++ b/plugins/quick-open/anjuta-quick-open.xml
@@ -0,0 +1,10 @@
+<!--*- xml -*-->
+<ui>
+ <menubar name="MenuMain">
+ <menu name="MenuFile" action="ActionMenuFile">
+ <placeholder name="PlaceholderFileMenus">
+ <menuitem name="QuickOpen" action="ActionQuickOpen" />
+ </placeholder>
+ </menu>
+ </menubar>
+</ui>
diff --git a/plugins/quick-open/plugin.c b/plugins/quick-open/plugin.c
new file mode 100755
index 0000000..73bc347
--- /dev/null
+++ b/plugins/quick-open/plugin.c
@@ -0,0 +1,332 @@
+/* -*- Mode: C; indent-spaces-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * plugin.c
+ * Copyright (C) 2012 Carl-Anton Ingmarsson <carlantoni src gnome org>
+ *
+ * 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 <config.h>
+
+#include "plugin.h"
+
+#include <libanjuta/anjuta-debug.h>
+#include <libanjuta/interfaces/ianjuta-file.h>
+#include <libanjuta/interfaces/ianjuta-file-loader.h>
+
+#include <signal.h>
+
+#define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-quick-open.xml"
+
+struct _QuickOpenPluginClass
+{
+ AnjutaPluginClass parent_class;
+};
+
+static void
+on_dialog_response(GtkDialog* dialog, gint response_id, gpointer user_data)
+{
+ QuickOpenPlugin* self = user_data;
+
+ gtk_widget_hide(GTK_WIDGET(dialog));
+
+ if (response_id == GTK_RESPONSE_ACCEPT)
+ {
+ GObject* object;
+
+ object = quick_open_dialog_get_selected_object(self->dialog);
+ if (!object)
+ return;
+
+ if (IANJUTA_IS_DOCUMENT(object))
+ {
+ ianjuta_document_manager_set_current_document(self->docman,
+ IANJUTA_DOCUMENT(object), NULL);
+ }
+ else if (G_IS_FILE(object))
+ {
+ IAnjutaFileLoader* loader;
+
+ loader = anjuta_shell_get_interface (ANJUTA_PLUGIN (self)->shell,
+ IAnjutaFileLoader, NULL);
+ g_return_if_fail (loader != NULL);
+
+ ianjuta_file_loader_load (loader, G_FILE(object), FALSE, NULL);
+ }
+
+ g_object_unref(object);
+ }
+}
+
+static void
+on_quick_open_activate(GtkAction *action, QuickOpenPlugin *self)
+{
+ gtk_widget_show(GTK_WIDGET(self->dialog));
+}
+
+static void
+project_node_foreach_func(AnjutaProjectNode *node, gpointer data)
+{
+ GSList** project_files = data;
+ AnjutaProjectNodeType type;
+
+ type = anjuta_project_node_get_node_type(node);
+
+ if (type == ANJUTA_PROJECT_SOURCE)
+ {
+ GFile* file;
+
+ file = anjuta_project_node_get_file(node);
+ *project_files = g_slist_prepend(*project_files, file);
+ }
+}
+
+static void
+quick_open_plugin_load_project_files(QuickOpenPlugin* self, IAnjutaProject* project)
+{
+ AnjutaProjectNode* root;
+ GSList* project_files = NULL;
+
+ root = ianjuta_project_get_root (project, NULL);
+
+ anjuta_project_node_foreach(root, G_POST_ORDER, project_node_foreach_func, &project_files);
+ quick_open_dialog_add_project_files(self->dialog, project_files);
+ g_slist_free(project_files);
+}
+
+static void
+on_project_loaded(IAnjutaProjectManager *project_manager, GError *error, gpointer user_data)
+{
+ QuickOpenPlugin* self = user_data;
+
+ IAnjutaProject* project;
+
+ project = ianjuta_project_manager_get_current_project(project_manager, NULL);
+ quick_open_plugin_load_project_files(self, project);
+}
+
+static void
+quick_open_plugin_project_added(QuickOpenPlugin* self, IAnjutaProject* project)
+{
+ AnjutaProjectNode* root;
+ GFile* project_root;
+
+ root = ianjuta_project_get_root(project, NULL);
+ project_root = anjuta_project_node_get_file(root);
+ quick_open_dialog_set_project_root(self->dialog, project_root);
+
+ if (ianjuta_project_is_loaded(project, NULL))
+ quick_open_plugin_load_project_files(self, project);
+}
+
+static void
+current_project_added(AnjutaPlugin *plugin, const char *name, const GValue *value, gpointer user_data)
+{
+ QuickOpenPlugin* self = ANJUTA_PLUGIN_QUICK_OPEN(plugin);
+
+ IAnjutaProject* project = g_value_get_object(value);
+ quick_open_plugin_project_added(self, project);
+}
+
+static void
+current_project_removed(AnjutaPlugin *plugin, const char *name, gpointer user_data)
+{
+ QuickOpenPlugin* self = ANJUTA_PLUGIN_QUICK_OPEN(plugin);
+
+ quick_open_dialog_set_project_root(self->dialog, NULL);
+}
+
+static void
+quick_open_plugin_setup_project_handling(QuickOpenPlugin* self)
+{
+ IAnjutaProject* project;
+
+ self->project_manager = anjuta_shell_get_interface (ANJUTA_PLUGIN(self)->shell,
+ IAnjutaProjectManager, NULL);
+ g_return_if_fail(self->project_manager);
+
+ g_object_add_weak_pointer(G_OBJECT(self->project_manager), (void**)&self->project_manager);
+
+ /* Connect to project manager events. */
+ self->project_watch_id = anjuta_plugin_add_watch(ANJUTA_PLUGIN(self),
+ IANJUTA_PROJECT_MANAGER_CURRENT_PROJECT,
+ current_project_added, current_project_removed, self);
+
+ g_signal_connect(self->project_manager, "project-loaded",
+ G_CALLBACK (on_project_loaded), self);
+
+ project = ianjuta_project_manager_get_current_project (self->project_manager, NULL);
+ if (project)
+ quick_open_plugin_project_added(self, project);
+}
+
+static void
+on_document_removed(IAnjutaDocumentManager* docman, IAnjutaDocument* doc, gpointer user_data)
+{
+ QuickOpenPlugin* self = user_data;
+
+ quick_open_dialog_remove_document(self->dialog, doc);
+}
+
+static void
+on_document_added(IAnjutaDocumentManager* docman, IAnjutaDocument* doc, gpointer user_data)
+{
+ QuickOpenPlugin* self = user_data;
+
+ quick_open_dialog_add_document(self->dialog, doc);
+}
+
+static void
+quick_open_plugin_setup_document_handling(QuickOpenPlugin* self)
+{
+ GList* documents, *l;
+
+ self->docman = anjuta_shell_get_interface(ANJUTA_PLUGIN(self)->shell,
+ IAnjutaDocumentManager, NULL);
+ g_return_if_fail(self->docman);
+
+ g_object_add_weak_pointer(G_OBJECT(self->docman), (void**)&self->docman);
+
+ documents = ianjuta_document_manager_get_doc_widgets(self->docman, NULL);
+ for (l = documents; l; l = l->next)
+ {
+ IAnjutaDocument* doc = IANJUTA_DOCUMENT(l->data);
+ quick_open_dialog_add_document(self->dialog, doc);
+ }
+ g_list_free(documents);
+
+ g_signal_connect(self->docman, "document-added", G_CALLBACK(on_document_added), self);
+ g_signal_connect(self->docman, "document-removed", G_CALLBACK(on_document_removed), self);
+}
+
+/* Actions table
+ *---------------------------------------------------------------------------*/
+
+static GtkActionEntry actions_quick_open[] = {
+ {
+ "ActionQuickOpen",
+ GTK_STOCK_OPEN,
+ N_("Quick open"),
+ "<Control><Alt>o",
+ N_("Quickly open a file in the current project."),
+ G_CALLBACK(on_quick_open_activate)
+ }
+};
+
+/* AnjutaPlugin functions
+ *---------------------------------------------------------------------------*/
+
+static gboolean
+quick_open_plugin_activate(AnjutaPlugin *plugin)
+{
+ QuickOpenPlugin *self = ANJUTA_PLUGIN_QUICK_OPEN(plugin);
+
+ AnjutaUI* ui;
+
+ DEBUG_PRINT("%s", "Quick Open Plugin: Activating pluginâ");
+
+ /* Add actions */
+ ui = anjuta_shell_get_ui(plugin->shell, NULL);
+ self->action_group = anjuta_ui_add_action_group_entries(ui,
+ "ActionsQuickOpen", _("Quick open operations"),
+ actions_quick_open, G_N_ELEMENTS(actions_quick_open),
+ GETTEXT_PACKAGE, TRUE, self);
+
+ self->uiid = anjuta_ui_merge(ui, UI_FILE);
+
+
+ /* Create the dialog. */
+ self->dialog = quick_open_dialog_new();
+ gtk_window_set_transient_for(GTK_WINDOW(self->dialog), GTK_WINDOW(plugin->shell));
+
+ g_signal_connect(self->dialog, "delete-event",
+ G_CALLBACK(gtk_widget_hide_on_delete), NULL);
+ g_signal_connect(self->dialog, "response",
+ G_CALLBACK(on_dialog_response), self);
+
+ quick_open_plugin_setup_project_handling(self);
+ quick_open_plugin_setup_document_handling(self);
+
+
+ return TRUE;
+}
+
+static gboolean
+quick_open_plugin_deactivate(AnjutaPlugin *plugin)
+{
+ QuickOpenPlugin *self = ANJUTA_PLUGIN_QUICK_OPEN(plugin);
+
+ AnjutaUI *ui;
+
+ DEBUG_PRINT("%s", "Quick Open Plugin: Deactivating pluginâ");
+
+ ui = anjuta_shell_get_ui(plugin->shell, NULL);
+ anjuta_ui_remove_action_group(ui, self->action_group);
+
+ anjuta_ui_unmerge(ui, self->uiid);
+
+ anjuta_plugin_remove_watch(plugin, self->project_watch_id, FALSE);
+
+ /* Disconnect signals. */
+ if (self->project_manager)
+ g_signal_handlers_disconnect_by_func(self->project_manager, on_project_loaded, self);
+
+ if (self->docman)
+ {
+ g_signal_handlers_disconnect_by_func(self->docman, on_document_added, self);
+ g_signal_handlers_disconnect_by_func(self->docman, on_document_removed, self);
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(self->dialog));
+
+ return TRUE;
+}
+
+/* GObject functions
+ *---------------------------------------------------------------------------*/
+
+/* Used in dispose and finalize */
+static gpointer parent_class;
+
+static void
+quick_open_plugin_instance_init(GObject *obj)
+{
+}
+
+static void
+quick_open_plugin_finalize(GObject *obj)
+{
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+quick_open_plugin_class_init(GObjectClass *klass)
+{
+ AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ plugin_class->activate = quick_open_plugin_activate;
+ plugin_class->deactivate = quick_open_plugin_deactivate;
+ klass->finalize = quick_open_plugin_finalize;
+}
+
+/* AnjutaPlugin declaration
+ *---------------------------------------------------------------------------*/
+
+ANJUTA_PLUGIN_BEGIN(QuickOpenPlugin, quick_open_plugin);
+ANJUTA_PLUGIN_END;
+
+ANJUTA_SIMPLE_PLUGIN(QuickOpenPlugin, quick_open_plugin);
diff --git a/plugins/quick-open/plugin.h b/plugins/quick-open/plugin.h
new file mode 100755
index 0000000..67b45b1
--- /dev/null
+++ b/plugins/quick-open/plugin.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; indent-spaces-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * plugin.h
+ * Copyright (C) 2012 Carl-Anton Ingmarsson <carlantoni src gnome org>
+ *
+ * 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 _PLUGIN_H_
+#define _PLUGIN_H_
+
+#include <libanjuta/anjuta-plugin.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-project-manager.h>
+
+#include "quick-open-dialog.h"
+
+
+#define ANJUTA_TYPE_PLUGIN_QUICK_OPEN (quick_open_plugin_get_type(NULL))
+#define ANJUTA_PLUGIN_QUICK_OPEN(o) (G_TYPE_CHECK_INSTANCE_CAST((o), ANJUTA_TYPE_PLUGIN_QUICK_OPEN, QuickOpenPlugin))
+#define ANJUTA_PLUGIN_QUICK_OPEN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), ANJUTA_TYPE_PLUGIN_QUICK_OPEN, QuickOpenPluginClass))
+#define ANJUTA_IS_PLUGIN_QUICK_OPEN(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), ANJUTA_TYPE_PLUGIN_QUICK_OPEN))
+#define ANJUTA_IS_PLUGIN_QUICK_OPEN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), ANJUTA_TYPE_PLUGIN_QUICK_OPEN))
+#define ANJUTA_PLUGIN_QUICK_OPEN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), ANJUTA_TYPE_PLUGIN_QUICK_OPEN, QuickOpenPluginClass))
+
+typedef struct _QuickOpenPlugin QuickOpenPlugin;
+typedef struct _QuickOpenPluginClass QuickOpenPluginClass;
+
+struct _QuickOpenPlugin
+{
+ AnjutaPlugin parent;
+
+ /* Menu item */
+ gint uiid;
+ GtkActionGroup* action_group;
+
+ IAnjutaProjectManager* project_manager;
+ guint project_watch_id;
+
+ IAnjutaDocumentManager* docman;
+
+ QuickOpenDialog* dialog;
+};
+
+
+extern GType quick_open_plugin_get_type(GTypeModule *module);
+
+#endif
diff --git a/plugins/quick-open/quick-open-dialog.c b/plugins/quick-open/quick-open-dialog.c
new file mode 100644
index 0000000..0740d83
--- /dev/null
+++ b/plugins/quick-open/quick-open-dialog.c
@@ -0,0 +1,881 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * quick-open-dialog.c
+ * Copyright (C) 2013 Carl-Anton Ingmarsson <carlantoni src gnome org>
+ *
+ * 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/gi18n.h>
+#include <libanjuta/interfaces/ianjuta-file.h>
+#include <libanjuta/interfaces/ianjuta-file-savable.h>
+#include <string.h>
+
+#include "quick-open-dialog.h"
+
+struct _QuickOpenDialogPrivate
+{
+ GFile* project_root;
+
+ GtkEntry* filter_entry;
+ guint filter_changed_timeout;
+ char** filter_strings;
+
+ GtkNotebook* tree_view_notebook;
+ GtkTreeView* tree_view;
+
+ GtkListStore* store;
+ GtkTreeModelFilter* filter_model;
+ GHashTable* project_files_hash;
+
+ GSList* documents;
+ GHashTable* document_files_hash;
+};
+
+enum {
+ COLUMN_IS_SEPARATOR = 0,
+ COLUMN_TITLE,
+ COLUMN_IS_DOCUMENT,
+ COLUMN_OBJECT
+};
+
+enum {
+ NOTEBOOK_PAGE_LOADING = 0,
+ NOTEBOOK_PAGE_VIEW = 1
+};
+
+#define BUILDER_FILE PACKAGE_DATA_DIR"/glade/anjuta-quick-open.ui"
+
+#define FILTER_CHANGED_TIMEOUT 150
+
+
+G_DEFINE_TYPE (QuickOpenDialog, quick_open_dialog, GTK_TYPE_DIALOG);
+
+static gboolean
+iter_next_skip_separator(GtkTreeModel* model, GtkTreeIter* iter)
+{
+ while (gtk_tree_model_iter_next(model, iter))
+ {
+ gboolean is_separator;
+
+ gtk_tree_model_get(model, iter, COLUMN_IS_SEPARATOR, &is_separator, -1);
+ if (!is_separator)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+iter_first_skip_separator(GtkTreeModel* model, GtkTreeIter* iter)
+{
+ gboolean is_separator;
+
+ if (!gtk_tree_model_get_iter_first(model, iter))
+ return FALSE;
+
+ gtk_tree_model_get(model, iter, COLUMN_IS_SEPARATOR, &is_separator, -1);
+ if (!is_separator)
+ return TRUE;
+
+ return iter_next_skip_separator(model, iter);
+}
+
+static void
+quick_open_dialog_select_path(QuickOpenDialog* self, GtkTreePath* path)
+{
+ GtkTreeSelection* selection;
+ selection = gtk_tree_view_get_selection(self->priv->tree_view);
+
+ gtk_tree_selection_select_path(selection, path);
+ gtk_tree_view_scroll_to_cell(self->priv->tree_view, path, NULL, TRUE, 0.5, 0);
+}
+
+typedef enum
+{
+ MOVE_SELECTION_FIRST,
+ MOVE_SELECTION_END,
+ MOVE_SELECTION_DIFF
+} MoveSelection;
+
+static void
+quick_open_dialog_move_selection(QuickOpenDialog* self, MoveSelection mode, gint howmany)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ gint n_items;
+ GtkTreeSelection* selection;
+ GtkTreeIter iter;
+ GtkTreePath* path;
+ gboolean is_separator;
+
+ if (!gtk_tree_view_get_model(priv->tree_view))
+ return;
+
+ n_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->filter_model), NULL);
+ if (n_items == 0)
+ return;
+
+ selection = gtk_tree_view_get_selection(priv->tree_view);
+
+ if (mode == MOVE_SELECTION_FIRST)
+ path = gtk_tree_path_new_first();
+
+ else if (mode == MOVE_SELECTION_END)
+ path = gtk_tree_path_new_from_indices(n_items - 1, -1);
+
+ else if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ {
+ if (howmany > 0)
+ {
+ mode = MOVE_SELECTION_FIRST;
+ path = gtk_tree_path_new_first();
+ }
+ else
+ {
+ mode = MOVE_SELECTION_END;
+ path = gtk_tree_path_new_from_indices(n_items - 1, -1);
+ }
+ }
+ else
+ {
+ gint* indices;
+ gint selected_index;
+
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->filter_model), &iter);
+ indices = gtk_tree_path_get_indices(path);
+ selected_index = indices[0];
+ gtk_tree_path_free(path);
+
+ selected_index += howmany;
+ if (selected_index < 0)
+ selected_index = 0;
+ else if (selected_index > (n_items - 1))
+ selected_index = n_items - 1;
+
+ path = gtk_tree_path_new_from_indices(selected_index, -1);
+ }
+
+ if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->filter_model), &iter, path))
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(priv->filter_model), &iter,
+ COLUMN_IS_SEPARATOR, &is_separator, -1);
+
+ if (is_separator)
+ {
+ gboolean res;
+
+ gtk_tree_path_free(path);
+
+ switch (mode)
+ {
+ case MOVE_SELECTION_FIRST:
+ res = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->filter_model), &iter);
+ break;
+ case MOVE_SELECTION_END:
+ res = gtk_tree_model_iter_previous(GTK_TREE_MODEL(priv->filter_model), &iter);
+ break;
+
+ case MOVE_SELECTION_DIFF:
+ {
+ if (howmany > 0)
+ {
+ res = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->filter_model), &iter);
+ if (!res && howmany > 1)
+ res = gtk_tree_model_iter_previous(GTK_TREE_MODEL(priv->filter_model), &iter);
+ }
+ else
+ {
+ res = gtk_tree_model_iter_previous(GTK_TREE_MODEL(priv->filter_model), &iter);
+ if (!res && howmany < -1)
+ res = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->filter_model), &iter);
+ }
+ break;
+ }
+
+ default:
+ g_assert_not_reached();
+ }
+
+ if (!res)
+ return;
+
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->filter_model), &iter);
+ }
+
+ quick_open_dialog_select_path(self, path);
+ gtk_tree_path_free(path);
+}
+
+static gboolean
+quick_open_dialog_filter_item(QuickOpenDialog* self, const char* title, GFile* file)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+ char** iter;
+
+ /* Don't show project files that are already shown as an open document. */
+ if (file && g_hash_table_lookup(priv->document_files_hash, file))
+ return FALSE;
+
+ if (!priv->filter_strings)
+ return TRUE;
+
+ for (iter = priv->filter_strings; *iter; iter++)
+ {
+ const char* filter_string = *iter;
+
+ if (!g_strstr_len(title, -1, filter_string))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+quick_open_dialog_tree_visible_func(GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer data)
+{
+ QuickOpenDialog* self = data;
+
+ gboolean is_separator, is_document, visible;
+ char* title;
+ GFile* file = NULL;
+
+ gtk_tree_model_get(model, iter, COLUMN_IS_SEPARATOR, &is_separator,
+ COLUMN_TITLE, &title, COLUMN_IS_DOCUMENT, &is_document, -1);
+ if (is_separator)
+ return TRUE;
+
+ if (!is_document)
+ gtk_tree_model_get(model, iter, COLUMN_OBJECT, &file, -1);
+
+ visible = quick_open_dialog_filter_item(self, title, file);
+
+ g_free(title);
+ if (file)
+ g_object_unref(file);
+
+ return visible;
+}
+
+static gboolean
+quick_open_dialog_row_separator_func(GtkTreeModel* model, GtkTreeIter* iter,
+ gpointer user_data)
+{
+ gboolean is_separator;
+
+ gtk_tree_model_get(model, iter, COLUMN_IS_SEPARATOR, &is_separator, -1);
+ if (is_separator)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static gint
+quick_open_dialog_tree_sort_func(GtkTreeModel* model, GtkTreeIter* a,
+ GtkTreeIter* b, gpointer user_data)
+{
+ gboolean is_separator;
+ gboolean is_document1, is_document2;
+ char* title1, *title2;
+ gboolean res;
+
+ gtk_tree_model_get(model, a, COLUMN_IS_SEPARATOR, &is_separator, -1);
+ /* a is separator. */
+ if (is_separator)
+ {
+ gtk_tree_model_get(model, b, COLUMN_IS_DOCUMENT, &is_document2, -1);
+ if (is_document2)
+ return 1;
+ else
+ return -1;
+ }
+
+ gtk_tree_model_get(model, b, COLUMN_IS_SEPARATOR, &is_separator, -1);
+ /* b is separator. */
+ if (is_separator)
+ {
+ gtk_tree_model_get(model, a, COLUMN_IS_DOCUMENT, &is_document1, -1);
+ if (is_document1)
+ return -1;
+ else
+ return 1;
+ }
+
+ gtk_tree_model_get(model, a, COLUMN_IS_DOCUMENT, &is_document1, -1);
+ gtk_tree_model_get(model, b, COLUMN_IS_DOCUMENT, &is_document2, -1);
+
+ if (is_document1 && !is_document2)
+ return -1;
+ if (!is_document1 && is_document2)
+ return 1;
+
+
+ gtk_tree_model_get(model, a, COLUMN_TITLE, &title1, -1);
+ gtk_tree_model_get(model, b, COLUMN_TITLE, &title2, -1);
+
+ res = strcmp(title1, title2);
+ g_free(title1);
+ g_free(title2);
+
+ return res;
+}
+
+static void
+on_tree_view_row_activated(GtkWidget* widget, GtkTreePath* path,
+ GtkTreeViewColumn* column, gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+
+ gtk_dialog_response(GTK_DIALOG(self), GTK_RESPONSE_ACCEPT);
+}
+
+static gboolean
+on_filter_entry_key_press_event(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+
+ if (!(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)))
+ {
+ switch (event->keyval)
+ {
+ case GDK_KEY_Up:
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_DIFF, -1);
+ return TRUE;
+ case GDK_KEY_Down:
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_DIFF, 1);
+ return TRUE;
+ case GDK_KEY_Page_Up:
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_DIFF, -5);
+ return TRUE;
+ case GDK_KEY_Page_Down:
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_DIFF, 5);
+ return TRUE;
+ case GDK_KEY_Home:
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_FIRST, 0);
+ return TRUE;
+ case GDK_KEY_End:
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_END, 0);
+ return TRUE;
+
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ gtk_dialog_response(GTK_DIALOG(self), GTK_RESPONSE_ACCEPT);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+filter_changed_timeout(gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ const char* filter_text;
+
+ filter_text = gtk_entry_get_text(priv->filter_entry);
+
+ g_strfreev(priv->filter_strings);
+
+ if (!filter_text || *filter_text == '\0')
+ priv->filter_strings = NULL;
+
+ else
+ priv->filter_strings = g_strsplit(filter_text, " ", -1);
+
+ gtk_tree_model_filter_refilter(priv->filter_model);
+
+ /* Select the first item. */
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_FIRST, 0);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+on_filter_changed(GtkEntry* filter_entry, gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ if (priv->filter_changed_timeout)
+ g_source_remove(priv->filter_changed_timeout);
+
+ priv->filter_changed_timeout = g_timeout_add(FILTER_CHANGED_TIMEOUT,
+ filter_changed_timeout, self);
+}
+
+static void
+on_dialog_show(GtkWidget* widget, gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ gtk_widget_grab_focus(GTK_WIDGET(priv->filter_entry));
+}
+
+GObject*
+quick_open_dialog_get_selected_object(QuickOpenDialog* self)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GtkTreeSelection* selection;
+ GtkTreeModel* model;
+ GtkTreeIter iter;
+ GObject* object;
+
+ selection = gtk_tree_view_get_selection(priv->tree_view);
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return NULL;
+
+ gtk_tree_model_get(model, &iter, COLUMN_OBJECT, &object, -1);
+ return object;
+}
+
+void
+quick_open_dialog_add_project_file(QuickOpenDialog* self, GFile* file)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ gboolean in_project_hash;
+ char* path;
+
+ if (!g_file_has_prefix(file, self->priv->project_root))
+ return;
+
+ in_project_hash = !!g_hash_table_lookup(priv->project_files_hash, file);
+ /* Project file already added. */
+ if (in_project_hash)
+ return;
+
+ if (priv->project_root && g_file_has_prefix(file, priv->project_root))
+ path = g_file_get_relative_path(priv->project_root, file);
+ else
+ path = g_file_get_path(file);
+
+ gtk_list_store_insert_with_values(priv->store, NULL, -1, COLUMN_TITLE, path,
+ COLUMN_OBJECT, file, -1);
+
+ g_free(path);
+
+ g_hash_table_add(priv->project_files_hash, g_object_ref(file));
+}
+
+void
+quick_open_dialog_add_project_files(QuickOpenDialog* self, GSList* files)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GSList* l;
+
+ g_return_if_fail(QUICK_IS_OPEN_DIALOG(self));
+
+ priv = self->priv;
+
+
+ /* Unset model to keep the tree view from doing unecessary stuff while we
+ * insert the files. */
+ gtk_tree_view_set_model(priv->tree_view, NULL);
+
+ /* Set sort column id to GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID so that
+ * every insert doesn't trigger a resort. It is faster to sort the model once
+ * when all files are inserted. */
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->store),
+ GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+
+ /* Add the files. */
+ for (l = files; l; l = l->next)
+ {
+ GFile* file = l->data;
+ quick_open_dialog_add_project_file(self, file);
+ }
+
+ /* Now we sort the model. */
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->store),
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+
+ /* Set the model again. */
+ gtk_tree_view_set_model (priv->tree_view, GTK_TREE_MODEL(priv->filter_model));
+
+ /* Select the first item. */
+ quick_open_dialog_move_selection(self, MOVE_SELECTION_FIRST, 0);
+
+ gtk_notebook_set_current_page (priv->tree_view_notebook, NOTEBOOK_PAGE_VIEW);
+}
+
+static void
+quick_open_dialog_clear_project_files(QuickOpenDialog* self)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GSList* documents;
+
+ gtk_list_store_clear(priv->store);
+ g_hash_table_remove_all(priv->project_files_hash);
+ g_hash_table_remove_all(priv->document_files_hash);
+
+ /* Insert separator */
+ gtk_list_store_insert_with_values(priv->store, NULL, -1,
+ COLUMN_IS_SEPARATOR, TRUE, -1);
+
+ /* Readd all documents. */
+ documents = priv->documents;
+ if (priv->documents)
+ {
+ g_slist_free(priv->documents);
+ priv->documents = NULL;
+ }
+ for (; documents; documents = documents->next)
+ {
+ IAnjutaDocument* doc = documents->data;
+ quick_open_dialog_add_document(self, doc);
+ }
+}
+
+void
+quick_open_dialog_set_project_root(QuickOpenDialog* self, GFile* project_root)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ g_clear_object(&priv->project_root);
+ if (project_root)
+ priv->project_root = g_object_ref(project_root);
+
+ quick_open_dialog_clear_project_files(self);
+
+ if (project_root)
+ gtk_notebook_set_current_page (priv->tree_view_notebook, NOTEBOOK_PAGE_LOADING);
+}
+
+static gboolean
+remove_matching_value_func(gpointer key, gpointer value, gpointer user_data)
+{
+ return value == user_data;
+}
+
+static void
+quick_open_dialog_document_file_changed(QuickOpenDialog* self,
+ IAnjutaDocument* document)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GFile* file, *old_file;
+ char* title;
+ GtkTreeModel* model;
+ gboolean res;
+ GtkTreeIter iter;
+
+ file = ianjuta_file_get_file(IANJUTA_FILE(document), NULL);
+ old_file = g_object_get_data(G_OBJECT(document), "quickopen_oldfile");
+
+ /* Don't do anything if the file hasn't changed. */
+ if ((file == old_file) || (file && old_file && g_file_equal(file, old_file)))
+ {
+ if (file)
+ g_object_unref(file);
+ return;
+ }
+
+ /* Remove the old file from the document_files_hash */
+ g_hash_table_foreach_remove(priv->document_files_hash,
+ remove_matching_value_func, document);
+
+ if (file)
+ {
+ if (priv->project_root && g_file_has_prefix(file, priv->project_root))
+ title = g_file_get_relative_path(priv->project_root, file);
+ else
+ title = g_file_get_path(file);
+
+ g_hash_table_add(priv->document_files_hash, file); // Takes the ref.
+ g_object_set_data_full(G_OBJECT(document), "quickopen_oldfile",
+ g_object_ref(file), g_object_unref);
+ }
+ else
+ {
+ title = g_strdup(ianjuta_document_get_filename(document, NULL));
+ g_object_set_data(G_OBJECT(document), "quickopen_oldfile", NULL);
+ }
+
+
+ /* Find it in the store and update the title. */
+ model = GTK_TREE_MODEL(priv->store);
+ for (res = iter_first_skip_separator(model, &iter);
+ res;
+ res = iter_next_skip_separator(model, &iter))
+ {
+ IAnjutaDocument* doc;
+ gboolean found;
+
+ gtk_tree_model_get(model, &iter, COLUMN_OBJECT, &doc, -1);
+ if (!doc)
+ /* All documents are sorted first so we can stop iterating if it's
+ * not been found yet. */
+ break;
+
+ found = (doc == document);
+ g_object_unref(doc);
+
+ if (found)
+ {
+ gtk_list_store_set(priv->store, &iter, COLUMN_TITLE, title, -1);
+ break;
+ }
+ }
+
+ g_free(title);
+}
+
+static void
+on_document_saved(IAnjutaFile* afile, GFile* file, gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+
+ quick_open_dialog_document_file_changed(self, IANJUTA_DOCUMENT(afile));
+}
+
+static void
+on_document_opened(IAnjutaFile* afile, GError* error, gpointer user_data)
+{
+ QuickOpenDialog* self = user_data;
+
+ if (error)
+ return;
+
+ quick_open_dialog_document_file_changed(self, IANJUTA_DOCUMENT(afile));
+}
+
+void
+quick_open_dialog_remove_document(QuickOpenDialog* self, IAnjutaDocument* document)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GFile* file;
+ GtkTreeModel* model;
+ gboolean res;
+ GtkTreeIter iter;
+
+ if (!IANJUTA_IS_FILE(document))
+ return;
+
+ file = ianjuta_file_get_file(IANJUTA_FILE(document), NULL);
+ if (file)
+ {
+ g_hash_table_remove(priv->document_files_hash, file);
+ g_object_unref(file);
+ }
+
+ g_signal_handlers_disconnect_by_func(document, on_document_opened, self);
+ g_signal_handlers_disconnect_by_func(document, on_document_saved, self);
+
+ /* Find it in the store and remove it */
+ model = GTK_TREE_MODEL(priv->store);
+ for (res = iter_first_skip_separator(model, &iter);
+ res;
+ res = iter_next_skip_separator(model, &iter))
+ {
+ IAnjutaDocument* doc;
+ gboolean found;
+
+ gtk_tree_model_get(model, &iter, COLUMN_OBJECT, &doc, -1);
+ if (!doc)
+ /* All documents are sorted first so we can stop iterating if it's
+ * not been found yet. */
+ break;
+
+ found = (doc == document);
+ g_object_unref(doc);
+
+ if (found)
+ {
+ gtk_list_store_remove(priv->store, &iter);
+ break;
+ }
+ }
+}
+
+void
+quick_open_dialog_add_document(QuickOpenDialog* self, IAnjutaDocument* document)
+{
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GFile* file;
+ char* title;
+
+ if (!IANJUTA_IS_FILE(document))
+ return;
+
+ file = ianjuta_file_get_file(IANJUTA_FILE(document), NULL);
+ if (file)
+ {
+ if (priv->project_root && g_file_has_prefix(file, priv->project_root))
+ title = g_file_get_relative_path(priv->project_root, file);
+ else
+ title = g_file_get_path(file);
+
+ g_hash_table_add(priv->document_files_hash, file);
+ g_object_set_data_full(G_OBJECT(document), "quickopen_oldfile",
+ g_object_ref(file), g_object_unref);
+ }
+ else
+ {
+ title = g_strdup(ianjuta_document_get_filename(document, NULL));
+ g_object_set_data(G_OBJECT(document), "quickopen_oldfile", NULL);
+ }
+
+ gtk_list_store_insert_with_values(priv->store, NULL, -1, COLUMN_TITLE, title,
+ COLUMN_IS_DOCUMENT, TRUE, COLUMN_OBJECT, document, -1);
+
+ g_free(title);
+
+ g_signal_connect(document, "opened", G_CALLBACK(on_document_opened), self);
+
+ if (IANJUTA_IS_FILE_SAVABLE(document))
+ g_signal_connect(document, "saved", G_CALLBACK(on_document_saved), self);
+
+ priv->documents = g_slist_prepend(priv->documents, document);
+}
+
+QuickOpenDialog*
+quick_open_dialog_new(void)
+{
+ return g_object_new(QUICK_TYPE_OPEN_DIALOG, NULL);
+}
+
+static void
+quick_open_dialog_init (QuickOpenDialog* self)
+{
+ QuickOpenDialogPrivate* priv;
+ GtkBuilder* builder;
+ GError* err = NULL;
+ GtkGrid* grid;
+ GtkCellRenderer* pixbuf_renderer, *text_renderer;
+
+ self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self, QUICK_TYPE_OPEN_DIALOG,
+ QuickOpenDialogPrivate);
+
+ gtk_window_set_title(GTK_WINDOW(self), _("Quick Open"));
+ gtk_window_set_modal(GTK_WINDOW(self), TRUE);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(self), TRUE);
+ gtk_widget_set_size_request(GTK_WIDGET(self), 400, 300);
+
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
+
+ g_signal_connect(self, "show", G_CALLBACK(on_dialog_show), self);
+
+ builder = gtk_builder_new();
+ if (!gtk_builder_add_from_file(builder, BUILDER_FILE, &err))
+ g_error("Couldn't load builder file: %s", err->message);
+
+ grid = GTK_GRID(gtk_builder_get_object(builder, "grid"));
+ gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(self))),
+ GTK_WIDGET(grid));
+
+#if GTK_CHECK_VERSION(3, 6, 0)
+ /* Add search entry here since glade doesn't support it yet. */
+ priv->filter_entry = GTK_ENTRY(gtk_search_entry_new());
+#else
+ priv->filter_entry = gtk_entry_new();
+#endif
+ gtk_widget_show(GTK_WIDGET(priv->filter_entry));
+ gtk_grid_attach(grid, GTK_WIDGET(priv->filter_entry), 0, 0, 1, 1);
+ g_signal_connect(priv->filter_entry, "changed",
+ G_CALLBACK(on_filter_changed), self);
+ g_signal_connect(priv->filter_entry, "key-press-event",
+ G_CALLBACK(on_filter_entry_key_press_event), self);
+
+ priv->tree_view_notebook = GTK_NOTEBOOK(gtk_builder_get_object(builder, "treeview_notebook"));
+ priv->tree_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview"));
+ g_signal_connect(priv->tree_view, "row-activated",
+ G_CALLBACK(on_tree_view_row_activated), self);
+
+ pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
+ g_object_set(pixbuf_renderer, "icon-name", "text-x-generic", NULL);
+ gtk_tree_view_insert_column_with_attributes(priv->tree_view, 0, NULL,
+ pixbuf_renderer, "visible", COLUMN_IS_DOCUMENT, NULL);
+
+ text_renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(priv->tree_view, 1, NULL,
+ text_renderer, "text", COLUMN_TITLE, NULL);
+
+ gtk_tree_view_set_row_separator_func(priv->tree_view,
+ quick_open_dialog_row_separator_func, NULL, NULL);
+
+ priv->store = GTK_LIST_STORE(g_object_ref(gtk_builder_get_object(builder, "liststore")));
+ gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(priv->store),
+ quick_open_dialog_tree_sort_func, NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->store),
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+
+ priv->filter_model = GTK_TREE_MODEL_FILTER(
+ gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->store), NULL));
+ gtk_tree_model_filter_set_visible_func(priv->filter_model,
+ quick_open_dialog_tree_visible_func, self, NULL);
+
+ priv->project_files_hash = g_hash_table_new_full(g_file_hash, (GEqualFunc)g_file_equal,
+ g_object_unref, NULL);
+ priv->document_files_hash = g_hash_table_new_full(g_file_hash, (GEqualFunc)g_file_equal,
+ g_object_unref, NULL);
+
+ quick_open_dialog_clear_project_files(self);
+}
+
+static void
+quick_open_dialog_finalize (GObject* object)
+{
+ QuickOpenDialog* self = (QuickOpenDialog*)object;
+ QuickOpenDialogPrivate* priv = self->priv;
+
+ GSList* l;
+
+ if (priv->filter_changed_timeout)
+ {
+ g_source_remove(priv->filter_changed_timeout);
+ priv->filter_changed_timeout = 0;
+ }
+
+ g_hash_table_unref(priv->project_files_hash);
+ g_hash_table_unref(priv->document_files_hash);
+
+ for (l = priv->documents; l; l = l->next)
+ {
+ IAnjutaDocument* doc = l->data;
+
+ g_signal_handlers_disconnect_by_func(doc, on_document_opened, self);
+ g_signal_handlers_disconnect_by_func(doc, on_document_saved, self);
+ }
+ g_slist_free(priv->documents);
+
+ g_clear_object(&priv->project_root);
+ g_clear_object(&priv->store);
+
+
+ G_OBJECT_CLASS (quick_open_dialog_parent_class)->finalize (object);
+}
+
+static void
+quick_open_dialog_class_init (QuickOpenDialogClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = quick_open_dialog_finalize;
+
+ g_type_class_add_private(klass, sizeof(QuickOpenDialogPrivate));
+}
diff --git a/plugins/quick-open/quick-open-dialog.h b/plugins/quick-open/quick-open-dialog.h
new file mode 100644
index 0000000..6213ae2
--- /dev/null
+++ b/plugins/quick-open/quick-open-dialog.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * quick-open-dialog.h
+ * Copyright (C) 2013 Carl-Anton Ingmarsson <carlantoni src gnome org>
+ *
+ * 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 _QUICK_OPEN_DIALOG_H_
+#define _QUICK_OPEN_DIALOG_H_
+
+#include <gtk/gtk.h>
+#include <libanjuta/interfaces/ianjuta-document.h>
+
+G_BEGIN_DECLS
+
+#define QUICK_TYPE_OPEN_DIALOG (quick_open_dialog_get_type ())
+#define QUICK_OPEN_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), QUICK_TYPE_OPEN_DIALOG, QuickOpenDialog))
+#define QUICK_OPEN_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), QUICK_TYPE_OPEN_DIALOG, QuickOpenDialogClass))
+#define QUICK_IS_OPEN_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), QUICK_TYPE_OPEN_DIALOG))
+#define QUICK_IS_OPEN_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), QUICK_TYPE_OPEN_DIALOG))
+#define QUICK_OPEN_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), QUICK_TYPE_OPEN_DIALOG, QuickOpenDialogClass))
+
+typedef struct _QuickOpenDialogClass QuickOpenDialogClass;
+typedef struct _QuickOpenDialog QuickOpenDialog;
+typedef struct _QuickOpenDialogPrivate QuickOpenDialogPrivate;
+
+
+struct _QuickOpenDialogClass
+{
+ GtkDialogClass parent_class;
+};
+
+struct _QuickOpenDialog
+{
+ GtkDialog parent_instance;
+
+ QuickOpenDialogPrivate* priv;
+};
+
+GType quick_open_dialog_get_type(void) G_GNUC_CONST;
+
+QuickOpenDialog* quick_open_dialog_new(void);
+
+
+void quick_open_dialog_add_document (QuickOpenDialog* self,
+ IAnjutaDocument* document);
+
+void quick_open_dialog_remove_document (QuickOpenDialog* self,
+ IAnjutaDocument* document);
+
+void quick_open_dialog_set_project_root (QuickOpenDialog* self,
+ GFile* project_root);
+
+void quick_open_dialog_add_project_files (QuickOpenDialog* self,
+ GSList* files);
+
+void quick_open_dialog_add_project_file (QuickOpenDialog* self,
+ GFile* file);
+
+void quick_open_dialog_add_project_file (QuickOpenDialog* self, GFile* file);
+
+GObject* quick_open_dialog_get_selected_object (QuickOpenDialog* self);
+
+G_END_DECLS
+
+#endif /* _QUICK_OPEN_DIALOG_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]