Added "search" architecture
authorChip Black <bytex64@bytex64.net>
Sun, 14 Jun 2009 23:41:51 +0000 (18:41 -0500)
committerChip Black <bytex64@bytex64.net>
Sun, 14 Jun 2009 23:41:51 +0000 (18:41 -0500)
A new "search" architecture was added, managed by SearchHandler.  In
WebThing, OpenUri*() was changed to simply Open*(), and changed to
handle invalid URIs by sending them through SearchHandler.  Search
handlers are configured with a new SearchHandler config directive.
As a proof-of-concept, a GoogleSearch plugin was created that searches
google.

Plugins were modified to suit the new Open*() calls, and Vimish's
command-line handling was changed to be more flexible.

12 files changed:
Config.cs
Makefile
PluginManager.cs
SearchHandler.cs [new file with mode: 0644]
WebThing.cs
WebThingPlugin.cs
plugins/DefaultPage.cs
plugins/GoogleSearch.cs [new file with mode: 0644]
plugins/Makefile
plugins/MiddleClickOpen.cs
plugins/Session.cs
plugins/Vimish.cs

index 3ffd66d..a4332e1 100644 (file)
--- a/Config.cs
+++ b/Config.cs
@@ -5,6 +5,16 @@ using System.Collections.Generic;
 using System.Text.RegularExpressions;
 
 namespace bytex64.WebThing {
+    public struct SearchHandlerPair {
+        public string Shortcut;
+        public string Plugin;
+
+        public SearchHandlerPair(string s, string p) {
+            Shortcut = s;
+            Plugin = p;
+        }
+    }
+
     public class Config {
         public static List<string> ConfigPath;
         public static string ConfigPathOut = null;
@@ -13,6 +23,7 @@ namespace bytex64.WebThing {
         public static Dictionary<string,Dictionary<string,string>> PluginOptions;
         public static string[] Arguments;
         public static List<string> Plugins;
+        public static List<SearchHandlerPair> SearchHandlers;
 
         private static HashSet<string> CommandLineOptions;
         private static Regex item_re = new Regex(@"^(?:([\w.]+)\.)?(\w+)$");
@@ -24,6 +35,7 @@ namespace bytex64.WebThing {
             PluginOptions = new Dictionary<string,Dictionary<string,string>>();
             CommandLineOptions = new HashSet<string>();
             Plugins = new List<string>();
+            SearchHandlers = new List<SearchHandlerPair>();
 
             // Set up ConfigPath
             IDictionary Env = Environment.GetEnvironmentVariables();
@@ -53,8 +65,7 @@ namespace bytex64.WebThing {
                     PluginOptions[plugin] = new Dictionary<string,string>();
                 PluginOptions[plugin][name] = val;
             } else {                   // Global Option
-                switch(key) {
-                case "Plugin":
+                switch(key.ToLower()) {
                 case "plugin":
                     Plugins.Add(val);
                     break;
@@ -169,12 +180,19 @@ namespace bytex64.WebThing {
 
                 m = file_command_re.Match(line);
                 if (m.Success) {
-                    string cmd = m.Groups[1].Value;
+                    string cmd = m.Groups[1].Value.ToLower();
                     switch(cmd) {
-                    case "Plugin":
                     case "plugin":
                         Plugins.Add(m.Groups[2].Value);
                         break;
+                    case "searchhandler":
+                        string[] args = Regex.Split(m.Groups[2].Value, @"\s+");
+                        if (args.Length != 2) {
+                            Console.WriteLine("Expecting two arguments for SearchHandler.");
+                            break;
+                        }
+                        SearchHandlers.Add(new SearchHandlerPair(args[0], args[1]));
+                        break;
                     default:
                         Console.WriteLine("Unknown Command in {0}: {1}", filename, line);
                         break;
index 249504a..d28b428 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ tags:
 WebThingMain.exe: WebThingMain.cs WebThing.dll SoupSettings.so
        $(CS) $(CSFLAGS) -r:WebThing.dll -out:$@ WebThingMain.cs
 
-WebThing.dll: WebThing.cs WebThingView.cs WebThingPlugin.cs Config.cs PluginManager.cs
+WebThing.dll: WebThing.cs WebThingView.cs WebThingPlugin.cs Config.cs PluginManager.cs SearchHandler.cs
        $(CS) $(CSFLAGS) $(references) -target:library -out:$@ $^
 
 SoupSettings.so: SoupSettings.c
index 551d5fd..56bfe55 100644 (file)
@@ -27,6 +27,8 @@ namespace bytex64.WebThing {
             Type[] types = a.GetTypes();
             foreach (Type t in types) {
                 if (t.IsSubclassOf(typeof(WebThingPlugin))) {
+                    if (Plugins.ContainsKey(t.FullName))
+                        continue;
                     WebThingPlugin p = (WebThingPlugin) a.CreateInstance(t.FullName, false, BindingFlags.ExactBinding, null, null, null, null);
                     p.Init(wt);
                     Plugins[t.FullName] = p;
diff --git a/SearchHandler.cs b/SearchHandler.cs
new file mode 100644 (file)
index 0000000..54d5009
--- /dev/null
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace bytex64.WebThing {
+    public class SearchHandler {
+        WebThing wt;
+        Dictionary<string,ISearchPlugin> Handlers;
+        
+        public SearchHandler(WebThing wt) {
+            Handlers = new Dictionary<string,ISearchPlugin>();
+            this.wt = wt;
+
+            foreach (SearchHandlerPair p in Config.SearchHandlers) {
+                AddHandler(p.Shortcut, p.Plugin);
+            }
+        }
+
+        public void AddHandler(string key, string plugin) {
+            if (Handlers.ContainsKey(key)) {
+                Console.WriteLine("Cannot add search handler {0}: shortcut {1} already registered", plugin, key);
+                return;
+            }
+            if (!wt.Plugins.Plugins.ContainsKey(plugin)) {
+                Console.WriteLine("Cannot add search handler {0}: Plugin for {0} not loaded", plugin);
+                return;
+            }
+
+            WebThingPlugin p = wt.Plugins.Plugins[plugin];
+            Type ptype = p.GetType();
+
+            if (ptype.GetInterface("ISearchPlugin") != null) {
+                Handlers[key] = (ISearchPlugin) p;
+                Console.WriteLine("Added handler {0} for key {1}", plugin, key);
+            } else {
+                Console.WriteLine("Cannot add search handler {0}: {0} Does not implement ISearchPlugin", plugin);
+            }
+        }
+
+        // Call a search handler based on the leading word
+        public string Transform(string search) {
+            Regex get_handler_re = new Regex(@"^([\w-]+)\s+");
+            Match m = get_handler_re.Match(search);
+            if (m.Success) {
+                string key = m.Groups[1].Value;
+                string query = get_handler_re.Replace(search, "");
+                if (!Handlers.ContainsKey(key)) {
+                    Console.WriteLine("Could not search with {0}: No search handler defined", key);
+                    return null;
+                }
+                return Handlers[key].SearchTransform(query);
+            } else {
+                return null;
+            }
+        }
+    }
+}
index 08cfb1b..5e2000b 100644 (file)
@@ -44,6 +44,7 @@ namespace bytex64.WebThing {
         private Gtk.Alignment InteriorOverlay;
 
         public PluginManager Plugins;
+        public SearchHandler Search;
 
         [DllImport ("SoupSettings.dll")]
         private static extern void soup_settings();
@@ -95,10 +96,13 @@ namespace bytex64.WebThing {
             Plugins = new PluginManager(this);
             Plugins.Load();
 
+            // Load Search Handlers
+            Search = new SearchHandler(this);
+
             // Create a new, default WebThingView if one has not already
             // been created.
             if (Tabs.NPages == 0)
-                OpenUriTab("http://dominionofawesome.com/");
+                OpenTab("http://dominionofawesome.com/");
             WebView.GrabFocus();
 
             Application.Run();
@@ -175,20 +179,36 @@ namespace bytex64.WebThing {
         }
 
         // Uri loading
-        public string FixUri(string Uri) {
-            if (!Regex.IsMatch(Uri, @"://")) {
-                return String.Format("http://{0}", Uri);
+        private string GetUri(string query) {
+            Uri u;
+            try {
+                u = new Uri(query);
+                return u.ToString();
+            } catch(UriFormatException) {
+                try {
+                    u = new Uri(String.Format("http://{0}", query));
+                    return u.ToString();
+                } catch (UriFormatException) {
+                    return Search.Transform(query);
+                }
             }
-            return Uri;
         }
 
-        public void OpenUri(string Uri) {
-            wv.Open(FixUri(Uri));
+        public bool Open(string query) {
+            string uri = GetUri(query);
+            if (uri == null) return false;
+            
+            wv.Open(uri);
+            return true;
         }
 
-        public void OpenUriTab(string Uri) {
+        public bool OpenTab(string query) {
             WebThingView wtv = NewTab();
-            wtv.WebView.Open(FixUri(Uri));
+            string uri = GetUri(query);
+            if (uri == null) return false;
+
+            wtv.WebView.Open(uri);
+            return true;
         }
     }
 }
index 6f23e9d..a201b86 100644 (file)
@@ -33,4 +33,12 @@ namespace bytex64.WebThing {
         public virtual void InitWebView(WebView wv) {}
         public virtual void DeinitWebView(WebView wv) {}
     }
+
+    // An interface implemented by plugins that handle search queries
+    // (a search query is anything that doesn't look like a URI)
+    public interface ISearchPlugin {
+        // Convert a search query into a URI.  If this plugin cannot
+        // handle the search, it should return null.
+        string SearchTransform(string search);
+    }
 }
index 3e99bb1..b534fe1 100644 (file)
@@ -6,11 +6,11 @@ public class DefaultPage : WebThingPlugin {
     public override void Init(WebThing wt) {
         if (Config.Arguments.Length > 0) {
             foreach (string arg in Config.Arguments)
-                wt.OpenUriTab(arg);
+                wt.OpenTab(arg);
         } else if (Config.Options.ContainsKey("DefaultPage")) {
             string[] pages = Regex.Split(Config.Options["DefaultPage"], @"\s+");
             foreach (string page in pages)
-                wt.OpenUriTab(page);
+                wt.OpenTab(page);
         }
     }
 }
diff --git a/plugins/GoogleSearch.cs b/plugins/GoogleSearch.cs
new file mode 100644 (file)
index 0000000..7b1b542
--- /dev/null
@@ -0,0 +1,10 @@
+using System;
+using bytex64.WebThing;
+using System.Text.RegularExpressions;
+
+public class GoogleSearch : WebThingPlugin, ISearchPlugin {
+    public string SearchTransform(string search) {
+        string[] words = Regex.Split(search, @"\s+");
+        return String.Format("http://google.com/search?q={0}", String.Join("%20", words));
+    }
+}
index 2f78065..36d32b0 100644 (file)
@@ -1,7 +1,7 @@
 CSFLAGS = -debug
 references = -r:../webkit-sharp.dll -pkg:gtk-sharp-2.0
 
-all: Vimish.dll FFNav.dll DefaultPage.dll LoadProgress.dll MiddleClickOpen.dll QuickSearch.dll Session.dll Fullscreen.dll
+all: Vimish.dll FFNav.dll DefaultPage.dll LoadProgress.dll MiddleClickOpen.dll QuickSearch.dll Session.dll Fullscreen.dll GoogleSearch.dll
 
 clean:
        rm -f *.dll *.mdb *.so
index e67e795..aefdd1a 100644 (file)
@@ -22,7 +22,7 @@ public class MiddleClickOpen : WebThingPlugin {
     private void WebView_ButtonRelease(object o, Gtk.ButtonReleaseEventArgs e) {
         if (e.Event.Button == 2) {
             if (LinkUri != null) {
-                wt.OpenUriTab(LinkUri);
+                wt.OpenTab(LinkUri);
                 wt.WebView.StopLoading();
                 e.RetVal = true;   // WHY DOESN'T THIS WORK!?
             }
index 02c9771..8a4a198 100644 (file)
@@ -33,7 +33,7 @@ public class Session : WebThingPlugin {
             }
             string[] pages = Regex.Split(Options[SessionName], @"\s+");
             foreach (string page in pages) {
-                wt.OpenUriTab(page);
+                wt.OpenTab(page);
             }
             CurrentSession = SessionName;
         } else {
index 65490f8..51c2cfc 100644 (file)
@@ -106,18 +106,28 @@ public class Vimish : WebThingPlugin {
 
     private void command_Activate(object o, EventArgs e) {
         string[] args = Regex.Split(commandline.Text, @"\s+");
-        switch(args[0]) {
+        if (args.Length == 0) return;
+        string cmd = args[0];
+        string[] tmp = new string[args.Length - 1];
+        Array.Copy(args, 1, tmp, 0, tmp.Length);
+        args = tmp;
+        string query = Regex.Replace(commandline.Text, String.Format(@"^{0}\s+", cmd), "");
+
+        switch(cmd) {
         case "close":
             wt.CloseTab();
             break;
         case "open":
-            if (args.Length < 2) return;
-            wt.OpenUri(args[1]);
+            if (args.Length < 1) return;
+            if (!wt.Open(query))
+                Error("Could not open query");
             break;
         case "tabopen":
-            if (args.Length < 2) return;
-            wt.OpenUriTab(args[1]);
-            wt.Tabs.CurrentPage = wt.Tabs.NPages - 1;
+            if (args.Length < 1) return;
+            if (wt.OpenTab(query))
+                wt.Tabs.CurrentPage = wt.Tabs.NPages - 1;
+            else
+                Error("Could not open query");
             break;
         case "n":
             wt.Tabs.NextPage();
@@ -129,10 +139,10 @@ public class Vimish : WebThingPlugin {
             wt.Quit();
             break;
         case "set":
-            if (args.Length == 2)
-                Options[args[1]] = null;
+            if (args.Length == 1)
+                Options[args[0]] = null;
             else
-                Options[args[1]] = args[2];
+                Options[args[0]] = args[1];
             ApplyOptions();
             break;
         case "save":
@@ -140,19 +150,17 @@ public class Vimish : WebThingPlugin {
             break;
         default:
             bool found;
-            if (args.Length > 1) {
-                string[] callargs = new string[args.Length - 1];
-                Array.Copy(args, 1, callargs, 0, args.Length - 1);
-                found = wt.Plugins.Call(args[0], callargs);
+            if (args.Length > 0) {
+                found = wt.Plugins.Call(cmd, args);
                 if (!found)
-                    Error("No function {0}({1}) found", args[0], String.Join(", ", callargs));
+                    Error("No function {0}({1}) found", cmd, String.Join(", ", args));
             } else {
-                found = wt.Plugins.Call(args[0]);
+                found = wt.Plugins.Call(cmd);
                 if (!found)
-                    Error("No function {0}() found", args[0]);
+                    Error("No function {0}() found", cmd);
             }
             if (found)
-                Console.WriteLine("Plugin function {0} called successfully", args[0]);
+                Console.WriteLine("Plugin function {0} called successfully", cmd);
             break;
         }
         CommandlineHide();