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