Skip to content

Commit 5e2536c

Browse files
authored
Merge branch 'dev' into dependabot/nuget/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Svg.Skia-3.2.1
2 parents 846cc65 + f72e469 commit 5e2536c

File tree

104 files changed

+1042
-721
lines changed

Some content is hidden

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

104 files changed

+1042
-721
lines changed

Flow.Launcher.Core/packages.lock.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@
8383
"resolved": "1.0.0",
8484
"contentHash": "nwbZAYd+DblXAIzlnwDSnl0CiCm8jWLfHSYnoN4wYhtIav6AegB3+T/vKzLbU2IZlPB8Bvl8U3NXpx3eaz+N5w=="
8585
},
86+
"ini-parser": {
87+
"type": "Transitive",
88+
"resolved": "2.5.2",
89+
"contentHash": "hp3gKmC/14+6eKLgv7Jd1Z7OV86lO+tNfOXr/stQbwmRhdQuXVSvrRAuAe7G5+lwhkov0XkqZ8/bn1PYWMx6eg=="
90+
},
8691
"InputSimulator": {
8792
"type": "Transitive",
8893
"resolved": "1.0.4",
@@ -263,7 +268,8 @@
263268
"NLog.OutputDebugString": "[6.0.4, )",
264269
"SharpVectors.Wpf": "[1.8.5, )",
265270
"System.Drawing.Common": "[7.0.0, )",
266-
"ToolGood.Words.Pinyin": "[3.1.0.3, )"
271+
"ToolGood.Words.Pinyin": "[3.1.0.3, )",
272+
"ini-parser": "[2.5.2, )"
267273
}
268274
},
269275
"flow.launcher.plugin": {

Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<PrivateAssets>all</PrivateAssets>
6161
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6262
</PackageReference>
63+
<PackageReference Include="ini-parser" Version="2.5.2" />
6364
<PackageReference Include="InputSimulator" Version="1.0.4" />
6465
<PackageReference Include="MemoryPack" Version="1.21.4" />
6566
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.14.15" />

Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
using System;
2-
using System.Runtime.InteropServices;
32
using System.IO;
3+
using System.Runtime.InteropServices;
44
using System.Windows;
55
using System.Windows.Interop;
66
using System.Windows.Media.Imaging;
7+
using IniParser;
78
using Windows.Win32;
89
using Windows.Win32.Foundation;
9-
using Windows.Win32.UI.Shell;
1010
using Windows.Win32.Graphics.Gdi;
11+
using Windows.Win32.UI.Shell;
1112

1213
namespace Flow.Launcher.Infrastructure.Image
1314
{
@@ -35,9 +36,32 @@ public class WindowsThumbnailProvider
3536

3637
private static readonly HRESULT S_PATHNOTFOUND = (HRESULT)0x8004B205;
3738

39+
private const string UrlExtension = ".url";
40+
41+
/// <summary>
42+
/// Obtains a BitmapSource thumbnail for the specified file.
43+
/// </summary>
44+
/// <remarks>
45+
/// If the file is a Windows URL shortcut (".url"), the method attempts to resolve the shortcut's icon and use that for the thumbnail; otherwise it requests a thumbnail for the file path. The native HBITMAP used to create the BitmapSource is always released to avoid native memory leaks.
46+
/// </remarks>
47+
/// <param name="fileName">Path to the file (can be a regular file or a ".url" shortcut).</param>
48+
/// <param name="width">Requested thumbnail width in pixels.</param>
49+
/// <param name="height">Requested thumbnail height in pixels.</param>
50+
/// <param name="options">Thumbnail extraction options (flags) controlling fallback and caching behavior.</param>
51+
/// <returns>A BitmapSource representing the requested thumbnail.</returns>
3852
public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
3953
{
40-
HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
54+
HBITMAP hBitmap;
55+
56+
var extension = Path.GetExtension(fileName);
57+
if (string.Equals(extension, UrlExtension, StringComparison.OrdinalIgnoreCase))
58+
{
59+
hBitmap = GetHBitmapForUrlFile(fileName, width, height, options);
60+
}
61+
else
62+
{
63+
hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
64+
}
4165

4266
try
4367
{
@@ -50,6 +74,21 @@ public static BitmapSource GetThumbnail(string fileName, int width, int height,
5074
}
5175
}
5276

77+
/// <summary>
78+
/// Obtains a native HBITMAP for the specified file at the requested size using the Windows Shell image factory.
79+
/// </summary>
80+
/// <remarks>
81+
/// If <paramref name="options"/> is <see cref="ThumbnailOptions.ThumbnailOnly"/> and thumbnail extraction fails
82+
/// due to extraction errors or a missing path, the method falls back to requesting an icon (<see cref="ThumbnailOptions.IconOnly"/>).
83+
/// The returned HBITMAP is a raw GDI handle; the caller is responsible for releasing it (e.g., via DeleteObject) to avoid native memory leaks.
84+
/// </remarks>
85+
/// <param name="fileName">Path to the file to thumbnail.</param>
86+
/// <param name="width">Requested thumbnail width in pixels.</param>
87+
/// <param name="height">Requested thumbnail height in pixels.</param>
88+
/// <param name="options">Thumbnail request flags that control behavior (e.g., ThumbnailOnly, IconOnly).</param>
89+
/// <returns>An HBITMAP handle containing the image. Caller must free the handle when finished.</returns>
90+
/// <exception cref="COMException">If creating the shell item fails (HRESULT returned by SHCreateItemFromParsingName).</exception>
91+
/// <exception cref="InvalidOperationException">If the shell item does not expose IShellItemImageFactory or if an unexpected error occurs while obtaining the image.</exception>
5392
private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
5493
{
5594
var retCode = PInvoke.SHCreateItemFromParsingName(
@@ -108,5 +147,44 @@ private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height,
108147

109148
return hBitmap;
110149
}
150+
151+
/// <summary>
152+
/// Obtains an HBITMAP for a Windows .url shortcut by resolving its IconFile entry and delegating to GetHBitmap.
153+
/// </summary>
154+
/// <remarks>
155+
/// The method parses the .url file as an INI, looks in the "InternetShortcut" section for the "IconFile" entry,
156+
/// and requests a bitmap for that icon path. If no IconFile is present or any error occurs while reading or
157+
/// resolving the icon, it falls back to requesting a thumbnail for the .url file itself.
158+
/// </remarks>
159+
/// <param name="fileName">Path to the .url shortcut file.</param>
160+
/// <param name="width">Requested thumbnail width (pixels).</param>
161+
/// <param name="height">Requested thumbnail height (pixels).</param>
162+
/// <param name="options">ThumbnailOptions flags controlling extraction behavior.</param>
163+
/// <returns>An HBITMAP containing the requested image; callers are responsible for freeing the native handle.</returns>
164+
private static unsafe HBITMAP GetHBitmapForUrlFile(string fileName, int width, int height, ThumbnailOptions options)
165+
{
166+
HBITMAP hBitmap;
167+
168+
try
169+
{
170+
var parser = new FileIniDataParser();
171+
var data = parser.ReadFile(fileName);
172+
var urlSection = data["InternetShortcut"];
173+
174+
var iconPath = urlSection?["IconFile"];
175+
if (!File.Exists(iconPath))
176+
{
177+
// If the IconFile is missing, throw exception to fallback to the default icon
178+
throw new FileNotFoundException("Icon file not specified in Internet shortcut (.url) file.");
179+
}
180+
hBitmap = GetHBitmap(Path.GetFullPath(iconPath), width, height, options);
181+
}
182+
catch
183+
{
184+
hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
185+
}
186+
187+
return hBitmap;
188+
}
111189
}
112190
}

Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ public static class DataLocation
77
{
88
public const string PortableFolderName = "UserData";
99
public const string DeletionIndicatorFile = ".dead";
10-
public static string PortableDataPath = Path.Combine(Constant.ProgramDirectory, PortableFolderName);
11-
public static string RoamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
10+
public static readonly string PortableDataPath = Path.Combine(Constant.ProgramDirectory, PortableFolderName);
11+
public static readonly string RoamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
1212
public static string DataDirectory()
1313
{
1414
if (PortableDataLocationInUse())
@@ -19,7 +19,8 @@ public static string DataDirectory()
1919

2020
public static bool PortableDataLocationInUse()
2121
{
22-
if (Directory.Exists(PortableDataPath) && !File.Exists(DeletionIndicatorFile))
22+
if (Directory.Exists(PortableDataPath) &&
23+
!File.Exists(Path.Combine(PortableDataPath, DeletionIndicatorFile)))
2324
return true;
2425

2526
return false;

Flow.Launcher.Infrastructure/packages.lock.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
"resolved": "6.9.3",
3030
"contentHash": "1CUGgFdyECDKgi5HaUBhdv6k+VG9Iy4OCforGfHyar3xQXAJypZkzymgKtWj/4SPd6nSG0Qi7NH71qHrDSZLaA=="
3131
},
32+
"ini-parser": {
33+
"type": "Direct",
34+
"requested": "[2.5.2, )",
35+
"resolved": "2.5.2",
36+
"contentHash": "hp3gKmC/14+6eKLgv7Jd1Z7OV86lO+tNfOXr/stQbwmRhdQuXVSvrRAuAe7G5+lwhkov0XkqZ8/bn1PYWMx6eg=="
37+
},
3238
"InputSimulator": {
3339
"type": "Direct",
3440
"requested": "[1.0.4, )",

Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,16 @@ public static bool FileExists(this string filePath)
150150
return File.Exists(filePath);
151151
}
152152

153+
/// <summary>
154+
/// Checks if a file or directory exists
155+
/// </summary>
156+
/// <param name="path"></param>
157+
/// <returns></returns>
158+
public static bool FileOrLocationExists(this string path)
159+
{
160+
return LocationExists(path) || FileExists(path);
161+
}
162+
153163
/// <summary>
154164
/// Open a directory window (using the OS's default handler, usually explorer)
155165
/// </summary>

Flow.Launcher/Languages/ar.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
225225
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
226226
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
227+
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
227228

228229
<!-- Setting Plugin Store -->
229230
<system:String x:Key="pluginStore">متجر الإضافات</system:String>
@@ -467,8 +468,10 @@
467468
<system:String x:Key="userdatapathButton">فتح المجلد</system:String>
468469
<system:String x:Key="advanced">Advanced</system:String>
469470
<system:String x:Key="logLevel">Log Level</system:String>
470-
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
471+
<system:String x:Key="LogLevelNONE">Silent</system:String>
472+
<system:String x:Key="LogLevelERROR">خطأ</system:String>
471473
<system:String x:Key="LogLevelINFO">Info</system:String>
474+
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
472475
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
473476

474477
<!-- Release Notes Window -->
@@ -490,6 +493,7 @@
490493
<system:String x:Key="fileManager_file_arg">حجة للملف</system:String>
491494
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
492495
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
496+
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
493497

494498
<!-- DefaultBrowser Setting Dialog -->
495499
<system:String x:Key="defaultBrowserTitle">متصفح الويب الافتراضي</system:String>
@@ -500,6 +504,8 @@
500504
<system:String x:Key="defaultBrowser_newWindow">نافذة جديدة</system:String>
501505
<system:String x:Key="defaultBrowser_newTab">تبويب جديد</system:String>
502506
<system:String x:Key="defaultBrowser_parameter">الوضع الخاص</system:String>
507+
<system:String x:Key="defaultBrowser_default">Default</system:String>
508+
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
503509

504510
<!-- Priority Setting Dialog -->
505511
<system:String x:Key="changePriorityWindow">تغيير الأولوية</system:String>

Flow.Launcher/Languages/cs.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
225225
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
226226
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
227+
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
227228

228229
<!-- Setting Plugin Store -->
229230
<system:String x:Key="pluginStore">Obchod s pluginy</system:String>
@@ -467,8 +468,10 @@
467468
<system:String x:Key="userdatapathButton">Open Folder</system:String>
468469
<system:String x:Key="advanced">Advanced</system:String>
469470
<system:String x:Key="logLevel">Log Level</system:String>
470-
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
471+
<system:String x:Key="LogLevelNONE">Silent</system:String>
472+
<system:String x:Key="LogLevelERROR">Chyba</system:String>
471473
<system:String x:Key="LogLevelINFO">Info</system:String>
474+
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
472475
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
473476

474477
<!-- Release Notes Window -->
@@ -490,6 +493,7 @@
490493
<system:String x:Key="fileManager_file_arg">Argumenty pro Soubor</system:String>
491494
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
492495
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
496+
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
493497

494498
<!-- DefaultBrowser Setting Dialog -->
495499
<system:String x:Key="defaultBrowserTitle">Výchozí prohlížeč</system:String>
@@ -500,6 +504,8 @@
500504
<system:String x:Key="defaultBrowser_newWindow">Nové okno</system:String>
501505
<system:String x:Key="defaultBrowser_newTab">Nová karta</system:String>
502506
<system:String x:Key="defaultBrowser_parameter">Soukromý režim</system:String>
507+
<system:String x:Key="defaultBrowser_default">Default</system:String>
508+
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
503509

504510
<!-- Priority Setting Dialog -->
505511
<system:String x:Key="changePriorityWindow">Změnit prioritu</system:String>

Flow.Launcher/Languages/da.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
225225
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
226226
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
227+
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
227228

228229
<!-- Setting Plugin Store -->
229230
<system:String x:Key="pluginStore">Plugin-butik</system:String>
@@ -467,8 +468,10 @@
467468
<system:String x:Key="userdatapathButton">Open Folder</system:String>
468469
<system:String x:Key="advanced">Advanced</system:String>
469470
<system:String x:Key="logLevel">Log Level</system:String>
470-
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
471+
<system:String x:Key="LogLevelNONE">Silent</system:String>
472+
<system:String x:Key="LogLevelERROR">Error</system:String>
471473
<system:String x:Key="LogLevelINFO">Info</system:String>
474+
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
472475
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
473476

474477
<!-- Release Notes Window -->
@@ -490,6 +493,7 @@
490493
<system:String x:Key="fileManager_file_arg">Arg for fil</system:String>
491494
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
492495
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
496+
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
493497

494498
<!-- DefaultBrowser Setting Dialog -->
495499
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
@@ -500,6 +504,8 @@
500504
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
501505
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
502506
<system:String x:Key="defaultBrowser_parameter">Privattilstand</system:String>
507+
<system:String x:Key="defaultBrowser_default">Default</system:String>
508+
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
503509

504510
<!-- Priority Setting Dialog -->
505511
<system:String x:Key="changePriorityWindow">Skift prioritet</system:String>

Flow.Launcher/Languages/de.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
225225
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
226226
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
227+
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
227228

228229
<!-- Setting Plugin Store -->
229230
<system:String x:Key="pluginStore">Plug-in-Store</system:String>
@@ -467,8 +468,10 @@
467468
<system:String x:Key="userdatapathButton">Ordner öffnen</system:String>
468469
<system:String x:Key="advanced">Erweitert</system:String>
469470
<system:String x:Key="logLevel">Log-Ebene</system:String>
470-
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
471+
<system:String x:Key="LogLevelNONE">Silent</system:String>
472+
<system:String x:Key="LogLevelERROR">Fehler</system:String>
471473
<system:String x:Key="LogLevelINFO">Info</system:String>
474+
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
472475
<system:String x:Key="settingWindowFontTitle">Einstellung der Fensterschriftart</system:String>
473476

474477
<!-- Release Notes Window -->
@@ -490,6 +493,7 @@
490493
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
491494
<system:String x:Key="fileManagerPathNotFound">Der Dateimanager '{0}' konnte nicht unter '{1}' gefunden werden. Möchten Sie fortfahren?</system:String>
492495
<system:String x:Key="fileManagerPathError">Pfadfehler bei Dateimanager</system:String>
496+
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
493497

494498
<!-- DefaultBrowser Setting Dialog -->
495499
<system:String x:Key="defaultBrowserTitle">Webbrowser per Default</system:String>
@@ -500,6 +504,8 @@
500504
<system:String x:Key="defaultBrowser_newWindow">Neues Fenster</system:String>
501505
<system:String x:Key="defaultBrowser_newTab">Neuer Tab</system:String>
502506
<system:String x:Key="defaultBrowser_parameter">Privater Modus</system:String>
507+
<system:String x:Key="defaultBrowser_default">Default</system:String>
508+
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
503509

504510
<!-- Priority Setting Dialog -->
505511
<system:String x:Key="changePriorityWindow">Priorität ändern</system:String>

0 commit comments

Comments
 (0)