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