1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <plumb.h> 11 #include "dat.h" 12 #include "fns.h" 13 14 int winid; 15 16 void 17 wininit(Window *w, Window *clone, Rectangle r) 18 { 19 Rectangle r1, br; 20 File *f; 21 Reffont *rf; 22 Rune *rp; 23 int nc; 24 25 w->tag.w = w; 26 w->body.w = w; 27 w->id = ++winid; 28 incref(w); 29 if(globalincref) 30 incref(w); 31 w->ctlfid = ~0; 32 w->utflastqid = -1; 33 r1 = r; 34 r1.max.y = r1.min.y + font->height; 35 incref(&reffont); 36 f = fileaddtext(nil, &w->tag); 37 textinit(&w->tag, f, r1, &reffont, tagcols); 38 w->tag.what = Tag; 39 /* tag is a copy of the contents, not a tracked image */ 40 if(clone){ 41 textdelete(&w->tag, 0, w->tag.file->nc, TRUE); 42 nc = clone->tag.file->nc; 43 rp = runemalloc(nc); 44 bufread(clone->tag.file, 0, rp, nc); 45 textinsert(&w->tag, 0, rp, nc, TRUE); 46 free(rp); 47 filereset(w->tag.file); 48 textsetselect(&w->tag, nc, nc); 49 } 50 r1 = r; 51 r1.min.y += font->height + 1; 52 if(r1.max.y < r1.min.y) 53 r1.max.y = r1.min.y; 54 f = nil; 55 if(clone){ 56 f = clone->body.file; 57 w->body.org = clone->body.org; 58 w->isscratch = clone->isscratch; 59 rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name); 60 }else 61 rf = rfget(FALSE, FALSE, FALSE, nil); 62 f = fileaddtext(f, &w->body); 63 w->body.what = Body; 64 textinit(&w->body, f, r1, rf, textcols); 65 r1.min.y -= 1; 66 r1.max.y = r1.min.y+1; 67 draw(screen, r1, tagcols[BORD], nil, ZP); 68 textscrdraw(&w->body); 69 w->r = r; 70 w->r.max.y = w->body.r.max.y; 71 br.min = w->tag.scrollr.min; 72 br.max.x = br.min.x + Dx(button->r); 73 br.max.y = br.min.y + Dy(button->r); 74 draw(screen, br, button, nil, button->r.min); 75 w->filemenu = TRUE; 76 w->maxlines = w->body.maxlines; 77 w->autoindent = globalautoindent; 78 if(clone){ 79 w->dirty = clone->dirty; 80 textsetselect(&w->body, clone->body.q0, clone->body.q1); 81 winsettag(w); 82 w->autoindent = clone->autoindent; 83 } 84 } 85 86 int 87 winresize(Window *w, Rectangle r, int safe) 88 { 89 Rectangle r1; 90 int y; 91 Image *b; 92 Rectangle br; 93 94 r1 = r; 95 r1.max.y = r1.min.y + font->height; 96 y = r1.max.y; 97 if(!safe || !eqrect(w->tag.r, r1)){ 98 y = textresize(&w->tag, r1); 99 b = button; 100 if(w->body.file->mod && !w->isdir && !w->isscratch) 101 b = modbutton; 102 br.min = w->tag.scrollr.min; 103 br.max.x = br.min.x + Dx(b->r); 104 br.max.y = br.min.y + Dy(b->r); 105 draw(screen, br, b, nil, b->r.min); 106 } 107 if(!safe || !eqrect(w->body.r, r1)){ 108 if(y+1+font->height > r.max.y){ /* no body */ 109 r1.min.y = y; 110 r1.max.y = y; 111 textresize(&w->body, r1); 112 w->r = r; 113 w->r.max.y = y; 114 return y; 115 } 116 r1 = r; 117 r1.min.y = y; 118 r1.max.y = y + 1; 119 draw(screen, r1, tagcols[BORD], nil, ZP); 120 r1.min.y = y + 1; 121 r1.max.y = r.max.y; 122 y = textresize(&w->body, r1); 123 w->r = r; 124 w->r.max.y = y; 125 textscrdraw(&w->body); 126 } 127 w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines)); 128 return w->r.max.y; 129 } 130 131 void 132 winlock1(Window *w, int owner) 133 { 134 incref(w); 135 qlock(w); 136 w->owner = owner; 137 } 138 139 void 140 winlock(Window *w, int owner) 141 { 142 int i; 143 File *f; 144 145 f = w->body.file; 146 for(i=0; i<f->ntext; i++) 147 winlock1(f->text[i]->w, owner); 148 } 149 150 void 151 winunlock(Window *w) 152 { 153 int i; 154 File *f; 155 156 /* 157 * subtle: loop runs backwards to avoid tripping over 158 * winclose indirectly editing f->text and freeing f 159 * on the last iteration of the loop. 160 */ 161 f = w->body.file; 162 for(i=f->ntext-1; i>=0; i--){ 163 w = f->text[i]->w; 164 w->owner = 0; 165 qunlock(w); 166 winclose(w); 167 } 168 } 169 170 void 171 winmousebut(Window *w) 172 { 173 moveto(mousectl, divpt(addpt(w->tag.scrollr.min, w->tag.scrollr.max), 2)); 174 } 175 176 void 177 windirfree(Window *w) 178 { 179 int i; 180 Dirlist *dl; 181 182 if(w->isdir){ 183 for(i=0; i<w->ndl; i++){ 184 dl = w->dlp[i]; 185 free(dl->r); 186 free(dl); 187 } 188 free(w->dlp); 189 } 190 w->dlp = nil; 191 w->ndl = 0; 192 } 193 194 void 195 winclose(Window *w) 196 { 197 int i; 198 199 if(decref(w) == 0){ 200 windirfree(w); 201 textclose(&w->tag); 202 textclose(&w->body); 203 if(activewin == w) 204 activewin = nil; 205 for(i=0; i<w->nincl; i++) 206 free(w->incl[i]); 207 free(w->incl); 208 free(w->events); 209 free(w); 210 } 211 } 212 213 void 214 windelete(Window *w) 215 { 216 Xfid *x; 217 218 x = w->eventx; 219 if(x){ 220 w->nevents = 0; 221 free(w->events); 222 w->events = nil; 223 w->eventx = nil; 224 sendp(x->c, nil); /* wake him up */ 225 } 226 } 227 228 void 229 winundo(Window *w, int isundo) 230 { 231 Text *body; 232 int i; 233 File *f; 234 Window *v; 235 236 w->utflastqid = -1; 237 body = &w->body; 238 fileundo(body->file, isundo, &body->q0, &body->q1); 239 textshow(body, body->q0, body->q1, 1); 240 f = body->file; 241 for(i=0; i<f->ntext; i++){ 242 v = f->text[i]->w; 243 v->dirty = (f->seq != v->putseq); 244 if(v != w){ 245 v->body.q0 = v->body.p0+v->body.org; 246 v->body.q1 = v->body.p1+v->body.org; 247 } 248 } 249 winsettag(w); 250 } 251 252 void 253 winsetname(Window *w, Rune *name, int n) 254 { 255 Text *t; 256 Window *v; 257 int i; 258 259 t = &w->body; 260 if(runeeq(t->file->name, t->file->nname, name, n) == TRUE) 261 return; 262 w->isscratch = FALSE; 263 if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6)) 264 w->isscratch = TRUE; 265 else if(n>=7 && runeeq(L"+Errors", 7, name+(n-7), 7)) 266 w->isscratch = TRUE; 267 filesetname(t->file, name, n); 268 for(i=0; i<t->file->ntext; i++){ 269 v = t->file->text[i]->w; 270 winsettag(v); 271 v->isscratch = w->isscratch; 272 } 273 } 274 275 void 276 wintype(Window *w, Text *t, Rune r) 277 { 278 int i; 279 280 texttype(t, r); 281 if(t->what == Body) 282 for(i=0; i<t->file->ntext; i++) 283 textscrdraw(t->file->text[i]); 284 winsettag(w); 285 } 286 287 void 288 wincleartag(Window *w) 289 { 290 int i, n; 291 Rune *r; 292 293 /* w must be committed */ 294 n = w->tag.file->nc; 295 r = runemalloc(n); 296 bufread(w->tag.file, 0, r, n); 297 for(i=0; i<n; i++) 298 if(r[i]==' ' || r[i]=='\t') 299 break; 300 for(; i<n; i++) 301 if(r[i] == '|') 302 break; 303 if(i == n) 304 return; 305 i++; 306 textdelete(&w->tag, i, n, TRUE); 307 free(r); 308 w->tag.file->mod = FALSE; 309 if(w->tag.q0 > i) 310 w->tag.q0 = i; 311 if(w->tag.q1 > i) 312 w->tag.q1 = i; 313 textsetselect(&w->tag, w->tag.q0, w->tag.q1); 314 } 315 316 void 317 winsettag1(Window *w) 318 { 319 int i, j, k, n, bar, dirty; 320 Rune *new, *old, *r; 321 Image *b; 322 uint q0, q1; 323 Rectangle br; 324 325 /* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */ 326 if(w->tag.ncache!=0 || w->tag.file->mod) 327 wincommit(w, &w->tag); /* check file name; also guarantees we can modify tag contents */ 328 old = runemalloc(w->tag.file->nc+1); 329 bufread(w->tag.file, 0, old, w->tag.file->nc); 330 old[w->tag.file->nc] = '\0'; 331 for(i=0; i<w->tag.file->nc; i++) 332 if(old[i]==' ' || old[i]=='\t') 333 break; 334 if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){ 335 textdelete(&w->tag, 0, i, TRUE); 336 textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE); 337 free(old); 338 old = runemalloc(w->tag.file->nc+1); 339 bufread(w->tag.file, 0, old, w->tag.file->nc); 340 old[w->tag.file->nc] = '\0'; 341 } 342 new = runemalloc(w->body.file->nname+100); 343 i = 0; 344 runemove(new+i, w->body.file->name, w->body.file->nname); 345 i += w->body.file->nname; 346 runemove(new+i, L" Del Snarf", 10); 347 i += 10; 348 if(w->filemenu){ 349 if(w->body.file->delta.nc>0 || w->body.ncache){ 350 runemove(new+i, L" Undo", 5); 351 i += 5; 352 } 353 if(w->body.file->epsilon.nc > 0){ 354 runemove(new+i, L" Redo", 5); 355 i += 5; 356 } 357 dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq); 358 if(!w->isdir && dirty){ 359 runemove(new+i, L" Put", 4); 360 i += 4; 361 } 362 } 363 if(w->isdir){ 364 runemove(new+i, L" Get", 4); 365 i += 4; 366 } 367 runemove(new+i, L" |", 2); 368 i += 2; 369 r = runestrchr(old, '|'); 370 if(r) 371 k = r-old+1; 372 else{ 373 k = w->tag.file->nc; 374 if(w->body.file->seq == 0){ 375 runemove(new+i, L" Look ", 6); 376 i += 6; 377 } 378 } 379 if(runeeq(new, i, old, k) == FALSE){ 380 n = k; 381 if(n > i) 382 n = i; 383 for(j=0; j<n; j++) 384 if(old[j] != new[j]) 385 break; 386 q0 = w->tag.q0; 387 q1 = w->tag.q1; 388 textdelete(&w->tag, j, k, TRUE); 389 textinsert(&w->tag, j, new+j, i-j, TRUE); 390 /* try to preserve user selection */ 391 r = runestrchr(old, '|'); 392 if(r){ 393 bar = r-old; 394 if(q0 > bar){ 395 bar = (runestrchr(new, '|')-new)-bar; 396 w->tag.q0 = q0+bar; 397 w->tag.q1 = q1+bar; 398 } 399 } 400 } 401 free(old); 402 free(new); 403 w->tag.file->mod = FALSE; 404 n = w->tag.file->nc+w->tag.ncache; 405 if(w->tag.q0 > n) 406 w->tag.q0 = n; 407 if(w->tag.q1 > n) 408 w->tag.q1 = n; 409 textsetselect(&w->tag, w->tag.q0, w->tag.q1); 410 b = button; 411 if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache)) 412 b = modbutton; 413 br.min = w->tag.scrollr.min; 414 br.max.x = br.min.x + Dx(b->r); 415 br.max.y = br.min.y + Dy(b->r); 416 draw(screen, br, b, nil, b->r.min); 417 } 418 419 void 420 winsettag(Window *w) 421 { 422 int i; 423 File *f; 424 Window *v; 425 426 f = w->body.file; 427 for(i=0; i<f->ntext; i++){ 428 v = f->text[i]->w; 429 if(v->col->safe || v->body.maxlines>0) 430 winsettag1(v); 431 } 432 } 433 434 void 435 wincommit(Window *w, Text *t) 436 { 437 Rune *r; 438 int i; 439 File *f; 440 441 textcommit(t, TRUE); 442 f = t->file; 443 if(f->ntext > 1) 444 for(i=0; i<f->ntext; i++) 445 textcommit(f->text[i], FALSE); /* no-op for t */ 446 if(t->what == Body) 447 return; 448 r = runemalloc(w->tag.file->nc); 449 bufread(w->tag.file, 0, r, w->tag.file->nc); 450 for(i=0; i<w->tag.file->nc; i++) 451 if(r[i]==' ' || r[i]=='\t') 452 break; 453 if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){ 454 seq++; 455 filemark(w->body.file); 456 w->body.file->mod = TRUE; 457 w->dirty = TRUE; 458 winsetname(w, r, i); 459 winsettag(w); 460 } 461 free(r); 462 } 463 464 void 465 winaddincl(Window *w, Rune *r, int n) 466 { 467 char *a; 468 Dir *d; 469 Runestr rs; 470 471 a = runetobyte(r, n); 472 d = dirstat(a); 473 if(d == nil){ 474 if(a[0] == '/') 475 goto Rescue; 476 rs = dirname(&w->body, r, n); 477 r = rs.r; 478 n = rs.nr; 479 free(a); 480 a = runetobyte(r, n); 481 d = dirstat(a); 482 if(d == nil) 483 goto Rescue; 484 r = runerealloc(r, n+1); 485 r[n] = 0; 486 } 487 free(a); 488 if((d->qid.type&QTDIR) == 0){ 489 free(d); 490 warning(nil, "%s: not a directory\n", a); 491 free(r); 492 return; 493 } 494 free(d); 495 w->nincl++; 496 w->incl = realloc(w->incl, w->nincl*sizeof(Rune*)); 497 memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*)); 498 w->incl[0] = runemalloc(n+1); 499 runemove(w->incl[0], r, n); 500 free(r); 501 return; 502 503 Rescue: 504 warning(nil, "%s: %r\n", a); 505 free(r); 506 free(a); 507 return; 508 } 509 510 int 511 winclean(Window *w, int conservative) /* as it stands, conservative is always TRUE */ 512 { 513 if(w->isscratch || w->isdir) /* don't whine if it's a guide file, error window, etc. */ 514 return TRUE; 515 if(!conservative && w->nopen[QWevent]>0) 516 return TRUE; 517 if(w->dirty){ 518 if(w->body.file->nname) 519 warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name); 520 else{ 521 if(w->body.file->nc < 100) /* don't whine if it's too small */ 522 return TRUE; 523 warning(nil, "unnamed file modified\n"); 524 } 525 w->dirty = FALSE; 526 return FALSE; 527 } 528 return TRUE; 529 } 530 531 char* 532 winctlprint(Window *w, char *buf, int fonts) 533 { 534 sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc, 535 w->body.file->nc, w->isdir, w->dirty); 536 if(fonts) 537 return smprint("%s%11d %q %11d " , buf, Dx(w->body.r), 538 w->body.reffont->f->name, w->body.maxtab); 539 return buf; 540 } 541 542 void 543 winevent(Window *w, char *fmt, ...) 544 { 545 int n; 546 char *b; 547 Xfid *x; 548 va_list arg; 549 550 if(w->nopen[QWevent] == 0) 551 return; 552 if(w->owner == 0) 553 error("no window owner"); 554 va_start(arg, fmt); 555 b = vsmprint(fmt, arg); 556 va_end(arg); 557 if(b == nil) 558 error("vsmprint failed"); 559 n = strlen(b); 560 w->events = realloc(w->events, w->nevents+1+n); 561 w->events[w->nevents++] = w->owner; 562 memmove(w->events+w->nevents, b, n); 563 free(b); 564 w->nevents += n; 565 x = w->eventx; 566 if(x){ 567 w->eventx = nil; 568 sendp(x->c, nil); 569 } 570 } 571