1 /*  Part of XPCE --- The SWI-Prolog GUI toolkit
2 
3     Author:        Jan Wielemaker and Anjo Anjewierden
4     E-mail:        jan@swi.psy.uva.nl
5     WWW:           http://www.swi.psy.uva.nl/projects/xpce/
6     Copyright (c)  1985-2002, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 #include <h/text.h>
38 
39 static Int    normalise_index(ListBrowser, Int);
40 static status clearSelectionListBrowser(ListBrowser);
41 static status ChangeItemListBrowser(ListBrowser, DictItem);
42 static status ChangedListBrowser(ListBrowser);
43 static status geometryListBrowser(ListBrowser, Int, Int, Int, Int);
44 static status deselectListBrowser(ListBrowser, DictItem);
45 static status showLabelListBrowser(ListBrowser, BoolObj);
46 static status selectListBrowser(ListBrowser, DictItem);
47 static status scrollUpListBrowser(ListBrowser, Int);
48 static status scrollDownListBrowser(ListBrowser, Int);
49 static status extendPrefixListBrowser(ListBrowser);
50 
51 #define swap(x, y)	{ int z; z=x; x=y; y=z; }
52 
53 		/********************************
54 		*            CREATE		*
55 		********************************/
56 
57 static status
initialiseListBrowser(ListBrowser lb,Dict dict,Int w,Int h)58 initialiseListBrowser(ListBrowser lb, Dict dict, Int w, Int h)
59 { int fw, fh, iw, ih;
60 
61   if ( isDefault(dict) )
62     dict = newObject(ClassDict, EAV);
63 
64   if ( notNil(dict->browser) )
65     return errorPce(lb, NAME_alreadyShown, dict, dict->browser);
66 
67   assign(lb, size, newObject(ClassSize, EAV));
68   copySize(lb->size, getClassVariableValueObject(lb, NAME_size));
69   if ( notDefault(w) ) assign(lb->size, w, w);
70   if ( notDefault(h) ) assign(lb->size, h, h);
71 
72   initialiseDevice((Device) lb);
73 
74   assign(lb,   pen,		      getClassVariableValueObject(lb, NAME_pen));
75   assign(lb,   dict,                  dict);
76   assign(dict, browser,               lb);
77   assign(lb,   status,		      NAME_inactive);
78   assign(lb,   key_binding,	      newObject(ClassKeyBinding, NIL,
79 						NAME_listBrowser, EAV));
80   assign(lb,   select_message,        NIL);
81   assign(lb,   open_message,          NIL);
82   assign(lb,   cancel_message,	      NIL);
83   assign(lb,   multiple_selection,    OFF);
84   assign(lb,   selection,             NIL);
85   assign(lb,   start,	              ZERO);
86   assign(lb,   search_string,         NIL);
87   assign(lb,   search_origin,         ZERO);
88   assign(lb,   search_hit,	      toInt(-1));
89   assign(lb,   label_text,	      NIL);
90   assign(lb,   styles,		      newObject(ClassSheet, EAV));
91   assign(lb,   selection_style,       getClassVariableValueObject(lb,
92 						      NAME_selectionStyle));
93 
94   lb->start_cell = NIL;
95 
96   assign(lb, font, getClassVariableValueObject(lb, NAME_font));
97   fw = valInt(getExFont(lb->font));
98   fh = valInt(getHeightFont(lb->font));
99   iw = valInt(lb->size->w) * fw + 2 * TXT_X_MARGIN;
100   ih = valInt(lb->size->h) * fh + 2 * TXT_Y_MARGIN;
101 
102   assign(lb, image, newObject(ClassTextImage, lb, toInt(iw), toInt(ih), EAV));
103   assign(lb->image, wrap, NAME_none);
104   assign(lb, scroll_bar, newObject(ClassScrollBar, lb, NAME_vertical, EAV));
105 
106   send(lb->image, NAME_cursor, getClassVariableValueObject(lb, NAME_cursor), EAV);
107   send(lb->image, NAME_set,
108        lb->scroll_bar->area->w, ZERO, DEFAULT, toInt(ih), EAV);
109   displayDevice(lb, lb->scroll_bar, DEFAULT);
110   displayDevice(lb, lb->image, DEFAULT);
111   if ( notNil(lb->scroll_bar) )
112     iw += valInt(getMarginScrollBar(lb->scroll_bar));
113 
114   doSetGraphical(lb, DEFAULT, DEFAULT, toInt(iw), toInt(ih));
115 
116   succeed;
117 }
118 
119 
120 static status
unlinkListBrowser(ListBrowser lb)121 unlinkListBrowser(ListBrowser lb)
122 { if ( notNil(lb->dict) )
123   { assign(lb->dict, browser, NIL);
124     assign(lb, dict, NIL);
125   }
126 
127   return unlinkDevice((Device) lb);
128 }
129 
130 
131 static Any
lbReceiver(ListBrowser lb)132 lbReceiver(ListBrowser lb)
133 { if ( instanceOfObject(lb->device, ClassBrowser) )
134     return lb->device;
135 
136   return lb;
137 }
138 
139 		 /*******************************
140 		 *		REDRAW		*
141 		 *******************************/
142 
143 static status
RedrawAreaListBrowser(ListBrowser lb,Area a)144 RedrawAreaListBrowser(ListBrowser lb, Area a)
145 { Any obg = r_background(getClassVariableValueObject(lb, NAME_background));
146 
147   RedrawAreaDevice((Device)lb, a);
148   if ( lb->pen != ZERO )
149   { int x, y, w, h;
150     int th = valInt(lb->image->area->y);
151 
152     initialiseDeviceGraphical(lb, &x, &y, &w, &h);
153     y += th;
154     h -= th;
155 
156     if ( h > 0 )
157     { r_thickness(valInt(lb->pen));
158       r_dash(lb->texture);
159 
160       r_box(x, y, w, h, 0, NIL);
161     }
162   }
163 
164   r_background(obg);
165 
166   succeed;
167 }
168 
169 		 /*******************************
170 		 *	     LOAD/SAVE		*
171 		 *******************************/
172 
173 static status
storeListBrowser(ListBrowser lb,FileObj file)174 storeListBrowser(ListBrowser lb, FileObj file)
175 { return storeSlotsObject(lb, file);
176 }
177 
178 
179 static status
loadListBrowser(ListBrowser lb,IOSTREAM * fd,ClassDef def)180 loadListBrowser(ListBrowser lb, IOSTREAM *fd, ClassDef def)
181 { TRY(loadSlotsObject(lb, fd, def));
182 
183   if ( isNil(lb->status) )
184     assign(lb, status, NAME_inactive);
185 
186   lb->start_cell = NIL;
187 
188   succeed;
189 }
190 
191 
192 		/********************************
193 		*            LABEL		*
194 		********************************/
195 
196 static status
labelListBrowser(ListBrowser lb,Name lbl)197 labelListBrowser(ListBrowser lb, Name lbl)
198 { showLabelListBrowser(lb, ON);
199 
200   send(lb->label_text, NAME_string, lbl, EAV);
201   geometryListBrowser(lb, DEFAULT, DEFAULT, DEFAULT, DEFAULT);
202 
203   succeed;
204 }
205 
206 
207 static Name
getLabelListBrowser(ListBrowser lb)208 getLabelListBrowser(ListBrowser lb)
209 { if ( notNil(lb->label_text) )
210     answer(getValueCharArray((CharArray) lb->label_text->string));
211 
212   fail;
213 }
214 
215 
216 static status
showLabelListBrowser(ListBrowser lb,BoolObj val)217 showLabelListBrowser(ListBrowser lb, BoolObj val)
218 { if ( isNil(lb->label_text) )
219   { if ( val == ON )
220     { assign(lb, label_text,
221 	     newObject(ClassText, GetLabelNameName(lb->name), NAME_left,
222 		       getClassVariableValueObject(lb, NAME_labelFont), EAV));
223       marginText(lb->label_text, lb->area->w, NAME_clip);
224       displayDevice(lb, lb->label_text, DEFAULT);
225       return geometryListBrowser(lb, DEFAULT, DEFAULT,
226 				 add(lb->image->area->x, lb->image->area->w),
227 				 lb->image->area->h);
228     } else
229       succeed;
230   }
231 
232   if ( lb->label_text->displayed != val )
233   { DisplayedGraphical(lb->label_text, val);
234     return geometryListBrowser(lb, DEFAULT, DEFAULT, DEFAULT, DEFAULT);
235   }
236 
237   succeed;
238 }
239 
240 
241 static BoolObj
getShowLabelListBrowser(ListBrowser lb)242 getShowLabelListBrowser(ListBrowser lb)
243 { if ( notNil(lb->label_text) )
244     answer(lb->label_text->displayed);
245 
246   answer(OFF);
247 }
248 
249 
250 		 /*******************************
251 		 *	     TYPING		*
252 		 *******************************/
253 
254 static status
statusListBrowser(ListBrowser lb,Name stat)255 statusListBrowser(ListBrowser lb, Name stat)
256 { if ( lb->status != stat )
257   { Elevation z;
258 
259     assign(lb, status, stat);
260 
261 				/* avoid unnecessary flickering (hack) */
262     if ( !((z = getClassVariableValueObject(lb->image, NAME_elevation)) &&
263 	   notNil(z)) )
264     { penGraphical((Graphical) lb->image,
265 		   stat == NAME_active ? add(lb->pen, ONE) : lb->pen);
266     }
267   }
268 
269   succeed;
270 }
271 
272 
273 static status
nextListBrowser(ListBrowser lb)274 nextListBrowser(ListBrowser lb)
275 { return send(lb->device, NAME_advance, lb, EAV);
276 }
277 
278 
279 static status
extendPrefixOrNextListBrowser(ListBrowser lb)280 extendPrefixOrNextListBrowser(ListBrowser lb)
281 { if ( notNil(lb->search_string) )
282   { StringObj ext = lb->search_string;
283 
284     extendPrefixListBrowser(lb);
285     if ( lb->search_string != ext )
286       succeed;
287   }
288 
289   return nextListBrowser(lb);
290 }
291 
292 
293 static status
WantsKeyboardFocusListBrowser(ListBrowser lb)294 WantsKeyboardFocusListBrowser(ListBrowser lb)
295 { if ( notNil(lb->dict) &&
296        getSizeChain(lb->dict->members) != ZERO )
297     succeed;
298 
299   fail;
300 }
301 
302 
303 		/********************************
304 		*          SCROLLBAR		*
305 		********************************/
306 
307 static Int
getViewListBrowser(ListBrowser lb)308 getViewListBrowser(ListBrowser lb)
309 { answer(div(getViewTextImage(lb->image), toInt(BROWSER_LINE_WIDTH)));
310 }
311 
312 
313 static Int
getLengthListBrowser(ListBrowser lb)314 getLengthListBrowser(ListBrowser lb)
315 { answer(notNil(lb->dict) ? lb->dict->members->size : ZERO);
316 }
317 
318 
319 		/********************************
320 		*            GEOMETRY		*
321 		********************************/
322 
323 static status
geometryListBrowser(ListBrowser lb,Int x,Int y,Int w,Int h)324 geometryListBrowser(ListBrowser lb, Int x, Int y, Int w, Int h)
325 { int ix, iw, sw, iy, ih;
326   int pen = valInt(lb->pen);
327 
328   if ( isDefault(w) || isDefault(h) )
329     computeBoundingBoxDevice((Device)lb);
330 
331   if ( isDefault(x) ) x = lb->area->x;
332   if ( isDefault(y) ) y = lb->area->y;
333   if ( isDefault(w) ) w = lb->area->w;
334   if ( isDefault(h) ) h = lb->area->h;
335 
336   if ( valInt(w) < 50 ) w = toInt(50);
337   if ( valInt(h) < 20 ) h = toInt(20);
338 
339   sw = isNil(lb->scroll_bar) ? 0 : valInt(getMarginScrollBar(lb->scroll_bar));
340   iw = valInt(w) - abs(sw);
341 
342   { int fw = valInt(getExFont(lb->font));
343     int fh = valInt(getHeightFont(lb->font));
344 
345     assign(lb->size, w, toInt((iw - 2 * TXT_X_MARGIN)/fw));
346     assign(lb->size, h, toInt((valInt(h) - 2 * TXT_Y_MARGIN)/fh));
347   }
348 
349   ix = (sw < 0 ? -sw : 0);
350   if ( getShowLabelListBrowser(lb) == ON )
351   { send(lb->label_text, NAME_set, ZERO, ZERO, w, EAV);
352     iy = valInt(lb->label_text->area->h) - pen;
353   } else
354   { iy = 0;
355   }
356   ih = valInt(h) - iy;
357 
358   send(lb->image, NAME_set, toInt(ix), toInt(iy), toInt(iw), toInt(ih), EAV);
359   if ( notNil(lb->scroll_bar) )
360     placeScrollBar(lb->scroll_bar, (Graphical) lb->image);
361 
362   return geometryDevice((Device) lb, x, y, DEFAULT, DEFAULT);
363 }
364 
365 
366 static status
SizeListBrowser(ListBrowser lb,Size size)367 SizeListBrowser(ListBrowser lb, Size size)
368 { return doSetGraphical(lb, DEFAULT, DEFAULT, size->w, size->h);
369 }
370 
371 
372 status
requestGeometryListBrowser(ListBrowser lb,Int x,Int y,Int w,Int h)373 requestGeometryListBrowser(ListBrowser lb, Int x, Int y, Int w, Int h)
374 { PceWindow v;
375 
376   if ( notDefault(w) )
377   { w = mul(w, getExFont(lb->font));
378     if ( notNil(lb->scroll_bar) )
379       w = add(w, getMarginScrollBar(lb->scroll_bar));
380     w = add(w, toInt(2 * TXT_X_MARGIN));
381   }
382 
383   if ( notDefault(h) )
384   { h = mul(h, getHeightFont(lb->font));
385     h = add(h, toInt(2 * TXT_Y_MARGIN));
386   }
387 
388   if ( instanceOfObject(v = lbReceiver(lb), ClassWindow) )
389   { int b = (valInt(v->tile->border) + valInt(v->pen)) * 2;
390 
391     if ( notDefault(w) )
392       w = add(w, toInt(b));
393     if ( notDefault(h) )
394       h = add(h, toInt(b));
395 
396     requestGeometryWindow(v, x, y, w, h);
397   } else
398     requestGeometryGraphical(lb, x, y, w, h);
399 
400   succeed;
401 }
402 
403 
404 Size
getSizeListBrowser(ListBrowser lb)405 getSizeListBrowser(ListBrowser lb)
406 { answer(lb->size);
407 }
408 
409 
410 static Int
getWidthListBrowser(ListBrowser lb)411 getWidthListBrowser(ListBrowser lb)
412 { answer(lb->size->w);
413 }
414 
415 
416 static Int
getHeightListBrowser(ListBrowser lb)417 getHeightListBrowser(ListBrowser lb)
418 { answer(lb->size->h);
419 }
420 
421 		/********************************
422 		*            FETCH		*
423 		********************************/
424 
425 static Dict	     current_dict;	/* Currently displayed dict */
426 static Cell	     current_cell;	/* Cell of this item */
427 static int	     current_item;	/* Index of current name */
428 static int	     current_index;	/* Current location */
429 static PceString	     current_name;	/* Working on this name */
430 static int	     current_search;	/* search feedback */
431 static unsigned char current_atts;	/* Attributes for it */
432 static FontObj	     current_font;	/* Current font */
433 static Colour	     current_colour;	/* Current colour */
434 static Any	     current_background; /* Current background */
435 static Image	     current_image;	/* Image to flag line */
436 
437 static void
compute_current(ListBrowser lb)438 compute_current(ListBrowser lb)
439 { if ( notNil(current_cell) )
440   { DictItem di = (DictItem) current_cell->value;
441     CharArray label = getLabelDictItem(di);
442     Style style;
443 
444     assert(valInt(di->index) == current_item);
445     current_name = (label ? &label->data : (PceString) NULL);
446 
447     if ( notDefault(di->style) &&
448 	 (style = getValueSheet(lb->styles, di->style)) )
449     { current_font	 = style->font;
450       current_colour     = style->colour;
451       current_background = style->background;
452       current_atts       = style->attributes;
453       current_image      = style->icon;
454 
455       if ( isDefault(current_font) )
456 	current_font = lb->font;
457     } else
458     { current_font       = lb->font;
459       current_colour     = DEFAULT;
460       current_background = DEFAULT;
461       current_atts       = 0;
462       current_image      = NIL;
463     }
464 
465     if ( selectedListBrowser(lb, di) )
466     { if ( isDefault(lb->selection_style) )
467 	current_atts ^= TXT_HIGHLIGHTED;
468       else
469       { current_atts |= lb->selection_style->attributes;
470 	if ( notDefault(lb->selection_style->font) )
471 	  current_font = lb->selection_style->font;
472 	if ( notDefault(lb->selection_style->colour) )
473 	  current_colour = lb->selection_style->colour;
474 	if ( notDefault(lb->selection_style->background) )
475 	  current_background = lb->selection_style->background;
476       }
477     }
478 
479     if ( di->index == lb->search_hit )
480     { current_search = lb->search_string->data.s_size;
481     } else
482       current_search = 0;
483   } else
484   { current_name       = NULL;		/* past the end */
485     current_atts       = 0;
486     current_font       = lb->font;
487     current_colour     = DEFAULT;
488     current_background = DEFAULT;
489     current_image      = NIL;
490   }
491 }
492 
493 
494 static Cell
find_cell_dict(Dict dict,Int item)495 find_cell_dict(Dict dict, Int item)
496 { if ( notNil(dict) )
497   { Cell cell;
498 
499     for_cell(cell, dict->members)
500       if ( ((DictItem) cell->value)->index == item )
501 	return cell;
502   }
503 
504   return NIL;
505 }
506 
507 
508 static void
seek_list_browser(Any obj,long int index)509 seek_list_browser(Any obj, long int index)
510 { ListBrowser lb = obj;
511   int item = index / BROWSER_LINE_WIDTH;
512   Dict d = lb->dict;
513 
514   if ( isNil(d) )
515     return;
516 
517   if ( item != current_item || d != current_dict )
518   { if ( item < current_item || d != current_dict )
519     { current_cell = find_cell_dict(lb->dict, toInt(item));
520       assert(current_cell != NULL);
521       current_dict = d;
522     } else
523     { for( ; item > current_item && notNil(current_cell); current_item++ )
524 	current_cell = current_cell->next;
525       assert(current_cell != NULL);
526     }
527 
528     current_item = item;
529 
530     compute_current(lb);
531   }
532 
533   current_index = index;
534 }
535 
536 
537 static long
scan_list_browser(Any obj,long int from,int dir,int how,int category,int * eof)538 scan_list_browser(Any obj, long int from, int dir,
539 		  int how, int category, int *eof)
540 { ListBrowser lb = obj;
541   int item = from / BROWSER_LINE_WIDTH;
542 
543   assert(dir > 0 && how == TEXT_SCAN_FOR && category == EL);
544 
545   *eof = (isNil(lb->dict) ||
546 	  ((item + 1 >= valInt(lb->dict->members->size)) ? TRUE : FALSE));
547 
548   return (item + 1) * BROWSER_LINE_WIDTH - 1;
549 }
550 
551 
552 static long
fetch_list_browser(Any obj,TextChar tc)553 fetch_list_browser(Any obj, TextChar tc)
554 { ListBrowser lb = obj;
555   int index = current_index;
556   int pos   = current_index++ % BROWSER_LINE_WIDTH;
557 
558   if ( current_name )
559   { int len = current_name->s_size;
560 
561     if ( pos <= len )
562     { if ( pos == 0 )
563       { if ( notNil(current_image) )
564 	{ tc->value.image  = current_image;
565 	  tc->type = CHAR_IMAGE;
566 	} else
567 	{ tc->value.image = NULL_IMAGE;
568 	  tc->type = CHAR_IMAGE;
569 	}
570       } else
571       { tc->value.c = str_fetch(current_name, pos-1);
572 	tc->type = CHAR_ASCII;
573       }
574     } else /* if ( pos == len+1 ) */
575     { tc->value.c = '\n';
576       tc->type = CHAR_ASCII;
577       current_index = ((index / BROWSER_LINE_WIDTH) + 1) * BROWSER_LINE_WIDTH;
578     }
579   } else
580   { tc->value.c = EOB;
581     tc->type = CHAR_ASCII;
582   }
583 
584   tc->font         = current_font;
585   tc->attributes   = current_atts;
586   tc->colour	   = current_colour;
587   tc->background   = current_background;
588   tc->index        = index;
589 
590   if ( pos > 0 && pos <= current_search )
591   { Style s = getClassVariableValueObject(lb, NAME_isearchStyle);
592 
593     if ( s && notDefault(s) )
594     { tc->attributes |= s->attributes;
595       if ( notDefault(s->font) )       tc->font = s->font;
596       if ( notDefault(s->colour) )     tc->colour = s->colour;
597       if ( notDefault(s->background) ) tc->background = s->background;
598     } else
599       tc->attributes ^= TXT_HIGHLIGHTED;
600   }
601 
602   return current_index;
603 }
604 
605 static void
rewind_list_browser(Any obj)606 rewind_list_browser(Any obj)
607 { ListBrowser lb = (ListBrowser) obj;
608   DictItem di;
609 
610   assign(lb, start, normalise_index(lb, lb->start));
611 
612   if ( isNil(lb->start_cell) ||
613        !isProperObject((di = lb->start_cell->value)) ||
614        di->index != lb->start )
615     lb->start_cell = find_cell_dict(lb->dict, lb->start);
616 
617   current_cell = lb->start_cell;
618   current_item = valInt(lb->start);
619   current_dict = lb->dict;
620   compute_current(lb);
621 }
622 
623 
624 static SeekFunction
getSeekFunctionListBrowser(ListBrowser lb)625 getSeekFunctionListBrowser(ListBrowser lb)
626 { DEBUG(NAME_SeekFunction,
627 	Cprintf("seek_list_browser = 0x%p\n", seek_list_browser));
628   answer(seek_list_browser);
629 }
630 
631 
632 static ScanFunction
getScanFunctionListBrowser(ListBrowser lb)633 getScanFunctionListBrowser(ListBrowser lb)
634 { answer(scan_list_browser);
635 }
636 
637 
638 static FetchFunction
getFetchFunctionListBrowser(ListBrowser lb)639 getFetchFunctionListBrowser(ListBrowser lb)
640 { answer(fetch_list_browser);
641 }
642 
643 
644 static MarginFunction
getMarginFunctionListBrowser(ListBrowser lb)645 getMarginFunctionListBrowser(ListBrowser lb)
646 { answer((MarginFunction) NULL);
647 }
648 
649 
650 static RewindFunction
getRewindFunctionListBrowser(ListBrowser lb)651 getRewindFunctionListBrowser(ListBrowser lb)
652 { answer(rewind_list_browser);
653 }
654 
655 
656 
657 		/********************************
658 		*            REDRAW		*
659 		********************************/
660 
661 static status
computeListBrowser(ListBrowser lb)662 computeListBrowser(ListBrowser lb)
663 { if ( notNil(lb->request_compute) )
664   { computeTextImage(lb->image);
665     requestComputeGraphical(lb->scroll_bar, DEFAULT); /* TBD: where to put? */
666     return computeDevice(lb);
667   }
668 
669   succeed;
670 }
671 
672 
673 static Int
normalise_index(ListBrowser lb,Int index)674 normalise_index(ListBrowser lb, Int index)
675 { Int size = (notNil(lb->dict) ? lb->dict->members->size : ZERO);
676 
677   if ( valInt(index) >= valInt(size) )
678     index = sub(size, ONE);
679 
680   if ( valInt(index) < 0 )
681     return ZERO;
682 
683   return index;
684 }
685 
686 
687 		/********************************
688 		*           SEARCHING		*
689 		********************************/
690 
691 static StringObj
getExtendPrefixDict(Dict dict,CharArray pref,BoolObj ign_case)692 getExtendPrefixDict(Dict dict, CharArray pref, BoolObj ign_case)
693 { LocalString(common, pref->data.s_iswide, LINESIZE);
694   Cell cell;
695   int hit = FALSE;
696 
697   common->s_size = 0;
698 
699   for_cell(cell, dict->members)
700   { DictItem di = cell->value;
701     CharArray c = getLabelDictItem(di);
702     PceString name;
703 
704     if ( !c )
705       continue;
706 
707     name = &c->data;
708     if ( name->s_size > LINESIZE ||
709 	 name->s_iswide != common->s_iswide ) /* TBD */
710       continue;
711 
712     if ( ign_case == OFF )
713     { if ( str_prefix(name, &pref->data) )
714       { if ( !hit++ )
715 	  str_cpy(common, name);
716 	else
717 	  common->s_size = str_common_length(common, name);
718       }
719     } else
720     { if ( str_icase_prefix(name, &pref->data) )
721       { if ( !hit++ )
722         { str_cpy(common, name);
723 	  str_downcase(common, 0, common->s_size);
724 	} else
725 	  common->s_size = str_icase_common_length(common, name);
726       }
727     }
728   }
729 
730   answer(StringToString(common));
731 }
732 
733 
734 static status
extendPrefixListBrowser(ListBrowser lb)735 extendPrefixListBrowser(ListBrowser lb)
736 { if ( notNil(lb->dict) )
737   { StringObj ext;
738 
739     ext = getExtendPrefixDict(lb->dict,
740 			      isNil(lb->search_string)
741 			        ? (CharArray) CtoName("")
742 			        : (CharArray) lb->search_string,
743 			      getClassVariableValueObject(lb,
744 						     NAME_searchIgnoreCase));
745 
746     assign(lb, search_string, ext);
747     executeSearchListBrowser(lb);
748   }
749 
750   succeed;
751 }
752 
753 
754 static status
extendToCurrentListBrowser(ListBrowser lb)755 extendToCurrentListBrowser(ListBrowser lb)
756 { if ( notNil(lb->search_string) )
757   { DictItem di;
758 
759     if ( notNil(lb->dict) && (di=getFindIndexDict(lb->dict, lb->search_hit)) )
760     { assign(lb, search_string,
761 	     newObject(ClassString, name_procent_s, getLabelDictItem(di), EAV));
762       return executeSearchListBrowser(lb);
763     }
764   }
765 
766   fail;
767 }
768 
769 
770 static status
cancelSearchListBrowser(ListBrowser lb)771 cancelSearchListBrowser(ListBrowser lb)
772 { DictItem di;
773 
774   assign(lb, caret, NIL);
775   assign(lb, search_string, NIL);
776   assign(lb, search_origin, ZERO);
777   if ( valInt(lb->search_hit) >= 0 )
778   { if ( notNil(lb->dict) && (di=getFindIndexDict(lb->dict, lb->search_hit)) )
779       ChangeItemListBrowser(lb, di);
780     assign(lb, search_hit, toInt(-1));
781   }
782 
783   succeed;
784 }
785 
786 
787 status
executeSearchListBrowser(ListBrowser lb)788 executeSearchListBrowser(ListBrowser lb)
789 { DictItem di;
790 
791   if ( isNil(lb->dict) ||
792        !(di=getFindPrefixDict(lb->dict, lb->search_string,
793 			      lb->search_origin,
794 			      getClassVariableValueObject(lb,
795 						     NAME_searchIgnoreCase))))
796     fail;
797 
798   if ( valInt(lb->search_hit) >= 0 )
799   { DictItem old = getFindIndexDict(lb->dict, lb->search_hit);
800 
801     if ( old != FAIL )
802       ChangeItemListBrowser(lb, old);
803   }
804   assign(lb, search_hit, di->index);
805   normaliseListBrowser(lb, di);
806   return ChangeItemListBrowser(lb, di);
807 }
808 
809 
810 static status
repeatSearchListBrowser(ListBrowser lb,Int chr,EventObj ev)811 repeatSearchListBrowser(ListBrowser lb, Int chr, EventObj ev)
812 { if ( notNil(lb->search_string) )
813   { Int oldorg = lb->search_origin;
814 
815     assign(lb, search_origin, add(lb->search_hit, ONE));
816     if ( !executeSearchListBrowser(lb) )
817     { assign(lb, search_origin, oldorg);
818       fail;
819     }
820     succeed;
821   }
822 
823   fail;
824 }
825 
826 
827 static status
backwardDeleteCharListBrowser(ListBrowser lb)828 backwardDeleteCharListBrowser(ListBrowser lb)
829 { StringObj ss = lb->search_string;
830 
831   if ( notNil(ss) )
832   { int size = valInt(getSizeCharArray(ss));
833 
834     if ( size > 1 )
835     { deleteString(ss, toInt(size-1), DEFAULT);
836       return executeSearchListBrowser(lb);
837     }
838 
839     cancelSearchListBrowser(lb);
840   }
841 
842   fail;
843 }
844 
845 
846 static status
insertSelfListBrowser(ListBrowser lb,Int times,Int chr)847 insertSelfListBrowser(ListBrowser lb, Int times, Int chr)
848 { wint_t c;
849 
850   if ( isDefault(times) )
851     times = ONE;
852 
853   if ( isDefault(chr) )
854   { EventObj ev = EVENT->value;
855 
856     if ( instanceOfObject(ev, ClassEvent) && isAEvent(ev, NAME_printable) )
857       c = valInt(getIdEvent(ev));
858     else
859       return errorPce(lb, NAME_noCharacter);
860   } else
861     c = valInt(chr);
862 
863   { LocalString(s, c <= 0xff ? FALSE : TRUE, valInt(times));
864     int i;
865 
866     for(i=0; i<valInt(times); )
867       str_store(s, i++, c);
868 
869     if ( isNil(lb->search_string) )
870     { assign(lb, search_string, StringToString(s));
871       if ( getClassVariableValueObject(lb, NAME_clearSelectionOnSearch) == ON )
872 	clearSelectionListBrowser(lb);
873     } else
874     { if ( !instanceOfObject(lb->search_string, ClassString) )
875 	assign(lb, search_string,
876 	       newObject(ClassString, name_procent_s, lb->search_string, EAV));
877       str_insert_string(lb->search_string, DEFAULT, s);
878     }
879 
880     if ( !executeSearchListBrowser(lb) )
881     { StringObj ss = lb->search_string;
882       int size = valInt(getSizeCharArray(ss));
883 
884       if ( size > 1 )
885 	deleteString(ss, toInt(size-1), DEFAULT);
886       else
887 	cancelSearchListBrowser(lb);
888 
889       fail;
890     }
891   }
892 
893   succeed;
894 }
895 
896 
897 static status
enterListBrowser(ListBrowser lb)898 enterListBrowser(ListBrowser lb)
899 { DictItem di;
900 
901   if ( isNil(lb->dict) )
902     fail;
903 
904   if ( (di=getFindIndexDict(lb->dict, lb->search_hit)) )
905   { send(lb, NAME_changeSelection, NAME_set, di, EAV);
906     return forwardListBrowser(lb, NAME_open);
907   }
908 
909   return forwardListBrowser(lb, NAME_open);
910 }
911 
912 		/********************************
913 		*        EVENT HANDLING		*
914 		********************************/
915 
916 
917 status
typedListBrowser(ListBrowser lb,EventId id)918 typedListBrowser(ListBrowser lb, EventId id)
919 { return typedKeyBinding(lb->key_binding, id, lbReceiver(lb));
920 }
921 
922 
923 DictItem
getDictItemListBrowser(ListBrowser lb,EventObj ev)924 getDictItemListBrowser(ListBrowser lb, EventObj ev)
925 { if ( insideEvent(ev, (Graphical)lb->image) )
926   { Int where = getIndexTextImage(lb->image, ev);
927 
928     if ( where && notNil(lb->dict) )
929       answer(getFindIndexDict(lb->dict,
930 			      toInt(valInt(where)/BROWSER_LINE_WIDTH)));
931   }
932 
933   fail;
934 }
935 
936 
937 Any
selectBrowserGesture()938 selectBrowserGesture()
939 { static Any g = NULL;
940 
941   if ( !g )
942     g = globalObject(NAME_browserSelectGesture, ClassBrowserSelectGesture, EAV);
943 
944   return g;
945 }
946 
947 
948 
949 static status
eventListBrowser(ListBrowser lb,EventObj ev)950 eventListBrowser(ListBrowser lb, EventObj ev)
951 { if ( isAEvent(ev, NAME_focus) )
952   { if ( isAEvent(ev, NAME_activateKeyboardFocus) )
953       return send(lb, NAME_status, NAME_active, EAV);
954     if ( isAEvent(ev, NAME_deactivateKeyboardFocus) )
955     { cancelSearchListBrowser(lb);
956       return send(lb, NAME_status, NAME_inactive, EAV);
957     }
958   }
959 
960   if ( eventDevice(lb, ev) )
961     succeed;
962 
963   if ( isAEvent(ev, NAME_keyboard) )
964     return send(lb, NAME_typed, getIdEvent(ev), EAV);
965 
966   if ( mapWheelMouseEvent(ev, lb) )
967     succeed;
968 
969   if ( isAEvent(ev, NAME_button) )
970   { DictItem di = getDictItemListBrowser(lb, ev);
971 
972     if ( di && notNil(lb->popup) && isAEvent(ev, NAME_msRightDown) )
973     { send(popupGesture(), NAME_context, di, EAV);
974 
975       if ( !postEvent(ev, (Graphical) lb, popupGesture()) )
976 	send(popupGesture(), NAME_context, NIL, EAV);
977       else
978 	succeed;
979     } else
980       return postEvent(ev, (Graphical)lb, selectBrowserGesture());
981   }
982 
983   fail;
984 }
985 
986 		/********************************
987 		*         EDIT FUNCTIONS	*
988 		********************************/
989 
990 static status
changeSelectionListBrowser(ListBrowser lb,Name action,DictItem di)991 changeSelectionListBrowser(ListBrowser lb, Name action, DictItem di)
992 { cancelSearchListBrowser(lb);
993 
994   if ( action == NAME_cancel )
995   { assign(lb, selection_origin, NIL);
996 
997     clearSelectionListBrowser(lb);
998     if ( instanceOfObject(di, ClassChain) )
999     { Cell cell;
1000 
1001       for_cell(cell, (Chain)di)
1002       { selectListBrowser(lb, cell->value);
1003       }
1004     } else if ( instanceOfObject(di, ClassDictItem) )
1005       selectListBrowser(lb, di);
1006 
1007     if ( instanceOfObject(lb->cancel_message, ClassCode) )
1008       forwardReceiverCode(lb->cancel_message,
1009 			  lbReceiver(lb),
1010 			  EAV);
1011 
1012     succeed;
1013   }
1014 
1015   if ( action != NAME_clear && isDefault(di) )
1016     return errorPce(di, NAME_unexpectedType, nameToType(NAME_dictItem));
1017 
1018   if ( action == NAME_set )
1019   { clearSelectionListBrowser(lb);
1020     selectListBrowser(lb, di);
1021     assign(lb, selection_origin, di->index);
1022   } else if ( action == NAME_toggle )
1023   { if ( selectedListBrowser(lb, di) )
1024       deselectListBrowser(lb, di);
1025     else
1026     { selectListBrowser(lb, di);
1027       assign(lb, selection_origin, di->index);
1028     }
1029   } else if ( action == NAME_extend )
1030   { if ( isNil(lb->selection) || isNil(lb->selection_origin) )
1031     { selectListBrowser(lb, di);
1032       assign(lb, selection_origin, di->index);
1033     } else
1034     { Chain ch = lb->selection;
1035       Cell cell, c2;
1036       int low, high;
1037 
1038       low = valInt(di->index);
1039       high = valInt(lb->selection_origin);
1040       if ( low > high )
1041 	swap(low, high);
1042 
1043       for_cell_save(cell, c2, ch)
1044       { DictItem di2 = cell->value;
1045 
1046 	if ( valInt(di2->index) < low || valInt(di2->index) > high )
1047 	  deselectListBrowser(lb, di2);
1048       }
1049 
1050       if ( (cell = find_cell_dict(lb->dict, toInt(low))) )
1051       { for( ; notNil(cell); cell = cell->next )
1052 	{ DictItem di2 = cell->value;
1053 
1054 	  selectListBrowser(lb, di2);
1055 	  if ( valInt(di2->index) == high )
1056 	    break;
1057 	}
1058       } else
1059       { clearSelectionListBrowser(lb);
1060 	selectListBrowser(lb, di);
1061 	assign(lb, selection_origin, di->index);
1062       }
1063     }
1064   } else /* clear */
1065   { clearSelectionListBrowser(lb);
1066     assign(lb, selection_origin, NIL);
1067   }
1068 
1069   succeed;
1070 }
1071 
1072 
1073 status
forwardListBrowser(ListBrowser lb,Name action)1074 forwardListBrowser(ListBrowser lb, Name action)
1075 { if ( notNil(lb->selection) )
1076   { if ( notNil(lb->select_message) )
1077       forwardReceiverCode(lb->select_message, lbReceiver(lb),
1078 			  lb->selection, EAV);
1079 
1080     if ( action == NAME_open )
1081     { if ( notNil(lb->open_message) )
1082       { DisplayObj d = getDisplayGraphical((Graphical)lb);
1083 
1084 	busyCursorDisplay(d, DEFAULT, DEFAULT);
1085 	forwardReceiverCode(lb->open_message, lbReceiver(lb),
1086 			    lb->selection, EAV);
1087 	busyCursorDisplay(d, NIL, DEFAULT);
1088       }
1089     }
1090   }
1091 
1092   succeed;
1093 }
1094 
1095 
1096 		/********************************
1097 		*       SELECTION HANDLING	*
1098 		********************************/
1099 
1100 status
selectedListBrowser(ListBrowser lb,DictItem di)1101 selectedListBrowser(ListBrowser lb, DictItem di)
1102 { if ( instanceOfObject(lb->selection, ClassChain) )
1103     return memberChain(lb->selection, di);
1104 
1105   if ( notNil(lb->selection) && (DictItem) lb->selection == di )
1106     succeed;
1107 
1108   fail;
1109 }
1110 
1111 
1112 static status
deselectListBrowser(ListBrowser lb,DictItem di)1113 deselectListBrowser(ListBrowser lb, DictItem di)
1114 { if ( instanceOfObject(lb->selection, ClassChain) )
1115   { if ( deleteChain(lb->selection, di) )
1116       ChangeItemListBrowser(lb, di);
1117   } else if ( notNil(lb->selection) && (DictItem) lb->selection == di )
1118   { assign(lb, selection, NIL);
1119     ChangeItemListBrowser(lb, di);
1120   }
1121 
1122   succeed;
1123 }
1124 
1125 
1126 static status
selectListBrowser(ListBrowser lb,DictItem di)1127 selectListBrowser(ListBrowser lb, DictItem di)
1128 { if ( selectedListBrowser(lb, di) )
1129     succeed;
1130 
1131   if ( lb->multiple_selection == ON )
1132   { appendChain(lb->selection, di);
1133     ChangeItemListBrowser(lb, di);
1134   } else
1135   { if ( notNil(lb->selection) )
1136       deselectListBrowser(lb, lb->selection);
1137     assign(lb, selection, di);
1138     ChangeItemListBrowser(lb, di);
1139   }
1140 
1141   succeed;
1142 }
1143 
1144 
1145 static status
clearSelectionListBrowser(ListBrowser lb)1146 clearSelectionListBrowser(ListBrowser lb)
1147 { if ( instanceOfObject(lb->selection, ClassChain) )
1148   { Chain ch = (Chain) lb->selection;
1149 
1150     while( notNil(ch->head) )
1151       deselectListBrowser(lb, ch->head->value);
1152   } else if ( notNil(lb->selection) )
1153     deselectListBrowser(lb, lb->selection);
1154 
1155   succeed;
1156 }
1157 
1158 
1159 status
selectionListBrowser(ListBrowser lb,Any obj)1160 selectionListBrowser(ListBrowser lb, Any obj)
1161 { clearSelectionListBrowser(lb);
1162 
1163   if ( instanceOfObject(obj, ClassChain) )
1164   { Chain ch = obj;
1165     Cell cell;
1166 
1167     for_cell(cell, ch)
1168       sendv(lb, NAME_select, 1, (Any *)&cell->value);
1169   } else if ( notNil(obj) )
1170     selectListBrowser(lb, obj);
1171 
1172   succeed;
1173 }
1174 
1175 
1176 Any
getSelectionListBrowser(ListBrowser lb)1177 getSelectionListBrowser(ListBrowser lb)
1178 { if ( notNil(lb->selection) )
1179     answer(lb->selection);
1180 
1181   fail;
1182 }
1183 
1184 		/********************************
1185 		*          SCROLLING		*
1186 		********************************/
1187 
1188 status
scrollToListBrowser(ListBrowser lb,Int index)1189 scrollToListBrowser(ListBrowser lb, Int index)
1190 { if ( isDefault(index) )
1191     index = (notNil(lb->dict) ? lb->dict->members->size : ZERO);
1192   index = normalise_index(lb, index);
1193 
1194   assign(lb, start, index);
1195   return startTextImage(lb->image, mul(index, toInt(BROWSER_LINE_WIDTH)), ZERO);
1196 }
1197 
1198 
1199 status
normaliseListBrowser(ListBrowser lb,DictItem di)1200 normaliseListBrowser(ListBrowser lb, DictItem di)
1201 { int here = valInt(di->index);
1202   int start, last;
1203 
1204   computeListBrowser(lb);
1205   start = valInt(lb->image->start) / BROWSER_LINE_WIDTH;
1206   last  = (valInt(lb->image->end) - 1) / BROWSER_LINE_WIDTH;
1207 
1208   if ( here >= start && here <= last )
1209     succeed;
1210   if ( here == start-1 )
1211     return scrollDownListBrowser(lb, ONE);
1212   if ( here == last+1 )
1213     return scrollUpListBrowser(lb, ONE);
1214 
1215   return scrollToListBrowser(lb,
1216 			toInt(here - valInt(getLinesTextImage(lb->image))/2));
1217 }
1218 
1219 
1220 static status
scrollUpListBrowser(ListBrowser lb,Int arg)1221 scrollUpListBrowser(ListBrowser lb, Int arg)
1222 { Int lines = (isDefault(arg) ? sub(getLinesTextImage(lb->image), ONE) : arg);
1223 
1224   if ( isDefault(arg) )
1225     cancelSearchListBrowser(lb);
1226   return scrollToListBrowser(lb, add(lb->start, lines));
1227 }
1228 
1229 
1230 static status
scrollDownListBrowser(ListBrowser lb,Int arg)1231 scrollDownListBrowser(ListBrowser lb, Int arg)
1232 { Int lines = (isDefault(arg) ? sub(getLinesTextImage(lb->image), ONE) : arg);
1233 
1234   if ( isDefault(arg) )
1235     cancelSearchListBrowser(lb);
1236   return scrollToListBrowser(lb, sub(lb->start, lines));
1237 }
1238 
1239 
1240 static status
recenterListBrowser(ListBrowser lb,Int arg)1241 recenterListBrowser(ListBrowser lb, Int arg)
1242 { cancelSearchListBrowser(lb);
1243 
1244   succeed;
1245 }
1246 
1247 
1248 static status
scrollVerticalListBrowser(ListBrowser lb,Name dir,Name unit,Int amount)1249 scrollVerticalListBrowser(ListBrowser lb, Name dir, Name unit, Int amount)
1250 { if ( unit == NAME_file )
1251   { if ( dir == NAME_goto )
1252     { int size = (isNil(lb->dict) ? 0 : valInt(lb->dict->members->size));
1253       int view = valInt(getLinesTextImage(lb->image));
1254       int h = ((size-view) * valInt(amount)) / 1000;
1255 
1256       if ( h < 0 )
1257 	h = 0;
1258 
1259       scrollToListBrowser(lb, toInt(h));
1260     }
1261   } else if ( unit == NAME_page )
1262   { int d = (valInt(getLinesTextImage(lb->image)) * valInt(amount)) / 1000;
1263 
1264     if ( d < 1 )
1265       d = 1;
1266 
1267     if ( dir == NAME_forwards )
1268       scrollUpListBrowser(lb, toInt(d));
1269     else
1270       scrollDownListBrowser(lb, toInt(d));
1271   } else if ( unit == NAME_line )
1272   { if ( dir == NAME_forwards )
1273       scrollUpListBrowser(lb, amount);
1274     else
1275       scrollDownListBrowser(lb, amount);
1276   }
1277 
1278   succeed;
1279 }
1280 
1281 
1282 static status
showScrollBarListBrowser(ListBrowser lb,BoolObj show,ScrollBar sb)1283 showScrollBarListBrowser(ListBrowser lb, BoolObj show, ScrollBar sb)
1284 { if ( isDefault(sb) || sb == lb->scroll_bar )
1285   { computeBoundingBoxDevice((Device) lb);
1286     DisplayedGraphical(lb->scroll_bar, show);
1287     geometryListBrowser(lb, DEFAULT, DEFAULT, lb->area->w, lb->area->h);
1288   }
1289 
1290   succeed;
1291 }
1292 
1293 
1294 		 /*******************************
1295 		 *	  LINE UP/DOWN		*
1296 		 *******************************/
1297 
1298 static int
onPage(DictItem di,int start,int end)1299 onPage(DictItem di, int start, int end)
1300 { if ( valInt(di->index) >= start &&
1301        valInt(di->index) <= end )
1302     succeed;
1303 
1304   fail;
1305 }
1306 
1307 
1308 static status
nextLineListBrowser(ListBrowser lb,Int lines)1309 nextLineListBrowser(ListBrowser lb, Int lines)
1310 { if ( notNil(lb->dict) )
1311   { int times = isDefault(lines) ? 1 : valInt(lines);
1312     DictItem di = NULL;
1313 
1314     if ( times == 0 )
1315       succeed;
1316 
1317     if ( valInt(lb->search_hit) >= 0 )			/* Searching */
1318     { Int newi = normalise_index(lb, toInt(valInt(lb->search_hit) + times));
1319 
1320       di = getNth0Chain(lb->dict->members, newi);
1321       if ( di )
1322       { CharArray lbl = getLabelDictItem(di);
1323 	DictItem di2 = getNth0Chain(lb->dict->members, lb->search_hit);
1324 	BoolObj ign_case = getClassVariableValueObject(lb,
1325 						    NAME_searchIgnoreCase);
1326 
1327 	ChangeItemListBrowser(lb, di2);
1328 
1329 	if ( !prefixCharArray(lbl, (CharArray)lb->search_string, ign_case) ||
1330 	     getSizeCharArray(lb->search_string) == ZERO )
1331 	{ assign(lb, search_string,
1332 		 newObject(ClassString, name_procent_s, lbl, EAV));
1333 	  assign(lb, search_origin, newi);
1334 	}
1335 	assign(lb, search_hit, newi);
1336       }
1337     } else
1338     { int start = valInt(lb->image->start) / BROWSER_LINE_WIDTH;
1339       int last  = (valInt(lb->image->end) - 1) / BROWSER_LINE_WIDTH;
1340       int caret = -1;
1341 
1342       if ( notNil(lb->caret) )
1343       { caret = valInt(lb->caret);
1344       } else if ( instanceOfObject(lb->selection, ClassDictItem) )
1345       { if ( onPage(lb->selection, start, last) )
1346 	{ DictItem di2 = lb->selection;
1347 
1348 	  caret = valInt(di2->index);
1349 	}
1350       } else if ( instanceOfObject(lb->selection, ClassChain) )
1351       { Cell cell;
1352 
1353 	for_cell(cell, (Chain)lb->selection)
1354 	{ DictItem di2 = cell->value;
1355 
1356 	  if ( onPage(di2, start, last) )
1357 	  { caret = valInt(di2->index);
1358 	    break;
1359 	  }
1360 	}
1361       }
1362       if ( caret >= 0 )
1363 	caret = valInt(normalise_index(lb, toInt(caret)));
1364       else
1365 	caret = start;
1366 
1367       caret += times;
1368       caret = valInt(normalise_index(lb, toInt(caret)));
1369       di   = getNth0Chain(lb->dict->members, toInt(caret));
1370 
1371       if ( di )
1372       { assign(lb, caret, toInt(caret));
1373 
1374 	if ( lb->multiple_selection == ON &&
1375 	     instanceOfObject(EVENT->value, ClassEvent) )
1376 	{ EventObj ev = EVENT->value;
1377 
1378 	  if ( valInt(ev->buttons) & BUTTON_shift )
1379 	    send(lb, NAME_changeSelection, NAME_extend, di, EAV);
1380 	  else
1381 	    send(lb, NAME_changeSelection, NAME_set, di, EAV);
1382 	} else
1383 	  send(lb, NAME_changeSelection, NAME_set, di, EAV);
1384       }
1385     }
1386 
1387     if ( di )
1388     { normaliseListBrowser(lb, di);
1389       return ChangeItemListBrowser(lb, di);
1390     }
1391 
1392     fail;
1393   }
1394 
1395   fail;
1396 }
1397 
1398 
1399 static status
previousLineListBrowser(ListBrowser lb,Int lines)1400 previousLineListBrowser(ListBrowser lb, Int lines)
1401 { if ( isDefault(lines) )
1402     lines = toInt(-1);
1403   else
1404     lines = neg(lines);
1405 
1406   return nextLineListBrowser(lb, lines);
1407 }
1408 
1409 
1410 		/********************************
1411 		*          ATTRIBUTES		*
1412 		********************************/
1413 
1414 
1415 static status
dictListBrowser(ListBrowser lb,Dict dict)1416 dictListBrowser(ListBrowser lb, Dict dict)
1417 { if ( lb->dict == dict )
1418     succeed;
1419 
1420   if ( notNil(dict) && notNil(dict->browser) )
1421     return errorPce(lb, NAME_alreadyShown, dict, dict->browser);
1422 
1423   if ( notNil(lb->dict) )
1424     assign(lb->dict, browser, NIL);
1425   assign(lb, dict, dict);
1426   if ( notNil(dict) )
1427     assign(dict, browser, lb);
1428   scrollToListBrowser(lb, ZERO);
1429   lb->start_cell = NIL;
1430 
1431   return ChangedListBrowser(lb);
1432 }
1433 
1434 
1435 static status
fontListBrowser(ListBrowser lb,FontObj font)1436 fontListBrowser(ListBrowser lb, FontObj font)
1437 { if ( lb->font != font )
1438   { assign(lb, font, font);
1439     setGraphical(lb, DEFAULT, DEFAULT, lb->size->w, lb->size->h);
1440     return ChangedListBrowser(lb);
1441   }
1442 
1443   succeed;
1444 }
1445 
1446 
1447 static status
styleListBrowser(ListBrowser lb,Name name,Style style)1448 styleListBrowser(ListBrowser lb, Name name, Style style)
1449 { valueSheet(lb->styles, name, style);
1450   ChangedListBrowser(lb);
1451 
1452   succeed;
1453 }
1454 
1455 
1456 static status
selectionStyleListBrowser(ListBrowser lb,Style style)1457 selectionStyleListBrowser(ListBrowser lb, Style style)
1458 { if ( lb->selection_style != style )
1459   { assign(lb, selection_style, style);
1460     ChangedListBrowser(lb);
1461   }
1462 
1463   succeed;
1464 }
1465 
1466 
1467 static status
multipleSelectionListBrowser(ListBrowser lb,BoolObj val)1468 multipleSelectionListBrowser(ListBrowser lb, BoolObj val)
1469 { if ( lb->multiple_selection != val )
1470   { if ( val == ON )
1471     { if ( isNil(lb->selection) )
1472         assign(lb, selection, newObject(ClassChain, EAV));
1473       else
1474 	assign(lb, selection, newObject(ClassChain, lb->selection, EAV));
1475     } else
1476     { if ( emptyChain(lb->selection) )
1477       { assign(lb, selection, NIL);
1478       } else
1479       { Cell cell;
1480 	int start = TRUE;
1481 
1482 	for_cell(cell, (Chain)lb->selection)
1483 	{ if ( start )
1484 	    start = FALSE;
1485 	  else
1486 	    deselectListBrowser(lb, cell->value);
1487 	}
1488 	assign(lb, selection, ((Chain) lb->selection)->head->value);
1489       }
1490     }
1491     assign(lb, multiple_selection, val);
1492   }
1493 
1494   succeed;
1495 }
1496 
1497 		/********************************
1498 		*      CHANGE NOTIFICATIONS	*
1499 		********************************/
1500 
1501 
1502 static status
DeleteItemListBrowser(ListBrowser lb,DictItem di)1503 DeleteItemListBrowser(ListBrowser lb, DictItem di)
1504 { Int where = mul(di->index, toInt(BROWSER_LINE_WIDTH));
1505 
1506   deselectListBrowser(lb, di);
1507   if ( di->index == lb->start && notNil(lb->start_cell) )
1508     lb->start_cell = lb->start_cell->next;
1509   if ( valInt(di->index) <= valInt(lb->start) && lb->start != ZERO )
1510     assign(lb, start, sub(lb->start, ONE));
1511 
1512   current_dict = NULL;			/* clears cache */
1513   return InsertTextImage(lb->image, where, toInt(-BROWSER_LINE_WIDTH));
1514 }
1515 
1516 
1517 static status
InsertItemListBrowser(ListBrowser lb,DictItem di)1518 InsertItemListBrowser(ListBrowser lb, DictItem di)
1519 { Int where = mul(di->index, toInt(BROWSER_LINE_WIDTH));
1520 
1521   current_dict = NULL;			/* clears cache */
1522   return InsertTextImage(lb->image, where, toInt(BROWSER_LINE_WIDTH));
1523 }
1524 
1525 
1526 static status
ClearListBrowser(ListBrowser lb)1527 ClearListBrowser(ListBrowser lb)
1528 { if ( !isFreeingObj(lb) )
1529   { int size = (isNil(lb->dict) ? 0 : valInt(lb->dict->members->size));
1530 
1531     lb->start_cell = NIL;
1532     assign(lb, start, ZERO);
1533 
1534     if ( instanceOfObject(lb->selection, ClassChain) )
1535       clearChain(lb->selection);
1536     else
1537       assign(lb, selection, NIL);
1538 
1539     current_dict = NULL;			/* clears cache */
1540     InsertTextImage(lb->image, ZERO, toInt(size * -BROWSER_LINE_WIDTH));
1541   }
1542 
1543   succeed;
1544 }
1545 
1546 
1547 static status
ChangeItemListBrowser(ListBrowser lb,DictItem di)1548 ChangeItemListBrowser(ListBrowser lb, DictItem di)
1549 { Int from = mul(di->index, toInt(BROWSER_LINE_WIDTH));
1550   Int to   = add(from, toInt(BROWSER_LINE_WIDTH));
1551 
1552   return ChangedRegionTextImage(lb->image, from, to);
1553 }
1554 
1555 
1556 static status
ChangedListBrowser(ListBrowser lb)1557 ChangedListBrowser(ListBrowser lb)
1558 { current_dict = NULL;			/* clears cache */
1559   ChangedRegionTextImage(lb->image, ZERO, toInt(PCE_MAX_INT));
1560 
1561   succeed;
1562 }
1563 
1564 		/********************************
1565 		*          DELEGATION		*
1566 		********************************/
1567 
1568 static status
tabStopsListBrowser(ListBrowser lb,Vector v)1569 tabStopsListBrowser(ListBrowser lb, Vector v)
1570 { return tabStopsTextImage(lb->image, v);
1571 
1572   succeed;
1573 }
1574 
1575 
1576 status
backgroundListBrowser(ListBrowser lb,Any bg)1577 backgroundListBrowser(ListBrowser lb, Any bg)
1578 { return backgroundTextImage(lb->image, bg);
1579 
1580   succeed;
1581 }
1582 					/* avoid capture by device */
1583 
1584 static status
clearListBrowser(ListBrowser lb)1585 clearListBrowser(ListBrowser lb)
1586 { if ( notNil(lb->dict) )
1587     send(lb->dict, NAME_clear, EAV);
1588 
1589   succeed;
1590 }
1591 
1592 
1593 DictItem
getMemberListBrowser(ListBrowser lb,Any key)1594 getMemberListBrowser(ListBrowser lb, Any key)
1595 { if ( notNil(lb->dict) )
1596     answer(getMemberDict(lb->dict, key));
1597 
1598   fail;
1599 }
1600 
1601 
1602 static status
referenceListBrowser(ListBrowser lb,Point ref)1603 referenceListBrowser(ListBrowser lb, Point ref)
1604 { return referenceGraphical((Graphical) lb, ref);
1605 }
1606 
1607 
1608 		/********************************
1609 		*             VISUAL		*
1610 		********************************/
1611 
1612 Chain
getContainsListBrowser(ListBrowser lb)1613 getContainsListBrowser(ListBrowser lb)
1614 { if ( notNil(lb->dict) )
1615     answer(answerObject(ClassChain, lb->dict, EAV));
1616 
1617   fail;
1618 }
1619 
1620 
1621 static Any
getMasterListBrowser(ListBrowser lb)1622 getMasterListBrowser(ListBrowser lb)
1623 { if ( instanceOfObject(lb->device, ClassBrowser) )
1624     answer(lb->device);
1625 
1626   answer(lb);
1627 }
1628 
1629 		 /*******************************
1630 		 *	 CLASS DECLARATION	*
1631 		 *******************************/
1632 
1633 /* Type declarations */
1634 
1635 static char *T_scrollVertical[] =
1636         { "{forwards,backwards,goto}", "{file,page,line}", "int" };
1637 static char *T_showScrollBar[] =
1638         { "show=[bool]", "which=[scroll_bar]" };
1639 static char *T_changeSelection[] =
1640         { "action={set,toggle,extend,clear,cancel}",
1641 	  "context=[dict_item|chain]" };
1642 static char *T_initialise[] =
1643         { "dict=[dict]", "width=[int]", "height=[int]" };
1644 static char *T_style[] =
1645         { "style_name=name", "style=style" };
1646 static char *T_insertSelf[] =
1647         { "times=[int]", "character=[char]" };
1648 static char *T_xADintD_yADintD_widthADintD_heightADintD[] =
1649         { "x=[int]", "y=[int]", "width=[int]", "height=[int]" };
1650 
1651 /* Instance Variables */
1652 
1653 static vardecl var_listBrowser[] =
1654 { SV(NAME_dict, "dict*", IV_GET|IV_STORE, dictListBrowser,
1655      NAME_delegate, "Associated dict object (table of items)"),
1656   IV(NAME_image, "text_image", IV_GET,
1657      NAME_components, "TextImage used to display textlines"),
1658   IV(NAME_scrollBar, "scroll_bar", IV_GET,
1659      NAME_components, "Scrollbar used to scroll window"),
1660   IV(NAME_labelText, "text*", IV_GET,
1661      NAME_components, "Text object that displays the label"),
1662   SV(NAME_status, "{active,inactive}", IV_GET|IV_STORE, statusListBrowser,
1663      NAME_event, "Handle typing?"),
1664   IV(NAME_keyBinding, "key_binding", IV_BOTH,
1665      NAME_accelerator, "Key binding table"),
1666   SV(NAME_selection, "chain|member:dict_item*", IV_NONE|IV_STORE,
1667      selectionListBrowser,
1668      NAME_selection, "Selected items"),
1669   SV(NAME_selectionStyle, "[style]", IV_GET|IV_STORE,
1670      selectionStyleListBrowser,
1671      NAME_appearance, "Style for selection feedback"),
1672   SV(NAME_multipleSelection, "bool", IV_GET|IV_STORE, multipleSelectionListBrowser,
1673      NAME_selection, "If @on, multiple items may be selected"),
1674   IV(NAME_selectMessage, "code*", IV_BOTH,
1675      NAME_action, "Send on left-click on item"),
1676   IV(NAME_openMessage, "code*", IV_BOTH,
1677      NAME_action, "Send on keyboard selection or double click"),
1678   IV(NAME_cancelMessage, "code*", IV_BOTH,
1679      NAME_action, "Send on drag-select with `up' outside browser"),
1680   IV(NAME_popup, "popup*", IV_BOTH,
1681      NAME_menu, "Associated popup menu"),
1682   SV(NAME_font, "font", IV_GET|IV_STORE, fontListBrowser,
1683      NAME_appearance, "Font for displayed items"),
1684   IV(NAME_styles, "sheet", IV_GET,
1685      NAME_appearance, "Name --> style mapping"),
1686   IV(NAME_size, "characters=size", IV_GET,
1687      NAME_area, "Size in characters/lines"),
1688   IV(NAME_start, "int", IV_GET,
1689      NAME_scroll, "Object on top-row of display"),
1690   IV(NAME_searchOrigin, "int", IV_NONE,
1691      NAME_search, "Start of incremental search"),
1692   IV(NAME_searchHit, "int", IV_NONE,
1693      NAME_search, "Current hit"),
1694   IV(NAME_searchString, "char_array*", IV_NONE,
1695      NAME_search, "Current search string"),
1696   IV(NAME_caret, "int*", IV_NONE,
1697      NAME_keyboard, "Location for ->next_line"),
1698   IV(NAME_selectionOrigin, "int*", IV_NONE,
1699      NAME_keyboard, "Origin for ->change_selection: extend"),
1700   IV(NAME_startCell, "alien:Cell", IV_NONE,
1701      NAME_cache, "Cell reference to top-row of display")
1702 };
1703 
1704 /* Send Methods */
1705 
1706 static senddecl send_listBrowser[] =
1707 { SM(NAME_compute, 0, NULL, computeListBrowser,
1708      DEFAULT, "Recompute the image"),
1709   SM(NAME_geometry, 4, T_xADintD_yADintD_widthADintD_heightADintD, geometryListBrowser,
1710      DEFAULT, "Resize the text_image"),
1711   SM(NAME_initialise, 3, T_initialise, initialiseListBrowser,
1712      DEFAULT, "Create from dict, width and height"),
1713   SM(NAME_requestGeometry, 4, T_xADintD_yADintD_widthADintD_heightADintD, requestGeometryListBrowser,
1714      DEFAULT, "Map size to character units"),
1715   SM(NAME_unlink, 0, NULL, unlinkListBrowser,
1716      DEFAULT, "Unlink from dict and device"),
1717   SM(NAME_typed, 1, "event_id", typedListBrowser,
1718      NAME_accelerator, "Handle typed character"),
1719   SM(NAME_showLabel, 1, "show=bool", showLabelListBrowser,
1720      NAME_appearance, "Show/unshow the label"),
1721   SM(NAME_style, 2, T_style, styleListBrowser,
1722      NAME_appearance, "Set style associated with name"),
1723   SM(NAME_tabStops, 1, "vector*", tabStopsListBrowser,
1724      NAME_appearance, "Set tab-stops (pixels)"),
1725   SV(NAME_background, 1, "[colour|pixmap]", backgroundListBrowser,
1726      NAME_appearance, "Background colour"),
1727   SM(NAME_Size, 1, "pixels=size", SizeListBrowser,
1728      NAME_area, "Set size in pixels (trap window resize)"),
1729   SM(NAME_extendPrefixOrNext, 0, NULL, extendPrefixOrNextListBrowser,
1730      NAME_caret, "->extend_prefix or ->next"),
1731   SM(NAME_next, 0, NULL, nextListBrowser,
1732      NAME_caret, "Move caret to next item (`device ->advance')"),
1733   SM(NAME_reference, 1, "point", referenceListBrowser,
1734      NAME_dialogItem, "Set reference as dialog_item"),
1735   SM(NAME_clear, 0, NULL, clearListBrowser,
1736      NAME_edit, "Remove all items from the associated dict"),
1737   SM(NAME_WantsKeyboardFocus, 0, NULL, WantsKeyboardFocusListBrowser,
1738      NAME_event, "Test if ready to accept input (non-empty)"),
1739   SM(NAME_event, 1, "event", eventListBrowser,
1740      NAME_event, "Handle arbitrary event"),
1741   SM(NAME_label, 1, "name", labelListBrowser,
1742      NAME_label, "Set the name of the label"),
1743   SM(NAME_ChangeItem, 1, "dict_item", ChangeItemListBrowser,
1744      NAME_repaint, "Handle changed item from dict"),
1745   SM(NAME_Clear, 0, NULL, ClearListBrowser,
1746      NAME_repaint, "Handle clear from dict"),
1747   SM(NAME_DeleteItem, 1, "dict_item", DeleteItemListBrowser,
1748      NAME_repaint, "Handle deleted item from dict"),
1749   SM(NAME_InsertItem, 1, "dict_item", InsertItemListBrowser,
1750      NAME_repaint, "Handle inserted item from dict"),
1751   SM(NAME_normalise, 1, "member:dict_item", normaliseListBrowser,
1752      NAME_scroll, "Make specified item visible"),
1753   SM(NAME_scrollDown, 1, "[int]", scrollDownListBrowser,
1754      NAME_scroll, "Scroll lines down (default one window)"),
1755   SM(NAME_scrollTo, 1, "[int]", scrollToListBrowser,
1756      NAME_scroll, "Make nth-1 item start of window"),
1757   SM(NAME_scrollUp, 1, "[int]", scrollUpListBrowser,
1758      NAME_scroll, "Scroll lines up (default one window)"),
1759   SM(NAME_recenter, 1, "[int]", recenterListBrowser,
1760      NAME_scroll, "Recenter current line (to be implemented)"),
1761   SM(NAME_scrollVertical, 3, T_scrollVertical, scrollVerticalListBrowser,
1762      NAME_scroll, "Handle scroll_bar request"),
1763   SM(NAME_showScrollBar, 2, T_showScrollBar, showScrollBarListBrowser,
1764      NAME_scroll, "Control visibility of the <-scroll_bar"),
1765   SM(NAME_nextLine, 1, "[int]", nextLineListBrowser,
1766      NAME_selection, "Set selection to next item"),
1767   SM(NAME_previousLine, 1, "[int]", previousLineListBrowser,
1768      NAME_selection, "Set selection to previous item"),
1769   SM(NAME_backwardDeleteChar, 0, NULL, backwardDeleteCharListBrowser,
1770      NAME_search, "Undo last search extension"),
1771   SM(NAME_cancelSearch, 0, NULL, cancelSearchListBrowser,
1772      NAME_search, "Cancel the current search operation"),
1773   SM(NAME_enter, 0, NULL, enterListBrowser,
1774      NAME_search, "Select current item as double-click"),
1775   SM(NAME_extendPrefix, 0, NULL, extendPrefixListBrowser,
1776      NAME_search, "Extend search with common part"),
1777   SM(NAME_extendToCurrent, 0, NULL, extendToCurrentListBrowser,
1778      NAME_search, "Extend search to current item"),
1779   SM(NAME_insertSelf, 2, T_insertSelf, insertSelfListBrowser,
1780      NAME_search, "Start/Continue incremental search"),
1781   SM(NAME_keyboardQuit, 0, NULL, cancelSearchListBrowser,
1782      NAME_search, "Equivalent to ->cancel_search"),
1783   SM(NAME_repeatSearch, 0, NULL, repeatSearchListBrowser,
1784      NAME_search, "Repeat with same string"),
1785   SM(NAME_changeSelection, 2, T_changeSelection, changeSelectionListBrowser,
1786      NAME_selection, "Hook in selection management"),
1787   SM(NAME_deselect, 1, "member:dict_item", deselectListBrowser,
1788      NAME_selection, "Unselect (remove from selection) item"),
1789   SM(NAME_select, 1, "member:dict_item", selectListBrowser,
1790      NAME_selection, "Select (add to selection) item"),
1791   SM(NAME_selected, 1, "member:dict_item", selectedListBrowser,
1792      NAME_selection, "Test if item is selected")
1793 };
1794 
1795 /* Get Methods */
1796 
1797 static getdecl get_listBrowser[] =
1798 { GM(NAME_contains, 0, "chain", NULL, getContainsListBrowser,
1799      DEFAULT, "Dict visualised"),
1800   GM(NAME_master, 0, "device", NULL, getMasterListBrowser,
1801      DEFAULT, "Principal visual I'm part of (self or browser)"),
1802   GM(NAME_selection, 0, "chain|dict_item", NULL, getSelectionListBrowser,
1803      DEFAULT, "Current value of selection"),
1804   GM(NAME_showLabel, 0, "bool", NULL, getShowLabelListBrowser,
1805      NAME_appearance, "Bool indicating if label is visible"),
1806   GM(NAME_height, 0, "characters=int", NULL, getHeightListBrowser,
1807      NAME_area, "Height in character units"),
1808   GM(NAME_width, 0, "characters=int", NULL, getWidthListBrowser,
1809      NAME_area, "Width in character units"),
1810   GM(NAME_dictItem, 1, "dict_item", "event", getDictItemListBrowser,
1811      NAME_event, "DictItem on which event occurred"),
1812   GM(NAME_FetchFunction, 0, "alien:FetchFunction", NULL, getFetchFunctionListBrowser,
1813      NAME_internal, "Pointer to C-function to fetch char"),
1814   GM(NAME_MarginFunction, 0, "alien:MarginFunction", NULL, getMarginFunctionListBrowser,
1815      NAME_internal, "Pointer to C-function to fetch margins"),
1816   GM(NAME_RewindFunction, 0, "alien:RewindFunction", NULL, getRewindFunctionListBrowser,
1817      NAME_internal, "Pointer to C-function to rewind object"),
1818   GM(NAME_ScanFunction, 0, "alien:ScanFunction", NULL, getScanFunctionListBrowser,
1819      NAME_internal, "Pointer to C-function to scan for char-type"),
1820   GM(NAME_SeekFunction, 0, "alien:SeekFunction", NULL, getSeekFunctionListBrowser,
1821      NAME_internal, "Pointer to C-function to seek to position"),
1822   GM(NAME_label, 0, "name", NULL, getLabelListBrowser,
1823      NAME_label, "Current value of the label"),
1824   GM(NAME_member, 1, "dict_item", "any", getMemberListBrowser,
1825      NAME_member, "DictItem with given key"),
1826   GM(NAME_length, 0, "int", NULL, getLengthListBrowser,
1827      NAME_scroll, "Length of contents (for scroll_bar)"),
1828   GM(NAME_view, 0, "int", NULL, getViewListBrowser,
1829      NAME_scroll, "Length of view (for scroll_bar)")
1830 };
1831 
1832 /* Resources */
1833 
1834 static classvardecl rc_listBrowser[] =
1835 { RC(NAME_background, "colour|pixmap", "white",
1836      "Colour/fill pattern of the background"),
1837   RC(NAME_clearSelectionOnSearch, "bool", "@on",
1838      "@on: clear selection when searching"),
1839   RC(NAME_cursor, "cursor", "right_ptr",
1840      "Default cursor"),
1841   RC(NAME_font, "font", "normal",
1842      "Default font"),
1843   RC(NAME_isearchStyle, "[style]",
1844      UXWIN("when(@colour_display,\n"
1845 	   "     style(background := green),\n"
1846 	   "     style(background:= @grey25_image))",
1847 	   "@_isearch_style"),
1848      "Style for incremental search"),
1849   RC(NAME_labelFont, "font", "bold",
1850      "Font used to display the label"),
1851   RC(NAME_pen, "0..", "1",
1852      "Thickness of box around list_browser"),
1853   RC(NAME_searchIgnoreCase, "bool", "@on",
1854      "@on: ignore case when searching"),
1855   RC(NAME_selectionStyle, "[style]",
1856      UXWIN("when(@colour_display,\n"
1857 	   "     style(background := black, colour := white),\n"
1858 	   "     style(highlight  := @on))",
1859 	   "@_select_style"),
1860      "Style object for <-selection"),
1861   RC(NAME_size, "size", "size(15,10)",
1862      "Default size in `characters x lines'")
1863 };
1864 
1865 /* Class Declaration */
1866 
1867 static Name listBrowser_termnames[] = { NAME_dict, NAME_width, NAME_height };
1868 
1869 ClassDecl(listBrowser_decls,
1870           var_listBrowser, send_listBrowser, get_listBrowser, rc_listBrowser,
1871           3, listBrowser_termnames,
1872           "$Rev$");
1873 
1874 
1875 status
makeClassListBrowser(Class class)1876 makeClassListBrowser(Class class)
1877 { declareClass(class, &listBrowser_decls);
1878 
1879   setLoadStoreFunctionClass(class, loadListBrowser, storeListBrowser);
1880   setRedrawFunctionClass(class, RedrawAreaListBrowser);
1881   delegateClass(class, NAME_dict);
1882 
1883   succeed;
1884 }
1885 
1886