]> git.decadent.org.uk Git - ion3.git/blob - mod_query/listing.c
[svn-inject] Installing original source of ion3
[ion3.git] / mod_query / listing.c
1 /*
2  * ion/mod_query/listing.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
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, const char *style)
145 {
146     int i, l;
147     
148     if(iinf==NULL){
149         grbrush_draw_string(brush, x, y, str, strlen(str), TRUE, style);
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, style);
162     
163     for(i=1; i<iinf->n_parts; i++){
164         grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE, style);
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, style);
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, const char *style,
352                             const char *selstyle)
353 {
354     int wrapw=grbrush_get_text_width(brush, "\\", 1);
355     int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
356     int r, c, i, x, y;
357     GrFontExtents fnte;
358     
359     if(l->nitemcol==0 || l->visrow==0)
360         return;
361     
362     grbrush_get_font_extents(brush, &fnte);
363     
364     x=0;
365     c=0;
366     while(1){
367         y=geom->y+fnte.baseline;
368         i=l->firstitem+c*l->nitemcol;
369         r=-l->firstoff;
370         y+=r*l->itemh;
371         while(r<l->visrow){
372             if(i>=l->nstrs)
373                 return;
374             
375             draw_multirow(brush, geom->x+x, y, l->itemh, l->strs[i],
376                           (l->iteminfos!=NULL ? &(l->iteminfos[i]) : NULL),
377                           geom->w-x, ciw, wrapw, 
378                           (i==l->selected_str ? selstyle : style));
379
380             y+=l->itemh*ITEMROWS(l, i);
381             r+=ITEMROWS(l, i);
382             i++;
383         }
384         x+=l->itemw;
385         c++;
386     }
387 }
388
389
390 void draw_listing(GrBrush *brush, const WRectangle *geom,
391                   WListing *l, bool complete, const char *style,
392                   const char *selstyle)
393 {
394     WRectangle geom2;
395     GrBorderWidths bdw;
396                          
397     grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_NEED_CLIP);
398
399     if(complete)
400         grbrush_clear_area(brush, geom);
401     
402     grbrush_draw_border(brush, geom, style);
403
404     grbrush_get_border_widths(brush, &bdw);
405     
406     geom2.x=geom->x+bdw.left;
407     geom2.y=geom->y+bdw.top;
408     geom2.w=geom->w-bdw.left-bdw.right;
409     geom2.h=geom->h-bdw.top-bdw.bottom;
410     
411     do_draw_listing(brush, &geom2, l, style, selstyle);
412     
413     grbrush_end(brush);
414 }
415
416
417 static bool do_scrollup_listing(WListing *l, int n)
418 {
419     int i=l->firstitem;
420     int r=l->firstoff;
421     bool ret=FALSE;
422     
423     while(n>0){
424         if(!one_row_up(l, &i, &r))
425             break;
426         ret=TRUE;
427         n--;
428     }
429
430     l->firstitem=i;
431     l->firstoff=r;
432     
433     return ret;
434 }
435
436
437 static bool do_scrolldown_listing(WListing *l, int n)
438 {
439     int i=l->firstitem;
440     int r=l->firstoff;
441     int br=r, bi=i;
442     int bc=l->visrow;
443     bool ret=FALSE;
444     
445     while(--bc>0)
446         one_row_down(l, &bi, &br);
447     
448     while(n>0){
449         if(!one_row_down(l, &bi, &br))
450             break;
451         one_row_down(l, &i, &r);
452         ret=TRUE;
453         n--;
454     }
455
456     l->firstitem=i;
457     l->firstoff=r;
458     
459     return ret;
460 }
461
462
463 bool scrollup_listing(WListing *l)
464 {
465     return do_scrollup_listing(l, l->visrow);
466 }
467
468
469 bool scrolldown_listing(WListing *l)
470 {
471     return do_scrolldown_listing(l, l->visrow);
472 }
473
474
475 static int listing_first_row_of_item(WListing *l, int i)
476 {
477     int fci=i%l->nitemcol, j;
478     int r=0;
479     
480     for(j=0; j<fci; j++)
481         r+=ITEMROWS(l, j);
482     
483     return r;
484 }
485
486
487 static int listing_first_visible_row(WListing *l)
488 {
489     return listing_first_row_of_item(l, l->firstitem)+l->firstoff;
490 }
491
492
493 bool listing_select(WListing *l, int i)
494 {
495     int irow, frow, lrow;
496     bool complredraw=FALSE;
497     
498     if(i<0){
499         l->selected_str=-1;
500         return FALSE;
501     }
502     
503     assert(i<l->nstrs);
504     
505     l->selected_str=i;
506     
507     /* Adjust visible area */
508     
509     irow=listing_first_row_of_item(l, i);
510     frow=listing_first_visible_row(l);
511     
512     while(irow<frow){
513         one_row_up(l, &(l->firstitem), &(l->firstoff));
514         frow--;
515         complredraw=TRUE;
516     }
517
518     irow+=ITEMROWS(l, i)-1;
519     lrow=frow+l->visrow-1;
520     
521     while(irow>lrow){
522         one_row_down(l, &(l->firstitem), &(l->firstoff));
523         lrow++;
524         complredraw=TRUE;
525     }
526     
527     return complredraw;
528 }
529