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