1 //
2 // "$Id$"
3 //
4 // An input/chooser widget.
5 //            ______________  ____
6 //           |              || __ |
7 //           | input area   || \/ |
8 //           |______________||____|
9 //
10 // Copyright 1998-2010 by Bill Spitzak and others.
11 // Copyright 2004 by Greg Ercolano.
12 //
13 // This library is free software. Distribution and use rights are outlined in
14 // the file "COPYING" which should have been included with this file.  If this
15 // file is missing or damaged, see the license at:
16 //
17 //     http://www.fltk.org/COPYING.php
18 //
19 // Please report all bugs and problems on the following page:
20 //
21 //     http://www.fltk.org/str.php
22 //
23 
24 /* \file
25    Fl_Input_Choice widget . */
26 
27 #ifndef Fl_Input_Choice_H
28 #define Fl_Input_Choice_H
29 
30 #include <FL/Fl.H>
31 #include <FL/Fl_Group.H>
32 #include <FL/Fl_Input.H>
33 #include <FL/Fl_Menu_Button.H>
34 #include <FL/fl_draw.H>
35 #include <string.h>
36 
37 /**
38   A combination of the input widget and a menu button.
39 
40   \image html input_choice.jpg
41   \image latex input_choice.jpg "Fl_Input_Choice widget" width=6cm
42 
43   The user can either type into the input area, or use the
44   menu button chooser on the right to choose an item which loads
45   the input area with the selected text.
46 
47   The application can directly access both the internal Fl_Input
48   and Fl_Menu_Button widgets respectively using the input() and menubutton()
49   accessor methods.
50 
51   The default behavior is to invoke the Fl_Input_Choice::callback()
52   if the user changes the input field's contents, either by typing,
53   pasting, or clicking a different item in the choice menu.
54 
55   The callback can determine if an item was picked vs. typing
56   into the input field by checking the value of menubutton()->changed(),
57   which will be:
58 
59       - 1: the user picked a different item in the choice menu
60       - 0: the user typed or pasted directly into the input field
61 
62   Example use:
63   \code
64   #include <stdio.h>
65   #include <FL/Fl.H>
66   #include <FL/Fl_Double_Window.H>
67   #include <FL/Fl_Input_Choice.H>
68   void choice_cb(Fl_Widget *w, void *userdata) {
69     // Show info about the picked item
70     Fl_Input_Choice *choice = (Fl_Input_Choice*)w;
71     const Fl_Menu_Item *item = choice->menubutton()->mvalue();
72     printf("*** Choice Callback:\n");
73     printf("    item label()='%s'\n", item ? item->label() : "(No item)");
74     printf("    item value()=%d\n", choice->menubutton()->value());
75     printf("    input value()='%s'\n", choice->input()->value());
76     printf("    The user %s\n", choice->menubutton()->changed()
77                                     ? "picked a menu item"
78                                     : "typed text");
79   }
80   int main() {
81     Fl_Double_Window win(200,100,"Input Choice");
82     win.begin();
83       Fl_Input_Choice choice(10,10,100,30);
84       choice.callback(choice_cb, 0);
85       choice.add("Red");
86       choice.add("Orange");
87       choice.add("Yellow");
88       //choice.value("Red");    // uncomment to make "Red" default
89     win.end();
90     win.show();
91     return Fl::run();
92   }
93   \endcode
94 */
95 class FL_EXPORT Fl_Input_Choice : public Fl_Group {
96   // Private class to handle slightly 'special' behavior of menu button
97   class InputMenuButton : public Fl_Menu_Button {
draw()98     void draw() {
99       draw_box(FL_UP_BOX, color());
100       fl_color(active_r() ? labelcolor() : fl_inactive(labelcolor()));
101       int xc = x()+w()/2, yc=y()+h()/2;
102       fl_polygon(xc-5,yc-3,xc+5,yc-3,xc,yc+3);
103       if (Fl::focus() == this) draw_focus();
104     }
105   public:
106     InputMenuButton(int X,int Y,int W,int H,const char*L=0) :
Fl_Menu_Button(X,Y,W,H,L)107 	Fl_Menu_Button(X, Y, W, H, L) { box(FL_UP_BOX); }
108   };
109 
110   Fl_Input *inp_;
111   InputMenuButton *menu_;
112 
113   // note: this is used by the Fl_Input_Choice ctor defined in Fl_Group.
menu_cb(Fl_Widget *,void * data)114   static void menu_cb(Fl_Widget*, void *data) {
115     Fl_Input_Choice *o=(Fl_Input_Choice *)data;
116     Fl_Widget_Tracker wp(o);
117     const Fl_Menu_Item *item = o->menubutton()->mvalue();
118     if (item && item->flags & (FL_SUBMENU|FL_SUBMENU_POINTER)) return;	// ignore submenus
119     if (!strcmp(o->inp_->value(), o->menu_->text()))
120     {
121       o->Fl_Widget::clear_changed();
122       if (o->when() & FL_WHEN_NOT_CHANGED)
123 	o->do_callback();
124     }
125     else
126     {
127       o->inp_->value(o->menu_->text());
128       o->inp_->set_changed();
129       o->Fl_Widget::set_changed();
130       if (o->when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE))
131 	o->do_callback();
132     }
133 
134     if (wp.deleted()) return;
135 
136     if (o->callback() != default_callback)
137     {
138       o->Fl_Widget::clear_changed();
139       o->inp_->clear_changed();
140     }
141   }
142 
143   // note: this is used by the Fl_Input_Choice ctor defined in Fl_Group.
inp_cb(Fl_Widget *,void * data)144   static void inp_cb(Fl_Widget*, void *data) {
145     Fl_Input_Choice *o=(Fl_Input_Choice *)data;
146     Fl_Widget_Tracker wp(o);
147     if (o->inp_->changed()) {
148       o->Fl_Widget::set_changed();
149       if (o->when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE))
150 	o->do_callback();
151     } else {
152       o->Fl_Widget::clear_changed();
153       if (o->when() & FL_WHEN_NOT_CHANGED)
154 	o->do_callback();
155     }
156 
157     if (wp.deleted()) return;
158 
159     if (o->callback() != default_callback)
160       o->Fl_Widget::clear_changed();
161   }
162 
163   // Custom resize behavior -- input stretches, menu button doesn't
inp_x()164   inline int inp_x() { return(x() + Fl::box_dx(box())); }
inp_y()165   inline int inp_y() { return(y() + Fl::box_dy(box())); }
inp_w()166   inline int inp_w() { return(w() - Fl::box_dw(box()) - 20); }
inp_h()167   inline int inp_h() { return(h() - Fl::box_dh(box())); }
168 
menu_x()169   inline int menu_x() { return(x() + w() - 20 - Fl::box_dx(box())); }
menu_y()170   inline int menu_y() { return(y() + Fl::box_dy(box())); }
menu_w()171   inline int menu_w() { return(20); }
menu_h()172   inline int menu_h() { return(h() - Fl::box_dh(box())); }
173 
174 public:
175   /**
176     Creates a new Fl_Input_Choice widget using the given position, size,
177     and label string.
178     Inherited destructor destroys the widget and any values associated with it.
179   */
180   Fl_Input_Choice(int X,int Y,int W,int H,const char*L=0);
181 
182   /** Adds an item to the menu.
183    You can access the more complex Fl_Menu_Button::add() methods
184    (setting callbacks, userdata, etc), via menubutton(). Example:
185    \code
186    Fl_Input_Choice *choice = new Fl_Input_Choice(100,10,120,25,"Fonts");
187    Fl_Menu_Button *mb = choice->menubutton();             // use Fl_Input_Choice's Fl_Menu_Button
188    mb->add("Helvetica", 0, MyFont_CB,     (void*)mydata); // use Fl_Menu_Button's add() methods
189    mb->add("Courier",   0, MyFont_CB,     (void*)mydata);
190    mb->add("More..",    0, FontDialog_CB, (void*)mydata);
191    \endcode
192    */
add(const char * s)193   void add(const char *s) { menu_->add(s);  }
194   /** Returns the combined changed() state of the input and menu button widget. */
changed()195   int changed() const { return inp_->changed() | Fl_Widget::changed(); }
196   /** Clears the changed() state of both input and menu button widgets. */
clear_changed()197   void clear_changed() {
198     inp_->clear_changed();
199     Fl_Widget::clear_changed();
200   }
201   /** Sets the changed() state of both input and menu button widgets
202    to the specfied value.*/
set_changed()203   void set_changed() {
204     inp_->set_changed();
205     // no need to call Fl_Widget::set_changed()
206   }
207   /** Removes all items from the menu. */
clear()208   void clear() { menu_->clear(); }
209   /** Gets the box type of the menu button */
down_box()210   Fl_Boxtype down_box() const { return (menu_->down_box()); }
211   /** Sets the box type of the menu button */
down_box(Fl_Boxtype b)212   void down_box(Fl_Boxtype b) { menu_->down_box(b); }
213   /** Gets the Fl_Menu_Item array used for the menu. */
menu()214   const Fl_Menu_Item *menu() { return (menu_->menu()); }
215   /** Sets the Fl_Menu_Item array used for the menu. */
menu(const Fl_Menu_Item * m)216   void menu(const Fl_Menu_Item *m) { menu_->menu(m); }
resize(int X,int Y,int W,int H)217   void resize(int X, int Y, int W, int H) {
218     Fl_Group::resize(X,Y,W,H);
219     inp_->resize(inp_x(), inp_y(), inp_w(), inp_h());
220     menu_->resize(menu_x(), menu_y(), menu_w(), menu_h());
221   }
222   /// Gets the Fl_Input text field's text color.
textcolor()223   Fl_Color textcolor() const { return (inp_->textcolor());}
224   /// Sets the Fl_Input text field's text color to \p c.
textcolor(Fl_Color c)225   void textcolor(Fl_Color c) { inp_->textcolor(c);}
226   /// Gets the Fl_Input text field's font style.
textfont()227   Fl_Font textfont() const { return (inp_->textfont());}
228   /// Sets the Fl_Input text field's font style to \p f.
textfont(Fl_Font f)229   void textfont(Fl_Font f) { inp_->textfont(f);}
230   /// Gets the Fl_Input text field's font size
textsize()231   Fl_Fontsize textsize() const { return (inp_->textsize()); }
232   /// Sets the Fl_Input text field's font size to \p s.
textsize(Fl_Fontsize s)233   void textsize(Fl_Fontsize s) { inp_->textsize(s); }
234   /// Returns the Fl_Input text field's current contents.
value()235   const char* value() const { return (inp_->value()); }
236   /** Sets the Fl_Input text field's contents to \p val.
237    Does not affect the menu selection.*/
value(const char * val)238   void value(const char *val) { inp_->value(val); }
239   /** Chooses item# \p val in the menu, and sets the Fl_Input text field
240    to that value. Any previous text is cleared.*/
value(int val)241   void value(int val) {
242     menu_->value(val);
243     inp_->value(menu_->text(val));
244   }
245   /** Returns a pointer to the internal Fl_Menu_Button widget.
246    This can be used to access any of the methods of the menu button, e.g.
247    \code
248    Fl_Input_Choice *choice = new Fl_Input_Choice(100,10,120,25,"Choice:");
249    [..]
250    // Print all the items in the choice menu
251    for ( int t=0; t<choice->menubutton()->size(); t++ ) {
252        const Fl_Menu_Item &item = choice->menubutton()->menu()[t];
253        printf("item %d -- label=%s\n", t, item.label() ? item.label() : "(Null)");
254    }
255    \endcode
256   */
menubutton()257   Fl_Menu_Button *menubutton() { return menu_; }
258   /** Returns a pointer to the internal Fl_Input widget.
259    This can be used to directly access all of the Fl_Input widget's
260    methods.*/
input()261   Fl_Input *input() { return inp_; }
262 };
263 
264 #endif // !Fl_Input_Choice_H
265 
266 //
267 // End of "$Id$".
268 //
269