Merge lp:~vthompson/ubuntu-weather-app/reboot-allow-adding-duplicate-current-location into lp:ubuntu-weather-app/obsolete.trunk

Proposed by Victor Thompson
Status: Superseded
Proposed branch: lp:~vthompson/ubuntu-weather-app/reboot-allow-adding-duplicate-current-location
Merge into: lp:ubuntu-weather-app/obsolete.trunk
Diff against target: 8157 lines (+7760/-0) (has conflicts)
70 files modified
.bzrignore (+19/-0)
CMakeLists.txt (+101/-0)
COPYING (+674/-0)
README (+11/-0)
README.translations (+37/-0)
app/CMakeLists.txt (+18/-0)
app/components/CMakeLists.txt (+7/-0)
app/components/CurrentLocation.qml (+84/-0)
app/components/DayDelegate.qml (+245/-0)
app/components/ExpandableListItem.qml (+90/-0)
app/components/FastScroll.js (+130/-0)
app/components/FastScroll.qml (+321/-0)
app/components/ForecastDetailsDelegate.qml (+48/-0)
app/components/HeaderRow.qml (+62/-0)
app/components/HomeGraphic.qml (+39/-0)
app/components/HomeHourly.qml (+101/-0)
app/components/HomeTempInfo.qml (+73/-0)
app/components/ListItemActions/CMakeLists.txt (+5/-0)
app/components/ListItemActions/CheckBox.qml (+25/-0)
app/components/ListItemActions/Remove.qml (+27/-0)
app/components/ListItemReorderComponent.qml (+106/-0)
app/components/ListItemWithActions.qml (+496/-0)
app/components/LoadingIndicator.qml (+87/-0)
app/components/MultiSelectHeadState.qml (+72/-0)
app/components/MultiSelectListView.qml (+52/-0)
app/components/PageWithBottomEdge.qml (+411/-0)
app/components/StandardListItem.qml (+48/-0)
app/components/WeatherListItem.qml (+137/-0)
app/components/WeatherListView.qml (+32/-0)
app/data/CMakeLists.txt (+5/-0)
app/data/CitiesList.js (+65/-0)
app/data/Storage.qml (+214/-0)
app/data/WeatherApi.js (+754/-0)
app/data/key.js (+1/-0)
app/graphics/CMakeLists.txt (+5/-0)
app/graphics/extended-information_chance-of-rain.svg (+153/-0)
app/graphics/extended-information_humidity.svg (+147/-0)
app/graphics/extended-information_uv-level.svg (+270/-0)
app/graphics/extended-information_wind.svg (+155/-0)
app/ubuntu-weather-app.qml (+291/-0)
app/ui/AddLocationPage.qml (+310/-0)
app/ui/CMakeLists.txt (+7/-0)
app/ui/HomePage.qml (+182/-0)
app/ui/LocationPane.qml (+195/-0)
app/ui/LocationsPage.qml (+279/-0)
app/ui/SettingsPage.qml (+53/-0)
app/ui/settings/CMakeLists.txt (+5/-0)
app/ui/settings/DataProviderPage.qml (+54/-0)
app/ui/settings/RefreshIntervalPage.qml (+55/-0)
app/ui/settings/UnitsPage.qml (+147/-0)
debian/changelog (+179/-0)
debian/compat (+1/-0)
debian/control (+41/-0)
debian/copyright (+54/-0)
debian/rules (+14/-0)
debian/source/format (+1/-0)
debian/ubuntu-weather-app-autopilot.install (+1/-0)
debian/ubuntu-weather-app.install (+1/-0)
manifest.json.in (+23/-0)
po/CMakeLists.txt (+33/-0)
po/com.ubuntu.weather.pot (+218/-0)
tests/CMakeLists.txt (+1/-0)
tests/autopilot/CMakeLists.txt (+10/-0)
tests/autopilot/ubuntu_weather_app/CMakeLists.txt (+8/-0)
tests/autopilot/ubuntu_weather_app/__init__.py (+59/-0)
tests/autopilot/ubuntu_weather_app/tests/CMakeLists.txt (+6/-0)
tests/autopilot/ubuntu_weather_app/tests/__init__.py (+150/-0)
tests/autopilot/ubuntu_weather_app/tests/test_weather.py (+35/-0)
ubuntu-weather-app.apparmor (+8/-0)
ubuntu-weather-app.desktop.in.in (+12/-0)
Conflict adding file .bzrignore.  Moved existing file to .bzrignore.moved.
Conflict adding file CMakeLists.txt.  Moved existing file to CMakeLists.txt.moved.
Conflict adding file COPYING.  Moved existing file to COPYING.moved.
Conflict adding file README.  Moved existing file to README.moved.
Conflict adding file README.translations.  Moved existing file to README.translations.moved.
Conflict adding file debian.  Moved existing file to debian.moved.
Conflict adding file po.  Moved existing file to po.moved.
Conflict adding file tests.  Moved existing file to tests.moved.
To merge this branch: bzr merge lp:~vthompson/ubuntu-weather-app/reboot-allow-adding-duplicate-current-location
Reviewer Review Type Date Requested Status
Ubuntu Weather Developers Pending
Review via email: mp+263208@code.launchpad.net

This proposal has been superseded by a proposal from 2015-06-28.

Commit message

Allow adding the current location as a duplicate to the Locations List.

Description of the change

Allow adding the current location as a duplicate to the Locations List.

To post a comment you must log in.

Unmerged revisions

58. By Victor Thompson

Allow adding the current location as a duplicate to the LocationsList.

57. By Victor Thompson

Properly migrate Locations.

Approved by Andrew Hayzen, Ubuntu Phone Apps Jenkins Bot.

56. By Nekhelesh Ramananthan

Migrates the following pages to the new SDK list items,
- SettingsPage.qml
- DayDelegate.qml
- Individual settings page like DataProviderPage.qml, RefreshIntervalPage.qml and UnitsPage.qml.
- AddLocationsPage.qml.

Approved by Victor Thompson, Ubuntu Phone Apps Jenkins Bot.

55. By Victor Thompson

Initial empty state.

Approved by Ubuntu Phone Apps Jenkins Bot, Nekhelesh Ramananthan.

54. By Victor Thompson

Show am/pm text depending upon locale.

Approved by Ubuntu Phone Apps Jenkins Bot, Nekhelesh Ramananthan.

53. By Victor Thompson

* Bump framework to 15.04 and apparmor policy version
* Bump QtQuick to 2.4 and UITK components to 1.2
* Remove useDeprecatedToolbar as it does not exist anymore.

Approved by Nekhelesh Ramananthan, Ubuntu Phone Apps Jenkins Bot.

52. By Victor Thompson

* Make current location static in LocationsPage.

Approved by Andrew Hayzen, Ubuntu Phone Apps Jenkins Bot.

51. By Victor Thompson

* Do not lose currentIndex when pull to refresh.

Approved by Andrew Hayzen, Ubuntu Phone Apps Jenkins Bot.

50. By Victor Thompson

* Sync debian version with click version.

Approved by Andrew Hayzen, Ubuntu Phone Apps Jenkins Bot.

49. By Victor Thompson

Update LocationsPage layout to better match the design spec.

Approved by Andrew Hayzen, Ubuntu Phone Apps Jenkins Bot.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2015-06-28 23:49:12 +0000
@@ -0,0 +1,19 @@
1*.user*
2debian/files
3debian/tmp
4debian/ubuntu-weather-app*
5debian/app-template/
6debian/*.debhelper.log
7debian/*.substvars
8.build
9Makefile
10CMakeCache.txt
11CMakeFiles/
12*.cmake
13*.gmo
14*.mo
15*.desktop
16*.desktop.in
17*.desktop.in.in.h
18.excludes
19ubuntu-weather-app.json
020
=== renamed file '.bzrignore' => '.bzrignore.moved'
=== added file 'CMakeLists.txt'
--- CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ CMakeLists.txt 2015-06-28 23:49:12 +0000
@@ -0,0 +1,101 @@
1project(com.ubuntu.weather)
2cmake_minimum_required(VERSION 2.8.9)
3
4find_program(INTLTOOL_MERGE intltool-merge)
5if(NOT INTLTOOL_MERGE)
6 message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package")
7endif()
8find_program(INTLTOOL_EXTRACT intltool-extract)
9if(NOT INTLTOOL_EXTRACT)
10 message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package")
11endif()
12
13set (UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Relative path to the manifest file")
14set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
15
16find_package(Qt5Core REQUIRED)
17find_package(Qt5Qml REQUIRED)
18find_package(Qt5Quick REQUIRED)
19
20# Automatically create moc files
21set(CMAKE_AUTOMOC ON)
22
23option(INSTALL_TESTS "Install the tests on make install" on)
24option(CLICK_MODE "Build as a click package" on)
25
26# Tests
27enable_testing()
28
29# Standard install paths
30include(GNUInstallDirs)
31
32set(APP_NAME weather)
33set(APP_HARDCODE ubuntu-weather-app)
34set(MAIN_QML ${APP_HARDCODE}.qml)
35set(DESKTOP_FILE "${APP_HARDCODE}.desktop")
36set(ICON weather-app@30.png)
37set(AUTOPILOT_DIR ubuntu_weather_app)
38
39# Set install paths
40if(CLICK_MODE)
41 set(CMAKE_INSTALL_PREFIX "/")
42 set(UBUNTU-WEATHER_APP_DIR "${CMAKE_INSTALL_DATADIR}/qml")
43
44 set(QT_IMPORTS_DIR "${CMAKE_INSTALL_LIBDIR}")
45 set(EXEC "qmlscene $@ ${UBUNTU-WEATHER_APP_DIR}/${MAIN_QML}")
46 set(MODULE_PATH ${QT_IMPORTS_DIR})
47 if(NOT BZR_REVNO)
48 execute_process(
49 COMMAND bzr revno
50 OUTPUT_VARIABLE BZR_REVNO
51 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
52 OUTPUT_STRIP_TRAILING_WHITESPACE
53 )
54 endif(NOT BZR_REVNO)
55 if(NOT BZR_SOURCE)
56 set(BZR_SOURCE "lp:${APP_HARDCODE}/reboot")
57 message("-- Setting BZR_SOURCE to ${BZR_SOURCE}")
58 endif(NOT BZR_SOURCE)
59else(CLICK_MODE)
60 set(UBUNTU-WEATHER_APP_DIR "${CMAKE_INSTALL_DATADIR}/ubuntu-weather-app")
61 execute_process(
62 COMMAND qmake -query QT_INSTALL_QML
63 OUTPUT_VARIABLE QT_IMPORTS_DIR
64 OUTPUT_STRIP_TRAILING_WHITESPACE
65 )
66 set(MODULE_PATH ${QT_IMPORTS_DIR}/WeatherApp)
67endif(CLICK_MODE)
68
69if(${CLICK_MODE})
70 message("-- Configuring manifest.json")
71
72 configure_file(${UBUNTU_MANIFEST_PATH} ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
73 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX})
74 install(FILES "${APP_HARDCODE}.apparmor" DESTINATION ${CMAKE_INSTALL_PREFIX})
75else(CLICK_MODE)
76 set(EXEC "qmlscene $@ -I ${MODULE_PATH} ${CMAKE_INSTALL_PREFIX}/${UBUNTU-WEATHER_APP_DIR}/${MAIN_QML}")
77endif()
78
79
80file(GLOB_RECURSE I18N_SRC_FILES
81 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po
82 *.qml *.js)
83list(APPEND I18N_SRC_FILES ${DESKTOP_FILE}.in.in.h)
84list(SORT I18N_SRC_FILES)
85
86configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in)
87
88add_custom_target(${DESKTOP_FILE} ALL
89 COMMENT "Merging translations into ${DESKTOP_FILE}..."
90 COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${DESKTOP_FILE}.in ${DESKTOP_FILE}
91)
92
93install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE}
94 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
95
96add_subdirectory(app)
97add_subdirectory(po)
98add_subdirectory(tests)
99
100# TODO: Add custom target for autopilot and run.
101
0102
=== renamed file 'CMakeLists.txt' => 'CMakeLists.txt.moved'
=== added file 'COPYING'
--- COPYING 1970-01-01 00:00:00 +0000
+++ COPYING 2015-06-28 23:49:12 +0000
@@ -0,0 +1,674 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 3, 29 June 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The GNU General Public License is a free, copyleft license for
11software and other kinds of works.
12
13 The licenses for most software and other practical works are designed
14to take away your freedom to share and change the works. By contrast,
15the GNU General Public License is intended to guarantee your freedom to
16share and change all versions of a program--to make sure it remains free
17software for all its users. We, the Free Software Foundation, use the
18GNU General Public License for most of our software; it applies also to
19any other work released this way by its authors. You can apply it to
20your programs, too.
21
22 When we speak of free software, we are referring to freedom, not
23price. Our General Public Licenses are designed to make sure that you
24have the freedom to distribute copies of free software (and charge for
25them if you wish), that you receive source code or can get it if you
26want it, that you can change the software or use pieces of it in new
27free programs, and that you know you can do these things.
28
29 To protect your rights, we need to prevent others from denying you
30these rights or asking you to surrender the rights. Therefore, you have
31certain responsibilities if you distribute copies of the software, or if
32you modify it: responsibilities to respect the freedom of others.
33
34 For example, if you distribute copies of such a program, whether
35gratis or for a fee, you must pass on to the recipients the same
36freedoms that you received. You must make sure that they, too, receive
37or can get the source code. And you must show them these terms so they
38know their rights.
39
40 Developers that use the GNU GPL protect your rights with two steps:
41(1) assert copyright on the software, and (2) offer you this License
42giving you legal permission to copy, distribute and/or modify it.
43
44 For the developers' and authors' protection, the GPL clearly explains
45that there is no warranty for this free software. For both users' and
46authors' sake, the GPL requires that modified versions be marked as
47changed, so that their problems will not be attributed erroneously to
48authors of previous versions.
49
50 Some devices are designed to deny users access to install or run
51modified versions of the software inside them, although the manufacturer
52can do so. This is fundamentally incompatible with the aim of
53protecting users' freedom to change the software. The systematic
54pattern of such abuse occurs in the area of products for individuals to
55use, which is precisely where it is most unacceptable. Therefore, we
56have designed this version of the GPL to prohibit the practice for those
57products. If such problems arise substantially in other domains, we
58stand ready to extend this provision to those domains in future versions
59of the GPL, as needed to protect the freedom of users.
60
61 Finally, every program is threatened constantly by software patents.
62States should not allow patents to restrict development and use of
63software on general-purpose computers, but in those that do, we wish to
64avoid the special danger that patents applied to a free program could
65make it effectively proprietary. To prevent this, the GPL assures that
66patents cannot be used to render the program non-free.
67
68 The precise terms and conditions for copying, distribution and
69modification follow.
70
71 TERMS AND CONDITIONS
72
73 0. Definitions.
74
75 "This License" refers to version 3 of the GNU General Public License.
76
77 "Copyright" also means copyright-like laws that apply to other kinds of
78works, such as semiconductor masks.
79
80 "The Program" refers to any copyrightable work licensed under this
81License. Each licensee is addressed as "you". "Licensees" and
82"recipients" may be individuals or organizations.
83
84 To "modify" a work means to copy from or adapt all or part of the work
85in a fashion requiring copyright permission, other than the making of an
86exact copy. The resulting work is called a "modified version" of the
87earlier work or a work "based on" the earlier work.
88
89 A "covered work" means either the unmodified Program or a work based
90on the Program.
91
92 To "propagate" a work means to do anything with it that, without
93permission, would make you directly or secondarily liable for
94infringement under applicable copyright law, except executing it on a
95computer or modifying a private copy. Propagation includes copying,
96distribution (with or without modification), making available to the
97public, and in some countries other activities as well.
98
99 To "convey" a work means any kind of propagation that enables other
100parties to make or receive copies. Mere interaction with a user through
101a computer network, with no transfer of a copy, is not conveying.
102
103 An interactive user interface displays "Appropriate Legal Notices"
104to the extent that it includes a convenient and prominently visible
105feature that (1) displays an appropriate copyright notice, and (2)
106tells the user that there is no warranty for the work (except to the
107extent that warranties are provided), that licensees may convey the
108work under this License, and how to view a copy of this License. If
109the interface presents a list of user commands or options, such as a
110menu, a prominent item in the list meets this criterion.
111
112 1. Source Code.
113
114 The "source code" for a work means the preferred form of the work
115for making modifications to it. "Object code" means any non-source
116form of a work.
117
118 A "Standard Interface" means an interface that either is an official
119standard defined by a recognized standards body, or, in the case of
120interfaces specified for a particular programming language, one that
121is widely used among developers working in that language.
122
123 The "System Libraries" of an executable work include anything, other
124than the work as a whole, that (a) is included in the normal form of
125packaging a Major Component, but which is not part of that Major
126Component, and (b) serves only to enable use of the work with that
127Major Component, or to implement a Standard Interface for which an
128implementation is available to the public in source code form. A
129"Major Component", in this context, means a major essential component
130(kernel, window system, and so on) of the specific operating system
131(if any) on which the executable work runs, or a compiler used to
132produce the work, or an object code interpreter used to run it.
133
134 The "Corresponding Source" for a work in object code form means all
135the source code needed to generate, install, and (for an executable
136work) run the object code and to modify the work, including scripts to
137control those activities. However, it does not include the work's
138System Libraries, or general-purpose tools or generally available free
139programs which are used unmodified in performing those activities but
140which are not part of the work. For example, Corresponding Source
141includes interface definition files associated with source files for
142the work, and the source code for shared libraries and dynamically
143linked subprograms that the work is specifically designed to require,
144such as by intimate data communication or control flow between those
145subprograms and other parts of the work.
146
147 The Corresponding Source need not include anything that users
148can regenerate automatically from other parts of the Corresponding
149Source.
150
151 The Corresponding Source for a work in source code form is that
152same work.
153
154 2. Basic Permissions.
155
156 All rights granted under this License are granted for the term of
157copyright on the Program, and are irrevocable provided the stated
158conditions are met. This License explicitly affirms your unlimited
159permission to run the unmodified Program. The output from running a
160covered work is covered by this License only if the output, given its
161content, constitutes a covered work. This License acknowledges your
162rights of fair use or other equivalent, as provided by copyright law.
163
164 You may make, run and propagate covered works that you do not
165convey, without conditions so long as your license otherwise remains
166in force. You may convey covered works to others for the sole purpose
167of having them make modifications exclusively for you, or provide you
168with facilities for running those works, provided that you comply with
169the terms of this License in conveying all material for which you do
170not control copyright. Those thus making or running the covered works
171for you must do so exclusively on your behalf, under your direction
172and control, on terms that prohibit them from making any copies of
173your copyrighted material outside their relationship with you.
174
175 Conveying under any other circumstances is permitted solely under
176the conditions stated below. Sublicensing is not allowed; section 10
177makes it unnecessary.
178
179 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
181 No covered work shall be deemed part of an effective technological
182measure under any applicable law fulfilling obligations under article
18311 of the WIPO copyright treaty adopted on 20 December 1996, or
184similar laws prohibiting or restricting circumvention of such
185measures.
186
187 When you convey a covered work, you waive any legal power to forbid
188circumvention of technological measures to the extent such circumvention
189is effected by exercising rights under this License with respect to
190the covered work, and you disclaim any intention to limit operation or
191modification of the work as a means of enforcing, against the work's
192users, your or third parties' legal rights to forbid circumvention of
193technological measures.
194
195 4. Conveying Verbatim Copies.
196
197 You may convey verbatim copies of the Program's source code as you
198receive it, in any medium, provided that you conspicuously and
199appropriately publish on each copy an appropriate copyright notice;
200keep intact all notices stating that this License and any
201non-permissive terms added in accord with section 7 apply to the code;
202keep intact all notices of the absence of any warranty; and give all
203recipients a copy of this License along with the Program.
204
205 You may charge any price or no price for each copy that you convey,
206and you may offer support or warranty protection for a fee.
207
208 5. Conveying Modified Source Versions.
209
210 You may convey a work based on the Program, or the modifications to
211produce it from the Program, in the form of source code under the
212terms of section 4, provided that you also meet all of these conditions:
213
214 a) The work must carry prominent notices stating that you modified
215 it, and giving a relevant date.
216
217 b) The work must carry prominent notices stating that it is
218 released under this License and any conditions added under section
219 7. This requirement modifies the requirement in section 4 to
220 "keep intact all notices".
221
222 c) You must license the entire work, as a whole, under this
223 License to anyone who comes into possession of a copy. This
224 License will therefore apply, along with any applicable section 7
225 additional terms, to the whole of the work, and all its parts,
226 regardless of how they are packaged. This License gives no
227 permission to license the work in any other way, but it does not
228 invalidate such permission if you have separately received it.
229
230 d) If the work has interactive user interfaces, each must display
231 Appropriate Legal Notices; however, if the Program has interactive
232 interfaces that do not display Appropriate Legal Notices, your
233 work need not make them do so.
234
235 A compilation of a covered work with other separate and independent
236works, which are not by their nature extensions of the covered work,
237and which are not combined with it such as to form a larger program,
238in or on a volume of a storage or distribution medium, is called an
239"aggregate" if the compilation and its resulting copyright are not
240used to limit the access or legal rights of the compilation's users
241beyond what the individual works permit. Inclusion of a covered work
242in an aggregate does not cause this License to apply to the other
243parts of the aggregate.
244
245 6. Conveying Non-Source Forms.
246
247 You may convey a covered work in object code form under the terms
248of sections 4 and 5, provided that you also convey the
249machine-readable Corresponding Source under the terms of this License,
250in one of these ways:
251
252 a) Convey the object code in, or embodied in, a physical product
253 (including a physical distribution medium), accompanied by the
254 Corresponding Source fixed on a durable physical medium
255 customarily used for software interchange.
256
257 b) Convey the object code in, or embodied in, a physical product
258 (including a physical distribution medium), accompanied by a
259 written offer, valid for at least three years and valid for as
260 long as you offer spare parts or customer support for that product
261 model, to give anyone who possesses the object code either (1) a
262 copy of the Corresponding Source for all the software in the
263 product that is covered by this License, on a durable physical
264 medium customarily used for software interchange, for a price no
265 more than your reasonable cost of physically performing this
266 conveying of source, or (2) access to copy the
267 Corresponding Source from a network server at no charge.
268
269 c) Convey individual copies of the object code with a copy of the
270 written offer to provide the Corresponding Source. This
271 alternative is allowed only occasionally and noncommercially, and
272 only if you received the object code with such an offer, in accord
273 with subsection 6b.
274
275 d) Convey the object code by offering access from a designated
276 place (gratis or for a charge), and offer equivalent access to the
277 Corresponding Source in the same way through the same place at no
278 further charge. You need not require recipients to copy the
279 Corresponding Source along with the object code. If the place to
280 copy the object code is a network server, the Corresponding Source
281 may be on a different server (operated by you or a third party)
282 that supports equivalent copying facilities, provided you maintain
283 clear directions next to the object code saying where to find the
284 Corresponding Source. Regardless of what server hosts the
285 Corresponding Source, you remain obligated to ensure that it is
286 available for as long as needed to satisfy these requirements.
287
288 e) Convey the object code using peer-to-peer transmission, provided
289 you inform other peers where the object code and Corresponding
290 Source of the work are being offered to the general public at no
291 charge under subsection 6d.
292
293 A separable portion of the object code, whose source code is excluded
294from the Corresponding Source as a System Library, need not be
295included in conveying the object code work.
296
297 A "User Product" is either (1) a "consumer product", which means any
298tangible personal property which is normally used for personal, family,
299or household purposes, or (2) anything designed or sold for incorporation
300into a dwelling. In determining whether a product is a consumer product,
301doubtful cases shall be resolved in favor of coverage. For a particular
302product received by a particular user, "normally used" refers to a
303typical or common use of that class of product, regardless of the status
304of the particular user or of the way in which the particular user
305actually uses, or expects or is expected to use, the product. A product
306is a consumer product regardless of whether the product has substantial
307commercial, industrial or non-consumer uses, unless such uses represent
308the only significant mode of use of the product.
309
310 "Installation Information" for a User Product means any methods,
311procedures, authorization keys, or other information required to install
312and execute modified versions of a covered work in that User Product from
313a modified version of its Corresponding Source. The information must
314suffice to ensure that the continued functioning of the modified object
315code is in no case prevented or interfered with solely because
316modification has been made.
317
318 If you convey an object code work under this section in, or with, or
319specifically for use in, a User Product, and the conveying occurs as
320part of a transaction in which the right of possession and use of the
321User Product is transferred to the recipient in perpetuity or for a
322fixed term (regardless of how the transaction is characterized), the
323Corresponding Source conveyed under this section must be accompanied
324by the Installation Information. But this requirement does not apply
325if neither you nor any third party retains the ability to install
326modified object code on the User Product (for example, the work has
327been installed in ROM).
328
329 The requirement to provide Installation Information does not include a
330requirement to continue to provide support service, warranty, or updates
331for a work that has been modified or installed by the recipient, or for
332the User Product in which it has been modified or installed. Access to a
333network may be denied when the modification itself materially and
334adversely affects the operation of the network or violates the rules and
335protocols for communication across the network.
336
337 Corresponding Source conveyed, and Installation Information provided,
338in accord with this section must be in a format that is publicly
339documented (and with an implementation available to the public in
340source code form), and must require no special password or key for
341unpacking, reading or copying.
342
343 7. Additional Terms.
344
345 "Additional permissions" are terms that supplement the terms of this
346License by making exceptions from one or more of its conditions.
347Additional permissions that are applicable to the entire Program shall
348be treated as though they were included in this License, to the extent
349that they are valid under applicable law. If additional permissions
350apply only to part of the Program, that part may be used separately
351under those permissions, but the entire Program remains governed by
352this License without regard to the additional permissions.
353
354 When you convey a copy of a covered work, you may at your option
355remove any additional permissions from that copy, or from any part of
356it. (Additional permissions may be written to require their own
357removal in certain cases when you modify the work.) You may place
358additional permissions on material, added by you to a covered work,
359for which you have or can give appropriate copyright permission.
360
361 Notwithstanding any other provision of this License, for material you
362add to a covered work, you may (if authorized by the copyright holders of
363that material) supplement the terms of this License with terms:
364
365 a) Disclaiming warranty or limiting liability differently from the
366 terms of sections 15 and 16 of this License; or
367
368 b) Requiring preservation of specified reasonable legal notices or
369 author attributions in that material or in the Appropriate Legal
370 Notices displayed by works containing it; or
371
372 c) Prohibiting misrepresentation of the origin of that material, or
373 requiring that modified versions of such material be marked in
374 reasonable ways as different from the original version; or
375
376 d) Limiting the use for publicity purposes of names of licensors or
377 authors of the material; or
378
379 e) Declining to grant rights under trademark law for use of some
380 trade names, trademarks, or service marks; or
381
382 f) Requiring indemnification of licensors and authors of that
383 material by anyone who conveys the material (or modified versions of
384 it) with contractual assumptions of liability to the recipient, for
385 any liability that these contractual assumptions directly impose on
386 those licensors and authors.
387
388 All other non-permissive additional terms are considered "further
389restrictions" within the meaning of section 10. If the Program as you
390received it, or any part of it, contains a notice stating that it is
391governed by this License along with a term that is a further
392restriction, you may remove that term. If a license document contains
393a further restriction but permits relicensing or conveying under this
394License, you may add to a covered work material governed by the terms
395of that license document, provided that the further restriction does
396not survive such relicensing or conveying.
397
398 If you add terms to a covered work in accord with this section, you
399must place, in the relevant source files, a statement of the
400additional terms that apply to those files, or a notice indicating
401where to find the applicable terms.
402
403 Additional terms, permissive or non-permissive, may be stated in the
404form of a separately written license, or stated as exceptions;
405the above requirements apply either way.
406
407 8. Termination.
408
409 You may not propagate or modify a covered work except as expressly
410provided under this License. Any attempt otherwise to propagate or
411modify it is void, and will automatically terminate your rights under
412this License (including any patent licenses granted under the third
413paragraph of section 11).
414
415 However, if you cease all violation of this License, then your
416license from a particular copyright holder is reinstated (a)
417provisionally, unless and until the copyright holder explicitly and
418finally terminates your license, and (b) permanently, if the copyright
419holder fails to notify you of the violation by some reasonable means
420prior to 60 days after the cessation.
421
422 Moreover, your license from a particular copyright holder is
423reinstated permanently if the copyright holder notifies you of the
424violation by some reasonable means, this is the first time you have
425received notice of violation of this License (for any work) from that
426copyright holder, and you cure the violation prior to 30 days after
427your receipt of the notice.
428
429 Termination of your rights under this section does not terminate the
430licenses of parties who have received copies or rights from you under
431this License. If your rights have been terminated and not permanently
432reinstated, you do not qualify to receive new licenses for the same
433material under section 10.
434
435 9. Acceptance Not Required for Having Copies.
436
437 You are not required to accept this License in order to receive or
438run a copy of the Program. Ancillary propagation of a covered work
439occurring solely as a consequence of using peer-to-peer transmission
440to receive a copy likewise does not require acceptance. However,
441nothing other than this License grants you permission to propagate or
442modify any covered work. These actions infringe copyright if you do
443not accept this License. Therefore, by modifying or propagating a
444covered work, you indicate your acceptance of this License to do so.
445
446 10. Automatic Licensing of Downstream Recipients.
447
448 Each time you convey a covered work, the recipient automatically
449receives a license from the original licensors, to run, modify and
450propagate that work, subject to this License. You are not responsible
451for enforcing compliance by third parties with this License.
452
453 An "entity transaction" is a transaction transferring control of an
454organization, or substantially all assets of one, or subdividing an
455organization, or merging organizations. If propagation of a covered
456work results from an entity transaction, each party to that
457transaction who receives a copy of the work also receives whatever
458licenses to the work the party's predecessor in interest had or could
459give under the previous paragraph, plus a right to possession of the
460Corresponding Source of the work from the predecessor in interest, if
461the predecessor has it or can get it with reasonable efforts.
462
463 You may not impose any further restrictions on the exercise of the
464rights granted or affirmed under this License. For example, you may
465not impose a license fee, royalty, or other charge for exercise of
466rights granted under this License, and you may not initiate litigation
467(including a cross-claim or counterclaim in a lawsuit) alleging that
468any patent claim is infringed by making, using, selling, offering for
469sale, or importing the Program or any portion of it.
470
471 11. Patents.
472
473 A "contributor" is a copyright holder who authorizes use under this
474License of the Program or a work on which the Program is based. The
475work thus licensed is called the contributor's "contributor version".
476
477 A contributor's "essential patent claims" are all patent claims
478owned or controlled by the contributor, whether already acquired or
479hereafter acquired, that would be infringed by some manner, permitted
480by this License, of making, using, or selling its contributor version,
481but do not include claims that would be infringed only as a
482consequence of further modification of the contributor version. For
483purposes of this definition, "control" includes the right to grant
484patent sublicenses in a manner consistent with the requirements of
485this License.
486
487 Each contributor grants you a non-exclusive, worldwide, royalty-free
488patent license under the contributor's essential patent claims, to
489make, use, sell, offer for sale, import and otherwise run, modify and
490propagate the contents of its contributor version.
491
492 In the following three paragraphs, a "patent license" is any express
493agreement or commitment, however denominated, not to enforce a patent
494(such as an express permission to practice a patent or covenant not to
495sue for patent infringement). To "grant" such a patent license to a
496party means to make such an agreement or commitment not to enforce a
497patent against the party.
498
499 If you convey a covered work, knowingly relying on a patent license,
500and the Corresponding Source of the work is not available for anyone
501to copy, free of charge and under the terms of this License, through a
502publicly available network server or other readily accessible means,
503then you must either (1) cause the Corresponding Source to be so
504available, or (2) arrange to deprive yourself of the benefit of the
505patent license for this particular work, or (3) arrange, in a manner
506consistent with the requirements of this License, to extend the patent
507license to downstream recipients. "Knowingly relying" means you have
508actual knowledge that, but for the patent license, your conveying the
509covered work in a country, or your recipient's use of the covered work
510in a country, would infringe one or more identifiable patents in that
511country that you have reason to believe are valid.
512
513 If, pursuant to or in connection with a single transaction or
514arrangement, you convey, or propagate by procuring conveyance of, a
515covered work, and grant a patent license to some of the parties
516receiving the covered work authorizing them to use, propagate, modify
517or convey a specific copy of the covered work, then the patent license
518you grant is automatically extended to all recipients of the covered
519work and works based on it.
520
521 A patent license is "discriminatory" if it does not include within
522the scope of its coverage, prohibits the exercise of, or is
523conditioned on the non-exercise of one or more of the rights that are
524specifically granted under this License. You may not convey a covered
525work if you are a party to an arrangement with a third party that is
526in the business of distributing software, under which you make payment
527to the third party based on the extent of your activity of conveying
528the work, and under which the third party grants, to any of the
529parties who would receive the covered work from you, a discriminatory
530patent license (a) in connection with copies of the covered work
531conveyed by you (or copies made from those copies), or (b) primarily
532for and in connection with specific products or compilations that
533contain the covered work, unless you entered into that arrangement,
534or that patent license was granted, prior to 28 March 2007.
535
536 Nothing in this License shall be construed as excluding or limiting
537any implied license or other defenses to infringement that may
538otherwise be available to you under applicable patent law.
539
540 12. No Surrender of Others' Freedom.
541
542 If conditions are imposed on you (whether by court order, agreement or
543otherwise) that contradict the conditions of this License, they do not
544excuse you from the conditions of this License. If you cannot convey a
545covered work so as to satisfy simultaneously your obligations under this
546License and any other pertinent obligations, then as a consequence you may
547not convey it at all. For example, if you agree to terms that obligate you
548to collect a royalty for further conveying from those to whom you convey
549the Program, the only way you could satisfy both those terms and this
550License would be to refrain entirely from conveying the Program.
551
552 13. Use with the GNU Affero General Public License.
553
554 Notwithstanding any other provision of this License, you have
555permission to link or combine any covered work with a work licensed
556under version 3 of the GNU Affero General Public License into a single
557combined work, and to convey the resulting work. The terms of this
558License will continue to apply to the part which is the covered work,
559but the special requirements of the GNU Affero General Public License,
560section 13, concerning interaction through a network will apply to the
561combination as such.
562
563 14. Revised Versions of this License.
564
565 The Free Software Foundation may publish revised and/or new versions of
566the GNU General Public License from time to time. Such new versions will
567be similar in spirit to the present version, but may differ in detail to
568address new problems or concerns.
569
570 Each version is given a distinguishing version number. If the
571Program specifies that a certain numbered version of the GNU General
572Public License "or any later version" applies to it, you have the
573option of following the terms and conditions either of that numbered
574version or of any later version published by the Free Software
575Foundation. If the Program does not specify a version number of the
576GNU General Public License, you may choose any version ever published
577by the Free Software Foundation.
578
579 If the Program specifies that a proxy can decide which future
580versions of the GNU General Public License can be used, that proxy's
581public statement of acceptance of a version permanently authorizes you
582to choose that version for the Program.
583
584 Later license versions may give you additional or different
585permissions. However, no additional obligations are imposed on any
586author or copyright holder as a result of your choosing to follow a
587later version.
588
589 15. Disclaimer of Warranty.
590
591 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
600 16. Limitation of Liability.
601
602 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610SUCH DAMAGES.
611
612 17. Interpretation of Sections 15 and 16.
613
614 If the disclaimer of warranty and limitation of liability provided
615above cannot be given local legal effect according to their terms,
616reviewing courts shall apply local law that most closely approximates
617an absolute waiver of all civil liability in connection with the
618Program, unless a warranty or assumption of liability accompanies a
619copy of the Program in return for a fee.
620
621 END OF TERMS AND CONDITIONS
622
623 How to Apply These Terms to Your New Programs
624
625 If you develop a new program, and you want it to be of the greatest
626possible use to the public, the best way to achieve this is to make it
627free software which everyone can redistribute and change under these terms.
628
629 To do so, attach the following notices to the program. It is safest
630to attach them to the start of each source file to most effectively
631state the exclusion of warranty; and each file should have at least
632the "copyright" line and a pointer to where the full notice is found.
633
634 <one line to give the program's name and a brief idea of what it does.>
635 Copyright (C) <year> <name of author>
636
637 This program is free software: you can redistribute it and/or modify
638 it under the terms of the GNU General Public License as published by
639 the Free Software Foundation, either version 3 of the License, or
640 (at your option) any later version.
641
642 This program is distributed in the hope that it will be useful,
643 but WITHOUT ANY WARRANTY; without even the implied warranty of
644 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 GNU General Public License for more details.
646
647 You should have received a copy of the GNU General Public License
648 along with this program. If not, see <http://www.gnu.org/licenses/>.
649
650Also add information on how to contact you by electronic and paper mail.
651
652 If the program does terminal interaction, make it output a short
653notice like this when it starts in an interactive mode:
654
655 <program> Copyright (C) <year> <name of author>
656 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 This is free software, and you are welcome to redistribute it
658 under certain conditions; type `show c' for details.
659
660The hypothetical commands `show w' and `show c' should show the appropriate
661parts of the General Public License. Of course, your program's commands
662might be different; for a GUI interface, you would use an "about box".
663
664 You should also get your employer (if you work as a programmer) or school,
665if any, to sign a "copyright disclaimer" for the program, if necessary.
666For more information on this, and how to apply and follow the GNU GPL, see
667<http://www.gnu.org/licenses/>.
668
669 The GNU General Public License does not permit incorporating your program
670into proprietary programs. If your program is a subroutine library, you
671may consider it more useful to permit linking proprietary applications with
672the library. If this is what you want to do, use the GNU Lesser General
673Public License instead of this License. But first, please read
674<http://www.gnu.org/philosophy/why-not-lgpl.html>.
0675
=== renamed file 'COPYING' => 'COPYING.moved'
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2015-06-28 23:49:12 +0000
@@ -0,0 +1,11 @@
1Weather app for Ubuntu devices
2
3To contribute:
4 https://wiki.ubuntu.com/Touch/CoreApps/Weather
5 https://wiki.ubuntu.com/Touch/CoreApps/DevelopmentGuide
6
7The following essential packages are required to develop this app:
8- ubuntu-sdk (see http://developer.ubuntu.com/start)
9- intltool - run the `sudo apt-get install intltool` command for installation
10
11See the debian/control file for an up-to-date list of dependencies
012
=== renamed file 'README' => 'README.moved'
=== added file 'README.translations'
--- README.translations 1970-01-01 00:00:00 +0000
+++ README.translations 2015-06-28 23:49:12 +0000
@@ -0,0 +1,37 @@
1# Updating translations
2
3Translations for the Weather app happen in [Launchpad Translations][] and
4are automatically committed daily on the trunk branch in the po/ folder.
5
6They are then built and installed as part of the package build, so that
7developers don't really need to worry about them.
8
9However, there is one task that needs to be taken care of: exposing new
10translatable messages to translators. So whenever you add new translatable
11messages in the code, make sure to follow these steps:
12
13 1. Run click-buddy to build the project and generate a new .pot file:
14 `click-buddy --dir .`
15 2. Commit the generated .pot file:
16 `bzr commit -m"Updated translation template"`
17 3. Push the branch and send a merge proposal as usual
18
19And that's it, once the branch lands Launchpad should take care of all the rest!
20
21# Behind the scenes
22
23Behind the scenes, whenever the po/*.pot file (also known as translations template)
24is committed to trunk Launchpad reads it and updates the translatable strings
25exposed in the web UI. This will enable translators to work on the new strings.
26The translations template contains all translatable strings that have been
27extracted from the source code files.
28
29Launchpad will then store translations in its database and will commit them daily
30in the form of textual po/*.po files to trunk. The PO files are also usually
31referred to as the translations files. You'll find a translation file for each
32language the app has got at least a translated message available for.
33
34Translations for core apps follow the standard [gettext format].
35
36 [Launchpad Translations]: https://translations.launchpad.net/ubuntu-weather-app
37 [gettext format]: https://www.gnu.org/software/gettext/
038
=== renamed file 'README.translations' => 'README.translations.moved'
=== added directory 'app'
=== added file 'app/CMakeLists.txt'
--- app/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/CMakeLists.txt 2015-06-28 23:49:12 +0000
@@ -0,0 +1,18 @@
1if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
2 file(GLOB QML_JS_FILES *.qml *.js)
3 add_custom_target(ubuntu-weather-app_QMlFiles ALL SOURCES ${QML_JS_FILES})
4endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
5
6
7if(CLICK_MODE)
8 set(ICON ${ICON})
9 install(FILES ${ICON} DESTINATION ${CMAKE_INSTALL_PREFIX})
10endif(CLICK_MODE)
11
12install(FILES ${MAIN_QML} DESTINATION ${UBUNTU-WEATHER_APP_DIR})
13
14add_subdirectory(components)
15add_subdirectory(data)
16add_subdirectory(graphics)
17add_subdirectory(ui)
18
019
=== added directory 'app/components'
=== added file 'app/components/CMakeLists.txt'
--- app/components/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/CMakeLists.txt 2015-06-28 23:49:12 +0000
@@ -0,0 +1,7 @@
1add_subdirectory(ListItemActions)
2
3file(GLOB COMPONENTS_QML_JS_FILES *.qml *.js)
4
5add_custom_target(ubuntu-weather-app_components_QMlFiles ALL SOURCES ${COMPONENTS_QML_JS_FILES})
6
7install(FILES ${COMPONENTS_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/components)
08
=== added file 'app/components/CurrentLocation.qml'
--- app/components/CurrentLocation.qml 1970-01-01 00:00:00 +0000
+++ app/components/CurrentLocation.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,84 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtLocation 5.3
20import QtPositioning 5.2
21import QtQuick 2.4
22import Ubuntu.Components 1.2
23import "../data/WeatherApi.js" as WeatherApi
24
25
26Item {
27 id: currentLocation
28
29 property string string: "Undefined"
30
31 function searchForLocation(lat, lon) {
32 WeatherApi.sendRequest({
33 action: "searchByPoint",
34 params: {
35 coords: {
36 lat: lat,
37 lon: lon
38 }
39 }
40 }, searchResponseHandler)
41 }
42
43 function searchResponseHandler(msgObject) {
44 if (!msgObject.error) {
45 console.log("Loc to add:", JSON.stringify(msgObject.result.locations[0]))
46 storage.updateCurrentLocation(msgObject.result.locations[0])
47 }
48 }
49
50
51 PositionSource {
52 id: currentPosition
53 updateInterval: 1000
54 active: true
55
56 onPositionChanged: {
57 var coord = currentPosition.position.coordinate
58 if (coord.isValid) {
59 geocodeModel.query = coord
60 geocodeModel.update()
61 }
62 }
63 }
64
65 Plugin {
66 id: osmPlugin
67 name: "osm"
68 }
69
70 GeocodeModel {
71 id: geocodeModel
72 autoUpdate: false
73 plugin: osmPlugin
74
75 onCountChanged: {
76 // Update the currentLocation if one is found and it does not match the stored location
77 if (count > 0 && currentLocation.string !== geocodeModel.get(0).address.city) {
78 var loc = geocodeModel.get(0)
79 currentLocation.string = loc.address.city
80 searchForLocation(loc.coordinate.latitude, loc.coordinate.longitude)
81 }
82 }
83 }
84}
085
=== added file 'app/components/DayDelegate.qml'
--- app/components/DayDelegate.qml 1970-01-01 00:00:00 +0000
+++ app/components/DayDelegate.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,245 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22ListItem {
23 id: dayDelegate
24 height: collapsedHeight
25
26 property int collapsedHeight: units.gu(8)
27 property int expandedHeight: collapsedHeight + units.gu(4) + extraInfoColumn.height
28
29 property alias day: dayLabel.text
30 property alias image: weatherImage.name
31 property alias high: highLabel.text
32 property alias low: lowLabel.text
33
34 property alias chanceOfRain: chanceOfRainForecast.chance
35 property alias humidity: humidityForecast.value
36 property alias pollen: pollenForecast.value
37 property alias sunrise: sunriseForecast.value
38 property alias sunset: sunsetForecast.value
39 property alias wind: windForecast.value
40 property alias uvIndex: uvIndexForecast.value
41
42 state: "normal"
43 states: [
44 State {
45 name: "normal"
46 PropertyChanges {
47 target: dayDelegate
48 height: collapsedHeight
49 }
50 },
51 State {
52 name: "expanded"
53 PropertyChanges {
54 target: dayDelegate
55 height: expandedHeight
56 }
57 PropertyChanges {
58 target: expandedInfo
59 opacity: 1
60 }
61 }
62 ]
63
64 transitions: [
65 Transition {
66 from: "normal"
67 to: "expanded"
68 SequentialAnimation {
69 NumberAnimation {
70 easing.type: Easing.InOutQuad
71 properties: "height"
72 }
73 NumberAnimation {
74 easing.type: Easing.InOutQuad
75 properties: "opacity"
76 }
77 }
78 },
79 Transition {
80 from: "expanded"
81 to: "normal"
82 SequentialAnimation {
83 NumberAnimation {
84 easing.type: Easing.InOutQuad
85 properties: "opacity"
86 }
87 NumberAnimation {
88 easing.type: Easing.InOutQuad
89 properties: "height"
90 }
91 }
92 }
93 ]
94
95 onClicked: {
96 state = state === "normal" ? "expanded" : "normal"
97 locationPages.collapseOtherDelegates(index)
98 }
99
100 Item {
101 id: mainInfo
102
103 height: collapsedHeight
104 anchors {
105 left: parent.left
106 right: parent.right
107 margins: units.gu(2)
108 }
109
110 Label {
111 id: dayLabel
112 anchors {
113 left: parent.left
114 right: weatherImage.left
115 rightMargin: units.gu(1)
116 top: parent.top
117 topMargin: (collapsedHeight - dayLabel.height) / 2
118 }
119 elide: Text.ElideRight
120 font.weight: Font.Light
121 fontSize: "medium"
122 }
123
124 Icon {
125 id: weatherImage
126 anchors {
127 horizontalCenter: parent.horizontalCenter
128 verticalCenter: dayLabel.verticalCenter
129 }
130 height: units.gu(3)
131 width: units.gu(3)
132 }
133
134 Label {
135 id: lowLabel
136 anchors {
137 left: weatherImage.right
138 right: highLabel.left
139 rightMargin: units.gu(1)
140 verticalCenter: dayLabel.verticalCenter
141 }
142 elide: Text.ElideRight
143 font.pixelSize: units.gu(2)
144 font.weight: Font.Light
145 fontSize: "medium"
146 height: units.gu(2)
147 horizontalAlignment: Text.AlignRight
148 verticalAlignment: Text.AlignTop // AlignTop appears to align bottom?
149 }
150
151 Label {
152 id: highLabel
153 anchors {
154 bottom: lowLabel.bottom
155 right: parent.right
156 }
157 color: UbuntuColors.orange
158 elide: Text.ElideRight
159 font.pixelSize: units.gu(3)
160 font.weight: Font.Normal
161 height: units.gu(3)
162 verticalAlignment: Text.AlignTop // AlignTop appears to align bottom?
163 }
164 }
165
166 Item {
167 id: expandedInfo
168 anchors {
169 bottom: parent.bottom
170 left: parent.left
171 right: parent.right
172 top: mainInfo.bottom
173 bottomMargin: units.gu(2)
174 }
175 opacity: 0
176 visible: opacity !== 0
177
178 Column {
179 id: extraInfoColumn
180 anchors {
181 centerIn: parent
182 }
183 spacing: units.gu(2)
184
185 // FIXME: extended-infomation_* aren't actually on device
186
187 // Overview text
188 Label {
189 id: chanceOfRainForecast
190 color: UbuntuColors.coolGrey
191 fontSize: "x-large"
192 horizontalAlignment: Text.AlignHCenter
193 text: i18n.tr("Chance of rain")
194 width: parent.width
195 visible: false // FIXME: add overview text eg "Chance of flurries"
196
197 property int chance: 0
198 }
199
200 ForecastDetailsDelegate {
201 id: windForecast
202 forecast: i18n.tr("Winds")
203 imageSource: "../graphics/extended-information_wind.svg"
204 }
205
206 ForecastDetailsDelegate {
207 id: uvIndexForecast
208 imageSource: "../graphics/extended-information_uv-level.svg"
209 forecast: i18n.tr("UV Index")
210 }
211
212 ForecastDetailsDelegate {
213 id: pollenForecast
214 forecast: i18n.tr("Pollen")
215 // FIXME: need icon
216 }
217
218 ForecastDetailsDelegate {
219 id: humidityForecast
220 forecast: i18n.tr("Humidity")
221 imageSource: "../graphics/extended-information_humidity.svg"
222 }
223
224 ForecastDetailsDelegate {
225 id: sunriseForecast
226 forecast: i18n.tr("Sunrise")
227 // FIXME: need icon
228 }
229
230 ForecastDetailsDelegate {
231 id: sunsetForecast
232 forecast: i18n.tr("Sunset")
233 // FIXME: need icon
234 }
235 }
236 }
237
238 Component.onCompleted: {
239 locationPages.collapseOtherDelegates.connect(function(otherIndex) {
240 if (dayDelegate && typeof index !== "undefined" && otherIndex !== index) {
241 state = "normal"
242 }
243 });
244 }
245}
0246
=== added file 'app/components/ExpandableListItem.qml'
--- app/components/ExpandableListItem.qml 1970-01-01 00:00:00 +0000
+++ app/components/ExpandableListItem.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,90 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21import Ubuntu.Components.ListItems 1.0 as ListItem
22
23/*
24 Component which extends the SDK Expandable list item and provides a easy
25 to use component where the title, subtitle and listview can be displayed. It
26 matches the design specification provided for clock.
27*/
28
29ListItem.Expandable {
30 id: expandableListItem
31
32 property ListModel model
33 property Component delegate
34 property alias text: expandableHeader.text
35 property alias subText: expandableHeader.subText
36 property alias listViewHeight: expandableList.height
37
38 anchors {
39 left: parent.left
40 right: parent.right
41 margins: units.gu(-2)
42 }
43
44 collapseOnClick: true
45 expandedHeight: contentColumn.height + units.gu(1)
46
47 Column {
48 id: contentColumn
49
50 anchors {
51 left: parent.left
52 right: parent.right
53 }
54
55 Item {
56 width: parent.width
57 height: expandableListItem.collapsedHeight
58
59 ListItem.Subtitled {
60 id: expandableHeader
61 onClicked: expandableListItem.expanded = true
62
63 Icon {
64 id: arrow
65
66 width: units.gu(2)
67 height: width
68 anchors.right: parent.right
69 anchors.verticalCenter: parent.verticalCenter
70
71 name: "go-down"
72 color: "Grey"
73 rotation: expandableListItem.expanded ? 180 : 0
74
75 Behavior on rotation {
76 UbuntuNumberAnimation {}
77 }
78 }
79 }
80 }
81
82 ListView {
83 id: expandableList
84 width: parent.width
85 interactive: false
86 model: expandableListItem.model
87 delegate: expandableListItem.delegate
88 }
89 }
90}
091
=== added file 'app/components/FastScroll.js'
--- app/components/FastScroll.js 1970-01-01 00:00:00 +0000
+++ app/components/FastScroll.js 2015-06-28 23:49:12 +0000
@@ -0,0 +1,130 @@
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** Copyright (C) 2014 Canonical Ltda
5** All rights reserved.
6** Contact: Nokia Corporation (qt-info@nokia.com)
7**
8** This file is part of the Qt Components project.
9**
10** $QT_BEGIN_LICENSE:BSD$
11** You may use this file under the terms of the BSD license as follows:
12**
13** "Redistribution and use in source and binary forms, with or without
14** modification, are permitted provided that the following conditions are
15** met:
16** * Redistributions of source code must retain the above copyright
17** notice, this list of conditions and the following disclaimer.
18** * Redistributions in binary form must reproduce the above copyright
19** notice, this list of conditions and the following disclaimer in
20** the documentation and/or other materials provided with the
21** distribution.
22** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
23** the names of its contributors may be used to endorse or promote
24** products derived from this software without specific prior written
25** permission.
26**
27** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42// FastScroll.js - this is just SectionScroller.js with a fix for
43// section.criteria == ViewSection.FirstCharacter
44var sectionData = [];
45var _sections = [];
46
47function initialize(list) {
48 initSectionData(list);
49}
50
51function contains(name) {
52 return (_sections.indexOf(name) > -1)
53}
54
55function initSectionData(list) {
56 if (!list || !list.model) return;
57 sectionData = [];
58 _sections = [];
59 var current = "",
60 prop = list.section.property,
61 sectionText;
62
63 if (list.section.criteria === ViewSection.FullString) {
64 for (var i = 0, count = list.model.count; i < count; i++) {
65 sectionText = list.getSectionText(i)
66 if (sectionText !== current) {
67 current = sectionText;
68 _sections.push(current);
69 sectionData.push({ index: i, header: current });
70 }
71 }
72 } else if (list.section.criteria === ViewSection.FirstCharacter) {
73 for (var i = 0, count = list.model.count; i < count; i++) {
74 sectionText = list.getSectionText(i).substring(0, 1)
75 if (sectionText !== current) {
76 current = sectionText
77 _sections.push(sectionText);
78 sectionData.push({ index: i, header: current });
79 }
80 }
81 }
82}
83
84function getSectionPositionString(name) {
85 var val = _sections.indexOf(name);
86 return val === 0 ? "first" :
87 val === _sections.length - 1 ? "last" : false;
88}
89
90function getAt(pos) {
91 return _sections[pos] ? _sections[pos] : "";
92}
93
94function getRelativeSections(current) {
95 var val = _sections.indexOf(current),
96 sect = [],
97 sl = _sections.length;
98
99 val = val < 1 ? 1 : val >= sl-1 ? sl-2 : val;
100 sect = [getAt(val - 1), getAt(val), getAt(val + 1)];
101
102 return sect;
103}
104
105function getClosestSection(pos, down) {
106 var tmp = (_sections.length) * pos;
107 var val = Math.ceil(tmp) // TODO: better algorithm
108 val = val < 2 ? 1 : val;
109 return _sections[val-1];
110}
111
112function getNextSection(current) {
113 var val = _sections.indexOf(current);
114 return (val > -1 ? _sections[(val < _sections.length - 1 ? val + 1 : val)] : _sections[0]) || "";
115}
116
117function getPreviousSection(current) {
118 var val = _sections.indexOf(current);
119 return (val > -1 ? _sections[(val > 0 ? val - 1 : val)] : _sections[0]) || "";
120}
121
122function getIndexFor(sectionName) {
123 var data = sectionData[_sections.indexOf(sectionName)]
124 if (data) {
125 var val = data.index;
126 return val === 0 || val > 0 ? val : -1;
127 } else {
128 return -1
129 }
130}
0131
=== added file 'app/components/FastScroll.qml'
--- app/components/FastScroll.qml 1970-01-01 00:00:00 +0000
+++ app/components/FastScroll.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,321 @@
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** Copyright (C) 2014, 2015 Canonical Ltda
5** All rights reserved.
6** Contact: Nokia Corporation (qt-info@nokia.com)
7**
8** This file is part of the Qt Components project.
9**
10** $QT_BEGIN_LICENSE:BSD$
11** You may use this file under the terms of the BSD license as follows:
12**
13** "Redistribution and use in source and binary forms, with or without
14** modification, are permitted provided that the following conditions are
15** met:
16** * Redistributions of source code must retain the above copyright
17** notice, this list of conditions and the following disclaimer.
18** * Redistributions in binary form must reproduce the above copyright
19** notice, this list of conditions and the following disclaimer in
20** the documentation and/or other materials provided with the
21** distribution.
22** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
23** the names of its contributors may be used to endorse or promote
24** products derived from this software without specific prior written
25** permission.
26**
27** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42// FastScroll.qml
43import QtQuick 2.4
44import Ubuntu.Components 1.2
45import "FastScroll.js" as Sections
46
47Item {
48 id: root
49
50 property ListView listView
51 property int pinSize: units.gu(2)
52
53 readonly property var letters: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
54 readonly property alias fastScrolling: internal.fastScrolling
55 readonly property bool showing: (rail.opacity !== 0.0)
56 readonly property double minimumHeight: rail.height
57
58 width: units.gu(7)
59 height: rail.height
60
61 onListViewChanged: {
62 if (listView && listView.model) {
63 internal.initDirtyObserver();
64 } else if (listView) {
65 listView.modelChanged.connect(function() {
66 if (listView.model) {
67 internal.initDirtyObserver();
68 }
69 });
70 }
71 }
72
73 Connections {
74 target: listView
75 onCurrentIndexChanged: {
76 if (currentIndex != -1) {
77 rail.opacity = 0.0
78 }
79 }
80 }
81
82 Rectangle {
83 id: magnified
84
85 color: Theme.palette.normal.overlay
86 radius: height * 0.3
87 height: pinSize * 2
88 width: height
89 opacity: internal.fastScrolling && root.enabled ? 1.0 : 0.0
90 x: -cursor.width - units.gu(3)
91 y: {
92 if (internal.currentItem) {
93 var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
94 return (itemCenterY - (magnified.height / 2))
95 } else {
96 return 0
97 }
98 }
99
100 Label {
101 anchors.fill: parent
102 horizontalAlignment: Text.AlignHCenter
103 verticalAlignment: Text.AlignVCenter
104 text: internal.desireSection
105 fontSize: "small"
106 }
107
108 Behavior on opacity {
109 UbuntuNumberAnimation {}
110 }
111 }
112
113 Rectangle {
114 id: cursor
115
116 property bool showLabel: false
117 property string currentSectionName: ""
118
119 radius: pinSize * 0.3
120 height: pinSize
121 width: height
122 color: Theme.palette.normal.foreground
123 opacity: rail.opacity
124 x: rail.x
125 y: {
126 if (internal.currentItem) {
127 var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
128 return (itemCenterY - (cursor.height / 2))
129 } else {
130 return 0
131 }
132 }
133 Behavior on y {
134 enabled: !internal.fastScrolling
135 UbuntuNumberAnimation { }
136 }
137 }
138
139 Column {
140 id: rail
141
142 property bool isVisible: root.enabled &&
143 (listView.flicking || dragArea.pressed) &&
144 (listView.currentIndex == -1)
145 anchors {
146 right: parent.right
147 rightMargin: units.gu(2)
148 left: parent.left
149 leftMargin: units.gu(2)
150 top: parent.top
151 }
152 height: childrenRect.height
153 opacity: 0.0
154 onIsVisibleChanged: {
155 if (isVisible) {
156 rail.opacity = 1.0
157 hideTimer.stop()
158 } else if (!root.enabled) {
159 rail.opacity = 0.0
160 } else {
161 hideTimer.restart()
162 }
163 }
164
165 Behavior on opacity {
166 UbuntuNumberAnimation { }
167 }
168
169 Repeater {
170 id: sectionsRepeater
171
172 model: root.letters
173 Label {
174 id: lbl
175
176 anchors.left: parent.left
177 height: pinSize
178 width: pinSize
179 verticalAlignment: Text.AlignVCenter
180 horizontalAlignment: Text.AlignHCenter
181 text: modelData
182 fontSize: "x-small"
183 color: cursor.y === y ? "white" : Theme.palette.selected.backgroundText
184 opacity: !internal.modelDirty && Sections.contains(text) ? 1.0 : 0.5
185 }
186 }
187
188 Timer {
189 id: hideTimer
190
191 running: false
192 interval: 2000
193 onTriggered: rail.opacity = 0.0
194 }
195 }
196
197 MouseArea {
198 id: dragArea
199
200 anchors {
201 left: parent.left
202 right: parent.right
203 }
204 y: rail.y
205 height: rail.height
206 visible: rail.opacity == 1.0
207
208 preventStealing: true
209 onPressed: {
210 internal.adjustContentPosition(mouseY)
211 dragginTimer.start()
212 }
213
214 onReleased: {
215 dragginTimer.stop()
216 internal.desireSection = ""
217 internal.fastScrolling = false
218 }
219
220 onPositionChanged: internal.adjustContentPosition(mouseY)
221
222 Timer {
223 id: dragginTimer
224
225 running: false
226 interval: 150
227 onTriggered: {
228 internal.fastScrolling = true
229 }
230 }
231 }
232
233 Timer {
234 id: dirtyTimer
235 interval: 500
236 running: false
237 onTriggered: {
238 Sections.initSectionData(listView);
239 internal.modelDirty = false;
240 }
241 }
242
243 Timer {
244 id: timerScroll
245
246 running: false
247 interval: 10
248 onTriggered: {
249 if (internal.desireSection != internal.currentSection) {
250 var idx = Sections.getIndexFor(internal.desireSection)
251 if (idx !== -1) {
252 listView.cancelFlick()
253 listView.positionViewAtIndex(idx, ListView.Beginning)
254 }
255 }
256 }
257 }
258
259 QtObject {
260 id: internal
261
262 property string currentSection: listView.currentSection
263 property string desireSection: ""
264 property string targetSection: fastScrolling ? desireSection : currentSection
265 property int oldY: 0
266 property bool modelDirty: false
267 property bool down: true
268 property bool fastScrolling: false
269 property var currentItem: null
270
271 onTargetSectionChanged: moveIndicator(targetSection)
272
273 function initDirtyObserver() {
274 Sections.initialize(listView);
275 function dirtyObserver() {
276 if (!internal.modelDirty) {
277 internal.modelDirty = true;
278 dirtyTimer.running = true;
279 }
280 }
281
282 if (listView.model.countChanged)
283 listView.model.countChanged.connect(dirtyObserver);
284
285 if (listView.model.itemsChanged)
286 listView.model.itemsChanged.connect(dirtyObserver);
287
288 if (listView.model.itemsInserted)
289 listView.model.itemsInserted.connect(dirtyObserver);
290
291 if (listView.model.itemsMoved)
292 listView.model.itemsMoved.connect(dirtyObserver);
293
294 if (listView.model.itemsRemoved)
295 listView.model.itemsRemoved.connect(dirtyObserver);
296 }
297
298 function adjustContentPosition(mouseY) {
299 var child = rail.childAt(rail.width / 2, mouseY)
300 if (!child || child.text === "") {
301 return
302 }
303 var section = child.text
304 if (internal.desireSection !== section) {
305 internal.desireSection = section
306 moveIndicator(section)
307 if (dragArea.pressed) {
308 timerScroll.restart()
309 }
310 }
311 }
312
313 function moveIndicator(section)
314 {
315 var index = root.letters.indexOf(section)
316 if (index != -1) {
317 currentItem = sectionsRepeater.itemAt(index)
318 }
319 }
320 }
321}
0322
=== added file 'app/components/ForecastDetailsDelegate.qml'
--- app/components/ForecastDetailsDelegate.qml 1970-01-01 00:00:00 +0000
+++ app/components/ForecastDetailsDelegate.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22Row {
23 height: icon.height
24 spacing: units.gu(2)
25 visible: value !== ""
26
27 property alias imageSource: icon.source
28 property alias forecast: forecastLabel.text
29 property alias value: forecastValue.text
30
31 Icon {
32 id: icon
33 color: "#000"
34 height: units.gu(2)
35 width: height
36 }
37
38 Label {
39 id: forecastLabel
40 elide: Text.ElideRight
41 width: units.gu(8)
42 }
43
44 Label {
45 id: forecastValue
46 width: units.gu(10)
47 }
48}
049
=== added file 'app/components/HeaderRow.qml'
--- app/components/HeaderRow.qml 1970-01-01 00:00:00 +0000
+++ app/components/HeaderRow.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,62 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.2
22
23RowLayout {
24 id: headerRow
25
26 property alias locationName: locationNameLabel.text
27
28 width: parent.width
29
30 Label {
31 id: locationNameLabel
32 color: UbuntuColors.darkGrey
33 elide: Text.ElideRight
34 font.weight: Font.Normal
35 fontSize: "large"
36 height: settingsButton.height
37 Layout.fillWidth: true
38 verticalAlignment: Text.AlignVCenter
39 }
40
41 AbstractButton {
42 id: settingsButton
43 height: width
44 width: units.gu(4)
45
46 onClicked: mainPageStack.push(Qt.resolvedUrl("../ui/SettingsPage.qml"))
47
48 Rectangle {
49 anchors.fill: parent
50 color: Theme.palette.selected.background
51 visible: parent.pressed
52 }
53
54 Icon {
55 anchors.centerIn: parent
56 color: UbuntuColors.darkGrey
57 height: width
58 name: "settings"
59 width: units.gu(2.5)
60 }
61 }
62}
063
=== added file 'app/components/HomeGraphic.qml'
--- app/components/HomeGraphic.qml 1970-01-01 00:00:00 +0000
+++ app/components/HomeGraphic.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22Item {
23 height: units.gu(32)
24 width: parent.width
25
26 property alias icon: iconImage.source
27
28 // TODO: will be on 'rails' (horizontal listview?) to reveal hourly forecast
29 Image {
30 id: iconImage
31 anchors {
32 centerIn: parent
33 }
34 fillMode: Image.PreserveAspectFit
35 height: parent.height
36 width: parent.width
37 }
38}
39
040
=== added file 'app/components/HomeHourly.qml'
--- app/components/HomeHourly.qml 1970-01-01 00:00:00 +0000
+++ app/components/HomeHourly.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,101 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22ListView {
23 id: homeHourly
24
25 clip:true
26 height: parent ? parent.height : undefined
27 width: parent ? parent.width : undefined
28 model: forecasts.length
29 orientation: ListView.Horizontal
30
31 property string currentDate: Qt.formatTime(new Date())
32
33 onVisibleChanged: {
34 if(visible) {
35 ListView.model = forecasts.length
36 }
37 }
38
39 MouseArea {
40 anchors.fill: parent
41 onClicked: {
42 homeGraphic.visible = true
43 }
44 }
45
46 delegate: Item {
47 id: delegate
48
49 property var hourData: forecasts[index]
50
51 height: parent.height
52 width: childrenRect.width
53
54 Column {
55 id: hourColumn
56
57 anchors.verticalCenter: parent.verticalCenter
58 height: childrenRect.height
59 width: units.gu(10)
60
61 Label {
62 anchors.horizontalCenter: parent.horizontalCenter
63 fontSize: "small"
64 font.weight: Font.Light
65 text: currentDate.search(Qt.locale().amText) !== -1 || currentDate.search(Qt.locale().pmText) !== -1 ? "%1 %2".arg(formatTimestamp(hourData.date, 'ddd')).arg(formatTime(hourData.date, 'hap')) : "%1 %2".arg(formatTimestamp(hourData.date, 'ddd')).arg(formatTime(hourData.date, 'h:mm'))
66 }
67
68 Item {
69 anchors.horizontalCenter: parent.horizontalCenter
70 height: units.gu(7)
71 width: units.gu(7)
72
73 Icon {
74 anchors {
75 fill: parent
76 margins: units.gu(0.5)
77 }
78 color: UbuntuColors.orange
79 name: (hourData.icon !== undefined && iconMap[hourData.icon] !== undefined) ? iconMap[hourData.icon] : ""
80 }
81 }
82
83 Label {
84 anchors.horizontalCenter: parent.horizontalCenter
85 font.pixelSize: units.gu(3)
86 font.weight: Font.Light
87 text: Math.round(hourData[tempUnits].temp).toString()+settings.tempScale
88 }
89 }
90
91 Rectangle {
92 anchors.verticalCenter: parent.verticalCenter
93 color: UbuntuColors.darkGrey
94 height: hourColumn.height
95 opacity: 0.2
96 visible: index > 0
97 width: units.gu(0.1)
98 }
99 }
100}
101
0102
=== added file 'app/components/HomeTempInfo.qml'
--- app/components/HomeTempInfo.qml 1970-01-01 00:00:00 +0000
+++ app/components/HomeTempInfo.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,73 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22
23Column {
24 anchors {
25 left: parent.left
26 right: parent.right
27 }
28 spacing: units.gu(1)
29
30 property alias description: descriptionLabel.text
31 property alias high: highLabel.text
32 property alias low: lowLabel.text
33 property alias now: nowLabel.text
34
35 Label {
36 font.weight: Font.Light
37 fontSize: "small"
38 text: i18n.tr("Today")
39 }
40
41 Label {
42 id: descriptionLabel
43 font.weight: Font.Normal
44 fontSize: "large"
45 }
46
47 Row {
48 spacing: units.gu(2)
49
50 Label {
51 id: nowLabel
52 color: UbuntuColors.orange
53 font.pixelSize: units.gu(8)
54 font.weight: Font.Light
55 height: units.gu(8)
56 verticalAlignment: Text.AlignBottom // AlignBottom seems to put it at the top?
57 }
58
59 Column {
60 Label {
61 id: lowLabel
62 font.weight: Font.Light
63 fontSize: "medium"
64 }
65
66 Label {
67 id: highLabel
68 font.weight: Font.Light
69 fontSize: "medium"
70 }
71 }
72 }
73}
074
=== added directory 'app/components/ListItemActions'
=== added file 'app/components/ListItemActions/CMakeLists.txt'
--- app/components/ListItemActions/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/ListItemActions/CMakeLists.txt 2015-06-28 23:49:12 +0000
@@ -0,0 +1,5 @@
1file(GLOB LISTITEMACTIONS_QML_JS_FILES *.qml *.js)
2
3add_custom_target(ubuntu-weather-app_listitemactions_QMlFiles ALL SOURCES ${LISTITEMACTIONS_QML_JS_FILES})
4
5install(FILES ${LISTITEMACTIONS_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/components/ListItemActions)
06
=== added file 'app/components/ListItemActions/CheckBox.qml'
--- app/components/ListItemActions/CheckBox.qml 1970-01-01 00:00:00 +0000
+++ app/components/ListItemActions/CheckBox.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,25 @@
1/*
2 * Copyright (C) 2012-2014, 2015 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
17import QtQuick 2.4
18import Ubuntu.Components 1.2
19
20CheckBox {
21 checked: root.selected
22 width: implicitWidth
23 // disable item mouse area to avoid conflicts with parent mouse area
24 __mouseArea.enabled: false
25}
026
=== added file 'app/components/ListItemActions/Remove.qml'
--- app/components/ListItemActions/Remove.qml 1970-01-01 00:00:00 +0000
+++ app/components/ListItemActions/Remove.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,27 @@
1/*
2 * Copyright (C) 2014, 2015 Andrew Hayzen <ahayzen@gmail.com>
3 * Daniel Holm <d.holmen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22Action {
23 id: removeAction
24 iconName: "delete"
25 objectName: "swipeDeleteAction"
26 text: i18n.tr("Remove")
27}
028
=== added file 'app/components/ListItemReorderComponent.qml'
--- app/components/ListItemReorderComponent.qml 1970-01-01 00:00:00 +0000
+++ app/components/ListItemReorderComponent.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,106 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.4
21import Ubuntu.Components 1.2
22
23
24Item {
25 id: actionReorder
26 width: units.gu(4)
27
28 Icon {
29 anchors {
30 horizontalCenter: parent.horizontalCenter
31 verticalCenter: parent.verticalCenter
32 }
33 name: "navigation-menu" // TODO: use proper image
34 height: width
35 width: units.gu(3)
36 }
37
38 MouseArea {
39 id: actionReorderMouseArea
40 anchors {
41 fill: parent
42 }
43 property int startY: 0
44 property int startContentY: 0
45
46 onPressed: {
47 root.parent.parent.interactive = false; // stop scrolling of listview
48 startY = root.y;
49 startContentY = root.parent.parent.contentY;
50 root.z += 10; // force ontop of other elements
51
52 console.debug("Reorder listitem pressed", root.y)
53 }
54 onMouseYChanged: root.y += mouse.y - (root.height / 2);
55 onReleased: {
56 console.debug("Reorder diff by position", getDiff());
57
58 var diff = getDiff();
59
60 // Remove the height of the actual item if moved down
61 if (diff > 0) {
62 diff -= 1;
63 }
64
65 root.parent.parent.interactive = true; // reenable scrolling
66
67 if (diff === 0) {
68 // Nothing has changed so reset the item
69 // z index is restored after animation
70 resetListItemYAnimation.start();
71 }
72 else {
73 var newIndex = index + diff;
74
75 if (newIndex < 0) {
76 newIndex = 0;
77 }
78 else if (newIndex > root.parent.parent.count - 1) {
79 newIndex = root.parent.parent.count - 1;
80 }
81
82 root.z -= 10; // restore z index
83 reorder(index, newIndex)
84 }
85 }
86
87 function getDiff() {
88 // Get the amount of items that have been passed over (by centre)
89 return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
90 }
91 }
92
93 SequentialAnimation {
94 id: resetListItemYAnimation
95 UbuntuNumberAnimation {
96 target: root;
97 property: "y";
98 to: actionReorderMouseArea.startY
99 }
100 ScriptAction {
101 script: {
102 root.z -= 10; // restore z index
103 }
104 }
105 }
106}
0107
=== added file 'app/components/ListItemWithActions.qml'
--- app/components/ListItemWithActions.qml 1970-01-01 00:00:00 +0000
+++ app/components/ListItemWithActions.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,496 @@
1/*
2 * Copyright (C) 2012-2015 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
17import QtQuick 2.4
18import Ubuntu.Components 1.2
19import Ubuntu.Components.ListItems 1.0 as ListItem
20
21
22Item {
23 id: root
24 width: parent.width
25
26 property Action leftSideAction: null
27 property list<Action> rightSideActions
28 property double defaultHeight: units.gu(8)
29 property bool locked: false
30 property Action activeAction: null
31 property var activeItem: null
32 property bool triggerActionOnMouseRelease: false
33 property color color: Theme.palette.normal.background
34 property color selectedColor: "#E6E6E6"
35 property bool selected: false
36 property bool selectionMode: false
37 property alias internalAnchors: mainContents.anchors
38 default property alias contents: mainContents.children
39
40 readonly property double actionWidth: units.gu(4)
41 readonly property double leftActionWidth: units.gu(10)
42 readonly property double actionThreshold: actionWidth * 0.4
43 readonly property double threshold: 0.4
44 readonly property string swipeState: main.x == 0 ? "Normal" : main.x > 0 ? "LeftToRight" : "RightToLeft"
45 readonly property alias swipping: mainItemMoving.running
46 readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
47
48 property alias _main: main // CUSTOM
49 property alias pressed: mouseArea.pressed // CUSTOM
50
51 /* internal */
52 property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
53
54 signal itemClicked(var mouse)
55 signal itemPressAndHold(var mouse)
56
57 function returnToBoundsRTL(direction)
58 {
59 var actionFullWidth = actionWidth + units.gu(2)
60
61 // go back to normal state if swipping reverse
62 if (direction === "LTR") {
63 updatePosition(0)
64 return
65 } else if (!triggerActionOnMouseRelease) {
66 updatePosition(-rightActionsView.width + units.gu(2))
67 return
68 }
69
70 var xOffset = Math.abs(main.x)
71 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
72 var newX = 0
73
74 if (index === _visibleRightSideActions.length) {
75 newX = -(rightActionsView.width - units.gu(2))
76 } else if (index >= 1) {
77 newX = -(actionFullWidth * index)
78 }
79
80 updatePosition(newX)
81 }
82
83 function returnToBoundsLTR(direction)
84 {
85 var finalX = leftActionWidth
86 if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))
87 finalX = 0
88 updatePosition(finalX)
89 }
90
91 function returnToBounds(direction)
92 {
93 if (main.x < 0) {
94 returnToBoundsRTL(direction)
95 } else if (main.x > 0) {
96 returnToBoundsLTR(direction)
97 } else {
98 updatePosition(0)
99 }
100 }
101
102 function contains(item, point, marginX)
103 {
104 var itemStartX = item.x - marginX
105 var itemEndX = item.x + item.width + marginX
106 return (point.x >= itemStartX) && (point.x <= itemEndX) &&
107 (point.y >= item.y) && (point.y <= (item.y + item.height));
108 }
109
110 function getActionAt(point)
111 {
112 if (leftSideAction && contains(leftActionViewLoader.item, point, 0)) {
113 return leftSideAction
114 } else if (contains(rightActionsView, point, 0)) {
115 var newPoint = root.mapToItem(rightActionsView, point.x, point.y)
116 for (var i = 0; i < rightActionsRepeater.count; i++) {
117 var child = rightActionsRepeater.itemAt(i)
118 if (contains(child, newPoint, units.gu(1))) {
119 return i
120 }
121 }
122 }
123 return -1
124 }
125
126 function updateActiveAction()
127 {
128 if (triggerActionOnMouseRelease &&
129 (main.x <= -(root.actionWidth + units.gu(2))) &&
130 (main.x > -(rightActionsView.width - units.gu(2)))) {
131 var actionFullWidth = actionWidth + units.gu(2)
132 var xOffset = Math.abs(main.x)
133 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
134 index = index - 1
135 if (index > -1) {
136 root.activeItem = rightActionsRepeater.itemAt(index)
137 root.activeAction = root._visibleRightSideActions[index]
138 }
139 } else {
140 root.activeAction = null
141 }
142 }
143
144 function resetSwipe()
145 {
146 updatePosition(0)
147 }
148
149 function filterVisibleActions(actions)
150 {
151 var visibleActions = []
152 for(var i = 0; i < actions.length; i++) {
153 var action = actions[i]
154 if (action.visible) {
155 visibleActions.push(action)
156 }
157 }
158 return visibleActions
159 }
160
161 function updatePosition(pos)
162 {
163 if (!root.triggerActionOnMouseRelease && (pos !== 0)) {
164 mouseArea.state = pos > 0 ? "RightToLeft" : "LeftToRight"
165 } else {
166 mouseArea.state = ""
167 }
168 main.x = pos
169 }
170
171 // CUSTOM remove animation
172 SequentialAnimation {
173 id: removeAnimation
174
175 property var action
176
177 UbuntuNumberAnimation {
178 target: root
179 duration: UbuntuAnimation.BriskDuration
180 property: "height";
181 to: 0
182 }
183 ScriptAction {
184 script: removeAnimation.action.trigger()
185 }
186 }
187
188 states: [
189 State {
190 name: "select"
191 when: selectionMode || selected
192 PropertyChanges {
193 target: selectionIcon
194 source: Qt.resolvedUrl("ListItemActions/CheckBox.qml")
195 anchors.leftMargin: units.gu(2)
196 }
197 PropertyChanges {
198 target: root
199 locked: true
200 }
201 PropertyChanges {
202 target: main
203 x: 0
204 }
205 }
206 ]
207
208 height: defaultHeight
209 //clip: height !== defaultHeight // CUSTOM
210
211 Loader { // CUSTOM
212 id: leftActionViewLoader
213 anchors {
214 top: parent.top
215 bottom: parent.bottom
216 right: main.left
217 }
218 asynchronous: true
219 sourceComponent: leftSideAction ? leftActionViewComponent : undefined
220 }
221
222 Component { // CUSTOM
223 id: leftActionViewComponent
224
225 Rectangle {
226 id: leftActionView
227 width: root.leftActionWidth + actionThreshold
228 color: UbuntuColors.red
229
230 Icon {
231 id: leftActionIcon
232 anchors {
233 centerIn: parent
234 horizontalCenterOffset: actionThreshold / 2
235 }
236 objectName: "swipeDeleteAction" // CUSTOM
237 name: leftSideAction && _showActions ? leftSideAction.iconName : ""
238 color: Theme.palette.selected.field
239 height: units.gu(3)
240 width: units.gu(3)
241 }
242 }
243 }
244
245 //Rectangle {
246 Item { // CUSTOM
247 id: rightActionsView
248
249 anchors {
250 top: main.top
251 left: main.right
252 bottom: main.bottom
253 }
254 visible: _visibleRightSideActions.length > 0
255 width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(2) : 0
256 // color: "white" // CUSTOM
257
258 Row {
259 anchors{
260 top: parent.top
261 left: parent.left
262 leftMargin: units.gu(2)
263 right: parent.right
264 rightMargin: units.gu(2)
265 bottom: parent.bottom
266 }
267 spacing: units.gu(2)
268 Repeater {
269 id: rightActionsRepeater
270
271 model: _showActions ? _visibleRightSideActions : []
272 Item {
273 property alias image: img
274
275 height: rightActionsView.height
276 width: root.actionWidth
277
278 Icon {
279 id: img
280
281 anchors.centerIn: parent
282 objectName: rightSideActions[index].objectName // CUSTOM
283 width: units.gu(3)
284 height: units.gu(3)
285 name: modelData.iconName
286 color: root.activeAction === modelData ? UbuntuColors.orange : UbuntuColors.coolGrey // CUSTOM
287 }
288 }
289 }
290 }
291 }
292
293 Rectangle {
294 id: main
295 objectName: "mainItem"
296
297 anchors {
298 top: parent.top
299 bottom: parent.bottom
300 }
301
302 width: parent.width
303 color: root.selected ? root.selectedColor : root.color
304
305 Loader {
306 id: selectionIcon
307
308 anchors {
309 left: main.left
310 verticalCenter: main.verticalCenter
311 }
312 asynchronous: true // CUSTOM
313 width: (status === Loader.Ready) ? item.implicitWidth : 0
314 visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
315
316 Behavior on width {
317 NumberAnimation {
318 duration: UbuntuAnimation.SnapDuration
319 }
320 }
321 }
322
323 Item {
324 id: mainContents
325
326 anchors {
327 left: selectionIcon.right
328 //leftMargin: units.gu(2) // CUSTOM
329 top: parent.top
330 //topMargin: units.gu(1) // CUSTOM
331 right: parent.right
332 //rightMargin: units.gu(2) // CUSTOM
333 bottom: parent.bottom
334 //bottomMargin: units.gu(1) // CUSTOM
335 }
336 }
337
338 Behavior on x {
339 UbuntuNumberAnimation {
340 id: mainItemMoving
341
342 easing.type: Easing.OutElastic
343 duration: UbuntuAnimation.SlowDuration
344 }
345 }
346 }
347
348 SequentialAnimation {
349 id: triggerAction
350
351 property var currentItem: root.activeItem ? root.activeItem.image : null
352
353 running: false
354 ParallelAnimation {
355 UbuntuNumberAnimation {
356 target: triggerAction.currentItem
357 property: "opacity"
358 from: 1.0
359 to: 0.0
360 duration: UbuntuAnimation.SlowDuration
361 easing {type: Easing.InOutBack; }
362 }
363 UbuntuNumberAnimation {
364 target: triggerAction.currentItem
365 properties: "width, height"
366 from: units.gu(3)
367 to: root.actionWidth
368 duration: UbuntuAnimation.SlowDuration
369 easing {type: Easing.InOutBack; }
370 }
371 }
372 PropertyAction {
373 target: triggerAction.currentItem
374 properties: "width, height"
375 value: units.gu(3)
376 }
377 PropertyAction {
378 target: triggerAction.currentItem
379 properties: "opacity"
380 value: 1.0
381 }
382 ScriptAction {
383 script: {
384 root.activeAction.triggered(root)
385 mouseArea.state = ""
386 }
387 }
388 PauseAnimation {
389 duration: 500
390 }
391 UbuntuNumberAnimation {
392 target: main
393 property: "x"
394 to: 0
395 }
396 }
397
398 MouseArea {
399 id: mouseArea
400
401 property bool locked: root.locked || ((root.leftSideAction === null) && (root._visibleRightSideActions.count === 0)) // CUSTOM
402 property bool manual: false
403 property string direction: "None"
404 property real lastX: -1
405
406 anchors.fill: parent
407 drag {
408 target: locked ? null : main
409 axis: Drag.XAxis
410 minimumX: rightActionsView.visible ? -(rightActionsView.width) : 0
411 maximumX: leftSideAction ? leftActionViewLoader.item.width : 0
412 threshold: root.actionThreshold
413 }
414
415 states: [
416 State {
417 name: "LeftToRight"
418 PropertyChanges {
419 target: mouseArea
420 drag.maximumX: 0
421 }
422 },
423 State {
424 name: "RightToLeft"
425 PropertyChanges {
426 target: mouseArea
427 drag.minimumX: 0
428 }
429 }
430 ]
431
432 onMouseXChanged: {
433 var offset = (lastX - mouseX)
434 if (Math.abs(offset) <= root.actionThreshold) {
435 return
436 }
437 lastX = mouseX
438 direction = offset > 0 ? "RTL" : "LTR";
439 }
440
441 onPressed: {
442 lastX = mouse.x
443 }
444
445 onReleased: {
446 if (root.triggerActionOnMouseRelease && root.activeAction) {
447 triggerAction.start()
448 } else {
449 root.returnToBounds()
450 root.activeAction = null
451 }
452 lastX = -1
453 direction = "None"
454 }
455 onClicked: {
456 if (selectionMode) { // CUSTOM - selecting a listitem should toggle selection if in selectionMode
457 selected = !selected
458 return
459 } else if (main.x === 0) {
460 root.itemClicked(mouse)
461 } else if (main.x > 0) {
462 var action = getActionAt(Qt.point(mouse.x, mouse.y))
463 if (action && action !== -1) {
464 //action.triggered(root)
465 removeAnimation.action = action // CUSTOM - use our animation instead
466 removeAnimation.start() // CUSTOM
467 }
468 } else {
469 var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
470
471 if (actionIndex !== -1 && actionIndex !== leftSideAction) { // CUSTOM - can be leftAction
472 root.activeItem = rightActionsRepeater.itemAt(actionIndex)
473 root.activeAction = root.rightSideActions[actionIndex]
474 triggerAction.start()
475 return
476 }
477 }
478 root.resetSwipe()
479 }
480
481 onPositionChanged: {
482 if (mouseArea.pressed) {
483 updateActiveAction()
484
485 listItemSwiping(index) // CUSTOM - tells other listitems to dismiss any swipe
486 }
487 }
488 onPressAndHold: {
489 if (main.x === 0) {
490 root.itemPressAndHold(mouse)
491 }
492 }
493
494 z: -1
495 }
496}
0497
=== added file 'app/components/LoadingIndicator.qml'
--- app/components/LoadingIndicator.qml 1970-01-01 00:00:00 +0000
+++ app/components/LoadingIndicator.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,87 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22Rectangle {
23 id: indicator
24 objectName: "processingIndicator"
25 anchors {
26 left: parent.left
27 right: parent.right
28 bottom: parent.bottom
29 bottomMargin: Qt.inputMethod.keyboardRectangle.height
30 }
31 height: units.dp(3)
32 color: "white"
33 opacity: 0
34 visible: opacity > 0
35
36 readonly property bool processing: loading
37
38 Behavior on opacity {
39 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
40 }
41
42 onProcessingChanged: {
43 if (processing) delay.start();
44 else if (!persist.running) indicator.opacity = 0;
45 }
46
47 Timer {
48 id: delay
49 interval: 200
50 onTriggered: if (indicator.processing) {
51 persist.restart();
52 indicator.opacity = 1;
53 }
54 }
55
56 Timer {
57 id: persist
58 interval: 2 * UbuntuAnimation.SleepyDuration - UbuntuAnimation.FastDuration
59 onTriggered: if (!indicator.processing) indicator.opacity = 0
60 }
61
62 Rectangle {
63 id: orange
64 anchors { top: parent.top; bottom: parent.bottom }
65 width: parent.width / 4
66 color: UbuntuColors.orange
67
68 SequentialAnimation {
69 running: indicator.visible
70 loops: Animation.Infinite
71 XAnimator {
72 from: -orange.width / 2
73 to: indicator.width - orange.width / 2
74 duration: UbuntuAnimation.SleepyDuration
75 easing.type: Easing.InOutSine
76 target: orange
77 }
78 XAnimator {
79 from: indicator.width - orange.width / 2
80 to: -orange.width / 2
81 duration: UbuntuAnimation.SleepyDuration
82 easing.type: Easing.InOutSine
83 target: orange
84 }
85 }
86 }
87}
088
=== added file 'app/components/MultiSelectHeadState.qml'
--- app/components/MultiSelectHeadState.qml 1970-01-01 00:00:00 +0000
+++ app/components/MultiSelectHeadState.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,72 @@
1/*
2 * Copyright (C) 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import Ubuntu.Components 1.2
21
22PageHeadState {
23 id: selectionState
24 actions: [
25 Action {
26 iconName: "select"
27 text: i18n.tr("Select All")
28 onTriggered: {
29 if (listview.selectedItems.length === listview.model.count) {
30 listview.clearSelection()
31 } else {
32 listview.selectAll()
33 }
34 }
35 },
36 Action {
37 enabled: listview.selectedItems.length > 0
38 iconName: "delete"
39 text: i18n.tr("Delete")
40 visible: removable
41
42 onTriggered: {
43 removed(listview.selectedItems)
44
45 listview.closeSelection()
46 }
47 }
48
49 ]
50 backAction: Action {
51 text: i18n.tr("Cancel selection")
52 iconName: "back"
53 onTriggered: {
54 listview.clearSelection()
55 listview.state = "normal"
56 }
57 }
58 head: thisPage.head
59 name: "selection"
60
61 PropertyChanges {
62 target: thisPage.head
63 backAction: selectionState.backAction
64 actions: selectionState.actions
65 }
66
67 property ListView listview
68 property bool removable: false
69 property Page thisPage
70
71 signal removed(var selectedItems)
72}
073
=== added file 'app/components/MultiSelectListView.qml'
--- app/components/MultiSelectListView.qml 1970-01-01 00:00:00 +0000
+++ app/components/MultiSelectListView.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,52 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.4
21import Ubuntu.Components 1.2
22
23
24WeatherListView {
25 property var selectedItems: []
26
27 signal clearSelection()
28 signal closeSelection()
29 signal selectAll()
30
31 onClearSelection: selectedItems = []
32 onCloseSelection: {
33 clearSelection()
34 state = "normal"
35 }
36 onSelectAll: {
37 var tmp = selectedItems
38
39 for (var i=0; i < model.count; i++) {
40 if (tmp.indexOf(i) === -1) {
41 tmp.push(i)
42 }
43 }
44
45 selectedItems = tmp
46 }
47 onVisibleChanged: {
48 if (!visible) {
49 closeSelection()
50 }
51 }
52}
053
=== added file 'app/components/PageWithBottomEdge.qml'
--- app/components/PageWithBottomEdge.qml 1970-01-01 00:00:00 +0000
+++ app/components/PageWithBottomEdge.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,411 @@
1/*
2 * Copyright (C) 2014, 2015 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/*
18 Example:
19
20 MainView {
21 objectName: "mainView"
22
23 applicationName: "com.ubuntu.developer.boiko.bottomedge"
24
25 width: units.gu(100)
26 height: units.gu(75)
27
28 Component {
29 id: pageComponent
30
31 PageWithBottomEdge {
32 id: mainPage
33 title: i18n.tr("Main Page")
34
35 Rectangle {
36 anchors.fill: parent
37 color: "white"
38 }
39
40 bottomEdgePageComponent: Page {
41 title: "Contents"
42 anchors.fill: parent
43 //anchors.topMargin: contentsPage.flickable.contentY
44
45 ListView {
46 anchors.fill: parent
47 model: 50
48 delegate: ListItems.Standard {
49 text: "One Content Item: " + index
50 }
51 }
52 }
53 bottomEdgeTitle: i18n.tr("Bottom edge action")
54 }
55 }
56
57 PageStack {
58 id: stack
59 Component.onCompleted: stack.push(pageComponent)
60 }
61 }
62
63*/
64
65import QtQuick 2.4
66import Ubuntu.Components 1.2
67
68Page {
69 id: page
70
71 property alias bottomEdgePageComponent: edgeLoader.sourceComponent
72 property alias bottomEdgePageSource: edgeLoader.source
73 property alias bottomEdgeTitle: tipLabel.text
74 property bool bottomEdgeEnabled: true
75 property int bottomEdgeExpandThreshold: page.height * 0.2
76 property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded
77 property bool reloadBottomEdgePage: true
78
79 readonly property alias bottomEdgePage: edgeLoader.item
80 readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active)
81 readonly property bool isCollapsed: (bottomEdge.y === page.height)
82 readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready)
83
84 property bool _showEdgePageWhenReady: false
85 property int _areaWhenExpanded: 0
86
87 // CUSTOM properties to allow changing of color
88 property alias tipColor: tip.color
89 property alias tipLabelColor: tipLabel.color
90
91 signal bottomEdgeReleased()
92 signal bottomEdgeDismissed()
93
94
95 function showBottomEdgePage(source, properties)
96 {
97 edgeLoader.setSource(source, properties)
98 _showEdgePageWhenReady = true
99 }
100
101 function setBottomEdgePage(source, properties)
102 {
103 edgeLoader.setSource(source, properties)
104 }
105
106 function _pushPage()
107 {
108 if (edgeLoader.status === Loader.Ready) {
109 edgeLoader.item.active = true
110 page.pageStack.push(edgeLoader.item)
111 if (edgeLoader.item.flickable) {
112 edgeLoader.item.flickable.contentY = -page.header.height
113 edgeLoader.item.flickable.returnToBounds()
114 }
115 if (edgeLoader.item.ready)
116 edgeLoader.item.ready()
117 }
118 }
119
120
121 Component.onCompleted: {
122 // avoid a binding on the expanded height value
123 var expandedHeight = height;
124 _areaWhenExpanded = expandedHeight;
125 }
126
127 onActiveChanged: {
128 if (active) {
129 bottomEdge.state = "collapsed"
130 }
131 }
132
133 onBottomEdgePageLoadedChanged: {
134 if (_showEdgePageWhenReady && bottomEdgePageLoaded) {
135 bottomEdge.state = "expanded"
136 _showEdgePageWhenReady = false
137 }
138 }
139
140 Rectangle {
141 id: bgVisual
142
143 color: "black"
144 anchors.fill: page
145 opacity: 0.7 * ((page.height - bottomEdge.y) / page.height)
146 z: 1
147 }
148
149 UbuntuShape {
150 id: tip
151 objectName: "bottomEdgeTip"
152
153 property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y)
154
155 enabled: mouseArea.enabled
156 visible: page.bottomEdgeEnabled
157 anchors {
158 bottom: parent.bottom
159 horizontalCenter: bottomEdge.horizontalCenter
160 bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1)
161 Behavior on bottomMargin {
162 SequentialAnimation {
163 // wait some msecs in case of the focus change again, to avoid flickering
164 PauseAnimation {
165 duration: 300
166 }
167 UbuntuNumberAnimation {
168 duration: UbuntuAnimation.SnapDuration
169 }
170 }
171 }
172 }
173
174 z: 1
175 width: tipLabel.paintedWidth + units.gu(6)
176 height: bottomEdge.tipHeight + units.gu(1)
177 color: Theme.palette.normal.overlay
178 Label {
179 id: tipLabel
180
181 anchors {
182 top: parent.top
183 left: parent.left
184 right: parent.right
185 }
186 height: bottomEdge.tipHeight
187 verticalAlignment: Text.AlignVCenter
188 horizontalAlignment: Text.AlignHCenter
189 opacity: tip.hidden ? 0.0 : 1.0
190 Behavior on opacity {
191 UbuntuNumberAnimation {
192 duration: UbuntuAnimation.SnapDuration
193 }
194 }
195 }
196 }
197
198 Rectangle {
199 id: shadow
200
201 anchors {
202 left: parent.left
203 right: parent.right
204 bottom: parent.bottom
205 }
206 height: units.gu(1)
207 z: 1
208 opacity: 0.0
209 gradient: Gradient {
210 GradientStop { position: 0.0; color: "transparent" }
211 GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
212 }
213 }
214
215 MouseArea {
216 id: mouseArea
217
218 property real previousY: -1
219 property string dragDirection: "None"
220
221 preventStealing: true
222 drag {
223 axis: Drag.YAxis
224 target: bottomEdge
225 minimumY: bottomEdge.pageStartY
226 maximumY: page.height
227 }
228 enabled: edgeLoader.status == Loader.Ready
229 visible: page.bottomEdgeEnabled
230
231 anchors {
232 left: parent.left
233 right: parent.right
234 bottom: parent.bottom
235
236 }
237 height: bottomEdge.tipHeight
238 z: 1
239
240 onReleased: {
241 page.bottomEdgeReleased()
242 if ((dragDirection === "BottomToTop") &&
243 bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
244 bottomEdge.state = "expanded"
245 } else {
246 bottomEdge.state = "collapsed"
247 }
248 previousY = -1
249 dragDirection = "None"
250 }
251
252 onPressed: {
253 previousY = mouse.y
254 tip.forceActiveFocus()
255 }
256
257 onMouseYChanged: {
258 var yOffset = previousY - mouseY
259 // skip if was a small move
260 if (Math.abs(yOffset) <= units.gu(2)) {
261 return
262 }
263 previousY = mouseY
264 dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom"
265 }
266 }
267
268 Rectangle {
269 id: bottomEdge
270 objectName: "bottomEdge"
271
272 readonly property int tipHeight: units.gu(3)
273 readonly property int pageStartY: 0
274
275 z: 1
276 color: Theme.palette.normal.background
277 clip: true
278 anchors {
279 left: parent.left
280 right: parent.right
281 }
282 height: page.height
283 y: height
284 visible: !page.isCollapsed
285 state: "collapsed"
286 states: [
287 State {
288 name: "collapsed"
289 PropertyChanges {
290 target: bottomEdge
291 y: bottomEdge.height
292 }
293 },
294 State {
295 name: "expanded"
296 PropertyChanges {
297 target: bottomEdge
298 y: bottomEdge.pageStartY
299 }
300 },
301 State {
302 name: "floating"
303 when: mouseArea.drag.active
304 PropertyChanges {
305 target: shadow
306 opacity: 1.0
307 }
308 }
309 ]
310
311 transitions: [
312 Transition {
313 to: "expanded"
314 SequentialAnimation {
315 alwaysRunToEnd: true
316
317 SmoothedAnimation {
318 target: bottomEdge
319 property: "y"
320 duration: UbuntuAnimation.FastDuration
321 easing.type: Easing.Linear
322 }
323 SmoothedAnimation {
324 target: edgeLoader
325 property: "anchors.topMargin"
326 to: - units.gu(4)
327 duration: UbuntuAnimation.FastDuration
328 easing.type: Easing.Linear
329 }
330 SmoothedAnimation {
331 target: edgeLoader
332 property: "anchors.topMargin"
333 to: 0
334 duration: UbuntuAnimation.FastDuration
335 easing: UbuntuAnimation.StandardEasing
336 }
337 ScriptAction {
338 script: page._pushPage()
339 }
340 }
341 },
342 Transition {
343 from: "expanded"
344 to: "collapsed"
345 SequentialAnimation {
346 alwaysRunToEnd: true
347
348 ScriptAction {
349 script: {
350 Qt.inputMethod.hide()
351 edgeLoader.item.parent = edgeLoader
352 edgeLoader.item.anchors.fill = edgeLoader
353 edgeLoader.item.active = false
354 }
355 }
356 SmoothedAnimation {
357 target: bottomEdge
358 property: "y"
359 duration: UbuntuAnimation.SlowDuration
360 }
361 ScriptAction {
362 script: {
363 // destroy current bottom page
364 if (page.reloadBottomEdgePage) {
365 edgeLoader.active = false
366 // tip will receive focus on page active true
367 } else {
368 tip.forceActiveFocus()
369 }
370
371 // notify
372 page.bottomEdgeDismissed()
373
374 edgeLoader.active = true
375 }
376 }
377 }
378 },
379 Transition {
380 from: "floating"
381 to: "collapsed"
382 SmoothedAnimation {
383 target: bottomEdge
384 property: "y"
385 duration: UbuntuAnimation.FastDuration
386 }
387 }
388 ]
389
390 Loader {
391 id: edgeLoader
392
393 asynchronous: true
394 anchors.fill: parent
395 //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
396 Binding {
397 target: edgeLoader.status === Loader.Ready ? edgeLoader : null
398 property: "anchors.topMargin"
399 value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
400 when: !page.isReady
401 }
402
403 onLoaded: {
404 tip.forceActiveFocus()
405 if (page.isReady && edgeLoader.item.active !== true) {
406 page._pushPage()
407 }
408 }
409 }
410 }
411}
0412
=== added file 'app/components/StandardListItem.qml'
--- app/components/StandardListItem.qml 1970-01-01 00:00:00 +0000
+++ app/components/StandardListItem.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This file is part of Ubuntu Weather App
5 *
6 * Ubuntu Weather App is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3 as
8 * published by the Free Software Foundation.
9 *
10 * Ubuntu Weather App is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.4
20import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.2
22
23ListItem {
24 id: listItem
25
26 property alias title: _title.text
27 property alias icon: _icon.name
28 property alias showIcon: _icon.visible
29
30 RowLayout {
31 anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter; margins: units.gu(2) }
32 height: _icon.height
33 spacing: units.gu(2)
34
35 Label {
36 id: _title
37 anchors.verticalCenter: _icon.verticalCenter
38 elide: Text.ElideRight
39 Layout.fillWidth: true
40 }
41
42 Icon {
43 id: _icon
44 height: units.gu(2); width: height
45 name: "go-next"
46 }
47 }
48}
049
=== added file 'app/components/WeatherListItem.qml'
--- app/components/WeatherListItem.qml 1970-01-01 00:00:00 +0000
+++ app/components/WeatherListItem.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,137 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.4
21import Ubuntu.Components 1.2
22
23ListItemWithActions {
24 id: root
25
26 property int listItemIndex: index
27 property bool multiselectable: false
28 property int previousListItemIndex: -1
29 property bool reorderable: false
30
31 signal reorder(int from, int to)
32
33 onItemPressAndHold: {
34 if (multiselectable) {
35 selectionMode = true
36 }
37 }
38
39 onListItemIndexChanged: {
40 var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)
41
42 if (i !== -1) {
43 parent.parent.selectedItems[i] = listItemIndex
44 }
45
46 previousListItemIndex = listItemIndex
47 }
48
49 onSelectedChanged: {
50 if (selectionMode) {
51 var tmp = parent.parent.selectedItems
52
53 if (selected) {
54 if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
55 tmp.push(listItemIndex)
56 parent.parent.selectedItems = tmp
57 }
58 } else {
59 tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
60 parent.parent.selectedItems = tmp
61 }
62 }
63 }
64
65 onSelectionModeChanged: {
66 if (reorderable && selectionMode) {
67 resetSwipe()
68 }
69
70 for (var j=0; j < _main.children.length; j++) {
71 if (_main.children[j] !== actionReorderLoader) {
72 _main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
73 }
74 }
75
76 parent.parent.state = selectionMode ? "multiselectable" : "normal"
77
78 if (!selectionMode) {
79 selected = false
80 }
81 }
82
83 /* Highlight the listitem on press */
84 Rectangle {
85 id: listItemBrighten
86 color: root.pressed ? UbuntuColors.coolGrey : "transparent"
87 opacity: 0.1
88 height: root.height
89 x: root.x - parent.x // -parent.x due to selectionIcon in ListItemWithActions
90 width: root.width
91 }
92
93 /* Reorder Component */
94 Loader {
95 id: actionReorderLoader
96 active: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
97 anchors {
98 bottom: parent.bottom
99 right: parent.right
100 rightMargin: units.gu(1)
101 top: parent.top
102 }
103 asynchronous: true
104 source: "ListItemReorderComponent.qml"
105 }
106
107 Item {
108 Connections { // Only allow one ListItem to be swiping at any time
109 target: weatherApp
110 onListItemSwiping: {
111 if (i !== index) {
112 root.resetSwipe();
113 }
114 }
115 }
116
117 Connections { // Connections from signals in the ListView
118 target: root.parent.parent
119 onClearSelection: selected = false
120 onFlickingChanged: {
121 if (root.parent.parent.flicking) {
122 root.resetSwipe()
123 }
124 }
125 onSelectAll: selected = true
126 onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
127 }
128 }
129
130 Component.onCompleted: { // reload settings as delegates are destroyed
131 if (parent.parent.selectedItems.indexOf(index) !== -1) {
132 selected = true
133 }
134
135 selectionMode = root.parent.parent.state === "multiselectable"
136 }
137}
0138
=== added file 'app/components/WeatherListView.qml'
--- app/components/WeatherListView.qml 1970-01-01 00:00:00 +0000
+++ app/components/WeatherListView.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,32 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.4
21import Ubuntu.Components 1.2
22
23
24ListView {
25 Component.onCompleted: {
26 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
27 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
28 var scaleFactor = units.gridUnit / 8;
29 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
30 flickDeceleration = flickDeceleration * scaleFactor;
31 }
32}
033
=== added directory 'app/data'
=== added file 'app/data/CMakeLists.txt'
--- app/data/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/data/CMakeLists.txt 2015-06-28 23:49:12 +0000
@@ -0,0 +1,5 @@
1file(GLOB DATA_QML_JS_FILES *.qml *.js)
2
3add_custom_target(ubuntu-weather-app_data_QMlFiles ALL SOURCES ${DATA_QML_JS_FILES})
4
5install(FILES ${DATA_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/data)
06
=== added file 'app/data/CitiesList.js'
--- app/data/CitiesList.js 1970-01-01 00:00:00 +0000
+++ app/data/CitiesList.js 2015-06-28 23:49:12 +0000
@@ -0,0 +1,65 @@
1.pragma library
2/*
3 * Copyright (C) 2013 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Martin Borho <martin@borho.net>
18 */
19
20var preList = [
21 {"coord":{"lon":"4.88969","lat":"52.37403"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Amsterdam"},"population":741636,"adminName2":"Gemeente Amsterdam","name":"Amsterdam","country":"NL","countryName":"Netherlands","adminName1":"North Holland","adminName3":"","services":{"geonames":2759794},"areaLabel":"North Holland, Netherlands"},
22 {"coord":{"lon":"116.39723","lat":"39.9075"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":7480601,"adminName2":"","name":"Beijing","country":"CN","countryName":"China","adminName1":"Beijing","adminName3":"","services":{"geonames":1816670},"areaLabel":"Beijing, China"},
23 {"coord":{"lon":"-74.08175","lat":"4.60971"},"timezone":{"gmtOffset":-5,"dstOffset":-5,"timeZoneId":"America/Bogota"},"population":7674366,"adminName2":"","name":"Bogotá","country":"CO","countryName":"Colombia","adminName1":"Bogota D.C.","adminName3":"","services":{"geonames":3688689},"areaLabel":"Bogota D.C., Colombia"},
24 {"coord":{"lon":"-58.37723","lat":"-34.61315"},"timezone":{"gmtOffset":-3,"dstOffset":-3,"timeZoneId":"America/Argentina/Buenos_Aires"},"population":13076300,"adminName2":"","name":"Buenos Aires","country":"AR","countryName":"Argentina","adminName1":"Buenos Aires F.D.","adminName3":"","services":{"geonames":3435910},"areaLabel":"Buenos Aires F.D., Argentina"},
25 {"coord":{"lon":"31.24967","lat":"30.06263"},"timezone":{"gmtOffset":2,"dstOffset":2,"timeZoneId":"Africa/Cairo"},"population":7734614,"adminName2":"","name":"Cairo","country":"EG","countryName":"Egypt","adminName1":"Al Qāhirah","adminName3":"","services":{"geonames":360630},"areaLabel":"Al Qāhirah, Egypt"},
26 {"coord":{"lon":"-66.87919","lat":"10.48801"},"timezone":{"gmtOffset":-4.5,"dstOffset":-4.5,"timeZoneId":"America/Caracas"},"population":3000000,"adminName2":"Municipio Libertador","name":"Caracas","country":"VE","countryName":"Venezuela","adminName1":"Distrito Federal","adminName3":"","services":{"geonames":3646738},"areaLabel":"Distrito Federal, Venezuela, Bolivarian Republic of"},
27 {"coord":{"lon":"-87.65005","lat":"41.85003"},"timezone":{"gmtOffset":-6,"dstOffset":-5,"timeZoneId":"America/Chicago"},"population":2695598,"adminName2":"Cook County","name":"Chicago","country":"US","countryName":"United States","adminName1":"Illinois","adminName3":"","services":{"geonames":4887398},"areaLabel":"Illinois, United States"},
28 {"coord":{"lon":"77.22897","lat":"28.65381"},"timezone":{"gmtOffset":5.5,"dstOffset":5.5,"timeZoneId":"Asia/Kolkata"},"population":10927986,"adminName2":"","name":"Dehli","country":"IN","countryName":"India","adminName1":"NCT","adminName3":"","services":{"geonames":1273294},"areaLabel":"NCT, India"},
29 {"coord":{"lon":"55.30472","lat":"25.25817"},"timezone":{"gmtOffset":4,"dstOffset":4,"timeZoneId":"Asia/Dubai"},"population":1137347,"adminName2":"","name":"Dubai","country":"AE","countryName":"United Arab Emirates","adminName1":"Dubai","adminName3":"","services":{"geonames":292223},"areaLabel":"Dubai, United Arab Emirates"},
30 {"coord":{"lon":"113.25","lat":"23.11667"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":3152825,"adminName2":"","name":"Guangzhou","country":"CN","countryName":"China","adminName1":"Guangdong Province","adminName3":"","services":{"geonames":1809858},"areaLabel":"Guangdong Province, China"},
31 {"coord":{"lon":"10.01534","lat":"53.57532"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Berlin"},"population":1739117,"adminName2":"","name":"Hamburg","country":"DE","countryName":"Germany","adminName1":"Hamburg","adminName3":"","services":{"geonames":2911298},"areaLabel":"Hamburg, Germany"},
32 {"coord":{"lon":"-95.36327","lat":"29.76328"},"timezone":{"gmtOffset":-6,"dstOffset":-5,"timeZoneId":"America/Chicago"},"population":2099451,"adminName2":"Harris County","name":"Houston","country":"US","countryName":"United States","adminName1":"Texas","adminName3":"","services":{"geonames":4699066},"areaLabel":"Texas, United States"},
33 {"coord":{"lon":"28.94966","lat":"41.01384"},"timezone":{"gmtOffset":2,"dstOffset":3,"timeZoneId":"Europe/Istanbul"},"population":11174257,"adminName2":"","name":"Istanbul","country":"TR","countryName":"Turkey","adminName1":"Istanbul","adminName3":"","services":{"geonames":745044},"areaLabel":"Istanbul, Turkey"},
34 {"coord":{"lon":"106.84513","lat":"-6.21462"},"timezone":{"gmtOffset":7,"dstOffset":7,"timeZoneId":"Asia/Jakarta"},"population":8540121,"adminName2":"","name":"Jakarta","country":"ID","countryName":"Indonesia","adminName1":"Jakarta Raya","adminName3":"","services":{"geonames":1642911},"areaLabel":"Jakarta Raya, Indonesia"},
35 {"coord":{"lon":"28.04363","lat":"-26.20227"},"timezone":{"gmtOffset":2,"dstOffset":2,"timeZoneId":"Africa/Johannesburg"},"population":2026469,"adminName2":"City of Johannesburg Metropolitan Municipality","name":"Johannesburg","country":"ZA","countryName":"South Africa","adminName1":"Gauteng","adminName3":"City of Johannesburg","services":{"geonames":993800},"areaLabel":"Gauteng, South Africa"},
36 {"coord":{"lon":"67.0822","lat":"24.9056"},"timezone":{"gmtOffset":5,"dstOffset":5,"timeZoneId":"Asia/Karachi"},"population":11624219,"adminName2":"Karāchi District","name":"Karachi","country":"PK","countryName":"Pakistan","adminName1":"Sindh","adminName3":"","services":{"geonames":1174872},"areaLabel":"Sindh, Pakistan"},
37 {"coord":{"lon":"15.30807","lat":"-4.32142"},"timezone":{"gmtOffset":1,"dstOffset":1,"timeZoneId":"Africa/Kinshasa"},"population":7785965,"adminName2":"","name":"Kinshasa","country":"CD","countryName":"Democratic Republic of the Congo","adminName1":"Kinshasa","adminName3":"","services":{"geonames":2314302},"areaLabel":"Kinshasa, Congo, the Democratic Republic of the"},
38 {"coord":{"lon":"88.36304","lat":"22.56263"},"timezone":{"gmtOffset":5.5,"dstOffset":5.5,"timeZoneId":"Asia/Kolkata"},"population":4631392,"adminName2":"","name":"Kolkata","country":"IN","countryName":"India","adminName1":"Bengal","adminName3":"","services":{"geonames":1275004},"areaLabel":"Bengal, India"},
39 {"coord":{"lon":"3.39583","lat":"6.45306"},"timezone":{"gmtOffset":1,"dstOffset":1,"timeZoneId":"Africa/Lagos"},"population":9000000,"adminName2":"","name":"Lagos","country":"NG","countryName":"Nigeria","adminName1":"Lagos","adminName3":"","services":{"geonames":2332459},"areaLabel":"Lagos, Nigeria"},
40 {"coord":{"lon":"-77.02824","lat":"-12.04318"},"timezone":{"gmtOffset":-5,"dstOffset":-5,"timeZoneId":"America/Lima"},"population":7737002,"adminName2":"","name":"Lima","country":"PE","countryName":"Peru","adminName1":"Provincia de Lima","adminName3":"","services":{"geonames":3936456},"areaLabel":"Provincia de Lima, Peru"},
41 {"coord":{"lon":"-0.12574","lat":"51.50853"},"timezone":{"gmtOffset":0,"dstOffset":1,"timeZoneId":"Europe/London"},"population":7556900,"adminName2":"Greater London","name":"London","country":"GB","countryName":"United Kingdom","adminName1":"England","adminName3":"","services":{"geonames":2643743},"areaLabel":"England, Greater London, United Kingdom"},
42 {"coord":{"lon":"-118.24368","lat":"34.05223"},"timezone":{"gmtOffset":-8,"dstOffset":-7,"timeZoneId":"America/Los_Angeles"},"population":3792621,"adminName2":"Los Angeles County","name":"Los Angeles","country":"US","countryName":"United States","adminName1":"California","adminName3":"","services":{"geonames":5368361},"areaLabel":"California, United States"},
43 {"coord":{"lon":"-3.70256","lat":"40.4165"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Madrid"},"population":3255944,"adminName2":"Madrid","name":"Madrid","country":"ES","countryName":"Spain","adminName1":"Madrid","adminName3":"Madrid","services":{"geonames":3117735},"areaLabel":"Madrid, Spain"},
44 {"coord":{"lon":"120.9822","lat":"14.6042"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Manila"},"population":10444527,"adminName2":"PH.NCR.D9","name":"Manila","country":"PH","countryName":"Philippines","adminName1":"National Capital Region","adminName3":"","services":{"geonames":1701668},"areaLabel":"National Capital, Philippines"},
45 {"coord":{"lon":"-73.58781","lat":"45.50884"},"timezone":{"gmtOffset":-5,"dstOffset":-4,"timeZoneId":"America/Montreal"},"population":3268513,"adminName2":"Montréal","name":"Montreal","country":"CA","countryName":"Canada","adminName1":"Quebec","adminName3":"","services":{"geonames":6077243},"areaLabel":"Quebec, Canada"},
46 {"coord":{"lon":"-99.12766","lat":"19.42847"},"timezone":{"gmtOffset":-6,"dstOffset":-5,"timeZoneId":"America/Mexico_City"},"population":12294193,"adminName2":"","name":"Mexico City","country":"MX","countryName":"Mexico","adminName1":"The Federal District","adminName3":"","services":{"geonames":3530597},"areaLabel":"The Federal District, Mexico"},
47 {"coord":{"lon":"37.61556","lat":"55.75222"},"timezone":{"gmtOffset":4,"dstOffset":4,"timeZoneId":"Europe/Moscow"},"population":10381222,"adminName2":"","name":"Moscow","country":"RU","countryName":"Russia","adminName1":"Moscow","adminName3":"","services":{"geonames":524901},"areaLabel":"Moscow, Russian Federation"},
48 {"coord":{"lon":"72.88261","lat":"19.07283"},"timezone":{"gmtOffset":5.5,"dstOffset":5.5,"timeZoneId":"Asia/Kolkata"},"population":12691836,"adminName2":"Konkan Division","name":"Mumbai","country":"IN","countryName":"India","adminName1":"Mahārāshtra","adminName3":"","services":{"geonames":1275339},"areaLabel":"Mahārāshtra, India"},
49 {"coord":{"lon":"36.81667","lat":"-1.28333"},"timezone":{"gmtOffset":3,"dstOffset":3,"timeZoneId":"Africa/Nairobi"},"population":2750547,"adminName2":"","name":"Nairobi","country":"KE","countryName":"Kenya","adminName1":"Nairobi Area","adminName3":"","services":{"geonames":184745},"areaLabel":"Nairobi Area, Kenya"},
50 {"coord":{"lon":"-74.00597","lat":"40.71427"},"timezone":{"gmtOffset":-5,"dstOffset":-4,"timeZoneId":"America/New_York"},"population":8175133,"adminName2":"","name":"New York City","country":"US","countryName":"United States","adminName1":"New York","adminName3":"","services":{"geonames":5128581},"areaLabel":"New York, United States"},
51 {"coord":{"lon":"135.50218","lat":"34.69374"},"timezone":{"gmtOffset":9,"dstOffset":9,"timeZoneId":"Asia/Tokyo"},"population":2592413,"adminName2":"","name":"Osaka","country":"JP","countryName":"Japan","adminName1":"Ōsaka","adminName3":"","services":{"geonames":1853909},"areaLabel":"Ōsaka, Japan"},
52 {"coord":{"lon":"-43.2075","lat":"-22.90278"},"timezone":{"gmtOffset":-2,"dstOffset":-3,"timeZoneId":"America/Sao_Paulo"},"population":6023699,"adminName2":"Rio de Janeiro","name":"Rio de Janeiro","country":"BR","countryName":"Brazil","adminName1":"Rio de Janeiro","adminName3":"","services":{"geonames":3451190},"areaLabel":"Rio de Janeiro, Brazil"},
53 {"coord":{"lon":"12.4839","lat":"41.89474"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Rome"},"population":2563241,"adminName2":"Rome","name":"Rome","country":"IT","countryName":"Italy","adminName1":"Latium","adminName3":"Rome","services":{"geonames":3169070},"areaLabel":"Latium, Italy"},
54 {"coord":{"lon":"126.97783","lat":"37.56826"},"timezone":{"gmtOffset":9,"dstOffset":9,"timeZoneId":"Asia/Seoul"},"population":10349312,"adminName2":"","name":"Seoul","country":"KR","countryName":"South Korea","adminName1":"Seoul","adminName3":"","services":{"geonames":1835848},"areaLabel":"Seoul, Korea, Republic of"},
55 {"coord":{"lon":"-122.41942","lat":"37.77493"},"timezone":{"gmtOffset":-8,"dstOffset":-7,"timeZoneId":"America/Los_Angeles"},"population":805235,"adminName2":"San Francisco County","name":"San Francisco","country":"US","countryName":"United States","adminName1":"California","adminName3":"","services":{"geonames":5391959},"areaLabel":"California, United States"},
56 {"coord":{"lon":"-46.63611","lat":"-23.5475"},"timezone":{"gmtOffset":-2,"dstOffset":-3,"timeZoneId":"America/Sao_Paulo"},"population":10021295,"adminName2":"São Paulo","name":"São Paulo","country":"BR","countryName":"Brazil","adminName1":"São Paulo","adminName3":"","services":{"geonames":3448439},"areaLabel":"São Paulo, Brazil"},
57 {"coord":{"lon":"121.45806","lat":"31.22222"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":14608512,"adminName2":"","name":"Shanghai","country":"CN","countryName":"China","adminName1":"Shanghai Shi","adminName3":"","services":{"geonames":1796236},"areaLabel":"Shanghai Shi, China"},
58 {"coord":{"lon":"114.0683","lat":"22.54554"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":3000000,"adminName2":"Shenzhen","name":"Shenzhen","country":"CN","countryName":"China","adminName1":"Guangdong Province","adminName3":"","services":{"geonames":1795565},"areaLabel":"Guangdong Province, Shenzhen, China"},
59 {"coord":{"lon":"103.85007","lat":"1.28967"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Singapore"},"population":3547809,"adminName2":"","name":"Singapore","country":"SG","countryName":"Singapore","adminName1":"","adminName3":"","services":{"geonames":1880252},"areaLabel":"Singapore"},
60 {"coord":{"lon":"151.20732","lat":"-33.86785"},"timezone":{"gmtOffset":11,"dstOffset":10,"timeZoneId":"Australia/Sydney"},"population":4627345,"adminName2":"City of Sydney","name":"Sydney","country":"AU","countryName":"Australia","adminName1":"New South Wales","adminName3":"","services":{"geonames":2147714},"areaLabel":"New South Wales, Australia"},
61 {"coord":{"lon":"51.42151","lat":"35.69439"},"timezone":{"gmtOffset":3.5,"dstOffset":4.5,"timeZoneId":"Asia/Tehran"},"population":7153309,"adminName2":"","name":"Tehran","country":"IR","countryName":"Iran","adminName1":"Tehrān","adminName3":"","services":{"geonames":112931},"areaLabel":"Tehrān, Iran, Islamic Republic of"},
62 {"coord":{"lon":"117.17667","lat":"39.14222"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":3766207,"adminName2":"","name":"Tianjin","country":"CN","countryName":"China","adminName1":"Tianjin Shi","adminName3":"","services":{"geonames":1792947},"areaLabel":"Tianjin Shi, China"},
63 {"coord":{"lon":"139.69171","lat":"35.6895"},"timezone":{"gmtOffset":9,"dstOffset":9,"timeZoneId":"Asia/Tokyo"},"population":8336599,"adminName2":"","name":"Tokyo","country":"JP","countryName":"Japan","adminName1":"Tōkyō","adminName3":"","services":{"geonames":1850147},"areaLabel":"Tōkyō, Japan"},
64 {"coord":{"lon":"-79.4163","lat":"43.70011"},"timezone":{"gmtOffset":-5,"dstOffset":-4,"timeZoneId":"America/Toronto"},"population":4612191,"adminName2":"","name":"Toronto","country":"CA","countryName":"Canada","adminName1":"Ontario","adminName3":"","services":{"geonames":6167865},"areaLabel":"Ontario, Canada"}
65]
066
=== added file 'app/data/Storage.qml'
--- app/data/Storage.qml 1970-01-01 00:00:00 +0000
+++ app/data/Storage.qml 2015-06-28 23:49:12 +0000
@@ -0,0 +1,214 @@
1/*
2 * Copyright (C) 2013, 2014, 2015 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 version 3 as
6 * published by the Free Software Foundation.
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 * Authored by: Martin Borho <martin@borho.net>
17 */
18import QtQuick.LocalStorage 2.0
19import QtQuick 2.4
20
21Item {
22 property var db: null
23
24 function openDB() {
25 if(db !== null) return;
26
27 db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
28
29 if (db.version === "") {
30 db.changeVersion("", "0.1",
31 function(tx) {
32 tx.executeSql('CREATE TABLE IF NOT EXISTS Locations(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT, date TEXT)');
33 console.log('Database created');
34 });
35 // reopen database with new version number
36 db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
37 }
38
39 if(db.version === "0.1") {
40 db.changeVersion("0.1", "0.2",
41 function(tx) {
42 tx.executeSql('CREATE TABLE IF NOT EXISTS settings(key TEXT UNIQUE, value TEXT)');
43 console.log('Settings table added, Database upgraded to v0.2');
44 });
45 // reopen database with new version number
46 db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
47 }
48
49 if(db.version === "0.2") {
50 db.changeVersion("0.2", "0.3",
51 function(tx) {
52 tx.executeSql('DELETE FROM Locations WHERE 1');
53 console.log('Removed old locations, Database upgraded to v0.3');
54 });
55 }
56
57 if (!settings.migrated) {
58 try { // attempt to read the old settings
59 var oldSettings = {};
60
61 // Load old settings
62 db.readTransaction( function(tx) {
63 var rs = tx.executeSql("SELECT * FROM settings")
64
65 for(var i = 0; i < rs.rows.length; i++) {
66 var row = rs.rows.item(i);
67 oldSettings[row.key] = row.value;
68 }
69 });
70
71 console.debug("Migrating old data:", JSON.stringify(oldSettings))
72
73 var metric = Qt.locale().measurementSystem === Locale.MetricSystem
74
75 // Move to new Settings API
76 settings.migrated = true
77 settings.service = oldSettings["service"] === undefined ? "weatherchannel" : oldSettings["service"]
78
79 if (oldSettings["precip_units"] !== undefined) {
80 settings.precipUnits = oldSettings["precip_units"]
81 } else {
82 settings.precipUnits = metric ? "mm" : "in"
83 }
84
85 if (oldSettings["units"] !== undefined) {
86 settings.tempScale = oldSettings["units"] === "metric" ? "°C" : "°F"
87 settings.units = oldSettings["units"]
88 } else {
89 settings.tempScale = metric ? "°C" : "°F"
90 settings.units = metric ? "metric" : "imperial"
91 }
92
93 if (oldSettings["units"] !== undefined) {
94 // If old wind speed was in "kmh" use "kph" instead
95 settings.windUnits = oldSettings["wind_units"] === "kmh" ? "kph" : oldSettings["wind_units"]
96 } else {
97 settings.windUnits = metric ? "kph" : "mph"
98 }
99
100 /*
101 TODO: uncomment when reboot is ready to replace existing app
102 db.transaction( function(tx) {
103 tx.executeSql("DROP TABLE IF EXISTS settings")
104 });
105 */
106 } catch (e) { // likely table did not exist
107 console.debug("No old data to migrate.")
108 settings.migrated = true
109 }
110 }
111 }
112
113 function insertLocation(data) {
114 openDB();
115 var res;
116
117 db.transaction( function(tx){
118 var r = tx.executeSql('INSERT INTO Locations(data, date) VALUES(?, ?)', [JSON.stringify(data), new Date().getTime()]);
119 res = r.insertId;
120 });
121 return res;
122 }
123
124 function insertLocationAtStart(data) {
125 var res = insertLocation(data);
126 reorder(res, 0);
127 return 0;
128 }
129
130 function updateLocation(dbId, data) {
131 openDB();
132 db.transaction( function(tx){
133 var r = tx.executeSql('UPDATE Locations SET data = ?, date=? WHERE id = ?', [JSON.stringify(data), new Date().getTime(), dbId])
134 });
135 }
136
137 function getLocations(callback) {
138 openDB();
139 db.readTransaction(
140 function(tx){
141 var locations = [];
142 var rs = tx.executeSql('SELECT * FROM Locations');
143 for(var i = 0; i < rs.rows.length; i++) {
144 var row = rs.rows.item(i),
145 locData = JSON.parse(row.data);
146 locData["updated"] = parseInt(row.date, 10);
147 locData["db"] = {id: row.id, updated: new Date(parseInt(row.date, 10))};
148 locations.push(locData);
149 }
150 callback(locations);
151 }
152 );
153 }
154
155 function clearLocation(location_id) {
156 openDB();
157 db.transaction(function(tx){
158 tx.executeSql('DELETE FROM Locations WHERE id = ?', [location_id]);
159 });
160 }
161
162 function clearMultiLocation(locations) {
163 openDB();
164
165 db.transaction(function (tx) {
166 // Remove all the deleted indexes
167 for (var i=0; i < locations.length; i++) {
168 tx.executeSql('DELETE FROM Locations WHERE id=?;', [locations[i]])
169 }
170
171 // Rebuild locations in order
172 var rs = tx.executeSql('SELECT id FROM Locations ORDER BY id ASC')
173
174 for (i=0; i < rs.rows.length; i++) {
175 tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
176 [i, rs.rows.item(i).id])
177 }
178 })
179 }
180
181 function clearDB() { // for dev purposes
182 openDB();
183 db.transaction(function(tx){
184 tx.executeSql('DELETE FROM Locations WHERE 1');
185 });
186 }
187
188 function reorder(from, to) {
189 openDB();
190
191 db.transaction(function(tx) {
192 // Track to move put as -1 for now
193 tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
194 [-1, from])
195
196 // Shuffle locations inbetween from->to
197 if (from > to) {
198 for (var i = from-1; i >= to; i--) {
199 tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
200 [i+1, i])
201 }
202 } else {
203 for (var j = from+1; j <= to; j++) {
204 tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
205 [j-1, j])
206 }
207 }
208
209 // Switch moving location to its new position
210 tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
211 [to, -1])
212 })
213 }
214}
0215
=== added file 'app/data/WeatherApi.js'
--- app/data/WeatherApi.js 1970-01-01 00:00:00 +0000
+++ app/data/WeatherApi.js 2015-06-28 23:49:12 +0000
@@ -0,0 +1,754 @@
1.pragma library
2/*
3 * Copyright (C) 2013 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Raúl Yeguas <neokore@gmail.com>
18 * Martin Borho <martin@borho.net>
19 * Andrew Starr-Bochicchio <a.starr.b@gmail.com>
20 */
21
22/**
23* Version of the response data format.
24* Increase this number to force a refresh.
25*/
26var RESPONSE_DATA_VERSION = 20150404;
27
28/**
29* Helper functions
30*/
31function debug(obj) {
32 print(JSON.stringify(obj))
33}
34//
35function calcFahrenheit(celsius) {
36 return celsius * 1.8 + 32;
37}
38//
39function calcMph(ms) {
40 return ms*2.24;
41}
42//
43function calcInch(mm) {
44 return mm/25.4;
45}
46//
47function calcKph(ms) {
48 return ms*3.6;
49}
50//
51function convertKphToMph(kph) {
52 return kph*0.621;
53}
54//
55function calcWindDir(degrees) {
56 var direction = "?";
57 if(degrees >=0 && degrees <= 30){
58 direction = "N";
59 } else if(degrees >30 && degrees <= 60){
60 direction = "NE";
61 } else if(degrees >60 && degrees <= 120){
62 direction = "E";
63 } else if(degrees >120 && degrees <= 150){
64 direction = "SE";
65 } else if(degrees >150 && degrees <= 210){
66 direction = "S";
67 } else if(degrees >210 && degrees <= 240){
68 direction = "SW";
69 } else if(degrees >240 && degrees <= 300){
70 direction = "W";
71 } else if(degrees >300 && degrees <= 330){
72 direction = "NW";
73 } else if(degrees >330 && degrees <= 360){
74 direction = "N";
75 }
76 return direction;
77}
78
79//
80function getLocationTime(tstamp) {
81 var locTime = new Date(tstamp);
82 return {
83 year: locTime.getUTCFullYear(),
84 month: locTime.getUTCMonth(),
85 date: locTime.getUTCDate(),
86 hours: locTime.getUTCHours(),
87 minutes: locTime.getUTCMinutes()
88 }
89}
90// Serialize a JavaScript object to URL parameters
91// E.g. {param1: value1, param2: value2} to "param1=value&param2=value"
92// TODO: it'd be nice to make it work with either passing a single object
93// or several at once
94function parameterize(obj) {
95 var str = [];
96 for(var param in obj) {
97 str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param]));
98 }
99 return str.join("&");
100}
101
102var GeoipApi = (function() {
103 var _baseUrl = "http://geoip.ubuntu.com/lookup";
104 return {
105 getLatLong: function(params, apiCaller, onSuccess, onError) {
106 var request = { type: "geolookup",url: _baseUrl},
107 resultHandler = (function(request, xmlDoc) {
108 var coords = {},
109 childNodes = xmlDoc.childNodes;
110 for(var i=0;i<childNodes.length;i++) {
111 if(childNodes[i].nodeName === "Latitude") {
112 coords.lat = childNodes[i].firstChild.nodeValue;
113 } else if(childNodes[i].nodeName === "Longitude") {
114 coords.lon = childNodes[i].firstChild.nodeValue;
115 }
116 }
117 onSuccess(coords);
118 }),
119 retryHandler = (function(err) {
120 console.log("geolookup retry of "+err.request.url);
121 apiCaller(request, resultHandler, onError);
122 });
123 apiCaller(request, resultHandler, retryHandler);
124 }
125 }
126})();
127
128var GeonamesApi = (function() {
129 /**
130 provides neccessary methods for requesting and preparing data from Geonames.org
131 */
132 var _baseUrl = "http://api.geonames.org/";
133 var _username = "uweatherdev"
134 var _addParams = "&maxRows=25&featureClass=P"
135 //
136 function _buildSearchResult(request, data) {
137 var searchResult = { locations: [], request: request };
138 if(data.geonames) {
139 data.geonames.forEach(function(r) {
140 searchResult.locations.push({
141 name: r.name,
142 coord: {lat: r.lat, lon: r.lng},
143 country: r.countryCode,
144 countryName: r.countryName,
145 timezone: r.timezone,
146 adminName1: r.adminName1,
147 adminName2: r.adminName2,
148 adminName3: r.adminName3,
149 population: r.population,
150 services: {
151 "geonames": r.geonameId
152 }
153 });
154 })
155 }
156 return searchResult;
157 }
158 //
159 return {
160 //
161 search: function(mode, params, apiCaller, onSuccess, onError) {
162 var request,
163 retryHandler = (function(err) {
164 console.log("search retry of "+err.request.url);
165 apiCaller(request, searchResponseHandler, onError);
166 }),
167 searchResponseHandler = function(request, data) {
168 onSuccess(_buildSearchResult(request, data));
169 };
170 if(mode === "point") {
171 request = { type: "search",
172 url: _baseUrl+ "findNearbyPlaceNameJSON?style=full&username="+encodeURIComponent(_username)
173 +"&lat="+encodeURIComponent(params.coords.lat)+"&lng="+encodeURIComponent(params.coords.lon)
174 +_addParams}
175 } else {
176 request = { type: "search",
177 url: _baseUrl+ "searchJSON?style=full&username="+encodeURIComponent(_username)
178 +"&name_startsWith="+encodeURIComponent(params.name)+_addParams}
179 }
180 apiCaller(request, searchResponseHandler, retryHandler);
181 }
182 }
183
184})();
185
186var OpenWeatherMapApi = (function() {
187 /**
188 provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
189 */
190 var _baseUrl = "http://api.openweathermap.org/data/2.5/";
191 //
192 var _serviceName = "openweathermap";
193 //
194 var _icon_map = {
195 "01d": "sun",
196 "01n": "moon",
197 "02d": "cloud_sun",
198 "02n": "cloud_moon",
199 "03d": "cloud_sun",
200 "03n": "cloud_moon",
201 "04d": "cloud",
202 "04n": "cloud",
203 "09d": "rain",
204 "09n": "rain",
205 "10d": "rain",
206 "10n": "rain",
207 "11d": "thunder",
208 "11n": "thunder",
209 "13d": "snow_shower",
210 "13n": "snow_shower",
211 "50d": "fog",
212 "50n": "fog"
213 }
214 //
215 function _buildDataPoint(date, data) {
216 var result = {
217 timestamp: data.dt,
218 date: date,
219 metric: {
220 temp:data.main.temp,
221 windSpeed: calcKph(data.wind.speed),
222 rain: data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0,
223 snow: data.main.snow || ((data.snow) ? data.snow["3h"] : false ) || 0
224 },
225 imperial: {
226 temp: calcFahrenheit(data.main.temp),
227 windSpeed: calcMph(data.wind.speed),
228 rain: calcInch(data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0),
229 snow: calcInch(data.main.snow || ((data.snow) ? data.snow["3h"] : false ) ||0)
230 },
231 humidity: data.main.humidity,
232 pressure: data.main.pressure,
233 windDeg: data.wind.deg,
234 windDir: calcWindDir(data.wind.deg),
235 icon: _icon_map[data.weather[0].icon],
236 condition: data.weather[0].main
237 };
238 if(data.id !== undefined) {
239 result["service"] = _serviceName;
240 result["service_id"] = data.id;
241 }
242 return result;
243 }
244 //
245 function _buildDayFormat(date, data) {
246 var result = {
247 date: date,
248 timestamp: data.dt,
249 metric: {
250 tempMin: data.temp.min,
251 tempMax: data.temp.max,
252 windSpeed: calcKph(data.speed),
253 rain: data.rain || 0,
254 snow: data.snow || 0
255 },
256 imperial: {
257 tempMin: calcFahrenheit(data.temp.min),
258 tempMax: calcFahrenheit(data.temp.max),
259 windSpeed: calcMph(data.speed),
260 rain: calcInch(data.rain || 0),
261 snow: calcInch(data.snow || 0)
262 },
263 pressure: data.pressure,
264 humidity: data.humidity,
265 icon: _icon_map[data.weather[0].icon],
266 condition: data.weather[0].main,
267 windDeg: data.deg,
268 windDir: calcWindDir(data.deg),
269 hourly: []
270 }
271 return result;
272 }
273 //
274 function formatResult(data, location) {
275 var tmpResult = {},
276 result = [],
277 day=null,
278 offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0,
279 localNow = getLocationTime(new Date().getTime()+offset),
280 todayDate;
281 print("["+location.name+"] "+JSON.stringify(localNow))
282 // add openweathermap id for faster responses
283 if(location.services && !location.services[_serviceName] && data["current"].id) {
284 location.services[_serviceName] = data["current"].id
285 }
286 //
287 data["daily"]["list"].forEach(function(dayData) {
288 var date = getLocationTime(((dayData.dt*1000)-1000)+offset), // minus 1 sec to handle +/-12 TZ
289 day = date.year+"-"+date.month+"-"+date.date;
290 if(!todayDate) {
291 if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
292 // skip "yesterday"
293 return;
294 }
295 todayDate = date;
296 }
297 tmpResult[day] = _buildDayFormat(date, dayData);
298 })
299 //
300 var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date
301 tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]);
302 if(data["forecast"] !== undefined) {
303 data["forecast"]["list"].forEach(function(hourData) {
304 var dateData = getLocationTime((hourData.dt*1000)+offset),
305 day = dateData.year+"-"+dateData.month+"-"+dateData.date;
306 if(tmpResult[day]) {
307 tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
308 }
309 })
310 }
311 //
312 for(var d in tmpResult) {
313 result.push(tmpResult[d]);
314 }
315 return result;
316 }
317 //
318 function _getUrls(params) {
319 var urls = {
320 current: "",
321 daily: "",
322 forecast: ""
323 },
324 latLongParams = "&lat="+encodeURIComponent(params.location.coord.lat)
325 + "&lon="+encodeURIComponent(params.location.coord.lon);
326 if(params.location.services && params.location.services[_serviceName]) {
327 urls.current = _baseUrl + "weather?units="+params.units+"&id="+params.location.services[_serviceName];
328 urls.daily = _baseUrl + "forecast/daily?id="+params.location.services[_serviceName]+"&cnt=10&units="+params.units
329 urls.forecast = _baseUrl + "forecast?id="+params.location.services[_serviceName]+"&units="+params.units
330
331 } else if (params.location.coord) {
332 urls.current = _baseUrl + "weather?units="+params.units+latLongParams;
333 urls.daily = _baseUrl+"forecast/daily?cnt=10&units="+params.units+latLongParams;
334 urls.forecast = _baseUrl+"forecast?units="+params.units+latLongParams;
335 }
336 return urls;
337 }
338 //
339 return {
340 //
341 getData: function(params, apiCaller, onSuccess, onError) {
342 var urls = _getUrls(params),
343 handlerMap = {
344 current: { type: "current",url: urls.current},
345 daily: { type: "daily",url: urls.daily},
346 forecast: { type: "forecast", url: urls.forecast}},
347 response = {
348 location: params.location,
349 db: (params.db) ? params.db : null,
350 format: RESPONSE_DATA_VERSION
351 },
352 respData = {},
353 addDataToResponse = (function(request, data) {
354 var formattedResult;
355 respData[request.type] = data;
356 if(respData["current"] !== undefined
357 && respData["forecast"] !== undefined
358 && respData["daily"] !== undefined) {
359 response["data"] = formatResult(respData, params.location)
360 onSuccess(response);
361 }
362 }),
363 onErrorHandler = (function(err) {
364 onError(err);
365 }),
366 retryHandler = (function(err) {
367 console.log("retry of "+err.request.url);
368 var retryFunc = handlerMap[err.request.type];
369 apiCaller(retryFunc, addDataToResponse, onErrorHandler);
370 });
371 //
372 apiCaller(handlerMap.current, addDataToResponse, retryHandler);
373 apiCaller(handlerMap.forecast, addDataToResponse, retryHandler);
374 apiCaller(handlerMap.daily, addDataToResponse, retryHandler);
375 }
376 }
377
378})();
379
380var WeatherChannelApi = (function() {
381 /**
382 provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
383 */
384 var _baseUrl = "http://wxdata.weather.com/wxdata/";
385 //
386 var _serviceName = "weatherchannel";
387 //
388 // see http://s.imwx.com/v.20131006.223722/img/wxicon/72/([0-9]+).png
389 var _iconMap = {
390 "0": "thunder", // ??
391 "1": "thunder", // ??
392 "2": "thunder", // ??
393 "3": "thunder", // ??
394 "4": "thunder", //T-Storms
395 "5": "snow_rain", //Rain / Snow
396 "6": "snow_rain", // ??
397 "7": "snow_rain", //Wintry Mix
398 "8": "scattered", //Freezing Drizzle
399 "9": "scattered", //Drizzle
400 "10": "rain", // ??
401 "11": "rain", //Showers
402 "12": "rain", //Rain
403 "13": "snow_shower", // ??
404 "14": "snow_shower", //Snow shower/Light snow
405 "15": "snow_shower", //
406 "16": "snow_shower", //Snow
407 "17": "thunder", // Hail??
408 "18": "snow_rain", // Rain / Snow ??
409 "19": "fog", //Fog ??
410 "20": "fog", //Fog
411 "21": "fog", //Haze
412 "22": "fog", // ??
413 "23": "fog", // Wind ??
414 "24": "overcast", //Partly Cloudy / Wind
415 "25": "overcast", // ??
416 "26": "overcast",//Cloudy
417 "27": "cloud_moon",//Mostly Cloudy
418 "28": "cloud_sun", //Mostly Cloudy
419 "29": "cloud_moon", //Partly Cloudy
420 "30": "cloud_sun", //Partly Cloudy
421 "31": "moon", //Clear
422 "32": "sun", //Sunny
423 "33": "cloud_moon", //Mostly Clear
424 "34": "cloud_sun", //Mostly Sunny
425 "35": "snow_rain", // ??
426 "36": "sun", //Sunny
427 "37": "thunder", //Isolated T-Storms
428 "38": "thunder", //Scattered T-Storms
429 "39": "scattered", //Scattered Showers
430 "40": "rain", // ??
431 "41": "snow", //Scattered Snow Showers
432 "42": "snow_shower", // ??
433 "43": "snow_shower", // ??
434 "44": "fog", // ??
435 "45": "scattered", // ??
436 "46": "snow_shower", //Snow Showers Early
437 "47": "thunder" //Isolated T-Storms
438 };
439 //
440 function _buildDataPoint(date, dataObj) {
441 var data = dataObj["Observation"] || dataObj,
442 result = {
443 timestamp: data.date || data.dateTime,
444 date: date,
445 metric: {
446 temp: data.temp,
447 tempFeels: data.feelsLike,
448 windSpeed: data.wSpeed
449 },
450 imperial: {
451 temp: calcFahrenheit(data.temp),
452 tempFeels: calcFahrenheit(data.feelsLike),
453 windSpeed: convertKphToMph(data.wSpeed)
454 },
455 precipType: (data.precip_type !== undefined) ? data.precip_type : null,
456 propPrecip: (data.pop !== undefined) ? data.pop : null,
457 humidity: data.humid,
458 pressure: data.pressure,
459 windDeg: data.wDir,
460 windDir: data.wDirText,
461 icon: _iconMap[(data.wxIcon||data.icon)],
462 condition: data.text || data.wDesc,
463 uv: data.uv
464 };
465 if(_iconMap[data.wxIcon||data.icon] === undefined) {
466 print("ICON MISSING POINT: "+(data.wxIcon||data.icon)+" "+result.condition)
467 }
468 return result;
469 }
470 //
471 function _buildDayFormat(date, data, now) {
472 var partData = (now > data.validDate || data.day === undefined) ? data.night : data.day,
473 result = {
474 date: date,
475 timestamp: data.validDate,
476 metric: {
477 tempMin: data.minTemp,
478 tempMax: data.maxTemp,
479 windSpeed: partData.wSpeed
480 },
481 imperial: {
482 tempMin: calcFahrenheit(data.minTemp),
483 tempMax: calcFahrenheit(data.maxTemp !== undefined ? data.maxTemp : data.minTemp),
484 windSpeed: convertKphToMph(partData.wSpeed)
485 },
486 precipType: partData.precip_type,
487 propPrecip: partData.pop,
488 pressure: null,
489 humidity: partData.humid,
490 icon: _iconMap[partData.icon],
491 condition: partData.phrase,
492 windDeg: partData.wDir,
493 windDir: partData.wDirText,
494 uv: partData.uv,
495 hourly: []
496 }
497 if(_iconMap[partData.icon] === undefined) {
498 print("ICON MISSING DAY: "+partData.icon+" "+result.condition)
499 }
500 return result;
501 }
502 //
503 function formatResult(combinedData, location) {
504 var tmpResult = {}, result = [],
505 day=null, todayDate,
506 offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0,
507 now = new Date().getTime(),
508 nowMs = parseInt(now/1000),
509 localNow = getLocationTime(now+offset),
510 data = {
511 "location": combinedData[0]["Location"],
512 "daily": combinedData[0]["DailyForecasts"],
513 "forecast": combinedData[0]["HourlyForecasts"],
514 "current": combinedData[0]["StandardObservation"],
515 };
516 print("["+location.name+"] "+JSON.stringify(localNow));
517 // add openweathermap id for faster responses
518 if(location.services && !location.services[_serviceName] && data["location"].key) {
519 location.services[_serviceName] = data["location"].key
520 }
521 // only 5 days of forecast for TWC
522 for(var x=0;x<5;x++) {
523 var dayData = data["daily"][x],
524 date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ
525 day = date.year+"-"+date.month+"-"+date.date;
526 if(!todayDate) {
527 if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
528 // skip "yesterday"
529 continue;
530 }
531 todayDate = date;
532 }
533 tmpResult[day] = _buildDayFormat(date, dayData, nowMs);
534 }
535 //
536 if(data["forecast"] !== undefined) {
537 data["forecast"].forEach(function(hourData) {
538 var dateData = getLocationTime((hourData.dateTime*1000)+offset),
539 day = dateData.year+"-"+dateData.month+"-"+dateData.date;
540 if(tmpResult[day]) {
541 tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
542 }
543 })
544 }
545 //
546 if(data["current"]) {
547 var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date;
548 tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]);
549 // if the icon is missing, use the first from the hourly forecast
550 if(!tmpResult[today]["current"].icon && tmpResult[today]["hourly"] && tmpResult[today]["hourly"].length > 0) {
551 tmpResult[today]["current"].icon = tmpResult[today]["hourly"][0].icon;
552 }
553 // if condition text is missing, use the condition from the first hourly forecast
554 if((tmpResult[today]["current"].condition === "-" || tmpResult[today]["current"].condition === undefined)
555 && (tmpResult[today]["hourly"] && tmpResult[today]["hourly"].length > 0)) {
556 tmpResult[today]["current"].condition = tmpResult[today]["hourly"][0].condition;
557 }
558 }
559 //
560 for(var d in tmpResult) {
561 result.push(tmpResult[d]);
562 }
563 return result;
564 }
565 //
566 function _getUrl(params) {
567 var url, serviceId,
568 baseParams = {
569 key: params.api_key,
570 units: (params.units === "metric") ? "m" : "e",
571 locale: Qt.locale().name,
572 hours: "48",
573 },
574 commands = {
575 "mobileaggregation": "mobile/mobagg/",
576 };
577 if(params.location.services && params.location.services[_serviceName]) {
578 serviceId = encodeURIComponent(params.location.services[_serviceName]);
579 url = _baseUrl+commands["mobileaggregation"]+serviceId+".js?"+parameterize(baseParams);
580 } else if (params.location.coord) {
581 var coord = {lat: params.location.coord.lat, lng: params.location.coord.lon};
582 url = _baseUrl+commands["mobileaggregation"]+"get.js?"+parameterize(baseParams)+"&"+
583 parameterize(coord);
584 }
585 return url;
586 }
587 //
588 return {
589 getData: function(params, apiCaller, onSuccess, onError) {
590 var url = _getUrl(params),
591 handlerMap = {
592 all: { type: "all", url: url}
593 },
594 response = {
595 location: params.location,
596 db: (params.db) ? params.db : null,
597 format: RESPONSE_DATA_VERSION
598 },
599 addDataToResponse = (function(request, data) {
600 var formattedResult;
601 response["data"] = formatResult(data, params.location);
602 onSuccess(response);
603 }),
604 onErrorHandler = (function(err) {
605 onError(err);
606 });
607 apiCaller(handlerMap.all, addDataToResponse, onErrorHandler);
608 }
609 }
610})();
611
612var WeatherApi = (function(_services) {
613 /**
614 proxy for requesting weather apis, the passed _services are providing the respective api endpoints
615 and formatters to build a uniform response object
616 */
617 function _getService(name) {
618 if(_services[name] !== undefined) {
619 return _services[name];
620 }
621 return _services["weatherchannel"];
622 }
623 //
624 function _sendRequest(request, onSuccess, onError) {
625 var xmlHttp = new XMLHttpRequest();
626 if (xmlHttp) {
627 console.log("Sent request URL: " + request.url);
628 xmlHttp.open('GET', request.url, true);
629 xmlHttp.onreadystatechange = function () {
630 try {
631 if (xmlHttp.readyState == 4) {
632 if(xmlHttp.status === 200) {
633 if(xmlHttp.responseXML) {
634 onSuccess(request, xmlHttp.responseXML.documentElement);
635 } else {
636 var json = JSON.parse(xmlHttp.responseText);
637 onSuccess(request,json);
638 }
639 } else {
640 onError({
641 msg: "wrong response http code, got "+xmlHttp.status,
642 request: request
643 });
644 }
645 }
646 } catch (e) {
647 print("Exception: "+e)
648 onError({msg: "wrong response data format", request: request});
649 }
650 };
651 xmlHttp.send(null);
652 }
653 }
654 //
655 return {
656 //
657 geoLookup: function(params, onSuccess, onError) {
658 var service = _getService('geoip'),
659 geoNameService = _getService('geonames'),
660 lookupHandler = function(data) {
661 print("Geolookup: "+JSON.stringify(data))
662 geoNameService.search("point", {coords:data}, _sendRequest, onSuccess, onError);
663 };
664 service.getLatLong(params, _sendRequest, lookupHandler, onError)
665 },
666 //
667 search: function(mode, params, onSuccess, onError) {
668 var service = _getService('geonames');
669 service.search(mode, params, _sendRequest, onSuccess, onError);
670 },
671 //
672 getLocationData: function(params, onSuccess, onError) {
673 var service = _getService(params.service);
674 service.getData(params, _sendRequest, onSuccess, onError);
675 },
676 }
677})({
678 "openweathermap": OpenWeatherMapApi,
679 "weatherchannel": WeatherChannelApi,
680 "geonames": GeonamesApi,
681 "geoip": GeoipApi
682});
683
684var sendRequest = function(message, responseCallback) {
685 // handles the response data
686 var finished = function(result) {
687 // print result to get data for test json files
688 // print(JSON.stringify(result));
689 //WorkerScript.sendMessage({
690 responseCallback({
691 action: message.action,
692 result: result
693 })
694 }
695 // handles errors
696 var onError = function(err) {
697 console.log(JSON.stringify(err, null, true));
698 //WorkerScript.sendMessage({ 'error': err})
699 responseCallback({ 'error': err})
700 }
701 // keep order of locations, sort results
702 var sortDataResults = function(locA, locB) {
703 return locA.db.id - locB.db.id;
704 }
705 // perform the api calls
706 if(message.action === "searchByName") {
707 WeatherApi.search("name", message.params, finished, onError);
708 } else if(message.action === "searchByPoint") {
709 WeatherApi.search("point", message.params, finished, onError);
710 } else if(message.action === "getGeoIp") {
711 WeatherApi.geoLookup(message.params, finished, onError);
712 } else if(message.action === "updateData") {
713 var locLength = message.params.locations.length,
714 locUpdated = 0,
715 result = [],
716 now = new Date().getTime();
717 if(locLength > 0) {
718 message.params.locations.forEach(function(loc) {
719 var updatedHnd = function (newData, cached) {
720 locUpdated += 1;
721 if(cached === true) {
722 newData["save"] = false;
723 } else {
724 newData["save"] = true;
725 newData["updated"] = new Date().getTime();
726 }
727 result.push(newData);
728 if(locUpdated === locLength) {
729 result.sort(sortDataResults);
730 finished(result);
731 }
732 },
733 params = {
734 location:loc.location,
735 db: loc.db,
736 units: 'metric',
737 service: message.params.service,
738 api_key: message.params.api_key,
739 interval: message.params.interval
740 },
741 secsFromLastFetch = (now-loc.updated)/1000;
742 if( message.params.force===true || loc.format !== RESPONSE_DATA_VERSION || secsFromLastFetch > params.interval){
743 // data older than 30min, location is new or data format is deprecated
744 WeatherApi.getLocationData(params, updatedHnd, onError);
745 } else {
746 console.log("["+loc.location.name+"] returning cached data, time from last fetch: "+secsFromLastFetch)
747 updatedHnd(loc, true);
748 }
749 })
750 } else {
751 finished(result);
752 }
753 }
754}
0755
=== added file 'app/data/key.js'
--- app/data/key.js 1970-01-01 00:00:00 +0000
+++ app/data/key.js 2015-06-28 23:49:12 +0000
@@ -0,0 +1,1 @@
1var twcKey = "";
02
=== added directory 'app/graphics'
=== added file 'app/graphics/Big-Rain.png'
1Binary files app/graphics/Big-Rain.png 1970-01-01 00:00:00 +0000 and app/graphics/Big-Rain.png 2015-06-28 23:49:12 +0000 differ3Binary files app/graphics/Big-Rain.png 1970-01-01 00:00:00 +0000 and app/graphics/Big-Rain.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/CMakeLists.txt'
--- app/graphics/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/graphics/CMakeLists.txt 2015-06-28 23:49:12 +0000
@@ -0,0 +1,5 @@
1file(GLOB IMAGE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.png *.svg)
2
3add_custom_target(ubuntu-weather-app_graphics_IMAGEFiles ALL SOURCES ${IMAGE_FILES})
4
5install(FILES ${IMAGE_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/graphics)
06
=== added file 'app/graphics/Cloudy-Circles.png'
1Binary files app/graphics/Cloudy-Circles.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Circles.png 2015-06-28 23:49:12 +0000 differ7Binary files app/graphics/Cloudy-Circles.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Circles.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Cloudy-Night.png'
2Binary files app/graphics/Cloudy-Night.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Night.png 2015-06-28 23:49:12 +0000 differ8Binary files app/graphics/Cloudy-Night.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Night.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Cloudy-Snow.png'
3Binary files app/graphics/Cloudy-Snow.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Snow.png 2015-06-28 23:49:12 +0000 differ9Binary files app/graphics/Cloudy-Snow.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Snow.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Cloudy.png'
4Binary files app/graphics/Cloudy.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy.png 2015-06-28 23:49:12 +0000 differ10Binary files app/graphics/Cloudy.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Fog.png'
5Binary files app/graphics/Fog.png 1970-01-01 00:00:00 +0000 and app/graphics/Fog.png 2015-06-28 23:49:12 +0000 differ11Binary files app/graphics/Fog.png 1970-01-01 00:00:00 +0000 and app/graphics/Fog.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Raindrop.png'
6Binary files app/graphics/Raindrop.png 1970-01-01 00:00:00 +0000 and app/graphics/Raindrop.png 2015-06-28 23:49:12 +0000 differ12Binary files app/graphics/Raindrop.png 1970-01-01 00:00:00 +0000 and app/graphics/Raindrop.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Showers.png'
7Binary files app/graphics/Showers.png 1970-01-01 00:00:00 +0000 and app/graphics/Showers.png 2015-06-28 23:49:12 +0000 differ13Binary files app/graphics/Showers.png 1970-01-01 00:00:00 +0000 and app/graphics/Showers.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Starry-Night.png'
8Binary files app/graphics/Starry-Night.png 1970-01-01 00:00:00 +0000 and app/graphics/Starry-Night.png 2015-06-28 23:49:12 +0000 differ14Binary files app/graphics/Starry-Night.png 1970-01-01 00:00:00 +0000 and app/graphics/Starry-Night.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Stormy.png'
9Binary files app/graphics/Stormy.png 1970-01-01 00:00:00 +0000 and app/graphics/Stormy.png 2015-06-28 23:49:12 +0000 differ15Binary files app/graphics/Stormy.png 1970-01-01 00:00:00 +0000 and app/graphics/Stormy.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Sunny.png'
10Binary files app/graphics/Sunny.png 1970-01-01 00:00:00 +0000 and app/graphics/Sunny.png 2015-06-28 23:49:12 +0000 differ16Binary files app/graphics/Sunny.png 1970-01-01 00:00:00 +0000 and app/graphics/Sunny.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/Windy-n-Snow.png'
11Binary files app/graphics/Windy-n-Snow.png 1970-01-01 00:00:00 +0000 and app/graphics/Windy-n-Snow.png 2015-06-28 23:49:12 +0000 differ17Binary files app/graphics/Windy-n-Snow.png 1970-01-01 00:00:00 +0000 and app/graphics/Windy-n-Snow.png 2015-06-28 23:49:12 +0000 differ
=== added file 'app/graphics/extended-information_chance-of-rain.svg'
--- app/graphics/extended-information_chance-of-rain.svg 1970-01-01 00:00:00 +0000
+++ app/graphics/extended-information_chance-of-rain.svg 2015-06-28 23:49:12 +0000
@@ -0,0 +1,153 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="90"
13 height="90"
14 id="svg6138"
15 version="1.1"
16 inkscape:version="0.91pre2 r"
17 viewBox="0 0 90 90.000001"
18 sodipodi:docname="weather-chance-of-rain02.svg">
19 <defs
20 id="defs6140" />
21 <sodipodi:namedview
22 id="base"
23 pagecolor="#ffffff"
24 bordercolor="#666666"
25 borderopacity="1.0"
26 inkscape:pageopacity="0.0"
27 inkscape:pageshadow="2"
28 inkscape:zoom="5.0931702"
29 inkscape:cx="112.1011"
30 inkscape:cy="19.378885"
31 inkscape:document-units="px"
32 inkscape:current-layer="g6442"
33 showgrid="true"
34 fit-margin-top="0"
35 fit-margin-left="0"
36 fit-margin-right="0"
37 fit-margin-bottom="0"
38 inkscape:snap-global="true"
39 inkscape:snap-bbox="true"
40 inkscape:bbox-paths="true"
41 inkscape:snap-bbox-midpoints="true"
42 inkscape:snap-bbox-edge-midpoints="true"
43 inkscape:bbox-nodes="true"
44 inkscape:object-paths="true"
45 inkscape:snap-intersection-paths="true"
46 inkscape:object-nodes="true"
47 inkscape:snap-smooth-nodes="true"
48 inkscape:snap-midpoints="true"
49 inkscape:snap-others="true"
50 inkscape:snap-object-midpoints="true"
51 inkscape:snap-center="true"
52 showguides="false"
53 inkscape:guide-bbox="true">
54 <inkscape:grid
55 type="xygrid"
56 id="grid6700"
57 empspacing="6" />
58 <sodipodi:guide
59 orientation="0,1"
60 position="90,87"
61 id="guide4064" />
62 <sodipodi:guide
63 orientation="0,1"
64 position="89,84"
65 id="guide4066" />
66 <sodipodi:guide
67 orientation="1,0"
68 position="3,69"
69 id="guide4068" />
70 <sodipodi:guide
71 orientation="1,0"
72 position="6,60"
73 id="guide4070" />
74 <sodipodi:guide
75 orientation="1,0"
76 position="87,58"
77 id="guide4072" />
78 <sodipodi:guide
79 orientation="1,0"
80 position="84,47"
81 id="guide4074" />
82 <sodipodi:guide
83 orientation="0,1"
84 position="77,3"
85 id="guide4076" />
86 <sodipodi:guide
87 orientation="0,1"
88 position="81,6"
89 id="guide4078" />
90 <sodipodi:guide
91 orientation="1,0"
92 position="81,25"
93 id="guide4080" />
94 <sodipodi:guide
95 orientation="0,1"
96 position="78,9"
97 id="guide4082" />
98 <sodipodi:guide
99 orientation="0,1"
100 position="79,81"
101 id="guide4084" />
102 <sodipodi:guide
103 orientation="1,0"
104 position="9,39"
105 id="guide4086" />
106 </sodipodi:namedview>
107 <metadata
108 id="metadata6143">
109 <rdf:RDF>
110 <cc:Work
111 rdf:about="">
112 <dc:format>image/svg+xml</dc:format>
113 <dc:type
114 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
115 <dc:title></dc:title>
116 </cc:Work>
117 </rdf:RDF>
118 </metadata>
119 <g
120 inkscape:label="Layer 1"
121 inkscape:groupmode="layer"
122 id="layer1"
123 transform="translate(-283.57144,-358.79068)">
124 <g
125 transform="translate(169.57144,223.42822)"
126 id="g5937"
127 inkscape:export-filename="envelope02.png"
128 inkscape:export-xdpi="90"
129 inkscape:export-ydpi="90" />
130 <g
131 id="g6442"
132 transform="translate(-753.45981,336.4283)">
133 <rect
134 style="fill:none;stroke:none"
135 id="rect1687"
136 width="90"
137 height="90"
138 x="1037.0312"
139 y="22.362379" />
140 <path
141 style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#808080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
142 d="M 44.929688 12.066406 C 34.037288 12.066406 24.849688 14.943458 17.367188 20.697266 C 9.7896875 26.523909 6 33.624202 6 42 C 7.7011 40.603306 9.6530686 39.474739 11.855469 38.615234 C 14.075869 37.773637 16.4573 37.353516 19 37.353516 C 21.5427 37.353516 23.914587 37.773637 26.117188 38.615234 C 28.337486 39.474739 30.2989 40.603306 32 42 C 33.7011 40.603306 35.653069 39.474739 37.855469 38.615234 C 40.075769 37.773637 42.4573 37.353516 45 37.353516 C 47.5427 37.353516 49.914586 37.773637 52.117188 38.615234 C 54.337487 39.474739 56.2989 40.603306 58 42 L 58.654297 42 C 60.235137 40.784631 62.009869 39.776665 64 39 C 66.2203 38.158403 68.601831 37.738281 71.144531 37.738281 C 73.687231 37.738281 76.059119 38.158403 78.261719 39 C 80.268226 39.776743 82.052173 40.784465 83.634766 42 L 84 42 C 84 33.624202 80.210313 26.523909 72.632812 20.697266 C 65.055613 14.943458 55.822287 12.066406 44.929688 12.066406 z "
143 transform="translate(1037.0313,22.36238)"
144 id="path4181" />
145 <path
146 style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-indent:0;text-align:end;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:end;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6.00000048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
147 d="m 1079.0312,28.363281 0,66.335938 0.01,0.09766 c 0.015,0.226687 0.022,0.780446 0.022,1.509766 0,1.918939 -0.4328,2.763898 -1.0039,3.289062 l -0.01,0.0059 -0.01,0.0078 c -0.6357,0.592743 -1.5903,1.007813 -3.4297,1.007813 -1.8313,0 -2.8017,-0.41364 -3.461,-1.015626 -0.5541,-0.519673 -0.9882,-1.37472 -0.9882,-3.294921 0,-0.72932 0.01,-1.281931 0.021,-1.501953 l -5.9863,-0.410157 c -0.041,0.598238 -0.035,1.18279 -0.035,1.91211 0,2.983063 0.8627,5.791367 2.9003,7.691407 l 0.01,0.008 0.01,0.006 c 1.9716,1.8129 4.6659,2.60547 7.5332,2.60547 2.8642,0 5.5555,-0.79304 7.5136,-2.61524 2.0532,-1.89396 2.9356,-4.70919 2.9356,-7.695307 0,-0.72932 0.01,-1.312724 -0.033,-1.904297 l 0.01,0.197265 0,-67.236328 -6,1 z"
148 id="path4225"
149 inkscape:connector-curvature="0"
150 sodipodi:nodetypes="cccscccscsccscccscsccccc" />
151 </g>
152 </g>
153</svg>
0154
=== added file 'app/graphics/extended-information_humidity.svg'
--- app/graphics/extended-information_humidity.svg 1970-01-01 00:00:00 +0000
+++ app/graphics/extended-information_humidity.svg 2015-06-28 23:49:12 +0000
@@ -0,0 +1,147 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="90"
13 height="90"
14 id="svg6138"
15 version="1.1"
16 inkscape:version="0.91pre2 r"
17 viewBox="0 0 90 90.000001"
18 sodipodi:docname="weather-chance-of-rain.svg">
19 <defs
20 id="defs6140" />
21 <sodipodi:namedview
22 id="base"
23 pagecolor="#ffffff"
24 bordercolor="#666666"
25 borderopacity="1.0"
26 inkscape:pageopacity="0.0"
27 inkscape:pageshadow="2"
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches