1implement Browser; 2 3# 4# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. 5# 6 7include "sys.m"; 8 sys : Sys; 9include "draw.m"; 10 draw: Draw; 11 Rect: import draw; 12include "tk.m"; 13 tk: Tk; 14include "tkclient.m"; 15 tkclient: Tkclient; 16include "./pathreader.m"; 17include "./browser.m"; 18 19entryheight := ""; 20 21init() 22{ 23 sys = load Sys Sys->PATH; 24 if (sys == nil) 25 badmod(Sys->PATH); 26 draw = load Draw Draw->PATH; 27 if (draw == nil) 28 badmod(Draw->PATH); 29 tk = load Tk Tk->PATH; 30 if (tk == nil) 31 badmod(Tk->PATH); 32 tkclient = load Tkclient Tkclient->PATH; 33 if (tkclient == nil) 34 badmod(Tkclient->PATH); 35 tkclient->init(); 36} 37 38Browse.new(top: ref Tk->Toplevel, tkchanname, root, rlabel: string, nopanes: int, reader: PathReader): ref Browse 39{ 40 b : Browse; 41 b.top = top; 42 b.tkchan = tkchanname; 43 if (nopanes < 1 || nopanes > 2) 44 return nil; 45 b.nopanes = 2; 46 b.bgnorm = bgnorm; 47 b.bgselect = bgselect; 48 b.selected = array[2] of { * => Selected (File(nil, nil), nil) }; 49 b.opened = (root, nil) :: nil; 50 if (root == nil) 51 return nil; 52 if (root[len root - 1] != '/') 53 root[len root] = '/'; 54 b.pane0width = "2 3"; 55 b.root = root; 56 b.rlabel = rlabel; 57 b.reader = reader; 58 b.pane1 = File (nil, "-123"); 59 b.released = 1; 60 tkcmds(top, pane0scr); 61 62 tkcmds(top, pane1scr); 63 tkcmd(top, "bind .fbrowse.lmov <Button-1> {send "+b.tkchan+" movdiv %X}"); 64 65 tkcmd(top, "label .fbrowse.l -text { } -anchor w -width 0" + 66 " -font /fonts/charon/plain.normal.font"); 67 tkcmd(top, ".fbrowse.l configure -height "+tkcmd(top, ".fbrowse.l cget -height")); 68 tkcmd(top, "grid .fbrowse.l -row 0 -column 0 -sticky ew -pady 2 -columnspan 4"); 69 rb := ref b; 70 rb.newroot(b.root, b.rlabel); 71 rb.changeview(nopanes); 72 setbrowsescrollr(rb); 73 return rb; 74} 75 76Browse.refresh(b: self ref Browse) 77{ 78 scrval := tkcmd(b.top, ".fbrowse.sy1 get"); 79 p := isat(scrval, " "); 80 p1 := b.pane1; 81 b.newroot(b.root, b.rlabel); 82 setbrowsescrollr(b); 83 if (b.nopanes == 2) 84 popdirpane1(b, p1); 85 b.selectfile(1,DESELECT, File (nil, nil), nil); 86 b.selectfile(0,DESELECT, File (nil, nil), nil); 87 tkcmd(b.top, ".fbrowse.c1 yview moveto "+scrval[:p]+"; update"); 88} 89 90bgnorm := "white"; 91bgselect := "#5555FF"; 92 93ft := " -font /fonts/charon/plain.normal.font"; 94fts := " -font /fonts/charon/plain.tiny.font"; 95ftb := " -font /fonts/charon/bold.normal.font"; 96 97Browse.gotoselectfile(b: self ref Browse, file: File): string 98{ 99 (dir, tkpath) := b.gotopath(file, 0); 100 if (tkpath == nil) 101 return nil; 102 # Select dir 103 tkpath += ".l"; 104 if (dir.qid != nil) 105 tkpath += "Q" + dir.qid; 106 b.selectfile(0, SELECT, dir, tkpath); 107 108 # If it is a file, select the file too 109 if (!File.eq(file, dir)) { 110 slaves := tkcmd(b.top, "grid slaves .fbrowse.fl2"); 111 (nil, lst) := sys->tokenize(slaves, " "); 112 for (; lst != nil; lst = tl lst) { 113 if (File.eq(file, *b.getpath(hd lst))) { 114 b.selectfile(1, SELECT, file, hd lst); 115 tkpath = hd lst; 116 break; 117 } 118 } 119 pane1see(b); 120 } 121 return tkpath; 122} 123 124pane1see(b: ref Browse) 125{ 126 f := b.selected[1].tkpath; 127 if (f == "") 128 return; 129 x1 := int tkcmd(b.top, f+" cget -actx") - int tkcmd(b.top, ".fbrowse.fl2 cget -actx"); 130 y1 := int tkcmd(b.top, f+" cget -acty") - int tkcmd(b.top, ".fbrowse.fl2 cget -acty"); 131 x2 := x1 + int tkcmd(b.top, f+" cget -actwidth"); 132 y2 := y1 + int tkcmd(b.top, f+" cget -actheight"); 133 tkcmd(b.top, sys->sprint(".fbrowse.c2 see %d %d %d %d", x1,y1,x2,y2)); 134} 135 136Browse.opendir(b: self ref Browse, file: File, tkpath: string, action: int): int 137{ 138 curr := tkcmd(b.top, tkpath+".lp cget -text"); 139 if ((action == OPEN || action == TOGGLE) && curr == "+") { 140 tkcmd(b.top, tkpath+".lp configure -text {-} -relief sunken"); 141 popdirpane0(b, file, tkpath); 142 seeframe(b.top, tkpath); 143 b.addopened(file, 1); 144 setbrowsescrollr(b); 145 return 1; 146 } 147 else if ((action == CLOSE || action == TOGGLE) && curr == "-") { 148 tkcmd(b.top, tkpath+".lp configure -text {+} -relief raised"); 149 slaves := tkcmd(b.top, "grid slaves "+tkpath+" -column 1"); 150 p := isat(slaves, " "); 151 if (p != -1) 152 tkcmd(b.top, "destroy "+slaves[p:]); 153 slaves = tkcmd(b.top, "grid slaves "+tkpath+" -column 2"); 154 if (slaves != "") 155 tkcmd(b.top, "destroy "+slaves); 156 b.addopened(file, 0); 157 setbrowsescrollr(b); 158 return 1; 159 } 160 return 0; 161} 162 163Browse.addopened(b: self ref Browse, file: File, add: int) 164{ 165 tmp : list of File = nil; 166 for (; b.opened != nil; b.opened = tl b.opened) { 167 dir := hd b.opened; 168 if (!File.eq(file, dir)) 169 tmp = dir :: tmp; 170 } 171 if (add) 172 tmp = file :: tmp; 173 b.opened = tmp; 174} 175 176Browse.changeview(b: self ref Browse, nopanes: int) 177{ 178 if (b.nopanes == nopanes) 179 return; 180# w := int tkcmd(b.top, ".fbrowse cget -actwidth"); 181# ws := int tkcmd(b.top, ".fbrowse.sy1 cget -width"); 182 if (nopanes == 1) { 183 b.pane0width = tkcmd(b.top, ".fbrowse.c1 cget -actwidth") + " " + 184 tkcmd(b.top, ".fbrowse.c2 cget -actwidth"); 185 tkcmd(b.top, "grid forget .fbrowse.sx2 .fbrowse.c2 .fbrowse.lmov"); 186 tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight 0"); 187 } 188 else { 189 (nil, wlist) := sys->tokenize(b.pane0width, " "); 190 tkcmd(b.top, "grid columnconfigure .fbrowse 1 -weight "+hd wlist); 191 tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight "+hd tl wlist); 192 193 tkcmd(b.top, "grid .fbrowse.sx2 -row 3 -column 3 -sticky ew"); 194 tkcmd(b.top, "grid .fbrowse.c2 -row 2 -column 3 -sticky nsew"); 195 tkcmd(b.top, "grid .fbrowse.lmov -row 2 -column 2 -rowspan 2 -sticky ns"); 196 } 197 b.nopanes = nopanes; 198} 199 200Browse.selectfile(b: self ref Browse, pane, action: int, file: File, tkpath: string) 201{ 202 if (action == SELECT && b.selected[pane].tkpath == tkpath) 203 return; 204 if (b.selected[pane].tkpath != nil) 205 tk->cmd(b.top, b.selected[pane].tkpath+" configure -bg "+bgnorm); 206 if ((action == TOGGLE && b.selected[pane].tkpath == tkpath) || action == DESELECT) { 207 if (pane == 0) 208 popdirpane1(b, File (nil,nil)); 209 b.selected[pane] = (File(nil, nil), nil); 210 return; 211 } 212 b.selected[pane] = (file, tkpath); 213 tkcmd(b.top, tkpath+" configure -bg "+bgselect); 214 if (pane == 0) 215 popdirpane1(b, file); 216} 217 218Browse.resize(b: self ref Browse) 219{ 220 p1 := b.pane1; 221 b.pane1 = File (nil, nil); 222 223 if (p1.path != "") 224 popdirpane1(b, p1); 225 226 if (b.selected[1].tkpath != nil) { 227 s := b.selected[1]; 228 b.selectfile(1, DESELECT, s.file, s.tkpath); 229 b.selectfile(1, SELECT, s.file, s.tkpath); 230 } 231} 232 233setbrowsescrollr(b: ref Browse) 234{ 235 h := tkcmd(b.top, ".fbrowse.fl cget -height"); 236 w := tkcmd(b.top, ".fbrowse.fl cget -width"); 237 tkcmd(b.top, ".fbrowse.c1 configure -scrollregion {0 0 "+w+" "+h+"}"); 238 if (b.nopanes == 2) { 239 h = tkcmd(b.top, ".fbrowse.fl2 cget -height"); 240 w = tkcmd(b.top, ".fbrowse.fl2 cget -width"); 241 tkcmd(b.top, ".fbrowse.c2 configure -scrollregion {0 0 "+w+" "+h+"}"); 242 } 243} 244 245seeframe(top: ref Tk->Toplevel, frame: string) 246{ 247 x := int tkcmd(top, frame+" cget -actx") - int tkcmd(top, ".fbrowse.fl cget -actx"); 248 y := int tkcmd(top, frame+" cget -acty") - int tkcmd(top, ".fbrowse.fl cget -acty"); 249 w := int tkcmd(top, frame+" cget -width"); 250 h := int tkcmd(top, frame+" cget -height"); 251 wc := int tkcmd(top, ".fbrowse.c1 cget -width"); 252 hc := int tkcmd(top, ".fbrowse.c1 cget -height"); 253 if (w > wc) 254 w = wc; 255 if (h > hc) 256 h = hc; 257 tkcmd(top, sys->sprint(".fbrowse.c1 see %d %d %d %d",x,y,x+w,y+h)); 258} 259 260# Goes to selected dir OR dir containing selected file 261Browse.gotopath(b: self ref Browse, file: File, openfinal: int): (File, string) 262{ 263 tkpath := ".fbrowse.fl.f0"; 264 path := b.root; 265 testqid := ""; 266 testpath := ""; 267 close : list of string; 268 trackbacklist : list of (string, list of string, list of string) = nil; 269 trackback := 0; 270 enddir := ""; 271 if (file.path[len file.path - 1] != '/') { 272 # i.e. is not a directory 273 p := isatback(file.path, "/"); 274 enddir = file.path[:p + 1]; 275 } 276 if (enddir == path) { 277 if (!dircontainsfile(b, File (path, nil), file)) 278 return (File (nil, nil), nil); 279 } 280 else { 281 for(;;) { 282 lst : list of string; 283 if (trackback) { 284 (path, lst, close) = hd trackbacklist; 285 trackbacklist = tl trackbacklist; 286 if (close != nil) 287 b.opendir(File (hd close, hd tl close), hd tl tl close, CLOSE); 288 trackback = 0; 289 } 290 else { 291 frames := tkcmd(b.top, "grid slaves "+tkpath+" -column 1"); 292 (nil, lst) = sys->tokenize(frames, " "); 293 if (lst != nil) 294 lst = tl lst; # ignore first frame (name of parent dir); 295 } 296 found := 0; 297 hasdups := 1; 298 for (; lst != nil; lst = tl lst) { 299 testpath = path; 300 if (hasdups) { 301 labels := tkcmd(b.top, "grid slaves "+hd lst+" -row 0"); 302 (nil, lst2) := sys->tokenize(labels, " "); 303 testpath += tkcmd(b.top, hd tl lst2+" cget -text") + "/"; 304 testqid = getqidfromlabel(hd tl lst2); 305 if (testqid == nil) 306 hasdups = 0; 307 } 308 else 309 testpath += tkcmd(b.top, hd lst+".l cget -text") + "/"; 310 if (len testpath <= len file.path && file.path[:len testpath] == testpath) { 311 opened := 0; 312 close = nil; 313 if (openfinal || testpath != file.path) 314 opened = b.opendir(File(testpath, testqid), hd lst, OPEN); 315 if (opened) 316 close = testpath :: testqid :: hd lst :: nil; 317 if (tl lst != nil && hasdups) 318 trackbacklist = (path, tl lst, close) :: trackbacklist; 319 tkpath = hd lst; 320 path = testpath; 321 found = 1; 322 break; 323 } 324 } 325 if (enddir != nil && path == enddir) 326 if (dircontainsfile(b, File(testpath, testqid), file)) 327 break; 328 if (!found) { 329 if (trackbacklist == nil) 330 return (File (nil, nil), nil); 331 trackback = 1; 332 } 333 else if (testpath == file.path && testqid == file.qid) 334 break; 335 } 336 } 337 seeframe(b.top, tkpath); 338 dir := File (path, testqid); 339 popdirpane1(b, dir); 340 return (dir, tkpath); 341} 342 343dircontainsfile(b: ref Browse, dir, file: File): int 344{ 345 (files, hasdups) := b.reader->readpath(dir); 346 for (j := 0; j < len files; j++) { 347 if (files[j].name == file.path[len dir.path:] && 348 (!hasdups || files[j].qid.path == big file.qid)) 349 return 1; 350 } 351 return 0; 352} 353 354Browse.getpath(b: self ref Browse, f: string): ref File 355{ 356 if (len f < 11 || f[:11] != ".fbrowse.fl") 357 return nil; 358 (nil, lst) := sys->tokenize(f, "."); 359 lst = tl lst; 360 if (hd lst == "fl2") { 361 # i.e. is in pane 1 362 qid := getqidfromlabel(f); 363 return ref File (b.pane1.path + tk->cmd(b.top, f+" cget -text"), qid); 364 } 365 tkpath := ".fbrowse.fl.f0"; 366 path := b.root; 367 lst = tl tl lst; 368# sys->print("getpath: %s %s\n",tkpath, path); 369 qid := ""; 370 for (; lst != nil; lst = tl lst) { 371 tkpath += "."+hd lst; 372 if ((hd lst)[0] == 'l') { 373 qid = getqidfromlabel(tkpath); 374 if (qid != nil) 375 qid = "Q" + qid; 376 if (len hd lst - len qid > 1) 377 path += tk->cmd(b.top, tkpath+" cget -text"); 378 } 379 else if ((hd lst)[0] == 'f') { 380 qid = getqidfromframe(b,tkpath); 381 if (qid != nil) 382 qid = "Q"+qid; 383 path += tk->cmd(b.top, tkpath+".l"+qid+" cget -text") + "/"; 384 } 385# sys->print("getpath: %s %s\n",tkpath, path); 386 } 387 # Temporary hack! 388 if (qid != nil) 389 qid = qid[1:]; 390 return ref File (path, qid); 391} 392 393setroot(b: ref Browse, rlabel, root: string) 394{ 395 b.root = root; 396 b.rlabel = rlabel; 397 makedir(b, File (root, nil), ".fbrowse.fl.f0", rlabel, "0"); 398 tkcmd(b.top, "grid forget .fbrowse.fl.f0.lp"); 399} 400 401getqidfromframe(b: ref Browse, frame: string): string 402{ 403 tmp := tkcmd(b.top, "grid slaves "+frame+" -row 0"); 404 (nil, lst) := sys->tokenize(tmp, " \t\n"); 405 if (lst == nil) 406 return nil; 407 return getqidfromlabel(hd tl lst); 408} 409 410getqidfromlabel(label: string): string 411{ 412 p := isatback(label, "Q"); 413 if (p != -1) 414 return label[p+1:]; 415 return nil; 416} 417 418popdirpane0(b: ref Browse, dir : File, frame: string) 419{ 420 (dirs, hasdups) := b.reader->readpath(dir); 421 for (i := 0; i < len dirs; i++) { 422 si := string i; 423 f : string; 424 dirqid := string dirs[i].qid.path; 425 if (!hasdups) 426 dirqid = nil; 427 if (dirs[i].mode & sys->DMDIR) { 428 f = frame + ".f"+si; 429 makedir(b, File (dir.path+dirs[i].name, dirqid), f, dirs[i].name, string (i+1)); 430 } 431 else { 432 if (b.nopanes == 1) { 433 f = frame + ".l"+si; 434 makefile(b, f, dirs[i].name, string (i+1), dirqid); 435 } 436 } 437 } 438 dirs = nil; 439} 440 441isopened(b: ref Browse, dir: File): int 442{ 443 for (tmp := b.opened; tmp != nil; tmp = tl tmp) { 444 if (File.eq(hd tmp, dir)) 445 return 1; 446 } 447 return 0; 448} 449 450makefile(b: ref Browse, f, name, row, qid: string) 451{ 452 if (qid != nil) 453 f += "Q" + qid; 454 bgcol := bgnorm; 455# if (f == selected[0].t1) 456# bgcol = bgselect; 457 p := isat(name, "\0"); 458 if (p != -1) { 459 tkcmd(b.top, "label "+f+" -text {"+name[:p]+"} -bg "+bgcol+ft); 460 tkcmd(b.top, "label "+f+"b -text {"+name[p+1:]+"} -bg "+bgcol+ft); 461 tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -pady 2"); 462 tkcmd(b.top, "grid "+f+"b -row "+row+" -column 2 -sticky w -pady 2"); 463 tkcmd(b.top, "bind "+f+" <Button-2> {send "+b.tkchan+" but2pane1 "+f+"}"); 464 tkcmd(b.top, "bind "+f+" <ButtonRelease-2> {send "+b.tkchan+" release}"); 465 } 466 else { 467 tkcmd(b.top, "label "+f+" -text {"+name+"} -bg "+bgcol+ft); 468 tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -pady 2"); 469 } 470 tkcmd(b.top, "bind "+f+" <Button-1> {send "+b.tkchan+" but1pane0 "+f+"}"); 471 tkcmd(b.top, "bind "+f+" <ButtonRelease-1> {send "+b.tkchan+" release}"); 472 tkcmd(b.top, "bind "+f+" <Button-2> {send "+b.tkchan+" but2pane0 "+f+"}"); 473 tkcmd(b.top, "bind "+f+" <ButtonRelease-2> {send "+b.tkchan+" release}"); 474 tkcmd(b.top, "bind "+f+" <Button-3> {send "+b.tkchan+" but3pane0 "+f+"}"); 475 tkcmd(b.top, "bind "+f+" <ButtonRelease-3> {send "+b.tkchan+" release}"); 476} 477 478Browse.defaultaction(b: self ref Browse, lst: list of string, rfile: ref File) 479{ 480 tkpath: string; 481 file: File; 482 if (len lst > 1) { 483 tkpath = hd tl lst; 484 if (len tkpath > 11 && tkpath[:11] == ".fbrowse.fl") { 485 if (rfile == nil) 486 file = *b.getpath(tkpath); 487 else 488 file = *rfile; 489 } 490 } 491 case hd lst { 492 "release" => 493 b.released = 1; 494 "open" or "double1pane0" => 495 if (file.path == b.root) 496 break; 497 if (b.released) { 498 b.selectfile(0, DESELECT, File(nil, nil), nil); 499 b.selectfile(1, DESELECT, File(nil, nil), nil); 500 b.opendir(file, prevframe(tkpath), TOGGLE); 501 b.selectfile(0, SELECT, file, tkpath); 502 b.released = 0; 503 } 504 "double1pane1" => 505 b.gotoselectfile(file); 506 "but1pane0" => 507 if (b.released) { 508 b.selectfile(1, DESELECT, File(nil, nil), nil); 509 b.selectfile(0, TOGGLE, file, tkpath); 510 b.released = 0; 511 } 512 "but1pane1" => 513 if (b.released) { 514 b.selectfile(1, TOGGLE, file, tkpath); 515 b.released = 0; 516 } 517 "movdiv" => 518 movdiv(b, int hd tl lst); 519 } 520} 521 522prevframe(tkpath: string): string 523{ 524 end := len tkpath; 525 for (;;) { 526 p := isatback(tkpath[:end], "."); 527 if (tkpath[p+1] == 'f') 528 return tkpath[:end]; 529 end = p; 530 } 531 return nil; 532} 533 534makedir(b: ref Browse, dir: File, f, name, row: string) 535{ 536 bgcol := bgnorm; 537 if (f == ".fbrowse.fl.f0") 538 dir = File (b.root, nil); 539# if (name == "") 540# name = path; 541 if (dir.path[len dir.path - 1] != '/') 542 dir.path[len dir.path] = '/'; 543 if (File.eq(dir, b.selected[0].file)) 544 bgcol = bgselect; 545 tkcmd(b.top, "frame "+f+" -bg white"); 546 label := f+".l"; 547 if (dir.qid != nil) 548 label += "Q" + dir.qid; 549 tkcmd(b.top, "label "+label+" -text {"+name+"} -bg "+bgcol+ftb); 550 if (isopened(b, dir)) { 551 popdirpane0(b, dir, f); 552 tkcmd(b.top, "label "+f+".lp -text {-} -borderwidth 1 -relief sunken -height 8 -width 8"+fts); 553 } 554 else tkcmd(b.top, "label "+f+".lp -text {+} -borderwidth 1 -relief raised -height 8 -width 8"+fts); 555 tkcmd(b.top, "bind "+label+" <Button-1> {send "+b.tkchan+" but1pane0 "+label+"}"); 556 tkcmd(b.top, "bind "+label+" <Double-Button-1> {send "+b.tkchan+" double1pane0 "+label+"}"); 557 tkcmd(b.top, "bind "+label+" <ButtonRelease-1> {send "+b.tkchan+" release}"); 558 tkcmd(b.top, "bind "+label+" <Button-3> {send "+b.tkchan+" but3pane0 "+label+"}"); 559 tkcmd(b.top, "bind "+label+" <ButtonRelease-3> {send "+b.tkchan+" release}"); 560 tkcmd(b.top, "bind "+label+" <Button-2> {send "+b.tkchan+" but2pane0 "+label+"}"); 561 tkcmd(b.top, "bind "+label+" <ButtonRelease-2> {send "+b.tkchan+" release}"); 562 563 tkcmd(b.top, "bind "+f+".lp <Button-1> {send "+b.tkchan+" open "+label+"}"); 564 tkcmd(b.top, "bind "+f+".lp <ButtonRelease-1> {send "+b.tkchan+" release}"); 565 tkcmd(b.top, "grid "+f+".lp -row 0 -column 0"); 566 tkcmd(b.top, "grid "+label+" -row 0 -column 1 -sticky w -padx 5 -pady 2 -columnspan 2"); 567 tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -columnspan 2"); 568} 569 570popdirpane1(b: ref Browse, dir: File) 571{ 572# if (path == b.pane1.path && qid == b.pane1.qid) 573# return; 574 b.pane1 = dir; 575 labelset(b, ".fbrowse.l", prevpath(dir.path+"/")); 576 if (b.nopanes == 1) 577 return; 578 tkcmd(b.top, "destroy .fbrowse.fl2; frame .fbrowse.fl2 -bg white"); 579 tkcmd(b.top, ".fbrowse.c2 create window 0 0 -window .fbrowse.fl2 -anchor nw"); 580 if (dir.path == nil) { 581 setbrowsescrollr(b); 582 return; 583 } 584 (dirs, hasdups) := b.reader->readpath(dir); 585# if (path[len path - 1] == '/') 586# path = path[:len path - 1]; 587# tkcmd(b.top, "label .fbrowse.fl2.l -text {"+path+"}"); 588 row := 0; 589 col := 0; 590 tkcmd(b.top, ".fbrowse.c2 see 0 0"); 591 ni := 0; 592 n := (int tkcmd(b.top, ".fbrowse.c2 cget -actheight")) / 21; 593 for (i := 0; i < len dirs; i++) { 594 595 f := ".fbrowse.fl2.l"+string ni; 596 if (hasdups) 597 f += "Q" + string dirs[i].qid.path; 598 name := dirs[i].name; 599 isdir := dirs[i].mode & sys->DMDIR; 600 if (isdir) 601 name[len name]= '/'; 602 bgcol := bgnorm; 603 # Sort this out later 604 # if (path+"/"+name == selected[1].t0) { 605 # bgcol = bgselect; 606 # selected[1].t1 = f; 607 #} 608 tkcmd(b.top, "label "+f+" -text {"+name+"} -bg "+bgcol+ft); 609 tkcmd(b.top, "bind "+f+" <Double-Button-1> {send "+b.tkchan+" double1pane1 "+f+"}"); 610 tkcmd(b.top, "bind "+f+" <Button-1> {send "+b.tkchan+" but1pane1 "+f+"}"); 611 tkcmd(b.top, "bind "+f+" <ButtonRelease-1> {send "+b.tkchan+" release}"); 612 tkcmd(b.top, "bind "+f+" <Button-3> {send "+b.tkchan+" but3pane1 "+f+" %X %Y}"); 613 tkcmd(b.top, "bind "+f+" <ButtonRelease-3> {send "+b.tkchan+" release}"); 614 tkcmd(b.top, "grid "+f+" -row "+string row+" -column "+string col+ 615 " -sticky w -padx 10 -pady 2"); 616 row++; 617 if (row >= n) { 618 row = 0; 619 col++; 620 } 621 ni++; 622 } 623 624 dirs = nil; 625 setbrowsescrollr(b); 626} 627 628pane0scr := array[] of { 629 "frame .fbrowse", 630 631 "scrollbar .fbrowse.sy1 -command {.fbrowse.c1 yview}", 632 "scrollbar .fbrowse.sx1 -command {.fbrowse.c1 xview} -orient horizontal", 633 "canvas .fbrowse.c1 -yscrollcommand {.fbrowse.sy1 set} -xscrollcommand {.fbrowse.sx1 set} -bg white -width 50 -height 20 -borderwidth 2 -relief sunken -xscrollincrement 10 -yscrollincrement 21", 634 "grid .fbrowse.sy1 -row 2 -column 0 -sticky ns -rowspan 2", 635 "grid .fbrowse.sx1 -row 3 -column 1 -sticky ew", 636 "grid .fbrowse.c1 -row 2 -column 1 -sticky nsew", 637 "grid rowconfigure .fbrowse 2 -weight 1", 638 "grid columnconfigure .fbrowse 1 -weight 2", 639 640}; 641 642pane1scr := array[] of { 643# ".fbrowse.c1 configure -width 146", 644 "frame .fbrowse.fl2 -bg white", 645 "label .fbrowse.fl2.l -text {}", 646 "scrollbar .fbrowse.sx2 -command {.fbrowse.c2 xview} -orient horizontal", 647 "label .fbrowse.lmov -text { } -relief sunken -borderwidth 2 -width 5", 648 649 "canvas .fbrowse.c2 -xscrollcommand {.fbrowse.sx2 set} -bg white -width 50 -height 20 -borderwidth 2 -relief sunken -xscrollincrement 10 -yscrollincrement 21", 650 ".fbrowse.c2 create window 0 0 -window .fbrowse.fl2 -anchor nw", 651 "grid .fbrowse.sx2 -row 3 -column 3 -sticky ew", 652 "grid .fbrowse.c2 -row 2 -column 3 -sticky nsew", 653 "grid .fbrowse.lmov -row 2 -column 2 -rowspan 2 -sticky ns", 654 "grid columnconfigure .fbrowse 3 -weight 3", 655}; 656 657Browse.newroot(b: self ref Browse, root, rlabel: string) 658{ 659 tk->cmd(b.top, "destroy .fbrowse.fl"); 660 tkcmd(b.top, "frame .fbrowse.fl -bg white"); 661 tkcmd(b.top, ".fbrowse.c1 create window 0 0 -window .fbrowse.fl -anchor nw"); 662 b.pane1 = File (root, nil); 663 setroot(b, rlabel, root); 664 setbrowsescrollr(b); 665} 666 667Browse.showpath(b: self ref Browse, on: int) 668{ 669 if (on == b.showpathlabel) 670 return; 671 if (on) { 672 b.showpathlabel = 1; 673 if (b.pane1.path != nil) 674 labelset(b, ".fbrowse.l", prevpath(b.pane1.path+"/")); 675 } 676 else { 677 b.showpathlabel = 0; 678 tkcmd(b.top, ".fbrowse.l configure -text {}"); 679 } 680} 681 682Browse.getselected(b: self ref Browse, pane: int): File 683{ 684 return b.selected[pane].file; 685} 686 687labelset(b: ref Browse, label, text: string) 688{ 689 if (!b.showpathlabel) 690 return; 691 if (text != nil) { 692 tmp := b.rlabel; 693 if (tmp[len tmp - 1] != '/') 694 tmp[len tmp] = '/'; 695 text = tmp + text[len b.root:]; 696 } 697 tkcmd(b.top, label + " configure -text {"+text+"}"); 698} 699 700movdiv(b: ref Browse, x: int) 701{ 702 x1 := int tkcmd(b.top, ".fbrowse.lmov cget -actx"); 703 x2 := x1 + int tkcmd(b.top, ".fbrowse.lmov cget -width"); 704 diff := 0; 705 if (x < x1) 706 diff = x - x1; 707 if (x > x2) 708 diff = x - x2; 709 if (abs(diff) > 5) { 710 w1 := int tkcmd(b.top, ".fbrowse.c1 cget -actwidth"); 711 w2 := int tkcmd(b.top, ".fbrowse.c2 cget -actwidth"); 712 if (w1 + diff < 36) 713 diff = 36 - w1; 714 if (w2 - diff < 36) 715 diff = w2 - 36; 716 w1 += diff; 717 w2 -= diff; 718 # sys->print("w1: %d w2: %d\n",w1,w2); 719 tkcmd(b.top, "grid columnconfigure .fbrowse 1 -weight "+string w1); 720 tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight "+string w2); 721 } 722} 723 724 725dialog(ctxt: ref draw->Context, oldtop: ref Tk->Toplevel, butlist: list of string, title, msg: string): int 726{ 727 (top, titlebar) := tkclient->toplevel(ctxt, "", title, tkclient->Popup); 728 butchan := chan of string; 729 tk->namechan(top, butchan, "butchan"); 730 tkcmd(top, "frame .f"); 731 tkcmd(top, "label .f.l -text {"+msg+"} -font /fonts/charon/plain.normal.font"); 732 tkcmd(top, "bind .Wm_t <Button-1> +{focus .}"); 733 tkcmd(top, "bind .Wm_t.title <Button-1> +{focus .}"); 734 735 l := len butlist; 736 tkcmd(top, "grid .f.l -row 0 -column 0 -columnspan "+string l+" -sticky w -padx 10 -pady 5"); 737 i := 0; 738 for(; butlist != nil; butlist = tl butlist) { 739 si := string i; 740 tkcmd(top, "button .f.b"+si+" -text {"+hd butlist+"} "+ 741 "-font /fonts/charon/plain.normal.font -command {send butchan "+si+"}"); 742 tkcmd(top, "grid .f.b"+si+" -row 1 -column "+si+" -padx 5 -pady 5"); 743 i++; 744 } 745 placement := ""; 746 if (oldtop != nil) { 747 setcentre(oldtop, top); 748 placement = "exact"; 749 } 750 tkcmd(top, "pack .f; update; focus ."); 751 tkclient->onscreen(top, placement); 752 tkclient->startinput(top, "kbd"::"ptr"::nil); 753 for (;;) { 754 alt { 755 s := <-top.ctxt.kbd => 756 tk->keyboard(top, s); 757 s := <-top.ctxt.ptr => 758 tk->pointer(top, *s); 759 inp := <- butchan => 760 tkcmd(oldtop, "focus ."); 761 return int inp; 762 title = <-top.ctxt.ctl or 763 title = <-top.wreq or 764 title = <-titlebar => 765 if (title == "exit") { 766 tkcmd(oldtop, "focus ."); 767 return -1; 768 } 769 tkclient->wmctl(top, title); 770 } 771 } 772} 773######################## Select Functions ######################### 774 775 776setselectscrollr(s: ref Select, f: string) 777{ 778 h := tkcmd(s.top, f+" cget -height"); 779 w := tkcmd(s.top, f+" cget -width"); 780 tkcmd(s.top, ".fselect.c configure -scrollregion {0 0 "+w+" "+h+"}"); 781} 782 783Select.setscrollr(s: self ref Select, fname: string) 784{ 785 frame := getframe(s, fname); 786 if (frame != nil) 787 setselectscrollr(s,frame.path); 788} 789 790Select.new(top: ref Tk->Toplevel, tkchanname: string): ref Select 791{ 792 s: Select; 793 s.top = top; 794 s.tkchan = tkchanname; 795 s.frames = nil; 796 s.currfname = nil; 797 s.currfid = nil; 798 tkcmds(top, selectscr); 799 if (entryheight == nil) { 800 tkcmd(top, "entry .fselect.test"); 801 entryheight = " -height " + tkcmd(top, ".fselect.test cget -height"); 802 tkcmd(top, "destroy .fselect.test"); 803 } 804 for (i := 1; i < 4; i++) 805 tkcmd(top, "bind .fselect.c <ButtonRelease-"+string i+"> {send "+s.tkchan+" release}"); 806 return ref s; 807} 808 809selectscr := array[] of { 810 "frame .fselect", 811 "scrollbar .fselect.sy -command {.fselect.c yview}", 812 "scrollbar .fselect.sx -command {.fselect.c xview} -orient horizontal", 813 "canvas .fselect.c -yscrollcommand {.fselect.sy set} -xscrollcommand {.fselect.sx set} -bg white -width 414 -borderwidth 2 -relief sunken -height 180 -xscrollincrement 10 -yscrollincrement 19", 814 815 "grid .fselect.sy -row 0 -column 0 -sticky ns -rowspan 2", 816 "grid .fselect.sx -row 1 -column 1 -sticky ew", 817 "grid .fselect.c -row 0 -column 1", 818}; 819 820Select.addframe(s: self ref Select, fname, title: string) 821{ 822 if (isat(fname, " ") != -1) 823 return; 824 f := ".fselect.f"+fname; 825 tkcmd(s.top, "frame "+f+" -bg white"); 826 if (title != nil){ 827 tkcmd(s.top, "label "+f+".l -text {"+title+"} -bg white "+ 828 "-font /fonts/charon/bold.normal.font; "+ 829 "grid "+f+".l -row 0 -column 0 -columnspan 3 -sticky w"); 830 } 831 fr: Frame; 832 fr.name = fname; 833 fr.path = f; 834 fr.selected = nil; 835 s.frames = ref fr :: s.frames; 836} 837 838getframe(s: ref Select, fname: string): ref Frame 839{ 840 for (tmp := s.frames; tmp != nil; tmp = tl tmp) 841 if ((hd tmp).name == fname) 842 return hd tmp; 843 return nil; 844} 845 846Select.delframe(s: self ref Select, fname: string) 847{ 848 if (s.currfname == fname) { 849 tkcmd(s.top, ".fselect.c delete " + s.currfid); 850 s.currfid = nil; 851 s.currfname = nil; 852 } 853 f := getframe(s,fname); 854 if (f != nil) { 855 tkcmd(s.top, "destroy "+f.path); 856 tmp: list of ref Frame = nil; 857 for (;s.frames != nil; s.frames = tl s.frames) { 858 if ((hd s.frames).name != fname) 859 tmp = hd s.frames :: tmp; 860 } 861 s.frames = tmp; 862 } 863} 864 865Select.showframe(s: self ref Select, fname: string) 866{ 867 if (s.currfid != nil) 868 tkcmd(s.top, ".fselect.c delete " + s.currfid); 869 f := getframe(s, fname); 870 if (f != nil) { 871 s.currfid = tkcmd(s.top, ".fselect.c create window 0 0 "+ 872 "-window "+f.path+" -anchor nw"); 873 s.currfname = fname; 874 } 875} 876 877Select.addselection(s: self ref Select, fname, text: string, lp: list of ref Parameter, allowdups: int): string 878{ 879 fr := getframe(s, fname); 880 if (fr == nil) 881 return nil; 882 f := fr.path; 883 if (!allowdups) { 884 slv := tkcmd(s.top, "grid slaves "+f+" -column 0"); 885 (nil, slaves) := sys->tokenize(slv, " \t\n"); 886 for (; slaves != nil; slaves = tl slaves) { 887 if (text == tkcmd(s.top, hd slaves+" cget -text")) 888 return nil; 889 } 890 } 891 font := " -font /fonts/charon/plain.normal.font"; 892 fontb := " -font /fonts/charon/bold.normal.font"; 893 (id, row) := newselected(s.top, f); 894 sid := string id; 895 label := f+".l"+sid; 896 tkcmd(s.top, "label "+label+" -text {"+text+"} -bg white"+entryheight+font); 897 gridpack := label+" "; 898 paramno := 0; 899 for (; lp != nil; lp = tl lp) { 900 spn := string paramno; 901 pframe := f+".f"+sid+"P"+spn; 902 tkcmd(s.top, "frame "+pframe+" -bg white"); 903 pick p := hd lp { 904 ArgIn => 905 tkp1 := pframe+".lA"; 906 tkp2 := pframe+".eA"; 907 908 tkcmd(s.top, "label "+tkp1+" -text {"+p.name+"} "+ 909 "-bg white "+entryheight+fontb); 910 tkcmd(s.top, "entry "+tkp2+" -bg white -width 50 "+ 911 "-borderwidth 1"+entryheight+font); 912 if (p.initval != nil) 913 tkcmd(s.top, tkp2+" insert end {"+p.initval+"}"); 914 tkcmd(s.top, "grid "+tkp1+" "+tkp2+" -row 0"); 915 916 IntIn => 917 tkp1 := pframe+".sI"; 918 tkp2 := pframe+".lI"; 919 tkcmd(s.top, "scale "+tkp1+" -showvalue 0 -orient horizontal -height 20"+ 920 " -from "+string p.min+" -to "+string p.max+" -command {send "+ 921 s.tkchan+" scale "+tkp2+"}"); 922 tkcmd(s.top, tkp1+" set "+string p.initval); 923 tkcmd(s.top, "label "+tkp2+" -text {"+string p.initval+"} "+ 924 "-bg white "+entryheight+fontb); 925 tkcmd(s.top, "grid "+tkp1+" "+tkp2+" -row 0"); 926 927 } 928 gridpack += " "+pframe; 929 paramno++; 930 } 931 tkcmd(s.top, "grid "+gridpack+" -row "+row+" -sticky w"); 932 933 sendstr := " " + label + " %X %Y}"; 934 tkcmd(s.top, "bind "+label+" <Double-Button-1> {send "+s.tkchan+" double1"+sendstr); 935 tkcmd(s.top, "bind "+label+" <Button-1> {send "+s.tkchan+" but1"+sendstr); 936 tkcmd(s.top, "bind "+label+" <ButtonRelease-1> {send "+s.tkchan+" release}"); 937 tkcmd(s.top, "bind "+label+" <Button-2> {send "+s.tkchan+" but2"+sendstr); 938 tkcmd(s.top, "bind "+label+" <ButtonRelease-2> {send "+s.tkchan+" release}"); 939 tkcmd(s.top, "bind "+label+" <Button-3> {send "+s.tkchan+" but3"+sendstr); 940 tkcmd(s.top, "bind "+label+" <ButtonRelease-3> {send "+s.tkchan+" release}"); 941 setselectscrollr(s, f); 942 if (s.currfname == fname) { 943 y := int tkcmd(s.top, label+" cget -acty") - 944 int tkcmd(s.top, f+" cget -acty"); 945 h := int tkcmd(s.top, label+" cget -height"); 946 tkcmd(s.top, ".fselect.c see 0 "+string (h+y)); 947 } 948 return label; 949} 950 951newselected(top: ref Tk->Toplevel, frame: string): (int, string) 952{ 953 (n, slaves) := sys->tokenize(tkcmd(top, "grid slaves "+frame+" -column 0"), " \t\n"); 954 id := 0; 955 slaves = tl slaves; # Ignore Title 956 for (;;) { 957 if (isin(slaves, frame+".l"+string id)) 958 id++; 959 else break; 960 } 961 return (id, string n); 962} 963 964isin(l: list of string, test: string): int 965{ 966 for(tmpl := l; tmpl != nil; tmpl = tl tmpl) 967 if (hd tmpl == test) 968 return 1; 969 return 0; 970} 971 972Select.delselection(s: self ref Select, fname, tkpath: string) 973{ 974 f := getframe(s, fname); 975 (row, nil) := getrowcol(s.top, tkpath); 976 slaves := tkcmd(s.top, "grid slaves "+f.path+" -row "+row); 977 # sys->print("row %s: deleting: %s\n",row,slaves); 978 tkcmd(s.top, "grid rowdelete "+f.path+" "+row); 979 tkcmd(s.top, "destroy "+slaves); 980 # Select the next one if the item deleted was selected 981 if (f.selected == tkpath) { 982 f.selected = nil; 983 for (;;) { 984 slaves = tkcmd(s.top, "grid slaves "+f.path+" -row "+row); 985 if (slaves != nil) 986 break; 987 r := (int row) - 1; 988 if (r < 1) 989 return; 990 row = string r; 991 } 992 (nil, lst) := sys->tokenize(slaves, " "); 993 if (lst != nil) 994 s.select(fname, hd lst, SELECT); 995 } 996} 997 998getrowcol(top: ref Tk->Toplevel, s: string): (string, string) 999{ 1000 row := ""; 1001 col := ""; 1002 (nil, lst) := sys->tokenize(tkcmd(top, "grid info "+s), " \t\n"); 1003 for (; lst != nil; lst = tl lst) { 1004 if (hd lst == "-row") 1005 row = hd tl lst; 1006 else if (hd lst == "-column") 1007 col = hd tl lst; 1008 } 1009 return (row, col); 1010} 1011 1012Select.select(s: self ref Select, fname, tkpath: string, action: int) 1013{ 1014 f := getframe(s, fname); 1015 if (action == SELECT && f.selected == tkpath) 1016 return; 1017 if (f.selected != nil) 1018 tkcmd(s.top, f.selected+" configure -bg "+bgnorm); 1019 if ((action == TOGGLE && f.selected == tkpath) || action == DESELECT) 1020 f.selected = nil; 1021 else { 1022 tkcmd(s.top, tkpath+" configure -bg "+bgselect); 1023 f.selected = tkpath; 1024 } 1025} 1026 1027Select.defaultaction(s: self ref Select, lst: list of string) 1028{ 1029 case hd lst { 1030 "but1" => 1031 s.select(s.currfname, hd tl lst, TOGGLE); 1032 "scale" => 1033 tkcmd(s.top, hd tl lst+" configure -text {"+hd tl tl lst+"}"); 1034 } 1035} 1036 1037Select.getselected(s: self ref Select, fname: string): string 1038{ 1039 f := getframe(s, fname); 1040 return f.selected; 1041} 1042 1043Select.getselection(s: self ref Select, fname: string): list of (string, list of ref Parameter) 1044{ 1045 retlist : list of (string, list of ref Parameter) = nil; 1046 row := 1; 1047 f := getframe(s, fname); 1048 for (;;) { 1049 slaves := tkcmd(s.top, "grid slaves "+f.path+" -row "+string (row++)); 1050 # sys->print("slaves: %s\n",slaves); 1051 if (slaves == nil || slaves[0] == '!') 1052 break; 1053 (nil, lst) := sys->tokenize(slaves, " "); 1054 tkpath := hd lst; 1055 lst = tl lst; 1056 lp : list of ref Parameter = nil; 1057 for (; lst != nil; lst = tl lst) { 1058 pslaves := tkcmd(s.top, "grid slaves "+hd lst); 1059 (nil, plist) := sys->tokenize(pslaves, " "); 1060 # sys->print("slaves of %s - hd plist: '%s'\n",hd lst, hd plist); 1061 case (hd plist)[len hd plist - 3:] { 1062 ".eA" or ".lA" => 1063 argname := tkcmd(s.top, hd lst+".lA cget -text"); 1064 argval := tkcmd(s.top, hd lst+".eA get"); 1065 lp = ref Parameter.ArgOut(argname, argval) :: lp; 1066 ".sI" or ".lI" => 1067 val := int tkcmd(s.top, hd lst+".lI cget -text"); 1068 lp = ref Parameter.IntOut(val) :: lp; 1069 } 1070 } 1071 retlist = (tkpath, lp) :: retlist; 1072 } 1073 return retlist; 1074} 1075 1076Select.resize(s: self ref Select, width, height: int) 1077{ 1078 ws := int tkcmd(s.top, ".fselect.sy cget -width"); 1079 hs := int tkcmd(s.top, ".fselect.sx cget -height"); 1080 1081 tkcmd(s.top, ".fselect.c configure -width "+string (width - ws - 8)+ 1082 " -height "+string (height - hs - 8)); 1083 f := getframe(s, s.currfname); 1084 if (f != nil) 1085 setselectscrollr(s, f.path); 1086 1087 tkcmd(s.top, "update"); 1088} 1089 1090File.eq(a,b: File): int 1091{ 1092 if (a.path != b.path || a.qid != b.qid) 1093 return 0; 1094 return 1; 1095} 1096 1097 1098######################## General Functions ######################## 1099 1100setcentre(top1, top2: ref Tk->Toplevel) 1101{ 1102 x1 := int tkcmd(top1, ". cget -actx"); 1103 y1 := int tkcmd(top1, ". cget -acty"); 1104 h1 := int tkcmd(top1, ". cget -height"); 1105 w1 := int tkcmd(top1, ". cget -width"); 1106 1107 h2 := int tkcmd(top2, ".f cget -height"); 1108 w2 := int tkcmd(top2, ".f cget -width"); 1109 1110 newx := (x1 + (w1 / 2)) - (w2/2); 1111 newy := (y1 + (h1 / 2)) - (h2/2); 1112 tkcmd(top2, ". configure -x "+string newx+" -y "+string newy); 1113} 1114 1115abs(x: int): int 1116{ 1117 if (x < 0) 1118 return -x; 1119 return x; 1120} 1121 1122prevpath(path: string): string 1123{ 1124 if (path == nil) 1125 return nil; 1126 p := isatback(path[:len path - 1], "/"); 1127 if (p == -1) 1128 return nil; 1129 return path[:p+1]; 1130} 1131 1132isat(s, test: string): int 1133{ 1134 if (len test > len s) 1135 return -1; 1136 for (i := 0; i < (1 + len s - len test); i++) 1137 if (test == s[i:i+len test]) 1138 return i; 1139 return -1; 1140} 1141 1142isatback(s, test: string): int 1143{ 1144 if (len test > len s) 1145 return -1; 1146 for (i := len s - len test; i >= 0; i--) 1147 if (test == s[i:i+len test]) 1148 return i; 1149 return -1; 1150} 1151 1152tkcmd(top: ref Tk->Toplevel, cmd: string): string 1153{ 1154 e := tk->cmd(top, cmd); 1155 if (e != "" && e[0] == '!') 1156 sys->print("Tk error: '%s': %s\n",cmd,e); 1157 return e; 1158} 1159 1160tkcmds(top: ref Tk->Toplevel, a: array of string) 1161{ 1162 for (j := 0; j < len a; j++) 1163 tkcmd(top, a[j]); 1164} 1165 1166badmod(path: string) 1167{ 1168 sys->print("Browser: failed to load: %s\n",path); 1169 exit; 1170} 1171