Files
Artemis/Artemis/Artemis/Models/ProfileEditorModel.cs
T

365 lines
13 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Artemis.DAL;
using Artemis.Managers;
using Artemis.Modules.Abstract;
using Artemis.Profiles;
using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Folder;
using Artemis.Properties;
using Artemis.Services;
using Artemis.Utilities;
using Artemis.ViewModels;
using Ninject.Parameters;
namespace Artemis.Models
{
public class ProfileEditorModel : IDisposable
{
private readonly DeviceManager _deviceManager;
private readonly LuaManager _luaManager;
private readonly DialogService _dialogService;
private readonly WindowService _windowService;
private FileSystemWatcher _watcher;
private ModuleModel _luaModule;
public ProfileEditorModel(WindowService windowService, MetroDialogService dialogService, DeviceManager deviceManager, LuaManager luaManager)
{
_windowService = windowService;
_dialogService = dialogService;
_deviceManager = deviceManager;
_luaManager = luaManager;
}
#region Layers
/// <summary>
/// Opens a new LayerEditorView for the given layer
/// </summary>
/// <param name="layer">The layer to open the view for</param>
/// <param name="dataModel">The datamodel to bind the editor to</param>
public void EditLayer(LayerModel layer, ModuleDataModel dataModel)
{
IParameter[] args =
{
new ConstructorArgument("dataModel", dataModel),
new ConstructorArgument("layer", layer)
};
_windowService.ShowDialog<LayerEditorViewModel>("Artemis | Edit layer", null, args);
// If the layer was a folder, but isn't anymore, assign it's children to it's parent.
if (layer.LayerType is FolderType || !layer.Children.Any())
return;
while (layer.Children.Any())
{
var child = layer.Children[0];
layer.Children.Remove(child);
if (layer.Parent != null)
{
layer.Parent.Children.Add(child);
layer.Parent.FixOrder();
}
else
{
layer.Profile.Layers.Add(child);
layer.Profile.FixOrder();
}
}
}
/// <summary>
/// Removes the given layer from the profile
/// </summary>
/// <param name="layer">The layer to remove</param>
/// <param name="profileModel">The profile to remove it from</param>
public void RemoveLayer(LayerModel layer, ProfileModel profileModel)
{
if (layer == null)
return;
if (layer.Parent != null)
{
var parent = layer.Parent;
layer.Parent.Children.Remove(layer);
parent.FixOrder();
}
else if (layer.Profile != null)
{
var profile = layer.Profile;
layer.Profile.Layers.Remove(layer);
profile.FixOrder();
}
// Extra cleanup in case of a wonky layer that has no parent
if (profileModel.Layers.Contains(layer))
profileModel.Layers.Remove(layer);
}
#endregion
#region Profiles
public async Task<ProfileModel> AddProfile(ModuleModel moduleModel)
{
if (_deviceManager.ActiveKeyboard == null)
{
_dialogService.ShowMessageBox("Cannot add profile.",
"To add a profile, please select a keyboard in the options menu first.");
return null;
}
var name = await GetValidProfileName("Name profile", "Please enter a unique name for your new profile");
// User cancelled
if (name == null)
return null;
var profile = new ProfileModel
{
Name = name,
KeyboardSlug = _deviceManager.ActiveKeyboard.Slug,
Width = _deviceManager.ActiveKeyboard.Width,
Height = _deviceManager.ActiveKeyboard.Height,
GameName = moduleModel.Name
};
if (!ProfileProvider.IsProfileUnique(profile))
{
var overwrite = await _dialogService.ShowQuestionMessageBox("Overwrite existing profile",
"A profile with this name already exists for this game. Would you like to overwrite it?");
if (!overwrite.Value)
return null;
}
ProfileProvider.AddOrUpdate(profile);
return profile;
}
public async Task RenameProfile(ProfileModel profileModel)
{
// Store the old name
var oldName = profileModel.Name;
var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
// User cancelled
if (name == null)
return;
// MakeProfileUnique does a check but also modifies the profile, set the old name back
var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
var newName = profileModel.Name;
profileModel.Name = oldName;
if (!doRename)
return;
ProfileProvider.RenameProfile(profileModel, newName);
}
public async Task<ProfileModel> DuplicateProfile(ProfileModel selectedProfile)
{
var newProfile = GeneralHelpers.Clone(selectedProfile);
var name = await GetValidProfileName("Duplicate profile", "Please enter a unique new profile name");
// User cancelled
if (name == null)
return null;
var doRename = await MakeProfileUnique(newProfile, name, newProfile.Name);
if (!doRename)
return null;
// Make sure it's not default, in case of copying a default profile
newProfile.IsDefault = false;
ProfileProvider.AddOrUpdate(newProfile);
return newProfile;
}
public async Task<bool> ConfirmDeleteProfile(ProfileModel selectedProfile, ModuleModel moduleModel)
{
var confirm = await _dialogService.ShowQuestionMessageBox("Delete profile",
$"Are you sure you want to delete the profile named: {selectedProfile.Name}?\n\n" +
"This cannot be undone.");
return confirm.Value;
}
public async Task<ProfileModel> ImportProfile(ModuleModel moduleModel)
{
var dialog = new OpenFileDialog {Filter = "Artemis profile (*.json)|*.json"};
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return null;
var profileModel = ProfileProvider.LoadProfileIfValid(dialog.FileName);
if (profileModel == null)
{
_dialogService.ShowErrorMessageBox("Oh noes, the profile you provided is invalid. " +
"If this keeps happening, please make an issue on GitHub and provide the profile.");
return null;
}
// Verify the game
if (profileModel.GameName != moduleModel.Name)
{
_dialogService.ShowErrorMessageBox(
$"Oh oops! This profile is ment for {profileModel.GameName}, not {moduleModel.Name} :c");
return null;
}
// Verify the keyboard
var deviceManager = _deviceManager;
if (profileModel.KeyboardSlug != deviceManager.ActiveKeyboard.Slug)
{
var adjustKeyboard = await _dialogService.ShowQuestionMessageBox("Profile not made for this keyboard",
$"Watch out, this profile wasn't ment for this keyboard, but for the {profileModel.KeyboardSlug}. " +
"You can still import it but you'll probably have to do some adjusting\n\n" +
"Continue?");
if (!adjustKeyboard.Value)
return null;
// Resize layers that are on the full keyboard width
profileModel.ResizeLayers(deviceManager.ActiveKeyboard);
// Put layers back into the canvas if they fell outside it
profileModel.FixBoundaries(deviceManager.ActiveKeyboard.KeyboardRectangle(1));
// Setup profile metadata to match the new keyboard
profileModel.KeyboardSlug = deviceManager.ActiveKeyboard.Slug;
profileModel.Width = deviceManager.ActiveKeyboard.Width;
profileModel.Height = deviceManager.ActiveKeyboard.Height;
}
var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
// User cancelled
if (name == null)
return null;
var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
if (!doRename)
return null;
profileModel.IsDefault = false;
ProfileProvider.AddOrUpdate(profileModel);
return profileModel;
}
public void ChangeProfileByName(ModuleModel moduleModel, string profileName)
{
if (string.IsNullOrEmpty(profileName))
profileName = "Default";
moduleModel.ChangeProfile(ProfileProvider.GetProfile(_deviceManager.ActiveKeyboard, moduleModel, profileName));
}
private async Task<string> GetValidProfileName(string title, string text)
{
var name = await _dialogService.ShowInputDialog(title, text);
// Null when the user cancelled
if (name == null)
return null;
if (name.Length >= 2)
return name;
_dialogService.ShowMessageBox("Invalid profile name",
"Please provide a valid profile name that's longer than 2 symbols");
return await GetValidProfileName(title, text);
}
private async Task<bool> MakeProfileUnique(ProfileModel profileModel, string name, string oldName)
{
profileModel.Name = name;
if (ProfileProvider.IsProfileUnique(profileModel))
return true;
name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
if (name != null)
return await MakeProfileUnique(profileModel, name, oldName);
// If cancelled, restore old name and stop
profileModel.Name = oldName;
return false;
}
#endregion
#region LUA
public void OpenLuaEditor(ModuleModel moduleModel)
{
// Clean up old environment
DisposeLuaWatcher();
// Create a temp file
var fileName = Guid.NewGuid() + ".lua";
var file = File.Create(Path.GetTempPath() + fileName);
file.Dispose();
// Add instructions to LUA script if it's a new file
if (string.IsNullOrEmpty(moduleModel.ProfileModel.LuaScript))
moduleModel.ProfileModel.LuaScript = Encoding.UTF8.GetString(Resources.lua_placeholder);
File.WriteAllText(Path.GetTempPath() + fileName, moduleModel.ProfileModel.LuaScript);
// Watch the file for changes
_luaModule = moduleModel;
_watcher = new FileSystemWatcher(Path.GetTempPath(), fileName);
_watcher.Changed += LuaFileChanged;
_watcher.EnableRaisingEvents = true;
_watcher.Path = Path.GetTempPath();
_watcher.Filter = fileName;
// Open the temp file with the default editor
System.Diagnostics.Process.Start(Path.GetTempPath() + fileName);
}
private void LuaFileChanged(object sender, FileSystemEventArgs args)
{
if (_luaModule == null)
{
DisposeLuaWatcher();
return;
}
if (args.ChangeType != WatcherChangeTypes.Changed)
return;
lock (_luaModule)
{
using (var fs = new FileStream(args.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var sr = new StreamReader(fs))
{
_luaModule.ProfileModel.LuaScript = sr.ReadToEnd();
}
}
ProfileProvider.AddOrUpdate(_luaModule.ProfileModel);
_luaManager.SetupLua(_luaModule.ProfileModel);
}
}
private void DisposeLuaWatcher()
{
if (_watcher == null)
return;
_watcher.Changed -= LuaFileChanged;
_watcher.Dispose();
_watcher = null;
}
public void Dispose()
{
DisposeLuaWatcher();
}
#endregion
#region Rendering
#endregion
}
}