Merge lp:~parnold-x/slingshot/conversion-plugin into lp:~elementary-pantheon/slingshot/trunk
- conversion-plugin
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~parnold-x/slingshot/conversion-plugin |
Merge into: | lp:~elementary-pantheon/slingshot/trunk |
Diff against target: |
422 lines (+362/-3) 4 files modified
lib/synapse-plugins/CMakeLists.txt (+6/-2) lib/synapse-plugins/conversion-plugin.vala (+349/-0) lib/synapse-plugins/vapi/monetary.vapi (+6/-0) src/Backend/SynapseSearch.vala (+1/-1) |
To merge this branch: | bzr merge lp:~parnold-x/slingshot/conversion-plugin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Danielle Foré | Needs Fixing | ||
Review via email: mp+229368@code.launchpad.net |
Commit message
Add a unit conversion-plugin
Description of the change
Add a unit conversion-plugin in synapse. It uses GNU units (http://
The "conversion word" is set to "to" and should be localised. To test type for example "100$ to €" or "123l to gallons".
To support temperature conversion some additional code is added.
Most code in the plugin is needed to update the currency conversion rates. GNU units has a python script for this purpose but it needs root rights and additional python modules that are not included in elementary os so I implemented the update mechanism in vala.
The plugin checks for a update every 6 hours by checking if the currency rate file modification date is from today. If not it get the rates from timegenie.com, parses it and write it to the file.
At the first run it also copies the definition file from /usr/share/units/ to ~/.local/
TODO: Add good icon. Till now it takes the calculator icon.
Djax (parnold-x) wrote : | # |
It depends on GNU units. So you need to install the "units" package. Same as the calculator depends on the "bc" package that i installed by default.
Danielle Foré (danrabbit) wrote : | # |
It would probably be good to show the final units in the result like 100 meters to miles = "0.06 miles"
Also, rounding off after the second decimal might be good :)
Can we also add "as" and "in"? so we could have "100 liters in gallons" etc
Djax (parnold-x) wrote : | # |
Added "as" and "in". Should the "conversion words" stay the same in all languages or should they be localised?
Also rounding the output and add the unit to the result. When having very big or small numbers it prints e+14 or e-9. I think this is better than 100000000000000. OK?
Is there a way to ammend to a commit like in git in bazaar?
Danielle Foré (danrabbit) wrote : | # |
I imagine it should probably be localized. I'm not sure how many words make sense in all languages so I wonder if there's a way to define an array that localizations can fill with however many words make sense for them?
If possible, I would prefer something more human readable like "100 Million Miles" until we get over billion (I think anything more than that is probably not common language) and then e+14 probably makes sense.
You can do 'uncommit' and 'commit -m "whatever"' and then 'bzr push --overwrite'
Djax (parnold-x) wrote : | # |
Good to know. Thanks for the bzr commands.
Yep, no problem. I added a comment for the localisation guys. But if they localise it wrong the regex match will fail.
/* @ localization if you want use mutiple words, split them with a "|" between the words.
If it's just one word use "to". Don't use whitspaces before or after the words!
*/
private const string CONVERSION_WORD = _("to|as|in");
private const string MILLION = _("million");
private const string BILLION = _("billion");
I also added formating the result to million and billion. And when over 999 billion it uses the e+xx format. Don't know if you also want thousand? Till now it prints 120000 as number or do you want 1 tousand till 999 thousand also reformated?
Also happy to do anything else :) I am just couple days offline starting tomorrow.
Danielle Foré (danrabbit) wrote : | # |
Hey I hate to mention this after the fact xD But I was just thinking "There must be a library for this" and it turns out there is a library that will format strings based on locale for decimal places and separators and all that. So I guess we should probably be using that :p
http://
Djax (parnold-x) wrote : | # |
Hey, I am back online.
I tried to write a vapi file that use the libc function and integrated it into cmake. I hope this is the right way since this is my first .vapi file. It seems to work well.
Djax (parnold-x) wrote : | # |
Added improved regex match for the temperatures. Thanks to Jakob Parker.
For the record:
I wondered if we now exclude other units and the symbols C and F are assign to different units.
http://
http://
celsius is assigned to °C
fahrenheit to °F
and rankin to °R
only kelvin is assigned to K without the degree symbol.
But google does it also without a degree symbol so I think temperature conversion is used way more often and converting in Farad/Coulomb is still possible with the full names.
https:/
Jacob Parker (jacobparker1992) wrote : | # |
Don't kill me, but I changed the code style once again to match the other files in the plugins directory (which is apparently different to the elementary code style :P).
How are digits handled? We pick up '1.3 meters to yards', but I don't think we handle the funny countries that would write '1,3' instead. I'm not sure why we pick up '1.3' though---is that somehow part of the digit regex (JS considers \d to only be 0-9)?
- Jacob
Djax (parnold-x) wrote : | # |
The other plugins are just imported from synapse, so no need to adapt to their code style.
Ah yeah, my "funny" country uses 1,3 but it is easy to fix.
"1.3 meters to yards". The match_regex splits just at " to " with no digit before the whitespace. So no problem with what digit is used.
In the temperature case there was a problem to match the digit in that case but I fixed that also.
Jacob Parker (jacobparker1992) wrote : | # |
Any chance this could get another review?
Djax (parnold-x) wrote : | # |
Note: Some superscripts look slightly missplaced. This is caused by the font size of the search result. With 12pt or greater everything looks ok.
Danielle Foré (danrabbit) wrote : | # |
Bumping. Needs to be merged into trunk
Unmerged revisions
- 439. By Djax
-
allow , as digit seperator
- 438. By Djax
-
improved regex and codestyle thanks to jacobparker1992; print small and very big numbers scientifically
- 437. By Djax
-
replacement after split
- 436. By Djax
-
using libc strfmon function
- 435. By Djax
-
localisation and formating output
- 434. By Djax
-
reformating the output
- 433. By Djax
-
conversion word = to,as,in and formating the output
- 432. By Djax
-
add unit conversion-plugin
Preview Diff
1 | === modified file 'lib/synapse-plugins/CMakeLists.txt' | |||
2 | --- lib/synapse-plugins/CMakeLists.txt 2014-06-29 14:50:36 +0000 | |||
3 | +++ lib/synapse-plugins/CMakeLists.txt 2014-08-16 22:26:39 +0000 | |||
4 | @@ -6,6 +6,8 @@ | |||
5 | 6 | gio-unix-2.0 | 6 | gio-unix-2.0 |
6 | 7 | gee-0.8 | 7 | gee-0.8 |
7 | 8 | gtk+-3.0 | 8 | gtk+-3.0 |
8 | 9 | libsoup-2.4 | ||
9 | 10 | libxml-2.0 | ||
10 | 9 | ) | 11 | ) |
11 | 10 | 12 | ||
12 | 11 | pkg_check_modules(PLUGINS_DEPS REQUIRED ${PLUGINS_PKG}) | 13 | pkg_check_modules(PLUGINS_DEPS REQUIRED ${PLUGINS_PKG}) |
13 | @@ -13,6 +15,7 @@ | |||
14 | 13 | set(PLUGINS_SOURCE | 15 | set(PLUGINS_SOURCE |
15 | 14 | calculator-plugin.vala | 16 | calculator-plugin.vala |
16 | 15 | command-plugin.vala | 17 | command-plugin.vala |
17 | 18 | conversion-plugin.vala | ||
18 | 16 | desktop-file-plugin.vala | 19 | desktop-file-plugin.vala |
19 | 17 | ) | 20 | ) |
20 | 18 | 21 | ||
21 | @@ -23,6 +26,8 @@ | |||
22 | 23 | PACKAGES | 26 | PACKAGES |
23 | 24 | ${PLUGINS_PKG} | 27 | ${PLUGINS_PKG} |
24 | 25 | synapse-core | 28 | synapse-core |
25 | 29 | CUSTOM_VAPIS | ||
26 | 30 | vapi/monetary.vapi | ||
27 | 26 | OPTIONS | 31 | OPTIONS |
28 | 27 | --vapidir=${CMAKE_BINARY_DIR}/lib/synapse-core | 32 | --vapidir=${CMAKE_BINARY_DIR}/lib/synapse-core |
29 | 28 | GENERATE_VAPI | 33 | GENERATE_VAPI |
30 | @@ -43,5 +48,4 @@ | |||
31 | 43 | SOVERSION ${PLUGINS_LIB_SOVERSION} | 48 | SOVERSION ${PLUGINS_LIB_SOVERSION} |
32 | 44 | ) | 49 | ) |
33 | 45 | 50 | ||
36 | 46 | target_link_libraries (${PLUGINS_LIBRARY_NAME} ${PLUGINS_DEPS_LIBRARIES} synapse-core) | 51 | target_link_libraries (${PLUGINS_LIBRARY_NAME} ${PLUGINS_DEPS_LIBRARIES} synapse-core) |
35 | 47 | |||
37 | 48 | \ No newline at end of file | 52 | \ No newline at end of file |
38 | 49 | 53 | ||
39 | === added file 'lib/synapse-plugins/conversion-plugin.vala' | |||
40 | --- lib/synapse-plugins/conversion-plugin.vala 1970-01-01 00:00:00 +0000 | |||
41 | +++ lib/synapse-plugins/conversion-plugin.vala 2014-08-16 22:26:39 +0000 | |||
42 | @@ -0,0 +1,349 @@ | |||
43 | 1 | /* | ||
44 | 2 | * Copyright (C) 2014 Peter Arnold <parnold1@gmail.com> | ||
45 | 3 | * | ||
46 | 4 | * This program is free software; you can redistribute it and/or modify | ||
47 | 5 | * it under the terms of the GNU General Public License as published by | ||
48 | 6 | * the Free Software Foundation; either version 2 of the License, or | ||
49 | 7 | * (at your option) any later version. | ||
50 | 8 | * | ||
51 | 9 | * This program is distributed in the hope that it will be useful, | ||
52 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
53 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
54 | 12 | * GNU General Public License for more details. | ||
55 | 13 | * | ||
56 | 14 | * You should have received a copy of the GNU General Public License | ||
57 | 15 | * along with this program; if not, write to the Free Software | ||
58 | 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
59 | 17 | * | ||
60 | 18 | * Authored by Peter Arnold <parnold1@gmail.com> | ||
61 | 19 | * | ||
62 | 20 | */ | ||
63 | 21 | |||
64 | 22 | namespace Synapse | ||
65 | 23 | { | ||
66 | 24 | errordomain IOError { | ||
67 | 25 | FILE_NOT_FOUND | ||
68 | 26 | } | ||
69 | 27 | |||
70 | 28 | public class ConversionPlugin: Object, Activatable, ItemProvider { | ||
71 | 29 | public bool enabled { get; set; default = true; } | ||
72 | 30 | |||
73 | 31 | public void activate () {} | ||
74 | 32 | |||
75 | 33 | public void deactivate () {} | ||
76 | 34 | |||
77 | 35 | private class Result: Object, Match { | ||
78 | 36 | // from Match interface | ||
79 | 37 | public string title { get; construct set; } | ||
80 | 38 | public string description { get; set; } | ||
81 | 39 | public string icon_name { get; construct set; } | ||
82 | 40 | public bool has_thumbnail { get; construct set; } | ||
83 | 41 | public string thumbnail_path { get; construct set; } | ||
84 | 42 | public MatchType match_type { get; construct set; } | ||
85 | 43 | |||
86 | 44 | public int default_relevancy { get; set; default = 0; } | ||
87 | 45 | |||
88 | 46 | public Result (string result) { | ||
89 | 47 | Object (match_type: MatchType.TEXT, | ||
90 | 48 | title: result, | ||
91 | 49 | description: result, | ||
92 | 50 | has_thumbnail: false, | ||
93 | 51 | icon_name: "accessories-calculator"); | ||
94 | 52 | } | ||
95 | 53 | } | ||
96 | 54 | |||
97 | 55 | static void register_plugin () { | ||
98 | 56 | DataSink.PluginRegistry.get_default ().register_plugin ( | ||
99 | 57 | typeof (ConversionPlugin), | ||
100 | 58 | _ ("Conversion"), | ||
101 | 59 | _ ("Conversion of units."), | ||
102 | 60 | "accessories-calculator", | ||
103 | 61 | register_plugin, | ||
104 | 62 | Environment.find_program_in_path ("units") != null, | ||
105 | 63 | _ ("units is not installed") | ||
106 | 64 | ); | ||
107 | 65 | } | ||
108 | 66 | |||
109 | 67 | static construct { | ||
110 | 68 | register_plugin (); | ||
111 | 69 | } | ||
112 | 70 | |||
113 | 71 | /* @ localization if you want use mutiple words, split them with a "|" between the words. | ||
114 | 72 | If it's just one word use "to". Don't use whitspaces before or after the words! | ||
115 | 73 | */ | ||
116 | 74 | private const string CONVERSION_WORD = _("to|as|in"); | ||
117 | 75 | private Regex match_regex; | ||
118 | 76 | private Regex temp_regex; | ||
119 | 77 | private Regex digit_regex; | ||
120 | 78 | |||
121 | 79 | construct { | ||
122 | 80 | try { | ||
123 | 81 | /* regex to match the conversion words (to, as, in) with no digit before the whitespace in cases the conversion word | ||
124 | 82 | has the same name as a unit for example in and in (inch) | ||
125 | 83 | */ | ||
126 | 84 | match_regex = new Regex (@"(?<=\\D)\\s*\\b(?:$CONVERSION_WORD)\\b\\s*", | ||
127 | 85 | RegexCompileFlags.OPTIMIZE); | ||
128 | 86 | // regex to match the temperature units | ||
129 | 87 | temp_regex = new Regex ("(?i)(?<=[\\d\\s])[FCKR]\\b|fahrenheit|celsius|kelvin|rankine", | ||
130 | 88 | RegexCompileFlags.OPTIMIZE); | ||
131 | 89 | digit_regex = new Regex ("(\\d*\\.?\\d*)", | ||
132 | 90 | RegexCompileFlags.OPTIMIZE); | ||
133 | 91 | } catch (Error e) { | ||
134 | 92 | Utils.Logger.error (this, "Error creating regexp."); | ||
135 | 93 | } | ||
136 | 94 | |||
137 | 95 | try { | ||
138 | 96 | // set a timer that wake ups every 6hours = 21600s to check if the currency rates should be updated | ||
139 | 97 | check_for_update.begin (); | ||
140 | 98 | Timeout.add_seconds_full (Priority.DEFAULT, 21600, () => { | ||
141 | 99 | check_for_update.begin (); | ||
142 | 100 | return true; | ||
143 | 101 | }); | ||
144 | 102 | } catch (Error e) { | ||
145 | 103 | Utils.Logger.error (this, "Error updating the currency rates"); | ||
146 | 104 | } | ||
147 | 105 | |||
148 | 106 | } | ||
149 | 107 | |||
150 | 108 | public bool handles_query (Query query) { | ||
151 | 109 | return (QueryFlags.ACTIONS in query.query_type); | ||
152 | 110 | } | ||
153 | 111 | |||
154 | 112 | static bool regex_function (MatchInfo info, StringBuilder resource) { | ||
155 | 113 | string match = info.fetch (0).slice (0, 1).up (); | ||
156 | 114 | resource.append (@"temp$match "); | ||
157 | 115 | return false; | ||
158 | 116 | } | ||
159 | 117 | |||
160 | 118 | public async ResultSet? search (Query query) throws SearchError { | ||
161 | 119 | string input = query.query_string; | ||
162 | 120 | bool matched = match_regex.match (input); | ||
163 | 121 | |||
164 | 122 | if (!matched) { | ||
165 | 123 | query.check_cancellable (); | ||
166 | 124 | return null; | ||
167 | 125 | } | ||
168 | 126 | |||
169 | 127 | try { | ||
170 | 128 | input = input.replace ("²", "^2").replace ("³", "^3").replace ("°", "").replace(",","."); | ||
171 | 129 | |||
172 | 130 | // to support the conversion of temperatures see http://www.gnu.org/software/units/manual/units.html#Temperature-Conversions | ||
173 | 131 | bool temparaturematch = temp_regex.match (input); | ||
174 | 132 | |||
175 | 133 | if (temparaturematch) | ||
176 | 134 | input = temp_regex.replace_eval (input, input.length, 0, RegexMatchFlags.NOTBOL, (RegexEvalCallback) regex_function); | ||
177 | 135 | |||
178 | 136 | string[] inputsplit = match_regex.split (input); | ||
179 | 137 | |||
180 | 138 | if (temparaturematch) { | ||
181 | 139 | MatchInfo info; | ||
182 | 140 | digit_regex.match (inputsplit[0], 0, out info); | ||
183 | 141 | string digit = info.fetch (0); | ||
184 | 142 | inputsplit[0] = inputsplit[0].replace (digit, "") + @"($digit)"; | ||
185 | 143 | } | ||
186 | 144 | |||
187 | 145 | Pid pid; | ||
188 | 146 | int read_fd, write_fd; | ||
189 | 147 | string[] argv = {"units", "-1", "-f", Environment.get_home_dir () + "/.local/share/units/definitions.units", inputsplit[0], inputsplit[1]}; | ||
190 | 148 | string? solution = null; | ||
191 | 149 | |||
192 | 150 | Process.spawn_async_with_pipes (null, argv, null, | ||
193 | 151 | SpawnFlags.SEARCH_PATH, | ||
194 | 152 | null, out pid, out write_fd, out read_fd); | ||
195 | 153 | |||
196 | 154 | UnixInputStream read_stream = new UnixInputStream (read_fd, true); | ||
197 | 155 | DataInputStream bc_output = new DataInputStream (read_stream); | ||
198 | 156 | solution = yield bc_output.read_line_async (Priority.DEFAULT_IDLE, query.cancellable); | ||
199 | 157 | |||
200 | 158 | if (solution != null && (solution.contains ("*") || temparaturematch)) { | ||
201 | 159 | query.check_cancellable (); | ||
202 | 160 | solution = solution.replace ("*", ""); | ||
203 | 161 | solution = solution.chug (); | ||
204 | 162 | bool res = double.try_parse (solution); | ||
205 | 163 | if (!res) | ||
206 | 164 | return null; | ||
207 | 165 | |||
208 | 166 | double d = double.parse (solution); | ||
209 | 167 | string digit; | ||
210 | 168 | // the smallest output of the strfmon function is 0.01 and > 10^11 is not really readable in float format | ||
211 | 169 | // so print it scientifically in those cases | ||
212 | 170 | if (d >= 0.01 && d < 999999999999.9) { | ||
213 | 171 | char buffer[100]; | ||
214 | 172 | Monetary.strfmon(buffer, "%!n", d); | ||
215 | 173 | digit = (string) buffer; | ||
216 | 174 | } else | ||
217 | 175 | digit = print_scientifically (d); | ||
218 | 176 | |||
219 | 177 | string unit = " " + inputsplit[1]; | ||
220 | 178 | unit = unit.replace (" tempK", " K").replace (" temp", " °"); | ||
221 | 179 | |||
222 | 180 | Result result = new Result (digit + unit); | ||
223 | 181 | ResultSet results = new ResultSet (); | ||
224 | 182 | results.add (result, Match.Score.AVERAGE); | ||
225 | 183 | |||
226 | 184 | return results; | ||
227 | 185 | } | ||
228 | 186 | } catch (Error err) { | ||
229 | 187 | if (!query.is_cancelled ()) | ||
230 | 188 | warning ("%s", err.message); | ||
231 | 189 | } | ||
232 | 190 | |||
233 | 191 | return null; | ||
234 | 192 | } | ||
235 | 193 | |||
236 | 194 | // prints the %g output of printf in "x 10⁷⁷" format | ||
237 | 195 | private string print_scientifically (double d) { | ||
238 | 196 | string digit = "%g".printf(d); | ||
239 | 197 | if (!digit.contains ("e")) | ||
240 | 198 | return digit; | ||
241 | 199 | string exponent = digit.substring (digit.last_index_of ("e")+2); | ||
242 | 200 | if (exponent.has_prefix ("0")) | ||
243 | 201 | exponent = exponent.replace ("0",""); | ||
244 | 202 | exponent = exponent.replace ("0","⁰").replace ("1","¹").replace ("2","²").replace ("3","³").replace ("4","⁴").replace ("5","⁵") | ||
245 | 203 | .replace ("6","⁶").replace ("7","⁷").replace ("8","⁸").replace ("9","⁹"); | ||
246 | 204 | if (digit.substring (digit.last_index_of ("e")+1,1) == "-") | ||
247 | 205 | exponent = "⁻" + exponent; | ||
248 | 206 | return digit.substring (0,digit.last_index_of ("e")) + " x 10" + exponent; | ||
249 | 207 | } | ||
250 | 208 | |||
251 | 209 | private async void check_for_update () { | ||
252 | 210 | var file = File.new_for_path (Environment.get_home_dir () + "/.local/share/units/currency.units"); | ||
253 | 211 | |||
254 | 212 | if (!file.query_exists ()) { | ||
255 | 213 | try { | ||
256 | 214 | //copy data to user directory to be able to modifiy it without root rights | ||
257 | 215 | var dir = File.new_for_path (Environment.get_home_dir () + "/.local/share/units/"); | ||
258 | 216 | if (!dir.query_exists ()) | ||
259 | 217 | dir.make_directory (); | ||
260 | 218 | var definitions_file = File.new_for_path ("/usr/share/units/definitions.units"); | ||
261 | 219 | var target_definitions = File.new_for_path (Environment.get_home_dir()+"/.local/share/units/definitions.units"); | ||
262 | 220 | if (!target_definitions.query_exists ()) | ||
263 | 221 | definitions_file.copy (target_definitions, FileCopyFlags.NONE); | ||
264 | 222 | var currency_file = File.new_for_path ("/usr/share/units/currency.units"); | ||
265 | 223 | currency_file.copy (file, FileCopyFlags.NONE); | ||
266 | 224 | } catch (Error e) { | ||
267 | 225 | Utils.Logger.error (this, "Failed to copy /usr/share/units/definitions.units"); | ||
268 | 226 | } | ||
269 | 227 | } | ||
270 | 228 | if (file.query_exists ()) { | ||
271 | 229 | try { | ||
272 | 230 | var file_info = file.query_info ("*", FileQueryInfoFlags.NONE); | ||
273 | 231 | var modification_date = new DateTime.from_timeval_local (file_info.get_modification_time ()); | ||
274 | 232 | var now = new DateTime.now_local (); | ||
275 | 233 | // timegenie update their rates once a day | ||
276 | 234 | if (now.get_day_of_year () != modification_date.get_day_of_year ()) | ||
277 | 235 | update (file, now); | ||
278 | 236 | } catch (Error e) { | ||
279 | 237 | Utils.Logger.error (this, "Failed to update '%s'".printf (file.get_path ())); | ||
280 | 238 | } | ||
281 | 239 | } | ||
282 | 240 | } | ||
283 | 241 | |||
284 | 242 | private void update (File file, DateTime now) throws Error { | ||
285 | 243 | file.delete (); | ||
286 | 244 | var dos = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); | ||
287 | 245 | Array<string> codes; | ||
288 | 246 | Array<string> names; | ||
289 | 247 | Array<string> rates; | ||
290 | 248 | // load and write data from timegenie | ||
291 | 249 | load_currency_rates_data (out codes, out names, out rates); | ||
292 | 250 | write_currency_data (dos, codes, names, rates, now); | ||
293 | 251 | } | ||
294 | 252 | |||
295 | 253 | private void write_currency_data (DataOutputStream dos, Array<string> codes, Array<string> names, Array<string> values, DateTime now) throws Error { | ||
296 | 254 | var builder = new StringBuilder ("# ISO Currency Codes\n\n"); | ||
297 | 255 | |||
298 | 256 | for (int i = 0; i < codes.length; i++) | ||
299 | 257 | builder.append ("%s%s%s\n".printf (codes.index (i), string.nfill (15, ' '), names.index (i))); | ||
300 | 258 | |||
301 | 259 | builder.append ("\n# Currency exchange rates from Time Genie (www.timegenie.com) from %s\n\n".printf (now.format ("%F"))); | ||
302 | 260 | |||
303 | 261 | for (int i = 0; i < codes.length; i++) | ||
304 | 262 | builder.append ("%s%s%s\n".printf (names.index (i), string.nfill (30-names.index (i).length, ' '), values.index (i))); | ||
305 | 263 | |||
306 | 264 | uint8[] data = builder.str.data; | ||
307 | 265 | long written = 0; | ||
308 | 266 | |||
309 | 267 | while (written < data.length) | ||
310 | 268 | written += dos.write (data[written:data.length]); | ||
311 | 269 | } | ||
312 | 270 | |||
313 | 271 | private void load_currency_rates_data (out Array<string> codes, out Array<string> names, out Array<string> values) throws IOError { | ||
314 | 272 | codes = new Array<string> (); | ||
315 | 273 | names = new Array<string> (); | ||
316 | 274 | values = new Array<string> (); | ||
317 | 275 | var tmp_values = new Array<string> (); | ||
318 | 276 | string url = "http://rss.timegenie.com/forex.xml"; | ||
319 | 277 | |||
320 | 278 | var session = new Soup.Session (); | ||
321 | 279 | var msg = new Soup.Message ("GET", url); | ||
322 | 280 | session.send_message (msg); | ||
323 | 281 | Xml.Doc* doc = Xml.Parser.parse_memory ( | ||
324 | 282 | (string) (msg.response_body.data), | ||
325 | 283 | (int) (msg.response_body.length)); | ||
326 | 284 | if (doc == null) | ||
327 | 285 | throw new IOError.FILE_NOT_FOUND("Failed to parse http://rss.timegenie.com/forex.xml"); | ||
328 | 286 | Xml.Node* root = doc->get_root_element (); | ||
329 | 287 | if (root == null) { | ||
330 | 288 | delete doc; | ||
331 | 289 | throw new IOError.FILE_NOT_FOUND ("The xml file is empty"); | ||
332 | 290 | } | ||
333 | 291 | int index_usd = -1; | ||
334 | 292 | int index_euro = -1; | ||
335 | 293 | int index = 0; | ||
336 | 294 | for (Xml.Node* iter = root->children; iter != null; iter = iter->next) { | ||
337 | 295 | if (iter->type == Xml.ElementType.ELEMENT_NODE) { | ||
338 | 296 | if (iter->name == "data") { | ||
339 | 297 | for (Xml.Node* iter2 = iter->children; iter2 != null; iter2 = iter2->next) { | ||
340 | 298 | if (iter2->type == Xml.ElementType.ELEMENT_NODE) { | ||
341 | 299 | switch (iter2->name) { | ||
342 | 300 | case "code": | ||
343 | 301 | var content = get_node_content (iter2); | ||
344 | 302 | if (content != "") { | ||
345 | 303 | if (content == "USD") | ||
346 | 304 | index_usd = index; | ||
347 | 305 | else if (content == "EUR") | ||
348 | 306 | index_euro = index; | ||
349 | 307 | codes.append_val (content); | ||
350 | 308 | } | ||
351 | 309 | break; | ||
352 | 310 | case "description": | ||
353 | 311 | var content = get_node_content (iter2); | ||
354 | 312 | if (content != "") { | ||
355 | 313 | if (content == "Anguilla (ECD)") content = "eastcaribbeandollar"; | ||
356 | 314 | content = content.replace (" ", "").down (); | ||
357 | 315 | names.append_val (content); | ||
358 | 316 | } | ||
359 | 317 | break; | ||
360 | 318 | case "rate": | ||
361 | 319 | var content = get_node_content (iter2); | ||
362 | 320 | if (content != "") | ||
363 | 321 | tmp_values.append_val (content); | ||
364 | 322 | break; | ||
365 | 323 | default: | ||
366 | 324 | // do nothing | ||
367 | 325 | break; | ||
368 | 326 | } | ||
369 | 327 | } | ||
370 | 328 | } | ||
371 | 329 | index++; | ||
372 | 330 | } | ||
373 | 331 | } | ||
374 | 332 | } | ||
375 | 333 | delete doc; | ||
376 | 334 | for (int i = 0; i < tmp_values.length; i++) { | ||
377 | 335 | if (i == index_euro) values.append_val (tmp_values.index (index_usd) + " US$"); | ||
378 | 336 | else if (i == index_usd) values.append_val ("1 US$"); | ||
379 | 337 | else values.append_val ("1|" + tmp_values.index (i) + " euro"); | ||
380 | 338 | } | ||
381 | 339 | } | ||
382 | 340 | |||
383 | 341 | private string get_node_content (Xml.Node* node) { | ||
384 | 342 | for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { | ||
385 | 343 | if (iter->type == Xml.ElementType.TEXT_NODE) | ||
386 | 344 | return iter->get_content (); | ||
387 | 345 | } | ||
388 | 346 | return ""; | ||
389 | 347 | } | ||
390 | 348 | } | ||
391 | 349 | } | ||
392 | 0 | \ No newline at end of file | 350 | \ No newline at end of file |
393 | 1 | 351 | ||
394 | === added directory 'lib/synapse-plugins/vapi' | |||
395 | === added file 'lib/synapse-plugins/vapi/monetary.vapi' | |||
396 | --- lib/synapse-plugins/vapi/monetary.vapi 1970-01-01 00:00:00 +0000 | |||
397 | +++ lib/synapse-plugins/vapi/monetary.vapi 2014-08-16 22:26:39 +0000 | |||
398 | @@ -0,0 +1,6 @@ | |||
399 | 1 | [CCode (cheader_filename = "monetary.h")] | ||
400 | 2 | public class Monetary { | ||
401 | 3 | [CCode(cname = "strfmon")] | ||
402 | 4 | public static ssize_t strfmon(char[] s, string format, double data); | ||
403 | 5 | |||
404 | 6 | } | ||
405 | 0 | \ No newline at end of file | 7 | \ No newline at end of file |
406 | 1 | 8 | ||
407 | === modified file 'src/Backend/SynapseSearch.vala' | |||
408 | --- src/Backend/SynapseSearch.vala 2014-06-29 14:50:36 +0000 | |||
409 | +++ src/Backend/SynapseSearch.vala 2014-08-16 22:26:39 +0000 | |||
410 | @@ -23,6 +23,7 @@ | |||
411 | 23 | private static Type[] plugins = { | 23 | private static Type[] plugins = { |
412 | 24 | typeof (Synapse.CalculatorPlugin), | 24 | typeof (Synapse.CalculatorPlugin), |
413 | 25 | typeof (Synapse.CommandPlugin), | 25 | typeof (Synapse.CommandPlugin), |
414 | 26 | typeof (Synapse.ConversionPlugin), | ||
415 | 26 | typeof (Synapse.DesktopFilePlugin) | 27 | typeof (Synapse.DesktopFilePlugin) |
416 | 27 | }; | 28 | }; |
417 | 28 | 29 | ||
418 | @@ -161,4 +162,3 @@ | |||
419 | 161 | } | 162 | } |
420 | 162 | } | 163 | } |
421 | 163 | } | 164 | } |
422 | 164 |
hmm this doesn't seem to do anything for me. I don't have a /usr/share/units or ~/.local/ share/units. Is there a dependency that wasn't added to cmake?