From 49175a564e8dee1bde66ea6c0271f3883d57c7af Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Sat, 8 Feb 2025 05:28:00 +0300 Subject: [PATCH 1/2] Editor: implement "Replace source paths" operation for sprites --- Editor/AGS.Editor/AGSEditor.csproj | 9 + .../GUI/ReplaceFolderDialog.Designer.cs | 165 ++++++++++++++++++ Editor/AGS.Editor/GUI/ReplaceFolderDialog.cs | 91 ++++++++++ .../AGS.Editor/GUI/ReplaceFolderDialog.resx | 120 +++++++++++++ Editor/AGS.Editor/Panes/SpriteSelector.cs | 67 +++++++ Editor/AGS.Editor/Utils/Utilities.cs | 62 ++++++- 6 files changed, 510 insertions(+), 4 deletions(-) create mode 100644 Editor/AGS.Editor/GUI/ReplaceFolderDialog.Designer.cs create mode 100644 Editor/AGS.Editor/GUI/ReplaceFolderDialog.cs create mode 100644 Editor/AGS.Editor/GUI/ReplaceFolderDialog.resx diff --git a/Editor/AGS.Editor/AGSEditor.csproj b/Editor/AGS.Editor/AGSEditor.csproj index 5cc787e8eb4..d0b1fe3dee7 100644 --- a/Editor/AGS.Editor/AGSEditor.csproj +++ b/Editor/AGS.Editor/AGSEditor.csproj @@ -232,6 +232,12 @@ Progress.cs + + Form + + + ReplaceFolderDialog.cs + Form @@ -472,6 +478,9 @@ Progress.cs + + ReplaceFolderDialog.cs + SpriteExportDialog.cs diff --git a/Editor/AGS.Editor/GUI/ReplaceFolderDialog.Designer.cs b/Editor/AGS.Editor/GUI/ReplaceFolderDialog.Designer.cs new file mode 100644 index 00000000000..48ad9e093e7 --- /dev/null +++ b/Editor/AGS.Editor/GUI/ReplaceFolderDialog.Designer.cs @@ -0,0 +1,165 @@ + +namespace AGS.Editor +{ + partial class ReplaceFolderDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblOperationDescription = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.tbOldPath = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.tbNewPath = new System.Windows.Forms.TextBox(); + this.btnBrowseOldPath = new System.Windows.Forms.Button(); + this.btnBrowseNewPath = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lblOperationDescription + // + this.lblOperationDescription.Location = new System.Drawing.Point(13, 13); + this.lblOperationDescription.Name = "lblOperationDescription"; + this.lblOperationDescription.Size = new System.Drawing.Size(392, 52); + this.lblOperationDescription.TabIndex = 0; + this.lblOperationDescription.Text = "label1"; + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(346, 145); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(92, 26); + this.btnCancel.TabIndex = 11; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.Location = new System.Drawing.Point(248, 145); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(92, 26); + this.btnOK.TabIndex = 10; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // tbOldPath + // + this.tbOldPath.Location = new System.Drawing.Point(77, 80); + this.tbOldPath.Name = "tbOldPath"; + this.tbOldPath.Size = new System.Drawing.Size(321, 20); + this.tbOldPath.TabIndex = 12; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 83); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(50, 13); + this.label1.TabIndex = 13; + this.label1.Text = "Old path:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 109); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(56, 13); + this.label2.TabIndex = 15; + this.label2.Text = "New path:"; + // + // tbNewPath + // + this.tbNewPath.Location = new System.Drawing.Point(77, 106); + this.tbNewPath.Name = "tbNewPath"; + this.tbNewPath.Size = new System.Drawing.Size(321, 20); + this.tbNewPath.TabIndex = 14; + // + // btnBrowseOldPath + // + this.btnBrowseOldPath.Location = new System.Drawing.Point(404, 80); + this.btnBrowseOldPath.Name = "btnBrowseOldPath"; + this.btnBrowseOldPath.Size = new System.Drawing.Size(33, 20); + this.btnBrowseOldPath.TabIndex = 16; + this.btnBrowseOldPath.Text = "..."; + this.btnBrowseOldPath.UseVisualStyleBackColor = true; + this.btnBrowseOldPath.Click += new System.EventHandler(this.btnBrowseOldPath_Click); + // + // btnBrowseNewPath + // + this.btnBrowseNewPath.Location = new System.Drawing.Point(404, 105); + this.btnBrowseNewPath.Name = "btnBrowseNewPath"; + this.btnBrowseNewPath.Size = new System.Drawing.Size(33, 20); + this.btnBrowseNewPath.TabIndex = 17; + this.btnBrowseNewPath.Text = "..."; + this.btnBrowseNewPath.UseVisualStyleBackColor = true; + this.btnBrowseNewPath.Click += new System.EventHandler(this.btnBrowseNewPath_Click); + // + // ReplaceFolderDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(450, 183); + this.Controls.Add(this.btnBrowseNewPath); + this.Controls.Add(this.btnBrowseOldPath); + this.Controls.Add(this.label2); + this.Controls.Add(this.tbNewPath); + this.Controls.Add(this.label1); + this.Controls.Add(this.tbOldPath); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.lblOperationDescription); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ReplaceFolderDialog"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "ReplaceFolderDialog"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lblOperationDescription; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.TextBox tbOldPath; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox tbNewPath; + private System.Windows.Forms.Button btnBrowseOldPath; + private System.Windows.Forms.Button btnBrowseNewPath; + } +} \ No newline at end of file diff --git a/Editor/AGS.Editor/GUI/ReplaceFolderDialog.cs b/Editor/AGS.Editor/GUI/ReplaceFolderDialog.cs new file mode 100644 index 00000000000..8e3b25fb349 --- /dev/null +++ b/Editor/AGS.Editor/GUI/ReplaceFolderDialog.cs @@ -0,0 +1,91 @@ +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 AGS.Editor +{ + public partial class ReplaceFolderDialog : Form + { + private string _makeRelativeToThisDir; + + public ReplaceFolderDialog() + { + InitializeComponent(); + } + + private ReplaceFolderDialog(string titleBar, string headerText, string oldPath, string newPath, string makeRelativeToThisDir = null) + { + InitializeComponent(); + Text = titleBar; + lblOperationDescription.Text = headerText; + _makeRelativeToThisDir = makeRelativeToThisDir; + OldPath = oldPath; + NewPath = newPath; + } + + public string OldPath + { + get { return tbOldPath.Text; } + set + { + string path = value; + if (!string.IsNullOrEmpty(_makeRelativeToThisDir)) + path = Utilities.GetRelativeToBasePath(path, _makeRelativeToThisDir); + tbOldPath.Text = path; + } + } + + public string NewPath + { + get { return tbNewPath.Text; } + set + { + string path = value; + if (!string.IsNullOrEmpty(_makeRelativeToThisDir)) + path = Utilities.GetRelativeToBasePath(path, _makeRelativeToThisDir); + tbNewPath.Text = path; + } + } + + public static Tuple Show(string titleBar, string headerText, string oldPath, string newPath, string makeRelativeToThisDir = null) + { + ReplaceFolderDialog dialog = new ReplaceFolderDialog(titleBar, headerText, oldPath, newPath, makeRelativeToThisDir); + Tuple result = null; + if (dialog.ShowDialog() == DialogResult.OK) + { + result = new Tuple(dialog.OldPath, dialog.NewPath); + } + dialog.Dispose(); + return result; + } + + private void btnOK_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } + + private void btnBrowseOldPath_Click(object sender, EventArgs e) + { + OldPath = Factory.GUIController.ShowSelectFolderOrDefaultDialog("Select folder", tbOldPath.Text); + } + + private void btnBrowseNewPath_Click(object sender, EventArgs e) + { + NewPath = Factory.GUIController.ShowSelectFolderOrDefaultDialog("Select folder", tbNewPath.Text); + } + } +} diff --git a/Editor/AGS.Editor/GUI/ReplaceFolderDialog.resx b/Editor/AGS.Editor/GUI/ReplaceFolderDialog.resx new file mode 100644 index 00000000000..1af7de150c9 --- /dev/null +++ b/Editor/AGS.Editor/GUI/ReplaceFolderDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Editor/AGS.Editor/Panes/SpriteSelector.cs b/Editor/AGS.Editor/Panes/SpriteSelector.cs index d04e3ef6f30..a25c9b77fe0 100644 --- a/Editor/AGS.Editor/Panes/SpriteSelector.cs +++ b/Editor/AGS.Editor/Panes/SpriteSelector.cs @@ -35,6 +35,7 @@ public partial class SpriteSelector : UserControl, IObjectConfigurable private const string MENU_ITEM_REPLACE_FROM_SOURCE_ALL = "ReplaceAllSpritesFromSource"; private const string MENU_ITEM_EXPORT_FOLDER = "ExportFolder"; private const string MENU_ITEM_EXPORT_FIXUP_SOURCES = "ExportFixupSources"; + private const string MENU_ITEM_REPLACE_SOURCE_FOLDER = "ReplaceSpritesSourceFolder"; private const string MENU_ITEM_SORT_BY_NUMBER = "SortSpritesByNumber"; private const string MENU_ITEM_REPLACE_FROM_SOURCE = "ReplaceSpriteFromSource"; private const string MENU_ITEM_FIND_BY_NUMBER = "FindSpriteByNumber"; @@ -851,6 +852,10 @@ private void SpriteContextMenuEventHandler(object sender, EventArgs e) { ReplaceSpritesFromSource(); } + else if (item.Name == MENU_ITEM_REPLACE_SOURCE_FOLDER) + { + ReplaceSourceFolderForSprites(); + } else if (item.Name == MENU_ITEM_PREVIEW_SIZE_1X) { SetSpritePreviewMultiplier(2); @@ -1223,6 +1228,67 @@ private void ExportFixupSources() Tasks.ExportSprites(opts); } + private void ReplaceSourceFolderForSprites() + { + var allSprites = Factory.AGSEditor.CurrentGame.RootSpriteFolder.GetAllSpritesFromAllSubFolders(); + + string firstFoundSourceFile = null; + foreach (ListViewItem listItem in spriteList.SelectedItems) + { + Sprite spr = GetSprite(listItem); + if (!string.IsNullOrEmpty(spr.SourceFile)) + { + firstFoundSourceFile = spr.SourceFile; + break; + } + } + + if (string.IsNullOrEmpty(firstFoundSourceFile)) + { + Sprite spr = allSprites.Where(s => !string.IsNullOrEmpty(s.SourceFile)).FirstOrDefault(); + if (spr != null) + firstFoundSourceFile = spr.SourceFile; + } + + if (string.IsNullOrEmpty(firstFoundSourceFile)) + { + Factory.GUIController.ShowMessage("None of the sprites has a source filename.", MessageBoxIcon.Warning); + return; + } + + string parentDir = Path.GetDirectoryName(firstFoundSourceFile); + var replaceDirs = ReplaceFolderDialog.Show("Replace sprite(s) source path", + "Please choose which part of the parent path should be replaced and provide a replacement. Relative paths will be assumed relative to your game's project folder.", + parentDir, parentDir, Factory.AGSEditor.CurrentGame.DirectoryPath); + + if (replaceDirs == null || replaceDirs.Item1 == replaceDirs.Item2) + return; + + int itemCount = 0; + foreach (var sprite in allSprites) + { + if (string.IsNullOrEmpty(sprite.SourceFile)) + continue; + + string newPath; + if (Utilities.ReplacePathBaseProjectRelative(sprite.SourceFile, replaceDirs.Item1, replaceDirs.Item2, out newPath)) + { + sprite.SourceFile = newPath; + itemCount++; + } + } + + if (itemCount > 0) + { + Factory.GUIController.ShowMessage($"{itemCount} sprite(s) had their source paths updated.", MessageBoxIcon.Information); + Factory.GUIController.RefreshPropertyGrid(); + } + else + { + Factory.GUIController.ShowMessage($"No sprites with the matching old paths found, no changes were made.", MessageBoxIcon.Information); + } + } + private void SortAllSpritesInCurrentFolderByNumber() { ((List)_currentFolder.Sprites).Sort(); @@ -1362,6 +1428,7 @@ private void ShowSpriteContextMenu(Point menuPosition) menu.Items.Add(new ToolStripSeparator()); menu.Items.Add(new ToolStripMenuItem("Export all sprites...", null, onClick, MENU_ITEM_EXPORT_FOLDER)); menu.Items.Add(new ToolStripMenuItem("Create source files for all sprites with missing / external sources...", null, onClick, MENU_ITEM_EXPORT_FIXUP_SOURCES)); + menu.Items.Add(new ToolStripMenuItem("Replace source paths for sprites...", null, onClick, MENU_ITEM_REPLACE_SOURCE_FOLDER)); menu.Items.Add(new ToolStripSeparator()); menu.Items.Add(new ToolStripMenuItem("Find sprite by number...", null, onClick, MENU_ITEM_FIND_BY_NUMBER)); menu.Items.Add(new ToolStripMenuItem("Sort sprites by number", null, onClick, MENU_ITEM_SORT_BY_NUMBER)); diff --git a/Editor/AGS.Editor/Utils/Utilities.cs b/Editor/AGS.Editor/Utils/Utilities.cs index 429d0ec5dd6..cd8da62c165 100644 --- a/Editor/AGS.Editor/Utils/Utilities.cs +++ b/Editor/AGS.Editor/Utils/Utilities.cs @@ -146,18 +146,23 @@ public static void AddAllMatchingFiles(IList list, string parentDir, } } - public static string GetRelativeToProjectPath(string absolutePath) + public static string GetRelativeToBasePath(string absolutePath, string basePath) { if (String.IsNullOrEmpty(absolutePath) || - !absolutePath.Contains(Factory.AGSEditor.CurrentGame.DirectoryPath)) + !absolutePath.Contains(basePath)) { return absolutePath; } - Uri currentProjectUri = new Uri(Factory.AGSEditor.CurrentGame.DirectoryPath + Path.DirectorySeparatorChar); + Uri basePathUri = new Uri(basePath + Path.DirectorySeparatorChar); Uri currentPathUri = new Uri(absolutePath); - return Uri.UnescapeDataString(currentProjectUri.MakeRelativeUri(currentPathUri).OriginalString); + return Uri.UnescapeDataString(basePathUri.MakeRelativeUri(currentPathUri).OriginalString); + } + + public static string GetRelativeToProjectPath(string absolutePath) + { + return GetRelativeToBasePath(absolutePath, Factory.AGSEditor.CurrentGame.DirectoryPath); } public static string[] GetRelativeToProjectPath(string[] absolutePaths) @@ -223,6 +228,55 @@ public static bool DoesPathContainDotDirs(string path) return parts.Any(p => p == "." || p == ".."); } + /// + /// Replaces "oldBase" parent part of the "path" with the "newBase", assigns "newPath" and returns a result. + /// If "path" does not contain "oldBase", then fails. + /// + public static bool ReplacePathBase(string path, string oldBase, string newBase, out string newPath) + { + Uri oldBaseUri = new Uri(oldBase + Path.DirectorySeparatorChar); + Uri pathUri = new Uri(path); + if (!oldBaseUri.IsBaseOf(pathUri)) + { + newPath = path; + return false; + } + + Uri relativeUri = pathUri.MakeRelativeUri(oldBaseUri); + Uri newBaseUri = new Uri(newBase + Path.DirectorySeparatorChar); + Uri absoluteUri; + if (Uri.TryCreate(newBaseUri, relativeUri, out absoluteUri)) + { + if (pathUri.IsFile) + newPath = Path.Combine(absoluteUri.LocalPath, Path.GetFileName(path)); + else + newPath = absoluteUri.LocalPath; + return true; + } + newPath = path; + return false; + } + + public static bool ReplacePathBaseProjectRelative(string path, string oldBase, string newBase, out string newPath) + { + string originalPath = path; + if (!Path.IsPathRooted(oldBase)) + oldBase = Path.Combine(Factory.AGSEditor.CurrentGame.DirectoryPath, oldBase); + if (!Path.IsPathRooted(newBase)) + newBase = Path.Combine(Factory.AGSEditor.CurrentGame.DirectoryPath, newBase); + if (!Path.IsPathRooted(path)) + path = Path.Combine(Factory.AGSEditor.CurrentGame.DirectoryPath, path); + + if (!ReplacePathBase(path, oldBase, newBase, out newPath)) + { + newPath = originalPath; + return false; + } + + newPath = GetRelativeToProjectPath(newPath); + return true; + } + /// /// Wraps Directory.GetFiles in a handler to deal with an exception /// erroneously being thrown on Linux network shares if no files match. From 301ed302811ddceac41fc57dda327f0d2a28cc01 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Sat, 8 Feb 2025 05:43:24 +0300 Subject: [PATCH 2/2] Editor: implement "Replace source paths" operation for audio clips --- .../AGS.Editor/Components/AudioComponent.cs | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/Editor/AGS.Editor/Components/AudioComponent.cs b/Editor/AGS.Editor/Components/AudioComponent.cs index f41d18f31f2..58054391b01 100644 --- a/Editor/AGS.Editor/Components/AudioComponent.cs +++ b/Editor/AGS.Editor/Components/AudioComponent.cs @@ -4,6 +4,7 @@ using System.Text; using System.Linq; using AGS.Types; +using System.Windows.Forms; namespace AGS.Editor.Components { @@ -13,6 +14,7 @@ class AudioComponent : BaseComponentWithFolders, IPr private const string COMPILED_AUDIO_FILENAME_PREFIX = "au"; private const string COMMAND_ADD_AUDIO = "AddAudioClipCmd"; private const string COMMAND_REIMPORT_ALL = "ReimportAllAudioClipCmd"; + private const string COMMAND_REPLACE_AUDIO_SOURCE_FOLDER = "ReplaceAudioClipSourceFolderCmd"; private const string COMMAND_PROPERTIES = "PropertiesAudioClip"; private const string COMMAND_RENAME = "RenameAudioClip"; private const string COMMAND_REIMPORT = "ReimportAudioClip"; @@ -114,6 +116,10 @@ protected override void ItemCommandClick(string controlID) { CommandForceReimportOfAllAudioClips(); } + else if (controlID == COMMAND_REPLACE_AUDIO_SOURCE_FOLDER) + { + CommandReplaceSourceFolderForAudioClips(); + } else if (controlID == COMMAND_RENAME) { _guiController.ProjectTree.BeginLabelEdit(this, _rightClickedID); @@ -508,7 +514,7 @@ private void UpdateScoreSound(IList allAudio) private void UpdateViewFrameSounds(IList allAudio, ViewFolder views) { - foreach (View view in views.Views) + foreach (AGS.Types.View view in views.Views) { foreach (ViewLoop loop in view.Loops) { @@ -702,6 +708,53 @@ public void ReplaceAudioClipSource(AudioClip clip) } } + private void CommandReplaceSourceFolderForAudioClips() + { + var allAudioClips = _agsEditor.CurrentGame.RootAudioClipFolder.AllItemsFlat; + string firstFoundSourceFile = null; + AudioClip foundClip = allAudioClips.Where(s => !string.IsNullOrEmpty(s.SourceFileName)).FirstOrDefault(); + if (foundClip != null) + firstFoundSourceFile = foundClip.SourceFileName; + + if (string.IsNullOrEmpty(firstFoundSourceFile)) + { + Factory.GUIController.ShowMessage("None of the audio clips has a source filename.", MessageBoxIcon.Warning); + return; + } + + string parentDir = Path.GetDirectoryName(firstFoundSourceFile); + var replaceDirs = ReplaceFolderDialog.Show("Replace audio clip(s) source path", + "Please choose which part of the parent path should be replaced and provide a replacement. Relative paths will be assumed relative to your game's project folder.", + parentDir, parentDir, Factory.AGSEditor.CurrentGame.DirectoryPath); + + if (replaceDirs == null || replaceDirs.Item1 == replaceDirs.Item2) + return; + + int itemCount = 0; + foreach (var clip in allAudioClips) + { + if (string.IsNullOrEmpty(clip.SourceFileName)) + continue; + + string newPath; + if (Utilities.ReplacePathBaseProjectRelative(clip.SourceFileName, replaceDirs.Item1, replaceDirs.Item2, out newPath)) + { + clip.SourceFileName = newPath; + itemCount++; + } + } + + if (itemCount > 0) + { + Factory.GUIController.ShowMessage($"{itemCount} audio clip(s) had their source paths updated.", MessageBoxIcon.Information); + Factory.GUIController.RefreshPropertyGrid(); + } + else + { + Factory.GUIController.ShowMessage($"No audio clips with the matching old paths found, no changes were made.", MessageBoxIcon.Information); + } + } + private void AddAudioClipToListIfFileNeedsToBeCopiedFromSource(AudioClip clip, PreCompileGameEventArgs evArgs, List filesToCopy, List fileNamesToUpdate) { string compiledFileName = clip.CacheFileName; @@ -908,11 +961,13 @@ protected override ProjectTreeItem CreateTreeItemForItem(AudioClip item) protected override void AddNewItemCommandsToFolderContextMenu(string controlID, IList menu) { menu.Add(new MenuCommand(COMMAND_ADD_AUDIO, "Add audio file(s)...", null)); - menu.Add(new MenuCommand(COMMAND_REIMPORT_ALL, "Force reimport all file(s)", null)); } protected override void AddExtraCommandsToFolderContextMenu(string controlID, IList menu) { + menu.Add(MenuCommand.Separator); + menu.Add(new MenuCommand(COMMAND_REPLACE_AUDIO_SOURCE_FOLDER, "Replace source paths for audio clips...", null)); + menu.Add(new MenuCommand(COMMAND_REIMPORT_ALL, "Force reimport all file(s)", null)); menu.Add(MenuCommand.Separator); menu.Add(new MenuCommand(COMMAND_PROPERTIES, "Properties", null)); }