first commit

This commit is contained in:
2025-04-07 07:44:27 -07:00
commit d6cde0c05e
512 changed files with 142392 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
namespace LibreHardwareMonitor.UI
{
sealed partial class AboutBox
{
/// <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()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutBox));
this.okButton = new System.Windows.Forms.Button();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.projectLinkLabel = new System.Windows.Forms.LinkLabel();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.licenseLinkLabel = new System.Windows.Forms.LinkLabel();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.okButton.Location = new System.Drawing.Point(269, 79);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 0;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
//
// pictureBox1
//
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Location = new System.Drawing.Point(10, 11);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(48, 48);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
this.pictureBox1.TabIndex = 1;
this.pictureBox1.TabStop = false;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(74, 12);
this.label1.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(117, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Libre Hardware Monitor";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(74, 46);
this.label2.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(250, 13);
this.label2.TabIndex = 3;
this.label2.Text = "Copyright © LibreHardwareMonitor and Contributors";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(74, 29);
this.label3.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(127, 13);
this.label3.TabIndex = 4;
this.label3.Text = "Version 9.0.30729.1 Beta";
//
// projectLinkLabel
//
this.projectLinkLabel.AutoSize = true;
this.projectLinkLabel.Location = new System.Drawing.Point(164, 80);
this.projectLinkLabel.Margin = new System.Windows.Forms.Padding(0);
this.projectLinkLabel.Name = "projectLinkLabel";
this.projectLinkLabel.Size = new System.Drawing.Size(82, 13);
this.projectLinkLabel.TabIndex = 6;
this.projectLinkLabel.TabStop = true;
this.projectLinkLabel.Text = "Project Website";
this.projectLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkLabel_LinkClicked);
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.AutoSize = true;
this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.flowLayoutPanel1.Location = new System.Drawing.Point(10, 100);
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(0, 0);
this.flowLayoutPanel1.TabIndex = 8;
//
// licenseLinkLabel
//
this.licenseLinkLabel.AutoSize = true;
this.licenseLinkLabel.Location = new System.Drawing.Point(25, 80);
this.licenseLinkLabel.Margin = new System.Windows.Forms.Padding(0);
this.licenseLinkLabel.Name = "licenseLinkLabel";
this.licenseLinkLabel.Size = new System.Drawing.Size(107, 13);
this.licenseLinkLabel.TabIndex = 9;
this.licenseLinkLabel.TabStop = true;
this.licenseLinkLabel.Text = "Licensing Information";
this.licenseLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkLabel_LinkClicked);
//
// AboutBox
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(359, 115);
this.Controls.Add(this.licenseLinkLabel);
this.Controls.Add(this.flowLayoutPanel1);
this.Controls.Add(this.projectLinkLabel);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.okButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutBox";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "About";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.LinkLabel projectLinkLabel;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
private System.Windows.Forms.LinkLabel licenseLinkLabel;
}
}

View File

@@ -0,0 +1,36 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using LibreHardwareMonitor.UI.Themes;
namespace LibreHardwareMonitor.UI;
public sealed partial class AboutBox : Form
{
public AboutBox()
{
InitializeComponent();
Font = SystemFonts.MessageBoxFont;
label3.Text = "Version " + Application.ProductVersion;
projectLinkLabel.Links.Remove(projectLinkLabel.Links[0]);
projectLinkLabel.Links.Add(0, projectLinkLabel.Text.Length, "https://github.com/LibreHardwareMonitor/LibreHardwareMonitor");
licenseLinkLabel.Links.Remove(licenseLinkLabel.Links[0]);
licenseLinkLabel.Links.Add(0, licenseLinkLabel.Text.Length, "https://www.mozilla.org/en-US/MPL/2.0/");
Theme.Current.Apply(this);
}
private void LinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo(e.Link.LinkData.ToString()));
}
catch { }
}
}

View File

@@ -0,0 +1,184 @@
<?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.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAADUJJREFUaEPtWelTVHcWzcyXVBJjkkoqCbJDd9PdNCICouyiuAAKhB3BFZhMLOO+
ILIoKq6AIIsYMSK474q4BEzUiRInJho1FZNUjeZL/os799zfe90NMpPumi/WVF7VrddU093n3Hvu8rvv
lT+vP68/r//Dq6WllZqamtmaqKGxgXbv3k3bt2+nrXV1tGXLFqrdtImqa6ppw4ZKKi9fT2vWrKFVq1bR
ihUraOmyZbR48WL6+OOPqbSslBaVLKIFCxbQvLlzqaioiPLz8yknO4c++iiLMjIyaNasWZSamkozZsyg
KVOn0OTJkykhIYFi42Jp0qRJNGHCBBo/fjyFhYURQ/uLZv/9AvCcnGy2HLlnZWXxD35EmZmZlJ6eTrNn
z5YfTktL4x9PoZkzU9lmCojp06ZRcnIyTZmig0mk+Ph4iouPo9jYWIqOjhZgEydOFHDhEeEUxgDHjRtH
oaGhNHbsWAoJCRGzWILJZLKQwRgk7zG00Wyj2F5l+yvbyFdzczPl5eWxtwoojz2G17m5uUJIEcl4gUhK
iiIxffp0msYkpiZPpaSkJErUPBrPFhcHEjFCQghERVFkRASFh4ezl8MoVCMREjKWbLYQMjOBE/UfkpEJ
gNDrr78+/o033rC99tprYxgmSIx8gQDAFxYWUkFBgYQ9l0lkMwFEA5HISNdJaJHQCCAKQmAqCDhFISGe
4p0IIApRE6MoIjLSTkCPAgi01HxA5qAQjkAwGQ1mCg4OJgaf9+abb6aBBMNEJEaWEyQE8HPmzJF7PpOQ
KOTkUnZ2NmVyFEBgaARSaMZMlpAeASHAEUhMVBGAjJhATEyMQ0IcgQh7BBwystlCKdg6loLMIRIBU5CZ
LGYLjRo1qmT06NF5b7311niGCTmNTKCxoZHBF1NRcbGQsEeBZZSVzRFgAvD+LCaQmpaqwIv+p1PyVNY/
A09iz48EPloDH8X6j7R7XyVp6LhQLQKhEoEgs43BW8jIeRAUFAQCZUyggAmEM8z/TKC+vp6K5hbRXFQO
PQrIBRDIAvjZlMKVA94e6mkGyokKoLEAOyxhIyMiKSJceRyAdY/DkLxyD0UScw6w5s3WYGrdyERMZpaS
yXUCu3btouLiuYpAkR4FLn9MALLJzculo0ePUk9PD3V3d1NXVxd9/vnn9Nlnn1F7ezshhxoa6qX0btm8
hWpqamhDRYWU2+UrltOSJUuorOxvtHDhQv6dYnEQSNlCbBTiXIWsKgIGUxAZDAbXCezcuZPmzpsntRs/
AAJIaug/n3MB12+//UY//PADfffdd/TNN9/QnTv/oJtffUU3Bm7Q1atXqbe3l86dO0enTp+iY0eOCMnO
A520b98+2tu8lxobGwm/s3XrVtq4cSP3lA0soTCysddtNptUIatTBAICAlwnAM/Nnz+f5jEJREERyJfG
c/z4cfr999/pyZMnTOChELh37x59/fXXdPPmTRoYGBACly5dEgInT52iIxytrq5DdODAAXuE6rUIbd68
mWqqq6mCI5TKuWQ2q4oDEiBgtVhZ/xby9fV1ncC2bdteIJCXly9JC+k8e/aMnv70lB4/fkzff/89ffvt
t3Tn7l26desW3bgxQNeuXVMROH+OTnMEjh4DAY5ApxaBvZBYA+3YscMuscrKKqlepiCTgLYGK7NYLELK
x8fbdQJ1dVuGEmCNIoFRLo+wHJ49f0Y///yzROHBgwdCYJAJ3L59mwncoOvXr1Pv5V46f/48EzhNx44d
o8OHDzsR2OsgwKOJEKiqlGKAZAVgALdaHQS8vLxcJ4CwDieAEoqZBYn7/Plz+uWXX+jJj0zg4QO6fx8E
BoXAl18ygWvX6XLvZTuBo0ygaxiBRiGwkwlsFQJVVVVcyRK46xq5fCoCFicCnp6e7hCotRNANUKVwBiB
bqsIqAg8dorAXURAJHSDrl1XEhoaAWcJ6REAAT0CVTIrodqg5g8hwI1sjIeH6wQ28bSJKgTvF3MZLShU
FShlZooQ+Bdy4OlP9EjPgX9yDty5IzkwwFXIngOcxJIDksRdnMSd1M4EmkeSUGWl9AxDYKAioEeBLYjt
ww8+dJ0AvhDgYRiB0YkxA2HWQd3/9ddfWT4/0sOHI1Whfrp65Yq9Cp3SqtChQ85VqInQLO1ViEdzSAjN
zhCoIqDngW7vv/++6wSquayh/ksPQAJzCcUUqhN4+vQpPXr0yF6BBgdVAvf397P2lXQAHNLplvqvgMPa
2trknLGbCWzbvo1qa2sJv4cIYC4K1CLgTACv3333XWLgrhGAHuH5ojnwPicwNy9MoJh3ICG9+ty/f5+b
mO79W9THngfwo8dZMlrSQjr4zMGDB2l/x35qbW2hxj17aBcfkuq4XEOu8D4aGSZSRcBRiSAls8VM77zz
jusE8GWFTjNQbm6ONDHMPogApAPwkA60f/v2La4+X0oDO3/hgpa4xwU4Roz9+/dTW2sbdegJPKwLw/to
ZJiNQAClFJXILN5XEXj77bddJ1BRsZ4KWffQPsboHE7g9Ix0aTRdh7rU+HCPx4e7AH+bvuIRYqB/gC5f
vkxnz56lkydP2BNXrzwtLS0CHkdV6N85geGw9eXrFIEARQCVxwoSYkEA7zqB8vJyNX3iDGCfQBUBSAHg
IRt4XoHvl8pz8eJFh/Z7uh3eZ91jfID2G/c0yhkb3V7pv4oq1q+ndevWyYkMM48REdDyAPcgJsPAXSew
bu06AQ6TAwzrH1PoVJ71MXEC/C2uOJBNP1cd1P2+vj6pOpiV0K0RKVQdZ++rKbXhBfkA/Jq1a2ScDgzw
l2ZmAgEGDvAg4RYBfJk60A/dHmD27+jokHKJmeeLL76ga6z7PpYOvA/tAzya1kGWTod4v5WaMLyxbHbX
71be5/JZW7tZqg+0j99btXq1EPD3BwGDygMGbuJRGvORWwRW85dlc91X518cHdNlDsKsAjlg4oRk+q70
ScO6wImrmtYZOnXiJNd9NT5L5dESGPLBBIruK97ftJGrD9Yy5bKSWblypYqAf6B0YyEA8HyHuUUAXwjZ
ZGY41ijYOoAA5HCFy2UvzzrwOmo+EhfeP3FCyeewBl4/4OiVBwclh/Z176+VfRIslE9jiAAIIA8AHHej
0U0C8AaA65bG8kETm5w4WTx56ZJqVucY+JkzZ7jqnBTt9/Q4PC/g97Uz4b20h+u+vhxD5dG1r3t/2bJl
QgDHSj+dAPKAgQM8XrtFYPny5eL1dDZ964AmhnMvTlJnRS6n5bAC4HK85P5wiA8tzp5HtJoYvF42IR29
caHy6EfMpUuXagQ4B/z8pRcYNRK4G9wlgPUggDuvTNDEEuITBIzuccw43d094nWUzM7OA6J5JZsW8Tz+
fyeDr6urs0sHdX8tSweRBnidAHLAz99PCBgMRomEbm4RWPLpp5TGsz9WJjgDQD7oAVhQ7dq5S7SPA4oD
eKd4fV/HPklYaH4P13sBzyVTB19TrZoWyuaqVatFOgCPe2lpqTQy9AFFQAPPw12gIdA9AosXLxGvw/Rt
W3LyNJEQEnlwcFCi0Mly0cGjvKJCCXjOE9R7JO2OHdtoa91WRUDruiCwkrUPqcLzAD87fbaUTSHAgAEe
RMS4MrlF4JNP/s6SwZYNu072PssnmXsA1oQ4dGD/U1lRSSi38B620aUlpXwAmi/zUy73D1QxyA/SU5+L
l1Ui1ifwtBhLBvewcWEKPCfwUO8HMqFAqUxuEcBqPDl5BrXXGihpCoNPni7bZixqsaSNiY6R2R3LKdkm
22yyx8EmARsFi9UxBuvjABqSSkqlbXgZcw88HhCoyYblogPXve/P7/v5+blHoKysjKUyjc63eVDzRiN7
cBqbY9OMNWF0jNowR04Yuh6ER5GMMJDT1+UgFqwdETEegJDBYGKwJgZqZDJsfA8IMChj2fj5M3hff1mp
uEUAmkxk0Odax9DpvZ7UXGOguPgkSWJUIuw6nXf9UVFRNCFywpBFrU4GMtHJ6FGyMpl5OX5UnOVLxdls
WX5UxK+LMpXNyfShORkwb/L18WUSbhIoKSmh2LgkOtsyhk42eQmJpmoDxcQmMngmEAcC2rLWaf8ZFaUW
tsOJ6Ht/kMDO02q18ayP1bmVZWVhOSF5zSwXE8vFSD6+BvLyCSQv7wDy9vYVEm4RWLRoEXt8Ml1sH8NR
8JRIXGgbQ62bAlg+atOMCLxIgCPBueEggCXueBoX5pAVdp8LcgNoXra/mETBKQLieY5AIXu/IN2L//Yi
H3cJLFi4UKpGTGwCaz1RbFI0v45m7bMJcM4BJPNwApEgMGxtHqY9vICh22L7HIwomNXa0GgySz4gB/xZ
/9C+j48fecO8/dwngK0xzsQoiXkYqbWnMhgvsBtCabQ/A2NJIRqTJkWLfABagIpUrDKQobqgFPr4+JCX
pyd5eHjQe++9p59zBdwfGf7PZQJ84Y3R2jOpPL6XsJU5G1Yc/6sBkDvGn3HtCQ1feGMUnkXhmRQ+BOYv
gbn2jEy7XmVPe+CfwZgt/CUw155Saheew+KfwBThelnsj58TD7sQppfNtOuVV/4NcZaFCnU4CO8AAAAA
SUVORK5CYII=
</value>
</data>
</root>

View File

@@ -0,0 +1,186 @@
namespace LibreHardwareMonitor.UI
{
partial class AuthForm
{
/// <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()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AuthForm));
this.enableHTTPAuthCheckBox = new System.Windows.Forms.CheckBox();
this.httpAuthUsernameTextBox = new System.Windows.Forms.TextBox();
this.httpAuthPasswordTextBox = new System.Windows.Forms.TextBox();
this.httpUsernameLabel = new System.Windows.Forms.Label();
this.httpPasswordLabel = new System.Windows.Forms.Label();
this.credentialsGroupBox = new System.Windows.Forms.GroupBox();
this.httpAuthCancelButton = new System.Windows.Forms.Button();
this.httpAuthOkButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.credentialsGroupBox.SuspendLayout();
this.SuspendLayout();
//
// enableHTTPAuthCheckBox
//
this.enableHTTPAuthCheckBox.AutoSize = true;
this.enableHTTPAuthCheckBox.Location = new System.Drawing.Point(12, 12);
this.enableHTTPAuthCheckBox.Name = "enableHTTPAuthCheckBox";
this.enableHTTPAuthCheckBox.Size = new System.Drawing.Size(191, 17);
this.enableHTTPAuthCheckBox.TabIndex = 0;
this.enableHTTPAuthCheckBox.Text = "Enable HTTP Basic Authentication";
this.enableHTTPAuthCheckBox.UseVisualStyleBackColor = true;
this.enableHTTPAuthCheckBox.CheckedChanged += new System.EventHandler(this.EnableHTTPAuthCheckBox_CheckedChanged);
//
// httpAuthUsernameTextBox
//
this.httpAuthUsernameTextBox.Location = new System.Drawing.Point(102, 23);
this.httpAuthUsernameTextBox.MaxLength = 255;
this.httpAuthUsernameTextBox.Name = "httpAuthUsernameTextBox";
this.httpAuthUsernameTextBox.Size = new System.Drawing.Size(171, 20);
this.httpAuthUsernameTextBox.TabIndex = 1;
//
// httpAuthPasswordTextBox
//
this.httpAuthPasswordTextBox.Location = new System.Drawing.Point(102, 60);
this.httpAuthPasswordTextBox.MaxLength = 255;
this.httpAuthPasswordTextBox.Name = "httpAuthPasswordTextBox";
this.httpAuthPasswordTextBox.PasswordChar = '*';
this.httpAuthPasswordTextBox.Size = new System.Drawing.Size(171, 20);
this.httpAuthPasswordTextBox.TabIndex = 2;
this.httpAuthPasswordTextBox.UseSystemPasswordChar = true;
//
// httpUsernameLabel
//
this.httpUsernameLabel.AutoSize = true;
this.httpUsernameLabel.Location = new System.Drawing.Point(6, 26);
this.httpUsernameLabel.Name = "httpUsernameLabel";
this.httpUsernameLabel.Size = new System.Drawing.Size(90, 13);
this.httpUsernameLabel.TabIndex = 6;
this.httpUsernameLabel.Text = "HTTP UserName:";
//
// httpPasswordLabel
//
this.httpPasswordLabel.AutoSize = true;
this.httpPasswordLabel.Location = new System.Drawing.Point(6, 63);
this.httpPasswordLabel.Name = "httpPasswordLabel";
this.httpPasswordLabel.Size = new System.Drawing.Size(88, 13);
this.httpPasswordLabel.TabIndex = 7;
this.httpPasswordLabel.Text = "HTTP Password:";
//
// credentialsGroupBox
//
this.credentialsGroupBox.Controls.Add(this.httpAuthUsernameTextBox);
this.credentialsGroupBox.Controls.Add(this.httpPasswordLabel);
this.credentialsGroupBox.Controls.Add(this.httpAuthPasswordTextBox);
this.credentialsGroupBox.Controls.Add(this.httpUsernameLabel);
this.credentialsGroupBox.Location = new System.Drawing.Point(12, 35);
this.credentialsGroupBox.Name = "credentialsGroupBox";
this.credentialsGroupBox.Size = new System.Drawing.Size(279, 100);
this.credentialsGroupBox.TabIndex = 5;
this.credentialsGroupBox.TabStop = false;
this.credentialsGroupBox.Text = "Credentials";
//
// httpAuthCancelButton
//
this.httpAuthCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.httpAuthCancelButton.Location = new System.Drawing.Point(76, 206);
this.httpAuthCancelButton.Name = "httpAuthCancelButton";
this.httpAuthCancelButton.Size = new System.Drawing.Size(75, 23);
this.httpAuthCancelButton.TabIndex = 3;
this.httpAuthCancelButton.Text = "Cancel";
this.httpAuthCancelButton.UseVisualStyleBackColor = true;
this.httpAuthCancelButton.Click += new System.EventHandler(this.HttpAuthCancelButton_Click);
//
// httpAuthOkButton
//
this.httpAuthOkButton.Location = new System.Drawing.Point(157, 206);
this.httpAuthOkButton.Name = "httpAuthOkButton";
this.httpAuthOkButton.Size = new System.Drawing.Size(75, 23);
this.httpAuthOkButton.TabIndex = 4;
this.httpAuthOkButton.Text = "OK";
this.httpAuthOkButton.UseVisualStyleBackColor = true;
this.httpAuthOkButton.Click += new System.EventHandler(this.HttpAuthOkButton_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 138);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(269, 26);
this.label1.TabIndex = 8;
this.label1.Text = "Password is stored hashed, if you forget it you will need \r\nto set a new one.";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 174);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(284, 26);
this.label2.TabIndex = 9;
this.label2.Text = "If the web server is running then it will need to be restarted \r\nfor the change t" +
"o take effect.";
//
// AuthForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.httpAuthCancelButton;
this.ClientSize = new System.Drawing.Size(305, 241);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.httpAuthOkButton);
this.Controls.Add(this.httpAuthCancelButton);
this.Controls.Add(this.credentialsGroupBox);
this.Controls.Add(this.enableHTTPAuthCheckBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AuthForm";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Set HTTP Credentials";
this.Load += new System.EventHandler(this.AuthForm_Load);
this.credentialsGroupBox.ResumeLayout(false);
this.credentialsGroupBox.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox enableHTTPAuthCheckBox;
private System.Windows.Forms.TextBox httpAuthUsernameTextBox;
private System.Windows.Forms.TextBox httpAuthPasswordTextBox;
private System.Windows.Forms.Label httpUsernameLabel;
private System.Windows.Forms.Label httpPasswordLabel;
private System.Windows.Forms.GroupBox credentialsGroupBox;
private System.Windows.Forms.Button httpAuthCancelButton;
private System.Windows.Forms.Button httpAuthOkButton;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
}
}

View File

@@ -0,0 +1,46 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI;
public partial class AuthForm : Form
{
private readonly MainForm _parent;
public AuthForm(MainForm m)
{
InitializeComponent();
_parent = m;
}
private void AuthForm_Load(object sender, EventArgs e)
{
httpAuthUsernameTextBox.Enabled = httpAuthPasswordTextBox.Enabled = enableHTTPAuthCheckBox.Checked = _parent.Server.AuthEnabled;
httpAuthUsernameTextBox.Text = _parent.Server.UserName;
}
private void HttpAuthCancelButton_Click(object sender, EventArgs e)
{
Close();
}
private void HttpAuthOkButton_Click(object sender, EventArgs e)
{
_parent.Server.UserName = httpAuthUsernameTextBox.Text;
_parent.Server.Password = httpAuthPasswordTextBox.Text;
_parent.Server.AuthEnabled = enableHTTPAuthCheckBox.Checked;
_parent.AuthWebServerMenuItemChecked = _parent.Server.AuthEnabled;
Close();
}
private void EnableHTTPAuthCheckBox_CheckedChanged(object sender, EventArgs e)
{
httpAuthUsernameTextBox.Enabled = httpAuthPasswordTextBox.Enabled = enableHTTPAuthCheckBox.Checked;
}
}

View File

@@ -0,0 +1,377 @@
<?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.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAAAwMAAAAQAgAKglAABGFQAAKAAAABAA
AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEU8OykuDgdIIxQSURwdHU8ZGhpPFhYWTxQU
FE8SEhJPDxAPTw0KCk8KBQRPCAQDTwcDAU8HAQBPBgQEUQUFBTxLVVb/L215/zdXXP89Ozv/ODc3/zM0
M/8vLy//Kioq/yYlJf8iJif/Gycp/xggI/8UHiD/Eh4g/w4SE/8GBARRQWRr/xGXtP8yUln/NDIx/y4u
Lf8qKir/KSgo/ycnJ/8lJCP/Iikq/xs4Pf8aOD7/GzpB/xo4Pf8SGhv/BwMDT01aXP46Ulj+Pzc0/kdH
R/5MTU3+Q0ND/jw8PP43Nzf+Ly8v/igoKP4lKCn+Iico/hwiI/4cHyD/FBMU/wYHBk9XVVX+Rj8+/klJ
Sf6ioqL+rq+u/p+fn/6YmJj+j4+P/oSEhP56enr+dXJy/mNgYP4iHx7+Gxka/xUVFf8HBwdPW1pa/kVF
RP5YWFj+rq6u/r6+vv6wsK/+qamp/qCgoP6UlJT+iYmJ/oGBgf5xcXH+JCQk/hsbG/8WFhb/BwcHT15e
Xv5ISEj+Wlpa/rW1tf7Gxsb+tra2/qysq/6hoaL+lpaW/oyMjP6EhIT+cnJy/iQkJP4cHBz/GBgY/wcH
B09jY2P+TUxM/l5eXv7AwMD+0dHR/r6+vv6vr6/+o6Ok/pmYmf6Rk5P+iouK/nd4d/4lJSX+HR0d/xkZ
Gf8HBwdPZ2dn/lBQUP5iYmL+ysrK/tzc3P7Hxsf+tbS1/qmpqf6fn5/+mJiY/o6Pj/57e3z+JSUm/h4e
Hv8aGhr/CAgIT2tra/5TU1P+ZGRk/tLS0v7j5eP+zMzM/ry8vP6wsLD+pqam/pubm/6TkZH+f39//igo
KP4fHx7/HBwc/wkJCk9ubm7+VlVW/mZlZv7X1tf+6Ojo/tPS0/7Hx8f+urq6/qurq/6dnp7+lJSU/oGB
gP4sLCz+Hx4e/x4dHP8LCwtPb29v/lZWVv5nZ2j+4eHh/vLx8v7Z2dn+zc3N/sDAwP6wsrL+pKOk/pmZ
mf6Ghob+MDAw/h8gH/8eHx//DQsNT29vb/5YWFj+WVpa/sHBwf7V1dX+wMC//rW1tP6pqan+m5ub/o6P
jv6FhYX+cXFx/i4qKv4gLC7/Hiot/w4JB09vb3D+Wlla/lJSUf5VVVX+YGBg/ltbWv5VVVP+Tk5O/kdH
R/5BQUH+Ozs6/jAvL/4oKCj+Izk9/x8tL/8QCQhRbm5u/lVVVf5SUlL+S0tL/kRERP5BQUH+PT09/jk5
Of41NTX+MzIy/i8vLv4sKSj+Jy4v/h9DTP8iLjH/Ew0KSHp7ev5qamr+ZWVl/mFhYv5cXFz+WFhY/lNS
U/5NTU3+R0ZH/kJCQv49PDz+NzU1/jAxMf4qMTL/Jigp/x4cHCn//wAAAAEAAAABAAAAAQAAAAEAAAAB
AAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAKAAAACAAAABAAAAAAQAgAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMBBwcHAwYGBgQGBgYEBgYGBAYG
BgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYG
BgQGBgYEBgYGBAYGBgQGBgYEBwcHAwUFBQIAAAAAAAAAAAAAAAAAAAAAAAAAAAoKCgkMDAwgDQ0NKg0N
DSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0N
DSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoMDAwlCgoKEQUFBQIAAAAAAAAAAAAAAAAAAAAACAgIFAoK
CkcMDAxbDAwMXAwMDFwMDAxcDAwMXAwNDFwNDQ1cDQ0NXA0NDVwNDQ1cDQ0NXA0NDVwNDQ5cDg4OXA4O
DlwODg5cDg4OXA4ODlwODg5cDg4OXA4ODlwODg5cDg4OWw0NDVIMDAwlBwcHAwAAAAAAAAAAAAAAAGFh
YY9ZWFjFUVBP0kpJSdhIR0fYQkJC2D0+Ptg5OTnZODc22TMyM9kuLi/ZKysr2SYmJtkjIyPZICAf2Rsb
G9kXFxjZExMT2Q8ODtkNDAzZDQwM2A0MDNgNDAzYDQ0N2A0NDdgODQ27Dg4OWw0NDSoGBgYEAAAAAAAA
AAAAAAAAYmFhvzxCQ/82Uln/NlJZ/zdPVP89PDz/PDw8/zo6Ov85OTn/ODg4/zY2Nv80NDT/NDQ0/zIy
Mv8xMTD/Ly4w/yw1OP8qNjj/JzQ1/yYyNP8kMDL/Ii4w/x8sLv8fKy3/HyIi/w0NDdgODg5cDQ0NKgYG
BgQAAAAAAAAAAAAAAABjY2K/OFRa/xqXsv8bk6v/M11l/z49Pf89PT3/Ozs7/zo6Of83Nzj/NTY1/zQ0
NP8zMzP/MTEw/y8vL/8uLi7/KzQ2/yk1Nv8nMjT/JjAz/yQvMf8jLjD/ISwu/x8rLf8fISL/DQ0N2A4O
DlwNDQ0qBgYGBAAAAAAAAAAAAAAAAGZkZL86V13/GpKs/zJkbv9BPz//Pj8+/zw8PP85OTn/Njc2/zQ0
Nf8zMjP/MjIy/zAwMP8vLy//Li4u/y0sLP8qKSn/I0hQ/yJHUP8hRk//IEZO/yBGTv8fRU3/HztB/yAf
H/8NDQ3YDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAaWdovz5WW/83YWr/RkND/0FAQP89PT3/ODc3/zIy
Mf8uLi7/LCws/ysrK/8pKSn/Kioq/ygoKP8nJyf/JSUl/yUkJP8kIyP/IyEh/yMiIv8kIiL/JCMj/yQj
I/8iISH/ISAg/w0NDdgODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAABqamq/SUhI/0hHR/9GRkb/QUFB/0ZH
Rv9/f4D/gICA/3h4eP9xcXH/a2tr/2RkZP9dXV7/V1dX/09PT/9ISEj/Q0NC/0BAQP9AQED/Ozs7/yYm
Jv8jIyP/JCQk/yMjI/8jIyH/DQ0N2A4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAG5ubr9LS0v/SkpL/0hI
SP9BQUH/i4uL/6ysrP+hoqH/n5+e/52bnf+YmJj/lJSU/5CQj/+Li4v/hYaF/4CAgP97e3r/dXR1/3Fx
cf9ra2v/Ozs7/yEhIv8kJCT/JCQk/yQkI/8NDQ3YDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAcHBwv01N
Tf9MTU3/SUpK/0BAP/+Wlpj/q6ur/6inp/+kpKT/oKGg/52cnP+YmJj/k5OT/46Ojv+IiYj/g4OC/319
ff93d3f/cXFx/3Fxcf8/Pz//ISEh/yQkJP8lJSX/JSQk/w0NDdkODg5cDQ0NKgYGBgQAAAAAAAAAAAAA
AABycnO/T05P/05PT/9MTEz/QUFB/5ubmv+ysrL/rq6u/6urq/+np6f/oqKi/52dnf+ZmZn/k5OT/42O
jv+IiIf/gYGB/3x7fP90dXT/cXFx/z8/P/8hISH/JCQl/yUlJf8lJib/DQ0N2Q4ODlwNDQ0qBgYGBAAA
AAAAAAAAAAAAAHR1db9RUVH/UFBQ/01NTf9CQkL/np6e/7i4uP+0s7T/sbGx/62trf+oqKj/o6Oj/52d
nf+Yl5f/kZKR/4yMi/+FhYX/fn9//3h4eP9zc3P/Pz8//yIiIv8lJSX/JiYm/yYmJv8NDQ3ZDg4OXA0N
DSoGBgYEAAAAAAAAAAAAAAAAdnZ2v1RUVP9TU1P/T09P/0VFRf+kpKT/v7+//7u7u/+2trb/sbGx/6mp
qf+jo6P/nZ2d/5eXl/+QkZH/jo2N/4mJif+CgoL/fHx8/3d3d/9AQED/IiIi/yYmJv8nJyf/Jycn/w0N
DdkODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAAB4eHi/V1dW/1VVVf9QUFD/RkZG/6moqf/Gxsb/wcHB/7y8
vP+2trb/q6ys/6Wlpf+fn5//mZiY/5WUlf+RkpL/jI2M/4WFhf9/fn//enp6/0FBQf8iIiP/JiYm/ygn
KP8oJyf/DQ0N2Q4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAHp6er9YWVn/V1dX/1JSU/9ISEj/ra2t/8zM
zP/Gxsb/wcHB/7u7u/+wsLD/qamp/6Ojo/+cnZ3/lpaW/5OTk/+Pj47/iIiI/4CBgP98fH3/QkJC/yMj
I/8nJyf/KCgo/ygoKP8ODg7ZDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAfXx9v1tbW/9ZWVn/VFVV/0hJ
Sf+xsbH/0tLS/8vLy//FxcX/v7++/7Ozs/+qqqr/pKWk/6Cfn/+ampr/mpia/5GRkf+Li4r/goOD/35+
fv9CQ0P/IyQk/ycnJ/8pKSn/KSkp/xAQENkODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAAB+fn6/XV1d/1tb
W/9WVlb/S0lJ/7Oysv/W1tb/z8/P/8nJyf/CwsL/tbW1/66trf+np6f/oqKi/5ycnP+ZmJn/k5OT/4yM
jP+FhYX/f39//0ZFRv8kJCT/KCgo/ykpKf8pKSn/EhIS2Q4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAIGB
gb9eXl7/XFxc/1dYV/9KSkr/tLS0/9rb2v/S0tL/zczM/8XFxf+/v7//uLi4/7Gxsf+rq6r/o6Oj/5yc
nP+UlZT/jY2N/4aFhv+AgID/SUlI/yMjI/8oKCj/KSkp/ykpKf8UFBTZDg4OXA0NDSoGBgYEAAAAAAAA
AAAAAAAAg4ODv19fX/9eXl7/WVhZ/0tLS/+0tLT/3d3d/9TU1P/Ozs7/x8bH/8DAwP+5ubn/srKy/6ur
q/+kpKT/nJyc/5WVlv+Ojo7/h4eH/4GBgf9NTU3/IyQk/ygpKP8qKSn/KSkp/xgYGNkODg5cDQ0NKgYG
BgQAAAAAAAAAAAAAAACFhYW/YGBf/15eXv9aWln/TU1N/7a2tv/e3t7/1dXV/87Pz//Hx8f/wcHB/7m5
uv+ysrP/rKyr/6SkpP+dnZ3/lZWW/46Ojv+Hh4f/gYGB/1BQUP8kJCT/KSkp/ykpKf8oKCj/GhkZ2Q4O
DlwNDQ0qBgYGBAAAAAAAAAAAAAAAAIiGhr9gYGD/Xl5e/1paWv9PT0//t7e3/93d3f/U1NT/zs7O/8fH
x//AwMD/ubm5/7Kysv+rq6v/o6Ok/5ycnP+VlZX/jo6N/4aGhv+BgYH/U1NT/yYmJv8qKir/Kisq/ygo
Kf8bHBzYDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAioqKv19fX/9eXl7/Wlta/1FRUf+srKz/4uLi/9LS
0v/Mzcz/xcXG/7+/v/+4ubj/srKx/6urq/+kpKT/nZ2d/5WVlf+Njo7/h4aH/4GBgf9TUlL/KSkp/yws
LP8qLzH/Jy4v/x4eHtgNDQ1cDQ0NKgYGBgQAAAAAAAAAAAAAAACMi4y/Xl5e/11dXf9aWlr/VVVV/1xc
XP+qqqr/sbGx/66urv+qqqr/pKSj/52dnf+VlZX/jo6O/4eHh/+Af3//d3l3/3BwcP9paWn/XFxc/zMz
M/8sLCz/LS0t/yswMf8nLS7/ICAg2A0NDVwNDQ0qBgYGBAAAAAAAAAAAAAAAAI6Ojr9cXFz/XFxc/1pa
Wv9WV1f/UFFQ/0xMS/9HR0f/RERE/0BBQP8+Pj7/PDw8/zo7Ov84ODj/ODg4/zU1Nf8zMzP/MTEx/zAw
MP8uLi//Li4u/y4tL/8qPUD/KDk+/yU4O/8jIiLYDQ0NXA0NDSoGBgYEAAAAAAAAAAAAAAAAkZGRv1pb
W/9bW1r/WFlY/1ZWVv9UVFT/UFBQ/05OTv9MTEz/SklK/0dHR/9FRUX/QkJC/0BAQP8+Pj7/PDw8/zo6
Ov83ODf/NTU1/zIyMv8xMTH/Ly4u/yk7P/8oO0D/JTpA/yUkJNgNDQ1bDQ0NKgcHBwQAAAAAAAAAAAAA
AACTk5O/WFhY/1lZWf9YV1j/VVVV/1JSUv9QUFD/Tk5O/01MTf9KSkr/SEhI/0ZGRv9DQkP/QEFA/z8/
P/89PT3/Ozs6/zg4OP82NTX/MzMz/zExMf8vLi3/IVtn/yovMf8nLi//KCgo0gwMDEcMDAwgBwcHAwAA
AAAAAAAAAAAAAJKTkr9VVFX/VFVV/1RUVP9SUVH/T09P/01NTv9MS0z/SkpK/0hJSP9HR0f/REVE/0FB
Qf8/Pz//Pj4+/zs7PP85Ojn/Nzc3/zQ0NP8xMTH/LzAw/y4uLv8tLCz/KS8w/yUtLv8tLS3FCgoKFAoK
CgkDAwMBAAAAAAAAAAAAAAAAl5aXj5KSkr+QkJC/jYuLv4eHh7+EhIS/gICAv3x8fL93d3e/dHR0v3Bw
cL9samy/ZmZmv2NjY79fX1+/W1tbv1VVVb9SUlK/Tk5Ov0pKSr9ERES/QUFBvz09Pb85OTm/MzMzvzEx
MY8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////gAAAH4AAAB+AA
AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AA
AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB////////////////ygAAAAwAAAAYAAAAAEA
IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAACCAgIBwkJCQwICAgOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcH
Bw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcH
Bw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOCAgIDgkJCQwICAgHAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAICAgHCwsLGQ0NDSwNDQ0yDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0N
DTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0N
DTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMg0NDSwLCwsaCAgIBwAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJCQkMDQ0NLA0NDUwODg5XDg4OWQ4ODlkODg5ZDg4OWQ4O
DlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4O
DlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OVw0N
DUwNDQ0sCQkJDAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFBQUOCgoKMgsLC1cLCwtkCwsLZgsL
C2YLCwtmCwsLZgwMDGYMDAxmDAwMZgwMDGYMDAxmDAwMZgwMDGYMDAxmDAwMZg0NDWYNDQ1mDQ0NZg0N
DWYNDQ1mDQ0NZg0NDWYNDQ1mDg4OZg4ODmYODg5mDg4OZg4ODmYODg5mDg4OZg4ODmYODg5mDg4OZg4O
DmYODg5mDg4OZA4ODlcNDQ0yCAgIDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGJiYv9cXFz/Wlpa/1VV
V/9TU1L/UVFR/05OTv9LSkv/R0dH/0NDQ/9BQUH/Pz4//zw8PP86OTn/NTU1/zIyMv8wMDD/LS0u/yoq
Kv8mJib/JCMk/yEhIf8eHh7/Gxsc/xkZGf8UFBT/ERER/w8PD/8ODg7/Dg4O/w4ODv8ODg7/Dg4O/w4O
Dv8ODg7/Dg4O/w4ODv8PDw//Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGJi
Yf8+PDz/Pzw7/0A7Ov9AOzr/Pzo5/z06Ov89Ojr/Ozs7/zo7Ov87Ojv/Ojo6/zk5Of84ODj/Nzc3/zY2
Nf81NTT/MzMz/zIyMv8xMTH/MTIx/zAwMP8wMDD/Ly4u/y4tLf8sKiv/Kigo/yknJ/8oJiX/JiQk/yUj
I/8kIiH/IiEg/yAeHv8gHh3/IB0d/x8eHv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAGNjY/9BPj3/Gpiz/xuWsP8alrD/Gpey/yKClv8+PT3/PT09/zw8PP87Ozv/Ojo6/zk5
Of84ODj/Nzc3/zY2Nv80NDT/MzMz/zMyMv8yMTH/MDEw/y8vL/8vLy//Li4t/yJWYf8hVF//IVRf/x9T
Xf8gU13/H1Nc/x9RXP8eUFv/HlFb/x1QWv8cT1n/HFBb/x8eHv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAGRkZP9CPTz/GpWv/xySq/8blK3/JH+S/0A8O/8/Pj7/Pj0+/z08
PP87Ozv/Ojo6/zk5Of84ODj/Njc3/zU1Nf80NDT/MzMz/zIyMv8xMTH/MDAv/y8vL/8uLi7/LS0s/ywq
Kv8tKSj/KiYm/yklJf8nJCP/KCIi/yYiIf8lICD/JB8e/yMeHP8iHRz/Hx0d/x8eH/8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGVlZf9GQUD/Gpaw/xuUrf8kgJP/Qj49/0A/
P/8/Pj//Pj4+/zw8PP86Ojr/OTk5/zc3N/82NTb/NDQ0/zMzM/8yMjL/MTEx/zAwMP8vLzD/Ly8v/y4u
Lv8tLS3/LCws/ysrKv8gVV//H1Nd/x9UXf8gU13/H1Nc/x9RXP8fUVz/HlFb/x5QW/8dUFv/Hx4f/x8f
IP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGZmZv9GQkL/GZex/yWB
k/9GQUH/QkFB/0FBQP8/Pz//PT09/zo7Ov84ODj/NTY1/zMzM/8yMjL/MTEx/zAwMP8vLy//Ly8v/y4u
Lv8uLi7/LS0t/ysrK/8qKiv/KSkp/ygnJ/8nJib/JiQk/yYjJP8lIyP/JSIj/yUjI/8kIiL/JCIi/yMh
Iv8iISD/IR8g/x8fH/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGho
aP9IRUX/JIOY/0dDQv9HRUX/Q0ND/0FBQP8/Pz7/Ozo7/zY2Nv8xMTH/Li4u/ywsLf8tLS3/LCws/ysr
K/8pKin/KSkp/ygoKP8oKCj/Jycn/ycmJv8lJSX/JSQl/yQkJP8jJCP/IyIj/yIiIv8iIyL/IyMj/yQj
JP8kJCT/IyMk/yMjI/8jIyP/IiIh/yAgIP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAGlqav9IR0f/SUdH/0lHR/9HR0b/RERF/0FBQf88PDz/Pj8//317ff91dXX/cHBw/2tr
a/9lZGX/YF9f/1paWv9VVVb/UVFR/0xMTP9HR0f/QUFB/z48PP83ODf/MzMz/zAwMP8wMDD/Ly8w/zAw
MP8xMDH/Jycn/yEhIv8jIyP/JCQk/yQkJP8jIyT/IyIi/yEhIf8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAG1ra/9KSkr/SkpK/0lJSf9ISEj/RkVF/0BAQP9CQkL/ycnJ/6ur
q/+fn57/np6e/5ycnP+ampr/l5eX/5SUlP+SkpH/j4+P/4yMjP+Iior/hoeG/4ODg/+AgID/fHx8/3h4
eP90dHP/cXFx/3Jycv9lZWX/Tk9O/ycnJ/8iIiL/IyQj/yQkJP8jIyP/IyMj/yMhI/8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAG5ubf9LS0z/S0xL/0tLS/9JSUn/RkZG/0A/
P/+Mi4z/s7Oz/6Ojo/+ioqL/oKCg/56env+cnJz/mZqZ/5eWl/+UlJT/kJGR/46Ojv+Li4v/h4eH/4OD
g/+Af4D/fHx8/3h4eP91dHT/cHBw/29vb/9wcHD/ZWVl/zAwMP8iIiL/IyMj/yQkJP8kJCT/JCMk/yMk
I/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHBwb/9NTU3/TU1N/0xM
TP9KSkr/R0dH/z8+P/+Ojo7/q6ur/6mpqf+np6b/paSk/6Kiov+goKD/nZ6e/5ubm/+YmJj/lZSU/5GR
kf+Ojo7/ioqL/4eHh/+Dg4P/f39//3x8fP93d3f/c3Nz/29vb/9vb2//cnJy/y8vL/8gISD/IyMj/yUk
JP8kJCT/JCQk/yQkJP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHFx
cf9OTk7/Tk5P/01NTf9MTEz/SEhI/0BAQP+QkJD/sLCv/62trf+srKv/qamp/6enp/+kpKX/oaKh/5+f
n/+cnJz/mJiY/5WVlf+RkZL/jo6O/4qKiv+Ghob/goKC/35+fv96env/dnZ2/3Fycf9vb2//cnJy/y8u
Lv8hICH/IyMj/yQkJf8lJSX/JCQl/yUlJP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAHNzc/9PT0//T09P/09PT/9NTUz/SUlJ/0FAQf+SkpL/tbS0/7Gxsf+vr7D/rq6u/6ur
q/+pqan/pqWl/6Kiov+fn5//nJyc/5iZmP+UlZX/kZGR/42Njf+JiYn/hYWF/4GBgf99fX3/eXl5/3R0
dP9wcHD/cnJy/y4uLv8hISD/JCQk/yUlJf8mJSb/JiUl/yUlJf8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAHR0dP9RUVH/UVBR/09PT/9OTk7/S0pK/0JCQv+VlZX/uLi4/7a1
tf+zs7P/sbGx/6+vr/+tra3/qaqq/6ampv+ioqP/n5+f/5ycnP+YmJj/lJSU/5CQj/+MjIz/iIiI/4OD
g/9/f3//e3t7/3d3dv9ycnL/cnJy/y4vL/8hISH/JCQk/yYmJf8mJib/JiYl/yYlJf8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHV1df9TU1T/U1NT/1BRUf9PTk7/TExM/0JC
Qv+Xl5f/vr6+/7q6uv+3uLf/tbW1/7Kysv+wsLD/rq2t/6uqrP+mp6b/oqKi/5+fn/+bm5v/l5aX/5KS
kv+Oj47/i4qK/4aGhv+BgoH/fX5+/3l5ef90dHT/c3Nz/y8vL/8jISP/JCQk/yYmJv8nJif/JiYm/yYm
Jv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHZ3dv9VVVX/VVVV/1NS
Uv9QUFD/TU1N/0RERP+ampr/wsLC/76/v/+8vLz/ubm6/7a2t/+zs7P/ra2t/6ampv+io6P/np6f/5ub
mv+Wl5f/k5OS/46Ojv+Njo3/jY2N/4mJiP+Eg4P/f39//3t7e/92d3b/dXV0/y8vL/8hISH/JCUl/yYm
Jv8nJyf/JyYm/yYnJv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHh3
eP9WVlb/VlZW/1RUVP9RUVH/Tk5O/0VERP+cnJz/xsbG/8LCw//AwMD/vb69/7q7uv+3t7f/sLCw/6mp
qf+mpqb/oqGi/52dnf+ZmZn/lZWV/5SUlP+Tk5P/j4+P/4uLi/+Ghob/gYGB/319ff94eHj/dnd2/y8v
L/8hIiP/JSUl/ycnJv8oJyj/KCcn/ycnJ/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAHl5ef9YWFj/V1dX/1ZWVv9SU1P/T09P/0ZGRv+fn5//ysvK/8bGxv/DxMT/wcHB/729
vv+6urr/s7Oz/6ysrP+oqKj/pKWk/6CgoP+bm5v/l5eX/5OTk/+SkpL/kJGQ/42Njf+IiIj/g4OD/35/
f/96env/eHh4/zAwL/8iIiL/JiYl/ycnJ/8oKCj/Jycn/ygnJ/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAHp7e/9ZWVr/WVlZ/1dXV/9UVFX/T1BP/0ZGRv+ioaL/z8/P/8rL
yv/HyMf/xMTE/8HBwf++vb7/tra2/6+vr/+qq6v/p6en/6Kiov+enp7/mZmZ/5WVlf+UlJT/k5KS/46O
jv+Kior/hYWF/4CAgP98fHz/eHl6/zAwMP8jIiP/JiYm/ygnKP8oKCr/KCgo/ycoKP8PDw//Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHx8fP9bW1r/Wlpa/1hYWP9VVlb/UFBQ/0dH
R/+kpKT/0tLS/87Ozv/Ly8v/x8fH/8PExP/AwMD/ubm5/7Kysf+tra3/qaio/6Wlpf+goKD/m5ub/5eW
l/+ZmZr/lJSU/5CQj/+Li4v/hoaG/4GBgf99fX3/e3t7/zAwMP8jIyP/JiYm/ygoJ/8pKSj/KSgo/ygo
KP8QEBD/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAH5+ff9cXFz/XFtc/1pZ
Wf9XV1b/UVFR/0hISP+lpaX/1tbW/9HR0f/Ozs7/y8rK/8bGxv/DwsL/uLi3/6ysrP+nqaf/o6Oi/56e
nv+ioqL/paWl/5+fn/+bmpv/lZaW/5GRkP+NjYz/iIiH/4KCgv9+fn7/fHx8/zEyMv8jIyP/Jicn/ygo
KP8pKSn/KCgp/ygpKP8RERH/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAH9/
f/9dXV3/XV1d/1paW/9YWFf/UlJS/0lJSf+kpKT/2tra/9TU1P/Q0ND/zc3N/8jJyf/ExMT/vb29/7W1
tv+xsbH/rKys/6ioqP+ioqT/nZ2e/5iYmP+YmJj/l5eX/5KSkv+Ojo3/iImI/4ODg/9/f3//fX19/zQ0
NP8jIyP/Jycn/ygoKP8pKSn/KSkp/ygoKP8TExP/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAICAgP9eXl7/Xl5e/1xcXP9ZWVn/U1NT/0lJSf+kpKT/3d3d/9fX1v/S0tL/z8/P/8vL
y//Gxsb/wsLC/72+vv+4uLn/tLS0/7CwsP+srKz/p6em/6Ghof+dnZ3/mJiY/5KTk/+Ojo7/iYmJ/4SE
hP9/gID/fn5+/zY2Nv8kIyT/Jycn/ygoKP8pKSn/KSkp/ykpKf8WFhb/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAIKCgv9fX17/Xl5e/11cXf9ZWln/VFRU/0pJSv+lpaX/4eHg/9na
2v/V1dT/0NDQ/83Mzf/Hx8f/w8PD/7++vv+6urr/tbW1/7CwsP+sra3/p6en/6Kiov+enZ3/mZmY/5SU
k/+Pj4//ioqK/4WFhf+AgID/fX19/zk5Of8jIyT/Jycn/ygoKf8pKSn/KSkp/ykpJ/8YFxj/DQ0NZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIOEhP9gYGD/X19f/15dXf9aWlr/VVVV/0tK
S/+lpaX/4eLi/9vb2//W1tX/0dHR/83Nzf/IyMj/w8PE/7+/v/+6u7r/tbW1/7Gxsf+tra3/qKio/6Kj
o/+enp7/mZmZ/5SUlP+Pj4//i4qK/4WFhf+AgID/fn59/zw8PP8kJCT/Jycn/ykpKf8qKin/KSkp/ygn
KP8ZGRr/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAISEhP9gYGD/X19f/15e
Xv9aWlv/VlZW/0tLS/+lpaX/4uLi/9zc3P/W19b/0dLR/83Ozv/JyMn/xMTE/7/AwP+7urv/trW2/7Gx
sf+tra3/qKin/6Ojov+enp7/mZmZ/5SUlP+Pj5D/i4qK/4aFhf+AgID/fn5+/z9AQP8lJSX/KCgn/ykp
Kf8qKin/KSkp/ygoKP8bGxv/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIWH
h/9gYGD/X19f/15eXv9aWlv/VlZX/0xMTP+mpqb/4eLh/9vb2//W1tb/0dHR/83Nzf/IyMn/xMTE/7+/
v/+7u7v/tba1/7Gxsf+tra3/qKio/6Ojo/+enp7/mZmZ/5SUlP+Pj4//ioqK/4WFhf+AgID/fX59/0JC
Qv8mJib/KCgo/ykpKf8qKir/KSkp/ygoKP8cHBz/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAIiIiP9gYGD/X19f/15eXv9bW1v/WFdX/05OTf+np6f/4eHh/9rZ2v/V1dX/0NDQ/83N
zf/IyMf/w8PD/76/v/+6urr/tbW1/7CxsP+sra3/p6in/6Kiov+enp7/mZmZ/5OUlP+Pj4//ioqK/4WE
hf+AgID/fn19/0ZGRv8mJyb/Kikq/yorKv8qKyr/KSkp/ygoKP8dHh7/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAImJif9gYGD/X19f/15eXv9bW1v/WFhY/1BQUP+pqan/6Ojo/9bW
1v/S09P/z8/P/8vLy//Gxsb/wsLC/76+vv+5ubn/tLS0/7CwsP+srKv/p6em/6Giov+dnZ3/mJiY/5OT
k/+Oj47/iYmJ/4SEhP9/f4D/e3t7/0pKS/8oKCj/Kysr/ywsLP8rKir/Kikq/yknJ/8fHx//DQ0NZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIuLi/9fX17/Xl5f/11dXf9bW1v/WVhY/1RU
VP9XV1j//////+Pj4//T09P/z8/P/8rKyv/Fx8f/wcHC/7++v/+5ubn/tbW1/7CwsP+sraz/pqao/6Ki
ov+enZ7/mZmZ/5STlP+QkJD/i4uL/4aGhv+BgYH/fHx8/zEwL/8rKiz/LCws/y0sLP8rKir/F4GW/ygm
J/8iIiL/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIyMjP9eXV3/Xl5e/11c
XP9bW1v/WFlZ/1ZWVf9OTk7/VldW/6enp/+lpaX/pKSk/6Kiov+goKD/nJyc/5iYmP+Tk5P/jo+P/4mJ
if+FhIX/gICA/3t7fP92dnb/cXJx/21tbf9nZmb/YmJi/11dXf9aWlr/MzMz/ywtLP8uLi3/LS0t/y0t
Lf8sKin/KScm/ycmJP8kJCT/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAI6O
jv9cXlz/Xl5e/1tbXP9aWlr/WFhY/1ZWV/9SU1P/Tk9P/0tLSv9HR0f/RERE/0JCQv9AQED/Pj8//zw8
Pf87Ozv/Ozs8/zo6Ov85OTn/ODg4/zY2Nv81NDX/NDM0/zMzM/8xMTH/MDAw/y8vL/8vLy//Li4u/y8v
L/8uLy7/Li0t/y0tLP8rKij/F4GW/ygmJ/8mJib/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAJCPkP9dW1v/XV1c/1tbW/9ZWVn/WFhY/1ZWVv9TVFP/UlJR/09PT/9OTk3/S0xL/0pK
Sv9ISEj/R0ZH/0VERf9CQkP/QUFC/0BAQP8/Pz//Pj4+/z09Pf87Ozv/OTk6/zg4OP82Njf/NTU1/zMz
M/8yMjL/MTEx/y8wL/8vLy//Ly0t/xeAlf8sKCf/KScm/ykmJf8nJyf/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAJKSkv9aWlr/W1tb/1paWv9ZWVj/V1dX/1ZVVv9UVFT/UlJR/1BP
UP9OTk7/TU1N/0xMTP9KSkr/SElI/0dHR/9GRkb/RERE/0JCQv9AQED/Pz8//z4+Pv89PT3/Ozs7/zk5
Of84ODj/Nzc2/zQ0NP8zMzP/MjIy/zAwMP8vLy//Lyws/y4qK/8tKCf/GI6q/ygnJv8pKCn/DQ0NZA4O
DlcNDQ0yCAgIDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk/9ZWFj/Wlpa/1lZWf9YWFj/V1dW/1VV
VP9TU1P/UVFR/1BPUP9PT0//TU5O/0xMTP9LSkv/SUlJ/0hISP9GRkb/RERE/0NCQv9BQUH/QD8//z4/
Pv89PT7/Ozw8/zo6Ov85OTn/Nzg3/zU1NP8zMzP/MTIy/zAwMP8vLy//Li0t/xeAlf8tJyj/KyYm/ygk
Jf8rKyv/DQ0NVw0NDUwNDQ0sCQkJDAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk/9XV1f/WVhY/1hY
V/9XV1f/VlZW/1VVVf9SUlL/UFFR/09PT/9OTk7/TU1N/0xMTP9LSkr/SUlJ/0dHR/9GRkb/REVE/0JC
Qv9BQUD/Pz8//z8/Pv89PT3/Ozw8/zo6Ov84ODj/Nzc2/zQ0Nf8zMzP/MTIy/zAwL/8vLy//Li0t/y0s
LP8rKSj/GI6q/yclJf8tLS3/DAwMMg0NDSwLCwsaCAgIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKS
kv9UVVX/VVVV/1RVVf9TU1P/UlJS/1FRUf9PT0//Tk5O/01NTf9MTEz/SktL/0tJSf9JSUn/SEhI/0ZG
R/9FRUX/Q0ND/0FBQf9APz//Pj4+/z0+Pf88PDz/Ojo6/zk5Of84ODj/NjY2/zM0M/8yMjH/MDAw/y8v
L/8uLi7/LS0t/y0tLf8rKSn/KSYm/yclJP8vLy//BwcHDgkJCQwICAgHAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAJaVlf+TkpL/kpKS/4+Qj/+NjY3/iYmJ/4eHhv+FhYX/goKD/3+Af/99fHz/enl5/3Z2
dv90dHT/cnJy/3Bwb/9ra2v/aGho/2VmZf9jZGP/YWFh/15eX/9cXFz/WFhY/1VUVf9TUlL/UFBQ/01N
Tf9LS0v/SEhI/0NEQ/9BQUH/Pz8//zw9PP85OTn/NjY2/zIyMv8xMTH/AAAAAQAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAf
AAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAf
AAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAP///////wAA////////
AAD///////8AAP///////wAA////////AAA=
</value>
</data>
</root>

View File

@@ -0,0 +1,178 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI;
public abstract class Gadget : IDisposable
{
private readonly GadgetWindow _window;
public event EventHandler VisibleChanged;
protected Gadget()
{
_window = new GadgetWindow();
_window.Paint += delegate (object sender, PaintEventArgs e)
{
OnPaint(e);
};
}
public virtual void Dispose()
{
_window.Dispose();
}
public Point Location
{
get
{
return _window.Location;
}
set
{
_window.Location = value;
}
}
public event EventHandler LocationChanged
{
add
{
_window.LocationChanged += value;
}
remove
{
_window.LocationChanged -= value;
}
}
public virtual Size Size
{
get
{
return _window.Size;
}
set
{
_window.Size = value;
}
}
public event EventHandler SizeChanged
{
add
{
_window.SizeChanged += value;
}
remove
{
_window.SizeChanged -= value;
}
}
public byte Opacity
{
get
{
return _window.Opacity;
}
set
{
_window.Opacity = value;
}
}
public bool LockPositionAndSize
{
get
{
return _window.LockPositionAndSize;
}
set
{
_window.LockPositionAndSize = value;
}
}
public bool AlwaysOnTop
{
get
{
return _window.AlwaysOnTop;
}
set
{
_window.AlwaysOnTop = value;
}
}
public ContextMenuStrip ContextMenuStrip
{
get
{
return _window.ContextMenuStrip;
}
set
{
_window.ContextMenuStrip = value;
}
}
public event HitTestEventHandler HitTest
{
add
{
_window.HitTest += value;
}
remove
{
_window.HitTest -= value;
}
}
public event MouseEventHandler MouseDoubleClick
{
add
{
_window.MouseDoubleClick += value;
}
remove
{
_window.MouseDoubleClick -= value;
}
}
public bool Visible
{
get
{
return _window.Visible;
}
set
{
if (value != _window.Visible)
{
_window.Visible = value;
VisibleChanged?.Invoke(this, EventArgs.Empty);
if (value)
Redraw();
}
}
}
public void Redraw()
{
_window.Redraw();
}
protected abstract void OnPaint(PaintEventArgs e);
}

View File

@@ -0,0 +1,574 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI;
public sealed class GadgetWindow : NativeWindow, IDisposable
{
private bool _visible;
private bool _alwaysOnTop;
private byte _opacity = 255;
private Point _location = new Point(100, 100);
private Size _size = new Size(130, 84);
private readonly MethodInfo _commandDispatch;
private IntPtr _handleBitmapDC;
private Size _bufferSize;
private Graphics _graphics;
public event EventHandler SizeChanged;
public event EventHandler LocationChanged;
public event HitTestEventHandler HitTest;
public event MouseEventHandler MouseDoubleClick;
public GadgetWindow()
{
Type commandType = typeof(Form).Assembly.GetType("System.Windows.Forms.Command");
_commandDispatch = commandType.GetMethod("DispatchID",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
null, new[] { typeof(int) }, null);
CreateHandle(CreateParams);
// move window to the bottom
MoveToBottom(Handle);
// prevent window from fading to a glass sheet when peek is invoked
try
{
bool value = true;
NativeMethods.DwmSetWindowAttribute(Handle, WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value, Marshal.SizeOf(true));
}
catch (DllNotFoundException) { }
catch (EntryPointNotFoundException) { }
CreateBuffer();
}
private void ShowDesktopChanged(bool showDesktop)
{
if (showDesktop)
MoveToTopMost(Handle);
else
MoveToBottom(Handle);
}
private void MoveToBottom(IntPtr handle)
{
NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
}
private void MoveToTopMost(IntPtr handle)
{
NativeMethods.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
}
private CreateParams CreateParams
{
get
{
CreateParams cp = new CreateParams
{
Width = 4096,
Height = 4096,
X = _location.X,
Y = _location.Y,
ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW
};
return cp;
}
}
protected override void WndProc(ref Message message)
{
switch (message.Msg)
{
case WM_COMMAND:
{
// need to dispatch the message for the context menu
if (message.LParam == IntPtr.Zero)
_commandDispatch.Invoke(null, new object[] {message.WParam.ToInt32() & 0xFFFF });
}
break;
case WM_NCHITTEST:
{
message.Result = (IntPtr)HitResult.Caption;
if (HitTest != null)
{
Point p = new Point(
Macros.GET_X_LPARAM(message.LParam) - _location.X,
Macros.GET_Y_LPARAM(message.LParam) - _location.Y
);
HitTestEventArgs e = new HitTestEventArgs(p, HitResult.Caption);
HitTest(this, e);
message.Result = (IntPtr)e.HitResult;
}
}
break;
case WM_NCLBUTTONDBLCLK:
{
MouseDoubleClick?.Invoke(this, new MouseEventArgs(MouseButtons.Left, 2, Macros.GET_X_LPARAM(message.LParam) - _location.X, Macros.GET_Y_LPARAM(message.LParam) - _location.Y, 0));
message.Result = IntPtr.Zero;
}
break;
case WM_NCRBUTTONDOWN:
{
message.Result = IntPtr.Zero;
}
break;
case WM_NCRBUTTONUP:
{
ContextMenuStrip?.Show(new Point(Macros.GET_X_LPARAM(message.LParam), Macros.GET_Y_LPARAM(message.LParam)));
message.Result = IntPtr.Zero;
}
break;
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS wp = (WINDOWPOS)Marshal.PtrToStructure(message.LParam, typeof(WINDOWPOS));
if (!LockPositionAndSize)
{
// prevent the window from leaving the screen
if ((wp.flags & SWP_NOMOVE) == 0)
{
Rectangle rect = Screen.GetWorkingArea(new Rectangle(wp.x, wp.y, wp.cx, wp.cy));
const int margin = 16;
wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
wp.x = Math.Min(wp.x, rect.Right - margin);
wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
wp.y = Math.Min(wp.y, rect.Bottom - margin);
}
// update location and fire event
if ((wp.flags & SWP_NOMOVE) == 0)
{
if (_location.X != wp.x || _location.Y != wp.y)
{
_location = new Point(wp.x, wp.y);
LocationChanged?.Invoke(this, EventArgs.Empty);
}
}
// update size and fire event
if ((wp.flags & SWP_NOSIZE) == 0)
{
if (_size.Width != wp.cx || _size.Height != wp.cy)
{
_size = new Size(wp.cx, wp.cy);
SizeChanged?.Invoke(this, EventArgs.Empty);
}
}
// update the size of the layered window
if ((wp.flags & SWP_NOSIZE) == 0)
NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero, ref _size, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, 0);
// update the position of the layered window
if ((wp.flags & SWP_NOMOVE) == 0)
NativeMethods.SetWindowPos(Handle, IntPtr.Zero, _location.X, _location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
}
// do not forward any move or size messages
wp.flags |= SWP_NOSIZE | SWP_NOMOVE;
// suppress any frame changed events
wp.flags &= ~SWP_FRAMECHANGED;
Marshal.StructureToPtr(wp, message.LParam, false);
message.Result = IntPtr.Zero;
}
break;
default:
{
base.WndProc(ref message);
}
break;
}
}
private BlendFunction CreateBlendFunction()
{
return new BlendFunction { BlendOp = AC_SRC_OVER, BlendFlags = 0, SourceConstantAlpha = _opacity, AlphaFormat = AC_SRC_ALPHA };
}
private void CreateBuffer()
{
IntPtr handleScreenDC = NativeMethods.GetDC(IntPtr.Zero);
_handleBitmapDC = NativeMethods.CreateCompatibleDC(handleScreenDC);
NativeMethods.ReleaseDC(IntPtr.Zero, handleScreenDC);
_bufferSize = _size;
BITMAPINFO info = new BITMAPINFO();
info.Size = Marshal.SizeOf(info);
info.Width = _size.Width;
info.Height = -_size.Height;
info.BitCount = 32;
info.Planes = 1;
IntPtr hBmp = NativeMethods.CreateDIBSection(_handleBitmapDC, ref info, 0, out IntPtr _, IntPtr.Zero, 0);
IntPtr hBmpOld = NativeMethods.SelectObject(_handleBitmapDC, hBmp);
NativeMethods.DeleteObject(hBmpOld);
_graphics = Graphics.FromHdc(_handleBitmapDC);
if (Environment.OSVersion.Version.Major > 5)
{
_graphics.TextRenderingHint = TextRenderingHint.SystemDefault;
_graphics.SmoothingMode = SmoothingMode.HighQuality;
}
}
private void DisposeBuffer()
{
_graphics.Dispose();
NativeMethods.DeleteDC(_handleBitmapDC);
}
public void Dispose()
{
DisposeBuffer();
}
public PaintEventHandler Paint;
public void Redraw()
{
if (!_visible || Paint == null)
return;
if (_size != _bufferSize)
{
DisposeBuffer();
CreateBuffer();
}
Paint(this, new PaintEventArgs(_graphics, new Rectangle(Point.Empty, _size)));
Point pointSource = Point.Empty;
BlendFunction blend = CreateBlendFunction();
NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero, ref _size, _handleBitmapDC, ref pointSource, 0, ref blend, ULW_ALPHA);
// make sure the window is at the right location
NativeMethods.SetWindowPos(Handle, IntPtr.Zero, _location.X, _location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
}
public byte Opacity
{
get
{
return _opacity;
}
set
{
if (_opacity != value)
{
_opacity = value;
BlendFunction blend = CreateBlendFunction();
NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blend, ULW_ALPHA);
}
}
}
public bool Visible
{
get
{
return _visible;
}
set
{
if (_visible != value)
{
_visible = value;
NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | (value ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
if (value)
{
if (!_alwaysOnTop)
ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
}
else
{
if (!_alwaysOnTop)
ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
}
}
}
}
// if locked, the window can not be moved or resized
public bool LockPositionAndSize { get; set; }
public bool AlwaysOnTop
{
get
{
return _alwaysOnTop;
}
set
{
if (value != _alwaysOnTop)
{
_alwaysOnTop = value;
if (_alwaysOnTop)
{
if (_visible)
ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
MoveToTopMost(Handle);
}
else
{
MoveToBottom(Handle);
if (_visible)
ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
}
}
}
}
public Size Size
{
get
{
return _size;
}
set
{
if (_size != value)
{
_size = value;
NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero, ref _size, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, 0);
SizeChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public Point Location
{
get
{
return _location;
}
set
{
if (_location != value)
{
_location = value;
NativeMethods.SetWindowPos(Handle, IntPtr.Zero, _location.X, _location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
LocationChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public ContextMenuStrip ContextMenuStrip { get; set; }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct BlendFunction
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct WINDOWPOS
{
public readonly IntPtr hwnd;
public readonly IntPtr hwndInsertAfter;
public int x;
public int y;
public readonly int cx;
public readonly int cy;
public uint flags;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
public int Size;
public int Width;
public int Height;
public short Planes;
public short BitCount;
public int Compression;
public int SizeImage;
public int XPelsPerMeter;
public int YPelsPerMeter;
public int ClrUsed;
public int ClrImportant;
public int Colors;
}
public static readonly IntPtr HWND_BOTTOM = (IntPtr)1;
public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
public const int WS_EX_LAYERED = 0x00080000;
public const int WS_EX_TOOLWINDOW = 0x00000080;
public const uint SWP_NOSIZE = 0x0001;
public const uint SWP_NOMOVE = 0x0002;
public const uint SWP_NOACTIVATE = 0x0010;
public const uint SWP_FRAMECHANGED = 0x0020;
public const uint SWP_HIDEWINDOW = 0x0080;
public const uint SWP_SHOWWINDOW = 0x0040;
public const uint SWP_NOZORDER = 0x0004;
public const uint SWP_NOSENDCHANGING = 0x0400;
public const int ULW_COLORKEY = 0x00000001;
public const int ULW_ALPHA = 0x00000002;
public const int ULW_OPAQUE = 0x00000004;
public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;
public const int WM_NCHITTEST = 0x0084;
public const int WM_NCLBUTTONDBLCLK = 0x00A3;
public const int WM_NCLBUTTONDOWN = 0x00A1;
public const int WM_NCLBUTTONUP = 0x00A2;
public const int WM_NCRBUTTONDOWN = 0x00A4;
public const int WM_NCRBUTTONUP = 0x00A5;
public const int WM_WINDOWPOSCHANGING = 0x0046;
public const int WM_COMMAND = 0x0111;
public const int TPM_RIGHTBUTTON = 0x0002;
public const int TPM_VERTICAL = 0x0040;
private enum WindowAttribute : int
{
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_LAST
}
/// <summary>
/// Some macros imported and converted from the Windows SDK
/// </summary>
private static class Macros
{
public static ushort LOWORD(IntPtr l)
{
return (ushort)((ulong)l & 0xFFFF);
}
public static ushort HIWORD(IntPtr l)
{
return (ushort)(((ulong)l >> 16) & 0xFFFF);
}
public static int GET_X_LPARAM(IntPtr lp)
{
return (short)LOWORD(lp);
}
public static int GET_Y_LPARAM(IntPtr lp)
{
return (short)HIWORD(lp);
}
}
/// <summary>
/// Imported native methods
/// </summary>
private static class NativeMethods
{
private const string USER = "user32.dll";
private const string GDI = "gdi32.dll";
private const string DWMAPI = "dwmapi.dll";
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, IntPtr pptDst, ref Size psize, IntPtr hdcSrc, IntPtr pprSrc, int crKey, IntPtr pblend, int dwFlags);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, IntPtr pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, int crKey, ref BlendFunction pblend, int dwFlags);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, IntPtr pptDst, IntPtr psize, IntPtr hdcSrc, IntPtr pprSrc, int crKey, ref BlendFunction pblend, int dwFlags);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern bool TrackPopupMenuEx(IntPtr hMenu, uint uFlags, int x, int y, IntPtr hWnd, IntPtr tpmParams);
[DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, uint pila, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);
}
}
public delegate void HitTestEventHandler(object sender, HitTestEventArgs e);
public enum HitResult
{
Transparent = -1,
Nowhere = 0,
Client = 1,
Caption = 2,
Left = 10,
Right = 11,
Top = 12,
TopLeft = 13,
TopRight = 14,
Bottom = 15,
BottomLeft = 16,
BottomRight = 17,
Border = 18
}
public class HitTestEventArgs : EventArgs
{
public HitTestEventArgs(Point location, HitResult hitResult)
{
Location = location;
HitResult = hitResult;
}
public Point Location { get; }
public HitResult HitResult { get; set; }
}

View File

@@ -0,0 +1,157 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
using System;
using System.Collections.Generic;
using System.Text;
namespace LibreHardwareMonitor.UI;
public class HardwareNode : Node, IExpandPersistNode
{
private readonly PersistentSettings _settings;
private readonly UnitManager _unitManager;
private readonly List<TypeNode> _typeNodes = new List<TypeNode>();
private readonly string _expandedIdentifier;
private bool _expanded;
public event EventHandler PlotSelectionChanged;
public HardwareNode(IHardware hardware, PersistentSettings settings, UnitManager unitManager)
{
_settings = settings;
_unitManager = unitManager;
_expandedIdentifier = new Identifier(hardware.Identifier, "expanded").ToString();
Hardware = hardware;
Image = HardwareTypeImage.Instance.GetImage(hardware.HardwareType);
foreach (SensorType sensorType in Enum.GetValues(typeof(SensorType)))
_typeNodes.Add(new TypeNode(sensorType, hardware.Identifier, _settings));
foreach (ISensor sensor in hardware.Sensors)
SensorAdded(sensor);
hardware.SensorAdded += SensorAdded;
hardware.SensorRemoved += SensorRemoved;
_expanded = settings.GetValue(_expandedIdentifier, true);
}
public override string Text
{
get { return Hardware.Name; }
set { Hardware.Name = value; }
}
public override string ToolTip
{
get
{
IDictionary<string, string> properties = Hardware.Properties;
if (properties.Count > 0)
{
StringBuilder stringBuilder = new();
stringBuilder.AppendLine("Hardware properties:");
foreach (KeyValuePair<string, string> property in properties)
stringBuilder.AppendFormat(" • {0}: {1}\n", property.Key, property.Value);
return stringBuilder.ToString();
}
return null;
}
}
public IHardware Hardware { get; }
public bool Expanded
{
get => _expanded;
set
{
_expanded = value;
_settings.SetValue(_expandedIdentifier, _expanded);
}
}
private void UpdateNode(TypeNode node)
{
if (node.Nodes.Count > 0)
{
if (!Nodes.Contains(node))
{
int i = 0;
while (i < Nodes.Count && ((TypeNode)Nodes[i]).SensorType < node.SensorType)
i++;
Nodes.Insert(i, node);
}
}
else
{
if (Nodes.Contains(node))
Nodes.Remove(node);
}
}
private void SensorRemoved(ISensor sensor)
{
foreach (TypeNode typeNode in _typeNodes)
{
if (typeNode.SensorType == sensor.SensorType)
{
SensorNode sensorNode = null;
foreach (Node node in typeNode.Nodes)
{
if (node is SensorNode n && n.Sensor == sensor)
sensorNode = n;
}
if (sensorNode != null)
{
sensorNode.PlotSelectionChanged -= SensorPlotSelectionChanged;
typeNode.Nodes.Remove(sensorNode);
UpdateNode(typeNode);
}
}
}
PlotSelectionChanged?.Invoke(this, null);
}
private void InsertSorted(Node node, ISensor sensor)
{
int i = 0;
while (i < node.Nodes.Count && ((SensorNode)node.Nodes[i]).Sensor.Index < sensor.Index)
i++;
SensorNode sensorNode = new SensorNode(sensor, _settings, _unitManager);
sensorNode.PlotSelectionChanged += SensorPlotSelectionChanged;
node.Nodes.Insert(i, sensorNode);
}
private void SensorPlotSelectionChanged(object sender, EventArgs e)
{
PlotSelectionChanged?.Invoke(this, null);
}
private void SensorAdded(ISensor sensor)
{
foreach (TypeNode typeNode in _typeNodes)
{
if (typeNode.SensorType == sensor.SensorType)
{
InsertSorted(typeNode, sensor);
UpdateNode(typeNode);
}
}
PlotSelectionChanged?.Invoke(this, null);
}
}

View File

@@ -0,0 +1,73 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System.Drawing;
using System.Collections.Generic;
using LibreHardwareMonitor.Hardware;
namespace LibreHardwareMonitor.UI;
public class HardwareTypeImage
{
private readonly IDictionary<HardwareType, Image> _images = new Dictionary<HardwareType, Image>();
private HardwareTypeImage() { }
public static HardwareTypeImage Instance { get; } = new HardwareTypeImage();
public Image GetImage(HardwareType hardwareType)
{
if (_images.TryGetValue(hardwareType, out Image image))
return image;
switch (hardwareType)
{
case HardwareType.Cpu:
image = Utilities.EmbeddedResources.GetImage("cpu.png");
break;
case HardwareType.GpuNvidia:
image = Utilities.EmbeddedResources.GetImage("nvidia.png");
break;
case HardwareType.GpuAmd:
image = Utilities.EmbeddedResources.GetImage("amd.png");
break;
case HardwareType.GpuIntel:
image = Utilities.EmbeddedResources.GetImage("intel.png");
break;
case HardwareType.Storage:
image = Utilities.EmbeddedResources.GetImage("hdd.png");
break;
case HardwareType.Motherboard:
image = Utilities.EmbeddedResources.GetImage("mainboard.png");
break;
case HardwareType.SuperIO:
case HardwareType.EmbeddedController:
image = Utilities.EmbeddedResources.GetImage("chip.png");
break;
case HardwareType.Memory:
image = Utilities.EmbeddedResources.GetImage("ram.png");
break;
case HardwareType.Network:
image = Utilities.EmbeddedResources.GetImage("nic.png");
break;
case HardwareType.Cooler:
image = Utilities.EmbeddedResources.GetImage("fan.png");
break;
case HardwareType.Psu:
image = Utilities.EmbeddedResources.GetImage("power-supply.png");
break;
case HardwareType.Battery:
image = Utilities.EmbeddedResources.GetImage("battery.png");
break;
default:
image = new Bitmap(1, 1);
break;
}
_images.Add(hardwareType, image);
return image;
}
}

View File

@@ -0,0 +1,6 @@
namespace LibreHardwareMonitor.UI;
public interface IExpandPersistNode
{
bool Expanded { get; set; }
}

View File

@@ -0,0 +1,221 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
namespace LibreHardwareMonitor.UI
{
partial class InterfacePortForm
{
/// <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()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InterfacePortForm));
this.portOKButton = new System.Windows.Forms.Button();
this.portCancelButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.webServerLinkLabel = new System.Windows.Forms.LinkLabel();
this.portNumericUpDn = new System.Windows.Forms.NumericUpDown();
this.label5 = new System.Windows.Forms.Label();
this.interfaceLabel = new System.Windows.Forms.Label();
this.interfaceComboBox = new System.Windows.Forms.ComboBox();
((System.ComponentModel.ISupportInitialize)(this.portNumericUpDn)).BeginInit();
this.SuspendLayout();
//
// portOKButton
//
this.portOKButton.Location = new System.Drawing.Point(244, 162);
this.portOKButton.Name = "portOKButton";
this.portOKButton.Size = new System.Drawing.Size(75, 23);
this.portOKButton.TabIndex = 0;
this.portOKButton.Text = "OK";
this.portOKButton.UseVisualStyleBackColor = true;
this.portOKButton.Click += new System.EventHandler(this.PortOKButton_Click);
//
// portCancelButton
//
this.portCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.portCancelButton.Location = new System.Drawing.Point(162, 162);
this.portCancelButton.Name = "portCancelButton";
this.portCancelButton.Size = new System.Drawing.Size(75, 23);
this.portCancelButton.TabIndex = 1;
this.portCancelButton.Text = "Cancel";
this.portCancelButton.UseVisualStyleBackColor = true;
this.portCancelButton.Click += new System.EventHandler(this.PortCancelButton_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 131);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(377, 13);
this.label1.TabIndex = 3;
this.label1.Text = "Note: You will need to open the port in firewall settings of the operating system" +
".";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(13, 34);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(190, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Port number for the remote web server:";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(13, 64);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(443, 13);
this.label3.TabIndex = 5;
this.label3.Text = "If the web server is running then it will need to be restarted for the port chang" +
"e to take effect.";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(13, 86);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(262, 13);
this.label4.TabIndex = 6;
this.label4.Text = "The web server will be accessible from the browser at ";
//
// webServerLinkLabel
//
this.webServerLinkLabel.AutoSize = true;
this.webServerLinkLabel.Location = new System.Drawing.Point(269, 86);
this.webServerLinkLabel.Name = "webServerLinkLabel";
this.webServerLinkLabel.Size = new System.Drawing.Size(55, 13);
this.webServerLinkLabel.TabIndex = 7;
this.webServerLinkLabel.TabStop = true;
this.webServerLinkLabel.Text = "linkLabel1";
this.webServerLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.WebServerLinkLabel_LinkClicked);
//
// portNumericUpDn
//
this.portNumericUpDn.Location = new System.Drawing.Point(208, 32);
this.portNumericUpDn.Maximum = new decimal(new int[] {
20000,
0,
0,
0});
this.portNumericUpDn.Minimum = new decimal(new int[] {
8080,
0,
0,
0});
this.portNumericUpDn.Name = "portNumericUpDn";
this.portNumericUpDn.Size = new System.Drawing.Size(75, 20);
this.portNumericUpDn.TabIndex = 8;
this.portNumericUpDn.Value = new decimal(new int[] {
8080,
0,
0,
0});
this.portNumericUpDn.ValueChanged += new System.EventHandler(this.PortNumericUpDn_ValueChanged);
this.portNumericUpDn.KeyUp += new System.Windows.Forms.KeyEventHandler(this.PortNumericUpDn_KeyUp);
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(13, 109);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(304, 13);
this.label5.TabIndex = 9;
this.label5.Text = "You will have to start the server by clicking Run from the menu.";
//
// interfaceLabel
//
this.interfaceLabel.AutoSize = true;
this.interfaceLabel.Location = new System.Drawing.Point(14, 8);
this.interfaceLabel.Name = "interfaceLabel";
this.interfaceLabel.Size = new System.Drawing.Size(217, 13);
this.interfaceLabel.TabIndex = 10;
this.interfaceLabel.Text = "Network interface for the remote web server:";
//
// interfaceComboBox
//
this.interfaceComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.interfaceComboBox.FormattingEnabled = true;
this.interfaceComboBox.Location = new System.Drawing.Point(233, 6);
this.interfaceComboBox.Margin = new System.Windows.Forms.Padding(2);
this.interfaceComboBox.Name = "interfaceComboBox";
this.interfaceComboBox.Size = new System.Drawing.Size(221, 21);
this.interfaceComboBox.TabIndex = 11;
this.interfaceComboBox.SelectedIndexChanged += new System.EventHandler(this.interfaceComboBox_SelectedIndexChanged);
//
// InterfacePortForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.portCancelButton;
this.ClientSize = new System.Drawing.Size(466, 202);
this.Controls.Add(this.interfaceComboBox);
this.Controls.Add(this.interfaceLabel);
this.Controls.Add(this.label5);
this.Controls.Add(this.portNumericUpDn);
this.Controls.Add(this.webServerLinkLabel);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.portCancelButton);
this.Controls.Add(this.portOKButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "InterfacePortForm";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Set Port";
this.Load += new System.EventHandler(this.PortForm_Load);
((System.ComponentModel.ISupportInitialize)(this.portNumericUpDn)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button portOKButton;
private System.Windows.Forms.Button portCancelButton;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.LinkLabel webServerLinkLabel;
private System.Windows.Forms.NumericUpDown portNumericUpDn;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label interfaceLabel;
private System.Windows.Forms.ComboBox interfaceComboBox;
}
}

View File

@@ -0,0 +1,105 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.Net.NetworkInformation;
namespace LibreHardwareMonitor.UI;
public partial class InterfacePortForm : Form
{
private readonly MainForm _parent;
private string _localIP;
public InterfacePortForm(MainForm m)
{
InitializeComponent();
_parent = m;
_localIP = LoadNetworkInterfaces(_parent.Server.ListenerIp);
}
private string LoadNetworkInterfaces(string selectedListenerIp)
{
IPHostEntry host;
interfaceComboBox.Items.Clear();
host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
interfaceComboBox.Items.Add(ip.ToString());
}
// select the last one by default to match the existing behavior
if (interfaceComboBox.Items.Count > 0)
{
interfaceComboBox.SelectedIndex = interfaceComboBox.Items.Count - 1;
} else
{
// default to ? just like previous version
interfaceComboBox.Items.Add("?");
interfaceComboBox.SelectedIndex = 0;
}
// check to see if the selected listener IP is in our list.
if (interfaceComboBox.Items.Contains(selectedListenerIp))
{
// default it to the previously selected IP.
interfaceComboBox.SelectedItem = selectedListenerIp;
}
return interfaceComboBox.SelectedItem as string;
}
private void PortNumericUpDn_ValueChanged(object sender, EventArgs e)
{
string url = "http://" + _localIP + ":" + portNumericUpDn.Value + "/";
webServerLinkLabel.Text = url;
webServerLinkLabel.Links.Remove(webServerLinkLabel.Links[0]);
webServerLinkLabel.Links.Add(0, webServerLinkLabel.Text.Length, url);
}
private void PortOKButton_Click(object sender, EventArgs e)
{
_parent.Server.ListenerPort = (int)portNumericUpDn.Value;
_parent.Server.ListenerIp = _localIP;
Close();
}
private void PortCancelButton_Click(object sender, EventArgs e)
{
Close();
}
private void PortForm_Load(object sender, EventArgs e)
{
interfaceComboBox.SelectedValue = _parent.Server.ListenerIp;
portNumericUpDn.Value = _parent.Server.ListenerPort;
PortNumericUpDn_ValueChanged(null, null);
}
private void WebServerLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo(e.Link.LinkData.ToString()));
}
catch { }
}
private void PortNumericUpDn_KeyUp(object sender, KeyEventArgs e)
{
PortNumericUpDn_ValueChanged(null, null);
}
private void interfaceComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
_localIP = interfaceComboBox.SelectedItem as string;
PortNumericUpDn_ValueChanged(null, null);
}
}

View File

@@ -0,0 +1,377 @@
<?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.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAAAwMAAAAQAgAKglAABGFQAAKAAAABAA
AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEU8OykuDgdIIxQSURwdHU8ZGhpPFhYWTxQU
FE8SEhJPDxAPTw0KCk8KBQRPCAQDTwcDAU8HAQBPBgQEUQUFBTxLVVb/L215/zdXXP89Ozv/ODc3/zM0
M/8vLy//Kioq/yYlJf8iJif/Gycp/xggI/8UHiD/Eh4g/w4SE/8GBARRQWRr/xGXtP8yUln/NDIx/y4u
Lf8qKir/KSgo/ycnJ/8lJCP/Iikq/xs4Pf8aOD7/GzpB/xo4Pf8SGhv/BwMDT01aXP46Ulj+Pzc0/kdH
R/5MTU3+Q0ND/jw8PP43Nzf+Ly8v/igoKP4lKCn+Iico/hwiI/4cHyD/FBMU/wYHBk9XVVX+Rj8+/klJ
Sf6ioqL+rq+u/p+fn/6YmJj+j4+P/oSEhP56enr+dXJy/mNgYP4iHx7+Gxka/xUVFf8HBwdPW1pa/kVF
RP5YWFj+rq6u/r6+vv6wsK/+qamp/qCgoP6UlJT+iYmJ/oGBgf5xcXH+JCQk/hsbG/8WFhb/BwcHT15e
Xv5ISEj+Wlpa/rW1tf7Gxsb+tra2/qysq/6hoaL+lpaW/oyMjP6EhIT+cnJy/iQkJP4cHBz/GBgY/wcH
B09jY2P+TUxM/l5eXv7AwMD+0dHR/r6+vv6vr6/+o6Ok/pmYmf6Rk5P+iouK/nd4d/4lJSX+HR0d/xkZ
Gf8HBwdPZ2dn/lBQUP5iYmL+ysrK/tzc3P7Hxsf+tbS1/qmpqf6fn5/+mJiY/o6Pj/57e3z+JSUm/h4e
Hv8aGhr/CAgIT2tra/5TU1P+ZGRk/tLS0v7j5eP+zMzM/ry8vP6wsLD+pqam/pubm/6TkZH+f39//igo
KP4fHx7/HBwc/wkJCk9ubm7+VlVW/mZlZv7X1tf+6Ojo/tPS0/7Hx8f+urq6/qurq/6dnp7+lJSU/oGB
gP4sLCz+Hx4e/x4dHP8LCwtPb29v/lZWVv5nZ2j+4eHh/vLx8v7Z2dn+zc3N/sDAwP6wsrL+pKOk/pmZ
mf6Ghob+MDAw/h8gH/8eHx//DQsNT29vb/5YWFj+WVpa/sHBwf7V1dX+wMC//rW1tP6pqan+m5ub/o6P
jv6FhYX+cXFx/i4qKv4gLC7/Hiot/w4JB09vb3D+Wlla/lJSUf5VVVX+YGBg/ltbWv5VVVP+Tk5O/kdH
R/5BQUH+Ozs6/jAvL/4oKCj+Izk9/x8tL/8QCQhRbm5u/lVVVf5SUlL+S0tL/kRERP5BQUH+PT09/jk5
Of41NTX+MzIy/i8vLv4sKSj+Jy4v/h9DTP8iLjH/Ew0KSHp7ev5qamr+ZWVl/mFhYv5cXFz+WFhY/lNS
U/5NTU3+R0ZH/kJCQv49PDz+NzU1/jAxMf4qMTL/Jigp/x4cHCn//wAAAAEAAAABAAAAAQAAAAEAAAAB
AAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAKAAAACAAAABAAAAAAQAgAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMBBwcHAwYGBgQGBgYEBgYGBAYG
BgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYG
BgQGBgYEBgYGBAYGBgQGBgYEBwcHAwUFBQIAAAAAAAAAAAAAAAAAAAAAAAAAAAoKCgkMDAwgDQ0NKg0N
DSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0N
DSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoMDAwlCgoKEQUFBQIAAAAAAAAAAAAAAAAAAAAACAgIFAoK
CkcMDAxbDAwMXAwMDFwMDAxcDAwMXAwNDFwNDQ1cDQ0NXA0NDVwNDQ1cDQ0NXA0NDVwNDQ5cDg4OXA4O
DlwODg5cDg4OXA4ODlwODg5cDg4OXA4ODlwODg5cDg4OWw0NDVIMDAwlBwcHAwAAAAAAAAAAAAAAAGFh
YY9ZWFjFUVBP0kpJSdhIR0fYQkJC2D0+Ptg5OTnZODc22TMyM9kuLi/ZKysr2SYmJtkjIyPZICAf2Rsb
G9kXFxjZExMT2Q8ODtkNDAzZDQwM2A0MDNgNDAzYDQ0N2A0NDdgODQ27Dg4OWw0NDSoGBgYEAAAAAAAA
AAAAAAAAYmFhvzxCQ/82Uln/NlJZ/zdPVP89PDz/PDw8/zo6Ov85OTn/ODg4/zY2Nv80NDT/NDQ0/zIy
Mv8xMTD/Ly4w/yw1OP8qNjj/JzQ1/yYyNP8kMDL/Ii4w/x8sLv8fKy3/HyIi/w0NDdgODg5cDQ0NKgYG
BgQAAAAAAAAAAAAAAABjY2K/OFRa/xqXsv8bk6v/M11l/z49Pf89PT3/Ozs7/zo6Of83Nzj/NTY1/zQ0
NP8zMzP/MTEw/y8vL/8uLi7/KzQ2/yk1Nv8nMjT/JjAz/yQvMf8jLjD/ISwu/x8rLf8fISL/DQ0N2A4O
DlwNDQ0qBgYGBAAAAAAAAAAAAAAAAGZkZL86V13/GpKs/zJkbv9BPz//Pj8+/zw8PP85OTn/Njc2/zQ0
Nf8zMjP/MjIy/zAwMP8vLy//Li4u/y0sLP8qKSn/I0hQ/yJHUP8hRk//IEZO/yBGTv8fRU3/HztB/yAf
H/8NDQ3YDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAaWdovz5WW/83YWr/RkND/0FAQP89PT3/ODc3/zIy
Mf8uLi7/LCws/ysrK/8pKSn/Kioq/ygoKP8nJyf/JSUl/yUkJP8kIyP/IyEh/yMiIv8kIiL/JCMj/yQj
I/8iISH/ISAg/w0NDdgODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAABqamq/SUhI/0hHR/9GRkb/QUFB/0ZH
Rv9/f4D/gICA/3h4eP9xcXH/a2tr/2RkZP9dXV7/V1dX/09PT/9ISEj/Q0NC/0BAQP9AQED/Ozs7/yYm
Jv8jIyP/JCQk/yMjI/8jIyH/DQ0N2A4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAG5ubr9LS0v/SkpL/0hI
SP9BQUH/i4uL/6ysrP+hoqH/n5+e/52bnf+YmJj/lJSU/5CQj/+Li4v/hYaF/4CAgP97e3r/dXR1/3Fx
cf9ra2v/Ozs7/yEhIv8kJCT/JCQk/yQkI/8NDQ3YDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAcHBwv01N
Tf9MTU3/SUpK/0BAP/+Wlpj/q6ur/6inp/+kpKT/oKGg/52cnP+YmJj/k5OT/46Ojv+IiYj/g4OC/319
ff93d3f/cXFx/3Fxcf8/Pz//ISEh/yQkJP8lJSX/JSQk/w0NDdkODg5cDQ0NKgYGBgQAAAAAAAAAAAAA
AABycnO/T05P/05PT/9MTEz/QUFB/5ubmv+ysrL/rq6u/6urq/+np6f/oqKi/52dnf+ZmZn/k5OT/42O
jv+IiIf/gYGB/3x7fP90dXT/cXFx/z8/P/8hISH/JCQl/yUlJf8lJib/DQ0N2Q4ODlwNDQ0qBgYGBAAA
AAAAAAAAAAAAAHR1db9RUVH/UFBQ/01NTf9CQkL/np6e/7i4uP+0s7T/sbGx/62trf+oqKj/o6Oj/52d
nf+Yl5f/kZKR/4yMi/+FhYX/fn9//3h4eP9zc3P/Pz8//yIiIv8lJSX/JiYm/yYmJv8NDQ3ZDg4OXA0N
DSoGBgYEAAAAAAAAAAAAAAAAdnZ2v1RUVP9TU1P/T09P/0VFRf+kpKT/v7+//7u7u/+2trb/sbGx/6mp
qf+jo6P/nZ2d/5eXl/+QkZH/jo2N/4mJif+CgoL/fHx8/3d3d/9AQED/IiIi/yYmJv8nJyf/Jycn/w0N
DdkODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAAB4eHi/V1dW/1VVVf9QUFD/RkZG/6moqf/Gxsb/wcHB/7y8
vP+2trb/q6ys/6Wlpf+fn5//mZiY/5WUlf+RkpL/jI2M/4WFhf9/fn//enp6/0FBQf8iIiP/JiYm/ygn
KP8oJyf/DQ0N2Q4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAHp6er9YWVn/V1dX/1JSU/9ISEj/ra2t/8zM
zP/Gxsb/wcHB/7u7u/+wsLD/qamp/6Ojo/+cnZ3/lpaW/5OTk/+Pj47/iIiI/4CBgP98fH3/QkJC/yMj
I/8nJyf/KCgo/ygoKP8ODg7ZDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAfXx9v1tbW/9ZWVn/VFVV/0hJ
Sf+xsbH/0tLS/8vLy//FxcX/v7++/7Ozs/+qqqr/pKWk/6Cfn/+ampr/mpia/5GRkf+Li4r/goOD/35+
fv9CQ0P/IyQk/ycnJ/8pKSn/KSkp/xAQENkODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAAB+fn6/XV1d/1tb
W/9WVlb/S0lJ/7Oysv/W1tb/z8/P/8nJyf/CwsL/tbW1/66trf+np6f/oqKi/5ycnP+ZmJn/k5OT/4yM
jP+FhYX/f39//0ZFRv8kJCT/KCgo/ykpKf8pKSn/EhIS2Q4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAIGB
gb9eXl7/XFxc/1dYV/9KSkr/tLS0/9rb2v/S0tL/zczM/8XFxf+/v7//uLi4/7Gxsf+rq6r/o6Oj/5yc
nP+UlZT/jY2N/4aFhv+AgID/SUlI/yMjI/8oKCj/KSkp/ykpKf8UFBTZDg4OXA0NDSoGBgYEAAAAAAAA
AAAAAAAAg4ODv19fX/9eXl7/WVhZ/0tLS/+0tLT/3d3d/9TU1P/Ozs7/x8bH/8DAwP+5ubn/srKy/6ur
q/+kpKT/nJyc/5WVlv+Ojo7/h4eH/4GBgf9NTU3/IyQk/ygpKP8qKSn/KSkp/xgYGNkODg5cDQ0NKgYG
BgQAAAAAAAAAAAAAAACFhYW/YGBf/15eXv9aWln/TU1N/7a2tv/e3t7/1dXV/87Pz//Hx8f/wcHB/7m5
uv+ysrP/rKyr/6SkpP+dnZ3/lZWW/46Ojv+Hh4f/gYGB/1BQUP8kJCT/KSkp/ykpKf8oKCj/GhkZ2Q4O
DlwNDQ0qBgYGBAAAAAAAAAAAAAAAAIiGhr9gYGD/Xl5e/1paWv9PT0//t7e3/93d3f/U1NT/zs7O/8fH
x//AwMD/ubm5/7Kysv+rq6v/o6Ok/5ycnP+VlZX/jo6N/4aGhv+BgYH/U1NT/yYmJv8qKir/Kisq/ygo
Kf8bHBzYDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAioqKv19fX/9eXl7/Wlta/1FRUf+srKz/4uLi/9LS
0v/Mzcz/xcXG/7+/v/+4ubj/srKx/6urq/+kpKT/nZ2d/5WVlf+Njo7/h4aH/4GBgf9TUlL/KSkp/yws
LP8qLzH/Jy4v/x4eHtgNDQ1cDQ0NKgYGBgQAAAAAAAAAAAAAAACMi4y/Xl5e/11dXf9aWlr/VVVV/1xc
XP+qqqr/sbGx/66urv+qqqr/pKSj/52dnf+VlZX/jo6O/4eHh/+Af3//d3l3/3BwcP9paWn/XFxc/zMz
M/8sLCz/LS0t/yswMf8nLS7/ICAg2A0NDVwNDQ0qBgYGBAAAAAAAAAAAAAAAAI6Ojr9cXFz/XFxc/1pa
Wv9WV1f/UFFQ/0xMS/9HR0f/RERE/0BBQP8+Pj7/PDw8/zo7Ov84ODj/ODg4/zU1Nf8zMzP/MTEx/zAw
MP8uLi//Li4u/y4tL/8qPUD/KDk+/yU4O/8jIiLYDQ0NXA0NDSoGBgYEAAAAAAAAAAAAAAAAkZGRv1pb
W/9bW1r/WFlY/1ZWVv9UVFT/UFBQ/05OTv9MTEz/SklK/0dHR/9FRUX/QkJC/0BAQP8+Pj7/PDw8/zo6
Ov83ODf/NTU1/zIyMv8xMTH/Ly4u/yk7P/8oO0D/JTpA/yUkJNgNDQ1bDQ0NKgcHBwQAAAAAAAAAAAAA
AACTk5O/WFhY/1lZWf9YV1j/VVVV/1JSUv9QUFD/Tk5O/01MTf9KSkr/SEhI/0ZGRv9DQkP/QEFA/z8/
P/89PT3/Ozs6/zg4OP82NTX/MzMz/zExMf8vLi3/IVtn/yovMf8nLi//KCgo0gwMDEcMDAwgBwcHAwAA
AAAAAAAAAAAAAJKTkr9VVFX/VFVV/1RUVP9SUVH/T09P/01NTv9MS0z/SkpK/0hJSP9HR0f/REVE/0FB
Qf8/Pz//Pj4+/zs7PP85Ojn/Nzc3/zQ0NP8xMTH/LzAw/y4uLv8tLCz/KS8w/yUtLv8tLS3FCgoKFAoK
CgkDAwMBAAAAAAAAAAAAAAAAl5aXj5KSkr+QkJC/jYuLv4eHh7+EhIS/gICAv3x8fL93d3e/dHR0v3Bw
cL9samy/ZmZmv2NjY79fX1+/W1tbv1VVVb9SUlK/Tk5Ov0pKSr9ERES/QUFBvz09Pb85OTm/MzMzvzEx
MY8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////gAAAH4AAAB+AA
AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AA
AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB////////////////ygAAAAwAAAAYAAAAAEA
IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAACCAgIBwkJCQwICAgOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcH
Bw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcH
Bw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOCAgIDgkJCQwICAgHAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAICAgHCwsLGQ0NDSwNDQ0yDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0N
DTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0N
DTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMg0NDSwLCwsaCAgIBwAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJCQkMDQ0NLA0NDUwODg5XDg4OWQ4ODlkODg5ZDg4OWQ4O
DlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4O
DlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OVw0N
DUwNDQ0sCQkJDAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFBQUOCgoKMgsLC1cLCwtkCwsLZgsL
C2YLCwtmCwsLZgwMDGYMDAxmDAwMZgwMDGYMDAxmDAwMZgwMDGYMDAxmDAwMZg0NDWYNDQ1mDQ0NZg0N
DWYNDQ1mDQ0NZg0NDWYNDQ1mDg4OZg4ODmYODg5mDg4OZg4ODmYODg5mDg4OZg4ODmYODg5mDg4OZg4O
DmYODg5mDg4OZA4ODlcNDQ0yCAgIDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGJiYv9cXFz/Wlpa/1VV
V/9TU1L/UVFR/05OTv9LSkv/R0dH/0NDQ/9BQUH/Pz4//zw8PP86OTn/NTU1/zIyMv8wMDD/LS0u/yoq
Kv8mJib/JCMk/yEhIf8eHh7/Gxsc/xkZGf8UFBT/ERER/w8PD/8ODg7/Dg4O/w4ODv8ODg7/Dg4O/w4O
Dv8ODg7/Dg4O/w4ODv8PDw//Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGJi
Yf8+PDz/Pzw7/0A7Ov9AOzr/Pzo5/z06Ov89Ojr/Ozs7/zo7Ov87Ojv/Ojo6/zk5Of84ODj/Nzc3/zY2
Nf81NTT/MzMz/zIyMv8xMTH/MTIx/zAwMP8wMDD/Ly4u/y4tLf8sKiv/Kigo/yknJ/8oJiX/JiQk/yUj
I/8kIiH/IiEg/yAeHv8gHh3/IB0d/x8eHv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAGNjY/9BPj3/Gpiz/xuWsP8alrD/Gpey/yKClv8+PT3/PT09/zw8PP87Ozv/Ojo6/zk5
Of84ODj/Nzc3/zY2Nv80NDT/MzMz/zMyMv8yMTH/MDEw/y8vL/8vLy//Li4t/yJWYf8hVF//IVRf/x9T
Xf8gU13/H1Nc/x9RXP8eUFv/HlFb/x1QWv8cT1n/HFBb/x8eHv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAGRkZP9CPTz/GpWv/xySq/8blK3/JH+S/0A8O/8/Pj7/Pj0+/z08
PP87Ozv/Ojo6/zk5Of84ODj/Njc3/zU1Nf80NDT/MzMz/zIyMv8xMTH/MDAv/y8vL/8uLi7/LS0s/ywq
Kv8tKSj/KiYm/yklJf8nJCP/KCIi/yYiIf8lICD/JB8e/yMeHP8iHRz/Hx0d/x8eH/8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGVlZf9GQUD/Gpaw/xuUrf8kgJP/Qj49/0A/
P/8/Pj//Pj4+/zw8PP86Ojr/OTk5/zc3N/82NTb/NDQ0/zMzM/8yMjL/MTEx/zAwMP8vLzD/Ly8v/y4u
Lv8tLS3/LCws/ysrKv8gVV//H1Nd/x9UXf8gU13/H1Nc/x9RXP8fUVz/HlFb/x5QW/8dUFv/Hx4f/x8f
IP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGZmZv9GQkL/GZex/yWB
k/9GQUH/QkFB/0FBQP8/Pz//PT09/zo7Ov84ODj/NTY1/zMzM/8yMjL/MTEx/zAwMP8vLy//Ly8v/y4u
Lv8uLi7/LS0t/ysrK/8qKiv/KSkp/ygnJ/8nJib/JiQk/yYjJP8lIyP/JSIj/yUjI/8kIiL/JCIi/yMh
Iv8iISD/IR8g/x8fH/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGho
aP9IRUX/JIOY/0dDQv9HRUX/Q0ND/0FBQP8/Pz7/Ozo7/zY2Nv8xMTH/Li4u/ywsLf8tLS3/LCws/ysr
K/8pKin/KSkp/ygoKP8oKCj/Jycn/ycmJv8lJSX/JSQl/yQkJP8jJCP/IyIj/yIiIv8iIyL/IyMj/yQj
JP8kJCT/IyMk/yMjI/8jIyP/IiIh/yAgIP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAGlqav9IR0f/SUdH/0lHR/9HR0b/RERF/0FBQf88PDz/Pj8//317ff91dXX/cHBw/2tr
a/9lZGX/YF9f/1paWv9VVVb/UVFR/0xMTP9HR0f/QUFB/z48PP83ODf/MzMz/zAwMP8wMDD/Ly8w/zAw
MP8xMDH/Jycn/yEhIv8jIyP/JCQk/yQkJP8jIyT/IyIi/yEhIf8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAG1ra/9KSkr/SkpK/0lJSf9ISEj/RkVF/0BAQP9CQkL/ycnJ/6ur
q/+fn57/np6e/5ycnP+ampr/l5eX/5SUlP+SkpH/j4+P/4yMjP+Iior/hoeG/4ODg/+AgID/fHx8/3h4
eP90dHP/cXFx/3Jycv9lZWX/Tk9O/ycnJ/8iIiL/IyQj/yQkJP8jIyP/IyMj/yMhI/8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAG5ubf9LS0z/S0xL/0tLS/9JSUn/RkZG/0A/
P/+Mi4z/s7Oz/6Ojo/+ioqL/oKCg/56env+cnJz/mZqZ/5eWl/+UlJT/kJGR/46Ojv+Li4v/h4eH/4OD
g/+Af4D/fHx8/3h4eP91dHT/cHBw/29vb/9wcHD/ZWVl/zAwMP8iIiL/IyMj/yQkJP8kJCT/JCMk/yMk
I/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHBwb/9NTU3/TU1N/0xM
TP9KSkr/R0dH/z8+P/+Ojo7/q6ur/6mpqf+np6b/paSk/6Kiov+goKD/nZ6e/5ubm/+YmJj/lZSU/5GR
kf+Ojo7/ioqL/4eHh/+Dg4P/f39//3x8fP93d3f/c3Nz/29vb/9vb2//cnJy/y8vL/8gISD/IyMj/yUk
JP8kJCT/JCQk/yQkJP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHFx
cf9OTk7/Tk5P/01NTf9MTEz/SEhI/0BAQP+QkJD/sLCv/62trf+srKv/qamp/6enp/+kpKX/oaKh/5+f
n/+cnJz/mJiY/5WVlf+RkZL/jo6O/4qKiv+Ghob/goKC/35+fv96env/dnZ2/3Fycf9vb2//cnJy/y8u
Lv8hICH/IyMj/yQkJf8lJSX/JCQl/yUlJP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAHNzc/9PT0//T09P/09PT/9NTUz/SUlJ/0FAQf+SkpL/tbS0/7Gxsf+vr7D/rq6u/6ur
q/+pqan/pqWl/6Kiov+fn5//nJyc/5iZmP+UlZX/kZGR/42Njf+JiYn/hYWF/4GBgf99fX3/eXl5/3R0
dP9wcHD/cnJy/y4uLv8hISD/JCQk/yUlJf8mJSb/JiUl/yUlJf8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAHR0dP9RUVH/UVBR/09PT/9OTk7/S0pK/0JCQv+VlZX/uLi4/7a1
tf+zs7P/sbGx/6+vr/+tra3/qaqq/6ampv+ioqP/n5+f/5ycnP+YmJj/lJSU/5CQj/+MjIz/iIiI/4OD
g/9/f3//e3t7/3d3dv9ycnL/cnJy/y4vL/8hISH/JCQk/yYmJf8mJib/JiYl/yYlJf8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHV1df9TU1T/U1NT/1BRUf9PTk7/TExM/0JC
Qv+Xl5f/vr6+/7q6uv+3uLf/tbW1/7Kysv+wsLD/rq2t/6uqrP+mp6b/oqKi/5+fn/+bm5v/l5aX/5KS
kv+Oj47/i4qK/4aGhv+BgoH/fX5+/3l5ef90dHT/c3Nz/y8vL/8jISP/JCQk/yYmJv8nJif/JiYm/yYm
Jv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHZ3dv9VVVX/VVVV/1NS
Uv9QUFD/TU1N/0RERP+ampr/wsLC/76/v/+8vLz/ubm6/7a2t/+zs7P/ra2t/6ampv+io6P/np6f/5ub
mv+Wl5f/k5OS/46Ojv+Njo3/jY2N/4mJiP+Eg4P/f39//3t7e/92d3b/dXV0/y8vL/8hISH/JCUl/yYm
Jv8nJyf/JyYm/yYnJv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHh3
eP9WVlb/VlZW/1RUVP9RUVH/Tk5O/0VERP+cnJz/xsbG/8LCw//AwMD/vb69/7q7uv+3t7f/sLCw/6mp
qf+mpqb/oqGi/52dnf+ZmZn/lZWV/5SUlP+Tk5P/j4+P/4uLi/+Ghob/gYGB/319ff94eHj/dnd2/y8v
L/8hIiP/JSUl/ycnJv8oJyj/KCcn/ycnJ/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAHl5ef9YWFj/V1dX/1ZWVv9SU1P/T09P/0ZGRv+fn5//ysvK/8bGxv/DxMT/wcHB/729
vv+6urr/s7Oz/6ysrP+oqKj/pKWk/6CgoP+bm5v/l5eX/5OTk/+SkpL/kJGQ/42Njf+IiIj/g4OD/35/
f/96env/eHh4/zAwL/8iIiL/JiYl/ycnJ/8oKCj/Jycn/ygnJ/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAHp7e/9ZWVr/WVlZ/1dXV/9UVFX/T1BP/0ZGRv+ioaL/z8/P/8rL
yv/HyMf/xMTE/8HBwf++vb7/tra2/6+vr/+qq6v/p6en/6Kiov+enp7/mZmZ/5WVlf+UlJT/k5KS/46O
jv+Kior/hYWF/4CAgP98fHz/eHl6/zAwMP8jIiP/JiYm/ygnKP8oKCr/KCgo/ycoKP8PDw//Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHx8fP9bW1r/Wlpa/1hYWP9VVlb/UFBQ/0dH
R/+kpKT/0tLS/87Ozv/Ly8v/x8fH/8PExP/AwMD/ubm5/7Kysf+tra3/qaio/6Wlpf+goKD/m5ub/5eW
l/+ZmZr/lJSU/5CQj/+Li4v/hoaG/4GBgf99fX3/e3t7/zAwMP8jIyP/JiYm/ygoJ/8pKSj/KSgo/ygo
KP8QEBD/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAH5+ff9cXFz/XFtc/1pZ
Wf9XV1b/UVFR/0hISP+lpaX/1tbW/9HR0f/Ozs7/y8rK/8bGxv/DwsL/uLi3/6ysrP+nqaf/o6Oi/56e
nv+ioqL/paWl/5+fn/+bmpv/lZaW/5GRkP+NjYz/iIiH/4KCgv9+fn7/fHx8/zEyMv8jIyP/Jicn/ygo
KP8pKSn/KCgp/ygpKP8RERH/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAH9/
f/9dXV3/XV1d/1paW/9YWFf/UlJS/0lJSf+kpKT/2tra/9TU1P/Q0ND/zc3N/8jJyf/ExMT/vb29/7W1
tv+xsbH/rKys/6ioqP+ioqT/nZ2e/5iYmP+YmJj/l5eX/5KSkv+Ojo3/iImI/4ODg/9/f3//fX19/zQ0
NP8jIyP/Jycn/ygoKP8pKSn/KSkp/ygoKP8TExP/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAICAgP9eXl7/Xl5e/1xcXP9ZWVn/U1NT/0lJSf+kpKT/3d3d/9fX1v/S0tL/z8/P/8vL
y//Gxsb/wsLC/72+vv+4uLn/tLS0/7CwsP+srKz/p6em/6Ghof+dnZ3/mJiY/5KTk/+Ojo7/iYmJ/4SE
hP9/gID/fn5+/zY2Nv8kIyT/Jycn/ygoKP8pKSn/KSkp/ykpKf8WFhb/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAIKCgv9fX17/Xl5e/11cXf9ZWln/VFRU/0pJSv+lpaX/4eHg/9na
2v/V1dT/0NDQ/83Mzf/Hx8f/w8PD/7++vv+6urr/tbW1/7CwsP+sra3/p6en/6Kiov+enZ3/mZmY/5SU
k/+Pj4//ioqK/4WFhf+AgID/fX19/zk5Of8jIyT/Jycn/ygoKf8pKSn/KSkp/ykpJ/8YFxj/DQ0NZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIOEhP9gYGD/X19f/15dXf9aWlr/VVVV/0tK
S/+lpaX/4eLi/9vb2//W1tX/0dHR/83Nzf/IyMj/w8PE/7+/v/+6u7r/tbW1/7Gxsf+tra3/qKio/6Kj
o/+enp7/mZmZ/5SUlP+Pj4//i4qK/4WFhf+AgID/fn59/zw8PP8kJCT/Jycn/ykpKf8qKin/KSkp/ygn
KP8ZGRr/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAISEhP9gYGD/X19f/15e
Xv9aWlv/VlZW/0tLS/+lpaX/4uLi/9zc3P/W19b/0dLR/83Ozv/JyMn/xMTE/7/AwP+7urv/trW2/7Gx
sf+tra3/qKin/6Ojov+enp7/mZmZ/5SUlP+Pj5D/i4qK/4aFhf+AgID/fn5+/z9AQP8lJSX/KCgn/ykp
Kf8qKin/KSkp/ygoKP8bGxv/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIWH
h/9gYGD/X19f/15eXv9aWlv/VlZX/0xMTP+mpqb/4eLh/9vb2//W1tb/0dHR/83Nzf/IyMn/xMTE/7+/
v/+7u7v/tba1/7Gxsf+tra3/qKio/6Ojo/+enp7/mZmZ/5SUlP+Pj4//ioqK/4WFhf+AgID/fX59/0JC
Qv8mJib/KCgo/ykpKf8qKir/KSkp/ygoKP8cHBz/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAIiIiP9gYGD/X19f/15eXv9bW1v/WFdX/05OTf+np6f/4eHh/9rZ2v/V1dX/0NDQ/83N
zf/IyMf/w8PD/76/v/+6urr/tbW1/7CxsP+sra3/p6in/6Kiov+enp7/mZmZ/5OUlP+Pj4//ioqK/4WE
hf+AgID/fn19/0ZGRv8mJyb/Kikq/yorKv8qKyr/KSkp/ygoKP8dHh7/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAImJif9gYGD/X19f/15eXv9bW1v/WFhY/1BQUP+pqan/6Ojo/9bW
1v/S09P/z8/P/8vLy//Gxsb/wsLC/76+vv+5ubn/tLS0/7CwsP+srKv/p6em/6Giov+dnZ3/mJiY/5OT
k/+Oj47/iYmJ/4SEhP9/f4D/e3t7/0pKS/8oKCj/Kysr/ywsLP8rKir/Kikq/yknJ/8fHx//DQ0NZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIuLi/9fX17/Xl5f/11dXf9bW1v/WVhY/1RU
VP9XV1j//////+Pj4//T09P/z8/P/8rKyv/Fx8f/wcHC/7++v/+5ubn/tbW1/7CwsP+sraz/pqao/6Ki
ov+enZ7/mZmZ/5STlP+QkJD/i4uL/4aGhv+BgYH/fHx8/zEwL/8rKiz/LCws/y0sLP8rKir/F4GW/ygm
J/8iIiL/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIyMjP9eXV3/Xl5e/11c
XP9bW1v/WFlZ/1ZWVf9OTk7/VldW/6enp/+lpaX/pKSk/6Kiov+goKD/nJyc/5iYmP+Tk5P/jo+P/4mJ
if+FhIX/gICA/3t7fP92dnb/cXJx/21tbf9nZmb/YmJi/11dXf9aWlr/MzMz/ywtLP8uLi3/LS0t/y0t
Lf8sKin/KScm/ycmJP8kJCT/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAI6O
jv9cXlz/Xl5e/1tbXP9aWlr/WFhY/1ZWV/9SU1P/Tk9P/0tLSv9HR0f/RERE/0JCQv9AQED/Pj8//zw8
Pf87Ozv/Ozs8/zo6Ov85OTn/ODg4/zY2Nv81NDX/NDM0/zMzM/8xMTH/MDAw/y8vL/8vLy//Li4u/y8v
L/8uLy7/Li0t/y0tLP8rKij/F4GW/ygmJ/8mJib/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAJCPkP9dW1v/XV1c/1tbW/9ZWVn/WFhY/1ZWVv9TVFP/UlJR/09PT/9OTk3/S0xL/0pK
Sv9ISEj/R0ZH/0VERf9CQkP/QUFC/0BAQP8/Pz//Pj4+/z09Pf87Ozv/OTk6/zg4OP82Njf/NTU1/zMz
M/8yMjL/MTEx/y8wL/8vLy//Ly0t/xeAlf8sKCf/KScm/ykmJf8nJyf/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAJKSkv9aWlr/W1tb/1paWv9ZWVj/V1dX/1ZVVv9UVFT/UlJR/1BP
UP9OTk7/TU1N/0xMTP9KSkr/SElI/0dHR/9GRkb/RERE/0JCQv9AQED/Pz8//z4+Pv89PT3/Ozs7/zk5
Of84ODj/Nzc2/zQ0NP8zMzP/MjIy/zAwMP8vLy//Lyws/y4qK/8tKCf/GI6q/ygnJv8pKCn/DQ0NZA4O
DlcNDQ0yCAgIDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk/9ZWFj/Wlpa/1lZWf9YWFj/V1dW/1VV
VP9TU1P/UVFR/1BPUP9PT0//TU5O/0xMTP9LSkv/SUlJ/0hISP9GRkb/RERE/0NCQv9BQUH/QD8//z4/
Pv89PT7/Ozw8/zo6Ov85OTn/Nzg3/zU1NP8zMzP/MTIy/zAwMP8vLy//Li0t/xeAlf8tJyj/KyYm/ygk
Jf8rKyv/DQ0NVw0NDUwNDQ0sCQkJDAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk/9XV1f/WVhY/1hY
V/9XV1f/VlZW/1VVVf9SUlL/UFFR/09PT/9OTk7/TU1N/0xMTP9LSkr/SUlJ/0dHR/9GRkb/REVE/0JC
Qv9BQUD/Pz8//z8/Pv89PT3/Ozw8/zo6Ov84ODj/Nzc2/zQ0Nf8zMzP/MTIy/zAwL/8vLy//Li0t/y0s
LP8rKSj/GI6q/yclJf8tLS3/DAwMMg0NDSwLCwsaCAgIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKS
kv9UVVX/VVVV/1RVVf9TU1P/UlJS/1FRUf9PT0//Tk5O/01NTf9MTEz/SktL/0tJSf9JSUn/SEhI/0ZG
R/9FRUX/Q0ND/0FBQf9APz//Pj4+/z0+Pf88PDz/Ojo6/zk5Of84ODj/NjY2/zM0M/8yMjH/MDAw/y8v
L/8uLi7/LS0t/y0tLf8rKSn/KSYm/yclJP8vLy//BwcHDgkJCQwICAgHAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAJaVlf+TkpL/kpKS/4+Qj/+NjY3/iYmJ/4eHhv+FhYX/goKD/3+Af/99fHz/enl5/3Z2
dv90dHT/cnJy/3Bwb/9ra2v/aGho/2VmZf9jZGP/YWFh/15eX/9cXFz/WFhY/1VUVf9TUlL/UFBQ/01N
Tf9LS0v/SEhI/0NEQ/9BQUH/Pz8//zw9PP85OTn/NjY2/zIyMv8xMTH/AAAAAQAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAf
AAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAf
AAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAP///////wAA////////
AAD///////8AAP///////wAA////////AAA=
</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,389 @@
<?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="treeContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>164, 17</value>
</metadata>
<metadata name="timer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>293, 17</value>
</metadata>
<metadata name="backgroundWorker1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>483, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAAAwMAAAAQAgAKglAABGFQAAKAAAABAA
AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEU8OykuDgdIIxQSURwdHU8ZGhpPFhYWTxQU
FE8SEhJPDxAPTw0KCk8KBQRPCAQDTwcDAU8HAQBPBgQEUQUFBTxLVVb/L215/zdXXP89Ozv/ODc3/zM0
M/8vLy//Kioq/yYlJf8iJif/Gycp/xggI/8UHiD/Eh4g/w4SE/8GBARRQWRr/xGXtP8yUln/NDIx/y4u
Lf8qKir/KSgo/ycnJ/8lJCP/Iikq/xs4Pf8aOD7/GzpB/xo4Pf8SGhv/BwMDT01aXP46Ulj+Pzc0/kdH
R/5MTU3+Q0ND/jw8PP43Nzf+Ly8v/igoKP4lKCn+Iico/hwiI/4cHyD/FBMU/wYHBk9XVVX+Rj8+/klJ
Sf6ioqL+rq+u/p+fn/6YmJj+j4+P/oSEhP56enr+dXJy/mNgYP4iHx7+Gxka/xUVFf8HBwdPW1pa/kVF
RP5YWFj+rq6u/r6+vv6wsK/+qamp/qCgoP6UlJT+iYmJ/oGBgf5xcXH+JCQk/hsbG/8WFhb/BwcHT15e
Xv5ISEj+Wlpa/rW1tf7Gxsb+tra2/qysq/6hoaL+lpaW/oyMjP6EhIT+cnJy/iQkJP4cHBz/GBgY/wcH
B09jY2P+TUxM/l5eXv7AwMD+0dHR/r6+vv6vr6/+o6Ok/pmYmf6Rk5P+iouK/nd4d/4lJSX+HR0d/xkZ
Gf8HBwdPZ2dn/lBQUP5iYmL+ysrK/tzc3P7Hxsf+tbS1/qmpqf6fn5/+mJiY/o6Pj/57e3z+JSUm/h4e
Hv8aGhr/CAgIT2tra/5TU1P+ZGRk/tLS0v7j5eP+zMzM/ry8vP6wsLD+pqam/pubm/6TkZH+f39//igo
KP4fHx7/HBwc/wkJCk9ubm7+VlVW/mZlZv7X1tf+6Ojo/tPS0/7Hx8f+urq6/qurq/6dnp7+lJSU/oGB
gP4sLCz+Hx4e/x4dHP8LCwtPb29v/lZWVv5nZ2j+4eHh/vLx8v7Z2dn+zc3N/sDAwP6wsrL+pKOk/pmZ
mf6Ghob+MDAw/h8gH/8eHx//DQsNT29vb/5YWFj+WVpa/sHBwf7V1dX+wMC//rW1tP6pqan+m5ub/o6P
jv6FhYX+cXFx/i4qKv4gLC7/Hiot/w4JB09vb3D+Wlla/lJSUf5VVVX+YGBg/ltbWv5VVVP+Tk5O/kdH
R/5BQUH+Ozs6/jAvL/4oKCj+Izk9/x8tL/8QCQhRbm5u/lVVVf5SUlL+S0tL/kRERP5BQUH+PT09/jk5
Of41NTX+MzIy/i8vLv4sKSj+Jy4v/h9DTP8iLjH/Ew0KSHp7ev5qamr+ZWVl/mFhYv5cXFz+WFhY/lNS
U/5NTU3+R0ZH/kJCQv49PDz+NzU1/jAxMf4qMTL/Jigp/x4cHCn//wAAAAEAAAABAAAAAQAAAAEAAAAB
AAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAKAAAACAAAABAAAAAAQAgAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMBBwcHAwYGBgQGBgYEBgYGBAYG
BgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYGBgQGBgYEBgYGBAYG
BgQGBgYEBgYGBAYGBgQGBgYEBwcHAwUFBQIAAAAAAAAAAAAAAAAAAAAAAAAAAAoKCgkMDAwgDQ0NKg0N
DSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0N
DSoNDQ0qDQ0NKg0NDSoNDQ0qDQ0NKg0NDSoMDAwlCgoKEQUFBQIAAAAAAAAAAAAAAAAAAAAACAgIFAoK
CkcMDAxbDAwMXAwMDFwMDAxcDAwMXAwNDFwNDQ1cDQ0NXA0NDVwNDQ1cDQ0NXA0NDVwNDQ5cDg4OXA4O
DlwODg5cDg4OXA4ODlwODg5cDg4OXA4ODlwODg5cDg4OWw0NDVIMDAwlBwcHAwAAAAAAAAAAAAAAAGFh
YY9ZWFjFUVBP0kpJSdhIR0fYQkJC2D0+Ptg5OTnZODc22TMyM9kuLi/ZKysr2SYmJtkjIyPZICAf2Rsb
G9kXFxjZExMT2Q8ODtkNDAzZDQwM2A0MDNgNDAzYDQ0N2A0NDdgODQ27Dg4OWw0NDSoGBgYEAAAAAAAA
AAAAAAAAYmFhvzxCQ/82Uln/NlJZ/zdPVP89PDz/PDw8/zo6Ov85OTn/ODg4/zY2Nv80NDT/NDQ0/zIy
Mv8xMTD/Ly4w/yw1OP8qNjj/JzQ1/yYyNP8kMDL/Ii4w/x8sLv8fKy3/HyIi/w0NDdgODg5cDQ0NKgYG
BgQAAAAAAAAAAAAAAABjY2K/OFRa/xqXsv8bk6v/M11l/z49Pf89PT3/Ozs7/zo6Of83Nzj/NTY1/zQ0
NP8zMzP/MTEw/y8vL/8uLi7/KzQ2/yk1Nv8nMjT/JjAz/yQvMf8jLjD/ISwu/x8rLf8fISL/DQ0N2A4O
DlwNDQ0qBgYGBAAAAAAAAAAAAAAAAGZkZL86V13/GpKs/zJkbv9BPz//Pj8+/zw8PP85OTn/Njc2/zQ0
Nf8zMjP/MjIy/zAwMP8vLy//Li4u/y0sLP8qKSn/I0hQ/yJHUP8hRk//IEZO/yBGTv8fRU3/HztB/yAf
H/8NDQ3YDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAaWdovz5WW/83YWr/RkND/0FAQP89PT3/ODc3/zIy
Mf8uLi7/LCws/ysrK/8pKSn/Kioq/ygoKP8nJyf/JSUl/yUkJP8kIyP/IyEh/yMiIv8kIiL/JCMj/yQj
I/8iISH/ISAg/w0NDdgODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAABqamq/SUhI/0hHR/9GRkb/QUFB/0ZH
Rv9/f4D/gICA/3h4eP9xcXH/a2tr/2RkZP9dXV7/V1dX/09PT/9ISEj/Q0NC/0BAQP9AQED/Ozs7/yYm
Jv8jIyP/JCQk/yMjI/8jIyH/DQ0N2A4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAG5ubr9LS0v/SkpL/0hI
SP9BQUH/i4uL/6ysrP+hoqH/n5+e/52bnf+YmJj/lJSU/5CQj/+Li4v/hYaF/4CAgP97e3r/dXR1/3Fx
cf9ra2v/Ozs7/yEhIv8kJCT/JCQk/yQkI/8NDQ3YDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAcHBwv01N
Tf9MTU3/SUpK/0BAP/+Wlpj/q6ur/6inp/+kpKT/oKGg/52cnP+YmJj/k5OT/46Ojv+IiYj/g4OC/319
ff93d3f/cXFx/3Fxcf8/Pz//ISEh/yQkJP8lJSX/JSQk/w0NDdkODg5cDQ0NKgYGBgQAAAAAAAAAAAAA
AABycnO/T05P/05PT/9MTEz/QUFB/5ubmv+ysrL/rq6u/6urq/+np6f/oqKi/52dnf+ZmZn/k5OT/42O
jv+IiIf/gYGB/3x7fP90dXT/cXFx/z8/P/8hISH/JCQl/yUlJf8lJib/DQ0N2Q4ODlwNDQ0qBgYGBAAA
AAAAAAAAAAAAAHR1db9RUVH/UFBQ/01NTf9CQkL/np6e/7i4uP+0s7T/sbGx/62trf+oqKj/o6Oj/52d
nf+Yl5f/kZKR/4yMi/+FhYX/fn9//3h4eP9zc3P/Pz8//yIiIv8lJSX/JiYm/yYmJv8NDQ3ZDg4OXA0N
DSoGBgYEAAAAAAAAAAAAAAAAdnZ2v1RUVP9TU1P/T09P/0VFRf+kpKT/v7+//7u7u/+2trb/sbGx/6mp
qf+jo6P/nZ2d/5eXl/+QkZH/jo2N/4mJif+CgoL/fHx8/3d3d/9AQED/IiIi/yYmJv8nJyf/Jycn/w0N
DdkODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAAB4eHi/V1dW/1VVVf9QUFD/RkZG/6moqf/Gxsb/wcHB/7y8
vP+2trb/q6ys/6Wlpf+fn5//mZiY/5WUlf+RkpL/jI2M/4WFhf9/fn//enp6/0FBQf8iIiP/JiYm/ygn
KP8oJyf/DQ0N2Q4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAHp6er9YWVn/V1dX/1JSU/9ISEj/ra2t/8zM
zP/Gxsb/wcHB/7u7u/+wsLD/qamp/6Ojo/+cnZ3/lpaW/5OTk/+Pj47/iIiI/4CBgP98fH3/QkJC/yMj
I/8nJyf/KCgo/ygoKP8ODg7ZDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAfXx9v1tbW/9ZWVn/VFVV/0hJ
Sf+xsbH/0tLS/8vLy//FxcX/v7++/7Ozs/+qqqr/pKWk/6Cfn/+ampr/mpia/5GRkf+Li4r/goOD/35+
fv9CQ0P/IyQk/ycnJ/8pKSn/KSkp/xAQENkODg5cDQ0NKgYGBgQAAAAAAAAAAAAAAAB+fn6/XV1d/1tb
W/9WVlb/S0lJ/7Oysv/W1tb/z8/P/8nJyf/CwsL/tbW1/66trf+np6f/oqKi/5ycnP+ZmJn/k5OT/4yM
jP+FhYX/f39//0ZFRv8kJCT/KCgo/ykpKf8pKSn/EhIS2Q4ODlwNDQ0qBgYGBAAAAAAAAAAAAAAAAIGB
gb9eXl7/XFxc/1dYV/9KSkr/tLS0/9rb2v/S0tL/zczM/8XFxf+/v7//uLi4/7Gxsf+rq6r/o6Oj/5yc
nP+UlZT/jY2N/4aFhv+AgID/SUlI/yMjI/8oKCj/KSkp/ykpKf8UFBTZDg4OXA0NDSoGBgYEAAAAAAAA
AAAAAAAAg4ODv19fX/9eXl7/WVhZ/0tLS/+0tLT/3d3d/9TU1P/Ozs7/x8bH/8DAwP+5ubn/srKy/6ur
q/+kpKT/nJyc/5WVlv+Ojo7/h4eH/4GBgf9NTU3/IyQk/ygpKP8qKSn/KSkp/xgYGNkODg5cDQ0NKgYG
BgQAAAAAAAAAAAAAAACFhYW/YGBf/15eXv9aWln/TU1N/7a2tv/e3t7/1dXV/87Pz//Hx8f/wcHB/7m5
uv+ysrP/rKyr/6SkpP+dnZ3/lZWW/46Ojv+Hh4f/gYGB/1BQUP8kJCT/KSkp/ykpKf8oKCj/GhkZ2Q4O
DlwNDQ0qBgYGBAAAAAAAAAAAAAAAAIiGhr9gYGD/Xl5e/1paWv9PT0//t7e3/93d3f/U1NT/zs7O/8fH
x//AwMD/ubm5/7Kysv+rq6v/o6Ok/5ycnP+VlZX/jo6N/4aGhv+BgYH/U1NT/yYmJv8qKir/Kisq/ygo
Kf8bHBzYDg4OXA0NDSoGBgYEAAAAAAAAAAAAAAAAioqKv19fX/9eXl7/Wlta/1FRUf+srKz/4uLi/9LS
0v/Mzcz/xcXG/7+/v/+4ubj/srKx/6urq/+kpKT/nZ2d/5WVlf+Njo7/h4aH/4GBgf9TUlL/KSkp/yws
LP8qLzH/Jy4v/x4eHtgNDQ1cDQ0NKgYGBgQAAAAAAAAAAAAAAACMi4y/Xl5e/11dXf9aWlr/VVVV/1xc
XP+qqqr/sbGx/66urv+qqqr/pKSj/52dnf+VlZX/jo6O/4eHh/+Af3//d3l3/3BwcP9paWn/XFxc/zMz
M/8sLCz/LS0t/yswMf8nLS7/ICAg2A0NDVwNDQ0qBgYGBAAAAAAAAAAAAAAAAI6Ojr9cXFz/XFxc/1pa
Wv9WV1f/UFFQ/0xMS/9HR0f/RERE/0BBQP8+Pj7/PDw8/zo7Ov84ODj/ODg4/zU1Nf8zMzP/MTEx/zAw
MP8uLi//Li4u/y4tL/8qPUD/KDk+/yU4O/8jIiLYDQ0NXA0NDSoGBgYEAAAAAAAAAAAAAAAAkZGRv1pb
W/9bW1r/WFlY/1ZWVv9UVFT/UFBQ/05OTv9MTEz/SklK/0dHR/9FRUX/QkJC/0BAQP8+Pj7/PDw8/zo6
Ov83ODf/NTU1/zIyMv8xMTH/Ly4u/yk7P/8oO0D/JTpA/yUkJNgNDQ1bDQ0NKgcHBwQAAAAAAAAAAAAA
AACTk5O/WFhY/1lZWf9YV1j/VVVV/1JSUv9QUFD/Tk5O/01MTf9KSkr/SEhI/0ZGRv9DQkP/QEFA/z8/
P/89PT3/Ozs6/zg4OP82NTX/MzMz/zExMf8vLi3/IVtn/yovMf8nLi//KCgo0gwMDEcMDAwgBwcHAwAA
AAAAAAAAAAAAAJKTkr9VVFX/VFVV/1RUVP9SUVH/T09P/01NTv9MS0z/SkpK/0hJSP9HR0f/REVE/0FB
Qf8/Pz//Pj4+/zs7PP85Ojn/Nzc3/zQ0NP8xMTH/LzAw/y4uLv8tLCz/KS8w/yUtLv8tLS3FCgoKFAoK
CgkDAwMBAAAAAAAAAAAAAAAAl5aXj5KSkr+QkJC/jYuLv4eHh7+EhIS/gICAv3x8fL93d3e/dHR0v3Bw
cL9samy/ZmZmv2NjY79fX1+/W1tbv1VVVb9SUlK/Tk5Ov0pKSr9ERES/QUFBvz09Pb85OTm/MzMzvzEx
MY8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////gAAAH4AAAB+AA
AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AA
AAfgAAAH4AAAB+AAAAfgAAAH4AAAB+AAAAfgAAAH4AAAB////////////////ygAAAAwAAAAYAAAAAEA
IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAACCAgIBwkJCQwICAgOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcH
Bw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcH
Bw4HBwcOBwcHDgcHBw4HBwcOBwcHDgcHBw4HBwcOCAgIDgkJCQwICAgHAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAICAgHCwsLGQ0NDSwNDQ0yDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0N
DTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0N
DTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMw0NDTMNDQ0zDQ0NMg0NDSwLCwsaCAgIBwAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJCQkMDQ0NLA0NDUwODg5XDg4OWQ4ODlkODg5ZDg4OWQ4O
DlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4O
DlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OWQ4ODlkODg5ZDg4OVw0N
DUwNDQ0sCQkJDAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFBQUOCgoKMgsLC1cLCwtkCwsLZgsL
C2YLCwtmCwsLZgwMDGYMDAxmDAwMZgwMDGYMDAxmDAwMZgwMDGYMDAxmDAwMZg0NDWYNDQ1mDQ0NZg0N
DWYNDQ1mDQ0NZg0NDWYNDQ1mDg4OZg4ODmYODg5mDg4OZg4ODmYODg5mDg4OZg4ODmYODg5mDg4OZg4O
DmYODg5mDg4OZA4ODlcNDQ0yCAgIDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGJiYv9cXFz/Wlpa/1VV
V/9TU1L/UVFR/05OTv9LSkv/R0dH/0NDQ/9BQUH/Pz4//zw8PP86OTn/NTU1/zIyMv8wMDD/LS0u/yoq
Kv8mJib/JCMk/yEhIf8eHh7/Gxsc/xkZGf8UFBT/ERER/w8PD/8ODg7/Dg4O/w4ODv8ODg7/Dg4O/w4O
Dv8ODg7/Dg4O/w4ODv8PDw//Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGJi
Yf8+PDz/Pzw7/0A7Ov9AOzr/Pzo5/z06Ov89Ojr/Ozs7/zo7Ov87Ojv/Ojo6/zk5Of84ODj/Nzc3/zY2
Nf81NTT/MzMz/zIyMv8xMTH/MTIx/zAwMP8wMDD/Ly4u/y4tLf8sKiv/Kigo/yknJ/8oJiX/JiQk/yUj
I/8kIiH/IiEg/yAeHv8gHh3/IB0d/x8eHv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAGNjY/9BPj3/Gpiz/xuWsP8alrD/Gpey/yKClv8+PT3/PT09/zw8PP87Ozv/Ojo6/zk5
Of84ODj/Nzc3/zY2Nv80NDT/MzMz/zMyMv8yMTH/MDEw/y8vL/8vLy//Li4t/yJWYf8hVF//IVRf/x9T
Xf8gU13/H1Nc/x9RXP8eUFv/HlFb/x1QWv8cT1n/HFBb/x8eHv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAGRkZP9CPTz/GpWv/xySq/8blK3/JH+S/0A8O/8/Pj7/Pj0+/z08
PP87Ozv/Ojo6/zk5Of84ODj/Njc3/zU1Nf80NDT/MzMz/zIyMv8xMTH/MDAv/y8vL/8uLi7/LS0s/ywq
Kv8tKSj/KiYm/yklJf8nJCP/KCIi/yYiIf8lICD/JB8e/yMeHP8iHRz/Hx0d/x8eH/8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGVlZf9GQUD/Gpaw/xuUrf8kgJP/Qj49/0A/
P/8/Pj//Pj4+/zw8PP86Ojr/OTk5/zc3N/82NTb/NDQ0/zMzM/8yMjL/MTEx/zAwMP8vLzD/Ly8v/y4u
Lv8tLS3/LCws/ysrKv8gVV//H1Nd/x9UXf8gU13/H1Nc/x9RXP8fUVz/HlFb/x5QW/8dUFv/Hx4f/x8f
IP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGZmZv9GQkL/GZex/yWB
k/9GQUH/QkFB/0FBQP8/Pz//PT09/zo7Ov84ODj/NTY1/zMzM/8yMjL/MTEx/zAwMP8vLy//Ly8v/y4u
Lv8uLi7/LS0t/ysrK/8qKiv/KSkp/ygnJ/8nJib/JiQk/yYjJP8lIyP/JSIj/yUjI/8kIiL/JCIi/yMh
Iv8iISD/IR8g/x8fH/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAGho
aP9IRUX/JIOY/0dDQv9HRUX/Q0ND/0FBQP8/Pz7/Ozo7/zY2Nv8xMTH/Li4u/ywsLf8tLS3/LCws/ysr
K/8pKin/KSkp/ygoKP8oKCj/Jycn/ycmJv8lJSX/JSQl/yQkJP8jJCP/IyIj/yIiIv8iIyL/IyMj/yQj
JP8kJCT/IyMk/yMjI/8jIyP/IiIh/yAgIP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAGlqav9IR0f/SUdH/0lHR/9HR0b/RERF/0FBQf88PDz/Pj8//317ff91dXX/cHBw/2tr
a/9lZGX/YF9f/1paWv9VVVb/UVFR/0xMTP9HR0f/QUFB/z48PP83ODf/MzMz/zAwMP8wMDD/Ly8w/zAw
MP8xMDH/Jycn/yEhIv8jIyP/JCQk/yQkJP8jIyT/IyIi/yEhIf8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAG1ra/9KSkr/SkpK/0lJSf9ISEj/RkVF/0BAQP9CQkL/ycnJ/6ur
q/+fn57/np6e/5ycnP+ampr/l5eX/5SUlP+SkpH/j4+P/4yMjP+Iior/hoeG/4ODg/+AgID/fHx8/3h4
eP90dHP/cXFx/3Jycv9lZWX/Tk9O/ycnJ/8iIiL/IyQj/yQkJP8jIyP/IyMj/yMhI/8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAG5ubf9LS0z/S0xL/0tLS/9JSUn/RkZG/0A/
P/+Mi4z/s7Oz/6Ojo/+ioqL/oKCg/56env+cnJz/mZqZ/5eWl/+UlJT/kJGR/46Ojv+Li4v/h4eH/4OD
g/+Af4D/fHx8/3h4eP91dHT/cHBw/29vb/9wcHD/ZWVl/zAwMP8iIiL/IyMj/yQkJP8kJCT/JCMk/yMk
I/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHBwb/9NTU3/TU1N/0xM
TP9KSkr/R0dH/z8+P/+Ojo7/q6ur/6mpqf+np6b/paSk/6Kiov+goKD/nZ6e/5ubm/+YmJj/lZSU/5GR
kf+Ojo7/ioqL/4eHh/+Dg4P/f39//3x8fP93d3f/c3Nz/29vb/9vb2//cnJy/y8vL/8gISD/IyMj/yUk
JP8kJCT/JCQk/yQkJP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHFx
cf9OTk7/Tk5P/01NTf9MTEz/SEhI/0BAQP+QkJD/sLCv/62trf+srKv/qamp/6enp/+kpKX/oaKh/5+f
n/+cnJz/mJiY/5WVlf+RkZL/jo6O/4qKiv+Ghob/goKC/35+fv96env/dnZ2/3Fycf9vb2//cnJy/y8u
Lv8hICH/IyMj/yQkJf8lJSX/JCQl/yUlJP8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAHNzc/9PT0//T09P/09PT/9NTUz/SUlJ/0FAQf+SkpL/tbS0/7Gxsf+vr7D/rq6u/6ur
q/+pqan/pqWl/6Kiov+fn5//nJyc/5iZmP+UlZX/kZGR/42Njf+JiYn/hYWF/4GBgf99fX3/eXl5/3R0
dP9wcHD/cnJy/y4uLv8hISD/JCQk/yUlJf8mJSb/JiUl/yUlJf8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAHR0dP9RUVH/UVBR/09PT/9OTk7/S0pK/0JCQv+VlZX/uLi4/7a1
tf+zs7P/sbGx/6+vr/+tra3/qaqq/6ampv+ioqP/n5+f/5ycnP+YmJj/lJSU/5CQj/+MjIz/iIiI/4OD
g/9/f3//e3t7/3d3dv9ycnL/cnJy/y4vL/8hISH/JCQk/yYmJf8mJib/JiYl/yYlJf8ODg7/Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHV1df9TU1T/U1NT/1BRUf9PTk7/TExM/0JC
Qv+Xl5f/vr6+/7q6uv+3uLf/tbW1/7Kysv+wsLD/rq2t/6uqrP+mp6b/oqKi/5+fn/+bm5v/l5aX/5KS
kv+Oj47/i4qK/4aGhv+BgoH/fX5+/3l5ef90dHT/c3Nz/y8vL/8jISP/JCQk/yYmJv8nJif/JiYm/yYm
Jv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHZ3dv9VVVX/VVVV/1NS
Uv9QUFD/TU1N/0RERP+ampr/wsLC/76/v/+8vLz/ubm6/7a2t/+zs7P/ra2t/6ampv+io6P/np6f/5ub
mv+Wl5f/k5OS/46Ojv+Njo3/jY2N/4mJiP+Eg4P/f39//3t7e/92d3b/dXV0/y8vL/8hISH/JCUl/yYm
Jv8nJyf/JyYm/yYnJv8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHh3
eP9WVlb/VlZW/1RUVP9RUVH/Tk5O/0VERP+cnJz/xsbG/8LCw//AwMD/vb69/7q7uv+3t7f/sLCw/6mp
qf+mpqb/oqGi/52dnf+ZmZn/lZWV/5SUlP+Tk5P/j4+P/4uLi/+Ghob/gYGB/319ff94eHj/dnd2/y8v
L/8hIiP/JSUl/ycnJv8oJyj/KCcn/ycnJ/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAHl5ef9YWFj/V1dX/1ZWVv9SU1P/T09P/0ZGRv+fn5//ysvK/8bGxv/DxMT/wcHB/729
vv+6urr/s7Oz/6ysrP+oqKj/pKWk/6CgoP+bm5v/l5eX/5OTk/+SkpL/kJGQ/42Njf+IiIj/g4OD/35/
f/96env/eHh4/zAwL/8iIiL/JiYl/ycnJ/8oKCj/Jycn/ygnJ/8ODg7/Dg4OZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAHp7e/9ZWVr/WVlZ/1dXV/9UVFX/T1BP/0ZGRv+ioaL/z8/P/8rL
yv/HyMf/xMTE/8HBwf++vb7/tra2/6+vr/+qq6v/p6en/6Kiov+enp7/mZmZ/5WVlf+UlJT/k5KS/46O
jv+Kior/hYWF/4CAgP98fHz/eHl6/zAwMP8jIiP/JiYm/ygnKP8oKCr/KCgo/ycoKP8PDw//Dg4OZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAHx8fP9bW1r/Wlpa/1hYWP9VVlb/UFBQ/0dH
R/+kpKT/0tLS/87Ozv/Ly8v/x8fH/8PExP/AwMD/ubm5/7Kysf+tra3/qaio/6Wlpf+goKD/m5ub/5eW
l/+ZmZr/lJSU/5CQj/+Li4v/hoaG/4GBgf99fX3/e3t7/zAwMP8jIyP/JiYm/ygoJ/8pKSj/KSgo/ygo
KP8QEBD/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAH5+ff9cXFz/XFtc/1pZ
Wf9XV1b/UVFR/0hISP+lpaX/1tbW/9HR0f/Ozs7/y8rK/8bGxv/DwsL/uLi3/6ysrP+nqaf/o6Oi/56e
nv+ioqL/paWl/5+fn/+bmpv/lZaW/5GRkP+NjYz/iIiH/4KCgv9+fn7/fHx8/zEyMv8jIyP/Jicn/ygo
KP8pKSn/KCgp/ygpKP8RERH/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAH9/
f/9dXV3/XV1d/1paW/9YWFf/UlJS/0lJSf+kpKT/2tra/9TU1P/Q0ND/zc3N/8jJyf/ExMT/vb29/7W1
tv+xsbH/rKys/6ioqP+ioqT/nZ2e/5iYmP+YmJj/l5eX/5KSkv+Ojo3/iImI/4ODg/9/f3//fX19/zQ0
NP8jIyP/Jycn/ygoKP8pKSn/KSkp/ygoKP8TExP/Dg4OZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAICAgP9eXl7/Xl5e/1xcXP9ZWVn/U1NT/0lJSf+kpKT/3d3d/9fX1v/S0tL/z8/P/8vL
y//Gxsb/wsLC/72+vv+4uLn/tLS0/7CwsP+srKz/p6em/6Ghof+dnZ3/mJiY/5KTk/+Ojo7/iYmJ/4SE
hP9/gID/fn5+/zY2Nv8kIyT/Jycn/ygoKP8pKSn/KSkp/ykpKf8WFhb/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAIKCgv9fX17/Xl5e/11cXf9ZWln/VFRU/0pJSv+lpaX/4eHg/9na
2v/V1dT/0NDQ/83Mzf/Hx8f/w8PD/7++vv+6urr/tbW1/7CwsP+sra3/p6en/6Kiov+enZ3/mZmY/5SU
k/+Pj4//ioqK/4WFhf+AgID/fX19/zk5Of8jIyT/Jycn/ygoKf8pKSn/KSkp/ykpJ/8YFxj/DQ0NZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIOEhP9gYGD/X19f/15dXf9aWlr/VVVV/0tK
S/+lpaX/4eLi/9vb2//W1tX/0dHR/83Nzf/IyMj/w8PE/7+/v/+6u7r/tbW1/7Gxsf+tra3/qKio/6Kj
o/+enp7/mZmZ/5SUlP+Pj4//i4qK/4WFhf+AgID/fn59/zw8PP8kJCT/Jycn/ykpKf8qKin/KSkp/ygn
KP8ZGRr/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAISEhP9gYGD/X19f/15e
Xv9aWlv/VlZW/0tLS/+lpaX/4uLi/9zc3P/W19b/0dLR/83Ozv/JyMn/xMTE/7/AwP+7urv/trW2/7Gx
sf+tra3/qKin/6Ojov+enp7/mZmZ/5SUlP+Pj5D/i4qK/4aFhf+AgID/fn5+/z9AQP8lJSX/KCgn/ykp
Kf8qKin/KSkp/ygoKP8bGxv/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIWH
h/9gYGD/X19f/15eXv9aWlv/VlZX/0xMTP+mpqb/4eLh/9vb2//W1tb/0dHR/83Nzf/IyMn/xMTE/7+/
v/+7u7v/tba1/7Gxsf+tra3/qKio/6Ojo/+enp7/mZmZ/5SUlP+Pj4//ioqK/4WFhf+AgID/fX59/0JC
Qv8mJib/KCgo/ykpKf8qKir/KSkp/ygoKP8cHBz/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAIiIiP9gYGD/X19f/15eXv9bW1v/WFdX/05OTf+np6f/4eHh/9rZ2v/V1dX/0NDQ/83N
zf/IyMf/w8PD/76/v/+6urr/tbW1/7CxsP+sra3/p6in/6Kiov+enp7/mZmZ/5OUlP+Pj4//ioqK/4WE
hf+AgID/fn19/0ZGRv8mJyb/Kikq/yorKv8qKyr/KSkp/ygoKP8dHh7/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAImJif9gYGD/X19f/15eXv9bW1v/WFhY/1BQUP+pqan/6Ojo/9bW
1v/S09P/z8/P/8vLy//Gxsb/wsLC/76+vv+5ubn/tLS0/7CwsP+srKv/p6em/6Giov+dnZ3/mJiY/5OT
k/+Oj47/iYmJ/4SEhP9/f4D/e3t7/0pKS/8oKCj/Kysr/ywsLP8rKir/Kikq/yknJ/8fHx//DQ0NZg4O
DlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIuLi/9fX17/Xl5f/11dXf9bW1v/WVhY/1RU
VP9XV1j//////+Pj4//T09P/z8/P/8rKyv/Fx8f/wcHC/7++v/+5ubn/tbW1/7CwsP+sraz/pqao/6Ki
ov+enZ7/mZmZ/5STlP+QkJD/i4uL/4aGhv+BgYH/fHx8/zEwL/8rKiz/LCws/y0sLP8rKir/F4GW/ygm
J/8iIiL/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAIyMjP9eXV3/Xl5e/11c
XP9bW1v/WFlZ/1ZWVf9OTk7/VldW/6enp/+lpaX/pKSk/6Kiov+goKD/nJyc/5iYmP+Tk5P/jo+P/4mJ
if+FhIX/gICA/3t7fP92dnb/cXJx/21tbf9nZmb/YmJi/11dXf9aWlr/MzMz/ywtLP8uLi3/LS0t/y0t
Lf8sKin/KScm/ycmJP8kJCT/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAI6O
jv9cXlz/Xl5e/1tbXP9aWlr/WFhY/1ZWV/9SU1P/Tk9P/0tLSv9HR0f/RERE/0JCQv9AQED/Pj8//zw8
Pf87Ozv/Ozs8/zo6Ov85OTn/ODg4/zY2Nv81NDX/NDM0/zMzM/8xMTH/MDAw/y8vL/8vLy//Li4u/y8v
L/8uLy7/Li0t/y0tLP8rKij/F4GW/ygmJ/8mJib/DQ0NZg4ODlkNDQ0zBwcHDgAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAJCPkP9dW1v/XV1c/1tbW/9ZWVn/WFhY/1ZWVv9TVFP/UlJR/09PT/9OTk3/S0xL/0pK
Sv9ISEj/R0ZH/0VERf9CQkP/QUFC/0BAQP8/Pz//Pj4+/z09Pf87Ozv/OTk6/zg4OP82Njf/NTU1/zMz
M/8yMjL/MTEx/y8wL/8vLy//Ly0t/xeAlf8sKCf/KScm/ykmJf8nJyf/DQ0NZg4ODlkNDQ0zBwcHDgAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAJKSkv9aWlr/W1tb/1paWv9ZWVj/V1dX/1ZVVv9UVFT/UlJR/1BP
UP9OTk7/TU1N/0xMTP9KSkr/SElI/0dHR/9GRkb/RERE/0JCQv9AQED/Pz8//z4+Pv89PT3/Ozs7/zk5
Of84ODj/Nzc2/zQ0NP8zMzP/MjIy/zAwMP8vLy//Lyws/y4qK/8tKCf/GI6q/ygnJv8pKCn/DQ0NZA4O
DlcNDQ0yCAgIDgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk/9ZWFj/Wlpa/1lZWf9YWFj/V1dW/1VV
VP9TU1P/UVFR/1BPUP9PT0//TU5O/0xMTP9LSkv/SUlJ/0hISP9GRkb/RERE/0NCQv9BQUH/QD8//z4/
Pv89PT7/Ozw8/zo6Ov85OTn/Nzg3/zU1NP8zMzP/MTIy/zAwMP8vLy//Li0t/xeAlf8tJyj/KyYm/ygk
Jf8rKyv/DQ0NVw0NDUwNDQ0sCQkJDAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk/9XV1f/WVhY/1hY
V/9XV1f/VlZW/1VVVf9SUlL/UFFR/09PT/9OTk7/TU1N/0xMTP9LSkr/SUlJ/0dHR/9GRkb/REVE/0JC
Qv9BQUD/Pz8//z8/Pv89PT3/Ozw8/zo6Ov84ODj/Nzc2/zQ0Nf8zMzP/MTIy/zAwL/8vLy//Li0t/y0s
LP8rKSj/GI6q/yclJf8tLS3/DAwMMg0NDSwLCwsaCAgIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKS
kv9UVVX/VVVV/1RVVf9TU1P/UlJS/1FRUf9PT0//Tk5O/01NTf9MTEz/SktL/0tJSf9JSUn/SEhI/0ZG
R/9FRUX/Q0ND/0FBQf9APz//Pj4+/z0+Pf88PDz/Ojo6/zk5Of84ODj/NjY2/zM0M/8yMjH/MDAw/y8v
L/8uLi7/LS0t/y0tLf8rKSn/KSYm/yclJP8vLy//BwcHDgkJCQwICAgHAAAAAgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAJaVlf+TkpL/kpKS/4+Qj/+NjY3/iYmJ/4eHhv+FhYX/goKD/3+Af/99fHz/enl5/3Z2
dv90dHT/cnJy/3Bwb/9ra2v/aGho/2VmZf9jZGP/YWFh/15eX/9cXFz/WFhY/1VUVf9TUlL/UFBQ/01N
Tf9LS0v/SEhI/0NEQ/9BQUH/Pz8//zw9PP85OTn/NjY2/zIyMv8xMTH/AAAAAQAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAf
AAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAf
AAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgA
AAAAHwAA+AAAAAAfAAD4AAAAAB8AAPgAAAAAHwAA+AAAAAAfAAD4AAAAAB8AAP///////wAA////////
AAD///////8AAP///////wAA////////AAA=
</value>
</data>
</root>

View File

@@ -0,0 +1,168 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.ObjectModel;
using System.Drawing;
namespace LibreHardwareMonitor.UI;
public class Node
{
private Node _parent;
private readonly NodeCollection _nodes;
private string _text;
private bool _visible;
public delegate void NodeEventHandler(Node node);
public event NodeEventHandler IsVisibleChanged;
public event NodeEventHandler NodeAdded;
public event NodeEventHandler NodeRemoved;
private TreeModel RootTreeModel()
{
Node node = this;
while (node != null)
{
if (node.Model != null)
return node.Model;
node = node._parent;
}
return null;
}
public Node() : this(string.Empty) { }
public Node(string text)
{
_text = text;
_nodes = new NodeCollection(this);
_visible = true;
}
public TreeModel Model { get; set; }
public Node Parent
{
get { return _parent; }
set
{
if (value != _parent)
{
_parent?._nodes.Remove(this);
value?._nodes.Add(this);
}
}
}
public Collection<Node> Nodes
{
get { return _nodes; }
}
public virtual string Text
{
get { return _text; }
set
{
_text = value;
}
}
public virtual string ToolTip { get; }
public Image Image { get; set; }
public virtual bool IsVisible
{
get { return _visible; }
set
{
if (value != _visible)
{
_visible = value;
TreeModel model = RootTreeModel();
if (model != null && _parent != null)
{
int index = 0;
for (int i = 0; i < _parent._nodes.Count; i++)
{
Node node = _parent._nodes[i];
if (node == this)
break;
if (node.IsVisible || model.ForceVisible)
index++;
}
if (model.ForceVisible)
{
model.OnNodeChanged(_parent, index, this);
}
else
{
if (value)
model.OnNodeInserted(_parent, index, this);
else
model.OnNodeRemoved(_parent, index, this);
}
}
IsVisibleChanged?.Invoke(this);
}
}
}
private class NodeCollection : Collection<Node>
{
private readonly Node _owner;
public NodeCollection(Node owner)
{
_owner = owner;
}
protected override void ClearItems()
{
while (Count != 0)
RemoveAt(Count - 1);
}
protected override void InsertItem(int index, Node item)
{
if (item == null)
throw new ArgumentNullException(nameof(item));
if (item._parent != _owner)
{
item._parent?._nodes.Remove(item);
item._parent = _owner;
base.InsertItem(index, item);
TreeModel model = _owner.RootTreeModel();
model?.OnStructureChanged(_owner);
_owner.NodeAdded?.Invoke(item);
}
}
protected override void RemoveItem(int index)
{
Node item = this[index];
item._parent = null;
base.RemoveItem(index);
TreeModel model = _owner.RootTreeModel();
model?.OnStructureChanged(_owner);
_owner.NodeRemoved?.Invoke(item);
}
protected override void SetItem(int index, Node item)
{
if (item == null)
throw new ArgumentNullException(nameof(item));
RemoveAt(index);
InsertItem(index, item);
}
}
}

View File

@@ -0,0 +1,14 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
using Aga.Controls.Tree;
using Aga.Controls.Tree.NodeControls;
namespace LibreHardwareMonitor.UI;
internal class NodeToolTipProvider : IToolTipProvider
{
public string GetToolTip(TreeNodeAdv node, NodeControl nodeControl) => (node.Tag as Node)?.ToolTip;
}

View File

@@ -0,0 +1,861 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI;
public class NotifyIconAdv : IDisposable
{
private readonly NotifyIcon _genericNotifyIcon;
private readonly NotifyIconWindowsImplementation _windowsNotifyIcon;
public NotifyIconAdv()
{
if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix)
_genericNotifyIcon = new NotifyIcon();
else
_windowsNotifyIcon = new NotifyIconWindowsImplementation();
}
public event EventHandler BalloonTipClicked
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipClicked += value;
else
_windowsNotifyIcon.BalloonTipClicked += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipClicked -= value;
else
_windowsNotifyIcon.BalloonTipClicked -= value;
}
}
public event EventHandler BalloonTipClosed
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipClosed += value;
else
_windowsNotifyIcon.BalloonTipClosed += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipClosed -= value;
else
_windowsNotifyIcon.BalloonTipClosed -= value;
}
}
public event EventHandler BalloonTipShown
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipShown += value;
else
_windowsNotifyIcon.BalloonTipShown += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipShown -= value;
else
_windowsNotifyIcon.BalloonTipShown -= value;
}
}
public event EventHandler Click
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.Click += value;
else
_windowsNotifyIcon.Click += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.Click -= value;
else
_windowsNotifyIcon.Click -= value;
}
}
public event EventHandler DoubleClick
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.DoubleClick += value;
else
_windowsNotifyIcon.DoubleClick += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.DoubleClick -= value;
else
_windowsNotifyIcon.DoubleClick -= value;
}
}
public event MouseEventHandler MouseClick
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseClick += value;
else
_windowsNotifyIcon.MouseClick += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseClick -= value;
else
_windowsNotifyIcon.MouseClick -= value;
}
}
public event MouseEventHandler MouseDoubleClick
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseDoubleClick += value;
else
_windowsNotifyIcon.MouseDoubleClick += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseDoubleClick -= value;
else
_windowsNotifyIcon.MouseDoubleClick -= value;
}
}
public event MouseEventHandler MouseDown
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseDown += value;
else
_windowsNotifyIcon.MouseDown += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseDown -= value;
else
_windowsNotifyIcon.MouseDown -= value;
}
}
public event MouseEventHandler MouseMove
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseMove += value;
else
_windowsNotifyIcon.MouseMove += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseMove -= value;
else
_windowsNotifyIcon.MouseMove -= value;
}
}
public event MouseEventHandler MouseUp
{
add
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseUp += value;
else
_windowsNotifyIcon.MouseUp += value;
}
remove
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.MouseUp -= value;
else
_windowsNotifyIcon.MouseUp -= value;
}
}
public string BalloonTipText
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.BalloonTipText;
return _windowsNotifyIcon.BalloonTipText;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipText = value;
else
_windowsNotifyIcon.BalloonTipText = value;
}
}
public ToolTipIcon BalloonTipIcon
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.BalloonTipIcon;
return _windowsNotifyIcon.BalloonTipIcon;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipIcon = value;
else
_windowsNotifyIcon.BalloonTipIcon = value;
}
}
public string BalloonTipTitle
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.BalloonTipTitle;
return _windowsNotifyIcon.BalloonTipTitle;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.BalloonTipTitle = value;
else
_windowsNotifyIcon.BalloonTipTitle = value;
}
}
public ContextMenuStrip ContextMenuStrip
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.ContextMenuStrip;
return _windowsNotifyIcon.ContextMenuStrip;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.ContextMenuStrip = value;
else
_windowsNotifyIcon.ContextMenuStrip = value;
}
}
public object Tag { get; set; }
public Icon Icon
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.Icon;
return _windowsNotifyIcon.Icon;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.Icon = value;
else
_windowsNotifyIcon.Icon = value;
}
}
public string Text
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.Text;
return _windowsNotifyIcon.Text;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.Text = value;
else
_windowsNotifyIcon.Text = value;
}
}
public bool Visible
{
get
{
if (_genericNotifyIcon != null)
return _genericNotifyIcon.Visible;
return _windowsNotifyIcon.Visible;
}
set
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.Visible = value;
else
_windowsNotifyIcon.Visible = value;
}
}
public void Dispose()
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.Dispose();
else
_windowsNotifyIcon.Dispose();
}
public void ShowBalloonTip(int timeout)
{
ShowBalloonTip(timeout, BalloonTipTitle, BalloonTipText, BalloonTipIcon);
}
public void ShowBalloonTip(int timeout, string tipTitle, string tipText,
ToolTipIcon tipIcon)
{
if (_genericNotifyIcon != null)
_genericNotifyIcon.ShowBalloonTip(timeout, tipTitle, tipText, tipIcon);
else
_windowsNotifyIcon.ShowBalloonTip(timeout, tipTitle, tipText, tipIcon);
}
private class NotifyIconWindowsImplementation : Component
{
private static int _nextId;
private readonly object _syncObj = new object();
private Icon _icon;
private string _text = "";
private readonly int _id;
private bool _created;
private NotifyIconNativeWindow _window;
private bool _doubleClickDown;
private bool _visible;
private readonly MethodInfo _commandDispatch;
public event EventHandler BalloonTipClicked;
public event EventHandler BalloonTipClosed;
public event EventHandler BalloonTipShown;
public event EventHandler Click;
public event EventHandler DoubleClick;
public event MouseEventHandler MouseClick;
public event MouseEventHandler MouseDoubleClick;
public event MouseEventHandler MouseDown;
public event MouseEventHandler MouseMove;
public event MouseEventHandler MouseUp;
public string BalloonTipText { get; set; }
public ToolTipIcon BalloonTipIcon { get; set; }
public string BalloonTipTitle { get; set; }
public ContextMenuStrip ContextMenuStrip { get; set; }
public Icon Icon
{
get
{
return _icon;
}
set
{
if (_icon != value)
{
_icon = value;
UpdateNotifyIcon(_visible);
}
}
}
public string Text
{
get
{
return _text;
}
set
{
if (value == null)
value = "";
if (value.Length > 63)
throw new ArgumentOutOfRangeException();
if (!value.Equals(_text))
{
_text = value;
if (_visible)
UpdateNotifyIcon(_visible);
}
}
}
public bool Visible
{
get
{
return _visible;
}
set
{
if (_visible != value)
{
_visible = value;
UpdateNotifyIcon(_visible);
}
}
}
public NotifyIconWindowsImplementation()
{
BalloonTipText = "";
BalloonTipTitle = "";
_commandDispatch = typeof(Form).Assembly.
GetType("System.Windows.Forms.Command").GetMethod("DispatchID",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
null, new[] { typeof(int) }, null);
_id = ++_nextId;
_window = new NotifyIconNativeWindow(this);
UpdateNotifyIcon(_visible);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_window != null)
{
_icon = null;
_text = "";
UpdateNotifyIcon(false);
_window.DestroyHandle();
_window = null;
ContextMenuStrip = null;
}
}
else
{
if (_window != null && _window.Handle != IntPtr.Zero)
{
NativeMethods.PostMessage(new HandleRef(_window, _window.Handle), WM_CLOSE, 0, 0);
_window.ReleaseHandle();
}
}
base.Dispose(disposing);
}
public void ShowBalloonTip(int timeout)
{
ShowBalloonTip(timeout, BalloonTipTitle, BalloonTipText, BalloonTipIcon);
}
public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
{
if (timeout < 0)
throw new ArgumentOutOfRangeException(nameof(timeout));
if (string.IsNullOrEmpty(tipText))
throw new ArgumentException("tipText");
if (DesignMode)
return;
if (_created)
{
NativeMethods.NotifyIconData data = new NativeMethods.NotifyIconData();
if (_window.Handle == IntPtr.Zero)
_window.CreateHandle(new CreateParams());
data.Window = _window.Handle;
data.ID = _id;
data.Flags = NativeMethods.NotifyIconDataFlags.Info;
data.TimeoutOrVersion = timeout;
data.InfoTitle = tipTitle;
data.Info = tipText;
data.InfoFlags = (int)tipIcon;
NativeMethods.Shell_NotifyIcon(NativeMethods.NotifyIconMessage.Modify, data);
}
}
private void ShowContextMenu()
{
if (ContextMenuStrip == null)
return;
NativeMethods.Point p = new NativeMethods.Point();
NativeMethods.GetCursorPos(ref p);
NativeMethods.SetForegroundWindow(new HandleRef(_window, _window.Handle));
ContextMenuStrip?.GetType().InvokeMember("ShowInTaskbar",
BindingFlags.NonPublic | BindingFlags.InvokeMethod |
BindingFlags.Instance, null, ContextMenuStrip, new object[] { p.X, p.Y });
}
private void UpdateNotifyIcon(bool showNotifyIcon)
{
if (DesignMode)
return;
lock (_syncObj)
{
_window.LockReference(showNotifyIcon);
NativeMethods.NotifyIconData data = new NativeMethods.NotifyIconData { CallbackMessage = WM_TRAYMOUSEMESSAGE, Flags = NativeMethods.NotifyIconDataFlags.Message };
if (showNotifyIcon && _window.Handle == IntPtr.Zero)
_window.CreateHandle(new CreateParams());
data.Window = _window.Handle;
data.ID = _id;
if (_icon != null)
{
data.Flags |= NativeMethods.NotifyIconDataFlags.Icon;
data.Icon = _icon.Handle;
}
data.Flags |= NativeMethods.NotifyIconDataFlags.Tip;
data.Tip = _text;
if (showNotifyIcon && _icon != null)
{
if (!_created)
{
if (NativeMethods.Shell_NotifyIcon(NativeMethods.NotifyIconMessage.Modify, data))
{
_created = true;
}
else
{
int i = 0;
do
{
_created = NativeMethods.Shell_NotifyIcon(NativeMethods.NotifyIconMessage.Add, data);
if (!_created)
{
System.Threading.Thread.Sleep(200);
i++;
}
} while (!_created && i < 40);
}
}
else
{
NativeMethods.Shell_NotifyIcon(NativeMethods.NotifyIconMessage.Modify, data);
}
}
else
{
if (_created)
{
int i = 0;
bool deleted;
do
{
deleted = NativeMethods.Shell_NotifyIcon(NativeMethods.NotifyIconMessage.Delete, data);
if (!deleted)
{
System.Threading.Thread.Sleep(200);
i++;
}
} while (!deleted && i < 40);
_created = false;
}
}
}
}
private void ProcessMouseDown(MouseButtons button, bool doubleClick)
{
if (doubleClick)
{
DoubleClick?.Invoke(this, new MouseEventArgs(button, 2, 0, 0, 0));
MouseDoubleClick?.Invoke(this, new MouseEventArgs(button, 2, 0, 0, 0));
_doubleClickDown = true;
}
MouseDown?.Invoke(this, new MouseEventArgs(button, doubleClick ? 2 : 1, 0, 0, 0));
}
private void ProcessMouseUp(MouseButtons button)
{
MouseUp?.Invoke(this, new MouseEventArgs(button, 0, 0, 0, 0));
if (!_doubleClickDown)
{
Click?.Invoke(this, new MouseEventArgs(button, 0, 0, 0, 0));
MouseClick?.Invoke(this, new MouseEventArgs(button, 0, 0, 0, 0));
}
_doubleClickDown = false;
}
private void ProcessInitMenuPopup(ref Message message)
{
_window.DefWndProc(ref message);
}
private void WndProc(ref Message message)
{
switch (message.Msg)
{
case WM_DESTROY:
UpdateNotifyIcon(false);
return;
case WM_COMMAND:
if (message.LParam != IntPtr.Zero)
{
_window.DefWndProc(ref message);
return;
}
_commandDispatch.Invoke(null, new object[] { message.WParam.ToInt32() & 0xFFFF });
return;
case WM_INITMENUPOPUP:
ProcessInitMenuPopup(ref message);
return;
case WM_TRAYMOUSEMESSAGE:
switch ((int)message.LParam)
{
case WM_MOUSEMOVE:
MouseMove?.Invoke(this,
new MouseEventArgs(Control.MouseButtons, 0, 0, 0, 0));
return;
case WM_LBUTTONDOWN:
ProcessMouseDown(MouseButtons.Left, false);
return;
case WM_LBUTTONUP:
ProcessMouseUp(MouseButtons.Left);
return;
case WM_LBUTTONDBLCLK:
ProcessMouseDown(MouseButtons.Left, true);
return;
case WM_RBUTTONDOWN:
ProcessMouseDown(MouseButtons.Right, false);
return;
case WM_RBUTTONUP:
if (ContextMenuStrip != null)
ShowContextMenu();
ProcessMouseUp(MouseButtons.Right);
return;
case WM_RBUTTONDBLCLK:
ProcessMouseDown(MouseButtons.Right, true);
return;
case WM_MBUTTONDOWN:
ProcessMouseDown(MouseButtons.Middle, false);
return;
case WM_MBUTTONUP:
ProcessMouseUp(MouseButtons.Middle);
return;
case WM_MBUTTONDBLCLK:
ProcessMouseDown(MouseButtons.Middle, true);
return;
case NIN_BALLOONSHOW:
BalloonTipShown?.Invoke(this, EventArgs.Empty);
return;
case NIN_BALLOONHIDE:
case NIN_BALLOONTIMEOUT:
BalloonTipClosed?.Invoke(this, EventArgs.Empty);
return;
case NIN_BALLOONUSERCLICK:
BalloonTipClicked?.Invoke(this, EventArgs.Empty);
return;
default:
return;
}
}
if (message.Msg == _wmTaskBarCreated)
{
lock (_syncObj)
{
_created = false;
}
UpdateNotifyIcon(_visible);
}
_window.DefWndProc(ref message);
}
private class NotifyIconNativeWindow : NativeWindow
{
private readonly NotifyIconWindowsImplementation _reference;
private GCHandle _referenceHandle;
internal NotifyIconNativeWindow(NotifyIconWindowsImplementation component)
{
_reference = component;
}
~NotifyIconNativeWindow()
{
if (Handle != IntPtr.Zero)
NativeMethods.PostMessage(new HandleRef(this, Handle), WM_CLOSE, 0, 0);
}
public void LockReference(bool locked)
{
if (locked)
{
if (!_referenceHandle.IsAllocated)
{
_referenceHandle = GCHandle.Alloc(_reference, GCHandleType.Normal);
}
}
else
{
if (_referenceHandle.IsAllocated)
_referenceHandle.Free();
}
}
protected override void OnThreadException(Exception e)
{
Application.OnThreadException(e);
}
protected override void WndProc(ref Message m)
{
_reference.WndProc(ref m);
}
}
private const int WM_NULL = 0x00;
private const int WM_DESTROY = 0x02;
private const int WM_CLOSE = 0x10;
private const int WM_COMMAND = 0x111;
private const int WM_INITMENUPOPUP = 0x117;
private const int WM_MOUSEMOVE = 0x200;
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_LBUTTONUP = 0x202;
private const int WM_LBUTTONDBLCLK = 0x203;
private const int WM_RBUTTONDOWN = 0x204;
private const int WM_RBUTTONUP = 0x205;
private const int WM_RBUTTONDBLCLK = 0x206;
private const int WM_MBUTTONDOWN = 0x207;
private const int WM_MBUTTONUP = 0x208;
private const int WM_MBUTTONDBLCLK = 0x209;
private const int WM_TRAYMOUSEMESSAGE = 0x800;
private const int NIN_BALLOONSHOW = 0x402;
private const int NIN_BALLOONHIDE = 0x403;
private const int NIN_BALLOONTIMEOUT = 0x404;
private const int NIN_BALLOONUSERCLICK = 0x405;
private static readonly int _wmTaskBarCreated = NativeMethods.RegisterWindowMessage("TaskbarCreated");
private static class NativeMethods
{
private const string DllNameUser32 = "user32.dll";
private const string DllNameShell32 = "shell32.dll";
[DllImport(DllNameUser32, CharSet = CharSet.Auto)]
public static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);
[DllImport(DllNameUser32, CharSet = CharSet.Auto)]
public static extern int RegisterWindowMessage(string msg);
[Flags]
public enum NotifyIconDataFlags : int
{
Message = 0x1,
Icon = 0x2,
Tip = 0x4,
State = 0x8,
Info = 0x10
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class NotifyIconData
{
private int _size = Marshal.SizeOf(typeof(NotifyIconData));
public IntPtr Window;
public int ID;
public NotifyIconDataFlags Flags;
public int CallbackMessage;
public IntPtr Icon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string Tip;
public int State;
public int StateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Info;
public int TimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string InfoTitle;
public int InfoFlags;
}
public enum NotifyIconMessage : int
{
Add = 0x0,
Modify = 0x1,
Delete = 0x2
}
[DllImport(DllNameShell32, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Shell_NotifyIcon(NotifyIconMessage message, NotifyIconData pnid);
[DllImport(DllNameUser32, CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool TrackPopupMenuEx(HandleRef hmenu, int fuFlags, int x, int y, HandleRef hwnd, IntPtr tpm);
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public readonly int X;
public readonly int Y;
}
[DllImport(DllNameUser32, CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool GetCursorPos(ref Point point);
[DllImport(DllNameUser32, CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool SetForegroundWindow(HandleRef hWnd);
}
}
}

View File

@@ -0,0 +1,202 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
namespace LibreHardwareMonitor.UI
{
partial class ParameterForm
{
/// <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();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
this.okButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.captionLabel = new System.Windows.Forms.Label();
this.dataGridView = new System.Windows.Forms.DataGridView();
this.NameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Default = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.ValueColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.bindingSource = new System.Windows.Forms.BindingSource(this.components);
this.descriptionLabel = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.bindingSource)).BeginInit();
this.SuspendLayout();
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(186, 213);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 2;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.OkButton_Click);
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(267, 213);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 3;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// captionLabel
//
this.captionLabel.AutoSize = true;
this.captionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.captionLabel.Location = new System.Drawing.Point(12, 9);
this.captionLabel.Name = "captionLabel";
this.captionLabel.Size = new System.Drawing.Size(80, 13);
this.captionLabel.TabIndex = 4;
this.captionLabel.Text = "captionLabel";
//
// dataGridView
//
this.dataGridView.AllowUserToAddRows = false;
this.dataGridView.AllowUserToDeleteRows = false;
this.dataGridView.AllowUserToResizeRows = false;
this.dataGridView.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.dataGridView.AutoGenerateColumns = false;
this.dataGridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridView.BackgroundColor = System.Drawing.SystemColors.Window;
this.dataGridView.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.NameColumn,
this.Default,
this.ValueColumn});
this.dataGridView.DataSource = this.bindingSource;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240)))));
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridView.DefaultCellStyle = dataGridViewCellStyle2;
this.dataGridView.Location = new System.Drawing.Point(15, 30);
this.dataGridView.MultiSelect = false;
this.dataGridView.Name = "dataGridView";
this.dataGridView.RowHeadersVisible = false;
this.dataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView.ShowRowErrors = false;
this.dataGridView.Size = new System.Drawing.Size(327, 121);
this.dataGridView.TabIndex = 0;
this.dataGridView.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView_CellEndEdit);
this.dataGridView.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.DataGridView_CellValidating);
this.dataGridView.CurrentCellDirtyStateChanged += new System.EventHandler(this.DataGridView_CurrentCellDirtyStateChanged);
this.dataGridView.RowEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView_RowEnter);
//
// NameColumn
//
this.NameColumn.DataPropertyName = "Name";
this.NameColumn.HeaderText = "Name";
this.NameColumn.Name = "NameColumn";
this.NameColumn.ReadOnly = true;
this.NameColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// Default
//
this.Default.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
this.Default.DataPropertyName = "Default";
this.Default.HeaderText = "Default";
this.Default.Name = "Default";
this.Default.Width = 47;
//
// ValueColumn
//
this.ValueColumn.DataPropertyName = "Value";
this.ValueColumn.HeaderText = "Value";
this.ValueColumn.Name = "ValueColumn";
this.ValueColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// bindingSource
//
this.bindingSource.AllowNew = false;
//
// descriptionLabel
//
this.descriptionLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.descriptionLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.descriptionLabel.Location = new System.Drawing.Point(15, 154);
this.descriptionLabel.Name = "descriptionLabel";
this.descriptionLabel.Padding = new System.Windows.Forms.Padding(2);
this.descriptionLabel.Size = new System.Drawing.Size(327, 50);
this.descriptionLabel.TabIndex = 6;
this.descriptionLabel.Text = "descriptionLabel";
//
// ParameterForm
//
this.AcceptButton = this.okButton;
this.AccessibleName = "";
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(354, 248);
this.Controls.Add(this.descriptionLabel);
this.Controls.Add(this.dataGridView);
this.Controls.Add(this.captionLabel);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.okButton);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ParameterForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Parameters";
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.bindingSource)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Button cancelButton;
public System.Windows.Forms.Label captionLabel;
private System.Windows.Forms.DataGridView dataGridView;
private System.Windows.Forms.Label descriptionLabel;
private System.Windows.Forms.BindingSource bindingSource;
private System.Windows.Forms.DataGridViewTextBoxColumn NameColumn;
private System.Windows.Forms.DataGridViewCheckBoxColumn Default;
private System.Windows.Forms.DataGridViewTextBoxColumn ValueColumn;
}
}

View File

@@ -0,0 +1,135 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using LibreHardwareMonitor.Hardware;
namespace LibreHardwareMonitor.UI;
public partial class ParameterForm : Form
{
private IReadOnlyList<IParameter> _parameters;
private BindingList<ParameterRow> _parameterRows;
public ParameterForm()
{
InitializeComponent();
}
public IReadOnlyList<IParameter> Parameters
{
get
{
return _parameters;
}
set
{
_parameters = value;
_parameterRows = new BindingList<ParameterRow>();
foreach (IParameter parameter in _parameters)
{
_parameterRows.Add(new ParameterRow(parameter));
}
bindingSource.DataSource = _parameterRows;
}
}
private class ParameterRow : INotifyPropertyChanged
{
public readonly IParameter Parameter;
private float _value = float.NaN;
private bool _default = true;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ParameterRow(IParameter parameter)
{
Parameter = parameter;
Value = parameter.Value;
Default = parameter.IsDefault;
}
public string Name
{
get { return Parameter.Name; }
}
public float Value
{
get { return _value; }
set
{
_default = false;
_value = value;
NotifyPropertyChanged(nameof(Default));
NotifyPropertyChanged(nameof(Value));
}
}
public bool Default
{
get { return _default; }
set
{
_default = value;
if (_default)
_value = Parameter.DefaultValue;
NotifyPropertyChanged(nameof(Default));
NotifyPropertyChanged(nameof(Value));
}
}
}
private void DataGridView_RowEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && e.RowIndex < _parameters.Count)
descriptionLabel.Text = _parameters[e.RowIndex].Description;
else
descriptionLabel.Text = string.Empty;
}
private void DataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (e.ColumnIndex == 2 && !float.TryParse(e.FormattedValue.ToString(), out float _))
{
dataGridView.Rows[e.RowIndex].Cells[0].ErrorText = "Invalid value";
e.Cancel = true;
}
}
private void DataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
dataGridView.Rows[e.RowIndex].Cells[0].ErrorText = string.Empty;
}
private void OkButton_Click(object sender, EventArgs e)
{
foreach (ParameterRow row in _parameterRows)
{
if (row.Default)
row.Parameter.IsDefault = true;
else
row.Parameter.Value = row.Value;
}
}
private void DataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dataGridView.CurrentCell is DataGridViewCheckBoxCell || dataGridView.CurrentCell is DataGridViewComboBoxCell)
dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}

View File

@@ -0,0 +1,132 @@
<?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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="NameColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Default.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ValueColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="bindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -0,0 +1,459 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
using OxyPlot;
using OxyPlot.Annotations;
using OxyPlot.Axes;
using OxyPlot.WindowsForms;
using OxyPlot.Series;
using LibreHardwareMonitor.UI.Themes;
namespace LibreHardwareMonitor.UI;
public class PlotPanel : UserControl
{
private readonly PersistentSettings _settings;
private readonly UnitManager _unitManager;
private readonly PlotView _plot;
private readonly PlotModel _model;
private readonly TimeSpanAxis _timeAxis = new TimeSpanAxis();
private readonly SortedDictionary<SensorType, LinearAxis> _axes = new SortedDictionary<SensorType, LinearAxis>();
private readonly Dictionary<SensorType, LineAnnotation> _annotations = new Dictionary<SensorType, LineAnnotation>();
private UserOption _stackedAxes;
private UserOption _showAxesLabels;
private UserOption _timeAxisEnableZoom;
private UserOption _yAxesEnableZoom;
private DateTime _now;
private float _dpiX;
private float _dpiY;
private double _dpiXScale = 1;
private double _dpiYScale = 1;
private Point _rightClickEnter;
private bool _cancelContextMenu = false;
public PlotPanel(PersistentSettings settings, UnitManager unitManager)
{
_settings = settings;
_unitManager = unitManager;
SetDpi();
_model = CreatePlotModel();
_plot = new PlotView { Dock = DockStyle.Fill, Model = _model, BackColor = Color.Black, ContextMenuStrip = CreateMenu() };
_plot.MouseDown += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
_rightClickEnter = e.Location;
}
};
_plot.MouseMove += (sender, e) =>
{
if (e.Button == MouseButtons.Right)
{
if (!_cancelContextMenu && e.Location.DistanceTo(_rightClickEnter) > 10.0f)
{
_cancelContextMenu = true;
}
}
};
UpdateAxesPosition();
SuspendLayout();
Controls.Add(_plot);
ResumeLayout(true);
_plot.ShowTracker(new TrackerHitResult());
_plot.HideTracker();
foreach (Control plotControl in _plot.Controls)
{
plotControl.BackColor = Theme.Current.PlotBackgroundColor;
plotControl.ForeColor = Theme.Current.PlotTextColor;
}
ApplyTheme();
}
public void ApplyTheme()
{
_model.Background = Theme.Current.PlotBackgroundColor.ToOxyColor();
_model.PlotAreaBorderColor = Theme.Current.PlotBorderColor.ToOxyColor();
foreach (Axis axis in _model.Axes)
{
axis.AxislineColor = Theme.Current.PlotBorderColor.ToOxyColor();
axis.MajorGridlineColor = Theme.Current.PlotGridMajorColor.ToOxyColor();
axis.MinorGridlineColor = Theme.Current.PlotGridMinorColor.ToOxyColor();
axis.TextColor = Theme.Current.PlotTextColor.ToOxyColor();
axis.TitleColor = Theme.Current.PlotTextColor.ToOxyColor();
axis.MinorTicklineColor = Theme.Current.PlotBorderColor.ToOxyColor();
axis.TicklineColor = Theme.Current.PlotBorderColor.ToOxyColor();
}
foreach (LineAnnotation annotation in _model.Annotations.Select(x => x as LineAnnotation).Where(x => x != null))
{
annotation.Color = Theme.Current.PlotBorderColor.ToOxyColor();
}
}
public void SetCurrentSettings()
{
foreach (LinearAxis axis in _axes.Values)
{
_settings.SetValue("plotPanel.Min" + axis.Key, (float)axis.ActualMinimum);
_settings.SetValue("plotPanel.Max" + axis.Key, (float)axis.ActualMaximum);
}
_settings.SetValue("plotPanel.MinTimeSpan", (float)_timeAxis.ActualMinimum);
_settings.SetValue("plotPanel.MaxTimeSpan", (float)_timeAxis.ActualMaximum);
}
private ContextMenuStrip CreateMenu()
{
ContextMenuStrip menu = new ContextMenuStrip();
menu.Renderer = new ThemedToolStripRenderer();
menu.Opening += (sender, e) =>
{
if (_cancelContextMenu)
{
e.Cancel = true;
_cancelContextMenu = false;
}
};
ToolStripMenuItem stackedAxesMenuItem = new ToolStripMenuItem("Stacked Axes");
_stackedAxes = new UserOption("stackedAxes", true, stackedAxesMenuItem, _settings);
_stackedAxes.Changed += (sender, e) =>
{
UpdateAxesPosition();
InvalidatePlot();
};
menu.Items.Add(stackedAxesMenuItem);
ToolStripMenuItem showAxesLabelsMenuItem = new ToolStripMenuItem("Show Axes Labels");
_showAxesLabels = new UserOption("showAxesLabels", true, showAxesLabelsMenuItem, _settings);
_showAxesLabels.Changed += (sender, e) =>
{
if (_showAxesLabels.Value)
_model.PlotMargins = new OxyThickness(double.NaN);
else
_model.PlotMargins = new OxyThickness(0);
};
menu.Items.Add(showAxesLabelsMenuItem);
ToolStripMenuItem timeAxisMenuItem = new ToolStripMenuItem("Time Axis");
ToolStripMenuItem[] timeAxisMenuItems =
{ new ToolStripMenuItem("Enable Zoom"),
new ToolStripMenuItem("Auto", null, (s, e) => { TimeAxisZoom(0, double.NaN); }),
new ToolStripMenuItem("5 min", null, (s, e) => { TimeAxisZoom(0, 5 * 60); }),
new ToolStripMenuItem("10 min", null, (s, e) => { TimeAxisZoom(0, 10 * 60); }),
new ToolStripMenuItem("20 min", null, (s, e) => { TimeAxisZoom(0, 20 * 60); }),
new ToolStripMenuItem("30 min", null, (s, e) => { TimeAxisZoom(0, 30 * 60); }),
new ToolStripMenuItem("45 min", null, (s, e) => { TimeAxisZoom(0, 45 * 60); }),
new ToolStripMenuItem("1 h", null, (s, e) => { TimeAxisZoom(0, 60 * 60); }),
new ToolStripMenuItem("1.5 h", null, (s, e) => { TimeAxisZoom(0, 1.5 * 60 * 60); }),
new ToolStripMenuItem("2 h", null, (s, e) => { TimeAxisZoom(0, 2 * 60 * 60); }),
new ToolStripMenuItem("3 h", null, (s, e) => { TimeAxisZoom(0, 3 * 60 * 60); }),
new ToolStripMenuItem("6 h", null, (s, e) => { TimeAxisZoom(0, 6 * 60 * 60); }),
new ToolStripMenuItem("12 h", null, (s, e) => { TimeAxisZoom(0, 12 * 60 * 60); }),
new ToolStripMenuItem("24 h", null, (s, e) => { TimeAxisZoom(0, 24 * 60 * 60); }) };
foreach (ToolStripItem mi in timeAxisMenuItems)
timeAxisMenuItem.DropDownItems.Add(mi);
menu.Items.Add(timeAxisMenuItem);
_timeAxisEnableZoom = new UserOption("timeAxisEnableZoom", true, timeAxisMenuItems[0], _settings);
_timeAxisEnableZoom.Changed += (sender, e) =>
{
_timeAxis.IsZoomEnabled = _timeAxisEnableZoom.Value;
};
ToolStripMenuItem yAxesMenuItem = new ToolStripMenuItem("Value Axes");
ToolStripMenuItem[] yAxesMenuItems =
{ new ToolStripMenuItem("Enable Zoom"),
new ToolStripMenuItem("Autoscale All", null, (s, e) => { AutoscaleAllYAxes(); }) };
foreach (ToolStripItem mi in yAxesMenuItems)
yAxesMenuItem.DropDownItems.Add(mi);
menu.Items.Add(yAxesMenuItem);
_yAxesEnableZoom = new UserOption("yAxesEnableZoom", true, yAxesMenuItems[0], _settings);
_yAxesEnableZoom.Changed += (sender, e) =>
{
foreach (LinearAxis axis in _axes.Values)
axis.IsZoomEnabled = _yAxesEnableZoom.Value;
};
return menu;
}
private PlotModel CreatePlotModel()
{
_timeAxis.Position = AxisPosition.Bottom;
_timeAxis.MajorGridlineStyle = LineStyle.Solid;
_timeAxis.MajorGridlineThickness = 1;
_timeAxis.MajorGridlineColor = OxyColor.FromRgb(192, 192, 192);
_timeAxis.MinorGridlineStyle = LineStyle.Solid;
_timeAxis.MinorGridlineThickness = 1;
_timeAxis.MinorGridlineColor = OxyColor.FromRgb(232, 232, 232);
_timeAxis.StartPosition = 1;
_timeAxis.EndPosition = 0;
_timeAxis.MinimumPadding = 0;
_timeAxis.MaximumPadding = 0;
_timeAxis.AbsoluteMinimum = 0;
_timeAxis.Minimum = 0;
_timeAxis.AbsoluteMaximum = 24 * 60 * 60;
_timeAxis.Zoom(
_settings.GetValue("plotPanel.MinTimeSpan", 0.0f),
_settings.GetValue("plotPanel.MaxTimeSpan", 10.0f * 60));
_timeAxis.StringFormat = "h:mm";
var units = new Dictionary<SensorType, string>
{
{ SensorType.Voltage, "V" },
{ SensorType.Current, "A" },
{ SensorType.Clock, "MHz" },
{ SensorType.Temperature, "°C" },
{ SensorType.Load, "%" },
{ SensorType.Fan, "RPM" },
{ SensorType.Flow, "L/h" },
{ SensorType.Control, "%" },
{ SensorType.Level, "%" },
{ SensorType.Factor, "1" },
{ SensorType.Power, "W" },
{ SensorType.Data, "GB" },
{ SensorType.Frequency, "Hz" },
{ SensorType.Energy, "mWh" },
{ SensorType.Noise, "dBA" },
{ SensorType.Conductivity, "µS/cm" },
{ SensorType.Humidity, "%" }
};
foreach (SensorType type in Enum.GetValues(typeof(SensorType)))
{
string typeName = type.ToString();
var axis = new LinearAxis
{
Position = AxisPosition.Left,
MajorGridlineStyle = LineStyle.Solid,
MajorGridlineThickness = 1,
MajorGridlineColor = _timeAxis.MajorGridlineColor,
MinorGridlineStyle = LineStyle.Solid,
MinorGridlineThickness = 1,
MinorGridlineColor = _timeAxis.MinorGridlineColor,
AxislineStyle = LineStyle.Solid,
Title = typeName,
Key = typeName,
};
var annotation = new LineAnnotation
{
Type = LineAnnotationType.Horizontal,
ClipByXAxis = false,
ClipByYAxis = false,
LineStyle = LineStyle.Solid,
Color = Theme.Current.PlotBorderColor.ToOxyColor(),
YAxisKey = typeName,
StrokeThickness = 2,
};
axis.AxisChanged += (sender, args) => annotation.Y = axis.ActualMinimum;
axis.TransformChanged += (sender, args) => annotation.Y = axis.ActualMinimum;
axis.Zoom(_settings.GetValue("plotPanel.Min" + axis.Key, float.NaN), _settings.GetValue("plotPanel.Max" + axis.Key, float.NaN));
if (units.ContainsKey(type))
axis.Unit = units[type];
_axes.Add(type, axis);
_annotations.Add(type, annotation);
}
var model = new ScaledPlotModel(_dpiXScale, _dpiYScale);
model.Axes.Add(_timeAxis);
foreach (LinearAxis axis in _axes.Values)
model.Axes.Add(axis);
model.IsLegendVisible = false;
return model;
}
private void SetDpi()
{
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx
const int defaultDpi = 96;
Graphics g = CreateGraphics();
try
{
_dpiX = g.DpiX;
_dpiY = g.DpiY;
}
finally
{
g.Dispose();
}
if (_dpiX > 0)
_dpiXScale = _dpiX / defaultDpi;
if (_dpiY > 0)
_dpiYScale = _dpiY / defaultDpi;
}
public void SetSensors(List<ISensor> sensors, IDictionary<ISensor, Color> colors, double strokeThickness)
{
_model.Series.Clear();
var types = new HashSet<SensorType>();
DataPoint CreateDataPoint(SensorType type, SensorValue value)
{
float displayedValue;
if (type == SensorType.Temperature && _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
{
displayedValue = UnitManager.CelsiusToFahrenheit(value.Value).Value;
}
else
{
displayedValue = value.Value;
}
return new DataPoint((_now - value.Time).TotalSeconds, displayedValue);
}
foreach (ISensor sensor in sensors)
{
var series = new LineSeries
{
ItemsSource = sensor.Values.Select(value => CreateDataPoint(sensor.SensorType, value)),
Color = colors[sensor].ToOxyColor(),
StrokeThickness = strokeThickness,
YAxisKey = _axes[sensor.SensorType].Key,
Title = sensor.Hardware.Name + " " + sensor.Name
};
_model.Series.Add(series);
types.Add(sensor.SensorType);
}
foreach (KeyValuePair<SensorType, LinearAxis> pair in _axes.Reverse())
{
LinearAxis axis = pair.Value;
SensorType type = pair.Key;
axis.IsAxisVisible = types.Contains(type);
}
UpdateAxesPosition();
InvalidatePlot();
}
public void UpdateStrokeThickness(double strokeThickness)
{
foreach (LineSeries series in _model.Series)
{
series.StrokeThickness = strokeThickness;
}
InvalidatePlot();
}
private void UpdateAxesPosition()
{
if (_stackedAxes.Value)
{
int count = _axes.Values.Count(axis => axis.IsAxisVisible);
double start = 0.0;
foreach (KeyValuePair<SensorType, LinearAxis> pair in _axes.Reverse())
{
LinearAxis axis = pair.Value;
axis.StartPosition = start;
double delta = axis.IsAxisVisible ? 1.0 / count : 0;
start += delta;
axis.EndPosition = start;
axis.PositionTier = 0;
axis.MajorGridlineStyle = LineStyle.Solid;
axis.MinorGridlineStyle = LineStyle.Solid;
LineAnnotation annotation = _annotations[pair.Key];
annotation.Y = axis.ActualMinimum;
if (!_model.Annotations.Contains(annotation))
_model.Annotations.Add(annotation);
}
}
else
{
int tier = 0;
foreach (KeyValuePair<SensorType, LinearAxis> pair in _axes.Reverse())
{
LinearAxis axis = pair.Value;
if (axis.IsAxisVisible)
{
axis.StartPosition = 0;
axis.EndPosition = 1;
axis.PositionTier = tier;
tier++;
}
else
{
axis.StartPosition = 0;
axis.EndPosition = 0;
axis.PositionTier = 0;
}
axis.MajorGridlineStyle = LineStyle.None;
axis.MinorGridlineStyle = LineStyle.None;
LineAnnotation annotation = _annotations[pair.Key];
if (_model.Annotations.Contains(annotation))
_model.Annotations.Remove(_annotations[pair.Key]);
}
}
}
public void InvalidatePlot()
{
_now = DateTime.UtcNow;
if (_axes != null)
{
foreach (KeyValuePair<SensorType, LinearAxis> pair in _axes)
{
LinearAxis axis = pair.Value;
SensorType type = pair.Key;
if (type == SensorType.Temperature)
axis.Unit = _unitManager.TemperatureUnit == TemperatureUnit.Celsius ? "°C" : "°F";
if (!_stackedAxes.Value)
continue;
var annotation = _annotations[pair.Key];
annotation.Y = axis.ActualMaximum;
}
}
_plot?.InvalidatePlot(true);
}
public void TimeAxisZoom(double min, double max)
{
bool timeAxisIsZoomEnabled = _timeAxis.IsZoomEnabled;
_timeAxis.IsZoomEnabled = true;
_timeAxis.Zoom(min, max);
InvalidatePlot();
_timeAxis.IsZoomEnabled = timeAxisIsZoomEnabled;
}
public void AutoscaleAllYAxes()
{
foreach (LinearAxis axis in _axes.Values)
axis.Zoom(double.NaN, double.NaN);
}
}

View File

@@ -0,0 +1,33 @@
using OxyPlot;
using OxyPlot.Legends;
namespace LibreHardwareMonitor.UI;
class ScaledPlotModel : PlotModel
{
public ScaledPlotModel(double dpiXscale, double dpiYscale)
{
PlotMargins = new OxyThickness(PlotMargins.Left * dpiXscale,
PlotMargins.Top * dpiYscale,
PlotMargins.Right * dpiXscale,
PlotMargins.Bottom * dpiYscale);
Padding = new OxyThickness(Padding.Left * dpiXscale,
Padding.Top * dpiYscale,
Padding.Right * dpiXscale,
Padding.Bottom * dpiYscale);
TitlePadding *= dpiXscale;
Legend legend = new();
legend.LegendSymbolLength *= dpiXscale;
legend.LegendSymbolMargin *= dpiXscale;
legend.LegendPadding *= dpiXscale;
legend.LegendColumnSpacing *= dpiXscale;
legend.LegendItemSpacing *= dpiXscale;
legend.LegendMargin *= dpiXscale;
Legends.Add(legend);
}
}

View File

@@ -0,0 +1,765 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.IO;
using System.Linq;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public class SensorGadget : Gadget
{
private const int TopBorder = 6;
private const int BottomBorder = 7;
private const int LeftBorder = 6;
private const int RightBorder = 7;
private readonly UnitManager _unitManager;
private Image _back = Utilities.EmbeddedResources.GetImage("gadget.png");
private Image _image;
private Image _fore;
private Image _barBack = Utilities.EmbeddedResources.GetImage("barback.png");
private Image _barFore = Utilities.EmbeddedResources.GetImage("barblue.png");
private Image _background = new Bitmap(1, 1);
private readonly float _scale;
private float _fontSize;
private int _iconSize;
private int _hardwareLineHeight;
private int _sensorLineHeight;
private int _rightMargin;
private int _leftMargin;
private int _topMargin;
private int _bottomMargin;
private int _progressWidth;
private readonly IDictionary<IHardware, IList<ISensor>> _sensors = new SortedDictionary<IHardware, IList<ISensor>>(new HardwareComparer());
private readonly PersistentSettings _settings;
private readonly UserOption _hardwareNames;
private Font _largeFont;
private Font _smallFont;
private Brush _textBrush;
private StringFormat _stringFormat;
private StringFormat _trimStringFormat;
private StringFormat _alignRightStringFormat;
public SensorGadget(IComputer computer, PersistentSettings settings, UnitManager unitManager)
{
_unitManager = unitManager;
_settings = settings;
computer.HardwareAdded += HardwareAdded;
computer.HardwareRemoved += HardwareRemoved;
_stringFormat = new StringFormat { FormatFlags = StringFormatFlags.NoWrap };
_trimStringFormat = new StringFormat { Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap };
_alignRightStringFormat = new StringFormat { Alignment = StringAlignment.Far, FormatFlags = StringFormatFlags.NoWrap };
if (File.Exists("gadget_background.png"))
{
try
{
Image newBack = new Bitmap("gadget_background.png");
_back.Dispose();
_back = newBack;
}
catch { }
}
if (File.Exists("gadget_image.png"))
{
try
{
_image = new Bitmap("gadget_image.png");
}
catch { }
}
if (File.Exists("gadget_foreground.png"))
{
try
{
_fore = new Bitmap("gadget_foreground.png");
}
catch { }
}
if (File.Exists("gadget_bar_background.png"))
{
try
{
Image newBarBack = new Bitmap("gadget_bar_background.png");
_barBack.Dispose();
_barBack = newBarBack;
}
catch { }
}
if (File.Exists("gadget_bar_foreground.png"))
{
try
{
Image newBarColor = new Bitmap("gadget_bar_foreground.png");
_barFore.Dispose();
_barFore = newBarColor;
}
catch { }
}
Location = new Point(settings.GetValue("sensorGadget.Location.X", 100), settings.GetValue("sensorGadget.Location.Y", 100));
LocationChanged += delegate
{
settings.SetValue("sensorGadget.Location.X", Location.X);
settings.SetValue("sensorGadget.Location.Y", Location.Y);
};
// get the custom to default dpi ratio
using (Bitmap b = new Bitmap(1, 1))
{
_scale = b.HorizontalResolution / 96.0f;
}
SetFontSize(settings.GetValue("sensorGadget.FontSize", 7.5f));
Resize(settings.GetValue("sensorGadget.Width", Size.Width));
ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
ToolStripMenuItem hardwareNamesItem = new ToolStripMenuItem("Hardware Names");
contextMenuStrip.Items.Add(hardwareNamesItem);
ToolStripMenuItem fontSizeMenu = new ToolStripMenuItem("Font Size");
for (int i = 0; i < 4; i++)
{
float size;
string name;
switch (i)
{
case 0: size = 6.5f; name = "Small"; break;
case 1: size = 7.5f; name = "Medium"; break;
case 2: size = 9f; name = "Large"; break;
case 3: size = 11f; name = "Very Large"; break;
default: throw new NotImplementedException();
}
ToolStripItem item = new ToolStripMenuItem(name) { Checked = _fontSize == size };
item.Click += delegate
{
SetFontSize(size);
settings.SetValue("sensorGadget.FontSize", size);
foreach (ToolStripMenuItem mi in fontSizeMenu.DropDownItems)
mi.Checked = mi == item;
};
fontSizeMenu.DropDownItems.Add(item);
}
contextMenuStrip.Items.Add(fontSizeMenu);
Color fontColor = settings.GetValue("sensorGadget.FontColor", Color.White);
int fontColorArgb = fontColor.ToArgb();
SetFontColor(fontColor);
IEnumerable<Color> providedColors = Enum.GetValues(typeof(KnownColor))
.Cast<KnownColor>()
.Select(x => Color.FromKnownColor(x))
.Where(x => !x.IsSystemColor && x.Name.Length < 7);
ToolStripMenuItem fontColorMenu = new ToolStripMenuItem("Font Color");
foreach (Color color in providedColors)
{
ToolStripItem item = new ToolStripMenuItem(color.Name) { Checked = fontColorArgb == color.ToArgb() };
item.Click += delegate
{
SetFontColor(color);
settings.SetValue("sensorGadget.FontColor", color);
foreach (ToolStripMenuItem mi in fontColorMenu.DropDownItems)
mi.Checked = mi == item;
};
fontColorMenu.DropDownItems.Add(item);
}
contextMenuStrip.Items.Add(fontColorMenu);
contextMenuStrip.Items.Add(new ToolStripSeparator());
ToolStripMenuItem lockItem = new ToolStripMenuItem("Lock Position and Size");
contextMenuStrip.Items.Add(lockItem);
contextMenuStrip.Items.Add(new ToolStripSeparator());
ToolStripMenuItem alwaysOnTopItem = new ToolStripMenuItem("Always on Top");
contextMenuStrip.Items.Add(alwaysOnTopItem);
ToolStripMenuItem opacityMenu = new ToolStripMenuItem("Opacity");
contextMenuStrip.Items.Add(opacityMenu);
Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255);
for (int i = 0; i < 5; i++)
{
ToolStripMenuItem item = new ToolStripMenuItem((20 * (i + 1)).ToString() + " %");
byte o = (byte)(51 * (i + 1));
item.Checked = Opacity == o;
item.Click += delegate
{
Opacity = o;
settings.SetValue("sensorGadget.Opacity", Opacity);
foreach (ToolStripMenuItem mi in opacityMenu.DropDownItems)
mi.Checked = mi == item;
};
opacityMenu.DropDownItems.Add(item);
}
ContextMenuStrip = contextMenuStrip;
_hardwareNames = new UserOption("sensorGadget.Hardwarenames", true, hardwareNamesItem, settings);
_hardwareNames.Changed += delegate
{
Resize();
};
UserOption alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false, alwaysOnTopItem, settings);
alwaysOnTop.Changed += delegate
{
AlwaysOnTop = alwaysOnTop.Value;
};
UserOption lockPositionAndSize = new UserOption("sensorGadget.LockPositionAndSize", false, lockItem, settings);
lockPositionAndSize.Changed += delegate
{
LockPositionAndSize = lockPositionAndSize.Value;
};
HitTest += delegate (object sender, HitTestEventArgs e)
{
if (lockPositionAndSize.Value)
return;
if (e.Location.X < LeftBorder)
{
e.HitResult = HitResult.Left;
return;
}
if (e.Location.X > Size.Width - 1 - RightBorder)
{
e.HitResult = HitResult.Right;
}
};
SizeChanged += delegate
{
settings.SetValue("sensorGadget.Width", Size.Width);
Redraw();
};
VisibleChanged += delegate
{
Rectangle bounds = new Rectangle(Location, Size);
Screen screen = Screen.FromRectangle(bounds);
Rectangle intersection = Rectangle.Intersect(screen.WorkingArea, bounds);
if (intersection.Width < Math.Min(16, bounds.Width) || intersection.Height < Math.Min(16, bounds.Height))
{
Location = new Point(screen.WorkingArea.Width / 2 - bounds.Width / 2, screen.WorkingArea.Height / 2 - bounds.Height / 2);
}
};
MouseDoubleClick += delegate
{
SendHideShowCommand();
};
}
public override void Dispose()
{
_largeFont.Dispose();
_largeFont = null;
_smallFont.Dispose();
_smallFont = null;
_textBrush.Dispose();
_textBrush = null;
_stringFormat.Dispose();
_stringFormat = null;
_trimStringFormat.Dispose();
_trimStringFormat = null;
_alignRightStringFormat.Dispose();
_alignRightStringFormat = null;
_back.Dispose();
_back = null;
_barFore.Dispose();
_barFore = null;
_barBack.Dispose();
_barBack = null;
_background.Dispose();
_background = null;
if (_image != null)
{
_image.Dispose();
_image = null;
}
if (_fore != null)
{
_fore.Dispose();
_fore = null;
}
base.Dispose();
}
private void HardwareRemoved(IHardware hardware)
{
hardware.SensorAdded -= SensorAdded;
hardware.SensorRemoved -= SensorRemoved;
foreach (ISensor sensor in hardware.Sensors)
SensorRemoved(sensor);
foreach (IHardware subHardware in hardware.SubHardware)
HardwareRemoved(subHardware);
}
private void HardwareAdded(IHardware hardware)
{
foreach (ISensor sensor in hardware.Sensors)
SensorAdded(sensor);
hardware.SensorAdded += SensorAdded;
hardware.SensorRemoved += SensorRemoved;
foreach (IHardware subHardware in hardware.SubHardware)
HardwareAdded(subHardware);
}
private void SensorAdded(ISensor sensor)
{
if (_settings.GetValue(new Identifier(sensor.Identifier, "gadget").ToString(), false))
Add(sensor);
}
private void SensorRemoved(ISensor sensor)
{
if (Contains(sensor))
Remove(sensor, false);
}
public bool Contains(ISensor sensor)
{
return _sensors.Values.Any(list => list.Contains(sensor));
}
public void Add(ISensor sensor)
{
if (Contains(sensor))
return;
// get the right hardware
IHardware hardware = sensor.Hardware;
while (hardware.Parent != null)
hardware = hardware.Parent;
// get the sensor list associated with the hardware
if (!_sensors.TryGetValue(hardware, out IList<ISensor> list))
{
list = new List<ISensor>();
_sensors.Add(hardware, list);
}
// insert the sensor at the right position
int i = 0;
while (i < list.Count && (list[i].SensorType < sensor.SensorType || (list[i].SensorType == sensor.SensorType && list[i].Index < sensor.Index)))
i++;
list.Insert(i, sensor);
_settings.SetValue(new Identifier(sensor.Identifier, "gadget").ToString(), true);
Resize();
}
public void Remove(ISensor sensor)
{
Remove(sensor, true);
}
private void Remove(ISensor sensor, bool deleteConfig)
{
if (deleteConfig)
_settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in _sensors)
{
if (keyValue.Value.Contains(sensor))
{
keyValue.Value.Remove(sensor);
if (keyValue.Value.Count == 0)
{
_sensors.Remove(keyValue.Key);
break;
}
}
}
Resize();
}
public event EventHandler HideShowCommand;
public void SendHideShowCommand()
{
HideShowCommand?.Invoke(this, null);
}
private Font CreateFont(float size, FontStyle style)
{
try
{
return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style);
}
catch (ArgumentException)
{
// if the style is not supported, fall back to the original one
return new Font(SystemFonts.MessageBoxFont.FontFamily, size,
SystemFonts.MessageBoxFont.Style);
}
}
private void SetFontSize(float size)
{
_fontSize = size;
_largeFont = CreateFont(_fontSize, FontStyle.Bold);
_smallFont = CreateFont(_fontSize, FontStyle.Regular);
double scaledFontSize = _fontSize * _scale;
_iconSize = (int)Math.Round(1.5 * scaledFontSize);
_hardwareLineHeight = (int)Math.Round(1.66 * scaledFontSize);
_sensorLineHeight = (int)Math.Round(1.33 * scaledFontSize);
_leftMargin = LeftBorder + (int)Math.Round(0.3 * scaledFontSize);
_rightMargin = RightBorder + (int)Math.Round(0.3 * scaledFontSize);
_topMargin = TopBorder;
_bottomMargin = BottomBorder + (int)Math.Round(0.3 * scaledFontSize);
_progressWidth = (int)Math.Round(5.3 * scaledFontSize);
Resize((int)Math.Round(17.3 * scaledFontSize));
}
private void SetFontColor(Color color)
{
_textBrush?.Dispose();
_textBrush = new SolidBrush(color);
}
private void Resize()
{
Resize(Size.Width);
}
private void Resize(int width)
{
int y = _topMargin;
foreach (KeyValuePair<IHardware, IList<ISensor>> pair in _sensors)
{
if (_hardwareNames.Value)
{
if (y > _topMargin)
y += _hardwareLineHeight - _sensorLineHeight;
y += _hardwareLineHeight;
}
y += pair.Value.Count * _sensorLineHeight;
}
if (_sensors.Count == 0)
y += 4 * _sensorLineHeight + _hardwareLineHeight;
y += _bottomMargin;
Size = new Size(width, y);
}
private void DrawImageWidthBorder(Graphics g, int width, int height, Image back, int t, int b, int l, int r)
{
GraphicsUnit u = GraphicsUnit.Pixel;
g.DrawImage(back, new Rectangle(0, 0, l, t), new Rectangle(0, 0, l, t), u);
g.DrawImage(back, new Rectangle(l, 0, width - l - r, t), new Rectangle(l, 0, back.Width - l - r, t), u);
g.DrawImage(back, new Rectangle(width - r, 0, r, t), new Rectangle(back.Width - r, 0, r, t), u);
g.DrawImage(back, new Rectangle(0, t, l, height - t - b), new Rectangle(0, t, l, back.Height - t - b), u);
g.DrawImage(back, new Rectangle(l, t, width - l - r, height - t - b), new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
g.DrawImage(back, new Rectangle(width - r, t, r, height - t - b), new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
g.DrawImage(back, new Rectangle(0, height - b, l, b), new Rectangle(0, back.Height - b, l, b), u);
g.DrawImage(back, new Rectangle(l, height - b, width - l - r, b), new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
g.DrawImage(back, new Rectangle(width - r, height - b, r, b), new Rectangle(back.Width - r, back.Height - b, r, b), u);
}
private void DrawBackground(Graphics g)
{
int w = Size.Width;
int h = Size.Height;
if (w != _background.Width || h != _background.Height)
{
_background.Dispose();
_background = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
using (Graphics graphics = Graphics.FromImage(_background))
{
DrawImageWidthBorder(graphics, w, h, _back, TopBorder, BottomBorder,LeftBorder, RightBorder);
if (_fore != null)
DrawImageWidthBorder(graphics, w, h, _fore, TopBorder, BottomBorder, LeftBorder, RightBorder);
if (_image != null)
{
int width = w - LeftBorder - RightBorder;
int height = h - TopBorder - BottomBorder;
float xRatio = width / (float)_image.Width;
float yRatio = height / (float)_image.Height;
float destWidth, destHeight;
float xOffset, yOffset;
if (xRatio < yRatio)
{
destWidth = width;
destHeight = _image.Height * xRatio;
xOffset = 0;
yOffset = 0.5f * (height - destHeight);
}
else
{
destWidth = _image.Width * yRatio;
destHeight = height;
xOffset = 0.5f * (width - destWidth);
yOffset = 0;
}
graphics.DrawImage(_image, new RectangleF(LeftBorder + xOffset, TopBorder + yOffset, destWidth, destHeight));
}
}
}
g.DrawImageUnscaled(_background, 0, 0);
}
private void DrawProgress(Graphics g, float x, float y, float width, float height, float progress)
{
g.DrawImage(_barBack,
new RectangleF(x + width * progress, y, width * (1 - progress), height),
new RectangleF(_barBack.Width * progress, 0, (1 - progress) * _barBack.Width, _barBack.Height),
GraphicsUnit.Pixel);
g.DrawImage(_barFore,
new RectangleF(x, y, width * progress, height),
new RectangleF(0, 0, progress * _barFore.Width, _barFore.Height), GraphicsUnit.Pixel);
}
protected override void OnPaint(PaintEventArgs e)
{
try
{
Graphics g = e.Graphics;
int w = Size.Width;
g.Clear(Color.Transparent);
DrawBackground(g);
int x;
int y = _topMargin;
if (_sensors.Count == 0)
{
x = LeftBorder + 1;
g.DrawString("Right-click on a sensor in the main window and select " +
"\"Show in Gadget\" to show the sensor here.",
_smallFont, _textBrush,
new Rectangle(x, y - 1, w - RightBorder - x, 0));
}
foreach (KeyValuePair<IHardware, IList<ISensor>> pair in _sensors)
{
if (_hardwareNames.Value)
{
if (y > _topMargin)
y += _hardwareLineHeight - _sensorLineHeight;
x = LeftBorder + 1;
g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType), new Rectangle(x, y + 1, _iconSize, _iconSize));
x += _iconSize + 1;
g.DrawString(pair.Key.Name, _largeFont, _textBrush, new Rectangle(x, y - 1, w - RightBorder - x, 0), _stringFormat);
y += _hardwareLineHeight;
}
foreach (ISensor sensor in pair.Value)
{
int remainingWidth;
if ((sensor.SensorType != SensorType.Load &&
sensor.SensorType != SensorType.Control &&
sensor.SensorType != SensorType.Level &&
sensor.SensorType != SensorType.Humidity) || !sensor.Value.HasValue)
{
string formatted;
if (sensor.Value.HasValue)
{
string format = "";
switch (sensor.SensorType)
{
case SensorType.Voltage:
format = "{0:F3} V";
break;
case SensorType.Current:
format = "{0:F3} A";
break;
case SensorType.Clock:
format = "{0:F0} MHz";
break;
case SensorType.Frequency:
format = "{0:F0} Hz";
break;
case SensorType.Temperature:
format = "{0:F1} °C";
break;
case SensorType.Fan:
format = "{0:F0} RPM";
break;
case SensorType.Flow:
format = "{0:F0} L/h";
break;
case SensorType.Power:
format = "{0:F1} W";
break;
case SensorType.Data:
format = "{0:F1} GB";
break;
case SensorType.SmallData:
format = "{0:F0} MB";
break;
case SensorType.Factor:
format = "{0:F3}";
break;
case SensorType.TimeSpan:
format = "{0:g}";
break;
case SensorType.Energy:
format = "{0:F0} mWh";
break;
case SensorType.Noise:
format = "{0:F0} dBA";
break;
case SensorType.Conductivity:
format = "{0:F1} µS/cm";
break;
}
if (sensor.SensorType == SensorType.Temperature && _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
{
formatted = $"{UnitManager.CelsiusToFahrenheit(sensor.Value):F1} °F";
}
else if (sensor.SensorType == SensorType.Throughput)
{
string result;
switch (sensor.Name)
{
case "Connection Speed":
{
switch (sensor.Value)
{
case 100000000:
result = "100Mbps";
break;
case 1000000000:
result = "1Gbps";
break;
default:
{
if (sensor.Value < 1024)
result = $"{sensor.Value:F0} bps";
else if (sensor.Value < 1048576)
result = $"{sensor.Value / 1024:F1} Kbps";
else if (sensor.Value < 1073741824)
result = $"{sensor.Value / 1048576:F1} Mbps";
else
result = $"{sensor.Value / 1073741824:F1} Gbps";
}
break;
}
}
break;
default:
{
if (sensor.Value < 1048576)
result = $"{sensor.Value / 1024:F1} KB/s";
else
result = $"{sensor.Value / 1048576:F1} MB/s";
}
break;
}
formatted = result;
}
else if (sensor.SensorType == SensorType.TimeSpan)
{
formatted = string.Format(format, TimeSpan.FromSeconds(sensor.Value.Value));
}
else
{
formatted = string.Format(format, sensor.Value);
}
}
else
{
formatted = "-";
}
g.DrawString(formatted, _smallFont, _textBrush, new RectangleF(-1, y - 1, w - _rightMargin + 3, 0), _alignRightStringFormat);
remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted, _smallFont, w, StringFormat.GenericTypographic).Width) - _rightMargin;
}
else
{
DrawProgress(g, w - _progressWidth - _rightMargin, y + 0.35f * _sensorLineHeight, _progressWidth, 0.6f * _sensorLineHeight, 0.01f * sensor.Value.Value);
remainingWidth = w - _progressWidth - _rightMargin;
}
remainingWidth -= _leftMargin + 2;
if (remainingWidth > 0)
{
g.DrawString(sensor.Name, _smallFont, _textBrush, new RectangleF(_leftMargin - 1, y - 1, remainingWidth, 0), _trimStringFormat);
}
y += _sensorLineHeight;
}
}
}
catch (ArgumentException)
{
// #1425.
}
}
private class HardwareComparer : IComparer<IHardware>
{
public int Compare(IHardware x, IHardware y)
{
switch (x)
{
case null when y == null:
return 0;
case null:
return -1;
}
if (y == null)
return 1;
if (x.HardwareType != y.HardwareType)
return x.HardwareType.CompareTo(y.HardwareType);
return x.Identifier.CompareTo(y.Identifier);
}
}
}

View File

@@ -0,0 +1,285 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Drawing;
using System.Globalization;
using System.Text;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public class SensorNode : Node
{
private readonly PersistentSettings _settings;
private readonly UnitManager _unitManager;
private Color? _penColor;
private bool _plot;
public SensorNode(ISensor sensor, PersistentSettings settings, UnitManager unitManager)
{
Sensor = sensor;
_settings = settings;
_unitManager = unitManager;
switch (sensor.SensorType)
{
case SensorType.Voltage:
Format = "{0:F3} V";
break;
case SensorType.Current:
Format = "{0:F3} A";
break;
case SensorType.Clock:
Format = "{0:F1} MHz";
break;
case SensorType.Load:
Format = "{0:F1} %";
break;
case SensorType.Temperature:
Format = "{0:F1} °C";
break;
case SensorType.Fan:
Format = "{0:F0} RPM";
break;
case SensorType.Flow:
Format = "{0:F1} L/h";
break;
case SensorType.Control:
Format = "{0:F1} %";
break;
case SensorType.Level:
Format = "{0:F1} %";
break;
case SensorType.Power:
Format = "{0:F1} W";
break;
case SensorType.Data:
Format = "{0:F1} GB";
break;
case SensorType.SmallData:
Format = "{0:F1} MB";
break;
case SensorType.Factor:
Format = "{0:F3}";
break;
case SensorType.Frequency:
Format = "{0:F1} Hz";
break;
case SensorType.Throughput:
Format = "{0:F1} B/s";
break;
case SensorType.TimeSpan:
Format = "{0:g}";
break;
case SensorType.Energy:
Format = "{0:F0} mWh";
break;
case SensorType.Noise:
Format = "{0:F0} dBA";
break;
case SensorType.Conductivity:
Format = "{0:F1} µS/cm";
break;
case SensorType.Humidity:
Format = "{0:F0} %";
break;
}
bool hidden = settings.GetValue(new Identifier(sensor.Identifier, "hidden").ToString(), sensor.IsDefaultHidden);
base.IsVisible = !hidden;
Plot = settings.GetValue(new Identifier(sensor.Identifier, "plot").ToString(), false);
string id = new Identifier(sensor.Identifier, "penColor").ToString();
if (settings.Contains(id))
PenColor = settings.GetValue(id, Color.Black);
}
public event EventHandler PlotSelectionChanged;
public string Format { get; set; } = "";
public override bool IsVisible
{
get { return base.IsVisible; }
set
{
base.IsVisible = value;
_settings.SetValue(new Identifier(Sensor.Identifier, "hidden").ToString(), !value);
}
}
public string Max
{
get { return ValueToString(Sensor.Max); }
}
public string Min
{
get { return ValueToString(Sensor.Min); }
}
public Color? PenColor
{
get { return _penColor; }
set
{
_penColor = value;
string id = new Identifier(Sensor.Identifier, "penColor").ToString();
if (value.HasValue)
_settings.SetValue(id, value.Value);
else
_settings.Remove(id);
PlotSelectionChanged?.Invoke(this, null);
}
}
public bool Plot
{
get { return _plot; }
set
{
_plot = value;
_settings.SetValue(new Identifier(Sensor.Identifier, "plot").ToString(), value);
PlotSelectionChanged?.Invoke(this, null);
}
}
public ISensor Sensor { get; }
public override string Text
{
get { return Sensor.Name; }
set { Sensor.Name = value; }
}
public override string ToolTip
{
get
{
StringBuilder stringBuilder = new();
if (Sensor is ICriticalSensorLimits criticalSensorLimits)
OptionallyAppendCriticalRange(stringBuilder, criticalSensorLimits.CriticalLowLimit, criticalSensorLimits.CriticalHighLimit, "critical");
if (Sensor is ISensorLimits sensorLimits)
OptionallyAppendCriticalRange(stringBuilder, sensorLimits.LowLimit, sensorLimits.HighLimit, "normal");
return stringBuilder.ToString();
}
}
public string Value
{
get { return ValueToString(Sensor.Value); }
}
public string ValueToString(float? value)
{
if (value.HasValue)
{
switch (Sensor.SensorType)
{
case SensorType.Temperature when _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit:
{
return $"{value * 1.8 + 32:F1} °F";
}
case SensorType.Throughput:
{
string result;
switch (Sensor.Name)
{
case "Connection Speed":
{
switch (value)
{
case 100000000:
{
result = "100Mbps";
break;
}
case 1000000000:
{
result = "1Gbps";
break;
}
default:
{
if (value < 1024)
result = $"{value:F0} bps";
else if (value < 1048576)
result = $"{value / 1024:F1} Kbps";
else if (value < 1073741824)
result = $"{value / 1048576:F1} Mbps";
else
result = $"{value / 1073741824:F1} Gbps";
}
break;
}
break;
}
default:
{
const int _1MB = 1048576;
result = value < _1MB ? $"{value / 1024:F1} KB/s" : $"{value / _1MB:F1} MB/s";
break;
}
}
return result;
}
case SensorType.TimeSpan:
{
return value.HasValue ? string.Format(Format, TimeSpan.FromSeconds(value.Value)) : "-";
}
default:
{
return string.Format(Format, value);
}
}
}
return "-";
}
private void OptionallyAppendCriticalRange(StringBuilder str, float? min, float? max, string kind)
{
if (min.HasValue)
{
str.AppendLine(max.HasValue
? $"{CultureInfo.CurrentUICulture.TextInfo.ToTitleCase(kind)} range: {ValueToString(min)} to {ValueToString(max)}."
: $"Minimal {kind} value: {ValueToString(min)}.");
}
else if (max.HasValue)
{
str.AppendLine($"Maximal {kind} value: {ValueToString(max)}.");
}
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (!(obj is SensorNode s))
return false;
return (Sensor == s.Sensor);
}
public override int GetHashCode()
{
return Sensor.GetHashCode();
}
}

View File

@@ -0,0 +1,311 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public class SensorNotifyIcon : IDisposable
{
private readonly UnitManager _unitManager;
private readonly NotifyIconAdv _notifyIcon;
private readonly Bitmap _bitmap;
private readonly Graphics _graphics;
private Color _color;
private Color _darkColor;
private Brush _brush;
private Brush _darkBrush;
private readonly Pen _pen;
private readonly Font _font;
private readonly Font _smallFont;
public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor, PersistentSettings settings, UnitManager unitManager)
{
_unitManager = unitManager;
Sensor = sensor;
_notifyIcon = new NotifyIconAdv();
Color defaultColor = Color.White;
if (sensor.SensorType == SensorType.Load || sensor.SensorType == SensorType.Control || sensor.SensorType == SensorType.Level)
defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
Color = settings.GetValue(new Identifier(sensor.Identifier, "traycolor").ToString(), defaultColor);
_pen = new Pen(Color.FromArgb(96, Color.Black));
ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
ToolStripItem hideShowItem = new ToolStripMenuItem("Hide/Show");
hideShowItem.Click += delegate
{
sensorSystemTray.SendHideShowCommand();
};
contextMenuStrip.Items.Add(hideShowItem);
contextMenuStrip.Items.Add(new ToolStripSeparator());
ToolStripItem removeItem = new ToolStripMenuItem("Remove Sensor");
removeItem.Click += delegate
{
sensorSystemTray.Remove(Sensor);
};
contextMenuStrip.Items.Add(removeItem);
ToolStripItem colorItem = new ToolStripMenuItem("Change Color...");
colorItem.Click += delegate
{
ColorDialog dialog = new ColorDialog { Color = Color };
if (dialog.ShowDialog() == DialogResult.OK)
{
Color = dialog.Color;
settings.SetValue(new Identifier(sensor.Identifier,
"traycolor").ToString(), Color);
}
};
contextMenuStrip.Items.Add(colorItem);
contextMenuStrip.Items.Add(new ToolStripSeparator());
ToolStripItem exitItem = new ToolStripMenuItem("Exit");
exitItem.Click += delegate
{
sensorSystemTray.SendExitCommand();
};
contextMenuStrip.Items.Add(exitItem);
_notifyIcon.ContextMenuStrip = contextMenuStrip;
_notifyIcon.DoubleClick += delegate
{
sensorSystemTray.SendHideShowCommand();
};
// get the default dpi to create an icon with the correct size
float dpiX, dpiY;
using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
{
dpiX = b.HorizontalResolution;
dpiY = b.VerticalResolution;
}
// adjust the size of the icon to current dpi (default is 16x16 at 96 dpi)
int width = (int)Math.Round(16 * dpiX / 96);
int height = (int)Math.Round(16 * dpiY / 96);
// make sure it does never get smaller than 16x16
width = width < 16 ? 16 : width;
height = height < 16 ? 16 : height;
// adjust the font size to the icon size
FontFamily family = SystemFonts.MessageBoxFont.FontFamily;
float baseSize;
switch (family.Name)
{
case "Segoe UI": baseSize = 12; break;
case "Tahoma": baseSize = 11; break;
default: baseSize = 12; break;
}
_font = new Font(family, baseSize * width / 16.0f, GraphicsUnit.Pixel);
_smallFont = new Font(family, 0.75f * baseSize * width / 16.0f, GraphicsUnit.Pixel);
_bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
_graphics = Graphics.FromImage(_bitmap);
if (Environment.OSVersion.Version.Major > 5)
{
_graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
_graphics.SmoothingMode = SmoothingMode.HighQuality;
}
}
public ISensor Sensor { get; }
public Color Color
{
get { return _color; }
set
{
_color = value;
_darkColor = Color.FromArgb(255, _color.R / 3, _color.G / 3, _color.B / 3);
Brush brush = _brush;
_brush = new SolidBrush(_color);
brush?.Dispose();
Brush darkBrush = _darkBrush;
_darkBrush = new SolidBrush(_darkColor);
darkBrush?.Dispose();
}
}
public void Dispose()
{
Icon icon = _notifyIcon.Icon;
_notifyIcon.Icon = null;
icon?.Destroy();
_notifyIcon.Dispose();
_brush?.Dispose();
_darkBrush?.Dispose();
_pen.Dispose();
_graphics.Dispose();
_bitmap.Dispose();
_font.Dispose();
_smallFont.Dispose();
}
private string GetString()
{
if (!Sensor.Value.HasValue)
return "-";
switch (Sensor.SensorType)
{
case SensorType.Temperature:
return _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit ? $"{UnitManager.CelsiusToFahrenheit(Sensor.Value):F0}" : $"{Sensor.Value:F0}";
case SensorType.TimeSpan:
return $"{TimeSpan.FromSeconds(Sensor.Value.Value):g}";
case SensorType.Clock:
case SensorType.Fan:
case SensorType.Flow:
return $"{1e-3f * Sensor.Value:F1}";
case SensorType.Voltage:
case SensorType.Current:
case SensorType.SmallData:
case SensorType.Factor:
case SensorType.Throughput:
case SensorType.Conductivity:
return $"{Sensor.Value:F1}";
case SensorType.Control:
case SensorType.Frequency:
case SensorType.Level:
case SensorType.Power:
case SensorType.Data:
case SensorType.Load:
case SensorType.Energy:
case SensorType.Noise:
case SensorType.Humidity:
return $"{Sensor.Value:F0}";
default:
return "-";
}
}
private Icon CreateTransparentIcon()
{
string text = GetString();
int count = 0;
for (int i = 0; i < text.Length; i++)
if ((text[i] >= '0' && text[i] <= '9') || text[i] == '-')
count++;
bool small = count > 2;
_graphics.Clear(Color.Black);
Rectangle bounds = new Rectangle(Point.Empty, _bitmap.Size);
TextRenderer.DrawText(_graphics, text, small ? _smallFont : _font,
bounds, Color.White, Color.Black, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
BitmapData data = _bitmap.LockBits(
new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
IntPtr Scan0 = data.Scan0;
int numBytes = _bitmap.Width * _bitmap.Height * 4;
byte[] bytes = new byte[numBytes];
Marshal.Copy(Scan0, bytes, 0, numBytes);
_bitmap.UnlockBits(data);
byte red, green, blue;
for (int i = 0; i < bytes.Length; i += 4)
{
blue = bytes[i];
green = bytes[i + 1];
red = bytes[i + 2];
bytes[i] = _color.B;
bytes[i + 1] = _color.G;
bytes[i + 2] = _color.R;
bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
}
return IconFactory.Create(bytes, _bitmap.Width, _bitmap.Height,
PixelFormat.Format32bppArgb);
}
private Icon CreatePercentageIcon()
{
try
{
_graphics.Clear(Color.Transparent);
}
catch (ArgumentException)
{
_graphics.Clear(Color.Black);
}
_graphics.FillRectangle(_darkBrush, 0.5f, -0.5f, _bitmap.Width - 2, _bitmap.Height);
float value = Sensor.Value.GetValueOrDefault();
float y = (float)(_bitmap.Height * 0.01f) * (100 - value);
_graphics.FillRectangle(_brush, 0.5f, -0.5f + y, _bitmap.Width - 2, _bitmap.Height - y);
_graphics.DrawRectangle(_pen, 1, 0, _bitmap.Width - 3, _bitmap.Height - 1);
return IconFactory.Create(_bitmap);
}
public void Update()
{
Icon icon = _notifyIcon.Icon;
switch (Sensor.SensorType)
{
case SensorType.Load:
case SensorType.Control:
case SensorType.Level:
_notifyIcon.Icon = CreatePercentageIcon();
break;
default:
_notifyIcon.Icon = CreateTransparentIcon();
break;
}
icon?.Destroy();
string format = "";
switch (Sensor.SensorType)
{
case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
case SensorType.Current: format = "\n{0}: {1:F2} A"; break;
case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
case SensorType.Energy: format = "\n{0}: {0:F0} mWh"; break;
case SensorType.Noise: format = "\n{0}: {0:F0} dBA"; break;
case SensorType.Conductivity: format = "\n{0}: {0:F1} µS/cm"; break;
case SensorType.Humidity: format = "\n{0}: {0:F0} %"; break;
}
string formattedValue = string.Format(format, Sensor.Name, Sensor.Value);
if (Sensor.SensorType == SensorType.Temperature && _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
{
format = "\n{0}: {1:F1} °F";
formattedValue = string.Format(format, Sensor.Name, UnitManager.CelsiusToFahrenheit(Sensor.Value));
}
string hardwareName = Sensor.Hardware.Name;
hardwareName = hardwareName.Substring(0, Math.Min(63 - formattedValue.Length, hardwareName.Length));
string text = hardwareName + formattedValue;
if (text.Length > 63)
text = null;
_notifyIcon.Text = text;
_notifyIcon.Visible = true;
}
}

View File

@@ -0,0 +1,157 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI;
public class ShowDesktop : IDisposable
{
private readonly NativeWindow _referenceWindow;
private readonly string _referenceWindowCaption = "LibreHardwareMonitorShowDesktopReferenceWindow";
private readonly System.Threading.Timer _timer;
private bool _showDesktop;
/// <summary>
/// Prevents a default instance of the <see cref="ShowDesktop" /> class from being created.
/// </summary>
private ShowDesktop()
{
// Create a reference window to detect show desktop
_referenceWindow = new NativeWindow();
CreateParams cp = new CreateParams { ExStyle = GadgetWindow.WS_EX_TOOLWINDOW, Caption = _referenceWindowCaption };
_referenceWindow.CreateHandle(cp);
NativeMethods.SetWindowPos(_referenceWindow.Handle,
GadgetWindow.HWND_BOTTOM,
0,
0,
0,
0,
GadgetWindow.SWP_NOMOVE |
GadgetWindow.SWP_NOSIZE |
GadgetWindow.SWP_NOACTIVATE |
GadgetWindow.SWP_NOSENDCHANGING);
// start a repeated timer to detect "Show Desktop" events
_timer = new System.Threading.Timer(OnTimer, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
public delegate void ShowDesktopChangedEventHandler(bool showDesktop);
private event ShowDesktopChangedEventHandler ShowDesktopChangedEvent;
// notify when the "show desktop" mode is changed
public event ShowDesktopChangedEventHandler ShowDesktopChanged
{
add
{
// start the monitor timer when someone is listening
if (ShowDesktopChangedEvent == null)
StartTimer();
ShowDesktopChangedEvent += value;
}
remove
{
ShowDesktopChangedEvent -= value;
// stop the monitor timer if nobody is interested
if (ShowDesktopChangedEvent == null)
StopTimer();
}
}
public static ShowDesktop Instance { get; } = new ShowDesktop();
/// <inheritdoc />
public void Dispose()
{
_timer?.Dispose();
_referenceWindow.ReleaseHandle();
}
private void StartTimer()
{
_timer.Change(0, 200);
}
private void StopTimer()
{
_timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
// the desktop worker window (if available) can hide the reference window
private IntPtr GetDesktopWorkerWindow()
{
IntPtr shellWindow = NativeMethods.GetShellWindow();
if (shellWindow == IntPtr.Zero)
return IntPtr.Zero;
NativeMethods.GetWindowThreadProcessId(shellWindow, out int shellId);
IntPtr workerWindow = IntPtr.Zero;
while ((workerWindow = NativeMethods.FindWindowEx(IntPtr.Zero, workerWindow, "WorkerW", null)) != IntPtr.Zero)
{
NativeMethods.GetWindowThreadProcessId(workerWindow, out int workerId);
if (workerId == shellId)
{
IntPtr window = NativeMethods.FindWindowEx(workerWindow, IntPtr.Zero, "SHELLDLL_DefView", null);
if (window != IntPtr.Zero)
{
IntPtr desktopWindow = NativeMethods.FindWindowEx(window, IntPtr.Zero, "SysListView32", null);
if (desktopWindow != IntPtr.Zero)
return workerWindow;
}
}
}
return IntPtr.Zero;
}
private void OnTimer(object state)
{
bool showDesktopDetected;
IntPtr workerWindow = GetDesktopWorkerWindow();
if (workerWindow != IntPtr.Zero)
{
// search if the reference window is behind the worker window
IntPtr reference = NativeMethods.FindWindowEx(IntPtr.Zero, workerWindow, null, _referenceWindowCaption);
showDesktopDetected = reference != IntPtr.Zero;
}
else
{
// if there is no worker window, then nothing can hide the reference
showDesktopDetected = false;
}
if (_showDesktop != showDesktopDetected)
{
_showDesktop = showDesktopDetected;
ShowDesktopChangedEvent?.Invoke(_showDesktop);
}
}
private static class NativeMethods
{
private const string USER = "user32.dll";
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr GetShellWindow();
[DllImport(USER, CallingConvention = CallingConvention.Winapi)]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
}
}

View File

@@ -0,0 +1,125 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Drawing;
using System.Windows.Forms;
using LibreHardwareMonitor.UI.Themes;
namespace LibreHardwareMonitor.UI;
public class SplitContainerAdv : SplitContainer
{
private int _delta;
private bool _mouseOver;
public SplitContainerAdv()
{
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ContainerControl, true);
UpdateStyles();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Rectangle r = SplitterRectangle;
using (SolidBrush brush = new SolidBrush(_mouseOver ? Theme.Current.SplitterHoverColor : Theme.Current.SplitterColor))
g.FillRectangle(brush, r);
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (!IsSplitterFixed)
{
switch (e.KeyData)
{
case Keys.Right:
case Keys.Down:
SplitterDistance += SplitterIncrement;
break;
case Keys.Left:
case Keys.Up:
SplitterDistance -= SplitterIncrement;
break;
}
Invalidate();
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (Orientation == Orientation.Vertical)
{
_delta = SplitterDistance - e.X;
Cursor.Current = Cursors.VSplit;
}
else
{
_delta = SplitterDistance - e.Y;
Cursor.Current = Cursors.HSplit;
}
IsSplitterFixed = true;
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
_mouseOver = true;
Invalidate();
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (IsSplitterFixed)
{
if (e.Button == MouseButtons.Left)
{
if (Orientation == Orientation.Vertical)
{
if (e.X > 0 && e.X < Width)
SplitterDistance = e.X + _delta < 0 ? 0 : e.X + _delta;
}
else
{
if (e.Y > 0 && e.Y < Height)
SplitterDistance = e.Y + _delta < 0 ? 0 : e.Y + _delta;
}
}
else
{
IsSplitterFixed = false;
}
Invalidate();
}
else
{
if (SplitterRectangle.Contains(e.Location))
Cursor = Orientation == Orientation.Vertical ? Cursors.VSplit : Cursors.HSplit;
}
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
Cursor = Cursors.Default;
_mouseOver = false;
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
_delta = 0;
IsSplitterFixed = false;
Cursor.Current = Cursors.Default;
}
}

View File

@@ -0,0 +1,180 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.IO;
using System.Linq;
using System.Security;
using System.Security.Principal;
using System.Windows.Forms;
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
using Action = Microsoft.Win32.TaskScheduler.Action;
namespace LibreHardwareMonitor.UI;
public class StartupManager
{
private const string RegistryPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
private bool _startup;
public StartupManager()
{
if (Environment.OSVersion.Platform >= PlatformID.Unix)
{
IsAvailable = false;
return;
}
if (IsAdministrator() && TaskService.Instance.Connected)
{
IsAvailable = true;
Task task = GetTask();
if (task != null)
{
foreach (Action action in task.Definition.Actions)
{
if (action.ActionType == TaskActionType.Execute && action is ExecAction execAction)
{
if (execAction.Path.Equals(Application.ExecutablePath, StringComparison.OrdinalIgnoreCase))
_startup = true;
}
}
}
}
else
{
try
{
using (RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(RegistryPath))
{
string value = (string)registryKey?.GetValue(nameof(LibreHardwareMonitor));
if (value != null)
_startup = value == Application.ExecutablePath;
}
IsAvailable = true;
}
catch (SecurityException)
{
IsAvailable = false;
}
}
}
public bool IsAvailable { get; }
public bool Startup
{
get { return _startup; }
set
{
if (_startup != value)
{
if (IsAvailable)
{
if (TaskService.Instance.Connected)
{
if (value)
CreateTask();
else
DeleteTask();
_startup = value;
}
else
{
try
{
if (value)
CreateRegistryKey();
else
DeleteRegistryKey();
_startup = value;
}
catch (UnauthorizedAccessException)
{
throw new InvalidOperationException();
}
}
}
else
{
throw new InvalidOperationException();
}
}
}
}
private static bool IsAdministrator()
{
try
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
catch
{
return false;
}
}
private static Task GetTask()
{
try
{
return TaskService.Instance.AllTasks.FirstOrDefault(x => x.Name.Equals(nameof(LibreHardwareMonitor), StringComparison.OrdinalIgnoreCase));
}
catch
{
return null;
}
}
private void CreateTask()
{
TaskDefinition taskDefinition = TaskService.Instance.NewTask();
taskDefinition.RegistrationInfo.Description = "Starts LibreHardwareMonitor on Windows startup.";
taskDefinition.Triggers.Add(new LogonTrigger());
taskDefinition.Settings.StartWhenAvailable = true;
taskDefinition.Settings.DisallowStartIfOnBatteries = false;
taskDefinition.Settings.StopIfGoingOnBatteries = false;
taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.Zero;
taskDefinition.Settings.AllowHardTerminate = false;
taskDefinition.Principal.RunLevel = TaskRunLevel.Highest;
taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken;
taskDefinition.Actions.Add(new ExecAction(Application.ExecutablePath, "", Path.GetDirectoryName(Application.ExecutablePath)));
TaskService.Instance.RootFolder.RegisterTaskDefinition(nameof(LibreHardwareMonitor), taskDefinition);
}
private static void DeleteTask()
{
Task task = GetTask();
task?.Folder.DeleteTask(task.Name, false);
}
private static void CreateRegistryKey()
{
RegistryKey registryKey = Registry.CurrentUser.CreateSubKey(RegistryPath);
registryKey?.SetValue(nameof(LibreHardwareMonitor), Application.ExecutablePath);
}
private static void DeleteRegistryKey()
{
RegistryKey registryKey = Registry.CurrentUser.CreateSubKey(RegistryPath);
registryKey?.DeleteValue(nameof(LibreHardwareMonitor));
}
}

View File

@@ -0,0 +1,185 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public class SystemTray : IDisposable
{
private IComputer _computer;
private readonly PersistentSettings _settings;
private readonly UnitManager _unitManager;
private readonly List<SensorNotifyIcon> _sensorList = new List<SensorNotifyIcon>();
private bool _mainIconEnabled;
private readonly NotifyIconAdv _mainIcon;
public SystemTray(IComputer computer, PersistentSettings settings, UnitManager unitManager)
{
_computer = computer;
_settings = settings;
_unitManager = unitManager;
computer.HardwareAdded += HardwareAdded;
computer.HardwareRemoved += HardwareRemoved;
_mainIcon = new NotifyIconAdv();
ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
ToolStripItem hideShowItem = new ToolStripMenuItem("Hide/Show");
hideShowItem.Click += delegate
{
SendHideShowCommand();
};
contextMenuStrip.Items.Add(hideShowItem);
contextMenuStrip.Items.Add(new ToolStripSeparator());
ToolStripItem exitItem = new ToolStripMenuItem("Exit");
exitItem.Click += delegate
{
SendExitCommand();
};
contextMenuStrip.Items.Add(exitItem);
_mainIcon.ContextMenuStrip = contextMenuStrip;
_mainIcon.DoubleClick += delegate
{
SendHideShowCommand();
};
_mainIcon.Icon = EmbeddedResources.GetIcon("smallicon.ico");
_mainIcon.Text = "Libre Hardware Monitor";
}
private void HardwareRemoved(IHardware hardware)
{
hardware.SensorAdded -= SensorAdded;
hardware.SensorRemoved -= SensorRemoved;
foreach (ISensor sensor in hardware.Sensors)
SensorRemoved(sensor);
foreach (IHardware subHardware in hardware.SubHardware)
HardwareRemoved(subHardware);
}
private void HardwareAdded(IHardware hardware)
{
foreach (ISensor sensor in hardware.Sensors)
SensorAdded(sensor);
hardware.SensorAdded += SensorAdded;
hardware.SensorRemoved += SensorRemoved;
foreach (IHardware subHardware in hardware.SubHardware)
HardwareAdded(subHardware);
}
private void SensorAdded(ISensor sensor)
{
if (_settings.GetValue(new Identifier(sensor.Identifier, "tray").ToString(), false))
Add(sensor, false);
}
private void SensorRemoved(ISensor sensor)
{
if (Contains(sensor))
Remove(sensor, false);
}
public void Dispose()
{
foreach (SensorNotifyIcon icon in _sensorList)
icon.Dispose();
_mainIcon.Dispose();
}
public void Redraw()
{
foreach (SensorNotifyIcon icon in _sensorList)
icon.Update();
}
public bool Contains(ISensor sensor)
{
foreach (SensorNotifyIcon icon in _sensorList)
if (icon.Sensor == sensor)
return true;
return false;
}
public void Add(ISensor sensor, bool balloonTip)
{
if (Contains(sensor))
return;
_sensorList.Add(new SensorNotifyIcon(this, sensor, _settings, _unitManager));
UpdateMainIconVisibility();
_settings.SetValue(new Identifier(sensor.Identifier, "tray").ToString(), true);
}
public void Remove(ISensor sensor)
{
Remove(sensor, true);
}
private void Remove(ISensor sensor, bool deleteConfig)
{
if (deleteConfig)
{
_settings.Remove(new Identifier(sensor.Identifier, "tray").ToString());
_settings.Remove(new Identifier(sensor.Identifier, "traycolor").ToString());
}
SensorNotifyIcon instance = null;
foreach (SensorNotifyIcon icon in _sensorList)
{
if (icon.Sensor == sensor)
instance = icon;
}
if (instance != null)
{
_sensorList.Remove(instance);
UpdateMainIconVisibility();
instance.Dispose();
}
}
public event EventHandler HideShowCommand;
public void SendHideShowCommand()
{
HideShowCommand?.Invoke(this, null);
}
public event EventHandler ExitCommand;
public void SendExitCommand()
{
ExitCommand?.Invoke(this, null);
}
private void UpdateMainIconVisibility()
{
if (_mainIconEnabled)
_mainIcon.Visible = _sensorList.Count == 0;
else
_mainIcon.Visible = false;
}
public bool IsMainIconEnabled
{
get { return _mainIconEnabled; }
set
{
if (_mainIconEnabled != value)
{
_mainIconEnabled = value;
UpdateMainIconVisibility();
}
}
}
}

View File

@@ -0,0 +1,297 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Aga.Controls.Tree;
using Microsoft.Win32;
namespace LibreHardwareMonitor.UI.Themes
{
public abstract class Theme
{
private static Theme _current = new LightTheme();
public static Theme Current
{
get { return _current; }
set
{
_current = value;
foreach (Form form in Application.OpenForms)
{
_current.Apply(form);
}
Init();
}
}
private static void Init()
{
TreeViewAdv.CustomPlusMinusRenderFunc = (g, rect, isExpanded) =>
{
int x = rect.Left;
int y = rect.Top + 5;
int size = 8;
using (Brush brush = new SolidBrush(Current.BackgroundColor))
{
g.FillRectangle(brush, x - 1, y - 1, size + 4, size + 4);
}
using (Pen pen = new Pen(Current.TreeOutlineColor))
{
g.DrawRectangle(pen, x, y, size, size);
g.DrawLine(pen, x + 2, y + (size / 2), x + size - 2, y + (size / 2));
if (!isExpanded)
{
g.DrawLine(pen, x + (size / 2), y + 2, x + (size / 2), y + size - 2);
}
}
};
TreeViewAdv.CustomCheckRenderFunc = (g, rect, isChecked) =>
{
int x = rect.Left;
int y = rect.Top + 1;
int size = 12;
using (Brush brush = new SolidBrush(Current.BackgroundColor))
{
g.FillRectangle(brush, x - 1, y - 1, 12, 12);
}
using (Pen pen = new Pen(Current.TreeOutlineColor))
{
g.DrawRectangle(pen, x, y, size, size);
if (isChecked)
{
x += 3;
y += 3;
g.DrawLine(pen, x, y + 3, x + 2, y + 5);
g.DrawLine(pen, x + 2, y + 5, x + 6, y + 1);
g.DrawLine(pen, x, y + 4, x + 2, y + 6);
g.DrawLine(pen, x + 2, y + 6, x + 6, y + 2);
}
}
};
TreeViewAdv.CustomColumnBackgroundRenderFunc = (g, rect, isPressed, isHot) =>
{
using (Brush brush = new SolidBrush(Current.TreeBackgroundColor))
{
g.FillRectangle(brush, rect);
}
using (Pen pen = new Pen(Current.TreeRowSepearatorColor))
{
g.DrawLine(pen, rect.Left, rect.Top, rect.Right, rect.Top);
g.DrawLine(pen, rect.Left, rect.Top + 1, rect.Right, rect.Top + 1);
}
};
TreeViewAdv.CustomColumnTextRenderFunc = (g, rect, font, text) =>
{
TextRenderer.DrawText(g, text, font, rect, Current.TreeTextColor, TextFormatFlags.Left);
};
TreeViewAdv.CustomHorizontalLinePen = new Pen(Current.TreeRowSepearatorColor);
TreeViewAdv.CustomSelectedRowBrush = new SolidBrush(Current.TreeSelectedBackgroundColor);
TreeViewAdv.CustomSelectedTextColor = Current.TreeSelectedTextColor;
}
private static List<Theme> _all;
public static List<Theme> All
{
get
{
if (_all == null)
{
_all = new List<Theme>();
foreach (Type type in typeof(Theme).Assembly.GetTypes())
{
if (type != typeof(Theme) && typeof(Theme).IsAssignableFrom(type))
{
_all.Add((Theme)type.GetConstructor(new Type[] { }).Invoke(new object[] { }));
}
}
}
return _all.OrderBy(x => x.DisplayName).ToList();
}
}
public static bool SupportsAutoThemeSwitching()
{
if (Software.OperatingSystem.IsUnix)
{
return false;
}
if (Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", -1) is int useLightTheme)
{
return useLightTheme != -1;
}
return false;
}
public static void SetAutoTheme()
{
if (Software.OperatingSystem.IsUnix)
{
return;
}
if (Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1) is int useLightTheme)
{
if (useLightTheme > 0)
{
Current = new LightTheme();
}
else
{
Current = new DarkTheme();
}
}
else
{
// Fallback incase registry fails
Current = new LightTheme();
}
}
public Theme(string id, string displayName)
{
Id = id;
DisplayName = displayName;
}
public string Id { get; }
public string DisplayName { get; }
public abstract Color BackgroundColor { get; }
public abstract Color ForegroundColor { get; }
public abstract Color HyperlinkColor { get; }
public abstract Color LineColor { get; }
public abstract Color StrongLineColor { get; }
public abstract Color SelectedBackgroundColor { get; }
public abstract Color SelectedForegroundColor { get; }
// button
public virtual Color ButtonBackgroundColor => BackgroundColor;
public virtual Color ButtonBorderColor => ForegroundColor;
public virtual Color ButtonHoverBackgroundColor => SelectedBackgroundColor;
public virtual Color ButtonPressedBackgroundColor => LineColor;
public virtual Color ButtonTextColor => ForegroundColor;
// menu
public virtual Color MenuBackgroundColor => BackgroundColor;
public virtual Color MenuBorderColor => StrongLineColor;
public virtual Color MenuForegroundColor => ForegroundColor;
public virtual Color MenuSelectedBackgroundColor => SelectedBackgroundColor;
public virtual Color MenuSelectedForegroundColor => SelectedForegroundColor;
// plot
public virtual Color PlotBackgroundColor => BackgroundColor;
public virtual Color PlotBorderColor => ForegroundColor;
public abstract Color[] PlotColorPalette { get; }
public abstract Color PlotGridMajorColor { get; }
public abstract Color PlotGridMinorColor { get; }
public virtual Color PlotTextColor => ForegroundColor;
// scrollbar
public virtual Color ScrollbarBackground => BackgroundColor;
public virtual Color ScrollbarTrack => StrongLineColor;
// splitter
public virtual Color SplitterColor => BackgroundColor;
public virtual Color SplitterHoverColor => SelectedBackgroundColor;
// tree
public virtual Color TreeBackgroundColor => BackgroundColor;
public virtual Color TreeOutlineColor => ForegroundColor;
public virtual Color TreeSelectedBackgroundColor => SelectedBackgroundColor;
public virtual Color TreeTextColor => ForegroundColor;
public virtual Color TreeSelectedTextColor => SelectedForegroundColor;
public virtual Color TreeRowSepearatorColor => LineColor;
// window
public virtual Color WindowTitlebarBackgroundColor => BackgroundColor;
public abstract bool WindowTitlebarFallbackToImmersiveDarkMode { get; }
public virtual Color WindowTitlebarForegroundColor => ForegroundColor;
public void Apply(Form form)
{
if (IsWindows10OrGreater(22000))
{
// Windows 11, Set the titlebar color based on theme
int color = ColorTranslator.ToWin32(WindowTitlebarBackgroundColor);
DwmSetWindowAttribute(form.Handle, DWMWA_CAPTION_COLOR, ref color, sizeof(int));
color = ColorTranslator.ToWin32(WindowTitlebarForegroundColor);
DwmSetWindowAttribute(form.Handle, DWMWA_TEXT_COLOR, ref color, sizeof(int));
}
else if (IsWindows10OrGreater(17763))
{
// Windows 10, fallback to using "Immersive Dark Mode" instead
var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
if (IsWindows10OrGreater(18985))
{
// Windows 10 20H1 or later
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
}
int useImmersiveDarkMode = WindowTitlebarFallbackToImmersiveDarkMode ? 1 : 0;
DwmSetWindowAttribute(form.Handle, (int)attribute, ref useImmersiveDarkMode, sizeof(int));
}
form.BackColor = BackgroundColor;
foreach (Control control in form.Controls)
{
Apply(control);
}
}
public void Apply(Control control)
{
if (control is Button button)
{
button.ForeColor = ButtonTextColor;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderColor = ButtonBorderColor;
button.FlatAppearance.MouseOverBackColor = ButtonHoverBackgroundColor;
button.FlatAppearance.MouseDownBackColor = ButtonPressedBackgroundColor;
}
else if (control is LinkLabel linkLabel)
{
linkLabel.LinkColor = HyperlinkColor;
}
else if (control is PlotPanel plotPanel)
{
plotPanel.ApplyTheme();
}
else if (control is TreeViewAdv treeView)
{
treeView.BackColor = TreeBackgroundColor;
treeView.ForeColor = TreeTextColor;
treeView.LineColor = TreeOutlineColor;
}
else
{
control.BackColor = BackgroundColor;
control.ForeColor = ForegroundColor;
}
foreach (Control child in control.Controls)
{
Apply(child);
}
}
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
private const int DWMWA_BORDER_COLOR = 34;
private const int DWMWA_CAPTION_COLOR = 35;
private const int DWMWA_TEXT_COLOR = 36;
private static bool IsWindows10OrGreater(int build = -1)
{
return !Software.OperatingSystem.IsUnix && Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Drawing;
using System.Linq;
namespace LibreHardwareMonitor.UI.Themes
{
public class BlackTheme : LightTheme
{
private readonly Color[] _plotColorPalette;
public override Color ForegroundColor => Color.FromArgb(218, 218, 218);
public override Color BackgroundColor => Color.FromArgb(0, 0, 0);
public override Color HyperlinkColor => Color.FromArgb(144, 220, 232);
public override Color SelectedForegroundColor => ForegroundColor;
public override Color SelectedBackgroundColor => ColorTranslator.FromHtml("#090A17");
public override Color LineColor => ColorTranslator.FromHtml("#070A12");
public override Color StrongLineColor => ColorTranslator.FromHtml("#091217");
public override Color[] PlotColorPalette => _plotColorPalette;
public override Color PlotGridMajorColor => Color.FromArgb(73, 73, 73);
public override Color PlotGridMinorColor => Color.FromArgb(33, 33, 33);
public override bool WindowTitlebarFallbackToImmersiveDarkMode => true;
public BlackTheme() : base("black", "Black")
{
string[] colors = {
"#FF2525",
"#1200FF",
"#00FF5B",
"#FFE53B",
"#00FFFF",
"#FF0A6C",
"#2D27FF",
"#FF2CDF",
"#00E1FD",
"#0A5057"
};
_plotColorPalette = colors.Select(color => ColorTranslator.FromHtml(color)).ToArray();
}
}
}

View File

@@ -0,0 +1,41 @@
using System.Drawing;
using System.Linq;
namespace LibreHardwareMonitor.UI.Themes
{
public class DarkTheme : LightTheme
{
private readonly Color[] _plotColorPalette;
public override Color ForegroundColor => Color.FromArgb(233, 233, 233);
public override Color BackgroundColor => Color.FromArgb(30, 30, 30);
public override Color HyperlinkColor => Color.FromArgb(144, 220, 232);
public override Color SelectedForegroundColor => ForegroundColor;
public override Color SelectedBackgroundColor => Color.FromArgb(45, 45, 45);
public override Color LineColor => Color.FromArgb(38, 38, 38);
public override Color StrongLineColor => Color.FromArgb(53, 53, 53);
public override Color[] PlotColorPalette => _plotColorPalette;
public override Color PlotGridMajorColor => Color.FromArgb(93, 93, 93);
public override Color PlotGridMinorColor => Color.FromArgb(53, 53, 53);
public override bool WindowTitlebarFallbackToImmersiveDarkMode => true;
public DarkTheme() : base("dark", "Dark")
{
string[] colors = {
"#F07178",
"#82AAFF",
"#C3E88D",
"#FFCB6B",
"#009688",
"#89DDF3",
"#FFE082",
"#7986CB",
"#C792EA",
"#FF5370",
"#73d1c8",
"#F78C6A"
};
_plotColorPalette = colors.Select(color => ColorTranslator.FromHtml(color)).ToArray();
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Drawing;
namespace LibreHardwareMonitor.UI.Themes
{
public class LightTheme : Theme
{
private readonly Color[] _plotColorPalette = new Color[] {
Color.Blue,
Color.OrangeRed,
Color.Green,
Color.LightSeaGreen,
Color.Goldenrod,
Color.DarkViolet,
Color.YellowGreen,
Color.SaddleBrown,
Color.RoyalBlue,
Color.DeepPink,
Color.MediumSeaGreen,
Color.Olive,
Color.Firebrick
};
public LightTheme(string id, string displayName) : base(id, displayName) { }
public override Color ForegroundColor => Color.FromArgb(0, 0, 0);
public override Color BackgroundColor => Color.FromArgb(255, 255, 255);
public override Color HyperlinkColor => Color.FromArgb(0, 0, 255);
public override Color SelectedForegroundColor => ForegroundColor;
public override Color SelectedBackgroundColor => Color.FromArgb(240, 240, 240);
public override Color LineColor => Color.FromArgb(247, 247, 247);
public override Color StrongLineColor => Color.FromArgb(209, 209, 209);
public override Color[] PlotColorPalette => _plotColorPalette;
public override Color PlotGridMajorColor => Color.FromArgb(192, 192, 192);
public override Color PlotGridMinorColor => Color.FromArgb(232, 232, 232);
public override bool WindowTitlebarFallbackToImmersiveDarkMode => false;
public LightTheme() : base("light", "Light") { }
}
}

View File

@@ -0,0 +1,114 @@
using System.Drawing;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI.Themes
{
public class ThemedHScrollIndicator : Control
{
private readonly HScrollBar _scrollbar;
private int _startValue = 0;
private int _startPos = 0;
private bool _isScrolling = false;
public static void AddToControl(Control control)
{
foreach (Control child in control.Controls)
{
if (child is HScrollBar scrollbar)
{
control.Controls.Add(new ThemedHScrollIndicator(scrollbar));
return;
}
}
}
public ThemedHScrollIndicator(HScrollBar scrollBar)
{
_scrollbar = scrollBar;
Height = 8;
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Left = 0;
Top = scrollBar.Parent.Height - Height;
Size = new Size(scrollBar.Parent.Width, Height);
Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Bottom;
Visible = scrollBar.Visible;
scrollBar.VisibleChanged += (s, e) => Visible = (s as ScrollBar).Visible;
scrollBar.Scroll += (s, e) => Invalidate();
scrollBar.ValueChanged += (s, e) => Invalidate();
scrollBar.Height = 0;
this.MouseDown += OnMouseDown;
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
if (_isScrolling)
return;
_isScrolling = true;
//note: this.Capture is true when the control is clicked, no need to handle this
_startPos = e.X;
_startValue = _scrollbar.Value;
this.MouseUp += OnMouseUp;
this.MouseMove += OnMouseMove;
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
_isScrolling = false;
this.MouseUp -= OnMouseUp;
this.MouseMove -= OnMouseMove;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!_isScrolling)
return;
//pixel to range scaling:
double totalRange = _scrollbar.Maximum - _scrollbar.Minimum;
if (totalRange <= 0)
return;
double scaleToPercent = totalRange / Bounds.Width;
double scrollValue = _startValue + (e.X - _startPos) * scaleToPercent;
if (scrollValue < _scrollbar.Minimum)
scrollValue = _scrollbar.Minimum;
if (scrollValue > (_scrollbar.Maximum - _scrollbar.LargeChange))
scrollValue = _scrollbar.Maximum - _scrollbar.LargeChange;
_scrollbar.Value = (int)scrollValue;
Refresh();
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
using (SolidBrush brush = new SolidBrush(Theme.Current.ScrollbarBackground))
g.FillRectangle(brush, new Rectangle(0, 0, Bounds.Width, Bounds.Height));
int width = Bounds.Width;
int range = _scrollbar.Maximum - _scrollbar.Minimum;
if (range > 0)
{
int start = width * (_scrollbar.Value - _scrollbar.Minimum) / range;
int end = width * (_scrollbar.Value - _scrollbar.Minimum + _scrollbar.LargeChange) / range;
using (SolidBrush brush = new SolidBrush(Theme.Current.ScrollbarTrack))
g.FillRectangle(brush, new Rectangle(start, 2, end - start, Bounds.Height - 4));
}
}
}
}

View File

@@ -0,0 +1,71 @@
using System.Drawing;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI.Themes
{
public class ThemedToolStripRenderer : ToolStripRenderer
{
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
{
if (e.Item is not ToolStripSeparator)
{
base.OnRenderSeparator(e);
return;
}
Rectangle bounds = new(Point.Empty, e.Item.Size);
using (Brush brush = new SolidBrush(Theme.Current.MenuBackgroundColor))
e.Graphics.FillRectangle(brush, bounds);
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
e.ArrowColor = e.Item.Selected ? Theme.Current.MenuSelectedForegroundColor : Theme.Current.MenuForegroundColor;
base.OnRenderArrow(e);
}
protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e)
{
using (Pen pen = new Pen(e.Item.Selected ? Theme.Current.MenuSelectedForegroundColor : Theme.Current.MenuForegroundColor))
{
int x = 10;
int y = 6;
e.Graphics.DrawLine(pen, x, y + 3, x + 2, y + 5);
e.Graphics.DrawLine(pen, x + 2, y + 5, x + 6, y + 1);
e.Graphics.DrawLine(pen, x, y + 4, x + 2, y + 6);
e.Graphics.DrawLine(pen, x + 2, y + 6, x + 6, y + 2);
}
}
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
e.TextColor = e.Item.Selected ? Theme.Current.MenuSelectedForegroundColor : Theme.Current.MenuForegroundColor;
base.OnRenderItemText(e);
}
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{
if (e.ToolStrip.Parent is not Form)
{
Rectangle bounds = new(Point.Empty, new Size(e.ToolStrip.Width - 1, e.ToolStrip.Height - 1));
using (Pen pen = new Pen(Theme.Current.MenuBorderColor))
e.Graphics.DrawRectangle(pen, bounds);
}
}
protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e)
{
Rectangle bounds = new(Point.Empty, e.ToolStrip.Size);
using (Brush brush = new SolidBrush(Theme.Current.MenuBackgroundColor))
e.Graphics.FillRectangle(brush, bounds);
}
protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
{
Rectangle bounds = new(Point.Empty, e.Item.Size);
using (Brush brush = new SolidBrush(e.Item.Selected ? Theme.Current.MenuSelectedBackgroundColor : Theme.Current.MenuBackgroundColor))
e.Graphics.FillRectangle(brush, bounds);
}
}
}

View File

@@ -0,0 +1,114 @@
using System.Drawing;
using System.Windows.Forms;
namespace LibreHardwareMonitor.UI.Themes
{
public class ThemedVScrollIndicator : Control
{
private readonly VScrollBar _scrollbar;
private int _startValue = 0;
private int _startPos = 0;
private bool _isScrolling = false;
public static void AddToControl(Control control)
{
foreach (Control child in control.Controls)
{
if (child is VScrollBar scrollbar)
{
control.Controls.Add(new ThemedVScrollIndicator(scrollbar));
return;
}
}
}
public ThemedVScrollIndicator(VScrollBar scrollBar)
{
_scrollbar = scrollBar;
Width = 8;
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Left = scrollBar.Parent.Width - Width;
Top = 0;
Size = new Size(Width, scrollBar.Parent.Height);
Anchor = AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
Visible = scrollBar.Visible;
scrollBar.VisibleChanged += (s, e) => Visible = (s as ScrollBar).Visible;
scrollBar.Scroll += (s, e) => Invalidate();
scrollBar.ValueChanged += (s, e) => Invalidate();
scrollBar.Width = 0;
this.MouseDown += OnMouseDown;
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
if (_isScrolling)
return;
_isScrolling = true;
//note: this.Capture is true when the control is clicked, no need to handle this
_startPos = e.Y;
_startValue = _scrollbar.Value;
this.MouseUp += OnMouseUp;
this.MouseMove += OnMouseMove;
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
_isScrolling = false;
this.MouseUp -= OnMouseUp;
this.MouseMove -= OnMouseMove;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!_isScrolling)
return;
//pixel to range scaling:
double totalRange = _scrollbar.Maximum - _scrollbar.Minimum;
if (totalRange <= 0)
return;
double scaleToPercent = totalRange / Bounds.Height;
double scrollValue = _startValue + (e.Y - _startPos) * scaleToPercent;
if (scrollValue < _scrollbar.Minimum)
scrollValue = _scrollbar.Minimum;
if (scrollValue > (_scrollbar.Maximum - _scrollbar.LargeChange))
scrollValue = _scrollbar.Maximum - _scrollbar.LargeChange;
_scrollbar.Value = (int)scrollValue;
Refresh();
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
using (SolidBrush brush = new SolidBrush(Theme.Current.ScrollbarBackground))
g.FillRectangle(brush, new Rectangle(0, 0, Bounds.Width, Bounds.Height));
int height = Bounds.Height;
int range = _scrollbar.Maximum - _scrollbar.Minimum;
if (range > 0)
{
int start = height * (_scrollbar.Value - _scrollbar.Minimum) / range;
int end = height * (_scrollbar.Value - _scrollbar.Minimum + _scrollbar.LargeChange) / range;
using (SolidBrush brush = new SolidBrush(Theme.Current.ScrollbarTrack))
g.FillRectangle(brush, new Rectangle(2, start, Bounds.Width - 4, end - start));
}
}
}
}

View File

@@ -0,0 +1,254 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace LibreHardwareMonitor.UI;
/// <summary>
/// https://docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-display-option-buttons-in-a-menustrip-windows-forms
/// </summary>
public class ToolStripRadioButtonMenuItem : ToolStripMenuItem
{
public ToolStripRadioButtonMenuItem()
: base()
{
Initialize();
}
public ToolStripRadioButtonMenuItem(string text)
: base(text, null, (EventHandler)null)
{
Initialize();
}
public ToolStripRadioButtonMenuItem(Image image)
: base(null, image, (EventHandler)null)
{
Initialize();
}
public ToolStripRadioButtonMenuItem(string text, Image image)
: base(text, image, (EventHandler)null)
{
Initialize();
}
public ToolStripRadioButtonMenuItem(string text, Image image,
EventHandler onClick)
: base(text, image, onClick)
{
Initialize();
}
public ToolStripRadioButtonMenuItem(string text, Image image,
EventHandler onClick, string name)
: base(text, image, onClick, name)
{
Initialize();
}
public ToolStripRadioButtonMenuItem(string text, Image image,
params ToolStripItem[] dropDownItems)
: base(text, image, dropDownItems)
{
Initialize();
}
public ToolStripRadioButtonMenuItem(string text, Image image,
EventHandler onClick, Keys shortcutKeys)
: base(text, image, onClick)
{
Initialize();
ShortcutKeys = shortcutKeys;
}
// Called by all constructors to initialize CheckOnClick.
private void Initialize()
{
CheckOnClick = true;
}
protected override void OnCheckedChanged(EventArgs e)
{
base.OnCheckedChanged(e);
// If this item is no longer in the checked state or if its
// parent has not yet been initialized, do nothing.
if (!Checked || Parent == null) return;
// Clear the checked state for all siblings.
foreach (ToolStripItem item in Parent.Items)
{
if (item is ToolStripRadioButtonMenuItem radioItem
&& radioItem != this
&& radioItem.Checked)
{
radioItem.Checked = false;
// Only one item can be selected at a time,
// so there is no need to continue.
return;
}
}
}
protected override void OnClick(EventArgs e)
{
// If the item is already in the checked state, do not call
// the base method, which would toggle the value.
if (Checked) return;
base.OnClick(e);
}
// Let the item paint itself, and then paint the RadioButton
// where the check mark is normally displayed.
protected override void OnPaint(PaintEventArgs e)
{
if (Image != null)
{
// If the client sets the Image property, the selection behavior
// remains unchanged, but the RadioButton is not displayed and the
// selection is indicated only by the selection rectangle.
base.OnPaint(e);
return;
}
else
{
// If the Image property is not set, call the base OnPaint method
// with the CheckState property temporarily cleared to prevent
// the check mark from being painted.
CheckState currentState = CheckState;
CheckState = CheckState.Unchecked;
base.OnPaint(e);
CheckState = currentState;
}
// Determine the correct state of the RadioButton.
RadioButtonState buttonState = RadioButtonState.UncheckedNormal;
if (Enabled)
{
if (mouseDownState)
{
if (Checked)
buttonState = RadioButtonState.CheckedPressed;
else
buttonState = RadioButtonState.UncheckedPressed;
}
else if (mouseHoverState)
{
if (Checked)
buttonState = RadioButtonState.CheckedHot;
else
buttonState = RadioButtonState.UncheckedHot;
}
else
{
if (Checked)
buttonState = RadioButtonState.CheckedNormal;
}
}
else
{
if (Checked)
buttonState = RadioButtonState.CheckedDisabled;
else
buttonState = RadioButtonState.UncheckedDisabled;
}
// Calculate the position at which to display the RadioButton.
int offset = (ContentRectangle.Height -
RadioButtonRenderer.GetGlyphSize(
e.Graphics, buttonState).Height) / 2;
Point imageLocation = new Point(
ContentRectangle.Location.X + 4,
ContentRectangle.Location.Y + offset);
// Paint the RadioButton.
RadioButtonRenderer.DrawRadioButton(e.Graphics, imageLocation, buttonState);
}
private bool mouseHoverState;
protected override void OnMouseEnter(EventArgs e)
{
mouseHoverState = true;
// Force the item to repaint with the new RadioButton state.
Invalidate();
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
mouseHoverState = false;
base.OnMouseLeave(e);
}
private bool mouseDownState;
protected override void OnMouseDown(MouseEventArgs e)
{
mouseDownState = true;
// Force the item to repaint with the new RadioButton state.
Invalidate();
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
mouseDownState = false;
base.OnMouseUp(e);
}
// Enable the item only if its parent item is in the checked state
// and its Enabled property has not been explicitly set to false.
public override bool Enabled
{
get
{
// Use the base value in design mode to prevent the designer
// from setting the base value to the calculated value.
if (!DesignMode
&& OwnerItem is ToolStripMenuItem ownerMenuItem
&& ownerMenuItem.CheckOnClick)
{
return base.Enabled && ownerMenuItem.Checked;
}
else
{
return base.Enabled;
}
}
set
{
base.Enabled = value;
}
}
// When OwnerItem becomes available, if it is a ToolStripMenuItem
// with a CheckOnClick property value of true, subscribe to its
// CheckedChanged event.
protected override void OnOwnerChanged(EventArgs e)
{
if (OwnerItem is ToolStripMenuItem ownerMenuItem
&& ownerMenuItem.CheckOnClick)
{
ownerMenuItem.CheckedChanged +=
new EventHandler(OwnerMenuItem_CheckedChanged);
}
base.OnOwnerChanged(e);
}
// When the checked state of the parent item changes,
// repaint the item so that the new Enabled state is displayed.
private void OwnerMenuItem_CheckedChanged(object sender, EventArgs e)
{
Invalidate();
}
}

View File

@@ -0,0 +1,124 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Aga.Controls.Tree;
namespace LibreHardwareMonitor.UI;
public class TreeModel : ITreeModel
{
private readonly Node _root;
private bool _forceVisible;
public TreeModel()
{
_root = new Node { Model = this };
}
public TreePath GetPath(Node node)
{
if (node == _root)
return TreePath.Empty;
Stack<object> stack = new Stack<object>();
while (node != _root)
{
stack.Push(node);
node = node.Parent;
}
return new TreePath(stack.ToArray());
}
public Collection<Node> Nodes
{
get { return _root.Nodes; }
}
private Node GetNode(TreePath treePath)
{
Node parent = _root;
foreach (object obj in treePath.FullPath)
{
if (!(obj is Node node) || node.Parent != parent)
return null;
parent = node;
}
return parent;
}
public IEnumerable GetChildren(TreePath treePath)
{
Node node = GetNode(treePath);
if (node != null)
{
return node.Nodes.Where(n => _forceVisible || n.IsVisible).ToList();
}
return Enumerable.Empty<Node>();
}
public bool IsLeaf(TreePath treePath)
{
return false;
}
public bool ForceVisible
{
get
{
return _forceVisible;
}
set
{
if (value != _forceVisible)
{
_forceVisible = value;
OnStructureChanged(_root);
}
}
}
#pragma warning disable 67
public event EventHandler<TreeModelEventArgs> NodesChanged;
public event EventHandler<TreePathEventArgs> StructureChanged;
public event EventHandler<TreeModelEventArgs> NodesInserted;
public event EventHandler<TreeModelEventArgs> NodesRemoved;
#pragma warning restore 67
public void OnNodeChanged(Node parent, int index, Node node)
{
if (parent != null)
{
TreePath path = GetPath(parent);
if (path != null)
NodesChanged?.Invoke(this, new TreeModelEventArgs(path, new[] { index }, new object[] { node }));
}
}
public void OnStructureChanged(Node node)
{
StructureChanged?.Invoke(this, new TreeModelEventArgs(GetPath(node), new object[0]));
}
public void OnNodeInserted(Node parent, int index, Node node)
{
TreeModelEventArgs args = new TreeModelEventArgs(GetPath(parent), new[] { index }, new object[] { node });
NodesInserted?.Invoke(this, args);
}
public void OnNodeRemoved(Node parent, int index, Node node)
{
TreeModelEventArgs args = new TreeModelEventArgs(GetPath(parent), new[] { index }, new object[] { node });
NodesRemoved?.Invoke(this, args);
}
}

View File

@@ -0,0 +1,149 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public sealed class TypeNode : Node, IExpandPersistNode
{
private readonly PersistentSettings _settings;
private readonly string _expandedIdentifier;
private bool _expanded;
public TypeNode(SensorType sensorType, Identifier parentId, PersistentSettings settings)
{
SensorType = sensorType;
_expandedIdentifier = new Identifier(parentId, SensorType.ToString(), ".expanded").ToString();
_settings = settings;
switch (sensorType)
{
case SensorType.Voltage:
Image = Utilities.EmbeddedResources.GetImage("voltage.png");
Text = "Voltages";
break;
case SensorType.Current:
Image = Utilities.EmbeddedResources.GetImage("voltage.png");
Text = "Currents";
break;
case SensorType.Energy:
Image = Utilities.EmbeddedResources.GetImage("battery.png");
Text = "Capacities";
break;
case SensorType.Clock:
Image = Utilities.EmbeddedResources.GetImage("clock.png");
Text = "Clocks";
break;
case SensorType.Load:
Image = Utilities.EmbeddedResources.GetImage("load.png");
Text = "Load";
break;
case SensorType.Temperature:
Image = Utilities.EmbeddedResources.GetImage("temperature.png");
Text = "Temperatures";
break;
case SensorType.Fan:
Image = Utilities.EmbeddedResources.GetImage("fan.png");
Text = "Fans";
break;
case SensorType.Flow:
Image = Utilities.EmbeddedResources.GetImage("flow.png");
Text = "Flows";
break;
case SensorType.Control:
Image = Utilities.EmbeddedResources.GetImage("control.png");
Text = "Controls";
break;
case SensorType.Level:
Image = Utilities.EmbeddedResources.GetImage("level.png");
Text = "Levels";
break;
case SensorType.Power:
Image = Utilities.EmbeddedResources.GetImage("power.png");
Text = "Powers";
break;
case SensorType.Data:
Image = Utilities.EmbeddedResources.GetImage("data.png");
Text = "Data";
break;
case SensorType.SmallData:
Image = Utilities.EmbeddedResources.GetImage("data.png");
Text = "Data";
break;
case SensorType.Factor:
Image = Utilities.EmbeddedResources.GetImage("factor.png");
Text = "Factors";
break;
case SensorType.Frequency:
Image = Utilities.EmbeddedResources.GetImage("clock.png");
Text = "Frequencies";
break;
case SensorType.Throughput:
Image = Utilities.EmbeddedResources.GetImage("throughput.png");
Text = "Throughput";
break;
case SensorType.TimeSpan:
Image = Utilities.EmbeddedResources.GetImage("time.png");
Text = "Times";
break;
case SensorType.Noise:
Image = Utilities.EmbeddedResources.GetImage("loudspeaker.png");
Text = "Noise Levels";
break;
case SensorType.Conductivity:
Image = Utilities.EmbeddedResources.GetImage("voltage.png");
Text = "Conductivities";
break;
case SensorType.Humidity:
Image = Utilities.EmbeddedResources.GetImage("humidity.png");
Text = "Humidity Levels";
break;
}
NodeAdded += TypeNode_NodeAdded;
NodeRemoved += TypeNode_NodeRemoved;
_expanded = settings.GetValue(_expandedIdentifier, true);
}
private void TypeNode_NodeRemoved(Node node)
{
node.IsVisibleChanged -= Node_IsVisibleChanged;
Node_IsVisibleChanged(null);
}
private void TypeNode_NodeAdded(Node node)
{
node.IsVisibleChanged += Node_IsVisibleChanged;
Node_IsVisibleChanged(null);
}
private void Node_IsVisibleChanged(Node node)
{
foreach (Node n in Nodes)
{
if (n.IsVisible)
{
IsVisible = true;
return;
}
}
IsVisible = false;
}
public SensorType SensorType { get; }
public bool Expanded
{
get => _expanded;
set
{
_expanded = value;
_settings.SetValue(_expandedIdentifier, _expanded);
}
}
}

View File

@@ -0,0 +1,43 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public enum TemperatureUnit
{
Celsius = 0,
Fahrenheit = 1
}
public class UnitManager
{
private readonly PersistentSettings _settings;
private TemperatureUnit _temperatureUnit;
public UnitManager(PersistentSettings settings)
{
_settings = settings;
_temperatureUnit = (TemperatureUnit)settings.GetValue("TemperatureUnit", (int)TemperatureUnit.Celsius);
}
public TemperatureUnit TemperatureUnit
{
get { return _temperatureUnit; }
set
{
_temperatureUnit = value;
_settings.SetValue("TemperatureUnit", (int)_temperatureUnit);
}
}
public static float? CelsiusToFahrenheit(float? valueInCelsius)
{
return valueInCelsius * 1.8f + 32;
}
}

View File

@@ -0,0 +1,28 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using LibreHardwareMonitor.Hardware;
namespace LibreHardwareMonitor.UI;
public class UpdateVisitor : IVisitor
{
public void VisitComputer(IComputer computer)
{
computer.Traverse(this);
}
public void VisitHardware(IHardware hardware)
{
hardware.Update();
foreach (IHardware subHardware in hardware.SubHardware)
subHardware.Accept(this);
}
public void VisitSensor(ISensor sensor) { }
public void VisitParameter(IParameter parameter) { }
}

View File

@@ -0,0 +1,64 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Windows.Forms;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public class UserOption
{
private readonly string _name;
private bool _value;
private readonly ToolStripMenuItem _menuItem;
private event EventHandler _changed;
private readonly PersistentSettings _settings;
public UserOption(string name, bool value, ToolStripMenuItem menuItem, PersistentSettings settings)
{
_settings = settings;
_name = name;
_value = name != null ? settings.GetValue(name, value) : value;
_menuItem = menuItem;
_menuItem.Checked = _value;
_menuItem.Click += MenuItem_Click;
}
private void MenuItem_Click(object sender, EventArgs e)
{
Value = !Value;
}
public bool Value
{
get { return _value; }
set
{
if (_value != value)
{
_value = value;
if (_name != null)
_settings.SetValue(_name, value);
_menuItem.Checked = value;
_changed?.Invoke(this, null);
}
}
}
public event EventHandler Changed
{
add
{
_changed += value;
_changed?.Invoke(this, null);
}
remove
{
_changed -= value;
}
}
}

View File

@@ -0,0 +1,69 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Windows.Forms;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
public class UserRadioGroup
{
private readonly string _name;
private int _value;
private readonly ToolStripMenuItem[] _menuItems;
private event EventHandler _changed;
private readonly PersistentSettings _settings;
public UserRadioGroup(string name, int value, ToolStripMenuItem[] menuItems, PersistentSettings settings)
{
_settings = settings;
_name = name;
_value = name != null ? settings.GetValue(name, value) : value;
_menuItems = menuItems;
_value = Math.Max(Math.Min(_value, menuItems.Length - 1), 0);
for (int i = 0; i < _menuItems.Length; i++)
{
_menuItems[i].Checked = i == _value;
int index = i;
_menuItems[i].Click += delegate
{
Value = index;
};
}
}
public int Value
{
get { return _value; }
set
{
if (_value != value)
{
_value = value;
if (_name != null)
_settings.SetValue(_name, value);
for (int i = 0; i < _menuItems.Length; i++)
_menuItems[i].Checked = i == value;
_changed?.Invoke(this, null);
}
}
}
public event EventHandler Changed
{
add
{
_changed += value;
_changed?.Invoke(this, null);
}
remove
{
_changed -= value;
}
}
}