1 //
2 // "$Id: Fl_Menu_add.cxx 5190 2006-06-09 16:16:34Z mike $"
3 //
4 // Menu utilities for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2005 by Bill Spitzak and others.
7 //
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.
12 //
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.
17 //
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 //     http://www.fltk.org/str.php
26 //
27 
28 // Methods to alter the menu in an Fl_Menu_ widget.
29 
30 // These are for Forms emulation and for dynamically changing the
31 // menus.  They are in this source file so they are not linked in if
32 // not used, which is what will happen if the the program only uses
33 // constant menu tables.
34 
35 // Not at all guaranteed to be Forms compatable, especially with any
36 // string with a % sign in it!
37 
38 #include <FL/Fl_Menu_.H>
39 #include "flstring.h"
40 #include <stdio.h>
41 #include <stdlib.h>
42 
43 // If the array is this, we will double-reallocate as necessary:
44 static Fl_Menu_Item* local_array = 0;
45 static int local_array_alloc = 0; // number allocated
46 static int local_array_size = 0; // == size(local_array)
47 extern Fl_Menu_* fl_menu_array_owner; // in Fl_Menu_.cxx
48 
49 // For historical reasons there are matching methods that work on a
50 // user-allocated array of Fl_Menu_Item.  These methods are quite
51 // depreciated and should not be used.  These old methods use the
52 // above pointers to detect if the array belongs to an Fl_Menu_
53 // widget, and if so it reallocates as necessary.
54 
55 // Insert a single Fl_Menu_Item into an array of size at offset n,
56 // if this is local_array it will be reallocated if needed.
insert(Fl_Menu_Item * array,int size,int n,const char * text,int flags)57 static Fl_Menu_Item* insert(
58   Fl_Menu_Item* array, int size,
59   int n,
60   const char *text,
61   int flags
62 ) {
63   if (array == local_array && size >= local_array_alloc) {
64     local_array_alloc = 2*size;
65     Fl_Menu_Item* newarray = new Fl_Menu_Item[local_array_alloc];
66     memmove(newarray, array, size*sizeof(Fl_Menu_Item));
67     delete[] local_array;
68     local_array = array = newarray;
69   }
70   // move all the later items:
71   memmove(array+n+1, array+n, sizeof(Fl_Menu_Item)*(size-n));
72   // create the new item:
73   Fl_Menu_Item* m = array+n;
74   m->text = text ? strdup(text) : 0;
75   m->shortcut_ = 0;
76   m->callback_ = 0;
77   m->user_data_ = 0;
78   m->flags = flags;
79   m->labeltype_ = m->labelfont_ = m->labelsize_ = m->labelcolor_ = 0;
80   return array;
81 }
82 
83 // Comparison that does not care about deleted '&' signs:
compare(const char * a,const char * b)84 static int compare(const char* a, const char* b) {
85   for (;;) {
86     int n = *a-*b;
87     if (n) {
88       if (*a == '&') a++;
89       else if (*b == '&') b++;
90       else return n;
91     } else if (*a) {
92       a++; b++;
93     } else {
94       return 0;
95     }
96   }
97 }
98 
99 // Add an item.  The text is split at '/' characters to automatically
100 // produce submenus (actually a totally unnecessary feature as you can
101 // now add submenu titles directly by setting SUBMENU in the flags):
add(const char * mytext,int sc,Fl_Callback * cb,void * data,int myflags)102 int Fl_Menu_Item::add(
103   const char *mytext,
104   int sc,
105   Fl_Callback *cb,
106   void *data,
107   int myflags
108 ) {
109   Fl_Menu_Item *array = this;
110   Fl_Menu_Item *m = this;
111   const char *p;
112   char *q;
113   char buf[1024];
114 
115   int msize = array==local_array ? local_array_size : array->size();
116   int flags1 = 0;
117   const char* item;
118 
119   // split at slashes to make submenus:
120   for (;;) {
121 
122     // leading slash makes us assumme it is a filename:
123     if (*mytext == '/') {item = mytext; break;}
124 
125     // leading underscore causes divider line:
126     if (*mytext == '_') {mytext++; flags1 = FL_MENU_DIVIDER;}
127 
128     // copy to buf, changing \x to x:
129     q = buf;
130     for (p=mytext; *p && *p != '/'; *q++ = *p++) if (*p=='\\' && p[1]) p++;
131     *q = 0;
132 
133     item = buf;
134     if (*p != '/') break; /* not a menu title */
135     mytext = p+1;	/* point at item title */
136 
137     /* find a matching menu title: */
138     for (; m->text; m = m->next())
139       if (m->flags&FL_SUBMENU && !compare(item, m->text)) break;
140 
141     if (!m->text) { /* create a new menu */
142       int n = m-array;
143       array = insert(array, msize, n, item, FL_SUBMENU|flags1);
144       msize++;
145       array = insert(array, msize, n+1, 0, 0);
146       msize++;
147       m = array+n;
148     }
149     m++;	/* go into the submenu */
150     flags1 = 0;
151   }
152 
153   /* find a matching menu item: */
154   for (; m->text; m = m->next())
155     if (!(m->flags&FL_SUBMENU) && !compare(m->text,item)) break;
156 
157   if (!m->text) {	/* add a new menu item */
158     int n = m-array;
159     array = insert(array, msize, n, item, myflags|flags1);
160     msize++;
161     if (myflags & FL_SUBMENU) { // add submenu delimiter
162       array = insert(array, msize, n+1, 0, 0);
163       msize++;
164     }
165     m = array+n;
166   }
167 
168   /* fill it in */
169   m->shortcut_ = sc;
170   m->callback_ = cb;
171   m->user_data_ = data;
172   m->flags = myflags|flags1;
173 
174   if (array == local_array) local_array_size = msize;
175   return m-array;
176 }
177 
add(const char * t,int s,Fl_Callback * c,void * v,int f)178 int Fl_Menu_::add(const char *t, int s, Fl_Callback *c,void *v,int f) {
179   // make this widget own the local array:
180   if (this != fl_menu_array_owner) {
181     if (fl_menu_array_owner) {
182       Fl_Menu_* o = fl_menu_array_owner;
183       // the previous owner get's its own correctly-sized array:
184       int value_offset = o->value_-local_array;
185       int n = local_array_size;
186       Fl_Menu_Item* newMenu = o->menu_ = new Fl_Menu_Item[n];
187       memcpy(newMenu, local_array, n*sizeof(Fl_Menu_Item));
188       if (o->value_) o->value_ = newMenu+value_offset;
189     }
190     if (menu_) {
191       // this already has a menu array, use it as the local one:
192       delete[] local_array;
193       if (!alloc) copy(menu_); // duplicate a user-provided static array
194       // add to the menu's current array:
195       local_array_alloc = local_array_size = size();
196       local_array = menu_;
197     } else {
198       // start with a blank array:
199       alloc = 2; // indicates that the strings can be freed
200       if (local_array) {
201 	menu_ = local_array;
202       } else {
203 	local_array_alloc = 15;
204 	local_array = menu_ = new Fl_Menu_Item[local_array_alloc];
205         memset(local_array, 0, sizeof(Fl_Menu_Item) * local_array_alloc);
206       }
207       memset(menu_, 0, sizeof(Fl_Menu_Item));
208       local_array_size = 1;
209     }
210     fl_menu_array_owner = this;
211   }
212   int r = menu_->add(t,s,c,v,f);
213   // if it rellocated array we must fix the pointer:
214   int value_offset = value_-menu_;
215   menu_ = local_array; // in case it reallocated it
216   if (value_) value_ = menu_+value_offset;
217   return r;
218 }
219 
220 // This is a Forms (and SGI GL library) compatable add function, it
221 // adds many menu items, with '|' seperating the menu items, and tab
222 // seperating the menu item names from an optional shortcut string.
add(const char * str)223 int Fl_Menu_::add(const char *str) {
224   char buf[1024];
225   int r = 0;
226   while (*str) {
227     int sc = 0;
228     char *c;
229     for (c = buf; c < (buf + sizeof(buf) - 2) && *str && *str != '|'; str++) {
230       if (*str == '\t') {*c++ = 0; sc = fl_old_shortcut(str);}
231       else *c++ = *str;
232     }
233     *c = 0;
234     r = add(buf, sc, 0, 0, 0);
235     if (*str) str++;
236   }
237   return r;
238 }
239 
replace(int i,const char * str)240 void Fl_Menu_::replace(int i, const char *str) {
241   if (i<0 || i>=size()) return;
242   if (!alloc) copy(menu_);
243   if (alloc > 1) {
244     free((void *)menu_[i].text);
245     str = strdup(str);
246   }
247   menu_[i].text = str;
248 }
249 
remove(int i)250 void Fl_Menu_::remove(int i) {
251   int n = size();
252   if (i<0 || i>=n) return;
253   if (!alloc) copy(menu_);
254   // find the next item, skipping submenus:
255   Fl_Menu_Item* item = menu_+i;
256   const Fl_Menu_Item* next_item = item->next();
257   // delete the text only if all items were created with add():
258   if (alloc > 1) {
259     for (Fl_Menu_Item* m = item; m < next_item; m++)
260       if (m->text) free((void*)(m->text));
261   }
262   // MRS: "n" is the menu size(), which includes the trailing NULL entry...
263   memmove(item, next_item, (menu_+n-next_item)*sizeof(Fl_Menu_Item));
264 }
265 
266 //
267 // End of "$Id: Fl_Menu_add.cxx 5190 2006-06-09 16:16:34Z mike $".
268 //
269