From 47fb23f1cf9e66ecc5153f1699e109bc945f0e7e Mon Sep 17 00:00:00 2001 From: bm98 Date: Mon, 22 Dec 2014 01:34:09 +0100 Subject: [PATCH] Re-design and update for AC1.0 mapping policies Can only map to appropriate device type (mixed ones no longer supported) Can extend an action which then uses addbind in XML (context menu) Removed Invert per command (no longer supported) Update options usage and syntax Changed actionmap version to 1 Persist treeview filter checkboxes in app settings --- AppSettings.cs | 60 +++- DeviceCls.cs | 12 +- Form1.Designer.cs | 114 ++++--- Form1.cs | 155 +++++---- Form1.resx | 80 +++-- Joystick/DeviceDeadzoneParameter.cs | 13 +- Joystick/DeviceTuningParameter.cs | 28 +- MySounds.cs | 43 +++ OGL/FormJSCalCurve.cs | 19 +- Properties/AssemblyInfo.cs | 4 +- SCJMapper-V2.csproj | 7 +- actions/ActionCls.cs | 134 +++++--- actions/ActionCommandCls.cs | 68 ++++ actions/ActionMapsCls.cs | 8 +- actions/ActionTree.cs | 513 +++++++++++++++++++++++----- actions/ActionTreeInputNode.cs | 125 +++++++ actions/ActionTreeNode.cs | 64 +--- graphics/Addbind.ico | Bin 0 -> 15358 bytes 18 files changed, 1043 insertions(+), 404 deletions(-) create mode 100644 MySounds.cs create mode 100644 actions/ActionCommandCls.cs create mode 100644 actions/ActionTreeInputNode.cs create mode 100644 graphics/Addbind.ico diff --git a/AppSettings.cs b/AppSettings.cs index 66116bc..9ed3a9f 100644 --- a/AppSettings.cs +++ b/AppSettings.cs @@ -91,7 +91,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "False" )] // false + [DefaultSettingValueAttribute( "False" )] public Boolean BlendUnmapped // Joystick (back compatibility) { get { return ( Boolean )this["BlendUnmapped"]; } @@ -99,18 +99,50 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "False" )] // false + [DefaultSettingValueAttribute( "False" )] public Boolean BlendUnmappedGP { get { return ( Boolean )this["BlendUnmappedGP"]; } set { this["BlendUnmappedGP"] = value; } } + [UserScopedSettingAttribute( )] + [DefaultSettingValueAttribute( "True" )] + public Boolean ShowJoystick + { + get { return ( Boolean )this["ShowJoystick"]; } + set { this["ShowJoystick"] = value; } + } + + [UserScopedSettingAttribute( )] + [DefaultSettingValueAttribute( "True" )] + public Boolean ShowGamepad + { + get { return ( Boolean )this["ShowGamepad"]; } + set { this["ShowGamepad"] = value; } + } + + [UserScopedSettingAttribute( )] + [DefaultSettingValueAttribute( "True" )] + public Boolean ShowKeyboard + { + get { return ( Boolean )this["ShowKeyboard"]; } + set { this["ShowKeyboard"] = value; } + } + + [UserScopedSettingAttribute( )] + [DefaultSettingValueAttribute( "False" )] + public Boolean ShowMapped + { + get { return ( Boolean )this["ShowMapped"]; } + set { this["ShowMapped"] = value; } + } + // Seetings Window [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS1 { get { return ( String )this["IgnoreJS1"]; } @@ -118,7 +150,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS2 { get { return ( String )this["IgnoreJS2"]; } @@ -126,7 +158,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS3 { get { return ( String )this["IgnoreJS3"]; } @@ -134,7 +166,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS4 { get { return ( String )this["IgnoreJS4"]; } @@ -142,7 +174,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS5 { get { return ( String )this["IgnoreJS5"]; } @@ -150,7 +182,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS6 { get { return ( String )this["IgnoreJS6"]; } @@ -158,7 +190,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS7 { get { return ( String )this["IgnoreJS7"]; } @@ -166,7 +198,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String IgnoreJS8 { get { return ( String )this["IgnoreJS8"]; } @@ -174,7 +206,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "" )] // empty + [DefaultSettingValueAttribute( "" )] public String UserSCPath { get { return ( String )this["UserSCPath"]; } @@ -182,7 +214,7 @@ namespace SCJMapper_V2 } [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "False" )] // false + [DefaultSettingValueAttribute( "False" )] public Boolean UserSCPathUsed { get { return ( Boolean )this["UserSCPathUsed"]; } @@ -199,7 +231,7 @@ namespace SCJMapper_V2 [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "False" )] // false + [DefaultSettingValueAttribute( "False" )] public Boolean ForceIgnoreversion { get { return ( Boolean )this["ForceIgnoreversion"]; } @@ -208,7 +240,7 @@ namespace SCJMapper_V2 [UserScopedSettingAttribute( )] - [DefaultSettingValueAttribute( "False" )] // false + [DefaultSettingValueAttribute( "False" )] public Boolean DetectGamepad { get { return ( Boolean )this["DetectGamepad"]; } diff --git a/DeviceCls.cs b/DeviceCls.cs index bb57abd..75b7507 100644 --- a/DeviceCls.cs +++ b/DeviceCls.cs @@ -15,21 +15,13 @@ namespace SCJMapper_V2 public const String BlendedInput = " "; static public Boolean IsDeviceClass( String deviceClass ) { return false; } - public enum InputKind - { - Other, - Kbd, - Joystick, - Gamepad, - } - public abstract String DevClass { get; } public abstract String DevName { get; } public abstract System.Drawing.Color MapColor { get; } public abstract Boolean Activated { get; set; } - public virtual void FinishDX( ) {} - public virtual void ApplySettings( ){} + public virtual void FinishDX( ) { } + public virtual void ApplySettings( ) { } public abstract String GetLastChange( ); public abstract void GetCmdData( String cmd, out int data ); diff --git a/Form1.Designer.cs b/Form1.Designer.cs index b466c4c..7e939fe 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -47,7 +47,6 @@ this.btGrab = new System.Windows.Forms.Button(); this.btDump = new System.Windows.Forms.Button(); this.panel2 = new System.Windows.Forms.Panel(); - this.cbxInvert = new System.Windows.Forms.CheckBox(); this.btJsKbd = new System.Windows.Forms.Button(); this.IL = new System.Windows.Forms.ImageList(this.components); this.btBlend = new System.Windows.Forms.Button(); @@ -76,8 +75,6 @@ this.tlpanel = new System.Windows.Forms.TableLayoutPanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.txFilter = new System.Windows.Forms.TextBox(); - this.btClearFilter = new System.Windows.Forms.Button(); this.btJSTuning = new System.Windows.Forms.Button(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); this.btSettings = new System.Windows.Forms.Button(); @@ -92,6 +89,9 @@ this.cbxShowGamepad = new System.Windows.Forms.CheckBox(); this.cbxShowKeyboard = new System.Windows.Forms.CheckBox(); this.cbxShowMappedOnly = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txFilter = new System.Windows.Forms.TextBox(); + this.btClearFilter = new System.Windows.Forms.Button(); this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel(); this.tsDDbtProfiles = new System.Windows.Forms.ToolStripDropDownButton(); this.tsBtReset = new System.Windows.Forms.ToolStripDropDownButton(); @@ -107,8 +107,10 @@ this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.cmAddDel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.tsiAddBinding = new System.Windows.Forms.ToolStripMenuItem(); + this.tdiDelBinding = new System.Windows.Forms.ToolStripMenuItem(); this.UC_JoyPanel = new SCJMapper_V2.UC_JoyPanel(); - this.label2 = new System.Windows.Forms.Label(); this.cmCopyPaste.SuspendLayout(); this.panel2.SuspendLayout(); this.tc1.SuspendLayout(); @@ -121,6 +123,7 @@ this.tableLayoutPanel3.SuspendLayout(); this.flowLayoutPanel2.SuspendLayout(); this.statusStrip1.SuspendLayout(); + this.cmAddDel.SuspendLayout(); this.SuspendLayout(); // // btDumpList @@ -232,7 +235,6 @@ // panel2 // this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.panel2.Controls.Add(this.cbxInvert); this.panel2.Controls.Add(this.btJsKbd); this.panel2.Controls.Add(this.btBlend); this.panel2.Controls.Add(this.lblLastJ); @@ -248,16 +250,6 @@ this.panel2.Size = new System.Drawing.Size(289, 142); this.panel2.TabIndex = 17; // - // cbxInvert - // - this.cbxInvert.AutoSize = true; - this.cbxInvert.Location = new System.Drawing.Point(89, 86); - this.cbxInvert.Name = "cbxInvert"; - this.cbxInvert.Size = new System.Drawing.Size(55, 17); - this.cbxInvert.TabIndex = 17; - this.cbxInvert.Text = "Invert"; - this.cbxInvert.UseVisualStyleBackColor = true; - // // btJsKbd // this.btJsKbd.FlatStyle = System.Windows.Forms.FlatStyle.Flat; @@ -283,6 +275,7 @@ this.IL.Images.SetKeyName(4, "X"); this.IL.Images.SetKeyName(5, "P"); this.IL.Images.SetKeyName(6, "Z"); + this.IL.Images.SetKeyName(7, "Add"); // // btBlend // @@ -308,7 +301,7 @@ // cbxThrottle // this.cbxThrottle.AutoSize = true; - this.cbxThrottle.Location = new System.Drawing.Point(89, 67); + this.cbxThrottle.Location = new System.Drawing.Point(89, 72); this.cbxThrottle.Name = "cbxThrottle"; this.cbxThrottle.Size = new System.Drawing.Size(66, 17); this.cbxThrottle.TabIndex = 13; @@ -375,6 +368,7 @@ // // treeView1 // + this.treeView1.ContextMenuStrip = this.cmAddDel; this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill; this.treeView1.HotTracking = true; this.treeView1.ImageKey = "Map"; @@ -572,26 +566,6 @@ this.tableLayoutPanel1.Size = new System.Drawing.Size(294, 131); this.tableLayoutPanel1.TabIndex = 23; // - // txFilter - // - this.txFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.txFilter.Location = new System.Drawing.Point(92, 28); - this.txFilter.Name = "txFilter"; - this.txFilter.Size = new System.Drawing.Size(120, 22); - this.txFilter.TabIndex = 25; - this.txFilter.WordWrap = false; - this.txFilter.TextChanged += new System.EventHandler(this.txFilter_TextChanged); - // - // btClearFilter - // - this.btClearFilter.Location = new System.Drawing.Point(218, 26); - this.btClearFilter.Name = "btClearFilter"; - this.btClearFilter.Size = new System.Drawing.Size(120, 24); - this.btClearFilter.TabIndex = 26; - this.btClearFilter.Text = "Clear Filter"; - this.btClearFilter.UseVisualStyleBackColor = true; - this.btClearFilter.Click += new System.EventHandler(this.btClearFilter_Click); - // // btJSTuning // this.btJSTuning.Location = new System.Drawing.Point(3, 93); @@ -767,6 +741,36 @@ this.cbxShowMappedOnly.UseVisualStyleBackColor = true; this.cbxShowMappedOnly.CheckedChanged += new System.EventHandler(this.cbxShowTreeOptions_CheckedChanged); // + // label2 + // + this.label2.Location = new System.Drawing.Point(3, 26); + this.label2.Margin = new System.Windows.Forms.Padding(3); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(83, 24); + this.label2.TabIndex = 27; + this.label2.Text = "Action Filter:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // txFilter + // + this.txFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.txFilter.Location = new System.Drawing.Point(92, 28); + this.txFilter.Name = "txFilter"; + this.txFilter.Size = new System.Drawing.Size(120, 22); + this.txFilter.TabIndex = 25; + this.txFilter.WordWrap = false; + this.txFilter.TextChanged += new System.EventHandler(this.txFilter_TextChanged); + // + // btClearFilter + // + this.btClearFilter.Location = new System.Drawing.Point(218, 26); + this.btClearFilter.Name = "btClearFilter"; + this.btClearFilter.Size = new System.Drawing.Size(120, 24); + this.btClearFilter.TabIndex = 26; + this.btClearFilter.Text = "Clear Filter"; + this.btClearFilter.UseVisualStyleBackColor = true; + this.btClearFilter.Click += new System.EventHandler(this.btClearFilter_Click); + // // toolStripStatusLabel2 // this.toolStripStatusLabel2.BackColor = System.Drawing.Color.DarkKhaki; @@ -912,6 +916,29 @@ this.statusStrip1.TabIndex = 26; this.statusStrip1.Text = "statusStrip1"; // + // cmAddDel + // + this.cmAddDel.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsiAddBinding, + this.tdiDelBinding}); + this.cmAddDel.Name = "cmAddDel"; + this.cmAddDel.Size = new System.Drawing.Size(159, 48); + this.cmAddDel.Opening += new System.ComponentModel.CancelEventHandler(this.cmAddDel_Opening); + // + // tsiAddBinding + // + this.tsiAddBinding.Name = "tsiAddBinding"; + this.tsiAddBinding.Size = new System.Drawing.Size(158, 22); + this.tsiAddBinding.Text = "Add Mapping"; + this.tsiAddBinding.Click += new System.EventHandler(this.tsiAddBinding_Click); + // + // tdiDelBinding + // + this.tdiDelBinding.Name = "tdiDelBinding"; + this.tdiDelBinding.Size = new System.Drawing.Size(158, 22); + this.tdiDelBinding.Text = "Delete Mapping"; + this.tdiDelBinding.Click += new System.EventHandler(this.tdiDelBinding_Click); + // // UC_JoyPanel // this.UC_JoyPanel.Dock = System.Windows.Forms.DockStyle.Fill; @@ -921,16 +948,6 @@ this.UC_JoyPanel.Size = new System.Drawing.Size(275, 315); this.UC_JoyPanel.TabIndex = 0; // - // label2 - // - this.label2.Location = new System.Drawing.Point(3, 26); - this.label2.Margin = new System.Windows.Forms.Padding(3); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(83, 24); - this.label2.TabIndex = 27; - this.label2.Text = "Action Filter:"; - this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -964,6 +981,7 @@ this.flowLayoutPanel2.PerformLayout(); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); + this.cmAddDel.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -1039,13 +1057,15 @@ private System.Windows.Forms.Button btJsKbd; private System.Windows.Forms.Button btBlend; private System.Windows.Forms.Button btClip; - private System.Windows.Forms.CheckBox cbxInvert; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; private System.Windows.Forms.CheckBox cbxShowJoystick; private System.Windows.Forms.CheckBox cbxShowGamepad; private System.Windows.Forms.CheckBox cbxShowKeyboard; private System.Windows.Forms.CheckBox cbxShowMappedOnly; private System.Windows.Forms.Label label2; + private System.Windows.Forms.ContextMenuStrip cmAddDel; + private System.Windows.Forms.ToolStripMenuItem tsiAddBinding; + private System.Windows.Forms.ToolStripMenuItem tdiDelBinding; } } diff --git a/Form1.cs b/Form1.cs index a9ae453..a8bd654 100644 --- a/Form1.cs +++ b/Form1.cs @@ -20,6 +20,8 @@ namespace SCJMapper_V2 private const String c_GithubLink = @"https://github.com/SCToolsfactory/SCJMapper-V2/releases"; private AppSettings m_AppSettings = new AppSettings( ); + private Boolean m_appLoading = true; // used to detect if we are loading (or running) + /// /// Holds the DXInput Joystick List @@ -77,19 +79,19 @@ namespace SCJMapper_V2 /// /// Detects and returns the current Input device /// - private DeviceCls.InputKind InputMode + private ActionCls.ActionDevice InputMode { get { if ( m_keyIn ) { - return DeviceCls.InputKind.Kbd; + return ActionCls.ActionDevice.AD_Keyboard; } else { if ( IsGamepadTab( tc1.SelectedTab ) ) { - return DeviceCls.InputKind.Gamepad; + return ActionCls.ActionDevice.AD_Gamepad; } else { - return DeviceCls.InputKind.Joystick; + return ActionCls.ActionDevice.AD_Joystick; } } } @@ -240,9 +242,17 @@ namespace SCJMapper_V2 txMappingName.BackColor = MyColors.ErrorColor; } + // load show checkboxes + cbxShowJoystick.Checked = m_AppSettings.ShowJoystick; + cbxShowGamepad.Checked = m_AppSettings.ShowGamepad; + cbxShowKeyboard.Checked = m_AppSettings.ShowKeyboard; + cbxShowMappedOnly.Checked = m_AppSettings.ShowMapped; + // poll the XInput log.Debug( "Start XInput polling" ); timer1.Start( ); // this one polls the joysticks to show the props + + m_appLoading = false; // no longer } @@ -336,7 +346,7 @@ namespace SCJMapper_V2 m_AT.Ctrl = treeView1; // the ActionTree owns the TreeView control m_AT.IgnoreMaps = m_AppSettings.IgnoreActionmaps; m_AT.DefineShowOptions( cbxShowJoystick.Checked, cbxShowGamepad.Checked, cbxShowKeyboard.Checked, cbxShowMappedOnly.Checked ); - m_AT.LoadTree( m_AppSettings.DefProfileName, addDefaultBinding ); // Init with default profile filepath + m_AT.LoadProfileTree( m_AppSettings.DefProfileName, addDefaultBinding ); // Init with default profile filepath // apply a default JS to Joystick mapping - can be changed and reloaded from XML mappings // must take care of Gamepads if there are (but we take care of one only...) @@ -575,6 +585,7 @@ namespace SCJMapper_V2 private void timer1_Tick( object sender, System.EventArgs e ) { if ( m_keyIn ) return; // allow keyboard / mouse input + if ( tc1.SelectedTab.Tag == null ) return; String ctrl = ""; int jsIndex = ( int )tc1.SelectedTab.Tag; // gets the index into the JS list @@ -592,14 +603,9 @@ namespace SCJMapper_V2 lblLastJ.Text = ctrl; if ( JoystickCls.CanThrottle( ctrl ) ) { cbxThrottle.Enabled = true; - cbxInvert.Enabled = true; - } - else if ( GamepadCls.CanInvert( ctrl ) ) { - cbxInvert.Enabled = true; } else { cbxThrottle.Checked = false; cbxThrottle.Enabled = false; - cbxInvert.Checked = false; cbxInvert.Enabled = false; } } @@ -608,15 +614,8 @@ namespace SCJMapper_V2 private void treeView1_AfterSelect( object sender, TreeViewEventArgs e ) { - if ( e.Node.Level == 1 ) { - // actions cannot have a blank - if there is one it's mapped - if ( e.Node.Text.IndexOf( " ", 0 ) > 0 ) { - lblAction.Text = e.Node.Text.Substring( 0, e.Node.Text.IndexOf( " ", 0 ) ); // get only the action part as Cmd. - } - else { - lblAction.Text = e.Node.Text; - } - } + String atx = m_AT.SelectedAction; + if ( !String.IsNullOrEmpty( atx ) ) lblAction.Text = atx; } @@ -624,8 +623,13 @@ namespace SCJMapper_V2 private void cbxShowTreeOptions_CheckedChanged( object sender, EventArgs e ) { + if (m_AT==null) return; // on init m_AT.DefineShowOptions( cbxShowJoystick.Checked, cbxShowGamepad.Checked, cbxShowKeyboard.Checked, cbxShowMappedOnly.Checked ); m_AT.ReloadTreeView( ); + + if ( m_appLoading ) return; // don't assign while loading defaults + m_AppSettings.ShowJoystick = cbxShowJoystick.Checked; m_AppSettings.ShowGamepad = cbxShowGamepad.Checked; + m_AppSettings.ShowKeyboard = cbxShowKeyboard.Checked; m_AppSettings.ShowMapped = cbxShowMappedOnly.Checked; } @@ -639,20 +643,33 @@ namespace SCJMapper_V2 private void btAssign_Click( object sender, EventArgs e ) { - m_AT.UpdateSelectedItem( JoystickCls.MakeThrottle( lblLastJ.Text, cbxThrottle.Checked ), cbxInvert.Checked, InputMode ); - if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + if ( m_AT.UpdateSelectedItem( JoystickCls.MakeThrottle( lblLastJ.Text, cbxThrottle.Checked ), InputMode ) ) { + if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + } + else MySounds.PlayNotfound( ); } - - // General Area Items + private void btBlend_Click( object sender, EventArgs e ) + { + if ( m_AT.CanBlendBinding ) { + m_AT.UpdateSelectedItem( DeviceCls.BlendedInput, InputMode ); + if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + } + else MySounds.PlayCannot( ); + } private void btClear_Click( object sender, EventArgs e ) { - - m_AT.UpdateSelectedItem( "", false, InputMode ); - if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + if ( m_AT.CanClearBinding ) { + m_AT.UpdateSelectedItem( "", InputMode ); + if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + } + else MySounds.PlayCannot( ); } + + // General Area Items + private void btDump_Click( object sender, EventArgs e ) { Dump( ); @@ -766,6 +783,7 @@ namespace SCJMapper_V2 // Context Menu Items + // RTB Menu private void tsiCopy_Click( object sender, EventArgs e ) { rtb.Focus( ); @@ -808,6 +826,28 @@ namespace SCJMapper_V2 } } + // Node Menu + private void cmAddDel_Opening( object sender, CancelEventArgs e ) + { + ContextMenuStrip cts = ( sender as ContextMenuStrip ); + Boolean any=false; + cts.Items[0].Visible = m_AT.CanAddBinding; any = any || m_AT.CanAddBinding; + cts.Items[1].Visible = m_AT.CanDelBinding; any = any || m_AT.CanDelBinding; + e.Cancel = !any; + } + + private void tsiAddBinding_Click( object sender, EventArgs e ) + { + m_AT.AddBinding( ); + if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + } + + private void tdiDelBinding_Click( object sender, EventArgs e ) + { + m_AT.DelBinding( ); + if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; + } + // rtb drop xml file @@ -961,14 +1001,6 @@ namespace SCJMapper_V2 } - // Blend - - private void btBlend_Click( object sender, EventArgs e ) - { - m_AT.UpdateSelectedItem( DeviceCls.BlendedInput, false, InputMode ); - if ( m_AT.Dirty ) btDump.BackColor = MyColors.DirtyColor; - } - // Joystick Tuning private void btJSTuning_Click( object sender, EventArgs e ) @@ -983,22 +1015,15 @@ namespace SCJMapper_V2 DeviceCls dev = null; String find = ""; - find = ActionTreeNode.ComposeNodeText( "v_yaw", ActionTreeNode.REG_MOD, "js" ); + // find action item for yaw + find = ActionTreeNode.ComposeNodeText( "v_yaw", "js" ); nodeText = m_AT.FindText( "spaceship_movement", find ); // returns "" or a complete text ("action - command") - if ( String.IsNullOrWhiteSpace( nodeText ) ) { - find = ActionTreeNode.ComposeNodeText( "v_yaw", ActionTreeNode.INV_MOD, "js" ); - nodeText = m_AT.FindText( "spaceship_movement", find ); // find inverted ones too - } if ( !String.IsNullOrWhiteSpace( nodeText ) ) { dev = m_Joystick.Find_jsN( JoystickCls.JSNum( ActionTreeNode.CommandFromNodeText( nodeText ) ) ); } else { - find = ActionTreeNode.ComposeNodeText( "v_yaw", ActionTreeNode.REG_MOD, "xi" ); + find = ActionTreeNode.ComposeNodeText( "v_yaw", "xi" ); nodeText = m_AT.FindText( "spaceship_movement", find ); - if ( String.IsNullOrWhiteSpace( nodeText ) ) { - find = ActionTreeNode.ComposeNodeText( "v_yaw", ActionTreeNode.INV_MOD, "xi" ); - nodeText = m_AT.FindText( "spaceship_movement", find ); // find inverted ones too - } if ( !String.IsNullOrWhiteSpace( nodeText ) ) { dev = m_Gamepad; } @@ -1008,21 +1033,21 @@ namespace SCJMapper_V2 // JS commands that are supported if ( nodeText.ToLowerInvariant( ).EndsWith( "_x" ) || nodeText.ToLowerInvariant( ).EndsWith( "_rotx" ) ) { m_AT.ActionMaps.TuningY.GameDevice = dev; - m_AT.ActionMaps.TuningY.ActionCommand = nodeText; + m_AT.ActionMaps.TuningY.Action = nodeText; m_AT.ActionMaps.DeadzoneX.GameDevice = dev; m_AT.ActionMaps.TuningY.Deadzone = m_AT.ActionMaps.DeadzoneX; JSCAL.YawTuning = m_AT.ActionMaps.TuningY; } else if ( nodeText.ToLowerInvariant( ).EndsWith( "_y" ) || nodeText.ToLowerInvariant( ).EndsWith( "_roty" ) ) { m_AT.ActionMaps.TuningY.GameDevice = dev; - m_AT.ActionMaps.TuningY.ActionCommand = nodeText; + m_AT.ActionMaps.TuningY.Action = nodeText; m_AT.ActionMaps.DeadzoneY.GameDevice = dev; m_AT.ActionMaps.TuningY.Deadzone = m_AT.ActionMaps.DeadzoneY; JSCAL.YawTuning = m_AT.ActionMaps.TuningY; } else if ( nodeText.ToLowerInvariant( ).EndsWith( "_z" ) || nodeText.ToLowerInvariant( ).EndsWith( "_rotz" ) ) { m_AT.ActionMaps.TuningY.GameDevice = dev; - m_AT.ActionMaps.TuningY.ActionCommand = nodeText; + m_AT.ActionMaps.TuningY.Action = nodeText; m_AT.ActionMaps.DeadzoneZ.GameDevice = dev; m_AT.ActionMaps.TuningY.Deadzone = m_AT.ActionMaps.DeadzoneZ; JSCAL.YawTuning = m_AT.ActionMaps.TuningY; @@ -1030,7 +1055,7 @@ namespace SCJMapper_V2 // GP commands that are supported - X else if ( nodeText.ToLowerInvariant( ).Contains( "_thumblx" ) || nodeText.ToLowerInvariant( ).Contains( "_thumbrx" ) ) { m_AT.ActionMaps.TuningY.GameDevice = dev; - m_AT.ActionMaps.TuningY.ActionCommand = nodeText; + m_AT.ActionMaps.TuningY.Action = nodeText; m_AT.ActionMaps.DeadzoneX.GameDevice = dev; m_AT.ActionMaps.TuningY.Deadzone = m_AT.ActionMaps.DeadzoneX; JSCAL.YawTuning = m_AT.ActionMaps.TuningY; @@ -1039,22 +1064,14 @@ namespace SCJMapper_V2 // attach Pitch command dev = null; - find = ActionTreeNode.ComposeNodeText( "v_pitch", ActionTreeNode.REG_MOD, "js" ); + find = ActionTreeNode.ComposeNodeText( "v_pitch", "js" ); nodeText = m_AT.FindText( "spaceship_movement", find ); // returns "" or a complete text ("action - command") - if ( String.IsNullOrWhiteSpace( nodeText ) ) { - find = ActionTreeNode.ComposeNodeText( "v_pitch", ActionTreeNode.INV_MOD, "js" ); - nodeText = m_AT.FindText( "spaceship_movement", find ); // find inverted ones too - } if ( !String.IsNullOrWhiteSpace( nodeText ) ) { dev = m_Joystick.Find_jsN( JoystickCls.JSNum( ActionTreeNode.CommandFromNodeText( nodeText ) ) ); } else { - find = ActionTreeNode.ComposeNodeText( "v_pitch", ActionTreeNode.REG_MOD, "xi" ); + find = ActionTreeNode.ComposeNodeText( "v_pitch", "xi" ); nodeText = m_AT.FindText( "spaceship_movement", find ); - if ( String.IsNullOrWhiteSpace( nodeText ) ) { - find = ActionTreeNode.ComposeNodeText( "v_pitch", ActionTreeNode.INV_MOD, "xi" ); - nodeText = m_AT.FindText( "spaceship_movement", find ); // find inverted ones too - } if ( !String.IsNullOrWhiteSpace( nodeText ) ) { dev = m_Gamepad; } @@ -1064,21 +1081,21 @@ namespace SCJMapper_V2 // JS commands that are supported if ( nodeText.ToLowerInvariant( ).EndsWith( "_x" ) || nodeText.ToLowerInvariant( ).EndsWith( "_rotx" ) ) { m_AT.ActionMaps.TuningP.GameDevice = dev; - m_AT.ActionMaps.TuningP.ActionCommand = nodeText; + m_AT.ActionMaps.TuningP.Action = nodeText; m_AT.ActionMaps.DeadzoneX.GameDevice = dev; m_AT.ActionMaps.TuningP.Deadzone = m_AT.ActionMaps.DeadzoneX; JSCAL.PitchTuning = m_AT.ActionMaps.TuningP; } else if ( nodeText.ToLowerInvariant( ).EndsWith( "_y" ) || nodeText.ToLowerInvariant( ).EndsWith( "_roty" ) ) { m_AT.ActionMaps.TuningP.GameDevice = dev; - m_AT.ActionMaps.TuningP.ActionCommand = nodeText; + m_AT.ActionMaps.TuningP.Action = nodeText; m_AT.ActionMaps.DeadzoneY.GameDevice = dev; m_AT.ActionMaps.TuningP.Deadzone = m_AT.ActionMaps.DeadzoneY; JSCAL.PitchTuning = m_AT.ActionMaps.TuningP; } else if ( nodeText.ToLowerInvariant( ).EndsWith( "_z" ) || nodeText.ToLowerInvariant( ).EndsWith( "_rotz" ) ) { m_AT.ActionMaps.TuningP.GameDevice = dev; - m_AT.ActionMaps.TuningP.ActionCommand = nodeText; + m_AT.ActionMaps.TuningP.Action = nodeText; m_AT.ActionMaps.DeadzoneZ.GameDevice = dev; m_AT.ActionMaps.TuningP.Deadzone = m_AT.ActionMaps.DeadzoneZ; JSCAL.PitchTuning = m_AT.ActionMaps.TuningP; @@ -1086,7 +1103,7 @@ namespace SCJMapper_V2 // GP commands that are supported - either Y else if ( nodeText.ToLowerInvariant( ).Contains( "_thumbly" ) || nodeText.ToLowerInvariant( ).Contains( "_thumbry" ) ) { m_AT.ActionMaps.TuningP.GameDevice = dev; - m_AT.ActionMaps.TuningP.ActionCommand = nodeText; + m_AT.ActionMaps.TuningP.Action = nodeText; m_AT.ActionMaps.DeadzoneY.GameDevice = dev; m_AT.ActionMaps.TuningP.Deadzone = m_AT.ActionMaps.DeadzoneY; JSCAL.PitchTuning = m_AT.ActionMaps.TuningP; @@ -1095,12 +1112,8 @@ namespace SCJMapper_V2 // attach Roll command - cannot use gamepad here dev = null; - find = ActionTreeNode.ComposeNodeText( "v_roll", ActionTreeNode.REG_MOD, "js" ); + find = ActionTreeNode.ComposeNodeText( "v_roll", "js" ); nodeText = m_AT.FindText( "spaceship_movement", find ); // returns "" or a complete text ("action - command") - if ( String.IsNullOrWhiteSpace( nodeText ) ) { - find = ActionTreeNode.ComposeNodeText( "v_roll", ActionTreeNode.INV_MOD, "js" ); - nodeText = m_AT.FindText( "spaceship_movement", find ); // find inverted ones too - } if ( !String.IsNullOrWhiteSpace( nodeText ) ) { dev = m_Joystick.Find_jsN( JoystickCls.JSNum( ActionTreeNode.CommandFromNodeText( nodeText ) ) ); } @@ -1109,21 +1122,21 @@ namespace SCJMapper_V2 // JS commands that are supported if ( nodeText.ToLowerInvariant( ).EndsWith( "_x" ) || nodeText.ToLowerInvariant( ).EndsWith( "_rotx" ) ) { m_AT.ActionMaps.TuningR.GameDevice = dev; - m_AT.ActionMaps.TuningR.ActionCommand = nodeText; + m_AT.ActionMaps.TuningR.Action = nodeText; m_AT.ActionMaps.DeadzoneX.GameDevice = dev; m_AT.ActionMaps.TuningR.Deadzone = m_AT.ActionMaps.DeadzoneX; JSCAL.RollTuning = m_AT.ActionMaps.TuningR; } else if ( nodeText.ToLowerInvariant( ).EndsWith( "_y" ) || nodeText.ToLowerInvariant( ).EndsWith( "_roty" ) ) { m_AT.ActionMaps.TuningR.GameDevice = dev; - m_AT.ActionMaps.TuningR.ActionCommand = nodeText; + m_AT.ActionMaps.TuningR.Action = nodeText; m_AT.ActionMaps.DeadzoneY.GameDevice = dev; m_AT.ActionMaps.TuningR.Deadzone = m_AT.ActionMaps.DeadzoneY; JSCAL.RollTuning = m_AT.ActionMaps.TuningR; } else if ( nodeText.ToLowerInvariant( ).EndsWith( "_z" ) || nodeText.ToLowerInvariant( ).EndsWith( "_rotz" ) ) { m_AT.ActionMaps.TuningR.GameDevice = dev; - m_AT.ActionMaps.TuningR.ActionCommand = nodeText; + m_AT.ActionMaps.TuningR.Action = nodeText; m_AT.ActionMaps.DeadzoneZ.GameDevice = dev; m_AT.ActionMaps.TuningR.Deadzone = m_AT.ActionMaps.DeadzoneZ; JSCAL.RollTuning = m_AT.ActionMaps.TuningR; @@ -1218,5 +1231,7 @@ namespace SCJMapper_V2 + + } } diff --git a/Form1.resx b/Form1.resx index 449401b..c1e6799 100644 --- a/Form1.resx +++ b/Form1.resx @@ -384,6 +384,9 @@ x1//2Q== + + 652, 17 + 494, 17 @@ -391,9 +394,9 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAAg - DgAAAk1TRnQBSQFMAgEBBwEAATgBCwE4AQsBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo - AwABQAMAASADAAEBAQABCAYAAQgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABK + DwAAAk1TRnQBSQFMAgEBCAEAAZABCwGQAQsBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + AwABQAMAATADAAEBAQABCAYAAQwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm @@ -420,39 +423,44 @@ AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw - AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/yYAAXQFRgHyFQAB/wH0 - CwABBxUAAUYHJQHyFAACQwEVCQABQwEVAUMCAAHwCwAB8AQAAUYJJQHyEwABQwIVCAAB8gFDARUBQwEA - AgcB7QgAAf8BkgG8Ae8CAAFGAiUB/wFMAyUCGgIlARsRAAEHAUMBFQETAUMB9AUAAQcBFQEUARUBQwEA - AfcBvAHtCQAB9wHwAZIBAAF0AiUB9gL/AUwBJQH2Av8DJREAAfQBQwEVAeoB+QIPAx8BQwEUAR8BFAEV - AUMB/wHtAfAB7AHqAW0B7wMAAe8BDgHsAe8B8AHsAQABRgIlAUYD/wH2A/8DJRIAAUMBbQHqAUQCEAMg - AQ4BbQEUAhABEQEAAewBvAH3AeoB6wESAewBBwHsARIB6wFtAgcB6wEAAUYDKwFMBf8EKxIAAUMBFQES - ASABDgEPAR8CIAIVAREBFAEQAUMBAAFtAuwB7QEUAW0BkgHsAZIBbQHqAe0B7AHtAQ4BAAFGBEwB9gP/ - AXQETBIAAUMB7AEUAQ8CHwEAAQ4BDwEBAR4BRAFDARABEQIAAfcB7wHrAu8B7QGSAe0B7wH3AZEC7wIA - AUYDTAH2Bf8BdANMEgABvAEUAQ4BEAFDAUUBHgEBAUMBHgFvAQ4CEAMAAuwBvAHsAQcBvAFtArwBjQG8 - ARUBvAIAAUYCTAEaA/8BTAP/A0wTAAFDARABbQEVAQEBEQEVAQ4BHgELAR8CDgMAAfIB8wHsAfIF9AHy - ARIB8gHzAgAB8wFTAUwBmgL/A0wC/wJMAU0TAAEVAhEBHgEABw4FAAG8AvAFAALwAbwBEQMAAfMBUwpN - FQAB7AEQAQ8BFQMAAUMCDwH0BQABEQcAAf8BAAETBQAB8wlTNwAB8wdTlAAg/wcAAUMBAAHyFgAB/w7x - AfAG/wH0AygG/wUAAQ4BkQGuApEB6gH/BwAL/wIAAf8B8QNrAYsBkQGLBIoBSQFKAZEB8Qb/ASgDKQEo - Bf8EAAFDAZoBegFSAZEBtQESAfEGAAH3AQcBvAIHAu8DBwHwAfIBBwEAAf8B8QIGAYsBkAG7BYsCUAFr - AfEG/wFQBCkBKAT/AwABDwEHAZECcwGRAbUBtAFDAfQEAAH/AbwB7wO8AwcBvAEHAfAB8gHxAQAB/wHx - AgYBiwGQAbsEkAFsAVABUQFrAfEH/wVQAXMD/wEAARQBUgHzArwBBwH3AZECtAEOAfQEAAH/AbwB7wPw - ArwC8AG8AfEB8gHwAQAB/wHxAosBkAGzAbsBKQGzAXEDUAGQAWsB8QL/AXMFKAVQAXMC/wIAARoB9AH3 - ARIBQwERAbsBkQF6AewB/wLxAfMBAAH/Ae8B9wG8BvABvALxAbwBAAH/AfIDkAGzAbsCswNRASkBswFr - AfIB/w1QAZkB/wIAAQ4B7wETAeoBFQEPAZEBDwEOBfEBAAH/Ae0BvAnyAfABBwEAAf8B8gSzAboBiwEp - BHgBUQFrAfIB/w5QAf8EAAFtAREBQwEQARUB8QL/AfQC/wIAAf8B9wHxCPIB8QHwAbwBAAH/AfIEswG6 - AbsBKAJ4AVABeAEoAWsB8gH/ASkMUAEoAf8EAAG7AbQBkQFtCQAB/wGSAfEB8gbzAfIC8QHwAQAB/wHz - BLMBuQG6AdwBtAFJAlABSQFrAfMC/wFQBXgBVwRRASkC/wMAAfMBAAGRARMLAAGSAfEB8gLzAQcBkgLz - AfIB8QHyAfMBAAH/AfMGuQG6AdsD3AHbAWsB8wf/ASgEVwFQA/8DAAHwAQ4B7AEVCwAB7QHxAfIC8wHt - Ae8C8wHyAfEB8gH/AQAB/wHzDNoBiwHzBv8BmQR4AVcE/wEAAfEBAAEHAe0B7AGuAfIKAAEHAvIC8wG8 - A/MB8gHxAfICAAH/D/MG/wEpBHgF/wEAAW4BeQG8AfcBEwHwDAAC8gbzAfIB8QH0AgAB/wjzAfIB9wHy - AfcB8gH3AfMG/wEcA3gG/wHtAesBvALvDgAC8QbyAvEDAAH/AfMN/wHvB/8BmQFQCP8BEwHwAeocACD/ - AgAB8wH3Af8bAAFCAU0BPgcAAT4DAAEoAwABQAMAASADAAEBAQABAQYAAQEWAAP/AQAE/wH4AQ8CAAGf - Af0C/wHwAQcCAAGPAfgB3wH9AeABAwIAAY8B8AGPAfABwAEBAgABAwHgAQcB8AGAAQEFAAKAAQECAAGA - AQABgAEAAYABAQIAAYABAAGAAQABgAEBAgABgAEAAYABAAGAAQECAAGAAQEBgAEAAYABAQIAAcABAQHA - AQEBgAEBAgABwAEBAcMB4QHAAQMCAAHgAQMB5wHjAeABBwIABP8B8AEPAgAG/wIABv8GAAH8AT8C/wQA - AfgBDwHgAQMEAAHwAQ8BwAEBBAABwAEHAYABAQQAAYABBwGAAQEEAAGAAQABgAEBBAABwAEAAYABAQQA - AeABAQGAAQEEAAHgAf8BgAEBBAAB4AH/AcABAQQAAeEB/wHAAQEEAAGAAf8BwAEDBAABgQH/AeABAwQA - AQMB/wHgAQcEAAEHA/8EAAHHA/8L + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8A/wD/AP8AKgABdAVG + AfIHAARIAQ8IEgEAAf8B9AsAAQcVAAFGByUB8gYAAUgCTwFIAW0D+AP0Ae4BEgEAAkMBFQkAAUMBFQFD + AgAB8AsAAfAEAAFGCSUB8gIAA5EBSAFWAU8BSANJAfgD/wG8ARIBAAFDAhUIAAHyAUMBFQFDAQACBwHt + CAAB/wGSAbwB7wIAAUYCJQH/AUwDJQIaAiUBGwEAAUgDTwJWAk8BTgFIAe0D/wHwARIBBwFDARUBEwFD + AfQFAAEHARUBFAEVAUMBAAH3AbwB7QkAAfcB8AGSAQABdAIlAfYC/wFMASUB9gL/AyUBAAFIA1cDVgJP + AUgB9wP/AfABEgH0AUMBFQHqAfkCDwMfAUMBFAEfARQBFQFDAf8B7QHwAewB6gFtAe8DAAHvAQ4B7AHv + AfAB7AEAAUYCJQFGA/8B9gP/AyUBAAFIBFcBVgJXAVYBSAHvA/8B8AESAQABQwFtAeoBRAIQAyABDgFt + ARQCEAERAQAB7AG8AfcB6gHrARIB7AEHAewBEgHrAW0CBwHrAQABRgMrAUwF/wQrAQADkQFIAlcBSANx + Ae4D/wHxARIBAAFDARUBEgEgAQ4BDwEfAiACFQERARQBEAFDAQABbQLsAe0BFAFtAZIB7AGSAW0B6gHt + AewB7QEOAQABRgRMAfYD/wF0BEwEAAFIAlcBSAHxA/ID/wHxARIBAAFDAewBFAEPAh8BAAEOAQ8BAQEe + AUQBQwEQARECAAH3Ae8B6wLvAe0BkgHtAe8B9wGRAu8CAAFGA0wB9gX/AXQDTAQAAUgCTwFIAfQG/wHy + ARIBAAG8ARQBDgEQAUMBRQEeAQEBQwEeAW8BDgIQAwAC7AG8AewBBwG8AW0CvAGNAbwBFQG8AgABRgJM + ARoD/wFMA/8DTAQAARQBuwIIB/8B8wESAgABQwEQAW0BFQEBAREBFQEOAR4BCwEfAg4DAAHyAfMB7AHy + BfQB8gESAfIB8wIAAfMBUwFMAZoC/wNMAv8CTAFNBAABEgH0Bv8B9AHxAe4B7wESAgABFQIRAR4BAAcO + BQABvALwBQAC8AG8AREDAAHzAVMKTQUAARIB9AX/AfQBBwPyARIDAAHsARABDwEVAwABQwIPAfQFAAER + BwAB/wEAARMFAAHzCVMGAAESAfQF/wHxAe4C/wESAfEkAAHzB1MHAAESAfUF/wLuAf8BEgHxNAABEgb1 + Ae8B7gESAfE1AAkSAfEDACD/BwABQwEAAfIWAAH/DvEB8Ab/AfQDKAb/BQABDgGRAa4CkQHqAf8HAAv/ + AgAB/wHxA2sBiwGRAYsEigFJAUoBkQHxBv8BKAMpASgF/wQAAUMBmgF6AVIBkQG1ARIB8QYAAfcBBwG8 + AgcC7wMHAfAB8gEHAQAB/wHxAgYBiwGQAbsFiwJQAWsB8Qb/AVAEKQEoBP8DAAEPAQcBkQJzAZEBtQG0 + AUMB9AQAAf8BvAHvA7wDBwG8AQcB8AHyAfEBAAH/AfECBgGLAZABuwSQAWwBUAFRAWsB8Qf/BVABcwP/ + AQABFAFSAfMCvAEHAfcBkQK0AQ4B9AQAAf8BvAHvA/ACvALwAbwB8QHyAfABAAH/AfECiwGQAbMBuwEp + AbMBcQNQAZABawHxAv8BcwUoBVABcwL/AgABGgH0AfcBEgFDAREBuwGRAXoB7AH/AvEB8wEAAf8B7wH3 + AbwG8AG8AvEBvAEAAf8B8gOQAbMBuwKzA1EBKQGzAWsB8gH/DVABmQH/AgABDgHvARMB6gEVAQ8BkQEP + AQ4F8QEAAf8B7QG8CfIB8AEHAQAB/wHyBLMBugGLASkEeAFRAWsB8gH/DlAB/wQAAW0BEQFDARABFQHx + Av8B9AL/AgAB/wH3AfEI8gHxAfABvAEAAf8B8gSzAboBuwEoAngBUAF4ASgBawHyAf8BKQxQASgB/wQA + AbsBtAGRAW0JAAH/AZIB8QHyBvMB8gLxAfABAAH/AfMEswG5AboB3AG0AUkCUAFJAWsB8wL/AVAFeAFX + BFEBKQL/AwAB8wEAAZEBEwsAAZIB8QHyAvMBBwGSAvMB8gHxAfIB8wEAAf8B8wa5AboB2wPcAdsBawHz + B/8BKARXAVAD/wMAAfABDgHsARULAAHtAfEB8gLzAe0B7wLzAfIB8QHyAf8BAAH/AfMM2gGLAfMG/wGZ + BHgBVwT/AQAB8QEAAQcB7QHsAa4B8goAAQcC8gLzAbwD8wHyAfEB8gIAAf8P8wb/ASkEeAX/AQABbgF5 + AbwB9wETAfAMAALyBvMB8gHxAfQCAAH/CPMB8gH3AfIB9wHyAfcB8wb/ARwDeAb/Ae0B6wG8Au8OAALx + BvIC8QMAAf8B8w3/Ae8H/wGZAVAI/wETAfAB6hwAIP8CAAHzAfcB/xsAAUIBTQE+BwABPgMAASgDAAFA + AwABMAMAAQEBAAEBBQABgAEBFgAD/4EABP8B+AEPAeABAAGfAf0C/wHwAQcB4AEAAY8B+AHfAf0B4AED + AgABjwHwAY8B8AHAAQECAAEDAeABBwHwAYABAQUAAoABAQIAAYABAAGAAQABgAEBAgABgAEAAYABAAGA + AQEB4AEAAYABAAGAAQABgAEBAeABAAGAAQEBgAEAAYABAQHgAQABwAEBAcABAQGAAQEB4AEAAcABAQHD + AeEBwAEDAeABAAHgAQMB5wHjAeABBwHgAQAE/wHwAQ8B4AEBBv8B4AEDBv8B4AEHBAAB/AE/Av8EAAH4 + AQ8B4AEDBAAB8AEPAcABAQQAAcABBwGAAQEEAAGAAQcBgAEBBAABgAEAAYABAQQAAcABAAGAAQEEAAHg + AQEBgAEBBAAB4AH/AYABAQQAAeAB/wHAAQEEAAHhAf8BwAEBBAABgAH/AcABAwQAAYEB/wHgAQMEAAED + Af8B4AEHBAABBwP/BAABxwP/Cw== diff --git a/Joystick/DeviceDeadzoneParameter.cs b/Joystick/DeviceDeadzoneParameter.cs index 471ba64..049f4e2 100644 --- a/Joystick/DeviceDeadzoneParameter.cs +++ b/Joystick/DeviceDeadzoneParameter.cs @@ -10,13 +10,10 @@ namespace SCJMapper_V2 private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType ); - private String m_actionCommand = ""; // v_pitch - js1_x .. private String m_cmdCtrl = ""; // x, y, rotz ... private String m_type = ""; // joystick OR xboxpad private int m_devInstanceNo = -1; // jsN - instance in XML - String m_option = ""; // the option name (level where it applies) - private String m_deviceName = ""; private bool m_deadzoneEnabled = false; // default @@ -33,18 +30,20 @@ namespace SCJMapper_V2 public DeviceCls GameDevice { get { return m_device; } - set { m_device = value; + set + { + m_device = value; m_type = ""; m_devInstanceNo = -1; if ( JoystickCls.IsDeviceClass( m_device.DevClass ) ) { m_type = m_device.DevClass; - m_devInstanceNo = (m_device as JoystickCls).JSAssignment; + m_devInstanceNo = ( m_device as JoystickCls ).JSAssignment; } else if ( GamepadCls.IsDeviceClass( m_device.DevClass ) ) { m_type = m_device.DevClass; m_devInstanceNo = 1; // supports ONE gamepad } - } + } } @@ -104,6 +103,6 @@ namespace SCJMapper_V2 } - + } } diff --git a/Joystick/DeviceTuningParameter.cs b/Joystick/DeviceTuningParameter.cs index 86799f0..015ca97 100644 --- a/Joystick/DeviceTuningParameter.cs +++ b/Joystick/DeviceTuningParameter.cs @@ -13,10 +13,10 @@ namespace SCJMapper_V2 { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType ); - private String m_actionCommand = ""; // v_pitch - js1_x .. - private String m_cmdCtrl = ""; // x, y, rotz ... - private String m_type = ""; // joystick OR xboxpad - private int m_devInstanceNo = -1; // jsN - instance in XML + private String m_action = ""; // v_pitch + private String m_cmdCtrl = ""; // js1_x, js1_y, js1_rotz ... + private String m_type = ""; // joystick OR xboxpad + private int m_devInstanceNo = -1; // jsN - instance in XML String m_option = ""; // the option name (level where it applies) @@ -38,7 +38,7 @@ namespace SCJMapper_V2 private DeviceDeadzoneParameter m_deadzone = null; - public DeviceTuningParameter( ) + public DeviceTuningParameter( ) { } @@ -47,18 +47,20 @@ namespace SCJMapper_V2 public DeviceCls GameDevice { get { return m_device; } - set { m_device = value; + set + { + m_device = value; m_type = ""; m_devInstanceNo = -1; if ( JoystickCls.IsDeviceClass( m_device.DevClass ) ) { m_type = m_device.DevClass; - m_devInstanceNo = (m_device as JoystickCls).JSAssignment; + m_devInstanceNo = ( m_device as JoystickCls ).JSAssignment; } else if ( GamepadCls.IsDeviceClass( m_device.DevClass ) ) { m_type = m_device.DevClass; m_devInstanceNo = 1; // supports ONE gamepad } - } + } } @@ -73,10 +75,10 @@ namespace SCJMapper_V2 set { m_deviceName = value; } } - public String ActionCommand + public String Action { - get { return m_actionCommand; } - set { m_actionCommand = value; DecomposeCommand( ); } + get { return m_action; } + set { m_action = value; DecomposeCommand( ); } } public String CommandCtrl @@ -150,8 +152,8 @@ namespace SCJMapper_V2 { // populate from input // something like "v_pitch - js1_x" OR "v_pitch - xi_thumbl" OR "v_pitch - ximod+xi_thumbl+xi_mod" - String cmd = ActionTreeNode.CommandFromNodeText( ActionCommand ); - String action = ActionTreeNode.ActionFromNodeText( ActionCommand ); + String cmd = ActionTree.CommandFromNodeText( Action ); + String action = ActionTreeNode.ActionFromNodeText( Action ); m_cmdCtrl = ""; if ( !String.IsNullOrWhiteSpace( cmd ) ) { // decomp gamepad entries - could have modifiers so check for contains... diff --git a/MySounds.cs b/MySounds.cs new file mode 100644 index 0000000..bc287a7 --- /dev/null +++ b/MySounds.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Media; + +namespace SCJMapper_V2 +{ + public class MySounds + { + + /* + SystemSounds.Asterisk, + SystemSounds.Beep, + SystemSounds.Exclamation, + SystemSounds.Hand, + SystemSounds.Question + * + * Asterisk - the sound that is played when a popup alert is displayed, like a warning message. + Default Beep - this sound is played for multiple reasons, depending on what you do. For example, it will play if you try to select a parent window before closing the active one. + Exclamation - the sound that is played when you try to do something that is not supported by Windows. + */ + + public static void PlayNotfound( ) + { + SystemSounds.Beep.Play( ); + } + + public static void PlayCannot( ) + { + SystemSounds.Beep.Play( ); + } + + public static void PlayError( ) + { + SystemSounds.Exclamation.Play( ); + } + + + + + } +} diff --git a/OGL/FormJSCalCurve.cs b/OGL/FormJSCalCurve.cs index c937f9e..ddec1c4 100644 --- a/OGL/FormJSCalCurve.cs +++ b/OGL/FormJSCalCurve.cs @@ -145,7 +145,6 @@ namespace SCJMapper_V2 private float m_liveYsense = 1.0f; private float m_liveYexponent = 1.0f; private xyPoints m_liveYnonLinCurve = new xyPoints( 1000 ); // max val of Joystick Input - private bool m_YcmdInvert = false; // inverted by command (not as Tuning) /// /// Submit the tuning parameters @@ -163,8 +162,7 @@ namespace SCJMapper_V2 m_Ytuning = value; m_Ydev = m_Ytuning.GameDevice; // populate from input - lblYCmd.Text = m_Ytuning.ActionCommand; - m_YcmdInvert = ActionTreeNode.CommandInvertFromNodeText( m_Ytuning.ActionCommand ); + lblYCmd.Text = m_Ytuning.Action; m_liveYawCommand = m_Ytuning.CommandCtrl; log.Info( "FormJSCalCurve : Yaw Command is: " + value ); @@ -223,7 +221,6 @@ namespace SCJMapper_V2 private float m_livePsense = 1.0f; private float m_livePexponent = 1.0f; private xyPoints m_livePnonLinCurve = new xyPoints( 1000 ); // max val of Joystick Input - private bool m_PcmdInvert = false; // inverted by command (not as Tuning) /// /// Submit the tuning parameters @@ -242,9 +239,7 @@ namespace SCJMapper_V2 m_Ptuning = value; m_Pdev = m_Ptuning.GameDevice; // populate from input - lblPCmd.Text = m_Ptuning.ActionCommand; // - m_PcmdInvert = ActionTreeNode.CommandInvertFromNodeText( m_Ptuning.ActionCommand ); - if ( GamepadCls.IsDeviceClass( m_Pdev.DevClass ) ) m_PcmdInvert = !m_PcmdInvert; // Gamepad Pitch Movement is inverted by default in AC + lblPCmd.Text = m_Ptuning.Action; // m_livePitchCommand = m_Ptuning.CommandCtrl; log.Info( "FormJSCalCurve : Pitch Command is: " + value ); @@ -303,7 +298,6 @@ namespace SCJMapper_V2 private float m_liveRsense = 1.0f; private float m_liveRexponent = 1.0f; private xyPoints m_liveRnonLinCurve = new xyPoints( 1000 ); // max val of Joystick Input - private bool m_RcmdInvert = false; // inverted by command (not as Tuning) /// /// Submit the tuning parameters @@ -322,8 +316,7 @@ namespace SCJMapper_V2 m_Rtuning = value; m_Rdev = m_Rtuning.GameDevice; // populate from input - lblRCmd.Text = m_Rtuning.ActionCommand; // - m_RcmdInvert = ActionTreeNode.CommandInvertFromNodeText( m_Rtuning.ActionCommand ); + lblRCmd.Text = m_Rtuning.Action; // m_liveRollCommand = m_Rtuning.CommandCtrl; log.Info( "FormJSCalCurve : Roll Command is: " + value ); @@ -725,7 +718,7 @@ namespace SCJMapper_V2 // update in/out labels if active axis lblYInput.Text = ( i_x / 1000.0 ).ToString( "0.00" ); lblYOutput.Text = ( fout ).ToString( "0.00" ); // calculate new direction vector - m.X = ( ( m_YcmdInvert ) ? -1 : 1 ) * ( ( m_Ytuning.InvertUsed ) ? -1 : 1 ) * ( ( !cbYuse.Checked ) ? fout : 0 ) * m_msElapsed * DegPerMS; + m.X = ( ( m_Ytuning.InvertUsed ) ? -1 : 1 ) * ( ( !cbYuse.Checked ) ? fout : 0 ) * m_msElapsed * DegPerMS; } // Pitch @@ -746,7 +739,7 @@ namespace SCJMapper_V2 } fout = ( fout > 1.0 ) ? 1.0 : fout; lblPInput.Text = ( i_y / 1000.0 ).ToString( "0.00" ); lblPOutput.Text = ( fout ).ToString( "0.00" ); - m.Y = ( ( m_PcmdInvert ) ? -1 : 1 ) * ( ( m_Ptuning.InvertUsed ) ? -1 : 1 ) * ( ( !cbPuse.Checked ) ? -fout : 0 ) * m_msElapsed * DegPerMS; + m.Y = ( ( m_Ptuning.InvertUsed ) ? -1 : 1 ) * ( ( !cbPuse.Checked ) ? -fout : 0 ) * m_msElapsed * DegPerMS; } // Roll @@ -767,7 +760,7 @@ namespace SCJMapper_V2 } fout = ( fout > 1.0 ) ? 1.0 : fout; lblRInput.Text = ( i_z / 1000.0 ).ToString( "0.00" ); lblROutput.Text = ( fout ).ToString( "0.00" ); - m.Z = ( ( m_RcmdInvert ) ? -1 : 1 ) * ( ( m_Rtuning.InvertUsed ) ? -1 : 1 ) * ( ( !cbRuse.Checked ) ? fout : 0 ) * m_msElapsed * DegPerMS; + m.Z = ( ( m_Rtuning.InvertUsed ) ? -1 : 1 ) * ( ( !cbRuse.Checked ) ? fout : 0 ) * m_msElapsed * DegPerMS; } // finalize diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 772d714..a0438ad 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion( "2.8.0.37" )] -[assembly: AssemblyFileVersion( "2.8.0.37" )] +[assembly: AssemblyVersion( "2.10.0.40" )] +[assembly: AssemblyFileVersion( "2.10.0.40" )] diff --git a/SCJMapper-V2.csproj b/SCJMapper-V2.csproj index 734bb74..4f2fd49 100644 --- a/SCJMapper-V2.csproj +++ b/SCJMapper-V2.csproj @@ -26,8 +26,8 @@ false false true - 37 - 2.8.0.%2a + 40 + 2.10.0.%2a false true @@ -116,6 +116,8 @@ + + @@ -124,6 +126,7 @@ + diff --git a/actions/ActionCls.cs b/actions/ActionCls.cs index 27204cb..8853d84 100644 --- a/actions/ActionCls.cs +++ b/actions/ActionCls.cs @@ -21,7 +21,7 @@ namespace SCJMapper_V2 /// /// /// - class ActionCls + public class ActionCls { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType ); @@ -34,6 +34,8 @@ namespace SCJMapper_V2 AD_Keyboard, } + #region Static Items + static public ActionDevice ADevice( String device ) { switch ( device.ToLower( ) ) { @@ -79,28 +81,29 @@ namespace SCJMapper_V2 } } + #endregion + // Class items public String key { get; set; } // the key is the "Daction" formatted item (as we can have the same name multiple times) - public String name { get; set; } - public String device { get; set; } - public String input { get; set; } - public String defBinding { get; set; } // the default binding - public Boolean inverted { get; set; } - public ActionDevice actionDevice { get; set; } + public String name { get; set; } // the plain action name e.g. v_yaw + public ActionDevice actionDevice { get; set; } // the enum of the device + public String device { get; set; } // name of the device (uses DeviceClass) + public String defBinding { get; set; } // the default binding + public List inputList { get; set; } + /// /// ctor /// public ActionCls( ) { - device = JoystickCls.DeviceClass; key = ""; + actionDevice = ActionDevice.AD_Unknown; + device = JoystickCls.DeviceClass; name = ""; - input = ""; defBinding = ""; - inverted = false; - actionDevice = ActionDevice.AD_Unknown; + inputList = new List( ); // empty list } @@ -114,24 +117,39 @@ namespace SCJMapper_V2 ActionCls newAc = new ActionCls( ); // full copy from 'this' newAc.key = this.key; - newAc.name = this.name; + newAc.actionDevice = this.actionDevice; newAc.device = this.device; + newAc.name = this.name; newAc.defBinding = this.defBinding; - newAc.input = this.input; - newAc.inverted = this.inverted; - - // reassign the jsX part for Joystick commands - if ( JoystickCls.IsDeviceClass( this.device ) && JoystickCls.IsDeviceClass( newAc.device ) ) { - int oldJsN = JoystickCls.JSNum( this.input ); - if ( JoystickCls.IsJSValid( oldJsN ) ) { - if ( newJsList.ContainsKey( oldJsN ) ) newAc.input = JoystickCls.ReassignJSTag( this.input, newJsList[oldJsN] ); - } + + foreach ( ActionCommandCls acc in inputList ) { + newAc.inputList.Add( acc.ReassignJsN( newJsList ) ); } return newAc; } + public ActionCommandCls AddCommand( String input, int index ) + { + ActionCommandCls acc = new ActionCommandCls( ); + acc.input = input; acc.nodeIndex = index; + inputList.Add( acc ); + return acc; + } + + public void DelCommand( int index ) + { + int removeIt = -1; + + for ( int i = 0; i < inputList.Count; i++ ) { + if ( inputList[i].nodeIndex == index ) removeIt = i; + if ( inputList[i].nodeIndex > index ) inputList[i].nodeIndex -= 1; // reorder trailing ones + } + if ( removeIt >= 0 ) inputList.RemoveAt( removeIt ); + } + + /// /// Merge action is simply copying the new input control @@ -139,8 +157,10 @@ namespace SCJMapper_V2 /// public void Merge( ActionCls newAc ) { - input = newAc.input; - inverted = newAc.inverted; + this.inputList.Clear( ); + foreach ( ActionCommandCls acc in newAc.inputList ) { + this.inputList.Add( acc ); + } } /// @@ -149,12 +169,21 @@ namespace SCJMapper_V2 /// the action as XML fragment public String toXML( ) { - String r = ""; - if ( !String.IsNullOrEmpty( input ) ) { - if ( inverted ) r = String.Format( "\t\n\t\t\t\n\t\t\n", name, device, input ); - else r = String.Format( "\t\n\t\t\t\n\t\t\n", name, device, input ); + String r = ""; String + bindCmd = "rebind"; + if ( inputList.Count > 0 ) { + if ( !String.IsNullOrEmpty( inputList[0].input ) ) { + r = String.Format( "\t\n", name ); + foreach ( ActionCommandCls acc in inputList ) { + if ( !String.IsNullOrEmpty( acc.input ) ) { + r += String.Format( "\t\t\t<{0} device=\"{1}\" {2}", bindCmd, device, acc.toXML( ) ); + bindCmd = "addbind"; + } + } + r += String.Format( "\t\t\n" ); + } } - + return r; } @@ -176,34 +205,43 @@ namespace SCJMapper_V2 if ( reader.Name == "action" ) { if ( reader.HasAttributes ) { name = reader["name"]; - // Move the reader back to the element node. - reader.ReadStartElement( "action" ); + reader.ReadStartElement( "action" ); // Checks that the current content node is an element with the given Name and advances the reader to the next node } else { return false; } } - if ( reader.Name == "rebind" ) { - if ( reader.HasAttributes ) { - device = reader["device"]; - input = reader["input"]; - if ( ( input == JoystickCls.BlendedInput ) || ( input == GamepadCls.BlendedInput ) ) input = ""; // don't carry jsx_reserved or xi_reserved into the action - key = DevID( device ) + name; // unique id of the action - actionDevice = ADevice( device ); // get the enum of the input device - String inv = reader["invert"]; - if ( String.IsNullOrWhiteSpace( inv ) ) { - inverted = false; + do { + if ( reader.Name == "rebind" ) { + if ( reader.HasAttributes ) { + device = reader["device"]; + ActionCommandCls acc = new ActionCommandCls( ); + acc.input = reader["input"]; + if ( ( acc.input == JoystickCls.BlendedInput ) || ( acc.input == GamepadCls.BlendedInput ) ) acc.input = ""; // don't carry jsx_reserved or xi_reserved into the action + key = DevID( device ) + name; // unique id of the action + actionDevice = ADevice( device ); // get the enum of the input device + inputList.Add( acc ); + // advances the reader to the next node + reader.ReadStartElement( "rebind" ); } - else { - inverted = ( inv == "1" ) ? true : false; + } + else if ( reader.Name == "addbind" ) { + if ( reader.HasAttributes ) { + device = reader["device"]; + ActionCommandCls acc = new ActionCommandCls( ); + acc.input = reader["input"]; + if ( ( acc.input == JoystickCls.BlendedInput ) || ( acc.input == GamepadCls.BlendedInput ) ) acc.input = ""; // don't carry jsx_reserved or xi_reserved into the action + key = DevID( device ) + name; // unique id of the action + actionDevice = ADevice( device ); // get the enum of the input device + inputList.Add( acc ); + // advances the reader to the next node + reader.ReadStartElement( "addbind" ); } - // Move the reader back to the element node. - reader.ReadStartElement( "rebind" ); } - } - else { - return false; - } + else { + return false; + } + } while ( reader.Name == "addbind" ); return true; } diff --git a/actions/ActionCommandCls.cs b/actions/ActionCommandCls.cs new file mode 100644 index 0000000..e02b638 --- /dev/null +++ b/actions/ActionCommandCls.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SCJMapper_V2 +{ + public class ActionCommandCls + { + + public String input { get; set; } // input command name e.g. js1_x + + /// + /// The index of the visible child node (-1 -> shown in ActionNode) + /// + public int nodeIndex { get; set; } // index of the vis treenode + + + // ctor + public ActionCommandCls( ) + { + input = "UNDEF"; + nodeIndex = -1; + } + + + /// + /// Copy return the action while reassigning the JsN Tag + /// + /// The JsN reassign list + /// The action copy with reassigned input + public ActionCommandCls ReassignJsN( Dictionary newJsList ) + { + ActionCommandCls newAc = new ActionCommandCls( ); + // full copy from 'this' + newAc.input = this.input; + + // reassign the jsX part for Joystick commands (silly but rather fast comparison) + if ( this.input.Contains( "js1_" ) || this.input.Contains( "js2_" ) || this.input.Contains( "js3_" ) || this.input.Contains( "js4_" ) ) { + int oldJsN = JoystickCls.JSNum( this.input ); + if ( JoystickCls.IsJSValid( oldJsN ) ) { + if ( newJsList.ContainsKey( oldJsN ) ) newAc.input = JoystickCls.ReassignJSTag( this.input, newJsList[oldJsN] ); + } + } + + return newAc; + } + + + /// + /// Dump the action as partial XML nicely formatted + /// + /// the action as XML fragment + public String toXML( ) + { + String r = ""; + if ( !String.IsNullOrEmpty( input ) ) { + r = String.Format( "input=\"{0}\" />\n", input ); + } + else { + r = String.Format( " />\n" ); // actually an ERROR... + } + + return r; + } + + } +} diff --git a/actions/ActionMapsCls.cs b/actions/ActionMapsCls.cs index 9134992..d96291a 100644 --- a/actions/ActionMapsCls.cs +++ b/actions/ActionMapsCls.cs @@ -35,6 +35,8 @@ namespace SCJMapper_V2 #endregion Static Part of ActionMaps + private const String ACM_VERSION = "1"; // the default version + private String version { get; set; } private String ignoreversion { get; set; } @@ -125,7 +127,7 @@ namespace SCJMapper_V2 /// public ActionMapsCls( JoystickList jsList ) { - version = "0"; + version = ACM_VERSION; m_joystickList = jsList; // have to save this for Reassign // create the Joystick assignments @@ -216,7 +218,7 @@ namespace SCJMapper_V2 r += String.Format( "version=\"{0}\" \n", version ); } else { - version = "0"; // preset if missing + version = ACM_VERSION; // preset if missing r += String.Format( "version=\"{0}\" \n", version ); } @@ -270,6 +272,8 @@ namespace SCJMapper_V2 if ( reader.Name == "ActionMaps" ) { if ( reader.HasAttributes ) { version = reader["version"]; + if ( version == "0" ) version = ACM_VERSION; // update from legacy to actual version + ignoreversion = reader["ignoreVersion"]; // could be either / or .. // get the joystick mapping if there is one diff --git a/actions/ActionTree.cs b/actions/ActionTree.cs index 409aab6..63814b0 100644 --- a/actions/ActionTree.cs +++ b/actions/ActionTree.cs @@ -97,6 +97,82 @@ namespace SCJMapper_V2 } + public Boolean CanBlendBinding + { + get + { + if ( Ctrl.SelectedNode == null ) return false; + else return ( Ctrl.SelectedNode.Level == 1 ); + } + } + + public Boolean CanClearBinding + { + get + { + if ( Ctrl.SelectedNode == null ) return false; + else return ( Ctrl.SelectedNode.Level == 1 ); + } + } + + public Boolean CanAddBinding + { + get + { + if ( Ctrl.SelectedNode == null ) return false; + else return ( Ctrl.SelectedNode.Level == 1 ); + } + } + + public Boolean CanDelBinding + { + get { + if ( Ctrl.SelectedNode == null ) return false; + else return ( Ctrl.SelectedNode.Level == 2 ); + } + } + + + /// + /// Add a new Action Child to the selected Node to apply an addtional mapping + /// + public void AddBinding( ) + { + if ( Ctrl.SelectedNode == null ) return; + if ( Ctrl.SelectedNode.Level != 1 ) return; // can only add to level 1 nodes + + ActionTreeNode matn = FindMasterAction( ( ActionTreeNode )Ctrl.SelectedNode ); + ActionCls ac = FindActionObject( matn.Parent.Name, matn.Name ); // the related action + // make new items + ActionTreeInputNode matin = new ActionTreeInputNode( "UNDEF" ); matin.ImageKey = "Add"; + matn.Nodes.Add( matin ); // add to master tree + ActionCommandCls acc = ac.AddCommand( "", matin.Index ); + // show stuff + FilterTree( ); + FindAndSelectCtrlByName( matn.Name ); + } + + + /// + /// Delete the selected ActionChild and remove corresponding ActionCommands + /// + public void DelBinding( ) + { + if ( Ctrl.SelectedNode == null ) return; + if ( Ctrl.SelectedNode.Level != 2 ) return; // can only delete level 2 nodes + + ActionTreeNode matn = FindMasterAction( ( ActionTreeNode )Ctrl.SelectedNode.Parent ); // the parent treenode + ActionCls ac = FindActionObject( matn.Parent.Name, matn.Name ); // the related action + // delete items + ac.DelCommand( m_ctrl.SelectedNode.Index ); + matn.Nodes.RemoveAt( m_ctrl.SelectedNode.Index ); + Dirty = true; + // show stuff + FilterTree( ); + FindAndSelectCtrlByName( matn.Name ); + } + + private void UpdateMasterNode( ActionTreeNode node ) { // copy to master node @@ -105,12 +181,47 @@ namespace SCJMapper_V2 // could return more than one if the action is the same in different actionmaps foreach ( ActionTreeNode mtn in masterNode ) { if ( mtn.Parent.Name == node.Parent.Name ) { - mtn.Command = node.Command; mtn.InvertCommand = node.InvertCommand; mtn.BackColor = node.BackColor; + mtn.Command = node.Command; mtn.BackColor = node.BackColor; + } + } + } + + private void UpdateMasterNode( ActionTreeInputNode node ) + { + // copy to master node + TreeNode[] masterNode = m_MasterTree.Nodes.Find( node.Name, true ); // find the same node in master + if ( masterNode.Length == 0 ) throw new IndexOutOfRangeException( "ActionTree ERROR - cannot find synched node in master" ); // OUT OF SYNC + // could return more than one if the action is the same in different actionmaps + foreach ( ActionTreeInputNode mtn in masterNode ) { + if ( mtn.Parent.Name == node.Parent.Name ) { + mtn.Command = node.Command; mtn.BackColor = node.BackColor; } } } + /// + /// Find the master element for the given ActionNode + /// + /// The ActionNode to find + /// The sought node or null + private ActionTreeNode FindMasterAction( ActionTreeNode atn ) + { + if ( atn.Level != 1 ) return null; // sanity + + TreeNode[] masterNode = m_MasterTree.Nodes.Find( atn.Name, true ); // find the same node in master + if ( masterNode.Length == 0 ) throw new IndexOutOfRangeException( "ActionTree ERROR - cannot find synched node in master" ); // OUT OF SYNC + // could return more than one if the action is the same in different actionmaps + foreach ( ActionTreeNode mtn in masterNode ) { + if ( mtn.Parent.Name == atn.Parent.Name ) { + return mtn; + } + } + return null; + } + + + /// /// Apply the filter to the GUI TreeView /// @@ -136,12 +247,15 @@ namespace SCJMapper_V2 } else { ActionTreeNode tnAction = new ActionTreeNode( stn ); tnMap.Nodes.Add( tnAction ); // copy level 1 nodes + foreach ( ActionTreeInputNode istn in stn.Nodes ) { + ActionTreeInputNode tnActionInput = new ActionTreeInputNode( istn ); tnAction.Nodes.Add( tnActionInput ); // copy level 2 nodes + } allHidden = false; } } // make it tidier.. if ( allHidden ) tnMap.Collapse( ); - else tnMap.Expand( ); + else tnMap.ExpandAll( ); } if ( topNode != null ) Ctrl.TopNode = topNode; // set view to topnode @@ -187,17 +301,18 @@ namespace SCJMapper_V2 /// /// The name of the profile to load (w/o extension) /// True if default mappings should be carried on - public void LoadTree( String defaultProfileName, Boolean applyDefaults ) + public void LoadProfileTree( String defaultProfileName, Boolean applyDefaults ) { - log.Debug( "LoadTree - Entry" ); + log.Debug( "LoadProfileTree - Entry" ); ActionTreeNode tn = null; ActionTreeNode[] cnl = { }; ActionTreeNode cn = null; ActionTreeNode topNode = null; - ActionCls ac = null; ActionMapCls acm = null; + ActionCls ac = null; + ActionCommandCls acc = null; ActionMaps = new ActionMapsCls( m_jsList ); m_MasterTree.Nodes.Clear( ); @@ -212,6 +327,9 @@ namespace SCJMapper_V2 txReader = new StringReader( dpReader.CSVMap ); } + // we assume no addbind items in the profile + // so all actions are shown in the ActionTreeNode and no ActionTreeNode childs must be created here + // however we create the ActionCommand for each entry that is supported - even if it is not mapped (input= "") using ( TextReader sr = txReader ) { String buf = sr.ReadLine( ); while ( !String.IsNullOrEmpty( buf ) ) { @@ -227,59 +345,64 @@ namespace SCJMapper_V2 // default assignments String action = elem[ei].Substring( 1 ); String defBinding = elem[ei + 1].Substring( 0 ); - cn = new ActionTreeNode( "UNDEF" ); cn.Name = elem[ei]; cn.Action = action; cn.BackColor = Color.White; // name with the key it to find it.. String devID = elem[ei].Substring( 0, 1 ); String device = ActionCls.DeviceFromID( devID ); - cn.ImageKey = devID; - cn.BackColor = Color.White; // some stuff does not work properly... + // visual item for the action + cn = new ActionTreeNode( "UNDEF" ); cn.Name = elem[ei]; cn.Action = action; cn.BackColor = Color.White; // name with the key it to find it.. + cn.ImageKey = devID; cn.BackColor = Color.White; // some stuff does not work properly... Array.Resize( ref cnl, cnl.Length + 1 ); cnl[cnl.Length - 1] = cn; + + // derive content tree ac = new ActionCls( ); ac.key = cn.Name; ac.name = action; ac.device = device; ac.actionDevice = ActionCls.ADevice( device ); ac.defBinding = defBinding; - cn.ActionDevice = ac.actionDevice; // should be known now acm.Add( ac ); // add to our map + cn.ActionDevice = ac.actionDevice; // should be known now + // create just an unmapped ActionCommand item + acc = new ActionCommandCls( ); acc.input = ""; acc.nodeIndex = -1; // profile items are shown in the ActionTreeNode (not in a child) + ac.inputList.Add( acc );// add to our Action + // modify defaults and blendings if ( applyDefaults ) { // apply the default mappings - if ( JoystickCls.IsDeviceClass( ac.device ) ) { + if ( ac.actionDevice == ActionCls.ActionDevice.AD_Joystick ) { int jNum = JoystickCls.JSNum( ac.defBinding ); if ( JoystickCls.IsJSValid( jNum ) ) { - ac.input = ac.defBinding; + acc.input = ac.defBinding; cn.Command = ac.defBinding; cn.BackColor = JoystickCls.JsNColor( jNum ); } else if ( BlendUnmappedJS ) { // jsx_reserved gets here - ac.input = JoystickCls.BlendedInput; + acc.input = JoystickCls.BlendedInput; cn.Command = JoystickCls.BlendedInput; cn.BackColor = MyColors.BlendedColor; } } - else if ( GamepadCls.IsDeviceClass( ac.device ) ) { + else if ( ac.actionDevice == ActionCls.ActionDevice.AD_Gamepad ) { if ( GamepadCls.IsXiValid( ac.defBinding ) ) { - ac.input = ac.defBinding; + acc.input = ac.defBinding; cn.Command = ac.defBinding; cn.BackColor = GamepadCls.XiColor( ); } else if ( BlendUnmappedGP ) { // xi_reserved gets here - ac.input = GamepadCls.BlendedInput; + acc.input = GamepadCls.BlendedInput; cn.Command = GamepadCls.BlendedInput; cn.BackColor = MyColors.BlendedColor; } } - else if ( KeyboardCls.IsDeviceClass( ac.device ) ) { + else if ( ac.actionDevice == ActionCls.ActionDevice.AD_Keyboard ) { if ( !String.IsNullOrEmpty( ac.defBinding ) ) { - ac.input = ac.defBinding; + acc.input = ac.defBinding; cn.Command = ac.defBinding; cn.BackColor = KeyboardCls.KbdColor( ); } } } // Don't apply defaults - but blend if checked else { - // init empty - if ( JoystickCls.IsDeviceClass( ac.device ) && BlendUnmappedJS ) { - ac.input = JoystickCls.BlendedInput; + if ( ( ac.actionDevice == ActionCls.ActionDevice.AD_Joystick ) && BlendUnmappedJS ) { cn.Command = JoystickCls.BlendedInput; cn.BackColor = MyColors.BlendedColor; + acc.input = JoystickCls.BlendedInput; } - else if ( GamepadCls.IsDeviceClass( ac.device ) && BlendUnmappedGP ) { - ac.input = GamepadCls.BlendedInput; + else if ( ( ac.actionDevice == ActionCls.ActionDevice.AD_Gamepad ) && BlendUnmappedGP ) { cn.Command = GamepadCls.BlendedInput; cn.BackColor = MyColors.BlendedColor; + acc.input = GamepadCls.BlendedInput; } } } @@ -319,15 +442,105 @@ namespace SCJMapper_V2 /// First apply to the GUI tree where the selection happend then copy it over to master tree /// /// The new Text property - public void UpdateSelectedItem( String input, Boolean invert, DeviceCls.InputKind inKind ) + public Boolean UpdateSelectedItem( String input, ActionCls.ActionDevice inKind ) { - if ( Ctrl.SelectedNode == null ) return; + if ( ( Ctrl.SelectedNode.Level == 0 ) || ( Ctrl.SelectedNode.Level > 2 ) ) return false; // ERROR exit + if ( Ctrl.SelectedNode == null ) return false; // ERROR exit + if ( Ctrl.SelectedNode.Parent == null ) return false; // ERROR exit + + // has a parent - must be level 1 or 2 + if ( Ctrl.SelectedNode.Level == 1 ) { + // this is the main node with Action Cmd + ActionCls ac = FindActionObject( Ctrl.SelectedNode.Parent.Name, Ctrl.SelectedNode.Name ); + if ( ac == null ) return false; // ERROR exit + if ( ac.actionDevice != inKind ) return false; // ERROR exit + + ActionCommandCls acc = FindActionInputObject( Ctrl.SelectedNode.Parent.Name, Ctrl.SelectedNode.Name, CommandFromNodeText( Ctrl.SelectedNode.Text ) ); + if ( acc == null ) return false; // ERROR exit + UpdateActionCommandFromInput( input, acc, inKind ); + UpdateNodeFromAction( ( ActionTreeNode )Ctrl.SelectedNode, acc, inKind ); + } + else if ( Ctrl.SelectedNode.Level == 2 ) { + // this is a child of an action with further commands + ActionTreeNode atn = ( m_ctrl.SelectedNode.Parent as ActionTreeNode ); // the parent treenode + ActionCls ac = FindActionObject( atn.Parent.Name, atn.Name ); // the related action + if ( ac == null ) return false; // ERROR exit + if ( ac.actionDevice != inKind ) return false; // ERROR exit + + ActionCommandCls acc = FindActionInputObject( ac, m_ctrl.SelectedNode.Index ); + if ( acc == null ) return false; // ERROR exit + UpdateActionCommandFromInput( input, acc, inKind ); + UpdateInputNodeFromAction( ( ActionTreeInputNode )Ctrl.SelectedNode, acc, inKind ); + } + return true; + } - ActionCls ac = FindActionObject( Ctrl.SelectedNode.Parent.Name, Ctrl.SelectedNode.Name ); - UpdateActionFromInput( input, invert, ac ); - UpdateNodeFromAction( ( ActionTreeNode )Ctrl.SelectedNode, ac, inKind ); + /// + /// Find an action with name in a actionmap + /// + /// The actionmap name + /// The action + /// The input + /// An actionCommand or null if not found + private ActionCommandCls FindActionInputObject( String actionMap, String action, String input ) + { + log.Debug( "FindActionInputObject - Entry" ); + // Apply the input to the ActionTree + ActionCls ac = null; ActionCommandCls acc = null; + ActionMapCls ACM = ActionMaps.Find( delegate( ActionMapCls _ACM ) { return _ACM.name == actionMap; } ); + if ( ACM != null ) ac = ACM.Find( delegate( ActionCls _AC ) { return _AC.key == action; } ); + if ( ac != null ) acc = ac.inputList.Find( delegate( ActionCommandCls _ACC ) { return _ACC.input == input; } ); + if ( acc == null ) { + log.Error( "FindActionInputObject - Action Input not found in tree" ); + return null; // ERROR - Action Input not found in tree + } + return acc; } + /// + /// Find an action with name in a actionmap + /// + /// The actionmap name + /// The action + /// The input + /// An actionCommand or null if not found + private ActionCommandCls FindActionInputObject( String actionMap, String action, int index ) + { + log.Debug( "FindActionInputObject - Entry" ); + // Apply the input to the ActionTree + ActionCls ac = null; ActionCommandCls acc = null; + ActionMapCls ACM = ActionMaps.Find( delegate( ActionMapCls _ACM ) { return _ACM.name == actionMap; } ); + if ( ACM != null ) ac = ACM.Find( delegate( ActionCls _AC ) { return _AC.key == action; } ); + if ( ac != null ) acc = ac.inputList.Find( delegate( ActionCommandCls _ACC ) { return _ACC.nodeIndex == index; } ); + if ( acc == null ) { + log.Error( "FindActionInputObject - Action Input not found in tree" ); + return null; // ERROR - Action Input not found in tree + } + return acc; + } + + + /// + /// Find an ActionCommand with index in an Action + /// + /// The actionmap name + /// The action + /// The input + /// An actionCommand or null if not found + private ActionCommandCls FindActionInputObject( ActionCls ac, int index ) + { + log.Debug( "FindActionInputObject - Entry" ); + // Apply the input to the ActionTree + ActionCommandCls acc = null; + if ( ac != null ) acc = ac.inputList.Find( delegate( ActionCommandCls _ACC ) { return _ACC.nodeIndex == index; } ); + if ( acc == null ) { + log.Error( "FindActionInputObject - Action Input not found in Action" ); + return null; // ERROR - Action Input not found in tree + } + return acc; + } + + /// /// Find an action with name in a actionmap /// @@ -350,28 +563,26 @@ namespace SCJMapper_V2 /// - /// Updates an action with a new input (command) + /// Updates an actionCommand with a new input (command) /// /// The input command - /// The action to update - /// The input device - private void UpdateActionFromInput( String input, Boolean invert, ActionCls action ) + /// The action to containing the command + /// The actionCommand to update + private void UpdateActionCommandFromInput( String input, ActionCommandCls actionCmd, ActionCls.ActionDevice inKind ) { - log.Debug( "UpdateActionFromInput - Entry" ); - if ( action == null ) return; + log.Debug( "UpdateActionCommandFromInput - Entry" ); + if ( actionCmd == null ) return; // Apply the input to the ActionTree if ( String.IsNullOrEmpty( input ) ) { // unmapped - handle the blended ones from setting - action.inverted = false; // reset in any case - if ( JoystickCls.IsDeviceClass( action.device ) && BlendUnmappedJS ) action.input = JoystickCls.BlendedInput; - else if ( GamepadCls.IsDeviceClass( action.device ) && BlendUnmappedGP ) action.input = GamepadCls.BlendedInput; - else action.input = ""; + if ( ( inKind == ActionCls.ActionDevice.AD_Joystick ) && BlendUnmappedJS ) actionCmd.input = JoystickCls.BlendedInput; + else if ( ( inKind == ActionCls.ActionDevice.AD_Gamepad ) && BlendUnmappedGP ) actionCmd.input = GamepadCls.BlendedInput; + else actionCmd.input = ""; } else { // mapped ones - action.input = input; - action.inverted = invert; + actionCmd.input = input; } Dirty = true; } @@ -384,43 +595,42 @@ namespace SCJMapper_V2 /// The TreeNode to update /// The action that carries the update /// The input device - private void UpdateNodeFromAction( ActionTreeNode node, ActionCls action, DeviceCls.InputKind inKind ) + private void UpdateNodeFromAction( ActionTreeNode node, ActionCommandCls actionCmd, ActionCls.ActionDevice inKind ) { log.Debug( "UpdateNodeFromAction - Entry" ); - if ( action == null ) return; + if ( actionCmd == null ) return; - // applies only to ActionNodes + // applies only to ActionTreeNode if ( node.Level == 1 ) { // input is either "" or a valid mapping or a blended mapping - if ( String.IsNullOrEmpty( action.input ) ) { + if ( String.IsNullOrEmpty( actionCmd.input ) ) { // new unmapped - node.Command = ""; node.InvertCommand = false; node.BackColor = MyColors.UnassignedColor; + node.Command = ""; node.BackColor = MyColors.UnassignedColor; } // blended mapped ones - can only get a Blend Background - else if ( JoystickCls.IsDeviceClass( action.device ) && ( action.input == JoystickCls.BlendedInput ) ) { - node.Command = action.input; node.InvertCommand = false; node.BackColor = MyColors.BlendedColor; + else if ( ( inKind == ActionCls.ActionDevice.AD_Joystick ) && ( actionCmd.input == JoystickCls.BlendedInput ) ) { + node.Command = actionCmd.input; node.BackColor = MyColors.BlendedColor; } - else if ( GamepadCls.IsDeviceClass( action.device ) && ( action.input == GamepadCls.BlendedInput ) ) { - node.Command = action.input; node.InvertCommand = false; node.BackColor = MyColors.BlendedColor; + else if ( ( inKind == ActionCls.ActionDevice.AD_Gamepad ) && ( actionCmd.input == GamepadCls.BlendedInput ) ) { + node.Command = actionCmd.input; node.BackColor = MyColors.BlendedColor; } - else if ( action.input == DeviceCls.BlendedInput ) { + else if ( actionCmd.input == DeviceCls.BlendedInput ) { // Manually Blended input - node.Command = action.input; node.InvertCommand = false; node.BackColor = MyColors.BlendedColor; + node.Command = actionCmd.input; node.BackColor = MyColors.BlendedColor; } else { // mapped ( regular ones ) - node.Command = action.input; - node.InvertCommand = action.inverted; + node.Command = actionCmd.input; // background is along the input - if ( inKind == DeviceCls.InputKind.Joystick ) { - int jNum = JoystickCls.JSNum( action.input ); + if ( inKind == ActionCls.ActionDevice.AD_Joystick ) { + int jNum = JoystickCls.JSNum( actionCmd.input ); node.BackColor = JoystickCls.JsNColor( jNum ); } - else if ( inKind == DeviceCls.InputKind.Gamepad ) { + else if ( inKind == ActionCls.ActionDevice.AD_Gamepad ) { node.BackColor = GamepadCls.XiColor( ); } - else if ( inKind == DeviceCls.InputKind.Kbd ) { + else if ( inKind == ActionCls.ActionDevice.AD_Keyboard ) { node.BackColor = KeyboardCls.KbdColor( ); } else { @@ -433,6 +643,62 @@ namespace SCJMapper_V2 } } + + /// + /// Apply an update from the action to the treenode + /// First apply to the GUI tree where the selection happend then copy it over to master tree + /// + /// The input command + /// The TreeNode to update + /// The actionCommand that carries the update + /// The input device + private void UpdateInputNodeFromAction( ActionTreeInputNode node, ActionCommandCls actionCmd, ActionCls.ActionDevice inKind ) + { + log.Debug( "UpdateInputNodeFromAction - Entry" ); + if ( actionCmd == null ) return; + if ( node.Level != 2 ) return; // applies only to ActionTreeInputNode + + // input is either "" or a valid mapping or a blended mapping + if ( String.IsNullOrEmpty( actionCmd.input ) ) { + // new unmapped + node.Command = ""; node.BackColor = MyColors.UnassignedColor; + } + + // blended mapped ones - can only get a Blend Background + else if ( ( inKind == ActionCls.ActionDevice.AD_Joystick ) && ( actionCmd.input == JoystickCls.BlendedInput ) ) { + node.Command = actionCmd.input; node.BackColor = MyColors.BlendedColor; + } + else if ( ( inKind == ActionCls.ActionDevice.AD_Gamepad ) && ( actionCmd.input == GamepadCls.BlendedInput ) ) { + node.Command = actionCmd.input; node.BackColor = MyColors.BlendedColor; + } + else if ( actionCmd.input == DeviceCls.BlendedInput ) { + // Manually Blended input + node.Command = actionCmd.input; node.BackColor = MyColors.BlendedColor; + } + else { + // mapped ( regular ones ) + node.Command = actionCmd.input; + + // background is along the input + if ( inKind == ActionCls.ActionDevice.AD_Joystick ) { + int jNum = JoystickCls.JSNum( actionCmd.input ); + node.BackColor = JoystickCls.JsNColor( jNum ); + } + else if ( inKind == ActionCls.ActionDevice.AD_Gamepad ) { + node.BackColor = GamepadCls.XiColor( ); + } + else if ( inKind == ActionCls.ActionDevice.AD_Keyboard ) { + node.BackColor = KeyboardCls.KbdColor( ); + } + else { + // ?? what else + node.BackColor = MyColors.UnassignedColor; + } + } + UpdateMasterNode( node ); + + } + /// /// Defines what to show in the tree /// @@ -440,7 +706,7 @@ namespace SCJMapper_V2 /// True to show Gamepad actions /// True to show Keyboard actions /// True to show mapped actions only - public void DefineShowOptions(Boolean showJoystick, Boolean showGamepad, Boolean showKeyboard, Boolean showMappedOnly) + public void DefineShowOptions( Boolean showJoystick, Boolean showGamepad, Boolean showKeyboard, Boolean showMappedOnly ) { m_showJoy = showJoystick; m_showGameP = showGamepad; @@ -460,37 +726,32 @@ namespace SCJMapper_V2 foreach ( ActionMapCls acm in ActionMaps ) { if ( IgnoreMaps.Contains( "," + acm.name + "," ) ) break; // next try { - ActionTreeNode amTn = ( ActionTreeNode )m_MasterTree.Nodes[acm.name]; // get the map node + ActionTreeNode mtn = ( ActionTreeNode )m_MasterTree.Nodes[acm.name]; // get the map node // find the item to reload into the treeview foreach ( ActionCls ac in acm ) { - try { - ActionTreeNode tnl = ( ActionTreeNode )amTn.Nodes[ac.key]; - UpdateActionFromInput( ac.input, ac.inverted, ac ); // this may apply (un)Blending if needed - // input kind priority first - if ( JoystickCls.IsJsN( ac.input ) ) { - UpdateNodeFromAction( tnl, ac, DeviceCls.InputKind.Joystick ); - } - else if ( GamepadCls.IsXiValid( ac.input ) ) { - UpdateNodeFromAction( tnl, ac, DeviceCls.InputKind.Gamepad ); - } - // device priority second - else if ( JoystickCls.IsDeviceClass( ac.device ) ) { - UpdateNodeFromAction( tnl, ac, DeviceCls.InputKind.Joystick ); - } - else if ( GamepadCls.IsDeviceClass( ac.device ) ) { - UpdateNodeFromAction( tnl, ac, DeviceCls.InputKind.Gamepad ); - } - else if ( KeyboardCls.IsDeviceClass( ac.device ) ) { - UpdateNodeFromAction( tnl, ac, DeviceCls.InputKind.Kbd ); + ActionTreeNode matn = ( ActionTreeNode )mtn.Nodes[ac.key]; // get the action node + Boolean first=true; + // refresh commands + foreach ( ActionCommandCls acc in ac.inputList ) { + try { + UpdateActionCommandFromInput( acc.input, acc, ac.actionDevice ); // this may apply (un)Blending if needed + // the first one goes into the node, further must be created if not existing + if ( first ) { + UpdateNodeFromAction( matn, acc, ac.actionDevice ); + matn.Nodes.Clear( ); // clear add childs - those don't persist from newly loaded actionmaps + first = false; + } + else { + // have to recreate the action child nodes + ActionTreeInputNode matin = new ActionTreeInputNode( "UNDEF" ); matin.ImageKey = "Add"; + acc.nodeIndex = matin.Index; // assign visual reference + matn.Nodes.Add( matin ); // add to master tree + UpdateInputNodeFromAction( matin, acc, ac.actionDevice ); + } } - else { - // ?? defaults to unassigned color - UpdateNodeFromAction( tnl, ac, DeviceCls.InputKind.Other ); + catch { + ; // key not found } - - } - catch { - ; // key not found } } } @@ -513,24 +774,59 @@ namespace SCJMapper_V2 { log.Debug( "FindAndSelectCtrl - Entry" ); - Boolean found = false; foreach ( ActionTreeNode tn in Ctrl.Nodes ) { // have to search nodes of nodes foreach ( ActionTreeNode stn in tn.Nodes ) { if ( stn.Text.Contains( ctrl ) ) { Ctrl.SelectedNode = stn; Ctrl.SelectedNode.EnsureVisible( ); - found = true; - break; + return; // exit all loops + } + // have to search nodes of nodes + foreach ( ActionTreeInputNode sstn in stn.Nodes ) { + if ( sstn.Text.Contains( ctrl ) ) { + Ctrl.SelectedNode = sstn; + Ctrl.SelectedNode.EnsureVisible( ); + return; // exit all loops + } } } - if ( found ) break; // exit all loops } } /// - /// Find a control that contains the Command + /// Find a control that contains the string and mark it + /// this method is applied to the GUI TreeView only + /// + /// The string to find + public void FindAndSelectCtrlByName( String ctrlName ) + { + log.Debug( "FindAndSelectCtrlByName - Entry" ); + + foreach ( ActionTreeNode tn in Ctrl.Nodes ) { + // have to search nodes of nodes + foreach ( ActionTreeNode stn in tn.Nodes ) { + if ( stn.Name == ctrlName ) { + Ctrl.SelectedNode = stn; + Ctrl.SelectedNode.EnsureVisible( ); + return; // exit all loops + } + // have to search nodes of nodes + foreach ( ActionTreeInputNode sstn in stn.Nodes ) { + if ( sstn.Name == ctrlName ) { + Ctrl.SelectedNode = sstn; + Ctrl.SelectedNode.EnsureVisible( ); + return; // exit all loops + } + } + } + } + } + + + /// + /// Find a control that contains the Action /// /// The actionmap to find the string /// The string to find @@ -561,6 +857,14 @@ namespace SCJMapper_V2 } + + static public String CommandFromNodeText( String actionCommand ) + { + String[] e = actionCommand.Split( new char[] { '-' } ); + if ( e.Length > 1 ) return e[1].Substring( 1 ); + return ""; + } + /// /// Find a control that contains the Command /// @@ -574,8 +878,10 @@ namespace SCJMapper_V2 if ( String.IsNullOrEmpty( actionmap ) || ( tn.Text == actionmap ) ) { // have to search nodes of nodes foreach ( ActionTreeNode stn in tn.Nodes ) { - if ( stn.Command.Contains( command ) ) { - return stn.Text; + foreach ( ActionTreeInputNode istn in stn.Nodes ) { + if ( istn.Command.Contains( command ) ) { + return stn.Text + " - " + istn.Text; + } } } } @@ -625,6 +931,23 @@ namespace SCJMapper_V2 } + public String SelectedAction + { + get { + if ( Ctrl.SelectedNode == null ) return ""; + if ( Ctrl.SelectedNode.Level == 1 ) { + ActionTreeNode matn = FindMasterAction( ( ActionTreeNode )Ctrl.SelectedNode ); + return ActionTreeNode.ActionFromNodeText( matn.Text ); + } + else if ( Ctrl.SelectedNode.Level == 2 ) { + ActionTreeNode matn = FindMasterAction( ( ActionTreeNode )Ctrl.SelectedNode.Parent ); // the parent treenode + return ActionTreeNode.ActionFromNodeText( matn.Text ); + } + else return ""; + } + } + + /// /// Reports a summary list of the mapped items /// @@ -644,9 +967,11 @@ namespace SCJMapper_V2 String rep = String.Format( "*** {0}\n", acm.name ); repList += rep; foreach ( ActionCls ac in acm ) { - if ( !String.IsNullOrEmpty( ac.input ) && !( ac.input == JoystickCls.BlendedInput ) ) { - rep = String.Format( " {0} - {1} - ({2})\n", ac.name.PadRight( 35 ), ac.input.PadRight( 30 ), ac.device ); - repList += rep; + foreach ( ActionCommandCls acc in ac.inputList ) { + if ( !String.IsNullOrEmpty( acc.input ) && !( acc.input == JoystickCls.BlendedInput ) ) { + rep = String.Format( " {0} - {1} - ({2})\n", ac.name.PadRight( 35 ), acc.input.PadRight( 30 ), ac.device ); + repList += rep; + } } } repList += String.Format( "\n" ); diff --git a/actions/ActionTreeInputNode.cs b/actions/ActionTreeInputNode.cs new file mode 100644 index 0000000..a0575e6 --- /dev/null +++ b/actions/ActionTreeInputNode.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace SCJMapper_V2 +{ + /// + /// Our INPUT TreeNode - inherits a regular one and adds some functionality + /// + /// contains the input command i.e. - js2_button3 OR ! js1_x (MODs applies at the very beginning of the string) + /// + class ActionTreeInputNode : TreeNode + { + + #region Static items + + + // Handle all text label composition and extraction here + + public static String ComposeNodeText( String cmd ) + { + if ( String.IsNullOrEmpty( cmd ) ) { + return ""; + } + else { + return cmd; + } + } + + + public static void DecompNodeText( String nodeText, out String cmd ) + { + cmd = nodeText; + } + + + /// + /// Returns the command part from a node text + /// i.e. v_pitch - js1_x returns js1_x + /// + /// The node text in 'action - command' notation + /// the command part or an empty string + public static String CommandFromNodeText( String nodeText ) + { + String cmd; + DecompNodeText( nodeText, out cmd ); + return cmd; + } + + #endregion + + + // Object defs + + // ctor + public ActionTreeInputNode( ) + : base( ) + { + } + + // ctor + public ActionTreeInputNode( ActionTreeInputNode srcNode ) + : base( ) + { + if ( srcNode == null ) return; + this.Name = srcNode.Name; + this.Text = srcNode.Text; + this.BackColor = srcNode.BackColor; + this.ForeColor = srcNode.ForeColor; + this.NodeFont = srcNode.NodeFont; + this.ImageKey = srcNode.ImageKey; + this.Tag = srcNode.Tag; + this.m_command = srcNode.m_command; + } + + // ctor + public ActionTreeInputNode( string text ) + { + this.Text = text; + } + + // ctor + public ActionTreeInputNode( string text, ActionTreeInputNode[] children ) + : base( text, children ) + { + } + + + private String m_command =""; + + public new String Text + { + get { return base.Text; } + set + { + DecompNodeText( value, out m_command ); + base.Text = ComposeNodeText( m_command ); + } + } + + + public String Command + { + get { return m_command; } + set + { + m_command = value; + base.Text = ComposeNodeText( m_command ); + } + } + + public Boolean IsMappedAction + { + get + { + return !( String.IsNullOrEmpty( m_command ) + || ( m_command == JoystickCls.BlendedInput ) + || ( m_command == GamepadCls.BlendedInput ) ); + } + } + + } +} diff --git a/actions/ActionTreeNode.cs b/actions/ActionTreeNode.cs index e34a281..cc3482e 100644 --- a/actions/ActionTreeNode.cs +++ b/actions/ActionTreeNode.cs @@ -14,27 +14,23 @@ namespace SCJMapper_V2 #region Static items - public const char REG_MOD = '-'; - public const char INV_MOD = '!'; - - // Handle all text label composition and extraction here - public static String ComposeNodeText( String action, char mod, String cmd ) + public static String ComposeNodeText( String action, String cmd ) { if ( String.IsNullOrEmpty( cmd ) ) { return action; } else { - return action + " " + mod + " " + cmd; + return action + " - " + cmd; } } - public static void DecompNodeText( String nodeText, out String action, out char mod, out String cmd ) + public static void DecompNodeText( String nodeText, out String action, out String cmd ) { - action = ""; cmd = ""; mod = ( nodeText.Contains( INV_MOD ) ) ? INV_MOD : REG_MOD; - String[] e = nodeText.Split( new char[] { REG_MOD, INV_MOD }, StringSplitOptions.RemoveEmptyEntries ); + action = ""; cmd = ""; + String[] e = nodeText.Split( new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries ); if ( e.Length > 1 ) { action = e[0].TrimEnd( ); if ( e[1] == " " + DeviceCls.BlendedInput ) { @@ -59,8 +55,8 @@ namespace SCJMapper_V2 /// the action part or an empty string public static String ActionFromNodeText( String nodeText ) { - String action, cmd; char mod; - DecompNodeText( nodeText, out action, out mod, out cmd ); + String action, cmd; + DecompNodeText( nodeText, out action, out cmd ); return action; } @@ -72,24 +68,11 @@ namespace SCJMapper_V2 /// the command part or an empty string public static String CommandFromNodeText( String nodeText ) { - String action, cmd; char mod; - DecompNodeText( nodeText, out action, out mod, out cmd ); + String action, cmd; + DecompNodeText( nodeText, out action, out cmd ); return cmd; } - /// - /// Returns the invert modifier of the command part from a node text - /// i.e. v_pitch - js1_x returns false v_pitch ! js1_x returns true - /// - /// The node text in 'action - command' notation - /// True if there is a command and if it contains an inverter else false - public static Boolean CommandInvertFromNodeText( String nodeText ) - { - String action, cmd; char mod; - DecompNodeText( nodeText, out action, out mod, out cmd ); - return ( mod == INV_MOD ); - } - #endregion @@ -115,8 +98,6 @@ namespace SCJMapper_V2 this.Tag = srcNode.Tag; this.m_action = srcNode.m_action; this.m_actionDevice = srcNode.m_actionDevice; - this.m_command = srcNode.m_command; - this.m_modifier = srcNode.m_modifier; } // ctor @@ -134,7 +115,6 @@ namespace SCJMapper_V2 private String m_action = ""; private String m_command =""; - private char m_modifier = REG_MOD; private ActionCls.ActionDevice m_actionDevice = ActionCls.ActionDevice.AD_Unknown; public new String Text @@ -142,8 +122,8 @@ namespace SCJMapper_V2 get { return base.Text; } set { - DecompNodeText( value, out m_action, out m_modifier, out m_command ); - base.Text = ComposeNodeText( m_action, m_modifier, m_command ); + DecompNodeText( value, out m_action, out m_command ); + base.Text = ComposeNodeText( m_action, m_command ); } } @@ -154,7 +134,7 @@ namespace SCJMapper_V2 set { m_action = value; - base.Text = ComposeNodeText( m_action, m_modifier, m_command ); + base.Text = ComposeNodeText( m_action, m_command ); } } @@ -164,17 +144,7 @@ namespace SCJMapper_V2 set { m_command = value; - base.Text = ComposeNodeText( m_action, m_modifier, m_command ); - } - } - - public Boolean InvertCommand - { - get { return ( m_modifier == INV_MOD ); } - set - { - m_modifier = ( value ) ? INV_MOD : REG_MOD; - base.Text = ComposeNodeText( m_action, m_modifier, m_command ); + base.Text = ComposeNodeText( m_action, m_command ); } } @@ -204,9 +174,11 @@ namespace SCJMapper_V2 public Boolean IsMappedAction { - get { return !( String.IsNullOrEmpty(m_command) - || ( m_command == JoystickCls.BlendedInput ) - || ( m_command == GamepadCls.BlendedInput ) ); + get + { + return !( String.IsNullOrEmpty( m_command ) + || ( m_command == JoystickCls.BlendedInput ) + || ( m_command == GamepadCls.BlendedInput ) ); } } diff --git a/graphics/Addbind.ico b/graphics/Addbind.ico new file mode 100644 index 0000000000000000000000000000000000000000..9200878d032165cdd4a053f3d7c0d367d7c0211c GIT binary patch literal 15358 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 literal 0 HcmV?d00001