source: trunk/WIDGET/COMPOUND_WIDGET/cw_pdmenu.pro @ 2

Last change on this file since 2 was 2, checked in by opalod, 22 years ago

Initial revision

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 KB
Line 
1; $Id$
2;
3; Copyright (c) 1992-1998, Research Systems, Inc.  All rights reserved.
4;       Unauthorized reproduction prohibited.
5;+
6; NAME:
7;       CW_PDMENU
8;
9; PURPOSE:
10;       CW_PDMENU is a compound widget that simplifies creating
11;       pulldown menus. It has a simpler interface than the XPDMENU
12;       procedure, which it is intended to replace. Events for the
13;       individual buttons are handled transparently, and a CW_PDMENU
14;       event returned. This event can return any one of the following:
15;               - The Index of the button within the base.
16;               - The widget ID of the button.
17;               - The name of the button.
18;               - The fully qualified name of the button. This allows
19;                 different sub-menus to contain buttons with the same
20;                 name in an unambiguous way.
21;
22;
23; CATEGORY:
24;       Compound widgets.
25;
26; CALLING SEQUENCE:
27;       widget = CW_PDMENU(Parent, Desc)
28;
29; INPUTS:
30;       Parent: The ID of the parent widget.
31;       Desc:   An array of strings or structures.  Each element contains
32;               a menu description with two fields, a flag field, and
33;               the name of the item.  If a structure, each element
34;               is defined as follows:
35;                       { CW_PDMENU_S, flags:0, name:'' }
36;
37;               The name tag gives the name of button. The flags
38;               field is a two-bit bitmask that controls how the button is
39;               interpreted:
40;
41;                   Value          Meaning
42;                   -------------------------------------------
43;                    0     This button is neither the beginning
44;                          nor the end of a pulldown level.
45;                    1     This button is the root of a
46;                          sub-pulldown menu. The sub-buttons
47;                          start with the next button.
48;                    2     This button is the last button at the
49;                          current pulldown level. The next button
50;                          belongs to the same level as the current
51;                          parent button.
52;                          If none or empty string is specified as a
53;                          the name, the button is not created, but
54;                          the next button belongs to the upward level.
55;                    3     This button is the root of a sub-pulldown
56;                          menu, but it is also the last entry of
57;                          the current level.
58;                    4     Same as 0, above, except that this button will
59;                          be preceeded by a separator as with the SEPARATOR
60;                          keyword to WIDGET_BUTTON.
61;                    5     Same as 1, above, except that this button will
62;                          be preceeded by a separator.
63;                    6     Same as 2, above, except that this button will
64;                          be preceeded by a separator.
65;                    7     Same as 3, above, except that this button will
66;                          be preceeded by a separator.
67;
68;       If Desc is a string, each element contains the flag field
69;       followed by a backslash character, followed by the menu item's
70;       contents.  See the example below.
71;
72;       EVENT PROCEDURES:  An event procedure may be specified for an
73;       element and all its children, by including a third field
74;       in Desc, if Desc is a string array.  Events for buttons without
75;       an event procedure, are dispatched normally.
76;       See the example below.
77;
78; KEYWORD PARAMETERS:
79;       DELIMITER:        The character used to separate the parts of a
80;                         fully qualified name in returned events. The
81;                         default is to use the '.' character.
82;       FONT:             The name of the font to be used for the button
83;                         titles. If this keyword is not specified, the
84;                         default font is used.
85;       HELP:             If MBAR is specified and one of the buttons on the
86;                         menubar has the label "help" (case insensitive) then
87;                         that button is created with the /HELP keyword to
88;                         give it any special appearance it is supposed to
89;                         have on a menubar. For example, Motif expects
90;                         help buttons to be on the right.
91;       IDS:              A named variable into which the button IDs will
92;                         be stored as a longword vector.
93;       MBAR:             if constructing a menu-bar pulldown, set this
94;                         keyword.  In this case, the parent must be the
95;                         widget id of the menu bar of a top-level base,
96;                         returned by WIDGET_BASE(..., MBAR=mbar).
97;       RETURN_ID:        If present and non-zero, the VALUE field of returned
98;                         events will be the widget ID of the button.
99;       RETURN_INDEX:     If present and non-zero, the VALUE field of returned
100;                         events will be the zero-based index of the button
101;                         within the base. THIS IS THE DEFAULT.
102;       RETURN_NAME:      If present and non-zero, the VALUE field of returned
103;                         events will be the name of the selected button.
104;       RETURN_FULL_NAME: If present and non-zero, the VALUE field of returned
105;                         events will be the fully qualified name of the
106;                         selected button. This means that the names of all
107;                         the buttons from the topmost button of the pulldown
108;                         menu to the selected one are concatenated with the
109;                         delimiter specified by the DELIMITER keyword. For
110;                         example, if the top button was named COLORS, the
111;                         second level button was named BLUE, and the selected
112;                         button was named LIGHT, the returned value would be
113;
114;                         COLORS.BLUE.LIGHT
115;
116;                         This allows different submenus to have buttons with
117;                         the same name (e.g. COLORS.RED.LIGHT).
118;       UVALUE:           The user value to be associated with the widget.
119;
120;       UNAME:            Set this keyword to a string that can be
121;       used to identify the widget in your code. You can associate a
122;       name with each widget in a specific hierarchy, and then use
123;       that name to query the widget hierarchy and get the correct
124;       widget ID.
125;
126;       XOFFSET:          The X offset of the widget relative to its parent.
127;       YOFFSET:          The Y offset of the widget relative to its parent.
128;
129;       _EXTRA:
130;
131; OUTPUTS:
132;       The ID of the top level button is returned.
133;
134; SIDE EFFECTS:
135;       This widget generates event structures with the following definition:
136;
137;               event = { ID:0L, TOP:0L, HANDLER:0L, VALUE:0 }
138;
139;       VALUE is either the INDEX, ID, NAME, or FULL_NAME of the button,
140;       depending on how the widget was created.
141;
142; RESTRICTIONS:
143;       Only buttons with textual names are handled by this widget.
144;       Bitmaps are not understood.
145;
146; EXAMPLE:
147;       The following is the description of a menu bar with two buttons,
148;       "Colors" and "Quit". Colors is a pulldown containing the colors
149;       "Red", "Green", Blue", "Cyan", and "Magenta". Blue is a sub-pulldown
150;       containing "Light", "Medium", "Dark", "Navy", and "Royal":
151;
152;               ; Make sure CW_PDMENU_S is defined
153;               junk = { CW_PDMENU_S, flags:0, name:'' }
154;
155;               ; The description
156;               desc = [ { CW_PDMENU_S, 1, 'Colors' }, $
157;                            { CW_PDMENU_S, 0, 'Red' }, $
158;                            { CW_PDMENU_S, 0, 'Green' }, $
159;                            { CW_PDMENU_S, 5, 'Blue\BLUE_EVENT_PROC' }, $
160;                                { CW_PDMENU_S, 0, 'Light' }, $
161;                                { CW_PDMENU_S, 0, 'Medium' }, $
162;                                { CW_PDMENU_S, 0, 'Dark' }, $
163;                                { CW_PDMENU_S, 0, 'Navy' }, $
164;                                { CW_PDMENU_S, 2, 'Royal' }, $
165;                              { CW_PDMENU_S, 4, 'Cyan' }, $
166;                              { CW_PDMENU_S, 2, 'Magenta\MAGENTA_EVENT_PROC' }, $
167;                        { CW_PDMENU_S, 2, 'Quit' } ]
168;
169;       The same menu may be defined as a string by equating the Desc parameter
170;       to the following string array:
171;       
172;       desc =[ '1\Colors' , $
173;               '0\Red' , $
174;               '0\Green' , $
175;               '5\Blue\BLUE_EVENT_PROC' , $
176;               '0\Light' , $
177;               '0\Medium' , $
178;               '0\Dark' , $
179;               '0\Navy' , $
180;               '2\Royal' , $
181;               '4\Cyan' , $
182;               '2\Magenta\MAGENTA_EVENT_PROC' , $
183;               '2\Quit'  ]
184;
185;
186;       The following small program can be used with the above description
187;       to create the specified menu:
188;
189;
190;               base = widget_base()
191;               menu = cw_pdmenu(base, desc, /RETURN_FULL_NAME)
192;               WIDGET_CONTROL, /REALIZE, base
193;               repeat begin
194;                 ev = WIDGET_EVENT(base)
195;                 print, ev.value
196;               end until ev.value eq 'Quit'
197;               WIDGET_CONTROL, /DESTROY, base
198;               end
199;
200;       Note that independent event procedures were specified for
201;       the multiple Blue buttons (blue_event_proc), and the Magenta button
202;       (magenta_event_proc).
203
204; MODIFICATION HISTORY:
205;       18 June 1992, AB
206;       16 Jan 1995, DMS, Added MBAR keyword, event procedures,
207;                       and menu descriptor strings.
208;       2 July 1995, AB, Added HELP keyword.
209;       3 September 1996, LP, Added button-less end of current level
210;-
211
212
213function CW_PDMENU_EVENT, ev
214
215  WIDGET_CONTROL, ev.id, get_uvalue=uvalue
216
217  return, { ID:ev.handler, TOP:ev.top, HANDLER:0L, value:uvalue }
218
219end
220
221
222
223
224
225
226
227pro CW_PDMENU_BUILD, parent, desc, cur, n, ev_type, full_qual_str, $
228        delim, ids, mbars, HELP_KW, FONT=font
229; Recursive routine that builds the pulldown hierarchy described in DESC.
230; Returns the ID of each button in ids.
231
232  is_string = size(desc)
233  is_string = is_string[is_string[0]+1] eq 7
234  while (cur lt n) do begin
235    separator=0
236    if is_string then begin
237        ; String version of DESC structure
238        a = str_sep(desc[cur], '\')
239        dflags = fix(strtrim(a[0],2))
240        if dflags GT 3 then begin
241          separator = 1
242          dflags = dflags - 4
243        endif
244        ; End of the level, and no button
245        if dflags EQ 2 AND N_ELEMENTS(a) LT 2 then begin
246            ids[cur] = 0
247            cur = cur + 1
248            return
249        endif
250        dname = a[1]
251    endif else begin
252        ; Structure version of DESC structure
253        dflags = desc[cur].flags
254        if dflags GT 3 then begin
255          separator = 1
256          dflags = dflags - 4
257        endif
258        dname = desc[cur].name
259        ; End of the level, and no button
260        if dflags EQ 2 AND dname EQ '' then begin
261            ids[cur] = 0
262            cur = cur + 1
263            return
264        endif
265        a = ['', str_sep(dname, '\')]
266        dname = a[1]
267    endelse
268
269    if (strlen(full_qual_str) ne 0) then $
270      new_qstr = full_qual_str + delim + dname $
271    else new_qstr = dname
272    ;If parented to a menu bar, don't draw a frame.
273    if (dflags and 1) then menu=2-mbars else menu = 0
274    ;
275    ; Build string with approriate keywords and execute it
276    ;
277    strExecute = 'new = WIDGET_BUTTON(parent, value=dname, MENU=menu'
278    if ((mbars ne 0) and (HELP_KW ne 0) $
279        and (strupcase(dname) eq 'HELP')) then $
280      strExecute = strExecute + ', /HELP'
281    if (keyword_set(font)) then strExecute = strExecute + ',  FONT=font'
282    strExecute = strExecute + ', SEPARATOR=separator'
283    strExecute = strExecute + ')'
284
285    status = EXECUTE(strExecute)
286
287    ; Set requested Return value
288    case ev_type of
289      0: uvalue = cur
290      1: uvalue = new
291      2: uvalue = dname
292      3: uvalue = new_qstr
293    endcase
294    WIDGET_CONTROL, new, SET_UVALUE=uvalue
295
296    ; Register event procedure
297    if n_elements(a) ge 3 then WIDGET_CONTROL, new, EVENT_PRO=strtrim(a[2],2)
298    ids[cur] = new
299    cur = cur + 1
300
301    ; Pullright menu button, start new level
302    if (dflags and 1) then $
303      CW_PDMENU_BUILD,new,desc,cur,n,ev_type,$
304      new_qstr,delim,ids,mbars, 0,FONT=font
305
306    ; End of the current level
307    if ((dflags and 2) ne 0) then return
308  endwhile
309
310end
311
312
313
314
315
316
317function CW_PDMENU, parent, desc, COLUMN=column, DELIMITER=delim, FONT=font, $
318        IDS=ids, MBAR=mbar, HELP=HELP_KW, $
319        RETURN_ID=r_id, RETURN_INDEX=ignore, RETURN_NAME=r_name, $
320        RETURN_FULL_NAME=r_full_name, $
321        UVALUE=uvalue, UNAME = uname, XOFFSET=xoffset, YOFFSET=yoffset, $
322           _EXTRA = ex
323
324
325  IF (N_PARAMS() ne 2) THEN MESSAGE, 'Incorrect number of arguments'
326
327  ON_ERROR, 2                                           ;return to caller
328
329  ; Set default values for the keywords
330  If KEYWORD_SET(column) then row = 0 else begin row = 1 & column = 0 & end
331  IF (N_ELEMENTS(delim) eq 0)   then delim = '.'
332  IF (N_ELEMENTS(uvalue) eq 0)  then uvalue = 0
333  IF (N_ELEMENTS(xoffset) eq 0) then xoffset=0
334  IF (N_ELEMENTS(yoffset) eq 0) then yoffset=0
335
336  ; How to interpret ev_type:
337  ;     0 - Return index
338  ;     1 - Return ID
339  ;     2 - Return name
340  ;     3 - Return fully qualified name.
341  ev_type = 0
342  if (keyword_set(r_id))        then ev_type = 1
343  if (keyword_set(r_name))      then ev_type = 2
344  if (keyword_set(r_full_name)) then ev_type = 3
345
346
347  n = n_elements(desc)
348  ids = lonarr(n)
349  mbars = KEYWORD_SET(mbar)
350  help_kw = KEYWORD_SET(HELP_KW)
351  if mbars then BEGIN
352    base = parent
353    if (keyword_set(uvalue)) then WIDGET_CONTROL, base, SET_UVALUE=uvalue, SET_UNAME=uname
354  ENDIF else BEGIN
355    base = widget_base(parent, COLUMN=column, UNAME = uname, $
356                     ROW=row, UVALUE=uvalue, XOFFSET=xoffset, YOFFSET=yoffset, _EXTRA = ex)
357  ENDELSE
358  WIDGET_CONTROL, base, EVENT_FUNC='CW_PDMENU_EVENT'
359 CW_PDMENU_BUILD, base, desc, 0, n, ev_type, '', delim, ids, mbars, help_kw, $
360        FONT=font
361  return, base
362END
Note: See TracBrowser for help on using the repository browser.