1 """
2 MoinMoin Ascii Diagram Macro (based on AD perl script).
3 Senya Basin 1998,2004 (senya at exposol dot com)
4 Revision 0.2.20040109
5
6 Usage:
7 [[AD([Box1] -> [Box2])]]
8 [[AD(fg:blue [Box 1] -> [Box2] -(3,1)- [Box 3] | [Box 4|...two liner...] -> [Box 5] <-(5,2)-> )]]
9
10 """
11 import re, sys
12
13 DEBUG = 0
14 H_CHAR = '-'
15 V_CHAR = '|'
16 C_CHAR = '+'
17
18 class ADSettings:
19 pos_x = 0
20 pos_y = 1
21 obj_id = 0
22 tab_nx = 0
23 tab_ny = 0
24 boxes = {}
25 links_from = {}
26 cols_width = {}
27 rows_height = {}
28 cols_start = []
29 rows_start = []
30 fgcolor = 'black'
31 DX = 5
32 DY = 3
33 NN = 0
34
35 def make_line(l, hch=H_CHAR, cch=C_CHAR):
36 sline = cch
37 while l > 2:
38 l, sline = l-1, sline + hch
39 return sline + cch
40
41 def frame_text (text, vch=V_CHAR):
42
43 lines = text.split(V_CHAR);
44
45 smax = 0
46 for line in lines:
47 if len(line) > smax: smax = len(line)
48
49 for i in range(len(lines)):
50 ltmp = vch + ' ' + lines[i].ljust(smax) + ' ' + vch
51 if re.match('\s*\-+\s*', lines[i]):
52 lines[i] = ltmp.replace(' ','-')
53 else:
54 lines[i] = ltmp
55
56 return (smax + 4, lines)
57
58 def make_textbox (text, symbols=''):
59 if not symbols: symbols = H_CHAR + V_CHAR + C_CHAR
60 hchar, vchar, cchar = symbols
61
62 (width, lines) = frame_text(text, vchar);
63
64 sline = make_line(width, hchar, cchar);
65 lines.insert(0, sline)
66 lines.append(sline)
67 return (width, lines);
68
69 def parse_int(val, dflt):
70 if val:
71 return int(val)
72 else:
73 return int(dflt)
74
75 def add_box(s, x='', y='', text='', attr='', id=None):
76
77 s.obj_id = parse_int(id, s.obj_id + 1)
78 s.pos_x = parse_int(x, s.pos_x + 1)
79 s.pos_y = parse_int(y, s.pos_y)
80
81 hvc = ''
82 if attr:
83 m = re.match('hvc=(...)', attr)
84 if m: hvc = m.group(1)
85 m = re.match('id=(\d+)', attr)
86 if m: s.obj_id = m.group(1)
87
88 if DEBUG > 1: print "adding BOX: x=",s.pos_x,"; y=",s.pos_y,"; text='",text,"'; hvc=",hvc,"; id=",s.obj_id
89 (width, lines) = make_textbox(text, hvc)
90
91 height = len(lines)
92
93 s.boxes[s.obj_id] = (s.obj_id, s.pos_x, s.pos_y, text, attr, width, height, lines)
94
95 if not s.cols_width.has_key(s.pos_x) or width > s.cols_width[s.pos_x]:
96 s.cols_width[s.pos_x] = width
97
98 if not s.rows_height.has_key(s.pos_y) or height > s.rows_height[s.pos_y]:
99 s.rows_height[s.pos_y] = height
100
101 def add_link(s, from_id='', to_id='', start=H_CHAR, end=H_CHAR, attr=None):
102
103 from_id = parse_int(from_id, s.obj_id)
104 to_id = parse_int(to_id, s.obj_id + 1)
105
106 if not s.links_from.has_key(from_id):
107 s.links_from[from_id] = []
108
109 links = s.links_from[from_id]
110
111 links.append([from_id, to_id, start, end, attr])
112 if DEBUG > 1: print "found LINK: from=",from_id," to=",to_id," link=\""+start+end+"\" attr=",attr
113
114 def parse_ddl(s, args):
115
116 m = re.match('.*(fgcolor|fg):(\S+).*', args, re.I)
117 if m:
118 s.fgcolor = m.group(2)
119
120 m = re.match('.*DY:(\d+)\s.*', args, re.I)
121 if m:
122 s.DY = int(m.group(1))
123
124 m = re.match('.*DX:(\d+)\s.*', args, re.I)
125 if m:
126 s.DX = int(m.group(1))
127
128 m = re.match('.*NN:\s.*', args, re.I)
129 if m:
130 s.NN = 1
131
132 re_box = re.compile(r'\[(\d*),?(\d*),?"?(.*?)\s*?"?,?(\w*?=.*?)?\]')
133 re_link = re.compile(r'(\s?[<-])[-(]*(\d*),?(\d*)[-)]*([->])\s?')
134
135 for tok in re.findall('\[.+?\]|[<-].*?[->]\s|\s\|\s', args):
136 if DEBUG > 2: print "--- TOKEN:" + tok
137 m = re_box.match(tok)
138 if m:
139 (x,y,text,attr) = m.group(1,2,3,4)
140 add_box(s, x, y, text, attr)
141
142 m = re_link.match(tok)
143 if m:
144 (fr,to,start,end) = m.group(2,3,1,4)
145 add_link(s, fr, to, start.strip(), end.strip())
146
147 if re.match('\s\|\s', tok) :
148 s.pos_y += 1
149 s.pos_x = 0
150
151 s.tab_nx = len(s.cols_width.keys())
152 s.tab_ny = len(s.rows_height.keys())
153
154 for i in range(s.tab_nx + 1):
155 s.cols_start.append(0)
156
157 for i in range(2, s.tab_nx + 1):
158 s.cols_start[i] = s.cols_start[i-1] + s.cols_width[i-1] + s.DX;
159
160 for i in range(s.tab_ny + 1):
161 s.rows_start.append(0)
162
163 for i in range(2, s.tab_ny + 1):
164 s.rows_start[i] = s.rows_start[i-1] + s.rows_height[i-1] + s.DY;
165
166 return s.tab_nx * s.tab_ny
167
168 def draw_box(s, screen, box_id):
169 (box_id, tx, ty, text, attr, width, height, lines) = s.boxes[box_id]
170 for j in range(height):
171 yy = s.rows_start[ty] + j
172 for i in range(width):
173 xx = s.cols_start[tx] + i
174 screen[yy][xx] = lines[j][i]
175 if s.NN:
176 sid = repr(box_id)
177 for i in range(len(sid)):
178 screen[s.rows_start[ty]][s.cols_start[tx] + i] = sid[i]
179
180 def draw_hline (s, screen, x1, x2, y, c_start, c_end):
181
182 for x in range(x1, x2):
183 screen[y][x] = H_CHAR
184
185 if c_start: screen[y][x1] = c_start
186 if c_end: screen[y][x2-1] = c_end
187
188 def draw_h2line (s, screen, x1, x2, y, c_start, c_end):
189 draw_hline(s, screen, x1, x2+2, y+1, c_start, c_end)
190 screen[y][x1] = '|'
191 screen[y+1][x1] = '+'
192 screen[y][x2+1] = '|'
193 screen[y+1][x2+1] = '+'
194
195 def draw_vline (s, screen, x, y1, y2, c_vert=V_CHAR):
196 for y in range(y1, y2):
197 screen[y][x] = c_vert
198
199 def draw_link(s, screen, link):
200 (fr, to, start, end, attr) = link
201
202 (box_id1, tx1, ty1, undef, undef, w1, h1, undef) = s.boxes[fr]
203 if not box_id1:
204 return
205
206 (box_id2, tx2, ty2, undef, undef, w2, h2, undef) = s.boxes[to]
207 if not box_id1:
208 return
209
210 if ( (ty1 - ty2) == 0):
211 if (abs(tx2 - tx1) == 1):
212 if (tx1 < tx2):
213 x1 = s.cols_start[tx1] + w1
214 x2 = s.cols_start[tx2]
215 else:
216 x1 = s.cols_start[tx2] + w1
217 x2 = s.cols_start[tx1]
218
219 h = h2//2
220 if h1 < h2: h = h1//2
221 draw_hline(s, screen, x1, x2, s.rows_start[ty1] + h, start, end)
222
223 elif (abs(tx2 - tx1) > 1):
224 if (tx1 < tx2):
225 x1 = s.cols_start[tx1] + w1//2
226 x2 = s.cols_start[tx2] + w2//2 - 1
227 else:
228 x1 = s.cols_start[tx2] + w2//2
229 x2 = s.cols_start[tx1] + w1//2 - 1
230
231 h = h1
232 if h1 < h2: h2
233 draw_h2line(s, screen, x1, x2, s.rows_start[ty1] + h, start, end)
234
235 elif ((tx1 - tx2)==0 and abs(ty2 - ty1)==1):
236 if (ty1 < ty2):
237 y1 = s.rows_start[ty1] + s.rows_height[ty1]
238 y2 = s.rows_start[ty2]
239 else:
240 y1 = s.rows_start[ty2] + s.rows_height[ty2]
241 y2 = s.rows_start[ty1]
242
243 w = w2//2
244 if w1 < w2: w = w1//2
245 draw_vline(s, screen, s.cols_start[tx1] + w, y1, y2)
246
247 else:
248 if (tx1 < tx2):
249 x1 = s.cols_start[tx1] + w1
250 x2 = s.cols_start[tx2]
251 else:
252 x1 = s.cols_start[tx2] + w1
253 x2 = s.cols_start[tx1]
254
255 if (ty1 < ty2):
256 y1 = s.rows_start[ty1] + h1//2
257 y2 = s.rows_start[ty2] + h2//2
258 else:
259 y1 = s.rows_start[ty2] + h2//2
260 y2 = s.rows_start[ty1] + h1//2
261
262 w = s.DX//2
263 draw_hline(s, screen, x1, x1 + w, y1, start, H_CHAR)
264 draw_vline(s, screen, x1 + w, y1, y2)
265 draw_hline(s, screen, x1 + w, x2, y2, H_CHAR, end)
266
267 def draw_diagram(s):
268
269 screen_w = s.cols_start[s.tab_nx] + s.cols_width[s.tab_nx]
270 screen_h = s.rows_start[s.tab_ny] + s.rows_height[s.tab_ny]
271 screen = []
272 while screen_h > 0:
273 screen.append(map(lambda x: ' ', range(screen_w)))
274 screen_h -= 1
275
276 for links in s.links_from.values():
277 for link in links:
278 draw_link(s, screen, link)
279
280 for box_id in s.boxes.keys():
281 draw_box(s, screen, box_id)
282
283 return print_screen(screen)
284
285 def print_screen(screen):
286 rc = ''
287 for j in range(len(screen)):
288 for i in range(len(screen[j])):
289 rc += screen[j][i]
290 rc += '\n'
291
292 return rc
293
294 def usage(args):
295 rc = 'AD Usage:<br> [[AD([Box1] -> [Box2])]] or:<br>[[AD(fgcolor:blue [Box 1] -> [Box2] -(3,1)- [Box 3] | [Box 4|...two liner...] -> [Box 5] --(5,2)-- )]]'
296 if args:
297 rc = '<font color="red">Invalid arguments: "' + args + '"</font><br><br>' + rc
298 return rc
299
300 def execute(macro, args):
301
302 if args:
303 settings = ADSettings()
304 rc = parse_ddl(settings, args)
305 if rc > 0:
306 pic = draw_diagram(settings)
307 return macro.formatter.rawHTML('<font color=\"' + settings.fgcolor + '\"><pre>\n'
308 + pic + '\n</pre></font>')
309 return usage(args)