diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj
index 3500de5f3..6452856de 100644
--- a/Artemis/Artemis/Artemis.csproj
+++ b/Artemis/Artemis/Artemis.csproj
@@ -356,6 +356,8 @@
+
+
@@ -654,17 +656,16 @@
-
-
+
-
+
@@ -709,7 +710,7 @@
LayerDynamicPropertiesView.xaml
-
+
LayerEditorView.xaml
@@ -721,7 +722,7 @@
LayerTweenView.xaml
-
+
ProfileEditorView.xaml
@@ -952,7 +953,7 @@
Designer
MSBuild:Compile
-
+
MSBuild:Compile
Designer
@@ -968,7 +969,7 @@
Designer
MSBuild:Compile
-
+
Designer
MSBuild:Compile
diff --git a/Artemis/Artemis/InjectionModules/BaseModules.cs b/Artemis/Artemis/InjectionModules/BaseModules.cs
index bd0108284..eb4bc0bad 100644
--- a/Artemis/Artemis/InjectionModules/BaseModules.cs
+++ b/Artemis/Artemis/InjectionModules/BaseModules.cs
@@ -1,4 +1,5 @@
using Artemis.DeviceProviders;
+using Artemis.Models;
using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
@@ -8,7 +9,6 @@ using Artemis.Utilities.DataReaders;
using Artemis.Utilities.GameState;
using Artemis.ViewModels;
using Artemis.ViewModels.Abstract;
-using Artemis.ViewModels.Profiles;
using Ninject.Extensions.Conventions;
using Ninject.Modules;
@@ -18,10 +18,16 @@ namespace Artemis.InjectionModules
{
public override void Load()
{
+ #region Models
+
+ Bind().ToSelf();
+ Bind().ToSelf();
+
+ #endregion
+
#region ViewModels
Bind().ToSelf().InSingletonScope();
- Bind().ToSelf();
Bind().ToSelf();
Bind().ToSelf().InSingletonScope();
Kernel.Bind(x =>
diff --git a/Artemis/Artemis/Models/LayerEditorModel.cs b/Artemis/Artemis/Models/LayerEditorModel.cs
new file mode 100644
index 000000000..c8c374310
--- /dev/null
+++ b/Artemis/Artemis/Models/LayerEditorModel.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Artemis.Models
+{
+ public class LayerEditorModel
+ {
+ }
+}
diff --git a/Artemis/Artemis/Models/ProfileEditorModel.cs b/Artemis/Artemis/Models/ProfileEditorModel.cs
new file mode 100644
index 000000000..caefa2926
--- /dev/null
+++ b/Artemis/Artemis/Models/ProfileEditorModel.cs
@@ -0,0 +1,354 @@
+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 ProfileModel _luaProfile;
+
+ public ProfileEditorModel(WindowService windowService, MetroDialogService dialogService,
+ DeviceManager deviceManager, LuaManager luaManager)
+ {
+ _windowService = windowService;
+ _dialogService = dialogService;
+ _deviceManager = deviceManager;
+ _luaManager = luaManager;
+ }
+
+ #region Layers
+
+ ///
+ /// Opens a new LayerEditorView for the given layer
+ ///
+ /// The layer to open the view for
+ /// The datamodel to bind the editor to
+ public void EditLayer(LayerModel layer, ModuleDataModel dataModel)
+ {
+ IParameter[] args =
+ {
+ new ConstructorArgument("dataModel", dataModel),
+ new ConstructorArgument("layer", layer)
+ };
+ _windowService.ShowDialog(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();
+ }
+ }
+ }
+
+ ///
+ /// Removes the given layer from the profile
+ ///
+ /// The layer to remove
+ /// The profile to remove it from
+ 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 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)
+ {
+ var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
+ // User cancelled
+ if (name == null)
+ return;
+ var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
+ if (!doRename)
+ return;
+
+ ProfileProvider.RenameProfile(profileModel, profileModel.Name);
+ }
+
+ public async Task DuplicateProfile(ProfileModel selectedProfile)
+ {
+ var newProfile = GeneralHelpers.Clone(selectedProfile);
+ var name = await GetValidProfileName("Rename profile", "Please enter a unique new profile name");
+ // User cancelled
+ if (name == null)
+ return;
+ var doRename = await MakeProfileUnique(newProfile, name, newProfile.Name);
+ if (!doRename)
+ return;
+
+ // Make sure it's not default, in case of copying a default profile
+ newProfile.IsDefault = false;
+ ProfileProvider.AddOrUpdate(newProfile);
+ }
+
+ public async Task DeleteProfile(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.");
+ if (!confirm.Value)
+ return;
+
+ var defaultProfile = ProfileProvider.GetProfile(_deviceManager.ActiveKeyboard, moduleModel, "Default");
+ var deleteProfile = selectedProfile;
+
+ moduleModel.ChangeProfile(defaultProfile);
+ ProfileProvider.DeleteProfile(deleteProfile);
+ }
+
+ public async Task ImportProfile(ModuleModel moduleModel)
+ {
+ var dialog = new OpenFileDialog {Filter = "Artemis profile (*.json)|*.json"};
+ var result = dialog.ShowDialog();
+ if (result != DialogResult.OK)
+ return;
+
+ 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;
+ }
+
+ // Verify the game
+ if (profileModel.GameName != moduleModel.Name)
+ {
+ _dialogService.ShowErrorMessageBox(
+ $"Oh oops! This profile is ment for {profileModel.GameName}, not {moduleModel.Name} :c");
+ return;
+ }
+
+ // 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;
+
+ // 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;
+ var doRename = await MakeProfileUnique(profileModel, name, profileModel.Name);
+ if (!doRename)
+ return;
+
+ profileModel.IsDefault = false;
+ ProfileProvider.AddOrUpdate(profileModel);
+ }
+
+ private async Task 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 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(ProfileModel profileModel)
+ {
+ // 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(profileModel.LuaScript))
+ profileModel.LuaScript = Encoding.UTF8.GetString(Resources.lua_placeholder);
+ File.WriteAllText(Path.GetTempPath() + fileName, profileModel.LuaScript);
+
+ // Watch the file for changes
+ _luaProfile = profileModel;
+ _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 (_luaProfile == null)
+ {
+ DisposeLuaWatcher();
+ return;
+ }
+
+ if (args.ChangeType != WatcherChangeTypes.Changed)
+ return;
+
+ lock (_luaProfile)
+ {
+ using (var fs = new FileStream(args.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ using (var sr = new StreamReader(fs))
+ {
+ _luaProfile.LuaScript = sr.ReadToEnd();
+ }
+ }
+
+ ProfileProvider.AddOrUpdate(_luaProfile);
+ _luaManager.SetupLua(_luaProfile);
+ }
+ }
+
+ private void DisposeLuaWatcher()
+ {
+ if (_watcher == null) return;
+ _watcher.Changed -= LuaFileChanged;
+ _watcher.Dispose();
+ _watcher = null;
+ }
+
+ public void Dispose()
+ {
+ DisposeLuaWatcher();
+ }
+
+ #endregion
+
+ #region Rendering
+
+
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Abstract/ModuleViewModel.cs b/Artemis/Artemis/Modules/Abstract/ModuleViewModel.cs
index 55fdfef5d..fa0eeaca5 100644
--- a/Artemis/Artemis/Modules/Abstract/ModuleViewModel.cs
+++ b/Artemis/Artemis/Modules/Abstract/ModuleViewModel.cs
@@ -2,6 +2,7 @@
using Artemis.Managers;
using Artemis.Services;
using Artemis.Settings;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
using Ninject;
diff --git a/Artemis/Artemis/Profiles/Layers/Abstract/LayerPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Abstract/LayerPropertiesViewModel.cs
index 33ea330e7..3669713a0 100644
--- a/Artemis/Artemis/Profiles/Layers/Abstract/LayerPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Abstract/LayerPropertiesViewModel.cs
@@ -1,5 +1,6 @@
using System.Windows.Media;
using Artemis.Profiles.Layers.Models;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs
index 83f8c9185..0faba2b3c 100644
--- a/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Interfaces/ILayerType.cs
@@ -2,6 +2,7 @@
using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Models;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Newtonsoft.Json;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightPropertiesViewModel.cs
index 7e71757d0..d4fcf84c1 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightPropertiesViewModel.cs
@@ -1,4 +1,5 @@
using Artemis.Profiles.Layers.Abstract;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.AmbientLight
diff --git a/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightType.cs b/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightType.cs
index d1170138b..01c4922ee 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/AmbientLight/AmbientLightType.cs
@@ -13,6 +13,7 @@ using Artemis.Profiles.Layers.Types.AmbientLight.Model.Extensions;
using Artemis.Profiles.Layers.Types.AmbientLight.ScreenCapturing;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Newtonsoft.Json;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioPropertiesViewModel.cs
index ca276c666..079b386b4 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioPropertiesViewModel.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs
index e983d0969..a115f9bcb 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Audio/AudioType.cs
@@ -9,6 +9,7 @@ using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Audio.AudioCapturing;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Audio
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderPropertiesViewModel.cs
index 318662690..ca33f411a 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderPropertiesViewModel.cs
@@ -1,4 +1,5 @@
using Artemis.Profiles.Layers.Abstract;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Folder
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs b/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs
index 52f5668c2..cfec51a00 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Folder/FolderType.cs
@@ -6,6 +6,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Folder
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericPropertiesViewModel.cs
index 80c7aa4ec..3de180e31 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericPropertiesViewModel.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericType.cs b/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericType.cs
index 04a01016e..f7ca251d5 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Generic/GenericType.cs
@@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Generic
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetPropertiesViewModel.cs
index e6db95ab0..5e81c97c5 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetPropertiesViewModel.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs b/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs
index 1df4777bc..3c99e55f4 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Headset/HeadsetType.cs
@@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Headset
diff --git a/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressPropertiesViewModel.cs
index 9efbd9907..352f7c45f 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressPropertiesViewModel.cs
@@ -1,4 +1,5 @@
using Artemis.Profiles.Layers.Abstract;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.KeyPress
diff --git a/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressType.cs b/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressType.cs
index 74f4d7cb0..a2a366ec4 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/KeyPress/KeyPressType.cs
@@ -13,6 +13,7 @@ using Artemis.Profiles.Layers.Models;
using Artemis.Properties;
using Artemis.Utilities;
using Artemis.Utilities.Keyboard;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.KeyPress
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesViewModel.cs
index c84b3c3aa..eeb7943b7 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardPropertiesViewModel.cs
@@ -3,6 +3,7 @@ using System.Windows.Forms;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs
index 6e6627d43..2ace6c684 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Keyboard/KeyboardType.cs
@@ -5,6 +5,7 @@ using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Animations;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Keyboard
diff --git a/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs b/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs
index bd0527a47..ef3e201e2 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/KeyboardGif/KeyboardGifType.cs
@@ -9,6 +9,7 @@ using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Keyboard;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.KeyboardGif
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Mouse/MousePropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Mouse/MousePropertiesViewModel.cs
index 00d683cc8..d88b692f9 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Mouse/MousePropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Mouse/MousePropertiesViewModel.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs b/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs
index df6d46592..31a2d4fa1 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Mouse/MouseType.cs
@@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Mouse
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematPropertiesViewModel.cs b/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematPropertiesViewModel.cs
index 65c42d6a2..b960966e9 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematPropertiesViewModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematPropertiesViewModel.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
diff --git a/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematType.cs b/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematType.cs
index 58583e475..f1379e754 100644
--- a/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematType.cs
+++ b/Artemis/Artemis/Profiles/Layers/Types/Mousemat/MousematType.cs
@@ -8,6 +8,7 @@ using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Properties;
using Artemis.Utilities;
+using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
namespace Artemis.Profiles.Layers.Types.Mousemat
diff --git a/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs b/Artemis/Artemis/ViewModels/LayerEditorViewModel.cs
similarity index 95%
rename from Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs
rename to Artemis/Artemis/ViewModels/LayerEditorViewModel.cs
index 035fd42ad..1c387abe2 100644
--- a/Artemis/Artemis/ViewModels/Profiles/LayerEditorViewModel.cs
+++ b/Artemis/Artemis/ViewModels/LayerEditorViewModel.cs
@@ -10,13 +10,14 @@ using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Keyboard;
using Artemis.Profiles.Layers.Types.KeyboardGif;
using Artemis.Services;
+using Artemis.Utilities;
+using Artemis.ViewModels.Profiles;
using Artemis.ViewModels.Profiles.Events;
using Caliburn.Micro;
using Newtonsoft.Json;
using Ninject;
-using static Artemis.Utilities.GeneralHelpers;
-namespace Artemis.ViewModels.Profiles
+namespace Artemis.ViewModels
{
public sealed class LayerEditorViewModel : Screen
{
@@ -30,13 +31,13 @@ namespace Artemis.ViewModels.Profiles
IEnumerable layerAnimations)
{
Layer = layer;
- ProposedLayer = Clone(layer);
+ ProposedLayer = GeneralHelpers.Clone(layer);
ProposedLayer.Children.Clear();
DataModel = DataModel;
LayerTypes = new BindableCollection(types.OrderBy(t => t.Name));
LayerAnimations = layerAnimations.OrderBy(l => l.Name).ToList();
- DataModelProps = new BindableCollection(GenerateTypeMap(dataModel));
+ DataModelProps = new BindableCollection(GeneralHelpers.GenerateTypeMap(dataModel));
if (Layer.Properties == null)
Layer.SetupProperties();
@@ -57,7 +58,7 @@ namespace Artemis.ViewModels.Profiles
public MetroDialogService DialogService { get; set; }
public BindableCollection LayerTypes { get; set; }
- public BindableCollection DataModelProps { get; set; }
+ public BindableCollection DataModelProps { get; set; }
public BindableCollection LayerConditionVms { get; set; }
public bool KeyboardGridIsVisible => ProposedLayer.LayerType is KeyboardType;
public bool GifGridIsVisible => ProposedLayer.LayerType is KeyboardGifType;
@@ -215,7 +216,7 @@ namespace Artemis.ViewModels.Profiles
// Ignore the children, can't just temporarily add them to the proposed layer because
// that would upset the child layers' relations (sounds like Dr. Phil amirite?)
- var currentObj = Clone(Layer);
+ var currentObj = GeneralHelpers.Clone(Layer);
currentObj.Children.Clear();
// Apply the IsEvent boolean
diff --git a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs
similarity index 52%
rename from Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs
rename to Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs
index 734598963..2b1da49ae 100644
--- a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs
+++ b/Artemis/Artemis/ViewModels/ProfileEditorViewModel.cs
@@ -1,11 +1,12 @@
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
-using System.IO;
using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
@@ -13,8 +14,10 @@ using Artemis.DAL;
using Artemis.DeviceProviders;
using Artemis.Events;
using Artemis.Managers;
+using Artemis.Models;
using Artemis.Modules.Abstract;
using Artemis.Profiles;
+using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.Folder;
using Artemis.Properties;
@@ -22,86 +25,84 @@ using Artemis.Services;
using Artemis.Styles.DropTargetAdorners;
using Artemis.Utilities;
using Caliburn.Micro;
+using Castle.Components.DictionaryAdapter;
using GongSolutions.Wpf.DragDrop;
+using MahApps.Metro;
using MahApps.Metro.Controls.Dialogs;
-using Ninject.Parameters;
using NuGet;
+using Application = System.Windows.Application;
+using Cursor = System.Windows.Input.Cursor;
+using Cursors = System.Windows.Input.Cursors;
using DragDropEffects = System.Windows.DragDropEffects;
using IDropTarget = GongSolutions.Wpf.DragDrop.IDropTarget;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using Screen = Caliburn.Micro.Screen;
-namespace Artemis.ViewModels.Profiles
+namespace Artemis.ViewModels
{
public sealed class ProfileEditorViewModel : Screen, IDropTarget, IDisposable
{
private readonly DeviceManager _deviceManager;
private readonly MetroDialogService _dialogService;
- private readonly LuaManager _luaManager;
+ private readonly LoopManager _loopManager;
private readonly ModuleModel _moduleModel;
- private readonly WindowService _windowService;
private ImageSource _keyboardPreview;
private ObservableCollection _layers;
private ObservableCollection _profileNames;
private bool _saving;
- private FileSystemWatcher _watcher;
- private ProfileViewModel _profileViewModel;
private LayerModel _selectedLayer;
+ private bool _showAll;
- public ProfileEditorViewModel(DeviceManager deviceManager, LuaManager luaManager, ModuleModel moduleModel,
- ProfileViewModel profileViewModel, MetroDialogService dialogService, WindowService windowService)
+ public ProfileEditorViewModel(ProfileEditorModel profileEditorModel, DeviceManager deviceManager,
+ LoopManager loopManager, ModuleModel moduleModel, MetroDialogService dialogService)
{
_deviceManager = deviceManager;
- _luaManager = luaManager;
+ _loopManager = loopManager;
_moduleModel = moduleModel;
_dialogService = dialogService;
- _windowService = windowService;
ProfileNames = new ObservableCollection();
Layers = new ObservableCollection();
- ProfileViewModel = profileViewModel;
-
- ProfileViewModel.ModuleModel = _moduleModel;
+ ProfileEditorModel = profileEditorModel;
PropertyChanged += EditorStateHandler;
_deviceManager.OnKeyboardChanged += DeviceManagerOnOnKeyboardChanged;
_moduleModel.ProfileChanged += ModuleModelOnProfileChanged;
+ _loopManager.RenderCompleted += LoopManagerOnRenderCompleted;
LoadProfiles();
}
- public ProfileViewModel ProfileViewModel
+ public void Dispose()
{
- get { return _profileViewModel; }
- set
+ SaveSelectedProfile();
+ ProfileEditorModel.Dispose();
+ _loopManager.RenderCompleted -= LoopManagerOnRenderCompleted;
+ _deviceManager.OnKeyboardChanged -= DeviceManagerOnOnKeyboardChanged;
+ }
+
+ #region LUA
+
+ public void EditLua()
+ {
+ if (SelectedProfile == null)
+ return;
+ try
{
- if (Equals(value, _profileViewModel)) return;
- _profileViewModel = value;
- NotifyOfPropertyChange();
- NotifyOfPropertyChange(nameof(LayerSelected));
+ ProfileEditorModel.OpenLuaEditor(SelectedProfile);
+ }
+ catch (Exception e)
+ {
+ _dialogService.ShowMessageBox("Couldn't open LUA file",
+ "Please make sure you have a text editor associated with the .lua extension.\n\n" +
+ "Windows error message: \n" + e.Message);
}
}
- public bool EditorEnabled
- =>
- SelectedProfile != null && !SelectedProfile.IsDefault &&
- _deviceManager.ActiveKeyboard != null;
+ #endregion
- public ProfileModel SelectedProfile => _moduleModel?.ProfileModel;
- public LayerModel SelectedLayer
- {
- get { return _selectedLayer; }
- set
- {
- if (_profileViewModel != null)
- _profileViewModel.SelectedLayer = value;
- if (Equals(value, _selectedLayer))
- return;
-
- _selectedLayer = value;
- NotifyOfPropertyChange(() => SelectedLayer);
- NotifyOfPropertyChange(() => LayerSelected);
- }
- }
+ #region Properties
+
+ public ProfileEditorModel ProfileEditorModel { get; }
public ObservableCollection ProfileNames
{
@@ -125,6 +126,29 @@ namespace Artemis.ViewModels.Profiles
}
}
+
+ public ImageSource KeyboardPreview
+ {
+ get { return _keyboardPreview; }
+ set
+ {
+ if (Equals(value, _keyboardPreview)) return;
+ _keyboardPreview = value;
+ NotifyOfPropertyChange(() => KeyboardPreview);
+ }
+ }
+
+ public bool ShowAll
+ {
+ get { return _showAll; }
+ set
+ {
+ if (value == _showAll) return;
+ _showAll = value;
+ NotifyOfPropertyChange();
+ }
+ }
+
public string SelectedProfileName
{
get { return SelectedProfile?.Name; }
@@ -138,22 +162,542 @@ namespace Artemis.ViewModels.Profiles
}
}
- public ImageSource KeyboardPreview
+ public LayerModel SelectedLayer
{
- get { return _keyboardPreview; }
+ get { return _selectedLayer; }
set
{
- if (Equals(value, _keyboardPreview)) return;
- _keyboardPreview = value;
- NotifyOfPropertyChange(() => KeyboardPreview);
+ if (Equals(value, _selectedLayer))
+ return;
+
+ _selectedLayer = value;
+ NotifyOfPropertyChange(() => SelectedLayer);
+ NotifyOfPropertyChange(() => LayerSelected);
}
}
- public PreviewSettings? PreviewSettings => _deviceManager.ActiveKeyboard?.PreviewSettings;
+ public ImageSource KeyboardImage => ImageUtilities.BitmapToBitmapImage(
+ _deviceManager.ActiveKeyboard?.PreviewSettings.Image ?? Resources.none);
+ public ProfileModel SelectedProfile => _moduleModel?.ProfileModel;
+ public PreviewSettings? PreviewSettings => _deviceManager.ActiveKeyboard?.PreviewSettings;
public bool ProfileSelected => SelectedProfile != null;
public bool LayerSelected => SelectedProfile != null && SelectedLayer != null;
+ public bool EditorEnabled => SelectedProfile != null && !SelectedProfile.IsDefault &&
+ _deviceManager.ActiveKeyboard != null;
+
+ #endregion
+
+ #region Layers
+
+ public void EditLayerFromDoubleClick()
+ {
+ if (SelectedLayer?.LayerType is FolderType)
+ return;
+
+ EditLayer();
+ }
+
+ public void EditLayer()
+ {
+ if (SelectedLayer == null)
+ return;
+
+ ProfileEditorModel.EditLayer(SelectedLayer, _moduleModel.DataModel);
+ UpdateLayerList(SelectedLayer);
+ }
+
+ public LayerModel AddLayer()
+ {
+ if (SelectedProfile == null)
+ return null;
+
+ var layer = SelectedProfile.AddLayer(SelectedLayer);
+ UpdateLayerList(layer);
+
+ return layer;
+ }
+
+ public LayerModel AddFolder()
+ {
+ if (SelectedProfile == null)
+ return null;
+
+ var layer = AddLayer();
+ if (layer == null)
+ return null;
+
+ layer.Name = "New folder";
+ layer.LayerType = new FolderType();
+ layer.LayerType.SetupProperties(layer);
+
+ return layer;
+ }
+
+ public void RemoveLayer()
+ {
+ RemoveLayer(SelectedLayer);
+ }
+
+ public void RemoveLayer(LayerModel layer)
+ {
+ ProfileEditorModel.RemoveLayer(layer, SelectedProfile);
+ UpdateLayerList(null);
+ }
+
+ public async void RenameLayer(LayerModel layer)
+ {
+ if (layer == null)
+ return;
+
+ var newName = await _dialogService.ShowInputDialog("Rename layer", "Please enter a name for the layer",
+ new MetroDialogSettings {DefaultText = layer.Name});
+
+ // Null when the user cancelled
+ if (string.IsNullOrEmpty(newName))
+ return;
+
+ layer.Name = newName;
+ UpdateLayerList(layer);
+ }
+
+ ///
+ /// Clones the currently selected layer and adds it to the profile, after the original
+ ///
+ public void CloneLayer()
+ {
+ if (SelectedLayer == null)
+ return;
+
+ CloneLayer(SelectedLayer);
+ }
+
+ ///
+ /// Clones the given layer and adds it to the profile, after the original
+ ///
+ ///
+ public void CloneLayer(LayerModel layer)
+ {
+ var clone = GeneralHelpers.Clone(layer);
+ layer.InsertAfter(clone);
+
+ UpdateLayerList(clone);
+ }
+
+ private void UpdateLayerList(LayerModel selectModel)
+ {
+ // Update the UI
+ Layers.Clear();
+ SelectedLayer = null;
+
+ if (SelectedProfile != null)
+ Layers.AddRange(SelectedProfile.Layers);
+
+ if (selectModel == null)
+ return;
+
+ // A small delay to allow the profile list to rebuild
+ Task.Factory.StartNew(() =>
+ {
+ Thread.Sleep(100);
+ SelectedLayer = selectModel;
+ });
+ }
+
+ #endregion
+
+ #region Profiles
+
+ ///
+ /// Loads all profiles for the current game and keyboard
+ ///
+ private void LoadProfiles()
+ {
+ Execute.OnUIThread(() =>
+ {
+ ProfileNames.Clear();
+ if (_moduleModel != null && _deviceManager.ActiveKeyboard != null)
+ ProfileNames.AddRange(ProfileProvider.GetProfileNames(_deviceManager.ActiveKeyboard, _moduleModel));
+
+ NotifyOfPropertyChange(() => SelectedProfile);
+ });
+ }
+
+ public void SaveSelectedProfile()
+ {
+ if (_saving || SelectedProfile == null || _deviceManager.ChangingKeyboard)
+ return;
+
+ _saving = true;
+ try
+ {
+ ProfileProvider.AddOrUpdate(SelectedProfile);
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ _saving = false;
+ }
+
+ public async void AddProfile()
+ {
+ if (_deviceManager.ActiveKeyboard == null)
+ {
+ _dialogService.ShowMessageBox("Cannot add profile.",
+ "To add a profile, please select a keyboard in the options menu first.");
+ return;
+ }
+
+ var profile = await ProfileEditorModel.AddProfile(_moduleModel);
+ if (profile == null)
+ return;
+
+ LoadProfiles();
+ }
+
+ public async void RenameProfile()
+ {
+ if (SelectedProfile == null)
+ return;
+
+ await ProfileEditorModel.RenameProfile(SelectedProfile);
+ LoadProfiles();
+ }
+
+ public async void DuplicateProfile()
+ {
+ if (SelectedProfile == null)
+ return;
+
+ await ProfileEditorModel.DuplicateProfile(SelectedProfile);
+ LoadProfiles();
+ }
+
+ public async void DeleteProfile()
+ {
+ if (SelectedProfile == null)
+ return;
+
+ await ProfileEditorModel.DeleteProfile(SelectedProfile, _moduleModel);
+ LoadProfiles();
+ }
+
+ public async void ImportProfile()
+ {
+ if (_deviceManager.ActiveKeyboard == null)
+ {
+ _dialogService.ShowMessageBox("Cannot import profile.",
+ "To import a profile, please select a keyboard in the options menu first.");
+ return;
+ }
+
+ await ProfileEditorModel.ImportProfile(_moduleModel);
+ LoadProfiles();
+ }
+
+ public void ExportProfile()
+ {
+ if (SelectedProfile == null)
+ return;
+
+ var dialog = new SaveFileDialog {Filter = "Artemis profile (*.json)|*.json"};
+ var result = dialog.ShowDialog();
+ if (result != DialogResult.OK)
+ return;
+
+ ProfileProvider.ExportProfile(SelectedProfile, dialog.FileName);
+ }
+
+ #endregion
+
+ #region Rendering
+
+ private void LoopManagerOnRenderCompleted(object sender, EventArgs eventArgs)
+ {
+ // Besides the usual checks, also check if the ActiveKeyboard isn't the NoneKeyboard
+ if (SelectedProfile == null || _deviceManager.ActiveKeyboard == null ||
+ _deviceManager.ActiveKeyboard.Slug == "none")
+ {
+ KeyboardPreview = null;
+
+ // Setup layers for the next frame
+ if (_moduleModel.IsInitialized && ActiveWindowHelper.MainWindowActive)
+ _moduleModel.PreviewLayers = new List();
+
+ return;
+ }
+
+ var renderLayers = GetRenderLayers();
+ // Draw the current frame to the preview
+ var keyboardRect = _deviceManager.ActiveKeyboard.KeyboardRectangle();
+ var visual = new DrawingVisual();
+ using (var drawingContext = visual.RenderOpen())
+ {
+ // Setup the DrawingVisual's size
+ drawingContext.PushClip(new RectangleGeometry(keyboardRect));
+ drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect);
+
+ // Draw the layers
+ foreach (var layer in renderLayers)
+ {
+ layer.Update(null, true, false);
+ if (layer.LayerType.ShowInEdtor)
+ layer.Draw(null, drawingContext, true, false);
+ }
+
+ // Get the selection color
+ var accentColor = ThemeManager.DetectAppStyle(Application.Current)?.Item2?.Resources["AccentColor"];
+ if (accentColor == null)
+ {
+ var preview = new DrawingImage();
+ preview.Freeze();
+ KeyboardPreview = preview;
+ return;
+ }
+
+ var pen = new Pen(new SolidColorBrush((Color) accentColor), 0.4);
+
+ // Draw the selection outline and resize indicator
+ if (SelectedLayer != null && SelectedLayer.MustDraw())
+ {
+ var layerRect = SelectedLayer.Properties.PropertiesRect();
+ // Deflate the rect so that the border is drawn on the inside
+ layerRect.Inflate(-0.2, -0.2);
+
+ // Draw an outline around the selected layer
+ drawingContext.DrawRectangle(null, pen, layerRect);
+ // Draw a resize indicator in the bottom-right
+ drawingContext.DrawLine(pen,
+ new Point(layerRect.BottomRight.X - 1, layerRect.BottomRight.Y - 0.5),
+ new Point(layerRect.BottomRight.X - 1.2, layerRect.BottomRight.Y - 0.7));
+ drawingContext.DrawLine(pen,
+ new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 1),
+ new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 1.2));
+ drawingContext.DrawLine(pen,
+ new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 0.5),
+ new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 0.7));
+ }
+
+ SelectedProfile?.RaiseDeviceDrawnEvent(new ProfileDeviceEventsArg(DrawType.Preview, null, true,
+ drawingContext));
+
+ // Remove the clip
+ drawingContext.Pop();
+ }
+ var drawnPreview = new DrawingImage(visual.Drawing);
+ drawnPreview.Freeze();
+ KeyboardPreview = drawnPreview;
+
+ // Setup layers for the next frame
+ if (_moduleModel.IsInitialized && ActiveWindowHelper.MainWindowActive)
+ _moduleModel.PreviewLayers = renderLayers;
+ }
+
+ public List GetRenderLayers()
+ {
+ // Get the layers that must be drawn
+ List drawLayers;
+ if (ShowAll)
+ return SelectedProfile.GetRenderLayers(null, false, true);
+
+ if (SelectedLayer == null || !SelectedLayer.Enabled)
+ return new EditableList();
+
+ if (SelectedLayer.LayerType is FolderType)
+ drawLayers = SelectedLayer.GetRenderLayers(null, false, true);
+ else
+ drawLayers = new List {SelectedLayer};
+
+ return drawLayers;
+ }
+
+ #endregion
+
+ #region Mouse actions
+
+ private DateTime _downTime;
+ private LayerModel _draggingLayer;
+ private Point? _draggingLayerOffset;
+ private Cursor _keyboardPreviewCursor;
+ private bool _resizing;
+
+ ///
+ /// Handler for clicking
+ ///
+ ///
+ public void MouseDownKeyboardPreview(MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ _downTime = DateTime.Now;
+ }
+
+ ///
+ /// Second handler for clicking, selects a the layer the user clicked on.
+ ///
+ ///
+ public void MouseUpKeyboardPreview(MouseButtonEventArgs e)
+ {
+ if (SelectedProfile == null || SelectedProfile.IsDefault)
+ return;
+
+ var timeSinceDown = DateTime.Now - _downTime;
+ if (!(timeSinceDown.TotalMilliseconds < 500))
+ return;
+ if (_draggingLayer != null)
+ return;
+
+ var keyboard = _deviceManager.ActiveKeyboard;
+ var pos = e.GetPosition((Image) e.OriginalSource);
+ var x = pos.X / ((double) keyboard.PreviewSettings.Width / keyboard.Width);
+ var y = pos.Y / ((double) keyboard.PreviewSettings.Height / keyboard.Height);
+
+ var hoverLayer = GetLayers().Where(l => l.MustDraw())
+ .FirstOrDefault(l => l.Properties.PropertiesRect(1).Contains(x, y));
+
+ if (hoverLayer != null)
+ SelectedLayer = hoverLayer;
+ }
+
+ ///
+ /// Handler for resizing and moving the currently selected layer
+ ///
+ ///
+ public void MouseMoveKeyboardPreview(MouseEventArgs e)
+ {
+ if (SelectedProfile == null)
+ return;
+
+ var pos = e.GetPosition((Image) e.OriginalSource);
+ var keyboard = _deviceManager.ActiveKeyboard;
+ var x = pos.X / ((double) keyboard.PreviewSettings.Width / keyboard.Width);
+ var y = pos.Y / ((double) keyboard.PreviewSettings.Height / keyboard.Height);
+ var hoverLayer = GetLayers().Where(l => l.MustDraw())
+ .FirstOrDefault(l => l.Properties.PropertiesRect(1).Contains(x, y));
+
+ HandleDragging(e, x, y, hoverLayer);
+
+ if (hoverLayer == null)
+ {
+ KeyboardPreviewCursor = Cursors.Arrow;
+ return;
+ }
+
+ // Turn the mouse pointer into a hand if hovering over an active layer
+ if (hoverLayer == SelectedLayer)
+ {
+ var rect = hoverLayer.Properties.PropertiesRect(1);
+ KeyboardPreviewCursor =
+ Math.Sqrt(Math.Pow(x - rect.BottomRight.X, 2) + Math.Pow(y - rect.BottomRight.Y, 2)) < 0.6
+ ? Cursors.SizeNWSE
+ : Cursors.SizeAll;
+ }
+ else
+ {
+ KeyboardPreviewCursor = Cursors.Hand;
+ }
+ }
+
+ public Cursor KeyboardPreviewCursor
+ {
+ get { return _keyboardPreviewCursor; }
+ set
+ {
+ if (Equals(value, _keyboardPreviewCursor)) return;
+ _keyboardPreviewCursor = value;
+ NotifyOfPropertyChange(() => KeyboardPreviewCursor);
+ }
+ }
+
+ ///
+ /// Handles dragging the given layer
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void HandleDragging(MouseEventArgs e, double x, double y, LayerModel hoverLayer)
+ {
+ // Reset the dragging state on mouse release
+ if (e.LeftButton == MouseButtonState.Released ||
+ _draggingLayer != null && SelectedLayer != _draggingLayer)
+ {
+ _draggingLayerOffset = null;
+ _draggingLayer = null;
+ return;
+ }
+
+ if (SelectedLayer == null || SelectedLayer.LayerType != null && !SelectedLayer.LayerType.ShowInEdtor)
+ return;
+
+ // Setup the dragging state on mouse press
+ if (_draggingLayerOffset == null && hoverLayer != null && e.LeftButton == MouseButtonState.Pressed)
+ {
+ var layerRect = hoverLayer.Properties.PropertiesRect(1);
+
+ _draggingLayerOffset = new Point(x - SelectedLayer.Properties.X, y - SelectedLayer.Properties.Y);
+ _draggingLayer = hoverLayer;
+ // Detect dragging if cursor is in the bottom right
+ _resizing = Math.Sqrt(Math.Pow(x - layerRect.BottomRight.X, 2) +
+ Math.Pow(y - layerRect.BottomRight.Y, 2)) < 0.6;
+ }
+
+ if (_draggingLayerOffset == null || _draggingLayer == null || _draggingLayer != SelectedLayer)
+ return;
+
+ var draggingProps = _draggingLayer.Properties;
+ // If no setup or reset was done, handle the actual dragging action
+ if (_resizing)
+ {
+ var newWidth = Math.Round(x - draggingProps.X);
+ var newHeight = Math.Round(y - draggingProps.Y);
+
+ // Ensure the layer doesn't leave the canvas
+ if (newWidth < 1 || draggingProps.X + newWidth <= 0)
+ newWidth = draggingProps.Width;
+ if (newHeight < 1 || draggingProps.Y + newHeight <= 0)
+ newHeight = draggingProps.Height;
+
+ draggingProps.Width = newWidth;
+ draggingProps.Height = newHeight;
+ }
+ else
+ {
+ var newX = Math.Round(x - _draggingLayerOffset.Value.X);
+ var newY = Math.Round(y - _draggingLayerOffset.Value.Y);
+
+ // Ensure the layer doesn't leave the canvas
+ if (newX >= SelectedProfile.Width || newX + draggingProps.Width <= 0)
+ newX = draggingProps.X;
+ if (newY >= SelectedProfile.Height || newY + draggingProps.Height <= 0)
+ newY = draggingProps.Y;
+
+ draggingProps.X = newX;
+ draggingProps.Y = newY;
+ }
+ }
+
+ private List GetLayers()
+ {
+ if (ShowAll)
+ return SelectedProfile.GetLayers();
+ if (SelectedLayer == null)
+ return new List();
+
+ lock (SelectedLayer)
+ {
+ // Get the layers that must be drawn
+ if (SelectedLayer.LayerType is FolderType)
+ return SelectedLayer.GetLayers().ToList();
+ return new List {SelectedLayer};
+ }
+ }
+
+ #endregion
+
+ #region Event handles
+
public void DragOver(IDropInfo dropInfo)
{
var source = dropInfo.Data as LayerModel;
@@ -226,7 +770,6 @@ namespace Artemis.ViewModels.Profiles
{
NotifyOfPropertyChange(() => SelectedProfileName);
NotifyOfPropertyChange(() => SelectedProfile);
- NotifyOfPropertyChange(() => ProfileViewModel.SelectedProfile);
}
///
@@ -235,461 +778,10 @@ namespace Artemis.ViewModels.Profiles
private void DeviceManagerOnOnKeyboardChanged(object sender, KeyboardChangedEventArgs e)
{
NotifyOfPropertyChange(() => PreviewSettings);
+ NotifyOfPropertyChange(() => KeyboardImage);
LoadProfiles();
}
- ///
- /// Loads all profiles for the current game and keyboard
- ///
- private void LoadProfiles()
- {
- Execute.OnUIThread(() =>
- {
- ProfileNames.Clear();
- if (_moduleModel != null && _deviceManager.ActiveKeyboard != null)
- ProfileNames.AddRange(ProfileProvider.GetProfileNames(_deviceManager.ActiveKeyboard, _moduleModel));
-
- NotifyOfPropertyChange(() => SelectedProfile);
- });
- }
-
-
- public void EditLayerFromDoubleClick()
- {
- if (SelectedLayer?.LayerType is FolderType)
- return;
-
- EditLayer();
- }
-
- public void EditLayer()
- {
- if (SelectedLayer == null)
- return;
-
- var selectedLayer = SelectedLayer;
- EditLayer(selectedLayer);
- }
-
- ///
- /// Opens a new LayerEditorView for the given layer
- ///
- /// The layer to open the view for
- public void EditLayer(LayerModel layer)
- {
- if (layer == null)
- return;
-
- IParameter[] args =
- {
- new ConstructorArgument("dataModel", _moduleModel.DataModel),
- new ConstructorArgument("layer", layer)
- };
- _windowService.ShowDialog(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())
- 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();
- }
- }
-
- UpdateLayerList(layer);
- }
-
- ///
- /// Adds a new layer to the profile and selects it
- ///
- public LayerModel AddLayer()
- {
- if (SelectedProfile == null)
- return null;
-
- var layer = SelectedProfile.AddLayer(SelectedLayer);
- UpdateLayerList(layer);
-
- return layer;
- }
-
- public LayerModel AddFolder()
- {
- var layer = AddLayer();
- if (layer == null)
- return null;
-
- layer.Name = "New folder";
- layer.LayerType = new FolderType();
- layer.LayerType.SetupProperties(layer);
-
- return layer;
- }
-
- ///
- /// Removes the currently selected layer from the profile
- ///
- public void RemoveLayer()
- {
- RemoveLayer(SelectedLayer);
- }
-
- ///
- /// Removes the given layer from the profile
- ///
- ///
- public void RemoveLayer(LayerModel layer)
- {
- 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 (SelectedProfile.Layers.Contains(layer))
- SelectedProfile.Layers.Remove(layer);
-
- UpdateLayerList(null);
- }
-
- public async void RenameLayer(LayerModel layer)
- {
- if (layer == null)
- return;
-
- var newName =
- await
- _dialogService.ShowInputDialog("Rename layer", "Please enter a name for the layer",
- new MetroDialogSettings {DefaultText = layer.Name});
- // Null when the user cancelled
- if (string.IsNullOrEmpty(newName))
- return;
-
- layer.Name = newName;
- UpdateLayerList(layer);
- }
-
- ///
- /// Clones the currently selected layer and adds it to the profile, after the original
- ///
- public void CloneLayer()
- {
- if (SelectedLayer == null)
- return;
-
- CloneLayer(SelectedLayer);
- }
-
- ///
- /// Clones the given layer and adds it to the profile, after the original
- ///
- ///
- public void CloneLayer(LayerModel layer)
- {
- var clone = GeneralHelpers.Clone(layer);
- layer.InsertAfter(clone);
-
- UpdateLayerList(clone);
- }
-
- private void UpdateLayerList(LayerModel selectModel)
- {
- // Update the UI
- Layers.Clear();
- SelectedLayer = null;
-
- if (SelectedProfile != null)
- Layers.AddRange(SelectedProfile.Layers);
-
- if (selectModel == null)
- return;
-
- // A small delay to allow the profile list to rebuild
- Task.Factory.StartNew(() =>
- {
- Thread.Sleep(100);
- SelectedLayer = selectModel;
- });
- }
-
- ///
- /// Handler for clicking
- ///
- ///
- public void MouseDownKeyboardPreview(MouseButtonEventArgs e)
- {
- ProfileViewModel.MouseDownKeyboardPreview(e);
- }
-
- ///
- /// Second handler for clicking, selects a the layer the user clicked on
- /// if the used clicked on an empty spot, deselects the current layer
- ///
- ///
- public void MouseUpKeyboardPreview(MouseButtonEventArgs e)
- {
- ProfileViewModel.MouseUpKeyboardPreview(e);
- }
-
- ///
- /// Handler for resizing and moving the currently selected layer
- ///
- ///
- public void MouseMoveKeyboardPreview(MouseEventArgs e)
- {
- ProfileViewModel.MouseMoveKeyboardPreview(e);
- }
-
- ///
- /// Adds a new profile to the current game and keyboard
- ///
- public async void AddProfile()
- {
- if (_deviceManager.ActiveKeyboard == null)
- {
- _dialogService.ShowMessageBox("Cannot add profile.",
- "To add a profile, please select a keyboard in the options menu first.");
- return;
- }
-
- var name = await _dialogService.ShowInputDialog("Add new profile",
- "Please provide a profile name unique to this game and keyboard.");
-
- // Null when the user cancelled
- if (name == null)
- return;
-
- if (name.Length < 2)
- {
- _dialogService.ShowMessageBox("Invalid profile name", "Please provide a valid profile name");
- return;
- }
-
- 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;
- }
-
- ProfileProvider.AddOrUpdate(profile);
-
- LoadProfiles();
- }
-
- public async void RenameProfile()
- {
- if (SelectedProfile == null)
- return;
-
- var oldName = SelectedProfile.Name;
- var name = await _dialogService.ShowInputDialog("Rename profile", "Please enter a unique new profile name");
-
- // Null when the user cancelled
- if (string.IsNullOrEmpty(name) || name.Length < 2)
- return;
-
- SelectedProfile.Name = name;
-
- // Verify the name
- while (!ProfileProvider.IsProfileUnique(SelectedProfile))
- {
- name = await _dialogService
- .ShowInputDialog("Name already in use", "Please enter a unique new profile name");
-
- // Null when the user cancelled
- if (string.IsNullOrEmpty(name) || name.Length < 2)
- {
- SelectedProfile.Name = oldName;
- return;
- }
- SelectedProfile.Name = name;
- }
-
- var profile = SelectedProfile;
- ProfileProvider.RenameProfile(profile, name);
-
- LoadProfiles();
- }
-
- public async void DuplicateProfile()
- {
- if (SelectedProfile == null)
- return;
-
- var newProfile = GeneralHelpers.Clone(SelectedProfile);
- newProfile.Name = await _dialogService
- .ShowInputDialog("Duplicate profile", "Please enter a unique profile name");
-
- // Null when the user cancelled
- if (string.IsNullOrEmpty(newProfile.Name))
- return;
-
- // Verify the name
- while (!ProfileProvider.IsProfileUnique(newProfile))
- {
- newProfile.Name = await _dialogService
- .ShowInputDialog("Name already in use", "Please enter a unique profile name");
-
- // Null when the user cancelled
- if (string.IsNullOrEmpty(newProfile.Name))
- return;
- }
-
- newProfile.IsDefault = false;
- ProfileProvider.AddOrUpdate(newProfile);
- LoadProfiles();
- }
-
- public async void DeleteProfile()
- {
- if (SelectedProfile == null)
- return;
-
- 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.");
- if (!confirm.Value)
- return;
-
- var defaultProfile = ProfileProvider.GetProfile(_deviceManager.ActiveKeyboard, _moduleModel, "Default");
- var deleteProfile = SelectedProfile;
-
- _moduleModel.ChangeProfile(defaultProfile);
- ProfileProvider.DeleteProfile(deleteProfile);
-
- LoadProfiles();
- }
-
- public async void ImportProfile()
- {
- if (_deviceManager.ActiveKeyboard == null)
- {
- _dialogService.ShowMessageBox("Cannot import profile.",
- "To import a profile, please select a keyboard in the options menu first.");
- return;
- }
- var dialog = new OpenFileDialog {Filter = "Artemis profile (*.json)|*.json"};
- var result = dialog.ShowDialog();
- if (result != DialogResult.OK)
- return;
-
- var profile = ProfileProvider.LoadProfileIfValid(dialog.FileName);
- if (profile == 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;
- }
-
- // Verify the game
- if (profile.GameName != _moduleModel.Name)
- {
- _dialogService.ShowErrorMessageBox(
- $"Oh oops! This profile is ment for {profile.GameName}, not {_moduleModel.Name} :c");
- return;
- }
-
- // Verify the keyboard
- var deviceManager = _deviceManager;
- if (profile.KeyboardSlug != deviceManager.ActiveKeyboard.Slug)
- {
- var adjustKeyboard = await _dialogService.ShowQuestionMessageBox(
- "Profile not inteded for this keyboard",
- $"Watch out, this profile wasn't ment for this keyboard, but for the {profile.KeyboardSlug}. " +
- "You can still import it but you'll probably have to do some adjusting\n\n" +
- "Continue?");
- if (!adjustKeyboard.Value)
- return;
-
- // Resize layers that are on the full keyboard width
- profile.ResizeLayers(deviceManager.ActiveKeyboard);
- // Put layers back into the canvas if they fell outside it
- profile.FixBoundaries(deviceManager.ActiveKeyboard.KeyboardRectangle(1));
-
- // Setup profile metadata to match the new keyboard
- profile.KeyboardSlug = deviceManager.ActiveKeyboard.Slug;
- profile.Width = deviceManager.ActiveKeyboard.Width;
- profile.Height = deviceManager.ActiveKeyboard.Height;
- }
-
- profile.IsDefault = false;
-
- // Verify the name
- while (!ProfileProvider.IsProfileUnique(profile))
- {
- profile.Name = await _dialogService.ShowInputDialog("Rename imported profile",
- "A profile with this name already exists for this game. Please enter a new name");
-
- // Null when the user cancelled
- if (string.IsNullOrEmpty(profile.Name))
- return;
- }
-
- ProfileProvider.AddOrUpdate(profile);
- LoadProfiles();
- }
-
- public void ExportProfile()
- {
- if (SelectedProfile == null)
- return;
-
- var dialog = new SaveFileDialog {Filter = "Artemis profile (*.json)|*.json"};
- var result = dialog.ShowDialog();
- if (result != DialogResult.OK)
- return;
-
- ProfileProvider.ExportProfile(SelectedProfile, dialog.FileName);
- }
-
- public void EditLua()
- {
- if (SelectedProfile == null)
- return;
- try
- {
- OpenEditor();
- }
- catch (Exception e)
- {
- _dialogService.ShowMessageBox("Couldn't open LUA file",
- "Please make sure you have a text editor associated with the .lua extension.\n\n" +
- "Windows error message: \n" + e.Message);
- }
- }
-
private void EditorStateHandler(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "SelectedProfile")
@@ -706,90 +798,6 @@ namespace Artemis.ViewModels.Profiles
NotifyOfPropertyChange(() => ProfileSelected);
}
- public void SaveSelectedProfile()
- {
- if (_saving || SelectedProfile == null || _deviceManager.ChangingKeyboard)
- return;
-
- _saving = true;
- try
- {
- ProfileProvider.AddOrUpdate(SelectedProfile);
- }
- catch (Exception)
- {
- // ignored
- }
- _saving = false;
- }
-
- #region LUA Editor
-
- public void OpenEditor()
- {
- if (SelectedProfile == null)
- return;
-
- // 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(SelectedProfile.LuaScript))
- SelectedProfile.LuaScript = Encoding.UTF8.GetString(Resources.lua_placeholder);
- File.WriteAllText(Path.GetTempPath() + fileName, SelectedProfile.LuaScript);
-
- // Watch the file for changes
- SetupWatcher(Path.GetTempPath(), fileName);
-
- // Open the temp file with the default editor
- System.Diagnostics.Process.Start(Path.GetTempPath() + fileName);
- }
-
- private void SetupWatcher(string path, string fileName)
- {
- if (_watcher == null)
- {
- _watcher = new FileSystemWatcher(Path.GetTempPath(), fileName);
- _watcher.Changed += LuaFileChanged;
- _watcher.EnableRaisingEvents = true;
- }
-
- _watcher.Path = path;
- _watcher.Filter = fileName;
- }
-
- private void LuaFileChanged(object sender, FileSystemEventArgs args)
- {
- if (args.ChangeType != WatcherChangeTypes.Changed)
- return;
-
- if (SelectedProfile == null)
- return;
-
- lock (SelectedProfile)
- {
- using (var fs = new FileStream(args.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- using (var sr = new StreamReader(fs))
- {
- SelectedProfile.LuaScript = sr.ReadToEnd();
- }
- }
-
- ProfileProvider.AddOrUpdate(SelectedProfile);
- _luaManager.SetupLua(SelectedProfile);
- }
- }
-
- public void Dispose()
- {
- SaveSelectedProfile();
- ProfileViewModel.Dispose();
- _watcher?.Dispose();
- }
-
#endregion
}
}
\ No newline at end of file
diff --git a/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs
deleted file mode 100644
index 9487b026a..000000000
--- a/Artemis/Artemis/ViewModels/Profiles/ProfileViewModel.cs
+++ /dev/null
@@ -1,382 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Media;
-using Artemis.Events;
-using Artemis.Managers;
-using Artemis.Modules.Abstract;
-using Artemis.Profiles;
-using Artemis.Profiles.Layers.Interfaces;
-using Artemis.Profiles.Layers.Models;
-using Artemis.Profiles.Layers.Types.Folder;
-using Artemis.Properties;
-using Artemis.Utilities;
-using Caliburn.Micro;
-using Castle.Components.DictionaryAdapter;
-using MahApps.Metro;
-
-namespace Artemis.ViewModels.Profiles
-{
- public class ProfileViewModel : PropertyChangedBase, IDisposable
- {
- private readonly DeviceManager _deviceManager;
- private readonly LoopManager _loopManager;
- private double _blurProgress;
- private double _blurRadius;
- private DateTime _downTime;
- private LayerModel _draggingLayer;
- private Point? _draggingLayerOffset;
- private DrawingImage _keyboardPreview;
- private Cursor _keyboardPreviewCursor;
- private bool _resizing;
- private bool _showAll;
-
- public ProfileViewModel(DeviceManager deviceManager, LoopManager loopManager)
- {
- _deviceManager = deviceManager;
- _loopManager = loopManager;
-
- ShowAll = false;
-
- _loopManager.RenderCompleted += LoopManagerOnRenderCompleted;
- _deviceManager.OnKeyboardChanged += DeviceManagerOnOnKeyboardChanged;
- }
-
- public ModuleModel ModuleModel { get; set; }
- public LayerModel SelectedLayer { get; set; }
-
- public ProfileModel SelectedProfile => ModuleModel?.ProfileModel;
-
- public DrawingImage KeyboardPreview
- {
- get { return _keyboardPreview; }
- set
- {
- if (Equals(value, _keyboardPreview)) return;
- _keyboardPreview = value;
- NotifyOfPropertyChange(() => KeyboardPreview);
- }
- }
-
- public double BlurRadius
- {
- get { return _blurRadius; }
- set
- {
- if (value.Equals(_blurRadius)) return;
- _blurRadius = value;
- NotifyOfPropertyChange(() => BlurRadius);
- }
- }
-
- public bool ShowAll
- {
- get { return _showAll; }
- set
- {
- if (value == _showAll) return;
- _showAll = value;
- NotifyOfPropertyChange(() => ShowAll);
- }
- }
-
- public ImageSource KeyboardImage => ImageUtilities
- .BitmapToBitmapImage(_deviceManager.ActiveKeyboard?.PreviewSettings.Image ?? Resources.none);
-
- private void LoopManagerOnRenderCompleted(object sender, EventArgs eventArgs)
- {
- // Update the glowing effect around the keyboard
- if (_blurProgress > 2)
- _blurProgress = 0;
- _blurProgress = _blurProgress + 0.025;
- BlurRadius = (Math.Sin(_blurProgress * Math.PI) + 1) * 10 + 10;
-
- // Besides the usual checks, also check if the ActiveKeyboard isn't the NoneKeyboard
- if (SelectedProfile == null || _deviceManager.ActiveKeyboard == null || _deviceManager.ActiveKeyboard.Slug == "none")
- {
- KeyboardPreview = null;
-
- // Setup layers for the next frame
- if (ModuleModel.IsInitialized && ActiveWindowHelper.MainWindowActive)
- ModuleModel.PreviewLayers = new List();
-
- return;
- }
-
- var renderLayers = GetRenderLayers();
- // Draw the current frame to the preview
- var keyboardRect = _deviceManager.ActiveKeyboard.KeyboardRectangle();
- var visual = new DrawingVisual();
- using (var drawingContext = visual.RenderOpen())
- {
- // Setup the DrawingVisual's size
- drawingContext.PushClip(new RectangleGeometry(keyboardRect));
- drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, keyboardRect);
-
- // Draw the layers
- foreach (var layer in renderLayers)
- {
- layer.Update(null, true, false);
- if (layer.LayerType.ShowInEdtor)
- layer.Draw(null, drawingContext, true, false);
- }
-
- // Get the selection color
- var accentColor = ThemeManager.DetectAppStyle(Application.Current)?.Item2?.Resources["AccentColor"];
- if (accentColor == null)
- {
- var preview = new DrawingImage();
- preview.Freeze();
- KeyboardPreview = preview;
- return;
- }
-
- var pen = new Pen(new SolidColorBrush((Color) accentColor), 0.4);
-
- // Draw the selection outline and resize indicator
- if (SelectedLayer != null && SelectedLayer.MustDraw())
- {
- var layerRect = SelectedLayer.Properties.PropertiesRect();
- // Deflate the rect so that the border is drawn on the inside
- layerRect.Inflate(-0.2, -0.2);
-
- // Draw an outline around the selected layer
- drawingContext.DrawRectangle(null, pen, layerRect);
- // Draw a resize indicator in the bottom-right
- drawingContext.DrawLine(pen,
- new Point(layerRect.BottomRight.X - 1, layerRect.BottomRight.Y - 0.5),
- new Point(layerRect.BottomRight.X - 1.2, layerRect.BottomRight.Y - 0.7));
- drawingContext.DrawLine(pen,
- new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 1),
- new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 1.2));
- drawingContext.DrawLine(pen,
- new Point(layerRect.BottomRight.X - 0.5, layerRect.BottomRight.Y - 0.5),
- new Point(layerRect.BottomRight.X - 0.7, layerRect.BottomRight.Y - 0.7));
- }
-
- SelectedProfile?.RaiseDeviceDrawnEvent(new ProfileDeviceEventsArg(DrawType.Preview, null, true, drawingContext));
-
- // Remove the clip
- drawingContext.Pop();
- }
- var drawnPreview = new DrawingImage(visual.Drawing);
- drawnPreview.Freeze();
- KeyboardPreview = drawnPreview;
-
- // Setup layers for the next frame
- if (ModuleModel.IsInitialized && ActiveWindowHelper.MainWindowActive)
- ModuleModel.PreviewLayers = renderLayers;
- }
-
- private void DeviceManagerOnOnKeyboardChanged(object sender, KeyboardChangedEventArgs e)
- {
- NotifyOfPropertyChange(() => KeyboardImage);
- }
-
- #region Processing
-
- ///
- /// Handler for clicking
- ///
- ///
- public void MouseDownKeyboardPreview(MouseButtonEventArgs e)
- {
- if (e.LeftButton == MouseButtonState.Pressed)
- _downTime = DateTime.Now;
- }
-
- ///
- /// Second handler for clicking, selects a the layer the user clicked on.
- ///
- ///
- public void MouseUpKeyboardPreview(MouseButtonEventArgs e)
- {
- if (SelectedProfile == null || SelectedProfile.IsDefault)
- return;
-
- var timeSinceDown = DateTime.Now - _downTime;
- if (!(timeSinceDown.TotalMilliseconds < 500))
- return;
- if (_draggingLayer != null)
- return;
-
- var keyboard = _deviceManager.ActiveKeyboard;
- var pos = e.GetPosition((Image) e.OriginalSource);
- var x = pos.X / ((double) keyboard.PreviewSettings.Width / keyboard.Width);
- var y = pos.Y / ((double) keyboard.PreviewSettings.Height / keyboard.Height);
-
- var hoverLayer = GetLayers().Where(l => l.MustDraw())
- .FirstOrDefault(l => l.Properties.PropertiesRect(1).Contains(x, y));
-
- if (hoverLayer != null)
- SelectedLayer = hoverLayer;
- }
-
- ///
- /// Handler for resizing and moving the currently selected layer
- ///
- ///
- public void MouseMoveKeyboardPreview(MouseEventArgs e)
- {
- if (SelectedProfile == null)
- return;
-
- var pos = e.GetPosition((Image) e.OriginalSource);
- var keyboard = _deviceManager.ActiveKeyboard;
- var x = pos.X / ((double) keyboard.PreviewSettings.Width / keyboard.Width);
- var y = pos.Y / ((double) keyboard.PreviewSettings.Height / keyboard.Height);
- var hoverLayer = GetLayers().Where(l => l.MustDraw())
- .FirstOrDefault(l => l.Properties.PropertiesRect(1).Contains(x, y));
-
- HandleDragging(e, x, y, hoverLayer);
-
- if (hoverLayer == null)
- {
- KeyboardPreviewCursor = Cursors.Arrow;
- return;
- }
-
- // Turn the mouse pointer into a hand if hovering over an active layer
- if (hoverLayer == SelectedLayer)
- {
- var rect = hoverLayer.Properties.PropertiesRect(1);
- KeyboardPreviewCursor =
- Math.Sqrt(Math.Pow(x - rect.BottomRight.X, 2) + Math.Pow(y - rect.BottomRight.Y, 2)) < 0.6
- ? Cursors.SizeNWSE
- : Cursors.SizeAll;
- }
- else
- {
- KeyboardPreviewCursor = Cursors.Hand;
- }
- }
-
- public Cursor KeyboardPreviewCursor
- {
- get { return _keyboardPreviewCursor; }
- set
- {
- if (Equals(value, _keyboardPreviewCursor)) return;
- _keyboardPreviewCursor = value;
- NotifyOfPropertyChange(() => KeyboardPreviewCursor);
- }
- }
-
- ///
- /// Handles dragging the given layer
- ///
- ///
- ///
- ///
- ///
- private void HandleDragging(MouseEventArgs e, double x, double y, LayerModel hoverLayer)
- {
- // Reset the dragging state on mouse release
- if (e.LeftButton == MouseButtonState.Released ||
- _draggingLayer != null && SelectedLayer != _draggingLayer)
- {
- _draggingLayerOffset = null;
- _draggingLayer = null;
- return;
- }
-
- if (SelectedLayer == null || SelectedLayer.LayerType != null && !SelectedLayer.LayerType.ShowInEdtor)
- return;
-
- // Setup the dragging state on mouse press
- if (_draggingLayerOffset == null && hoverLayer != null && e.LeftButton == MouseButtonState.Pressed)
- {
- var layerRect = hoverLayer.Properties.PropertiesRect(1);
-
- _draggingLayerOffset = new Point(x - SelectedLayer.Properties.X, y - SelectedLayer.Properties.Y);
- _draggingLayer = hoverLayer;
- // Detect dragging if cursor is in the bottom right
- _resizing = Math.Sqrt(Math.Pow(x - layerRect.BottomRight.X, 2) +
- Math.Pow(y - layerRect.BottomRight.Y, 2)) < 0.6;
- }
-
- if (_draggingLayerOffset == null || _draggingLayer == null || _draggingLayer != SelectedLayer)
- return;
-
- var draggingProps = _draggingLayer.Properties;
- // If no setup or reset was done, handle the actual dragging action
- if (_resizing)
- {
- var newWidth = Math.Round(x - draggingProps.X);
- var newHeight = Math.Round(y - draggingProps.Y);
-
- // Ensure the layer doesn't leave the canvas
- if (newWidth < 1 || draggingProps.X + newWidth <= 0)
- newWidth = draggingProps.Width;
- if (newHeight < 1 || draggingProps.Y + newHeight <= 0)
- newHeight = draggingProps.Height;
-
- draggingProps.Width = newWidth;
- draggingProps.Height = newHeight;
- }
- else
- {
- var newX = Math.Round(x - _draggingLayerOffset.Value.X);
- var newY = Math.Round(y - _draggingLayerOffset.Value.Y);
-
- // Ensure the layer doesn't leave the canvas
- if (newX >= SelectedProfile.Width || newX + draggingProps.Width <= 0)
- newX = draggingProps.X;
- if (newY >= SelectedProfile.Height || newY + draggingProps.Height <= 0)
- newY = draggingProps.Y;
-
- draggingProps.X = newX;
- draggingProps.Y = newY;
- }
- }
-
- public List GetRenderLayers()
- {
- // Get the layers that must be drawn
- List drawLayers;
- if (ShowAll)
- return SelectedProfile.GetRenderLayers(null, false, true);
-
- if (SelectedLayer == null || !SelectedLayer.Enabled)
- return new EditableList();
-
- if (SelectedLayer.LayerType is FolderType)
- drawLayers = SelectedLayer.GetRenderLayers(null, false, true);
- else
- drawLayers = new List {SelectedLayer};
-
- return drawLayers;
- }
-
-
- private List GetLayers()
- {
- if (ShowAll)
- return SelectedProfile.GetLayers();
- if (SelectedLayer == null)
- return new List();
-
- lock (SelectedLayer)
- {
- // Get the layers that must be drawn
- if (SelectedLayer.LayerType is FolderType)
- return SelectedLayer.GetLayers().ToList();
- return new List {SelectedLayer};
- }
- }
-
- public void Dispose()
- {
- Disposed = true;
- _loopManager.RenderCompleted -= LoopManagerOnRenderCompleted;
- _deviceManager.OnKeyboardChanged -= DeviceManagerOnOnKeyboardChanged;
- }
-
- public bool Disposed { get; set; }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/Artemis/Artemis/Views/Profiles/LayerEditorView.xaml b/Artemis/Artemis/Views/LayerEditorView.xaml
similarity index 99%
rename from Artemis/Artemis/Views/Profiles/LayerEditorView.xaml
rename to Artemis/Artemis/Views/LayerEditorView.xaml
index dabe59270..8c7adca13 100644
--- a/Artemis/Artemis/Views/Profiles/LayerEditorView.xaml
+++ b/Artemis/Artemis/Views/LayerEditorView.xaml
@@ -1,4 +1,4 @@
-
/// Interaction logic for LayerEditorView.xaml
diff --git a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml b/Artemis/Artemis/Views/ProfileEditorView.xaml
similarity index 93%
rename from Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml
rename to Artemis/Artemis/Views/ProfileEditorView.xaml
index 4a3c9e8b7..7c138d830 100644
--- a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml
+++ b/Artemis/Artemis/Views/ProfileEditorView.xaml
@@ -1,288 +1,297 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml.cs b/Artemis/Artemis/Views/ProfileEditorView.xaml.cs
similarity index 89%
rename from Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml.cs
rename to Artemis/Artemis/Views/ProfileEditorView.xaml.cs
index b2abd5ee2..35d68c535 100644
--- a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml.cs
+++ b/Artemis/Artemis/Views/ProfileEditorView.xaml.cs
@@ -1,6 +1,6 @@
using System.Windows.Controls;
-namespace Artemis.Views.Profiles
+namespace Artemis.Views
{
///
/// Interaction logic for ProfileEditorView.xaml