Released version 3-2015061300
[notion.git] / mod_query / listing.c
blob90d9bedcb4cd9eda477a37dfa421987183f850a6
1 /*
2 * ion/mod_query/listing.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
10 #include <limits.h>
12 #include <ioncore/common.h>
13 #include <ioncore/global.h>
14 #include <ioncore/gr.h>
15 #include <ioncore/strings.h>
16 #include "listing.h"
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)
25 static int strings_maxw(GrBrush *brush, char **strs, int nstrs)
27 int maxw=0, w, i;
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;
35 return maxw;
39 static int getbeg(GrBrush *brush, int maxw, char *str, int *wret)
41 int n=0, nprev=0, w;
42 GrFontExtents fnte;
44 if(maxw<=0){
45 *wret=0;
46 return 0;
49 grbrush_get_font_extents(brush, &fnte);
51 if(fnte.max_width!=0){
52 /* Do an initial skip. */
53 int n2=maxw/fnte.max_width;
55 n=0;
56 while(n2>0){
57 n+=str_nextoff(str, n);
58 n2--;
62 w=grbrush_get_text_width(brush, str, n);
63 nprev=n;
64 *wret=w;
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);
75 return nprev;
79 static void reset_iteminfo(WListingItemInfo *iinf)
81 iinf->n_parts=1;
82 if(iinf->part_lens!=NULL){
83 free(iinf->part_lens);
84 iinf->part_lens=NULL;
89 static void string_do_calc_parts(GrBrush *brush, int maxw, char *str, int l,
90 WListingItemInfo *iinf,
91 int wrapw, int ciw)
93 int i=iinf->n_parts, l2=l, w;
94 int rmaxw=maxw-(i==0 ? 0 : ciw);
96 iinf->n_parts++;
98 w=grbrush_get_text_width(brush, str, l);
100 if(w>rmaxw){
101 l2=getbeg(brush, rmaxw-wrapw, str, &w);
102 if(l2<=0)
103 l2=1;
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;
117 if(iinf->part_lens!=NULL)
118 iinf->part_lens[i]=l2;
122 static void string_calc_parts(GrBrush *brush, int maxw, char *str,
123 WListingItemInfo *iinf)
125 int wrapw=grbrush_get_text_width(brush, "\\", 1);
126 int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
128 iinf->n_parts=0;
129 iinf->len=strlen(str);
131 if(maxw<=0)
132 reset_iteminfo(iinf);
133 else
134 string_do_calc_parts(brush, maxw, str, iinf->len, iinf, wrapw, ciw);
138 static void draw_multirow(GrBrush *brush, int x, int y, int h,
139 char *str, WListingItemInfo *iinf,
140 int maxw, int ciw, int wrapw)
142 int i, l;
144 if(iinf==NULL){
145 grbrush_draw_string(brush, x, y, str, strlen(str), TRUE);
146 return;
149 assert(iinf->n_parts>=1);
150 if(iinf->part_lens==NULL){
151 assert(iinf->n_parts==1);
152 l=iinf->len;
153 }else{
154 l=iinf->part_lens[0];
157 grbrush_draw_string(brush, x, y, str, l, TRUE);
159 for(i=1; i<iinf->n_parts; i++){
160 grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE);
162 y+=h;
163 str+=l;
164 if(i==1){
165 x+=ciw;
166 maxw-=ciw;
168 l=iinf->part_lens[i];
170 grbrush_draw_string(brush, x, y, str, l, TRUE);
175 static int col_fit(int w, int itemw, int spacing)
177 int ncol=1;
178 int tmp=w-itemw;
179 itemw+=spacing;
181 if(tmp>0)
182 ncol+=tmp/itemw;
184 return ncol;
187 static bool one_row_up(WListing *l, int *ip, int *rp)
189 int i=*ip, r=*rp;
191 if(r>0){
192 (*rp)--;
193 return TRUE;
196 if(i==0)
197 return FALSE;
199 (*ip)--;
200 *rp=ITEMROWS(l, i-1)-1;
201 return TRUE;
205 static bool one_row_down(WListing *l, int *ip, int *rp)
207 int i=*ip, r=*rp;
208 int ir=ITEMROWS(l, i);
210 if(r<ir-1){
211 (*rp)++;
212 return TRUE;
215 if(i==l->nitemcol-1)
216 return FALSE;
218 (*ip)++;
219 *rp=0;
220 return TRUE;
224 void setup_listing(WListing *l, char **strs, int nstrs, bool onecol)
226 if(l->strs!=NULL)
227 deinit_listing(l);
229 l->iteminfos=ALLOC_N(WListingItemInfo, nstrs);
230 l->strs=strs;
231 l->nstrs=nstrs;
232 l->onecol=onecol;
233 l->selected_str=-1;
237 void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l)
239 int ncol, nrow=0, visrow=INT_MAX;
240 int i, maxw, w, h;
241 GrFontExtents fnte;
242 GrBorderWidths bdw;
244 grbrush_get_font_extents(brush, &fnte);
245 grbrush_get_border_widths(brush, &bdw);
247 w=geom->w-bdw.left-bdw.right;
248 h=geom->h-bdw.top-bdw.bottom;
250 maxw=strings_maxw(brush, l->strs, l->nstrs);
251 l->itemw=maxw+COL_SPACING;
252 l->itemh=fnte.max_height;
254 if(l->onecol)
255 ncol=1;
256 else
257 ncol=col_fit(w, l->itemw-COL_SPACING, COL_SPACING);
259 if(l->iteminfos!=NULL){
260 for(i=0; i<l->nstrs; i++){
261 if(ncol!=1){
262 reset_iteminfo(&(l->iteminfos[i]));
263 l->iteminfos[i].len=strlen(l->strs[i]);
264 }else{
265 string_calc_parts(brush, w, l->strs[i], &(l->iteminfos[i]));
267 nrow+=l->iteminfos[i].n_parts;
269 }else{
270 nrow=l->nstrs;
273 if(ncol>1){
274 nrow=l->nstrs/ncol+(l->nstrs%ncol ? 1 : 0);
275 l->nitemcol=nrow;
276 }else{
277 l->nitemcol=l->nstrs;
280 if(l->itemh>0)
281 visrow=h/l->itemh;
283 if(visrow>nrow)
284 visrow=nrow;
286 l->ncol=ncol;
287 l->nrow=nrow;
288 l->visrow=visrow;
289 l->toth=visrow*l->itemh;
291 #if 0
292 l->firstitem=l->nitemcol-1;
293 l->firstoff=ITEMROWS(l, l->nitemcol-1)-1;
294 for(i=1; i<visrow; i++)
295 one_row_up(l, &(l->firstitem), &(l->firstoff));
296 #else
297 l->firstitem=0;
298 l->firstoff=0;
299 #endif
304 void deinit_listing(WListing *l)
306 if(l->strs==NULL)
307 return;
309 while(l->nstrs--){
310 free(l->strs[l->nstrs]);
311 if(l->iteminfos!=NULL)
312 reset_iteminfo(&(l->iteminfos[l->nstrs]));
315 free(l->strs);
316 l->strs=NULL;
318 if(l->iteminfos!=NULL){
319 free(l->iteminfos);
320 l->iteminfos=NULL;
325 void init_listing(WListing *l)
327 l->nstrs=0;
328 l->strs=NULL;
329 l->iteminfos=NULL;
330 l->nstrs=0;
331 l->selected_str=-1;
332 l->onecol=TRUE;
333 l->itemw=0;
334 l->itemh=0;
335 l->ncol=0;
336 l->nrow=0;
337 l->nitemcol=0;
338 l->visrow=0;
339 l->toth=0;
343 static void do_draw_listing(GrBrush *brush, const WRectangle *geom,
344 WListing *l, GrAttr selattr, int mode)
346 int wrapw=grbrush_get_text_width(brush, "\\", 1);
347 int ciw=grbrush_get_text_width(brush, CONT_INDENT, CONT_INDENT_LEN);
348 int r, c, i, x, y;
349 GrFontExtents fnte;
351 if(l->nitemcol==0 || l->visrow==0)
352 return;
354 grbrush_get_font_extents(brush, &fnte);
356 x=0;
357 c=0;
358 while(1){
359 y=geom->y+fnte.baseline;
360 i=l->firstitem+c*l->nitemcol;
361 r=-l->firstoff;
362 y+=r*l->itemh;
363 while(r<l->visrow){
364 if(i>=l->nstrs)
365 return;
367 if(mode>=0 ||
368 l->selected_str==i ||
369 LISTING_DRAW_GET_SELECTED(mode)==i){
371 if(i==l->selected_str)
372 grbrush_set_attr(brush, selattr);
374 draw_multirow(brush, geom->x+x, y, l->itemh, l->strs[i],
375 (l->iteminfos!=NULL ? &(l->iteminfos[i]) : NULL),
376 geom->w-x, ciw, wrapw);
378 if(i==l->selected_str)
379 grbrush_unset_attr(brush, selattr);
382 y+=l->itemh*ITEMROWS(l, i);
383 r+=ITEMROWS(l, i);
384 i++;
386 x+=l->itemw;
387 c++;
392 void draw_listing(GrBrush *brush, const WRectangle *geom,
393 WListing *l, int mode, GrAttr selattr)
395 WRectangle geom2;
396 GrBorderWidths bdw;
398 grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR
399 |GRBRUSH_NEED_CLIP);
401 if(mode==LISTING_DRAW_COMPLETE)
402 grbrush_clear_area(brush, geom);
404 grbrush_draw_border(brush, geom);
406 grbrush_get_border_widths(brush, &bdw);
408 geom2.x=geom->x+bdw.left;
409 geom2.y=geom->y+bdw.top;
410 geom2.w=geom->w-bdw.left-bdw.right;
411 geom2.h=geom->h-bdw.top-bdw.bottom;
413 do_draw_listing(brush, &geom2, l, selattr, mode);
415 grbrush_end(brush);
419 static bool do_scrollup_listing(WListing *l, int n)
421 int i=l->firstitem;
422 int r=l->firstoff;
423 bool ret=FALSE;
425 while(n>0){
426 if(!one_row_up(l, &i, &r))
427 break;
428 ret=TRUE;
429 n--;
432 l->firstitem=i;
433 l->firstoff=r;
435 return ret;
439 static bool do_scrolldown_listing(WListing *l, int n)
441 int i=l->firstitem;
442 int r=l->firstoff;
443 int br=r, bi=i;
444 int bc=l->visrow;
445 bool ret=FALSE;
447 while(--bc>0)
448 one_row_down(l, &bi, &br);
450 while(n>0){
451 if(!one_row_down(l, &bi, &br))
452 break;
453 one_row_down(l, &i, &r);
454 ret=TRUE;
455 n--;
458 l->firstitem=i;
459 l->firstoff=r;
461 return ret;
465 bool scrollup_listing(WListing *l)
467 return do_scrollup_listing(l, l->visrow);
471 bool scrolldown_listing(WListing *l)
473 return do_scrolldown_listing(l, l->visrow);
477 static int listing_first_row_of_item(WListing *l, int i)
479 int fci=i%l->nitemcol, j;
480 int r=0;
482 for(j=0; j<fci; j++)
483 r+=ITEMROWS(l, j);
485 return r;
489 static int listing_first_visible_row(WListing *l)
491 return listing_first_row_of_item(l, l->firstitem)+l->firstoff;
495 int listing_select(WListing *l, int i)
497 int irow, frow, lrow;
498 int redraw;
500 redraw=LISTING_DRAW_SELECTED(l->selected_str);
502 if(i<0){
503 l->selected_str=-1;
504 return redraw;
507 assert(i<l->nstrs);
509 l->selected_str=i;
511 /* Adjust visible area */
513 irow=listing_first_row_of_item(l, i);
514 frow=listing_first_visible_row(l);
516 while(irow<frow){
517 one_row_up(l, &(l->firstitem), &(l->firstoff));
518 frow--;
519 redraw=LISTING_DRAW_COMPLETE;
522 irow+=ITEMROWS(l, i)-1;
523 lrow=frow+l->visrow-1;
525 while(irow>lrow){
526 one_row_down(l, &(l->firstitem), &(l->firstoff));
527 lrow++;
528 redraw=LISTING_DRAW_COMPLETE;
531 return redraw;