1implement Brutusext; 2 3# <Extension mod file> 4# For module descriptions (in book) 5 6Name: con "Brutus mod"; 7 8include "sys.m"; 9 sys: Sys; 10 11include "draw.m"; 12 draw: Draw; 13 Context, Font: import draw; 14 15include "bufio.m"; 16 bufio: Bufio; 17 Iobuf: import bufio; 18 19include "tk.m"; 20 tk: Tk; 21 22include "tkclient.m"; 23 tkclient: Tkclient; 24 25include "string.m"; 26 S : String; 27 28include "brutus.m"; 29 Size8, Index, Roman, Italic, Bold, Type, NFONT, NSIZE: import Brutus; 30 31include "brutusext.m"; 32 33Mstring: adt 34{ 35 s: string; 36 style: int; 37 indexed: int; 38 width: int; 39 next: cyclic ref Mstring; 40}; 41 42fontname := array[NFONT] of { 43 "/fonts/lucidasans/unicode.7.font", 44 "/fonts/lucidasans/italiclatin1.7.font", 45 "/fonts/lucidasans/boldlatin1.7.font", 46 "/fonts/lucidasans/typelatin1.7.font", 47 }; 48 49fontswitch := array[NFONT] of { 50 "\\fontseries{m}\\rmfamily ", 51 "\\itshape ", 52 "\\fontseries{b}\\rmfamily ", 53 "\\fontseries{mc}\\ttfamily ", 54 }; 55 56fontref := array[NFONT] of ref Font; 57 58LEFTCHARS: con 45; 59LEFTPIX: con LEFTCHARS*7; # 7 is width of lucidasans/typelatin1.7 chars 60 61init(s: Sys, d: Draw, b: Bufio, t: Tk, w: Tkclient) 62{ 63 sys = s; 64 draw = d; 65 bufio = b; 66 tk = t; 67 tkclient = w; 68 S = load String String->PATH; 69} 70 71create(parent: string, t: ref Tk->Toplevel, name, args: string): string 72{ 73 (spec, err) := getspec(parent, args); 74 if(err != nil) 75 return err; 76 n := len spec; 77 if(n == 0) 78 return "empty spec"; 79 d := t.image.display; 80 for(i:=0; i < NFONT; i++) { 81 if(i == Bold || fontref[i] != nil) 82 continue; 83 fontref[i] = Font.open(d, fontname[i]); 84 if(fontref[i] == nil) 85 return sys->sprint("can't open font %s: %r\n", fontname[i]); 86 } 87 (nil, nil, rw, nil) := measure(spec, 1); 88 lw := LEFTPIX; 89 wd := lw + rw; 90 fnt := fontref[Roman]; 91 ht := n * fnt.height; 92 err = tk->cmd(t, "canvas " + name + " -width " + string wd 93 + " -height " + string ht 94 + " -font " + fontname[Type]); 95 if(len err > 0 && err[0] == '!') 96 return "problem creating canvas"; 97 y := 0; 98 xl := 0; 99 xr := lw; 100 for(l := spec; l != nil; l = tl l) { 101 (lm, rm) := hd l; 102 canvmstring(t, name, lm, xl, y); 103 canvmstring(t, name, rm, xr, y); 104 y += fnt.height; 105 } 106 tk->cmd(t, "update"); 107 return ""; 108} 109 110canvmstring(t: ref Tk->Toplevel, canv: string, m: ref Mstring, x, y: int) 111{ 112 # assume fonts all have same ascent 113 while(m != nil) { 114 pos := string x + " " + string y; 115 font := ""; 116 if(m.style != Type) 117 font = " -font " + fontname[m.style]; 118 e := tk->cmd(t, canv + " create text " + pos + " -anchor nw " 119 + font + " -text '" + m.s); 120 x += m.width; 121 m = m.next; 122 } 123} 124 125getspec(parent, args: string) : (list of (ref Mstring, ref Mstring), string) 126{ 127 (n, argl) := sys->tokenize(args, " "); 128 if(n != 1) 129 return (nil, "usage: " + Name + " file"); 130 b := bufio->open(fullname(parent, hd argl), Sys->OREAD); 131 if(b == nil) 132 return (nil, sys->sprint("can't open %s, the error was: %r", hd argl)); 133 mm : list of (ref Mstring, ref Mstring) = nil; 134 for(;;) { 135 s := b.gets('\n'); 136 if(s == "") 137 break; 138 (nf, fl) := sys->tokenize(s, " "); 139 if(nf == 0) 140 mm = (nil, nil) :: mm; 141 else { 142 sleft := ""; 143 sright := ""; 144 if(nf == 1) { 145 f := hd fl; 146 if(s[0] == '\t') 147 sright = f; 148 else 149 sleft = f; 150 } 151 else { 152 sleft = hd fl; 153 sright = hd tl fl; 154 } 155 mm = (tom(sleft, Type, Roman, 1), tom(sright, Italic, Type, 0)) :: mm; 156 } 157 } 158 ans : list of (ref Mstring, ref Mstring) = nil; 159 while(mm != nil) { 160 ans = hd mm :: ans; 161 mm = tl mm; 162 } 163 return (ans, ""); 164} 165 166tom(str: string, defstyle, altstyle, doindex: int) : ref Mstring 167{ 168 if(str == "") 169 return nil; 170 if(str[len str - 1] == '\n') 171 str = str[0: len str - 1]; 172 if(str == "") 173 return nil; 174 style := defstyle; 175 if(str[0] == '|') 176 style = altstyle; 177 (nil, l) := sys->tokenize(str, "|"); 178 dummy := ref Mstring; 179 last := dummy; 180 if(doindex && l != nil && S->prefix(" ", hd l)) 181 doindex = 0; # continuation line 182 while(l != nil) { 183 s := hd l; 184 m : ref Mstring; 185 if(doindex && style == defstyle) { 186 # index 'words' in defstyle, but not past : or ( 187 (sl,sr) := S->splitl(s, ":("); 188 while(sl != nil) { 189 a : string; 190 (a,sl) = S->splitl(sl, "a-zA-Z"); 191 if(a != "") { 192 m = ref Mstring(a, style, 0, 0, nil); 193 last.next = m; 194 last = m; 195 } 196 if(sl != "") { 197 b : string; 198 (b,sl) = S->splitl(sl, "^a-zA-Z0-9_"); 199 if(b != "") { 200 m = ref Mstring(b, style, 1, 0, nil); 201 last.next = m; 202 last = m; 203 } 204 } 205 } 206 if(sr != "") { 207 m = ref Mstring(sr, style, 0, 0, nil); 208 last.next = m; 209 last = m; 210 doindex = 0; 211 } 212 } 213 else { 214 m = ref Mstring(s, style, 0, 0, nil); 215 last.next = m; 216 last = m; 217 } 218 l = tl l; 219 if(style == defstyle) 220 style = altstyle; 221 else 222 style = defstyle; 223 } 224 return dummy.next; 225} 226 227measure(spec: list of (ref Mstring, ref Mstring), pixels: int) : (int, ref Mstring, int, ref Mstring) 228{ 229 maxl := 0; 230 maxr := 0; 231 maxlm : ref Mstring = nil; 232 maxrm : ref Mstring = nil; 233 while(spec != nil) { 234 (lm, rm) := hd spec; 235 spec = tl spec; 236 (maxl, maxlm) = measuremax(lm, maxl, maxlm, pixels); 237 (maxr, maxrm) = measuremax(rm, maxr, maxrm, pixels); 238 } 239 return (maxl, maxlm, maxr, maxrm); 240} 241 242measuremax(m: ref Mstring, maxw: int, maxm: ref Mstring, pixels: int) : (int, ref Mstring) 243{ 244 w := 0; 245 for(mm := m; mm != nil; mm = mm.next) { 246 if(pixels) 247 mm.width = fontref[mm.style].width(mm.s); 248 else 249 mm.width = len mm.s; 250 w += mm.width; 251 } 252 if(w > maxw) { 253 maxw = w; 254 maxm = m; 255 } 256 return (maxw, maxm); 257} 258 259cook(parent: string, nil: int, args: string): (ref Celem, string) 260{ 261 (spec, err) := getspec(parent, args); 262 if(err != nil) 263 return (nil, err); 264 (nil, maxlm, nil, nil) := measure(spec, 0); 265 ans := fontce(Roman); 266 tail := specialce("\\begin{tabbing}\\hspace{3in}\\=\\kill\n"); 267 tail = add(ans, nil, tail); 268 for(l := spec; l != nil; l = tl l) { 269 (lm, rm) := hd l; 270 tail = cookmstring(ans, tail, lm, 1); 271 tail = add(ans, tail, specialce("\\>")); 272 tail = cookmstring(ans, tail, rm, 0); 273 tail = add(ans, tail, specialce("\\\\\n")); 274 } 275 add(ans, tail, specialce("\\end{tabbing}")); 276 return (ans, ""); 277} 278 279cookmstring(par, tail: ref Celem, m: ref Mstring, doindex: int) : ref Celem 280{ 281 s := ""; 282 if(m == nil) 283 return tail; 284 while(m != nil) { 285 e := fontce(m.style); 286 te := textce(m.s); 287 add(e, nil, te); 288 if(doindex && m.indexed) { 289 ie := ref Celem(Index, nil, nil, nil, nil, nil); 290 add(ie, nil, e); 291 e = ie; 292 } 293 tail = add(par, tail, e); 294 m = m.next; 295 } 296 return tail; 297} 298 299specialce(s: string) : ref Celem 300{ 301 return ref Celem(Special, s, nil, nil, nil, nil); 302} 303 304textce(s: string) : ref Celem 305{ 306 return ref Celem(Text, s, nil, nil, nil, nil); 307} 308 309fontce(sty: int) : ref Celem 310{ 311 return ref Celem(sty*NSIZE+Size8, nil, nil, nil, nil, nil); 312} 313 314add(par, tail: ref Celem, e: ref Celem) : ref Celem 315{ 316 if(tail == nil) { 317 par.contents = e; 318 e.parent = par; 319 } 320 else 321 tail.next = e; 322 e.prev = tail; 323 return e; 324} 325 326fullname(parent, file: string): string 327{ 328 if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#'))) 329 return file; 330 331 for(i:=len parent-1; i>=0; i--) 332 if(parent[i] == '/') 333 return parent[0:i+1] + file; 334 return file; 335} 336