added android components
[mardrone] / mardrone / imports / com / nokia / extras / DatePickerDialog.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Components project.
8 **
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
11 **
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
14 ** met:
15 **   * Redistributions of source code must retain the above copyright
16 **     notice, this list of conditions and the following disclaimer.
17 **   * Redistributions in binary form must reproduce the above copyright
18 **     notice, this list of conditions and the following disclaimer in
19 **     the documentation and/or other materials provided with the
20 **     distribution.
21 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 **     the names of its contributors may be used to endorse or promote
23 **     products derived from this software without specific prior written
24 **     permission.
25 **
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 import QtQuick 1.1
42 import com.nokia.meego 1.0
43 import "constants.js" as C
44 import "TumblerIndexHelper.js" as TH
45
46 /*
47    Class: DatePickerDialog
48    Dialog that shows a date picker.
49 */
50
51 Dialog {
52     id: root
53
54     /*
55      * Property: titleText
56      * [string] If not null, it will be used as the title text for the dialog.
57      *          If further customization is needed, use property title instead
58      */
59     property alias titleText: title.text
60
61     /*
62      * Property: year
63      * [int] The displayed year.
64      */
65     property int year: dateTime.currentYear()
66
67     /*
68      * Property: month
69      * [int] The displayed month.
70      */
71     property int month: 1
72
73     /*
74      * Property: day
75      * [int] The displayed day.
76      */
77     property int day: 1
78
79     /*
80      * Property: minimumYear
81      * [int] Optional, the minimum year shown on the tumbler. This property should
82      *       only be set once during construction. If the value is not specified,
83      *       it is default to current year - 1.
84      */
85     property int minimumYear: dateTime.currentYear() - 1
86
87     /*
88      * Property: maximumYear
89      * [int] Optional, the maximum year shown on the tumbler. This property should
90      *       only be set once during construction. If the value is not specified,
91      *       it is default to current year + 20.
92      */
93     property int maximumYear: dateTime.currentYear() + 20
94
95     /*
96      * Property: acceptButtonText
97      * [string] Optional, the button text for the accept button.
98      */
99     property alias acceptButtonText: confirmButton.text
100
101     /*
102      * Property: rejectButtonText
103      * [string] Optional, the button text for the reject button.
104      */
105     property alias rejectButtonText: rejectButton.text
106
107     // TODO do not dismiss the dialog when empty area is clicked
108     style: DialogStyle {
109         titleBarHeight: 48
110         leftMargin: screen.currentOrientation == Screen.Portrait || screen.currentOrientation == Screen.PortraitInverted ? 16 : 215
111         rightMargin: screen.currentOrientation == Screen.Portrait || screen.currentOrientation == Screen.PortraitInverted ? 16 : 215
112         centered: true
113     }
114     title: Text {
115         id: title
116         objectName: "title"
117         visible: text.length > 0
118         color: theme.selectionColor
119         font { pixelSize: 32; family: C.FONT_FAMILY_BOLD }
120         elide: Text.ElideRight
121     }
122     content: Item {
123         id: dialogContent
124         height: 300
125         width: parent.width
126
127         Tumbler {
128             id: tumbler
129
130             function _handleTumblerChanges(index) {
131                 if (index == 1 || index == 2) {
132                     var curYear = yearColumn.selectedIndex + yearList.get(0).value;
133                     var curMonth = monthColumn.selectedIndex + 1;
134
135                     var d = dateTime.daysInMonth(curYear, curMonth);
136                     if (dayColumn.selectedIndex >= d)
137                         dayColumn.selectedIndex = d - 1
138                     while (dayList.count > d)
139                         dayList.remove(dayList.count - 1)
140                     while (dayList.count < d)
141                         dayList.append({"value" : dayList.count + 1})
142                 }
143             }
144
145             columns:  [dayColumn, monthColumn, yearColumn]
146             onChanged: {
147                 _handleTumblerChanges(index);
148             }
149             height: 300
150             privateDelayInit: true
151             
152             TumblerColumn {
153                 id: dayColumn
154                 items: ListModel {
155                     id: dayList
156                 }
157                 label: "DAY"
158                 selectedIndex: root.day - (root.day > 0 ?  1 : 0)
159             }
160     
161             TumblerColumn {
162                 id: monthColumn
163                 items: ListModel {
164                     id: monthList
165                 }
166                 label: "MONTH"
167                 selectedIndex: root.month - (root.month > 0 ?  1 : 0)
168             }
169     
170             TumblerColumn {
171                 id: yearColumn
172                 items: ListModel {
173                     id: yearList
174                 }
175                 label: "YEAR"
176                 selectedIndex: yearList.length > 0 ? internal.year - yearList.get(0).value : 0
177             }
178         }
179     }
180     buttons: Row {
181         height: 56
182         anchors.horizontalCenter: parent.horizontalCenter
183         spacing: 6
184         Button {
185             id: confirmButton
186             text: "CONFIRM"
187             onClicked: accept()
188             width: (root.width / 2) - 3
189             style: ButtonStyle { inverted: true }
190         }
191         Button {
192             id: rejectButton
193             text: "CANCEL"
194             onClicked: reject()
195             width: (root.width / 2) - 3
196             platformStyle: ButtonStyle { inverted: true }
197         }
198     }
199     onMinimumYearChanged: {
200         if (!internal.surpassUpdate) {
201             internal.year = root.year
202             internal.minYear = root.minimumYear
203             
204             if (internal.minYear < 0)
205                 internal.minYear = dateTime.currentYear() - 1;
206             else if (internal.minYear > root.maximumYear)
207                 internal.minYear = root.maximumYear;
208
209             internal.updateYearList()
210             internal.validateDate()
211             internal.year = internal.year < internal.minYear ? internal.minYear : 
212                             (internal.year > root.maximumYear ? root.maximumYear :internal.year) 
213         }
214     }
215     onMaximumYearChanged: {
216         internal.minYear = root.minimumYear
217         
218         if (root.maximumYear < 0)
219             root.maximumYear = dateTime.currentYear() + 20;
220         else if (root.maximumYear < internal.minYear)
221             root.maximumYear = internal.minYear;
222
223         internal.updateYearList()
224         internal.validateDate()
225         internal.year = internal.year > root.maximumYear ? root.maximumYear : 
226                         (internal.year < internal.minYear ? internal.minYear : internal.year)
227         if (internal.minYear < 0)
228             root.minimumYear = dateTime.currentYear() - 1
229     }
230     onStatusChanged: {
231         if (status == DialogStatus.Opening) {
232             TH.saveIndex(tumbler);
233             if (!internal.initialized)
234                 internal.initializeDataModels();
235             if (internal.year > 0)
236                 yearColumn.selectedIndex = internal.year - yearList.get(0).value;
237             tumbler._handleTumblerChanges(2);
238             dayColumn.selectedIndex = root.day - 1;
239         }
240         if (status == DialogStatus.Closing) {
241             internal.surpassUpdate = true
242             if (internal.surpassUpdate) {
243                 root.year = internal.year
244                 root.minimumYear = internal.minYear
245             }
246             internal.surpassUpdate = false
247         }
248     }
249     onDayChanged: {
250         internal.validateDate()
251         if (dayColumn.items.length > root.day - 1)
252             dayColumn.selectedIndex = root.day - 1
253     }
254     onMonthChanged: {
255         internal.validateDate()
256         monthColumn.selectedIndex = root.month - 1
257     }
258     onYearChanged: {
259         if (!internal.surpassUpdate) {
260             internal.year = root.year
261             internal.validateDate()
262             internal.year = internal.year < internal.minYear ? internal.minYear : 
263                                   (internal.year > root.maximumYear ? root.maximumYear : internal.year)
264         
265             if (internal.initialized)
266                 yearColumn.selectedIndex = internal.year - yearList.get(0).value  
267         }
268     }
269     onAccepted: {
270         tumbler.privateForceUpdate();
271         root.year = yearColumn.selectedIndex + yearList.get(0).value;
272         root.month = monthColumn.selectedIndex + 1;
273         root.day = dayColumn.selectedIndex + 1;
274     }
275     onRejected: {
276         TH.restoreIndex(tumbler);
277     }
278
279     QtObject {
280         id: internal
281
282         property variant initialized: false
283         property int year
284         property int minYear
285         property bool surpassUpdate: false
286
287         function initializeDataModels() {
288             var currentYear = new Date().getFullYear();
289             minimumYear = minimumYear ? minimumYear : currentYear - 1;
290             maximumYear = maximumYear ? maximumYear : currentYear + 20;
291
292             for (var y = minimumYear; y <= maximumYear; ++y)
293                 yearList.append({"value" : y}) // year
294
295             var nDays = dateTime.daysInMonth(internal.year, root.month);
296             for (var d = 1; d <= nDays; ++d)
297                 dayList.append({"value" : d})  // day
298             for (var m = 1; m <= 12; ++m)
299                 monthList.append({"value" : dateTime.shortMonthName(m)});
300
301             tumbler.privateInitialize();
302             internal.initialized = true;
303         }
304
305         function updateYearList() {
306             if (internal.initialized) {
307                 var tmp = yearColumn.selectedIndex;
308                 yearList.clear();
309                 for (var i = internal.minYear; i <= root.maximumYear; ++i)
310                     yearList.append({"value" : i})
311                 if (tmp < yearList.count) {
312                     yearColumn.selectedIndex = 0;
313                     yearColumn.selectedIndex = tmp;
314                 }
315             }
316         }
317
318         function validateDate() {
319             if (internal.year < 1){
320                 internal.year = new Date().getFullYear()
321                 if (maximumYear < internal.year)
322                     root.maximumYear = dateTime.currentYear() + 20;
323                 if (minimumYear > internal.year)
324                     internal.minYear = dateTime.currentYear() - 1;
325             }
326             
327             root.month = Math.max(1, Math.min(12, root.month))
328             var d = dateTime.daysInMonth(internal.year, root.month);
329             root.day = Math.max(1, Math.min(d, root.day))
330         }
331     }
332 }