using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reactive; using System.Threading.Tasks; using System.Windows.Input; using ClassicAssist.Data; using ClassicAssist.Data.Hotkeys; using ClassicAssist.Data.Macros; using ClassicAssist.Data.Macros.Commands; using ClassicAssist.Misc; using ClassicAssist.Shared; using ClassicAssist.Shared.Resources; using ClassicAssist.Shared.UO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using ReactiveUI; namespace ClassicAssist.UI.ViewModels { public class MacrosTabViewModel : HotkeyEntryViewModel, ISettingProvider { private readonly MacroManager _manager; private int _caretPosition; private ReactiveCommand _clearHotkeyCommand; //TODO //private TextDocument _document; private ReactiveCommand _executeCommand; private ICommand _inspectObjectCommand; private bool _isRecording; private RelayCommand _newMacroCommand; private ICommand _recordCommand; private RelayCommand _removeMacroCommand; private ReactiveCommand _removeMacroConfirmCommand; private ICommand _saveMacroCommand; private MacroEntry _selectedItem; private ICommand _showActiveObjectsWindowCommand; private ICommand _showCommandsCommand; private ICommand _showMacrosWikiCommand; private ReactiveCommand _stopCommand; public MacrosTabViewModel() : base( Strings.Macros ) { Engine.DisconnectedEvent += OnDisconnectedEvent; _manager = MacroManager.GetInstance(); _manager.IsRecording = () => _isRecording; _manager.InsertDocument = str => { _dispatcher.Invoke( () => { SelectedItem.Macro += str; } ); }; _manager.NewMacro = NewMacro; _manager.Items = Items; } public int CaretPosition { get => _caretPosition; set => SetProperty( ref _caretPosition, value ); } public ReactiveCommand ClearHotkeyCommand => _clearHotkeyCommand ?? ( _clearHotkeyCommand = ReactiveCommand.Create( ClearHotkey, this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) ); //TODO //public TextDocument Document //{ // get => _document; // set => SetProperty( ref _document, value ); //} public ReactiveCommand ExecuteCommand => _executeCommand ?? ( _executeCommand = ReactiveCommand.CreateFromTask( Execute, this.WhenAnyValue( x => x.IsRecording, x => x.SelectedItem, x => x.SelectedItem.IsRunning, ( b, entry, running ) => !b && entry != null && !running ) ) ); public ShortcutKeys Hotkey { get => SelectedItem?.Hotkey; set => CheckOverwriteHotkey( SelectedItem, value ); } //TODO public ICommand InspectObjectCommand => _inspectObjectCommand ?? ( _inspectObjectCommand = new RelayCommandAsync( InspectObject, o => true ) ); public bool IsRecording { get => _isRecording; set => SetProperty( ref _isRecording, value ); } public RelayCommand NewMacroCommand => _newMacroCommand ?? ( _newMacroCommand = new RelayCommand( NewMacro, o => !SelectedItem?.IsRunning ?? true ) ); public ICommand RecordCommand => _recordCommand ?? ( _recordCommand = new RelayCommand( Record, o => SelectedItem != null ) ); public string RecordLabel => IsRecording ? Strings.Stop : Strings.Record; public RelayCommand RemoveMacroCommand => _removeMacroCommand ?? ( _removeMacroCommand = new RelayCommand( RemoveMacro, o => !SelectedItem?.IsRunning ?? SelectedItem != null ) ); public ReactiveCommand RemoveMacroConfirmCommand => _removeMacroConfirmCommand ?? ( _removeMacroConfirmCommand = ReactiveCommand.Create( RemoveMacroConfirm, this.WhenAnyValue( o => o.SelectedItem, selector: o => o != null ) ) ); public ICommand SaveMacroCommand => _saveMacroCommand ?? ( _saveMacroCommand = new RelayCommand( SaveMacro, o => true ) ); public MacroEntry SelectedItem { get => _selectedItem; set { SetProperty( ref _selectedItem, value ); NotifyPropertyChanged( nameof( Hotkey ) ); } } public ICommand ShowActiveObjectsWindowCommand => _showActiveObjectsWindowCommand ?? ( _showActiveObjectsWindowCommand = new RelayCommand( ShowActiveObjectsWindow, o => true ) ); public ICommand ShowCommandsCommand => _showCommandsCommand ?? ( _showCommandsCommand = new RelayCommand( ShowCommands, o => true ) ); public ICommand ShowMacrosWikiCommand => _showMacrosWikiCommand ?? ( _showMacrosWikiCommand = new RelayCommand( ShowMacrosWiki, o => true ) ); //public ICommand StopCommand => // _stopCommand ?? ( _stopCommand = new RelayCommandAsync( Stop, o => SelectedItem?.IsRunning ?? false ) ); public ReactiveCommand StopCommand => _stopCommand ?? ( _stopCommand = ReactiveCommand.CreateFromTask( Stop, this.WhenAnyValue( e => e.SelectedItem.IsRunning ) ) ); public void Serialize( JObject json ) { JObject macros = new JObject(); JArray macroArray = new JArray(); IEnumerable globalMacros = Items.Where( e => e.Global ); if ( globalMacros.Any() ) { string globalJson = JsonConvert.SerializeObject( globalMacros, Formatting.Indented ); File.WriteAllText( Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Macros.json" ), globalJson ); } foreach ( MacroEntry macroEntry in Items.Where( e => !e.Global ) ) { JObject entry = new JObject { { "Name", macroEntry.Name }, { "Loop", macroEntry.Loop }, { "DoNotAutoInterrupt", macroEntry.DoNotAutoInterrupt }, { "Macro", macroEntry.Macro }, { "PassToUO", macroEntry.PassToUO }, { "Keys", macroEntry.Hotkey.ToJObject() }, { "IsBackground", macroEntry.IsBackground }, { "IsAutostart", macroEntry.IsAutostart }, { "Disableable", macroEntry.Disableable } }; JArray aliasesArray = new JArray(); foreach ( JObject aliasObj in macroEntry.Aliases.Select( kvp => new JObject { { "Key", kvp.Key }, { "Value", kvp.Value } } ) ) { aliasesArray.Add( aliasObj ); } entry.Add( "Aliases", aliasesArray ); macroArray.Add( entry ); } macros.Add( "Macros", macroArray ); JArray aliasArray = new JArray(); foreach ( JObject entry in AliasCommands.GetAllAliases() .Select( kvp => new JObject { { "Name", kvp.Key }, { "Value", kvp.Value } } ) ) { aliasArray.Add( entry ); } macros.Add( "Alias", aliasArray ); json?.Add( "Macros", macros ); } public void Deserialize( JObject json, Options options ) { Items.Clear(); string globalPath = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Macros.json" ); if ( File.Exists( globalPath ) ) { string globalJson = File.ReadAllText( globalPath ); MacroEntry[] globalMacros = JsonConvert.DeserializeObject( globalJson ); if ( globalMacros != null ) { foreach ( MacroEntry entry in globalMacros ) { entry.Action = async hks => await Execute( entry ); if ( Options.CurrentOptions.SortMacrosAlphabetical ) { Items.AddSorted( entry ); } else { Items.Add( entry ); } } } } JToken config = json?["Macros"]; if ( config == null ) { return; } if ( config["Macros"] != null ) { foreach ( JToken token in config["Macros"] ) { MacroEntry entry = new MacroEntry { Name = GetJsonValue( token, "Name", string.Empty ), Loop = GetJsonValue( token, "Loop", false ), DoNotAutoInterrupt = GetJsonValue( token, "DoNotAutoInterrupt", false ), Macro = GetJsonValue( token, "Macro", string.Empty ), PassToUO = GetJsonValue( token, "PassToUO", true ), Hotkey = new ShortcutKeys( token["Keys"] ), IsBackground = GetJsonValue( token, "IsBackground", false ), IsAutostart = GetJsonValue( token, "IsAutostart", false ), Disableable = GetJsonValue( token, "Disableable", true ) }; if ( token["Aliases"] != null ) { foreach ( JToken aliasToken in token["Aliases"] ) { entry.Aliases.Add( aliasToken["Key"].ToObject(), aliasToken["Value"].ToObject() ); } } // Global macro takes precedence for hotkey if ( Items.Any( e => Equals( e.Hotkey, entry.Hotkey ) && e.Global ) ) { entry.Hotkey = ShortcutKeys.Default; } entry.Action = async hks => await Engine.Dispatcher.InvokeAsync( async () => await Execute( entry ) ); if ( Options.CurrentOptions.SortMacrosAlphabetical ) { Items.AddSorted( entry ); } else { Items.Add( entry ); } } } if ( config["Alias"] != null ) { foreach ( JToken token in config["Alias"] ) { AliasCommands.SetAlias( token["Name"].ToObject(), token["Value"].ToObject() ); } } string modulePath = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Modules" ); if ( !Directory.Exists( modulePath ) ) { Directory.CreateDirectory( modulePath ); } string assistantModule = Path.Combine( modulePath, "Assistant.py" ); if ( !File.Exists( assistantModule ) ) { File.WriteAllText( assistantModule, "from ClassicAssist.Shared import Engine as _Engine\nEngine = _Engine" ); } } private Unit RemoveMacroConfirm( object obj ) { if ( !( obj is MacroEntry entry ) ) { return Unit.Default; } //TODO //MessageBoxResult result = MessageBox.Show( string.Format( Strings.Really_remove_macro___0___, entry.Name ), // Strings.Warning, MessageBoxButton.YesNo, MessageBoxImage.Warning ); //if ( result == MessageBoxResult.No ) //{ // return; //} RemoveMacro( entry ); return Unit.Default; } private static void ShowMacrosWiki( object obj ) { Process.Start( Strings.MACRO_WIKI_URL ); } private bool CanExecute( object arg ) { if ( !( arg is MacroEntry entry ) ) { return false; } if ( entry.IsRunning ) { return false; } return true; } private void CheckOverwriteHotkey( HotkeyEntry selectedItem, ShortcutKeys hotkey ) { HotkeyEntry conflict = null; foreach ( HotkeyEntry hotkeyEntry in HotkeyManager.GetInstance().Items ) { foreach ( HotkeyEntry entry in hotkeyEntry.Children ) { if ( entry.Hotkey.Equals( hotkey ) ) { conflict = entry; } } } if ( conflict != null && !ReferenceEquals( selectedItem, conflict ) ) { //TODO //MessageBoxResult result = // MessageBox.Show( string.Format( Strings.Overwrite_existing_hotkey___0____, conflict ), // Strings.Warning, MessageBoxButton.YesNo ); //if ( result == MessageBoxResult.No ) //{ // NotifyPropertyChanged( nameof( Hotkey ) ); // return; //} } SelectedItem.Hotkey = hotkey; NotifyPropertyChanged( nameof( Hotkey ) ); } private static void SaveMacro( object obj ) { //Saves whole profile, think of better way Options.Save( Options.CurrentOptions ); } private async Task Execute( object obj ) { if ( !( obj is MacroEntry entry ) ) { return; } _manager.Execute( entry ); await Task.CompletedTask; } private static void ShowActiveObjectsWindow( object obj ) { //TODO UI //ActiveObjectsWindow window = new ActiveObjectsWindow(); //window.Show(); } private void OnDisconnectedEvent() { _manager.StopAll(); } private void ClearHotkey( object obj ) { if ( !( obj is MacroEntry entry ) ) { return; } entry.Hotkey = ShortcutKeys.Default; this.RaisePropertyChanged( nameof( Hotkey ) ); } private static async Task InspectObject( object arg ) { await Commands.InspectObjectAsync(); } private void NewMacro( object obj ) { int count = Items.Count; MacroEntry macro = new MacroEntry { Name = $"Macro-{count + 1}", Macro = string.Empty }; macro.Action = async hks => await Execute( macro ); Items.Add( macro ); } private void NewMacro( string name, string macroText ) { MacroEntry macro = new MacroEntry { Name = name, Macro = macroText }; macro.Action = async hks => await Execute( macro ); Items.Add( macro ); SelectedItem = macro; } private void RemoveMacro( object obj ) { if ( obj is MacroEntry entry ) { Items.Remove( entry ); } } private async Task Stop( object obj ) { if ( !( obj is MacroEntry entry ) ) { return; } entry.Stop(); await Task.CompletedTask; } private void ShowCommands( object obj ) { //TODO UI //MacrosCommandWindow window = new MacrosCommandWindow { DataContext = new MacrosCommandViewModel( this ) }; //window.ShowDialog(); } private void Record( object obj ) { if ( IsRecording ) { IsRecording = false; NotifyPropertyChanged( nameof( RecordLabel ) ); return; } IsRecording = true; NotifyPropertyChanged( nameof( RecordLabel ) ); } } }