1implement WmMan; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 draw: Draw; 8 Font: import draw; 9 10include "tk.m"; 11 tk: Tk; 12 13include "tkclient.m"; 14 tkclient: Tkclient; 15 16include "plumbmsg.m"; 17include "man.m"; 18 man: Man; 19 20WmMan: module { 21 init: fn (ctxt: ref Draw->Context, argv: list of string); 22}; 23 24window: ref Tk->Toplevel; 25 26W: adt { 27 textwidth: fn(nil: self ref W, text: Text): int; 28}; 29 30ROMAN: con "/fonts/lucidasans/unicode.7.font"; 31BOLD: con "/fonts/lucidasans/typelatin1.7.font"; 32ITALIC: con "/fonts/lucidasans/italiclatin1.7.font"; 33HEADING1: con "/fonts/lucidasans/boldlatin1.7.font"; 34HEADING2: con "/fonts/lucidasans/italiclatin1.7.font"; 35rfont, bfont, ifont, h1font, h2font: ref Font; 36 37GOATTR: con Parseman->ATTR_LAST << iota; 38MANPATH: con "/man/1/man"; 39INDENT: con 40; 40 41metrics: Parseman->Metrics; 42parser: Parseman; 43Text: import parser; 44 45 46tkconfig := array [] of { 47 "frame .input", 48 "frame .view", 49 "text .view.t -state disabled -width 0 -height 0 -bg white -yscrollcommand {.view.yscroll set} -xscrollcommand {.view.xscroll set}", 50 "scrollbar .view.yscroll -orient vertical -command {.view.t yview}", 51 "scrollbar .view.xscroll -orient horizontal -command {.view.t xview}", 52 "entry .input.e -bg white", 53 "button .input.back -state disabled -bitmap small_color_left.bit -command {send nav b}", 54 "button .input.forward -state disabled -bitmap small_color_right.bit -command {send nav f}", 55 56 "pack .input.back .input.forward -side left -anchor w", 57 "pack .input.e -expand 1 -fill x", 58 59 "pack .view.yscroll -fill y -side left", 60 "pack .view.t -expand 1 -fill both", 61 62 "bind .input.e <Key-\n> {send nav e}", 63 "bind .input.e <Button-1> +{grab set .input.e}", 64 "bind .input.e <ButtonRelease-1> +{grab release .input.e}", 65 "bind .view.t <Button-1> +{grab set .view.t}", 66 "bind .view.t <ButtonRelease-1> +{grab release .view.t}", 67 "bind .view.t <ButtonRelease-3> {send plumb %x %y}", 68 69 "pack .input -fill x", 70 "pack .view -expand 1 -fill both", 71 "pack propagate . 0", 72 ". configure -width 500 -height 500", 73 "focus .input.e", 74}; 75 76History: adt { 77 prev: cyclic ref History; 78 next: cyclic ref History; 79 topline: string; 80 searchstart: string; 81 searchend: string; 82 pick { 83 Search => 84 search: list of string; 85 Go => 86 path: string; 87 } 88}; 89 90history: ref History; 91 92 93init(ctxt: ref Draw->Context, argv: list of string) 94{ 95 doplumb := 0; 96 97 sys = load Sys Sys->PATH; 98 if (ctxt == nil) { 99 sys->fprint(sys->fildes(2), "man: no window context\n"); 100 raise "fail:bad context"; 101 } 102 sys->pctl(Sys->NEWPGRP, nil); 103 104 draw = load Draw Draw->PATH; 105 if (draw == nil) 106 loaderr("Draw"); 107 108 tk = load Tk Tk->PATH; 109 if (tk == nil) 110 loaderr(Tk->PATH); 111 112 man = load Man Man->PATH; 113 if (man == nil) 114 loaderr(Man->PATH); 115 116 tkclient = load Tkclient Tkclient->PATH; 117 if (tkclient == nil) 118 loaderr(Tkclient->PATH); 119 120 parser = load Parseman Parseman->PATH; 121 if (parser == nil) 122 loaderr(Parseman->PATH); 123 parser->init(); 124 125 plumber := load Plumbmsg Plumbmsg->PATH; 126 if (plumber != nil) { 127 if (plumber->init(1, nil, 0) >= 0) 128 doplumb = 1; 129 } 130 131 argv = tl argv; 132 133 rfont = Font.open(ctxt.display, ROMAN); 134 bfont = Font.open(ctxt.display, BOLD); 135 ifont = Font.open(ctxt.display, ITALIC); 136 h1font = Font.open(ctxt.display, HEADING1); 137 h2font = Font.open(ctxt.display, HEADING2); 138 139 em := rfont.width("m"); 140 en := rfont.width("n"); 141 metrics = Parseman->Metrics(490, 80, em, en, 14, 40, 20); 142 143 tkclient->init(); 144 buts := Tkclient->Resize | Tkclient->Hide; 145 winctl: chan of string; 146 (window, winctl) = tkclient->toplevel(ctxt, nil, "Man", buts); 147 nav := chan of string; 148 plumb := chan of string; 149 tk->namechan(window, nav, "nav"); 150 tk->namechan(window, plumb, "plumb"); 151 for(tc:=0; tc<len tkconfig; tc++) 152 tkcmd(window, tkconfig[tc]); 153 if ((err := tkcmd(window, "variable lasterror")) != nil) { 154 sys->fprint(sys->fildes(2), "man: tk initialization failed: %s\n", err); 155 raise "fail:tk"; 156 } 157 fittoscreen(window); 158 tkcmd(window, "update"); 159 mktags(); 160 161 vw := int tkcmd(window, ".view.t cget -actwidth") - 10; 162 if (vw <= 0) 163 vw = 1; 164 metrics.pagew = vw; 165 166 linechan := chan of list of (int, Text); 167 man->loadsections(nil); 168 169 pidc := chan of int; 170 171 if (argv != nil) { 172 if (hd argv == "-f") { 173 first: ref History; 174 for (argv = tl argv; argv != nil; argv = tl argv) { 175 hnode := ref History.Go(history, nil, "", "", "", hd argv); 176 if (history != nil) 177 history.next = hnode; 178 history = hnode; 179 if (first == nil) 180 first = history; 181 } 182 history = first; 183 } else 184 history = ref History.Search(nil, nil, "", "", "", argv); 185 } 186 187 if (history == nil) 188 history = ref History.Go(nil, nil, "", "", "", MANPATH); 189 190 setbuttons(); 191 spawn printman(pidc, linechan, history); 192 layoutpid := <- pidc; 193 tkclient->onscreen(window, nil); 194 tkclient->startinput(window, "kbd"::"ptr"::nil); 195 for (;;) alt { 196 s := <-window.ctxt.kbd => 197 tk->keyboard(window, s); 198 s := <-window.ctxt.ptr => 199 tk->pointer(window, *s); 200 s := <-window.ctxt.ctl or 201 s = <-window.wreq or 202 s = <-winctl => 203 e := tkclient->wmctl(window, s); 204 if (e == nil && s[0] == '!') { 205 topline := tkcmd(window, ".view.t yview"); 206 (nil, toptoks) := sys->tokenize(topline, " "); 207 if (toptoks != nil) 208 history.topline = hd toptoks; 209 vw = int tkcmd(window, ".view.t cget -actwidth") - 10; 210 if (vw <= 0) 211 vw = 1; 212 if (vw != metrics.pagew) { 213 if (layoutpid != -1) 214 kill(layoutpid); 215 metrics.pagew = vw; 216 tkcmd(window, ".view.t delete 1.0 end"); 217 tkcmd(window, "update"); 218 spawn printman(pidc, linechan, history); 219 layoutpid = <- pidc; 220 } 221 } 222 line := <- linechan => 223 if (line == nil) { 224 # layout done 225 if (history.topline != "") { 226 topline := tkcmd(window, ".view.t yview"); 227 (nil, toptoks) := sys->tokenize(topline, " "); 228 if (toptoks != nil) 229 if (hd toptoks == "0") 230 tkcmd(window, ".view.t yview moveto " + history.topline); 231 } 232 tkcmd(window, "update"); 233 } else 234 setline(line); 235 go := <- nav => 236 topline := tkcmd(window, ".view.t yview"); 237 (nil, toptoks) := sys->tokenize(topline, " "); 238 if (toptoks != nil) 239 history.topline = hd toptoks; 240 case go[0] { 241 'f' => 242 # forward 243 history = history.next; 244 setbuttons(); 245 if (layoutpid != -1) 246 kill(layoutpid); 247 tkcmd(window, ".view.t delete 1.0 end"); 248 tkcmd(window, "update"); 249 spawn printman(pidc, linechan, history); 250 layoutpid = <- pidc; 251 'b' => 252 # back 253 history = history.prev; 254 setbuttons(); 255 if (layoutpid != -1) 256 kill(layoutpid); 257 tkcmd(window, ".view.t delete 1.0 end"); 258 tkcmd(window, "update"); 259 spawn printman(pidc, linechan, history); 260 layoutpid = <- pidc; 261 'e' or 'l' => 262 t := ""; 263 if (go[0] == 'l') { 264 # link 265 t = go[1:]; 266 } else { 267 # entry 268 t = tkcmd(window, ".input.e get"); 269 for (i := 0; i < len t; i++) 270 if (!(t[i] == ' ' || t[i] == '\t')) 271 break; 272 if (i == len t) 273 break; 274 t = t[i:]; 275 if (t[0] == '/' || t[0] == '?') { 276 search(t); 277 break; 278 } 279 } 280 (n, toks) := sys->tokenize(t, " \t"); 281 if (n == 0) 282 continue; 283 h := ref History.Search(history, nil, "", "", "", toks); 284 history.next = h; 285 history = h; 286 setbuttons(); 287 if (layoutpid != -1) 288 kill(layoutpid); 289 tkcmd(window, ".view.t delete 1.0 end"); 290 tkcmd(window, "update"); 291 spawn printman(pidc, linechan, history); 292 layoutpid = <- pidc; 293 'g' => 294 # goto file 295 h := ref History.Go(history, nil, "", "", "", go[1:]); 296 history.next = h; 297 history = h; 298 setbuttons(); 299 if (layoutpid != 0) 300 kill(layoutpid); 301 tkcmd(window, ".view.t delete 1.0 end"); 302 tkcmd(window, "update"); 303 spawn printman(pidc, linechan, history); 304 layoutpid = <- pidc; 305 } 306 p := <- plumb => 307 if (!doplumb) 308 break; 309 (nil, l) := sys->tokenize(p, " "); 310 x := int hd l; 311 y := int hd tl l; 312 index := tkcmd(window, ".view.t index @"+string x+","+string y); 313 selindex := tkcmd(window, ".view.t tag ranges sel"); 314 insel := 0; 315 if(selindex != "") 316 insel = tkcmd(window, ".view.t compare sel.first <= "+index)=="1" && 317 tkcmd(window, ".view.t compare sel.last >= "+index)=="1"; 318 text := ""; 319 attr := ""; 320 if (insel) 321 text = tkcmd(window, ".view.t get sel.first sel.last"); 322 else{ 323 # have line with text in it 324 # now extract whitespace-bounded string around click 325 (nil, w) := sys->tokenize(index, "."); 326 charno := int hd tl w; 327 left := tkcmd(window, ".view.t index {"+index+" linestart}"); 328 right := tkcmd(window, ".view.t index {"+index+" lineend}"); 329 line := tkcmd(window, ".view.t get "+left+" "+right); 330 for(i:=charno; i>0; --i) 331 if(line[i-1]==' ' || line[i-1]=='\t') 332 break; 333 for(j:=charno; j<len line; j++) 334 if(line[j]==' ' || line[j]=='\t') 335 break; 336 text = line[i:j]; 337 attr = "click="+string (charno-i); 338 } 339 msg := ref Plumbmsg->Msg( 340 "WmMan", 341 "", 342 "", 343 "text", 344 attr, 345 array of byte text); 346 plumber->msg.send(); 347 348 layoutpid = <- pidc => 349 ; 350 } 351} 352 353search(pat: string) 354{ 355 dir: string; 356 start: string; 357 if (pat[0] == '/') { 358 dir = "-forwards"; 359 start = history.searchend; 360 } else { 361 dir = "-backwards"; 362 start = history.searchstart; 363 } 364 pat = pat[1:]; 365 if (start == "") 366 start = "1.0"; 367 r := tkcmd(window, ".view.t search " + dir + " -- " + tk->quote(pat) + " " + start); 368 if (r != nil) { 369 history.searchstart = r; 370 history.searchend = r + "+" + string len pat + "c"; 371 tkcmd(window, ".view.t tag remove sel 1.0 end"); 372 tkcmd(window, ".view.t tag add sel " + history.searchstart + " " + history.searchend); 373 tkcmd(window, ".view.t see " + r); 374 tkcmd(window, "update"); 375 } 376} 377 378setbuttons() 379{ 380 if (history.prev == nil) 381 tkcmd(window, ".input.back configure -state disabled"); 382 else 383 tkcmd(window, ".input.back configure -state normal"); 384 if (history.next == nil) 385 tkcmd(window, ".input.forward configure -state disabled"); 386 else 387 tkcmd(window, ".input.forward configure -state normal"); 388} 389 390dolayout(linechan: chan of list of (int, Text), path: string) 391{ 392 fd := sys->open(path, Sys->OREAD); 393 if (fd == nil) { 394 layouterror(linechan, sys->sprint("cannot open file %s: %r", path)); 395 return; 396 } 397 w: ref W; 398 parser->parseman(fd, metrics, 0, w, linechan); 399} 400 401printman(pidc: chan of int, linechan: chan of list of (int, Text), h: ref History) 402{ 403 pidc <-= sys->pctl(0, nil); 404 args: list of string; 405 pick hp := h { 406 Search => 407 args = hp.search; 408 Go => 409 dolayout(linechan, hp.path); 410 pidc <-= -1; 411 return; 412 } 413 sections: list of string; 414 argstext := ""; 415 addsections := 1; 416 keywords: list of string; 417 for (; args != nil; args = tl args) { 418 arg := hd args; 419 if (arg == nil) 420 continue; 421 if (addsections && !isint(trimdot(arg))) { 422 addsections = 0; 423 keywords = args; 424 } 425 if (addsections) 426 sections = arg :: sections; 427 argstext = argstext + " " + arg; 428 } 429 manpages := man->getfiles(sections, keywords); 430 pagelist := sortpages(manpages); 431 if (len pagelist == 1) { 432 (nil, path, nil) := hd pagelist; 433 dolayout(linechan, path); 434 pidc <-= -1; 435 return; 436 } 437 438 tt := Text(Parseman->FONT_ROMAN, 0, "Search:", 1, nil); 439 at := Text(Parseman->FONT_BOLD, 0, argstext, 0, nil); 440 linechan <-= (0, tt)::(0, at)::nil; 441 tt.text = ""; 442 linechan <-= (0, tt)::nil; 443 444 if (pagelist == nil) { 445 donet := Text(Parseman->FONT_ROMAN, 0, "No matches", 0, nil); 446 linechan <-= (INDENT, donet) :: nil; 447 linechan <-= nil; 448 pidc <-= -1; 449 return; 450 } 451 452 linelist: list of list of Text; 453 pathlist: list of Text; 454 455 maxkwlen := 0; 456 comma := Text(Parseman->FONT_ROMAN, 0, ", ", 0, ""); 457 for (; pagelist != nil; pagelist = tl pagelist) { 458 (n, p, kwl) := hd pagelist; 459 l := 0; 460 keywords: list of Text = nil; 461 for (; kwl != nil; kwl = tl kwl) { 462 kw := hd kwl; 463 kwt := Text(Parseman->FONT_ITALIC, GOATTR, kw, 0, p); 464 nt := Text(Parseman->FONT_ROMAN, GOATTR, "(" + string n + ")", 0, p); 465 l += textwidth(kwt) + textwidth(nt); 466 if (keywords != nil) { 467 l += textwidth(comma); 468 keywords = nt :: kwt :: comma :: keywords; 469 } else 470 keywords = nt :: kwt :: nil; 471 } 472 if (l > maxkwlen) 473 maxkwlen = l; 474 linelist = keywords :: linelist; 475 ptext := Text(Parseman->FONT_ROMAN, GOATTR, p, 0, ""); 476 pathlist = ptext :: pathlist; 477 } 478 479 for (; pathlist != nil; (pathlist, linelist) = (tl pathlist, tl linelist)) { 480 line := (10 + INDENT + maxkwlen, hd pathlist) :: nil; 481 for (ll := hd linelist; ll != nil; ll = tl ll) { 482 litem := hd ll; 483 if (tl ll == nil) 484 line = (INDENT, litem) :: line; 485 else 486 line = (0, litem) :: line; 487 } 488 linechan <-= line; 489 } 490 linechan <-= nil; 491 pidc <-= -1; 492} 493 494layouterror(linechan: chan of list of (int, Text), msg: string) 495{ 496 text := "ERROR: " + msg; 497 t := Text(Parseman->FONT_ROMAN, 0, text, 0, nil); 498 linechan <-= (0, t)::nil; 499 linechan <-= nil; 500} 501 502loaderr(modname: string) 503{ 504 sys->print("cannot load %s module: %r\n", modname); 505 raise "fail:init"; 506} 507 508W.textwidth(nil: self ref W, text: Text): int 509{ 510 return textwidth(text); 511} 512 513textwidth(text: Text): int 514{ 515 f: ref Font; 516 if (text.heading == 1) 517 f = h1font; 518 else if (text.heading == 2) 519 f = h2font; 520 else { 521 case text.font { 522 Parseman->FONT_ROMAN => 523 f = rfont; 524 Parseman->FONT_BOLD => 525 f = bfont; 526 Parseman->FONT_ITALIC => 527 f = ifont; 528 * => 529 return 8 * len text.text; 530 } 531 } 532 return draw->f.width(text.text); 533} 534 535lnum := 0; 536 537setline(line: list of (int, Text)) 538{ 539 tabstr := ""; 540 linestr := ""; 541 lastoff := 0; 542 curfont := Parseman->FONT_ROMAN; 543 curlink := ""; 544 curgtag := ""; 545 curheading := 0; 546 fonttext := ""; 547 548 for (l := line; l != nil; l = tl l) { 549 (offset, nil) := hd l; 550 if (offset != 0) { 551 lastoff = offset; 552 if (tabstr != "") 553 tabstr[len tabstr] = ' '; 554 tabstr = tabstr + string offset; 555 } 556 } 557 # fudge up tabs for rest of line 558 if (lastoff != 0) 559 tabstr = tabstr + " " + string lastoff + " " + string (lastoff + INDENT); 560 ttag := ""; 561 gtag := ""; 562 if (tabstr != nil) 563 ttag = tabtag(tabstr) + " "; 564 565 for (l = line; l != nil; l = tl l) { 566 (offset, text) := hd l; 567 gtag = ""; 568 if (text.link != nil) { 569 if (text.attr & GOATTR) 570 gtag = gotag(text.link) + " "; 571 else { 572 gtag = linktag(text.link) + " "; 573 } 574 } 575 if (offset != 0) 576 fonttext[len fonttext] = '\t'; 577 if (text.font != curfont || text.link != curlink || text.heading != curheading || gtag != curgtag) { 578 # need to change tags 579 linestr = linestr + " " + tk->quote(fonttext) + " {" + ttag + curgtag + fonttag(curfont, curheading) + "}"; 580 ttag = ""; 581 curgtag = gtag; 582 fonttext = ""; 583 curfont = text.font; 584 curlink = text.link; 585 curheading = text.heading; 586 } 587 fonttext = fonttext + text.text; 588 } 589 if (fonttext != nil) 590 linestr = linestr + " " + tk->quote(fonttext) + " {" + ttag + curgtag + fonttag(curfont, curheading) + "}"; 591 tkcmd(window, ".view.t insert end " + linestr); 592 tkcmd(window, ".view.t insert end {\n}"); 593 # only update on every other line 594 if (lnum++ & 1) 595 tkcmd(window, "update"); 596} 597 598mktags() 599{ 600 tkcmd(window, ".view.t tag configure ROMAN -font " + ROMAN); 601 tkcmd(window, ".view.t tag configure BOLD -font " + BOLD); 602 tkcmd(window, ".view.t tag configure ITALIC -font " + ITALIC); 603 tkcmd(window, ".view.t tag configure H1 -font " + HEADING1); 604 tkcmd(window, ".view.t tag configure H2 -font " + HEADING2); 605} 606 607fonttag(font, heading: int): string 608{ 609 if (heading == 1) 610 return "H1"; 611 if (heading == 2) 612 return "H2"; 613 case font { 614 Parseman->FONT_ROMAN => 615 return "ROMAN"; 616 Parseman->FONT_BOLD => 617 return "BOLD"; 618 Parseman->FONT_ITALIC => 619 return "ITALIC"; 620 } 621 return nil; 622} 623 624nexttag := 0; 625lasttabstr := ""; 626lasttagname := ""; 627 628tabtag(tabstr: string): string 629{ 630 if (tabstr == lasttabstr) 631 return lasttagname; 632 lasttagname = "TAB" + string nexttag++; 633 lasttabstr = tabstr; 634 tkcmd(window, ".view.t tag configure " + lasttagname + " -tabs " + tk->quote(tabstr)); 635 return lasttagname; 636} 637 638# optimise this! 639gotag(path: string): string 640{ 641 cmd := "{send nav g" + path + "}"; 642 name := "GO" + string nexttag++; 643 tkcmd(window, ".view.t tag bind " + name + " <ButtonRelease-1> +" + cmd); 644 tkcmd(window, ".view.t tag configure " + name + " -fg green"); 645 return name; 646} 647 648# and this! 649linktag(search: string): string 650{ 651 cmd := tk->quote("send nav l" + search); 652 name := "LN" + string nexttag++; 653 tkcmd(window, ".view.t tag bind " + name + " <ButtonRelease-1> +" + cmd); 654 tkcmd(window, ".view.t tag configure " + name + " -fg green"); 655 return name; 656} 657 658isint(s: string): int 659{ 660 for (i := 0; i < len s; i++) 661 if (s[i] < '0' || s[i] > '9') 662 return 0; 663 return 1; 664} 665 666kill(pid: int) 667{ 668 fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE); 669 if (fd != nil) 670 sys->fprint(fd, "kill"); 671} 672 673revsortuniq(strlist: list of string): list of string 674{ 675 strs := array [len strlist] of string; 676 for (i := 0; strlist != nil; (i, strlist) = (i+1, tl strlist)) 677 strs[i] = hd strlist; 678 679 # simple sort (ascending) 680 for (i = 0; i < len strs - 1; i++) { 681 for (j := i+1; j < len strs; j++) 682 if (strs[i] < strs[j]) 683 (strs[i], strs[j]) = (strs[j], strs[i]); 684 } 685 686 # construct list (result is descending) 687 r: list of string; 688 prev := ""; 689 for (i = 0; i < len strs; i++) { 690 if (strs[i] != prev) { 691 r = strs[i] :: r; 692 prev = strs[i]; 693 } 694 } 695 return r; 696} 697 698sortpages(pagelist: list of (int, string, string)): list of (int, string, list of string) 699{ 700 pages := array [len pagelist] of (int, string, string); 701 for (i := 0; pagelist != nil; (i, pagelist) = (i+1, tl pagelist)) 702 pages[i] = hd pagelist; 703 704 for (i = 0; i < len pages - 1; i++) { 705 for (j := i+1; j < len pages; j++) { 706 (nil, nil, ipath) := pages[i]; 707 (nil, nil, jpath) := pages[j]; 708 if (ipath > jpath) 709 (pages[i], pages[j]) = (pages[j], pages[i]); 710 } 711 } 712 713 r: list of (int, string, list of string); 714 filecmds: list of string; 715 lastfile := ""; 716 lastsect := 0; 717 for (i = 0; i < len pages; i++) { 718 (section, cmd, file) := pages[i]; 719 if (lastfile == "") { 720 lastfile = file; 721 lastsect = section; 722 } 723 724 if (file != lastfile) { 725 r = (lastsect, lastfile, filecmds) :: r; 726 lastfile = file; 727 lastsect = section; 728 filecmds = nil; 729 } 730 filecmds = cmd :: filecmds; 731 } 732 if (filecmds != nil) 733 r = (lastsect, lastfile, revsortuniq(filecmds)) :: r; 734 return r; 735} 736 737fittoscreen(win: ref Tk->Toplevel) 738{ 739 Point, Rect: import draw; 740 if (win.image == nil || win.image.screen == nil) 741 return; 742 r := win.image.screen.image.r; 743 scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y)); 744 bd := int tkcmd(win, ". cget -bd"); 745 winsize := Point(int tkcmd(win, ". cget -actwidth") + bd * 2, int tkcmd(win, ". cget -actheight") + bd * 2); 746 if (winsize.x > scrsize.x) 747 tkcmd(win, ". configure -width " + string (scrsize.x - bd * 2)); 748 if (winsize.y > scrsize.y) 749 tkcmd(win, ". configure -height " + string (scrsize.y - bd * 2)); 750 actr: Rect; 751 actr.min = Point(int tkcmd(win, ". cget -actx"), int tkcmd(win, ". cget -acty")); 752 actr.max = actr.min.add((int tkcmd(win, ". cget -actwidth") + bd*2, 753 int tkcmd(win, ". cget -actheight") + bd*2)); 754 (dx, dy) := (actr.dx(), actr.dy()); 755 if (actr.max.x > r.max.x) 756 (actr.min.x, actr.max.x) = (r.max.x - dx, r.max.x); 757 if (actr.max.y > r.max.y) 758 (actr.min.y, actr.max.y) = (r.max.y - dy, r.max.y); 759 if (actr.min.x < r.min.x) 760 (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); 761 if (actr.min.y < r.min.y) 762 (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); 763 tkcmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); 764} 765 766tkcmd(top: ref Tk->Toplevel, s: string): string 767{ 768 e := tk->cmd(top, s); 769 if (e != nil && e[0] == '!') 770 sys->print("tk error %s on '%s'\n", e, s); 771 return e; 772} 773 774trimdot(s: string): string 775{ 776 for(i := 0; i < len s; i++) 777 if(s[i] == '.') 778 return s[0: i]; 779 return s; 780} 781