    // @(#)root/base:$Name:  $:$Id: TQCommand.cxx,v 1.4 2005/02/11 18:40:08 rdm Exp $
    // Author: Valeriy Onuchin 04/27/2004

    /*************************************************************************
     * Copyright (C) 1995-2001, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // The Command design pattern is based on the idea, that all editing    //
    // in an application is done by creating instances of command objects.  //
    // Command objects apply changes to the edited object and then are      //
    // stored  on a command stack. Furthermore, each command knows how to   //
    // undo its changes to bring the edited object back to its previous     //
    // state. As long as the application only uses command objects to       //
    // change the state of the edited object, it is possible to undo        //
    // a sequence of commands by traversing the command stack downwards and //
    // calling the "undo" method of each command in turn. It is also        //
    // possible to redo a sequence of commands by traversing the command    //
    // stack upwards and calling the "redo" method of each command.         //
    //                                                                      //
    //                                                                      //
    // Examples:                                                            //
    //                                                                      //
    // 1. Create a new command                                              //
    //                                                                      //
    // TQCommand *com = new TQCommand("TH1", hpx, "SetFillColor(Color_t)"   //
    //                                "SetFillColor(Color_t)");             //
    //                                                                      //
    // 1st parameter - the name of class                                    //
    // 2nd parameter - object                                               //
    // 3rd parameter - the name of do/redo method                           //
    // 4th parameter - the name of undo method                              //
    //                                                                      //
    // Since redo,undo methods are the same, undo name can be omitted, e.g. //
    //                                                                      //
    // TQCommand *com = new TQCommand("TH1", hpx, "SetFillColor(Color_t)"); //
    //                                                                      //
    // For objects derived from TObject class name can be omitted, e.g.     //
    //                                                                      //
    //    TQCommand *com = new TQCommand(hpx, "SetFillColor(Color_t)");     //
    //                                                                      //
    // 2. Setting undo, redo parameters.                                    //
    //                                                                      //
    //    Color_t old_color = hpx->GetFillColor();                          //
    //    Color_t new_color = 4;  // blue color                             //
    //                                                                      //
    //    com->SetRedoArgs(1, new_color);                                   //
    //    com->SetUndoArgs(1, old_color);                                   //
    //                                                                      //
    // 1st argument - the number of undo, redo parameters                   //
    // the other arguments - undo, redo values                              //
    //                                                                      //
    // Since the number of undo,redo parameters is the same one can use     //
    //                                                                      //
    //    com->SetArgs(1, new_color, old_color);                            //
    //                                                                      //
    // 3. Undo, redo method execution                                       //
    //                                                                      //
    //    com->Redo(); // execute redo method                               //
    //    com->Undo(); // execute undo method                               //
    //                                                                      //
    // 4. Merged commands                                                   //
    //                                                                      //
    // It possible to group several commands together so an end user        //
    // can undo and redo them with one command.                             //
    //                                                                      //
    //    TQCommand *update = new TQCommand(gPad, "Modified()");            //
    //    com->Add(update);                                                 //
    //                                                                      //
    // 5. Macro commands                                                    //
    //                                                                      //
    // "Merging" allows to create macro commands, e.g.                      //
    //                                                                      //
    //    TQCommand *macro = new TQCommand("my macro");                     //
    //    macro->Add(com1);                                                 //
    //    macro->Add(com2);                                                 //
    //    ...                                                               //
    //                                                                      //
    // During Redo operation commands composing macro command are executed  //
    // sequentially in direct  order (first in first out). During Undo,     //
    // they are executed in reverse order (last in first out).              //
    //                                                                      //
    // 6. Undo manager.                                                     //
    //                                                                      //
    // TQUndoManager is recorder of undo and redo operations. This is       //
    // command history list which can be traversed backwards and upwards    //
    // performing undo and redo operations.                                 //
    // To register command TQUndoManager::Add(TObject*) method is used.     //
    //                                                                      //
    //    TQUndoManager *history = new TQUndoManager();                     //
    //    history->Add(com);                                                //
    //                                                                      //
    // TQUndoManager::Add automatically invokes execution of command's      //
    // Redo method.                                                         //
    //                                                                      //
    // Use TQUndoManager::Undo to undo commands in  history list.           //
    // Redo is Undo for undo action. Use TQUndoManager::Redo method for that//
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TQCommand.h"
    #include "TQConnection.h"
    #include "TDataType.h"
    #include "stdarg.h"

    ClassImp(TQCommand)
    ClassImp(TQUndoManager)

    static TQCommand *gActiveCommand = 0;

    //////////////////////// auxilary private functions ////////////////////////////
    //______________________________________________________________________________
    static char *ResolveTypes(const char *method)
    {
       // Resolve any typedefs in the method signature. For example:
       // func(Float_t,Int_t) becomes func(float,int).
       // The returned string must be deleted by the user.

       if (!method || !*method) return 0;

       char *str = new char[strlen(method)+1];
       if (str) strcpy(str, method);

       TString res;

       char *s = strtok(str, "(");
       res = s;
       res += "(";

       Bool_t first = kTRUE;
       while ((s = strtok(0, ",)"))) {
          char *s1, s2 = 0;
          if ((s1 = strchr(s, '*'))) {
             *s1 = 0;
             s2  = '*';
          }
          if (!s1 && (s1 = strchr(s, '&'))) {
             *s1 = 0;
             s2  = '&';
          }
          TDataType *dt = gROOT->GetType(s);
          if (s1) *s1 = s2;
          if (!first) res += ",";
          if (dt) {
             res += dt->GetFullTypeName();
             if (s1) res += s1;
          } else
             res += s;
          first = kFALSE;
       }

       res += ")";

       delete [] str;
       str = new char[res.Length()+1];
       strcpy(str, res.Data());

       return str;
    }

    //______________________________________________________________________________
    static char *CompressName(const char *method_name)
    {
       // Removes "const" words and blanks from full (with prototype)
       // method name.
       //
       //  Example: CompressName(" Draw(const char *, const char *,
       //                               Option_t * , Int_t , Int_t)")
       //
       // Returns the string "Draw(char*,char*,char*,int,int)"
       // The returned string must be deleted by the user.

       if (!method_name || !*method_name) return 0;

       char *str = new char[strlen(method_name)+1];
       if (str) strcpy(str, method_name);

       char *tmp = str;

       // substitute "const" with white spaces
       while ((tmp = strstr(tmp,"const"))) {
          for (int i = 0; i < 5; i++) *(tmp+i) = ' ';
       }

       tmp = str;
       char *s;
       s = str;

       Bool_t quote = kFALSE;
       while (*tmp) {
          if (*tmp == '\"')
             quote = quote ? kFALSE : kTRUE;
          if (*tmp != ' ' || quote)
             *s++ = *tmp;
          tmp++;
       }
       *s = '\0';

       s = ResolveTypes(str);

       delete [] str;

       return s;
    }

    ////////////////////////////////////////////////////////////////////////////////
    //______________________________________________________________________________
    void TQCommand::Init(const char *clname, void *obj, const char *redo, const char *undo)
    {
       // common protected method used in several constructors

       char *credo = CompressName(redo);
       char *cundo = CompressName(undo);

       fNRargs = fNUargs = -1;
       fNewDelete = kFALSE;
       fObject = obj;

       fRedo = redo ? new TQConnection(clname, obj, credo) : 0;
       fUndo = undo ? new TQConnection(clname, obj, cundo) : fRedo;

       fRedoArgs = 0;
       fUndoArgs = 0;
       fStatus = 0;
       fState = 0;

       delete [] credo;
       delete [] cundo;

       if (!obj && !redo && !undo) { // macros
          fName = clname;
       }
    }

    //______________________________________________________________________________
    TQCommand::TQCommand(const char *clname, void *obj, const char *redo,
                         const char *undo) : TList(), TQObject()
    {
       // Constructor.
       //
       //   Input parameters:
       //     1. clname - class name.
       //     2. obj - an object
       //     3. redo - method or function to do/redo operation
       //     4. undo - method or function to undo operation
       //
       // Comments:
       //    - if either clname or obj is NULL that means that redo/undo is function
       //    - to specify default arguments for redo/undo method/function
       //       '=' must precede to argument value.
       //
       //  Example:
       //    TQCommand("TPad", gPad, "SetEditable(=kTRUE)", "SetEditable(=kFALSE)");
       //
       //    - undo method can be same as redo one. In that case undo parameter
       //       can be omitted.
       //
       //  Example:
       //    TQCommand("TPad", gPad, "SetFillStyle(Style_t)");

       Init(clname, obj, redo, undo);
    }

    //______________________________________________________________________________
    TQCommand::TQCommand(TObject *obj, const char *redo, const char *undo) :
               TList(), TQObject()
    {
       // Constructor.
       //
       //   Input parameters:
       //     1. obj - an object
       //     2. redo - method or function to do/redo operation
       //     3. undo - method or function to undo operation
       //
       // Comments:
       //    - to specify default arguments for redo/undo method/function
       //       '=' must precede to argument value.
       //
       //  Example:
       //    TQCommand(gPad, "SetEditable(=kTRUE)", "SetEditable(=kFALSE)");
       //
       //    - undo method can be same as redo one. In that case "undo"
       //       can parameter be omitted.
       //
       //  Example:
       //    TQCommand(gPad, "SetFillStyle(Style_t)");

       if (obj) Init(obj->ClassName(), obj, redo, undo);
       else Init(0, 0, redo, undo);
    }

    //______________________________________________________________________________
    TQCommand::TQCommand(const TQCommand &com) : TList(), TQObject()
    {
       // Copy constructor.

       fRedo = new TQConnection(*(com.fRedo));
       fUndo = new TQConnection(*(com.fUndo));

       fRedoArgs = 0;
       fUndoArgs = 0;
       fNRargs = com.fNRargs;
       fNUargs = com.fNUargs;

       if (fNRargs > 0) {
          fRedoArgs = new Long_t[fNRargs];
          for (int i = 0; i< fNRargs; i++) {
             fRedoArgs[i] = com.fRedoArgs[i];
          }
       }
      if (fNUargs > 0) {
          fUndoArgs = new Long_t[fNUargs];
          for (int i = 0; i < fNUargs; i++) {
             fUndoArgs[i] = com.fUndoArgs[i];
          }
       }
       fStatus = com.fStatus;
       fNewDelete = com.fNewDelete;
       fName = com.fName;
       fTitle = com.fTitle;
       fObject = com.fObject;
       fState = com.fState;

       // copy merged commands
       TIter next(&com);
       TQCommand *obj;
       while ((obj = (TQCommand*)next())) {
          TList::Add(new TQCommand(*obj));
       }
    }

    //______________________________________________________________________________
    TQCommand::~TQCommand()
    {
       // dtor.

       if (fRedo != fUndo) delete fUndo;

       delete fRedo;
       delete fRedoArgs;
       delete fUndoArgs;

       Delete();
    }

    //______________________________________________________________________________
    TQCommand *TQCommand::gCommand()
    {
       // Return a command which is doing redo/undo action
       //
       // This static method allows to set undo parameters dynamically, i.e.
       // during execution of Redo function.
       //
       // Example:
       //    For redo actions like TGTextEdit::DelChar() it is not possible to
       //    know ahead what character will be deleted.
       //    To set arguments for undo action ( e.g. TGTextEdit::InsChar(char)),
       //    one needs to call TQCommand::SetUndoArgs(1, character) from
       //    inside of TGTextEdit::DelChar() method, i.e.
       //
       //    TQCommand::gCommand()->SetUndoArgs(1, somechar);

       return gActiveCommand;
    }

    //______________________________________________________________________________
    void TQCommand::Delete(Option_t *opt)
    {
       // If "opt" is not zero delete every merged command which option string is
       // equal to "opt". If "opt" is zero - delete all merged commands.

       if (!opt) {
          TList::Delete();
          return;
       }

       TObjLink *lnk = fFirst;
       TObjLink *sav;

       while (lnk) {
          sav = lnk->Next();
          TString ostr = lnk->GetOption();
          if (ostr.Contains(opt)) {   // remove command
             delete lnk->GetObject();
             Remove(lnk);
          }
          lnk = sav;
       }
    }

    //______________________________________________________________________________
    Bool_t TQCommand::CanMerge(TQCommand *) const
    {
       // Two commands can be merged if they can be composed into
       // a single command (Macro command).
       //
       // To allow merging commands user might override this function.

       return (!fRedo && !fUndo);
    }

    //______________________________________________________________________________
    void TQCommand::Merge(TQCommand *c)
    {
       // Add command to the list of merged commands.
       // This make it possible to group complex actions together so an end user
       // can undo and redo them with one command. Execution of TQUndoManager::Undo(),
       // TQUndoManager::Redo() methods only invokes the top level command as a whole.
       //
       // Merge method is analogous to logical join operation.
       //
       // Note:  Merge method invokes redo action.

       Add(c, "merge");
    }

    //______________________________________________________________________________
    void TQCommand::Add(TObject *obj, Option_t *opt)
    {
       // Add command to the list of merged commands.
       //
       // Option string can contain substrings:
       //  "compress" - try to compress input command
       //  "radd" - execute redo action of input command
       //  "uadd" - execute undo action of input command

       if (!obj->InheritsFrom(TQCommand::Class())) return;

       TQCommand *o = (TQCommand *)obj;
       TQCommand *c = (TQCommand *)Last();
       TString ostr = opt;

       if (c) {
          if (c->CanCompress(o) || (c->IsEqual(o) && ostr.Contains("compress"))) {
             c->Compress(o);
             return;
          }
       }
       TList::AddLast(o, opt);
       if (o->CanRedo() && ostr.Contains("radd")) o->Redo();
       if (o->CanUndo() && ostr.Contains("uadd")) o->Undo();
    }

    //______________________________________________________________________________
    Bool_t TQCommand::CanCompress(TQCommand *c) const
    {
       // By default, commands can be compressed if they are:
       //
       //  - equal
       //  - setter commands
       //
       // More complicated commands might want to override this function.

       return (IsEqual(c) && IsSetter());
    }

    //______________________________________________________________________________
    void TQCommand::Compress(TQCommand *c)
    {
       // Compress command. Compression is analogous to arithmetic "addition operation".
       //
       // Note:
       //    - The compressed command will be deleted.
       //    - Execution Compress method invokes Redo action with new redo arguments
       //      inheritied from compressed command.
       //
       // More complicated commands might want to override this function.

       for (int i = 0; i < fNRargs; i++) {
          fRedoArgs[i] = c->fRedoArgs[i];
       }
       Redo();
       fStatus--;   //do not change the state of command
       delete c;
    }

    //______________________________________________________________________________
    Bool_t TQCommand::IsEqual(const TObject* obj) const
    {
       // Equal comparison. The commands are equal if they are
       // applied to the same object and have the same Redo/Undo actions
       //
       // More complicated commands might want to override this function.

       if (!obj->InheritsFrom(TQCommand::Class())) return kFALSE;
       TQCommand *c = (TQCommand *)obj;
       if (!fRedo || !fUndo || (c->GetObject() != fObject)) return kFALSE;

       TString cname = fRedo->GetClassName();
       TString rname = fRedo->GetName();

       return ((cname == c->GetRedo()->GetClassName()) &&
               (rname == c->GetRedo()->GetName()));
    }

    //______________________________________________________________________________
    Bool_t TQCommand::IsSetter() const
    {
       // Returns kTRUE is command if Redo is the same as Undo function
       // and is the setter action.
       //
       // By default, all functions with names like "SetXXX" or "setXXX"
       // considered as setters. Another type of setters are Move, Resize operations
       //
       // More complicated commands might want to override this function.

       TString redo = GetRedoName();
       TString undo = GetUndoName();

       if (!redo || !undo || (redo != undo)) return kFALSE;

       return (redo.BeginsWith("Set") ||
               redo.BeginsWith("set") ||
               redo.BeginsWith("Move") ||
               redo.BeginsWith("move") ||
               redo.BeginsWith("Resize") ||
               redo.BeginsWith("resize"));
    }

    //______________________________________________________________________________
    void TQCommand::SetArgs(Int_t narg, ...)
    {
       // Set do/redo and undo parameters. The format is
       //    SetArgs(number_of_params, redo_params, undo_params)
       //
       // Example:
       //     move_command->SetArgs(2, 100, 100, 200, 200);
       //      2 params, (100,100) - do/redo position, (200,200) - undo position

       if (narg < 0) {
          return;
       } else if (!narg) {  // no arguments
          fNRargs = fNUargs = narg;
          return;
       }

       va_list ap;
       va_start(ap, narg);

       if (fNRargs != narg ) {
          delete fRedoArgs;
       }
       fRedoArgs = new Long_t[narg];

       if (fNUargs != narg ) {
          delete fUndoArgs;
       }
       fUndoArgs = new Long_t[narg];

       fNRargs = fNUargs = narg;

       Int_t i;
       for (i = 0; i < fNRargs; i++) {
          fRedoArgs[i] = va_arg(ap, Long_t);
       }
       for (i = 0; i < fNUargs; i++) {
          fUndoArgs[i] = va_arg(ap, Long_t);
       }
       va_end(ap);
    }

    //______________________________________________________________________________
    void TQCommand::SetRedoArgs(Int_t narg, ...)
    {
       // Set redo parameters. The format is
       //    SetRedoArgs(number_of_params, params)
       //
       // Example:
       //     move_command->SetRedoArgs(2, 100, 100);

       if (narg < 0) {
          return;
       } else if (!narg) {  // no arguments
          fNRargs = 0;
          return;
       }

       va_list ap;
       va_start(ap, narg);

       if (fNRargs != narg ) {
          delete fRedoArgs;
       }
       fRedoArgs = new Long_t[narg];

       fNRargs = narg;

       for (int i = 0; i < fNRargs; i++) {
          fRedoArgs[i] = va_arg(ap, Long_t);
       }
       va_end(ap);
    }

    //______________________________________________________________________________
    void TQCommand::SetUndoArgs(Int_t narg, ...)
    {
       // Set undo parameters. The format is
       //    SetUndoArgs(number_of_params, params)
       //
       // Example:
       //     move_command->SetUndoArgs(2, 200, 200);

       if (narg < 0) {
          return;
       } else if (!narg) {  // no arguments
          fNUargs = narg;
          return;
       }

       va_list ap;
       va_start(ap, narg);

       if (fNUargs != narg ) {
          delete fUndoArgs;
       }
       fUndoArgs = new Long_t[narg];

       fNUargs = narg;

       for (int i = 0; i < fNUargs; i++) {
          fUndoArgs[i] = va_arg(ap, Long_t);
       }
       va_end(ap);
    }

    //______________________________________________________________________________
    Bool_t TQCommand::CanRedo() const
    {
       // Returns kTRUE if Redo action is possible, kFALSE if it's not.
       // By default, only single sequential redo action is possible.

       return (fStatus <= 0);
    }

    //______________________________________________________________________________
    Bool_t TQCommand::CanUndo() const
    {
       // Returns kTRUE if Undo action is possible, kFALSE if it's not.
       // By default, only single tial undo action is possible.

       return (fStatus > 0);
    }

    //______________________________________________________________________________
    void TQCommand::Redo(Option_t *)
    {
       // Execute command and then smerged commands

       Bool_t done = kFALSE;
       fState = 1;

       gActiveCommand = this;

       if (fNRargs > 0) {
          if (fRedo) {
             fRedo->ExecuteMethod(fRedoArgs, fNRargs);
             done = kTRUE;
          }
       } else if (!fNRargs) {
          if (fRedo) {
             fRedo->ExecuteMethod();
             done = kTRUE;
          }
       }

       // execute merged commands
       TObjLink *lnk = fFirst;
       while (lnk) {
          TQCommand *c = (TQCommand *)lnk->GetObject();
          c->Redo();
          done = kTRUE;
          lnk = lnk->Next();
       }

       if (done) Emit("Redo()");
       fStatus++;
       fState = 0;
       gActiveCommand = 0;
    }

    //______________________________________________________________________________
    void TQCommand::Undo(Option_t *)
    {
       // Unexecute all merged commands and the command.
       // Merged commands are executed in reverse order.

       Bool_t done = kFALSE;
       fState = -1;

       gActiveCommand = this;

       // unexecute merged commands
       TObjLink *lnk = fLast;
       while (lnk) {
          TQCommand *c = (TQCommand *)lnk->GetObject();
          TString opt = lnk->GetOption();
          TObjLink *sav = lnk->Prev();
          c->Undo();
          done = kTRUE;
          if (opt.Contains("remove")) {   // remove  command
             delete lnk->GetObject();
             Remove(lnk);
          }
          lnk = sav;
       }
       if (fNUargs > 0) {
          if (fUndo) {
             fUndo->ExecuteMethod(fUndoArgs, fNUargs);
             done = kTRUE;
          }
       } else if (!fNUargs) {
          if (fUndo) {
             fUndo->ExecuteMethod();
             done = kTRUE;
          }
       }

       if (done) Emit("Undo()");
       fStatus--;
       fState = 0;
       gActiveCommand = 0;
    }

    //______________________________________________________________________________
    const char *TQCommand::GetName() const
    {
       // Returns the command name. Default name is "ClassName::RedoName(args)"
       // If list of merged commands is not empty the name is
       // "ClassName::RedoName(args):cname1:cname2 ..."

       const Int_t maxname = 100;

       if (!fName.IsNull()) return fName.Data();

       TString name;

       if (fRedo) {
          if (fRedo->GetClassName()) {
             name = fRedo->GetClassName();
          }
          name += "::";
          name += fRedo->GetName();
       }
       TQCommand *c;
       TObjLink *lnk = fFirst;

       while (lnk && (fName.Length() < maxname)) {
          c = (TQCommand *)lnk->GetObject();
          name += ":";
          name += c->GetName();
          lnk = lnk->Next();
       }

       // trick against "constness"
       TQCommand *m = (TQCommand *)this;
       m->fName = name;

       return name.Data();
    }

    //______________________________________________________________________________
    const char *TQCommand::GetTitle() const
    {
       // Returns command description.
       // By default, "ClassName::RedoName(args)_ClassName::UndoName(args)"

       if (!fTitle.IsNull()) return fTitle.Data();

       TString title = GetName();

       if (fUndo) {
          title += "_";
          title += fUndo->GetClassName();
          title += "::";
          if (fUndo->GetName())  title += fUndo->GetName();
       }
       return title.Data();
    }

    //______________________________________________________________________________
    const char *TQCommand::GetRedoName() const
    {
       // Returns the name of redo command

       return (fRedo ? fRedo->GetName() : 0);
    }

    //______________________________________________________________________________
    const char *TQCommand::GetUndoName() const
    {
       // Returns the name of undo command

       return (fUndo ? fUndo->GetName() : 0);
    }

    //______________________________________________________________________________
    Long_t *TQCommand::GetRedoArgs() const
    {
       // Returns a pointer to array of redo arguments

       return fRedoArgs;
    }

    //______________________________________________________________________________
    Long_t *TQCommand::GetUndoArgs() const
    {
       // Returns a pointer to array of undo arguments

       return fUndoArgs;
    }

    //______________________________________________________________________________
    Int_t TQCommand::GetNRargs() const
    {
       // Returns a number of redo arguments

       return fNRargs;
    }

    //______________________________________________________________________________
    Int_t TQCommand::GetNUargs() const
    {
       // Returns a number of undo arguments

       return fNUargs;
    }

    //______________________________________________________________________________
    void *TQCommand::GetObject() const
    {
       // Returns an object for which undo redo acions are applied

       return fObject;
    }

    //______________________________________________________________________________
    Int_t TQCommand::GetStatus() const
    {
       // Returns a number of sequential undo or redo operations

       return fStatus;
    }

    //______________________________________________________________________________
    Bool_t TQCommand::IsMacro() const
    {
       // Returns kTRUE if neither redo nor undo action specified

       return (!fRedo && !fUndo);
    }

    //______________________________________________________________________________
    Bool_t TQCommand::IsUndoing() const
    {
       // Undo action is in progress

       return (fState < 0);
    }

    //______________________________________________________________________________
    Bool_t TQCommand::IsRedoing() const
    {
       // Redo action is in progress

       return (fState > 0);
    }

    //______________________________________________________________________________
    Bool_t TQCommand::IsExecuting() const
    {
       // Returns kTRUE if command execution is in progress

       return fState;
    }

    //______________________________________________________________________________
    void TQCommand::SetName(const char *name)
    {
       // Sets name of the command

       fName = name;
    }

    //______________________________________________________________________________
    void TQCommand::SetTitle(const char *title)
    {
       // Sets description of the command

       fTitle = title;
    }

    //______________________________________________________________________________
    void TQCommand::ls(Option_t *) const
    {
       // ls this command and merged commands

       TString name = GetName();
       printf("%d %s\n", fStatus, name.Data());

       TObjLink *lnk = fFirst;
       while (lnk) {
          printf("\t");
          lnk->GetObject()->ls();
          lnk = lnk->Next();
       }
    }

    //______________________________________________________________________________
    void TQCommand::Print(Option_t *option) const
    {
       // print this command and all merged commands

       ls(option);
    }

    ///////////////////////////////////////////////////////////////////////////////
    //______________________________________________________________________________
     TQUndoManager::TQUndoManager() : TQCommand(0, 0, 0, 0)
    {
       // Constructor

       fCursor = 0;
       fLimit = kMaxUInt;   // maximum value for UInt_t
       fLogging = kFALSE;
       fLogBook = 0;
       fCurrent = 0;
    }

    //______________________________________________________________________________
     TQUndoManager::~TQUndoManager()
    {
       // Destructor

       Delete();

       if (fLogBook) {
          delete fLogBook;
       }
    }

    //______________________________________________________________________________
     void TQUndoManager::ls(Option_t *option) const
    {
       // Lists all commands in stack

       if (!IsEmpty()) {
          TObjLink *lnk = fFirst;
          while (lnk) {
             if (lnk == fCursor) {
                printf("->");
             } else {
                printf("  ");
             }
             TQCommand *com = (TQCommand*)lnk->GetObject();
             com->ls(option);
             lnk = lnk->Next();
          }
       }
    }

    //______________________________________________________________________________
     void TQUndoManager::Print(Option_t *option) const
    {
       // Prints all commands in history stack

       ls(option);
    }

    //______________________________________________________________________________
     void  TQUndoManager::SetLogging(Bool_t on)
    {
       // Start logging. Delete all previous log records
       // Note: logging is not implemented yet

       fLogging = on;

       if (fLogging) {
          if (fLogBook) {
             fLogBook->Delete();
          } else {
             fLogBook = new TList();
          }
       }
    }

    //______________________________________________________________________________
     void TQUndoManager::Add(TObject *obj, Option_t *opt)
    {
       // Add command to the stack of commands.
       // Command's redo action will be executed.
       //
       // option string can contain the following substrings:
       //    "merge" - input command will be merged
       //    "compress" - input command will be compressed

       if (!obj->InheritsFrom(TQCommand::Class())) return;

       TQCommand *o = (TQCommand *)obj;
       TQCommand *c;
       Bool_t onredo = fCursor && fCursor->Next();
       TString ostr = onredo ? "1radd" : "0radd"; // execute redo on add
       if (opt) ostr += opt;

       if (fState) { // undo/redo in progress
          c = fCurrent;
          if (c) {
             fCurrent = o;
             c->Add(o, "remove");   // add nested command
          }
          return;
       }

       // delete all commands after cursor position
       if (fCursor && fCursor->Next()) {
          TObjLink *lnk = fCursor->Next();
          TObjLink *sav;
          while (lnk) {
             sav = lnk->Next();
             delete lnk->GetObject();
             Remove(lnk);
             lnk = sav;
          }
       }

       c = GetCursor();
       if (c) {
          if (c->CanCompress(o) || c->CanMerge(o) ||
              ostr.Contains("merge") || ostr.Contains("compress")) {
             fState = 1;
             c->Add(o, ostr.Data());
             fState = 0;
             return;
          }
       }

       TList::AddLast(obj, ostr.Data());
       fCursor = fLast;
       Redo(ostr.Data());

       if ((fSize > 0) && ((UInt_t)fSize > fLimit)) {
          Remove(fFirst);
       }
    }

    //______________________________________________________________________________
     void TQUndoManager::CurrentChanged(TQCommand *c)
    {
       // emit signal

       Emit("CurrentChanged(TQCommand*)", (long)c);
    }

    //______________________________________________________________________________
     void TQUndoManager::Undo(Option_t *option)
    {
       // Performs undo action. Move cursor position backward in history stack

       Bool_t done = kFALSE;
       if (!CanUndo()) return;

       TQCommand *sav = fCurrent;
       TQCommand *c = (TQCommand*)fCursor->GetObject();

       if (c->CanUndo()) {
          fState = -1;
          fCurrent = c;
          fCurrent->Undo(option);
          fState = 0;
          done = kTRUE;
          fCursor = fCursor->Prev() ? fCursor->Prev() : fFirst;
       } else {
          fCursor = fCursor->Prev();
          fCurrent = (TQCommand*)fCursor->GetObject();
          fState = -1;
          fCurrent->Undo(option);
          fState = 0;
          done = kTRUE;
       }
       if (done && fLogging && fLogBook) {
          fLogBook->Add(new TQCommand(*fCurrent));
       }
       if (sav != fCurrent) CurrentChanged(fCurrent);
    }

    //______________________________________________________________________________
     void TQUndoManager::Redo(Option_t *option)
    {
       // Performs redo action. Move cursor position forward in history stack

       Bool_t done = kFALSE;
       if (!CanRedo()) return;

       TQCommand *sav = fCurrent;
       TQCommand *c = (TQCommand*)fCursor->GetObject();

       if (c->CanRedo()) {
          fState = 1;
          fCurrent = c;
          fCurrent->Redo(option);
          fState = 0;
          done = kTRUE;
          fCursor = fCursor->Next() ? fCursor->Next() : fLast;
       } else {
          fCursor = fCursor->Next();
          fCurrent = (TQCommand*)fCursor->GetObject();
          fState = 1;
          fCurrent->Redo(option);
          fState = 0;
          done = kTRUE;
       }
       if (done && fLogging && fLogBook) {
          fLogBook->Add(new TQCommand(*fCurrent));
       }
       if (sav != fCurrent) CurrentChanged(fCurrent);
    }

    //______________________________________________________________________________
     Bool_t TQUndoManager::CanRedo() const
    {
       // Returns kTRUE if redo action is possible

       if (!fCursor) return kFALSE;

       TQCommand *c = (TQCommand*)fCursor->GetObject();
       if (c->CanRedo()) return kTRUE;

       c = fCursor->Next() ? (TQCommand*)fCursor->Next()->GetObject() : 0;
       return (c && c->CanRedo());
    }

    //______________________________________________________________________________
     Bool_t TQUndoManager::CanUndo() const
    {
       // Returns kTRUE if undo action is possible

       if (!fCursor) return kFALSE;

       TQCommand *c = (TQCommand*)fCursor->GetObject();
       if (c->CanUndo()) return kTRUE;

       c = fCursor->Prev() ? (TQCommand*)fCursor->Prev()->GetObject() : 0;
       return (c && c->CanUndo());
    }

    //_____________________________________________________________________________
     Bool_t TQUndoManager::IsLogging() const
    {
       // Returns kTRUE if logging is ON

       return fLogging;
    }

    //_____________________________________________________________________________
     TQCommand *TQUndoManager::GetCurrent() const
    {
       // Returns the last executed command

       return fCurrent;
    }

    //_____________________________________________________________________________
     TQCommand *TQUndoManager::GetCursor() const
    {
       // Returns a command correspondent to the current cursor position in stack

       return (TQCommand*)(fCursor ? fCursor->GetObject() : 0);
    }

    //_____________________________________________________________________________
     void TQUndoManager::SetLimit(UInt_t limit)
    {
       // Returns a maximum number of commands which could be located in stack

       fLimit = limit;
    }

    //_____________________________________________________________________________
     UInt_t TQUndoManager::GetLimit() const
    {
       // Returns a maximum number of commands which  could be located in stack

       return fLimit;
    }