logo

class

fwt::Command

sys::Obj
  fwt::Command
//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   22 Jul 08  Brian Frank  Creation
//

**
** Command packages up the diplay name, icon, execution,
** and undo support for a user command.  You can create a
** command two ways:
**   1. use a closure (or any function) for `onInvoke`
**   2. subclass Command and override `invoke`
**
** If the command supports undo, then you must create a
** a subclass and override `undo`.
**
** Commands are often used to centralize control of multiple
** widgets.  For example if a Command is associated with
** both a menu item and a toolbar button, then disabling the
** command will disable both the menu item and toolbar button.
**
class Command
{

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  **
  ** Name of the command.
  **
  Str name

  **
  ** Icon of the command or null.  Typically a 16x16.
  **
  Image icon

  **
  ** The function to invoke when the command is executed.  If
  ** null, then `invoke` must be overridden.
  **
  |Event event| onInvoke

//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////

  **
  ** Construct a command with the specified onInvoke function.
  ** If onInvoke is not specified, then the `invoke` method
  ** must be overridden to execute the command.
  **
  new make(Str name := null, Image icon := null, |Event event| onInvoke := null)
  {
    this.name = name
    this.icon = icon
    this.onInvoke = onInvoke
  }

//////////////////////////////////////////////////////////////////////////
// Enabled
//////////////////////////////////////////////////////////////////////////

  **
  ** The enable state of the command automatically controls
  ** the enabled state of all the registered widgets.
  **
  Bool enabled := true
  {
    set
    {
      if (@enabled == val) return
      @enabled = val
      registry.each |Widget w| { w.enabled = val }
    }
  }

  **
  ** Get the associated widgets with this command.  Widgets are
  ** automatically associated with their command field is set.
  **
  Widget[] widgets() { return registry.ro }

  **
  ** Register a widget with this command.  This is done
  ** automatically by the widget.  You only need to call
  ** this method if you are developing a custom widget.
  **
  Void register(Widget w) { registry.add(w) }

  **
  ** Unregister a widget with this command.  This is done
  ** automatically by the widget.  You only need to call
  ** this method if you are developing a custom widget.
  **
  Void unregister(Widget w) { registry.removeSame(w) }

  private Widget[] registry := Widget[,]

//////////////////////////////////////////////////////////////////////////
// Invoke
//////////////////////////////////////////////////////////////////////////

  **
  ** Invoke the command.  If the user event is known
  ** then is passed, otherwise it might be null.
  **
  virtual Void invoke(Event event)
  {
    if (onInvoke == null) throw UnsupportedErr("Must set onInvoke or override invoke: $name")
    onInvoke.call1(event)
  }

//////////////////////////////////////////////////////////////////////////
// Undo
//////////////////////////////////////////////////////////////////////////

  **
  ** Return if the command can be undone.  Default implementation
  ** returns true is the `undo` method has been overridden.
  **
  virtual Bool undoable()
  {
    return type.method("undo").parent != Command#
  }

  **
  ** This method is invoked when the command invoked as
  ** a redo.  It is not called on the first invocation.
  ** Default calls `invoke` with a null event.
  **
  virtual Void redo()
  {
    invoke(null)
  }

  **
  ** This method is invoked to undo the command.  This
  ** method is only used if `undoable` returns true.
  **
  virtual Void undo()
  {
    throw UnsupportedErr("Command not undoable $name")
  }

}