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