2 // "$Id: Fl_Menu_Type.cxx 8645 2011-05-10 16:46:42Z manolo $"
4 // Menu item code for the Fast Light Tool Kit (FLTK).
6 // Menu items are kludged by making a phony Fl_Box widget so the normal
7 // widget panel can be used to control them.
9 // This file also contains code to make Fl_Menu_Button, Fl_Menu_Bar,
12 // Copyright 1998-2010 by Bill Spitzak and others.
14 // This library is free software; you can redistribute it and/or
15 // modify it under the terms of the GNU Library General Public
16 // License as published by the Free Software Foundation; either
17 // version 2 of the License, or (at your option) any later version.
19 // This library is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 // Library General Public License for more details.
24 // You should have received a copy of the GNU Library General Public
25 // License along with this library; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 // Please report all bugs and problems on the following page:
31 // http://www.fltk.org/str.php
35 #include "Fl_Widget_Type.h"
36 #include "alignment_panel.h"
37 #include <FL/fl_message.H>
38 #include <FL/Fl_Menu_.H>
39 #include <FL/Fl_Button.H>
40 #include <FL/Fl_Value_Input.H>
41 #include <FL/Fl_Text_Display.H>
42 #include "../src/flstring.h"
46 Fl_Menu_Item menu_item_type_menu
[] = {
47 {"Normal",0,0,(void*)0},
48 {"Toggle",0,0,(void*)FL_MENU_BOX
},
49 {"Radio",0,0,(void*)FL_MENU_RADIO
},
52 extern int reading_file
;
53 extern int force_parent
;
55 extern const char* i18n_include
;
56 extern const char* i18n_function
;
57 extern const char* i18n_file
;
58 extern const char* i18n_set
;
60 static char submenuflag
;
62 void Fl_Input_Choice_Type::build_menu() {
63 Fl_Input_Choice
* w
= (Fl_Input_Choice
*)o
;
64 // count how many Fl_Menu_Item structures needed:
67 for (q
= next
; q
&& q
->level
> level
; q
= q
->next
) {
68 if (q
->is_parent()) n
++; // space for null at end of submenu
72 if (menusize
) delete[] (Fl_Menu_Item
*)(w
->menu());
76 n
++; // space for null at end of menu
78 if (menusize
) delete[] (Fl_Menu_Item
*)(w
->menu());
80 w
->menu(new Fl_Menu_Item
[menusize
]);
83 Fl_Menu_Item
* m
= (Fl_Menu_Item
*)(w
->menu());
85 for (q
= next
; q
&& q
->level
> level
; q
= q
->next
) {
86 Fl_Menu_Item_Type
* i
= (Fl_Menu_Item_Type
*)q
;
87 if (i
->o
->image()) i
->o
->image()->label(m
);
89 m
->label(i
->o
->label() ? i
->o
->label() : "(nolabel)");
90 m
->labeltype(i
->o
->labeltype());
92 m
->shortcut(((Fl_Button
*)(i
->o
))->shortcut());
93 m
->callback(0,(void*)i
);
94 m
->flags
= i
->flags();
95 m
->labelfont(i
->o
->labelfont());
96 m
->labelsize(i
->o
->labelsize());
97 m
->labelcolor(i
->o
->labelcolor());
98 if (q
->is_parent()) {lvl
++; m
->flags
|= FL_SUBMENU
;}
101 (q
->next
&& q
->next
->is_menu_item()) ? q
->next
->level
: level
;
102 while (lvl
> l1
) {m
->label(0); m
++; lvl
--;}
110 Fl_Type
*Fl_Menu_Item_Type::make() {
111 // Find the current menu item:
112 Fl_Type
* q
= Fl_Type::current
;
115 if ( (force_parent
&& q
->is_menu_item()) || !q
->is_parent()) p
= p
->parent
;
118 if (!p
|| !(p
->is_menu_button() || (p
->is_menu_item() && p
->is_parent()))) {
119 fl_message("Please select a menu to add to");
123 o
= new Fl_Button(0,0,100,20); // create template widget
124 o
->labelsize(Fl_Widget_Type::default_size
);
127 Fl_Menu_Item_Type
* t
= submenuflag
? new Fl_Submenu_Type() : new Fl_Menu_Item_Type();
128 t
->o
= new Fl_Button(0,0,100,20);
131 if (!reading_file
) t
->label(submenuflag
? "submenu" : "item");
135 Fl_Type
*Fl_Submenu_Type::make() {
137 Fl_Type
* t
= Fl_Menu_Item_Type::make();
142 Fl_Menu_Item_Type Fl_Menu_Item_type
;
143 Fl_Submenu_Type Fl_Submenu_type
;
145 ////////////////////////////////////////////////////////////////
146 // Writing the C code:
148 // test functions in Fl_Widget_Type.C:
149 int is_name(const char *c
);
150 const char *array_name(Fl_Widget_Type
*o
);
151 int isdeclare(const char *c
);
153 // Search backwards to find the parent menu button and return it's name.
154 // Also put in i the index into the button's menu item array belonging
155 // to this menu item.
156 const char* Fl_Menu_Item_Type::menu_name(int& i
) {
159 while (t
&& t
->is_menu_item()) {
160 // be sure to count the {0} that ends a submenu:
161 if (t
->level
> t
->next
->level
) i
+= (t
->level
- t
->next
->level
);
162 // detect empty submenu:
163 else if (t
->level
== t
->next
->level
&& t
->is_parent()) i
++;
167 return unique_id(t
, "menu", t
->name(), t
->label());
170 #include "Fluid_Image.h"
172 void Fl_Menu_Item_Type::write_static() {
173 if (callback() && is_name(callback()) && !user_defined(callback()))
174 write_declare("extern void %s(Fl_Menu_*, %s);", callback(),
175 user_data_type() ? user_data_type() : "void*");
176 for (int n
=0; n
< NUM_EXTRA_CODE
; n
++) {
177 if (extra_code(n
) && isdeclare(extra_code(n
)))
178 write_declare("%s", extra_code(n
));
180 if (callback() && !is_name(callback())) {
181 // see if 'o' or 'v' used, to prevent unused argument warnings:
185 for (d
= callback(); *d
;) {
186 if (*d
== 'o' && !is_id(d
[1])) use_o
= 1;
187 if (*d
== 'v' && !is_id(d
[1])) use_v
= 1;
188 do d
++; while (is_id(*d
));
189 while (*d
&& !is_id(*d
)) d
++;
191 const char* cn
= callback_name();
192 const char* k
= class_name(1);
194 write_c("\nvoid %s::%s_i(Fl_Menu_*", k
, cn
);
196 write_c("\nstatic void %s(Fl_Menu_*", cn
);
198 if (use_o
) write_c(" o");
199 const char* ut
= user_data_type() ? user_data_type() : "void*";
201 if (use_v
) write_c(" v");
202 write_c(") {\n %s", callback());
204 const char *p
= strrchr(callback(), '\n');
207 // Only add trailing semicolon if the last line is not a preprocessor
209 if (*p
!= '#' && *p
) write_c(";");
213 write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k
, cn
, ut
);
214 write_c(" ((%s*)(o", k
);
215 Fl_Type
* t
= parent
; while (t
->is_menu_item()) t
= t
->parent
;
217 for (t
= t
->parent
; t
&& t
->is_widget() && !is_class(); q
= t
, t
= t
->parent
)
218 write_c("->parent()");
219 if (!q
|| strcmp(q
->type_name(), "widget_class"))
220 write_c("->user_data()");
221 write_c("))->%s_i(o,v);\n}\n", cn
);
225 if (image
->written
!= write_number
) {
226 image
->write_static();
227 image
->written
= write_number
;
230 if (next
&& next
->is_menu_item()) return;
231 // okay, when we hit last item in the menu we have to write the
233 const char* k
= class_name(1);
236 if (i18n_type
) write_c("\nunsigned char %s::%s_i18n_done = 0;", k
, menu_name(i
));
237 write_c("\nFl_Menu_Item %s::%s[] = {\n", k
, menu_name(i
));
240 if (i18n_type
) write_c("\nunsigned char %s_i18n_done = 0;", menu_name(i
));
241 write_c("\nFl_Menu_Item %s[] = {\n", menu_name(i
));
243 Fl_Type
* t
= prev
; while (t
&& t
->is_menu_item()) t
= t
->prev
;
244 for (Fl_Type
* q
= t
->next
; q
&& q
->is_menu_item(); q
= q
->next
) {
245 ((Fl_Menu_Item_Type
*)q
)->write_item();
246 int thislevel
= q
->level
; if (q
->is_parent()) thislevel
++;
248 (q
->next
&& q
->next
->is_menu_item()) ? q
->next
->level
: t
->level
+1;
249 while (thislevel
> nextlevel
) {write_c(" {0,0,0,0,0,0,0,0,0},\n"); thislevel
--;}
251 write_c(" {0,0,0,0,0,0,0,0,0}\n};\n");
254 // Write menu item variables...
255 t
= prev
; while (t
&& t
->is_menu_item()) t
= t
->prev
;
256 for (Fl_Type
* q
= t
->next
; q
&& q
->is_menu_item(); q
= q
->next
) {
257 Fl_Menu_Item_Type
*m
= (Fl_Menu_Item_Type
*)q
;
258 const char *c
= array_name(m
);
261 // assign a menu item address directly to a variable
263 const char* n
= ((Fl_Menu_Item_Type
*)q
)->menu_name(i
);
264 write_c("Fl_Menu_Item* %s::%s = %s::%s + %d;\n", k
, c
, k
, n
, i
);
266 // if the name is an array, only define the array.
267 // The actual assignment is in write_code1()
268 write_c("Fl_Menu_Item* %s::%s;\n", k
, c
);
275 int Fl_Menu_Item_Type::flags() {
277 if (((Fl_Button
*)o
)->value()) i
|= FL_MENU_VALUE
;
278 if (!o
->active()) i
|= FL_MENU_INACTIVE
;
279 if (!o
->visible()) i
|= FL_MENU_INVISIBLE
;
281 if (user_data() == NULL
) i
|= FL_SUBMENU
;
282 else i
|= FL_SUBMENU_POINTER
;
284 if (hotspot()) i
|= FL_MENU_DIVIDER
;
288 void Fl_Menu_Item_Type::write_item() {
289 static const char * const labeltypes
[] = {
301 if (image
) write_c("0");
302 else if (label()) write_cstring(label()); // we will call i18n when the widget is instantiated for the first time
303 else write_c("\"\"");
304 if (((Fl_Button
*)o
)->shortcut()) {
305 int s
= ((Fl_Button
*)o
)->shortcut();
306 if (use_FL_COMMAND
&& (s
& (FL_CTRL
|FL_META
))) {
307 write_c(", FL_COMMAND|0x%x, ", s
& ~(FL_CTRL
|FL_META
));
309 write_c(", 0x%x, ", s
);
314 const char* k
= is_name(callback()) ? 0 : class_name(1);
316 write_c(" (Fl_Callback*)%s::%s,", k
, callback_name());
318 write_c(" (Fl_Callback*)%s,", callback_name());
323 write_c(" (void*)(%s),", user_data());
326 write_c(" %d, %s, %d, %d, %d", flags(),
327 labeltypes
[o
->labeltype()], o
->labelfont(), o
->labelsize(), o
->labelcolor());
331 void Fl_Menu_Item_Type::write_code1() {
332 int i
; const char* mname
= menu_name(i
);
333 if (!prev
->is_menu_item()) {
334 // for first menu item, declare the array
336 if (i18n_type
) write_h(" static unsigned char %s_i18n_done;\n", mname
);
337 write_h(" static Fl_Menu_Item %s[];\n", mname
);
339 if (i18n_type
) write_h("extern unsigned char %s_i18n_done;\n", mname
);
340 write_h("extern Fl_Menu_Item %s[];\n", mname
);
344 const char *c
= array_name(this);
347 write_public(public_
);
348 write_h(" static Fl_Menu_Item *%s;\n", c
);
351 write_h("#define %s (%s+%d)\n", c
, mname
, i
);
353 write_h("extern Fl_Menu_Item *%s;\n", c
);
358 if (!is_name(callback()) && class_name(1)) {
359 const char* cn
= callback_name();
360 const char* ut
= user_data_type() ? user_data_type() : "void*";
362 write_h(" void %s_i(Fl_Menu_*, %s);\n", cn
, ut
);
363 write_h(" static void %s(Fl_Menu_*, %s);\n", cn
, ut
);
368 // if the name is an array variable, assign the value here
369 if (name() && strchr(name(), '[')) {
370 write_c("%s%s = &%s[%d];\n", indent(), name(), mname
, i
);
375 write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), mname
, i
);
377 image
->write_code("o");
379 for (int n
=0; n
< NUM_EXTRA_CODE
; n
++)
380 if (extra_code(n
) && !isdeclare(extra_code(n
))) {
383 write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), mname
, i
);
385 write_c("%s %s\n", indent(), extra_code(n
));
387 if (init
) write_c("%s}\n",indent());
390 void Fl_Menu_Item_Type::write_code2() {}
392 ////////////////////////////////////////////////////////////////
393 // This is the base class for widgets that contain a menu (ie
394 // subclasses of Fl_Menu_.
395 // This is a parent widget and menu items can be added as
396 // children. An actual array of Fl_Menu_Items is kept parallel
397 // with the child objects and updated as they change.
399 void Fl_Menu_Type::build_menu() {
400 Fl_Menu_
* w
= (Fl_Menu_
*)o
;
401 // count how many Fl_Menu_Item structures needed:
404 for (q
= next
; q
&& q
->level
> level
; q
= q
->next
) {
405 if (q
->is_parent()) n
++; // space for null at end of submenu
409 if (menusize
) delete[] (Fl_Menu_Item
*)(w
->menu());
413 n
++; // space for null at end of menu
415 if (menusize
) delete[] (Fl_Menu_Item
*)(w
->menu());
417 w
->menu(new Fl_Menu_Item
[menusize
]);
420 Fl_Menu_Item
* m
= (Fl_Menu_Item
*)(w
->menu());
422 for (q
= next
; q
&& q
->level
> level
; q
= q
->next
) {
423 Fl_Menu_Item_Type
* i
= (Fl_Menu_Item_Type
*)q
;
424 if (i
->o
->image()) i
->o
->image()->label(m
);
426 m
->label(i
->o
->label() ? i
->o
->label() : "(nolabel)");
427 m
->labeltype(i
->o
->labeltype());
429 m
->shortcut(((Fl_Button
*)(i
->o
))->shortcut());
430 m
->callback(0,(void*)i
);
431 m
->flags
= i
->flags();
432 m
->labelfont(i
->o
->labelfont());
433 m
->labelsize(i
->o
->labelsize());
434 m
->labelcolor(i
->o
->labelcolor());
435 if (q
->is_parent()) {lvl
++; m
->flags
|= FL_SUBMENU
;}
438 (q
->next
&& q
->next
->is_menu_item()) ? q
->next
->level
: level
;
439 while (lvl
> l1
) {m
->label(0); m
++; lvl
--;}
446 Fl_Type
* Fl_Menu_Type::click_test(int, int) {
447 if (selected
) return 0; // let user move the widget
448 Fl_Menu_
* w
= (Fl_Menu_
*)o
;
449 if (!menusize
) return 0;
450 const Fl_Menu_Item
* save
= w
->mvalue();
451 w
->value((Fl_Menu_Item
*)0);
454 const Fl_Menu_Item
* m
= w
->mvalue();
456 // restore the settings of toggles & radio items:
457 if (m
->flags
& (FL_MENU_RADIO
| FL_MENU_TOGGLE
)) build_menu();
458 return (Fl_Type
*)(m
->user_data());
464 void Fl_Menu_Type::write_code2() {
465 if (next
&& next
->is_menu_item()) {
467 // take care of i18n now!
468 Fl_Menu_Item_Type
*mi
= (Fl_Menu_Item_Type
*)next
;
469 int i
, nItem
= 0, nLabel
= 0;
470 const char *mName
= mi
->menu_name(i
);
471 for (Fl_Type
* q
= next
; q
&& q
->is_menu_item(); q
= q
->next
) {
472 if (((Fl_Menu_Item_Type
*)q
)->label()) nLabel
++;
473 int thislevel
= q
->level
; if (q
->is_parent()) thislevel
++;
475 (q
->next
&& q
->next
->is_menu_item()) ? q
->next
->level
: next
->level
+1;
476 nItem
+= 1 + ((thislevel
> nextlevel
) ? (thislevel
-nextlevel
) : 0);
479 write_c("%sif (!%s_i18n_done) {\n", indent(), mName
);
480 write_c("%s int i=0;\n", indent());
481 write_c("%s for ( ; i<%d; i++)\n", indent(), nItem
);
482 write_c("%s if (%s[i].label())\n", indent(), mName
);
485 write_c("%s %s[i].label(%s(%s[i].label()));\n",
486 indent(), mName
, i18n_function
, mName
);
489 write_c("%s %s[i].label(catgets(%s,%s,i+%d,%s[i].label()));\n",
490 indent(), mName
, i18n_file
[0] ? i18n_file
: "_catalog",
491 i18n_set
, mi
->msgnum(), mName
);
494 write_c("%s %s_i18n_done = 1;\n", indent(), mName
);
495 write_c("%s}\n", indent());
498 write_c("%s%s->menu(%s);\n", indent(), name() ? name() : "o",
499 unique_id(this, "menu", name(), label()));
501 Fl_Widget_Type::write_code2();
504 void Fl_Menu_Type::copy_properties() {
505 Fl_Widget_Type::copy_properties();
506 Fl_Menu_
*s
= (Fl_Menu_
*)o
, *d
= (Fl_Menu_
*)live_widget
;
508 d
->down_box(s
->down_box());
509 d
->textcolor(s
->textcolor());
510 d
->textfont(s
->textfont());
511 d
->textsize(s
->textsize());
514 ////////////////////////////////////////////////////////////////
516 #include <FL/Fl_Menu_Button.H>
517 Fl_Menu_Item button_type_menu
[] = {
518 {"normal",0,0,(void*)0},
519 {"popup1",0,0,(void*)Fl_Menu_Button::POPUP1
},
520 {"popup2",0,0,(void*)Fl_Menu_Button::POPUP2
},
521 {"popup3",0,0,(void*)Fl_Menu_Button::POPUP3
},
522 {"popup12",0,0,(void*)Fl_Menu_Button::POPUP12
},
523 {"popup23",0,0,(void*)Fl_Menu_Button::POPUP23
},
524 {"popup13",0,0,(void*)Fl_Menu_Button::POPUP13
},
525 {"popup123",0,0,(void*)Fl_Menu_Button::POPUP123
},
528 Fl_Menu_Button_Type Fl_Menu_Button_type
;
530 ////////////////////////////////////////////////////////////////
532 Fl_Menu_Item dummymenu
[] = {{"CHOICE"},{0}};
534 Fl_Choice_Type Fl_Choice_type
;
536 Fl_Input_Choice_Type Fl_Input_Choice_type
;
538 void Fl_Input_Choice_Type::copy_properties() {
539 Fl_Widget_Type::copy_properties();
540 Fl_Input_Choice
*s
= (Fl_Input_Choice
*)o
, *d
= (Fl_Input_Choice
*)live_widget
;
542 d
->down_box(s
->down_box());
543 d
->textcolor(s
->textcolor());
544 d
->textfont(s
->textfont());
545 d
->textsize(s
->textsize());
548 Fl_Type
* Fl_Input_Choice_Type::click_test(int, int) {
549 if (selected
) return 0; // let user move the widget
550 Fl_Menu_
* w
= ((Fl_Input_Choice
*)o
)->menubutton();
551 if (!menusize
) return 0;
552 const Fl_Menu_Item
* save
= w
->mvalue();
553 w
->value((Fl_Menu_Item
*)0);
556 const Fl_Menu_Item
* m
= w
->mvalue();
558 // restore the settings of toggles & radio items:
559 if (m
->flags
& (FL_MENU_RADIO
| FL_MENU_TOGGLE
)) build_menu();
560 return (Fl_Type
*)(m
->user_data());
566 ////////////////////////////////////////////////////////////////
568 Fl_Menu_Bar_Type Fl_Menu_Bar_type
;
570 ////////////////////////////////////////////////////////////////
571 // Shortcut entry item in panel:
573 #include <FL/Fl_Output.H>
574 #include "Shortcut_Button.h"
575 #include <FL/fl_draw.H>
577 void Shortcut_Button::draw() {
578 if (value()) draw_box(FL_DOWN_BOX
, (Fl_Color
)9);
579 else draw_box(FL_UP_BOX
, FL_WHITE
);
580 fl_font(FL_HELVETICA
,14); fl_color(FL_FOREGROUND_COLOR
);
581 if (use_FL_COMMAND
&& (svalue
& (FL_CTRL
|FL_META
))) {
583 fl_snprintf(buf
, 1023, "Command+%s", fl_shortcut_label(svalue
&~(FL_CTRL
|FL_META
)));
584 fl_draw(buf
,x()+6,y(),w(),h(),FL_ALIGN_LEFT
);
586 fl_draw(fl_shortcut_label(svalue
),x()+6,y(),w(),h(),FL_ALIGN_LEFT
);
590 int Shortcut_Button::handle(int e
) {
591 when(0); type(FL_TOGGLE_BUTTON
);
592 if (e
== FL_KEYBOARD
) {
593 if (!value()) return 0;
594 int v
= Fl::event_text()[0];
595 if ( (v
> 32 && v
< 0x7f) || (v
> 0xa0 && v
<= 0xff) ) {
600 v
= v
| (Fl::event_state()&(FL_META
|FL_ALT
|FL_CTRL
));
602 v
= (Fl::event_state()&(FL_META
|FL_ALT
|FL_CTRL
|FL_SHIFT
)) | Fl::event_key();
603 if (v
== FL_BackSpace
&& svalue
) v
= 0;
605 if (v
!= svalue
) {svalue
= v
; set_changed(); redraw(); do_callback(); }
607 } else if (e
== FL_UNFOCUS
) {
608 int c
= changed(); value(0); if (c
) set_changed();
610 } else if (e
== FL_FOCUS
) {
613 int r
= Fl_Button::handle(e
);
614 if (e
== FL_RELEASE
&& value() && Fl::focus() != this) take_focus();
619 void shortcut_in_cb(Shortcut_Button
* i
, void* v
) {
621 if (current_widget
->is_button())
622 i
->svalue
= ((Fl_Button
*)(current_widget
->o
))->shortcut();
623 else if (current_widget
->is_input())
624 i
->svalue
= ((Fl_Input_
*)(current_widget
->o
))->shortcut();
625 else if (current_widget
->is_value_input())
626 i
->svalue
= ((Fl_Value_Input
*)(current_widget
->o
))->shortcut();
627 else if (current_widget
->is_text_display())
628 i
->svalue
= ((Fl_Text_Display
*)(current_widget
->o
))->shortcut();
637 for (Fl_Type
*o
= Fl_Type::first
; o
; o
= o
->next
)
638 if (o
->selected
&& o
->is_button()) {
639 Fl_Button
* b
= (Fl_Button
*)(((Fl_Widget_Type
*)o
)->o
);
640 if (b
->shortcut()!=i
->svalue
) mod
= 1;
641 b
->shortcut(i
->svalue
);
642 if (o
->is_menu_item()) ((Fl_Widget_Type
*)o
)->redraw();
643 } else if (o
->selected
&& o
->is_input()) {
644 Fl_Input_
* b
= (Fl_Input_
*)(((Fl_Widget_Type
*)o
)->o
);
645 if (b
->shortcut()!=i
->svalue
) mod
= 1;
646 b
->shortcut(i
->svalue
);
647 } else if (o
->selected
&& o
->is_value_input()) {
648 Fl_Value_Input
* b
= (Fl_Value_Input
*)(((Fl_Widget_Type
*)o
)->o
);
649 if (b
->shortcut()!=i
->svalue
) mod
= 1;
650 b
->shortcut(i
->svalue
);
651 } else if (o
->selected
&& o
->is_text_display()) {
652 Fl_Text_Display
* b
= (Fl_Text_Display
*)(((Fl_Widget_Type
*)o
)->o
);
653 if (b
->shortcut()!=i
->svalue
) mod
= 1;
654 b
->shortcut(i
->svalue
);
656 if (mod
) set_modflag(1);
661 // End of "$Id: Fl_Menu_Type.cxx 8645 2011-05-10 16:46:42Z manolo $".