From 11835c28c3407f9bbd4aa68b875a528d13ff0d73 Mon Sep 17 00:00:00 2001 From: Chip Black Date: Sun, 14 Jun 2009 18:41:51 -0500 Subject: [PATCH] Added "search" architecture 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. --- Config.cs | 26 ++++++++++++++--- Makefile | 2 +- PluginManager.cs | 2 ++ SearchHandler.cs | 57 ++++++++++++++++++++++++++++++++++++++ WebThing.cs | 38 +++++++++++++++++++------ WebThingPlugin.cs | 8 ++++++ plugins/DefaultPage.cs | 4 +-- plugins/GoogleSearch.cs | 10 +++++++ plugins/Makefile | 2 +- plugins/MiddleClickOpen.cs | 2 +- plugins/Session.cs | 2 +- plugins/Vimish.cs | 42 ++++++++++++++++------------ 12 files changed, 159 insertions(+), 36 deletions(-) create mode 100644 SearchHandler.cs create mode 100644 plugins/GoogleSearch.cs diff --git a/Config.cs b/Config.cs index 3ffd66d..a4332e1 100644 --- 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 ConfigPath; public static string ConfigPathOut = null; @@ -13,6 +23,7 @@ namespace bytex64.WebThing { public static Dictionary> PluginOptions; public static string[] Arguments; public static List Plugins; + public static List SearchHandlers; private static HashSet CommandLineOptions; private static Regex item_re = new Regex(@"^(?:([\w.]+)\.)?(\w+)$"); @@ -24,6 +35,7 @@ namespace bytex64.WebThing { PluginOptions = new Dictionary>(); CommandLineOptions = new HashSet(); Plugins = new List(); + SearchHandlers = new List(); // Set up ConfigPath IDictionary Env = Environment.GetEnvironmentVariables(); @@ -53,8 +65,7 @@ namespace bytex64.WebThing { PluginOptions[plugin] = new Dictionary(); 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; diff --git a/Makefile b/Makefile index 249504a..d28b428 100644 --- 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 diff --git a/PluginManager.cs b/PluginManager.cs index 551d5fd..56bfe55 100644 --- a/PluginManager.cs +++ b/PluginManager.cs @@ -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 index 0000000..54d5009 --- /dev/null +++ b/SearchHandler.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace bytex64.WebThing { + public class SearchHandler { + WebThing wt; + Dictionary Handlers; + + public SearchHandler(WebThing wt) { + Handlers = new Dictionary(); + 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; + } + } + } +} diff --git a/WebThing.cs b/WebThing.cs index 08cfb1b..5e2000b 100644 --- a/WebThing.cs +++ b/WebThing.cs @@ -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; } } } diff --git a/WebThingPlugin.cs b/WebThingPlugin.cs index 6f23e9d..a201b86 100644 --- a/WebThingPlugin.cs +++ b/WebThingPlugin.cs @@ -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); + } } diff --git a/plugins/DefaultPage.cs b/plugins/DefaultPage.cs index 3e99bb1..b534fe1 100644 --- a/plugins/DefaultPage.cs +++ b/plugins/DefaultPage.cs @@ -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 index 0000000..7b1b542 --- /dev/null +++ b/plugins/GoogleSearch.cs @@ -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)); + } +} diff --git a/plugins/Makefile b/plugins/Makefile index 2f78065..36d32b0 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -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 diff --git a/plugins/MiddleClickOpen.cs b/plugins/MiddleClickOpen.cs index e67e795..aefdd1a 100644 --- a/plugins/MiddleClickOpen.cs +++ b/plugins/MiddleClickOpen.cs @@ -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!? } diff --git a/plugins/Session.cs b/plugins/Session.cs index 02c9771..8a4a198 100644 --- a/plugins/Session.cs +++ b/plugins/Session.cs @@ -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 { diff --git a/plugins/Vimish.cs b/plugins/Vimish.cs index 65490f8..51c2cfc 100644 --- a/plugins/Vimish.cs +++ b/plugins/Vimish.cs @@ -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(); -- 2.25.1