[regexxer] Completion of all entries (files, regex, substitution) with the last 10 used.



commit 1e61b5acbca8c229b862cbda10d7ee2eccf05fe5
Author: Fabien Parent <parent f gmail com>
Date:   Thu Sep 24 20:40:03 2009 +0200

    Completion of all entries (files, regex, substitution) with the last 10 used.

 Makefile.am            |    2 +
 src/completionstack.cc |  107 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/completionstack.h  |   76 ++++++++++++++++++++++++++++++++++
 src/globalstrings.h    |    3 +
 src/mainwindow.cc      |   79 +++++++++++++++++++++++++++--------
 src/mainwindow.h       |   18 +++++++-
 ui/mainwindow.ui       |   51 ++++++++++------------
 ui/regexxer.schemas.in |   55 ++++++++++++++++++++++++-
 8 files changed, 341 insertions(+), 50 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a291242..c420083 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,6 +31,8 @@ SUBDIRS = po
 bin_PROGRAMS = src/regexxer
 
 src_regexxer_SOURCES =		\
+	src/completionstack.cc \
+	src/completionstack.h \
 	src/controller.cc	\
 	src/controller.h	\
 	src/filebuffer.cc	\
diff --git a/src/completionstack.cc b/src/completionstack.cc
new file mode 100644
index 0000000..1735948
--- /dev/null
+++ b/src/completionstack.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2009  Fabien Parent  <parent f gmail com>
+ *
+ * This file is part of regexxer.
+ *
+ * regexxer 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.
+ *
+ * regexxer 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 regexxer; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "completionstack.h"
+
+#include <gtkmm/liststore.h>
+
+namespace Regexxer
+{
+
+CompletionStack::CompletionStack(const std::list<Glib::ustring>& stack) :
+  infinite_stack_(true),
+  stack_size_(0),
+  completion_column_model_(),
+  completion_model_(Gtk::ListStore::create(completion_column_model_))
+{
+  push(stack);
+}
+
+CompletionStack::CompletionStack(unsigned int stack_size, const std::list<Glib::ustring>& stack) :
+  infinite_stack_(false),
+  stack_size_(stack_size),
+  completion_column_model_(),
+  completion_model_(Gtk::ListStore::create(completion_column_model_))
+{
+  push(stack);
+}
+
+void CompletionStack::push(const std::list<Glib::ustring> values)
+{
+  for (std::list<Glib::ustring>::const_reverse_iterator item = values.rbegin();
+       item != values.rend(); item++)
+  {
+    push(*item);
+  }
+}
+
+void CompletionStack::push(const Glib::ustring value)
+{
+  if (value.empty())
+    return;
+  
+  Gtk::ListStore::Children children = completion_model_->children();
+  for (Gtk::ListStore::Children::iterator i = children.begin(); i != children.end(); i++)
+  {
+    if (i->get_value(completion_column_model_.value_) == value)
+    {
+      completion_model_->move(i, children.begin());
+      return;
+    }
+  }
+  
+  Gtk::TreeModel::Row row = *(completion_model_->prepend());
+  row[completion_column_model_.value_] = value;
+  
+  if (!infinite_stack_ && children.size() > stack_size_)
+  {
+    Gtk::ListStore::Children::iterator last_element = --(children.end());
+    completion_model_->erase(last_element);
+  }
+}
+
+std::list<Glib::ustring> CompletionStack::get_stack()
+{
+  std::list<Glib::ustring> return_stack;
+  Gtk::ListStore::Children children = completion_model_->children();
+  for (Gtk::ListStore::Children::iterator i = children.begin(); i != children.end(); i++)
+  {
+    return_stack.push_back(i->get_value(completion_column_model_.value_));
+  }
+  
+  return return_stack;
+}
+
+Glib::RefPtr<Gtk::ListStore> CompletionStack::get_completion_model()
+{
+  return completion_model_;
+}
+
+Gtk::TreeModelColumn<Glib::ustring> CompletionStack::get_completion_column()
+{
+	return completion_column_model_.value_;
+}
+
+CompletionStack::~CompletionStack()
+{
+  
+}
+
+} // namespace Regexxer
diff --git a/src/completionstack.h b/src/completionstack.h
new file mode 100644
index 0000000..aa4cd8d
--- /dev/null
+++ b/src/completionstack.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009  Fabien Parent  <parent f gmail com>
+ *
+ * This file is part of regexxer.
+ *
+ * regexxer 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.
+ *
+ * regexxer 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 regexxer; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef REGEXXER_COMPLETIONSTACK_H_INCLUDED
+#define REGEXXER_COMPLETIONSTACK_H_INCLUDED
+
+#include <gtkmm/treemodel.h>
+
+#include <list>
+
+namespace Gtk
+{
+class ListStore;
+}
+
+
+namespace Regexxer
+{
+
+class CompletionStack
+{
+public:
+  CompletionStack(const std::list<Glib::ustring>& stack = std::list<Glib::ustring>());
+  CompletionStack(unsigned int stack_size, const std::list<Glib::ustring>& stack = std::list<Glib::ustring>());
+  virtual ~CompletionStack();
+  
+  void push(const Glib::ustring value);
+  void push(const std::list<Glib::ustring> values);
+  
+  Glib::RefPtr<Gtk::ListStore> get_completion_model();
+  Gtk::TreeModelColumn<Glib::ustring> get_completion_column();
+  
+  std::list<Glib::ustring> get_stack();
+  
+protected:
+  CompletionStack(const CompletionStack&);
+  CompletionStack operator=(const CompletionStack&);
+
+private:
+  class CompletionColumnModel : public Gtk::TreeModel::ColumnRecord
+  {
+  public:
+    CompletionColumnModel()
+    {
+      add(value_);
+    }
+
+    Gtk::TreeModelColumn<Glib::ustring> value_;
+  };
+  
+  bool infinite_stack_;
+  unsigned int stack_size_;
+  CompletionColumnModel completion_column_model_;
+  Glib::RefPtr<Gtk::ListStore> completion_model_;
+};
+
+} // namespace Regexxer
+
+#endif /* REGEXXER_COMPLETIONSTACK_H_INCLUDED */
diff --git a/src/globalstrings.h b/src/globalstrings.h
index 4a4c467..8cf336d 100644
--- a/src/globalstrings.h
+++ b/src/globalstrings.h
@@ -38,6 +38,9 @@ const char *const conf_key_match_color         = "/apps/regexxer/match_color";
 const char *const conf_key_current_match_color = "/apps/regexxer/current_match_color";
 const char *const conf_key_toolbar_style       = "/apps/regexxer/toolbar_style";
 const char *const conf_key_fallback_encoding   = "/apps/regexxer/fallback_encoding";
+const char *const conf_key_substitution_patterns = "/apps/regexxer/substitution_patterns";
+const char *const conf_key_regex_patterns      = "/apps/regexxer/regex_patterns";
+const char *const conf_key_files_patterns      = "/apps/regexxer/files_patterns";
 
 const char *const ui_mainwindow_filename       = REGEXXER_PKGDATADIR G_DIR_SEPARATOR_S
                                                  "mainwindow.ui";
diff --git a/src/mainwindow.cc b/src/mainwindow.cc
index b404452..7927084 100644
--- a/src/mainwindow.cc
+++ b/src/mainwindow.cc
@@ -183,10 +183,16 @@ MainWindow::MainWindow()
   table_file_             (0),
   button_folder_          (0),
   combo_entry_pattern_    (Gtk::manage(new Gtk::ComboBoxEntryText())),
+  combo_entry_pattern_completion_stack_(10, Gnome::Conf::Client::get_default_client()->get_string_list(conf_key_files_patterns)),
+  combo_entry_pattern_completion_ (Gtk::EntryCompletion::create()),
   button_recursive_       (0),
   button_hidden_          (0),
   entry_regex_            (0),
+  entry_regex_completion_stack_(10, Gnome::Conf::Client::get_default_client()->get_string_list(conf_key_regex_patterns)),
+  entry_regex_completion_ (Gtk::EntryCompletion::create()),
   entry_substitution_     (0),
+  entry_substitution_completion_stack_(10, Gnome::Conf::Client::get_default_client()->get_string_list(conf_key_substitution_patterns)),
+  entry_substitution_completion_ (Gtk::EntryCompletion::create()),
   button_multiple_        (0),
   button_caseless_        (0),
   filetree_               (Gtk::manage(new FileTree())),
@@ -201,7 +207,10 @@ MainWindow::MainWindow()
   undo_stack_             (new UndoStack())
 {
   load_xml();
-
+  
+  entry_regex_ = comboboxentry_regex_->get_entry();
+  entry_substitution_ = comboboxentry_substitution_->get_entry();
+  
   textview_->set_buffer(FileBuffer::create());
   window_->set_title(PACKAGE_NAME);
 
@@ -234,19 +243,35 @@ void MainWindow::initialize(const InitState& init)
   const bool folder_exists = button_folder_->set_current_folder(folder);
 
   combo_entry_pattern_->get_entry()->set_text((init.pattern.empty()) ? Glib::ustring(1, '*') : init.pattern);
-  entry_regex_  ->set_text(init.regex);
+  combo_entry_pattern_->set_model(combo_entry_pattern_completion_stack_.get_completion_model());
+  combo_entry_pattern_->set_text_column(combo_entry_pattern_completion_stack_.get_completion_column());
+  combo_entry_pattern_->get_entry()->set_completion(combo_entry_pattern_completion_);
+  
+  entry_regex_->set_text(init.regex);
+  entry_regex_->set_completion(entry_regex_completion_);
   entry_substitution_->set_text(init.substitution);
+  entry_substitution_->set_completion(entry_substitution_completion_);
+
+  comboboxentry_regex_->set_model(entry_regex_completion_stack_.get_completion_model());
+  comboboxentry_regex_->set_text_column(entry_regex_completion_stack_.get_completion_column());
+  comboboxentry_substitution_->set_model(entry_substitution_completion_stack_.get_completion_model());
+  comboboxentry_substitution_->set_text_column(entry_substitution_completion_stack_.get_completion_column());
+  
+  combo_entry_pattern_completion_->set_model(combo_entry_pattern_completion_stack_.get_completion_model());
+  combo_entry_pattern_completion_->set_text_column(combo_entry_pattern_completion_stack_.get_completion_column());
+  combo_entry_pattern_completion_->set_inline_completion(true);
+  combo_entry_pattern_completion_->set_popup_completion(false);
+  
+  entry_regex_completion_->set_model(entry_regex_completion_stack_.get_completion_model());
+  entry_regex_completion_->set_text_column(entry_regex_completion_stack_.get_completion_column());
+  entry_regex_completion_->set_inline_completion(true);
+  entry_regex_completion_->set_popup_completion(false);
+  
 
-  combo_entry_pattern_->append_text("*.[ch]");
-  combo_entry_pattern_->append_text("*.{c,cc,cpp,cxx,c++,C,h,hh,hpp,hxx,h++}");
-  combo_entry_pattern_->append_text("*.{ccg,hg}");
-  combo_entry_pattern_->append_text("*.idl");
-  combo_entry_pattern_->append_text("*.{java,jsp}");
-  combo_entry_pattern_->append_text("*.{pl,pm,cgi}");
-  combo_entry_pattern_->append_text("*.py");
-  combo_entry_pattern_->append_text("*.php[0-9]?");
-  combo_entry_pattern_->append_text("*.{html,htm,shtml,js,wml}");
-  combo_entry_pattern_->append_text("*.{xml,xsl,css,dtd,xsd}");
+  entry_substitution_completion_->set_model(entry_substitution_completion_stack_.get_completion_model());
+  entry_substitution_completion_->set_text_column(entry_substitution_completion_stack_.get_completion_column());
+  entry_substitution_completion_->set_inline_completion(true);
+  entry_substitution_completion_->set_popup_completion(false);
 
   button_recursive_->set_active(!init.no_recursive);
   button_hidden_   ->set_active(init.hidden);
@@ -282,8 +307,8 @@ void MainWindow::load_xml()
   xml->get_widget("button_folder",       button_folder_);
   xml->get_widget("button_recursive",    button_recursive_);
   xml->get_widget("button_hidden",       button_hidden_);
-  xml->get_widget("entry_regex",         entry_regex_);
-  xml->get_widget("entry_substitution",  entry_substitution_);
+  xml->get_widget("comboboxentry_regex",         comboboxentry_regex_);
+  xml->get_widget("comboboxentry_substitution",  comboboxentry_substitution_);
   xml->get_widget("button_multiple",     button_multiple_);
   xml->get_widget("button_caseless",     button_caseless_);
   xml->get_widget("scrollwin_textview",  scrollwin_textview_);
@@ -505,6 +530,10 @@ void MainWindow::on_find_files()
     if (dialog.run() != Gtk::RESPONSE_OK)
       return;
   }
+  
+  const Glib::ustring files_regex = combo_entry_pattern_->get_entry()->get_text();
+  combo_entry_pattern_completion_stack_.push(files_regex);
+  Gnome::Conf::Client::get_default_client()->set_string_list(conf_key_files_patterns, combo_entry_pattern_completion_stack_.get_stack());
 
   std::string folder = button_folder_->get_filename();
 
@@ -519,7 +548,7 @@ void MainWindow::on_find_files()
 
   try
   {
-    Pcre::Pattern pattern (Util::shell_pattern_to_regex(combo_entry_pattern_->get_entry()->get_text()), Pcre::DOTALL);
+    Pcre::Pattern pattern (Util::shell_pattern_to_regex(files_regex), Pcre::DOTALL);
 
     filetree_->find_files(folder, pattern,
                           button_recursive_->get_active(),
@@ -548,7 +577,10 @@ void MainWindow::on_exec_search()
   const Glib::ustring regex = entry_regex_->get_text();
   const bool caseless = button_caseless_->get_active();
   const bool multiple = button_multiple_->get_active();
-
+  
+  entry_regex_completion_stack_.push(regex);
+  Gnome::Conf::Client::get_default_client()->set_string_list(conf_key_regex_patterns, entry_regex_completion_stack_.get_stack());
+  
   try
   {
     Pcre::Pattern pattern (regex, (caseless) ? Pcre::CASELESS : Pcre::CompileOptions(0));
@@ -733,7 +765,10 @@ void MainWindow::on_replace()
 {
   if (const FileBufferPtr buffer = FileBufferPtr::cast_static(textview_->get_buffer()))
   {
-    buffer->replace_current_match(entry_substitution_->get_text());
+    const Glib::ustring substitution = entry_substitution_->get_text();
+    entry_substitution_completion_stack_.push(substitution);
+    Gnome::Conf::Client::get_default_client()->set_string_list(conf_key_substitution_patterns, entry_substitution_completion_stack_.get_stack());
+    buffer->replace_current_match(substitution);
     on_go_next(true);
   }
 }
@@ -742,7 +777,10 @@ void MainWindow::on_replace_file()
 {
   if (const FileBufferPtr buffer = FileBufferPtr::cast_static(textview_->get_buffer()))
   {
-    buffer->replace_all_matches(entry_substitution_->get_text());
+    const Glib::ustring substitution = entry_substitution_->get_text();
+    entry_substitution_completion_stack_.push(substitution);
+    Gnome::Conf::Client::get_default_client()->set_string_list(conf_key_substitution_patterns, entry_substitution_completion_stack_.get_stack());
+    buffer->replace_all_matches(substitution);
     statusline_->set_match_index(0);
   }
 }
@@ -751,7 +789,10 @@ void MainWindow::on_replace_all()
 {
   BusyAction busy (*this);
 
-  filetree_->replace_all_matches(entry_substitution_->get_text());
+  const Glib::ustring substitution = entry_substitution_->get_text();
+  entry_substitution_completion_stack_.push(substitution);
+  Gnome::Conf::Client::get_default_client()->set_string_list(conf_key_substitution_patterns, entry_substitution_completion_stack_.get_stack());
+  filetree_->replace_all_matches(substitution);
   statusline_->set_match_index(0);
 }
 
diff --git a/src/mainwindow.h b/src/mainwindow.h
index a915e86..2deafa3 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -24,6 +24,7 @@
 #include "controller.h"
 #include "filebuffer.h"
 #include "sharedptr.h"
+#include "completionstack.h"
 
 #include <sigc++/sigc++.h>
 #include <glibmm/refptr.h>
@@ -41,10 +42,11 @@ class Entry;
 class FileChooser;
 class Toolbar;
 class Window;
-class ComboBoxEntryText;
+class ComboBoxEntry;
 class VBox;
 class ScrolledWindow;
 class Table;
+class EntryCompletion;
 }
 
 namespace gtksourceview
@@ -100,12 +102,24 @@ private:
 
   Gtk::Table*                 table_file_;
   Gtk::FileChooser*           button_folder_;
-  Gtk::ComboBoxEntryText*     combo_entry_pattern_;
+  
+  Gtk::ComboBoxEntry*         combo_entry_pattern_;
+  CompletionStack             combo_entry_pattern_completion_stack_;
+  Glib::RefPtr<Gtk::EntryCompletion> combo_entry_pattern_completion_;
+  
   Gtk::CheckButton*           button_recursive_;
   Gtk::CheckButton*           button_hidden_;
 
+  Gtk::ComboBoxEntry*         comboboxentry_regex_;
   Gtk::Entry*                 entry_regex_;
+  CompletionStack             entry_regex_completion_stack_;
+  Glib::RefPtr<Gtk::EntryCompletion> entry_regex_completion_;
+  
+  Gtk::ComboBoxEntry*         comboboxentry_substitution_;
   Gtk::Entry*                 entry_substitution_;
+  CompletionStack             entry_substitution_completion_stack_;
+  Glib::RefPtr<Gtk::EntryCompletion> entry_substitution_completion_;
+  
   Gtk::CheckButton*           button_multiple_;
   Gtk::CheckButton*           button_caseless_;
 
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index b8470db..cf3b8b2 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -551,7 +551,6 @@
                             <property name="visible">True</property>
                             <property name="xalign">0</property>
                             <property name="label" translatable="yes">Search:</property>
-                            <property name="mnemonic_widget">entry_regex</property>
                           </object>
                           <packing>
                             <property name="x_options">GTK_FILL</property>
@@ -563,7 +562,6 @@
                             <property name="visible">True</property>
                             <property name="xalign">0</property>
                             <property name="label" translatable="yes">Replace:</property>
-                            <property name="mnemonic_widget">entry_substitution</property>
                           </object>
                           <packing>
                             <property name="top_attach">1</property>
@@ -573,32 +571,6 @@
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkEntry" id="entry_regex">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="tooltip_text" translatable="yes">A regular expression in Perl syntax</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkEntry" id="entry_substitution">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="tooltip_text" translatable="yes">The new string to substitute. As in Perl, you can refer to parts of the match using $1, $2, etc. or even $+, $&amp;, $` and $'. The operators \l, \u, \L, \U and \E are supported as well.</property>
-                          </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
                           <object class="GtkHBox" id="hbox_options">
                             <property name="visible">True</property>
                             <property name="spacing">6</property>
@@ -659,6 +631,29 @@
                             <property name="y_options"></property>
                           </packing>
                         </child>
+                        <child>
+                          <object class="GtkComboBoxEntry" id="comboboxentry_regex">
+                            <property name="visible">True</property>
+                            <property name="tooltip_text" translatable="yes">A regular expression in Perl syntax</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBoxEntry" id="comboboxentry_substitution">
+                            <property name="visible">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
diff --git a/ui/regexxer.schemas.in b/ui/regexxer.schemas.in
index 93638cc..a68d93e 100644
--- a/ui/regexxer.schemas.in
+++ b/ui/regexxer.schemas.in
@@ -1,7 +1,60 @@
 <?xml version="1.0"?>
 <gconfschemafile>
   <schemalist>
-
+    <schema>
+      <key>/schemas/apps/regexxer/files_patterns</key>
+      <applyto>/apps/regexxer/files_patterns</applyto>
+      <owner>regexxer</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default_value>
+        <value>
+          <list type="string">
+          </list>
+        </value>
+      </default_value>
+      <locale name="C">
+	<short>Regex Patterns</short>
+	<long>List of last pattern used for the file's regex entry.</long>
+      </locale>
+    </schema>
+  
+    <schema>
+      <key>/schemas/apps/regexxer/regex_patterns</key>
+      <applyto>/apps/regexxer/regex_patterns</applyto>
+      <owner>regexxer</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default_value>
+        <value>
+          <list type="string">
+          </list>
+        </value>
+      </default_value>
+      <locale name="C">
+	<short>Regex Patterns</short>
+	<long>List of last pattern used for the regex entry.</long>
+      </locale>
+    </schema>
+    
+    <schema>
+      <key>/schemas/apps/regexxer/substitution_patterns</key>
+      <applyto>/apps/regexxer/substitution_patterns</applyto>
+      <owner>regexxer</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default_value>
+        <value>
+          <list type="string">
+          </list>
+        </value>
+      </default_value>
+      <locale name="C">
+	<short>Regex Patterns</short>
+	<long>List of last pattern used for the substitution entry.</long>
+      </locale>
+    </schema>
+    
     <schema>
       <key>/schemas/apps/regexxer/textview_font</key>
       <applyto>/apps/regexxer/textview_font</applyto>



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