context: /* --------------------------------------------------------------------------
   * Copyrights
   *
   * Portions created by or assigned to Cursive Systems, Inc. are
   * Copyright (c) 2002-2008 Cursive Systems, Inc.  All Rights Reserved.  Contact
   * information for Cursive Systems, Inc. is available at
   * http://www.cursive.net/.
   *
   * License
   *
   * Jabber-Net can be used under either JOSL or the GPL.
   * See LICENSE.txt for details.
   * --------------------------------------------------------------------------*/
  using System;
  
  using System.Collections;
  using System.ComponentModel;
  using System.ComponentModel.Design;
  using System.Drawing;
  using System.Diagnostics;
  using System.Windows.Forms;
  
  using bedrock.collections;
  using bedrock.util;
  using jabber;
  using jabber.client;
  using jabber.protocol.client;
  using jabber.protocol.iq;
  
  namespace muzzle
  {
      /// <summary>
      /// A TreeView optimized for showing Jabber roster items.  Make sure that the
      /// form you drop this on has a JabberClient, a PresenceManager, and a RosterManager
      /// on the form first, and this widget will automatically connect to them.
      /// </summary>
      [SVN(@"$Id: RosterTree.cs 680 2008-06-12 18:35:08Z hildjj $")]
      public class RosterTree : System.Windows.Forms.TreeView
      {
          // image list offsets
          private const int OFFLINE    = 0;
          private const int ONLINE     = 1;
          private const int AWAY       = 2;
          private const int XA         = 3;
          private const int DND        = 4;
          private const int CHATTY     = 5;
          private const int EXPANDED   = 6;
          private const int COLLAPSED  = 7;
  
          private const string UNFILED = "Unfiled";
  
          private RosterManager   m_roster = null;
          private PresenceManager m_pres   = null;
          private JabberClient    m_client = null;
  
          private IDictionary m_groups = new SkipList();
          private IDictionary m_items  = new SkipList();
  
          private System.Windows.Forms.ImageList il;
          private System.Windows.Forms.ToolTip tt;
          private Color m_statusColor = Color.Teal;
  
          private System.ComponentModel.IContainer components;
  
          /// <summary>
          /// Create a new RosterTree
          /// </summary>
          public RosterTree()
          {
              // This call is required by the Windows.Forms Form Designer.
              InitializeComponent();
              this.ImageIndex = 1;
              this.ImageList = il;
              this.ShowRootLines = false;
              this.ShowLines = false;
              this.Sorted = true;
  
              this.AllowDrop = true;
              this.ItemDrag += new ItemDragEventHandler(RosterTree_ItemDrag);
              this.DragEnter += new DragEventHandler(RosterTree_DragEnter);
              this.DragOver += new DragEventHandler(RosterTree_DragOver);
              this.DragDrop += new DragEventHandler(RosterTree_DragDrop);
              this.AfterSelect += new TreeViewEventHandler(RosterTree_AfterSelect);
              this.DrawMode = TreeViewDrawMode.OwnerDrawText;
              this.DrawNode += new DrawTreeNodeEventHandler(RosterTree_DrawNode);
          }
  
          private void RosterTree_AfterSelect(object sender, TreeViewEventArgs e)
          {
          }
  
          private void DrawGroup(DrawTreeNodeEventArgs e)
          {
              GroupNode node = (GroupNode)e.Node;
              string counts = String.Format("({0}/{1})", node.Current, node.Total);
  
              if (node.IsSelected)
              {
                  string newText = node.GroupName + " " + counts;
                  e.DrawDefault = true;
                  if (node.Text != newText)
                      node.Text = newText;
                  return;
              }
              Graphics g = e.Graphics;
              Brush fg = new SolidBrush(this.ForeColor);
              Brush stat_fg = new SolidBrush(this.StatusColor);
  
              g.DrawString(node.GroupName, this.Font, fg, new Point(e.Bounds.Left, e.Bounds.Top), StringFormat.GenericTypographic);
              if (node.Total > 0)
              {
                  SizeF name_size = g.MeasureString(node.GroupName, this.Font);
                  g.DrawString(counts, this.Font, stat_fg, new PointF(e.Bounds.Left + name_size.Width, e.Bounds.Top), StringFormat.GenericTypographic);
              }
          }
  
          private void DrawItem(DrawTreeNodeEventArgs e)
          {
              ItemNode node = (ItemNode)e.Node;
              if (node.IsSelected)
              {
                  e.DrawDefault = true;
                  return;
              }
  
              Graphics g = e.Graphics;
              Brush fg = new SolidBrush(this.ForeColor);
              Brush stat_fg = new SolidBrush(this.StatusColor);
  
              g.DrawString(node.Nickname, this.Font, fg, new Point(e.Bounds.Left, e.Bounds.Top), StringFormat.GenericTypographic);
              if (node.Status != null)
              {
                  SizeF nick_size = g.MeasureString(node.Nickname, this.Font);
                  g.DrawString("(" + node.Status + ")", this.Font, stat_fg, new PointF(e.Bounds.Left + nick_size.Width, e.Bounds.Top), StringFormat.GenericTypographic);
              }
          }
  
  
          private void RosterTree_DrawNode(object sender, DrawTreeNodeEventArgs e)
          {
              if (e.Node is GroupNode)
                  DrawGroup(e);
              else if (e.Node is ItemNode)
                  DrawItem(e);
              else
                  e.DrawDefault = true; // or assert(false)
          }
  
          private GroupNode GetDropGroup(DragEventArgs e)
          {
              if (!e.Data.GetDataPresent(typeof(ItemNode)))
                  return null;
  
              Point pt = this.PointToClient(new Point(e.X, e.Y));
              TreeNode node = this.GetNodeAt(pt);
              while (!(node is GroupNode) && (node != null))
              {
                  node = node.Parent;
              }
              if (node == null)
                  return null;
  
              ItemNode item = e.Data.GetData(typeof(ItemNode)) as ItemNode;
              if (item.Parent == node)
                  return null;
              return (GroupNode)node;
          }
  
          private void RosterTree_DragDrop(object sender, DragEventArgs e)
          {
              GroupNode group = GetDropGroup(e);
              if (group == null)
                  return;
              ItemNode item = e.Data.GetData(typeof(ItemNode)) as ItemNode;
              GroupNode parent = (GroupNode)item.Parent;
              Item i = (Item)item.Item.CloneNode(true, m_client.Document);
              i.RemoveGroup(parent.GroupName);
              i.AddGroup(group.GroupName);
              m_roster.Modify(i);
          }
  
  
          private void RosterTree_DragOver(object sender, DragEventArgs e)
          {
              if (GetDropGroup(e) == null)
                  e.Effect = DragDropEffects.None;
              else
                  e.Effect = DragDropEffects.Move;
          }
  
  
          private void RosterTree_DragEnter(object sender, DragEventArgs e)
          {
              if (e.Data.GetDataPresent(typeof(ItemNode)))
                  e.Effect = DragDropEffects.Move;
              else
                  e.Effect = DragDropEffects.None;
          }
  
          private void RosterTree_ItemDrag(object sender, ItemDragEventArgs e)
          {
              if (e.Item is ItemNode)
                  this.DoDragDrop(e.Item, DragDropEffects.Move);
          }
  
          /// <summary>
          /// Clean up any resources being used.
          /// </summary>
          protected override void Dispose( bool disposing )
          {
              if( disposing )
              {
                  if(components != null)
                  {
                      components.Dispose();
                  }
              }
  
gt:             base.Dispose( disposing );