using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.IO; using System.Xml.Linq; using System.Windows.Forms; using SCJMapper_V2.SC; using SCJMapper_V2.Devices.Mouse; using SCJMapper_V2.Devices.Keyboard; using System.Collections; namespace SCJMapper_V2.Devices.Options { /// /// Maintains Options - something like: /// /// /// /// /// /// /// /// /// /// /// [type] : set to shared, mouse, xboxpad, or joystick (NOTE the CIG type used is keyboard .. for the mouse....(Grrr) - we use MOUSE /// [instance] : set to the device number; js1=1, js2=2, etc /// [optiongroup] : set to what group the option should affect (for available groups see default actionmap) /// [option] : instance, sensitivity, exponent, nonlinearity *instance is a bug that will be fixed to 'invert' in the future /// [value] : for invert use 0/1; for others use 0.0 to 2.0 /// /// options are given per deviceClass and instance - it seems /// public class OptionTree : ICloneable { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType ); // Support only one set of independent options (string storage) List m_stringOptions = new List( ); #region Clonable support // bag for all tuning items - key is the option name CloneableDictionary m_tuning = new CloneableDictionary( ); /// /// Clone this object /// /// A deep Clone of this object public object Clone() { var ot = (OptionTree)this.MemberwiseClone( ); // more objects to deep copy ot.m_stringOptions = new List( m_stringOptions ); if ( this.m_tuning != null ) ot.m_tuning = (CloneableDictionary)this.m_tuning.Clone( ); return ot; } /// /// Check clone against This /// /// /// True if the clone is identical but not a shallow copy internal bool CheckClone( OptionTree clone ) { bool ret = true; // object vars first ret &= ( !object.ReferenceEquals( this.m_stringOptions, clone.m_stringOptions ) ); // shall not be the same object !! // check m_tuning Dictionary ret &= ( this.m_tuning.Count == clone.m_tuning.Count ); if ( ret ) { for ( int i = 0; i < this.m_tuning.Count; i++ ) { ret &= ( this.m_tuning.ElementAt( i ).Key == clone.m_tuning.ElementAt( i ).Key ); ret &= ( !object.ReferenceEquals( this.m_tuning.ElementAt( i ).Value, clone.m_tuning.ElementAt( i ).Value ) ); // shall not be the same object !! ret &= ( this.m_tuning.ElementAt( i ).Value.CheckClone( clone.m_tuning.ElementAt( i ).Value ) ); } } return ret; } #endregion // ctor public OptionTree( DeviceCls device ) { if ( m_profileOptions.Count( ) == 0 ) { log.Error( "cTor OptionTree - profile not yet read" ); } // get options for the device class var devOpts = m_profileOptions.Where( x => x.DeviceClass == device.DevClass ); foreach (ProfileOptionRec rec in devOpts ) { m_tuning.Add( rec.OptName, new DeviceTuningParameter( rec.OptName, device ) ); } } public int Count { get { return ( m_stringOptions.Count + 1 ); } } public void ResetDynamicItems() { foreach ( KeyValuePair kv in m_tuning ) { DeviceTuningParameter item = kv.Value; if ( item != null ) { item.ResetDynamicItems( ); } } } // provide access to Tuning items /// /// Returns a tuning item for the asked option /// /// The option to get /// A DeviceTuning item or null if it does not exist public DeviceTuningParameter TuningItem( string optionName ) { if ( m_tuning.ContainsKey( optionName ) ) return m_tuning[optionName]; else return null; } private string[] FormatXml( string xml ) { try { var doc = XDocument.Parse( xml ); return doc.ToString( ).Split( new string[] { string.Format( "\n" ) }, StringSplitOptions.RemoveEmptyEntries ); } catch ( Exception ) { return new string[] { xml }; } } /// /// Dump the Options as partial XML nicely formatted /// /// the action as XML fragment public string toXML() { string r = ""; // and dump the contents of plain string options foreach ( string x in m_stringOptions ) { if ( !string.IsNullOrWhiteSpace( x ) ) { foreach ( string line in FormatXml( x ) ) { r += string.Format( "\t{0}", line ); } } r += string.Format( "\n" ); } // dump Tuning foreach ( KeyValuePair kv in m_tuning ) { r += kv.Value.Options_toXML( ); } return r; } /// /// Read an Options from XML - do some sanity check /// /// the XML action fragment /// True if an action was decoded public bool fromXML( XElement options ) { /* * This can be a lot of the following options * try to do our best.... * * * */ string instance = (string)options.Attribute( "instance" ); // mandadory string type = (string)options.Attribute( "type" ); // mandadory if ( !int.TryParse( instance, out int nInstance ) ) nInstance = 0; // get the one from the map if given (else it's a map error...) string productS = (string)options.Attribute( "Product" ); // optional 3.5 ?? string devClass = DeviceCls.DeviceClass; // the generic one if ( !string.IsNullOrEmpty(type)) devClass = type; // get the one from the map if given (else it's a map error...) // mouse arrives as type keyboard - fix it to deviceClass mouse here if ( KeyboardCls.IsDeviceClass( devClass ) ) devClass = MouseCls.DeviceClass; // check if the profile contains the one we found in the map - then parse the element foreach ( XElement item in options.Elements( ) ) { if ( m_profileOptions.Contains( new ProfileOptionRec( ) { DeviceClass = devClass, OptName = item.Name.LocalName } ) ) { m_tuning[item.Name.LocalName].Options_fromXML( item, devClass, int.Parse( instance ) ); } } return true; } #region static - Profile Handling public class ProfileOptionRec : IEquatable { public string DeviceClass { get; set; } public string OptGroup { get; set; } public string OptName { get; set; } public bool ShowCurve { get; set; } public bool ShowInvert { get; set; } public ProfileOptionRec() { DeviceClass = DeviceCls.DeviceClass; OptGroup = ""; OptName = ""; ShowCurve = false; ShowInvert = false; } // same class and name means records match public bool Equals( ProfileOptionRec other ) { return ( ( this.DeviceClass == other.DeviceClass ) && ( this.OptName == other.OptName ) ); } } private static List m_profileOptions = new List( ); /// /// Returns a list of ProfileOptions found in the defaultProfile /// public static IList ProfileOptions { get => m_profileOptions; } /// /// Clears the stored optiontree items from the profile /// must be cleared before re-reading them from profile /// public static void InitOptionReader() { m_profileOptions = new List( ); } /// /// Reads optiongroup nodes /// the tree is composed of such nodes - we dive recursively /// /// The optiongroup to parse /// The deviceclass it belongs to /// True if OK private static bool ReadOptiongroup( XElement optiongroupIn, string devClass, string optGroup ) { bool retVal = true; // collect content and process further groups string name = (string)optiongroupIn.Attribute( "name" ); string uiLabel = (string)optiongroupIn.Attribute( "UILabel" ); if ( string.IsNullOrEmpty( uiLabel ) ) uiLabel = name; // subst if not found in Action node SCUiText.Instance.Add( name, uiLabel ); // Register item for translation // further groups IEnumerable optiongroups = from x in optiongroupIn.Elements( ) where ( x.Name == "optiongroup" ) select x; foreach ( XElement optiongroup in optiongroups ) { retVal &= ReadOptiongroup( optiongroup, devClass, name ); // current is the group if we dive one down } // murks.. determine if it is a terminal node, then get items if ( optiongroups.Count() == 0 ) { ProfileOptionRec optRec = new ProfileOptionRec { DeviceClass = devClass, OptGroup=optGroup, OptName = name }; // create a new one // override props if they arrive in the node string attr = (string)optiongroupIn.Attribute( "UIShowCurve" ); if ( !string.IsNullOrEmpty( attr ) ) { if ( int.TryParse( attr, out int showCurve ) ) { optRec.ShowCurve = ( showCurve == 1 ); } } attr = (string)optiongroupIn.Attribute( "UIShowInvert" ); if ( !string.IsNullOrEmpty( attr ) ) { if ( int.TryParse( attr, out int showInvert ) ) { optRec.ShowInvert = ( showInvert == 1 ); } } if ( optRec.ShowCurve || optRec.ShowInvert) m_profileOptions.Add( optRec ); // add only if something is to tweak.. } return retVal; } /// /// Collects items from the profile optiontree /// /// The optiontree node /// True if OK public static bool fromProfileXML( XElement optiontree ) { /* // // // */ log.Debug( "fromProfileXML - Entry" ); bool retVal = true; string devClass = (string)optiontree.Attribute( "type" ); // mouse arrives as type keyboard - fix it to deviceClass mouse here if ( KeyboardCls.IsDeviceClass( devClass ) ) devClass = MouseCls.DeviceClass; IEnumerable optiongroups = from x in optiontree.Elements( ) where ( x.Name == "optiongroup" ) select x; foreach ( XElement optiongroup in optiongroups ) { retVal &= ReadOptiongroup( optiongroup, devClass, "master" ); } return retVal; } #endregion } }