From 9e91e0fd454169a1d59eaf7891ef3077da675d07 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Fri, 5 Nov 2021 16:28:58 +1300 Subject: [PATCH] Now can import images as icons As an extension to what was requested, you can now import any images and use those as the shortcut image (including the desktop image!). This amazing new functionality now makes it possible to make your shortcuts look exactly as you want them! --- DisplayMagician/DisplayMagician.csproj | 12 +- DisplayMagician/ImageUtils.cs | 241 ++---------------- DisplayMagician/Program.cs | 1 + DisplayMagician/Properties/AssemblyInfo.cs | 4 +- DisplayMagician/Resources/x.png | Bin 0 -> 9496 bytes DisplayMagician/UIForms/ChooseIconForm.cs | 106 -------- ...esigner.cs => ChooseImageForm.Designer.cs} | 58 +++-- DisplayMagician/UIForms/ChooseImageForm.cs | 215 ++++++++++++++++ ...ooseIconForm.resx => ChooseImageForm.resx} | 3 + DisplayMagician/UIForms/ShortcutForm.cs | 4 +- DisplayMagician/Utils.cs | 59 +++++ 11 files changed, 350 insertions(+), 353 deletions(-) create mode 100644 DisplayMagician/Resources/x.png delete mode 100644 DisplayMagician/UIForms/ChooseIconForm.cs rename DisplayMagician/UIForms/{ChooseIconForm.Designer.cs => ChooseImageForm.Designer.cs} (82%) create mode 100644 DisplayMagician/UIForms/ChooseImageForm.cs rename DisplayMagician/UIForms/{ChooseIconForm.resx => ChooseImageForm.resx} (99%) create mode 100644 DisplayMagician/Utils.cs diff --git a/DisplayMagician/DisplayMagician.csproj b/DisplayMagician/DisplayMagician.csproj index 2626140..704c96e 100644 --- a/DisplayMagician/DisplayMagician.csproj +++ b/DisplayMagician/DisplayMagician.csproj @@ -151,11 +151,11 @@ LoadingForm.cs - + Form - - ChooseIconForm.cs + + ChooseImageForm.cs Form @@ -195,6 +195,7 @@ StartProgramControl.cs + @@ -235,8 +236,8 @@ MainForm.cs Designer - - ChooseIconForm.cs + + ChooseImageForm.cs ProfileSettingsForm.cs @@ -357,6 +358,7 @@ + diff --git a/DisplayMagician/ImageUtils.cs b/DisplayMagician/ImageUtils.cs index 2849168..739ce57 100644 --- a/DisplayMagician/ImageUtils.cs +++ b/DisplayMagician/ImageUtils.cs @@ -143,225 +143,6 @@ namespace DisplayMagician return newBitmap; } - public static Bitmap GetMeABitmapFromFile(string fileNameAndPath) - { - if (String.IsNullOrWhiteSpace(fileNameAndPath)) - { - logger.Warn($"ShortcutItem/GetMeABitmapFromFile: Bitmap fileNameAndPath is empty! Unable to get the icon from the file."); - return null; - } - - Icon myIcon = null; - Bitmap bm = null; - Bitmap bmToReturn = new Bitmap(1, 1); - - if (fileNameAndPath.EndsWith(".ico")) - { - - try - { - - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: The file we want to get the image from is an icon file. Attempting to extract the icon file from {fileNameAndPath}."); - - - myIcon = new Icon(fileNameAndPath, 256, 256); - //Icon myIcon = Icon.ExtractAssociatedIcon(fileNameAndPath); - bm = myIcon.ToBitmap(); - - - //myIcon = Icon.ExtractAssociatedIcon(fileNameAndPath); - //bm = myIcon.ToBitmap(); - - if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) - { - bmToReturn = bm; - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using standard Icon access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - else - { - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using standard Icon access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - } - catch (Exception ex) - { - logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to extract the icon from a *.ico using Standard Icon tools."); - } - - try - { - MultiIcon myMultiIcon = new MultiIcon(); - SingleIcon mySingleIcon = myMultiIcon.Add("Icon1"); - //mySingleIcon.Load(fileNameAndPath, IconOutputFormat.All); - mySingleIcon.Load(fileNameAndPath); - - foreach (IconImage myIconImage in mySingleIcon) - { - bm = myIconImage.Image; - - if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) - { - bmToReturn = bm; - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using MultiIcon access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - else - { - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using MultiIcon access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - } - } - catch (Exception ex) - { - logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to extract the icon from a *.ico using MultiIcon tools."); - } - } - else - { - - /*try - { - List myIcons = ImageUtils.ExtractIconsFromExe(fileNameAndPath, true); - if (myIcons != null && myIcons.Count > 0) - { - foreach (Icon myExtractedIcon in myIcons) - { - bm = myExtractedIcon.ToBitmap(); - - if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) - { - bmToReturn = bm; - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: This new bitmap from the icon file {fileNameAndPath} is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - } - } - } - catch (Exception ex) - { - logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to extract the icon from an *.exe or *.dll using ImageUtils.ExtractIconsFromExe."); - }*/ - - try - { - var ie = new TsudaKageyu.IconExtractor(fileNameAndPath); - Icon[] allIcons = ie.GetAllIcons(); - foreach (Icon myExtractedIcon in allIcons) - { - bm = myExtractedIcon.ToBitmap(); - - if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) - { - bmToReturn = bm; - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the exe file {fileNameAndPath} using TsudaKageyu.IconExtractor access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - else - { - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the exe file {fileNameAndPath} using TsudaKageyu.IconExtractor access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - } - } - catch (Exception ex) - { - logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to extract the icon from an *.exe or *.dll using TsudaKageyu.IconExtractor."); - } - - } - - try - { - - List myExtractedIcons = MintPlayer.IconUtils.IconExtractor.Split(fileNameAndPath); - Size largeSize = new Size(256, 256); - foreach (Icon myExtractedIcon in myExtractedIcons) - { - - try - { - myIcon = (Icon)IconUtil.TryGetIcon(myExtractedIcon, largeSize, 32, true, true); - } - catch (ArgumentNullException nullex) - { - logger.Debug(nullex, $"ShortcutItem/GetMeABitmapFromFile: There was a faulty icon image within this icon that we couldn't test, so skipping it."); - continue; - } - - if (myIcon != null) - { - bm = myIcon.ToBitmap(); - - if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) - { - bmToReturn = bm; - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the file {fileNameAndPath} using MintPlayer.IconUtils.IconExtractor access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - else - { - logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the file {fileNameAndPath} using MintPlayer.IconUtils.IconExtractor access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead."); - } - } - else - { - logger.Warn($"ShortcutItem/GetMeABitmapFromFile: Couldn't extract an Icon from the file {fileNameAndPath} using MintPlayer.IconUtils.IconExtractor access method, so can't try to get the Icon using IconUtils.TryGetIcon."); - } - } - - } - catch (Exception ex) - { - logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to Split the icon using MintPlayer IconExtractor! "); - } - - - if (bmToReturn == null) - { - // If we couldn't get any bitmaps at all - logger.Warn( $"ShortcutItem/GetMeABitmapFromFile: Haven't managed to get a valid icon file so returning null :(."); - return null; - } - else if (bmToReturn.Width == 1 && bmToReturn.Height == 1) - { - // If we couldn't extract anything, so we return null - logger.Warn($"ShortcutItem/GetMeABitmapFromFile: Haven't managed to get a valid icon file so returning null instead of a 1x1 bitmap!."); - return null; - } - else - { - return bmToReturn; - } - - } - - public static Bitmap GetMeABitmapFromFile(ArrayList fileNamesAndPaths) - { - Bitmap bmToReturn = null; - - - if (fileNamesAndPaths.Count == 0) - { - logger.Warn($"ShortcutItem/GetMeABitmapFromFile2: The fileNamesAndPaths list is empty! Can't get the bitmap from the files."); - return null; - } - logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: We have {fileNamesAndPaths.Count} files to try and extract a bitmap from."); - foreach (string fileNameAndPath in fileNamesAndPaths) - { - logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: Getting a bitmap from {fileNameAndPath} by running GetMeABitmapFromFile."); - Bitmap bm = GetMeABitmapFromFile(fileNameAndPath); - - if (bmToReturn == null) - { - bmToReturn = bm; - } - if (bm != null && bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) - { - bmToReturn = bm; - } - logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: The biggest bitmap we could get from {fileNameAndPath} was {bm.Width}x{bm.Height}."); - } - - // Now we check if the icon is still too small. - logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: The biggest bitmap we could get from the {fileNamesAndPaths.Count} files was {bmToReturn.Width}x{bmToReturn.Height}."); - return bmToReturn; - - } - public static List GetMeAllBitmapsFromFile(string fileNameAndPath) { if (String.IsNullOrWhiteSpace(fileNameAndPath)) @@ -375,7 +156,27 @@ namespace DisplayMagician int bmCount = 0; string fileNameOnly = Path.GetFileName(fileNameAndPath); - if (fileNameAndPath.EndsWith(".ico")) + if (fileNameAndPath.EndsWith(".jpg") || fileNameAndPath.EndsWith(".gif") || fileNameAndPath.EndsWith(".tif") || fileNameAndPath.EndsWith(".png") || fileNameAndPath.EndsWith(".bmp") || + fileNameAndPath.EndsWith(".jpeg") || fileNameAndPath.EndsWith(".tiff")) + { + + try + { + + logger.Trace($"ShortcutItem/GetMeABitmapFromFile: The file we want to get the image from is an image file. Attempting to extract the image from {fileNameAndPath}."); + + Bitmap bmap = new Bitmap(fileNameAndPath); + ShortcutBitmap bm = CreateShortcutBitmap(bmap, fileNameOnly, fileNameAndPath, bmCount++); + // Add the shortcutbitmap to the list + bmList.Add(bm); + logger.Trace($"ShortcutItem/GetMeABitmapFromFile: Added new bitmap from the image file {fileNameAndPath} using standard bitmap decoder access method."); + } + catch (Exception ex) + { + logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to extract the bitmap from an image ({fileNameAndPath})using standard bitmap decoder tools."); + } + } + else if (fileNameAndPath.EndsWith(".ico")) { try diff --git a/DisplayMagician/Program.cs b/DisplayMagician/Program.cs index 1c2cf6b..b0a18b9 100644 --- a/DisplayMagician/Program.cs +++ b/DisplayMagician/Program.cs @@ -37,6 +37,7 @@ namespace DisplayMagician { public static string AppSteamIconFilename = Path.Combine(AppIconPath, @"Steam.ico"); public static string AppUplayIconFilename = Path.Combine(AppIconPath, @"Uplay.ico"); public static string AppEpicIconFilename = Path.Combine(AppIconPath, @"Epic.ico"); + public static string AppDownloadsPath = Utils.GetDownloadsPath(); public static bool AppToastActivated = false; public static bool WaitingForGameToExit = false; public static ProgramSettings AppProgramSettings; diff --git a/DisplayMagician/Properties/AssemblyInfo.cs b/DisplayMagician/Properties/AssemblyInfo.cs index 36c1863..06704a1 100644 --- a/DisplayMagician/Properties/AssemblyInfo.cs +++ b/DisplayMagician/Properties/AssemblyInfo.cs @@ -26,8 +26,8 @@ using System.Resources; [assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")] // Version information -[assembly: AssemblyVersion("2.1.0.145")] -[assembly: AssemblyFileVersion("2.1.0.145")] +[assembly: AssemblyVersion("2.1.0.157")] +[assembly: AssemblyFileVersion("2.1.0.157")] [assembly: NeutralResourcesLanguageAttribute( "en" )] [assembly: CLSCompliant(true)] diff --git a/DisplayMagician/Resources/x.png b/DisplayMagician/Resources/x.png new file mode 100644 index 0000000000000000000000000000000000000000..0f88813d12228a792fb6c6f344e519a3399f8888 GIT binary patch literal 9496 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 AvailableImages - { - get; - set; - } - - public ShortcutBitmap SelectedImage - { - get; - set; - } - - private void btn_back_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.Cancel; - this.Close(); - } - - private void ChooseIconForm_Load(object sender, EventArgs e) - { - if (AvailableImages.Count > 0) - { - bool alreadySelected = false; - // Load all the images into the list - int imageCount = 1; - foreach (ShortcutBitmap sc in AvailableImages) - { - string[] stringsToAdd = new string[] { - $"Image {sc.Order} from {sc.Name}", - $"{sc.Size.Width} x {sc.Size.Height}" - }; - ListViewItem lvi = new ListViewItem(stringsToAdd); - lvi.Name = sc.UUID; - if (sc.Equals(SelectedImage)) - { - lvi.Selected = true; - pb_selected_icon.Image = SelectedImage.Image; - _selectedImage = sc; - } - lv_icons.Items.Add(lvi); - } - - // Select the first largest image listed if there isn't one already - if (lv_icons.SelectedItems.Count == 0) - { - lv_icons.Items[0].Selected = true; - pb_selected_icon.Image = AvailableImages[0].Image; - _selectedImage = AvailableImages[0]; - } - - - - } - } - - private void btn_select_Click(object sender, EventArgs e) - { - SelectedImage = _selectedImage; - DialogResult = DialogResult.OK; - this.Close(); - } - - private void lv_icons_SelectedIndexChanged(object sender, EventArgs e) - { - if (lv_icons.SelectedItems.Count > 0) - { - string uuidToFind = lv_icons.SelectedItems[0].Name; - foreach (ShortcutBitmap sc in AvailableImages) - { - if (sc.UUID.Equals(uuidToFind)) - { - pb_selected_icon.Image = sc.Image; - _selectedImage = sc; - break; - } - } - } - } - - private void btn_add_Click(object sender, EventArgs e) - { - - } - } -} diff --git a/DisplayMagician/UIForms/ChooseIconForm.Designer.cs b/DisplayMagician/UIForms/ChooseImageForm.Designer.cs similarity index 82% rename from DisplayMagician/UIForms/ChooseIconForm.Designer.cs rename to DisplayMagician/UIForms/ChooseImageForm.Designer.cs index c10bbc1..84bf454 100644 --- a/DisplayMagician/UIForms/ChooseIconForm.Designer.cs +++ b/DisplayMagician/UIForms/ChooseImageForm.Designer.cs @@ -1,7 +1,7 @@  namespace DisplayMagician.UIForms { - partial class ChooseIconForm + partial class ChooseImageForm { /// /// Required designer variable. @@ -29,16 +29,18 @@ namespace DisplayMagician.UIForms /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChooseIconForm)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChooseImageForm)); this.lv_icons = new System.Windows.Forms.ListView(); this.columnHeaderName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.pb_selected_icon = new System.Windows.Forms.PictureBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.btn_add = new System.Windows.Forms.Button(); this.btn_select = new System.Windows.Forms.Button(); this.btn_back = new System.Windows.Forms.Button(); - this.columnHeaderSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.dialog_open = new System.Windows.Forms.OpenFileDialog(); + this.btn_remove = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.pb_selected_icon)).BeginInit(); this.SuspendLayout(); // @@ -64,6 +66,11 @@ namespace DisplayMagician.UIForms this.columnHeaderName.Text = "Name"; this.columnHeaderName.Width = 270; // + // columnHeaderSize + // + this.columnHeaderSize.Text = "Size"; + this.columnHeaderSize.Width = 70; + // // pb_selected_icon // this.pb_selected_icon.BackColor = System.Drawing.Color.DimGray; @@ -82,9 +89,9 @@ namespace DisplayMagician.UIForms this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(139, 21); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(125, 16); + this.label1.Size = new System.Drawing.Size(138, 16); this.label1.TabIndex = 2; - this.label1.Text = "Choose icon to use:"; + this.label1.Text = "Choose image to use:"; this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter; // // label2 @@ -95,9 +102,9 @@ namespace DisplayMagician.UIForms this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(457, 21); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(93, 16); + this.label2.Size = new System.Drawing.Size(106, 16); this.label2.TabIndex = 3; - this.label2.Text = "Selected icon:"; + this.label2.Text = "Selected image:"; this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter; // // btn_add @@ -107,9 +114,9 @@ namespace DisplayMagician.UIForms this.btn_add.ForeColor = System.Drawing.Color.White; this.btn_add.Location = new System.Drawing.Point(26, 261); this.btn_add.Name = "btn_add"; - this.btn_add.Size = new System.Drawing.Size(77, 30); + this.btn_add.Size = new System.Drawing.Size(86, 30); this.btn_add.TabIndex = 39; - this.btn_add.Text = "Add icons"; + this.btn_add.Text = "Add images"; this.btn_add.UseVisualStyleBackColor = true; this.btn_add.Click += new System.EventHandler(this.btn_add_Click); // @@ -118,11 +125,11 @@ namespace DisplayMagician.UIForms this.btn_select.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_select.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_select.ForeColor = System.Drawing.Color.White; - this.btn_select.Location = new System.Drawing.Point(218, 261); + this.btn_select.Location = new System.Drawing.Point(305, 261); this.btn_select.Name = "btn_select"; - this.btn_select.Size = new System.Drawing.Size(176, 30); + this.btn_select.Size = new System.Drawing.Size(178, 30); this.btn_select.TabIndex = 40; - this.btn_select.Text = "Save and use selected icon"; + this.btn_select.Text = "Save and use selected image"; this.btn_select.UseVisualStyleBackColor = true; this.btn_select.Click += new System.EventHandler(this.btn_select_Click); // @@ -139,17 +146,30 @@ namespace DisplayMagician.UIForms this.btn_back.UseVisualStyleBackColor = true; this.btn_back.Click += new System.EventHandler(this.btn_back_Click); // - // columnHeaderSize + // dialog_open // - this.columnHeaderSize.Text = "Size"; - this.columnHeaderSize.Width = 70; + this.dialog_open.FileName = "openFileDialog1"; // - // ChooseIconForm + // btn_remove + // + this.btn_remove.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btn_remove.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_remove.ForeColor = System.Drawing.Color.White; + this.btn_remove.Location = new System.Drawing.Point(153, 261); + this.btn_remove.Name = "btn_remove"; + this.btn_remove.Size = new System.Drawing.Size(113, 30); + this.btn_remove.TabIndex = 42; + this.btn_remove.Text = "Remove image"; + this.btn_remove.UseVisualStyleBackColor = true; + this.btn_remove.Click += new System.EventHandler(this.btn_remove_Click); + // + // ChooseImageForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Black; this.ClientSize = new System.Drawing.Size(629, 319); + this.Controls.Add(this.btn_remove); this.Controls.Add(this.btn_back); this.Controls.Add(this.btn_select); this.Controls.Add(this.btn_add); @@ -161,11 +181,11 @@ namespace DisplayMagician.UIForms this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; - this.Name = "ChooseIconForm"; + this.Name = "ChooseImageForm"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Choose shortcut icon"; + this.Text = "Choose shortcut image"; this.TopMost = true; this.Load += new System.EventHandler(this.ChooseIconForm_Load); ((System.ComponentModel.ISupportInitialize)(this.pb_selected_icon)).EndInit(); @@ -185,5 +205,7 @@ namespace DisplayMagician.UIForms private System.Windows.Forms.Button btn_back; private System.Windows.Forms.ColumnHeader columnHeaderName; private System.Windows.Forms.ColumnHeader columnHeaderSize; + private System.Windows.Forms.OpenFileDialog dialog_open; + private System.Windows.Forms.Button btn_remove; } } \ No newline at end of file diff --git a/DisplayMagician/UIForms/ChooseImageForm.cs b/DisplayMagician/UIForms/ChooseImageForm.cs new file mode 100644 index 0000000..554c02d --- /dev/null +++ b/DisplayMagician/UIForms/ChooseImageForm.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace DisplayMagician.UIForms +{ + public partial class ChooseImageForm : Form + { + private ShortcutBitmap _selectedImage = new ShortcutBitmap(); + + public ChooseImageForm() + { + InitializeComponent(); + } + + public List AvailableImages + { + get; + set; + } + + public ShortcutBitmap SelectedImage + { + get; + set; + } + + private void btn_back_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + this.Close(); + } + + private void ChooseIconForm_Load(object sender, EventArgs e) + { + UpdateImageListBox(); + /*if (AvailableImages.Count > 0) + { + bool alreadySelected = false; + // Load all the images into the list + int imageCount = 1; + foreach (ShortcutBitmap sc in AvailableImages) + { + string[] stringsToAdd = new string[] { + $"Image {sc.Order} from {sc.Name}", + $"{sc.Size.Width} x {sc.Size.Height}" + }; + ListViewItem lvi = new ListViewItem(stringsToAdd); + lvi.Name = sc.UUID; + if (sc.Equals(SelectedImage)) + { + lvi.Selected = true; + pb_selected_icon.Image = SelectedImage.Image; + _selectedImage = sc; + } + lv_icons.Items.Add(lvi); + } + + // Select the first largest image listed if there isn't one already + if (lv_icons.SelectedItems.Count == 0) + { + lv_icons.Items[0].Selected = true; + pb_selected_icon.Image = AvailableImages[0].Image; + _selectedImage = AvailableImages[0]; + } + }*/ + } + + private void UpdateImageListBox() + { + lv_icons.Items.Clear(); + if (AvailableImages.Count > 0) + { + bool alreadySelected = false; + // Load all the images into the list + int imageCount = 1; + foreach (ShortcutBitmap sc in AvailableImages) + { + string[] stringsToAdd = new string[] { + $"Image {sc.Order} from {sc.Name}", + $"{sc.Size.Width} x {sc.Size.Height}" + }; + ListViewItem lvi = new ListViewItem(stringsToAdd); + lvi.Name = sc.UUID; + if (sc.Equals(SelectedImage)) + { + lvi.Selected = true; + pb_selected_icon.Image = SelectedImage.Image; + _selectedImage = sc; + } + lv_icons.Items.Add(lvi); + } + + // Select the first largest image listed if there isn't one already + if (lv_icons.SelectedItems.Count == 0) + { + lv_icons.Items[0].Selected = true; + pb_selected_icon.Image = AvailableImages[0].Image; + _selectedImage = AvailableImages[0]; + } + } + } + + + private void btn_select_Click(object sender, EventArgs e) + { + SelectedImage = _selectedImage; + DialogResult = DialogResult.OK; + this.Close(); + } + + private void lv_icons_SelectedIndexChanged(object sender, EventArgs e) + { + if (lv_icons.SelectedItems.Count > 0) + { + string uuidToFind = lv_icons.SelectedItems[0].Name; + foreach (ShortcutBitmap sc in AvailableImages) + { + if (sc.UUID.Equals(uuidToFind)) + { + pb_selected_icon.Image = sc.Image; + _selectedImage = sc; + break; + } + } + } + } + + private void btn_add_Click(object sender, EventArgs e) + { + dialog_open.InitialDirectory = Program.AppDownloadsPath; + dialog_open.DefaultExt = "*.exe; *.com; *.ico; *.bmp; *.jpg; *.png; *.tif; *.gif"; + dialog_open.Filter = "All exe and image files (*.exe; *.com; *.ico; *.bmp; *.jpg; *.png; *.tif; *.gif) | *.exe; *.com; *.ico; *.bmp; *.jpg; *.png; *.tif; *.gif | All files(*.*) | *.*"; + if (dialog_open.ShowDialog(this) == DialogResult.OK) + { + if (File.Exists(dialog_open.FileName)) + { + try + { + List newImages = ImageUtils.GetMeAllBitmapsFromFile(dialog_open.FileName); + if (newImages.Count == 0) + { + MessageBox.Show( + $"No new images found when parsing {dialog_open.FileName} for images. Are you sure it's a valid image format?", + "Add images to icon", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + } + else + { + AvailableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(dialog_open.FileName)); + UpdateImageListBox(); + MessageBox.Show( + $"Added {newImages.Count} image(s) from {dialog_open.FileName} to the end of this image list.", + "Add images to icon", + MessageBoxButtons.OK, + MessageBoxIcon.Information); + + } + dialog_open.FileName = string.Empty; + } + catch(Exception ex) + { + MessageBox.Show( + $"Unable to parse {dialog_open.FileName} for images. Are you sure it's a valid image format?", + "Add images to icon", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + + } + } + else + { + MessageBox.Show( + $"Unable to open {dialog_open.FileName} to parse it for images. Are you sure you have the right file permissions?", + "Add images to icon", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + } + } + } + + private void btn_remove_Click(object sender, EventArgs e) + { + if (lv_icons.SelectedItems.Count > 0) + { + string uuidToFind = lv_icons.SelectedItems[0].Name; + foreach (ShortcutBitmap sc in AvailableImages) + { + if (sc.UUID.Equals(uuidToFind)) + { + AvailableImages.Remove(sc); + pb_selected_icon.Image = null; + _selectedImage = default(ShortcutBitmap); + UpdateImageListBox(); + return; + } + } + // If we get here we didn't find anything to remove! + MessageBox.Show( + $"Unable to remove selected item from the list of images!", + "Remove image from icon", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + } + } + } +} diff --git a/DisplayMagician/UIForms/ChooseIconForm.resx b/DisplayMagician/UIForms/ChooseImageForm.resx similarity index 99% rename from DisplayMagician/UIForms/ChooseIconForm.resx rename to DisplayMagician/UIForms/ChooseImageForm.resx index d05f62e..a766400 100644 --- a/DisplayMagician/UIForms/ChooseIconForm.resx +++ b/DisplayMagician/UIForms/ChooseImageForm.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + diff --git a/DisplayMagician/UIForms/ShortcutForm.cs b/DisplayMagician/UIForms/ShortcutForm.cs index 9a993ad..afa8a74 100644 --- a/DisplayMagician/UIForms/ShortcutForm.cs +++ b/DisplayMagician/UIForms/ShortcutForm.cs @@ -2734,7 +2734,7 @@ namespace DisplayMagician.UIForms { if (rb_standalone.Checked && _availableImages.Count > 0) { - ChooseIconForm exeIconForm = new ChooseIconForm(); + ChooseImageForm exeIconForm = new ChooseImageForm(); exeIconForm.AvailableImages = _availableImages; exeIconForm.SelectedImage = _selectedImage; if (exeIconForm.ShowDialog() == DialogResult.OK) @@ -2751,7 +2751,7 @@ namespace DisplayMagician.UIForms { if (rb_launcher.Checked && _shortcutToEdit.AvailableImages.Count > 0) { - ChooseIconForm gameIconForm = new ChooseIconForm(); + ChooseImageForm gameIconForm = new ChooseImageForm(); gameIconForm.AvailableImages = _availableImages; gameIconForm.SelectedImage = _selectedImage; if (gameIconForm.ShowDialog() == DialogResult.OK) diff --git a/DisplayMagician/Utils.cs b/DisplayMagician/Utils.cs new file mode 100644 index 0000000..5c2c713 --- /dev/null +++ b/DisplayMagician/Utils.cs @@ -0,0 +1,59 @@ +using System; +using System.Runtime.InteropServices; + +namespace DisplayMagician +{ + class Utils + { + // 1. Import InteropServices + + /// 2. Declare DownloadsFolder KNOWNFOLDERID + private static Guid FolderDownloads = new Guid("374DE290-123F-4565-9164-39C4925E467B"); + + /// 3. Import SHGetKnownFolderPath method + /// + /// Retrieves the full path of a known folder identified by the folder's KnownFolderID. + /// + /// A KnownFolderID that identifies the folder. + /// Flags that specify special retrieval options. This value can be + /// 0; otherwise, one or more of the KnownFolderFlag values. + /// An access token that represents a particular user. If this + /// parameter is NULL, which is the most common usage, the function requests the known + /// folder for the current user. Assigning a value of -1 indicates the Default User. + /// The default user profile is duplicated when any new user account is created. + /// Note that access to the Default User folders requires administrator privileges. + /// + /// When this method returns, contains the address of a string that + /// specifies the path of the known folder. The returned path does not include a + /// trailing backslash. + /// Returns S_OK if successful, or an error value otherwise. + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + private static extern int SHGetKnownFolderPath(ref Guid id, int flags, IntPtr token, out IntPtr path); + + /// 4. Declare method that returns the Downloads Path as string + /// + /// Returns the absolute downloads directory specified on the system. + /// + /// + public static string GetDownloadsPath() + { + if (Environment.OSVersion.Version.Major < 6) throw new NotSupportedException(); + + IntPtr pathPtr = IntPtr.Zero; + + try + { + SHGetKnownFolderPath(ref FolderDownloads, 0, IntPtr.Zero, out pathPtr); + return Marshal.PtrToStringUni(pathPtr); + } + finally + { + Marshal.FreeCoTaskMem(pathPtr); + } + } + + } + + + +}