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