HubPage.cs :  » Business-Application » DC » DCSharp » GUI » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Business Application » DC 
DC » DCSharp » GUI » HubPage.cs
/*
 * Copyright (C) 2006 Eskil Bylund
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Text;

using Gtk;
using Glade;
using Mono.Unix;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Objects;
using DCSharp.Backend.Managers;
using DCSharp.Extras;

namespace DCSharp.GUI{
  public class HubPage : Page
  {
    private VisibleColumnsWindow columnsWindow;
    
    private ArrayList addQueue;
    private ArrayList updateQueue;
    private bool handlersAdded;
    
    private TextMark helperStartMark;
    private TextMark helperEndMark;
    private Widget separator;
    
    private TextTag nickTag;
    
    private bool hovering;
    private static Gdk.Cursor handCursor;
    private static Gdk.Cursor normalCursor;
    
    private UserStore userStore;
    private TreeModelFilter filterModel;
    private TreeModelSort sortModel;
    
    private SortType sortOrder;
    private int sortColumnId;
    
    private uint searchTimeoutId;
    
    [Widget]
    private Box mainBox;
    
    [Widget]
    private HPaned hpaned;
    
    [Widget]
    private ChatView chatView;
    
    [Widget]
    private Entry messageEntry;
    
    [Widget]
    private Entry searchEntry;
    
    [Widget]
    private UserView userView;
    
    #region Constructors
    
    static HubPage()
    {
      handCursor = new Gdk.Cursor(Gdk.CursorType.Hand2);
      normalCursor = new Gdk.Cursor(Gdk.CursorType.Xterm);
    }
    
    public HubPage(HubConnection hub) : base("HubPage.glade")
    {
      if (hub == null)
      {
        throw new ArgumentNullException("hub");
      }
      
      addQueue = ArrayList.Synchronized(new ArrayList());
      updateQueue = ArrayList.Synchronized(new ArrayList());
      
      // User interface
      ActionEntry[] entries = new ActionEntry[] {
        new ActionEntry("VisibleColumns", null, Catalog.GetString("_Visible Columns..."), null,
          Catalog.GetString("Edit visible columns"),
          OnVisibleColumns),
      };
      
      actionGroup = new ActionGroup("DownloadPageActions");
      actionGroup.Add(entries);
      
      // Widgets
      nickTag = chatView.Buffer.TagTable.Lookup("nick");
      
      TextTag tag = new TextTag("separator");
      tag.Justification = Justification.Center;
      chatView.Buffer.TagTable.Add(tag);
      
      chatView.MotionNotifyEvent += OnChatViewMotionNotifyEvent;
      chatView.WidgetEvent += OnChatViewWidgetEvent;
      chatView.Show();
      
      // User view
      userStore = new UserStore();
      filterModel = new TreeModelFilter(userStore, null);
      filterModel.VisibleFunc = FilterUsers;
      
      sortModel = new TreeModelSort(filterModel);
      sortModel.SetSortColumnId((int)UserStore.Column.Nick,
        SortType.Ascending);
      sortModel.SetSortFunc((int)UserStore.Column.ShareSize, SortByShareSize);
      
      // TODO: Enable custom sorting once the bug in GTK+ causing a
      // segfault when sorting is fixed.
      //sortModel.SetSortFunc((int)UserStore.Column.Nick, UserSortFunc);
      
      userView.Model = sortModel;
      userView.SetSizeRequest(140, 0);
      userView.Show();
      
      userView.GetColumn(1).Visible = false;
      
      // Message entry
      messageEntry = new UserEntry();
      
      // Modifying a TreeModel connected to a EntryCompletion can be
      // incredibly slow!
      messageEntry.FocusInEvent += delegate
      {
        messageEntry.Completion.Model = userStore;
      };
      messageEntry.FocusOutEvent += delegate
      {
        messageEntry.Completion.Model = null;
      };
      messageEntry.Activated += OnMessageEntryActivated;
      messageEntry.Show();
      messageEntry.GrabFocus();
      
      mainBox.PackEnd(messageEntry, false, false, 0);
      
      // Search entry
      searchEntry.Changed += OnSearchEntryChanged;
      searchEntry.Show();
      
      Hub = hub;
    }
    
    #endregion
    
    #region Classes
    
    private class UserEntry : Entry
    {
      public UserEntry()
      {
        Completion = new NickEntryCompletion();
        
        Completion.TextColumn = (int)UserStore.Column.Nick;
      }
      
      // This is the same hack as the GtkFileChooserEntry uses to keep
      // the focus in the entry when pressing tab.
      protected override bool OnFocused(DirectionType direction)
      {
        bool controlPressed = false;
        Gdk.ModifierType state;
        
        if (Gtk.Global.GetCurrentEventState(out state))
        {
          controlPressed = (state & Gdk.ModifierType.ControlMask) ==
            Gdk.ModifierType.ControlMask;
        }
        
        if (direction == DirectionType.TabForward && HasFocus &&
          !controlPressed)
        {
          Position = -1;
          
          return true;
        }
        return base.OnFocused(direction);
      }
    }
    
    private class NickEntryCompletion : EntryCompletion
    {
      public NickEntryCompletion()
      {
        InlineCompletion = true;
        PopupCompletion = true;
      }
      
      protected override bool OnMatchSelected(TreeModel model,
        TreeIter iter)
      {
        Entry entry = Entry as Entry;
        
        if (TextColumn >= 0)
        {
          string nick = model.GetValue(iter, TextColumn) as string;
        
          entry.Text = nick + ": ";
          entry.Position = -1;
          
          return true;
        }
        return false;
      }
    }
    
    #endregion
    
    #region Properties
    
    public override string Title
    {
      get { return Util.GetHubName(hub); }
    }
    
    public override Gdk.Pixbuf Icon
    {
      get { return RenderIcon(Stock.Network, IconSize.Menu, null); }
    }
    
    public override string Tooltip
    {
      get
      {
        string title = Util.GetHubName(hub);
        if (title != hub.Hostname)
        {
          title += " (" + hub.Hostname + ")";
        }
        if (hub.Name != null)
        {
          title += "\n";
          title += hub.Name;
        }
        return title;
      }
    }
    
    public override ActionGroup Actions
    {
      get { return actionGroup; }
    }
    private ActionGroup actionGroup;
    
    public int PanePosition
    {
      get { return hpaned.Position; }
      set { hpaned.Position = value; }
    }
    
    public int VisibleColumns
    {
      get { return Util.GetVisibleColumns(userView.Columns); }
      set { Util.SetVisibleColumns(userView.Columns, value | 1); }
    }
    
    /// <summary>
    /// Gets or sets whether or not to show the new message separator on a
    ///  new message.
    /// <summary>
    public bool ShowSeparatorOnMessage
    {
      get { return showSeparator; }
      set { showSeparator = value; }
    }
    private bool showSeparator;
    
    public HubConnection Hub
    {
      get { return hub; }
      set
      {
        if (hub != null)
        {
          hub.Message -= OnHubMessage;
          hub.NameChanged -= OnHubNameChanged;
          hub.StateChanged -= OnHubStateChanged;
          hub.Error -= OnHubError;
        }
        
        hub = value;
        if (hub != null)
        {
          hub.Message += OnHubMessage;
          hub.NameChanged += OnHubNameChanged;
          hub.StateChanged += OnHubStateChanged;
          hub.Error += OnHubError;
        }
      }
    }
    private HubConnection hub;
    
    #endregion
    
    #region Methods
    
    public override void Dispose()
    {
      HubConnection hub = this.hub;
      if (hub != null)
      {
        // Remove the event handlers
        Hub = null;
        
        hub.Disconnect();
      }
      if (columnsWindow != null)
      {
        columnsWindow.Destroy();
        columnsWindow = null;
      }
      base.Dispose();
    }
    
    /// <summary>
    /// Updates the position of the new message separator, moving it to the
    /// end of the chat view.
    /// </summary>
    public void UpdateNewMessageSeparator()
    {
      // TODO: Move code to the ChatView?
      TextIter startIter;
      TextIter endIter;
      TextBuffer buffer = chatView.Buffer;
      
      // Remove the previous anchor
      if (helperStartMark != null && helperEndMark != null)
      {
        startIter = chatView.Buffer.GetIterAtMark(helperStartMark);
        endIter = chatView.Buffer.GetIterAtMark(helperEndMark);
        
        buffer.Delete(ref startIter, ref endIter);
        buffer.DeleteMark(helperStartMark);
        buffer.DeleteMark(helperEndMark);
      }
      
      // Create a new anchor
      endIter = chatView.Buffer.EndIter;
      helperStartMark = buffer.CreateMark(null, endIter, true);
      
      TextChildAnchor anchor = buffer.CreateChildAnchor(ref endIter);
      buffer.Insert(ref endIter, "\n");
      
      startIter = buffer.GetIterAtMark(helperStartMark);
      buffer.ApplyTag("separator", startIter, endIter);
      
      helperEndMark = buffer.CreateMark(null, endIter, true);
      
      // Attach the separator to the anchor
      if (separator == null)
      {
        separator = new HSeparator();
        separator.SetSizeRequest(200, -1);
      }
      chatView.AddChildAtAnchor(separator, anchor);
    }
    
    protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
    {
      if (evnt.Key == Gdk.Key.Escape && hub != null &&
        hub.State == ConnectionState.Connecting)
      {
        hub.Disconnect();
        return true;
      }
      return base.OnKeyPressEvent(evnt);
    }
    
    protected void DisplayMessage(Identity user, string message)
    {
      // Display the new message separator
      if (showSeparator && separator != null)
      {
        separator.Show();
      }
      
      if (user != null)
      {
        bool self = user.Nick == hub.LocalIdentity.Nick;
        chatView.InsertMessage(user.Nick, message, self);
      }
      else
      {
        chatView.InsertText(message);
      }
      OnPageChanged();
    }
    
    protected void DisplayState(ConnectionState state)
    {
      string message;
      
      switch (state)
      {
        case ConnectionState.Connected:
          IsWorking = false;
          message = Catalog.GetString("Connected");
          break;
        
        case ConnectionState.Disconnected:
          IsWorking = false;
          message = Catalog.GetString("Disconnected");
          break;
        
        default:
          IsWorking = true;
          message = String.Format(
            Catalog.GetString("Connecting to {0}..."),
            hub.Hostname);
          break;
      }
      
      chatView.InsertStatus(message);
      OnPageChanged();
      
      Status = message;
    }
    
    protected void DisplayError(ConnectionError error)
    {
      string message;
      
      switch (error)
      {
        case ConnectionError.Dns:
          message = String.Format(
            Catalog.GetString("Could not resolve the address {0}"),
            hub.Hostname);
          break;
        
        case ConnectionError.Timeout:
          message = Catalog.GetString("The connection timed out");
          break;
        
        default:
          // FIXME: Should be "Could not connect to {0}".
          message = Catalog.GetString("There was an error connecting");
          break;
      }
      
      chatView.InsertError(message);
      OnPageChanged();
    }
    
    protected void UpdateStatus()
    {
      if (hub != null && hub.State == ConnectionState.Connected)
      {
        int count = hub.Users.Count;
        string status = Catalog.GetPluralString("{0} user online",
          "{0} users online", count);
        status = String.Format(status, count);
        
        Status = Catalog.GetString("Connected") + ". " + status;
      }
    }
    
    private bool FilterUsers(TreeModel model, TreeIter iter)
    {
      if (searchEntry.Text.Length == 0)
      {
        return true;
      }
      
      Identity user = model.GetValue(iter,
        (int)UserStore.Column.User) as Identity;
      
      if (user != null)
      {
        CultureInfo culture = CultureInfo.CurrentCulture;
        
        return culture.CompareInfo.IndexOf(user.Nick, searchEntry.Text,
          CompareOptions.IgnoreCase) >= 0;
      }
      return false;
    }
    
    private int UserSortFunc(TreeModel model, TreeIter tia, TreeIter tib)
    {
      Identity userA = model.GetValue(tia, (int)UserStore.Column.User) as Identity;
      Identity userB = model.GetValue(tib, (int)UserStore.Column.User) as Identity;
      
      if (userA != null && userB != null)
      {
        // Put operators before regular users
        if (userA.Op && !userB.Op)
        {
          return -1;
        }
        if (!userA.Op && userB.Op)
        {
          return 1;
        }
        return String.Compare(userA.Nick, userB.Nick);
      }
      
      // Fallback
      string namea = model.GetValue(tia, (int)UserStore.Column.Nick) as string;
      string nameb = model.GetValue(tib, (int)UserStore.Column.Nick) as string;
      
      return String.Compare(namea, nameb);
    }
    
    private void IdleAdd(Identity user)
    {
      bool addIdleHandler = addQueue.Count == 0;
      addQueue.Add(user);
      
      if (addIdleHandler)
      {
        GLib.Idle.Add(AddUsersInQueue);
      }
    }
    
    private void IdleUpdate(Identity user)
    {
      bool addIdleHandler = updateQueue.Count == 0;
      updateQueue.Add(user);
      
      if (addIdleHandler)
      {
        GLib.Idle.Add(UpdateUsersInQueue);
      }
    }
    
    private bool AddUsersInQueue()
    {
      long ticks = DateTime.Now.Ticks;
      
      DisableSorting();
      while (addQueue.Count > 0 &&
        hub.State == ConnectionState.Connected)
      {
        userStore.AddUser((Identity)addQueue[0], hub);
        addQueue.RemoveAt(0);
        
        // Are we blocking the GUI?
        if ((DateTime.Now.Ticks - ticks) >= 500000)
        {
          RestoreSorting();
          UpdateStatus();
          return true;
        }
      }
      RestoreSorting();
      UpdateStatus();
      return false;
    }
    
    private bool UpdateUsersInQueue()
    {
      long ticks = DateTime.Now.Ticks;
      
      DisableSorting();
      while (updateQueue.Count > 0 &&
        hub.State == ConnectionState.Connected)
      {
        Identity user = (Identity)updateQueue[0];
        updateQueue.Remove(user);
        
        // User disconnected
        if (!hub.Users.Contains(user))
        {
          addQueue.Remove(user);
          userStore.RemoveUser(user);
        }
        
        // Are we blocking the GUI?
        if ((DateTime.Now.Ticks - ticks) >= 500000)
        {
          RestoreSorting();
          UpdateStatus();
          return true;
        }
      }
      RestoreSorting();
      UpdateStatus();
      return false;
    }
    
    private void DisableSorting()
    {
      if (sortModel.GetSortColumnId(out sortColumnId, out sortOrder))
      {
        sortModel.SetSortColumnId(-1, SortType.Ascending);
      }
    }
    
    private void RestoreSorting()
    {
      sortModel.SetSortColumnId(sortColumnId, sortOrder);
    }
    
    private int SortByShareSize(TreeModel model, TreeIter a, TreeIter b)
    {
      Identity userA = (Identity)model.GetValue(a,
        (int)UserStore.Column.User);
      Identity userB = (Identity)model.GetValue(b,
        (int)UserStore.Column.User);
      
      return userA.ShareSize.CompareTo(userB.ShareSize);
    }
    
    private void OnChatViewWidgetEvent(object obj, WidgetEventArgs args)
    {
      Gdk.EventButton evnt = args.Event as Gdk.EventButton;
      if (evnt == null || evnt.Type != Gdk.EventType.ButtonPress ||
        evnt.Button != 1)
      {
        return;
      }
      
      int x, y;
      chatView.WindowToBufferCoords(TextWindowType.Text, (int)evnt.X,
        (int)evnt.Y, out x, out y);
      
      TextIter iter = chatView.GetIterAtLocation(x, y);
      TextIter end = iter;
      
      if (iter.HasTag(nickTag) && iter.BackwardToTagToggle(nickTag) &&
        end.ForwardToTagToggle(nickTag))
      {
        string nick = iter.GetText(end);
        nick = nick.Substring(0, nick.IndexOf(":"));
        
        Identity user = GetUser(nick);
        TreeIter treeIter;
        if (user != null && userStore.GetIter(user, out treeIter))
        {
          treeIter = filterModel.ConvertChildIterToIter(treeIter);
          treeIter = sortModel.ConvertChildIterToIter(treeIter);
          
          userView.Selection.SelectIter(treeIter);
          
          TreePath path = userView.Model.GetPath(treeIter);
          userView.ScrollToCell(path, userView.Columns[0], true,
            0.5f, 0f);
        }
        else
        {
          userView.Selection.UnselectAll();
        }
      }
    }
    
    private void OnChatViewMotionNotifyEvent(object obj,
      MotionNotifyEventArgs args)
    {
      int pointerX, pointerY;
      Gdk.ModifierType pointerMask;
      
      Gdk.Window window = chatView.GetWindow(TextWindowType.Text);
      window.GetPointer(out pointerX, out pointerY, out pointerMask);
      
      int x, y;
      chatView.WindowToBufferCoords(TextWindowType.Widget, pointerX,
        pointerY, out x, out y);
      
      TextIter iter = chatView.GetIterAtLocation(x, y);
      bool hovering = iter.HasTag(nickTag);
      
      if (hovering != this.hovering)
      {
        this.hovering = hovering;
        if (this.hovering)
        {
          window.Cursor = handCursor;
        }
        else
        {
          window.Cursor = normalCursor;
        }
      }
    }
    
    private Identity GetUser(string nick)
    {
      if (hub == null)
      {
        return null;
      }
      return hub.Users.Find(delegate(Identity user)
      {
        return user.Nick == nick;
      });
    }
    
    private void OnMessageEntryActivated(object obj, EventArgs args)
    {
      string message = messageEntry.Text.Trim();
      if (hub != null && message.Length > 0)
      {
        messageEntry.Text = String.Empty;
        
        hub.SendMessage(message);
      }
    }
    
    private void OnSearchEntryChanged(object obj, EventArgs args)
    {
      if (searchTimeoutId > 0)
      {
        GLib.Source.Remove(searchTimeoutId);
      }
      
      searchTimeoutId = GLib.Timeout.Add(300, delegate
      {
        filterModel.Refilter();
        return false;
      });
    }
    
    private void OnVisibleColumns(object obj, EventArgs args)
    {
      if (columnsWindow == null)
      {
        columnsWindow = new VisibleColumnsWindow(userView.Columns,
          Toplevel as Window, userView.Columns[0]);
        
        columnsWindow.Window.Destroyed += delegate
        {
          columnsWindow = null;
        };
      }
      columnsWindow.Show();
    }
    
    private void OnHubMessage(object obj, MessageEventArgs args)
    {
      Application.Invoke(delegate
      {
        DisplayMessage(args.From, args.Message);
      });
    }
    
    private void OnHubNameChanged(object obj, EventArgs args)
    {
      string name = ((HubConnection)obj).Name;
      
      Application.Invoke(delegate
      {
        string status = String.Format(
          Catalog.GetString("The topic is: {0}"), name);
        
        chatView.InsertStatus(status);
        OnPageChanged();
      });
    }
    
    private void OnHubStateChanged(object obj, EventArgs args)
    {
      ConnectionState state = hub.State;
      
      // Handle the user events
      if (state == ConnectionState.Connected)
      {
        if (!handlersAdded)
        {
          hub.Users.UserAdded += OnUserAdded;
          hub.Users.UserRemoved += OnUserRemoved;
          hub.Users.UserChanged += OnUserChanged; 
          handlersAdded = true;
        }
      }
      else if (state == ConnectionState.Disconnected)
      {
        hub.Users.UserAdded -= OnUserAdded;
        hub.Users.UserRemoved -= OnUserRemoved;
        hub.Users.UserChanged -= OnUserChanged;
        handlersAdded = false;
      }
      
      // Update the GUI
      Application.Invoke(delegate
      {
        if (state == ConnectionState.Connected)
        {
          messageEntry.Sensitive = true;
        }
        else if (state == ConnectionState.Disconnected)
        {
          addQueue.Clear();
          updateQueue.Clear();
          
          userStore.Clear();
          
          messageEntry.Sensitive = false;
        }
        DisplayState(state);
      });
    }
    
    private void OnHubError(object obj, ConnectionErrorEventArgs args)
    {
      Application.Invoke(delegate
      {
        DisplayError(args.Error);
      });
    }
    
    private void OnUserAdded(object obj, UserEventArgs args)
    {
      IdleAdd(args.User);
    }
    
    private void OnUserRemoved(object obj, UserEventArgs args)
    {
      IdleUpdate(args.User);
    }
    
    private void OnUserChanged(object obj, UserEventArgs args)
    {
      IdleUpdate(args.User);
    }
    
    #endregion
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.