2016-06-15 14:53:11 -07:00
/ *
2018-11-25 07:39:09 -08:00
Copyright ( c ) 2018 , Lars Brubaker , John Lewin
2016-06-15 14:53:11 -07:00
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 ;
2016-12-14 17:19:19 -08:00
using System.Collections.ObjectModel ;
2016-06-15 14:53:11 -07:00
using System.IO ;
using System.Linq ;
2016-12-14 17:19:19 -08:00
using System.Threading.Tasks ;
using MatterHackers.Agg ;
2016-09-20 09:39:57 -07:00
using MatterHackers.Agg.UI ;
2018-11-24 07:29:56 -08:00
using MatterHackers.Localizations ;
2016-12-14 17:19:19 -08:00
using MatterHackers.MatterControl.DataStorage ;
using MatterHackers.MatterControl.SettingsManagement ;
2016-09-20 09:39:57 -07:00
using Newtonsoft.Json ;
2016-06-15 14:53:11 -07:00
namespace MatterHackers.MatterControl.SlicerConfiguration
{
2018-11-16 14:39:20 -08:00
public class ProfileManager : IDisposable
2016-06-15 14:53:11 -07:00
{
2016-07-11 15:28:28 -07:00
public static RootedObjectEventHandler ProfilesListChanged = new RootedObjectEventHandler ( ) ;
2018-11-21 10:58:52 -08:00
public static event EventHandler UserChanged ;
2018-11-16 14:40:42 -08:00
private static ProfileManager _instance = null ;
public static ProfileManager Instance
{
get = > _instance ;
private set
{
_instance ? . Dispose ( ) ;
_instance = value ;
}
}
2016-10-21 12:59:10 -07:00
2016-07-14 11:36:14 -07:00
public const string ProfileExtension = ".printer" ;
2016-10-21 12:59:10 -07:00
public const string ProfileDocExtension = ".profiles" ;
2016-07-13 14:40:08 -07:00
2016-10-21 12:59:10 -07:00
private object writeLock = new object ( ) ;
2016-09-06 14:04:44 -07:00
2016-10-21 12:59:10 -07:00
static ProfileManager ( )
2016-07-18 15:18:17 -07:00
{
2016-11-30 12:56:15 -08:00
ReloadActiveUser ( ) ;
2016-07-18 15:18:17 -07:00
}
2018-11-16 14:42:27 -08:00
public ProfileManager ( )
{
// Register listeners
PrinterSettings . AnyPrinterSettingChanged + = this . Printer_SettingsChanged ;
}
2018-04-17 17:13:18 -07:00
public Task Initialize ( )
2017-12-20 18:25:12 -08:00
{
2018-04-17 17:13:18 -07:00
return Task . CompletedTask ;
2017-12-20 18:25:12 -08:00
}
2016-10-21 12:59:10 -07:00
public string UserName { get ; set ; }
2016-07-06 14:04:23 -07:00
2016-10-21 12:59:10 -07:00
/// <summary>
2020-09-08 19:10:01 -07:00
/// Gets the user specific path to the Profiles directory
2016-10-21 12:59:10 -07:00
/// </summary>
[JsonIgnore]
2022-02-11 10:16:39 -08:00
public string UserProfilesDirectory = > GetProfilesDirectoryForUser ( this . UserName ) ;
2016-07-06 14:04:23 -07:00
2018-10-13 17:58:54 -07:00
[JsonIgnore]
public string ProfileThemeSetPath = > Path . Combine ( UserProfilesDirectory , "themeset.json" ) ;
2018-12-07 18:41:32 -08:00
[JsonIgnore]
public string OpenTabsPath = > Path . Combine ( UserProfilesDirectory , "opentabs.json" ) ;
2016-10-21 12:59:10 -07:00
/// <summary>
2020-09-01 18:07:34 -07:00
/// Gets the user specific path to the Profiles document
2016-10-21 12:59:10 -07:00
/// </summary>
[JsonIgnore]
public string ProfilesDocPath = > GetProfilesDocPathForUser ( this . UserName ) ;
2016-10-04 17:54:57 -07:00
2016-10-21 12:59:10 -07:00
private static string GetProfilesDocPathForUser ( string userName )
{
return Path . Combine ( GetProfilesDirectoryForUser ( userName ) , $"{userName}{ProfileDocExtension}" ) ;
2016-07-06 14:04:23 -07:00
}
2016-06-15 14:53:11 -07:00
2016-10-21 12:59:10 -07:00
private static string GetProfilesDirectoryForUser ( string userName )
2016-06-23 15:15:11 -07:00
{
2016-11-02 15:55:55 -07:00
string userAndEnvName = ( userName = = "guest" ) ? userName : ApplicationController . EnvironmentName + userName ;
string userProfilesDirectory = Path . Combine ( ApplicationDataStorage . ApplicationUserDataPath , "Profiles" , userAndEnvName ) ;
2016-06-24 11:58:29 -07:00
2016-10-21 12:59:10 -07:00
// Ensure directory exists
Directory . CreateDirectory ( userProfilesDirectory ) ;
2016-06-24 11:58:29 -07:00
2016-10-21 12:59:10 -07:00
return userProfilesDirectory ;
2016-07-11 15:28:28 -07:00
}
2016-06-15 14:53:11 -07:00
2020-09-01 18:07:34 -07:00
private static Dictionary < string , List < ( string key , string currentValue , string newValue ) > > oemSettingsNeedingUpdateCache
2020-08-30 13:23:22 -07:00
= new Dictionary < string , List < ( string key , string currentValue , string newValue ) > > ( ) ;
2020-08-29 09:00:09 -07:00
2020-08-28 13:51:20 -07:00
public static IEnumerable < ( string key , string currentValue , string newValue ) > GetOemSettingsNeedingUpdate ( PrinterConfig printer )
{
2020-08-30 13:23:22 -07:00
var key = printer . Settings . ID ;
2020-08-29 09:00:09 -07:00
Task . Run ( async ( ) = >
{
2020-08-30 13:23:22 -07:00
ProfileManager . oemSettingsNeedingUpdateCache [ key ] = await GetChangedOemSettings ( printer ) ;
2020-08-29 10:56:16 -07:00
} ) ;
2020-09-01 18:07:34 -07:00
if ( oemSettingsNeedingUpdateCache . TryGetValue ( key , out List < ( string key , string currentValue , string newValue ) > cache ) )
2020-08-29 10:56:16 -07:00
{
2020-08-30 13:23:22 -07:00
foreach ( var item in cache )
{
2021-12-22 11:09:11 -08:00
if ( PrinterSettings . SettingsData . ContainsKey ( item . key ) )
{
yield return ( item . key , item . currentValue , item . newValue ) ;
}
2020-08-30 13:23:22 -07:00
}
2020-08-29 10:56:16 -07:00
}
}
public static async Task < List < ( string key , string currentValue , string newValue ) > > GetChangedOemSettings ( PrinterConfig printer )
{
var oemSettingsNeedingUpdateCache = new List < ( string key , string currentValue , string newValue ) > ( ) ;
var make = printer . Settings . GetValue ( SettingsKey . make ) ;
var model = printer . Settings . GetValue ( SettingsKey . model ) ;
var serverOemSettings = await ProfileManager . LoadOemSettingsAsync ( OemSettings . Instance . OemProfiles [ make ] [ model ] ,
make ,
model ) ;
2020-08-28 13:51:20 -07:00
2020-08-29 10:56:16 -07:00
var ignoreSettings = new HashSet < string > ( )
2020-08-29 14:12:16 -07:00
{
SettingsKey . created_date ,
SettingsKey . active_material_key ,
SettingsKey . active_quality_key ,
SettingsKey . oem_profile_token ,
2020-08-30 14:11:05 -07:00
SettingsKey . extruder_offset ,
2020-08-29 14:12:16 -07:00
} ;
2020-08-28 13:51:20 -07:00
2020-09-08 19:10:01 -07:00
var serverValuesToIgnore = new Dictionary < string , string > ( )
{
[SettingsKey.probe_offset] = "0,0,0"
} ;
2020-08-29 10:56:16 -07:00
foreach ( var localOemSetting in printer . Settings . OemLayer )
{
2020-09-08 19:10:01 -07:00
var key = localOemSetting . Key ;
if ( ! ignoreSettings . Contains ( key )
& & ! PrinterSettingsExtensions . SettingsToReset . ContainsKey ( key )
& & serverOemSettings . GetValue ( key ) ! = localOemSetting . Value )
2020-08-28 13:51:20 -07:00
{
2020-09-08 19:10:01 -07:00
var serverSetting = serverOemSettings . GetValue ( localOemSetting . Key ) ;
if ( ! serverValuesToIgnore . ContainsKey ( key )
| | serverSetting ! = serverValuesToIgnore [ key ] )
{
oemSettingsNeedingUpdateCache . Add ( ( localOemSetting . Key , localOemSetting . Value , serverSetting ) ) ;
}
2020-08-28 13:51:20 -07:00
}
}
2020-08-29 10:56:16 -07:00
2020-08-30 13:02:46 -07:00
oemSettingsNeedingUpdateCache . Sort ( ( a , b ) = >
{
2020-09-07 16:04:54 -07:00
var aInfo = "Unknown" ;
if ( PrinterSettings . SettingsData . ContainsKey ( a . key ) )
{
var aData = PrinterSettings . SettingsData [ a . key ] ;
var aGroup = aData . OrganizerGroup ;
var aCategory = aGroup ? . Category ;
aInfo = $"{aCategory?.Name}:{aGroup?.Name}:{aData.PresentationName}" ;
}
var bInfo = "Unknown" ;
if ( PrinterSettings . SettingsData . ContainsKey ( b . key ) )
{
var bData = PrinterSettings . SettingsData [ b . key ] ;
var bGroup = bData . OrganizerGroup ;
var bCategory = bGroup ? . Category ;
bInfo = $"{bCategory?.Name}:{bGroup?.Name}:{bData.PresentationName}" ;
}
2020-08-30 13:02:46 -07:00
return string . Compare ( aInfo , bInfo ) ;
} ) ;
2020-08-29 10:56:16 -07:00
return oemSettingsNeedingUpdateCache ;
2020-08-28 13:51:20 -07:00
}
2018-11-11 21:25:50 -08:00
public void DeletePrinter ( string printerID )
2018-10-07 11:36:52 -07:00
{
var printerInfo = ProfileManager . Instance [ printerID ] ;
if ( printerInfo ! = null )
{
2018-11-11 21:25:50 -08:00
printerInfo . MarkedForDelete = true ;
2018-10-07 11:36:52 -07:00
ProfileManager . Instance . Save ( ) ;
}
2018-11-21 10:58:52 -08:00
// TODO: Consolidate ActivePrinters into ProfileManager.OpenPrinters
2018-11-11 21:25:50 -08:00
var openedPrinter = ApplicationController . Instance . ActivePrinters . FirstOrDefault ( p = > p . Settings . ID = = printerID ) ;
if ( openedPrinter ! = null )
2018-10-07 11:36:52 -07:00
{
// Clear selected printer state
2018-11-21 09:48:43 -08:00
ProfileManager . Instance . ClosePrinter ( printerID ) ;
2018-10-07 11:36:52 -07:00
}
2018-11-21 10:58:52 -08:00
_activeProfileIDs . Remove ( printerID ) ;
2018-11-11 21:25:50 -08:00
UiThread . RunOnIdle ( ( ) = >
2018-10-07 11:36:52 -07:00
{
2018-11-11 21:25:50 -08:00
if ( openedPrinter ! = null )
2018-10-07 11:36:52 -07:00
{
2018-11-11 21:25:50 -08:00
ApplicationController . Instance . ClosePrinter ( openedPrinter ) ;
2018-10-07 11:36:52 -07:00
}
// Notify listeners of a ProfileListChange event due to this printers removal
ProfileManager . ProfilesListChanged . CallEvents ( this , null ) ;
2019-04-17 07:33:14 -07:00
// Queue sync after marking printer for delete
ApplicationController . QueueCloudProfileSync ? . Invoke ( "SettingsHelpers.SetMarkedForDelete()" ) ;
2018-10-07 11:36:52 -07:00
} ) ;
}
2016-07-13 14:40:08 -07:00
[JsonIgnore]
2016-10-21 16:09:44 -07:00
public bool IsGuestProfile = > this . UserName = = "guest" ;
2016-07-13 14:40:08 -07:00
2016-11-30 12:56:15 -08:00
/// <summary>
/// Updates ProfileManager.Instance to reflect the current authenticated/guest user
/// </summary>
public static void ReloadActiveUser ( )
2016-07-11 15:28:28 -07:00
{
2016-10-21 12:59:10 -07:00
string userName = AuthenticationData . Instance . FileSystemSafeUserName ;
2016-11-30 12:56:15 -08:00
if ( ! string . IsNullOrEmpty ( userName ) & & Instance ? . UserName = = userName )
2016-10-21 12:59:10 -07:00
{
2016-11-30 12:56:15 -08:00
// No work needed if user hasn't changed
2016-10-21 12:59:10 -07:00
return ;
}
2016-07-13 17:07:37 -07:00
if ( Instance ? . Profiles ! = null )
{
// Release event registration
Instance . Profiles . CollectionChanged - = Profiles_CollectionChanged ;
}
2016-11-30 12:56:15 -08:00
Instance = Load ( userName ) ;
// Wire up the CollectionChanged event
Instance . Profiles . CollectionChanged + = Profiles_CollectionChanged ;
2018-12-19 15:05:25 -08:00
// Only execute RestoreUserTabs if the application is up and running, never during startup
// During startup this behavior must be executed after the MainViewWidget has loaded
if ( ! AppContext . IsLoading )
{
UiThread . RunOnIdle ( ( ) = >
{
// Delay then load user tabs
ApplicationController . Instance . RestoreUserTabs ( ) . ConfigureAwait ( false ) ;
} , . 2 ) ;
}
2016-11-30 12:56:15 -08:00
}
/// <summary>
/// Loads a ProfileManager for the given user
/// </summary>
/// <param name="userName">The user name to load</param>
public static ProfileManager Load ( string userName )
{
if ( string . IsNullOrEmpty ( userName ) )
{
userName = "guest" ;
}
2016-10-21 12:59:10 -07:00
string profilesDocPath = GetProfilesDocPathForUser ( userName ) ;
2016-11-30 12:56:15 -08:00
ProfileManager loadedInstance ;
// Deserialize from disk or if missing, initialize a new instance
2016-10-21 12:59:10 -07:00
if ( File . Exists ( profilesDocPath ) )
2016-06-15 14:53:11 -07:00
{
2016-10-21 12:59:10 -07:00
string json = File . ReadAllText ( profilesDocPath ) ;
2016-11-30 12:56:15 -08:00
loadedInstance = JsonConvert . DeserializeObject < ProfileManager > ( json ) ;
loadedInstance . UserName = userName ;
2016-06-15 14:53:11 -07:00
}
2016-07-01 17:11:05 -07:00
else
2016-07-06 14:04:23 -07:00
{
2016-11-30 12:56:15 -08:00
loadedInstance = new ProfileManager ( ) { UserName = userName } ;
2016-07-06 14:04:23 -07:00
}
2016-07-06 17:45:53 -07:00
2018-11-21 10:58:52 -08:00
ProfileManager . UserChanged ? . Invoke ( loadedInstance , null ) ;
2018-11-21 08:37:29 -08:00
// Ensure SQLite printers are imported
2021-09-10 17:52:07 -07:00
loadedInstance ? . EnsurePrintersImported ( ) ;
2018-11-21 08:37:29 -08:00
2016-11-30 12:56:15 -08:00
return loadedInstance ;
2016-07-13 17:15:43 -07:00
}
2016-10-30 09:04:40 -07:00
public ObservableCollection < PrinterInfo > Profiles { get ; } = new ObservableCollection < PrinterInfo > ( ) ;
2016-06-15 14:53:11 -07:00
[JsonIgnore]
2016-09-12 16:20:01 -07:00
public IEnumerable < PrinterInfo > ActiveProfiles = > Profiles . Where ( profile = > ! profile . MarkedForDelete ) . ToList ( ) ;
2016-06-15 14:53:11 -07:00
2020-07-16 07:59:57 -07:00
public static bool DebugPrinterDelete { get ; set ; } = false ;
2016-06-15 14:53:11 -07:00
public PrinterInfo this [ string profileID ]
{
get
{
2020-07-16 07:59:57 -07:00
if ( DebugPrinterDelete )
{
return null ;
}
2016-06-15 14:53:11 -07:00
return Profiles . Where ( p = > p . ID = = profileID ) . FirstOrDefault ( ) ;
}
}
2018-11-21 08:34:14 -08:00
private List < string > profileIDsBackingField = null ;
2018-11-11 21:25:50 -08:00
2018-11-21 08:34:14 -08:00
private List < string > _activeProfileIDs
2016-07-11 15:14:23 -07:00
{
get
{
2018-11-11 21:25:50 -08:00
// Lazy load from db if null
2018-11-21 08:34:14 -08:00
if ( profileIDsBackingField = = null )
2018-11-11 21:25:50 -08:00
{
2018-12-07 18:41:32 -08:00
profileIDsBackingField = new List < string > ( ) ;
2018-11-11 21:25:50 -08:00
}
2018-11-21 08:34:14 -08:00
return profileIDsBackingField ;
2016-07-11 15:14:23 -07:00
}
}
2018-11-21 09:48:43 -08:00
public void ClosePrinter ( string printerID )
2018-11-11 21:26:08 -08:00
{
try
{
2018-11-30 10:32:30 -08:00
// Unregister listener
if ( ApplicationController . Instance . ActivePrinters . FirstOrDefault ( p = > p . Settings . ID = = printerID ) is PrinterConfig printer )
{
printer . Settings . SettingChanged - = PrinterSettings_SettingChanged ;
}
2018-11-11 21:26:08 -08:00
}
catch
{
}
}
2016-11-30 12:56:15 -08:00
/// <summary>
2020-08-21 11:35:12 -07:00
/// Gets or sets a value indicating whether indicates if given import has been run for the current user. For the guest profile, this means the
2016-11-30 12:56:15 -08:00
/// Sqlite import has been run and all db printers are now in the guest profile. For normal users
2018-04-17 17:13:18 -07:00
/// this means the CopyGuestProfilesToUser wizard has been completed and one or more printers were
2016-11-30 12:56:15 -08:00
/// imported or the "Don't ask me again" option was selected
/// </summary>
2016-07-13 17:15:43 -07:00
public bool PrintersImported { get ; set ; } = false ;
2016-10-21 12:59:10 -07:00
public string ProfilePath ( string printerID )
2016-07-11 15:14:23 -07:00
{
2020-07-16 07:59:57 -07:00
var printer = this [ printerID ] ;
if ( printer ! = null )
{
return ProfilePath ( printer ) ;
}
// the printer may have been deleted
return null ;
2016-07-11 15:14:23 -07:00
}
2016-07-13 14:40:08 -07:00
public string ProfilePath ( PrinterInfo printer )
{
2016-10-21 12:59:10 -07:00
return Path . Combine ( UserProfilesDirectory , printer . ID + ProfileExtension ) ;
2016-07-13 14:40:08 -07:00
}
2018-11-15 15:34:13 -08:00
public PrinterSettings LoadSettingsWithoutRecovery ( string profileID )
2016-08-11 11:09:06 -07:00
{
2016-09-28 16:29:11 -07:00
var printerInfo = Instance [ profileID ] ;
string profilePath = printerInfo ? . ProfilePath ;
2018-04-17 17:13:18 -07:00
if ( profilePath ! = null
2016-09-28 16:29:11 -07:00
& & File . Exists ( profilePath )
& & ! printerInfo . MarkedForDelete )
2016-08-11 11:09:06 -07:00
{
try
{
return PrinterSettings . LoadFile ( profilePath ) ;
}
catch
{
return null ;
}
}
return null ;
}
2016-07-08 07:30:33 -07:00
/// <summary>
2018-11-15 15:34:13 -08:00
/// Loads the specified printer settings, performing recovery options if required
2016-07-08 07:30:33 -07:00
/// </summary>
2018-11-15 15:34:13 -08:00
/// <param name="printerID">The printer ID to load</param>
2016-07-08 07:30:33 -07:00
/// <param name="useActiveInstance">Return the in memory instance if already loaded. Alternatively, reload from disk</param>
/// <returns></returns>
2018-11-15 15:34:13 -08:00
public static async Task < PrinterSettings > LoadSettingsAsync ( string printerID , bool useActiveInstance = true )
2016-06-15 14:53:11 -07:00
{
2018-11-12 07:26:34 -08:00
// Check loaded printers for printerID and return if found
2018-11-15 15:34:13 -08:00
if ( ApplicationController . Instance . ActivePrinters . FirstOrDefault ( p = > p . Settings . ID = = printerID ) is PrinterConfig activePrinter )
2016-08-11 11:23:05 -07:00
{
2018-10-05 09:24:57 -07:00
return activePrinter . Settings ;
2016-08-11 11:23:05 -07:00
}
2016-08-10 09:49:32 -07:00
2016-07-13 14:40:08 -07:00
// Only load profiles by ID that are defined in the profiles document
2018-11-15 15:34:13 -08:00
var printerInfo = Instance [ printerID ] ;
2016-08-10 09:49:32 -07:00
if ( printerInfo = = null )
2016-06-16 10:38:37 -07:00
{
return null ;
}
2016-08-11 11:23:05 -07:00
// Attempt to load from disk, pull from the web or fall back using recovery logic
2018-11-15 15:34:13 -08:00
PrinterSettings printerSettings = Instance . LoadSettingsWithoutRecovery ( printerID ) ;
2016-08-11 11:23:05 -07:00
if ( printerSettings ! = null )
2016-07-01 17:11:05 -07:00
{
2017-08-11 16:00:24 -07:00
// Make sure we have the name set
if ( printerSettings . GetValue ( SettingsKey . printer_name ) = = "" )
{
// This can happen when a profile is pushed to a user account from the web.
printerSettings . SetValue ( SettingsKey . printer_name , printerInfo . Name ) ;
}
2016-07-01 17:11:05 -07:00
}
2016-08-15 13:34:38 -07:00
else if ( ApplicationController . GetPrinterProfileAsync ! = null )
2016-08-10 09:49:32 -07:00
{
// Attempt to load from MCWS if missing on disk
2016-08-15 13:34:38 -07:00
printerSettings = await ApplicationController . GetPrinterProfileAsync ( printerInfo , null ) ;
2016-08-10 09:49:32 -07:00
if ( printerSettings ! = null )
{
2016-08-11 11:23:05 -07:00
// If successful, persist downloaded profile and return
2019-05-29 14:16:25 -07:00
printerSettings . Save ( userDrivenChange : false ) ;
2016-08-10 09:49:32 -07:00
}
2016-07-07 17:05:50 -07:00
}
2016-08-10 09:49:32 -07:00
2018-11-30 10:32:30 -08:00
// Recover to a default working profile if still null
if ( printerSettings = = null )
{
printerSettings = await RecoverProfile ( printerInfo ) ;
}
// Register listener on non-null settings
if ( printerSettings ! = null )
{
2020-08-21 11:35:12 -07:00
// TODO: This is likely to leak and keep printerSettings in memory in some cases until we combine PrinterConfig
// loading into profile manager and have a single owner of loaded printers that can unwire this when their
2018-11-30 10:32:30 -08:00
// tabs close
//
// Register listener
printerSettings . SettingChanged + = PrinterSettings_SettingChanged ;
}
return printerSettings ;
}
2022-03-18 15:58:13 -07:00
public static bool SaveOnSingleSettingChange { get ; set ; } = true ;
2018-11-30 10:32:30 -08:00
// Settings persistence moved from PrinterSettings into ProfileManager to break dependency around ProfileManager paths/MatterControl specific details
2018-12-20 12:38:05 -08:00
private static void PrinterSettings_SettingChanged ( object sender , StringEventArgs e )
2018-11-30 10:32:30 -08:00
{
2022-03-18 15:58:13 -07:00
if ( SaveOnSingleSettingChange
& & sender is PrinterSettings settings )
2018-11-30 10:32:30 -08:00
{
settings . Save ( ) ;
}
2016-06-15 14:53:11 -07:00
}
2020-08-21 11:35:12 -07:00
public static async Task < PrinterSettings > RecoverProfile ( PrinterInfo printerInfo )
2018-11-24 07:29:56 -08:00
{
bool userIsLoggedIn = ! ApplicationController . GuestUserActive ? . Invoke ( ) ? ? false ;
if ( userIsLoggedIn & & printerInfo ! = null )
{
// Attempt to load from MCWS history
var printerSettings = await GetFirstValidHistoryItem ( printerInfo ) ;
if ( printerSettings = = null )
{
// Fall back to OemProfile defaults if load from history fails
printerSettings = RestoreFromOemProfile ( printerInfo ) ;
}
if ( printerSettings = = null )
{
// If we still have failed to recover a profile, create an empty profile with
// just enough data to delete the printer
printerSettings = PrinterSettings . Empty ;
printerSettings . ID = printerInfo . ID ;
printerSettings . UserLayer [ SettingsKey . device_token ] = printerInfo . DeviceToken ;
printerSettings . Helpers . SetComPort ( printerInfo . ComPort ) ;
printerSettings . SetValue ( SettingsKey . printer_name , printerInfo . Name ) ;
// Add any setting value to the OemLayer to pass the .PrinterSelected property
2020-09-01 18:07:34 -07:00
printerSettings . OemLayer = new PrinterSettingsLayer
{
{ "empty" , "setting" }
} ;
2019-05-29 14:16:25 -07:00
printerSettings . Save ( userDrivenChange : false ) ;
2018-11-24 07:29:56 -08:00
}
if ( printerSettings ! = null )
{
// Persist any profile recovered above as the current
2019-05-29 14:16:25 -07:00
printerSettings . Save ( userDrivenChange : false ) ;
2018-11-24 07:29:56 -08:00
WarnAboutRevert ( printerInfo ) ;
}
return printerSettings ;
}
return null ;
}
private static bool warningWindowOpen = false ;
public static void WarnAboutRevert ( PrinterInfo profile )
{
if ( ! warningWindowOpen )
{
warningWindowOpen = true ;
UiThread . RunOnIdle ( ( ) = >
{
StyledMessageBox . ShowMessageBox ( ( clicedOk ) = >
{
warningWindowOpen = false ;
} ,
"The profile you are attempting to load has been corrupted. We loaded your last usable {0} {1} profile from your recent profile history instead." . Localize ( )
. FormatWith ( profile . Make , profile . Model ) ,
"Recovered printer profile" . Localize ( ) ,
messageType : StyledMessageBox . MessageType . OK ) ;
} ) ;
}
}
public static PrinterSettings RestoreFromOemProfile ( PrinterInfo profile )
{
PrinterSettings oemProfile = null ;
try
{
var publicDevice = OemSettings . Instance . OemProfiles [ profile . Make ] [ profile . Model ] ;
string cacheScope = Path . Combine ( "public-profiles" , profile . Make ) ;
string publicProfileToLoad = ApplicationController . CacheablePath ( cacheScope , publicDevice . CacheKey ) ;
oemProfile = JsonConvert . DeserializeObject < PrinterSettings > ( File . ReadAllText ( publicProfileToLoad ) ) ;
oemProfile . ID = profile . ID ;
oemProfile . SetValue ( SettingsKey . printer_name , profile . Name ) ;
oemProfile . DocumentVersion = PrinterSettings . LatestVersion ;
oemProfile . Helpers . SetComPort ( profile . ComPort ) ;
2019-05-29 14:16:25 -07:00
oemProfile . Save ( userDrivenChange : false ) ;
2018-11-24 07:29:56 -08:00
}
2018-12-10 14:32:56 -08:00
catch
{
}
2018-11-24 07:29:56 -08:00
return oemProfile ;
}
private static async Task < PrinterSettings > GetFirstValidHistoryItem ( PrinterInfo printerInfo )
{
var recentProfileHistoryItems = await ApplicationController . GetProfileHistory ? . Invoke ( printerInfo . DeviceToken ) ;
if ( recentProfileHistoryItems ! = null )
{
// Iterate history, skipping the first item, limiting to the next five, attempt to load and return the first success
foreach ( var keyValue in recentProfileHistoryItems . OrderByDescending ( kvp = > kvp . Key ) . Skip ( 1 ) . Take ( 5 ) )
{
// Attempt to download and parse each profile, returning if successful
try
{
var printerSettings = await ApplicationController . GetPrinterProfileAsync ( printerInfo , keyValue . Value ) ;
if ( printerSettings ! = null )
{
return printerSettings ;
}
}
2018-12-10 14:32:56 -08:00
catch
{
}
2018-11-24 07:29:56 -08:00
}
}
return null ;
}
2020-10-30 18:03:37 -07:00
internal static bool ImportFromExisting ( string settingsFilePath , bool resetSettingsForNewProfile , out string printerName )
2016-06-15 14:53:11 -07:00
{
2020-10-30 18:03:37 -07:00
printerName = Path . GetFileNameWithoutExtension ( settingsFilePath ) ;
2016-06-15 14:53:11 -07:00
if ( string . IsNullOrEmpty ( settingsFilePath ) | | ! File . Exists ( settingsFilePath ) )
{
2016-08-10 16:38:58 -07:00
return false ;
2016-06-15 14:53:11 -07:00
}
2016-10-04 17:54:57 -07:00
string fileName = Path . GetFileNameWithoutExtension ( settingsFilePath ) ;
2021-02-05 17:52:19 -08:00
var existingPrinterNames = new HashSet < string > ( Instance . ActiveProfiles . Select ( p = > p . Name ) ) ;
2016-10-04 17:54:57 -07:00
2016-06-15 14:53:11 -07:00
var printerInfo = new PrinterInfo
{
2017-12-06 16:29:31 -08:00
Name = agg_basics . GetNonCollidingName ( fileName , existingPrinterNames ) ,
2016-10-04 17:54:57 -07:00
ID = Guid . NewGuid ( ) . ToString ( ) ,
Make = "Other" ,
Model = "Other" ,
2016-06-15 14:53:11 -07:00
} ;
2016-09-04 11:06:07 -07:00
2016-08-10 16:38:58 -07:00
bool importSuccessful = false ;
2016-09-04 11:06:07 -07:00
2016-06-15 14:53:11 -07:00
string importType = Path . GetExtension ( settingsFilePath ) . ToLower ( ) ;
switch ( importType )
{
2016-07-14 11:36:14 -07:00
case ProfileManager . ProfileExtension :
2016-09-04 11:06:07 -07:00
// Add the Settings as a profile before performing any actions on it to ensure file paths resolve
2016-10-04 17:54:57 -07:00
{
Instance . Profiles . Add ( printerInfo ) ;
2016-09-04 11:06:07 -07:00
2016-10-04 17:54:57 -07:00
var printerSettings = PrinterSettings . LoadFile ( settingsFilePath ) ;
printerSettings . ID = printerInfo . ID ;
printerSettings . ClearValue ( SettingsKey . device_token ) ;
printerInfo . DeviceToken = "" ;
2016-06-15 14:53:11 -07:00
2016-10-04 17:54:57 -07:00
// TODO: Resolve name conflicts
printerSettings . Helpers . SetName ( printerInfo . Name ) ;
2016-06-15 14:53:11 -07:00
2016-10-10 12:44:22 -07:00
if ( printerSettings . OemLayer . ContainsKey ( SettingsKey . make ) )
{
printerInfo . Make = printerSettings . OemLayer [ SettingsKey . make ] ;
}
if ( printerSettings . OemLayer . ContainsKey ( SettingsKey . model ) )
{
printerInfo . Model = printerSettings . OemLayer [ SettingsKey . model ] ? ? "Other" ;
}
2016-10-04 17:54:57 -07:00
2020-08-21 11:35:12 -07:00
if ( resetSettingsForNewProfile )
2019-05-29 12:20:06 -07:00
{
2020-08-21 11:35:12 -07:00
printerSettings . ResetSettingsForNewProfile ( ) ;
2019-05-29 12:20:06 -07:00
}
2019-05-29 14:16:25 -07:00
printerSettings . Save ( userDrivenChange : false ) ;
2016-10-04 17:54:57 -07:00
importSuccessful = true ;
}
2020-08-21 11:35:12 -07:00
2016-06-15 14:53:11 -07:00
break ;
2020-10-30 18:03:37 -07:00
case ".fff" : // simplify profile
{
// load the material settings
var materials = PrinterSettingsLayer . LoadMaterialSettingsFromFff ( settingsFilePath ) ;
// load the quality settings
var qualitySettings = PrinterSettingsLayer . LoadQualitySettingsFromFff ( settingsFilePath ) ;
// load the main settings
var settingsToImport = PrinterSettingsLayer . LoadFromFff ( settingsFilePath ) ;
var printerSettings = new PrinterSettings ( )
{
ID = printerInfo . ID ,
} ;
// create the profile we will populate
printerSettings . OemLayer = new PrinterSettingsLayer
{
[SettingsKey.make] = "Other" ,
[SettingsKey.model] = "Other"
} ;
// add all the main settings
foreach ( var item in settingsToImport )
{
if ( printerSettings . Contains ( item . Key ) )
{
string currentValue = printerSettings . GetValue ( item . Key ) . Trim ( ) ;
// Compare the value to import to the layer cascade value and only set if different
if ( currentValue ! = item . Value )
{
printerSettings . OemLayer [ item . Key ] = item . Value ;
}
}
}
printerName = settingsToImport [ SettingsKey . printer_name ] ;
printerSettings . UserLayer [ SettingsKey . printer_name ] = printerName ;
printerSettings . ClearValue ( SettingsKey . device_token ) ;
printerInfo . DeviceToken = "" ;
printerInfo . Name = printerName ;
Instance . Profiles . Add ( printerInfo ) ;
printerSettings . Helpers . SetName ( printerName ) ;
// copy in the material settings
if ( materials . Count > 0 )
{
printerSettings . MaterialLayers . AddRange ( materials . Values ) ;
// set the preferred setting if described
if ( settingsToImport . ContainsKey ( "printMaterial" ) )
{
foreach ( var material in printerSettings . MaterialLayers )
{
if ( material . Name = = settingsToImport [ "printMaterial" ] )
{
printerSettings . SetValue ( SettingsKey . active_material_key , material . LayerID ) ;
break ;
}
}
}
}
// copy in the quality settings
if ( qualitySettings . Count > 0 )
{
printerSettings . QualityLayers . AddRange ( qualitySettings . Values ) ;
// set the preferred setting if described
if ( settingsToImport . ContainsKey ( "printQuality" ) )
{
foreach ( var qualitySetting in printerSettings . QualityLayers )
{
if ( qualitySetting . Name = = settingsToImport [ "printQuality" ] )
{
printerSettings . SetValue ( SettingsKey . active_quality_key , qualitySetting . LayerID ) ;
break ;
}
}
}
}
printerSettings . ResetSettingsForNewProfile ( ) ;
printerSettings . Save ( userDrivenChange : false ) ;
importSuccessful = true ;
}
break ;
2016-06-15 14:53:11 -07:00
case ".ini" :
2020-08-21 11:35:12 -07:00
// Scope variables
2016-06-15 14:53:11 -07:00
{
2016-08-11 14:59:23 -07:00
var settingsToImport = PrinterSettingsLayer . LoadFromIni ( settingsFilePath ) ;
2016-10-04 17:54:57 -07:00
var printerSettings = new PrinterSettings ( )
2016-08-10 16:38:58 -07:00
{
ID = printerInfo . ID ,
} ;
2016-08-11 14:59:23 -07:00
bool containsValidSetting = false ;
2016-10-04 17:54:57 -07:00
2020-09-01 18:07:34 -07:00
printerSettings . OemLayer = new PrinterSettingsLayer
{
[SettingsKey.make] = "Other" ,
[SettingsKey.model] = "Other"
} ;
2016-08-10 16:38:58 -07:00
2016-08-11 14:59:23 -07:00
foreach ( var item in settingsToImport )
{
2016-10-04 17:54:57 -07:00
if ( printerSettings . Contains ( item . Key ) )
2016-08-11 14:59:23 -07:00
{
containsValidSetting = true ;
2016-10-04 17:54:57 -07:00
string currentValue = printerSettings . GetValue ( item . Key ) . Trim ( ) ;
2016-08-11 14:59:23 -07:00
// Compare the value to import to the layer cascade value and only set if different
if ( currentValue ! = item . Value )
{
2016-10-04 17:54:57 -07:00
printerSettings . OemLayer [ item . Key ] = item . Value ;
2016-08-11 14:59:23 -07:00
}
}
}
2016-10-04 17:54:57 -07:00
2019-05-29 14:16:25 -07:00
if ( containsValidSetting )
2016-08-11 14:59:23 -07:00
{
2016-10-04 17:54:57 -07:00
printerSettings . UserLayer [ SettingsKey . printer_name ] = printerInfo . Name ;
2016-08-11 14:59:23 -07:00
2016-10-04 17:54:57 -07:00
printerSettings . ClearValue ( SettingsKey . device_token ) ;
2016-08-11 14:59:23 -07:00
printerInfo . DeviceToken = "" ;
2016-10-04 17:54:57 -07:00
printerInfo . Make = printerSettings . OemLayer [ SettingsKey . make ] ? ? "Other" ;
printerInfo . Model = printerSettings . OemLayer [ SettingsKey . model ] ? ? "Other" ;
2016-08-11 14:59:23 -07:00
Instance . Profiles . Add ( printerInfo ) ;
2016-08-10 16:38:58 -07:00
2016-10-04 17:54:57 -07:00
printerSettings . Helpers . SetName ( printerInfo . Name ) ;
2020-08-21 11:35:12 -07:00
if ( resetSettingsForNewProfile )
2019-05-29 12:20:06 -07:00
{
2020-08-21 11:35:12 -07:00
printerSettings . ResetSettingsForNewProfile ( ) ;
2019-05-29 12:20:06 -07:00
}
2019-05-29 14:16:25 -07:00
printerSettings . Save ( userDrivenChange : false ) ;
2016-08-11 14:59:23 -07:00
importSuccessful = true ;
}
2016-08-10 16:38:58 -07:00
}
2019-05-29 12:20:06 -07:00
2016-06-15 14:53:11 -07:00
break ;
}
2018-10-24 18:08:41 -07:00
2016-08-10 16:38:58 -07:00
return importSuccessful ;
2016-06-15 14:53:11 -07:00
}
2019-12-06 10:19:47 -08:00
public static async Task < PrinterConfig > CreatePrinterAsync ( string make , string model , string printerName )
2016-06-15 14:53:11 -07:00
{
string guid = Guid . NewGuid ( ) . ToString ( ) ;
2016-09-01 17:24:09 -07:00
var publicDevice = OemSettings . Instance . OemProfiles [ make ] [ model ] ;
2016-10-04 14:40:59 -07:00
if ( publicDevice = = null )
{
2017-09-17 21:08:16 -07:00
return null ;
2016-10-04 14:40:59 -07:00
}
2016-09-01 17:24:09 -07:00
2018-11-15 15:34:13 -08:00
var printerSettings = await LoadOemSettingsAsync ( publicDevice , make , model ) ;
2016-10-04 14:40:59 -07:00
if ( printerSettings = = null )
{
2017-09-17 21:08:16 -07:00
return null ;
2016-10-04 14:40:59 -07:00
}
2016-09-14 12:36:05 -07:00
printerSettings . ID = guid ;
printerSettings . DocumentVersion = PrinterSettings . LatestVersion ;
2016-06-15 14:53:11 -07:00
2018-10-13 17:58:54 -07:00
printerSettings . UserLayer [ SettingsKey . printer_name ] = printerName ;
2016-10-18 11:45:47 -07:00
2016-09-14 12:36:05 -07:00
// Add to Profiles - fires ProfileManager.Save due to ObservableCollection event listener
2016-06-15 14:53:11 -07:00
Instance . Profiles . Add ( new PrinterInfo
{
Name = printerName ,
2016-07-14 16:48:18 -07:00
ID = guid ,
Make = make ,
Model = model
2016-06-15 14:53:11 -07:00
} ) ;
2016-09-14 12:36:05 -07:00
// Persist changes to PrinterSettings - must come after adding to Profiles above
2020-08-21 11:35:12 -07:00
printerSettings . ResetSettingsForNewProfile ( ) ;
2019-05-29 14:16:25 -07:00
printerSettings . Save ( userDrivenChange : false ) ;
2016-07-07 12:24:42 -07:00
2016-09-14 12:36:05 -07:00
// Set as active profile
2022-01-20 14:52:03 -08:00
return await ApplicationController . Instance . OpenEmptyPrinter ( guid , true ) ;
2016-06-15 14:53:11 -07:00
}
2020-06-05 07:52:23 -07:00
public static async Task < PrinterSettings > LoadOemSettingsAsync ( PublicDevice publicDevice , string make , string model )
2016-06-15 14:53:11 -07:00
{
2016-09-01 17:24:09 -07:00
string cacheScope = Path . Combine ( "public-profiles" , make ) ;
string cachePath = ApplicationController . CacheablePath ( cacheScope , publicDevice . CacheKey ) ;
2016-07-24 08:21:59 -07:00
2016-07-22 13:57:55 -07:00
return await ApplicationController . LoadCacheableAsync < PrinterSettings > (
2016-09-01 17:24:09 -07:00
publicDevice . CacheKey ,
cacheScope ,
2016-07-25 14:37:38 -07:00
async ( ) = >
2016-06-27 12:35:12 -07:00
{
2016-09-01 17:24:09 -07:00
// The collector specifically returns null to ensure LoadCacheable skips writing the
// result to the cache. After this result is returned, it will attempt to load from
// the local cache if the collector yielded no result
2020-06-05 07:52:23 -07:00
if ( File . Exists ( cachePath )
2017-02-24 09:02:50 -08:00
| | ApplicationController . DownloadPublicProfileAsync = = null )
2016-07-26 12:54:16 -07:00
{
return null ;
}
else
2016-07-11 16:53:00 -07:00
{
2016-09-01 17:24:09 -07:00
// If the cache file for the current deviceToken does not exist, attempt to download it.
// An http 304 results in a null value and LoadCacheable will then load from the cache
return await ApplicationController . DownloadPublicProfileAsync ( publicDevice . ProfileToken ) ;
2016-07-11 16:53:00 -07:00
}
2016-07-21 14:23:22 -07:00
} ,
2016-09-08 18:10:25 -07:00
Path . Combine ( "Profiles" , make , model + ProfileManager . ProfileExtension ) ) ;
2016-06-15 14:53:11 -07:00
}
2018-11-21 08:37:29 -08:00
private void EnsurePrintersImported ( )
2016-07-13 17:15:43 -07:00
{
if ( IsGuestProfile & & ! PrintersImported )
{
2018-11-21 11:38:12 -08:00
int intialCount = this . Profiles . Count ;
2016-07-13 17:15:43 -07:00
// Import Sqlite printer profiles into local json files
2020-11-05 09:47:50 -08:00
DataStorage . ClassicDB . ClassicSqlitePrinterProfiles . ImportPrinters ( Instance ) ;
2016-07-13 17:15:43 -07:00
PrintersImported = true ;
2018-11-21 11:38:12 -08:00
if ( intialCount ! = this . Profiles . Count )
{
this . Save ( ) ;
}
2016-07-13 17:15:43 -07:00
}
}
2016-06-15 14:53:11 -07:00
private static void Profiles_CollectionChanged ( object sender , System . Collections . Specialized . NotifyCollectionChangedEventArgs e )
{
2016-06-20 13:13:18 -07:00
// Any time the list changes, persist the updates to disk
Instance . Save ( ) ;
2016-06-16 13:23:44 -07:00
ProfilesListChanged . CallEvents ( null , null ) ;
2016-10-04 13:21:44 -07:00
2019-04-17 07:33:14 -07:00
// Queue sync after any collection change event
ApplicationController . QueueCloudProfileSync ? . Invoke ( "ProfileManager.Profiles_CollectionChanged()" ) ;
2016-06-15 14:53:11 -07:00
}
2018-12-20 12:38:05 -08:00
private void Printer_SettingsChanged ( object sender , StringEventArgs e )
2018-11-16 14:42:27 -08:00
{
2020-09-01 18:07:34 -07:00
var settings = sender as PrinterSettings ;
2018-12-19 09:09:17 -08:00
string printerID = settings ? . ID ;
2018-11-16 14:42:27 -08:00
2018-12-19 09:09:17 -08:00
if ( string . IsNullOrWhiteSpace ( printerID ) )
2018-11-16 14:42:27 -08:00
{
2018-12-19 09:09:17 -08:00
// Exit early if PrinterSettings or ID is invalid
2018-11-16 14:42:27 -08:00
return ;
}
2018-12-19 09:09:17 -08:00
var profile = Instance [ printerID ] ;
2018-11-16 14:42:27 -08:00
if ( profile = = null )
{
2018-12-19 09:09:17 -08:00
// Exit early if printer is not known
2018-11-16 14:42:27 -08:00
return ;
}
2018-12-20 12:38:05 -08:00
string settingsKey = e ? . Data ;
2018-11-16 14:42:27 -08:00
switch ( settingsKey )
{
case SettingsKey . printer_name :
2018-12-19 09:09:17 -08:00
profile . Name = settings . GetValue ( SettingsKey . printer_name ) ;
2018-11-16 14:42:27 -08:00
Instance . Save ( ) ;
break ;
case SettingsKey . com_port :
2018-12-19 09:09:17 -08:00
profile . ComPort = settings . Helpers . ComPort ( ) ;
2018-11-16 14:42:27 -08:00
Instance . Save ( ) ;
break ;
}
}
2018-11-21 10:58:52 -08:00
internal void ChangeID ( string oldID , string newID )
{
if ( _activeProfileIDs . Contains ( oldID ) )
{
_activeProfileIDs . Remove ( oldID ) ;
_activeProfileIDs . Add ( newID ) ;
}
}
2016-06-15 14:53:11 -07:00
public void Save ( )
{
2020-09-01 18:07:34 -07:00
lock ( writeLock )
2016-08-30 10:41:38 -07:00
{
2021-11-26 07:41:43 -08:00
try
{
File . WriteAllText ( ProfilesDocPath , JsonConvert . SerializeObject ( this , Newtonsoft . Json . Formatting . Indented ) ) ;
}
catch ( Exception ex )
{
ApplicationController . Instance . ShowNotification ( $"Profile Save Error: {ex.Message}" ) ;
}
2016-08-30 10:41:38 -07:00
}
2016-06-15 14:53:11 -07:00
}
2018-11-16 14:39:20 -08:00
public void Dispose ( )
{
2018-11-16 14:42:27 -08:00
// Unregister listeners
PrinterSettings . AnyPrinterSettingChanged - = this . Printer_SettingsChanged ;
2018-11-16 14:39:20 -08:00
}
2016-06-15 14:53:11 -07:00
}
}