Merge pull request #5419 from larsbrubaker/main

main
This commit is contained in:
Lars Brubaker 2023-03-20 10:39:07 -07:00 committed by GitHub
commit 8474ef2ac6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 529 additions and 571 deletions

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2022, Lars Brubaker, John Lewin
Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -1033,18 +1033,18 @@ namespace MatterHackers.MatterControl
}));
}
this.Library.DesignAppsCollectionContainer = new DesignAppsCollectionContainer();
this.Library.BundledPartsCollectionContainer = new BundledPartsCollectionContainer();
// this.Library.LibraryCollectionContainer.HeaderMarkdown = "Here you can find the collection of libraries you can use".Localize();
this.Library.RegisterContainer(
new DynamicContainerLink(
"Design Apps".Localize(),
"Bundled".Localize(),
StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")),
StaticData.Instance.LoadIcon(Path.Combine("Library", "design_apps_icon.png")),
() => this.Library.DesignAppsCollectionContainer)
{
() => this.Library.BundledPartsCollectionContainer)
{
IsReadOnly = true
});
});
if (File.Exists(ApplicationDataStorage.Instance.CustomLibraryFoldersPath))
{
@ -1654,30 +1654,17 @@ namespace MatterHackers.MatterControl
public event EventHandler UiHintChanged;
public string ShortProductName
{
get
{
if (this.IsMatterControlPro())
{
return "MatterControl Pro";
}
return "MatterControl";
}
}
public string ProductName
{
get
{
if (this.IsMatterControlPro())
{
return "MatterControl Pro Edition";
return OemSettings.Instance.RegisteredProductName;
}
return "MatterHackers: MatterControl";
}
return OemSettings.Instance.UnregisteredProductName;
}
}
public void SwitchToPurchasedLibrary()

View file

@ -69,7 +69,7 @@ namespace MatterHackers.MatterControl
Selectable = false
});
row.AddChild(new TextWidget(ApplicationController.Instance.ShortProductName, textColor: theme.TextColor)
row.AddChild(new TextWidget(ApplicationController.Instance.ProductName, textColor: theme.TextColor)
{
VAnchor = VAnchor.Center
});
@ -176,7 +176,7 @@ namespace MatterHackers.MatterControl
graphics.DrawString("64", imageBuffer.Width / 2, imageBuffer.Height / 2, 8, Agg.Font.Justification.Center, Agg.Font.Baseline.BoundsCenter, color: menuTheme.PrimaryAccentColor);
}
menuItem = popupMenu.CreateMenuItem("About".Localize() + " MatterControl", imageBuffer);
menuItem = popupMenu.CreateMenuItem("About".Localize() + " " + ApplicationController.Instance.ProductName, imageBuffer);
menuItem.Click += (s, e) => ApplicationController.Instance.ShowAboutPage();
return popupMenu;
}

View file

@ -837,9 +837,9 @@ namespace MatterHackers.MatterControl
ToggleFuzzyOperation(),
}
},
new OperationGroup("Design Apps")
new OperationGroup("Constraints")
{
TitleGetter = () => "Design Apps".Localize(),
TitleGetter = () => "Constraints".Localize(),
Visible = OperationGroup.GetVisible("Path", false),
Operations = new List<SceneOperation>()
{

View file

@ -37,6 +37,7 @@ using MatterHackers.Agg.UI;
using MatterHackers.ImageProcessing;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.SettingsManagement;
using MatterHackers.VectorMath;
using Newtonsoft.Json;
@ -73,7 +74,7 @@ namespace MatterHackers.MatterControl
{
HAnchor = HAnchor.Center | HAnchor.Fit
};
productTitle.AddChild(new TextWidget("MatterControl".Localize(), textColor: theme.TextColor, pointSize: 20) { Margin = new BorderDouble(right: 3) });
productTitle.AddChild(new TextWidget(ApplicationController.Instance.ProductName, textColor: theme.TextColor, pointSize: 20) { Margin = new BorderDouble(right: 3) });
productTitle.AddChild(new TextWidget("TM".Localize(), textColor: theme.TextColor, pointSize: 7) { VAnchor = VAnchor.Top });
altHeadingRow.AddChild(productInfo);
@ -105,7 +106,7 @@ namespace MatterHackers.MatterControl
contentRow.AddChild(
new WrappedTextWidget(
"MatterControl is made possible by the team at MatterHackers and other open source software".Localize() + ":",
"{0} is made possible by the team at MatterHackers and other open source software".Localize().FormatWith(ApplicationController.Instance.ProductName) + ":",
pointSize: theme.DefaultFontSize,
textColor: theme.TextColor)
{

View file

@ -43,6 +43,7 @@ using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.PrintQueue;
using MatterHackers.MatterControl.SettingsManagement;
using Newtonsoft.Json;
namespace MatterHackers.MatterControl.PrintHistory
@ -314,7 +315,7 @@ namespace MatterHackers.MatterControl.PrintHistory
}
else // upsell MatterControl Pro
{
string text = "Exporting print history is a MatterControl Pro feature. Upgrade to Pro to unlock MatterControl Pro.".Localize();
string text = "Exporting print history is a {0} feature. Upgrade to Pro to unlock MatterControl Pro.".Localize().FormatWith(OemSettings.Instance.RegisteredProductName);
WebCache.RetrieveText(
"https://matterhackers.github.io/MatterControl-Docs/ProContent/Unlock_Export_Print_History.md",
(markDown) =>

View file

@ -133,7 +133,7 @@ namespace MatterHackers.MatterControl.Library
}
}
public DesignAppsCollectionContainer DesignAppsCollectionContainer { get; internal set; }
public BundledPartsCollectionContainer BundledPartsCollectionContainer { get; internal set; }
public SafeList<LibraryAction> MenuExtensions { get; } = new SafeList<LibraryAction>();

View file

@ -38,13 +38,33 @@ using System.Threading.Tasks;
namespace MatterHackers.MatterControl.Library
{
public class DesignAppsCollectionContainer : LibraryContainer
public class BundledPartsCollectionContainer : LibraryContainer
{
public DesignAppsCollectionContainer()
public BundledPartsCollectionContainer()
{
this.ChildContainers = new SafeList<ILibraryContainerLink>();
this.Items = new SafeList<ILibraryItem>();
this.Name = "Design Apps".Localize();
this.Name = "Bundled".Localize();
this.ChildContainers.Add(
new DynamicContainerLink(
"Calibration Parts".Localize(),
StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")),
StaticData.Instance.LoadIcon(Path.Combine("Library", "calibration_library_icon.png")),
() => new CalibrationPartsContainer())
{
IsReadOnly = true
});
this.ChildContainers.Add(
new DynamicContainerLink(
"Scripting".Localize(),
StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")),
StaticData.Instance.LoadIcon(Path.Combine("Library", "scripting_icon.png")),
() => new ScriptingPartsContainer())
{
IsReadOnly = true
});
this.ChildContainers.Add(
new DynamicContainerLink(

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2019, John Lewin
Copyright (c) 2023, John Lewin, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -27,74 +27,74 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Primitives;
using System;
using System.Collections.Generic;
namespace MatterHackers.MatterControl.Library
{
public class Primitives2DContainer : LibraryContainer
{
public Primitives2DContainer()
{
Name = "Primitives 2D".Localize();
DefaultSort = new LibrarySortBehavior()
{
SortKey = SortKey.ModifiedDate,
Ascending = true,
};
}
{
public Primitives2DContainer()
{
Name = "Primitives 2D".Localize();
DefaultSort = new LibrarySortBehavior()
{
SortKey = SortKey.ModifiedDate,
Ascending = true,
};
}
public override void Load()
{
var library = ApplicationController.Instance.Library;
public override void Load()
{
var library = ApplicationController.Instance.Library;
long index = DateTime.Now.Ticks;
var libraryItems = new List<GeneratorItem>()
{
new GeneratorItem(
"Box".Localize(),
async () => await BoxPathObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Triangle".Localize(),
async () => await PyramidObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Trapezoid".Localize(),
async () => await WedgeObject3D_2.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Text".Localize(),
async () => await TextObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Oval".Localize(),
async () => await CylinderObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Star".Localize(),
async () => await ConeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Ring".Localize(),
async () => await RingObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Circle".Localize(),
async () => await SphereObject3D.Create())
{ DateCreated = new DateTime(index++) },
};
long index = DateTime.Now.Ticks;
var libraryItems = new List<GeneratorItem>()
{
new GeneratorItem(
"Box".Localize(),
async () => await BoxPathObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Triangle".Localize(),
async () => await PyramidObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Trapezoid".Localize(),
async () => await WedgeObject3D_2.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Text".Localize(),
async () => await TextObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Oval".Localize(),
async () => await CylinderObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Star".Localize(),
async () => await ConeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Ring".Localize(),
async () => await RingObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Circle".Localize(),
async () => await SphereObject3D.Create())
{ DateCreated = new DateTime(index++) },
};
string title = "2D Shapes".Localize();
string title = "2D Shapes".Localize();
foreach (var item in libraryItems)
{
item.Category = title;
Items.Add(item);
}
}
}
}
foreach (var item in libraryItems)
{
item.Category = title;
Items.Add(item);
}
}
}
}

View file

@ -0,0 +1,177 @@
/*
Copyright (c) 2023, John Lewin, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Agg.Platform;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MatterHackers.MatterControl.Library
{
public class Primitives3DContainer : LibraryContainer
{
public Primitives3DContainer()
{
Name = "Primitives".Localize();
DefaultSort = new LibrarySortBehavior()
{
SortKey = SortKey.ModifiedDate,
Ascending = true,
};
}
public override void Load()
{
var library = ApplicationController.Instance.Library;
long index = DateTime.Now.Ticks;
var libraryItems = new List<GeneratorItem>()
{
new GeneratorItem(
"Cube".Localize(),
async () => await CubeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Hole".Localize(),
async () => await CubeHoleObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Pyramid".Localize(),
async () => await PyramidObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Wedge".Localize(),
async () => await WedgeObject3D_2.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Half Wedge".Localize(),
async () => await HalfWedgeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Text".Localize(),
async () => await TextObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Cylinder".Localize(),
async () => await CylinderObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Cone".Localize(),
async () => await ConeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Half Cylinder".Localize(),
async () => await HalfCylinderObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Torus".Localize(),
async () => await TorusObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Ring".Localize(),
async () => await RingObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Sphere".Localize(),
async () => await SphereObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Half Sphere".Localize(),
async () => await HalfSphereObject3D.Create())
{ DateCreated = new DateTime(index++) },
#if DEBUG
new GeneratorItem(
"SCAD Script".Localize(),
async () => await OpenScadScriptObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Dual Contouring".Localize(),
async () => await DualContouringObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"QR Code".Localize(),
async () => await QrCodeObject3D.Create())
{ DateCreated = new DateTime(index++) },
#endif
new GeneratorItem(
"Image Converter".Localize(),
() =>
{
// Construct an image
var imageObject = new ImageObject3D()
{
AssetPath = StaticData.Instance.ToAssetPath(Path.Combine("Images", "mh-logo.png"))
};
// Construct a scene
var bedConfig = new BedConfig(null);
var tempScene = bedConfig.Scene;
tempScene.Children.Add(imageObject);
tempScene.SelectedItem = imageObject;
// Invoke ImageConverter operation, passing image and scene
SceneOperations.ById("ImageConverter").Action(bedConfig);
// Return replacement object constructed in ImageConverter operation
var constructedComponent = tempScene.Children.LastOrDefault();
tempScene.SelectedItem = constructedComponent;
tempScene.Children.Remove(constructedComponent);
return Task.FromResult(constructedComponent);
})
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Measure Tool".Localize(),
async () => await MeasureToolObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Description".Localize(),
async () => await DescriptionObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Variable Sheet".Localize(),
async () => await SheetObject3D.Create())
{ DateCreated = new DateTime(index++) },
};
string title = "Primitive Shapes".Localize();
foreach (var item in libraryItems)
{
item.Category = title;
Items.Add(item);
}
}
}
}

View file

@ -1,191 +0,0 @@
/*
Copyright (c) 2019, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MatterHackers.Agg.Platform;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
namespace MatterHackers.MatterControl.Library
{
public class Primitives3DContainer : LibraryContainer
{
public Primitives3DContainer()
{
Name = "Primitives".Localize();
DefaultSort = new LibrarySortBehavior()
{
SortKey = SortKey.ModifiedDate,
Ascending = true,
};
}
public override void Load()
{
var library = ApplicationController.Instance.Library;
long index = DateTime.Now.Ticks;
var libraryItems = new List<GeneratorItem>()
{
new GeneratorItem(
"Cube".Localize(),
async () => await CubeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Hole".Localize(),
async () => await CubeHoleObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Pyramid".Localize(),
async () => await PyramidObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Wedge".Localize(),
async () => await WedgeObject3D_2.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Half Wedge".Localize(),
async () => await HalfWedgeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Text".Localize(),
async () => await TextObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Cylinder".Localize(),
async () => await CylinderObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Cone".Localize(),
async () => await ConeObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Half Cylinder".Localize(),
async () => await HalfCylinderObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Torus".Localize(),
async () => await TorusObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Ring".Localize(),
async () => await RingObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Sphere".Localize(),
async () => await SphereObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Half Sphere".Localize(),
async () => await HalfSphereObject3D.Create())
{ DateCreated = new DateTime(index++) },
#if DEBUG
new GeneratorItem(
"SCAD Script".Localize(),
async () => await OpenScadScriptObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Dual Contouring".Localize(),
async () => await DualContouringObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"QR Code".Localize(),
async () => await QrCodeObject3D.Create())
{ DateCreated = new DateTime(index++) },
#endif
new GeneratorItem(
"Image Converter".Localize(),
() =>
{
// Construct an image
var imageObject = new ImageObject3D()
{
AssetPath = StaticData.Instance.ToAssetPath(Path.Combine("Images", "mh-logo.png"))
};
// Construct a scene
var bedConfig = new BedConfig(null);
var tempScene = bedConfig.Scene;
tempScene.Children.Add(imageObject);
tempScene.SelectedItem = imageObject;
// Invoke ImageConverter operation, passing image and scene
SceneOperations.ById("ImageConverter").Action(bedConfig);
// Return replacement object constructed in ImageConverter operation
var constructedComponent = tempScene.Children.LastOrDefault();
tempScene.SelectedItem = constructedComponent;
tempScene.Children.Remove(constructedComponent);
return Task.FromResult(constructedComponent);
})
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Measure Tool".Localize(),
async () => await MeasureToolObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Description".Localize(),
async () => await DescriptionObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Variable Sheet".Localize(),
async () => await SheetObject3D.Create())
{ DateCreated = new DateTime(index++) },
};
string title = "Primitive Shapes".Localize();
foreach (var item in libraryItems)
{
item.Category = title;
Items.Add(item);
}
#if DEBUG
this.ChildContainers.Add(
new DynamicContainerLink(
"Primitives 2D".Localize(),
StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")),
StaticData.Instance.LoadIcon(Path.Combine("Library", "primitives_library_icon.png")),
() => new Primitives2DContainer())
{
IsReadOnly = true
});
#endif
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2022, John Lewin, Lars Brubaker
Copyright (c) 2023, John Lewin, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without

View file

@ -30,6 +30,8 @@ either expressed or implied, of the FreeBSD Project.
using Markdig.Agg;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.SettingsManagement;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.PlusTab
@ -53,7 +55,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.PlusTab
Padding = new BorderDouble(left: theme.DefaultContainerPadding / 2)
};
markdownWidget.Markdown = "# Upgrade to [MatterControl Pro](https://www.matterhackers.com/admin/product-preview/ag1zfm1oLXBscy1wcm9kchsLEg5Qcm9kdWN0TGlzdGluZxiAgIC_65WICww)";
markdownWidget.Markdown = "# Upgrade to [{0}](https://www.matterhackers.com/admin/product-preview/ag1zfm1oLXBscy1wcm9kchsLEg5Qcm9kdWN0TGlzdGluZxiAgIC_65WICww)".Localize().FormatWith(OemSettings.Instance.RegisteredProductName);
this.AddChild(markdownWidget);

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2022, Lars Brubaker, John Lewin
Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -45,6 +45,7 @@ using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.Library;
using MatterHackers.MatterControl.Library.Widgets;
using MatterHackers.MatterControl.PartPreviewWindow.PlusTab;
using MatterHackers.MatterControl.SettingsManagement;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
@ -312,8 +313,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
});
EnableReduceWidth(tab, theme);
// Hardware tab
tabControl.AddTab(
if (!OemSettings.Instance.DesignToolsOnly)
{
// Hardware tab
tabControl.AddTab(
tab = new ChromeTab(
"Hardware",
"Printers".Localize(),
@ -329,6 +332,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
Name = "Hardware Tab",
Padding = new BorderDouble(15, 0),
});
}
EnableReduceWidth(tab, theme);
SetInitialTab();

View file

@ -82,13 +82,13 @@ namespace MatterHackers.MatterControl
/// Arrange the given parts on the bed and return a list of the parts that were arranged
/// </summary>
/// <param name="object3DList">The parts to arrange</param>
/// <param name="arangePosition">A position to arrange around</param>
/// <param name="arrangePosition">A position to arrange around</param>
/// <param name="positionType">The way to consider the possition</param>
/// <param name="bedBounds">Optional bounds to arrange into</param>
/// <param name="progressReporter">The current progress of arranging</param>
/// <returns>A list of the parts that were arranged</returns>
public static List<IObject3D> ArrangeOnBed(List<IObject3D> object3DList,
Vector3 arangePosition,
Vector3 arrangePosition,
PositionType positionType,
RectangleDouble? bedBounds = null,
Action<double, string> progressReporter = null)
@ -117,14 +117,14 @@ namespace MatterHackers.MatterControl
{
objectsThatHaveBeenPlaced.Add(object3D);
objectsThatWereArrange.Add(object3D);
// and put it on the bed (set the bottom to z = 0)
PlaceOnBed(object3D);
}
progressReporter?.Invoke(Util.GetRatio(0, 1, meshGroupIndex, object3DList.Count), null);
currentRatioDone += ratioPerMeshGroup;
// and put it on the bed (set the bottom to z = 0)
PlaceOnBed(object3D);
}
// and finally center whatever we have as a group
@ -145,7 +145,7 @@ namespace MatterHackers.MatterControl
for (int i = 0; i < object3DList.Count; i++)
{
object3DList[i].Matrix *= Matrix4X4.CreateTranslation(arangePosition - offset);
object3DList[i].Matrix *= Matrix4X4.CreateTranslation(arrangePosition - offset);
}
}
@ -263,11 +263,11 @@ namespace MatterHackers.MatterControl
AxisAlignedBoundingBox testBounds = meshToMoveBounds.NewTransformed(transform);
foreach (IObject3D meshToTest in itemsToAvoid)
foreach (IObject3D itemToTest in itemsToAvoid)
{
if (meshToTest != itemToMove)
if (itemToTest != itemToMove)
{
AxisAlignedBoundingBox existingMeshBounds = meshToTest.GetAxisAlignedBoundingBox();
AxisAlignedBoundingBox existingMeshBounds = itemToTest.GetAxisAlignedBoundingBox();
var intersection = AxisAlignedBoundingBox.Intersection(testBounds, existingMeshBounds);
if (intersection.XSize > 0 && intersection.YSize > 0)
{

View file

@ -27,264 +27,267 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.MatterControl.SlicerConfiguration;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.ImageProcessing;
using MatterHackers.MatterControl.SlicerConfiguration;
using Newtonsoft.Json;
namespace MatterHackers.MatterControl.SettingsManagement
{
public class OemSettings
{
private static OemSettings instance = null;
public class OemSettings
{
private static OemSettings instance = null;
public static OemSettings Instance
{
get
{
if (instance == null)
{
string oemSettings = StaticData.Instance.ReadAllText(Path.Combine("OEMSettings", "Settings.json"));
instance = JsonConvert.DeserializeObject<OemSettings>(oemSettings) as OemSettings;
}
return instance;
}
}
public bool UseSimpleModeByDefault = false;
public string ThemeColor = "";
public string AffiliateCode = "";
public string WindowTitleExtra = "";
public bool ShowShopButton = true;
public bool CheckForUpdatesOnFirstRun = false;
public List<string> PrinterWhiteList { get; private set; } = new List<string>();
public List<ManufacturerNameMapping> ManufacturerNameMappings { get; set; }
public ImageBuffer GetIcon(string oemName, ThemeConfig theme)
{
var size = (int)(16 * GuiWidget.DeviceScale);
var imageBuffer = new ImageBuffer(size, size);
string oemUrl = ApplicationController.Instance.GetFavIconUrl(oemName);
if (!string.IsNullOrWhiteSpace(oemUrl))
{
WebCache.RetrieveImageAsync(imageBuffer, oemUrl, scaleToImageX: true);
}
else
{
var graphics = imageBuffer.NewGraphics2D();
graphics.Clear(AppContext.Theme.SlightShade);
}
if (theme.IsDarkTheme)
public static OemSettings Instance
{
get
{
// put the icon on a light background
var background = new ImageBuffer(size, size);
background.NewGraphics2D().Render(new RoundedRect(background.GetBoundingRect(), 1), theme.TextColor);
background.NewGraphics2D().Render(imageBuffer, 0, 0);
imageBuffer.CopyFrom(background);
}
if (instance == null)
{
string oemSettings = StaticData.Instance.ReadAllText(Path.Combine("OEMSettings", "Settings.json"));
instance = JsonConvert.DeserializeObject<OemSettings>(oemSettings) as OemSettings;
}
return imageBuffer;
}
return instance;
}
}
internal void SetManufacturers(IEnumerable<KeyValuePair<string, string>> unorderedManufacturers, List<string> whitelist = null)
{
// Sort manufacturers by name
var manufacturers = new List<KeyValuePair<string, string>>();
var otherInfo = new KeyValuePair<string, string>(null, null);
public bool UseSimpleModeByDefault = false;
foreach (var printer in unorderedManufacturers.OrderBy(k => k.Value))
{
if (printer.Value == "Other")
{
otherInfo = printer;
}
else
{
manufacturers.Add(printer);
}
}
public string ThemeColor = "";
if (otherInfo.Key != null)
{
// add it at the end
manufacturers.Add(otherInfo);
}
public string AffiliateCode = "";
if (whitelist != null)
{
this.PrinterWhiteList = whitelist;
}
public string WindowTitleExtra = "";
// Apply whitelist
var whiteListedItems = manufacturers?.Where(keyValue => PrinterWhiteList.Contains(keyValue.Key));
public bool ShowShopButton = true;
if (whiteListedItems == null
|| whiteListedItems.Count() == 0)
{
// No whitelist means all items
whiteListedItems = manufacturers;
}
public bool DesignToolsOnly = true;
var newItems = new List<KeyValuePair<string, string>>();
public bool CheckForUpdatesOnFirstRun = false;
// Apply manufacturer name mappings
foreach (var keyValue in whiteListedItems)
{
string labelText = keyValue.Value;
public string UnregisteredProductName { get; set; } = "MatterControl";
public string RegisteredProductName { get; set; } = "MatterControl Pro";
// Override the manufacturer name if a manufacturerNameMappings exists
string mappedName = ManufacturerNameMappings.Where(m => m.NameOnDisk == keyValue.Key).FirstOrDefault()?.NameOnDisk;
if (!string.IsNullOrEmpty(mappedName))
{
labelText = mappedName;
}
public List<string> PrinterWhiteList { get; private set; } = new List<string>();
newItems.Add(new KeyValuePair<string, string>(keyValue.Key, labelText));
}
public List<ManufacturerNameMapping> ManufacturerNameMappings { get; set; }
AllOems = newItems;
}
public ImageBuffer GetIcon(string oemName, ThemeConfig theme)
{
var size = (int)(16 * GuiWidget.DeviceScale);
var imageBuffer = new ImageBuffer(size, size);
public List<KeyValuePair<string, string>> AllOems { get; private set; }
string oemUrl = ApplicationController.Instance.GetFavIconUrl(oemName);
public OemProfileDictionary OemProfiles { get; set; }
if (!string.IsNullOrWhiteSpace(oemUrl))
{
WebCache.RetrieveImageAsync(imageBuffer, oemUrl, scaleToImageX: true);
}
else
{
var graphics = imageBuffer.NewGraphics2D();
graphics.Clear(AppContext.Theme.SlightShade);
}
public Dictionary<string, string> OemUrls { get; }
if (theme.IsDarkTheme)
{
// put the icon on a light background
var background = new ImageBuffer(size, size);
background.NewGraphics2D().Render(new RoundedRect(background.GetBoundingRect(), 1), theme.TextColor);
background.NewGraphics2D().Render(imageBuffer, 0, 0);
imageBuffer.CopyFrom(background);
}
public Dictionary<string, StorePrinterID> OemPrinters { get; }
return imageBuffer;
}
[OnDeserialized]
private void Deserialized(StreamingContext context)
{
// Load local OemProfile content during initial startup
OemProfiles = this.LoadOemProfiles();
internal void SetManufacturers(IEnumerable<KeyValuePair<string, string>> unorderedManufacturers, List<string> whitelist = null)
{
// Sort manufacturers by name
var manufacturers = new List<KeyValuePair<string, string>>();
var otherInfo = new KeyValuePair<string, string>(null, null);
var manufacturesList = OemProfiles.Keys.ToDictionary(oem => oem);
SetManufacturers(manufacturesList);
}
foreach (var printer in unorderedManufacturers.OrderBy(k => k.Value))
{
if (printer.Value == "Other")
{
otherInfo = printer;
}
else
{
manufacturers.Add(printer);
}
}
private OemProfileDictionary LoadOemProfiles()
{
string cachePath = ApplicationController.CacheablePath("public-profiles", "oemprofiles.json");
if (otherInfo.Key != null)
{
// add it at the end
manufacturers.Add(otherInfo);
}
// Load data from cache or fall back to stale StaticData content
string json = File.Exists(cachePath) ? File.ReadAllText(cachePath) : null;
if (whitelist != null)
{
this.PrinterWhiteList = whitelist;
}
if (string.IsNullOrWhiteSpace(json))
{
// If empty, purge the cache file and fall back to StaticData
File.Delete(cachePath);
// Apply whitelist
var whiteListedItems = manufacturers?.Where(keyValue => PrinterWhiteList.Contains(keyValue.Key));
json = StaticData.Instance.ReadAllText(Path.Combine("Profiles", "oemprofiles.json"));
}
if (whiteListedItems == null
|| whiteListedItems.Count() == 0)
{
// No whitelist means all items
whiteListedItems = manufacturers;
}
try
{
return JsonConvert.DeserializeObject<OemProfileDictionary>(json);
}
catch
{
// If json parse fails, purge the cache file and fall back to StaticData
File.Delete(cachePath);
var newItems = new List<KeyValuePair<string, string>>();
json = StaticData.Instance.ReadAllText(Path.Combine("Profiles", "oemprofiles.json"));
return JsonConvert.DeserializeObject<OemProfileDictionary>(json);
}
}
// Apply manufacturer name mappings
foreach (var keyValue in whiteListedItems)
{
string labelText = keyValue.Value;
public async Task ReloadOemProfiles()
{
// In public builds this won't be assigned to and we should exit
if (ApplicationController.GetPublicProfileList == null)
{
return;
}
// Override the manufacturer name if a manufacturerNameMappings exists
string mappedName = ManufacturerNameMappings.Where(m => m.NameOnDisk == keyValue.Key).FirstOrDefault()?.NameOnDisk;
if (!string.IsNullOrEmpty(mappedName))
{
labelText = mappedName;
}
await ApplicationController.LoadCacheableAsync<OemProfileDictionary>(
"oemprofiles.json",
"public-profiles",
async () =>
{
var result = await ApplicationController.GetPublicProfileList();
if (result != null)
{
// Refresh the in memory instance any time the server responds with updated content - caller will serialize
OemProfiles = result;
newItems.Add(new KeyValuePair<string, string>(keyValue.Key, labelText));
}
SetManufacturers(result.Keys.ToDictionary(oem => oem));
}
AllOems = newItems;
}
return result;
});
public List<KeyValuePair<string, string>> AllOems { get; private set; }
await DownloadMissingProfiles();
}
public OemProfileDictionary OemProfiles { get; set; }
private async Task DownloadMissingProfiles()
{
int index = 0;
foreach (string oem in OemProfiles.Keys)
{
string cacheScope = Path.Combine("public-profiles", oem);
public Dictionary<string, string> OemUrls { get; }
index++;
foreach (var model in OemProfiles[oem].Keys)
{
var publicDevice = OemProfiles[oem][model];
string cachePath = ApplicationController.CacheablePath(cacheScope, publicDevice.CacheKey);
if (!File.Exists(cachePath))
{
await Task.Delay(20000);
await ProfileManager.LoadOemSettingsAsync(publicDevice, oem, model);
public Dictionary<string, StorePrinterID> OemPrinters { get; }
if (ApplicationController.Instance.ApplicationExiting)
{
return;
}
}
}
}
}
[OnDeserialized]
private void Deserialized(StreamingContext context)
{
// Load local OemProfile content during initial startup
OemProfiles = this.LoadOemProfiles();
private OemSettings()
{
this.ManufacturerNameMappings = new List<ManufacturerNameMapping>();
this.OemUrls = JsonConvert.DeserializeObject<Dictionary<string, string>>(StaticData.Instance.ReadAllText(Path.Combine("OEMSettings", "OEMUrls.json")));
this.OemPrinters = JsonConvert.DeserializeObject<Dictionary<string, StorePrinterID>>(StaticData.Instance.ReadAllText(Path.Combine("OEMSettings", "Printers.json")));
}
}
var manufacturesList = OemProfiles.Keys.ToDictionary(oem => oem);
SetManufacturers(manufacturesList);
}
public class StorePrinterID
{
public string SID { get; set; }
private OemProfileDictionary LoadOemProfiles()
{
string cachePath = ApplicationController.CacheablePath("public-profiles", "oemprofiles.json");
public string AltInfoUrl { get; set; }
}
// Load data from cache or fall back to stale StaticData content
string json = File.Exists(cachePath) ? File.ReadAllText(cachePath) : null;
public class ManufacturerNameMapping
{
public string NameOnDisk { get; set; }
if (string.IsNullOrWhiteSpace(json))
{
// If empty, purge the cache file and fall back to StaticData
File.Delete(cachePath);
public string NameToDisplay { get; set; }
}
json = StaticData.Instance.ReadAllText(Path.Combine("Profiles", "oemprofiles.json"));
}
try
{
return JsonConvert.DeserializeObject<OemProfileDictionary>(json);
}
catch
{
// If json parse fails, purge the cache file and fall back to StaticData
File.Delete(cachePath);
json = StaticData.Instance.ReadAllText(Path.Combine("Profiles", "oemprofiles.json"));
return JsonConvert.DeserializeObject<OemProfileDictionary>(json);
}
}
public async Task ReloadOemProfiles()
{
// In public builds this won't be assigned to and we should exit
if (ApplicationController.GetPublicProfileList == null)
{
return;
}
await ApplicationController.LoadCacheableAsync<OemProfileDictionary>(
"oemprofiles.json",
"public-profiles",
async () =>
{
var result = await ApplicationController.GetPublicProfileList();
if (result != null)
{
// Refresh the in memory instance any time the server responds with updated content - caller will serialize
OemProfiles = result;
SetManufacturers(result.Keys.ToDictionary(oem => oem));
}
return result;
});
await DownloadMissingProfiles();
}
private async Task DownloadMissingProfiles()
{
int index = 0;
foreach (string oem in OemProfiles.Keys)
{
string cacheScope = Path.Combine("public-profiles", oem);
index++;
foreach (var model in OemProfiles[oem].Keys)
{
var publicDevice = OemProfiles[oem][model];
string cachePath = ApplicationController.CacheablePath(cacheScope, publicDevice.CacheKey);
if (!File.Exists(cachePath))
{
await Task.Delay(20000);
await ProfileManager.LoadOemSettingsAsync(publicDevice, oem, model);
if (ApplicationController.Instance.ApplicationExiting)
{
return;
}
}
}
}
}
private OemSettings()
{
this.ManufacturerNameMappings = new List<ManufacturerNameMapping>();
this.OemUrls = JsonConvert.DeserializeObject<Dictionary<string, string>>(StaticData.Instance.ReadAllText(Path.Combine("OEMSettings", "OEMUrls.json")));
this.OemPrinters = JsonConvert.DeserializeObject<Dictionary<string, StorePrinterID>>(StaticData.Instance.ReadAllText(Path.Combine("OEMSettings", "Printers.json")));
}
}
public class StorePrinterID
{
public string SID { get; set; }
public string AltInfoUrl { get; set; }
}
public class ManufacturerNameMapping
{
public string NameOnDisk { get; set; }
public string NameToDisplay { get; set; }
}
}

View file

@ -31,6 +31,7 @@ using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.SettingsManagement;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.Tour
@ -40,10 +41,10 @@ namespace MatterHackers.MatterControl.Tour
public WelcomePage()
: base("Done".Localize())
{
this.WindowTitle = "MatterControl".Localize();
this.WindowTitle = ApplicationController.Instance.ProductName;
this.WindowSize = new Vector2(400 * GuiWidget.DeviceScale, 250 * GuiWidget.DeviceScale);
this.HeaderText = "Welcome to MatterControl".Localize();
this.HeaderText = "Welcome to {0}".Localize().FormatWith(ApplicationController.Instance.ProductName);
var welcome = "Let's show you around before you get started.".Localize();

View file

@ -34,6 +34,9 @@ Translated:(Press 'Skip' to setup connection later)
English:{0} (Update Available)
Translated:{0} (Update Available)
English:{0} is made possible by the team at MatterHackers and other open source software
Translated:{0} is made possible by the team at MatterHackers and other open source software
English:{0} must be greater than 0 and less than your nozzle diameter. You may be missing a '%'.
Translated:{0} must be greater than 0 and less than your nozzle diameter. You may be missing a '%'.
@ -412,9 +415,6 @@ Translated:Are you sure you want to sign out? You will not have access to your p
English:Arrange All Parts
Translated:Arrange All Parts
English:Arranging
Translated:Arranging
English:As the time to print a layer decreases to this, the fan speed will be increased up to its maximum speed.
Translated:As the time to print a layer decreases to this, the fan speed will be increased up to its maximum speed.
@ -505,9 +505,6 @@ Translated:Bad
English:Bad Thermistor
Translated:Bad Thermistor
English:Bambu Studio
Translated:Bambu Studio
English:Base
Translated:Base
@ -538,9 +535,6 @@ Translated:Be sure you are not pressing down on the bed while moving the paper.
English:Bed
Translated:Bed
English:Bed Depth
Translated:Bed Depth
English:Bed Dislodged
Translated:Bed Dislodged
@ -586,9 +580,6 @@ Translated:Bed Temperature Set to 0
English:Bed Temperature:
Translated:Bed Temperature:
English:Bed Width
Translated:Bed Width
English:Bed Wipe Temperature
Translated:Bed Wipe Temperature
@ -1036,6 +1027,9 @@ Translated:Connection Timeout
English:Connection Troubleshooting
Translated:Connection Troubleshooting
English:Constraints
Translated:Constraints
English:Continue
Translated:Continue
@ -1144,9 +1138,6 @@ Translated:Create Part Sheet
English:Create Perimeter
Translated:Create Perimeter
English:Create Plates
Translated:Create Plates
English:Create Printer
Translated:Create Printer
@ -1165,9 +1156,6 @@ Translated:Create Support
English:Create Supports
Translated:Create Supports
English:Create the plates for all the selected STLs.
Translated:Create the plates for all the selected STLs.
English:Creates a brim attached to the base of the print. Useful to prevent warping when printing ABS (and other warping-prone plastics) as it helps parts adhere to the bed.
Translated:Creates a brim attached to the base of the print. Useful to prevent warping when printing ABS (and other warping-prone plastics) as it helps parts adhere to the bed.
@ -2104,9 +2092,6 @@ Translated:Generate Supports
English:Generates an outline around the support material to improve strength and hold up interface layers.
Translated:Generates an outline around the support material to improve strength and hold up interface layers.
English:Generating Config
Translated:Generating Config
English:Generating Lithophane
Translated:Generating Lithophane
@ -2710,9 +2695,6 @@ Translated:Loading GCode
English:Loading Help
Translated:Loading Help
English:Loading Stls
Translated:Loading Stls
English:Local Library
Translated:Local Library
@ -3394,9 +3376,6 @@ Translated:Open Recent
English:Open Settings View Options
Translated:Open Settings View Options
English:Open With
Translated:Open With
English:OpenSCAD not installed
Translated:OpenSCAD not installed
@ -3442,9 +3421,6 @@ Translated:Outlines (default)
English:Output
Translated:Output
English:Output Folder
Translated:Output Folder
English:Output only the first layer of the print. Especially useful for outputting gcode data for applications like engraving or cutting.
Translated:Output only the first layer of the print. Especially useful for outputting gcode data for applications like engraving or cutting.
@ -3499,9 +3475,6 @@ Translated:Part(s) to Subtract and Replace
English:Parts
Translated:Parts
English:Parts Folder
Translated:Parts Folder
English:Password
Translated:Password
@ -3547,12 +3520,6 @@ Translated:PEI Bed Temperature
English:Percentage of
Translated:Percentage of
English:Performe a union before exporting. Might be slower but can clean up some models.
Translated:Performe a union before exporting. Might be slower but can clean up some models.
English:Performe Union
Translated:Performe Union
English:Perimeter Acceleration
Translated:Perimeter Acceleration
@ -3775,6 +3742,9 @@ Translated:Primitives
English:Primitives 2D
Translated:Primitives 2D
English:Primitives 3D
Translated:Primitives 3D
English:Print
Translated:Print
@ -4402,12 +4372,6 @@ Translated:Save changes ?
English:Save Changes?
Translated:Save Changes?
English:Save Each Separately
Translated:Save Each Separately
English:Save every object as a separate STL using its name. The save filename will be used if no name can be found.
Translated:Save every object as a separate STL using its name. The save filename will be used if no name can be found.
English:Save file:
Translated:Save file:
@ -4435,9 +4399,6 @@ Translated:Saving changes
English:Saving Changes
Translated:Saving Changes
English:Saving PDF
Translated:Saving PDF
English:SCAD Script
Translated:SCAD Script
@ -4510,9 +4471,6 @@ Translated:Select an object to copy its color
English:Select cell to edit
Translated:Select cell to edit
English:Select Folder
Translated:Select Folder
English:Select Parts
Translated:Select Parts
@ -4534,9 +4492,6 @@ Translated:Select this option only if your printer does not appear in the list
English:Select What to Import
Translated:Select What to Import
English:Selecte a directory
Translated:Selecte a directory
English:Selected Children
Translated:Selected Children
@ -4990,9 +4945,6 @@ Translated:Starting Angle
English:Starting firmware update...
Translated:Starting firmware update...
English:Starting slicer
Translated:Starting slicer
English:Starting Sync
Translated:Starting Sync