Skip to content

Commit e9b486f

Browse files
committed
[WIP] File Actions Menu
1 parent bc1214a commit e9b486f

File tree

10 files changed

+327
-85
lines changed

10 files changed

+327
-85
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Microsoft Corporation
2+
// The Microsoft Corporation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Security.Cryptography;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
using System.Windows.Controls;
13+
using Wpf.Ui.Controls;
14+
15+
namespace FileActionsMenu.Ui.Actions
16+
{
17+
internal static class Hashes
18+
{
19+
internal static void GenerateHashes(object sender, string[] selectedItems)
20+
{
21+
Func<string, string> hashGeneratorFunction;
22+
#pragma warning disable CS0219 // Variable is assigned but its value is never used
23+
string fileExtension;
24+
#pragma warning restore CS0219 // Variable is assigned but its value is never used
25+
26+
switch (((System.Windows.Controls.MenuItem)sender).Name)
27+
{
28+
case "Md5HashMenuItem":
29+
#pragma warning disable CA5351
30+
hashGeneratorFunction = (string filename) => BitConverter.ToString(MD5.Create().ComputeHash(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))).Replace("-", string.Empty);
31+
#pragma warning restore CA5351
32+
fileExtension = ".md5";
33+
break;
34+
case "Sha1HashMenuItem":
35+
#pragma warning disable CA5350
36+
hashGeneratorFunction = (string filename) => BitConverter.ToString(SHA1.Create().ComputeHash(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))).Replace("-", string.Empty);
37+
#pragma warning restore CA5350
38+
fileExtension = ".sha1";
39+
break;
40+
case "Sha256HashMenuItem":
41+
hashGeneratorFunction = (string filename) => BitConverter.ToString(SHA256.Create().ComputeHash(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))).Replace("-", string.Empty);
42+
fileExtension = ".sha256";
43+
break;
44+
default:
45+
throw new InvalidOperationException("Unknown hash type");
46+
}
47+
48+
FluentWindow window = new FluentWindow();
49+
window.Content = new ContentPresenter();
50+
ContentDialog contentDialog = new ContentDialog((ContentPresenter)window.Content);
51+
contentDialog.Title = "Save hashes to ... file(s)?";
52+
contentDialog.PrimaryButtonText = "Multiple";
53+
contentDialog.SecondaryButtonText = "Single";
54+
window.Width = 0;
55+
window.Height = 0;
56+
window.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
57+
try
58+
{
59+
window.Show();
60+
}
61+
catch (InvalidOperationException)
62+
{
63+
// Ignore
64+
}
65+
66+
window.Focus();
67+
window.Activate();
68+
contentDialog.ShowAsync().ContinueWith((task) =>
69+
{
70+
if (task.Result == ContentDialogResult.Primary)
71+
{
72+
foreach (string filename in selectedItems)
73+
{
74+
string hash = hashGeneratorFunction(filename);
75+
76+
string hashFilename = filename + fileExtension;
77+
78+
File.WriteAllText(hashFilename, hash);
79+
}
80+
}
81+
else if (task.Result == ContentDialogResult.Secondary)
82+
{
83+
StringBuilder fileContent = new();
84+
85+
foreach (string filename in selectedItems)
86+
{
87+
fileContent.Append(filename + ":\n" + hashGeneratorFunction(filename) + "\n\n");
88+
}
89+
90+
File.WriteAllText((Path.GetDirectoryName(selectedItems[0]) ?? throw new ArgumentNullException(nameof(selectedItems))) + "\\hashes" + fileExtension, fileContent.ToString());
91+
}
92+
93+
window.Dispatcher.Invoke(() => window.Close());
94+
});
95+
}
96+
}
97+
}

src/modules/previewpane/FileActionsMenu/FileActionsMenu.Ui/App.xaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
<Application x:Class="FileActionsMenu.App"
1+
<Application x:Class="FileActionsMenu.Ui.App"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4-
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
5-
StartupUri="MainWindow.xaml">
4+
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
65
<Application.Resources>
76
<ResourceDictionary>
87
<ResourceDictionary.MergedDictionaries>

src/modules/previewpane/FileActionsMenu/FileActionsMenu.Ui/App.xaml.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@
22
// The Microsoft Corporation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System.Runtime.Versioning;
5+
using System;
6+
using System.Collections;
67
using System.Windows;
7-
using Wpf.Ui.Markup;
88

9-
namespace FileActionsMenu
9+
namespace FileActionsMenu.Ui
1010
{
11-
/// <summary>
12-
/// Interaction logic for App.xaml
13-
/// </summary>
1411
public partial class App : Application
1512
{
13+
public App()
14+
{
15+
string[] items = ExplorerHelper.GetSelectedItems();
16+
if (items.Length == 0)
17+
{
18+
Environment.Exit(0);
19+
return;
20+
}
21+
22+
MainWindow main = new MainWindow(items);
23+
24+
// main.AllowsTransparency = true;
25+
// main.WindowStyle = WindowStyle.None;
26+
}
1627
}
1728
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Microsoft Corporation
2+
// The Microsoft Corporation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections;
7+
using System.Collections.Generic;
8+
using System.Globalization;
9+
using System.IO;
10+
11+
namespace FileActionsMenu.Ui
12+
{
13+
public sealed class ExplorerHelper
14+
{
15+
// Source: https://stackoverflow.com/questions/14193388/how-to-get-windows-explorers-selected-files-from-within-c
16+
public static string[] GetSelectedItems()
17+
{
18+
string filename;
19+
List<string> selected = [];
20+
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows())
21+
{
22+
filename = Path.GetFileNameWithoutExtension(window.FullName).ToLower(CultureInfo.InvariantCulture);
23+
if (filename.Equals("explorer", StringComparison.OrdinalIgnoreCase))
24+
{
25+
Shell32.FolderItems items = ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems();
26+
foreach (Shell32.FolderItem item in items)
27+
{
28+
selected.Add(item.Path);
29+
}
30+
}
31+
}
32+
33+
return selected.ToArray();
34+
}
35+
}
36+
}

src/modules/previewpane/FileActionsMenu/FileActionsMenu.Ui/FileActionsMenu.Ui.csproj

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<PropertyGroup>
55
<OutputType>WinExe</OutputType>
6-
<TargetFramework>net8.0-windows</TargetFramework>
6+
<TargetFramework>net8.0-windows10.0.20348</TargetFramework>
77
<AssemblyTitle>PowerToys.FileActionsMenu.Ui</AssemblyTitle>
88
<AssemblyDescription>PowerToys File Actions Menu</AssemblyDescription>
99
<Description>PowerToys File Actions Menu</Description>
@@ -19,11 +19,28 @@
1919
</PropertyGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="WPF-UI" />
22+
<COMReference Include="SHDocVw">
23+
<WrapperTool>tlbimp</WrapperTool>
24+
<VersionMinor>1</VersionMinor>
25+
<VersionMajor>1</VersionMajor>
26+
<Guid>eab22ac0-30c1-11cf-a7eb-0000c05bae0b</Guid>
27+
<Lcid>0</Lcid>
28+
<Isolated>false</Isolated>
29+
<EmbedInteropTypes>true</EmbedInteropTypes>
30+
</COMReference>
31+
<COMReference Include="Shell32">
32+
<WrapperTool>tlbimp</WrapperTool>
33+
<VersionMinor>0</VersionMinor>
34+
<VersionMajor>1</VersionMajor>
35+
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
36+
<Lcid>0</Lcid>
37+
<Isolated>false</Isolated>
38+
<EmbedInteropTypes>true</EmbedInteropTypes>
39+
</COMReference>
2340
</ItemGroup>
2441

2542
<ItemGroup>
26-
<Folder Include="Actions\" />
43+
<PackageReference Include="WPF-UI" />
2744
</ItemGroup>
2845

2946
</Project>
Lines changed: 72 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,79 @@
1-
<Window x:Class="FileActionsMenu.MainWindow"
1+
<ui:FluentWindow x:Class="FileActionsMenu.Ui.MainWindow"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
55
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
66
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
77
mc:Ignorable="d"
8+
Name="_this"
9+
WindowStyle="None"
810
Title="MainWindow" Height="0" Width="0" MinHeight="0" MinWidth="0" MaxHeight="0" MaxWidth="0"
9-
AllowsTransparency="True" WindowStyle="None" Background="Transparent">
10-
<Window.Resources>
11-
<ContextMenu x:Key="Menu">
12-
<MenuItem Header="PowerToys File actions" IsEnabled="False"></MenuItem>
13-
<Separator />
14-
<Separator />
15-
<MenuItem Header="Generate Checksum">
16-
<MenuItem Header="Md5 hash" Click="GenerateHash" />
17-
<MenuItem Header="Sha1 hash" Click="GenerateHashes"/>
18-
<MenuItem Header="Sha256 hash" />
19-
</MenuItem>
20-
<Separator/>
21-
<ui:MenuItem Header="Copy to" Icon="{ui:FontIcon '&#xE8C3;'}">
22-
</ui:MenuItem>
23-
<ui:MenuItem Header="Move to">
24-
<ui:MenuItem.Icon>
25-
<ui:FontIcon Glyph="&#xE8C6;" />
26-
</ui:MenuItem.Icon>
27-
</ui:MenuItem>
28-
<MenuItem Header="New folder with selection">
29-
<MenuItem.Icon>
30-
<ui:FontIcon Glyph="&#xE8DE;" />
31-
</MenuItem.Icon>
32-
</MenuItem>
33-
<Separator/>
34-
<MenuItem Header="Copy path of files sperated by">
35-
<MenuItem Header='";"'/>
36-
<MenuItem Header='" "'/>
37-
<MenuItem Header='","'/>
38-
<MenuItem Header='Newline'/>
39-
<MenuItem Header='Custom...' />
40-
</MenuItem>
41-
<Separator />
42-
<MenuItem Header="Rename with PowerRename">
43-
<MenuItem.Icon>
44-
<ui:FontIcon Glyph="&#xE8AC;" />
45-
</MenuItem.Icon>
46-
</MenuItem>
47-
<MenuItem Header="Transform images with Image Resizer">
48-
<MenuItem.Icon>
49-
<ui:FontIcon Glyph="&#xEE71;" />
50-
</MenuItem.Icon>
51-
</MenuItem>
52-
<Separator />
53-
<MenuItem Header="Following items only apply to the first-selected file (test.txt)" IsEnabled="False"/>
54-
<MenuItem Header="What's using this file?">
55-
<MenuItem.Icon>
56-
<ui:FontIcon Glyph="&#xE72E;" />
57-
</MenuItem.Icon>
58-
</MenuItem>
59-
<MenuItem Header="Copy image to clipboard">
60-
<MenuItem.Icon>
61-
<ui:FontIcon Glyph="&#xF413;" />
62-
</MenuItem.Icon>
63-
</MenuItem>
64-
</ContextMenu>
65-
</Window.Resources>
66-
</Window>
11+
ui:AllowsTransparency="True" Background="Transparent">
12+
<ui:FluentWindow.Resources>
13+
<ResourceDictionary>
14+
<ResourceDictionary.MergedDictionaries>
15+
<ui:ThemesDictionary Theme="Dark"></ui:ThemesDictionary>
16+
<ui:ControlsDictionary></ui:ControlsDictionary>
17+
</ResourceDictionary.MergedDictionaries>
18+
<ContextMenu x:Key="Menu">
19+
<ui:MenuItem Header="PowerToys File actions" IsEnabled="False"></ui:MenuItem>
20+
<Separator/>
21+
<Separator/>
22+
<ui:MenuItem Header="Generate Checksum">
23+
<ui:MenuItem x:Name="Md5HashMenuItem" Header="Md5 hash" Click="GenerateHashes"/>
24+
<ui:MenuItem x:Name="Sha1HashMenuItem" Header="Sha1 hash" Click="GenerateHashes"/>
25+
<ui:MenuItem x:Name="Sha256HashMenuItem" Header="Sha256 hash" Click="GenerateHashes"/>
26+
</ui:MenuItem>
27+
<Separator/>
28+
<ui:MenuItem Header="Copy to">
29+
<ui:MenuItem.Icon>
30+
<ui:FontIcon Glyph="&#xE8C3;" />
31+
</ui:MenuItem.Icon>
32+
</ui:MenuItem>
33+
<ui:MenuItem Header="Move to">
34+
<ui:MenuItem.Icon>
35+
<ui:FontIcon Glyph="&#xE8C6;" />
36+
</ui:MenuItem.Icon>
37+
</ui:MenuItem>
38+
<ui:MenuItem Header="New folder with selection">
39+
<ui:MenuItem.Icon>
40+
<ui:FontIcon Glyph="&#xE8DE;" />
41+
</ui:MenuItem.Icon>
42+
</ui:MenuItem>
43+
<Separator/>
44+
<ui:MenuItem Header="Copy path of files sperated by">
45+
<ui:MenuItem Header='";"' Click="CopyPath_Click"/>
46+
<ui:MenuItem Header='" "' Click="CopyPath_Click"/>
47+
<ui:MenuItem Header='","' Click="CopyPath_Click"/>
48+
<ui:MenuItem Header='Newline' Click="CopyPath_Click"/>
49+
<ui:MenuItem Header='Custom...' Click="CopyPath_Click"/>
50+
</ui:MenuItem>
51+
<Separator />
52+
<ui:MenuItem Header="Rename with PowerRename">
53+
<ui:MenuItem.Icon>
54+
<ui:FontIcon Glyph="&#xE8AC;" />
55+
</ui:MenuItem.Icon>
56+
</ui:MenuItem>
57+
<ui:MenuItem Header="Transform images with Image Resizer">
58+
<ui:MenuItem.Icon>
59+
<ui:FontIcon Glyph="&#xEE71;" />
60+
</ui:MenuItem.Icon>
61+
</ui:MenuItem>
62+
<Separator />
63+
<ui:MenuItem Header="Following items only apply to the first-selected file" IsEnabled="False"/>
64+
<ui:MenuItem Tag="SingleItem" Header="What's using this file?">
65+
<ui:MenuItem.Icon>
66+
<ui:FontIcon Glyph="&#xE72E;" />
67+
</ui:MenuItem.Icon>
68+
</ui:MenuItem>
69+
<ui:MenuItem IsEnabled="{Binding SingleItem, RelativeSource={RelativeSource AncestorType=Window}}" Header="Copy image to clipboard">
70+
<ui:MenuItem.Icon>
71+
<ui:FontIcon Glyph="&#xF413;" />
72+
</ui:MenuItem.Icon>
73+
</ui:MenuItem>
74+
<Separator/>
75+
<ui:MenuItem Header="Close Menu"></ui:MenuItem>
76+
</ContextMenu>
77+
</ResourceDictionary>
78+
</ui:FluentWindow.Resources>
79+
</ui:FluentWindow>

0 commit comments

Comments
 (0)