/*
*
* Copyright (c) 1996
* Knowledge Science Institute, University of Calgary
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. The Knowledge Science Institute makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
*/
#ifndef MCOMMAND_H
#define MCOMMAND_H
#ifndef VECTOR_H
#include
#endif
#ifndef STACK_H
#include
#endif
#ifndef LIST_H
#include
#endif
#ifndef MLEXER_H
#include
#endif
#ifndef MOBJLIB_H
#include
#endif
#ifndef MOBJECT_H
#include
#endif
#ifndef __TIME_H
#include
#endif
class Command;
class ObjectLocator;
extern ObjectLibrary < CloneableObject,
0 > *CloneableLibrary; // instance declaration is left up to the outer program
#ifndef DONTDECLARECOMMANDLIBRARY
extern ObjectLibrary < Command,
0 > *CommandLibrary; // the instance is declared in MCommand.cpp
extern ObjectLibrary < ObjectLocator,
0 > *ObjectLocatorLibrary; // the instance is declared in MCommand.cpp
#endif
class Command;
class CompositeCommand;
//
class CommandReceiverContainer
{
public:
virtual int executeOnBehalf( Command * ) = 0;
virtual int undoOnBehalf( Command * ) = 0;
};
//
class CommandReceiver
{
public:
virtual int execute( Command * ) = 0;
virtual int undo( Command * ) = 0;
};
//
class ObjectLocator
{
public:
virtual CommandReceiver *getObject( ) const = 0;
virtual CommandReceiverContainer *getSuper( ) const
{
return NULL;
} // reasonable default
virtual ObjectLocator *clone( ) = 0;
virtual void printOn( ostream & o ) = 0;
virtual void readFrom( istream & i ) = 0;
virtual void setRoot( void *p ) = 0;
};
//
class NullObjectLocator : public ObjectLocator
{
public:
virtual CommandReceiver *getObject( ) const
{
return NULL;
}
virtual CommandReceiverContainer *getSuper( ) const
{
return NULL;
} // reasonable default
virtual ObjectLocator *clone( )
{
return new NullObjectLocator;
}
virtual void printOn( ostream & o )
{
Lexer::writeDelim( o, '(' );
Lexer::writeDelim( o, ')' );
}
virtual void readFrom( istream & i )
{
Lexer::scanPastDelim( i, ')' );
}
virtual void setRoot( void *p )
{
}
};
//
/*Command on a stream is:
FULL_COMMAND -> COMMAND \n
COMMAND -> COMMAND_PREF SUBTYPE_INFO
COMMAND_PREF -> ,,,,,
*/
class Command
{
friend ostream & operator << ( ostream &, Command & );
friend istream & operator >> ( istream &, Command * );
friend class CompositeCommand;
public:
Command( const ObjectLocator & obj, unsigned int id );
Command( ); // use only as an intermediate step!!
Command( const Command & c );
virtual ~Command( )
{
delete Locator;
}
virtual Command *clone( )
{
return new Command( *this );
}
virtual Command & operator = ( const Command & c );
virtual unsigned int getCommandID( )
{
return ID;
}
virtual time_t getTimeStamp( )
{
return TimeStamp;
}
virtual string getUser( )
{
return User;
}
virtual unsigned long getPID( )
{
return PID;
}
virtual unsigned long getThread( )
{
return Thread;
}
virtual ObjectLocator *peekObjectLocator( )
{
return Locator;
}
virtual void setLocatorRoot( void *p )
{
if ( Locator )
Locator->setRoot( p );
}
virtual int execute( );
virtual int undo( );
virtual unsigned int id( )
{
return ID;
}
virtual unsigned int setID( unsigned int newId )
{
unsigned int ret = ID;
ID = newId;
return ret;
}
virtual int operator < ( Command & c );
virtual int operator == ( Command & c );
virtual void emptyTheTrash( )
{
}
static Command *readCommand( istream & i );
virtual int isMine( ); // returns true iff the command originated with this process/thread
protected:
virtual void printOn( ostream & o );
virtual void readFrom( istream & i );
unsigned int ID;
ObjectLocator *Locator;
string User;
time_t TimeStamp;
unsigned long PID;
unsigned long Thread; // need a thread because a multithread processes (like Netscape)
// will have the same PID for different instances
};
ostream & operator << ( ostream & o, Command & c )
{
c.printOn( o );
Lexer::writeDelim( o, '\n' );
return o;
}
/*
istream& operator>>(istream& i, Command* c) {
*c = Command::readCommand(i);
Lexer::scanPastDelim(i,'\n');
return i;
}
*/
//
class CommandHandler
{
public:
CommandHandler( )
{
HistoryPtr = 0;
}
virtual ~CommandHandler( );
virtual int handleCommand( Command * c );
virtual int undo( void );
virtual int redo( void );
virtual int undoable( void ) {return HistoryPtr;}
virtual int redoable( void ) {return HistoryPtr < HistoryList.size( );}
virtual int resetHistoryList( void );
/* Batch mode works differently than one might expect: */
virtual int beginBatch(unsigned int id);
virtual int endBatch( void );
virtual int AbortBatch(int undoIt=0);
protected:
vector HistoryList;
unsigned long HistoryPtr;
stack > BatchStack;
virtual int doPostCommandProcessing(Command *c);
virtual int AppendToHistoryList(Command *c);
};
//The following are useful templates for subtypes of class Command
//
/*Command1 on a stream is (see Command)
SUBTYPE_INFO -> ,,
*/
template < class T >
class Command1 : public Command
{
public:
Command1( const ObjectLocator & obj, unsigned int id, T & value, T & originalValue )
:
Command( obj, id )
{
Value = value;
OriginalValue = originalValue;
}
Command1( ):
Command( )
{
} // use only as an intermediate!!
Command1( const Command1 < T > &c ):
Command( c )
{
Value = c.Value;
OriginalValue = c.OriginalValue;
}
virtual ~Command1( )
{
}
virtual Command *clone( )
{
return new Command1 < T > ( *this );
}
virtual Command1 < T > &operator = ( const Command1 < T > &c )
{Command::operator = ( c );
Value = c.Value;
OriginalValue = c.OriginalValue;
}
virtual void setValue( T & value )
{
Value = value;
}
virtual T & getValue( )
{
return Value;
}
virtual void setOriginalValue( T & value )
{
OriginalValue = value;
}
virtual T & getOriginalValue( )
{
return OriginalValue;
}
protected:
virtual void printOn( ostream & o )
{
Command::printOn( o );
Lexer::writeDelim( o );
o << Value;
Lexer::writeDelim( o );
o << OriginalValue;
}
virtual void readFrom( istream & i )
{
Command::readFrom( i );
Lexer::readDelim( i );
i >> Value;
Lexer::readDelim( i );
i >> OriginalValue;
}
T Value,
OriginalValue;
};
//
/*CommandPtr1 on a stream is (see Command)
SUBTYPE_INFO -> ,,[],[],
*/
template < class T >
class CommandPtr1 : public Command
{
public:
CommandPtr1( const ObjectLocator & obj, unsigned int id, T * value = NULL, T * originalValue = NULL )
:
Command( obj, id )
{Value = value;
OriginalValue = originalValue;
}
CommandPtr1( ):
Command( ) // use only as an intermediate!!
{
Value = NULL;
OriginalValue = NULL;
}
CommandPtr1( const CommandPtr1 < T > &c ):
Command( c )
{
Value = c.Value;
OriginalValue = c.OriginalValue;
}
virtual ~CommandPtr1( )
{
}
virtual Command *clone( )
{
return new CommandPtr1 < T > ( *this );
}
virtual CommandPtr1 < T > &operator = ( const CommandPtr1 < T > &c )
{Command::operator = ( c );
Value = c.Value;
OriginalValue = c.OriginalValue;
return *this;
}
virtual void setValue( T * value )
{
Value = value;
}
virtual T *getValue( )
{
return Value;
}
virtual void setOriginalValue( T * value )
{
OriginalValue = value;
}
virtual T *getOriginalValue( )
{
return OriginalValue;
}
virtual void emptyTheTrash( )
{
if ( Value )
delete Value;
if ( OriginalValue )
delete OriginalValue;
}
protected:
virtual void printOn( ostream & o )
{
Command::printOn( o );
Lexer::writeDelim( o );
if ( Value )
Lexer::writeQuotedString( o, typeid( *Value ).name( ) );
Lexer::writeDelim( o );
if ( Value )
o << *Value;
Lexer::writeDelim( o );
if ( OriginalValue )
Lexer::writeQuotedString( o, typeid( *OriginalValue ).name( ) );
Lexer::writeDelim( o );
if ( OriginalValue )
o << *OriginalValue;
Lexer::writeDelim( o );
}
virtual void readFrom( istream & i )
{
ObjectLibrary < CloneableObject, 0 > *ol = CloneableLibrary; /* ObjectLibrary::getInstance(); */
Command::readFrom( i );
Lexer::readDelim( i );
string typeName( typeid( T ).name( ) );
Lexer::readQuotedString( i, typeName );
Lexer::readDelim( i );
if ( !Lexer::readDelim( i ) )
{
if ( Value )
delete Value;
Value = NULL;
}
else
{
if ( !Value )
Value = dynamic_cast < T * >( ol->makeCopy( typeName ) );
if ( Value )
i >> *Value;
else
error( 0, 'E', "Can't find %s in ObjectLibrary", typeName.c_str( ) );
Lexer::readDelim( i );
}
Lexer::readQuotedString( i, typeName );
Lexer::readDelim( i );
if ( !Lexer::readDelim( i ) )
{
if ( OriginalValue )
delete OriginalValue;
OriginalValue = NULL;
}
else
{
if ( !OriginalValue )
OriginalValue = dynamic_cast < T * >( ol->makeCopy( typeName ) );
if ( OriginalValue )
i >> *OriginalValue;
else
error( 0, 'E', "Can't find %s in ObjectLibrary", typeName.c_str( ) );
Lexer::readDelim( i );
}
}
T *Value,
*OriginalValue;
};
//
/*CommandPtr2 on a stream is (see Command)
SUBTYPE_INFO -> ,,[],[],[],[],
*/
template < class T1,
class T2 >
class CommandPtr2 : public CommandPtr1 < T1 >
{
public:
CommandPtr2( const ObjectLocator & obj, unsigned int id, T1 * value1 = NULL, T1 * originalValue1 = NULL,
T2 * value2 = NULL, T2 * originalValue2 = NULL )
:
CommandPtr1 < T1 > ( obj, id, value1, originalValue1 )
{Value2 = value2;
OriginalValue2 = originalValue2;
}
CommandPtr2( ):
CommandPtr1 < T1 > ( ) // use only as an intermediate!!
{
Value2 = NULL;
OriginalValue2 = NULL;
}
CommandPtr2( const CommandPtr2 < T1, T2 > &c ):
CommandPtr1 < T1 > ( c )
{
Value2 = c.Value2;
OriginalValue2 = c.OriginalValue2;
}
virtual ~CommandPtr2( )
{
}
virtual Command *clone( )
{
return new CommandPtr2 < T1, T2 > ( *this );
}
virtual CommandPtr2 < T1,
T2 > &operator = ( const CommandPtr2 < T1, T2 > &c )
{CommandPtr1 < T1 > ::operator = ( c );
Value2 = c.Value2;
OriginalValue2 = c.OriginalValue2;
return *this;
}
virtual void setValue2( T2 * value )
{
Value2 = value;
}
virtual T2 *getValue2( )
{
return Value2;
}
virtual void setOriginalValue2( T2 * value )
{
OriginalValue2 = value;
}
virtual T2 *getOriginalValue2( )
{
return OriginalValue2;
}
virtual void emptyTheTrash( )
{
CommandPtr1 < T1 > ::emptyTheTrash( );
if ( Value2 )
delete Value2;
if ( OriginalValue2 )
delete OriginalValue2;
}
protected:
virtual void printOn( ostream & o )
{
CommandPtr1 < T1 > ::printOn( o );
Lexer::writeDelim( o );
if ( Value2 )
Lexer::writeQuotedString( o, typeid( *Value2 ).name( ) );
Lexer::writeDelim( o );
if ( Value2 )
o << *Value2;
Lexer::writeDelim( o );
if ( OriginalValue2 )
Lexer::writeQuotedString( o, typeid( *OriginalValue2 ).name( ) );
Lexer::writeDelim( o );
if ( OriginalValue2 )
o << *OriginalValue2;
Lexer::writeDelim( o );
}
virtual void readFrom( istream & i )
{
ObjectLibrary < CloneableObject, 0 > *ol = CloneableLibrary; /* ObjectLibrary::getInstance(); */
CommandPtr1 < T1 > ::readFrom( i );
Lexer::readDelim( i );
string typeName( typeid( T2 ).name( ) );
Lexer::readQuotedString( i, typeName );
Lexer::readDelim( i );
if ( !Lexer::readDelim( i ) )
{
if ( Value2 )
delete Value2;
Value2 = NULL;
}
else
{
if ( !Value2 )
Value2 = dynamic_cast < T2 * >( ol->makeCopy( typeName ) );
if ( Value2 )
i >> *Value2;
else
error( 0, 'E', "Can't find %s in ObjectLibrary", typeName.c_str( ) );
Lexer::readDelim( i );
}
Lexer::readQuotedString( i, typeName );
Lexer::readDelim( i );
if ( !Lexer::readDelim( i ) )
{
if ( OriginalValue2 )
delete OriginalValue2;
OriginalValue2 = NULL;
}
else
{
if ( !OriginalValue2 )
OriginalValue2 = dynamic_cast < T2 * >( ol->makeCopy( typeName ) );
if ( OriginalValue2 )
i >> *OriginalValue2;
else
error( 0, 'E', "Can't find %s in ObjectLibrary", typeName.c_str( ) );
Lexer::readDelim( i );
}
}
T2 *Value2,
*OriginalValue2;
};
//
/*CommandPtr1Obj1 on a stream is (see Command)
SUBTYPE_INFO -> ,,[],[],[],[],
*/
template < class T1,
class T2 >
class CommandPtr1Obj1 : public CommandPtr1 < T1 >
{
public:
CommandPtr1Obj1( const ObjectLocator & obj, unsigned int id, T1 * value1 = NULL, T1 * originalValue1 = NULL,
T2 value2 = T2( ), T2 originalValue2 = T2( ) )
:
CommandPtr1 < T1 > ( obj, id, value1, originalValue1 )
{Value2 = value2;
OriginalValue2 = originalValue2;
}
CommandPtr1Obj1( ):
CommandPtr1 < T1 > ( ) // use only as an intermediate!!
{
Value2 = T2( );
OriginalValue2 = T2( );
}
CommandPtr1Obj1( const CommandPtr1Obj1 < T1, T2 > &c ):
CommandPtr1 < T1 > ( c )
{
Value2 = c.Value2;
OriginalValue2 = c.OriginalValue2;
}
virtual ~CommandPtr1Obj1( )
{
}
virtual Command *clone( )
{
return new CommandPtr1Obj1 < T1, T2 > ( *this );
}
virtual CommandPtr1Obj1 < T1,
T2 > &operator = ( const CommandPtr1Obj1 < T1, T2 > &c )
{CommandPtr1 < T1 > ::operator = ( c );
Value2 = c.Value2;
OriginalValue2 = c.OriginalValue2;
return *this;
}
virtual void setValue2( T2 value )
{
Value2 = value;
}
virtual T2 & getValue2( )
{
return Value2;
}
virtual void setOriginalValue2( T2 value )
{
OriginalValue2 = value;
}
virtual T2 & getOriginalValue2( )
{
return OriginalValue2;
}
protected:
virtual void printOn( ostream & o )
{
CommandPtr1 < T1 > ::printOn( o );
o << Value2;
Lexer::writeDelim( o );
o << OriginalValue2;
Lexer::writeDelim( o );
}
virtual void readFrom( istream & i )
{
CommandPtr1 < T1 > ::readFrom( i );
Lexer::readDelim( i );
i >> Value2;
Lexer::readDelim( i );
i >> OriginalValue2;
}
T2 Value2,
OriginalValue2;
};
//
/*CommandPtr1Obj2 on a stream is (see Command)
SUBTYPE_INFO -> ,,[],[],[],[],[],[],
*/
template < class T1,
class T2,
class T3 >
class CommandPtr1Obj2 : public CommandPtr1Obj1 < T1,
T2 >
{
public:
CommandPtr1Obj2( const ObjectLocator & obj, unsigned int id, T1 * value1 = NULL, T1 * originalValue1 = NULL,
T2 value2 = T2( ), T2 originalValue2 = T2( ),
T3 value3 = T3( ), T3 originalValue3 = T3( ) )
:
CommandPtr1Obj1 < T1, T2 > ( obj, id, value1, originalValue1, value2, originalValue2 )
{
Value3 = value3;
OriginalValue3 = originalValue3;
}
CommandPtr1Obj2( ):
CommandPtr1Obj1 < T1, T2 > ( ) // use only as an intermediate!!
{
Value3 = T3( );
OriginalValue3 = T3( );
}
CommandPtr1Obj2( const CommandPtr1Obj2 < T1, T2, T3 > &c ):
CommandPtr1Obj1 < T1, T2 > ( c )
{
Value3 = c.Value3;
OriginalValue3 = c.OriginalValue3;
}
virtual ~CommandPtr1Obj2( )
{
}
virtual Command *clone( )
{
return new CommandPtr1Obj2 < T1, T2, T3 > ( *this );
}
virtual CommandPtr1Obj2 < T1,
T2,
T3 > &operator = ( const CommandPtr1Obj2 < T1, T2, T3 > &c )
{CommandPtr1Obj1 < T1, T2 > ::operator = ( c );
Value3 = c.Value3;
OriginalValue3 = c.OriginalValue3;
return *this;
}
virtual void setValue3( T3 value )
{
Value3 = value;
}
virtual T3 & getValue3( )
{
return Value3;
}
virtual void setOriginalValue3( T3 value )
{
OriginalValue3 = value;
}
virtual T3 & getOriginalValue3( )
{
return OriginalValue3;
}
protected:
virtual void printOn( ostream & o )
{
CommandPtr1Obj1 < T1, T2 > ::printOn( o );
o << Value3;
Lexer::writeDelim( o );
o << OriginalValue3;
Lexer::writeDelim( o );
}
virtual void readFrom( istream & i )
{
CommandPtr1Obj1 < T1, T2 > ::readFrom( i );
Lexer::readDelim( i );
i >> Value3;
Lexer::readDelim( i );
i >> OriginalValue3;
}
T3 Value3,
OriginalValue3;
};
//
/*Command2 on a stream is (see Command)
SUBTYPE_INFO -> ,,,,
*/
template < class T1,
class T2 >
class Command2 : public Command1 < T1 >
{
public:
Command2( const ObjectLocator & obj, unsigned int id, T1 value1 = T1( ), T2 value2 = T2( ),
T1 originalValue1 = T1( ), T2 originalValue2 = T2( ) )
:
Command1 < T1 > ( obj, id, value1, originalValue1 )
{Value2 = value2;
OriginalValue2 = originalValue2;
}
Command2( ):
Command1 < T1 > ( )
{
} // use only as an intermediate!!
Command2( const Command2 < T1, T2 > &c ):
Command1 < T1 > ( c )
{
Value2 = c.Value2;
OriginalValue2 = c.OriginalValue2;
}
virtual ~Command2( )
{
}
virtual Command *clone( )
{
return new Command2 < T1, T2 > ( *this );
}
virtual Command2 < T1,
T2 > &operator = ( const Command2 < T1, T2 > &c )
{Command1 < T1 > ::operator = ( c );
Value2 = c.Value2;
OriginalValue2 = c.OriginalValue2;
}
virtual void setValue2( T2 & value )
{
Value2 = value;
}
virtual T2 & getValue2( )
{
return Value2;
}
virtual void setOriginalValue2( T2 & value )
{
OriginalValue2 = value;
}
virtual T2 & getOriginalValue2( )
{
return OriginalValue2;
}
protected:
virtual void printOn( ostream & o )
{
Command1 < T1 > ::printOn( o );
Lexer::writeDelim( o );
o << Value2;
Lexer::writeDelim( o );
o << OriginalValue2;
}
virtual void readFrom( istream & i )
{
Command1 < T1 > ::readFrom( i );
Lexer::readDelim( i );
i >> Value2;
Lexer::readDelim( i );
i >> OriginalValue2;
}
T2 Value2,
OriginalValue2;
};
#ifdef DEFINE_DESTROY_COMMAND //??don't know why this is different for different compilations -- rck
inline void destroy(Command**) {}
#endif
//
/*CompositeCommand on a stream is (see Command)
SUBTYPE_INFO -> ( COMMAND { , COMMAND }* )
*/
class CompositeCommand : public Command
{
public:
CompositeCommand( unsigned int id ):
Command( NullObjectLocator( ), id ) {}
CompositeCommand( const CompositeCommand & c );
CompositeCommand( ):Command( ) {} // use only as an intermediate step
virtual ~CompositeCommand( ){emptyTheTrash();}
virtual CompositeCommand *clone( ) {return new CompositeCommand( *this );}
virtual CompositeCommand & operator = ( const CompositeCommand & c );
virtual int execute( );
virtual int undo( );
virtual void emptyTheTrash( );
int append( Command * c ) {Commands.push_back( c ); return 0;} // this 'owns' c!!!
virtual void setLocatorRoot( void *p );
protected:
virtual void printOn( ostream & o );
virtual void readFrom( istream & i );
typedef vector < Command * >Commands_type;
Commands_type Commands;
};
#endif