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)

MoinMoin: macro/AD.py (last edited 2007-10-29 19:07:59 by localhost)