2 // "$Id: Fl_Menu_add.cxx 8110 2010-12-23 08:02:52Z manolo $"
4 // Menu utilities for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
27 // Methods to alter the menu in an Fl_Menu_ widget.
29 // These are for Forms emulation and for dynamically changing the
30 // menus. They are in this source file so they are not linked in if
31 // not used, which is what will happen if the program only uses
32 // constant menu tables.
34 // Not at all guaranteed to be Forms compatible, especially with any
35 // string with a % sign in it!
37 #include <FL/Fl_Menu_.H>
42 // If the array is this, we will double-reallocate as necessary:
43 static Fl_Menu_Item
* local_array
= 0;
44 static int local_array_alloc
= 0; // number allocated
45 static int local_array_size
= 0; // == size(local_array)
46 extern Fl_Menu_
* fl_menu_array_owner
; // in Fl_Menu_.cxx
48 // For historical reasons there are matching methods that work on a
49 // user-allocated array of Fl_Menu_Item. These methods are quite
50 // depreciated and should not be used. These old methods use the
51 // above pointers to detect if the array belongs to an Fl_Menu_
52 // widget, and if so it reallocates as necessary.
56 // Insert a single Fl_Menu_Item into an array of size at offset n,
57 // if this is local_array it will be reallocated if needed.
58 static Fl_Menu_Item
* array_insert(
59 Fl_Menu_Item
* array
, // array to modify
60 int size
, // size of array
61 int n
, // index of new insert position
62 const char *text
, // text of new item (copy is made)
63 int flags
// flags for new item
65 if (array
== local_array
&& size
>= local_array_alloc
) {
66 local_array_alloc
= 2*size
;
67 Fl_Menu_Item
* newarray
= new Fl_Menu_Item
[local_array_alloc
];
68 memmove(newarray
, array
, size
*sizeof(Fl_Menu_Item
));
70 local_array
= array
= newarray
;
72 // move all the later items:
73 memmove(array
+n
+1, array
+n
, sizeof(Fl_Menu_Item
)*(size
-n
));
74 // create the new item:
75 Fl_Menu_Item
* m
= array
+n
;
76 m
->text
= text
? strdup(text
) : 0;
81 m
->labeltype_
= m
->labelsize_
= m
->labelcolor_
= 0;
82 m
->labelfont_
= FL_HELVETICA
;
88 // Comparison that does not care about deleted '&' signs:
89 static int compare(const char* a
, const char* b
) {
94 else if (*b
== '&') b
++;
106 /** Adds an item. The text is split at '/' characters to automatically
107 produce submenus (actually a totally unnecessary feature as you can
108 now add submenu titles directly by setting SUBMENU in the flags):
110 int Fl_Menu_Item::add(
117 return(insert(-1,mytext
,sc
,cb
,data
,myflags
)); // -1: append
123 Inserts an item at position \p index.
125 If \p index is -1, the item is added the same way as Fl_Menu_Item::add().
127 If 'mytext' contains any un-escaped front slashes (/), it's assumed
128 a menu pathname is being specified, and the value of \p index
131 In all other aspects, the behavior of insert() is the same as add().
133 \param index insert new items here
134 \param mytext new label string, details see above
135 \param sc keyboard shortcut for new item
136 \param cb callback function for new item
137 \param data user data for new item
138 \param myflags menu flags as described in FL_Menu_Item
139 \returns the index into the menu() array, where the entry was added
141 int Fl_Menu_Item::insert(
149 Fl_Menu_Item
*array
= this;
150 Fl_Menu_Item
*m
= this;
155 int msize
= array
==local_array
? local_array_size
: array
->size();
159 // split at slashes to make submenus:
162 // leading slash makes us assume it is a filename:
163 if (*mytext
== '/') {item
= mytext
; break;}
165 // leading underscore causes divider line:
166 if (*mytext
== '_') {mytext
++; flags1
= FL_MENU_DIVIDER
;}
168 // copy to buf, changing \x to x:
170 for (p
=mytext
; *p
&& *p
!= '/'; *q
++ = *p
++) if (*p
=='\\' && p
[1]) p
++;
174 if (*p
!= '/') break; /* not a menu title */
175 index
= -1; /* any submenu specified overrides insert position */
176 mytext
= p
+1; /* point at item title */
178 /* find a matching menu title: */
179 for (; m
->text
; m
= m
->next())
180 if (m
->flags
&FL_SUBMENU
&& !compare(item
, m
->text
)) break;
182 if (!m
->text
) { /* create a new menu */
183 int n
= (index
==-1) ? m
-array
: index
;
184 array
= array_insert(array
, msize
, n
, item
, FL_SUBMENU
|flags1
);
186 array
= array_insert(array
, msize
, n
+1, 0, 0);
190 m
++; /* go into the submenu */
194 /* find a matching menu item: */
195 for (; m
->text
; m
= m
->next())
196 if (!(m
->flags
&FL_SUBMENU
) && !compare(m
->text
,item
)) break;
198 if (!m
->text
) { /* add a new menu item */
199 int n
= (index
==-1) ? m
-array
: index
;
200 array
= array_insert(array
, msize
, n
, item
, myflags
|flags1
);
202 if (myflags
& FL_SUBMENU
) { // add submenu delimiter
203 array
= array_insert(array
, msize
, n
+1, 0, 0);
212 m
->user_data_
= data
;
213 m
->flags
= myflags
|flags1
;
215 if (array
== local_array
) local_array_size
= msize
;
222 Adds a new menu item.
224 \param[in] label The text label for the menu item.
225 \param[in] shortcut Optional keyboard shortcut that can be an int or string; (FL_CTRL+'a') or "^a". Default 0 if none.
226 \param[in] callback Optional callback invoked when user clicks the item. Default 0 if none.
227 \param[in] userdata Optional user data passed as an argument to the callback. Default 0 if none.
228 \param[in] flags Optional flags that control the type of menu item; see below. Default is 0 for none.
229 \returns The index into the menu() array, where the entry was added.
232 If the menu array was directly set with menu(x), then copy() is done
233 to make a private array.
235 Since this method can change the internal menu array, any menu item
236 pointers or indecies the application may have cached can become stale,
237 and should be recalculated/refreshed.
239 A menu item's callback must not add() items to its parent menu during the callback.
241 <B>Detailed Description of Parameters</B>
243 The menu item's label. This option is required.
245 The characters "&", "/", "\", and "_" are treated as special characters in the label string.
246 The "&" character specifies that the following character is an accelerator and will be underlined.
247 The "\" character is used to escape the next character in the string.
248 Labels starting with the "_" character cause a divider to be placed after that menu item.
250 A label of the form "File/Quit" will create the submenu "File"
251 with a menu item called "Quit". The "/" character is ignored if it appears
252 as the first character of the label string, e.g. "/File/Quit".
254 The label string is copied to new memory and can be freed.
255 The other arguments (including the shortcut) are copied into the
258 If an item exists already with that name then it is replaced with
259 this new one. Otherwise this new one is added to the end of the
260 correct menu or submenu. The return value is the offset into the array
261 that the new entry was placed at.
264 The keyboard shortcut for this menu item.
266 This parameter is optional, and defaults to 0 to indicate no shortcut.
268 The shortcut can either be a raw integer value (eg. FL_CTRL+'A')
269 or a string (eg. "^c" or "^97").
271 Raw integer shortcuts can be a combination of keyboard chars (eg. 'A')
272 and optional keyboard modifiers (see Fl::event_state(), e.g. FL_SHIFT, etc).
273 In addition, FL_COMMAND can be used to denote FL_META under Mac OS X and
274 FL_CTRL under other platforms.
276 String shortcuts can be specified in one of two ways:
279 [#+^]<ascii_value> e.g. "97", "^97", "+97", "#97"
280 [#+^]<ascii_char> e.g. "a", "^a", "+a", "#a"
283 ..where \<ascii_value\> is a decimal value representing an
284 ascii character (eg. 97 is the ascii code for 'a'), and the optional
285 prefixes enhance the value that follows. Multiple prefixes must
286 appear in the order below.
294 Internally, the text shortcuts are converted to integer values using
295 fl_old_shortcut(const char*).
298 The callback to invoke when this menu item is selected.
300 This parameter is optional, and defaults to 0 for no callback.
303 The callback's 'user data' that is passed to the callback.
305 This parameter is optional, and defaults to 0.
308 These are bit flags to define what kind of menu item this is.
310 This parameter is optional, and defaults to 0 to define a 'regular' menu item.
312 These flags can be 'OR'ed together:
314 FL_MENU_INACTIVE // Deactivate menu item (gray out)
315 FL_MENU_TOGGLE // Item is a checkbox toggle (shows checkbox for on/off state)
316 FL_MENU_VALUE // The on/off state for checkbox/radio buttons (if set, state is 'on')
317 FL_MENU_RADIO // Item is a radio button (one checkbox of many can be on)
318 FL_MENU_INVISIBLE // Item will not show up (shortcut will work)
319 FL_SUBMENU_POINTER // Indicates user_data() is a pointer to another menu array
320 FL_SUBMENU // This item is a submenu to other items
321 FL_MENU_DIVIDER // Creates divider line below this item. Also ends a group of radio buttons.
324 \todo Raw integer shortcut needs examples.
325 Dependent on responses to http://fltk.org/newsgroups.php?gfltk.development+v:10086 and results of STR#2344
327 int Fl_Menu_::add(const char *label
,int shortcut
,Fl_Callback
*callback
,void *userdata
,int flags
) {
328 return(insert(-1,label
,shortcut
,callback
,userdata
,flags
)); // -1: append
334 Inserts a new menu item at the specified \p index position.
336 If \p index is -1, the menu item is appended; same behavior as add().
338 To properly insert a menu item, \p label must be the name of the item (eg. "Quit"),
339 and not a 'menu pathname' (eg. "File/Quit"). If a menu pathname is specified,
340 the value of \p index is \em ignored, the new item's position defined by the pathname.
342 For more details, see add(). Except for the \p index parameter, add()
343 has more detailed information on parameters and behavior, and is
344 functionally equivalent.
346 \param[in] index The menu array's index position where the new item
347 is inserted. If -1, behavior is the same as add().
348 \param[in] label The text label for the menu item. If the label
349 is a menu pathname, \p index is ignored, and the pathname
350 indicates the position of the new item.
351 \param[in] shortcut Optional keyboard shortcut. Can be an int (FL_CTRL+'a')
352 or a string ("^a"). Default is 0.
353 \param[in] callback Optional callback invoked when user clicks the item.
355 \param[in] userdata Optional user data passed as an argument to the callback.
357 \param[in] flags Optional flags that control the type of menu item;
358 see add() for more info. Default is 0 for none.
359 \returns The index into the menu() array, where the entry was added.
364 int Fl_Menu_::insert(
368 Fl_Callback
*callback
,
372 // make this widget own the local array:
373 if (this != fl_menu_array_owner
) {
374 if (fl_menu_array_owner
) {
375 Fl_Menu_
* o
= fl_menu_array_owner
;
376 // the previous owner get's its own correctly-sized array:
377 int value_offset
= o
->value_
-local_array
;
378 int n
= local_array_size
;
379 Fl_Menu_Item
* newMenu
= o
->menu_
= new Fl_Menu_Item
[n
];
380 memcpy(newMenu
, local_array
, n
*sizeof(Fl_Menu_Item
));
381 if (o
->value_
) o
->value_
= newMenu
+value_offset
;
384 // this already has a menu array, use it as the local one:
385 delete[] local_array
;
386 if (!alloc
) copy(menu_
); // duplicate a user-provided static array
387 // add to the menu's current array:
388 local_array_alloc
= local_array_size
= size();
391 // start with a blank array:
392 alloc
= 2; // indicates that the strings can be freed
396 local_array_alloc
= 15;
397 local_array
= menu_
= new Fl_Menu_Item
[local_array_alloc
];
398 memset(local_array
, 0, sizeof(Fl_Menu_Item
) * local_array_alloc
);
400 memset(menu_
, 0, sizeof(Fl_Menu_Item
));
401 local_array_size
= 1;
403 fl_menu_array_owner
= this;
405 int r
= menu_
->insert(index
,label
,shortcut
,callback
,userdata
,flags
);
406 // if it rellocated array we must fix the pointer:
407 int value_offset
= value_
-menu_
;
408 menu_
= local_array
; // in case it reallocated it
409 if (value_
) value_
= menu_
+value_offset
;
416 This is a Forms (and SGI GL library) compatible add function, it
417 adds many menu items, with '|' separating the menu items, and tab
418 separating the menu item names from an optional shortcut string.
420 The passed string is split at any '|' characters and then
421 add(s,0,0,0,0) is done with each section. This is
422 often useful if you are just using the value, and is compatible
423 with Forms and other GL programs. The section strings use the
424 same special characters as described for the long version of add().
426 No items must be added to a menu during a callback to the same menu.
428 \param str string containing multiple menu labels as described above
429 \returns the index into the menu() array, where the entry was added
431 int Fl_Menu_::add(const char *str
) {
437 for (c
= buf
; c
< (buf
+ sizeof(buf
) - 2) && *str
&& *str
!= '|'; str
++) {
438 if (*str
== '\t') {*c
++ = 0; sc
= fl_old_shortcut(str
);}
442 r
= add(buf
, sc
, 0, 0, 0);
451 Changes the text of item \p i. This is the only way to get
452 slash into an add()'ed menu item. If the menu array was directly set
453 with menu(x) then copy() is done to make a private array.
455 \param i index into menu array
456 \param str new label for menu item at index i
458 void Fl_Menu_::replace(int i
, const char *str
) {
459 if (i
<0 || i
>=size()) return;
460 if (!alloc
) copy(menu_
);
462 free((void *)menu_
[i
].text
);
471 Deletes item \p i from the menu. If the menu array was directly
472 set with menu(x) then copy() is done to make a private array.
474 No items must be removed from a menu during a callback to the same menu.
476 \param i index into menu array
478 void Fl_Menu_::remove(int i
) {
480 if (i
<0 || i
>=n
) return;
481 if (!alloc
) copy(menu_
);
482 // find the next item, skipping submenus:
483 Fl_Menu_Item
* item
= menu_
+i
;
484 const Fl_Menu_Item
* next_item
= item
->next();
485 // delete the text only if all items were created with add():
487 for (Fl_Menu_Item
* m
= item
; m
< next_item
; m
++)
488 if (m
->text
) free((void*)(m
->text
));
490 // MRS: "n" is the menu size(), which includes the trailing NULL entry...
491 memmove(item
, next_item
, (menu_
+n
-next_item
)*sizeof(Fl_Menu_Item
));
495 // End of "$Id: Fl_Menu_add.cxx 8110 2010-12-23 08:02:52Z manolo $".