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 WCALENDAR_H_
8 #define WCALENDAR_H_
9 
10 #include <Wt/WCompositeWidget.h>
11 #include <Wt/WDate.h>
12 #include <set>
13 
14 namespace Wt {
15 
16 /*! \brief The calendar header format.
17  */
18 enum class CalendarHeaderFormat {
19   SingleLetterDayNames,  //!< First letter of a day (e.g. 'M' for Monday)
20   ShortDayNames,         //!< First 3 letters of a day (e.g. 'Mon' for Monday)
21   LongDayNames           //!< Full day name
22     // NoHorizontalHeader  //No horizontal header (not yet implemented)
23 };
24 
25 class WComboBox;
26 class WInPlaceEdit;
27 class WTemplate;
28 
29 /*! \class WCalendar Wt/WCalendar.h Wt/WCalendar.h
30  *  \brief A calendar.
31  *
32  * The calendar provides navigation by month and year, and indicates the
33  * current day.
34  *
35  * You can listen for single click or double click events on a
36  * calendar cell using the clicked() and activated() methods.
37  *
38  * The calendar may be configured to allow selection of single or
39  * multiple days using setSelectionMode(), and you may listen for
40  * changes in the selection using the selectionChanged()
41  * signals. Selection can also be entirely disabled in which case you
42  * can implement your own selection handling by listening for cell
43  * click events.
44  *
45  * Cell rendering may be customized by reimplementing renderCell().
46  *
47  * Internationalization is provided by the internationalization
48  * features of the Wt::WDate class.
49  *
50  * \if cpp
51  * Usage example:
52  * \code
53  * Wt::WDate today = Wt::WDate::currentDate();
54  *
55  * Wt::WCalendar *calendar = addWidget(std::make_unique<Wt::WCalendar>());
56  * calendar->browseTo(today.addMonths(1));
57  * calendar->select(today.addMonths(1).addDays(3));
58  * calendar->selected().connect(this, &MyWidget::daySelected);
59  * \endcode
60  * \endif
61  *
62  * Here is a snapshot, taken on 19/01/2010 (shown as
63  * today), and 14/01/2010 currently selected.
64  * <TABLE border="0" align="center"> <TR> <TD>
65  * \image html WCalendar-default-1.png "WCalendar with default look"
66  * </TD> <TD>
67  * \image html WCalendar-polished-1.png "WCalendar with polished look"
68  * </TD> </TR> </TABLE>
69  *
70  */
71 class WT_API WCalendar : public WCompositeWidget
72 {
73 public:
74   /*! \brief Typedef for enum Wt::CalendarHeaderFormat */
75   typedef CalendarHeaderFormat HeaderFormat;
76 
77   /*! \brief Creates a new calendar.
78    *
79    * Constructs a new calendar with English day/month names.  The
80    * calendar shows the current day, and has an empty selection.
81    */
82   WCalendar();
83 
84   /*! \brief Sets the selection mode.
85    *
86    * The default selection mode is
87    * \link Wt::SingleSelection SingleSelection\endlink.
88    */
89   void setSelectionMode(SelectionMode mode);
90 
91   /*! \brief Browses to the same month in the previous year.
92    *
93    * Displays the same month in the previous year. This does not
94    * affect the selection.
95    *
96    * This will emit the currentPageChanged() singal.
97    */
98   void browseToPreviousYear();
99 
100   /*! \brief Browses to the previous month.
101    *
102    * Displays the previous month. This does not affect the selection.
103    *
104    * This will emit the currentPageChanged() singal.
105    */
106   void browseToPreviousMonth();
107 
108   /*! \brief Browses to the same month in the next year.
109    *
110    * Displays the same month in the next year. This does not change
111    * the current selection.
112    *
113    * This will emit the currentPageChanged() singal.
114    */
115   void browseToNextYear();
116 
117   /*! \brief Browses to the next month.
118    *
119    * Displays the next month. This does not change the current selection.
120    *
121    * This will emit the currentPageChanged() singal.
122    */
123   void browseToNextMonth();
124 
125   /*! \brief Browses to a date.
126    *
127    * Displays the month which contains the given date. This does not change
128    * the current selection.
129    *
130    * This will emit the currentPageChanged() signal if another month
131    * is displayed.
132    */
133   void browseTo(const WDate& date);
134 
135   /*! \brief Returns the current month displayed
136    *
137    * Returns the month (1-12) that is currently displayed.
138    */
currentMonth()139   int currentMonth() const { return currentMonth_; }
140 
141   /*! \brief Returns the current year displayed
142    *
143    * Returns the year that is currently displayed.
144    */
currentYear()145   int currentYear() const { return currentYear_; }
146 
147   /*! \brief Clears the current selection.
148    *
149    * Clears the current selection. Will result in a selection() that is
150    * empty().
151    */
152   void clearSelection();
153 
154   /*! \brief Selects a date.
155    *
156    * Select one date. Both in single or multiple selection mode, this results
157    * in a selection() that contains exactly one date.
158    */
159   void select(const WDate& date);
160 
161   /*! \brief Selects multiple dates.
162    *
163    * Select multiple dates. In multiple selection mode, this results
164    * in a selection() that contains exactly the given dates. In single
165    * selection mode, at most one date is set.
166    */
167   void select(const std::set<WDate>& dates);
168 
169   /*! \brief Sets the horizontal header format.
170    *
171    * The default horizontal header format is CalendarHeaderFormat::ShortDayNames.
172    */
173   void setHorizontalHeaderFormat(CalendarHeaderFormat format);
174 
175   /*! \brief Returns the horizontal header format.
176    *
177    * \sa setHorizontalHeaderFormat()
178    */
horizontalHeaderFormat()179   CalendarHeaderFormat horizontalHeaderFormat() {
180     return horizontalHeaderFormat_;
181   }
182 
183   /*! \brief Sets the first day of the week.
184    *
185    * Possible values are 1 to 7. The default value is 1 ("Monday").
186    */
187   void setFirstDayOfWeek(int dayOfWeek);
188 
189   /*! \brief Returns the current selection.
190    *
191    * Returns the set of dates currently selected. In single selection mode,
192    * this set contains 0 or 1 dates.
193    */
selection()194   const std::set<WDate>& selection() const { return selection_; }
195 
196   /*! \brief %Signal emitted when the user changes the selection.
197    *
198    * Emitted after the user has changed the current selection.
199    */
selectionChanged()200   Signal<>& selectionChanged() { return selectionChanged_; }
201 
202   /*! \brief %Signal emitted when the user double-clicks a date.
203    *
204    * You may want to connect to this signal to treat a double click
205    * as the selection of a date.
206    */
activated()207   Signal<WDate>& activated() { return activated_; }
208 
209   /*! \brief %Signal emitted when the user clicks a date.
210    *
211    * You may want to connect to this signal if you want to provide a
212    * custom selection handling.
213    */
clicked()214   Signal<WDate>& clicked() { return clicked_; }
215 
216   /*! \brief %Signal emitted when the current month is changed.
217    *
218    * The method is emitted both when the change is done through the
219    * user interface or via the public API. The two parameters are
220    * respectively the new year and month.
221    */
currentPageChanged()222   Signal<int, int>& currentPageChanged() { return currentPageChanged_; }
223 
224   /*! \brief Configures the calendar to use single click for activation
225    *
226    * By default, double click will trigger activate(). Use this method
227    * if you want a single click to trigger activate() (and the now
228    * deprecated selected() method). This only applies to a
229    * single-selection calendar.
230    *
231    * If selectionMode() is set to \link Wt::SingleSelection SingleSelection\endlink,
232    * this will cause the selection to change on a single click instead of a double click.
233    *
234    * Instead of enabling single click, you can also listen to the clicked()
235    * signal to process a single click.
236    *
237    * \sa setSelectionMode()
238    */
239   void setSingleClickSelect(bool single);
240 
241   /*! \brief Sets the bottom of the valid date range.
242    *
243    * \if cpp
244    * The default is a null date constructed using WDate().
245    * \elseif java
246    * The default bottom is null.
247    * \endif
248    */
249   void setBottom(const WDate& bottom);
250 
251   /*! \brief Returns the bottom date of the valid range.
252    */
bottom()253   const WDate& bottom() const { return bottom_; }
254 
255   /*! \brief Sets the top of the valid date range.
256    *
257    * \if cpp
258    * The default is a null date constructed using WDate().
259    * \elseif java
260    * The default top is null.
261    * \endif
262    */
263   void setTop(const WDate& top);
264 
265   /*! \brief Returns the top date of the valid range.
266    */
top()267   const WDate& top() const { return top_; }
268 
269   virtual void load() override;
270 
271 protected:
272   virtual void render(WFlags<RenderFlag> renderFlags) override;
273 
274   /*! \brief Creates or updates a widget that renders a cell.
275    *
276    * The default implementation creates a WText
277    *
278    * You may want to reimplement this method if you wish to customize
279    * how a cell is rendered. When \p widget is \c 0, a new widget
280    * should be created and returned. Otherwise, you may either modify
281    * the passed \p widget, or return a new widget. If you return a new
282    * widget, the prevoius widget will be deleted.
283    */
284   virtual WWidget* renderCell(WWidget* widget, const WDate& date);
285 
286   /*! \brief Returns whether a date is selected.
287    *
288    * This is a convenience method that can be used when reimplementing
289    * renderCell().
290    */
291   bool isSelected(const WDate& date) const;
292 
293   virtual void enableAjax() override;
294 
295 private:
296   SelectionMode     selectionMode_;
297   bool              singleClickSelect_;
298   int               currentYear_;
299   int               currentMonth_;
300   CalendarHeaderFormat horizontalHeaderFormat_;
301   int               firstDayOfWeek_;
302   std::set<WDate>   selection_;
303   bool              needRenderMonth_;
304 
305   Signal<>          selectionChanged_;
306   Signal<WDate>     activated_;
307   Signal<WDate>     clicked_;
308   Signal<int, int>  currentPageChanged_;
309 
310   WDate             bottom_, top_;
311 
312   struct Coordinate {
313     int i, j;
314 
CoordinateCoordinate315     Coordinate() : i(0), j(0) { }
CoordinateCoordinate316     Coordinate(int x, int y) { i = x; j = y; }
317   };
318 
319   WTemplate *impl_;
320   WComboBox *monthEdit_;
321   WInPlaceEdit *yearEdit_;
322 
323   void create();
324   void renderMonth();
325 
326   void emitCurrentPageChanged();
327 
328   void monthChanged(int newMonth);
329   void yearChanged(WString newYear);
330   WDate dateForCell(int week, int dayOfWeek);
331 
332   void selectInCurrentMonth(const WDate& d);
333 
334   bool isInvalid(const WDate& d);
335   void cellClicked(Coordinate c);
336   void cellDblClicked(Coordinate c);
337 };
338 
339 }
340 
341 #endif // WCALENDAR_H_
342