diff --git a/CHANGELOG.md b/CHANGELOG.md index a647936e4..367fd6428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - [CLI] Include `supports` relationship in `ckan show` (#4262 by: HebaruSan) - [Multiple] Pre-release handling (#4260, #4266, #4294 by: HebaruSan, JonnyOThan) - [Multiple] French translation updates (#4268 by: HebaruSan) +- [Multiple] Checkboxes to copy more dirs when cloning instances (#4304 by: HebaruSan) ### Bugfixes diff --git a/Core/GameInstanceManager.cs b/Core/GameInstanceManager.cs index f0e6b1648..f2c458459 100644 --- a/Core/GameInstanceManager.cs +++ b/Core/GameInstanceManager.cs @@ -243,6 +243,7 @@ public GameInstance AddInstance(GameInstance instance) /// The KSP instance to clone. /// The name for the new instance. /// The path where the new instance should be located. + /// True to make junctions or symlinks to stock folders instead of copying /// Thrown if the instance name is already in use. /// Thrown by AddInstance() if created instance is not valid, e.g. if something went wrong with copying. /// Thrown by CopyDirectory() if directory doesn't exist. Should never be thrown here. @@ -252,6 +253,30 @@ public void CloneInstance(GameInstance existingInstance, string newName, string newPath, bool shareStockFolders = false) + { + CloneInstance(existingInstance, newName, newPath, + existingInstance.game.LeaveEmptyInClones, + shareStockFolders); + } + + /// + /// Clones an existing KSP installation. + /// + /// The KSP instance to clone. + /// The name for the new instance. + /// The path where the new instance should be located. + /// Dirs whose contents should not be copied + /// True to make junctions or symlinks to stock folders instead of copying + /// Thrown if the instance name is already in use. + /// Thrown by AddInstance() if created instance is not valid, e.g. if something went wrong with copying. + /// Thrown by CopyDirectory() if directory doesn't exist. Should never be thrown here. + /// Thrown by CopyDirectory() if the target folder already exists and is not empty. + /// Thrown by CopyDirectory() if something goes wrong during the process. + public void CloneInstance(GameInstance existingInstance, + string newName, + string newPath, + string[] leaveEmpty, + bool shareStockFolders = false) { if (HasInstance(newName)) { @@ -267,7 +292,7 @@ public void CloneInstance(GameInstance existingInstance, Utilities.CopyDirectory(existingInstance.GameDir(), newPath, shareStockFolders ? existingInstance.game.StockFolders : Array.Empty(), - existingInstance.game.LeaveEmptyInClones); + leaveEmpty); // Add the new instance to the config AddInstance(new GameInstance(existingInstance.game, newPath, newName, User)); diff --git a/GUI/Dialogs/CloneGameInstanceDialog.Designer.cs b/GUI/Dialogs/CloneGameInstanceDialog.Designer.cs index dbe1c2d05..7aa57c7f3 100644 --- a/GUI/Dialogs/CloneGameInstanceDialog.Designer.cs +++ b/GUI/Dialogs/CloneGameInstanceDialog.Designer.cs @@ -44,6 +44,9 @@ private void InitializeComponent() this.checkBoxSetAsDefault = new System.Windows.Forms.CheckBox(); this.checkBoxSwitchInstance = new System.Windows.Forms.CheckBox(); this.checkBoxShareStock = new System.Windows.Forms.CheckBox(); + this.OptionalPathsLabel = new System.Windows.Forms.Label(); + this.OptionalPathsListView = new ThemedListView(); + this.PathHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.buttonOK = new System.Windows.Forms.Button(); this.buttonCancel = new System.Windows.Forms.Button(); this.progressBar = new System.Windows.Forms.ProgressBar(); @@ -196,10 +199,39 @@ private void InitializeComponent() this.checkBoxShareStock.UseVisualStyleBackColor = true; resources.ApplyResources(this.checkBoxShareStock, "checkBoxShareStock"); // + // OptionalPathsLabel + // + this.OptionalPathsLabel.AutoSize = true; + this.OptionalPathsLabel.Location = new System.Drawing.Point(12, 234); + this.OptionalPathsLabel.Name = "OptionalPathsLabel"; + this.OptionalPathsLabel.Size = new System.Drawing.Size(131, 13); + this.OptionalPathsLabel.TabIndex = 22; + resources.ApplyResources(this.OptionalPathsLabel, "OptionalPathsLabel"); + // + // OptionalPathsListView + // + this.OptionalPathsListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.OptionalPathsListView.CheckBoxes = true; + this.OptionalPathsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.PathHeader}); + this.OptionalPathsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.OptionalPathsListView.Location = new System.Drawing.Point(181, 234); + this.OptionalPathsListView.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.OptionalPathsListView.Name = "OptionalPathsListView"; + this.OptionalPathsListView.Size = new System.Drawing.Size(218, 120); + this.OptionalPathsListView.TabIndex = 23; + this.OptionalPathsListView.UseCompatibleStateImageBehavior = false; + this.OptionalPathsListView.View = System.Windows.Forms.View.Details; + // + // PathHeader + // + this.PathHeader.Width = 180; + resources.ApplyResources(this.PathHeader, "PathHeader"); + // // buttonOK // this.buttonOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.buttonOK.Location = new System.Drawing.Point(256, 230); + this.buttonOK.Location = new System.Drawing.Point(256, 360); this.buttonOK.Name = "buttonOK"; this.buttonOK.Size = new System.Drawing.Size(75, 23); this.buttonOK.TabIndex = 22; @@ -210,7 +242,7 @@ private void InitializeComponent() // buttonCancel // this.buttonCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.buttonCancel.Location = new System.Drawing.Point(337, 230); + this.buttonCancel.Location = new System.Drawing.Point(337, 360); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(75, 23); this.buttonCancel.TabIndex = 23; @@ -235,7 +267,7 @@ private void InitializeComponent() this.AllowDrop = true; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(424, 265); + this.ClientSize = new System.Drawing.Size(424, 395); this.Controls.Add(this.labelOldInstance); this.Controls.Add(this.comboBoxKnownInstance); this.Controls.Add(this.labelOldPath); @@ -245,6 +277,8 @@ private void InitializeComponent() this.Controls.Add(this.buttonPathBrowser); this.Controls.Add(this.checkBoxSwitchInstance); this.Controls.Add(this.checkBoxShareStock); + this.Controls.Add(this.OptionalPathsLabel); + this.Controls.Add(this.OptionalPathsListView); this.Controls.Add(this.textBoxNewPath); this.Controls.Add(this.labelNewPath); this.Controls.Add(this.checkBoxSetAsDefault); @@ -284,6 +318,9 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox checkBoxSetAsDefault; private System.Windows.Forms.CheckBox checkBoxSwitchInstance; private System.Windows.Forms.CheckBox checkBoxShareStock; + private System.Windows.Forms.Label OptionalPathsLabel; + private System.Windows.Forms.ListView OptionalPathsListView; + private System.Windows.Forms.ColumnHeader PathHeader; private System.Windows.Forms.Button buttonOK; private System.Windows.Forms.Button buttonCancel; private System.Windows.Forms.FolderBrowserDialog folderBrowserDialogNewPath; diff --git a/GUI/Dialogs/CloneGameInstanceDialog.cs b/GUI/Dialogs/CloneGameInstanceDialog.cs index 796bab75f..12d101e6c 100644 --- a/GUI/Dialogs/CloneGameInstanceDialog.cs +++ b/GUI/Dialogs/CloneGameInstanceDialog.cs @@ -57,10 +57,21 @@ public CloneGameInstanceDialog(GameInstanceManager manager, private void comboBoxKnownInstance_SelectedIndexChanged(object? sender, EventArgs? e) { - textBoxClonePath.Text = comboBoxKnownInstance.SelectedItem is string sel - && !string.IsNullOrEmpty(sel) - ? Platform.FormatPath(manager.Instances[sel].GameDir()) - : ""; + if (comboBoxKnownInstance.SelectedItem is string sel + && !string.IsNullOrEmpty(sel)) + { + var inst = manager.Instances[sel]; + textBoxClonePath.Text = Platform.FormatPath(inst.GameDir()); + OptionalPathsListView.Items.Clear(); + OptionalPathsListView.Items.AddRange( + inst.game.LeaveEmptyInClones + .OrderBy(path => path, + StringComparer.OrdinalIgnoreCase) + .Select(path => new ListViewItem(path) { Tag = path }) + .ToArray()); + OptionalPathsLabel.Visible = OptionalPathsListView.Visible = + OptionalPathsListView.Items.Count > 0; + } } /// @@ -143,7 +154,14 @@ await Task.Run(() => { if (instanceToClone.Valid) { - manager.CloneInstance(instanceToClone, newName, newPath, checkBoxShareStock.Checked); + manager.CloneInstance(instanceToClone, newName, newPath, + OptionalPathsListView.Items + .OfType() + .Where(lvi => !lvi.Checked) + .Select(lvi => lvi.Tag) + .OfType() + .ToArray(), + checkBoxShareStock.Checked); } else { diff --git a/GUI/Dialogs/CloneGameInstanceDialog.resx b/GUI/Dialogs/CloneGameInstanceDialog.resx index a40d752b1..bba8c30fd 100644 --- a/GUI/Dialogs/CloneGameInstanceDialog.resx +++ b/GUI/Dialogs/CloneGameInstanceDialog.resx @@ -126,6 +126,8 @@ Set new instance as default Switch to new instance Share stock files + Optional paths to copy: + Optional paths Clone Cancel Clone Game Instance