mirror of https://github.com/2dust/v2rayN
Refactor the main interface
parent
355a424be2
commit
bc3593871b
|
@ -74,6 +74,7 @@ namespace v2rayN
|
||||||
public const string CommandClearMsg = "CommandClearMsg";
|
public const string CommandClearMsg = "CommandClearMsg";
|
||||||
public const string CommandSendMsgView = "CommandSendMsgView";
|
public const string CommandSendMsgView = "CommandSendMsgView";
|
||||||
public const string CommandStopSpeedTest = "CommandStopSpeedTest";
|
public const string CommandStopSpeedTest = "CommandStopSpeedTest";
|
||||||
|
public const string CommandRefreshProfiles = "CommandRefreshProfiles";
|
||||||
public const string DelayUnit = "";
|
public const string DelayUnit = "";
|
||||||
public const string SpeedUnit = "";
|
public const string SpeedUnit = "";
|
||||||
public const int MinFontSize = 10;
|
public const int MinFontSize = 10;
|
||||||
|
|
|
@ -4,6 +4,9 @@ namespace v2rayN.Handler.Statistics
|
||||||
{
|
{
|
||||||
internal class StatisticsHandler
|
internal class StatisticsHandler
|
||||||
{
|
{
|
||||||
|
private static readonly Lazy<StatisticsHandler> instance = new(() => new());
|
||||||
|
public static StatisticsHandler Instance => instance.Value;
|
||||||
|
|
||||||
private Config _config;
|
private Config _config;
|
||||||
private ServerStatItem? _serverStatItem;
|
private ServerStatItem? _serverStatItem;
|
||||||
private List<ServerStatItem> _lstServerStat;
|
private List<ServerStatItem> _lstServerStat;
|
||||||
|
@ -12,20 +15,17 @@ namespace v2rayN.Handler.Statistics
|
||||||
private StatisticsSingbox? _statisticsSingbox;
|
private StatisticsSingbox? _statisticsSingbox;
|
||||||
|
|
||||||
public List<ServerStatItem> ServerStat => _lstServerStat;
|
public List<ServerStatItem> ServerStat => _lstServerStat;
|
||||||
public bool Enable { get; set; }
|
|
||||||
|
|
||||||
public StatisticsHandler(Config config, Action<ServerSpeedItem> update)
|
public void Init(Config config, Action<ServerSpeedItem> update)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
Enable = config.guiItem.enableStatistics;
|
_updateFunc = update;
|
||||||
if (!Enable)
|
if (!config.guiItem.enableStatistics)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateFunc = update;
|
InitData();
|
||||||
|
|
||||||
Init();
|
|
||||||
|
|
||||||
_statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat);
|
_statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat);
|
||||||
_statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat);
|
_statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat);
|
||||||
|
@ -55,7 +55,10 @@ namespace v2rayN.Handler.Statistics
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SQLiteHelper.Instance.UpdateAll(_lstServerStat);
|
if (_lstServerStat != null)
|
||||||
|
{
|
||||||
|
SQLiteHelper.Instance.UpdateAll(_lstServerStat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -63,7 +66,7 @@ namespace v2rayN.Handler.Statistics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Init()
|
private void InitData()
|
||||||
{
|
{
|
||||||
SQLiteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
SQLiteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
namespace v2rayN.Models
|
namespace v2rayN.Models
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
internal class ServerSpeedItem : ServerStatItem
|
public class ServerSpeedItem : ServerStatItem
|
||||||
{
|
{
|
||||||
public long proxyUp
|
public long proxyUp
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,6 @@ using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using v2rayN.Enums;
|
using v2rayN.Enums;
|
||||||
using v2rayN.Handler;
|
using v2rayN.Handler;
|
||||||
using v2rayN.Handler.Fmt;
|
|
||||||
using v2rayN.Handler.Statistics;
|
using v2rayN.Handler.Statistics;
|
||||||
using v2rayN.Models;
|
using v2rayN.Models;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
@ -29,14 +28,9 @@ namespace v2rayN.ViewModels
|
||||||
#region private prop
|
#region private prop
|
||||||
|
|
||||||
private CoreHandler _coreHandler;
|
private CoreHandler _coreHandler;
|
||||||
private StatisticsHandler _statistics;
|
|
||||||
private List<ProfileItem> _lstProfile;
|
|
||||||
private string _subId = string.Empty;
|
|
||||||
private string _serverFilter = string.Empty;
|
|
||||||
private static Config _config;
|
private static Config _config;
|
||||||
private NoticeHandler? _noticeHandler;
|
private NoticeHandler? _noticeHandler;
|
||||||
private readonly PaletteHelper _paletteHelper = new();
|
private readonly PaletteHelper _paletteHelper = new();
|
||||||
private Dictionary<string, bool> _dicHeaderSort = new();
|
|
||||||
private Action<EViewAction> _updateView;
|
private Action<EViewAction> _updateView;
|
||||||
private bool _showInTaskbar;
|
private bool _showInTaskbar;
|
||||||
|
|
||||||
|
@ -44,38 +38,18 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
#region ObservableCollection
|
#region ObservableCollection
|
||||||
|
|
||||||
private IObservableCollection<ProfileItemModel> _profileItems = new ObservableCollectionExtended<ProfileItemModel>();
|
|
||||||
public IObservableCollection<ProfileItemModel> ProfileItems => _profileItems;
|
|
||||||
|
|
||||||
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
|
||||||
public IObservableCollection<SubItem> SubItems => _subItems;
|
|
||||||
|
|
||||||
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
|
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
|
||||||
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
|
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
|
||||||
|
|
||||||
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
||||||
public IObservableCollection<ComboItem> Servers => _servers;
|
public IObservableCollection<ComboItem> Servers => _servers;
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public ProfileItemModel SelectedProfile { get; set; }
|
|
||||||
|
|
||||||
public IList<ProfileItemModel> SelectedProfiles { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public SubItem SelectedSub { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public SubItem SelectedMoveToGroup { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public RoutingItem SelectedRouting { get; set; }
|
public RoutingItem SelectedRouting { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ComboItem SelectedServer { get; set; }
|
public ComboItem SelectedServer { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string ServerFilter { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public bool BlServers { get; set; }
|
public bool BlServers { get; set; }
|
||||||
|
|
||||||
|
@ -98,41 +72,9 @@ namespace v2rayN.ViewModels
|
||||||
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
||||||
|
|
||||||
//servers delete
|
|
||||||
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> RemoveServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RemoveDuplicateServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerCmd { get; }
|
|
||||||
|
|
||||||
//servers move
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
|
||||||
|
|
||||||
//servers ping
|
|
||||||
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> TcpingServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RealPingServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; }
|
|
||||||
|
|
||||||
//servers export
|
|
||||||
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> Export2ShareUrlCmd { get; }
|
|
||||||
|
|
||||||
//Subscription
|
//Subscription
|
||||||
public ReactiveCommand<Unit, Unit> SubSettingCmd { get; }
|
public ReactiveCommand<Unit, Unit> SubSettingCmd { get; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> AddSubCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> EditSubCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SubUpdateCmd { get; }
|
public ReactiveCommand<Unit, Unit> SubUpdateCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SubUpdateViaProxyCmd { get; }
|
public ReactiveCommand<Unit, Unit> SubUpdateViaProxyCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SubGroupUpdateCmd { get; }
|
public ReactiveCommand<Unit, Unit> SubGroupUpdateCmd { get; }
|
||||||
|
@ -147,7 +89,6 @@ namespace v2rayN.ViewModels
|
||||||
public ReactiveCommand<Unit, Unit> RebootAsAdminCmd { get; }
|
public ReactiveCommand<Unit, Unit> RebootAsAdminCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ClearServerStatisticsCmd { get; }
|
public ReactiveCommand<Unit, Unit> ClearServerStatisticsCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> OpenTheFileLocationCmd { get; }
|
public ReactiveCommand<Unit, Unit> OpenTheFileLocationCmd { get; }
|
||||||
//public ReactiveCommand<Unit, Unit> ImportOldGuiConfigCmd { get; }
|
|
||||||
|
|
||||||
//CheckUpdate
|
//CheckUpdate
|
||||||
public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; }
|
public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; }
|
||||||
|
@ -169,9 +110,6 @@ namespace v2rayN.ViewModels
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ImageSource AppIcon { get; set; }
|
public ImageSource AppIcon { get; set; }
|
||||||
|
|
||||||
//[Reactive]
|
|
||||||
//public bool BlShowTrayTip { get; set; }
|
|
||||||
|
|
||||||
#endregion Menu
|
#endregion Menu
|
||||||
|
|
||||||
#region System Proxy
|
#region System Proxy
|
||||||
|
@ -246,7 +184,7 @@ namespace v2rayN.ViewModels
|
||||||
public string CurrentLanguage { get; set; }
|
public string CurrentLanguage { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public bool ShowCalshUI { get; set; }
|
public bool ShowClashUI { get; set; }
|
||||||
|
|
||||||
#endregion UI
|
#endregion UI
|
||||||
|
|
||||||
|
@ -257,13 +195,12 @@ namespace v2rayN.ViewModels
|
||||||
_updateView = updateView;
|
_updateView = updateView;
|
||||||
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
|
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
|
||||||
|
|
||||||
Locator.CurrentMutable.RegisterLazySingleton(() => new NoticeHandler(snackbarMessageQueue), typeof(NoticeHandler));
|
_noticeHandler = new NoticeHandler(snackbarMessageQueue);
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
Locator.CurrentMutable.RegisterLazySingleton(() => _noticeHandler, typeof(NoticeHandler));
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
|
|
||||||
SelectedProfile = new();
|
MessageBus.Current.Listen<string>(Global.CommandRefreshProfiles).Subscribe(x => RefreshServersBiz());
|
||||||
SelectedSub = new();
|
|
||||||
SelectedMoveToGroup = new();
|
|
||||||
SelectedRouting = new();
|
SelectedRouting = new();
|
||||||
SelectedServer = new();
|
SelectedServer = new();
|
||||||
if (_config.tunModeItem.enableTun)
|
if (_config.tunModeItem.enableTun)
|
||||||
|
@ -277,7 +214,6 @@ namespace v2rayN.ViewModels
|
||||||
_config.tunModeItem.enableTun = EnableTun = false;
|
_config.tunModeItem.enableTun = EnableTun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_subId = _config.subIndexId;
|
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
BindingUI();
|
BindingUI();
|
||||||
|
@ -285,19 +221,6 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
#region WhenAnyValue && ReactiveCommand
|
#region WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
|
||||||
x => x.SelectedProfile,
|
|
||||||
selectedSource => selectedSource != null && !selectedSource.indexId.IsNullOrEmpty());
|
|
||||||
|
|
||||||
this.WhenAnyValue(
|
|
||||||
x => x.SelectedSub,
|
|
||||||
y => y != null && !y.remarks.IsNullOrEmpty() && _subId != y.id)
|
|
||||||
.Subscribe(c => SubSelectedChanged(c));
|
|
||||||
this.WhenAnyValue(
|
|
||||||
x => x.SelectedMoveToGroup,
|
|
||||||
y => y != null && !y.remarks.IsNullOrEmpty())
|
|
||||||
.Subscribe(c => MoveToGroup(c));
|
|
||||||
|
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(
|
||||||
x => x.SelectedRouting,
|
x => x.SelectedRouting,
|
||||||
y => y != null && !y.remarks.IsNullOrEmpty())
|
y => y != null && !y.remarks.IsNullOrEmpty())
|
||||||
|
@ -308,11 +231,6 @@ namespace v2rayN.ViewModels
|
||||||
y => y != null && !y.Text.IsNullOrEmpty())
|
y => y != null && !y.Text.IsNullOrEmpty())
|
||||||
.Subscribe(c => ServerSelectedChanged(c));
|
.Subscribe(c => ServerSelectedChanged(c));
|
||||||
|
|
||||||
this.WhenAnyValue(
|
|
||||||
x => x.ServerFilter,
|
|
||||||
y => y != null && _serverFilter != y)
|
|
||||||
.Subscribe(c => ServerFilterChanged(c));
|
|
||||||
|
|
||||||
SystemProxySelected = (int)_config.sysProxyType;
|
SystemProxySelected = (int)_config.sysProxyType;
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(
|
||||||
x => x.SystemProxySelected,
|
x => x.SystemProxySelected,
|
||||||
|
@ -327,43 +245,43 @@ namespace v2rayN.ViewModels
|
||||||
//servers
|
//servers
|
||||||
AddVmessServerCmd = ReactiveCommand.Create(() =>
|
AddVmessServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.VMess);
|
AddServer(true, EConfigType.VMess);
|
||||||
});
|
});
|
||||||
AddVlessServerCmd = ReactiveCommand.Create(() =>
|
AddVlessServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.VLESS);
|
AddServer(true, EConfigType.VLESS);
|
||||||
});
|
});
|
||||||
AddShadowsocksServerCmd = ReactiveCommand.Create(() =>
|
AddShadowsocksServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Shadowsocks);
|
AddServer(true, EConfigType.Shadowsocks);
|
||||||
});
|
});
|
||||||
AddSocksServerCmd = ReactiveCommand.Create(() =>
|
AddSocksServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Socks);
|
AddServer(true, EConfigType.Socks);
|
||||||
});
|
});
|
||||||
AddHttpServerCmd = ReactiveCommand.Create(() =>
|
AddHttpServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Http);
|
AddServer(true, EConfigType.Http);
|
||||||
});
|
});
|
||||||
AddTrojanServerCmd = ReactiveCommand.Create(() =>
|
AddTrojanServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Trojan);
|
AddServer(true, EConfigType.Trojan);
|
||||||
});
|
});
|
||||||
AddHysteria2ServerCmd = ReactiveCommand.Create(() =>
|
AddHysteria2ServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Hysteria2);
|
AddServer(true, EConfigType.Hysteria2);
|
||||||
});
|
});
|
||||||
AddTuicServerCmd = ReactiveCommand.Create(() =>
|
AddTuicServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Tuic);
|
AddServer(true, EConfigType.Tuic);
|
||||||
});
|
});
|
||||||
AddWireguardServerCmd = ReactiveCommand.Create(() =>
|
AddWireguardServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Wireguard);
|
AddServer(true, EConfigType.Wireguard);
|
||||||
});
|
});
|
||||||
AddCustomServerCmd = ReactiveCommand.Create(() =>
|
AddCustomServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
EditServer(true, EConfigType.Custom);
|
AddServer(true, EConfigType.Custom);
|
||||||
});
|
});
|
||||||
AddServerViaClipboardCmd = ReactiveCommand.Create(() =>
|
AddServerViaClipboardCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
|
@ -373,97 +291,13 @@ namespace v2rayN.ViewModels
|
||||||
{
|
{
|
||||||
return ScanScreenTaskAsync();
|
return ScanScreenTaskAsync();
|
||||||
});
|
});
|
||||||
//servers delete
|
|
||||||
EditServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
EditServer(false, EConfigType.Custom);
|
|
||||||
}, canEditRemove);
|
|
||||||
RemoveServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
RemoveServer();
|
|
||||||
}, canEditRemove);
|
|
||||||
RemoveDuplicateServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
RemoveDuplicateServer();
|
|
||||||
});
|
|
||||||
CopyServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
CopyServer();
|
|
||||||
}, canEditRemove);
|
|
||||||
SetDefaultServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SetDefaultServer();
|
|
||||||
}, canEditRemove);
|
|
||||||
ShareServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
ShareServer();
|
|
||||||
}, canEditRemove);
|
|
||||||
SetDefaultMultipleServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SetDefaultMultipleServer();
|
|
||||||
}, canEditRemove);
|
|
||||||
//servers move
|
|
||||||
MoveTopCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
MoveServer(EMove.Top);
|
|
||||||
}, canEditRemove);
|
|
||||||
MoveUpCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
MoveServer(EMove.Up);
|
|
||||||
}, canEditRemove);
|
|
||||||
MoveDownCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
MoveServer(EMove.Down);
|
|
||||||
}, canEditRemove);
|
|
||||||
MoveBottomCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
MoveServer(EMove.Bottom);
|
|
||||||
}, canEditRemove);
|
|
||||||
|
|
||||||
//servers ping
|
|
||||||
MixedTestServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
ServerSpeedtest(ESpeedActionType.Mixedtest);
|
|
||||||
});
|
|
||||||
TcpingServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
ServerSpeedtest(ESpeedActionType.Tcping);
|
|
||||||
}, canEditRemove);
|
|
||||||
RealPingServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
ServerSpeedtest(ESpeedActionType.Realping);
|
|
||||||
}, canEditRemove);
|
|
||||||
SpeedServerCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
ServerSpeedtest(ESpeedActionType.Speedtest);
|
|
||||||
}, canEditRemove);
|
|
||||||
SortServerResultCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SortServer(EServerColName.delayVal.ToString());
|
|
||||||
});
|
|
||||||
//servers export
|
|
||||||
Export2ClientConfigCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
Export2ClientConfig();
|
|
||||||
}, canEditRemove);
|
|
||||||
Export2ShareUrlCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
Export2ShareUrl();
|
|
||||||
}, canEditRemove);
|
|
||||||
|
|
||||||
//Subscription
|
//Subscription
|
||||||
SubSettingCmd = ReactiveCommand.Create(() =>
|
SubSettingCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
SubSetting();
|
SubSetting();
|
||||||
});
|
});
|
||||||
AddSubCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
EditSub(true);
|
|
||||||
});
|
|
||||||
EditSubCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
EditSub(false);
|
|
||||||
});
|
|
||||||
SubUpdateCmd = ReactiveCommand.Create(() =>
|
SubUpdateCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
UpdateSubscriptionProcess("", false);
|
UpdateSubscriptionProcess("", false);
|
||||||
|
@ -474,11 +308,11 @@ namespace v2rayN.ViewModels
|
||||||
});
|
});
|
||||||
SubGroupUpdateCmd = ReactiveCommand.Create(() =>
|
SubGroupUpdateCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
UpdateSubscriptionProcess(_subId, false);
|
UpdateSubscriptionProcess(_config.subIndexId, false);
|
||||||
});
|
});
|
||||||
SubGroupUpdateViaProxyCmd = ReactiveCommand.Create(() =>
|
SubGroupUpdateViaProxyCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
UpdateSubscriptionProcess(_subId, true);
|
UpdateSubscriptionProcess(_config.subIndexId, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Setting
|
//Setting
|
||||||
|
@ -507,17 +341,13 @@ namespace v2rayN.ViewModels
|
||||||
});
|
});
|
||||||
ClearServerStatisticsCmd = ReactiveCommand.Create(() =>
|
ClearServerStatisticsCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
_statistics?.ClearAllServerStatistics();
|
StatisticsHandler.Instance.ClearAllServerStatistics();
|
||||||
RefreshServers();
|
RefreshServers();
|
||||||
});
|
});
|
||||||
OpenTheFileLocationCmd = ReactiveCommand.Create(() =>
|
OpenTheFileLocationCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
Utils.ProcessStart("Explorer", $"/select,{Utils.GetConfigPath()}");
|
Utils.ProcessStart("Explorer", $"/select,{Utils.GetConfigPath()}");
|
||||||
});
|
});
|
||||||
//ImportOldGuiConfigCmd = ReactiveCommand.Create(() =>
|
|
||||||
//{
|
|
||||||
// ImportOldGuiConfig();
|
|
||||||
//});
|
|
||||||
|
|
||||||
//CheckUpdate
|
//CheckUpdate
|
||||||
CheckUpdateNCmd = ReactiveCommand.Create(() =>
|
CheckUpdateNCmd = ReactiveCommand.Create(() =>
|
||||||
|
@ -586,15 +416,14 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
if (_config.guiItem.enableStatistics)
|
if (_config.guiItem.enableStatistics)
|
||||||
{
|
{
|
||||||
_statistics = new StatisticsHandler(_config, UpdateStatisticsHandler);
|
StatisticsHandler.Instance.Init(_config, UpdateStatisticsHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainFormHandler.Instance.UpdateTask(_config, UpdateTaskHandler);
|
MainFormHandler.Instance.UpdateTask(_config, UpdateTaskHandler);
|
||||||
MainFormHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, UpdateTaskHandler);
|
MainFormHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, UpdateTaskHandler);
|
||||||
|
|
||||||
InitSubscriptionView();
|
|
||||||
RefreshRoutingsMenu();
|
RefreshRoutingsMenu();
|
||||||
RefreshServers();
|
//RefreshServers();
|
||||||
|
|
||||||
Reload();
|
Reload();
|
||||||
ChangeSystemProxyStatus(_config.sysProxyType, true);
|
ChangeSystemProxyStatus(_config.sysProxyType, true);
|
||||||
|
@ -647,41 +476,19 @@ namespace v2rayN.ViewModels
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (!_showInTaskbar)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
{
|
{
|
||||||
if (!_showInTaskbar)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown));
|
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown));
|
||||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown));
|
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown));
|
||||||
|
|
||||||
if (update.proxyUp + update.proxyDown > 0)
|
if ((update.proxyUp + update.proxyDown) > 0 && DateTime.Now.Second % 3 == 0)
|
||||||
{
|
{
|
||||||
var second = DateTime.Now.Second;
|
Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update);
|
||||||
if (second % 3 == 0)
|
|
||||||
{
|
|
||||||
var item = _profileItems.Where(it => it.indexId == update.indexId).FirstOrDefault();
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
item.todayDown = Utils.HumanFy(update.todayDown);
|
|
||||||
item.todayUp = Utils.HumanFy(update.todayUp);
|
|
||||||
item.totalDown = Utils.HumanFy(update.totalDown);
|
|
||||||
item.totalUp = Utils.HumanFy(update.totalUp);
|
|
||||||
|
|
||||||
if (SelectedProfile?.indexId == item.indexId)
|
|
||||||
{
|
|
||||||
var temp = JsonUtils.DeepCopy(item);
|
|
||||||
_profileItems.Replace(item, temp);
|
|
||||||
SelectedProfile = temp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -691,39 +498,6 @@ namespace v2rayN.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSpeedtestHandler(string indexId, string delay, string speed)
|
|
||||||
{
|
|
||||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
|
||||||
{
|
|
||||||
SetTestResult(indexId, delay, speed);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetTestResult(string indexId, string delay, string speed)
|
|
||||||
{
|
|
||||||
if (Utils.IsNullOrEmpty(indexId))
|
|
||||||
{
|
|
||||||
_noticeHandler?.SendMessage(delay, true);
|
|
||||||
_noticeHandler?.Enqueue(delay);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var item = _profileItems.Where(it => it.indexId == indexId).FirstOrDefault();
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
if (!Utils.IsNullOrEmpty(delay))
|
|
||||||
{
|
|
||||||
int.TryParse(delay, out int temp);
|
|
||||||
item.delay = temp;
|
|
||||||
item.delayVal = $"{delay} {Global.DelayUnit}";
|
|
||||||
}
|
|
||||||
if (!Utils.IsNullOrEmpty(speed))
|
|
||||||
{
|
|
||||||
item.speedVal = $"{speed} {Global.SpeedUnit}";
|
|
||||||
}
|
|
||||||
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHotkeyHandler(EGlobalHotkey e)
|
private void OnHotkeyHandler(EGlobalHotkey e)
|
||||||
{
|
{
|
||||||
switch (e)
|
switch (e)
|
||||||
|
@ -758,7 +532,6 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
ConfigHandler.SaveConfig(_config);
|
ConfigHandler.SaveConfig(_config);
|
||||||
|
|
||||||
//HttpProxyHandle.CloseHttpAgent(config);
|
|
||||||
if (blWindowsShutDown)
|
if (blWindowsShutDown)
|
||||||
{
|
{
|
||||||
SysProxyHandle.ResetIEProxy4WindowsShutDown();
|
SysProxyHandle.ResetIEProxy4WindowsShutDown();
|
||||||
|
@ -770,8 +543,8 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
ProfileExHandler.Instance.SaveTo();
|
ProfileExHandler.Instance.SaveTo();
|
||||||
|
|
||||||
_statistics?.SaveTo();
|
StatisticsHandler.Instance.SaveTo();
|
||||||
_statistics?.Close();
|
StatisticsHandler.Instance.Close();
|
||||||
|
|
||||||
_coreHandler.CoreStop();
|
_coreHandler.CoreStop();
|
||||||
Logging.SaveLog("MyAppExit End");
|
Logging.SaveLog("MyAppExit End");
|
||||||
|
@ -787,91 +560,15 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
#region Servers && Groups
|
#region Servers && Groups
|
||||||
|
|
||||||
private void SubSelectedChanged(bool c)
|
private void RefreshServers()
|
||||||
{
|
{
|
||||||
if (!c)
|
MessageBus.Current.SendMessage("", Global.CommandRefreshProfiles);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_subId = SelectedSub?.id;
|
|
||||||
_config.subIndexId = _subId;
|
|
||||||
|
|
||||||
RefreshServers();
|
|
||||||
|
|
||||||
_updateView(EViewAction.ProfilesFocus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ServerFilterChanged(bool c)
|
private void RefreshServersBiz()
|
||||||
{
|
{
|
||||||
if (!c)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_serverFilter = ServerFilter;
|
|
||||||
if (Utils.IsNullOrEmpty(_serverFilter))
|
|
||||||
{
|
|
||||||
RefreshServers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshServers()
|
|
||||||
{
|
|
||||||
List<ProfileItemModel> lstModel = LazyConfig.Instance.ProfileItems(_subId, _serverFilter);
|
|
||||||
|
|
||||||
ConfigHandler.SetDefaultServer(_config, lstModel);
|
|
||||||
|
|
||||||
List<ServerStatItem> lstServerStat = new();
|
|
||||||
if (_statistics != null && _statistics.Enable)
|
|
||||||
{
|
|
||||||
lstServerStat = _statistics.ServerStat;
|
|
||||||
}
|
|
||||||
var lstProfileExs = ProfileExHandler.Instance.ProfileExs;
|
|
||||||
lstModel = (from t in lstModel
|
|
||||||
join t2 in lstServerStat on t.indexId equals t2.indexId into t2b
|
|
||||||
from t22 in t2b.DefaultIfEmpty()
|
|
||||||
join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b
|
|
||||||
from t33 in t3b.DefaultIfEmpty()
|
|
||||||
select new ProfileItemModel
|
|
||||||
{
|
|
||||||
indexId = t.indexId,
|
|
||||||
configType = t.configType,
|
|
||||||
remarks = t.remarks,
|
|
||||||
address = t.address,
|
|
||||||
port = t.port,
|
|
||||||
security = t.security,
|
|
||||||
network = t.network,
|
|
||||||
streamSecurity = t.streamSecurity,
|
|
||||||
subid = t.subid,
|
|
||||||
subRemarks = t.subRemarks,
|
|
||||||
isActive = t.indexId == _config.indexId,
|
|
||||||
sort = t33 == null ? 0 : t33.sort,
|
|
||||||
delay = t33 == null ? 0 : t33.delay,
|
|
||||||
delayVal = t33?.delay != 0 ? $"{t33?.delay} {Global.DelayUnit}" : string.Empty,
|
|
||||||
speedVal = t33?.speed != 0 ? $"{t33?.speed} {Global.SpeedUnit}" : string.Empty,
|
|
||||||
todayDown = t22 == null ? "" : Utils.HumanFy(t22.todayDown),
|
|
||||||
todayUp = t22 == null ? "" : Utils.HumanFy(t22.todayUp),
|
|
||||||
totalDown = t22 == null ? "" : Utils.HumanFy(t22.totalDown),
|
|
||||||
totalUp = t22 == null ? "" : Utils.HumanFy(t22.totalUp)
|
|
||||||
}).OrderBy(t => t.sort).ToList();
|
|
||||||
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel));
|
|
||||||
|
|
||||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
{
|
{
|
||||||
_profileItems.Clear();
|
|
||||||
_profileItems.AddRange(lstModel);
|
|
||||||
if (lstModel.Count > 0)
|
|
||||||
{
|
|
||||||
var selected = lstModel.FirstOrDefault(t => t.indexId == _config.indexId);
|
|
||||||
if (selected != null)
|
|
||||||
{
|
|
||||||
SelectedProfile = selected;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedProfile = lstModel[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshServersMenu();
|
RefreshServersMenu();
|
||||||
|
|
||||||
//display running server
|
//display running server
|
||||||
|
@ -891,17 +588,19 @@ namespace v2rayN.ViewModels
|
||||||
|
|
||||||
private void RefreshServersMenu()
|
private void RefreshServersMenu()
|
||||||
{
|
{
|
||||||
|
var lstModel = LazyConfig.Instance.ProfileItems(_config.subIndexId, "");
|
||||||
|
|
||||||
_servers.Clear();
|
_servers.Clear();
|
||||||
if (_lstProfile.Count > _config.guiItem.trayMenuServersLimit)
|
if (lstModel.Count > _config.guiItem.trayMenuServersLimit)
|
||||||
{
|
{
|
||||||
BlServers = false;
|
BlServers = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlServers = true;
|
BlServers = true;
|
||||||
for (int k = 0; k < _lstProfile.Count; k++)
|
for (int k = 0; k < lstModel.Count; k++)
|
||||||
{
|
{
|
||||||
ProfileItem it = _lstProfile[k];
|
ProfileItem it = lstModel[k];
|
||||||
string name = it.GetSummary();
|
string name = it.GetSummary();
|
||||||
|
|
||||||
var item = new ComboItem() { ID = it.indexId, Text = name };
|
var item = new ComboItem() { ID = it.indexId, Text = name };
|
||||||
|
@ -913,83 +612,24 @@ namespace v2rayN.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitSubscriptionView()
|
private void RefreshSubscriptions()
|
||||||
{
|
{
|
||||||
_subItems.Clear();
|
Locator.Current.GetService<ProfilesViewModel>()?.RefreshSubscriptions();
|
||||||
|
|
||||||
_subItems.Add(new SubItem { remarks = ResUI.AllGroupServers });
|
|
||||||
foreach (var item in LazyConfig.Instance.SubItems().OrderBy(t => t.sort))
|
|
||||||
{
|
|
||||||
_subItems.Add(item);
|
|
||||||
}
|
|
||||||
if (_subId != null && _subItems.FirstOrDefault(t => t.id == _subId) != null)
|
|
||||||
{
|
|
||||||
SelectedSub = _subItems.FirstOrDefault(t => t.id == _subId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedSub = _subItems[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Servers && Groups
|
#endregion Servers && Groups
|
||||||
|
|
||||||
#region Add Servers
|
#region Add Servers
|
||||||
|
|
||||||
private int GetProfileItems(out List<ProfileItem> lstSelecteds, bool latest)
|
public void AddServer(bool blNew, EConfigType eConfigType)
|
||||||
{
|
{
|
||||||
lstSelecteds = new List<ProfileItem>();
|
ProfileItem item = new()
|
||||||
if (SelectedProfiles == null || SelectedProfiles.Count <= 0)
|
|
||||||
{
|
{
|
||||||
return -1;
|
subid = _config.subIndexId,
|
||||||
}
|
configType = eConfigType,
|
||||||
|
isSub = false,
|
||||||
|
};
|
||||||
|
|
||||||
var orderProfiles = SelectedProfiles?.OrderBy(t => t.sort);
|
|
||||||
if (latest)
|
|
||||||
{
|
|
||||||
foreach (var profile in orderProfiles)
|
|
||||||
{
|
|
||||||
var item = LazyConfig.Instance.GetProfileItem(profile.indexId);
|
|
||||||
if (item is not null)
|
|
||||||
{
|
|
||||||
lstSelecteds.Add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lstSelecteds = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(orderProfiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EditServer(bool blNew, EConfigType eConfigType)
|
|
||||||
{
|
|
||||||
ProfileItem item;
|
|
||||||
if (blNew)
|
|
||||||
{
|
|
||||||
item = new()
|
|
||||||
{
|
|
||||||
subid = _subId,
|
|
||||||
configType = eConfigType,
|
|
||||||
isSub = false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Utils.IsNullOrEmpty(SelectedProfile?.indexId))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
eConfigType = item.configType;
|
|
||||||
}
|
|
||||||
bool? ret = false;
|
bool? ret = false;
|
||||||
if (eConfigType == EConfigType.Custom)
|
if (eConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
|
@ -1012,10 +652,10 @@ namespace v2rayN.ViewModels
|
||||||
public void AddServerViaClipboard()
|
public void AddServerViaClipboard()
|
||||||
{
|
{
|
||||||
var clipboardData = Utils.GetClipboardData();
|
var clipboardData = Utils.GetClipboardData();
|
||||||
int ret = ConfigHandler.AddBatchServers(_config, clipboardData!, _subId, false);
|
int ret = ConfigHandler.AddBatchServers(_config, clipboardData!, _config.subIndexId, false);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
{
|
{
|
||||||
InitSubscriptionView();
|
RefreshSubscriptions();
|
||||||
RefreshServers();
|
RefreshServers();
|
||||||
_noticeHandler?.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
|
_noticeHandler?.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
|
||||||
}
|
}
|
||||||
|
@ -1039,69 +679,16 @@ namespace v2rayN.ViewModels
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int ret = ConfigHandler.AddBatchServers(_config, result, _subId, false);
|
int ret = ConfigHandler.AddBatchServers(_config, result, _config.subIndexId, false);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
{
|
{
|
||||||
InitSubscriptionView();
|
RefreshSubscriptions();
|
||||||
RefreshServers();
|
RefreshServers();
|
||||||
_noticeHandler?.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
|
_noticeHandler?.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveServer()
|
|
||||||
{
|
|
||||||
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var exists = lstSelecteds.Exists(t => t.indexId == _config.indexId);
|
|
||||||
|
|
||||||
ConfigHandler.RemoveServer(_config, lstSelecteds);
|
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
|
|
||||||
RefreshServers();
|
|
||||||
if (exists)
|
|
||||||
{
|
|
||||||
Reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveDuplicateServer()
|
|
||||||
{
|
|
||||||
var tuple = ConfigHandler.DedupServerList(_config, _subId);
|
|
||||||
RefreshServers();
|
|
||||||
Reload();
|
|
||||||
_noticeHandler?.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CopyServer()
|
|
||||||
{
|
|
||||||
if (GetProfileItems(out List<ProfileItem> lstSelecteds, false) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ConfigHandler.CopyServer(_config, lstSelecteds) == 0)
|
|
||||||
{
|
|
||||||
RefreshServers();
|
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDefaultServer()
|
|
||||||
{
|
|
||||||
if (Utils.IsNullOrEmpty(SelectedProfile?.indexId))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetDefaultServer(SelectedProfile.indexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetDefaultServer(string indexId)
|
private void SetDefaultServer(string indexId)
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(indexId))
|
if (Utils.IsNullOrEmpty(indexId))
|
||||||
|
@ -1143,68 +730,6 @@ namespace v2rayN.ViewModels
|
||||||
SetDefaultServer(SelectedServer.ID);
|
SetDefaultServer(SelectedServer.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ShareServer()
|
|
||||||
{
|
|
||||||
var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var url = FmtHandler.GetShareUri(item);
|
|
||||||
if (Utils.IsNullOrEmpty(url))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var img = QRCodeHelper.GetQRCode(url);
|
|
||||||
var dialog = new QrcodeView()
|
|
||||||
{
|
|
||||||
imgQrcode = { Source = img },
|
|
||||||
txtContent = { Text = url },
|
|
||||||
};
|
|
||||||
|
|
||||||
await DialogHost.Show(dialog, "RootDialog");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetDefaultMultipleServer()
|
|
||||||
{
|
|
||||||
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ConfigHandler.AddCustomServer4Multiple(_config, lstSelecteds, out string indexId) != 0)
|
|
||||||
{
|
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationFailed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (indexId == _config.indexId)
|
|
||||||
{
|
|
||||||
Reload();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetDefaultServer(indexId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortServer(string colName)
|
|
||||||
{
|
|
||||||
if (Utils.IsNullOrEmpty(colName))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dicHeaderSort.TryAdd(colName, true);
|
|
||||||
_dicHeaderSort.TryGetValue(colName, out bool asc);
|
|
||||||
if (ConfigHandler.SortServers(_config, _subId, colName, asc) != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_dicHeaderSort[colName] = !asc;
|
|
||||||
RefreshServers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TestServerAvailability()
|
public void TestServerAvailability()
|
||||||
{
|
{
|
||||||
var item = ConfigHandler.GetDefaultServer(_config);
|
var item = ConfigHandler.GetDefaultServer(_config);
|
||||||
|
@ -1226,109 +751,6 @@ namespace v2rayN.ViewModels
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//move server
|
|
||||||
private void MoveToGroup(bool c)
|
|
||||||
{
|
|
||||||
if (!c)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigHandler.MoveToGroup(_config, lstSelecteds, SelectedMoveToGroup.id);
|
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
|
|
||||||
RefreshServers();
|
|
||||||
SelectedMoveToGroup = new();
|
|
||||||
//Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MoveServer(EMove eMove)
|
|
||||||
{
|
|
||||||
var item = _lstProfile.FirstOrDefault(t => t.indexId == SelectedProfile.indexId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = _lstProfile.IndexOf(item);
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ConfigHandler.MoveServer(_config, ref _lstProfile, index, eMove) == 0)
|
|
||||||
{
|
|
||||||
RefreshServers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MoveServerTo(int startIndex, ProfileItemModel targetItem)
|
|
||||||
{
|
|
||||||
var targetIndex = _profileItems.IndexOf(targetItem);
|
|
||||||
if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex)
|
|
||||||
{
|
|
||||||
if (ConfigHandler.MoveServer(_config, ref _lstProfile, startIndex, EMove.Position, targetIndex) == 0)
|
|
||||||
{
|
|
||||||
RefreshServers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ServerSpeedtest(ESpeedActionType actionType)
|
|
||||||
{
|
|
||||||
if (actionType == ESpeedActionType.Mixedtest)
|
|
||||||
{
|
|
||||||
SelectedProfiles = _profileItems;
|
|
||||||
}
|
|
||||||
if (GetProfileItems(out List<ProfileItem> lstSelecteds, false) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//ClearTestResult();
|
|
||||||
new SpeedtestHandler(_config, _coreHandler, lstSelecteds, actionType, UpdateSpeedtestHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Export2ClientConfig()
|
|
||||||
{
|
|
||||||
var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MainFormHandler.Instance.Export2ClientConfig(item, _config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Export2ShareUrl()
|
|
||||||
{
|
|
||||||
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder sb = new();
|
|
||||||
foreach (var it in lstSelecteds)
|
|
||||||
{
|
|
||||||
var url = FmtHandler.GetShareUri(it);
|
|
||||||
if (Utils.IsNullOrEmpty(url))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sb.Append(url);
|
|
||||||
sb.AppendLine();
|
|
||||||
}
|
|
||||||
if (sb.Length > 0)
|
|
||||||
{
|
|
||||||
Utils.SetClipboardData(sb.ToString());
|
|
||||||
_noticeHandler?.SendMessage(ResUI.BatchExportURLSuccessfully);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Add Servers
|
#endregion Add Servers
|
||||||
|
|
||||||
#region Subscription
|
#region Subscription
|
||||||
|
@ -1337,31 +759,7 @@ namespace v2rayN.ViewModels
|
||||||
{
|
{
|
||||||
if ((new SubSettingWindow()).ShowDialog() == true)
|
if ((new SubSettingWindow()).ShowDialog() == true)
|
||||||
{
|
{
|
||||||
InitSubscriptionView();
|
RefreshSubscriptions();
|
||||||
SubSelectedChanged(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EditSub(bool blNew)
|
|
||||||
{
|
|
||||||
SubItem item;
|
|
||||||
if (blNew)
|
|
||||||
{
|
|
||||||
item = new();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item = LazyConfig.Instance.GetSubItem(_subId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var ret = (new SubEditWindow(item)).ShowDialog();
|
|
||||||
if (ret == true)
|
|
||||||
{
|
|
||||||
InitSubscriptionView();
|
|
||||||
SubSelectedChanged(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1516,8 +914,8 @@ namespace v2rayN.ViewModels
|
||||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
{
|
{
|
||||||
BlReloadEnabled = true;
|
BlReloadEnabled = true;
|
||||||
ShowCalshUI = (_config.runningCoreType is ECoreType.sing_box or ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo);
|
ShowClashUI = (_config.runningCoreType is ECoreType.sing_box or ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo);
|
||||||
if (ShowCalshUI)
|
if (ShowClashUI)
|
||||||
{
|
{
|
||||||
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
|
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,791 @@
|
||||||
|
using DynamicData;
|
||||||
|
using DynamicData.Binding;
|
||||||
|
using MaterialDesignThemes.Wpf;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Fody.Helpers;
|
||||||
|
using Splat;
|
||||||
|
using System.Reactive;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using v2rayN.Enums;
|
||||||
|
using v2rayN.Handler;
|
||||||
|
using v2rayN.Handler.Fmt;
|
||||||
|
using v2rayN.Handler.Statistics;
|
||||||
|
using v2rayN.Models;
|
||||||
|
using v2rayN.Resx;
|
||||||
|
using v2rayN.Views;
|
||||||
|
|
||||||
|
namespace v2rayN.ViewModels
|
||||||
|
{
|
||||||
|
public class ProfilesViewModel : ReactiveObject
|
||||||
|
{
|
||||||
|
#region private prop
|
||||||
|
|
||||||
|
private List<ProfileItem> _lstProfile;
|
||||||
|
private string _serverFilter = string.Empty;
|
||||||
|
private static Config _config;
|
||||||
|
private NoticeHandler? _noticeHandler;
|
||||||
|
private Dictionary<string, bool> _dicHeaderSort = new();
|
||||||
|
private Action<EViewAction> _updateView;
|
||||||
|
|
||||||
|
#endregion private prop
|
||||||
|
|
||||||
|
#region ObservableCollection
|
||||||
|
|
||||||
|
private IObservableCollection<ProfileItemModel> _profileItems = new ObservableCollectionExtended<ProfileItemModel>();
|
||||||
|
public IObservableCollection<ProfileItemModel> ProfileItems => _profileItems;
|
||||||
|
|
||||||
|
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
||||||
|
public IObservableCollection<SubItem> SubItems => _subItems;
|
||||||
|
|
||||||
|
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public ProfileItemModel SelectedProfile { get; set; }
|
||||||
|
|
||||||
|
public IList<ProfileItemModel> SelectedProfiles { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public SubItem SelectedSub { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public SubItem SelectedMoveToGroup { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public ComboItem SelectedServer { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string ServerFilter { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public bool BlServers { get; set; }
|
||||||
|
|
||||||
|
#endregion ObservableCollection
|
||||||
|
|
||||||
|
#region Menu
|
||||||
|
|
||||||
|
//servers delete
|
||||||
|
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> RemoveServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RemoveDuplicateServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerCmd { get; }
|
||||||
|
|
||||||
|
//servers move
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
||||||
|
|
||||||
|
//servers ping
|
||||||
|
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> TcpingServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RealPingServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; }
|
||||||
|
|
||||||
|
//servers export
|
||||||
|
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> Export2ShareUrlCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> AddSubCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> EditSubCmd { get; }
|
||||||
|
|
||||||
|
#endregion Menu
|
||||||
|
|
||||||
|
#region Init
|
||||||
|
|
||||||
|
public ProfilesViewModel(Action<EViewAction> updateView)
|
||||||
|
{
|
||||||
|
_updateView = updateView;
|
||||||
|
|
||||||
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
|
MessageBus.Current.Listen<string>(Global.CommandRefreshProfiles).Subscribe(x => RefreshServersBiz());
|
||||||
|
|
||||||
|
SelectedProfile = new();
|
||||||
|
SelectedSub = new();
|
||||||
|
SelectedMoveToGroup = new();
|
||||||
|
SelectedServer = new();
|
||||||
|
|
||||||
|
RefreshSubscriptions();
|
||||||
|
RefreshServers();
|
||||||
|
|
||||||
|
#region WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
|
var canEditRemove = this.WhenAnyValue(
|
||||||
|
x => x.SelectedProfile,
|
||||||
|
selectedSource => selectedSource != null && !selectedSource.indexId.IsNullOrEmpty());
|
||||||
|
|
||||||
|
this.WhenAnyValue(
|
||||||
|
x => x.SelectedSub,
|
||||||
|
y => y != null && !y.remarks.IsNullOrEmpty() && _config.subIndexId != y.id)
|
||||||
|
.Subscribe(c => SubSelectedChanged(c));
|
||||||
|
this.WhenAnyValue(
|
||||||
|
x => x.SelectedMoveToGroup,
|
||||||
|
y => y != null && !y.remarks.IsNullOrEmpty())
|
||||||
|
.Subscribe(c => MoveToGroup(c));
|
||||||
|
|
||||||
|
this.WhenAnyValue(
|
||||||
|
x => x.SelectedServer,
|
||||||
|
y => y != null && !y.Text.IsNullOrEmpty())
|
||||||
|
.Subscribe(c => ServerSelectedChanged(c));
|
||||||
|
|
||||||
|
this.WhenAnyValue(
|
||||||
|
x => x.ServerFilter,
|
||||||
|
y => y != null && _serverFilter != y)
|
||||||
|
.Subscribe(c => ServerFilterChanged(c));
|
||||||
|
|
||||||
|
//servers delete
|
||||||
|
EditServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
EditServer(false, EConfigType.Custom);
|
||||||
|
}, canEditRemove);
|
||||||
|
RemoveServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
RemoveServer();
|
||||||
|
}, canEditRemove);
|
||||||
|
RemoveDuplicateServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
RemoveDuplicateServer();
|
||||||
|
});
|
||||||
|
CopyServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
CopyServer();
|
||||||
|
}, canEditRemove);
|
||||||
|
SetDefaultServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
SetDefaultServer();
|
||||||
|
}, canEditRemove);
|
||||||
|
ShareServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
ShareServer();
|
||||||
|
}, canEditRemove);
|
||||||
|
SetDefaultMultipleServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
SetDefaultMultipleServer();
|
||||||
|
}, canEditRemove);
|
||||||
|
//servers move
|
||||||
|
MoveTopCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
MoveServer(EMove.Top);
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveUpCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
MoveServer(EMove.Up);
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveDownCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
MoveServer(EMove.Down);
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveBottomCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
MoveServer(EMove.Bottom);
|
||||||
|
}, canEditRemove);
|
||||||
|
|
||||||
|
//servers ping
|
||||||
|
MixedTestServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
ServerSpeedtest(ESpeedActionType.Mixedtest);
|
||||||
|
});
|
||||||
|
TcpingServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
ServerSpeedtest(ESpeedActionType.Tcping);
|
||||||
|
}, canEditRemove);
|
||||||
|
RealPingServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
ServerSpeedtest(ESpeedActionType.Realping);
|
||||||
|
}, canEditRemove);
|
||||||
|
SpeedServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
ServerSpeedtest(ESpeedActionType.Speedtest);
|
||||||
|
}, canEditRemove);
|
||||||
|
SortServerResultCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
SortServer(EServerColName.delayVal.ToString());
|
||||||
|
});
|
||||||
|
//servers export
|
||||||
|
Export2ClientConfigCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
Export2ClientConfig();
|
||||||
|
}, canEditRemove);
|
||||||
|
Export2ShareUrlCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
Export2ShareUrl();
|
||||||
|
}, canEditRemove);
|
||||||
|
|
||||||
|
//Subscription
|
||||||
|
|
||||||
|
AddSubCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
EditSub(true);
|
||||||
|
});
|
||||||
|
EditSubCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
EditSub(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion WhenAnyValue && ReactiveCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Init
|
||||||
|
|
||||||
|
#region Actions
|
||||||
|
|
||||||
|
private void Reload()
|
||||||
|
{
|
||||||
|
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSpeedtestHandler(string indexId, string delay, string speed)
|
||||||
|
{
|
||||||
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
|
{
|
||||||
|
SetTestResult(indexId, delay, speed);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTestResult(string indexId, string delay, string speed)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(indexId))
|
||||||
|
{
|
||||||
|
_noticeHandler?.SendMessage(delay, true);
|
||||||
|
_noticeHandler?.Enqueue(delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var item = _profileItems.Where(it => it.indexId == indexId).FirstOrDefault();
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
if (!Utils.IsNullOrEmpty(delay))
|
||||||
|
{
|
||||||
|
int.TryParse(delay, out int temp);
|
||||||
|
item.delay = temp;
|
||||||
|
item.delayVal = $"{delay} {Global.DelayUnit}";
|
||||||
|
}
|
||||||
|
if (!Utils.IsNullOrEmpty(speed))
|
||||||
|
{
|
||||||
|
item.speedVal = $"{speed} {Global.SpeedUnit}";
|
||||||
|
}
|
||||||
|
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateStatistics(ServerSpeedItem update)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
|
{
|
||||||
|
var item = _profileItems.Where(it => it.indexId == update.indexId).FirstOrDefault();
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
item.todayDown = Utils.HumanFy(update.todayDown);
|
||||||
|
item.todayUp = Utils.HumanFy(update.todayUp);
|
||||||
|
item.totalDown = Utils.HumanFy(update.totalDown);
|
||||||
|
item.totalUp = Utils.HumanFy(update.totalUp);
|
||||||
|
|
||||||
|
if (SelectedProfile?.indexId == item.indexId)
|
||||||
|
{
|
||||||
|
var temp = JsonUtils.DeepCopy(item);
|
||||||
|
_profileItems.Replace(item, temp);
|
||||||
|
SelectedProfile = temp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Actions
|
||||||
|
|
||||||
|
#region Servers && Groups
|
||||||
|
|
||||||
|
private void SubSelectedChanged(bool c)
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_config.subIndexId = SelectedSub?.id;
|
||||||
|
|
||||||
|
RefreshServers();
|
||||||
|
|
||||||
|
_updateView(EViewAction.ProfilesFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServerFilterChanged(bool c)
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_serverFilter = ServerFilter;
|
||||||
|
if (Utils.IsNullOrEmpty(_serverFilter))
|
||||||
|
{
|
||||||
|
RefreshServers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshServers()
|
||||||
|
{
|
||||||
|
MessageBus.Current.SendMessage("", Global.CommandRefreshProfiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshServersBiz()
|
||||||
|
{
|
||||||
|
var lstModel = LazyConfig.Instance.ProfileItems(_config.subIndexId, _serverFilter);
|
||||||
|
|
||||||
|
ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||||
|
|
||||||
|
var lstServerStat = (_config.guiItem.enableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||||
|
var lstProfileExs = ProfileExHandler.Instance.ProfileExs;
|
||||||
|
lstModel = (from t in lstModel
|
||||||
|
join t2 in lstServerStat on t.indexId equals t2.indexId into t2b
|
||||||
|
from t22 in t2b.DefaultIfEmpty()
|
||||||
|
join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b
|
||||||
|
from t33 in t3b.DefaultIfEmpty()
|
||||||
|
select new ProfileItemModel
|
||||||
|
{
|
||||||
|
indexId = t.indexId,
|
||||||
|
configType = t.configType,
|
||||||
|
remarks = t.remarks,
|
||||||
|
address = t.address,
|
||||||
|
port = t.port,
|
||||||
|
security = t.security,
|
||||||
|
network = t.network,
|
||||||
|
streamSecurity = t.streamSecurity,
|
||||||
|
subid = t.subid,
|
||||||
|
subRemarks = t.subRemarks,
|
||||||
|
isActive = t.indexId == _config.indexId,
|
||||||
|
sort = t33 == null ? 0 : t33.sort,
|
||||||
|
delay = t33 == null ? 0 : t33.delay,
|
||||||
|
delayVal = t33?.delay != 0 ? $"{t33?.delay} {Global.DelayUnit}" : string.Empty,
|
||||||
|
speedVal = t33?.speed != 0 ? $"{t33?.speed} {Global.SpeedUnit}" : string.Empty,
|
||||||
|
todayDown = t22 == null ? "" : Utils.HumanFy(t22.todayDown),
|
||||||
|
todayUp = t22 == null ? "" : Utils.HumanFy(t22.todayUp),
|
||||||
|
totalDown = t22 == null ? "" : Utils.HumanFy(t22.totalDown),
|
||||||
|
totalUp = t22 == null ? "" : Utils.HumanFy(t22.totalUp)
|
||||||
|
}).OrderBy(t => t.sort).ToList();
|
||||||
|
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel));
|
||||||
|
|
||||||
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
|
{
|
||||||
|
_profileItems.Clear();
|
||||||
|
_profileItems.AddRange(lstModel);
|
||||||
|
if (lstModel.Count > 0)
|
||||||
|
{
|
||||||
|
var selected = lstModel.FirstOrDefault(t => t.indexId == _config.indexId);
|
||||||
|
if (selected != null)
|
||||||
|
{
|
||||||
|
SelectedProfile = selected;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectedProfile = lstModel[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshSubscriptions()
|
||||||
|
{
|
||||||
|
_subItems.Clear();
|
||||||
|
|
||||||
|
_subItems.Add(new SubItem { remarks = ResUI.AllGroupServers });
|
||||||
|
foreach (var item in LazyConfig.Instance.SubItems().OrderBy(t => t.sort))
|
||||||
|
{
|
||||||
|
_subItems.Add(item);
|
||||||
|
}
|
||||||
|
if (_config.subIndexId != null && _subItems.FirstOrDefault(t => t.id == _config.subIndexId) != null)
|
||||||
|
{
|
||||||
|
SelectedSub = _subItems.FirstOrDefault(t => t.id == _config.subIndexId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectedSub = _subItems[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Servers && Groups
|
||||||
|
|
||||||
|
#region Add Servers
|
||||||
|
|
||||||
|
private int GetProfileItems(out List<ProfileItem> lstSelecteds, bool latest)
|
||||||
|
{
|
||||||
|
lstSelecteds = new List<ProfileItem>();
|
||||||
|
if (SelectedProfiles == null || SelectedProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orderProfiles = SelectedProfiles?.OrderBy(t => t.sort);
|
||||||
|
if (latest)
|
||||||
|
{
|
||||||
|
foreach (var profile in orderProfiles)
|
||||||
|
{
|
||||||
|
var item = LazyConfig.Instance.GetProfileItem(profile.indexId);
|
||||||
|
if (item is not null)
|
||||||
|
{
|
||||||
|
lstSelecteds.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lstSelecteds = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(orderProfiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EditServer(bool blNew, EConfigType eConfigType)
|
||||||
|
{
|
||||||
|
ProfileItem item;
|
||||||
|
if (blNew)
|
||||||
|
{
|
||||||
|
item = new()
|
||||||
|
{
|
||||||
|
subid = _config.subIndexId,
|
||||||
|
configType = eConfigType,
|
||||||
|
isSub = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(SelectedProfile?.indexId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eConfigType = item.configType;
|
||||||
|
}
|
||||||
|
bool? ret = false;
|
||||||
|
if (eConfigType == EConfigType.Custom)
|
||||||
|
{
|
||||||
|
ret = (new AddServer2Window(item)).ShowDialog();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = (new AddServerWindow(item)).ShowDialog();
|
||||||
|
}
|
||||||
|
if (ret == true)
|
||||||
|
{
|
||||||
|
RefreshServers();
|
||||||
|
if (item.indexId == _config.indexId)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveServer()
|
||||||
|
{
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var exists = lstSelecteds.Exists(t => t.indexId == _config.indexId);
|
||||||
|
|
||||||
|
ConfigHandler.RemoveServer(_config, lstSelecteds);
|
||||||
|
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||||
|
|
||||||
|
RefreshServers();
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveDuplicateServer()
|
||||||
|
{
|
||||||
|
var tuple = ConfigHandler.DedupServerList(_config, _config.subIndexId);
|
||||||
|
RefreshServers();
|
||||||
|
Reload();
|
||||||
|
_noticeHandler?.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyServer()
|
||||||
|
{
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, false) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ConfigHandler.CopyServer(_config, lstSelecteds) == 0)
|
||||||
|
{
|
||||||
|
RefreshServers();
|
||||||
|
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDefaultServer()
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(SelectedProfile?.indexId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetDefaultServer(SelectedProfile.indexId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDefaultServer(string indexId)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(indexId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (indexId == _config.indexId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var item = LazyConfig.Instance.GetProfileItem(indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0)
|
||||||
|
{
|
||||||
|
RefreshServers();
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServerSelectedChanged(bool c)
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SelectedServer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Utils.IsNullOrEmpty(SelectedServer.ID))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetDefaultServer(SelectedServer.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void ShareServer()
|
||||||
|
{
|
||||||
|
var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = FmtHandler.GetShareUri(item);
|
||||||
|
if (Utils.IsNullOrEmpty(url))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var img = QRCodeHelper.GetQRCode(url);
|
||||||
|
var dialog = new QrcodeView()
|
||||||
|
{
|
||||||
|
imgQrcode = { Source = img },
|
||||||
|
txtContent = { Text = url },
|
||||||
|
};
|
||||||
|
|
||||||
|
await DialogHost.Show(dialog, "RootDialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDefaultMultipleServer()
|
||||||
|
{
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigHandler.AddCustomServer4Multiple(_config, lstSelecteds, out string indexId) != 0)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.OperationFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (indexId == _config.indexId)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetDefaultServer(indexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortServer(string colName)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(colName))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dicHeaderSort.TryAdd(colName, true);
|
||||||
|
_dicHeaderSort.TryGetValue(colName, out bool asc);
|
||||||
|
if (ConfigHandler.SortServers(_config, _config.subIndexId, colName, asc) != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_dicHeaderSort[colName] = !asc;
|
||||||
|
RefreshServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
//move server
|
||||||
|
private void MoveToGroup(bool c)
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigHandler.MoveToGroup(_config, lstSelecteds, SelectedMoveToGroup.id);
|
||||||
|
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||||
|
|
||||||
|
RefreshServers();
|
||||||
|
SelectedMoveToGroup = new();
|
||||||
|
//Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MoveServer(EMove eMove)
|
||||||
|
{
|
||||||
|
var item = _lstProfile.FirstOrDefault(t => t.indexId == SelectedProfile.indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = _lstProfile.IndexOf(item);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ConfigHandler.MoveServer(_config, ref _lstProfile, index, eMove) == 0)
|
||||||
|
{
|
||||||
|
RefreshServers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MoveServerTo(int startIndex, ProfileItemModel targetItem)
|
||||||
|
{
|
||||||
|
var targetIndex = _profileItems.IndexOf(targetItem);
|
||||||
|
if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex)
|
||||||
|
{
|
||||||
|
if (ConfigHandler.MoveServer(_config, ref _lstProfile, startIndex, EMove.Position, targetIndex) == 0)
|
||||||
|
{
|
||||||
|
RefreshServers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ServerSpeedtest(ESpeedActionType actionType)
|
||||||
|
{
|
||||||
|
if (actionType == ESpeedActionType.Mixedtest)
|
||||||
|
{
|
||||||
|
SelectedProfiles = _profileItems;
|
||||||
|
}
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, false) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//ClearTestResult();
|
||||||
|
var coreHandler = Locator.Current.GetService<CoreHandler>();
|
||||||
|
if (coreHandler != null)
|
||||||
|
{
|
||||||
|
new SpeedtestHandler(_config, coreHandler, lstSelecteds, actionType, UpdateSpeedtestHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Export2ClientConfig()
|
||||||
|
{
|
||||||
|
var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MainFormHandler.Instance.Export2ClientConfig(item, _config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Export2ShareUrl()
|
||||||
|
{
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new();
|
||||||
|
foreach (var it in lstSelecteds)
|
||||||
|
{
|
||||||
|
var url = FmtHandler.GetShareUri(it);
|
||||||
|
if (Utils.IsNullOrEmpty(url))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sb.Append(url);
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
if (sb.Length > 0)
|
||||||
|
{
|
||||||
|
Utils.SetClipboardData(sb.ToString());
|
||||||
|
_noticeHandler?.SendMessage(ResUI.BatchExportURLSuccessfully);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Add Servers
|
||||||
|
|
||||||
|
#region Subscription
|
||||||
|
|
||||||
|
private void EditSub(bool blNew)
|
||||||
|
{
|
||||||
|
SubItem item;
|
||||||
|
if (blNew)
|
||||||
|
{
|
||||||
|
item = new();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = LazyConfig.Instance.GetSubItem(_config.subIndexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var ret = (new SubEditWindow(item)).ShowDialog();
|
||||||
|
if (ret == true)
|
||||||
|
{
|
||||||
|
RefreshSubscriptions();
|
||||||
|
SubSelectedChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Subscription
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,6 @@
|
||||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Popupbox.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Popupbox.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
|
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
|
||||||
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
|
|
||||||
|
@ -390,62 +389,6 @@
|
||||||
</materialDesign:PopupBox>
|
</materialDesign:PopupBox>
|
||||||
</ToolBar>
|
</ToolBar>
|
||||||
</ToolBarTray>
|
</ToolBarTray>
|
||||||
<WrapPanel Margin="2" DockPanel.Dock="Top">
|
|
||||||
<ListBox
|
|
||||||
x:Name="lstGroup"
|
|
||||||
MaxHeight="120"
|
|
||||||
FontSize="{DynamicResource StdFontSize}"
|
|
||||||
ItemContainerStyle="{StaticResource MyChipListBoxItem}"
|
|
||||||
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}">
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Text="{Binding remarks}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
x:Name="btnEditSub"
|
|
||||||
Width="30"
|
|
||||||
Height="30"
|
|
||||||
Margin="4,0"
|
|
||||||
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
|
||||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Edit" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
x:Name="btnAddSub"
|
|
||||||
Width="30"
|
|
||||||
Height="30"
|
|
||||||
Margin="4,0"
|
|
||||||
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
|
||||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Plus" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
x:Name="btnAutofitColumnWidth"
|
|
||||||
Width="30"
|
|
||||||
Height="30"
|
|
||||||
Margin="20,0"
|
|
||||||
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
|
||||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
|
|
||||||
</Button>
|
|
||||||
<TextBox
|
|
||||||
x:Name="txtServerFilter"
|
|
||||||
Width="200"
|
|
||||||
Margin="4,0"
|
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgServerTitle}"
|
|
||||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
|
||||||
Style="{StaticResource DefTextBox}" />
|
|
||||||
<Button
|
|
||||||
x:Name="btnShowCalshUI"
|
|
||||||
Width="30"
|
|
||||||
Height="30"
|
|
||||||
Margin="20,0"
|
|
||||||
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
|
||||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="EyeOutline" />
|
|
||||||
</Button>
|
|
||||||
</WrapPanel>
|
|
||||||
|
|
||||||
<materialDesign:ColorZone
|
<materialDesign:ColorZone
|
||||||
Height="50"
|
Height="50"
|
||||||
|
@ -527,250 +470,22 @@
|
||||||
</materialDesign:ColorZone>
|
</materialDesign:ColorZone>
|
||||||
|
|
||||||
<Grid x:Name="gridMain">
|
<Grid x:Name="gridMain">
|
||||||
<Grid.RowDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<RowDefinition Height="1*" />
|
<ColumnDefinition Width="1*" />
|
||||||
<RowDefinition Height="10" />
|
<ColumnDefinition Width="10" />
|
||||||
<RowDefinition Height="1*" />
|
<ColumnDefinition Width="1*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<DockPanel Grid.Row="0">
|
<ContentControl x:Name="tabProfiles" Grid.Column="0" />
|
||||||
<ContentControl x:Name="tabClashUI" DockPanel.Dock="Right" />
|
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||||
<DataGrid
|
<TabControl Grid.Column="2" HorizontalContentAlignment="Left">
|
||||||
x:Name="lstProfiles"
|
<TabItem x:Name="tabMsgView" Header="{x:Static resx:ResUI.MsgInformationTitle}" />
|
||||||
materialDesign:DataGridAssist.CellPadding="2,2"
|
<TabItem x:Name="tabClashUI" Header="{x:Static resx:ResUI.TbProxies}" />
|
||||||
AutoGenerateColumns="False"
|
</TabControl>
|
||||||
BorderThickness="1"
|
|
||||||
CanUserAddRows="False"
|
|
||||||
CanUserResizeRows="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
EnableRowVirtualization="True"
|
|
||||||
Focusable="True"
|
|
||||||
GridLinesVisibility="All"
|
|
||||||
HeadersVisibility="All"
|
|
||||||
IsReadOnly="True"
|
|
||||||
RowHeaderWidth="40"
|
|
||||||
Style="{StaticResource DefDataGrid}">
|
|
||||||
<DataGrid.InputBindings>
|
|
||||||
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+C" />
|
|
||||||
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+V" />
|
|
||||||
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Delete" />
|
|
||||||
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Enter" />
|
|
||||||
</DataGrid.InputBindings>
|
|
||||||
<DataGrid.ContextMenu>
|
|
||||||
<ContextMenu Style="{StaticResource DefContextMenu}">
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuEditServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuEditServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuSetDefaultServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuRemoveServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuRemoveServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuRemoveDuplicateServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuCopyServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuCopyServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuShareServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuShareServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuSetDefaultMultipleServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuMixedTestServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuMixedTestServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuTcpingServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuTcpingServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuRealPingServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuRealPingServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuSpeedServer"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuSpeedServer}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuSortServerResult"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuSortServerResult}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuMoveToGroup"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuMoveToGroup}">
|
|
||||||
<MenuItem Height="Auto">
|
|
||||||
<MenuItem.Header>
|
|
||||||
<DockPanel>
|
|
||||||
<ComboBox
|
|
||||||
x:Name="cmbMoveToGroup"
|
|
||||||
Width="200"
|
|
||||||
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSubscription}"
|
|
||||||
DisplayMemberPath="remarks"
|
|
||||||
FontSize="{DynamicResource StdFontSize}"
|
|
||||||
Style="{StaticResource MaterialDesignFilledComboBox}" />
|
|
||||||
</DockPanel>
|
|
||||||
</MenuItem.Header>
|
|
||||||
</MenuItem>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuMoveTop"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuMoveTop}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuMoveUp"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuMoveUp}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuMoveDown"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuMoveDown}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuMoveBottom"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuMoveBottom}" />
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuSelectAll"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Click="menuSelectAll_Click"
|
|
||||||
Header="{x:Static resx:ResUI.menuSelectAll}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuExport2ClientConfig"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuExport2ClientConfig}" />
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuExport2ShareUrl"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuExport2ShareUrl}" />
|
|
||||||
</ContextMenu>
|
|
||||||
</DataGrid.ContextMenu>
|
|
||||||
<DataGrid.Resources>
|
|
||||||
<Style BasedOn="{StaticResource MaterialDesignDataGridRow}" TargetType="DataGridRow">
|
|
||||||
<EventSetter Event="MouseDoubleClick" Handler="LstProfiles_MouseDoubleClick" />
|
|
||||||
</Style>
|
|
||||||
<Style BasedOn="{StaticResource MaterialDesignDataGridColumnHeader}" TargetType="DataGridColumnHeader">
|
|
||||||
<EventSetter Event="Click" Handler="LstProfiles_ColumnHeader_Click" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell">
|
|
||||||
<Style.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding isActive}" Value="True">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
|
||||||
<Setter Property="Foreground" Value="Black" />
|
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
|
||||||
</DataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.Resources>
|
|
||||||
<DataGrid.Columns>
|
|
||||||
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="80"
|
|
||||||
Binding="{Binding configType}"
|
|
||||||
ExName="configType"
|
|
||||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="150"
|
|
||||||
Binding="{Binding remarks}"
|
|
||||||
ExName="remarks"
|
|
||||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="120"
|
|
||||||
Binding="{Binding address}"
|
|
||||||
ExName="address"
|
|
||||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="60"
|
|
||||||
Binding="{Binding port}"
|
|
||||||
ExName="port"
|
|
||||||
Header="{x:Static resx:ResUI.LvPort}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding network}"
|
|
||||||
ExName="network"
|
|
||||||
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding streamSecurity}"
|
|
||||||
ExName="streamSecurity"
|
|
||||||
Header="{x:Static resx:ResUI.LvTLS}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding subRemarks}"
|
|
||||||
ExName="subRemarks"
|
|
||||||
Header="{x:Static resx:ResUI.LvSubscription}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding delayVal}"
|
|
||||||
ExName="delayVal"
|
|
||||||
Header="{x:Static resx:ResUI.LvTestDelay}">
|
|
||||||
<DataGridTextColumn.ElementStyle>
|
|
||||||
<Style TargetType="{x:Type TextBlock}">
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
|
||||||
<Setter Property="Foreground" Value="{Binding delay, Converter={StaticResource DelayColorConverter}}" />
|
|
||||||
</Style>
|
|
||||||
</DataGridTextColumn.ElementStyle>
|
|
||||||
</base:MyDGTextColumn>
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding speedVal}"
|
|
||||||
ExName="speedVal"
|
|
||||||
Header="{x:Static resx:ResUI.LvTestSpeed}">
|
|
||||||
<DataGridTextColumn.ElementStyle>
|
|
||||||
<Style TargetType="{x:Type TextBlock}">
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
|
||||||
</Style>
|
|
||||||
</DataGridTextColumn.ElementStyle>
|
|
||||||
</base:MyDGTextColumn>
|
|
||||||
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
x:Name="colTodayUp"
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding todayUp}"
|
|
||||||
ExName="todayUp"
|
|
||||||
Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
x:Name="colTodayDown"
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding todayDown}"
|
|
||||||
ExName="todayDown"
|
|
||||||
Header="{x:Static resx:ResUI.LvTodayDownloadDataAmount}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
x:Name="colTotalUp"
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding totalUp}"
|
|
||||||
ExName="totalUp"
|
|
||||||
Header="{x:Static resx:ResUI.LvTotalUploadDataAmount}" />
|
|
||||||
<base:MyDGTextColumn
|
|
||||||
x:Name="colTotalDown"
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding totalDown}"
|
|
||||||
ExName="totalDown"
|
|
||||||
Header="{x:Static resx:ResUI.LvTotalDownloadDataAmount}" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" />
|
|
||||||
<local:MsgView Grid.Row="2" />
|
|
||||||
<materialDesign:Snackbar
|
<materialDesign:Snackbar
|
||||||
x:Name="MainSnackbar"
|
x:Name="MainSnackbar"
|
||||||
Grid.Row="2"
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
MessageQueue="{materialDesign:MessageQueue}" />
|
MessageQueue="{materialDesign:MessageQueue}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
|
@ -5,17 +5,14 @@ using System.Reactive.Disposables;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Controls.Primitives;
|
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using v2rayN.Base;
|
|
||||||
using v2rayN.Enums;
|
using v2rayN.Enums;
|
||||||
using v2rayN.Handler;
|
using v2rayN.Handler;
|
||||||
using v2rayN.Models;
|
using v2rayN.Models;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
using v2rayN.ViewModels;
|
using v2rayN.ViewModels;
|
||||||
using Point = System.Windows.Point;
|
|
||||||
|
|
||||||
namespace v2rayN.Views
|
namespace v2rayN.Views
|
||||||
{
|
{
|
||||||
|
@ -27,39 +24,13 @@ namespace v2rayN.Views
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
// 设置窗口的尺寸不大于屏幕的尺寸
|
|
||||||
if (this.Width > SystemParameters.WorkArea.Width)
|
|
||||||
{
|
|
||||||
this.Width = SystemParameters.WorkArea.Width;
|
|
||||||
}
|
|
||||||
if (this.Height > SystemParameters.WorkArea.Height)
|
|
||||||
{
|
|
||||||
this.Height = SystemParameters.WorkArea.Height;
|
|
||||||
}
|
|
||||||
|
|
||||||
lstGroup.MaxHeight = Math.Floor(SystemParameters.WorkArea.Height * 0.20 / 40) * 40;
|
|
||||||
|
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
|
|
||||||
App.Current.SessionEnding += Current_SessionEnding;
|
App.Current.SessionEnding += Current_SessionEnding;
|
||||||
this.Closing += MainWindow_Closing;
|
this.Closing += MainWindow_Closing;
|
||||||
this.PreviewKeyDown += MainWindow_PreviewKeyDown;
|
this.PreviewKeyDown += MainWindow_PreviewKeyDown;
|
||||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
|
||||||
txtServerFilter.PreviewKeyDown += TxtServerFilter_PreviewKeyDown;
|
|
||||||
btnShowCalshUI.Click += BtnShowCalshUI_Click;
|
|
||||||
lstProfiles.PreviewKeyDown += LstProfiles_PreviewKeyDown;
|
|
||||||
lstProfiles.SelectionChanged += lstProfiles_SelectionChanged;
|
|
||||||
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
|
|
||||||
if (_config.uiItem.enableDragDropSort)
|
|
||||||
{
|
|
||||||
lstProfiles.AllowDrop = true;
|
|
||||||
lstProfiles.PreviewMouseLeftButtonDown += LstProfiles_PreviewMouseLeftButtonDown;
|
|
||||||
lstProfiles.MouseMove += LstProfiles_MouseMove;
|
|
||||||
lstProfiles.DragEnter += LstProfiles_DragEnter;
|
|
||||||
lstProfiles.Drop += LstProfiles_Drop;
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewModel = new MainWindowViewModel(MainSnackbar.MessageQueue!, UpdateViewHandler);
|
ViewModel = new MainWindowViewModel(MainSnackbar.MessageQueue, null);
|
||||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||||
|
|
||||||
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 8; i++)
|
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 8; i++)
|
||||||
|
@ -74,15 +45,6 @@ namespace v2rayN.Views
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedProfile, v => v.lstProfiles.SelectedItem).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstGroup.ItemsSource).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//servers
|
//servers
|
||||||
this.BindCommand(ViewModel, vm => vm.AddVmessServerCmd, v => v.menuAddVmessServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddVmessServerCmd, v => v.menuAddVmessServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddVlessServerCmd, v => v.menuAddVlessServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddVlessServerCmd, v => v.menuAddVlessServer).DisposeWith(disposables);
|
||||||
|
@ -97,35 +59,6 @@ namespace v2rayN.Views
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
||||||
|
|
||||||
//servers delete
|
|
||||||
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveServerCmd, v => v.menuRemoveServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveDuplicateServerCmd, v => v.menuRemoveDuplicateServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerCmd, v => v.menuSetDefaultMultipleServer).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//servers move
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//servers ping
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MixedTestServerCmd, v => v.menuMixedTestServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.TcpingServerCmd, v => v.menuTcpingServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//servers export
|
|
||||||
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//sub
|
//sub
|
||||||
this.BindCommand(ViewModel, vm => vm.SubSettingCmd, v => v.menuSubSetting).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubSettingCmd, v => v.menuSubSetting).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate).DisposeWith(disposables);
|
||||||
|
@ -183,7 +116,6 @@ namespace v2rayN.Views
|
||||||
this.OneWayBind(ViewModel, vm => vm.RunningServerToolTipText, v => v.tbNotify.ToolTipText).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.RunningServerToolTipText, v => v.tbNotify.ToolTipText).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.NotifyLeftClickCmd, v => v.tbNotify.LeftClickCommand).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.NotifyLeftClickCmd, v => v.tbNotify.LeftClickCommand).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.AppIcon, v => v.Icon).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.AppIcon, v => v.Icon).DisposeWith(disposables);
|
||||||
//this.OneWayBind(ViewModel, vm => vm.BlShowTrayTip, v => v.borTrayToolTip.Visibility).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//status bar
|
//status bar
|
||||||
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
|
||||||
|
@ -206,8 +138,7 @@ namespace v2rayN.Views
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSwatch, v => v.cmbSwatches.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSwatch, v => v.cmbSwatches.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.Text).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowCalshUI, v => v.btnShowCalshUI.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashUI.Visibility).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowCalshUI, v => v.tabClashUI.Visibility).DisposeWith(disposables);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
|
@ -216,11 +147,6 @@ namespace v2rayN.Views
|
||||||
var IsAdministrator = Utils.IsAdministrator();
|
var IsAdministrator = Utils.IsAdministrator();
|
||||||
this.Title = $"{Utils.GetVersion()} - {(IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
this.Title = $"{Utils.GetVersion()} - {(IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
||||||
|
|
||||||
//if (_config.uiItem.autoHideStartup)
|
|
||||||
//{
|
|
||||||
// WindowState = WindowState.Minimized;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (!_config.guiItem.enableHWA)
|
if (!_config.guiItem.enableHWA)
|
||||||
{
|
{
|
||||||
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
|
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
|
||||||
|
@ -244,25 +170,22 @@ namespace v2rayN.Views
|
||||||
|
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
});
|
});
|
||||||
|
if (tabProfiles.Content is null)
|
||||||
|
{
|
||||||
|
tabProfiles.Content = new ProfilesView();
|
||||||
|
}
|
||||||
|
if (tabMsgView.Content is null)
|
||||||
|
{
|
||||||
|
tabMsgView.Content = new MsgView();
|
||||||
|
}
|
||||||
|
if (tabClashUI.Content is null)
|
||||||
|
{
|
||||||
|
tabClashUI.Content = new ClashProxiesView();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Event
|
#region Event
|
||||||
|
|
||||||
private void UpdateViewHandler(EViewAction action)
|
|
||||||
{
|
|
||||||
if (action == EViewAction.AdjustMainLvColWidth)
|
|
||||||
{
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
AutofitColumnWidth();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (action == EViewAction.ProfilesFocus)
|
|
||||||
{
|
|
||||||
lstProfiles.Focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MainWindow_Closing(object? sender, CancelEventArgs e)
|
private void MainWindow_Closing(object? sender, CancelEventArgs e)
|
||||||
{
|
{
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
|
@ -271,6 +194,8 @@ namespace v2rayN.Views
|
||||||
|
|
||||||
private void menuExit_Click(object sender, RoutedEventArgs e)
|
private void menuExit_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
tabProfiles = null;
|
||||||
|
|
||||||
tbNotify.Dispose();
|
tbNotify.Dispose();
|
||||||
StorageUI();
|
StorageUI();
|
||||||
ViewModel?.MyAppExit(false);
|
ViewModel?.MyAppExit(false);
|
||||||
|
@ -283,45 +208,6 @@ namespace v2rayN.Views
|
||||||
ViewModel?.MyAppExit(true);
|
ViewModel?.MyAppExit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void lstProfiles_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
|
|
||||||
{
|
|
||||||
e.Row.Header = $" {e.Row.GetIndex() + 1}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
if (_config.uiItem.doubleClick2Activate)
|
|
||||||
{
|
|
||||||
ViewModel?.SetDefaultServer();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ViewModel?.EditServer(false, EConfigType.Custom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_ColumnHeader_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var colHeader = sender as DataGridColumnHeader;
|
|
||||||
if (colHeader == null || colHeader.TabIndex < 0 || colHeader.Column == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var colName = ((MyDGTextColumn)colHeader.Column).ExName;
|
|
||||||
ViewModel?.SortServer(colName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void menuSelectAll_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
lstProfiles.SelectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
|
private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
||||||
|
@ -332,25 +218,9 @@ namespace v2rayN.Views
|
||||||
ViewModel?.AddServerViaClipboard();
|
ViewModel?.AddServerViaClipboard();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.O:
|
|
||||||
ViewModel?.ServerSpeedtest(ESpeedActionType.Tcping);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Key.R:
|
|
||||||
ViewModel?.ServerSpeedtest(ESpeedActionType.Realping);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Key.S:
|
case Key.S:
|
||||||
ViewModel?.ScanScreenTaskAsync().ContinueWith(_ => { });
|
ViewModel?.ScanScreenTaskAsync().ContinueWith(_ => { });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.T:
|
|
||||||
ViewModel?.ServerSpeedtest(ESpeedActionType.Speedtest);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Key.E:
|
|
||||||
ViewModel?.ServerSpeedtest(ESpeedActionType.Mixedtest);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -359,60 +229,6 @@ namespace v2rayN.Views
|
||||||
{
|
{
|
||||||
ViewModel?.Reload();
|
ViewModel?.Reload();
|
||||||
}
|
}
|
||||||
else if (e.Key == Key.Escape)
|
|
||||||
{
|
|
||||||
MessageBus.Current.SendMessage("true", Global.CommandStopSpeedTest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_PreviewKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
|
||||||
{
|
|
||||||
if (e.Key == Key.A)
|
|
||||||
{
|
|
||||||
menuSelectAll_Click(null, null);
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.C)
|
|
||||||
{
|
|
||||||
ViewModel?.Export2ShareUrl();
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.D)
|
|
||||||
{
|
|
||||||
ViewModel?.EditServer(false, EConfigType.Custom);
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.F)
|
|
||||||
{
|
|
||||||
ViewModel?.ShareServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (e.Key is Key.Enter or Key.Return)
|
|
||||||
{
|
|
||||||
ViewModel?.SetDefaultServer();
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.Delete)
|
|
||||||
{
|
|
||||||
ViewModel?.RemoveServer();
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.T)
|
|
||||||
{
|
|
||||||
ViewModel?.MoveServer(EMove.Top);
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.U)
|
|
||||||
{
|
|
||||||
ViewModel?.MoveServer(EMove.Up);
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.D)
|
|
||||||
{
|
|
||||||
ViewModel?.MoveServer(EMove.Down);
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.B)
|
|
||||||
{
|
|
||||||
ViewModel?.MoveServer(EMove.Bottom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,48 +253,6 @@ namespace v2rayN.Views
|
||||||
Utils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
Utils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BtnAutofitColumnWidth_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
AutofitColumnWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AutofitColumnWidth()
|
|
||||||
{
|
|
||||||
foreach (var it in lstProfiles.Columns)
|
|
||||||
{
|
|
||||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TxtServerFilter_PreviewKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Key is Key.Enter or Key.Return)
|
|
||||||
{
|
|
||||||
ViewModel?.RefreshServers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool blShowClashUI = false;
|
|
||||||
|
|
||||||
private void BtnShowCalshUI_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (blShowClashUI)
|
|
||||||
{
|
|
||||||
tabClashUI.Visibility = Visibility.Hidden;
|
|
||||||
tabClashUI.Width = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tabClashUI.Visibility = Visibility.Visible;
|
|
||||||
if (tabClashUI.Content is null)
|
|
||||||
{
|
|
||||||
tabClashUI.Content = new ClashProxiesView();
|
|
||||||
}
|
|
||||||
tabClashUI.Width = this.ActualWidth * 5 / 6;
|
|
||||||
}
|
|
||||||
blShowClashUI = !blShowClashUI;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Event
|
#endregion Event
|
||||||
|
|
||||||
#region UI
|
#region UI
|
||||||
|
@ -497,44 +271,8 @@ namespace v2rayN.Views
|
||||||
if (Height > maxHeight) Height = maxHeight;
|
if (Height > maxHeight) Height = maxHeight;
|
||||||
if (_config.uiItem.mainGirdHeight1 > 0 && _config.uiItem.mainGirdHeight2 > 0)
|
if (_config.uiItem.mainGirdHeight1 > 0 && _config.uiItem.mainGirdHeight2 > 0)
|
||||||
{
|
{
|
||||||
gridMain.RowDefinitions[0].Height = new GridLength(_config.uiItem.mainGirdHeight1, GridUnitType.Star);
|
gridMain.ColumnDefinitions[0].Width = new GridLength(_config.uiItem.mainGirdHeight1, GridUnitType.Star);
|
||||||
gridMain.RowDefinitions[2].Height = new GridLength(_config.uiItem.mainGirdHeight2, GridUnitType.Star);
|
gridMain.ColumnDefinitions[2].Width = new GridLength(_config.uiItem.mainGirdHeight2, GridUnitType.Star);
|
||||||
}
|
|
||||||
|
|
||||||
var lvColumnItem = _config.uiItem.mainColumnItem.OrderBy(t => t.Index).ToList();
|
|
||||||
var displayIndex = 0;
|
|
||||||
foreach (var item in lvColumnItem)
|
|
||||||
{
|
|
||||||
foreach (MyDGTextColumn item2 in lstProfiles.Columns)
|
|
||||||
{
|
|
||||||
if (item2.ExName == item.Name)
|
|
||||||
{
|
|
||||||
if (item.Width < 0)
|
|
||||||
{
|
|
||||||
item2.Visibility = Visibility.Hidden;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item2.Width = item.Width;
|
|
||||||
item2.DisplayIndex = displayIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_config.guiItem.enableStatistics)
|
|
||||||
{
|
|
||||||
colTodayUp.Visibility =
|
|
||||||
colTodayDown.Visibility =
|
|
||||||
colTotalUp.Visibility =
|
|
||||||
colTotalDown.Visibility = Visibility.Hidden;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
colTodayUp.Visibility =
|
|
||||||
colTodayDown.Visibility =
|
|
||||||
colTotalUp.Visibility =
|
|
||||||
colTotalDown.Visibility = Visibility.Visible;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,21 +281,8 @@ namespace v2rayN.Views
|
||||||
_config.uiItem.mainWidth = Utils.ToInt(this.Width);
|
_config.uiItem.mainWidth = Utils.ToInt(this.Width);
|
||||||
_config.uiItem.mainHeight = Utils.ToInt(this.Height);
|
_config.uiItem.mainHeight = Utils.ToInt(this.Height);
|
||||||
|
|
||||||
List<ColumnItem> lvColumnItem = new();
|
_config.uiItem.mainGirdHeight1 = Math.Ceiling(gridMain.ColumnDefinitions[0].ActualWidth + 0.1);
|
||||||
for (int k = 0; k < lstProfiles.Columns.Count; k++)
|
_config.uiItem.mainGirdHeight2 = Math.Ceiling(gridMain.ColumnDefinitions[2].ActualWidth + 0.1);
|
||||||
{
|
|
||||||
var item2 = (MyDGTextColumn)lstProfiles.Columns[k];
|
|
||||||
lvColumnItem.Add(new()
|
|
||||||
{
|
|
||||||
Name = item2.ExName,
|
|
||||||
Width = item2.Visibility == Visibility.Visible ? Utils.ToInt(item2.ActualWidth) : -1,
|
|
||||||
Index = item2.DisplayIndex
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_config.uiItem.mainColumnItem = lvColumnItem;
|
|
||||||
|
|
||||||
_config.uiItem.mainGirdHeight1 = Math.Ceiling(gridMain.RowDefinitions[0].ActualHeight + 0.1);
|
|
||||||
_config.uiItem.mainGirdHeight2 = Math.Ceiling(gridMain.RowDefinitions[2].ActualHeight + 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddHelpMenuItem()
|
private void AddHelpMenuItem()
|
||||||
|
@ -588,97 +313,5 @@ namespace v2rayN.Views
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion UI
|
#endregion UI
|
||||||
|
|
||||||
#region Drag and Drop
|
|
||||||
|
|
||||||
private Point startPoint = new();
|
|
||||||
private int startIndex = -1;
|
|
||||||
private string formatData = "ProfileItemModel";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper to search up the VisualTree
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="current"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (current is T)
|
|
||||||
{
|
|
||||||
return (T)current;
|
|
||||||
}
|
|
||||||
current = VisualTreeHelper.GetParent(current);
|
|
||||||
}
|
|
||||||
while (current != null);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
// Get current mouse position
|
|
||||||
startPoint = e.GetPosition(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_MouseMove(object sender, MouseEventArgs e)
|
|
||||||
{
|
|
||||||
// Get the current mouse position
|
|
||||||
Point mousePos = e.GetPosition(null);
|
|
||||||
Vector diff = startPoint - mousePos;
|
|
||||||
|
|
||||||
if (e.LeftButton == MouseButtonState.Pressed &&
|
|
||||||
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
|
||||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
|
||||||
{
|
|
||||||
// Get the dragged Item
|
|
||||||
if (sender is not DataGrid listView) return;
|
|
||||||
var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
|
|
||||||
if (listViewItem == null) return; // Abort
|
|
||||||
// Find the data behind the ListViewItem
|
|
||||||
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
|
|
||||||
if (item == null) return; // Abort
|
|
||||||
// Initialize the drag & drop operation
|
|
||||||
startIndex = lstProfiles.SelectedIndex;
|
|
||||||
DataObject dragData = new(formatData, item);
|
|
||||||
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_DragEnter(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
if (!e.Data.GetDataPresent(formatData) || sender != e.Source)
|
|
||||||
{
|
|
||||||
e.Effects = DragDropEffects.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_Drop(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Data.GetDataPresent(formatData) && sender == e.Source)
|
|
||||||
{
|
|
||||||
// Get the drop Item destination
|
|
||||||
if (sender is not DataGrid listView) return;
|
|
||||||
var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
|
|
||||||
if (listViewItem == null)
|
|
||||||
{
|
|
||||||
// Abort
|
|
||||||
e.Effects = DragDropEffects.None;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Find the data behind the Item
|
|
||||||
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
|
|
||||||
if (item == null) return;
|
|
||||||
// Move item into observable collection
|
|
||||||
// (this will be automatically reflected to lstView.ItemsSource)
|
|
||||||
e.Effects = DragDropEffects.Move;
|
|
||||||
|
|
||||||
ViewModel?.MoveServerTo(startIndex, item);
|
|
||||||
|
|
||||||
startIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Drag and Drop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,302 @@
|
||||||
|
<reactiveui:ReactiveUserControl
|
||||||
|
x:Class="v2rayN.Views.ProfilesView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:base="clr-namespace:v2rayN.Base"
|
||||||
|
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:reactiveui="http://reactiveui.net"
|
||||||
|
xmlns:resx="clr-namespace:v2rayN.Resx"
|
||||||
|
xmlns:vms="clr-namespace:v2rayN.ViewModels"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
x:TypeArguments="vms:ProfilesViewModel"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
|
||||||
|
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<Grid>
|
||||||
|
<DockPanel>
|
||||||
|
<WrapPanel Margin="2" DockPanel.Dock="Top">
|
||||||
|
<ListBox
|
||||||
|
x:Name="lstGroup"
|
||||||
|
MaxHeight="200"
|
||||||
|
FontSize="{DynamicResource StdFontSize}"
|
||||||
|
ItemContainerStyle="{StaticResource MyChipListBoxItem}"
|
||||||
|
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding remarks}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="btnEditSub"
|
||||||
|
Width="30"
|
||||||
|
Height="30"
|
||||||
|
Margin="4,0"
|
||||||
|
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
||||||
|
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Edit" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
x:Name="btnAddSub"
|
||||||
|
Width="30"
|
||||||
|
Height="30"
|
||||||
|
Margin="4,0"
|
||||||
|
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
||||||
|
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Plus" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="btnAutofitColumnWidth"
|
||||||
|
Width="30"
|
||||||
|
Height="30"
|
||||||
|
Margin="20,0"
|
||||||
|
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}">
|
||||||
|
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
|
||||||
|
</Button>
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtServerFilter"
|
||||||
|
Width="200"
|
||||||
|
Margin="4,0"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgServerTitle}"
|
||||||
|
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
</WrapPanel>
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstProfiles"
|
||||||
|
materialDesign:DataGridAssist.CellPadding="2,2"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserResizeRows="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
EnableRowVirtualization="True"
|
||||||
|
Focusable="True"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="All"
|
||||||
|
IsReadOnly="True"
|
||||||
|
RowHeaderWidth="40"
|
||||||
|
Style="{StaticResource DefDataGrid}">
|
||||||
|
<DataGrid.InputBindings>
|
||||||
|
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+C" />
|
||||||
|
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Ctrl+V" />
|
||||||
|
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Delete" />
|
||||||
|
<KeyBinding Command="ApplicationCommands.NotACommand" Gesture="Enter" />
|
||||||
|
</DataGrid.InputBindings>
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu Style="{StaticResource DefContextMenu}">
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuEditServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuEditServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSetDefaultServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSetDefaultServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuRemoveServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuRemoveServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuRemoveDuplicateServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuCopyServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuCopyServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuShareServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuShareServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSetDefaultMultipleServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMixedTestServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMixedTestServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuTcpingServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuTcpingServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuRealPingServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuRealPingServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSpeedServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSpeedServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSortServerResult"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSortServerResult}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveToGroup"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveToGroup}">
|
||||||
|
<MenuItem Height="Auto">
|
||||||
|
<MenuItem.Header>
|
||||||
|
<DockPanel>
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbMoveToGroup"
|
||||||
|
Width="200"
|
||||||
|
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSubscription}"
|
||||||
|
DisplayMemberPath="remarks"
|
||||||
|
FontSize="{DynamicResource StdFontSize}"
|
||||||
|
Style="{StaticResource MaterialDesignFilledComboBox}" />
|
||||||
|
</DockPanel>
|
||||||
|
</MenuItem.Header>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveTop"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveTop}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveUp"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveUp}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveDown"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveDown}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveBottom"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveBottom}" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSelectAll"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Click="menuSelectAll_Click"
|
||||||
|
Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuExport2ClientConfig"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuExport2ClientConfig}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuExport2ShareUrl"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuExport2ShareUrl}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.Resources>
|
||||||
|
<Style BasedOn="{StaticResource MaterialDesignDataGridRow}" TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="MouseDoubleClick" Handler="LstProfiles_MouseDoubleClick" />
|
||||||
|
</Style>
|
||||||
|
<Style BasedOn="{StaticResource MaterialDesignDataGridColumnHeader}" TargetType="DataGridColumnHeader">
|
||||||
|
<EventSetter Event="Click" Handler="LstProfiles_ColumnHeader_Click" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style BasedOn="{StaticResource MaterialDesignDataGridCell}" TargetType="DataGridCell">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding isActive}" Value="True">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||||
|
<Setter Property="Foreground" Value="Black" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.Primary.Light}" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.Resources>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="80"
|
||||||
|
Binding="{Binding configType}"
|
||||||
|
ExName="configType"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding remarks}"
|
||||||
|
ExName="remarks"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding address}"
|
||||||
|
ExName="address"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="60"
|
||||||
|
Binding="{Binding port}"
|
||||||
|
ExName="port"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding network}"
|
||||||
|
ExName="network"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding streamSecurity}"
|
||||||
|
ExName="streamSecurity"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding subRemarks}"
|
||||||
|
ExName="subRemarks"
|
||||||
|
Header="{x:Static resx:ResUI.LvSubscription}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding delayVal}"
|
||||||
|
ExName="delayVal"
|
||||||
|
Header="{x:Static resx:ResUI.LvTestDelay}">
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
|
<Setter Property="Foreground" Value="{Binding delay, Converter={StaticResource DelayColorConverter}}" />
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
|
</base:MyDGTextColumn>
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding speedVal}"
|
||||||
|
ExName="speedVal"
|
||||||
|
Header="{x:Static resx:ResUI.LvTestSpeed}">
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
|
</base:MyDGTextColumn>
|
||||||
|
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
x:Name="colTodayUp"
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding todayUp}"
|
||||||
|
ExName="todayUp"
|
||||||
|
Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
x:Name="colTodayDown"
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding todayDown}"
|
||||||
|
ExName="todayDown"
|
||||||
|
Header="{x:Static resx:ResUI.LvTodayDownloadDataAmount}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
x:Name="colTotalUp"
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding totalUp}"
|
||||||
|
ExName="totalUp"
|
||||||
|
Header="{x:Static resx:ResUI.LvTotalUploadDataAmount}" />
|
||||||
|
<base:MyDGTextColumn
|
||||||
|
x:Name="colTotalDown"
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding totalDown}"
|
||||||
|
ExName="totalDown"
|
||||||
|
Header="{x:Static resx:ResUI.LvTotalDownloadDataAmount}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</reactiveui:ReactiveUserControl>
|
|
@ -0,0 +1,399 @@
|
||||||
|
using ReactiveUI;
|
||||||
|
using Splat;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using v2rayN.Base;
|
||||||
|
using v2rayN.Enums;
|
||||||
|
using v2rayN.Handler;
|
||||||
|
using v2rayN.Models;
|
||||||
|
using v2rayN.ViewModels;
|
||||||
|
using Point = System.Windows.Point;
|
||||||
|
|
||||||
|
namespace v2rayN.Views
|
||||||
|
{
|
||||||
|
public partial class ProfilesView
|
||||||
|
{
|
||||||
|
private static Config _config;
|
||||||
|
|
||||||
|
public ProfilesView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
lstGroup.MaxHeight = Math.Floor(SystemParameters.WorkArea.Height * 0.20 / 40) * 40;
|
||||||
|
|
||||||
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
|
|
||||||
|
Application.Current.Exit += Current_Exit;
|
||||||
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
|
txtServerFilter.PreviewKeyDown += TxtServerFilter_PreviewKeyDown;
|
||||||
|
lstProfiles.PreviewKeyDown += LstProfiles_PreviewKeyDown;
|
||||||
|
lstProfiles.SelectionChanged += lstProfiles_SelectionChanged;
|
||||||
|
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
|
||||||
|
if (_config.uiItem.enableDragDropSort)
|
||||||
|
{
|
||||||
|
lstProfiles.AllowDrop = true;
|
||||||
|
lstProfiles.PreviewMouseLeftButtonDown += LstProfiles_PreviewMouseLeftButtonDown;
|
||||||
|
lstProfiles.MouseMove += LstProfiles_MouseMove;
|
||||||
|
lstProfiles.DragEnter += LstProfiles_DragEnter;
|
||||||
|
lstProfiles.Drop += LstProfiles_Drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewModel = new ProfilesViewModel(UpdateViewHandler);
|
||||||
|
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
|
||||||
|
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedProfile, v => v.lstProfiles.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstGroup.ItemsSource).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
|
||||||
|
|
||||||
|
//servers delete
|
||||||
|
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.RemoveServerCmd, v => v.menuRemoveServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.RemoveDuplicateServerCmd, v => v.menuRemoveDuplicateServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerCmd, v => v.menuSetDefaultMultipleServer).DisposeWith(disposables);
|
||||||
|
|
||||||
|
//servers move
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
||||||
|
|
||||||
|
//servers ping
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MixedTestServerCmd, v => v.menuMixedTestServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.TcpingServerCmd, v => v.menuTcpingServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
|
||||||
|
|
||||||
|
//servers export
|
||||||
|
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
|
||||||
|
RestoreUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Event
|
||||||
|
|
||||||
|
private void Current_Exit(object sender, ExitEventArgs e)
|
||||||
|
{
|
||||||
|
StorageUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateViewHandler(EViewAction action)
|
||||||
|
{
|
||||||
|
if (action == EViewAction.AdjustMainLvColWidth)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
AutofitColumnWidth();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (action == EViewAction.ProfilesFocus)
|
||||||
|
{
|
||||||
|
lstProfiles.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lstProfiles_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||||
|
{
|
||||||
|
e.Row.Header = $" {e.Row.GetIndex() + 1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (_config.uiItem.doubleClick2Activate)
|
||||||
|
{
|
||||||
|
ViewModel?.SetDefaultServer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ViewModel?.EditServer(false, EConfigType.Custom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_ColumnHeader_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var colHeader = sender as DataGridColumnHeader;
|
||||||
|
if (colHeader == null || colHeader.TabIndex < 0 || colHeader.Column == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var colName = ((MyDGTextColumn)colHeader.Column).ExName;
|
||||||
|
ViewModel?.SortServer(colName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void menuSelectAll_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
lstProfiles.SelectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
||||||
|
{
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.A:
|
||||||
|
menuSelectAll_Click(null, null);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.C:
|
||||||
|
ViewModel?.Export2ShareUrl();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.D:
|
||||||
|
ViewModel?.EditServer(false, EConfigType.Custom);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.F:
|
||||||
|
ViewModel?.ShareServer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.O:
|
||||||
|
ViewModel?.ServerSpeedtest(ESpeedActionType.Tcping);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.R:
|
||||||
|
ViewModel?.ServerSpeedtest(ESpeedActionType.Realping);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.T:
|
||||||
|
ViewModel?.ServerSpeedtest(ESpeedActionType.Speedtest);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.E:
|
||||||
|
ViewModel?.ServerSpeedtest(ESpeedActionType.Mixedtest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (e.Key is Key.Enter or Key.Return)
|
||||||
|
{
|
||||||
|
ViewModel?.SetDefaultServer();
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Delete)
|
||||||
|
{
|
||||||
|
ViewModel?.RemoveServer();
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.T)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Top);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.U)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Up);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.D)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Down);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.B)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Bottom);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Escape)
|
||||||
|
{
|
||||||
|
MessageBus.Current.SendMessage("true", Global.CommandStopSpeedTest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnAutofitColumnWidth_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
AutofitColumnWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutofitColumnWidth()
|
||||||
|
{
|
||||||
|
foreach (var it in lstProfiles.Columns)
|
||||||
|
{
|
||||||
|
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TxtServerFilter_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key is Key.Enter or Key.Return)
|
||||||
|
{
|
||||||
|
ViewModel?.RefreshServers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Event
|
||||||
|
|
||||||
|
#region UI
|
||||||
|
|
||||||
|
private void RestoreUI()
|
||||||
|
{
|
||||||
|
var lvColumnItem = _config.uiItem.mainColumnItem.OrderBy(t => t.Index).ToList();
|
||||||
|
var displayIndex = 0;
|
||||||
|
foreach (var item in lvColumnItem)
|
||||||
|
{
|
||||||
|
foreach (MyDGTextColumn item2 in lstProfiles.Columns)
|
||||||
|
{
|
||||||
|
if (item2.ExName == item.Name)
|
||||||
|
{
|
||||||
|
if (item.Width < 0)
|
||||||
|
{
|
||||||
|
item2.Visibility = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item2.Width = item.Width;
|
||||||
|
item2.DisplayIndex = displayIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_config.guiItem.enableStatistics)
|
||||||
|
{
|
||||||
|
colTodayUp.Visibility =
|
||||||
|
colTodayDown.Visibility =
|
||||||
|
colTotalUp.Visibility =
|
||||||
|
colTotalDown.Visibility = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
colTodayUp.Visibility =
|
||||||
|
colTodayDown.Visibility =
|
||||||
|
colTotalUp.Visibility =
|
||||||
|
colTotalDown.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StorageUI()
|
||||||
|
{
|
||||||
|
List<ColumnItem> lvColumnItem = new();
|
||||||
|
for (int k = 0; k < lstProfiles.Columns.Count; k++)
|
||||||
|
{
|
||||||
|
var item2 = (MyDGTextColumn)lstProfiles.Columns[k];
|
||||||
|
lvColumnItem.Add(new()
|
||||||
|
{
|
||||||
|
Name = item2.ExName,
|
||||||
|
Width = item2.Visibility == Visibility.Visible ? Utils.ToInt(item2.ActualWidth) : -1,
|
||||||
|
Index = item2.DisplayIndex
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_config.uiItem.mainColumnItem = lvColumnItem;
|
||||||
|
ConfigHandler.SaveConfig(_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion UI
|
||||||
|
|
||||||
|
#region Drag and Drop
|
||||||
|
|
||||||
|
private Point startPoint = new();
|
||||||
|
private int startIndex = -1;
|
||||||
|
private string formatData = "ProfileItemModel";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to search up the VisualTree
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="current"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (current is T)
|
||||||
|
{
|
||||||
|
return (T)current;
|
||||||
|
}
|
||||||
|
current = VisualTreeHelper.GetParent(current);
|
||||||
|
}
|
||||||
|
while (current != null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
// Get current mouse position
|
||||||
|
startPoint = e.GetPosition(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
// Get the current mouse position
|
||||||
|
Point mousePos = e.GetPosition(null);
|
||||||
|
Vector diff = startPoint - mousePos;
|
||||||
|
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed &&
|
||||||
|
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||||
|
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||||
|
{
|
||||||
|
// Get the dragged Item
|
||||||
|
if (sender is not DataGrid listView) return;
|
||||||
|
var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
|
||||||
|
if (listViewItem == null) return; // Abort
|
||||||
|
// Find the data behind the ListViewItem
|
||||||
|
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
|
||||||
|
if (item == null) return; // Abort
|
||||||
|
// Initialize the drag & drop operation
|
||||||
|
startIndex = lstProfiles.SelectedIndex;
|
||||||
|
DataObject dragData = new(formatData, item);
|
||||||
|
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_DragEnter(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.Data.GetDataPresent(formatData) || sender != e.Source)
|
||||||
|
{
|
||||||
|
e.Effects = DragDropEffects.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstProfiles_Drop(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Data.GetDataPresent(formatData) && sender == e.Source)
|
||||||
|
{
|
||||||
|
// Get the drop Item destination
|
||||||
|
if (sender is not DataGrid listView) return;
|
||||||
|
var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
|
||||||
|
if (listViewItem == null)
|
||||||
|
{
|
||||||
|
// Abort
|
||||||
|
e.Effects = DragDropEffects.None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Find the data behind the Item
|
||||||
|
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
|
||||||
|
if (item == null) return;
|
||||||
|
// Move item into observable collection
|
||||||
|
// (this will be automatically reflected to lstView.ItemsSource)
|
||||||
|
e.Effects = DragDropEffects.Move;
|
||||||
|
|
||||||
|
ViewModel?.MoveServerTo(startIndex, item);
|
||||||
|
|
||||||
|
startIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Drag and Drop
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue