Kludge to better position xfce4 notification popups (if you want to run those at...
[notion/jeffpc.git] / mod_query / listing.c
blobf4b31f61ff8797be124260b6c2f45d44aae2ea97
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 l, 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, l, &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);
127 int i, s;
129 iinf->n_parts=0;
130 iinf->len=strlen(str);
132 if(maxw<=0)
133 reset_iteminfo(iinf);
134 else
135 string_do_calc_parts(brush, maxw, str, iinf->len, iinf, wrapw, ciw);
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)
143 int i, l;
145 if(iinf==NULL){
146 grbrush_draw_string(brush, x, y, str, strlen(str), TRUE);
147 return;
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];
158 grbrush_draw_string(brush, x, y, str, l, TRUE);
160 for(i=1; i<iinf->n_parts; i++){
161 grbrush_draw_string(brush, x+maxw-wrapw, y, "\\", 1, TRUE);
163 y+=h;
164 str+=l;
165 if(i==1){
166 x+=ciw;
167 maxw-=ciw;
169 l=iinf->part_lens[i];
171 grbrush_draw_string(brush, x, y, str, l, TRUE);
176 static int col_fit(int w, int itemw, int spacing)
178 int ncol=1;
179 int tmp=w-itemw;
180 itemw+=spacing;
182 if(tmp>0)
183 ncol+=tmp/itemw;
185 return ncol;
188 static bool one_row_up(WListing *l, int *ip, int *rp)
190 int i=*ip, r=*rp;
191 int ir=ITEMROWS(l, i);
193 if(r>0){
194 (*rp)--;
195 return TRUE;
198 if(i==0)
199 return FALSE;
201 (*ip)--;
202 *rp=ITEMROWS(l, i-1)-1;
203 return TRUE;
207 static bool one_row_down(WListing *l, int *ip, int *rp)
209 int i=*ip, r=*rp;
210 int ir=ITEMROWS(l, i);
212 if(r<ir-1){
213 (*rp)++;
214 return TRUE;
217 if(i==l->nitemcol-1)
218 return FALSE;
220 (*ip)++;
221 *rp=0;
222 return TRUE;
226 void setup_listing(WListing *l, char **strs, int nstrs, bool onecol)
228 if(l->strs!=NULL)
229 deinit_listing(l);
231 l->iteminfos=ALLOC_N(WListingItemInfo, nstrs);
232 l->strs=strs;
233 l->nstrs=nstrs;
234 l->onecol=onecol;
235 l->selected_str=-1;
239 void fit_listing(GrBrush *brush, const WRectangle *geom, WListing *l)
241 int ncol, nrow=0, visrow=INT_MAX;
242 int i, maxw, w, h;
243 GrFontExtents fnte;
244 GrBorderWidths bdw;
246 grbrush_get_font_extents(brush, &fnte);
247 grbrush_get_border_widths(brush, &bdw);
249 w=geom->w-bdw.left-bdw.right;
250 h=geom->h-bdw.top-bdw.bottom;
252 maxw=strings_maxw(brush, l->strs, l->nstrs);
253 l->itemw=maxw+COL_SPACING;
254 l->itemh=fnte.max_height;
256 if(l->onecol)
257 ncol=1;
258 else
259 ncol=col_fit(w, l->itemw-COL_SPACING, COL_SPACING);
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]));
269 nrow+=l->iteminfos[i].n_parts;
271 }else{
272 nrow=l->nstrs;
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;
282 if(l->itemh>0)
283 visrow=h/l->itemh;
285 if(visrow>nrow)
286 visrow=nrow;
288 l->ncol=ncol;
289 l->nrow=nrow;
290 l->visrow=visrow;
291 l->toth=visrow*l->itemh;
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
306 void deinit_listing(WListing *l)
308 int i;
310 if(l->strs==NULL)
311 return;
313 while(l->nstrs--){
314 free(l->strs[l->nstrs]);
315 if(l->iteminfos!=NULL)
316 reset_iteminfo(&(l->iteminfos[l->nstrs]));
319 free(l->strs);
320 l->strs=NULL;
322 if(l->iteminfos!=NULL){
323 free(l->iteminfos);
324 l->iteminfos=NULL;
329 void init_listing(WListing *l)
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;
347 static void do_draw_listing(GrBrush *brush, const WRectangle *geom,
348 WListing *l, GrAttr selattr, int mode)
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;
355 if(l->nitemcol==0 || l->visrow==0)
356 return;
358 grbrush_get_font_extents(brush, &fnte);
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;
371 if(mode>=0 ||
372 l->selected_str==i ||
373 LISTING_DRAW_GET_SELECTED(mode)==i){
375 if(i==l->selected_str)
376 grbrush_set_attr(brush, selattr);
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);
382 if(i==l->selected_str)
383 grbrush_unset_attr(brush, selattr);
386 y+=l->itemh*ITEMROWS(l, i);
387 r+=ITEMROWS(l, i);
388 i++;
390 x+=l->itemw;
391 c++;
396 static int prevsel=-1;
398 static bool filteridx_sel(WListing *l, int i)
400 return (i==prevsel || i==l->selected_str);
404 void draw_listing(GrBrush *brush, const WRectangle *geom,
405 WListing *l, int mode, GrAttr selattr)
407 WRectangle geom2;
408 GrBorderWidths bdw;
410 grbrush_begin(brush, geom, GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR
411 |GRBRUSH_NEED_CLIP);
413 if(mode==LISTING_DRAW_COMPLETE)
414 grbrush_clear_area(brush, geom);
416 grbrush_draw_border(brush, geom);
418 grbrush_get_border_widths(brush, &bdw);
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;
425 do_draw_listing(brush, &geom2, l, selattr, mode);
427 grbrush_end(brush);
431 static bool do_scrollup_listing(WListing *l, int n)
433 int i=l->firstitem;
434 int r=l->firstoff;
435 bool ret=FALSE;
437 while(n>0){
438 if(!one_row_up(l, &i, &r))
439 break;
440 ret=TRUE;
441 n--;
444 l->firstitem=i;
445 l->firstoff=r;
447 return ret;
451 static bool do_scrolldown_listing(WListing *l, int n)
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;
459 while(--bc>0)
460 one_row_down(l, &bi, &br);
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--;
470 l->firstitem=i;
471 l->firstoff=r;
473 return ret;
477 bool scrollup_listing(WListing *l)
479 return do_scrollup_listing(l, l->visrow);
483 bool scrolldown_listing(WListing *l)
485 return do_scrolldown_listing(l, l->visrow);
489 static int listing_first_row_of_item(WListing *l, int i)
491 int fci=i%l->nitemcol, j;
492 int r=0;
494 for(j=0; j<fci; j++)
495 r+=ITEMROWS(l, j);
497 return r;
501 static int listing_first_visible_row(WListing *l)
503 return listing_first_row_of_item(l, l->firstitem)+l->firstoff;
507 int listing_select(WListing *l, int i)
509 int irow, frow, lrow;
510 int redraw;
512 redraw=LISTING_DRAW_SELECTED(l->selected_str);
514 if(i<0){
515 l->selected_str=-1;
516 return redraw;
519 assert(i<l->nstrs);
521 l->selected_str=i;
523 /* Adjust visible area */
525 irow=listing_first_row_of_item(l, i);
526 frow=listing_first_visible_row(l);
528 while(irow<frow){
529 one_row_up(l, &(l->firstitem), &(l->firstoff));
530 frow--;
531 redraw=LISTING_DRAW_COMPLETE;
534 irow+=ITEMROWS(l, i)-1;
535 lrow=frow+l->visrow-1;
537 while(irow>lrow){
538 one_row_down(l, &(l->firstitem), &(l->firstoff));
539 lrow++;
540 redraw=LISTING_DRAW_COMPLETE;
543 return redraw;