From df57a66a04dc9676235311a7b6c4023c65f61beb Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20LAVAL?= Date: Sun, 15 Jul 2007 20:24:46 +0200 Subject: [PATCH] /!\ Warning: this commit doesn't compile /! * Design changes * Added font configurability * Added some keybidding to navigate inside Circ * Revamped configuration system * Various cleaning and refractoring * Bugs fixing * Added missing files --- CirC/CilControls/ChannelControl.cs | 6 +- CirC/CilControls/ConnectionControl.cs | 2 +- CirC/CilControls/MainControl.cs | 33 ++-- CirC/Main.cs | 6 +- CirC/PluginsContainer.cs | 45 +++++ Circ.Backend.Cil/AssemblyInfo.cs | 2 + Circ.Backend.Cil/CilIrcChannel.cs | 34 +++- Circ.Backend.Cil/CilIrcConnection.cs | 31 ++-- Circ.Backend.Cil/CilIrcUser.cs | 1 + Circ.Frontend.GtkSharp/ChannelPanelImpl.cs | 2 +- Circ.Frontend.GtkSharp/ChannelTabLabel.cs | 17 ++ Circ.Frontend.GtkSharp/Circ.Frontend.GtkSharp.mdp | 21 +-- Circ.Frontend.GtkSharp/Factory.cs | 2 +- Circ.Frontend.GtkSharp/GtkFrontend.cs | 9 +- Circ.Frontend.GtkSharp/MainWindow.cs | 73 +++++++- Circ.Frontend.GtkSharp/MessagesPanel.cs | 18 +- Circ.Frontend.GtkSharp/ServerPanel.cs | 17 +- Circ.Frontend.GtkSharp/ServerPanelImpl.cs | 2 +- Circ.Frontend.GtkSharp/SettingsManager.cs | 20 +++ Circ.Frontend.GtkSharp/TabWidget.cs | 46 +++++ Circ.Frontend.GtkSharp/UsersPanel.cs | 4 +- .../gtk-gui/Circ.Frontend.GtkSharp.ChannelPanel.cs | 78 +++++++++ .../Circ.Frontend.GtkSharp.ChannelTabLabel.cs | 71 ++++++++ .../Circ.Frontend.GtkSharp.ConnectionDialog.cs | 5 - .../gtk-gui/Circ.Frontend.GtkSharp.MainWindow.cs | 36 ++-- .../gtk-gui/Circ.Frontend.GtkSharp.ServerPanel.cs | 53 ++++++ .../Circ.Frontend.GtkSharp.SettingsManager.cs | 77 ++++++++ Circ.Frontend.GtkSharp/gtk-gui/gui.stetic | 160 ++++++++++++++--- Circ.Lib/AssemblyInfo.cs | 1 + Circ.Lib/Backend/ChannelMessageBakery.cs | 193 +++++++++++++++++++++ Circ.Lib/Backend/ConnectionFactory.cs | 14 ++ Circ.Lib/Backend/ConnectionInfo.cs | 7 +- Circ.Lib/Backend/EventArgs.cs | 4 +- Circ.Lib/Backend/IrcChannel.cs | 30 +++- Circ.Lib/Backend/IrcConnection.cs | 94 ++++++---- Circ.Lib/Backend/Structs.cs | 20 +++ Circ.Lib/Circ.Lib.mdp | 4 +- Circ.Lib/Common/Logger.cs | 2 + Circ.Lib/Common/Options.cs | 83 ++++----- Circ.Lib/Controller/IChannelControl.cs | 2 +- Circ.Lib/Frontend/EventArgs.cs | 16 ++ .../{IDiscussionPanel.cs => IChannelPanel.cs} | 2 +- Circ.Lib/Frontend/IFactory.cs | 2 +- Circ.Lib/Frontend/IFrontend.cs | 2 +- Circ.Lib/Frontend/IMainWindow.cs | 2 + TODO | 5 +- Tests/ChannelMessageBakeryTest.cs | 95 ++++++++++ Tests/Tests.mdp | 51 +++--- Tests/Tests.pidb | Bin 240762 -> 41823 bytes data/Circ.conf | 4 +- data/{chat.png => circ.png} | Bin data/{chat_small.png => circ_small.png} | Bin data/pixmaps/chat.png | Bin 0 -> 1904 bytes 53 files changed, 1259 insertions(+), 245 deletions(-) create mode 100644 CirC/PluginsContainer.cs mode change 100755 => 100644 Circ.Backend.Cil/AssemblyInfo.cs create mode 100644 Circ.Frontend.GtkSharp/ChannelTabLabel.cs create mode 100644 Circ.Frontend.GtkSharp/SettingsManager.cs create mode 100644 Circ.Frontend.GtkSharp/TabWidget.cs create mode 100644 Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelPanel.cs create mode 100644 Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelTabLabel.cs create mode 100644 Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ServerPanel.cs create mode 100644 Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.SettingsManager.cs create mode 100644 Circ.Lib/Backend/ChannelMessageBakery.cs create mode 100644 Circ.Lib/Backend/Structs.cs create mode 100644 Circ.Lib/Frontend/EventArgs.cs rename Circ.Lib/Frontend/{IDiscussionPanel.cs => IChannelPanel.cs} (95%) create mode 100644 Tests/ChannelMessageBakeryTest.cs rewrite Tests/Tests.pidb (87%) rename data/{chat.png => circ.png} (100%) rename data/{chat_small.png => circ_small.png} (100%) create mode 100644 data/pixmaps/chat.png diff --git a/CirC/CilControls/ChannelControl.cs b/CirC/CilControls/ChannelControl.cs index 343dd4d..c4efd9b 100644 --- a/CirC/CilControls/ChannelControl.cs +++ b/CirC/CilControls/ChannelControl.cs @@ -17,7 +17,7 @@ namespace Circ public sealed class ChannelControl: IChannelControl { IrcChannel chan; - IDiscussionPanel frontend; + IChannelPanel frontend; ConnectionControl connCtrl; public ChannelControl(IrcChannel chan, ConnectionControl connCtrl) @@ -48,7 +48,7 @@ namespace Circ } } - public IDiscussionPanel Frontend { + public IChannelPanel Frontend { get { return frontend; } @@ -71,7 +71,7 @@ namespace Circ void UpdatePresentation() { - frontend = GuiFactory.Factory.GetDiscussionPanel(connCtrl.Frontend, this); + frontend = GuiFactory.Factory.GetChannelPanelnnCtrl.Frontend, this); connCtrl.AddChildDiscussion(frontend); Logger.Debug("Added new chan"); } diff --git a/CirC/CilControls/ConnectionControl.cs b/CirC/CilControls/ConnectionControl.cs index 7c9082e..6ec63d3 100644 --- a/CirC/CilControls/ConnectionControl.cs +++ b/CirC/CilControls/ConnectionControl.cs @@ -37,7 +37,7 @@ namespace Circ try { connection.Connect(); } catch (ConnectionException e) { - mainCtrl.Frontend.ShowErrorMessage(string.Format("Error while connecting to server : {0}{1}Error Message : {2}", this.connection.Server, + mainCtrl.Frontend.ShowErrorMessage(string.Format("Error while connecting to server : {0}{1}Error Message : {2}", this.connection.Info.Server, Environment.NewLine, e.Message)); } Logger.Debug("New Connection set up"); diff --git a/CirC/CilControls/MainControl.cs b/CirC/CilControls/MainControl.cs index 8fe4e71..043ee43 100644 --- a/CirC/CilControls/MainControl.cs +++ b/CirC/CilControls/MainControl.cs @@ -34,7 +34,7 @@ namespace Circ { IrcConnection.ConnectionCreated += ConnectionCreatedHandler; InitializeDefaultCommand(); - StartTheWholeThingUp(); + StartTheWholeThing(); } ~MainControl() @@ -59,16 +59,17 @@ namespace Circ disposed = true; } - void StartTheWholeThingUp() + void StartTheWholeThing() { Logger.Debug("MainControl fetching Frontend & Backend"); - frontend = AcquireFrontend(Options.GetStringConfig("frontend", Section.Controller)); - backend = AcquireBackend(Options.GetStringConfig("backend", Section.Controller)); + frontend = AcquireFrontend(Options.GetStringConfig("frontend")); + backend = AcquireBackend(Options.GetStringConfig("backend")); if (frontend == null || backend == null) Logger.Error(string.Format("Backend not loaded {0} ; Frontend not loaded : {1}", backend == null, frontend == null), null); GuiFactory.Factory = frontend.ToolkitFactory; + ConnectionFactory.Factory = backend.GetConnectionFactory(); Logger.Debug("Frontend & Backend fetched"); InitializePresentation(); @@ -81,7 +82,7 @@ namespace Circ frontend.ShowErrorMessage("The connection command you typed is ill-formed. The correct form is : \"/c irc.yourserver.org yourNick\""); return; } - ConnectNewServer(Circ.Backend.ConnectionInfo.GetDefault(args[0], args[1])); + ConnectionFactory.Factory.RequestConnection(Circ.Backend.ConnectionInfo.GetDefault(args[0], args[1])); }); cmdProc.RegisterCommand("j", delegate(object sender, string[] args) { if (args.Length != 1) { @@ -119,14 +120,12 @@ namespace Circ public IrcConnection ConnectNewServer(ConnectionInfo ci) { - /* - 1) Type typeOfGenericClass = System.Type.GetType("MyGenericClass`1") - 2) Type typeParameters = typeof(myGenericTypeToCreate) - 3) Type genericType = typeOfGenericClass.MakeGericType(typeparameters) - 4) ConstructorInfo ci = genericType.GetConstrucors()[0] - */ - if (factory == null) - factory = backend.GetConnectionFactory(); + + //1) Type typeOfGenericClass = System.Type.GetType("MyGenericClass`1") + //2) Type typeParameters = typeof(myGenericTypeToCreate) + //3) Type genericType = typeOfGenericClass.MakeGericType(typeparameters) + //4) ConstructorInfo ci = genericType.GetConstrucors()[0] + return factory.RequestConnection(ci); } @@ -167,7 +166,7 @@ namespace Circ IConnectionControl temp = null; foreach (IConnectionControl ctrl in connControls) { - if (ctrl.Backend.Server == serverName) { + if (ctrl.Backend.Info.Server == serverName) { temp = ctrl; break; } @@ -179,7 +178,11 @@ namespace Circ void InitializePresentation() { Logger.Debug("Initializing Presentation layer"); - presentationThread = new Thread(delegate() { frontend.StartPresentation(this); }); + + presentationThread = new Thread(delegate() { + frontend.StartPresentation(this); + }); + presentationThread.IsBackground = false; presentationThread.Name = "PresentationThreadChannel"; presentationThread.Start(); diff --git a/CirC/Main.cs b/CirC/Main.cs index da8fce7..ed2ce5a 100644 --- a/CirC/Main.cs +++ b/CirC/Main.cs @@ -38,7 +38,11 @@ namespace Circ try { Logger.Debug("Loading Plugins"); PluginsContainer.LoadPlugins(); - mainControl.Idle(); + try { + mainControl.Idle(); + } catch (Exception ex) { + mainControl.Frontend.ShowErrorMessage("An error has occured during the execution : " + ex.Message); + } } finally { PluginsContainer.Unload(); mainControl.Dispose(); diff --git a/CirC/PluginsContainer.cs b/CirC/PluginsContainer.cs new file mode 100644 index 0000000..8aff839 --- /dev/null +++ b/CirC/PluginsContainer.cs @@ -0,0 +1,45 @@ +#region License +/* Circ : Main program + * Copyright (C) 2007 LAVAL Jérémie + * + * This file is licensed under the terms of the LGPL. + * + * For the complete licence see the file COPYING. + */ +#endregion +using System; +using System.Collections.Generic; +using Mono.Addins; +using Circ.Plugins; + +namespace Circ +{ + public static class PluginsContainer + { + static IList pluginList = new List(); + + /*~PluginsContainer() + { + Unload(); + }*/ + + public static void LoadPlugins() + { + foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/Circ/Plugins")) { + Plugin temp = (Plugin)node.CreateInstance(); + if (!temp.InitializePlugin()) { + temp.Dispose(); + return; + } + pluginList.Add(temp); + Logger.Debug("New Plugin loaded : " + temp.Name); + } + } + + public static void Unload() + { + foreach (Plugin plugin in pluginList) + plugin.Dispose(); + } + } +} diff --git a/Circ.Backend.Cil/AssemblyInfo.cs b/Circ.Backend.Cil/AssemblyInfo.cs old mode 100755 new mode 100644 index 9334822..ce45fc8 --- a/Circ.Backend.Cil/AssemblyInfo.cs +++ b/Circ.Backend.Cil/AssemblyInfo.cs @@ -21,6 +21,8 @@ using System.Runtime.InteropServices; [assembly: Mono.Addins.AddinDependency("Circ", "0.1")] [assembly: Mono.Addins.Addin("Cil")] +[assembly: InternalsVisibleToAttribute("Tests")] + [assembly: ComVisible(false)] // The assembly version has following format : diff --git a/Circ.Backend.Cil/CilIrcChannel.cs b/Circ.Backend.Cil/CilIrcChannel.cs index 843dc37..a92004f 100644 --- a/Circ.Backend.Cil/CilIrcChannel.cs +++ b/Circ.Backend.Cil/CilIrcChannel.cs @@ -33,21 +33,21 @@ namespace Circ.Cil void InitBakery() { // TODO: turn this into static call using the sender arg - bakery.Message = delegate(IrcChannel sender, string[] args) { + bakery.Message += delegate(IrcChannel sender, string[] args) { Logger.Debug("Receiving message from " + sender.Name); OnMessageReceived(new ChannelMessageEventArgs(args[0], args[1])); }; - bakery.NewUserList = delegate(IrcChannel sender, string[] users) { + bakery.NewList += delegate(IrcChannel sender, string[] users) { Logger.Debug("Receiving users list from " + sender.Name); AddInitialUsers(users); }; - bakery.Topic = delegate(IrcChannel sender, string topic) { + bakery.Topic += delegate(IrcChannel sender, string topic) { OnTopicUpdated(new TopicEventArgs(topic)); }; - bakery.QuitOrPart = delegate(IrcChannel sender, string[] temp) { + bakery.Quit += delegate(IrcChannel sender, string[] temp) { Logger.Debug("An user has departed from " + sender.Name); IrcUser usertemp = null; if (temp[0] == null) @@ -58,7 +58,18 @@ namespace Circ.Cil OnUserDisconnected(new UserQuitEventArgs(temp[1] ?? string.Empty, usertemp)); }; - bakery.Join = delegate(IrcChannel sender, string[] infos) { + bakery.Part += delegate(IrcChannel sender, string[] temp) { + Logger.Debug("An user has departed from " + sender.Name); + IrcUser usertemp = null; + if (temp[0] == null) + return; + if ((usertemp = GetUserByName(temp[0])) == null) + return; + this.userList.Remove(usertemp); + OnUserDisconnected(new UserQuitEventArgs(temp[1] ?? string.Empty, usertemp)); + }; + + bakery.Join += delegate(IrcChannel sender, string[] infos) { // Dont add ourself if (infos[0].Equals(this.ParentConnection.Nick, StringComparison.Ordinal)) return; @@ -79,7 +90,7 @@ namespace Circ.Cil ParentConnection.SendRawMessage(Rfc.PrivMsg(Name, message)); } - protected override void CloseChannel (string byeMessage) + protected override void SendQuitMessage (string byeMessage) { ParentConnection.SendRawMessage(Rfc.Part(Name, byeMessage)); } @@ -130,8 +141,12 @@ namespace Circ.Cil }*/ } +#region worker methods +#endregion + +#region Helper methods private void AddInitialUsers(string[] names) { if (names == null) @@ -144,11 +159,15 @@ namespace Circ.Cil // HACK: WTF ? IndexOutOfRange even with names.Length for (int i = 0; i< names.Length - 1; i++) { if (names[i][0] == Rfc.HalfOpChar) - userList.Add(new CilIrcUser(names[i], "foo", this, true, false)); + userList.Add(new CilIrcUser(names[i].Substring(1), "foo", this, false, true)); + else if (names[i][0] == Rfc.OpChar) + userList.Add(new CilIrcUser(names[i].Substring(1), "foo", this, true, false)); else userList.Add(new CilIrcUser(names[i], "foo", this, false, false)); } + IrcUser[] temp = userList.ToArray(); + OnNewUserList(new UserListEventArgs(userList.ToArray())); } @@ -163,5 +182,6 @@ namespace Circ.Cil } return temp; } +#endregion } } diff --git a/Circ.Backend.Cil/CilIrcConnection.cs b/Circ.Backend.Cil/CilIrcConnection.cs index 177c4ff..cdc681a 100644 --- a/Circ.Backend.Cil/CilIrcConnection.cs +++ b/Circ.Backend.Cil/CilIrcConnection.cs @@ -13,6 +13,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections.Generic; +using System.Collections; using Circ.Backend; namespace Circ.Cil @@ -20,7 +21,7 @@ namespace Circ.Cil public sealed class CilIrcConnection: IrcConnection { // State info - IList channelsJoined = new List(); + List channelsJoined = new List(); // Config //const int DefaultTime = 1000; @@ -55,10 +56,8 @@ namespace Circ.Cil // Set common option client.LingerState = new LingerOption(true, 2); - ConnectionEventArgs e = new ConnectionEventArgs(this.Server, this.Info.Port); - try { - OnConnecting(e); + OnConnecting(); client.Connect(Info.Server, Info.Port); } catch (Exception ex) { client.Close(); @@ -84,7 +83,7 @@ namespace Circ.Cil // Inform that we are connected. Warning : Connected doesn't mean Identified // (i.e. you can't join channels until you are identified) - OnConnected(e); + OnConnected(); } public override void Disconnect() @@ -98,8 +97,8 @@ namespace Circ.Cil protected override void Dispose (bool managedRes) { - foreach (IrcChannel chan in JoinedChannels) - chan.CloseChannel(); + /*foreach (IrcChannel chan in JoinedChannels) + chan.CloseChannel();*/ // Disconnect the machine this.Disconnect(); @@ -139,17 +138,19 @@ namespace Circ.Cil public override IrcChannel JoinChannel(string channelName) { + if (!identified) { + while (!identified) + System.Threading.Thread.Sleep(300); + } IrcChannel temp = new CilIrcChannel(channelName, this); channelsJoined.Add(temp); OnChannelCreated(new ChannelEventArgs(temp)); return temp; } - public override IrcChannel[] JoinedChannels { + public override IList JoinedChannels { get { - IrcChannel[] temp = new IrcChannel[channelsJoined.Count]; - channelsJoined.CopyTo(temp, 0); - return temp; + return channelsJoined.AsReadOnly(); } } @@ -169,9 +170,9 @@ namespace Circ.Cil get { return Info.RealName; } } - public override string Server { +/* public override string Server { get { return Info.Server; } - } + }*/ public bool IsIdentified { get { return identified; } @@ -214,6 +215,8 @@ namespace Circ.Cil } catch (IOException ex) { Logger.Error(string.Format("Exception while reading data. Inner Exception : {O} ::: Error code : {1}", ex.InnerException.Message, ((ex.InnerException is SocketException) ? ((SocketException)ex.InnerException).ErrorCode.ToString() : "No code")), ex); + } catch (Exception ex) { + Logger.Error("Common error while reading", ex); } // Let other things happens Thread.Sleep(500); @@ -237,7 +240,7 @@ namespace Circ.Cil if (parser.RetrieveMessageType(e.Message) == MessageType.Reply && parser.RetrieveNumericalReply(e.Message) == 1) { this.MessageReceived -= Identification; - OnIdentified(new ConnectionEventArgs(this.Server, Info.Port)); + OnIdentified(); messageCount = 0; identified = true; } diff --git a/Circ.Backend.Cil/CilIrcUser.cs b/Circ.Backend.Cil/CilIrcUser.cs index fe3c1bf..c5b67d4 100644 --- a/Circ.Backend.Cil/CilIrcUser.cs +++ b/Circ.Backend.Cil/CilIrcUser.cs @@ -19,6 +19,7 @@ namespace Circ.Cil bool op; bool halfOp; + // TODO: refractoring : use an enum to describe op/half-op states internal CilIrcUser(string pseudo, string realName, IrcChannel parentChannel, bool op, bool halfOp) : base(pseudo, realName, parentChannel) { diff --git a/Circ.Frontend.GtkSharp/ChannelPanelImpl.cs b/Circ.Frontend.GtkSharp/ChannelPanelImpl.cs index 95896f4..eed5552 100644 --- a/Circ.Frontend.GtkSharp/ChannelPanelImpl.cs +++ b/Circ.Frontend.GtkSharp/ChannelPanelImpl.cs @@ -16,7 +16,7 @@ using System.Text.RegularExpressions; namespace Circ.Frontend.GtkSharp { - public partial class ChannelPanel: IDiscussionPanel + public partial class ChannelPanel: IChannelPanel { bool emoticon, url; TransformationPool pool = new TransformationPool(); diff --git a/Circ.Frontend.GtkSharp/ChannelTabLabel.cs b/Circ.Frontend.GtkSharp/ChannelTabLabel.cs new file mode 100644 index 0000000..74f62a1 --- /dev/null +++ b/Circ.Frontend.GtkSharp/ChannelTabLabel.cs @@ -0,0 +1,17 @@ + + +using System; + +namespace Circ.Frontend.GtkSharp +{ + + + public partial class ChannelTabLabel : Gtk.Bin + { + + public ChannelTabLabel() + { + this.Build(); + } + } +} diff --git a/Circ.Frontend.GtkSharp/Circ.Frontend.GtkSharp.mdp b/Circ.Frontend.GtkSharp/Circ.Frontend.GtkSharp.mdp index c899500..66ee61c 100644 --- a/Circ.Frontend.GtkSharp/Circ.Frontend.GtkSharp.mdp +++ b/Circ.Frontend.GtkSharp/Circ.Frontend.GtkSharp.mdp @@ -1,7 +1,7 @@ - + @@ -40,8 +40,6 @@ - - @@ -50,6 +48,14 @@ + + + + + + + + @@ -64,11 +70,6 @@ - - - - Circ.Frontend.GtkSharp.ChannelPanel - Circ.Frontend.GtkSharp.ServerPanel - - + + \ No newline at end of file diff --git a/Circ.Frontend.GtkSharp/Factory.cs b/Circ.Frontend.GtkSharp/Factory.cs index f24f012..de9e58d 100644 --- a/Circ.Frontend.GtkSharp/Factory.cs +++ b/Circ.Frontend.GtkSharp/Factory.cs @@ -15,7 +15,7 @@ namespace Circ.Frontend.GtkSharp { public class Factory: Circ.Frontend.IFactory { - public IDiscussionPanel GetDiscussionPanel(IServerPanel servPanel, IChannelControl ctrl) + public IChannelPanel GetChannelPanel(IServerPanel servPanel, IChannelControl ctrl) { return new ChannelPanel(servPanel, ctrl); } diff --git a/Circ.Frontend.GtkSharp/GtkFrontend.cs b/Circ.Frontend.GtkSharp/GtkFrontend.cs index d4d81a9..4675cc3 100644 --- a/Circ.Frontend.GtkSharp/GtkFrontend.cs +++ b/Circ.Frontend.GtkSharp/GtkFrontend.cs @@ -22,10 +22,15 @@ namespace Circ.Frontend.GtkSharp MainWindow mainWindow; bool inited = false; + // Static ctor to initialize Gtk first of all + static GtkFrontend() + { + Gtk.Application.Init(); + } + public void StartPresentation(IMainControl ctrl) { Mono.Unix.Catalog.Init("circ", "/usr/share/locale"); - Gtk.Application.Init(); mainWindow = new MainWindow(ctrl); mainCtrl = ctrl; inited = true; @@ -39,7 +44,7 @@ namespace Circ.Frontend.GtkSharp }); } - public void AddDiscussion(IDiscussionPanel panel) + public void AddDiscussion(IChannelPanel panel) { Gtk.Application.Invoke( delegate (object s, EventArgs e) { ChannelPanel temp = (ChannelPanel)panel; diff --git a/Circ.Frontend.GtkSharp/MainWindow.cs b/Circ.Frontend.GtkSharp/MainWindow.cs index b525d36..60318df 100644 --- a/Circ.Frontend.GtkSharp/MainWindow.cs +++ b/Circ.Frontend.GtkSharp/MainWindow.cs @@ -18,6 +18,9 @@ namespace Circ.Frontend.GtkSharp { IMainControl ctrl; + internal static ChannelPanel CurrentChannelPanel = null; + internal static ServerPanel CurrentServerPanel = null; + public MainWindow (IMainControl ctrl): base (Gtk.WindowType.Toplevel) { this.ctrl = ctrl; @@ -29,6 +32,13 @@ namespace Circ.Frontend.GtkSharp chatEntry.KeyPressEvent += ChatEntryTab; serverContainer.SwitchPage += UpdateNickname; //this.WindowStateEvent += delegate { this.UrgencyHint = false; }; + this.chatEntry.ReceivesDefault = true; + + this.serverContainer.SwitchPage += delegate(object sender, SwitchPageArgs e) { + CurrentServerPanel = (ServerPanel)this.serverContainer.GetNthPage((int)e.PageNum); + }; + + this.evtBox.KeyReleaseEvent += SwitchingShorcut; } protected void OnDeleteEvent (object sender, DeleteEventArgs a) @@ -57,9 +67,9 @@ namespace Circ.Frontend.GtkSharp dlg.Name = "Circ"; dlg.Comments = "Circ is an IRC client written in C# using the Mono Plateform. It has been designed from the ground up to be very extensible : you can choose different IRC engines (backends), use different interface (frontends) and extend Circ with plugins. It use the excellent Mono.Addins for addins loading and management. Currently Circ is shipped with an homemade IRC library as the main backend and with a GTK# frontend as the main interface. D-Bus integration is provided with plugins."; dlg.Authors = new string[] { "Jérémie LAVAL" }; - dlg.Icon = Gdk.Pixbuf.LoadFromResource("chat.png"); + dlg.Icon = Gdk.Pixbuf.LoadFromResource("circ_small.png"); dlg.Version = "0.1"; - dlg.Logo = Gdk.Pixbuf.LoadFromResource("chat.png"); + dlg.Logo = Gdk.Pixbuf.LoadFromResource("circ.png"); dlg.License = "Circ is licensed under the LGPL terms"; dlg.WrapLicense = true; dlg.Website = "http://netherilshade.free.fr/circ/"; @@ -74,21 +84,36 @@ namespace Circ.Frontend.GtkSharp Mono.Addins.Gui.AddinManagerWindow.Run(this); } + /*Widget CurrentPanelShown { + get { + Widget temp; + ((ServerPanel)this.evtBox.Child).CurrentPageWidget; + } + } + + PanelType CurrentPanelType { + get { + return (CurrentPanelShown is ServerPanel) ? PanelType.Server : PanelType.Channel; + } + }*/ + // HACK: too much ugly; replace it with the MainControl facilities provided with the Shop or something void ChatEntryActivated(object sender, EventArgs e) { if (chatEntry.Text.Length >= 2 && chatEntry.Text[0] == '/') { - object s = null; - try { + object s = CurrentChannelPanel; + if (s == null) + s = CurrentServerPanel; + /*try { if (((ServerPanel)this.serverContainer.CurrentPageWidget).CurrentPage == 0) s = ((ServerPanel)this.serverContainer.CurrentPageWidget).ConnectionControl.Backend; else s = ((ChannelPanel)((ServerPanel)this.serverContainer.CurrentPageWidget).CurrentPageWidget).ChannelControl.Backend.ParentConnection; - } catch {} + } catch {}*/ ctrl.CommandEntered(s, chatEntry.Text.Substring(1)); chatEntry.Text = string.Empty; - } else if (((ServerPanel)this.serverContainer.CurrentPageWidget).CurrentPage != 0) { + } else if (CurrentChannelPanel != null) { // Assume it's on a DiscussionContainer - ChannelPanel temp = (ChannelPanel)((ServerPanel)this.serverContainer.CurrentPageWidget).CurrentPageWidget; + ChannelPanel temp = CurrentChannelPanel; temp.SendMessage(chatEntry.Text); temp.AddSelfMessage(nickLabel.Text, chatEntry.Text); chatEntry.Text = string.Empty; @@ -106,9 +131,41 @@ namespace Circ.Frontend.GtkSharp temp.DoAutocompletation(sender, e); } + void SwitchingShorcut(object sender, KeyReleaseEventArgs e) { + if (!((e.Event.State & Gdk.ModifierType.SuperMask) > 0)) + return; + + if ((e.Event.State & Gdk.ModifierType.ControlMask) > 0) { + switch (e.Event.Key) { + case Gdk.Key.Right: + this.serverContainer.NextPage(); + break; + case Gdk.Key.Left: + this.serverContainer.PrevPage(); + break; + } + } else { + switch (e.Event.Key) { + case Gdk.Key.Right: + CurrentServerPanel.Pages.NextPage(); + break; + case Gdk.Key.Left: + CurrentServerPanel.Pages.PrevPage(); + break; + } + } + } + + // HACK: Backend info shouldn't be available like this internal void UpdateNickname(object sender, SwitchPageArgs e) { - nickLabel.Text = ((ServerPanel)this.serverContainer.CurrentPageWidget).Nickname + " : "; + if (CurrentServerPanel != null) + nickLabel.Text = CurrentServerPanel.Nickname + " : "; } } + + /*public enum PanelType { + Server = 1, + Channel + }*/ } \ No newline at end of file diff --git a/Circ.Frontend.GtkSharp/MessagesPanel.cs b/Circ.Frontend.GtkSharp/MessagesPanel.cs index 2509c3c..4e99fcc 100644 --- a/Circ.Frontend.GtkSharp/MessagesPanel.cs +++ b/Circ.Frontend.GtkSharp/MessagesPanel.cs @@ -34,13 +34,14 @@ namespace Circ.Frontend.GtkSharp this.CanFocus = false; background = new Gdk.Color (); - Gdk.Color.Parse (Options.GetStringConfig("background", Section.Frontend), ref background); + Gdk.Color.Parse (Options.GetStringConfig("background"), ref background); this.ModifyBg (StateType.Normal, background); this.ModifyBase (StateType.Normal, background); RgbColor textColor = LibContrast.GetBestColorFromBackground(new RgbColor((byte)(background.Red/257), (byte)(background.Green/257), (byte)(background.Blue/257)), ColorPalette.Aqua); this.ModifyText(StateType.Normal, new Gdk.Color(textColor.Red, textColor.Green, textColor.Blue)); + this.ModifyFont(Pango.FontDescription.FromString(Options.GetStringConfig("font"))); if (txtTags == null) InitTextTags(); @@ -66,26 +67,26 @@ namespace Circ.Frontend.GtkSharp // Tuning (follow Tango color scheme) - RgbColor hlColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("hl", Section.Frontend))); + RgbColor hlColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("hl"))); hl.ForegroundGdk = new Gdk.Color(hlColor.Red, hlColor.Green, hlColor.Blue); hl.PixelsBelowLines = 3; - RgbColor messageColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)Enum.Parse(typeof(ColorPalette), Options.GetStringConfig("author", Section.Frontend))); + RgbColor messageColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)Enum.Parse(typeof(ColorPalette), Options.GetStringConfig("author"))); message.ForegroundGdk = new Color(messageColor.Red, messageColor.Green, messageColor.Blue); message.PixelsBelowLines = 2; // This Enum.Parse() method is really really crappy - RgbColor authorColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)Enum.Parse(typeof(ColorPalette), Options.GetStringConfig("author", Section.Frontend))); + RgbColor authorColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)Enum.Parse(typeof(ColorPalette), Options.GetStringConfig("author"))); author.ForegroundGdk = new Color(authorColor.Red, authorColor.Green, authorColor.Blue); author.Weight = Pango.Weight.Bold; - RgbColor meColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("self", Section.Frontend))); + RgbColor meColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("self"))); itsme.ForegroundGdk = new Color(meColor.Red, meColor.Green, meColor.Blue); - RgbColor tiColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("date", Section.Frontend))); + RgbColor tiColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("date"))); time.ForegroundGdk = new Color(tiColor.Red, tiColor.Green, tiColor.Blue); - RgbColor noticeColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("notice", Section.Frontend))); + RgbColor noticeColor = LibContrast.GetBestColorFromBackground(backgroundColor, (ColorPalette)ColorPalette.Parse(typeof(ColorPalette), Options.GetStringConfig("notice"))); notice.ForegroundGdk = new Color(noticeColor.Red, noticeColor.Green, noticeColor.Blue); txtTags.Add(message); @@ -98,7 +99,8 @@ namespace Circ.Frontend.GtkSharp internal void ScrollToEnd() { - this.ScrollMarkOnscreen(endMark); + if (endMark.Visible) + this.ScrollMarkOnscreen(endMark); } } } diff --git a/Circ.Frontend.GtkSharp/ServerPanel.cs b/Circ.Frontend.GtkSharp/ServerPanel.cs index 4b010f9..65a0aef 100644 --- a/Circ.Frontend.GtkSharp/ServerPanel.cs +++ b/Circ.Frontend.GtkSharp/ServerPanel.cs @@ -29,10 +29,23 @@ namespace Circ.Frontend.GtkSharp this.Build(); this.textWindow.Add(messages); + }); + this.channelsNb.SwitchPage += PageSwitched; } - public int CurrentPage { + internal Notebook Pages { + get { + return this.channelsNb; + } + } + + void PageSwitched(object sender, SwitchPageArgs e) + { + MainWindow.CurrentChannelPanel = (ChannelPanel)this.channelsNb.GetNthPage(e.PageNum); + } + + /*public int CurrentPage { get { return this.channelsNb.CurrentPage; } @@ -61,6 +74,6 @@ namespace Circ.Frontend.GtkSharp public void NextPage() { this.channelsNb.NextPage(); - } + }*/ } } diff --git a/Circ.Frontend.GtkSharp/ServerPanelImpl.cs b/Circ.Frontend.GtkSharp/ServerPanelImpl.cs index 881b995..14f8c01 100644 --- a/Circ.Frontend.GtkSharp/ServerPanelImpl.cs +++ b/Circ.Frontend.GtkSharp/ServerPanelImpl.cs @@ -52,7 +52,7 @@ namespace Circ.Frontend.GtkSharp internal string ServerName { get { - return ctrl.Backend.Server; + return ctrl.Backend.Info.Server; } } diff --git a/Circ.Frontend.GtkSharp/SettingsManager.cs b/Circ.Frontend.GtkSharp/SettingsManager.cs new file mode 100644 index 0000000..706daa8 --- /dev/null +++ b/Circ.Frontend.GtkSharp/SettingsManager.cs @@ -0,0 +1,20 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace Circ.Frontend.GtkSharp { + + + public partial class SettingsManager : Gtk.Dialog { + + public SettingsManager() { + this.Build(); + } + } +} diff --git a/Circ.Frontend.GtkSharp/TabWidget.cs b/Circ.Frontend.GtkSharp/TabWidget.cs new file mode 100644 index 0000000..27a0eed --- /dev/null +++ b/Circ.Frontend.GtkSharp/TabWidget.cs @@ -0,0 +1,46 @@ +// Code adapted from Stetic which is Copyright (c) 2004 Novell, Inc + +using Gtk; +using System; + +namespace Circ.Frontend.GtkSharp +{ + public class TabWidget: Gtk.Bin + { + Label tabLabel; + Image hl; + + public TabWidget(string tabName, Notebook nb, int index) + { + tabLabel = new Label(tabName); + hl = new Gtk.Image (Gdk.Pixbuf.LoadFromResource("chat.png")); + + HBox tabLabel = new HBox (); + tabLabel.PackStart (hl, true, true, 0); + tabLabel.PackStart (tabLabel, true, true, 3); + Button b = new Button (new Gtk.Image ("gtk-close", IconSize.Menu)); + b.Relief = Gtk.ReliefStyle.None; + b.WidthRequest = b.HeightRequest = 24; + + b.Clicked += delegate (object s, EventArgs a) { + nb.RemovePage(index); + }; + + tabLabel.PackStart (b, false, false, 0); + this.Add(tabLabel); + this.ShowAll(); + this.hl.Hide(); + } + + public bool IsHl { + get { + return hl.Visible; + } set { + if (value) + hl.Show(); + else + hl.Hide(); + } + } + } +} diff --git a/Circ.Frontend.GtkSharp/UsersPanel.cs b/Circ.Frontend.GtkSharp/UsersPanel.cs index 7ff4f18..1e9c358 100644 --- a/Circ.Frontend.GtkSharp/UsersPanel.cs +++ b/Circ.Frontend.GtkSharp/UsersPanel.cs @@ -30,7 +30,7 @@ namespace Circ.Frontend.GtkSharp public void AddUser(string user) { this.usersList.Add(user); - Gtk.Application.Invoke( delegate(object s, EventArgs e) { + Gtk.Application.Invoke( delegate { ((ListStore)this.Model).AppendValues(user); }); usersChanged = true; @@ -38,7 +38,7 @@ namespace Circ.Frontend.GtkSharp public void AddUser(string[] users) { - Gtk.Application.Invoke( delegate(object ss, EventArgs e) { + Gtk.Application.Invoke( delegate { foreach (string s in users) { this.usersList.Add(s); ((ListStore)this.Model).AppendValues(s); diff --git a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelPanel.cs b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelPanel.cs new file mode 100644 index 0000000..32bf741 --- /dev/null +++ b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelPanel.cs @@ -0,0 +1,78 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace Circ.Frontend.GtkSharp { + + + public partial class ChannelPanel { + + private Gtk.VBox vbox2; + + private Gtk.Entry topicEntry; + + private Gtk.HPaned hpaned1; + + private Gtk.ScrolledWindow textWindow; + + private Gtk.ScrolledWindow userWindow; + + protected virtual void Build() { + Stetic.Gui.Initialize(); + // Widget Circ.Frontend.GtkSharp.ChannelPanel + Stetic.BinContainer.Attach(this); + this.Events = ((Gdk.EventMask)(256)); + this.Name = "Circ.Frontend.GtkSharp.ChannelPanel"; + // Container child Circ.Frontend.GtkSharp.ChannelPanel.Gtk.Container+ContainerChild + this.vbox2 = new Gtk.VBox(); + this.vbox2.Name = "vbox2"; + this.vbox2.Spacing = 6; + // Container child vbox2.Gtk.Box+BoxChild + this.topicEntry = new Gtk.Entry(); + this.topicEntry.Name = "topicEntry"; + this.topicEntry.IsEditable = false; + this.topicEntry.InvisibleChar = '●'; + this.vbox2.Add(this.topicEntry); + Gtk.Box.BoxChild w1 = ((Gtk.Box.BoxChild)(this.vbox2[this.topicEntry])); + w1.Position = 0; + w1.Expand = false; + w1.Fill = false; + w1.Padding = ((uint)(2)); + // Container child vbox2.Gtk.Box+BoxChild + this.hpaned1 = new Gtk.HPaned(); + this.hpaned1.Name = "hpaned1"; + this.hpaned1.Position = 329; + // Container child hpaned1.Gtk.Paned+PanedChild + this.textWindow = new Gtk.ScrolledWindow(); + this.textWindow.Name = "textWindow"; + this.textWindow.VscrollbarPolicy = ((Gtk.PolicyType)(1)); + this.textWindow.HscrollbarPolicy = ((Gtk.PolicyType)(2)); + this.textWindow.ShadowType = ((Gtk.ShadowType)(2)); + this.hpaned1.Add(this.textWindow); + Gtk.Paned.PanedChild w2 = ((Gtk.Paned.PanedChild)(this.hpaned1[this.textWindow])); + w2.Resize = false; + // Container child hpaned1.Gtk.Paned+PanedChild + this.userWindow = new Gtk.ScrolledWindow(); + this.userWindow.WidthRequest = 100; + this.userWindow.Name = "userWindow"; + this.userWindow.VscrollbarPolicy = ((Gtk.PolicyType)(1)); + this.userWindow.HscrollbarPolicy = ((Gtk.PolicyType)(2)); + this.userWindow.ShadowType = ((Gtk.ShadowType)(1)); + this.hpaned1.Add(this.userWindow); + this.vbox2.Add(this.hpaned1); + Gtk.Box.BoxChild w4 = ((Gtk.Box.BoxChild)(this.vbox2[this.hpaned1])); + w4.Position = 1; + this.Add(this.vbox2); + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.Show(); + } + } +} diff --git a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelTabLabel.cs b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelTabLabel.cs new file mode 100644 index 0000000..064fe22 --- /dev/null +++ b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ChannelTabLabel.cs @@ -0,0 +1,71 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace Circ.Frontend.GtkSharp { + + + public partial class ChannelTabLabel { + + private Gtk.HBox hbox1; + + private Gtk.Label chanName; + + private Gtk.Label message; + + private Gtk.Label hls; + + protected virtual void Build() { + Stetic.Gui.Initialize(); + // Widget Circ.Frontend.GtkSharp.ChannelTabLabel + Stetic.BinContainer.Attach(this); + this.Name = "Circ.Frontend.GtkSharp.ChannelTabLabel"; + // Container child Circ.Frontend.GtkSharp.ChannelTabLabel.Gtk.Container+ContainerChild + this.hbox1 = new Gtk.HBox(); + this.hbox1.Name = "hbox1"; + this.hbox1.Spacing = 4; + // Container child hbox1.Gtk.Box+BoxChild + this.chanName = new Gtk.Label(); + this.chanName.Name = "chanName"; + this.chanName.Xpad = 9; + this.chanName.LabelProp = Mono.Unix.Catalog.GetString("label1"); + this.chanName.UseMarkup = true; + this.hbox1.Add(this.chanName); + Gtk.Box.BoxChild w1 = ((Gtk.Box.BoxChild)(this.hbox1[this.chanName])); + w1.Position = 0; + w1.Expand = false; + w1.Fill = false; + // Container child hbox1.Gtk.Box+BoxChild + this.message = new Gtk.Label(); + this.message.Name = "message"; + this.message.LabelProp = Mono.Unix.Catalog.GetString("label2"); + this.message.UseMarkup = true; + this.hbox1.Add(this.message); + Gtk.Box.BoxChild w2 = ((Gtk.Box.BoxChild)(this.hbox1[this.message])); + w2.Position = 1; + w2.Expand = false; + w2.Fill = false; + // Container child hbox1.Gtk.Box+BoxChild + this.hls = new Gtk.Label(); + this.hls.Name = "hls"; + this.hls.LabelProp = Mono.Unix.Catalog.GetString("label3"); + this.hls.UseMarkup = true; + this.hbox1.Add(this.hls); + Gtk.Box.BoxChild w3 = ((Gtk.Box.BoxChild)(this.hbox1[this.hls])); + w3.Position = 2; + w3.Expand = false; + w3.Fill = false; + this.Add(this.hbox1); + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.Show(); + } + } +} diff --git a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ConnectionDialog.cs b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ConnectionDialog.cs index e143798..22721b2 100644 --- a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ConnectionDialog.cs +++ b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ConnectionDialog.cs @@ -71,7 +71,6 @@ namespace Circ.Frontend.GtkSharp { this.serverList = new Gtk.TreeView(); this.serverList.WidthRequest = 133; this.serverList.HeightRequest = 235; - this.serverList.CanFocus = true; this.serverList.Name = "serverList"; this.hbox2.Add(this.serverList); Gtk.Box.BoxChild w2 = ((Gtk.Box.BoxChild)(this.hbox2[this.serverList])); @@ -89,7 +88,6 @@ namespace Circ.Frontend.GtkSharp { this.table1.ColumnSpacing = ((uint)(6)); // Container child table1.Gtk.Table+TableChild this.entry3 = new Gtk.Entry(); - this.entry3.CanFocus = true; this.entry3.Name = "entry3"; this.entry3.IsEditable = true; this.entry3.InvisibleChar = '●'; @@ -99,7 +97,6 @@ namespace Circ.Frontend.GtkSharp { w3.YOptions = ((Gtk.AttachOptions)(4)); // Container child table1.Gtk.Table+TableChild this.entry4 = new Gtk.Entry(); - this.entry4.CanFocus = true; this.entry4.Name = "entry4"; this.entry4.IsEditable = true; this.entry4.InvisibleChar = '●'; @@ -111,7 +108,6 @@ namespace Circ.Frontend.GtkSharp { w4.YOptions = ((Gtk.AttachOptions)(4)); // Container child table1.Gtk.Table+TableChild this.entry5 = new Gtk.Entry(); - this.entry5.CanFocus = true; this.entry5.Name = "entry5"; this.entry5.IsEditable = true; this.entry5.InvisibleChar = '●'; @@ -178,7 +174,6 @@ namespace Circ.Frontend.GtkSharp { // Container child Circ.Frontend.GtkSharp.ConnectionDialog_ActionArea.Gtk.ButtonBox+ButtonBoxChild this.button1 = new Gtk.Button(); this.button1.CanDefault = true; - this.button1.CanFocus = true; this.button1.Name = "button1"; this.button1.UseStock = true; this.button1.UseUnderline = true; diff --git a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.MainWindow.cs b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.MainWindow.cs index a9d586d..cfe1cd4 100644 --- a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.MainWindow.cs +++ b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.MainWindow.cs @@ -29,6 +29,8 @@ namespace Circ.Frontend.GtkSharp { private Gtk.MenuBar menubar1; + private Gtk.EventBox evtBox; + private Gtk.Notebook serverContainer; private Gtk.HBox hbox1; @@ -82,13 +84,16 @@ namespace Circ.Frontend.GtkSharp { w3.Expand = false; w3.Fill = false; // Container child MainVBox.Gtk.Box+BoxChild + this.evtBox = new Gtk.EventBox(); + this.evtBox.Name = "evtBox"; + // Container child evtBox.Gtk.Container+ContainerChild this.serverContainer = new Gtk.Notebook(); - this.serverContainer.CanFocus = true; this.serverContainer.Name = "serverContainer"; this.serverContainer.CurrentPage = 0; - this.MainVBox.Add(this.serverContainer); - Gtk.Box.BoxChild w4 = ((Gtk.Box.BoxChild)(this.MainVBox[this.serverContainer])); - w4.Position = 1; + this.evtBox.Add(this.serverContainer); + this.MainVBox.Add(this.evtBox); + Gtk.Box.BoxChild w5 = ((Gtk.Box.BoxChild)(this.MainVBox[this.evtBox])); + w5.Position = 1; // Container child MainVBox.Gtk.Box+BoxChild this.hbox1 = new Gtk.HBox(); this.hbox1.Name = "hbox1"; @@ -96,26 +101,25 @@ namespace Circ.Frontend.GtkSharp { // Container child hbox1.Gtk.Box+BoxChild this.nickLabel = new Gtk.Label(); this.nickLabel.Name = "nickLabel"; - this.nickLabel.LabelProp = Mono.Unix.Catalog.GetString("Your Nick : "); + this.nickLabel.LabelProp = Mono.Unix.Catalog.GetString("(null) : "); this.hbox1.Add(this.nickLabel); - Gtk.Box.BoxChild w5 = ((Gtk.Box.BoxChild)(this.hbox1[this.nickLabel])); - w5.Position = 0; - w5.Expand = false; - w5.Fill = false; + Gtk.Box.BoxChild w6 = ((Gtk.Box.BoxChild)(this.hbox1[this.nickLabel])); + w6.Position = 0; + w6.Expand = false; + w6.Fill = false; // Container child hbox1.Gtk.Box+BoxChild this.chatEntry = new Gtk.Entry(); - this.chatEntry.CanFocus = true; this.chatEntry.Name = "chatEntry"; this.chatEntry.IsEditable = true; this.chatEntry.InvisibleChar = '●'; this.hbox1.Add(this.chatEntry); - Gtk.Box.BoxChild w6 = ((Gtk.Box.BoxChild)(this.hbox1[this.chatEntry])); - w6.Position = 1; + Gtk.Box.BoxChild w7 = ((Gtk.Box.BoxChild)(this.hbox1[this.chatEntry])); + w7.Position = 1; this.MainVBox.Add(this.hbox1); - Gtk.Box.BoxChild w7 = ((Gtk.Box.BoxChild)(this.MainVBox[this.hbox1])); - w7.Position = 2; - w7.Expand = false; - w7.Fill = false; + Gtk.Box.BoxChild w8 = ((Gtk.Box.BoxChild)(this.MainVBox[this.hbox1])); + w8.Position = 2; + w8.Expand = false; + w8.Fill = false; this.Add(this.MainVBox); if ((this.Child != null)) { this.Child.ShowAll(); diff --git a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ServerPanel.cs b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ServerPanel.cs new file mode 100644 index 0000000..defe2e4 --- /dev/null +++ b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.ServerPanel.cs @@ -0,0 +1,53 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace Circ.Frontend.GtkSharp { + + + public partial class ServerPanel { + + private Gtk.Notebook channelsNb; + + private Gtk.ScrolledWindow textWindow; + + private Gtk.Label label3; + + protected virtual void Build() { + Stetic.Gui.Initialize(); + // Widget Circ.Frontend.GtkSharp.ServerPanel + Stetic.BinContainer.Attach(this); + this.Events = ((Gdk.EventMask)(256)); + this.Name = "Circ.Frontend.GtkSharp.ServerPanel"; + // Container child Circ.Frontend.GtkSharp.ServerPanel.Gtk.Container+ContainerChild + this.channelsNb = new Gtk.Notebook(); + this.channelsNb.Name = "channelsNb"; + this.channelsNb.CurrentPage = 0; + // Container child channelsNb.Gtk.Notebook+NotebookChild + this.textWindow = new Gtk.ScrolledWindow(); + this.textWindow.Name = "textWindow"; + this.textWindow.VscrollbarPolicy = ((Gtk.PolicyType)(1)); + this.textWindow.HscrollbarPolicy = ((Gtk.PolicyType)(2)); + this.textWindow.ShadowType = ((Gtk.ShadowType)(3)); + this.channelsNb.Add(this.textWindow); + Gtk.Notebook.NotebookChild w1 = ((Gtk.Notebook.NotebookChild)(this.channelsNb[this.textWindow])); + w1.TabExpand = false; + // Notebook tab + this.label3 = new Gtk.Label(); + this.label3.Name = "label3"; + this.label3.LabelProp = Mono.Unix.Catalog.GetString("Server Infos"); + this.channelsNb.SetTabLabel(this.textWindow, this.label3); + this.Add(this.channelsNb); + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.Show(); + } + } +} diff --git a/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.SettingsManager.cs b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.SettingsManager.cs new file mode 100644 index 0000000..0d2db81 --- /dev/null +++ b/Circ.Frontend.GtkSharp/gtk-gui/Circ.Frontend.GtkSharp.SettingsManager.cs @@ -0,0 +1,77 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace Circ.Frontend.GtkSharp { + + + public partial class SettingsManager { + + private Gtk.Notebook notebook1; + + private Gtk.Button button6; + + private Gtk.Button button7; + + protected virtual void Build() { + Stetic.Gui.Initialize(); + // Widget Circ.Frontend.GtkSharp.SettingsManager + this.Name = "Circ.Frontend.GtkSharp.SettingsManager"; + this.Title = Mono.Unix.Catalog.GetString("Settings Manager"); + this.WindowPosition = ((Gtk.WindowPosition)(4)); + this.HasSeparator = false; + // Internal child Circ.Frontend.GtkSharp.SettingsManager.VBox + Gtk.VBox w1 = this.VBox; + w1.Name = "dialog1_VBox"; + w1.BorderWidth = ((uint)(2)); + // Container child dialog1_VBox.Gtk.Box+BoxChild + this.notebook1 = new Gtk.Notebook(); + this.notebook1.Name = "notebook1"; + this.notebook1.CurrentPage = 0; + w1.Add(this.notebook1); + Gtk.Box.BoxChild w2 = ((Gtk.Box.BoxChild)(w1[this.notebook1])); + w2.Position = 0; + // Internal child Circ.Frontend.GtkSharp.SettingsManager.ActionArea + Gtk.HButtonBox w3 = this.ActionArea; + w3.Name = "dialog1_ActionArea"; + w3.Spacing = 6; + w3.BorderWidth = ((uint)(5)); + w3.LayoutStyle = ((Gtk.ButtonBoxStyle)(4)); + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.button6 = new Gtk.Button(); + this.button6.CanDefault = true; + this.button6.Name = "button6"; + this.button6.UseStock = true; + this.button6.UseUnderline = true; + this.button6.Label = "gtk-cancel"; + this.AddActionWidget(this.button6, -6); + Gtk.ButtonBox.ButtonBoxChild w4 = ((Gtk.ButtonBox.ButtonBoxChild)(w3[this.button6])); + w4.Expand = false; + w4.Fill = false; + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.button7 = new Gtk.Button(); + this.button7.CanDefault = true; + this.button7.Name = "button7"; + this.button7.UseStock = true; + this.button7.UseUnderline = true; + this.button7.Label = "gtk-ok"; + this.AddActionWidget(this.button7, -5); + Gtk.ButtonBox.ButtonBoxChild w5 = ((Gtk.ButtonBox.ButtonBoxChild)(w3[this.button7])); + w5.Position = 1; + w5.Expand = false; + w5.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.DefaultWidth = 592; + this.DefaultHeight = 273; + this.Show(); + } + } +} diff --git a/Circ.Frontend.GtkSharp/gtk-gui/gui.stetic b/Circ.Frontend.GtkSharp/gtk-gui/gui.stetic index db28d18..96f602b 100644 --- a/Circ.Frontend.GtkSharp/gtk-gui/gui.stetic +++ b/Circ.Frontend.GtkSharp/gtk-gui/gui.stetic @@ -3,9 +3,6 @@ ../ - - - @@ -77,20 +74,24 @@ - + - True - 0 - - - - + + + 0 + + + + + + + 1 - True + False @@ -100,7 +101,7 @@ - Your Nick : + (null) : 0 @@ -112,7 +113,6 @@ - True True @@ -165,7 +165,6 @@ 133 235 - True 0 @@ -188,7 +187,6 @@ - True True @@ -207,7 +205,6 @@ - True True @@ -228,7 +225,6 @@ - True True @@ -348,7 +344,6 @@ True - True True StockItem gtk-apply @@ -374,7 +369,6 @@ - True False @@ -389,12 +383,10 @@ - True - 324 + 329 - True Automatic Never Out @@ -416,7 +408,6 @@ 100 - True Automatic Never In @@ -446,12 +437,10 @@ - True 0 - True Automatic Never EtchedIn @@ -481,4 +470,125 @@ + + + + + + 4 + + + + 9 + label1 + True + + + 0 + True + False + False + + + + + + label2 + True + + + 1 + True + False + False + + + + + + <span color="red">label3</span> + True + + + 2 + True + False + False + + + + + + + + Settings Manager + CenterOnParent + 2 + False + False + + + + 2 + + + + 0 + + + + + + + + + 0 + True + + + + + + + + 6 + 5 + 2 + End + + + + True + True + StockItem + gtk-cancel + True + -6 + gtk-cancel + + + False + False + + + + + + True + True + StockItem + gtk-ok + True + -5 + gtk-ok + + + 1 + False + False + + + + + \ No newline at end of file diff --git a/Circ.Lib/AssemblyInfo.cs b/Circ.Lib/AssemblyInfo.cs index 7ffc471..c74cbf2 100644 --- a/Circ.Lib/AssemblyInfo.cs +++ b/Circ.Lib/AssemblyInfo.cs @@ -32,6 +32,7 @@ using System.Runtime.InteropServices; [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] [assembly: InternalsVisibleToAttribute("Circ")] +[assembly: InternalsVisibleToAttribute("Tests")] // The assembly version has following format : // diff --git a/Circ.Lib/Backend/ChannelMessageBakery.cs b/Circ.Lib/Backend/ChannelMessageBakery.cs new file mode 100644 index 0000000..5b66bc2 --- /dev/null +++ b/Circ.Lib/Backend/ChannelMessageBakery.cs @@ -0,0 +1,193 @@ +// /home/jeremie/CirCTemp/Circ.Lib/Backend/ChannelMessageBakery.cs +// Writtent by jeremie at 17:16 11/06/2007 +// +// This file is licensed under the LGPL licence as described in the COPYING file + +using System; + +namespace Circ.Backend +{ + // TODO: returning arrays isnt efficient IMO + public delegate void ChannelBakeryDelegate(IrcChannel sender, T obj); + /*public delegate void JoinDelegate(string[] joinInfos); + public delegate void QuitPartDelegate(string[] quitInfos); + public delegate void TopicDelegate(string topic); + public delegate void NewUserListDelegate(string[] users);*/ + + public class ChannelMessageBakery + { + IrcChannel parent; + string[] tokens; + + public event ChannelBakeryDelegate Part; + public event ChannelBakeryDelegate Message; + public event ChannelBakeryDelegate Join; + public event ChannelBakeryDelegate Quit; + public event ChannelBakeryDelegate Topic; + public event ChannelBakeryDelegate NewList; + public event ChannelBakeryDelegate NickChanged; + + public ChannelMessageBakery(IrcChannel parent) + { + this.parent = parent; + } + + public void PostNewMessage(string message) + { + Parse(message); + if (tokens == null) + return; + + /*foreach (string s in tokens) { + Console.Write(s); + Console.Write(" "); + } + Console.WriteLine(" ");*/ + + int code; + if (int.TryParse(tokens[1], out code)) + HandleReply(code); + else + HandleAction(); + } + + void Parse(string message) + { + int separator = -1; + for (int i = 2; i < message.Length; i++) { + if (message[i] == ':') { + separator = i; + break; + } + } + + if (separator != -1) { + string[] temp = message.Substring(0, separator - 1).Split(' '); + tokens = new string[temp.Length + 1]; + temp.CopyTo(tokens, 0); + tokens[tokens.Length - 1] = message.Substring(separator + 1); + } else { + tokens = message.Split(' '); + } + } + + void HandleAction() + { + string command = (tokens[0][0] == ':') ? tokens[1] : tokens[0]; + + if (command.Equals("PRIVMSG", StringComparison.Ordinal)) { + if(CheckDest(2)) + return; + + if (Message != null) + Message(parent, new string[] { tokens[0].Substring(1, tokens[0].IndexOf('!') - 1), tokens[3] }); + } else if (command.Equals("JOIN", StringComparison.Ordinal)) { + // Check name + if (CheckDest(tokens.Length - 1)) + return; + + int indice = tokens[0].IndexOf('!'); + string nick = tokens[0].Substring(1, indice - 1); + if (Join != null) + Join(parent, new string[] { nick, string.Empty }); + } else if (command.Equals("PART", StringComparison.Ordinal)) { + // TODO: handle the multipart message (with chan separated with commas) + if (CheckDest((tokens[0][0] == ':') ? 1 : 2)) + return; + JoinOrQuitProcess(Part); + } else if (command.Equals("QUIT", StringComparison.Ordinal)) { + JoinOrQuitProcess(Quit); + } + + /*Console.Write(command); + Console.Write(" "); + if (command.Equals("PRIVMSG", StringComparison.OrdinalIgnoreCase)) Console.WriteLine(tokens[3]); + Console.WriteLine(" ");*/ + } + + void HandleReply(int code) + { + switch (code) { + case Rfc.NamesReply: + if (!tokens[4].Equals(parent.Name, StringComparison.Ordinal)) + break; + if (NewList == null) + break; + string[] temp = tokens[tokens.Length - 1].Split(' '); + temp[0] = (temp[0][0] == ':') ? temp[0].Substring(1) : temp[0]; + NewList(parent, temp); + break; + case Rfc.TopicReply: + if (CheckDest(3)) + break; + if (Topic == null) + break; + Topic(parent, tokens[4]); + break; + } + } + + bool CheckDest(int index) + { + return !tokens[index].Equals(this.parent.Name, StringComparison.Ordinal); + } + + void JoinOrQuitProcess(ChannelBakeryDelegate evt) + { + if (evt == null) return; + + int indice = tokens[0].IndexOf('!'); + string nick = tokens[0].Substring(1, indice - 1); + + evt(parent, new string[] { nick, (tokens.Length >= 2) ? tokens[tokens.Length - 1] : string.Empty }); + } + + /*public ChannelBakeryDelegate Message { + set { + if (value == null) + return; + mess = value; + } + } + + public ChannelBakeryDelegate Join { + set { + if (value == null) + return; + join = value; + } + } + + public ChannelBakeryDelegate QuitOrPart { + set { + if (value == null) + return; + quit = value; + } + } + + public ChannelBakeryDelegate Topic { + set { + if (value == null) + return; + topic = value; + } + } + + public ChannelBakeryDelegate NewUserList { + set { + if (value == null) + return; + newList = value; + } + } + + public ChannelBakeryDelegate NickChanged { + set { + if (value == null) + return; + nickChanged = value; + } + }*/ + } +} diff --git a/Circ.Lib/Backend/ConnectionFactory.cs b/Circ.Lib/Backend/ConnectionFactory.cs index bfd6e9c..675fc9f 100644 --- a/Circ.Lib/Backend/ConnectionFactory.cs +++ b/Circ.Lib/Backend/ConnectionFactory.cs @@ -14,6 +14,20 @@ namespace Circ.Backend // where T : IrcConnection, new() public abstract class ConnectionFactory { + static ConnectionFactory factory; + + public static ConnectionFactory Factory { + get { + return factory; + } + set { + if (value == null) + return; + + factory = value; + } + } + /*public ConnectionFactory(Type t) { if (!t.IsSubclassOf(typeof(IrcConnection))) diff --git a/Circ.Lib/Backend/ConnectionInfo.cs b/Circ.Lib/Backend/ConnectionInfo.cs index b6b9dfa..964f717 100644 --- a/Circ.Lib/Backend/ConnectionInfo.cs +++ b/Circ.Lib/Backend/ConnectionInfo.cs @@ -27,7 +27,7 @@ namespace Circ.Backend /// This struct store all the information needed by IrcConnection /// /// If the password is null or empty, it is ignored - public class ConnectionInfo + public class ConnectionInfo: ICloneable { readonly string server; readonly int port; @@ -164,5 +164,10 @@ namespace Circ.Backend ci.QuitMessage = "Powered by Circ"; return ci; } + + public object Clone() + { + return this.MemberwiseClone(); + } } } diff --git a/Circ.Lib/Backend/EventArgs.cs b/Circ.Lib/Backend/EventArgs.cs index 7fe9b41..bdcc6a4 100644 --- a/Circ.Lib/Backend/EventArgs.cs +++ b/Circ.Lib/Backend/EventArgs.cs @@ -13,7 +13,7 @@ using System.Net.Sockets; namespace Circ.Backend { - public class ConnectionEventArgs: EventArgs + /*public class ConnectionEventArgs: EventArgs { public readonly string Server; public readonly int Port; @@ -23,7 +23,7 @@ namespace Circ.Backend Server = server; Port = port; } - } + }*/ public class MessageEventArgs: EventArgs { diff --git a/Circ.Lib/Backend/IrcChannel.cs b/Circ.Lib/Backend/IrcChannel.cs index c4fdf3d..bc1e880 100644 --- a/Circ.Lib/Backend/IrcChannel.cs +++ b/Circ.Lib/Backend/IrcChannel.cs @@ -11,7 +11,7 @@ using System; namespace Circ.Backend { - public abstract class IrcChannel + public abstract class IrcChannel: IDisposable { string channelName; IrcConnection connection; @@ -22,6 +22,27 @@ namespace Circ.Backend this.connection = ic; } + ~IrcChannel() + { + DisposeImpl(false); + } + + public void Dispose() + { + DisposeImpl(true); + System.GC.SuppressFinalize(this); + } + + void DisposeImpl(bool managedRes) + { + this.SendQuitMessage(connection.Info.QuitMessage); + Dispose(managedRes); + } + + protected virtual void Dispose(bool managedRes) + { + } + public string Name { get { return channelName; @@ -39,12 +60,7 @@ namespace Circ.Backend //public abstract IrcUser[] UsersPresent { get; } //public abstract IrcUser[] UsersAway { get; } - public virtual void CloseChannel() - { - CloseChannel(connection.Info.QuitMessage); - } - - protected abstract void CloseChannel(string byeMessage); + protected abstract void SendQuitMessage(string byeMessage); public abstract void SendMessage(string message); public override string ToString () diff --git a/Circ.Lib/Backend/IrcConnection.cs b/Circ.Lib/Backend/IrcConnection.cs index 0081240..87a1666 100644 --- a/Circ.Lib/Backend/IrcConnection.cs +++ b/Circ.Lib/Backend/IrcConnection.cs @@ -8,10 +8,12 @@ */ #endregion using System; +using System.Collections; +using System.Collections.Generic; namespace Circ.Backend { - public abstract class IrcConnection: IDisposable + public abstract class IrcConnection: IDisposable, IEnumerable { ConnectionInfo ci; bool disposed = false; @@ -42,16 +44,34 @@ namespace Circ.Backend public abstract void SendPrivateMessage(IrcUser user); */ - public abstract IrcChannel[] JoinedChannels { get; } + public abstract IList JoinedChannels { get; } - protected abstract void Dispose(bool managedRes); + IEnumerator IEnumerable.GetEnumerator () + { + return EnumeratorHelper(); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return EnumeratorHelper(); + } + + IEnumerator EnumeratorHelper () + { + foreach (IrcChannel chan in JoinedChannels) { + yield return chan; + } + } + + protected virtual void Dispose(bool managedRes) + {} ~IrcConnection() { if (disposed) return; - Dispose(false); + DisposeImpl(false); disposed = true; } @@ -61,85 +81,93 @@ namespace Circ.Backend if (disposed) return; - Dispose(true); + DisposeImpl(true); System.GC.SuppressFinalize(this); disposed = true; } + private void DisposeImpl(bool managedRes) + { + foreach (IrcChannel chan in this) { + chan.Dispose(); + } + Dispose(managedRes); + } + public ConnectionInfo Info { get { - return ci; + return (ConnectionInfo)ci.Clone(); } internal set { ci = value; } } - public abstract string Server { get; } + /*public abstract string Server { get; }*/ public abstract string Nick { get; set; } public abstract string RealName { get; } + public override string ToString () + { + return ci.Server; + } + #region events and events related stuff - public static event EventHandler ConnectionCreated; - public event EventHandler Connecting; - public event EventHandler Connected; - public event EventHandler Disconnecting; - public event EventHandler Disconnected; - public event EventHandler Identified; + public static event EventHandler ConnectionCreated; + public event EventHandler Connecting; + public event EventHandler Connected; + public event EventHandler Disconnecting; + public event EventHandler Disconnected; + public event EventHandler Identified; public event EventHandler MessageReceived; public event EventHandler MessageSent; public event EventHandler ChannelCreated; public event EventHandler NicknameChanged; - protected void OnConnecting(ConnectionEventArgs e) + protected void OnConnecting() { if (Connecting == null) return; - if (e == null) - throw new ArgumentNullException("e"); + - Connecting(this, e); + Connecting(this, EventArgs.Empty); } - protected void OnConnected(ConnectionEventArgs e) + protected void OnConnected() { if (Connected == null) return; - if (e == null) - throw new ArgumentNullException("e"); + - Connected(this, e); + Connected(this, EventArgs.Empty); } - protected void OnDisconnecting(ConnectionEventArgs e) + protected void OnDisconnecting() { if (Disconnecting == null) return; - if (e == null) - throw new ArgumentNullException("e"); + - Disconnecting(this, e); + Disconnecting(this, EventArgs.Empty); } - protected void OnDisconnected(ConnectionEventArgs e) + protected void OnDisconnected() { if (Disconnected == null) return; - if (e == null) - throw new ArgumentNullException("e"); + - Disconnected(this, e); + Disconnected(this, EventArgs.Empty); } - protected void OnIdentified(ConnectionEventArgs e) + protected void OnIdentified() { if (Identified == null) return; - if (e == null) - throw new ArgumentNullException("e"); + - Identified(this, e); + Identified(this, EventArgs.Empty); } protected void OnMessageReceived(MessageEventArgs e) diff --git a/Circ.Lib/Backend/Structs.cs b/Circ.Lib/Backend/Structs.cs new file mode 100644 index 0000000..269ee5b --- /dev/null +++ b/Circ.Lib/Backend/Structs.cs @@ -0,0 +1,20 @@ +///home/jeremie/CirCTemp/Circ.Lib/Backend/Structs.cs +//Writtent by jeremie at 18:16 14/06/2007 +// +//This file is licensed under the LGPL licence as described in the COPYING file + +using System; + +namespace Circ.Lib +{ + public struct Message + { + string message; + string author; + + public Message(string message, string author) + { + + } + } +} diff --git a/Circ.Lib/Circ.Lib.mdp b/Circ.Lib/Circ.Lib.mdp index e2fefbc..618515f 100644 --- a/Circ.Lib/Circ.Lib.mdp +++ b/Circ.Lib/Circ.Lib.mdp @@ -35,7 +35,7 @@ - + @@ -58,6 +58,8 @@ + + diff --git a/Circ.Lib/Common/Logger.cs b/Circ.Lib/Common/Logger.cs index 4846f71..1258082 100644 --- a/Circ.Lib/Common/Logger.cs +++ b/Circ.Lib/Common/Logger.cs @@ -61,7 +61,9 @@ namespace Circ { string message = (ex == null) ? mess : (mess + ", Exception type : " + ex.GetType().ToString() + ", Exception message : " + ex.Message); + Console.ForegroundColor = ConsoleColor.DarkRed; Log(LogType.Error, message); + Console.ResetColor(); } } } diff --git a/Circ.Lib/Common/Options.cs b/Circ.Lib/Common/Options.cs index 9ee4d64..6bd6cb6 100644 --- a/Circ.Lib/Common/Options.cs +++ b/Circ.Lib/Common/Options.cs @@ -9,6 +9,7 @@ #endregion using System; using System.IO; +using System.Diagnostics; using System.Collections.Generic; using Nini.Config; using Nini; @@ -29,21 +30,34 @@ namespace Circ config = new Configuration(); } - public static bool GetBooleanConfig(string option, Section sect) + public static bool GetBooleanConfig(string option) { - return cmd.IsDefined(option) ? true : config.InternalGetBooleanConfig(option, sect); + return cmd.IsDefined(option) ? true : config.InternalGetBooleanConfig(option, GetSection()); } - public static string GetStringConfig(string option, Section sect) + public static string GetStringConfig(string option) { - return cmd.IsDefined(option) ? cmd[option] : config.InternalGetStringConfig(option, sect); + return cmd.IsDefined(option) ? cmd[option] : config.InternalGetStringConfig(option, GetSection()); } - public static void SetStringConfig(string option, Section sect, string value) + public static void SetStringConfig(string option, string value) { } + public static void SetBooleanConfig(string option, bool value) + { + + } + + static string GetSection() + { + StackTrace st = new StackTrace(); + StackFrame sf = st.GetFrame(2); + + return (sf == null) ? "General" : sf.GetMethod().ReflectedType.Assembly.GetName().Name; + } + /*public static bool Plugins { get { return (cmd.IsDefined("Uplugins")) ? true : config.Plugins; @@ -87,9 +101,10 @@ namespace Circ { FileInfo fileInfo; IConfigSource source = null; - IConfig controller; + /*IConfig controller; IConfig backend; - IConfig frontend; + IConfig frontend;*/ + //Dictionary dict = new Dictionary(); internal Configuration(string filePath) { @@ -98,14 +113,19 @@ namespace Circ if (!fileInfo.Exists) { if (!fileInfo.Directory.Exists) fileInfo.Directory.Create(); - source = new IniConfigSource(fileInfo.Create()); - } else { - source = new IniConfigSource(fileInfo.OpenRead()); + StreamWriter sw = new StreamWriter(fileInfo.OpenWrite()); + Type t = typeof(Configuration); + StreamReader sr = new StreamReader(t.Assembly.GetManifestResourceStream("Circ.conf")); + sw.Write(sr.ReadToEnd()); + sw.Dispose(); + sr.Dispose(); } - controller = source.Configs["Controller"]; + source = new IniConfigSource(fileInfo.OpenRead()); + + /*controller = source.Configs["Controller"]; backend = source.Configs["Backend"]; - frontend = source.Configs["Frontend"]; + frontend = source.Configs["Frontend"];*/ /* AliasText alias = new AliasText(); alias.AddAlias("true", true); @@ -118,37 +138,17 @@ namespace Circ { } - public bool InternalGetBooleanConfig(string option, Section sect) + public bool InternalGetBooleanConfig(string option, string sectionName) { - switch (sect) { - case Section.Controller: - return controller.GetBoolean(option, false); - break; - case Section.Frontend: - return frontend.GetBoolean(option, false); - break; - case Section.Backend: - return backend.GetBoolean(option, false); - break; - } - return false; + return source.Configs[sectionName].GetBoolean(option, false); } - public string InternalGetStringConfig(string option, Section sect) + public string InternalGetStringConfig(string option, string sectionName) { - switch (sect) { - case Section.Controller: - return controller.GetString(option, string.Empty); - break; - case Section.Frontend: - return frontend.GetString(option, string.Empty); - break; - case Section.Backend: - return backend.GetString(option, string.Empty); - break; - } - return string.Empty; + return source.Configs[sectionName].GetString(option, string.Empty); } + + //public void InternalSetBoolean /*public string Frontend { get { @@ -299,11 +299,4 @@ namespace Circ } } } - - public enum Section { - Controller, - Backend, - Frontend, - Plugin - } } diff --git a/Circ.Lib/Controller/IChannelControl.cs b/Circ.Lib/Controller/IChannelControl.cs index 299b730..3918327 100644 --- a/Circ.Lib/Controller/IChannelControl.cs +++ b/Circ.Lib/Controller/IChannelControl.cs @@ -20,7 +20,7 @@ namespace Circ.Controller //void SendMessage(string message); //string ChannelName { get; } IrcChannel Backend { get; } - IDiscussionPanel Frontend { get; } + IChannelPanel Frontend { get; } void CloseChannel(); } diff --git a/Circ.Lib/Frontend/EventArgs.cs b/Circ.Lib/Frontend/EventArgs.cs new file mode 100644 index 0000000..21f0d3d --- /dev/null +++ b/Circ.Lib/Frontend/EventArgs.cs @@ -0,0 +1,16 @@ + + +using System; + +namespace Circ.Lib +{ + + + public class EventArgs + { + + public EventArgs() + { + } + } +} diff --git a/Circ.Lib/Frontend/IDiscussionPanel.cs b/Circ.Lib/Frontend/IChannelPanel.cs similarity index 95% rename from Circ.Lib/Frontend/IDiscussionPanel.cs rename to Circ.Lib/Frontend/IChannelPanel.cs index 64de66f..b61125f 100644 --- a/Circ.Lib/Frontend/IDiscussionPanel.cs +++ b/Circ.Lib/Frontend/IChannelPanel.cs @@ -11,7 +11,7 @@ using System; namespace Circ.Frontend { - public interface IDiscussionPanel + public interface IChannelPanel { void AddNewMessage(DateTime timestamp, string author, string message, MessageFlags flags); //void AddNewNotice(string notice); diff --git a/Circ.Lib/Frontend/IFactory.cs b/Circ.Lib/Frontend/IFactory.cs index 77c3ac6..ffc3f7d 100644 --- a/Circ.Lib/Frontend/IFactory.cs +++ b/Circ.Lib/Frontend/IFactory.cs @@ -14,7 +14,7 @@ namespace Circ.Frontend { public interface IFactory { - IDiscussionPanel GetDiscussionPanel (IServerPanel pannel, IChannelControl ctrl); + IChannelPanel GetChannelPanel (IServerPanel pannel, IChannelControl ctrl); IServerPanel GetServerPanel (IConnectionControl ctrl); } } diff --git a/Circ.Lib/Frontend/IFrontend.cs b/Circ.Lib/Frontend/IFrontend.cs index e3211c5..2a7ea28 100644 --- a/Circ.Lib/Frontend/IFrontend.cs +++ b/Circ.Lib/Frontend/IFrontend.cs @@ -22,7 +22,7 @@ namespace Circ.Frontend void StopPresentation(); void AddServer(IServerPanel serverPanel); - void AddDiscussion(IDiscussionPanel discussionsPanel); + void AddDiscussion(IChannelPanel discussionsPanel); void ShowErrorMessage(string message); diff --git a/Circ.Lib/Frontend/IMainWindow.cs b/Circ.Lib/Frontend/IMainWindow.cs index 776d656..96b5d9f 100644 --- a/Circ.Lib/Frontend/IMainWindow.cs +++ b/Circ.Lib/Frontend/IMainWindow.cs @@ -16,5 +16,7 @@ namespace Circ.Frontend void ShowWindow(); void HideWindow(); //IDiscussionContainer Discussions { get; } + + //event EventHandler< } } diff --git a/TODO b/TODO index 0cb6c36..6203f17 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -* Faire que les label des chan se colore en rouge dans le cas de nouveaux messages -* Permettre de se connecter à des serv et chan arbitraire (/keyword + méthode lasts) -* Tenir compte de la configuration +* Faire que les label des chan se colore en rouge dans le cas de nouveaux messages +* Permettre de se connecter à des serv et chan arbitraire (/keyword + méthode lasts) (Partially implemented) * Fixer le plugin X-Chat diff --git a/Tests/ChannelMessageBakeryTest.cs b/Tests/ChannelMessageBakeryTest.cs new file mode 100644 index 0000000..7bb8a4b --- /dev/null +++ b/Tests/ChannelMessageBakeryTest.cs @@ -0,0 +1,95 @@ +///home/jeremie/CirCTemp/Tests/ChannelMessageBakeryTest.cs +//Written by jeremie at 11:15 16/06/2007 +// +//This file is licensed under the LGPL licence as described in the COPYING file + +using System; +using Circ.Lib; +using Circ.Backend; +using NUnit.Core; +using NUnit.Framework; +using System.Collections.Generic; + +[TestFixtureAttribute] +public class ChannelMessageBakeryTest +{ + ChannelMessageBakery bakery; + IrcChannel chan; + + public ChannelMessageBakeryTest() + { + + } + + [SetUpAttribute] + public void Setup() + { + bakery = new ChannelMessageBakery(null); + } + +} + +internal class TestIrcConn : IrcConnection +{ + public TestIrcConn(): base() + { + } + + public override void Connect () + { + + } + + public override void Disconnect () + { + + } + + public override IrcChannel JoinChannel (string channelName) + { + return null; + } + + public override IList JoinedChannels { + get { return null; } + } + + public override string Nick { + get { return "Foo"; } + set { } + } + + protected override void Dispose (bool managedRes) + { + + } + + public override string RealName { + get { return string.Empty; } + } + + public override void SendRawMessage (string message) + { + + } +} + +internal class TestIrcChan: IrcChannel +{ + public TestIrcChan(string chan, IrcConnection parent): base(chan, parent) + { + + } + + protected override void SendQuitMessage(string byeMessage) + { + + } + + public override void SendMessage (string message) + { + + } + +} + diff --git a/Tests/Tests.mdp b/Tests/Tests.mdp index 0267fe0..a0eafa2 100644 --- a/Tests/Tests.mdp +++ b/Tests/Tests.mdp @@ -14,36 +14,37 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + + diff --git a/Tests/Tests.pidb b/Tests/Tests.pidb dissimilarity index 87% index f9d5c61a89f2039b15a4e15149799b29cfde2e62..b540739d77bf45e78dc8cc82f2354be2149c97c0 100644 GIT binary patch literal 41823 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 literal 240762 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