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

TextMessageDisplay.cs

/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2005-2007 Ben Motmans <ben.motmans@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.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;

using Gdk;
using Gtk;
using Pango;

using Galaxium.Core;
using Galaxium.Protocol;
using Anculus.Core;

namespace Galaxium.Gui.GtkGui
{
      public class TextMessageDisplay : TextView, IMessageDisplay
      {
            internal struct EmoticonMark
            {
                  public IEmoticon Emoticon;
                  public TextChildAnchor Anchor;
                  public Gtk.Image Image;
                  public string Equivalent;
            }
            
            protected TextBuffer _textBuffer;
            internal TextTagTable _tagTable;
            protected TextMark _endMark;
            
            List<EmoticonMark> _emoticons = new List<EmoticonMark> ();
            
            protected string _lastIdentifier = String.Empty;
            
            protected TextTag _messageTag;
            protected TextTag _boldTag;
            protected TextTag _italicTag;
            protected TextTag _underlineTag;
            protected TextTag _strikeTag;
            
            protected Cursor _cursorHand;
            protected Cursor _cursorPointer;
            
            protected LinkTag _activeLink;
            protected bool _mouseOverLink;
            protected TextIter _mouseOverIter;
            
            private Gtk.Tooltips _tooltips;
            
            public TextMessageDisplay (IConversation conversation)
            {
                  _cursorHand = new Cursor (CursorType.Hand2);
                  _cursorPointer = new Cursor (CursorType.Xterm);
                  
                  Editable = false;
                  DoubleBuffered = true;
                  WrapMode = Gtk.WrapMode.Word;
                  CursorVisible = false;
                  LeftMargin = 2;
                  RightMargin = 2;
                  CanFocus = false;
                  
                  // Setup a normal message text tag.
                  _messageTag = new TextTag("message_tag");
                  _messageTag.LeftMargin = 50;
                  
                  TextTag nonMargineMessageTag = new TextTag("no_margin_message_tag");
                  nonMargineMessageTag.LeftMargin = 5;
                  
                  // Setup a action message text tag.
                  TextTag actionTag = new TextTag("action_tag");
                  actionTag.Weight = Pango.Weight.Bold;
                  actionTag.Scale = 0.8;
                  actionTag.PixelsAboveLines = 5;
                  actionTag.PixelsBelowLines = 5;
                  
                  TextTag historyTag = new TextTag("history_tag");
                  historyTag.Weight = Pango.Weight.Light;
                  historyTag.Foreground = "slate gray";
                  historyTag.PixelsAboveLines = 5;
                  
                  // Setup a warning message text tag.
                  TextTag warningTag = new TextTag("warning_tag");
                  warningTag.Foreground = "red";
                  warningTag.Weight = Pango.Weight.Bold;
                  warningTag.Scale = 0.8;
                  warningTag.PixelsAboveLines = 5;
                  warningTag.PixelsBelowLines = 5;
                  
                  // Setup a time message text tag.
                  TextTag timeTag = new TextTag("time_tag");
                  timeTag.Scale = 0.9;
                  timeTag.LeftMargin = 10;
                  timeTag.Style = Pango.Style.Italic;
            
                  // Setup a margin text tag.
                  TextTag marginTag = new TextTag("margin_tag");
                  marginTag.LeftMargin = 10;
                  
                  // Setup a bold message text tag.
                  _boldTag = new TextTag("bold_tag");
                  _boldTag.Weight = Pango.Weight.Ultrabold;
                  
                  // Setup a italic message text tag.
                  _italicTag = new TextTag("italic_tag");
                  _italicTag.Style = Pango.Style.Italic;
                  
                  // Setup a underline message text tag.
                  _underlineTag = new TextTag("underline_tag");
                  _underlineTag.Underline = Pango.Underline.Single;
                  
                  // Setup a strikethrough message text tag.
                  _strikeTag = new TextTag("strike_tag");
                  _strikeTag.Strikethrough = true; 
                  
                  // Add the above text tags to the table.
                  _tagTable = new TextTagTable();
                  _tagTable.Add(timeTag);
                  _tagTable.Add(marginTag);
                  _tagTable.Add(historyTag);
                  _tagTable.Add(_messageTag);
                  _tagTable.Add(nonMargineMessageTag);
                  _tagTable.Add(actionTag);
                  _tagTable.Add(warningTag);
                  _tagTable.Add(_boldTag);
                  _tagTable.Add(_italicTag);
                  _tagTable.Add(_underlineTag);
                  _tagTable.Add(_strikeTag);
                  
                  _textBuffer = new TextBuffer (_tagTable);
                  this.Buffer = _textBuffer;
                  
                  _tooltips = new Gtk.Tooltips();
                  
                  this.ButtonReleaseEvent += new ButtonReleaseEventHandler (OnTextViewButtonRelease);
                  ShowAll ();
            }
            
            public TextBuffer TextBuffer
            {
                  get { return _textBuffer; }
            }
            
            public object Widget
            {
                  get { return this; }
            }
            
            public virtual void Clear ()
            {
                  _textBuffer.Clear ();
            }
            
            public virtual void AddMessage (ITextMessage message)
            {
                  if(!_lastIdentifier.Equals(message.Source.UniqueIdentifier))
                  {
                        WriteFrom(message.Source);
                        WriteNewLine();
                  }
                  
                  _lastIdentifier = message.Source.UniqueIdentifier;
                  
                  WriteMargin();
                  WriteTime(message.TimeStamp);
                  
                  WriteMessage(message);
                  
                  WriteNewLine();
                  AutoScroll ();
            }
            
            public virtual void AddHistoryMessage (ITextMessage message)
            {
                  foreach (ITextChunk chunk in message.Chunks)
                        chunk.Style.ForeColor = 8947848;
                  
                  AddMessage (message);
            }
            
            public virtual void AddOfflineMessage (ITextMessage message)
            {
                  if(!_lastIdentifier.Equals(message.Source.UniqueIdentifier + " [Offline]"))
                  {
                        WriteFromOffline(message.Source);
                        WriteNewLine();
                  }
                  
                  _lastIdentifier = message.Source.UniqueIdentifier + " [Offline]";
                  
                  WriteMargin();
                  WriteTime(message.TimeStamp);
                  
                  WriteMessage(message);
                  
                  WriteNewLine();
                  AutoScroll ();
            }
            
            public virtual void AddImageMessage (IEntity source, byte[] data)
            {
                  if(!_lastIdentifier.Equals (source.UniqueIdentifier))
                  {
                        WriteFrom (source);
                        WriteNewLine ();
                  }
                  
                  _lastIdentifier = source.UniqueIdentifier;
                  
                  WriteMargin ();
                  WriteTime (DateTime.Now);
                  
                  WriteImage (data);
                  
                  WriteNewLine ();
                  AutoScroll ();
            }
            
            public virtual void AddSystemMessage(string message)
            {
                  string format = String.Empty;
                  
                  //if (string.IsNullOrEmpty (format))
                        format = DateTime.Now.ToShortTimeString ();
                  //else
                        //format = DateTime.Now.ToString (format);
                  
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.InsertWithTagsByName(ref endIter, "["+format+"] "+message, "action_tag");
                  
                  WriteNewLine();
                  AutoScroll ();
                  
                  _textBuffer.SelectRange(_textBuffer.EndIter, _textBuffer.EndIter);
                  
                  _lastIdentifier = String.Empty;
            }
            
            public virtual void UpdateEmoticon(IEmoticon emot)
            {
                  //emoticon has been received, we need to show it wherever it has been used
                  
                  for (int i = 0; i < _emoticons.Count; i++)
                  {
                        EmoticonMark mark = _emoticons[i];
                        
                        if (mark.Emoticon != emot)
                              continue;
                        
                        Gtk.Image newImage = LoadEmoticon (emot);
                        
                        if (newImage == null)
                              continue;
                        
                        _tooltips.SetTip (newImage, string.Format("{0} ({1})", emot.Name, mark.Equivalent), null);
                        
                        Remove (mark.Image);
                        mark.Image = newImage;
                        AddChildAtAnchor (mark.Image, mark.Anchor);
                  }
            }
            
            internal static Gtk.Image LoadImage (byte[] data)
            {
                  try
                  {
                        PixbufAnimation anim = null;
                        Pixbuf pixbuf = null;
                        
                        if ((data != null) && (data.Length > 0))
                        {
                              PixbufLoader loader = new PixbufLoader (data);
                                    
                              if (loader.Animation.IsStaticImage)
                                    pixbuf = loader.Pixbuf;
                              else
                                    anim = loader.Animation;
                        }     
                        else  
                              pixbuf = IconUtility.GetIcon ("galaxium-image");
                        
                        Gtk.Image image;
                        
                        if (anim != null)
                              image = new Gtk.Image (anim);
                        else
                              image = new Gtk.Image (pixbuf);
                        
                        image.Visible = true;
                        
                        return image;
                  }
                  catch (Exception)
                  {
                        return new Gtk.Image (IconUtility.GetIcon ("galaxium-image"));
                  }
            }
            
            internal static Gtk.Image LoadEmoticon (IEmoticon emot)
            {
                  return LoadImage (emot.Data);
            }
            
            protected virtual void WriteFromOffline (IEntity from)
            {
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.InsertWithTags(ref endIter,
                        string.IsNullOrEmpty(from.DisplayIdentifier) ? from.UniqueIdentifier : from.DisplayIdentifier + " [Offline]",
                        new EntityTag(this, from));
            }
            
            protected virtual void WriteFrom (IEntity from)
            {
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.InsertWithTags(ref endIter,
                        from.DisplayIdentifier,
                        new EntityTag(this, from));
            }
            
            protected virtual void WriteNewLine ()
            {
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.Insert(ref endIter, Environment.NewLine);
            }
            
            protected virtual void WriteUnformattedText (string text)
            {
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.Insert(ref endIter, text);
            }
            
            protected virtual void WriteTime (DateTime timestamp)
            {
                  string format = String.Empty;
                  
                  //if (string.IsNullOrEmpty (format))
                        format = timestamp.ToShortTimeString ();
                  //else
                        //format = timestamp.ToString (format);
                  
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.InsertWithTagsByName(ref endIter, String.Concat ("(", format, ") "), "time_tag");
            }
            
            protected virtual void WriteMargin ()
            {
                  TextIter endIter = _textBuffer.EndIter;
                  _textBuffer.InsertWithTagsByName(ref endIter, "", "margin_tag");
            }
            
            protected virtual void WriteImage (byte[] data)
            {
                  TextIter endIter = _textBuffer.EndIter;
                  Gtk.TextChildAnchor anchor = _textBuffer.CreateChildAnchor (ref endIter);
                  
                  AddChildAtAnchor (LoadImage (data), anchor);
            }
            
            protected virtual void WriteMessage (ITextMessage message)
            {
                  TextMark firstMark = _textBuffer.CreateMark(null, _textBuffer.EndIter, true);
                  
                  foreach (ITextChunk chunk in message.Chunks)
                  {
                        ITextStyle style = chunk.Style;
                        
                        TextMark beginMark = _textBuffer.CreateMark (null, _textBuffer.EndIter, true);
                        TextIter beginIter = _textBuffer.EndIter;
                        
                        if (chunk.Type == TextChunkType.Emoticon)
                        {
                              IEmoticon emot = ((EmoticonTextChunk)chunk).Emoticon;
                              
                              EmoticonMark mark = new EmoticonMark();
                              mark.Equivalent = chunk.Text;
                              mark.Emoticon = emot;
                              mark.Anchor = _textBuffer.CreateChildAnchor (ref beginIter);
                              mark.Image = LoadEmoticon (emot);
                              
                              if (mark.Image == null)
                                    continue;
                              
                              _tooltips.SetTip (mark.Image, string.Format("{0} ({1})", emot.Name, chunk.Text), null);
                              
                              _emoticons.Add (mark);
                              
                              AddChildAtAnchor (mark.Image, mark.Anchor);
                        }
                        else
                        {
                              WriteUnformattedText (chunk.Text);
                              
                              beginIter = _textBuffer.GetIterAtMark(beginMark);
                              TextIter endIter = _textBuffer.EndIter;
                              
                              if (style == null || style.IsDefault)
                                    continue;
                        
                              if (style.IsHyperlink || (chunk.Type == TextChunkType.URL))
                              {
                                    _textBuffer.ApplyTag (new LinkTag (_tagTable, chunk.Text), beginIter, endIter);
                              }
                              else
                              {
                                    if (style.Bold)
                                          _textBuffer.ApplyTag (_boldTag, beginIter, endIter);
                                    if (style.Underline)
                                          _textBuffer.ApplyTag (_underlineTag, beginIter, endIter);
                                    if (style.Italic)
                                          _textBuffer.ApplyTag (_italicTag, beginIter, endIter);
                                    if (style.Strikethrough)
                                          _textBuffer.ApplyTag (_strikeTag, beginIter, endIter);
                                    
                                    string tagName = null;
                                    if (style.ForeColor >= 0) {
                                          tagName = "color_" + style.ForeColor.ToString ();
                                          Gtk.TextTag colorTag = _tagTable.Lookup (tagName);
                                          if (colorTag == null) {
                                                colorTag = new Gtk.TextTag (tagName);
                                          
                                                byte r = (byte)((style.ForeColor & 0xFF0000) >> 16);
                                                byte g = (byte)((style.ForeColor & 0xFF00) >> 8);
                                                byte b = (byte)(style.ForeColor & 0xFF);
                                                
                                                colorTag.ForegroundGdk = new Gdk.Color (r, g, b);
                                                _tagTable.Add (colorTag);
                                          }
                                          _textBuffer.ApplyTag (colorTag, beginIter, endIter);
                                    }//TODO: bg color support
                                    
                                    if (!style.IsDefaultFont) {
                                          if (style.Font != null) {
                                                tagName = "font_" + style.Font;
                                                Gtk.TextTag fontTag = _tagTable.Lookup (tagName);
                                                if (fontTag == null) {
                                                      fontTag = new Gtk.TextTag (tagName);
                                                      
                                                      fontTag.Font = style.Font;
                                                      _tagTable.Add (fontTag);
                                                }
                                                _textBuffer.ApplyTag (fontTag, beginIter, endIter);
                                          }
                                          
                                          if (style.FontSize > 0) {
                                                tagName = "fontsize_" + style.FontSize;
                                                Gtk.TextTag fontSizeTag = _tagTable.Lookup (tagName);
                                                if (fontSizeTag == null) {
                                                      fontSizeTag = new Gtk.TextTag (tagName);
                                                      
                                                      fontSizeTag.FontDesc.Size = style.FontSize;
                                                      _tagTable.Add (fontSizeTag);
                                                }
                                                _textBuffer.ApplyTag (fontSizeTag, beginIter, endIter);
                                                
                                          }
                                    }
                              }
                        }
                  }
                  
                  TextIter firstIter = _textBuffer.GetIterAtMark(firstMark);
                  TextIter lastIter = _textBuffer.EndIter;
                  _textBuffer.ApplyTag (_messageTag, firstIter, lastIter);
                  _textBuffer.DeleteMark(firstMark);
                  
                  _textBuffer.SelectRange(_textBuffer.EndIter, _textBuffer.EndIter);
            }
            
            protected virtual void WriteWithAttributes(string text, string[] attributes, bool emoticons)
            {
                  // Keep the iteration of where we started.
                  TextIter endIter = _textBuffer.EndIter;
                  
                  if (!emoticons)
                  {
                        _textBuffer.InsertWithTagsByName(ref endIter, text, attributes);
                        return;
                  }
                  
                  string temp = text;
                  string emoticon = String.Empty;
                  int index = 0;
                  bool found = false;
                  
                  /*while(sTemp.Length > 0)
                  {
                        int iTempIndex = sTemp.Length;
                        
                        // Go through each emoticon.
                        foreach (string sTempEmoticon in EmoticonBasket.EmoticonTable.Keys)
                        {
                              // If the emoticon exists, keep track of where.
                              int iIndexOfEmoticon = sTemp.IndexOf(sTempEmoticon);
                              
                              if (iIndexOfEmoticon >= 0)
                              {
                                    // Make sure we only keep the lowest index.
                                    if (iIndexOfEmoticon < iTempIndex)
                                    {
                                          iTempIndex = iIndexOfEmoticon;
                                          sEmoticon = sTempEmoticon;
                                          bFound = true;
                                    }
                              }
                        }
                        
                        if (bFound)
                        {
                              string sBefore = sTemp.Substring(iCurrentIndex, iTempIndex);
                              
                              _textBuffer.InsertWithTagsByName(ref endIter, sBefore, aAttributes);
                              
                              // Insert the emoticon itself.
                              //_textBuffer.InsertPixbuf(ref endIter, EmoticonBasket.Get_Emoticon(sEmoticon).Pixbuf);
                              
                              sTemp = sTemp.Substring(sBefore.Length+sEmoticon.Length, sTemp.Length - (sBefore.Length+sEmoticon.Length));
                              
                              bFound = false;
                        }
                        else
                        {
                              break;
                        }
                  }*/
                  
                  _textBuffer.InsertWithTagsByName(ref endIter, temp, attributes);
                  
                  _textBuffer.SelectRange(_textBuffer.EndIter, _textBuffer.EndIter);
            }
            
            void AutoScroll ()
            {
                  int endY, endHeight;
                  base.GetLineYrange (_textBuffer.EndIter, out endY, out endHeight);
                  
                  if (VisibleRect.Y >= (endY + endHeight - (VisibleRect.Height * 1.2)))
                  {
                        TextMark endMark = _textBuffer.CreateMark ("end", _textBuffer.EndIter, false);
                        ScrollToMark (endMark, 0.4, true, 0.0, 1.0);
                        _textBuffer.DeleteMark (endMark);
                  }
            }
            
            protected void OnTextViewButtonRelease (object o, ButtonReleaseEventArgs args)
            {
                  Gtk.Menu menu = null;
                  
                  if (_mouseOverIter.ChildAnchor != null)
                  {
                        IEmoticon emot = null;
                        
                        foreach (EmoticonMark mark in _emoticons)
                        {
                              if (mark.Anchor != _mouseOverIter.ChildAnchor)
                                    continue;
                              
                              emot = mark.Emoticon;
                              break;
                        }
                        
                        if (emot != null)
                              menu = MenuUtility.CreateContextMenu ("/Galaxium/Gui/MessageDisplay/Emoticons/Menu", new DefaultExtensionContext (emot));
                  }
                  else
                  {
                        //TODO: uncomment + pass correct extension path
                        //Menu menu = MenuUtility.CreateContextMenu ("/Galaxium/", new DefaultExtensionContext (this));
                  }
                  
                  if (menu != null)
                  {
                        menu.Popup (null, null, null, args.Event.Button, args.Event.Time);
                        menu.ShowAll ();
                  }
            }
            
            protected override bool OnKeyPressEvent (Gdk.EventKey e)
            {
                  base.OnKeyPressEvent (e);
                  return false;
            }
            
            protected override bool OnButtonPressEvent (Gdk.EventButton e)
            {
                  if ((e.Button == 3) && (_mouseOverIter.ChildAnchor != null)) //disable right click menu
                        return false;
                  
                  if ((e.Button == 1) && (_activeLink != null))
                        BaseUtility.SafeOpenURL (_activeLink.URL);
                  
                  return base.OnButtonPressEvent (e);
            }
            
            protected override bool OnMotionNotifyEvent (EventMotion evnt)
            {
                  bool ret = base.OnMotionNotifyEvent (evnt);
                  
                  int x, y;
                  WindowToBufferCoords (TextWindowType.Widget, (int)evnt.X, (int)evnt.Y, out x, out y);
                  ChangeCursorIfNeeded (x, y);
                  return ret;
            }
            
            protected override bool OnVisibilityNotifyEvent (EventVisibility evnt)
            {
                  bool ret = base.OnVisibilityNotifyEvent (evnt);
                  
                  int wx, wy, x, y;
                  GetPointer (out wx, out wy);
                  WindowToBufferCoords (TextWindowType.Widget, wx, wy, out x, out y);
                  ChangeCursorIfNeeded (x, y);  
                  
                  return ret;
            }
            
            protected virtual void ChangeCursorIfNeeded (int x, int y)
            {
                  bool mouseOver = false;
                  LinkTag linkTag = null;
                  
                  _mouseOverIter = GetIterAtLocation (x, y);
                  foreach (TextTag tag in _mouseOverIter.Tags)
                  {
                        if (tag is LinkTag)
                        {
                              linkTag = (LinkTag)tag;
                              mouseOver = true;
                              
                              break;
                        }
                  }
                  
                  if (mouseOver != _mouseOverLink)
                  {
                        _mouseOverLink = mouseOver;
                        
                        GetWindow (TextWindowType.Text).Cursor = mouseOver ? _cursorHand : _cursorPointer;
                        _activeLink = mouseOver ? linkTag : null;
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index