Skip to content

Commit 4221568

Browse files
authored
Feature: Added support for pasting items as shortcuts (#16410)
1 parent fa3bce1 commit 4221568

File tree

7 files changed

+91
-1
lines changed

7 files changed

+91
-1
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) 2024 Files Community
2+
// Licensed under the MIT License. See the LICENSE.
3+
4+
namespace Files.App.Actions
5+
{
6+
internal sealed class PasteItemAsShortcutAction : ObservableObject, IAction
7+
{
8+
private readonly IContentPageContext context;
9+
10+
public string Label
11+
=> Strings.PasteShortcut.GetLocalizedResource();
12+
13+
public string Description
14+
=> Strings.PasteShortcutDescription.GetLocalizedResource();
15+
16+
public RichGlyph Glyph
17+
=> new(themedIconStyle: "App.ThemedIcons.Paste");
18+
19+
public bool IsExecutable
20+
=> GetIsExecutable();
21+
22+
public PasteItemAsShortcutAction()
23+
{
24+
context = Ioc.Default.GetRequiredService<IContentPageContext>();
25+
26+
context.PropertyChanged += Context_PropertyChanged;
27+
App.AppModel.PropertyChanged += AppModel_PropertyChanged;
28+
}
29+
30+
public Task ExecuteAsync(object? parameter = null)
31+
{
32+
if (context.ShellPage is null)
33+
return Task.CompletedTask;
34+
35+
string path = context.ShellPage.ShellViewModel.WorkingDirectory;
36+
return UIFilesystemHelpers.PasteItemAsShortcutAsync(path, context.ShellPage);
37+
}
38+
39+
public bool GetIsExecutable()
40+
{
41+
return
42+
App.AppModel.IsPasteEnabled &&
43+
context.PageType != ContentPageTypes.Home &&
44+
context.PageType != ContentPageTypes.RecycleBin &&
45+
context.PageType != ContentPageTypes.SearchResults;
46+
}
47+
48+
private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e)
49+
{
50+
if (e.PropertyName is nameof(IContentPageContext.PageType))
51+
OnPropertyChanged(nameof(IsExecutable));
52+
}
53+
54+
private void AppModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
55+
{
56+
if (e.PropertyName is nameof(AppModel.IsPasteEnabled))
57+
OnPropertyChanged(nameof(IsExecutable));
58+
}
59+
}
60+
}

src/Files.App/Data/Commands/Manager/CommandCodes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public enum CommandCodes
3535
CopyPathWithQuotes,
3636
CutItem,
3737
PasteItem,
38+
PasteItemAsShortcut,
3839
PasteItemToSelection,
3940
DeleteItem,
4041
DeleteItemPermanently,

src/Files.App/Data/Commands/Manager/CommandManager.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public IRichCommand this[HotKey hotKey]
8787
public IRichCommand CopyPathWithQuotes => commands[CommandCodes.CopyPathWithQuotes];
8888
public IRichCommand CutItem => commands[CommandCodes.CutItem];
8989
public IRichCommand PasteItem => commands[CommandCodes.PasteItem];
90+
public IRichCommand PasteItemAsShortcut => commands[CommandCodes.PasteItemAsShortcut];
9091
public IRichCommand PasteItemToSelection => commands[CommandCodes.PasteItemToSelection];
9192
public IRichCommand DeleteItem => commands[CommandCodes.DeleteItem];
9293
public IRichCommand DeleteItemPermanently => commands[CommandCodes.DeleteItemPermanently];
@@ -281,6 +282,7 @@ public IEnumerator<IRichCommand> GetEnumerator() =>
281282
[CommandCodes.CopyPathWithQuotes] = new CopyPathWithQuotesAction(),
282283
[CommandCodes.CutItem] = new CutItemAction(),
283284
[CommandCodes.PasteItem] = new PasteItemAction(),
285+
[CommandCodes.PasteItemAsShortcut] = new PasteItemAsShortcutAction(),
284286
[CommandCodes.PasteItemToSelection] = new PasteItemToSelectionAction(),
285287
[CommandCodes.DeleteItem] = new DeleteItemAction(),
286288
[CommandCodes.DeleteItemPermanently] = new DeleteItemPermanentlyAction(),

src/Files.App/Data/Commands/Manager/ICommandManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public interface ICommandManager : IEnumerable<IRichCommand>
3939
IRichCommand CopyPathWithQuotes { get; }
4040
IRichCommand CutItem { get; }
4141
IRichCommand PasteItem { get; }
42+
IRichCommand PasteItemAsShortcut { get; }
4243
IRichCommand PasteItemToSelection { get; }
4344
IRichCommand DeleteItem { get; }
4445
IRichCommand DeleteItemPermanently { get; }

src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ public static List<ContextMenuFlyoutItemViewModel> GetBaseItemMenuItems(
459459
IsPrimary = true,
460460
IsVisible = true,
461461
}.Build(),
462+
new ContextMenuFlyoutItemViewModelBuilder(Commands.PasteItemAsShortcut).Build(),
462463
new ContextMenuFlyoutItemViewModelBuilder(Commands.CopyItemPath)
463464
{
464465
IsVisible = UserSettingsService.GeneralSettingsService.ShowCopyPath

src/Files.App/Helpers/UI/UIFilesystemHelpers.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License. See the LICENSE.
33

44
using Files.App.Dialogs;
5-
using Files.App.Storage.Storables;
65
using Microsoft.Extensions.Logging;
76
using System.IO;
87
using System.Net;
@@ -26,6 +25,26 @@ public static async Task PasteItemAsync(string destinationPath, IShellPage assoc
2625
}
2726
}
2827

28+
public static async Task PasteItemAsShortcutAsync(string destinationPath, IShellPage associatedInstance)
29+
{
30+
FilesystemResult<DataPackageView> packageView = await FilesystemTasks.Wrap(() => Task.FromResult(Clipboard.GetContent()));
31+
if (packageView.Result.Contains(StandardDataFormats.StorageItems))
32+
{
33+
var items = await packageView.Result.GetStorageItemsAsync();
34+
await Task.WhenAll(items.Select(async item =>
35+
{
36+
var fileName = FilesystemHelpers.GetShortcutNamingPreference(item.Name);
37+
var filePath = Path.Combine(destinationPath ?? string.Empty, fileName);
38+
39+
if (!await FileOperationsHelpers.CreateOrUpdateLinkAsync(filePath, item.Path))
40+
await HandleShortcutCannotBeCreated(fileName, item.Path);
41+
}));
42+
}
43+
44+
if (associatedInstance is not null)
45+
await associatedInstance.RefreshIfNoWatcherExistsAsync();
46+
}
47+
2948
public static async Task<bool> RenameFileItemAsync(ListedItem item, string newName, IShellPage associatedInstance, bool showExtensionDialog = true)
3049
{
3150
if (item is AlternateStreamItem ads) // For alternate streams Name is not a substring ItemNameRaw

src/Files.App/Strings/en-US/Resources.resw

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@
252252
<data name="Paste" xml:space="preserve">
253253
<value>Paste</value>
254254
</data>
255+
<data name="PasteShortcut" xml:space="preserve">
256+
<value>Paste shortcut</value>
257+
</data>
255258
<data name="BaseLayoutContextFlyoutNew.Label" xml:space="preserve">
256259
<value>New</value>
257260
</data>
@@ -2411,6 +2414,9 @@
24112414
<data name="PasteItemDescription" xml:space="preserve">
24122415
<value>Paste item(s) from clipboard to current folder</value>
24132416
</data>
2417+
<data name="PasteShortcutDescription" xml:space="preserve">
2418+
<value>Paste item(s) from clipboard to current folder as shortcuts</value>
2419+
</data>
24142420
<data name="PasteItemToSelectionDescription" xml:space="preserve">
24152421
<value>Paste item(s) from clipboard to selected folder</value>
24162422
</data>

0 commit comments

Comments
 (0)