using DynamicData; using DynamicData.Binding; using MaterialDesignColors; using MaterialDesignColors.ColorManipulation; using MaterialDesignThemes.Wpf; using NHotkey; using ReactiveUI; using ReactiveUI.Fody.Helpers; using Splat; using System.Drawing; using System.Reactive; using System.Reactive.Linq; using System.Text; using System.Windows; using System.Windows.Forms; using System.Windows.Media; using v2rayN.Base; using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; using v2rayN.Tool; using v2rayN.Views; using Application = System.Windows.Application; namespace v2rayN.ViewModels { public class MainWindowViewModel : ReactiveObject { #region private prop private CoreHandler _coreHandler; private StatisticsHandler _statistics; private List _lstProfile; private string _subId = string.Empty; private string _serverFilter = string.Empty; private static Config _config; private NoticeHandler? _noticeHandler; private readonly PaletteHelper _paletteHelper = new PaletteHelper(); private Dictionary _dicHeaderSort = new(); private Action _updateView; #endregion #region ObservableCollection private IObservableCollection _profileItems = new ObservableCollectionExtended(); public IObservableCollection ProfileItems => _profileItems; private IObservableCollection _subItems = new ObservableCollectionExtended(); public IObservableCollection SubItems => _subItems; private IObservableCollection _routingItems = new ObservableCollectionExtended(); public IObservableCollection RoutingItems => _routingItems; private IObservableCollection _servers = new ObservableCollectionExtended(); public IObservableCollection Servers => _servers; [Reactive] public ProfileItemModel SelectedProfile { get; set; } public IList SelectedProfiles { get; set; } [Reactive] public SubItem SelectedSub { get; set; } [Reactive] public RoutingItem SelectedRouting { get; set; } [Reactive] public ComboItem SelectedServer { get; set; } [Reactive] public string ServerFilter { get; set; } #endregion #region Menu //servers public ReactiveCommand AddVmessServerCmd { get; } public ReactiveCommand AddVlessServerCmd { get; } public ReactiveCommand AddShadowsocksServerCmd { get; } public ReactiveCommand AddSocksServerCmd { get; } public ReactiveCommand AddTrojanServerCmd { get; } public ReactiveCommand AddCustomServerCmd { get; } public ReactiveCommand AddServerViaClipboardCmd { get; } public ReactiveCommand AddServerViaScanCmd { get; } //servers delete public ReactiveCommand RemoveServerCmd { get; } public ReactiveCommand RemoveDuplicateServerCmd { get; } public ReactiveCommand CopyServerCmd { get; } public ReactiveCommand SetDefaultServerCmd { get; } public ReactiveCommand ShareServerCmd { get; } //servers move public ReactiveCommand MoveTopCmd { get; } public ReactiveCommand MoveUpCmd { get; } public ReactiveCommand MoveDownCmd { get; } public ReactiveCommand MoveBottomCmd { get; } //servers ping public ReactiveCommand MixedTestServerCmd { get; } public ReactiveCommand PingServerCmd { get; } public ReactiveCommand TcpingServerCmd { get; } public ReactiveCommand RealPingServerCmd { get; } public ReactiveCommand SpeedServerCmd { get; } public ReactiveCommand SortServerResultCmd { get; } //servers export public ReactiveCommand Export2ClientConfigCmd { get; } public ReactiveCommand Export2ServerConfigCmd { get; } public ReactiveCommand Export2ShareUrlCmd { get; } public ReactiveCommand Export2SubContentCmd { get; } //Subscription public ReactiveCommand SubSettingCmd { get; } public ReactiveCommand AddSubCmd { get; } public ReactiveCommand SubUpdateCmd { get; } public ReactiveCommand SubGroupUpdateCmd { get; } public ReactiveCommand SubUpdateViaProxyCmd { get; } public ReactiveCommand SubGroupUpdateViaProxyCmd { get; } //Setting public ReactiveCommand OptionSettingCmd { get; } public ReactiveCommand RoutingSettingCmd { get; } public ReactiveCommand GlobalHotkeySettingCmd { get; } public ReactiveCommand ClearServerStatisticsCmd { get; } public ReactiveCommand ImportOldGuiConfigCmd { get; } //CheckUpdate public ReactiveCommand CheckUpdateNCmd { get; } public ReactiveCommand CheckUpdateV2flyCoreCmd { get; } public ReactiveCommand CheckUpdateSagerNetCoreCmd { get; } public ReactiveCommand CheckUpdateXrayCoreCmd { get; } public ReactiveCommand CheckUpdateClashCoreCmd { get; } public ReactiveCommand CheckUpdateClashMetaCoreCmd { get; } public ReactiveCommand CheckUpdateGeoCmd { get; } public ReactiveCommand ReloadCmd { get; } [Reactive] public bool BlReloadEnabled { get; set; } public ReactiveCommand NotifyLeftClickCmd { get; } [Reactive] public Icon NotifyIcon { get; set; } [Reactive] public ImageSource AppIcon { get; set; } #endregion #region System Proxy [Reactive] public bool BlSystemProxyClear { get; set; } [Reactive] public bool BlSystemProxySet { get; set; } [Reactive] public bool BlSystemProxyNothing { get; set; } [Reactive] public bool BlSystemProxyPac { get; set; } public ReactiveCommand SystemProxyClearCmd { get; } public ReactiveCommand SystemProxySetCmd { get; } public ReactiveCommand SystemProxyNothingCmd { get; } public ReactiveCommand SystemProxyPacCmd { get; } [Reactive] public bool BlRouting { get; set; } [Reactive] public int SystemProxySelected { get; set; } #endregion #region UI [Reactive] public string InboundDisplay { get; set; } [Reactive] public string InboundLanDisplay { get; set; } [Reactive] public string RunningServerDisplay { get; set; } [Reactive] public string RunningInfoDisplay { get; set; } [Reactive] public string SpeedProxyDisplay { get; set; } [Reactive] public string SpeedDirectDisplay { get; set; } [Reactive] public bool EnableTun { get; set; } [Reactive] public bool ColorModeDark { get; set; } private IObservableCollection _swatches = new ObservableCollectionExtended(); public IObservableCollection Swatches => _swatches; [Reactive] public Swatch SelectedSwatch { get; set; } [Reactive] public string CurrentLanguage { get; set; } #endregion #region Init public MainWindowViewModel(ISnackbarMessageQueue snackbarMessageQueue, Action updateView) { _updateView = updateView; ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); Locator.CurrentMutable.RegisterLazySingleton(() => new NoticeHandler(snackbarMessageQueue), typeof(NoticeHandler)); _noticeHandler = Locator.Current.GetService(); _config = LazyConfig.Instance.GetConfig(); //ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); Init(); SelectedProfile = new(); SelectedSub = new(); SelectedRouting = new(); SelectedServer = new(); //RefreshServers(); InitSubscriptionView(); RefreshRoutingsMenu(); 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.SelectedRouting, y => y != null && !y.remarks.IsNullOrEmpty()) .Subscribe(c => RoutingSelectedChanged(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)); SystemProxySelected = (int)_config.sysProxyType; this.WhenAnyValue( x => x.SystemProxySelected, y => y != null && y >= 0) .Subscribe(c => DoSystemProxySelected(c)); this.WhenAnyValue( x => x.EnableTun, y => y == true) .Subscribe(c => DoEnableTun(c)); BindingUI(); RestoreUI(); AutoHideStartup(); //servers AddVmessServerCmd = ReactiveCommand.Create(() => { EditServer(true, EConfigType.VMess); }); AddVlessServerCmd = ReactiveCommand.Create(() => { EditServer(true, EConfigType.VLESS); }); AddShadowsocksServerCmd = ReactiveCommand.Create(() => { EditServer(true, EConfigType.Shadowsocks); }); AddSocksServerCmd = ReactiveCommand.Create(() => { EditServer(true, EConfigType.Socks); }); AddTrojanServerCmd = ReactiveCommand.Create(() => { EditServer(true, EConfigType.Trojan); }); AddCustomServerCmd = ReactiveCommand.Create(() => { EditServer(true, EConfigType.Custom); }); AddServerViaClipboardCmd = ReactiveCommand.Create(() => { AddServerViaClipboard(); }); AddServerViaScanCmd = ReactiveCommand.CreateFromTask(() => { return ScanScreenTaskAsync(); }); //servers delete RemoveServerCmd = ReactiveCommand.Create(() => { RemoveServer(); }, canEditRemove); RemoveDuplicateServerCmd = ReactiveCommand.Create(() => { RemoveDuplicateServer(); }); CopyServerCmd = ReactiveCommand.Create(() => { CopyServer(); }, canEditRemove); SetDefaultServerCmd = ReactiveCommand.Create(() => { SetDefaultServer(); }, canEditRemove); ShareServerCmd = ReactiveCommand.Create(() => { ShareServer(); }, 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); }); PingServerCmd = ReactiveCommand.Create(() => { ServerSpeedtest(ESpeedActionType.Ping); }, canEditRemove); 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((int)EServerColName.delay); }); //servers export Export2ClientConfigCmd = ReactiveCommand.Create(() => { Export2ClientConfig(); }, canEditRemove); Export2ServerConfigCmd = ReactiveCommand.Create(() => { Export2ServerConfig(); }, canEditRemove); Export2ShareUrlCmd = ReactiveCommand.Create(() => { Export2ShareUrl(); }, canEditRemove); Export2SubContentCmd = ReactiveCommand.Create(() => { Export2SubContent(); }, canEditRemove); //Subscription SubSettingCmd = ReactiveCommand.Create(() => { SubSetting(); }); AddSubCmd = ReactiveCommand.Create(() => { AddSub(); }); SubUpdateCmd = ReactiveCommand.Create(() => { UpdateSubscriptionProcess("", false); }); SubGroupUpdateCmd = ReactiveCommand.Create(() => { UpdateSubscriptionProcess(_subId, true); }); SubUpdateViaProxyCmd = ReactiveCommand.Create(() => { UpdateSubscriptionProcess("", false); }); SubGroupUpdateViaProxyCmd = ReactiveCommand.Create(() => { UpdateSubscriptionProcess(_subId, true); }); //Setting OptionSettingCmd = ReactiveCommand.Create(() => { OptionSetting(); }); RoutingSettingCmd = ReactiveCommand.Create(() => { RoutingSetting(); }); GlobalHotkeySettingCmd = ReactiveCommand.Create(() => { if ((new GlobalHotkeySettingWindow()).ShowDialog() == true) { _noticeHandler?.Enqueue(ResUI.OperationSuccess); } }); ClearServerStatisticsCmd = ReactiveCommand.Create(() => { _statistics?.ClearAllServerStatistics(); RefreshServers(); }); ImportOldGuiConfigCmd = ReactiveCommand.Create(() => { ImportOldGuiConfig(); }); //CheckUpdate CheckUpdateNCmd = ReactiveCommand.Create(() => { CheckUpdateN(); }); CheckUpdateV2flyCoreCmd = ReactiveCommand.Create(() => { CheckUpdateCore(ECoreType.v2fly_v5); }); CheckUpdateSagerNetCoreCmd = ReactiveCommand.Create(() => { CheckUpdateCore(ECoreType.SagerNet); }); CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() => { CheckUpdateCore(ECoreType.Xray); }); CheckUpdateClashCoreCmd = ReactiveCommand.Create(() => { CheckUpdateCore(ECoreType.clash); }); CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() => { CheckUpdateCore(ECoreType.clash_meta); }); CheckUpdateGeoCmd = ReactiveCommand.Create(() => { CheckUpdateGeo(); }); ReloadCmd = ReactiveCommand.Create(() => { Reload(); }); NotifyLeftClickCmd = ReactiveCommand.Create(() => { ShowHideWindow(null); }); //System proxy SystemProxyClearCmd = ReactiveCommand.Create(() => { SetListenerType(ESysProxyType.ForcedClear); }); SystemProxySetCmd = ReactiveCommand.Create(() => { SetListenerType(ESysProxyType.ForcedChange); }); SystemProxyNothingCmd = ReactiveCommand.Create(() => { SetListenerType(ESysProxyType.Unchanged); }); SystemProxyPacCmd = ReactiveCommand.Create(() => { SetListenerType(ESysProxyType.Pac); }); Global.ShowInTaskbar = true; } private void Init() { ConfigHandler.InitBuiltinRouting(ref _config); //MainFormHandler.Instance.BackupGuiNConfig(_config, true); _coreHandler = new CoreHandler(UpdateHandler); if (_config.enableStatistics) { _statistics = new StatisticsHandler(_config, UpdateStatisticsHandler); } MainFormHandler.Instance.UpdateTask(_config, UpdateTaskHandler); MainFormHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, UpdateTaskHandler); Reload(); ChangeSystemProxyStatus(_config.sysProxyType, true); } private void OnProgramStarted(object state, bool timeout) { Application.Current.Dispatcher.Invoke((Action)(() => { ShowHideWindow(true); })); } #endregion #region Actions private void UpdateHandler(bool notify, string msg) { _noticeHandler?.SendMessage(msg); } private async void UpdateTaskHandler(bool success, string msg) { _noticeHandler?.SendMessage(msg); if (success) { Reload(); } } private void UpdateStatisticsHandler(ServerSpeedItem update) { try { Application.Current.Dispatcher.Invoke((Action)(() => { if (!Global.ShowInTaskbar) { return; } SpeedProxyDisplay = string.Format("{0}:{1}/s¡ü | {2}/s¡ý", Global.agentTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown)); SpeedDirectDisplay = string.Format("{0}:{1}/s¡ü | {2}/s¡ý", Global.directTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown)); })); } catch (Exception ex) { Utils.SaveLog(ex.Message, ex); } } 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); 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, Utils.DeepCopy(item)); } } private void OnHotkeyHandler(object sender, HotkeyEventArgs e) { switch (Utils.ToInt(e.Name)) { case (int)EGlobalHotkey.ShowForm: ShowHideWindow(null); break; case (int)EGlobalHotkey.SystemProxyClear: SetListenerType(ESysProxyType.ForcedClear); break; case (int)EGlobalHotkey.SystemProxySet: SetListenerType(ESysProxyType.ForcedChange); break; case (int)EGlobalHotkey.SystemProxyUnchanged: SetListenerType(ESysProxyType.Unchanged); break; case (int)EGlobalHotkey.SystemProxyPac: SetListenerType(ESysProxyType.Pac); break; } e.Handled = true; } public void MyAppExit(bool blWindowsShutDown) { try { Utils.SaveLog("MyAppExit Begin"); StorageUI(); ConfigHandler.SaveConfig(ref _config); //HttpProxyHandle.CloseHttpAgent(config); if (blWindowsShutDown) { SysProxyHandle.ResetIEProxy4WindowsShutDown(); } else { SysProxyHandle.UpdateSysProxy(_config, true); } _statistics?.Close(); _coreHandler.CoreStop(); Utils.SaveLog("MyAppExit End"); } catch { } finally { Application.Current.Shutdown(); Environment.Exit(0); } } #endregion #region Servers && Groups private void SubSelectedChanged(bool c) { if (!c) { return; } _subId = SelectedSub?.id; RefreshServers(); _updateView("ProfilesFocus"); } private void ServerFilterChanged(bool c) { if (!c) { return; } _serverFilter = ServerFilter; RefreshServers(); } private void RefreshServers() { List lstModel = LazyConfig.Instance.ProfileItems(_subId, _serverFilter); _lstProfile = Utils.FromJson>(Utils.ToJson(lstModel)); List lstServerStat = new(); if (_statistics != null && _statistics.Enable) { lstServerStat = LazyConfig.Instance.ServerStatItems(); } lstModel = (from t in lstModel join t2 in lstServerStat on t.indexId equals t2.indexId into t2b from t22 in t2b.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, subRemarks = t.subRemarks, isActive = t.isActive, delay = t.delay, delayVal = t.delay > 0 ? $"{t.delay} {Global.DelayUnit}" : string.Empty, speedVal = t.speed > 0 ? $"{t.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) }).ToList(); ConfigHandler.SetDefaultServer(_config, _lstProfile); Application.Current.Dispatcher.Invoke((Action)(() => { _profileItems.Clear(); _profileItems.AddRange(lstModel); if (lstModel.Count > 0) { SelectedProfile = lstModel[0]; } RefreshServersMenu(); //display running server var running = ConfigHandler.GetDefaultServer(ref _config); if (running != null) { RunningServerDisplay = string.Format("{0}:{1}", ResUI.menuServers, running.GetSummary()); } })); } private void RefreshServersMenu() { _servers.Clear(); if (_lstProfile.Count > _config.trayMenuServersLimit) { return; } for (int k = 0; k < _lstProfile.Count; k++) { ProfileItem it = _lstProfile[k]; string name = it.GetSummary(); var item = new ComboItem() { ID = it.indexId, Text = name }; _servers.Add(item); if (_config.indexId.Equals(it.indexId)) { SelectedServer = item; } } } private void InitSubscriptionView() { _subItems.Clear(); _subItems.Add(new SubItem { remarks = ResUI.AllGroupServers }); foreach (var item in LazyConfig.Instance.SubItems().OrderBy(t => t.sort)) { _subItems.Add(item); } SelectedSub = _subItems[0]; } #endregion #region Add Servers private int GetProfileItems(out List lstSelecteds) { lstSelecteds = new List(); if (SelectedProfiles == null && SelectedProfiles.Count() <= 0) { return -1; } foreach (var profile in SelectedProfiles) { var item = LazyConfig.Instance.GetProfileItem(profile.indexId); if (item is not null) { lstSelecteds.Add(item); } } return 0; } public void EditServer(bool blNew, EConfigType eConfigType) { ProfileItem item; if (blNew) { item = new() { subid = _subId, configType = eConfigType, }; } 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 AddServerViaClipboard() { string clipboardData = Utils.GetClipboardData(); int ret = ConfigHandler.AddBatchServers(ref _config, clipboardData, _subId, false); if (ret > 0) { RefreshServers(); _noticeHandler?.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); } } public async Task ScanScreenTaskAsync() { ShowHideWindow(false); string result = await Task.Run(() => { return Utils.ScanScreen(); }); ShowHideWindow(true); if (Utils.IsNullOrEmpty(result)) { _noticeHandler?.Enqueue(ResUI.NoValidQRcodeFound); } else { int ret = ConfigHandler.AddBatchServers(ref _config, result, _subId, false); if (ret > 0) { RefreshServers(); _noticeHandler?.Enqueue(ResUI.SuccessfullyImportedServerViaScan); } } } public void RemoveServer() { if (GetProfileItems(out List lstSelecteds) < 0) { return; } if (UI.ShowYesNo(ResUI.RemoveServer) == DialogResult.No) { return; } ConfigHandler.RemoveServer(_config, lstSelecteds); _noticeHandler?.Enqueue(ResUI.OperationSuccess); RefreshServers(); Reload(); } private void RemoveDuplicateServer() { int oldCount = _lstProfile.Count; int newCount = ConfigHandler.DedupServerList(ref _config, ref _lstProfile); RefreshServers(); Reload(); _noticeHandler?.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, oldCount, newCount)); } private void CopyServer() { if (GetProfileItems(out List lstSelecteds) < 0) { return; } if (ConfigHandler.CopyServer(ref _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.SetDefaultServer(ref _config, item) == 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; } string url = ShareHandler.GetShareUrl(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"); } public void SortServer(int colIndex) { if (colIndex < 0) { return; } if (!_dicHeaderSort.ContainsKey(colIndex)) { _dicHeaderSort.Add(colIndex, true); } _dicHeaderSort.TryGetValue(colIndex, out bool asc); if (ConfigHandler.SortServers(ref _config, _subId, (EServerColName)colIndex, asc) != 0) { return; } _dicHeaderSort[colIndex] = !asc; RefreshServers(); } public void TestServerAvailability() { var item = ConfigHandler.GetDefaultServer(ref _config); if (item == null || item.configType == EConfigType.Custom) { return; } (new UpdateHandle()).RunAvailabilityCheck((bool success, string msg) => { _noticeHandler?.SendMessage(msg); Application.Current.Dispatcher.Invoke((Action)(() => { if (!Global.ShowInTaskbar) { return; } RunningInfoDisplay = msg; })); }); } //move server 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(ref _config, ref _lstProfile, index, eMove) == 0) { RefreshServers(); } } public void ServerSpeedtest(ESpeedActionType actionType) { if (actionType == ESpeedActionType.Mixedtest) { SelectedProfiles = _profileItems; } if (GetProfileItems(out List lstSelecteds) < 0) { return; } //ClearTestResult(); SpeedtestHandler statistics = 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); } private void Export2ServerConfig() { var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); if (item is null) { _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); return; } MainFormHandler.Instance.Export2ServerConfig(item, _config); } public void Export2ShareUrl() { if (GetProfileItems(out List lstSelecteds) < 0) { return; } StringBuilder sb = new StringBuilder(); foreach (var it in lstSelecteds) { string url = ShareHandler.GetShareUrl(it); if (Utils.IsNullOrEmpty(url)) { continue; } sb.Append(url); sb.AppendLine(); } if (sb.Length > 0) { Utils.SetClipboardData(sb.ToString()); _noticeHandler?.Enqueue(ResUI.BatchExportURLSuccessfully); } } private void Export2SubContent() { if (GetProfileItems(out List lstSelecteds) < 0) { return; } StringBuilder sb = new StringBuilder(); foreach (var it in lstSelecteds) { string url = ShareHandler.GetShareUrl(it); if (Utils.IsNullOrEmpty(url)) { continue; } sb.Append(url); sb.AppendLine(); } if (sb.Length > 0) { Utils.SetClipboardData(Utils.Base64Encode(sb.ToString())); _noticeHandler?.Enqueue(ResUI.BatchExportSubscriptionSuccessfully); } } #endregion #region Subscription private void SubSetting() { if ((new SubSettingWindow()).ShowDialog() == true) { InitSubscriptionView(); SubSelectedChanged(true); } } private void AddSub() { SubItem item = new(); var ret = (new SubEditWindow(item)).ShowDialog(); if (ret == true) { InitSubscriptionView(); SubSelectedChanged(true); } } private void UpdateSubscriptionProcess(string subId, bool blProxy) { void _updateUI(bool success, string msg) { _noticeHandler?.SendMessage(msg); if (success) { RefreshServers(); if (_config.uiItem.enableAutoAdjustMainLvColWidth) { _updateView("AdjustMainLvColWidth"); } } }; (new UpdateHandle()).UpdateSubscriptionProcess(_config, subId, blProxy, _updateUI); } #endregion #region Setting private void OptionSetting() { var ret = (new OptionSettingWindow()).ShowDialog(); if (ret == true) { //RefreshServers(); Reload(); TunModeSwitch(); } } private void RoutingSetting() { var ret = (new RoutingSettingWindow()).ShowDialog(); if (ret == true) { ConfigHandler.InitBuiltinRouting(ref _config); RefreshRoutingsMenu(); //RefreshServers(); Reload(); } } private void ImportOldGuiConfig() { OpenFileDialog fileDialog = new OpenFileDialog { Multiselect = false, Filter = "guiNConfig|*.json|All|*.*" }; if (fileDialog.ShowDialog() != DialogResult.OK) { return; } string fileName = fileDialog.FileName; if (Utils.IsNullOrEmpty(fileName)) { return; } var ret = ConfigHandler.ImportOldGuiConfig(ref _config, fileName); if (ret == 0) { RefreshRoutingsMenu(); InitSubscriptionView(); RefreshServers(); Reload(); UI.Show(ResUI.OperationSuccess); } else { _noticeHandler.Enqueue(ResUI.OperationFailed); } } #endregion #region CheckUpdate private void CheckUpdateN() { void _updateUI(bool success, string msg) { _noticeHandler?.SendMessage(msg); if (success) { MyAppExit(false); } }; (new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI, _config.checkPreReleaseUpdate); } private void CheckUpdateCore(ECoreType type) { void _updateUI(bool success, string msg) { _noticeHandler?.SendMessage(msg); if (success) { CloseV2ray(); string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(msg)); string toPath = Utils.GetBinPath("", type); FileManager.ZipExtractToFile(fileName, toPath, _config.ignoreGeoUpdateCore ? "geo" : ""); _noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfullyMore); Reload(); _noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfully); } }; (new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, _config.checkPreReleaseUpdate); } private void CheckUpdateGeo() { Task.Run(() => { var updateHandle = new UpdateHandle(); updateHandle.UpdateGeoFile("geosite", _config, UpdateTaskHandler); updateHandle.UpdateGeoFile("geoip", _config, UpdateTaskHandler); }); } #endregion #region v2ray job public void Reload() { Global.reloadCore = true; _ = LoadV2ray(); } async Task LoadV2ray() { Application.Current.Dispatcher.Invoke((Action)(() => { BlReloadEnabled = false; })); //if (Global.reloadV2ray) //{ // _noticeHandler?.SendMessage(Global.CommandClearMsg); //} await Task.Run(() => { _coreHandler.LoadCore(_config); Global.reloadCore = false; //ConfigHandler.SaveConfig(ref _config, false); ChangeSystemProxyStatus(_config.sysProxyType, false); }); TestServerAvailability(); Application.Current.Dispatcher.Invoke((Action)(() => { BlReloadEnabled = true; })); } private void CloseV2ray() { ConfigHandler.SaveConfig(ref _config, false); ChangeSystemProxyStatus(ESysProxyType.ForcedClear, false); _coreHandler.CoreStop(); } #endregion #region System proxy and Routings public void SetListenerType(ESysProxyType type) { if (_config.sysProxyType == type) { return; } _config.sysProxyType = type; ChangeSystemProxyStatus(type, true); SystemProxySelected = (int)_config.sysProxyType; ConfigHandler.SaveConfig(ref _config, false); } private void ChangeSystemProxyStatus(ESysProxyType type, bool blChange) { SysProxyHandle.UpdateSysProxy(_config, false); _noticeHandler?.SendMessage(ResUI.TipChangeSystemProxy, true); Application.Current.Dispatcher.Invoke((Action)(() => { BlSystemProxyClear = (type == ESysProxyType.ForcedClear); BlSystemProxySet = (type == ESysProxyType.ForcedChange); BlSystemProxyNothing = (type == ESysProxyType.Unchanged); BlSystemProxyPac = (type == ESysProxyType.Pac); InboundDisplayStaus(); if (blChange) { NotifyIcon = MainFormHandler.Instance.GetNotifyIcon(_config); AppIcon = MainFormHandler.Instance.GetAppIcon(_config); } })); } private void RefreshRoutingsMenu() { _routingItems.Clear(); if (!_config.enableRoutingAdvanced) { BlRouting = false; return; } BlRouting = true; var routings = LazyConfig.Instance.RoutingItems(); foreach (var item in routings) { _routingItems.Add(item); if (item.id.Equals(_config.routingIndexId)) { SelectedRouting = item; } } } private void RoutingSelectedChanged(bool c) { if (!c) { return; } if (SelectedRouting == null) { return; } var item = LazyConfig.Instance.GetRoutingItem(SelectedRouting?.id); if (item is null) { return; } if (_config.routingIndexId == item.id) { return; } if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0) { _noticeHandler?.SendMessage(ResUI.TipChangeRouting, true); Reload(); } } void DoSystemProxySelected(bool c) { if (!c) { return; } if (_config.sysProxyType == (ESysProxyType)SystemProxySelected) { return; } SetListenerType((ESysProxyType)SystemProxySelected); } void DoEnableTun(bool c) { if (_config.tunModeItem.enableTun != EnableTun) { _config.tunModeItem.enableTun = EnableTun; } TunModeSwitch(); } void TunModeSwitch() { if (EnableTun) { TunHandler.Instance.Start(); } else { TunHandler.Instance.Stop(); } } #endregion #region UI public void ShowHideWindow(bool? blShow) { var bl = blShow.HasValue ? blShow.Value : !Global.ShowInTaskbar; if (bl) { //Application.Current.MainWindow.ShowInTaskbar = true; Application.Current.MainWindow.Show(); if (Application.Current.MainWindow.WindowState == WindowState.Minimized) { Application.Current.MainWindow.WindowState = WindowState.Normal; } Application.Current.MainWindow.Activate(); Application.Current.MainWindow.Focus(); } else { Application.Current.MainWindow.Hide(); //Application.Current.MainWindow.ShowInTaskbar = false; //IntPtr windowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle; //Utils.RegWriteValue(Global.MyRegPath, Utils.WindowHwndKey, Convert.ToString((long)windowHandle)); }; Global.ShowInTaskbar = bl; } private void RestoreUI() { ModifyTheme(_config.uiItem.colorModeDark); if (!_config.uiItem.colorPrimaryName.IsNullOrEmpty()) { var swatch = new SwatchesProvider().Swatches.FirstOrDefault(t => t.Name == _config.uiItem.colorPrimaryName); if (swatch != null && swatch.ExemplarHue != null && swatch.ExemplarHue?.Color != null) { ChangePrimaryColor(swatch.ExemplarHue.Color); } } } private void StorageUI() { } private void BindingUI() { ColorModeDark = _config.uiItem.colorModeDark; _swatches.AddRange(new SwatchesProvider().Swatches); if (!_config.uiItem.colorPrimaryName.IsNullOrEmpty()) { SelectedSwatch = _swatches.FirstOrDefault(t => t.Name == _config.uiItem.colorPrimaryName); } CurrentLanguage = _config.uiItem.currentLanguage; this.WhenAnyValue( x => x.ColorModeDark, y => y == true) .Subscribe(c => { if (_config.uiItem.colorModeDark != ColorModeDark) { _config.uiItem.colorModeDark = ColorModeDark; ModifyTheme(ColorModeDark); ConfigHandler.SaveConfig(ref _config); } }); this.WhenAnyValue( x => x.SelectedSwatch, y => y != null && !y.Name.IsNullOrEmpty()) .Subscribe(c => { if (SelectedSwatch == null || SelectedSwatch.Name.IsNullOrEmpty() || SelectedSwatch.ExemplarHue == null || SelectedSwatch.ExemplarHue?.Color == null) { return; } if (_config.uiItem.colorPrimaryName != SelectedSwatch?.Name) { _config.uiItem.colorPrimaryName = SelectedSwatch?.Name; ChangePrimaryColor(SelectedSwatch.ExemplarHue.Color); ConfigHandler.SaveConfig(ref _config); } }); this.WhenAnyValue( x => x.CurrentLanguage, y => y != null && !y.IsNullOrEmpty()) .Subscribe(c => { if (!Utils.IsNullOrEmpty(CurrentLanguage)) { _config.uiItem.currentLanguage = CurrentLanguage; Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage); ConfigHandler.SaveConfig(ref _config); } }); } public void InboundDisplayStaus() { StringBuilder sb = new StringBuilder(); sb.Append($"[{Global.InboundSocks}:{LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}]"); sb.Append(" | "); //if (_config.sysProxyType == ESysProxyType.ForcedChange) //{ // sb.Append($"[{Global.InboundHttp}({ResUI.SystemProxy}):{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]"); //} //else //{ sb.Append($"[{Global.InboundHttp}:{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]"); //} InboundDisplay = $"{ResUI.LabLocal}:{sb.ToString()}"; if (_config.inbound[0].allowLANConn) { if (_config.inbound[0].newPort4LAN) { StringBuilder sb2 = new StringBuilder(); sb2.Append($"[{Global.InboundSocks}:{LazyConfig.Instance.GetLocalPort(Global.InboundSocks2)}]"); sb2.Append(" | "); sb2.Append($"[{Global.InboundHttp}:{LazyConfig.Instance.GetLocalPort(Global.InboundHttp2)}]"); InboundLanDisplay = $"{ResUI.LabLAN}:{sb2.ToString()}"; } else { InboundLanDisplay = $"{ResUI.LabLAN}:{sb.ToString()}"; } } else { InboundLanDisplay = $"{ResUI.LabLAN}:None"; } } public void ModifyTheme(bool isDarkTheme) { var theme = _paletteHelper.GetTheme(); theme.SetBaseTheme(isDarkTheme ? Theme.Dark : Theme.Light); _paletteHelper.SetTheme(theme); Utils.SetDarkBorder(Application.Current.MainWindow, isDarkTheme); } public void ChangePrimaryColor(System.Windows.Media.Color color) { var theme = _paletteHelper.GetTheme(); theme.PrimaryLight = new ColorPair(color.Lighten()); theme.PrimaryMid = new ColorPair(color); theme.PrimaryDark = new ColorPair(color.Darken()); _paletteHelper.SetTheme(theme); } private void AutoHideStartup() { if (_config.autoHideStartup) { Observable.Range(1, 1) .Delay(TimeSpan.FromSeconds(1)) .Subscribe(x => { Application.Current.Dispatcher.Invoke((Action)(() => { ShowHideWindow(false); })); }); } } #endregion } }