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

AdiumMessageDisplay.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
 */

#define DEBUGWHILE

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

using Anculus.Core;

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

namespace Galaxium.AdiumThemes
{
      public class AdiumMessageDisplay : IMessageDisplay
      {
            IHTMLWidget _htmlWidget;
            IConversation _conversation;
            
            AdiumMessageStyle _style;
            string _variant;
            
            bool _lastContext;
            string _lastUid;
            
            AdiumSubstitutionCollection _headerFooterSubstitutions = new AdiumSubstitutionCollection ();
            AdiumSubstitutionCollection _messageSubstitutions = new AdiumSubstitutionCollection ();
            AdiumSubstitutionCollection _statusSubstitutions = new AdiumSubstitutionCollection ();
            
            public AdiumMessageStyle Style
            {
                  get { return _style; }
                  set
                  {
                        ThrowUtility.ThrowIfNull ("style", value);
                        
                        if (_style == value)
                              return;
                        
                        _style = value;

                        if (string.IsNullOrEmpty (_variant) || (!_style.Variants.Contains (_variant)))
                              _variant = _style.DefaultVariant;
                        
                        Reload ();
                  }
            }
            
            public string Variant
            {
                  get { return _variant; }
                  set
                  {
                        if (_variant == value)
                              return;
                        
                        _variant = value;
                        
                        if (string.IsNullOrEmpty (_variant) || (!_style.Variants.Contains (_variant)))
                              _variant = _style.DefaultVariant;
                        
                        Reload ();
                  }
            }
            
            public object Widget
            {
                  get { return _htmlWidget; }
            }
            
            public AdiumMessageDisplay (IConversation conversation)
            {
                  ThrowUtility.ThrowIfNull ("conversation", conversation);
                  _conversation = conversation;
                  
                  _htmlWidget = HTMLUtility.CreateHTMLWidget ();
                  ThrowUtility.ThrowIfNull ("htmlWidget", _htmlWidget);
                  
                  _htmlWidget.LinkClicked += delegate (object sender, LinkEventArgs args)
                  {
                        BaseUtility.SafeOpenURL (args.URL);
                        
                        // Don't navigate
                        return false;
                  };
                  _htmlWidget.Ready += delegate { Reload (); };
                  
                  _style = AdiumMessageStyleFactory.ActiveStyle;
                  _variant = AdiumMessageStyleFactory.ActiveVariant;
                  
                  _headerFooterSubstitutions["chatName"] = new SubstitutionDelegate (delegate { return HtmlStripEncode (_conversation.PrimaryContact, (_conversation.ContactCollection.Count > 1) ? "Group Chat" : _conversation.PrimaryContact.DisplayIdentifier); });
                  _headerFooterSubstitutions["sourceName"] = new SubstitutionDelegate (delegate { return GenerateNameHtml (_conversation.Session.Account, _conversation.Session.Account.UniqueIdentifier); });
                  _headerFooterSubstitutions["destinationName"] = new SubstitutionDelegate (delegate { return HtmlEncode (_conversation.PrimaryContact.UniqueIdentifier); });
                  _headerFooterSubstitutions["destinationDisplayName"] = new SubstitutionDelegate (delegate { return HtmlStripEncode (_conversation.PrimaryContact, _conversation.PrimaryContact.DisplayIdentifier); });
                  _headerFooterSubstitutions["incomingIconPath"] = new SubstitutionDelegate (delegate { return (_conversation.PrimaryContact.DisplayImage != null) ? _conversation.PrimaryContact.DisplayImage.Filename : _style.DefaultIncomingDisplayIconFilename; });
                  _headerFooterSubstitutions["outgoingIconPath"] = new SubstitutionDelegate (delegate { return (_conversation.Session.Account.DisplayImage != null) ? _conversation.Session.Account.DisplayImage.Filename : _style.DefaultOutgoingDisplayIconFilename; });
                  _headerFooterSubstitutions["timeOpened"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode (FormatTime (DateTime.Now, param)); });
                  
                  /*TODO:
    * %senderColor% - A color derived from the user's name. If a colon separated list of html colors is at Incoming/SenderColors.txt it will be used instead of the default colors. (Adium 1.0+)
    * %senderStatusIcon% - The path to the status icon of the sender (available, away, etc...) (Adium 1.0+)
    * %textbackgroundcolor{X}% - documentation forthcoming */
                  /*
    * %messageClasses% - A space separated list of type information for messages, suitable for use as a class attribute. Currently available types are listed below. ("Adium 1.1+")
          o history - the message is from a previous conversation
          o outgoing - the message is being sent from this computer
          o incoming - the message is being sent to this computer
          o message - the message is actually a message, and not something like a status change (note: the exact meaning of this with regard to things like file transfers may change in a future version of Adium)
          o autoreply - the message is an automatic response, generally due to an away status
          o status - the message is a status change
          o event - the message is a notification of something happening (for example, encryption being turned on) */
                  
                  _messageSubstitutions["message"] = new SubstitutionDelegate (delegate (string param, object val) { return GenerateMessageHtml (val as ITextMessage); });
                  _messageSubstitutions["messageDirection"] = new SubstitutionDelegate (delegate (string param, object val) { return "ltr"; });
                  _messageSubstitutions["time"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode (FormatTime ((val as ITextMessage).TimeStamp, param)); });
                  _messageSubstitutions["shortTime"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode ((val as ITextMessage).TimeStamp.ToShortTimeString ()); });
                  _messageSubstitutions["service"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode ((val as ITextMessage).Source.Session.Account.Protocol.Name); });
                  _messageSubstitutions["sender"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlStripEncode ((val as ITextMessage).Source, (val as ITextMessage).Source.DisplayIdentifier); });
                  _messageSubstitutions["senderDisplayName"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlStripEncode ((val as ITextMessage).Source, (val as ITextMessage).Source.DisplayName); });
                  _messageSubstitutions["senderScreenName"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode ((val as ITextMessage).Source.UniqueIdentifier); });
                  _messageSubstitutions["userIconPath"] = new SubstitutionDelegate (delegate (string param, object val) { return ((val as ITextMessage).Source.DisplayImage != null) ? (val as ITextMessage).Source.DisplayImage.Filename : ((val as ITextMessage).Source.Local ? _style.DefaultOutgoingDisplayIconFilename : _style.DefaultIncomingDisplayIconFilename); });

                  /* TODO:
    * %messageClasses% - A space separated list of type information for messages, suitable for use as a class attribute. Currently available types are listed below. ("Adium 1.1+")
          o history - the message is from a previous conversation
          o outgoing - the message is being sent from this computer
          o incoming - the message is being sent to this computer
          o message - the message is actually a message, and not something like a status change (note: the exact meaning of this with regard to things like file transfers may change in a future version of Adium)
          o autoreply - the message is an automatic response, generally due to an away status
          o status - the message is a status change
          o event - the message is a notification of something happening (for example, encryption being turned on) */
                  
                  _statusSubstitutions["message"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode (val as string); });
                  _statusSubstitutions["time"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode (FormatTime (DateTime.Now, param)); });
                  _statusSubstitutions["shortTime"] = new SubstitutionDelegate (delegate (string param, object val) { return HtmlEncode (DateTime.Now.ToShortTimeString ()); });
            }
            
            public void AddMessage (ITextMessage message)
            {
                  AddMessage (message, false);
            }
            
            public void AddHistoryMessage (ITextMessage message)
            {
                  AddMessage (message, true);
            }
            
            void AddMessage (ITextMessage message, bool context)
            {
                  if (_style == null)
                  {
                        Log.Error ("No Style");
                        return;
                  }
                  
                  bool newContact = (message.Source.UniqueIdentifier != _lastUid) || (context != _lastContext);
                  string html = _style.GenerateMessageHtml (message, newContact, context, _messageSubstitutions);
                  string function = newContact ? "appendMessage" : "appendNextMessage";
                  
                  _lastUid = message.Source.UniqueIdentifier;
                  _lastContext = context;
                  
                  _htmlWidget.RunJavaScript (string.Format ("{0} ('{1}');", function, html));
            }
            
            public void AddOfflineMessage (ITextMessage message)
            {
                  //TODO: do something to show a difference from a regular message
                  AddMessage (message);
            }
            
            public void AddImageMessage (IEntity from, byte[] data)
            {
                  
            }
            
            public void AddSystemMessage (string message)
            {
                  if (_style == null)
                  {
                        Log.Error ("No Style");
                        return;
                  }
                  
                  _lastUid = string.Empty;

                  string html = _style.GenerateStatusHtml (message, _statusSubstitutions);
                  _htmlWidget.RunJavaScript (string.Format ("appendMessage ('{0}');", html));
            }
            
            public void Clear ()
            {
                  Reload ();
            }
            
            public void UpdateEmoticon (IEmoticon emot)
            {
                  //getElementsByClassName
            //Written by Jonathan Snook, http://www.snook.ca/jonathan
            //Add-ons by Robert Nyman, http://www.robertnyman.com
                  
                  string js = 
                            "function getElementsByClassName(oElm, strTagName, strClassName)" +
                              "{" +
                              "    var arrElements = (strTagName == '*' && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);" +
                              "    var arrReturnElements = new Array();" +
                           @"    strClassName = strClassName.replace(/\-/g, '\\-');" +
                              "    var oRegExp = new RegExp('(^|\\s)' + strClassName + '(\\s|$)');" +
                              "    var oElement;" +
                              "    for(var i=0; i<arrElements.length; i++)" +
                              "    {" +
                              "        oElement = arrElements[i];" +
                              "        if(oRegExp.test(oElement.className))" +
                              "        {" +
                              "            arrReturnElements.push(oElement);" +
                              "        }" +
                              "    }" +
                              "    return (arrReturnElements)" +
                              "}" +
                              
                              "var emoticons = getElementsByClassName(document, 'img', 'galaxiumEmoticon');" +
                              "for (var i = 0; i < emoticons.length; i++)" +
                              "{" +
                              "    var url = unescape(emoticons[i].src).replace('file://', '');" +
                              "    if (url != '" + emot.Filename + "')" +
                              "        continue;" +
                              "    emoticons[i].src = url;" +
                              "    emoticons[i].style.width = '" + emot.Width.ToString () + "px';" +
                              "    emoticons[i].style.height = '" + emot.Height.ToString () + "px';" +
                              "}" +
                              "scrollToBottom();";
                  
                  _htmlWidget.RunJavaScript (js);
            }
            
            void Reload ()
            {
                  _style = AdiumMessageStyleFactory.ActiveStyle;
                  _variant = AdiumMessageStyleFactory.ActiveVariant;
                  
                  if (_style != null)
                  {
                        string filename = _style.GenerateBaseHtmlFile (_variant, _headerFooterSubstitutions);
                        //Log.Debug ("Filename: {0}", filename);
                        _htmlWidget.LoadUrl (filename);
                  }
                  else
                  {
                        string html = string.Format ("<h1>Unable to load a style</h1><p>Galaxium was unable to load a message style from the directory {0}</p>",
                                                     AdiumMessageStyleFactory._systemThemePath);
                        
                        _htmlWidget.LoadHtml (html);
                  }
            }
            
            string GenerateMessageHtml (ITextMessage msg)
            {
                  if (msg == null || msg.Chunks == null)
                        return string.Empty;
                  
                  return GenerateHtml (msg.Chunks);
            }
            
            string GenerateHtml (IEnumerable<ITextChunk> chunks)
            {
                  string result = string.Empty;
                  
                  foreach (ITextChunk chunk in chunks)
                  {
                        switch (chunk.Type)
                        {
                              case TextChunkType.Text:
                                    result += ApplyStyle (HtmlEncodeMessage (chunk.Text), chunk.Style);
                                    break;
                              case TextChunkType.URL:
                                    string url = chunk.Text;
                                    result += string.Format ("<a href=\"{0}\">{1}</a>", XmlUtility.GetEncodedString (url.Trim ()), ApplyStyle (HtmlEncodeMessage (url), chunk.Style));
                                    break;
                              case TextChunkType.Emoticon:
                              
                                    IEmoticon emot = ((EmoticonTextChunk)chunk).Emoticon;
                              
                                    string html = "<img src=\"";
                              
                                    if (!string.IsNullOrEmpty (emot.Filename))
                                          html += emot.Filename;
                                    else
                                          Log.Warn("Emoticon With No Filename! " + emot.Name);
                              
                                    html += "\" class=\"galaxiumEmoticon\" style=\"";
                         
                                    if (emot.Width > 0)
                                          html += string.Format ("width: {0}px; ", emot.Width);
                                    if (emot.Height > 0)
                                          html += string.Format ("height: {0}px; ", emot.Height);

                                    html += string.Format ("\" title=\"{0} ({1})\"/>", HtmlEncode (emot.Name), HtmlEncode (chunk.Text));
                                    
                                    result += html;
                              
                                    break;
                        }
                  }
                  
                  return result;
            }
            
            string HtmlStripEncode (IEntity entity, string txt)
            {
                  if (entity == null)
                        return HtmlEncode (txt);
                  
                  return HtmlEncode (MessageUtility.StripMarkup (txt, entity.Session.Account.Protocol));
            }
            
            string HtmlEncode (string str)
            {
                  string encoded = System.Web.HttpUtility.HtmlEncode (str);
                  
                  encoded = BaseUtility.ReplaceString (encoded, "'", "&apos;", true);
                  encoded = BaseUtility.ReplaceString (encoded, "\r\n", "<br/>", true);
                  encoded = BaseUtility.ReplaceString (encoded, "\n", "<br/>", true);
                  encoded = BaseUtility.ReplaceString (encoded, "\r", "<br/>", true);
                  encoded = BaseUtility.ReplaceString (encoded, "\\", "\\\\", true);
                  
                  return encoded;
            }
            
            string HtmlEncodeMessage (string str)
            {
                  string ret = HtmlEncode (str);
                  
                  // Encode messages as adium does in AIHTMLDecoder.m
                  
                  ret = ret.Replace ("\t", " &nbsp;&nbsp;&nbsp;");
                  
                  if (ret.StartsWith (" "))
                        ret = "&nbsp;" + ret.Substring (1);
                  
                  ret = ret.Replace ("  ", " &nbsp;");
                  
                  return ret;
            }
            
            string GenerateNameHtml (IEntity entity, string str)
            {
                  List<ITextChunk> chunks = MessageUtility.SplitMessage (str, new TextStyle (), entity.Session.Account.Protocol, entity, null);
                  return GenerateHtml (chunks);
            }
            
            string FormatTime (DateTime time, string format)
            {
                  if (string.IsNullOrEmpty (format))
                        return time.ToLongTimeString ();
                  
                  int i = 0;
                  string ret = string.Empty;
                  
#if DEBUGWHILE
                  int iterations = 0;
#endif
                  
                  while (i < format.Length)
                  {
#if DEBUGWHILE
                        iterations++;
                        
                        if (iterations > 100)
                              Log.Warn ("Iteration {0}, pos {1} format '{2}'", iterations, i, format);
#endif
                        
                        if (format[i] != '%')
                        {
                              ret += format[i++];
                              continue;
                        }
                        
                        char c = (format.Length > i + 1) ? format[i + 1] : '\0';
                        
                        switch (c)
                        {
                        case '%':
                              ret += '%';
                              break;
                        case 'a':
                              ret += time.ToString ("ddd");
                              break;
                        case 'A':
                              ret += time.ToString ("dddd");
                              break;
                        case 'b':
                              ret += time.ToString ("MMM");
                              break;
                        case 'B':
                              ret += time.ToString ("MMMM");
                              break;
                        case 'c':
                              ret += time.ToString ("hh:mm:ss tt dd/MM/yyyy zzz");
                              break;
                        case 'd':
                              ret += time.ToString ("dd");
                              break;
                        case 'e':
                              ret += time.ToString ("d");
                              break;
                        case 'F':
                              ret += time.ToString ("FFF");
                              break;
                        case 'H':
                              ret += time.ToString ("HH");
                              break;
                        case 'I':
                              ret += time.ToString ("hh");
                              break;
                        case 'j':
                              ret += time.DayOfYear.ToString ();
                              break;
                        case 'm':
                              ret += time.ToString ("MM");
                              break;
                        case 'M':
                              ret += time.ToString ("mm");
                              break;
                        case 'p':
                              ret += time.ToString ("tt");
                              break;
                        case 'S':
                              ret += time.ToString ("ss");
                              break;
                        case 'w':
                              ret += ((int)time.DayOfWeek).ToString ();
                              break;
                        case 'x':
                              ret += time.ToString ("dd/MM/yyyy zzz");
                              break;
                        case 'X':
                              ret += time.ToString ("hh:mm:ss tt");
                              break;
                        case 'y':
                              ret += time.ToString ("yy");
                              break;
                        case 'Y':
                              ret += time.ToString ("yyyy");
                              break;
                        case 'Z':
                              ret += TimeZone.CurrentTimeZone.StandardName;
                              break;
                        case 'z':
                              ret += time.ToString ("zzz");
                              break;
                        default:
                              break;
                        }
                        
                        i += ((c == '\0') ? 1 : 2);
                  }
                  
                  return ret;
            }
            
            string ApplyStyle (string msg, ITextStyle style)
            {
                  StringBuilder sb = new StringBuilder ();
                  
                  if (style.Bold)
                        sb.Append ("<b>");
                  if (style.Italic)
                        sb.Append ("<i>");
                  if (style.Underline)
                        sb.Append ("<u>");
                  if (style.Strikethrough)
                        sb.Append ("<s>");
                  
                  // The font tag is old and depreciated, but adium uses it, so...
                  bool font = false;
                  bool fontStyle = false;
                  
                  if (!style.IsDefaultFont)
                  {
                        if (!font)
                        {
                              sb.Append ("<font ");
                              font = true;
                        }
                        
                        sb.AppendFormat ("face=\"{0}\" ", style.Font);
                  }
                  
                  if (style.ForeColor >= 0)
                  {
                        if (!font)
                        {
                              sb.Append ("<font ");
                              font = true;
                        }
                        
                        sb.AppendFormat ("color=\"#{0:x6}\" ", style.ForeColor);
                  }
                  
                  if (style.FontSize > 0)
                  {
                        if (!font)
                        {
                              sb.Append ("<font ");
                              font = true;
                        }
                        
                        sb.AppendFormat ("absz=\"{0}\" size=\"{1}\" ", style.FontSize, PointsToHTML (style.FontSize));
                  }
                  
                  if (style.BackColor >= 0)
                  {
                        if (!fontStyle)
                        {
                              if (!font)
                              {
                                    sb.Append ("<font ");
                                    font = true;
                              }
                              
                              sb.Append ("style=\"");
                              fontStyle = true;
                        }
                        
                        sb.AppendFormat ("background-color: #{0:x6}; ", style.BackColor);
                  }
                  
                  if (fontStyle)
                        sb.Append ("\"");
                  if (font)
                        sb.Append (">");
                  
                  sb.Append (msg);
                  
                  if (font)
                        sb.Append ("</font>");
                  
                  if (style.Strikethrough)
                        sb.Append ("</s>");
                  if (style.Underline)
                        sb.Append ("</u>");
                  if (style.Italic)
                        sb.Append ("</i>");
                  if (style.Bold)
                        sb.Append ("</b>");
                  
                  return sb.ToString ();
            }
            
            int PointsToHTML (int points)
            {
                  if (points <= 9)
                        return 1;
                  if (points <= 10)
                        return 2;
                  if (points <= 12)
                        return 3;
                  if (points <= 14)
                        return 4;
                  if (points <= 18)
                        return 5;
                  if (points <= 24)
                        return 6;
                  
                  return 7;
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index