Unity 8
50-timezone.qml
1 /*
2  * Copyright (C) 2015-2016 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import QtQuick.Layouts 1.1
19 import Ubuntu.Components 1.3
20 import Wizard 0.1
21 import Ubuntu.SystemSettings.TimeDate 1.1
22 import Utils 0.1 as Utils
23 import ".." as LocalComponents
24 
25 LocalComponents.Page {
26  id: tzPage
27  objectName: "tzPage"
28 
29  title: i18n.tr("Time Zone")
30  forwardButtonSourceComponent: forwardButton
31 
32  property string selectedTimeZone: ""
33  property string selectedTimeZoneName: ""
34  readonly property bool showingMap: wideMode && width >= units.gu(110)
35 
36  // for testing
37  readonly property alias tdModule: timeDatePanel
38 
39  function highlightTimezone(offset) {
40  highlightImage.source = "data/timezonemap/timezone_" + offset + ".png";
41  }
42 
43  // geo coords conversion functions (adapted from libtimezonemap)
44  function radians(degrees) {
45  return degrees * Math.PI / 180;
46  }
47 
48  function longitudeToX(longitude, map_width) {
49  const xdeg_offset = -6;
50  const x = (map_width * (180.0 + longitude) / 360.0) + (map_width * xdeg_offset / 180.0);
51  return x;
52  }
53 
54  function latitudeToY(latitude, map_height) {
55  const bottom_lat = -59;
56  const top_lat = 81;
57  const top_per = top_lat / 180.0;
58 
59  var y = 1.25 * Math.log(Math.tan(0.25*Math.PI + 0.4 * radians(latitude)));
60  const full_range = 4.6068250867599998;
61  const top_offset = full_range * top_per;
62  const map_range = Math.abs(1.25 * Math.log(Math.tan(0.25*Math.PI + 0.4 * radians(bottom_lat))) - top_offset);
63  y = Math.abs(y - top_offset);
64  y = y / map_range;
65  y = y * map_height;
66  return y;
67  }
68 
69  function resetViews() {
70  selectedTimeZone = ""
71  selectedTimeZoneName = ""
72  tzList.currentIndex = -1
73  highlightImage.source = ""
74  pinImage.x = 0;
75  pinImage.y = 0;
76  }
77 
78  UbuntuTimeDatePanel {
79  id: timeDatePanel
80  }
81 
82  onContentAnimationRunningChanged: {
83  if (!contentAnimationRunning) {
84  if (tzList.count == 1) { // preselect the first (and only) TZ
85  var tz = tzList.itemAt(0,0);
86  if (!!tz) {
87  tz.clicked();
88  }
89  }
90 
91  resetViews();
92  searchField.forceActiveFocus();
93  }
94  }
95 
96  Component {
97  id: tzComponent
98  ListItem {
99  id: tz
100  objectName: "tz" + index
101  highlightColor: backgroundColor
102  divider.colorFrom: dividerColor
103  divider.colorTo: backgroundColor
104  readonly property bool currentTz: ListView.view.currentIndex === index
105 
106  Column {
107  anchors.verticalCenter: parent.verticalCenter
108  anchors.left: parent.left
109  anchors.leftMargin: !wideMode ? staticMargin : 0
110  anchors.right: image.left
111  anchors.rightMargin: units.gu(2)
112 
113  Label {
114  id: cityLabel
115  text: displayName
116  font.weight: tz.currentTz ? Font.Normal : Font.Light
117  fontSize: "medium"
118  color: textColor
119  elide: Text.ElideMiddle
120  maximumLineCount: 1
121  width: parent.width
122  }
123  Label {
124  id: timeLabel
125  text: Utils.TimezoneFormatter.currentTimeInTimezoneWithAbbrev(timeZone)
126  font.weight: tz.currentTz ? Font.Normal : Font.Light
127  fontSize: "small"
128  color: textColor
129  }
130  }
131  Image {
132  id: image
133  anchors {
134  right: parent.right
135  verticalCenter: parent.verticalCenter
136  rightMargin: !wideMode ? staticMargin : 0
137  }
138  fillMode: Image.PreserveAspectFit
139  height: units.gu(1.5)
140 
141  source: "data/Tick@30.png"
142  visible: tz.currentTz
143  }
144 
145  onClicked: {
146  highlightTimezone(offset);
147  ListView.view.currentIndex = index;
148  selectedTimeZone = timeZone;
149  selectedTimeZoneName = city;
150  //print("Clicked at city with coords:", longitude, latitude);
151  //print("Clicked on TZ:", timeZone);
152  //print("Highlight at (x,y):", longitudeToX(longitude, map.width), latitudeToY(latitude, map.height));
153  pinImage.x = Qt.binding(function() { return longitudeToX(longitude, map.width) - pinImage.width; });
154  pinImage.y = Qt.binding(function() { return latitudeToY(latitude, map.height) - pinImage.height; });
155  }
156  }
157  }
158 
159 
160  ColumnLayout {
161  id: leftColumn
162  anchors {
163  left: content.left
164  top: content.top
165  bottom: content.bottom
166  right: !showingMap ? content.right : undefined
167  leftMargin: showingMap ? staticMargin : (wideMode ? tzPage.leftMargin : 0)
168  rightMargin: showingMap ? staticMargin : (wideMode ? tzPage.rightMargin : 0)
169  topMargin: customMargin
170  }
171 
172  width: Math.min(parent.width, units.gu(34))
173 
174  LocalComponents.WizardTextField {
175  id: searchField
176  objectName: "tzFilter"
177  anchors.left: parent.left
178  anchors.right: parent.right
179  anchors.leftMargin: !showingMap && !wideMode ? staticMargin : undefined
180  anchors.rightMargin: !showingMap && !wideMode ? staticMargin : undefined
181  placeholderText: i18n.tr("Enter your city")
182  inputMethodHints: Qt.ImhNoPredictiveText
183  onTextChanged: resetViews();
184  }
185 
186  ListView {
187  Layout.fillHeight: true
188  id: tzList
189  objectName: "tzList"
190  clip: true
191  anchors.left: parent.left
192  anchors.right: parent.right
193  currentIndex: -1
194  model: TimeZoneModel {
195  id: timeZoneModel
196  filter: searchField.text
197  country: i18n.language.split('_')[1].split('.')[0]
198  }
199  delegate: tzComponent
200  }
201 
202  ActivityIndicator {
203  anchors.centerIn: tzList
204  running: tzList.count == 0 &&
205  searchField.length > 0 &&
206  timeZoneModel.listUpdating
207  visible: running
208  }
209  }
210 
211  Item {
212  id: mapContainer
213  visible: showingMap && !contentAnimationRunning
214  enabled: visible
215 
216  anchors {
217  left: leftColumn.right
218  leftMargin: units.gu(4)
219  right: content.right
220  rightMargin: staticMargin
221  top: content.top
222  topMargin: customMargin
223  bottom: parent.bottom
224  bottomMargin: buttonBarHeight
225  }
226 
227  Item {
228  id: map
229  width: Math.min(parent.width, height * 1.95) // keep our aspect ratio
230  height: parent.height
231  anchors {
232  centerIn: parent
233  }
234 
235  Image {
236  id: backgroundImage
237  source: "data/timezonemap/map.png"
238  sourceSize: Qt.size(map.width, map.height)
239  fillMode: Image.PreserveAspectFit
240  smooth: false
241  visible: mapContainer.visible
242  asynchronous: true
243  anchors.fill: parent
244  }
245 
246  Image {
247  id: highlightImage
248  sourceSize: Qt.size(map.width, map.height)
249  fillMode: Image.PreserveAspectFit
250  smooth: false
251  visible: selectedTimeZone != ""
252  asynchronous: true
253  anchors.fill: backgroundImage
254  }
255 
256  Image {
257  id: pinImage
258  source: "data/timezonemap/pin.png"
259  visible: x != 0 && y != 0
260  width: units.dp(12)
261  height: units.dp(20)
262  z: map.z + 1
263  }
264  }
265  }
266 
267  Component {
268  id: forwardButton
269  LocalComponents.StackButton {
270  text: i18n.tr("Next")
271  enabled: selectedTimeZone != ""
272  onClicked: {
273  timeDatePanel.setTimeZone(selectedTimeZone, selectedTimeZoneName);
274  pageStack.next();
275  }
276  }
277  }
278 }