Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 : #include "gdraw.h"
28 : #include "gkeysym.h"
29 : #include "ggadgetP.h"
30 : #include "gwidget.h"
31 : #include "ustring.h"
32 : #include "gwidget.h"
33 :
34 : static int GListTypeTime = 500; /* half a second between keystrokes */
35 : static int GListScrollTime = 500; /* half a second between scrolls when mouse out of listbox */
36 :
37 0 : static void GListSelected(GList *l,int frommouse,int index) {
38 : GEvent e;
39 :
40 0 : e.type = et_controlevent;
41 0 : e.w = l->g.base;
42 0 : e.u.control.subtype = et_listselected;
43 0 : e.u.control.g = &l->g;
44 0 : e.u.control.u.list.from_mouse = frommouse;
45 0 : e.u.control.u.list.changed_index = index;
46 0 : if ( l->g.handle_controlevent != NULL )
47 0 : (l->g.handle_controlevent)(&l->g,&e);
48 : else
49 0 : GDrawPostEvent(&e);
50 0 : }
51 :
52 0 : static void GListDoubleClick(GList *l,int frommouse,int index) {
53 : GEvent e;
54 :
55 0 : e.type = et_controlevent;
56 0 : e.w = l->g.base;
57 0 : e.u.control.subtype = et_listdoubleclick;
58 0 : e.u.control.g = &l->g;
59 0 : e.u.control.u.list.from_mouse = frommouse;
60 0 : e.u.control.u.list.changed_index = index;
61 0 : if ( l->g.handle_controlevent != NULL )
62 0 : (l->g.handle_controlevent)(&l->g,&e);
63 : else
64 0 : GDrawPostEvent(&e);
65 0 : }
66 :
67 0 : static void GListClose(GList *l) {
68 : GEvent e;
69 :
70 0 : e.type = et_close;
71 0 : e.w = l->g.base;
72 0 : if ( l->g.handle_controlevent != NULL )
73 0 : (l->g.handle_controlevent)(&l->g,&e);
74 : else
75 0 : GDrawPostEvent(&e);
76 0 : }
77 :
78 0 : static int GListTopInWindow(GList *gl,int last) {
79 : /* If we want to display last at the bottom of our list, then what line */
80 : /* do we display at the top? */
81 0 : int32 height = gl->g.inner.height, temp;
82 : int l;
83 :
84 0 : for ( l=last; l>=0; --l ) {
85 0 : temp = GTextInfoGetHeight(gl->g.base,gl->ti[l],gl->font);
86 0 : if ( height<temp )
87 0 : return( l==last?last:l+1 ); /* if we can't even fit one line on, pretend it fits */
88 0 : height -= temp;
89 : }
90 0 : return( 0 );
91 : }
92 :
93 0 : static int GListLinesInWindow(GList *gl,int first) {
94 : /* Ok, the first line displayed is "first", how many others do we have */
95 : /* room for? */
96 0 : int32 height = gl->g.inner.height, temp;
97 0 : int l, lcnt=0;
98 :
99 0 : for ( l=first; l<gl->ltot; ++l ) {
100 0 : temp = GTextInfoGetHeight(gl->g.base,gl->ti[l],gl->font);
101 0 : if ( height<temp )
102 0 : return( l==first?1:lcnt ); /* if we can't even fit one line on, pretend it fits */
103 0 : height -= temp;
104 0 : ++lcnt;
105 : }
106 0 : if ( height>0 ) {
107 0 : if ( gl->fh==0 ) {
108 : int as, ds, ld;
109 0 : GDrawWindowFontMetrics(gl->g.base,gl->font,&as, &ds, &ld);
110 0 : gl->fh = as+ds;
111 0 : gl->as = as;
112 : }
113 0 : lcnt += height/gl->fh;
114 : }
115 0 : if ( lcnt==0 )
116 0 : lcnt=1;
117 0 : return( lcnt );
118 : }
119 :
120 0 : static int GListAlphaCompare(const void *v1, const void *v2) {
121 0 : GTextInfo * const *pt1 = v1, * const *pt2 = v2;
122 0 : return( GTextInfoCompare(*pt1,*pt2));
123 : }
124 :
125 0 : static void GListOrderIt(GList *gl) {
126 0 : qsort(gl->ti,gl->ltot,sizeof(GTextInfo *),gl->orderer);
127 0 : if ( gl->backwards ) {
128 : int i;
129 : GTextInfo *ti;
130 0 : for ( i=0; i<gl->ltot/2; ++i ) {
131 0 : ti = gl->ti[i];
132 0 : gl->ti[i] = gl->ti[gl->ltot-1-i];
133 0 : gl->ti[gl->ltot-1-i] = ti;
134 : }
135 : }
136 0 : }
137 :
138 0 : static void GListClearSel(GList *gl) {
139 : int i;
140 :
141 0 : for ( i=0; (i<gl->ltot && gl->ti[i]!=NULL); ++i )
142 0 : gl->ti[i]->selected = false;
143 0 : }
144 :
145 0 : static int GListAnyOtherSels(GList *gl, int pos) {
146 : int i;
147 :
148 0 : for ( i=0; i<gl->ltot; ++i )
149 0 : if ( gl->ti[i]->selected && i!=pos )
150 0 : return( true );
151 :
152 0 : return( false );
153 : }
154 :
155 0 : static int32 GListGetFirstSelPos(GGadget *g) {
156 : int i;
157 0 : GList *gl = (GList *) g;
158 :
159 0 : for ( i=0; i<gl->ltot; ++i )
160 0 : if ( gl->ti[i]->selected )
161 0 : return( i );
162 :
163 0 : return( -1 );
164 : }
165 :
166 0 : static void GListSelect(GGadget *g, int32 pos, int32 sel) {
167 0 : GList *gl = (GList *) g;
168 : int i;
169 :
170 0 : if ( pos==-1 && (gl->multiple_sel || (!sel && !gl->exactly_one)) ) {
171 : /* Select/deselect all */
172 0 : for ( i=0; i<gl->ltot; ++i )
173 0 : gl->ti[i]->selected = sel;
174 0 : _ggadget_redraw(g);
175 0 : return;
176 : }
177 :
178 0 : if ( pos>=gl->ltot || pos<0 )
179 0 : return;
180 0 : if ( gl->exactly_one && !sel )
181 0 : return;
182 0 : if ( !gl->multiple_sel && sel )
183 0 : GListClearSel(gl);
184 0 : if ( gl->ltot>0 ) {
185 0 : gl->ti[pos]->selected = sel;
186 0 : _ggadget_redraw(g);
187 : }
188 : }
189 :
190 0 : static void GListSelectOne(GGadget *g, int32 pos) {
191 0 : GList *gl = (GList *) g;
192 :
193 0 : GListClearSel(gl);
194 0 : if ( pos>=gl->ltot ) pos = gl->ltot-1;
195 0 : if ( pos<0 ) pos = 0;
196 0 : if ( gl->ltot>0 ) {
197 0 : gl->ti[pos]->selected = true;
198 0 : _ggadget_redraw(g);
199 : }
200 0 : }
201 :
202 0 : static int32 GListIsItemSelected(GGadget *g, int32 pos) {
203 0 : GList *gl = (GList *) g;
204 :
205 0 : if ( pos>=gl->ltot )
206 0 : return( false );
207 0 : if ( pos<0 )
208 0 : return( false );
209 0 : if ( gl->ltot>0 )
210 0 : return( gl->ti[pos]->selected );
211 :
212 0 : return( false );
213 : }
214 :
215 0 : static void GListExpandSelection(GList *gl,int pos) {
216 : int i;
217 :
218 0 : if ( gl->start!=65535 ) {
219 0 : if ( gl->start<gl->end )
220 0 : for ( i=gl->start; i<=gl->end; ++i )
221 0 : gl->ti[i]->selected = false;
222 : else
223 0 : for ( i=gl->start; i>=gl->end; --i )
224 0 : gl->ti[i]->selected = false;
225 : } else
226 0 : gl->start = pos;
227 0 : gl->end = pos;
228 0 : if ( gl->start<gl->end )
229 0 : for ( i=gl->start; i<=gl->end; ++i )
230 0 : gl->ti[i]->selected = true;
231 : else
232 0 : for ( i=gl->start; i>=gl->end; --i )
233 0 : gl->ti[i]->selected = true;
234 0 : }
235 :
236 0 : static int GListIndexFromPos(GList *gl,int y) {
237 : int i, height;
238 :
239 0 : y -= gl->g.inner.y;
240 0 : if ( y<0 ) y=0;
241 0 : if ( y>=gl->g.inner.height ) y = gl->g.inner.height-1;
242 0 : for ( i=gl->loff, height=0; i<gl->ltot; ++i ) {
243 0 : int temp = GTextInfoGetHeight(gl->g.base,gl->ti[i],gl->font);
244 0 : if ( height+temp>y )
245 0 : break;
246 0 : height += temp;
247 : }
248 0 : if ( i==gl->ltot )
249 0 : return( -1 );
250 0 : if ( gl->ti[i]->disabled )
251 0 : return( -1 );
252 0 : return( i );
253 : }
254 :
255 0 : static void GListScrollBy(GList *gl,int loff,int xoff) {
256 0 : int top = GListTopInWindow(gl,gl->ltot-1);
257 : int ydiff, i;
258 :
259 0 : if ( gl->loff + loff < 0 )
260 0 : loff = -gl->loff;
261 0 : else if ( gl->loff + loff > top )
262 0 : loff = top-gl->loff;
263 0 : if ( xoff+gl->xoff<0 )
264 0 : xoff = -gl->xoff;
265 0 : else if ( xoff+gl->xoff+gl->g.inner.width > gl->xmax ) {
266 0 : xoff = gl->xmax-gl->g.inner.width-gl->xoff;
267 0 : if ( xoff<0 ) xoff = 0;
268 : }
269 0 : if ( loff == 0 && xoff==0 )
270 0 : return;
271 :
272 0 : ydiff = 0;
273 0 : if ( loff>0 ) {
274 0 : for ( i=0; i<loff && ydiff<gl->g.inner.height; ++i )
275 0 : ydiff += GTextInfoGetHeight(gl->g.base,gl->ti[i+gl->loff],gl->font);
276 0 : } else if ( loff<0 ) {
277 0 : for ( i=loff; i<0 && -ydiff<gl->g.inner.height; ++i )
278 0 : ydiff -= GTextInfoGetHeight(gl->g.base,gl->ti[i+gl->loff],gl->font);
279 : }
280 0 : if ( !GDrawIsVisible(gl->g.base))
281 0 : return;
282 0 : GDrawForceUpdate(gl->g.base);
283 0 : gl->loff += loff; gl->xoff += xoff;
284 0 : if ( ydiff>=gl->g.inner.height || -ydiff >= gl->g.inner.height )
285 0 : _ggadget_redraw(&gl->g);
286 0 : else if ( ydiff!=0 || xoff!=0 )
287 0 : GDrawScroll(gl->g.base,&gl->g.inner,xoff,ydiff);
288 0 : if ( loff!=0 && gl->vsb!=NULL )
289 0 : GScrollBarSetPos(&gl->vsb->g,gl->loff);
290 : }
291 :
292 0 : static int GListFindPosition(GList *gl,unichar_t *text) {
293 0 : GTextInfo temp, *ptemp=&temp;
294 : int i, order;
295 :
296 0 : if ( gl->orderer!=NULL ) {
297 0 : memset(&temp,'\0',sizeof(temp));
298 0 : temp.text = text;
299 :
300 : /* I don't think I need to write a binary search here... */
301 0 : for ( i=0; i<gl->ltot; ++i ) {
302 0 : order = (gl->orderer)(&ptemp,&gl->ti[i]);
303 0 : if (( order<= 0 && !gl->backwards ) || ( order>=0 && gl->backwards ))
304 0 : return( i );
305 : }
306 0 : return( 0 );
307 : } else {
308 0 : for ( i=0; i<gl->ltot; ++i ) {
309 0 : if (u_strmatch(text,gl->ti[i]->text)==0 )
310 0 : return( i );
311 : }
312 : }
313 0 : return( 0 );
314 : }
315 :
316 0 : static int GListAdjustPos(GGadget *g,int pos) {
317 0 : GList *gl = (GList *) g;
318 0 : int newoff = gl->loff;
319 :
320 0 : if ( pos<gl->loff ) {
321 0 : if (( newoff = pos-1)<0 ) newoff = 0;
322 0 : if ( GListLinesInWindow(gl,newoff)<2 )
323 0 : newoff = pos;
324 0 : } else if ( pos >= gl->loff + GListLinesInWindow(gl,gl->loff) ) {
325 0 : newoff = GListTopInWindow(gl,pos);
326 0 : if ( pos!=gl->ltot-1 && GListLinesInWindow(gl,newoff+1)>=2 )
327 0 : ++newoff;
328 : }
329 0 : return( newoff );
330 : }
331 :
332 0 : static void GListShowPos(GGadget *g,int32 pos) {
333 0 : GList *gl = (GList *) g;
334 0 : int newoff = GListAdjustPos(g,pos);
335 0 : if ( newoff!=gl->loff )
336 0 : GListScrollBy(gl,newoff-gl->loff,0);
337 0 : }
338 :
339 0 : static void GListScrollToText(GGadget *g,const unichar_t *text,int32 sel) {
340 0 : GList *gl = (GList *) g;
341 : int pos;
342 :
343 0 : pos = GListFindPosition(gl,(unichar_t *) text);
344 0 : if ( sel && pos<gl->ltot ) {
345 0 : GListClearSel(gl);
346 0 : if ( gl->exactly_one || u_strmatch(text,gl->ti[pos]->text)==0 )
347 0 : gl->ti[pos]->selected = true;
348 : }
349 0 : gl->loff = GListAdjustPos(g,pos);
350 0 : if ( gl->vsb!=NULL )
351 0 : GScrollBarSetPos(&gl->vsb->g,gl->loff);
352 0 : _ggadget_redraw(g);
353 0 : }
354 :
355 0 : static void GListSetOrderer(GGadget *g,int (*orderer)(const void *, const void *)) {
356 0 : GList *gl = (GList *) g;
357 :
358 0 : gl->orderer = orderer;
359 0 : if ( orderer!=NULL ) {
360 0 : GListOrderIt(gl);
361 0 : GListScrollBy(gl,-gl->loff,-gl->xoff);
362 0 : _ggadget_redraw(&gl->g);
363 : }
364 0 : }
365 :
366 : static int glist_scroll(GGadget *g, GEvent *event);
367 0 : static void GListCheckSB(GList *gl) {
368 0 : if ( gl->vsb==NULL ) {
369 : GGadgetData gd;
370 0 : memset(&gd,'\0',sizeof(gd));
371 0 : gd.pos.y = gl->g.r.y; gd.pos.height = gl->g.r.height;
372 0 : gd.pos.width = GDrawPointsToPixels(gl->g.base,_GScrollBar_Width);
373 0 : gd.pos.x = gl->g.r.x+gl->g.r.width - gd.pos.width;
374 0 : gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert|gg_pos_use0;
375 0 : gd.handle_controlevent = glist_scroll;
376 0 : gl->vsb = (GScrollBar *) GScrollBarCreate(gl->g.base,&gd,gl);
377 0 : gl->vsb->g.contained = true;
378 :
379 0 : gd.pos.width += GDrawPointsToPixels(gl->g.base,1);
380 0 : gl->g.r.width -= gd.pos.width;
381 0 : gl->g.inner.width -= gd.pos.width;
382 : }
383 0 : if ( gl->always_show_sb || GListLinesInWindow(gl,0)<gl->ltot ) {
384 0 : if ( gl->vsb->g.state == gs_invisible ) {
385 0 : int wid = gl->vsb->g.r.width + GDrawPointsToPixels(gl->g.base,1);
386 0 : gl->vsb->g.state = gs_enabled;
387 0 : gl->g.r.width -= wid;
388 0 : gl->g.inner.width -= wid;
389 : }
390 0 : GScrollBarSetBounds(&gl->vsb->g,0,gl->ltot,GListLinesInWindow(gl,0));
391 0 : GScrollBarSetPos(&gl->vsb->g,gl->loff);
392 : } else {
393 0 : if ( gl->vsb->g.state != gs_invisible ) {
394 0 : int wid = gl->vsb->g.r.width + GDrawPointsToPixels(gl->g.base,1);
395 0 : gl->vsb->g.state = gs_invisible;
396 0 : gl->g.r.width += wid;
397 0 : gl->g.inner.width += wid;
398 : }
399 : }
400 0 : }
401 :
402 0 : static int GListFindXMax(GList *gl) {
403 0 : int i, width=0, temp;
404 :
405 0 : for ( i=0; i<gl->ltot; ++i ) {
406 0 : temp = GTextInfoGetWidth(gl->g.base,gl->ti[i],gl->font);
407 0 : if ( temp>width ) width=temp;
408 : }
409 0 : gl->xmax = width;
410 0 : return( width );
411 : }
412 :
413 0 : static void GListSetList(GGadget *g,GTextInfo **ti,int32 docopy) {
414 0 : GList *gl = (GList *) g;
415 : int same;
416 :
417 0 : GTextInfoArrayFree(gl->ti);
418 0 : if ( docopy || ti==NULL )
419 0 : ti = GTextInfoArrayCopy(ti);
420 0 : gl->ti = ti;
421 0 : gl->ltot = GTextInfoArrayCount(ti);
422 0 : if ( gl->orderer!=NULL )
423 0 : GListOrderIt(gl);
424 0 : gl->loff = gl->xoff = 0;
425 0 : gl->hmax = GTextInfoGetMaxHeight(g->base,ti,gl->font,&same);
426 0 : gl->sameheight = same;
427 0 : GListCheckSB(gl);
428 0 : _ggadget_redraw(&gl->g);
429 0 : }
430 :
431 0 : static void GListClear(GGadget *g) {
432 0 : GListSetList(g,NULL,true);
433 0 : }
434 :
435 0 : static GTextInfo **GListGetList(GGadget *g,int32 *len) {
436 0 : GList *gl = (GList *) g;
437 0 : if ( len!=NULL ) *len = gl->ltot;
438 0 : return( gl->ti );
439 : }
440 :
441 0 : static GTextInfo *GListGetListItem(GGadget *g,int32 pos) {
442 0 : GList *gl = (GList *) g;
443 0 : if ( pos<0 || pos>=gl->ltot )
444 0 : return( NULL );
445 :
446 0 : return(gl->ti[pos]);
447 : }
448 :
449 0 : static int glist_expose(GWindow pixmap, GGadget *g, GEvent *event) {
450 0 : GList *gl = (GList *) g;
451 : GRect old0, old1, old2;
452 : Color fg, dfg;
453 : int y, l, ymax;
454 :
455 0 : if ( g->state == gs_invisible )
456 0 : return( false );
457 :
458 0 : GDrawPushClip(pixmap,&event->u.expose.rect,&old0);
459 0 : GDrawPushClip(pixmap,&g->r,&old1);
460 :
461 0 : GBoxDrawBackground(pixmap,&g->r,g->box, g->state,false);
462 0 : if ( g->box->border_type!=bt_none ||
463 0 : (g->box->flags&(box_foreground_border_inner|box_foreground_border_outer|box_active_border_inner))!=0 ) {
464 0 : GBoxDrawBorder(pixmap,&g->r,g->box,g->state,false);
465 :
466 0 : GDrawPushClip(pixmap,&g->inner,&old2);
467 : }
468 :
469 0 : fg = g->state==gs_disabled?g->box->disabled_foreground:g->box->main_foreground;
470 0 : dfg = g->box->disabled_foreground;
471 0 : y = g->inner.y;
472 0 : ymax = g->inner.y+g->inner.height;
473 0 : if ( ymax>event->u.expose.rect.y+event->u.expose.rect.height )
474 0 : ymax = event->u.expose.rect.y+event->u.expose.rect.height;
475 0 : for ( l = gl->loff; y<ymax && l<gl->ltot; ++l ) {
476 0 : if ( y+gl->hmax > event->u.expose.rect.y )
477 0 : y += GTextInfoDraw(pixmap,g->inner.x-gl->xoff,y,gl->ti[l],
478 0 : gl->font,gl->ti[l]->disabled?dfg:fg,g->box->active_border,
479 : ymax);
480 0 : else if ( gl->sameheight )
481 0 : y += gl->hmax;
482 : else
483 0 : y += GTextInfoGetHeight(pixmap,gl->ti[l],gl->font);
484 : }
485 0 : if ( g->box->border_type!=bt_none ||
486 0 : (g->box->flags&(box_foreground_border_inner|box_foreground_border_outer|box_active_border_inner))!=0 )
487 0 : GDrawPopClip(pixmap,&old2);
488 0 : GDrawPopClip(pixmap,&old1);
489 0 : GDrawPopClip(pixmap,&old0);
490 0 : return( true );
491 : }
492 :
493 0 : static void glist_scroll_selbymouse(GList *gl, GEvent *event) {
494 0 : int loff=0, xoff=0, pos;
495 :
496 0 : if ( event->u.mouse.y<gl->g.inner.y ) {
497 0 : if ( gl->loff>0 ) loff = -1;
498 0 : } else if ( event->u.mouse.y >= gl->g.inner.y+gl->g.inner.height ) {
499 0 : int top = GListTopInWindow(gl,gl->ltot-1);
500 0 : if ( gl->loff<top ) loff = 1;
501 : }
502 0 : if ( event->u.mouse.x<gl->g.inner.x ) {
503 0 : xoff = -GDrawPointsToPixels(gl->g.base,6);
504 0 : } else if ( event->u.mouse.x >= gl->g.inner.x+gl->g.inner.width ) {
505 0 : xoff = GDrawPointsToPixels(gl->g.base,6);
506 : }
507 0 : GListScrollBy(gl,loff,xoff);
508 0 : pos = GListIndexFromPos(gl,event->u.mouse.y);
509 0 : if ( pos==-1 || pos == gl->end )
510 : /* Do Nothing, nothing selectable */;
511 0 : else if ( !gl->multiple_sel ) {
512 0 : GListClearSel(gl);
513 0 : gl->ti[pos]->selected = true;
514 0 : gl->start = gl->end = pos;
515 0 : _ggadget_redraw(&gl->g);
516 : } else {
517 0 : GListExpandSelection(gl,pos);
518 0 : gl->end = pos;
519 0 : _ggadget_redraw(&gl->g);
520 : }
521 0 : }
522 :
523 0 : static int glist_mouse(GGadget *g, GEvent *event) {
524 0 : GList *gl = (GList *) g;
525 : int pos;
526 :
527 0 : if ( !g->takes_input || (g->state!=gs_active && g->state!=gs_enabled && g->state!=gs_focused))
528 0 : return( false );
529 :
530 0 : if ( event->type == et_crossing )
531 0 : return( false );
532 0 : if (( event->type==et_mouseup || event->type==et_mousedown ) &&
533 0 : (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
534 0 : if ( gl->vsb!=NULL )
535 0 : return( GGadgetDispatchEvent(&gl->vsb->g,event));
536 : else
537 0 : return( true );
538 : }
539 0 : if ( event->type==et_mousemove && !gl->pressed && !gl->parentpressed ) {
540 0 : if ( GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y) ) {
541 0 : if ( gl->popup_callback!=NULL )
542 0 : (gl->popup_callback)(g,GListIndexFromPos(gl,event->u.mouse.y));
543 0 : else if ( g->popup_msg )
544 0 : GGadgetPreparePopup(g->base,g->popup_msg);
545 : }
546 0 : return( true );
547 0 : } else if ( event->type==et_mouseup && gl->parentpressed /* &&
548 : !GGadgetInnerWithin(&gl->g,event->u.mouse.x,event->u.mouse.y)*/ ) {
549 0 : gl->parentpressed = false;
550 0 : GDrawPointerUngrab(GDrawGetDisplayOfWindow(gl->g.base));
551 0 : } else if ( event->type==et_mousemove && gl->parentpressed &&
552 0 : GGadgetInnerWithin(&gl->g,event->u.mouse.x,event->u.mouse.y)) {
553 0 : if ( gl->pressed == NULL )
554 0 : gl->pressed = GDrawRequestTimer(g->base,GListScrollTime,GListScrollTime,NULL);
555 0 : GDrawPointerUngrab(GDrawGetDisplayOfWindow(gl->g.base));
556 0 : gl->parentpressed = false;
557 0 : glist_scroll_selbymouse(gl,event);
558 0 : return( true );
559 0 : } else if ( event->type==et_mousemove && gl->pressed ) {
560 0 : glist_scroll_selbymouse(gl,event);
561 0 : return( true );
562 0 : } else if ( event->type==et_mousedown ) {
563 0 : if ( gl->pressed == NULL )
564 0 : gl->pressed = GDrawRequestTimer(g->base,GListScrollTime,GListScrollTime,NULL);
565 0 : pos = GListIndexFromPos(gl,event->u.mouse.y);
566 0 : if ( pos==-1 )
567 0 : return( true ); /* Do Nothing, nothing selectable */
568 0 : else if ( !gl->exactly_one && gl->ti[pos]->selected &&
569 0 : (event->u.mouse.state&(ksm_control|ksm_shift))) {
570 0 : gl->ti[pos]->selected = false;
571 0 : gl->start = gl->end = 0xffff;
572 0 : } else if ( !gl->multiple_sel ||
573 0 : (!gl->ti[pos]->selected && !(event->u.mouse.state&(ksm_control|ksm_shift)))) {
574 0 : GListClearSel(gl);
575 0 : gl->ti[pos]->selected = true;
576 0 : gl->start = gl->end = pos;
577 0 : } else if ( event->u.mouse.state&ksm_control ||
578 0 : ((event->u.mouse.state&ksm_shift) && gl->ti[pos]->selected)) {
579 0 : gl->ti[pos]->selected = !gl->ti[pos]->selected;
580 0 : gl->start = gl->end = pos;
581 0 : } else if ( event->u.mouse.state&ksm_shift ) {
582 0 : GListExpandSelection(gl,pos);
583 : } else {
584 0 : gl->ti[pos]->selected = true;
585 0 : gl->start = gl->end = pos;
586 : }
587 0 : _ggadget_redraw(&gl->g);
588 0 : } else if ( event->type==et_mouseup && gl->pressed ) {
589 0 : GDrawCancelTimer(gl->pressed); gl->pressed = NULL;
590 0 : if ( GGadgetInnerWithin(&gl->g,event->u.mouse.x,event->u.mouse.y) ) {
591 0 : pos = GListIndexFromPos(gl,event->u.mouse.y);
592 0 : if ( !(event->u.mouse.state&(ksm_control|ksm_shift)) || gl->start!=0xffff )
593 0 : glist_scroll_selbymouse(gl,event);
594 0 : if ( event->u.mouse.clicks==2 )
595 0 : GListDoubleClick(gl,true,pos);
596 : else
597 0 : GListSelected(gl,true,pos);
598 : }
599 : } else
600 0 : return( false );
601 :
602 0 : return( true );
603 : }
604 :
605 0 : static int glist_key(GGadget *g, GEvent *event) {
606 0 : GList *gl = (GList *) g;
607 0 : uint16 keysym = event->u.chr.keysym;
608 0 : int sofar_pos = gl->sofar_pos;
609 0 : int loff, xoff, sel=-1;
610 0 : int refresh = false;
611 :
612 0 : if ( event->type == et_charup )
613 0 : return( false );
614 0 : if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
615 0 : return(false );
616 :
617 0 : if ( gl->ispopup && event->u.chr.keysym == GK_Return ) {
618 0 : GListDoubleClick(gl,false,-1);
619 0 : return( true );
620 0 : } else if ( gl->ispopup && event->u.chr.keysym == GK_Escape ) {
621 0 : GListClose(gl);
622 0 : return( true );
623 : }
624 :
625 0 : if ( event->u.chr.keysym == GK_Return || event->u.chr.keysym == GK_Tab ||
626 0 : event->u.chr.keysym == GK_BackTab || event->u.chr.keysym == GK_Escape )
627 0 : return( false );
628 :
629 0 : GDrawCancelTimer(gl->enduser); gl->enduser = NULL; gl->sofar_pos = 0;
630 :
631 0 : loff = 0x80000000; xoff = 0x80000000; sel = -1;
632 0 : if ( keysym == GK_Home || keysym == GK_KP_Home || keysym == GK_Begin || keysym == GK_KP_Begin ) {
633 0 : loff = -gl->loff;
634 0 : xoff = -gl->xoff;
635 0 : sel = 0;
636 0 : } else if ( keysym == GK_End || keysym == GK_KP_End ) {
637 0 : loff = GListTopInWindow(gl,gl->ltot-1)-gl->loff;
638 0 : xoff = -gl->xoff;
639 0 : sel = gl->ltot-1;
640 0 : } else if ( keysym == GK_Up || keysym == GK_KP_Up ) {
641 0 : if (( sel = GListGetFirstSelPos(&gl->g)-1 )<0 ) {
642 : /*if ( gl->loff!=0 ) loff = -1; else loff = 0;*/
643 0 : sel = 0;
644 : }
645 0 : } else if ( keysym == GK_Down || keysym == GK_KP_Down ) {
646 0 : if (( sel = GListGetFirstSelPos(&gl->g))!= -1 )
647 0 : ++sel;
648 : else
649 : /*if ( gl->loff + GListLinesInWindow(gl,gl->loff)<gl->ltot ) loff = 1; else loff = 0;*/
650 0 : sel = 0;
651 0 : } else if ( keysym == GK_Left || keysym == GK_KP_Left ) {
652 0 : xoff = -GDrawPointsToPixels(gl->g.base,6);
653 0 : } else if ( keysym == GK_Right || keysym == GK_KP_Right ) {
654 0 : xoff = GDrawPointsToPixels(gl->g.base,6);
655 0 : } else if ( keysym == GK_Page_Up || keysym == GK_KP_Page_Up ) {
656 0 : loff = GListTopInWindow(gl,gl->loff);
657 0 : if ( loff == gl->loff ) /* Normally we leave one line in window from before, except if only one line fits */
658 0 : loff = GListTopInWindow(gl,gl->loff-1);
659 0 : loff -= gl->loff;
660 0 : if (( sel = GListGetFirstSelPos(&gl->g))!= -1 ) {
661 0 : if (( sel += loff )<0 ) sel = 0;
662 : }
663 0 : } else if ( keysym == GK_Page_Down || keysym == GK_KP_Page_Down ) {
664 0 : loff = GListLinesInWindow(gl,gl->loff)-1;
665 0 : if ( loff<=0 ) loff = 1;
666 0 : if ( loff + gl->loff >= gl->ltot )
667 0 : loff = GListTopInWindow(gl,gl->ltot-1)-gl->loff;
668 0 : if (( sel = GListGetFirstSelPos(&gl->g))!= -1 ) {
669 0 : if (( sel += loff )>=gl->ltot ) sel = gl->ltot-1;
670 : }
671 0 : } else if ( keysym == GK_BackSpace && gl->orderer ) {
672 : /* ordered lists may be reversed by typing backspace */
673 0 : gl->backwards = !gl->backwards;
674 0 : GListOrderIt(gl);
675 0 : sel = GListGetFirstSelPos(&gl->g);
676 0 : if ( sel!=-1 ) {
677 0 : int top = GListTopInWindow(gl,gl->ltot-1);
678 0 : gl->loff = sel-1;
679 0 : if ( gl->loff > top )
680 0 : gl->loff = top;
681 0 : if ( sel-1<0 )
682 0 : gl->loff = 0;
683 : }
684 0 : GScrollBarSetPos(&gl->vsb->g,gl->loff);
685 0 : _ggadget_redraw(&gl->g);
686 0 : return( true );
687 0 : } else if ( event->u.chr.chars[0]!='\0' && gl->orderer ) {
688 0 : int len = u_strlen(event->u.chr.chars);
689 0 : if ( sofar_pos+len >= gl->sofar_max ) {
690 0 : if ( gl->sofar_max == 0 )
691 0 : gl->sofar = malloc((gl->sofar_max = len+10) * sizeof(unichar_t));
692 : else
693 0 : gl->sofar = realloc(gl->sofar,(gl->sofar_max = sofar_pos+len+10)*sizeof(unichar_t));
694 : }
695 0 : u_strcpy(gl->sofar+sofar_pos,event->u.chr.chars);
696 0 : gl->sofar_pos = sofar_pos + len;
697 0 : sel = GListFindPosition(gl,gl->sofar);
698 0 : gl->enduser = GDrawRequestTimer(gl->g.base,GListTypeTime,0,NULL);
699 : }
700 :
701 0 : if ( loff==0x80000000 && sel>=0 ) {
702 0 : if ( sel>=gl->ltot ) sel = gl->ltot-1;
703 0 : if ( sel<gl->loff ) loff = sel-gl->loff;
704 0 : else if ( sel>=gl->loff+GListLinesInWindow(gl,gl->loff) )
705 0 : loff = sel-(gl->loff+GListLinesInWindow(gl,gl->loff)-1);
706 : } else
707 0 : sel = -1;
708 0 : if ( sel!=-1 ) {
709 0 : int wassel = gl->ti[sel]->selected;
710 0 : refresh = GListAnyOtherSels(gl,sel) || !wassel;
711 0 : GListSelectOne(&gl->g,sel);
712 0 : if ( refresh )
713 0 : GListSelected(gl,false,sel);
714 : }
715 0 : if ( loff!=0x80000000 || xoff!=0x80000000 ) {
716 0 : if ( loff==0x80000000 ) loff = 0;
717 0 : if ( xoff==0x80000000 ) xoff = 0;
718 0 : GListScrollBy(gl,loff,xoff);
719 : }
720 0 : if ( refresh )
721 0 : _ggadget_redraw(g);
722 0 : if ( loff!=0x80000000 || xoff!=0x80000000 || sel!=-1 )
723 0 : return( true );
724 :
725 0 : return( false );
726 : }
727 :
728 0 : static int glist_timer(GGadget *g, GEvent *event) {
729 0 : GList *gl = (GList *) g;
730 :
731 0 : if ( event->u.timer.timer == gl->enduser ) {
732 0 : gl->enduser = NULL;
733 0 : gl->sofar_pos = 0;
734 0 : return( true );
735 0 : } else if ( event->u.timer.timer == gl->pressed ) {
736 : GEvent e;
737 0 : e.type = et_mousemove;
738 0 : GDrawGetPointerPosition(g->base,&e);
739 0 : if ( e.u.mouse.x<g->inner.x || e.u.mouse.y <g->inner.y ||
740 0 : e.u.mouse.x >= g->inner.x + g->inner.width ||
741 0 : e.u.mouse.y >= g->inner.y + g->inner.height )
742 0 : glist_scroll_selbymouse(gl,&e);
743 0 : return( true );
744 : }
745 0 : return( false );
746 : }
747 :
748 0 : static int glist_scroll(GGadget *g, GEvent *event) {
749 0 : int loff = 0;
750 0 : enum sb sbt = event->u.control.u.sb.type;
751 0 : GList *gl = (GList *) (g->data);
752 :
753 0 : g = (GGadget *) gl;
754 :
755 0 : if ( sbt==et_sb_top )
756 0 : loff = -gl->loff;
757 0 : else if ( sbt==et_sb_bottom )
758 0 : loff = GListTopInWindow(gl,gl->ltot-1)-gl->loff;
759 0 : else if ( sbt==et_sb_up ) {
760 0 : if ( gl->loff!=0 ) loff = -1; else loff = 0;
761 0 : } else if ( sbt==et_sb_down ) {
762 0 : if ( gl->loff + GListLinesInWindow(gl,gl->loff)<gl->ltot ) loff = 1; else loff = 0;
763 0 : } else if ( sbt==et_sb_uppage ) {
764 0 : loff = GListTopInWindow(gl,gl->loff);
765 0 : if ( loff == gl->loff ) /* Normally we leave one line in window from before, except if only one line fits */
766 0 : loff = GListTopInWindow(gl,gl->loff-1);
767 0 : loff -= gl->loff;
768 0 : } else if ( sbt==et_sb_downpage ) {
769 0 : loff = GListLinesInWindow(gl,gl->loff)-1;
770 0 : if ( loff<=0 ) loff = 1;
771 0 : if ( loff + gl->loff >= gl->ltot )
772 0 : loff = GListTopInWindow(gl,gl->ltot-1)-gl->loff;
773 : } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
774 0 : loff = event->u.control.u.sb.pos - gl->loff;
775 : }
776 0 : GListScrollBy(gl,loff,0);
777 0 : return( true );
778 : }
779 :
780 0 : static void GList_destroy(GGadget *g) {
781 0 : GList *gl = (GList *) g;
782 :
783 0 : if ( gl==NULL )
784 0 : return;
785 0 : GDrawCancelTimer(gl->enduser);
786 0 : GDrawCancelTimer(gl->pressed);
787 0 : if ( gl->freeti )
788 0 : GTextInfoArrayFree(gl->ti);
789 0 : free(gl->sofar);
790 0 : if ( gl->vsb!=NULL )
791 0 : (gl->vsb->g.funcs->destroy)(&gl->vsb->g);
792 0 : _ggadget_destroy(g);
793 : }
794 :
795 0 : static void GListSetFont(GGadget *g,FontInstance *new) {
796 0 : GList *gl = (GList *) g;
797 : int same;
798 :
799 0 : gl->font = new;
800 0 : gl->hmax = GTextInfoGetMaxHeight(gl->g.base,gl->ti,gl->font,&same);
801 0 : gl->sameheight = same;
802 0 : }
803 :
804 0 : static FontInstance *GListGetFont(GGadget *g) {
805 0 : GList *b = (GList *) g;
806 0 : return( b->font );
807 : }
808 :
809 0 : static void glist_redraw(GGadget *g) {
810 0 : GList *gl = (GList *) g;
811 0 : if ( gl->vsb!=NULL )
812 0 : _ggadget_redraw((GGadget *) (gl->vsb));
813 0 : _ggadget_redraw(g);
814 0 : }
815 :
816 0 : static void glist_move(GGadget *g, int32 x, int32 y ) {
817 0 : GList *gl = (GList *) g;
818 0 : if ( gl->vsb!=NULL )
819 0 : _ggadget_move((GGadget *) (gl->vsb),x+(gl->vsb->g.r.x-g->r.x),y);
820 0 : _ggadget_move(g,x,y);
821 0 : }
822 :
823 0 : static void glist_resize(GGadget *g, int32 width, int32 height ) {
824 0 : GList *gl = (GList *) g;
825 0 : if ( gl->vsb!=NULL ) {
826 0 : int oldwidth = gl->vsb->g.r.x+gl->vsb->g.r.width-g->r.x;
827 0 : _ggadget_move((GGadget *) (gl->vsb),gl->vsb->g.r.x+width-oldwidth,gl->vsb->g.r.y);
828 0 : _ggadget_resize(g,width-(oldwidth-g->r.width), height);
829 0 : _ggadget_resize((GGadget *) (gl->vsb),gl->vsb->g.r.width,height);
830 0 : GListCheckSB(gl);
831 : } else
832 0 : _ggadget_resize(g,width,height);
833 0 : }
834 :
835 0 : static GRect *glist_getsize(GGadget *g, GRect *r ) {
836 0 : GList *gl = (GList *) g;
837 0 : _ggadget_getsize(g,r);
838 0 : if ( gl->vsb!=NULL )
839 0 : r->width = gl->vsb->g.r.x+gl->vsb->g.r.width-g->r.x;
840 0 : return( r );
841 : }
842 :
843 0 : static void glist_setvisible(GGadget *g, int visible ) {
844 0 : GList *gl = (GList *) g;
845 0 : if ( gl->vsb!=NULL ) _ggadget_setvisible(&gl->vsb->g,visible);
846 0 : _ggadget_setvisible(g,visible);
847 0 : }
848 :
849 0 : static void glist_setenabled(GGadget *g, int enabled ) {
850 0 : GList *gl = (GList *) g;
851 0 : if ( gl->vsb!=NULL ) _ggadget_setenabled(&gl->vsb->g,enabled);
852 0 : _ggadget_setenabled(g,enabled);
853 0 : }
854 :
855 0 : static void GListGetDesiredSize(GGadget *g,GRect *outer, GRect *inner) {
856 0 : GList *gl = (GList *) g;
857 0 : int width=0, height=0, temp;
858 0 : int bp = GBoxBorderWidth(gl->g.base,gl->g.box);
859 : int i;
860 :
861 : /* can't deal with eliptical scrolling lists nor diamond ones. Just rects and roundrects */
862 0 : if ( g->desired_width<=0 ) {
863 0 : GListFindXMax(gl);
864 :
865 0 : width = gl->xmax;
866 0 : temp = GDrawPointsToPixels(gl->g.base,50);
867 0 : if ( width<temp ) width = temp;
868 0 : width += GDrawPointsToPixels(gl->g.base,_GScrollBar_Width) +
869 0 : GDrawPointsToPixels(gl->g.base,1);
870 : } else
871 0 : width = g->desired_width - 2*bp;
872 :
873 0 : if ( g->desired_height<=0 ) {
874 0 : for ( i=0; i<gl->ltot && i<8; ++i ) {
875 0 : height += GTextInfoGetHeight(gl->g.base,gl->ti[i],gl->font);
876 : }
877 0 : if ( i<4 ) {
878 : int as, ds, ld;
879 0 : GDrawWindowFontMetrics(g->base,gl->font,&as, &ds, &ld);
880 0 : height += (4-i)*(as+ds);
881 : }
882 : } else
883 0 : height = g->desired_height - 2*bp;
884 0 : if ( inner!=NULL ) {
885 0 : inner->x = inner->y = 0;
886 0 : inner->width = width;
887 0 : inner->height = height;
888 : }
889 0 : if ( outer!=NULL ) {
890 0 : outer->x = outer->y = 0;
891 0 : outer->width = width + 2*bp;
892 0 : outer->height = height + 2*bp;
893 : }
894 0 : }
895 :
896 0 : static int glist_FillsWindow(GGadget *g) {
897 0 : return( g->prev==NULL &&
898 0 : (_GWidgetGetGadgets(g->base)==g ||
899 0 : _GWidgetGetGadgets(g->base)==(GGadget *) ((GList *) g)->vsb));
900 : }
901 :
902 : struct gfuncs GList_funcs = {
903 : 0,
904 : sizeof(struct gfuncs),
905 :
906 : glist_expose,
907 : glist_mouse,
908 : glist_key,
909 : NULL,
910 : NULL,
911 : glist_timer,
912 : NULL,
913 :
914 : glist_redraw,
915 : glist_move,
916 : glist_resize,
917 : glist_setvisible,
918 : glist_setenabled,
919 : glist_getsize,
920 : _ggadget_getinnersize,
921 :
922 : GList_destroy,
923 :
924 : NULL,
925 : NULL,
926 : NULL,
927 : NULL,
928 : NULL,
929 : GListSetFont,
930 : GListGetFont,
931 :
932 : GListClear,
933 : GListSetList,
934 : GListGetList,
935 : GListGetListItem,
936 : GListSelect,
937 : GListSelectOne,
938 : GListIsItemSelected,
939 : GListGetFirstSelPos,
940 : GListShowPos,
941 : GListScrollToText,
942 : GListSetOrderer,
943 :
944 : GListGetDesiredSize,
945 : _ggadget_setDesiredSize,
946 : glist_FillsWindow,
947 : NULL
948 : };
949 :
950 : static GBox list_box = GBOX_EMPTY; /* Don't initialize here */;
951 : static FontInstance *list_font = NULL;
952 : static int glist_inited = false;
953 :
954 : static GTextInfo list_choices[] = {
955 : { (unichar_t *) "1", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
956 : { (unichar_t *) "2", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
957 : { (unichar_t *) "3", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
958 : GTEXTINFO_EMPTY
959 : };
960 : static GGadgetCreateData list_gcd[] = {
961 : { GListCreate, { { 0, 0, 0, 36 }, NULL, 0, 0, 0, 0, 0, &list_choices[0], { list_choices }, gg_visible, NULL, NULL }, NULL, NULL },
962 : { GListCreate, { { 0, 0, 0, 36 }, NULL, 0, 0, 0, 0, 0, &list_choices[0], { list_choices }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL }
963 : };
964 : static GGadgetCreateData *tarray[] = { GCD_Glue, &list_gcd[0], GCD_Glue, &list_gcd[1], GCD_Glue, NULL, NULL };
965 : static GGadgetCreateData listhvbox =
966 : { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) tarray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
967 : static GResInfo glist_ri = {
968 : NULL, &ggadget_ri,NULL, NULL,
969 : &list_box,
970 : &list_font,
971 : &listhvbox,
972 : NULL,
973 : N_("List"),
974 : N_("List"),
975 : "GList",
976 : "Gdraw",
977 : false,
978 : box_foreground_border_outer,
979 : NULL,
980 : GBOX_EMPTY,
981 : NULL,
982 : NULL,
983 : NULL
984 : };
985 :
986 0 : static void GListInit() {
987 0 : _GGadgetCopyDefaultBox(&list_box);
988 0 : list_box.flags |= box_foreground_border_outer;
989 0 : list_font = _GGadgetInitDefaultBox("GList.",&list_box,NULL);
990 0 : glist_inited = true;
991 0 : }
992 :
993 0 : static void GListFit(GList *gl) {
994 0 : int bp = GBoxBorderWidth(gl->g.base,gl->g.box);
995 : GRect inner, outer;
996 :
997 0 : GListGetDesiredSize(&gl->g,&outer,&inner);
998 0 : if ( gl->g.r.width==0 )
999 0 : gl->g.r.width = outer.width;
1000 0 : if ( gl->g.r.height==0 )
1001 0 : gl->g.r.height = outer.height;
1002 0 : gl->g.inner = gl->g.r;
1003 0 : gl->g.inner.x += bp; gl->g.inner.y += bp;
1004 0 : gl->g.inner.width -= 2*bp; gl->g.inner.height -= 2*bp;
1005 0 : GListCheckSB(gl);
1006 0 : }
1007 :
1008 0 : static GList *_GListCreate(GList *gl, struct gwindow *base, GGadgetData *gd,void *data, GBox *def) {
1009 : int same;
1010 :
1011 0 : if ( !glist_inited )
1012 0 : GListInit();
1013 0 : gl->g.funcs = &GList_funcs;
1014 0 : _GGadget_Create(&gl->g,base,gd,data,def);
1015 0 : gl->font = list_font;
1016 0 : gl->g.takes_input = gl->g.takes_keyboard = true; gl->g.focusable = true;
1017 :
1018 0 : if ( !(gd->flags & gg_list_internal ) ) {
1019 0 : gl->ti = GTextInfoArrayFromList(gd->u.list,&gl->ltot);
1020 0 : gl->freeti = true;
1021 : } else {
1022 0 : gl->ti = (GTextInfo **) (gd->u.list);
1023 0 : gl->ltot = GTextInfoArrayCount(gl->ti);
1024 : }
1025 0 : gl->hmax = GTextInfoGetMaxHeight(gl->g.base,gl->ti,gl->font,&same);
1026 0 : gl->sameheight = same;
1027 0 : if ( gd->flags & gg_list_alphabetic ) {
1028 0 : gl->orderer = GListAlphaCompare;
1029 0 : GListOrderIt(gl);
1030 : }
1031 0 : gl->start = gl->end = -1;
1032 0 : if ( gd->flags & gg_list_multiplesel )
1033 0 : gl->multiple_sel = true;
1034 0 : else if ( gd->flags & gg_list_exactlyone ) {
1035 0 : int sel = GListGetFirstSelPos(&gl->g);
1036 0 : gl->exactly_one = true;
1037 0 : if ( sel==-1 ) sel = 0;
1038 0 : GListClearSel(gl);
1039 0 : if ( gl->ltot>0 ) gl->ti[sel]->selected = true;
1040 : }
1041 :
1042 0 : GListFit(gl);
1043 0 : _GGadget_FinalPosition(&gl->g,base,gd);
1044 :
1045 0 : if ( gd->flags & gg_group_end )
1046 0 : _GGadgetCloseGroup(&gl->g);
1047 0 : GWidgetIndicateFocusGadget(&gl->g);
1048 0 : return( gl );
1049 : }
1050 :
1051 0 : GGadget *GListCreate(struct gwindow *base, GGadgetData *gd,void *data) {
1052 0 : GList *gl = _GListCreate(calloc(1,sizeof(GList)),base,gd,data,&list_box);
1053 :
1054 0 : return( &gl->g );
1055 : }
1056 :
1057 0 : static int popup_eh(GWindow popup,GEvent *event) {
1058 0 : GGadget *owner = GDrawGetUserData(popup);
1059 :
1060 0 : if ( event->type == et_controlevent ) {
1061 0 : GList *gl = (GList *) (event->u.control.g);
1062 0 : void (*inform)(GGadget *,int) = (void (*) (GGadget *,int)) GGadgetGetUserData(&gl->g);
1063 : int i;
1064 0 : for ( i=0; i<gl->ltot; ++i )
1065 0 : if ( gl->ti[i]->selected )
1066 0 : break;
1067 0 : if ( i>=gl->ltot ) i = -1;
1068 0 : GDrawDestroyWindow(popup);
1069 0 : (inform)(owner,i);
1070 0 : } else if ( event->type == et_close ) {
1071 0 : GGadget *g = GWindowGetFocusGadgetOfWindow(popup);
1072 0 : void (*inform)(GGadget *,int) = (void (*) (GGadget *,int)) GGadgetGetUserData(g);
1073 0 : GDrawDestroyWindow(popup);
1074 0 : _GWidget_ClearPopupOwner(owner);
1075 0 : _GWidget_ClearGrabGadget(owner);
1076 0 : (inform)(owner,-1);
1077 0 : } else if ( event->type == et_destroy ) {
1078 0 : _GWidget_ClearPopupOwner(owner);
1079 0 : _GWidget_ClearGrabGadget(owner);
1080 : }
1081 0 : return( true );
1082 : }
1083 :
1084 0 : static void GListPopupFigurePos(GGadget *owner,GTextInfo **ti,GRect *pos) {
1085 : int width, height, width1, maxh;
1086 : int i;
1087 0 : GWindow root = GDrawGetRoot(GDrawGetDisplayOfWindow(owner->base));
1088 : GRect rsize, rootsize;
1089 : int bp;
1090 : GPoint pt;
1091 :
1092 0 : if ( !glist_inited )
1093 0 : GListInit();
1094 0 : GDrawGetSize(GDrawGetRoot(GDrawGetDisplayOfWindow(owner->base)),&rootsize);
1095 0 : maxh = 2*rootsize.height/3;
1096 0 : width = GTextInfoGetMaxWidth(owner->base,ti,list_font);
1097 0 : height = 0;
1098 0 : for ( i=0; height<maxh && (ti[i]->text!=NULL || ti[i]->image!=NULL || ti[i]->line); ++i )
1099 0 : height += GTextInfoGetHeight(owner->base,ti[i],list_font);
1100 0 : if ( ti[i]->text!=NULL || ti[i]->image!=NULL || ti[i]->line ) /* Need a scroll bar if more */
1101 0 : width += GDrawPointsToPixels(owner->base,_GScrollBar_Width) +
1102 0 : GDrawPointsToPixels(owner->base,1);
1103 0 : bp = GBoxBorderWidth(owner->base,&list_box);
1104 0 : width += 2*bp; height += 2*bp;
1105 0 : if ( (width1 = width)<owner->r.width ) width = owner->r.width;
1106 :
1107 0 : GDrawGetSize(root,&rsize);
1108 0 : if ( width>rsize.width ) width = rsize.width;
1109 0 : if ( height>rsize.height ) height = rsize.height;
1110 0 : pt.x = owner->r.x; pt.y = owner->r.y+owner->r.height;
1111 0 : GDrawTranslateCoordinates(owner->base,root,&pt);
1112 0 : if ( pt.y+height > rsize.height ) {
1113 0 : pt.x = owner->r.x; pt.y = owner->r.y-height;
1114 0 : GDrawTranslateCoordinates(owner->base,root,&pt);
1115 0 : if ( pt.y<0 ) {
1116 0 : pt.y = 0;
1117 : /* Ok, it will overlap the base widget. not that good an idea */
1118 0 : if ( pt.x+owner->r.width+width+3<rsize.width )
1119 0 : pt.x += owner->r.width+3;
1120 0 : else if ( pt.x-width-3>=0 )
1121 0 : pt.x -= width+3;
1122 : else {
1123 : /* But there doesn't seem much we can do about it if we get here */
1124 : ;
1125 : }
1126 : }
1127 : }
1128 0 : pos->y = pt.y;
1129 :
1130 0 : if ( pt.x+width > rsize.width ) width = width1;
1131 0 : if ( pt.x+width > rsize.width ) {
1132 0 : pt.x = owner->r.x+owner->r.width-width; pt.y = 0;
1133 0 : GDrawTranslateCoordinates(owner->base,root,&pt);
1134 0 : if ( pt.x<0 )
1135 0 : pt.x = 0;
1136 : }
1137 0 : pos->x = pt.x;
1138 0 : pos->width = width;
1139 0 : pos->height = height;
1140 0 : }
1141 :
1142 0 : GWindow GListPopupCreate(GGadget *owner,void (*inform)(GGadget *,int), GTextInfo **ti) {
1143 : GWindow popup;
1144 : GWindowAttrs pattrs;
1145 0 : GDisplay *disp = GDrawGetDisplayOfWindow(owner->base);
1146 : GRect pos;
1147 : GGadgetData gd;
1148 : GList *gl;
1149 : int i;
1150 : GEvent e;
1151 :
1152 0 : if ( ti==NULL )
1153 0 : return(NULL);
1154 :
1155 0 : GDrawPointerUngrab(disp);
1156 0 : GDrawGetPointerPosition(owner->base,&e);
1157 :
1158 0 : pattrs.mask = wam_events|wam_nodecor|wam_positioned|wam_cursor|wam_transient|wam_verytransient;
1159 0 : pattrs.event_masks = -1;
1160 0 : pattrs.nodecoration = true;
1161 0 : pattrs.positioned = true;
1162 0 : pattrs.cursor = ct_pointer;
1163 0 : pattrs.transient = GWidgetGetTopWidget(owner->base);
1164 :
1165 0 : GListPopupFigurePos(owner,ti,&pos);
1166 0 : popup = GDrawCreateTopWindow(disp,&pos,popup_eh,owner,&pattrs);
1167 :
1168 0 : memset(&gd,'\0',sizeof(gd));
1169 0 : gd.pos.x = gd.pos.y = 0;
1170 0 : gd.pos.width = pos.width; gd.pos.height = pos.height;
1171 0 : gd.u.list = (GTextInfo *) ti;
1172 0 : gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels | gg_list_internal |
1173 : gg_pos_use0;
1174 0 : gl = (GList *) GListCreate(popup,&gd,(void *) inform);
1175 0 : for ( i=0; ti[i]->text!=NULL || ti[i]->image!=NULL || ti[i]->line ; ++i )
1176 0 : if ( ti[i]->selected ) {
1177 0 : GListScrollBy(gl,i,0);
1178 0 : break;
1179 : }
1180 0 : GDrawSetVisible(popup,true);
1181 0 : GDrawPointerGrab(popup);
1182 0 : _GWidget_SetGrabGadget(&gl->g);
1183 0 : if ( e.u.mouse.state&(ksm_button1|ksm_button2|ksm_button3) )
1184 0 : gl->parentpressed = true;
1185 0 : gl->ispopup = true;
1186 0 : _GWidget_SetPopupOwner(owner);
1187 0 : return( popup );
1188 : }
1189 :
1190 0 : int GListIndexFromY(GGadget *g,int y) {
1191 0 : return( GListIndexFromPos( (GList *) g, y ));
1192 : }
1193 :
1194 0 : void GListSetSBAlwaysVisible(GGadget *g,int always) {
1195 0 : ((GList *) g)->always_show_sb = always;
1196 0 : }
1197 :
1198 0 : void GListSetPopupCallback(GGadget *g,void (*callback)(GGadget *,int)) {
1199 0 : ((GList *) g)->popup_callback = callback;
1200 0 : }
1201 :
1202 0 : GResInfo *_GListRIHead(void) {
1203 : int as,ds,ld;
1204 :
1205 0 : if ( !glist_inited )
1206 0 : GListInit();
1207 : /* bp = GBoxBorderWidth(GDrawGetRoot(NULL),&list_box);*/ /* This gives bizarre values */
1208 0 : GDrawWindowFontMetrics(GDrawGetRoot(NULL),list_font,&as, &ds, &ld); /* I don't have a window yet... */
1209 0 : list_gcd[0].gd.pos.height = list_gcd[1].gd.pos.height = 2*(as+ds)+4;
1210 0 : return( &glist_ri );
1211 : }
|