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