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