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