]> git.decadent.org.uk Git - ion3.git/blob - mod_query/listing.c
Imported Upstream version 20090110
[ion3.git] / mod_query / listing.c
1 /*
2  * ion/mod_query/listing.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10 #include <limits.h>
11
12 #include <ioncore/common.h>
13 #include <ioncore/global.h>
14 #include <ioncore/gr.h>
15 #include <ioncore/strings.h>
16 #include "listing.h"
17
18
19 #define COL_SPACING 16
20 #define CONT_INDENT "xx"
21 #define CONT_INDENT_LEN 2
22 #define ITEMROWS(L, R) ((L)->iteminfos==NULL ? 1 : (L)->iteminfos[R].n_parts)
23
24
25 static int strings_maxw(GrBrush *brush, char **strs, int nstrs)
26 {
27     int maxw=0, w, i;
28     
29     for(i=0; i<nstrs; i++){
30         w=grbrush_get_text_width(brush, strs[i], strlen(strs[i]));
31         if(w>maxw)
32             maxw=w;
33     }
34     
35     return maxw;
36 }
37
38
39 static int getbeg(GrBrush *brush, int maxw, char *str, int l, int *wret)
40 {
41     int n=0, nprev=0, w;
42     GrFontExtents fnte;
43     
44     if(maxw<=0){
45         *wret=0;
46         return 0;
47     }
48     
49     grbrush_get_font_extents(brush, &fnte);
50     
51     if(fnte.max_width!=0){
52         /* Do an initial skip. */
53         int n2=maxw/fnte.max_width;
54     
55         n=0;
56         while(n2>0){
57             n+=str_nextoff(str, n);
58             n2--;
59         }
60     }
61     
62     w=grbrush_get_text_width(brush, str, n);
63     nprev=n;
64     *wret=w;
65
66     while(w<=maxw){
67         *wret=w;
68         nprev=n;
69         n+=str_nextoff(str, n);
70         if(n==nprev)
71             break;
72         w=grbrush_get_text_width(brush, str, n);
73     }
74     
75     return nprev;
76 }
77
78
79 static void reset_iteminfo(WListingItemInfo *iinf)
80 {
81     iinf->n_parts=1;
82     if(iinf->part_lens!=NULL){
83         free(iinf->part_lens);
84         iinf->part_lens=NULL;
85     }
86 }
87
88
89 static void string_do_calc_parts(GrBrush *brush, int maxw, char *str, int l,
90                                  WListingItemInfo *iinf,
91                                  int wrapw, int ciw)
92 {
93     int i=iinf->n_parts, l2=l, w;
94     int rmaxw=maxw-(i==0 ? 0 : ciw);
95     
96     iinf->n_parts++;
97     
98     w=grbrush_get_text_width(brush, str, l);
99     
100     if(w>rmaxw){
101         l2=getbeg(brush, rmaxw-wrapw, str, l, &w);
102         if(l2<=0)
103             l2=1;
104     }
105         
106     if(l2<l){
107         string_do_calc_parts(brush, maxw, str+l2, l-l2, iinf, wrapw, ciw);
108     }else{
109         int *p=(int*)realloc(iinf->part_lens, iinf->n_parts*sizeof(int));
110         if(p==NULL){
111             reset_iteminfo(iinf);
112         }else{
113             iinf->part_lens=p;
114         }
115     }
116
117     if(iinf->part_lens!=NULL)
118         iinf->part_lens[i]=l2;
119 }
120
121
122 static void string_calc_parts(GrBrush *brush, int maxw, char *str,
123                               WListingItemInfo *iinf)
124 {
125     int wrapw=grbrush_get_text_width(brush, "\\", 1);
126     int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
127     int i, s;
128
129     iinf->n_parts=0;
130     iinf->len=strlen(str);
131
132     if(maxw<=0)
133         reset_iteminfo(iinf);
134     else
135         string_do_calc_parts(brush, maxw, str, iinf->len, iinf, wrapw, ciw);
136 }
137
138
139 static void draw_multirow(GrBrush *brush, int x, int y, int h, 
140                           char *str, WListingItemInfo *iinf,
141                           int maxw, int ciw, int wrapw)
142 {
143     int i, l;
144     
145     if(iinf==NULL){
146         grbrush_draw_string(brush, x, y, str, strlen(str), TRUE);
147         return;
148     }
149
150     assert(iinf->n_parts>=1);
151     if(iinf->part_lens==NULL){
152         assert(iinf->n_parts==1);
153         l=iinf->len;
154     }else{
155         l=iinf->part_lens[0];
156     }
157
158     grbrush_draw_string(brush, x, y, str, l, TRUE);
159     
160     for(i=1; i<iinf->n_parts; i++){
161         grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE);
162         
163         y+=h;
164         str+=l;
165         if(i==1){
166             x+=ciw;
167             maxw-=ciw;
168         }
169         l=iinf->part_lens[i];
170             
171         grbrush_draw_string(brush, x, y, str, l, TRUE);
172     }
173 }
174
175                           
176 static int col_fit(int w, int itemw, int spacing)
177 {
178     int ncol=1;
179     int tmp=w-itemw;
180     itemw+=spacing;
181     
182     if(tmp>0)
183         ncol+=tmp/itemw;
184     
185     return ncol;
186 }
187
188 static bool one_row_up(WListing *l, int *ip, int *rp)
189 {
190     int i=*ip, r=*rp;
191     int ir=ITEMROWS(l, i);
192     
193     if(r>0){
194         (*rp)--;
195         return TRUE;
196     }
197     
198     if(i==0)
199         return FALSE;
200     
201     (*ip)--;
202     *rp=ITEMROWS(l, i-1)-1;
203     return TRUE;
204 }
205
206
207 static bool one_row_down(WListing *l, int *ip, int *rp)
208 {
209     int i=*ip, r=*rp;
210     int ir=ITEMROWS(l, i);
211     
212     if(r<ir-1){
213         (*rp)++;
214         return TRUE;
215     }
216     
217     if(i==l->nitemcol-1)
218         return FALSE;
219     
220     (*ip)++;
221     *rp=0;
222     return TRUE;
223 }
224
225
226 void setup_listing(WListing *l, char **strs, int nstrs, bool onecol)
227 {
228     if(l->strs!=NULL)
229         deinit_listing(l);
230
231     l->iteminfos=ALLOC_N(WListingItemInfo, nstrs);
232     l->strs=strs;
233     l->nstrs=nstrs;
234     l->onecol=onecol;
235     l->selected_str=-1;
236 }
237
238
239 void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l)
240 {
241     int ncol, nrow=0, visrow=INT_MAX;
242     int i, maxw, w, h;
243     GrFontExtents fnte;
244     GrBorderWidths bdw;
245     
246     grbrush_get_font_extents(brush, &fnte);
247     grbrush_get_border_widths(brush, &bdw);
248     
249     w=geom->w-bdw.left-bdw.right;
250     h=geom->h-bdw.top-bdw.bottom;
251     
252     maxw=strings_maxw(brush, l->strs, l->nstrs);
253     l->itemw=maxw+COL_SPACING;
254     l->itemh=fnte.max_height;
255     
256     if(l->onecol)
257         ncol=1;
258     else
259         ncol=col_fit(w, l->itemw-COL_SPACING, COL_SPACING);
260
261     if(l->iteminfos!=NULL){
262         for(i=0; i<l->nstrs; i++){
263             if(ncol!=1){
264                 reset_iteminfo(&(l->iteminfos[i]));
265                 l->iteminfos[i].len=strlen(l->strs[i]);
266             }else{
267                 string_calc_parts(brush, w, l->strs[i], &(l->iteminfos[i]));
268             }
269             nrow+=l->iteminfos[i].n_parts;
270         }
271     }else{
272         nrow=l->nstrs;
273     }
274     
275     if(ncol>1){
276         nrow=l->nstrs/ncol+(l->nstrs%ncol ? 1 : 0);
277         l->nitemcol=nrow;
278     }else{
279         l->nitemcol=l->nstrs;
280     }
281     
282     if(l->itemh>0)
283         visrow=h/l->itemh;
284     
285     if(visrow>nrow)
286         visrow=nrow;
287     
288     l->ncol=ncol;
289     l->nrow=nrow;
290     l->visrow=visrow;
291     l->toth=visrow*l->itemh;
292
293 #if 0
294     l->firstitem=l->nitemcol-1;
295     l->firstoff=ITEMROWS(l, l->nitemcol-1)-1;
296     for(i=1; i<visrow; i++)
297         one_row_up(l, &(l->firstitem), &(l->firstoff));
298 #else
299     l->firstitem=0;
300     l->firstoff=0;
301 #endif
302
303 }
304
305
306 void deinit_listing(WListing *l)
307 {
308     int i;
309     
310     if(l->strs==NULL)
311         return;
312     
313     while(l->nstrs--){
314         free(l->strs[l->nstrs]);
315         if(l->iteminfos!=NULL)
316             reset_iteminfo(&(l->iteminfos[l->nstrs]));
317     }
318
319     free(l->strs);
320     l->strs=NULL;
321     
322     if(l->iteminfos!=NULL){
323         free(l->iteminfos);
324         l->iteminfos=NULL;
325     }
326 }
327
328
329 void init_listing(WListing *l)
330 {
331     l->nstrs=0;
332     l->strs=NULL;
333     l->iteminfos=NULL;
334     l->nstrs=0;
335     l->selected_str=-1;
336     l->onecol=TRUE;
337     l->itemw=0;
338     l->itemh=0;
339     l->ncol=0;
340     l->nrow=0;
341     l->nitemcol=0;
342     l->visrow=0;
343     l->toth=0;
344 }
345
346
347 static void do_draw_listing(GrBrush *brush, const WRectangle *geom, 
348                             WListing *l, GrAttr selattr, int mode)
349 {
350     int wrapw=grbrush_get_text_width(brush, "\\", 1);
351     int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
352     int r, c, i, x, y;
353     GrFontExtents fnte;
354     
355     if(l->nitemcol==0 || l->visrow==0)
356         return;
357     
358     grbrush_get_font_extents(brush, &fnte);
359     
360     x=0;
361     c=0;
362     while(1){
363         y=geom->y+fnte.baseline;
364         i=l->firstitem+c*l->nitemcol;
365         r=-l->firstoff;
366         y+=r*l->itemh;
367         while(r<l->visrow){
368             if(i>=l->nstrs)
369                 return;
370             
371             if(mode>=0 || 
372                l->selected_str==i ||
373                LISTING_DRAW_GET_SELECTED(mode)==i){
374             
375                 if(i==l->selected_str)
376                     grbrush_set_attr(brush, selattr);
377                     
378                 draw_multirow(brush, geom->x+x, y, l->itemh, l->strs[i],
379                               (l->iteminfos!=NULL ? &(l->iteminfos[i]) : NULL),
380                               geom->w-x, ciw, wrapw);
381                 
382                 if(i==l->selected_str)
383                     grbrush_unset_attr(brush, selattr);
384             }
385
386             y+=l->itemh*ITEMROWS(l, i);
387             r+=ITEMROWS(l, i);
388             i++;
389         }
390         x+=l->itemw;
391         c++;
392     }
393 }
394
395
396 static int prevsel=-1;
397
398 static bool filteridx_sel(WListing *l, int i)
399 {
400     return (i==prevsel || i==l->selected_str);
401 }
402
403
404 void draw_listing(GrBrush *brush, const WRectangle *geom,
405                   WListing *l, int mode, GrAttr selattr)
406 {
407     WRectangle geom2;
408     GrBorderWidths bdw;
409     
410     grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR
411                                |GRBRUSH_NEED_CLIP);
412     
413     if(mode==LISTING_DRAW_COMPLETE)
414         grbrush_clear_area(brush, geom);
415     
416     grbrush_draw_border(brush, geom);
417
418     grbrush_get_border_widths(brush, &bdw);
419     
420     geom2.x=geom->x+bdw.left;
421     geom2.y=geom->y+bdw.top;
422     geom2.w=geom->w-bdw.left-bdw.right;
423     geom2.h=geom->h-bdw.top-bdw.bottom;
424     
425     do_draw_listing(brush, &geom2, l, selattr, mode);
426     
427     grbrush_end(brush);
428 }
429
430
431 static bool do_scrollup_listing(WListing *l, int n)
432 {
433     int i=l->firstitem;
434     int r=l->firstoff;
435     bool ret=FALSE;
436     
437     while(n>0){
438         if(!one_row_up(l, &i, &r))
439             break;
440         ret=TRUE;
441         n--;
442     }
443
444     l->firstitem=i;
445     l->firstoff=r;
446     
447     return ret;
448 }
449
450
451 static bool do_scrolldown_listing(WListing *l, int n)
452 {
453     int i=l->firstitem;
454     int r=l->firstoff;
455     int br=r, bi=i;
456     int bc=l->visrow;
457     bool ret=FALSE;
458     
459     while(--bc>0)
460         one_row_down(l, &bi, &br);
461     
462     while(n>0){
463         if(!one_row_down(l, &bi, &br))
464             break;
465         one_row_down(l, &i, &r);
466         ret=TRUE;
467         n--;
468     }
469
470     l->firstitem=i;
471     l->firstoff=r;
472     
473     return ret;
474 }
475
476
477 bool scrollup_listing(WListing *l)
478 {
479     return do_scrollup_listing(l, l->visrow);
480 }
481
482
483 bool scrolldown_listing(WListing *l)
484 {
485     return do_scrolldown_listing(l, l->visrow);
486 }
487
488
489 static int listing_first_row_of_item(WListing *l, int i)
490 {
491     int fci=i%l->nitemcol, j;
492     int r=0;
493     
494     for(j=0; j<fci; j++)
495         r+=ITEMROWS(l, j);
496     
497     return r;
498 }
499
500
501 static int listing_first_visible_row(WListing *l)
502 {
503     return listing_first_row_of_item(l, l->firstitem)+l->firstoff;
504 }
505
506
507 int listing_select(WListing *l, int i)
508 {
509     int irow, frow, lrow;
510     int redraw;
511     
512     redraw=LISTING_DRAW_SELECTED(l->selected_str);
513     
514     if(i<0){
515         l->selected_str=-1;
516         return redraw;
517     }
518     
519     assert(i<l->nstrs);
520     
521     l->selected_str=i;
522     
523     /* Adjust visible area */
524     
525     irow=listing_first_row_of_item(l, i);
526     frow=listing_first_visible_row(l);
527     
528     while(irow<frow){
529         one_row_up(l, &(l->firstitem), &(l->firstoff));
530         frow--;
531         redraw=LISTING_DRAW_COMPLETE;
532     }
533
534     irow+=ITEMROWS(l, i)-1;
535     lrow=frow+l->visrow-1;
536     
537     while(irow>lrow){
538         one_row_down(l, &(l->firstitem), &(l->firstoff));
539         lrow++;
540         redraw=LISTING_DRAW_COMPLETE;
541     }
542     
543     return redraw;
544 }
545