URI: 
       tcols.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tcols.c (12516B)
       ---
            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 <libsec.h>
           12 #include "dat.h"
           13 #include "fns.h"
           14 
           15 static Rune Lheader[] = {
           16         'N', 'e', 'w', ' ',
           17         'C', 'u', 't', ' ',
           18         'P', 'a', 's', 't', 'e', ' ',
           19         'S', 'n', 'a', 'r', 'f', ' ',
           20         'S', 'o', 'r', 't', ' ',
           21         'Z', 'e', 'r', 'o', 'x', ' ',
           22         'D', 'e', 'l', 'c', 'o', 'l', ' ',
           23         0
           24 };
           25 
           26 void
           27 colinit(Column *c, Rectangle r)
           28 {
           29         Rectangle r1;
           30         Text *t;
           31 
           32         draw(screen, r, display->white, nil, ZP);
           33         c->r = r;
           34         c->w = nil;
           35         c->nw = 0;
           36         t = &c->tag;
           37         t->w = nil;
           38         t->col = c;
           39         r1 = r;
           40         r1.max.y = r1.min.y + font->height;
           41         textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols);
           42         t->what = Columntag;
           43         r1.min.y = r1.max.y;
           44         r1.max.y += Border;
           45         draw(screen, r1, display->black, nil, ZP);
           46         textinsert(t, 0, Lheader, 38, TRUE);
           47         textsetselect(t, t->file->b.nc, t->file->b.nc);
           48         draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
           49         c->safe = TRUE;
           50 }
           51 
           52 Window*
           53 coladd(Column *c, Window *w, Window *clone, int y)
           54 {
           55         Rectangle r, r1;
           56         Window *v;
           57         int i, j, minht, ymax, buggered;
           58 
           59         v = nil;
           60         r = c->r;
           61         r.min.y = c->tag.fr.r.max.y+Border;
           62         if(y<r.min.y && c->nw>0){        /* steal half of last window by default */
           63                 v = c->w[c->nw-1];
           64                 y = v->body.fr.r.min.y+Dy(v->body.fr.r)/2;
           65         }
           66         /* look for window we'll land on */
           67         for(i=0; i<c->nw; i++){
           68                 v = c->w[i];
           69                 if(y < v->r.max.y)
           70                         break;
           71         }
           72         buggered = 0;
           73         if(c->nw > 0){
           74                 if(i < c->nw)
           75                         i++;        /* new window will go after v */
           76                 /*
           77                  * if landing window (v) is too small, grow it first.
           78                  */
           79                 minht = v->tag.fr.font->height+Border+1;
           80                 j = 0;
           81                 while(!c->safe || v->body.fr.maxlines<=3 || Dy(v->body.all) <= minht){
           82                         if(++j > 10){
           83                                 buggered = 1;        /* too many windows in column */
           84                                 break;
           85                         }
           86                         colgrow(c, v, 1);
           87                 }
           88 
           89                 /*
           90                  * figure out where to split v to make room for w
           91                  */
           92 
           93                 /* new window stops where next window begins */
           94                 if(i < c->nw)
           95                         ymax = c->w[i]->r.min.y-Border;
           96                 else
           97                         ymax = c->r.max.y;
           98 
           99                 /* new window must start after v's tag ends */
          100                 y = max(y, v->tagtop.max.y+Border);
          101 
          102                 /* new window must start early enough to end before ymax */
          103                 y = min(y, ymax - minht);
          104 
          105                 /* if y is too small, too many windows in column */
          106                 if(y < v->tagtop.max.y+Border)
          107                         buggered = 1;
          108 
          109                 /*
          110                  * resize & redraw v
          111                  */
          112                 r = v->r;
          113                 r.max.y = ymax;
          114                 draw(screen, r, textcols[BACK], nil, ZP);
          115                 r1 = r;
          116                 y = min(y, ymax-(v->tag.fr.font->height*v->taglines+v->body.fr.font->height+Border+1));
          117                 r1.max.y = min(y, v->body.fr.r.min.y+v->body.fr.nlines*v->body.fr.font->height);
          118                 r1.min.y = winresize(v, r1, FALSE, FALSE);
          119                 r1.max.y = r1.min.y+Border;
          120                 draw(screen, r1, display->black, nil, ZP);
          121 
          122                 /*
          123                  * leave r with w's coordinates
          124                  */
          125                 r.min.y = r1.max.y;
          126         }
          127         if(w == nil){
          128                 w = emalloc(sizeof(Window));
          129                 w->col = c;
          130                 draw(screen, r, textcols[BACK], nil, ZP);
          131                 wininit(w, clone, r);
          132         }else{
          133                 w->col = c;
          134                 winresize(w, r, FALSE, TRUE);
          135         }
          136         w->tag.col = c;
          137         w->tag.row = c->row;
          138         w->body.col = c;
          139         w->body.row = c->row;
          140         c->w = realloc(c->w, (c->nw+1)*sizeof(Window*));
          141         memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*));
          142         c->nw++;
          143         c->w[i] = w;
          144         c->safe = TRUE;
          145 
          146         /* if there were too many windows, redraw the whole column */
          147         if(buggered)
          148                 colresize(c, c->r);
          149 
          150         savemouse(w);
          151         /* near the button, but in the body */
          152         moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3)));
          153         barttext = &w->body;
          154         return w;
          155 }
          156 
          157 void
          158 colclose(Column *c, Window *w, int dofree)
          159 {
          160         Rectangle r;
          161         int i, didmouse, up;
          162 
          163         /* w is locked */
          164         if(!c->safe)
          165                 colgrow(c, w, 1);
          166         for(i=0; i<c->nw; i++)
          167                 if(c->w[i] == w)
          168                         goto Found;
          169         error("can't find window");
          170   Found:
          171         r = w->r;
          172         w->tag.col = nil;
          173         w->body.col = nil;
          174         w->col = nil;
          175         didmouse = restoremouse(w);
          176         if(dofree){
          177                 windelete(w);
          178                 winclose(w);
          179         }
          180         c->nw--;
          181         memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
          182         c->w = realloc(c->w, c->nw*sizeof(Window*));
          183         if(c->nw == 0){
          184                 draw(screen, r, display->white, nil, ZP);
          185                 return;
          186         }
          187         up = 0;
          188         if(i == c->nw){                /* extend last window down */
          189                 w = c->w[i-1];
          190                 r.min.y = w->r.min.y;
          191                 r.max.y = c->r.max.y;
          192         }else{                        /* extend next window up */
          193                 up = 1;
          194                 w = c->w[i];
          195                 r.max.y = w->r.max.y;
          196         }
          197         draw(screen, r, textcols[BACK], nil, ZP);
          198         if(c->safe) {
          199                 if(!didmouse && up)
          200                         w->showdel = TRUE;
          201                 winresize(w, r, FALSE, TRUE);
          202                 if(!didmouse && up)
          203                         movetodel(w);
          204         }
          205 }
          206 
          207 void
          208 colcloseall(Column *c)
          209 {
          210         int i;
          211         Window *w;
          212 
          213         if(c == activecol)
          214                 activecol = nil;
          215         textclose(&c->tag);
          216         for(i=0; i<c->nw; i++){
          217                 w = c->w[i];
          218                 winclose(w);
          219         }
          220         c->nw = 0;
          221         free(c->w);
          222         free(c);
          223         clearmouse();
          224 }
          225 
          226 void
          227 colmousebut(Column *c)
          228 {
          229         moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2));
          230 }
          231 
          232 void
          233 colresize(Column *c, Rectangle r)
          234 {
          235         int i, old, new;
          236         Rectangle r1, r2;
          237         Window *w;
          238 
          239         clearmouse();
          240         r1 = r;
          241         r1.max.y = r1.min.y + c->tag.fr.font->height;
          242         textresize(&c->tag, r1, TRUE);
          243         draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
          244         r1.min.y = r1.max.y;
          245         r1.max.y += Border;
          246         draw(screen, r1, display->black, nil, ZP);
          247         r1.max.y = r.max.y;
          248         new = Dy(r) - c->nw*(Border + font->height);
          249         old = Dy(c->r) - c->nw*(Border + font->height);
          250         for(i=0; i<c->nw; i++){
          251                 w = c->w[i];
          252                 w->maxlines = 0;
          253                 if(i == c->nw-1)
          254                         r1.max.y = r.max.y;
          255                 else{
          256                         r1.max.y = r1.min.y;
          257                         if(new > 0 && old > 0 && Dy(w->r) > Border+font->height){
          258                                 r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height;
          259                         }
          260                 }
          261                 r1.max.y = max(r1.max.y, r1.min.y + Border+font->height);
          262                 r2 = r1;
          263                 r2.max.y = r2.min.y+Border;
          264                 draw(screen, r2, display->black, nil, ZP);
          265                 r1.min.y = r2.max.y;
          266                 r1.min.y = winresize(w, r1, FALSE, i==c->nw-1);
          267         }
          268         c->r = r;
          269 }
          270 
          271 static
          272 int
          273 colcmp(const void *a, const void *b)
          274 {
          275         Rune *r1, *r2;
          276         int i, nr1, nr2;
          277 
          278         r1 = (*(Window**)a)->body.file->name;
          279         nr1 = (*(Window**)a)->body.file->nname;
          280         r2 = (*(Window**)b)->body.file->name;
          281         nr2 = (*(Window**)b)->body.file->nname;
          282         for(i=0; i<nr1 && i<nr2; i++){
          283                 if(*r1 != *r2)
          284                         return *r1-*r2;
          285                 r1++;
          286                 r2++;
          287         }
          288         return nr1-nr2;
          289 }
          290 
          291 void
          292 colsort(Column *c)
          293 {
          294         int i, y;
          295         Rectangle r, r1, *rp;
          296         Window **wp, *w;
          297 
          298         if(c->nw == 0)
          299                 return;
          300         clearmouse();
          301         rp = emalloc(c->nw*sizeof(Rectangle));
          302         wp = emalloc(c->nw*sizeof(Window*));
          303         memmove(wp, c->w, c->nw*sizeof(Window*));
          304         qsort(wp, c->nw, sizeof(Window*), colcmp);
          305         for(i=0; i<c->nw; i++)
          306                 rp[i] = wp[i]->r;
          307         r = c->r;
          308         r.min.y = c->tag.fr.r.max.y;
          309         draw(screen, r, textcols[BACK], nil, ZP);
          310         y = r.min.y;
          311         for(i=0; i<c->nw; i++){
          312                 w = wp[i];
          313                 r.min.y = y;
          314                 if(i == c->nw-1)
          315                         r.max.y = c->r.max.y;
          316                 else
          317                         r.max.y = r.min.y+Dy(w->r)+Border;
          318                 r1 = r;
          319                 r1.max.y = r1.min.y+Border;
          320                 draw(screen, r1, display->black, nil, ZP);
          321                 r.min.y = r1.max.y;
          322                 y = winresize(w, r, FALSE, i==c->nw-1);
          323         }
          324         free(rp);
          325         free(c->w);
          326         c->w = wp;
          327 }
          328 
          329 void
          330 colgrow(Column *c, Window *w, int but)
          331 {
          332         Rectangle r, cr;
          333         int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h;
          334         Window *v;
          335 
          336         for(i=0; i<c->nw; i++)
          337                 if(c->w[i] == w)
          338                         goto Found;
          339         error("can't find window");
          340 
          341   Found:
          342         cr = c->r;
          343         if(but < 0){        /* make sure window fills its own space properly */
          344                 r = w->r;
          345                 if(i==c->nw-1 || c->safe==FALSE)
          346                         r.max.y = cr.max.y;
          347                 else
          348                         r.max.y = c->w[i+1]->r.min.y - Border;
          349                 winresize(w, r, FALSE, TRUE);
          350                 return;
          351         }
          352         cr.min.y = c->w[0]->r.min.y;
          353         if(but == 3){        /* full size */
          354                 if(i != 0){
          355                         v = c->w[0];
          356                         c->w[0] = w;
          357                         c->w[i] = v;
          358                 }
          359                 draw(screen, cr, textcols[BACK], nil, ZP);
          360                 winresize(w, cr, FALSE, TRUE);
          361                 for(i=1; i<c->nw; i++)
          362                         c->w[i]->body.fr.maxlines = 0;
          363                 c->safe = FALSE;
          364                 return;
          365         }
          366         /* store old #lines for each window */
          367         onl = w->body.fr.maxlines;
          368         nl = emalloc(c->nw * sizeof(int));
          369         ny = emalloc(c->nw * sizeof(int));
          370         tot = 0;
          371         for(j=0; j<c->nw; j++){
          372                 l = c->w[j]->taglines-1 + c->w[j]->body.fr.maxlines;
          373                 nl[j] = l;
          374                 tot += l;
          375         }
          376         /* approximate new #lines for this window */
          377         if(but == 2){        /* as big as can be */
          378                 memset(nl, 0, c->nw * sizeof(int));
          379                 goto Pack;
          380         }
          381         nnl = min(onl + max(min(5, w->taglines-1+w->maxlines), onl/2), tot);
          382         if(nnl < w->taglines-1+w->maxlines)
          383                 nnl = (w->taglines-1+w->maxlines + nnl)/2;
          384         if(nnl == 0)
          385                 nnl = 2;
          386         dnl = nnl - onl;
          387         /* compute new #lines for each window */
          388         for(k=1; k<c->nw; k++){
          389                 /* prune from later window */
          390                 j = i+k;
          391                 if(j<c->nw && nl[j]){
          392                         l = min(dnl, max(1, nl[j]/2));
          393                         nl[j] -= l;
          394                         nl[i] += l;
          395                         dnl -= l;
          396                 }
          397                 /* prune from earlier window */
          398                 j = i-k;
          399                 if(j>=0 && nl[j]){
          400                         l = min(dnl, max(1, nl[j]/2));
          401                         nl[j] -= l;
          402                         nl[i] += l;
          403                         dnl -= l;
          404                 }
          405         }
          406     Pack:
          407         /* pack everyone above */
          408         y1 = cr.min.y;
          409         for(j=0; j<i; j++){
          410                 v = c->w[j];
          411                 r = v->r;
          412                 r.min.y = y1;
          413                 r.max.y = y1+Dy(v->tagtop);
          414                 if(nl[j])
          415                         r.max.y += 1 + nl[j]*v->body.fr.font->height;
          416                 r.min.y = winresize(v, r, c->safe, FALSE);
          417                 r.max.y += Border;
          418                 draw(screen, r, display->black, nil, ZP);
          419                 y1 = r.max.y;
          420         }
          421         /* scan to see new size of everyone below */
          422         y2 = c->r.max.y;
          423         for(j=c->nw-1; j>i; j--){
          424                 v = c->w[j];
          425                 r = v->r;
          426                 r.min.y = y2-Dy(v->tagtop);
          427                 if(nl[j])
          428                         r.min.y -= 1 + nl[j]*v->body.fr.font->height;
          429                 r.min.y -= Border;
          430                 ny[j] = r.min.y;
          431                 y2 = r.min.y;
          432         }
          433         /* compute new size of window */
          434         r = w->r;
          435         r.min.y = y1;
          436         r.max.y = y2;
          437         h = w->body.fr.font->height;
          438         if(Dy(r) < Dy(w->tagtop)+1+h+Border)
          439                 r.max.y = r.min.y + Dy(w->tagtop)+1+h+Border;
          440         /* draw window */
          441         r.max.y = winresize(w, r, c->safe, TRUE);
          442         if(i < c->nw-1){
          443                 r.min.y = r.max.y;
          444                 r.max.y += Border;
          445                 draw(screen, r, display->black, nil, ZP);
          446                 for(j=i+1; j<c->nw; j++)
          447                         ny[j] -= (y2-r.max.y);
          448         }
          449         /* pack everyone below */
          450         y1 = r.max.y;
          451         for(j=i+1; j<c->nw; j++){
          452                 v = c->w[j];
          453                 r = v->r;
          454                 r.min.y = y1;
          455                 r.max.y = y1+Dy(v->tagtop);
          456                 if(nl[j])
          457                         r.max.y += 1 + nl[j]*v->body.fr.font->height;
          458                 y1 = winresize(v, r, c->safe, j==c->nw-1);
          459                 if(j < c->nw-1){        /* no border on last window */
          460                         r.min.y = y1;
          461                         r.max.y += Border;
          462                         draw(screen, r, display->black, nil, ZP);
          463                         y1 = r.max.y;
          464                 }
          465         }
          466         free(nl);
          467         free(ny);
          468         c->safe = TRUE;
          469         winmousebut(w);
          470 }
          471 
          472 void
          473 coldragwin(Column *c, Window *w, int but)
          474 {
          475         Rectangle r;
          476         int i, b;
          477         Point p, op;
          478         Window *v;
          479         Column *nc;
          480 
          481         clearmouse();
          482         setcursor2(mousectl, &boxcursor, &boxcursor2);
          483         b = mouse->buttons;
          484         op = mouse->xy;
          485         while(mouse->buttons == b)
          486                 readmouse(mousectl);
          487         setcursor(mousectl, nil);
          488         if(mouse->buttons){
          489                 while(mouse->buttons)
          490                         readmouse(mousectl);
          491                 return;
          492         }
          493 
          494         for(i=0; i<c->nw; i++)
          495                 if(c->w[i] == w)
          496                         goto Found;
          497         error("can't find window");
          498 
          499   Found:
          500         if(w->tagexpand)        /* force recomputation of window tag size */
          501                 w->taglines = 1;
          502         p = mouse->xy;
          503         if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
          504                 colgrow(c, w, but);
          505                 winmousebut(w);
          506                 return;
          507         }
          508         /* is it a flick to the right? */
          509         if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c)
          510                 p.x = op.x+Dx(w->r);        /* yes: toss to next column */
          511         nc = rowwhichcol(c->row, p);
          512         if(nc!=nil && nc!=c){
          513                 colclose(c, w, FALSE);
          514                 coladd(nc, w, nil, p.y);
          515                 winmousebut(w);
          516                 return;
          517         }
          518         if(i==0 && c->nw==1)
          519                 return;                        /* can't do it */
          520         if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y)
          521         || (i==0 && p.y>w->r.max.y)){
          522                 /* shuffle */
          523                 colclose(c, w, FALSE);
          524                 coladd(c, w, nil, p.y);
          525                 winmousebut(w);
          526                 return;
          527         }
          528         if(i == 0)
          529                 return;
          530         v = c->w[i-1];
          531         if(p.y < v->tagtop.max.y)
          532                 p.y = v->tagtop.max.y;
          533         if(p.y > w->r.max.y-Dy(w->tagtop)-Border)
          534                 p.y = w->r.max.y-Dy(w->tagtop)-Border;
          535         r = v->r;
          536         r.max.y = p.y;
          537         if(r.max.y > v->body.fr.r.min.y){
          538                 r.max.y -= (r.max.y-v->body.fr.r.min.y)%v->body.fr.font->height;
          539                 if(v->body.fr.r.min.y == v->body.fr.r.max.y)
          540                         r.max.y++;
          541         }
          542         r.min.y = winresize(v, r, c->safe, FALSE);
          543         r.max.y = r.min.y+Border;
          544         draw(screen, r, display->black, nil, ZP);
          545         r.min.y = r.max.y;
          546         if(i == c->nw-1)
          547                 r.max.y = c->r.max.y;
          548         else
          549                 r.max.y = c->w[i+1]->r.min.y-Border;
          550         winresize(w, r, c->safe, TRUE);
          551         c->safe = TRUE;
          552         winmousebut(w);
          553 }
          554 
          555 Text*
          556 colwhich(Column *c, Point p)
          557 {
          558         int i;
          559         Window *w;
          560 
          561         if(!ptinrect(p, c->r))
          562                 return nil;
          563         if(ptinrect(p, c->tag.all))
          564                 return &c->tag;
          565         for(i=0; i<c->nw; i++){
          566                 w = c->w[i];
          567                 if(ptinrect(p, w->r)){
          568                         if(ptinrect(p, w->tagtop) || ptinrect(p, w->tag.all))
          569                                 return &w->tag;
          570                         /* exclude partial line at bottom */
          571                         if(p.x >= w->body.scrollr.max.x && p.y >= w->body.fr.r.max.y)
          572                                 return nil;
          573                         return &w->body;
          574                 }
          575         }
          576         return nil;
          577 }
          578 
          579 int
          580 colclean(Column *c)
          581 {
          582         int i, clean;
          583 
          584         clean = TRUE;
          585         for(i=0; i<c->nw; i++)
          586                 clean &= winclean(c->w[i], TRUE);
          587         return clean;
          588 }