1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WCOMBOBOX_H_
8 #define WCOMBOBOX_H_
9 
10 #include <Wt/WFormWidget.h>
11 #include <string>
12 
13 namespace Wt {
14 
15 /*! \class WComboBox Wt/WComboBox.h Wt/WComboBox.h
16  *  \brief A widget that provides a drop-down combo-box control.
17  *
18  * A combo box provides the user with a set of options, from which one
19  * option may be selected.
20  *
21  * %WComboBox is an MVC view class, using a simple string list model
22  * by default. The model may be populated using
23  * addItem(const WString&) or
24  * insertItem(int, const WString&) and the contents can
25  * be cleared through clear(). These methods manipulate the underlying
26  * model().
27  *
28  * To use the combo box with a custom model instead of the default
29  * WStringListModel, use setModel().
30  *
31  * To react to selection events, connect to the changed(), activated()
32  * or sactivated() signals.
33  *
34  * At all times, the current selection index is available through
35  * currentIndex() and the current selection text using currentText().
36  *
37  * WComboBox does not have support for auto-completion, this behaviour
38  * can be found in the WSuggestionPopup.
39  *
40  * \if cpp
41  * Usage example:
42  * \code
43  * Wt::WComboBox *combo = addWidget(std::make_unique<Wt::WComboBox>());
44  * combo->addItem("Clint Eastwood");
45  * combo->addItem("Mick Jagger");
46  * combo->addItem("Johnny Depp");
47  * combo->addItem("Kate Winslet");
48  *
49  * combo->setCurrentIndex(2); // Johnny Depp
50  * combo->activated().connect(this, &MyWidget::comboChanged);
51  * \endcode
52  * \endif
53  *
54  * %WComboBox is an \link WWidget::setInline(bool) inline \endlink widget.
55  *
56  * <h3>CSS</h3>
57  *
58  * The widget corresponds to the HTML <tt>&lt;select&gt;</tt> tag and
59  * does not provide styling. It can be styled using inline or external
60  * CSS as appropriate.
61  *
62  * \ingroup modelview
63  */
64 class WT_API WComboBox : public WFormWidget
65 {
66 public:
67   /*! \brief Creates an empty combo-box..
68    */
69   WComboBox();
70 
71   /*! \brief Adds an option item.
72    *
73    * This adds an item to the underlying model. This requires that the
74    * model() is editable.
75    *
76    * Equivalent to
77    * \link insertItem(int, const WString&) insertItem\endlink (count(),
78    * \p text).
79    */
80   void addItem(const WString& text);
81 
82   /*! \brief Returns the number of items
83    */
84   int count() const;
85 
86   /*! \brief Returns the currently selected item.
87    *
88    * If no item is currently selected, the method returns -1.
89    *
90    * \sa setNoSelectionEnabled()
91    */
92   int currentIndex() const;
93 
94   /*! \brief Inserts an item at the specified position.
95    *
96    * The item is inserted in the underlying model at position
97    * \p index. This requires that the model() is editable.
98    *
99    * \sa addItem(const WString&), removeItem(int)
100    */
101   void insertItem(int index, const WString& text);
102 
103   /*! \brief Removes the item at the specified position.
104    *
105    * The item is removed from the underlying model. This requires that
106    * the model() is editable.
107    *
108    * \sa insertItem(int index, const WString&), clear()
109    */
110   void removeItem(int index);
111 
112   /*! \brief Changes the current selection.
113    *
114    * Specify a value of -1 for \p index to clear the selection.
115    *
116    * \note Setting a value of -1 works only if JavaScript is available.
117    */
118   void setCurrentIndex(int index);
119 
120   /*! \brief Changes the text for a specified option.
121    *
122    * The text for the item at position \p index is changed. This requires
123    * that the model() is editable.
124    */
125   void setItemText(int index, const WString& text);
126 
127   /*! \brief Returns the text of the currently selected item.
128    *
129    * \sa currentIndex(), itemText(int) const
130    */
131   const WString currentText() const;
132 
133   /*! \brief Returns the text of a particular item.
134    *
135    * \sa setItemText(int, const WString&), currentText()
136    */
137   const WString itemText(int index) const;
138 
139   /*! \brief Sets the model to be used for the items.
140    *
141    * The default model is a WStringListModel.
142    *
143    * Items in the model can be grouped by setting the \ref
144    * ItemDataRole::Level. The contents is interpreted by \ref Wt::asString, and
145    * subsequent items of the same group are rendered as children of a
146    * HTML <tt> <optgroup> </tt>element.
147    *
148    * \sa setModelColumn(int)
149    */
150   void setModel(const std::shared_ptr<WAbstractItemModel> model);
151 
152   /*! \brief Sets the column in the model to be used for the items.
153    *
154    * The column \p index in the model will be used to retrieve data.
155    *
156    * The default value is 0.
157    *
158    * \sa setModel()
159    */
160   void setModelColumn(int index);
161 
162   /*! \brief Returns the data model.
163    *
164    * \sa setModel()
165    */
model()166   std::shared_ptr<WAbstractItemModel> model() const { return model_; }
167 
168   /*! \brief Returns the index of the first item that matches a text.
169    */
170   int findText(const WString& text,
171 	       WFlags<MatchFlag> flags
172 	       = MatchFlag::Exactly | MatchFlag::CaseSensitive) const;
173 
174   /*! \brief Returns the selection mode.
175    *
176    * Always returns SelectionMode::Single for a combo box, but may return
177    * SelectionMode::Extended for a selection box
178    *
179    * \sa WSelectionBox::setSelectionMode()
180    */
selectionMode()181   virtual SelectionMode selectionMode() const {
182     return SelectionMode::Single;
183   }
184 
185   /*! \brief Returns the current value.
186    *
187    * \if cpp
188    * Returns currentText().
189    * \else
190    * Returns currentText() as a String.
191    * \endif
192    */
193   virtual WT_USTRING valueText() const override;
194 
195   /*! \brief Sets the current value.
196    *
197    * Sets the current index to the item corresponding to \p value.
198    */
199   virtual void setValueText(const WT_USTRING& value) override;
200 
201   virtual void refresh() override;
202 
203   /*! \brief Clears all items.
204    *
205    * Removes all items from the underlying model. This requires that the
206    * model() is editable.
207    */
208   void clear();
209 
210   /*! \brief %Signal emitted when the selection changed.
211    *
212    * The newly selected item is passed as an argument.
213    *
214    * \sa sactivated(), currentIndex()
215    */
activated()216   Signal<int>& activated() { return activated_; }
217 
218   /*! \brief %Signal emitted when the selection changed.
219    *
220    * The newly selected text is passed as an argument.
221    *
222    * \sa activated(), currentText()
223    */
sactivated()224   Signal<WString>& sactivated() { return sactivated_; }
225 
226   /*! \brief Enables the ability to have 'no currently selected' item.
227    *
228    * The setting may only be changed for a combo box (and not for a
229    * selection box). When enabled, the currentIndex() may be '-1' also
230    * when the combo box contains values. The user can however not
231    * select this option, it is thus only useful as a default value.
232    *
233    * By default, no selection is \c false for a combo-box and \c true
234    * for a selection box.
235    */
236   void setNoSelectionEnabled(bool enabled);
237 
238   /*! \brief Returns whether 'no selection' is a valid state.
239    *
240    * \sa setNoSelectionEnabled()
241    */
noSelectionEnabled()242   bool noSelectionEnabled() const { return noSelectionEnabled_; }
243 
244 private:
245   std::shared_ptr<WAbstractItemModel> model_;
246   int modelColumn_;
247   int currentIndex_;
248   void *currentIndexRaw_;
249 
250   bool itemsChanged_;
251   bool selectionChanged_;
252   bool currentlyConnected_;
253   bool noSelectionEnabled_;
254 
255   std::vector<Wt::Signals::connection> modelConnections_;
256 
257   Signal<int> activated_;
258   Signal<WString> sactivated_;
259 
260   void layoutChanged();
261   void itemsChanged();
262   void propagateChange();
263 
264   void rowsInserted(const WModelIndex &index, int from, int to);
265   void rowsRemoved(const WModelIndex &index, int from, int to);
266   void saveSelection();
267   void restoreSelection();
268 
269   virtual bool supportsNoSelection() const;
270 
271 protected:
272   virtual void updateDom(DomElement& element, bool all) override;
273   virtual DomElementType domElementType() const override;
274   virtual void propagateRenderOk(bool deep) override;
275 
276   virtual void setFormData(const FormData& formData) override;
277 
278   virtual bool isSelected(int index) const;
279 
280   friend class WSelectionBox;
281 
282 private:
283   void makeCurrentIndexValid();
284 };
285 
286 }
287 
288 #endif // WCOMBOBOX_H_
289