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