1 //
2 // "$Id: Fl_Menu_.cxx 5190 2006-06-09 16:16:34Z mike $"
3 //
4 // Common menu code 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 // This is a base class for all items that have a menu:
29 //	Fl_Menu_Bar, Fl_Menu_Button, Fl_Choice
30 // This provides storage for a menu item, functions to add/modify/delete
31 // items, and a call for when the user picks a menu item.
32 
33 // More code in Fl_Menu_add.cxx
34 
35 #include <FL/Fl.H>
36 #include <FL/Fl_Menu_.H>
37 #include "flstring.h"
38 #include <stdio.h>
39 #include <stdlib.h>
40 
41 // Set 'pathname' of specified menuitem
42 //    If finditem==NULL, mvalue() is used (the most recently picked menuitem)
43 //    Returns:
44 //       0 : OK
45 //      -1 : item not found (name="")
46 //      -2 : 'name' not large enough (name="")
47 //
48 #define SAFE_STRCAT(s) \
49     { len += strlen(s); if ( len >= namelen ) { *name='\0'; return(-2); } else strcat(name,(s)); }
item_pathname(char * name,int namelen,const Fl_Menu_Item * finditem) const50 int Fl_Menu_::item_pathname(char *name, int namelen, const Fl_Menu_Item *finditem) const {
51     int len = 0;
52     finditem = finditem ? finditem : mvalue();
53     name[0] = '\0';
54     for ( int t=0; t<size(); t++ ) {
55         const Fl_Menu_Item *m = &(menu()[t]);
56 	if ( m->submenu() ) {				// submenu? descend
57 	    if (*name) SAFE_STRCAT("/");
58 	    if (m->label()) SAFE_STRCAT(m->label());
59 	} else {
60 	    if (m->label()) {				// menu item?
61 		if ( m == finditem ) {			// found? tack on itemname, done.
62 		    SAFE_STRCAT("/");
63 		    SAFE_STRCAT(m->label());
64 		    return(0);
65 		}
66 	    } else {					// end of submenu? pop
67 	        char *ss = strrchr(name, '/');
68 		if ( ss ) { *ss = 0; len = strlen(name); }	// "File/Edit" -> "File"
69 		else { name[0] = '\0'; len = 0; }		// "File" -> ""
70 		continue;
71 	    }
72 	}
73     }
74     *name = '\0';
75     return(-1);						// item not found
76 }
77 
78 // FIND MENU ITEM INDEX, GIVEN MENU PATHNAME
79 //     eg. "Edit/Copy"
80 //     Will also return submenus, eg. "Edit"
81 //     Returns NULL if not found.
82 //
83 const Fl_Menu_Item *
find_item(const char * name)84 Fl_Menu_::find_item(const char *name)
85 {
86   char menupath[1024] = "";	// File/Export
87 
88   for ( int t=0; t < size(); t++ ) {
89     Fl_Menu_Item *m = menu_ + t;
90 
91     if (m->flags&FL_SUBMENU) {
92       // IT'S A SUBMENU
93       // we do not support searches through FL_SUBMENU_POINTER links
94       if (menupath[0]) strlcat(menupath, "/", sizeof(menupath));
95       strlcat(menupath, m->label(), sizeof(menupath));
96       if (!strcmp(menupath, name)) return m;
97     } else {
98       if (!m->label()) {
99 	// END OF SUBMENU? Pop back one level.
100 	char *ss = strrchr(menupath, '/');
101 	if ( ss ) *ss = 0;
102 	else menupath[0] = '\0';
103 	continue;
104       }
105 
106       // IT'S A MENU ITEM
107       char itempath[1024];	// eg. Edit/Copy
108       strcpy(itempath, menupath);
109       if (itempath[0]) strlcat(itempath, "/", sizeof(itempath));
110       strlcat(itempath, m->label(), sizeof(itempath));
111       if (!strcmp(itempath, name)) return m;
112     }
113   }
114 
115   return (const Fl_Menu_Item *)0;
116 }
117 
value(const Fl_Menu_Item * m)118 int Fl_Menu_::value(const Fl_Menu_Item* m) {
119   clear_changed();
120   if (value_ != m) {value_ = m; return 1;}
121   return 0;
122 }
123 
124 // When user picks a menu item, call this.  It will do the callback.
125 // Unfortunatly this also casts away const for the checkboxes, but this
126 // was necessary so non-checkbox menus can really be declared const...
picked(const Fl_Menu_Item * v)127 const Fl_Menu_Item* Fl_Menu_::picked(const Fl_Menu_Item* v) {
128   if (v) {
129     if (v->radio()) {
130       if (!v->value()) { // they are turning on a radio item
131 	set_changed();
132 	((Fl_Menu_Item*)v)->setonly();
133       }
134       redraw();
135     } else if (v->flags & FL_MENU_TOGGLE) {
136       set_changed();
137       ((Fl_Menu_Item*)v)->flags ^= FL_MENU_VALUE;
138       redraw();
139     } else if (v != value_) { // normal item
140       set_changed();
141     }
142     value_ = v;
143     if (when()&(FL_WHEN_CHANGED|FL_WHEN_RELEASE)) {
144       if (changed() || when()&FL_WHEN_NOT_CHANGED) {
145 	if (value_ && value_->callback_) value_->do_callback((Fl_Widget*)this);
146 	else do_callback();
147       }
148     }
149   }
150   return v;
151 }
152 
153 // turn on one of a set of radio buttons
setonly()154 void Fl_Menu_Item::setonly() {
155   flags |= FL_MENU_RADIO | FL_MENU_VALUE;
156   Fl_Menu_Item* j;
157   for (j = this; ; ) {	// go down
158     if (j->flags & FL_MENU_DIVIDER) break; // stop on divider lines
159     j++;
160     if (!j->text || !j->radio()) break; // stop after group
161     j->clear();
162   }
163   for (j = this-1; ; j--) { // go up
164     if (!j->text || (j->flags&FL_MENU_DIVIDER) || !j->radio()) break;
165     j->clear();
166   }
167 }
168 
Fl_Menu_(int X,int Y,int W,int H,const char * l)169 Fl_Menu_::Fl_Menu_(int X,int Y,int W,int H,const char* l)
170 : Fl_Widget(X,Y,W,H,l) {
171   set_flag(SHORTCUT_LABEL);
172   box(FL_UP_BOX);
173   when(FL_WHEN_RELEASE_ALWAYS);
174   value_ = menu_ = 0;
175   alloc = 0;
176   selection_color(FL_SELECTION_COLOR);
177   textfont(FL_HELVETICA);
178   textsize((uchar)FL_NORMAL_SIZE);
179   textcolor(FL_FOREGROUND_COLOR);
180   down_box(FL_NO_BOX);
181 }
182 
size() const183 int Fl_Menu_::size() const {
184   if (!menu_) return 0;
185   return menu_->size();
186 }
187 
menu(const Fl_Menu_Item * m)188 void Fl_Menu_::menu(const Fl_Menu_Item* m) {
189   clear();
190   value_ = menu_ = (Fl_Menu_Item*)m;
191 }
192 
193 // this version is ok with new Fl_Menu_add code with fl_menu_array_owner:
194 
copy(const Fl_Menu_Item * m,void * ud)195 void Fl_Menu_::copy(const Fl_Menu_Item* m, void* ud) {
196   int n = m->size();
197   Fl_Menu_Item* newMenu = new Fl_Menu_Item[n];
198   memcpy(newMenu, m, n*sizeof(Fl_Menu_Item));
199   menu(newMenu);
200   alloc = 1; // make destructor free array, but not strings
201   // for convienence, provide way to change all the user data pointers:
202   if (ud) for (; n--;) {
203     if (newMenu->callback_) newMenu->user_data_ = ud;
204     newMenu++;
205   }
206 }
207 
~Fl_Menu_()208 Fl_Menu_::~Fl_Menu_() {
209   clear();
210 }
211 
212 // Fl_Menu::add() uses this to indicate the owner of the dynamically-
213 // expanding array.  We must not free this array:
214 Fl_Menu_* fl_menu_array_owner = 0;
215 
clear()216 void Fl_Menu_::clear() {
217   if (alloc) {
218     if (alloc>1) for (int i = size(); i--;)
219       if (menu_[i].text) free((void*)menu_[i].text);
220     if (this == fl_menu_array_owner)
221       fl_menu_array_owner = 0;
222     else
223       delete[] menu_;
224     menu_ = 0;
225     value_ = 0;
226     alloc = 0;
227   }
228 }
229 
230 //
231 // End of "$Id: Fl_Menu_.cxx 5190 2006-06-09 16:16:34Z mike $".
232 //
233