Fix a potential crash in html parsing code. How come it was never reported ?
[beagle.git] / Tiles / TileCanvas.cs
blobd434bfc0ebb3e5363fcdccb5f2bf25e65734435b
1 //
2 // TileCanvas.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
27 using System;
28 using System.Collections;
29 using System.Diagnostics;
30 using System.Reflection;
31 using System.IO;
32 using System.Text;
33 using Beagle.Util;
34 using Gecko;
36 namespace Beagle.Tile {
38 public class TileCanvas : Gecko.WebControl {
40 public event EventHandler PreRenderEvent;
41 public event EventHandler PostRenderEvent;
43 public TileCanvas () : base ()
45 OpenUri += OnOpenUri;
48 private void DispatchAction (string tile_id,
49 string action)
51 Tile t = GetTile (tile_id);
53 if (t == null)
54 return;
57 MethodInfo info = t.GetType().GetMethod (action,
58 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
59 null,
60 CallingConventions.Any,
61 new Type[] {},
62 null);
63 if (info == null) {
64 Console.WriteLine ("Couldn't find method called {0}", action);
65 return;
67 object[] attrs = info.GetCustomAttributes (false);
68 foreach (object attr in attrs) {
69 if (attr is TileActionAttribute) {
70 info.Invoke (t, null);
71 return;
74 Console.WriteLine ("{0} does not have the TileAction attribute");
77 private void OnOpenUri (object o, OpenUriArgs args)
79 string uri = args.AURI;
80 System.Console.WriteLine ("Open URI: {0}", uri);
82 args.RetVal = true;
84 if (DoAction (uri))
85 return;
87 if (uri.StartsWith ("action:")) {
88 int pos1 = "action:".Length;
89 int pos2 = uri.IndexOf ("!");
90 if (pos2 > 0) {
91 string tile_id = uri.Substring (pos1, pos2 - pos1);
92 string action = uri.Substring (pos2 + 1);
93 DispatchAction (tile_id, action);
97 string command = null;
98 string commandArgs = null;
100 if (uri.StartsWith (Uri.UriSchemeHttp)
101 || uri.StartsWith (Uri.UriSchemeHttps)
102 || uri.StartsWith (Uri.UriSchemeFile)) {
103 command = "gnome-open";
104 commandArgs = "'" + uri + "'";
105 } else if (uri.StartsWith (Uri.UriSchemeMailto)) {
106 command = "evolution";
107 commandArgs = uri;
110 if (command != null) {
111 Process p = new Process ();
112 p.StartInfo.UseShellExecute = false;
113 p.StartInfo.FileName = command;
114 if (args != null)
115 p.StartInfo.Arguments = commandArgs;
116 try {
117 p.Start ();
118 } catch (Exception e) {
119 Console.WriteLine ("Unable to run {0}: {1}", command, e);
121 return;
125 return;
128 /////////////////////////////////////////////////
130 Tile root = null;
132 public Tile Root {
133 get { return root; }
134 set {
135 root = value;
136 root.SetChangedHandler (new TileChangedHandler (OnTileChanged));
140 /////////////////////////////////////////////////
142 Hashtable actionTable = null;
143 int actionId = 1;
145 private void ClearActions ()
147 actionTable = new Hashtable ();
148 actionId = 1;
151 private string AddAction (TileActionHandler handler)
153 if (handler == null)
154 return "dynaction:NULL";
155 string key = "dynaction:" + actionId.ToString ();
156 ++actionId;
157 actionTable [key] = handler;
158 return key;
161 private bool DoAction (string key)
163 TileActionHandler handler = (TileActionHandler) actionTable [key];
164 if (handler != null) {
165 handler ();
166 return true;
168 return false;
171 /////////////////////////////////////////////////
173 Hashtable tileTable = null;
175 private void ClearTiles ()
177 tileTable = new Hashtable ();
180 private void CacheTile (Tile tile)
182 tileTable [tile.UniqueKey] = tile;
183 tile.SetChangedHandler (new TileChangedHandler (OnTileChanged));
186 private Tile GetTile (string key)
188 if (key == "")
189 return root;
190 return (Tile) tileTable [key];
193 /////////////////////////////////////////////////
195 private class TileCanvasRenderContext : TileRenderContext {
197 TileCanvas canvas;
198 Tile tileMain;
200 public TileCanvasRenderContext (TileCanvas _canvas, Tile _tile)
202 canvas = _canvas;
203 tileMain = _tile;
204 canvas.CacheTile (tileMain);
207 override public void Write (string markup)
209 canvas.AppendData (markup);
212 override public void Link (string label,
213 TileActionHandler handler)
215 string key = canvas.AddAction (handler);
216 Write ("<a href=\"{0}\">{1}</a>", key, label);
219 override public void Tile (Tile tile)
221 canvas.CacheTile (tile);
222 tile.Render (this);
226 private static ArrayList style_attributes = null;
227 private static ArrayList style_templates = null;
228 private static string preferred_font_family;
229 private static double preferred_font_size;
231 static private void GetFontSettings ()
233 GConf.Client client = new GConf.Client ();
234 string font = null;
236 try {
237 font = client.Get ("/desktop/gnome/interface/font_name") as string;
238 } catch (GConf.NoSuchKeyException) { }
240 // Sans 10 seems to be the default GNOME fallback
241 if (font == null)
242 font = "Sans 10";
244 Pango.FontDescription font_desc = Pango.FontDescription.FromString (font);
246 preferred_font_family = font_desc.Family;
247 preferred_font_size = font_desc.Size / Pango.Scale.PangoScale;
250 static private void ScanAssembly (Assembly assembly)
252 style_attributes = new ArrayList ();
253 foreach (Type type in assembly.GetTypes ()) {
254 if (type.IsSubclassOf (typeof (Tile))) {
255 foreach (object obj in Attribute.GetCustomAttributes (type)) {
256 if (obj is TileStyleAttribute) {
257 style_attributes.Add (obj);
264 private void RenderStyles (TileRenderContext ctx)
266 if (style_attributes == null)
267 ScanAssembly (Assembly.GetExecutingAssembly ());
269 if (style_templates == null) {
270 GetFontSettings ();
272 style_templates = new ArrayList ();
273 foreach (TileStyleAttribute attr in style_attributes) {
274 Template t = new Template (attr.Resource);
275 t["FontFamily"] = preferred_font_family;
276 t["FontSize"] = preferred_font_size.ToString ();
277 style_templates.Add (t);
281 foreach (Template t in style_templates) {
282 ctx.Write (t.ToString ());
286 private void PaintTile (Tile tile)
288 TileCanvasRenderContext ctx;
289 ctx = new TileCanvasRenderContext (this,
290 tile);
292 // IMPORTANT! The <meta> tag has to be in the first
293 // chunk written to the stream or else the encoding
294 // will be wrong!
295 ctx.Write ("<html>\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n");
297 ctx.Write ("<style type=\"text/css\" media=\"screen\">\n");
298 RenderStyles (ctx);
299 ctx.Write ("</style>\n");
301 ctx.Write ("</head>\n");
303 if (tile != null) {
304 tile.Render (ctx);
307 ctx.Write ("</html>");
310 /////////////////////////////////////////////////
312 StringBuilder content;
314 public String Source {
315 get { return content.ToString (); }
318 public new void AppendData (string data)
320 content.Append (data);
323 /////////////////////////////////////////////////
325 private void DoRender ()
327 if (time == null) {
328 time = new Beagle.Util.Stopwatch ();
329 time.Start ();
331 System.Console.WriteLine ("Rendering");
332 if (PreRenderEvent != null)
333 PreRenderEvent (this, new EventArgs ());
335 ClearActions ();
336 ClearTiles ();
338 string mime_type = "text/html";
339 if (Environment.GetEnvironmentVariable ("BEST_DEBUG_HTML") != null)
340 mime_type = "text/plain";
342 content = new StringBuilder ();
343 PaintTile (root);
344 RenderData (content.ToString (), "file:///", mime_type);
346 if (PostRenderEvent != null)
347 PostRenderEvent (this, new EventArgs ());
349 time.Stop ();
350 System.Console.WriteLine ("Done Rendering: {0}",
351 time);
352 time = null;
357 /////////////////////////////////////////////////
359 private uint renderId = 0;
361 public void Render ()
363 lock (this) {
364 if (renderId != 0) {
365 GLib.Source.Remove (renderId);
366 renderId = 0;
368 DoRender ();
372 private bool RenderHandler ()
374 lock (this) {
375 renderId = 0;
376 DoRender ();
378 return false;
381 Beagle.Util.Stopwatch time;
382 public void ScheduleRender ()
384 time = new Beagle.Util.Stopwatch ();
385 time.Start ();
386 lock (this) {
387 if (renderId != 0)
388 return;
389 renderId = GLib.Idle.Add (new GLib.IdleHandler (RenderHandler));
393 private void OnTileChanged (Tile tile)
395 ScheduleRender ();