New upstream version 121
Jonas Smedegaard
5 years ago
0 | [flake8] | |
1 | ||
2 | # E402 module level import not at top of file | |
3 | # gi.require_version() is required before later imports | |
4 | ||
5 | ignore = E402 |
0 | GNU LIBRARY GENERAL PUBLIC LICENSE | |
1 | Version 2, June 1991 | |
2 | ||
3 | Copyright (C) 1991 Free Software Foundation, Inc. | |
4 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
5 | Everyone is permitted to copy and distribute verbatim copies | |
6 | of this license document, but changing it is not allowed. | |
7 | ||
8 | [This is the first released version of the library GPL. It is | |
9 | numbered 2 because it goes with version 2 of the ordinary GPL.] | |
10 | ||
11 | Preamble | |
12 | ||
13 | The licenses for most software are designed to take away your | |
14 | freedom to share and change it. By contrast, the GNU General Public | |
15 | Licenses are intended to guarantee your freedom to share and change | |
16 | free software--to make sure the software is free for all its users. | |
17 | ||
18 | This license, the Library General Public License, applies to some | |
19 | specially designated Free Software Foundation software, and to any | |
20 | other libraries whose authors decide to use it. You can use it for | |
21 | your libraries, too. | |
22 | ||
23 | When we speak of free software, we are referring to freedom, not | |
24 | price. Our General Public Licenses are designed to make sure that you | |
25 | have the freedom to distribute copies of free software (and charge for | |
26 | this service if you wish), that you receive source code or can get it | |
27 | if you want it, that you can change the software or use pieces of it | |
28 | in new free programs; and that you know you can do these things. | |
29 | ||
30 | To protect your rights, we need to make restrictions that forbid | |
31 | anyone to deny you these rights or to ask you to surrender the rights. | |
32 | These restrictions translate to certain responsibilities for you if | |
33 | you distribute copies of the library, or if you modify it. | |
34 | ||
35 | For example, if you distribute copies of the library, whether gratis | |
36 | or for a fee, you must give the recipients all the rights that we gave | |
37 | you. You must make sure that they, too, receive or can get the source | |
38 | code. If you link a program with the library, you must provide | |
39 | complete object files to the recipients so that they can relink them | |
40 | with the library, after making changes to the library and recompiling | |
41 | it. And you must show them these terms so they know their rights. | |
42 | ||
43 | Our method of protecting your rights has two steps: (1) copyright | |
44 | the library, and (2) offer you this license which gives you legal | |
45 | permission to copy, distribute and/or modify the library. | |
46 | ||
47 | Also, for each distributor's protection, we want to make certain | |
48 | that everyone understands that there is no warranty for this free | |
49 | library. If the library is modified by someone else and passed on, we | |
50 | want its recipients to know that what they have is not the original | |
51 | version, so that any problems introduced by others will not reflect on | |
52 | the original authors' reputations. | |
53 | ||
54 | Finally, any free program is threatened constantly by software | |
55 | patents. We wish to avoid the danger that companies distributing free | |
56 | software will individually obtain patent licenses, thus in effect | |
57 | transforming the program into proprietary software. To prevent this, | |
58 | we have made it clear that any patent must be licensed for everyone's | |
59 | free use or not licensed at all. | |
60 | ||
61 | Most GNU software, including some libraries, is covered by the ordinary | |
62 | GNU General Public License, which was designed for utility programs. This | |
63 | license, the GNU Library General Public License, applies to certain | |
64 | designated libraries. This license is quite different from the ordinary | |
65 | one; be sure to read it in full, and don't assume that anything in it is | |
66 | the same as in the ordinary license. | |
67 | ||
68 | The reason we have a separate public license for some libraries is that | |
69 | they blur the distinction we usually make between modifying or adding to a | |
70 | program and simply using it. Linking a program with a library, without | |
71 | changing the library, is in some sense simply using the library, and is | |
72 | analogous to running a utility program or application program. However, in | |
73 | a textual and legal sense, the linked executable is a combined work, a | |
74 | derivative of the original library, and the ordinary General Public License | |
75 | treats it as such. | |
76 | ||
77 | Because of this blurred distinction, using the ordinary General | |
78 | Public License for libraries did not effectively promote software | |
79 | sharing, because most developers did not use the libraries. We | |
80 | concluded that weaker conditions might promote sharing better. | |
81 | ||
82 | However, unrestricted linking of non-free programs would deprive the | |
83 | users of those programs of all benefit from the free status of the | |
84 | libraries themselves. This Library General Public License is intended to | |
85 | permit developers of non-free programs to use free libraries, while | |
86 | preserving your freedom as a user of such programs to change the free | |
87 | libraries that are incorporated in them. (We have not seen how to achieve | |
88 | this as regards changes in header files, but we have achieved it as regards | |
89 | changes in the actual functions of the Library.) The hope is that this | |
90 | will lead to faster development of free libraries. | |
91 | ||
92 | The precise terms and conditions for copying, distribution and | |
93 | modification follow. Pay close attention to the difference between a | |
94 | "work based on the library" and a "work that uses the library". The | |
95 | former contains code derived from the library, while the latter only | |
96 | works together with the library. | |
97 | ||
98 | Note that it is possible for a library to be covered by the ordinary | |
99 | General Public License rather than by this special one. | |
100 | ||
101 | GNU LIBRARY GENERAL PUBLIC LICENSE | |
102 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
103 | ||
104 | 0. This License Agreement applies to any software library which | |
105 | contains a notice placed by the copyright holder or other authorized | |
106 | party saying it may be distributed under the terms of this Library | |
107 | General Public License (also called "this License"). Each licensee is | |
108 | addressed as "you". | |
109 | ||
110 | A "library" means a collection of software functions and/or data | |
111 | prepared so as to be conveniently linked with application programs | |
112 | (which use some of those functions and data) to form executables. | |
113 | ||
114 | The "Library", below, refers to any such software library or work | |
115 | which has been distributed under these terms. A "work based on the | |
116 | Library" means either the Library or any derivative work under | |
117 | copyright law: that is to say, a work containing the Library or a | |
118 | portion of it, either verbatim or with modifications and/or translated | |
119 | straightforwardly into another language. (Hereinafter, translation is | |
120 | included without limitation in the term "modification".) | |
121 | ||
122 | "Source code" for a work means the preferred form of the work for | |
123 | making modifications to it. For a library, complete source code means | |
124 | all the source code for all modules it contains, plus any associated | |
125 | interface definition files, plus the scripts used to control compilation | |
126 | and installation of the library. | |
127 | ||
128 | Activities other than copying, distribution and modification are not | |
129 | covered by this License; they are outside its scope. The act of | |
130 | running a program using the Library is not restricted, and output from | |
131 | such a program is covered only if its contents constitute a work based | |
132 | on the Library (independent of the use of the Library in a tool for | |
133 | writing it). Whether that is true depends on what the Library does | |
134 | and what the program that uses the Library does. | |
135 | ||
136 | 1. You may copy and distribute verbatim copies of the Library's | |
137 | complete source code as you receive it, in any medium, provided that | |
138 | you conspicuously and appropriately publish on each copy an | |
139 | appropriate copyright notice and disclaimer of warranty; keep intact | |
140 | all the notices that refer to this License and to the absence of any | |
141 | warranty; and distribute a copy of this License along with the | |
142 | Library. | |
143 | ||
144 | You may charge a fee for the physical act of transferring a copy, | |
145 | and you may at your option offer warranty protection in exchange for a | |
146 | fee. | |
147 | ||
148 | 2. You may modify your copy or copies of the Library or any portion | |
149 | of it, thus forming a work based on the Library, and copy and | |
150 | distribute such modifications or work under the terms of Section 1 | |
151 | above, provided that you also meet all of these conditions: | |
152 | ||
153 | a) The modified work must itself be a software library. | |
154 | ||
155 | b) You must cause the files modified to carry prominent notices | |
156 | stating that you changed the files and the date of any change. | |
157 | ||
158 | c) You must cause the whole of the work to be licensed at no | |
159 | charge to all third parties under the terms of this License. | |
160 | ||
161 | d) If a facility in the modified Library refers to a function or a | |
162 | table of data to be supplied by an application program that uses | |
163 | the facility, other than as an argument passed when the facility | |
164 | is invoked, then you must make a good faith effort to ensure that, | |
165 | in the event an application does not supply such function or | |
166 | table, the facility still operates, and performs whatever part of | |
167 | its purpose remains meaningful. | |
168 | ||
169 | (For example, a function in a library to compute square roots has | |
170 | a purpose that is entirely well-defined independent of the | |
171 | application. Therefore, Subsection 2d requires that any | |
172 | application-supplied function or table used by this function must | |
173 | be optional: if the application does not supply it, the square | |
174 | root function must still compute square roots.) | |
175 | ||
176 | These requirements apply to the modified work as a whole. If | |
177 | identifiable sections of that work are not derived from the Library, | |
178 | and can be reasonably considered independent and separate works in | |
179 | themselves, then this License, and its terms, do not apply to those | |
180 | sections when you distribute them as separate works. But when you | |
181 | distribute the same sections as part of a whole which is a work based | |
182 | on the Library, the distribution of the whole must be on the terms of | |
183 | this License, whose permissions for other licensees extend to the | |
184 | entire whole, and thus to each and every part regardless of who wrote | |
185 | it. | |
186 | ||
187 | Thus, it is not the intent of this section to claim rights or contest | |
188 | your rights to work written entirely by you; rather, the intent is to | |
189 | exercise the right to control the distribution of derivative or | |
190 | collective works based on the Library. | |
191 | ||
192 | In addition, mere aggregation of another work not based on the Library | |
193 | with the Library (or with a work based on the Library) on a volume of | |
194 | a storage or distribution medium does not bring the other work under | |
195 | the scope of this License. | |
196 | ||
197 | 3. You may opt to apply the terms of the ordinary GNU General Public | |
198 | License instead of this License to a given copy of the Library. To do | |
199 | this, you must alter all the notices that refer to this License, so | |
200 | that they refer to the ordinary GNU General Public License, version 2, | |
201 | instead of to this License. (If a newer version than version 2 of the | |
202 | ordinary GNU General Public License has appeared, then you can specify | |
203 | that version instead if you wish.) Do not make any other change in | |
204 | these notices. | |
205 | ||
206 | Once this change is made in a given copy, it is irreversible for | |
207 | that copy, so the ordinary GNU General Public License applies to all | |
208 | subsequent copies and derivative works made from that copy. | |
209 | ||
210 | This option is useful when you wish to copy part of the code of | |
211 | the Library into a program that is not a library. | |
212 | ||
213 | 4. You may copy and distribute the Library (or a portion or | |
214 | derivative of it, under Section 2) in object code or executable form | |
215 | under the terms of Sections 1 and 2 above provided that you accompany | |
216 | it with the complete corresponding machine-readable source code, which | |
217 | must be distributed under the terms of Sections 1 and 2 above on a | |
218 | medium customarily used for software interchange. | |
219 | ||
220 | If distribution of object code is made by offering access to copy | |
221 | from a designated place, then offering equivalent access to copy the | |
222 | source code from the same place satisfies the requirement to | |
223 | distribute the source code, even though third parties are not | |
224 | compelled to copy the source along with the object code. | |
225 | ||
226 | 5. A program that contains no derivative of any portion of the | |
227 | Library, but is designed to work with the Library by being compiled or | |
228 | linked with it, is called a "work that uses the Library". Such a | |
229 | work, in isolation, is not a derivative work of the Library, and | |
230 | therefore falls outside the scope of this License. | |
231 | ||
232 | However, linking a "work that uses the Library" with the Library | |
233 | creates an executable that is a derivative of the Library (because it | |
234 | contains portions of the Library), rather than a "work that uses the | |
235 | library". The executable is therefore covered by this License. | |
236 | Section 6 states terms for distribution of such executables. | |
237 | ||
238 | When a "work that uses the Library" uses material from a header file | |
239 | that is part of the Library, the object code for the work may be a | |
240 | derivative work of the Library even though the source code is not. | |
241 | Whether this is true is especially significant if the work can be | |
242 | linked without the Library, or if the work is itself a library. The | |
243 | threshold for this to be true is not precisely defined by law. | |
244 | ||
245 | If such an object file uses only numerical parameters, data | |
246 | structure layouts and accessors, and small macros and small inline | |
247 | functions (ten lines or less in length), then the use of the object | |
248 | file is unrestricted, regardless of whether it is legally a derivative | |
249 | work. (Executables containing this object code plus portions of the | |
250 | Library will still fall under Section 6.) | |
251 | ||
252 | Otherwise, if the work is a derivative of the Library, you may | |
253 | distribute the object code for the work under the terms of Section 6. | |
254 | Any executables containing that work also fall under Section 6, | |
255 | whether or not they are linked directly with the Library itself. | |
256 | ||
257 | 6. As an exception to the Sections above, you may also compile or | |
258 | link a "work that uses the Library" with the Library to produce a | |
259 | work containing portions of the Library, and distribute that work | |
260 | under terms of your choice, provided that the terms permit | |
261 | modification of the work for the customer's own use and reverse | |
262 | engineering for debugging such modifications. | |
263 | ||
264 | You must give prominent notice with each copy of the work that the | |
265 | Library is used in it and that the Library and its use are covered by | |
266 | this License. You must supply a copy of this License. If the work | |
267 | during execution displays copyright notices, you must include the | |
268 | copyright notice for the Library among them, as well as a reference | |
269 | directing the user to the copy of this License. Also, you must do one | |
270 | of these things: | |
271 | ||
272 | a) Accompany the work with the complete corresponding | |
273 | machine-readable source code for the Library including whatever | |
274 | changes were used in the work (which must be distributed under | |
275 | Sections 1 and 2 above); and, if the work is an executable linked | |
276 | with the Library, with the complete machine-readable "work that | |
277 | uses the Library", as object code and/or source code, so that the | |
278 | user can modify the Library and then relink to produce a modified | |
279 | executable containing the modified Library. (It is understood | |
280 | that the user who changes the contents of definitions files in the | |
281 | Library will not necessarily be able to recompile the application | |
282 | to use the modified definitions.) | |
283 | ||
284 | b) Accompany the work with a written offer, valid for at | |
285 | least three years, to give the same user the materials | |
286 | specified in Subsection 6a, above, for a charge no more | |
287 | than the cost of performing this distribution. | |
288 | ||
289 | c) If distribution of the work is made by offering access to copy | |
290 | from a designated place, offer equivalent access to copy the above | |
291 | specified materials from the same place. | |
292 | ||
293 | d) Verify that the user has already received a copy of these | |
294 | materials or that you have already sent this user a copy. | |
295 | ||
296 | For an executable, the required form of the "work that uses the | |
297 | Library" must include any data and utility programs needed for | |
298 | reproducing the executable from it. However, as a special exception, | |
299 | the source code distributed need not include anything that is normally | |
300 | distributed (in either source or binary form) with the major | |
301 | components (compiler, kernel, and so on) of the operating system on | |
302 | which the executable runs, unless that component itself accompanies | |
303 | the executable. | |
304 | ||
305 | It may happen that this requirement contradicts the license | |
306 | restrictions of other proprietary libraries that do not normally | |
307 | accompany the operating system. Such a contradiction means you cannot | |
308 | use both them and the Library together in an executable that you | |
309 | distribute. | |
310 | ||
311 | 7. You may place library facilities that are a work based on the | |
312 | Library side-by-side in a single library together with other library | |
313 | facilities not covered by this License, and distribute such a combined | |
314 | library, provided that the separate distribution of the work based on | |
315 | the Library and of the other library facilities is otherwise | |
316 | permitted, and provided that you do these two things: | |
317 | ||
318 | a) Accompany the combined library with a copy of the same work | |
319 | based on the Library, uncombined with any other library | |
320 | facilities. This must be distributed under the terms of the | |
321 | Sections above. | |
322 | ||
323 | b) Give prominent notice with the combined library of the fact | |
324 | that part of it is a work based on the Library, and explaining | |
325 | where to find the accompanying uncombined form of the same work. | |
326 | ||
327 | 8. You may not copy, modify, sublicense, link with, or distribute | |
328 | the Library except as expressly provided under this License. Any | |
329 | attempt otherwise to copy, modify, sublicense, link with, or | |
330 | distribute the Library is void, and will automatically terminate your | |
331 | rights under this License. However, parties who have received copies, | |
332 | or rights, from you under this License will not have their licenses | |
333 | terminated so long as such parties remain in full compliance. | |
334 | ||
335 | 9. You are not required to accept this License, since you have not | |
336 | signed it. However, nothing else grants you permission to modify or | |
337 | distribute the Library or its derivative works. These actions are | |
338 | prohibited by law if you do not accept this License. Therefore, by | |
339 | modifying or distributing the Library (or any work based on the | |
340 | Library), you indicate your acceptance of this License to do so, and | |
341 | all its terms and conditions for copying, distributing or modifying | |
342 | the Library or works based on it. | |
343 | ||
344 | 10. Each time you redistribute the Library (or any work based on the | |
345 | Library), the recipient automatically receives a license from the | |
346 | original licensor to copy, distribute, link with or modify the Library | |
347 | subject to these terms and conditions. You may not impose any further | |
348 | restrictions on the recipients' exercise of the rights granted herein. | |
349 | You are not responsible for enforcing compliance by third parties to | |
350 | this License. | |
351 | ||
352 | 11. If, as a consequence of a court judgment or allegation of patent | |
353 | infringement or for any other reason (not limited to patent issues), | |
354 | conditions are imposed on you (whether by court order, agreement or | |
355 | otherwise) that contradict the conditions of this License, they do not | |
356 | excuse you from the conditions of this License. If you cannot | |
357 | distribute so as to satisfy simultaneously your obligations under this | |
358 | License and any other pertinent obligations, then as a consequence you | |
359 | may not distribute the Library at all. For example, if a patent | |
360 | license would not permit royalty-free redistribution of the Library by | |
361 | all those who receive copies directly or indirectly through you, then | |
362 | the only way you could satisfy both it and this License would be to | |
363 | refrain entirely from distribution of the Library. | |
364 | ||
365 | If any portion of this section is held invalid or unenforceable under any | |
366 | particular circumstance, the balance of the section is intended to apply, | |
367 | and the section as a whole is intended to apply in other circumstances. | |
368 | ||
369 | It is not the purpose of this section to induce you to infringe any | |
370 | patents or other property right claims or to contest validity of any | |
371 | such claims; this section has the sole purpose of protecting the | |
372 | integrity of the free software distribution system which is | |
373 | implemented by public license practices. Many people have made | |
374 | generous contributions to the wide range of software distributed | |
375 | through that system in reliance on consistent application of that | |
376 | system; it is up to the author/donor to decide if he or she is willing | |
377 | to distribute software through any other system and a licensee cannot | |
378 | impose that choice. | |
379 | ||
380 | This section is intended to make thoroughly clear what is believed to | |
381 | be a consequence of the rest of this License. | |
382 | ||
383 | 12. If the distribution and/or use of the Library is restricted in | |
384 | certain countries either by patents or by copyrighted interfaces, the | |
385 | original copyright holder who places the Library under this License may add | |
386 | an explicit geographical distribution limitation excluding those countries, | |
387 | so that distribution is permitted only in or among countries not thus | |
388 | excluded. In such case, this License incorporates the limitation as if | |
389 | written in the body of this License. | |
390 | ||
391 | 13. The Free Software Foundation may publish revised and/or new | |
392 | versions of the Library General Public License from time to time. | |
393 | Such new versions will be similar in spirit to the present version, | |
394 | but may differ in detail to address new problems or concerns. | |
395 | ||
396 | Each version is given a distinguishing version number. If the Library | |
397 | specifies a version number of this License which applies to it and | |
398 | "any later version", you have the option of following the terms and | |
399 | conditions either of that version or of any later version published by | |
400 | the Free Software Foundation. If the Library does not specify a | |
401 | license version number, you may choose any version ever published by | |
402 | the Free Software Foundation. | |
403 | ||
404 | 14. If you wish to incorporate parts of the Library into other free | |
405 | programs whose distribution conditions are incompatible with these, | |
406 | write to the author to ask for permission. For software which is | |
407 | copyrighted by the Free Software Foundation, write to the Free | |
408 | Software Foundation; we sometimes make exceptions for this. Our | |
409 | decision will be guided by the two goals of preserving the free status | |
410 | of all derivatives of our free software and of promoting the sharing | |
411 | and reuse of software generally. | |
412 | ||
413 | NO WARRANTY | |
414 | ||
415 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | |
416 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | |
417 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | |
418 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | |
419 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | |
420 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
421 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | |
422 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | |
423 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |
424 | ||
425 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | |
426 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | |
427 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | |
428 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | |
429 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | |
430 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | |
431 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | |
432 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | |
433 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
434 | DAMAGES. | |
435 | ||
436 | END OF TERMS AND CONDITIONS | |
437 | ||
438 | How to Apply These Terms to Your New Libraries | |
439 | ||
440 | If you develop a new library, and you want it to be of the greatest | |
441 | possible use to the public, we recommend making it free software that | |
442 | everyone can redistribute and change. You can do so by permitting | |
443 | redistribution under these terms (or, alternatively, under the terms of the | |
444 | ordinary General Public License). | |
445 | ||
446 | To apply these terms, attach the following notices to the library. It is | |
447 | safest to attach them to the start of each source file to most effectively | |
448 | convey the exclusion of warranty; and each file should have at least the | |
449 | "copyright" line and a pointer to where the full notice is found. | |
450 | ||
451 | <one line to give the library's name and a brief idea of what it does.> | |
452 | Copyright (C) <year> <name of author> | |
453 | ||
454 | This library is free software; you can redistribute it and/or | |
455 | modify it under the terms of the GNU Library General Public | |
456 | License as published by the Free Software Foundation; either | |
457 | version 2 of the License, or (at your option) any later version. | |
458 | ||
459 | This library is distributed in the hope that it will be useful, | |
460 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
461 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
462 | Library General Public License for more details. | |
463 | ||
464 | You should have received a copy of the GNU Library General Public | |
465 | License along with this library; if not, write to the Free Software | |
466 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
467 | ||
468 | Also add information on how to contact you by electronic and paper mail. | |
469 | ||
470 | You should also get your employer (if you work as a programmer) or your | |
471 | school, if any, to sign a "copyright disclaimer" for the library, if | |
472 | necessary. Here is a sample; alter the names: | |
473 | ||
474 | Yoyodyne, Inc., hereby disclaims all copyright interest in the | |
475 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. | |
476 | ||
477 | <signature of Ty Coon>, 1 April 1990 | |
478 | Ty Coon, President of Vice | |
479 | ||
480 | That's all there is to it! |
0 | 121 | |
1 | ||
2 | * Add README.md (Rudra Sadhu), | |
3 | * Flake8 fixes (Rahul Bothra), | |
4 | * Update metadata (Rahul Bothra), | |
5 | * Add metedata, license copies and screenshot (Vipul Gupta), | |
6 | * Port from GObject to GLib (James Cameron), | |
7 | * Move PyGObject API version check to main program (James Cameron), | |
8 | * Port to TelepathyGLib (James Cameron), | |
9 | * Remove some log warnings and errors (James Cameron), | |
10 | * Port to sugar3.speech (James Cameron). | |
11 | ||
12 | 120 | |
13 | ||
14 | * Fix use of Gst.Message.structure (James Cameron), | |
15 | * Port from GConf (James Cameron), | |
16 | * New translations (Chris Leonard et al). | |
17 | ||
0 | 18 | 119 |
1 | 19 | |
2 | 20 | * New translations (Chris Leonard, et al) |
0 | What is this? | |
1 | ============= | |
2 | ||
3 | Read activity allows the laptop to act as a book reader. It has a simple interface, and will view many kinds of text and image-based book-like materials. | |
4 | ||
5 | How to use? | |
6 | =========== | |
7 | ||
8 | Read is part of the Sugar desktop. Please refer to; | |
9 | ||
10 | * [How to Get Sugar on sugarlabs.org](https://sugarlabs.org/), | |
11 | * [How to use Sugar](https://help.sugarlabs.org/), | |
12 | * [How to use Read](https://help.sugarlabs.org/read.html) |
2 | 2 | bundle_id = org.laptop.sugar.ReadActivity |
3 | 3 | icon = activity-read |
4 | 4 | exec = sugar-activity readactivity.ReadActivity |
5 | activity_version = 119 | |
5 | activity_version = 121 | |
6 | 6 | mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff;text/plain;application/zip;application/x-cbz |
7 | license = GPLv2+ | |
7 | max_participants = 10 | |
8 | license = GPLv2+;LGPLv2+ | |
8 | 9 | summary = Use this activity when you are ready to read! Remember to flip your computer around to feel like you are really holding a book! |
9 | categories = language documents media system | |
10 | repository = https://github.com/sugarlabs/read-activity.git | |
11 | max_participants = 10 | |
10 | tags = Language;Documents;Media;System | |
11 | url = https://help.sugarlabs.org/en/read.html | |
12 | repository = https://github.com/sugarlabs/read-activity |
0 | import logging | |
1 | ||
2 | import gi | |
3 | gi.require_version('Gtk', '3.0') | |
4 | ||
5 | 0 | from gi.repository import Gtk |
6 | 1 | |
7 | 2 | from sugar3.graphics import style |
19 | 19 | import cairo |
20 | 20 | import math |
21 | 21 | |
22 | from gi.repository import GLib | |
22 | 23 | from gi.repository import Gtk |
23 | 24 | from gi.repository import Gdk |
24 | 25 | from gi.repository import GdkPixbuf |
91 | 92 | __gproperties__ = { |
92 | 93 | "hscroll-policy": (Gtk.ScrollablePolicy, "hscroll-policy", |
93 | 94 | "hscroll-policy", Gtk.ScrollablePolicy.MINIMUM, |
94 | GObject.PARAM_READWRITE), | |
95 | GObject.ParamFlags.READWRITE), | |
95 | 96 | "hadjustment": (Gtk.Adjustment, "hadjustment", "hadjustment", |
96 | GObject.PARAM_READWRITE), | |
97 | GObject.ParamFlags.READWRITE), | |
97 | 98 | "vscroll-policy": (Gtk.ScrollablePolicy, "hscroll-policy", |
98 | 99 | "hscroll-policy", Gtk.ScrollablePolicy.MINIMUM, |
99 | GObject.PARAM_READWRITE), | |
100 | GObject.ParamFlags.READWRITE), | |
100 | 101 | "vadjustment": (Gtk.Adjustment, "hadjustment", "hadjustment", |
101 | GObject.PARAM_READWRITE), | |
102 | GObject.ParamFlags.READWRITE), | |
102 | 103 | } |
103 | 104 | |
104 | 105 | __gsignals__ = { |
221 | 222 | # be set to False. This is to perform a faster drawing while |
222 | 223 | # scrolling. |
223 | 224 | if self._scrolling_hid is not None: |
224 | GObject.source_remove(self._scrolling_hid) | |
225 | self._scrolling_hid = GObject.timeout_add(200, | |
226 | self._stop_scrolling) | |
225 | GLib.source_remove(self._scrolling_hid) | |
226 | self._scrolling_hid = GLib.timeout_add(200, | |
227 | self._stop_scrolling) | |
227 | 228 | |
228 | 229 | def __hadj_value_changed_cb(self, adj): |
229 | 230 | alloc = self.get_allocation() |
10 | 10 | # Lesser General Public License for more details. |
11 | 11 | # |
12 | 12 | # You should have received a copy of the GNU Lesser General Public |
13 | # License along with this library; if not, write to the | |
14 | # Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
15 | # Boston, MA 02111-1307, USA. | |
13 | # License along with this library; if not, write to the Free Software | |
14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | # MA 02110-1301 USA | |
16 | 16 | |
17 | 17 | from gi.repository import Gtk |
18 | 18 | from gi.repository import Gdk |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: PACKAGE VERSION\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2017-08-12 13:27+1000\n" | |
10 | "POT-Creation-Date: 2018-04-02 15:53+1000\n" | |
11 | 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
12 | 12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
13 | 13 | "Language-Team: LANGUAGE <LL@li.org>\n" |
214 | 214 | msgid "Hide Tray" |
215 | 215 | msgstr "" |
216 | 216 | |
217 | #: speechtoolbar.py:57 | |
217 | #: speechtoolbar.py:55 | |
218 | 218 | msgid "Play / Pause" |
219 | 219 | msgstr "" |
220 | 220 | |
221 | #: speechtoolbar.py:65 | |
221 | #: speechtoolbar.py:63 | |
222 | 222 | msgid "Stop" |
223 | 223 | msgstr "" |
14 | 14 | "Project-Id-Version: PACKAGE VERSION\n" |
15 | 15 | "Report-Msgid-Bugs-To: \n" |
16 | 16 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" |
17 | "PO-Revision-Date: 2013-09-07 14:30+0200\n" | |
18 | "Last-Translator: Aputsiaq Niels <aj@isit.gl>\n" | |
17 | "PO-Revision-Date: 2017-11-03 09:26+0000\n" | |
18 | "Last-Translator: dark159123 <r.j.hansen@protonmail.com>\n" | |
19 | 19 | "Language-Team: LANGUAGE <LL@li.org>\n" |
20 | 20 | "Language: da\n" |
21 | 21 | "MIME-Version: 1.0\n" |
22 | 22 | "Content-Type: text/plain; charset=UTF-8\n" |
23 | 23 | "Content-Transfer-Encoding: 8bit\n" |
24 | 24 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" |
25 | "X-Generator: Pootle 2.0.5\n" | |
25 | "X-Generator: Pootle 2.5.1.1\n" | |
26 | "X-POOTLE-MTIME: 1509701219.000000\n" | |
26 | 27 | |
27 | 28 | #: activity/activity.info:2 |
28 | 29 | msgid "Read" |
50 | 51 | |
51 | 52 | #: readdialog.py:147 |
52 | 53 | msgid "<b>Author</b>:" |
53 | msgstr "" | |
54 | msgstr "<b>Forfatter</b>:" | |
54 | 55 | |
55 | 56 | #: readdialog.py:163 |
56 | 57 | msgid "<b>Details</b>:" |
127 | 128 | #: readactivity.py:1032 readactivity.py:1227 |
128 | 129 | #, python-format |
129 | 130 | msgid "%s (Page %d)" |
130 | msgstr "" | |
131 | msgstr "%s (Side %d)" | |
131 | 132 | |
132 | 133 | #: readtoolbar.py:61 |
133 | 134 | msgid "Previous" |
226 | 227 | |
227 | 228 | #: comicadapter.py:69 |
228 | 229 | msgid "Can not read Comic Book Archive" |
229 | msgstr "" | |
230 | msgstr "Kan ikke læse Tegneserie Arkivet" | |
230 | 231 | |
231 | 232 | #: comicadapter.py:70 |
232 | 233 | msgid "No readable images were found" |
233 | msgstr "" | |
234 | msgstr "Ingen læsbare billeder blev fundet" | |
234 | 235 | |
235 | 236 | #, python-format |
236 | 237 | #~ msgid "Page %d" |
0 | # SOME DESCRIPTIVE TITLE. | |
1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |
2 | # This file is distributed under the same license as the PACKAGE package. | |
3 | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |
4 | msgid "" | |
5 | msgstr "" | |
6 | "Project-Id-Version: PACKAGE VERSION\n" | |
7 | "Report-Msgid-Bugs-To: \n" | |
8 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" | |
9 | "PO-Revision-Date: 2017-12-15 18:48+0000\n" | |
10 | "Last-Translator: Berend <berendy@gmail.com>\n" | |
11 | "Language-Team: LANGUAGE <LL@li.org>\n" | |
12 | "Language: fy\n" | |
13 | "MIME-Version: 1.0\n" | |
14 | "Content-Type: text/plain; charset=UTF-8\n" | |
15 | "Content-Transfer-Encoding: 8bit\n" | |
16 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | |
17 | "X-Generator: Pootle 2.5.1.1\n" | |
18 | "X-POOTLE-MTIME: 1513363716.000000\n" | |
19 | ||
20 | #: activity/activity.info:2 | |
21 | msgid "Read" | |
22 | msgstr "Lêze" | |
23 | ||
24 | #: activity/activity.info:3 | |
25 | msgid "" | |
26 | "Use this activity when you are ready to read! Remember to flip your computer " | |
27 | "around to feel like you are really holding a book!" | |
28 | msgstr "" | |
29 | "Brûk dizze aktiviteit ast klear bist om te lêzen! Tink derom om dyn " | |
30 | "kompjûter sa no en dan om te slaan om it gefoel fan it fêsthâlden fan in " | |
31 | "echt boek te behâlden!" | |
32 | ||
33 | #: readdialog.py:52 | |
34 | msgid "Cancel" | |
35 | msgstr "Ofbrekke" | |
36 | ||
37 | #: readdialog.py:58 | |
38 | msgid "Ok" | |
39 | msgstr "Okee" | |
40 | ||
41 | #: readdialog.py:120 | |
42 | msgid "<b>Title</b>:" | |
43 | msgstr "<b>Titel</b>:" | |
44 | ||
45 | #: readdialog.py:147 | |
46 | msgid "<b>Author</b>:" | |
47 | msgstr "<b>Skriuwer</b>:" | |
48 | ||
49 | #: readdialog.py:163 | |
50 | msgid "<b>Details</b>:" | |
51 | msgstr "<b>Details</b>:" | |
52 | ||
53 | #: evinceadapter.py:92 | |
54 | msgid "URL from Read" | |
55 | msgstr "URL-adres fan lêzen" | |
56 | ||
57 | #: speechtoolbar.py:57 | |
58 | msgid "Play / Pause" | |
59 | msgstr "Ofspylje / Skoftsje" | |
60 | ||
61 | #: speechtoolbar.py:65 | |
62 | msgid "Stop" | |
63 | msgstr "Ophâlde" | |
64 | ||
65 | #: readactivity.py:379 | |
66 | msgid "Please wait" | |
67 | msgstr "In amerijke" | |
68 | ||
69 | #: readactivity.py:380 | |
70 | msgid "Starting connection..." | |
71 | msgstr "Ferbining opbouwe..." | |
72 | ||
73 | #: readactivity.py:388 | |
74 | msgid "No book" | |
75 | msgstr "Gjin boek" | |
76 | ||
77 | #: readactivity.py:388 | |
78 | msgid "Choose something to read" | |
79 | msgstr "Kies wat om te lêzen" | |
80 | ||
81 | #: readactivity.py:393 | |
82 | msgid "Back" | |
83 | msgstr "Tebek" | |
84 | ||
85 | #: readactivity.py:397 | |
86 | msgid "Previous page" | |
87 | msgstr "Foarige side" | |
88 | ||
89 | #: readactivity.py:399 | |
90 | msgid "Previous bookmark" | |
91 | msgstr "Foarige blêdwizer" | |
92 | ||
93 | #: readactivity.py:411 | |
94 | msgid "Forward" | |
95 | msgstr "Folgjende" | |
96 | ||
97 | #: readactivity.py:415 | |
98 | msgid "Next page" | |
99 | msgstr "Folgjende side" | |
100 | ||
101 | #: readactivity.py:417 | |
102 | msgid "Next bookmark" | |
103 | msgstr "Folgjende blêdwizer" | |
104 | ||
105 | #: readactivity.py:468 | |
106 | msgid "Index" | |
107 | msgstr "Yndeks" | |
108 | ||
109 | #: readactivity.py:564 | |
110 | msgid "Delete bookmark" | |
111 | msgstr "Blêdwizer wiskje" | |
112 | ||
113 | #: readactivity.py:565 | |
114 | msgid "All the information related with this bookmark will be lost" | |
115 | msgstr "Alle ynformaasje by dizze blêdwizer sil ferlern gean" | |
116 | ||
117 | #: readactivity.py:954 | |
118 | msgid "Receiving book..." | |
119 | msgstr "Untfang boek..." | |
120 | ||
121 | #: readactivity.py:1032 readactivity.py:1227 | |
122 | #, python-format | |
123 | msgid "%s (Page %d)" | |
124 | msgstr "%s (Side %d)" | |
125 | ||
126 | #: readtoolbar.py:61 | |
127 | msgid "Previous" | |
128 | msgstr "Foarige" | |
129 | ||
130 | #: readtoolbar.py:68 | |
131 | msgid "Next" | |
132 | msgstr "Folgjende" | |
133 | ||
134 | #: readtoolbar.py:79 | |
135 | msgid "Highlight" | |
136 | msgstr "Markearje" | |
137 | ||
138 | #: readtoolbar.py:160 | |
139 | msgid "Find first" | |
140 | msgstr "Folgjende side" | |
141 | ||
142 | #: readtoolbar.py:166 | |
143 | msgid "Find previous" | |
144 | msgstr "Foarige sykje" | |
145 | ||
146 | #: readtoolbar.py:168 | |
147 | msgid "Find next" | |
148 | msgstr "Folgjende sykje" | |
149 | ||
150 | #: readtoolbar.py:188 | |
151 | msgid "Table of contents" | |
152 | msgstr "Ynhâldsopjefte" | |
153 | ||
154 | #: readtoolbar.py:197 | |
155 | msgid "Zoom out" | |
156 | msgstr "Zoom út" | |
157 | ||
158 | #: readtoolbar.py:203 | |
159 | msgid "Zoom in" | |
160 | msgstr "Zoom yn" | |
161 | ||
162 | #: readtoolbar.py:209 | |
163 | msgid "Zoom to width" | |
164 | msgstr "Zoom neffens breedte" | |
165 | ||
166 | #: readtoolbar.py:215 | |
167 | msgid "Zoom to fit" | |
168 | msgstr "Zoom nei hiele side" | |
169 | ||
170 | #: readtoolbar.py:221 | |
171 | msgid "Actual size" | |
172 | msgstr "Wiere grutte" | |
173 | ||
174 | #: readtoolbar.py:232 | |
175 | msgid "Fullscreen" | |
176 | msgstr "Folslein skerm" | |
177 | ||
178 | #: readtoolbar.py:252 | |
179 | msgid "Rotate left" | |
180 | msgstr "Links rotearje" | |
181 | ||
182 | #: readtoolbar.py:258 | |
183 | msgid "Rotate right" | |
184 | msgstr "Rjochts rotearje" | |
185 | ||
186 | #: readtoolbar.py:324 | |
187 | msgid "Show Tray" | |
188 | msgstr "Plato sjen litte" | |
189 | ||
190 | #: readtoolbar.py:326 | |
191 | msgid "Hide Tray" | |
192 | msgstr "Plato ferbergje" | |
193 | ||
194 | #: linkbutton.py:133 | |
195 | msgid "Go to Bookmark" | |
196 | msgstr "Gean nei blêdwizer" | |
197 | ||
198 | #: linkbutton.py:139 | |
199 | msgid "Remove" | |
200 | msgstr "Wiskje" | |
201 | ||
202 | #: bookmarkview.py:107 | |
203 | #, python-format | |
204 | msgid "Bookmark added by %(user)s %(time)s" | |
205 | msgstr "Blêdwizer taheakke troch %(user)s %(time)s" | |
206 | ||
207 | #: bookmarkview.py:143 bookmarkview.py:192 | |
208 | msgid "Add notes for bookmark: " | |
209 | msgstr "Notysjes foar blêdwizer taheakje: " | |
210 | ||
211 | #: bookmarkview.py:188 | |
212 | #, python-format | |
213 | msgid "%s's bookmark" | |
214 | msgstr "%s's blêdwizer" | |
215 | ||
216 | #: bookmarkview.py:189 | |
217 | #, python-format | |
218 | msgid "Bookmark for page %d" | |
219 | msgstr "Blêdwizer foar side %d" | |
220 | ||
221 | #: comicadapter.py:69 | |
222 | msgid "Can not read Comic Book Archive" | |
223 | msgstr "Kin it stripboek argyf net lêze" | |
224 | ||
225 | #: comicadapter.py:70 | |
226 | msgid "No readable images were found" | |
227 | msgstr "Gjin te lêzen ôfbyldings binne fûn" |
6 | 6 | "Project-Id-Version: PACKAGE VERSION\n" |
7 | 7 | "Report-Msgid-Bugs-To: \n" |
8 | 8 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" |
9 | "PO-Revision-Date: 2017-06-10 11:29+0000\n" | |
10 | "Last-Translator: belkacem77 <belkacem77@gmail.com>\n" | |
9 | "PO-Revision-Date: 2017-12-06 14:53+0000\n" | |
10 | "Last-Translator: hociben <hociben@gmail.com>\n" | |
11 | 11 | "Language-Team: LANGUAGE <LL@li.org>\n" |
12 | 12 | "Language: kab\n" |
13 | 13 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Transfer-Encoding: 8bit\n" |
16 | 16 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" |
17 | 17 | "X-Generator: Pootle 2.5.1.1\n" |
18 | "X-POOTLE-MTIME: 1497094194.000000\n" | |
18 | "X-POOTLE-MTIME: 1512572038.000000\n" | |
19 | 19 | |
20 | 20 | #: activity/activity.info:2 |
21 | 21 | msgid "Read" |
87 | 87 | |
88 | 88 | #: readactivity.py:399 |
89 | 89 | msgid "Previous bookmark" |
90 | msgstr "" | |
90 | msgstr "Ticreḍt n deffir" | |
91 | 91 | |
92 | 92 | #: readactivity.py:411 |
93 | 93 | msgid "Forward" |
94 | msgstr "" | |
94 | msgstr "Ɣer zdat" | |
95 | 95 | |
96 | 96 | #: readactivity.py:415 |
97 | 97 | msgid "Next page" |
98 | msgstr "" | |
98 | msgstr "Asebter ɣer zdat" | |
99 | 99 | |
100 | 100 | #: readactivity.py:417 |
101 | 101 | msgid "Next bookmark" |
102 | msgstr "" | |
102 | msgstr "Ticreḍt n zdat" | |
103 | 103 | |
104 | 104 | #: readactivity.py:468 |
105 | 105 | msgid "Index" |
107 | 107 | |
108 | 108 | #: readactivity.py:564 |
109 | 109 | msgid "Delete bookmark" |
110 | msgstr "" | |
110 | msgstr "Kkes ticreḍt" | |
111 | 111 | |
112 | 112 | #: readactivity.py:565 |
113 | 113 | msgid "All the information related with this bookmark will be lost" |
114 | msgstr "" | |
114 | msgstr "Talɣut akk yerzan ticreḍt-agi ad truḥ" | |
115 | 115 | |
116 | 116 | #: readactivity.py:954 |
117 | 117 | msgid "Receiving book..." |
120 | 120 | #: readactivity.py:1032 readactivity.py:1227 |
121 | 121 | #, python-format |
122 | 122 | msgid "%s (Page %d)" |
123 | msgstr "" | |
123 | msgstr "%s (Asebter %d)" | |
124 | 124 | |
125 | 125 | #: readtoolbar.py:61 |
126 | 126 | msgid "Previous" |
127 | msgstr "" | |
127 | msgstr "Send" | |
128 | 128 | |
129 | 129 | #: readtoolbar.py:68 |
130 | 130 | msgid "Next" |
131 | msgstr "" | |
131 | msgstr "Seld" | |
132 | 132 | |
133 | 133 | #: readtoolbar.py:79 |
134 | 134 | msgid "Highlight" |
135 | msgstr "" | |
135 | msgstr "Asebrureq" | |
136 | 136 | |
137 | 137 | #: readtoolbar.py:160 |
138 | 138 | msgid "Find first" |
139 | msgstr "" | |
139 | msgstr "Af-d d amezwaru" | |
140 | 140 | |
141 | 141 | #: readtoolbar.py:166 |
142 | 142 | msgid "Find previous" |
148 | 148 | |
149 | 149 | #: readtoolbar.py:188 |
150 | 150 | msgid "Table of contents" |
151 | msgstr "" | |
151 | msgstr "Agbur" | |
152 | 152 | |
153 | 153 | #: readtoolbar.py:197 |
154 | 154 | msgid "Zoom out" |
192 | 192 | |
193 | 193 | #: linkbutton.py:133 |
194 | 194 | msgid "Go to Bookmark" |
195 | msgstr "" | |
195 | msgstr "Ddu ɣer ticreḍt " | |
196 | 196 | |
197 | 197 | #: linkbutton.py:139 |
198 | 198 | msgid "Remove" |
199 | msgstr "" | |
199 | msgstr "Kkes" | |
200 | 200 | |
201 | 201 | #: bookmarkview.py:107 |
202 | 202 | #, python-format |
14 | 14 | "Project-Id-Version: PACKAGE VERSION\n" |
15 | 15 | "Report-Msgid-Bugs-To: \n" |
16 | 16 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" |
17 | "PO-Revision-Date: 2017-04-17 06:15+0000\n" | |
18 | "Last-Translator: Chris <cjl@sugarlabs.org>\n" | |
17 | "PO-Revision-Date: 2017-10-14 08:59+0000\n" | |
18 | "Last-Translator: yuray <yuray@komyakino.ru>\n" | |
19 | 19 | "Language-Team: LANGUAGE <LL@li.org>\n" |
20 | 20 | "Language: ru\n" |
21 | 21 | "MIME-Version: 1.0\n" |
24 | 24 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " |
25 | 25 | "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" |
26 | 26 | "X-Generator: Pootle 2.5.1.1\n" |
27 | "X-POOTLE-MTIME: 1492409727.000000\n" | |
27 | "X-POOTLE-MTIME: 1507971582.000000\n" | |
28 | 28 | |
29 | 29 | #: activity/activity.info:2 |
30 | 30 | msgid "Read" |
52 | 52 | |
53 | 53 | #: readdialog.py:147 |
54 | 54 | msgid "<b>Author</b>:" |
55 | msgstr "" | |
55 | msgstr "<b>Автор</b>:" | |
56 | 56 | |
57 | 57 | #: readdialog.py:163 |
58 | 58 | msgid "<b>Details</b>:" |
72 | 72 | |
73 | 73 | #: readactivity.py:379 |
74 | 74 | msgid "Please wait" |
75 | msgstr "" | |
75 | msgstr "Подождите" | |
76 | 76 | |
77 | 77 | #: readactivity.py:380 |
78 | 78 | msgid "Starting connection..." |
79 | msgstr "" | |
79 | msgstr "Начинается подключение…" | |
80 | 80 | |
81 | 81 | #: readactivity.py:388 |
82 | 82 | msgid "No book" |
124 | 124 | |
125 | 125 | #: readactivity.py:954 |
126 | 126 | msgid "Receiving book..." |
127 | msgstr "" | |
127 | msgstr "Получение книги…" | |
128 | 128 | |
129 | 129 | #: readactivity.py:1032 readactivity.py:1227 |
130 | 130 | #, python-format |
131 | 131 | msgid "%s (Page %d)" |
132 | msgstr "" | |
132 | msgstr "%s (страница %d)" | |
133 | 133 | |
134 | 134 | #: readtoolbar.py:61 |
135 | 135 | msgid "Previous" |
185 | 185 | |
186 | 186 | #: readtoolbar.py:252 |
187 | 187 | msgid "Rotate left" |
188 | msgstr "" | |
188 | msgstr "Повернуть влево" | |
189 | 189 | |
190 | 190 | #: readtoolbar.py:258 |
191 | 191 | msgid "Rotate right" |
192 | msgstr "" | |
192 | msgstr "Повернуть вправо" | |
193 | 193 | |
194 | 194 | #: readtoolbar.py:324 |
195 | 195 | msgid "Show Tray" |
228 | 228 | |
229 | 229 | #: comicadapter.py:69 |
230 | 230 | msgid "Can not read Comic Book Archive" |
231 | msgstr "" | |
231 | msgstr "Невозможно прочитать архив книги комиксов" | |
232 | 232 | |
233 | 233 | #: comicadapter.py:70 |
234 | 234 | msgid "No readable images were found" |
235 | msgstr "" | |
235 | msgstr "Доступные для чтения изображения не найдены" | |
236 | 236 | |
237 | 237 | #, python-format |
238 | 238 | #~ msgid "Page %d" |
14 | 14 | "Project-Id-Version: PACKAGE VERSION\n" |
15 | 15 | "Report-Msgid-Bugs-To: \n" |
16 | 16 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" |
17 | "PO-Revision-Date: 2009-08-25 15:36-0400\n" | |
18 | "Last-Translator: Chris Leonard <cjl@laptop.org>\n" | |
17 | "PO-Revision-Date: 2017-11-03 12:45+0000\n" | |
18 | "Last-Translator: jana <jpastyrova@gmail.com>\n" | |
19 | 19 | "Language-Team: LANGUAGE <LL@li.org>\n" |
20 | 20 | "Language: sk\n" |
21 | 21 | "MIME-Version: 1.0\n" |
22 | 22 | "Content-Type: text/plain; charset=UTF-8\n" |
23 | 23 | "Content-Transfer-Encoding: 8bit\n" |
24 | "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2 ;\n" | |
25 | "X-Generator: Pootle 1.2.1\n" | |
24 | "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" | |
25 | "X-Generator: Pootle 2.5.1.1\n" | |
26 | "X-POOTLE-MTIME: 1509713154.000000\n" | |
26 | 27 | |
27 | 28 | #: activity/activity.info:2 |
28 | 29 | msgid "Read" |
33 | 34 | "Use this activity when you are ready to read! Remember to flip your computer " |
34 | 35 | "around to feel like you are really holding a book!" |
35 | 36 | msgstr "" |
37 | "Táto činnosť je určená na čítanie! Nezabudnite obrátiť počítač tak aby sme " | |
38 | "mali pocit, že držíte skutočnú knihu. " | |
36 | 39 | |
37 | 40 | #: readdialog.py:52 |
38 | 41 | msgid "Cancel" |
44 | 47 | |
45 | 48 | #: readdialog.py:120 |
46 | 49 | msgid "<b>Title</b>:" |
47 | msgstr "" | |
50 | msgstr "<b>Názov</b>:" | |
48 | 51 | |
49 | 52 | #: readdialog.py:147 |
50 | 53 | msgid "<b>Author</b>:" |
51 | msgstr "" | |
54 | msgstr "<b>Autor</b>:" | |
52 | 55 | |
53 | 56 | #: readdialog.py:163 |
54 | 57 | msgid "<b>Details</b>:" |
55 | msgstr "" | |
58 | msgstr "<b>Detaily</b>:" | |
56 | 59 | |
57 | 60 | #: evinceadapter.py:92 |
58 | 61 | msgid "URL from Read" |
59 | msgstr "" | |
62 | msgstr "URL z Read" | |
60 | 63 | |
61 | 64 | #: speechtoolbar.py:57 |
62 | 65 | msgid "Play / Pause" |
63 | msgstr "" | |
66 | msgstr "Prehrať/Zastaviť" | |
64 | 67 | |
65 | 68 | #: speechtoolbar.py:65 |
66 | 69 | msgid "Stop" |
67 | msgstr "" | |
70 | msgstr "Zastaviť" | |
68 | 71 | |
69 | 72 | #: readactivity.py:379 |
70 | 73 | msgid "Please wait" |
71 | msgstr "" | |
74 | msgstr "Prosím čakajte" | |
72 | 75 | |
73 | 76 | #: readactivity.py:380 |
74 | 77 | msgid "Starting connection..." |
75 | msgstr "" | |
78 | msgstr "Pripájanie......" | |
76 | 79 | |
77 | 80 | #: readactivity.py:388 |
78 | 81 | msgid "No book" |
79 | msgstr "" | |
82 | msgstr "Žiadna kniha" | |
80 | 83 | |
81 | 84 | #: readactivity.py:388 |
82 | 85 | msgid "Choose something to read" |
83 | msgstr "" | |
86 | msgstr "Vyberte si niečo na čítanie" | |
84 | 87 | |
85 | 88 | #: readactivity.py:393 |
86 | 89 | msgid "Back" |
87 | 90 | msgstr "Späť" |
88 | 91 | |
89 | 92 | #: readactivity.py:397 |
90 | #, fuzzy | |
91 | 93 | msgid "Previous page" |
92 | msgstr "Predchádzajúci strana" | |
94 | msgstr "Predchádzajúca strana" | |
93 | 95 | |
94 | 96 | #: readactivity.py:399 |
95 | 97 | msgid "Previous bookmark" |
96 | msgstr "" | |
98 | msgstr "Predchádzajúca záložka" | |
97 | 99 | |
98 | 100 | #: readactivity.py:411 |
99 | 101 | msgid "Forward" |
100 | 102 | msgstr "Dopredu" |
101 | 103 | |
102 | 104 | #: readactivity.py:415 |
103 | #, fuzzy | |
104 | 105 | msgid "Next page" |
105 | msgstr "Ďaľší strana" | |
106 | msgstr "Ďalšia strana" | |
106 | 107 | |
107 | 108 | #: readactivity.py:417 |
108 | 109 | msgid "Next bookmark" |
109 | msgstr "" | |
110 | msgstr "Ďalšia záložka" | |
110 | 111 | |
111 | 112 | #: readactivity.py:468 |
112 | 113 | msgid "Index" |
113 | msgstr "" | |
114 | msgstr "Zoznam" | |
114 | 115 | |
115 | 116 | #: readactivity.py:564 |
116 | 117 | msgid "Delete bookmark" |
117 | msgstr "" | |
118 | msgstr "Vymazať záložky" | |
118 | 119 | |
119 | 120 | #: readactivity.py:565 |
120 | 121 | msgid "All the information related with this bookmark will be lost" |
121 | msgstr "" | |
122 | msgstr "Všetky informácie súvisiace s touto záložkou budú vymazané" | |
122 | 123 | |
123 | 124 | #: readactivity.py:954 |
124 | 125 | msgid "Receiving book..." |
125 | msgstr "" | |
126 | msgstr "Prijímanie knihy...." | |
126 | 127 | |
127 | 128 | #: readactivity.py:1032 readactivity.py:1227 |
128 | 129 | #, python-format |
129 | 130 | msgid "%s (Page %d)" |
130 | msgstr "" | |
131 | msgstr "%s (Strana %d)" | |
131 | 132 | |
132 | 133 | #: readtoolbar.py:61 |
133 | 134 | msgid "Previous" |
139 | 140 | |
140 | 141 | #: readtoolbar.py:79 |
141 | 142 | msgid "Highlight" |
142 | msgstr "" | |
143 | msgstr "Zvýraznenie" | |
143 | 144 | |
144 | 145 | #: readtoolbar.py:160 |
145 | 146 | msgid "Find first" |
150 | 151 | msgstr "Nájsť predchádzajúci" |
151 | 152 | |
152 | 153 | #: readtoolbar.py:168 |
153 | #, fuzzy | |
154 | 154 | msgid "Find next" |
155 | 155 | msgstr "Hľadať ďalej" |
156 | 156 | |
157 | 157 | #: readtoolbar.py:188 |
158 | 158 | msgid "Table of contents" |
159 | msgstr "" | |
159 | msgstr "Zoznam obsahov" | |
160 | 160 | |
161 | 161 | #: readtoolbar.py:197 |
162 | 162 | msgid "Zoom out" |
168 | 168 | |
169 | 169 | #: readtoolbar.py:209 |
170 | 170 | msgid "Zoom to width" |
171 | msgstr "" | |
171 | msgstr "Priblíženie na šírku" | |
172 | 172 | |
173 | 173 | #: readtoolbar.py:215 |
174 | 174 | msgid "Zoom to fit" |
175 | msgstr "" | |
175 | msgstr "Priblíženie na presnosť" | |
176 | 176 | |
177 | 177 | #: readtoolbar.py:221 |
178 | 178 | msgid "Actual size" |
180 | 180 | |
181 | 181 | #: readtoolbar.py:232 |
182 | 182 | msgid "Fullscreen" |
183 | msgstr "" | |
183 | msgstr "Plná obrazovka" | |
184 | 184 | |
185 | 185 | #: readtoolbar.py:252 |
186 | 186 | msgid "Rotate left" |
187 | msgstr "" | |
187 | msgstr "Otočiť doľava" | |
188 | 188 | |
189 | 189 | #: readtoolbar.py:258 |
190 | 190 | msgid "Rotate right" |
191 | msgstr "" | |
191 | msgstr "Otočiť doprava" | |
192 | 192 | |
193 | 193 | #: readtoolbar.py:324 |
194 | 194 | msgid "Show Tray" |
195 | msgstr "" | |
195 | msgstr "Ukázať lištu" | |
196 | 196 | |
197 | 197 | #: readtoolbar.py:326 |
198 | 198 | msgid "Hide Tray" |
199 | msgstr "" | |
199 | msgstr "Skryť lištu" | |
200 | 200 | |
201 | 201 | #: linkbutton.py:133 |
202 | 202 | msgid "Go to Bookmark" |
203 | msgstr "" | |
203 | msgstr "Choďte na záložku" | |
204 | 204 | |
205 | 205 | #: linkbutton.py:139 |
206 | 206 | msgid "Remove" |
207 | msgstr "" | |
207 | msgstr "Odstrániť" | |
208 | 208 | |
209 | 209 | #: bookmarkview.py:107 |
210 | 210 | #, python-format |
211 | 211 | msgid "Bookmark added by %(user)s %(time)s" |
212 | msgstr "" | |
212 | msgstr "Pridaná záložka od %(užívateľ)ia %(čas)" | |
213 | 213 | |
214 | 214 | #: bookmarkview.py:143 bookmarkview.py:192 |
215 | 215 | msgid "Add notes for bookmark: " |
216 | msgstr "" | |
216 | msgstr "Pridajte poznámky k záložke" | |
217 | 217 | |
218 | 218 | #: bookmarkview.py:188 |
219 | 219 | #, python-format |
220 | 220 | msgid "%s's bookmark" |
221 | msgstr "" | |
221 | msgstr "%ove/ ina záložka" | |
222 | 222 | |
223 | 223 | #: bookmarkview.py:189 |
224 | 224 | #, python-format |
225 | 225 | msgid "Bookmark for page %d" |
226 | msgstr "" | |
226 | msgstr "Záložka pre stránku %d" | |
227 | 227 | |
228 | 228 | #: comicadapter.py:69 |
229 | 229 | msgid "Can not read Comic Book Archive" |
230 | msgstr "" | |
230 | msgstr "Comic Book Archív je nedostupný" | |
231 | 231 | |
232 | 232 | #: comicadapter.py:70 |
233 | 233 | msgid "No readable images were found" |
234 | msgstr "" | |
234 | msgstr "Boli nájdené nečitateľné položky" | |
235 | 235 | |
236 | 236 | #~ msgid "%" |
237 | 237 | #~ msgstr "%" |
14 | 14 | "Project-Id-Version: PACKAGE VERSION\n" |
15 | 15 | "Report-Msgid-Bugs-To: \n" |
16 | 16 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" |
17 | "PO-Revision-Date: 2011-12-18 22:47+0200\n" | |
18 | "Last-Translator: Chris <cjl@laptop.org>\n" | |
17 | "PO-Revision-Date: 2017-12-03 16:41+0000\n" | |
18 | "Last-Translator: Martin <miles@filmsi.net>\n" | |
19 | 19 | "Language-Team: LANGUAGE <LL@li.org>\n" |
20 | 20 | "Language: sl\n" |
21 | 21 | "MIME-Version: 1.0\n" |
23 | 23 | "Content-Transfer-Encoding: 8bit\n" |
24 | 24 | "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " |
25 | 25 | "n%100==4 ? 2 : 3);\n" |
26 | "X-Generator: Pootle 2.0.5\n" | |
26 | "X-Generator: Pootle 2.5.1.1\n" | |
27 | "X-POOTLE-MTIME: 1512319274.000000\n" | |
27 | 28 | |
28 | 29 | #: activity/activity.info:2 |
29 | 30 | msgid "Read" |
34 | 35 | "Use this activity when you are ready to read! Remember to flip your computer " |
35 | 36 | "around to feel like you are really holding a book!" |
36 | 37 | msgstr "" |
38 | "Uporabi to dejavnost, ko želiš brati! Ne pozabiti obrniti računalnika " | |
39 | "pokonci, da dobiš občutek, da v roki držiš knjigo!" | |
37 | 40 | |
38 | 41 | #: readdialog.py:52 |
39 | 42 | msgid "Cancel" |
49 | 52 | |
50 | 53 | #: readdialog.py:147 |
51 | 54 | msgid "<b>Author</b>:" |
52 | msgstr "" | |
55 | msgstr "<b>Avtor</b>:" | |
53 | 56 | |
54 | 57 | #: readdialog.py:163 |
55 | 58 | msgid "<b>Details</b>:" |
57 | 60 | |
58 | 61 | #: evinceadapter.py:92 |
59 | 62 | msgid "URL from Read" |
60 | msgstr "" | |
63 | msgstr "URL iz Beri" | |
61 | 64 | |
62 | 65 | #: speechtoolbar.py:57 |
63 | 66 | msgid "Play / Pause" |
65 | 68 | |
66 | 69 | #: speechtoolbar.py:65 |
67 | 70 | msgid "Stop" |
68 | msgstr "" | |
71 | msgstr "Ustavi" | |
69 | 72 | |
70 | 73 | #: readactivity.py:379 |
71 | 74 | msgid "Please wait" |
72 | msgstr "" | |
75 | msgstr "Počakaj malo" | |
73 | 76 | |
74 | 77 | #: readactivity.py:380 |
75 | 78 | msgid "Starting connection..." |
76 | msgstr "" | |
79 | msgstr "Povezava se vzpostavlja ..." | |
77 | 80 | |
78 | 81 | #: readactivity.py:388 |
79 | 82 | msgid "No book" |
80 | msgstr "" | |
83 | msgstr "Ni knjige" | |
81 | 84 | |
82 | 85 | #: readactivity.py:388 |
83 | 86 | msgid "Choose something to read" |
84 | msgstr "" | |
87 | msgstr "Izberi nekaj za branje" | |
85 | 88 | |
86 | 89 | #: readactivity.py:393 |
87 | 90 | msgid "Back" |
109 | 112 | |
110 | 113 | #: readactivity.py:468 |
111 | 114 | msgid "Index" |
112 | msgstr "" | |
115 | msgstr "Kazalo" | |
113 | 116 | |
114 | 117 | #: readactivity.py:564 |
115 | 118 | msgid "Delete bookmark" |
116 | msgstr "" | |
119 | msgstr "Izbriši zaznamek" | |
117 | 120 | |
118 | 121 | #: readactivity.py:565 |
119 | 122 | msgid "All the information related with this bookmark will be lost" |
120 | msgstr "" | |
123 | msgstr "Vsi podatki o tem zaznamku bodo izgubljeni" | |
121 | 124 | |
122 | 125 | #: readactivity.py:954 |
123 | 126 | msgid "Receiving book..." |
124 | msgstr "" | |
127 | msgstr "Prejemanje knjige ..." | |
125 | 128 | |
126 | 129 | #: readactivity.py:1032 readactivity.py:1227 |
127 | 130 | #, python-format |
128 | 131 | msgid "%s (Page %d)" |
129 | msgstr "" | |
132 | msgstr "%s (stran %d)" | |
130 | 133 | |
131 | 134 | #: readtoolbar.py:61 |
132 | 135 | msgid "Previous" |
154 | 157 | |
155 | 158 | #: readtoolbar.py:188 |
156 | 159 | msgid "Table of contents" |
157 | msgstr "" | |
160 | msgstr "Kazalo vsebine" | |
158 | 161 | |
159 | 162 | #: readtoolbar.py:197 |
160 | 163 | msgid "Zoom out" |
182 | 185 | |
183 | 186 | #: readtoolbar.py:252 |
184 | 187 | msgid "Rotate left" |
185 | msgstr "" | |
188 | msgstr "Zasukaj levo" | |
186 | 189 | |
187 | 190 | #: readtoolbar.py:258 |
188 | 191 | msgid "Rotate right" |
189 | msgstr "" | |
192 | msgstr "Zasukaj desno" | |
190 | 193 | |
191 | 194 | #: readtoolbar.py:324 |
192 | 195 | msgid "Show Tray" |
193 | msgstr "" | |
196 | msgstr "Pokaži pladenj" | |
194 | 197 | |
195 | 198 | #: readtoolbar.py:326 |
196 | 199 | msgid "Hide Tray" |
197 | msgstr "" | |
200 | msgstr "Skrij pladenj" | |
198 | 201 | |
199 | 202 | #: linkbutton.py:133 |
200 | 203 | msgid "Go to Bookmark" |
201 | msgstr "" | |
204 | msgstr "Pojdi na zaznamek" | |
202 | 205 | |
203 | 206 | #: linkbutton.py:139 |
204 | 207 | msgid "Remove" |
205 | msgstr "" | |
208 | msgstr "Odstrani" | |
206 | 209 | |
207 | 210 | #: bookmarkview.py:107 |
208 | 211 | #, python-format |
225 | 228 | |
226 | 229 | #: comicadapter.py:69 |
227 | 230 | msgid "Can not read Comic Book Archive" |
228 | msgstr "" | |
231 | msgstr "Ni mogoče brati iz Comic Book Archive" | |
229 | 232 | |
230 | 233 | #: comicadapter.py:70 |
231 | 234 | msgid "No readable images were found" |
232 | msgstr "" | |
235 | msgstr "Nobenih berljivih slik ni mogoče najti" | |
233 | 236 | |
234 | 237 | #, python-format |
235 | 238 | #~ msgid "Page %(current)i of %(total_pages)i" |
14 | 14 | "Project-Id-Version: PACKAGE VERSION\n" |
15 | 15 | "Report-Msgid-Bugs-To: \n" |
16 | 16 | "POT-Creation-Date: 2017-03-24 17:39+1100\n" |
17 | "PO-Revision-Date: 2017-04-17 05:32+0000\n" | |
18 | "Last-Translator: Chris <cjl@sugarlabs.org>\n" | |
17 | "PO-Revision-Date: 2018-02-03 21:13+0000\n" | |
18 | "Last-Translator: Besnik_b <besnik@programeshqip.org>\n" | |
19 | 19 | "Language-Team: LANGUAGE <LL@li.org>\n" |
20 | 20 | "Language: sq\n" |
21 | 21 | "MIME-Version: 1.0\n" |
23 | 23 | "Content-Transfer-Encoding: 8bit\n" |
24 | 24 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" |
25 | 25 | "X-Generator: Pootle 2.5.1.1\n" |
26 | "X-POOTLE-MTIME: 1492407135.000000\n" | |
26 | "X-POOTLE-MTIME: 1517692424.000000\n" | |
27 | 27 | |
28 | 28 | #: activity/activity.info:2 |
29 | 29 | msgid "Read" |
52 | 52 | |
53 | 53 | #: readdialog.py:147 |
54 | 54 | msgid "<b>Author</b>:" |
55 | msgstr "" | |
55 | msgstr "<b>Autor</b>:" | |
56 | 56 | |
57 | 57 | #: readdialog.py:163 |
58 | 58 | msgid "<b>Details</b>:" |
72 | 72 | |
73 | 73 | #: readactivity.py:379 |
74 | 74 | msgid "Please wait" |
75 | msgstr "" | |
75 | msgstr "Ju lutemi, pritni" | |
76 | 76 | |
77 | 77 | #: readactivity.py:380 |
78 | 78 | msgid "Starting connection..." |
79 | msgstr "" | |
79 | msgstr "Po fillohet lidhja…" | |
80 | 80 | |
81 | 81 | #: readactivity.py:388 |
82 | 82 | msgid "No book" |
124 | 124 | |
125 | 125 | #: readactivity.py:954 |
126 | 126 | msgid "Receiving book..." |
127 | msgstr "" | |
127 | msgstr "Po merret libër…" | |
128 | 128 | |
129 | 129 | #: readactivity.py:1032 readactivity.py:1227 |
130 | 130 | #, python-format |
131 | 131 | msgid "%s (Page %d)" |
132 | msgstr "" | |
132 | msgstr "%s (Faqja %d)" | |
133 | 133 | |
134 | 134 | #: readtoolbar.py:61 |
135 | 135 | msgid "Previous" |
157 | 157 | |
158 | 158 | #: readtoolbar.py:188 |
159 | 159 | msgid "Table of contents" |
160 | msgstr "Tabelë e lëndës" | |
160 | msgstr "Pasqyrë e lëndës" | |
161 | 161 | |
162 | 162 | #: readtoolbar.py:197 |
163 | 163 | msgid "Zoom out" |
185 | 185 | |
186 | 186 | #: readtoolbar.py:252 |
187 | 187 | msgid "Rotate left" |
188 | msgstr "" | |
188 | msgstr "Rrotulloje majtas" | |
189 | 189 | |
190 | 190 | #: readtoolbar.py:258 |
191 | 191 | msgid "Rotate right" |
192 | msgstr "" | |
192 | msgstr "Rrotulloje djathtas" | |
193 | 193 | |
194 | 194 | #: readtoolbar.py:324 |
195 | 195 | msgid "Show Tray" |
228 | 228 | |
229 | 229 | #: comicadapter.py:69 |
230 | 230 | msgid "Can not read Comic Book Archive" |
231 | msgstr "" | |
231 | msgstr "S’lexohet dot te Comic Book Archive" | |
232 | 232 | |
233 | 233 | #: comicadapter.py:70 |
234 | 234 | msgid "No readable images were found" |
235 | msgstr "" | |
235 | msgstr "S’u gjetën figura të lexueshme" | |
236 | 236 | |
237 | 237 | #, python-format |
238 | 238 | #~ msgid "Page %d" |
26 | 26 | import cairo |
27 | 27 | import json |
28 | 28 | |
29 | import emptypanel | |
30 | ||
31 | 29 | import dbus |
30 | ||
31 | import gi | |
32 | gi.require_version('Gtk', '3.0') | |
33 | gi.require_version('Gst', '1.0') | |
34 | gi.require_version('TelepathyGLib', '0.12') | |
35 | ||
36 | from gi.repository import GLib | |
32 | 37 | from gi.repository import GObject |
33 | 38 | from gi.repository import Gtk |
34 | 39 | from gi.repository import Gdk |
35 | 40 | from gi.repository import Gio |
41 | from gi.repository import TelepathyGLib | |
36 | 42 | |
37 | 43 | GObject.threads_init() |
38 | ||
39 | import telepathy | |
40 | 44 | |
41 | 45 | from sugar3.activity import activity |
42 | 46 | from sugar3.graphics.toolbutton import ToolButton |
48 | 52 | from sugar3.activity.widgets import ActivityToolbarButton |
49 | 53 | from sugar3.activity.widgets import StopButton |
50 | 54 | from sugar3.graphics.tray import HTray |
55 | from sugar3.graphics.menuitem import MenuItem | |
51 | 56 | from sugar3 import network |
52 | 57 | from sugar3 import mime |
53 | 58 | from sugar3 import profile |
61 | 66 | |
62 | 67 | from sugar3.graphics import style |
63 | 68 | |
69 | import emptypanel | |
64 | 70 | from readtoolbar import EditToolbar |
65 | 71 | from readtoolbar import ViewToolbar |
66 | 72 | from bookmarkview import BookmarkView |
67 | 73 | from readdb import BookmarkManager |
68 | from sugar3.graphics.menuitem import MenuItem | |
69 | 74 | from linkbutton import LinkButton |
75 | from speechtoolbar import SpeechToolbar | |
70 | 76 | |
71 | 77 | _HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager' |
72 | 78 | _HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager' |
504 | 510 | |
505 | 511 | def __toogle_tray_cb(self, button, visible): |
506 | 512 | if visible: |
507 | logging.error('Show tray') | |
513 | logging.debug('Show tray') | |
508 | 514 | self.tray.show() |
509 | 515 | else: |
510 | logging.error('Hide tray') | |
516 | logging.debug('Hide tray') | |
511 | 517 | self.tray.hide() |
512 | 518 | |
513 | 519 | def __num_page_entry_insert_text_cb(self, entry, text, length, position): |
684 | 690 | if self.props.active: |
685 | 691 | # Now active, start initial suspend timeout |
686 | 692 | if self._idle_timer > 0: |
687 | GObject.source_remove(self._idle_timer) | |
688 | self._idle_timer = GObject.timeout_add_seconds(15, | |
689 | self._suspend_cb) | |
693 | GLib.source_remove(self._idle_timer) | |
694 | self._idle_timer = GLib.timeout_add_seconds(15, | |
695 | self._suspend_cb) | |
690 | 696 | self._sleep_inhibit = False |
691 | 697 | else: |
692 | 698 | # Now inactive |
704 | 710 | def _user_action_cb(self, widget): |
705 | 711 | """Set a timer for going back to ebook mode idle sleep.""" |
706 | 712 | if self._idle_timer > 0: |
707 | GObject.source_remove(self._idle_timer) | |
708 | self._idle_timer = GObject.timeout_add_seconds(5, self._suspend_cb) | |
713 | GLib.source_remove(self._idle_timer) | |
714 | self._idle_timer = GLib.timeout_add_seconds(5, self._suspend_cb) | |
709 | 715 | |
710 | 716 | def _suspend_cb(self): |
711 | 717 | """Go into ebook mode idle sleep.""" |
730 | 736 | self._load_document('file://' + tempfile) |
731 | 737 | |
732 | 738 | # FIXME: This should obviously be fixed properly |
733 | GObject.timeout_add_seconds( | |
739 | GLib.timeout_add_seconds( | |
734 | 740 | 1, self.__view_toolbar_needs_update_size_cb, None) |
735 | 741 | |
736 | 742 | def write_file(self, file_path): |
840 | 846 | self._progress_alert = None |
841 | 847 | |
842 | 848 | # download the metadata |
843 | GObject.idle_add(self._download_metadata, tube_id, tube_ip, tube_port) | |
849 | GLib.idle_add(self._download_metadata, tube_id, tube_ip, tube_port) | |
844 | 850 | |
845 | 851 | def _download_metadata_result_cb(self, getter, tempfile, suggested_name, |
846 | 852 | tube_id): |
850 | 856 | os.remove(tempfile) |
851 | 857 | |
852 | 858 | # load the object from the datastore to update the file path |
853 | GObject.idle_add(self._open_downloaded_file, shared_metadata) | |
859 | GLib.idle_add(self._open_downloaded_file, shared_metadata) | |
854 | 860 | |
855 | 861 | def _open_downloaded_file(self, shared_metadata): |
856 | 862 | self._jobject = datastore.get(self._jobject.object_id) |
881 | 887 | if self._progress_alert is not None: |
882 | 888 | self.remove_alert(self._progress_alert) |
883 | 889 | self._progress_alert = None |
884 | GObject.idle_add(self._get_document) | |
890 | GLib.idle_add(self._get_document) | |
885 | 891 | |
886 | 892 | def _get_connection_params(self, tube_id): |
887 | 893 | # return ip and port to download a file |
888 | 894 | chan = self.shared_activity.telepathy_tubes_chan |
889 | iface = chan[telepathy.CHANNEL_TYPE_TUBES] | |
895 | iface = chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES] | |
890 | 896 | addr = iface.AcceptStreamTube( |
891 | 897 | tube_id, |
892 | telepathy.SOCKET_ADDRESS_TYPE_IPV4, | |
893 | telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0, | |
898 | TelepathyGLib.SocketAddressType.IPV4, | |
899 | TelepathyGLib.SocketAccessControl.LOCALHOST, 0, | |
894 | 900 | utf8_strings=True) |
895 | 901 | _logger.debug('Accepted stream tube: listening address is %r', addr) |
896 | 902 | # SOCKET_ADDRESS_TYPE_IPV4 is defined to have addresses of type '(sq)' |
939 | 945 | |
940 | 946 | # Avoid trying to download the document multiple times at once |
941 | 947 | self._want_document = False |
942 | GObject.idle_add(self._download_document, tube_id) | |
948 | GLib.idle_add(self._download_document, tube_id) | |
943 | 949 | return False |
944 | 950 | |
945 | 951 | def _joined_cb(self, also_self): |
951 | 957 | if self._progress_alert is not None: |
952 | 958 | self._progress_alert.props.msg = _('Receiving book...') |
953 | 959 | |
954 | GObject.idle_add(self._get_document) | |
960 | GLib.idle_add(self._get_document) | |
955 | 961 | |
956 | 962 | def _load_document(self, filepath): |
957 | 963 | """Load the specified document and set up the UI. |
1019 | 1025 | page = bookmark.page_no |
1020 | 1026 | thumb = self._bookmarkmanager.get_bookmark_preview(page) |
1021 | 1027 | if thumb is None: |
1022 | logging.error('Preview NOT FOUND') | |
1028 | logging.debug('Preview NOT FOUND') | |
1023 | 1029 | thumb = self._get_screenshot() |
1024 | 1030 | # The database is zero based |
1025 | 1031 | num_page = int(page) + 1 |
1060 | 1066 | if self._view.can_highlight(): |
1061 | 1067 | self._highlight.show() |
1062 | 1068 | if self._view.can_do_text_to_speech(): |
1063 | import speech | |
1064 | from speechtoolbar import SpeechToolbar | |
1065 | if speech.supported: | |
1066 | self.speech_toolbar = SpeechToolbar(self) | |
1067 | self.speech_toolbar_button.set_page(self.speech_toolbar) | |
1068 | self.speech_toolbar_button.show() | |
1069 | self.speech_toolbar = SpeechToolbar(self) | |
1070 | self.speech_toolbar_button.set_page(self.speech_toolbar) | |
1071 | self.speech_toolbar_button.show() | |
1069 | 1072 | |
1070 | 1073 | def _share_document(self): |
1071 | 1074 | """Share the document.""" |
1080 | 1083 | |
1081 | 1084 | # Make a tube for it |
1082 | 1085 | chan = self.shared_activity.telepathy_tubes_chan |
1083 | iface = chan[telepathy.CHANNEL_TYPE_TUBES] | |
1086 | iface = chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES] | |
1084 | 1087 | self._fileserver_tube_id = iface.OfferStreamTube( |
1085 | 1088 | READ_STREAM_SERVICE, |
1086 | 1089 | {}, |
1087 | telepathy.SOCKET_ADDRESS_TYPE_IPV4, | |
1090 | TelepathyGLib.SocketAddressType.IPV4, | |
1088 | 1091 | ('127.0.0.1', dbus.UInt16(self.port)), |
1089 | telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0) | |
1092 | TelepathyGLib.SocketAccessControl.LOCALHOST, 0) | |
1090 | 1093 | |
1091 | 1094 | def create_metadata_file(self): |
1092 | 1095 | # store the metadata in a json file |
1097 | 1100 | for key in self.metadata.keys(): |
1098 | 1101 | if key not in ['preview', 'cover_image']: |
1099 | 1102 | shared_metadata[str(key)] = self.metadata[key] |
1100 | logging.error('save metadata in %s', metadata_file_path) | |
1103 | logging.debug('save metadata in %s', metadata_file_path) | |
1101 | 1104 | with open(metadata_file_path, 'w') as json_file: |
1102 | 1105 | json.dump(shared_metadata, json_file) |
1103 | 1106 | return metadata_file_path |
1106 | 1109 | """Watch for new tubes.""" |
1107 | 1110 | tubes_chan = self.shared_activity.telepathy_tubes_chan |
1108 | 1111 | |
1109 | tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( | |
1112 | tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].connect_to_signal( | |
1110 | 1113 | 'NewTube', self._new_tube_cb) |
1111 | tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( | |
1114 | tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].ListTubes( | |
1112 | 1115 | reply_handler=self._list_tubes_reply_cb, |
1113 | 1116 | error_handler=self._list_tubes_error_cb) |
1114 | 1117 | |
1123 | 1126 | self.unused_download_tubes.add(tube_id) |
1124 | 1127 | # if no download is in progress, let's fetch the document |
1125 | 1128 | if self._want_document: |
1126 | GObject.idle_add(self._get_document) | |
1129 | GLib.idle_add(self._get_document) | |
1127 | 1130 | |
1128 | 1131 | def _list_tubes_reply_cb(self, tubes): |
1129 | 1132 | """Callback when new tubes are available.""" |
1217 | 1220 | self.fullscreen() |
1218 | 1221 | |
1219 | 1222 | def _added_bookmark_cb(self, bookmarkmanager, page, title): |
1220 | logging.error('Bookmark added page %d', page) | |
1223 | logging.debug('Bookmark added page %d', page) | |
1221 | 1224 | title = _('%s (Page %d)') % (title, page) |
1222 | 1225 | color = profile.get_color().to_string() |
1223 | 1226 | owner = profile.get_nick_name() |
1226 | 1229 | bookmarkmanager.add_bookmark_preview(page - 1, thumb) |
1227 | 1230 | |
1228 | 1231 | def _removed_bookmark_cb(self, bookmarkmanager, page): |
1229 | logging.error('Bookmark removed page %d', page) | |
1232 | logging.debug('Bookmark removed page %d', page) | |
1230 | 1233 | # remove button from tray |
1231 | 1234 | for button in self.tray.get_children(): |
1232 | 1235 | if button.page == page: |
1254 | 1257 | # but we don't have a event yet, Evince model have a event |
1255 | 1258 | # we need check the differnt backends and implement |
1256 | 1259 | # in all the backends. |
1257 | GObject.timeout_add_seconds(2, self._update_preview, button, page) | |
1260 | GLib.timeout_add_seconds(2, self._update_preview, button, page) | |
1258 | 1261 | |
1259 | 1262 | def _update_preview(self, button, page): |
1260 | 1263 | thumb = self._get_screenshot() |
36 | 36 | dbpath = os.path.join(dbdir, 'read_v1.db') |
37 | 37 | olddbpath = os.path.join(dbdir, 'read.db') |
38 | 38 | |
39 | srcpath = os.path.join(os.environ['SUGAR_BUNDLE_PATH'], 'read_v1.db') | |
40 | ||
41 | 39 | # Situation 0: Db is existent |
42 | 40 | if os.path.exists(dbpath): |
43 | 41 | return dbpath |
119 | 117 | |
120 | 118 | self._bookmarks = [] |
121 | 119 | self._populate_bookmarks() |
122 | self._highlights = {0: []} | |
120 | self._highlights = {0: []} | |
123 | 121 | self._populate_highlights() |
124 | 122 | |
125 | 123 | self._user = profile.get_nick_name() |
125 | 125 | self._search_entry_changed = True |
126 | 126 | self._update_find_buttons() |
127 | 127 | |
128 | # GObject.timeout_add(500, self._search_entry_timeout_cb) | |
128 | # GLib.timeout_add(500, self._search_entry_timeout_cb) | |
129 | 129 | # |
130 | 130 | # def _search_entry_timeout_cb(self): |
131 | 131 | # self._clear_find_job() |
0 | # Copyright (C) 2008, 2009 James D. Simmons | |
1 | # Copyright (C) 2009 Aleksey S. Lim | |
2 | # | |
3 | # This program is free software; you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation; either version 2 of the License, or | |
6 | # (at your option) any later version. | |
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, write to the Free Software | |
15 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
16 | ||
17 | import logging | |
18 | ||
19 | _logger = logging.getLogger('read-activity') | |
20 | ||
21 | supported = True | |
22 | ||
23 | try: | |
24 | from gi.repository import Gst | |
25 | Gst.init(None) | |
26 | Gst.ElementFactory.make('espeak', None) | |
27 | from speech_gst import * | |
28 | _logger.error('use gst-plugins-espeak') | |
29 | except Exception, e: | |
30 | _logger.error('disable gst-plugins-espeak: %s' % e) | |
31 | try: | |
32 | from speech_dispatcher import * | |
33 | _logger.error('use speech-dispatcher') | |
34 | except Exception, e: | |
35 | supported = False | |
36 | _logger.error('disable speech: %s' % e) | |
37 | ||
38 | voice = 'default' | |
39 | pitch = 0 | |
40 | rate = 0 | |
41 | ||
42 | highlight_cb = None | |
43 | end_text_cb = None | |
44 | reset_cb = None |
0 | # Copyright (C) 2008 James D. Simmons | |
1 | # | |
2 | # This program is free software; you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation; either version 2 of the License, or | |
5 | # (at your option) any later version. | |
6 | # | |
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. | |
11 | # | |
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program; if not, write to the Free Software | |
14 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
15 | ||
16 | from gi.repository import Gdk | |
17 | import time | |
18 | import threading | |
19 | import speechd | |
20 | import logging | |
21 | ||
22 | import speech | |
23 | ||
24 | _logger = logging.getLogger('read-etexts-activity') | |
25 | ||
26 | done = True | |
27 | ||
28 | ||
29 | def voices(): | |
30 | try: | |
31 | client = speechd.SSIPClient('readetextstest') | |
32 | voices = client.list_synthesis_voices() | |
33 | client.close() | |
34 | return voices | |
35 | except Exception, e: | |
36 | _logger.warning('speech dispatcher not started: %s' % e) | |
37 | return [] | |
38 | ||
39 | ||
40 | def say(words): | |
41 | try: | |
42 | client = speechd.SSIPClient('readetextstest') | |
43 | client.set_rate(int(speech.rate)) | |
44 | client.set_pitch(int(speech.pitch)) | |
45 | client.set_language(speech.voice[1]) | |
46 | client.speak(words) | |
47 | client.close() | |
48 | except Exception, e: | |
49 | _logger.warning('speech dispatcher not running: %s' % e) | |
50 | ||
51 | ||
52 | def is_stopped(): | |
53 | return done | |
54 | ||
55 | ||
56 | def pause(): | |
57 | pass | |
58 | ||
59 | ||
60 | def stop(): | |
61 | global done | |
62 | done = True | |
63 | ||
64 | ||
65 | def play(words): | |
66 | global thread | |
67 | thread = EspeakThread(words) | |
68 | thread.start() | |
69 | ||
70 | ||
71 | class EspeakThread(threading.Thread): | |
72 | ||
73 | def __init__(self, words): | |
74 | threading.Thread.__init__(self) | |
75 | self.words = words | |
76 | ||
77 | def run(self): | |
78 | "This is the code that is executed when the start() method is called" | |
79 | self.client = None | |
80 | try: | |
81 | self.client = speechd.SSIPClient('readetexts') | |
82 | self.client._conn.send_command('SET', speechd.Scope.SELF, | |
83 | 'SSML_MODE', "ON") | |
84 | if speech.voice: | |
85 | self.client.set_language(speech.voice[1]) | |
86 | self.client.set_rate(speech.rate) | |
87 | self.client.set_pitch(speech.pitch) | |
88 | self.client.speak(self.words, self.next_word_cb, | |
89 | (speechd.CallbackType.INDEX_MARK, | |
90 | speechd.CallbackType.END)) | |
91 | global done | |
92 | done = False | |
93 | while not done: | |
94 | time.sleep(0.1) | |
95 | self.cancel() | |
96 | self.client.close() | |
97 | except Exception, e: | |
98 | _logger.warning('speech-dispatcher client not created: %s' % e) | |
99 | ||
100 | def cancel(self): | |
101 | if self.client: | |
102 | try: | |
103 | self.client.cancel() | |
104 | except Exception, e: | |
105 | _logger.warning('speech dispatcher cancel failed: %s' % e) | |
106 | ||
107 | def next_word_cb(self, type, **kargs): | |
108 | if type == speechd.CallbackType.INDEX_MARK: | |
109 | mark = kargs['index_mark'] | |
110 | word_count = int(mark) | |
111 | Gdk.threads_enter() | |
112 | speech.highlight_cb(word_count) | |
113 | Gdk.threads_leave() | |
114 | elif type == speechd.CallbackType.END: | |
115 | Gdk.threads_enter() | |
116 | if speech.reset_cb is not None: | |
117 | speech.reset_cb() | |
118 | if speech.reset_buttons_cb is not None: | |
119 | speech.reset_buttons_cb() | |
120 | ||
121 | Gdk.threads_leave() | |
122 | global done | |
123 | done = True |
0 | # Copyright (C) 2009 Aleksey S. Lim | |
1 | # | |
2 | # This program is free software; you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation; either version 2 of the License, or | |
5 | # (at your option) any later version. | |
6 | # | |
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. | |
11 | # | |
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program; if not, write to the Free Software | |
14 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
15 | ||
16 | from gi.repository import Gst | |
17 | import logging | |
18 | ||
19 | import speech | |
20 | ||
21 | _logger = logging.getLogger('read-activity') | |
22 | ||
23 | ||
24 | def get_all_voices(): | |
25 | all_voices = {} | |
26 | for voice in Gst.ElementFactory.make('espeak', None).props.voices: | |
27 | name, language, dialect = voice | |
28 | if dialect != 'none': | |
29 | all_voices[language + '_' + dialect] = name | |
30 | else: | |
31 | all_voices[language] = name | |
32 | return all_voices | |
33 | ||
34 | ||
35 | def _message_cb(bus, message, pipe): | |
36 | if message.type == Gst.MessageType.EOS: | |
37 | pipe.set_state(Gst.State.NULL) | |
38 | if speech.end_text_cb is not None: | |
39 | speech.end_text_cb() | |
40 | if message.type == Gst.MessageType.ERROR: | |
41 | pipe.set_state(Gst.State.NULL) | |
42 | if pipe is play_speaker[1]: | |
43 | speech.reset_cb() | |
44 | elif message.type == Gst.MessageType.ELEMENT and \ | |
45 | message.structure.get_name() == 'espeak-mark': | |
46 | mark = message.structure['mark'] | |
47 | speech.highlight_cb(int(mark)) | |
48 | ||
49 | ||
50 | def _create_pipe(): | |
51 | pipe = Gst.parse_launch('espeak name=espeak ! autoaudiosink') | |
52 | source = pipe.get_by_name('espeak') | |
53 | ||
54 | bus = pipe.get_bus() | |
55 | bus.add_signal_watch() | |
56 | bus.connect('message', _message_cb, pipe) | |
57 | ||
58 | return (source, pipe) | |
59 | ||
60 | ||
61 | def _speech(speaker, words): | |
62 | speaker[0].props.pitch = speech.pitch | |
63 | speaker[0].props.rate = speech.rate | |
64 | speaker[0].props.voice = speech.voice[1] | |
65 | speaker[0].props.text = words | |
66 | speaker[1].set_state(Gst.State.NULL) | |
67 | speaker[1].set_state(Gst.State.PLAYING) | |
68 | ||
69 | ||
70 | info_speaker = _create_pipe() | |
71 | play_speaker = _create_pipe() | |
72 | play_speaker[0].props.track = 2 | |
73 | ||
74 | ||
75 | def voices(): | |
76 | return info_speaker[0].props.voices | |
77 | ||
78 | ||
79 | def say(words): | |
80 | _speech(info_speaker, words) | |
81 | ||
82 | ||
83 | def play(words): | |
84 | _speech(play_speaker, words) | |
85 | ||
86 | ||
87 | def pause(): | |
88 | play_speaker[1].set_state(Gst.State.PAUSED) | |
89 | ||
90 | ||
91 | def continue_play(): | |
92 | play_speaker[1].set_state(Gst.State.PLAYING) | |
93 | ||
94 | ||
95 | def is_stopped(): | |
96 | for i in play_speaker[1].get_state(): | |
97 | if isinstance(i, Gst.State) and i == Gst.State.NULL: | |
98 | return True | |
99 | return False | |
100 | ||
101 | ||
102 | def stop(): | |
103 | play_speaker[1].set_state(Gst.State.NULL) |
18 | 18 | from gettext import gettext as _ |
19 | 19 | |
20 | 20 | from gi.repository import Gtk |
21 | from gi.repository import GConf | |
22 | 21 | |
23 | 22 | from sugar3.graphics.toolbutton import ToolButton |
24 | 23 | from sugar3.graphics.toggletoolbutton import ToggleToolButton |
25 | 24 | from sugar3.graphics.combobox import ComboBox |
26 | 25 | from sugar3.graphics.toolcombobox import ToolComboBox |
27 | ||
28 | import speech | |
26 | from sugar3.speech import SpeechManager | |
29 | 27 | |
30 | 28 | |
31 | 29 | class SpeechToolbar(Gtk.Toolbar): |
33 | 31 | def __init__(self, activity): |
34 | 32 | Gtk.Toolbar.__init__(self) |
35 | 33 | self._activity = activity |
36 | if not speech.supported: | |
37 | return | |
38 | self.is_paused = False | |
34 | self._speech = SpeechManager() | |
35 | self._is_paused = False | |
39 | 36 | |
40 | self._cnf_client = GConf.Client.get_default() | |
41 | 37 | self.load_speech_parameters() |
42 | 38 | |
43 | self.sorted_voices = [i for i in speech.voices()] | |
44 | self.sorted_voices.sort(self.compare_voices) | |
45 | default = 0 | |
46 | for voice in self.sorted_voices: | |
47 | if voice[0] == speech.voice[0]: | |
48 | break | |
49 | default = default + 1 | |
39 | self._voices = self._speech.get_all_voices() # a dictionary | |
40 | ||
41 | locale = os.environ.get('LANG', '') | |
42 | language_location = locale.split('.', 1)[0].lower() | |
43 | language = language_location.split('_')[0] | |
44 | # if the language is es but not es_es default to es_la (latin voice) | |
45 | if language == 'es' and language_location != 'es_es': | |
46 | language_location = 'es_la' | |
47 | ||
48 | self._voice = 'en_us' | |
49 | if language_location in self._voices: | |
50 | self._voice = language_location | |
51 | elif language in self._voices: | |
52 | self._voice = language | |
53 | ||
54 | voice_names = [] | |
55 | for language, name in self._voices.iteritems(): | |
56 | voice_names.append((language, name)) | |
57 | voice_names.sort(self._compare_voice) | |
50 | 58 | |
51 | 59 | # Play button |
52 | self.play_btn = ToggleToolButton('media-playback-start') | |
53 | self.play_btn.show() | |
54 | self.play_btn.connect('toggled', self.play_cb) | |
55 | self.insert(self.play_btn, -1) | |
56 | self.play_btn.set_tooltip(_('Play / Pause')) | |
60 | self._play_button = ToggleToolButton('media-playback-start') | |
61 | self._play_button.show() | |
62 | self._play_button.connect('toggled', self._play_toggled_cb) | |
63 | self.insert(self._play_button, -1) | |
64 | self._play_button.set_tooltip(_('Play / Pause')) | |
57 | 65 | |
58 | 66 | # Stop button |
59 | self.stop_btn = ToolButton('media-playback-stop') | |
60 | self.stop_btn.show() | |
61 | self.stop_btn.connect('clicked', self.stop_cb) | |
62 | self.stop_btn.set_sensitive(False) | |
63 | self.insert(self.stop_btn, -1) | |
64 | self.stop_btn.set_tooltip(_('Stop')) | |
67 | self._stop_button = ToolButton('media-playback-stop') | |
68 | self._stop_button.show() | |
69 | self._stop_button.connect('clicked', self._stop_clicked_cb) | |
70 | self._stop_button.set_sensitive(False) | |
71 | self.insert(self._stop_button, -1) | |
72 | self._stop_button.set_tooltip(_('Stop')) | |
65 | 73 | |
66 | self.voice_combo = ComboBox() | |
67 | for voice in self.sorted_voices: | |
68 | self.voice_combo.append_item(voice, voice[0]) | |
69 | self.voice_combo.set_active(default) | |
74 | # Language list | |
75 | combo = ComboBox() | |
76 | which = 0 | |
77 | for pair in voice_names: | |
78 | language, name = pair | |
79 | combo.append_item(language, name) | |
80 | if language == self._voice: | |
81 | combo.set_active(which) | |
82 | which += 1 | |
70 | 83 | |
71 | self.voice_combo.connect('changed', self.voice_changed_cb) | |
72 | combotool = ToolComboBox(self.voice_combo) | |
84 | combo.connect('changed', self._voice_changed_cb) | |
85 | combotool = ToolComboBox(combo) | |
73 | 86 | self.insert(combotool, -1) |
74 | 87 | combotool.show() |
75 | speech.reset_buttons_cb = self.reset_buttons_cb | |
76 | 88 | |
77 | def compare_voices(self, a, b): | |
78 | if a[0].lower() == b[0].lower(): | |
89 | self._speech.connect('stop', self._reset_buttons_cb) | |
90 | ||
91 | def _compare_voice(self, a, b): | |
92 | if a[1].lower() == b[1].lower(): | |
79 | 93 | return 0 |
80 | if a[0] .lower() < b[0].lower(): | |
94 | if a[1].lower() < b[1].lower(): | |
81 | 95 | return -1 |
82 | if a[0] .lower() > b[0].lower(): | |
96 | if a[1].lower() > b[1].lower(): | |
83 | 97 | return 1 |
84 | 98 | |
85 | def voice_changed_cb(self, combo): | |
86 | speech.voice = combo.props.value | |
87 | speech.say(speech.voice[0]) | |
99 | def _voice_changed_cb(self, combo): | |
100 | self._voice = combo.props.value | |
101 | self._speech.say_text(self._voices[self._voice]) | |
88 | 102 | self.save_speech_parameters() |
89 | 103 | |
90 | 104 | def load_speech_parameters(self): |
95 | 109 | f = open(data_file_name, 'r') |
96 | 110 | try: |
97 | 111 | speech_parameters = json.load(f) |
98 | speech.voice = speech_parameters['voice'] | |
112 | self._voice = speech_parameters['voice'] | |
99 | 113 | finally: |
100 | 114 | f.close() |
101 | 115 | |
102 | self._cnf_client.add_dir('/desktop/sugar/speech', | |
103 | GConf.ClientPreloadType.PRELOAD_NONE) | |
104 | speech.pitch = self._cnf_client.get_int('/desktop/sugar/speech/pitch') | |
105 | speech.rate = self._cnf_client.get_int('/desktop/sugar/speech/rate') | |
106 | self._cnf_client.notify_add('/desktop/sugar/speech/pitch', | |
107 | self.__conf_changed_cb, None) | |
108 | self._cnf_client.notify_add('/desktop/sugar/speech/rate', | |
109 | self.__conf_changed_cb, None) | |
110 | ||
111 | def __conf_changed_cb(self, client, connection_id, entry, args): | |
112 | key = entry.get_key() | |
113 | value = client.get_int(key) | |
114 | if key == '/desktop/sugar/speech/pitch': | |
115 | speech.pitch = value | |
116 | if key == '/desktop/sugar/speech/rate': | |
117 | speech.rate = value | |
118 | ||
119 | 116 | def save_speech_parameters(self): |
120 | 117 | speech_parameters = {} |
121 | speech_parameters['voice'] = speech.voice | |
118 | speech_parameters['voice'] = self._voice | |
122 | 119 | data_path = os.path.join(self._activity.get_activity_root(), 'data') |
123 | 120 | data_file_name = os.path.join(data_path, 'speech_params.json') |
124 | 121 | f = open(data_file_name, 'w') |
127 | 124 | finally: |
128 | 125 | f.close() |
129 | 126 | |
130 | def reset_buttons_cb(self): | |
131 | self.play_btn.set_icon_name('media-playback-start') | |
132 | self.stop_btn.set_sensitive(False) | |
133 | self.is_paused = False | |
127 | def _reset_buttons_cb(self, widget=None): | |
128 | self._play_button.set_icon_name('media-playback-start') | |
129 | self._stop_button.set_sensitive(False) | |
130 | self._is_paused = False | |
134 | 131 | |
135 | def play_cb(self, widget): | |
136 | self.stop_btn.set_sensitive(True) | |
132 | def _play_toggled_cb(self, widget): | |
133 | self._stop_button.set_sensitive(True) | |
137 | 134 | if widget.get_active(): |
138 | self.play_btn.set_icon_name('media-playback-pause') | |
139 | if not self.is_paused: | |
140 | speech.play(self._activity._view.get_marked_words()) | |
135 | self._play_button.set_icon_name('media-playback-pause') | |
136 | if not self._is_paused: | |
137 | self._speech.say_text( | |
138 | self._activity._view.get_marked_words(), | |
139 | lang_code=self._voice) | |
141 | 140 | else: |
142 | speech.continue_play() | |
141 | self._speech.restart() | |
143 | 142 | else: |
144 | self.play_btn.set_icon_name('media-playback-start') | |
145 | self.is_paused = True | |
146 | speech.pause() | |
143 | self._play_button.set_icon_name('media-playback-start') | |
144 | self._is_paused = True | |
145 | self._speech.pause() | |
147 | 146 | |
148 | def stop_cb(self, widget): | |
149 | self.stop_btn.set_sensitive(False) | |
150 | self.play_btn.set_icon_name('media-playback-start') | |
151 | self.play_btn.set_active(False) | |
152 | self.is_paused = False | |
153 | speech.stop() | |
147 | def _stop_clicked_cb(self, widget): | |
148 | self._stop_button.set_sensitive(False) | |
149 | self._play_button.set_icon_name('media-playback-start') | |
150 | self._play_button.set_active(False) | |
151 | self._is_paused = False | |
152 | self._speech.stop() |
8 | 8 | |
9 | 9 | from sugar3 import mime |
10 | 10 | from sugar3.graphics import style |
11 | ||
12 | import speech | |
13 | 11 | |
14 | 12 | PAGE_SIZE = 38 |
15 | 13 | |
96 | 94 | self.spoken_word_tag = self.textview.get_buffer().create_tag() |
97 | 95 | self.spoken_word_tag.set_property('weight', Pango.Weight.BOLD) |
98 | 96 | self.normal_tag = self.textview.get_buffer().create_tag() |
99 | self.normal_tag.set_property('weight', Pango.Weight.NORMAL) | |
97 | self.normal_tag.set_property('weight', Pango.Weight.NORMAL) | |
100 | 98 | |
101 | 99 | def load_document(self, file_path): |
102 | 100 | |
103 | 101 | file_name = file_path.replace('file://', '') |
104 | 102 | mimetype = mime.get_for_file(file_path) |
105 | 103 | if mimetype == 'application/zip': |
106 | logging.error('opening zip file') | |
104 | logging.debug('opening zip file') | |
107 | 105 | self.zf = zipfile.ZipFile(file_path.replace('file://', ''), 'r') |
108 | 106 | self.book_files = self.zf.namelist() |
109 | 107 | extract_path = os.path.join(self._activity.get_activity_root(), |
113 | 111 | self.zf.extract(book_file, extract_path) |
114 | 112 | file_name = os.path.join(extract_path, book_file) |
115 | 113 | |
116 | logging.error('opening file_name %s' % file_name) | |
114 | logging.debug('opening file_name %s' % file_name) | |
117 | 115 | self._etext_file = open(file_name, 'r') |
118 | 116 | |
119 | 117 | self.page_index = [0] |
135 | 133 | self._scrollbar.set_range(1.0, self._pagecount - 1.0) |
136 | 134 | self._scrollbar.set_increments(1.0, 1.0) |
137 | 135 | |
138 | speech.highlight_cb = self.highlight_next_word | |
139 | speech.reset_cb = self.reset_text_to_speech | |
136 | # TODO: if ever sugar3.speech has word signals | |
137 | # call self.highlight_next_word on each word | |
138 | # call self.reset_text_to_speech at end | |
140 | 139 | |
141 | 140 | def _show_page(self, page_number): |
142 | 141 | position = self.page_index[page_number] |
149 | 148 | break |
150 | 149 | else: |
151 | 150 | line = _clean_text(line) |
152 | label_text = label_text + unicode(line, "iso-8859-1") | |
151 | label_text = label_text + unicode(line, "iso-8859-1") | |
153 | 152 | line_increment = (len(line) / 80) + 1 |
154 | 153 | linecount = linecount + line_increment |
155 | 154 | textbuffer = self.textview.get_buffer() |
195 | 194 | self._current_page = int(value) |
196 | 195 | self.emit('page-changed', old_page, self._current_page) |
197 | 196 | else: |
198 | print 'Warning: unknown scrolltype %s with value %f' \ | |
199 | % (str(scrolltype), value) | |
197 | print('Warning: unknown scrolltype %s with value %f' | |
198 | % (str(scrolltype), value)) | |
200 | 199 | |
201 | 200 | # FIXME: This should not be needed here |
202 | 201 | self._scrollbar.set_value(self._current_page) |
280 | 279 | marked_up_text = '<speak> ' |
281 | 280 | while i < len(self.word_tuples): |
282 | 281 | word_tuple = self.word_tuples[i] |
283 | marked_up_text = marked_up_text + '<mark name="' + str(i) + '"/>' \ | |
284 | + word_tuple[2] | |
282 | marked_up_text = marked_up_text + \ | |
283 | ' <mark name="' + str(i) + '"/>' + word_tuple[2] | |
285 | 284 | i = i + 1 |
286 | 285 | print marked_up_text |
287 | 286 | return marked_up_text + '</speak>' |
294 | 293 | j = 0 |
295 | 294 | word_begin = 0 |
296 | 295 | word_end = 0 |
297 | ignore_chars = [' ', '\n', u'\r', '_', '[', '{', ']', '}', '|', | |
298 | '<', '>', '*', '+', '/', '\\'] | |
296 | ignore_chars = [' ', '\n', u'\r', '_', '[', '{', ']', '}', '|', | |
297 | '<', '>', '*', '+', '/', '\\'] | |
299 | 298 | ignore_set = set(ignore_chars) |
300 | 299 | self.word_tuples = [] |
301 | 300 | len_page_text = len(page_text) |
313 | 312 | self.word_tuples.append(word_tuple) |
314 | 313 | i = i + 1 |
315 | 314 | |
316 | def highlight_next_word(self, word_count): | |
315 | def highlight_next_word(self, word_count): | |
317 | 316 | if word_count < len(self.word_tuples): |
318 | 317 | word_tuple = self.word_tuples[word_count] |
319 | 318 | textbuffer = self.textview.get_buffer() |
320 | 319 | iterStart = textbuffer.get_iter_at_offset(word_tuple[0]) |
321 | 320 | iterEnd = textbuffer.get_iter_at_offset(word_tuple[1]) |
322 | 321 | bounds = textbuffer.get_bounds() |
323 | textbuffer.apply_tag(self.normal_tag, bounds[0], iterStart) | |
322 | textbuffer.apply_tag(self.normal_tag, bounds[0], iterStart) | |
324 | 323 | textbuffer.apply_tag(self.spoken_word_tag, iterStart, iterEnd) |
325 | 324 | v_adjustment = self._sw.get_vadjustment() |
326 | 325 | max_pos = v_adjustment.get_upper() - v_adjustment.get_page_size() |
338 | 337 | self.metadata = activity.metadata |
339 | 338 | if 'Read_zoom' in self.metadata: |
340 | 339 | try: |
341 | logging.error('Loading zoom %s', self.metadata['Read_zoom']) | |
340 | logging.debug('Loading zoom %s', self.metadata['Read_zoom']) | |
342 | 341 | self.set_zoom(float(self.metadata['Read_zoom'])) |
343 | 342 | except: |
344 | 343 | pass |
435 | 434 | def get_has_selection(self): |
436 | 435 | return self._has_selection |
437 | 436 | |
438 | def find_set_highlight_search(self, True): | |
437 | def find_set_highlight_search(self, boolean): | |
439 | 438 | pass |
440 | 439 | |
441 | 440 | def setup_find_job(self, text, _find_updated_cb): |
621 | 620 | |
622 | 621 | return False |
623 | 622 | |
624 | def _allindices(self, line, search, listindex=None, offset=0): | |
623 | def _allindices(self, line, search, listindex=None, offset=0): | |
625 | 624 | if listindex is None: |
626 | 625 | listindex = [] |
627 | 626 | if (line.find(search) == -1): |