Skip to content

Commit ffb57c7

Browse files
authored
Merge branch 'main' into terminal
2 parents 58b5043 + 8781bfa commit ffb57c7

File tree

106 files changed

+3312
-3688
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+3312
-3688
lines changed

src/Files.App (Package)/Package.appxmanifest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<Identity
1717
Name="FilesDev"
1818
Publisher="CN=Files"
19-
Version="3.3.5.0" />
19+
Version="3.3.6.0" />
2020

2121
<Properties>
2222
<DisplayName>Files - Dev</DisplayName>

src/Files.App.OpenDialog/FilesOpenDialog.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,43 @@ IShellItem* CloneShellItem(IShellItem* psi)
4848
return item;
4949
}
5050

51+
std::string wstring_to_utf8_hex(const std::wstring& input)
52+
{
53+
std::string output;
54+
55+
int cbNeeded = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, NULL, 0, NULL, NULL);
56+
if (cbNeeded > 0)
57+
{
58+
char* utf8 = new char[cbNeeded];
59+
if (WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, utf8, cbNeeded, NULL, NULL) != 0)
60+
{
61+
for (char* p = utf8; *p; p++)
62+
{
63+
char onehex[5];
64+
sprintf_s(onehex, sizeof(onehex), "%%%02.2X", (unsigned char)*p);
65+
output.append(onehex);
66+
}
67+
}
68+
69+
delete[] utf8;
70+
}
71+
72+
return output;
73+
}
74+
75+
std::wstring str2wstr(const std::string& str)
76+
{
77+
int cbNeeded = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
78+
if (cbNeeded > 0)
79+
{
80+
std::wstring wstrTo(cbNeeded, 0);
81+
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], cbNeeded);
82+
return wstrTo;
83+
}
84+
85+
return L"";
86+
}
87+
5188
template <typename T>
5289
CComPtr<T> AsInterface(CComPtr<IFileOpenDialog> dialog)
5390
{
@@ -118,19 +155,27 @@ STDAPICALL CFilesOpenDialog::Show(HWND hwndOwner)
118155
SHELLEXECUTEINFO ShExecInfo = { 0 };
119156
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
120157
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
121-
ShExecInfo.lpFile = L"files.exe";
158+
122159
PWSTR pszPath = NULL;
160+
WCHAR szBuf[MAX_PATH];
161+
TCHAR args[1024] = { 0 };
162+
ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1);
123163

124164
HANDLE closeEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("FILEDIALOG"));
125165

126166
if (_initFolder && SUCCEEDED(_initFolder->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszPath)))
127167
{
128-
TCHAR args[1024];
129-
wsprintf(args, L"-directory \"%s\" -outputpath \"%s\"", pszPath, _outputPath.c_str());
168+
swprintf(args, _countof(args) - 1, L"\"%s\" -directory \"%s\" -outputpath \"%s\"", szBuf, pszPath, _outputPath.c_str());
130169
wcout << L"Invoking: " << args << endl;
131-
ShExecInfo.lpParameters = args;
132170
CoTaskMemFree(pszPath);
133171
}
172+
else
173+
{
174+
swprintf(args, _countof(args) - 1, L"\"%s\" -outputpath \"%s\"", szBuf, _outputPath.c_str());
175+
}
176+
177+
std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args));
178+
ShExecInfo.lpFile = uriWithArgs.c_str();
134179
ShExecInfo.nShow = SW_SHOW;
135180
ShellExecuteEx(&ShExecInfo);
136181

src/Files.App.SaveDialog/FilesSaveDialog.cpp

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,43 @@ IShellItem* CloneShellItem(IShellItem* psi)
4646
return item;
4747
}
4848

49+
std::string wstring_to_utf8_hex(const std::wstring& input)
50+
{
51+
std::string output;
52+
53+
int cbNeeded = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, NULL, 0, NULL, NULL);
54+
if (cbNeeded > 0)
55+
{
56+
char* utf8 = new char[cbNeeded];
57+
if (WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, utf8, cbNeeded, NULL, NULL) != 0)
58+
{
59+
for (char* p = utf8; *p; p++)
60+
{
61+
char onehex[5];
62+
sprintf_s(onehex, sizeof(onehex), "%%%02.2X", (unsigned char)*p);
63+
output.append(onehex);
64+
}
65+
}
66+
67+
delete[] utf8;
68+
}
69+
70+
return output;
71+
}
72+
73+
std::wstring str2wstr(const std::string& str)
74+
{
75+
int cbNeeded = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
76+
if (cbNeeded > 0)
77+
{
78+
std::wstring wstrTo(cbNeeded, 0);
79+
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], cbNeeded);
80+
return wstrTo;
81+
}
82+
83+
return L"";
84+
}
85+
4986
template <typename T>
5087
CComPtr<T> AsInterface(CComPtr<IFileSaveDialog> dialog)
5188
{
@@ -394,26 +431,34 @@ HRESULT __stdcall CFilesSaveDialog::Show(HWND hwndOwner)
394431
SHELLEXECUTEINFO ShExecInfo = { 0 };
395432
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
396433
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
397-
ShExecInfo.lpFile = L"files.exe";
434+
398435
PWSTR pszPath = NULL;
436+
WCHAR szBuf[MAX_PATH];
437+
TCHAR args[1024] = { 0 };
438+
ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\files.exe", szBuf, MAX_PATH - 1);
399439

400440
HANDLE closeEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("FILEDIALOG"));
401441

402442
if (_initFolder && SUCCEEDED(_initFolder->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszPath)))
403443
{
404-
TCHAR args[1024];
405444
if (!_initName.empty())
406445
{
407-
wsprintf(args, L"-directory \"%s\" -outputpath \"%s\" -select \"%s\"", pszPath, _outputPath.c_str(), _initName.c_str());
446+
swprintf(args, _countof(args) - 1, L"\"%s\" -directory \"%s\" -outputpath \"%s\" -select \"%s\"", szBuf, pszPath, _outputPath.c_str(), _initName.c_str());
408447
}
409448
else
410449
{
411-
wsprintf(args, L"-directory \"%s\" -outputpath \"%s\"", pszPath, _outputPath.c_str());
450+
swprintf(args, _countof(args) - 1, L"\"%s\" -directory \"%s\" -outputpath \"%s\"", szBuf, pszPath, _outputPath.c_str());
412451
}
413452
wcout << L"Invoking: " << args << endl;
414-
ShExecInfo.lpParameters = args;
415453
CoTaskMemFree(pszPath);
416454
}
455+
else
456+
{
457+
swprintf(args, _countof(args) - 1, L"\"%s\" -outputpath \"%s\"", szBuf, _outputPath.c_str());
458+
}
459+
460+
std::wstring uriWithArgs = L"files-uwp:?cmd=" + str2wstr(wstring_to_utf8_hex(args));
461+
ShExecInfo.lpFile = uriWithArgs.c_str();
417462
ShExecInfo.nShow = SW_SHOW;
418463
ShellExecuteEx(&ShExecInfo);
419464

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright (c) 2024 Files Community
2+
// Licensed under the MIT License. See the LICENSE.
3+
4+
using Files.App.Actions;
5+
using Microsoft.AppCenter.Analytics;
6+
using Microsoft.UI.Xaml;
7+
using Microsoft.UI.Xaml.Controls;
8+
using Microsoft.UI.Xaml.Input;
9+
10+
namespace Files.App.Data.Commands
11+
{
12+
[DebuggerDisplay("Command {Code}")]
13+
internal sealed class ActionCommand : ObservableObject, IRichCommand
14+
{
15+
private IActionsSettingsService ActionsSettingsService { get; } = Ioc.Default.GetRequiredService<IActionsSettingsService>();
16+
17+
public event EventHandler? CanExecuteChanged;
18+
19+
public IAction Action { get; }
20+
21+
public CommandCodes Code { get; }
22+
23+
public string Label
24+
=> Action.Label;
25+
26+
public string LabelWithHotKey
27+
=> HotKeyText is null ? Label : $"{Label} ({HotKeyText})";
28+
29+
public string AutomationName
30+
=> Label;
31+
32+
public string Description
33+
=> Action.Description;
34+
35+
public RichGlyph Glyph
36+
=> Action.Glyph;
37+
38+
public object? Icon { get; }
39+
40+
public FontIcon? FontIcon { get; }
41+
42+
public Style? OpacityStyle { get; }
43+
44+
private bool isCustomHotKeys = false;
45+
public bool IsCustomHotKeys
46+
{
47+
get => isCustomHotKeys;
48+
set => SetProperty(ref isCustomHotKeys, value);
49+
}
50+
51+
public string? HotKeyText
52+
{
53+
get
54+
{
55+
string text = HotKeys.LocalizedLabel;
56+
if (string.IsNullOrEmpty(text))
57+
return null;
58+
return text;
59+
}
60+
}
61+
62+
private HotKeyCollection hotKeys;
63+
public HotKeyCollection HotKeys
64+
{
65+
get => hotKeys;
66+
set
67+
{
68+
if (SetProperty(ref hotKeys, value))
69+
{
70+
OnPropertyChanged(nameof(HotKeyText));
71+
OnPropertyChanged(nameof(LabelWithHotKey));
72+
IsCustomHotKeys = true;
73+
}
74+
}
75+
}
76+
77+
public HotKeyCollection DefaultHotKeys { get; }
78+
79+
public bool IsToggle
80+
=> Action is IToggleAction;
81+
82+
public bool IsOn
83+
{
84+
get => Action is IToggleAction toggleAction && toggleAction.IsOn;
85+
set
86+
{
87+
if (Action is IToggleAction toggleAction && toggleAction.IsOn != value)
88+
Execute(null);
89+
}
90+
}
91+
92+
public bool IsExecutable
93+
=> Action.IsExecutable;
94+
95+
public ActionCommand(CommandManager manager, CommandCodes code, IAction action)
96+
{
97+
Code = code;
98+
Action = action;
99+
Icon = action.Glyph.ToIcon();
100+
FontIcon = action.Glyph.ToFontIcon();
101+
OpacityStyle = action.Glyph.ToOpacityStyle();
102+
hotKeys = CommandManager.GetDefaultKeyBindings(action);
103+
DefaultHotKeys = CommandManager.GetDefaultKeyBindings(action);
104+
105+
if (action is INotifyPropertyChanging notifyPropertyChanging)
106+
notifyPropertyChanging.PropertyChanging += Action_PropertyChanging;
107+
if (action is INotifyPropertyChanged notifyPropertyChanged)
108+
notifyPropertyChanged.PropertyChanged += Action_PropertyChanged;
109+
}
110+
111+
public bool CanExecute(object? parameter)
112+
{
113+
return Action.IsExecutable;
114+
}
115+
116+
public async void Execute(object? parameter)
117+
{
118+
await ExecuteAsync();
119+
}
120+
121+
public Task ExecuteAsync()
122+
{
123+
if (IsExecutable)
124+
{
125+
Analytics.TrackEvent($"Triggered {Code} action");
126+
return Action.ExecuteAsync();
127+
}
128+
129+
return Task.CompletedTask;
130+
}
131+
132+
public async void ExecuteTapped(object sender, TappedRoutedEventArgs e)
133+
{
134+
await ExecuteAsync();
135+
}
136+
137+
internal void OverwriteKeyBindings(HotKeyCollection hotKeys)
138+
{
139+
HotKeys = hotKeys;
140+
}
141+
142+
internal void RestoreKeyBindings()
143+
{
144+
hotKeys = DefaultHotKeys;
145+
IsCustomHotKeys = false;
146+
}
147+
148+
private void Action_PropertyChanging(object? sender, PropertyChangingEventArgs e)
149+
{
150+
switch (e.PropertyName)
151+
{
152+
case nameof(IAction.Label):
153+
OnPropertyChanging(nameof(Label));
154+
OnPropertyChanging(nameof(LabelWithHotKey));
155+
OnPropertyChanging(nameof(AutomationName));
156+
break;
157+
case nameof(IToggleAction.IsOn) when IsToggle:
158+
OnPropertyChanging(nameof(IsOn));
159+
break;
160+
case nameof(IAction.IsExecutable):
161+
OnPropertyChanging(nameof(IsExecutable));
162+
break;
163+
}
164+
}
165+
166+
private void Action_PropertyChanged(object? sender, PropertyChangedEventArgs e)
167+
{
168+
switch (e.PropertyName)
169+
{
170+
case nameof(IAction.Label):
171+
OnPropertyChanged(nameof(Label));
172+
OnPropertyChanged(nameof(LabelWithHotKey));
173+
OnPropertyChanged(nameof(AutomationName));
174+
break;
175+
case nameof(IToggleAction.IsOn) when IsToggle:
176+
OnPropertyChanged(nameof(IsOn));
177+
break;
178+
case nameof(IAction.IsExecutable):
179+
OnPropertyChanged(nameof(IsExecutable));
180+
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
181+
break;
182+
}
183+
}
184+
}
185+
}

src/Files.App/Data/Commands/IRichCommand.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,5 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper
3434

3535
Task ExecuteAsync();
3636
void ExecuteTapped(object sender, TappedRoutedEventArgs e);
37-
38-
void ResetHotKeys();
3937
}
4038
}

0 commit comments

Comments
 (0)