]> git.neil.brown.name Git - edlib.git/blob - python/lib-macro.py
TODO: clean out done items.
[edlib.git] / python / lib-macro.py
1 # -*- coding: utf-8 -*-
2 # Copyright Neil Brown (c)2020-2023 <neil@brown.name>
3 # May be distributed under terms of GPLv2 - see file:COPYING
4 #
5 # This module supports keyboard-macros, both recording and replaying.
6 # To start recording, we request Keystroke-notify from the display and record
7 # keystrokes until asked to stop, and which point we discard the last couple
8 # as advised.  The set of keystrokes, as a comma-separated string, is added
9 # to *Macro History*
10 # Individual macros in this history can be named with attributes at the start
11 # of the line.  A macro can then be accessed by index from end, or by name.
12 # A macro can be replayed by sending keystrokes until one returns an error.
13 #
14 # While capturing keystrokes we register a pane to receive the notifications.
15 # This is a child of the root.
16 # Other functionality is available simply through global commands.
17 #
18
19 import edlib
20
21 docname = "*Macro History*"
22
23 # A macro is a list of keystroke separated by commas
24 # A keystroke can contain a comma, but only preceeded by a '-'.
25
26 def macro_join(l):
27     return ','.join(l)
28
29 def macro_split(mac):
30     l = []
31     st = 0
32     while mac:
33         c = mac.find(',', st)
34         if c < 0:
35             l.append(mac)
36             mac = ""
37         elif c == 0:
38             mac = mac[1:]
39         elif mac[c-1] == '-':
40             st = c+1
41         else:
42             l.append(mac[:c])
43             mac = mac[c+1:]
44             st = 0
45     return l
46
47 class CapturePane(edlib.Pane):
48     def __init__(self, focus):
49         root = focus
50         while root.parent != root:
51             root = root.parent
52         edlib.Pane.__init__(self, root)
53         focus.call("Window:request:Keystroke-notify", self)
54         focus.call("Window:request:macro:capture-active", self)
55         focus.call("Window:request:macro:capture-done", self)
56         focus.call("Mode:set-mode", str2 = "()")
57         self.line = []
58
59     def capture_active(self, key, focus, **a):
60         "handle:macro:capture-active"
61         return 1
62
63     def capture_key(self, key, focus, str, **a):
64         "handle:Keystroke-notify"
65         self.line.append(str)
66         return 1
67
68     def capture_done(self, key, focus, num, **a):
69         "handle:macro:capture-done"
70         ret = 0
71         if num > 0:
72             self.line[-num:] = []
73         if self.line:
74             ret = self.call("history:add", docname, macro_join(self.line));
75             if ret == 0:
76                 self.call("global-load-module", "lib-history")
77                 ret = self.call("history:add", docname, macro_join(self.line))
78             if ret == 0:
79                 ret = edlib.Efalse
80         focus.call("Mode:set-mode", str2 = "")
81         self.close()
82         return ret
83
84 def start_capture(key, focus, **a):
85     if focus.call("Window:notify:macro:capture-active") >= 1:
86         # capture currently active
87         return edlib.Efalse
88     CapturePane(focus)
89     if focus.call("Window:notify:macro:capture-active") >= 1:
90         # Good, it is active now
91         return 1
92     return edlib.Efail
93
94 def end_capture(key, focus, num, **a):
95     try:
96         if focus.call("Window:notify:macro:capture-active") <= 0:
97             # capture currently active
98             return edlib.Efalse
99     except edlib.commandfailed:
100         return edlib.Efalse
101     ret = focus.call("Window:notify:macro:capture-done", num)
102     if ret == 0:
103         return edlib.Efalse
104     if ret > 0:
105         return 1
106     return ret
107
108 def play_macro(key, focus, num, str, **a):
109     if str:
110         mac = focus.call("history:get-last", docname, str, ret='str')
111     else:
112         mac = focus.call("history:get-last", docname, num, ret='str')
113     if not mac:
114         return edlib.Efail
115     keys = macro_split(mac)
116     error = False
117     i = 0
118     while not error and i < len(keys):
119         try:
120             if focus.call("Keystroke", keys[i]) < 0:
121                 error = True
122             else:
123                 i += 1
124         except edlib.commandfailed:
125             error = True
126     if error:
127         return edlib.Efail
128     return 1
129
130 def name_macro(key, focus, num, str, **a):
131     # Give name 'str' to macro numbered 'num'
132     if num <= 0:
133         num = 1
134     focus.call("history:get-last", num, docname, str)
135
136 edlib.editor.call("global-set-command", "macro:capture", start_capture)
137 edlib.editor.call("global-set-command", "macro:finished", end_capture)
138 edlib.editor.call("global-set-command", "macro:replay", play_macro)
139 edlib.editor.call("global-set-command", "macro:name", name_macro)