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/dialog.h>
37 
38 static status	nameDialogItem(DialogItem di, Name name);
39 
40 		/********************************
41 		*         CREATE/DESTROY	*
42 		********************************/
43 
44 status
createDialogItem(Any obj,Name name)45 createDialogItem(Any obj, Name name)
46 { DialogItem di = obj;
47 
48   initialiseGraphical(di, ZERO, ZERO, ZERO, ZERO);
49 
50   if ( isDefault(name) )
51     name = getClassNameObject(di);
52   nameDialogItem(di, name);
53 
54   assign(di, status,		 NAME_inactive);
55 /*assign(di, message,		 NIL);
56   assign(di, popup,		 NIL);
57   assign(di, above,		 NIL);
58   assign(di, below,		 NIL);
59   assign(di, right,		 NIL);
60   assign(di, left,		 NIL); */
61   assign(di, reference,		 DEFAULT);
62   assign(di, label_width,	 DEFAULT);
63   assign(di, auto_label_align,	 ON);
64   assign(di, auto_value_align,	 ON);
65   assign(di, auto_align,	 ON);
66 
67   succeed;
68 }
69 
70 
71 status
unlinkDialogItem(DialogItem di)72 unlinkDialogItem(DialogItem di)
73 { return unlinkGraphical((Graphical)di);
74 }
75 
76 
77 status
RedrawLabelDialogItem(Any obj,int acc,int x,int y,int w,int h,Name hadjust,Name vadjust,int flags)78 RedrawLabelDialogItem(Any obj, int acc,
79 		      int x, int y, int w, int h,
80 		      Name hadjust, Name vadjust, int flags)
81 { DialogItem di = obj;
82 
83   if ( instanceOfObject(di->label, ClassImage) )
84   { Image i = di->label;
85     int iw = valInt(i->size->w);
86     int ih = valInt(i->size->h);
87     int ix, iy;
88 
89     if ( hadjust == NAME_left )
90       ix = x;
91     else if ( hadjust == NAME_center )
92       ix = x + (w-iw)/2;
93     else
94       ix = x + w-iw;
95 
96     if ( vadjust == NAME_top )
97       iy = y;
98     else if ( vadjust == NAME_center )
99       iy = y + (h-ih)/2;
100     else
101       iy = y + h-ih;
102 
103     r_image(i, 0, 0, ix, iy, iw, ih, ON);
104   } else if ( instanceOfObject(di->label, ClassCharArray) )
105   { CharArray label = di->label;
106 
107     str_label(&label->data, acc, di->label_font,
108 	      x, y, w, h,
109 	      hadjust, vadjust, flags);
110   }
111 
112   succeed;
113 }
114 
115 
116 status
dia_label_size(Any obj,int * w,int * h,int * isimage)117 dia_label_size(Any obj, int *w, int *h, int *isimage)
118 { DialogItem di = obj;
119 
120   if ( instanceOfObject(di->label, ClassImage) )
121   { Image i = di->label;
122 
123     *w = valInt(i->size->w);
124     *h = valInt(i->size->h);
125     if ( isimage )
126       *isimage = TRUE;
127   } else
128   { if ( isimage )
129       *isimage = FALSE;
130 
131     if ( instanceOfObject(di->label, ClassCharArray) )
132     { CharArray ca = di->label;
133 
134       str_size(&ca->data, di->label_font, w, h);
135     } else
136     { *w = *h = 0;
137     }
138   }
139 
140   succeed;
141 }
142 
143 
144 
145 static status
deviceDialogItem(DialogItem di,Device dev)146 deviceDialogItem(DialogItem di, Device dev)
147 { if ( di->device != dev && notNil(di->device) )
148   { Graphical gr = (Graphical) di;
149 
150     aboveGraphical(gr, NIL);
151     belowGraphical(gr, NIL);
152     rightGraphical(gr, NIL);
153     leftGraphical(gr, NIL);
154   }
155 
156   return deviceGraphical((Graphical)di, dev);
157 }
158 
159 
160 		/********************************
161 		*          ATTRIBUTES		*
162 		********************************/
163 
164 status
labelDialogItem(DialogItem di,Any label)165 labelDialogItem(DialogItem di, Any label)
166 { return assignGraphical(di, NAME_label, label);
167 }
168 
169 
170 status
labelFontDialogItem(DialogItem di,FontObj font)171 labelFontDialogItem(DialogItem di, FontObj font)
172 { return assignGraphical(di, NAME_labelFont, font);
173 }
174 
175 
176 static CharArray
getLabelNameDialogItem(DialogItem di,Name name)177 getLabelNameDialogItem(DialogItem di, Name name)
178 { Any suffix, label = GetLabelNameName(name);
179 
180   if ( !label || !instanceOfObject(label, ClassCharArray) )
181     label = name;			/* play safe */
182 
183   if ( (suffix = getClassVariableValueObject(di, NAME_labelSuffix)) )
184     label = getEnsureSuffixCharArray(label, suffix);
185 
186   answer(label);
187 }
188 
189 
190 static status
nameDialogItem(DialogItem di,Name name)191 nameDialogItem(DialogItem di, Name name)
192 { Any label = get(di, NAME_labelName, name, EAV);
193 
194   assign(di, name, name);
195   if ( !label )
196     label = name;
197 
198   return sendv(di, NAME_label, 1, &label);
199 }
200 
201 
202 static status
lookDialogItem(DialogItem di,Name look)203 lookDialogItem(DialogItem di, Name look)
204 { return assignGraphical(di, NAME_look, look);
205 }
206 
207 
208 static status
acceleratorDialogItem(DialogItem di,Name acc)209 acceleratorDialogItem(DialogItem di, Name acc)
210 { return assignGraphical(di, NAME_accelerator, acc);
211 }
212 
213 
214 static status
backgroundDialogItem(DialogItem di,Any bg)215 backgroundDialogItem(DialogItem di, Any bg)
216 { return assignGraphical(di, NAME_background, bg);
217 }
218 
219 
220 static status
labelFormatDialogItem(DialogItem di,Name format)221 labelFormatDialogItem(DialogItem di, Name format)
222 { return assignGraphical(di, NAME_labelFormat, format);
223 }
224 
225 
226 		/********************************
227 		*        EVENT_HANDLING		*
228 		********************************/
229 
230 static status
advanceEventDialogItem(Any obj,EventObj ev)231 advanceEventDialogItem(Any obj, EventObj ev)
232 { if ( (ev->id == toInt(9) ||
233 	ev->id == NAME_cursorRight ||
234 	ev->id == NAME_cursorLeft) &&
235        getKeyboardFocusGraphical(obj) == ON )
236   { Name dir = (ev->id == NAME_cursorLeft ? NAME_backwards : NAME_forwards);
237     Device dev = ((Graphical)obj)->device;
238 
239     send(dev, NAME_advance, obj, DEFAULT, dir, EAV);
240 
241     succeed;
242   }
243 
244   fail;
245 }
246 
247 
248 status
eventDialogItem(Any obj,EventObj ev)249 eventDialogItem(Any obj, EventObj ev)
250 { DialogItem di = obj;
251 
252   if ( eventGraphical(di, ev) )
253     succeed;
254   if ( advanceEventDialogItem(obj, ev) )
255     succeed;
256 
257   if ( di->active == ON && notNil(di->popup) && isDownEvent(ev) &&
258        send(popupGesture(), NAME_event, ev, EAV) )
259     succeed;
260 
261   fail;
262 }
263 
264 
265 static status
statusDialogItem(DialogItem di,Name stat)266 statusDialogItem(DialogItem di, Name stat)
267 { assign(di, status, stat);
268   changedDialogItem(di);
269 
270   succeed;
271 }
272 
273 
274 static status
cancelDialogItem(DialogItem di)275 cancelDialogItem(DialogItem di)
276 { return send(di, NAME_status, NAME_inactive, EAV);
277 }
278 
279 
280 		/********************************
281 		*           REPAINT		*
282 		********************************/
283 
284 
285 status
changedDialogItem(Any obj)286 changedDialogItem(Any obj)
287 { DialogItem di = obj;
288 
289   CHANGING_GRAPHICAL(di,
290 	changedEntireImageGraphical(di));
291 
292   succeed;
293 }
294 
295 
296 static status
showDialogItem(DialogItem di,BoolObj val)297 showDialogItem(DialogItem di, BoolObj val)
298 { if ( val == OFF )
299   { PceWindow sw = getWindowGraphical((Graphical) di);
300 
301     if ( sw != FAIL && sw->keyboard_focus == (Graphical) di )
302       send(di->device, NAME_advance, di, EAV);
303   }
304 
305   return DisplayedGraphical(di, val);
306 }
307 
308 
309 static BoolObj
getShowDialogItem(DialogItem di)310 getShowDialogItem(DialogItem di)
311 { answer(di->displayed);
312 }
313 
314 
315 		/********************************
316 		*          POSITIONS		*
317 		********************************/
318 
319 Point
getReferenceDialogItem(Any obj)320 getReferenceDialogItem(Any obj)
321 { DialogItem i = obj;
322 
323   ComputeGraphical(i);
324   if ( notDefault(i->reference) )
325     answer(i->reference);
326 
327   fail;
328 }
329 
330 
331 static status
resetDialogItem(DialogItem i)332 resetDialogItem(DialogItem i)
333 { send(i, NAME_status, NAME_inactive, EAV);
334 
335   succeed;
336 }
337 
338 
339 static status
openDialogItem(DialogItem di)340 openDialogItem(DialogItem di)
341 { if ( isNil(di->device) )
342   { Dialog d;
343 
344     TRY( d = newObject(ClassDialog, EAV) );
345     TRY( send(d, NAME_append, di, EAV) );
346   }
347 
348   return send(di->device, NAME_open, EAV);
349 }
350 
351 
352 		/********************************
353 		*        COMMUNICATION		*
354 		********************************/
355 
356 static BoolObj
getModifiedDialogItem(Dialog di)357 getModifiedDialogItem(Dialog di)
358 { answer(OFF);
359 }
360 
361 
362 status
modifiedDialogItem(Any di,BoolObj modified)363 modifiedDialogItem(Any di, BoolObj modified)
364 { Dialog d = di;
365 
366   if ( modified == ON )
367     return send(d->device, NAME_modifiedItem, d, ON, EAV);
368 
369   succeed;
370 }
371 
372 
373 		 /*******************************
374 		 *	   ACCELERATORS		*
375 		 *******************************/
376 
377 #define ACC_WSEP  0
378 #define ACC_UPPER 1
379 #define ACC_LOWER 2
380 #define ACC_DIGIT 3
381 
382 #define ACC_CHARSETSIZE 256
383 
384 typedef struct
385 { int	      acc;
386   int	      index;
387   int	      mode;			/* ACC_UPPER, ACC_ALNUM */
388   const char *label;			/* TBD: wide-character version */
389   Any	      object;
390 } abin, *Abin;
391 
392 
393 static status
acc_index(Abin a,unsigned char * used)394 acc_index(Abin a, unsigned char *used)
395 { int i;
396 
397   if ( a->mode == ACC_WSEP )
398   { i = a->index+1;
399 
400     do
401     { int acc = a->label[i];
402 
403       if ( isalpha(acc) )
404       { acc = tolower(acc);
405 
406 	if ( !(used && used[acc]) )
407 	{ a->index = i;
408 	  a->acc = acc;
409 	  succeed;
410 	}
411       }
412       while( a->label[i] && !isspace(a->label[i]) )
413 	i++;
414       while( a->label[i] && isspace(a->label[i]) )
415 	i++;
416     } while( a->label[i] );
417 
418     a->mode = ACC_UPPER;
419   }
420 
421   if ( a->mode == ACC_UPPER )
422   { for( i = a->index+1; a->label[i]; i++ )
423     { int acc = a->label[i];
424 
425       if ( isupper(acc) )
426       { acc = tolower(acc);
427 
428 	if ( used && used[acc] )
429 	  continue;
430 
431 	a->index = i;
432 	a->acc = acc;
433 	succeed;
434       }
435     }
436 
437     a->mode = ACC_LOWER;
438   }
439 
440   if ( a->mode == ACC_LOWER )
441   { for( i = a->index+1; a->label[i]; i++ )
442     { int acc = a->label[i];
443 
444       if ( islower(acc) )
445       { if ( used && used[acc] )
446 	  continue;
447 
448 	a->index = i;
449 	a->acc = acc;
450 	succeed;
451       }
452     }
453 
454     a->mode = ACC_DIGIT;
455   }
456 
457 
458   for( i = a->index+1; a->label[i]; i++ )
459   { int acc = a->label[i];
460 
461     if ( isdigit(acc) )
462     { if ( used && used[acc] )
463 	continue;
464 
465       a->index = i;
466       a->acc = acc;
467       succeed;
468     }
469   }
470 
471   fail;
472 }
473 
474 
475 status
assignAccelerators(Chain objects,Name prefix,Name label_method)476 assignAccelerators(Chain objects, Name prefix, Name label_method)
477 { int  size = valInt(objects->size);
478   Abin bins = alloca(sizeof(abin) * size);
479   int  n;
480   Cell cell;
481   Abin a = bins;
482   unsigned char used[ACC_CHARSETSIZE];
483   int do_free = FALSE;
484 
485   if ( size && !bins )
486   { bins = pceMalloc(sizeof(abin) * size);
487     do_free = TRUE;
488   }
489 
490   for(n=0; n<ACC_CHARSETSIZE; n++)
491     used[n] = 0;
492 
493   for_cell(cell, objects)
494   { Any lbl;
495     const char *s;
496 
497     if ( !hasSendMethodObject(cell->value, NAME_accelerator) )
498       continue;
499 
500     if ( hasGetMethodObject(cell->value, label_method) &&
501 	 (lbl = get(cell->value, label_method, EAV)) &&
502 	 ( !instanceOfObject(lbl, ClassCharArray) ||
503 	   !((CharArray)lbl)->data.s_iswide ) &&
504 	 (s = toCharp(lbl)) )
505     { a->label = s;
506       a->index = -1;
507       a->mode  = ACC_WSEP;
508       if ( acc_index(a, NULL) )
509       { used[tolower(a->acc)]++;
510 	a->object = cell->value;
511 	DEBUG(NAME_accelerator,
512 	      Cprintf("Proposing %c for %s\n", a->acc, pp(cell->value)));
513 	a++;
514       } else
515 	send(cell->value, NAME_accelerator, NIL, EAV);
516     } else
517       send(cell->value, NAME_accelerator, NIL, EAV);
518   }
519 
520   size = a - bins;
521   DEBUG(NAME_accelerator,
522 	Cprintf("Trying to find accelerators for %d objects\n", size));
523 
524   for(n=0; n<size; n++)
525   { int acc = bins[n].acc;
526 
527     if ( used[acc] > 1 )
528     { int m;
529 
530       for( m=n+1; m<size; m++ )
531       { if ( acc == bins[m].acc )
532 	{ if ( acc_index(&bins[m], used) )
533 	    used[bins[m].acc] = 1;
534 	  else
535 	    bins[m].acc = 0;
536 
537 	  used[acc]--;
538 	}
539       }
540     }
541   }
542 
543   for(n=0; n<size; n++)
544   { int acc = bins[n].acc;
545 
546     if ( acc > 0 )
547     { char buf[100];
548 
549       sprintf(buf, "%s%c", strName(prefix), acc);
550       send(bins[n].object, NAME_accelerator, CtoKeyword(buf), EAV);
551     } else
552       send(bins[n].object, NAME_accelerator, NIL, EAV);
553   }
554 
555   if ( do_free )
556     pceFree(bins);
557 
558   succeed;
559 }
560 
561 
562 
563 		 /*******************************
564 		 *	 CLASS DECLARATION	*
565 		 *******************************/
566 
567 /* Type declarations */
568 
569 
570 /* Instance Variables */
571 
572 static vardecl var_dialogItem[] =
573 { SV(NAME_label, "char_array|image*", IV_GET|IV_STORE, labelDialogItem,
574      NAME_label, "Label of the item"),
575   SV(NAME_labelFont, "font*", IV_GET|IV_STORE, labelFontDialogItem,
576      NAME_label, "Font used for the label if not an image"),
577   IV(NAME_labelWidth, "[int]", IV_NONE,
578      NAME_layout, "Width of the label in pixels"),
579   SV(NAME_labelFormat, "{left,center,right}", IV_GET|IV_STORE,
580      labelFormatDialogItem,
581      NAME_layout, "Align labels in their box"),
582   SV(NAME_background, "image|colour*", IV_GET|IV_STORE, backgroundDialogItem,
583      NAME_appearance, "Opaque background for item"),
584   SV(NAME_status, "{inactive,active,preview,execute}", IV_GET|IV_STORE,
585      statusDialogItem,
586      NAME_event, "Status for event-processing"),
587   IV(NAME_message, "[code]*", IV_BOTH,
588      NAME_action, "Associated command"),
589   IV(NAME_popup, "popup*", IV_BOTH,
590      NAME_menu, "Associated popup menu"),
591   SV(NAME_look, "{x,open_look,motif,win}|name",IV_GET|IV_STORE,lookDialogItem,
592      NAME_appearance, "Look-and-feel switch"),
593   IV(NAME_autoAlign, "bool", IV_BOTH,
594      NAME_layout, "Item is automatically placed by its dialog"),
595   IV(NAME_reference, "[point]", IV_SEND,
596      NAME_layout, "Reference point for alignment"),
597   IV(NAME_above, "graphical*", IV_GET,
598      NAME_layout, "DialogItem above me"),
599   IV(NAME_below, "graphical*", IV_GET,
600      NAME_layout, "DialogItem below me"),
601   IV(NAME_right, "graphical*", IV_GET,
602      NAME_layout, "DialogItem right of me"),
603   IV(NAME_left, "graphical*", IV_GET,
604      NAME_layout, "DialogItem left of me"),
605   IV(NAME_alignment, "{column,left,center,right}", IV_BOTH,
606      NAME_layout, "Align in columns or right of item to the left"),
607   IV(NAME_autoLabelAlign, "bool", IV_BOTH,
608      NAME_layout, "Automatically align label"),
609   IV(NAME_autoValueAlign, "bool", IV_BOTH,
610      NAME_layout, "Automatically align value"),
611   SV(NAME_accelerator, "name*", IV_GET|IV_STORE, acceleratorDialogItem,
612      NAME_layout, "Automatically align value")
613 };
614 
615 /* Send Methods */
616 
617 static senddecl send_dialogItem[] =
618 { SM(NAME_initialise, 1, "name=name", createDialogItem,
619      DEFAULT, "Create from name"),
620   SM(NAME_device, 1, "device*", deviceDialogItem,
621      DEFAULT, "Device I'm displayed on"),
622   SM(NAME_name, 1, "name", nameDialogItem,
623      DEFAULT, "Change <-name, update <-label"),
624   SM(NAME_reset, 0, NULL, resetDialogItem,
625      DEFAULT, "Change status to `inactive'"),
626 /*SM(NAME_unlink, 0, NULL, unlinkDialogItem,		empty these days
627      DEFAULT, "Remove left,right,above,below links"), */
628   SM(NAME_default, 1, "any", virtualObject,
629      NAME_apply, "Virtual method"),
630   SM(NAME_modified, 1, "bool", modifiedDialogItem,
631      NAME_apply, "Forward modification to associated <-device"),
632   SM(NAME_cancel, 0, NULL, cancelDialogItem,
633      NAME_event, "Cancel operation (enter inactive state)"),
634   SM(NAME_event, 1, "event", eventDialogItem,
635      NAME_event, "Process an event"),
636   SM(NAME_labelWidth, 1, "[int]", virtualObject,
637      NAME_layout, "Virtual method"),
638   SM(NAME_valueWidth, 1, "[int]", virtualObject,
639      NAME_layout, "Virtual method"),
640   SM(NAME_open, 0, NULL, openDialogItem,
641      NAME_organisation, "Create dialog with this item and ->open"),
642   SM(NAME_show, 1, "bool", showDialogItem,
643      NAME_organisation, "Equivalent to <->displayed")
644 };
645 
646 /* Get Methods */
647 
648 static getdecl get_dialogItem[] =
649 { GM(NAME_default, 0, "any", NULL, getVirtualObject,
650      NAME_apply, "Virtual method"),
651   GM(NAME_modified, 0, "bool", NULL, getModifiedDialogItem,
652      NAME_apply, "Virtual method (return @off)"),
653   GM(NAME_labelName, 1, "name", "name", getLabelNameDialogItem,
654      NAME_label, "Determine default-label from the name"),
655   GM(NAME_labelWidth, 0, "int", NULL, getVirtualObject,
656      NAME_layout, "Virtual method"),
657   GM(NAME_reference, 0, "point", NULL, getReferenceDialogItem,
658      NAME_layout, "Reference point for alignment"),
659   GM(NAME_valueWidth, 0, "int", NULL, getVirtualObject,
660      NAME_layout, "Virtual method"),
661   GM(NAME_show, 0, "bool", NULL, getShowDialogItem,
662      NAME_organisation, "Equivalent to <-displayed")
663 };
664 
665 /* Resources */
666 
667 static classvardecl rc_dialogItem[] =
668 { RC(NAME_alignment, "{column,left,center,right}", "column",
669      "Alignment in the row"),
670   RC(NAME_background, "colour|pixmap*", "@_dialog_bg",
671      "Background of the item"),
672   RC(NAME_elevation, "elevation*", "when(@colour_display, 1, 0)",
673      "3-D elevation"),
674   RC(NAME_labelFont, "font", "bold",
675      "Default font for labels"),
676   RC(NAME_labelFormat, "{left,center,right}", "right",
677      "Alignment of the label in its box"),
678   RC(NAME_labelSuffix, "name", ":",
679      "Ensured suffix of label"),
680   RC(NAME_look, "{x,open_look,motif,win,gtk}",
681      UXWIN("open_look", "win"),
682      "Look-and-feel switch"),
683   RC(NAME_selectionHandles, RC_REFINE, "@nil",
684      NULL),
685   RC(NAME_valueFont, "font", "normal",
686      "Default font for values")
687 };
688 
689 /* Class Declaration */
690 
691 static Name dialogItem_termnames[] = { NAME_label };
692 
693 ClassDecl(dialogItem_decls,
694           var_dialogItem, send_dialogItem, get_dialogItem, rc_dialogItem,
695           1, dialogItem_termnames,
696           "$Rev$");
697 
698 status
makeClassDialogItem(Class class)699 makeClassDialogItem(Class class)
700 { return declareClass(class, &dialogItem_decls);
701 }
702 
703