|
@@ -0,0 +1,478 @@
|
|
1
|
+@** A Widget for Selecting Date Ranges.
|
|
2
|
+
|
|
3
|
+\noindent Many of the reports in Typica operate over a range of dates. In these
|
|
4
|
+cases it should generally be possible to set that range to any arbitrary start
|
|
5
|
+or end, however there are some ranges that are commonly useful where it may be
|
|
6
|
+convenient to provide easy access to that range. While Qt provides a widget
|
|
7
|
+for selecting a single date, it does not provide a widget that allows two dates
|
|
8
|
+to be conveniently selected. One approach which Typica has previously taken is
|
|
9
|
+to simply use two |QDateEdit| widgets. This works, however validation that the
|
|
10
|
+range is valid must then be performed in every report that uses such an
|
|
11
|
+approach. Another down side to this is that changing either side of the date
|
|
12
|
+range is either going to result in a database query to obtain results in the
|
|
13
|
+new range or another button must be introduced to make setting a new range
|
|
14
|
+explicit. One typically wants to adjust both sides of the range at the same
|
|
15
|
+time and only have one trip to the database for the new data and increasing the
|
|
16
|
+number of controls required for each filter quickly creates a mess.
|
|
17
|
+
|
|
18
|
+The solution to this is the introduction of a new composite widget for
|
|
19
|
+selecting date ranges. The main widget consists of two parts. First there is a
|
|
20
|
+|QComboBox| which contains many common date ranges. A |QToolButton| is also
|
|
21
|
+provided for convenient one click access to the Custom range. Whether selected
|
|
22
|
+from the |QComboBox| or the |QToolButton|, selecting Custom creates a new pop
|
|
23
|
+up widget containing two |QCalendarWidget|s and a button to explicitly set the
|
|
24
|
+range. This button will not be available unless the selected ending date is not
|
|
25
|
+before the selected starting date.
|
|
26
|
+
|
|
27
|
+As the common use for the selected date is database operations, convenient
|
|
28
|
+access to the ISO 8601 string representation of these dates is provided.
|
|
29
|
+
|
|
30
|
+@(daterangeselector.h@>=
|
|
31
|
+
|
|
32
|
+#include <QComboBox>
|
|
33
|
+
|
|
34
|
+#ifndef TypicaDateRangeSelectorHeader
|
|
35
|
+#define TypicaDateRangeSelectorHeader
|
|
36
|
+
|
|
37
|
+class CustomDateRangePopup;
|
|
38
|
+
|
|
39
|
+class DateRangeSelector : public QWidget
|
|
40
|
+{
|
|
41
|
+ @[Q_OBJECT@]@;
|
|
42
|
+ public:@/
|
|
43
|
+ DateRangeSelector(QWidget *parent = NULL);
|
|
44
|
+ void setCustomRange(QVariant range);
|
|
45
|
+ Q_INVOKABLE QVariant currentRange();@/
|
|
46
|
+ @[public slots@]:@/
|
|
47
|
+ void setCurrentIndex(int index);
|
|
48
|
+ void setLifetimeRange(QString startDate, QString endDate);
|
|
49
|
+ void removeIndex(int index);@/
|
|
50
|
+ @[signals@]:@/
|
|
51
|
+ void rangeUpdated(QVariant);
|
|
52
|
+ @[private slots@]:@/
|
|
53
|
+ void toggleCustom();
|
|
54
|
+ void popupHidden();
|
|
55
|
+ void updateRange(int index);@/
|
|
56
|
+ private:@/
|
|
57
|
+ QComboBox *quickSelector;
|
|
58
|
+ CustomDateRangePopup *customRangeSelector;
|
|
59
|
+ int lastIndex;
|
|
60
|
+};
|
|
61
|
+
|
|
62
|
+#endif
|
|
63
|
+
|
|
64
|
+@ Implementation details are in a different file.
|
|
65
|
+
|
|
66
|
+@(daterangeselector.cpp@>=
|
|
67
|
+#include <QCalendarWidget>
|
|
68
|
+#include <QPushButton>
|
|
69
|
+#include <QBoxLayout>
|
|
70
|
+#include <QLabel>
|
|
71
|
+#include <QToolButton>
|
|
72
|
+#include <QApplication>
|
|
73
|
+#include <QDesktopWidget>
|
|
74
|
+
|
|
75
|
+#include "daterangeselector.h"
|
|
76
|
+
|
|
77
|
+@<CustomDateRangePopup declaration@>
|
|
78
|
+@<CustomDateRangePopup implementation@>
|
|
79
|
+@<DateRangeSelector implementation@>
|
|
80
|
+
|
|
81
|
+#include "moc_daterangeselector.cpp"
|
|
82
|
+
|
|
83
|
+@ The custom range pop up is represented as a separate class which is not to be
|
|
84
|
+instantiated except by |DateRangeSelector|.
|
|
85
|
+
|
|
86
|
+@<CustomDateRangePopup declaration@>=
|
|
87
|
+class CustomDateRangePopup : public QWidget
|
|
88
|
+{
|
|
89
|
+ @[Q_OBJECT@]@;
|
|
90
|
+ public:@/
|
|
91
|
+ CustomDateRangePopup(QWidget *parent = NULL);@/
|
|
92
|
+ @[public slots@]:@/
|
|
93
|
+ void applyRange();@/
|
|
94
|
+ @[signals@]:@/
|
|
95
|
+ void hidingPopup();@/
|
|
96
|
+ protected:@/
|
|
97
|
+ virtual void hideEvent(QHideEvent *event);@/
|
|
98
|
+ @[private slots@]:@/
|
|
99
|
+ void validateRange();@/
|
|
100
|
+ private:@/
|
|
101
|
+ QCalendarWidget *startDateSelector;
|
|
102
|
+ QCalendarWidget *endDateSelector;
|
|
103
|
+ QPushButton *applyButton;
|
|
104
|
+};
|
|
105
|
+
|
|
106
|
+@ The pop up constructor is responsible for laying out the component widgets,
|
|
107
|
+setting the dates selected in each calendar to match the currently selected
|
|
108
|
+range, and connecting the appropriate signal handlers.
|
|
109
|
+
|
|
110
|
+@<CustomDateRangePopup implementation@>=
|
|
111
|
+CustomDateRangePopup::CustomDateRangePopup(QWidget *parent) :
|
|
112
|
+ QWidget(parent, Qt::Popup), startDateSelector(new QCalendarWidget),
|
|
113
|
+ endDateSelector(new QCalendarWidget), applyButton(new QPushButton(tr("Apply")))
|
|
114
|
+{
|
|
115
|
+ setAttribute(Qt::WA_WindowPropagation);
|
|
116
|
+
|
|
117
|
+ QVBoxLayout *outerLayout = new QVBoxLayout;
|
|
118
|
+ QHBoxLayout *calendarsLayout = new QHBoxLayout;
|
|
119
|
+ QVBoxLayout *startDateLayout = new QVBoxLayout;
|
|
120
|
+ QVBoxLayout *endDateLayout = new QVBoxLayout;
|
|
121
|
+ QHBoxLayout *buttonLayout = new QHBoxLayout;
|
|
122
|
+ QLabel *startDateLabel = new QLabel(tr("From"));
|
|
123
|
+ QLabel *endDateLabel = new QLabel(tr("To"));
|
|
124
|
+ startDateSelector->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
|
|
125
|
+ endDateSelector->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
|
|
126
|
+ DateRangeSelector *selector = qobject_cast<DateRangeSelector *>(parent);
|
|
127
|
+ if(parent) {
|
|
128
|
+ QStringList range = selector->currentRange().toStringList();
|
|
129
|
+ startDateSelector->setSelectedDate(QDate::fromString(range.first(), Qt::ISODate));
|
|
130
|
+ endDateSelector->setSelectedDate(QDate::fromString(range.last(), Qt::ISODate));
|
|
131
|
+ }
|
|
132
|
+ connect(startDateSelector, SIGNAL(selectionChanged()), this, SLOT(validateRange()));
|
|
133
|
+ connect(endDateSelector, SIGNAL(selectionChanged()), this, SLOT(validateRange()));
|
|
134
|
+
|
|
135
|
+ startDateLayout->addWidget(startDateLabel);
|
|
136
|
+ startDateLayout->addWidget(startDateSelector);
|
|
137
|
+ endDateLayout->addWidget(endDateLabel);
|
|
138
|
+ endDateLayout->addWidget(endDateSelector);
|
|
139
|
+
|
|
140
|
+ connect(applyButton, SIGNAL(clicked()), this, SLOT(applyRange()));
|
|
141
|
+
|
|
142
|
+ buttonLayout->addStretch();
|
|
143
|
+ buttonLayout->addWidget(applyButton);
|
|
144
|
+
|
|
145
|
+ calendarsLayout->addLayout(startDateLayout);
|
|
146
|
+ calendarsLayout->addLayout(endDateLayout);
|
|
147
|
+ outerLayout->addLayout(calendarsLayout);
|
|
148
|
+ outerLayout->addLayout(buttonLayout);
|
|
149
|
+ setLayout(outerLayout);
|
|
150
|
+}
|
|
151
|
+
|
|
152
|
+@ The pop up can be hidden in two ways. Clicking anywhere outside of the widget
|
|
153
|
+will hide the pop up. Clicking the Apply button will also hide the pop up. In
|
|
154
|
+the former case, we must inform the parent widget that it is fine to destroy
|
|
155
|
+the pop up widget, which we do by emitting a signal. Note that clicking outside
|
|
156
|
+of the widget will cause the |QHideEvent| to be posted automatically.
|
|
157
|
+
|
|
158
|
+@<CustomDateRangePopup implementation@>=
|
|
159
|
+void CustomDateRangePopup::hideEvent(QHideEvent *)
|
|
160
|
+{
|
|
161
|
+ emit hidingPopup();
|
|
162
|
+}
|
|
163
|
+
|
|
164
|
+@ Clicking the Apply button requires setting the Custom date range to the
|
|
165
|
+currently selected range and then hiding the pop up manually.
|
|
166
|
+
|
|
167
|
+@<CustomDateRangePopup implementation@>=
|
|
168
|
+void CustomDateRangePopup::applyRange()
|
|
169
|
+{
|
|
170
|
+ DateRangeSelector *selector = qobject_cast<DateRangeSelector *>(parentWidget());
|
|
171
|
+ if(selector)
|
|
172
|
+ {
|
|
173
|
+ selector->setCustomRange(QVariant(QStringList() <<
|
|
174
|
+ startDateSelector->selectedDate().toString(Qt::ISODate) <<
|
|
175
|
+ endDateSelector->selectedDate().toString(Qt::ISODate)));
|
|
176
|
+ }
|
|
177
|
+ hide();
|
|
178
|
+}
|
|
179
|
+
|
|
180
|
+@ The Apply button is enabled or disabled depending on if the currently
|
|
181
|
+selected dates form a valid range in which the end date does not occur before
|
|
182
|
+the start date.
|
|
183
|
+
|
|
184
|
+@<CustomDateRangePopup implementation@>=
|
|
185
|
+void CustomDateRangePopup::validateRange()
|
|
186
|
+{
|
|
187
|
+ if(startDateSelector->selectedDate() > endDateSelector->selectedDate())
|
|
188
|
+ {
|
|
189
|
+ applyButton->setEnabled(false);
|
|
190
|
+ }
|
|
191
|
+ else
|
|
192
|
+ {
|
|
193
|
+ applyButton->setEnabled(true);
|
|
194
|
+ }
|
|
195
|
+}
|
|
196
|
+
|
|
197
|
+@ The |DateRangeSelector| constructor is responsible for setting up the layout
|
|
198
|
+of the |QComboBox| and the |QToolButton|, adding appropriate items to the
|
|
199
|
+|QComboBox|, and connecting the signals required to handle the pop up
|
|
200
|
+correctly.
|
|
201
|
+
|
|
202
|
+@<DateRangeSelector implementation@>=
|
|
203
|
+DateRangeSelector::DateRangeSelector(QWidget *parent) :
|
|
204
|
+ QWidget(parent), quickSelector(new QComboBox(this)),
|
|
205
|
+ customRangeSelector(NULL), lastIndex(0)
|
|
206
|
+{
|
|
207
|
+ connect(quickSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateRange(int)));
|
|
208
|
+
|
|
209
|
+ QDate currentDate = QDate::currentDate();
|
|
210
|
+
|
|
211
|
+ QHBoxLayout *layout = new QHBoxLayout;
|
|
212
|
+ @<Set common date ranges to quick selector@>@;
|
|
213
|
+ QToolButton *customButton = new QToolButton;
|
|
214
|
+ customButton->setIcon(QIcon::fromTheme("office-calendar",
|
|
215
|
+ QIcon(":/resources/icons/tango/scalable/apps/office-calendar.svg")));
|
|
216
|
+ layout->addWidget(quickSelector);
|
|
217
|
+ layout->addWidget(customButton);
|
|
218
|
+ setLayout(layout);
|
|
219
|
+
|
|
220
|
+ connect(customButton, SIGNAL(clicked()), this, SLOT(toggleCustom()));
|
|
221
|
+}
|
|
222
|
+
|
|
223
|
+@ The |QComboBox| provides a mechanism for associating additional data with an
|
|
224
|
+item. Several possible representations were considered, but what was ultimately
|
|
225
|
+selected was a |QVariant| containing a |QStringList| in which the first entry
|
|
226
|
+in the list is the starting date of the range and the last entry in the list is
|
|
227
|
+the ending date of the range. Note that the list may contain only one item in
|
|
228
|
+cases where the range only covers a single date, however one should not assume
|
|
229
|
+that a range covering a single date will only have a single list entry.
|
|
230
|
+
|
|
231
|
+@<Set common date ranges to quick selector@>=
|
|
232
|
+quickSelector->addItem("Yesterday", QVariant(QStringList() <<
|
|
233
|
+ currentDate.addDays(-1).toString(Qt::ISODate)));
|
|
234
|
+quickSelector->addItem("Today", QVariant(QStringList() <<
|
|
235
|
+ currentDate.toString(Qt::ISODate)));
|
|
236
|
+quickSelector->insertSeparator(quickSelector->count());
|
|
237
|
+quickSelector->addItem("This Week", QVariant(QStringList() <<
|
|
238
|
+ (currentDate.dayOfWeek() % 7 ?
|
|
239
|
+ currentDate.addDays(-currentDate.dayOfWeek()).toString(Qt::ISODate) :
|
|
240
|
+ currentDate.toString(Qt::ISODate)) <<
|
|
241
|
+ currentDate.addDays(6 - (currentDate.dayOfWeek() % 7)).toString(Qt::ISODate)));
|
|
242
|
+quickSelector->addItem("This Week to Date", currentDate.dayOfWeek() % 7 ?
|
|
243
|
+ QVariant(QStringList() <<
|
|
244
|
+ currentDate.addDays(-currentDate.dayOfWeek()).toString(Qt::ISODate) <<
|
|
245
|
+ currentDate.toString(Qt::ISODate)) :
|
|
246
|
+ QVariant(QStringList() << currentDate.toString(Qt::ISODate)));
|
|
247
|
+quickSelector->addItem("Last Week", QVariant(QStringList() <<
|
|
248
|
+ currentDate.addDays(-(currentDate.dayOfWeek() % 7) - 7).toString(Qt::ISODate) <<
|
|
249
|
+ currentDate.addDays(-(currentDate.dayOfWeek() % 7) - 1).toString(Qt::ISODate)));
|
|
250
|
+quickSelector->addItem("Last 7 Days", QVariant(QStringList() <<
|
|
251
|
+ currentDate.addDays(-6).toString(Qt::ISODate) <<
|
|
252
|
+ currentDate.toString(Qt::ISODate)));
|
|
253
|
+quickSelector->insertSeparator(quickSelector->count());
|
|
254
|
+quickSelector->addItem("This Month", QVariant(QStringList() <<
|
|
255
|
+ QDate(currentDate.year(), currentDate.month(), 1).toString(Qt::ISODate) <<
|
|
256
|
+ QDate(currentDate.year(), currentDate.month(),
|
|
257
|
+ currentDate.daysInMonth()).toString(Qt::ISODate)));
|
|
258
|
+quickSelector->addItem("This Month to Date", (currentDate.day() == 1 ?
|
|
259
|
+ (QVariant(QStringList() << currentDate.toString(Qt::ISODate))) :
|
|
260
|
+ (QVariant(QStringList() <<
|
|
261
|
+ QDate(currentDate.year(), currentDate.month(), 1).toString(Qt::ISODate) <<
|
|
262
|
+ currentDate.toString(Qt::ISODate)))));
|
|
263
|
+quickSelector->addItem("Last Four Weeks", QVariant(QStringList() <<
|
|
264
|
+ currentDate.addDays(-27).toString(Qt::ISODate) <<
|
|
265
|
+ currentDate.toString(Qt::ISODate)));
|
|
266
|
+quickSelector->addItem("Last 30 Days", QVariant(QStringList() <<
|
|
267
|
+ currentDate.addDays(-29).toString(Qt::ISODate) <<
|
|
268
|
+ currentDate.toString(Qt::ISODate)));
|
|
269
|
+quickSelector->insertSeparator(quickSelector->count());
|
|
270
|
+quickSelector->addItem("This Quarter", QVariant(QStringList() <<
|
|
271
|
+ QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3), 1).toString(Qt::ISODate) <<
|
|
272
|
+ (currentDate.month() > 9 ?
|
|
273
|
+ QDate(currentDate.year(), 12, 31).toString(Qt::ISODate) :
|
|
274
|
+ QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3) + 3, 1).addDays(-1).toString(Qt::ISODate))));
|
|
275
|
+quickSelector->addItem("This Quarter to Date",
|
|
276
|
+ (currentDate.day() == 1 && (currentDate.month() - 1) % 3 == 0) ?
|
|
277
|
+ QVariant(QStringList() << currentDate.toString(Qt::ISODate)) :
|
|
278
|
+ QVariant(QStringList() <<
|
|
279
|
+ QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3), 1).toString(Qt::ISODate) <<
|
|
280
|
+ currentDate.toString(Qt::ISODate)));
|
|
281
|
+quickSelector->addItem("Last Quarter", currentDate.month() < 4 ?
|
|
282
|
+ QVariant(QStringList() <<
|
|
283
|
+ QDate(currentDate.year() - 1, 10, 1).toString(Qt::ISODate) <<
|
|
284
|
+ QDate(currentDate.year() - 1, 12, 31).toString(Qt::ISODate)) :
|
|
285
|
+ QVariant(QStringList() <<
|
|
286
|
+ QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3) - 3, 1).toString(Qt::ISODate) <<
|
|
287
|
+ QDate(currentDate.year(), currentDate.month() - ((currentDate.month() - 1) % 3), 1).addDays(-1).toString(Qt::ISODate)));
|
|
288
|
+quickSelector->addItem("Last 90 Days", QVariant(QStringList() <<
|
|
289
|
+ currentDate.addDays(-89).toString(Qt::ISODate) <<
|
|
290
|
+ currentDate.toString(Qt::ISODate)));
|
|
291
|
+quickSelector->insertSeparator(quickSelector->count());
|
|
292
|
+quickSelector->addItem("This Year", QVariant(QStringList() <<
|
|
293
|
+ QDate(currentDate.year(), 1, 1).toString(Qt::ISODate) <<
|
|
294
|
+ QDate(currentDate.year(), 12, 31).toString(Qt::ISODate)));
|
|
295
|
+quickSelector->addItem("This Year to Date", (currentDate.dayOfYear() == 1) ?
|
|
296
|
+ QVariant(QStringList() << currentDate.toString(Qt::ISODate)) :
|
|
297
|
+ QVariant(QStringList() << QDate(currentDate.year(), 1, 1).toString(Qt::ISODate) <<
|
|
298
|
+ currentDate.toString(Qt::ISODate)));
|
|
299
|
+quickSelector->addItem("Last Year", QVariant(QStringList() <<
|
|
300
|
+ QDate(currentDate.year() - 1, 1, 1).toString(Qt::ISODate) <<
|
|
301
|
+ QDate(currentDate.year() - 1, 12, 31).toString(Qt::ISODate)));
|
|
302
|
+quickSelector->addItem("Last 365 Days", QVariant(QStringList() <<
|
|
303
|
+ currentDate.addDays(-364).toString(Qt::ISODate) <<
|
|
304
|
+ currentDate.toString(Qt::ISODate)));
|
|
305
|
+quickSelector->insertSeparator(quickSelector->count());
|
|
306
|
+quickSelector->addItem("Lifetime");
|
|
307
|
+quickSelector->addItem("Custom");
|
|
308
|
+
|
|
309
|
+@ Special handling of the Custom range is required because it is possible to
|
|
310
|
+select this from the |QComboBox| and then not set a range. This should result
|
|
311
|
+in the selection changing back to the most recent valid selection. Creating the
|
|
312
|
+pop up in this way is handled in |updateRange()|.
|
|
313
|
+
|
|
314
|
+@<DateRangeSelector implementation@>=
|
|
315
|
+void DateRangeSelector::updateRange(int index)
|
|
316
|
+{
|
|
317
|
+ if(index != lastIndex && index == quickSelector->count() - 1)
|
|
318
|
+ {
|
|
319
|
+ toggleCustom();
|
|
320
|
+ }
|
|
321
|
+ else
|
|
322
|
+ {
|
|
323
|
+ lastIndex = index;
|
|
324
|
+ emit rangeUpdated(quickSelector->itemData(quickSelector->currentIndex()));
|
|
325
|
+ }
|
|
326
|
+}
|
|
327
|
+
|
|
328
|
+@ Resetting the range to the most recent valid selection is handled in
|
|
329
|
+|popupHidden()|.
|
|
330
|
+
|
|
331
|
+@<DateRangeSelector implementation@>=
|
|
332
|
+void DateRangeSelector::popupHidden()
|
|
333
|
+{
|
|
334
|
+ customRangeSelector->deleteLater();
|
|
335
|
+ customRangeSelector = NULL;
|
|
336
|
+ quickSelector->setCurrentIndex(lastIndex);
|
|
337
|
+}
|
|
338
|
+
|
|
339
|
+@ If Custom is set to a new valid range, |lastIndex| will have been set to
|
|
340
|
+point to the appropriate item by a call to |setCustomRange()|.
|
|
341
|
+
|
|
342
|
+@<DateRangeSelector implementation@>=
|
|
343
|
+void DateRangeSelector::setCustomRange(QVariant range)
|
|
344
|
+{
|
|
345
|
+ quickSelector->setItemData(quickSelector->count() - 1, range);
|
|
346
|
+ emit rangeUpdated(range);
|
|
347
|
+ lastIndex = quickSelector->count() - 1;
|
|
348
|
+ quickSelector->setCurrentIndex(lastIndex);
|
|
349
|
+}
|
|
350
|
+
|
|
351
|
+@ When creating the pop up, it should ideally be placed such that the left of
|
|
352
|
+the pop up is aligned with the left of the widget that is normally shown and
|
|
353
|
+immediately under it, however if this would result in part of the pop up not
|
|
354
|
+fitting on the same screen, it should be moved to make a best effort at full
|
|
355
|
+visibility.
|
|
356
|
+
|
|
357
|
+@<DateRangeSelector implementation@>=
|
|
358
|
+void DateRangeSelector::toggleCustom()
|
|
359
|
+{
|
|
360
|
+ if(!customRangeSelector) {
|
|
361
|
+ customRangeSelector = new CustomDateRangePopup(this);
|
|
362
|
+ QPoint pos = rect().bottomLeft();
|
|
363
|
+ QPoint pos2 = rect().topLeft();
|
|
364
|
+ pos = mapToGlobal(pos);
|
|
365
|
+ pos2 = mapToGlobal(pos2);
|
|
366
|
+ QSize size = customRangeSelector->sizeHint();
|
|
367
|
+ QRect screen = QApplication::desktop()->availableGeometry(pos);
|
|
368
|
+ if(pos.x()+size.width() > screen.right()) {
|
|
369
|
+ pos.setX(screen.right()-size.width());
|
|
370
|
+ }
|
|
371
|
+ pos.setX(qMax(pos.x(), screen.left()));
|
|
372
|
+ if(pos.y() + size.height() > screen.bottom()) {
|
|
373
|
+ pos.setY(pos2.y() - size.height());
|
|
374
|
+ } else if (pos.y() < screen.top()){
|
|
375
|
+ pos.setY(screen.top());
|
|
376
|
+ }
|
|
377
|
+ if(pos.y() < screen.top()) {
|
|
378
|
+ pos.setY(screen.top());
|
|
379
|
+ }
|
|
380
|
+ if(pos.y()+size.height() > screen.bottom()) {
|
|
381
|
+ pos.setY(screen.bottom()-size.height());
|
|
382
|
+ }
|
|
383
|
+ customRangeSelector->move(pos);
|
|
384
|
+ customRangeSelector->show();
|
|
385
|
+ connect(customRangeSelector, SIGNAL(hidingPopup()),
|
|
386
|
+ this, SLOT(popupHidden()));
|
|
387
|
+ }
|
|
388
|
+ else
|
|
389
|
+ {
|
|
390
|
+ customRangeSelector->close();
|
|
391
|
+ customRangeSelector->deleteLater();
|
|
392
|
+ customRangeSelector = NULL;
|
|
393
|
+ }
|
|
394
|
+}
|
|
395
|
+
|
|
396
|
+@ While a signal is emitted when the selected range changes, it is frequently
|
|
397
|
+convenient to have a way to request the currently selected range at any time.
|
|
398
|
+
|
|
399
|
+@<DateRangeSelector implementation@>=
|
|
400
|
+QVariant DateRangeSelector::currentRange()
|
|
401
|
+{
|
|
402
|
+ return quickSelector->itemData(lastIndex);
|
|
403
|
+}
|
|
404
|
+
|
|
405
|
+@ Similarly, a method is provided to set the current index of the combo box.
|
|
406
|
+
|
|
407
|
+@<DateRangeSelector implementation@>=
|
|
408
|
+void DateRangeSelector::setCurrentIndex(int index)
|
|
409
|
+{
|
|
410
|
+ quickSelector->setCurrentIndex(index);
|
|
411
|
+}
|
|
412
|
+
|
|
413
|
+@ The Lifetime range is handled somewhat differently from other ranges as there
|
|
414
|
+is no general way to know what that range should be without making unsafe
|
|
415
|
+assumptions. As such, reports are expected to remove the option, provide a
|
|
416
|
+sensible range for it, or handle this selection in a special case. The expected
|
|
417
|
+source of the lifetime date range is the result of a database query so a method
|
|
418
|
+is provided that accepts string representations of the dates. Note that this
|
|
419
|
+method must not be called if the Lifetime option is no longer the second to
|
|
420
|
+last option in the combo box.
|
|
421
|
+
|
|
422
|
+@<DateRangeSelector implementation@>=
|
|
423
|
+void DateRangeSelector::setLifetimeRange(QString startDate, QString endDate)
|
|
424
|
+{
|
|
425
|
+ quickSelector->setItemData(quickSelector->count() - 2,
|
|
426
|
+ QVariant(QStringList() << startDate << endDate));
|
|
427
|
+}
|
|
428
|
+
|
|
429
|
+@ The |removeIndex()| method is intended for removing the Lifetime option in
|
|
430
|
+cases where this is not supported. Use of this method is strongly discouraged.
|
|
431
|
+
|
|
432
|
+@<DateRangeSelector implementation@>=
|
|
433
|
+void DateRangeSelector::removeIndex(int index)
|
|
434
|
+{
|
|
435
|
+ quickSelector->removeItem(index);
|
|
436
|
+}
|
|
437
|
+
|
|
438
|
+@ To use this new control in Typica, we should provide a way to create it from
|
|
439
|
+the XML description of a window.
|
|
440
|
+
|
|
441
|
+@<Additional box layout elements@>=
|
|
442
|
+else if(currentElement.tagName() == "daterange")
|
|
443
|
+{
|
|
444
|
+ addDateRangeToLayout(currentElement, widgetStack, layoutStack);
|
|
445
|
+}
|
|
446
|
+
|
|
447
|
+@ The method for adding a date range selector to a layout is currently trivial.
|
|
448
|
+The |"id"| attribute is supported as usual, as is an |"initial"| attribute for
|
|
449
|
+setting the combo box index.
|
|
450
|
+
|
|
451
|
+@<Functions for scripting@>=
|
|
452
|
+void addDateRangeToLayout(QDomElement element, QStack<QWidget *> *,@|
|
|
453
|
+ QStack<QLayout *> *layoutStack)
|
|
454
|
+{
|
|
455
|
+ DateRangeSelector *widget = new DateRangeSelector;
|
|
456
|
+ if(element.hasAttribute("id"))
|
|
457
|
+ {
|
|
458
|
+ widget->setObjectName(element.attribute("id"));
|
|
459
|
+ }
|
|
460
|
+ if(element.hasAttribute("initial"))
|
|
461
|
+ {
|
|
462
|
+ widget->setCurrentIndex(element.attribute("initial").toInt());
|
|
463
|
+ }
|
|
464
|
+ QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
|
|
465
|
+ layout->addWidget(widget);
|
|
466
|
+}
|
|
467
|
+
|
|
468
|
+@ The prototype needs to be specified.
|
|
469
|
+
|
|
470
|
+@<Function prototypes for scripting@>=
|
|
471
|
+void addDateRangeToLayout(QDomElement element,
|
|
472
|
+ QStack<QWidget *> *widgetStack,
|
|
473
|
+ QStack<QLayout *> *layoutStack);
|
|
474
|
+
|
|
475
|
+@ Our header is also required.
|
|
476
|
+
|
|
477
|
+@<Header files to include@>=
|
|
478
|
+#include "daterangeselector.h"
|