Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

AdiumMessageStyle.cs

/*
 * Galaxium Messenger
 * Copyright (C) 2008 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program 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.
 *
 * This program 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, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

using Anculus.Core;

using Galaxium.Core;
using Galaxium.Gui;
using Galaxium.Protocol;

namespace Galaxium.AdiumThemes
{
      public delegate string SubstitutionDelegate (string param, object val);
      
      public class AdiumSubstitutionCollection : Dictionary<string, SubstitutionDelegate>
      {
      }
      
      public class AdiumMessageStyle
      {
            static Regex _regexSubstitutions = new Regex (@"%([A-Za-z0-9]*)(\{(.*)\})?%", RegexOptions.Compiled);
            
            static string _defaultTemplateFilename;
            
            string _archiveFilename;
            string _cacheDir;
            string _contentsDir;
            string _resourcesDir;
            string _variantsDir;
            string _incomingDir;
            string _outgoingDir;
            string _templatesDir;
            string _plistFilename;
            string _mainCssFilename;
            string _headerFilename;
            string _footerFilename;
            string _statusFilename;
            string _defaultIncomingDisplayIconFilename;
            string _defaultOutgoingDisplayIconFilename;
            
            PListDict _plistRoot;
            
            string _name;
            string _infoString;
            string _defaultVariant;
            string _displayNameForNoVariant;
            
            List<string> _variants = new List<string> ();
            
            public string ArchiveFilename
            {
                  get { return _archiveFilename; }
            }
            
            public string Name
            {
                  get { return _name; }
            }
            
            public string InfoString
            {
                  get { return _infoString; }
            }
            
            public string DisplayNameForNoVariant
            {
                  get { return _displayNameForNoVariant; }
            }
            
            public string DefaultIncomingDisplayIconFilename
            {
                  get { return _defaultIncomingDisplayIconFilename; }
            }
            
            public string DefaultOutgoingDisplayIconFilename
            {
                  get { return _defaultOutgoingDisplayIconFilename; }
            }
            
            public List<string> Variants
            {
                  get { return _variants; }
            }
            
            public string DefaultVariant
            {
                  get
                  {
                        if ((!string.IsNullOrEmpty (_defaultVariant)) && _variants.Contains (_defaultVariant))
                              return _defaultVariant;
                        
                        if (!string.IsNullOrEmpty (_displayNameForNoVariant))
                              return _displayNameForNoVariant;
                        
                        if (_variants.Count > 0)
                              return _variants[0];
                        
                        return string.Empty;
                  }
            }
            
            static AdiumMessageStyle ()
            {
                  // Write the default template to disk
                  
                  _defaultTemplateFilename = Path.Combine (AdiumMessageStyleFactory._cacheStylesPath, "Template.html");
                  
                  Stream templateStream = Assembly.GetCallingAssembly ().GetManifestResourceStream ("Template.html");
                  byte[] data = new byte[templateStream.Length];
                  templateStream.Read (data, 0, data.Length);
                  
                  File.WriteAllBytes (_defaultTemplateFilename, data);
            }
            
            public AdiumMessageStyle (string archiveFilename)
            {
                  _archiveFilename = archiveFilename;
                  
                  Extract ();
                  FindDirectories ();
                  LoadPList ();
                  FindVariants ();
            }
            
            void Extract ()
            {
                  ThrowUtility.ThrowIfFalse ("Unsupported archive " + _archiveFilename, ArchiveUtility.IsFileSupported (_archiveFilename));
                  
                  _cacheDir = Path.Combine (AdiumMessageStyleFactory._cacheStylesPath, ArchiveUtility.GetArchiveNameWithoutExtension (_archiveFilename));
                  ThrowUtility.ThrowIfFalse ("Unable to extract " + _archiveFilename, ArchiveUtility.Extract (_archiveFilename, _cacheDir));
            }
            
            void DeleteCache ()
            {
                  if (Directory.Exists (_cacheDir))
                        Directory.Delete (_cacheDir, true);
            }
            
            void FindDirectories ()
            {
                  _contentsDir = ArchiveUtility.FindContentsDir (_cacheDir, ".adiummessagestyle");
                  ThrowUtility.ThrowIfEmpty ("Unable to find contents directory of " + _archiveFilename, _contentsDir);
                  
                  _plistFilename = FileNoCase (Path.Combine (_contentsDir, "Info.plist"));
                  ThrowUtility.ThrowIfFalse ("Unable to find Info.plist of " + _archiveFilename, File.Exists (_plistFilename));
                  
                  _resourcesDir = DirNoCase (Path.Combine (_contentsDir, "Resources"));
                  ThrowUtility.ThrowIfFalse ("Unable to find resources directory of " + _archiveFilename, Directory.Exists (_resourcesDir));
                  
                  _statusFilename = FileNoCase (Path.Combine (_resourcesDir, "Status.html"));
                  ThrowUtility.ThrowIfFalse ("Unable to find Status.html of " + _archiveFilename, File.Exists (_statusFilename));
                  
                  _mainCssFilename = FileNoCase (Path.Combine (_resourcesDir, "main.css"));
                  _headerFilename = FileNoCase (Path.Combine (_resourcesDir, "Header.html"));
                  _footerFilename = FileNoCase (Path.Combine (_resourcesDir, "Footer.html"));
                                    
                  _variantsDir = DirNoCase (Path.Combine (_resourcesDir, "Variants"));
                  
                  _incomingDir = DirNoCase (Path.Combine (_resourcesDir, "Incoming"));
                  ThrowUtility.ThrowIfFalse ("Unable to find incoming directory of " + _archiveFilename, Directory.Exists (_incomingDir));
                  
                  _outgoingDir = DirNoCase (Path.Combine (_resourcesDir, "Outgoing"));
                  
                  _defaultIncomingDisplayIconFilename = FileNoCase (Path.Combine (_incomingDir, "buddy_icon.png"));
                  if (!File.Exists (_defaultIncomingDisplayIconFilename))
                        _defaultIncomingDisplayIconFilename = FileNoCase (Path.Combine (_resourcesDir, "incoming_icon.png"));
                  
                  if (!string.IsNullOrEmpty (_outgoingDir))
                        _defaultOutgoingDisplayIconFilename = FileNoCase (Path.Combine (_outgoingDir, "buddy_icon.png"));
                  if (string.IsNullOrEmpty (_defaultOutgoingDisplayIconFilename) || (!File.Exists (_defaultOutgoingDisplayIconFilename)))
                  {
                        _defaultOutgoingDisplayIconFilename = FileNoCase (Path.Combine (_resourcesDir, "outgoing_icon.png"));
                        if (!File.Exists (_defaultOutgoingDisplayIconFilename))
                              _defaultOutgoingDisplayIconFilename = _defaultIncomingDisplayIconFilename;
                  }
                  
                  _templatesDir = Path.Combine (Path.GetDirectoryName (_contentsDir), "Templates");
                  BaseUtility.CreateDirectoryIfNeeded (_templatesDir);
            }
            
            void LoadPList ()
            {
                  PList plist = new PList (_plistFilename);
                  _plistRoot = plist[0] as PListDict;
                  
                  _name = _plistRoot.Value<string> ("CFBundleName", Path.GetFileNameWithoutExtension (_archiveFilename));
                  _infoString = _plistRoot.Value<string> ("CFBundleGetInfoString", string.Empty);
                  _defaultVariant = _plistRoot.Value<string> ("DefaultVariant", string.Empty);
                  _displayNameForNoVariant = _plistRoot.Value<string> ("DisplayNameForNoVariant", string.Empty);
            }
            
            T PListValue<T> (string keyName, string variant, T def)
            {
                  if (_plistRoot.ContainsKey (keyName + ":" + variant))
                        return _plistRoot.Value<T> (keyName + ":" + variant, def);
                  
                  return _plistRoot.Value<T> (keyName, def);
            }
            
#region PList Value Methods
            public bool GetAllowTextColors (string variant)
            {
                  return PListValue<bool> ("AllowTextColors", variant, true);
            }
            
            public string GetDefaultBackgroundColor (string variant)
            {
                  return PListValue<string> ("DefaultBackgroundColor", variant,  "FFFFFF");
            }
            
            public string GetDefaultFontFamily (string variant)
            {
                  return PListValue<string> ("DefaultFontFamily", variant, string.Empty);
            }
            
            public int GetDefaultFontSize (string variant)
            {
                  return PListValue<int> ("DefaultFontSize", variant, 0);
            }
            
            public bool GetDisableCustomBackground (string variant)
            {
                  return PListValue<bool> ("DisableCustomBackground", variant, false);
            }
            
            public string GetImageMask (string variant)
            {
                  return PListValue<string> ("ImageMask", variant, string.Empty);
            }
            
            public bool ShowsUserIcons (string variant)
            {
                  return PListValue<bool> ("ShowsUserIcons", variant, true);
            }
#endregion
            
            void FindVariants ()
            {
                  _variants.Clear ();
                  
                  if (!string.IsNullOrEmpty (_displayNameForNoVariant))
                        _variants.Add (_displayNameForNoVariant);
                  
                  if (string.IsNullOrEmpty (_variantsDir))
                        return;
                  
                  foreach (string filename in Directory.GetFiles (_variantsDir, "*.css"))
                  {
                        string name = Path.GetFileNameWithoutExtension (filename);
                        
                        if (name != _displayNameForNoVariant)
                              _variants.Add (name);
                  }
            }
            
            string FindTemplate ()
            {
                  string file = Path.Combine (_resourcesDir, "template.html");
                  
                  if (File.Exists (file))
                        return file;
                  
                  return _defaultTemplateFilename;
            }
            
            string GenerateBodyBackground ()
            {
                  string css = string.Empty;
                  
                  //TODO: set background images here
                  
                  // This is a hack to make styles look reasonable in gecko
                  if (HTMLUtility.WidgetType.Name.Contains ("Gecko"))
                        css += "font-size: 11px; overflow: auto; ";
                  
                  return css;
            }
            
            public string GenerateBaseHtmlFile (string variant, AdiumSubstitutionCollection headerFooterSubstitutions)
            {
                  string destFilename = Path.Combine (_templatesDir, variant + ".html");
                  string html = File.ReadAllText (FindTemplate ());
                  
                  html = BaseUtility.ReplaceString (html, "%@", "file://" + _resourcesDir + "/");
                  
                  if (CountOccurances (html, "%@") > 3)
                  {
                        html = BaseUtility.ReplaceString (html, "%@", string.IsNullOrEmpty (_mainCssFilename) ? string.Empty : string.Format ("@import url(\"file://{0}\");", _mainCssFilename));
                        html = BaseUtility.ReplaceString (html, "%@", FindVariantCssFile (variant));
                  }
                  else
                        html = BaseUtility.ReplaceString (html, "%@", string.IsNullOrEmpty (_mainCssFilename) ? string.Empty : string.Format ("@import url(\"file://{0}\");", _mainCssFilename));
                  
                  html = BaseUtility.ReplaceString (html, "%@", GenerateHeader (headerFooterSubstitutions));
                  html = BaseUtility.ReplaceString (html, "%@", GenerateFooter (headerFooterSubstitutions));
                  
                  html = BaseUtility.ReplaceString (html, "==bodyBackground==", GenerateBodyBackground ());
                  
                  File.WriteAllText (destFilename, html);
                  
                  //Log.Debug ("Saved to {0}", destFilename);
                  
                  return destFilename;
            }
            
            string GenerateHeader (AdiumSubstitutionCollection substitutions)
            {
                  if (string.IsNullOrEmpty (_headerFilename))
                        return string.Empty;
                  
                  return Substitute (substitutions, File.ReadAllText (_headerFilename), null);
            }
            
            string GenerateFooter (AdiumSubstitutionCollection substitutions)
            {
                  if (string.IsNullOrEmpty (_footerFilename))
                        return string.Empty;
                  
                  return Substitute (substitutions, File.ReadAllText (_footerFilename), null);
            }
            
            public string FindVariantCssFile (string variant)
            {
                  if (string.IsNullOrEmpty (_variantsDir))
                        return string.Empty;
                  
                  if (string.IsNullOrEmpty (variant))
                        return string.Empty;
                  
                  return Path.Combine (_variantsDir, variant + ".css");
            }
            
            public string GenerateMessageHtml (ITextMessage msg, bool newContact, bool context, AdiumSubstitutionCollection substitutions)
            {
                  string file = FindMessageFile (!msg.Source.Local, newContact, context);
                  
                  if (!File.Exists (file))
                        return string.Empty;
                  
                  return Substitute (substitutions, File.ReadAllText (file), msg).Replace ("\r\n", "\\r\\n").Replace ("\n", "\\n");
            }
            
            public string GenerateStatusHtml (string msg, AdiumSubstitutionCollection substitutions)
            {
                  return Substitute (substitutions, File.ReadAllText (_statusFilename), msg).Replace ("\r\n", "\\r\\n").Replace ("\n", "\\n");
            }
            
            public string FindMessageFile (bool incoming, bool newContact, bool context)
            {
                  string dir;
                  string filename = newContact ? string.Empty : "Next";
                  
                  if (incoming)
                        dir = _incomingDir;
                  else
                        dir = string.IsNullOrEmpty (_outgoingDir) ? _incomingDir : _outgoingDir;

                  filename += context ? "Context" : "Content";
                  filename += ".html";
                  
                  filename = Path.Combine (dir, filename);
                  
                  if (File.Exists (filename))
                        return filename;
                  
                  if (context)
                        return FindMessageFile (incoming, newContact, false);
                  if (!newContact)
                        return FindMessageFile (incoming, true, context);
                  if (!incoming)
                        return FindMessageFile (true, newContact, context);
                  
                  return null;
            }
            
            int CountOccurances (string str, string sub)
            {
                  ThrowUtility.ThrowIfNull ("str", str);
                  ThrowUtility.ThrowIfEmpty ("sub", sub);
                  
                  int count = 0;
                  int pos = str.IndexOf (sub);
                  
                  while (pos >= 0)
                  {
                        count++;
                        pos = str.IndexOf (sub, pos + sub.Length);
                  }
                  
                  return count;
            }
            
            string Substitute (AdiumSubstitutionCollection substitutions, string str, object val)
            {
                  MatchCollection matches = _regexSubstitutions.Matches (str);
                  
                  string ret = string.Empty;
                  int pos = 0;
                  
                  foreach (Match match in matches)
                  {
                        string name = match.Groups[1].Value;
                        string param = match.Groups[3].Value;
                        
                        ret += str.Substring (pos, match.Index - pos);
                        
                        if (substitutions.ContainsKey (name))
                              ret += substitutions[name] (param, val);
                        else
                              Log.Warn ("Unknown keyword '{0}', param '{1}'", name, param);
                        
                        pos = match.Index + match.Length;
                  }
                  
                  ret += str.Substring (pos);
                  
                  return ret;
            }
            
            string DirNoCase (string dir)
            {
                  if (Directory.Exists (dir))
                        return dir;
                  
                  foreach (string subDir in Directory.GetDirectories (Path.GetDirectoryName (dir)))
                        if (subDir.Equals (dir, StringComparison.InvariantCultureIgnoreCase))
                              return subDir;
                  
                  return string.Empty;
            }
            
            string FileNoCase (string filename)
            {
                  if (File.Exists (filename))
                        return filename;
                  
                  foreach (string dirFile in Directory.GetFiles (Path.GetDirectoryName (filename)))
                        if (dirFile.Equals (filename, StringComparison.InvariantCultureIgnoreCase))
                              return dirFile;
                  
                  return string.Empty;
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index