/********************************************************************************
*                                                                               *
*                  U n d o / R e d o - a b l e   C o m m a n d                  *
*                                                                               *
*********************************************************************************
* Copyright (C) 2000,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or                 *
* modify it under the terms of the GNU Lesser General Public                    *
* License as published by the Free Software Foundation; either                  *
* version 2.1 of the License, or (at your option) any later version.            *
*                                                                               *
* This library 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             *
* Lesser General Public License for more details.                               *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public              *
* License along with this library; if not, write to the Free Software           *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
*********************************************************************************
* $Id: FXUndoList.h,v 1.38 2006/01/22 17:58:12 fox Exp $                        *
********************************************************************************/
#ifndef FXUNDOLIST_H
#define FXUNDOLIST_H

#ifndef FXOBJECT_H
#include "FXObject.h"
#endif

namespace FX {


class FXUndoList;
class FXCommandGroup;


/**
* Base class for undoable commands.  Each undo records all the
* information necessary to undo as well as redo a given operation.
* Since commands are derived from FXObject, subclassed commands can
* both send and receive messages (like ID_GETINTVALUE, for example).
*/
class FXAPI FXCommand : public FXObject {
  FXDECLARE_ABSTRACT(FXCommand)
  friend class FXUndoList;
  friend class FXCommandGroup;
private:
  FXCommand *next;
private:
  FXCommand(const FXCommand&);
  FXCommand &operator=(const FXCommand&);
protected:
  FXCommand():next(NULL){}
public:

  /**
  * Undo this command; this should save the
  * information for a subsequent redo.
  */
  virtual void undo() = 0;

  /**
  * Redo this command; this should save the
  * information for a subsequent undo.
  */
  virtual void redo() = 0;

  /**
  * Return the size of the information in the undo record.
  * The undo list may be trimmed to limit memory usage to
  * a certain limit.  The value returned should include
  * the size of the command record itself as well as any
  * data linked from it.
  */
  virtual FXuint size() const;

  /**
  * Name of the undo command to be shown on a button;
  * for example, "Undo Delete".
  */
  virtual FXString undoName() const;

  /**
  * Name of the redo command to be shown on a button;
  * for example, "Redo Delete".
  */
  virtual FXString redoName() const;

  /**
  * Return TRUE if this command can be merged with previous undo
  * commands.  This is useful to combine e.g. multiple consecutive
  * single-character text changes into a single block change.
  * The default implementation returns FALSE.
  */
  virtual bool canMerge() const;

  /**
  * Called by the undo system to try and merge the new incoming command
  * with this command; should return TRUE if merging was possible.
  * The default implementation returns FALSE.
  */
  virtual bool mergeWith(FXCommand* command);

  /// Delete undo command
  virtual ~FXCommand(){}
  };



/**
* Group of undoable commands.  A group may comprise multiple
* individual actions which together undo (or redo) a larger
* operation.  Even larger operations may be built by nesting
* multiple undo groups.
*/
class FXAPI FXCommandGroup : public FXCommand {
  FXDECLARE(FXCommandGroup)
  friend class FXUndoList;
private:
  FXCommand      *undolist;
  FXCommand      *redolist;
  FXCommandGroup *group;
private:
  FXCommandGroup(const FXCommandGroup&);
  FXCommandGroup &operator=(const FXCommandGroup&);
public:

  /// Construct initially empty undo command group
  FXCommandGroup():undolist(NULL),redolist(NULL),group(NULL){}

  /// Return TRUE if empty
  bool empty(){ return !undolist; }

  /// Undo whole command group
  virtual void undo();

  /// Redo whole command group
  virtual void redo();

  /// Return the size of the command group
  virtual FXuint size() const;

  /// Delete undo command and sub-commands
  virtual ~FXCommandGroup();
  };



/**
* The Undo List class manages a list of undoable commands.
*/
class FXAPI FXUndoList : public FXCommandGroup {
  FXDECLARE(FXUndoList)
private:
  FXint      undocount;     // Number of undo records
  FXint      redocount;     // Number of redo records
  FXint      marker;        // Marker value
  FXuint     space;         // Space taken up by all the undo records
  bool       working;       // Currently busy with undo or redo
private:
  FXUndoList(const FXUndoList&);
  FXUndoList &operator=(const FXUndoList&);
public:
  long onCmdUndo(FXObject*,FXSelector,void*);
  long onUpdUndo(FXObject*,FXSelector,void*);
  long onCmdRedo(FXObject*,FXSelector,void*);
  long onUpdRedo(FXObject*,FXSelector,void*);
  long onCmdClear(FXObject*,FXSelector,void*);
  long onUpdClear(FXObject*,FXSelector,void*);
  long onCmdRevert(FXObject*,FXSelector,void*);
  long onUpdRevert(FXObject*,FXSelector,void*);
  long onCmdUndoAll(FXObject*,FXSelector,void*);
  long onCmdRedoAll(FXObject*,FXSelector,void*);
  long onUpdUndoCount(FXObject*,FXSelector,void*);
  long onUpdRedoCount(FXObject*,FXSelector,void*);
public:
  enum{
    ID_CLEAR=FXWindow::ID_LAST,
    ID_REVERT,
    ID_UNDO,
    ID_REDO,
    ID_UNDO_ALL,
    ID_REDO_ALL,
    ID_UNDO_COUNT,
    ID_REDO_COUNT,
    ID_LAST
    };
public:

  /**
  * Make new empty undo list, initially unmarked.
  */
  FXUndoList();

  /**
  * Cut the redo list.
  * This is automatically invoked when a new undo command is added.
  */
  void cut();

  /**
  * Add new command, executing it if desired. The new command will be merged
  * with the previous command if merge is TRUE and we're not at a marked position
  * and the commands are mergeable.  Otherwise the new command will be appended
  * after the last undo command in the currently active undo group.
  * If the new command is successfully merged, it will be deleted.  Furthermore,
  * all redo commands will be deleted since it is no longer possible to redo
  * from this point.
  */
  void add(FXCommand* command,bool doit=false,bool merge=true);

  /**
  * Begin undo command sub-group. This begins a new group of commands that
  * are treated as a single command.  Must eventually be followed by a
  * matching end() after recording the sub-commands.  The new sub-group
  * will be appended to its parent group's undo list when end() is called.
  */
  void begin(FXCommandGroup *command);

  /**
  * End undo command sub-group.  If the sub-group is still empty, it will
  * be deleted; otherwise, the sub-group will be added as a new command
  * into parent group.
  * A matching begin() must have been called previously.
  */
  void end();

  /**
  * Abort the current command sub-group being compiled.  All commands
  * already added to the sub-groups undo list will be discarded.
  * Intermediate command groups will be left intact.
  */
  void abort();

  /**
  * Undo last command. This will move the command to the redo list.
  */
  virtual void undo();

  /**
  * Redo next command. This will move the command back to the undo list.
  */
  virtual void redo();

  /// Undo all commands
  void undoAll();

  /// Redo all commands
  void redoAll();

  /// Revert to marked
  void revert();

  /// Can we undo more commands
  bool canUndo() const;

  /// Can we redo more commands
  bool canRedo() const;

  /// Can revert to marked
  bool canRevert() const;

  /**
  * Return TRUE if currently inside undo or redo operation; this
  * is useful to avoid generating another undo command while inside
  * an undo operation.
  */
  bool busy() const { return working; }

  /// Current top level undo command
  FXCommand* current() const { return undolist; }

  /**
  * Return name of the first undo command available; if no
  * undo command available this will return the empty string.
  */
  virtual FXString undoName() const;

  /**
  * Return name of the first redo command available; if no
  * Redo command available this will return the empty string.
  */
  virtual FXString redoName() const;

  /// Number of undo records
  FXint undoCount() const { return undocount; }

  /// Number of redo records
  FXint redoCount() const { return redocount; }

  /// Size of undo information
  virtual FXuint size() const;

  /**
  * Clear list, and unmark all states.
  * All undo and redo information will be destroyed.
  */
  void clear();

  /**
  * Trim undo list down to at most nc commands.
  * Call this periodically to prevent the undo-list from growing
  * beyond a certain number of records.
  */
  void trimCount(FXint nc);

  /**
  * Trim undo list down to at most size sz.
  * Call this periodically to prevent the undo-list from growing
  * beyond a certain amount of memory.
  */
  void trimSize(FXuint sz);

  /**
  * Mark the current state of the undo list, which is initially unmarked.
  * There can be only one active mark at any time.  Call mark() at any
  * time when you know the document to be "clean"; for example when you
  * save the document to disk.
  */
  void mark();

  /**
  * Unmark all states in the undo list.
  */
  void unmark();

  /**
  * Check if the current state was marked, if the application has returned
  * to the previously marked state.
  */
  bool marked() const;
  };


}

#endif
