2 Maps Qt signals to Command objects.
4 The command factory connects to the global notifier and
5 creates commands objects as registered signals are
8 The factory itself is undoable in that it responds to
9 the undo and redo signals and manages the undo/redo stack.
13 from cola
import signals
14 from cola
import errors
15 from cola
.decorators
import memoize
20 """Return a static instance of the command factory."""
21 return CommandFactory()
24 def SLOT(signal
, *args
, **opts
):
26 Returns a callback that broadcasts a message over the notifier.
29 def broadcast(*local_args
, **opts
):
30 cola
.notifier().broadcast(signal
, *args
, **opts
)
34 class CommandFactory(object):
35 def __init__(self
, context
=None):
36 """Setup the undo/redo stacks and register for notifications."""
40 self
.signal_to_command
= {}
42 self
.context
= context
44 def has_command(self
, signal
):
45 return signal
in self
.signal_to_command
47 def add_command(self
, signal
, command
):
48 """Register a signal/command pair."""
49 self
.signal_to_command
[signal
] = command
51 def add_global_command(self
, signal
, command
):
52 """Register a global signal/command pair."""
53 self
.add_command(signal
, command
)
54 cola
.notifier().connect(signal
, self
.cmdrunner(signal
))
56 def add_command_wrapper(self
, cmd_wrapper
):
57 self
.callbacks
.update(cmd_wrapper
.callbacks
)
59 def prompt_user(self
, name
, *args
, **opts
):
61 return self
.callbacks
[name
](*args
, **opts
)
63 raise NotImplementedError('No callback for "%s' % name
)
66 """Clear the undo and redo stacks."""
70 def cmdrunner(self
, signal
):
71 """Return a function to create and run a signal's command."""
72 def run(*args
, **opts
):
73 return self
.do(signal
, *args
, **opts
)
76 def do(self
, signal
, *args
, **opts
):
77 """Given a signal and arguments, run its corresponding command."""
78 cmdclass
= self
.signal_to_command
[signal
]
79 cmdobj
= cmdclass(*args
, **opts
)
80 cmdobj
.context
= self
.context
81 # TODO we disable undo/redo for now; views just need to
82 # inspect the stack and add menu entries when we enable it.
83 ok
, result
= self
._do
(cmdobj
)
84 if ok
and self
.undoable
and cmdobj
.is_undoable():
85 self
.undostack
.append(cmdobj
)
88 def _do(self
, cmdobj
):
91 except errors
.UsageError
, e
:
92 self
.prompt_user(signals
.information
, e
.title
, e
.message
)
98 """Undo the last command and add it to the redo stack."""
100 cmdobj
= self
.undostack
.pop()
101 result
= cmdobj
.undo()
102 self
.redostack
.append(cmdobj
)
105 print 'warning: undo stack is empty, doing nothing'
109 """Redo the last command and add it to the undo stack."""
111 cmdobj
= self
.redostack
.pop()
112 ok
, result
= self
._do
(cmdobj
)
113 if ok
and cmdobj
.is_undoable():
114 self
.undo
.append(cmdobj
)
116 self
.redostack
.push(cmdobj
)
119 print 'warning: redo stack is empty, doing nothing'
121 def is_undoable(self
):
122 """Does the undo stack contain any commands?"""
123 return bool(self
.undostack
)
125 def is_redoable(self
):
126 """Does the redo stack contain any commands?"""
127 return bool(self
.redostack
)