diff --git a/src/modules/Projects/Projects.sln b/src/modules/Projects/Projects.sln index 347249c62b..19ae1db479 100644 --- a/src/modules/Projects/Projects.sln +++ b/src/modules/Projects/Projects.sln @@ -21,52 +21,120 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectsLauncher", "Project EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectsEditor", "ProjectsEditor\ProjectsEditor.csproj", "{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCommon", "..\..\common\ManagedCommon\ManagedCommon.csproj", "{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToys.Interop", "..\..\common\interop\PowerToys.Interop.vcxproj", "{F055103B-F80B-4D0C-BF48-057C55620033}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedTelemetry", "..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj", "{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|Any CPU.ActiveCfg = Debug|x64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|Any CPU.Build.0 = Debug|x64 + {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|ARM64.Build.0 = Debug|ARM64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x64.ActiveCfg = Debug|x64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x64.Build.0 = Debug|x64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x86.ActiveCfg = Debug|Win32 {3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x86.Build.0 = Debug|Win32 {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|Any CPU.ActiveCfg = Release|x64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|Any CPU.Build.0 = Release|x64 + {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|ARM64.ActiveCfg = Release|ARM64 + {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|ARM64.Build.0 = Release|ARM64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x64.ActiveCfg = Release|x64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x64.Build.0 = Release|x64 {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x86.ActiveCfg = Release|Win32 {3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x86.Build.0 = Release|Win32 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|Any CPU.ActiveCfg = Debug|x64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|Any CPU.Build.0 = Debug|x64 + {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|ARM64.Build.0 = Debug|ARM64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x64.ActiveCfg = Debug|x64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x64.Build.0 = Debug|x64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x86.ActiveCfg = Debug|Win32 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x86.Build.0 = Debug|Win32 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|Any CPU.ActiveCfg = Release|x64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|Any CPU.Build.0 = Release|x64 + {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|ARM64.ActiveCfg = Release|ARM64 + {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|ARM64.Build.0 = Release|ARM64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x64.ActiveCfg = Release|x64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x64.Build.0 = Release|x64 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x86.ActiveCfg = Release|Win32 {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x86.Build.0 = Release|Win32 {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM64.Build.0 = Debug|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.ActiveCfg = Debug|x64 {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.Build.0 = Debug|x64 {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.ActiveCfg = Debug|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.Build.0 = Debug|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.ActiveCfg = Release|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Release|Any CPU + {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM64.ActiveCfg = Release|Any CPU + {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM64.Build.0 = Release|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.ActiveCfg = Release|x64 {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.Build.0 = Release|x64 {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.ActiveCfg = Release|Any CPU {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.Build.0 = Release|Any CPU + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|Any CPU.ActiveCfg = Debug|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|Any CPU.Build.0 = Debug|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|ARM64.Build.0 = Debug|ARM64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x64.ActiveCfg = Debug|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x64.Build.0 = Debug|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x86.ActiveCfg = Debug|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x86.Build.0 = Debug|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|Any CPU.ActiveCfg = Release|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|Any CPU.Build.0 = Release|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|ARM64.ActiveCfg = Release|ARM64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|ARM64.Build.0 = Release|ARM64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x64.ActiveCfg = Release|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x64.Build.0 = Release|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x86.ActiveCfg = Release|x64 + {A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x86.Build.0 = Release|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|Any CPU.ActiveCfg = Debug|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|Any CPU.Build.0 = Debug|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|ARM64.Build.0 = Debug|ARM64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.ActiveCfg = Debug|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.Build.0 = Debug|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x86.ActiveCfg = Debug|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x86.Build.0 = Debug|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|Any CPU.ActiveCfg = Release|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|Any CPU.Build.0 = Release|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|ARM64.ActiveCfg = Release|ARM64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|ARM64.Build.0 = Release|ARM64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.ActiveCfg = Release|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64 + {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|Any CPU.ActiveCfg = Debug|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|Any CPU.Build.0 = Debug|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|ARM64.Build.0 = Debug|ARM64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x64.ActiveCfg = Debug|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x64.Build.0 = Debug|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x86.ActiveCfg = Debug|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x86.Build.0 = Debug|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|Any CPU.ActiveCfg = Release|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|Any CPU.Build.0 = Release|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|ARM64.ActiveCfg = Release|ARM64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|ARM64.Build.0 = Release|ARM64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x64.ActiveCfg = Release|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x64.Build.0 = Release|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x86.ActiveCfg = Release|x64 + {6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/modules/Projects/ProjectsEditor/App.xaml.cs b/src/modules/Projects/ProjectsEditor/App.xaml.cs index a4d1c608e2..568893ad0a 100644 --- a/src/modules/Projects/ProjectsEditor/App.xaml.cs +++ b/src/modules/Projects/ProjectsEditor/App.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Windows; +using ManagedCommon; using ProjectsEditor.Common; using ProjectsEditor.Utils; using ProjectsEditor.ViewModels; @@ -34,6 +35,8 @@ namespace ProjectsEditor { AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; + Logger.InitializeLogger("\\Projects\\Logs"); + _themeManager = new ThemeManager(this); if (_mainViewModel == null) @@ -46,6 +49,7 @@ namespace ProjectsEditor string[] args = Environment.GetCommandLineArgs(); if (args != null && args.Length > 1) { + Logger.LogInfo($"Started with a parameter: {args[1]}. Trying to launch that project."); _mainViewModel.LaunchProject(args[1]); return; } diff --git a/src/modules/Projects/ProjectsEditor/Models/Application.cs b/src/modules/Projects/ProjectsEditor/Models/Application.cs index fb409a3b8e..fec8e20b94 100644 --- a/src/modules/Projects/ProjectsEditor/Models/Application.cs +++ b/src/modules/Projects/ProjectsEditor/Models/Application.cs @@ -12,7 +12,11 @@ using System.IO; using System.Linq; using System.Text.Json.Serialization; using System.Text.RegularExpressions; +using System.Threading.Tasks; using System.Windows.Media.Imaging; +using ManagedCommon; +using Windows.ApplicationModel.Core; +using Windows.Management.Deployment; namespace ProjectsEditor.Models { @@ -77,10 +81,22 @@ namespace ProjectsEditor.Models { try { - _icon = Icon.ExtractAssociatedIcon(AppPath); + if (!File.Exists(AppPath) && IsPackagedApp) + { + Task task = Task.Run(async () => await GetAppByPackageFamilyNameAsync()); + AppListEntry packApp = task.Result; + string filename = Path.GetFileName(AppPath); + string newExeLocation = Path.Combine(packApp.AppInfo.Package.InstalledPath, filename); + _icon = Icon.ExtractAssociatedIcon(newExeLocation); + } + else + { + _icon = Icon.ExtractAssociatedIcon(AppPath); + } } - catch (Exception) + catch (Exception e) { + Logger.LogError($"Exception while extracting icon from app path: {AppPath}. Exception message: {e.Message}"); _icon = new Icon(@"images\DefaultIcon.ico"); } } @@ -89,6 +105,31 @@ namespace ProjectsEditor.Models } } + public async Task GetAppByPackageFamilyNameAsync() + { + var pkgManager = new PackageManager(); + var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault(); + + if (pkg == null) + { + return null; + } + + var apps = await pkg.GetAppListEntriesAsync(); + if (apps == null || apps.Count == 0) + { + return null; + } + + AppListEntry firstApp = apps[0]; + + // RandomAccessStreamReference stream = firstApp.AppInfo.DisplayInfo.GetLogo(new Windows.Foundation.Size(64, 64)); + // IRandomAccessStreamWithContentType content = await stream.OpenReadAsync(); + // BitmapImage bitmapImage = new BitmapImage(); + // bitmapImage.StreamSource = (Stream)content; + return firstApp; + } + private BitmapImage _iconBitmapImage; public BitmapImage IconBitmapImage @@ -124,9 +165,9 @@ namespace ProjectsEditor.Models _iconBitmapImage = bitmapImage; } } - catch (Exception) + catch (Exception e) { - // todo + Logger.LogError($"Exception while drawing icon for app with path: {AppPath}. Exception message: {e.Message}"); } } diff --git a/src/modules/Projects/ProjectsEditor/Models/Project.cs b/src/modules/Projects/ProjectsEditor/Models/Project.cs index 937527b579..81f167988d 100644 --- a/src/modules/Projects/ProjectsEditor/Models/Project.cs +++ b/src/modules/Projects/ProjectsEditor/Models/Project.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Windows.Media.Imaging; +using ManagedCommon; using ProjectsEditor.Utils; namespace ProjectsEditor.Models @@ -275,8 +276,9 @@ namespace ProjectsEditor.Models { graphics.DrawIcon(app.Icon, new Rectangle(32 * appIndex, 0, 24, 24)); } - catch (Exception) + catch (Exception e) { + Logger.LogError($"Exception while drawing the icon for app {Name}. Exception message: {e.Message}"); } appIndex++; @@ -315,5 +317,27 @@ namespace ProjectsEditor.Models return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); } + + internal string GetShortcutChars() + { + if (string.IsNullOrEmpty(Name)) + { + return "PR"; + } + + string[] words = Name.Trim().ToUpperInvariant().Split(' '); + if (words.Length > 2) + { + return $"{words[0][0]}{words[1][0]}{words[2][0]}"; + } + else if (words.Length == 2) + { + return $"{words[0][0]}{words[1][0]}"; + } + else + { + return words[0].Substring(0, Math.Min(3, words[0].Length)); + } + } } } diff --git a/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml b/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml index 9c8e071492..675fcc232a 100644 --- a/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml +++ b/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml @@ -161,6 +161,7 @@ + @@ -190,55 +191,56 @@ GotFocus="EditNameTextBox_GotFocus" KeyDown="EditNameTextBoxKeyDown" /> + + + + + + + + + + + + + + + + + + + + + + + + + + Grid.Row="3"> - - - - - - - - - - - - - - - - - - - - - - - - - @@ -251,7 +253,7 @@ - + diff --git a/src/modules/Projects/ProjectsEditor/ProjectsEditor.csproj b/src/modules/Projects/ProjectsEditor/ProjectsEditor.csproj index 8fe07bbcd5..1f84035f4b 100644 --- a/src/modules/Projects/ProjectsEditor/ProjectsEditor.csproj +++ b/src/modules/Projects/ProjectsEditor/ProjectsEditor.csproj @@ -84,6 +84,12 @@ + + + + + + True diff --git a/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs b/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs index 7dd3b6f529..fc42b44908 100644 --- a/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs +++ b/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs @@ -14,12 +14,43 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media.Imaging; +using System.Windows.Media.Media3D; +using ModernWpf.Media.Animation; using ProjectsEditor.Models; namespace ProjectsEditor.Utils { public class DrawHelper { + private const int IconSize = 128; + private static Font font = new("Tahoma", 24); + private static List iconBrushes = new List + { + ////Brushes.Gold, + ////Brushes.SteelBlue, + ////Brushes.SkyBlue, + ////Brushes.DarkGoldenrod, + ////Brushes.ForestGreen, + ////Brushes.Peru, + ////Brushes.Chartreuse, + ////Brushes.LightPink, + ////Brushes.CadetBlue, + ////Brushes.DarkSalmon, + ////Brushes.Orange, + ////Brushes.DarkSeaGreen, + ////Brushes.Yellow, + ////Brushes.Green, + ////Brushes.Orange, + ////Brushes.White, + new SolidBrush(Color.FromArgb(255, 40, 101, 120)), + new SolidBrush(Color.FromArgb(255, 58, 91, 153)), + new SolidBrush(Color.FromArgb(255, 87, 88, 163)), + new SolidBrush(Color.FromArgb(255, 116, 87, 160)), + new SolidBrush(Color.FromArgb(255, 139, 82, 145)), + }; + + private static int iconBrushIndex; + public static BitmapImage DrawPreview(Project project, Rectangle bounds) { double scale = 0.1; @@ -132,7 +163,6 @@ namespace ProjectsEditor.Utils if (app.RepeatIndex > 0) { string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture); - System.Drawing.Font font = new System.Drawing.Font("Tahoma", 8); int indexSize = (int)(iconBounds.Width * 0.5); Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize); @@ -194,7 +224,6 @@ namespace ProjectsEditor.Utils if (app.RepeatIndex > 0) { string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture); - System.Drawing.Font font = new System.Drawing.Font("Tahoma", 8); int indexSize = (int)(iconBounds.Width * 0.5); Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize); @@ -247,5 +276,169 @@ namespace ProjectsEditor.Utils path.CloseFigure(); return path; } + + internal static string CreateShortcutIcon(Project project, out Bitmap bitmap) + { + object shDesktop = (object)"Desktop"; + IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); + string shortcutIconFilename = (string)shell.SpecialFolders.Item(ref shDesktop) + $"\\{project.Name}.ico"; + bitmap = new Bitmap(IconSize, IconSize); + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + // if (project != null) + // { + // List selectedApps = project.Applications.Where(x => x.IsSelected).ToList(); + // if (selectedApps.Count > 0) + // { + // graphics.DrawIcon(selectedApps[0].Icon, new Rectangle(0, 0, IconSize / 2, IconSize / 2)); + // } + // if (selectedApps.Count > 1) + // { + // graphics.DrawIcon(selectedApps[1].Icon, new Rectangle(IconSize / 2, 0, IconSize / 2, IconSize / 2)); + // } + // if (selectedApps.Count > 2) + // { + // graphics.DrawIcon(selectedApps[2].Icon, new Rectangle(0, IconSize / 2, IconSize / 2, IconSize / 2)); + // } + // if (selectedApps.Count > 3) + // { + // graphics.DrawIcon(selectedApps[3].Icon, new Rectangle(IconSize / 2, IconSize / 2, IconSize / 2, IconSize / 2)); + // } + // } + // graphics.FillRectangle(new System.Drawing.SolidBrush(Color.FromArgb(128, 32, 32, 32)), 0, 0, IconSize, IconSize); + graphics.FillEllipse(iconBrushes[iconBrushIndex], 0, 0, IconSize, IconSize); + + string shortcutChars = "PR"; + + if (project != null) + { + shortcutChars = project.GetShortcutChars(); + } + + Rectangle indexBounds; + if (shortcutChars.Length > 1) + { + indexBounds = new Rectangle(0, 0, IconSize, IconSize); + } + else + { + indexBounds = new Rectangle(IconSize / 4, 0, IconSize / 2, IconSize); + } + + var textSize = graphics.MeasureString(shortcutChars, font); + var state = graphics.Save(); + graphics.TranslateTransform(indexBounds.Left, indexBounds.Top); + graphics.ScaleTransform(indexBounds.Width / textSize.Width, indexBounds.Height / textSize.Height); + graphics.DrawString(shortcutChars, font, Brushes.White, PointF.Empty); + graphics.Restore(state); + iconBrushIndex++; + if (iconBrushIndex >= iconBrushes.Count) + { + iconBrushIndex = 0; + } + } + + FileStream fileStream = new FileStream(shortcutIconFilename, FileMode.OpenOrCreate); + + using (var memoryStream = new MemoryStream()) + { + bitmap.Save(memoryStream, ImageFormat.Png); + + BinaryWriter iconWriter = new BinaryWriter(fileStream); + if (fileStream != null && iconWriter != null) + { + // 0-1 reserved, 0 + iconWriter.Write((byte)0); + iconWriter.Write((byte)0); + + // 2-3 image type, 1 = icon, 2 = cursor + iconWriter.Write((short)1); + + // 4-5 number of images + iconWriter.Write((short)1); + + // image entry 1 + // 0 image width + iconWriter.Write((byte)IconSize); + + // 1 image height + iconWriter.Write((byte)IconSize); + + // 2 number of colors + iconWriter.Write((byte)0); + + // 3 reserved + iconWriter.Write((byte)0); + + // 4-5 color planes + iconWriter.Write((short)0); + + // 6-7 bits per pixel + iconWriter.Write((short)32); + + // 8-11 size of image data + iconWriter.Write((int)memoryStream.Length); + + // 12-15 offset of image data + iconWriter.Write((int)(6 + 16)); + + // write image data + // png data must contain the whole png data file + iconWriter.Write(memoryStream.ToArray()); + + iconWriter.Flush(); + } + } + + fileStream.Flush(); + fileStream.Close(); + return shortcutIconFilename; + } + + private static void CreateExamples(Project project) + { + Bitmap bitmap = new Bitmap(IconSize + 1000, IconSize * iconBrushes.Count); + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + for (int brushIndex = 0; brushIndex < iconBrushes.Count; brushIndex++) + { + graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + graphics.FillEllipse(iconBrushes[brushIndex], 0, IconSize * brushIndex, IconSize, IconSize); + + string shortcutChars = "PR"; + + Rectangle indexBounds; + indexBounds = new Rectangle(0, IconSize * brushIndex, IconSize, IconSize); + + var textSize = graphics.MeasureString(shortcutChars, font); + var state = graphics.Save(); + graphics.TranslateTransform(indexBounds.Left, indexBounds.Top); + graphics.ScaleTransform(indexBounds.Width / textSize.Width, indexBounds.Height / textSize.Height); + graphics.DrawString(shortcutChars, font, Brushes.Black, 0, 0); + graphics.Restore(state); + + var b = (SolidBrush)iconBrushes[brushIndex]; + var colorName = (from p in typeof(System.Drawing.Color).GetProperties() + where p.PropertyType.Equals(typeof(System.Drawing.Color)) + let value = (System.Drawing.Color)p.GetValue(null, null) + where value.R == b.Color.R && + value.G == b.Color.G && + value.B == b.Color.B && + value.A == b.Color.A + select p.Name).DefaultIfEmpty("unknown").First(); + + graphics.DrawString(colorName, font, Brushes.White, IconSize, IconSize * brushIndex); + } + } + + bitmap.Save(@"C:\temp\shortcutIcons.png"); + } } } diff --git a/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs b/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs index 8aee9b8f27..b90c7ccdb8 100644 --- a/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs +++ b/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Windows; +using ManagedCommon; using ProjectsEditor.Data; using ProjectsEditor.Models; using ProjectsEditor.ViewModels; @@ -25,12 +26,14 @@ namespace ProjectsEditor.Utils ProjectsData parser = new ProjectsData(); if (!File.Exists(parser.File)) { + Logger.LogWarning($"Projects storage file not found: {parser.File}"); return new ParsingResult(true); } ProjectsData.ProjectsListWrapper projects = parser.Read(parser.File); if (!SetProjects(mainViewModel, projects)) { + Logger.LogWarning($"Projects storage file content could not be set. Reason: {Properties.Resources.Error_Parsing_Message}"); return new ParsingResult(false, ProjectsEditor.Properties.Resources.Error_Parsing_Message); } @@ -38,6 +41,7 @@ namespace ProjectsEditor.Utils } catch (Exception e) { + Logger.LogError($"Exception while parsing storage file: {e.Message}"); return new ParsingResult(false, e.Message); } } @@ -50,12 +54,14 @@ namespace ProjectsEditor.Utils ProjectsData parser = new ProjectsData(); if (!File.Exists(fileName)) { + Logger.LogWarning($"ParseProject method. Projects storage file not found: {parser.File}"); return new ParsingResult(true); } ProjectsData.ProjectsListWrapper projects = parser.Read(fileName); if (!ExtractProject(projects, out project)) { + Logger.LogWarning($"ParseProject method. Projects storage file content could not be set. Reason: {Properties.Resources.Error_Parsing_Message}"); return new ParsingResult(false, ProjectsEditor.Properties.Resources.Error_Parsing_Message); } @@ -63,6 +69,7 @@ namespace ProjectsEditor.Utils } catch (Exception e) { + Logger.LogError($"ParseProject method. Exception while parsing storage file: {e.Message}"); return new ParsingResult(false, e.Message); } } @@ -209,9 +216,10 @@ namespace ProjectsEditor.Utils IOUtils ioUtils = new IOUtils(); ioUtils.WriteFile(serializer.File, serializer.Serialize(projectsWrapper)); } - catch (Exception) + catch (Exception e) { // TODO: show error + Logger.LogError($"Exception while writing storage file: {e.Message}"); } } diff --git a/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs b/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs index 15a7b99fbe..17713257b3 100644 --- a/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs +++ b/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs @@ -7,10 +7,14 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Timers; +using System.Windows.Media.Imaging; +using ManagedCommon; using ProjectsEditor.Models; using ProjectsEditor.Utils; using static ProjectsEditor.Data.ProjectsData; @@ -130,17 +134,19 @@ namespace ProjectsEditor.ViewModels } } - private void CreateShortcut(Project editedProject) + private void CreateShortcut(Project project) { object shDesktop = (object)"Desktop"; IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); - string shortcutAddress = (string)shell.SpecialFolders.Item(ref shDesktop) + $"\\{editedProject.Name}.lnk"; + string shortcutAddress = (string)shell.SpecialFolders.Item(ref shDesktop) + $"\\{project.Name}.lnk"; IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(shortcutAddress); - shortcut.Description = $"Project Launcher {editedProject.Id}"; + shortcut.Description = $"Project Launcher {project.Id}"; string basePath = AppDomain.CurrentDomain.BaseDirectory; - shortcut.TargetPath = Path.Combine(basePath, "ProjectsEditor.exe"); - shortcut.Arguments = '"' + editedProject.Id + '"'; + shortcut.TargetPath = Path.Combine(basePath, "ProjectsLauncher.exe"); + shortcut.Arguments = '"' + project.Id + '"'; shortcut.WorkingDirectory = basePath; + string iconFilename = DrawHelper.CreateShortcutIcon(project, out Bitmap bitmap); + shortcut.IconLocation = iconFilename; shortcut.Save(); } @@ -221,6 +227,7 @@ namespace ProjectsEditor.ViewModels { if (!Projects.Where(x => x.Id == projectId).Any()) { + Logger.LogWarning($"Project to launch not find. Id: {projectId}"); return; } @@ -237,6 +244,7 @@ namespace ProjectsEditor.ViewModels if (exitAfterLaunch) { + Logger.LogInfo($"Launched the project {project.Name}. Exiting."); Environment.Exit(0); } } diff --git a/src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.cpp b/src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.cpp index d717dc14a5..06b25fd7b9 100644 --- a/src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.cpp +++ b/src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.cpp @@ -144,19 +144,22 @@ namespace Utils }; } - std::wstring installPathUpper(appData.installPath); - std::transform(installPathUpper.begin(), installPathUpper.end(), installPathUpper.begin(), towupper); - - if (appPathUpper.contains(installPathUpper)) + if (!appData.installPath.empty()) { - return appData; - } + std::wstring installPathUpper(appData.installPath); + std::transform(installPathUpper.begin(), installPathUpper.end(), installPathUpper.begin(), towupper); - // edge case, some apps (e.g., Gitkraken) have different .exe files in the subfolders. - // apps list contains only one path, so in this case app is not found - if (std::filesystem::path(appPath).filename() == std::filesystem::path(appData.installPath).filename()) - { - return appData; + if (appPathUpper.contains(installPathUpper)) + { + return appData; + } + + // edge case, some apps (e.g., Gitkraken) have different .exe files in the subfolders. + // apps list contains only one path, so in this case app is not found + if (std::filesystem::path(appPath).filename() == std::filesystem::path(appData.installPath).filename()) + { + return appData; + } } } diff --git a/src/modules/Projects/ProjectsSnapshotTool/main.cpp b/src/modules/Projects/ProjectsSnapshotTool/main.cpp index 029a9dbae0..7806ff2ff5 100644 --- a/src/modules/Projects/ProjectsSnapshotTool/main.cpp +++ b/src/modules/Projects/ProjectsSnapshotTool/main.cpp @@ -124,7 +124,7 @@ int main(int argc, char* argv[]) Project::Application app { .name = data.value().name, .title = title, - .path = data.value().installPath, + .path = processPath, .packageFullName = data.value().packageFullName, .commandLineArgs = L"", .isMinimized = WindowUtils::IsMinimized(window),