mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-26 22:01:06 -04:00
refactor(ui): split MainForm into per-menu partial files
Moves members verbatim into MainForm.File/View/Tools/Nest/Plate/Status partials (nested under MainForm.cs in the project); MainForm.cs keeps the shell: fields, startup, overrides and the Window menu. Each file keeps only the usings it needs. No logic changes; private member names used by reflection-based tests are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
686bb4d749
commit
d87d113ceb
8 files changed
+1952
-1874
No files matched your search
@@ -0,0 +1,460 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Data;
|
||||
using OpenNest.Engine.BestFit;
|
||||
using OpenNest.IO;
|
||||
using OpenNest.Properties;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
private Nest CreateDefaultNest()
|
||||
{
|
||||
var nest = new Nest();
|
||||
LoadNestDefaults().ApplyTo(nest);
|
||||
// After the defaults so the rail is converted to the new nest's units.
|
||||
LoadClampSettings(this).ApplyToNewNest(nest);
|
||||
return nest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the machine clamp settings for creating new nests (New, BOM import); an
|
||||
/// unreadable or partly invalid file warns once per session, because the new nest's
|
||||
/// clamps may not match the machine.
|
||||
/// </summary>
|
||||
internal static ClampSettings LoadClampSettings(IWin32Window owner)
|
||||
{
|
||||
var settings = LoadSavedClampSettings(out var status);
|
||||
|
||||
if (
|
||||
status is ClampSettingsStatus.Invalid or ClampSettingsStatus.Partial
|
||||
&& !clampSettingsWarned
|
||||
)
|
||||
{
|
||||
clampSettingsWarned = true;
|
||||
var problem =
|
||||
status == ClampSettingsStatus.Invalid
|
||||
? "could not be read, so new nests have no clamps"
|
||||
: "has invalid values that were ignored, so new nests may be missing clamps";
|
||||
MessageBox.Show(
|
||||
owner,
|
||||
$"The clamp settings file {problem}:\n{ClampSettings.DefaultPath}\n\nCheck the clamps in Nest > Clamps and use Save as Machine Defaults to rewrite the file.",
|
||||
"Clamps",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static bool clampSettingsWarned;
|
||||
|
||||
/// <summary>The single desktop entry point for the persisted machine clamp settings.</summary>
|
||||
internal static ClampSettings LoadSavedClampSettings(out ClampSettingsStatus status) =>
|
||||
ClampSettings.Load(ClampSettings.DefaultPath, out status);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the persisted nest defaults (see <see cref="LoadSavedNestDefaults"/>);
|
||||
/// a corrupt or unreadable file warns once per session.
|
||||
/// </summary>
|
||||
private NestDefaults LoadNestDefaults()
|
||||
{
|
||||
var defaults = LoadSavedNestDefaults(out var status);
|
||||
|
||||
if (status == NestDefaultsStatus.Invalid && !defaultsWarned)
|
||||
{
|
||||
defaultsWarned = true;
|
||||
MessageBox.Show(
|
||||
$"The nest defaults file could not be read:\n{NestDefaults.DefaultPath}\n\nBuilt-in defaults will be used. Re-save your defaults from Tools > Nest Defaults.",
|
||||
"Nest Defaults",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
||||
private bool defaultsWarned;
|
||||
|
||||
/// <summary>
|
||||
/// The single desktop entry point for persisted nest defaults. Units
|
||||
/// fall back to the legacy DefaultUnit setting whenever the file is
|
||||
/// missing or unusable or has no valid unit, so New, BOM import and
|
||||
/// the defaults dialog always agree.
|
||||
/// </summary>
|
||||
internal static NestDefaults LoadSavedNestDefaults(out NestDefaultsStatus status) =>
|
||||
NestDefaults.Load(
|
||||
NestDefaults.DefaultPath,
|
||||
Properties.Settings.Default.DefaultUnit,
|
||||
out status
|
||||
);
|
||||
|
||||
private string GetNestName(DateTime date, int id)
|
||||
{
|
||||
var year = (date.Year % 100).ToString("D2");
|
||||
var seq = ToBase36(id).PadLeft(3, '2');
|
||||
|
||||
return $"N{year}-{seq}";
|
||||
}
|
||||
|
||||
private static string ToBase36(int value)
|
||||
{
|
||||
const string chars = "2345679ACDEFGHJKLMNPQRSTUVWXYZ";
|
||||
if (value == 0)
|
||||
return chars[0].ToString();
|
||||
|
||||
var result = "";
|
||||
while (value > 0)
|
||||
{
|
||||
result = chars[value % chars.Length] + result;
|
||||
value /= chars.Length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void LoadNest(Nest nest, FormWindowState windowState = FormWindowState.Maximized,
|
||||
Guid remoteId = default, string serverUrl = null, int remoteRevision = 0)
|
||||
{
|
||||
var editForm = new EditNestForm(nest);
|
||||
if (remoteId != Guid.Empty)
|
||||
editForm.Document.BindRemote(remoteId, serverUrl, remoteRevision);
|
||||
editForm.MdiParent = this;
|
||||
editForm.PlateView.FilesDropped += (sender, paths) => OpenDroppedNestFiles(paths);
|
||||
editForm.PlateChanged += (sender, e) =>
|
||||
{
|
||||
NavigationEnableCheck();
|
||||
UpdatePlateStatus();
|
||||
mnuPlateRemove.Enabled = activeForm?.PlateManager.CanRemoveCurrent ?? false;
|
||||
};
|
||||
editForm.WindowState = windowState;
|
||||
editForm.Show();
|
||||
editForm.PlateView.ZoomToFit();
|
||||
}
|
||||
|
||||
#region File Menu Events
|
||||
|
||||
private void New_Click(object sender, EventArgs e)
|
||||
{
|
||||
var windowState =
|
||||
ActiveMdiChild != null ? ActiveMdiChild.WindowState : FormWindowState.Maximized;
|
||||
|
||||
var nest = CreateDefaultNest();
|
||||
|
||||
nest.DateCreated = DateTime.Now;
|
||||
nest.DateLastModified = DateTime.Now;
|
||||
|
||||
if (DateTime.Now.Date != Settings.Default.LastNestCreatedDate.Date)
|
||||
{
|
||||
Settings.Default.NestNumber = 1;
|
||||
Settings.Default.LastNestCreatedDate = DateTime.Now.Date;
|
||||
}
|
||||
|
||||
nest.Name = GetNestName(DateTime.Now, Settings.Default.NestNumber++);
|
||||
LoadNest(nest, windowState);
|
||||
}
|
||||
|
||||
private async void Open_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (NestStorage.Settings.Mode == NestStorageMode.File)
|
||||
{
|
||||
using var dlg = new OpenFileDialog { Filter = NestFormat.FileFilter, Multiselect = true };
|
||||
if (dlg.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
var reader = new NestReader(dlg.FileName) { BestFitStore = BestFitCacheStore.Shared };
|
||||
var nest = reader.Read();
|
||||
LoadNest(nest);
|
||||
ShowNestWarnings(reader);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var repository = NestStorage.CreateRepository();
|
||||
var serverUrl = NestStorage.Settings.ServerUrl;
|
||||
using var dlg = new SavedNestsForm(repository, serverUrl);
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
databaseOpenInProgress = true;
|
||||
storageModeMenu.Enabled = false;
|
||||
mnuToolsOptions.Enabled = false;
|
||||
var snapshot = await NestHistorySession.ReadCurrentAsync(repository, dlg.SelectedId, serverUrl);
|
||||
using var stream = new MemoryStream(snapshot.Archive);
|
||||
var reader = new NestReader(stream) { BestFitStore = BestFitCacheStore.Shared };
|
||||
var nest = reader.Read();
|
||||
// Metadata edits create revisions without rewriting the archive. Use their
|
||||
// current editable fields rather than silently reverting them on the next save.
|
||||
nest.Name = snapshot.Metadata.Name;
|
||||
nest.Customer = snapshot.Metadata.Customer;
|
||||
nest.Notes = snapshot.Metadata.Comments;
|
||||
nest.Status = snapshot.Metadata.Status;
|
||||
nest.MadeBy = snapshot.Metadata.MadeBy;
|
||||
nest.DateLastModified = snapshot.Metadata.DateModified;
|
||||
LoadNest(nest, remoteId: snapshot.Metadata.Id, serverUrl: snapshot.ServerUrl,
|
||||
remoteRevision: snapshot.Metadata.Revision);
|
||||
ShowNestWarnings(reader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowStorageError("open", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
databaseOpenInProgress = false;
|
||||
if (!IsDisposed)
|
||||
{
|
||||
storageModeMenu.Enabled = !databaseSaveInProgress;
|
||||
mnuToolsOptions.Enabled = !databaseSaveInProgress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowNestWarnings(NestReader reader)
|
||||
{
|
||||
if (reader.Warnings.Count > 0)
|
||||
MessageBox.Show(this, string.Join(Environment.NewLine, reader.Warnings),
|
||||
"Nest Load Warnings", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
private void ImportBom_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = new BomImportForm();
|
||||
form.MdiParentForm = this;
|
||||
form.ShowDialog(this);
|
||||
}
|
||||
|
||||
private async void Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = activeForm;
|
||||
if (form == null || databaseSaveInProgress)
|
||||
return;
|
||||
if (NestStorage.Settings.Mode == NestStorageMode.File)
|
||||
form.Save();
|
||||
else
|
||||
await SaveDatabaseAsync(form, saveCopy: false);
|
||||
}
|
||||
|
||||
private async void SaveAs_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = activeForm;
|
||||
if (form == null || databaseSaveInProgress)
|
||||
return;
|
||||
if (NestStorage.Settings.Mode == NestStorageMode.File)
|
||||
{
|
||||
form.SaveAs();
|
||||
return;
|
||||
}
|
||||
|
||||
// A pending upload is reconciled without writing, so only ask for a name
|
||||
// when this click will actually create the copy.
|
||||
string copyName = null;
|
||||
if (!form.Document.PendingCreateRequiresReconciliation)
|
||||
{
|
||||
using var dlg = new NestNameForm(form.Nest.Name);
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
copyName = dlg.NestName;
|
||||
}
|
||||
await SaveDatabaseAsync(form, saveCopy: true, copyName);
|
||||
}
|
||||
|
||||
private async Task SaveDatabaseAsync(EditNestForm form, bool saveCopy, string copyName = null)
|
||||
{
|
||||
var originalName = form.Nest.Name;
|
||||
databaseSaveInProgress = true;
|
||||
storageModeMenu.Enabled = false;
|
||||
mnuToolsOptions.Enabled = false;
|
||||
EnableCheck();
|
||||
try
|
||||
{
|
||||
var serverUrl = NestStorage.Settings.ServerUrl;
|
||||
var repository = NestStorage.CreateRepository();
|
||||
if (form.Document.PendingCreateRequiresReconciliation)
|
||||
{
|
||||
var reconciled = await form.Document.ReconcilePendingCreateAsync(repository, serverUrl);
|
||||
if (!IsDisposed)
|
||||
statusLabel1.Text = reconciled is null
|
||||
? $"No pending nest was found. Review your work, then {(form.Document.PendingCreateWasCopy ? "Save As" : "Save")} again to retry the same ID."
|
||||
: $"Found saved version {reconciled.Revision}. Review the current nest before saving again.";
|
||||
return; // Never combine reconciliation with a write in the same click.
|
||||
}
|
||||
if (copyName != null)
|
||||
form.Nest.Name = copyName;
|
||||
var saved = await form.Document.SaveToDatabaseAsync(
|
||||
repository, serverUrl, saveCopy);
|
||||
if (!form.IsDisposed)
|
||||
form.Text = form.Document.Name;
|
||||
if (!IsDisposed)
|
||||
statusLabel1.Text = $"Saved {saved.Name} to nest database";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Keep the new name only while its copy may still be pending on the server;
|
||||
// otherwise a later Save would rename the original nest.
|
||||
if (copyName != null && !form.Document.PendingCreateWasCopy)
|
||||
form.Nest.Name = originalName;
|
||||
if (!IsDisposed)
|
||||
ShowStorageError("save", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
databaseSaveInProgress = false;
|
||||
if (!IsDisposed)
|
||||
{
|
||||
storageModeMenu.Enabled = !databaseOpenInProgress;
|
||||
mnuToolsOptions.Enabled = !databaseOpenInProgress;
|
||||
EnableCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StorageMode_Click(object sender, EventArgs e)
|
||||
{
|
||||
using var dlg = new StorageModeForm();
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
try
|
||||
{
|
||||
NestStorage.Save(dlg.SelectedMode, dlg.ServerUrl);
|
||||
statusLabel1.Text = $"Nest storage: {dlg.SelectedMode}";
|
||||
foreach (var form in MdiChildren.OfType<EditNestForm>())
|
||||
form.UpdateDrawingLibraryCommand();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowStorageError("change storage mode", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportNest_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = activeForm;
|
||||
if (form == null)
|
||||
return;
|
||||
using var dlg = new SaveFileDialog { Filter = NestFormat.FileFilter, FileName = form.Nest.Name };
|
||||
if (dlg.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
try
|
||||
{
|
||||
// Export never changes the document's save path, remote id, or nest name.
|
||||
new NestWriter(form.Nest) { BestFitStore = BestFitCacheStore.Shared }.Write(dlg.FileName);
|
||||
statusLabel1.Text = $"Exported .nest to {dlg.FileName}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowStorageError("export", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowStorageError(string action, Exception ex) =>
|
||||
MessageBox.Show(this, $"Could not {action} nest: {ex.Message}",
|
||||
"Nest Storage", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
|
||||
private void Export_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.Export();
|
||||
}
|
||||
|
||||
private void ExportAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ExportAll();
|
||||
}
|
||||
|
||||
private void ExportNestReport_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = activeForm;
|
||||
if (form == null)
|
||||
return;
|
||||
|
||||
// The report never depends on a selected post or NC output; it only needs a
|
||||
// quiescent nest so the snapshot is coherent.
|
||||
Func<bool> isJobBusy = IsNestJobBusy;
|
||||
EditNestForm.NestReportTargets targets;
|
||||
try
|
||||
{
|
||||
targets = form.CaptureReportTargets(isJobBusy);
|
||||
}
|
||||
catch (Exception ex) when (ex is InvalidOperationException or ArgumentException)
|
||||
{
|
||||
ReportFailure(null, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
var destination = ShowReportSaveDialog(targets.SuggestedFileName);
|
||||
if (destination == null)
|
||||
return; // Cancel: nothing written, job untouched.
|
||||
|
||||
try
|
||||
{
|
||||
form.WriteNestReport(targets, destination, isJobBusy);
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ex is InvalidOperationException
|
||||
or NotSupportedException
|
||||
or InvalidDataException
|
||||
or IOException
|
||||
or UnauthorizedAccessException
|
||||
or ArgumentException)
|
||||
{
|
||||
// Failures describe the captured target; an active-document switch is irrelevant.
|
||||
ReportFailure(targets, ex);
|
||||
return;
|
||||
}
|
||||
statusLabel1.Text = $"Saved nest report to {destination}";
|
||||
ReportSuccess(destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True while any operation could mutate nests or hold their plate views: whole-job
|
||||
/// nesting, an open progress window (a closed one can still await its commit), or a
|
||||
/// background database save serializing the live nest.
|
||||
/// </summary>
|
||||
private bool IsNestJobBusy() =>
|
||||
nestingInProgress
|
||||
|| Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| databaseSaveInProgress;
|
||||
|
||||
/// <summary>Returns the chosen path, or null on cancel. Overwrite consent is the dialog prompt.</summary>
|
||||
internal virtual string ShowReportSaveDialog(string suggestedFileName)
|
||||
{
|
||||
using var dlg = new SaveFileDialog
|
||||
{
|
||||
Filter = "PDF report (*.pdf)|*.pdf",
|
||||
FileName = suggestedFileName,
|
||||
AddExtension = true,
|
||||
DefaultExt = ".pdf",
|
||||
OverwritePrompt = true,
|
||||
};
|
||||
return dlg.ShowDialog(this) == DialogResult.OK ? dlg.FileName : null;
|
||||
}
|
||||
|
||||
/// <summary>Failure notice naming the fixed target; internal so tests observe it without dialogs.</summary>
|
||||
internal virtual void ReportFailure(
|
||||
EditNestForm.NestReportTargets targets,
|
||||
Exception error) =>
|
||||
MessageBox.Show(this,
|
||||
$"Could not export the nest report{(targets == null ? "" : $" for '{targets.Nest.Name}'")}: {error.Message}",
|
||||
"Export Nest Report", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
|
||||
/// <summary>Success notice; internal so tests observe completion without dialogs.</summary>
|
||||
internal virtual void ReportSuccess(string destination) =>
|
||||
MessageBox.Show(this, $"Nest report saved to:\n\n{destination}", "Export Nest Report",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
|
||||
private void Exit_Click(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
#endregion File Menu Events
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Actions;
|
||||
using OpenNest.Collections;
|
||||
using OpenNest.Controls;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Engine;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Engine.Jobs;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
#region Nest Menu Events
|
||||
|
||||
private void Import_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.Import();
|
||||
}
|
||||
|
||||
private void ShapeLibrary_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
var form = new ShapeLibraryForm(activeForm.Nest.Drawings.Select(d => d.Name));
|
||||
form.ShowDialog();
|
||||
|
||||
var drawings = form.GetDrawings();
|
||||
if (drawings.Count == 0)
|
||||
return;
|
||||
|
||||
drawings.ForEach(d => activeForm.Nest.Drawings.Add(d));
|
||||
activeForm.UpdateDrawingList();
|
||||
}
|
||||
|
||||
private void DrawingLibrary_Click(object sender, EventArgs e)
|
||||
{
|
||||
activeForm?.AddFromDrawingLibrary();
|
||||
}
|
||||
|
||||
private void NestMenu_Opening(object sender, EventArgs e)
|
||||
{
|
||||
// The drawing library lives on the nest server; File mode has nothing to browse.
|
||||
var databaseMode = NestStorage.Settings.IsDatabaseMode;
|
||||
mnuNest.DropDown.ShowItemToolTips = true;
|
||||
mnuNestDrawingLibrary.Enabled = activeForm != null && databaseMode;
|
||||
mnuNestDrawingLibrary.ToolTipText = databaseMode ? "" : NestStorage.DatabaseModeOnly;
|
||||
}
|
||||
|
||||
private void EditNest_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ShowNestInfoEditor();
|
||||
}
|
||||
|
||||
private PlateView[] ViewsOf(Nest nest) =>
|
||||
MdiChildren
|
||||
.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, nest))
|
||||
.Select(form => form.PlateView)
|
||||
.ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// True while a nest run or fill may be reading <paramref name="nest"/>'s clamps.
|
||||
/// Clamps feed nesting and validation: never change them under a running operation.
|
||||
/// </summary>
|
||||
private bool ClampsInUse(Nest nest) =>
|
||||
nestingInProgress
|
||||
|| Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| ViewsOf(nest).Any(v => v.IsFillInProgress);
|
||||
|
||||
private void Clamps_Click(object sender, EventArgs e)
|
||||
{
|
||||
var editForm = activeForm;
|
||||
if (editForm == null)
|
||||
return;
|
||||
|
||||
var nest = editForm.Nest;
|
||||
var views = ViewsOf(nest);
|
||||
if (ClampsInUse(nest) || views.Any(v => v.Actions.CurrentAction?.IsBusy() == true))
|
||||
{
|
||||
MessageBox.Show(
|
||||
this,
|
||||
"Finish or cancel the current nesting or plate action first.",
|
||||
"Clamps",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var defaults = LoadSavedClampSettings(out _);
|
||||
using var form = new ClampsForm(nest.PlateDefaults.ClampRail, nest.Units, defaults);
|
||||
if (form.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
form.Editor.ApplyTo(nest);
|
||||
foreach (var view in views)
|
||||
view.Invalidate();
|
||||
}
|
||||
|
||||
private void RemoveEmptyPlates_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.Nest.Plates.RemoveEmptyPlates();
|
||||
}
|
||||
|
||||
private void LoadFirstPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateManager.LoadFirst();
|
||||
}
|
||||
|
||||
private void LoadLastPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateManager.LoadLast();
|
||||
}
|
||||
|
||||
private void LoadPreviousPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateManager.LoadPrevious();
|
||||
}
|
||||
|
||||
private void LoadNextPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateManager.LoadNext();
|
||||
}
|
||||
|
||||
private RemnantViewerForm remnantViewer;
|
||||
|
||||
private void ShowRemnants_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm?.PlateView?.Plate == null)
|
||||
return;
|
||||
|
||||
var plate = activeForm.PlateView.Plate;
|
||||
|
||||
// Minimum remnant dimension = smallest part bbox dimension on the plate.
|
||||
var minDim = 0.0;
|
||||
var nest = activeForm.Nest;
|
||||
if (nest != null)
|
||||
{
|
||||
foreach (var drawing in nest.Drawings)
|
||||
{
|
||||
var bbox = drawing.Program.BoundingBox();
|
||||
var dim = System.Math.Min(bbox.Width, bbox.Length);
|
||||
if (minDim == 0 || dim < minDim)
|
||||
minDim = dim;
|
||||
}
|
||||
}
|
||||
|
||||
var finder = RemnantFinder.FromPlate(plate);
|
||||
|
||||
if (remnantViewer == null || remnantViewer.IsDisposed)
|
||||
{
|
||||
remnantViewer = new RemnantViewerForm();
|
||||
remnantViewer.Owner = this;
|
||||
|
||||
// Position next to the main form's right edge.
|
||||
var screen = Screen.FromControl(this);
|
||||
remnantViewer.Location = new Point(
|
||||
System.Math.Min(Right, screen.WorkingArea.Right - remnantViewer.Width),
|
||||
Top
|
||||
);
|
||||
}
|
||||
|
||||
remnantViewer.LoadRemnants(finder, minDim, activeForm.PlateView);
|
||||
remnantViewer.Show();
|
||||
remnantViewer.BringToFront();
|
||||
}
|
||||
|
||||
private async void RunAutoNest_Click(object sender, EventArgs e)
|
||||
{
|
||||
var target = activeForm;
|
||||
if (target == null || target.IsDisposed || target.PlateView.Plate == null)
|
||||
return;
|
||||
var views = MdiChildren.OfType<EditNestForm>()
|
||||
.Where(f => ReferenceEquals(f.Nest, target.Nest)).Select(f => f.PlateView).ToArray();
|
||||
if (nestingInProgress || Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| views.Any(v => v.IsFillInProgress || v.Actions.CurrentAction?.IsBusy() == true))
|
||||
{
|
||||
MessageBox.Show(this, "Finish or cancel the current plate operation before Auto Nest.",
|
||||
"Auto Nest", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
foreach (var view in views)
|
||||
view.SetAction(typeof(ActionSelect));
|
||||
|
||||
using var form = new AutoNestForm(target.Nest);
|
||||
if (target.Nest.PlateOptions.Count > 0)
|
||||
form.LoadPlateOptions(target.Nest.PlateOptions, target.Nest.SalvageRate);
|
||||
if (form.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var items = form.GetNestItems();
|
||||
if (!items.Any(it => it.Quantity > 0))
|
||||
return;
|
||||
var plateOptions = default(List<PlateOption>);
|
||||
if (form.OptimizePlateSize && !form.TryGetPlateOptions(out plateOptions, out var stockError))
|
||||
{
|
||||
MessageBox.Show(this, stockError, "Auto Nest", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
var engineName = form.EngineName ?? EngineSelection.EngineName;
|
||||
EngineSelection.EngineName = engineName;
|
||||
engineComboBox.SelectedItem = engineName;
|
||||
using var cts = new CancellationTokenSource();
|
||||
nestingCts = cts;
|
||||
using var progressForm = new NestProgressForm(cts, showPlateRow: true)
|
||||
{
|
||||
AllowAccept = false,
|
||||
HoldOpenUntilCompleted = true,
|
||||
};
|
||||
var receivingProgress = true;
|
||||
var progress = new Progress<NestProgress>(p =>
|
||||
{
|
||||
if (!receivingProgress || target.IsDisposed || cts.IsCancellationRequested)
|
||||
return;
|
||||
progressForm.UpdateProgress(p);
|
||||
if (p.IsOverallBest)
|
||||
target.PlateView.SetActiveParts(p.BestParts);
|
||||
target.PlateView.ActiveWorkArea = p.ActiveWorkArea;
|
||||
});
|
||||
var jobProgress = JobEngineNest.CreateProgress(engineName, progress);
|
||||
SetNestingLockout(true);
|
||||
try
|
||||
{
|
||||
var request = new NestPipelineRequest(engineName, items,
|
||||
NestStockBuilder.FromTemplate(target.PlateView.Plate, plateOptions),
|
||||
new NestJobOptions(maxPlates: 100,
|
||||
salvageRate: plateOptions?.Count > 0 ? form.SalvageRate : 0,
|
||||
minimumSalvageDimension: form.MinRemnantSize));
|
||||
|
||||
async Task<NestPipelineResult> SolveAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Task.Run(() => NestPipeline.Run(request, jobProgress, cts.Token));
|
||||
}
|
||||
finally
|
||||
{
|
||||
receivingProgress = false;
|
||||
progressForm.ShowCompleted();
|
||||
progressForm.Close();
|
||||
}
|
||||
}
|
||||
|
||||
var solve = SolveAsync();
|
||||
// Owned modal progress holds drawings/plates stable through solve and cancellation.
|
||||
if (!solve.IsCompleted)
|
||||
progressForm.ShowDialog(this);
|
||||
var result = await solve;
|
||||
cts.Token.ThrowIfCancellationRequested();
|
||||
target.PlateView.ClearPreviewParts();
|
||||
target.PlateView.ActiveWorkArea = null;
|
||||
|
||||
var allowInvalid = false;
|
||||
if (!result.IsValid)
|
||||
{
|
||||
using var review = new NestValidationForm(result.Violations, result.CanKeep);
|
||||
if (review.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
allowInvalid = true;
|
||||
}
|
||||
|
||||
var costs = result.IsValid && result.CanKeep ? NestCostSummary.FromAccepted(result) : null;
|
||||
var applied = NestPipelineCommit.ApplyToEmptyPlates(result, target.PlateManager,
|
||||
allowInvalid, cts.Token);
|
||||
if (applied.Count > 0)
|
||||
target.PlateManager.LoadAt(target.Nest.Plates.IndexOf(applied[0]));
|
||||
if (plateOptions?.Count > 0)
|
||||
{
|
||||
target.Nest.PlateOptions = plateOptions;
|
||||
target.Nest.SalvageRate = form.SalvageRate;
|
||||
}
|
||||
target.PlateView.Invalidate();
|
||||
if (allowInvalid)
|
||||
{
|
||||
target.OverlapDisplay = OverlapDisplayMode.Both;
|
||||
await target.CheckOverlapsAsync();
|
||||
}
|
||||
// D8/D9: name every clamp Auto Nest pulled as a last resort and its added time.
|
||||
var removalNotices = applied
|
||||
.Where(plate => plate.RemovedClamps.Count > 0)
|
||||
.Select(plate => $"Sheet {target.Nest.Plates.IndexOf(plate) + 1}: remove "
|
||||
+ $"{string.Join(", ", plate.RemovedClamps.OrderBy(id => id, StringComparer.Ordinal))} "
|
||||
+ $"(+{plate.ClampRemovalMinutes:0.#} min)")
|
||||
.ToList();
|
||||
if (removalNotices.Count > 0)
|
||||
MessageBox.Show(this,
|
||||
"Auto Nest removed clamps as a last resort so a part that would not "
|
||||
+ "otherwise fit could be nested:\n\n" + string.Join("\n", removalNotices),
|
||||
"Auto Nest", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
if (costs?.Basis == "supplied-cost")
|
||||
MessageBox.Show(this, $"Gross sheet cost: {costs.GrossTotal:G}\nEstimated salvage credit: {costs.SalvageCredit:G}\nNet score: {costs.NetScore:G}",
|
||||
"Auto Nest Cost", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
// Never drop demand silently: name every unplaced part and why (the drawing
|
||||
// list keeps showing the remaining quantities after this closes).
|
||||
if (result.Unplaced.Count > 0)
|
||||
MessageBox.Show(this, $"{engineName} could not place every part:\n\n"
|
||||
+ string.Join("\n", result.Unplaced.Select(u => u.Describe())),
|
||||
"Auto Nest", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
catch (OperationCanceledException) when (cts.IsCancellationRequested)
|
||||
{
|
||||
// Stop discards the entire proposal, including engines that ignore cancellation.
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, $"Nesting error: {ex.Message}", "Auto Nest",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
receivingProgress = false;
|
||||
if (!target.IsDisposed)
|
||||
{
|
||||
target.PlateView.ClearPreviewParts();
|
||||
target.PlateView.ActiveWorkArea = null;
|
||||
}
|
||||
SetNestingLockout(false);
|
||||
nestingCts = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SequenceAllPlates_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
activeForm.AutoSequenceAllPlates();
|
||||
}
|
||||
|
||||
private void ValidateNest_Click(object sender, EventArgs e)
|
||||
{
|
||||
var editForm = activeForm;
|
||||
if (editForm == null)
|
||||
return;
|
||||
|
||||
var views = MdiChildren.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, editForm.Nest))
|
||||
.Select(form => form.PlateView).ToArray();
|
||||
if (nestingInProgress || Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| views.Any(view => view.IsFillInProgress || view.Actions.CurrentAction?.IsBusy() == true))
|
||||
{
|
||||
MessageBox.Show(this, "Finish or cancel the current nesting or plate action before validating.",
|
||||
"Validate Nest", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var view in views)
|
||||
view.SetAction(typeof(ActionSelect));
|
||||
editForm.OpenValidationInspection();
|
||||
}
|
||||
|
||||
private void PostProcessor_Click(object sender, EventArgs e)
|
||||
{
|
||||
var menuItem = (ToolStripMenuItem)sender;
|
||||
var postProcessor = menuItem.Tag as IPostProcessor;
|
||||
|
||||
var editForm = activeForm;
|
||||
if (postProcessor == null || editForm == null)
|
||||
return;
|
||||
|
||||
// Closed progress windows can still have a fill awaiting its final commit.
|
||||
// Keep the nest stable throughout verification and all owned modal dialogs.
|
||||
if (MdiChildren.OfType<EditNestForm>().Any(form => form.CuttingInspection != null))
|
||||
{
|
||||
MessageBox.Show(this, "Close cutting inspection before posting. Posting will run fresh checks.",
|
||||
"Verify Nest Before Posting", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
var views = MdiChildren.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, editForm.Nest))
|
||||
.Select(form => form.PlateView).ToArray();
|
||||
if (nestingInProgress || Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| views.Any(view => view.IsFillInProgress || view.Actions.CurrentAction?.IsBusy() == true))
|
||||
{
|
||||
MessageBox.Show(this, "Finish or cancel the current nesting or plate action before posting.",
|
||||
"Verify Nest Before Posting", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var view in views)
|
||||
view.SetAction(typeof(ActionSelect));
|
||||
var nest = editForm.Nest;
|
||||
|
||||
if (postProcessor is IPostProcessorNestAware nestAware)
|
||||
nestAware.PrepareForNest(nest);
|
||||
|
||||
if (postProcessor is IConfigurablePostProcessor configurable)
|
||||
{
|
||||
using var configForm = new PostProcessorConfigForm(configurable);
|
||||
if (configForm.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
}
|
||||
|
||||
using (var verification = new PostVerificationForm(nest, postProcessor))
|
||||
{
|
||||
if (verification.ShowDialog(this) != DialogResult.OK)
|
||||
return;
|
||||
}
|
||||
|
||||
using var dialog = new SaveFileDialog();
|
||||
dialog.Filter = "CNC File (*.cnc) | *.cnc";
|
||||
dialog.FileName = nest.Name;
|
||||
|
||||
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
var path = dialog.FileName;
|
||||
|
||||
if (postProcessor is IMultiFilePostProcessor multiFile)
|
||||
{
|
||||
var files = multiFile.GetOutputFiles(nest, path);
|
||||
if (!ConfirmOverwrite(files, path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
postProcessor.Post(nest, path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ex is InvalidOperationException or IOException or UnauthorizedAccessException)
|
||||
{
|
||||
MessageBox.Show(
|
||||
this,
|
||||
ex.Message,
|
||||
postProcessor.Name,
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.Count > 1)
|
||||
MessageBox.Show(
|
||||
this,
|
||||
$"Saved {files.Count} programs, one per sheet:\n\n"
|
||||
+ string.Join("\n", files.Select(Path.GetFileName))
|
||||
+ $"\n\nin {Path.GetDirectoryName(path)}",
|
||||
postProcessor.Name,
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
postProcessor.Post(nest, path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The save dialog only checks the chosen name; ask before replacing any
|
||||
/// other existing file a multi-file post will write.
|
||||
/// </summary>
|
||||
private bool ConfirmOverwrite(IReadOnlyList<string> files, string chosenPath)
|
||||
{
|
||||
var existing = files
|
||||
.Where(f => File.Exists(f) && !string.Equals(f, chosenPath, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
if (existing.Count == 0)
|
||||
return true;
|
||||
|
||||
var answer = MessageBox.Show(
|
||||
this,
|
||||
"These files already exist and will be replaced:\n\n"
|
||||
+ string.Join("\n", existing.Select(Path.GetFileName))
|
||||
+ "\n\nReplace them?",
|
||||
"Confirm Save",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
return answer == DialogResult.Yes;
|
||||
}
|
||||
|
||||
private void CalculateNestCutTime_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
activeForm.CalculateNestCutTime();
|
||||
}
|
||||
|
||||
private void NestAssignLeadIns_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.AssignLeadInsAllPlates();
|
||||
}
|
||||
|
||||
private void NestRemoveLeadIns_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.RemoveLeadInsAllPlates();
|
||||
}
|
||||
|
||||
#endregion Nest Menu Events
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Actions;
|
||||
using OpenNest.Controls;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
#region Plate Menu Events
|
||||
|
||||
private void SetAsNestDefault_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.SetCurrentPlateAsNestDefault();
|
||||
}
|
||||
|
||||
private void AddPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.Nest.CreatePlate();
|
||||
NavigationEnableCheck();
|
||||
}
|
||||
|
||||
private void EditPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.EditPlate();
|
||||
}
|
||||
|
||||
private void RemovePlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.RemoveCurrentPlate();
|
||||
}
|
||||
|
||||
private void ResizeToFitParts_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ResizePlateToFitParts();
|
||||
UpdatePlateStatus();
|
||||
}
|
||||
|
||||
private void RotateCw_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.RotateCw();
|
||||
}
|
||||
|
||||
private void RotateCcw_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.RotateCcw();
|
||||
}
|
||||
|
||||
private void Rotate180_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.Rotate180();
|
||||
}
|
||||
|
||||
private void OpenInExternalCad_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.OpenCurrentPlate();
|
||||
}
|
||||
|
||||
private void CalculatePlateCutTime_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
activeForm.CalculateCurrentPlateCutTime();
|
||||
}
|
||||
|
||||
private void AutoSequenceCurrentPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
activeForm.AutoSequenceCurrentPlate();
|
||||
}
|
||||
|
||||
private void ManualSequenceParts_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null || activeForm.PlateView.Plate.Parts.Count < 2)
|
||||
return;
|
||||
|
||||
activeForm.PlateView.SetAction(typeof(ActionSetSequence));
|
||||
}
|
||||
|
||||
private void CutOff_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
activeForm.PlateView.SetAction(typeof(ActionCutOff));
|
||||
}
|
||||
|
||||
private void AutomaticCutOff_Click(object sender, EventArgs e) => ShowAutomaticCutOff(allPlates: false);
|
||||
|
||||
private void NestAutomaticCutOff_Click(object sender, EventArgs e) => ShowAutomaticCutOff(allPlates: true);
|
||||
|
||||
private void ShowAutomaticCutOff(bool allPlates)
|
||||
{
|
||||
var editForm = activeForm;
|
||||
var view = editForm?.PlateView;
|
||||
if (view?.Plate == null)
|
||||
return;
|
||||
|
||||
var nest = editForm.Nest;
|
||||
var views = MdiChildren.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, nest))
|
||||
.Select(form => form.PlateView).ToArray();
|
||||
// A closed progress window can still have a fill awaiting completion/commit.
|
||||
bool IsBusy() => nestingInProgress || Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| views.Any(v => v.IsFillInProgress || v.Actions.CurrentAction?.IsBusy() == true);
|
||||
if (IsBusy())
|
||||
{
|
||||
MessageBox.Show(this, "Finish or cancel the current nesting or plate action first.",
|
||||
"Automatic Scrap Cutoffs", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var plateView in views)
|
||||
plateView.SetAction(typeof(ActionSelect));
|
||||
using var form = allPlates
|
||||
? new AutomaticCutOffForm(view, nest, IsBusy)
|
||||
: new AutomaticCutOffForm(view, nest.Units, IsBusy);
|
||||
form.ShowDialog(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, ex.Message, "Automatic Scrap Cutoffs",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!view.IsDisposed)
|
||||
view.ClearPreviewParts();
|
||||
}
|
||||
}
|
||||
|
||||
private void PlatePlanCutting_Click(object sender, EventArgs e) => ShowCuttingPlan(allPlates: false);
|
||||
|
||||
private void NestPlanCutting_Click(object sender, EventArgs e) => ShowCuttingPlan(allPlates: true);
|
||||
|
||||
internal bool IsCuttingPlanBusy(PlateView[] views) => nestingInProgress
|
||||
|| Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| views.Any(v => v.IsFillInProgress || v.Actions.CurrentAction?.IsBusy() == true);
|
||||
|
||||
// Native tests capture this notice without opening a modal dialog in session zero.
|
||||
internal System.Action<string, string> CuttingPlanNotice { get; set; }
|
||||
|
||||
private void ShowCuttingPlanNotice(string message, string caption)
|
||||
{
|
||||
if (CuttingPlanNotice != null)
|
||||
CuttingPlanNotice(message, caption);
|
||||
else
|
||||
MessageBox.Show(this, message, caption, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
private void ShowCuttingPlan(bool allPlates)
|
||||
{
|
||||
if (MdiChildren.OfType<EditNestForm>().Any(form => form.CuttingInspection != null && !ReferenceEquals(form, activeForm)))
|
||||
return;
|
||||
var editForm = activeForm;
|
||||
if (editForm?.PlateView?.Plate == null)
|
||||
return;
|
||||
|
||||
var nest = editForm.Nest;
|
||||
var views = MdiChildren.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, nest))
|
||||
.Select(form => form.PlateView).ToArray();
|
||||
// A closed progress window can still have a fill awaiting completion/commit.
|
||||
if (IsCuttingPlanBusy(views))
|
||||
{
|
||||
ShowCuttingPlanNotice("Finish or cancel the current nesting or plate action first.", "Plan Cutting");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var plateView in views)
|
||||
plateView.SetAction(typeof(ActionSelect));
|
||||
editForm.OpenCuttingInspection(allPlates);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(this, ex.Message, "Plan Cutting", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlateAssignLeadIns_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.AssignLeadIns_Click(sender, e);
|
||||
}
|
||||
|
||||
private void PlatePlaceLeadIn_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlaceLeadIn_Click(sender, e);
|
||||
}
|
||||
|
||||
private void PlateRemoveLeadIns_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.RemoveLeadIns_Click(sender, e);
|
||||
}
|
||||
|
||||
#endregion Plate Menu Events
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Collections;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
private void EnableCheck()
|
||||
{
|
||||
NavigationEnableCheck();
|
||||
|
||||
var hasValue = activeForm != null;
|
||||
|
||||
mnuFileSave.Enabled = hasValue && !databaseSaveInProgress;
|
||||
btnSave.Enabled = hasValue && !databaseSaveInProgress;
|
||||
btnSaveAs.Enabled = hasValue && !databaseSaveInProgress;
|
||||
mnuFileSaveAs.Enabled = hasValue && !databaseSaveInProgress;
|
||||
exportNestMenu.Enabled = hasValue;
|
||||
mnuFileExport.Enabled = hasValue;
|
||||
mnuFileExportAll.Enabled = hasValue;
|
||||
mnuFileExportNestReport.Enabled = hasValue && !databaseSaveInProgress;
|
||||
mnuEdit.Visible = hasValue;
|
||||
mnuView.Visible = hasValue;
|
||||
mnuNest.Visible = hasValue;
|
||||
mnuPlate.Visible = hasValue;
|
||||
mnuWindow.Visible = hasValue;
|
||||
mnuToolsMeasureArea.Visible = hasValue;
|
||||
mnuToolsSaveCurrentAsDefaults.Visible = hasValue;
|
||||
|
||||
toolStripMenuItem14.Visible = hasValue;
|
||||
mnuSetOffsetIncrement.Visible = hasValue;
|
||||
mnuSetRotationIncrement.Visible = hasValue;
|
||||
toolStripMenuItem15.Visible = hasValue;
|
||||
}
|
||||
|
||||
private void NavigationEnableCheck()
|
||||
{
|
||||
if (activeForm == null)
|
||||
{
|
||||
mnuNestPreviousPlate.Enabled = false;
|
||||
mnuNestNextPlate.Enabled = false;
|
||||
mnuNestFirstPlate.Enabled = false;
|
||||
mnuNestLastPlate.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mnuNestPreviousPlate.Enabled = !activeForm.PlateManager.IsFirst;
|
||||
mnuNestNextPlate.Enabled = !activeForm.PlateManager.IsLast;
|
||||
mnuNestFirstPlate.Enabled =
|
||||
activeForm.PlateManager.Count > 0 && !activeForm.PlateManager.IsFirst;
|
||||
mnuNestLastPlate.Enabled =
|
||||
activeForm.PlateManager.Count > 0 && !activeForm.PlateManager.IsLast;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetNestingLockout(bool locked)
|
||||
{
|
||||
nestingInProgress = locked;
|
||||
|
||||
// Disable nesting-related menus while running
|
||||
mnuNest.Enabled = !locked;
|
||||
mnuPlate.Enabled = !locked;
|
||||
|
||||
// Lock plate navigation
|
||||
mnuNestPreviousPlate.Enabled =
|
||||
!locked && activeForm != null && !activeForm.PlateManager.IsFirst;
|
||||
mnuNestNextPlate.Enabled =
|
||||
!locked && activeForm != null && !activeForm.PlateManager.IsLast;
|
||||
mnuNestFirstPlate.Enabled =
|
||||
!locked
|
||||
&& activeForm != null
|
||||
&& activeForm.PlateManager.Count > 0
|
||||
&& !activeForm.PlateManager.IsFirst;
|
||||
mnuNestLastPlate.Enabled =
|
||||
!locked
|
||||
&& activeForm != null
|
||||
&& activeForm.PlateManager.Count > 0
|
||||
&& !activeForm.PlateManager.IsLast;
|
||||
}
|
||||
|
||||
private void UpdateLocationStatus()
|
||||
{
|
||||
if (activeForm == null)
|
||||
{
|
||||
locationStatusLabel.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
locationStatusLabel.Text = string.Format(
|
||||
"Location: [{0}, {1}]",
|
||||
activeForm.PlateView.CurrentPoint.X.ToString("n4"),
|
||||
activeForm.PlateView.CurrentPoint.Y.ToString("n4")
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdatePlateStatus()
|
||||
{
|
||||
if (activeForm == null)
|
||||
{
|
||||
plateIndexStatusLabel.Text = string.Empty;
|
||||
plateSizeStatusLabel.Text = string.Empty;
|
||||
plateQtyStatusLabel.Text = string.Empty;
|
||||
plateUtilStatusLabel.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
plateIndexStatusLabel.Text = string.Format(
|
||||
"Plate: {0} of {1}",
|
||||
activeForm.PlateManager.CurrentIndex + 1,
|
||||
activeForm.PlateManager.Count
|
||||
);
|
||||
|
||||
plateSizeStatusLabel.Text = string.Format("Size: {0}", activeForm.PlateView.Plate.Size);
|
||||
|
||||
plateQtyStatusLabel.Text = string.Format(
|
||||
"Qty: {0}",
|
||||
activeForm.PlateView.Plate.Quantity
|
||||
);
|
||||
|
||||
plateUtilStatusLabel.Text = string.Format(
|
||||
"Util: {0:P1}",
|
||||
activeForm.PlateView.Plate.Utilization()
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdateSelectionStatus()
|
||||
{
|
||||
if (activeForm == null || activeForm.PlateView.SelectedParts.Count == 0)
|
||||
{
|
||||
selectionStatusLabel.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
var selected = activeForm.PlateView.SelectedParts;
|
||||
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
var box = selected[0].BoundingBox;
|
||||
selectionStatusLabel.Text = string.Format(
|
||||
"Selected: [{0}, {1}] {2} x {3}",
|
||||
box.X.ToString("n4"),
|
||||
box.Y.ToString("n4"),
|
||||
box.Width.ToString("n4"),
|
||||
box.Length.ToString("n4")
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bounds = selected.Select(p => p.BasePart).ToList().GetBoundingBox();
|
||||
selectionStatusLabel.Text = string.Format(
|
||||
"Selected ({0}): [{1}, {2}] {3} x {4}",
|
||||
selected.Count,
|
||||
bounds.X.ToString("n4"),
|
||||
bounds.Y.ToString("n4"),
|
||||
bounds.Width.ToString("n4"),
|
||||
bounds.Length.ToString("n4")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
UpdateLocationStatus();
|
||||
UpdatePlateStatus();
|
||||
UpdateSelectionStatus();
|
||||
}
|
||||
|
||||
private void EngineComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (engineComboBox.SelectedItem is string name)
|
||||
EngineSelection.EngineName = name;
|
||||
}
|
||||
|
||||
private void UpdateLocationMode()
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
if (clickUpdateLocation)
|
||||
{
|
||||
clickUpdateLocation = true;
|
||||
locationStatusLabel.ForeColor = Color.Gray;
|
||||
activeForm.PlateView.MouseMove -= PlateView_MouseMove;
|
||||
activeForm.PlateView.MouseClick += PlateView_MouseClick;
|
||||
}
|
||||
else
|
||||
{
|
||||
clickUpdateLocation = false;
|
||||
locationStatusLabel.ForeColor = Color.Black;
|
||||
activeForm.PlateView.MouseMove += PlateView_MouseMove;
|
||||
activeForm.PlateView.MouseClick -= PlateView_MouseClick;
|
||||
}
|
||||
}
|
||||
|
||||
#region Statusbar Events
|
||||
|
||||
private void LocationStatusLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
clickUpdateLocation = !clickUpdateLocation;
|
||||
UpdateLocationMode();
|
||||
}
|
||||
|
||||
#endregion Statusbar Events
|
||||
|
||||
#region PlateView Events
|
||||
|
||||
private void PlateView_PartAdded(object sender, ItemAddedEventArgs<Part> e) =>
|
||||
UpdatePlateStatus();
|
||||
|
||||
private void PlateView_PartRemoved(object sender, ItemRemovedEventArgs<Part> e) =>
|
||||
UpdatePlateStatus();
|
||||
|
||||
private void PlateView_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
UpdateLocationStatus();
|
||||
}
|
||||
|
||||
private void PlateView_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
UpdateLocationStatus();
|
||||
}
|
||||
|
||||
private void PlateView_StatusChanged(object sender, EventArgs e)
|
||||
{
|
||||
statusLabel1.Text = activeForm.PlateView.Status;
|
||||
}
|
||||
|
||||
private void PlateView_SelectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateSelectionStatus();
|
||||
}
|
||||
|
||||
#endregion PlateView Events
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Actions;
|
||||
using OpenNest.Data;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
#region Tools Menu Events
|
||||
|
||||
private void MeasureArea_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
activeForm.PlateView.SetAction(typeof(ActionSelectArea));
|
||||
}
|
||||
|
||||
private void BestFitViewer_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
var plate = activeForm.PlateView.Plate;
|
||||
var drawings = activeForm.Nest.Drawings;
|
||||
|
||||
if (drawings.Count == 0)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"No drawings available.",
|
||||
"Best-Fit Viewer",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var form = new BestFitViewerForm(drawings, plate, activeForm.Nest.Units))
|
||||
{
|
||||
if (form.ShowDialog(this) == DialogResult.OK && form.SelectedResult != null)
|
||||
{
|
||||
var parts =
|
||||
form.SelectedParts
|
||||
?? form.SelectedResult.BuildSourceParts(form.SelectedDrawing);
|
||||
activeForm.PlateView.SetAction(typeof(ActionClone), parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpandSpacing_Click(object sender, EventArgs e)
|
||||
{
|
||||
var editForm = activeForm;
|
||||
if (editForm == null)
|
||||
return;
|
||||
|
||||
// The search reads the plate on a worker, so a nest or fill that can still
|
||||
// add parts must finish first.
|
||||
if (IsExpandSpacingBusy(editForm))
|
||||
{
|
||||
ShowExpandSpacingNotice(
|
||||
"Finish or cancel the current nesting or fill before Expand Spacing."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Complex layouts can search for minutes; run behind a Cancel window.
|
||||
var expanded = editForm.PlateView.ExpandSelected(work =>
|
||||
OperationProgressForm.Run(
|
||||
this,
|
||||
"Expand Spacing",
|
||||
"Expanding spacing between the selected parts...",
|
||||
work
|
||||
)
|
||||
);
|
||||
|
||||
if (!expanded)
|
||||
ShowExpandSpacingNotice("Select at least two parts on the plate to expand.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True while whole-job nesting or a fill on any view of the nest can still
|
||||
/// change its plates. Unlike the other plate commands' guard, a busy current
|
||||
/// action does not count: ActionSelect reports busy whenever parts are
|
||||
/// selected, and Expand Spacing needs a selection.
|
||||
/// </summary>
|
||||
internal bool IsExpandSpacingBusy(EditNestForm editForm) =>
|
||||
nestingInProgress
|
||||
|| Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| MdiChildren
|
||||
.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, editForm.Nest))
|
||||
.Any(form => form.PlateView.IsFillInProgress);
|
||||
|
||||
// Native tests capture this notice without opening a modal dialog in session zero.
|
||||
internal System.Action<string> ExpandSpacingNotice { get; set; }
|
||||
|
||||
private void ShowExpandSpacingNotice(string message)
|
||||
{
|
||||
if (ExpandSpacingNotice != null)
|
||||
ExpandSpacingNotice(message);
|
||||
else
|
||||
MessageBox.Show(
|
||||
this,
|
||||
message,
|
||||
"Expand Spacing",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
|
||||
private void PatternTile_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
if (activeForm.Nest.Drawings.Count == 0)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"No drawings available.",
|
||||
"Pattern Tile",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var form = new PatternTileForm(activeForm.Nest))
|
||||
{
|
||||
if (form.ShowDialog(this) != DialogResult.OK || form.Result == null)
|
||||
return;
|
||||
|
||||
var result = form.Result;
|
||||
|
||||
if (result.Target == PatternTileTarget.CurrentPlate)
|
||||
{
|
||||
activeForm.PlateView.Plate.Parts.Clear();
|
||||
|
||||
foreach (var part in result.Parts)
|
||||
activeForm.PlateView.Plate.Parts.Add(part);
|
||||
|
||||
activeForm.PlateView.ZoomToFit();
|
||||
}
|
||||
else
|
||||
{
|
||||
var plate = activeForm.Nest.CreatePlate();
|
||||
plate.Size = result.PlateSize;
|
||||
|
||||
foreach (var part in result.Parts)
|
||||
plate.Parts.Add(part);
|
||||
|
||||
activeForm.PlateManager.LoadLast();
|
||||
}
|
||||
|
||||
activeForm.Nest.UpdateDrawingQuantities();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetOffsetIncrement_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
var form = new SetValueForm();
|
||||
form.Text = "Set Offset Increment";
|
||||
form.Value = activeForm.PlateView.OffsetIncrementDistance;
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
activeForm.PlateView.OffsetIncrementDistance = form.Value;
|
||||
}
|
||||
|
||||
private void SetRotationIncrement_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
var form = new SetValueForm();
|
||||
form.Text = "Set Rotation Increment";
|
||||
form.Value = activeForm.PlateView.RotateIncrementAngle;
|
||||
form.Maximum = 360;
|
||||
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
activeForm.PlateView.RotateIncrementAngle = form.Value;
|
||||
}
|
||||
|
||||
private void Options_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = new OptionsForm();
|
||||
form.ShowDialog();
|
||||
}
|
||||
|
||||
private void NestDefaults_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var form = new NestDefaultsForm(LoadSavedNestDefaults(out _)))
|
||||
{
|
||||
if (form.ShowDialog(this) == DialogResult.OK)
|
||||
form.GetDefaults().Save(NestDefaults.DefaultPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveCurrentAsDefaults_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
NestDefaults
|
||||
.FromPlate(activeForm.Nest.Units, activeForm.PlateView.Plate)
|
||||
.Save(NestDefaults.DefaultPath);
|
||||
}
|
||||
|
||||
private void MachineConfig_Click(object sender, EventArgs e)
|
||||
{
|
||||
var appDataPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"OpenNest",
|
||||
"Machines"
|
||||
);
|
||||
var provider = new LocalJsonProvider(appDataPath);
|
||||
provider.EnsureDefaults();
|
||||
using (var form = new MachineConfigForm(provider))
|
||||
{
|
||||
form.ShowDialog(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void AlignLeft_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.Left);
|
||||
}
|
||||
|
||||
private void AlignRight_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.Right);
|
||||
}
|
||||
|
||||
private void AlignTop_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.Top);
|
||||
}
|
||||
|
||||
private void AlignBottom_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.Bottom);
|
||||
}
|
||||
|
||||
private void AlignVertical_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.Vertically);
|
||||
}
|
||||
|
||||
private void AlignHorizontal_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.Horizontally);
|
||||
}
|
||||
|
||||
private void EvenlySpaceHorizontally_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.EvenlySpaceHorizontally);
|
||||
}
|
||||
|
||||
private void EvenlySpaceVertically_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.AlignSelected(AlignType.EvenlySpaceVertically);
|
||||
}
|
||||
|
||||
#endregion Tools Menu Events
|
||||
|
||||
private void centerPartsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var plateCenter = this.activeForm.PlateView.Plate.BoundingBox(false).Center;
|
||||
var partsCenter = this.activeForm.PlateView.Parts.GetBoundingBox().Center;
|
||||
|
||||
var offset = plateCenter - partsCenter;
|
||||
|
||||
foreach (var part in this.activeForm.PlateView.Parts)
|
||||
{
|
||||
part.Offset(offset);
|
||||
}
|
||||
|
||||
activeForm.PlateView.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenNest.Actions;
|
||||
using OpenNest.Diagnostics;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
#region Edit Menu Events
|
||||
|
||||
private void EditCut_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
var selectedParts = activeForm.PlateView.SelectedParts;
|
||||
|
||||
if (selectedParts.Count > 0)
|
||||
{
|
||||
var partsToClone = selectedParts.Select(p => p.BasePart).ToList();
|
||||
activeForm.PlateView.SetAction(typeof(ActionClone), partsToClone);
|
||||
|
||||
foreach (var part in partsToClone)
|
||||
activeForm.PlateView.Plate.Parts.Remove(part);
|
||||
}
|
||||
}
|
||||
|
||||
private void EditPaste_Click(object sender, EventArgs e) { }
|
||||
|
||||
private void EditCopy_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
var selectedParts = activeForm.PlateView.SelectedParts;
|
||||
|
||||
if (selectedParts.Count > 0)
|
||||
{
|
||||
var partsToClone = selectedParts.Select(p => p.BasePart).ToList();
|
||||
activeForm.PlateView.SetAction(typeof(ActionClone), partsToClone);
|
||||
}
|
||||
}
|
||||
|
||||
private void EditSelectAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.SelectAllParts();
|
||||
}
|
||||
|
||||
#endregion Edit Menu Events
|
||||
|
||||
#region View Menu Events
|
||||
|
||||
private void OverlapStateChanged(object sender, EventArgs e) => UpdateOverlapMenu();
|
||||
private void OverlapMenu_Opening(object sender, EventArgs e) => UpdateOverlapMenu();
|
||||
|
||||
private void UpdateOverlapMenu()
|
||||
{
|
||||
var hasPlate = activeForm != null && !activeForm.IsDisposed && activeForm.PlateView.Plate != null;
|
||||
mnuViewOverlapCheck.Enabled = hasPlate;
|
||||
mnuOverlapCheckActive.Enabled = hasPlate;
|
||||
mnuOverlapCancel.Enabled = hasPlate && activeForm.PlateView.IsOverlapCheckRunning;
|
||||
mnuOverlapDisplay.Enabled = hasPlate;
|
||||
mnuOverlapOff.Enabled = hasPlate;
|
||||
mnuOverlapAreas.Enabled = hasPlate;
|
||||
mnuOverlapCentroids.Enabled = hasPlate;
|
||||
mnuOverlapBoth.Enabled = hasPlate;
|
||||
mnuOverlapOff.Checked = hasPlate && activeForm.OverlapDisplay == OverlapDisplayMode.Off;
|
||||
mnuOverlapAreas.Checked = hasPlate && activeForm.OverlapDisplay == OverlapDisplayMode.Areas;
|
||||
mnuOverlapCentroids.Checked = hasPlate && activeForm.OverlapDisplay == OverlapDisplayMode.Centroids;
|
||||
mnuOverlapBoth.Checked = hasPlate && activeForm.OverlapDisplay == OverlapDisplayMode.Both;
|
||||
}
|
||||
|
||||
private async void CheckOverlaps_Click(object sender, EventArgs e)
|
||||
{
|
||||
var form = activeForm;
|
||||
if (form != null)
|
||||
await form.CheckOverlapsAsync();
|
||||
}
|
||||
|
||||
private void CancelOverlapCheck_Click(object sender, EventArgs e) => activeForm?.CancelOverlapCheck();
|
||||
|
||||
private void OverlapOff_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm != null)
|
||||
activeForm.OverlapDisplay = OverlapDisplayMode.Off;
|
||||
}
|
||||
|
||||
private void OverlapAreas_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm != null)
|
||||
activeForm.OverlapDisplay = OverlapDisplayMode.Areas;
|
||||
}
|
||||
|
||||
private void OverlapCentroids_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm != null)
|
||||
activeForm.OverlapDisplay = OverlapDisplayMode.Centroids;
|
||||
}
|
||||
|
||||
private void OverlapBoth_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm != null)
|
||||
activeForm.OverlapDisplay = OverlapDisplayMode.Both;
|
||||
}
|
||||
|
||||
private void ToggleDrawRapids_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ToggleRapid();
|
||||
mnuViewDrawRapids.Checked = activeForm.PlateView.DrawRapid;
|
||||
}
|
||||
|
||||
private void ToggleDrawPiercePoints_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.TogglePiercePoints();
|
||||
mnuViewDrawPiercePoints.Checked = activeForm.PlateView.DrawPiercePoints;
|
||||
}
|
||||
|
||||
private void ToggleDrawBounds_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ToggleDrawBounds();
|
||||
mnuViewDrawBounds.Checked = activeForm.PlateView.DrawBounds;
|
||||
}
|
||||
|
||||
private void ToggleDrawOffset_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ToggleDrawOffset();
|
||||
mnuViewDrawOffset.Checked = activeForm.PlateView.DrawOffset;
|
||||
}
|
||||
|
||||
private void ToggleDrawCutDirection_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.ToggleCutDirection();
|
||||
mnuViewDrawCutDirection.Checked = activeForm.PlateView.DrawCutDirection;
|
||||
}
|
||||
|
||||
private void ZoomToArea_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.SetAction(typeof(ActionZoomWindow));
|
||||
}
|
||||
|
||||
private void ZoomToFit_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.ZoomToFit();
|
||||
}
|
||||
|
||||
private void ZoomToPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.ZoomToPlate();
|
||||
}
|
||||
|
||||
private void ZoomToSelected_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
activeForm.PlateView.ZoomToSelected();
|
||||
}
|
||||
|
||||
private void ZoomIn_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
var pt = new Point(activeForm.PlateView.Width / 2, activeForm.PlateView.Height / 2);
|
||||
|
||||
activeForm.PlateView.ZoomToControlPoint(pt, ZoomInFactor);
|
||||
}
|
||||
|
||||
private void ZoomOut_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
return;
|
||||
|
||||
var pt = new Point(activeForm.PlateView.Width / 2, activeForm.PlateView.Height / 2);
|
||||
|
||||
activeForm.PlateView.ZoomToControlPoint(pt, ZoomOutFactor);
|
||||
}
|
||||
|
||||
#endregion View Menu Events
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -44,6 +44,10 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<Compile Update="Forms\MainForm.File.cs;Forms\MainForm.Nest.cs;Forms\MainForm.Plate.cs;Forms\MainForm.Status.cs;Forms\MainForm.Tools.cs;Forms\MainForm.View.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
|
||||
Reference in new issue
Block a user