[anjuta/search-files] search: Initial "Find in files" dialog work



commit 7180b3d8e1fe95cc0cefb51c9dbdf2d636ec1776
Author: Johannes Schmid <jhs gnome org>
Date:   Fri Feb 10 22:35:39 2012 +0100

    search: Initial "Find in files" dialog work

 plugins/document-manager/Makefile.am               |    6 +-
 plugins/document-manager/action-callbacks.c        |   14 +
 plugins/document-manager/action-callbacks.h        |    2 +
 .../document-manager/anjuta-document-manager.ui    |  225 +++++++++++
 .../document-manager/anjuta-document-manager.xml   |    2 +
 plugins/document-manager/plugin.c                  |   16 +-
 plugins/document-manager/plugin.h                  |    3 +
 plugins/document-manager/search-file-command.c     |  324 +++++++++++++++
 plugins/document-manager/search-file-command.h     |   59 +++
 plugins/document-manager/search-files.c            |  422 ++++++++++++++++++++
 plugins/document-manager/search-files.h            |   57 +++
 11 files changed, 1122 insertions(+), 8 deletions(-)
---
diff --git a/plugins/document-manager/Makefile.am b/plugins/document-manager/Makefile.am
index 1e102e8..b962516 100644
--- a/plugins/document-manager/Makefile.am
+++ b/plugins/document-manager/Makefile.am
@@ -46,7 +46,11 @@ libanjuta_document_manager_la_SOURCES= \
 	search-box.h \
 	search-box.c \
 	anjuta-bookmarks.h \
-	anjuta-bookmarks.c
+	anjuta-bookmarks.c \
+	search-files.c \
+	search-files.h \
+	search-file-command.c \
+	search-file-command.h
 
 gsettings_in_file = org.gnome.anjuta.document-manager.gschema.xml.in
 gsettings_SCHEMAS = $(gsettings_in_file:.xml.in=.xml)
diff --git a/plugins/document-manager/action-callbacks.c b/plugins/document-manager/action-callbacks.c
index 2b64379..3096783 100644
--- a/plugins/document-manager/action-callbacks.c
+++ b/plugins/document-manager/action-callbacks.c
@@ -45,6 +45,7 @@
 #include "plugin.h"
 #include "file_history.h"
 #include "search-box.h"
+#include "search-files.h"
 #include "anjuta-bookmarks.h"
 
 static IAnjutaDocument *
@@ -911,6 +912,19 @@ on_search_popup_regex_search (GtkAction *action, gpointer user_data)
 	search_box_toggle_regex (SEARCH_BOX (search_box), regex_active);
 }
 
+void 
+on_search_find_in_files (GtkAction *action, gpointer user_data)
+{
+	DocmanPlugin *plugin;
+	plugin = ANJUTA_PLUGIN_DOCMAN (user_data);
+
+	if (!plugin->search_files)
+	{
+		plugin->search_files = G_OBJECT (search_files_new (ANJUTA_DOCMAN (plugin->docman)));
+	}
+	search_files_present (SEARCH_FILES(plugin->search_files));
+}
+
 void
 on_editor_add_view_activate (GtkAction *action, gpointer user_data)
 {
diff --git a/plugins/document-manager/action-callbacks.h b/plugins/document-manager/action-callbacks.h
index fba2e87..b25afe7 100644
--- a/plugins/document-manager/action-callbacks.h
+++ b/plugins/document-manager/action-callbacks.h
@@ -103,6 +103,8 @@ void on_search_popup_clear_highlight (GtkAction *action, gpointer user_data);
 
 void on_search_popup_regex_search (GtkAction *action, gpointer user_data);
 
+void on_search_find_in_files (GtkAction *action, gpointer user_data);
+
 void on_next_document (GtkAction *action, gpointer user_data);
 void on_previous_document (GtkAction *action, gpointer user_data);
 
diff --git a/plugins/document-manager/anjuta-document-manager.ui b/plugins/document-manager/anjuta-document-manager.ui
index 827b673..a3a522c 100644
--- a/plugins/document-manager/anjuta-document-manager.ui
+++ b/plugins/document-manager/anjuta-document-manager.ui
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="2.16"/>
+  <!-- interface-requires anjuta 0.0 -->
   <object class="GtkAdjustment" id="adjustment1">
     <property name="lower">1</property>
     <property name="upper">1000</property>
@@ -14,6 +15,48 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkListStore" id="file_type_model">
+    <columns>
+      <!-- column-name Name -->
+      <column type="gchararray"/>
+      <!-- column-name mime-types -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">All Files</col>
+        <col id="1" translatable="yes"></col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">C/C++ Source files</col>
+        <col id="1" translatable="yes">text/x-c</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Vala Source files</col>
+        <col id="1" translatable="yes">text/x-vala</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Python Source files</col>
+        <col id="1" translatable="yes">text/x-python,application/x-extension-wsgi</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Javascript Source files</col>
+        <col id="1" translatable="yes">application/javascript</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">XML files</col>
+        <col id="1" translatable="yes">application/xml</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">User interface files</col>
+        <col id="1" translatable="yes">application/x-glade,application/x-designer</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Source files</col>
+        <col id="1" translatable="yes">text/source</col>
+      </row>
+    </data>
+  </object>
   <object class="GtkListStore" id="model1">
     <columns>
       <!-- column-name gchararray -->
@@ -350,4 +393,186 @@
       </object>
     </child>
   </object>
+  <object class="GtkWindow" id="search_files">
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkGrid" id="main_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="row_spacing">5</property>
+        <property name="column_spacing">5</property>
+        <property name="n_rows">4</property>
+        <property name="n_columns">2</property>
+        <child>
+          <object class="GtkEntry" id="search_entry">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="valign">start</property>
+            <property name="hexpand">True</property>
+            <property name="invisible_char">â</property>
+            <property name="invisible_char_set">True</property>
+            <property name="primary_icon_stock">gtk-find</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="GtkEntry" id="replace_entry">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="valign">start</property>
+            <property name="hexpand">True</property>
+            <property name="invisible_char">â</property>
+            <property name="invisible_char_set">True</property>
+            <property name="primary_icon_stock">gtk-find-and-replace</property>
+          </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>
+          <object class="GtkButton" id="search_button">
+            <property name="label">gtk-find</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="halign">start</property>
+            <property name="valign">start</property>
+            <property name="use_action_appearance">False</property>
+            <property name="use_stock">True</property>
+            <signal name="clicked" handler="search_files_search_clicked" swapped="yes"/>
+          </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>
+        <child>
+          <object class="GtkButton" id="replace_button">
+            <property name="label" translatable="yes">Replace</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="halign">start</property>
+            <property name="valign">start</property>
+            <property name="use_action_appearance">False</property>
+            <signal name="clicked" handler="search_files_replace_clicked" swapped="yes"/>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">1</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="grid2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="valign">start</property>
+            <property name="hexpand">True</property>
+            <property name="row_spacing">5</property>
+            <property name="column_spacing">5</property>
+            <property name="n_rows">1</property>
+            <property name="n_columns">2</property>
+            <child>
+              <object class="GtkComboBox" id="file_type_combo">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="model">file_type_model</property>
+                <property name="active">0</property>
+                <property name="entry_text_column">0</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>
+            <child>
+              <object class="AnjutaPmChooserButton" id="project_combo">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="valign">start</property>
+                <property name="use_action_appearance">False</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>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="find_files_button">
+            <property name="label" translatable="yes">Find Files</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="halign">start</property>
+            <property name="valign">start</property>
+            <property name="use_action_appearance">False</property>
+            <signal name="clicked" handler="search_files_find_files_clicked" swapped="yes"/>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">2</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkTreeView" id="files_tree">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="vexpand">True</property>
+                <property name="enable_search">False</property>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection" id="treeview-selection"/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">3</property>
+            <property name="width">2</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/plugins/document-manager/anjuta-document-manager.xml b/plugins/document-manager/anjuta-document-manager.xml
index e544955..ed81080 100644
--- a/plugins/document-manager/anjuta-document-manager.xml
+++ b/plugins/document-manager/anjuta-document-manager.xml
@@ -49,6 +49,8 @@
 							<menuitem name="QuickSearchPrevious" action="ActionEditSearchFindPrevious"/>							
 							<menuitem name="QuickReplace" action="ActionEditSearchReplace"/>
 							<separator name="separator8"/>
+							<menuitem name="FindFiles" action="ActionEditFindFiles"/>							
+							<separator name="separator9"/>
 						</placeholder>
 					</menu>
 				</placeholder>
diff --git a/plugins/document-manager/plugin.c b/plugins/document-manager/plugin.c
index 1aaaa52..ec0fe8a 100644
--- a/plugins/document-manager/plugin.c
+++ b/plugins/document-manager/plugin.c
@@ -48,6 +48,7 @@
 #include "action-callbacks.h"
 #include "plugin.h"
 #include "search-box.h"
+#include "search-files.h"
 #include "anjuta-bookmarks.h"
 
 #define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-document-manager.xml"
@@ -278,7 +279,10 @@ static GtkActionEntry actions_search[] = {
 	G_CALLBACK (on_search_previous)},
   { "ActionSearchboxPopupClearHighlight", GTK_STOCK_FIND, N_("Clear Highlight"),
 	NULL, N_("Clear all highlighted text"),
-	G_CALLBACK (on_search_popup_clear_highlight)}
+	G_CALLBACK (on_search_popup_clear_highlight)},
+  { "ActionEditFindFiles", GTK_STOCK_FIND_AND_REPLACE, N_("Find in Files"),
+	NULL, N_("Search in project files"),
+	G_CALLBACK (on_search_find_in_files)}
 };
 
 static GtkToggleActionEntry actions_searchbox_popup[] = {
@@ -1649,7 +1653,7 @@ activate_plugin (AnjutaPlugin *plugin)
 	gint i;
 	static gboolean initialized = FALSE;
 	GList *actions, *act;
-
+	
 	DEBUG_PRINT ("%s", "DocmanPlugin: Activating document manager pluginâ");
 
 	dplugin = ANJUTA_PLUGIN_DOCMAN (plugin);
@@ -1659,7 +1663,7 @@ activate_plugin (AnjutaPlugin *plugin)
 	docman = anjuta_docman_new (dplugin);
 	dplugin->docman = docman;
 
-	ANJUTA_DOCMAN(docman)->shell = plugin->shell;
+	ANJUTA_DOCMAN(docman)->shell = anjuta_plugin_get_shell(plugin);
 	g_signal_connect (G_OBJECT (docman), "document-added",
 					  G_CALLBACK (on_document_added), plugin);
 	g_signal_connect (G_OBJECT (docman), "document-changed",
@@ -1729,16 +1733,14 @@ activate_plugin (AnjutaPlugin *plugin)
 	/* Create the default searchbox instance, but don't yet put it into
 	   dplugin->vbox, to prevent the box being displayed at session start */
 	dplugin->search_box = search_box_new (ANJUTA_DOCMAN (docman));
-
+	
 	/* Setup popup menu */
 	popup_menu = gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui),
 											"/PopupDocumentManager");
 	g_assert (popup_menu != NULL && GTK_IS_MENU (popup_menu));
 	anjuta_docman_set_popup_menu (ANJUTA_DOCMAN (docman), popup_menu);
 
-	if (!initialized)
-	{
-	}
+
 	/* Connect to save session */
 	g_signal_connect (G_OBJECT (plugin->shell), "save-session",
 					  G_CALLBACK (on_session_save), plugin);
diff --git a/plugins/document-manager/plugin.h b/plugins/document-manager/plugin.h
index 4644750..2cc127f 100644
--- a/plugins/document-manager/plugin.h
+++ b/plugins/document-manager/plugin.h
@@ -46,6 +46,9 @@ struct _DocmanPlugin{
 
 	/* SearchBox */
 	GtkWidget* search_box;
+
+    /* SearchFiles */
+    GObject* search_files;
 	
 	/* Base box of the widget */
 	GtkWidget* vbox;
diff --git a/plugins/document-manager/search-file-command.c b/plugins/document-manager/search-file-command.c
new file mode 100644
index 0000000..b106ebb
--- /dev/null
+++ b/plugins/document-manager/search-file-command.c
@@ -0,0 +1,324 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta
+ * Copyright (C) Johannes Schmid 2012 <jhs gnome org>
+ * 
+ * anjuta 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * anjuta 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/>.
+ */
+
+#include "search-file-command.h"
+#include <string.h>
+
+struct _SearchFileCommandPrivate
+{
+	GFile* file;
+	gchar* pattern;
+	gchar* replace;
+	gboolean regex;
+
+	gint n_matches;
+};
+
+enum
+{
+	PROP_0,
+	PROP_FILE,
+	PROP_PATTERN,
+	PROP_REPLACE,
+	PROP_REGEX
+};
+
+#define BUFFER_SIZE 1024
+
+static void
+search_file_command_save (SearchFileCommand* cmd, const gchar* new_content, GError **error)
+{
+	GFileOutputStream* ostream;
+
+	ostream = g_file_replace (cmd->priv->file,
+	                          NULL,
+	                          TRUE,
+	                          G_FILE_CREATE_NONE,
+	                          NULL,
+	                          error);
+	if (*error)
+	{
+		return;
+	}
+	/* TODO: Convert to original encoding */
+	g_output_stream_write_all (G_OUTPUT_STREAM(ostream),
+	                           new_content,
+	                           strlen(new_content),
+	                           NULL,
+	                           NULL,
+	                           error);
+	g_object_unref (ostream);
+}
+
+static gchar*
+search_file_command_load (SearchFileCommand* cmd, GError **error)
+{
+	GString* content;
+	GFileInputStream* istream;
+	gchar buffer[BUFFER_SIZE];
+	gssize bytes_read;
+	
+	istream = g_file_read (cmd->priv->file, NULL, error);
+	if (*error)
+	{
+		return NULL;
+	}
+	
+	/* Read file into buffer */
+	content = g_string_new (NULL);
+	while ((bytes_read = g_input_stream_read (G_INPUT_STREAM (istream),
+	                                          buffer,
+	                                          BUFFER_SIZE,
+	                                          NULL,
+	                                          error)))
+	{
+		if (*error)
+		{
+			g_string_free (content, TRUE);
+			g_object_unref (istream);
+			return NULL;
+		}
+		/* TODO: Non-UTF8 files... */
+		g_string_append_len (content, buffer, bytes_read);
+	}
+	g_object_unref (istream);
+
+	return g_string_free (content, FALSE);
+}
+
+static guint
+search_file_command_run (AnjutaCommand* anjuta_cmd)
+{
+	SearchFileCommand* cmd = SEARCH_FILE_COMMAND(anjuta_cmd);
+	GError* error = NULL;
+	gchar* pattern;
+	gchar* replace;
+	gchar* content;
+	
+	g_return_val_if_fail (cmd->priv->file != NULL && G_IS_FILE (cmd->priv->file), 1);
+
+	cmd->priv->n_matches = 0;
+	
+	content = search_file_command_load (cmd, &error);
+	if (error)
+	{
+		int code = error->code;
+		g_error_free (error);
+		return code;
+	}
+	
+	if (!cmd->priv->regex)
+	{
+		pattern = g_regex_escape_string (cmd->priv->pattern, -1);
+		if (cmd->priv->replace)
+			replace = g_regex_escape_string (cmd->priv->replace, -1);
+		else
+			replace = NULL;
+	}
+	else
+	{
+		pattern = g_strdup (cmd->priv->pattern);
+		if (cmd->priv->replace)
+			replace = g_strdup (cmd->priv->replace);
+		else
+			replace = NULL;
+	}
+
+	if (!replace)
+	{
+		GRegex *regex;
+		GMatchInfo *match_info;
+
+		regex = g_regex_new (pattern, 0, 0, NULL);
+		g_regex_match (regex, content, 0, &match_info);
+		while (g_match_info_matches (match_info))
+		{
+			cmd->priv->n_matches++;
+			g_match_info_next (match_info, NULL);
+		}
+		g_match_info_free (match_info);
+		g_regex_unref (regex);                      
+	}
+	else
+	{
+		gchar* new_content;
+		GRegex *regex;
+		
+		regex = g_regex_new (pattern, 0, 0, NULL);
+		new_content = g_regex_replace (regex, content, -1, 0, replace, 0, NULL);
+		g_regex_unref (regex);
+
+		search_file_command_save (cmd, new_content, &error);
+		g_free (new_content);
+
+		if (error)
+		{
+			anjuta_async_command_set_error_message (anjuta_cmd, error->message);
+			return 1;
+		}
+	}
+	g_free (content);
+
+	return 0;
+}
+
+G_DEFINE_TYPE (SearchFileCommand, search_file_command, ANJUTA_TYPE_ASYNC_COMMAND);
+
+static void
+search_file_command_init (SearchFileCommand *cmd)
+{
+	cmd->priv = G_TYPE_INSTANCE_GET_PRIVATE (cmd, SEARCH_TYPE_FILE_COMMAND, SearchFileCommandPrivate);
+
+	cmd->priv->file = NULL;
+	cmd->priv->pattern = NULL;
+	cmd->priv->replace = NULL;
+	cmd->priv->regex = FALSE;
+}
+
+static void
+search_file_command_finalize (GObject *object)
+{
+	/* TODO: Add deinitalization code here */
+
+	G_OBJECT_CLASS (search_file_command_parent_class)->finalize (object);
+}
+
+static void
+search_file_command_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	SearchFileCommand* cmd;
+	
+	g_return_if_fail (SEARCH_IS_FILE_COMMAND (object));
+
+	cmd = SEARCH_FILE_COMMAND (object);
+
+	switch (prop_id)
+	{
+	case PROP_FILE:
+		if (cmd->priv->file)
+				g_object_unref (cmd->priv->file);
+		cmd->priv->file = g_value_dup_object (value);
+		break;
+	case PROP_PATTERN:
+		g_free (cmd->priv->pattern);
+		cmd->priv->pattern = g_value_dup_string (value);
+		break;
+	case PROP_REPLACE:
+		g_free (cmd->priv->replace);
+		cmd->priv->replace = g_value_dup_string (value);
+		break;
+	case PROP_REGEX:
+		cmd->priv->regex = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+search_file_command_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	SearchFileCommand* cmd;
+	
+	g_return_if_fail (SEARCH_IS_FILE_COMMAND (object));
+
+	cmd = SEARCH_FILE_COMMAND (object);
+	
+	switch (prop_id)
+	{
+	case PROP_FILE:
+		g_value_set_object (value, cmd->priv->file);
+		break;
+	case PROP_PATTERN:
+		g_value_set_string (value, cmd->priv->pattern);
+		break;
+	case PROP_REPLACE:
+		g_value_set_string (value, cmd->priv->replace);
+		break;
+	case PROP_REGEX:
+		g_value_set_boolean (value, cmd->priv->regex);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+search_file_command_class_init (SearchFileCommandClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+	AnjutaCommandClass* command_class = ANJUTA_COMMAND_CLASS(klass);
+	
+	object_class->finalize = search_file_command_finalize;
+	object_class->set_property = search_file_command_set_property;
+	object_class->get_property = search_file_command_get_property;
+
+	g_object_class_install_property (object_class,
+	                                 PROP_FILE,
+	                                 g_param_spec_object ("file",
+	                                                      "filename",
+	                                                      "Filename to search in",
+	                                                      G_TYPE_FILE,
+	                                                      G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_PATTERN,
+	                                 g_param_spec_string ("pattern", "", "",
+	                                                       "",
+	                                                       G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (object_class,
+	                                 PROP_PATTERN,
+	                                 g_param_spec_string ("replace", "", "",
+	                                                       "",
+	                                                       G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));	
+
+	g_object_class_install_property (object_class,
+	                                 PROP_REGEX,
+	                                 g_param_spec_boolean ("regex", "", "",
+	                                                       FALSE,
+	                                                       G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	command_class->run = search_file_command_run;
+
+	g_type_class_add_private (klass, sizeof(SearchFileCommandPrivate));
+}
+
+
+SearchFileCommand*
+search_file_command_new (GFile* file, const gchar* pattern, const gchar* replace, gboolean regex)
+{
+	SearchFileCommand* command;
+
+	command = SEARCH_FILE_COMMAND (g_object_new (SEARCH_TYPE_FILE_COMMAND,
+	                                             "file", file,
+	                                             "pattern", pattern,
+	                                             "replace", replace,
+	                                             "regex", regex, NULL));
+	return command;
+}
+
+gint
+search_command_get_n_matches (SearchFileCommand* cmd)
+{
+	g_return_val_if_fail (cmd != NULL && !SEARCH_IS_FILE_COMMAND (cmd), 0);
+
+	return cmd->priv->n_matches;
+}
\ No newline at end of file
diff --git a/plugins/document-manager/search-file-command.h b/plugins/document-manager/search-file-command.h
new file mode 100644
index 0000000..ef606af
--- /dev/null
+++ b/plugins/document-manager/search-file-command.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta
+ * Copyright (C) Johannes Schmid 2012 <jhs gnome org>
+ * 
+ * anjuta 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * anjuta 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/>.
+ */
+
+#ifndef _SEARCH_FILE_COMMAND_H_
+#define _SEARCH_FILE_COMMAND_H_
+
+#include <libanjuta/anjuta-async-command.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SEARCH_TYPE_FILE_COMMAND             (search_file_command_get_type ())
+#define SEARCH_FILE_COMMAND(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEARCH_TYPE_FILE_COMMAND, SearchFileCommand))
+#define SEARCH_FILE_COMMAND_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), SEARCH_TYPE_FILE_COMMAND, SearchFileCommandClass))
+#define SEARCH_IS_FILE_COMMAND(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEARCH_TYPE_FILE_COMMAND))
+#define SEARCH_IS_FILE_COMMAND_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), SEARCH_TYPE_FILE_COMMAND))
+#define SEARCH_FILE_COMMAND_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), SEARCH_TYPE_FILE_COMMAND, SearchFileCommandClass))
+
+typedef struct _SearchFileCommandClass SearchFileCommandClass;
+typedef struct _SearchFileCommand SearchFileCommand;
+typedef struct _SearchFileCommandPrivate SearchFileCommandPrivate;
+
+struct _SearchFileCommandClass
+{
+	AnjutaAsyncCommandClass parent_class;
+};
+
+struct _SearchFileCommand
+{
+	AnjutaAsyncCommand parent_instance;
+
+	SearchFileCommandPrivate* priv;
+};
+
+GType search_file_command_get_type (void) G_GNUC_CONST;
+SearchFileCommand* search_file_command_new (GFile* file, 
+                                            const gchar* pattern, 
+                                            const gchar* replace, gboolean regex);
+gint search_command_get_n_matches (SearchFileCommand* cmd);
+
+G_END_DECLS
+
+#endif /* _SEARCH_FILE_COMMAND_H_ */
diff --git a/plugins/document-manager/search-files.c b/plugins/document-manager/search-files.c
new file mode 100644
index 0000000..0162400
--- /dev/null
+++ b/plugins/document-manager/search-files.c
@@ -0,0 +1,422 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta
+ * Copyright (C) Johannes Schmid 2012 <jhs gnome org>
+ * 
+ * anjuta 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * anjuta 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/>.
+ */
+
+#include "search-files.h"
+#include <libanjuta/interfaces/ianjuta-project-manager.h>
+#include <libanjuta/interfaces/ianjuta-project-chooser.h>
+
+#define BUILDER_FILE PACKAGE_DATA_DIR"/glade/anjuta-document-manager.ui"
+
+struct _SearchFilesPrivate
+{
+	GtkBuilder* builder;
+
+	GtkWidget* main_box;
+	
+	GtkWidget* search_button;
+	GtkWidget* replace_button;
+	GtkWidget* find_files_button;
+	
+	GtkWidget* search_entry;
+	GtkWidget* replace_entry;
+	
+	GtkWidget* files_combo;
+	GtkWidget* project_combo;
+	GtkWidget* file_type_combo;
+
+	GtkWidget* files_tree;
+	GtkTreeModel* files_model;
+
+	GtkWidget* files_tree_check;
+	
+	AnjutaDocman* docman;
+	GtkWidget* dialog;
+};
+
+enum
+{
+	COLUMN_SELECTED,
+	COLUMN_FILENAME,
+	COLUMN_COUNT,
+	COLUMN_PULSE,
+	COLUMN_SPINNER,
+	COLUMN_FILE
+};
+
+G_DEFINE_TYPE (SearchFiles, search_files, G_TYPE_OBJECT);
+
+void search_files_search_clicked (SearchFiles* sf);
+void search_files_replace_clicked (SearchFiles* sf);
+void search_files_find_files_clicked (SearchFiles* sf);
+
+static void
+search_files_get_files (GFile* parent, GList** files, IAnjutaProjectManager* pm)
+{
+	GList* node;
+	GList* children = ianjuta_project_manager_get_children(pm, parent, NULL);
+	for (node = children;node != NULL; node = g_list_next(node))
+	{
+		search_files_get_files(G_FILE(node), files, pm);
+		g_message (g_file_get_path (G_FILE(node)));
+	}
+	g_list_foreach (children, (GFunc)g_object_unref, NULL);
+	g_list_free(children);
+}
+
+static void
+search_files_check_column_clicked (SearchFiles* sf,
+                                   GtkCheckButton* button)
+{
+
+}
+
+static void
+search_files_check_column_toggled (SearchFiles* sf,
+                                   GtkCellRenderer* renderer)
+{
+
+}
+
+void
+search_files_search_clicked (SearchFiles* sf)
+{
+
+}
+
+void
+search_files_replace_clicked (SearchFiles* sf)
+{
+
+}
+
+void
+search_files_find_files_clicked (SearchFiles* sf)
+{
+	GFile* selected;
+	IAnjutaProjectManager* pm;
+	GList* files;
+	GList* file;
+
+	gchar* project_uri = NULL;
+	GFile* project_file = NULL;
+	
+	g_return_if_fail (sf != NULL && SEARCH_IS_FILES (sf));
+	
+	gtk_list_store_clear(GTK_LIST_STORE (sf->priv->files_model));
+
+	pm = anjuta_shell_get_interface (sf->priv->docman->shell,
+	                                 IAnjutaProjectManager,
+	                                 NULL);
+	files = ianjuta_project_manager_get_elements(pm,
+	                                             ANJUTA_PROJECT_SOURCE,
+	                                             NULL);
+	anjuta_shell_get (sf->priv->docman->shell,
+	                  IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
+	                  G_TYPE_STRING,
+	                  &project_uri, NULL);
+	
+	if (project_uri)
+		project_file = g_file_new_for_uri (project_uri);
+	g_free (project_uri);
+	
+	for (file = files; file != NULL; file = g_list_next (file))
+	{
+		GtkTreeIter iter;
+
+		gchar* display_name = NULL;
+
+		if (project_file)
+		{
+			display_name = g_file_get_relative_path (project_file,
+			                                         G_FILE (file->data));
+			if (!display_name)
+				continue;
+		}
+#if 0
+		if (!display_name)
+			display_name = g_file_get_path (G_FILE (file->data));
+		if (!display_name)
+			display_name = g_file_get_uri (G_FILE (file->data));
+#endif	
+		gtk_list_store_append(GTK_LIST_STORE (sf->priv->files_model),
+		                      &iter);
+		gtk_list_store_set (GTK_LIST_STORE (sf->priv->files_model), &iter,
+		                    COLUMN_SELECTED, TRUE,
+		                    COLUMN_FILENAME, display_name,
+		                    COLUMN_FILE, file->data,
+		                    COLUMN_COUNT, 0,
+		                    COLUMN_SPINNER, FALSE,
+		                    COLUMN_PULSE, FALSE, -1);
+	}
+	g_object_unref (project_file);
+	
+	g_list_foreach (files, (GFunc) g_object_unref, NULL);
+	g_list_free (files);
+}
+
+static void
+search_files_render_count (GtkTreeViewColumn *tree_column,
+                           GtkCellRenderer *cell,
+                           GtkTreeModel *tree_model,
+                           GtkTreeIter *iter,
+                           gpointer data)
+{
+	int count;
+	gchar* count_str;
+
+	gtk_tree_model_get (tree_model, iter,
+	                    COLUMN_COUNT, &count,
+	                    -1);
+	count_str = g_strdup_printf("%d", count);
+	g_object_set (cell, "text", count_str, NULL);
+	g_free (count_str);
+}
+
+static void
+search_files_update_ui (SearchFiles* sf)
+{
+	GtkTreeIter iter;
+	gboolean file_available = FALSE;
+	
+	if (gtk_tree_model_get_iter_first(sf->priv->files_model, &iter))
+	{
+		do
+		{
+			gboolean selected;
+			gtk_tree_model_get (sf->priv->files_model, &iter,
+			                    COLUMN_SELECTED, &selected, -1);
+			if (selected)
+			{
+				file_available = TRUE;
+				break;
+			}
+		}
+		while (gtk_tree_model_iter_next(sf->priv->files_model, &iter));
+	}
+
+	gtk_widget_set_sensitive (sf->priv->search_button, file_available);
+	gtk_widget_set_sensitive (sf->priv->replace_button, file_available);	
+}
+
+static void
+search_files_init_tree (SearchFiles* sf)
+{
+	GtkTreeViewColumn* column_select;
+	GtkTreeViewColumn* column_filename;
+	GtkTreeViewColumn* column_count;
+
+	GtkCellRenderer* selection_renderer;
+	GtkCellRenderer* filename_renderer;
+	GtkCellRenderer* count_renderer;
+	
+	column_select = gtk_tree_view_column_new();
+	sf->priv->files_tree_check = gtk_check_button_new();
+	gtk_widget_show (sf->priv->files_tree_check);
+	gtk_tree_view_column_set_widget(column_select,
+	                                sf->priv->files_tree_check);
+	g_signal_connect_swapped (sf->priv->files_tree_check, "clicked",
+	                          G_CALLBACK(search_files_check_column_clicked), sf);
+	selection_renderer = gtk_cell_renderer_toggle_new ();
+	gtk_tree_view_column_pack_start(column_select,
+	                                selection_renderer,
+	                                FALSE);
+	gtk_tree_view_column_add_attribute(column_select,
+	                                   selection_renderer,
+	                                   "active",
+	                                   COLUMN_SELECTED);
+	g_signal_connect_swapped (selection_renderer, "toggled",
+	                          G_CALLBACK(search_files_check_column_toggled), sf);
+	
+	column_filename = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_expand(column_filename,
+	                                TRUE);
+	gtk_tree_view_column_set_title (column_filename, _("Filename"));
+	filename_renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(column_filename,
+	                                filename_renderer,
+	                                TRUE);
+	gtk_tree_view_column_add_attribute(column_filename,
+	                                   filename_renderer,
+	                                   "text",
+	                                   COLUMN_FILENAME);
+
+	column_count = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_title (column_count, "#");
+	count_renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(column_count,
+	                                count_renderer,
+	                                TRUE);
+	gtk_tree_view_column_set_cell_data_func(column_count,
+	                                        count_renderer,
+	                                        search_files_render_count,
+	                                        NULL,
+	                                        NULL);
+
+	sf->priv->files_model = GTK_TREE_MODEL (gtk_list_store_new (6,
+	                                                            G_TYPE_BOOLEAN,
+	                                                            G_TYPE_STRING,
+	                                                            G_TYPE_INT,
+	                                                            G_TYPE_BOOLEAN,
+	                                                            G_TYPE_BOOLEAN,
+	                                                            G_TYPE_FILE));
+
+	g_signal_connect_swapped (sf->priv->files_model,
+	                          "row-inserted",
+	                          G_CALLBACK (search_files_update_ui),
+	                          sf);
+	g_signal_connect_swapped (sf->priv->files_model,
+	                          "row-deleted",
+	                          G_CALLBACK (search_files_update_ui),
+	                          sf);
+	g_signal_connect_swapped (sf->priv->files_model,
+	                          "row-changed",
+	                          G_CALLBACK (search_files_update_ui),
+	                          sf);		
+	
+	gtk_tree_view_set_model (GTK_TREE_VIEW (sf->priv->files_tree), sf->priv->files_model);
+	gtk_tree_view_append_column(GTK_TREE_VIEW (sf->priv->files_tree),
+	                            column_select);
+	gtk_tree_view_append_column(GTK_TREE_VIEW (sf->priv->files_tree),
+	                            column_filename);
+	gtk_tree_view_append_column(GTK_TREE_VIEW (sf->priv->files_tree),
+	                            column_count);
+}
+
+static void
+search_files_init (SearchFiles* sf)
+{
+	GError* error = NULL;
+	GtkCellRenderer* combo_renderer;
+	
+	sf->priv = 
+		G_TYPE_INSTANCE_GET_PRIVATE (sf, SEARCH_TYPE_FILES, SearchFilesPrivate);
+
+	sf->priv->builder = gtk_builder_new();
+	gtk_builder_add_from_file(sf->priv->builder, BUILDER_FILE, &error);
+
+	if (error)
+	{
+		g_warning("Could load ui file for search files: %s",
+		          error->message);
+		g_error_free(error);
+		return;
+	}
+
+	combo_renderer = gtk_cell_renderer_text_new();
+	
+	sf->priv->main_box = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                         "main_box"));
+	sf->priv->search_button = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                         "search_button"));
+	sf->priv->replace_button = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                         "replace_button"));
+	sf->priv->find_files_button = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                                 "find_files_button"));
+	sf->priv->search_entry = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                            "search_entry"));
+	sf->priv->replace_entry = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                             "replace_entry"));
+	sf->priv->project_combo = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                             "project_combo"));
+	sf->priv->file_type_combo = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                             "file_type_combo"));
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (sf->priv->file_type_combo),
+	                           combo_renderer, TRUE);
+	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (sf->priv->file_type_combo),
+	                               combo_renderer, "text", 0);
+	sf->priv->files_tree = GTK_WIDGET (gtk_builder_get_object(sf->priv->builder,
+	                                                          "files_tree"));	
+
+	search_files_init_tree(sf);
+	
+	gtk_builder_connect_signals(sf->priv->builder, sf);
+	
+	g_object_ref (sf->priv->main_box);
+	gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (sf->priv->main_box)),
+	                      sf->priv->main_box);
+
+	search_files_update_ui(sf);
+}
+
+static void
+search_files_finalize (GObject* object)
+{
+	SearchFiles* sf = SEARCH_FILES(object);
+
+	g_object_unref (sf->priv->main_box);
+	g_object_unref (sf->priv->builder);
+	
+	G_OBJECT_CLASS (search_files_parent_class)->finalize (object);
+}
+
+static void
+search_files_class_init (SearchFilesClass* klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+	GObjectClass* parent_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = search_files_finalize;
+
+	g_type_class_add_private(klass, sizeof(SearchFilesPrivate));
+}
+
+static void
+search_files_project_loaded (SearchFiles* sf, IAnjutaProjectManager *pm, GError* e)
+{
+	if (!e)
+	{
+		ianjuta_project_chooser_set_project_model(IANJUTA_PROJECT_CHOOSER(sf->priv->project_combo),
+		                                          pm,
+		                                          ANJUTA_PROJECT_GROUP,
+		                                          NULL);
+	}
+}
+
+SearchFiles* 
+search_files_new (AnjutaDocman* docman)
+{
+	AnjutaShell* shell = docman->shell;
+	GObject* obj = g_object_new (SEARCH_TYPE_FILES, NULL);
+	SearchFiles* sf = SEARCH_FILES(obj);
+	IAnjutaProjectManager* pm = anjuta_shell_get_interface(shell,
+	                                                       IAnjutaProjectManager,
+	                                                       NULL);
+	search_files_project_loaded(sf, pm, NULL);
+	g_signal_connect_swapped (pm, "project-loaded",
+	                          G_CALLBACK (search_files_project_loaded), NULL);
+
+	anjuta_shell_add_widget(shell, sf->priv->main_box,
+	                        "search_files",
+	                        _("Find in files"),
+	                        GTK_STOCK_FIND_AND_REPLACE,
+	                        ANJUTA_SHELL_PLACEMENT_BOTTOM, NULL);
+
+	sf->priv->docman = docman;
+	
+	gtk_widget_show_all (sf->priv->main_box);
+	
+	return sf; 
+}
+
+void search_files_present (SearchFiles* sf)
+{
+	anjuta_shell_present_widget(sf->priv->docman->shell,
+	                            sf->priv->main_box,
+	                            NULL);
+}
\ No newline at end of file
diff --git a/plugins/document-manager/search-files.h b/plugins/document-manager/search-files.h
new file mode 100644
index 0000000..cecc569
--- /dev/null
+++ b/plugins/document-manager/search-files.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta
+ * Copyright (C) Johannes Schmid 2012 <jhs gnome org>
+ * 
+ * anjuta 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * anjuta 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/>.
+ */
+
+#ifndef _SEARCH_FILES_H_
+#define _SEARCH_FILES_H_
+
+#include <anjuta-docman.h>
+
+G_BEGIN_DECLS
+
+#define SEARCH_TYPE_FILES             (search_files_get_type ())
+#define SEARCH_FILES(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEARCH_TYPE_FILES, SearchFiles))
+#define SEARCH_FILES_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), SEARCH_TYPE_FILES, SearchFilesClass))
+#define SEARCH_IS_FILES(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEARCH_TYPE_FILES))
+#define SEARCH_IS_FILES_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), SEARCH_TYPE_FILES))
+#define SEARCH_FILES_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), SEARCH_TYPE_FILES, SearchFilesClass))
+
+typedef struct _SearchFilesClass SearchFilesClass;
+typedef struct _SearchFiles SearchFiles;
+typedef struct _SearchFilesPrivate SearchFilesPrivate;
+
+struct _SearchFilesClass
+{
+	GObjectClass parent_class;
+};
+
+struct _SearchFiles
+{
+	GObject parent_instance;
+
+	SearchFilesPrivate* priv;
+};
+
+GType search_files_get_type (void) G_GNUC_CONST;
+SearchFiles* search_files_new (AnjutaDocman* docman);
+
+void search_files_present (SearchFiles* files);
+
+G_END_DECLS
+
+#endif /* _SEARCH_FILES_H_ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]