Profile settings
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.13.35919.96 d17.13
|
VisualStudioVersion = 17.13.35919.96
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proxmox-spice-launcher", "src\proxmox-spice-launcher\proxmox-spice-launcher\proxmox-spice-launcher.csproj", "{9A4EC4BE-8920-461F-8653-A9650A68B727}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proxmox-spice-launcher", "src\proxmox-spice-launcher\proxmox-spice-launcher.csproj", "{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -11,10 +11,10 @@ Global
|
|||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Proxmox.SpiceLauncher.Extensions;
|
||||||
|
internal static class ObservableCollectionExtensions
|
||||||
|
{
|
||||||
|
public static ObservableCollection<T> Sort<T>(this ObservableCollection<T> collection, Comparison<T> comparison)
|
||||||
|
{
|
||||||
|
var sortableList = new List<T>(collection);
|
||||||
|
sortableList.Sort(comparison);
|
||||||
|
for (int i = 0; i < sortableList.Count; i++)
|
||||||
|
{
|
||||||
|
collection.Move(collection.IndexOf(sortableList[i]), i);
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
xmlns:local="clr-namespace:Proxmox.SpiceLauncher"
|
xmlns:local="clr-namespace:Proxmox.SpiceLauncher"
|
||||||
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Proxmox Spice Launcher" Height="391" Width="350" ResizeMode="NoResize"
|
Title="Proxmox Spice Launcher" Height="391" Width="539" ResizeMode="NoResize"
|
||||||
Initialized="MetroWindow_Initialized" Closing="MetroWindow_Closing" SizeToContent="Height"
|
Initialized="MetroWindow_Initialized" Closing="MetroWindow_Closing" SizeToContent="Height"
|
||||||
Icon="pack://application:,,,/Proxmox.SpiceLauncher.png">
|
Icon="pack://application:,,,/Proxmox.SpiceLauncher.png">
|
||||||
<mah:MetroWindow.Resources>
|
<mah:MetroWindow.Resources>
|
||||||
@ -29,18 +29,24 @@
|
|||||||
</mah:MetroWindow.RightWindowCommands>
|
</mah:MetroWindow.RightWindowCommands>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel Margin="10,10,10,10">
|
<StackPanel>
|
||||||
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
|
<StackPanel Grid.IsSharedSizeScope="True" Margin="10,10,10,10">
|
||||||
<mah:MetroHeader Header="Profile">
|
<mah:MetroHeader Header="Profile" HorizontalAlignment="Stretch"
|
||||||
<ComboBox x:Name="ComboProfiles" SelectionChanged="ComboProfiles_OnSelectionChanged" />
|
VerticalAlignment="Stretch">
|
||||||
|
<Grid>
|
||||||
|
<ComboBox x:Name="ComboProfiles" SelectionChanged="ComboProfiles_OnSelectionChanged" Margin="0,0,100,0" />
|
||||||
|
<Button x:Name="ButtonConnect" Content="Connect" Click="ButtonConnect_Click"
|
||||||
|
HorizontalAlignment="Right" Width="90" />
|
||||||
|
</Grid>
|
||||||
</mah:MetroHeader>
|
</mah:MetroHeader>
|
||||||
<Button x:Name="ButtonConnect" Content="Connect" Click="ButtonConnect_Click" Margin="5,0,0,0" />
|
<mah:MetroHeader Header="VM" HorizontalAlignment="Stretch"
|
||||||
</StackPanel>
|
VerticalAlignment="Stretch">
|
||||||
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
|
<Grid>
|
||||||
<mah:MetroHeader Header="VM">
|
<ComboBox x:Name="ComboVms" Margin="0,0,100,0" />
|
||||||
<ComboBox x:Name="ComboVms" />
|
<Button x:Name="ButtonSpice" Content="Spice" Click="ButtonSpice_Click"
|
||||||
|
HorizontalAlignment="Right" IsEnabled="False" Width="90" />
|
||||||
|
</Grid>
|
||||||
</mah:MetroHeader>
|
</mah:MetroHeader>
|
||||||
<Button x:Name="ButtonSpice" Content="Spice" Click="ButtonSpice_Click" IsEnabled="False" Margin="5,0,0,0" RenderTransformOrigin="4.474,0.491" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -19,7 +20,7 @@ namespace Proxmox.SpiceLauncher;
|
|||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private PveClient? _client;
|
private PveClient? _client;
|
||||||
private List<Settings> _settings;
|
private ObservableCollection<Settings> _settings;
|
||||||
private List<VmWrapper> _vms = new();
|
private List<VmWrapper> _vms = new();
|
||||||
private readonly string _settingsFile;
|
private readonly string _settingsFile;
|
||||||
|
|
||||||
@ -30,22 +31,13 @@ public partial class MainWindow
|
|||||||
}
|
}
|
||||||
private async void MetroWindow_Initialized(object sender, EventArgs e)
|
private async void MetroWindow_Initialized(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
List<Settings>? settings = null;
|
ObservableCollection<Settings>? settings = null;
|
||||||
if (File.Exists(_settingsFile))
|
if (File.Exists(_settingsFile))
|
||||||
{
|
{
|
||||||
await using var f = File.OpenRead(_settingsFile);
|
await using var f = File.OpenRead(_settingsFile);
|
||||||
settings = await JsonSerializer.DeserializeAsync<List<Settings>>(f);
|
settings = await JsonSerializer.DeserializeAsync<ObservableCollection<Settings>>(f);
|
||||||
}
|
|
||||||
if (settings == null || settings.Count == 0)
|
|
||||||
{
|
|
||||||
settings =
|
|
||||||
[
|
|
||||||
new Settings()
|
|
||||||
{
|
|
||||||
Server = "192.168.0.190"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
settings ??= [];
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
ComboProfiles.ItemsSource = _settings;
|
ComboProfiles.ItemsSource = _settings;
|
||||||
ComboProfiles.SelectedIndex = 0;
|
ComboProfiles.SelectedIndex = 0;
|
||||||
@ -54,13 +46,12 @@ public partial class MainWindow
|
|||||||
|
|
||||||
private void ButtonSettings_Click(object sender, RoutedEventArgs e)
|
private void ButtonSettings_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
var settings = new SettingsWindow
|
var settings = new SettingsWindow
|
||||||
{
|
{
|
||||||
Owner = this
|
Owner = this,
|
||||||
|
Settings = _settings
|
||||||
};
|
};
|
||||||
settings.ShowDialog();
|
settings.ShowDialog();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DoEvent(Func<Task> e)
|
private async Task DoEvent(Func<Task> e)
|
||||||
@ -105,6 +96,12 @@ public partial class MainWindow
|
|||||||
throw new Exception("Login failed");
|
throw new Exception("Login failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nodes = await _client.GetHostAndIpAsync();
|
||||||
|
var current = nodes.FirstOrDefault(x => x.Value == _client.Host);
|
||||||
|
if (current.Key != null)
|
||||||
|
{
|
||||||
|
settings.Name = current.Key;
|
||||||
|
}
|
||||||
var vms = await _client.GetVmsAsync();
|
var vms = await _client.GetVmsAsync();
|
||||||
foreach (var vm in vms)
|
foreach (var vm in vms)
|
||||||
{
|
{
|
50
src/proxmox-spice-launcher/Models/Settings.cs
Normal file
50
src/proxmox-spice-launcher/Models/Settings.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Proxmox.SpiceLauncher.Models;
|
||||||
|
|
||||||
|
public class Settings{
|
||||||
|
private string? _password;
|
||||||
|
private string? _passwordEnc;
|
||||||
|
public string? Server { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? Username { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string? Password
|
||||||
|
{
|
||||||
|
get => _password;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_password = value;
|
||||||
|
_passwordEnc = value != null ? DPApi.EncryptStringToBase64(value) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? PasswordEnc
|
||||||
|
{
|
||||||
|
get => _passwordEnc;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_passwordEnc = value;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_password = value == null ? null : DPApi.DecryptStringFromBase64(value);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_password = null;
|
||||||
|
_passwordEnc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var server = (Username != null ? $"{Username}@" : null) + Server ?? "Unknown";
|
||||||
|
if (Name != null)
|
||||||
|
{
|
||||||
|
return $"{Name} ({server})";
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
26
src/proxmox-spice-launcher/SettingsWindow.xaml
Normal file
26
src/proxmox-spice-launcher/SettingsWindow.xaml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<mah:MetroWindow x:Class="Proxmox.SpiceLauncher.SettingsWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
|
||||||
|
xmlns:local="clr-namespace:Proxmox.SpiceLauncher"
|
||||||
|
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Settings" Height="368" Width="625" ResizeMode="NoResize" WindowStartupLocation="CenterOwner"
|
||||||
|
SizeToContent="Height">
|
||||||
|
<mah:MetroWindow.Resources>
|
||||||
|
<Style BasedOn="{StaticResource MahApps.Styles.MetroHeader.Horizontal}" TargetType="mah:MetroHeader" />
|
||||||
|
</mah:MetroWindow.Resources>
|
||||||
|
<Grid>
|
||||||
|
<StackPanel Margin="10,10,10,10">
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,10">
|
||||||
|
<Button x:Name="ButtonAdd" Content="{iconPacks:FontAwesome Kind=PlusSolid}" Click="ButtonAdd_Click"
|
||||||
|
Margin="0,0,5,0" />
|
||||||
|
<Button x:Name="ButtonDelete" Content="{iconPacks:FontAwesome Kind=MinusSolid}" Click="ButtonDelete_Click"
|
||||||
|
Margin="0,0,5,0" />
|
||||||
|
</StackPanel>
|
||||||
|
<ListBox Name="ListSettings"></ListBox>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</mah:MetroWindow>
|
50
src/proxmox-spice-launcher/SettingsWindow.xaml.cs
Normal file
50
src/proxmox-spice-launcher/SettingsWindow.xaml.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Proxmox.SpiceLauncher.Models;
|
||||||
|
using System.Windows;
|
||||||
|
using MahApps.Metro.Controls.Dialogs;
|
||||||
|
using Proxmox.SpiceLauncher.Extensions;
|
||||||
|
|
||||||
|
namespace Proxmox.SpiceLauncher;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for SettingsWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class SettingsWindow
|
||||||
|
{
|
||||||
|
private ObservableCollection<Settings> _settings;
|
||||||
|
|
||||||
|
public SettingsWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<Settings> Settings
|
||||||
|
{
|
||||||
|
get => _settings;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_settings = value;
|
||||||
|
ListSettings.ItemsSource = _settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ButtonAdd_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var server = await this.ShowInputAsync("Proxmox Server", "Proxmox server IP or Hostname");
|
||||||
|
if (string.IsNullOrEmpty(server))
|
||||||
|
return;
|
||||||
|
Settings.Add(new Settings
|
||||||
|
{
|
||||||
|
Server = server
|
||||||
|
});
|
||||||
|
Settings.Sort((a, b) => string.Compare(a.ToString(), b.ToString(), StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonDelete_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (ListSettings.SelectedItem is Settings settings)
|
||||||
|
{
|
||||||
|
_settings.Remove(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Proxmox.SpiceLauncher.Models;
|
|
||||||
|
|
||||||
class Settings
|
|
||||||
{
|
|
||||||
private string _password;
|
|
||||||
private string _passwordEnc;
|
|
||||||
public string Server { get; set; }
|
|
||||||
public string Username { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string Password
|
|
||||||
{
|
|
||||||
get => _password;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_password = value;
|
|
||||||
_passwordEnc = DPApi.EncryptStringToBase64(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string PasswordEnc
|
|
||||||
{
|
|
||||||
get => _passwordEnc;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_passwordEnc = value;
|
|
||||||
_password = DPApi.DecryptStringFromBase64(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Server;
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user