1implement Windowm; 2 3include "common.m"; 4 5sys : Sys; 6utils : Utils; 7drawm : Draw; 8graph : Graph; 9gui : Gui; 10dat : Dat; 11bufferm : Bufferm; 12textm : Textm; 13filem : Filem; 14look : Look; 15scrl : Scroll; 16acme : Acme; 17 18sprint : import sys; 19FALSE, TRUE, XXX, Astring : import Dat; 20Reffont, reffont, Lock, Ref, button, modbutton : import dat; 21Point, Rect, Image : import drawm; 22min, max, error, warning, stralloc, strfree : import utils; 23font, draw : import graph; 24black, white, mainwin : import gui; 25Buffer : import bufferm; 26Body, Text, Tag : import textm; 27File : import filem; 28Xfid : import Xfidm; 29scrdraw : import scrl; 30tagcols, textcols : import acme; 31BORD : import Framem; 32 33init(mods : ref Dat->Mods) 34{ 35 sys = mods.sys; 36 dat = mods.dat; 37 utils = mods.utils; 38 drawm = mods.draw; 39 graph = mods.graph; 40 gui = mods.gui; 41 textm = mods.textm; 42 filem = mods.filem; 43 bufferm = mods.bufferm; 44 look = mods.look; 45 scrl = mods.scroll; 46 acme = mods.acme; 47} 48 49winid : int; 50nullwin : Window; 51 52Window.init(w : self ref Window, clone : ref Window, r : Rect) 53{ 54 r1, br : Rect; 55 f : ref File; 56 rf : ref Reffont; 57 rp : ref Astring; 58 nc : int; 59 dummy : ref File = nil; 60 61 c := w.col; 62 *w = nullwin; 63 w.col = c; 64 w.nopen = array[Dat->QMAX] of byte; 65 for (i := 0; i < Dat->QMAX; i++) 66 w.nopen[i] = byte 0; 67 w.qlock = Lock.init(); 68 w.ctllock = Lock.init(); 69 w.refx = Ref.init(); 70 w.tag = textm->newtext(); 71 w.tag.w = w; 72 w.body = textm->newtext(); 73 w.body.w = w; 74 w.id = ++winid; 75 w.refx.inc(); 76 w.ctlfid = ~0; 77 w.utflastqid = -1; 78 r1 = r; 79 r1.max.y = r1.min.y + font.height; 80 reffont.r.inc(); 81 f = dummy.addtext(w.tag); 82 w.tag.init(f, r1, reffont, tagcols); 83 w.tag.what = Tag; 84 # tag is a copy of the contents, not a tracked image 85 if(clone != nil){ 86 w.tag.delete(0, w.tag.file.buf.nc, TRUE); 87 nc = clone.tag.file.buf.nc; 88 rp = utils->stralloc(nc); 89 clone.tag.file.buf.read(0, rp, 0, nc); 90 w.tag.insert(0, rp.s, nc, TRUE, 0); 91 utils->strfree(rp); 92 rp = nil; 93 w.tag.file.reset(); 94 w.tag.setselect(nc, nc); 95 } 96 r1 = r; 97 r1.min.y += font.height + 1; 98 if(r1.max.y < r1.min.y) 99 r1.max.y = r1.min.y; 100 f = nil; 101 if(clone != nil){ 102 f = clone.body.file; 103 w.body.org = clone.body.org; 104 w.isscratch = clone.isscratch; 105 rf = Reffont.get(FALSE, FALSE, FALSE, clone.body.reffont.f.name); 106 }else 107 rf = Reffont.get(FALSE, FALSE, FALSE, nil); 108 f = f.addtext(w.body); 109 w.body.what = Body; 110 w.body.init(f, r1, rf, textcols); 111 r1.min.y -= 1; 112 r1.max.y = r1.min.y+1; 113 draw(mainwin, r1, tagcols[BORD], nil, (0, 0)); 114 scrdraw(w.body); 115 w.r = r; 116 w.r.max.y = w.body.frame.r.max.y; 117 br.min = w.tag.scrollr.min; 118 br.max.x = br.min.x + button.r.dx(); 119 br.max.y = br.min.y + button.r.dy(); 120 draw(mainwin, br, button, nil, button.r.min); 121 w.filemenu = TRUE; 122 w.maxlines = w.body.frame.maxlines; 123 if(clone != nil){ 124 w.dirty = clone.dirty; 125 w.body.setselect(clone.body.q0, clone.body.q1); 126 w.settag(); 127 } 128} 129 130Window.reshape(w : self ref Window, r : Rect, safe : int) : int 131{ 132 r1, br : Rect; 133 y : int; 134 b : ref Image; 135 136 r1 = r; 137 r1.max.y = r1.min.y + font.height; 138 y = r1.max.y; 139 if(!safe || !w.tag.frame.r.eq(r1)){ 140 y = w.tag.reshape(r1); 141 b = button; 142 if(w.body.file.mod && !w.isdir && !w.isscratch) 143 b = modbutton; 144 br.min = w.tag.scrollr.min; 145 br.max.x = br.min.x + b.r.dx(); 146 br.max.y = br.min.y + b.r.dy(); 147 draw(mainwin, br, b, nil, b.r.min); 148 } 149 if(!safe || !w.body.frame.r.eq(r1)){ 150 if(y+1+font.height > r.max.y){ # no body 151 r1.min.y = y; 152 r1.max.y = y; 153 w.body.reshape(r1); 154 w.r = r; 155 w.r.max.y = y; 156 return y; 157 } 158 r1 = r; 159 r1.min.y = y; 160 r1.max.y = y + 1; 161 draw(mainwin, r1, tagcols[BORD], nil, (0, 0)); 162 r1.min.y = y + 1; 163 r1.max.y = r.max.y; 164 y = w.body.reshape(r1); 165 w.r = r; 166 w.r.max.y = y; 167 scrdraw(w.body); 168 } 169 w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines)); 170 return w.r.max.y; 171} 172 173Window.lock1(w : self ref Window, owner : int) 174{ 175 w.refx.inc(); 176 w.qlock.lock(); 177 w.owner = owner; 178} 179 180Window.lock(w : self ref Window, owner : int) 181{ 182 i : int; 183 f : ref File; 184 185 f = w.body.file; 186 for(i=0; i<f.ntext; i++) 187 f.text[i].w.lock1(owner); 188} 189 190Window.unlock(w : self ref Window) 191{ 192 f := w.body.file; 193 # 194 # subtle: loop runs backwards to avoid tripping over 195 # winclose indirectly editing f.text and freeing f 196 # on the last iteration of the loop 197 # 198 for(i:=f.ntext-1; i>=0; i--){ 199 w = f.text[i].w; 200 w.owner = 0; 201 w.qlock.unlock(); 202 w.close(); 203 } 204} 205 206Window.mousebut(w : self ref Window) 207{ 208 graph->cursorset(w.tag.scrollr.min.add(w.tag.scrollr.max).div(2)); 209} 210 211Window.dirfree(w : self ref Window) 212{ 213 i : int; 214 dl : ref Dat->Dirlist; 215 216 if(w.isdir){ 217 for(i=0; i<w.ndl; i++){ 218 dl = w.dlp[i]; 219 dl.r = nil; 220 dl = nil; 221 } 222 } 223 w.dlp = nil; 224 w.ndl = 0; 225} 226 227Window.close(w : self ref Window) 228{ 229 i : int; 230 231 if(w.refx.dec() == 0){ 232 w.dirfree(); 233 w.tag.close(); 234 w.body.close(); 235 if(dat->activewin == w) 236 dat->activewin = nil; 237 for(i=0; i<w.nincl; i++) 238 w.incl[i] = nil; 239 w.incl = nil; 240 w.events = nil; 241 w = nil; 242 } 243} 244 245Window.delete(w : self ref Window) 246{ 247 x : ref Xfid; 248 249 x = w.eventx; 250 if(x != nil){ 251 w.nevents = 0; 252 w.events = nil; 253 w.eventx = nil; 254 x.c <-= Xfidm->Xnil; 255 } 256} 257 258Window.undo(w : self ref Window, isundo : int) 259{ 260 body : ref Text; 261 i : int; 262 f : ref File; 263 v : ref Window; 264 265 if(w==nil) 266 return; 267 w.utflastqid = -1; 268 body = w.body; 269 (body.q0, body.q1) = body.file.undo(isundo, body.q0, body.q1); 270 body.show(body.q0, body.q1, TRUE); 271 f = body.file; 272 for(i=0; i<f.ntext; i++){ 273 v = f.text[i].w; 274 v.dirty = (f.seq != v.putseq); 275 if(v != w){ 276 v.body.q0 = v.body.frame.p0+v.body.org; 277 v.body.q1 = v.body.frame.p1+v.body.org; 278 } 279 } 280 w.settag(); 281} 282 283Window.setname(w : self ref Window, name : string, n : int) 284{ 285 t : ref Text; 286 v : ref Window; 287 i : int; 288 289 t = w.body; 290 if(t.file.name == name) 291 return; 292 w.isscratch = FALSE; 293 if(n>=6 && name[n-6:n] == "/guide") 294 w.isscratch = TRUE; 295 else if(n>=7 && name[n-7:n] == "+Errors") 296 w.isscratch = TRUE; 297 t.file.setname(name, n); 298 for(i=0; i<t.file.ntext; i++){ 299 v = t.file.text[i].w; 300 v.settag(); 301 v.isscratch = w.isscratch; 302 } 303} 304 305Window.typex(w : self ref Window, t : ref Text, r : int) 306{ 307 i : int; 308 309 t.typex(r, w.echomode); 310 if(t.what == Body) 311 for(i=0; i<t.file.ntext; i++) 312 scrdraw(t.file.text[i]); 313 w.settag(); 314} 315 316Window.cleartag(w : self ref Window) 317{ 318 i, n : int; 319 r : ref Astring; 320 321 # w must be committed 322 n = w.tag.file.buf.nc; 323 r = utils->stralloc(n); 324 w.tag.file.buf.read(0, r, 0, n); 325 for(i=0; i<n; i++) 326 if(r.s[i]==' ' || r.s[i]=='\t') 327 break; 328 for(; i<n; i++) 329 if(r.s[i] == '|') 330 break; 331 if(i == n) 332 return; 333 i++; 334 w.tag.delete(i, n, TRUE); 335 utils->strfree(r); 336 r = nil; 337 w.tag.file.mod = FALSE; 338 if(w.tag.q0 > i) 339 w.tag.q0 = i; 340 if(w.tag.q1 > i) 341 w.tag.q1 = i; 342 w.tag.setselect(w.tag.q0, w.tag.q1); 343} 344 345Window.settag(w : self ref Window) 346{ 347 i : int; 348 f : ref File; 349 350 f = w.body.file; 351 for(i=0; i<f.ntext; i++){ 352 v := f.text[i].w; 353 if(v.col.safe || v.body.frame.maxlines>0) 354 v.settag1(); 355 } 356} 357 358Window.settag1(w : self ref Window) 359{ 360 ii, j, k, n, bar, dirty : int; 361 old : ref Astring; 362 new : string; 363 r : int; 364 b : ref Image; 365 q0, q1 : int; 366 br : Rect; 367 368 if(w.tag.ncache!=0 || w.tag.file.mod) 369 w.commit(w.tag); # check file name; also can now modify tag 370 old = utils->stralloc(w.tag.file.buf.nc); 371 w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc); 372 for(ii=0; ii<w.tag.file.buf.nc; ii++) 373 if(old.s[ii]==' ' || old.s[ii]=='\t') 374 break; 375 if(old.s[0:ii] != w.body.file.name){ 376 w.tag.delete(0, ii, TRUE); 377 w.tag.insert(0, w.body.file.name, len w.body.file.name, TRUE, 0); 378 strfree(old); 379 old = nil; 380 old = utils->stralloc(w.tag.file.buf.nc); 381 w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc); 382 } 383 new = w.body.file.name + " Del Snarf"; 384 if(w.filemenu){ 385 if(w.body.file.delta.nc>0 || w.body.ncache) 386 new += " Undo"; 387 if(w.body.file.epsilon.nc > 0) 388 new += " Redo"; 389 dirty = w.body.file.name != nil && (w.body.ncache || w.body.file.seq!=w.putseq); 390 if(!w.isdir && dirty) 391 new += " Put"; 392 } 393 if(w.isdir) 394 new += " Get"; 395 l := len w.body.file.name; 396 if(l >= 2 && w.body.file.name[l-2: ] == ".b") 397 new += " Limbo"; 398 new += " |"; 399 r = utils->strchr(old.s, '|'); 400 if(r >= 0) 401 k = r+1; 402 else{ 403 k = w.tag.file.buf.nc; 404 if(w.body.file.seq == 0) 405 new += " Look "; 406 } 407 if(new != old.s[0:k]){ 408 n = k; 409 if(n > len new) 410 n = len new; 411 for(j=0; j<n; j++) 412 if(old.s[j] != new[j]) 413 break; 414 q0 = w.tag.q0; 415 q1 = w.tag.q1; 416 w.tag.delete(j, k, TRUE); 417 w.tag.insert(j, new[j:], len new - j, TRUE, 0); 418 # try to preserve user selection 419 r = utils->strchr(old.s, '|'); 420 if(r >= 0){ 421 bar = r; 422 if(q0 > bar){ 423 bar = utils->strchr(new, '|')-bar; 424 w.tag.q0 = q0+bar; 425 w.tag.q1 = q1+bar; 426 } 427 } 428 } 429 strfree(old); 430 old = nil; 431 new = nil; 432 w.tag.file.mod = FALSE; 433 n = w.tag.file.buf.nc+w.tag.ncache; 434 if(w.tag.q0 > n) 435 w.tag.q0 = n; 436 if(w.tag.q1 > n) 437 w.tag.q1 = n; 438 w.tag.setselect(w.tag.q0, w.tag.q1); 439 b = button; 440 if(!w.isdir && !w.isscratch && (w.body.file.mod || w.body.ncache)) 441 b = modbutton; 442 br.min = w.tag.scrollr.min; 443 br.max.x = br.min.x + b.r.dx(); 444 br.max.y = br.min.y + b.r.dy(); 445 draw(mainwin, br, b, nil, b.r.min); 446} 447 448Window.commit(w : self ref Window, t : ref Text) 449{ 450 r : ref Astring; 451 i : int; 452 f : ref File; 453 454 t.commit(TRUE); 455 f = t.file; 456 if(f.ntext > 1) 457 for(i=0; i<f.ntext; i++) 458 f.text[i].commit(FALSE); # no-op for t 459 if(t.what == Body) 460 return; 461 r = utils->stralloc(w.tag.file.buf.nc); 462 w.tag.file.buf.read(0, r, 0, w.tag.file.buf.nc); 463 for(i=0; i<w.tag.file.buf.nc; i++) 464 if(r.s[i]==' ' || r.s[i]=='\t') 465 break; 466 if(r.s[0:i] != w.body.file.name){ 467 dat->seq++; 468 w.body.file.mark(); 469 w.body.file.mod = TRUE; 470 w.dirty = TRUE; 471 w.setname(r.s, i); 472 w.settag(); 473 } 474 utils->strfree(r); 475 r = nil; 476} 477 478Window.addincl(w : self ref Window, r : string, n : int) 479{ 480 { 481 (ok, d) := sys->stat(r); 482 if(ok < 0){ 483 if(r[0] == '/') 484 raise "e"; 485 (r, n) = look->dirname(w.body, r, n); 486 (ok, d) = sys->stat(r); 487 if(ok < 0) 488 raise "e"; 489 } 490 if((d.mode&Sys->DMDIR) == 0){ 491 warning(nil, sprint("%s: not a directory\n", r)); 492 r = nil; 493 return; 494 } 495 w.nincl++; 496 owi := w.incl; 497 w.incl = array[w.nincl] of string; 498 w.incl[1:] = owi[0:w.nincl-1]; 499 owi = nil; 500 w.incl[0] = r; 501 r = nil; 502 } 503 exception{ 504 * => 505 warning(nil, sprint("%s: %r\n", r)); 506 r = nil; 507 } 508} 509 510Window.clean(w : self ref Window, conservative : int, exiting : int) : int # as it stands, conservative is always TRUE 511{ 512 if(w.isscratch || w.isdir) # don't whine if it's a guide file, error window, etc. 513 return TRUE; 514 if((!conservative||exiting) && w.nopen[Dat->QWevent]>byte 0) 515 return TRUE; 516 if(w.dirty){ 517 if(w.body.file.name != nil) 518 warning(nil, sprint("%s modified\n", w.body.file.name)); 519 else{ 520 if(w.body.file.buf.nc < 100) # don't whine if it's too small 521 return TRUE; 522 warning(nil, "unnamed file modified\n"); 523 } 524 w.dirty = FALSE; 525 return FALSE; 526 } 527 return TRUE; 528} 529 530Window.ctlprint(w : self ref Window, fonts : int) : string 531{ 532 s := sprint("%11d %11d %11d %11d %11d ", w.id, w.tag.file.buf.nc, 533 w.body.file.buf.nc, w.isdir, w.dirty); 534 if(fonts) 535 return sprint("%s%11d %q %11d ", s, w.body.frame.r.dx(), 536 w.body.reffont.f.name, w.body.frame.maxtab); 537 return s; 538} 539 540Window.event(w : self ref Window, fmt : string) 541{ 542 n : int; 543 x : ref Xfid; 544 545 if(w.nopen[Dat->QWevent] == byte 0) 546 return; 547 if(w.owner == 0) 548 error("no window owner"); 549 n = len fmt; 550 w.events[len w.events] = w.owner; 551 w.events += fmt; 552 w.nevents += n+1; 553 x = w.eventx; 554 if(x != nil){ 555 w.eventx = nil; 556 x.c <-= Xfidm->Xnil; 557 } 558} 559