Skip to content

Commit 35e95b5

Browse files
Bura ChuhadarBuraChuhadar
Bura Chuhadar
authored andcommitted
174: First attempt on windows Explorer context menu
1 parent 2740832 commit 35e95b5

11 files changed

+238
-38
lines changed

Files.Launcher/Files.Launcher.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@
8585
<Prefer32Bit>true</Prefer32Bit>
8686
</PropertyGroup>
8787
<ItemGroup>
88+
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
89+
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
90+
</Reference>
8891
<Reference Include="System" />
8992
<Reference Include="System.Core" />
9093
<Reference Include="System.Runtime" />
@@ -107,6 +110,7 @@
107110
</ItemGroup>
108111
<ItemGroup>
109112
<None Include="app.config" />
113+
<None Include="packages.config" />
110114
</ItemGroup>
111115
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
112116
</Project>

Files.Launcher/Program.cs

+51-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
24
using System.ComponentModel;
35
using System.Diagnostics;
46
using System.IO;
@@ -40,44 +42,65 @@ private static void Main(string[] args)
4042
process.StartInfo.Arguments = (string)ApplicationData.Current.LocalSettings.Values["ShellCommand"];
4143
process.Start();
4244
}
43-
else
45+
}
46+
else
47+
{
48+
ExecuteApplications();
49+
50+
}
51+
}
52+
53+
private static void ExecuteApplications(string arguments = null)
54+
{
55+
var executables = (string)ApplicationData.Current.LocalSettings.Values["ApplicationList"];
56+
57+
if (executables != null)
58+
{
59+
var executablesList = JsonConvert.DeserializeObject<IEnumerable<string>>(executables);
60+
foreach (var executable in executablesList)
4461
{
45-
var executable = (string)ApplicationData.Current.LocalSettings.Values["Application"];
46-
Process process = new Process();
47-
process.StartInfo.UseShellExecute = false;
48-
process.StartInfo.FileName = executable;
49-
process.StartInfo.CreateNoWindow = false;
50-
process.StartInfo.Arguments = arguments;
51-
process.Start();
62+
ExecuteApplication(executable, arguments);
5263
}
5364
}
5465
else
5566
{
67+
var executable = (string)ApplicationData.Current.LocalSettings.Values["Application"];
68+
ExecuteApplication(executable, arguments);
69+
}
70+
}
71+
72+
private static void ExecuteApplication(string executable, string arguments = null)
73+
{
74+
try
75+
{
76+
Process process = new Process();
77+
process.StartInfo.UseShellExecute = false;
78+
process.StartInfo.FileName = executable;
79+
process.StartInfo.CreateNoWindow = true;
80+
if (arguments != null)
81+
{
82+
process.StartInfo.Arguments = arguments;
83+
}
84+
process.Start();
85+
}
86+
catch (Win32Exception)
87+
{
88+
Process process = new Process();
89+
process.StartInfo.UseShellExecute = true;
90+
process.StartInfo.Verb = "runas";
91+
process.StartInfo.FileName = executable;
92+
process.StartInfo.CreateNoWindow = true;
93+
if (arguments != null)
94+
{
95+
process.StartInfo.Arguments = arguments;
96+
}
5697
try
5798
{
58-
var executable = (string)ApplicationData.Current.LocalSettings.Values["Application"];
59-
Process process = new Process();
60-
process.StartInfo.UseShellExecute = false;
61-
process.StartInfo.FileName = executable;
62-
process.StartInfo.CreateNoWindow = true;
6399
process.Start();
64100
}
65101
catch (Win32Exception)
66102
{
67-
var executable = (string)ApplicationData.Current.LocalSettings.Values["Application"];
68-
Process process = new Process();
69-
process.StartInfo.UseShellExecute = true;
70-
process.StartInfo.Verb = "runas";
71-
process.StartInfo.FileName = executable;
72-
process.StartInfo.CreateNoWindow = true;
73-
try
74-
{
75-
process.Start();
76-
}
77-
catch (Win32Exception)
78-
{
79-
Process.Start(executable);
80-
}
103+
Process.Start(executable);
81104
}
82105
}
83106
}

Files.Launcher/packages.config

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
4+
</packages>

Files/BaseLayout.cs

+102-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Files.Filesystem;
2+
using Files.Helpers;
23
using Files.Interacts;
34
using Files.View_Models;
45
using Files.Views.Pages;
@@ -24,6 +25,7 @@ namespace Files
2425
public abstract class BaseLayout : Page, INotifyPropertyChanged
2526
{
2627
public bool IsQuickLookEnabled { get; set; } = false;
28+
public MenuFlyout BaseLayoutItemContextFlyout { get; set; }
2729

2830
public ItemViewModel AssociatedViewModel = null;
2931
public Interaction AssociatedInteractions = null;
@@ -123,9 +125,40 @@ public BaseLayout()
123125
}
124126
}
125127

126-
protected abstract void SetSelectedItemOnUi(ListedItem selectedItem);
128+
protected virtual void SetSelectedItemOnUi(ListedItem selectedItem)
129+
{
130+
ClearShellContextMenus();
131+
if (selectedItem != null)
132+
{
133+
134+
var menuFlyoutItems = new RegistryReader().GetExtensionContextMenuForFiles(selectedItem.FileExtension);
135+
LoadMenuFlyoutItem(menuFlyoutItems);
136+
}
127137

128-
protected abstract void SetSelectedItemsOnUi(List<ListedItem> selectedItems);
138+
}
139+
140+
private void ClearShellContextMenus()
141+
{
142+
var contextMenuItems = BaseLayoutItemContextFlyout.Items.Where(c => c.Tag != null && ParseContextMenuTag(c.Tag).commandKey != null);
143+
144+
foreach (var contextMenuItem in contextMenuItems)
145+
{
146+
BaseLayoutItemContextFlyout.Items.Remove(contextMenuItem);
147+
}
148+
}
149+
150+
protected virtual void SetSelectedItemsOnUi(List<ListedItem> selectedItems)
151+
{
152+
ClearShellContextMenus();
153+
if (selectedItems != null)
154+
{
155+
foreach (var selectedItem in selectedItems)
156+
{
157+
var menuFlyoutItems = new RegistryReader().GetExtensionContextMenuForFiles(selectedItem.FileExtension);
158+
LoadMenuFlyoutItem(menuFlyoutItems);
159+
}
160+
}
161+
}
129162

130163
public abstract void FocusSelectedItems();
131164

@@ -206,6 +239,73 @@ private void UnloadMenuFlyoutItemByName(string nameToUnload)
206239
Windows.UI.Xaml.Markup.XamlMarkupHelper.UnloadObject(this.FindName(nameToUnload) as DependencyObject);
207240
}
208241

242+
private void LoadMenuFlyoutItem(IEnumerable<(string commandKey,string commandName, string commandIcon, string command)> menuFlyoutItems)
243+
{
244+
foreach (var menuFlyoutItem in menuFlyoutItems)
245+
{
246+
if (BaseLayoutItemContextFlyout.Items.Any(c => ParseContextMenuTag(c.Tag).commandKey == menuFlyoutItem.commandKey))
247+
{
248+
continue;
249+
}
250+
251+
var menuLayoutItem = new MenuFlyoutItem()
252+
{
253+
Text = menuFlyoutItem.commandName,
254+
Tag = menuFlyoutItem
255+
};
256+
menuLayoutItem.Click += MenuLayoutItem_Click;
257+
258+
BaseLayoutItemContextFlyout.Items.Add(menuLayoutItem);
259+
}
260+
}
261+
262+
private (string commandKey, string commandName, string commandIcon, string command) ParseContextMenuTag(object tag)
263+
{
264+
if(tag is ValueTuple<string, string, string, string>)
265+
{
266+
(string commandKey, string commandName, string commandIcon, string command) = (ValueTuple<string, string, string, string>)tag;
267+
return (commandKey, commandName, commandIcon, command);
268+
}
269+
270+
return (null, null, null, null);
271+
}
272+
273+
private async void MenuLayoutItem_Click(object sender, RoutedEventArgs e)
274+
{
275+
var selectedFileSystemItems = (App.CurrentInstance.ContentPage as BaseLayout).SelectedItems;
276+
var currentMenuLayoutItem = (MenuFlyoutItem)sender;
277+
if (currentMenuLayoutItem != null)
278+
{
279+
var (_, _, _, command) = ParseContextMenuTag(currentMenuLayoutItem.Tag);
280+
if (selectedFileSystemItems.Count > 1)
281+
{
282+
var commandsToExecute = new List<string>();
283+
foreach (var selectedDataItem in selectedFileSystemItems)
284+
{
285+
var commandToExecute = command?.Replace("%1", selectedDataItem.ItemPath);
286+
if (!string.IsNullOrEmpty(commandToExecute))
287+
{
288+
commandsToExecute.Add(commandToExecute);
289+
}
290+
}
291+
if(commandsToExecute.Count > 0)
292+
{
293+
await Interaction.InvokeWin32Components(commandsToExecute);
294+
}
295+
}
296+
else if (selectedFileSystemItems.Count == 1)
297+
{
298+
var selectedDataItem = selectedFileSystemItems[0] as ListedItem;
299+
300+
var commandToExecute = command?.Replace("%1", selectedDataItem.ItemPath);
301+
if (!string.IsNullOrEmpty(commandToExecute))
302+
{
303+
await Interaction.InvokeWin32Component(commandToExecute);
304+
}
305+
}
306+
}
307+
}
308+
209309
public void RightClickContextMenu_Opening(object sender, object e)
210310
{
211311
var selectedFileSystemItems = (App.CurrentInstance.ContentPage as BaseLayout).SelectedItems;

Files/Files.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
<Compile Include="Helpers\NegateConverter.cs" />
158158
<Compile Include="Helpers\ThemeHelper.cs" />
159159
<Compile Include="Program.cs" />
160+
<Compile Include="Helpers\RegistryReader.cs" />
160161
<Compile Include="ResourceController.cs" />
161162
<Compile Include="UserControls\NavigationToolbar\INavigationToolbar.cs" />
162163
<Compile Include="UserControls\NavigationToolbar\ModernNavigationToolbar.xaml.cs">

Files/Helpers/RegistryReader.cs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Microsoft.Win32;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Security.Principal;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Files.Helpers
10+
{
11+
class RegistryReader
12+
{
13+
14+
private void ParseRegistryAndAddToList(List<(string commandKey, string commandName, string commandIcon, string command)> shellList, RegistryKey shellKey)
15+
{
16+
if(shellKey != null)
17+
{
18+
foreach (var keyname in shellKey.GetSubKeyNames())
19+
{
20+
var commandNameKey = shellKey.OpenSubKey(keyname);
21+
var commandName = commandNameKey.GetValue(String.Empty).ToString()?.Replace("&", "");
22+
var commandIcon = commandNameKey.GetValue("Icon").ToString();
23+
24+
var commandNameKeyNames = commandNameKey.GetSubKeyNames();
25+
if (commandNameKeyNames.Contains("command") && !shellList.Any(c => c.commandKey == keyname))
26+
{
27+
var command = commandNameKey.OpenSubKey("command");
28+
shellList.Add((commandKey: keyname, commandName, commandIcon, command: command.GetValue(string.Empty).ToString()));
29+
30+
}
31+
}
32+
}
33+
}
34+
35+
public IEnumerable<(string commanyKey, string commandName, string commandIcon, string command)> GetExtensionContextMenuForFiles(string fileExtension)
36+
{
37+
38+
var shellList = new List<(string commandKey, string commandName, string commandIcon, string command)>();
39+
try
40+
{
41+
using RegistryKey currentUserShellKey = Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell");
42+
ParseRegistryAndAddToList(shellList, currentUserShellKey);
43+
44+
using RegistryKey currentUserFileExtensionShellKey = Registry.CurrentUser.OpenSubKey($"Software\\Classes\\{fileExtension}\\shell");
45+
ParseRegistryAndAddToList(shellList, currentUserFileExtensionShellKey);
46+
47+
48+
return shellList;
49+
}
50+
catch
51+
{
52+
return shellList;
53+
}
54+
}
55+
}
56+
}

Files/Interacts/Interaction.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
using Files.Helpers;
4040
using Windows.UI.Xaml.Data;
4141
using System.Security.Cryptography;
42+
using Newtonsoft.Json;
4243

4344
namespace Files.Interacts
4445
{
@@ -250,11 +251,16 @@ public void GetPath_Click(object sender, RoutedEventArgs e)
250251
}
251252
}
252253

253-
public static async Task InvokeWin32Component(string ApplicationPath)
254+
public static async Task InvokeWin32Component(string applicationPath, string arguments = null)
255+
{
256+
await InvokeWin32Components(new List<string>() { applicationPath }, arguments);
257+
}
258+
259+
public static async Task InvokeWin32Components(List<string> applicationPaths, string arguments = null)
254260
{
255261
Debug.WriteLine("Launching EXE in FullTrustProcess");
256-
ApplicationData.Current.LocalSettings.Values["Application"] = ApplicationPath;
257-
ApplicationData.Current.LocalSettings.Values["Arguments"] = null;
262+
ApplicationData.Current.LocalSettings.Values["ApplicationList"] = JsonConvert.SerializeObject(applicationPaths);
263+
ApplicationData.Current.LocalSettings.Values["Arguments"] = arguments;
258264
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
259265
}
260266

Files/UserControls/LayoutModes/GenericFileBrowser.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
</MenuFlyoutItem>
138138
</MenuFlyout>
139139

140-
<MenuFlyout x:Key="BaseLayoutItemContextFlyout" Opening="RightClickContextMenu_Opening">
140+
<MenuFlyout x:Key="BaseLayoutItemContextFlyout" x:Name="BaseLayoutItemContextFlyout" Opening="RightClickContextMenu_Opening">
141141
<MenuFlyoutItem
142142
x:Name="UnzipItem"
143143
x:Uid="BaseLayoutItemContextFlyoutExtract"

Files/UserControls/LayoutModes/GenericFileBrowser.xaml.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public DataGridColumn SortedColumn
6161
public GenericFileBrowser()
6262
{
6363
this.InitializeComponent();
64-
64+
base.BaseLayoutItemContextFlyout = this.BaseLayoutItemContextFlyout;
6565
switch (App.CurrentInstance.ViewModel.DirectorySortOption)
6666
{
6767
case SortOption.Name:
@@ -107,15 +107,18 @@ protected override void SetSelectedItemOnUi(ListedItem selectedItem)
107107
if (AllView.SelectedItem != selectedItem)
108108
{
109109
AllView.SelectedItem = selectedItem;
110+
base.SetSelectedItemOnUi(selectedItem);
110111
AllView.UpdateLayout();
111112
}
112113
}
113114

114-
protected override void SetSelectedItemsOnUi(List<ListedItem> selectedItems)
115+
protected async override void SetSelectedItemsOnUi(List<ListedItem> selectedItems)
115116
{
116117
// To prevent program from crashing when the page is first loaded
117118
if (selectedItems.Count > 0)
118119
{
120+
121+
base.SetSelectedItemsOnUi(selectedItems);
119122
var rows = new List<DataGridRow>();
120123
Interacts.Interaction.FindChildren<DataGridRow>(rows, AllView);
121124
foreach (DataGridRow row in rows)

Files/UserControls/LayoutModes/PhotoAlbum.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
</MenuFlyoutItem>
133133
</MenuFlyout>
134134

135-
<MenuFlyout x:Key="BaseLayoutItemContextFlyout" Opening="RightClickContextMenu_Opening">
135+
<MenuFlyout x:Key="BaseLayoutItemContextFlyout" x:Name="BaseLayoutItemContextFlyout" Opening="RightClickContextMenu_Opening">
136136
<MenuFlyoutItem
137137
x:Name="UnzipItem"
138138
x:Uid="BaseLayoutItemContextFlyoutExtract"

0 commit comments

Comments
 (0)