Initial commit
25
RCM.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.28307.572
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCM", "RCM\RCM.csproj", "{F5912EF4-EDC9-421D-B474-4CDADB121E55}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {144EB07E-AE7D-4681-B9C9-DB12547017F3}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
82
RCM/Config.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public static class Config {
|
||||||
|
private static readonly char[] _equals = new char[] { '=' };
|
||||||
|
private static readonly char[] _brackets = new char[] { '[', ']' };
|
||||||
|
private static readonly string _fileName = "RCM.conf";
|
||||||
|
|
||||||
|
private static Icon _emptyIcon;
|
||||||
|
public static ImageList IconList { get; } = new ImageList() { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(20, 20) };
|
||||||
|
|
||||||
|
public static List<Type> DataSourceTypes { get; } = new List<Type>();
|
||||||
|
public static List<Type> RecordTypes { get; } = new List<Type>();
|
||||||
|
|
||||||
|
public static List<IDataSource> DataSources { get; } = new List<IDataSource>();
|
||||||
|
public static Dictionary<Type, IConfigSection> ConfigSections { get; } = new Dictionary<Type, IConfigSection>();
|
||||||
|
private static List<JObject> _uknownSections = new List<JObject>();
|
||||||
|
|
||||||
|
public static void LoadModules() {
|
||||||
|
_emptyIcon = (Icon)new ComponentResourceManager(typeof(Config)).GetObject("EmptyIcon");
|
||||||
|
|
||||||
|
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes().Where(type => !type.IsInterface);
|
||||||
|
foreach (Type type in types) {
|
||||||
|
if (typeof(IDataSource).IsAssignableFrom(type)) {
|
||||||
|
DataSourceTypes.Add(type);
|
||||||
|
AddIconForType(type);
|
||||||
|
} else if (typeof(IRecord).IsAssignableFrom(type)) {
|
||||||
|
RecordTypes.Add(type);
|
||||||
|
AddIconForType(type);
|
||||||
|
} else if (typeof(IConfigSection).IsAssignableFrom(type)) {
|
||||||
|
ConfigSections[type] = (IConfigSection)Activator.CreateInstance(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddIconForType(Type type) {
|
||||||
|
try {
|
||||||
|
IconList.Images.Add(type.FullName, (Icon)new ComponentResourceManager(type).GetObject("Icon"));
|
||||||
|
} catch {
|
||||||
|
IconList.Images.Add(type.FullName, _emptyIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadConfiguration() {
|
||||||
|
if (File.Exists(_fileName)) {
|
||||||
|
JArray json = JArray.Parse(File.ReadAllText(_fileName));
|
||||||
|
foreach (JObject section in json) {
|
||||||
|
try {
|
||||||
|
LoadSection(section);
|
||||||
|
} catch {
|
||||||
|
_uknownSections.Add(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadSection(JObject section) {
|
||||||
|
object obj = ConfigSerializer.JsonToObj(section);
|
||||||
|
if (obj is IDataSource dataSource) {
|
||||||
|
DataSources.Add(dataSource);
|
||||||
|
} else if (obj is IConfigSection configSection) {
|
||||||
|
ConfigSections[configSection.GetType()] = configSection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Save() {
|
||||||
|
JArray json = new JArray(DataSources.Select(dataSource => ConfigSerializer.ObjToJson(dataSource))
|
||||||
|
.Union(ConfigSections.Values.Select(configSection => ConfigSerializer.ObjToJson(configSection)))
|
||||||
|
.Union(_uknownSections));
|
||||||
|
File.WriteAllText(_fileName, ConfigSerializer.JsonToString(json));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Config.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="EmptyIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Empty.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
167
RCM/ConfigForm.Designer.cs
generated
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class ConfigForm {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.treeView = new System.Windows.Forms.TreeView();
|
||||||
|
this.tabControl = new System.Windows.Forms.TabControl();
|
||||||
|
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||||
|
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||||
|
this.panelDataSource = new System.Windows.Forms.Panel();
|
||||||
|
this.buttonSave = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxTitle = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelTitle = new System.Windows.Forms.Label();
|
||||||
|
this.tabControl.SuspendLayout();
|
||||||
|
this.tabPage1.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
||||||
|
this.splitContainer1.Panel1.SuspendLayout();
|
||||||
|
this.splitContainer1.Panel2.SuspendLayout();
|
||||||
|
this.splitContainer1.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// treeView
|
||||||
|
//
|
||||||
|
this.treeView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.treeView.LabelEdit = true;
|
||||||
|
this.treeView.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.treeView.Name = "treeView";
|
||||||
|
this.treeView.ShowRootLines = false;
|
||||||
|
this.treeView.Size = new System.Drawing.Size(239, 464);
|
||||||
|
this.treeView.TabIndex = 0;
|
||||||
|
this.treeView.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.TreeView_AfterLabelEdit);
|
||||||
|
this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterSelect);
|
||||||
|
this.treeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseClick);
|
||||||
|
this.treeView.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TreeView_KeyDown);
|
||||||
|
this.treeView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TreeView_MouseUp);
|
||||||
|
//
|
||||||
|
// tabControl
|
||||||
|
//
|
||||||
|
this.tabControl.Controls.Add(this.tabPage1);
|
||||||
|
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.tabControl.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.tabControl.Name = "tabControl";
|
||||||
|
this.tabControl.SelectedIndex = 0;
|
||||||
|
this.tabControl.Size = new System.Drawing.Size(574, 496);
|
||||||
|
this.tabControl.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// tabPage1
|
||||||
|
//
|
||||||
|
this.tabPage1.BackColor = System.Drawing.SystemColors.Control;
|
||||||
|
this.tabPage1.Controls.Add(this.splitContainer1);
|
||||||
|
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.tabPage1.Name = "tabPage1";
|
||||||
|
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||||
|
this.tabPage1.Size = new System.Drawing.Size(566, 470);
|
||||||
|
this.tabPage1.TabIndex = 0;
|
||||||
|
this.tabPage1.Text = "Data Sources";
|
||||||
|
//
|
||||||
|
// splitContainer1
|
||||||
|
//
|
||||||
|
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.splitContainer1.Location = new System.Drawing.Point(3, 3);
|
||||||
|
this.splitContainer1.Name = "splitContainer1";
|
||||||
|
//
|
||||||
|
// splitContainer1.Panel1
|
||||||
|
//
|
||||||
|
this.splitContainer1.Panel1.Controls.Add(this.treeView);
|
||||||
|
//
|
||||||
|
// splitContainer1.Panel2
|
||||||
|
//
|
||||||
|
this.splitContainer1.Panel2.Controls.Add(this.panelDataSource);
|
||||||
|
this.splitContainer1.Panel2.Controls.Add(this.buttonSave);
|
||||||
|
this.splitContainer1.Panel2.Controls.Add(this.textBoxTitle);
|
||||||
|
this.splitContainer1.Panel2.Controls.Add(this.labelTitle);
|
||||||
|
this.splitContainer1.Size = new System.Drawing.Size(560, 464);
|
||||||
|
this.splitContainer1.SplitterDistance = 239;
|
||||||
|
this.splitContainer1.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// panelDataSource
|
||||||
|
//
|
||||||
|
this.panelDataSource.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelDataSource.Location = new System.Drawing.Point(10, 53);
|
||||||
|
this.panelDataSource.Name = "panelDataSource";
|
||||||
|
this.panelDataSource.Size = new System.Drawing.Size(307, 373);
|
||||||
|
this.panelDataSource.TabIndex = 19;
|
||||||
|
//
|
||||||
|
// buttonSave
|
||||||
|
//
|
||||||
|
this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.buttonSave.Enabled = false;
|
||||||
|
this.buttonSave.Location = new System.Drawing.Point(131, 432);
|
||||||
|
this.buttonSave.Name = "buttonSave";
|
||||||
|
this.buttonSave.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonSave.TabIndex = 18;
|
||||||
|
this.buttonSave.Text = "Save changes";
|
||||||
|
this.buttonSave.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSave.Click += new System.EventHandler(this.ButtonSave_Click);
|
||||||
|
//
|
||||||
|
// textBoxTitle
|
||||||
|
//
|
||||||
|
this.textBoxTitle.Enabled = false;
|
||||||
|
this.textBoxTitle.Location = new System.Drawing.Point(130, 9);
|
||||||
|
this.textBoxTitle.Name = "textBoxTitle";
|
||||||
|
this.textBoxTitle.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxTitle.TabIndex = 17;
|
||||||
|
//
|
||||||
|
// labelTitle
|
||||||
|
//
|
||||||
|
this.labelTitle.AutoSize = true;
|
||||||
|
this.labelTitle.Location = new System.Drawing.Point(10, 12);
|
||||||
|
this.labelTitle.Name = "labelTitle";
|
||||||
|
this.labelTitle.Size = new System.Drawing.Size(30, 13);
|
||||||
|
this.labelTitle.TabIndex = 16;
|
||||||
|
this.labelTitle.Text = "Title:";
|
||||||
|
//
|
||||||
|
// ConfigForm
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(574, 496);
|
||||||
|
this.Controls.Add(this.tabControl);
|
||||||
|
this.Name = "ConfigForm";
|
||||||
|
this.Text = "Configuration - Remote Connection Manager";
|
||||||
|
this.tabControl.ResumeLayout(false);
|
||||||
|
this.tabPage1.ResumeLayout(false);
|
||||||
|
this.splitContainer1.Panel1.ResumeLayout(false);
|
||||||
|
this.splitContainer1.Panel2.ResumeLayout(false);
|
||||||
|
this.splitContainer1.Panel2.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
|
||||||
|
this.splitContainer1.ResumeLayout(false);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.TreeView treeView;
|
||||||
|
private System.Windows.Forms.TabControl tabControl;
|
||||||
|
private System.Windows.Forms.TabPage tabPage1;
|
||||||
|
private System.Windows.Forms.SplitContainer splitContainer1;
|
||||||
|
private System.Windows.Forms.Panel panelDataSource;
|
||||||
|
private System.Windows.Forms.Button buttonSave;
|
||||||
|
private System.Windows.Forms.TextBox textBoxTitle;
|
||||||
|
private System.Windows.Forms.Label labelTitle;
|
||||||
|
}
|
||||||
|
}
|
182
RCM/ConfigForm.cs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class ConfigForm : Form {
|
||||||
|
#region Form
|
||||||
|
protected override CreateParams CreateParams { // Workaround for control flickering reduction
|
||||||
|
get {
|
||||||
|
CreateParams cp = base.CreateParams;
|
||||||
|
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigForm() {
|
||||||
|
InitializeComponent();
|
||||||
|
treeView.ImageList = Config.IconList;
|
||||||
|
treeView.ContextMenuStrip = GetNewDataSourceContextMenu();
|
||||||
|
foreach (IDataSource dataSource in Config.DataSources) {
|
||||||
|
string dataSourceType = dataSource.GetType().FullName;
|
||||||
|
treeView.Nodes.Add(CreateTreeNodeFromDataSource(dataSource));
|
||||||
|
SortTreeAndSelectNode(null);
|
||||||
|
}
|
||||||
|
foreach (IConfigSection configSection in Config.ConfigSections.Values) {
|
||||||
|
TabPage tabPage = new TabPage(configSection.GetType().GetDisplayName());
|
||||||
|
tabPage.Controls.Add(new ConfigFormTab(configSection));
|
||||||
|
tabControl.TabPages.Add(tabPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TreeView display
|
||||||
|
private TreeNode CreateTreeNodeFromDataSource(IDataSource dataSource) {
|
||||||
|
string imageKey = dataSource.GetType().FullName;
|
||||||
|
return new TreeNode(dataSource.Title) { Tag = dataSource, ImageKey = imageKey, SelectedImageKey = imageKey, ContextMenuStrip = GetContextMenu(dataSource) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SortTreeAndSelectNode(TreeNode node) {
|
||||||
|
treeView.Sort();
|
||||||
|
if (treeView.SelectedNode == node) {
|
||||||
|
ChangeSelectedDataSource();
|
||||||
|
} else {
|
||||||
|
treeView.SelectedNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TreeView navigation & selection
|
||||||
|
private void TreeView_AfterSelect(object sender, TreeViewEventArgs e) => ChangeSelectedDataSource();
|
||||||
|
|
||||||
|
private void TreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) => treeView.SelectedNode = e.Node;
|
||||||
|
|
||||||
|
private void TreeView_MouseUp(object sender, MouseEventArgs e) {
|
||||||
|
if (treeView.HitTest(e.X, e.Y).Node == null) {
|
||||||
|
treeView.SelectedNode = null;
|
||||||
|
ChangeSelectedDataSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TreeView_KeyDown(object sender, KeyEventArgs e) {
|
||||||
|
if (treeView.SelectedNode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.KeyCode == Keys.F2) {
|
||||||
|
treeView.SelectedNode.BeginEdit();
|
||||||
|
} else if (e.KeyCode == Keys.Delete) {
|
||||||
|
DeleteSelectedDataSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeSelectedDataSource() {
|
||||||
|
bool isSelected = treeView.SelectedNode != null;
|
||||||
|
textBoxTitle.Enabled = isSelected;
|
||||||
|
textBoxTitle.Text = "";
|
||||||
|
buttonSave.Enabled = isSelected;
|
||||||
|
LoadDataSourceControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadDataSourceControl() {
|
||||||
|
foreach (Control control in panelDataSource.Controls) {
|
||||||
|
panelDataSource.Controls.Remove(control);
|
||||||
|
control.Dispose();
|
||||||
|
}
|
||||||
|
if (treeView.SelectedNode != null) {
|
||||||
|
IDataSource dataSource = (IDataSource)treeView.SelectedNode.Tag;
|
||||||
|
textBoxTitle.Text = dataSource.Title;
|
||||||
|
panelDataSource.Controls.Add(dataSource.GetControl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DataSource create/update/delete
|
||||||
|
public delegate void AfterLabelEditWorkaround(TreeNode node);
|
||||||
|
|
||||||
|
private void TreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) {
|
||||||
|
if (e.Label == null) {
|
||||||
|
e.CancelEdit = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IDataSource dataSource = (IDataSource)e.Node.Tag;
|
||||||
|
dataSource.Title = e.Label;
|
||||||
|
Config.Save();
|
||||||
|
BeginInvoke(new AfterLabelEditWorkaround(SortTreeAndSelectNode), new object[] { e.Node }); // Workaround for treeView.Sort() triggering relabel on every sorted node. See note in https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.listview.afterlabeledit?view=netframework-4.6
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSave_Click(object sender, EventArgs e) {
|
||||||
|
TreeNode selectedNode = treeView.SelectedNode;
|
||||||
|
IDataSource dataSource = (IDataSource)selectedNode.Tag;
|
||||||
|
dataSource.UpdateFromControl();
|
||||||
|
dataSource.Title = selectedNode.Text = textBoxTitle.Text;
|
||||||
|
Config.Save();
|
||||||
|
SortTreeAndSelectNode(selectedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewDataSourceContextMenuItem_Click(object sender, EventArgs e) {
|
||||||
|
Type type = (Type)((ToolStripMenuItem)sender).Tag;
|
||||||
|
IDataSource dataSource = (IDataSource)Activator.CreateInstance(type);
|
||||||
|
dataSource.Title = string.Format("New {0}", type.GetDisplayName());
|
||||||
|
InsertNewDataSource(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DuplicateDataSourceContextMenuItem_Click(object sender, EventArgs e) {
|
||||||
|
IDataSource source = (IDataSource)treeView.SelectedNode.Tag;
|
||||||
|
IDataSource dataSource = (IDataSource)ConfigSerializer.JsonToObj(ConfigSerializer.ObjToJson(source));
|
||||||
|
dataSource.Title = string.Format("{0} - Copy", dataSource.Title);
|
||||||
|
InsertNewDataSource(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InsertNewDataSource(IDataSource dataSource) {
|
||||||
|
Config.DataSources.Add(dataSource);
|
||||||
|
Config.Save();
|
||||||
|
TreeNode node = CreateTreeNodeFromDataSource(dataSource);
|
||||||
|
treeView.Nodes.Add(node);
|
||||||
|
SortTreeAndSelectNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteSelectedDataSource() {
|
||||||
|
IDataSource dataSource = (IDataSource)treeView.SelectedNode.Tag;
|
||||||
|
if (MessageBox.Show("Do you really want to delete selected data source?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
|
||||||
|
Config.DataSources.Remove(dataSource);
|
||||||
|
Config.Save();
|
||||||
|
treeView.SelectedNode.Remove();
|
||||||
|
SortTreeAndSelectNode(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteDataSourceContextMenuItem_Click(object sender, EventArgs e) => DeleteSelectedDataSource();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DataSource ContextMenu
|
||||||
|
private ContextMenuStrip GetNewDataSourceContextMenu() {
|
||||||
|
ContextMenuStrip menu = new ContextMenuStrip();
|
||||||
|
ToolStripMenuItem menuItem = new ToolStripMenuItem("New item");
|
||||||
|
foreach (Type type in Config.DataSourceTypes) {
|
||||||
|
ToolStripMenuItem item = new ToolStripMenuItem(type.GetDisplayName()) { Tag = type };
|
||||||
|
item.Click += NewDataSourceContextMenuItem_Click;
|
||||||
|
menuItem.DropDownItems.Add(item);
|
||||||
|
}
|
||||||
|
menu.Items.Add(menuItem);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContextMenuStrip GetContextMenu(IDataSource dataSource) {
|
||||||
|
ContextMenuStrip menu = new ContextMenuStrip();
|
||||||
|
ToolStripMenuItem duplicateItem = new ToolStripMenuItem("Duplicate item");
|
||||||
|
duplicateItem.Click += DuplicateDataSourceContextMenuItem_Click;
|
||||||
|
menu.Items.Add(duplicateItem);
|
||||||
|
ToolStripMenuItem deleteItem = new ToolStripMenuItem("Delete item");
|
||||||
|
deleteItem.Click += DeleteDataSourceContextMenuItem_Click;
|
||||||
|
menu.Items.Add(deleteItem);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
120
RCM/ConfigForm.resx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
67
RCM/ConfigFormTab.Designer.cs
generated
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class ConfigFormTab {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.buttonSave = new System.Windows.Forms.Button();
|
||||||
|
this.panel = new System.Windows.Forms.Panel();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// buttonSave
|
||||||
|
//
|
||||||
|
this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.buttonSave.Location = new System.Drawing.Point(371, 429);
|
||||||
|
this.buttonSave.Name = "buttonSave";
|
||||||
|
this.buttonSave.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonSave.TabIndex = 19;
|
||||||
|
this.buttonSave.Text = "Save changes";
|
||||||
|
this.buttonSave.Click += new System.EventHandler(this.ButtonSave_Click);
|
||||||
|
//
|
||||||
|
// panel
|
||||||
|
//
|
||||||
|
this.panel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panel.Location = new System.Drawing.Point(3, 3);
|
||||||
|
this.panel.Name = "panel";
|
||||||
|
this.panel.Size = new System.Drawing.Size(554, 412);
|
||||||
|
this.panel.TabIndex = 20;
|
||||||
|
//
|
||||||
|
// ConfigFormTab
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.panel);
|
||||||
|
this.Controls.Add(this.buttonSave);
|
||||||
|
this.Name = "ConfigFormTab";
|
||||||
|
this.Size = new System.Drawing.Size(560, 464);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button buttonSave;
|
||||||
|
public System.Windows.Forms.Panel panel;
|
||||||
|
}
|
||||||
|
}
|
27
RCM/ConfigFormTab.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class ConfigFormTab : UserControl {
|
||||||
|
private IConfigSection _configSection;
|
||||||
|
|
||||||
|
public ConfigFormTab(IConfigSection configSection) {
|
||||||
|
InitializeComponent();
|
||||||
|
Dock = DockStyle.Fill;
|
||||||
|
_configSection = configSection;
|
||||||
|
panel.Controls.Add(configSection.GetControl());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSave_Click(object sender, EventArgs e) {
|
||||||
|
_configSection.UpdateFromControl();
|
||||||
|
Config.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
RCM/ConfigFormTab.resx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
90
RCM/ConfigSerializer.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public static class ConfigSerializer {
|
||||||
|
private static readonly string _typeProp = "SerializedType";
|
||||||
|
|
||||||
|
public static JObject ObjToJson(object obj) {
|
||||||
|
JObject json = JObject.FromObject(obj);
|
||||||
|
json.Add(_typeProp, obj.GetType().FullName);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object JsonToObj(JObject json) {
|
||||||
|
Type type = Type.GetType(json.GetValue(_typeProp).ToString(), true);
|
||||||
|
json.Remove(_typeProp);
|
||||||
|
return json.ToObject(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string JsonToString(JToken json) {
|
||||||
|
var stringWriter = new StringWriter();
|
||||||
|
var jsonWriter = new JsonTextWriter(stringWriter) { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii };
|
||||||
|
new JsonSerializer().Serialize(jsonWriter, json);
|
||||||
|
return stringWriter.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JObject StringToJson(string str) {
|
||||||
|
return JObject.Parse(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ObjToString(object obj) {
|
||||||
|
return JsonToString(ObjToJson(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object StringToObj(string str) {
|
||||||
|
return JsonToObj(StringToJson(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] Decrypt(byte[] cipherbytes, string password) {
|
||||||
|
if (string.IsNullOrEmpty(password)) {
|
||||||
|
return cipherbytes;
|
||||||
|
}
|
||||||
|
byte[] salt = new byte[16];
|
||||||
|
byte[] iv = new byte[16];
|
||||||
|
Array.Copy(cipherbytes, salt, 16);
|
||||||
|
Array.Copy(cipherbytes, 16, iv, 0, 16);
|
||||||
|
using (AesManaged aes = new AesManaged() { KeySize = 128 })
|
||||||
|
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt, 1024))
|
||||||
|
using (ICryptoTransform decryptor = aes.CreateDecryptor(pbkdf2.GetBytes(16), iv))
|
||||||
|
using (MemoryStream memoryStream = new MemoryStream())
|
||||||
|
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(cryptoStream)) {
|
||||||
|
writer.Write(cipherbytes, 32, cipherbytes.Length - 32);
|
||||||
|
cryptoStream.FlushFinalBlock();
|
||||||
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object DecryptObject(byte[] cipherbytes, string password) {
|
||||||
|
return StringToObj(Encoding.ASCII.GetString(Decrypt(cipherbytes, password)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] Encrypt(byte[] plaintext, string password) {
|
||||||
|
if (string.IsNullOrEmpty(password)) {
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
using (AesManaged aes = new AesManaged() { KeySize = 128 }) {
|
||||||
|
aes.GenerateIV();
|
||||||
|
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, 16, 1024))
|
||||||
|
using (ICryptoTransform encryptor = aes.CreateEncryptor(pbkdf2.GetBytes(16), aes.IV))
|
||||||
|
using (MemoryStream memoryStream = new MemoryStream())
|
||||||
|
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(cryptoStream)) {
|
||||||
|
writer.Write(plaintext);
|
||||||
|
cryptoStream.FlushFinalBlock();
|
||||||
|
return pbkdf2.Salt.Concat(aes.IV).Concat(memoryStream.ToArray()).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] EncryptObject(object obj, string password) {
|
||||||
|
return Encrypt(Encoding.ASCII.GetBytes(ObjToString(obj)), password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
146
RCM/DataSource/MySQL/MySQL.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("MySQL/MariaDB")]
|
||||||
|
[Serializable]
|
||||||
|
public class MySQL : IDataSource {
|
||||||
|
public string Title { get; set; }
|
||||||
|
public HashSet<Guid> ExpandedNodes { get; } = new HashSet<Guid>();
|
||||||
|
|
||||||
|
public string Host { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public string Database { get; set; }
|
||||||
|
public string AESPassword { get; set; }
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
private MySqlConnection _connection;
|
||||||
|
[NonSerialized]
|
||||||
|
private MySQLControl _control;
|
||||||
|
|
||||||
|
public override string ToString() => Title;
|
||||||
|
|
||||||
|
private MySqlConnection CreateConnection(string host, string username, string password, string database) {
|
||||||
|
string[] hostport = host.Split(':');
|
||||||
|
string port = hostport.Length > 1 ? hostport[1] : "3306";
|
||||||
|
return new MySqlConnection(string.Format("server={0};port={1};user={2};password={3};database={4}", hostport[0], port, username, password, database));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenConnection() {
|
||||||
|
if (_connection == null) {
|
||||||
|
_connection = CreateConnection(Host, Username, Password, Database);
|
||||||
|
}
|
||||||
|
_connection.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<Guid, IRecord> SelectAll() {
|
||||||
|
Dictionary<Guid, IRecord> records = new Dictionary<Guid, IRecord>();
|
||||||
|
OpenConnection();
|
||||||
|
using (MySqlCommand command = new MySqlCommand("SELECT `data`, LENGTH(`data`) AS `datalength` FROM `data`", _connection))
|
||||||
|
using (MySqlDataReader reader = command.ExecuteReader()) {
|
||||||
|
while (reader.Read()) {
|
||||||
|
int dataBytesLength = reader.GetInt32(1);
|
||||||
|
byte[] dataBytes = new byte[dataBytesLength];
|
||||||
|
reader.GetBytes(0, 0, dataBytes, 0, dataBytesLength);
|
||||||
|
IRecord record = (IRecord)ConfigSerializer.DecryptObject(dataBytes, AESPassword);
|
||||||
|
records.Add(record.Id, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(IRecord record) {
|
||||||
|
OpenConnection();
|
||||||
|
using (MySqlCommand command = new MySqlCommand("INSERT INTO `data` (`id`, `group`, `data`) VALUES (@id, @group, @data)", _connection)) {
|
||||||
|
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
|
||||||
|
if (record.GroupId == Guid.Empty) {
|
||||||
|
command.Parameters.AddWithValue("@group", null);
|
||||||
|
} else {
|
||||||
|
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
|
||||||
|
}
|
||||||
|
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(IRecord record) {
|
||||||
|
OpenConnection();
|
||||||
|
using (MySqlCommand command = new MySqlCommand("UPDATE `data` SET `group` = @group, `data` = @data WHERE `id` = @id", _connection)) {
|
||||||
|
if (record.GroupId == Guid.Empty) {
|
||||||
|
command.Parameters.AddWithValue("@group", null);
|
||||||
|
} else {
|
||||||
|
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
|
||||||
|
}
|
||||||
|
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
|
||||||
|
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(IRecord record) {
|
||||||
|
OpenConnection();
|
||||||
|
using (MySqlCommand command = new MySqlCommand("DELETE FROM `data` WHERE `id` = @id", _connection)) {
|
||||||
|
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new MySQLControl(this);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
Host = _control.textBoxHostAddress.Text;
|
||||||
|
Username = _control.textBoxUsername.Text;
|
||||||
|
Password = _control.textBoxPassword.Text;
|
||||||
|
Database = _control.textBoxDatabase.Text;
|
||||||
|
AESPassword = _control.textBoxEncPassword.Text;
|
||||||
|
_connection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SchemaExists(string host, string username, string password, string database) {
|
||||||
|
using (MySqlConnection tempConnection = CreateConnection(host, username, password, database)) {
|
||||||
|
tempConnection.Open();
|
||||||
|
using (MySqlCommand command = new MySqlCommand("SHOW TABLES LIKE \"data\"", tempConnection))
|
||||||
|
using (MySqlDataReader reader = command.ExecuteReader()) {
|
||||||
|
return reader.HasRows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateSchema(string host, string username, string password, string database) {
|
||||||
|
using (MySqlConnection tempConnection = CreateConnection(host, username, password, database)) {
|
||||||
|
tempConnection.Open();
|
||||||
|
using (MySqlCommand command = new MySqlCommand(@"DROP TABLE IF EXISTS `data`;
|
||||||
|
CREATE TABLE `data` (
|
||||||
|
`id` binary(16) NOT NULL,
|
||||||
|
`group` binary(16),
|
||||||
|
`data` blob NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
FOREIGN KEY (`group`) REFERENCES `data`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;", tempConnection)) {
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TestConnection(string host, string username, string password, string database) {
|
||||||
|
using (MySqlConnection tempConnection = CreateConnection(host, username, password, database)) {
|
||||||
|
tempConnection.Open();
|
||||||
|
using (MySqlCommand command = new MySqlCommand("SELECT VERSION()", tempConnection)) {
|
||||||
|
return (string)command.ExecuteScalar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/DataSource/MySQL/MySQL.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\..\Resources\MySQL.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
206
RCM/DataSource/MySQL/MySQLControl.Designer.cs
generated
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class MySQLControl {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.buttonUnmaskPassword = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxPassword = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxUsername = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxHostAddress = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelPassword = new System.Windows.Forms.Label();
|
||||||
|
this.labelUsername = new System.Windows.Forms.Label();
|
||||||
|
this.labelHostAddress = new System.Windows.Forms.Label();
|
||||||
|
this.buttonUnmaskEncPassword = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxEncPassword = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxDatabase = new System.Windows.Forms.TextBox();
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
|
this.buttonTestConnection = new System.Windows.Forms.Button();
|
||||||
|
this.buttonCreateSchema = new System.Windows.Forms.Button();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// buttonUnmaskPassword
|
||||||
|
//
|
||||||
|
this.buttonUnmaskPassword.Location = new System.Drawing.Point(273, 51);
|
||||||
|
this.buttonUnmaskPassword.Name = "buttonUnmaskPassword";
|
||||||
|
this.buttonUnmaskPassword.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonUnmaskPassword.TabIndex = 4;
|
||||||
|
this.buttonUnmaskPassword.Text = "●";
|
||||||
|
this.buttonUnmaskPassword.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonUnmaskPassword.Click += new System.EventHandler(this.ButtonUnmaskPassword_Click);
|
||||||
|
//
|
||||||
|
// textBoxPassword
|
||||||
|
//
|
||||||
|
this.textBoxPassword.Location = new System.Drawing.Point(120, 52);
|
||||||
|
this.textBoxPassword.Name = "textBoxPassword";
|
||||||
|
this.textBoxPassword.Size = new System.Drawing.Size(147, 20);
|
||||||
|
this.textBoxPassword.TabIndex = 3;
|
||||||
|
this.textBoxPassword.UseSystemPasswordChar = true;
|
||||||
|
//
|
||||||
|
// textBoxUsername
|
||||||
|
//
|
||||||
|
this.textBoxUsername.Location = new System.Drawing.Point(120, 26);
|
||||||
|
this.textBoxUsername.Name = "textBoxUsername";
|
||||||
|
this.textBoxUsername.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxUsername.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// textBoxHostAddress
|
||||||
|
//
|
||||||
|
this.textBoxHostAddress.Location = new System.Drawing.Point(120, 0);
|
||||||
|
this.textBoxHostAddress.Name = "textBoxHostAddress";
|
||||||
|
this.textBoxHostAddress.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxHostAddress.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// labelPassword
|
||||||
|
//
|
||||||
|
this.labelPassword.AutoSize = true;
|
||||||
|
this.labelPassword.Location = new System.Drawing.Point(0, 55);
|
||||||
|
this.labelPassword.Name = "labelPassword";
|
||||||
|
this.labelPassword.Size = new System.Drawing.Size(56, 13);
|
||||||
|
this.labelPassword.TabIndex = 14;
|
||||||
|
this.labelPassword.Text = "Password:";
|
||||||
|
//
|
||||||
|
// labelUsername
|
||||||
|
//
|
||||||
|
this.labelUsername.AutoSize = true;
|
||||||
|
this.labelUsername.Location = new System.Drawing.Point(0, 29);
|
||||||
|
this.labelUsername.Name = "labelUsername";
|
||||||
|
this.labelUsername.Size = new System.Drawing.Size(58, 13);
|
||||||
|
this.labelUsername.TabIndex = 13;
|
||||||
|
this.labelUsername.Text = "Username:";
|
||||||
|
//
|
||||||
|
// labelHostAddress
|
||||||
|
//
|
||||||
|
this.labelHostAddress.AutoSize = true;
|
||||||
|
this.labelHostAddress.Location = new System.Drawing.Point(0, 3);
|
||||||
|
this.labelHostAddress.Name = "labelHostAddress";
|
||||||
|
this.labelHostAddress.Size = new System.Drawing.Size(72, 13);
|
||||||
|
this.labelHostAddress.TabIndex = 12;
|
||||||
|
this.labelHostAddress.Text = "Host address:";
|
||||||
|
//
|
||||||
|
// buttonUnmaskEncPassword
|
||||||
|
//
|
||||||
|
this.buttonUnmaskEncPassword.Location = new System.Drawing.Point(273, 104);
|
||||||
|
this.buttonUnmaskEncPassword.Name = "buttonUnmaskEncPassword";
|
||||||
|
this.buttonUnmaskEncPassword.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonUnmaskEncPassword.TabIndex = 17;
|
||||||
|
this.buttonUnmaskEncPassword.Text = "●";
|
||||||
|
this.buttonUnmaskEncPassword.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonUnmaskEncPassword.Click += new System.EventHandler(this.ButtonUnmaskEncPassword_Click);
|
||||||
|
//
|
||||||
|
// textBoxEncPassword
|
||||||
|
//
|
||||||
|
this.textBoxEncPassword.Location = new System.Drawing.Point(120, 105);
|
||||||
|
this.textBoxEncPassword.Name = "textBoxEncPassword";
|
||||||
|
this.textBoxEncPassword.Size = new System.Drawing.Size(147, 20);
|
||||||
|
this.textBoxEncPassword.TabIndex = 16;
|
||||||
|
this.textBoxEncPassword.UseSystemPasswordChar = true;
|
||||||
|
//
|
||||||
|
// textBoxDatabase
|
||||||
|
//
|
||||||
|
this.textBoxDatabase.Location = new System.Drawing.Point(120, 79);
|
||||||
|
this.textBoxDatabase.Name = "textBoxDatabase";
|
||||||
|
this.textBoxDatabase.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxDatabase.TabIndex = 15;
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.AutoSize = true;
|
||||||
|
this.label1.Location = new System.Drawing.Point(0, 108);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(108, 13);
|
||||||
|
this.label1.TabIndex = 19;
|
||||||
|
this.label1.Text = "Encryption password:";
|
||||||
|
//
|
||||||
|
// label2
|
||||||
|
//
|
||||||
|
this.label2.AutoSize = true;
|
||||||
|
this.label2.Location = new System.Drawing.Point(0, 82);
|
||||||
|
this.label2.Name = "label2";
|
||||||
|
this.label2.Size = new System.Drawing.Size(56, 13);
|
||||||
|
this.label2.TabIndex = 18;
|
||||||
|
this.label2.Text = "Database:";
|
||||||
|
//
|
||||||
|
// buttonTestConnection
|
||||||
|
//
|
||||||
|
this.buttonTestConnection.Location = new System.Drawing.Point(120, 132);
|
||||||
|
this.buttonTestConnection.Name = "buttonTestConnection";
|
||||||
|
this.buttonTestConnection.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonTestConnection.TabIndex = 20;
|
||||||
|
this.buttonTestConnection.Text = "Test connection";
|
||||||
|
this.buttonTestConnection.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonTestConnection.Click += new System.EventHandler(this.ButtonTestConnection_Click);
|
||||||
|
//
|
||||||
|
// buttonCreateSchema
|
||||||
|
//
|
||||||
|
this.buttonCreateSchema.Location = new System.Drawing.Point(120, 161);
|
||||||
|
this.buttonCreateSchema.Name = "buttonCreateSchema";
|
||||||
|
this.buttonCreateSchema.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonCreateSchema.TabIndex = 21;
|
||||||
|
this.buttonCreateSchema.Text = "Create schema";
|
||||||
|
this.buttonCreateSchema.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonCreateSchema.Click += new System.EventHandler(this.ButtonCreateSchema_Click);
|
||||||
|
//
|
||||||
|
// MySQLControl
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.buttonCreateSchema);
|
||||||
|
this.Controls.Add(this.buttonTestConnection);
|
||||||
|
this.Controls.Add(this.buttonUnmaskEncPassword);
|
||||||
|
this.Controls.Add(this.textBoxEncPassword);
|
||||||
|
this.Controls.Add(this.textBoxDatabase);
|
||||||
|
this.Controls.Add(this.label1);
|
||||||
|
this.Controls.Add(this.label2);
|
||||||
|
this.Controls.Add(this.buttonUnmaskPassword);
|
||||||
|
this.Controls.Add(this.textBoxPassword);
|
||||||
|
this.Controls.Add(this.textBoxUsername);
|
||||||
|
this.Controls.Add(this.textBoxHostAddress);
|
||||||
|
this.Controls.Add(this.labelPassword);
|
||||||
|
this.Controls.Add(this.labelUsername);
|
||||||
|
this.Controls.Add(this.labelHostAddress);
|
||||||
|
this.Name = "MySQLControl";
|
||||||
|
this.Size = new System.Drawing.Size(300, 190);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button buttonUnmaskPassword;
|
||||||
|
private System.Windows.Forms.Label labelPassword;
|
||||||
|
private System.Windows.Forms.Label labelUsername;
|
||||||
|
private System.Windows.Forms.Label labelHostAddress;
|
||||||
|
public System.Windows.Forms.TextBox textBoxHostAddress;
|
||||||
|
public System.Windows.Forms.TextBox textBoxUsername;
|
||||||
|
public System.Windows.Forms.TextBox textBoxPassword;
|
||||||
|
private System.Windows.Forms.Button buttonUnmaskEncPassword;
|
||||||
|
public System.Windows.Forms.TextBox textBoxEncPassword;
|
||||||
|
public System.Windows.Forms.TextBox textBoxDatabase;
|
||||||
|
private System.Windows.Forms.Label label1;
|
||||||
|
private System.Windows.Forms.Label label2;
|
||||||
|
private System.Windows.Forms.Button buttonTestConnection;
|
||||||
|
private System.Windows.Forms.Button buttonCreateSchema;
|
||||||
|
}
|
||||||
|
}
|
43
RCM/DataSource/MySQL/MySQLControl.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class MySQLControl : UserControl {
|
||||||
|
private MySQL _mySQL;
|
||||||
|
|
||||||
|
public MySQLControl(MySQL mySQL) {
|
||||||
|
InitializeComponent();
|
||||||
|
_mySQL = mySQL;
|
||||||
|
textBoxHostAddress.Text = mySQL.Host;
|
||||||
|
textBoxUsername.Text = mySQL.Username;
|
||||||
|
textBoxPassword.Text = mySQL.Password;
|
||||||
|
textBoxDatabase.Text = mySQL.Database;
|
||||||
|
textBoxEncPassword.Text = mySQL.AESPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonUnmaskPassword_Click(object sender, EventArgs e) {
|
||||||
|
textBoxPassword.UseSystemPasswordChar = !textBoxPassword.UseSystemPasswordChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonTestConnection_Click(object sender, EventArgs e) {
|
||||||
|
try {
|
||||||
|
string version = _mySQL.TestConnection(textBoxHostAddress.Text, textBoxUsername.Text, textBoxPassword.Text, textBoxDatabase.Text);
|
||||||
|
MessageBox.Show(version, "Test successful", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
MessageBox.Show(ex.Message, "Test failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonCreateSchema_Click(object sender, EventArgs e) {
|
||||||
|
bool schemaExists = _mySQL.SchemaExists(textBoxHostAddress.Text, textBoxUsername.Text, textBoxPassword.Text, textBoxDatabase.Text);
|
||||||
|
if (!schemaExists || MessageBox.Show("Do you want to drop existing schema and create a new one?", "Schema already exists", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) {
|
||||||
|
_mySQL.CreateSchema(textBoxHostAddress.Text, textBoxUsername.Text, textBoxPassword.Text, textBoxDatabase.Text);
|
||||||
|
MessageBox.Show("New schema successfully created", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonUnmaskEncPassword_Click(object sender, EventArgs e) {
|
||||||
|
textBoxEncPassword.UseSystemPasswordChar = !textBoxEncPassword.UseSystemPasswordChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
137
RCM/DataSource/SQLite/SQLite.cs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.SQLite;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[Serializable]
|
||||||
|
public class SQLite : IDataSource {
|
||||||
|
public string Title { get; set; }
|
||||||
|
public HashSet<Guid> ExpandedNodes { get; } = new HashSet<Guid>();
|
||||||
|
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public string AESPassword { get; set; }
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
private SQLiteConnection _connection;
|
||||||
|
[NonSerialized]
|
||||||
|
private SQLiteControl _control;
|
||||||
|
|
||||||
|
public override string ToString() => Title;
|
||||||
|
|
||||||
|
private SQLiteConnection CreateConnection(string fileName, bool failIfMissing) {
|
||||||
|
return new SQLiteConnection(string.Format("Data Source={0};Version=3;FailIfMissing={1}", fileName, failIfMissing ? "True" : "False"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenConnection() {
|
||||||
|
if (_connection == null) {
|
||||||
|
_connection = CreateConnection(FileName, true);
|
||||||
|
}
|
||||||
|
_connection.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<Guid, IRecord> SelectAll() {
|
||||||
|
Dictionary<Guid, IRecord> records = new Dictionary<Guid, IRecord>();
|
||||||
|
OpenConnection();
|
||||||
|
using (SQLiteCommand command = new SQLiteCommand("SELECT \"data\", LENGTH(\"data\") AS \"datalength\" FROM \"data\"", _connection))
|
||||||
|
using (SQLiteDataReader reader = command.ExecuteReader()) {
|
||||||
|
while (reader.Read()) {
|
||||||
|
int dataBytesLength = reader.GetInt32(1);
|
||||||
|
byte[] dataBytes = new byte[dataBytesLength];
|
||||||
|
reader.GetBytes(0, 0, dataBytes, 0, dataBytesLength);
|
||||||
|
IRecord record = (IRecord)ConfigSerializer.DecryptObject(dataBytes, AESPassword);
|
||||||
|
records.Add(record.Id, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(IRecord record) {
|
||||||
|
OpenConnection();
|
||||||
|
EnableForeignKeys();
|
||||||
|
using (SQLiteCommand command = new SQLiteCommand("INSERT INTO \"data\" (\"id\", \"group\", \"data\") VALUES (@id, @group, @data)", _connection)) {
|
||||||
|
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
|
||||||
|
if (record.GroupId == Guid.Empty) {
|
||||||
|
command.Parameters.AddWithValue("@group", null);
|
||||||
|
} else {
|
||||||
|
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
|
||||||
|
}
|
||||||
|
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(IRecord record) {
|
||||||
|
OpenConnection();
|
||||||
|
EnableForeignKeys();
|
||||||
|
using (SQLiteCommand command = new SQLiteCommand("UPDATE \"data\" SET \"group\" = @group, \"data\" = @data WHERE \"id\" = @id", _connection)) {
|
||||||
|
if (record.GroupId == Guid.Empty) {
|
||||||
|
command.Parameters.AddWithValue("@group", null);
|
||||||
|
} else {
|
||||||
|
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
|
||||||
|
}
|
||||||
|
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
|
||||||
|
command.Parameters.AddWithValue("@id", record.Id);
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(IRecord record) {
|
||||||
|
OpenConnection();
|
||||||
|
EnableForeignKeys();
|
||||||
|
using (SQLiteCommand command = new SQLiteCommand("DELETE FROM \"data\" WHERE \"id\" = @id", _connection)) {
|
||||||
|
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
_connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnableForeignKeys() {
|
||||||
|
using (SQLiteCommand command = new SQLiteCommand("PRAGMA foreign_keys = ON", _connection)) {
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new SQLiteControl(this);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
FileName = _control.textBoxDatabase.Text;
|
||||||
|
AESPassword = _control.textBoxEncPassword.Text;
|
||||||
|
_connection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateSchema(string fileName) {
|
||||||
|
using (SQLiteConnection tempConnection = CreateConnection(fileName, false)) {
|
||||||
|
tempConnection.Open();
|
||||||
|
SQLiteCommand command = new SQLiteCommand(@"
|
||||||
|
DROP TABLE IF EXISTS ""data"";
|
||||||
|
CREATE TABLE ""data"" (
|
||||||
|
""id"" binary(16) NOT NULL,
|
||||||
|
""group"" binary(16),
|
||||||
|
""data"" blob NOT NULL,
|
||||||
|
PRIMARY KEY (""id""),
|
||||||
|
FOREIGN KEY (""group"") REFERENCES ""data""(""id"") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);", tempConnection);
|
||||||
|
command.ExecuteNonQuery();
|
||||||
|
tempConnection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TestConnection(string fileName) {
|
||||||
|
using (SQLiteConnection tempConnection = CreateConnection(fileName, true)) {
|
||||||
|
tempConnection.Open();
|
||||||
|
using (SQLiteCommand command = new SQLiteCommand("PRAGMA quick_check", tempConnection)) {
|
||||||
|
return (string)command.ExecuteScalar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/DataSource/SQLite/SQLite.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\..\Resources\SQLite.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
146
RCM/DataSource/SQLite/SQLiteControl.Designer.cs
generated
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class SQLiteControl {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.buttonSelectDatabase = new System.Windows.Forms.Button();
|
||||||
|
this.buttonUnmaskEncPassword = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxEncPassword = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxDatabase = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelEncPassword = new System.Windows.Forms.Label();
|
||||||
|
this.labelDatabase = new System.Windows.Forms.Label();
|
||||||
|
this.buttonTestConnection = new System.Windows.Forms.Button();
|
||||||
|
this.buttonCreateSchema = new System.Windows.Forms.Button();
|
||||||
|
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// buttonSelectDatabase
|
||||||
|
//
|
||||||
|
this.buttonSelectDatabase.Location = new System.Drawing.Point(273, -1);
|
||||||
|
this.buttonSelectDatabase.Name = "buttonSelectDatabase";
|
||||||
|
this.buttonSelectDatabase.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonSelectDatabase.TabIndex = 4;
|
||||||
|
this.buttonSelectDatabase.Text = "⋯";
|
||||||
|
this.buttonSelectDatabase.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSelectDatabase.Click += new System.EventHandler(this.ButtonSelectDatabase_Click);
|
||||||
|
//
|
||||||
|
// buttonUnmaskEncPassword
|
||||||
|
//
|
||||||
|
this.buttonUnmaskEncPassword.Location = new System.Drawing.Point(273, 25);
|
||||||
|
this.buttonUnmaskEncPassword.Name = "buttonUnmaskEncPassword";
|
||||||
|
this.buttonUnmaskEncPassword.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonUnmaskEncPassword.TabIndex = 17;
|
||||||
|
this.buttonUnmaskEncPassword.Text = "●";
|
||||||
|
this.buttonUnmaskEncPassword.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonUnmaskEncPassword.Click += new System.EventHandler(this.ButtonUnmaskEncPassword_Click);
|
||||||
|
//
|
||||||
|
// textBoxEncPassword
|
||||||
|
//
|
||||||
|
this.textBoxEncPassword.Location = new System.Drawing.Point(120, 26);
|
||||||
|
this.textBoxEncPassword.Name = "textBoxEncPassword";
|
||||||
|
this.textBoxEncPassword.Size = new System.Drawing.Size(147, 20);
|
||||||
|
this.textBoxEncPassword.TabIndex = 16;
|
||||||
|
this.textBoxEncPassword.UseSystemPasswordChar = true;
|
||||||
|
//
|
||||||
|
// textBoxDatabase
|
||||||
|
//
|
||||||
|
this.textBoxDatabase.Location = new System.Drawing.Point(120, 0);
|
||||||
|
this.textBoxDatabase.Name = "textBoxDatabase";
|
||||||
|
this.textBoxDatabase.Size = new System.Drawing.Size(147, 20);
|
||||||
|
this.textBoxDatabase.TabIndex = 15;
|
||||||
|
//
|
||||||
|
// labelEncPassword
|
||||||
|
//
|
||||||
|
this.labelEncPassword.AutoSize = true;
|
||||||
|
this.labelEncPassword.Location = new System.Drawing.Point(0, 29);
|
||||||
|
this.labelEncPassword.Name = "labelEncPassword";
|
||||||
|
this.labelEncPassword.Size = new System.Drawing.Size(108, 13);
|
||||||
|
this.labelEncPassword.TabIndex = 19;
|
||||||
|
this.labelEncPassword.Text = "Encryption password:";
|
||||||
|
//
|
||||||
|
// labelDatabase
|
||||||
|
//
|
||||||
|
this.labelDatabase.AutoSize = true;
|
||||||
|
this.labelDatabase.Location = new System.Drawing.Point(0, 3);
|
||||||
|
this.labelDatabase.Name = "labelDatabase";
|
||||||
|
this.labelDatabase.Size = new System.Drawing.Size(72, 13);
|
||||||
|
this.labelDatabase.TabIndex = 18;
|
||||||
|
this.labelDatabase.Text = "Database file:";
|
||||||
|
//
|
||||||
|
// buttonTestConnection
|
||||||
|
//
|
||||||
|
this.buttonTestConnection.Location = new System.Drawing.Point(120, 53);
|
||||||
|
this.buttonTestConnection.Name = "buttonTestConnection";
|
||||||
|
this.buttonTestConnection.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonTestConnection.TabIndex = 20;
|
||||||
|
this.buttonTestConnection.Text = "Test connection";
|
||||||
|
this.buttonTestConnection.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonTestConnection.Click += new System.EventHandler(this.ButtonTestConnection_Click);
|
||||||
|
//
|
||||||
|
// buttonCreateSchema
|
||||||
|
//
|
||||||
|
this.buttonCreateSchema.Location = new System.Drawing.Point(120, 82);
|
||||||
|
this.buttonCreateSchema.Name = "buttonCreateSchema";
|
||||||
|
this.buttonCreateSchema.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonCreateSchema.TabIndex = 21;
|
||||||
|
this.buttonCreateSchema.Text = "Create schema";
|
||||||
|
this.buttonCreateSchema.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonCreateSchema.Click += new System.EventHandler(this.ButtonCreateSchema_Click);
|
||||||
|
//
|
||||||
|
// saveFileDialog
|
||||||
|
//
|
||||||
|
this.saveFileDialog.DefaultExt = "sqlite";
|
||||||
|
this.saveFileDialog.Filter = "SQLite database|*.sqlite";
|
||||||
|
//
|
||||||
|
// SQLiteControl
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.buttonCreateSchema);
|
||||||
|
this.Controls.Add(this.buttonTestConnection);
|
||||||
|
this.Controls.Add(this.buttonUnmaskEncPassword);
|
||||||
|
this.Controls.Add(this.textBoxEncPassword);
|
||||||
|
this.Controls.Add(this.textBoxDatabase);
|
||||||
|
this.Controls.Add(this.labelEncPassword);
|
||||||
|
this.Controls.Add(this.labelDatabase);
|
||||||
|
this.Controls.Add(this.buttonSelectDatabase);
|
||||||
|
this.Name = "SQLiteControl";
|
||||||
|
this.Size = new System.Drawing.Size(300, 110);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button buttonSelectDatabase;
|
||||||
|
private System.Windows.Forms.Button buttonUnmaskEncPassword;
|
||||||
|
public System.Windows.Forms.TextBox textBoxEncPassword;
|
||||||
|
public System.Windows.Forms.TextBox textBoxDatabase;
|
||||||
|
private System.Windows.Forms.Label labelEncPassword;
|
||||||
|
private System.Windows.Forms.Label labelDatabase;
|
||||||
|
private System.Windows.Forms.Button buttonTestConnection;
|
||||||
|
private System.Windows.Forms.Button buttonCreateSchema;
|
||||||
|
private System.Windows.Forms.SaveFileDialog saveFileDialog;
|
||||||
|
}
|
||||||
|
}
|
46
RCM/DataSource/SQLite/SQLiteControl.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class SQLiteControl : UserControl {
|
||||||
|
private SQLite _sqLite;
|
||||||
|
|
||||||
|
public SQLiteControl(SQLite sqLite) {
|
||||||
|
InitializeComponent();
|
||||||
|
_sqLite = sqLite;
|
||||||
|
textBoxDatabase.Text = _sqLite.FileName;
|
||||||
|
textBoxEncPassword.Text = _sqLite.AESPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSelectDatabase_Click(object sender, EventArgs e) {
|
||||||
|
if (saveFileDialog.ShowDialog() == DialogResult.OK) {
|
||||||
|
textBoxDatabase.Text = saveFileDialog.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonTestConnection_Click(object sender, EventArgs e) {
|
||||||
|
try {
|
||||||
|
string version = _sqLite.TestConnection(textBoxDatabase.Text);
|
||||||
|
MessageBox.Show(version, "Test successful", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
MessageBox.Show(ex.Message, "Test failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonCreateSchema_Click(object sender, EventArgs e) {
|
||||||
|
if (File.Exists(textBoxDatabase.Text)) {
|
||||||
|
if (MessageBox.Show("Do you want to delete existing database and create a new one?", "Database already exists", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) != DialogResult.Yes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File.Delete(textBoxDatabase.Text);
|
||||||
|
}
|
||||||
|
_sqLite.CreateSchema(textBoxDatabase.Text);
|
||||||
|
MessageBox.Show("New database successfully created", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonUnmaskEncPassword_Click(object sender, EventArgs e) {
|
||||||
|
textBoxEncPassword.UseSystemPasswordChar = !textBoxEncPassword.UseSystemPasswordChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
RCM/DisplayName.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public class DisplayName : Attribute {
|
||||||
|
public string Text { get; private set; }
|
||||||
|
|
||||||
|
public DisplayName(string text) {
|
||||||
|
Text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
RCM/Group.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[Serializable]
|
||||||
|
[DisplayName("Group")]
|
||||||
|
public class Group : IRecord { // TODO: Prvni v poradi new items
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public Guid GroupId { get; set; }
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
private readonly IAction[] _actions = new IAction[0];
|
||||||
|
|
||||||
|
public IAction[] GetActions() => _actions;
|
||||||
|
public UserControl GetControl() => new UserControl();
|
||||||
|
public void UpdateFromControl() { }
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Group.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Group.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
23
RCM/GroupPath.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public class GroupPath {
|
||||||
|
public Guid Id { get; private set; }
|
||||||
|
public TreeNode Node { get; private set; }
|
||||||
|
public string FullPath { get; private set; }
|
||||||
|
|
||||||
|
public GroupPath(TreeNode node) {
|
||||||
|
Node = node;
|
||||||
|
if (node == null) {
|
||||||
|
Id = Guid.Empty;
|
||||||
|
FullPath = "Root";
|
||||||
|
} else {
|
||||||
|
Id = ((IRecord)node.Tag).Id;
|
||||||
|
FullPath = node.FullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => FullPath;
|
||||||
|
}
|
||||||
|
}
|
5
RCM/IAction.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace RCM {
|
||||||
|
public interface IAction {
|
||||||
|
void Run();
|
||||||
|
}
|
||||||
|
}
|
9
RCM/IConfigSection.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public interface IConfigSection {
|
||||||
|
UserControl GetControl();
|
||||||
|
void UpdateFromControl();
|
||||||
|
}
|
||||||
|
}
|
19
RCM/IDataSource.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public interface IDataSource {
|
||||||
|
string Title { get; set; }
|
||||||
|
HashSet<Guid> ExpandedNodes { get; }
|
||||||
|
|
||||||
|
Dictionary<Guid, IRecord> SelectAll();
|
||||||
|
void Insert(IRecord record);
|
||||||
|
void Update(IRecord record);
|
||||||
|
void Delete(IRecord record);
|
||||||
|
UserControl GetControl();
|
||||||
|
void UpdateFromControl();
|
||||||
|
|
||||||
|
string ToString();
|
||||||
|
}
|
||||||
|
}
|
15
RCM/IRecord.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public interface IRecord {
|
||||||
|
Guid Id { get; set; }
|
||||||
|
string Title { get; set; }
|
||||||
|
Guid GroupId { get; set; }
|
||||||
|
// DateTime LastUpdate { get; set; }
|
||||||
|
|
||||||
|
IAction[] GetActions();
|
||||||
|
UserControl GetControl();
|
||||||
|
void UpdateFromControl();
|
||||||
|
}
|
||||||
|
}
|
249
RCM/MainForm.Designer.cs
generated
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
namespace RCM
|
||||||
|
{
|
||||||
|
partial class MainForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.treeView = new System.Windows.Forms.TreeView();
|
||||||
|
this.splitContainer = new System.Windows.Forms.SplitContainer();
|
||||||
|
this.buttonConfig = new System.Windows.Forms.Button();
|
||||||
|
this.comboBoxDatabase = new System.Windows.Forms.ComboBox();
|
||||||
|
this.panelRecord = new System.Windows.Forms.Panel();
|
||||||
|
this.buttonSave = new System.Windows.Forms.Button();
|
||||||
|
this.labelGroup = new System.Windows.Forms.Label();
|
||||||
|
this.comboBoxGroup = new System.Windows.Forms.ComboBox();
|
||||||
|
this.textBoxTitle = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelTitle = new System.Windows.Forms.Label();
|
||||||
|
this.timerDataSourceRefresh = new System.Windows.Forms.Timer(this.components);
|
||||||
|
this.statusStrip = new System.Windows.Forms.StatusStrip();
|
||||||
|
this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
|
this.toolStripStatusLastRefresh = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
||||||
|
this.splitContainer.Panel1.SuspendLayout();
|
||||||
|
this.splitContainer.Panel2.SuspendLayout();
|
||||||
|
this.splitContainer.SuspendLayout();
|
||||||
|
this.statusStrip.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// treeView
|
||||||
|
//
|
||||||
|
this.treeView.AllowDrop = true;
|
||||||
|
this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.treeView.Enabled = false;
|
||||||
|
this.treeView.Indent = 24;
|
||||||
|
this.treeView.LabelEdit = true;
|
||||||
|
this.treeView.Location = new System.Drawing.Point(3, 30);
|
||||||
|
this.treeView.Name = "treeView";
|
||||||
|
this.treeView.Size = new System.Drawing.Size(244, 440);
|
||||||
|
this.treeView.TabIndex = 0;
|
||||||
|
this.treeView.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.TreeView_AfterLabelEdit);
|
||||||
|
this.treeView.AfterCollapse += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterCollapse);
|
||||||
|
this.treeView.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterExpand);
|
||||||
|
this.treeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.TreeView_ItemDrag);
|
||||||
|
this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterSelect);
|
||||||
|
this.treeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseClick);
|
||||||
|
this.treeView.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseDoubleClick);
|
||||||
|
this.treeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.TreeView_DragDrop);
|
||||||
|
this.treeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.TreeView_DragEnter);
|
||||||
|
this.treeView.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TreeView_KeyDown);
|
||||||
|
this.treeView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TreeView_MouseUp);
|
||||||
|
//
|
||||||
|
// splitContainer
|
||||||
|
//
|
||||||
|
this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.splitContainer.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.splitContainer.Name = "splitContainer";
|
||||||
|
//
|
||||||
|
// splitContainer.Panel1
|
||||||
|
//
|
||||||
|
this.splitContainer.Panel1.Controls.Add(this.buttonConfig);
|
||||||
|
this.splitContainer.Panel1.Controls.Add(this.treeView);
|
||||||
|
this.splitContainer.Panel1.Controls.Add(this.comboBoxDatabase);
|
||||||
|
//
|
||||||
|
// splitContainer.Panel2
|
||||||
|
//
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.panelRecord);
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.buttonSave);
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.labelGroup);
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.comboBoxGroup);
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.textBoxTitle);
|
||||||
|
this.splitContainer.Panel2.Controls.Add(this.labelTitle);
|
||||||
|
this.splitContainer.Size = new System.Drawing.Size(574, 496);
|
||||||
|
this.splitContainer.SplitterDistance = 250;
|
||||||
|
this.splitContainer.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// buttonConfig
|
||||||
|
//
|
||||||
|
this.buttonConfig.Image = global::RCM.Properties.Resources.ConfigIcon;
|
||||||
|
this.buttonConfig.Location = new System.Drawing.Point(3, 1);
|
||||||
|
this.buttonConfig.Name = "buttonConfig";
|
||||||
|
this.buttonConfig.Size = new System.Drawing.Size(24, 24);
|
||||||
|
this.buttonConfig.TabIndex = 2;
|
||||||
|
this.buttonConfig.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonConfig.Click += new System.EventHandler(this.ButtonConfig_Click);
|
||||||
|
//
|
||||||
|
// comboBoxDatabase
|
||||||
|
//
|
||||||
|
this.comboBoxDatabase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.comboBoxDatabase.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboBoxDatabase.Location = new System.Drawing.Point(33, 3);
|
||||||
|
this.comboBoxDatabase.Name = "comboBoxDatabase";
|
||||||
|
this.comboBoxDatabase.Size = new System.Drawing.Size(214, 21);
|
||||||
|
this.comboBoxDatabase.TabIndex = 1;
|
||||||
|
this.comboBoxDatabase.SelectedIndexChanged += new System.EventHandler(this.ComboBoxDatabase_SelectedIndexChanged);
|
||||||
|
//
|
||||||
|
// panelRecord
|
||||||
|
//
|
||||||
|
this.panelRecord.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.panelRecord.Location = new System.Drawing.Point(11, 82);
|
||||||
|
this.panelRecord.Name = "panelRecord";
|
||||||
|
this.panelRecord.Size = new System.Drawing.Size(306, 350);
|
||||||
|
this.panelRecord.TabIndex = 15;
|
||||||
|
//
|
||||||
|
// buttonSave
|
||||||
|
//
|
||||||
|
this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.buttonSave.Enabled = false;
|
||||||
|
this.buttonSave.Location = new System.Drawing.Point(131, 438);
|
||||||
|
this.buttonSave.Name = "buttonSave";
|
||||||
|
this.buttonSave.Size = new System.Drawing.Size(175, 23);
|
||||||
|
this.buttonSave.TabIndex = 14;
|
||||||
|
this.buttonSave.Text = "Save changes";
|
||||||
|
this.buttonSave.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSave.Click += new System.EventHandler(this.ButtonSave_Click);
|
||||||
|
//
|
||||||
|
// labelGroup
|
||||||
|
//
|
||||||
|
this.labelGroup.AutoSize = true;
|
||||||
|
this.labelGroup.Location = new System.Drawing.Point(11, 42);
|
||||||
|
this.labelGroup.Name = "labelGroup";
|
||||||
|
this.labelGroup.Size = new System.Drawing.Size(39, 13);
|
||||||
|
this.labelGroup.TabIndex = 13;
|
||||||
|
this.labelGroup.Text = "Group:";
|
||||||
|
//
|
||||||
|
// comboBoxGroup
|
||||||
|
//
|
||||||
|
this.comboBoxGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboBoxGroup.Enabled = false;
|
||||||
|
this.comboBoxGroup.FormattingEnabled = true;
|
||||||
|
this.comboBoxGroup.Location = new System.Drawing.Point(131, 39);
|
||||||
|
this.comboBoxGroup.Name = "comboBoxGroup";
|
||||||
|
this.comboBoxGroup.Size = new System.Drawing.Size(175, 21);
|
||||||
|
this.comboBoxGroup.TabIndex = 12;
|
||||||
|
//
|
||||||
|
// textBoxTitle
|
||||||
|
//
|
||||||
|
this.textBoxTitle.Enabled = false;
|
||||||
|
this.textBoxTitle.Location = new System.Drawing.Point(131, 13);
|
||||||
|
this.textBoxTitle.Name = "textBoxTitle";
|
||||||
|
this.textBoxTitle.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxTitle.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// labelTitle
|
||||||
|
//
|
||||||
|
this.labelTitle.AutoSize = true;
|
||||||
|
this.labelTitle.Location = new System.Drawing.Point(11, 16);
|
||||||
|
this.labelTitle.Name = "labelTitle";
|
||||||
|
this.labelTitle.Size = new System.Drawing.Size(30, 13);
|
||||||
|
this.labelTitle.TabIndex = 0;
|
||||||
|
this.labelTitle.Text = "Title:";
|
||||||
|
//
|
||||||
|
// timerDataSourceRefresh
|
||||||
|
//
|
||||||
|
this.timerDataSourceRefresh.Interval = 30000;
|
||||||
|
this.timerDataSourceRefresh.Tick += new System.EventHandler(this.TimerDataSourceRefresh_Tick);
|
||||||
|
//
|
||||||
|
// statusStrip
|
||||||
|
//
|
||||||
|
this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.toolStripStatusLabel,
|
||||||
|
this.toolStripStatusLastRefresh});
|
||||||
|
this.statusStrip.Location = new System.Drawing.Point(0, 474);
|
||||||
|
this.statusStrip.Name = "statusStrip";
|
||||||
|
this.statusStrip.Size = new System.Drawing.Size(574, 22);
|
||||||
|
this.statusStrip.TabIndex = 0;
|
||||||
|
this.statusStrip.Text = "statusStrip1";
|
||||||
|
//
|
||||||
|
// toolStripStatusLabel
|
||||||
|
//
|
||||||
|
this.toolStripStatusLabel.Name = "toolStripStatusLabel";
|
||||||
|
this.toolStripStatusLabel.Size = new System.Drawing.Size(134, 17);
|
||||||
|
this.toolStripStatusLabel.Text = "Last data source refresh:";
|
||||||
|
//
|
||||||
|
// toolStripStatusLastRefresh
|
||||||
|
//
|
||||||
|
this.toolStripStatusLastRefresh.Name = "toolStripStatusLastRefresh";
|
||||||
|
this.toolStripStatusLastRefresh.Size = new System.Drawing.Size(36, 17);
|
||||||
|
this.toolStripStatusLastRefresh.Text = "never";
|
||||||
|
//
|
||||||
|
// MainForm
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(574, 496);
|
||||||
|
this.Controls.Add(this.statusStrip);
|
||||||
|
this.Controls.Add(this.splitContainer);
|
||||||
|
this.Name = "MainForm";
|
||||||
|
this.Text = "Remote Connection Manager";
|
||||||
|
this.splitContainer.Panel1.ResumeLayout(false);
|
||||||
|
this.splitContainer.Panel2.ResumeLayout(false);
|
||||||
|
this.splitContainer.Panel2.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
|
||||||
|
this.splitContainer.ResumeLayout(false);
|
||||||
|
this.statusStrip.ResumeLayout(false);
|
||||||
|
this.statusStrip.PerformLayout();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.TreeView treeView;
|
||||||
|
private System.Windows.Forms.SplitContainer splitContainer;
|
||||||
|
private System.Windows.Forms.ComboBox comboBoxDatabase;
|
||||||
|
private System.Windows.Forms.TextBox textBoxTitle;
|
||||||
|
private System.Windows.Forms.Label labelTitle;
|
||||||
|
private System.Windows.Forms.Label labelGroup;
|
||||||
|
private System.Windows.Forms.ComboBox comboBoxGroup;
|
||||||
|
private System.Windows.Forms.Button buttonSave;
|
||||||
|
private System.Windows.Forms.Panel panelRecord;
|
||||||
|
private System.Windows.Forms.Timer timerDataSourceRefresh;
|
||||||
|
private System.Windows.Forms.Button buttonConfig;
|
||||||
|
private System.Windows.Forms.StatusStrip statusStrip;
|
||||||
|
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel;
|
||||||
|
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLastRefresh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
463
RCM/MainForm.cs
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class MainForm : Form {
|
||||||
|
#region Form
|
||||||
|
protected override CreateParams CreateParams { // Workaround for control flickering reduction
|
||||||
|
get {
|
||||||
|
CreateParams cp = base.CreateParams;
|
||||||
|
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainForm() {
|
||||||
|
InitializeComponent();
|
||||||
|
treeView.ImageList = Config.IconList;
|
||||||
|
treeView.ContextMenuStrip = GetNewRecordContextMenu();
|
||||||
|
treeView.TreeViewNodeSorter = new RecordNodeSorter();
|
||||||
|
LoadDataSources(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadDataSources(int selectedIndex) {
|
||||||
|
comboBoxDatabase.Items.Clear();
|
||||||
|
comboBoxDatabase.Items.AddRange(Config.DataSources.ToArray());
|
||||||
|
if (comboBoxDatabase.Items.Count == 0) {
|
||||||
|
treeView.Enabled = false;
|
||||||
|
treeView.Nodes.Clear();
|
||||||
|
} else {
|
||||||
|
treeView.Enabled = true;
|
||||||
|
comboBoxDatabase.SelectedIndex = selectedIndex != -1 ? selectedIndex : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonConfig_Click(object sender, EventArgs e) {
|
||||||
|
int selectedIndex = comboBoxDatabase.SelectedIndex;
|
||||||
|
ConfigForm configForm = new ConfigForm();
|
||||||
|
configForm.ShowDialog();
|
||||||
|
configForm.Dispose();
|
||||||
|
LoadDataSources(selectedIndex);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ComboBox & DataSource
|
||||||
|
private void RebuildGroupCombobox(IRecord record) {
|
||||||
|
List<GroupPath> groupPaths = new List<GroupPath>() { new GroupPath(null) };
|
||||||
|
groupPaths.AddRange(RebuildGroupCombobox(treeView.Nodes));
|
||||||
|
comboBoxGroup.Items.Clear();
|
||||||
|
comboBoxGroup.Items.AddRange(groupPaths.ToArray());
|
||||||
|
comboBoxGroup.SelectedItem = groupPaths.First(gp => gp.Id == record.GroupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GroupPath> RebuildGroupCombobox(TreeNodeCollection nodes) {
|
||||||
|
List<GroupPath> groupPaths = new List<GroupPath>();
|
||||||
|
foreach (TreeNode node in nodes) {
|
||||||
|
if ((IRecord)node.Tag is Group && !ReferenceEquals(node, treeView.SelectedNode)) {
|
||||||
|
groupPaths.Add(new GroupPath(node));
|
||||||
|
groupPaths.AddRange(RebuildGroupCombobox(node.Nodes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groupPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReloadDataSource() {
|
||||||
|
RebuildTreeView();
|
||||||
|
timerDataSourceRefresh.Stop();
|
||||||
|
timerDataSourceRefresh.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ComboBoxDatabase_SelectedIndexChanged(object sender, EventArgs e) => ReloadDataSource();
|
||||||
|
|
||||||
|
private void RefreshContextMenuItem_Click(object sender, EventArgs e) => ReloadDataSource();
|
||||||
|
|
||||||
|
private void TimerDataSourceRefresh_Tick(object sender, EventArgs e) {
|
||||||
|
RebuildTreeView();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TreeView display
|
||||||
|
|
||||||
|
/* test */
|
||||||
|
private void RebuildTreeView() {
|
||||||
|
// Select all records from database
|
||||||
|
IDataSource dataSource = (IDataSource)comboBoxDatabase.SelectedItem;
|
||||||
|
Dictionary<Guid, IRecord> dataSourceRecords = dataSource.SelectAll();
|
||||||
|
Guid selectedGuid = treeView.SelectedNode == null ? Guid.Empty : ((IRecord)treeView.SelectedNode.Tag).Id;
|
||||||
|
|
||||||
|
// Flatten tree nodes
|
||||||
|
List<TreeNode> flattenedTreeList = new List<TreeNode>();
|
||||||
|
int lastProcessPosition = 0;
|
||||||
|
flattenedTreeList.AddRange(treeView.Nodes.Cast<TreeNode>());
|
||||||
|
while (lastProcessPosition < flattenedTreeList.Count) {
|
||||||
|
flattenedTreeList.AddRange(flattenedTreeList[lastProcessPosition++].Nodes.Cast<TreeNode>());
|
||||||
|
}
|
||||||
|
Dictionary<Guid, TreeNode> flattenedTree = flattenedTreeList.ToDictionary(n => ((IRecord)n.Tag).Id, n => n);
|
||||||
|
|
||||||
|
// Iterate over existing nodes
|
||||||
|
foreach (TreeNode node in flattenedTree.Values) {
|
||||||
|
IRecord record = (IRecord)node.Tag;
|
||||||
|
// Delete node if record no longer present in database
|
||||||
|
if (!dataSourceRecords.ContainsKey(record.Id)) {
|
||||||
|
node.Remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Update existing record if it has changed since the last refresh
|
||||||
|
IRecord updatedRecord = dataSourceRecords[record.Id];
|
||||||
|
if (ConfigSerializer.ObjToString(record) != ConfigSerializer.ObjToString(updatedRecord)) {
|
||||||
|
node.Tag = updatedRecord;
|
||||||
|
node.Text = updatedRecord.Title;
|
||||||
|
if (updatedRecord.GroupId != record.GroupId) {
|
||||||
|
node.Remove();
|
||||||
|
if (updatedRecord.GroupId == Guid.Empty) {
|
||||||
|
treeView.Nodes.Add(node);
|
||||||
|
} else {
|
||||||
|
flattenedTree[updatedRecord.GroupId].Nodes.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove processed record
|
||||||
|
dataSourceRecords.Remove(record.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create nodes from unprocessed (new) records
|
||||||
|
foreach (IRecord record in dataSourceRecords.Values) {
|
||||||
|
TreeNode node = CreateTreeNodeFromRecord(record);
|
||||||
|
if (dataSource.ExpandedNodes.Contains(record.Id)) {
|
||||||
|
node.Expand();
|
||||||
|
}
|
||||||
|
flattenedTree.Add(record.Id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign nodes to tree structure
|
||||||
|
foreach (IRecord record in dataSourceRecords.Values) {
|
||||||
|
TreeNode node = flattenedTree[record.Id];
|
||||||
|
if (record.GroupId == Guid.Empty) {
|
||||||
|
treeView.Nodes.Add(node);
|
||||||
|
} else {
|
||||||
|
flattenedTree[record.GroupId].Nodes.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode selectedNode = flattenedTree.ContainsKey(selectedGuid) ? flattenedTree[selectedGuid] : null;
|
||||||
|
SortTreeAndSelectNode(selectedNode);
|
||||||
|
toolStripStatusLastRefresh.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
/* test end */
|
||||||
|
|
||||||
|
/*private void RebuildTreeView() {
|
||||||
|
IDataSource dataSource = (IDataSource)comboBoxDatabase.SelectedItem;
|
||||||
|
Dictionary<Guid, TreeNode> nodes = new Dictionary<Guid, TreeNode>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach (IRecord record in dataSource.SelectAll()) {
|
||||||
|
nodes.Add(record.Id, CreateTreeNodeFromRecord(record));
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to retieve records from data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Guid selectedGuid = treeView.SelectedNode == null ? Guid.Empty : ((IRecord)treeView.SelectedNode.Tag).Id;
|
||||||
|
TreeNode selectedNode = null;
|
||||||
|
treeView.Nodes.Clear();
|
||||||
|
foreach (TreeNode node in nodes.Values) {
|
||||||
|
IRecord record = (IRecord)node.Tag;
|
||||||
|
if (record.GroupId == Guid.Empty) {
|
||||||
|
treeView.Nodes.Add(node);
|
||||||
|
} else {
|
||||||
|
nodes[record.GroupId].Nodes.Add(node);
|
||||||
|
}
|
||||||
|
if (record.Id == selectedGuid) {
|
||||||
|
selectedNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SortTreeAndSelectNode(selectedNode);
|
||||||
|
foreach (TreeNode node in nodes.Values) {
|
||||||
|
if (dataSource.ExpandedNodes.Contains(((IRecord)node.Tag).Id)) {
|
||||||
|
node.Expand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toolStripStatusLastRefresh.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private TreeNode CreateTreeNodeFromRecord(IRecord record) {
|
||||||
|
string imageKey = record.GetType().FullName;
|
||||||
|
return new TreeNode(record.Title) { Tag = record, ImageKey = imageKey, SelectedImageKey = imageKey, ContextMenuStrip = GetContextMenu(record) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SortTreeAndSelectNode(TreeNode node) {
|
||||||
|
treeView.Sort();
|
||||||
|
if (treeView.SelectedNode == node) {
|
||||||
|
ChangeSelectedRecord();
|
||||||
|
} else {
|
||||||
|
treeView.SelectedNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TreeView drag & drop
|
||||||
|
private void TreeView_ItemDrag(object sender, ItemDragEventArgs e) => DoDragDrop(e.Item, DragDropEffects.Move);
|
||||||
|
|
||||||
|
private void TreeView_DragEnter(object sender, DragEventArgs e) => e.Effect = DragDropEffects.Move;
|
||||||
|
|
||||||
|
private void TreeView_DragDrop(object sender, DragEventArgs e) {
|
||||||
|
TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
|
||||||
|
if (draggedNode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TreeNode targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
|
||||||
|
bool canDrop = true;
|
||||||
|
if (targetNode != null) {
|
||||||
|
TreeNode parentNode = targetNode;
|
||||||
|
canDrop = !draggedNode.Equals(targetNode) && targetNode != null && (IRecord)targetNode.Tag is Group;
|
||||||
|
while (canDrop && (parentNode != null)) {
|
||||||
|
canDrop = !ReferenceEquals(draggedNode, parentNode);
|
||||||
|
parentNode = parentNode.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canDrop) {
|
||||||
|
IRecord record = (IRecord)draggedNode.Tag;
|
||||||
|
record.GroupId = targetNode == null ? Guid.Empty : ((IRecord)targetNode.Tag).Id;
|
||||||
|
try {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).Update(record);
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to update record in data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
draggedNode.Remove();
|
||||||
|
if (targetNode == null) {
|
||||||
|
treeView.Nodes.Add(draggedNode);
|
||||||
|
} else {
|
||||||
|
targetNode.Nodes.Add(draggedNode);
|
||||||
|
}
|
||||||
|
SortTreeAndSelectNode(draggedNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TreeView navigation & selection
|
||||||
|
private void TreeView_AfterSelect(object sender, TreeViewEventArgs e) => ChangeSelectedRecord();
|
||||||
|
|
||||||
|
private void TreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) => treeView.SelectedNode = e.Node;
|
||||||
|
|
||||||
|
private void TreeView_MouseUp(object sender, MouseEventArgs e) {
|
||||||
|
if (treeView.HitTest(e.X, e.Y).Node == null) {
|
||||||
|
treeView.SelectedNode = null;
|
||||||
|
ChangeSelectedRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TreeView_KeyDown(object sender, KeyEventArgs e) {
|
||||||
|
if (e.KeyCode == Keys.Delete) {
|
||||||
|
if (treeView.SelectedNode != null) {
|
||||||
|
DeleteSelectedNode();
|
||||||
|
}
|
||||||
|
} else if (e.KeyCode == Keys.F2) {
|
||||||
|
if (treeView.SelectedNode != null) {
|
||||||
|
treeView.SelectedNode.BeginEdit();
|
||||||
|
}
|
||||||
|
} else if (e.KeyCode == Keys.F5) {
|
||||||
|
ReloadDataSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) {
|
||||||
|
IAction[] actions = ((IRecord)e.Node.Tag).GetActions();
|
||||||
|
if (actions.Length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
actions[0].Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeSelectedRecord() {
|
||||||
|
bool isSelected = treeView.SelectedNode != null;
|
||||||
|
textBoxTitle.Enabled = isSelected;
|
||||||
|
textBoxTitle.Text = "";
|
||||||
|
comboBoxGroup.Enabled = isSelected;
|
||||||
|
comboBoxGroup.SelectedItem = null;
|
||||||
|
buttonSave.Enabled = isSelected;
|
||||||
|
LoadRecordControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadRecordControl() {
|
||||||
|
foreach (Control control in panelRecord.Controls) {
|
||||||
|
panelRecord.Controls.Remove(control);
|
||||||
|
control.Dispose();
|
||||||
|
}
|
||||||
|
if (treeView.SelectedNode != null) {
|
||||||
|
IRecord record = (IRecord)treeView.SelectedNode.Tag;
|
||||||
|
textBoxTitle.Text = record.Title;
|
||||||
|
RebuildGroupCombobox(record);
|
||||||
|
panelRecord.Controls.Add(record.GetControl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TreeView_AfterExpand(object sender, TreeViewEventArgs e) {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).ExpandedNodes.Add(((IRecord)e.Node.Tag).Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TreeView_AfterCollapse(object sender, TreeViewEventArgs e) {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).ExpandedNodes.Remove(((IRecord)e.Node.Tag).Id);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Record create/update/delete
|
||||||
|
public delegate void AfterLabelEditWorkaround(TreeNode node);
|
||||||
|
|
||||||
|
private void TreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) {
|
||||||
|
if (e.Label != null) {
|
||||||
|
IRecord record = (IRecord)e.Node.Tag;
|
||||||
|
record.Title = e.Label;
|
||||||
|
try {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).Update(record);
|
||||||
|
BeginInvoke(new AfterLabelEditWorkaround(SortTreeAndSelectNode), new object[] { e.Node }); // Workaround for treeView.Sort() triggering relabel on every sorted node. See note in https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.listview.afterlabeledit?view=netframework-4.6
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to update record in data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.CancelEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSave_Click(object sender, EventArgs e) {
|
||||||
|
TreeNode selectedNode = treeView.SelectedNode;
|
||||||
|
IRecord record = (IRecord)selectedNode.Tag;
|
||||||
|
record.UpdateFromControl();
|
||||||
|
record.Title = selectedNode.Text = textBoxTitle.Text;
|
||||||
|
GroupPath groupPath = (GroupPath)comboBoxGroup.SelectedItem;
|
||||||
|
bool groupChanged = record.GroupId != groupPath.Id;
|
||||||
|
record.GroupId = groupPath.Id;
|
||||||
|
try {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).Update(record);
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to update record in data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (groupChanged) {
|
||||||
|
selectedNode.Remove();
|
||||||
|
if (groupPath.Node != null) {
|
||||||
|
groupPath.Node.Nodes.Add(selectedNode);
|
||||||
|
} else {
|
||||||
|
treeView.Nodes.Add(selectedNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SortTreeAndSelectNode(selectedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewRecordContextMenuItem_Click(object sender, EventArgs e) {
|
||||||
|
Type type = (Type)((ToolStripMenuItem)sender).Tag;
|
||||||
|
TreeNode parentNode = treeView.SelectedNode;
|
||||||
|
IRecord record = (IRecord)Activator.CreateInstance(type);
|
||||||
|
record.Id = Guid.NewGuid();
|
||||||
|
record.Title = string.Format("New {0}", type.GetDisplayName());
|
||||||
|
record.GroupId = parentNode == null ? Guid.Empty : ((IRecord)parentNode.Tag).Id;
|
||||||
|
try {
|
||||||
|
TreeNode node = InsertNewRecord(record, parentNode);
|
||||||
|
SortTreeAndSelectNode(node);
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to insert record into data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DuplicateRecordContextMenuItem_Click(object sender, EventArgs e) {
|
||||||
|
try {
|
||||||
|
TreeNode node = DuplicateRecord(treeView.SelectedNode, treeView.SelectedNode.Parent);
|
||||||
|
SortTreeAndSelectNode(node);
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to insert record into data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeNode DuplicateRecord(TreeNode sourceNode, TreeNode parentNode) {
|
||||||
|
IRecord source = (IRecord)sourceNode.Tag;
|
||||||
|
IRecord record = (IRecord)ConfigSerializer.JsonToObj(ConfigSerializer.ObjToJson(source));
|
||||||
|
record.Id = Guid.NewGuid();
|
||||||
|
record.Title = string.Format("{0} - Copy", record.Title);
|
||||||
|
record.GroupId = parentNode == null ? Guid.Empty : ((IRecord)parentNode.Tag).Id;
|
||||||
|
TreeNode node = InsertNewRecord(record, parentNode);
|
||||||
|
if (record is Group) {
|
||||||
|
foreach (TreeNode sourceChildNode in sourceNode.Nodes) {
|
||||||
|
DuplicateRecord(sourceChildNode, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeNode InsertNewRecord(IRecord record, TreeNode parentNode) {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).Insert(record);
|
||||||
|
TreeNode node = CreateTreeNodeFromRecord(record);
|
||||||
|
if (parentNode != null) {
|
||||||
|
parentNode.Nodes.Add(node);
|
||||||
|
} else {
|
||||||
|
treeView.Nodes.Add(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteSelectedNode() {
|
||||||
|
IRecord record = (IRecord)treeView.SelectedNode.Tag;
|
||||||
|
string message = string.Format("Do you really want to delete selected item{0}?", record is Group ? " and all its subitems" : "");
|
||||||
|
if (MessageBox.Show(message, "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
|
||||||
|
try {
|
||||||
|
((IDataSource)comboBoxDatabase.SelectedItem).Delete(record);
|
||||||
|
} catch {
|
||||||
|
MessageBox.Show("Failed to remove record from data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
treeView.SelectedNode.Remove();
|
||||||
|
SortTreeAndSelectNode(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteRecordContextMenuItem_Click(object sender, EventArgs e) => DeleteSelectedNode();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Record ContextMenu
|
||||||
|
private ContextMenuStrip GetNewRecordContextMenu() {
|
||||||
|
ContextMenuStrip menu = new ContextMenuStrip();
|
||||||
|
ToolStripMenuItem menuItemNew = new ToolStripMenuItem("New item");
|
||||||
|
foreach (Type type in Config.RecordTypes) {
|
||||||
|
ToolStripMenuItem item = new ToolStripMenuItem(type.GetDisplayName()) { Tag = type };
|
||||||
|
item.Click += NewRecordContextMenuItem_Click;
|
||||||
|
menuItemNew.DropDownItems.Add(item);
|
||||||
|
}
|
||||||
|
menu.Items.Add(menuItemNew);
|
||||||
|
ToolStripMenuItem menuItemRefresh = new ToolStripMenuItem("Refresh data source");
|
||||||
|
menuItemRefresh.Click += RefreshContextMenuItem_Click;
|
||||||
|
menu.Items.Add(menuItemRefresh);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContextMenuStrip GetContextMenu(IRecord record) {
|
||||||
|
ContextMenuStrip menu = record is Group ? GetNewRecordContextMenu() : new ContextMenuStrip();
|
||||||
|
bool isDefaultAction = true;
|
||||||
|
foreach (IAction action in record.GetActions()) {
|
||||||
|
ToolStripMenuItem actionItem = new ToolStripMenuItem(action.GetType().GetDisplayName()) { Tag = action };
|
||||||
|
if (isDefaultAction) {
|
||||||
|
actionItem.Font = actionItem.Font = new Font(actionItem.Font, actionItem.Font.Style | FontStyle.Bold);
|
||||||
|
isDefaultAction = false;
|
||||||
|
}
|
||||||
|
actionItem.Click += ActionItem_Click;
|
||||||
|
menu.Items.Add(actionItem);
|
||||||
|
}
|
||||||
|
menu.Items.Add(new ToolStripSeparator());
|
||||||
|
ToolStripMenuItem duplicateItem = new ToolStripMenuItem("Duplicate item");
|
||||||
|
duplicateItem.Click += DuplicateRecordContextMenuItem_Click;
|
||||||
|
menu.Items.Add(duplicateItem);
|
||||||
|
ToolStripMenuItem deleteItem = new ToolStripMenuItem("Delete item");
|
||||||
|
deleteItem.Click += DeleteRecordContextMenuItem_Click;
|
||||||
|
menu.Items.Add(deleteItem);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ActionItem_Click(object sender, EventArgs e) {
|
||||||
|
((IAction)((ToolStripMenuItem)sender).Tag).Run();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
129
RCM/MainForm.resx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="timerDataSourceRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>201, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>39</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
21
RCM/Program.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
static class Program {
|
||||||
|
[STAThread]
|
||||||
|
static void Main() {
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
|
Config.LoadModules();
|
||||||
|
Config.LoadConfiguration();
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
RCM/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("RCM")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("RCM")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Disassembler 2019")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("f5912ef4-edc9-421d-b474-4cdadb121e55")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
73
RCM/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace RCM.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RCM.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap ConfigIcon {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("ConfigIcon", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Properties/Resources.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="ConfigIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\Resources\Config.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
229
RCM/RCM.csproj
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{F5912EF4-EDC9-421D-B474-4CDADB121E55}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>RCM</RootNamespace>
|
||||||
|
<AssemblyName>RCM</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>bin\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="CredentialManagement, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\CredentialManagement.1.0.2\lib\net35\CredentialManagement.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="MySql.Data, Version=8.0.16.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\MySql.Data.8.0.16\lib\net452\MySql.Data.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Data.SQLite, Version=1.0.110.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net46\System.Data.SQLite.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="ConfigFormTab.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="ConfigFormTab.Designer.cs">
|
||||||
|
<DependentUpon>ConfigFormTab.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="ConfigSerializer.cs" />
|
||||||
|
<Compile Include="DataSource\MySQL\MySQL.cs" />
|
||||||
|
<Compile Include="DataSource\MySQL\MySQLControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="DataSource\MySQL\MySQLControl.Designer.cs">
|
||||||
|
<DependentUpon>MySQLControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="DataSource\SQLite\SQLite.cs" />
|
||||||
|
<Compile Include="DataSource\SQLite\SQLiteControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="DataSource\SQLite\SQLiteControl.Designer.cs">
|
||||||
|
<DependentUpon>SQLiteControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Record\RDP\RDP.cs" />
|
||||||
|
<Compile Include="Record\RDP\RDPAction.cs" />
|
||||||
|
<Compile Include="Record\RDP\SMBAction.cs" />
|
||||||
|
<Compile Include="Record\SSH\SCPAction.cs" />
|
||||||
|
<Compile Include="Record\SSH\SSH.cs" />
|
||||||
|
<Compile Include="Record\SSH\SSHAction.cs" />
|
||||||
|
<Compile Include="Record\SSH\SSHConfig.cs" />
|
||||||
|
<Compile Include="Record\SSH\SSHConfigControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Record\SSH\SSHConfigControl.Designer.cs">
|
||||||
|
<DependentUpon>SSHConfigControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Record\WebSite\BrowserAction.cs" />
|
||||||
|
<Compile Include="Record\WebSite\WebSite.cs" />
|
||||||
|
<Compile Include="Record\WebSite\WebSiteControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Record\WebSite\WebSiteControl.Designer.cs">
|
||||||
|
<DependentUpon>WebSiteControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Record\WinBox\WinBox.cs" />
|
||||||
|
<Compile Include="Record\WinBox\WinBoxAction.cs" />
|
||||||
|
<Compile Include="Record\WinBox\WinBoxConfig.cs" />
|
||||||
|
<Compile Include="Record\WinBox\WinBoxConfigControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Record\WinBox\WinBoxConfigControl.Designer.cs">
|
||||||
|
<DependentUpon>WinBoxConfigControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="TypeExtensions.cs" />
|
||||||
|
<EmbeddedResource Include="ConfigForm.resx">
|
||||||
|
<DependentUpon>ConfigForm.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="ConfigFormTab.resx">
|
||||||
|
<DependentUpon>ConfigFormTab.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="DataSource\MySQL\MySQL.resx">
|
||||||
|
<DependentUpon>MySQL.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="DataSource\SQLite\SQLite.resx">
|
||||||
|
<DependentUpon>SQLite.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="MainForm.resx">
|
||||||
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Config.resx">
|
||||||
|
<DependentUpon>Config.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<Compile Include="ConfigForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="ConfigForm.Designer.cs">
|
||||||
|
<DependentUpon>ConfigForm.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="DisplayName.cs" />
|
||||||
|
<Compile Include="GroupPath.cs" />
|
||||||
|
<Compile Include="Config.cs" />
|
||||||
|
<Compile Include="IConfigSection.cs" />
|
||||||
|
<Compile Include="IDataSource.cs" />
|
||||||
|
<Compile Include="MainForm.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MainForm.Designer.cs">
|
||||||
|
<DependentUpon>MainForm.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="RecordNodeSorter.cs" />
|
||||||
|
<Compile Include="IAction.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="SimpleHostControl.cs">
|
||||||
|
<SubType>UserControl</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="SimpleHostControl.Designer.cs">
|
||||||
|
<DependentUpon>SimpleHostControl.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Group.cs" />
|
||||||
|
<Compile Include="IRecord.cs" />
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Record\RDP\RDP.resx">
|
||||||
|
<DependentUpon>RDP.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Record\SSH\SSH.resx">
|
||||||
|
<DependentUpon>SSH.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Record\WebSite\WebSite.resx">
|
||||||
|
<DependentUpon>WebSite.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Record\WinBox\WinBox.resx">
|
||||||
|
<DependentUpon>WinBox.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Record\WinBox\WinBoxConfigControl.resx">
|
||||||
|
<DependentUpon>WinBoxConfigControl.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="SimpleHostControl.resx">
|
||||||
|
<DependentUpon>SimpleHostControl.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Group.resx">
|
||||||
|
<DependentUpon>Group.cs</DependentUpon>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup />
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets')" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
49
RCM/Record/RDP/RDP.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using CredentialManagement;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[Serializable]
|
||||||
|
[DisplayName("Remote Desktop (RDP)")]
|
||||||
|
public class RDP : IRecord {
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public Guid GroupId { get; set; }
|
||||||
|
|
||||||
|
public string Host { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
[NonSerialized]
|
||||||
|
private SimpleHostControl _control;
|
||||||
|
[NonSerialized]
|
||||||
|
private IAction[] _actions;
|
||||||
|
|
||||||
|
public IAction[] GetActions() {
|
||||||
|
if (_actions == null) {
|
||||||
|
_actions = new IAction[] { new RDPAction(this), new SMBAction(this) };
|
||||||
|
}
|
||||||
|
return _actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new SimpleHostControl(Host, Username, Password);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
Host = _control.textBoxHostAddress.Text;
|
||||||
|
Username = _control.textBoxUsername.Text;
|
||||||
|
Password = _control.textBoxPassword.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Record/RDP/RDP.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\..\Resources\RDP.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
55
RCM/Record/RDP/RDPAction.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using CredentialManagement;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("Start Remote Desktop")]
|
||||||
|
public class RDPAction : IAction {
|
||||||
|
private RDP _rdp;
|
||||||
|
|
||||||
|
public RDPAction(RDP rdp) {
|
||||||
|
_rdp = rdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Run() {
|
||||||
|
await Task.Run(() => {
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Unix) {
|
||||||
|
RunUnix();
|
||||||
|
} else {
|
||||||
|
RunWin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunWin() {
|
||||||
|
string[] host = _rdp.Host.Split(new char[] { ':' });
|
||||||
|
string[] username = _rdp.Username.Split(new char[] { '\\' });
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/ff393699(v%3dws.10)
|
||||||
|
string rdpFile = Path.GetTempPath() + Guid.NewGuid().ToString() + ".rdp";
|
||||||
|
using (StreamWriter sw = new StreamWriter(rdpFile)) {
|
||||||
|
sw.Write("drivestoredirect:s:*\r\nredirectprinters:i:0\r\nredirectcomports:i:0\r\nredirectsmartcards:i:0\r\nredirectposdevices:i:0\r\nnetworkautodetect:i:0\r\nauthentication level:i:0\r\nprompt for credentials:i:0\r\nnegotiate security layer:i:1\r\npromptcredentialonce:i:0\r\nsmart sizing:i:0\r\nuse multimon:i:0\r\nbandwidthautodetect:i:1\r\n");
|
||||||
|
sw.Write(string.Format("full address:s:{0}\r\n", _rdp.Host));
|
||||||
|
sw.Write(string.Format("username:s:{0}\r\n", _rdp.Username));
|
||||||
|
if (username.Length == 2) {
|
||||||
|
sw.Write(string.Format("domain:s:{0}\r\n", username[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Credential credential = new Credential(_rdp.Username, _rdp.Password, string.Format("TERMSRV/{0}", host[0]));
|
||||||
|
credential.Save();
|
||||||
|
Process p = Process.Start("mstsc", string.Format("\"{0}\"", rdpFile));
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
File.Delete(rdpFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunUnix() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
RCM/Record/RDP/SMBAction.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("Browse via SMB")]
|
||||||
|
public class SMBAction : IAction {
|
||||||
|
private RDP _rdp;
|
||||||
|
|
||||||
|
public SMBAction(RDP rdp) {
|
||||||
|
_rdp = rdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run() {
|
||||||
|
string[] host = _rdp.Host.Split(new char[] { ':' });
|
||||||
|
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Unix) {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
Process.Start("explorer.exe", string.Format("\\\\{0}", host[0])); // TODO: net use credentials?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
RCM/Record/SSH/SCPAction.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("Browse via SCP")]
|
||||||
|
public class SCPAction : IAction {
|
||||||
|
private SSH _ssh;
|
||||||
|
private SSHConfig _config;
|
||||||
|
|
||||||
|
public SCPAction(SSH ssh) {
|
||||||
|
_ssh = ssh;
|
||||||
|
_config = (SSHConfig)Config.ConfigSections[typeof(SSHConfig)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Run() {
|
||||||
|
await Task.Run(() => {
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Unix) {
|
||||||
|
RunUnix();
|
||||||
|
} else {
|
||||||
|
RunWin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunWin() {
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.WinSCPPath)) {
|
||||||
|
MessageBox.Show("WinSCP path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!File.Exists(_config.WinSCPPath)) {
|
||||||
|
MessageBox.Show("WinSCP path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.SSHKeys) && string.IsNullOrWhiteSpace(_ssh.Password)) {
|
||||||
|
MessageBox.Show("No authentication method was set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.PageantPath)) {
|
||||||
|
MessageBox.Show("Pageant path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!File.Exists(_config.PageantPath)) {
|
||||||
|
MessageBox.Show("Pageant path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder scpParams = new StringBuilder();
|
||||||
|
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
|
||||||
|
Process.Start(_config.PageantPath, _config.SSHKeys);
|
||||||
|
scpParams.Append(string.Format("/privatekey={0} ", _config.SSHKeys)); //TODO: Zvlada vic klicu?
|
||||||
|
}
|
||||||
|
scpParams.AppendFormat("scp://{0}@{1}", _ssh.Username, _ssh.Host); // TODO: password?
|
||||||
|
Process.Start(_config.WinSCPPath, scpParams.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunUnix() {
|
||||||
|
// TOOD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
RCM/Record/SSH/SSH.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[Serializable]
|
||||||
|
public class SSH : IRecord {
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public Guid GroupId { get; set; }
|
||||||
|
|
||||||
|
public string Host { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
[NonSerialized]
|
||||||
|
private SimpleHostControl _control;
|
||||||
|
[NonSerialized]
|
||||||
|
private IAction[] _actions;
|
||||||
|
|
||||||
|
public IAction[] GetActions() {
|
||||||
|
if (_actions == null) {
|
||||||
|
_actions = new IAction[] { new SSHAction(this), new SCPAction(this) };
|
||||||
|
}
|
||||||
|
return _actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new SimpleHostControl(Host, Username, Password);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
Host = _control.textBoxHostAddress.Text;
|
||||||
|
Username = _control.textBoxUsername.Text;
|
||||||
|
Password = _control.textBoxPassword.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Record/SSH/SSH.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\..\Resources\SSH.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
72
RCM/Record/SSH/SSHAction.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("Start SSH")]
|
||||||
|
public class SSHAction : IAction {
|
||||||
|
private SSH _ssh;
|
||||||
|
private SSHConfig _config;
|
||||||
|
|
||||||
|
public SSHAction(SSH ssh) {
|
||||||
|
_ssh = ssh;
|
||||||
|
_config = (SSHConfig)Config.ConfigSections[typeof(SSHConfig)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Run() {
|
||||||
|
await Task.Run(() => {
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Unix) {
|
||||||
|
RunUnix();
|
||||||
|
} else {
|
||||||
|
RunWin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunWin() {
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.PuttyPath)) {
|
||||||
|
MessageBox.Show("PuTTY path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!File.Exists(_config.PuttyPath)) {
|
||||||
|
MessageBox.Show("PuTTY path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.SSHKeys) && string.IsNullOrWhiteSpace(_ssh.Password)) {
|
||||||
|
MessageBox.Show("No authentication method was set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.PageantPath)) {
|
||||||
|
MessageBox.Show("Pageant path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!File.Exists(_config.PageantPath)) {
|
||||||
|
MessageBox.Show("Pageant path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] host = _ssh.Host.Split(new char[] { ':' });
|
||||||
|
StringBuilder puttyParams = new StringBuilder();
|
||||||
|
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
|
||||||
|
Process.Start(_config.PageantPath, _config.SSHKeys);
|
||||||
|
puttyParams.AppendFormat("-i {0} ", _config.SSHKeys);
|
||||||
|
} else {
|
||||||
|
puttyParams.AppendFormat("-pw {0} ", _ssh.Password);
|
||||||
|
}
|
||||||
|
if (host.Length > 1) {
|
||||||
|
puttyParams.AppendFormat("-P {0} ", host[1]);
|
||||||
|
}
|
||||||
|
puttyParams.AppendFormat("{0}@{1}", _ssh.Username, host[0]);
|
||||||
|
Process.Start(_config.PuttyPath, puttyParams.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunUnix() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
RCM/Record/SSH/SSHConfig.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("SSH")]
|
||||||
|
[Serializable]
|
||||||
|
public class SSHConfig : IConfigSection {
|
||||||
|
public string SSHKeys { get; set; }
|
||||||
|
public string PuttyPath { get; set; }
|
||||||
|
public string PageantPath { get; set; }
|
||||||
|
public string WinSCPPath { get; set; }
|
||||||
|
[NonSerialized]
|
||||||
|
private SSHConfigControl _control;
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new SSHConfigControl(this);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
SSHKeys = _control.textBoxSshKeys.Text;
|
||||||
|
PuttyPath = _control.textBoxPutty.Text;
|
||||||
|
PageantPath = _control.textBoxPageant.Text;
|
||||||
|
WinSCPPath = _control.textBoxWinSCP.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
222
RCM/Record/SSH/SSHConfigControl.Designer.cs
generated
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class SSHConfigControl {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.labelPutty = new System.Windows.Forms.Label();
|
||||||
|
this.groupBoxWindows = new System.Windows.Forms.GroupBox();
|
||||||
|
this.buttonSelectPageant = new System.Windows.Forms.Button();
|
||||||
|
this.buttonSelectPutty = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxPageant = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxPutty = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelPageant = new System.Windows.Forms.Label();
|
||||||
|
this.labelSshKey = new System.Windows.Forms.Label();
|
||||||
|
this.textBoxSshKeys = new System.Windows.Forms.TextBox();
|
||||||
|
this.buttonSelectSshKeys = new System.Windows.Forms.Button();
|
||||||
|
this.openFileDialogSshKeys = new System.Windows.Forms.OpenFileDialog();
|
||||||
|
this.openFileDialogPutty = new System.Windows.Forms.OpenFileDialog();
|
||||||
|
this.openFileDialogPageant = new System.Windows.Forms.OpenFileDialog();
|
||||||
|
this.buttonSelectWinSCP = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxWinSCP = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelWinSCP = new System.Windows.Forms.Label();
|
||||||
|
this.openFileDialogWinSCP = new System.Windows.Forms.OpenFileDialog();
|
||||||
|
this.groupBoxWindows.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// labelPutty
|
||||||
|
//
|
||||||
|
this.labelPutty.AutoSize = true;
|
||||||
|
this.labelPutty.Location = new System.Drawing.Point(19, 22);
|
||||||
|
this.labelPutty.Name = "labelPutty";
|
||||||
|
this.labelPutty.Size = new System.Drawing.Size(101, 13);
|
||||||
|
this.labelPutty.TabIndex = 0;
|
||||||
|
this.labelPutty.Text = "Path to PuTTY.exe:";
|
||||||
|
//
|
||||||
|
// groupBoxWindows
|
||||||
|
//
|
||||||
|
this.groupBoxWindows.Controls.Add(this.buttonSelectWinSCP);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.textBoxWinSCP);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.labelWinSCP);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.buttonSelectPageant);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.buttonSelectPutty);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.textBoxPageant);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.textBoxPutty);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.labelPageant);
|
||||||
|
this.groupBoxWindows.Controls.Add(this.labelPutty);
|
||||||
|
this.groupBoxWindows.Location = new System.Drawing.Point(3, 39);
|
||||||
|
this.groupBoxWindows.Name = "groupBoxWindows";
|
||||||
|
this.groupBoxWindows.Size = new System.Drawing.Size(487, 108);
|
||||||
|
this.groupBoxWindows.TabIndex = 1;
|
||||||
|
this.groupBoxWindows.TabStop = false;
|
||||||
|
this.groupBoxWindows.Text = "Windows";
|
||||||
|
//
|
||||||
|
// buttonSelectPageant
|
||||||
|
//
|
||||||
|
this.buttonSelectPageant.Location = new System.Drawing.Point(452, 44);
|
||||||
|
this.buttonSelectPageant.Name = "buttonSelectPageant";
|
||||||
|
this.buttonSelectPageant.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonSelectPageant.TabIndex = 6;
|
||||||
|
this.buttonSelectPageant.Text = "⋯";
|
||||||
|
this.buttonSelectPageant.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSelectPageant.Click += new System.EventHandler(this.ButtonSelectPageant_Click);
|
||||||
|
//
|
||||||
|
// buttonSelectPutty
|
||||||
|
//
|
||||||
|
this.buttonSelectPutty.Location = new System.Drawing.Point(452, 18);
|
||||||
|
this.buttonSelectPutty.Name = "buttonSelectPutty";
|
||||||
|
this.buttonSelectPutty.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonSelectPutty.TabIndex = 6;
|
||||||
|
this.buttonSelectPutty.Text = "⋯";
|
||||||
|
this.buttonSelectPutty.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSelectPutty.Click += new System.EventHandler(this.ButtonSelectPutty_Click);
|
||||||
|
//
|
||||||
|
// textBoxPageant
|
||||||
|
//
|
||||||
|
this.textBoxPageant.Location = new System.Drawing.Point(132, 45);
|
||||||
|
this.textBoxPageant.Name = "textBoxPageant";
|
||||||
|
this.textBoxPageant.Size = new System.Drawing.Size(314, 20);
|
||||||
|
this.textBoxPageant.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// textBoxPutty
|
||||||
|
//
|
||||||
|
this.textBoxPutty.Location = new System.Drawing.Point(132, 19);
|
||||||
|
this.textBoxPutty.Name = "textBoxPutty";
|
||||||
|
this.textBoxPutty.Size = new System.Drawing.Size(314, 20);
|
||||||
|
this.textBoxPutty.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// labelPageant
|
||||||
|
//
|
||||||
|
this.labelPageant.AutoSize = true;
|
||||||
|
this.labelPageant.Location = new System.Drawing.Point(19, 48);
|
||||||
|
this.labelPageant.Name = "labelPageant";
|
||||||
|
this.labelPageant.Size = new System.Drawing.Size(107, 13);
|
||||||
|
this.labelPageant.TabIndex = 1;
|
||||||
|
this.labelPageant.Text = "Path to Pageant.exe:";
|
||||||
|
//
|
||||||
|
// labelSshKey
|
||||||
|
//
|
||||||
|
this.labelSshKey.AutoSize = true;
|
||||||
|
this.labelSshKey.Location = new System.Drawing.Point(8, 10);
|
||||||
|
this.labelSshKey.Name = "labelSshKey";
|
||||||
|
this.labelSshKey.Size = new System.Drawing.Size(99, 13);
|
||||||
|
this.labelSshKey.TabIndex = 4;
|
||||||
|
this.labelSshKey.Text = "Paths to SSH keys:";
|
||||||
|
//
|
||||||
|
// textBoxSshKeys
|
||||||
|
//
|
||||||
|
this.textBoxSshKeys.Location = new System.Drawing.Point(135, 7);
|
||||||
|
this.textBoxSshKeys.Name = "textBoxSshKeys";
|
||||||
|
this.textBoxSshKeys.Size = new System.Drawing.Size(314, 20);
|
||||||
|
this.textBoxSshKeys.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// buttonSelectSshKeys
|
||||||
|
//
|
||||||
|
this.buttonSelectSshKeys.Location = new System.Drawing.Point(455, 6);
|
||||||
|
this.buttonSelectSshKeys.Name = "buttonSelectSshKeys";
|
||||||
|
this.buttonSelectSshKeys.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonSelectSshKeys.TabIndex = 5;
|
||||||
|
this.buttonSelectSshKeys.Text = "⋯";
|
||||||
|
this.buttonSelectSshKeys.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSelectSshKeys.Click += new System.EventHandler(this.ButtonSelectSshKeys_Click);
|
||||||
|
//
|
||||||
|
// openFileDialogSshKeys
|
||||||
|
//
|
||||||
|
this.openFileDialogSshKeys.Filter = "OpenSSH keys (*.pub)|*.pub|PuTTY keys (*.ppk)|*.ppk|All files (*.*)|*.*";
|
||||||
|
this.openFileDialogSshKeys.Multiselect = true;
|
||||||
|
//
|
||||||
|
// openFileDialogPutty
|
||||||
|
//
|
||||||
|
this.openFileDialogPutty.Filter = "PuTTY.exe|PuTTY.exe";
|
||||||
|
//
|
||||||
|
// openFileDialogPageant
|
||||||
|
//
|
||||||
|
this.openFileDialogPageant.Filter = "Pageant.exe|Pageant.exe";
|
||||||
|
//
|
||||||
|
// buttonSelectWinSCP
|
||||||
|
//
|
||||||
|
this.buttonSelectWinSCP.Location = new System.Drawing.Point(452, 70);
|
||||||
|
this.buttonSelectWinSCP.Name = "buttonSelectWinSCP";
|
||||||
|
this.buttonSelectWinSCP.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonSelectWinSCP.TabIndex = 9;
|
||||||
|
this.buttonSelectWinSCP.Text = "⋯";
|
||||||
|
this.buttonSelectWinSCP.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSelectWinSCP.Click += new System.EventHandler(this.ButtonSelectWinSCP_Click);
|
||||||
|
//
|
||||||
|
// textBoxWinSCP
|
||||||
|
//
|
||||||
|
this.textBoxWinSCP.Location = new System.Drawing.Point(132, 71);
|
||||||
|
this.textBoxWinSCP.Name = "textBoxWinSCP";
|
||||||
|
this.textBoxWinSCP.Size = new System.Drawing.Size(314, 20);
|
||||||
|
this.textBoxWinSCP.TabIndex = 8;
|
||||||
|
//
|
||||||
|
// labelWinSCP
|
||||||
|
//
|
||||||
|
this.labelWinSCP.AutoSize = true;
|
||||||
|
this.labelWinSCP.Location = new System.Drawing.Point(19, 74);
|
||||||
|
this.labelWinSCP.Name = "labelWinSCP";
|
||||||
|
this.labelWinSCP.Size = new System.Drawing.Size(107, 13);
|
||||||
|
this.labelWinSCP.TabIndex = 7;
|
||||||
|
this.labelWinSCP.Text = "Path to WinSCP.exe:";
|
||||||
|
//
|
||||||
|
// openFileDialogWinSCP
|
||||||
|
//
|
||||||
|
this.openFileDialogWinSCP.Filter = "WinSCP.exe|WinSCP.exe";
|
||||||
|
//
|
||||||
|
// SSHConfigControl
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.buttonSelectSshKeys);
|
||||||
|
this.Controls.Add(this.textBoxSshKeys);
|
||||||
|
this.Controls.Add(this.labelSshKey);
|
||||||
|
this.Controls.Add(this.groupBoxWindows);
|
||||||
|
this.Name = "SSHConfigControl";
|
||||||
|
this.Size = new System.Drawing.Size(499, 172);
|
||||||
|
this.groupBoxWindows.ResumeLayout(false);
|
||||||
|
this.groupBoxWindows.PerformLayout();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label labelPutty;
|
||||||
|
private System.Windows.Forms.GroupBox groupBoxWindows;
|
||||||
|
public System.Windows.Forms.TextBox textBoxPageant;
|
||||||
|
public System.Windows.Forms.TextBox textBoxPutty;
|
||||||
|
private System.Windows.Forms.Label labelPageant;
|
||||||
|
private System.Windows.Forms.Label labelSshKey;
|
||||||
|
public System.Windows.Forms.TextBox textBoxSshKeys;
|
||||||
|
private System.Windows.Forms.Button buttonSelectSshKeys;
|
||||||
|
private System.Windows.Forms.Button buttonSelectPageant;
|
||||||
|
private System.Windows.Forms.Button buttonSelectPutty;
|
||||||
|
private System.Windows.Forms.OpenFileDialog openFileDialogSshKeys;
|
||||||
|
private System.Windows.Forms.OpenFileDialog openFileDialogPutty;
|
||||||
|
private System.Windows.Forms.OpenFileDialog openFileDialogPageant;
|
||||||
|
private System.Windows.Forms.Button buttonSelectWinSCP;
|
||||||
|
public System.Windows.Forms.TextBox textBoxWinSCP;
|
||||||
|
private System.Windows.Forms.Label labelWinSCP;
|
||||||
|
private System.Windows.Forms.OpenFileDialog openFileDialogWinSCP;
|
||||||
|
}
|
||||||
|
}
|
37
RCM/Record/SSH/SSHConfigControl.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class SSHConfigControl : UserControl {
|
||||||
|
public SSHConfigControl(SSHConfig sshConfig) {
|
||||||
|
InitializeComponent();
|
||||||
|
textBoxPutty.Text = sshConfig.PuttyPath;
|
||||||
|
textBoxPageant.Text = sshConfig.PageantPath;
|
||||||
|
textBoxSshKeys.Text = sshConfig.SSHKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSelectSshKeys_Click(object sender, EventArgs e) {
|
||||||
|
if (openFileDialogSshKeys.ShowDialog() == DialogResult.OK) {
|
||||||
|
textBoxSshKeys.Text = string.Join(",", openFileDialogSshKeys.FileNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSelectPutty_Click(object sender, EventArgs e) {
|
||||||
|
if (openFileDialogPutty.ShowDialog() == DialogResult.OK) {
|
||||||
|
textBoxPutty.Text = openFileDialogPutty.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSelectPageant_Click(object sender, EventArgs e) {
|
||||||
|
if (openFileDialogPageant.ShowDialog() == DialogResult.OK) {
|
||||||
|
textBoxPageant.Text = openFileDialogPageant.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSelectWinSCP_Click(object sender, EventArgs e) {
|
||||||
|
if (openFileDialogWinSCP.ShowDialog() == DialogResult.OK) {
|
||||||
|
textBoxWinSCP.Text = openFileDialogWinSCP.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
RCM/Record/WebSite/BrowserAction.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("Open in browser")]
|
||||||
|
public class BrowserAction : IAction {
|
||||||
|
private WebSite _website;
|
||||||
|
|
||||||
|
public BrowserAction(WebSite website) {
|
||||||
|
_website = website;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Run() {
|
||||||
|
await Task.Run(() => {
|
||||||
|
if (_website.URL.StartsWith("http://") || _website.URL.StartsWith("https://"))
|
||||||
|
Process.Start(_website.URL); // TODO: vyplnovat hesla
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
RCM/Record/WebSite/WebSite.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[Serializable]
|
||||||
|
public class WebSite : IRecord {
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public Guid GroupId { get; set; }
|
||||||
|
|
||||||
|
public string URL { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
[NonSerialized]
|
||||||
|
private WebSiteControl _control;
|
||||||
|
[NonSerialized]
|
||||||
|
private IAction[] _actions;
|
||||||
|
|
||||||
|
public IAction[] GetActions() {
|
||||||
|
if (_actions == null) {
|
||||||
|
_actions = new IAction[] { new BrowserAction(this) };
|
||||||
|
}
|
||||||
|
return _actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new WebSiteControl(this);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
URL = _control.textBoxURL.Text;
|
||||||
|
Username = _control.textBoxUsername.Text;
|
||||||
|
Password = _control.textBoxPassword.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Record/WebSite/WebSite.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\..\Resources\Website.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
122
RCM/Record/WebSite/WebsiteControl.Designer.cs
generated
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class WebSiteControl {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.buttonUnmaskPassword = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxPassword = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxUsername = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxURL = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelPassword = new System.Windows.Forms.Label();
|
||||||
|
this.labelUsername = new System.Windows.Forms.Label();
|
||||||
|
this.labelURL = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// buttonUnmaskPassword
|
||||||
|
//
|
||||||
|
this.buttonUnmaskPassword.Location = new System.Drawing.Point(273, 51);
|
||||||
|
this.buttonUnmaskPassword.Name = "buttonUnmaskPassword";
|
||||||
|
this.buttonUnmaskPassword.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonUnmaskPassword.TabIndex = 4;
|
||||||
|
this.buttonUnmaskPassword.Text = "●";
|
||||||
|
this.buttonUnmaskPassword.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonUnmaskPassword.Click += new System.EventHandler(this.ButtonUnmaskPassword_Click);
|
||||||
|
//
|
||||||
|
// textBoxPassword
|
||||||
|
//
|
||||||
|
this.textBoxPassword.Location = new System.Drawing.Point(120, 52);
|
||||||
|
this.textBoxPassword.Name = "textBoxPassword";
|
||||||
|
this.textBoxPassword.Size = new System.Drawing.Size(147, 20);
|
||||||
|
this.textBoxPassword.TabIndex = 3;
|
||||||
|
this.textBoxPassword.UseSystemPasswordChar = true;
|
||||||
|
//
|
||||||
|
// textBoxUsername
|
||||||
|
//
|
||||||
|
this.textBoxUsername.Location = new System.Drawing.Point(120, 26);
|
||||||
|
this.textBoxUsername.Name = "textBoxUsername";
|
||||||
|
this.textBoxUsername.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxUsername.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// textBoxURL
|
||||||
|
//
|
||||||
|
this.textBoxURL.Location = new System.Drawing.Point(120, 0);
|
||||||
|
this.textBoxURL.Name = "textBoxURL";
|
||||||
|
this.textBoxURL.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxURL.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// labelPassword
|
||||||
|
//
|
||||||
|
this.labelPassword.AutoSize = true;
|
||||||
|
this.labelPassword.Location = new System.Drawing.Point(0, 55);
|
||||||
|
this.labelPassword.Name = "labelPassword";
|
||||||
|
this.labelPassword.Size = new System.Drawing.Size(56, 13);
|
||||||
|
this.labelPassword.TabIndex = 14;
|
||||||
|
this.labelPassword.Text = "Password:";
|
||||||
|
//
|
||||||
|
// labelUsername
|
||||||
|
//
|
||||||
|
this.labelUsername.AutoSize = true;
|
||||||
|
this.labelUsername.Location = new System.Drawing.Point(0, 29);
|
||||||
|
this.labelUsername.Name = "labelUsername";
|
||||||
|
this.labelUsername.Size = new System.Drawing.Size(58, 13);
|
||||||
|
this.labelUsername.TabIndex = 13;
|
||||||
|
this.labelUsername.Text = "Username:";
|
||||||
|
//
|
||||||
|
// labelURL
|
||||||
|
//
|
||||||
|
this.labelURL.AutoSize = true;
|
||||||
|
this.labelURL.Location = new System.Drawing.Point(0, 3);
|
||||||
|
this.labelURL.Name = "labelURL";
|
||||||
|
this.labelURL.Size = new System.Drawing.Size(32, 13);
|
||||||
|
this.labelURL.TabIndex = 12;
|
||||||
|
this.labelURL.Text = "URL:";
|
||||||
|
//
|
||||||
|
// WebsiteControl
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.buttonUnmaskPassword);
|
||||||
|
this.Controls.Add(this.textBoxPassword);
|
||||||
|
this.Controls.Add(this.textBoxUsername);
|
||||||
|
this.Controls.Add(this.textBoxURL);
|
||||||
|
this.Controls.Add(this.labelPassword);
|
||||||
|
this.Controls.Add(this.labelUsername);
|
||||||
|
this.Controls.Add(this.labelURL);
|
||||||
|
this.Name = "WebsiteControl";
|
||||||
|
this.Size = new System.Drawing.Size(300, 77);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button buttonUnmaskPassword;
|
||||||
|
private System.Windows.Forms.Label labelPassword;
|
||||||
|
private System.Windows.Forms.Label labelUsername;
|
||||||
|
private System.Windows.Forms.Label labelURL;
|
||||||
|
public System.Windows.Forms.TextBox textBoxURL;
|
||||||
|
public System.Windows.Forms.TextBox textBoxUsername;
|
||||||
|
public System.Windows.Forms.TextBox textBoxPassword;
|
||||||
|
}
|
||||||
|
}
|
17
RCM/Record/WebSite/WebsiteControl.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class WebSiteControl : UserControl {
|
||||||
|
public WebSiteControl(WebSite website) {
|
||||||
|
InitializeComponent();
|
||||||
|
textBoxURL.Text = website.URL;
|
||||||
|
textBoxUsername.Text = website.Username;
|
||||||
|
textBoxPassword.Text = website.Password;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonUnmaskPassword_Click(object sender, EventArgs e) {
|
||||||
|
textBoxPassword.UseSystemPasswordChar = !textBoxPassword.UseSystemPasswordChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
RCM/Record/WinBox/WinBox.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[Serializable]
|
||||||
|
public class WinBox : IRecord {
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public Guid GroupId { get; set; }
|
||||||
|
|
||||||
|
public string Host { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
[NonSerialized]
|
||||||
|
private SimpleHostControl _control;
|
||||||
|
[NonSerialized]
|
||||||
|
private IAction[] _actions;
|
||||||
|
|
||||||
|
public IAction[] GetActions() {
|
||||||
|
if (_actions == null) {
|
||||||
|
_actions = new IAction[] { new WinBoxAction(this) };
|
||||||
|
}
|
||||||
|
return _actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new SimpleHostControl(Host, Username, Password);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
Host = _control.textBoxHostAddress.Text;
|
||||||
|
Username = _control.textBoxUsername.Text;
|
||||||
|
Password = _control.textBoxPassword.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
RCM/Record/WinBox/WinBox.resx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\..\..\Resources\WinBox.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
40
RCM/Record/WinBox/WinBoxAction.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("Start WinBox")]
|
||||||
|
public class WinBoxAction : IAction {
|
||||||
|
private WinBox _winbox;
|
||||||
|
private WinBoxConfig _config;
|
||||||
|
|
||||||
|
public WinBoxAction(WinBox winbox) {
|
||||||
|
_winbox = winbox;
|
||||||
|
_config = (WinBoxConfig)Config.ConfigSections[typeof(WinBoxConfig)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Run() {
|
||||||
|
await Task.Run(() => {
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Unix) {
|
||||||
|
RunUnix();
|
||||||
|
} else {
|
||||||
|
RunWin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunWin() {
|
||||||
|
string winboxParam = string.Format("{0} \"{1}\" \"{2}\"", _winbox.Host, _winbox.Username, _winbox.Password);
|
||||||
|
Process.Start(_config.WinBoxPath, winboxParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunUnix() {
|
||||||
|
string winboxParam = string.Format("{0} {1} \"{2}\" \"{3}\"", _config.WinBoxPath, _winbox.Host, _winbox.Username, _winbox.Password);
|
||||||
|
Process.Start("wine", winboxParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
RCM/Record/WinBox/WinBoxConfig.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
[DisplayName("WinBox")]
|
||||||
|
[Serializable]
|
||||||
|
public class WinBoxConfig : IConfigSection {
|
||||||
|
public string WinBoxPath { get; set; }
|
||||||
|
[NonSerialized]
|
||||||
|
private WinBoxConfigControl _control;
|
||||||
|
|
||||||
|
public UserControl GetControl() {
|
||||||
|
if (_control == null || _control.IsDisposed) {
|
||||||
|
_control = new WinBoxConfigControl(this);
|
||||||
|
}
|
||||||
|
return _control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFromControl() {
|
||||||
|
WinBoxPath = _control.textBoxWinBox.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
RCM/Record/WinBox/WinBoxConfigControl.Designer.cs
generated
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class WinBoxConfigControl {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.buttonSelectWinBox = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxWinBox = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelWinBox = new System.Windows.Forms.Label();
|
||||||
|
this.openFileDialogWinBox = new System.Windows.Forms.OpenFileDialog();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// buttonSelectWinBox
|
||||||
|
//
|
||||||
|
this.buttonSelectWinBox.Location = new System.Drawing.Point(455, 6);
|
||||||
|
this.buttonSelectWinBox.Name = "buttonSelectWinBox";
|
||||||
|
this.buttonSelectWinBox.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonSelectWinBox.TabIndex = 9;
|
||||||
|
this.buttonSelectWinBox.Text = "⋯";
|
||||||
|
this.buttonSelectWinBox.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonSelectWinBox.Click += new System.EventHandler(this.ButtonSelectWinBox_Click);
|
||||||
|
//
|
||||||
|
// textBoxWinBox
|
||||||
|
//
|
||||||
|
this.textBoxWinBox.Location = new System.Drawing.Point(135, 7);
|
||||||
|
this.textBoxWinBox.Name = "textBoxWinBox";
|
||||||
|
this.textBoxWinBox.Size = new System.Drawing.Size(314, 20);
|
||||||
|
this.textBoxWinBox.TabIndex = 8;
|
||||||
|
//
|
||||||
|
// labelWinBox
|
||||||
|
//
|
||||||
|
this.labelWinBox.AutoSize = true;
|
||||||
|
this.labelWinBox.Location = new System.Drawing.Point(6, 10);
|
||||||
|
this.labelWinBox.Name = "labelWinBox";
|
||||||
|
this.labelWinBox.Size = new System.Drawing.Size(104, 13);
|
||||||
|
this.labelWinBox.TabIndex = 7;
|
||||||
|
this.labelWinBox.Text = "Path to WinBox.exe:";
|
||||||
|
//
|
||||||
|
// openFileDialogWinBox
|
||||||
|
//
|
||||||
|
this.openFileDialogWinBox.Filter = "WinBox.exe|WinBox.exe";
|
||||||
|
//
|
||||||
|
// WinBoxConfigControl
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.buttonSelectWinBox);
|
||||||
|
this.Controls.Add(this.textBoxWinBox);
|
||||||
|
this.Controls.Add(this.labelWinBox);
|
||||||
|
this.Name = "WinBoxConfigControl";
|
||||||
|
this.Size = new System.Drawing.Size(494, 116);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button buttonSelectWinBox;
|
||||||
|
public System.Windows.Forms.TextBox textBoxWinBox;
|
||||||
|
private System.Windows.Forms.Label labelWinBox;
|
||||||
|
private System.Windows.Forms.OpenFileDialog openFileDialogWinBox;
|
||||||
|
}
|
||||||
|
}
|
16
RCM/Record/WinBox/WinBoxConfigControl.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class WinBoxConfigControl : UserControl {
|
||||||
|
public WinBoxConfigControl(WinBoxConfig winBoxConfig) {
|
||||||
|
InitializeComponent();
|
||||||
|
textBoxWinBox.Text = winBoxConfig.WinBoxPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSelectWinBox_Click(object sender, System.EventArgs e) {
|
||||||
|
if (openFileDialogWinBox.ShowDialog() == DialogResult.OK) {
|
||||||
|
textBoxWinBox.Text = openFileDialogWinBox.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
RCM/Record/WinBox/WinBoxConfigControl.resx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="openFileDialogWinBox.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 21</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
32
RCM/RecordNodeSorter.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public class RecordNodeSorter : IComparer {
|
||||||
|
public int Compare(object o1, object o2) {
|
||||||
|
IRecord record1 = (IRecord)((TreeNode)o1).Tag;
|
||||||
|
IRecord record2 = (IRecord)((TreeNode)o2).Tag;
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if (record1 is Group) {
|
||||||
|
if (record2 is Group) {
|
||||||
|
result = string.Compare(record1.Title, record2.Title);
|
||||||
|
if (result == 0) {
|
||||||
|
result = string.Compare(record1.Id.ToString(), record2.Id.ToString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
} else if (record2 is Group) {
|
||||||
|
result = 1;
|
||||||
|
} else {
|
||||||
|
result = string.Compare(record1.Title, record2.Title);
|
||||||
|
if (result == 0) {
|
||||||
|
result = string.Compare(record1.Id.ToString(), record2.Id.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
RCM/SimpleHostControl.Designer.cs
generated
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
namespace RCM {
|
||||||
|
partial class SimpleHostControl {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Component Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.buttonUnmaskPassword = new System.Windows.Forms.Button();
|
||||||
|
this.textBoxPassword = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxUsername = new System.Windows.Forms.TextBox();
|
||||||
|
this.textBoxHostAddress = new System.Windows.Forms.TextBox();
|
||||||
|
this.labelPassword = new System.Windows.Forms.Label();
|
||||||
|
this.labelUsername = new System.Windows.Forms.Label();
|
||||||
|
this.labelHostAddress = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// buttonUnmaskPassword
|
||||||
|
//
|
||||||
|
this.buttonUnmaskPassword.Location = new System.Drawing.Point(273, 51);
|
||||||
|
this.buttonUnmaskPassword.Name = "buttonUnmaskPassword";
|
||||||
|
this.buttonUnmaskPassword.Size = new System.Drawing.Size(22, 22);
|
||||||
|
this.buttonUnmaskPassword.TabIndex = 4;
|
||||||
|
this.buttonUnmaskPassword.Text = "●";
|
||||||
|
this.buttonUnmaskPassword.UseVisualStyleBackColor = true;
|
||||||
|
this.buttonUnmaskPassword.Click += new System.EventHandler(this.ButtonUnmaskPassword_Click);
|
||||||
|
//
|
||||||
|
// textBoxPassword
|
||||||
|
//
|
||||||
|
this.textBoxPassword.Location = new System.Drawing.Point(120, 52);
|
||||||
|
this.textBoxPassword.Name = "textBoxPassword";
|
||||||
|
this.textBoxPassword.Size = new System.Drawing.Size(147, 20);
|
||||||
|
this.textBoxPassword.TabIndex = 3;
|
||||||
|
this.textBoxPassword.UseSystemPasswordChar = true;
|
||||||
|
//
|
||||||
|
// textBoxUsername
|
||||||
|
//
|
||||||
|
this.textBoxUsername.Location = new System.Drawing.Point(120, 26);
|
||||||
|
this.textBoxUsername.Name = "textBoxUsername";
|
||||||
|
this.textBoxUsername.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxUsername.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// textBoxHostAddress
|
||||||
|
//
|
||||||
|
this.textBoxHostAddress.Location = new System.Drawing.Point(120, 0);
|
||||||
|
this.textBoxHostAddress.Name = "textBoxHostAddress";
|
||||||
|
this.textBoxHostAddress.Size = new System.Drawing.Size(175, 20);
|
||||||
|
this.textBoxHostAddress.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// labelPassword
|
||||||
|
//
|
||||||
|
this.labelPassword.AutoSize = true;
|
||||||
|
this.labelPassword.Location = new System.Drawing.Point(0, 55);
|
||||||
|
this.labelPassword.Name = "labelPassword";
|
||||||
|
this.labelPassword.Size = new System.Drawing.Size(56, 13);
|
||||||
|
this.labelPassword.TabIndex = 14;
|
||||||
|
this.labelPassword.Text = "Password:";
|
||||||
|
//
|
||||||
|
// labelUsername
|
||||||
|
//
|
||||||
|
this.labelUsername.AutoSize = true;
|
||||||
|
this.labelUsername.Location = new System.Drawing.Point(0, 29);
|
||||||
|
this.labelUsername.Name = "labelUsername";
|
||||||
|
this.labelUsername.Size = new System.Drawing.Size(58, 13);
|
||||||
|
this.labelUsername.TabIndex = 13;
|
||||||
|
this.labelUsername.Text = "Username:";
|
||||||
|
//
|
||||||
|
// labelHostAddress
|
||||||
|
//
|
||||||
|
this.labelHostAddress.AutoSize = true;
|
||||||
|
this.labelHostAddress.Location = new System.Drawing.Point(0, 3);
|
||||||
|
this.labelHostAddress.Name = "labelHostAddress";
|
||||||
|
this.labelHostAddress.Size = new System.Drawing.Size(72, 13);
|
||||||
|
this.labelHostAddress.TabIndex = 12;
|
||||||
|
this.labelHostAddress.Text = "Host address:";
|
||||||
|
//
|
||||||
|
// SimpleHostControl
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.buttonUnmaskPassword);
|
||||||
|
this.Controls.Add(this.textBoxPassword);
|
||||||
|
this.Controls.Add(this.textBoxUsername);
|
||||||
|
this.Controls.Add(this.textBoxHostAddress);
|
||||||
|
this.Controls.Add(this.labelPassword);
|
||||||
|
this.Controls.Add(this.labelUsername);
|
||||||
|
this.Controls.Add(this.labelHostAddress);
|
||||||
|
this.Name = "SimpleHostControl";
|
||||||
|
this.Size = new System.Drawing.Size(300, 75);
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button buttonUnmaskPassword;
|
||||||
|
private System.Windows.Forms.Label labelPassword;
|
||||||
|
private System.Windows.Forms.Label labelUsername;
|
||||||
|
private System.Windows.Forms.Label labelHostAddress;
|
||||||
|
public System.Windows.Forms.TextBox textBoxHostAddress;
|
||||||
|
public System.Windows.Forms.TextBox textBoxUsername;
|
||||||
|
public System.Windows.Forms.TextBox textBoxPassword;
|
||||||
|
}
|
||||||
|
}
|
17
RCM/SimpleHostControl.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public partial class SimpleHostControl : UserControl {
|
||||||
|
public SimpleHostControl(string host, string username, string password) {
|
||||||
|
InitializeComponent();
|
||||||
|
textBoxHostAddress.Text = host;
|
||||||
|
textBoxUsername.Text = username;
|
||||||
|
textBoxPassword.Text = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonUnmaskPassword_Click(object sender, EventArgs e) {
|
||||||
|
textBoxPassword.UseSystemPasswordChar = !textBoxPassword.UseSystemPasswordChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
RCM/SimpleHostControl.resx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
14
RCM/TypeExtensions.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RCM {
|
||||||
|
public static class TypeExtensions {
|
||||||
|
public static string GetDisplayName(this Type type) {
|
||||||
|
DisplayName displayName = (DisplayName)type.GetCustomAttributes(typeof(DisplayName), false).FirstOrDefault();
|
||||||
|
return displayName != null ? displayName.Text : type.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
RCM/packages.config
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="BouncyCastle" version="1.8.3.1" targetFramework="net461" />
|
||||||
|
<package id="CredentialManagement" version="1.0.2" targetFramework="net461" />
|
||||||
|
<package id="Google.Protobuf" version="3.6.1" targetFramework="net461" />
|
||||||
|
<package id="MySql.Data" version="8.0.16" targetFramework="net461" />
|
||||||
|
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net461" />
|
||||||
|
<package id="System.Data.SQLite.Core" version="1.0.110.0" targetFramework="net461" />
|
||||||
|
</packages>
|
BIN
Resources/Config.png
Normal file
After Width: | Height: | Size: 512 B |
BIN
Resources/Empty.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/Group.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/MySQL.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/RDP.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/SQLite.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/SSH.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/Website.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/WinBox.ico
Normal file
After Width: | Height: | Size: 1.7 KiB |