2026-01-26 23:30:33 +00:00
|
|
|
#nullable enable
|
2022-07-04 00:50:02 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-06-08 08:41:59 +00:00
|
|
|
using System.Diagnostics;
|
2022-12-04 17:38:19 +00:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2024-05-24 03:51:24 +00:00
|
|
|
using System.Globalization;
|
2022-07-01 04:56:15 +00:00
|
|
|
using System.IO;
|
2022-07-04 00:50:02 +00:00
|
|
|
using System.Linq;
|
2026-01-26 23:30:33 +00:00
|
|
|
using System.Threading.Tasks;
|
2022-07-04 00:50:02 +00:00
|
|
|
using System.Windows;
|
2025-04-15 05:18:06 +00:00
|
|
|
using CommunityToolkit.Mvvm.Input;
|
2025-05-25 19:48:20 +00:00
|
|
|
using Flow.Launcher.Plugin.Explorer.Helper;
|
2025-04-15 05:18:06 +00:00
|
|
|
using Flow.Launcher.Plugin.Explorer.Search;
|
|
|
|
|
using Flow.Launcher.Plugin.Explorer.Search.Everything;
|
|
|
|
|
using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions;
|
|
|
|
|
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
|
2026-01-31 15:09:41 +00:00
|
|
|
using Flow.Launcher.Plugin.Explorer.Views.Avalonia;
|
2026-01-26 23:30:33 +00:00
|
|
|
using AvaloniaApp = Avalonia.Application;
|
2020-05-11 13:15:15 +00:00
|
|
|
|
|
|
|
|
namespace Flow.Launcher.Plugin.Explorer.ViewModels
|
|
|
|
|
{
|
2025-04-15 05:18:06 +00:00
|
|
|
public partial class SettingsViewModel : BaseModel
|
2020-05-11 13:15:15 +00:00
|
|
|
{
|
2026-01-26 23:30:33 +00:00
|
|
|
/// <summary>
|
2026-01-31 15:09:41 +00:00
|
|
|
/// Gets the current active Avalonia window to use as dialog owner
|
2026-01-26 23:30:33 +00:00
|
|
|
/// </summary>
|
2026-01-31 15:09:41 +00:00
|
|
|
private static global::Avalonia.Controls.Window? GetAvaloniaOwnerWindow()
|
|
|
|
|
{
|
|
|
|
|
if (AvaloniaApp.Current?.ApplicationLifetime is not global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
// First try to find an active window
|
|
|
|
|
var activeWindow = desktop.Windows.FirstOrDefault(w => w.IsActive);
|
|
|
|
|
if (activeWindow != null)
|
|
|
|
|
return activeWindow;
|
|
|
|
|
|
|
|
|
|
// Fall back to main window
|
|
|
|
|
return desktop.MainWindow;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-25 21:19:00 +00:00
|
|
|
public Settings Settings { get; set; }
|
2020-05-11 13:15:15 +00:00
|
|
|
|
2020-06-08 04:20:22 +00:00
|
|
|
internal PluginInitContext Context { get; set; }
|
|
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
public IReadOnlyList<EnumBindingModel<Settings.IndexSearchEngineOption>> IndexSearchEngines { get; set; }
|
|
|
|
|
public IReadOnlyList<EnumBindingModel<Settings.ContentIndexSearchEngineOption>> ContentIndexSearchEngines { get; set; }
|
|
|
|
|
public IReadOnlyList<EnumBindingModel<Settings.PathEnumerationEngineOption>> PathEnumerationEngines { get; set; }
|
|
|
|
|
|
2021-05-11 12:18:57 +00:00
|
|
|
public SettingsViewModel(PluginInitContext context, Settings settings)
|
2020-05-11 13:15:15 +00:00
|
|
|
{
|
2020-06-08 04:20:22 +00:00
|
|
|
Context = context;
|
2021-05-11 12:18:57 +00:00
|
|
|
Settings = settings;
|
2022-07-04 00:50:02 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
ActionKeywordModel.Init(settings);
|
2022-07-04 00:50:02 +00:00
|
|
|
InitializeEngineSelection();
|
|
|
|
|
InitializeActionKeywordModels();
|
2020-05-11 13:15:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Save()
|
|
|
|
|
{
|
2021-05-13 11:29:21 +00:00
|
|
|
Context.API.SaveSettingJsonStorage<Settings>();
|
2020-05-11 13:15:15 +00:00
|
|
|
}
|
2020-06-08 04:20:22 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
#region Engine Selection
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
private EnumBindingModel<Settings.IndexSearchEngineOption> _selectedIndexSearchEngine;
|
|
|
|
|
private EnumBindingModel<Settings.ContentIndexSearchEngineOption> _selectedContentSearchEngine;
|
|
|
|
|
private EnumBindingModel<Settings.PathEnumerationEngineOption> _selectedPathEnumerationEngine;
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
public EnumBindingModel<Settings.IndexSearchEngineOption> SelectedIndexSearchEngine
|
|
|
|
|
{
|
|
|
|
|
get => _selectedIndexSearchEngine;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_selectedIndexSearchEngine = value;
|
|
|
|
|
Settings.IndexSearchEngine = value.Value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public EnumBindingModel<Settings.ContentIndexSearchEngineOption> SelectedContentSearchEngine
|
|
|
|
|
{
|
|
|
|
|
get => _selectedContentSearchEngine;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_selectedContentSearchEngine = value;
|
|
|
|
|
Settings.ContentSearchEngine = value.Value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
public EnumBindingModel<Settings.PathEnumerationEngineOption> SelectedPathEnumerationEngine
|
|
|
|
|
{
|
|
|
|
|
get => _selectedPathEnumerationEngine;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_selectedPathEnumerationEngine = value;
|
|
|
|
|
Settings.PathEnumerationEngine = value.Value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 17:38:19 +00:00
|
|
|
[MemberNotNull(nameof(IndexSearchEngines),
|
|
|
|
|
nameof(ContentIndexSearchEngines),
|
|
|
|
|
nameof(PathEnumerationEngines),
|
|
|
|
|
nameof(_selectedIndexSearchEngine),
|
|
|
|
|
nameof(_selectedContentSearchEngine),
|
|
|
|
|
nameof(_selectedPathEnumerationEngine))]
|
2022-07-04 00:50:02 +00:00
|
|
|
private void InitializeEngineSelection()
|
|
|
|
|
{
|
|
|
|
|
IndexSearchEngines = EnumBindingModel<Settings.IndexSearchEngineOption>.CreateList();
|
|
|
|
|
ContentIndexSearchEngines = EnumBindingModel<Settings.ContentIndexSearchEngineOption>.CreateList();
|
|
|
|
|
PathEnumerationEngines = EnumBindingModel<Settings.PathEnumerationEngineOption>.CreateList();
|
|
|
|
|
|
2022-12-04 17:38:19 +00:00
|
|
|
_selectedIndexSearchEngine = IndexSearchEngines.First(x => x.Value == Settings.IndexSearchEngine);
|
|
|
|
|
_selectedContentSearchEngine = ContentIndexSearchEngines.First(x => x.Value == Settings.ContentSearchEngine);
|
|
|
|
|
_selectedPathEnumerationEngine = PathEnumerationEngines.First(x => x.Value == Settings.PathEnumerationEngine);
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2024-06-07 08:30:19 +00:00
|
|
|
#region Native Context Menu
|
|
|
|
|
|
|
|
|
|
public bool ShowWindowsContextMenu
|
|
|
|
|
{
|
2024-06-08 04:16:33 +00:00
|
|
|
get => Settings.ShowInlinedWindowsContextMenu;
|
2024-06-07 08:30:19 +00:00
|
|
|
set
|
|
|
|
|
{
|
2024-06-08 04:16:33 +00:00
|
|
|
Settings.ShowInlinedWindowsContextMenu = value;
|
2024-06-07 08:30:19 +00:00
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 12:36:36 +00:00
|
|
|
public string WindowsContextMenuIncludedItems
|
2024-06-07 08:30:19 +00:00
|
|
|
{
|
2024-06-12 12:36:36 +00:00
|
|
|
get => Settings.WindowsContextMenuIncludedItems;
|
2024-06-07 08:30:19 +00:00
|
|
|
set
|
|
|
|
|
{
|
2024-06-12 12:36:36 +00:00
|
|
|
Settings.WindowsContextMenuIncludedItems = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string WindowsContextMenuExcludedItems
|
|
|
|
|
{
|
|
|
|
|
get => Settings.WindowsContextMenuExcludedItems;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.WindowsContextMenuExcludedItems = value;
|
2024-06-07 08:30:19 +00:00
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2024-05-24 03:51:24 +00:00
|
|
|
#region Preview Panel
|
2022-07-04 00:50:02 +00:00
|
|
|
|
2024-05-24 03:51:24 +00:00
|
|
|
public bool ShowFileSizeInPreviewPanel
|
|
|
|
|
{
|
|
|
|
|
get => Settings.ShowFileSizeInPreviewPanel;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.ShowFileSizeInPreviewPanel = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ShowCreatedDateInPreviewPanel
|
|
|
|
|
{
|
|
|
|
|
get => Settings.ShowCreatedDateInPreviewPanel;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.ShowCreatedDateInPreviewPanel = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
OnPropertyChanged(nameof(ShowPreviewPanelDateTimeChoices));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ShowModifiedDateInPreviewPanel
|
|
|
|
|
{
|
|
|
|
|
get => Settings.ShowModifiedDateInPreviewPanel;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.ShowModifiedDateInPreviewPanel = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
OnPropertyChanged(nameof(ShowPreviewPanelDateTimeChoices));
|
2025-05-24 03:12:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-24 06:16:03 +00:00
|
|
|
public bool ShowFileAgeInPreviewPanel
|
2025-05-24 03:12:56 +00:00
|
|
|
{
|
2025-05-24 06:16:03 +00:00
|
|
|
get => Settings.ShowFileAgeInPreviewPanel;
|
2025-05-24 03:12:56 +00:00
|
|
|
set
|
|
|
|
|
{
|
2025-05-24 06:16:03 +00:00
|
|
|
Settings.ShowFileAgeInPreviewPanel = value;
|
2025-05-24 03:12:56 +00:00
|
|
|
OnPropertyChanged();
|
|
|
|
|
OnPropertyChanged(nameof(ShowPreviewPanelDateTimeChoices));
|
2024-05-24 03:51:24 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string PreviewPanelDateFormat
|
|
|
|
|
{
|
|
|
|
|
get => Settings.PreviewPanelDateFormat;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.PreviewPanelDateFormat = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
OnPropertyChanged(nameof(PreviewPanelDateFormatDemo));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string PreviewPanelTimeFormat
|
|
|
|
|
{
|
|
|
|
|
get => Settings.PreviewPanelTimeFormat;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.PreviewPanelTimeFormat = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
OnPropertyChanged(nameof(PreviewPanelTimeFormatDemo));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string PreviewPanelDateFormatDemo => DateTime.Now.ToString(PreviewPanelDateFormat, CultureInfo.CurrentCulture);
|
|
|
|
|
public string PreviewPanelTimeFormatDemo => DateTime.Now.ToString(PreviewPanelTimeFormat, CultureInfo.CurrentCulture);
|
|
|
|
|
|
|
|
|
|
public bool ShowPreviewPanelDateTimeChoices => ShowCreatedDateInPreviewPanel || ShowModifiedDateInPreviewPanel;
|
|
|
|
|
|
|
|
|
|
public List<string> TimeFormatList { get; } = new()
|
|
|
|
|
{
|
|
|
|
|
"h:mm",
|
|
|
|
|
"hh:mm",
|
|
|
|
|
"H:mm",
|
|
|
|
|
"HH:mm",
|
|
|
|
|
"tt h:mm",
|
|
|
|
|
"tt hh:mm",
|
|
|
|
|
"h:mm tt",
|
|
|
|
|
"hh:mm tt",
|
|
|
|
|
"hh:mm:ss tt",
|
|
|
|
|
"HH:mm:ss"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<string> DateFormatList { get; } = new()
|
|
|
|
|
{
|
|
|
|
|
"dd/MM/yyyy",
|
|
|
|
|
"dd/MM/yyyy ddd",
|
|
|
|
|
"dd/MM/yyyy, dddd",
|
|
|
|
|
"dd-MM-yyyy",
|
|
|
|
|
"dd-MM-yyyy ddd",
|
|
|
|
|
"dd-MM-yyyy, dddd",
|
|
|
|
|
"dd.MM.yyyy",
|
|
|
|
|
"dd.MM.yyyy ddd",
|
|
|
|
|
"dd.MM.yyyy, dddd",
|
|
|
|
|
"MM/dd/yyyy",
|
|
|
|
|
"MM/dd/yyyy ddd",
|
|
|
|
|
"MM/dd/yyyy, dddd",
|
|
|
|
|
"yyyy-MM-dd",
|
|
|
|
|
"yyyy-MM-dd ddd",
|
|
|
|
|
"yyyy-MM-dd, dddd",
|
2025-06-06 01:33:34 +00:00
|
|
|
"dd/MMM/yyyy",
|
|
|
|
|
"dd/MMM/yyyy ddd",
|
|
|
|
|
"dd/MMM/yyyy, dddd",
|
|
|
|
|
"dd-MMM-yyyy",
|
|
|
|
|
"dd-MMM-yyyy ddd",
|
|
|
|
|
"dd-MMM-yyyy, dddd",
|
|
|
|
|
"dd.MMM.yyyy",
|
|
|
|
|
"dd.MMM.yyyy ddd",
|
|
|
|
|
"dd.MMM.yyyy, dddd",
|
|
|
|
|
"MMM/dd/yyyy",
|
|
|
|
|
"MMM/dd/yyyy ddd",
|
|
|
|
|
"MMM/dd/yyyy, dddd",
|
|
|
|
|
"yyyy-MMM-dd",
|
|
|
|
|
"yyyy-MMM-dd ddd",
|
|
|
|
|
"yyyy-MMM-dd, dddd",
|
2024-05-24 03:51:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endregion
|
2022-07-04 00:50:02 +00:00
|
|
|
|
|
|
|
|
#region ActionKeyword
|
|
|
|
|
|
2022-12-04 17:38:19 +00:00
|
|
|
[MemberNotNull(nameof(ActionKeywordsModels))]
|
2022-07-04 00:50:02 +00:00
|
|
|
private void InitializeActionKeywordModels()
|
|
|
|
|
{
|
|
|
|
|
ActionKeywordsModels = new List<ActionKeywordModel>
|
|
|
|
|
{
|
|
|
|
|
new(Settings.ActionKeyword.SearchActionKeyword,
|
2022-12-21 07:51:53 +00:00
|
|
|
"plugin_explorer_actionkeywordview_search"),
|
2022-07-04 00:50:02 +00:00
|
|
|
new(Settings.ActionKeyword.FileContentSearchActionKeyword,
|
2022-12-21 07:51:53 +00:00
|
|
|
"plugin_explorer_actionkeywordview_filecontentsearch"),
|
2022-07-04 00:50:02 +00:00
|
|
|
new(Settings.ActionKeyword.PathSearchActionKeyword,
|
2022-12-21 07:51:53 +00:00
|
|
|
"plugin_explorer_actionkeywordview_pathsearch"),
|
2022-07-04 00:50:02 +00:00
|
|
|
new(Settings.ActionKeyword.IndexSearchActionKeyword,
|
2022-12-21 07:51:53 +00:00
|
|
|
"plugin_explorer_actionkeywordview_indexsearch"),
|
2022-07-04 00:50:02 +00:00
|
|
|
new(Settings.ActionKeyword.QuickAccessActionKeyword,
|
2022-12-21 07:51:53 +00:00
|
|
|
"plugin_explorer_actionkeywordview_quickaccess")
|
2022-07-04 00:50:02 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<ActionKeywordModel> ActionKeywordsModels { get; set; }
|
|
|
|
|
|
2022-12-04 07:47:48 +00:00
|
|
|
public ActionKeywordModel? SelectedActionKeyword { get; set; }
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2025-04-15 05:18:06 +00:00
|
|
|
[RelayCommand]
|
2026-01-31 15:09:41 +00:00
|
|
|
private async Task EditActionKeywordAsync(object obj)
|
2022-07-01 04:56:15 +00:00
|
|
|
{
|
2022-12-04 07:47:48 +00:00
|
|
|
if (SelectedActionKeyword is not { } actionKeyword)
|
2022-07-01 04:56:15 +00:00
|
|
|
{
|
|
|
|
|
ShowUnselectedMessage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
var dialog = new ActionKeywordSetting(actionKeyword);
|
|
|
|
|
var ownerWindow = GetAvaloniaOwnerWindow();
|
|
|
|
|
bool dialogResult;
|
|
|
|
|
if (ownerWindow != null)
|
|
|
|
|
{
|
|
|
|
|
dialogResult = await dialog.ShowDialog<bool?>(ownerWindow) ?? false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Fallback: show as normal window if owner is not available
|
|
|
|
|
dialog.Show();
|
|
|
|
|
var tcs = new TaskCompletionSource<bool?>();
|
|
|
|
|
dialog.Closed += (_, _) => tcs.TrySetResult(true);
|
|
|
|
|
await tcs.Task;
|
|
|
|
|
dialogResult = true;
|
|
|
|
|
}
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
if (!dialogResult)
|
2022-07-01 04:56:15 +00:00
|
|
|
{
|
2022-07-04 00:50:02 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
var newKeyword = dialog.ActionKeyword;
|
|
|
|
|
var newEnabled = dialog.KeywordEnabled;
|
|
|
|
|
|
|
|
|
|
switch (actionKeyword.Enabled, newEnabled)
|
2022-07-04 00:50:02 +00:00
|
|
|
{
|
|
|
|
|
case (true, false):
|
2022-07-01 04:56:15 +00:00
|
|
|
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
|
2022-07-04 00:50:02 +00:00
|
|
|
break;
|
|
|
|
|
case (true, true):
|
2022-07-01 04:56:15 +00:00
|
|
|
// same keyword will have dialog result false
|
|
|
|
|
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
|
2026-01-31 15:09:41 +00:00
|
|
|
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, newKeyword);
|
2022-07-04 00:50:02 +00:00
|
|
|
break;
|
|
|
|
|
case (false, true):
|
2026-01-31 15:09:41 +00:00
|
|
|
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, newKeyword);
|
2022-07-04 00:50:02 +00:00
|
|
|
break;
|
|
|
|
|
case (false, false):
|
2026-01-31 15:09:41 +00:00
|
|
|
break;
|
2022-07-01 04:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
(actionKeyword.Keyword, actionKeyword.Enabled) = (newKeyword, newEnabled);
|
2022-07-01 04:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
#endregion
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
#region AccessLinks
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-12-04 07:47:48 +00:00
|
|
|
public AccessLink? SelectedQuickAccessLink { get; set; }
|
|
|
|
|
public AccessLink? SelectedIndexSearchExcludedPath { get; set; }
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
public void AppendLink(string containerName, AccessLink link)
|
|
|
|
|
{
|
|
|
|
|
var container = containerName switch
|
|
|
|
|
{
|
|
|
|
|
"QuickAccessLink" => Settings.QuickAccessLinks,
|
2022-11-04 06:29:18 +00:00
|
|
|
"IndexSearchExcludedPaths" => Settings.IndexSearchExcludedSubdirectoryPaths,
|
2022-07-04 00:50:02 +00:00
|
|
|
_ => throw new ArgumentException($"Unknown container name: {containerName}")
|
|
|
|
|
};
|
|
|
|
|
container.Add(link);
|
2022-07-01 04:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-25 07:31:08 +00:00
|
|
|
[RelayCommand]
|
2026-01-31 15:09:41 +00:00
|
|
|
private async Task EditIndexSearchExcludePathsAsync()
|
2025-05-25 07:31:08 +00:00
|
|
|
{
|
2025-06-07 06:00:56 +00:00
|
|
|
var selectedLink = SelectedIndexSearchExcludedPath;
|
2025-05-25 07:31:08 +00:00
|
|
|
var collection = Settings.IndexSearchExcludedSubdirectoryPaths;
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2025-06-07 06:00:56 +00:00
|
|
|
if (selectedLink is null)
|
2022-07-01 04:56:15 +00:00
|
|
|
{
|
2022-07-04 00:50:02 +00:00
|
|
|
ShowUnselectedMessage();
|
|
|
|
|
return;
|
2022-07-01 04:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
var path = await PromptUserSelectPathAsync(selectedLink.Type,
|
2025-06-07 06:00:56 +00:00
|
|
|
selectedLink.Type == ResultType.Folder
|
|
|
|
|
? selectedLink.Path
|
|
|
|
|
: Path.GetDirectoryName(selectedLink.Path));
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
if (path is null)
|
2022-07-01 04:56:15 +00:00
|
|
|
return;
|
|
|
|
|
|
2025-06-07 06:00:56 +00:00
|
|
|
collection.Remove(selectedLink);
|
2022-07-04 00:50:02 +00:00
|
|
|
collection.Add(new AccessLink
|
|
|
|
|
{
|
2025-06-07 06:02:54 +00:00
|
|
|
Path = path, Type = selectedLink.Type, Name = path.GetPathName()
|
2022-07-04 00:50:02 +00:00
|
|
|
});
|
2025-06-07 06:05:48 +00:00
|
|
|
Save();
|
2022-07-01 04:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-25 05:41:17 +00:00
|
|
|
[RelayCommand]
|
2026-01-31 15:09:41 +00:00
|
|
|
private async Task AddIndexSearchExcludePathsAsync()
|
2025-05-25 05:41:17 +00:00
|
|
|
{
|
|
|
|
|
var container = Settings.IndexSearchExcludedSubdirectoryPaths;
|
2025-06-07 06:02:32 +00:00
|
|
|
|
|
|
|
|
if (container is null) return;
|
2025-06-07 06:03:33 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
var path = await PromptUserSelectFolderAsync();
|
|
|
|
|
|
|
|
|
|
if (path is null)
|
2022-07-01 04:56:15 +00:00
|
|
|
return;
|
2025-06-07 06:02:32 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
var newAccessLink = new AccessLink
|
2022-07-01 04:56:15 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
Name = path.GetPathName(),
|
|
|
|
|
Path = path
|
2022-07-04 00:50:02 +00:00
|
|
|
};
|
2025-06-07 06:02:32 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
container.Add(newAccessLink);
|
2025-05-25 19:48:20 +00:00
|
|
|
Save();
|
2022-07-01 04:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-25 07:51:56 +00:00
|
|
|
[RelayCommand]
|
2026-01-26 23:30:33 +00:00
|
|
|
private async Task EditQuickAccessLinkAsync()
|
2025-05-25 07:51:56 +00:00
|
|
|
{
|
2025-06-07 06:00:56 +00:00
|
|
|
var selectedLink = SelectedQuickAccessLink;
|
|
|
|
|
var collection = Settings.QuickAccessLinks;
|
|
|
|
|
|
|
|
|
|
if (selectedLink is null)
|
2025-05-25 07:51:56 +00:00
|
|
|
{
|
|
|
|
|
ShowUnselectedMessage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-06-07 06:05:18 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
var dialog = new QuickAccessLinkSettings(collection, selectedLink);
|
|
|
|
|
var ownerWindow = GetAvaloniaOwnerWindow();
|
|
|
|
|
bool dialogResult;
|
|
|
|
|
if (ownerWindow != null)
|
2026-01-26 23:30:33 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
dialogResult = await dialog.ShowDialog<bool?>(ownerWindow) ?? false;
|
2026-01-26 23:30:33 +00:00
|
|
|
}
|
|
|
|
|
else
|
2025-06-07 06:00:56 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
// Fallback: show as normal window if owner is not available
|
|
|
|
|
dialog.Show();
|
|
|
|
|
var tcs = new TaskCompletionSource<bool?>();
|
|
|
|
|
dialog.Closed += (_, _) => tcs.TrySetResult(true);
|
|
|
|
|
await tcs.Task;
|
|
|
|
|
dialogResult = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dialogResult)
|
|
|
|
|
{
|
|
|
|
|
Save();
|
2025-06-07 06:00:56 +00:00
|
|
|
}
|
2025-05-25 07:51:56 +00:00
|
|
|
}
|
2026-01-31 15:09:41 +00:00
|
|
|
|
2025-05-25 07:51:56 +00:00
|
|
|
[RelayCommand]
|
2026-01-26 23:30:33 +00:00
|
|
|
private async Task AddQuickAccessLinkAsync()
|
2025-05-25 07:51:56 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
var dialog = new QuickAccessLinkSettings(Settings.QuickAccessLinks);
|
|
|
|
|
var ownerWindow = GetAvaloniaOwnerWindow();
|
|
|
|
|
bool dialogResult;
|
|
|
|
|
if (ownerWindow != null)
|
2026-01-26 23:30:33 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
dialogResult = await dialog.ShowDialog<bool?>(ownerWindow) ?? false;
|
2026-01-26 23:30:33 +00:00
|
|
|
}
|
|
|
|
|
else
|
2025-06-07 06:00:56 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
// Fallback: show as normal window if owner is not available
|
|
|
|
|
dialog.Show();
|
|
|
|
|
var tcs = new TaskCompletionSource<bool?>();
|
|
|
|
|
dialog.Closed += (_, _) => tcs.TrySetResult(true);
|
|
|
|
|
await tcs.Task;
|
|
|
|
|
dialogResult = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dialogResult)
|
|
|
|
|
{
|
|
|
|
|
Save();
|
2025-06-07 06:00:56 +00:00
|
|
|
}
|
2025-05-25 07:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-15 05:18:06 +00:00
|
|
|
[RelayCommand]
|
2025-06-07 06:00:56 +00:00
|
|
|
private void RemoveLink(object commandParameter)
|
2022-07-01 04:56:15 +00:00
|
|
|
{
|
2025-06-07 06:00:56 +00:00
|
|
|
if (commandParameter is not string container) return;
|
2022-07-01 04:56:15 +00:00
|
|
|
|
|
|
|
|
switch (container)
|
|
|
|
|
{
|
|
|
|
|
case "QuickAccessLink":
|
|
|
|
|
if (SelectedQuickAccessLink == null) return;
|
2025-07-17 04:05:10 +00:00
|
|
|
if (Context.API.ShowMsgBox(
|
2025-10-14 10:49:29 +00:00
|
|
|
Localize.plugin_explorer_delete_quick_access_link(),
|
|
|
|
|
Localize.plugin_explorer_delete(),
|
2025-07-17 04:05:10 +00:00
|
|
|
MessageBoxButton.OKCancel,
|
|
|
|
|
MessageBoxImage.Warning)
|
|
|
|
|
== MessageBoxResult.Cancel)
|
|
|
|
|
return;
|
2022-07-01 04:56:15 +00:00
|
|
|
Settings.QuickAccessLinks.Remove(SelectedQuickAccessLink);
|
|
|
|
|
break;
|
|
|
|
|
case "IndexSearchExcludedPaths":
|
|
|
|
|
if (SelectedIndexSearchExcludedPath == null) return;
|
2025-07-17 04:05:10 +00:00
|
|
|
if (Context.API.ShowMsgBox(
|
2025-10-14 10:49:29 +00:00
|
|
|
Localize.plugin_explorer_delete_index_search_excluded_path(),
|
|
|
|
|
Localize.plugin_explorer_delete(),
|
2025-07-17 04:05:10 +00:00
|
|
|
MessageBoxButton.OKCancel,
|
|
|
|
|
MessageBoxImage.Warning)
|
|
|
|
|
== MessageBoxResult.Cancel)
|
|
|
|
|
return;
|
2022-07-01 04:56:15 +00:00
|
|
|
Settings.IndexSearchExcludedSubdirectoryPaths.Remove(SelectedIndexSearchExcludedPath);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Save();
|
|
|
|
|
}
|
2026-01-31 15:09:41 +00:00
|
|
|
|
2025-05-25 07:51:56 +00:00
|
|
|
private void ShowUnselectedMessage()
|
|
|
|
|
{
|
2025-10-14 10:49:29 +00:00
|
|
|
var warning = Localize.plugin_explorer_make_selection_warning();
|
2025-05-25 07:51:56 +00:00
|
|
|
Context.API.ShowMsgBox(warning);
|
|
|
|
|
}
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
private static async Task<string?> PromptUserSelectPathAsync(ResultType type, string? initialDirectory = null)
|
2022-07-04 00:50:02 +00:00
|
|
|
{
|
2022-12-04 07:47:48 +00:00
|
|
|
string? path = null;
|
2022-07-04 00:50:02 +00:00
|
|
|
|
|
|
|
|
if (type is ResultType.Folder)
|
|
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
path = await PromptUserSelectFolderAsync(initialDirectory);
|
|
|
|
|
}
|
|
|
|
|
else if (type is ResultType.File)
|
|
|
|
|
{
|
|
|
|
|
path = await PromptUserSelectFileAsync(initialDirectory);
|
|
|
|
|
}
|
|
|
|
|
return path;
|
|
|
|
|
}
|
2022-07-04 00:50:02 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
private static async Task<string?> PromptUserSelectFolderAsync(string? initialDirectory = null)
|
|
|
|
|
{
|
|
|
|
|
var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop
|
|
|
|
|
? desktop.MainWindow
|
|
|
|
|
: null;
|
2022-07-04 00:50:02 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
if (mainWindow == null) return null;
|
2022-07-04 00:50:02 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
var folders = await mainWindow.StorageProvider.OpenFolderPickerAsync(new global::Avalonia.Platform.Storage.FolderPickerOpenOptions
|
2022-07-04 00:50:02 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
AllowMultiple = false
|
|
|
|
|
});
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
return folders.Count > 0 ? folders[0].Path.LocalPath : null;
|
|
|
|
|
}
|
2022-07-01 04:56:15 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
private static async Task<string?> PromptUserSelectFileAsync(string? initialDirectory = null)
|
|
|
|
|
{
|
|
|
|
|
var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop
|
|
|
|
|
? desktop.MainWindow
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
if (mainWindow == null) return null;
|
|
|
|
|
|
|
|
|
|
var files = await mainWindow.StorageProvider.OpenFilePickerAsync(new global::Avalonia.Platform.Storage.FilePickerOpenOptions
|
|
|
|
|
{
|
|
|
|
|
AllowMultiple = false
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return files.Count > 0 ? files[0].Path.LocalPath : null;
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
2020-06-08 04:20:22 +00:00
|
|
|
|
2022-03-25 21:19:00 +00:00
|
|
|
internal static void OpenWindowsIndexingOptions()
|
2020-06-08 08:41:59 +00:00
|
|
|
{
|
|
|
|
|
var psi = new ProcessStartInfo
|
|
|
|
|
{
|
|
|
|
|
FileName = "control.exe",
|
|
|
|
|
UseShellExecute = true,
|
|
|
|
|
Arguments = Constants.WindowsIndexingOptions
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Process.Start(psi);
|
|
|
|
|
}
|
2020-07-18 14:12:11 +00:00
|
|
|
|
2025-04-15 05:18:06 +00:00
|
|
|
[RelayCommand]
|
2026-01-31 15:09:41 +00:00
|
|
|
private async Task OpenFileEditorPathAsync()
|
2022-07-04 00:50:02 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
var path = await PromptUserSelectFileAsync(Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
|
2022-07-04 00:50:02 +00:00
|
|
|
if (path is null)
|
|
|
|
|
return;
|
2020-07-18 14:12:11 +00:00
|
|
|
|
2022-12-19 20:07:07 +00:00
|
|
|
FileEditorPath = path;
|
2025-04-15 05:18:06 +00:00
|
|
|
}
|
2022-12-19 20:07:07 +00:00
|
|
|
|
2025-04-15 05:18:06 +00:00
|
|
|
[RelayCommand]
|
2026-01-31 15:09:41 +00:00
|
|
|
private async Task OpenFolderEditorPathAsync()
|
2022-12-19 20:07:07 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
var path = await PromptUserSelectFolderAsync(Settings.FolderEditorPath != null ? Path.GetDirectoryName(Settings.FolderEditorPath) : null);
|
2022-12-19 20:07:07 +00:00
|
|
|
if (path is null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
FolderEditorPath = path;
|
2025-04-15 05:18:06 +00:00
|
|
|
}
|
2022-08-17 00:52:53 +00:00
|
|
|
|
2025-04-15 05:18:06 +00:00
|
|
|
[RelayCommand]
|
2026-01-31 15:09:41 +00:00
|
|
|
private async Task OpenShellPathAsync()
|
2022-08-17 00:52:53 +00:00
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
var path = await PromptUserSelectFileAsync(Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
|
2022-08-17 00:52:53 +00:00
|
|
|
if (path is null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ShellPath = path;
|
2025-04-15 05:18:06 +00:00
|
|
|
}
|
2022-08-17 00:52:53 +00:00
|
|
|
|
2022-12-19 20:07:07 +00:00
|
|
|
public string FileEditorPath
|
2022-07-04 00:50:02 +00:00
|
|
|
{
|
|
|
|
|
get => Settings.EditorPath;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.EditorPath = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-19 20:07:07 +00:00
|
|
|
public string FolderEditorPath
|
|
|
|
|
{
|
|
|
|
|
get => Settings.FolderEditorPath;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.FolderEditorPath = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-17 00:52:53 +00:00
|
|
|
public string ShellPath
|
|
|
|
|
{
|
|
|
|
|
get => Settings.ShellPath;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.ShellPath = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-16 22:44:18 +00:00
|
|
|
|
|
|
|
|
public string ExcludedFileTypes
|
|
|
|
|
{
|
|
|
|
|
get => Settings.ExcludedFileTypes;
|
|
|
|
|
set
|
|
|
|
|
{
|
2024-07-16 22:59:30 +00:00
|
|
|
// remove spaces and dots from the string before saving
|
|
|
|
|
string sanitized = string.IsNullOrEmpty(value) ? "" : value.Replace(" ", "").Replace(".", "");
|
2024-07-16 22:44:18 +00:00
|
|
|
Settings.ExcludedFileTypes = sanitized;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-05 13:34:24 +00:00
|
|
|
|
2025-09-21 12:23:00 +00:00
|
|
|
public int MaxResultLowerLimit { get; } = 1;
|
|
|
|
|
public int MaxResultUpperLimit { get; } = 100000;
|
2024-08-10 11:24:48 +00:00
|
|
|
|
2024-08-05 13:34:24 +00:00
|
|
|
public int MaxResult
|
|
|
|
|
{
|
|
|
|
|
get => Settings.MaxResult;
|
|
|
|
|
set
|
|
|
|
|
{
|
2024-08-10 11:24:48 +00:00
|
|
|
Settings.MaxResult = Math.Clamp(value, MaxResultLowerLimit, MaxResultUpperLimit);
|
2024-08-05 13:34:24 +00:00
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-17 00:52:53 +00:00
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
#endregion
|
|
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
#region Everything FastSortWarning
|
2022-03-13 03:27:46 +00:00
|
|
|
|
2025-09-21 12:23:00 +00:00
|
|
|
public List<EverythingSortOptionLocalized> AllEverythingSortOptions { get; } = EverythingSortOptionLocalized.GetValues();
|
2025-07-16 13:28:17 +00:00
|
|
|
|
|
|
|
|
public EverythingSortOption SelectedEverythingSortOption
|
|
|
|
|
{
|
|
|
|
|
get => Settings.SortOption;
|
|
|
|
|
set
|
|
|
|
|
{
|
2025-07-18 13:33:32 +00:00
|
|
|
if (value == Settings.SortOption)
|
|
|
|
|
return;
|
2025-07-16 13:28:17 +00:00
|
|
|
Settings.SortOption = value;
|
2025-07-23 01:09:12 +00:00
|
|
|
OnPropertyChanged();
|
2025-07-16 13:28:17 +00:00
|
|
|
OnPropertyChanged(nameof(FastSortWarningVisibility));
|
|
|
|
|
OnPropertyChanged(nameof(SortOptionWarningMessage));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 15:09:41 +00:00
|
|
|
public bool FastSortWarningVisibility
|
2022-07-04 00:50:02 +00:00
|
|
|
{
|
2022-03-13 03:27:46 +00:00
|
|
|
get
|
|
|
|
|
{
|
2022-07-04 00:50:02 +00:00
|
|
|
try
|
|
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
return !EverythingApi.IsFastSortOption(Settings.SortOption);
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
catch (IPCErrorException)
|
|
|
|
|
{
|
|
|
|
|
// this error occurs if the Everything service is not running, in this instance show the warning and
|
|
|
|
|
// update the message to let user know in the settings panel.
|
2026-01-31 15:09:41 +00:00
|
|
|
return true;
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
2022-11-29 03:38:17 +00:00
|
|
|
catch (DllNotFoundException)
|
|
|
|
|
{
|
2026-01-31 15:09:41 +00:00
|
|
|
return false;
|
2022-11-29 03:38:17 +00:00
|
|
|
}
|
2022-03-13 03:27:46 +00:00
|
|
|
}
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
2025-07-23 01:09:12 +00:00
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
public string SortOptionWarningMessage
|
|
|
|
|
{
|
|
|
|
|
get
|
2022-03-13 03:27:46 +00:00
|
|
|
{
|
2022-07-04 00:50:02 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// this method is used to determine if Everything service is running because as at Everything v1.4.1
|
|
|
|
|
// the sdk does not provide a dedicated interface to determine if it is running.
|
|
|
|
|
return EverythingApi.IsFastSortOption(Settings.SortOption) ? string.Empty
|
2025-07-16 13:28:17 +00:00
|
|
|
: Localize.flowlauncher_plugin_everything_nonfastsort_warning();
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
catch (IPCErrorException)
|
|
|
|
|
{
|
2025-07-16 13:28:17 +00:00
|
|
|
return Localize.flowlauncher_plugin_everything_is_not_running();
|
2022-07-04 00:50:02 +00:00
|
|
|
}
|
2022-11-29 03:38:17 +00:00
|
|
|
catch (DllNotFoundException)
|
|
|
|
|
{
|
2025-07-16 13:28:17 +00:00
|
|
|
return Localize.flowlauncher_plugin_everything_sdk_issue();
|
2022-11-29 03:38:17 +00:00
|
|
|
}
|
2022-03-13 03:27:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-04 00:50:02 +00:00
|
|
|
|
2022-08-17 01:32:49 +00:00
|
|
|
public string EverythingInstalledPath
|
|
|
|
|
{
|
|
|
|
|
get => Settings.EverythingInstalledPath;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Settings.EverythingInstalledPath = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-04 00:50:02 +00:00
|
|
|
#endregion
|
2020-05-11 13:15:15 +00:00
|
|
|
}
|
2022-11-04 06:29:18 +00:00
|
|
|
}
|