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 FontObj	getFontMenuItemMenu(Menu m, MenuItem mi);
39 static MenuItem	getItemSelectionMenu(Menu m);
40 static status	nextMenu(Menu m);
41 static status	kindMenu(Menu m, Name kind);
42 static status	ensureSingleSelectionMenu(Menu m);
43 static status	multipleSelectionMenu(Menu m, BoolObj val);
44 static status	restoreMenu(Menu m);
45 static status	compute_popup_indicator(Menu m, MenuItem mi, int *w, int *h);
46 static status	modifiedMenu(Menu m, BoolObj val);
47 static MenuItem getMemberMenu(Menu m, Any obj);
48 
49 #define CYCLE_DROP_WIDTH 14
50 #define CYCLE_DROP_HEIGHT 14
51 #define CYCLE_TRIANGLE_WIDTH 9
52 #define CYCLE_TRIANGLE_HEIGHT 8
53 #define CYCLE_DROP_DISTANCE 5
54 
55 #define MARK_DIAMOND_SIZE 14
56 #define MARK_BOX_SIZE     13
57 #define MARK_CIRCLE_SIZE  8
58 
59 status
initialiseMenu(Menu m,Name name,Name kind,Code msg)60 initialiseMenu(Menu m, Name name, Name kind, Code msg)
61 { createDialogItem(m, name);
62 
63   assign(m, message,		msg);
64   assign(m, members,	        newObject(ClassChain, EAV));
65   assign(m, multiple_selection, OFF);
66 
67   assign(m, preview,		NIL);
68 
69   assign(m, kind,		kind);
70   assign(m, columns,            ONE);
71 
72   assign(m, left_offset,	ZERO);
73   assign(m, right_offset,	ZERO);
74   assign(m, label_area,		NIL);
75   assign(m, item_offset,	newObject(ClassPoint, EAV));
76   assign(m, item_size,		newObject(ClassSize, EAV));
77   obtainClassVariablesObject(m);
78 
79   kindMenu(m, kind);
80 
81   return requestComputeGraphical(m, NAME_assignAccelerators);
82 }
83 
84 
85 static status
unlinkMenu(Menu m)86 unlinkMenu(Menu m)
87 { Cell cell;
88 
89   for_cell(cell, m->members)
90   { MenuItem mi = cell->value;
91 
92     assign(mi, menu, NIL);
93   }
94   clearChain(m->members);
95 
96   return unlinkDialogItem((DialogItem) m);
97 }
98 
99 
100 		/********************************
101 		*          COMPUTING		*
102 		********************************/
103 
104 static void
rows_and_cols(Menu m,int * rows,int * cols)105 rows_and_cols(Menu m, int *rows, int *cols)
106 { int size = valInt(getSizeChain(m->members));
107 
108   *cols = valInt(m->columns);
109   *cols = min(*cols, size);
110   *rows = (*cols == 0 ? 0 : (size + *cols - 1) / *cols);
111 
112   DEBUG(NAME_columns, Cprintf("%d rows; %d cols\n", *rows, *cols));
113 }
114 
115 
116 static int
x_gap(Menu m)117 x_gap(Menu m)
118 { int iw = valInt(m->item_size->w);
119   int gx = valInt(m->gap->w);
120   int vw = valInt(m->value_width);
121   int gap = iw + gx > vw ? gx : vw - iw;
122 
123   return gap == 0 ? -valInt(m->pen) : gap;
124 }
125 
126 
127 static int
y_gap(Menu m)128 y_gap(Menu m)
129 { int gap = valInt(m->gap->h);
130 
131   return gap == 0 ? -valInt(m->pen) : gap;
132 }
133 
134 
135 void
area_menu_item(Menu m,MenuItem mi,int * x,int * y,int * w,int * h)136 area_menu_item(Menu m, MenuItem mi, int *x, int *y, int *w, int *h)
137 { *w = valInt(m->item_size->w);
138   *h = valInt(m->item_size->h);
139 
140   *x = valInt(m->item_offset->x) + valInt(m->margin);
141   *y = valInt(m->item_offset->y);
142 
143   if ( m->feedback != NAME_showSelectionOnly )
144   { int index = valInt(getIndexChain(m->members, mi)) - 1; /* 0-based */
145     int rows, cols;
146     int gx = x_gap(m);
147     int gy = y_gap(m);
148 
149     *w += gx;				/* when pen != 0 or box feedback */
150     *h += gy;
151 
152     rows_and_cols(m, &rows, &cols);
153     if ( m->layout == NAME_horizontal )
154     { *x += (index % rows) * (*w);
155       *y += (index / rows) * (*h);
156     } else
157     { *x += (index / rows) * (*w);
158       *y += (index % rows) * (*h);
159     }
160   }
161 }
162 
163 
164 static status
computeLabelMenu(Menu m)165 computeLabelMenu(Menu m)
166 { int iox;				/* item_offset <- x */
167   int ioy;				/* item_offset <- y */
168 
169   obtainClassVariablesObject(m);
170   if ( isDefault(m->show_label) )
171     assign(m, show_label, getClassVariableValueObject(m, NAME_showLabel));
172 
173   if ( m->show_label == ON )
174   { int w, h;
175 
176     if ( isNil(m->label_area) )
177       assign(m, label_area, newObject(ClassArea, EAV));
178 
179     dia_label_size(m, &w, &h, NULL);
180     if ( m->layout == NAME_horizontal )
181       w += valInt(getExFont(m->label_font));
182     setArea(m->label_area, DEFAULT, DEFAULT, toInt(w), toInt(h));
183 
184     if ( m->layout == NAME_vertical )
185     { iox = 0;
186       ioy = h;
187     } else
188     { iox = w;
189       ioy = 0;
190     }
191   } else
192   { assign(m, label_area, NIL);
193     iox = ioy = 0;
194   }
195 
196   if ( notDefault(m->label_width) &&
197        m->layout == NAME_horizontal &&
198        valInt(m->label_width) > iox )
199     iox = valInt(m->label_width);
200 
201   if ( m->feedback == NAME_showSelectionOnly )
202   { Any ci = getClassVariableValueObject(m, NAME_cycleIndicator);
203 
204     if ( (Name)ci == NAME_comboBox )
205     { iox += 0;
206     } else if ( instanceOfObject(ci, ClassElevation) )
207     { iox += CYCLE_DROP_WIDTH + CYCLE_DROP_DISTANCE;
208     } else /* if ( instanceOfObject(ci, ClassImage) ) */
209     { Image i = ci;
210       iox += valInt(i->size->w) + CYCLE_DROP_DISTANCE;
211     }
212   }
213 
214   assign(m->item_offset, x, toInt(iox));
215   assign(m->item_offset, y, toInt(ioy));
216 
217   succeed;
218 }
219 
220 
221 static void
size_menu_item(Menu m,MenuItem mi,int * w,int * h)222 size_menu_item(Menu m, MenuItem mi, int *w, int *h)
223 { if ( instanceOfObject(mi->label, ClassImage) )
224   { Image image = (Image) mi->label;
225 
226     *w = valInt(image->size->w);
227     *h = valInt(image->size->h);
228   } else if ( isName(mi->label) )
229   { FontObj f = getFontMenuItemMenu(m, mi);
230     int fw = valInt(getExFont(f));
231 
232     str_size(&((CharArray)mi->label)->data, f, w, h);
233     *w += fw;
234   } else
235     *w = *h = 0;
236 }
237 
238 
239 static status
computeItemsMenu(Menu m)240 computeItemsMenu(Menu m)
241 { int w = 0, h = 0, iw, ih;
242   int rm = 0, lm = 0;
243   Cell cell;
244   int border = valInt(m->border);
245   int popup = (instanceOfObject(m, ClassPopup) ? 0 : 1);
246 
247   for_cell(cell, m->members)
248   { MenuItem mi = cell->value;
249 
250     size_menu_item(m, mi, &iw, &ih);
251     w = max(w, iw);
252     h = max(h, ih);
253 
254     if ( notNil(mi->popup) && !popup++ )
255     { int iw, ih;
256 
257       compute_popup_indicator(m, mi, &iw, &ih);
258       rm = max(rm, iw+border);
259     }
260   }
261 
262   w += 2 * border;
263   h += 2 * border;
264 
265   if ( m->feedback == NAME_showSelectionOnly )
266   { Image ci = getClassVariableValueObject(m, NAME_cycleIndicator);
267 
268     if ( (Name)ci == NAME_comboBox )
269       rm = ws_combo_box_width()+2;	/* 2 for the margin */
270   } else
271   { if ( notNil(m->on_image) || notNil(m->off_image) )
272     { int cw, ch;
273 
274       if ( instanceOfObject(m->on_image, ClassImage) )
275 	lm = valInt(m->on_image->size->w);
276       else if ( (Name)m->on_image == NAME_marked )
277       { ws_checkbox_size(0, &cw, &ch);
278 	lm = cw;
279       }
280 
281       if ( instanceOfObject(m->off_image, ClassImage) )
282 	lm = max(lm, valInt(m->off_image->size->w));
283       else if ( (Name)m->off_image == NAME_marked )
284       { ws_checkbox_size(0, &cw, &ch);
285 	lm = max(lm, cw);
286       }
287 
288       lm += 5;				/* TBD: Parameter? */
289     }
290   }
291 
292   if ( isDefault(m->accelerator_font) )
293     assign(m, accelerator_font,
294 	   getClassVariableValueObject(m, NAME_acceleratorFont));
295 
296   if ( notNil(m->accelerator_font) )
297   { int am = 0;
298     FontObj f = m->accelerator_font;
299 
300     for_cell(cell, m->members)
301     { MenuItem mi = cell->value;
302       int aw, ah;
303 
304       if ( notNil(mi->accelerator) )
305       { str_size(&mi->accelerator->data, f, &aw, &ah);
306 	am = max(am, aw);
307       }
308     }
309 
310     rm += am + valInt(getExFont(f));
311   }
312 
313   w += lm + rm;
314 
315   assign(m->item_size, w, toInt(w));
316   assign(m->item_size, h, toInt(h));
317   assign(m, right_offset, toInt(rm));
318   assign(m, left_offset, toInt(lm));
319 
320   succeed;
321 }
322 
323 
324 static status
computeMenu(Menu m)325 computeMenu(Menu m)
326 { if ( notNil(m->request_compute) )
327   { int x, y, w, h;
328     int ix, iy, iw, ih;
329     int aw, ah;
330 
331     if ( m->request_compute == NAME_assignAccelerators )
332       send(m, NAME_assignAccelerators, EAV);
333     if ( m->multiple_selection == OFF )
334       ensureSingleSelectionMenu(m);
335 
336     computeLabelMenu(m);
337     computeItemsMenu(m);
338 
339     if ( m->show_label == ON )
340     { Area a = m->label_area;
341 
342       if ( m->layout == NAME_horizontal )
343       { if ( valInt(m->item_size->h) > valInt(a->h) )
344 	  assign(a, h, m->item_size->h);
345       }
346 
347       x = valInt(a->x); y = valInt(a->y);
348       w = valInt(a->w); h = valInt(a->h);
349     } else
350       x = y = w = h = 0;
351 
352     ix = valInt(m->item_offset->x);
353     iy = valInt(m->item_offset->y);
354 
355     if ( m->feedback == NAME_showSelectionOnly )
356     { iw = valInt(m->item_size->w);
357       ih = valInt(m->item_size->h);
358       iw = max(iw, valInt(m->value_width));
359     } else
360     { int rows, cols;
361       int pen = valInt(m->pen);
362 
363       rows_and_cols(m, &rows, &cols);
364       if ( m->layout == NAME_horizontal )
365       { iw = rows * (valInt(m->item_size->w) + x_gap(m)) + pen;
366 	ih = cols * (valInt(m->item_size->h) + y_gap(m)) + pen;
367       } else
368       { iw = cols * (valInt(m->item_size->w) + x_gap(m)) + pen;
369 	ih = rows * (valInt(m->item_size->h) + y_gap(m)) + pen;
370       }
371     }
372 
373     iw += 2*valInt(m->margin);
374 
375     aw = max(x+w, ix+iw);
376     ah = max(y+h, iy+ih);
377 
378     CHANGING_GRAPHICAL(m,
379 	assign(m->area, w, toInt(aw));
380 	assign(m->area, h, toInt(ah));
381 	changedEntireImageGraphical(m));
382 
383     assign(m, request_compute, NIL);
384   }
385 
386   succeed;
387 }
388 
389 
390 static Point
getReferenceMenu(Menu m)391 getReferenceMenu(Menu m)
392 { Point ref;
393 
394   if ( !(ref = getReferenceDialogItem(m)) )
395   { int ry;
396 
397     ComputeGraphical(m);
398 
399     if ( m->show_label == ON )
400     { int h, fh, ascent;
401 
402       h      = valInt(m->label_area->h);
403       fh     = valInt(getHeightFont(m->label_font));
404       ascent = valInt(getAscentFont(m->label_font));
405       ry     = (h-fh)/2 + ascent;
406     } else
407     { MenuItem mi = getHeadChain(m->members);
408 
409       if ( mi && instanceOfObject(mi->label, ClassCharArray) )
410       { FontObj f = getFontMenuItemMenu(m, mi);
411 	int vw, vh;
412 	int vy;
413 	int ih = valInt(m->item_size->h);
414 
415 	str_size(&((Name) mi->label)->data, f, &vw, &vh);
416 	vy = (m->vertical_format == NAME_top    ? 0 :
417 	      m->vertical_format == NAME_center ? (ih-vh)/2 :
418 					          ih - vh);
419         ry = vy + valInt(getAscentFont(f));
420       } else
421 	ry = valInt(m->item_offset->y) + valInt(m->item_size->h);
422     }
423 
424     ref = answerObject(ClassPoint, ZERO, toInt(ry), EAV);
425   }
426 
427   answer(ref);
428 }
429 
430 
431 		/********************************
432 		*           CHANGES		*
433 		********************************/
434 
435 static status
ChangedItemMenu(Menu m,MenuItem mi)436 ChangedItemMenu(Menu m, MenuItem mi)
437 { int x, y, w, h;
438 
439   computeMenu(m);
440   area_menu_item(m, mi, &x, &y, &w, &h);
441   return changedImageGraphical(m, toInt(x), toInt(y), toInt(w), toInt(h));
442 }
443 
444 		/********************************
445 		*            REDRAW		*
446 		********************************/
447 
448 static void
draw_cycle_blob(int x,int y,Elevation z,int up)449 draw_cycle_blob(int x, int y, Elevation z, int up)
450 { int w = CYCLE_DROP_WIDTH;
451   int h = CYCLE_DROP_HEIGHT;
452   int tw = CYCLE_TRIANGLE_WIDTH;
453   int th = CYCLE_TRIANGLE_HEIGHT;
454   int tx = x + (w - tw)/2;
455   int ty = y + (h - th)/2;
456 
457   r_3d_box(x, y, w, h, 0, z, up);
458   r_3d_triangle(tx + tw/2, ty+th, tx, ty, tx+tw, ty, z, up, 0x3);
459 }
460 
461 
462 static int
item_mark_y(Menu m,int y,int h,int mh)463 item_mark_y(Menu m, int y, int h, int mh)
464 { return (m->vertical_format == NAME_top    ? y :
465 	  m->vertical_format == NAME_center ? y + (h-mh)/2 :
466 					      y + (h-mh));
467 }
468 
469 
470 static status
compute_popup_indicator(Menu m,MenuItem mi,int * w,int * h)471 compute_popup_indicator(Menu m, MenuItem mi, int *w, int *h)
472 { if ( notNil(mi->popup) )
473   { if ( notNil(m->popup_image) )
474     { Image pi = m->popup_image;
475       *w = valInt(pi->size->w);
476       *h = valInt(pi->size->h);
477     } else
478     { *w = 8;
479       *h = 7;
480     }
481 
482     succeed;
483   }
484 
485   *w = *h = 0;
486 
487   fail;
488 }
489 
490 
491 static void
draw_popup_indicator(Menu m,MenuItem mi,int x,int y,int w,int h,int b)492 draw_popup_indicator(Menu m, MenuItem mi, int x, int y, int w, int h, int b)
493 { Elevation z;
494   int iw, ih, ix, iy;
495 
496   if ( !instanceOfObject(m, ClassPopup) )
497     return;
498 
499   compute_popup_indicator(m, mi, &iw, &ih);
500   iy = (m->vertical_format == NAME_top    ? y :
501 	m->vertical_format == NAME_center ? y + (h-ih)/2 :
502 					    y + h - ih);
503   ix = x+w-b-iw;
504 
505   if ( notNil(m->popup_image) )
506   { r_image(m->popup_image, 0, 0, ix, iy, iw, ih, ON);
507   } else if ( (z = getClassVariableValueObject(m, NAME_elevation)) )
508   { r_3d_triangle(ix, iy+ih, ix, iy, ix+iw, iy+ih/2,
509 		  z, m->preview != mi, 0x3);
510   }
511 }
512 
513 
514 static inline status
elevated_items(Menu m,Elevation z)515 elevated_items(Menu m, Elevation z)
516 { if ( m->feedback == NAME_showSelectionOnly )
517     fail;
518 
519   if ( instanceOfObject(z, ClassElevation) )
520   { if ( m->kind == NAME_choice )
521       succeed;
522 
523     if ( m->look == NAME_openLook )
524     { if ( m->feedback == NAME_image )
525 	fail;
526       succeed;
527     }
528 
529     if ( m->look == NAME_motif )
530       return instanceOfObject(m, ClassPopup);
531 
532     if ( m->look == NAME_win)
533       return (m->preview_feedback != NAME_colour &&
534 	      instanceOfObject(m, ClassPopup));
535   }
536 
537   fail;
538 }
539 
540 
541 static status
RedrawMenuItem(Menu m,MenuItem mi,int x,int y,int w,int h,Elevation iz)542 RedrawMenuItem(Menu m, MenuItem mi, int x, int y, int w, int h, Elevation iz)
543 { int b = valInt(m->border);
544   int lm = valInt(m->left_offset);
545   int rm = valInt(m->right_offset);
546   Image leftmark = NIL;
547   int pen = valInt(m->pen);
548   int ix, iy, iw, ih;
549   int radius = 0;
550   Image fill = NIL;
551   Any colour = mi->colour;
552   Elevation z = iz;
553   int lblflags = (mi->active == ON && m->active == ON ? 0 : LABEL_INACTIVE);
554   int flags = 0;
555 
556   DEBUG(NAME_menu, Cprintf("Redraw %s at %d %d %d %d\n",
557 			   pp(mi->value), x, y, w, h));
558 
559   if ( mi->active == OFF )
560   { Any c2 = getClassVariableValueObject(m, NAME_inactiveColour);
561 
562     if ( c2 && notNil(c2) )
563       colour = c2;
564 
565     r_colour(colour);
566   }
567 
568 					/* Windows '95 popup */
569   if ( m->preview_feedback == NAME_colour && m->preview == mi )
570   { Any fg = getClassVariableValueObject(m, NAME_selectedForeground);
571     Any bg = getClassVariableValueObject(m, NAME_selectedBackground);
572     Elevation mz = getClassVariableValueObject(m, NAME_elevation);
573     int bw = (mz && notNil(mz) ? valInt(mz->height) : 0);
574     int bh = valInt(m->border);
575 
576     r_fill(x, y+bh/2, w-2*bw, h-bh, bg);
577     colour = fg;
578     r_colour(fg);
579   }
580   if ( mi->end_group == ON && m->look == NAME_win )
581   { Elevation mz = getClassVariableValueObject(m, NAME_elevation);
582 
583     if ( m->layout == NAME_vertical )
584       r_3d_line(x, y+h, x+w, y+h, mz, FALSE);
585     else
586       r_3d_line(x+w, y, x+w, y+h, mz, FALSE);
587   }
588 
589   if ( mi->selected == ON && notNil(m->on_image) )
590     leftmark = m->on_image;
591   else if ( mi->selected == OFF && notNil(m->off_image) )
592     leftmark = m->off_image;
593 
594   if ( elevated_items(m, z) )
595   { int up = TRUE;
596 
597     if ( m->preview == mi )
598     { z = getClassVariableValueObject(m, NAME_previewElevation);
599     } else if ( mi->selected == ON )
600       up = FALSE;
601 
602     if ( !ws_draw_button_face((DialogItem)m, x, y, w, h, up, FALSE, FALSE) )
603       r_3d_box(x, y, w, h, 0, z, up);
604 
605     if ( mi->end_group == ON )
606     { Elevation mz = getClassVariableValueObject(m, NAME_elevation);
607 
608       if ( m->layout == NAME_vertical )
609 	r_3d_line(x, y+h, x+w, y+h, mz, FALSE);
610       else
611 	r_3d_line(x+w, y, x+w, y+h, mz, FALSE);
612     }
613 
614     if ( notNil(mi->popup) )
615       draw_popup_indicator(m, mi, x, y, w, h, b);
616   } else
617   { if ( (mi->selected == ON && m->feedback == NAME_box) )
618       pen++;
619     else if ( m->preview == mi )
620     { if ( m->preview_feedback == NAME_box )
621 	pen++;
622       else if ( m->preview_feedback == NAME_roundedBox )
623       { radius = 10;
624 	pen++;
625       } else if ( m->preview_feedback == NAME_invertedRoundedBox )
626       { fill = BLACK_IMAGE;
627 	radius = 10;
628       }
629 
630       DEBUG(NAME_menu, Cprintf("Feedback = %s, p = %d; r = %d, fill = %s\n",
631 			       pp(m->preview_feedback),
632 			       pen, radius, pp(fill)));
633     }
634 
635     if ( mi->end_group == ON && m->look != NAME_win )
636     { r_thickness(pen+1);
637       r_dash(m->texture);
638       if ( m->layout == NAME_vertical )
639 	r_line(x, y+h, x+w, y+h);
640       else
641 	r_line(x+w, y, x+w, y+h);
642     }
643 
644     if ( pen != 0 || notNil(fill) )
645     { r_thickness(pen);
646       r_dash(m->texture);
647       r_box(x, y, w, h, radius, fill);
648       if ( notNil(fill) )
649 	r_swap_background_and_foreground();
650     }
651 
652     if ( notNil(mi->popup) )
653       draw_popup_indicator(m, mi, x, y, w, h, b);
654   }
655 
656   if ( mi->selected == ON )
657     flags |= CHECKBOX_SELECTED;
658   if ( mi->active == ON && m->active == ON )
659     flags |= CHECKBOX_ACTIVE;
660   if ( m->multiple_selection == ON )
661     flags |= CHECKBOX_MULTIPLE;
662 
663   if ( ((mi->selected == ON  && (Name)m->on_image == NAME_marked ) ||
664 	(mi->selected == OFF && (Name)m->off_image == NAME_marked)) )
665   { ws_draw_checkbox(x, y, w, h, b, flags);
666   } else
667   { if ( instanceOfObject(leftmark, ClassImage) )
668     { int bw, bh, by;
669       Elevation mz = getClassVariableValueObject(m, NAME_markElevation);
670 
671       bw = valInt(leftmark->size->w);
672       bh = valInt(leftmark->size->h);
673       by = item_mark_y(m, y, h, bh);
674 
675       if ( instanceOfObject(mz, ClassElevation) && mz->height != ZERO )
676       { int h = valInt(mz->height);
677 	r_3d_box(x+b-h, by-h, bw+2*h, bh+2*h, 0, mz, FALSE);
678       }
679 
680       r_image(leftmark, 0, 0, x+b, by, bw, bh, ON);
681     } else if ( (Name) leftmark == NAME_marked )
682     { if ( m->look == NAME_motif )
683       { Elevation mz = getClassVariableValueObject(m, NAME_markElevation);
684 
685 	if ( m->multiple_selection == ON )
686 	{ int dy = item_mark_y(m, y, h, MARK_BOX_SIZE);
687 
688 	  r_3d_box(x+b, dy, MARK_BOX_SIZE, MARK_BOX_SIZE,
689 		   0, mz, mi->selected == OFF);
690 	} else
691 	{ int dy = item_mark_y(m, y, h, MARK_DIAMOND_SIZE);
692 
693 	  r_3d_diamond(x+b, dy, MARK_DIAMOND_SIZE, MARK_DIAMOND_SIZE,
694 		       mz, mi->selected == OFF);
695 	}
696       } else if ( m->look == NAME_win )
697       { if ( m->multiple_selection == OFF )
698 	{ int d = MARK_CIRCLE_SIZE;
699 	  int zh = valInt(z->height);
700 	  int dy = item_mark_y(m, y, h, d);
701 	  int dx = x+b+lm - (MARK_CIRCLE_SIZE+5);
702 	  int mw = 3;			/* mark-width */
703 
704 	  r_3d_ellipse(dx-zh, dy-zh, d+2*zh, d+2*zh, z, FALSE);
705 	  r_thickness(0);
706 	  r_ellipse(dx, dy, d, d, WHITE_COLOUR);
707 	  if ( mi->selected == ON )
708 	    r_fill(dx+(d-mw)/2, dy+(d-mw)/2, mw, mw, BLACK_COLOUR);
709 	}
710       }
711     }
712   }
713 
714   if ( notNil(m->accelerator_font) && notNil(mi->accelerator) )
715   { FontObj f = m->accelerator_font;
716     int fw = valInt(getExFont(f));
717 
718     str_label(&mi->accelerator->data,
719 	      0,
720 	      f,
721 	      x, y, w-fw/2, h,
722 	      NAME_right, m->vertical_format,
723 	      lblflags);
724   }
725 
726   ix = x + lm;
727   iw = w - (lm + rm);
728   iy = y;
729   ih = h;
730 
731   if ( notDefault(colour) )
732     r_colour(colour);
733   if ( notDefault(mi->background) )
734   { int m;
735 
736     if ( instanceOfObject(z, ClassElevation) )
737     { m = labs(valInt(z->height));
738     } else
739       m = 0;
740 
741     r_fill(ix+m, iy+m, iw-2*m, ih-2*m, mi->background);
742   }
743 
744   ix += b;
745   iy += b;
746   iw -= 2*b;
747   ih -= 2*b;
748 
749   if ( instanceOfObject(mi->label, ClassCharArray) )
750   { FontObj f = getFontMenuItemMenu(m, mi);
751     int fw = valInt(getExFont(f));
752 
753     str_label(&((Name) mi->label)->data,
754 	      accelerator_code(mi->accelerator),
755 	      f,
756 	      ix+fw/2, iy, iw-fw, ih,
757 	      m->format, m->vertical_format,
758 	      lblflags);
759 
760   } else if ( instanceOfObject(mi->label, ClassImage) )
761   { int bx, by, bw, bh;
762     Image image = (Image) mi->label;
763 
764     bw = valInt(image->size->w);
765     bh = valInt(image->size->h);
766     bx = (m->format == NAME_left   ? ix :
767 	  m->format == NAME_center ? ix + (iw-bw)/2 :
768 				     ix + iw - bw);
769     by = (m->vertical_format == NAME_top    ? iy :
770 	  m->vertical_format == NAME_center ? iy + (ih-bh)/2 :
771 					      iy + ih - bh);
772 
773     r_image(mi->label, 0, 0, bx, by, bw, bh, ON);
774   }
775 
776   if ( notNil(fill) )
777     r_swap_background_and_foreground();
778 
779   if ( (mi->selected == ON && m->feedback == NAME_invert) )
780     r_complement(ix, iy, iw, ih);
781   if ( (m->preview == mi && m->preview_feedback == NAME_invert) )
782     r_complement(x, y, w, h);
783 
784   if ( notDefault(colour) )
785     r_colour(DEFAULT);
786 
787   succeed;
788 }
789 
790 
791 static status
RedrawAreaMenu(Menu m,Area a)792 RedrawAreaMenu(Menu m, Area a)
793 { int x, y, w, h;
794   int bx, by, cx, cy, iw, ih;
795   int gx = x_gap(m);
796   int gy = y_gap(m);
797   Elevation z  = getClassVariableValueObject(m, NAME_elevation);
798   Elevation iz = getClassVariableValueObject(m, NAME_itemElevation);
799 
800   initialiseDeviceGraphical(m, &x, &y, &w, &h);
801   NormaliseArea(x, y, w, h);
802 
803   if ( m->show_label == ON )
804   { int flags = (m->active == ON ? 0 : LABEL_INACTIVE);
805     int lw = (isDefault(m->label_width) ? valInt(m->label_area->w)
806 				        : valInt(m->label_width));
807     if ( m->layout == NAME_horizontal )
808       lw -= valInt(getExFont(m->label_font));
809 
810     RedrawLabelDialogItem(m,
811 			  accelerator_code(m->accelerator),
812 			  x + valInt(m->label_area->x),
813 			  y + valInt(m->label_area->y),
814 			  lw,
815 			  valInt(m->label_area->h),
816 			  m->label_format, m->vertical_format, flags);
817   }
818 
819   bx = cx = x + valInt(m->item_offset->x);
820   by = cy = y + valInt(m->item_offset->y);
821   iw = valInt(m->item_size->w);
822   ih = valInt(m->item_size->h);
823 
824   if ( m->feedback == NAME_showSelectionOnly )
825   { MenuItem mi = getItemSelectionMenu(m);
826     Any ci = getClassVariableValueObject(m, NAME_cycleIndicator);
827     int fm = 0;
828 
829     iw = max(iw, valInt(m->value_width));
830 
831     if ( (Name)ci == NAME_comboBox )
832     { int flags = TEXTFIELD_COMBO;
833 
834       if ( mi && mi->active == ON && m->active == ON )
835 	flags |= TEXTFIELD_EDITABLE;
836 
837       ws_entry_field(cx, by, iw, ih, flags);
838       /*fm = ws_entry_field_margin();*/
839     } else if ( instanceOfObject(ci, ClassElevation) )
840     { int bw = CYCLE_DROP_WIDTH;
841 
842       draw_cycle_blob(cx-(bw+CYCLE_DROP_DISTANCE), cy, ci, TRUE);
843     } else /*if ( instanceOfObject(ci, ClassImage) )*/
844     { Image i = ci;
845       int bw = valInt(i->size->w);
846       int bh = valInt(i->size->h);
847 
848       r_image(i, 0, 0, cx-(bw+CYCLE_DROP_DISTANCE), cy, bw, bh, ON);
849     }
850 
851     if ( mi != FAIL )
852       RedrawMenuItem(m, mi, cx+fm, cy+fm, iw-2*fm, ih-2*fm, iz);
853   } else
854   { int rows, cols;
855     int n = 1;
856     Cell cell;
857     int ax, ay, aw, ah;
858 
859     ax = valInt(a->x); ay = valInt(a->y);
860     aw = valInt(a->w); ah = valInt(a->h);
861     ax += x - valInt(m->area->x);
862     ay += y - valInt(m->area->y);
863     rows_and_cols(m, &rows, &cols);
864 
865     if ( z && notNil(z) )
866       r_3d_box(cx, cy, w-(cx-x), h-(cy-y), 0, z, TRUE);
867     cx += valInt(m->margin);
868 
869     if ( m->look == NAME_motif ||
870 	 m->look == NAME_win ||
871 	 (m->look == NAME_openLook && instanceOfObject(iz, ClassElevation)) )
872     { iw += gx; ih += gy;
873       gx = gy = 0;
874     } else if ( m->pen != ZERO )
875     { iw += gx + 1; ih += gy + 1;
876       gx = gy = -1;
877     }
878 
879     for_cell(cell, m->members)
880     { MenuItem mi = cell->value;
881 
882       if ( OverlapArea(ax, ay, aw, ah, cx, cy, iw, ih) )
883 	RedrawMenuItem(m, mi, cx, cy, iw, ih, iz);
884 
885       if ( m->layout == NAME_vertical )
886       { if ( rows == 1 || (n > 1 && n % rows == 0) )
887 	{ cy = by;
888 	  cx += iw+gx;
889 	} else
890 	{ cy += ih+gy;
891 	}
892       } else
893       { if ( rows == 1 || (n > 1 && n % rows == 0) )
894 	{ cx = bx;
895 	  cy += ih+gy;
896 	} else
897 	{ cx += iw+gx;
898 	}
899       }
900       n++;
901     }
902   }
903 
904   return RedrawAreaGraphical(m, a);
905 }
906 
907 
908 		/********************************
909 		*           COMPUTING		*
910 		********************************/
911 
912 static FontObj
getFontMenuItemMenu(Menu m,MenuItem mi)913 getFontMenuItemMenu(Menu m, MenuItem mi)
914 { if ( notDefault(mi->font) )
915     answer(mi->font);
916 
917   answer(m->value_font);
918 }
919 
920 
921 Int
getCenterYMenuItemMenu(Menu m,Any obj)922 getCenterYMenuItemMenu(Menu m, Any obj)
923 { MenuItem mi;
924   int x, y, w, h;
925 
926   if ( isDefault(obj) )
927   { if ( (mi = getItemSelectionMenu(m)) == FAIL )
928     { if ( emptyChain(m->members) != SUCCEED )
929       	mi = getHeadChain(m->members);
930       else
931       	return ZERO;
932     }
933   } else
934     if ( (mi = findMenuItemMenu(m, obj)) == FAIL )
935       return ZERO;
936 
937   computeMenu(m);
938   area_menu_item(m, mi, &x, &y, &w, &h);
939 
940   answer(toInt(y+h/2 + valInt(m->area->y)));
941 }
942 
943 
944 static Int
getLabelWidthMenu(Menu m)945 getLabelWidthMenu(Menu m)
946 { ComputeGraphical(m);
947 
948   if ( m->show_label == ON )
949     answer(m->label_area->w);
950   else
951     answer(ZERO);
952 }
953 
954 
955 static status
labelWidthMenu(Menu m,Int w)956 labelWidthMenu(Menu m, Int w)
957 { return assignGraphical(m, NAME_labelWidth, w);
958 }
959 
960 		/********************************
961 		*        EVENT HANDLING		*
962 		********************************/
963 
964 static status
statusMenu(Menu m,Name stat)965 statusMenu(Menu m, Name stat)
966 { assign(m, status, stat);
967 
968   succeed;
969 }
970 
971 
972 MenuItem
getItemFromEventMenu(Menu m,EventObj ev)973 getItemFromEventMenu(Menu m, EventObj ev)
974 { Int X, Y;
975   int x, y;
976   int index;
977   int rows, cols;
978 
979   rows_and_cols(m, &rows, &cols);
980 
981   ComputeGraphical(m);
982   get_xy_event(ev, m, ON, &X, &Y);
983   x = valInt(X) - valInt(m->item_offset->x);
984   y = valInt(Y) - valInt(m->item_offset->y);
985   if ( x < 0 || y < 0 )
986     fail;
987   DEBUG(NAME_event, Cprintf("event at %d,%d\n", x, y));
988 
989   x /= valInt(m->item_size->w) + x_gap(m);
990   y /= valInt(m->item_size->h) + y_gap(m);
991   DEBUG(NAME_event, Cprintf("item at %d,%d; rows = %d\n", x, y, rows));
992 
993   if ( m->layout == NAME_horizontal )
994     index = x + y * rows + 1;
995   else
996     index = y + x * rows + 1;
997 
998   return getNth1Chain(m->members, toInt(index));
999 }
1000 
1001 
1002 static status
eventMenu(Menu m,EventObj ev)1003 eventMenu(Menu m, EventObj ev)
1004 { if ( completerShownDialogItem(m) )
1005   { forwardCompletionEvent(ev);
1006     succeed;
1007   }
1008 
1009   if ( eventDialogItem(m, ev) )
1010     succeed;
1011 
1012   if ( m->active == ON )
1013   { makeButtonGesture();
1014 
1015     return eventGesture(GESTURE_button, ev);
1016   }
1017 
1018   fail;
1019 }
1020 
1021 
1022 status
forwardMenu(Menu m,Code msg,EventObj ev)1023 forwardMenu(Menu m, Code msg, EventObj ev)
1024 { MenuItem mi;
1025 
1026   TRY(mi = getItemSelectionMenu(m));
1027 
1028   if ( notDefault(mi->message) )
1029   { if ( notNil(mi->message) )
1030     { forwardReceiverCode(mi->message, m, mi->value, ev, EAV);
1031       succeed;
1032     }
1033     succeed;
1034   }
1035 
1036   if ( notNil(m->message) && notDefault(m->message) )
1037   { Any val;
1038 
1039     if ( (val = get(m, NAME_selection, EAV)) )
1040       forwardReceiverCode(m->message, m, val, ev, EAV);
1041   }
1042 
1043   succeed;
1044 }
1045 
1046 
1047 
1048 static status
selectedCompletionMenu(Menu m,DictItem di)1049 selectedCompletionMenu(Menu m, DictItem di)
1050 { selectionMenu(m, di->key);
1051   quitCompleterDialogItem(m);
1052   flushGraphical(m);
1053   if ( !send(m->device, NAME_modifiedItem, m, ON, EAV) )
1054     forwardMenu(m, m->message, EVENT->value);
1055 
1056   succeed;
1057 }
1058 
1059 
1060 static status
keyboardQuitMenu(Menu m)1061 keyboardQuitMenu(Menu m)
1062 { quitCompleterDialogItem(m);
1063 
1064   succeed;
1065 }
1066 
1067 
1068 static status
openComboBoxMenu(Menu m)1069 openComboBoxMenu(Menu m)
1070 { Browser c = CompletionBrowser();
1071   Cell cell;
1072   DictItem selection = NIL;
1073 
1074   send(c, NAME_clear, EAV);
1075   for_cell(cell, m->members)
1076   { MenuItem mi = cell->value;
1077     DictItem di;
1078 
1079     if ( mi->active == ON )
1080     { send(c, NAME_append,
1081 	   (di=newObject(ClassDictItem, mi->value, mi->label, EAV)), EAV);
1082       if ( mi->selected == ON )
1083 	selection = di;
1084     }
1085   }
1086 
1087   if ( notNil(selection) )
1088     send(c, NAME_selection, selection, EAV);
1089 
1090   selectCompletionDialogItem(m, NIL, NIL, ZERO);
1091 
1092   changedDialogItem(m);
1093 
1094   succeed;
1095 }
1096 
1097 
1098 
1099 static status
executeMenuItem(Menu m,MenuItem mi,EventObj ev)1100 executeMenuItem(Menu m, MenuItem mi, EventObj ev)
1101 { if ( m->multiple_selection == ON )
1102   { toggleMenu(m, mi);
1103     flushGraphical(m);
1104     send(m->device, NAME_modifiedItem, m, ON, EAV);
1105 
1106     if ( notDefault(mi->message) )
1107     { if ( notNil(mi->message) )
1108 	forwardReceiverCode(mi->message, m,
1109 			    mi, mi->selected, ev, EAV);
1110     } else if ( !modifiedMenu(m, ON) &&
1111 		notNil(m->message) &&
1112 		notDefault(m->message) )
1113     { forwardReceiverCode(m->message, m,
1114 			  mi->value, mi->selected, ev, EAV);
1115     }
1116   } else
1117   { selectionMenu(m, mi);
1118     flushGraphical(m);
1119     send(m->device, NAME_modifiedItem, m, ON, EAV);
1120 
1121     if ( notDefault(mi->message) )
1122     { if ( notNil(mi->message) )
1123 	forwardReceiverCode(mi->message, m,
1124 			    mi->value, ev, EAV);
1125     } else if ( !modifiedMenu(m, ON) )
1126       forwardMenu(m, m->message, ev);
1127   }
1128 
1129   succeed;
1130 }
1131 
1132 
1133 static status
executeMenu(Menu m,EventObj ev)1134 executeMenu(Menu m, EventObj ev)
1135 { MenuItem mi;
1136 
1137   if ( m->feedback == NAME_showSelectionOnly )
1138   { Any img = getClassVariableValueObject(m, NAME_cycleIndicator); /* TBD */
1139 
1140     if ( img == NAME_comboBox )
1141       return openComboBoxMenu(m);
1142     else
1143     { nextMenu(m);
1144       flushGraphical(m);
1145       if ( !send(m->device, NAME_modifiedItem, m, ON, EAV) )
1146 	forwardMenu(m, m->message, ev);
1147       succeed;
1148     }
1149   }
1150 
1151   if ( isDefault(ev) )
1152     ev = EVENT->value;			/* @event */
1153   TRY((mi = getItemFromEventMenu(m, ev)) && mi->active == ON);
1154 
1155   return executeMenuItem(m, mi, ev);
1156 }
1157 
1158 
1159 		/********************************
1160 		*           SELECTION		*
1161 		********************************/
1162 
1163 static MenuItem
getItemSelectionMenu(Menu m)1164 getItemSelectionMenu(Menu m)
1165 { if ( m->multiple_selection == OFF )
1166   { Cell cell;
1167 
1168     for_cell(cell, m->members)
1169     { MenuItem mi = cell->value;
1170 
1171       if ( mi->selected == ON )
1172 	answer(mi);
1173     }
1174   }
1175 
1176   fail;
1177 }
1178 
1179 
1180 static Any
getSelectionMenu(Menu m)1181 getSelectionMenu(Menu m)
1182 { ComputeGraphical(m);
1183 
1184   if ( m->multiple_selection == OFF )
1185   { MenuItem mi;
1186 
1187     TRY( mi = getItemSelectionMenu(m) );
1188     assign(m, selection, mi->value);
1189   } else
1190   { Chain ch = answerObject(ClassChain, EAV);
1191     Cell cell;
1192 
1193     for_cell(cell, m->members)
1194     { MenuItem mi = cell->value;
1195 
1196       if ( mi->selected == ON )
1197 	appendChain(ch, mi->value);
1198     }
1199     assign(m, selection, ch);
1200   }
1201 
1202   answer(m->selection);
1203 }
1204 
1205 
1206 MenuItem
findMenuItemMenu(Menu m,Any spec)1207 findMenuItemMenu(Menu m, Any spec)
1208 { if ( instanceOfObject(spec, ClassMenuItem) )
1209   { MenuItem mi = spec;
1210 
1211     if ( mi->menu == m )
1212       answer(mi);
1213   } else
1214   { Cell cell;
1215 
1216     for_cell(cell, m->members)
1217     { MenuItem mi = cell->value;
1218 
1219       if ( mi->value == spec )
1220 	answer(mi);
1221     }
1222 
1223     for_cell(cell, m->members)
1224       if ( hasValueMenuItem(cell->value, spec) )
1225 	answer(cell->value);
1226   }
1227 
1228   fail;
1229 }
1230 
1231 
1232 static int
index_item_menu(Menu m,Any spec)1233 index_item_menu(Menu m, Any spec)
1234 { if ( instanceOfObject(spec, ClassMenuItem) )
1235   { Cell cell; int n = 1;
1236 
1237     for_cell(cell, m->members)
1238     { if ( cell->value == spec )
1239         return n;
1240       n++;
1241     }
1242   } else
1243   { Cell cell;
1244     int n;
1245 
1246     n = 1;				/* exact match */
1247     for_cell(cell, m->members)
1248     { MenuItem mi = cell->value;
1249 
1250       if ( mi->value == spec )
1251         return n;
1252       n++;
1253     }
1254 
1255     n = 1;				/* equal string representation */
1256     for_cell(cell, m->members)
1257     { if ( hasValueMenuItem(cell->value, spec) )
1258         return n;
1259       n++;
1260     }
1261   }
1262 
1263   fail;
1264 }
1265 
1266 
1267 status
previewMenu(Menu m,MenuItem mi)1268 previewMenu(Menu m, MenuItem mi)
1269 { if ( !mi )
1270     mi = NIL;
1271 
1272   if ( m->preview != mi )
1273   { if ( notNil(m->preview) )
1274       ChangedItemMenu(m, m->preview);
1275     assign(m, preview, mi);
1276     if ( notNil(m->preview) )
1277       ChangedItemMenu(m, m->preview);
1278   }
1279 
1280   succeed;
1281 }
1282 
1283 
1284 static status
clearSelectionMenu(Menu m)1285 clearSelectionMenu(Menu m)
1286 { return selectionMenu(m, FAIL);	/* not very nice! */
1287 }
1288 
1289 
1290 #define IS_SET 1
1291 #define NEEDS_SET 2
1292 
1293 status
selectionMenu(Menu m,Any selection)1294 selectionMenu(Menu m, Any selection)
1295 { Cell cell;
1296   char *is_set;
1297   int size = valInt(m->members->size);
1298   int n, do_free;
1299 
1300   DEBUG(NAME_popup, Cprintf("selectionMenu(%s, %s)\n", pp(m), pp(selection)));
1301 
1302   if ( !(is_set = alloca((size+1)*sizeof(char))) )
1303   { is_set = pceMalloc((size+1)*sizeof(char));
1304     do_free = TRUE;
1305   } else
1306     do_free = FALSE;
1307 
1308   is_set[0] = 0;
1309   n = 1;
1310   for_cell(cell, m->members)
1311   { MenuItem mi = cell->value;
1312 
1313     is_set[n] = (mi->selected == ON ? IS_SET : 0);
1314     n++;
1315   }
1316 
1317   if ( selection )
1318   { if ( instanceOfObject(selection, ClassChain) )
1319     { for_cell(cell, (Chain)selection)
1320 	is_set[index_item_menu(m, cell->value)] |= NEEDS_SET;
1321     } else
1322       is_set[index_item_menu(m, selection)] |= NEEDS_SET;
1323 
1324     assign(m, selection, selection);
1325   }
1326 
1327   n = 1;
1328   for_cell(cell, m->members)
1329   { MenuItem mi = cell->value;
1330 
1331     if ( is_set[n] & NEEDS_SET )
1332     { if ( !(is_set[n] & IS_SET) )
1333       { assign(mi, selected, ON);
1334       }
1335     } else
1336     { if ( is_set[n] & IS_SET )
1337       { assign(mi, selected, OFF);
1338       }
1339     }
1340     n++;
1341   }
1342 
1343 					/* 2nd pass to avoid a call-back */
1344 					/* through ensureSingleSelectionMenu */
1345   n = 1;
1346   for_cell(cell, m->members)
1347   { MenuItem mi = cell->value;
1348 
1349     if ( is_set[n] & NEEDS_SET )
1350     { if ( !(is_set[n] & IS_SET) )
1351       { ChangedItemMenu(m, mi);
1352       }
1353     } else
1354     { if ( is_set[n] & IS_SET )
1355       { ChangedItemMenu(m, mi);
1356       }
1357     }
1358     n++;
1359   }
1360 
1361   if ( do_free )
1362     pceFree(is_set);
1363 
1364   succeed;
1365 }
1366 
1367 
1368 static status
selectedMenu(Menu m,MenuItem mi,BoolObj val)1369 selectedMenu(Menu m, MenuItem mi, BoolObj val)
1370 { if ( mi->selected != val )
1371   { assign(mi, selected, val);
1372     ChangedItemMenu(m, mi);
1373   }
1374 
1375   succeed;
1376 }
1377 
1378 
1379 static BoolObj
getSelectedMenu(Menu m,MenuItem mi)1380 getSelectedMenu(Menu m, MenuItem mi)
1381 { answer(mi->selected);
1382 }
1383 
1384 
1385 status
toggleMenu(Menu m,MenuItem mi)1386 toggleMenu(Menu m, MenuItem mi)
1387 { CHANGING_GRAPHICAL(m,
1388 	assign(mi, selected, mi->selected == ON ? OFF : ON);
1389 	ChangedItemMenu(m, mi));
1390 
1391   succeed;
1392 }
1393 
1394 
1395 static status
nextMenu(Menu m)1396 nextMenu(Menu m)
1397 { Cell cell;
1398   MenuItem current = NIL;
1399   MenuItem next = NIL;
1400   int skipping = TRUE;
1401 
1402   for_cell(cell, m->members)
1403   { MenuItem mi = cell->value;
1404 
1405     if ( skipping )
1406     { if ( mi->active == ON && isNil(next) )
1407         next = mi;
1408       if ( mi->selected == ON )
1409       { skipping = FALSE;
1410         current = mi;
1411       }
1412     } else if ( mi->active == ON )
1413     { next = mi;
1414       break;
1415     }
1416   }
1417 
1418   if ( current != next )
1419     selectionMenu(m, next);
1420 
1421   succeed;
1422 }
1423 
1424 
1425 
1426 		/********************************
1427 		*            MEMBERS		*
1428 		********************************/
1429 
1430 static status
append_menu(Menu m,MenuItem mi,Any where)1431 append_menu(Menu m, MenuItem mi, Any where)
1432 { if ( notNil(mi->menu) )
1433     return errorPce(mi, NAME_alreadyShown, mi, mi->menu);
1434 
1435   if ( where == NAME_head )
1436     prependChain(m->members, mi);
1437   else if ( where == NAME_tail )
1438     appendChain(m->members, mi);
1439   else
1440     insertBeforeChain(m->members, mi, where);
1441 
1442   assign(mi, menu, m);
1443 
1444   return requestComputeGraphical(m, NAME_assignAccelerators);
1445 }
1446 
1447 
1448 status
appendMenu(Menu m,Any mi)1449 appendMenu(Menu m, Any mi)
1450 { return append_menu(m, mi, NAME_tail);
1451 }
1452 
1453 
1454 static status
prependMenu(Menu m,Any mi)1455 prependMenu(Menu m, Any mi)
1456 { return append_menu(m, mi, NAME_head);
1457 }
1458 
1459 
1460 static status
insertBeforeMenu(Menu m,MenuItem mi,MenuItem mi2)1461 insertBeforeMenu(Menu m, MenuItem mi, MenuItem mi2)
1462 { if ( isName(mi2) )
1463   { if ( !(mi2 = getMemberMenu(m, mi2)) )
1464       mi2 = NIL;
1465   }
1466 
1467   return append_menu(m, mi, mi2);
1468 }
1469 
1470 
1471 status
deleteMenu(Menu m,Any obj)1472 deleteMenu(Menu m, Any obj)
1473 { MenuItem mi;
1474 
1475   TRY( mi = findMenuItemMenu(m, obj) );
1476 
1477   assign(mi, menu, NIL);
1478   deleteChain(m->members, mi);
1479   return requestComputeGraphical(m, NAME_assignAccelerators);
1480 }
1481 
1482 
1483 static status
clearMenu(Menu m)1484 clearMenu(Menu m)
1485 { Cell cell;
1486 
1487   GcProtect(m,
1488 	    { for_cell(cell, m->members)
1489 	      { MenuItem mi = cell->value;
1490 
1491 		assign(mi, menu, NIL);
1492 	      }
1493 	      clearChain(m->members);
1494 	    });
1495 
1496   return requestComputeGraphical(m, DEFAULT);
1497 }
1498 
1499 
1500 static status
membersMenu(Menu m,Chain members)1501 membersMenu(Menu m, Chain members)
1502 { Any val;
1503 
1504   send(m, NAME_clear, EAV);
1505   for_chain(members, val, TRY(send(m, NAME_append, val, EAV)));
1506 
1507   succeed;
1508 }
1509 
1510 
1511 
1512 static status
memberMenu(Menu m,Any obj)1513 memberMenu(Menu m, Any obj)
1514 { TRY( findMenuItemMenu(m, obj) );
1515 
1516   succeed;
1517 }
1518 
1519 
1520 static MenuItem
getMemberMenu(Menu m,Any obj)1521 getMemberMenu(Menu m, Any obj)
1522 { MenuItem mi;
1523 
1524   TRY( mi = findMenuItemMenu(m, obj) );
1525 
1526   answer(mi);
1527 }
1528 
1529 
1530 		/********************************
1531 		*           ACTIVATE		*
1532 		********************************/
1533 
1534 status
updateMenu(Menu m,Any context)1535 updateMenu(Menu m, Any context)
1536 { Cell cell;
1537   int changed = FALSE;
1538 
1539   for_cell(cell, m->members)
1540   { MenuItem mi = cell->value;
1541 
1542     if ( notNil(mi->condition) )
1543     { BoolObj a = (forwardReceiverCode(mi->condition, mi, context, EAV) ? ON : OFF);
1544 
1545       if ( a != mi->active )
1546       { changed = TRUE;
1547 	qadSendv(mi, NAME_active, 1, (Any*) &a);
1548       }
1549     }
1550   }
1551 
1552   if ( changed )
1553     CHANGING_GRAPHICAL(m, changedEntireImageGraphical(m));
1554 
1555   succeed;
1556 }
1557 
1558 
1559 static status
activeItemMenu(Menu m,Any obj,BoolObj val)1560 activeItemMenu(Menu m, Any obj, BoolObj val)
1561 { MenuItem mi;
1562 
1563   TRY( mi = findMenuItemMenu(m, obj) );
1564 
1565   CHANGING_GRAPHICAL(m,
1566 	assign(mi, active, val);
1567 	changedEntireImageGraphical(m));
1568 
1569   succeed;
1570 }
1571 
1572 
1573 static status
onMenu(Menu m,Any obj)1574 onMenu(Menu m, Any obj)
1575 { return activeItemMenu(m, obj, ON);
1576 }
1577 
1578 
1579 static status
offMenu(Menu m,Any obj)1580 offMenu(Menu m, Any obj)
1581 { return activeItemMenu(m, obj, OFF);
1582 }
1583 
1584 
1585 static BoolObj
getActiveItemMenu(Menu m,Any obj)1586 getActiveItemMenu(Menu m, Any obj)
1587 { MenuItem mi;
1588 
1589   TRY( mi = findMenuItemMenu(m, obj) );
1590 
1591   answer(mi->active);
1592 }
1593 
1594 
1595 static status
isOnMenu(Menu m,Any obj)1596 isOnMenu(Menu m, Any obj)
1597 { if ( getActiveItemMenu(m, obj) == ON )
1598     succeed;
1599 
1600   fail;
1601 }
1602 
1603 
1604 static status
isOffMenu(Menu m,Any obj)1605 isOffMenu(Menu m, Any obj)
1606 { if ( getActiveItemMenu(m, obj) == OFF )
1607     succeed;
1608 
1609   fail;
1610 }
1611 
1612 
1613 static status
activeAllItemsMenu(Menu m,BoolObj val)1614 activeAllItemsMenu(Menu m, BoolObj val)
1615 { Cell cell;
1616 
1617   for_cell(cell, m->members)
1618   { MenuItem mi = cell->value;
1619 
1620     assign(mi, active, val);
1621   }
1622 
1623   CHANGING_GRAPHICAL(m,
1624 	changedEntireImageGraphical(m));
1625 
1626   succeed;
1627 }
1628 
1629 
1630 static status
allOnMenu(Menu m)1631 allOnMenu(Menu m)
1632 { return activeAllItemsMenu(m, ON);
1633 }
1634 
1635 
1636 static status
allOffMenu(Menu m)1637 allOffMenu(Menu m)
1638 { return activeAllItemsMenu(m, OFF);
1639 }
1640 
1641 		/********************************
1642 		*            KIND		*
1643 		********************************/
1644 
1645 static status
kindMenu(Menu m,Name kind)1646 kindMenu(Menu m, Name kind)
1647 { if ( m->look == NAME_openLook || m->look == NAME_gtk )
1648   { if ( kind == NAME_choice || kind == NAME_toggle )
1649     { assign(m, on_image, NIL);
1650       assign(m, off_image, NIL);
1651       assign(m, feedback, NAME_box);
1652       assign(m, pen, ONE);
1653       assign(m, border, TWO);
1654       multipleSelectionMenu(m, kind == NAME_toggle ? ON : OFF);
1655       assign(m, kind, kind);
1656       return requestComputeGraphical(m, DEFAULT);
1657     } else if ( kind == NAME_marked )
1658     { assign(m, on_image, MARK_IMAGE);
1659       assign(m, off_image, NOMARK_IMAGE);
1660       assign(m, feedback, NAME_image);
1661       assign(m, pen, ZERO);
1662       assign(m, border, TWO);
1663       multipleSelectionMenu(m, OFF);
1664       assign(m, kind, kind);
1665 
1666       return requestComputeGraphical(m, DEFAULT);
1667     } else if ( kind == NAME_cycle )
1668     { assign(m, pen, ZERO);
1669     }
1670   } else if ( m->look == NAME_motif )
1671   { if ( kind == NAME_marked || kind == NAME_toggle )
1672     { assign(m, on_image, NAME_marked);
1673       assign(m, off_image, NAME_marked);
1674       assign(m, feedback, NAME_image);
1675       assign(m, pen, ZERO);
1676       assign(m, border, toInt(3));
1677       multipleSelectionMenu(m, kind == NAME_toggle ? ON : OFF);
1678 
1679       assign(m, kind, kind);
1680       return requestComputeGraphical(m, DEFAULT);
1681     } else if ( kind == NAME_choice )
1682     { assign(m, on_image, NIL);
1683       assign(m, off_image, NIL);
1684       assign(m, feedback, NAME_box);
1685       assign(m, pen, ONE);
1686       assign(m, border, TWO);
1687       multipleSelectionMenu(m, OFF);
1688 
1689       assign(m, kind, kind);
1690       return requestComputeGraphical(m, DEFAULT);
1691     } else if ( kind == NAME_cycle )
1692     { assign(m, pen, ZERO);
1693     }
1694   } else if ( m->look == NAME_win )
1695   { if ( kind == NAME_marked || kind == NAME_toggle )
1696     { if ( kind == NAME_marked )
1697       { assign(m, on_image, NAME_marked);
1698 	assign(m, off_image, NAME_marked);
1699       } else
1700       { assign(m, on_image, NAME_marked);
1701 	assign(m, off_image, NAME_marked);
1702       }
1703       assign(m, feedback, NAME_image);
1704       assign(m, pen, ZERO);
1705       assign(m, border, toInt(3));
1706       multipleSelectionMenu(m, kind == NAME_toggle ? ON : OFF);
1707 
1708       assign(m, kind, kind);
1709       return requestComputeGraphical(m, DEFAULT);
1710     } else if ( kind == NAME_choice )
1711     { assign(m, on_image, NIL);
1712       assign(m, off_image, NIL);
1713       assign(m, feedback, NAME_box);
1714       assign(m, pen, ONE);
1715       assign(m, border, TWO);
1716       multipleSelectionMenu(m, OFF);
1717 
1718       assign(m, kind, kind);
1719       return requestComputeGraphical(m, DEFAULT);
1720     }
1721   }
1722 
1723   if ( kind == NAME_cycle )
1724   { assign(m, on_image, NIL);
1725     assign(m, off_image, NIL);
1726     assign(m, feedback, NAME_showSelectionOnly);
1727     assign(m, layout,   NAME_horizontal);
1728     assign(m, accelerator_font, NIL);
1729     assign(m, auto_value_align, OFF),
1730     multipleSelectionMenu(m, OFF);
1731     if ( getClassVariableValueObject(m, NAME_cycleIndicator) != NAME_comboBox )
1732     { assign(m, popup, newObject(ClassPopup, EAV));
1733       assign(m->popup, members, m->members);
1734       kindMenu((Menu) m->popup,  NAME_cyclePopup);
1735     } else
1736     { assign(m, border, toInt(4));
1737     }
1738   } else
1739   { assign(m, auto_value_align, ON);
1740 
1741     if ( kind == NAME_marked )
1742     { assign(m, on_image, MARK_IMAGE);
1743       assign(m, off_image, NOMARK_IMAGE);
1744       assign(m, feedback, NAME_image);
1745       multipleSelectionMenu(m, OFF);
1746     } else if ( kind == NAME_choice )
1747     { assign(m, on_image, NIL);
1748       assign(m, off_image, NIL);
1749       assign(m, feedback, NAME_invert);
1750       multipleSelectionMenu(m, OFF);
1751     } else if ( kind == NAME_toggle )
1752     { assign(m, on_image, MARK_IMAGE);
1753       assign(m, off_image, NOMARK_IMAGE);
1754       assign(m, feedback, NAME_image);
1755       multipleSelectionMenu(m, ON);
1756     } else if ( kind == NAME_popup )
1757     { if ( instanceOfObject(m, ClassPopup) )
1758       { defaultPopupImages((PopupObj)m);
1759       } else
1760       { assign(m, on_image, NIL);
1761 	assign(m, off_image, NIL);
1762       }
1763       multipleSelectionMenu(m, OFF);
1764     } else if ( kind == NAME_cyclePopup )
1765     { if ( m->look == NAME_win || m->look == NAME_motif )
1766 	assign(m, on_image, NAME_marked);
1767       else
1768 	assign(m, on_image, MARK_IMAGE);
1769       assign(m, off_image, NIL);
1770       multipleSelectionMenu(m, OFF);
1771     } else
1772       fail;
1773   }
1774 
1775   assign(m, kind, kind);
1776 
1777   succeed;
1778 }
1779 
1780 
1781 
1782 
1783 
1784 		/********************************
1785 		*          ATTRIBUTES		*
1786 		********************************/
1787 
1788 
1789 static status
ensureSingleSelectionMenu(Menu m)1790 ensureSingleSelectionMenu(Menu m)
1791 { MenuItem first = NIL;
1792   int sel = 0;
1793   Cell cell;
1794 
1795   for_cell(cell, m->members)
1796   { MenuItem mi = cell->value;
1797 
1798     if ( mi->active == ON && isNil(first) )
1799       first = mi;
1800     if ( mi->selected == ON )
1801     { if ( sel++ > 0 )
1802 	selectedMenuItem(mi, OFF);
1803     }
1804   }
1805 
1806   if ( sel == 0 )
1807   { if ( notNil(first) )
1808     { DEBUG(NAME_popup, Cprintf("%s: selecting first\n", pp(m)));
1809       return selectionMenu(m, first);
1810     }
1811 
1812     fail;
1813   }
1814 
1815   succeed;
1816 }
1817 
1818 
1819 static status
multipleSelectionMenu(Menu m,BoolObj val)1820 multipleSelectionMenu(Menu m, BoolObj val)
1821 { assignGraphical(m, NAME_multipleSelection, val);
1822 
1823   get(m, NAME_selection, EAV);		/* update <-selection */
1824 
1825   succeed;
1826 }
1827 
1828 
1829 static status
layoutMenu(Menu m,Name or)1830 layoutMenu(Menu m, Name or)
1831 { return assignGraphical(m, NAME_layout, or);
1832 }
1833 
1834 
1835 static status
columnsMenu(Menu m,Int n)1836 columnsMenu(Menu m, Int n)
1837 { assignGraphical(m, NAME_columns, n);
1838   if ( m->feedback == NAME_showSelectionOnly && notNil(m->popup) )
1839     send(m->popup, NAME_columns, n, EAV);
1840 
1841   succeed;
1842 }
1843 
1844 
1845 static status
valueFontMenu(Menu m,FontObj font)1846 valueFontMenu(Menu m, FontObj font)
1847 { return assignGraphical(m, NAME_valueFont, font);
1848 }
1849 
1850 
1851 static status
valueWidthMenu(Menu m,Int w)1852 valueWidthMenu(Menu m, Int w)
1853 { return assignGraphical(m, NAME_valueWidth, w);
1854 }
1855 
1856 
1857 static Int
getValueWidthMenu(Menu m)1858 getValueWidthMenu(Menu m)
1859 { ComputeGraphical(m);
1860   answer(m->item_size->w);
1861 }
1862 
1863 
1864 static status
showLabelMenu(Menu m,BoolObj val)1865 showLabelMenu(Menu m, BoolObj val)
1866 { return assignGraphical(m, NAME_showLabel, val);
1867 }
1868 
1869 
1870 static status
offImageMenu(Menu m,Image image)1871 offImageMenu(Menu m, Image image)
1872 { return assignGraphical(m, NAME_offImage, image);
1873 }
1874 
1875 
1876 static status
onImageMenu(Menu m,Image image)1877 onImageMenu(Menu m, Image image)
1878 { return assignGraphical(m, NAME_onImage, image);
1879 }
1880 
1881 
1882 static status
popupImageMenu(Menu m,Image image)1883 popupImageMenu(Menu m, Image image)
1884 { return assignGraphical(m, NAME_popupImage, image);
1885 }
1886 
1887 
1888 static status
borderMenu(Menu m,Int b)1889 borderMenu(Menu m, Int b)
1890 { return assignGraphical(m, NAME_border, b);
1891 }
1892 
1893 
1894 static status
gapMenu(Menu m,Size gap)1895 gapMenu(Menu m, Size gap)
1896 { return assignGraphical(m, NAME_gap, gap);
1897 }
1898 
1899 
1900 static status
formatMenu(Menu m,Name name)1901 formatMenu(Menu m, Name name)
1902 { return assignGraphical(m, NAME_format, name);
1903 }
1904 
1905 
1906 static status
verticalFormatMenu(Menu m,Name name)1907 verticalFormatMenu(Menu m, Name name)
1908 { return assignGraphical(m, NAME_verticalFormat, name);
1909 }
1910 
1911 
1912 static status
feedbackMenu(Menu m,Name feedback)1913 feedbackMenu(Menu m, Name feedback)
1914 { return assignGraphical(m, NAME_feedback, feedback);
1915 }
1916 
1917 
1918 static status
marginMenu(Menu m,Int margin)1919 marginMenu(Menu m, Int margin)
1920 { return assignGraphical(m, NAME_margin, margin);
1921 }
1922 
1923 
1924 static status
sortMenu(Menu m,Code msg)1925 sortMenu(Menu m, Code msg)
1926 { sortChain(m->members, msg, OFF);
1927 
1928   return requestComputeGraphical(m, NAME_assignAccelerators);
1929 }
1930 
1931 
1932 
1933 		/********************************
1934 		*           VISUALISER		*
1935 		********************************/
1936 
1937 static Chain
getContainsMenu(Menu m)1938 getContainsMenu(Menu m)
1939 { answer(m->members);
1940 }
1941 
1942 
1943 		/********************************
1944 		*         COMMUNICATION		*
1945 		********************************/
1946 
1947 static BoolObj
getModifiedMenu(Menu m)1948 getModifiedMenu(Menu m)
1949 { if ( m->multiple_selection == OFF )
1950   { MenuItem mi = getItemSelectionMenu(m);
1951 
1952     answer((mi && m->selection == mi->value) ? OFF : ON);
1953   } else
1954   { Cell cell;
1955     int size = valInt(m->members->size);
1956     char *is_set;
1957     int n, do_free;
1958 
1959     if ( !instanceOfObject(m->selection, ClassChain) )
1960       answer(ON);
1961 
1962     if ( !(is_set = alloca(size*sizeof(char))) )
1963     { is_set = pceMalloc(size*sizeof(char));
1964       do_free = TRUE;
1965     } else
1966       do_free = FALSE;
1967 
1968     n = 1;
1969     for_cell(cell, m->members)
1970       is_set[n++] = 0;
1971 
1972     for_cell(cell, (Chain) m->selection)
1973     { int index = index_item_menu(m, cell->value);
1974 
1975       is_set[index]++;
1976     }
1977 
1978     n = 1;
1979     for_cell(cell, m->members)
1980     { MenuItem mi = cell->value;
1981 
1982       if ( (is_set[n] && mi->selected == OFF) ||
1983 	   (!is_set[n] && mi->selected == ON) )
1984       { if ( do_free )
1985 	  pceFree(is_set);
1986 
1987 	answer(ON);
1988       }
1989       n++;
1990     }
1991 
1992     if ( do_free )
1993       pceFree(is_set);
1994 
1995     answer(OFF);
1996   }
1997 }
1998 
1999 
2000 static status
modifiedMenu(Menu m,BoolObj val)2001 modifiedMenu(Menu m, BoolObj val)
2002 { if ( val == OFF )
2003     getSelectionMenu(m);
2004   else
2005     return modifiedDialogItem(m, ON);
2006 
2007   succeed;
2008 }
2009 
2010 
2011 static Any
getDefaultMenu(Menu m)2012 getDefaultMenu(Menu m)
2013 { if ( notNil(m->default_value) )
2014     answer(checkType(m->default_value, TypeAny, m));
2015 
2016   fail;
2017 }
2018 
2019 
2020 static status
defaultMenu(Menu m,Any def)2021 defaultMenu(Menu m, Any def)
2022 { if ( m->default_value != def )
2023   { assign(m, default_value, def);
2024 
2025     restoreMenu(m);
2026   }
2027 
2028   succeed;
2029 }
2030 
2031 
2032 static status
restoreMenu(Menu m)2033 restoreMenu(Menu m)
2034 { Any val;
2035 
2036   TRY(val = getDefaultMenu(m));
2037   return send(m, NAME_selection, val, EAV);
2038 }
2039 
2040 
2041 static status
applyMenu(Menu m,BoolObj always)2042 applyMenu(Menu m, BoolObj always)
2043 { Any val;
2044 
2045   if ( instanceOfObject(m->message, ClassCode) &&
2046        (always == ON || getModifiedMenu(m) == ON) &&
2047        (val = get(m, NAME_selection, EAV)) )
2048   { forwardReceiverCode(m->message, m, val, EAV);
2049     succeed;
2050   }
2051 
2052   fail;
2053 }
2054 
2055 		 /*******************************
2056 		 *	   ACCELERATORS		*
2057 		 *******************************/
2058 
2059 static status
assignAcceletatorsMenu(Menu m)2060 assignAcceletatorsMenu(Menu m)
2061 { return assignAccelerators(m->members, CtoName("\\e"), NAME_label);
2062 }
2063 
2064 
2065 static status
keyMenu(Menu m,Name key)2066 keyMenu(Menu m, Name key)
2067 { Cell cell;
2068 
2069   for_cell(cell, m->members)
2070   { MenuItem mi = cell->value;
2071 
2072     if ( mi->accelerator == key )
2073     { return executeMenuItem(m, mi, EVENT->value);
2074     }
2075   }
2076 
2077   fail;
2078 }
2079 
2080 
2081 		 /*******************************
2082 		 *	 CLASS DECLARATION	*
2083 		 *******************************/
2084 
2085 /* Type declarations */
2086 
2087 static char *T_activeItem[] =
2088         { "item=member:menu_item", "active=bool" };
2089 static char *T_selected[] =
2090         { "item=member:menu_item", "selected=bool" };
2091 static char *T_initialise[] =
2092         { "name=[name]", "kind=[name]", "message=[code]*" };
2093 static char *T_insertBefore[] =
2094         { "item=menu_item", "before=name|menu_item" };
2095 
2096 /* Instance Variables */
2097 
2098 static vardecl var_menu[] =
2099 { IV(NAME_selection, "values=any|chain", IV_NONE,
2100      NAME_selection, "Value(s) of currently selected menu_items"),
2101   IV(NAME_members, "items=chain", IV_GET,
2102      NAME_organisation, "Menu items (alternatives)"),
2103   IV(NAME_default, "value=any|chain|function*", IV_NONE,
2104      NAME_apply, "Default value"),
2105   SV(NAME_kind, "kind={cycle,marked,choice,toggle,popup,cycle_popup}", IV_GET|IV_STORE, kindMenu,
2106      NAME_appearance, "Kind of menu"),
2107   SV(NAME_preview, "item=menu_item*", IV_GET|IV_STORE, previewMenu,
2108      NAME_event, "Item in `preview' state"),
2109   IV(NAME_previewFeedback, "feedback={box,rounded_box,inverted_rounded_box,invert,colour}", IV_BOTH,
2110      NAME_appearance, "Feedback given to item in preview state"),
2111   SV(NAME_feedback, "feedback={box,invert,image,show_selection_only}", IV_GET|IV_STORE, feedbackMenu,
2112      NAME_appearance, "Type of feedback for selection"),
2113   SV(NAME_multipleSelection, "multiple=bool", IV_GET|IV_STORE, multipleSelectionMenu,
2114      NAME_selection, "If @on, more than one item may be selected"),
2115   SV(NAME_showLabel, "show=bool", IV_GET|IV_STORE, showLabelMenu,
2116      NAME_appearance, "Whether label is visible"),
2117   SV(NAME_valueFont, "font=font", IV_GET|IV_STORE, valueFontMenu,
2118      NAME_appearance, "Font for value"),
2119   SV(NAME_valueWidth, "width=0..", IV_GET|IV_STORE, valueWidthMenu,
2120      NAME_layout, "Minimum width for values"),
2121   SV(NAME_layout, "orientation={horizontal,vertical}", IV_GET|IV_STORE, layoutMenu,
2122      NAME_appearance, "Horizontal or vertical layout"),
2123   SV(NAME_columns, "number=1..", IV_GET|IV_STORE, columnsMenu,
2124      NAME_appearance, "Number of columns"),
2125   SV(NAME_format, "alignment={left,center,right}", IV_GET|IV_STORE, formatMenu,
2126      NAME_appearance, "Horizontal alignment of items in box"),
2127   SV(NAME_verticalFormat, "alignment={top,center,bottom}", IV_GET|IV_STORE, verticalFormatMenu,
2128      NAME_appearance, "Vertical alignment of items (and decorations) in box"),
2129   SV(NAME_gap, "size", IV_GET|IV_STORE, gapMenu,
2130      NAME_layout, "Gap between items"),
2131   SV(NAME_border, "width=0..", IV_GET|IV_STORE, borderMenu,
2132      NAME_appearance, "Width of border around item"),
2133   SV(NAME_onImage, "image=image|{marked}*", IV_GET|IV_STORE, onImageMenu,
2134      NAME_appearance, "Left mark if selected equals @on"),
2135   SV(NAME_offImage, "image=image|{marked}*", IV_GET|IV_STORE, offImageMenu,
2136      NAME_appearance, "Left mark if selected equals @off"),
2137   SV(NAME_popupImage, "image=image*", IV_GET|IV_STORE, popupImageMenu,
2138      NAME_appearance, "Right mark if popup not equal @nil"),
2139   IV(NAME_acceleratorFont, "font=font*", IV_GET,
2140      NAME_appearance, "When not @nil, font for accelerators"),
2141   SV(NAME_margin, "margin=0..", IV_GET|IV_STORE, marginMenu,
2142      NAME_appearance, "Extra margin at left and right side of values (for popup)"),
2143   IV(NAME_leftOffset, "offset=0..", IV_GET,
2144      NAME_update, "Offset of item in its box (left-side)"),
2145   IV(NAME_rightOffset, "offset=0..", IV_GET,
2146      NAME_update, "Offset of item in its box (right-side)"),
2147   IV(NAME_itemOffset, "offset=point", IV_GET,
2148      NAME_update, "Offset of the first item"),
2149   IV(NAME_itemSize, "size=size", IV_GET,
2150      NAME_update, "Size of item-box"),
2151   IV(NAME_labelArea, "area=area*", IV_GET,
2152      NAME_update, "Area for the label (if show_label equals @on)")
2153 };
2154 
2155 /* Send Methods */
2156 
2157 static senddecl send_menu[] =
2158 { SM(NAME_compute, 0, NULL, computeMenu,
2159      DEFAULT, "Compute desired size"),
2160   SM(NAME_status, 1, "{inactive,active,preview,execute}", statusMenu,
2161      DEFAULT, "Status for event-processing"),
2162   SM(NAME_event, 1, "event", eventMenu,
2163      DEFAULT, "Process an event"),
2164   SM(NAME_initialise, 3, T_initialise, initialiseMenu,
2165      DEFAULT, "Create from label, kind and message"),
2166   SM(NAME_unlink, 0, NULL, unlinkMenu,
2167      DEFAULT, "Unlink from menu-items"),
2168   SM(NAME_assignAccelerators, 0, NULL, assignAcceletatorsMenu,
2169      NAME_accelerator, "Assign accelerators for the items"),
2170   SM(NAME_key, 1, "key=name", keyMenu,
2171      NAME_accelerator, "Handle accelerator key `name'"),
2172   SM(NAME_activeAllItems, 1, "active=bool", activeAllItemsMenu,
2173      NAME_active, "(De)activate all items in the menu"),
2174   SM(NAME_activeItem, 2, T_activeItem, activeItemMenu,
2175      NAME_active, "(De)activate item in menu"),
2176   SM(NAME_allOff, 0, NULL, allOffMenu,
2177      NAME_active, "Deactivate all menu_items"),
2178   SM(NAME_allOn, 0, NULL, allOnMenu,
2179      NAME_active, "Activate all menu_items"),
2180   SM(NAME_isOff, 1, "item=member:menu_item", isOffMenu,
2181      NAME_active, "Test if item is off"),
2182   SM(NAME_isOn, 1, "item=member:menu_item", isOnMenu,
2183      NAME_active, "Test if item is on"),
2184   SM(NAME_off, 1, "item=member:menu_item", offMenu,
2185      NAME_active, "Deactivate a menu_item or value"),
2186   SM(NAME_on, 1, "item=member:menu_item", onMenu,
2187      NAME_active, "Activate a menu_item or value"),
2188   SM(NAME_update, 1, "context=[any]", updateMenu,
2189      NAME_active, "Evaluate each item's condition"),
2190   SM(NAME_apply, 1, "always=[bool]", applyMenu,
2191      NAME_apply, "->execute if <-modified or @on"),
2192   SM(NAME_default, 1, "value=any|chain|function", defaultMenu,
2193      NAME_apply, "Set variable -default and ->selection"),
2194   SM(NAME_modified, 1, "modified=bool", modifiedMenu,
2195      NAME_apply, "Reset modified flag"),
2196   SM(NAME_restore, 0, NULL, restoreMenu,
2197      NAME_apply, "Set ->selection to <-default"),
2198   SM(NAME_execute, 1, "event=[event]", executeMenu,
2199      NAME_execute, "Change selection and forward message"),
2200   SM(NAME_append, 1, "item=menu_item", appendMenu,
2201      NAME_items, "Append a menu_item, value or popup"),
2202   SM(NAME_clear, 0, NULL, clearMenu,
2203      NAME_items, "Delete all menu_items"),
2204   SM(NAME_delete, 1, "item=member:menu_item", deleteMenu,
2205      NAME_items, "Delete a menu_item or value"),
2206   SM(NAME_member, 1, "item=member:menu_item", memberMenu,
2207      NAME_items, "Test if menu_item or value is member"),
2208   SM(NAME_members, 1, "chain", membersMenu,
2209      NAME_items, "->clear and ->append members of chain"),
2210   SM(NAME_prepend, 1, "item=menu_item", prependMenu,
2211      NAME_items, "Prepend a menu_item, value or popup"),
2212   SM(NAME_insertBefore, 2, T_insertBefore, insertBeforeMenu,
2213      NAME_items, "Insert item before another one"),
2214   SM(NAME_sort, 1, "[code]", sortMenu,
2215      NAME_items, "Sort members (see `chain ->sort')"),
2216   SM(NAME_labelWidth, 1, "width=[int]", labelWidthMenu,
2217      NAME_layout, "Set width in pixels used for label"),
2218   SM(NAME_ChangedItem, 1, "changed=menu_item", ChangedItemMenu,
2219      NAME_repaint, "Handle change of item"),
2220   SM(NAME_clearSelection, 0, NULL, clearSelectionMenu,
2221      NAME_selection, "Clear the selection"),
2222   SM(NAME_next, 0, NULL, nextMenu,
2223      NAME_selection, "Set selection to next (`cycle')"),
2224   SM(NAME_selected, 2, T_selected, selectedMenu,
2225      NAME_selection, "(De)select a single menu_item or value"),
2226   SM(NAME_selection, 1, "selection=member:menu_item|chain*", selectionMenu,
2227      NAME_selection, "Select menu_item or value (or chain)"),
2228   SM(NAME_selectedCompletion, 1, "dict_item", selectedCompletionMenu,
2229      NAME_internal, "Handle combo_box message"),
2230   SM(NAME_keyboardQuit, 0, NULL, keyboardQuitMenu,
2231      NAME_complete, "Remove completer")
2232 };
2233 
2234 /* Get Methods */
2235 
2236 static getdecl get_menu[] =
2237 { GM(NAME_contains, 0, "items=chain", NULL, getContainsMenu,
2238      DEFAULT, "Chain with menu_items contained"),
2239   GM(NAME_reference, 0, "point", NULL, getReferenceMenu,
2240      DEFAULT, "Baseline of label"),
2241   GM(NAME_activeItem, 1, "active=bool", "item=member:menu_item", getActiveItemMenu,
2242      NAME_active, "Active value if indicated item"),
2243   GM(NAME_default, 0, "value=any|chain", NULL, getDefaultMenu,
2244      NAME_apply, "Current default value"),
2245   GM(NAME_modified, 0, "modified=bool", NULL, getModifiedMenu,
2246      NAME_apply, "If @on, menu has been modified"),
2247   GM(NAME_itemFromEvent, 1, "item=menu_item", "event=event", getItemFromEventMenu,
2248      NAME_event, "Find item on which event occurred"),
2249   GM(NAME_member, 1, "member=menu_item", "value=any", getMemberMenu,
2250      NAME_items, "Find menu_item with given value"),
2251   GM(NAME_labelWidth, 0, "width=int", NULL, getLabelWidthMenu,
2252      NAME_layout, "Minimum width for label in pixels"),
2253   GM(NAME_valueWidth, 0, "width=int", NULL, getValueWidthMenu,
2254      NAME_layout, "Minimum width for value in pixels"),
2255   GM(NAME_selected, 1, "selected=bool", "item=member:menu_item", getSelectedMenu,
2256      NAME_selection, "Find out if menu_item or value is selected"),
2257   GM(NAME_selection, 0, "values=any|chain*", NULL, getSelectionMenu,
2258      NAME_selection, "Get current selection (menu_item<-value or chain)")
2259 };
2260 
2261 /* Resources */
2262 
2263 static classvardecl rc_menu[] =
2264 { RC(NAME_acceleratorFont, "font*", "@nil",
2265      "Show the accelerators"),
2266   RC(NAME_border, "int", "2",
2267      "Border around each item"),
2268   RC(NAME_cycleIndicator, "{combo_box}|image|elevation",
2269      "combo_box",
2270      "Indication of a ->kind: cycle menu"),
2271   RC(NAME_feedback, "name", "image",
2272      "Type of feedback for selection"),
2273   RC(NAME_format, "{left,center,right}", "left",
2274      "Adjust items {left,center,right} in their box"),
2275   RC(NAME_gap, "size", "size(0,0)",
2276      "Gap between items (XxY)"),
2277   RC(NAME_itemElevation, "elevation*",
2278      UXWIN("when(@colour_display, button, @nil)", "2"),
2279      "Elevation of items in the menu"),
2280   RC(NAME_markElevation, "elevation*",
2281      UXWIN("when(@colour_display, mark, @nil)",
2282 	   "elevation(mark, 2, colour := white)"),
2283      "Elevation of marks"),
2284   RC(NAME_kind, "name", "marked",
2285      "Default menu kind"),
2286   RC(NAME_layout, "name", "horizontal",
2287      "Layout of the menu: {horizontal,vertical}"),
2288   RC(NAME_margin, "0..", "0",
2289      "Margin to the left and right"),
2290   RC(NAME_offImage, "{marked}|image*",
2291      UXWIN("@nomark_image", "marked"),
2292      "Marker for items not in selection"),
2293   RC(NAME_onImage, "{marked}|image*",
2294      UXWIN("@mark_image", "marked"),
2295      "Marker for items in selection"),
2296   RC(NAME_pen, "int", "when(@colour_display, 0, 1)",
2297      "Thickness of pen around items"),
2298   RC(NAME_popupImage, "image*", "@nil",
2299      "Marker for items with popup"),
2300   RC(NAME_previewElevation, "elevation*", "0",
2301      "Elevation of item in preview mode"),
2302   RC(NAME_previewFeedback, "name", "box",
2303      "Indication item is in preview state"),
2304   RC(NAME_showLabel, "bool", "@on",
2305      "Show the label"),
2306   RC(NAME_valueWidth, "int", "0",
2307      "Minimum width for popup menu"),
2308   RC(NAME_verticalFormat, "{top,center,bottom}", "center",
2309      "Adjust items {top,center,bottom} in their box"),
2310   RC(NAME_selectedForeground, "colour|pixmap",
2311      UXWIN("white", "win_highlighttext"),
2312      "Text colour for selected item"),
2313   RC(NAME_selectedBackground, "colour|pixmap",
2314      UXWIN("black", "win_highlight"),
2315      "Background colour for selected item"),
2316   RC(NAME_elevation, RC_REFINE,
2317      UXWIN("when(@colour_display, 0, @nil)", "@nil"), NULL),
2318   RC(NAME_valueFont, RC_REFINE, "normal", NULL),
2319   RC(NAME_comboBoxHeight, "1..", "6",
2320      "Maximum height of the combo-box shown for completions")
2321 };
2322 
2323 /* Class Declaration */
2324 
2325 static Name menu_termnames[] = { NAME_label, NAME_kind, NAME_message };
2326 
2327 ClassDecl(menu_decls,
2328           var_menu, send_menu, get_menu, rc_menu,
2329           3, menu_termnames,
2330           "$Rev$");
2331 
2332 status
makeClassMenu(Class class)2333 makeClassMenu(Class class)
2334 { declareClass(class, &menu_decls);
2335   setRedrawFunctionClass(class, RedrawAreaMenu);
2336 
2337   succeed;
2338 }
2339