2007-06-24 22:53:41 +00:00
#region = = = = = = = = = = = = = = = = = = Copyright ( c ) 2007 Pascal vd Heiden
/ *
* Copyright ( c ) 2007 Pascal vd Heiden , www . codeimp . com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* /
#endregion
#region = = = = = = = = = = = = = = = = = = Namespaces
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Drawing ;
using System.Text ;
using CodeImp.DoomBuilder.Properties ;
using System.IO ;
using CodeImp.DoomBuilder.IO ;
using System.Collections ;
2008-01-02 21:49:43 +00:00
using System.Reflection ;
2008-04-27 12:07:26 +00:00
using System.Windows.Forms ;
2007-06-24 22:53:41 +00:00
#endregion
2008-05-29 11:34:01 +00:00
namespace CodeImp.DoomBuilder.Actions
2007-06-24 22:53:41 +00:00
{
2008-01-02 21:49:43 +00:00
internal class ActionManager
2007-06-24 22:53:41 +00:00
{
#region = = = = = = = = = = = = = = = = = = Constants
2008-01-02 21:49:43 +00:00
private const string ACTIONS_RESOURCE = "Actions.cfg" ;
2007-06-24 22:53:41 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// Actions
private Dictionary < string , Action > actions ;
2008-10-08 22:01:10 +00:00
// Categories
private SortedDictionary < string , string > categories ;
2008-04-27 12:07:26 +00:00
// Keys state
private int modifiers ;
private List < int > pressedkeys ;
// Begun actions
private List < Action > activeactions ;
2007-06-24 22:53:41 +00:00
// Disposing
private bool isdisposed = false ;
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
2008-10-08 22:01:10 +00:00
public SortedDictionary < string , string > Categories { get { return categories ; } }
2007-12-08 15:18:14 +00:00
public Action this [ string action ] { get { if ( actions . ContainsKey ( action ) ) return actions [ action ] ; else throw new ArgumentException ( "There is no such action \"" + action + "\"" ) ; } }
2007-06-24 22:53:41 +00:00
public bool IsDisposed { get { return isdisposed ; } }
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
public ActionManager ( )
{
// Initialize
2007-10-04 18:16:05 +00:00
General . WriteLogLine ( "Starting action manager..." ) ;
2007-06-24 22:53:41 +00:00
actions = new Dictionary < string , Action > ( ) ;
2008-04-27 12:07:26 +00:00
pressedkeys = new List < int > ( ) ;
activeactions = new List < Action > ( ) ;
2008-10-08 22:01:10 +00:00
categories = new SortedDictionary < string , string > ( ) ;
2008-04-27 12:07:26 +00:00
2008-01-02 21:49:43 +00:00
// Load all actions in this assembly
LoadActions ( General . ThisAssembly ) ;
2007-06-24 22:53:41 +00:00
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
2008-01-02 21:49:43 +00:00
// Disposer
2007-06-24 22:53:41 +00:00
public void Dispose ( )
{
// Not already disposed?
if ( ! isdisposed )
{
// Clean up
// Done
isdisposed = true ;
}
}
#endregion
#region = = = = = = = = = = = = = = = = = = Actions
2008-01-02 21:49:43 +00:00
// This loads all actions from an assembly
public void LoadActions ( Assembly asm )
2007-06-24 22:53:41 +00:00
{
Stream actionsdata ;
StreamReader actionsreader ;
Configuration cfg ;
2008-10-08 22:01:10 +00:00
string cat , name , title , desc , shortname ;
2008-04-27 12:07:26 +00:00
bool amouse , akeys , ascroll , debugonly , noshift , repeat ;
2008-01-02 21:49:43 +00:00
string [ ] resnames ;
AssemblyName asmname = asm . GetName ( ) ;
2007-06-24 22:53:41 +00:00
2008-01-02 21:49:43 +00:00
// Find a resource named Actions.cfg
resnames = asm . GetManifestResourceNames ( ) ;
foreach ( string rn in resnames )
{
// Found one?
if ( rn . EndsWith ( ACTIONS_RESOURCE , StringComparison . InvariantCultureIgnoreCase ) )
{
// Get a stream from the resource
actionsdata = asm . GetManifestResourceStream ( rn ) ;
actionsreader = new StreamReader ( actionsdata , Encoding . ASCII ) ;
// Load configuration from stream
cfg = new Configuration ( ) ;
cfg . InputConfiguration ( actionsreader . ReadToEnd ( ) ) ;
2008-12-27 00:22:31 +00:00
if ( cfg . ErrorResult ! = 0 )
2008-10-08 22:01:10 +00:00
{
2008-12-27 00:22:31 +00:00
string errordesc = "Error in Actions configuration on line " + cfg . ErrorLine + ": " + cfg . ErrorDescription ;
General . CancelAutoMapLoad ( ) ;
General . WriteLogLine ( "ERROR: Unable to read Actions configuration from assembly " + Path . GetFileName ( asm . Location ) + "!" ) ;
General . WriteLogLine ( errordesc ) ;
General . ShowErrorMessage ( "Unable to read Actions configuration from assembly " + Path . GetFileName ( asm . Location ) + "!\n" + errordesc , MessageBoxButtons . OK ) ;
2008-10-08 22:01:10 +00:00
}
2008-12-27 00:22:31 +00:00
else
2008-01-02 21:49:43 +00:00
{
2008-12-27 00:22:31 +00:00
// Read the categories structure
IDictionary cats = cfg . ReadSetting ( "categories" , new Hashtable ( ) ) ;
foreach ( DictionaryEntry c in cats )
{
// Make the category if not already added
if ( ! categories . ContainsKey ( c . Key . ToString ( ) ) )
categories . Add ( c . Key . ToString ( ) , c . Value . ToString ( ) ) ;
}
2008-01-06 20:56:48 +00:00
2008-12-27 00:22:31 +00:00
// Go for all objects in the configuration
foreach ( DictionaryEntry a in cfg . Root )
2008-01-06 20:56:48 +00:00
{
2008-12-27 00:22:31 +00:00
// Get action properties
shortname = a . Key . ToString ( ) ;
name = asmname . Name . ToLowerInvariant ( ) + "_" + shortname ;
debugonly = cfg . ReadSetting ( a . Key + ".debugonly" , false ) ;
// Not the categories structure?
if ( shortname . ToLowerInvariant ( ) ! = "categories" )
2008-10-08 22:01:10 +00:00
{
2008-12-27 00:22:31 +00:00
// Check if action should be included
if ( General . DebugBuild | | ! debugonly )
{
// Create an action
CreateAction ( cfg , name , shortname ) ;
}
2008-10-08 22:01:10 +00:00
}
2008-01-06 20:56:48 +00:00
}
2008-01-02 21:49:43 +00:00
}
2008-12-27 00:22:31 +00:00
// Done with the resource
actionsreader . Dispose ( ) ;
actionsdata . Dispose ( ) ;
2008-01-02 21:49:43 +00:00
}
}
}
2007-06-24 22:53:41 +00:00
2008-01-02 21:49:43 +00:00
// This manually creates an action
2008-10-09 05:49:46 +00:00
private void CreateAction ( Configuration cfg , string name , string shortname )
2008-01-02 21:49:43 +00:00
{
// Action does not exist yet?
if ( ! actions . ContainsKey ( name ) )
2007-06-24 22:53:41 +00:00
{
2008-01-02 21:49:43 +00:00
// Read the key from configuration
2008-10-09 05:49:46 +00:00
int key = General . Settings . ReadSetting ( "shortcuts." + name , - 1 ) ;
2008-01-02 21:49:43 +00:00
2007-06-24 22:53:41 +00:00
// Create an action
2008-10-09 05:49:46 +00:00
actions . Add ( name , new Action ( cfg , name , shortname , key ) ) ;
2008-01-02 21:49:43 +00:00
}
else
{
// Action already exists!
General . WriteLogLine ( "WARNING: Action '" + name + "' already exists. Action names must be unique!" ) ;
2007-06-24 22:53:41 +00:00
}
}
2008-04-27 12:07:26 +00:00
// This binds all methods marked with this attribute
internal void BindMethods ( Type type )
{
// Bind static methods
BindMethods ( null , type ) ;
}
// This binds all methods marked with this attribute
internal void BindMethods ( object obj )
{
// Bind instance methods
BindMethods ( obj , obj . GetType ( ) ) ;
}
// This binds all methods marked with this attribute
private void BindMethods ( object obj , Type type )
{
MethodInfo [ ] methods ;
ActionAttribute [ ] attrs ;
ActionDelegate del ;
string actionname ;
if ( obj = = null )
General . WriteLogLine ( "Binding static action methods for class " + type . Name + "..." ) ;
else
General . WriteLogLine ( "Binding action methods for " + type . Name + " object..." ) ;
// Go for all methods on obj
methods = type . GetMethods ( BindingFlags . Instance | BindingFlags . NonPublic | BindingFlags . Public | BindingFlags . Static ) ;
foreach ( MethodInfo m in methods )
{
// Check if the method has this attribute
attrs = ( ActionAttribute [ ] ) m . GetCustomAttributes ( typeof ( BeginActionAttribute ) , true ) ;
// Go for all attributes
foreach ( ActionAttribute a in attrs )
{
// Create a delegate for this method
del = ( ActionDelegate ) Delegate . CreateDelegate ( typeof ( ActionDelegate ) , obj , m ) ;
// Make proper name
actionname = a . GetFullActionName ( type . Assembly ) ;
// Bind method to action
if ( Exists ( actionname ) )
actions [ actionname ] . BindBegin ( del ) ;
else
throw new ArgumentException ( "Could not bind " + m . ReflectedType . Name + "." + m . Name + " to action \"" + actionname + "\", that action does not exist! Refer to, or edit Actions.cfg for all available application actions." ) ;
}
// Check if the method has this attribute
attrs = ( ActionAttribute [ ] ) m . GetCustomAttributes ( typeof ( EndActionAttribute ) , true ) ;
// Go for all attributes
foreach ( ActionAttribute a in attrs )
{
// Create a delegate for this method
del = ( ActionDelegate ) Delegate . CreateDelegate ( typeof ( ActionDelegate ) , obj , m ) ;
// Make proper name
actionname = a . GetFullActionName ( type . Assembly ) ;
// Bind method to action
if ( Exists ( actionname ) )
actions [ actionname ] . BindEnd ( del ) ;
else
throw new ArgumentException ( "Could not bind " + m . ReflectedType . Name + "." + m . Name + " to action \"" + actionname + "\", that action does not exist! Refer to, or edit Actions.cfg for all available application actions." ) ;
}
}
}
// This binds a delegate manually
internal void BindBeginDelegate ( Assembly asm , ActionDelegate d , BeginActionAttribute a )
{
string actionname ;
// Make proper name
actionname = a . GetFullActionName ( asm ) ;
// Bind delegate to action
if ( Exists ( actionname ) )
actions [ actionname ] . BindBegin ( d ) ;
else
General . WriteLogLine ( "WARNING: Could not bind delegate for " + d . Method . Name + " to action \"" + a . ActionName + "\" (" + actionname + "), that action does not exist! Refer to, or edit Actions.cfg for all available application actions." ) ;
}
// This binds a delegate manually
internal void BindEndDelegate ( Assembly asm , ActionDelegate d , EndActionAttribute a )
{
string actionname ;
// Make proper name
actionname = a . GetFullActionName ( asm ) ;
// Bind delegate to action
if ( Exists ( actionname ) )
actions [ actionname ] . BindEnd ( d ) ;
else
General . WriteLogLine ( "WARNING: Could not bind delegate for " + d . Method . Name + " to action \"" + a . ActionName + "\" (" + actionname + "), that action does not exist! Refer to, or edit Actions.cfg for all available application actions." ) ;
}
// This unbinds all methods marked with this attribute
internal void UnbindMethods ( Type type )
{
// Unbind static methods
UnbindMethods ( null , type ) ;
}
// This unbinds all methods marked with this attribute
internal void UnbindMethods ( object obj )
{
// Unbind instance methods
UnbindMethods ( obj , obj . GetType ( ) ) ;
}
// This unbinds all methods marked with this attribute
private void UnbindMethods ( object obj , Type type )
{
MethodInfo [ ] methods ;
ActionAttribute [ ] attrs ;
ActionDelegate del ;
string actionname ;
if ( obj = = null )
General . WriteLogLine ( "Unbinding static action methods for class " + type . Name + "..." ) ;
else
General . WriteLogLine ( "Unbinding action methods for " + type . Name + " object..." ) ;
// Go for all methods on obj
methods = type . GetMethods ( BindingFlags . Instance | BindingFlags . NonPublic | BindingFlags . Public | BindingFlags . Static ) ;
foreach ( MethodInfo m in methods )
{
// Check if the method has this attribute
attrs = ( ActionAttribute [ ] ) m . GetCustomAttributes ( typeof ( BeginActionAttribute ) , true ) ;
// Go for all attributes
foreach ( ActionAttribute a in attrs )
{
// Create a delegate for this method
del = ( ActionDelegate ) Delegate . CreateDelegate ( typeof ( ActionDelegate ) , obj , m ) ;
// Make proper name
actionname = a . GetFullActionName ( type . Assembly ) ;
// Unbind method from action
actions [ actionname ] . UnbindBegin ( del ) ;
}
// Check if the method has this attribute
attrs = ( ActionAttribute [ ] ) m . GetCustomAttributes ( typeof ( EndActionAttribute ) , true ) ;
// Go for all attributes
foreach ( ActionAttribute a in attrs )
{
// Create a delegate for this method
del = ( ActionDelegate ) Delegate . CreateDelegate ( typeof ( ActionDelegate ) , obj , m ) ;
// Make proper name
actionname = a . GetFullActionName ( type . Assembly ) ;
// Unbind method from action
actions [ actionname ] . UnbindEnd ( del ) ;
}
}
}
// This unbinds a delegate manually
internal void UnbindBeginDelegate ( Assembly asm , ActionDelegate d , BeginActionAttribute a )
{
string actionname ;
// Make proper name
actionname = a . GetFullActionName ( asm ) ;
// Unbind delegate to action
actions [ actionname ] . UnbindBegin ( d ) ;
}
// This unbinds a delegate manually
internal void UnbindEndDelegate ( Assembly asm , ActionDelegate d , EndActionAttribute a )
{
string actionname ;
// Make proper name
actionname = a . GetFullActionName ( asm ) ;
// Unbind delegate to action
actions [ actionname ] . UnbindEnd ( d ) ;
}
2008-01-02 21:49:43 +00:00
2007-06-24 22:53:41 +00:00
// This checks if a given action exists
public bool Exists ( string action )
{
return actions . ContainsKey ( action ) ;
}
2007-09-29 15:43:59 +00:00
// This returns a list of all actions
public Action [ ] GetAllActions ( )
{
Action [ ] list = new Action [ actions . Count ] ;
actions . Values . CopyTo ( list , 0 ) ;
return list ;
}
2009-01-05 21:20:51 +00:00
// This returns the specified action
public Action GetActionByName ( string fullname )
{
return actions [ fullname ] ;
}
2007-09-29 15:43:59 +00:00
2007-09-30 19:37:57 +00:00
// This saves the control settings
public void SaveSettings ( )
{
// Go for all actions
foreach ( KeyValuePair < string , Action > a in actions )
{
// Write to configuration
General . Settings . WriteSetting ( "shortcuts." + a . Key , a . Value . ShortcutKey ) ;
}
}
2008-12-10 22:58:58 +00:00
// This invokes the Begin and End of the given action
public bool InvokeAction ( string actionname )
{
if ( Exists ( actionname ) )
{
2009-01-05 21:20:51 +00:00
actions [ actionname ] . Invoke ( ) ;
2008-12-10 22:58:58 +00:00
return true ;
}
else
{
return false ;
}
}
2007-09-30 19:37:57 +00:00
2007-06-24 22:53:41 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Shortcut Keys
2008-04-27 12:07:26 +00:00
2008-10-09 05:49:46 +00:00
// This applies default keys if they are not already in use
public void ApplyDefaultShortcutKeys ( )
{
// Find actions that have no key set
foreach ( KeyValuePair < string , Action > a in actions )
{
2008-10-16 09:23:25 +00:00
// Key set?
if ( a . Value . ShortcutKey = = - 1 )
2008-10-09 05:49:46 +00:00
{
2008-10-16 09:23:25 +00:00
// Check if the default key is not already used
bool keyused = false ;
foreach ( KeyValuePair < string , Action > d in actions )
{
// Check if the keys are the same
// Note that I use the mask of the source action to check if they match any combination
if ( ( d . Value . ShortcutKey & a . Value . ShortcutMask ) = = ( a . Value . DefaultShortcutKey & a . Value . ShortcutMask ) )
{
// No party.
keyused = true ;
break ;
}
}
// Party?
if ( ! keyused )
{
// Apply the default key
a . Value . SetShortcutKey ( a . Value . DefaultShortcutKey ) ;
}
else
2008-10-09 05:49:46 +00:00
{
// No party.
2008-10-16 09:23:25 +00:00
a . Value . SetShortcutKey ( 0 ) ;
2008-10-09 05:49:46 +00:00
}
}
}
}
2008-04-27 12:07:26 +00:00
// This checks if a given action is active
public bool CheckActionActive ( Assembly asm , string actionname )
{
// Find active action
string fullname = asm . GetName ( ) . Name . ToLowerInvariant ( ) + "_" + actionname ;
foreach ( Action a in activeactions )
{
if ( a . Name = = fullname ) return true ;
}
2007-06-24 22:53:41 +00:00
2008-04-27 12:07:26 +00:00
// No such active action
return false ;
}
2007-06-24 22:53:41 +00:00
// Removes all shortcut keys
public void RemoveShortcutKeys ( )
{
// Clear all keys
foreach ( KeyValuePair < string , Action > a in actions )
a . Value . SetShortcutKey ( 0 ) ;
}
2008-04-27 12:07:26 +00:00
// This notifies a key has been pressed
public void KeyPressed ( int key )
{
int strippedkey = key & ~ ( ( int ) Keys . Alt | ( int ) Keys . Shift | ( int ) Keys . Control ) ;
if ( ( strippedkey = = ( int ) Keys . ShiftKey ) | | ( strippedkey = = ( int ) Keys . ControlKey ) ) key = strippedkey ;
bool repeat = pressedkeys . Contains ( strippedkey ) ;
// Update pressed keys
if ( ! repeat ) pressedkeys . Add ( strippedkey ) ;
2008-05-08 16:39:14 +00:00
// Add action to active list
Action [ ] acts = GetActionsByKey ( key ) ;
foreach ( Action a in acts ) if ( ! activeactions . Contains ( a ) ) activeactions . Add ( a ) ;
// Invoke actions
BeginActionByKey ( key , repeat ) ;
2008-04-27 12:07:26 +00:00
}
// This notifies a key has been released
public void KeyReleased ( int key )
{
int strippedkey = key & ~ ( ( int ) Keys . Alt | ( int ) Keys . Shift | ( int ) Keys . Control ) ;
List < Action > keepactions = new List < Action > ( ) ;
// Update pressed keys
if ( pressedkeys . Contains ( strippedkey ) ) pressedkeys . Remove ( strippedkey ) ;
// End actions that no longer match
EndActiveActions ( ) ;
}
2008-05-08 16:39:14 +00:00
// This releases all pressed keys
public void ReleaseAllKeys ( )
{
// Clear pressed keys
pressedkeys . Clear ( ) ;
// End actions
EndActiveActions ( ) ;
}
2008-04-27 12:07:26 +00:00
// This updates the modifiers
public void UpdateModifiers ( int mods )
{
// Update modifiers
modifiers = mods ;
// End actions that no longer match
EndActiveActions ( ) ;
}
2008-01-13 21:23:59 +00:00
// This will call the associated actions for a keypress
2008-04-27 12:07:26 +00:00
private void BeginActionByKey ( int key , bool repeated )
2007-06-24 22:53:41 +00:00
{
2008-05-17 08:00:25 +00:00
// Get all actions for which a begin is bound
List < Action > boundactions = new List < Action > ( actions . Count ) ;
2007-06-24 22:53:41 +00:00
foreach ( KeyValuePair < string , Action > a in actions )
2008-05-17 08:00:25 +00:00
if ( a . Value . BeginBound ) boundactions . Add ( a . Value ) ;
// Go for all actions
foreach ( Action a in boundactions )
2007-06-24 22:53:41 +00:00
{
// This action is associated with this key?
2008-05-17 08:00:25 +00:00
if ( a . KeyMatches ( key ) )
2007-06-24 22:53:41 +00:00
{
2008-05-08 16:39:14 +00:00
// Allowed to repeat?
2008-05-17 08:00:25 +00:00
if ( a . Repeat | | ! repeated )
2008-05-08 16:39:14 +00:00
{
// Invoke action
2008-05-17 08:00:25 +00:00
a . Begin ( ) ;
2008-05-08 16:39:14 +00:00
}
else
{
//General.WriteLogLine("Action \"" + a.Value.Name + "\" failed because it does not support repeating activation!");
}
2008-04-27 12:07:26 +00:00
}
}
}
// This will end active actions for which the pressed keys do not match
private void EndActiveActions ( )
{
2008-05-16 20:00:49 +00:00
bool listchanged ;
do
2008-04-27 12:07:26 +00:00
{
2008-05-16 20:00:49 +00:00
// Go for all active actions
listchanged = false ;
for ( int i = 0 ; i < activeactions . Count ; i + + )
2008-04-27 12:07:26 +00:00
{
2008-05-16 20:00:49 +00:00
Action a = activeactions [ i ] ;
// Go for all pressed keys
bool stillactive = false ;
foreach ( int k in pressedkeys )
{
if ( ( k = = ( int ) Keys . ShiftKey ) | | ( k = = ( int ) Keys . ControlKey ) )
stillactive | = a . KeyMatches ( k ) ;
else
stillactive | = a . KeyMatches ( k | modifiers ) ;
}
2008-04-27 12:07:26 +00:00
2008-05-16 20:00:49 +00:00
// End the action if no longer matches any of the keys
if ( ! stillactive )
{
activeactions . RemoveAt ( i ) ;
listchanged = true ;
a . End ( ) ;
break ;
}
2007-06-24 22:53:41 +00:00
}
}
2008-05-16 20:00:49 +00:00
while ( listchanged ) ;
2007-06-24 22:53:41 +00:00
}
2008-01-13 21:23:59 +00:00
// This returns all action names for a given key
2008-04-27 12:07:26 +00:00
public string [ ] GetActionNamesByKey ( int key )
2008-01-13 21:23:59 +00:00
{
List < string > actionnames = new List < string > ( ) ;
// Go for all actions
foreach ( KeyValuePair < string , Action > a in actions )
{
// This action is associated with this key?
2008-04-27 12:07:26 +00:00
if ( a . Value . KeyMatches ( key ) )
2008-01-13 21:23:59 +00:00
{
// List short name
actionnames . Add ( a . Value . ShortName ) ;
}
}
// Return result;
return actionnames . ToArray ( ) ;
}
2008-04-27 12:07:26 +00:00
// This returns all action names for a given key
public Action [ ] GetActionsByKey ( int key )
{
List < Action > actionnames = new List < Action > ( ) ;
// Go for all actions
foreach ( KeyValuePair < string , Action > a in actions )
{
// This action is associated with this key?
if ( a . Value . KeyMatches ( key ) )
{
// List short name
actionnames . Add ( a . Value ) ;
}
}
// Return result;
return actionnames . ToArray ( ) ;
}
2008-01-13 21:23:59 +00:00
2007-06-24 22:53:41 +00:00
#endregion
}
}