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