has been started; it's goal is to replace the C++/CLI loader stub with a pure
+ IL stub consisting from 3 unmanaged exports only...
+
+v0.6.7.0
+==========================================================================================================
+- MenuItemDeclarationAttribute API refactored
+- StandardServices & StandardEvent class removed
+- ContactList now supports adding and modifying menu items
+- Some collection changes
+- ContactList.ProtocolStatusChange event added
+
+v0.6.6.0
+==========================================================================================================
+- All previous known issues resolved (probably caused by some hook-twice operation)
+- PluginContext is now MirandaContext
+- PluginContext.MirandaPInvokeBridge is not public now, it was visible due a bug
+- Miranda lazy event binding introduced (binds to Miranda's internal event only when somebody subscribes
+ a one)
+- PrivatePluginAttribute introduced (makes a plugin manually loadable only, fusion will ignore it)
+
+v0.6.5.0
+==========================================================================================================
+- Some optimalizations
+- KNOWN ISSUES
+ - MirandaDatabase::EventAdded event fired twice. I think Miranda does this, but don't know why...
+
+v0.6.3.0
+==========================================================================================================
+- Bunch of new stuff added, see for yourself...
+
+v0.5.8.0
+==========================================================================================================
+- Contact info query support
+- UnmanagedStructHandle´1 struct made public for use
+ (can be used for easy unmanaged memory allocation and deallocation)
+- Performance and stability tuning
+- MirandaPlugin base class expansion; some new virtual methods added
+- Threaded fusion
+
+v0.5.0.0
+==========================================================================================================
+- initial alpha release
\ No newline at end of file
diff --git a/Hyphen/Plugins/Docs/README.txt b/Hyphen/Plugins/Docs/README.txt
new file mode 100644
index 0000000..33def41
--- /dev/null
+++ b/Hyphen/Plugins/Docs/README.txt
@@ -0,0 +1,71 @@
+HYPHEN SRC README
+===================================================
+© 2006 - 2009, virtuoso
+deml.tomas@seznam.cz
+
+v0.8.2009.0201
+
+PREREQUISITES
+===================================================
+- Microsoft Visual Studio 2008 (no Express editions)
+ Full MS VS 2k8 is required because the build
+ solution features multilanguage projects.
+ (NMAKE, C#)
+- Microsoft .NET 2.0 SDK
+
+SOLUTION
+===================================================
+- In \Virtuoso.Hyphen\Virtuoso.Hyphen.sln
+- HYPHEN project should be set as START PROJECT
+
+- PLUGINS project
+ This is a C# project with Miranda .NET API.
+ It is build with VS and then disassembled using IlDasm.exe
+ into the Plugins.IL file.
+- HYPHEN project
+ This is an NMAKE project containing an MSIL stub
+ which acts as a bootstrapper for MS.NET.
+ It merges the Plugins.il with the LoaderStub.il
+ into a single Hyphen.dll assembly using IlAsm.exe.
+- HYPHEN.MINI project
+ This is an NMAKE project containg custom MSIL stub
+ for database and protocol plugins which cannot
+ be loaded with Hyphen directly.
+
+HOW TO BUILD
+===================================================
+- 1) Include
+ "%WINDIR%\Microsoft.NET\Framework\v2.0.50727" and
+ "%PROGRAMFILES%\Microsoft Visual Studio 8\SDK\v2.0\Bin"
+ in the PATH variable.
+ These paths contain IlAsm.exe and IlDasm.exe respectively
+ which are needed during the build process.
+
+- 2) Open the solution.
+
+- 3) Switch to the release configuration.
+
+- 4) Open the Hyphen project properties and change
+ the Output directory to point to
+ %YOUR_MIRANDA_FOLDER_GOES_HERE\Plugins directory.
+
+- 5) Press F6 to build the solution. You should see
+ IlAsm output in the output window and IlDasm window
+ with progress bar.
+
+- 6) Once it's done, you should find Hyphen.dll in your
+ Miranda\Plugins directory.
+
+PROBLEMS
+===================================================
+- If you are getting "Failed to define document writer"
+ errors during the build, make sure the
+ Hyphen\Bin\(CONFIG_NAME_HERE) folder exists.
+ CONFIG_NAME_HERE stands for Debug or Release.
+- Feel free to !MAIL! me in case of problems.
+
+
+SOURCES
+===================================================
+Source codes are mostly undocumented (unfortunately).
+
diff --git a/Hyphen/Plugins/Docs/SDK Docs/index.htm b/Hyphen/Plugins/Docs/SDK Docs/index.htm
new file mode 100644
index 0000000..6bd242f
--- /dev/null
+++ b/Hyphen/Plugins/Docs/SDK Docs/index.htm
@@ -0,0 +1,212 @@
+
+
+
+
+Midas SDK Docs
+
+
+
+
+
+Midas SDK Documentation v1.0
+Miranda .net Association Plugin SDK, © 2006 Virtuoso
+Contents
+
+ Overview
+ Architecture
+ Namespaces
+ Key namespaces
+ Native interop namespaces
+
+
+ Classes
+ Key classes
+ Plugin definition/construction classes
+ Miranda API wrapper classes
+
+
+ Native interop classes
+ Helper classes
+
+
+ A Hello World plugin Walkthrough
+
+
+Overview
+Midas (Miranda .net Assocication Plugin) is a plugin allowing Microsoft.net
+platform developers to access Miranda APIs thus allowing them to write Miranda
+targeted managed plugins.
+I created Midas because I'm an avid C# programmer and I wanted to extend
+Miranda in an easy, reliable and fast way - with Microsoft.net managed plugins.
+You can write Miranda plugins in any Ecma CLI compliant language, for example
+C#, J#, Delphi, C++/CLI or Visual Basic.net. Please note that you may not be
+able to take advantage of all Midas features from languages that do not support
+some CLS non-compliant constructs. You may have problems using Microsoft Visual
+Basic.net because it, for example, does not support unsigned data types. Miranda
+and Midas use these types (System.UIntPtr) extensively.
+In a nutshell, Midas represents a native-to-managed wrapper presenting core
+Miranda APIs in the OO fashion to .net. It wraps Miranda's procedural APIs to a
+neat and logical object oriented interface.
+Using Midas, you can write Miranda plugins that you or anybody else find
+useful. With .net plugins, you can accomplish complicated and complex tasks in
+the much faster way.
+
+Architecture
+Midas consinsts of two crucial assemblies:
+
+ Midas.dll
+ this assembly was written in IL (Intermediate Language) and contains 3
+ unmanaged exports exposing required unmanaged plugin API exports to Miranda
+ previously, there was an C++/CLI assembly but it's maintentace was not
+ as easy as it ought to be (and me, primarily .net programmer, concluded a
+ pure IL assembly as a more transparent way to accomplish the connection with
+ Miranda than the opaque IJW magic C++/CLI compiler uses)
+ this assembly instantiates the Virtuoso.Midas.Loader class that provides
+ Miranda with a PLUGININFO information and initiates the .net plugins fusion
+
+
+ Virtuoso.Miranda.Plugins.dll
+ this assembly was written in safe C# and exposes unmanaged Miranda APIs
+ to the .net world
+ in this assembly, there is the Virtuoso.Miranda.Plugins.PluginManager
+ type (not publicly exposed) that finds, loads and initiates all the managed
+ plugins in the <mirandaFolder>\plugins\managed folder
+ this assembly is the one you will want to reference on your plugin's
+ compile time
+ this assembly also contains the most important type for us, developers,
+ the Virtuoso.Mirandas.Plugins.MirandaPlugin class representing the base
+ class for all of your plugins
+
+
+
+
+Namespaces
+There are many publicly visible namespaces containing key classes and many
+native interop helper classes making easy to access not yet wrapped parts of the
+Miranda API.
+Key namespaces:
+
+ Virtuoso.Miranda.Plugins
+ contains funtionality concerning the plugin fusion, maintentace,
+ definition and construction
+
+
+ Virtuoso.Miranda.Plugins.Infrastructure
+ contains all the functionality you may need to develop your plugins;
+ provides Database, Contact List, Contact Information, Network Protocol and
+ many other Miranda API wrappers
+ also contains some helper classes you may find useful
+ (unmanaged-to-managed data translation services and localization helpers)
+
+
+ Virtoso.Miranda.Plugins.Infrastructure.IndividualMirandaConnection
+ contains additional helper classes for plugins not managed by the
+ Microsoft.net plugins supervisor
+
+
+ Virtuoso.Miranda.Plugins.ObjectCollections
+ contains specialized object collections
+
+
+
+Native interop namespaces:
+
+ Virtuoso.Miranda.Plugins.Native
+ contains types you may use when you need to directly interface with
+ Miranda APIs; provides you with unmanaged memory, string and struct handles
+
+
+
+
+Classes
+There are several classes representing the Miranda API wrappers for Database,
+Contact List and many other aspects of Miranda API model.
+Key classes:
+
+
+ Plugin definition/construction classes
+
+
+ Virtuoso.Miranda.Plugins namespace
+
+ abstract MirandaPlugin
+ represents a base class for all .net plugins
+ to define a plugin, derive your class from this one, implement
+ required abstract members and you are ready to go
+ members
+ methods
+ protected
+ void .ctor()
+ Initializes an instance of the MirandaPlugin class
+
+
+ virtual void AfterPluginInitialization()
+
+ executed after the first plugin initialization
+ executed only once a Midas lifetime
+ use this method to initialize your plugin after Miranda
+ startup, for example for additional event hook-ups
+ DO NOT EXECUTE ANY TIME CONSUMING CODE IN THIS METHOD
+ NEITHER SHOW ANY WINDOWS FORMS
+
+
+ virtual void AfterMenuItemsPopulation()
+ executed after the plugin menu items detection
+ executed only once a Midas lifetime
+ use this method to change properties or manipulate with your
+ plugin menu items
+ DO NOT EXECUTE ANY TIME CONSUMING CODE IN THIS METHOD
+ NEITHER SHOW ANY WINDOWS FORMS
+
+
+ virtual void AfterPluginEnable()
+ executed right after a plugin is enabled by the
+ user/supervisor
+ executed every time a plugin is enabled
+ DO NOT EXECUTE ANY TIME CONSUMING CODE IN THIS METHOD
+ NEITHER SHOW ANY WINDOWS FORMS
+
+
+ virtual void BeforePluginDisable()
+ executed right before a plugin is disabled by the
+ user/supervisor
+ use this method to unhook events or destroy service
+ functions you created manually; events and services declared
+ through attributes will be unhooked/destroyed automatically by
+ the plugin supervisor
+ DO NOT EXECUTE ANY TIME CONSUMING CODE IN THIS METHOD
+ NEITHER SHOW ANY WINDOWS FORMS
+
+
+ virtual void BeforeMirandaShutdown
+ executed right before Miranda or the plugin supervisor is
+ shutdown
+ use this method to perform possible memory cleanup
+ DO NOT ALLOCATE NEW MEMORY NEITHER INTERFACE WITH MIRANDA
+ THROUGH WRAPPERS OR NATIVE INTEROP FROM THIS METHOD; MIRANDA IS
+ ABOUT TO SHUTDOWN AND MANY OF ITS APIS ARE NO LONGER AVAILABLE;
+ USE THE BEFOREPLUGINDISABLE() METHOD INSTEAD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/ExposingPluginAttribute.cs b/Hyphen/Plugins/ExposingPluginAttribute.cs
new file mode 100644
index 0000000..92b0f52
--- /dev/null
+++ b/Hyphen/Plugins/ExposingPluginAttribute.cs
@@ -0,0 +1,57 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExposingPluginAttribute : Attribute
+ {
+ #region Fields
+
+ private Type pluginType;
+
+ #endregion
+
+ #region .ctors
+
+ public ExposingPluginAttribute(Type pluginType)
+ {
+ if (pluginType == null)
+ throw new ArgumentNullException("pluginType");
+
+ this.pluginType = pluginType;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public Type PluginType
+ {
+ get
+ {
+ return this.pluginType;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/ConfigurationDialog.Designer.cs b/Hyphen/Plugins/Forms/ConfigurationDialog.Designer.cs
new file mode 100644
index 0000000..f653422
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ConfigurationDialog.Designer.cs
@@ -0,0 +1,120 @@
+using Virtuoso.Miranda.Plugins.Forms.Controls;
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ partial class ConfigurationDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConfigurationDialog));
+ this.OkBTN = new System.Windows.Forms.Button();
+ this.CancelBTN = new System.Windows.Forms.Button();
+ this.MainPanel = new Virtuoso.Miranda.Plugins.Forms.Controls.ConfigurationPanel();
+ this.HideExpertOptionsCHBOX = new System.Windows.Forms.CheckBox();
+ this.SuspendLayout();
+ //
+ // OkBTN
+ //
+ this.OkBTN.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.OkBTN.Location = new System.Drawing.Point(556, 524);
+ this.OkBTN.Name = "OkBTN";
+ this.OkBTN.Size = new System.Drawing.Size(75, 23);
+ this.OkBTN.TabIndex = 1;
+ this.OkBTN.Text = "Save";
+ this.OkBTN.UseVisualStyleBackColor = true;
+ this.OkBTN.Click += new System.EventHandler(this.OkBTN_Click);
+ //
+ // CancelBTN
+ //
+ this.CancelBTN.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.CancelBTN.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.CancelBTN.Location = new System.Drawing.Point(637, 524);
+ this.CancelBTN.Name = "CancelBTN";
+ this.CancelBTN.Size = new System.Drawing.Size(75, 23);
+ this.CancelBTN.TabIndex = 1;
+ this.CancelBTN.Text = "Cancel";
+ this.CancelBTN.UseVisualStyleBackColor = true;
+ this.CancelBTN.Click += new System.EventHandler(this.CancelBTN_Click);
+ //
+ // MainPanel
+ //
+ this.MainPanel.Font = new System.Drawing.Font("Tahoma", 8F);
+ this.MainPanel.HideExpertOptions = false;
+ this.MainPanel.Location = new System.Drawing.Point(14, 13);
+ this.MainPanel.MinimumSize = new System.Drawing.Size(700, 500);
+ this.MainPanel.Name = "MainPanel";
+ this.MainPanel.Size = new System.Drawing.Size(700, 500);
+ this.MainPanel.TabIndex = 0;
+ //
+ // HideExpertOptionsCHBOX
+ //
+ this.HideExpertOptionsCHBOX.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.HideExpertOptionsCHBOX.AutoSize = true;
+ this.HideExpertOptionsCHBOX.Checked = global::Virtuoso.Miranda.Plugins.Properties.Settings.Default.ConfigurationDialog_HideExpertOptions_Checked;
+ this.HideExpertOptionsCHBOX.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::Virtuoso.Miranda.Plugins.Properties.Settings.Default, "ConfigurationDialog_HideExpertOptions_Checked", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
+ this.HideExpertOptionsCHBOX.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.HideExpertOptionsCHBOX.Location = new System.Drawing.Point(12, 530);
+ this.HideExpertOptionsCHBOX.Name = "HideExpertOptionsCHBOX";
+ this.HideExpertOptionsCHBOX.Size = new System.Drawing.Size(137, 17);
+ this.HideExpertOptionsCHBOX.TabIndex = 2;
+ this.HideExpertOptionsCHBOX.Text = "Hide expert options";
+ this.HideExpertOptionsCHBOX.UseVisualStyleBackColor = true;
+ this.HideExpertOptionsCHBOX.CheckedChanged += new System.EventHandler(this.HideExpertOptionsCHBOX_CheckedChanged);
+ //
+ // ConfigurationDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(724, 559);
+ this.Controls.Add(this.HideExpertOptionsCHBOX);
+ this.Controls.Add(this.CancelBTN);
+ this.Controls.Add(this.OkBTN);
+ this.Controls.Add(this.MainPanel);
+ this.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.Name = "ConfigurationDialog";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Configuration";
+ this.Shown += new System.EventHandler(this.ConfigurationDialog_Shown);
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ConfigurationDialog_FormClosing);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button OkBTN;
+ private System.Windows.Forms.Button CancelBTN;
+ private ConfigurationPanel MainPanel;
+ private System.Windows.Forms.CheckBox HideExpertOptionsCHBOX;
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/ConfigurationDialog.cs b/Hyphen/Plugins/Forms/ConfigurationDialog.cs
new file mode 100644
index 0000000..f8bf24a
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ConfigurationDialog.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Forms.Controls;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Miranda.Plugins.Properties;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ public sealed partial class ConfigurationDialog : SingletonDialog
+ {
+ #region Fields
+
+ private bool Ok;
+ private IConfigurablePlugin ConfigurableEntity;
+
+ #endregion
+
+ #region .ctors
+
+ private ConfigurationDialog(IConfigurablePlugin configurableEntity) : base(configurableEntity.Name)
+ {
+ InitializeComponent();
+
+ this.MainPanel.HideExpertOptions = HideExpertOptionsCHBOX.Checked;
+ this.ConfigurableEntity = configurableEntity;
+ this.Text = TextResources.UI_Caption_Configure_ + configurableEntity.Name;
+
+ configurableEntity.PopulateConfigurationPanel(MainPanel);
+ }
+
+ public static void Present(IConfigurablePlugin configurableEntity, bool modal)
+ {
+ Present(configurableEntity, null, modal);
+ }
+
+ public static void Present(IConfigurablePlugin configurableEntity, string path, bool modal)
+ {
+ if (configurableEntity == null)
+ throw new ArgumentNullException("configurableEntity");
+
+ ConfigurationDialog singleton = ConfigurationDialog.GetSingleton(false, configurableEntity.Name) ??
+ new ConfigurationDialog(configurableEntity);
+
+ if (singleton.MainPanel.Categories.Count == 0)
+ MessageBox.Show(TextResources.MsgBox_Text_NoOptionsAvailable, TextResources.MsgBox_Caption_NoOptionsAvailable, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ else
+ {
+ singleton.MainPanel.SetPath(path);
+ singleton.ShowSingleton(modal);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static string CreatePath(string categoryName, string itemName)
+ {
+ return ConfigurationPanel.CreatePath(categoryName, itemName);
+ }
+
+ #endregion
+
+ #region UI Handlers
+
+ private void ConfigurationDialog_Shown(object sender, EventArgs e)
+ {
+ MainPanel.Initialize();
+ }
+
+ private void OkBTN_Click(object sender, EventArgs e)
+ {
+ Ok = true;
+ Close();
+ }
+
+ private void CancelBTN_Click(object sender, EventArgs e)
+ {
+ Ok = false;
+ Close();
+ }
+
+ private void ConfigurationDialog_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (Ok)
+ {
+ ConfigurableEntity.Configuration.Save();
+ Settings.Default.Save();
+ }
+ else
+ {
+ ConfigurableEntity.ReloadConfiguration();
+ Settings.Default.Reload();
+ }
+ }
+
+ private void HideExpertOptionsCHBOX_CheckedChanged(object sender, EventArgs e)
+ {
+ MainPanel.HideExpertOptions = HideExpertOptionsCHBOX.Checked;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/ConfigurationDialog.resx b/Hyphen/Plugins/Forms/ConfigurationDialog.resx
new file mode 100644
index 0000000..45185c8
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ConfigurationDialog.resx
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+
+
+ AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAB
+ AAAAAAAAnoFlAKCEZwChhmkAo4lrAKOIbQCkiW0ApoxuAKiMbwCmjHEAqI5xAKiOdACrkXYArZJ1AK+V
+ dwCsk3kArpR5ALGXewCwl34Asph6ALGZfgC0m38AtZ1/ALOagAC1nYEAtZ2FALeghwC5oYYAvKKGALmh
+ iAC7pIkAu6ePAL2mjgC9qI8Av6qTAMCqkQDCrJMAwKuUAMKulQDErZYAxrKbAMi2oADKuKIAyrikAMy6
+ pgDOvakA0L+rANDArQDQwbAA1sm5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAB8PAAAAAAAAAAAAAAAPLwAIAwAsAQAAAAAAAAAACgoKCAYDAwEAAAAAAAAAIhQd
+ HxcSDwkGFwAAAAAXFBgiHxIUFA8PCgkCBgAAABIiIhQAAAAADw8JBgAAAAAUKiAAAAAAAAAPDwYAAB0U
+ FiocAAAAAAAADxQKBgAsHxYuHAAAAAAAAA8XDxQAAAAYLyIAAAAAADEXHw8AAAAvGCkqIC8AACwXIx0P
+ LAAAHRgiLiwiGB0fIicWDg8AAAAAACMpLy8qKigdAAAAAAAAAAAYGBgYGBYWFgAAAAAAAAAAIgAAHBgA
+ ABgAAAAAAAAAAAAAACkcAAAAAAAAAP5/AADyTwAA8A8AAOAHAACAAQAAw8MAAMfjAAAH4QAAB+EAAMfD
+ AACBgQAAgAEAAPAPAADwDwAA9m8AAP5/AAA=
+
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/Controls/CommandButton.cs b/Hyphen/Plugins/Forms/Controls/CommandButton.cs
new file mode 100644
index 0000000..e7b0ac7
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/CommandButton.cs
@@ -0,0 +1,54 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ public class CommandButton : Button
+ {
+ public CommandButton()
+ {
+ FlatStyle = FlatStyle.Standard;
+ ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ TextAlign = ContentAlignment.MiddleLeft;
+ TextImageRelation = TextImageRelation.ImageBeforeText;
+ FlatAppearance.BorderSize = 3;
+ FlatAppearance.BorderColor = SystemColors.GradientActiveCaption;
+ }
+
+ private void InitializeComponent()
+ {
+ this.SuspendLayout();
+ //
+ // CommandButton
+ //
+ this.FlatAppearance.BorderColor = System.Drawing.SystemColors.Control;
+ this.FlatAppearance.BorderSize = 2;
+ this.FlatAppearance.MouseDownBackColor = System.Drawing.SystemColors.ButtonShadow;
+ this.FlatAppearance.MouseOverBackColor = System.Drawing.SystemColors.ButtonHighlight;
+ this.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.ResumeLayout(false);
+
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/CommandButton.resx b/Hyphen/Plugins/Forms/Controls/CommandButton.resx
new file mode 100644
index 0000000..3276f5c
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/CommandButton.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/Controls/ContactListView.cs b/Hyphen/Plugins/Forms/Controls/ContactListView.cs
new file mode 100644
index 0000000..3086b5c
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/ContactListView.cs
@@ -0,0 +1,197 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.ObjectModel;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Windows.Forms;
+using System.Drawing;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ public class ContactListView : ListView
+ {
+ #region Fields
+
+ private ImageList ContactImages;
+ private System.ComponentModel.IContainer components;
+
+ #endregion
+
+ #region .ctors
+
+ public ContactListView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ContactListView));
+ this.ContactImages = new System.Windows.Forms.ImageList(this.components);
+ this.SuspendLayout();
+ //
+ // ContactImages
+ //
+ this.ContactImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ContactImages.ImageStream")));
+ this.ContactImages.TransparentColor = System.Drawing.Color.Transparent;
+ this.ContactImages.Images.SetKeyName(0, "Contact");
+ //
+ // ContactListView
+ //
+ this.SmallImageList = this.ContactImages;
+ this.Sorting = System.Windows.Forms.SortOrder.Ascending;
+ this.View = System.Windows.Forms.View.List;
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ #region Events
+
+ public event EventHandler FilterContact;
+
+ #endregion
+
+ #region Handlers
+
+ protected virtual object CreateItemTag(ContactInfo contact)
+ {
+ return contact;
+ }
+
+ protected virtual string CreateItemText(ContactInfo contact, object tag)
+ {
+ return contact.ToString();
+ }
+
+ protected virtual int GetImageIndex(ContactInfo contact, object tag)
+ {
+ return 0;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public virtual void LoadContacts()
+ {
+ ReadOnlyCollection contacts = MirandaContext.Current.MirandaDatabase.GetContacts(false);
+ Items.Clear();
+
+ ContactFilterEventArgs e = new ContactFilterEventArgs();
+
+ for (int i = 0; i < contacts.Count; i++)
+ InsertContact(contacts[i], e);
+ }
+
+ public virtual void InsertContact(ContactInfo contact)
+ {
+ InsertContact(contact, null);
+ }
+
+ private void InsertContact(ContactInfo contact, ContactFilterEventArgs e)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ if (e != null && FilterContact != null)
+ {
+ e.Contact = contact;
+ e.Skip = false;
+
+ FilterContact(this, e);
+
+ if (e.Skip)
+ return;
+ }
+
+ object tag = CreateItemTag(contact);
+
+ if (tag != null)
+ {
+ ListViewItem item = new ListViewItem(CreateItemText(contact, tag), GetImageIndex(contact, tag));
+ item.Tag = tag;
+
+ Items.Add(item);
+ }
+ }
+
+ public virtual bool RemoveContact(ContactInfo contact)
+ {
+ int index = -1;
+
+ for (int i = 0; index == -1 && i < Items.Count; i++)
+ if (Items[i].Tag.Equals(CreateItemTag(contact)))
+ index = i;
+
+ if (index != -1)
+ {
+ Items.RemoveAt(index);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ public virtual ListViewItem FindContactItem(ContactInfo contact)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ object tag = CreateItemTag(contact);
+
+ ListViewItem item = null;
+
+ if (Items.Count > 0)
+ item = FindItemWithText(CreateItemText(contact, tag), false, 0, false);
+
+ if (item == null)
+ return null;
+
+ if (object.ReferenceEquals(item.Tag, tag) || (tag != null && tag.Equals(item.Tag)))
+ return item;
+ else
+ return null;
+ }
+
+ #endregion
+ }
+
+ public class ContactFilterEventArgs : EventArgs
+ {
+ public ContactFilterEventArgs() { }
+
+ private ContactInfo contact;
+ public ContactInfo Contact
+ {
+ get { return contact; }
+ internal set { contact = value; }
+ }
+
+ private bool skip;
+ public bool Skip
+ {
+ get { return skip; }
+ set { skip = value; }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/ContactListView.resx b/Hyphen/Plugins/Forms/Controls/ContactListView.resx
new file mode 100644
index 0000000..5f0d18f
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/ContactListView.resx
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
+ LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAAq
+ BwAAAk1TRnQBSQFMAwEBAAEEAQABBAEAARABAAEQAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA
+ ARADAAEBAQABIAYAARAaAAEDAgEBAgEYAgEBHwFIAgIBggFVAgIBqgFcAgIBvgFkAQkBCAHWAVYCAgGp
+ ARICAQEX3AABDwIBARIBZQIIAdUBfQEwASoB7gHVAbABjgH/AegBvQGRAf8B7AHAAZcB/wHgAbYBjAH/
+ AdMBqgGBAf8BawEWAREB5AEmAgEBN9QAAQ0CAQEPAXIBHAEUAeQB4AG1AYoB/wHmAcgBtwH/AeMBvgGg
+ Af8B4QG3AY0B/wHlAb4BmQH/AdoBtgGRAf8B2QGwAYgB/wHQAaUBgQH/AUgCAgGB2AABYQIGAc0B4QGx
+ AYgB/wHLAboBrwH/AfIB2QHGAf8B6AG+AZEB/wHsAcgBpAH/AeEBwgGkAf8B2AGyAYsB/wHFAZ0BfwH/
+ AUECAQFw1AABmwGcAZMB/wFwASQBHAHsAfcBvAGVAf8BzQHAAbYB/wL9AfwC/wHQAaAB/wH1AcwBpgH/
+ AeYByQGtAf8B6AG/AZYB/wGPAVcBPQH5AYgBhwGEAf8EAAFeAgYB1wFeAQYBBAHWAV0CBgHZAVUCAgGr
+ wAABbgEgARwB5wHjAe4B5QH/AfMBzwGfAf8BxgGuAZ8B/wFMAXcBmQH/AZcBlQGUAf8B8QHCAZUB/wH3
+ AcwBogH/Ac4BpQGBAf8BigJ/Af4BXAENAQwB5gGCAYUBgQH/AfMB6AHbAf8B7QHiAdQC/wH3AewB/wF1
+ AS0BKAHtwAABbQEaARkB5QT/AesB4AHPAf8BfQF6AYEB/wEoAVYBgQH/ASIBUwGBAf8BlwGMAYEB/wHH
+ AaYBhQH/AdMBxgG8Af8B9wHtAeUB/wH3Ae0B5QH/AfcB7QHlAf8B9wHtAeUB/wH3Ae0B5QH/AfcB7QHl
+ Af8BcQEoASQB7MAAAW0BGgEZAeUE/wG8AbsBvQH/ATMBTwF9Af4BRgFrAYkB/wE4AWMBiAH/AWgBSAFJ
+ AfgB5QHbAdMB/wH3Ae0B5QH/AfcB7QHlAf8B9wHtAeUB/wH3Ae0B5QH/AfcB7QHlAf8B9wHtAeUB/wH3
+ Ae0B5QH/AXEBKAEkAezAAAFtARoBGQHlAfcB9QH2Af8BdQGKAaoB/wFWAX8BoAH/AVoBgQGgAf8BRQFw
+ AZEB/wGlAasBsQH/AdIBvAGwAf8B9wHtAeUB/wH3Ae0B5QH/AfcB7QHlAf8B9wHtAeUB/wH3Ae0B5QH/
+ AfcB7QHlAf8B9wHtAeUB/wFxASgBJAHswAABbQEaARkB5QHqAegB6gH/AXYBlAG6Af8BeAGZAb8B/wFu
+ AY0BswH/AVMBgAGiAf8BRQFeAXQB/wHGAbQBqgH/AfcB7QHlAf8BxgGzAaQB/wHGAbMBpAH/AcYBswGk
+ Af8BxgGzAaQB/wHGAbMBpAH/AfUB6QHgAf8BcQEoASQB7MAAAW0BGgEZAeUC5wHtAf8BjQGyAdcB/wGb
+ Ab8B5QH/AYEBpQHPAf8BWgF+AZkB/wE2AUIBTQH/AaYBmgGTAf8B9wHtAeUB/wHSAcIBtQH/AdQBxgG4
+ Af8B1AHGAbgB/wHUAcYBuAH/AdMBxAG2Af8B9wHtAeUB/wFxASkBJAHswAABbQEaARkB5QH0AfIB8wH/
+ AZEBrgHKAf4BnwHGAe4B/wGBAaoB1wH/AVYBawGBAf8BNwEuAScB/wGSAYcBgQH/AfcB7QHlAf8B0QHB
+ AbIB/wHUAcQBtwH/AdQBxAG3Af8B1AHGAbcB/wHTAcQBtgH/AfoB8QHpAf8BcQEpASQB7MAAAW0BGgEZ
+ AeUB+QH3AfYB/wGdAaIBqQH/AYABgQGLAf8BdQF9AYEB/wJaAVsB/wFFAUMBQQH/AcgBvgG4Af8B9wHt
+ AeUB/wHGAbMBpAH/AcYBswGkAf8BxgGzAaQB/wHGAbMBpAH/AcYBswGkAf8B+wH0Ae0B/wFzASYBIAHr
+ wAABbQEZARcB5QT/AdgBzgHHAf8BpQGfAZoB/wGBAXsBeQH/AXMBcQFuAf8BrQGmAaIB/wHpAdwB1AP/
+ Af4B/wH9AfsB9gH/AfoB9wH0Af8B9wH1AfIB/wH1AfIB7AH/AfMB7AHlAf8B/gH6AfQB/wFtAR0BHAHn
+ wAABaQEXARUB4xz/AYMCQAHvAXQBHwEeAeUBcQEfAR4B5QFxAR8BHgHlAXEBHwEeAeUBcQEfAR4B5QF0
+ ASIBHwHlAVACAgGYwAAB2AHbAdEB/wGRAZMBiAH/AZEBkwGIAf8BkQGTAYgB/wGRAZMBiAH/AZEBkwGI
+ Af8BkQGTAYgB/wGRAZMBiAH/AdgB2wHRAf/cAAFCAU0BPgcAAT4DAAEoAwABQAMAARADAAEBAQABAQUA
+ AYAXAAP/AQABwAE/BgABgAEfBwABHwYAAYABHwcAARBXAAF/BgAL
+
+
+
+ False
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/Controls/CueBannerTextBox.cs b/Hyphen/Plugins/Forms/Controls/CueBannerTextBox.cs
new file mode 100644
index 0000000..d1ee45d
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/CueBannerTextBox.cs
@@ -0,0 +1,189 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ ///
+ /// Draws a textbox with a prompt inside of it, similar to the "Quick Search" box
+ /// in Outlook 2007, IE7 or the Firefox 2.0 search box. The prompt will disappear when
+ /// the focus is placed in the textbox, and will not display again if the Text property
+ /// contains any value. If the Text property is empty, then the prompt will display
+ /// again when the textbox loses the focus.
+ ///
+ public class CueBannerTextBox : System.Windows.Forms.TextBox
+ {
+ // Windows message constants
+ const int WM_SETFOCUS = 7;
+ const int WM_KILLFOCUS = 8;
+ const int WM_ERASEBKGND = 14;
+ const int WM_PAINT = 15;
+
+ // private internal variables
+ private bool _focusSelect = true;
+ private bool _drawPrompt = true;
+ private string _bannerText = String.Empty;
+ private Color _bannerColor = SystemColors.GrayText;
+ private Font _bannerFont = null;
+
+ ///
+ /// Public constructor
+ ///
+ /// Uncomment the SetStyle line to activate the OnPaint logic in place of the WndProc logic
+ public CueBannerTextBox()
+ {
+ //this.SetStyle(ControlStyles.UserPaint, true);
+ this.BannerFont = this.Font;
+ }
+
+ [Browsable(true)]
+ [EditorBrowsable(EditorBrowsableState.Always)]
+ [Category("Appearance")]
+ [Description("The prompt text to display when there is nothing in the Text property.")]
+ public string BannerText
+ {
+ get { return _bannerText; }
+ set { if (value == null) value = String.Empty; _bannerText = value.Trim(); this.Invalidate(); }
+ }
+
+ [Browsable(true)]
+ [EditorBrowsable(EditorBrowsableState.Always)]
+ [Category("Appearance")]
+ [Description("The ForeColor to use when displaying the PromptText.")]
+ public Color BannerForeColor
+ {
+ get { return _bannerColor; }
+ set { _bannerColor = value; this.Invalidate(); }
+ }
+
+ [Browsable(true)]
+ [EditorBrowsable(EditorBrowsableState.Always)]
+ [Category("Appearance")]
+ [Description("The Font to use when displaying the PromptText.")]
+ public Font BannerFont
+ {
+ get { return _bannerFont; }
+ set { _bannerFont = value; this.Invalidate(); }
+ }
+
+ [Browsable(true)]
+ [EditorBrowsable(EditorBrowsableState.Always)]
+ [Category("Behavior")]
+ [Description("Automatically select the text when control receives the focus.")]
+ public bool FocusSelect
+ {
+ get { return _focusSelect; }
+ set { _focusSelect = value; }
+ }
+
+ ///
+ /// When the textbox receives an OnEnter event, select all the text if any text is present
+ ///
+ ///
+ protected override void OnEnter(EventArgs e)
+ {
+ if (this.Text.Length > 0 && _focusSelect)
+ this.SelectAll();
+
+ base.OnEnter(e);
+ }
+
+ ///
+ /// Redraw the control when the text alignment changes
+ ///
+ ///
+ protected override void OnTextAlignChanged(EventArgs e)
+ {
+ base.OnTextAlignChanged(e);
+ this.Invalidate();
+ }
+
+ ///
+ /// Redraw the control with the prompt
+ ///
+ ///
+ /// This event will only fire if ControlStyles.UserPaint is set to true in the constructor
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ base.OnPaint(e);
+
+ // Only draw the prompt in the OnPaint event and when the Text property is empty
+ if (_drawPrompt && this.Text.Length == 0)
+ DrawTextPrompt(e.Graphics);
+ }
+
+ ///
+ /// Overrides the default WndProc for the control
+ ///
+ /// The Windows message structure
+ ///
+ /// This technique is necessary because the OnPaint event seems to be doing some
+ /// extra processing that I haven't been able to figure out.
+ ///
+ protected override void WndProc(ref System.Windows.Forms.Message m)
+ {
+ switch (m.Msg)
+ {
+ case WM_SETFOCUS:
+ _drawPrompt = false;
+ break;
+
+ case WM_KILLFOCUS:
+ _drawPrompt = true;
+ break;
+ }
+
+ base.WndProc(ref m);
+
+ // Only draw the prompt on the WM_PAINT event and when the Text property is empty
+ if (m.Msg == WM_PAINT && _drawPrompt && this.Text.Length == 0 && !this.GetStyle(ControlStyles.UserPaint))
+ DrawTextPrompt();
+ }
+
+ ///
+ /// Overload to automatically create the Graphics region before drawing the text prompt
+ ///
+ /// The Graphics region is disposed after drawing the prompt.
+ protected virtual void DrawTextPrompt()
+ {
+ using (Graphics g = this.CreateGraphics())
+ {
+ DrawTextPrompt(g);
+ }
+ }
+
+ ///
+ /// Draws the PromptText in the TextBox.ClientRectangle using the PromptFont and PromptForeColor
+ ///
+ /// The Graphics region to draw the prompt on
+ protected virtual void DrawTextPrompt(Graphics g)
+ {
+ TextFormatFlags flags = TextFormatFlags.NoPadding | TextFormatFlags.Top | TextFormatFlags.EndEllipsis;
+ Rectangle rect = this.ClientRectangle;
+
+ // Offset the rectangle based on the HorizontalAlignment,
+ // otherwise the display looks a little strange
+ switch (this.TextAlign)
+ {
+ case HorizontalAlignment.Center:
+ flags = flags | TextFormatFlags.HorizontalCenter;
+ rect.Offset(0, 1);
+ break;
+ case HorizontalAlignment.Left:
+ flags = flags | TextFormatFlags.Left;
+ rect.Offset(1, 1);
+ break;
+ case HorizontalAlignment.Right:
+ flags = flags | TextFormatFlags.Right;
+ rect.Offset(0, 1);
+ break;
+ }
+
+ // Draw the prompt text using TextRenderer
+ TextRenderer.DrawText(g, _bannerText, _bannerFont, rect, _bannerColor, this.BackColor, flags);
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/CueBannerTextBox_old.cs b/Hyphen/Plugins/Forms/Controls/CueBannerTextBox_old.cs
new file mode 100644
index 0000000..3533582
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/CueBannerTextBox_old.cs
@@ -0,0 +1,125 @@
+//--------------------------------------------------------------------------
+//
+// Copyright (c) Chili Software. All rights reserved.
+//
+// File: CueBannerTextBox.cs
+//
+// Description: Text box that allows to display a cue banner.
+//
+//--------------------------------------------------------------------------
+
+// Idea from: http://www.delphipraxis.net/topic13132_editgetcuebannertext+win+xp.html
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using System.Runtime.InteropServices;
+using System.Drawing;
+using System.Diagnostics;
+using System.ComponentModel;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ ///
+ /// This class represents a text box that is enhanced to display a little cue banner, if
+ /// no text has been entered. This could be used to inform the user what should be entered
+ /// in the text box.
+ ///
+ public class CueBannerTextBox : TextBox
+ {
+ #region NativeMethods
+
+ private const uint ECM_FIRST = 0x1500;
+ private const uint EM_SETCUEBANNER = ECM_FIRST + 1;
+ private const uint WM_SETFONT = 0x30;
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, string lParam);
+
+ #endregion
+
+ private string _bannerText;
+ private Font _bannerFont;
+
+ ///
+ /// The banner text associated with the control.
+ ///
+ [Description("The banner text associated with the control.")]
+ [Category("Appearance")]
+ public string BannerText
+ {
+ get { return _bannerText; }
+ set
+ {
+ _bannerText = value;
+
+ // If supported set the value as banner text.
+ if (IsSupported)
+ SendMessage(this.Handle, EM_SETCUEBANNER, IntPtr.Zero, value);
+ }
+ }
+
+ ///
+ /// The banner font used to display the banner text in the control.
+ ///
+ [Description("The banner font used to display the banner text in the control.")]
+ [Category("Appearance")]
+ public Font BannerFont
+ {
+ get
+ {
+ if (_bannerFont == null && this.Parent != null)
+ return this.Parent.Font;
+
+ return _bannerFont;
+ }
+ set { _bannerFont = value; }
+ }
+
+ #region Overridden Members
+
+ ///
+ /// Invoked each time the focus is lost.
+ ///
+ ///
+ protected override void OnLostFocus(EventArgs e)
+ {
+ base.OnLostFocus(e);
+
+ // Notify the text box to change the font to the banner font.
+ if (this.Text.Length == 0 && IsSupported)
+ {
+ SendMessage(this.Handle, WM_SETFONT, _bannerFont.ToHfont(), null);
+ }
+ }
+
+ ///
+ /// Invoked each time the focus is gotten.
+ ///
+ ///
+ protected override void OnGotFocus(EventArgs e)
+ {
+ base.OnGotFocus(e);
+
+ // Notify the text box to change the font back.
+ if (IsSupported)
+ SendMessage(this.Handle, WM_SETFONT, base.Font.ToHfont(), null);
+ }
+
+ #endregion
+
+ ///
+ /// Returns whether the OS supports banner texts. It is fine if the application
+ /// runs on XP or higher.
+ ///
+ private bool IsSupported
+ {
+ get
+ {
+ Version v = Environment.OSVersion.Version;
+ return ((v.Major == 5 && v.Minor == 1) || v.Major > 5);
+ }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/GradientPanel.cs b/Hyphen/Plugins/Forms/Controls/GradientPanel.cs
new file mode 100644
index 0000000..1c7dae1
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/GradientPanel.cs
@@ -0,0 +1,56 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ [ToolboxBitmap(typeof(Panel))]
+ public sealed class GradientPanel : Panel
+ {
+ public GradientPanel() { }
+
+ private Color gradientColor;
+ public Color GradientColor
+ {
+ get { return gradientColor; }
+ set { gradientColor = value; Refresh(); }
+ }
+
+ private float rotation;
+ public float Rotation
+ {
+ get { return rotation; }
+ set { rotation = value; Refresh(); }
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ if (e.ClipRectangle.IsEmpty) return;
+
+ using (LinearGradientBrush brush = new LinearGradientBrush(ClientRectangle, BackColor, GradientColor, Rotation))
+ e.Graphics.FillRectangle(brush, ClientRectangle);
+
+ base.OnPaint(e);
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/ManagedMainMenu.cs b/Hyphen/Plugins/Forms/Controls/ManagedMainMenu.cs
new file mode 100644
index 0000000..e340034
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/ManagedMainMenu.cs
@@ -0,0 +1,242 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Drawing;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Miranda.Plugins.Forms.Controls;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ // Impl note: must be public, remoting rejects non-public method calls.
+ public sealed class ManagedMainMenu : ContextMenuStrip
+ {
+ #region Delegates
+
+ private delegate void AddMainMenuItemInvoker(ManagedMainMenu menu, ManagedMainMenuItem item);
+ private delegate void ModifyMenuItemInvoker(ManagedMainMenu menu, ref CLISTMENUITEM itemData, string handle);
+
+ #endregion
+
+ #region Fields
+
+ private static readonly Random HandleGenerator = new Random();
+
+ private static readonly AddMainMenuItemInvoker AddMainMenuItemDelegate = new AddMainMenuItemInvoker(AddMainMenuItem);
+ private static readonly ModifyMenuItemInvoker ModifyMenuItemDelegate = new ModifyMenuItemInvoker(ModifyMenuItem);
+
+ private static readonly ToolStripMenuItem EmptyItem;
+ private readonly int EmptyItemIndex;
+
+ #endregion
+
+ #region .ctors & .dctors
+
+ static ManagedMainMenu()
+ {
+ EmptyItem = new ToolStripMenuItem(TextResources.UI_Label_Empty);
+ EmptyItem.Visible = false;
+ }
+
+ internal ManagedMainMenu()
+ {
+ RenderMode = ToolStripRenderMode.System;
+ EmptyItemIndex = Items.Add(EmptyItem);
+ }
+
+ #endregion
+
+ #region Methods
+
+ #region Overrides
+
+ public override object InitializeLifetimeService()
+ {
+ return null;
+ }
+
+ #endregion
+
+ #region UI
+
+ internal void ShowUnderCursor()
+ {
+ if (Items.Count == 1)
+ Items[EmptyItemIndex].Visible = true;
+ else
+ Items[EmptyItemIndex].Visible = false;
+
+ Show(Cursor.Position);
+ }
+
+ #endregion
+
+ #region Interceptors
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This method is static to not let the execution run in default AppDomain but in a domain of the menu itself.
+ ///
+ internal static void RegisterInterceptors(ManagedMainMenu menu)
+ {
+ MirandaContext context = MirandaContext.Current;
+
+ context.ServiceCallInterceptors.Register(MirandaServices.MS_CLIST_ADDMAINMENUITEM, menu.AddMainMenuItemServiceInterceptor);
+ context.ServiceCallInterceptors.Register(MirandaServices.MS_CLIST_MODIFYMENUITEM, menu.ModifyMenuItemInterceptor);
+ }
+
+ internal static void UnregisterInterceptors(ManagedMainMenu menu)
+ {
+ MirandaContext context = MirandaContext.Current;
+
+ context.ServiceCallInterceptors.Unregister(MirandaServices.MS_CLIST_ADDMAINMENUITEM);
+ context.ServiceCallInterceptors.Unregister(MirandaServices.MS_CLIST_MODIFYMENUITEM);
+ }
+
+ private int AddMainMenuItemServiceInterceptor(UIntPtr wParam, IntPtr lParam)
+ {
+ CLISTMENUITEM itemData = (CLISTMENUITEM)Marshal.PtrToStructure(lParam, typeof(CLISTMENUITEM));
+ ManagedMainMenuItem menuItem = null;
+ Image itemImage = null;
+
+ if (itemData.Icon != IntPtr.Zero)
+ itemImage = IconImageCache.Singleton.GetIconImage(itemData.Icon);
+
+ menuItem = new ManagedMainMenuItem(itemData.Text, itemData.PopUpMenu, itemData.Service, itemImage);
+
+ if (InvokeRequired)
+ Invoke(AddMainMenuItemDelegate, this, menuItem);
+ else
+ AddMainMenuItemDelegate(this, menuItem);
+
+ return menuItem.Handle;
+ }
+
+ private static void AddMainMenuItem(ManagedMainMenu menu, ManagedMainMenuItem item)
+ {
+ if (!String.IsNullOrEmpty(item.PopUpMenu))
+ {
+ string popupName = item.PopUpMenu;
+ ToolStripMenuItem popupItem = null;
+
+ ToolStripItem[] popupItems = menu.Items.Find(popupName, false);
+
+ if (popupItems.Length > 0)
+ popupItem = (ToolStripMenuItem)popupItems[0];
+ else
+ {
+ popupItem = new ToolStripMenuItem(popupName);
+ popupItem.Name = popupName;
+
+ menu.Items.Add(popupItem);
+ }
+
+ popupItem.DropDownItems.Add(item);
+ }
+ else
+ menu.Items.Add(item);
+ }
+
+ private int ModifyMenuItemInterceptor(UIntPtr wParam, IntPtr lParam)
+ {
+ try
+ {
+ CLISTMENUITEM itemData = (CLISTMENUITEM)Marshal.PtrToStructure(lParam, typeof(CLISTMENUITEM));
+ string handle = wParam.ToString();
+
+ if (!Items.ContainsKey(handle))
+ {
+ if (String.IsNullOrEmpty(itemData.PopUpMenu) ||
+ !Items.ContainsKey(itemData.PopUpMenu) ||
+ !((ToolStripMenuItem)Items[itemData.PopUpMenu]).DropDownItems.ContainsKey(handle))
+ return MirandaContext.Current.CallService(MirandaServices.MS_CLIST_MODIFYMENUITEM, wParam, lParam, true);
+ }
+
+ if (InvokeRequired)
+ Invoke(ModifyMenuItemDelegate, this, itemData, handle);
+ else
+ ModifyMenuItemDelegate(this, ref itemData, handle);
+
+ return 0;
+ }
+ catch
+ {
+ return -1;
+ }
+ }
+
+ private static void ModifyMenuItem(ManagedMainMenu menu, ref CLISTMENUITEM itemData, string handle)
+ {
+ ManagedMainMenuItem item = (ManagedMainMenuItem)menu.Items.Find(handle, itemData.PopUpMenu != null)[0];
+ MenuItemModifyFlags flags = (MenuItemModifyFlags)itemData.Flags;
+
+ if ((flags & MenuItemModifyFlags.CMIM_NAME) == MenuItemModifyFlags.CMIM_NAME)
+ item.Text = itemData.Text;
+
+ if ((flags & MenuItemModifyFlags.CMIM_ICON) == MenuItemModifyFlags.CMIM_ICON)
+ item.Image = IconImageCache.Singleton.GetIconImage(itemData.Icon);
+
+ if ((flags & MenuItemModifyFlags.CMIM_HOTKEY) == MenuItemModifyFlags.CMIM_HOTKEY)
+ item.ShortcutKeys = (Keys)itemData.HotKey;
+
+ if ((flags & MenuItemModifyFlags.CMIM_FLAGS) == MenuItemModifyFlags.CMIM_FLAGS)
+ {
+ MenuItemProperties itemFlags = (MenuItemProperties)(flags & ~MenuItemModifyFlags.CMIM_ALL);
+
+ switch (itemFlags)
+ {
+ case MenuItemProperties.Grayed:
+ item.Enabled = false;
+ break;
+ case MenuItemProperties.Hidden:
+ if (item.OwnerItem == null)
+ menu.Items.Remove(item);
+ else
+ {
+ ToolStripMenuItem popupItem = (ToolStripMenuItem)item.OwnerItem;
+ popupItem.DropDownItems.Remove(item);
+
+ if (popupItem.DropDownItems.Count == 0)
+ menu.Items.Remove(popupItem);
+ }
+ break;
+ case MenuItemProperties.Checked:
+ item.Checked = true;
+ break;
+ case MenuItemProperties.None:
+ item.Enabled = true;
+ item.Visible = true;
+ item.Checked = false;
+ break;
+ }
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/ManagedMainMenuItem.cs b/Hyphen/Plugins/Forms/Controls/ManagedMainMenuItem.cs
new file mode 100644
index 0000000..99c9ea1
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/ManagedMainMenuItem.cs
@@ -0,0 +1,85 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Drawing;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ internal class ManagedMainMenuItem : ToolStripMenuItem
+ {
+ #region Fields
+
+ private static readonly Random HandleGenerator = new Random();
+
+ private int handle;
+ private string service, popUpMenu;
+
+ #endregion
+
+ #region .ctors
+
+ public ManagedMainMenuItem(string text, string popUpMenu, string service, Image image)
+ {
+ if (String.IsNullOrEmpty(text)) throw new ArgumentNullException("text");
+ if (String.IsNullOrEmpty(service)) throw new ArgumentNullException("service");
+
+ this.Name = (handle = HandleGenerator.Next()).ToString();
+
+ this.Text = text;
+ this.popUpMenu = popUpMenu;
+ this.service = service;
+ this.Image = image;
+
+ this.Click += new EventHandler(ManagedMainMenuItem_Click);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public string PopUpMenu
+ {
+ get { return popUpMenu; }
+ }
+
+ public string Service
+ {
+ get { return service; }
+ }
+
+ public int Handle
+ {
+ get { return handle; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ private void ManagedMainMenuItem_Click(object sender, EventArgs e)
+ {
+ MirandaContext.Current.CallService(service);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/TrayContextMenu.cs b/Hyphen/Plugins/Forms/Controls/TrayContextMenu.cs
new file mode 100644
index 0000000..326bf06
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/TrayContextMenu.cs
@@ -0,0 +1,84 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Configuration.Forms;
+using Virtuoso.Miranda.Plugins.Configuration;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Hyphen;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ internal sealed class TrayContextMenu : ContextMenuStrip
+ {
+ #region Fields
+
+ private ToolStripMenuItem ManagePluginsITEM;
+
+ #endregion
+
+ #region .ctors
+
+ public TrayContextMenu()
+ {
+ InitializeComponent();
+ }
+
+ #endregion
+
+ #region Designer
+
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TrayContextMenu));
+ this.ManagePluginsITEM = new System.Windows.Forms.ToolStripMenuItem();
+ this.SuspendLayout();
+ //
+ // ManagePluginsITEM
+ //
+ this.ManagePluginsITEM.Image = ((System.Drawing.Image)(resources.GetObject("ManagePluginsITEM.Image")));
+ this.ManagePluginsITEM.Name = "ManagePluginsITEM";
+ this.ManagePluginsITEM.Size = new System.Drawing.Size(111, 22);
+ this.ManagePluginsITEM.Text = "Options";
+ this.ManagePluginsITEM.Click += new System.EventHandler(this.ManagePluginsITEM_Click);
+ //
+ // TrayContextMenu
+ //
+ this.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.ManagePluginsITEM});
+ this.RenderMode = System.Windows.Forms.ToolStripRenderMode.System;
+ this.Size = new System.Drawing.Size(112, 26);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ #region UI Handlers
+
+ private void ManagePluginsITEM_Click(object sender, EventArgs e)
+ {
+ Loader.GetInstance().ManagePlugins();
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/Controls/TrayContextMenu.resx b/Hyphen/Plugins/Forms/Controls/TrayContextMenu.resx
new file mode 100644
index 0000000..2959f3c
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/TrayContextMenu.resx
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAA8AAAAQCAYAAADJViUEAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAA
+ CxMBAJqcGAAAAtdJREFUOE99U0tPE1EUblgQV64MCQv3rtwZE2NMiIkbQzTEldENFmpbWvqkpVT6pC2l
+ 7ZS+3/Td6UyH0k6nBUt5yQKNAv4Gg0YXxMQYg6VzvDbBCGm4m3tz73e+853zncvhXLI0YXpTG6Y/X4b5
+ 96aJ1oZm4/VDTYR5OB2mB3RR5rsxXoepYOWm0r8imQ5UjiRu8lZPMm2UeWJINMAQr3f0MaZtiDOAdtBF
+ a6wuQrO6KA1SD/X4XLAmUrutjTF3Z6LMiKuw+TtVfwvL2x+B3DwEfH0f8q8/QKD8BuZSa6zcU1aIsdI9
+ kYu4w1GFqld18fqxId4ADN8AYuMACs334CG3wbjU6BgTjVNnoQXB5V0IULtgRGrUvjIoPcttjjJQ6Z+J
+ 1BJIIpus70EeBdqyzc50uBZWh+nr6hA9qA5WKX2UaWN4C2ypNVB5y6zIVUx05auC1QFDjGFLW4ewiDKi
+ hkkuNkXpL+uNsRrYs01QeMgOkt3HUYeq5KsI/UmP5BRbB2BIrP3o1U25j7ox5S93TAgnw4ow6SoecZCX
+ X3XoAtmCGrMPuhhz3CtY5qEGFW6irQtXQOIqgtiRhy5O4VsZRtkhvfoO7Lkmq/CVRy8SIH8lKi8JMz4K
+ xPY8K1jAh7oYibs0rAlWOt7SDvipHdCGKqeTbvL+GQE6j8gw4pfaS4Ecw0FozZwK7PkHHKGj2C/3Ukea
+ UBVMyC4fuQVYvgVoKDpyT+mbbJH8MuUmWZWHBIULB5EtAzxzEsbNqZ9d8gkX8VLgILgiZ3F9FtVkTTaQ
+ JatgRtM1G1wBjbcEUlSjcD4D43OpvXFL+jnXnOaeK03gKDxVuEus1EWwUicOk84Cqi8LAmsG+HNJ4JmW
+ YMySHus52xMOXCvFiBO+vWARLOSfTdjzJwJLGoT2nJhny/pe6OMdrjl1PuP/TCjwEX8+d+XvnWA+t8G3
+ ZbpfkmfNXuOakrxR01LfGf4P/J24pw50qlAAAAAASUVORK5CYII=
+
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/Controls/TrayMenuManager.cs b/Hyphen/Plugins/Forms/Controls/TrayMenuManager.cs
new file mode 100644
index 0000000..07637ee
--- /dev/null
+++ b/Hyphen/Plugins/Forms/Controls/TrayMenuManager.cs
@@ -0,0 +1,61 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.ComponentModel;
+using System.Drawing;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Forms.Controls
+{
+ internal sealed class TrayMenuManager : IDisposable
+ {
+ #region Fields
+
+ private readonly NotifyIcon TrayIcon;
+
+ #endregion
+
+ #region .ctors
+
+ public TrayMenuManager()
+ {
+ TrayIcon = new NotifyIcon();
+
+ TrayIcon.Text = TextResources.UI_ToolTip_HyphenTrayIcon;
+ TrayIcon.Visible = true;
+ TrayIcon.Icon = VisualResources.Icon_16x16_Hyphen;
+ TrayIcon.ContextMenuStrip = new TrayContextMenu();
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ if (TrayIcon != null)
+ TrayIcon.Dispose();
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/ErrorDialog.Designer.cs b/Hyphen/Plugins/Forms/ErrorDialog.Designer.cs
new file mode 100644
index 0000000..418164b
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ErrorDialog.Designer.cs
@@ -0,0 +1,152 @@
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ internal partial class ErrorDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ErrorDialog));
+ this.MessageLABEL = new System.Windows.Forms.Label();
+ this.DetailsTBOX = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.OkBTN = new System.Windows.Forms.Button();
+ this.label3 = new System.Windows.Forms.Label();
+ this.Panel1 = new Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemHeader();
+ this.CancelBTN = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.SendReportLBTN = new System.Windows.Forms.LinkLabel();
+ this.Panel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // MessageLABEL
+ //
+ resources.ApplyResources(this.MessageLABEL, "MessageLABEL");
+ this.MessageLABEL.Name = "MessageLABEL";
+ //
+ // DetailsTBOX
+ //
+ this.DetailsTBOX.BackColor = System.Drawing.SystemColors.Window;
+ resources.ApplyResources(this.DetailsTBOX, "DetailsTBOX");
+ this.DetailsTBOX.Name = "DetailsTBOX";
+ this.DetailsTBOX.ReadOnly = true;
+ //
+ // label2
+ //
+ resources.ApplyResources(this.label2, "label2");
+ this.label2.Name = "label2";
+ //
+ // OkBTN
+ //
+ resources.ApplyResources(this.OkBTN, "OkBTN");
+ this.OkBTN.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.OkBTN.Name = "OkBTN";
+ this.OkBTN.UseVisualStyleBackColor = true;
+ //
+ // label3
+ //
+ resources.ApplyResources(this.label3, "label3");
+ this.label3.BackColor = System.Drawing.Color.Transparent;
+ this.label3.Name = "label3";
+ //
+ // Panel1
+ //
+ this.Panel1.BackColor = System.Drawing.Color.Transparent;
+ this.Panel1.Color = System.Drawing.SystemColors.ActiveCaption;
+ this.Panel1.Controls.Add(this.label3);
+ resources.ApplyResources(this.Panel1, "Panel1");
+ this.Panel1.HeaderFont = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.Panel1.Image = ((System.Drawing.Image)(resources.GetObject("Panel1.Image")));
+ this.Panel1.MinimumSize = new System.Drawing.Size(300, 40);
+ this.Panel1.Name = "Panel1";
+ //
+ // CancelBTN
+ //
+ this.CancelBTN.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ resources.ApplyResources(this.CancelBTN, "CancelBTN");
+ this.CancelBTN.Name = "CancelBTN";
+ this.CancelBTN.UseVisualStyleBackColor = true;
+ //
+ // label1
+ //
+ resources.ApplyResources(this.label1, "label1");
+ this.label1.Name = "label1";
+ //
+ // label4
+ //
+ resources.ApplyResources(this.label4, "label4");
+ this.label4.Name = "label4";
+ //
+ // SendReportLBTN
+ //
+ resources.ApplyResources(this.SendReportLBTN, "SendReportLBTN");
+ this.SendReportLBTN.Name = "SendReportLBTN";
+ this.SendReportLBTN.TabStop = true;
+ this.SendReportLBTN.UseCompatibleTextRendering = true;
+ this.SendReportLBTN.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.SendReportLBTN_LinkClicked);
+ //
+ // ErrorDialog
+ //
+ this.AcceptButton = this.OkBTN;
+ resources.ApplyResources(this, "$this");
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.CancelButton = this.CancelBTN;
+ this.Controls.Add(this.SendReportLBTN);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.CancelBTN);
+ this.Controls.Add(this.MessageLABEL);
+ this.Controls.Add(this.DetailsTBOX);
+ this.Controls.Add(this.OkBTN);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.Panel1);
+ this.Controls.Add(this.label2);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "ErrorDialog";
+ this.Shown += new System.EventHandler(this.PluginErrorDialog_Shown);
+ this.Panel1.ResumeLayout(false);
+ this.Panel1.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label MessageLABEL;
+ private System.Windows.Forms.TextBox DetailsTBOX;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button OkBTN;
+ private System.Windows.Forms.Label label3;
+ private Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemHeader Panel1;
+ private System.Windows.Forms.Button CancelBTN;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.LinkLabel SendReportLBTN;
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/ErrorDialog.cs b/Hyphen/Plugins/Forms/ErrorDialog.cs
new file mode 100644
index 0000000..01e087f
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ErrorDialog.cs
@@ -0,0 +1,133 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Diagnostics;
+using System.Media;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ internal sealed partial class ErrorDialog : Form
+ {
+ #region Fields
+
+ private IExceptionReporter Reporter;
+ private Exception Exception;
+
+ #endregion
+
+ #region .ctors
+
+ private ErrorDialog()
+ {
+ InitializeComponent();
+ }
+
+ public static DialogResult PresentModal(Exception e)
+ {
+ return PresentModal(e, null, null, false);
+ }
+
+ public static DialogResult PresentModal(Exception e, IExceptionReporter reporter)
+ {
+ return PresentModal(e, reporter, null, false);
+ }
+
+ public static DialogResult PresentModal(Exception e, string message, bool canCancel)
+ {
+ return PresentModal(e, null, message, canCancel);
+ }
+
+ public static DialogResult PresentModal(Exception e, IExceptionReporter reporter, string message, bool canCancel)
+ {
+ using (ErrorDialog dlg = new ErrorDialog())
+ {
+ return dlg.BindAndShow(e, reporter, message, canCancel);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ private DialogResult BindAndShow(Exception exception, IExceptionReporter reporter, string message, bool canCancel)
+ {
+ if (exception == null)
+ throw new ArgumentNullException("exception");
+
+ this.Exception = exception;
+ this.MessageLABEL.Text = message ?? exception.Message;
+
+ PrepareReportLink(exception, reporter);
+ DumpException(exception);
+
+ CancelBTN.Visible = canCancel;
+ OkBTN.Focus();
+
+ return ShowDialog();
+ }
+
+ private void PrepareReportLink(Exception exception, IExceptionReporter reporter)
+ {
+ if (reporter == null)
+ {
+ SendReportLBTN.Enabled = false;
+ }
+ else
+ {
+ this.Reporter = reporter;
+ }
+ }
+
+ private void DumpException(Exception e)
+ {
+ StringBuilder dump = new StringBuilder();
+
+ if (e is IExceptionDumpController)
+ {
+ ((IExceptionDumpController)e).DumpException(e, dump);
+ }
+
+ dump.AppendFormat("=== Exception dump ==={0}{1}{0}{0}", Environment.NewLine, e.ToString());
+ DetailsTBOX.Text = dump.ToString();
+ }
+
+ #endregion
+
+ #region UI Handlers
+
+ private void PluginErrorDialog_Shown(object sender, EventArgs e)
+ {
+ SystemSounds.Hand.Play();
+ }
+
+ private void SendReportLBTN_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ Reporter.ReportException(Exception);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/ErrorDialog.resx b/Hyphen/Plugins/Forms/ErrorDialog.resx
new file mode 100644
index 0000000..c281922
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ErrorDialog.resx
@@ -0,0 +1,538 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ True
+
+
+
+ 9, 56
+
+
+ 401, 56
+
+
+
+ 0
+
+
+ (message)
+
+
+ MessageLABEL
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 3
+
+
+ True
+
+
+ 12, 186
+
+
+ True
+
+
+
+ Both
+
+
+ 398, 171
+
+
+ 1
+
+
+ DetailsTBOX
+
+
+ System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 4
+
+
+ True
+
+
+ True
+
+
+ Tahoma, 8pt, style=Bold
+
+
+ 9, 170
+
+
+ 46, 13
+
+
+ 2
+
+
+ Details
+
+
+ label2
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 8
+
+
+ True
+
+
+ True
+
+
+ 12, 363
+
+
+ 75, 23
+
+
+ 3
+
+
+ OK
+
+
+ OkBTN
+
+
+ System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 5
+
+
+ True
+
+
+ True
+
+
+ Franklin Gothic Medium, 14pt
+
+
+ NoControl
+
+
+ 12, 12
+
+
+ 273, 24
+
+
+ 5
+
+
+ Failed to load a managed plugin
+
+
+ label3
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Panel1
+
+
+ 1
+
+
+ True
+
+
+ Top
+
+
+ Tahoma, 8.25pt
+
+
+ An error occured in Hyphen or one of its plugins.
+
+
+
+ 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
+ TkSuQmCC
+
+
+
+ 0, 0
+
+
+ 422, 53
+
+
+ 4
+
+
+ Panel1
+
+
+ Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemHeader, Hyphen, Version=0.8.6.1931, Culture=neutral, PublicKeyToken=null
+
+
+ $this
+
+
+ 7
+
+
+ True
+
+
+ 93, 363
+
+
+ 75, 23
+
+
+ 5
+
+
+ Cancel
+
+
+ False
+
+
+ CancelBTN
+
+
+ System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 2
+
+
+ True
+
+
+ True
+
+
+ Tahoma, 8pt, style=Bold
+
+
+ NoControl
+
+
+ 9, 124
+
+
+ 118, 13
+
+
+ 2
+
+
+ Report the problem
+
+
+ label1
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 6
+
+
+ True
+
+
+ True
+
+
+ 9, 137
+
+
+ 169, 13
+
+
+ 6
+
+
+ To send this report to the author,
+
+
+ label4
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 1
+
+
+ True
+
+
+ True
+
+
+ 0, 10
+
+
+ 177, 137
+
+
+ 53, 18
+
+
+ 7
+
+
+ click here.
+
+
+ SendReportLBTN
+
+
+ System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 0
+
+
+ True
+
+
+ True
+
+
+ 6, 13
+
+
+ 422, 397
+
+
+ Tahoma, 8pt
+
+
+
+ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAABweLRgcHi40HB4vHBweL8AcHi/AHB4vHBweLjQcHi0YAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAHB4sgBweLjDAwqf5oaMj/lpbd/6qq5v+goOP/kJDb/2hoyP8wMKn+BweLjAcH
+ iyAAAAAAAAAAAAAAAAAHB4sgCQmf81ZWwf+Wl+H/dnbY/0NDyv8eHr//Hh6//0NDyv92dtj/l5fh/1ZW
+ wf8KCpzzBweLIAAAAAAAAAAABweLjFVWw/+UlOP/TU2s/xYWkv8WFpL/Dw+8/w8Pu/8PD7v/FhaS/01N
+ rP+YmM//VlbB/wcHi4wAAAAABweLRi8vrP6VleT/RkbS///////29v3/FhaS/w4Ov/8ODr7/FhaS////
+ ////////TU2s/5eX4f8wMKn+BweLRgcHi41nZ83/cnLg/wwMyP/29v3//////9DQ6f9CQqf/Nzeh/7Ky
+ 2//9/f7//////xYWkv92ddn/aGjI/wcHi40HB4vHi4vi/zs72f8KC87/CgvM/9LS9f//////vr7h/6am
+ 1v//////zs7o/xYWkv8WFpL/QUHN/5OU3v8HB4vHBweL8JeY7P8WFtf/CQnT/x0d1f83N9n/qKjW////
+ ////////wMDi/1BQrf8bG8r/DQ3F/xscyP+pqen/BweL8AcHi/ClpfD/FRXb/wcI2P8ICNb/Nzeh/7Cw
+ 2v///////////8vL5/9ERKf/CwvM/wsMyf8bG8v/sbHs/wcHi/AHB4vHkpHp/zs75v8GBt3/FhaS/9nZ
+ 7f//////urrf/6en1v//////29vu/xYWkv8KCs//QD/Y/5WV4/8HB4vHBweLjWRl1f9wcO//BQXi//r6
+ /v//////3t7w/0BApv8wMOD/xsb2///////+/v//FhaS/3Jy5v9mZ8//BweLjQcHi0YtLbL+kpLy/z8/
+ 7P//////+/v//xYWkv8ICOH/BQXg/wYG3v///////////01NrP+Tk+z/Li6u/gcHi0YAAAAABweLjFFR
+ 0/+Pj/X/Pz7t/wME6P8EBOf/BATm/wQF5P8EBOP/BQbh/0FA5/+RkfH/UlLO/wcHi4wAAAAAAAAAAAcH
+ iyAHB6rzUVHT/5GR9P9vb/P/ODju/xIT6v8SEuj/OTns/29v8P+SkvL/UVLQ/wcHpvMHB4sgAAAAAAAA
+ AAAAAAAABweLIAcHi4wtLbL+ZGTY/5OT7f+oqPb/n5/1/46O7P9kZNf/LS2y/gcHi4wHB4sgAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAABweLRgcHi40HB4vHBweL8AcHi/AHB4vHBweLjQcHi0YAAAAAAAAAAAAA
+ AAAAAAAA8A8AAMADAACAAQAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAIAB
+ AADAAwAA8A8AAA==
+
+
+
+ CenterScreen
+
+
+ Plugin error
+
+
+ ErrorDialog
+
+
+ System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/FusionProgressDialog.Designer.cs b/Hyphen/Plugins/Forms/FusionProgressDialog.Designer.cs
new file mode 100644
index 0000000..eaa6e16
--- /dev/null
+++ b/Hyphen/Plugins/Forms/FusionProgressDialog.Designer.cs
@@ -0,0 +1,82 @@
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ partial class FusionProgressDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FusionProgressDialog));
+ this.FusionWorker = new System.ComponentModel.BackgroundWorker();
+ this.BackgroundPBOX = new System.Windows.Forms.PictureBox();
+ ((System.ComponentModel.ISupportInitialize)(this.BackgroundPBOX)).BeginInit();
+ this.SuspendLayout();
+ //
+ // FusionWorker
+ //
+ this.FusionWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.FusionWorker_DoWork);
+ this.FusionWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.FusionWorker_RunWorkerCompleted);
+ //
+ // BackgroundPBOX
+ //
+ this.BackgroundPBOX.Image = ((System.Drawing.Image)(resources.GetObject("BackgroundPBOX.Image")));
+ this.BackgroundPBOX.Location = new System.Drawing.Point(0, 0);
+ this.BackgroundPBOX.Name = "BackgroundPBOX";
+ this.BackgroundPBOX.Size = new System.Drawing.Size(200, 100);
+ this.BackgroundPBOX.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
+ this.BackgroundPBOX.TabIndex = 2;
+ this.BackgroundPBOX.TabStop = false;
+ //
+ // FusionProgressDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.LightSteelBlue;
+ this.ClientSize = new System.Drawing.Size(200, 100);
+ this.ControlBox = false;
+ this.Controls.Add(this.BackgroundPBOX);
+ this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
+ this.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "FusionProgressDialog";
+ this.Opacity = 0.75;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Loading plugins";
+ this.TransparencyKey = System.Drawing.Color.LightSteelBlue;
+ ((System.ComponentModel.ISupportInitialize)(this.BackgroundPBOX)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.ComponentModel.BackgroundWorker FusionWorker;
+ private System.Windows.Forms.PictureBox BackgroundPBOX;
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/FusionProgressDialog.cs b/Hyphen/Plugins/Forms/FusionProgressDialog.cs
new file mode 100644
index 0000000..538606b
--- /dev/null
+++ b/Hyphen/Plugins/Forms/FusionProgressDialog.cs
@@ -0,0 +1,89 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using System.Threading;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ internal sealed partial class FusionProgressDialog : Form
+ {
+ #region Delegates
+
+ public delegate void WorkerDelegate();
+
+ #endregion
+
+ #region Fields
+
+ private WorkerDelegate Worker;
+
+ #endregion
+
+ #region .ctors
+
+ public FusionProgressDialog(WorkerDelegate del)
+ {
+ InitializeComponent();
+
+ Worker = del;
+ Shown += FusionProgressDialog_Shown;
+ }
+
+ public static void ShowDialog(WorkerDelegate del)
+ {
+ if (del == null)
+ throw new ArgumentNullException("del");
+
+ PluginDialog.ExecuteOnSTAThread(delegate(object delegateObj)
+ {
+ using (FusionProgressDialog dlg = new FusionProgressDialog((WorkerDelegate)delegateObj))
+ dlg.ShowDialog();
+ }, del);
+ }
+
+ #endregion
+
+ #region UI Handlers
+
+ private void FusionProgressDialog_Shown(object sender, EventArgs e)
+ {
+ FusionWorker.RunWorkerAsync();
+ }
+
+ private void FusionWorker_DoWork(object sender, DoWorkEventArgs e)
+ {
+ if (Worker != null)
+ Worker();
+ }
+
+ private void FusionWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
+ {
+ Close();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/FusionProgressDialog.resx b/Hyphen/Plugins/Forms/FusionProgressDialog.resx
new file mode 100644
index 0000000..cdac14d
--- /dev/null
+++ b/Hyphen/Plugins/Forms/FusionProgressDialog.resx
@@ -0,0 +1,611 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ True
+
+
+
+
+ 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
+
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/ISynchronizedSubscriber.cs b/Hyphen/Plugins/Forms/ISynchronizedSubscriber.cs
new file mode 100644
index 0000000..ea40237
--- /dev/null
+++ b/Hyphen/Plugins/Forms/ISynchronizedSubscriber.cs
@@ -0,0 +1,29 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ public interface ISynchronizedSubscriber
+ {
+ SynchronizationContext SyncContext { get; }
+ }
+}
diff --git a/Hyphen/Plugins/Forms/InformationDialog.Designer.cs b/Hyphen/Plugins/Forms/InformationDialog.Designer.cs
new file mode 100644
index 0000000..7fab966
--- /dev/null
+++ b/Hyphen/Plugins/Forms/InformationDialog.Designer.cs
@@ -0,0 +1,136 @@
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ partial class InformationDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InformationDialog));
+ this.InformationLABEL = new System.Windows.Forms.Label();
+ this.OkBTN = new System.Windows.Forms.Button();
+ this.DialogHeader = new Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemHeader();
+ this.CaptionLABEL = new System.Windows.Forms.Label();
+ this.BackgroundPBOX = new System.Windows.Forms.PictureBox();
+ this.DialogHeader.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.BackgroundPBOX)).BeginInit();
+ this.SuspendLayout();
+ //
+ // InformationLABEL
+ //
+ this.InformationLABEL.BackColor = System.Drawing.Color.Transparent;
+ this.InformationLABEL.ForeColor = System.Drawing.SystemColors.WindowText;
+ this.InformationLABEL.Location = new System.Drawing.Point(9, 56);
+ this.InformationLABEL.Name = "InformationLABEL";
+ this.InformationLABEL.Size = new System.Drawing.Size(430, 155);
+ this.InformationLABEL.TabIndex = 2;
+ this.InformationLABEL.Text = "Information";
+ //
+ // OkBTN
+ //
+ this.OkBTN.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.OkBTN.Location = new System.Drawing.Point(12, 220);
+ this.OkBTN.Name = "OkBTN";
+ this.OkBTN.Size = new System.Drawing.Size(75, 23);
+ this.OkBTN.TabIndex = 0;
+ this.OkBTN.Text = "OK";
+ this.OkBTN.UseVisualStyleBackColor = true;
+ //
+ // DialogHeader
+ //
+ this.DialogHeader.BackColor = System.Drawing.Color.Transparent;
+ this.DialogHeader.Color = System.Drawing.SystemColors.ActiveCaption;
+ this.DialogHeader.Controls.Add(this.CaptionLABEL);
+ this.DialogHeader.Dock = System.Windows.Forms.DockStyle.Top;
+ this.DialogHeader.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.DialogHeader.HeaderFont = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.DialogHeader.HeaderText = "Caption";
+ this.DialogHeader.Image = global::Virtuoso.Miranda.Plugins.Properties.Resources.Icon_232_32x32;
+ this.DialogHeader.Location = new System.Drawing.Point(0, 0);
+ this.DialogHeader.MinimumSize = new System.Drawing.Size(300, 40);
+ this.DialogHeader.Name = "DialogHeader";
+ this.DialogHeader.Size = new System.Drawing.Size(451, 53);
+ this.DialogHeader.TabIndex = 1;
+ //
+ // CaptionLABEL
+ //
+ this.CaptionLABEL.AutoSize = true;
+ this.CaptionLABEL.BackColor = System.Drawing.Color.Transparent;
+ this.CaptionLABEL.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.CaptionLABEL.Location = new System.Drawing.Point(12, 23);
+ this.CaptionLABEL.Name = "CaptionLABEL";
+ this.CaptionLABEL.Size = new System.Drawing.Size(59, 13);
+ this.CaptionLABEL.TabIndex = 0;
+ this.CaptionLABEL.Text = "(caption)";
+ //
+ // BackgroundPBOX
+ //
+ this.BackgroundPBOX.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.BackgroundPBOX.Image = ((System.Drawing.Image)(resources.GetObject("BackgroundPBOX.Image")));
+ this.BackgroundPBOX.Location = new System.Drawing.Point(330, 127);
+ this.BackgroundPBOX.Name = "BackgroundPBOX";
+ this.BackgroundPBOX.Size = new System.Drawing.Size(120, 129);
+ this.BackgroundPBOX.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
+ this.BackgroundPBOX.TabIndex = 3;
+ this.BackgroundPBOX.TabStop = false;
+ this.BackgroundPBOX.Visible = false;
+ //
+ // InformationDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(451, 255);
+ this.Controls.Add(this.BackgroundPBOX);
+ this.Controls.Add(this.OkBTN);
+ this.Controls.Add(this.InformationLABEL);
+ this.Controls.Add(this.DialogHeader);
+ this.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "InformationDialog";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Information";
+ this.TopMost = true;
+ this.Shown += new System.EventHandler(this.InformationDialog_Shown);
+ this.DialogHeader.ResumeLayout(false);
+ this.DialogHeader.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.BackgroundPBOX)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemHeader DialogHeader;
+ private System.Windows.Forms.Label InformationLABEL;
+ private System.Windows.Forms.Label CaptionLABEL;
+ private System.Windows.Forms.Button OkBTN;
+ private System.Windows.Forms.PictureBox BackgroundPBOX;
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/InformationDialog.cs b/Hyphen/Plugins/Forms/InformationDialog.cs
new file mode 100644
index 0000000..98ad3df
--- /dev/null
+++ b/Hyphen/Plugins/Forms/InformationDialog.cs
@@ -0,0 +1,63 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using System.Media;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ public partial class InformationDialog : Form
+ {
+ public const string NewLineToken = "[n]";
+
+ private InformationDialog()
+ {
+ InitializeComponent();
+ }
+
+ public static void PresentModal(string caption, string information, Image icon)
+ {
+ if (String.IsNullOrEmpty(caption))
+ throw new ArgumentNullException("caption");
+
+ if (String.IsNullOrEmpty(information))
+ throw new ArgumentNullException("information");
+
+ using (InformationDialog dlg = new InformationDialog())
+ {
+ dlg.DialogHeader.HeaderText = dlg.Text = caption;
+ dlg.InformationLABEL.Text = information.Replace(NewLineToken, Environment.NewLine);
+
+ if (icon != null)
+ dlg.DialogHeader.Image = icon;
+
+ dlg.ShowDialog();
+ }
+ }
+
+ private void InformationDialog_Shown(object sender, EventArgs e)
+ {
+ SystemSounds.Asterisk.Play();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/InformationDialog.resx b/Hyphen/Plugins/Forms/InformationDialog.resx
new file mode 100644
index 0000000..7b86164
--- /dev/null
+++ b/Hyphen/Plugins/Forms/InformationDialog.resx
@@ -0,0 +1,537 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+
+
+ 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
+
+
+
+ True
+
+
+
+ AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAB
+ AAAAAAAAenl6AJQxEgCUMRQAmTIUAKg/GgCsPxoAsz8cALo/HwCqQBsArkQeALBAGwC2QBwAsEQeALpA
+ HwCWRiwArkwqALBHIQC/QCAAskkhALBLKgC/WDEA3l0tAMJcNADGYDkAymI6AM9lOgDKYzwAzWU8ALxo
+ TgCkZlEAsGhUALptUQC9b1MAv29UALR1XwC4dmQAzmlBAMpsRwDVbUIA1G9HANRwRwDRcU0A3XVKANp2
+ TgDDaVAAx21XAMhtVwDKfV8A5XJHAO1zRQDhfFMA5X1RAOh/VwC6g3AAvYNyAO+DWQDNhmkA1ItuANeN
+ bwDBjn0AyYx4AOaKZADui2UA8JFrAPSTbQD0lG4A6pp2APeceACLiYkAkJCQAJuXlwCloaAApKSkALGw
+ sQC+ubcAzI+AAMmRgQDMmocAzJuJAN2cggDWppMA16qbANesmwDhoIcAzqyjAMuwpgDEsq4Az7SqANuw
+ oADFtrEAyLi0ANO5sADdu7EA3by0ANDJyADR0tQA19jaAPXk3QD/+fIA+vv7AAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAD///8AAAAAAABKRgEBRkoAAAAAAAAAAEpFSEtfX0tIRUoAAAAAAEhHV00tDw8eTFdHSAAAAEpHViIM
+ PV1dHwQvVUdKAABFVyEMC1JkZCQEEi1XRQBKSE8TCwtSYGQkAxISTUhKRkswFRMLUmFkNwMMBx1LRgFf
+ JhsXC1JhZDwDDAkUXwEBXyomJRtSYWQ3AwsLFF8BRks7JSslU2JkIwULCh9LRkpIUysrLDUyMRYbGBdP
+ SEoARVtQNTVAYmM/GiY5WkUAAEpHXFQ1RGRkQiU7WEdKAAAASEdbWUNAQD9RW0dIAAAAAABKRUhLX19L
+ SEVKAAAAAAAAAABKRgEBRkoAAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAgAEAAIABAADAAwAA4AcAAPgfAAA=
+
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Forms/LocalizableDialog.cs b/Hyphen/Plugins/Forms/LocalizableDialog.cs
new file mode 100644
index 0000000..4529ec9
--- /dev/null
+++ b/Hyphen/Plugins/Forms/LocalizableDialog.cs
@@ -0,0 +1,103 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.ComponentModel;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ [Flags]
+ public enum FormTranslationFlags : int
+ {
+ None = 0,
+ TranslateNonReadOnlyEditControls = 1, //translate all edit controls. By default non-read-only edit controls are not translated
+ NoTitleTranslation = 2 //do not translate the title of the dialog
+ }
+
+ public class LocalizableDialog : SingletonDialog
+ {
+ #region Fields
+
+ private const string MS_LANGPACK_TRANSLATEDIALOG = "LangPack/TranslateDialog";
+
+ private FormTranslationFlags translateFlags;
+ private readonly Collections.ControlCollection nonLocalizableControls;
+
+ [Browsable(false)]
+ public Collections.ControlCollection NonLocalizableControls
+ {
+ get { return nonLocalizableControls; }
+ }
+
+ public FormTranslationFlags TranslateFlags
+ {
+ get { return translateFlags; }
+ set { translateFlags = value; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ protected LocalizableDialog() : this(null, FormTranslationFlags.None) { }
+
+ protected LocalizableDialog(FormTranslationFlags flags) : this(null, flags) { }
+
+ protected LocalizableDialog(string dialogName, FormTranslationFlags flags) : base(dialogName)
+ {
+ translateFlags = flags;
+ nonLocalizableControls = new Collections.ControlCollection();
+ }
+
+ #endregion
+
+ #region UI handlers
+
+ protected override void OnLoad(EventArgs e)
+ {
+ if (!DesignMode)
+ {
+ if ((translateFlags & FormTranslationFlags.NoTitleTranslation) != FormTranslationFlags.NoTitleTranslation)
+ Text = LanguagePack.TranslateString(Text);
+
+ foreach (Control control in Controls)
+ {
+ TextBoxBase editCtrl = control as TextBoxBase;
+
+ if (!nonLocalizableControls.Contains(control))
+ {
+ if (editCtrl != null && !editCtrl.ReadOnly && (translateFlags & FormTranslationFlags.TranslateNonReadOnlyEditControls) != FormTranslationFlags.TranslateNonReadOnlyEditControls)
+ continue;
+
+ control.Text = LanguagePack.TranslateString(control.Text);
+ }
+ }
+ }
+
+ base.OnLoad(e);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/PluginDialog.cs b/Hyphen/Plugins/Forms/PluginDialog.cs
new file mode 100644
index 0000000..a48e1ab
--- /dev/null
+++ b/Hyphen/Plugins/Forms/PluginDialog.cs
@@ -0,0 +1,171 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Threading;
+using System.ComponentModel;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ public class PluginDialog : RemotableForm
+ {
+ #region Fields
+
+ private static readonly List ActiveDialogs = new List();
+
+ #endregion
+
+ #region .ctors
+
+ protected PluginDialog() { }
+
+ #endregion
+
+ #region Overrides
+
+ protected override void OnShown(EventArgs e)
+ {
+ RegisterDialog();
+ base.OnShown(e);
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ UnregisterDialog();
+ base.OnClosed(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ UnregisterDialog();
+ base.Dispose(disposing);
+ }
+
+ #endregion
+
+ #region Properties
+
+ protected MirandaContext Context
+ {
+ get { return MirandaContext.Current; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal virtual void RegisterDialog()
+ {
+ lock (ActiveDialogs)
+ ActiveDialogs.Add(this);
+ }
+
+ ///
+ /// Remove the dialog from the active dialog tracking list.
+ ///
+ internal virtual void UnregisterDialog()
+ {
+ lock (ActiveDialogs)
+ ActiveDialogs.Remove(this);
+ }
+
+ public static void CloseDialogs(PluginDescriptor owner, bool force)
+ {
+ foreach (PluginDialog dialog in UnregisterAndGetActiveDialogs(owner))
+ {
+ try
+ {
+ if (dialog.InvokeRequired)
+ dialog.Invoke(new MethodInvoker(delegate { dialog.Dispose(); }));
+ else
+ dialog.Dispose();
+ }
+ catch { if (!force) throw; }
+ }
+ }
+
+ ///
+ /// Gathers active dialogs of the plugin and unregisters them.
+ ///
+ /// Plugin.
+ /// Unregistered dialogs to dispose.
+ private static List UnregisterAndGetActiveDialogs(PluginDescriptor plugin)
+ {
+ Assembly pluginAssembly = plugin.Plugin.GetType().Assembly;
+ List dialogsToRemove = new List(2);
+
+ lock (ActiveDialogs)
+ {
+ foreach (PluginDialog dialog in ActiveDialogs)
+ {
+ // Account only undisposed dialogs from plugin's assembly
+ if (dialog.IsDisposed || dialog.GetType().Assembly != pluginAssembly)
+ continue;
+
+ dialogsToRemove.Add(dialog);
+ }
+
+ foreach (PluginDialog dialog in dialogsToRemove)
+ dialog.UnregisterDialog();
+ }
+
+ return dialogsToRemove;
+ }
+
+ public static void ExecuteOnSTAThread(ParameterizedThreadStart threadStart)
+ {
+ ExecuteOnSTAThread(threadStart, null);
+ }
+
+ public static void ExecuteOnSTAThread(ParameterizedThreadStart threadStart, object state)
+ {
+ if (threadStart == null)
+ throw new ArgumentNullException("threadStart");
+
+ Thread thread = new Thread(delegate(object _state)
+ {
+ try
+ {
+ Application.ThreadException += Application_ThreadException;
+ threadStart(_state);
+ }
+ finally
+ {
+ Application.ThreadException -= Application_ThreadException;
+ }
+ });
+
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.IsBackground = true;
+
+ thread.Start(state);
+ }
+
+ private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
+ {
+ DefaultExceptionHandler.Create().HandleException(e.Exception, null);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/RemotableForm.cs b/Hyphen/Plugins/Forms/RemotableForm.cs
new file mode 100644
index 0000000..1935354
--- /dev/null
+++ b/Hyphen/Plugins/Forms/RemotableForm.cs
@@ -0,0 +1,42 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ public class RemotableForm : Form
+ {
+ #region .ctors
+
+ internal RemotableForm() { }
+
+ #endregion
+
+ #region Overrides
+
+ public override object InitializeLifetimeService()
+ {
+ return null;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Forms/SingletonDialog.cs b/Hyphen/Plugins/Forms/SingletonDialog.cs
new file mode 100644
index 0000000..e1fe92b
--- /dev/null
+++ b/Hyphen/Plugins/Forms/SingletonDialog.cs
@@ -0,0 +1,134 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Forms
+{
+ public class SingletonDialog : PluginDialog
+ {
+ #region Fields
+
+ private static readonly Dictionary visibleDialogs = new Dictionary(1);
+ protected static Dictionary VisibleDialogs
+ {
+ get { return visibleDialogs; }
+ }
+
+ private string singletonName;
+ protected string SingletonName
+ {
+ get { return singletonName; }
+ private set { singletonName = value; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ protected SingletonDialog() : this(null) { }
+
+ protected SingletonDialog(string name)
+ {
+ this.singletonName = String.IsNullOrEmpty(name) ? GetDefaultName(GetType()) : name;
+ }
+
+ #endregion
+
+ #region Overrides
+
+ internal override void RegisterDialog()
+ {
+ lock (visibleDialogs)
+ {
+ if (!visibleDialogs.ContainsKey(SingletonName))
+ visibleDialogs[SingletonName] = this;
+ }
+
+ base.RegisterDialog();
+ }
+
+ internal override void UnregisterDialog()
+ {
+ lock (visibleDialogs)
+ visibleDialogs.Remove(SingletonName);
+
+ base.UnregisterDialog();
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static TForm GetSingleton(bool createIfNeeded) where TForm : PluginDialog
+ {
+ return GetSingleton(createIfNeeded, typeof(TForm).FullName);
+ }
+
+ public static TForm GetSingleton(bool createIfNeeded, string name) where TForm : PluginDialog
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ lock (visibleDialogs)
+ {
+ if (!visibleDialogs.ContainsKey(name) || visibleDialogs[name].IsDisposed)
+ {
+ if (createIfNeeded)
+ return (TForm)Activator.CreateInstance(typeof(TForm), true);
+ else
+ return null;
+ }
+ else
+ return visibleDialogs[name] as TForm;
+ }
+ }
+
+ private delegate void ShowSingletonInvoker(bool modal);
+
+ public void ShowSingleton(bool modal)
+ {
+ if (InvokeRequired)
+ Invoke(new ShowSingletonInvoker(DoShowSingleton), modal);
+ else
+ DoShowSingleton(modal);
+ }
+
+ private void DoShowSingleton(bool modal)
+ {
+ if (Visible)
+ Activate();
+ else if (modal)
+ ShowDialog();
+ else
+ Show();
+ }
+
+ public static string GetDefaultName(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ return type.FullName;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/FriendlyStrongName/Desktop.ini b/Hyphen/Plugins/FriendlyStrongName/Desktop.ini
new file mode 100644
index 0000000..e36c1c0
--- /dev/null
+++ b/Hyphen/Plugins/FriendlyStrongName/Desktop.ini
@@ -0,0 +1,3 @@
+[.ShellClassInfo]
+IconFile=%SystemRoot%\system32\SHELL32.dll
+IconIndex=19
diff --git a/Hyphen/Plugins/FriendlyStrongName/FriendlyStrongName.snk b/Hyphen/Plugins/FriendlyStrongName/FriendlyStrongName.snk
new file mode 100644
index 0000000..8d3bd51
Binary files /dev/null and b/Hyphen/Plugins/FriendlyStrongName/FriendlyStrongName.snk differ
diff --git a/Hyphen/Plugins/FriendlyStrongName/PublicKey.publickey b/Hyphen/Plugins/FriendlyStrongName/PublicKey.publickey
new file mode 100644
index 0000000..5eac4be
Binary files /dev/null and b/Hyphen/Plugins/FriendlyStrongName/PublicKey.publickey differ
diff --git a/Hyphen/Plugins/FusionException.cs b/Hyphen/Plugins/FusionException.cs
new file mode 100644
index 0000000..af8a74c
--- /dev/null
+++ b/Hyphen/Plugins/FusionException.cs
@@ -0,0 +1,96 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Runtime.Serialization;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [Serializable]
+ public class FusionException : Exception, IExceptionDumpController
+ {
+ #region Fields
+
+ private readonly Assembly assembly;
+ private readonly Type pluginType;
+ private readonly MirandaPlugin instantiatedPlugin;
+ private readonly string fusionLog;
+
+ #endregion
+
+ #region .ctors
+
+ public FusionException(string message, Assembly assembly, Type type, MirandaPlugin plugin, Exception inner)
+ : this(message, TextResources.UI_Label_Empty, assembly, type, plugin, inner)
+ { }
+
+ public FusionException(string message, string fusionLog, Assembly assembly, Type type, MirandaPlugin plugin, Exception inner) : base(message, inner)
+ {
+ this.assembly = assembly;
+ this.pluginType = type;
+ this.instantiatedPlugin = plugin;
+ this.fusionLog = fusionLog;
+ }
+
+ protected FusionException(SerializationInfo info, StreamingContext context) : base(info, context) {}
+
+ #endregion
+
+ #region Properties
+
+ public Assembly Assembly
+ {
+ get { return assembly; }
+ }
+
+ public Type PluginType
+ {
+ get { return pluginType; }
+ }
+
+ public MirandaPlugin InstantiatedPlugin
+ {
+ get { return instantiatedPlugin; }
+ }
+
+ public string FusionLog
+ {
+ get { return fusionLog; }
+ }
+
+ #endregion
+
+ #region IExceptionDumpController Members
+
+ void IExceptionDumpController.DumpException(Exception e, StringBuilder dump)
+ {
+ FusionException ex = (FusionException)e;
+
+ dump.AppendFormat("=== Description ==={0}{1}{0}{0}", Environment.NewLine, ex.Message);
+ dump.AppendFormat("=== Assembly ==={0}{1}{0}{0}", Environment.NewLine, ex.Assembly == null ? TextResources.UI_Label_Unknown : ex.Assembly.ToString());
+ dump.AppendFormat("=== Type ==={0}{1}{0}{0}", Environment.NewLine, ex.PluginType == null ? TextResources.UI_Label_Unknown : ex.PluginType.FullName);
+ dump.AppendFormat("=== Fusion log ==={0}{1}{0}{0}", Environment.NewLine, ex.FusionLog);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Graphics/LoadingBanner.psd b/Hyphen/Plugins/Graphics/LoadingBanner.psd
new file mode 100644
index 0000000..5784e9c
Binary files /dev/null and b/Hyphen/Plugins/Graphics/LoadingBanner.psd differ
diff --git a/Hyphen/Plugins/Graphics/Thumbs.db b/Hyphen/Plugins/Graphics/Thumbs.db
new file mode 100644
index 0000000..251a93a
Binary files /dev/null and b/Hyphen/Plugins/Graphics/Thumbs.db differ
diff --git a/Hyphen/Plugins/Helpers/EnumValueFriendlyNameAttribute.cs b/Hyphen/Plugins/Helpers/EnumValueFriendlyNameAttribute.cs
new file mode 100644
index 0000000..5c44c23
--- /dev/null
+++ b/Hyphen/Plugins/Helpers/EnumValueFriendlyNameAttribute.cs
@@ -0,0 +1,84 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+using System.Collections.Specialized;
+
+namespace Virtuoso.Miranda.Plugins.Helpers
+{
+ [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
+ public sealed class EnumValueFriendlyNameAttribute : Attribute
+ {
+ #region Fields
+
+ private string friendlyName;
+
+ #endregion
+
+ #region .ctors
+
+ public EnumValueFriendlyNameAttribute(string name)
+ {
+ this.friendlyName = name;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public string FriendlyName
+ {
+ get { return friendlyName; }
+ set { friendlyName = value; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static Dictionary GetFriendlyNames() where TEnum : struct
+ {
+ Type enumType = typeof(TEnum);
+
+ if (!enumType.IsEnum)
+ throw new ArgumentException("TEnum is not an enumeration.", "TEnum");
+
+ Type thisType = typeof(EnumValueFriendlyNameAttribute);
+ Dictionary results = new Dictionary(1);
+
+ foreach (FieldInfo field in enumType.GetFields())
+ {
+ if ((field.Attributes & FieldAttributes.Literal) != FieldAttributes.Literal)
+ continue;
+
+ EnumValueFriendlyNameAttribute[] names = (EnumValueFriendlyNameAttribute[])field.GetCustomAttributes(thisType, false);
+
+ if (names != null && names.Length > 0)
+ results.Add((TEnum)field.GetRawConstantValue(), names[0].FriendlyName);
+ else
+ results.Add((TEnum)field.GetRawConstantValue(), field.Name);
+ }
+
+ return results;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Helpers/IniStructure.cs b/Hyphen/Plugins/Helpers/IniStructure.cs
new file mode 100644
index 0000000..0be472c
--- /dev/null
+++ b/Hyphen/Plugins/Helpers/IniStructure.cs
@@ -0,0 +1,652 @@
+/* /--==###################==--\
+ * | Bram's Ini File Handler |
+ * \--==###################==--/
+ *
+ * This handles Ini files and all their content.
+ *
+ * Some explanation:
+ * Categories are in fact sections, but i didn't think
+ * "sections" so i wrote "categories". Sorry.
+ *
+ * comment lines in ini files begin with #, ; or //
+ * multi-line are not supported (Because I've never seen such)
+ *
+ * It ignores comments on reading but can write them
+ *
+ * How it works:
+ * All data is saved in one System.Collections.SortedList which
+ * contains the category names as keys, and all key-value pairs
+ * as values, saved as SortedList too:
+ *
+ * explanation sheet
+ *
+ * SortedList Categories
+ * {
+ * {"Category1", {Key1, value1}
+ * {Key2, value2}
+ * ...
+ * }
+ * {"Category2", {Key1, value1}
+ * {Key2, value2}
+ * ...
+ * }
+ * ...
+ * }
+ *
+ * that behaves like an array in an array (array[][]), but with dynamic bounds
+ * and strings as indexers.
+ *
+ * I hope you did understand this, it would have been easier to explain
+ * in French or German...
+ *
+ * You can make with it what you want, but I would be pleased to
+ * hear some feedback. (Ok, I admit: I would be pleased only if it's
+ * positive feedback...)
+ *
+ * Send me an email! kratchkov@inbox.lv
+ *
+ * Thanks
+ *
+ * DISCLAIMER:
+ * THIS CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THIS
+ * CODE IS WITH YOU. SHOULD THIS CODE PROVE DEFECTIVE, YOU ASSUME
+ * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION .
+ * You take full responsibility for the use of this code and any
+ * consequences thereof. I can not accept liability for damages
+ * or failures arising from the use of this code, or parts of this code.
+ */
+
+using System;
+using System.IO;
+using System.Text;
+using System.Collections;
+
+namespace Virtuoso.Miranda.Plugins.Helpers
+{
+ ///
+ /// Handles Ini categories, keys and their associated values, static methods implemented for file
+ /// handling (saving and reading)
+ ///
+ public class IniStructure
+ {
+ #region Ini structure code
+ private SortedList Categories = new SortedList();
+
+ ///
+ /// Initialies a new IniStructure
+ ///
+ public IniStructure()
+ {
+ }
+
+ ///
+ /// Adds a category to the IniStructure
+ ///
+ /// Name of the new category
+ public bool AddCategory(string Name)
+ {
+ if (Name == "" | Categories.ContainsKey(Name))
+ return false;
+ if (Name.IndexOf('=') != -1
+ | Name.IndexOf('[') != -1
+ | Name.IndexOf(']') != -1) // these characters are not allowed in a category name
+ return false;
+
+ Categories.Add(Name, new SortedList());
+ return true;
+ }
+
+ ///
+ /// Deletes a category and its contents
+ ///
+ /// category to delete
+ public bool DeleteCategory(string Name)
+ {
+ if (Name == "" | !Categories.ContainsKey(Name))
+ return false;
+ Categories.Remove(Name);
+ return true;
+ }
+
+ ///
+ /// Renames a category
+ ///
+ /// Category to rename
+ /// New name
+ public bool RenameCategory(string Name, string NewName)
+ { // Or rather moves a category to a new name
+ if (Name == "" | !Categories.ContainsKey(Name) | NewName == "")
+ return false;
+
+ if (NewName.IndexOf('=') != -1
+ | NewName.IndexOf('[') != -1
+ | NewName.IndexOf(']') != -1) // these characters are not allowed in a category name
+ return false;
+
+ SortedList Category = (SortedList)(Categories[Name]);
+ Categories.Add(NewName, Category);
+ this.DeleteCategory(Name);
+ return true;
+ }
+
+ ///
+ /// Returns the names of all categories
+ ///
+ ///
+ public string[] GetCategories()
+ {
+ string[] CatNames = new string[Categories.Count];
+ IList KeyList = Categories.GetKeyList();
+ int KeyCount = Categories.Count;
+ for (int i = 0; i < KeyCount; i++)
+ {
+ CatNames[i] = KeyList[i].ToString();
+ }
+ return CatNames;
+ }
+
+ ///
+ /// Returns the name of a category by specifying the index.
+ /// Useful to enumerate through all categories.
+ ///
+ /// The category index
+ ///
+ public string GetCategoryName(int Index)
+ {
+ if (Index < 0 | Index >= Categories.Count)
+ return null;
+ return Categories.GetKey(Index).ToString();
+ }
+
+ ///
+ /// Adds a key-value pair to a specified category
+ ///
+ /// Name of the category
+ /// New name of the key
+ /// Associated value
+ public bool AddValue(string CategoryName, string Key, string Value)
+ {
+ if (CategoryName == "" | Key == "")
+ return false;
+ if (Key.IndexOf('=') != -1
+ | Key.IndexOf('[') != -1
+ | Key.IndexOf(']') != -1 // these chars are not allowed for keynames
+ | Key.IndexOf(';') != -1
+ | Key.IndexOf('#') != -1
+ )
+ return false;
+ if (!Categories.ContainsKey(CategoryName))
+ return false;
+ SortedList Category = (SortedList)(Categories[CategoryName]);
+ if (Category.ContainsKey(Key))
+ return false;
+ Category.Add(Key, Value);
+ return true;
+ }
+
+ ///
+ /// Returns the value of a key-value pair in a specified category by specifying the key
+ ///
+ /// Name of the category
+ /// Name of the Key
+ ///
+ public string GetValue(string CategoryName, string Key)
+ {
+ if (CategoryName == "" | Key == "")
+ return null;
+ if (!Categories.ContainsKey(CategoryName))
+ return null;
+ SortedList Category = (SortedList)(Categories[CategoryName]);
+ if (!Category.ContainsKey(Key))
+ return null;
+ return Category[Key].ToString();
+ }
+
+ ///
+ /// Returns the key-value pair in a specified category by specifying the index
+ ///
+ /// Index of the category
+ /// Index of the Key
+ ///
+ public string GetValue(int CatIndex, int KeyIndex)
+ {
+ if (CatIndex < 0 | KeyIndex < 0
+ |CatIndex >= Categories.Count)
+ return null;
+ SortedList Category = (SortedList)(Categories.GetByIndex(CatIndex));
+ if (KeyIndex >= Category.Count)
+ return null;
+ return Category.GetByIndex(KeyIndex).ToString();
+ }
+
+ ///
+ /// Returns the name of the key in a key-value pair in a specified category by specifying the index
+ ///
+ /// Index of the category
+ /// Index of the key
+ ///
+ public string GetKeyName(int CatIndex, int KeyIndex)
+ {
+ if (CatIndex < 0 | KeyIndex < 0
+ |CatIndex >= Categories.Count)
+ return null;
+ SortedList Category = (SortedList)(Categories.GetByIndex(CatIndex));
+ if (KeyIndex >= Category.Count)
+ return null;
+ return Category.GetKey(KeyIndex).ToString();
+ }
+
+
+ ///
+ /// Deletes a key-value pair
+ ///
+ /// Name of the category
+ /// Name of the Key
+ public bool DeleteValue(string CategoryName, string Key)
+ {
+ if (CategoryName == "" | Key == "")
+ return false;
+ if (!Categories.ContainsKey(CategoryName))
+ return false;
+ SortedList Category = (SortedList)(Categories[CategoryName]);
+ if (!Category.ContainsKey(Key))
+ return false;
+ Category.Remove(Key);
+ return true;
+ }
+
+ ///
+ /// Renames the keyname in a key-value pair
+ ///
+ /// Name of the category
+ /// Name of the Key
+ /// New name of the Key
+ public bool RenameKey(string CategoryName, string KeyName, string NewKeyName)
+ {
+ if (CategoryName == "" | KeyName == "" | NewKeyName == "")
+ return false;
+ if (!Categories.ContainsKey(CategoryName))
+ return false;
+ if (NewKeyName.IndexOf('=') != -1
+ | NewKeyName.IndexOf('[') != -1
+ | NewKeyName.IndexOf(']') != -1 // these chars are not allowed for keynames
+ | NewKeyName.IndexOf(';') != -1
+ | NewKeyName.IndexOf('#') != -1
+ )
+ return false;
+ SortedList Category = (SortedList)(Categories[CategoryName]);
+ if ( !Category.ContainsKey(KeyName))
+ return false;
+
+ object value = Category[KeyName];
+ Category.Remove(KeyName);
+ Category.Add(NewKeyName, value);
+ return true;
+ }
+
+ ///
+ /// Modifies the value in a key-value pair
+ ///
+ /// Name of the category
+ /// Name of the Key
+ /// New name of the Key
+ public bool ModifyValue(string CategoryName, string KeyName, string NewValue)
+ {
+ if (CategoryName == "" | KeyName == "")
+ return false;
+ if (!Categories.ContainsKey(CategoryName))
+ return false;
+ SortedList Category = (SortedList)(Categories[CategoryName]);
+ if ( !Category.ContainsKey(KeyName))
+ return false;
+
+ Category[KeyName] = NewValue;
+ return true;
+ }
+
+ ///
+ /// Returns all keys in a category
+ ///
+ /// Name of the category
+ ///
+ public string[] GetKeys(string CategoryName)
+ {
+ SortedList Category = (SortedList)(Categories[CategoryName]);
+ if (Category == null)
+ return new string[0];
+ int KeyCount = Category.Count;
+ string[] KeyNames = new string[KeyCount];
+ IList KeyList = Category.GetKeyList();
+ for (int i = 0; i < KeyCount; i++)
+ {
+ KeyNames[i] = KeyList[i].ToString();
+ }
+ return KeyNames;
+ }
+
+ #endregion
+
+ #region Ini writing code
+ ///
+ /// Writes an IniStructure to a file with a comment.
+ ///
+ /// The contents to write
+ /// The complete path and name of the file
+ /// Comment to add
+ ///
+ public static bool WriteIni(IniStructure IniData, string Filename, string comment)
+ {
+ string DataToWrite = CreateData(IniData, BuildComment(comment));
+ return WriteFile(Filename, DataToWrite);
+ }
+
+ ///
+ /// Writes an IniStructure to a file without a comment.
+ ///
+ /// The contents to write
+ /// The complete path and name of the file
+ ///
+ public static bool WriteIni(IniStructure IniData, string Filename)
+ {
+ string DataToWrite = CreateData(IniData);
+ return WriteFile(Filename, DataToWrite);
+ }
+
+ private static bool WriteFile(string Filename, string Data)
+ { // Writes a string to a file
+ try
+ {
+ FileStream IniStream = new FileStream(Filename,FileMode.Create);
+ if (!IniStream.CanWrite)
+ {
+ IniStream.Close();
+ return false;
+ }
+ StreamWriter writer = new StreamWriter(IniStream);
+ writer.Write(Data);
+ writer.Flush();
+ writer.Close();
+ IniStream.Close();
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static string BuildComment(string comment)
+ { // Adds a # at the beginning of each line
+ if (comment == "")
+ return "";
+ string[] Lines = DivideToLines(comment);
+ string temp = "";
+ foreach (string line in Lines)
+ {
+ temp += "# " + line + "\r\n";
+ }
+ return temp;
+ }
+
+ private static string CreateData(IniStructure IniData)
+ {
+ return CreateData(IniData,"");
+ }
+
+ private static string CreateData(IniStructure IniData, string comment)
+ { //Iterates through all categories and keys and appends all data to Data
+ int CategoryCount = IniData.GetCategories().Length;
+ int[] KeyCountPerCategory = new int[CategoryCount];
+ string Data = comment;
+ string[] temp = new string[2]; // will contain key-value pair
+
+ for (int i = 0; i < CategoryCount; i++) // Gets keycount per category
+ {
+ string CategoryName = IniData.GetCategories()[i];
+ KeyCountPerCategory[i] = IniData.GetKeys(CategoryName).Length;
+ }
+
+ for (int catcounter = 0; catcounter < CategoryCount; catcounter++)
+ {
+ Data += "\r\n[" + IniData.GetCategoryName(catcounter) + "]\r\n";
+ // writes [Category] to Data
+ for (int keycounter = 0; keycounter < KeyCountPerCategory[catcounter]; keycounter++)
+ {
+ temp[0] = IniData.GetKeyName(catcounter, keycounter);
+ temp[1] = IniData.GetValue(catcounter, keycounter);
+ Data += temp[0] + "=" + temp[1] + "\r\n";
+ // writes the key-value pair to Data
+ }
+ }
+ return Data;
+ }
+ #endregion
+
+ #region Ini reading code
+
+ ///
+ /// Reads an ini file and returns the content as an IniStructure. Returns null if an error occurred.
+ ///
+ /// The filename to read
+ ///
+ public static IniStructure ReadIni(string Filename)
+ {
+ string Data = ReadFile(Filename);
+ if (Data == null)
+ return null;
+
+ IniStructure data = InterpretIni(Data);
+
+ return data;
+ }
+
+ public static IniStructure InterpretIni(string Data)
+ {
+ IniStructure IniData = new IniStructure();
+ string[] Lines = RemoveAndVerifyIni(DivideToLines(Data));
+ // Divides the Data in lines, removes comments and empty lines
+ // and verifies if the ini is not corrupted
+ // Returns null if it is.
+ if (Lines == null)
+ return null;
+
+ if (IsLineACategoryDef(Lines[0]) != LineType.Category)
+ {
+ return null;
+ // Ini is faulty - does not begin with a categorydef
+ }
+ string CurrentCategory = "";
+ foreach (string line in Lines)
+ {
+ switch (IsLineACategoryDef(line))
+ {
+ case LineType.Category: // the line is a correct category definition
+ string NewCat = line.Substring(1,line.Length - 2);
+ IniData.AddCategory(NewCat); // adds the category to the IniData
+ CurrentCategory = NewCat;
+ break;
+ case LineType.NotACategory: // the line is not a category definition
+ string[] keyvalue = GetDataFromLine(line);
+ IniData.AddValue(CurrentCategory, keyvalue[0], keyvalue[1]);
+ // Adds the key-value to the current category
+ break;
+ case LineType.Faulty: // the line is faulty
+ return null;
+ }
+ }
+ return IniData;
+ }
+
+ private static string ReadFile(string filename)
+ { // Reads a file to a string.
+ if (!File.Exists(filename))
+ return null;
+ StringBuilder IniData;
+ try
+ {
+ using (FileStream IniStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
+ {
+ if (!IniStream.CanRead)
+ return null;
+
+ using (StreamReader reader = new StreamReader(IniStream))
+ {
+ IniData = new StringBuilder();
+ IniData.Append(reader.ReadToEnd());
+ return IniData.ToString();
+ }
+ }
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static string[] GetDataFromLine(string Line)
+ {
+ // returns the key and the value of a key-value pair in "key=value" format.
+ int EqualPos = 0;
+ EqualPos = Line.IndexOf("=", 0);
+ if (EqualPos < 1)
+ {
+ return null;
+ }
+ string LeftKey = Line.Substring(0, EqualPos);
+ string RightValue = Line.Substring(EqualPos + 1);
+
+ string[] ToReturn = {LeftKey, RightValue};
+ return ToReturn;
+ }
+
+ private enum LineType // return type for IsLineACategoryDef and LineVerify
+ {
+ NotACategory,
+ Category,
+ Faulty,
+ Comment,
+ Empty,
+ Ok
+ }
+
+ private static LineType IsLineACategoryDef(string Line)
+ {
+ if (Line.Length < 3)
+ return LineType.NotACategory; // must be a short keyname like "k="
+
+ if (Line.Substring(0,1) == "[" & Line.Substring(Line.Length - 1, 1) == "]")
+ // seems to be a categorydef
+ {
+ if (Line.IndexOf("=") != -1)
+ // '=' found -> is incorrect for category def
+ return LineType.Faulty;
+ if (ContainsMoreThanOne(Line,'[') | ContainsMoreThanOne(Line, ']'))
+ // two or more '[' or ']' found -> incorrect
+ return LineType.Faulty;
+ return LineType.Category;
+ }
+ return LineType.NotACategory;
+ }
+
+ private static string[] DivideToLines(string Data)
+ { // Divides a string into lines
+ string[] Lines = new string[Data.Length];
+ int oldnewlinepos = 0;
+ int LineCounter = 0;
+ for (int i = 0; i < Data.Length; i++)
+ {
+ if (Data.ToCharArray(i,1)[0] == '\n')
+ {
+ Lines[LineCounter] = Data.Substring(oldnewlinepos, i - oldnewlinepos - 1);
+ oldnewlinepos = i + 1;
+ LineCounter++;
+ }
+ }
+
+ // Lines[] array is too big: needs to be trimmed
+
+ Lines[LineCounter] = Data.Substring(oldnewlinepos, Data.Length - oldnewlinepos);
+ string[] LinesTrimmed = new string[LineCounter + 1];
+ for (int i = 0; i < LineCounter + 1; i++)
+ {
+ LinesTrimmed[i] = Lines[i];
+ }
+ return LinesTrimmed;
+ }
+
+ private static bool ContainsMoreThanOne(string Data, char verify)
+ { // returns true if Data contains verify more than once
+ char[] data = Data.ToCharArray();
+ int count = 0;
+ foreach (char c in data)
+ {
+ if (c == verify)
+ count++;
+ }
+ if (count > 1)
+ return true;
+ return false;
+ }
+
+ private static LineType LineVerify(string line)
+ { // Verifies a line of an ini
+ if (line == "")
+ return LineType.Empty;
+
+ if (line.IndexOf(";") == 0 | line.IndexOf("#") == 0 | line.IndexOf("//") == 0)
+ {
+ return LineType.Comment; // line is a comment: ignore
+ }
+
+ int equalindex = line.IndexOf('=');
+ if (equalindex == 0)
+ return LineType.Faulty; // an '=' cannot be on first place
+
+ if (equalindex != -1) // if = is found in line
+ {
+ // Verify: no '[' , ']' ,';' or '#' before the '='
+ if (line.IndexOf('[', 0, equalindex) != -1
+ | line.IndexOf(']', 0, equalindex) != -1
+ | line.IndexOf(';', 0, equalindex) != -1
+ | line.IndexOf('#', 0, equalindex) != -1)
+ return LineType.Faulty;
+ }
+
+ return LineType.Ok;
+ }
+
+ private static string[] RemoveAndVerifyIni(string[] Lines)
+ {
+ // removes empty lines and comments, and verifies every line
+ string[] temp = new string[Lines.Length];
+ int TempCounter = 0; // number of lines to return
+ foreach (string line in Lines)
+ {
+ switch (LineVerify(line))
+ {
+ case LineType.Faulty: // line is faulty
+ return null;
+ case LineType.Comment: // line is a comment
+ continue;
+ case LineType.Ok: // line is ok
+ temp[TempCounter] = line;
+ TempCounter++;
+ break;
+ case LineType.Empty: // line is empty
+ continue;
+ }
+ }
+ // the temp[] array is too big: needs to be trimmed.
+ string[] OKLines = new string[TempCounter];
+ for (int i = 0; i < TempCounter; i++)
+ {
+ OKLines[i] = temp[i];
+ }
+ return OKLines;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Helpers/MessageQueue.cs b/Hyphen/Plugins/Helpers/MessageQueue.cs
new file mode 100644
index 0000000..5f52f83
--- /dev/null
+++ b/Hyphen/Plugins/Helpers/MessageQueue.cs
@@ -0,0 +1,261 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+namespace Virtuoso.Miranda.Plugins.Helpers
+{
+ /* CLASS ORIGIN: Spearhead project */
+ public class MessageQueue
+ {
+ #region Fields
+
+ private volatile bool enabled, suspended;
+
+ private readonly Queue> queue = new Queue>(5);
+ private Thread QueueThread;
+
+ private readonly ManualResetEvent waitHandle = new ManualResetEvent(true);
+
+ #endregion
+
+ #region Enums
+
+ protected enum CommonWaitTime : int
+ {
+ QueueSuspension = 50,
+ QueueItemProcessed = 1000,
+ QueueProcessed = 1000,
+ }
+
+ #endregion
+
+ #region Events
+
+ public event EventHandler MessageSending;
+ public event EventHandler MessageSent;
+
+ #endregion
+
+ #region .ctors & .dctors
+
+ public MessageQueue()
+ {
+ QueueThread = InitializeQueueThread();
+ }
+
+ protected virtual Thread InitializeQueueThread()
+ {
+ Thread thread = new Thread(ProcessQueue);
+ thread.IsBackground = true;
+
+ return thread;
+ }
+
+ ~MessageQueue()
+ {
+ SetState(false);
+ waitHandle.Close();
+ }
+
+ #endregion
+
+ #region Properties
+
+ public bool Suspended
+ {
+ get
+ {
+ return suspended;
+ }
+ }
+
+ public ManualResetEvent WaitHandle
+ {
+ get
+ {
+ return waitHandle;
+ }
+ }
+
+ protected Queue> Queue
+ {
+ get { return queue; }
+ }
+
+ public bool Enabled
+ {
+ get
+ {
+ return enabled;
+ }
+ }
+
+ public bool QueueHasItems
+ {
+ get
+ {
+ lock (Queue)
+ return Queue.Count > 0;
+ }
+ }
+
+ protected virtual bool ClearQueueWhenDisabled
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ #endregion
+
+ #region Virtuals
+
+ protected virtual void ProcessQueue()
+ {
+ while (Enabled)
+ {
+ while (suspended)
+ Wait(CommonWaitTime.QueueSuspension);
+
+ lock (Queue)
+ {
+ while (QueueHasItems)
+ {
+ waitHandle.Reset();
+ RaiseMessageForwardingEvent();
+
+ DequeueAndSendMessage();
+ Wait(QueueItemProcessedWaitTime);
+
+ RaiseMessageForwardedEvent();
+ }
+
+ waitHandle.Set();
+ }
+
+ Wait(QueueProcessedWaitTime);
+ }
+
+ waitHandle.Set();
+ }
+
+ protected void DequeueAndSendMessage()
+ {
+ lock (Queue)
+ {
+ KeyValuePair data = Queue.Dequeue();
+ SendMessage(data.Key, data.Value);
+ }
+ }
+
+ protected virtual void SendMessage(ContactInfo recipient, string message)
+ {
+ recipient.SendMessage(message);
+ }
+
+ protected virtual int QueueItemProcessedWaitTime
+ {
+ get
+ {
+ return (int)CommonWaitTime.QueueItemProcessed;
+ }
+ }
+
+ protected virtual int QueueProcessedWaitTime
+ {
+ get
+ {
+ return (int)CommonWaitTime.QueueProcessed;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void SuspendQueue()
+ {
+ suspended = true;
+ }
+
+ public void ResumeQueue()
+ {
+ suspended = false;
+ }
+
+ public void EnqueueMessage(ContactInfo to, string message)
+ {
+ lock (queue)
+ queue.Enqueue(new KeyValuePair(to, message));
+
+ SetState(true);
+ }
+
+ public void SetState(bool enabled)
+ {
+ // Queue is used as a sync object here...
+ lock (Queue)
+ {
+ if (enabled)
+ {
+ if ((QueueThread.ThreadState & ThreadState.Stopped) == ThreadState.Stopped)
+ QueueThread = InitializeQueueThread();
+
+ if ((QueueThread.ThreadState & ThreadState.Unstarted) == ThreadState.Unstarted)
+ QueueThread.Start();
+ }
+ else
+ {
+ Queue.Clear();
+ }
+
+ this.enabled = enabled;
+ }
+ }
+
+ protected void RaiseMessageForwardingEvent()
+ {
+ if (MessageSending != null)
+ MessageSending(this, EventArgs.Empty);
+ }
+
+ protected void RaiseMessageForwardedEvent()
+ {
+ if (MessageSent != null)
+ MessageSent(this, EventArgs.Empty);
+ }
+
+ protected void Wait(CommonWaitTime miliseconds)
+ {
+ Wait((int)miliseconds);
+ }
+
+ protected void Wait(int miliseconds)
+ {
+ Thread.Sleep(miliseconds);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Helpers/Sandbox.cs b/Hyphen/Plugins/Helpers/Sandbox.cs
new file mode 100644
index 0000000..b2504bf
--- /dev/null
+++ b/Hyphen/Plugins/Helpers/Sandbox.cs
@@ -0,0 +1,185 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Security;
+using System.Security.Policy;
+using System.IO;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Diagnostics;
+using System.Security.Permissions;
+
+namespace Virtuoso.Miranda.Plugins.Helpers
+{
+ /* CLASS ORIGIN: Loki project */
+ public abstract class Sandbox : RemoteObject
+ {
+ #region Fields
+
+ private AppDomain hostingAppDomain;
+ protected AppDomain HostingAppDomain
+ {
+ get { return hostingAppDomain; }
+ }
+
+ #endregion
+
+ #region Nested classes
+
+ private sealed class MirandaContextInitHelper : RemoteObject
+ {
+ public MirandaContextInitHelper(MirandaContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ MirandaContext.InvalidateCurrent();
+ MirandaContext.InitializeCurrent(context);
+ }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ protected Sandbox() { }
+
+ public static void Unload(Sandbox sandbox)
+ {
+ if (sandbox == null)
+ throw new ArgumentNullException("sandbox");
+
+ if (sandbox.hostingAppDomain == null)
+ throw new ArgumentException();
+
+ if (sandbox.hostingAppDomain == AppDomain.CurrentDomain)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_UnableToUnloadPluginMangerFromCurrentAppDomain);
+
+ sandbox.OnSandboxUnload();
+ sandbox.UnloadHostingAppDomain();
+ }
+
+ #endregion
+
+ #region Virtuals
+
+ protected virtual void InitializeAppDomainSetup(AppDomainSetup domainSetup) { }
+
+ protected virtual void OnSandboxUnload() { }
+
+ #endregion
+
+ #region Helpers
+
+ protected static StrongName GetStrongName(Assembly assembly)
+ {
+ if (assembly == null)
+ throw new ArgumentNullException("assembly");
+
+ AssemblyName assemblyName = assembly.GetName();
+ Debug.Assert(assemblyName != null, "Could not get assembly name");
+
+ byte[] publicKey = assemblyName.GetPublicKey();
+ if (publicKey == null || publicKey.Length == 0)
+ throw new InvalidOperationException(String.Format("{0} is not strongly named", assembly));
+
+ StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
+ return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected void SetUpHostingAppDomain(string name)
+ {
+ SetUpHostingAppDomain(name, null, null);
+ }
+
+ protected void SetUpHostingAppDomain(string name, Evidence evidence, PermissionSet permissions, params StrongName[] fullTrust)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ if (hostingAppDomain != null)
+ throw new InvalidOperationException();
+
+ AppDomainSetup currentSetup = AppDomain.CurrentDomain.SetupInformation;
+ AppDomainSetup domainSetup = new AppDomainSetup();
+
+ domainSetup.ApplicationName = name;
+ domainSetup.ApplicationBase = currentSetup.ApplicationBase;
+ domainSetup.PrivateBinPath = String.Format("{0};{1};", MirandaEnvironment.MirandaPluginsFolderRelativePath, MirandaEnvironment.ManagedPluginsFolderRelativePath);
+ domainSetup.ConfigurationFile = currentSetup.ConfigurationFile;
+
+ InitializeAppDomainSetup(domainSetup);
+
+ if (permissions == null)
+ hostingAppDomain = AppDomain.CreateDomain(name, null, domainSetup);
+ else
+ hostingAppDomain = AppDomain.CreateDomain(name, evidence, domainSetup, permissions, fullTrust);
+ }
+
+ protected void UnloadHostingAppDomain()
+ {
+ if (hostingAppDomain == null)
+ throw new InvalidOperationException();
+ else
+ {
+ AppDomain.Unload(hostingAppDomain);
+ hostingAppDomain = null;
+ }
+ }
+
+ protected T InstantiateRemoteObject(string assemblyName, string typeName, params object[] args) where T : class
+ {
+ return Activator.CreateInstance(hostingAppDomain, assemblyName, typeName, true, BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Public, null, args, null, null, null).Unwrap() as T;
+ }
+
+ protected T InstantiateRemoteObjectFrom(string assemblyFile, string typeName, params object[] args) where T : class
+ {
+ return Activator.CreateInstanceFrom(hostingAppDomain, assemblyFile, typeName, true, BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Public, null, args, null, null, null).Unwrap() as T;
+ }
+
+ protected void InitializeRemoteContext(MirandaContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ if (hostingAppDomain == null)
+ throw new InvalidOperationException();
+
+ InstantiateRemoteObject(Assembly.GetExecutingAssembly().FullName, typeof(MirandaContextInitHelper).FullName, context);
+ }
+
+ public void SetUnhandledExceptionHandler(UnhandledExceptionEventHandler handler)
+ {
+ hostingAppDomain.UnhandledException += handler;
+ }
+
+ public void RemoveUnhandledExceptionHandler(UnhandledExceptionEventHandler handler)
+ {
+ hostingAppDomain.UnhandledException -= handler;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Helpers/TypeInstanceCache.cs b/Hyphen/Plugins/Helpers/TypeInstanceCache.cs
new file mode 100644
index 0000000..28aca20
--- /dev/null
+++ b/Hyphen/Plugins/Helpers/TypeInstanceCache.cs
@@ -0,0 +1,52 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Helpers
+{
+ internal class TypeInstanceCache : Dictionary
+ {
+ #region .ctors
+
+ public TypeInstanceCache() { }
+
+ #endregion
+
+ #region Methods
+
+ public T Instantiate(Type type)
+ {
+ lock (this)
+ {
+ if (ContainsKey(type))
+ return this[type];
+ else
+ {
+ T instance = (T)Activator.CreateInstance(type);
+ this[type] = instance;
+
+ return instance;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Helpers/Utilities.cs b/Hyphen/Plugins/Helpers/Utilities.cs
new file mode 100644
index 0000000..479dc58
--- /dev/null
+++ b/Hyphen/Plugins/Helpers/Utilities.cs
@@ -0,0 +1,38 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Helpers
+{
+ public static class Utilities
+ {
+ [CLSCompliant(false)]
+ public static uint GetTimestamp()
+ {
+ return GetTimestamp(DateTime.Now);
+ }
+
+ [CLSCompliant(false)]
+ public static uint GetTimestamp(DateTime dateTime)
+ {
+ return (uint)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds;
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.Designer.cs b/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.Designer.cs
new file mode 100644
index 0000000..b94c59e
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.Designer.cs
@@ -0,0 +1,169 @@
+using Virtuoso.Miranda.Plugins.Configuration.Forms.Controls;
+namespace Virtuoso.Hyphen.Configuration.Controls
+{
+ partial class AboutContent
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutContent));
+ this.panel1 = new Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemHeader();
+ this.panel2 = new Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemSection();
+ this.VersionLABEL = new System.Windows.Forms.Label();
+ this.HomepageLINK = new System.Windows.Forms.LinkLabel();
+ this.pictureBox1 = new System.Windows.Forms.PictureBox();
+ this.categoryItemSection1 = new Virtuoso.Miranda.Plugins.Configuration.Forms.Controls.CategoryItemSection();
+ this.label1 = new System.Windows.Forms.Label();
+ this.listBox1 = new System.Windows.Forms.ListBox();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+ this.SuspendLayout();
+ //
+ // panel1
+ //
+ this.panel1.BackColor = System.Drawing.Color.Transparent;
+ this.panel1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0)))));
+ this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
+ this.panel1.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.panel1.HeaderFont = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.panel1.HeaderText = "About Hyphen";
+ this.panel1.Image = global::Virtuoso.Miranda.Plugins.Properties.Resources.Icon_232_32x32;
+ this.panel1.Location = new System.Drawing.Point(0, 0);
+ this.panel1.MinimumSize = new System.Drawing.Size(300, 40);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(792, 40);
+ this.panel1.TabIndex = 0;
+ //
+ // panel2
+ //
+ this.panel2.BackColor = System.Drawing.Color.Transparent;
+ this.panel2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(224)))), ((int)(((byte)(192)))));
+ this.panel2.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.panel2.ForeColor = System.Drawing.Color.Black;
+ this.panel2.Location = new System.Drawing.Point(10, 46);
+ this.panel2.MinimumSize = new System.Drawing.Size(300, 20);
+ this.panel2.Name = "panel2";
+ this.panel2.SectionName = "Hyphen";
+ this.panel2.Size = new System.Drawing.Size(765, 20);
+ this.panel2.TabIndex = 1;
+ //
+ // VersionLABEL
+ //
+ this.VersionLABEL.AutoSize = true;
+ this.VersionLABEL.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.VersionLABEL.Location = new System.Drawing.Point(127, 72);
+ this.VersionLABEL.Name = "VersionLABEL";
+ this.VersionLABEL.Size = new System.Drawing.Size(51, 13);
+ this.VersionLABEL.TabIndex = 2;
+ this.VersionLABEL.Text = "v0.0.0.0";
+ //
+ // HomepageLINK
+ //
+ this.HomepageLINK.AutoSize = true;
+ this.HomepageLINK.Location = new System.Drawing.Point(130, 85);
+ this.HomepageLINK.Name = "HomepageLINK";
+ this.HomepageLINK.Size = new System.Drawing.Size(121, 13);
+ this.HomepageLINK.TabIndex = 3;
+ this.HomepageLINK.TabStop = true;
+ this.HomepageLINK.Text = "© (Assembly copyright)";
+ this.HomepageLINK.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.HomepageLINK_LinkClicked);
+ //
+ // pictureBox1
+ //
+ this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
+ this.pictureBox1.Location = new System.Drawing.Point(21, 72);
+ this.pictureBox1.Name = "pictureBox1";
+ this.pictureBox1.Size = new System.Drawing.Size(100, 50);
+ this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
+ this.pictureBox1.TabIndex = 5;
+ this.pictureBox1.TabStop = false;
+ //
+ // categoryItemSection1
+ //
+ this.categoryItemSection1.BackColor = System.Drawing.Color.Transparent;
+ this.categoryItemSection1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(224)))), ((int)(((byte)(192)))));
+ this.categoryItemSection1.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.categoryItemSection1.ForeColor = System.Drawing.Color.Black;
+ this.categoryItemSection1.Location = new System.Drawing.Point(10, 149);
+ this.categoryItemSection1.MinimumSize = new System.Drawing.Size(300, 20);
+ this.categoryItemSection1.Name = "categoryItemSection1";
+ this.categoryItemSection1.SectionName = "Components";
+ this.categoryItemSection1.Size = new System.Drawing.Size(765, 20);
+ this.categoryItemSection1.TabIndex = 4;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Font = new System.Drawing.Font("Tahoma", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
+ this.label1.Location = new System.Drawing.Point(18, 172);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(239, 13);
+ this.label1.TabIndex = 5;
+ this.label1.Text = "Hyphen uses these 3rd party assemblies:";
+ //
+ // listBox1
+ //
+ this.listBox1.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.listBox1.FormattingEnabled = true;
+ this.listBox1.Items.AddRange(new object[] {
+ "RibbonPanel, © Juan Pablo G.C."});
+ this.listBox1.Location = new System.Drawing.Point(33, 188);
+ this.listBox1.Name = "listBox1";
+ this.listBox1.SelectionMode = System.Windows.Forms.SelectionMode.None;
+ this.listBox1.Size = new System.Drawing.Size(224, 104);
+ this.listBox1.Sorted = true;
+ this.listBox1.TabIndex = 6;
+ //
+ // AboutContent
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.listBox1);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.pictureBox1);
+ this.Controls.Add(this.HomepageLINK);
+ this.Controls.Add(this.VersionLABEL);
+ this.Controls.Add(this.categoryItemSection1);
+ this.Controls.Add(this.panel2);
+ this.Controls.Add(this.panel1);
+ this.Name = "AboutContent";
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private CategoryItemHeader panel1;
+ private CategoryItemSection panel2;
+ private System.Windows.Forms.Label VersionLABEL;
+ private System.Windows.Forms.LinkLabel HomepageLINK;
+ private System.Windows.Forms.PictureBox pictureBox1;
+ private CategoryItemSection categoryItemSection1;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.ListBox listBox1;
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.cs b/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.cs
new file mode 100644
index 0000000..8a15f91
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.cs
@@ -0,0 +1,56 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Configuration.Forms.Controls;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace Virtuoso.Hyphen.Configuration.Controls
+{
+ internal sealed partial class AboutContent : CategoryItemControl
+ {
+ private AboutContent()
+ {
+ InitializeComponent();
+ }
+
+ protected internal override bool OnShow(bool firstTime)
+ {
+ if (firstTime)
+ {
+ Assembly assembly = GetType().Assembly;
+
+ VersionLABEL.Text = String.Format("v{0}", assembly.GetName().Version);
+ HomepageLINK.Text =
+ ((AssemblyCopyrightAttribute)
+ assembly.GetCustomAttributes(typeof (AssemblyCopyrightAttribute), false)[0]).Copyright;
+ }
+
+ return false;
+ }
+
+ private void HomepageLINK_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ ProcessStartInfo info = new ProcessStartInfo(Loader.GetInstance().PluginInfo.HomePage);
+ info.UseShellExecute = true;
+
+ Process.Start(info);
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.resx b/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.resx
new file mode 100644
index 0000000..38dc1b1
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Configuration/Controls/AboutContent.resx
@@ -0,0 +1,199 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+
+
+ R0lGODlhZAAyAIcAAAAAAP///9rw2tbh1u347d7o3pKWknBxcP3+/ebn5oiJiISFhBCxCxu1GAaqAAql
+ BA+sBw6nCBOuCxemEBWgDyCuGxyTFyayHjqvNDu0N0O4PTCGLDpyN33GeoXRggq1ABO7CCucIzCsKEq7
+ Q1O/TEWVP2rKYnXJbmamYlN+UEtrSZ7embnltqa2peLy4RvGDCnOFyO2FUbLN0nUOk7VQE3JQVbWSlrE
+ UX3Wc224ZWmrY22uZnmzdKHOnK/PrMrqx2p1aePs4ji+Jid0HVPZQ1baR1/fTVChRt/13Of45fP78vH5
+ 8DjWIFPQQE3DPFfIRV7RS2vnVlWLTF19WJTEjG2NaE7JNlC7O13cRmLWSmvQWGnGV3PSYHjUZYbYdYy1
+ hHiHdV1nW+z26lLcMWjgTGTMTIDuaGSlVdbw0ErZJmXTR3HdVHDUVma4UICdeOjw5oXvZH/bY2eKXIaz
+ eKvgnNTpzmduZev553vlWHjUV4HjX3nHYJXbfJzjg6TkjlBlSdDxxVlcWPz/+2XgOG3lQHrcVonZarno
+ p8TeupDnar7VtMnSxXjkQ4bnVo/pYozlY5bzaqHhg6joinmhZWVpY/X98ZnqbZrpcYvGarXqmMfrs2N0
+ WtHywPL97PT976HtdKr3fqjvfKXse6juf6rvgavvgqntgazvhFR1Qa3vhq7vh6/wibDwi7LwjbHujLPw
+ jrTxkJjLerbxk7fxlLjxlrrymbzym73ynb3ynsHzo8HzpMLzpcPzpsT0p8X0qcb0q8n1r8r1sc31ttD2
+ udL2vNT3v9b3w9f3xNj3xdn4x9z4zN75zuD50eH40+X62Ob62uf63On73p+rmOv74u785vD86fH769H2
+ us/yudv4yeL50+375Pj+9NDXy/b98ff98v7+/v39/fv7+/j4+Pb29vX19fLy8u/v7+zs7Onp6ePj4+Dg
+ 4N/f39zc3NnZ2dXV1dHR0c7OzsnJycLCwr29vbe3t7GxsaqqqqOjo52dnY+Pj4uLi3t7e3V1dWlpaWRk
+ ZCH/C05FVFNDQVBFMi4wAwEBAAAh+QQAAAAAACwAAAAAZAAyAAAI/wADCBxIsKDBgwgTKlzIsKHDhxAj
+ SpxIsaLFixghcqsWrZnHZtGqcctIsqTCTs6QXQvmq6WvYNeQOfNksmZJQdGMCdtFCxarn6xg0dolLNs2
+ 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
+ r83tbnv72+AOt7jHXZGAAAA7
+
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Hyphen/Plugins/Hyphen/Configuration/RuntimeConfiguration.cs b/Hyphen/Plugins/Hyphen/Configuration/RuntimeConfiguration.cs
new file mode 100644
index 0000000..33644fe
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Configuration/RuntimeConfiguration.cs
@@ -0,0 +1,122 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Hyphen.Configuration
+{
+ /* NOTE: WHEN ACCESSING THE CONFIG FROM THE OUTSIDE OF THE LOADER, REMEMBER THAT ITS SINGLETON
+ * RESIDES IN THE DEFAULT APPDOMAIN, THUS IT IS NOT INITIALIZED IN OTHER APPDOMAINS */
+ [Serializable, ConfigurationOptions("0.0.0.6", Encrypt = false, ProfileBound = false)]
+ internal sealed class RuntimeConfiguration : PluginConfiguration
+ {
+ #region Fields
+
+ private static RuntimeConfiguration singleton;
+
+ private string customFusionAssemblyName, customAssemblyProbeTypeName, customPluginManagerTypeName;
+ private bool loadPluginsOnStartup, lazyUnload;
+
+ #endregion
+
+ #region .ctors
+
+ private RuntimeConfiguration() { }
+
+ public static RuntimeConfiguration Singleton
+ {
+ get
+ {
+ if (singleton == null)
+ throw new InvalidOperationException("Configuration not initialized.");
+
+ return singleton;
+ }
+ }
+
+ public static bool Initialized
+ {
+ get
+ {
+ return singleton != null;
+ }
+ }
+
+ protected override void InitializeDefaultConfiguration()
+ {
+ lazyUnload = true;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public string CustomPluginManagerTypeName
+ {
+ get { return customPluginManagerTypeName; }
+ set { customPluginManagerTypeName = value; }
+ }
+
+ public string CustomAssemblyProbeTypeName
+ {
+ get { return customAssemblyProbeTypeName; }
+ set { customAssemblyProbeTypeName = value; }
+ }
+
+ public string CustomFusionAssemblyName
+ {
+ get { return customFusionAssemblyName; }
+ set { customFusionAssemblyName = value; }
+ }
+
+ public bool LoadPluginsOnStartup
+ {
+ get { return loadPluginsOnStartup; }
+ set { loadPluginsOnStartup = value; }
+ }
+
+ public bool UseLazyUnload
+ {
+ get { return lazyUnload; }
+ set { lazyUnload = value; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static void Initialize()
+ {
+ singleton = RuntimeConfiguration.Load();
+ }
+
+ public static void Reset()
+ {
+ singleton = PluginConfiguration.GetDefaultConfiguration();
+ }
+
+ public static void Reload()
+ {
+ Initialize();
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Configuration/RuntimeConfigurator.cs b/Hyphen/Plugins/Hyphen/Configuration/RuntimeConfigurator.cs
new file mode 100644
index 0000000..2e3a852
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Configuration/RuntimeConfigurator.cs
@@ -0,0 +1,86 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Configuration;
+using Virtuoso.Miranda.Plugins;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Miranda.Plugins.Forms;
+using Virtuoso.Hyphen.Configuration.Controls;
+
+namespace Virtuoso.Hyphen.Configuration
+{
+ internal sealed class RuntimeConfigurator : IInternalConfigurator
+ {
+ #region .ctors
+
+ private RuntimeConfigurator() { }
+
+ #endregion
+
+ #region Properties
+
+ private static IConfigurablePlugin singleton;
+ public static IConfigurablePlugin Singleton
+ {
+ get
+ {
+ return singleton ?? (singleton = new RuntimeConfigurator());
+ }
+ }
+
+ public string Name
+ {
+ get { return "Hyphen"; }
+ }
+
+ private PluginConfiguration[] configuration;
+ public PluginConfiguration[] Configuration
+ {
+ get { return configuration ?? (configuration = new PluginConfiguration[] { RuntimeConfiguration.Singleton }); }
+ }
+
+ public void ResetConfiguration()
+ {
+ RuntimeConfiguration.Reset();
+ }
+
+ public void ReloadConfiguration()
+ {
+ RuntimeConfiguration.Reload();
+ }
+ #endregion
+
+ #region Handlers
+
+ public void PopulateConfiguration(CategoryCollection categories)
+ {
+ Category category = new Category(TextResources.Config_General, TextResources.Config_General_Description);
+
+ CategoryItem item = new CategoryItem(TextResources.Config_General_About, TextResources.Config_General_About_Description, typeof(AboutContent));
+ item.Image = VisualResources.Image_64x67_Information;
+ category.Items.Add(item);
+
+ categories.Add(category);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/FusionContext.cs b/Hyphen/Plugins/Hyphen/FusionContext.cs
new file mode 100644
index 0000000..38b6096
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/FusionContext.cs
@@ -0,0 +1,107 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins;
+using System.IO;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Hyphen
+{
+ [CLSCompliant(false)]
+ public sealed class FusionContext : RemoteObject
+ {
+ #region Fields
+
+ private readonly AssemblyProbe assemblyProbe;
+ private readonly IntPtr nativePluginLink;
+ private readonly Loader loader;
+
+ private static FusionContext emptySingleton;
+
+ #endregion
+
+ #region .ctors
+
+ // Empty .ctor
+ private FusionContext() { }
+
+ internal FusionContext(Loader loader, AssemblyProbe pluginProbe, IntPtr nativePluginLink)
+ {
+ if (loader == null)
+ throw new ArgumentNullException("loader");
+
+ if (pluginProbe == null)
+ throw new ArgumentNullException("pluginProbe");
+
+ if (nativePluginLink == IntPtr.Zero)
+ throw new ArgumentNullException("nativePluginLink");
+
+ this.loader = loader;
+ this.assemblyProbe = pluginProbe;
+ this.nativePluginLink = nativePluginLink;
+ }
+
+ #endregion
+
+ #region Properties
+
+ private void CheckEmpty()
+ {
+ // TODO: Localize
+ if (IsInvalid) throw new InvalidOperationException("This context is empty.");
+ }
+
+ public bool IsInvalid
+ {
+ get
+ {
+ return this.nativePluginLink == IntPtr.Zero;
+ }
+ }
+
+ internal static FusionContext Empty
+ {
+ get
+ {
+ return emptySingleton ?? (emptySingleton = new FusionContext());
+ }
+ }
+
+ public AssemblyProbe AssemblyProbe
+ {
+ get { CheckEmpty(); return assemblyProbe; }
+ }
+
+ internal IntPtr NativePluginLink
+ {
+ get { CheckEmpty(); return nativePluginLink; }
+ }
+
+ internal Loader Loader
+ {
+ get
+ {
+ return loader;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Loader.cs b/Hyphen/Plugins/Hyphen/Loader.cs
new file mode 100644
index 0000000..aa6ef8c
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Loader.cs
@@ -0,0 +1,1105 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Windows.Forms;
+using Virtuoso.Hyphen.Configuration;
+using Virtuoso.Hyphen.Mini;
+using Virtuoso.Hyphen.Native;
+using Virtuoso.Miranda.Plugins;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Configuration;
+using Virtuoso.Miranda.Plugins.Configuration.Forms;
+using Virtuoso.Miranda.Plugins.Forms;
+using Virtuoso.Miranda.Plugins.Forms.Controls;
+using Virtuoso.Miranda.Plugins.Helpers;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Miranda.Plugins.ThirdParty.Updater;
+
+namespace Virtuoso.Hyphen
+{
+ ///
+ /// Controls managed plugin fusion and orchestrates the runtime.
+ ///
+ internal sealed class Loader : RemoteObject
+ {
+ #region Fields
+
+ #region Constants
+
+ public const string LogCategory = "Hyphen";
+
+ private static readonly Uri HyphenUpdateUrl = new Uri("http://virtuosity.aspone.cz/files/miranda/development/hyphen/updates/hyphen_update.zip"),
+ HyphenVersionUrl = new Uri("http://virtuosity.aspone.cz/files/miranda/development/hyphen/updates/hyphen_update_version.txt"),
+ HyphenHomepageUrl = new Uri("http://virtuosity.aspone.cz");
+
+ private static readonly Version MinMirandaVersion = new Version(0, 7, 0, 0);
+
+ #endregion
+
+ #region Services
+
+ private const string ServicePrefix = "Virtuoso.Hyphen.Loader.Services";
+
+ private const string LoadUnloadPluginsServiceName = ServicePrefix + "LoadUnloadPlugins";
+ private const string ConfigureModulesServiceName = ServicePrefix + "ConfigureModules";
+ private const string ShowManagedMenuServiceName = ServicePrefix + "ShowManagedMenu";
+ private const string ManagePluginsServiceName = ServicePrefix + "ManagePlugins";
+
+ #endregion
+
+ #region Common
+
+ private static Loader Singleton;
+ private static readonly object SyncObject = new object();
+
+ private volatile bool Unloaded;
+
+ private PLUGININFO pluginInfo;
+ private static IntPtr DummyPluginInfo;
+
+ private UnmanagedStructHandle PluginInfoHandle;
+ private UnmanagedStructHandle PluginInfoExHandle;
+ private MirandaPluginLink PluginLink;
+
+ private FusionContext FusionContext;
+ private PluginsSandbox IsolatedPluginsSandbox;
+
+ private MenuItemDeclarationAttribute PluginTasksItem;
+ private ManagedMainMenu ManagedMainMenu;
+
+ private readonly HookDescriptorCollection InternalHooks = new HookDescriptorCollection();
+
+ private FileSystemWatcher PluginsFolderWatcher;
+ private readonly ManualResetEvent PluginsLoadedEvent = new ManualResetEvent(false);
+
+ private Mutex SingleInstanceMutex;
+ private SynchronizationContext UIThreadSyncContext;
+
+ #endregion
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets a current version of the runtime.
+ ///
+ public static Version HyphenVersion
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get
+ {
+ return Assembly.GetExecutingAssembly().GetName().Version;
+ }
+ }
+
+ ///
+ /// Gets a value whether the plugins are loaded. Not synchronized, always use in a lock.
+ ///
+ /// THIS MUST NOT BE SYNCHRONIZED, ALWAYS CALLED IN LOCK! (possible deadlock in FusionProgressDialog).
+ public bool PluginsLoaded
+ {
+ get
+ {
+ return IsolatedPluginsSandbox != null;
+ }
+ }
+
+ ///
+ /// Gets Hyphen plugin info.
+ ///
+ public PLUGININFO PluginInfo
+ {
+ get
+ {
+ return this.pluginInfo;
+ }
+ }
+
+ #endregion
+
+ #region API IMPL
+
+ #region First-Init
+
+ ///
+ /// Initializes the runtime.
+ ///
+ ///
+ /// Called from the exported IL stubs as a first method to initialize the Loader singleton.
+ ///
+ public static void Initialize()
+ {
+ lock (SyncObject)
+ {
+ if (Singleton == null)
+ Singleton = new Loader();
+ }
+ }
+
+ ///
+ /// Initializes the Loader and prepares its PLUGININFO.
+ ///
+ private Loader()
+ {
+ AppDomain.CurrentDomain.UnhandledException += TrapUnhandledException;
+ Log.DebuggerWrite(0, LogCategory, "Initializing Hyphen...");
+
+ try
+ {
+ InitializePluginInfo();
+ Virtuoso.Miranda.Plugins.Infrastructure.RuntimeEnvironment.Initialize();
+
+ Log.DebuggerWrite(0, LogCategory, "Hyphen successfully initialized.");
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Failed constructing the PLUGININFO." + Environment.NewLine + e.ToString());
+ throw;
+ }
+ }
+
+ ///
+ /// Publishes the plugin info and marshals it into a ptr.
+ ///
+ private void InitializePluginInfo()
+ {
+ PLUGININFO pluginInfo = new PLUGININFO();
+ PopulatePluginInfo(pluginInfo);
+ PluginInfoHandle = new UnmanagedStructHandle(ref pluginInfo);
+
+ PLUGININFOEX pluginInfoEx = new PLUGININFOEX(UUID.HyphenUUID);
+ PopulatePluginInfo(pluginInfoEx);
+ PluginInfoExHandle = new UnmanagedStructHandle(ref pluginInfoEx);
+
+ this.pluginInfo = pluginInfoEx;
+ }
+
+ ///
+ /// Populates the plugin info with Hyphen's identity.
+ ///
+ /// Plugin info.
+ private static void PopulatePluginInfo(PLUGININFO pluginInfo)
+ {
+ if (pluginInfo == null)
+ throw new ArgumentNullException("pluginInfo");
+
+ pluginInfo.Size = Marshal.SizeOf(pluginInfo.GetType());
+ pluginInfo.Author = "virtuoso";
+ pluginInfo.AuthorEmail = "deml.tomas@seznam.cz";
+ pluginInfo.Copyright = "© 2006-2010, virtuoso";
+ pluginInfo.Description = "Microsoft.net runtime for managed plugins.";
+ pluginInfo.HomePage = HyphenHomepageUrl.ToString();
+ pluginInfo.Flags = (byte)PluginFlags.UnicodeAware;
+ pluginInfo.ReplacesDefaultModule = 0;
+ pluginInfo.ShortName = "Hyphen";
+ pluginInfo.Version = Translate.ToMirandaVersion(HyphenVersion);
+ }
+
+ #endregion
+
+ #region Getters
+
+ ///
+ /// Gets a specified version of the Loader.
+ ///
+ /// A version of the Loader to be returned.
+ /// An instance of the Loader or NULL when the version cannot be satisfied.
+ ///
+ /// Cannot be inlined because of the HyphenVersion property which relies on the Assembly.GetExecutingAssembly() method.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static Loader GetInstance(Version requiredVersion)
+ {
+ if (requiredVersion > HyphenVersion)
+ return null;
+ else
+ return GetInstance();
+ }
+
+ ///
+ /// Gets an instance of the Loader.
+ ///
+ /// An instance of the Loader.
+ /// method not called.
+ public static Loader GetInstance()
+ {
+ lock (SyncObject)
+ {
+ if (Singleton == null)
+ throw new InvalidOperationException();
+
+ return Singleton;
+ }
+ }
+
+ #endregion
+
+ #region APIs
+
+ #region MirandaPluginInfo
+
+ ///
+ /// Represents the MirandaPluginInfo export of Miranda's API.
+ ///
+ /// Miranda version (in Miranda's format)
+ /// Ptr to an instance of the PLUGININFO structure.
+ public IntPtr MirandaPluginInfo(uint version)
+ {
+ MirandaPluginInfoShared(version);
+
+ if (!SupportsMirandaVersion(version))
+ return GetDummyPluginInfo();
+ else
+ {
+ lock (SyncObject)
+ return PluginInfoHandle.IntPtr;
+ }
+ }
+
+ ///
+ /// Represents the MirandaPluginInfoEx export of Miranda's API.
+ ///
+ /// Miranda version (in Miranda's format)
+ /// Ptr to an instance of the PLUGININFOEX structure.
+ ///
+ /// Specific to post-0.7#20 Miranda API.
+ ///
+ public IntPtr MirandaPluginInfoEx(uint version)
+ {
+ MirandaPluginInfoShared(version);
+
+ if (!SupportsMirandaVersion(version))
+ return GetDummyPluginInfo();
+ else
+ {
+ lock (SyncObject)
+ return PluginInfoExHandle.IntPtr;
+ }
+ }
+
+ ///
+ /// Gets the pointer to a dummy plugin info structure.
+ ///
+ /// Pointer.
+ public static IntPtr GetDummyPluginInfo()
+ {
+ lock (SyncObject)
+ {
+ if (DummyPluginInfo == IntPtr.Zero)
+ {
+ DummyPluginInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PLUGININFO)));
+ Marshal.StructureToPtr(new PLUGININFO(), DummyPluginInfo, false);
+ }
+
+ return DummyPluginInfo;
+ }
+ }
+
+ ///
+ /// Performs initialization steps common to all MirandaPluginInfo exports.
+ ///
+ /// Miranda version.
+ public void MirandaPluginInfoShared(uint version)
+ {
+ if (MirandaEnvironment.MirandaVersion == null)
+ MirandaEnvironment.MirandaVersion = Translate.FromMirandaVersion(version);
+ }
+
+ public static bool SupportsMirandaVersion(uint version)
+ {
+ return SupportsMirandaVersion(Translate.FromMirandaVersion(version));
+ }
+
+ public static bool SupportsMirandaVersion(Version version)
+ {
+ return (version >= MinMirandaVersion);
+ }
+
+ ///
+ /// Represents the MirandaPluginInterfaces export of Miranda's API.
+ ///
+ /// Ptr to an array of interface GUIDs.
+ public IntPtr MirandaPluginInterfaces()
+ {
+ lock (SyncObject)
+ return UUID.HyphenInterfaceUUIDs;
+ }
+
+ #endregion
+
+ #region Load
+
+ ///
+ /// Called by a standalone module to ensure the Loader is ready (the module could be loaded before Hyphen).
+ ///
+ /// Ptr to an instance of the PLUGINLINK structure.
+ ///
+ /// Calls the to ensure that the Loader is ready. This method is needed
+ /// to handle a situation when a standalone module is loaded before Hyphen.
+ /// Does nothing when Hyphen is already loaded.
+ ///
+ public void ModuleInducedLoad(IntPtr pPluginLink)
+ {
+ Load(pPluginLink);
+ }
+
+ ///
+ /// Represents the Load export of Miranda API. Loads Hyphen and initializes the runtime.
+ ///
+ /// Ptr to an instance of the PLUGINLINK structure.
+ /// Result.
+ public int Load(IntPtr pPluginLink)
+ {
+ lock (SyncObject)
+ {
+ Virtuoso.Miranda.Plugins.Infrastructure.RuntimeEnvironment.HyphenIsLoading = true;
+
+ try
+ {
+ // Hyphen not loaded yet...
+ if (PluginLink == null)
+ {
+ Log.DebuggerWrite(0, LogCategory, "Loading Hyphen...");
+
+ VerifyFxConfiguration();
+ EnsureSingleInstance();
+
+ InitializeRuntimeContext(pPluginLink);
+ HookRuntimeEvents();
+ }
+
+ Log.DebuggerWrite(0, LogCategory, "Hyphen is loaded.");
+ return CallbackResult.Success;
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Failed loading Hyphen - " + e.ToString());
+ MirandaPlugin.Hyphen.Singleton.HandleException(e, null);
+
+ Unload();
+ return CallbackResult.Failure;
+ }
+ finally
+ {
+ Virtuoso.Miranda.Plugins.Infrastructure.RuntimeEnvironment.HyphenIsLoading = false;
+ }
+ }
+ }
+
+ ///
+ /// Initializes the runtime context (including configuration).
+ ///
+ /// Ptr to PLUGINLINK to initialize from.
+ private void InitializeRuntimeContext(IntPtr pPluginLink)
+ {
+ // Init configuration
+ RuntimeConfiguration.Initialize();
+
+ // Marshal the plugin link
+ PluginLink = MirandaPluginLink.FromPointer(pPluginLink);
+
+ // Initialize temporary context (specific to default AppDomain) for standalone modules
+ MirandaContext.InitializeCurrent(PluginLink, true);
+ }
+
+ ///
+ /// Hooks to the ModulesLoaded event to complete context initialization.
+ ///
+ private void HookRuntimeEvents()
+ {
+ try
+ {
+ HookDescriptor modulesLoadedEventHook = HookDescriptor.SetUpAndStore(InternalHooks, MirandaEvents.ME_SYSTEM_MODULESLOADED, MirandaPlugin.Hyphen.Singleton.Descriptor, CompleteInitialization, HookType.EventHook);
+ HookManager.CreateHook(modulesLoadedEventHook);
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Failed hooking to the modules-loaded event. Initialization failed.");
+ throw new MirandaException(TextResources.ExceptionMsg_InternalErrorOccurred, e);
+ }
+ }
+
+ #region Initialization
+
+ ///
+ /// Completes the runtime initialization and fires the ModulesLoaded event.
+ ///
+ private int CompleteInitialization(UIntPtr wParam, IntPtr lParam)
+ {
+ lock (SyncObject)
+ {
+ try
+ {
+ // Unhook the event
+ HookDescriptor descriptor = InternalHooks.Find(MirandaEvents.ME_SYSTEM_MODULESLOADED);
+ HookManager.DestroyHook(descriptor);
+ InternalHooks.Remove(descriptor);
+
+ // Hook the shutdown event to unload Hyphen
+ HookManager.CreateHook(HookDescriptor.SetUpAndStore(InternalHooks, MirandaEvents.ME_SYSTEM_OKTOEXIT, MirandaPlugin.Hyphen.Singleton.Descriptor, UnloadOnShutdownService, HookType.EventHook));
+
+ InitializeUpdater();
+ InitializePluginsFolder();
+ InitializeMenu();
+
+ MirandaContext.Current.RaiseModulesLoadedEvent();
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Failed initializing Loader - " + e.Message);
+ Unload();
+ }
+
+ return (int)CallbackResult.Success;
+ }
+ }
+
+ ///
+ /// Registers Hyphen for updates via Updater.
+ ///
+ private static void InitializeUpdater()
+ {
+ if (UpdaterPlugin.IsUpdateSupported())
+ {
+ Update update = new Update(MirandaPlugin.Hyphen.Singleton, HyphenUpdateUrl, HyphenVersionUrl, " ");
+ UpdaterPlugin.RegisterForUpdate(update);
+ }
+ }
+
+ ///
+ /// Initializes the FileSystemWatcher to watch for plugin changes.
+ ///
+ private void InitializePluginsFolder()
+ {
+ if (!Directory.Exists(MirandaEnvironment.ManagedPluginsFolderPath))
+ Directory.CreateDirectory(MirandaEnvironment.ManagedPluginsFolderPath);
+
+ PluginsFolderWatcher = new FileSystemWatcher(MirandaEnvironment.ManagedPluginsFolderPath, "*.dll");
+ PluginsFolderWatcher.IncludeSubdirectories = false;
+ PluginsFolderWatcher.NotifyFilter = NotifyFilters.LastWrite;
+ PluginsFolderWatcher.Deleted += PluginsWatcherHandler;
+ PluginsFolderWatcher.Changed += PluginsWatcherHandler;
+ PluginsFolderWatcher.Created += PluginsWatcherHandler;
+ }
+
+ ///
+ /// Populates Miranda's menu with Hyphen's items and initializes managed menu for plugins.
+ ///
+ private void InitializeMenu()
+ {
+ PluginDescriptor descriptor = MirandaPlugin.Hyphen.Singleton.Descriptor;
+ ContactList clist = MirandaContext.Current.ContactList;
+
+ // Create services for the items
+ HookManager.CreateHook(HookDescriptor.SetUpAndStore(InternalHooks, LoadUnloadPluginsServiceName, descriptor, LoadUnloadPluginsService, HookType.ServiceFunction));
+ HookManager.CreateHook(HookDescriptor.SetUpAndStore(InternalHooks, ConfigureModulesServiceName, descriptor, ConfigureModulesService, HookType.ServiceFunction));
+ HookManager.CreateHook(HookDescriptor.SetUpAndStore(InternalHooks, ShowManagedMenuServiceName, descriptor, ShowManagedMenuService, HookType.ServiceFunction));
+ HookManager.CreateHook(HookDescriptor.SetUpAndStore(InternalHooks, ManagePluginsServiceName, descriptor, ManagePluginsService, HookType.ServiceFunction));
+
+ InitializeManagedMenu();
+
+ MenuItemDeclarationAttribute item = new MenuItemDeclarationAttribute(TextResources.UI_Text_LoadUnloadPlugins, TextResources.UI_Text_Hyphen, LoadUnloadPluginsServiceName);
+ item.IsContactMenuItem = false;
+ item.HasIcon = true;
+ item.UseEmbeddedIcon = true;
+ item.IconID = "Virtuoso.Miranda.Plugins.Resources.LoadUnloadPlugins.ico";
+ clist.AddMenuItem(MirandaPlugin.Hyphen.Singleton, item);
+
+ item = PluginTasksItem = new MenuItemDeclarationAttribute(TextResources.UI_Text_ManagePlugins, TextResources.UI_Text_Hyphen, ManagePluginsServiceName);
+ item.IsContactMenuItem = false;
+ item.HasIcon = true;
+ item.UseEmbeddedIcon = true;
+ item.IconID = "Virtuoso.Miranda.Plugins.Resources.Configure.ico";
+ clist.AddMenuItem(MirandaPlugin.Hyphen.Singleton, item);
+
+ item = new MenuItemDeclarationAttribute(TextResources.UI_Text_ConfigureStandaloneModules, TextResources.UI_Text_Hyphen, ConfigureModulesServiceName);
+ item.IsContactMenuItem = false;
+ item.HasIcon = true;
+ item.UseEmbeddedIcon = true;
+ item.IconID = "Virtuoso.Miranda.Plugins.Resources.Configure.ico";
+ clist.AddMenuItem(MirandaPlugin.Hyphen.Singleton, item);
+ }
+
+ ///
+ /// Initializes the managed menu for managed plugins. This menu, unlike Miranda's, supports item removal.
+ ///
+ private void InitializeManagedMenu()
+ {
+ ManagedMainMenu = new ManagedMainMenu();
+
+ MenuItemDeclarationAttribute item = new MenuItemDeclarationAttribute(TextResources.UI_Text_ShowManagedMainMenu, ShowManagedMenuServiceName);
+ item.IsContactMenuItem = false;
+ item.HasIcon = true;
+ item.UseEmbeddedIcon = true;
+ item.IconID = "Virtuoso.Miranda.Plugins.Resources.ShowManagedMenuItems.ico";
+
+ // Add a proxy item for the managed menu into M's menu (on behalf of a dummy plugin)
+ MirandaContext.Current.ContactList.AddMenuItem(MirandaPlugin.Hyphen.Singleton, item);
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Unload
+
+ ///
+ /// Represents the Unload export of Miranda API. Unloads Hyphen and shuts down the runtime.
+ ///
+ /// Unload result.
+ ///
+ /// Called by the UnloadOnShutdownService to unload managed plugins before Miranda does.
+ /// Managed plugins SHOULD NOT be unloaded when Miranda's unloading, because I do not consider it safe
+ /// (i.e. heap corruption is likely to occur).
+ ///
+ public int Unload()
+ {
+ lock (SyncObject)
+ {
+ try
+ {
+ Log.DebuggerWrite(0, LogCategory, "Hyphen unload begin.");
+
+ if (Unloaded)
+ return (int)CallbackResult.Success;
+
+ if (RuntimeConfiguration.Initialized)
+ RuntimeConfiguration.Singleton.Save();
+
+ bool lazy = RuntimeConfiguration.Singleton.UseLazyUnload;
+
+ if (PluginsLoaded)
+ UnloadPlugins(lazy);
+
+ // Lazy unload (only Tray disposal, all other resources will be cleaned by the CLR)
+ if (lazy)
+ {
+ Log.DebuggerWrite(0, LogCategory, "Lazy unload completed.");
+ }
+ // Complete unload
+ else
+ {
+ PerformFullUnload();
+ }
+
+ return (int)CallbackResult.Success;
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Failed unloading Hyphen.\n" + e.ToString());
+ return (int)CallbackResult.Failure;
+ }
+ finally
+ {
+ Unloaded = true;
+ }
+ }
+ }
+
+ ///
+ /// Unloads Hyphen completelly.
+ ///
+ private void PerformFullUnload()
+ {
+ DisposePluginsWatcher();
+
+ PluginInfoHandle.Free();
+ PluginInfoExHandle.Free();
+
+ InteropBufferPool.Dispose();
+
+ // Invalidate context only if there are no standalone modules that may depend on it
+ if (MirandaContext.Initialized && !ModuleManager.Singleton.HasModules)
+ MirandaContext.InvalidateCurrent();
+
+ Log.DebuggerWrite(0, LogCategory, "Unload completed.");
+ }
+
+ ///
+ /// Disposes the plugin's folder watcher (if needed).
+ ///
+ private void DisposePluginsWatcher()
+ {
+ if (PluginsFolderWatcher != null)
+ PluginsFolderWatcher.Dispose();
+ }
+
+ ///
+ /// Disposes the Tray manager (if needed).
+ ///
+ private void DisposePluginTasksMenu()
+ {
+ /*if (trayManager != null)
+ trayManager.Dispose();*/
+ }
+
+ ///
+ /// Broadcasts the BeforeShutdown event to the managed plugins and unloads Hyphen on Miranda's shutdown.
+ ///
+ private int UnloadOnShutdownService(UIntPtr wParam, IntPtr lParam)
+ {
+ try
+ {
+ lock (SyncObject)
+ {
+ HookDescriptor descriptor = InternalHooks.Find(MirandaEvents.ME_SYSTEM_OKTOEXIT);
+ HookManager.DestroyHook(descriptor);
+ InternalHooks.Remove(descriptor);
+
+ if (PluginsLoaded)
+ IsolatedPluginsSandbox.PluginManager.DoContextCallback(BroadcastBeforeMirandaExitEvent, null);
+
+ Unload();
+ }
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Unable to unload Hyphen from UnloadOnShutdownService: " + e.Message);
+ }
+
+ return 0;
+ }
+
+ ///
+ /// Broadcasts the BeforeShutdown event to the managed plugins.
+ ///
+ private static void BroadcastBeforeMirandaExitEvent(PluginManagerBase sender, object state)
+ {
+ try
+ {
+ SynchronizationHelper.BeginCollectionUpdate(sender.Plugins);
+
+ foreach (PluginDescriptor descriptor in sender.Plugins)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(descriptor);
+ descriptor.Plugin.BeforeMirandaShutdownInternal();
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(descriptor);
+ }
+ }
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(sender.Plugins);
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+
+ #region Loader Control
+
+ #region Loading
+
+ #region Core
+
+ ///
+ /// Loads / unloads managed plugins.
+ ///
+ private int LoadUnloadPluginsService(UIntPtr wParam, IntPtr lParam)
+ {
+ lock (SyncObject)
+ {
+ try
+ {
+ if (!PluginsLoaded)
+ LoadPlugins();
+ else if (DialogResult.Yes == MessageBox.Show(TextResources.MsgBox_Text_LoadUnloadPlugins_Unload, TextResources.MsgBox_Caption_LoadUnloadPlugins, MessageBoxButtons.YesNo, MessageBoxIcon.Warning))
+ UnloadPlugins();
+ }
+ catch (Exception e)
+ {
+ MirandaPlugin.Hyphen.Singleton.HandleException(e, null);
+ }
+ }
+
+ return (int)CallbackResult.Success;
+ }
+
+ ///
+ /// Reloads managed plugins.
+ ///
+ private void ReloadPlugins()
+ {
+ UnloadPlugins();
+ LoadPlugins();
+ }
+
+ ///
+ /// Loads managed plugins.
+ ///
+ private void LoadPlugins()
+ {
+ lock (SyncObject)
+ {
+ if (PluginsLoaded)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_InternalErrorOccurred);
+
+ InitializePluginTasksMenu();
+
+ // Show the progress dialog => start fusion
+ FusionProgressDialog.ShowDialog(StartFusion);
+ }
+ }
+
+ ///
+ /// Called by the FusionProgressDialog on another thread to perform the fusion itself.
+ ///
+ private void StartFusion()
+ {
+ Log.DebuggerWrite(0, LogCategory, "Loading plugins...");
+
+ InitializeSandbox();
+ InitializeFusionContext();
+
+ LoadPluginManager();
+
+ try
+ {
+ IsolatedPluginsSandbox.PluginManager.FindAndLoadPlugins();
+ ClearStringResolverCache();
+ }
+ catch (Exception e)
+ {
+ UnloadPlugins();
+ TrapUnhandledException(this, new UnhandledExceptionEventArgs(e, false));
+ }
+ }
+
+ ///
+ /// Initializes a sandbox for plugins.
+ ///
+ private void InitializeSandbox()
+ {
+ IsolatedPluginsSandbox = new PluginsSandbox();
+ IsolatedPluginsSandbox.SetUnhandledExceptionHandler(TrapUnhandledException);
+ IsolatedPluginsSandbox.LoadAssemblyProbe();
+ }
+
+ ///
+ /// Initializes a fusion context.
+ ///
+ private void InitializeFusionContext()
+ {
+ FusionContext = new FusionContext(this, IsolatedPluginsSandbox.AssemblyProbe, PluginLink.NativePluginLinkPtr);
+ }
+
+ ///
+ /// Loads the Plugin Manager.
+ ///
+ private void LoadPluginManager()
+ {
+ IsolatedPluginsSandbox.LoadPluginManager(FusionContext);
+
+ // Associate the PM with the default context for management purposes
+ MirandaContext.Current.AssociatePluginManager(IsolatedPluginsSandbox.PluginManager);
+
+ IsolatedPluginsSandbox.PluginManager.FusionCompleted += delegate
+ {
+ PluginsLoadedEvent.Set();
+ PluginsFolderWatcher.EnableRaisingEvents = true;
+ };
+
+ PromoteManagedMenuIntoAppDomain(IsolatedPluginsSandbox.PluginManager);
+ }
+
+ ///
+ /// Initializes the Tray Manager.
+ ///
+ public void InitializePluginTasksMenu()
+ {
+ /*InitializeUISyncContext();
+ UIThreadSyncContext.Send(delegate { trayManager = new TrayMenuManager(true); }, null);*/
+ }
+
+ #endregion
+
+ #region Helpers
+
+ ///
+ /// Initializes the UI sync context for event dispathing on the current thread.
+ ///
+ private void InitializeUISyncContext()
+ {
+ if (UIThreadSyncContext == null)
+ UIThreadSyncContext = SynchronizationContext.Current ?? new SynchronizationContext();
+ }
+
+ ///
+ /// Registers managed main menu for Miranda's AddMenuItem services in an AppDomain.
+ ///
+ /// Plugin manager.
+ public void PromoteManagedMenuIntoAppDomain(PluginManagerBase pluginManager)
+ {
+ if (pluginManager == null)
+ throw new ArgumentNullException("pluginManager");
+
+ if (ManagedMainMenu != null)
+ {
+ pluginManager.DoContextCallback(delegate(PluginManagerBase _sender, ManagedMainMenu _menu)
+ {
+ ManagedMainMenu.RegisterInterceptors(_menu);
+ }, ManagedMainMenu);
+ }
+ }
+
+ ///
+ /// Clears the StringResolver cache (plugins are loaded and resolvers are junk now).
+ ///
+ private static void ClearStringResolverCache()
+ {
+ StringResolverCache cache = StringResolverCache.Singleton;
+
+ lock (cache)
+ cache.Clear();
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Services
+
+ ///
+ /// Shows managed menu.
+ ///
+ private int ShowManagedMenuService(UIntPtr wParam, IntPtr lParam)
+ {
+ lock (SyncObject)
+ {
+ // If there are no standalone modules and managed plugins are not loaded, LOAD THEM AND SHOW THE MENU
+ if (!ModuleManager.Singleton.HasModules && !PluginsLoaded)
+ {
+ // Async
+ LoadPlugins();
+ PluginsLoadedEvent.WaitOne(10000, false);
+ }
+
+ if (ManagedMainMenu != null)
+ ManagedMainMenu.ShowUnderCursor();
+ }
+
+ return (int)CallbackResult.Success;
+ }
+
+ ///
+ /// Shows a configuration dialog to configure standalone modules.
+ ///
+ private int ConfigureModulesService(UIntPtr wParam, IntPtr lParam)
+ {
+ ConfigurationDialog.Present(false);
+ return (int)CallbackResult.Success;
+ }
+
+ ///
+ /// Shows Isolated-plugins management dialog.
+ ///
+ private int ManagePluginsService(UIntPtr wParam, IntPtr lParam)
+ {
+ if (!PluginsLoaded)
+ MessageBox.Show(TextResources.MsgBox_Text_NoPluginsLoaded, TextResources.MsgBox_Caption_NoPluginsLoaded, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ else
+ ManagePlugins();
+
+ return CallbackResult.Success;
+ }
+
+ ///
+ /// Shows the plugin management dialog.
+ ///
+ public void ManagePlugins()
+ {
+ if (PluginsLoaded)
+ {
+ MirandaContext.Current.PluginManager.DoContextCallback(delegate
+ {
+ ConfigurationDialog.Present(false, ConfigurationDialog.CreatePath(PMConfigurator.Singleton, TextResources.Config_Management, TextResources.Config_Management_Plugins));
+ }, null);
+ }
+ }
+
+ ///
+ /// Handles a plugin file change.
+ ///
+ private void PluginsWatcherHandler(object sender, FileSystemEventArgs e)
+ {
+ lock (SyncObject)
+ {
+ try
+ {
+ if (!PluginsLoaded)
+ return;
+
+ if (DialogResult.Yes == MessageBox.Show(TextResources.MsgBox_Text_PluginUpdated, String.Format(TextResources.MsgBox_Formatable1_Caption_PluginUpdated, e.Name.Substring(0, e.Name.Length - 4)), MessageBoxButtons.YesNo, MessageBoxIcon.Information))
+ {
+ // Async
+ ReloadPlugins();
+ PluginsLoadedEvent.WaitOne(10000, false);
+
+ MessageBox.Show(TextResources.MsgBox_Text_PluginReloadComplete, TextResources.MsgBox_Caption_PluginReloadComplete, MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.DebuggerWrite(0, LogCategory, "Plugin reload failed: " + ex.ToString());
+ }
+ }
+ }
+
+ #endregion
+
+ #region Unloading
+
+ ///
+ /// Unloads managed plugins.
+ ///
+ public void UnloadPlugins()
+ {
+ UnloadPlugins(false);
+ }
+
+ ///
+ /// Unloads managed plugins.
+ ///
+ /// TRUE to perform fast unload only; FALSE to perfrom full unload.
+ public void UnloadPlugins(bool lazy)
+ {
+ lock (SyncObject)
+ {
+ if (!PluginsLoaded)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_InternalErrorOccurred);
+
+ DisposePluginTasksMenu();
+
+ PluginsFolderWatcher.EnableRaisingEvents = false;
+ PluginsLoadedEvent.Reset();
+
+ MirandaContext.Current.DetachPluginManager();
+ IsolatedPluginsSandbox.PluginManager.Shutdown(lazy);
+
+ if (!lazy)
+ Sandbox.Unload(IsolatedPluginsSandbox);
+
+ IsolatedPluginsSandbox = null;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Misc.
+
+ ///
+ /// Handles Hyphen (default AppDomain) unhandled exceptions.
+ ///
+ private void TrapUnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ MirandaPlugin.Hyphen.Singleton.HandleException((Exception)e.ExceptionObject, null);
+ }
+
+ ///
+ /// Handles Windows Forms (default AppDomain) unhandled exceptions.
+ ///
+ private void TrapUnhandledException(object sender, ThreadExceptionEventArgs e)
+ {
+ TrapUnhandledException(sender, new UnhandledExceptionEventArgs(e.Exception, false));
+ }
+
+ ///
+ /// Verifies the .config file is present and when it is not, the default one is created.
+ ///
+ private static void VerifyFxConfiguration()
+ {
+ string configFileName = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
+
+ if (String.IsNullOrEmpty(configFileName) || !File.Exists(configFileName))
+ {
+ using (StreamReader reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Virtuoso.Miranda.Plugins.Resources.miranda32.exe.config")))
+ using (StreamWriter writer = new StreamWriter(configFileName))
+ writer.Write(reader.ReadToEnd());
+
+ MessageBox.Show(TextResources.MsgBox_Text_MirandaRestartRequired, TextResources.ExceptionMsg_MirandaRestartRequired, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ throw new MirandaException(TextResources.ExceptionMsg_MirandaRestartRequired);
+ }
+ }
+
+ ///
+ /// Ensures only a single instance of Hyphen is loaded into the process.
+ ///
+ private void EnsureSingleInstance()
+ {
+ bool acquired = false;
+ SingleInstanceMutex = new Mutex(true, GetSingletonMutexName(), out acquired);
+
+ if (!acquired)
+ throw new NotSupportedException(TextResources.ExceptionMsg_HyphenSxSNotSupported);
+ }
+
+ ///
+ /// Formats singleton's mutex name.
+ ///
+ /// Mutext name.
+ private static string GetSingletonMutexName()
+ {
+ return String.Format("{0}::Hyphen", Process.GetCurrentProcess().Id.ToString());
+ }
+
+ ///
+ /// Invokes a delegate on behalf of Loader's AppDomain (i.e. the default one).
+ ///
+ /// Delegate.
+ /// Optional arguments.
+ ///
+ public object DoContextCallback(Delegate del, params object[] args)
+ {
+ if (del == null)
+ throw new ArgumentNullException("del");
+
+ return del.DynamicInvoke(args);
+ }
+
+ ///
+ /// Verifies whether the code is executing in the default AppDomain.
+ ///
+ /// The code is not executing in the default AppDomain.
+ public static void VerifyDefaultDomain()
+ {
+ if (!AppDomain.CurrentDomain.IsDefaultAppDomain())
+ throw new NotSupportedException(TextResources.ExceptionMsg_FeatureNotAvailableInDomain);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Hyphen/Mini/Custom/CustomApiExportDescriptor.cs b/Hyphen/Plugins/Hyphen/Mini/Custom/CustomApiExportDescriptor.cs
new file mode 100644
index 0000000..7f61c8f
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Custom/CustomApiExportDescriptor.cs
@@ -0,0 +1,71 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Hyphen.Mini.Custom
+{
+ public delegate object CustomApiExportCallback(object[] args);
+
+ public sealed class CustomApiExportDescriptor
+ {
+ #region Fields
+
+ private readonly string exportName;
+ public string ExportName
+ {
+ get { return exportName; }
+ }
+
+ private readonly CustomApiExportCallback callback;
+ public CustomApiExportCallback Callback
+ {
+ get { return callback; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public CustomApiExportDescriptor(string exportName, CustomApiExportCallback callback)
+ {
+ if (String.IsNullOrEmpty(exportName)) throw new ArgumentNullException("exportName");
+ if (callback == null) throw new ArgumentNullException("callback");
+
+ this.exportName = exportName;
+ this.callback = callback;
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal T Execute(params object[] args)
+ {
+ object result = callback(args);
+
+ if (result != null)
+ return (T)result;
+ else
+ return (T)default(T);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/Custom/CustomApiExportHandlerAttribute.cs b/Hyphen/Plugins/Hyphen/Mini/Custom/CustomApiExportHandlerAttribute.cs
new file mode 100644
index 0000000..998205e
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Custom/CustomApiExportHandlerAttribute.cs
@@ -0,0 +1,47 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Hyphen.Mini.Custom
+{
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class CustomApiExportHandlerAttribute : Attribute
+ {
+ #region Fields
+
+ private readonly string exportName;
+ public string ExportName
+ {
+ get { return exportName; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public CustomApiExportHandlerAttribute(string exportName)
+ {
+ if (exportName == null) throw new ArgumentNullException("exportName");
+ this.exportName = exportName;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/Custom/CustomizedDatabaseDriver.cs b/Hyphen/Plugins/Hyphen/Mini/Custom/CustomizedDatabaseDriver.cs
new file mode 100644
index 0000000..ced4c1a
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Custom/CustomizedDatabaseDriver.cs
@@ -0,0 +1,88 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Hyphen.Mini;
+using Virtuoso.Miranda.Plugins;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Hyphen.Native;
+
+namespace Virtuoso.Hyphen.Mini.Custom
+{
+ public abstract class CustomizedDatabaseDriver : DatabaseDriver
+ {
+ #region .ctors
+
+ protected CustomizedDatabaseDriver() { }
+
+ #endregion
+
+ #region Thunks
+
+ protected override sealed int GetCapabilityThunk(int flags)
+ {
+ return Convert.ToInt32(GetCapability(flags));
+ }
+
+ protected override sealed int GetFriendlyNameThunk(IntPtr buffer, int size, int shortName)
+ {
+ string name = GetFriendlyName(shortName != 0, size);
+ if (name == null) return -1;
+
+ byte[] bytes = Encoding.Default.GetBytes(name);
+
+ if (size < bytes.Length)
+ return -1;
+ else
+ {
+ Marshal.Copy(bytes, 0, buffer, bytes.Length);
+ return 0;
+ }
+ }
+
+ protected override sealed int InitThunk(string profile, IntPtr pLink)
+ {
+ return Init(profile, pLink);
+ }
+
+ protected override sealed int UnloadThunk(int wasLoaded)
+ {
+ return Unload(wasLoaded != 0);
+ }
+
+ #endregion
+
+ #region Abstract Members
+
+ protected abstract bool GetCapability(int flags);
+
+ protected abstract string GetFriendlyName(bool shortName, int size);
+
+ protected abstract int MakeDatabase(string profile, ref int error);
+
+ protected abstract int GrokHeader(string profile, ref int error);
+
+ protected abstract int Init(string profile, IntPtr pLink);
+
+ protected abstract int Unload(bool wasLoaded);
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseDriver.cs b/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseDriver.cs
new file mode 100644
index 0000000..7dc16d4
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseDriver.cs
@@ -0,0 +1,120 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins;
+
+namespace Virtuoso.Hyphen.Mini.Custom
+{
+ [LoaderOptions(LoaderOptions.HasCustomApiExports | LoaderOptions.CannotBeUnloaded)]
+ public abstract class DatabaseDriver : StandalonePlugin
+ {
+ #region Fields
+
+ private GCHandle DatabaseLinkGcHandle;
+ private UnmanagedStructHandle DatabaseLinkHandle;
+
+ private volatile bool Disposed;
+
+ #endregion
+
+ #region .ctors
+
+ protected DatabaseDriver() { }
+
+ ~DatabaseDriver()
+ {
+ UnloadPreThunk(0);
+ }
+
+ #endregion
+
+ #region Api Handlers
+
+ [CustomApiExportHandler("DatabasePluginInfo")]
+ internal object DatabasePluginInfoThunk(object[] args)
+ {
+ DatabaseLink link = new DatabaseLink();
+
+ link.Size = Marshal.SizeOf(typeof(DatabaseLink));
+ link.GetCapability = GetCapabilityThunk;
+ link.GetFriendlyName = GetFriendlyNameThunk;
+ link.GrokHeader = GrokHeaderThunk;
+ link.Init = InitPreThunk;
+ link.MakeDatabase = MakeDatabaseThunk;
+ link.Unload = UnloadPreThunk;
+ DatabaseLinkHandle = new UnmanagedStructHandle(ref link);
+
+ DatabaseLinkGcHandle = GCHandle.Alloc(link);
+ return DatabaseLinkHandle.IntPtr;
+ }
+
+ #endregion
+
+ #region Thunks
+
+ ///
+ /// MirandaContext is not available at the time of the invocation.
+ ///
+ ///
+ ///
+ ///
+ protected abstract int MakeDatabaseThunk(string profile, ref int error);
+
+ protected abstract int GrokHeaderThunk(string profile, ref int error);
+
+ protected abstract int GetCapabilityThunk(int flags);
+
+ protected abstract int GetFriendlyNameThunk(IntPtr buffer, int size, int shortName);
+
+ private int InitPreThunk(string profile, IntPtr pLink)
+ {
+ return -(Math.Abs(Module.Load(pLink)) + Math.Abs(InitThunk(profile, pLink)));
+ }
+
+ protected abstract int InitThunk(string profile, IntPtr link);
+
+ private int UnloadPreThunk(int wasLoaded)
+ {
+ try
+ {
+ if (!Disposed)
+ {
+ UnloadThunk(wasLoaded);
+ DatabaseLinkHandle.Free();
+
+ if (DatabaseLinkGcHandle.IsAllocated)
+ DatabaseLinkGcHandle.Free();
+ }
+ }
+ finally
+ {
+ Disposed = true;
+ }
+
+ return 0;
+ }
+
+ protected abstract int UnloadThunk(int wasLoaded);
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseLink.cs b/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseLink.cs
new file mode 100644
index 0000000..f295347
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseLink.cs
@@ -0,0 +1,91 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Hyphen.Mini.Custom
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct DatabaseLink
+ {
+ public int Size;
+
+ public GetCapabilityPrototype GetCapability;
+ public GetFriendlyNamePrototype GetFriendlyName;
+ public MakeDatabasePrototype MakeDatabase;
+ public GrokHeaderPrototype GrokHeader;
+ public InitPrototype Init;
+ public UnloadPrototype Unload;
+ }}
+
+ /* typedef struct {
+ int cbSize;
+
+ returns what the driver can do given the flag
+
+ int (*getCapability) ( int flag );
+
+ /
+ buf: pointer to a string buffer
+ cch: length of buffer
+ shortName: if true, the driver should return a short but descriptive name, e.g. "3.xx profile"
+ Affect: The database plugin must return a "friendly name" into buf and not exceed cch bytes,
+ e.g. "Database driver for 3.xx profiles"
+ Returns: 0 on success, non zero on failure
+
+ int (*getFriendlyName) ( char * buf, size_t cch, int shortName );
+
+
+ profile: pointer to a string which contains full path + name
+ Affect: The database plugin should create the profile, the filepath will not exist at
+ the time of this call, profile will be C:\..\.dat
+ Note: Do not prompt the user in anyway about this operation.
+ Note: Do not initialise internal data structures at this point!
+ Returns: 0 on success, non zero on failure - error contains extended error information, see EMKPRF_*
+
+ int (*makeDatabase) ( char * profile, int * error );
+
+
+ profile: [in] a null terminated string to file path of selected profile
+ error: [in/out] pointer to an int to set with error if any
+ Affect: Ask the database plugin if it supports the given profile, if it does it will
+ return 0, if it doesnt return 1, with the error set in error -- EGROKPRF_* can be valid error
+ condition, most common error would be [EGROKPRF_UNKHEADER]
+ Note: Just because 1 is returned, doesnt mean the profile is not supported, the profile might be damaged
+ etc.
+ Returns: 0 on success, non zero on failure
+
+ int (*grokHeader) ( char * profile, int * error );
+
+
+ Affect: Tell the database to create all services/hooks that a 3.xx legecy database might support into link,
+ which is a PLUGINLINK structure
+ Returns: 0 on success, nonzero on failure
+
+ int (*Init) ( char * profile, void * link );
+
+
+ Affect: The database plugin should shutdown, unloading things from the core and freeing internal structures
+ Returns: 0 on success, nonzero on failure
+ Note: Unload() might be called even if Init() was never called, wasLoaded is set to 1 if Init() was ever called.
+
+ int (*Unload) ( int wasLoaded );
+
+} DATABASELINK; */
diff --git a/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseLinkPrototypes.cs b/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseLinkPrototypes.cs
new file mode 100644
index 0000000..d6b7981
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Custom/DatabaseLinkPrototypes.cs
@@ -0,0 +1,105 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Hyphen;
+
+namespace Virtuoso.Hyphen.Mini.Custom
+{
+ //int (*getCapability) ( int flag );
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int GetCapabilityPrototype(int flags);
+
+ //int (*getFriendlyName) ( char * buf, size_t cch, int shortName );
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int GetFriendlyNamePrototype(IntPtr buf, int size, int shortName);
+
+ //int (*makeDatabase) ( char * profile, int * error );
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int MakeDatabasePrototype([MarshalAs(UnmanagedType.LPStr)] string profile, ref int error);
+
+ //int (*grokHeader) ( char * profile, int * error );
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int GrokHeaderPrototype([MarshalAs(UnmanagedType.LPStr)] string profile, ref int error);
+
+ //int (*Init) ( char * profile, void * link );
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int InitPrototype([MarshalAs(UnmanagedType.LPStr)] string profile, IntPtr link);
+
+ //int (*Unload) ( int wasLoaded );
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int UnloadPrototype(int wasLoaded);
+}
+
+/* typedef struct {
+ int cbSize;
+
+ returns what the driver can do given the flag
+
+ int (*getCapability) ( int flag );
+
+ /
+ buf: pointer to a string buffer
+ cch: length of buffer
+ shortName: if true, the driver should return a short but descriptive name, e.g. "3.xx profile"
+ Affect: The database plugin must return a "friendly name" into buf and not exceed cch bytes,
+ e.g. "Database driver for 3.xx profiles"
+ Returns: 0 on success, non zero on failure
+
+ int (*getFriendlyName) ( char * buf, size_t cch, int shortName );
+
+
+ profile: pointer to a string which contains full path + name
+ Affect: The database plugin should create the profile, the filepath will not exist at
+ the time of this call, profile will be C:\..\.dat
+ Note: Do not prompt the user in anyway about this operation.
+ Note: Do not initialise internal data structures at this point!
+ Returns: 0 on success, non zero on failure - error contains extended error information, see EMKPRF_*
+
+ int (*makeDatabase) ( char * profile, int * error );
+
+
+ profile: [in] a null terminated string to file path of selected profile
+ error: [in/out] pointer to an int to set with error if any
+ Affect: Ask the database plugin if it supports the given profile, if it does it will
+ return 0, if it doesnt return 1, with the error set in error -- EGROKPRF_* can be valid error
+ condition, most common error would be [EGROKPRF_UNKHEADER]
+ Note: Just because 1 is returned, doesnt mean the profile is not supported, the profile might be damaged
+ etc.
+ Returns: 0 on success, non zero on failure
+
+ int (*grokHeader) ( char * profile, int * error );
+
+
+ Affect: Tell the database to create all services/hooks that a 3.xx legecy database might support into link,
+ which is a PLUGINLINK structure
+ Returns: 0 on success, nonzero on failure
+
+ int (*Init) ( char * profile, void * link );
+
+
+ Affect: The database plugin should shutdown, unloading things from the core and freeing internal structures
+ Returns: 0 on success, nonzero on failure
+ Note: Unload() might be called even if Init() was never called, wasLoaded is set to 1 if Init() was ever called.
+
+ int (*Unload) ( int wasLoaded );
+
+} DATABASELINK; */
+
diff --git a/Hyphen/Plugins/Hyphen/Mini/Module.AuxiliaryPluginManager.cs b/Hyphen/Plugins/Hyphen/Mini/Module.AuxiliaryPluginManager.cs
new file mode 100644
index 0000000..c4d365b
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Module.AuxiliaryPluginManager.cs
@@ -0,0 +1,72 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins;
+using System.Runtime.CompilerServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Hyphen.Mini
+{
+ partial class Module
+ {
+ ///
+ /// Auxiliary Plugin Manager used to load a standalone plugin into the default AppDomain.
+ ///
+ private sealed class AuxiliaryPluginManager : PluginManagerBase
+ {
+ #region Fields
+
+ private static AuxiliaryPluginManager Singleton;
+
+ #endregion
+
+ #region .ctors
+
+ private AuxiliaryPluginManager()
+ : base(FusionContext.Empty, false, false) { }
+
+ #endregion
+
+ #region Impl
+
+ protected internal override void FindAndLoadPlugins()
+ {
+ throw new NotSupportedException();
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public static AuxiliaryPluginManager GetInstance()
+ {
+ return Singleton ?? (Singleton = new AuxiliaryPluginManager());
+ }
+
+ ///
+ /// Populates context information to be available for standalone plugins residing in the default AppDomain.
+ ///
+ /// The information are not published when there are no standalone modules to conserve resources.
+ public void FinishInitialization()
+ {
+ MirandaContext.Current.PopulateContextInformation();
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/Module.cs b/Hyphen/Plugins/Hyphen/Mini/Module.cs
new file mode 100644
index 0000000..8455744
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/Module.cs
@@ -0,0 +1,529 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Reflection;
+using System.IO;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Runtime.ConstrainedExecution;
+using System.Diagnostics;
+using Virtuoso.Hyphen.Native;
+using Virtuoso.Miranda.Plugins;
+using System.Runtime.CompilerServices;
+using Virtuoso.Hyphen.Mini.Custom;
+using System.Windows.Forms;
+using Virtuoso.Miranda.Plugins.Forms;
+
+namespace Virtuoso.Hyphen.Mini
+{
+ ///
+ /// Represents a standalone module loaded into the default AppDomain.
+ ///
+ public sealed partial class Module
+ {
+ #region Fields
+
+ private const string LogCategory = "HyphenMini";
+ private const string MasterSuffx = ".master.dll";
+
+ private Assembly MasterAssembly;
+ private StandalonePlugin standalonePlugin;
+ private PluginDescriptor PluginDescriptor;
+ private bool isPostV07Build20Api;
+
+ private IntPtr PluginInfoPtr;
+ private IntPtr MirandaPluginInterfacesPtr;
+
+ private readonly Assembly MiniAssembly;
+ private volatile bool Loaded, Unloaded;
+ private LoaderOptions LoaderOptions;
+
+ private AuxiliaryPluginManager PluginManager;
+ private Loader HyphenLoader;
+
+ private readonly string MasterDirectory, MasterAssemblyPath;
+
+ #endregion
+
+ #region .ctors & .dctors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// TRUE if the modules runs under a post-0.7#20 API; FALSE otherwise.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal Module(bool exApi)
+ {
+ // Init runtime
+ Loader.Initialize();
+
+ // Get a stub assembly establishing the connection
+ MiniAssembly = Assembly.GetCallingAssembly();
+
+ // Get the Loader instance based on stub's version
+ Version supportedVersion = MiniAssembly.GetName().Version;
+ HyphenLoader = Loader.GetInstance(new Version(supportedVersion.ToString(3)));
+
+ if (HyphenLoader == null)
+ {
+ string message = String.Format("Hyphen.Mini module requested a Loader of version {0}, but that one is not available. Upgrade Hyphen.", supportedVersion);
+ Log.DebuggerWrite(5, LogCategory, message);
+
+ throw new RuntimeNotSupportedException(null, supportedVersion);
+ }
+
+ isPostV07Build20Api = exApi;
+ MasterDirectory = Path.GetDirectoryName(MiniAssembly.Location);
+ MasterAssemblyPath = Path.GetFileName(MiniAssembly.Location);
+
+ Log.DebuggerWrite(0, LogCategory, "Connection between Miranda and '" + MasterAssemblyPath + "' established.");
+ }
+
+ ///
+ /// Finalizes the module.
+ ///
+ ~Module()
+ {
+ Unloaded = true;
+ }
+
+ #endregion
+
+ #region API IMPL
+
+ ///
+ /// Represents the MirandaPluginInfo export of Miranda's API.
+ ///
+ /// Miranda version (in Miranda's format)
+ /// Ptr to an instance of the PLUGININFO(EX) structure.
+ internal IntPtr MirandaPluginInfo(uint version)
+ {
+ // Already called?
+ if (PluginInfoPtr != IntPtr.Zero)
+ return PluginInfoPtr;
+
+ Log.DebuggerWrite(0, LogCategory, "MirandaPluginInfo export invoked for " + MasterAssemblyPath);
+ StandalonePlugin plugin = null;
+
+ try
+ {
+ // Execute common init tasks
+ HyphenLoader.MirandaPluginInfoShared(version);
+
+ // Load the plugin
+ plugin = LoadActualPlugin();
+
+ // OK
+ if (plugin != null)
+ {
+ plugin.Module = this;
+ plugin.AfterModuleInitializationInternal();
+
+ // OK
+ if (PublishPluginInformation(plugin, version))
+ {
+ standalonePlugin = plugin;
+ MasterAssembly = plugin.GetType().Assembly;
+
+ ProbeCustomApiExports(plugin);
+ return PluginInfoPtr;
+ }
+ }
+ else
+ Log.DebuggerWrite(0, LogCategory, "No master assembly found for '" + MasterAssemblyPath + "' - aborting initialization");
+ }
+ catch (Exception e)
+ {
+ DefaultExceptionHandler.Create(plugin).HandleException(e, null);
+ Log.DebuggerWrite(5, LogCategory, "An error occurred while executing the MirandaPluginInfo export\n" + e.ToString());
+ }
+
+ // Return dummy instance, not null (crashes Miranda 0.8.0.1)
+ return PluginInfoPtr = Loader.GetDummyPluginInfo();
+ }
+
+ ///
+ /// Represents the MirandaPluginInterfaces export of Miranda's API.
+ ///
+ /// Ptr to an array of interface GUIDs.
+ internal IntPtr MirandaPluginInterfaces()
+ {
+ if (MirandaPluginInterfacesPtr != IntPtr.Zero)
+ return MirandaPluginInterfacesPtr;
+
+ try
+ {
+ return (MirandaPluginInterfacesPtr = standalonePlugin.MirandaPluginInterfaces());
+ }
+ catch (Exception e)
+ {
+ DefaultExceptionHandler.Create(StandalonePlugin).HandleException(e, StandalonePlugin.Descriptor);
+ Log.DebuggerWrite(5, LogCategory, "An error occured while executing the MirandaPluginInterfaces export\n" + e.ToString());
+ }
+
+ return IntPtr.Zero;
+ }
+
+ ///
+ /// Represents the Load export of Miranda API. Loads Hyphen and initializes the module.
+ ///
+ /// Ptr to an instance of the PLUGINLINK structure.
+ /// Load result.
+ internal int Load(IntPtr pPluginLink)
+ {
+ try
+ {
+ if (Loaded)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginAlreadyInitialized);
+
+ Log.DebuggerWrite(0, LogCategory, "Load export invoked for " + MasterAssemblyPath);
+
+ // Initialize the runtime (if necessary)
+ HyphenLoader.ModuleInducedLoad(pPluginLink);
+ ModuleManager.Singleton.RegisterModule(this);
+
+ // Get the auxiliary plugin manager to load a standalone plugin into the default AppDomain
+ PluginManager = AuxiliaryPluginManager.GetInstance();
+ PluginDescriptor = PluginManager.LoadPlugin(standalonePlugin, false);
+
+ // Call plugin's Load export
+ standalonePlugin.LoadInternal(pPluginLink);
+
+ // When Miranda completes initialization...
+ MirandaContext.Current.ModulesLoaded += ModulesLoadedHandler;
+
+ Log.DebuggerWrite(0, LogCategory, "Finishing " + MasterAssemblyPath + " initialization");
+ return (int)CallbackResult.Success;
+ }
+ catch (Exception e)
+ {
+ DefaultExceptionHandler.Create(StandalonePlugin).HandleException(e, StandalonePlugin.Descriptor);
+ Log.DebuggerWrite(5, LogCategory, "An error occurred while executing the Load export\n" + e.ToString());
+
+ return (int)CallbackResult.Failure;
+ }
+ finally
+ {
+ Loaded = true;
+ }
+ }
+
+ ///
+ /// Enables the plugin when Miranda completes initialization.
+ ///
+ private void ModulesLoadedHandler(object sender, EventArgs e)
+ {
+ try
+ {
+ // DO NOT TOUCH
+ PluginManager.FinishInitialization();
+
+ /* Inject the managed menu into our (default) AppDomain
+ * (the menu is used for additional items ONLY) */
+ HyphenLoader.PromoteManagedMenuIntoAppDomain(PluginManager);
+
+ // Enable the plugin
+ if (PluginLoaded && PluginInitialized)
+ PluginManager.SetPluginState(PluginDescriptor, PluginState.Enabled);
+ }
+ catch (Exception ex)
+ {
+ DefaultExceptionHandler.Create(PluginDescriptor.Plugin).HandleException(ex, PluginDescriptor);
+ }
+ finally
+ {
+ PluginConfiguration.FlushCaches();
+ }
+ }
+
+ ///
+ /// Represents the Unload export of Miranda API. Unloads Hyphen and shuts down the runtime.
+ ///
+ /// Unload result.
+ internal int Unload()
+ {
+ try
+ {
+ if (!CanUnload())
+ return (int)CallbackResult.Success;
+
+ // Call the Unload export
+ standalonePlugin.UnloadInternal();
+
+ PluginManager.SetPluginState(PluginDescriptor, PluginState.Disabled);
+ ModuleManager.Singleton.UnregisterModule(this);
+
+ if (!standalonePlugin.HasCustomPluginInfo)
+ Marshal.FreeHGlobal(PluginInfoPtr);
+
+ if (!standalonePlugin.HasCustomPluginInterfaces)
+ Marshal.FreeHGlobal(MirandaPluginInterfacesPtr);
+
+ PluginDescriptor = null;
+ standalonePlugin = null;
+
+ Log.DebuggerWrite(0, LogCategory, "Connection between Miranda and '" + MasterAssemblyPath + "' broken.");
+ }
+ catch (Exception e)
+ {
+ DefaultExceptionHandler.Create(StandalonePlugin).HandleException(e, StandalonePlugin.Descriptor);
+ Log.DebuggerWrite(5, LogCategory, "An error occurred while executing the Unload export\n" + e.ToString());
+
+ return (int)CallbackResult.Failure;
+ }
+ finally
+ {
+ Unloaded = true;
+ }
+
+ return (int)CallbackResult.Success;
+ }
+
+ ///
+ /// Gets a indication whether a plugin can be unloaded.
+ ///
+ ///
+ private bool CanUnload()
+ {
+ return !Unloaded && PluginInitialized &&
+ (LoaderOptions & LoaderOptions.CannotBeUnloaded) != LoaderOptions.CannotBeUnloaded;
+ }
+
+ #endregion
+
+ #region Helpers
+
+ ///
+ /// Gathers and publishes plugin information.
+ ///
+ /// Plugin to evaluate.
+ /// Miranda version to pass to the plugin.
+ /// TRUE if the information were obtained; FALSE if not.
+ private bool PublishPluginInformation(StandalonePlugin plugin, uint version)
+ {
+ if (plugin == null)
+ throw new ArgumentNullException("plugin");
+
+ try
+ {
+ LoaderOptionsAttribute loaderOptions = LoaderOptionsAttribute.Get(plugin.GetType(), LoaderOptionsOwner.Type);
+
+ if (!loaderOptions.SupportsMirandaVersion(version))
+ return false;
+
+ LoaderOptions = loaderOptions.Options;
+ PluginInfoPtr = plugin.MirandaPluginInfo(version, isPostV07Build20Api);
+
+ return true;
+ }
+ catch (Exception)
+ {
+ PluginInfoPtr = IntPtr.Zero;
+ return false;
+ }
+ }
+
+ ///
+ /// Probes custom API exports of a standalone plugin.
+ ///
+ /// Plugin.
+ private void ProbeCustomApiExports(StandalonePlugin plugin)
+ {
+ if (plugin == null)
+ throw new ArgumentNullException("plugin");
+
+ if ((LoaderOptions & LoaderOptions.HasCustomApiExports) != LoaderOptions.HasCustomApiExports)
+ return;
+ try
+ {
+ Type customApiHandlerAttribType = typeof(CustomApiExportHandlerAttribute),
+ customApiHandlerCallbackType = typeof(CustomApiExportCallback);
+
+ foreach (MethodInfo method in plugin.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy))
+ {
+ if (!method.IsDefined(customApiHandlerAttribType, true))
+ continue;
+
+ Delegate callbackDeleg = Delegate.CreateDelegate(customApiHandlerCallbackType, plugin, method, false);
+
+ if (callbackDeleg == null)
+ continue;
+
+ CustomApiExportHandlerAttribute attrib = (CustomApiExportHandlerAttribute)method.GetCustomAttributes(customApiHandlerAttribType, true)[0];
+ plugin.CustomApiHandlers.Add(new CustomApiExportDescriptor(attrib.ExportName, (CustomApiExportCallback)callbackDeleg));
+
+ }
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Unable to probe custom plugin api exports. " + e.Message);
+ throw;
+ }
+ }
+
+ ///
+ /// Loads the actual plugin behind a proxy assembly.
+ ///
+ /// An instance of a plugin.
+ private StandalonePlugin LoadActualPlugin()
+ {
+ try
+ {
+ string assemblyPath = Path.Combine(MasterDirectory, Path.GetFileNameWithoutExtension(MasterAssemblyPath) + MasterSuffx);
+
+ if (!File.Exists(assemblyPath))
+ return null;
+
+ Assembly masterAssembly = Assembly.LoadFile(assemblyPath);
+ Type[] exposedTypes = PluginManagerBase.GetExposedPlugins(masterAssembly);
+
+ // Find a first standalone plugin
+ Type masterType = Array.Find(exposedTypes, delegate(Type _type)
+ {
+ return _type.IsSubclassOf(typeof(StandalonePlugin));
+ });
+
+ // None found
+ if (masterType == null)
+ throw new TypeLoadException(String.Format(TextResources.ExceptionMsg_Formatable1_UnableToLoadMasterType, MasterAssemblyPath));
+
+ try
+ {
+ return (StandalonePlugin)PluginManagerBase.InstantiatePlugin(masterType, true);
+ }
+ catch (RuntimeNotSupportedException rvnsE)
+ {
+ MessageBox.Show(String.Format(TextResources.ExceptionMsg_Formatable2_RuntimeVersionNotAvailable, masterType.FullName, rvnsE.RequiredVersion), TextResources.MsgBox_Caption_RuntimeVersionNotAvailable, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ throw;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(5, LogCategory, "Unable to instantiate the master plugin. " + e.Message);
+ throw;
+ }
+ }
+
+ #endregion
+
+ #region Equals etc.
+
+ public override int GetHashCode()
+ {
+ return MiniAssembly.ToString().GetHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ Module other = obj as Module;
+
+ if (other == null)
+ return false;
+
+ return other.GetHashCode() == GetHashCode();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets a standalone plugin behind this module.
+ ///
+ public StandalonePlugin StandalonePlugin
+ {
+ get
+ {
+ return standalonePlugin;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the runtime runs under the post-0.7#20 Miranda API.
+ ///
+ public bool IsPostV07Build20Api
+ {
+ get
+ {
+ return isPostV07Build20Api;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the standalone plugin was loaded.
+ ///
+ private bool PluginLoaded
+ {
+ get
+ {
+ return standalonePlugin != null;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the standalone plugins was initialized (i.e. has a descriptor).
+ ///
+ private bool PluginInitialized
+ {
+ get
+ {
+ return PluginDescriptor != null;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Executes a custom API export.
+ ///
+ /// Type of the return value.
+ /// Export name.
+ /// Additional data.
+ /// Return value.
+ internal T ExecuteCustomApiExport(string exportName, params object[] data)
+ {
+ if (String.IsNullOrEmpty(exportName))
+ throw new ArgumentNullException("exportName");
+
+ if (data == null)
+ throw new ArgumentNullException("data");
+
+ if (!PluginLoaded)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginNotInitialized);
+
+ // Find the export
+ CustomApiExportDescriptor descriptor = standalonePlugin.CustomApiHandlers.Find(delegate(CustomApiExportDescriptor _handler)
+ {
+ return _handler.ExportName == exportName;
+ });
+
+ if (descriptor == null)
+ throw new NotImplementedException(exportName);
+ else
+ return descriptor.Execute(data);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/ModuleManager.cs b/Hyphen/Plugins/Hyphen/Mini/ModuleManager.cs
new file mode 100644
index 0000000..aa2ef0c
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/ModuleManager.cs
@@ -0,0 +1,102 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.CompilerServices;
+using System.Collections.ObjectModel;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins;
+using Virtuoso.Miranda.Plugins.Forms;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Hyphen.Mini
+{
+ public sealed class ModuleManager
+ {
+ #region Fields
+
+ private static readonly object SyncObject = new object();
+ private static ModuleManager singleton;
+
+ private readonly ModuleCollection RegistredModulesCollection = new ModuleCollection();
+ private readonly ModuleReadOnlyCollection registeredModules;
+
+ #endregion
+
+ #region .ctors
+
+ private ModuleManager()
+ {
+ this.registeredModules = new ModuleReadOnlyCollection(RegistredModulesCollection);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public static ModuleManager Singleton
+ {
+ get
+ {
+ Loader.VerifyDefaultDomain();
+
+ lock (SyncObject)
+ return singleton ?? (singleton = new ModuleManager());
+ }
+ }
+
+ public ModuleReadOnlyCollection RegisteredModules
+ {
+ get
+ {
+ return registeredModules;
+ }
+ }
+
+ public bool HasModules
+ {
+ get
+ {
+ return registeredModules.Count > 0;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal void RegisterModule(Module module)
+ {
+ if (module == null)
+ throw new ArgumentNullException("module");
+
+ RegistredModulesCollection.Add(module);
+ }
+
+ internal void UnregisterModule(Module module)
+ {
+ if (module == null)
+ throw new ArgumentNullException("module");
+
+ RegistredModulesCollection.Remove(module);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/ProtocolPlugin.cs b/Hyphen/Plugins/Hyphen/Mini/ProtocolPlugin.cs
new file mode 100644
index 0000000..6e1fe11
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/ProtocolPlugin.cs
@@ -0,0 +1,264 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Runtime.InteropServices;
+using System.Drawing;
+using Virtuoso.Miranda.Plugins;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Infrastructure.Protocols;
+
+namespace Virtuoso.Hyphen.Mini
+{
+ ///
+ /// Represents a base class for managed Miranda protocols.
+ ///
+ public abstract class ProtocolPlugin : StandalonePlugin
+ {
+ #region Fields
+
+ private IntPtr namePtr;
+ private int NameCapacity;
+
+ #endregion
+
+ #region .ctors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected ProtocolPlugin() { }
+
+ #endregion
+
+ #region API overrides
+
+ ///
+ /// Gets the plugin interfaces.
+ ///
+ public override Guid[] PluginInterfaces
+ {
+ get { return new Guid[] { Virtuoso.Miranda.Plugins.Native.UUID.ProtocolUUID }; }
+ }
+
+ ///
+ /// Initializes the instance and creates essential protocol services.
+ ///
+ ///
+ internal override void LoadInternal(IntPtr pPluginLink)
+ {
+ ThisProtocol = new ManagedProtocol(Name, this.ProtocolType);
+ ThisProtocol.Register();
+
+ ProtocolStatus = StatusMode.Offline;
+
+ ServiceManager.CreateServiceFunction(ThisProtocol.GetProtoServiceName(Protocol.PS_GETNAME), PSGetName, this);
+ ServiceManager.CreateServiceFunction(ThisProtocol.GetProtoServiceName(Protocol.PS_GETSTATUS), PSGetStatus, this);
+ ServiceManager.CreateServiceFunction(ThisProtocol.GetProtoServiceName(Protocol.PS_SETSTATUS), PSSetStatus, this);
+ ServiceManager.CreateServiceFunction(ThisProtocol.GetProtoServiceName(Protocol.PS_LOADICON), PSLoadIcon, this);
+ ServiceManager.CreateServiceFunction(ThisProtocol.GetProtoServiceName(Protocol.PS_GETCAPS), PSGetCaps, this);
+ ServiceManager.CreateServiceFunction(ThisProtocol.GetProtoServiceName(Protocol.PSS_MESSAGE), PSSSendMessage, this);
+
+ base.LoadInternal(pPluginLink);
+ }
+
+ ///
+ /// Unloads the protocol.
+ ///
+ internal override void UnloadInternal()
+ {
+ base.UnloadInternal();
+ ThisProtocol.Unregister();
+ }
+
+ #endregion
+
+ #region Services
+
+ ///
+ /// Gets a human-readable name for the protocol.
+ ///
+ /// The number of characters in the buffer.
+ /// Buffer pointer.
+ /// Returns 0 on success, nonzero on failure.
+ private unsafe int PSGetName(UIntPtr capacity, IntPtr pBuffer)
+ {
+ if (namePtr == IntPtr.Zero)
+ {
+ byte[] nameBytes = Encoding.Default.GetBytes(Name ?? String.Empty);
+ NameCapacity = nameBytes.Length + 1;
+
+ namePtr = Marshal.AllocHGlobal(NameCapacity);
+ Marshal.Copy(nameBytes, 0, namePtr, nameBytes.Length);
+
+ *(((byte*)namePtr) + nameBytes.Length) = 0;
+ }
+
+ uint count = capacity.ToUInt32();
+
+ for (long i = 0; i < count && i < NameCapacity; i++)
+ *(byte*)(pBuffer.ToInt64() + i) = *(byte*)(namePtr.ToInt64() + i);
+
+ return 0;
+ }
+
+ ///
+ /// Gets the status mode that a protocol is currently in.
+ ///
+ /// Not used.
+ /// Not used.
+ /// Returns the status mode.
+ private int PSGetStatus(UIntPtr wParam, IntPtr lParam)
+ {
+ return (int)ProtocolStatus;
+ }
+
+ ///
+ /// Changes the protocol's status mode.
+ ///
+ /// New status values.
+ /// Not used.
+ /// Returns 0 on success, nonzero on failure.
+ ///
+ /// Will send an ack with:
+ /// type=ACKTYPE_STATUS, result=ACKRESULT_SUCCESS, hProcess=(HANDLE)previousMode, lParam=newMode.
+ ///
+ private int PSSetStatus(UIntPtr newStatusRaw, IntPtr lParam)
+ {
+ StatusMode prevStatus = ProtocolStatus;
+ StatusMode newStatus = (StatusMode)newStatusRaw;
+
+ ProtocolStatus = newStatus;
+ AckBroadcaster.BroadcastAck(ThisProtocol.NativeDescriptor.Name, AckType.Status, true, IntPtr.Zero, (IntPtr)(int)prevStatus, Translate.ToHandle(newStatusRaw));
+
+ return 0;
+ }
+
+ ///
+ /// Loads one of the protocol-specific icons
+ ///
+ /// Which icon (currently ignored).
+ /// Not used.
+ /// Icon handle (HICON).
+ private int PSLoadIcon(UIntPtr whichIcon, IntPtr lParam)
+ {
+ if (ProtocolIcon == null)
+ return SystemIcons.Application.Handle.ToInt32();
+ else
+ return ProtocolIcon.Handle.ToInt32();
+ }
+
+ ///
+ /// Gets the capability flags of the module.
+ ///
+ /// Flags category.
+ /// Not used.
+ ///
+ private int PSGetCaps(UIntPtr flagsNum, IntPtr lParam)
+ {
+ switch ((ProtocolFlagsKind)flagsNum)
+ {
+ case ProtocolFlagsKind.Capabilities:
+ return (int)SupportedCapabilities;
+ case ProtocolFlagsKind.StatusModes:
+ return (int)SupportedStatusModes;
+ case ProtocolFlagsKind.AwayStatusModes:
+ return (int)SupportedAwayStatusModes;
+ default:
+ return 0;
+ }
+ }
+
+ ///
+ /// Sends an instant message.
+ ///
+ /// Flags.
+ /// Contact-Chain-Send data pointer.
+ /// Returns a hProcess corresponding to the one in the ack event.
+ ///
+ /// Will send an ack when the message actually gets sent type=ACKTYPE_MESSAGE, result=success/failure, lParam=0.
+ ///
+ private unsafe int PSSSendMessage(UIntPtr flags, IntPtr pCcsData)
+ {
+ return SendMessage(new ContactChainData(pCcsData));
+ }
+
+ #endregion
+
+ #region Properties
+
+ private ManagedProtocol thisProtocol;
+ ///
+ /// Gets the managed protocol descriptor for this module.
+ ///
+ public ManagedProtocol ThisProtocol
+ {
+ get { return thisProtocol; }
+ internal set { thisProtocol = value; }
+ }
+
+ protected IntPtr NamePtr
+ {
+ get { return namePtr; }
+ }
+
+ #endregion
+
+ #region Abstracts
+
+ ///
+ /// Gets the protocol type.
+ ///
+ protected internal abstract ProtocolType ProtocolType { get; }
+
+ ///
+ /// Gets the protocol icon.
+ ///
+ protected internal abstract Icon ProtocolIcon { get; }
+
+ ///
+ /// Gets the protocol supported capabilities.
+ ///
+ protected internal abstract ProtocolCapabilities SupportedCapabilities { get; }
+
+ #endregion
+
+ #region Virtuals
+
+ ///
+ /// Gets the status modes the protocol supports an away message for.
+ ///
+ protected internal virtual ProtocolStatusModes SupportedAwayStatusModes { get { return (ProtocolStatusModes)0; } }
+
+ ///
+ /// Gets the protocol supported status modes.
+ ///
+ protected internal virtual ProtocolStatusModes SupportedStatusModes { get { return (ProtocolStatusModes)0; } }
+
+ ///
+ /// Gets or sets the protocol status.
+ ///
+ protected internal virtual StatusMode ProtocolStatus { get { return StatusMode.Offline; } set { } }
+
+ protected virtual int SendMessage(ContactChainData msgData) { return 0; }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Mini/StandalonePlugin.cs b/Hyphen/Plugins/Hyphen/Mini/StandalonePlugin.cs
new file mode 100644
index 0000000..dfbf146
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Mini/StandalonePlugin.cs
@@ -0,0 +1,164 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Hyphen.Mini.Custom;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Forms.Controls;
+using Virtuoso.Hyphen.Native;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Hyphen.Mini
+{
+ public abstract class StandalonePlugin : MirandaPlugin
+ {
+ #region Fields
+
+ internal static readonly Type PluginType = typeof(StandalonePlugin);
+
+ private readonly CustomApiExportDescriptorCollection customApiHandlers;
+ private Module module;
+
+ #endregion
+
+ #region .ctors
+
+ protected StandalonePlugin()
+ {
+ customApiHandlers = new CustomApiExportDescriptorCollection();
+ }
+
+ #endregion
+
+ #region Properties
+
+ protected internal Module Module
+ {
+ get { return this.module; }
+ internal set { this.module = value; }
+ }
+
+ public abstract string Copyright { get; }
+
+ public abstract string AuthorEmail { get; }
+
+ public virtual PluginFlags Flags { get { return PluginFlags.UnicodeAware; } }
+
+ public abstract int ReplacesDefaultModule { get; }
+
+ public abstract Guid UUID { get; }
+
+ public abstract Guid[] PluginInterfaces { get; }
+
+ internal CustomApiExportDescriptorCollection CustomApiHandlers
+ {
+ get { return customApiHandlers; }
+ }
+
+ private bool hasCustomPluginInterfaces = true;
+ internal bool HasCustomPluginInterfaces
+ {
+ get { return hasCustomPluginInterfaces; }
+ }
+
+ private bool hasCustomPluginInfo = true;
+ internal bool HasCustomPluginInfo
+ {
+ get { return hasCustomPluginInfo; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ /* Impl note: We use this to unload a plugin instead of the Unload export because there were some Access
+ * Violation exceptions before */
+ [EventHook(MirandaEvents.ME_SYSTEM_OKTOEXIT)]
+ internal int BeforeMirandaShutdownTriggerService(UIntPtr wParam, IntPtr lParam)
+ {
+ BeforeMirandaShutdown();
+ module.Unload();
+
+ return 0;
+ }
+
+ [CLSCompliant(false)]
+ protected internal virtual IntPtr MirandaPluginInfo(uint version, bool ex)
+ {
+ hasCustomPluginInfo = false;
+ PLUGININFO info = ex ? new PLUGININFOEX() : new PLUGININFO();
+
+ info.Size = Marshal.SizeOf(info.GetType());
+ info.Author = Author;
+ info.AuthorEmail = AuthorEmail;
+ info.Copyright = Copyright;
+ info.Description = Description;
+ info.HomePage = HomePage == null ? String.Empty : HomePage.ToString();
+ info.Flags = (byte)Flags;
+ info.ReplacesDefaultModule = ReplacesDefaultModule;
+ info.ShortName = Name;
+ info.Version = Translate.ToMirandaVersion(Version);
+ if (ex) ((PLUGININFOEX)info).UUID = UUID;
+
+ IntPtr pInfo = Marshal.AllocHGlobal(info.Size);
+ Marshal.StructureToPtr(info, pInfo, false);
+
+ return pInfo;
+ }
+
+ protected internal virtual IntPtr MirandaPluginInterfaces()
+ {
+ hasCustomPluginInterfaces = false;
+ Guid[] interfaces = PluginInterfaces;
+
+ int uuidSize = Marshal.SizeOf(typeof(Guid));
+ IntPtr pInterfaces = Marshal.AllocHGlobal((interfaces.Length + 1) * uuidSize);
+
+ byte[] uuidBytes = null;
+ long baseAddr = pInterfaces.ToInt64();
+
+ for (int i = 0; i < interfaces.Length; i++)
+ {
+ uuidBytes = interfaces[i].ToByteArray();
+ Marshal.Copy(uuidBytes, 0, new IntPtr(baseAddr + i * uuidSize), uuidBytes.Length);
+ }
+
+ // MIID_LAST
+ uuidBytes = Virtuoso.Miranda.Plugins.Native.UUID.Last.ToByteArray();
+ Marshal.Copy(uuidBytes, 0, new IntPtr(baseAddr + interfaces.Length * uuidSize), uuidBytes.Length);
+
+ return pInterfaces;
+ }
+
+ internal virtual void AfterModuleInitializationInternal() { AfterModuleInitialization(); }
+ protected virtual void AfterModuleInitialization() { }
+
+ internal virtual void LoadInternal(IntPtr pPluginLink) { Load(pPluginLink); }
+ protected virtual void Load(IntPtr pPluginLink) { }
+
+ internal virtual void UnloadInternal() { Unload(); }
+ protected virtual void Unload() { }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/MirandaPluginLink.cs b/Hyphen/Plugins/Hyphen/MirandaPluginLink.cs
new file mode 100644
index 0000000..feafcd2
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/MirandaPluginLink.cs
@@ -0,0 +1,77 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Hyphen.Native;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Hyphen
+{
+ internal sealed class MirandaPluginLink
+ {
+ #region Fields
+
+ private readonly NativePluginLink nativePluginLink;
+
+ private readonly IntPtr nativePluginLinkPtr;
+ public readonly CallServiceUnsafePrototype CallServiceUnsafe;
+
+ #endregion
+
+ #region .ctors
+
+ private MirandaPluginLink(IntPtr nativeLinkPtr)
+ {
+ if (nativeLinkPtr == IntPtr.Zero)
+ throw new ArgumentNullException("nativeLinkPtr");
+
+ this.nativePluginLinkPtr = nativeLinkPtr;
+ this.nativePluginLink = (NativePluginLink)Marshal.PtrToStructure(nativeLinkPtr, typeof(NativePluginLink));
+
+ this.CallServiceUnsafe = (CallServiceUnsafePrototype)Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(nativeLinkPtr, 9 * IntPtr.Size), typeof(CallServiceUnsafePrototype));
+ }
+
+ public static MirandaPluginLink FromPointer(IntPtr nativeLinkPtr)
+ {
+ return new MirandaPluginLink(nativeLinkPtr);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public IntPtr NativePluginLinkPtr
+ {
+ get
+ {
+ return nativePluginLinkPtr;
+ }
+ }
+
+ public NativePluginLink NativePluginLink
+ {
+ get
+ {
+ return nativePluginLink;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Native/Enums.cs b/Hyphen/Plugins/Hyphen/Native/Enums.cs
new file mode 100644
index 0000000..1c7118b
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Native/Enums.cs
@@ -0,0 +1,29 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Hyphen.Native
+{
+ [Flags]
+ public enum PluginFlags : byte
+ {
+ UnicodeAware = 1,
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Native/NativePluginLink.cs b/Hyphen/Plugins/Hyphen/Native/NativePluginLink.cs
new file mode 100644
index 0000000..7ec1064
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Native/NativePluginLink.cs
@@ -0,0 +1,65 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Hyphen.Native;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Security;
+
+namespace Virtuoso.Hyphen.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity]
+ internal sealed class NativePluginLink
+ {
+ public readonly CreateHookableEventPrototype CreateHookableEvent;
+ public readonly DestroyHookableEventPrototype DestroyHookableEvent;
+ public readonly NotifyEventHooksPrototype NotifyEventHooks;
+ public readonly HookEventPrototype HookEvent;
+ public readonly HookEventMessagePrototype HookEventMessage;
+ public readonly UnhookEventPrototype UnhookEvent;
+ public readonly CreateServiceFunctionPrototype CreateServiceFunction;
+ public readonly CreateTransientServiceFunctionPrototype CreateTransientServiceFunction;
+ public readonly DestroyServiceFunctionPrototype DestroyServiceFunction;
+ public readonly CallServicePrototype CallService;
+ public readonly ServiceExistsPrototype ServiceExists;
+ public readonly CallServiceSyncPrototype CallServiceSync;
+ public readonly CallFunctionAsyncPrototype CallFunctionAsync;
+ public readonly SetHookDefaultForHookableEventPrototype SetHookDefaultForHookableEvent;
+
+ //see modules.h for what all this stuff is
+ /*typedef struct {
+ HANDLE (*CreateHookableEvent)(const char *);
+ int (*DestroyHookableEvent)(HANDLE);
+ int (*NotifyEventHooks)(HANDLE,WPARAM,LPARAM);
+ HANDLE (*CreateEventHook)(const char *,MIRANDAHOOK);
+ HANDLE (*HookEventMessage)(const char *,HWND,UINT);
+ int (*RemoveEventHook)(HANDLE);
+ HANDLE (*CreateServiceFunction)(const char *,MIRANDASERVICE);
+ HANDLE (*CreateTransientServiceFunction)(const char *,MIRANDASERVICE);
+ int (*RemoveServiceFunction)(HANDLE);
+ int (*CallService)(const char *,WPARAM,LPARAM);
+ int (*ServiceExists)(const char *); //v0.1.0.1+
+ int (*CallServiceSync)(const char *,WPARAM,LPARAM); //v0.3.3+
+ int (*CallFunctionAsync) (void (__stdcall *)(void *), void *); //v0.3.4+
+ int (*SetHookDefaultForHookableEvent) (HANDLE, MIRANDAHOOK); // v0.3.4 (2004/09/15)
+ } PLUGINLINK;
+ */
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Native/PLUGININFO.cs b/Hyphen/Plugins/Hyphen/Native/PLUGININFO.cs
new file mode 100644
index 0000000..b02a9e9
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Native/PLUGININFO.cs
@@ -0,0 +1,54 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Hyphen.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal class PLUGININFO
+ {
+ public int Size;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string ShortName;
+
+ public UInt32 Version;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string Description;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string Author;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string AuthorEmail;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string Copyright;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string HomePage;
+
+ public byte Flags;
+
+ public int ReplacesDefaultModule;
+ }
+}
diff --git a/Hyphen/Plugins/Hyphen/Native/PLUGININFOEX.cs b/Hyphen/Plugins/Hyphen/Native/PLUGININFOEX.cs
new file mode 100644
index 0000000..f0a08c4
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Native/PLUGININFOEX.cs
@@ -0,0 +1,53 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Hyphen.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal sealed class PLUGININFOEX : PLUGININFO
+ {
+ public Guid UUID;
+
+ public PLUGININFOEX() { }
+
+ public PLUGININFOEX(Guid uuid)
+ {
+ this.UUID = uuid;
+ }
+ }
+}
+
+/*
+ typedef struct {
+ int cbSize;
+ char *shortName;
+ DWORD version;
+ char *description;
+ char *author;
+ char *authorEmail;
+ char *copyright;
+ char *homepage;
+ BYTE isTransient;
+ int replacesDefaultModule;
+ MUUID uuid;
+} PLUGININFOEX;
+ */
diff --git a/Hyphen/Plugins/Hyphen/Native/PluginLinkPrototypes.cs b/Hyphen/Plugins/Hyphen/Native/PluginLinkPrototypes.cs
new file mode 100644
index 0000000..cd7a8c8
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/Native/PluginLinkPrototypes.cs
@@ -0,0 +1,73 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins;
+
+namespace Virtuoso.Hyphen.Native
+{
+ [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+ public delegate void AsyncFunctionCall(IntPtr ptr);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate IntPtr CreateHookableEventPrototype([MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int DestroyHookableEventPrototype(IntPtr handle);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate int NotifyEventHooksPrototype(IntPtr handle, UIntPtr wParam, IntPtr lParam);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate IntPtr HookEventPrototype([MarshalAs(UnmanagedType.LPStr)] string name, Callback hook);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate IntPtr HookEventMessagePrototype([MarshalAs(UnmanagedType.LPStr)] string name, IntPtr hwnd, uint msg);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int UnhookEventPrototype(IntPtr handle);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate IntPtr CreateServiceFunctionPrototype([MarshalAs(UnmanagedType.LPStr)] string name, Callback service);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate IntPtr CreateTransientServiceFunctionPrototype([MarshalAs(UnmanagedType.LPStr)] string name, Callback service);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int DestroyServiceFunctionPrototype(IntPtr handle);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate int CallServicePrototype([MarshalAs(UnmanagedType.LPStr)] string name, UIntPtr wParam, IntPtr lParam);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public unsafe delegate int CallServiceUnsafePrototype([MarshalAs(UnmanagedType.LPStr)] string serviceName, void* wParam, void* lParam);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate int ServiceExistsPrototype([MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate int CallServiceSyncPrototype([MarshalAs(UnmanagedType.LPStr)] string name, UIntPtr wParam, IntPtr lParam);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate int CallFunctionAsyncPrototype(AsyncFunctionCall function, IntPtr ptr);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate int SetHookDefaultForHookableEventPrototype(IntPtr handle, Callback hook);
+}
diff --git a/Hyphen/Plugins/Hyphen/PluginSandbox.cs b/Hyphen/Plugins/Hyphen/PluginSandbox.cs
new file mode 100644
index 0000000..afbc739
--- /dev/null
+++ b/Hyphen/Plugins/Hyphen/PluginSandbox.cs
@@ -0,0 +1,119 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins;
+using System.Windows.Forms;
+using System.Reflection;
+using System.IO;
+using Virtuoso.Miranda.Plugins.Helpers;
+using Virtuoso.Hyphen.Configuration;
+
+namespace Virtuoso.Hyphen
+{
+ internal sealed class PluginsSandbox : Sandbox
+ {
+ #region Fields
+
+ private AssemblyProbe assemblyProbe;
+ public AssemblyProbe AssemblyProbe
+ {
+ get { return assemblyProbe; }
+ }
+
+ private PluginManagerBase pluginManager;
+ public PluginManagerBase PluginManager
+ {
+ get { return pluginManager; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public PluginsSandbox()
+ {
+ SetUpHostingAppDomain("HyphenPlugins");
+ }
+
+ protected override void InitializeAppDomainSetup(AppDomainSetup domainSetup)
+ {
+ base.InitializeAppDomainSetup(domainSetup);
+
+ domainSetup.ShadowCopyFiles = true.ToString();
+ domainSetup.ShadowCopyDirectories = MirandaEnvironment.ManagedPluginsFolderPath;
+ }
+
+ public void LoadAssemblyProbe()
+ {
+ AssemblyProbe customProbe = null;
+
+ try
+ {
+ string fusionAssemblyName = RuntimeConfiguration.Singleton.CustomFusionAssemblyName;
+ string assemblyProbeTypeName = RuntimeConfiguration.Singleton.CustomAssemblyProbeTypeName;
+
+ if (!String.IsNullOrEmpty(fusionAssemblyName) && !String.IsNullOrEmpty(assemblyProbeTypeName))
+ customProbe = InstantiateRemoteObject(fusionAssemblyName, assemblyProbeTypeName, null);
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(0, Loader.LogCategory, "Unable to load custom assembly probe (" + e.Message + "), defaulting to the built-in one.");
+ }
+
+ assemblyProbe = customProbe ?? InstantiateRemoteObject(GetType().Assembly.FullName, typeof(DefaultAssemblyProbe).FullName);
+ }
+
+ public void LoadPluginManager(FusionContext context)
+ {
+ PluginManagerBase customManager = null;
+
+ try
+ {
+ string fusionAssemblyName = RuntimeConfiguration.Singleton.CustomFusionAssemblyName;
+ string pluginManagerTypeName = RuntimeConfiguration.Singleton.CustomPluginManagerTypeName;
+
+ if (!String.IsNullOrEmpty(fusionAssemblyName) && !String.IsNullOrEmpty(pluginManagerTypeName))
+ customManager = InstantiateRemoteObject(fusionAssemblyName, pluginManagerTypeName, context);
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(0, Loader.LogCategory, "Unable to load custom plugin manager (" + e.Message + "), defaulting to the built-in one.");
+ }
+
+ pluginManager = customManager ?? InstantiateRemoteObject(GetType().Assembly.FullName, typeof(DefaultPluginManager).FullName, context);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public string HostingAppDomainPrivateBinPath
+ {
+ get
+ {
+ return HostingAppDomain.SetupInformation.PrivateBinPath;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/IConfigurablePlugin.cs b/Hyphen/Plugins/IConfigurablePlugin.cs
new file mode 100644
index 0000000..92a7c7d
--- /dev/null
+++ b/Hyphen/Plugins/IConfigurablePlugin.cs
@@ -0,0 +1,40 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Configuration.Forms.Controls;
+using Virtuoso.Miranda.Plugins.Configuration.Forms;
+using Virtuoso.Miranda.Plugins.Configuration;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ public interface IConfigurablePlugin
+ {
+ string Name { get; }
+ PluginConfiguration[] Configuration { get; }
+
+ void PopulateConfiguration(CategoryCollection categories);
+ void ResetConfiguration();
+ void ReloadConfiguration();
+ }
+
+ ///
+ /// Represents a marker interface for classes wrapping Hyphen runtime configuration.
+ ///
+ internal interface IInternalConfigurator : IConfigurablePlugin { }
+}
diff --git a/Hyphen/Plugins/IConfigurablePluginBase.cs b/Hyphen/Plugins/IConfigurablePluginBase.cs
new file mode 100644
index 0000000..f2b3bc9
--- /dev/null
+++ b/Hyphen/Plugins/IConfigurablePluginBase.cs
@@ -0,0 +1,27 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ internal interface IConfigurablePluginBase : IConfigurablePlugin where TConfiguration : PluginConfiguration
+ {
+ TConfiguration PluginConfiguration { get; }
+ }
+}
diff --git a/Hyphen/Plugins/IDescriptor.cs b/Hyphen/Plugins/IDescriptor.cs
new file mode 100644
index 0000000..f001d1f
--- /dev/null
+++ b/Hyphen/Plugins/IDescriptor.cs
@@ -0,0 +1,25 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ internal interface IDescriptor { }
+}
diff --git a/Hyphen/Plugins/IExceptionDumpController.cs b/Hyphen/Plugins/IExceptionDumpController.cs
new file mode 100644
index 0000000..6d7fa17
--- /dev/null
+++ b/Hyphen/Plugins/IExceptionDumpController.cs
@@ -0,0 +1,28 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ public interface IExceptionDumpController
+ {
+ void DumpException(Exception e, StringBuilder dump);
+ }
+}
diff --git a/Hyphen/Plugins/IExceptionHandler.cs b/Hyphen/Plugins/IExceptionHandler.cs
new file mode 100644
index 0000000..f11dac0
--- /dev/null
+++ b/Hyphen/Plugins/IExceptionHandler.cs
@@ -0,0 +1,28 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ public interface IExceptionHandler
+ {
+ void HandleException(Exception e, PluginDescriptor descriptor);
+ }
+}
diff --git a/Hyphen/Plugins/IExceptionReporter.cs b/Hyphen/Plugins/IExceptionReporter.cs
new file mode 100644
index 0000000..727ff87
--- /dev/null
+++ b/Hyphen/Plugins/IExceptionReporter.cs
@@ -0,0 +1,28 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ public interface IExceptionReporter
+ {
+ void ReportException(Exception e);
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/CallbackWrapper.cs b/Hyphen/Plugins/Infrastructure/CallbackWrapper.cs
new file mode 100644
index 0000000..2410c91
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/CallbackWrapper.cs
@@ -0,0 +1,63 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal class CallbackWrapper
+ {
+ #region Properties
+
+ private string serviceName;
+ public string ServiceName
+ {
+ get { return serviceName; }
+ protected set { serviceName = value; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ private CallbackWrapper(string serviceName)
+ {
+ if (String.IsNullOrEmpty(serviceName))
+ throw new ArgumentNullException("serviceName");
+
+ this.serviceName = serviceName;
+ }
+
+ public static Callback Create(string serviceName)
+ {
+ return new Callback(new CallbackWrapper(serviceName).Callback);
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected int Callback(UIntPtr wParam, IntPtr lParam)
+ {
+ return MirandaContext.Current.CallService(ServiceName, wParam, lParam);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ConfigurationException.cs b/Hyphen/Plugins/Infrastructure/ConfigurationException.cs
new file mode 100644
index 0000000..e3a7d56
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ConfigurationException.cs
@@ -0,0 +1,33 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.Serialization;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public class ConfigurationException : Exception
+ {
+ public ConfigurationException() { }
+ public ConfigurationException(string message) : base(message) { }
+ public ConfigurationException(string message, Exception inner) : base(message, inner) { }
+ protected ConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ConfigurationOptionsAttribute.cs b/Hyphen/Plugins/Infrastructure/ConfigurationOptionsAttribute.cs
new file mode 100644
index 0000000..8797416
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ConfigurationOptionsAttribute.cs
@@ -0,0 +1,102 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Configuration;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ public sealed class ConfigurationOptionsAttribute : Attribute
+ {
+ #region .ctors
+
+ internal ConfigurationOptionsAttribute() { }
+
+ public ConfigurationOptionsAttribute(string configurationVersion) : this(configurationVersion, false, true) { }
+
+ [Obsolete("Will be removed in future. Use named arguments instead.", true)]
+ public ConfigurationOptionsAttribute(bool encrypt, bool profileBound) : this(null, encrypt, profileBound) { }
+
+ [Obsolete("Will be removed in future. Use named arguments instead.", false)]
+ public ConfigurationOptionsAttribute(string configurationVersion, bool encrypt, bool profileBound)
+ {
+ if (!String.IsNullOrEmpty(configurationVersion))
+ this.version = new Version(configurationVersion);
+
+ this.encrypt = encrypt;
+ this.profileBound = profileBound;
+ }
+
+ #endregion
+
+ #region Properties
+
+ private readonly Version version;
+ public Version Version
+ {
+ get { return version; }
+ }
+
+ private bool profileBound;
+ public bool ProfileBound
+ {
+ get { return profileBound; }
+ set { profileBound = value; }
+ }
+
+ private bool encrypt;
+ public bool Encrypt
+ {
+ get { return encrypt; }
+ set { encrypt = value; }
+ }
+
+ private Type storage;
+ public Type Storage
+ {
+ get { return storage; }
+ set { storage = value; }
+ }
+
+ private Type encryption;
+ public Type Encryption
+ {
+ get { return encryption; }
+ set { encryption = value; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal ConfigurationOptionsAttribute Finalize()
+ {
+ if (storage == null)
+ storage = typeof(IsolatedStorage);
+
+ if (encrypt && encryption == null)
+ encryption = typeof(WindowsEncryption);
+
+ return this;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContactInfo.cs b/Hyphen/Plugins/Infrastructure/ContactInfo.cs
new file mode 100644
index 0000000..854f7ab
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContactInfo.cs
@@ -0,0 +1,753 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Runtime.Serialization;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public class ContactInfo : MirandaItem, ISerializable
+ {
+ #region Enums
+
+ [Flags]
+ private enum ContactDisplayNameOptions : int
+ {
+ ///
+ /// Will return char*, as usual.
+ ///
+ Ansi = 0,
+
+ ///
+ /// Will never return the user's custom name.
+ ///
+ NoMyHandle = 1,
+
+ ///
+ /// Will return TCHAR* instead of char*.
+ ///
+ Unicode = 2,
+
+ ///
+ /// Will not use the cache.
+ ///
+ NoCache = 4
+ }
+
+ #endregion
+
+ #region Contants
+
+ private const string MS_DB_CONTACT_IS = "DB/Contact/Is",
+ MS_PROTO_GETCONTACTBASEPROTO = "Proto/GetContactBaseProto",
+ MS_DB_CONTACT_WRITESETTING = "DB/Contact/WriteSetting",
+ MS_DB_CONTACT_GETSETTING = "DB/Contact/GetSetting",
+ MS_DB_CONTACT_GETSETTINGSTATIC = "DB/Contact/GetSettingStatic",
+ MS_DB_CONTACT_DELETESETTING = "DB/Contact/DeleteSetting",
+
+ MS_DB_CONTACT_ADD = "DB/Contact/Add",
+ MS_DB_CONTACT_DELETE = "DB/Contact/Delete",
+
+ MS_CLIST_GETCONTACTDISPLAYNAME = "CList/GetContactDisplayName",
+
+ SETTING_STATUS = "Status",
+
+ MS_MSG_SENDMESSAGE = "SRMsg/SendCommand",
+ MS_MSG_SENDMESSAGE_2 = "SRMsg/LaunchMessageWindow";
+
+ #endregion
+
+ #region Fields
+
+ private static readonly object SyncObject = new object();
+ private static readonly ContactInfo meNeutral = new ContactInfo();
+
+ private readonly Protocol owningModule;
+
+ private object value;
+ private ContactInfoPropertyType valueType;
+
+ #endregion
+
+ #region .ctors
+
+ private ContactInfo()
+ : base(IntPtr.Zero, ItemType.Contact)
+ {
+ this.owningModule = Protocol.UnknownProtocol;
+ }
+
+ protected ContactInfo(SerializationInfo info, StreamingContext context)
+ : this((IntPtr)info.GetInt64("MirandaHandle"))
+ {
+ }
+
+ [CLSCompliant(false), Obsolete("Will be removed in the future, use FromHandle(UIntPtr) instead.")]
+ public ContactInfo(UIntPtr contactHandle) : this(Translate.ToHandle(contactHandle)) { }
+
+ [Obsolete("Will be removed in the future, use FromHandle(IntPtr) instead.")]
+ public ContactInfo(IntPtr contactHandle)
+ : base(contactHandle, ItemType.Contact)
+ {
+ MirandaContext context = MirandaContext.Current;
+
+ if (contactHandle != IntPtr.Zero && context.CallService(MS_DB_CONTACT_IS, contactHandle, IntPtr.Zero) == 0)
+ throw new ArgumentException("Contact not found in Miranda database.");
+
+ IntPtr protoNamePtr = GetModuleNamePtr(contactHandle);
+
+ if (protoNamePtr != IntPtr.Zero)
+ {
+ string protoName = Translate.ToString(protoNamePtr, StringEncoding.Ansi);
+ MirandaContext.Current.Protocols.TryGetValue(protoName, out owningModule);
+ }
+
+ if (owningModule == null)
+ {
+ owningModule = Protocol.UnknownProtocol;
+
+ if (contactHandle != IntPtr.Zero)
+ Log.DebuggerWrite(5, Loader.LogCategory, "Unable to obtain contact's protocol");
+ }
+
+ this.MirandaHandle = contactHandle;
+ }
+
+ public static ContactInfo FromHandle(IntPtr contactHandle)
+ {
+ return new ContactInfo(contactHandle);
+ }
+
+ [CLSCompliant(false)]
+ public static ContactInfo FromHandle(UIntPtr contactHandle)
+ {
+ return new ContactInfo(contactHandle);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public ContactInfoPropertyType PropertyType
+ {
+ get { return valueType; }
+ }
+
+ public object PropertyValue
+ {
+ get { return this.value; }
+ }
+
+ public Protocol OwningModule
+ {
+ get { return owningModule; }
+ }
+
+ public bool IsSelf
+ {
+ get
+ {
+ return (MirandaHandle == IntPtr.Zero);
+ }
+ }
+
+ public static ContactInfo MeNeutral
+ {
+ get
+ {
+ return meNeutral;
+ }
+ }
+
+ public StatusMode? Status
+ {
+ get
+ {
+ if (String.IsNullOrEmpty(OwningModule.Name))
+ return null;
+
+ object obj = ReadSetting(SETTING_STATUS, DatabaseSettingType.UInt16);
+
+ if (obj != null)
+ return (StatusMode)(UInt16)obj; //(StatusMode)Enum.Parse(typeof(StatusMode), (Int16) obj);
+ else
+ return null;
+ }
+ }
+
+ public string DisplayName
+ {
+ get
+ {
+ return GetDisplayName(MirandaHandle);
+ }
+ }
+
+ private object uniqueID;
+ public object UniqueID
+ {
+ get
+ {
+ lock (SyncObject)
+ {
+ if (uniqueID == null)
+ {
+ ContactInfoPropertyType type;
+ GetUniqueID(MirandaHandle, out uniqueID, out type);
+ }
+
+ return uniqueID;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ #region Settings/Write
+
+ public bool WriteSetting(string name, object value, DatabaseSettingType saveAs)
+ {
+ return WriteSetting(name, OwningModule, value, saveAs);
+ }
+
+ public bool WriteSetting(string name, ISettingOwner owner, object value, DatabaseSettingType saveAs)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ return WriteSetting(name, owner.Name, value, saveAs);
+ }
+
+ public bool WriteSettingAsBlob(string name, ISettingOwner owner, byte[] blob)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ return WriteSettingAsBlob(name, owner.Name, blob);
+ }
+
+ public bool WriteSettingAsBlob(string name, string owner, byte[] blob)
+ {
+ if (blob == null)
+ throw new ArgumentNullException("blob");
+
+ throw new NotImplementedException();
+
+ //return WriteSetting(name, owner, blob, DatabaseSettingType.Blob);
+ }
+
+ public unsafe bool WriteSetting(string name, string owner, object value, DatabaseSettingType saveAs)
+ {
+ if (String.IsNullOrEmpty(owner))
+ throw new ArgumentNullException("owner");
+
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ UnmanagedStringHandle valueHandle = UnmanagedStringHandle.Empty;
+ UnmanagedStringHandle nameHandle = Translate.ToHandle(name, StringEncoding.Ansi);
+ UnmanagedStringHandle moduleNameHandle = Translate.ToHandle(owner, StringEncoding.Ansi);
+
+ IntPtr blobPtr = IntPtr.Zero;
+
+ try
+ {
+ DBCONTACTWRITESETTING dbSetting = new DBCONTACTWRITESETTING();
+ dbSetting.Name = nameHandle;
+ dbSetting.Module = moduleNameHandle;
+ dbSetting.Value = new DBVARIANT();
+ dbSetting.Value.Type = (byte)saveAs;
+
+ switch (saveAs)
+ {
+ case DatabaseSettingType.AsciiString:
+ valueHandle = new UnmanagedStringHandle(value.ToString(), StringEncoding.Ansi);
+ dbSetting.Value.Text.TextPtr = valueHandle.IntPtr;
+ break;
+ case DatabaseSettingType.UnicodeString:
+ case DatabaseSettingType.UTF8String:
+ valueHandle = new UnmanagedStringHandle(value.ToString(), StringEncoding.Unicode);
+ dbSetting.Value.Text.TextPtr = valueHandle.IntPtr;
+ break;
+
+ case DatabaseSettingType.Byte:
+ dbSetting.Value.Primitives.Byte = Convert.ToByte(value);
+ break;
+ case DatabaseSettingType.UInt16:
+ dbSetting.Value.Primitives.Word = Convert.ToUInt16(value);
+ break;
+ case DatabaseSettingType.UInt32:
+ dbSetting.Value.Primitives.DWord = Convert.ToUInt32(value);
+ break;
+ case DatabaseSettingType.Blob:
+ throw new NotImplementedException();
+
+ /*byte[] blob = value as byte[];
+
+ if (blob == null)
+ throw new ArgumentException("value");
+
+ blobPtr = Marshal.AllocHGlobal(blob.Length);
+ Marshal.Copy(blob, 0, blobPtr, blob.Length);
+
+ dbSetting.Value.Blob.BlobPtr = blobPtr;
+ dbSetting.Value.Blob.Size = (UInt16)blob.Length;
+ break;*/
+ default:
+ throw new ArgumentOutOfRangeException("saveAs");
+ }
+
+ bool result = MirandaContext.Current.CallServiceUnsafe(MS_DB_CONTACT_WRITESETTING, MirandaHandle.ToPointer(), &dbSetting) == 0;
+ Debug.Assert(result);
+
+ return result;
+ }
+ catch (FormatException fE)
+ {
+ throw new ArgumentOutOfRangeException("value", fE);
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(TextResources.ExceptionMsg_ErrorWhileCallingMirandaService, e);
+ }
+ finally
+ {
+ valueHandle.Free();
+ nameHandle.Free();
+ moduleNameHandle.Free();
+
+ if (blobPtr != IntPtr.Zero)
+ Marshal.FreeHGlobal(blobPtr);
+ }
+ }
+
+ #endregion
+
+ #region Settings/Read
+
+ public object ReadSetting(string name, DatabaseSettingType readAs)
+ {
+ return ReadSetting(MirandaHandle, name, readAs);
+ }
+
+ public static object ReadSetting(IntPtr contactHandle, string name, DatabaseSettingType readAs)
+ {
+ IntPtr pOwnerName = GetModuleNamePtr(contactHandle);
+
+ if (pOwnerName == IntPtr.Zero)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_OwnerUnknown);
+
+ return ReadSetting(contactHandle, name, Marshal.PtrToStringAnsi(pOwnerName), readAs);
+ }
+
+ public object ReadSetting(string name, ISettingOwner owner, DatabaseSettingType readAs)
+ {
+ return ReadSetting(MirandaHandle, name, owner, readAs);
+ }
+
+ public static object ReadSetting(IntPtr contactHandle, string name, ISettingOwner owner, DatabaseSettingType readAs)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ return ReadSetting(contactHandle, name, owner.Name, readAs);
+ }
+
+ public object ReadSetting(string name, string owner, DatabaseSettingType readAs)
+ {
+ return ReadSetting(MirandaHandle, name, owner, readAs);
+ }
+
+ public static object ReadSetting(IntPtr contactHandle, string name, string owner, DatabaseSettingType readAs)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ InteropBuffer buffer = InteropBufferPool.AcquireBuffer();
+
+ try
+ {
+ DBCONTACTGETSETTING dbSetting = new DBCONTACTGETSETTING();
+ dbSetting.Name = name;
+
+ DBVARIANT dbVariant = new DBVARIANT();
+ dbVariant.Type = (byte)readAs;
+
+ buffer.Lock();
+
+ if (readAs != DatabaseSettingType.Blob)
+ {
+ dbVariant.Text.TextPtr = buffer.IntPtr;
+ dbVariant.Text.TextBufferSize = (ushort)buffer.Size;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ //dbVariant.Blob.BlobPtr = buffer.IntPtr;
+ }
+
+ return ReadSettingInternal(contactHandle, owner, ref dbSetting, ref dbVariant);
+
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(TextResources.ExceptionMsg_ErrorWhileCallingMirandaService, e);
+ }
+ finally
+ {
+ buffer.Unlock();
+ InteropBufferPool.ReleaseBuffer(buffer);
+ }
+ }
+
+ private static object ReadSettingInternal(IntPtr contactHandle, string owner, ref DBCONTACTGETSETTING setting, ref DBVARIANT variant)
+ {
+ UnmanagedStructHandle dbSettingHandle = UnmanagedStructHandle.Empty;
+ UnmanagedStructHandle dbVariantHandle = UnmanagedStructHandle.Empty;
+
+ try
+ {
+ dbVariantHandle = new UnmanagedStructHandle(ref variant);
+
+ setting.Module = owner;
+ setting.DbVariantPtr = dbVariantHandle.IntPtr;
+
+ dbSettingHandle = new UnmanagedStructHandle(ref setting);
+
+ int result = MirandaContext.Current.CallService(MS_DB_CONTACT_GETSETTINGSTATIC, contactHandle, dbSettingHandle.IntPtr);
+ Debug.Assert(result != 2, "Deleted setting encountered");
+
+ if (result != 0)
+ return null;
+
+ dbVariantHandle.MarshalBack(out variant);
+ return Translate.ValueFromVariant(ref variant);
+ }
+ finally
+ {
+ dbVariantHandle.Free();
+ dbSettingHandle.Free();
+ }
+ }
+
+ #endregion
+
+ #region Settings/Delete
+
+ public bool DeleteSetting(string name, ISettingOwner owner)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ return DeleteSetting(name, owner.Name);
+ }
+
+ public bool DeleteSetting(string name, string owner)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ if (String.IsNullOrEmpty(owner))
+ throw new ArgumentNullException("owner");
+
+ DBCONTACTGETSETTING dbGetSetting = new DBCONTACTGETSETTING();
+ dbGetSetting.Name = name;
+ dbGetSetting.Module = owner;
+
+ UnmanagedStructHandle dbSettingHandle = new UnmanagedStructHandle(ref dbGetSetting);
+
+ try
+ {
+ return MirandaContext.Current.CallService(MS_DB_CONTACT_DELETESETTING, MirandaHandle, dbSettingHandle.IntPtr) == 0;
+ }
+ finally
+ {
+ dbSettingHandle.Free();
+ }
+ }
+
+ #endregion
+
+ #region Converters
+
+ public static TId[] GetUniqueIDs(params ContactInfo[] contacts)
+ {
+ if (contacts == null)
+ throw new ArgumentNullException("contacts");
+
+ if (contacts.Length == 0)
+ return new TId[0];
+
+ return Array.ConvertAll(contacts, delegate(ContactInfo contact)
+ {
+ if (contact != null)
+ return contact.UniqueIdAs();
+ else
+ return default(TId);
+ });
+ }
+
+ public static string[] GetDisplayNames(params string[] uuids)
+ {
+ if (uuids == null)
+ throw new ArgumentNullException("uuids");
+
+ if (uuids.Length == 0)
+ return new string[0];
+
+ return Array.ConvertAll(uuids, delegate(string uuid)
+ {
+ if (uuid == null)
+ return null;
+
+ ContactInfo contact = MirandaContext.Current.MirandaDatabase.FindContact(uuid);
+
+ if (contact != null)
+ return contact.DisplayName;
+ else
+ return null;
+ });
+ }
+
+ #endregion
+
+ #region Helpers
+
+ private static IntPtr GetModuleNamePtr(IntPtr contactHandle)
+ {
+ return (IntPtr)MirandaContext.Current.CallService(MS_PROTO_GETCONTACTBASEPROTO, contactHandle, IntPtr.Zero);
+ }
+
+ public static string GetDisplayName(IntPtr contactHandle)
+ {
+ ContactDisplayNameOptions options = ContactDisplayNameOptions.Unicode;
+
+ IntPtr pName = (IntPtr)MirandaContext.Current.CallService(MS_CLIST_GETCONTACTDISPLAYNAME, contactHandle, (IntPtr)options);
+
+ if (pName == IntPtr.Zero)
+ return null;
+ else
+ return Translate.ToString(pName, StringEncoding.Unicode);
+ }
+
+ public static bool GetUniqueID(IntPtr contactHandle, out object uuid, out ContactInfoPropertyType uuidType)
+ {
+ return GetProperty(contactHandle, ContactInfoProperty.UniqueID, out uuid, out uuidType);
+ }
+
+ public static bool GetProperty(IntPtr contactHandle, ContactInfoProperty property, out object value, out ContactInfoPropertyType valueType)
+ {
+ CONTACTINFO contactInfo = new CONTACTINFO(contactHandle, GetModuleNamePtr(contactHandle));
+ contactInfo.Flag = (byte)((byte)property | (byte)ContactInfoPropertyFlags.Unicode);
+
+ unsafe
+ {
+ int result = MirandaContext.Current.CallServiceUnsafe(MirandaServices.MS_CONTACT_GETCONTACTINFO, null, &contactInfo);
+
+ if (result != 0)
+ {
+ value = null;
+ valueType = ContactInfoPropertyType.Unknown;
+
+ return false;
+ }
+ }
+
+ switch (valueType = (ContactInfoPropertyType)contactInfo.Type)
+ {
+ case ContactInfoPropertyType.Byte:
+ value = Convert.ToByte(contactInfo.Value.ToInt32());
+ break;
+ case ContactInfoPropertyType.String:
+ value = Translate.ToString(contactInfo.Value, StringEncoding.Unicode);
+ break;
+ case ContactInfoPropertyType.UInt16:
+ value = Convert.ToUInt16(contactInfo.Value.ToInt32());
+ break;
+ case ContactInfoPropertyType.UInt32:
+ value = contactInfo.Value.ToInt32();
+ break;
+ default:
+ value = null;
+ valueType = ContactInfoPropertyType.Unknown;
+ return false;
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ #region ISerializable Members
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("MirandaHandle", MirandaHandle.ToInt64());
+ }
+
+ #endregion
+
+ public TId UniqueIdAs()
+ {
+ object uuid = UniqueID;
+ return uuid == null ? default(TId) : (TId)Convert.ChangeType(uuid, typeof(TId));
+ }
+
+ public override string ToString()
+ {
+ return !Protocol.UnknownProtocol.Equals(OwningModule) ?
+ String.Format("{0} ({1})", DisplayName, OwningModule.Name) : DisplayName;
+ }
+
+ public static bool operator ==(ContactInfo first, ContactInfo second)
+ {
+ if (object.ReferenceEquals(first, second))
+ return true;
+ else if (object.ReferenceEquals(first, null))
+ return false;
+ else
+ return first.Equals(second);
+ }
+
+ public static bool operator !=(ContactInfo first, ContactInfo second)
+ {
+ return !(first == second);
+ }
+
+ public override bool Equals(object obj)
+ {
+ ContactInfo other = obj as ContactInfo;
+
+ if (object.ReferenceEquals(other, null))
+ return false;
+ else
+ return GetHashCode() == other.GetHashCode();
+ }
+
+ public override int GetHashCode()
+ {
+ return MirandaHandle.ToInt32();
+ }
+
+ public static ContactInfo CreateContact()
+ {
+ IntPtr hContact = (IntPtr)MirandaContext.Current.CallService(MS_DB_CONTACT_ADD);
+
+ if (hContact == IntPtr.Zero)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_CONTACT_ADD, hContact.ToString()));
+
+ return ContactInfo.FromHandle(hContact);
+ }
+
+ public bool Delete()
+ {
+ if (IsSelf)
+ throw new InvalidOperationException();
+
+ return (0 == MirandaContext.Current.CallService(MS_DB_CONTACT_DELETE, MirandaHandle, IntPtr.Zero));
+ }
+
+ public bool GetProperty(ContactInfoProperty property, StringEncoding stringEncoding)
+ {
+ return GetProperty(MirandaHandle, property, out value, out valueType);
+ }
+
+ public int CallContactService(string serviceName)
+ {
+ return CallContactService(serviceName, UIntPtr.Zero, IntPtr.Zero);
+ }
+
+ [CLSCompliant(false)]
+ public int CallContactService(string serviceName, UIntPtr wParam, IntPtr lParam)
+ {
+ if (serviceName == null)
+ throw new ArgumentNullException("serviceName");
+
+ CCSDATA ccsData = new CCSDATA(this, serviceName);
+ ccsData.WParam = wParam;
+ ccsData.LParam = lParam;
+
+ try
+ {
+ unsafe
+ {
+ return MirandaContext.Current.CallServiceUnsafe(MirandaServices.MS_PROTO_CALLCONTACTSERVICE, null, &ccsData);
+ }
+ }
+ finally
+ {
+ ccsData.Free();
+ }
+ }
+
+ public void SendMessage(string message)
+ {
+ if (message == null)
+ throw new ArgumentNullException("message");
+
+ UnmanagedStringHandle nativeHandle = UnmanagedStringHandle.Empty;
+
+ try
+ {
+ nativeHandle = new UnmanagedStringHandle(message, StringEncoding.Ansi);
+ CallContactService(MirandaServices.PS_MESSAGE, UIntPtr.Zero, nativeHandle.IntPtr);
+ }
+ finally
+ {
+ nativeHandle.Free();
+ }
+ }
+
+ public void OpenMessageWindow()
+ {
+ OpenMessageWindow(null);
+ }
+
+ public void OpenMessageWindow(string message)
+ {
+ UnmanagedStringHandle messageHandle = UnmanagedStringHandle.Empty;
+
+ try
+ {
+ if (!String.IsNullOrEmpty(message))
+ messageHandle = new UnmanagedStringHandle(message, StringEncoding.Ansi);
+
+ int result = (int)CallbackResult.Success;
+
+ if (result != MirandaContext.Current.CallService(MS_MSG_SENDMESSAGE, MirandaHandle, messageHandle.IntPtr))
+ if ((int)CallbackResult.Success != (result = MirandaContext.Current.CallService(MS_MSG_SENDMESSAGE_2, MirandaHandle, messageHandle.IntPtr)))
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_MSG_SENDMESSAGE, result.ToString()));
+ }
+ finally
+ {
+ messageHandle.Free();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContactInfoProperty.cs b/Hyphen/Plugins/Infrastructure/ContactInfoProperty.cs
new file mode 100644
index 0000000..dc64d0e
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContactInfoProperty.cs
@@ -0,0 +1,49 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public enum ContactInfoProperty : byte
+ {
+ // Types of information you can retreive by setting the dwFlag in CONTACTINFO
+ FirstName = 1, // returns first eventName (string)
+ LastName = 2, // returns last eventName (string)
+ Nick = 3, // returns nick eventName (string)
+ CustomNick = 4, // returns custom nick name, clist name (string)
+ Email = 5, // returns email (string)
+ City = 6, // returns city (string)
+ State = 7, // returns state (string)
+ Country = 8, // returns country (string)
+ Phone = 9, // returns phone (string)
+ HomePage = 10, // returns homepage (string)
+ About = 11, // returns about info (string)
+ Gender = 12, // returns gender (byte,'M','F' character)
+ Age = 13, // returns age (byte, 0==unspecified)
+ FirstAndLastName = 14, // returns first eventName + last eventName (string)
+ UniqueID = 15, // returns uniqueid, protocol username (must check type for type of return)
+ }
+
+ [Flags]
+ internal enum ContactInfoPropertyFlags : byte
+ {
+ Unicode = 0x80,
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContactList.cs b/Hyphen/Plugins/Infrastructure/ContactList.cs
new file mode 100644
index 0000000..410c882
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContactList.cs
@@ -0,0 +1,503 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Drawing;
+using Virtuoso.Hyphen.Mini;
+using System.Windows.Forms;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public sealed class ContactList : EventPublisher
+ {
+ #region Constants
+
+ private const string ME_CLC_SHOWINFOTIP = "CLC/ShowInfoTip",
+ ME_CLC_HIDEINFOTIP = "CLC/HideInfoTip",
+ MS_CLC_SETINFOTIPHOVERTIME = "CLC/SetInfoTipHoverTime",
+ MS_CLC_GETINFOTIPHOVERTIME = "CLC/GetInfoTipHoverTime",
+ ME_CLIST_STATUSMODECHANGE = "CList/StatusModeChange",
+ ME_CLIST_PREBUILDCONTACTMENU = "CList/PreBuildContactMenu",
+ ME_CLIST_DOUBLECLICKED = "CList/DoubleClicked";
+
+ private const string MS_CLIST_ADDEVENT = "CList/AddEvent",
+ MS_CLIST_REMOVEEVENT = "Clist/RemoveEvent";
+
+ #endregion
+
+ #region Fields
+
+ private static readonly object SyncObject = new object();
+
+ private EventHandler> InfoTipShowEventHandler,
+ InfoTipHideEventHandler;
+
+ private EventHandler ProtocolStatusChangeEventHandler;
+ private EventHandler> ContactMenuShowingEventHandler;
+ private MirandaEventHandler> ContactDoubleClickedEventHandler;
+
+ private bool ContactSelectionTrackingEnabled;
+ private ContactInfo selectedContact;
+
+ private static readonly Random Random = new Random();
+
+ #endregion
+
+ #region .ctors
+
+ internal ContactList() { }
+
+ #endregion
+
+ #region Properties
+
+ public ContactInfo SelectedContact
+ {
+ get
+ {
+ return selectedContact;
+ }
+ }
+
+ public byte? Transparency
+ {
+ get
+ {
+ object alpha = ContactInfo.MeNeutral.ReadSetting("Alpha", "CList", DatabaseSettingType.Byte);
+
+ if (alpha != null)
+ return (byte)alpha;
+ else
+ return null;
+ }
+ set
+ {
+ ContactInfo.MeNeutral.WriteSetting("Alpha", "CList", value.GetValueOrDefault(byte.MaxValue), DatabaseSettingType.Byte);
+ }
+ }
+
+ public bool TransparencyEnabled
+ {
+ get
+ {
+ object enabled = ContactInfo.MeNeutral.ReadSetting("Transparent", "CList", DatabaseSettingType.Byte);
+
+ if (enabled != null)
+ return Convert.ToBoolean((byte)enabled);
+ else
+ return false;
+ }
+ set
+ {
+ ContactInfo.MeNeutral.WriteSetting("Transparent", "CList", Convert.ToByte(value), DatabaseSettingType.Byte);
+ }
+ }
+
+ #endregion
+
+ #region Events
+
+ private void FireInfoTipEvent(EventHandler> e, IntPtr lParam)
+ {
+ if (e == null)
+ return;
+
+ ContactListInfoTip infoTip = (ContactListInfoTip)Marshal.PtrToStructure(lParam, typeof(ContactListInfoTip));
+ ContactListEventArgs eArgs = new ContactListEventArgs(infoTip);
+
+ e(this, eArgs);
+ }
+
+ public event EventHandler> InfoTipShow
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>>(ref InfoTipShowEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_CLC_SHOWINFOTIP,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ FireInfoTipEvent(InfoTipShowEventHandler, lParam);
+ return (int)CallbackResult.Success;
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>>(ref InfoTipShowEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_CLC_SHOWINFOTIP, this.InfoTipShowEventHandler);
+ }
+ }
+
+ public event EventHandler> InfoTipHide
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>>(ref InfoTipHideEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_CLC_HIDEINFOTIP,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ FireInfoTipEvent(InfoTipHideEventHandler, lParam);
+ return (int)CallbackResult.Success;
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>>(ref InfoTipHideEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_CLC_HIDEINFOTIP, this.InfoTipHideEventHandler);
+ }
+ }
+
+ public event EventHandler ProtocolStatusChange
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref ProtocolStatusChangeEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_CLIST_STATUSMODECHANGE,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ string protocolName = lParam == IntPtr.Zero ? null : Translate.ToString(lParam, StringEncoding.Ansi);
+ Protocol protocol = null;
+
+ if (!String.IsNullOrEmpty(protocolName))
+ protocol = new Protocol(protocolName, ProtocolType.Protocol);
+
+ ProtocolStatusChangeEventArgs eArgs = new ProtocolStatusChangeEventArgs(protocol, Translate.ToStatus(wParam));
+
+ if (ProtocolStatusChangeEventHandler != null)
+ ProtocolStatusChangeEventHandler(this, eArgs);
+
+ return (int)CallbackResult.Success;
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref ProtocolStatusChangeEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_CLIST_STATUSMODECHANGE, ProtocolStatusChangeEventHandler);
+ }
+ }
+
+ public event EventHandler> ContactMenuShowing
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>>(ref ContactMenuShowingEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_CLIST_PREBUILDCONTACTMENU,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ if (ContactMenuShowingEventHandler != null)
+ ContactMenuShowingEventHandler(this, new ContactListEventArgs(ContactInfo.FromHandle(wParam)));
+
+ return (int)CallbackResult.Success;
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>>(ref ContactMenuShowingEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_CLIST_PREBUILDCONTACTMENU, ContactMenuShowingEventHandler);
+ }
+ }
+
+ public event MirandaEventHandler> ContactDoubleClicked
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>>(ref ContactDoubleClickedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_CLIST_DOUBLECLICKED,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ bool retValue = InvokeChainCancelable>(ContactDoubleClickedEventHandler, new ContactListEventArgs(ContactInfo.FromHandle(wParam)));
+ return Convert.ToInt32(retValue);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>>(ref ContactDoubleClickedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_CLIST_DOUBLECLICKED, ContactDoubleClickedEventHandler);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ #region Menu
+
+ public void AddMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute item)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ string serviceName = item.IsContactMenuItem ? MirandaServices.MS_CLIST_ADDCONTACTMENUITEM : MirandaServices.MS_CLIST_ADDMAINMENUITEM;
+
+ UnmanagedStructHandle nativeHandle = UnmanagedStructHandle.Empty;
+ CLISTMENUITEM nativeItem = new CLISTMENUITEM(owner, item);
+
+ try
+ {
+ nativeHandle = new UnmanagedStructHandle(ref nativeItem);
+
+ IntPtr handle = (IntPtr)MirandaContext.Current.CallService(serviceName, UIntPtr.Zero, nativeHandle.IntPtr,
+ (owner is StandalonePlugin && !item.IsAdditional));
+
+ item.MirandaHandle = handle;
+ Debug.Assert(handle != IntPtr.Zero);
+ }
+ finally
+ {
+ nativeHandle.Free();
+ }
+ }
+
+ public bool ModifyMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute menuItem, string text)
+ {
+ return ModifyMenuItem(owner, menuItem, text, MenuItemProperties.None, null, 0, true);
+ }
+
+ public bool ModifyMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute menuItem, MenuItemProperties flags)
+ {
+ return ModifyMenuItem(owner, menuItem, null, flags, null, 0, true);
+ }
+
+ public bool ModifyMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute menuItem, HotKeys hotKey)
+ {
+ return ModifyMenuItem(owner, menuItem, null, MenuItemProperties.None, null, hotKey, true);
+ }
+
+ public bool ModifyMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute menuItem, Icon icon)
+ {
+ return ModifyMenuItem(owner, menuItem, null, MenuItemProperties.None, icon, 0, true);
+ }
+
+ public bool ModifyMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute menuItem, string text, MenuItemProperties flags, Icon icon, HotKeys hotKey)
+ {
+ return ModifyMenuItem(owner, menuItem, text, flags, icon, hotKey, true);
+ }
+
+ public bool ModifyMenuItem(MirandaPlugin owner, MenuItemDeclarationAttribute menuItem, string text, MenuItemProperties flags, Icon icon, HotKeys hotKey, bool updateItemDescriptor)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (menuItem == null)
+ throw new ArgumentNullException("menuItem");
+
+ if (menuItem.MirandaHandle == IntPtr.Zero)
+ throw new ArgumentException("Invalid menu item handle.");
+
+ UnmanagedStructHandle nativeHandle = UnmanagedStructHandle.Empty;
+
+ try
+ {
+ SynchronizationHelper.BeginMenuItemUpdate(menuItem);
+
+ CLISTMENUITEM nativeItem = new CLISTMENUITEM(owner, menuItem);
+ MenuItemModifyFlags modifyFlags = MenuItemModifyFlags.None;
+
+ if (text != null)
+ {
+ modifyFlags |= MenuItemModifyFlags.CMIM_NAME;
+ nativeItem.Text = text;
+
+ if (updateItemDescriptor) menuItem.Text = text;
+ }
+ if (flags != MenuItemProperties.KeepCurrent)
+ {
+ modifyFlags |= MenuItemModifyFlags.CMIM_FLAGS;
+ nativeItem.Flags = (uint)flags;
+
+ if (updateItemDescriptor) menuItem.Flags = flags;
+ }
+ if (icon != null)
+ {
+ modifyFlags |= MenuItemModifyFlags.CMIM_ICON;
+ nativeItem.Icon = icon.Handle;
+ }
+ if (hotKey != 0)
+ {
+ modifyFlags |= MenuItemModifyFlags.CMIM_HOTKEY;
+ nativeItem.HotKey = (uint)hotKey;
+ if (updateItemDescriptor) menuItem.HotKey = hotKey;
+ }
+
+ nativeItem.Flags |= (uint)modifyFlags;
+
+ nativeHandle = new UnmanagedStructHandle(ref nativeItem);
+ bool result = MirandaContext.Current.CallService(MirandaServices.MS_CLIST_MODIFYMENUITEM, (UIntPtr)(uint)menuItem.MirandaHandle, nativeHandle.IntPtr) == 0
+ ? true : false;
+
+ Debug.Assert(result);
+ return result;
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(TextResources.ExceptionMsg_ErrorWhileCallingMirandaService + e.Message, e);
+ }
+ finally
+ {
+ nativeHandle.Free();
+ SynchronizationHelper.EndUpdate(menuItem);
+ }
+ }
+
+ #endregion
+
+ #region UI
+
+ public bool ShowBaloonTip(string title, string text, string protocol, ToolTipIcon icon, int timeout)
+ {
+ UnmanagedStructHandle nativeHandle = UnmanagedStructHandle.Empty;
+
+ try
+ {
+ MIRANDASYSTRAYNOTIFY msn = new MIRANDASYSTRAYNOTIFY(title, text, icon);
+ msn.Timeout = (uint)timeout;
+ msn.Protocol = protocol;
+
+ nativeHandle = new UnmanagedStructHandle(ref msn);
+ int result = MirandaContext.Current.CallService(MirandaServices.MS_CLIST_SYSTRAY_NOTIFY, UIntPtr.Zero, nativeHandle.IntPtr);
+
+ bool retValue = (result == 0);
+ Debug.Assert(retValue);
+
+ return retValue;
+ }
+ finally
+ {
+ nativeHandle.Free();
+ }
+ }
+
+ public bool SetInfoTipHoverTime(int time)
+ {
+ int result = MirandaContext.Current.CallService(MS_CLC_SETINFOTIPHOVERTIME, (UIntPtr)(uint)time, IntPtr.Zero);
+ Debug.Assert(result == 0);
+
+ return result == 0;
+ }
+
+ public int GetInfoTipHoverTime()
+ {
+ return MirandaContext.Current.CallService(MS_CLC_GETINFOTIPHOVERTIME);
+ }
+
+ public void EnableContactSelectionTracking()
+ {
+ lock (SyncObject)
+ {
+ if (ContactSelectionTrackingEnabled)
+ return;
+
+ ContactMenuShowing += HandleContactSelection;
+ ContactSelectionTrackingEnabled = true;
+
+ MirandaContext.Current.IsolatedModePluginsUnloading += delegate { ContactMenuShowing -= HandleContactSelection; };
+ }
+ }
+
+ private void HandleContactSelection(object sender, ContactListEventArgs e)
+ {
+ lock (SyncObject)
+ selectedContact = e.EventData;
+ }
+
+ #endregion
+
+ #region Events
+
+ public IntPtr AddEvent(ContactInfo contact, Icon icon, string serviceToCall, ContactListEventProperties properties, string toolTip)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ return AddEvent(contact, icon, serviceToCall, IntPtr.Zero, contact.MirandaHandle, properties, toolTip);
+ }
+
+ public IntPtr AddEvent(ContactInfo contact, Icon icon, string serviceToCall, IntPtr lParamToPass, IntPtr eventToken, ContactListEventProperties properties, string toolTip)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ if (icon == null)
+ throw new ArgumentNullException("icon");
+
+ ContactListEvent clistEvent = new ContactListEvent();
+ clistEvent.ContactHandle = contact.MirandaHandle;
+ clistEvent.EventHandle = eventToken;
+ clistEvent.Flags = (uint)properties;
+ clistEvent.IconHandle = icon.Handle;
+ clistEvent.LParam = lParamToPass;
+ clistEvent.ServiceName = serviceToCall;
+ clistEvent.Tooltip = toolTip;
+
+ UnmanagedStructHandle nativeStruct = UnmanagedStructHandle.Empty;
+
+ try
+ {
+ nativeStruct = new UnmanagedStructHandle(ref clistEvent);
+ IntPtr eventHandle = (IntPtr)MirandaContext.Current.CallService(MS_CLIST_ADDEVENT, UIntPtr.Zero, nativeStruct.IntPtr);
+
+ if (eventHandle != IntPtr.Zero)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_CLIST_ADDEVENT, eventHandle.ToString()));
+
+ return clistEvent.EventHandle;
+ }
+ finally
+ {
+ nativeStruct.Free();
+ }
+ }
+
+ public bool RemoveEvent(ContactInfo contact)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ return RemoveEvent(contact, contact.MirandaHandle);
+ }
+
+ public bool RemoveEvent(ContactInfo contact, IntPtr eventToken)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ return !Convert.ToBoolean(MirandaContext.Current.CallService(MS_CLIST_REMOVEEVENT, contact.MirandaHandle, eventToken));
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContactListEvent.cs b/Hyphen/Plugins/Infrastructure/ContactListEvent.cs
new file mode 100644
index 0000000..f960383
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContactListEvent.cs
@@ -0,0 +1,91 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ public sealed class ContactListEvent
+ {
+ #region Native fields
+
+ private readonly int Size;
+ internal IntPtr ContactHandle;
+ internal IntPtr IconHandle;
+ internal UInt32 Flags;
+ internal IntPtr EventHandle;
+ internal IntPtr lParam;
+ internal string ServiceName;
+ internal string Tooltip;
+
+ #endregion
+
+ #region .ctors
+
+ internal ContactListEvent()
+ {
+ Size = Marshal.SizeOf(this);
+ }
+
+ public static ContactListEvent FromPointer(IntPtr pClistEvent)
+ {
+ if (pClistEvent == IntPtr.Zero)
+ throw new ArgumentNullException("pClistEventHandle");
+
+ return (ContactListEvent)Marshal.PtrToStructure(pClistEvent, typeof(ContactListEvent));
+ }
+
+ #endregion
+
+ #region Managed properties
+
+ public ContactInfo Contact
+ {
+ get { return ContactInfo.FromHandle(ContactHandle); }
+ }
+
+ public IntPtr LParam
+ {
+ get { return lParam; }
+ internal set { lParam = value; }
+ }
+
+ #endregion
+ }
+
+ // typedef struct {
+ // int cbSize; //size in bytes of this structure
+ // HANDLE hContact; //handle to the contact to put the icon by
+ // HICON hIcon; //icon to flash
+ // DWORD flags; //...of course
+ // union
+ // {
+ // HANDLE hDbEvent; //caller defined but should be unique for hContact
+ // char * lpszProtocol;
+ // };
+ // LPARAM lParam; //caller defined
+ // char *pszService; //name of the service to call on activation
+ // union {
+ // char *pszTooltip; //short description of the event to display as a
+ // TCHAR *ptszTooltip; //tooltip on the system tray
+ // };
+ //} CLISTEVENT;
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContactListEventArgs.cs b/Hyphen/Plugins/Infrastructure/ContactListEventArgs.cs
new file mode 100644
index 0000000..2b96dc3
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContactListEventArgs.cs
@@ -0,0 +1,49 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public class ContactListEventArgs : MirandaEventArgs
+ {
+ #region Fields
+
+ private T eventData;
+ public T EventData
+ {
+ get
+ {
+ return this.eventData;
+ }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ internal ContactListEventArgs(T eventData)
+ {
+ this.eventData = eventData;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContactListInfoTip.cs b/Hyphen/Plugins/Infrastructure/ContactListInfoTip.cs
new file mode 100644
index 0000000..a3d3411
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContactListInfoTip.cs
@@ -0,0 +1,81 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Drawing;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct ContactListInfoTip
+ {
+ #region Native fields
+
+ private int size, isTreeFocused, isGroup;
+ private IntPtr itemPtr;
+
+ private Point point;
+ private Rectangle rectangle;
+
+ #endregion
+
+ #region Properties
+
+ public bool IsTreeFocused
+ {
+ get { return Convert.ToBoolean(isTreeFocused); }
+ }
+
+ public bool IsGroup
+ {
+ get { return Convert.ToBoolean(isGroup); }
+ }
+
+ public MirandaItem Item
+ {
+ get
+ {
+ return IsGroup ? (MirandaItem)new GroupInfo(itemPtr) : (MirandaItem)ContactInfo.FromHandle(itemPtr);
+ }
+ }
+
+ public Point Point
+ {
+ get { return point; }
+ }
+
+ public Rectangle Rectangle
+ {
+ get { return rectangle; }
+ }
+
+ #endregion
+ }
+
+ /*
+ * typedef struct {
+ int cbSize;
+ int isTreeFocused; //so the plugin can provide an option
+ int isGroup; //0 if it's a contact, 1 if it's a group
+ HANDLE hItem; //handle to group or contact
+ POINT ptCursor;
+ RECT rcItem;
+ } CLCINFOTIP;*/
+}
diff --git a/Hyphen/Plugins/Infrastructure/ContextWorker.cs b/Hyphen/Plugins/Infrastructure/ContextWorker.cs
new file mode 100644
index 0000000..26e82f1
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ContextWorker.cs
@@ -0,0 +1,36 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ ///
+ /// Represents a base class for cross-domain callable object that operate with Miranda Context.
+ ///
+ public abstract class ContextWorker : RemoteObject
+ {
+ protected internal ContextWorker() { }
+
+ protected MirandaContext Context
+ {
+ get { return MirandaContext.Current; }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/DatabaseEventInfo.cs b/Hyphen/Plugins/Infrastructure/DatabaseEventInfo.cs
new file mode 100644
index 0000000..a3e29d5
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/DatabaseEventInfo.cs
@@ -0,0 +1,419 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ ///
+ /// Represents database event information.
+ ///
+ public class DatabaseEventInfo : IMirandaObject
+ {
+ #region Constants
+
+ private const string MS_DB_EVENT_GETBLOBSIZE = "DB/Event/GetBlobSize";
+
+ ///
+ /// DB/Event/Get
+ /// Retrieves all the information stored in hDbEvent
+ /// wParam=(WPARAM)(HANDLE)hDbEvent
+ /// lParam=(LPARAM)(DBEVENTINFO*)&dbe
+ /// hDbEvent should have been returned by db/event/add or db/event/find*event
+ /// Returns 0 on success or nonzero if hDbEvent is invalid
+ /// Don't forget to set dbe.cbSize, dbe.pBlob and dbe.cbBlob before calling this
+ /// service
+ /// The correct value dbe.cbBlob can be got using db/event/getblobsize
+ /// If successful, all the fields of dbe are filled. dbe.cbBlob is set to the
+ /// actual number of bytes retrieved and put in dbe.pBlob
+ /// If dbe.cbBlob is too small, dbe.pBlob is filled up to the size of dbe.cbBlob
+ /// and then dbe.cbBlob is set to the required size of data to go in dbe.pBlob
+ /// On return, dbe.szModule is a pointer to the database module's own internal list
+ /// of modules. Look but don't touch.
+ ///
+ private const string MS_DB_EVENT_GET = "DB/Event/Get";
+
+ private const string MS_DB_TIME_TIMESTAMPTOSTRING = "DB/Time/TimestampToString";
+
+ ///
+ /// DB/Event/GetContact
+ /// Retrieves a handle to the contact that owns hDbEvent.
+ /// wParam=(WPARAM)(HANDLE)hDbEvent
+ /// lParam=0
+ /// hDbEvent should have been returned by db/event/add or db/event/find*event
+ /// NULL is a valid return value, meaning, as usual, the user.
+ /// Returns (HANDLE)(-1) if hDbEvent is invalid, or the handle to the contact on
+ /// success
+ /// This service is exceptionally slow. Use only when you have no other choice at
+ /// all.
+ ///
+ private const string MS_DB_EVENT_GETCONTACT = "DB/Event/GetContact";
+
+ ///
+ /// DB/Event/GetText (0.7.0+)
+ /// Retrieves the event's text
+ /// wParam=(WPARAM)0 (unused)
+ /// lParam=(LPARAM)(DBEVENTGETTEXT*)egt - pointer to structure with parameters
+ /// egt->dbei should be the valid database event read via MS_DB_EVENT_GET
+ /// egt->datatype = DBVT_WCHAR or DBVT_ASCIIZ or DBVT_TCHAR. If a caller wants to
+ /// suppress Unicode part of event in answer, add DBVTF_DENYUNICODE to this field.
+ /// egt->codepage is any valid codepage, CP_ACP by default.
+ /// Function returns a pointer to a string in the required format.
+ /// This string should be freed by a call of mir_free
+ ///
+ private const string MS_DB_EVENT_GETTEXT = "DB/Event/GetText";
+
+ #endregion
+
+ #region .ctors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected DatabaseEventInfo() { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Event handle.
+ protected DatabaseEventInfo(IntPtr eventHandle)
+ {
+ if (eventHandle == IntPtr.Zero)
+ throw new ArgumentNullException("eventHandle");
+
+ this.mirandaHandle = eventHandle;
+ FromHandle(eventHandle, out type, out flags, out data, out owningModule, out timestamp);
+ }
+
+ ///
+ /// Creates a new instance of the from an event handle.
+ ///
+ /// Event handle.
+ /// Database event info.
+ public static DatabaseEventInfo FromHandle(IntPtr eventHandle)
+ {
+ return new DatabaseEventInfo(eventHandle);
+ }
+
+ #endregion
+
+ #region Initializers
+
+ ///
+ /// Gets the event information based on its handle.
+ ///
+ /// Event handle.
+ /// [OUT] Event type.
+ /// [OUT] Event flags.
+ /// [OUT] Event data.
+ /// [OUT] Event related module.
+ /// [OUT] Event timestamp.
+ public static void FromHandle(IntPtr eventHandle, out DatabaseEventType type, out DatabaseEventProperties flags, out string data, out Protocol owningModule, out DateTime timestamp)
+ {
+ InteropBuffer buffer = null;
+
+ try
+ {
+ unsafe
+ {
+ DBEVENTINFO dbEventInfo;
+ PrepareDbEventInfo(eventHandle, out dbEventInfo, out buffer);
+
+ GetEventInfo(ref dbEventInfo, eventHandle, buffer, out type, out flags, out data, out owningModule, out timestamp);
+ }
+ }
+ catch (MirandaException)
+ {
+ throw;
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(TextResources.ExceptionMsg_CannotFinishMarshaling, e);
+ }
+ finally
+ {
+ if (buffer != null)
+ {
+ buffer.Unlock();
+ InteropBufferPool.ReleaseBuffer(buffer);
+ }
+ }
+ }
+
+ ///
+ /// Prepares the for information extraction and the blob buffer.
+ ///
+ /// Event handle.
+ /// [OUT] DB event info to marshal data into.
+ /// [OUT] Locked Blob buffer.
+ private unsafe static void PrepareDbEventInfo(IntPtr eventHandle, out DBEVENTINFO dbEventInfo, out InteropBuffer buffer)
+ {
+ int blobSize = MirandaContext.Current.CallServiceUnsafe(MS_DB_EVENT_GETBLOBSIZE, eventHandle.ToPointer(), null);
+
+ if (blobSize == -1)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_GETBLOBSIZE, blobSize.ToString()));
+
+ // Acquire a buffer for the blob
+ buffer = InteropBufferPool.AcquireBuffer(blobSize);
+ buffer.Lock();
+
+ dbEventInfo = new DBEVENTINFO(blobSize, buffer.IntPtr);
+ }
+
+ ///
+ /// Initializes the instance by marshaling data from a pointer.
+ ///
+ /// pointer.
+ private void MarshalEventInfo(IntPtr pDbEventInfo)
+ {
+ DBEVENTINFO info = (DBEVENTINFO)Marshal.PtrToStructure(pDbEventInfo, typeof(DBEVENTINFO));
+
+ // Get an appropriately size buffer for the blob
+ InteropBuffer buffer = InteropBufferPool.AcquireBuffer((int)info.BlobSize);
+
+ try
+ {
+ buffer.Lock();
+ GetEventInfo(ref info, IntPtr.Zero, buffer, out type, out flags, out data, out owningModule, out timestamp);
+ }
+ finally
+ {
+ buffer.Unlock();
+ InteropBufferPool.ReleaseBuffer(buffer);
+ }
+ }
+
+ ///
+ /// Get the event information from a struct.
+ ///
+ /// [REF] struct.
+ /// Event handle (the blob buffer will be populated if not null).
+ /// Buffer to use for blob marshaling.
+ /// [OUT] Event type.
+ /// [OUT] Event flags.
+ /// [OUT] Event data.
+ /// [OUT] Event related module.
+ /// [OUT] Event timestamp.
+ private static void GetEventInfo(ref DBEVENTINFO dbEventInfo, IntPtr eventHandle, InteropBuffer blobBuffer, out DatabaseEventType type, out DatabaseEventProperties flags, out string data, out Protocol owningModule, out DateTime timestamp)
+ {
+ MirandaContext context = MirandaContext.Current;
+
+ unsafe
+ {
+ // If the event handle is set, we probably want to populate the blob buffer...
+ if (eventHandle != IntPtr.Zero)
+ PopulateBlobBuffer(ref dbEventInfo, eventHandle);
+
+ type = (DatabaseEventType)dbEventInfo.EventType;
+ flags = (DatabaseEventProperties)dbEventInfo.Flags;
+ data = GetEventData(ref dbEventInfo);
+ }
+
+ owningModule = GetEventModule(ref dbEventInfo);
+ GetEventTimestamp(ref dbEventInfo, blobBuffer, out timestamp);
+ }
+
+ ///
+ /// Populates the blob buffer set by the parameter.
+ ///
+ /// [REF] struct identifiing the buffer.
+ /// Event handle.
+ /// Buffer could bet populated.
+ private unsafe static void PopulateBlobBuffer(ref DBEVENTINFO dbEventInfo, IntPtr eventHandle)
+ {
+ int result;
+
+ fixed (void* pDbEventInfo = &dbEventInfo)
+ result = MirandaContext.Current.CallServiceUnsafe(MS_DB_EVENT_GET, eventHandle.ToPointer(), pDbEventInfo);
+
+ if (result != 0)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_GET, result.ToString()));
+ }
+
+ ///
+ /// Gets the event timestamp.
+ ///
+ /// [REF] struct.
+ /// Buffer to reuse.
+ /// [OUT] Timestamp.
+ private static void GetEventTimestamp(ref DBEVENTINFO dbEventInfo, InteropBuffer blobBuffer, out DateTime timestamp)
+ {
+ try
+ {
+ DBTIMETOSTRING timeToString = new DBTIMETOSTRING("s D");
+ timeToString.MaxBytes = blobBuffer.Size;
+ timeToString.Output = blobBuffer.IntPtr;
+
+ unsafe
+ {
+ MirandaContext.Current.CallServiceUnsafe(MS_DB_TIME_TIMESTAMPTOSTRING, (void*)dbEventInfo.Timestamp, &timeToString);
+ }
+
+ timestamp = DateTime.Parse(Translate.ToString(timeToString.Output, StringEncoding.Ansi));
+ }
+ catch (FormatException)
+ {
+ timestamp = DateTime.MinValue;
+ }
+ }
+
+ ///
+ /// Gets the event module.
+ ///
+ /// [REF] struct.
+ /// Event module.
+ private static Protocol GetEventModule(ref DBEVENTINFO dbEventInfo)
+ {
+ Protocol owningModule;
+ bool moduleFound = false;
+
+ if (dbEventInfo.Module != IntPtr.Zero)
+ moduleFound = MirandaContext.Current.Protocols.TryGetValue(Translate.ToString(dbEventInfo.Module, StringEncoding.Ansi), out owningModule);
+ else
+ owningModule = Protocol.UnknownProtocol;
+
+ if (!moduleFound)
+ owningModule = Protocol.UnknownProtocol;
+
+ return owningModule;
+ }
+
+ ///
+ /// Gets the event data.
+ ///
+ /// [REF] struct.
+ /// Event data.
+ private unsafe static string GetEventData(ref DBEVENTINFO dbEventInfo)
+ {
+ string data;
+
+ DBEVENTGETTEXT dbGetText = new DBEVENTGETTEXT();
+ dbGetText.Codepage = 0;
+ dbGetText.DataType = (int)DatabaseSettingType.UnicodeString;
+
+ IntPtr pText;
+
+ fixed (void* pDbEventInfo = &dbEventInfo)
+ {
+ dbGetText.DbEventInfoPtr = new IntPtr(pDbEventInfo);
+ pText = (IntPtr)MirandaContext.Current.CallServiceUnsafe(MS_DB_EVENT_GETTEXT, null, &dbGetText);
+ }
+
+ if (pText != IntPtr.Zero)
+ {
+ data = Translate.ToString(pText, StringEncoding.Unicode);
+ MirandaContext.Current.MirandaMemoryManager.Free(pText);
+ }
+ else
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_GETTEXT, "null"));
+
+ return data;
+ }
+
+ #endregion
+
+ #region Properties
+
+ private IntPtr mirandaHandle;
+ public IntPtr MirandaHandle
+ {
+ get { return mirandaHandle; }
+ }
+
+ private Protocol owningModule;
+ public Protocol OwningModule
+ {
+ get { return owningModule; }
+ }
+
+ private DateTime timestamp;
+ public DateTime Timestamp
+ {
+ get { return timestamp; }
+ }
+
+ private DatabaseEventProperties flags;
+ public DatabaseEventProperties Flags
+ {
+ get { return flags; }
+ }
+
+ private DatabaseEventType type;
+ public DatabaseEventType Type
+ {
+ get { return type; }
+ }
+
+ private string data;
+ public string Data
+ {
+ get { return data; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Marshals the from a struct pointer.
+ ///
+ /// struct pointer.
+ /// Event info.
+ internal static DatabaseEventInfo FromPointer(IntPtr pDbEventInfo)
+ {
+ DatabaseEventInfo info = new DatabaseEventInfo();
+ info.MarshalEventInfo(pDbEventInfo);
+
+ return info;
+ }
+
+ ///
+ /// Gets the handle of the contact owning this event. This method is very slow, use wisely.
+ ///
+ /// Associated contact handle.
+ public IntPtr GetContactHandle()
+ {
+ return GetContactHandle(mirandaHandle);
+ }
+
+ ///
+ /// Gets the handle of the contact owning this event. This method is very slow, use wisely.
+ ///
+ /// Event handle to get the contact handle for.
+ /// Associated contact handle.
+ public static IntPtr GetContactHandle(IntPtr eventHandle)
+ {
+ if (eventHandle == IntPtr.Zero)
+ throw new ArgumentNullException("eventHandle");
+
+ int contactHandle = MirandaContext.Current.CallService(MS_DB_EVENT_GETCONTACT, eventHandle, IntPtr.Zero);
+
+ if (contactHandle == -1)
+ throw new ArgumentException(TextResources.ExceptionMsg_InvalidHandle, "eventHandle");
+
+ return (IntPtr)contactHandle;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/EventHandle.cs b/Hyphen/Plugins/Infrastructure/EventHandle.cs
new file mode 100644
index 0000000..46f1361
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/EventHandle.cs
@@ -0,0 +1,114 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Hyphen;
+using System.Runtime.CompilerServices;
+using Virtuoso.Miranda.Plugins.Collections;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [CLSCompliant(false)]
+ public sealed class EventHandle : MirandaObject
+ {
+ #region Fields
+
+ private readonly MirandaPlugin owner;
+ internal MirandaPlugin Owner
+ {
+ get { return owner; }
+ }
+
+ private readonly string eventName;
+ public string EventName
+ {
+ get { return eventName; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ internal EventHandle(MirandaPlugin owner, string eventName, IntPtr handle)
+ {
+ if (handle == IntPtr.Zero)
+ throw new ArgumentNullException("handle");
+
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (eventName == null)
+ throw new ArgumentNullException("eventName");
+
+ this.owner = owner;
+ this.MirandaHandle = handle;
+ this.eventName = eventName;
+
+ List eventHandles = owner.Descriptor.EventHandles;
+
+ try
+ {
+ SynchronizationHelper.BeginCollectionUpdate(eventHandles);
+ eventHandles.Add(this);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(eventHandles);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public int FireEvent()
+ {
+ return FireEvent(UIntPtr.Zero, IntPtr.Zero);
+ }
+
+ public int FireEvent(UIntPtr wParam, IntPtr lParam)
+ {
+ MirandaPluginLink link = MirandaContext.Current.PluginLink;
+
+ lock (link)
+ return link.NativePluginLink.NotifyEventHooks(MirandaHandle, wParam, lParam);
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public void SetDefaultSubscriber(Callback subscriber)
+ {
+ if (subscriber == null)
+ throw new ArgumentNullException("subscriber");
+
+ MirandaPluginLink link = MirandaContext.Current.PluginLink;
+
+ lock (link)
+ {
+ int result;
+
+ if ((result = link.NativePluginLink.SetHookDefaultForHookableEvent(MirandaHandle, subscriber)) != 0)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, "SetHookDefaultForHookableEvent", result.ToString()));
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/EventHookAttribute.cs b/Hyphen/Plugins/Infrastructure/EventHookAttribute.cs
new file mode 100644
index 0000000..ddcdae7
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/EventHookAttribute.cs
@@ -0,0 +1,66 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
+ public sealed class EventHookAttribute : HookAttribute
+ {
+ #region Fields
+
+ private string eventName;
+ public string EventName
+ {
+ get
+ {
+ return this.eventName;
+ }
+ set
+ {
+ this.eventName = value;
+ }
+ }
+
+ internal override string HookName
+ {
+ get { return EventName; }
+ }
+
+ internal override HookType HookType
+ {
+ get { return HookType.EventHook; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public EventHookAttribute(string eventName)
+ {
+ if (eventName == null)
+ throw new ArgumentNullException("eventName");
+
+ this.eventName = eventName;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/EventManager.cs b/Hyphen/Plugins/Infrastructure/EventManager.cs
new file mode 100644
index 0000000..774efaa
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/EventManager.cs
@@ -0,0 +1,151 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Native;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public static class EventManager
+ {
+ [CLSCompliant(false)]
+ public static void CreateEventHook(string eventName, Callback callback, MirandaPlugin owner)
+ {
+ if (String.IsNullOrEmpty(eventName))
+ throw new ArgumentNullException("eventName");
+
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (!owner.Initialized)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginNotInitialized);
+
+ HookDescriptorCollection collection = owner.Descriptor.EventHooks;
+
+ try
+ {
+ SynchronizationHelper.BeginPluginUpdate(owner);
+ SynchronizationHelper.BeginCollectionUpdate(collection);
+
+ HookDescriptor descriptor = HookDescriptor.SetUpAndStore(collection, eventName, owner.Descriptor, callback, HookType.EventHook);
+ descriptor.RegisteredManually = true;
+
+ HookManager.CreateHook(descriptor);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(owner);
+ SynchronizationHelper.EndUpdate(collection);
+ }
+ }
+
+ public static void RemoveEventHook(string eventName, MirandaPlugin owner)
+ {
+ if (String.IsNullOrEmpty(eventName))
+ throw new ArgumentNullException("eventName");
+
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (!owner.Initialized)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginNotInitialized);
+
+ HookDescriptorCollection collection = owner.Descriptor.EventHooks;
+
+ try
+ {
+ SynchronizationHelper.BeginCollectionUpdate(collection);
+ HookDescriptor descriptor = null;
+
+ if ((descriptor = owner.Descriptor.EventHooks.Find(eventName)) == null)
+ return;
+
+ HookManager.DestroyHook(descriptor);
+ collection.Remove(descriptor);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(collection);
+ }
+ }
+
+ [CLSCompliant(false)]
+ public static EventHandle CreateEvent(string eventName, MirandaPlugin owner)
+ {
+ return CreateEvent(eventName, owner, null);
+ }
+
+ [CLSCompliant(false)]
+ public static EventHandle CreateEvent(string eventName, MirandaPlugin owner, Callback defaultSubscriber)
+ {
+ if (String.IsNullOrEmpty(eventName))
+ throw new ArgumentNullException("eventName");
+
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (!owner.Initialized)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginNotInitialized);
+
+ if (ServiceManager.ServiceExists(eventName))
+ throw new ArgumentException("eventName");
+
+ EventHandle handle = new EventHandle(owner, eventName, MirandaContext.Current.PluginLink.NativePluginLink.CreateHookableEvent(eventName));
+
+ if (defaultSubscriber != null)
+ handle.SetDefaultSubscriber(defaultSubscriber);
+
+ return handle;
+ }
+
+ public static void RemoveEvent(EventHandle eventHandle)
+ {
+ if (eventHandle == null)
+ throw new ArgumentNullException("eventHandle");
+
+ MirandaPluginLink link = MirandaContext.Current.PluginLink;
+
+ if (eventHandle.MirandaHandle != IntPtr.Zero)
+ {
+ int result;
+ if ((result = link.NativePluginLink.DestroyHookableEvent(eventHandle.MirandaHandle)) != 0)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, "DestroyHookableEvent", result.ToString()));
+
+ eventHandle.MirandaHandle = IntPtr.Zero;
+ EventHandleCollection handles = eventHandle.Owner.Descriptor.EventHandles;
+
+ try
+ {
+ SynchronizationHelper.BeginCollectionUpdate(handles);
+ handles.Remove(eventHandle);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(handles);
+ }
+ }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/EventPublisher.cs b/Hyphen/Plugins/Infrastructure/EventPublisher.cs
new file mode 100644
index 0000000..a167f0d
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/EventPublisher.cs
@@ -0,0 +1,49 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public abstract class EventPublisher
+ {
+ internal EventPublisher() { }
+
+ protected bool InvokeChainCancelable(MirandaEventHandler handler, TEventArgs eventArgs) where TEventArgs : MirandaEventArgs
+ {
+ return InvokeChainCancelable(handler, this, eventArgs);
+ }
+
+ public static bool InvokeChainCancelable(MirandaEventHandler handler, object sender, TEventArgs eventArgs) where TEventArgs : MirandaEventArgs
+ {
+ bool retValue = EventResult.HonourEventChain;
+
+ if (handler != null)
+ {
+ foreach (MirandaEventHandler del in handler.GetInvocationList())
+ {
+ if ((retValue = del(sender, eventArgs)) == EventResult.BreakEventChain)
+ break;
+ }
+ }
+
+ return retValue;
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/GroupInfo.cs b/Hyphen/Plugins/Infrastructure/GroupInfo.cs
new file mode 100644
index 0000000..e547946
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/GroupInfo.cs
@@ -0,0 +1,75 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public sealed class GroupInfo : MirandaItem
+ {
+ #region Fields
+
+ private const string MS_CLIST_GROUPGETNAME = "CList/GroupGetName";
+
+ #endregion
+
+ #region .ctors
+
+ internal GroupInfo(IntPtr handle) : base(handle, ItemType.Group) { }
+
+ #endregion
+
+ #region Properties
+
+ public string Name
+ {
+ get
+ {
+ IntPtr pName = (IntPtr)MirandaContext.Current.CallService(MS_CLIST_GROUPGETNAME, MirandaHandle, IntPtr.Zero);
+ Debug.Assert(pName != IntPtr.Zero);
+
+ if (pName != IntPtr.Zero)
+ return Marshal.PtrToStringAnsi(pName);
+ else
+ return null;
+ }
+ }
+
+ public unsafe bool IsExpanded
+ {
+ get
+ {
+ int expanded = 0;
+
+ IntPtr pName = (IntPtr)MirandaContext.Current.CallServiceUnsafe(MS_CLIST_GROUPGETNAME, MirandaHandle.ToPointer(), &expanded);
+ Debug.Assert(pName != IntPtr.Zero);
+
+ if (pName != IntPtr.Zero)
+ return Convert.ToBoolean(expanded);
+ else
+ return false;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/HookAttribute.cs b/Hyphen/Plugins/Infrastructure/HookAttribute.cs
new file mode 100644
index 0000000..eacc125
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/HookAttribute.cs
@@ -0,0 +1,32 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public abstract class HookAttribute : Attribute
+ {
+ protected internal HookAttribute() { }
+
+ internal abstract string HookName { get; }
+
+ internal abstract HookType HookType { get; }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/HookDescriptor.cs b/Hyphen/Plugins/Infrastructure/HookDescriptor.cs
new file mode 100644
index 0000000..89481a2
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/HookDescriptor.cs
@@ -0,0 +1,164 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Forms;
+using Virtuoso.Hyphen.Mini;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal sealed class HookDescriptor : IMirandaObject, IDescriptor
+ {
+ #region Fields
+
+ private string name;
+ private IntPtr handle;
+
+ private Callback callback, callbackStub;
+ private HookType hookType;
+
+ private PluginDescriptor owner;
+ private bool registeredManually;
+
+ #endregion
+
+ #region .ctor
+
+ public HookDescriptor(string name, PluginDescriptor owner, Callback callback, HookType type)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ this.callbackStub = SafeCallbackStub;
+
+ this.name = name;
+ this.owner = owner;
+ this.callback = callback;
+ this.hookType = type;
+ }
+
+ public static HookDescriptor SetUpAndStore(IList targetContainer, string name, PluginDescriptor owner, Callback callback, HookType type)
+ {
+ if (targetContainer == null)
+ throw new ArgumentNullException("targetContainer");
+
+ HookDescriptor descriptor = new HookDescriptor(name, owner, callback, type);
+ targetContainer.Add(descriptor);
+
+ return descriptor;
+ }
+
+ public static HookDescriptor SetUpAndStore(IDictionary targetContainer, string name, PluginDescriptor owner, Callback callback, HookType type)
+ {
+ return SetUpAndStore(targetContainer, name, name, owner, callback, type);
+ }
+
+ public static HookDescriptor SetUpAndStore(IDictionary targetContainer, T key, string name, PluginDescriptor owner, Callback callback, HookType type)
+ {
+ if (targetContainer == null)
+ throw new ArgumentNullException("targetContainer");
+
+ HookDescriptor descriptor = new HookDescriptor(name, owner, callback, type);
+ targetContainer.Add(key, descriptor);
+
+ return descriptor;
+ }
+
+ #endregion
+
+ #region Methods
+
+ private int SafeCallbackStub(UIntPtr wParam, IntPtr lParam)
+ {
+ try
+ {
+ return callback(wParam, lParam);
+ }
+ catch (Exception e)
+ {
+ MirandaPlugin.GetExceptionHandler(owner).HandleException(e, owner);
+ return (int)CallbackResult.Failure;
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ return (name.GetHashCode() + callback.Method.Name.GetHashCode() + (int)hookType);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null)
+ return false;
+
+ HookDescriptor other = obj as HookDescriptor;
+
+ if (other == null)
+ return false;
+
+ return GetHashCode() == other.GetHashCode();
+ }
+
+ #endregion
+
+ #region Properties
+
+ public string Name
+ {
+ get { return name; }
+ }
+
+ public IntPtr MirandaHandle
+ {
+ get { return handle; }
+ internal set { handle = value; }
+ }
+
+ public Callback Callback
+ {
+ get { return callbackStub; }
+ }
+
+ public HookType HookType
+ {
+ get { return hookType; }
+ }
+
+ public PluginDescriptor Owner
+ {
+ get { return owner; }
+ }
+
+ public bool RegisteredManually
+ {
+ get { return registeredManually; }
+ set { registeredManually = value; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/HookManager.cs b/Hyphen/Plugins/Infrastructure/HookManager.cs
new file mode 100644
index 0000000..04beff8
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/HookManager.cs
@@ -0,0 +1,177 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Diagnostics;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal static class HookManager
+ {
+ public static void CreateHooks(params HookDescriptor[] hooks)
+ {
+ if (hooks == null)
+ throw new ArgumentNullException("hooks");
+
+ if (hooks.Length == 0)
+ return;
+
+ foreach (HookDescriptor hook in hooks)
+ CreateHook(hook);
+ }
+
+ public static void CreateHook(HookDescriptor hook)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(hook);
+
+ switch (hook.HookType)
+ {
+ case HookType.EventHook:
+ {
+ HookEvent(hook);
+ break;
+ }
+ case HookType.ServiceFunction:
+ {
+ CreateServiceFunction(hook);
+ break;
+ }
+ default:
+ throw new ArgumentException("descriptor");
+ }
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(hook);
+ }
+ }
+
+ private static void CreateServiceFunction(HookDescriptor hook)
+ {
+ if (!ServiceManager.ServiceExists(hook.Name))
+ {
+ hook.MirandaHandle = MirandaContext.Current.PluginLink.NativePluginLink.CreateServiceFunction(hook.Name, hook.Callback);
+
+ if (hook.MirandaHandle == IntPtr.Zero)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable1_ServiceFunctionCreationFailed, hook.Name));
+ }
+ else
+ {
+ if (!hook.Owner.ServiceFunctions.Exists(delegate(HookDescriptor _hook)
+ {
+ return _hook.Equals(hook) && _hook.MirandaHandle != IntPtr.Zero;
+ }))
+ throw new InvalidOperationException(String.Format(TextResources.ExceptionMsg_Formatable1_ServiceFunctionAlreadyExists, hook.Name));
+ }
+ }
+
+ private static void HookEvent(HookDescriptor hook)
+ {
+ hook.MirandaHandle = MirandaContext.Current.PluginLink.NativePluginLink.HookEvent(hook.Name, hook.Callback);
+
+ if (hook.MirandaHandle == IntPtr.Zero)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable1_EventHookingFailed, hook.Name));
+ }
+
+ public static void DestroyHook(HookDescriptor hook)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(hook);
+
+ switch (hook.HookType)
+ {
+ case HookType.EventHook:
+ {
+ UnhookEvent(hook);
+ break;
+ }
+ case HookType.ServiceFunction:
+ {
+ DestroyServiceFunction(hook);
+ break;
+ }
+ default:
+ throw new ArgumentException("descriptor");
+ }
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(hook);
+ }
+ }
+
+ private static void UnhookEvent(HookDescriptor descriptor)
+ {
+ if (descriptor == null)
+ throw new ArgumentNullException("descriptor");
+
+ if (descriptor.HookType != HookType.EventHook)
+ throw new ArgumentOutOfRangeException("descriptor");
+
+ if (descriptor.MirandaHandle == IntPtr.Zero)
+ return;
+
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(descriptor);
+
+ int result = MirandaContext.Current.PluginLink.NativePluginLink.UnhookEvent(descriptor.MirandaHandle);
+ Debug.Assert(result == 0);
+
+ descriptor.MirandaHandle = IntPtr.Zero;
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(descriptor);
+ }
+ }
+
+ private static void DestroyServiceFunction(HookDescriptor descriptor)
+ {
+ if (descriptor == null)
+ throw new ArgumentNullException("descriptor");
+
+ if (descriptor.HookType != HookType.ServiceFunction)
+ throw new ArgumentOutOfRangeException("descriptor");
+
+ if (descriptor.MirandaHandle == IntPtr.Zero)
+ return;
+
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(descriptor);
+
+ int result = MirandaContext.Current.PluginLink.NativePluginLink.DestroyServiceFunction(descriptor.MirandaHandle);
+ Debug.Assert(result == 0);
+
+ descriptor.MirandaHandle = IntPtr.Zero;
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(descriptor);
+ }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/HookType.cs b/Hyphen/Plugins/Infrastructure/HookType.cs
new file mode 100644
index 0000000..03a0e47
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/HookType.cs
@@ -0,0 +1,30 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal enum HookType
+ {
+ Undefined,
+ EventHook,
+ ServiceFunction,
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/IMirandaObject.cs b/Hyphen/Plugins/Infrastructure/IMirandaObject.cs
new file mode 100644
index 0000000..dab07f6
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/IMirandaObject.cs
@@ -0,0 +1,28 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public interface IMirandaObject
+ {
+ IntPtr MirandaHandle { get; }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ISettingOwner.cs b/Hyphen/Plugins/Infrastructure/ISettingOwner.cs
new file mode 100644
index 0000000..76f90c5
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ISettingOwner.cs
@@ -0,0 +1,28 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public interface ISettingOwner
+ {
+ string Name { get; }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/IStringResolver.cs b/Hyphen/Plugins/Infrastructure/IStringResolver.cs
new file mode 100644
index 0000000..b6131a8
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/IStringResolver.cs
@@ -0,0 +1,28 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public interface IStringResolver
+ {
+ string ResolveString(string stringToResolve, string tag);
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/LanguagePack.cs b/Hyphen/Plugins/Infrastructure/LanguagePack.cs
new file mode 100644
index 0000000..a741739
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/LanguagePack.cs
@@ -0,0 +1,75 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public static class LanguagePack
+ {
+ #region Enums
+
+ private enum LanguagePackEncoding : ushort
+ {
+ Ansi = 0,
+ Unicode = 0x1000
+ }
+
+ #endregion
+
+ #region Constants
+
+ private const string MS_LANGPACK_TRANSLATESTRING = "LangPack/TranslateString";
+
+ #endregion
+
+ #region FromPointer methods
+
+ public static string TranslateString(string str)
+ {
+ if (String.IsNullOrEmpty(str))
+ return str;
+
+ UnmanagedStringHandle stringHandle = UnmanagedStringHandle.Empty;
+
+ try
+ {
+ StringEncoding mirandaEncoding = MirandaEnvironment.MirandaStringEncoding;
+ LanguagePackEncoding encoding = (mirandaEncoding == StringEncoding.Unicode ? LanguagePackEncoding.Unicode : LanguagePackEncoding.Ansi);
+
+ stringHandle = new UnmanagedStringHandle(str, mirandaEncoding);
+ IntPtr translatedPtr = (IntPtr)MirandaContext.Current.CallService(MS_LANGPACK_TRANSLATESTRING, (UIntPtr)encoding, stringHandle.IntPtr);
+
+ return translatedPtr == stringHandle.IntPtr ? str : Translate.ToString(translatedPtr, mirandaEncoding);
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(TextResources.ExceptionMsg_ErrorWhileCallingMirandaService, e);
+ }
+ finally
+ {
+ stringHandle.Free();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/LanguagePackStringResolver.cs b/Hyphen/Plugins/Infrastructure/LanguagePackStringResolver.cs
new file mode 100644
index 0000000..86e89a6
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/LanguagePackStringResolver.cs
@@ -0,0 +1,33 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public class LanguagePackStringResolver : IStringResolver
+ {
+ public LanguagePackStringResolver() { }
+
+ public string ResolveString(string stringToResolve, string tag)
+ {
+ return LanguagePack.TranslateString(stringToResolve);
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/LazyEventBinder.cs b/Hyphen/Plugins/Infrastructure/LazyEventBinder.cs
new file mode 100644
index 0000000..6b376fc
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/LazyEventBinder.cs
@@ -0,0 +1,102 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Threading;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Runtime.CompilerServices;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ ///
+ /// Represents a late-binded master subscriber of Miranda events.
+ ///
+ internal static class LazyEventBinder
+ {
+ #region Fields
+
+ private static readonly Dictionary EventHandlerDescriptorsTable;
+
+ #endregion
+
+ #region .ctors
+
+ static LazyEventBinder()
+ {
+ EventHandlerDescriptorsTable = new Dictionary(3);
+ }
+
+ #endregion
+
+ #region Management methods
+
+ public static void AttachDelegate(ref T destination, T value) where T : class
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (!typeof(T).IsSubclassOf(typeof(Delegate)))
+ throw new ArgumentException("T");
+
+ destination = Delegate.Combine(destination as Delegate, value as Delegate) as T;
+ }
+
+ public static void DetachDelegate(ref T destination, T value) where T : class
+ {
+ if (!typeof(T).IsSubclassOf(typeof(Delegate)))
+ throw new ArgumentException("T");
+
+ destination = Delegate.Remove(destination as Delegate, value as Delegate) as T;
+ }
+
+ public static void HookMirandaEvent(string eventName, Callback callback)
+ {
+ lock (EventHandlerDescriptorsTable)
+ {
+ if (EventHandlerDescriptorsTable.ContainsKey(eventName))
+ return;
+
+ HookDescriptor descriptor = HookDescriptor.SetUpAndStore(EventHandlerDescriptorsTable, eventName, MirandaPlugin.Hyphen.Singleton.Descriptor, callback, HookType.EventHook);
+ HookManager.CreateHook(descriptor);
+ }
+ }
+
+ public static void UnhookMirandaEvent(string eventName, Delegate callback)
+ {
+ if (callback != null)
+ return;
+
+ lock (EventHandlerDescriptorsTable)
+ {
+ if (!EventHandlerDescriptorsTable.ContainsKey(eventName))
+ return;
+
+ int result = MirandaContext.Current.PluginLink.NativePluginLink.UnhookEvent(EventHandlerDescriptorsTable[eventName].MirandaHandle);
+ Debug.Assert(result == 0);
+
+ EventHandlerDescriptorsTable.Remove(eventName);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MenuItemDeclarationAttribute.cs b/Hyphen/Plugins/Infrastructure/MenuItemDeclarationAttribute.cs
new file mode 100644
index 0000000..4777f06
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MenuItemDeclarationAttribute.cs
@@ -0,0 +1,214 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Reflection;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
+ public sealed class MenuItemDeclarationAttribute : Attribute, IMirandaObject
+ {
+ #region Fields & Properties
+
+ private static readonly Type StringResolverType = typeof(IStringResolver);
+
+ private string text;
+ public string Text
+ {
+ get { return text; }
+ set { text = value; }
+ }
+
+ private MenuItemProperties flags;
+ public MenuItemProperties Flags
+ {
+ get { return flags; }
+ set { flags = value; }
+ }
+
+ private int position, popUpPosition;
+ public int PopUpPosition
+ {
+ get { return popUpPosition; }
+ set { popUpPosition = value; }
+ }
+ public int Position
+ {
+ get { return position; }
+ set { position = value; }
+ }
+
+ private bool useEmbeddedIcon;
+ public bool UseEmbeddedIcon
+ {
+ get { return useEmbeddedIcon; }
+ set { useEmbeddedIcon = value; }
+ }
+
+ private bool hasIcon;
+ public bool HasIcon
+ {
+ get { return hasIcon; }
+ set { hasIcon = value; }
+ }
+
+ private string iconID;
+ public string IconID
+ {
+ get { return iconID; }
+ set { iconID = value; }
+ }
+
+ private string service;
+ public string Service
+ {
+ get { return service; }
+ internal set { service = value; }
+ }
+
+ private string popUpMenu;
+ public string PopUpMenu
+ {
+ get { return popUpMenu; }
+ }
+
+ private string owningModule;
+ public string OwningModule
+ {
+ get { return owningModule; }
+ set { owningModule = value; }
+ }
+
+ private HotKeys hotKey;
+ public HotKeys HotKey
+ {
+ get { return hotKey; }
+ set { hotKey = value; }
+ }
+
+ private Type stringResolver;
+ public Type StringResolver
+ {
+ get { return stringResolver; }
+ }
+
+ private string tag;
+ public string Tag
+ {
+ get { return tag; }
+ set { tag = value; }
+ }
+
+ private bool isContactMenuItem;
+ public bool IsContactMenuItem
+ {
+ get { return isContactMenuItem; }
+ set { isContactMenuItem = value; }
+ }
+
+ private IntPtr mirandaHandle;
+ public IntPtr MirandaHandle
+ {
+ get
+ {
+ return this.mirandaHandle;
+ }
+ internal set
+ {
+ this.mirandaHandle = value;
+ }
+ }
+
+ private bool isAdditional;
+ public bool IsAdditional
+ {
+ get { return isAdditional; }
+ set { isAdditional = value; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public MenuItemDeclarationAttribute(string text) : this(text, null, null, null) { }
+
+ public MenuItemDeclarationAttribute(string text, Type stringResolver) : this(text, null, stringResolver) { }
+
+ public MenuItemDeclarationAttribute(string text, string service) : this(text, null, service, null) { }
+
+ public MenuItemDeclarationAttribute(string text, string popUpMenu, string service) : this(text, popUpMenu, service, null) { }
+
+ public MenuItemDeclarationAttribute(string text, string service, Type stringResolver) : this(text, null, service, stringResolver) { }
+
+ public MenuItemDeclarationAttribute(string text, string popUpMenu, string service, Type stringResolver)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+
+ this.text = text;
+ this.service = (service == null ? String.Empty : service);
+ this.popUpMenu = popUpMenu;
+ this.stringResolver = stringResolver;
+
+ /* m_clist.h
+ * WARNING: do not use Translate(TS) for p(t)szName or p(t)szPopupName as they
+ are translated by the core, which may lead to double translation. */
+ if (stringResolver != null && stringResolver.GetType() != typeof(LanguagePackStringResolver))
+ ResolveStrings(text, popUpMenu, stringResolver);
+ }
+
+ private void ResolveStrings(string text, string popUpMenu, Type stringResolver)
+ {
+ try
+ {
+ if (stringResolver != null && stringResolver.GetInterface(StringResolverType.FullName) != null)
+ {
+ IStringResolver resolver = null;
+ StringResolverCache cache = StringResolverCache.Singleton;
+
+ lock (cache)
+ {
+ if (!cache.TryGetValue(stringResolver, out resolver))
+ {
+ resolver = (IStringResolver)Activator.CreateInstance(stringResolver, true);
+
+ if (resolver == null)
+ throw new TypeLoadException(stringResolver.FullName);
+ else
+ cache.Add(stringResolver, resolver);
+ }
+ }
+
+ this.text = resolver.ResolveString(text, tag);
+ this.popUpMenu = resolver.ResolveString(popUpMenu, tag);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new FusionException(String.Format(TextResources.ExceptionMsg_Formatable1_CannotLoadStringResolver, stringResolver.FullName), null, null, null, e);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaContactEventArgs.cs b/Hyphen/Plugins/Infrastructure/MirandaContactEventArgs.cs
new file mode 100644
index 0000000..ec6ee37
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaContactEventArgs.cs
@@ -0,0 +1,54 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public class MirandaContactEventArgs : MirandaEventArgs
+ {
+ #region Fields
+
+ private readonly ContactInfo contactInfo;
+
+ #endregion
+
+ #region .ctors
+
+ public MirandaContactEventArgs(ContactInfo contactInfo)
+ {
+ if (contactInfo == null)
+ throw new ArgumentNullException("contactInfo");
+
+ this.contactInfo = contactInfo;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public ContactInfo ContactInfo
+ {
+ get { return contactInfo; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaContactSettingEventArgs.cs b/Hyphen/Plugins/Infrastructure/MirandaContactSettingEventArgs.cs
new file mode 100644
index 0000000..5daa4fe
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaContactSettingEventArgs.cs
@@ -0,0 +1,71 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public class MirandaContactSettingEventArgs : MirandaContactEventArgs
+ {
+ #region Properties
+
+ private string settingName;
+ public string SettingName
+ {
+ get { return settingName; }
+ }
+
+ private string settingOwner;
+ public string SettingOwner
+ {
+ get { return settingOwner; }
+ }
+
+ private object value;
+ public object Value
+ {
+ get { return value; }
+ }
+
+ private DatabaseSettingType valueType;
+ public DatabaseSettingType ValueType
+ {
+ get { return valueType; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public MirandaContactSettingEventArgs(ContactInfo contactInfo, string name, string owner, object value, DatabaseSettingType valueType)
+ : base(contactInfo)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ this.settingName = name;
+ this.settingOwner = owner;
+ this.value = value;
+ this.valueType = valueType;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaContext.cs b/Hyphen/Plugins/Infrastructure/MirandaContext.cs
new file mode 100644
index 0000000..9fad5a3
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaContext.cs
@@ -0,0 +1,373 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Miranda.Plugins.Collections;
+using System.Collections.ObjectModel;
+using Virtuoso.Hyphen;
+using System.Runtime.CompilerServices;
+using System.Windows.Forms;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public sealed class MirandaContext
+ {
+ #region Constants
+
+ private const string MS_SYSTEM_GETVERSIONTEXT = "Miranda/System/GetVersionText";
+
+ ///
+ /// Returns Miranda's RTL/CRT function poiners to malloc() free() realloc() -- 0.1.2.2+
+ /// This is useful for preallocation of memory for use with Miranda's services
+ /// that Miranda can free -- or reallocation of a block of memory passed with a service.
+ /// Do not use with memory unless it is explicitly expected the memory *can*
+ /// or *shall* be used in this way. The passed structure is expected to have it's .cbSize initialised
+ /// wParam=0, lParam = (LPARAM) &MM_INTERFACE.
+ ///
+ private const string MS_SYSTEM_GET_MMI = "Miranda/System/GetMMI";
+
+ #endregion
+
+ #region Fields
+
+ private static MirandaContext singleton;
+ private PluginManagerBase pluginManager;
+
+ private readonly ServiceCallInterceptionManager serviceInterceptors;
+ private readonly MirandaDatabase mirandaDatabase;
+ private ProtocolDictionary protocols;
+ private readonly MirandaPluginLink pluginLink;
+ private readonly ContactList contactList;
+
+ #endregion
+
+ #region .ctors
+
+ private MirandaContext(PluginManagerBase pluginManager, MirandaPluginLink mirandaLink, bool skipContextInfo)
+ {
+ if (mirandaLink == null)
+ throw new ArgumentNullException("mirandaLink");
+
+ this.pluginManager = pluginManager;
+ this.mirandaDatabase = new MirandaDatabase();
+ this.pluginLink = mirandaLink;
+ this.contactList = new ContactList();
+ this.serviceInterceptors = new ServiceCallInterceptionManager();
+
+ GetMMInterface();
+
+ PopulateEnvironmentInformation();
+
+ if (!skipContextInfo)
+ PopulateContextInformation();
+ else
+ this.protocols = new ProtocolDictionary(0);
+ }
+
+ ///
+ /// Initializes a context from a plugin link.
+ ///
+ ///
+ ///
+ internal static void InitializeCurrent(MirandaPluginLink mirandaLink, bool skipContextPopulation)
+ {
+ InitializeCurrent(mirandaLink, null, skipContextPopulation);
+ }
+
+ internal static void InitializeCurrent(MirandaPluginLink mirandaLink, PluginManagerBase pluginManager)
+ {
+ InitializeCurrent(mirandaLink, pluginManager, false);
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal static void InitializeCurrent(MirandaPluginLink mirandaLink, PluginManagerBase pluginManager, bool skipContextPopulation)
+ {
+ if (singleton == null)
+ singleton = new MirandaContext(pluginManager, mirandaLink, skipContextPopulation);
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal static void InitializeCurrent(MirandaContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ if (singleton == null)
+ singleton = context;
+ else
+ throw new InvalidOperationException();
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal static void InvalidateCurrent()
+ {
+ if (Initialized)
+ {
+ singleton.DetachPluginManager();
+ singleton = null;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal void AssociatePluginManager(PluginManagerBase manager)
+ {
+ if (manager == null)
+ throw new ArgumentNullException("manager");
+
+ pluginManager = manager;
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ internal void DetachPluginManager()
+ {
+ pluginManager = null;
+ }
+
+ #endregion
+
+ #region Initialization
+
+ private void GetMMInterface()
+ {
+ mirandaMemoryManager = new MM_INTERFACE();
+ mirandaMemoryManager.Size = Marshal.SizeOf(typeof(MM_INTERFACE));
+
+ UnmanagedStructHandle mmiHandle = new UnmanagedStructHandle(ref mirandaMemoryManager);
+
+ try
+ {
+ if (CallService(MS_SYSTEM_GET_MMI, IntPtr.Zero, mmiHandle.IntPtr) == CallbackResult.Success)
+ mmiHandle.MarshalBack(out mirandaMemoryManager);
+ else
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_SYSTEM_GET_MMI, "1"));
+ }
+ finally
+ {
+ mmiHandle.Free();
+ }
+ }
+
+ internal void PopulateContextInformation()
+ {
+ PopulateNetworkProtocols();
+ }
+
+ private unsafe void PopulateNetworkProtocols()
+ {
+ try
+ {
+ int count;
+ PROTOCOLDESCRIPTOR** pointerArrayPtr;
+
+ int result = CallServiceUnsafe(MirandaServices.MS_PROTO_ENUMPROTOCOLS, &count, &pointerArrayPtr);
+ if (result != 0) throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MirandaServices.MS_PROTO_ENUMPROTOCOLS, result.ToString()));
+
+ ProtocolDictionary protocols = new ProtocolDictionary(count);
+
+ for (int i = 0; i < count; i++)
+ {
+ // *(ptr_to_array_of_ptrs + i * sizeof(PROTOCOLDESCRIPTOR)) = *ptr_to_ptr = *ptr = data
+ PROTOCOLDESCRIPTOR nativeDescriptor = **(((PROTOCOLDESCRIPTOR**)pointerArrayPtr) + i);
+ Protocol protocol = new Protocol(ref nativeDescriptor);
+
+ protocols.Add(protocol.Name, protocol);
+ }
+
+ this.protocols = protocols;
+ }
+ catch (Exception)
+ {
+ this.protocols = new ProtocolDictionary(0);
+ }
+ }
+
+ private void PopulateEnvironmentInformation()
+ {
+ InteropBuffer buffer = InteropBufferPool.AcquireBuffer();
+
+ try
+ {
+ buffer.Lock();
+
+ int result = CallService(MS_SYSTEM_GETVERSIONTEXT, buffer.SizeAsUIntPtr, buffer.IntPtr);
+ Debug.Assert(result == 0);
+
+ if (result == 0 && Translate.ToString(buffer.IntPtr, StringEncoding.Ansi).IndexOf("Unicode") != -1)
+ MirandaEnvironment.MirandaStringEncoding = StringEncoding.Unicode;
+ else
+ MirandaEnvironment.MirandaStringEncoding = StringEncoding.Ansi;
+ }
+ finally
+ {
+ buffer.Unlock();
+ InteropBufferPool.ReleaseBuffer(buffer);
+ }
+
+ MirandaEnvironment.MirandaVersion = Translate.FromMirandaVersion((uint)CallService(MirandaServices.MS_SYSTEM_GETVERSION));
+ }
+
+ #endregion
+
+ #region Properties
+
+ internal MirandaPluginLink PluginLink
+ {
+ get
+ {
+ return this.pluginLink;
+ }
+ }
+
+ private MM_INTERFACE mirandaMemoryManager;
+ internal MM_INTERFACE MirandaMemoryManager
+ {
+ get { return mirandaMemoryManager; }
+ }
+
+ internal PluginManagerBase PluginManager
+ {
+ get
+ {
+ if (this.pluginManager == null)
+ throw new InvalidOperationException("No plugin manager associated with this context.");
+
+ return this.pluginManager;
+ }
+ }
+
+ public bool HasPluginManager
+ {
+ get
+ {
+ return this.pluginManager != null;
+ }
+ }
+
+ public static MirandaContext Current
+ {
+ get
+ {
+ if (singleton == null)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_MirandaContextNotAvailable);
+
+ return singleton;
+ }
+ }
+
+ public static bool Initialized
+ {
+ get
+ {
+ return singleton != null;
+ }
+ }
+
+ public ServiceCallInterceptionManager ServiceCallInterceptors
+ {
+ get
+ {
+ return this.serviceInterceptors;
+ }
+ }
+
+ public MirandaDatabase MirandaDatabase
+ {
+ get
+ {
+ return this.mirandaDatabase;
+ }
+ }
+
+ public ProtocolDictionary Protocols
+ {
+ get
+ {
+ return this.protocols;
+ }
+ }
+
+ public ContactList ContactList
+ {
+ get { return contactList; }
+ }
+
+ #endregion
+
+ #region Events
+
+ public event EventHandler ModulesLoaded;
+
+ internal void RaiseModulesLoadedEvent()
+ {
+ if (ModulesLoaded != null)
+ ModulesLoaded(this, EventArgs.Empty);
+ }
+
+ internal event EventHandler IsolatedModePluginsUnloading;
+
+ internal void RaiseIsolatedModePluginsUnloadingEvent()
+ {
+ if (IsolatedModePluginsUnloading != null)
+ IsolatedModePluginsUnloading(null, EventArgs.Empty);
+ }
+
+ #endregion
+
+ #region CallService
+
+ public int CallService(string serviceName)
+ {
+ return CallService(serviceName, UIntPtr.Zero, IntPtr.Zero, false);
+ }
+
+ public int CallService(string serviceName, IntPtr wParam, IntPtr lParam)
+ {
+ return CallService(serviceName, Translate.ToHandle(wParam), lParam, false);
+ }
+
+ [CLSCompliant(false)]
+ public int CallService(string serviceName, UIntPtr wParam, IntPtr lParam)
+ {
+ return CallService(serviceName, wParam, lParam, false);
+ }
+
+ internal int CallService(string serviceName, UIntPtr wParam, IntPtr lParam, bool noInterception)
+ {
+ if (String.IsNullOrEmpty(serviceName))
+ throw new ArgumentNullException("service");
+
+ int returnCode =
+ serviceInterceptors.RequiresInterception(serviceName) && !noInterception ?
+ serviceInterceptors[serviceName](wParam, lParam) : pluginLink.NativePluginLink.CallService(serviceName, wParam, lParam);
+
+ return returnCode;
+ }
+
+ internal unsafe int CallServiceUnsafe(string serviceName, void* wParam, void* lParam)
+ {
+ return pluginLink.CallServiceUnsafe(serviceName, wParam, lParam);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaDatabase.cs b/Hyphen/Plugins/Infrastructure/MirandaDatabase.cs
new file mode 100644
index 0000000..9fed1a1
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaDatabase.cs
@@ -0,0 +1,507 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins;
+using System.Runtime.CompilerServices;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Threading;
+using System.Collections.ObjectModel;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Helpers;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public sealed class MirandaDatabase : EventPublisher
+ {
+ #region Constants
+
+ private const string ME_DB_EVENT_ADDED = "DB/Event/Added",
+ ME_DB_EVENT_DELETED = "DB/Event/Deleted",
+ ME_DB_EVENT_FILTER_ADD = "DB/Event/FilterAdd",
+ ME_DB_CONTACT_ADDED = "DB/Contact/Added",
+ ME_DB_CONTACT_DELETED = "DB/Contact/Deleted",
+ ME_DB_CONTACT_SETTINGCHANGED = "DB/Contact/SettingChanged";
+
+ private const string MS_DB_GETPROFILENAME = "DB/GetProfileName",
+ MS_DB_GETPROFILEPATH = "DB/GetProfilePath",
+ MS_DB_EVENT_ADD = "DB/Event/Add";
+
+ private const string MS_DB_CONTACT_GETCOUNT = "DB/Contact/GetCount",
+ MS_DB_CONTACT_FINDFIRST = "DB/Contact/FindFirst",
+ MS_DB_CONTACT_FINDNEXT = "DB/Contact/FindNext";
+
+ private const string MS_DB_EVENT_FINDFIRST = "DB/Event/FindFirst",
+ MS_DB_EVENT_FINDNEXT = "DB/Event/FindNext";
+
+ #endregion
+
+ #region .ctors
+
+ internal MirandaDatabase() { }
+
+ #endregion
+
+ #region Event handlers
+
+ private MirandaEventHandler EventAddedEventHandler,
+ EventDeletedEventHandler, BeforeEventAddedEventHandler;
+
+ private MirandaEventHandler ContactAddedEventHandler,
+ ContactDeletedEventHandler;
+
+ private MirandaEventHandler ContactSettingChangedEventHandler;
+
+ #endregion
+
+ #region Events & Triggers
+
+ private int RaiseDbEvent(MirandaEventHandler handler, bool fromPointer, UIntPtr wParam, IntPtr lParam)
+ {
+ if (handler == null)
+ return 0;
+
+ ContactInfo contactInfo = ContactInfo.FromHandle(wParam);
+ DatabaseEventInfo eventInfo = fromPointer ? DatabaseEventInfo.FromPointer(lParam) : DatabaseEventInfo.FromHandle(lParam);
+ MirandaDatabaseEventArgs eventArgs = new MirandaDatabaseEventArgs(contactInfo, eventInfo);
+
+ bool retValue = InvokeChainCancelable(handler, eventArgs);
+ return Convert.ToInt32(retValue);
+ }
+
+ public event MirandaEventHandler EventAdded
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref EventAddedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_DB_EVENT_ADDED,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ return RaiseDbEvent(EventAddedEventHandler, false, wParam, lParam);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref EventAddedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_DB_EVENT_ADDED, EventAddedEventHandler);
+ }
+ }
+
+ public event MirandaEventHandler EventDeleted
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref EventDeletedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_DB_EVENT_DELETED,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ return RaiseDbEvent(EventDeletedEventHandler, false, wParam, lParam);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref EventDeletedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_DB_EVENT_DELETED, EventDeletedEventHandler);
+ }
+ }
+
+ ///
+ /// Return TRUE to filter out the event, FALSE to pass the message along.
+ ///
+ public event MirandaEventHandler BeforeEventAdded
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref BeforeEventAddedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_DB_EVENT_FILTER_ADD,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ return RaiseDbEvent(BeforeEventAddedEventHandler, true, wParam, lParam);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref BeforeEventAddedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_DB_EVENT_FILTER_ADD, BeforeEventAddedEventHandler);
+ }
+ }
+
+ private int RaiseContactEvent(MirandaEventHandler handler, UIntPtr wParam)
+ {
+ if (handler == null)
+ return 0;
+
+ ContactInfo contactInfo = GetContactInfo(wParam);
+ MirandaContactEventArgs eventArgs = new MirandaContactEventArgs(contactInfo);
+
+ bool retValue = InvokeChainCancelable(handler, eventArgs);
+ return Convert.ToInt32(retValue);
+ }
+
+ private static ContactInfo GetContactInfo(UIntPtr wParam)
+ {
+ if (wParam == UIntPtr.Zero)
+ return ContactInfo.MeNeutral;
+ else
+ return ContactInfo.FromHandle(wParam);
+ }
+
+ private unsafe int RaiseContactSettingEvent(UIntPtr hContact, IntPtr pDbWriteSetting)
+ {
+ DBCONTACTWRITESETTING dbWriteSetting = *(DBCONTACTWRITESETTING*)pDbWriteSetting.ToPointer();
+ ContactInfo contactInfo = GetContactInfo(hContact);
+
+ string name = Translate.ToString(dbWriteSetting.Name, StringEncoding.Ansi);
+ string moduleName = Translate.ToString(dbWriteSetting.Module, StringEncoding.Ansi);
+ object value = null;
+
+ if ((DatabaseSettingType)dbWriteSetting.Value.Type != DatabaseSettingType.Blob)
+ value = DBCONTACTWRITESETTING.ExtractValue(pDbWriteSetting);
+ else
+ Debugger.Log(10, Loader.LogCategory, "Blob settings are not yet supported, the value will be null.");
+
+ MirandaContactSettingEventArgs eventArgs = new MirandaContactSettingEventArgs(contactInfo, name, moduleName, value, (DatabaseSettingType)dbWriteSetting.Value.Type);
+
+ bool retValue = InvokeChainCancelable(ContactSettingChangedEventHandler, eventArgs);
+ return Convert.ToInt32(retValue);
+ }
+
+ public event MirandaEventHandler ContactAdded
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref ContactAddedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_DB_CONTACT_ADDED,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ return RaiseContactEvent(ContactAddedEventHandler, wParam);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref ContactAddedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_DB_CONTACT_ADDED, ContactAddedEventHandler);
+ }
+ }
+
+ public event MirandaEventHandler ContactDeleted
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref ContactDeletedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_DB_CONTACT_DELETED,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ return RaiseContactEvent(ContactDeletedEventHandler, wParam);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref ContactDeletedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_DB_CONTACT_DELETED, ContactDeletedEventHandler);
+ }
+ }
+
+ public event MirandaEventHandler ContactSettingChanged
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref ContactSettingChangedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_DB_CONTACT_SETTINGCHANGED,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ return RaiseContactSettingEvent(wParam, lParam);
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref ContactSettingChangedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_DB_CONTACT_SETTINGCHANGED, ContactSettingChangedEventHandler);
+ }
+ }
+
+ #endregion
+
+ #region Properties
+
+ #region Profile
+
+ public string ProfileName
+ {
+ get
+ {
+ InteropBuffer buffer = InteropBufferPool.AcquireBuffer();
+
+ try
+ {
+ buffer.Lock();
+
+ int result = MirandaContext.Current.CallService(MS_DB_GETPROFILENAME, buffer.SizeAsUIntPtr, buffer.IntPtr);
+ Debug.Assert(result == 0);
+
+ if (result != 0) return null;
+ return Translate.ToString(buffer.IntPtr, StringEncoding.Ansi);
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(TextResources.ExceptionMsg_ErrorWhileCallingMirandaService, e);
+ }
+ finally
+ {
+ buffer.Unlock();
+ InteropBufferPool.ReleaseBuffer(buffer);
+ }
+ }
+ }
+
+ public string ProfilePath
+ {
+ get
+ {
+ InteropBuffer buffer = InteropBufferPool.AcquireBuffer();
+
+ try
+ {
+ buffer.Lock();
+
+ int result = MirandaContext.Current.CallService(MS_DB_GETPROFILEPATH, buffer.SizeAsUIntPtr, buffer.IntPtr);
+ Debug.Assert(result == 0);
+
+ if (result != 0) return null;
+ return Translate.ToString(buffer.IntPtr, StringEncoding.Ansi);
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(TextResources.ExceptionMsg_ErrorWhileCallingMirandaService, e);
+ }
+ finally
+ {
+ buffer.Unlock();
+ InteropBufferPool.ReleaseBuffer(buffer);
+ }
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Methods
+
+ #region Contacts
+
+ ///
+ /// Enumerates contact handles, excluding the Me contact.
+ ///
+ /// Contact handles.
+ public IEnumerable GetContactHandles()
+ {
+ MirandaContext context = MirandaContext.Current;
+ Callback findNext = ServiceManager.GetService(MS_DB_CONTACT_FINDNEXT);
+
+ UIntPtr handle = (UIntPtr)(uint)context.CallService(MS_DB_CONTACT_FINDFIRST);
+
+ do
+ {
+ if (handle != UIntPtr.Zero)
+ yield return Translate.ToHandle(handle);
+ }
+ while ((handle = (UIntPtr)(uint)findNext(handle, IntPtr.Zero)) != UIntPtr.Zero);
+ }
+
+ public ReadOnlyCollection GetContacts()
+ {
+ return GetContacts(false);
+ }
+
+ public ReadOnlyCollection GetContacts(bool includeSelf)
+ {
+ MirandaContext context = MirandaContext.Current;
+ Callback findNext = ServiceManager.GetService(MS_DB_CONTACT_FINDNEXT);
+
+ List contacts = new List(context.CallService(MS_DB_CONTACT_GETCOUNT));
+
+ if (includeSelf)
+ contacts.Add(ContactInfo.MeNeutral);
+
+ foreach (IntPtr handle in GetContactHandles())
+ contacts.Add(ContactInfo.FromHandle(handle));
+
+ return contacts.AsReadOnly();
+ }
+
+ public ContactInfo FindContact(string uuid)
+ {
+ return FindContact(uuid, ContactInfoProperty.UniqueID, StringEncoding.Ansi);
+ }
+
+ public ContactInfo FindContact(string searchValue, ContactInfoProperty searchCriterion, StringEncoding valueEncoding)
+ {
+ return FindContact(searchValue, searchCriterion, valueEncoding, StringComparison.Ordinal);
+ }
+
+ public ContactInfo FindContact(string searchValue, ContactInfoProperty searchCriterion, StringEncoding valueEncoding, StringComparison comparisonType)
+ {
+ if (searchValue == null)
+ throw new ArgumentNullException("searchValues");
+
+ foreach (IntPtr handle in GetContactHandles())
+ {
+ object value;
+ ContactInfoPropertyType type;
+
+ if (ContactInfo.GetProperty(handle, searchCriterion, out value, out type)
+ && searchValue.Equals(value.ToString(), comparisonType))
+ return ContactInfo.FromHandle(handle);
+ }
+
+ Debug.Assert(false);
+ return null;
+ }
+
+ public ContactInfo[] FindContacts(params string[] uuids)
+ {
+ if (uuids == null)
+ throw new ArgumentNullException("uuids");
+
+ List results = new List(uuids.Length);
+
+ foreach (string uuid in uuids)
+ {
+ ContactInfo contact = FindContact(uuid, ContactInfoProperty.UniqueID, StringEncoding.Ansi);
+
+ if (contact != null)
+ results.Add(contact);
+ }
+
+ return results.ToArray();
+ }
+
+ #endregion
+
+ #region Events
+
+ public IntPtr AddEvent(ContactInfo associatedContact, object data, ISettingOwner owner, DatabaseEventType type, DatabaseEventProperties flags, DateTime? timestamp)
+ {
+ return AddEvent(associatedContact, data, owner, type, flags, timestamp, true);
+ }
+
+ public IntPtr AddEvent(ContactInfo associatedContact, object data, ISettingOwner owner, DatabaseEventType type, DatabaseEventProperties flags, DateTime? timestamp, bool throwOnFailure)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ return AddEvent(associatedContact, data, owner.Name, type, flags, timestamp, throwOnFailure);
+ }
+
+ public IntPtr AddEvent(ContactInfo associatedContact, object data, string owner, DatabaseEventType type, DatabaseEventProperties flags, DateTime? timestamp, bool throwOnFailure)
+ {
+ if (associatedContact == null)
+ throw new ArgumentNullException("associatedContact");
+
+ if (String.IsNullOrEmpty(owner))
+ throw new ArgumentNullException("owner");
+
+ if (data == null)
+ throw new ArgumentNullException("data");
+
+ IntPtr pBlob = IntPtr.Zero;
+ UnmanagedStructHandle nativeStruct = UnmanagedStructHandle.Empty;
+
+ try
+ {
+ int totalBytes;
+
+ if (data is string)
+ {
+ totalBytes = DBEVENTINFO.LayoutAnsiUniString((string)data, out pBlob);
+ }
+ else if (data is byte[])
+ {
+ byte[] dataBytes = (byte[])data;
+ totalBytes = dataBytes.Length;
+
+ pBlob = Marshal.AllocHGlobal(totalBytes);
+ Marshal.Copy(dataBytes, 0, pBlob, dataBytes.Length);
+ }
+ else
+ throw new ArgumentOutOfRangeException("data");
+
+ DBEVENTINFO info = new DBEVENTINFO(0, IntPtr.Zero);
+ info.Module = Translate.ToHandle(owner, StringEncoding.Ansi).IntPtr;
+ info.BlobSize = (uint)totalBytes;
+ info.BlobPtr = pBlob;
+ info.EventType = (ushort)type;
+ info.Flags = (uint)flags;
+ info.Timestamp = Utilities.GetTimestamp(timestamp.HasValue ? timestamp.Value : DateTime.Now);
+
+ nativeStruct = new UnmanagedStructHandle(ref info, pBlob, info.Module);
+ IntPtr eventHandle = (IntPtr)MirandaContext.Current.CallService(MS_DB_EVENT_ADD, associatedContact.MirandaHandle, nativeStruct.IntPtr);
+
+ if (eventHandle == IntPtr.Zero && throwOnFailure)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_ADD, eventHandle.ToString()));
+ else
+ return eventHandle;
+ }
+ finally
+ {
+ nativeStruct.Free();
+ }
+ }
+
+ public IEnumerable GetEventHandles(ContactInfo owner)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ return GetEventHandles(owner.MirandaHandle);
+ }
+
+ public IEnumerable GetEventHandles(IntPtr ownerHandle)
+ {
+ Callback findNext = ServiceManager.GetService(MS_DB_EVENT_FINDNEXT);
+ IntPtr pEvent = (IntPtr)MirandaContext.Current.CallService(MS_DB_EVENT_FINDFIRST, ownerHandle, IntPtr.Zero);
+
+ while (pEvent != IntPtr.Zero)
+ {
+ yield return pEvent;
+ pEvent = (IntPtr)findNext(Translate.ToHandle(pEvent), IntPtr.Zero);
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaDatabaseEventArgs.cs b/Hyphen/Plugins/Infrastructure/MirandaDatabaseEventArgs.cs
new file mode 100644
index 0000000..e92b80c
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaDatabaseEventArgs.cs
@@ -0,0 +1,49 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public class MirandaDatabaseEventArgs : MirandaContactEventArgs
+ {
+ #region .ctors
+
+ public MirandaDatabaseEventArgs(ContactInfo contact, DatabaseEventInfo eventInfo) : base(contact)
+ {
+ if (eventInfo == null)
+ throw new ArgumentNullException("eventInfo");
+
+ this.eventInfo = eventInfo;
+ }
+
+ #endregion
+
+ #region Properties
+
+ private readonly DatabaseEventInfo eventInfo;
+ public DatabaseEventInfo EventInfo
+ {
+ get { return eventInfo; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaEnvironment.cs b/Hyphen/Plugins/Infrastructure/MirandaEnvironment.cs
new file mode 100644
index 0000000..3936149
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaEnvironment.cs
@@ -0,0 +1,123 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.IO;
+using Virtuoso.Hyphen;
+using System.Windows.Forms;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public static class MirandaEnvironment
+ {
+ #region Fields
+
+ private static Version mirandaVersion;
+ private static StringEncoding mirandaStringEncoding;
+
+ internal const string MirandaPluginsFolderRelativePath = @"plugins\",
+ ManagedPluginsFolderName = "managed",
+ ManagedPluginsFolderRelativePath = MirandaPluginsFolderRelativePath + ManagedPluginsFolderName;
+
+ private static readonly string mirandaFolderPath = Application.StartupPath,
+ mirandaPluginsFolderPath = Path.Combine(mirandaFolderPath, MirandaPluginsFolderRelativePath),
+ managedPluginsFolderPath = Path.Combine(mirandaFolderPath, ManagedPluginsFolderRelativePath),
+ mirandaBootIniPath = mirandaFolderPath + @"\MirandaBoot.ini";
+
+ #endregion
+
+ #region Methods
+
+ public static string GetManagedSubdirectoryRelativePath(string subDir)
+ {
+ return Path.Combine(ManagedPluginsFolderRelativePath, subDir);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public static StringEncoding MirandaStringEncoding
+ {
+ get
+ {
+ return mirandaStringEncoding;
+ }
+ internal set
+ {
+ mirandaStringEncoding = value;
+ }
+ }
+
+ public static string MirandaFolderPath
+ {
+ get
+ {
+ return mirandaFolderPath;
+ }
+ }
+
+ public static string MirandaPluginsFolderPath
+ {
+ get
+ {
+ return mirandaPluginsFolderPath;
+ }
+ }
+
+ public static string ManagedPluginsFolderPath
+ {
+ get
+ {
+ return managedPluginsFolderPath;
+ }
+ }
+
+ public static Version MirandaVersion
+ {
+ get
+ {
+ return mirandaVersion;
+ }
+ internal set
+ {
+ mirandaVersion = value;
+ }
+ }
+
+ public static Version HyphenVersion
+ {
+ get
+ {
+ return Loader.HyphenVersion;
+ }
+ }
+
+ public static string MirandaBootIniPath
+ {
+ get
+ {
+ return mirandaBootIniPath;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaEventArgs.cs b/Hyphen/Plugins/Infrastructure/MirandaEventArgs.cs
new file mode 100644
index 0000000..3078cbd
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaEventArgs.cs
@@ -0,0 +1,29 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public abstract class MirandaEventArgs : EventArgs
+ {
+ protected MirandaEventArgs() { }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaEventHandler.cs b/Hyphen/Plugins/Infrastructure/MirandaEventHandler.cs
new file mode 100644
index 0000000..b6c897f
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaEventHandler.cs
@@ -0,0 +1,32 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ ///
+ /// Represents a generic handler for Miranda-raised events.
+ ///
+ /// Type of EventArgs.
+ /// Sender of the event.
+ /// Event arguments.
+ /// TRUE to block the event from bubbling to another subscribers, FALSE to pass it along.
+ public delegate bool MirandaEventHandler(object sender, TEventArgs e) where TEventArgs : MirandaEventArgs;
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaEvents.cs b/Hyphen/Plugins/Infrastructure/MirandaEvents.cs
new file mode 100644
index 0000000..a6a16c5
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaEvents.cs
@@ -0,0 +1,35 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal static class MirandaEvents
+ {
+ public const string ME_SYSTEM_SHUTDOWN = "Miranda/System/Shutdown",
+ ME_SYSTEM_OKTOEXIT = "Miranda/System/OkToExitEvent",
+ ME_DB_EVENT_ADDED = "DB/Event/Added",
+ ME_DB_EVENT_DELETED = "DB/Event/Deleted",
+ ME_DB_CONTACT_ADDED = "DB/Contact/Added",
+ ME_DB_CONTACT_DELETED = "DB/Contact/Deleted",
+ ME_DB_CONTACT_SETTINGCHANGED = "DB/Contact/SettingChanged",
+ ME_SYSTEM_MODULESLOADED = "Miranda/System/ModulesLoaded";
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaItem.cs b/Hyphen/Plugins/Infrastructure/MirandaItem.cs
new file mode 100644
index 0000000..31a7787
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaItem.cs
@@ -0,0 +1,73 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public enum ItemType : int
+ {
+ Unspecified,
+ Contact,
+ Group
+ }
+
+ public class MirandaItem : MirandaObject
+ {
+ #region Fields
+
+ private static readonly Type ItemTypeType = typeof(ItemType);
+
+ private ItemType type;
+
+ #endregion
+
+ #region .ctors
+
+ protected MirandaItem(IntPtr handle, ItemType type) : this(handle)
+ {
+ if (!Enum.IsDefined(ItemTypeType, type))
+ throw new ArgumentOutOfRangeException("type");
+
+ if (type == ItemType.Unspecified)
+ throw new ArgumentException("Unspecified type is not supported.");
+
+ this.type = type;
+ }
+
+ internal MirandaItem(IntPtr handle)
+ {
+ this.MirandaHandle = handle;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public ItemType Type
+ {
+ get
+ {
+ return this.type;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaObject.cs b/Hyphen/Plugins/Infrastructure/MirandaObject.cs
new file mode 100644
index 0000000..64414cd
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaObject.cs
@@ -0,0 +1,48 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public abstract class MirandaObject : IMirandaObject
+ {
+ #region Fields
+
+ private IntPtr mirandaHandle;
+
+ #endregion
+
+ #region .ctors
+
+ protected MirandaObject() { }
+
+ #endregion
+
+ #region Properties
+
+ public IntPtr MirandaHandle
+ {
+ get { return mirandaHandle; }
+ protected internal set { mirandaHandle = value; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/MirandaServices.cs b/Hyphen/Plugins/Infrastructure/MirandaServices.cs
new file mode 100644
index 0000000..97be71a
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/MirandaServices.cs
@@ -0,0 +1,44 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal static class MirandaServices
+ {
+ public const string MS_CLIST_ADDMAINMENUITEM = "CList/AddMainMenuItem",
+ MS_CLIST_ADDCONTACTMENUITEM = "CList/AddContactMenuItem",
+ MS_CLIST_MODIFYMENUITEM = "CList/ModifyMenuItem",
+ MS_CLIST_SYSTRAY_NOTIFY = "Miranda/Systray/Notify",
+ MS_CONTACT_GETCONTACTINFO = "Miranda/Contact/GetContactInfo",
+
+ MS_PROTO_GETCONTACTBASEPROTO = "Proto/GetContactBaseProto",
+ MS_PROTO_ENUMPROTOCOLS = "Proto/EnumProtocols",
+ MS_PROTO_CALLCONTACTSERVICE = "Proto/CallContactService",
+ MS_PROTO_REGISTERMODULE = "Proto/RegisterModule",
+ PS_MESSAGE = "/SendMsg",
+ MS_SYSTEM_GETVERSION = "Miranda/System/GetVersion";
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/PluginConfiguration.cs b/Hyphen/Plugins/Infrastructure/PluginConfiguration.cs
new file mode 100644
index 0000000..bc8207b
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/PluginConfiguration.cs
@@ -0,0 +1,277 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization.Formatters.Binary;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Configuration;
+using Virtuoso.Miranda.Plugins.Helpers;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [Serializable]
+ public abstract class PluginConfiguration
+ {
+ #region Fields
+
+ private static readonly object SyncObject = new object();
+
+ private static readonly TypeInstanceCache Stores = new TypeInstanceCache();
+ private static readonly TypeInstanceCache Encryptions = new TypeInstanceCache();
+
+ internal readonly ConfigurationValues values;
+
+ private bool isDirty;
+
+ #endregion
+
+ #region .ctors
+
+ protected PluginConfiguration()
+ {
+ values = new ConfigurationValues();
+ }
+
+ protected virtual void InitializeDefaultConfiguration() { }
+
+ #endregion
+
+ #region Events
+
+ [field: NonSerialized]
+ public event EventHandler ConfigurationChanged;
+
+ protected void RaiseChangedEvent()
+ {
+ if (ConfigurationChanged != null)
+ ConfigurationChanged(this, EventArgs.Empty);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public ConfigurationValues Values
+ {
+ get
+ {
+ return values;
+ }
+ }
+
+ public bool IsDirty
+ {
+ get
+ {
+ return isDirty;
+ }
+ protected internal set
+ {
+ isDirty = value;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected virtual void OnBeforeSerialization() { }
+
+ protected virtual void OnAfterDeserialization() { }
+
+ protected void MarkDirty()
+ {
+ IsDirty = true;
+ RaiseChangedEvent();
+ }
+
+ #region Helpers
+
+ private static ConfigurationOptionsAttribute GetOptions(Type configType)
+ {
+ if (configType == null)
+ throw new ArgumentNullException("configType");
+
+ ConfigurationOptionsAttribute options = null;
+ Type configAttribType = typeof(ConfigurationOptionsAttribute);
+
+ if (configType.IsDefined(configAttribType, false))
+ options = (ConfigurationOptionsAttribute)configType.GetCustomAttributes(configAttribType, false)[0];
+ else
+ options = new ConfigurationOptionsAttribute();
+
+ return options.Finalize();
+ }
+
+ private static byte[] FetchStream(Stream stream)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ if (!stream.CanRead)
+ throw new ArgumentException();
+
+ byte[] buffer = new byte[stream.Length];
+
+ if (stream.Read(buffer, 0, buffer.Length) != stream.Length)
+ throw new IOException();
+
+ return buffer;
+ }
+
+ internal static void FlushCaches()
+ {
+ lock (SyncObject)
+ {
+ foreach (IStorage storage in Stores.Values)
+ storage.Dispose();
+
+ Stores.Clear();
+ Encryptions.Clear();
+ }
+ }
+
+ #endregion
+
+ #region Save
+
+ public void Save()
+ {
+ try
+ {
+ lock (SyncObject)
+ {
+ OnBeforeSerialization();
+
+ ConfigurationOptionsAttribute options = GetOptions(GetType());
+ IStorage storage = Stores.Instantiate(options.Storage);
+
+ if (options.Encrypt)
+ SerializeEncrypted(storage, options);
+ else
+ Serialize(storage, options);
+ }
+ }
+ catch (IsolatedStorageException isE)
+ {
+ throw new ConfigurationException(TextResources.ExceptionMsg_UnableToSaveConfiguration_StorageError, isE);
+ }
+ }
+
+ private void Serialize(IStorage storage, ConfigurationOptionsAttribute options)
+ {
+ BinaryFormatter serializer = new BinaryFormatter();
+
+ using (Stream stream = storage.OpenWrite(GetType(), options))
+ serializer.Serialize(stream, this);
+ }
+
+ private void SerializeEncrypted(IStorage storage, ConfigurationOptionsAttribute options)
+ {
+ IEncryption encryption = Encryptions.Instantiate(options.Encryption);
+
+ using (Stream serializationStream = new MemoryStream(2048))
+ {
+ new BinaryFormatter().Serialize(serializationStream, this);
+ serializationStream.Seek(0, SeekOrigin.Begin);
+
+ byte[] data = FetchStream(serializationStream);
+ byte[] protectedData = encryption.Encrypt(data);
+
+ using (Stream stream = storage.OpenWrite(GetType(), options))
+ stream.Write(protectedData, 0, protectedData.Length);
+ }
+ }
+
+ #endregion
+
+ #region Load
+
+ public static T Load() where T : PluginConfiguration
+ {
+ try
+ {
+ lock (SyncObject)
+ {
+ Type configType = typeof(T);
+ ConfigurationOptionsAttribute options = GetOptions(configType);
+
+ IStorage storage = Stores.Instantiate(options.Storage);
+
+ if (!storage.Exists(configType, options))
+ return GetDefaultConfiguration();
+
+ using (Stream stream = storage.OpenRead(configType, options))
+ {
+ T result = null;
+
+ if (options.Encrypt)
+ result = DeserializeEncrypted(stream, options);
+ else
+ result = Deserialize(stream);
+
+ result.OnAfterDeserialization();
+ return result;
+ }
+ }
+ }
+ catch (OperationCanceledException e)
+ {
+ throw new ConfigurationException(TextResources.ExceptionMsg_UnableToLoadConfiguration_StorageError, e);
+ }
+ catch (Exception e)
+ {
+ T defaults = GetDefaultConfiguration();
+ defaults.Save();
+
+ throw new ConfigurationException(TextResources.ExceptionMsg_UnableToLoadConfiguration_StorageError, e);
+ }
+ }
+
+ private static T Deserialize(Stream stream) where T : PluginConfiguration
+ {
+ return new BinaryFormatter().Deserialize(stream) as T;
+ }
+
+ private static T DeserializeEncrypted(Stream stream, ConfigurationOptionsAttribute options) where T : PluginConfiguration
+ {
+ byte[] protectedData = FetchStream(stream);
+ byte[] data = Encryptions.Instantiate(options.Encryption).Decrypt(protectedData);
+
+ using (Stream serializedStream = new MemoryStream(data))
+ return new BinaryFormatter().Deserialize(serializedStream) as T;
+ }
+
+ public static TConfig GetDefaultConfiguration() where TConfig : PluginConfiguration
+ {
+ TConfig result = Activator.CreateInstance(typeof(TConfig), true) as TConfig;
+
+ if (result == null)
+ throw new ConfigurationException();
+
+ result.InitializeDefaultConfiguration();
+ return result;
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocol.cs b/Hyphen/Plugins/Infrastructure/Protocol.cs
new file mode 100644
index 0000000..fff89ba
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocol.cs
@@ -0,0 +1,225 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Hyphen;
+using System.Runtime.CompilerServices;
+using Virtuoso.Miranda.Plugins.Infrastructure.Protocols;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public class Protocol : ContextWorker, ISettingOwner
+ {
+ #region Constants
+
+ internal const string PS_GETSTATUS = "/GetStatus";
+ internal const string PS_SETSTATUS = "/SetStatus";
+ internal const string PS_GETCAPS = "/GetCaps";
+ internal const string PS_GETNAME = "/GetName";
+ internal const string PS_LOADICON = "/LoadIcon";
+ internal const string PSS_MESSAGE = "/SendMsg";
+
+ private const string MS_PROTO_ISPROTOONCONTACT = "Proto/IsProtoOnContact";
+
+ #endregion
+
+ #region Fields
+
+ private static readonly Protocol unknownProtocol = new Protocol();
+
+ private readonly string name;
+ private readonly ProtocolType type;
+
+ #endregion
+
+ #region .ctors
+
+ internal Protocol()
+ {
+ this.name = String.Empty;
+ this.type = ProtocolType.Other;
+ }
+
+ internal Protocol(string name, ProtocolType type)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentNullException("name");
+
+ if (!Enum.IsDefined(typeof(ProtocolType), type))
+ throw new ArgumentOutOfRangeException("type");
+
+ this.name = name;
+ this.type = type;
+ }
+
+ internal Protocol(ref PROTOCOLDESCRIPTOR descriptor)
+ {
+ if (descriptor.Name == IntPtr.Zero)
+ throw new ArgumentException();
+
+ this.name = Translate.ToString(descriptor.Name, StringEncoding.Ansi);
+ this.type = (ProtocolType)descriptor.Type;
+ }
+
+ ~Protocol()
+ {
+ if (namePtr.IsValid)
+ namePtr.Free();
+ }
+
+ #endregion
+
+ #region Events
+
+ private static MirandaEventHandler StatusChangedEventHandler;
+ public static event MirandaEventHandler StatusChanged
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ // Important to check to not hook the Ack for nothing
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ // First subscriber?
+ if (StatusChangedEventHandler == null)
+ AckRouter.AckReceived += AckRouter_AckReceived;
+
+ LazyEventBinder.AttachDelegate>(ref StatusChangedEventHandler, value);
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref StatusChangedEventHandler, value);
+
+ // No more subscribers?
+ if (StatusChangedEventHandler == null)
+ AckRouter.AckReceived -= AckRouter_AckReceived;
+ }
+ }
+
+ private static bool AckRouter_AckReceived(object sender, AckEventArgs e)
+ {
+ if (e.Type == AckType.Status)
+ {
+ if (StatusChangedEventHandler != null)
+ StatusChangedEventHandler(e.Protocol, new ProtocolStatusChangeEventArgs(e.Protocol, (StatusMode)e.LParam));
+ }
+
+ return EventResult.HonourEventChain;
+ }
+
+ #endregion
+
+ #region Methods
+
+ private void CheckUnknown()
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new InvalidOperationException(TextResources.ExceptionMsg_CallInvalidForUnknownNetworkProtocol);
+ }
+
+ [CLSCompliant(false)]
+ public int CallProtocolService(string serviceName, UIntPtr wParam, IntPtr lParam)
+ {
+ if (serviceName == null)
+ throw new ArgumentNullException("serviceName");
+
+ CheckUnknown();
+ return MirandaContext.Current.CallService(GetProtoServiceName(serviceName), wParam, lParam);
+ }
+
+ public string GetProtoServiceName(string service)
+ {
+ if (service == null)
+ throw new ArgumentNullException("service");
+
+ return String.Format("{0}{1}", Name, service);
+ }
+
+ public bool HasInChain(ContactInfo contact)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ return HasInChain(contact.MirandaHandle);
+ }
+
+ public bool HasInChain(IntPtr contactHandle)
+ {
+ return Convert.ToBoolean(Context.CallService(MS_PROTO_ISPROTOONCONTACT, contactHandle, NamePtr));
+ }
+
+ #endregion
+
+ #region Properties
+
+ public static Protocol UnknownProtocol
+ {
+ get { return Protocol.unknownProtocol; }
+ }
+
+ public StatusMode Status
+ {
+ get
+ {
+ return (StatusMode)CallProtocolService(PS_GETSTATUS, UIntPtr.Zero, IntPtr.Zero);
+ }
+ set
+ {
+ CallProtocolService(PS_SETSTATUS, (UIntPtr)value, IntPtr.Zero);
+ }
+ }
+
+ public string Name
+ {
+ get { return name; }
+ }
+
+ public ProtocolType Type
+ {
+ get { return type; }
+ }
+
+ public bool IsUnknown
+ {
+ get
+ {
+ return object.ReferenceEquals(this, UnknownProtocol);
+ }
+ }
+
+ private UnmanagedStringHandle namePtr;
+ protected virtual IntPtr NamePtr
+ {
+ get
+ {
+ if (!namePtr.IsValid)
+ namePtr = Translate.ToHandle(Name, StringEncoding.Ansi);
+
+ return namePtr.IntPtr;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ProtocolStatus.cs b/Hyphen/Plugins/Infrastructure/ProtocolStatus.cs
new file mode 100644
index 0000000..1092301
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ProtocolStatus.cs
@@ -0,0 +1,74 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public static class ProtocolStatus
+ {
+ #region Fields
+
+ private const string MS_AWAYMSG_SHOWAWAYMSG = "SRAway/GetMessage",
+ MS_AWAYMSG_GETSTATUSMSG = "SRAway/GetStatusMessage";
+
+ #endregion
+
+ #region Methods
+
+ public static bool ShowAwayMessage(ContactInfo contact)
+ {
+ int result = MirandaContext.Current.CallService(MS_AWAYMSG_SHOWAWAYMSG, contact.MirandaHandle, IntPtr.Zero);
+ Debug.Assert(result == 0);
+
+ return result == 0;
+ }
+
+ public static string GetStatusMessage(StatusMode status)
+ {
+ if (!Enum.IsDefined(typeof(StatusMode), status))
+ throw new ArgumentOutOfRangeException("status");
+
+ IntPtr statusPtr = IntPtr.Zero;
+
+ try
+ {
+ statusPtr = (IntPtr)MirandaContext.Current.CallService(MS_AWAYMSG_GETSTATUSMSG, (UIntPtr)(ulong)status, IntPtr.Zero);
+ if (statusPtr == IntPtr.Zero) return null;
+
+ return Translate.ToString(statusPtr, Virtuoso.Miranda.Plugins.Native.StringEncoding.Ansi);
+ }
+ catch (Exception e)
+ {
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_AWAYMSG_GETSTATUSMSG, 0), e);
+ }
+ finally
+ {
+ if (statusPtr != IntPtr.Zero)
+ Marshal.FreeHGlobal(statusPtr);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ProtocolStatusChangeEventArgs.cs b/Hyphen/Plugins/Infrastructure/ProtocolStatusChangeEventArgs.cs
new file mode 100644
index 0000000..022e256
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ProtocolStatusChangeEventArgs.cs
@@ -0,0 +1,52 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public class ProtocolStatusChangeEventArgs : MirandaEventArgs
+ {
+ #region Properties
+
+ private StatusMode newStatus;
+ public StatusMode NewStatus
+ {
+ get { return newStatus; }
+ }
+
+ private Protocol protocol;
+ public Protocol Protocol
+ {
+ get { return protocol; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public ProtocolStatusChangeEventArgs(Protocol protocol, StatusMode newStatus)
+ {
+ this.newStatus = newStatus;
+ this.protocol = protocol;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocols/AckBroadcaster.cs b/Hyphen/Plugins/Infrastructure/Protocols/AckBroadcaster.cs
new file mode 100644
index 0000000..3d12eab
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocols/AckBroadcaster.cs
@@ -0,0 +1,90 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using System.Threading;
+using System.Security;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure.Protocols
+{
+ [SuppressUnmanagedCodeSecurity]
+ public static class AckBroadcaster
+ {
+ #region Constants
+
+ private const string MS_PROTO_BROADCASTACK = "Proto/BroadcastAck";
+
+ #endregion
+
+ #region Helpers
+
+ private static ACKDATA BuildAckData(IntPtr pModuleName, AckType type, bool success, IntPtr contactHandle, IntPtr processHandle, IntPtr lParam)
+ {
+ ACKDATA ack = new ACKDATA(pModuleName, (int)type, success ? (int)AckResult.Success : (int)AckResult.Failure);
+ ack.ContactHandle = contactHandle;
+ ack.ProcessHandle = processHandle;
+ ack.LParam = lParam;
+
+ return ack;
+ }
+
+ private static unsafe int BroadcastAck(ACKDATA* ack)
+ {
+ return MirandaContext.Current.CallServiceUnsafe(MS_PROTO_BROADCASTACK, null, ack);
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static int BroadcastAck(string moduleName, AckType type, bool success, IntPtr contactHandle, IntPtr processHandle, IntPtr lParam)
+ {
+ UnmanagedStringHandle pModuleName = UnmanagedStringHandle.Empty;
+
+ try
+ {
+ pModuleName = new UnmanagedStringHandle(moduleName, StringEncoding.Ansi);
+ return BroadcastAck(pModuleName.IntPtr, type, success, contactHandle, processHandle, lParam);
+ }
+ finally
+ {
+ pModuleName.Free();
+ }
+ }
+
+ public static unsafe void BroadcastMessageAckAsync(IntPtr pModuleName, bool success, IntPtr contactHandle, int processCookie)
+ {
+ ACKDATA ack = BuildAckData(pModuleName, AckType.Message, success, contactHandle, (IntPtr)processCookie, IntPtr.Zero);
+ ThreadPool.QueueUserWorkItem(delegate(object ackObject)
+ {
+ ACKDATA _ack = (ACKDATA)ackObject;
+ BroadcastAck(&_ack);
+ }, ack);
+ }
+
+ public static unsafe int BroadcastAck(IntPtr pModuleName, AckType type, bool success, IntPtr contactHandle, IntPtr processHandle, IntPtr lParam)
+ {
+ ACKDATA ack = BuildAckData(pModuleName, type, success, contactHandle, processHandle, lParam);
+ return BroadcastAck(&ack);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocols/AckEnums.cs b/Hyphen/Plugins/Infrastructure/Protocols/AckEnums.cs
new file mode 100644
index 0000000..06423bb
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocols/AckEnums.cs
@@ -0,0 +1,48 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure.Protocols
+{
+ public enum AckType : int
+ {
+ Message = 0,
+ Url = 1,
+ File = 2,
+ Chat = 3,
+ AwayMessage = 4,
+ AuthorizationRequest = 5,
+ Added = 6,
+ GetInfo = 7,
+ SetInfo = 8,
+ Login = 9,
+ Search = 10,
+ NewUser = 11,
+ Status = 12,
+ Contacts = 13, //send/recv of contacts
+ Avatar = 14, //send/recv of avatars from a protocol
+ }
+
+ public enum AckResult : int
+ {
+ Success = 0,
+ Failure = 1,
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocols/AckEventArgs.cs b/Hyphen/Plugins/Infrastructure/Protocols/AckEventArgs.cs
new file mode 100644
index 0000000..d78298f
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocols/AckEventArgs.cs
@@ -0,0 +1,93 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure.Protocols
+{
+ public sealed class AckEventArgs : MirandaEventArgs
+ {
+ #region Properties
+
+ private Protocol protocol;
+ public Protocol Protocol
+ {
+ get { return protocol; }
+ }
+
+ private ContactInfo contact;
+ public ContactInfo Contact
+ {
+ get { return contact; }
+ }
+
+ private AckType type;
+ public AckType Type
+ {
+ get { return type; }
+ }
+
+ private AckResult result;
+ public AckResult Result
+ {
+ get { return result; }
+ }
+
+ private IntPtr processHandle;
+ public IntPtr ProcessHandle
+ {
+ get { return processHandle; }
+ }
+
+ private IntPtr lParam;
+ public IntPtr LParam
+ {
+ get { return lParam; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public AckEventArgs() { }
+
+ internal unsafe static AckEventArgs FromACKDATA(IntPtr pAckData)
+ {
+ if (pAckData == IntPtr.Zero)
+ throw new ArgumentNullException("pAckData");
+
+ ACKDATA ackData = *(ACKDATA*)pAckData.ToPointer();
+ AckEventArgs ackArgs = new AckEventArgs();
+
+ ackArgs.contact = ContactInfo.FromHandle(ackData.ContactHandle);
+ ackArgs.lParam = ackData.LParam;
+ ackArgs.processHandle = ackData.ProcessHandle;
+ ackArgs.result = (AckResult)ackData.Result;
+ ackArgs.type = (AckType)ackData.Type;
+
+ if (ackData.ModuleName != IntPtr.Zero)
+ ackArgs.protocol = MirandaContext.Current.Protocols[Translate.ToString(ackData.ModuleName, StringEncoding.Ansi)];
+
+ return ackArgs;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocols/AckRouter.cs b/Hyphen/Plugins/Infrastructure/Protocols/AckRouter.cs
new file mode 100644
index 0000000..283e3a6
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocols/AckRouter.cs
@@ -0,0 +1,125 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.CompilerServices;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure.Protocols
+{
+ public static class AckRouter
+ {
+ #region Constants
+
+ ///
+ /// Call the next service in the chain for this send operation.
+ /// wParam=wParam
+ /// lParam=lParam
+ /// The return value should be returned immediately
+ /// wParam and lParam should be passed as the parameters that your service was
+ /// called with. wParam must remain untouched but lParam is a CCSDATA structure
+ /// that can be copied and modified if needed.
+ /// Typically, the last line of any chaining protocol function is
+ /// return CallService(MS_PROTO_CHAINSEND,wParam,lParam);
+ ///
+ private const string MS_PROTO_CHAINSEND = "Proto/ChainSend";
+
+ ///
+ /// Call the next service in the chain for this receive operation
+ /// wParam=wParam
+ /// lParam=lParam
+ /// The return value should be returned immediately
+ /// wParam and lParam should be passed as the parameters that your service was
+ /// called with. wParam must remain untouched but lParam is a CCSDATA structure
+ /// that can be copied and modified if needed.
+ /// When being initiated by the network-access protocol module, wParam should be
+ /// zero.
+ /// Thread safety: ms_proto_chainrecv is completely thread safe since 0.1.2.0
+ /// Calls to it are translated to the main thread and passed on from there. The
+ /// function will not return until all callees have returned, irrepective of
+ /// differences between threads the functions are in.
+ ///
+ private const string MS_PROTO_CHAINRECV = "Proto/ChainRecv";
+
+ #endregion
+
+ #region Fields
+
+ private const string ME_PROTO_ACK = "Proto/Ack";
+ private static MirandaEventHandler AckReceivedEventHandler;
+
+ #endregion
+
+ #region Events
+
+ public static event MirandaEventHandler AckReceived
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ add
+ {
+ LazyEventBinder.AttachDelegate>(ref AckReceivedEventHandler, value);
+ LazyEventBinder.HookMirandaEvent(ME_PROTO_ACK,
+ delegate(UIntPtr wParam, IntPtr lParam)
+ {
+ AckEventArgs e = AckEventArgs.FromACKDATA(lParam);
+
+ EventPublisher.InvokeChainCancelable(AckReceivedEventHandler, null, e);
+ return (int)CallbackResult.Success;
+ });
+ }
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ remove
+ {
+ LazyEventBinder.DetachDelegate>(ref AckReceivedEventHandler, value);
+ LazyEventBinder.UnhookMirandaEvent(ME_PROTO_ACK, AckReceivedEventHandler);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static int ChainSend(ContactChainData chainData)
+ {
+ if (chainData == null)
+ throw new ArgumentNullException("chainData");
+
+ return ChainSend(chainData.WParam, chainData.CcsDataPtr);
+ }
+
+ public static int ChainSend(UIntPtr wParam, IntPtr lParam)
+ {
+ return MirandaContext.Current.CallService(MS_PROTO_CHAINSEND, wParam, lParam);
+ }
+
+ public static int ChainReceive(ContactChainData chainData)
+ {
+ if (chainData == null)
+ throw new ArgumentNullException("chainData");
+
+ return ChainReceive(chainData.WParam, chainData.CcsDataPtr);
+ }
+
+ public static int ChainReceive(UIntPtr wParam, IntPtr lParam)
+ {
+ return MirandaContext.Current.CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocols/ContactChainData.cs b/Hyphen/Plugins/Infrastructure/Protocols/ContactChainData.cs
new file mode 100644
index 0000000..c3dde1c
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocols/ContactChainData.cs
@@ -0,0 +1,89 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure.Protocols
+{
+ public sealed class ContactChainData
+ {
+ #region .ctors
+
+ internal unsafe ContactChainData(IntPtr pCcsData)
+ {
+ if (pCcsData == IntPtr.Zero)
+ throw new ArgumentNullException("pCssData");
+
+ this.ccsDataPtr = pCcsData;
+
+ CCSDATA ccsData = *(CCSDATA*)pCcsData.ToPointer();
+ this.contactInfo = ContactInfo.FromHandle(ccsData.ContactHandle);
+ this.wParam = ccsData.WParam;
+ this.lParam = ccsData.LParam;
+ this.serviceName = Translate.ToString(ccsData.ServiceNamePtr, StringEncoding.Ansi);
+ }
+
+ #endregion
+
+ #region Properties
+
+ private ContactInfo contactInfo;
+ public ContactInfo ContactInfo
+ {
+ get { return contactInfo; }
+ }
+
+ private string serviceName;
+ public string ServiceName
+ {
+ get { return serviceName; }
+ }
+
+ private UIntPtr wParam;
+ [CLSCompliant(false)]
+ public UIntPtr WParam
+ {
+ get { return wParam; }
+ }
+
+ private IntPtr lParam;
+ public IntPtr LParam
+ {
+ get { return lParam; }
+ }
+
+ private IntPtr ccsDataPtr;
+ internal IntPtr CcsDataPtr
+ {
+ get { return ccsDataPtr; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public string GetLParamAsString(StringEncoding encoding)
+ {
+ return Translate.ToString(LParam, encoding);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Protocols/ManagedProtocol.cs b/Hyphen/Plugins/Infrastructure/Protocols/ManagedProtocol.cs
new file mode 100644
index 0000000..0f44aa6
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Protocols/ManagedProtocol.cs
@@ -0,0 +1,107 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Helpers;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure.Protocols
+{
+ public sealed class ManagedProtocol : Protocol
+ {
+ #region Constants
+
+ private const string MS_PROTO_ADDTOCONTACT = "Proto/AddToContact";
+
+ private bool Registered;
+
+ #endregion
+
+ #region .ctors
+
+ internal ManagedProtocol(string name, ProtocolType type) : base(name, type) { }
+
+ #endregion
+
+ #region Properties
+
+ private PROTOCOLDESCRIPTOR nativeDescriptor;
+ public PROTOCOLDESCRIPTOR NativeDescriptor
+ {
+ get { return nativeDescriptor; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal void Register()
+ {
+ if (Registered)
+ throw new InvalidOperationException();
+
+ PROTOCOLDESCRIPTOR descriptor = new PROTOCOLDESCRIPTOR(Name, Type);
+ UnmanagedStructHandle nativeHandle = UnmanagedStructHandle.Empty;
+
+ try
+ {
+ nativeHandle = new UnmanagedStructHandle(ref descriptor);
+ int result = Context.CallService(MirandaServices.MS_PROTO_REGISTERMODULE, UIntPtr.Zero, nativeHandle.IntPtr);
+
+ if (result != 0)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, result.ToString()));
+
+ this.nativeDescriptor = descriptor;
+ Registered = true;
+ }
+ finally
+ {
+ nativeHandle.Free();
+ }
+ }
+
+ internal void Unregister()
+ {
+ if (!Registered)
+ throw new InvalidOperationException();
+
+ // Currently, nothing else to do
+ }
+
+ public void AddToContact(ContactInfo contact)
+ {
+ if (contact == null)
+ throw new ArgumentNullException("contact");
+
+ AddToContact(contact.MirandaHandle);
+ }
+
+ public void AddToContact(IntPtr contactHandle)
+ {
+ int result = Context.CallService(MS_PROTO_ADDTOCONTACT, contactHandle, NativeDescriptor.Name);
+
+ if (result != 0)
+ throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_PROTO_ADDTOCONTACT, result.ToString()));
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/PublicEnums.cs b/Hyphen/Plugins/Infrastructure/PublicEnums.cs
new file mode 100644
index 0000000..1a6f060
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/PublicEnums.cs
@@ -0,0 +1,322 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public enum DatabaseSettingType : byte
+ {
+ Deleted = 0, //this setting just got deleted, no other values are valid
+ Byte = 1, //bVal and cVal are valid
+ UInt16 = 2, //wVal and sVal are valid
+ UInt32 = 4, //dVal and lVal are valid
+ AsciiString = 255, //pszVal is valid
+ Blob = 254, //cpbVal and pbVal are valid
+ UTF8String = 253, //pszVal is valid
+ UnicodeString = 252, //pszVal is valid
+ }
+
+ public enum ProtocolType : int
+ {
+ Ignore = 50, // added during v0.3.3
+ Protocol = 1000,
+ Encryption = 2000,
+ Filter = 3000,
+ Translation = 4000,
+ Other = 10000 //avoid using this if at all possible
+ }
+
+ internal enum ProtocolFlagsKind : int
+ {
+ ///
+ /// The network capabilities that the protocol supports.
+ ///
+ Capabilities = 1,
+
+ ///
+ /// The status modes that the protocol supports.
+ ///
+ StatusModes = 2,
+
+ ///
+ /// The status modes that the protocol supports away-style messages for. Uses the flags.
+ ///
+ AwayStatusModes = 3
+ }
+
+ [Flags, CLSCompliant(false)]
+ public enum ProtocolCapabilities : uint
+ {
+ ///
+ /// None.
+ ///
+ None = 0,
+
+ ///
+ /// Supports IM sending.
+ ///
+ IMSend = 0x00000001,
+ ///
+ /// Supports IM receiving.
+ ///
+ IMReceive = 0x00000002,
+ IM = (IMSend | IMReceive),
+ ///
+ /// Aupports separate URL sending.
+ ///
+ UrlSend = 0x00000004,
+ ///
+ /// Supports separate URL receiving.
+ ///
+ UrlReceive = 0x00000008,
+ Url = (UrlSend | UrlReceive),
+ ///
+ /// Supports file sending.
+ ///
+ FileSend = 0x00000010,
+ ///
+ /// Supports file receiving.
+ ///
+ FileReceive = 0x00000020,
+ File = (FileSend | FileReceive),
+ ///
+ /// Supports broadcasting away messages.
+ ///
+ ModeMessageSend = 0x00000040,
+ ///
+ /// Supports reading others' away messages.
+ ///
+ ModeMessageReceive = 0x00000080,
+ ModeMessage = (ModeMessageSend | ModeMessageReceive),
+ ///
+ /// Contact lists are stored on the server, not locally. See notes below.
+ ///
+ ServerContactList = 0x00000100,
+ ///
+ /// Will get authorisation requests for some or all contacts.
+ ///
+ AuthorizationRequired = 0x00000200,
+ ///
+ /// Will get 'you were added' notifications.
+ ///
+ Added = 0x00000400,
+ ///
+ /// Has an invisible list.
+ ///
+ VisibleList = 0x00000800,
+ ///
+ /// Has a visible list for when in invisible mode.
+ ///
+ InvisibleList = 0x00001000,
+ ///
+ /// Supports setting different status modes to each contact.
+ ///
+ IndividualStatus = 0x00002000,
+ ///
+ /// the protocol is extensible and Supports plugin-defined messages.
+ ///
+ Extensible = 0x00004000,
+ ///
+ /// Supports direct (not server mediated) communication between clients.
+ ///
+ P2P = 0x00008000,
+ ///
+ /// Supports creation of new user IDs.
+ ///
+ NewUser = 0x00010000,
+ ///
+ /// Has a realtime chat capability.
+ ///
+ Chat = 0x00020000,
+ ///
+ /// Supports replying to a mode message request with different text depending on the contact requesting.
+ ///
+ IndividualModeMessage = 0x00040000,
+ ///
+ /// Supports a basic user searching facility.
+ ///
+ BasicSearch = 0x00080000,
+ ///
+ /// Supports one or more protocol-specific extended search schemes.
+ ///
+ ExtendedSearch = 0x00100000,
+ ///
+ /// Supports renaming of incoming files as they are transferred.
+ ///
+ CanRenameFile = 0x00200000,
+ ///
+ /// Can resume broken file transfers.
+ ///
+ FileResume = 0x00400000,
+ ///
+ /// Can add search results to the contact list.
+ ///
+ AddSearches = 0x00800000,
+ ///
+ /// Can send contacts to other users.
+ ///
+ ContactSend = 0x01000000,
+ ///
+ /// Can receive contacts from other users.
+ ///
+ ContactReceive = 0x02000000,
+ Contact = (ContactSend | ContactReceive),
+ ///
+ /// Can change our user information stored on server.
+ ///
+ ChangeInfo = 0x04000000,
+ ///
+ /// Supports a search by e-mail feature.
+ ///
+ SearchByEmail = 0x08000000,
+ ///
+ /// Set if the uniquely identifying field of the network is the e-mail address.
+ ///
+ UserIDIsEmail = 0x10000000,
+ ///
+ /// Supports searching by nick/first/last names.
+ ///
+ SearchByName = 0x20000000,
+ ///
+ /// Has a dialog box to allow searching all the possible fields.
+ ///
+ ExtendedSearchUI = 0x40000000,
+ ///
+ /// The unique user IDs for this protocol are numeric.
+ ///
+ NumericUserID = 0x80000000,
+ }
+
+ [Flags]
+ public enum ProtocolStatusModes : int
+ {
+ Online = 0x00000001, //an unadorned online mode
+ Invisible = 0x00000002,
+ ShortAway = 0x00000004, //Away on ICQ, BRB on MSN
+ LongAway = 0x00000008, //NA on ICQ, Away on MSN
+ LightDND = 0x00000010, //Occupied on ICQ, Busy on MSN
+ HeavyDND = 0x00000020, //DND on ICQ
+ FreeForChat = 0x00000040,
+ OutToLunch = 0x00000080,
+ OnThePhone = 0x00000100,
+ Idle = 0x00000200 //added during 0.3.4 (2004/09/13)
+ }
+
+ public enum DatabaseEventType : short
+ {
+ Message = 0,
+ Url = 1,
+ Contacts = 2, //v0.1.2.2+
+ Added = 1000, //v0.1.1.0+: these used to be module-
+ AuthorizationRequest = 1001, //specific codes, hence the module-
+ File = 1002, //specific limit has been raised to 2000
+ }
+
+ [Flags]
+ public enum DatabaseEventProperties : int
+ {
+ None = 0,
+
+ First = 1, //this is the first event in the chain;
+ //internal only: *do not* use this flag
+ Sent = 2, //this event was sent by the user. If not set this
+ //event was received.
+ Read = 4, //event has been read by the user. It does not need
+ //to be processed any more except for history.
+
+ Rtl = 8, //event contains the right-to-left aligned text
+ Utf8 = 16 //event contains a text in utf-8
+ }
+
+ [Flags]
+ public enum MenuItemProperties : int
+ {
+ KeepCurrent = -1,
+ None = 0,
+ Grayed = 1,
+ Checked = 2,
+ Hidden = 4,
+ OnlineOnly = 8,
+ OfflineOnly = 16,
+ NotOnListOnly = 32,
+ NonNotOnListOnly = 64
+ }
+
+ [Flags]
+ public enum HotKeys : int
+ {
+ Alt = 0x0001,
+ Ctrl = 0x0002,
+ Shift = 0x0004,
+ WinKey = 0x0008
+ }
+
+ public enum ContactInfoPropertyType : int
+ {
+ Unknown = 0,
+ Byte = 1,
+ UInt16 = 2,
+ UInt32 = 3,
+ String = 4
+ }
+
+ public static class CallbackResult
+ {
+ public const int Success = 0;
+ public const int Failure = -1;
+ }
+
+ public static class EventResult
+ {
+ public const bool HonourEventChain = false;
+ public const bool BreakEventChain = true;
+ }
+
+ [Flags]
+ public enum ContactListEventProperties : int
+ {
+ None = 0,
+
+ ///
+ /// Flashes the icon even if the user is occupied, and puts the event at the top of the queue.
+ ///
+ Urgent = 1,
+
+ ///
+ /// The icon will not flash for ever, only a few times. This is for eg online alert.
+ ///
+ Minor = 2,
+
+ // #define CLEF_URGENT 1 //flashes the icon even if the user is occupied,
+ // //and puts the event at the top of the queue
+ //#define CLEF_ONLYAFEW 2 //the icon will not flash for ever, only a few
+ // //times. This is for eg online alert
+ //#define CLEF_UNICODE 4 //set pszTooltip as unicode
+
+ //#define CLEF_PROTOCOLGLOBAL 8 //set event globally for protocol, hContact has to be NULL,
+ // //lpszProtocol the protocol ID name to be set
+
+ //#if defined( _UNICODE )
+ // #define CLEF_TCHAR CLEF_UNICODE //will use TCHAR* instead of char*
+ //#else
+ // #define CLEF_TCHAR 0 //will return char*, as usual
+ //#endif
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/RemoteObject.cs b/Hyphen/Plugins/Infrastructure/RemoteObject.cs
new file mode 100644
index 0000000..5797036
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/RemoteObject.cs
@@ -0,0 +1,33 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public abstract class RemoteObject : MarshalByRefObject
+ {
+ protected RemoteObject() { }
+
+ public override object InitializeLifetimeService()
+ {
+ return null;
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/RuntimeCaches.cs b/Hyphen/Plugins/Infrastructure/RuntimeCaches.cs
new file mode 100644
index 0000000..77891b5
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/RuntimeCaches.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal static class RuntimeCaches
+ {
+ #region Fields
+
+ private static readonly Dictionary Caches = new Dictionary(1);
+
+ #endregion
+
+ #region Getters
+
+ public static IDictionary GetCache(string cacheName)
+ {
+ return GetCache(cacheName) as IDictionary;
+ }
+
+ public static object GetCache(string name)
+ {
+ if (Caches.ContainsKey(name))
+ throw new ArgumentException("Cache not found.", "name");
+
+ return Caches[name];
+ }
+
+ public static bool CacheRegistered(string name)
+ {
+ return Caches.ContainsKey(name);
+ }
+
+ #endregion
+
+ #region Setters
+
+ public static void RegisterCache(string name, object cache)
+ {
+ if (Caches.ContainsKey(name))
+ throw new ArgumentException("Cache already registered.", "name");
+
+ Caches[name] = cache;
+ }
+
+ public static bool UnregisterCache(string name)
+ {
+ return Caches.Remove(name);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/RuntimeEnvironment.cs b/Hyphen/Plugins/Infrastructure/RuntimeEnvironment.cs
new file mode 100644
index 0000000..ee45be9
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/RuntimeEnvironment.cs
@@ -0,0 +1,85 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Hyphen;
+using System.Threading;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ ///
+ /// Provides information about current state of Hyphen runtime.
+ /// This class can be used only from the context of standalone modules (i.e. from the default AppDomain only).
+ ///
+ public static class RuntimeEnvironment
+ {
+ #region Fields
+
+ private static volatile bool Initialized;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Marks the class initialized.
+ ///
+ internal static void Initialize()
+ {
+ Initialized = true;
+ }
+
+ ///
+ /// Verifies whether the class was initialized (from the default AppDomain).
+ ///
+ /// Class not initialized (i.e. called from other than default AppDomain).
+ private static void VerifyInitialized()
+ {
+ if (!Initialized)
+ throw new NotSupportedException();
+ }
+
+ #endregion
+
+ #region Properties
+
+ private static bool hyphenIsLoading;
+
+ ///
+ /// Gets an indication whether the Hyphen runtime is currently loading.
+ ///
+ /// Class not initialized (i.e. called from other than default AppDomain).
+ public static bool HyphenIsLoading
+ {
+ get { VerifyInitialized(); return hyphenIsLoading; }
+ internal set { hyphenIsLoading = value; }
+ }
+
+ ///
+ /// Gets an indication whether the Isolated plugins are loaded.
+ ///
+ /// Class not initialized (i.e. called from other than default AppDomain).
+ public static bool IsolatedModePluginsLoaded
+ {
+ get { VerifyInitialized(); return Loader.GetInstance().PluginsLoaded; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ServiceCallInterceptionManager.cs b/Hyphen/Plugins/Infrastructure/ServiceCallInterceptionManager.cs
new file mode 100644
index 0000000..93203d0
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ServiceCallInterceptionManager.cs
@@ -0,0 +1,82 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.Serialization;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public sealed class ServiceCallInterceptionManager
+ {
+ #region Fields
+
+ private readonly Dictionary Interceptors;
+
+ #endregion
+
+ #region .ctors
+
+ public ServiceCallInterceptionManager()
+ {
+ this.Interceptors = new Dictionary(2);
+ }
+
+ #endregion
+
+ #region Methods
+
+ public bool RequiresInterception(string serviceName)
+ {
+ lock (Interceptors)
+ return Interceptors.ContainsKey(serviceName);
+ }
+
+ public Callback this[string serviceName]
+ {
+ get
+ {
+ lock (Interceptors)
+ {
+ Callback interceptor = null;
+ Interceptors.TryGetValue(serviceName, out interceptor);
+
+ return interceptor;
+ }
+ }
+ set
+ {
+ Register(serviceName, value);
+ }
+ }
+
+ public void Register(string serviceName, Callback interceptor)
+ {
+ lock (Interceptors)
+ Interceptors[serviceName] = interceptor;
+ }
+
+ public void Unregister(string serviceName)
+ {
+ lock (Interceptors)
+ Interceptors.Remove(serviceName);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ServiceFunctionAttribute.cs b/Hyphen/Plugins/Infrastructure/ServiceFunctionAttribute.cs
new file mode 100644
index 0000000..79a68cc
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ServiceFunctionAttribute.cs
@@ -0,0 +1,66 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
+ public sealed class ServiceFunctionAttribute : HookAttribute
+ {
+ #region Fields
+
+ private string serviceName;
+ public string ServiceName
+ {
+ get
+ {
+ return this.serviceName;
+ }
+ set
+ {
+ this.serviceName = value;
+ }
+ }
+
+ internal override string HookName
+ {
+ get { return ServiceName; }
+ }
+
+ internal override HookType HookType
+ {
+ get { return HookType.ServiceFunction; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public ServiceFunctionAttribute(string serviceName)
+ {
+ if (serviceName == null)
+ throw new ArgumentNullException("serviceName");
+
+ this.serviceName = serviceName;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/ServiceManager.cs b/Hyphen/Plugins/Infrastructure/ServiceManager.cs
new file mode 100644
index 0000000..221ac91
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/ServiceManager.cs
@@ -0,0 +1,101 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Diagnostics;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public static class ServiceManager
+ {
+ [CLSCompliant(false)]
+ public static void CreateServiceFunction(string serviceName, Callback callback, MirandaPlugin owner)
+ {
+ if (String.IsNullOrEmpty(serviceName))
+ throw new ArgumentNullException("serviceName");
+
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (!owner.Initialized)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginNotInitialized);
+
+ HookDescriptorCollection collection = owner.Descriptor.ServiceFunctions;
+
+ try
+ {
+ SynchronizationHelper.BeginPluginUpdate(owner);
+ SynchronizationHelper.BeginCollectionUpdate(collection);
+
+ HookDescriptor descriptor = HookDescriptor.SetUpAndStore(collection, serviceName, owner.Descriptor, callback, HookType.ServiceFunction);
+ descriptor.RegisteredManually = true;
+
+ HookManager.CreateHook(descriptor);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(owner);
+ SynchronizationHelper.EndUpdate(collection);
+ }
+ }
+
+ public static bool ServiceExists(string name)
+ {
+ return MirandaContext.Current.PluginLink.NativePluginLink.ServiceExists(name) != 0;
+ }
+
+ [CLSCompliant(false)]
+ public static Callback GetService(string serviceName)
+ {
+ return CallbackWrapper.Create(serviceName);
+ }
+
+ public static void RemoveServiceFunction(MirandaPlugin owner, string eventName)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (String.IsNullOrEmpty(eventName))
+ throw new ArgumentNullException("eventName");
+
+ HookDescriptorCollection collection = owner.Descriptor.ServiceFunctions;
+
+ try
+ {
+ SynchronizationHelper.BeginCollectionUpdate(collection);
+ HookDescriptor descriptor = null;
+
+ if ((descriptor = collection.Find(eventName)) == null)
+ return;
+
+ HookManager.DestroyHook(descriptor);
+ collection.Remove(descriptor);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(collection);
+ }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Skin.cs b/Hyphen/Plugins/Infrastructure/Skin.cs
new file mode 100644
index 0000000..e5a0d7a
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Skin.cs
@@ -0,0 +1,81 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ ///
+ /// Manages Miranda skin entities, for example icons and sounds.
+ ///
+ /// m_skin.h
+ public static class Skin
+ {
+ #region Constants
+
+ private const string MS_SKIN_LOADICON = "Skin/Icons/Load",
+ MS_SKIN_LOADPROTOICON = "Skin/Icons/LoadProto";
+
+ #endregion
+
+ #region Icons
+
+ public static class Icons
+ {
+ public const int Message = 100;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static IntPtr LoadIcon(int id)
+ {
+ return (IntPtr)MirandaContext.Current.CallService(MS_SKIN_LOADICON, (UIntPtr)(uint)id, IntPtr.Zero);
+ }
+
+ public static IntPtr LoadProtocolIcon(StatusMode status)
+ {
+ return LoadProtocolIcon((string)null, status);
+ }
+
+ public static IntPtr LoadProtocolIcon(Protocol protocol, StatusMode status)
+ {
+ return LoadProtocolIcon((protocol != null ? protocol.Name : null), status);
+ }
+
+ public static IntPtr LoadProtocolIcon(string protocolName, StatusMode status)
+ {
+ UnmanagedStringHandle protoNamePtr = UnmanagedStringHandle.Empty;
+
+ try
+ {
+ protoNamePtr = new UnmanagedStringHandle(protocolName, StringEncoding.Ansi);
+ return (IntPtr)MirandaContext.Current.CallService(MS_SKIN_LOADPROTOICON, protoNamePtr.IntPtr, (IntPtr)status);
+ }
+ finally
+ {
+ protoNamePtr.Free();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/StatusMode.cs b/Hyphen/Plugins/Infrastructure/StatusMode.cs
new file mode 100644
index 0000000..3ba1884
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/StatusMode.cs
@@ -0,0 +1,46 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Helpers;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ public enum StatusMode : int
+ {
+ Offline = 40071,
+ Online = 40072,
+ Away = 40073,
+ DND = 40074,
+ NA = 40075,
+ Occupied = 40076,
+
+ [EnumValueFriendlyName("Free for chat")]
+ FreeForChat = 40077,
+ Invisible = 40078,
+
+ [EnumValueFriendlyName("On the phone")]
+ OnThePhone = 40079,
+
+ [EnumValueFriendlyName("Out to lunch")]
+ OutToLunch = 40080,
+
+ Idle = 40081 /* do not use as a status */
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/StringResolverCache.cs b/Hyphen/Plugins/Infrastructure/StringResolverCache.cs
new file mode 100644
index 0000000..8b922b3
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/StringResolverCache.cs
@@ -0,0 +1,51 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Helpers;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ internal sealed class StringResolverCache : TypeInstanceCache
+ {
+ #region Fields
+
+ private static StringResolverCache singleton;
+
+ #endregion
+
+ #region .ctors
+
+ private StringResolverCache() { }
+
+ #endregion
+
+ #region Properties
+
+ public static StringResolverCache Singleton
+ {
+ get
+ {
+ return singleton ?? (singleton = new StringResolverCache());
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Infrastructure/Translate.cs b/Hyphen/Plugins/Infrastructure/Translate.cs
new file mode 100644
index 0000000..0b0a2a2
--- /dev/null
+++ b/Hyphen/Plugins/Infrastructure/Translate.cs
@@ -0,0 +1,199 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Native;
+
+namespace Virtuoso.Miranda.Plugins.Infrastructure
+{
+ [CLSCompliant(false)]
+ public static class Translate
+ {
+ #region Fields
+
+ private static readonly Type StatusEnumType = typeof(StatusMode);
+
+ #endregion
+
+ #region ToStatus
+
+ public static StatusMode ToStatus(UIntPtr wParam)
+ {
+ if (!Enum.IsDefined(StatusEnumType, (int)wParam.ToUInt32()))
+ throw new ArgumentException("wParam", TextResources.ExceptionMsg_InvalidValueToTranslate);
+
+ return (StatusMode)(wParam);
+ }
+
+ #endregion
+
+ #region ToString
+
+ public static string ToString(IntPtr lParam, StringEncoding marshalAs)
+ {
+ return ToString(lParam, 0, marshalAs, false);
+ }
+
+ public static string ToString(IntPtr lParam, StringEncoding marshalAs, bool transformExceptionsToNull)
+ {
+ return ToString(lParam, 0, marshalAs, transformExceptionsToNull);
+ }
+
+ public static string ToString(IntPtr lParam, int length, StringEncoding marshalAs)
+ {
+ return ToString(lParam, length, marshalAs, false);
+ }
+
+ public static string ToString(IntPtr lParam, int length, StringEncoding marshalAs, bool tranformExceptionsToNull)
+ {
+ if (lParam == IntPtr.Zero)
+ throw new ArgumentNullException("lParam");
+
+ if (length < 0)
+ throw new ArgumentOutOfRangeException("length");
+
+ if (marshalAs != StringEncoding.MirandaDefault && marshalAs != StringEncoding.Ansi && marshalAs != StringEncoding.Unicode)
+ throw new ArgumentOutOfRangeException("marshalAs");
+
+ try
+ {
+ reEval:
+ switch (marshalAs)
+ {
+ case StringEncoding.MirandaDefault:
+ marshalAs = MirandaEnvironment.MirandaStringEncoding;
+ if (marshalAs == StringEncoding.MirandaDefault) throw new ArgumentException(TextResources.ExceptionMsg_CannotDetectMirandaDefaultStringEncoding);
+ goto reEval;
+ case StringEncoding.Ansi:
+ if (length > 0)
+ return Marshal.PtrToStringAnsi(lParam, length);
+ else
+ return Marshal.PtrToStringAnsi(lParam);
+ case StringEncoding.Unicode:
+ if (length > 0)
+ return Marshal.PtrToStringUni(lParam, length);
+ else
+ return Marshal.PtrToStringUni(lParam);
+ default:
+ return null;
+ }
+ }
+ catch (Exception e)
+ {
+ if (!tranformExceptionsToNull)
+ throw new ArgumentException("lParam", TextResources.ExceptionMsg_InvalidValueToTranslate + e.Message, e);
+ else
+ return null;
+ }
+ }
+
+ #endregion
+
+ #region ToHandle
+
+ public static IntPtr ToHandle(UIntPtr wParam)
+ {
+ return new IntPtr((long)wParam.ToUInt64());
+ }
+
+ public static UIntPtr ToHandle(IntPtr lParam)
+ {
+ return new UIntPtr((ulong)lParam.ToInt64());
+ }
+
+ public static UnmanagedStringHandle ToHandle(string str, StringEncoding encoding)
+ {
+ return new UnmanagedStringHandle(str, encoding);
+ }
+
+ #endregion
+
+ #region To/From Miranda HyphenVersion Format
+
+ [CLSCompliant(false)]
+ public static uint ToMirandaVersion(Version version)
+ {
+ if (version == null)
+ throw new ArgumentNullException("version");
+
+ return (((((uint)(version.Major)) & 0xFF) << 24) | ((((uint)(version.Minor)) & 0xFF) << 16) | ((((uint)(version.Build)) & 0xFF) << 8) | (((uint)(version.MinorRevision)) & 0xFF));
+ }
+
+ [CLSCompliant(false)]
+ public static Version FromMirandaVersion(uint version)
+ {
+ return new Version((int)((version >> 24) & 0xff), (int)((version >> 16) & 0xff), (int)((version >> 8) & 0xff), (int)(version & 0xff));
+ }
+
+ #endregion
+
+ #region Misc.
+
+ public static byte[] ToBlob(IntPtr blobPtr, int size)
+ {
+ if (blobPtr == IntPtr.Zero)
+ throw new ArgumentNullException("blobPtr");
+
+ if (size < 0)
+ throw new ArgumentOutOfRangeException("size");
+
+ byte[] blob = new byte[size];
+ Marshal.Copy(blobPtr, blob, 0, size);
+
+ return blob;
+ }
+
+ #endregion
+
+ #region Internal helpers
+
+ internal static object ValueFromVariant(ref DBVARIANT dbVariant)
+ {
+ switch ((DbVariantValue)dbVariant.Type)
+ {
+ case DbVariantValue.DBVT_ASCIIZ:
+ return Translate.ToString(dbVariant.Text.TextPtr, dbVariant.Text.TextBufferSize, StringEncoding.Ansi);
+ case DbVariantValue.DBVT_UTF8:
+ return Translate.ToString(dbVariant.Text.TextPtr, dbVariant.Text.TextBufferSize, StringEncoding.Unicode);
+ case DbVariantValue.DBVT_BLOB:
+ return ToBlob(dbVariant.Blob.BlobPtr, dbVariant.Blob.Size);
+ case DbVariantValue.DBVT_BYTE:
+ return dbVariant.Primitives.Byte;
+ case DbVariantValue.DBVT_DELETED:
+ return null;
+ case DbVariantValue.DBVT_DWORD:
+ return dbVariant.Primitives.DWord;
+ case DbVariantValue.DBVT_WORD:
+ return dbVariant.Primitives.Word;
+ case DbVariantValue.DBVT_WCHAR:
+ return Marshal.PtrToStringBSTR(dbVariant.Text.TextPtr);
+ case DbVariantValue.DBVTF_VARIABLELENGTH:
+ throw new NotSupportedException();
+ default:
+ return null;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/LoaderOptionsAttribute.cs b/Hyphen/Plugins/LoaderOptionsAttribute.cs
new file mode 100644
index 0000000..f9ce73b
--- /dev/null
+++ b/Hyphen/Plugins/LoaderOptionsAttribute.cs
@@ -0,0 +1,160 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [Flags]
+ public enum LoaderOptions : int
+ {
+ None = 0,
+ HasCustomApiExports = 2,
+ CannotBeUnloaded = 4,
+ }
+
+ internal enum LoaderOptionsOwner
+ {
+ Type,
+ Assembly
+ }
+
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
+ public sealed class LoaderOptionsAttribute : Attribute
+ {
+ #region Fields
+
+ private LoaderOptions options;
+ public LoaderOptions Options
+ {
+ get { return options; }
+ set { options = value; }
+ }
+
+ private Version requiredVersion;
+ public Version RequiredVersion
+ {
+ get { return requiredVersion; }
+ }
+
+ private Version minimalMirandaVersion;
+ public Version MinimalMirandaVersion
+ {
+ get { return minimalMirandaVersion; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ public LoaderOptionsAttribute(LoaderOptions options)
+ : this(null, null, options) { }
+
+ public LoaderOptionsAttribute(string requiredVersion)
+ : this(requiredVersion, null, LoaderOptions.None) { }
+
+ public LoaderOptionsAttribute(string requiredVersion, LoaderOptions options)
+ : this(requiredVersion, null, options) { }
+
+ public LoaderOptionsAttribute(string requiredVersion, string minimalMirandaVersion)
+ : this(requiredVersion, minimalMirandaVersion, LoaderOptions.None) { }
+
+ public LoaderOptionsAttribute(string requiredVersion, string minimalMirandaVersion, LoaderOptions options)
+ {
+ if (!String.IsNullOrEmpty(requiredVersion))
+ this.requiredVersion = new Version(requiredVersion);
+
+ if (!String.IsNullOrEmpty(minimalMirandaVersion))
+ this.minimalMirandaVersion = new Version(minimalMirandaVersion);
+
+ this.options = options;
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal bool SupportsMirandaVersion(uint mirandaVersion)
+ {
+ return SupportsMirandaVersion(Translate.FromMirandaVersion(mirandaVersion));
+ }
+
+ internal bool SupportsMirandaVersion(Version mirandaVersion)
+ {
+ // If the plugin has a min supported Miranda info..
+ if (MinimalMirandaVersion != null)
+ {
+ if (mirandaVersion < MinimalMirandaVersion)
+ return false;
+ }
+ // If not, use Hyphen's...
+ else if (!Loader.SupportsMirandaVersion(mirandaVersion))
+ return false;
+
+ return true;
+ }
+
+ internal static LoaderOptionsAttribute Get(Type pluginType, LoaderOptionsOwner target)
+ {
+ if (pluginType == null)
+ throw new ArgumentNullException("pluginType");
+
+ Type thisType = typeof(LoaderOptionsAttribute);
+ LoaderOptionsAttribute result = null;
+
+ switch (target)
+ {
+ case LoaderOptionsOwner.Type:
+ foreach (LoaderOptionsAttribute attrib in pluginType.GetCustomAttributes(thisType, true))
+ {
+ if (result == null)
+ result = attrib;
+ else
+ {
+ if (result.requiredVersion == null)
+ result.requiredVersion = attrib.requiredVersion;
+ else if (attrib.RequiredVersion != null)
+ throw new NotSupportedException(TextResources.ExceptionMsg_DuplicitLoaderOptions);
+
+ if (result.minimalMirandaVersion == null)
+ result.minimalMirandaVersion = attrib.minimalMirandaVersion;
+ else if (attrib.MinimalMirandaVersion != null)
+ throw new NotSupportedException(TextResources.ExceptionMsg_DuplicitLoaderOptions);
+ }
+
+ result.options |= attrib.options;
+ }
+ break;
+ case LoaderOptionsOwner.Assembly:
+ if (pluginType.Assembly.IsDefined(thisType, false))
+ result = (LoaderOptionsAttribute)pluginType.Assembly.GetCustomAttributes(thisType, false)[0];
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("target");
+ }
+
+ return result ?? new LoaderOptionsAttribute(LoaderOptions.None);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Log.cs b/Hyphen/Plugins/Log.cs
new file mode 100644
index 0000000..e8e3d85
--- /dev/null
+++ b/Hyphen/Plugins/Log.cs
@@ -0,0 +1,57 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ internal static class Log
+ {
+ #region Properties
+
+ private static TraceSwitch traceSwitch;
+ public static TraceSwitch TraceSwitch
+ {
+ get { return traceSwitch; }
+ }
+
+ #endregion
+
+ #region .ctors
+
+ static Log()
+ {
+ traceSwitch = new TraceSwitch("HyphenTracing", "Hyphen Tracing", "Warning");
+ }
+
+ #endregion
+
+ [Conditional("DEBUG")]
+ public static void DebuggerWrite(int priority, string source, string message)
+ {
+ Debugger.Log(priority, source, message);
+ }
+
+ public static void Warning(string message, string category, params string[] formatArgs)
+ {
+ Trace.WriteLineIf(TraceSwitch.TraceWarning, String.Format(message, formatArgs), category);
+ }
+ }
+}
diff --git a/Hyphen/Plugins/MirandaPlugin.EmptyPlugin.cs b/Hyphen/Plugins/MirandaPlugin.EmptyPlugin.cs
new file mode 100644
index 0000000..de227ee
--- /dev/null
+++ b/Hyphen/Plugins/MirandaPlugin.EmptyPlugin.cs
@@ -0,0 +1,114 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2008 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+using Virtuoso.Miranda.Plugins.Forms;
+using Virtuoso.Miranda.Plugins.Resources;
+using System.Diagnostics;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ partial class MirandaPlugin
+ {
+ ///
+ /// Represents an unknown plugin. Hyphen will impersonate itself with this plugin when binding to Miranda's events.
+ ///
+ internal sealed class Hyphen : MirandaPlugin, IExceptionHandler, IExceptionReporter
+ {
+ #region Fields
+
+ private static readonly Hyphen singleton = new Hyphen();
+
+ #endregion
+
+ #region .ctors
+
+ private Hyphen()
+ {
+ PluginDescriptor.SetUp(this);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public static Hyphen Singleton
+ {
+ get
+ {
+ return singleton;
+ }
+ }
+
+ public override string Name
+ {
+ get { return "Hyphen"; }
+ }
+
+ public override string Author
+ {
+ get { return "virtuoso"; }
+ }
+
+ public override string Description
+ {
+ get { return String.Empty; }
+ }
+
+ public override Uri HomePage
+ {
+ get { return new Uri("http://www.none.com"); }
+ }
+
+ public override Version Version
+ {
+ get { return Assembly.GetExecutingAssembly().GetName().Version; }
+ }
+
+ public override bool HasOptions
+ {
+ get { return false; }
+ }
+
+ #endregion
+
+ #region IExceptionHandler Members
+
+ public void HandleException(Exception e, PluginDescriptor descriptor)
+ {
+ ErrorDialog.PresentModal(e, this, TextResources.MsgBox_Text_HyphenCrashed, false);
+ }
+
+ #endregion
+
+ #region IExceptionReporter Members
+
+ void IExceptionReporter.ReportException(Exception e)
+ {
+ ProcessStartInfo startInfo = new ProcessStartInfo(String.Format("mailto:{0}?subject={1}&body={2}", "deml.tomas@seznam.cz", TextResources.MsgBox_Caption_HyphenCrashed, e.ToString()));
+ startInfo.UseShellExecute = true;
+
+ Process.Start(startInfo);
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/Hyphen/Plugins/MirandaPlugin.cs b/Hyphen/Plugins/MirandaPlugin.cs
new file mode 100644
index 0000000..3cf4312
--- /dev/null
+++ b/Hyphen/Plugins/MirandaPlugin.cs
@@ -0,0 +1,178 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), CLSCompliant(false)]
+ public delegate int Callback(UIntPtr wParam, IntPtr lParam);
+
+ public enum PluginState : int
+ {
+ Disabled,
+ Enabled,
+ CrashDisabled
+ }
+
+ public abstract partial class MirandaPlugin : ContextWorker, ISettingOwner
+ {
+ #region Fields
+
+ private MenuItemDeclarationCollection menuItemsCollection;
+ private MenuItemDeclarationReadOnlyCollection menuItemsReadOnly;
+
+ private PluginDescriptor descriptor;
+
+ #endregion
+
+ #region .ctors
+
+ protected MirandaPlugin()
+ {
+ menuItemsCollection = new MenuItemDeclarationCollection();
+ menuItemsReadOnly = new MenuItemDeclarationReadOnlyCollection(menuItemsCollection);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public abstract string Name { get; }
+
+ public abstract string Author { get; }
+
+ public abstract string Description { get; }
+
+ public abstract Uri HomePage { get; }
+
+ public abstract Version Version { get; }
+
+ public abstract bool HasOptions { get; }
+
+ public MenuItemDeclarationReadOnlyCollection MenuItems
+ {
+ get { return menuItemsReadOnly; }
+ }
+
+ public bool Initialized
+ {
+ get
+ {
+ return descriptor != null;
+ }
+ }
+
+ internal MenuItemDeclarationCollection MenuItemsCollection
+ {
+ get
+ {
+ return menuItemsCollection;
+ }
+ }
+
+ protected internal PluginDescriptor Descriptor
+ {
+ get
+ {
+ if (descriptor == null)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginNotInitialized);
+
+ return descriptor;
+ }
+ internal set
+ {
+ if (descriptor != null)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginAlreadyInitialized);
+
+ descriptor = value;
+ }
+ }
+
+ internal string UniqueName
+ {
+ get
+ {
+ return String.Format("{0}.{1}.{2}", Author, Name, Version);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public sealed override int GetHashCode()
+ {
+ return GetType().FullName.GetHashCode();
+ }
+
+ public sealed override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ MirandaPlugin other = obj as MirandaPlugin;
+
+ if (other == null) return false;
+ return GetHashCode() == other.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0} by {1}, v{2}", Name, Author, Version);
+ }
+
+ internal static IExceptionHandler GetExceptionHandler(PluginDescriptor descriptor)
+ {
+ if (descriptor == null)
+ throw new ArgumentNullException("descriptor");
+
+ // Custom handler (i.e. Hyphen's)
+ if (descriptor.Plugin is IExceptionHandler)
+ return (IExceptionHandler)descriptor.Plugin;
+ // Isolated plugin crashed
+ else if (!descriptor.IsStandalone)
+ return MirandaContext.Current.PluginManager;
+ // Generic handler
+ else
+ return DefaultExceptionHandler.Create(descriptor.Plugin);
+ }
+
+ #endregion
+
+ #region Events
+
+ internal virtual void AfterMenuItemsPopulationInternal(MenuItemDeclarationCollection items) { AfterMenuItemsPopulation(items); }
+ protected virtual void AfterMenuItemsPopulation(MenuItemDeclarationCollection items) { }
+
+ internal virtual void BeforeMirandaShutdownInternal() { BeforeMirandaShutdown(); }
+ protected virtual void BeforeMirandaShutdown() { }
+
+ internal virtual void BeforePluginDisableInternal() { BeforePluginDisable(); }
+ protected virtual void BeforePluginDisable() { }
+
+ internal virtual void AfterPluginEnableInternal() { AfterPluginEnable(); }
+ protected virtual void AfterPluginEnable() { }
+
+ internal virtual void AfterPluginInitializationInternal() { AfterPluginInitialization(); }
+ protected virtual void AfterPluginInitialization() { }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/ACKDATA.cs b/Hyphen/Plugins/Native/ACKDATA.cs
new file mode 100644
index 0000000..d8c46de
--- /dev/null
+++ b/Hyphen/Plugins/Native/ACKDATA.cs
@@ -0,0 +1,65 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct ACKDATA
+ {
+ private readonly int Size;
+ public IntPtr ModuleName;
+ public IntPtr ContactHandle;
+ public int Type;
+ public int Result;
+ public IntPtr ProcessHandle;
+ public IntPtr LParam;
+
+ public ACKDATA(IntPtr moduleName, int type, int result)
+ {
+ this.ModuleName = moduleName;
+ this.Type = type;
+ this.Result = result;
+
+ this.ContactHandle = IntPtr.Zero;
+ this.ProcessHandle = IntPtr.Zero;
+ this.LParam = IntPtr.Zero;
+
+ this.Size = Marshal.SizeOf(typeof(ACKDATA));
+ }
+ }
+
+ //a general network 'ack'
+ //wParam=0
+ //lParam=(LPARAM)(ACKDATA*)&ack
+ //Note that just because definitions are here doesn't mean they will be sent.
+ //Read the documentation for the function you are calling to see what replies
+ //you will receive.
+ /*typedef struct {
+ int cbSize;
+ const char *szModule; //the name of the protocol module which initiated this ack
+ HANDLE hContact;
+ int type; //an ACKTYPE_ constant
+ int result; //an ACKRESULT_ constant
+ HANDLE hProcess; //a caller-defined process code
+ LPARAM lParam; //caller-defined extra info
+ } ACKDATA;*/
+}
diff --git a/Hyphen/Plugins/Native/ADDCONTACTSTRUCT.cs b/Hyphen/Plugins/Native/ADDCONTACTSTRUCT.cs
new file mode 100644
index 0000000..f6eeddb
--- /dev/null
+++ b/Hyphen/Plugins/Native/ADDCONTACTSTRUCT.cs
@@ -0,0 +1,41 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout( LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct ADDCONTACTSTRUCT
+ {
+ public int HandleType;
+ public IntPtr Handle;
+ public string Protocol;
+ }
+
+ /*
+ * typedef struct{
+ int handleType; //one of the HANDLE_ constants
+ HANDLE handle; //hDbEvent if acs.handleType==HANDLE_EVENT, hContact if acs.handleType==HANDLE_CONTACT, ignored if acs.handleType==HANDLE_SEARCHRESULT
+ const char *szProto; //ignored if acs.handleType!=HANDLE_SEARCHRESULT
+ PROTOSEARCHRESULT *psr; //ignored if acs.handleType!=HANDLE_SEARCHRESULT
+ }ADDCONTACTSTRUCT;
+ */
+}
diff --git a/Hyphen/Plugins/Native/CCSDATA.cs b/Hyphen/Plugins/Native/CCSDATA.cs
new file mode 100644
index 0000000..acdacb4
--- /dev/null
+++ b/Hyphen/Plugins/Native/CCSDATA.cs
@@ -0,0 +1,58 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct CCSDATA
+ {
+ #region Fields
+
+ public readonly IntPtr ContactHandle;
+ public IntPtr ServiceNamePtr;
+
+ public UIntPtr WParam;
+ public IntPtr LParam;
+
+ #endregion
+
+ #region .ctors
+
+ public CCSDATA(ContactInfo contact, string serviceName)
+ {
+ this.ContactHandle = contact.MirandaHandle;
+ this.ServiceNamePtr = new UnmanagedStringHandle(serviceName, StringEncoding.Ansi).IntPtr;
+
+ this.WParam = UIntPtr.Zero;
+ this.LParam = IntPtr.Zero;
+ }
+
+ public void Free()
+ {
+ if (ServiceNamePtr != IntPtr.Zero)
+ Marshal.FreeHGlobal(ServiceNamePtr);
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/CLISTMENUITEM.cs b/Hyphen/Plugins/Native/CLISTMENUITEM.cs
new file mode 100644
index 0000000..592f4f5
--- /dev/null
+++ b/Hyphen/Plugins/Native/CLISTMENUITEM.cs
@@ -0,0 +1,128 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using System.Drawing;
+using System.Diagnostics;
+using System.IO;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
+ internal struct CLISTMENUITEM
+ {
+ #region Fields
+
+ private readonly int Size;
+
+ public string Text;
+ public uint Flags;
+ public int Position;
+ public IntPtr Icon;
+ public string Service;
+ public string PopUpMenu;
+ public int PopUpPosition;
+ public uint HotKey;
+ public string ContactOwner;
+
+ #endregion
+
+ #region .ctors
+
+ public CLISTMENUITEM(MirandaPlugin owner, MenuItemDeclarationAttribute attrib)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+
+ if (attrib == null)
+ throw new ArgumentNullException("attrib");
+
+ this.Text = attrib.Text;
+ this.Service = attrib.Service;
+ this.ContactOwner = attrib.OwningModule;
+ this.Flags = (uint)attrib.Flags;
+ this.PopUpMenu = attrib.PopUpMenu;
+ this.PopUpPosition = attrib.PopUpPosition;
+ this.Position = attrib.Position;
+ this.HotKey = (uint)attrib.HotKey;
+ this.Icon = IntPtr.Zero;
+ this.Size = Marshal.SizeOf(typeof(CLISTMENUITEM));
+
+ LoadIcon(owner, attrib);
+ }
+
+ #endregion
+
+ #region Methods
+
+ private void LoadIcon(MirandaPlugin owner, MenuItemDeclarationAttribute attrib)
+ {
+ try
+ {
+ if (!attrib.HasIcon)
+ return;
+
+ if (attrib.UseEmbeddedIcon)
+ {
+ using (Stream stream = owner.GetType().Assembly.GetManifestResourceStream(attrib.IconID))
+ {
+ if (stream != null)
+ Icon = IconImageCache.Singleton.GetStreamedIcon(stream).Handle;
+ else
+ Debug.Fail("Embedded icon not found.");
+ }
+ }
+ else
+ Icon = Skin.LoadIcon(int.Parse(attrib.IconID));
+ }
+ catch
+ {
+ this.Icon = IntPtr.Zero;
+ }
+ }
+
+ #endregion
+ }
+
+ /*
+ typedef struct {
+ int cbSize; //size in bytes of this structure
+ char *pszName; //text of the menu item
+ DWORD flags; //flags
+ int position; //approx position on the menu. lower numbers go nearer the top
+ HICON hIcon; //HasIcon to put by the item. If this was not loaded from
+ //a resource, you can delete it straight after the call
+ char *pszService; //eventName of service to call when the item gets selected
+ char *pszPopupName; //eventName of the popup menu that this item is on. If this
+ //is NULL the item is on the root of the menu
+ int popupPosition; //position of the popup menu on the root menu. Ignored
+ //if pszPopupName is NULL or the popup menu already
+ //existed
+ DWORD hotKey; //keyboard accelerator, same as lParam of WM_HOTKEY
+ //0 for none
+ char *pszContactOwner; //contact menus only. The protocol module that owns
+ //the contacts to which this menu item applies. NULL if it
+ //applies to all contacts. If it applies to multiple but not all
+ //protocols, add multiple menu items or use ME_CLIST_PREBUILDCONTACTMENU
+ } CLISTMENUITEM;
+ */
+}
diff --git a/Hyphen/Plugins/Native/CONTACTINFO.cs b/Hyphen/Plugins/Native/CONTACTINFO.cs
new file mode 100644
index 0000000..1ff7c14
--- /dev/null
+++ b/Hyphen/Plugins/Native/CONTACTINFO.cs
@@ -0,0 +1,71 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct CONTACTINFO
+ {
+ #region Fields
+
+ private readonly int Size;
+
+ public byte Flag;
+ public IntPtr ContactHandle;
+ public IntPtr Module;
+ public byte Type;
+
+ public IntPtr Value;
+
+ #endregion
+
+ #region .ctors
+
+ public CONTACTINFO(IntPtr contactHandle, IntPtr module)
+ {
+ this.ContactHandle = contactHandle;
+ this.Flag = 0;
+ this.Module = module;
+ this.Type = 0;
+ this.Value = IntPtr.Zero;
+ this.Size = Marshal.SizeOf(typeof(CONTACTINFO));
+ }
+
+ #endregion
+ }
+
+ /*typedef struct {
+ int cbSize;
+ BYTE dwFlag;
+ HANDLE hContact;
+ char *szProto;
+ BYTE type;
+ union {
+ BYTE bVal;
+ WORD wVal;
+ DWORD dVal;
+ char *pszVal;
+ WORD cchVal;
+ };
+} CONTACTINFO;*/
+}
diff --git a/Hyphen/Plugins/Native/DBCONTACTSETTING.cs b/Hyphen/Plugins/Native/DBCONTACTSETTING.cs
new file mode 100644
index 0000000..3beac7d
--- /dev/null
+++ b/Hyphen/Plugins/Native/DBCONTACTSETTING.cs
@@ -0,0 +1,102 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct DBCONTACTWRITESETTING
+ {
+ #region Fields
+
+ public IntPtr Module;
+ public IntPtr Name;
+
+ public DBVARIANT Value;
+
+ #endregion
+
+ #region Methods
+
+ public static unsafe object ExtractValue(IntPtr pDbWriteSetting)
+ {
+ DBCONTACTWRITESETTING dbWriteSetting = *(DBCONTACTWRITESETTING*)pDbWriteSetting.ToPointer();
+
+ switch ((DatabaseSettingType)dbWriteSetting.Value.Type)
+ {
+ case DatabaseSettingType.AsciiString:
+ return Marshal.PtrToStringAnsi(dbWriteSetting.Value.Text.TextPtr);
+ case DatabaseSettingType.UnicodeString:
+ case DatabaseSettingType.UTF8String:
+ return Marshal.PtrToStringUni(dbWriteSetting.Value.Text.TextPtr);
+ case DatabaseSettingType.Byte:
+ return dbWriteSetting.Value.Primitives.Byte;
+ case DatabaseSettingType.UInt16:
+ return dbWriteSetting.Value.Primitives.Word;
+ case DatabaseSettingType.UInt32:
+ return dbWriteSetting.Value.Primitives.DWord;
+ case DatabaseSettingType.Blob:
+ return Translate.ToBlob(dbWriteSetting.Value.Blob.BlobPtr, dbWriteSetting.Value.Blob.Size);
+ case DatabaseSettingType.Deleted:
+ return null;
+ default:
+ LogUnsupportedValue(dbWriteSetting);
+ return null;
+ }
+ }
+
+ private static void LogUnsupportedValue(DBCONTACTWRITESETTING dbWriteSetting)
+ {
+ string name = "N/A";
+ string module = "N/A";
+
+ try
+ {
+ name = Translate.ToString(dbWriteSetting.Name, StringEncoding.Ansi);
+ module = Translate.ToString(dbWriteSetting.Module, StringEncoding.Ansi);
+ }
+ catch (Exception)
+ { }
+
+ Log.Warning("Attempted to extract an unsupported DB contact setting {0}:{1} of type {1}",
+ module, name, dbWriteSetting.Value.Type.ToString("X"));
+ }
+
+ #endregion
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct DBCONTACTGETSETTING
+ {
+ public string Module;
+ public string Name;
+
+ public IntPtr DbVariantPtr;
+ }
+
+ /*[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct DBCONTACTGETSETTING_PTR
+ {
+ public IntPtr Module;
+ public IntPtr Name;
+
+ public IntPtr DbVariant;
+ }*/
+}
diff --git a/Hyphen/Plugins/Native/DBEVENTGETTEXT.cs b/Hyphen/Plugins/Native/DBEVENTGETTEXT.cs
new file mode 100644
index 0000000..c8ee108
--- /dev/null
+++ b/Hyphen/Plugins/Native/DBEVENTGETTEXT.cs
@@ -0,0 +1,40 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct DBEVENTGETTEXT
+ {
+ public IntPtr DbEventInfoPtr;
+ public int DataType;
+ public int Codepage;
+ }
+
+ /* m_database.h
+ * typedef struct {
+ * DBEVENTINFO* dbei;
+ * int datatype;
+ * int codepage;
+ * } DBEVENTGETTEXT;
+ */
+}
diff --git a/Hyphen/Plugins/Native/DBEVENTINFO.cs b/Hyphen/Plugins/Native/DBEVENTINFO.cs
new file mode 100644
index 0000000..801bb2e
--- /dev/null
+++ b/Hyphen/Plugins/Native/DBEVENTINFO.cs
@@ -0,0 +1,140 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4), CLSCompliant(false), Serializable]
+ public struct DBEVENTINFO
+ {
+ #region Fields
+
+ private readonly int Size;
+
+ public IntPtr Module;
+ public UInt32 Timestamp;
+ public UInt32 Flags;
+ public UInt16 EventType;
+ public UInt32 BlobSize;
+
+ [NonSerialized]
+ public IntPtr BlobPtr;
+
+ #endregion
+
+ #region .ctors
+
+ public DBEVENTINFO(int blobSize, IntPtr blobPtr)
+ {
+ this.Module = IntPtr.Zero;
+ this.Timestamp = this.Flags = this.EventType = 0;
+ this.BlobSize = (uint)blobSize;
+ this.BlobPtr = blobPtr;
+
+ unsafe
+ {
+ this.Size = sizeof(DBEVENTINFO);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Formats the ANSI-\0-UNICODE-\0\0 layout in the memory.
+ ///
+ /// String to layout.
+ /// [OUT] Blob pointer to the resulting memory layout.
+ /// Blob size in bytes.
+ /// Message blob format: ansi\0unicode\0\0
+ public static int LayoutAnsiUniString(string data, out IntPtr pBlob)
+ {
+ int ansiBytesCount = Encoding.Default.GetByteCount(data);
+ int unicodeBytesCount = Encoding.Unicode.GetByteCount(data);
+ int terminatorBytesCount = 3;
+
+ int totalBytes = ansiBytesCount + unicodeBytesCount + terminatorBytesCount;
+
+ pBlob = Marshal.AllocHGlobal(totalBytes);
+ IntPtr pAnsiEnd = new IntPtr(pBlob.ToInt64() + (long)ansiBytesCount);
+ IntPtr pAnsiTermEnd = new IntPtr(pAnsiEnd.ToInt64() + 1L);
+
+ Marshal.Copy(Encoding.Default.GetBytes(data), 0, pBlob, ansiBytesCount);
+ Marshal.Copy(new char[] { '\0' }, 0, pAnsiEnd, 1);
+
+ Marshal.Copy(Encoding.Unicode.GetBytes(data), 0, pAnsiTermEnd, unicodeBytesCount);
+ Marshal.Copy(new char[] { '\0', '\0' }, 0, new IntPtr(pAnsiTermEnd.ToInt64() + (long)unicodeBytesCount), 2);
+
+ return totalBytes;
+ }
+
+ #endregion
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct DBTIMETOSTRING
+ {
+ #region Fields
+
+ public IntPtr Format;
+ public IntPtr Output;
+ public int MaxBytes;
+
+ #endregion
+
+ #region .ctors
+
+ public DBTIMETOSTRING(string format)
+ {
+ Format = new UnmanagedStringHandle(format, StringEncoding.Ansi).IntPtr;
+ Output = IntPtr.Zero;
+ MaxBytes = 0;
+ }
+
+ public void Free()
+ {
+ if (Format != IntPtr.Zero)
+ Marshal.FreeHGlobal(Format);
+ }
+
+ #endregion
+ }
+
+ // typedef struct {
+ // char *szFormat; //format string, as above
+ // char *szDest; //place to put the output string
+ // int cbDest; //maximum number of bytes to put in szDest
+ //} DBTIMETOSTRING;
+
+ // typedef struct {
+ // int cbSize; //size of the structure in bytes
+ // char *szModule; //pointer to eventName of the module that 'owns' this
+ // //event, ie the one that is in control of the data format
+ // DWORD timestamp; //seconds since 00:00, 01/01/1970. Gives us times until
+ // //2106 unless you use the standard C library which is
+ // //signed and can only do until 2038. In GMT.
+ // DWORD flags; //the omnipresent flags
+ // WORD eventType; //module-defined event type field
+ // DWORD cbBlob; //size of pBlob in bytes
+ // PBYTE pBlob; //pointer to buffer containing module-defined event data
+ //} DBEVENTINFO;
+}
diff --git a/Hyphen/Plugins/Native/DBVARIANT.cs b/Hyphen/Plugins/Native/DBVARIANT.cs
new file mode 100644
index 0000000..d6c0818
--- /dev/null
+++ b/Hyphen/Plugins/Native/DBVARIANT.cs
@@ -0,0 +1,116 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ internal enum DbVariantValue : byte
+ {
+ DBVT_DELETED = 0, //this setting just got deleted, no other values are valid
+ DBVT_BYTE = 1, //bVal and cVal are valid
+ DBVT_WORD = 2, //wVal and sVal are valid
+ DBVT_DWORD = 4, //dVal and lVal are valid
+ DBVT_ASCIIZ = 255, //pszVal is valid
+ DBVT_BLOB = 254, //cpbVal and pbVal are valid
+ DBVT_UTF8 = 253, //pszVal is valid
+ DBVT_WCHAR = 252, //pszVal is valid
+ DBVTF_VARIABLELENGTH = 0x80
+ }
+
+ [StructLayout(LayoutKind.Explicit, Pack = 4, Size = 12)]
+ internal struct DBVARIANT
+ {
+ [FieldOffset(0)]
+ public byte Type;
+
+ [FieldOffset(4)]
+ public DBVARIANT_PRIMITIVE Primitives;
+
+ [FieldOffset(4)]
+ public DBVARIANT_TEXT Text;
+
+ [FieldOffset(4)]
+ public DBVARIANT_BLOB Blob;
+ }
+
+ [StructLayout(LayoutKind.Explicit, Pack = 4)]
+ internal struct DBVARIANT_PRIMITIVE
+ {
+ [FieldOffset(0)]
+ public byte Byte;
+
+ [FieldOffset(0)]
+ public char Char;
+
+ [FieldOffset(0)]
+ public UInt16 Word;
+
+ [FieldOffset(0)]
+ public short Short;
+
+ [FieldOffset(0)]
+ public UInt32 DWord;
+
+ [FieldOffset(0)]
+ public int Integer;
+ }
+
+ [StructLayout(LayoutKind.Explicit, Pack = 4)]
+ internal struct DBVARIANT_TEXT
+ {
+ [FieldOffset(0)]
+ public IntPtr TextPtr;
+
+ [FieldOffset(4)]
+ public UInt16 TextBufferSize;
+ }
+
+ [StructLayout(LayoutKind.Explicit, Pack = 4)]
+ internal struct DBVARIANT_BLOB
+ {
+ [FieldOffset(0)]
+ public UInt16 Size;
+
+ [FieldOffset(2)]
+ public IntPtr BlobPtr;
+ }
+
+ // typedef struct {
+ // BYTE type;
+ // union {
+ // BYTE bVal; char cVal;
+ // WORD wVal; short sVal;
+ // DWORD dVal; long lVal;
+ // struct {
+ // union {
+ // char *pszVal;
+ // TCHAR *ptszVal;
+ // WCHAR *pwszVal;
+ // };
+ // WORD cchVal; //only used for db/contact/getsettingstatic
+ // };
+ // struct {
+ // WORD cpbVal;
+ // BYTE *pbVal;
+ // };
+ // };
+ //} DBVARIANT;
+}
diff --git a/Hyphen/Plugins/Native/IUnmanagedMemoryHandle.cs b/Hyphen/Plugins/Native/IUnmanagedMemoryHandle.cs
new file mode 100644
index 0000000..8c30442
--- /dev/null
+++ b/Hyphen/Plugins/Native/IUnmanagedMemoryHandle.cs
@@ -0,0 +1,29 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ public interface IUnmanagedMemoryHandle : IDisposable
+ {
+ IntPtr IntPtr { get; }
+ void Free();
+ }
+}
diff --git a/Hyphen/Plugins/Native/IconImageCache.cs b/Hyphen/Plugins/Native/IconImageCache.cs
new file mode 100644
index 0000000..182c996
--- /dev/null
+++ b/Hyphen/Plugins/Native/IconImageCache.cs
@@ -0,0 +1,193 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ internal sealed class IconImageCache
+ {
+ #region Fields
+
+ private static IconImageCache singleton;
+
+ private readonly Dictionary IconCache;
+ private readonly Dictionary StreamedIconCache;
+ private readonly Dictionary ImageCache;
+
+ #endregion
+
+ #region .ctors
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ private IconImageCache()
+ {
+ IconCache = new Dictionary(1);
+ StreamedIconCache = new Dictionary(1);
+ ImageCache = new Dictionary(1);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public static IconImageCache Singleton
+ {
+ get
+ {
+ return singleton ?? (singleton = new IconImageCache());
+ }
+ }
+
+ #endregion
+
+ #region GetXY methods
+
+ public Icon GetIcon(IntPtr handle)
+ {
+ lock (IconCache)
+ {
+ Icon icon = null;
+ IntPtr key = handle;
+
+ if (IconCache.ContainsKey(key))
+ icon = IconCache[key];
+ else
+ IconCache[key] = icon = Icon.FromHandle(handle);
+
+ return icon;
+ }
+ }
+
+ public Icon GetStreamedIcon(Stream stream)
+ {
+ lock (StreamedIconCache)
+ {
+ lock (IconCache)
+ {
+ int streamHandle = ComputeStreamHandle(stream);
+
+ if (StreamedIconCache.ContainsKey(streamHandle))
+ return StreamedIconCache[streamHandle];
+
+ Icon icon = new Icon(stream);
+
+ StreamedIconCache[streamHandle] = icon;
+
+ if (!IconCache.ContainsKey(icon.Handle))
+ IconCache[icon.Handle] = icon;
+
+ return icon;
+ }
+ }
+ }
+
+ private static int ComputeStreamHandle(Stream stream)
+ {
+ lock (stream)
+ {
+ int streamHandle = 0, result;
+
+ long prevPosition = stream.Position;
+ stream.Position = 0;
+
+ while ((result = stream.ReadByte()) != -1)
+ streamHandle += (byte)result;
+
+ stream.Position = prevPosition;
+ return streamHandle;
+ }
+ }
+
+ public Image GetIconImage(IntPtr handle)
+ {
+ Image image = null;
+ Icon icon = GetIcon(handle);
+
+ lock (ImageCache)
+ {
+ if (ImageCache.ContainsKey(icon))
+ image = ImageCache[icon];
+ else
+ ImageCache[icon] = image = icon.ToBitmap();
+
+ return image;
+ }
+ }
+
+ public bool IsCached(IntPtr handle)
+ {
+ lock (IconCache)
+ return IconCache.ContainsKey(handle);
+ }
+
+ public bool IsCached(Stream stream)
+ {
+ lock (StreamedIconCache)
+ return StreamedIconCache.ContainsKey(ComputeStreamHandle(stream));
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ private void Dispose(bool disposing)
+ {
+ GC.SuppressFinalize(this);
+
+ if (disposing)
+ {
+ lock (IconCache)
+ {
+ lock (ImageCache)
+ {
+ lock (StreamedIconCache)
+ {
+ foreach (Icon icon in IconCache.Values)
+ icon.Dispose();
+
+ foreach (Icon icon in StreamedIconCache.Values)
+ icon.Dispose();
+
+ foreach (Image image in ImageCache.Values)
+ image.Dispose();
+
+ IconCache.Clear();
+ StreamedIconCache.Clear();
+ ImageCache.Clear();
+
+ singleton = null;
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/InteropBuffer.cs b/Hyphen/Plugins/Native/InteropBuffer.cs
new file mode 100644
index 0000000..5eb3e18
--- /dev/null
+++ b/Hyphen/Plugins/Native/InteropBuffer.cs
@@ -0,0 +1,246 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using System.Diagnostics;
+using Virtuoso.Hyphen;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ // Every method touching a memory location or returning its address requires the buffer to be suspended!
+ [CLSCompliant(false)]
+ public sealed class InteropBuffer : IUnmanagedMemoryHandle
+ {
+ #region Fields
+
+ private const string LogCategory = Loader.LogCategory + "::InteropBuffer";
+
+ private readonly int size;
+ private IntPtr sizeAsIntPtr;
+ private UIntPtr sizeAsUIntPtr;
+
+ private readonly object SyncObject;
+
+ private IntPtr intPtr;
+ private volatile int Owner;
+
+ private volatile bool reserved;
+
+ #endregion
+
+ #region .ctors
+
+ internal InteropBuffer(int size)
+ {
+ if (size <= 0) throw new ArgumentOutOfRangeException("size");
+
+ SyncObject = new object();
+ Log.DebuggerWrite(0, LogCategory, "InteropBuffer SyncObject initialized");
+
+ this.size = size;
+
+ intPtr = Marshal.AllocHGlobal(size);
+ Log.DebuggerWrite(0, LogCategory, "InteropBuffer memory allocated (" + size + " B)");
+ }
+
+ ~InteropBuffer()
+ {
+ Dispose(false);
+ }
+
+ private void CheckLock()
+ {
+ if (Owner == 0) throw new InvalidOperationException(TextResources.ExceptionMsg_InteropBufferNotLocked);
+ }
+
+ #endregion
+
+ #region IUnmanagedMemoryHandle Members
+
+ public IntPtr IntPtr
+ {
+ get
+ {
+ lock (SyncObject)
+ {
+ // Touches memory, lock needed
+ CheckLock();
+
+ if (intPtr == IntPtr.Zero)
+ throw new ObjectDisposedException("InteropBuffer");
+
+ return intPtr;
+ }
+ }
+ }
+
+ void IUnmanagedMemoryHandle.Free()
+ {
+ lock (SyncObject)
+ {
+ CheckLock();
+ Dispose(true);
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ void IDisposable.Dispose()
+ {
+ ((IUnmanagedMemoryHandle)this).Free();
+ }
+
+ internal void Dispose(bool disposing)
+ {
+ GC.SuppressFinalize(this);
+
+ if (intPtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(intPtr);
+ intPtr = IntPtr.Zero;
+
+ Log.DebuggerWrite(0, LogCategory, "InteropBuffer memory released");
+ }
+ }
+
+ #endregion
+
+ #region Properties
+
+ public bool Locked
+ {
+ get
+ {
+ return Owner != 0;
+ }
+ }
+
+ public int Size
+ {
+ get
+ {
+ return size;
+ }
+ }
+
+ public IntPtr SizeAsIntPtr
+ {
+ get
+ {
+ if (sizeAsIntPtr == IntPtr.Zero)
+ sizeAsIntPtr = new IntPtr(size);
+
+ return sizeAsIntPtr;
+ }
+ }
+
+ public UIntPtr SizeAsUIntPtr
+ {
+ get
+ {
+ if (sizeAsUIntPtr == UIntPtr.Zero)
+ sizeAsUIntPtr = (UIntPtr)(ulong)size;
+
+ return sizeAsUIntPtr;
+ }
+ }
+
+ internal bool Reserved
+ {
+ get
+ {
+ return reserved;
+ }
+ set
+ {
+ reserved = value;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public override int GetHashCode()
+ {
+ return intPtr.GetHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+
+ InteropBuffer other = obj as InteropBuffer;
+ if (other == null) return false;
+
+ return (GetHashCode() == other.GetHashCode());
+ }
+
+ public void Lock()
+ {
+ Log.DebuggerWrite(0, LogCategory, "Attempting to lock InteropBuffer for thread id " + Thread.CurrentThread.ManagedThreadId);
+
+ Monitor.Enter(SyncObject);
+ Owner = Thread.CurrentThread.ManagedThreadId;
+
+ Log.DebuggerWrite(0, LogCategory, "InteropBuffer locked for thread id " + Owner);
+ }
+
+ public void Unlock()
+ {
+ Log.DebuggerWrite(0, LogCategory, "Attempting to unlock InteropBuffer of thread id " + Thread.CurrentThread.ManagedThreadId);
+ CheckLock();
+
+ if (Owner == Thread.CurrentThread.ManagedThreadId)
+ {
+ Monitor.Exit(SyncObject);
+
+ Log.DebuggerWrite(0, LogCategory, "InteropBuffer unlocked by thread id " + this.Owner);
+ Owner = 0;
+ }
+ else
+ throw new InvalidOperationException(TextResources.ExceptionMsg_InvalidCrossThreadInteropBufferUnlock);
+ }
+
+ public void Zero()
+ {
+ lock (SyncObject)
+ {
+ // Touches memory, lock needed
+ CheckLock();
+
+ for (int i = 0; i < Size; i++)
+ Marshal.WriteByte(intPtr, i, 0);
+ }
+ }
+
+ public static implicit operator IntPtr(InteropBuffer buffer)
+ {
+ return buffer.intPtr;
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/InteropBufferPool.cs b/Hyphen/Plugins/Native/InteropBufferPool.cs
new file mode 100644
index 0000000..0cc9746
--- /dev/null
+++ b/Hyphen/Plugins/Native/InteropBufferPool.cs
@@ -0,0 +1,186 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.CompilerServices;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ // Impl note: should be static, but UnitTests' private accessors dislike that...
+ [CLSCompliant(false)]
+ public sealed class InteropBufferPool
+ {
+ #region Fields
+
+ public const int DefaultMaximumAvailableBufferSize = 260;
+ private const int DefaultBuffersCount = 2;
+
+ private static int maximumAvailableBufferSize = DefaultMaximumAvailableBufferSize;
+
+ public static int MaximumAvailableBufferSize
+ {
+ get { return InteropBufferPool.maximumAvailableBufferSize; }
+ }
+
+ private static int NextAvailableBufferIndex;
+ private static InteropBuffer[] Buffers;
+
+ private static volatile bool Disposed;
+
+ #endregion
+
+ #region .ctors & .dctors
+
+ private InteropBufferPool() { }
+
+ #endregion
+
+ #region Methods
+
+ internal static void Dispose()
+ {
+ if (Disposed || Buffers == null)
+ return;
+
+ lock (Buffers)
+ {
+ Disposed = true;
+
+ for (int i = 0; i < Buffers.Length; i++)
+ {
+ try
+ {
+ Buffers[i].Dispose(true);
+ Buffers[i] = null;
+ }
+ catch { }
+ }
+ }
+ }
+
+ internal static void Refresh()
+ {
+ Dispose();
+
+ Buffers = null;
+ Disposed = false;
+
+ VerifyPoolConsistency();
+ }
+
+ private static void VerifyPoolConsistency()
+ {
+ if (Disposed)
+ throw new ObjectDisposedException("InteropBufferPool");
+
+ if (Buffers == null)
+ {
+ NextAvailableBufferIndex = 0;
+ maximumAvailableBufferSize = DefaultMaximumAvailableBufferSize;
+
+ Buffers = new InteropBuffer[DefaultBuffersCount];
+
+ for (int i = 0; i < DefaultBuffersCount; i++)
+ Buffers[i] = new InteropBuffer(maximumAvailableBufferSize);
+ }
+ }
+
+ public static InteropBuffer AcquireBuffer()
+ {
+ return AcquireBuffer(maximumAvailableBufferSize);
+ }
+
+ public static InteropBuffer AcquireBuffer(int size)
+ {
+ if (size <= 0)
+ throw new ArgumentOutOfRangeException("size");
+
+ VerifyPoolConsistency();
+
+ lock (Buffers)
+ {
+ InteropBuffer buffer = null;
+
+ if (NextAvailableBufferIndex > Buffers.Length - 1 || size > maximumAvailableBufferSize)
+ buffer = new InteropBuffer(size);
+ else
+ {
+ int index = Array.FindIndex(Buffers, delegate(InteropBuffer _buffer)
+ {
+ if (_buffer.Size >= size)
+ return true;
+ else
+ return false;
+ });
+
+ if (index == -1 || index < NextAvailableBufferIndex)
+ buffer = new InteropBuffer(size);
+ else
+ {
+ buffer = Buffers[index];
+ NextAvailableBufferIndex++;
+ }
+ }
+
+ buffer.Reserved = true;
+ return buffer;
+ }
+ }
+
+ public static void ReleaseBuffer(InteropBuffer buffer)
+ {
+ if (buffer == null)
+ return;
+
+ if (buffer.Locked)
+ throw new InvalidOperationException(TextResources.ExceptionMsg_InteropBufferNotUnlocked);
+
+ if (!buffer.Reserved)
+ throw new ArgumentException();
+
+ VerifyPoolConsistency();
+
+ lock (Buffers)
+ {
+ if (Array.IndexOf(Buffers, buffer) == -1)
+ {
+ if (buffer.Size > maximumAvailableBufferSize)
+ {
+ if (NextAvailableBufferIndex <= Buffers.Length - 1)
+ {
+ Buffers[NextAvailableBufferIndex].Dispose(true);
+ Buffers[NextAvailableBufferIndex] = buffer;
+
+ maximumAvailableBufferSize = buffer.Size;
+ }
+ }
+ else
+ buffer.Dispose(true);
+ }
+ else
+ NextAvailableBufferIndex--;
+
+ buffer.Reserved = false;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/MIRANDASYSTRAYNOTIFY.cs b/Hyphen/Plugins/Native/MIRANDASYSTRAYNOTIFY.cs
new file mode 100644
index 0000000..65adeaf
--- /dev/null
+++ b/Hyphen/Plugins/Native/MIRANDASYSTRAYNOTIFY.cs
@@ -0,0 +1,65 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
+ internal struct MIRANDASYSTRAYNOTIFY
+ {
+ #region Fields
+
+ private readonly int Size;
+
+ public string Protocol;
+ public string Title;
+ public string Text;
+ public uint Flags;
+ public uint Timeout;
+
+ #endregion
+
+ #region .ctors
+
+ public MIRANDASYSTRAYNOTIFY(string title, string text, System.Windows.Forms.ToolTipIcon flags)
+ {
+ this.Protocol = String.Empty;
+ this.Title = title;
+ this.Text = text;
+ this.Flags = (uint)flags;
+ this.Timeout = 10 * 1000;
+
+ this.Size = Marshal.SizeOf(typeof(MIRANDASYSTRAYNOTIFY));
+ }
+
+ #endregion
+ }
+
+ /*typedef struct {
+ int cbSize; // sizeof(MIRANDASYSTRAY)
+ char *szProto; // protocol to show under (may have no effect)
+ char *szInfoTitle; // only 64chars of it will be used
+ char *szInfo; // only 256chars of it will be used
+ DWORD dwInfoFlags; // see NIIF_* stuff
+ UINT uTimeout; // how long to show the tip for
+} MIRANDASYSTRAYNOTIFY;*/
+}
diff --git a/Hyphen/Plugins/Native/MM_INTERFACE.cs b/Hyphen/Plugins/Native/MM_INTERFACE.cs
new file mode 100644
index 0000000..51453c8
--- /dev/null
+++ b/Hyphen/Plugins/Native/MM_INTERFACE.cs
@@ -0,0 +1,52 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate IntPtr MMI_MallocPrototype(int size);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate IntPtr MMI_ReallocPrototype(IntPtr ptr, int size);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate IntPtr MMI_FreePrototype(IntPtr ptr);
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct MM_INTERFACE
+ {
+ public int Size;
+ public readonly MMI_MallocPrototype Malloc;
+ public readonly MMI_ReallocPrototype Realloc;
+ public readonly MMI_FreePrototype Free;
+ }
+
+ /* m_system.h
+ * struct MM_INTERFACE
+ {
+ int cbSize;
+ void* (*mmi_malloc) (size_t);
+ void* (*mmi_realloc) (void*, size_t);
+ void (*mmi_free) (void*);
+ }
+ */
+}
\ No newline at end of file
diff --git a/Hyphen/Plugins/Native/MenuItemModifyFlags.cs b/Hyphen/Plugins/Native/MenuItemModifyFlags.cs
new file mode 100644
index 0000000..961fe64
--- /dev/null
+++ b/Hyphen/Plugins/Native/MenuItemModifyFlags.cs
@@ -0,0 +1,34 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [Flags]
+ internal enum MenuItemModifyFlags : uint
+ {
+ None = 0x0,
+ CMIM_NAME = 0x80000000,
+ CMIM_FLAGS = 0x40000000,
+ CMIM_ICON = 0x20000000,
+ CMIM_HOTKEY = 0x10000000,
+ CMIM_ALL = 0xF0000000
+ }
+}
diff --git a/Hyphen/Plugins/Native/MirandaException.cs b/Hyphen/Plugins/Native/MirandaException.cs
new file mode 100644
index 0000000..dee87b4
--- /dev/null
+++ b/Hyphen/Plugins/Native/MirandaException.cs
@@ -0,0 +1,37 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.Serialization;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [Serializable]
+ public class MirandaException : Exception
+ {
+ #region .ctors
+
+ internal MirandaException(string message) : base(message) { }
+ internal MirandaException(string message, Exception inner) : base(message, inner) { }
+
+ protected MirandaException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/NativeMethods.cs b/Hyphen/Plugins/Native/NativeMethods.cs
new file mode 100644
index 0000000..33c43f9
--- /dev/null
+++ b/Hyphen/Plugins/Native/NativeMethods.cs
@@ -0,0 +1,32 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Drawing;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ /*internal static class NativeMethods
+ {
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetCursorPos(ref Point lpPoint);
+ }*/
+}
diff --git a/Hyphen/Plugins/Native/PROTOCOLDESCRIPTOR.cs b/Hyphen/Plugins/Native/PROTOCOLDESCRIPTOR.cs
new file mode 100644
index 0000000..008e0fc
--- /dev/null
+++ b/Hyphen/Plugins/Native/PROTOCOLDESCRIPTOR.cs
@@ -0,0 +1,50 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct PROTOCOLDESCRIPTOR
+ {
+ #region Fields
+
+ private readonly int Size;
+
+ public readonly IntPtr Name;
+ public readonly ProtocolType Type;
+
+ #endregion
+
+ #region .ctors
+
+ public PROTOCOLDESCRIPTOR(string name, ProtocolType type)
+ {
+ this.Name = Translate.ToHandle(name, StringEncoding.Ansi);
+ this.Type = type;
+
+ this.Size = Marshal.SizeOf(typeof(PROTOCOLDESCRIPTOR));
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/UUID.cs b/Hyphen/Plugins/Native/UUID.cs
new file mode 100644
index 0000000..4fc52b6
--- /dev/null
+++ b/Hyphen/Plugins/Native/UUID.cs
@@ -0,0 +1,87 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ internal static class UUID
+ {
+ #region Hyphen
+
+ #region UUID
+
+ private static readonly Guid hyphenUUID = new Guid("A9CB92EC-A8C9-493a-8763-77EB1DBA8228");
+ public static Guid HyphenUUID
+ {
+ get { return UUID.hyphenUUID; }
+ }
+
+ #endregion
+
+ #region Interfaces
+
+ private static readonly Guid HyphenInterfaceUUID = new Guid("9E54961E-D2A2-4939-A23E-FF07F0A27D79");
+
+ private static IntPtr hyphenInterfaceUUIDs;
+ public static IntPtr HyphenInterfaceUUIDs
+ {
+ get
+ {
+ if (hyphenInterfaceUUIDs == IntPtr.Zero)
+ {
+ int uuidSize = Marshal.SizeOf(typeof(Guid));
+ hyphenInterfaceUUIDs = Marshal.AllocHGlobal(2 * uuidSize);
+
+ byte[] uuidBytes = HyphenInterfaceUUID.ToByteArray();
+ Marshal.Copy(uuidBytes, 0, hyphenInterfaceUUIDs, uuidBytes.Length);
+
+ // MIID_LAST
+ uuidBytes = Last.ToByteArray();
+ Marshal.Copy(uuidBytes, 0, new IntPtr(hyphenInterfaceUUIDs.ToInt64() + uuidSize), uuidBytes.Length);
+ }
+
+ return hyphenInterfaceUUIDs;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Miranda
+
+ public static Guid Last
+ {
+ get
+ {
+ return Guid.Empty;
+ }
+ }
+
+ private static Guid protocolUUID = new Guid(0x2a3c815e, 0xa7d9, 0x424b, 0xba, 0x30, 0x2, 0xd0, 0x83, 0x22, 0x90, 0x85);
+ public static Guid ProtocolUUID
+ {
+ get { return protocolUUID; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/UnmanagedStringHandle.cs b/Hyphen/Plugins/Native/UnmanagedStringHandle.cs
new file mode 100644
index 0000000..d834dd9
--- /dev/null
+++ b/Hyphen/Plugins/Native/UnmanagedStringHandle.cs
@@ -0,0 +1,152 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ public enum StringEncoding
+ {
+ Ansi,
+ Unicode,
+ MirandaDefault
+ }
+
+ public struct UnmanagedStringHandle : IUnmanagedMemoryHandle
+ {
+ #region Fields
+
+ private IntPtr intPtr;
+ public static readonly UnmanagedStringHandle Empty = new UnmanagedStringHandle();
+
+ private string originalString;
+ private StringEncoding encoding;
+
+ #endregion
+
+ #region .ctors
+
+ public UnmanagedStringHandle(string str, StringEncoding encoding)
+ {
+ reEval:
+ switch (encoding)
+ {
+ case StringEncoding.Unicode:
+ this.intPtr = Marshal.StringToHGlobalUni(str);
+ break;
+ case StringEncoding.Ansi:
+ this.intPtr = Marshal.StringToHGlobalAnsi(str);
+ break;
+ default:
+ encoding = MirandaEnvironment.MirandaStringEncoding;
+
+ if (encoding == StringEncoding.MirandaDefault)
+ throw new ArgumentException(TextResources.ExceptionMsg_CannotDetectMirandaDefaultStringEncoding);
+
+ goto reEval;
+ }
+
+ this.originalString = str;
+ this.encoding = encoding;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public string OriginalString
+ {
+ get { return originalString; }
+ }
+
+ public StringEncoding Encoding
+ {
+ get
+ {
+ return encoding;
+ }
+ }
+
+ public IntPtr IntPtr
+ {
+ get { return intPtr; }
+ }
+
+ public static implicit operator IntPtr(UnmanagedStringHandle operand)
+ {
+ return operand.IntPtr;
+ }
+
+ [CLSCompliant(false)]
+ public static implicit operator UIntPtr(UnmanagedStringHandle operand)
+ {
+ return Translate.ToHandle(operand.IntPtr);
+ }
+
+ public int Size
+ {
+ get
+ {
+ if (!IsValid)
+ return 0;
+
+ switch (encoding)
+ {
+ case StringEncoding.Ansi:
+ return System.Text.Encoding.Default.GetByteCount(originalString);
+ case StringEncoding.Unicode:
+ return System.Text.Encoding.Unicode.GetByteCount(originalString);
+ default:
+ return -1;
+ }
+ }
+ }
+
+ public bool IsValid
+ {
+ get
+ {
+ return (intPtr != IntPtr.Zero);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void Free()
+ {
+ if (IsValid)
+ {
+ Marshal.FreeHGlobal(intPtr);
+ intPtr = IntPtr.Zero;
+ }
+ }
+
+ void IDisposable.Dispose()
+ {
+ Free();
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/Native/UnmanagedStructHandle.cs b/Hyphen/Plugins/Native/UnmanagedStructHandle.cs
new file mode 100644
index 0000000..ea65b76
--- /dev/null
+++ b/Hyphen/Plugins/Native/UnmanagedStructHandle.cs
@@ -0,0 +1,211 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins.Native
+{
+ public enum MarshalKind
+ {
+ Copy,
+ PinBlittable
+ }
+
+ public struct UnmanagedStructHandle : IUnmanagedMemoryHandle
+ {
+ #region Fields
+
+ public readonly static UnmanagedStructHandle Empty = new UnmanagedStructHandle();
+
+ private Type ActualType;
+ private MarshalKind MarshalKind;
+
+ private GCHandle GcHandle;
+ private IntPtr intPtr;
+
+ private IntPtr SinglePressure;
+ private IntPtr[] Pressure;
+
+ #endregion
+
+ #region .ctors
+
+ public UnmanagedStructHandle(ref T structure)
+ : this(ref structure, MarshalKind.Copy, null)
+ { }
+
+ public UnmanagedStructHandle(ref T structure, IntPtr pressure)
+ : this(ref structure, MarshalKind.Copy, null)
+ {
+ this.SinglePressure = pressure;
+ }
+
+ public UnmanagedStructHandle(ref T structure, params IntPtr[] pressure)
+ : this(ref structure, MarshalKind.Copy, pressure)
+ { }
+
+ public UnmanagedStructHandle(ref T structure, MarshalKind marshalKind)
+ : this(ref structure, marshalKind, null)
+ { }
+
+ public UnmanagedStructHandle(ref T structure, MarshalKind marshalKind, IntPtr pressure)
+ : this(ref structure, marshalKind, null)
+ {
+ this.SinglePressure = pressure;
+ }
+
+ public UnmanagedStructHandle(ref T structure, MarshalKind marshalKind, params IntPtr[] pressure)
+ {
+ // The structure is of a reference type and is null...
+ if (!typeof(T).IsValueType && object.ReferenceEquals(structure, null))
+ throw new ArgumentNullException("structure");
+
+ this.SinglePressure = IntPtr.Zero;
+ this.Pressure = pressure;
+ this.MarshalKind = marshalKind;
+ this.ActualType = structure.GetType();
+
+ switch (marshalKind)
+ {
+ case MarshalKind.Copy:
+ this.intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ActualType));
+ Marshal.StructureToPtr((object)structure, this.intPtr, false);
+ this.GcHandle = new GCHandle();
+ break;
+ case MarshalKind.PinBlittable:
+ this.GcHandle = GCHandle.Alloc((object)structure, GCHandleType.Pinned);
+ this.intPtr = this.GcHandle.AddrOfPinnedObject();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("marshalKind");
+ }
+ }
+
+ #endregion
+
+ #region Operator overloads
+
+ public static implicit operator IntPtr(UnmanagedStructHandle operand)
+ {
+ return operand.IntPtr;
+ }
+
+ [CLSCompliant(false)]
+ public static implicit operator UIntPtr(UnmanagedStructHandle operand)
+ {
+ return Translate.ToHandle(operand.IntPtr);
+ }
+
+ #endregion
+
+ #region Properties
+
+ public IntPtr IntPtr
+ {
+ get
+ {
+ return intPtr;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void MarshalBack(out T destination)
+ {
+ if (MarshalKind == MarshalKind.PinBlittable)
+ destination = (T)GcHandle.Target;
+ else
+ destination = (T)Marshal.PtrToStructure(IntPtr, ActualType);
+ }
+
+ public override int GetHashCode()
+ {
+ return intPtr.GetHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null || !(obj is UnmanagedStructHandle))
+ return false;
+
+ return Equals((UnmanagedStructHandle)obj);
+ }
+
+ public bool Equals(UnmanagedStructHandle other)
+ {
+ return (other.intPtr == this.intPtr && other.Pressure == this.Pressure && other.SinglePressure == this.SinglePressure);
+ }
+
+ #endregion
+
+ void IDisposable.Dispose()
+ {
+ Free();
+ }
+
+ public void Free()
+ {
+ try
+ {
+ if (IntPtr != IntPtr.Zero)
+ {
+ FreePressure();
+
+ if (MarshalKind == MarshalKind.PinBlittable)
+ GcHandle.Free();
+ else
+ Marshal.DestroyStructure(IntPtr, ActualType);
+
+ intPtr = IntPtr.Zero;
+ ActualType = null;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.DebuggerWrite(100, GetType().FullName, "Unable to free a struct handle: " + e.ToString());
+ }
+ }
+
+ private void FreePressure()
+ {
+ if (SinglePressure != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(SinglePressure);
+ SinglePressure = IntPtr.Zero;
+ }
+
+ if (Pressure != null)
+ {
+ for (int i = 0; i < Pressure.Length; i++)
+ {
+ IntPtr ptr = Pressure[i];
+
+ if (ptr != IntPtr.Zero)
+ Marshal.FreeHGlobal(ptr);
+ }
+
+ Pressure = null;
+ }
+ }
+ }
+}
diff --git a/Hyphen/Plugins/PMConfiguration.cs b/Hyphen/Plugins/PMConfiguration.cs
new file mode 100644
index 0000000..2650573
--- /dev/null
+++ b/Hyphen/Plugins/PMConfiguration.cs
@@ -0,0 +1,85 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [ConfigurationOptions("0.1.0.0", ProfileBound = true), Serializable]
+ internal class PMConfiguration : PluginConfiguration
+ {
+ #region .ctors
+
+ private PMConfiguration() { }
+
+ protected override void InitializeDefaultConfiguration()
+ {
+ disabledPlugins = new List(1);
+ base.InitializeDefaultConfiguration();
+ }
+
+ public static void Initialize()
+ {
+ if (singleton != null)
+ throw new InvalidOperationException();
+
+ singleton = Load();
+ }
+
+ #endregion
+
+ #region Properties
+
+ private static PMConfiguration singleton;
+ public static PMConfiguration Singleton
+ {
+ get
+ {
+ if (singleton == null)
+ throw new InvalidOperationException();
+
+ return singleton;
+ }
+ }
+
+ private List disabledPlugins;
+ public List DisabledPlugins
+ {
+ get { return disabledPlugins; }
+ set { disabledPlugins = value; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public static void Reset()
+ {
+ singleton = PluginConfiguration.GetDefaultConfiguration();
+ }
+
+ public static void Reload()
+ {
+ Initialize();
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/PluginDescriptor.cs b/Hyphen/Plugins/PluginDescriptor.cs
new file mode 100644
index 0000000..2cd5803
--- /dev/null
+++ b/Hyphen/Plugins/PluginDescriptor.cs
@@ -0,0 +1,320 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Virtuoso.Miranda.Plugins.Native;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Collections;
+using System.Reflection;
+using System.Diagnostics;
+using Virtuoso.Miranda.Plugins.Resources;
+using Virtuoso.Hyphen.Mini;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [DebuggerDisplay("{Plugin}")]
+ public sealed class PluginDescriptor : IDescriptor
+ {
+ #region Fields
+
+ private static readonly Type HookDescriptorType = typeof(HookDescriptor),
+ EventHookAttribType = typeof(EventHookAttribute),
+ MenuItemAttribType = typeof(MenuItemDeclarationAttribute),
+ ServiceFncAttribType = typeof(ServiceFunctionAttribute),
+ CallbackDelegType = typeof(Callback);
+
+ private readonly MirandaPlugin plugin;
+ private PluginState PluginStateInternal;
+
+ private readonly HookDescriptorCollection eventHooks, serviceFunctions;
+ private readonly EventHandleCollection eventHandles;
+
+ #endregion
+
+ #region .ctors
+
+ private PluginDescriptor(MirandaPlugin plugin)
+ {
+ if (plugin == null)
+ throw new ArgumentNullException("plugin");
+
+ this.plugin = plugin;
+
+ this.eventHooks = new HookDescriptorCollection();
+ this.serviceFunctions = new HookDescriptorCollection();
+ this.eventHandles = new EventHandleCollection();
+
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ Plugin.Descriptor = this;
+
+ Type pluginType = Plugin.GetType();
+ Assembly pluginAssembly = pluginType.Assembly;
+
+ MethodInfo[] methods = pluginType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy);
+
+ foreach (MethodInfo method in methods)
+ {
+ PopulateMethodHooksByAttribute(method, EventHooks);
+ PopulateMethodHooksByAttribute(method, ServiceFunctions);
+
+ PopulateMethodLevelDeclaredMenuItems(method);
+ }
+
+ PopulateTopLevelDeclaredMenuItems();
+ Plugin.AfterMenuItemsPopulationInternal(Plugin.MenuItemsCollection);
+
+ Plugin.AfterPluginInitializationInternal();
+ }
+
+ internal static PluginDescriptor SetUp(MirandaPlugin plugin)
+ {
+ if (plugin == null)
+ throw new ArgumentNullException("plugin");
+
+ try
+ {
+ return new PluginDescriptor(plugin);
+ }
+ catch (FusionException)
+ {
+ throw;
+ }
+ catch (Exception e)
+ {
+ throw new FusionException(String.Format(TextResources.ExceptionMsg_Formatable1_UnableToSetUpPluginDescriptor, e.Message), plugin.GetType().Assembly, plugin.GetType(), plugin, e);
+ }
+ }
+
+ #endregion
+
+ #region Helpers
+
+ private void PopulateMethodHooksByAttribute(MethodInfo method, HookDescriptorCollection hookBag) where TAttrib : HookAttribute
+ {
+ if (method == null)
+ throw new ArgumentNullException("method");
+
+ if (hookBag == null)
+ throw new ArgumentNullException("hookBag");
+
+ Type attribType = typeof(TAttrib);
+
+ if (method.IsDefined(attribType, true))
+ {
+ TAttrib[] attributes = (TAttrib[])Attribute.GetCustomAttributes(method, attribType, true);
+
+ if (attributes.Length > 0)
+ PopulateMethodHooks(method, hookBag, attributes);
+ }
+ }
+
+ private void PopulateMethodHooks(MethodInfo method, HookDescriptorCollection hookBag, params TAttrib[] attributes) where TAttrib : HookAttribute
+ {
+ if (method == null)
+ throw new ArgumentNullException("method");
+
+ if (hookBag == null)
+ throw new ArgumentNullException("hookBag");
+
+ if (attributes == null)
+ throw new ArgumentNullException("attributes");
+
+ if (attributes.Length == 0)
+ return;
+
+ Callback hookCallback = Delegate.CreateDelegate(CallbackDelegType, Plugin, method, false) as Callback;
+
+ if (hookCallback == null)
+ throw new FusionException(String.Format(TextResources.ExceptionMsg_Formatable1_InvalidMethodSignature, method.Name), Plugin.GetType().Assembly, Plugin.GetType(), Plugin, null);
+
+ foreach (TAttrib attribute in attributes)
+ {
+ HookDescriptor.SetUpAndStore(hookBag, attribute.HookName, this, hookCallback, attribute.HookType);
+ }
+ }
+
+ private void PopulateTopLevelDeclaredMenuItems()
+ {
+ Type pluginType = Plugin.GetType();
+
+ if (pluginType.IsDefined(MenuItemAttribType, true))
+ {
+ MenuItemDeclarationAttribute[] menuItemAttribs = (MenuItemDeclarationAttribute[])pluginType.GetCustomAttributes(MenuItemAttribType, true);
+ Plugin.MenuItemsCollection.AddRange(menuItemAttribs);
+ }
+ }
+
+ private void PopulateMethodLevelDeclaredMenuItems(MethodInfo method)
+ {
+ if (method == null)
+ throw new ArgumentNullException("method");
+
+ if (method.IsDefined(MenuItemAttribType, true))
+ {
+ string methodService = String.Format("{0}.{1}", Plugin.GetType().FullName, method.Name);
+ MenuItemDeclarationAttribute[] menuItems = (MenuItemDeclarationAttribute[])method.GetCustomAttributes(MenuItemAttribType, true);
+
+ bool serviceRegistered = false;
+
+ foreach (MenuItemDeclarationAttribute menuItem in menuItems)
+ {
+ if (!String.IsNullOrEmpty(menuItem.Service))
+ throw new InvalidOperationException(String.Format(TextResources.ExceptionMsg_Formatable3_MenuItemServiceAlreadySet, menuItem.Text, methodService, menuItem.Service));
+
+ if (!serviceRegistered)
+ {
+ PopulateMethodHooks(method, ServiceFunctions, new ServiceFunctionAttribute(methodService));
+ serviceRegistered = true;
+ }
+
+ menuItem.Service = methodService;
+ Plugin.MenuItemsCollection.Add(menuItem);
+ }
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public override int GetHashCode()
+ {
+ return plugin.GetHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null || !(obj is PluginDescriptor))
+ return false;
+
+ return plugin.Equals(((PluginDescriptor)obj).plugin);
+ }
+
+ public void SetPluginState(PluginState newState)
+ {
+ SetPluginState(newState, false);
+ }
+
+ public void SetPluginState(PluginState newState, bool rememberState)
+ {
+ MirandaContext.Current.PluginManager.SetPluginState(this, newState, rememberState);
+ }
+
+ internal void AssociateHook(HookDescriptor hook)
+ {
+ if (hook == null)
+ throw new ArgumentNullException("hook");
+
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(this);
+
+ switch (hook.HookType)
+ {
+ case HookType.EventHook:
+ eventHooks.Add(hook);
+ break;
+ case HookType.ServiceFunction:
+ serviceFunctions.Add(hook);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("hook");
+ }
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(this);
+ }
+ }
+
+ internal void UpdatePluginState(PluginState state)
+ {
+ if (!Enum.IsDefined(typeof(PluginState), state))
+ throw new ArgumentOutOfRangeException("state");
+
+ PluginStateInternal = state;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public PluginState PluginState
+ {
+ get
+ {
+ return PluginStateInternal;
+ }
+ }
+
+ public MirandaPlugin Plugin
+ {
+ get
+ {
+ return plugin;
+ }
+ }
+
+ public bool IsStandalone
+ {
+ get
+ {
+ return plugin is StandalonePlugin;
+ }
+ }
+
+ internal bool IsConfigurable
+ {
+ get
+ {
+ return plugin.HasOptions && plugin is IConfigurablePlugin;
+ }
+ }
+
+ internal HookDescriptorCollection EventHooks
+ {
+ get
+ {
+ return eventHooks;
+ }
+ }
+
+ internal HookDescriptorCollection ServiceFunctions
+ {
+ get
+ {
+ return serviceFunctions;
+ }
+ }
+
+ internal EventHandleCollection EventHandles
+ {
+ get
+ {
+ return eventHandles;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Hyphen/Plugins/PluginManagerBase.cs b/Hyphen/Plugins/PluginManagerBase.cs
new file mode 100644
index 0000000..19dc9a5
--- /dev/null
+++ b/Hyphen/Plugins/PluginManagerBase.cs
@@ -0,0 +1,467 @@
+/***********************************************************************\
+ * Virtuoso.Miranda.Plugins (Hyphen) *
+ * Provides a managed wrapper for API of IM client Miranda. *
+ * Copyright (C) 2006-2009 virtuoso *
+ * deml.tomas@seznam.cz *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2.1 of the License, or (at your option) any later version. *
+ * *
+ * This library 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 *
+ * Lesser General Public License for more details. *
+\***********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using System.Windows.Forms;
+using Virtuoso.Hyphen;
+using Virtuoso.Hyphen.Mini;
+using Virtuoso.Miranda.Plugins.Collections;
+using Virtuoso.Miranda.Plugins.Forms;
+using Virtuoso.Miranda.Plugins.Infrastructure;
+using Virtuoso.Miranda.Plugins.Resources;
+
+namespace Virtuoso.Miranda.Plugins
+{
+ [CLSCompliant(false)]
+ public abstract class PluginManagerBase : ContextWorker, IExceptionHandler
+ {
+ #region Constants
+
+ internal const string LogCategory = Loader.LogCategory + "::PluginManagerBase";
+
+ #endregion
+
+ #region Fields
+
+ protected internal static readonly Type PluginType = typeof(MirandaPlugin);
+ protected internal static readonly Type ExposingPluginAttributeType = typeof(ExposingPluginAttribute);
+
+ private bool initialized;
+ private readonly PluginDescriptorCollection pluginDescriptors;
+ private readonly AppDomain livingDomain;
+ private readonly PluginDescriptorReadOnlyCollection pluginDescriptorsAsReadOnly;
+ private readonly FusionContext fusionContext;
+
+ #endregion
+
+ #region .ctors
+
+ protected PluginManagerBase(FusionContext fusionContext) : this(fusionContext, true, true) { }
+
+ internal PluginManagerBase(FusionContext fusionContext, bool initializeMirandaContext, bool initializeConfiguration)
+ {
+ if (fusionContext == null)
+ throw new ArgumentNullException("fusionContext");
+
+ this.livingDomain = AppDomain.CurrentDomain;
+ this.fusionContext = fusionContext;
+
+ this.pluginDescriptors = new PluginDescriptorCollection();
+ this.pluginDescriptorsAsReadOnly = new PluginDescriptorReadOnlyCollection(this.pluginDescriptors);
+
+ if (initializeMirandaContext)
+ {
+ if (!fusionContext.IsInvalid)
+ {
+ IntPtr pluginLink = fusionContext.NativePluginLink;
+
+ // Invalidate IsolatedPluginsSandbox's AppDomain context available to managed plugins
+ MirandaContext.InvalidateCurrent();
+
+ /* Why am I marshaling the link again? 'Cause MirandaPluginLink resides in another AppDomain
+ * and se/de-serialization of it would be much slower */
+ MirandaContext.InitializeCurrent(MirandaPluginLink.FromPointer(pluginLink), this);
+ }
+ else
+ throw new ArgumentException("fusionContext");
+ }
+
+ if (initializeConfiguration)
+ PMConfiguration.Initialize();
+ }
+
+ #endregion
+
+ #region Events & delegates
+
+ public delegate void PluginManagerContextCallback(PluginManagerBase sender, T state);
+
+ public static event EventHandler PrimaryPluginManagerInitialized;
+ public event EventHandler FusionCompleted;
+ public event EventHandler PluginStateChange;
+
+ protected static void FirePrimaryPluginManagerInitializedEvent(PluginManagerBase sender, EventArgs e)
+ {
+ if (PrimaryPluginManagerInitialized != null)
+ PrimaryPluginManagerInitialized(sender, e);
+ }
+
+ protected void RaiseFusionCompletedEvent(EventArgs e)
+ {
+ if (FusionCompleted != null)
+ FusionCompleted(this, e);
+ }
+
+ protected void FirePluginStateChangeEvent(PluginStateChangeEventArgs e)
+ {
+ if (PluginStateChange != null)
+ PluginStateChange(this, e);
+ }
+
+ #endregion
+
+ #region Properties
+
+ protected bool Initialized
+ {
+ get
+ {
+ return initialized;
+ }
+ }
+
+ protected PluginDescriptorCollection PluginDescriptors
+ {
+ get { return pluginDescriptors; }
+ }
+
+ protected AppDomain LivingDomain
+ {
+ get
+ {
+ return livingDomain;
+ }
+ }
+
+ public PluginDescriptorReadOnlyCollection Plugins
+ {
+ get
+ {
+ return pluginDescriptorsAsReadOnly;
+ }
+ }
+
+ public FusionContext FusionContext
+ {
+ get
+ {
+ return fusionContext;
+ }
+ }
+
+ #endregion
+
+ #region Fusion
+
+ protected internal abstract void FindAndLoadPlugins();
+
+ protected internal static Type[] GetExposedPlugins(Assembly assembly)
+ {
+ if (!assembly.IsDefined(ExposingPluginAttributeType, false))
+ return new Type[0];
+
+ return Array.ConvertAll((ExposingPluginAttribute[])assembly.GetCustomAttributes(PluginManagerBase.ExposingPluginAttributeType, false),
+ delegate(ExposingPluginAttribute attrib)
+ {
+ return attrib.PluginType;
+ });
+ }
+
+ protected void DeclareInitialized()
+ {
+ initialized = true;
+ }
+
+ protected virtual void AccountPluginDescriptor(PluginDescriptor pluginDescriptor)
+ {
+ if (pluginDescriptor == null)
+ throw new ArgumentNullException("pluginDescriptor");
+
+ try
+ {
+ SynchronizationHelper.BeginCollectionUpdate(pluginDescriptors);
+
+ if (pluginDescriptors.ContainsDescriptorOf(pluginDescriptor.Plugin))
+ throw new InvalidOperationException(TextResources.ExceptionMsg_PluginAlreadyInitialized);
+
+ pluginDescriptors.Add(pluginDescriptor);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(pluginDescriptors);
+ }
+ }
+
+ protected internal static MirandaPlugin InstantiatePlugin(Type type)
+ {
+ return InstantiatePlugin(type, false);
+ }
+
+ internal static MirandaPlugin InstantiatePlugin(Type type, bool acceptIndividualPlugins)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ if (!type.IsSubclassOf(PluginType) || (!acceptIndividualPlugins && type.IsSubclassOf(StandalonePlugin.PluginType)))
+ return null;
+
+ LoaderOptionsAttribute loaderOptions = LoaderOptionsAttribute.Get(type, LoaderOptionsOwner.Type);
+
+ if (loaderOptions.RequiredVersion > Loader.HyphenVersion)
+ throw new RuntimeNotSupportedException(type, loaderOptions.RequiredVersion);
+
+ if (!loaderOptions.SupportsMirandaVersion(MirandaEnvironment.MirandaVersion))
+ throw new RuntimeNotSupportedException(type, loaderOptions.MinimalMirandaVersion, false);
+
+ return (MirandaPlugin)Activator.CreateInstance(type, true);
+ }
+
+ protected static void RegisterMenuItems(PluginDescriptor pluginDescriptor)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(pluginDescriptor);
+ MirandaPlugin owner = pluginDescriptor.Plugin;
+
+ ContactList list = MirandaContext.Current.ContactList;
+
+ foreach (MenuItemDeclarationAttribute menuItemAttrib in owner.MenuItemsCollection)
+ list.AddMenuItem(owner, menuItemAttrib);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(pluginDescriptor);
+ }
+ }
+
+ protected static void UnregisterMenuItems(PluginDescriptor pluginDescriptor)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(pluginDescriptor);
+ MirandaPlugin owner = pluginDescriptor.Plugin;
+
+ ContactList list = MirandaContext.Current.ContactList;
+
+ foreach (MenuItemDeclarationAttribute menuItemAttrib in owner.MenuItems)
+ {
+ bool result = list.ModifyMenuItem(owner, menuItemAttrib, null, MenuItemProperties.Hidden, null, 0, false);
+ Debug.Assert(result);
+ }
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(pluginDescriptor);
+ }
+ }
+
+ protected void HookPlugin(PluginDescriptor pluginDescriptor)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(pluginDescriptor);
+
+ MirandaContext context = MirandaContext.Current;
+
+ HookManager.CreateHooks(pluginDescriptor.ServiceFunctions.ToArray());
+ HookManager.CreateHooks(pluginDescriptor.EventHooks.ToArray());
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(pluginDescriptor);
+ }
+ }
+
+ #endregion
+
+ #region Management
+
+ public void DoContextCallback(PluginManagerContextCallback del, T state)
+ {
+ if (del == null)
+ throw new ArgumentNullException("del");
+
+ del(this, state);
+ }
+
+ public virtual void SetPluginState(PluginDescriptor pluginDescriptor, PluginState newState)
+ {
+ SetPluginState(pluginDescriptor, newState, false);
+ }
+
+ public virtual void SetPluginState(PluginDescriptor pluginDescriptor, PluginState newState, bool rememberState)
+ {
+ try
+ {
+ SynchronizationHelper.BeginDescriptorUpdate(pluginDescriptor);
+ PluginState previousState = pluginDescriptor.PluginState;
+
+ if (previousState == newState || previousState == PluginState.CrashDisabled && newState != PluginState.Enabled)
+ return;
+
+ pluginDescriptor.UpdatePluginState(newState);
+ FirePluginStateChangeEvent(new PluginStateChangeEventArgs(previousState, newState));
+
+ if (newState == PluginState.Enabled)
+ EnablePlugin(pluginDescriptor, rememberState);
+ else
+ DisablePlugin(pluginDescriptor, rememberState);
+ }
+ finally
+ {
+ SynchronizationHelper.EndUpdate(pluginDescriptor);
+ }
+ }
+
+ public PluginDescriptor LoadPlugin(MirandaPlugin plugin)
+ {
+ return LoadPlugin(plugin, true);
+ }
+
+ #region Internals
+
+ private void EnablePlugin(PluginDescriptor pluginDescriptor, bool rememberState)
+ {
+ HookPlugin(pluginDescriptor);
+ RegisterMenuItems(pluginDescriptor);
+
+ pluginDescriptor.Plugin.AfterPluginEnableInternal();
+
+ if (rememberState)
+ {
+ List