Compare commits

..

236 Commits

Author SHA1 Message Date
97afc379e8 Pushed a new snapshot 2021-06-28 00:47:23 -05:00
7f401ba789 Removed unneeded locales 2021-06-28 00:39:45 -05:00
ff22ae5653 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2021-06-28 00:38:54 -05:00
02d94fcea0 Fixed a small issue when loading a conversation buffer 2021-06-28 00:36:53 -05:00
df2015f360 Merge pull request #384 from manuelcortez/twitter_videos
Support for Twitter video playback
2021-06-27 23:40:11 -05:00
64a14c831b Added initial support for playback of Twitter videos (only works in tweets so far) 2021-06-27 21:40:22 -05:00
fbbe7852c2 Added serbian to generate documentation 2021-06-27 20:57:13 -05:00
9dfccd2bd0 Updated documentation generators 2021-06-27 20:55:16 -05:00
969a75e9f3 Merge pull request #380 from manuelcortez/better_memory_management
Replace the cache database for a SQLite backed implementation
2021-06-27 19:10:50 -05:00
a59ba5ef78 Reset cache every time we call to save_persistent_data in tests 2021-06-27 19:09:49 -05:00
3ebfdbc48b Request for a restart when switching memory cache 2021-06-27 19:09:21 -05:00
8db14a95c1 Call reduce.reduce_tweet and session.save_users when retrieving previous items for a buffer 2021-06-27 18:07:56 -05:00
1b9062d86f aHandles deleted users for direct messages without wasting too many API calls 2021-06-27 18:05:35 -05:00
4b60a79e49 Made get_all_mentioned to take into account sometimes tweets might have no entities defined 2021-06-27 18:04:26 -05:00
002e1ccb55 If get_user is called with a full user object as an argument, logs it and changes the object to be only the user_id 2021-06-27 16:03:36 -05:00
0bcdf88290 Merge pull request #382 from nidza07/SerbianTranslation
Added Serbian documentation and changelog.
2021-06-27 02:35:55 -05:00
0612c653b8 Make TWBlue to respect persist_size when loading the cache database. Added tests for both reading from disk and loading in memory caches 2021-06-27 02:32:28 -05:00
nikola nikola
c5dadb063a Added Serbian documentation and changelog. 2021-06-27 04:22:02 +02:00
35d6010298 Save tweets cache by taking into account persist_size 2021-06-26 15:16:50 -05:00
40a63d9e16 Merge pull request #381 from nidza07/SerbianTranslation
Updated Serbian translation.
2021-06-26 02:16:13 -05:00
5712dd735b Added a new setting in account dialog to control whehter we load the cache db into memory or read it from disk 2021-06-26 01:52:07 -05:00
2c75ea5005 Fixed a small issue referencing an user in the old way while retrieving all mentioned users in a tweet 2021-06-26 01:50:47 -05:00
e35f37fcc2 Updated changelog 2021-06-26 01:49:55 -05:00
71358ea74d changed function names and cleaned code a little bit to reflect better the changes to object percistance 2021-06-25 23:35:33 -05:00
b8f822830f Added proper docstrings to reduce twitter objects 2021-06-25 22:47:10 -05:00
74e4fe6357 Ensure direct message buffers are correctly saved in database 2021-06-25 16:49:23 -05:00
77bee64421 Pass a null value to tweepy.models.Status as sometimes database saved objects might not include it 2021-06-25 16:48:44 -05:00
c761230566 Reduce the size of all tweets so it might make easier to handle those in a realtime database 2021-06-25 16:25:51 -05:00
49505fabcd Modify utils so those will take into account that entities might be not present in tweet objects 2021-06-25 13:14:01 -05:00
4ad01d7833 Retrieve user objects from the users list stored in SqlDict as opposed to loading it from tweet objects 2021-06-25 13:13:00 -05:00
ab1a13f886 Improve save_users() and get_user() as those will be used in more places later 2021-06-25 13:11:33 -05:00
nikola nikola
44c25e54f8 Updated Serbian translation. 2021-06-25 18:33:16 +02:00
cdabd6f055 Merge branch 'next-gen' into better_memory_management 2021-06-24 09:54:45 -05:00
60144a6b08 Added initial support to SqliteDict package 2021-06-24 09:52:10 -05:00
382acf7c8c Use slitedict to attempt to reduce memory usage when caching tweets 2021-06-23 13:40:21 -05:00
José Manuel Delicado
03ba59028f Merge pull request #379 from ivnc/next-gen
Fix arrow for Galician
2021-06-23 20:05:24 +02:00
Iván Novegil
50125fc55a Fix arrow for Galician. Modify some existing translations to localize them properly 2021-06-23 18:45:33 +02:00
39e1fb017c Made code indentation to comply with PEP8 2021-06-16 16:18:41 -05:00
2aaa4eced3 Removed Catala and Basque locale as they are in arrow already. Disabled Galician locale cause it's not fully implemented and fails 2021-06-16 16:17:16 -05:00
José Manuel
6d2eac5b1c Merge pull request #332 from Oreonan/fr-04062020
Some changes for french translation
2021-05-29 21:56:33 +02:00
Oreonan
40040d1b17 Merge branch 'next-gen' into fr-04062020 2021-05-14 21:24:46 +02:00
2a791d43bf Fixed an error when parsing a DM sent from an deleted account 2021-05-14 09:52:19 -05:00
b10aeb046d Changed label of direct message's text field so it will not reference any username in the hint. Closes #366 2021-05-07 17:18:21 -05:00
7d6e230fd9 Fix issue that avoids TWBlue to use Shift+F10 as menu key. Fixes #353 2021-05-07 16:52:10 -05:00
9346bba7a0 Fixed a small bug when sending long tweets via twyshort 2021-05-03 10:05:14 -05:00
30f739c42e Updated version to a new snapshot 2021-04-19 16:51:26 -05:00
eb0679cb96 Make unexistent users to throw an error when loading a timeline 2021-04-17 13:00:30 -05:00
45deae3402 Fixed rendering of retweets of quoted tweets. Fixes #365 2021-03-09 16:41:58 -06:00
5b0b26799d Install platform_utils, accessible_output2, libloader and sound_lib from upstream. Closes #369 2021-03-09 16:36:23 -06:00
ee234b80a7 Fixed error when parsing long tweets. 2021-03-09 11:35:52 -06:00
0065af2aef Avoid removing buffers when api_error=130 2021-02-25 16:57:32 -06:00
9c086cfa0f Prevend some errors to be identified as current user being blocked 2021-02-11 12:50:09 -06:00
2f263a23b7 Avoid giving false positive errors when buffers are updating 2021-02-10 09:34:30 -06:00
9cb6eafbbc Fixed issue in autocomplete users feature. closes #367 2021-02-04 12:30:20 -06:00
cba7c39a0e Updated to snapshot 4 2021-01-27 17:49:59 -06:00
e2e8b84e6a Fixed focus when search dialog opens. Fixes #364 2021-01-27 17:32:12 -06:00
eca0c0dbbd Fixed shelve method 2021-01-27 17:31:14 -06:00
36cc3f9365 Fix call to retrieve muted user IDS on twitter session 2021-01-27 17:30:10 -06:00
63d7cbe7c4 Implemented user searches. 2021-01-27 16:27:33 -06:00
ae57cc3404 Retrieve up to 5000 users when getting list IDS 2021-01-27 15:03:47 -06:00
23df8f8a7f Updated python version in CI config file 2021-01-27 14:00:29 -06:00
2f278b7f3c Removed yet another reference to Twython 2021-01-27 13:30:37 -06:00
9444939c35 Replaced cursored calls for manual calls to function with return_cursors. This way we will avoid hitting TWitter Rate limits accidentally 2021-01-27 13:20:47 -06:00
6688dc1163 Removed unneeded parameters when retrieving direct messages 2021-01-27 13:19:16 -06:00
6a7300b35f Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2021-01-27 10:38:21 -06:00
c95a2feb5e Faster implementation of dm loading due to a call to lookup_users instead to get_user 2021-01-27 10:36:44 -06:00
8042d28b13 Merge pull request #358 from riku22/update_japanese_translation
Update japanese translation
2021-01-26 17:46:29 -06:00
890359f8c7 Fixed Rate limit issue in cursored functions. Closes #354 2021-01-26 17:09:38 -06:00
91955e80d2 Fixed Picture OCR. Fixes #356 2021-01-26 16:23:48 -06:00
6c1c66226d Merge branch 'googletrans-integration' into next-gen 2021-01-26 16:11:49 -06:00
1a76913aac Removed debug info 2021-01-26 16:09:55 -06:00
f85af2cbd2 Merge pull request #361 from manuelcortez/googletrans-integration
Change translation service
2021-01-26 16:06:38 -06:00
351f700927 Replaced translation services. Fixes #355 2021-01-26 16:01:26 -06:00
Jose Manuel Delicado
abdde4c1f0 Updated windows-dependencies submodule 2021-01-23 19:31:58 +01:00
Jose Manuel Delicado
4899285eca Updated readme 2021-01-23 19:30:30 +01:00
riku
fb3e6b537c Updated Japanese translation 2021-01-23 09:05:35 +09:00
riku
22d1cc9ce9 Merge branch 'next-gen' into update_japanese_translation 2021-01-23 09:03:01 +09:00
304d91e8b7 Started replacing yandex.translate with googletrans 2021-01-22 17:55:15 -06:00
cf650052e4 Merge pull request #351 from riku22/add_requirements
Add pyenchant to requirements
2021-01-22 17:34:20 -06:00
riku
10055788e4 Removed twython 2021-01-23 07:26:42 +09:00
riku
a67f3f037e Merge branch 'next-gen' into add_requirements 2021-01-23 07:26:14 +09:00
Jose Manuel Delicado
4d736c00fc main.py: Check for other running instances before anything else 2021-01-22 18:50:09 +01:00
Jose Manuel Delicado
29b8a645db Updated changelog 2021-01-22 18:22:07 +01:00
Jose Manuel Delicado
2459a499ce Merge branch 'next-gen' into proxy 2021-01-22 18:01:29 +01:00
28f4e3a534 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2021-01-22 10:56:07 -06:00
e2931e96eb Remove references to Twython. #351 2021-01-22 10:55:08 -06:00
Jose Manuel Delicado
fbcba39e69 Small fixes. Support for socks4a proxies 2021-01-22 17:54:47 +01:00
Jose Manuel Delicado
b59d262dca Apply proxy settings before TWBlue main components start loading 2021-01-22 17:40:48 +01:00
Jose Manuel Delicado
7f4a13231f config.py: update the list of supported proxy types 2021-01-22 17:09:39 +01:00
Jose Manuel Delicado
4ab5af2ae9 Store the proxy type as an integer when saving the configuration 2021-01-22 17:06:32 +01:00
Jose Manuel Delicado
4c4eaf4012 controller/settings: assign the proxy type in the combo box based on the retrieved integer from config 2021-01-22 17:01:44 +01:00
Jose Manuel Delicado
b03198a39f Proxy type is now an integer instead of a string 2021-01-22 16:55:38 +01:00
Jose Manuel Delicado
06dc26e962 Proxy port is now an integer in the default configuration 2021-01-22 16:54:20 +01:00
Jose Manuel Delicado
bcc8f5968e Configuration dialog: use SpinCTRL instead of TextCTRL to specify the proxy port 2021-01-22 16:53:07 +01:00
Jose Manuel Delicado
4c144d2f7e Requirements: always install the latest WXPython version 2021-01-22 16:20:41 +01:00
747290e16a Require extended tweets for quoted tweets. Fixes #352 2021-01-22 09:15:47 -06:00
Jose Manuel Delicado
64b6d4df74 Requirements: do not install an idna version greather than 3.0 to avoid breaking requests package dependencies. Added wheel as a requirement to increase installation speed 2021-01-22 12:46:37 +01:00
riku
22d48cb5d9 Update Japanese translation 2021-01-22 12:52:19 +09:00
riku
fea01c44ca Add twython and pyenchant to requirements 2021-01-22 11:31:40 +09:00
b80a342f92 Count tweet characters and URLS as specified by Twitter. Fixes #199. Fixes #315 2021-01-21 17:12:18 -06:00
60a13d974c Added twitter-text-parser as a dependency 2021-01-21 17:10:56 -06:00
4c9000839d Fixed some errors when starting people buffers 2021-01-21 08:27:50 -06:00
b3c24c6734 Merge pull request #347 from manuelcortez/tweepy
Migration to Tweepy. Closes #237 , closes #240
2021-01-20 17:50:19 -06:00
a2a8cc5b79 Updated changelog 2021-01-20 17:44:29 -06:00
da7a208c1f Implemented people buffers and searches 2021-01-20 17:06:59 -06:00
979a3d3e99 Changed a function to order people buffers after being retrieved from Twitter 2021-01-20 17:06:15 -06:00
1f11ea7aa0 fixed an issue in compose for followers 2021-01-20 17:05:39 -06:00
8bdc933bce Migrated direct messages. Cursor support still require testing 2021-01-20 15:56:20 -06:00
999cbba464 Implemented function to get previous item for tweets and searches 2021-01-20 11:02:21 -06:00
7457521398 Restored media uploading (needs testing). Fixes #240 2021-01-20 10:35:11 -06:00
366b61134e Fixed trending topics dialog 2021-01-20 10:00:19 -06:00
52154ac271 Migrated support to Trending Topics 2021-01-20 09:59:38 -06:00
cdc285e362 Implemented support to list buffer creation 2021-01-20 09:21:50 -06:00
4643301764 Implemented list creation, edition and removal 2021-01-19 17:47:54 -06:00
f546543e9b Implemented tweet searches and conversation fupport 2021-01-14 09:18:57 -06:00
85fe94ec0c Implemented user actions (needs testing) 2021-01-13 10:38:26 -06:00
db13c96baa Fixed some incorrect error handling on utils 2021-01-13 10:33:49 -06:00
33b6000b41 Implemented user profile viewer 2021-01-13 10:32:03 -06:00
a261365682 Fixed some stuff in main controller. Likes work again 2021-01-13 10:31:37 -06:00
4064582583 Remove old reference to Twython error handling 2021-01-13 08:47:13 -06:00
fc0da0bdbb Migrated the main controller to use tweepy 2021-01-13 08:41:32 -06:00
76c678d4ba Fixed replies, retweets and iand tweet deletion 2021-01-05 11:10:55 -06:00
3db8b7b021 Fixed some rendering for dm's and people 2021-01-05 11:04:06 -06:00
fa49637d0e Replaced twython old exception handling by tweepy 2021-01-05 09:34:52 -06:00
65ec231349 Fixed an indentation issue 2021-01-05 09:20:36 -06:00
93705bf534 Fixed user timeline creation 2021-01-04 15:52:25 -06:00
29a905199d Basic implementation of tweetDisplayerDialog. Needs testing with lists of tweets and image description entities 2021-01-04 12:55:29 -06:00
8569d9b0a0 Readded code to load timelines with tweepy 2021-01-04 12:30:38 -06:00
1e1f2b089f Fix call to api.supported_languages() 2021-01-04 12:23:04 -06:00
78cc6e6784 Changed library calls on some basic buffer creation and manipulation 2021-01-04 11:16:56 -06:00
a37f339fea Fixed some issues when determining the presence of long tweets based on entities present in objects 2021-01-04 11:15:27 -06:00
d0cc12ef5c Added basic tweet rendering 2021-01-04 11:14:55 -06:00
bcd51d6259 Migrated most of twitter util functions from sessions/twitter/utils.py 2021-01-04 11:13:20 -06:00
7ceb806af2 Added more changes in order to deal with tweet objects 2020-12-22 17:29:33 -06:00
José Manuel
8f35fc0ec0 Merge pull request #342 from maniyax/next-gen
Edit Russian localization
2020-10-29 10:40:11 +01:00
maniyax
3f79fab8e5 update contributors.txt 2020-10-23 14:33:33 +03:00
maniyax
53dee8cb81 edit ru-locale 2020-10-23 14:31:47 +03:00
56103466fa Replaced get_account_settings, get_supported_languages, show_user, get_mute_ids 2020-07-20 13:26:22 -05:00
13f5df3a48 Use Tweepy functions for authentication and login 2020-07-20 13:11:37 -05:00
a89cc35d40 Started work: Added Tweepy as a dependency 2020-07-20 12:56:29 -05:00
276cd4b4dd Fixed a typo in last commit 2020-06-09 11:45:00 -05:00
c509433b2c Added an helper function for expanding t.co URLS for tweets 2020-06-09 11:34:02 -05:00
7292b36137 Removed old code from utils 2020-06-09 10:45:09 -05:00
Oreonan
0111c8aae1 some changes for french translation 2020-06-04 21:20:18 +02:00
eefe5e6200 Updated snapshot information 2020-06-04 12:14:57 -05:00
492f1d8aa5 Expand URL'S in profile bios 2020-06-04 10:25:00 -05:00
ac20ced5fa Expand URL in user profiles. Closes #275 2020-06-03 12:32:01 -05:00
866cd53328 Fixed typo in changelog 2020-06-03 11:24:20 -05:00
f523e3e81e Updated changelog 2020-06-03 11:22:07 -05:00
bdd7d617c3 Update title on user changes when sending a DM. Fixes #276 2020-06-03 11:19:23 -05:00
a0bc5f6cb3 Ignore folder env for virtual environments. Closes #321 2020-05-30 08:42:30 -05:00
3f238635f2 Fixed a typo 2020-05-29 21:25:13 -05:00
c300476ad2 Updated changelog to add a reference to an issue 2020-05-29 21:22:00 -05:00
6a2e00c467 Fixed calls to paths.config_path in Autocompletion module 2020-05-29 12:37:25 -05:00
4fa983a314 Show error when loading an account with invalid or expired tokens. Fixes #328 2020-05-29 11:17:18 -05:00
ed5b66d033 Fixed audio playback from URL 2020-05-29 10:28:10 -05:00
bec45c37c1 Updated Readme instructions to Python 3 and cx_freeze 2020-05-29 10:13:28 -05:00
ad42c09fef Updated changelog 2020-05-29 09:51:34 -05:00
084fa1894c Restart TWBlue gracefully 2020-05-29 09:49:57 -05:00
6bc63d79ff Updated data for ftp stuff 2020-05-28 16:52:18 -05:00
b1f06f6b1d Fixed some params in CI script 2020-05-28 16:45:36 -05:00
7e9d5359f1 CI environment: Make the Python 3 snapshot the default one 2020-05-28 10:57:46 -05:00
86b8a2ab0f Restarted version number for snapshots as these are built with Python 3 already 2020-05-28 10:56:44 -05:00
b8043f2c4a Added Microsoft redistributables in new builds 2020-05-28 10:35:45 -05:00
Jose Manuel Delicado
eff11f18c5 Updated dependencies. Run git submodule update 2020-05-26 19:34:25 +02:00
eca8b1ec46 Setup: Enchant works again. Python 3 build works. Closes #326 and #309 2020-05-01 13:29:04 -05:00
59bd2842c4 Don't set a custom dictionary path for now as Enchant 2.2.x is not compatible with calls to set_param. #326 2020-05-01 13:14:27 -05:00
92564d4075 Setup: Redirect exceptions properly by overriding sys.excepthook 2020-05-01 13:03:13 -05:00
9d59ee9d8d Use relative paths in setup file for better error handling 2020-04-29 08:33:05 -05:00
b845e751aa Wrote a new setup file (by using cx_freeze) 2019-12-16 16:52:59 -06:00
246a10535c Updated to the latest version of arrow (0.15.4) 2019-12-16 09:56:59 -06:00
977c521d7c Fix the cache database under Python 3 2019-12-16 09:49:49 -06:00
0e3ba0b89e Disable autogeneration of snapshot versions for a while 2019-12-16 09:47:49 -06:00
18d75e0564 Reverted to Arrow 0.14.5 for #317 2019-10-04 16:22:02 -05:00
482f95ba55 Updated code for supporting arrow 0.15 (timestamps passed as int, use two digits lang code) 2019-10-03 22:47:19 -05:00
c70b900c96 Removed arrow Warnings as 0.15 has been released 2019-10-03 22:11:06 -05:00
75877661a2 Try to upload artifact to ftp 2019-10-03 21:59:04 -05:00
1828b14790 Fixed unicodeError in documentation importer for python 3.7.4 2019-10-03 21:22:47 -05:00
35582650dc Removed py2exe from CI script 2019-10-03 17:55:27 -05:00
9500703408 Set py3 path in CI 2019-10-03 17:50:26 -05:00
ba48a817f2 Avoid printing filesystem root in CI environment 2019-10-03 17:45:31 -05:00
b06f28d3e6 Testing CI environment. #309 2019-10-03 17:34:42 -05:00
838e0c7b12 Added pyinstaller to requirements file. #309 2019-10-03 17:23:05 -05:00
08b34138e0 Added main specification file for PyInstaller (tested with Python 3.7.2). #309 2019-10-03 17:21:17 -05:00
073ccbf6c7 Updated requests fix to match with pyinstaller later. #309 2019-10-03 17:18:24 -05:00
a58187e5f7 Change call to app_path() to reflect latest changes when retrieving WX locales in frozen application. #309 2019-10-03 17:17:36 -05:00
6af39c6a2f Use shift instead of replace in arrow object for dm's. Closes #306 2019-10-03 10:33:04 -05:00
d97733fb1a API errors should be spoken from Twython. Fixes #310 2019-10-03 09:26:54 -05:00
80212fc61f Changed some calls to functions inside the paths module. Fixes #308 2019-08-15 09:51:31 -05:00
fe60ac8f67 Fixed issue in Global settings dialog, fixes #307 2019-08-15 09:46:47 -05:00
b6d5c9be63 Spell checker module should work properly. Fixes #296 2019-08-15 09:16:55 -05:00
e5a40288af Use shift() instead of replace() in arrow when displaying tweet's date. #306 2019-08-15 08:42:15 -05:00
bafb56651c remove explicitly some warnings present in arrow==0.14.5 2019-08-15 08:22:22 -05:00
29290b1468 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2019-08-13 17:53:06 -05:00
316a539a11 Docs can be generated from python 3.7 2019-08-13 17:39:05 -05:00
6320f0d93c Merge pull request #298 from yncat/yncat-jp-translation
Translated a few missing entries
2019-08-13 10:01:36 -05:00
6e12d70cd9 Merge branch 'python3' into next-gen 2019-08-13 09:57:24 -05:00
bca6861bcb Fixed error when displaying URL'S in multiline tweets. Fixes #305 2019-08-13 09:55:50 -05:00
José Manuel
60074919f6 Merge pull request #304 from Oreonan2/frcontributors-24072019
Added Corentin Bacqué-Cazenave to contributors.txt
2019-07-24 17:54:54 +02:00
Corentin
7f9e5e181a Added Corentin Bacqué-Cazenave to contributors.txt 2019-07-24 14:24:44 +02:00
0f2662e52d Merge pull request #303 from Oreonan2/frtr-22072019
update french translation
2019-07-23 11:02:15 -05:00
Corentin
5930470023 update files with conflicts 2019-07-22 17:28:59 +02:00
Corentin
56930a8921 update french translation 2019-07-22 16:17:56 +02:00
nyanchan
991f46e923 View in twitter entry's fuzzy tag has been removed to make the entry work actually 2019-07-06 23:54:58 +09:00
nyanchan
2ec47ad1e2 Translated a few missing entryes 2019-07-06 23:46:25 +09:00
ae81dd94a5 Fixed Python 3 issues when adding a new session 2019-07-05 20:17:58 -05:00
dcc4f4c782 Merge pull request #293 from manuelcortez/python3
Initial Python 3 compatible code
2019-07-01 17:43:25 -05:00
c65d149590 Fixed conflicts during the last stable update 2019-07-01 10:04:48 -05:00
2620348573 Returns the first available format retrieved by youtubeDL 2019-06-15 07:25:05 -05:00
Jose Manuel Delicado
ba1bcd550d Updated requirements to include more recent sound_lib dlls. Updated vlc to version 3.0.7.1 2019-06-15 12:53:15 +02:00
Jose Manuel Delicado
0dd082079e setup.py: include again all soundpacks. Include enchant dictionaries 2019-06-09 22:09:16 +02:00
Jose Manuel Delicado
7928cf5f24 Set snapshot variable to False 2019-06-06 22:12:06 +02:00
Jose Manuel Delicado
1333ebf859 Updated version to 0.95 2019-06-06 22:07:19 +02:00
Jose Manuel Delicado
ec3779147f Updated translations 2019-06-06 22:01:36 +02:00
ebd8e42154 Install a newer accessible_output2 package 2019-06-06 13:06:51 -05:00
a4f20872b0 Install latest pypubsub for Python 3 instead of the 3.3.0 version 2019-06-06 13:03:17 -05:00
ef689d04fc Initial Python 3 compatible code 2019-06-06 11:52:23 -05:00
Jose Manuel Delicado
d441536f01 Updated windows dependencies and readme 2019-05-02 20:26:33 +02:00
f976beb751 OCR should be performed in quoted tweets too 2019-04-08 05:41:49 -05:00
bed6142fa1 Remove builds on every commit 2019-04-08 05:11:41 -05:00
4c34099751 Removed unneeded file 2019-04-08 05:06:45 -05:00
Jose Manuel Delicado
e0db808dce Updated Windows dependencies. Please, run git submodule update 2019-03-17 15:05:52 +01:00
Jose Manuel Delicado
c500e8f513 Updated translation catalogs 2019-03-17 13:35:00 +01:00
José Manuel
7f86da1123 Merge pull request #286 from pauliyobo/next-gen
audio_services should now be completely independent  from the source …
2019-03-15 19:14:56 +01:00
aa4ebfff29 Replaced __gen_path__ with __build_path__ in fixes/fix_libloader.py 2019-02-28 08:34:30 -06:00
paul
ae4deb412f fixed other import issues derived from the removal of youtube_utils #273 2019-02-26 23:53:56 +01:00
paul
6eb27b655a deleted youtube_utils, and fixed various imports from the source code #273 2019-02-26 21:44:15 +01:00
paul
fe72fe97b5 audio_services should now be completely independent from the source code it's self. #273 2019-02-26 08:50:38 +01:00
c82ef1855c Apply fix_libloader before fix_win32com 2019-02-21 15:01:43 -06:00
fd43865105 Fixed errors when creating audio recordings 2019-02-20 17:21:27 -06:00
cf095ba13b Fixed keystroke for 'view in twitter' feature 2019-02-20 17:21:08 -06:00
092c5312cb Try to use a forked accessible_output2 to see how libloader fixes work on it #282 2019-02-20 14:13:29 -06:00
fc21b43475 Build a version for every commit. 2019-02-20 10:40:57 -06:00
3fce3c58e2 Added logging to fix_libloader. 2019-02-20 10:40:08 -06:00
207315937a Add files from accessible_output2 by using the right function 2019-02-19 23:04:45 -06:00
61d9180b2e Installs wx==4.0.3 due to issues present in 4.0.4 2019-02-19 23:03:37 -06:00
274 changed files with 59526 additions and 27304 deletions

3
.gitignore vendored
View File

@@ -19,4 +19,5 @@ src/sounds/iOs
release-snapshot/
src/com_cache/
doc/strings.py
doc/changelog.py
doc/changelog.py
env/

View File

@@ -24,45 +24,37 @@ This document describes how to run tw blue from source and how to build a binary
### Required dependencies.
Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash:
```
git submodule init
git submodule update
All the dependencies provided in this folder are prebuilt. If you want to build them from source, you will need Microsoft visual Studio 2008.
```
#### Dependencies packaged in windows installers
* [Python,](http://python.org) version 2.7.15
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python27 and python x64 to C:\python27x64, for example.
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
x64 version has been built by TWBlue developers, so you only will find it in windows-dependencies folder
The windows installers are available only in the windows-dependencies folder.
To build a binary version:
* [Py2exe](http://www.sourceforge.net/projects/py2exe/) for Python 2.7, version 0.6.9
* [Python,](https://python.org) version 3.8.7
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python38 and python x64 to C:\python38x64, for example.
#### Dependencies that must be installed using pip
Python installs a tool called Pip that allows to install packages in a simple way. You can find it in the python scripts directory. To install packages using Pip, you have to navigate to the scripts directory using a command prompt, for example:
cd C:\python27x64\scripts
`cd C:\python37x64\scripts`
You can also add the scripts folder to your path environment variable or choose the corresponding option when installing Python.
Note: pip and setuptools are included in the Python installer since version 2.7.9.
Pip is able to install packages listed in a special text file, called the requirements file. To install all remaining dependencies, perform the following command:
pip install -r requirements.txt
`pip install -r requirements.txt`
Note that if you perform the command from the path where Pip is located, you need to specify the path to your Tw Blue root folder where the requirements file is located, for example:
pip install -r D:\repos\TwBlue\requirements.txt
`pip install -r D:\repos\TwBlue\requirements.txt`
Pip will automatically get the additional libraries that the listed packages need to work properly.
If you need to update your dependencies, perform the following command:
pip install --upgrade -r requirements.txt
`pip install --upgrade -r requirements.txt`
#### Other dependencies
@@ -71,18 +63,18 @@ These dependencies are located in the windows-dependencies directory. You don't
* Bootstrap 1.2.1: included in dependencies directory.
This dependency has been built using pure basic 4.61. Its source can be found at http://hg.q-continuum.net/updater
* [oggenc2.exe,](http://www.rarewares.org/ogg-oggenc.php) version 2.87
* Microsoft Visual c++ 2008 redistributable dlls.
* Microsoft Visual c++ 2019 redistributable dlls.
* VLC plugins and DLL libraries.
#### Dependencies required to build the installer
* [NSIS,](http://nsis.sourceforge.net/) version 3.03
* [NSIS,](http://nsis.sourceforge.net/) version 3.04
#### Dependencies required to build the portableApps.com format archive
* [NSIS Portable,](http://portableapps.com/apps/development/nsis_portable) version 3.03
* [PortableApps.com Launcher,](http://portableapps.com/apps/development/portableapps.com_launcher) version 2.2.1
* [PortableApps.com Installer,](http://portableapps.com/apps/development/portableapps.com_installer) version 3.5.8
* [PortableApps.com Installer,](http://portableapps.com/apps/development/portableapps.com_installer) version 3.5.11
Important! Install these 3 apps into the same folder, otherwise you won't be able to build the pa.c version. For example: D:\portableApps\NSISPortable, D:\PortableApps\PortableApps.com installer, ...
@@ -94,7 +86,7 @@ In order to add the support for spell checking in more languages than english yo
Now that you have installed all these packages, you can run TW Blue from source using a command prompt. Navigate to the repo's `src` directory, and type the following command:
python main.py
`python main.py`
If necessary, change the first part of the command to reflect the location of your python executable. You can run TW Blue using python x86 and x64.
@@ -102,8 +94,8 @@ Now that you have installed all these packages, you can run TW Blue from source
To generate the documentation in html format, navigate to the doc folder inside this repo. After that, run these commands:
python document_importer.py
python generator.py
`python document_importer.py`
`python generator.py`
The documentation will be generated, placing each language in a separate folder in the doc directory. Move these folders (for example `de`, `en`, `es`, `fr`, `it`, ...) to `src/documentation`, creating the directory if necessary.
Also, copy the `license.txt` file located in the root of the repo to the documentation folder.
@@ -114,7 +106,7 @@ A binary version doesn't need python and the other dependencies to run, it's the
To build it, run the following command from the src folder:
python setup.py py2exe
`python setup.py build`
You will find the binaries in the dist directory.
@@ -122,9 +114,9 @@ To build it, run the following command from the src folder:
If you want to install TWBlue on your computer, you must create the installer first. Follow these steps:
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
* Navigate to the src directory, and create a binary version for x86: C:\python37\python setup.py build
* Move the dist directory to the scripts folder in this repo, and rename it to twblue
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
* Repeat these steps with Python for x64: C:\python37x64\python setup.py build
* Move the new dist directory to the scripts folder, and rename it to twblue64
* Go to the scripts folder, right click on the twblue.nsi file, and choose compyle unicode NSIS script
* This may take a while. After the process, you will find the installer in the scripts folder
@@ -137,9 +129,9 @@ Run the gen_pot.bat file, located in the tools directory. Your python installati
If you want to have TWBlue on your PortableApps.com platform, follow these steps:
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
* Navigate to the src directory, and create a binary version for x86: C:\python37\python setup.py build
* Move the dist directory to the misc\pa.c format\app folder in this repo, and rename it to twblue
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
* Repeat these steps with Python for x64: C:\python37x64\python setup.py build
* Move the new dist directory to the misc\pa.c format\app folder, and rename it to twblue64
* Run the PortableApps.com Launcher Generator, and follow the wizard. Choose the pa.c format folder and continue to generate the launcher. If the wizard is completed, you will see a file named TWBlue portable.exe inside the pa.c format folder.
* Run the PortableApps.com Installer, and follow the wizard. As in the above step, choose the pa.c format folder. When it completes, you will see a file named TWBluePortable_x.y.paf.exe inside the misc folder, where x.y is the version number.

View File

@@ -10,13 +10,13 @@ environment:
matrix:
# List of python versions we want to work with.
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
- PYTHON: "C:\\Python38"
PYTHON_VERSION: "3.8.x" # currently 2.7.9
PYTHON_ARCH: "32"
# perhaps we may enable this one in future?
# - PYTHON: "C:\\Python27-x64"
# PYTHON_VERSION: "2.7.x" # currently 2.7.9
# - PYTHON: "C:\\Python37-x64"
# PYTHON_VERSION: "3.7.x" # currently 2.7.9
# PYTHON_ARCH: "64"
# This is important so we will retrieve everything in submodules as opposed to default method.
@@ -37,10 +37,11 @@ install:
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
# - ECHO "Filesystem root:"
# - ps: "ls \"C:/\""
# Check that we have the expected version and architecture for Python
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
@@ -53,7 +54,7 @@ install:
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- "%CMD_IN_ENV% pip install -r requirements.txt"
- "%CMD_IN_ENV% pip install pyenchant py2exe_py2"
- "%CMD_IN_ENV% pip install pyenchant"
build_script:
# Build documentation at first, so setup.py won't fail when copying everything.
@@ -64,7 +65,7 @@ build_script:
- "cd ..\\src"
- "%CMD_IN_ENV% python ..\\doc\\generator.py"
# Build distributable files.
- "%CMD_IN_ENV% python setup.py py2exe"
- "%CMD_IN_ENV% python setup.py build"
- "cd dist"
# Zip it all.
- cmd: 7z a ..\..\snapshot.zip *
@@ -77,7 +78,7 @@ deploy:
host: twblue.es
protocol: ftp
beta: true
username: twblue
username: twblue.es
password:
secure: ml/xB8YEoZ7DmjzDr+KSNw==
# folder: '//pubs'
secure: lQZqpYRnHf4LLVOg0C42NQ==
folder: 'web/pubs'

View File

@@ -38,4 +38,6 @@ Burak Yüksek
florian Ionașcu
Christian Leo Mameli
Natalia Hedlund (Наталья Хедлунд)
Valeria (Валерия)
Valeria (Валерия)
Oreonan
Artem Plaksin (maniyax)

View File

@@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
name = 'TWBlue'
snapshot = False
if snapshot == False:
version = "0.94"
update_url = 'http://twblue.es/updates/twblue_ngen.json'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
else:
version = "10.99"
update_url = 'http://twblue.es/updates/snapshots_ngen.json'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
author = u"Manuel Cortéz"
authorEmail = "manuel@manuelcortez.net"
copyright = u"Copyright (C) 2013-2016, Manuel cortéz."
description = unicode(name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features.")
translators = [u"Bryner Villalobos, Bill Dengler (English)", u"Mohammed Al Shara (Arabic)", u"Joan Rabat, Juan Carlos Rivilla (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Juan Buño (Galician)", u"Steffen Schultz (German)", u"Robert Osztolykan (Hungarian)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Alexander Jaszyn (Russian)", u"Burak (Turkish)"]
url = u"http://twblue.es"
report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl"

View File

@@ -2,6 +2,33 @@
## changes in this version
* Added support for Twitter audio and videos: Tweets which contains audio or videos will be detected as audio items, and you can playback those with the regular command to play audios. ([#384,](https://github.com/manuelcortez/TWBlue/pull/384))
* We just implemented some changes in the way TWBlue handles tweets in order to reduce its RAM memory usage [#380](https://github.com/manuelcortez/TWBlue/pull/380):
* We reduced the tweets size by storing only the tweet fields we currently use. This should reduce tweet's size in memory for every object up to 75%.
* When using the cache database to store your tweets, there is a new setting present in the account settings dialog, in the general tab. This setting allows you to control whether TWBlue will load the whole database into memory (which is the current behaviour) or not.
* Loading the whole database into memory has the advantage of being extremely fast to access any element (for example when moving through tweets in a buffer), but it requires more memory as the tweet buffers grow up. This should, however, use less memory than before thanks to the optimizations performed in tweet objects. If you have a machine with enough memory, this should be a good option for your case.
* If you uncheck this setting, TWBlue will read the whole database from disk. This is significantly slower, but the advantage of this setting is that it will consume almost no extra memory, no matter how big is the tweets dataset. Be ware, though, that TWBlue might start to feel slower when accessing elements (for example when reading tweets) as the buffers grow up. This setting is suggested for computers with low memory or for those people not wanting to keep a really big amount of tweets stored.
* Changed the label in the direct message's text control so it will indicate that the user needs to write the text there, without referring to any username in particular. ([#366,](https://github.com/manuelcortez/TWBlue/issues/366))
* TWBlue will take Shift+F10 again as the contextual menu key in the list of items in a buffer. This stopped working after we have migrated to WX 4.1. ([#353,](https://github.com/manuelcortez/TWBlue/issues/353))
* TWBlue should render correctly retweets of quoted tweets. ([#365,](https://github.com/manuelcortez/TWBlue/issues/365))
* Fixed an error that was causing TWBlue to be unable to output to screen readers at times. ([#369,](https://github.com/manuelcortez/TWBlue/issues/369))
* Fixed autocomplete users feature. ([#367,](https://github.com/manuelcortez/TWBlue/issues/367))
* Fixed error when displaying an URL at the end of a line, when the tweet or direct message contained multiple lines. Now the URL should be displayed correctly. ([#305,](https://github.com/manuelcortez/TWBlue/issues/305) [#272,](https://github.com/manuelcortez/TWBlue/issues/272))
* TWBlue has been migrated completely to Python 3 (currently, the software builds with Python 3.8).
* TWBlue should be restarted gracefully. Before, the application was alerting users of not being closed properly every time the application restarted by itself.
* If TWBlue attemps to load an account with invalid tokens (this happens when reactivating a previously deactivated account, or when access to the ap is revoqued), TWBlue will inform the user about this error and will skip the account. Before, the app was unable to start due to a critical error. ([#328,](https://github.com/manuelcortez/TWBlue/issues/328))
* When sending a direct message, the title of the window will change appropiately when the recipient is edited. ([#276,](https://github.com/manuelcortez/TWBlue/issues/276))
* URL'S in user profiles are expanded automatically. ([#275,](https://github.com/manuelcortez/TWBlue/issues/275))
* TWBlue now uses [Tweepy,](https://github.com/tweepy/tweepy) to connect with Twitter. We have adopted this change in order to support Twitter'S API V 2 in the very near future. ([#333,](https://github.com/manuelcortez/TWBlue/issues/337) [#347](https://github.com/manuelcortez/TWBlue/pull/347))
* TWBlue can upload images in Tweets and replies again. ([#240,](https://github.com/manuelcortez/TWBlue/issues/240))
* Fixed the way we use to count characters in Twitter. The new methods in TWBlue take into account special characters and URLS as documented in Twitter. ([#199,](https://github.com/manuelcortez/TWBlue/issues/199) [#315](https://github.com/manuelcortez/TWBlue/issues/315))
* Proxy support now works as expected.
* Changed translation service from yandex.translate to Google Translator. ([#355,](https://github.com/manuelcortez/TWBlue/issues/355))
* Improved method to load direct messages in the buffers. Now it should be faster due to less calls to Twitter API performed from the client.
* And more. ([#352,](https://github.com/manuelcortez/TWBlue/issues/352))
## Changes in version 0.95
* TWBlue can open a Tweet or user directly in Twitter. There is a new option in the context menu for people and tweet buffers, and also, the shortcut control+win+alt+Enter will open the focused item in Twitter.
* Some keystrokes were remapped in the Windows 10 Keymap:
* Read location of a tweet: Ctrl+Win+G. ([#177](https://github.com/manuelcortez/TWBlue/pull/177))

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from codecs import open
""" This script converts the hold documentation (saved in markdown files) in a python file with a list of strings to translate it using gettext."""
def prepare_documentation_in_file(fileSource, fileDest):
@@ -6,8 +7,8 @@ def prepare_documentation_in_file(fileSource, fileDest):
@fileSource str: A markdown(.md) file.
@fileDest str: A file where this will put the new strings"""
f1 = open(fileSource, "r")
f2 = open(fileDest, "w")
f1 = open(fileSource, "r", encoding="utf-8")
f2 = open(fileDest, "w", encoding="utf-8")
lns = f1.readlines()
f2.write("# -*- coding: utf-8 -*-\n")
f2.write("documentation = [\n")
@@ -15,9 +16,9 @@ def prepare_documentation_in_file(fileSource, fileDest):
if "\n" == i:
newvar = "\"\","
elif "\n" == i[-1]:
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i[:-1])
newvar = "\"\"\"%s\"\"\",\n" % (i[:-1])
else:
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i)
newvar = "\"\"\"%s\"\"\",\n" % (i)
f2.write(newvar)
f1.close()
f2.write("]")

View File

@@ -1,32 +1,34 @@
# -*- coding: utf-8 -*-
import markdown
import gettext
import os
import locale
import paths
import markdown
import shutil
from codecs import open as _open
import languageHandler
languageHandler.setLanguage("en")
import strings
import changelog
from importlib import reload
def get_translation_function(name, language):
if language == "en":
return gettext.NullTranslations()
translation_function = gettext.translation(name, os.path.join(paths.app_path(), "locales"), languages=[language])
return translation_function
# the list of supported language codes of TW Blue
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da"]
#"eu", "ar", "ca", "es", "fi", "fr", "gl", "hu", "it", "pl", "pt", "ru", "tr"]
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro", "eu", "ca", "da", "sr"]
def generate_document(language, document_type="documentation"):
reload(languageHandler)
if document_type == "documentation":
translation_file = "twblue-documentation"
languageHandler.setLanguage(language, translation_file)
reload(strings)
markdown_file = markdown.markdown("\n".join(strings.documentation[1:]), extensions=["markdown.extensions.toc"])
title = strings.documentation[0]
translation_function = get_translation_function(translation_file, language)
markdown_file = markdown.markdown("\n".join([translation_function.gettext(s[:-1]) if s != "\n" else s for s in strings.documentation[1:]]), extensions=["markdown.extensions.toc"])
title = translation_function.gettext(strings.documentation[0][:-1])
filename = "manual.html"
elif document_type == "changelog":
translation_file = "twblue-changelog"
languageHandler.setLanguage(language, translation_file)
reload(changelog)
markdown_file = markdown.markdown("\n".join(changelog.documentation[1:]), extensions=["markdown.extensions.toc"])
title = changelog.documentation[0]
translation_function = get_translation_function(translation_file, language)
markdown_file = markdown.markdown("\n".join([translation_function.gettext(s[:-1]) if s != "\n" else s for s in changelog.documentation[1:]]), extensions=["markdown.extensions.toc"])
title = translation_function.gettext(changelog.documentation[0][:-1])
filename = "changelog.html"
first_html_block = """<!doctype html>
<html lang="%s">
@@ -53,8 +55,13 @@ def create_documentation():
shutil.copy(os.path.join("..", "license.txt"), os.path.join("documentation", "license.txt"))
for i in languages:
print("Creating documentation for: %s" % (i,))
generate_document(i)
generate_document(i, "changelog")
try:
generate_document(i)
generate_document(i, "changelog")
except:
continue
print("Done")
import strings
import changelog
create_documentation()

View File

@@ -1,176 +0,0 @@
import __builtin__
import os
import sys
import ctypes
import locale
import gettext
#import paths
import platform
# A fix for the mac locales
if platform.system() != 'Windows':
if locale.getlocale()[0] is None:
locale.setlocale(locale.LC_ALL, 'en_US')
#a few Windows locale constants
LOCALE_SLANGUAGE=0x2
LOCALE_SLANGDISPLAYNAME=0x6f
curLang="en"
def localeNameToWindowsLCID(localeName):
"""Retreave the Windows locale identifier (LCID) for the given locale name
@param localeName: a string of 2letterLanguage_2letterCountry or or just 2letterLanguage
@type localeName: string
@returns: a Windows LCID
@rtype: integer
"""
#Windows Vista is able to convert locale names to LCIDs
func_LocaleNameToLCID=getattr(ctypes.windll.kernel32,'LocaleNameToLCID',None)
if func_LocaleNameToLCID is not None:
localeName=localeName.replace('_','-')
LCID=func_LocaleNameToLCID(unicode(localeName),0)
else: #Windows doesn't have this functionality, manually search Python's windows_locale dictionary for the LCID
localeName=locale.normalize(localeName)
if '.' in localeName:
localeName=localeName.split('.')[0]
LCList=[x[0] for x in locale.windows_locale.iteritems() if x[1]==localeName]
if len(LCList)>0:
LCID=LCList[0]
else:
LCID=0
return LCID
def getLanguageDescription(language):
"""Finds out the description (localized full name) of a given local name"""
desc=None
if platform.system() == "Windows":
LCID=localeNameToWindowsLCID(language)
if LCID!=0:
buf=ctypes.create_unicode_buffer(1024)
if '_' not in language:
res=ctypes.windll.kernel32.GetLocaleInfoW(LCID,LOCALE_SLANGDISPLAYNAME,buf,1024)
else:
res=0
if res==0:
res=ctypes.windll.kernel32.GetLocaleInfoW(LCID,LOCALE_SLANGUAGE,buf,1024)
desc=buf.value
elif platform.system() == "Linux" or not desc:
desc={
"am":pgettext("languageName","Amharic"),
"an":pgettext("languageName","Aragonese"),
"es":pgettext("languageName","Spanish"),
"pt":pgettext("languageName","Portuguese"),
"ru":pgettext("languageName","Russian"),
"it":pgettext("languageName","italian"),
"tr":pgettext("languageName","Turkey"),
"gl":pgettext("languageName","Galician"),
"ca":pgettext("languageName","Catala"),
"eu":pgettext("languageName","Vasque"),
"pl":pgettext("languageName","polish"),
"ar":pgettext("languageName","Arabic"),
"ne":pgettext("languageName","Nepali"),
"sr":pgettext("languageName","Serbian (Latin)"),
}.get(language,None)
return desc
def getAvailableLanguages():
"""generates a list of locale names, plus their full localized language and country names.
@rtype: list of tuples
"""
#Make a list of all the locales found in NVDA's locale dir
l=[x for x in os.listdir("locales") if not x.startswith('.')]
l=[x for x in l if os.path.isfile('locales/%s/LC_MESSAGES/twblue-documentation.mo' % x)]
#Make sure that en (english) is in the list as it may not have any locale files, but is default
if 'en' not in l:
l.append('en')
l.sort()
#For each locale, ask Windows for its human readable display name
d=[]
for i in l:
desc=getLanguageDescription(i)
label="%s, %s"%(desc,i) if desc else i
d.append(label)
#include a 'user default, windows' language, which just represents the default language for this user account
l.append("system")
# Translators: the label for the Windows default NVDA interface language.
d.append(_("User default"))
#return a zipped up version of both the lists (a list with tuples of locale,label)
return zip(l,d)
def makePgettext(translations):
"""Obtaina pgettext function for use with a gettext translations instance.
pgettext is used to support message contexts,
but Python 2.7's gettext module doesn't support this,
so NVDA must provide its own implementation.
"""
if isinstance(translations, gettext.GNUTranslations):
def pgettext(context, message):
message = unicode(message)
try:
# Look up the message with its context.
return translations._catalog[u"%s\x04%s" % (context, message)]
except KeyError:
return message
else:
def pgettext(context, message):
return unicode(message)
return pgettext
def setLanguage(lang, translation_file="twblue-documentation"):
system = platform.system()
global curLang
try:
if lang=="system":
if system == "Windows":
windowsLCID=ctypes.windll.kernel32.GetUserDefaultUILanguage()
localeName=locale.windows_locale[windowsLCID]
else:
localeName=locale.getlocale()[0]
trans=gettext.translation(translation_file, localedir="locales", languages=[localeName])
curLang=localeName
else:
trans=gettext.translation(translation_file, localedir="locales", languages=[lang])
curLang=lang
localeChanged=False
#Try setting Python's locale to lang
try:
locale.setlocale(locale.LC_ALL, lang)
localeChanged=True
except:
pass
if not localeChanged and '_' in lang:
#Python couldn'tsupport the language_country locale, just try language.
try:
locale.setlocale(locale.LC_ALL, lang.split('_')[0])
except:
pass
#Set the windows locale for this thread (NVDA core) to this locale.
if system == "Windows":
LCID=localeNameToWindowsLCID(lang)
ctypes.windll.kernel32.SetThreadLocale(LCID)
except IOError:
trans=gettext.translation(translation_file,fallback=True)
curLang="en"
trans.install(unicode=True)
# Install our pgettext function.
__builtin__.__dict__["pgettext"] = makePgettext(trans)
def getLanguage():
return curLang
def normalizeLanguage(lang):
"""
Normalizes a language-dialect string in to a standard form we can deal with.
Converts any dash to underline, and makes sure that language is lowercase and dialect is upercase.
"""
lang=lang.replace('-','_')
ld=lang.split('_')
ld[0]=ld[0].lower()
#Filter out meta languages such as x-western
if ld[0]=='x':
return None
if len(ld)>=2:
ld[1]=ld[1].upper()
return "_".join(ld)

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TW Blue documentation 0.89\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2017-03-25 15:15+0100\n"
"Last-Translator: Joan Rabat <joanrabat@hotmail.com>\n"
"Language-Team: Francisco Torres Gallego <frantorresgallego@gmail.com>\n"

View File

@@ -5,16 +5,16 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"PO-Revision-Date: 2017-12-11 09:17-0600\n"
"Last-Translator: \n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-11-17 10:25+0100\n"
"Last-Translator: Nicolai Svendsen <chojiro1990@gmail.com>\n"
"Language-Team: \n"
"Language: da\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 2.0.2\n"
"X-Generator: Poedit 2.2\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../doc/strings.py:3
@@ -110,7 +110,6 @@ msgid "## Usage"
msgstr "## Brug"
#: ../doc/strings.py:21
#, fuzzy
msgid ""
"Twitter is a social networking or micro-blogging tool which allows you to "
"compose short status updates of your activities in 280 characters or less. "
@@ -120,7 +119,7 @@ msgid ""
"anyone to access them."
msgstr ""
"Twitter er et socialt netværk eller mikro-blogging-værktøj, der giver dig "
"mulighed for at skrive korte statusopdateringer om dine aktiviteter i 140 "
"mulighed for at skrive korte statusopdateringer om dine aktiviteter på 280 "
"tegn eller mindre. Twitter er en måde for venner, familie og kollegaer til "
"at kommunikere og holde kontakten gennem udveksling af hurtige, hyppige "
"meddelelser. Du kan begrænse levering af opdateringer til dem i din cirkel "
@@ -170,7 +169,7 @@ msgstr ""
#: ../doc/strings.py:25
msgid "### Authorising the application"
msgstr "### Tillad applikationen"
msgstr "### Godkend applikationen"
#: ../doc/strings.py:26
msgid ""
@@ -222,8 +221,8 @@ msgid ""
"Enter your username and password into the appropriate edit fields if you're "
"not already logged in, select the authorise button, and press it."
msgstr ""
"Standardbrowseren åbnes på Twitter-siden for at anmode om tilladelse. "
"Indtast dit Brugernavn og adgangskode i de passende redigeringsfelterne hvis "
"Standardbrowseren åbnes på Twitter-siden for at anmode om godkendelse. "
"Indtast dit Brugernavn og adgangskode i de passende redigeringsfelter, hvis "
"du ikke allerede er logget ind. Vælg så knappen Godkend, og tryk den."
#: ../doc/strings.py:29
@@ -266,7 +265,7 @@ msgid ""
"sound, and the screen reader will say \"ready\" (this behaviour can be "
"configured)."
msgstr ""
"Når processen er afsluttet, vil programmet afspilles en anden lyd som "
"Når processen er afsluttet, vil programmet afspille en anden lyd som "
"standard og skærmlæseren vil sige “Klar” (denne adfærd kan blive "
"konfigureret)."
@@ -352,7 +351,7 @@ msgstr "* Sendte tweets: Denne viser alle tweets sendt fra din konto."
#: ../doc/strings.py:43
msgid "* Likes: here you will see all the tweets you have liked."
msgstr "* Synes godt om: Her vil du se alle de tweets, du har synes godt om."
msgstr "* Synes godt om: Her vil du se alle de tweets, du synes godt om."
#: ../doc/strings.py:44
msgid ""
@@ -422,7 +421,7 @@ msgid ""
"geographical region. This region may be a country or a city. Trends are "
"updated every five minutes."
msgstr ""
"* Trending emner: Denne buffer viser top ti mest anvendte udtryk i et "
"* Trending emner: Denne buffer viser top ti mest anvendte ord i et "
"geografisk område. Dette område kan være et land eller en by. Trends "
"opdateres hvert femte minut."
@@ -494,8 +493,8 @@ msgid ""
"* a menu bar accomodating six menus (application, tweet, user, buffer, "
"audio and help);"
msgstr ""
"* en menu bar der består af seks menuer (Applikation, tweet, bruger, buffer, "
"lyd og hjælp);"
"* en menulinje der består af seks menuer (Applikation, tweet, bruger, "
"buffer, lyd og hjælp);"
#: ../doc/strings.py:59
msgid "* One tree view,"
@@ -533,7 +532,6 @@ msgid "#### Buttons in the application"
msgstr "### Knapper i programmet"
#: ../doc/strings.py:65
#, fuzzy
msgid ""
"* Tweet: this button opens up a dialogue box to write your tweet. Normal "
"tweets must not exceed 280 characters. However you can press the long tweet "
@@ -550,7 +548,7 @@ msgid ""
"message in English describing the problem."
msgstr ""
"* Tweet: Denne knap åbner en dialogboks hvor du kan skrive dit tweet. "
"Normale tweets må ikke overstige 140 tegn. Du kan dog markere checkboksen "
"Normale tweets må ikke overstige 280 tegn. Du kan dog markere checkboksen "
"“Lang tweet”, og dit tweet vil blive sendt gennem Twishort, som tillader dig "
"at skrive længere tweets (10000 tegn). Hvis du skriver og overstiger denne "
"grænse, afspilles en lyd for at advare dig. Bemærk at antallet af indtastede "
@@ -1762,14 +1760,12 @@ msgid "## Credits"
msgstr "## Krediteringer"
#: ../doc/strings.py:238
#, fuzzy
msgid ""
"TWBlue is developed and maintained by [Manuel Cortéz](https://twitter.com/"
"manuelcortez00) and [José Manuel Delicado](https://twitter.com/jmdaweb)."
msgstr ""
"TWBlue er udviklet og vedligeholdt af [Manuel Cort\\303\\251z](https://"
"twitter.com/manuelcortez00) og [Jos\\303\\251 Manuel Delicado](https://"
"twitter.com/jmdaweb)."
"TWBlue et udviklet og vedligeholdt af [Manuel Cortéz](https://twitter.com/"
"manuelcortez00) and [José Manuel Delicado](https://twitter.com/jmdaweb)."
#: ../doc/strings.py:239
msgid ""
@@ -1796,31 +1792,24 @@ msgid "* Catalan: [Francisco Torres](https://twitter.com/ftgalleg)"
msgstr "* Catalansk: [Francisco Torres](https://twitter.com/ftgalleg)"
#: ../doc/strings.py:243
#, fuzzy
msgid "* Croatian: [Zvonimir Stanečić](https://twitter.com/zvonimirek222)."
msgstr ""
"* Kroatisk: [Zvonimir Stane\\304\\215i\\304\\207](https://twitter.com/"
"zvonimirek222)."
msgstr "* Kroatisk: [Zvonimir Stanečić](https://twitter.com/zvonimirek222)."
#: ../doc/strings.py:244
#, fuzzy
msgid "* English: [Manuel Cortéz](https://twitter.com/manuelcortez00)."
msgstr ""
"* Engelsk: [Manuel Cort\\303\\251z](https://twitter.com/manuelcortez00)."
msgstr "* Engelsk: [Manuel Cortéz](https://twitter.com/manuelcortez00)."
#: ../doc/strings.py:245
msgid "* Finnish: [Jani Kinnunen](https://twitter.com/jani_kinnunen)."
msgstr "* Finsk: [Jani Kinnunen](https://twitter.com/jani_kinnunen)."
#: ../doc/strings.py:246
#, fuzzy
msgid "* French: [Rémy Ruiz](https://twitter.com/blindhelp38)."
msgstr "* Fransk: [R\\303\\251my Ruiz](https://twitter.com/blindhelp38)."
msgstr "* Fransk: [Rémy Ruiz](https://twitter.com/blindhelp38)."
#: ../doc/strings.py:247
#, fuzzy
msgid "* Galician: [Juan Buño](https://twitter.com/Quetzatl_)."
msgstr "* Galicisk: [Juan Bu\\303\\261o](https://twitter.com/Quetzatl_)."
msgstr "* Galicisk: [Juan Buño](https://twitter.com/Quetzatl_)."
#: ../doc/strings.py:248
msgid "* German: [Steffen Schultz](https://twitter.com/schulle4u)."
@@ -1843,44 +1832,37 @@ msgid "* Polish: [Pawel Masarczyk.](https://twitter.com/Piciok)"
msgstr "* Polsk: [Pawel Masarczyk.](https://twitter.com/Piciok)"
#: ../doc/strings.py:253
#, fuzzy
msgid "* Portuguese: [Odenilton Júnior Santos.](https://twitter.com/romaleif)"
msgstr ""
"* Portugisisk: [Odenilton J\\303\\272nior Santos.](https://twitter.com/"
"romaleif)"
"* Portugisisk: [Odenilton Júnior Santos.](https://twitter.com/romaleif)"
#: ../doc/strings.py:254
#, fuzzy
msgid ""
"* Romanian: [Florian Ionașcu](https://twitter.com/florianionascu7) and "
"[Nicușor Untilă](https://twitter.com/dj_storm2001)"
msgstr ""
"* Rumænsk: [Florian Iona\\310\\231cu](https://twitter.com/florianionascu7) "
"og [Nicu\\310\\231or Until\\304\\203](https://twitter.com/dj_storm2001)"
"* Rumænsk: [Florian Ionașcu](https://twitter.com/florianionascu7) and "
"[Nicușor Untilă](https://twitter.com/dj_storm2001)"
#: ../doc/strings.py:255
msgid ""
"* Russian: [Наталья Хедлунд](https://twitter.com/Lifestar_n) and [Валерия "
"Кузнецова](https://twitter.com/ValeriaK305)."
msgstr ""
"* Russisk: [Наталья Хедлунд](https://twitter.com/Lifestar_n) and [Валерия "
"Кузнецова](https://twitter.com/ValeriaK305)."
#: ../doc/strings.py:256
#, fuzzy
msgid "* Serbian: [Aleksandar Đurić](https://twitter.com/sokodtreshnje)"
msgstr ""
"* Serbisk: [Aleksandar \\304\\220uri\\304\\207](https://twitter.com/"
"sokodtreshnje)"
msgstr "* Serbisk: [Aleksandar Đurić](https://twitter.com/sokodtreshnje)"
#: ../doc/strings.py:257
#, fuzzy
msgid "* Spanish: [Manuel Cortéz](https://twitter.com/manuelcortez00)."
msgstr ""
"* Spansk: [Manuel Cort\\303\\251z](https://twitter.com/manuelcortez00)."
msgstr "* Spansk: [Manuel Cortéz](https://twitter.com/manuelcortez00)."
#: ../doc/strings.py:258
#, fuzzy
msgid "* Turkish: [Burak Yüksek](https://twitter.com/burakyuksek)."
msgstr "* Tyrkisk: [Burak Y\\303\\274ksek](https://twitter.com/burakyuksek)."
msgstr "* Tyrkisk: [Burak Yüksek](https://twitter.com/burakyuksek)."
#: ../doc/strings.py:259
msgid ""
@@ -1910,9 +1892,8 @@ msgid "------------------------------------------------------------------------"
msgstr "————————————————————————————————————"
#: ../doc/strings.py:261
#, fuzzy
msgid "Copyright © 2013-2017. Manuel Cortéz"
msgstr "Copyright \\302\\251 2013-2017. Manuel Cort\\303\\251z"
msgstr "Copyright © 2013-2017. Manuel Cortéz"
#~ msgid ""
#~ "* Russian: "

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TWBlue Documentation\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-08 11:01+0200\n"
"Last-Translator: Steffen Schultz <schulle3o@yahoo.de>\n"
"Language-Team: Steffen Schultz <schulle3o@yahoo.de>\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TW Blue documentation 0.46\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2015-11-27 08:33-0600\n"
"Last-Translator: Manuel Cortéz <manuel@manuelcortez.net>\n"
"Language-Team: Sukil Echenique <sukiletxe@yahoo.es>\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TW Blue documentation 0.88\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-11 12:25+0200\n"
"Last-Translator: Rémy Ruiz <remyruiz@gmail.com>\n"
"Language-Team: Rémy Ruiz <remyruiz@gmail.com>\n"

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: tw blue documentation 0.46\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-08 11:34+0100\n"
"Last-Translator: Juan C. Buño <oprisniki@gmail.com>\n"
"Language-Team: Alba Quinteiro <alba_080695@hotmail.com>\n"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TW Blue documentation 0.46\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-09 11:51+0100\n"
"Last-Translator: Chris Leo Mameli <llajta2012@gmail.com>\n"
"Language-Team: \n"

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-08 19:09+0900\n"
"Last-Translator: Masamitsu Misono <misono@nvsupport.org>\n"
"Language-Team: NVDA Help Desk <nvdahelp@center-aikoh.net>\n"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

285
doc/locales/pt/manual.md Normal file
View File

@@ -0,0 +1,285 @@
% Documentação do TW Blue 0.42
# Versão 0.42 (alpha)
# ¡Perigro!
Você está lendo um documento gerado para uma aplicação em fase de desenvolvimento. A intenção deste manual é esclarecer alguns detalhes sobre o funcionamento do programa. Note-se que sendo desenvolvido ativamente, o software pode mudar um pouco em relação a esta documentação num futuro próximo. Por isso é aconselhável dar uma olhada de vez em quando para não se perder muito.
Si quieres ver lo que ha cambiado con respecto a la versión anterior, [lee la lista de novedades aquí.](changes.html)
# TW Blue
TW Blue é um aplicativo para utilizar o Twitter de forma simples e rápida, além de evitar tanto quanto possível consumir demasiados recursos do computador. Com ele é possível realizar ações do Twitter, tais como:
* Crear, responder, reenviar y eliminar Tuits,
* Marcar como favorito, eliminar de tus favoritos un tuit,
* Enviar y eliminar mensajes directos,
* Ver tus amigos y seguidores,
* Seguir, dejar de seguir, reportar como spam y bloquear a un usuario,
* Abrir una línea temporal para un usuario, lo que permite obtener todos los Tuits de ese usuario únicamente,
* Abrir direcciones URL cuando vayan en un tuit o mensaje directo,
* Reproducir varios tipos de archivos o direcciones que contengan audio.
* Y más.
# Tabla de contenidos
Para poder utilizar una aplicación como TW Blue que te permita gestionar una cuenta de Twitter, primero tienes que estar registrado en esta red social. Esta documentación no tiene como objetivo explicar el procedimiento para hacerlo. Partiremos desde el punto que tienes una cuenta con su respectivo nombre de usuario y contraseña. La documentación cubrirá estas secciones.
* [Autorizar la aplicación](#autorizar)
* [La interfaz del programa](#interfaz)
* [Controles](#controles)
* [La interfaz gráfica (GUI)](#gui)
* [Botones de la aplicación](#botones)
* [Menús](#menus)
* [Menú aplicación](#app)
* [Menú Tuit](#tuit)
* [Menú Usuario](#usuario)
* [Menú buffer](#buffer)
* [Menú ayuda](#ayuda)
* [La interfaz No Visible](#interfaz_no_visible)
* [Atajos de Teclado para la Interfaz Gráfica](#atajos)
* [Atajos de Teclado para la Interfaz no Visible](#atajos_invisibles)
* [Listas](#listas)
* [Reportando Errores desde la web](#reportar)
* [Contacto](#contacto)
## Autorizando la aplicación {#autorizar}
Antes de nada, lo primero que se necesita es autorizar al programa para que este pueda acceder a tu cuenta de Twitter, y desde ella realizar lo que le pidas. El proceso de autorización es bastante sencillo, y en ningún momento el programa podrá tener acceso a tus datos como usuario y contraseña. Para autorizar la aplicación, solo tienes que abrir el archivo principal del programa, llamado TW Blue.exe (en algunos PC, solo se muestra como TW Blue).
Al hacerlo, si no has configurado ninguna vez el programa, se mostrará un cuadro de diálogo donde te informa que serás llevado a Twitter para autorizar la aplicación una vez pulses sobre "aceptar". Para empezar con el proceso de autorización presiona sobre el único botón de ese diálogo.
A continuación, tu navegador predeterminado se abrirá con la página de Twitter solicitándote autorizar la aplicación. Escribe, si no estás autenticado ya, tu nombre de usuario y contraseña, luego busca el botón autorizar, y presiónalo.
De la página a la que serás redirigido (si el proceso ha tenido éxito), busca las instrucciones que te proporciona Twitter. En resumen, te dará un código numérico de varios dígitos que deberás pegar en un cuadro de texto que la aplicación ha abierto en otra ventana.
Pega el código de verificación, y pulsa la tecla Intro.
Si todo ha salido bien, la aplicación empezará a reproducir un grupo de sonidos en señal que se están actualizando tus datos.
Cuando termine, el programa reproducirá otro sonido, y el lector de pantalla dirá "listo".
## La interfaz del programa {#interfaz}
La forma más simple de describir la interfaz gráfica de la aplicación es la de una ventana con una barra de menú con cinco menús (aplicación, tuit, usuario, buffer y ayuda); una lista de varios elementos y en la mayoría de los casos tres botones. Tuit, retuit y responder. Las acciones para cada uno de estos elementos serán descritas más adelante.
Los elementos que hay en las listas pueden ser Tuits, mensajes directos o usuarios. TW Blue crea diferentes pestañas para cada lista, pues estos elementos pueden ser Tuits enviados, Tuits recividos en la línea principal, favoritos, o mensajes directos, y cada pestaña tiene un solo tipo de Tuit. Estas pestañas se llaman listas o buffers.
Para cambiar entre las listas se hace presionando Control+Tab si se desea avanzar, y Control+Shift+Tab para retroceder. En todo momento los lectores de pantalla anunciarán la lista hacia la que se cambie el foco de la aplicación. Aquí están las listas básicas de TW Blue, que aparecen si se usa la configuración por defecto.
* Principal: Aquí van todos los Tuits que se muestran en la línea principal. Estos son los Tuits de los usuarios a los que sigues.
* Menciones: Si un usuario (lo sigas o no) te menciona en Twitter, lo verás en esta lista.
* Mensajes directos: Aquí están los mensajes directos (privados) que intercambias con los usuarios que sigues y te siguen. Esta lista solo muestra los mensajes recividos.
* Enviados: En esta lista se muestran todos los Tuits y mensajes directos que se han enviado desde tu cuenta.
* Favoritos: Aquí verás los Tuits que has marcado como favoritos.
* Seguidores: Cuando los usuarios sigan tu cuenta, podrás verlos en esta lista, junto con un poco de información de la cuenta.
* Amigos: Igual que la lista anterior, pero estos usuarios son a los que tú sigues.
* Eventos: Un evento en TW Blue es "algo" que pase en Twitter. En la línea de eventos, podrás ver registrados los eventos más comunes (p. Ej. Te han comenzado a seguir, han marcado o removido un tweet tuyo de los favoritos, te has suscrito a una lista). Son como pequeñas notificaciones que envía Twitter y TW Blue organiza para que no te pierdas lo que ha pasado con tu cuenta.
* Línea temporal de un usuario: Estas son listas que tú deberás crear. Es una lista que contiene únicamente los Tuits de un usuario. Se usan si algún día necesitas o quieres ver los Tuits que ha realizado solo una persona y no deseas buscar por todo tu timeline. Puedes crear tantas como usuarios necesites.
* Lista: Una lista es parecida a una línea temporal, pero compuesta por los tweets de cada usuario que forme parte de ella. De momento las listas son una característica experimental de TW Blue. Si experimentas problemas con ellas, por favor escríbenos para contárnoslo.
* Búsqueda: Un buffer de búsqueda contiene los resultados de una búsqueda hecha en TW Blue. Las búsquedas pueden ser por tuits, en cuyo caso buscas un término en los tuits relevantes de Twitter, o por usuarios, donde los resultados son nombres de usuario de Twitter.
* Favoritos de un usuario: Es posible pedirle a TW Blue que te muestre los tuits que un usuario ha marcado como favoritos.
Nota: Únicamente para esta versión de TW Blue, los amigos y seguidores actualizarán hasta 400, o cerca a los 400. En la próxima versión proporcionaremos un método para ver los amigos y seguidores sin exponerse tanto a los errores causados por el uso de la API de Twitter, muy frecuente entre personas con más de 600 amigos o seguidores.
Ten en cuenta que por defecto la configuración solo permite obtener los 200 últimos Tuits para las listas principal, menciones, mensajes directos y líneas temporales. Esto puedes cambiarlo desde el diálogo de configuración. Para los enviados se obtendrán los últimos 200 Tuits y 200 mensajes directos. En versiones futuras se permitirá ajustar este parámetro.
Si hay una dirección URL en algún tuit, TW Blue intentará abrirla cuando presiones Intro sobre ella. Si hay más de una, te mostrará una lista con todas para que selecciones la que quieras abrir. Si estás en el cuadro de diálogo de los amigos o seguidores, la tecla intro te mostrará detalles del mismo.
Si pulsas Control+Intro, TW Blue intentará reproducir el audio que tenga el tuit sobre el que está el foco del sistema, siempre que tenga una URL. Si el tuit lleva la etiqueta #audio, un sonido al pasar por él te alertará que es un audio y puedes intentar reproducirlo. No obstante, también puede que no esté etiquetado y que TW Blue pueda reproducirlo, siempre que lleve a una dirección URL donde exista audio.
## Controles {#controles}
A partir de la versión 0.36, existe soporte para una interfaz que no requiere de una ventana visible. Esta puede ser activada pulsando Control+m, o seleccionando desde el menú aplicación la opción "Esconder ventana". Esta interfaz se maneja completamente con atajos de teclado. Estos atajos son diferentes a los que se utilizan para la interfaz gráfica. Cada una de ellas podrá utilizar solo los atajos que le correspondan, lo que quiere decir que no se permitirá utilizar los atajos de la interfaz no visible si se tiene activada la interfaz gráfica. En esta sección se detallará tanto la interfaz gráfica como la no visible.
### Interfaz gráfica (GUI) {#gui}
Aquí una lista dividida en dos partes. Por un lado, los botones que encontrarás si presionas Tab o Shift+Tab en la interfaz del programa, y por otro, los diferentes elementos que hay en la barra de menú.
#### Botones de la aplicación {#botones}
* Twit: Este botón abre el diálogo para escribir un tuit. El mensaje solo debe tener 140 caracteres. Al escribir el caracter número 141, un sonido será reproducido para indicarte que te has pasado del límite permitido por Twitter. Puedes querer acortar o desacortar una URL si la incluye tu tuit a fin de ganar más espacio donde escribir, para eso están los botones con esos nombres. Pulsa Intro para enviar el tuit. Si todo sale bien, el mensaje se enviará y tú escucharás un sonido que te lo confirme, si no, el lector de pantalla te responderá con un error en inglés, que indica por qué no se ha podido enviar el mensaje.
* Retuit: Este botón se encarga de reenviar el tuit sobre el que estás leyendo. Al presionarlo se te preguntará si deseas añadirle un comentario al tuit original (citándolo) o simplemente enviarlo como se ha escrito sin añadir nada más.
* Responder: Cuando estés visualizando un Tuit, puedes responderle al usuario que lo escribió pulsando sobre este botón. Se abrirá el mismo diálogo de Tuit, pero con el nombre del usuario (por ejemplo @usuario) en el, para que solo escribas el mensaje que quieres responderle. Si en el tuit hay más de un usuario mencionado, pulsa Shift+Tab y pulsa el botón "Mencionar a todos los usuarios". Cuando estés en la lista de amigos o seguidores, este botón se llamará mencionar.
* mensaje directo: Exactamente igual que enviar un Tuit, pero es un mensaje privado que solo podrá ver el usuario al que se lo envías. Pulsa Shift+Tab para ver el destinatario de tu mensaje. Si en el Tuit donde estabas para enviar el mensaje había más de un usuario mencionado, puedes navegar con las flechas de arriba y abajo para seleccionar otro, o escribir tú mismo el usuario (sin el signo de arroba).
Ten en cuenta que los botones aparecerán según las acciones que se puedan hacer en la lista donde estés. Por ejemplo, en la línea principal, menciones, enviados, favoritos y las líneas temporales de los usuarios podrás ver los cuatro botones; mientras que en la lista de mensajes directos solo estará disponible el botón de "Mensaje Directo" y "tuit", y en las listas de amigos y seguidores, se verá el botón para "Twit" y el de "Mensaje directo" junto a "mencionar".
#### Menús {#menus}
En la parte superior de la ventana del programa podrás encontrar una barra de menú que hace las mismas cosas, y algunas cuantas más. A la barra de menú se accede presionando la tecla ALT, y cuenta en este momento con cuatro menús para diferentes acciones: Aplicación, Tuit, usuario y Ayuda. En esta sección se describen las acciones para cada uno de ellos.
##### Menú aplicación {#app}
* Actualizar Perfil: Abre un diálogo desde donde se podrá actualizar parte de tu información en Twitter. Nombre, ubicación, dirección URL y descripción. Si ya tienes alguno de estos campos actualmente en el perfil se llenarán automáticamente con lo que tiene tu configuración de Twitter. También podrás subir una foto a tu perfil.
* Esconder Ventana: Desactiva la interfaz gráfica. Lee el apartado sobre la interfaz no visible para más detalles sobre este comportamiento.
* Búsqueda: Muestra un cuadro de diálogo desde donde puedes buscar por tuits o por usuarios en twitter.
* Gestor de listas: Para poder utilizar las listas de Twitter, primero necesitarás crearlas. Este diálogo permite ver tus listas, editarlas, crearlas, borrarlas y, opcionalmente, verlas en buffers tal como lo harías con las líneas temporales.
* Tutorial de sonidos: Abre un diálogo donde verás una lista de los sonidos de TW blue, para que puedas aprenderlos y no te cueste trabajo familiarizarte con TW Blue.
* Preferencias: Abre un diálogo de configuración desde donde se pueden controlar algunos aspectos del programa. Las opciones no necesitan de explicación.
* Salir: pregunta si quieres salir o no del programa. Si la respuesta es que sí, cierra la aplicación.
##### Menú Tuit {#tuit}
* Las primeras opciones del menú son Twit, responder y retuit, que corresponden a los botones del mismo nombre.
* Marcar como favorito: Marca el tuit que estés viendo como favorito.
* Quitar tuit de favoritos: Elimina el tuit de tus favoritos. Esto no significa que se borra de Twitter, solo deja de estar en tu lista de favoritos.
* Ver Tuit: Abre un diálogo donde puedes ver el Tuit, mensaje directo, amigo o seguidor sobre el que esté el foco de la aplicación. Puedes leer el texto con los cursores. El diálogo es el mismo que el que se usa para escribir un Tuit.
* Eliminar: Elimina el Tuit o mensaje directo sobre el que estés, borrándolo definitivamente de Twitter y qitándolo de tus listas. Ten en cuenta que en el caso de los Tuits, Twitter solo permite borrar los que tú mismo has escrito.
##### Menú usuario {#usuario}
Ten en cuenta que las primeras seis opciones de este menú abren un mismo diálogo. Este diálogo tiene un cuadro de edición donde puedes seleccionar el usuario sobre el que deseas actuar, bien con los cursores arriba y abajo o escribiendo tú mismo el nombre. Después, hay un grupo de botones de radio para seguir, dejar de seguir, silenciar, des-silenciar, reportar como Spam y bloquear. Si seleccionas desde el menú la opción seguir, el botón del cuadro de diálogo estará marcado con esa opción, así como sucederá respectivamente con dejar de seguir, reportar como Spam y bloquear. Pulsa el botón Aceptar para que el programa trate de hacer lo que le pides. Si no se ha podido, escucharás el error en inglés.
A continuación se describen las opciones restantes para este menú:
* Mensaje Directo: La misma acción que el botón.
* Añadir a lista: Para que puedas ver los tweets de un usuario en tus listas, primero hay que añadirlo. Esta opción abrirá un diálogo desde donde puedes seleccionar al usuario que deseas añadir, para después abrir otra ventana donde puedes seleccionar la lista a la cual añadir a ese usuario. Una vez hecho esto, la lista contendrá un nuevo usuario y podrás ver sus tweets.
* Ver Perfil del usuario: Abre un diálogo desde donde te permite seleccionar el usuario al que quieres ver el perfil.
* Línea temporal: Abre un diálogo desde donde puedes seleccionar el usuario para el que se creará la línea temporal. Al presionar intro, se creará. Si se hace una línea temporal de un usuario que no tenga Tuits, el programa fallará. Si se crea una línea que ya existe el programa te avisará y no permitirá crearla de nuevo.
* Ver favoritos: Abre un buffer para seguir los favoritos que marca el usuario seleccionado.
##### Menú Buffer {#buffer}
* Silenciar: Silencia completamente el buffer, con lo que no escucharás sonido alguno cuando nuevos elementos aparezcan.
* Leer automáticamente tuits para este buffer: Esta opción activa o desactiva la lectura automática de tuits. Si está activada, el lector de pantalla o la voz Sapi5 (si está activada una) leerá automáticamente los nuevos tuits conforme estos vayan llegando al buffer.
* Limpiar Buffer: Vacía los elementos de este buffer.
* Eliminar buffer: Borra la lista sobre la que te encuentras actualmente.
##### Menú Ayuda {#ayuda}
* Documentación: Abre este archivo, donde puedes leer algunos conceptos interesantes del programa.
* ¿Qué hay de nuevo en esta versión?: Abre un documento con la lista de cambios desde la versión actual, hasta la primera en existencia.
* Buscar actualizaciones: Cada que se abre el programa él mismo busca automáticamente si hay una nueva versión. Si lo hay, te preguntará si quieres descargarla; si aceptas, TW Blue descargará la actualización, la instalará y te pedirá reiniciarla (algo que hace automáticamente). Esta opción comprueba si hay actualizaciones sin tener que reiniciar la aplicación.
* Sitio web de TW Blue. Ve a nuestra [página principal](http://twblue.com.mx) donde podrás encontrar toda la información y descargas relativas a TW Blue, así como participar de la comunidad.
* Reportar un error: Lanza un diálogo desde donde puedes reportar un error solo llenando un par de campos. El título y una pequeña descripción de lo que pasó. Al pulsar en "enviar" el error se reportará. Si no se ha podido el programa te mostrará un mensaje informándolo.
* Sobre TW Blue: Muestra información de créditos del programa.
### Interfaz no visible {#interfaz_no_visible}
Si presionas Control+M, o si desde el menú aplicación seleccionas esconder ventana, estarás activando una interfaz a la que no se podrá acceder por la manera convencional, porque no se ve.
En la interfaz no visible todo lo que hagas será mediante atajos de teclado, incluso para recorrer las listas. Eventualmente se abrirán diálogos y estos sí serán visibles, pero la ventana principal de la aplicación no. Ve a la sección de atajos de teclado de la interfaz no visible para saber cuales puedes usar de momento.
### Atajos de teclado para la Interfaz Gráfica {#atajos}
Además de los botones y menús, la mayoría de las acciones pueden hacerse presionando una combinación de teclado. Aquí están las existentes en este momento:
* Intro: Abrir una dirección URL. Si hay más de una podrás ver una lista que te permitirá seleccionar la que quieras. Si estás en la lista de amigos o seguidores, mostrará detalles del seleccionado.
* Control+Intro: Intenta reproducir un audio si en el Tuit hay una dirección URL.
* F5: Baja un 5% el volumen de los sonidos. Esto afecta a los sonidos que reproduce el programa y al audio que puedas escuchar a través de él.
* F6: Sube un 5% el volumen de los sonidos de la aplicación.
* Control+N: Abre el diálogo para escribir un nuevo Tuit.
* Control+M: Oculta la ventana.
* Control+Q: Sale de la aplicación.
* Control+R: Abre el diálogo para responder.
* Control+Shift+R: Equivalente a la acción Retuit.
* Control+D: Enviar mensaje directo.
* Control+F: Marcar como favorito.
* Control+Shift+F: Quitar de favoritos.
* Control+Shift+V: Ver Tuit.
* Control+S: Seguir a un usuario.
* Control+Shift+S: Dejar de seguir a un usuario.
* Control+K: Bloquear a un usuario.
* Control+Shift+K: Reportar como Spam.
* Control+I: Abrir línea temporal a un usuario.
* Control+Shift+I: Eliminar línea temporal.
* Control+p: Editar tu perfil.
* Suprimir: Eliminar tuit o mensaje directo.
* Shift+suprimir: vacía el buffer, quitando todos los elementos hasta ese entonces. Esto ocurre sin borrar nada de Twitter.
### Atajos de teclado para la Interfaz no Visible {#atajos_invisibles}
Estos son los atajos de teclado que puedes usar desde la interfaz no visible. Ten en cuenta que cuando la vista de la interfaz gráfica esté activada ninguno de ellos podrá usarse. Al decir "windows", nos estamos refiriendo a la tecla de Windows izquierda.
* Control+Windows+Flecha Arriba: Va arriba en la lista actual.
* Control+Windows+Flecha abajo: Va hacia abajo en la lista actual.
* Control+Windows+Izquierda: Se desplaza a la pestaña de la izquierda.
* Control+Windows+Derecha: Se desplaza hacia la pestaña de la derecha.
* Control+Windows+Inicio: Ir al primer elemento de la lista.
* Control+Windows+Fin: Ir al final de la lista.
* Control+Windows+Avance de página: Ir 20 elementos hacia abajo en la lista actual.
* Control+Windows+Retroceso de página: ir 20 elementos hacia arriba en la lista actual.
* Control+Windows+Alt+Flecha Arriba: Subir volumen un 5%.
* Control+Windows+Alt+Flecha Abajo: Bajar volumen un 5%.
* Control+Windows+Intro: Abrir URL en el tuit, o ver detalles del usuario si estás en la lista de amigos o seguidores.
* Control+Windows+Alt+Intro: Intentar reproducir un audio.
* Control+Windows+M: Muestra la interfaz gráfica, desactivando la no visible.
* Control+Windows+N: Hacer un nuevo Tuit.
* Control+Windows+R: Responder a un tuit.
* Control+Windows+Shift+R: Hacer un retuit.
* Control+Windows+D: Enviar un mensaje directo.
* Control+Windows+Suprimir: Eliminar un tuit o mensaje directo.
* control+win+Shift+suprimir: vacía el buffer, quitando todos los elementos hasta ese entonces. Esto ocurre sin borrar nada de Twitter.
* Windows+Alt+F: Marcar como favorito.
* Windows+Alt+Shift+F: Quitar de favoritos.
* Control+Windows+S: Seguir a un usuario.
* Control+Windows+Shift+S: Dejar de seguir a alguien.
* Control+Windows+Alt+N: Ver detalles de un usuario,
* Control+Windows+V: Ver tuit en un cuadro de texto.
* Control+Windows+I: Abrir línea temporal.
* Control+Windows+Shift+I: Eliminar línea temporal de un usuario.
* Alt+Windows+P: Editar tu perfil.
* Control+win+espacio: ver tweet actual.
* Control+win+c: Copiar tweet al portapapeles.
* Control+windows+a: Añadir a un usuario a la lista.
* Control+shift+windows+a: qitar de la lista.
* Control+Windows+Shift+Flecha arriba: Ir un tuit hacia arriba en la conversación.
* Control+Windows+Flecha Abajo: Ir un tuit hacia abajo en la conversación.
* Control+Windows+Shift+M: Activar o desactivar el sonido para el buffer actual.
* Windows+Alt+M: Activar o desactivar el silencio global de TW Blue.
* Control+Windows+E: Activar o desactivar la lectura automática de los tuits en el buffer actual.
* Control+windows+Guion: buscar en Twitter.
* Control+Windows+F4: Cerrar el programa.
## Listas {#listas}
Una de las características más interesantes de Twitter son las listas, ya que son una manera de mantenerse actualizado sin tener que leer los tweets de todos los usuarios a los que sigues. Con una lista de Twitter solo verás los tweets de sus miembros (la gente que está dentro de la lista). Es parecido a una línea temporal, pero para muchos más usuarios.
En TW blue hemos empezado a dar soporte para esta característica. De momento vamos poco a poco, pero ya es posible usar esta función. Te presentamos los pasos que hay que dar para poder tener una lista abierta en TW Blue.
* Primero necesitarás ir al gestor de listas, ubicado bajo el menú aplicación.
* en el gestor de listas podrás ver todas las listas a las que estás unido, empezando por las que tú has creado. Si no ves ninguna lista en este diálogo, significa que no has creado ni te has unido a ninguna lista. Está bien.
* Verás un grupo de botones que se explican por sí solos: Crear nueva lista, editar, eliminar, abrir en buffer (este quizá es el menos claro, se refiere a abrir un nuevo buffer para que TW Blue actualice los tweets de la lista, como cuando pasa con las líneas temporales).
Una vez que hayas creado una nueva lista, no deberías abrirla en buffer. Al menos no de inmediato, porque en este momento no tiene miembro alguno y eso significa que cuando se carguen los tweets para empezar a actualizarla no verás nada. Es recomendable primero añadir a gente a la lista, tal como sigue:
* Cuando hayas cerrado el gestor de listas y estés navegando por entre los tweets de los usuarios, busca el usuario al que quieres añadir a la lista.
* Una vez encontrado, presiona el atajo Ctrl+Win+A o ve al menú usuario y selecciona la opción "Añadir a lista".
* Lo siguiente que verás es un diálogo que te permitirá seleccionar el usuario, asegúrate que el que está como predeterminado es el que deseas, o cámbialo si es necesario, y presiona Aceptar.
* Ahora verás otro diálogo, pero aquí están todas tus listas. Selecciona una (simplemente lleva el cursor hacia ella), y presiona el botón añadir.
* Para qitar a un usuario de una lista repite el mismo proceso, pero presiona Control+Win+Shift+A o selecciona la opción "Quitar de lista", y en el diálogo de las listas presiona sobre el botón "remover".
## Reportando Errores Desde la Web {#reportar}
Nota: Si estás usando el programa también puedes reportar un error desde el mismo, usando para ello la opción del menú ayuda. Este proceso solo te pide llenar dos cuadros de edición, y se encarga del resto. Estos pasos están escritos para quienes no pueden abrir el programa, no lo tienen en uso en este momento o sencillamente quieran reportar desde la web en lugar del sistema integrado de reporte de errores.
Las cosas en este mundo (sí, incluidos los programas informáticos) están muy lejos de ser perfectas, con lo que a menudo te encontrarás con errores no previstos en la aplicación. Pero como la intención es siempre mejorar, eres libre (es más, sería genial que lo hicieras) de reportar los errores que vayas encontrando del programa para que se puedan revisar y eventualmente corregir.
Para entrar a la web de reporte de incidencias, sigue [Este enlace.](http://twblue.com.mx/errores/bug_report_page.php) Es una web con un formulario donde tienes que llenar varios campos. Solo tres de ellos son realmente obligatorios (los que tienen marcado un asterisco), pero entre más campos puedas llenar, será mejor.
Aquí están los diferentes campos del formulario y lo que deberías introducir en cada uno de ellos. Recuerda que son obligatorios solamente los campos marcados con un asterisco (*):
* Categoría: Este cuadro combinado permite seleccionar a qué categoría asignar el error. Puede ser a la categoría General, si es un error del programa, o a documentación, si has encontrado un error en este archivo o en la lista de cambios. Este campo es obligatorio.
* Reproducibilidad: Aquí deberías indicar qué tan fácil o no es de reproducir el error. Las opciones disponibles son Desconocido, No reproducible, No se ha intentado (por defecto), aleatorio, a veces o siempre. Dependiendo de si se puede reproducir el error o no, deberías indicar lo que se parezca más a tu caso. Si estás solicitando una nueva funcionalidad, no importa este cuadro combinado.
* Severidad: Aquí se selecciona que tanto afecta esto al programa. Las opciones disponibles son funcionalidad (selecciona esto para solicitar una nueva funcionalidad), Trivial, Texto, Ajuste, Menor, Mayor, fallo o bloqueo. Nota que las opciones aumentan de nivel. Selecciona lo que más creas. Si no estás seguro de que seleccionar puedes dejarlo como está.
* Prioridad: En este cuadro se selecciona la opción de acuerdo con la importancia del error o funcionalidad solicitada. Las opciones disponibles son Ninguna, baja, normal, alta, hurgente e inmediata.
* Seleccionar Perfil: Aquí puedes escojer entre la configuración de arquitectura (32 o 64 bits), y el sistema operativo (Windows siete de momento). Si no, puedes llenar los tres cuadros de edición que están en la siguiente tabla con tus datos en específico.
* Versión del producto: Selecciona la versión del programa que estás utilizando para poder averiguar donde se ha generado el error. Este cuadro combinado tendrá la lista de las versiones en orden. Si bien no es obligatorio, ayudaría mucho a resolver más rápidamente el error.
* Resumen: Un título para el error, que explique en pocas palabras qué ocurre. Es un cuadro de texto obligatorio.
* Descripción: Este campo también obligatorio, te pide que describas con más detalles qué fue lo que ha ocurrido con el programa.
* Pasos para reproducir: Este campo de texto te sirve si sabes como hacer que la aplicación genere el error. Esto no es obligatorio, pero ayudaría mucho conocer como hacer que el programa tenga este error para rastrearlo mejor.
* Información adicional: Si tienes un comentario o nota que añadir, aquí puede ir. No es obligatorio.
* Subir archivo: Puedes subir aquí el archivo TW Blue.exe.log que se creó con el error que el programa tuvo. No es obligatorio.
* Visibilidad: Selecciona si quieres que el error sea público o privado. Por defecto es público, y es recomendable que así continúe.
* Enviar reporte. Presiona aquí para publicar el error y que este sea atendido.
Muchas gracias por participar reportando errores y probando las funciones nuevas.
## Contacto {#contacto}
Si lo que se expone en este documento no es suficiente, si deseas colaborar de alguna otra forma o si simplemente deseas mantenerte en contacto con quien hace esta aplicación, sigue a la cuenta [@tw_blue2](https://twitter.com/tw_blue2) o a [@manuelcortez00.](https://twitter.com/manuelcortez00) También puedes visitar nuestro [Sitio web](http://twblue.com.mx)
---
Copyright © 2013-2014. Manuel Cortéz.

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: twblue-documentation 0.46\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-08 10:32+0300\n"
"Last-Translator: Florian Ionașcu <florianionascu@hotmail.com>\n"
"Language-Team: Spanish <manuel@manuelcortez.net>\n"

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TW Blue documentation 0.46\n"
"POT-Creation-Date: 2018-08-07 13:19+Hora de verano central (Mexico)\n"
"POT-Creation-Date: 2019-03-17 13:34+Hora estndar romance\n"
"PO-Revision-Date: 2018-08-14 21:44+0400\n"
"Last-Translator: Valeria <luciana.lu3a@gmail.com>\n"
"Language-Team: \n"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

67
doc/paths.py Normal file
View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
import sys
import platform
import os
import glob
from platform_utils import paths as paths_
mode = "portable"
directory = None
fsencoding = sys.getfilesystemencoding()
if len(glob.glob("Uninstall.exe")) > 0: # installed copy
mode= "installed"
def app_path():
return paths_.app_path()
def config_path():
global mode, directory
if mode == "portable":
if directory != None: path = os.path.join(directory, "config")
elif directory == None: path = os.path.join(app_path(), "config")
elif mode == "installed":
path = os.path.join(data_path(), "config")
if not os.path.exists(path):
# log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path)
return path
def logs_path():
global mode, directory
if mode == "portable":
if directory != None: path = os.path.join(directory, "logs")
elif directory == None: path = os.path.join(app_path(), "logs")
elif mode == "installed":
path = os.path.join(data_path(), "logs")
if not os.path.exists(path):
# log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path)
return path
def data_path(app_name='socializer'):
if platform.system() == "Windows":
data_path = os.path.join(os.getenv("AppData"), app_name)
else:
data_path = os.path.join(os.environ['HOME'], ".%s" % app_name)
if not os.path.exists(data_path):
os.mkdir(data_path)
return data_path
def locale_path():
return os.path.join(app_path(), "locales")
def sound_path():
return os.path.join(app_path(), "sounds")
def com_path():
global mode, directory
if mode == "portable":
if directory != None: path = os.path.join(directory, "com_cache")
elif directory == None: path = os.path.join(app_path(), "com_cache")
elif mode == "installed":
path = os.path.join(data_path(), "com_cache")
if not os.path.exists(path):
# log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path)
return path

View File

@@ -20,8 +20,8 @@ CommercialUse=true
EULAVersion=2
[Version]
PackageVersion=0.94.0.0
DisplayVersion=0.94
PackageVersion=0.95.0.0
DisplayVersion=0.95
[Control]
Icons=1

View File

@@ -1,4 +1,5 @@
wxpython
wheel
six
configobj
markdown
@@ -7,7 +8,7 @@ requests
oauthlib
requests-oauthlib
requests-toolbelt
pypubsub==3.3.0
pypubsub
pygeocoder
arrow
python-dateutil
@@ -15,8 +16,10 @@ futures
winpaths
PySocks
win_inet_pton
yandex.translate
idna
# Install the latest RC of this lib
# see https://github.com/ssut/py-googletrans/issues/234
googletrans==4.0.0-rc1
idna<3,>=2.5
chardet
urllib3
youtube-dl
@@ -24,8 +27,12 @@ python-vlc
pypiwin32
certifi
backports.functools_lru_cache
git+https://github.com/manuelcortez/twython
git+https://github.com/manuelcortez/libloader
git+https://github.com/manuelcortez/platform_utils
git+https://github.com/manuelcortez/accessible_output2
git+https://github.com/chrisnorman7/sound_lib
cx_freeze
tweepy
twitter-text-parser
pyenchant
sqlitedict
git+https://github.com/accessibleapps/libloader
git+https://github.com/accessibleapps/platform_utils
git+https://github.com/accessibleapps/accessible_output2
git+https://github.com/accessibleapps/sound_lib

View File

@@ -15,10 +15,10 @@ SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TWBlue"
VIAddVersionKey LegalCopyright "Copyright 2018 Manuel Cortéz."
VIAddVersionKey ProductVersion "0.94"
VIAddVersionKey FileVersion "0.94"
VIProductVersion "0.94.0.0"
VIFileVersion "0.94.0.0"
VIAddVersionKey ProductVersion "0.95"
VIAddVersionKey FileVersion "0.95"
VIProductVersion "0.95.0.0"
VIFileVersion "0.95.0.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
@@ -72,10 +72,10 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "D
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.94"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.95"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.es"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 94
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 95
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd

View File

@@ -12,6 +12,7 @@ reverse_timelines = boolean(default=False)
announce_stream_status = boolean(default=True)
retweet_mode = string(default="ask")
persist_size = integer(default=0)
load_cache_in_memory=boolean(default=True)
show_screen_names = boolean(default=False)
buffer_order = list(default=list('home','mentions', 'dm', 'sent_dm', 'sent_tweets','favorites','followers','friends','blocks','muted','events'))

View File

@@ -24,8 +24,8 @@ mention_all = boolean(default=False)
no_streaming = boolean(default=False)
[proxy]
type = string(default="Direct connection")
type = integer(default=0)
server = string(default="")
port = string(default="")
port = integer(default=8080)
user = string(default="")
password = string(default="")

View File

@@ -2,20 +2,21 @@
import datetime
name = 'TWBlue'
short_name='twblue'
snapshot = True
if snapshot == False:
version = "0.94"
update_url = 'https://twblue.es/updates/stable.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
version = "0.95"
update_url = 'https://twblue.es/updates/stable.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/stable.json'
else:
version = "12"
update_url = 'https://twblue.es/updates/snapshot.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
authors = [u"Manuel Cortéz", u"José Manuel Delicado"]
version = "6"
update_url = 'https://twblue.es/updates/snapshot.php'
mirror_update_url = 'https://raw.githubusercontent.com/manuelcortez/TWBlue/next-gen/updates/snapshots.json'
authors = ["Manuel Cortéz", "José Manuel Delicado"]
authorEmail = "manuel@manuelcortez.net"
copyright = u"Copyright (C) 2013-2018, Manuel cortéz."
description = unicode(name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features.")
translators = [u"Manuel Cortéz (English)", u"Mohammed Al Shara, Hatoun Felemban (Arabic)", u"Francisco Torres (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Juan Buño (Galician)", u"Steffen Schultz (German)", u"Zvonimir Stanečić (Croatian)", u"Robert Osztolykan (Hungarian)", u"Christian Leo Mameli (Italian)", u"Riku (Japanese)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Florian Ionașcu, Nicușor Untilă (Romanian)", u"Natalia Hedlund, Valeria Kuznetsova (Russian)", u"Aleksandar Đurić (Serbian)", u"Burak Yüksek (Turkish)"]
copyright = "Copyright (C) 2013-2021, Manuel cortéz."
description = name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features."
translators = ["Manuel Cortéz (English)", "Mohammed Al Shara, Hatoun Felemban (Arabic)", "Francisco Torres (Catalan)", "Manuel cortéz (Spanish)", "Sukil Etxenike Arizaleta (Basque)", "Jani Kinnunen (finnish)", "Oreonan (Français)", "Juan Buño (Galician)", "Steffen Schultz (German)", "Zvonimir Stanečić (Croatian)", "Robert Osztolykan (Hungarian)", "Christian Leo Mameli (Italian)", "Riku (Japanese)", "Paweł Masarczyk (Polish)", "Odenilton Júnior Santos (Portuguese)", "Florian Ionașcu, Nicușor Untilă (Romanian)", "Natalia Hedlund, Valeria Kuznetsova (Russian)", "Aleksandar Đurić (Serbian)", "Burak Yüksek (Turkish)"]
url = u"https://twblue.es"
report_bugs_url = "https://github.com/manuelcortez/twblue/issues"
supported_languages = []
supported_languages = []

View File

@@ -1,22 +1,23 @@
from __future__ import unicode_literals
from functools import wraps
def matches_url(url):
def url_setter(func):
@wraps(func)
def internal_url_setter(*args, **kwargs):
return func(*args, **kwargs)
internal_url_setter.url = url
return internal_url_setter
return url_setter
def url_setter(func):
@wraps(func)
def internal_url_setter(*args, **kwargs):
return func(*args, **kwargs)
internal_url_setter.url = url
return internal_url_setter
return url_setter
def find_url_transformer(url):
from audio_services import services
funcs = []
for i in dir(services):
possible = getattr(services, i)
if callable(possible) and hasattr(possible, 'url'):
funcs.append(possible)
for f in funcs:
if url.lower().startswith(f.url.lower()):
return f
return services.convert_generic_audio
from audio_services import services
funcs = []
for i in dir(services):
possible = getattr(services, i)
if callable(possible) and hasattr(possible, 'url'):
funcs.append(possible)
for f in funcs:
if url.lower().startswith(f.url.lower()):
return f
return services.convert_generic_audio

View File

@@ -1,44 +1,41 @@
from __future__ import unicode_literals
from audio_services import matches_url
import json
import re
import urllib
import youtube_utils
import requests
from . import youtube_utils
@matches_url('https://audioboom.com')
def convert_audioboom(url):
if "audioboom.com" not in url.lower():
raise TypeError('%r is not a valid URL' % url)
audio_id = url.split('.com/')[-1]
return 'https://audioboom.com/%s.mp3' % audio_id
if "audioboom.com" not in url.lower():
raise TypeError('%r is not a valid URL' % url)
audio_id = url.split('.com/')[-1]
return 'https://audioboom.com/%s.mp3' % audio_id
@matches_url ('https://soundcloud.com/')
def convert_soundcloud (url):
client_id = "df8113ca95c157b6c9731f54b105b473"
permalink = urllib.urlopen ('http://api.soundcloud.com/resolve.json?client_id=%s&url=%s' %(client_id, url))
if permalink.getcode () == 404:
permalink.close ()
raise TypeError('%r is not a valid URL' % url)
else:
resolved_url = permalink.geturl ()
permalink.close ()
track_url = urllib.urlopen (resolved_url)
track_data = json.loads (track_url.read ())
track_url.close ()
if track_data ['streamable']:
return track_data ['stream_url'] + "?client_id=%s" %client_id
else:
raise TypeError('%r is not streamable' % url)
client_id = "df8113ca95c157b6c9731f54b105b473"
with requests.get('http://api.soundcloud.com/resolve.json', client_id=client_id, url=url) as permalink:
if permalink.status_code==404:
raise TypeError('%r is not a valid URL' % permalink.url)
else:
resolved_url = permalink.url
with requests.get(resolved_url) as track_url:
track_data = track_url.json()
if track_data ['streamable']:
return track_data ['stream_url'] + "?client_id=%s" %client_id
else:
raise TypeError('%r is not streamable' % url)
@matches_url ('https://www.youtube.com/watch')
def convert_youtube_long (url):
return youtube_utils.get_video_url(url)
return youtube_utils.get_video_url(url)
@matches_url ('http://anyaudio.net/listen')
def convert_anyaudio(url):
values = url.split("audio=")
if len(values) != 2:
raise TypeError('%r is not streamable' % url)
return "http://anyaudio.net/audiodownload?audio=%s" % (values[1],)
values = url.split("audio=")
if len(values) != 2:
raise TypeError('%r is not streamable' % url)
return "http://anyaudio.net/audiodownload?audio=%s" % (values[1],)
def convert_generic_audio(url):
return url
return url

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import youtube_dl
def get_video_url(url):
ydl = youtube_dl.YoutubeDL({'quiet': True, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
with ydl:
result = ydl.extract_info(url, download=False)
if 'entries' in result:
video = result['entries'][0]
else:
video = result
return video["formats"][0]["url"]

View File

@@ -1,4 +1,5 @@
# -*- coding: cp1252 -*-
import os
import config_utils
import paths
import logging
@@ -8,20 +9,20 @@ log = logging.getLogger("config")
MAINFILE = "twblue.conf"
MAINSPEC = "app-configuration.defaults"
proxyTypes=[u"http", u"https", u"socks4", u"socks5"]
proxyTypes = ["system", "http", "socks4", "socks4a", "socks5", "socks5h"]
app = None
keymap=None
changed_keymap = False
def setup ():
global app
log.debug("Loading global app settings...")
app = config_utils.load_config(paths.config_path(MAINFILE), paths.app_path(MAINSPEC))
log.debug("Loading keymap...")
global keymap
if float(platform.version()[:2]) >= 10 and app["app-settings"]["load_keymap"] == "default.keymap":
app["app-settings"]["load_keymap"] = "Windows 10.keymap"
app.write()
global changed_keymap
changed_keymap = True
keymap = config_utils.load_config(paths.config_path("keymap.keymap"), paths.app_path("keymaps/"+app['app-settings']['load_keymap']), copy=False)
global app
log.debug("Loading global app settings...")
app = config_utils.load_config(os.path.join(paths.config_path(), MAINFILE), os.path.join(paths.app_path(), MAINSPEC))
log.debug("Loading keymap...")
global keymap
if float(platform.version()[:2]) >= 10 and app["app-settings"]["load_keymap"] == "default.keymap":
app["app-settings"]["load_keymap"] = "Windows 10.keymap"
app.write()
global changed_keymap
changed_keymap = True
keymap = config_utils.load_config(os.path.join(paths.config_path(), "keymap.keymap"), os.path.join(paths.app_path(), "keymaps/"+app['app-settings']['load_keymap']), copy=False)

View File

@@ -9,69 +9,69 @@ log = getLogger("config_utils")
class ConfigLoadError(Exception): pass
def load_config(config_path, configspec_path=None, copy=True, *args, **kwargs):
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
try:
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
except ParseError:
raise ConfigLoadError("Unable to load %r" % config_path)
validator = Validator()
validated = config.validate(validator, preserve_errors=False, copy=copy)
if validated == True:
config.write()
return config
else:
log.exception("Error in config file: {0}".format(validated,))
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
try:
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
except ParseError:
raise ConfigLoadError("Unable to load %r" % config_path)
validator = Validator()
validated = config.validate(validator, preserve_errors=False, copy=copy)
if validated == True:
config.write()
return config
else:
log.exception("Error in config file: {0}".format(validated,))
def is_blank(arg):
"Check if a line is blank."
for c in arg:
if c not in string.whitespace:
return False
return True
"Check if a line is blank."
for c in arg:
if c not in string.whitespace:
return False
return True
def get_keys(path):
"Gets the keys of a configobj config file."
res=[]
fin=open(path)
for line in fin:
if not is_blank(line):
res.append(line[0:line.find('=')].strip())
fin.close()
return res
"Gets the keys of a configobj config file."
res=[]
fin=open(path)
for line in fin:
if not is_blank(line):
res.append(line[0:line.find('=')].strip())
fin.close()
return res
def hist(keys):
"Generates a histogram of an iterable."
res={}
for k in keys:
res[k]=res.setdefault(k,0)+1
return res
"Generates a histogram of an iterable."
res={}
for k in keys:
res[k]=res.setdefault(k,0)+1
return res
def find_problems(hist):
"Takes a histogram and returns a list of items occurring more than once."
res=[]
for k,v in hist.items():
if v>1:
res.append(k)
return res
"Takes a histogram and returns a list of items occurring more than once."
res=[]
for k,v in hist.items():
if v>1:
res.append(k)
return res
def clean_config(path):
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
orig=[]
cleaned=[]
fin=open(path)
for line in fin:
orig.append(line)
fin.close()
for p in find_problems(hist(get_keys(path))):
for o in orig:
o.strip()
if p not in o:
cleaned.append(o)
if len(cleaned) != 0:
cam=open(path,'w')
for c in cleaned:
cam.write(c)
cam.close()
return True
else:
return False
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
orig=[]
cleaned=[]
fin=open(path)
for line in fin:
orig.append(line)
fin.close()
for p in find_problems(hist(get_keys(path))):
for o in orig:
o.strip()
if p not in o:
cleaned.append(o)
if len(cleaned) != 0:
cam=open(path,'w')
for c in cleaned:
cam.write(c)
cam.close()
return True
else:
return False

View File

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from builtins import object
import os
import widgetUtils
import logging
@@ -6,33 +8,33 @@ from wxUI.dialogs import attach as gui
log = logging.getLogger("controller.attach")
class attach(object):
def __init__(self):
self.attachments = list()
self.dialog = gui.attachDialog()
widgetUtils.connect_event(self.dialog.photo, widgetUtils.BUTTON_PRESSED, self.upload_image)
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
self.dialog.get_response()
log.debug("Attachments controller started.")
def __init__(self):
self.attachments = list()
self.dialog = gui.attachDialog()
widgetUtils.connect_event(self.dialog.photo, widgetUtils.BUTTON_PRESSED, self.upload_image)
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_attachment)
self.dialog.get_response()
log.debug("Attachments controller started.")
def upload_image(self, *args, **kwargs):
image, description = self.dialog.get_image()
if image != None:
imageInfo = {"type": "photo", "file": image, "description": description}
log.debug("Image data to upload: %r" % (imageInfo,))
self.attachments.append(imageInfo)
info = [_(u"Photo"), description]
self.dialog.attachments.insert_item(False, *info)
self.dialog.remove.Enable(True)
def upload_image(self, *args, **kwargs):
image, description = self.dialog.get_image()
if image != None:
imageInfo = {"type": "photo", "file": image, "description": description}
log.debug("Image data to upload: %r" % (imageInfo,))
self.attachments.append(imageInfo)
info = [_(u"Photo"), description]
self.dialog.attachments.insert_item(False, *info)
self.dialog.remove.Enable(True)
def remove_attachment(self, *args, **kwargs):
current_item = self.dialog.attachments.get_selected()
log.debug("Removing item %d" % (current_item,))
if current_item == -1: current_item = 0
self.attachments.pop(current_item)
self.dialog.attachments.remove_item(current_item)
self.check_remove_status()
log.debug("Removed")
def remove_attachment(self, *args, **kwargs):
current_item = self.dialog.attachments.get_selected()
log.debug("Removing item %d" % (current_item,))
if current_item == -1: current_item = 0
self.attachments.pop(current_item)
self.dialog.attachments.remove_item(current_item)
self.check_remove_status()
log.debug("Removed")
def check_remove_status(self):
if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0:
self.dialog.remove.Enable(False)
def check_remove_status(self):
if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0:
self.dialog.remove.Enable(False)

View File

@@ -4,4 +4,5 @@
Currently, the package contains the following modules:
* baseBuffers: Define a set of functions and structure to be expected in all buffers. New buffers should inherit its classes from one of the classes present here.
* twitterBuffers: All other code, specific to Twitter.
"""
"""
from __future__ import unicode_literals

View File

@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
""" Common logic to all buffers in TWBlue."""
from __future__ import unicode_literals
from builtins import object
import logging
import wx
import output
@@ -12,190 +14,196 @@ from wxUI import buffers
log = logging.getLogger("controller.buffers.baseBuffers")
def _items_exist(function):
""" A decorator to execute a function only if the selected buffer contains at least one item."""
def function_(self, *args, **kwargs):
if self.buffer.list.get_count() > 0:
function(self, *args, **kwargs)
return function_
""" A decorator to execute a function only if the selected buffer contains at least one item."""
def function_(self, *args, **kwargs):
if self.buffer.list.get_count() > 0:
function(self, *args, **kwargs)
return function_
class buffer(object):
""" A basic buffer object. This should be the base class for all other derived buffers."""
""" A basic buffer object. This should be the base class for all other derived buffers."""
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
"""Inits the main controller for this buffer:
@ parent wx.Treebook object: Container where we will put this buffer.
@ function str or None: function to be called periodically and update items on this buffer.
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
"""
super(buffer, self).__init__()
self.function = function
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
# Read more about compose functions in twitter/compose.py.
self.compose_function = None
self.args = args
self.kwargs = kwargs
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
self.buffer = None
# This should countains the account associated to this buffer.
self.account = ""
# This controls whether the start_stream function should be called when starting the program.
self.needs_init = True
# if this is set to False, the buffer will be ignored on the invisible interface.
self.invisible = False
# Control variable, used to track time of execution for calls to start_stream.
self.execution_time = 0
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
"""Inits the main controller for this buffer:
@ parent wx.Treebook object: Container where we will put this buffer.
@ function str or None: function to be called periodically and update items on this buffer.
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
"""
super(buffer, self).__init__()
self.function = function
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
# Read more about compose functions in twitter/compose.py.
self.compose_function = None
self.args = args
self.kwargs = kwargs
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
self.buffer = None
# This should countains the account associated to this buffer.
self.account = ""
# This controls whether the start_stream function should be called when starting the program.
self.needs_init = True
# if this is set to False, the buffer will be ignored on the invisible interface.
self.invisible = False
# Control variable, used to track time of execution for calls to start_stream.
self.execution_time = 0
def clear_list(self):
pass
def clear_list(self):
pass
def get_event(self, ev):
""" Catch key presses in the WX interface and generate the corresponding event names."""
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
else:
event = None
ev.Skip()
if event != None:
try:
getattr(self, event)()
except AttributeError:
pass
def volume_down(self):
""" Decreases volume by 5%"""
if self.session.settings["sound"]["volume"] > 0.0:
if self.session.settings["sound"]["volume"] <= 0.05:
self.session.settings["sound"]["volume"] = 0.0
else:
self.session.settings["sound"]["volume"] -=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def get_event(self, ev):
""" Catch key presses in the WX interface and generate the corresponding event names."""
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
# Raise a Special event when pressed Shift+F10 because Wx==4.1.x does not seems to trigger this by itself.
# See https://github.com/manuelcortez/TWBlue/issues/353
elif ev.GetKeyCode() == wx.WXK_F10 and ev.ShiftDown(): event = "show_menu"
else:
event = None
ev.Skip()
if event != None:
try:
### ToDo: Remove after WX fixes issue #353 in the widgets.
if event == "show_menu":
return self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
getattr(self, event)()
except AttributeError:
pass
def volume_up(self):
""" Increases volume by 5%."""
if self.session.settings["sound"]["volume"] < 1.0:
if self.session.settings["sound"]["volume"] >= 0.95:
self.session.settings["sound"]["volume"] = 1.0
else:
self.session.settings["sound"]["volume"] +=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def volume_down(self):
""" Decreases volume by 5%"""
if self.session.settings["sound"]["volume"] > 0.0:
if self.session.settings["sound"]["volume"] <= 0.05:
self.session.settings["sound"]["volume"] = 0.0
else:
self.session.settings["sound"]["volume"] -=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def start_stream(self, mandatory=False, play_sound=True):
pass
def volume_up(self):
""" Increases volume by 5%."""
if self.session.settings["sound"]["volume"] < 1.0:
if self.session.settings["sound"]["volume"] >= 0.95:
self.session.settings["sound"]["volume"] = 1.0
else:
self.session.settings["sound"]["volume"] +=0.05
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
self.session.sound.play("volume_changed.ogg")
self.session.settings.write()
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"), True)
def start_stream(self, mandatory=False, play_sound=True):
pass
def put_items_on_list(self, items):
pass
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"), True)
def remove_buffer(self):
return False
def put_items_on_list(self, items):
pass
def remove_item(self, item):
f = self.buffer.list.get_selected()
self.buffer.list.remove_item(item)
self.buffer.list.select_item(f)
def remove_buffer(self):
return False
def bind_events(self):
pass
def remove_item(self, item):
f = self.buffer.list.get_selected()
self.buffer.list.remove_item(item)
self.buffer.list.select_item(f)
def get_object(self):
return self.buffer
def bind_events(self):
pass
def get_message(self):
pass
def get_object(self):
return self.buffer
def set_list_position(self, reversed=False):
if reversed == False:
self.buffer.list.select_item(-1)
else:
self.buffer.list.select_item(0)
def get_message(self):
pass
def reply(self):
pass
def set_list_position(self, reversed=False):
if reversed == False:
self.buffer.list.select_item(-1)
else:
self.buffer.list.select_item(0)
def send_message(self):
pass
def reply(self):
pass
def share_item(self):
pass
def send_message(self):
pass
def destroy_status(self):
pass
def share_item(self):
pass
def post_status(self, *args, **kwargs):
pass
def destroy_status(self):
pass
def save_positions(self):
try:
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
except AttributeError:
pass
def post_status(self, *args, **kwargs):
pass
def save_positions(self):
try:
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
except AttributeError:
pass
class accountPanel(buffer):
def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.accountPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.session = None
self.needs_init = False
self.account = account
self.buffer.account = account
self.name = name
self.account_id = account_id
def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.accountPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.session = None
self.needs_init = False
self.account = account
self.buffer.account = account
self.name = name
self.account_id = account_id
def setup_account(self):
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(False)
else:
self.buffer.change_autostart(True)
if not hasattr(self, "logged"):
self.buffer.change_login(login=False)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
else:
self.buffer.change_login(login=True)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
def setup_account(self):
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(False)
else:
self.buffer.change_autostart(True)
if not hasattr(self, "logged"):
self.buffer.change_login(login=False)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
else:
self.buffer.change_login(login=True)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
def login(self, *args, **kwargs):
del self.logged
self.setup_account()
pub.sendMessage("login", session_id=self.account_id)
def login(self, *args, **kwargs):
del self.logged
self.setup_account()
pub.sendMessage("login", session_id=self.account_id)
def logout(self, *args, **kwargs):
self.logged = False
self.setup_account()
pub.sendMessage("logout", session_id=self.account_id)
def logout(self, *args, **kwargs):
self.logged = False
self.setup_account()
pub.sendMessage("logout", session_id=self.account_id)
def autostart(self, *args, **kwargs):
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(True)
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
else:
self.buffer.change_autostart(False)
config.app["sessions"]["ignored_sessions"].append(self.account_id)
config.app.write()
def autostart(self, *args, **kwargs):
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(True)
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
else:
self.buffer.change_autostart(False)
config.app["sessions"]["ignored_sessions"].append(self.account_id)
config.app.write()
class emptyPanel(buffer):
def __init__(self, parent, name, account):
super(emptyPanel, self).__init__(parent=parent)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.emptyPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.account = account
self.buffer.account = account
self.name = name
self.session = None
self.needs_init = True
def __init__(self, parent, name, account):
super(emptyPanel, self).__init__(parent=parent)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.emptyPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.account = account
self.buffer.account = account
self.name = name
self.session = None
self.needs_init = True

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from builtins import object
import time
import widgetUtils
import application
@@ -6,71 +8,71 @@ from wxUI.dialogs import filterDialogs
from wxUI import commonMessageDialogs
class filter(object):
def __init__(self, buffer, filter_title=None, if_word_exists=None, in_lang=None, regexp=None, word=None, in_buffer=None):
self.buffer = buffer
self.dialog = filterDialogs.filterDialog(languages=[i["name"] for i in application.supported_languages])
if self.dialog.get_response() == widgetUtils.OK:
title = self.dialog.get("title")
contains = self.dialog.get("contains")
term = self.dialog.get("term")
regexp = self.dialog.get("regexp")
allow_rts = self.dialog.get("allow_rts")
allow_quotes = self.dialog.get("allow_quotes")
allow_replies = self.dialog.get("allow_replies")
load_language = self.dialog.get("load_language")
ignore_language = self.dialog.get("ignore_language")
lang_option = None
if ignore_language:
lang_option = False
elif load_language:
lang_option = True
langs = self.dialog.get_selected_langs()
langcodes = []
for i in application.supported_languages:
if i["name"] in langs:
langcodes.append(i["code"])
d = dict(in_buffer=self.buffer.name, word=term, regexp=regexp, in_lang=lang_option, languages=langcodes, if_word_exists=contains, allow_rts=allow_rts, allow_quotes=allow_quotes, allow_replies=allow_replies)
if title in self.buffer.session.settings["filters"]:
return commonMessageDialogs.existing_filter()
self.buffer.session.settings["filters"][title] = d
self.buffer.session.settings.write()
def __init__(self, buffer, filter_title=None, if_word_exists=None, in_lang=None, regexp=None, word=None, in_buffer=None):
self.buffer = buffer
self.dialog = filterDialogs.filterDialog(languages=[i["name"] for i in application.supported_languages])
if self.dialog.get_response() == widgetUtils.OK:
title = self.dialog.get("title")
contains = self.dialog.get("contains")
term = self.dialog.get("term")
regexp = self.dialog.get("regexp")
allow_rts = self.dialog.get("allow_rts")
allow_quotes = self.dialog.get("allow_quotes")
allow_replies = self.dialog.get("allow_replies")
load_language = self.dialog.get("load_language")
ignore_language = self.dialog.get("ignore_language")
lang_option = None
if ignore_language:
lang_option = False
elif load_language:
lang_option = True
langs = self.dialog.get_selected_langs()
langcodes = []
for i in application.supported_languages:
if i["name"] in langs:
langcodes.append(i["code"])
d = dict(in_buffer=self.buffer.name, word=term, regexp=regexp, in_lang=lang_option, languages=langcodes, if_word_exists=contains, allow_rts=allow_rts, allow_quotes=allow_quotes, allow_replies=allow_replies)
if title in self.buffer.session.settings["filters"]:
return commonMessageDialogs.existing_filter()
self.buffer.session.settings["filters"][title] = d
self.buffer.session.settings.write()
class filterManager(object):
def __init__(self, session):
self.session = session
self.dialog = filterDialogs.filterManagerDialog()
self.insert_filters(self.session.settings["filters"])
if self.dialog.filters.get_count() == 0:
self.dialog.edit.Enable(False)
self.dialog.delete.Enable(False)
else:
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_filter)
widgetUtils.connect_event(self.dialog.delete, widgetUtils.BUTTON_PRESSED, self.delete_filter)
response = self.dialog.get_response()
def __init__(self, session):
self.session = session
self.dialog = filterDialogs.filterManagerDialog()
self.insert_filters(self.session.settings["filters"])
if self.dialog.filters.get_count() == 0:
self.dialog.edit.Enable(False)
self.dialog.delete.Enable(False)
else:
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_filter)
widgetUtils.connect_event(self.dialog.delete, widgetUtils.BUTTON_PRESSED, self.delete_filter)
response = self.dialog.get_response()
def insert_filters(self, filters):
self.dialog.filters.clear()
for f in filters.keys():
filterName = f
buffer = filters[f]["in_buffer"]
if filters[f]["if_word_exists"] == "True" and filters[f]["word"] != "":
filter_by_word = "True"
else:
filter_by_word = "False"
filter_by_lang = ""
if filters[f]["in_lang"] != "None":
filter_by_lang = "True"
b = [f, buffer, filter_by_word, filter_by_lang]
self.dialog.filters.insert_item(False, *b)
def insert_filters(self, filters):
self.dialog.filters.clear()
for f in list(filters.keys()):
filterName = f
buffer = filters[f]["in_buffer"]
if filters[f]["if_word_exists"] == "True" and filters[f]["word"] != "":
filter_by_word = "True"
else:
filter_by_word = "False"
filter_by_lang = ""
if filters[f]["in_lang"] != "None":
filter_by_lang = "True"
b = [f, buffer, filter_by_word, filter_by_lang]
self.dialog.filters.insert_item(False, *b)
def edit_filter(self, *args, **kwargs):
pass
def edit_filter(self, *args, **kwargs):
pass
def delete_filter(self, *args, **kwargs):
filter_title = self.dialog.filters.get_text_column(self.dialog.filters.get_selected(), 0)
response = commonMessageDialogs.delete_filter()
if response == widgetUtils.YES:
self.session.settings["filters"].pop(filter_title)
self.session.settings.write()
self.insert_filters(self.session.settings["filters"])
def delete_filter(self, *args, **kwargs):
filter_title = self.dialog.filters.get_text_column(self.dialog.filters.get_selected(), 0)
response = commonMessageDialogs.delete_filter()
if response == widgetUtils.YES:
self.session.settings["filters"].pop(filter_title)
self.session.settings.write()
self.insert_filters(self.session.settings["filters"])

View File

@@ -1,106 +1,110 @@
# -*- coding: utf-8 -*-
import widgetUtils
import output
import logging
from wxUI.dialogs import lists
from twython import TwythonError
from tweepy.error import TweepError
from sessions.twitter import compose, utils
from pubsub import pub
log = logging.getLogger("controller.listsController")
class listsController(object):
def __init__(self, session, user=None):
super(listsController, self).__init__()
self.session = session
if user == None:
self.dialog = lists.listViewer()
self.dialog.populate_list(self.get_all_lists())
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
else:
self.dialog = lists.userListViewer(user)
self.dialog.populate_list(self.get_user_lists(user))
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
self.dialog.get_response()
def __init__(self, session, user=None):
super(listsController, self).__init__()
self.session = session
if user == None:
self.dialog = lists.listViewer()
self.dialog.populate_list(self.get_all_lists())
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
else:
self.dialog = lists.userListViewer(user)
self.dialog.populate_list(self.get_user_lists(user))
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
self.dialog.get_response()
def get_all_lists(self):
return [compose.compose_list(item) for item in self.session.db["lists"]]
def get_all_lists(self):
return [compose.compose_list(item) for item in self.session.db["lists"]]
def get_user_lists(self, user):
self.lists = self.session.twitter.show_lists(reverse=True, screen_name=user)
return [compose.compose_list(item) for item in self.lists]
def get_user_lists(self, user):
self.lists = self.session.twitter.lists_all(reverse=True, screen_name=user)
return [compose.compose_list(item) for item in self.lists]
def create_list(self, *args, **kwargs):
dialog = lists.createListDialog()
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
self.session.db["lists"].append(new_list)
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
dialog.destroy()
def create_list(self, *args, **kwargs):
dialog = lists.createListDialog()
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
new_list = self.session.twitter.create_list(name=name, description=description, mode=mode)
self.session.db["lists"].append(new_list)
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
except TweepError as e:
output.speak("error %s: %s" % (e.api_code, e.reason))
log.exception("error %s: %s" % (e.api_code, e.reason))
dialog.destroy()
def edit_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
dialog = lists.editListDialog(list)
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
self.session.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
self.session.get_lists()
self.dialog.populate_list(self.get_all_lists(), True)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
dialog.destroy()
def edit_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
dialog = lists.editListDialog(list)
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
self.session.twitter.update_list(list_id=list.id, name=name, description=description, mode=mode)
self.session.get_lists()
self.dialog.populate_list(self.get_all_lists(), True)
except TweepError as e:
output.speak("error %s: %s" % (e.api_code, e.reason))
dialog.destroy()
def remove_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]["id"]
if lists.remove_list() == widgetUtils.YES:
try:
self.session.twitter.delete_list(list_id=list)
self.session.db["lists"].pop(self.dialog.get_item())
self.dialog.lista.remove_item(self.dialog.get_item())
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
def remove_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()].id
if lists.remove_list() == widgetUtils.YES:
try:
self.session.twitter.destroy_list(list_id=list)
self.session.db["lists"].pop(self.dialog.get_item())
self.dialog.lista.remove_item(self.dialog.get_item())
except TweepError as e:
output.speak("error %s: %s" % (e.api_code, e.reason))
def open_list_as_buffer(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list["name"])
def open_list_as_buffer(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list.name)
def subscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()]["id"]
try:
list = self.session.twitter.subscribe_to_list(list_id=list_id)
item = utils.find_item(list["id"], self.session.db["lists"])
self.session.db["lists"].append(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
def subscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()].id
try:
list = self.session.twitter.subscribe_list(list_id=list_id)
item = utils.find_item(list.id, self.session.db["lists"])
self.session.db["lists"].append(list)
except TweepError as e:
output.speak("error %s: %s" % (e.api_code, e.reason))
def unsubscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()]["id"]
try:
list = self.session.twitter.unsubscribe_from_list(list_id=list_id)
self.session.db["lists"].remove(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
def unsubscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()].id
try:
list = self.session.twitter.unsubscribe_list(list_id=list_id)
self.session.db["lists"].remove(list)
except TweepError as e:
output.speak("error %s: %s" % (e.api_code, e.reason))

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import re
import platform
from . import attach
import arrow
import languageHandler
system = platform.system()
@@ -12,271 +10,283 @@ import url_shortener
import sound
import config
from pubsub import pub
from twitter_text import parse_tweet
if system == "Windows":
from wxUI.dialogs import message, urlList
from wxUI import commonMessageDialogs
from extra import translator, SpellChecker, autocompletionUsers
from extra.AudioUploader import audioUploader
from wxUI.dialogs import message, urlList
from wxUI import commonMessageDialogs
from extra import translator, SpellChecker, autocompletionUsers
from extra.AudioUploader import audioUploader
elif system == "Linux":
from gtkUI.dialogs import message
from gtkUI.dialogs import message
from sessions.twitter import utils
from . import attach
class basicTweet(object):
""" This class handles the tweet main features. Other classes should derive from this class."""
def __init__(self, session, title, caption, text, messageType="tweet", max=280, *args, **kwargs):
super(basicTweet, self).__init__()
self.max = max
self.title = title
self.session = session
self.message = getattr(message, messageType)(title, caption, text, *args, **kwargs)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if hasattr(self.message, "long_tweet"):
widgetUtils.connect_event(self.message.long_tweet, widgetUtils.CHECKBOX, self.text_processor)
if config.app["app-settings"]["remember_mention_and_longtweet"]:
self.message.long_tweet.SetValue(config.app["app-settings"]["longtweet"])
self.attachments = []
""" This class handles the tweet main features. Other classes should derive from this class."""
def __init__(self, session, title, caption, text, messageType="tweet", max=280, *args, **kwargs):
super(basicTweet, self).__init__()
self.max = max
self.title = title
self.session = session
self.message = getattr(message, messageType)(title, caption, text, *args, **kwargs)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if hasattr(self.message, "long_tweet"):
widgetUtils.connect_event(self.message.long_tweet, widgetUtils.CHECKBOX, self.text_processor)
if config.app["app-settings"]["remember_mention_and_longtweet"]:
self.message.long_tweet.SetValue(config.app["app-settings"]["longtweet"])
self.attachments = []
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.get_text()
dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
msg = translator.translator.translate(text=text_to_translate, target=dest)
self.message.set_text(msg)
self.text_processor()
self.message.text_focus()
output.speak(_(u"Translated"))
else:
return
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.get_text()
language_dict = translator.translator.available_languages()
for k in language_dict:
if language_dict[k] == dlg.dest_lang.GetStringSelection():
dst = k
msg = translator.translator.translate(text=text_to_translate, target=dst)
self.message.set_text(msg)
self.text_processor()
self.message.text_focus()
output.speak(_(u"Translated"))
else:
return
def shorten(self, event=None):
urls = utils.find_urls_in_text(self.message.get_text())
if len(urls) == 0:
output.speak(_(u"There's no URL to be shortened"))
self.message.text_focus()
elif len(urls) == 1:
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
output.speak(_(u"URL shortened"))
self.text_processor()
self.message.text_focus()
elif len(urls) > 1:
list_urls = urlList.urlList()
list_urls.populate_list(urls)
if list_urls.get_response() == widgetUtils.OK:
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
output.speak(_(u"URL shortened"))
self.text_processor()
self.message.text_focus()
def shorten(self, event=None):
urls = utils.find_urls_in_text(self.message.get_text())
if len(urls) == 0:
output.speak(_(u"There's no URL to be shortened"))
self.message.text_focus()
elif len(urls) == 1:
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
output.speak(_(u"URL shortened"))
self.text_processor()
self.message.text_focus()
elif len(urls) > 1:
list_urls = urlList.urlList()
list_urls.populate_list(urls)
if list_urls.get_response() == widgetUtils.OK:
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
output.speak(_(u"URL shortened"))
self.text_processor()
self.message.text_focus()
def unshorten(self, event=None):
urls = utils.find_urls_in_text(self.message.get_text())
if len(urls) == 0:
output.speak(_(u"There's no URL to be expanded"))
self.message.text_focus()
elif len(urls) == 1:
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
output.speak(_(u"URL expanded"))
self.text_processor()
self.message.text_focus()
elif len(urls) > 1:
list_urls = urlList.urlList()
list_urls.populate_list(urls)
if list_urls.get_response() == widgetUtils.OK:
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
output.speak(_(u"URL expanded"))
self.text_processor()
self.message.text_focus()
def unshorten(self, event=None):
urls = utils.find_urls_in_text(self.message.get_text())
if len(urls) == 0:
output.speak(_(u"There's no URL to be expanded"))
self.message.text_focus()
elif len(urls) == 1:
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
output.speak(_(u"URL expanded"))
self.text_processor()
self.message.text_focus()
elif len(urls) > 1:
list_urls = urlList.urlList()
list_urls.populate_list(urls)
if list_urls.get_response() == widgetUtils.OK:
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
output.speak(_(u"URL expanded"))
self.text_processor()
self.message.text_focus()
def text_processor(self, *args, **kwargs):
if len(self.message.get_text()) > 1:
self.message.enable_button("shortenButton")
self.message.enable_button("unshortenButton")
else:
self.message.disable_button("shortenButton")
self.message.disable_button("unshortenButton")
if self.message.get("long_tweet") == False:
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, len(self.message.get_text()), self.max))
if len(self.message.get_text()) > self.max:
self.session.sound.play("max_length.ogg")
else:
self.message.set_title(_(u"%s - %s characters") % (self.title, len(self.message.get_text())))
def text_processor(self, *args, **kwargs):
if len(self.message.get_text()) > 1:
self.message.enable_button("shortenButton")
self.message.enable_button("unshortenButton")
else:
self.message.disable_button("shortenButton")
self.message.disable_button("unshortenButton")
if self.message.get("long_tweet") == False:
text = self.message.get_text()
results = parse_tweet(text)
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, results.weightedLength, self.max))
if results.weightedLength > self.max:
self.session.sound.play("max_length.ogg")
else:
self.message.set_title(_(u"%s - %s characters") % (self.title, len(self.message.get_text())))
def spellcheck(self, event=None):
text = self.message.get_text()
checker = SpellChecker.spellchecker.spellChecker(text, "")
if hasattr(checker, "fixed_text"):
self.message.set_text(checker.fixed_text)
self.text_processor()
self.message.text_focus()
def spellcheck(self, event=None):
text = self.message.get_text()
checker = SpellChecker.spellchecker.spellChecker(text, "")
if hasattr(checker, "fixed_text"):
self.message.set_text(checker.fixed_text)
self.text_processor()
self.message.text_focus()
def attach(self, *args, **kwargs):
def completed_callback(dlg):
url = dlg.uploaderFunction.get_url()
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
dlg.uploaderDialog.destroy()
if "sndup.net/" in url:
self.message.set_text(self.message.get_text()+url+" #audio")
self.text_processor()
else:
commonMessageDialogs.common_error(url)
def attach(self, *args, **kwargs):
def completed_callback(dlg):
url = dlg.uploaderFunction.get_url()
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
dlg.uploaderDialog.destroy()
if "sndup.net/" in url:
self.message.set_text(self.message.get_text()+url+" #audio")
self.text_processor()
else:
commonMessageDialogs.common_error(url)
dlg.cleanup()
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
self.message.text_focus()
dlg.cleanup()
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
self.message.text_focus()
class tweet(basicTweet):
def __init__(self, session, title, caption, text, max=280, messageType="tweet", *args, **kwargs):
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
self.image = None
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
self.text_processor()
def __init__(self, session, title, caption, text, max=280, messageType="tweet", *args, **kwargs):
super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs)
self.image = None
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
self.text_processor()
def upload_image(self, *args, **kwargs):
a = attach.attach()
if len(a.attachments) != 0:
self.attachments = a.attachments
def upload_image(self, *args, **kwargs):
a = attach.attach()
if len(a.attachments) != 0:
self.attachments = a.attachments
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu()
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu()
class reply(tweet):
def __init__(self, session, title, caption, text, users=[], ids=[]):
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
self.ids = ids
self.users = users
if len(users) > 0:
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all)
self.message.enable_button("mentionAll")
if config.app["app-settings"]["remember_mention_and_longtweet"]:
self.message.mentionAll.SetValue(config.app["app-settings"]["mention_all"])
self.mention_all()
self.message.set_cursor_at_end()
self.text_processor()
def __init__(self, session, title, caption, text, users=[], ids=[]):
super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users)
self.ids = ids
self.users = users
if len(users) > 0:
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all)
self.message.enable_button("mentionAll")
if config.app["app-settings"]["remember_mention_and_longtweet"]:
self.message.mentionAll.SetValue(config.app["app-settings"]["mention_all"])
self.mention_all()
self.message.set_cursor_at_end()
self.text_processor()
def mention_all(self, *args, **kwargs):
if self.message.mentionAll.GetValue() == True:
for i in self.message.checkboxes:
i.SetValue(True)
i.Hide()
else:
for i in self.message.checkboxes:
i.SetValue(False)
i.Show()
def mention_all(self, *args, **kwargs):
if self.message.mentionAll.GetValue() == True:
for i in self.message.checkboxes:
i.SetValue(True)
i.Hide()
else:
for i in self.message.checkboxes:
i.SetValue(False)
i.Show()
def get_ids(self):
excluded_ids = ""
for i in xrange(0, len(self.message.checkboxes)):
if self.message.checkboxes[i].GetValue() == False:
excluded_ids = excluded_ids + "{0},".format(self.ids[i],)
return excluded_ids
def get_ids(self):
excluded_ids = ""
for i in range(0, len(self.message.checkboxes)):
if self.message.checkboxes[i].GetValue() == False:
excluded_ids = excluded_ids + "{0},".format(self.ids[i],)
return excluded_ids
def get_people(self):
people = ""
for i in xrange(0, len(self.message.checkboxes)):
if self.message.checkboxes[i].GetValue() == True:
people = people + "{0} ".format(self.message.checkboxes[i].GetLabel(),)
return people
def get_people(self):
people = ""
for i in range(0, len(self.message.checkboxes)):
if self.message.checkboxes[i].GetValue() == True:
people = people + "{0} ".format(self.message.checkboxes[i].GetLabel(),)
return people
class dm(basicTweet):
def __init__(self, session, title, caption, text):
super(dm, self).__init__(session, title, caption, text, messageType="dm", max=10000)
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
self.text_processor()
def __init__(self, session, title, caption, text):
super(dm, self).__init__(session, title, caption, text, messageType="dm", max=10000)
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
self.text_processor()
widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed)
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu("dm")
def user_changed(self, *args, **kwargs):
self.title = _("Direct message to %s") % (self.message.get_user())
self.text_processor()
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu("dm")
class viewTweet(basicTweet):
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date=""):
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
param is_tweet: True or false, depending wether the passed object is a tweet or not."""
if is_tweet == True:
self.title = _(u"Tweet")
image_description = []
text = ""
for i in xrange(0, len(tweetList)):
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
if "message" in tweetList[i] and tweetList[i]["is_quote_status"] == False:
value = "message"
else:
value = "full_text"
if "retweeted_status" in tweetList[i] and tweetList[i]["is_quote_status"] == False:
if ("message" in tweetList[i]) == False:
text = text + "rt @%s: %s\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i]["retweeted_status"]["full_text"])
else:
text = text + "rt @%s: %s\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i][value])
else:
text = text + " @%s: %s\n" % (tweetList[i]["user"]["screen_name"], tweetList[i][value])
# tweets with extended_entities could include image descriptions.
if "extended_entities" in tweetList[i] and "media" in tweetList[i]["extended_entities"]:
for z in tweetList[i]["extended_entities"]["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if "retweeted_status" in tweetList[i] and "extended_entities" in tweetList[i]["retweeted_status"] and "media" in tweetList[i]["retweeted_status"]["extended_entities"]:
for z in tweetList[i]["retweeted_status"]["extended_entities"]["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
# set rt and likes counters.
rt_count = str(tweet["retweet_count"])
favs_count = str(tweet["favorite_count"])
# Gets the client from where this tweet was made.
source = str(re.sub(r"(?s)<.*?>", "", tweet["source"].encode("utf-8")))
original_date = arrow.get(tweet["created_at"], "ddd MMM DD H:m:s Z YYYY", locale="en")
date = original_date.replace(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
if text == "":
if "message" in tweet:
value = "message"
else:
value = "full_text"
if "retweeted_status" in tweet:
if ("message" in tweet) == False:
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["full_text"])
else:
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet[value])
else:
text = tweet[value]
text = self.clear_text(text)
if "extended_entities" in tweet and "media" in tweet["extended_entities"]:
for z in tweet["extended_entities"]["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if "retweeted_status" in tweet and "extended_entities" in tweet["retweeted_status"] and "media" in tweet["retweeted_status"]["extended_entities"]:
for z in tweet["retweeted_status"]["extended_entities"]["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
self.message = message.viewTweet(text, rt_count, favs_count, source.decode("utf-8"), date)
self.message.set_title(len(text))
[self.message.set_image_description(i) for i in image_description]
else:
self.title = _(u"View item")
text = tweet
self.message = message.viewNonTweet(text, date)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if self.contain_urls() == True:
self.message.enable_button("unshortenButton")
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
self.message.get_response()
def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date=""):
""" This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event.
param tweet: A dictionary that represents a full tweet or a string for non-tweets.
param tweetList: If is_tweet is set to True, this could be a list of quoted tweets.
param is_tweet: True or false, depending wether the passed object is a tweet or not."""
if is_tweet == True:
self.title = _(u"Tweet")
image_description = []
text = ""
for i in range(0, len(tweetList)):
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
if hasattr(tweetList[i], "message") and tweetList[i].is_quote_status == False:
value = "message"
else:
value = "full_text"
if hasattr(tweetList[i], "retweeted_status") and tweetList[i].is_quote_status == False:
if not hasattr(tweetList[i], "message"):
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, tweetList[i].retweeted_status.full_text)
else:
text = text + "rt @%s: %s\n" % (tweetList[i].retweeted_status.user.screen_name, getattr(tweetList[i], value))
else:
text = text + " @%s: %s\n" % (tweetList[i].user.screen_name, getattr(tweetList[i], value))
# tweets with extended_entities could include image descriptions.
if hasattr(tweetList[i], "extended_entities") and "media" in tweetList[i].extended_entities:
for z in tweetList[i].extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if hasattr(tweetList[i], "retweeted_status") and hasattr(tweetList[i].retweeted_status, "extended_entities") and "media" in tweetList[i].retweeted_status["extended_entities"]:
for z in tweetList[i].retweeted_status.extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
# set rt and likes counters.
rt_count = str(tweet.retweet_count)
favs_count = str(tweet.favorite_count)
# Gets the client from where this tweet was made.
source = tweet.source
original_date = arrow.get(tweet.created_at, locale="en")
date = original_date.shift(seconds=utc_offset).format(_(u"MMM D, YYYY. H:m"), locale=languageHandler.getLanguage())
if text == "":
if hasattr(tweet, "message"):
value = "message"
else:
value = "full_text"
if hasattr(tweet, "retweeted_status"):
if not hasattr(tweet, "message"):
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, tweet.retweeted_status.full_text)
else:
text = "rt @%s: %s" % (tweet.retweeted_status.user.screen_name, getattr(tweet, value))
else:
text = getattr(tweet, value)
text = self.clear_text(text)
if hasattr(tweet, "extended_entities") and "media" in tweet.extended_entities:
for z in tweet.extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
if hasattr(tweet, "retweeted_status") and hasattr(tweet.retweeted_status, "extended_entities") and "media" in tweet.retweeted_status.extended_entities:
for z in tweet.retweeted_status.extended_entities["media"]:
if "ext_alt_text" in z and z["ext_alt_text"] != None:
image_description.append(z["ext_alt_text"])
self.message = message.viewTweet(text, rt_count, favs_count, source, date)
results = parse_tweet(text)
self.message.set_title(results.weightedLength)
[self.message.set_image_description(i) for i in image_description]
else:
self.title = _(u"View item")
text = tweet
self.message = message.viewNonTweet(text, date)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if self.contain_urls() == True:
self.message.enable_button("unshortenButton")
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
self.message.get_response()
def contain_urls(self):
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
return True
return False
def contain_urls(self):
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
return True
return False
def clear_text(self, text):
urls = utils.find_urls_in_text(text)
for i in urls:
if "https://twitter.com/" in i:
text = text.replace(i, "\n")
return text
def clear_text(self, text):
urls = utils.find_urls_in_text(text)
for i in urls:
if "https://twitter.com/" in i:
text = text.replace(i, "\n")
return text

View File

@@ -21,202 +21,206 @@ from collections import OrderedDict
from mysc import autostart as autostart_windows
class globalSettingsController(object):
def __init__(self):
super(globalSettingsController, self).__init__()
self.dialog = configuration.configurationDialog()
self.create_config()
self.needs_restart = False
self.is_started = True
def __init__(self):
super(globalSettingsController, self).__init__()
self.dialog = configuration.configurationDialog()
self.create_config()
self.needs_restart = False
self.is_started = True
def make_kmmap(self):
res={}
for i in os.listdir(paths.app_path('keymaps')):
if ".keymap" not in i:
continue
try:
res[i[:-7]] =i
except:
log.exception("Exception while loading keymap " + i)
return res
def make_kmmap(self):
res={}
for i in os.listdir(os.path.join(paths.app_path(), 'keymaps')):
if ".keymap" not in i:
continue
try:
res[i[:-7]] =i
except:
log.exception("Exception while loading keymap " + i)
return res
def create_config(self):
self.kmmap=self.make_kmmap()
self.langs = languageHandler.getAvailableLanguages()
langs = []
[langs.append(i[1]) for i in self.langs]
self.codes = []
[self.codes.append(i[0]) for i in self.langs]
id = self.codes.index(config.app["app-settings"]["language"])
self.kmfriendlies=[]
self.kmnames=[]
for k,v in self.kmmap.items():
self.kmfriendlies.append(k)
self.kmnames.append(v)
self.kmid=self.kmnames.index(config.app['app-settings']['load_keymap'])
self.dialog.create_general(langs,self.kmfriendlies)
self.dialog.general.language.SetSelection(id)
self.dialog.general.km.SetSelection(self.kmid)
if paths.mode == "installed":
self.dialog.set_value("general", "autostart", config.app["app-settings"]["autostart"])
else:
self.dialog.general.autostart.Enable(False)
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
self.dialog.set_value("general", "handle_longtweets", config.app["app-settings"]["handle_longtweets"])
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
self.dialog.set_value("general", "update_period", config.app["app-settings"]["update_period"])
self.dialog.set_value("general", "check_for_updates", config.app["app-settings"]["check_for_updates"])
self.dialog.set_value("general", "remember_mention_and_longtweet", config.app["app-settings"]["remember_mention_and_longtweet"])
proxyTypes=config.proxyTypes
self.dialog.create_proxy([_(u"Direct connection")]+proxyTypes)
if config.app["proxy"]["type"] not in proxyTypes:
self.dialog.proxy.type.SetSelection(0)
else:
self.dialog.proxy.type.SetSelection(proxyTypes.index(config.app["proxy"]["type"])+1)
self.dialog.set_value("proxy", "server", config.app["proxy"]["server"])
self.dialog.set_value("proxy", "port", config.app["proxy"]["port"])
self.dialog.set_value("proxy", "user", config.app["proxy"]["user"])
self.dialog.set_value("proxy", "password", config.app["proxy"]["password"])
def create_config(self):
self.kmmap=self.make_kmmap()
self.langs = languageHandler.getAvailableLanguages()
langs = []
[langs.append(i[1]) for i in self.langs]
self.codes = []
[self.codes.append(i[0]) for i in self.langs]
id = self.codes.index(config.app["app-settings"]["language"])
self.kmfriendlies=[]
self.kmnames=[]
for k,v in list(self.kmmap.items()):
self.kmfriendlies.append(k)
self.kmnames.append(v)
self.kmid=self.kmnames.index(config.app['app-settings']['load_keymap'])
self.dialog.create_general(langs,self.kmfriendlies)
self.dialog.general.language.SetSelection(id)
self.dialog.general.km.SetSelection(self.kmid)
if paths.mode == "installed":
self.dialog.set_value("general", "autostart", config.app["app-settings"]["autostart"])
else:
self.dialog.general.autostart.Enable(False)
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
self.dialog.set_value("general", "handle_longtweets", config.app["app-settings"]["handle_longtweets"])
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
self.dialog.set_value("general", "update_period", config.app["app-settings"]["update_period"])
self.dialog.set_value("general", "check_for_updates", config.app["app-settings"]["check_for_updates"])
self.dialog.set_value("general", "remember_mention_and_longtweet", config.app["app-settings"]["remember_mention_and_longtweet"])
proxyTypes = [_("System default"), _("HTTP"), _("SOCKS v4"), _("SOCKS v4 with DNS support"), _("SOCKS v5"), _("SOCKS v5 with DNS support")]
self.dialog.create_proxy(proxyTypes)
try:
self.dialog.proxy.type.SetSelection(config.app["proxy"]["type"])
except:
self.dialog.proxy.type.SetSelection(0)
self.dialog.set_value("proxy", "server", config.app["proxy"]["server"])
self.dialog.set_value("proxy", "port", config.app["proxy"]["port"])
self.dialog.set_value("proxy", "user", config.app["proxy"]["user"])
self.dialog.set_value("proxy", "password", config.app["proxy"]["password"])
self.dialog.realize()
self.response = self.dialog.get_response()
self.dialog.realize()
self.response = self.dialog.get_response()
def save_configuration(self):
if self.codes[self.dialog.general.language.GetSelection()] != config.app["app-settings"]["language"]:
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
languageHandler.setLanguage(config.app["app-settings"]["language"])
self.needs_restart = True
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
kmFile = open(paths.config_path("keymap.keymap"), "w")
kmFile.close()
self.needs_restart = True
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
autostart_windows.setAutoStart(application.name, enable=self.dialog.get_value("general", "autostart"))
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
if config.app["app-settings"]["no_streaming"] != self.dialog.get_value("general", "no_streaming"):
config.app["app-settings"]["no_streaming"] = self.dialog.get_value("general", "no_streaming")
self.needs_restart = True
if config.app["app-settings"]["update_period"] != self.dialog.get_value("general", "update_period"):
config.app["app-settings"]["update_period"] = self.dialog.get_value("general", "update_period")
self.needs_restart = True
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
config.app["app-settings"]["handle_longtweets"] = self.dialog.get_value("general", "handle_longtweets")
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
config.app["app-settings"]["remember_mention_and_longtweet"] = self.dialog.get_value("general", "remember_mention_and_longtweet")
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
if self.is_started == True:
self.needs_restart = True
config.app["proxy"]["type"]=self.dialog.get_value("proxy", "type")
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
config.app.write()
def save_configuration(self):
if self.codes[self.dialog.general.language.GetSelection()] != config.app["app-settings"]["language"]:
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
languageHandler.setLanguage(config.app["app-settings"]["language"])
self.needs_restart = True
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
kmFile = open(os.path.join(paths.config_path(), "keymap.keymap"), "w")
kmFile.close()
self.needs_restart = True
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
autostart_windows.setAutoStart(application.name, enable=self.dialog.get_value("general", "autostart"))
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
if config.app["app-settings"]["no_streaming"] != self.dialog.get_value("general", "no_streaming"):
config.app["app-settings"]["no_streaming"] = self.dialog.get_value("general", "no_streaming")
self.needs_restart = True
if config.app["app-settings"]["update_period"] != self.dialog.get_value("general", "update_period"):
config.app["app-settings"]["update_period"] = self.dialog.get_value("general", "update_period")
self.needs_restart = True
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
config.app["app-settings"]["handle_longtweets"] = self.dialog.get_value("general", "handle_longtweets")
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
config.app["app-settings"]["remember_mention_and_longtweet"] = self.dialog.get_value("general", "remember_mention_and_longtweet")
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
if self.is_started == True:
self.needs_restart = True
config.app["proxy"]["type"] = self.dialog.proxy.type.Selection
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
config.app.write()
class accountSettingsController(globalSettingsController):
def __init__(self, buffer, window):
self.user = buffer.session.db["user_name"]
self.buffer = buffer
self.window = window
self.config = buffer.session.settings
super(accountSettingsController, self).__init__()
def __init__(self, buffer, window):
self.user = buffer.session.db["user_name"]
self.buffer = buffer
self.window = window
self.config = buffer.session.settings
super(accountSettingsController, self).__init__()
def create_config(self):
self.dialog.create_general_account()
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
rt = self.config["general"]["retweet_mode"]
if rt == "ask":
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
elif rt == "direct":
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
else:
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
self.dialog.create_reporting()
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"])
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
self.dialog.create_other_buffers()
buffer_values = self.get_buffers_list()
self.dialog.buffers.insert_buffers(buffer_values)
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
def create_config(self):
self.dialog.create_general_account()
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
rt = self.config["general"]["retweet_mode"]
if rt == "ask":
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
elif rt == "direct":
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
else:
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
self.dialog.set_value("general", "load_cache_in_memory", self.config["general"]["load_cache_in_memory"])
self.dialog.create_reporting()
self.dialog.set_value("reporting", "speech_reporting", self.config["reporting"]["speech_reporting"])
self.dialog.set_value("reporting", "braille_reporting", self.config["reporting"]["braille_reporting"])
self.dialog.create_other_buffers()
buffer_values = self.get_buffers_list()
self.dialog.buffers.insert_buffers(buffer_values)
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
self.input_devices = sound_lib.input.Input.get_device_names()
self.output_devices = sound_lib.output.Output.get_device_names()
self.soundpacks = []
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(paths.sound_path(i)) == True ]
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
self.dialog.set_value("sound", "volumeCtrl", self.config["sound"]["volume"]*100)
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
self.dialog.set_value("sound", "indicate_audio", self.config["sound"]["indicate_audio"])
self.dialog.set_value("sound", "indicate_geo", self.config["sound"]["indicate_geo"])
self.dialog.set_value("sound", "indicate_img", self.config["sound"]["indicate_img"])
self.dialog.create_extras(OCRSpace.translatable_langs)
self.dialog.set_value("extras", "sndup_apiKey", self.config["sound"]["sndup_api_key"])
language_index = OCRSpace.OcrLangs.index(self.config["mysc"]["ocr_language"])
self.dialog.extras.ocr_lang.SetSelection(language_index)
self.dialog.realize()
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
self.response = self.dialog.get_response()
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
self.input_devices = sound_lib.input.Input.get_device_names()
self.output_devices = sound_lib.output.Output.get_device_names()
self.soundpacks = []
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(os.path.join(paths.sound_path(), i)) == True ]
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
self.dialog.set_value("sound", "volumeCtrl", self.config["sound"]["volume"]*100)
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
self.dialog.set_value("sound", "indicate_audio", self.config["sound"]["indicate_audio"])
self.dialog.set_value("sound", "indicate_geo", self.config["sound"]["indicate_geo"])
self.dialog.set_value("sound", "indicate_img", self.config["sound"]["indicate_img"])
self.dialog.create_extras(OCRSpace.translatable_langs)
self.dialog.set_value("extras", "sndup_apiKey", self.config["sound"]["sndup_api_key"])
language_index = OCRSpace.OcrLangs.index(self.config["mysc"]["ocr_language"])
self.dialog.extras.ocr_lang.SetSelection(language_index)
self.dialog.realize()
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
self.response = self.dialog.get_response()
def save_configuration(self):
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
self.needs_restart = True
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
if self.dialog.get_value("general", "persist_size") == '':
self.config["general"]["persist_size"] =-1
else:
try:
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
except ValueError:
output.speak("Invalid cache size, setting to default.",True)
self.config["general"]["persist_size"] =1764
def save_configuration(self):
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
self.needs_restart = True
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
self.config["general"]["show_screen_names"] = self.dialog.get_value("general", "show_screen_names")
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
if self.config["general"]["load_cache_in_memory"] != self.dialog.get_value("general", "load_cache_in_memory"):
self.config["general"]["load_cache_in_memory"] = self.dialog.get_value("general", "load_cache_in_memory")
self.needs_restart = True
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
if self.dialog.get_value("general", "persist_size") == '':
self.config["general"]["persist_size"] =-1
else:
try:
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
except ValueError:
output.speak("Invalid cache size, setting to default.",True)
self.config["general"]["persist_size"] =1764
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
self.needs_restart = True
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
rt = self.dialog.get_value("general", "retweet_mode")
if rt == _(u"Ask"):
self.config["general"]["retweet_mode"] = "ask"
elif rt == _(u"Retweet without comments"):
self.config["general"]["retweet_mode"] = "direct"
else:
self.config["general"]["retweet_mode"] = "comment"
buffers_list = self.dialog.buffers.get_list()
if buffers_list != self.config["general"]["buffer_order"]:
self.needs_restart = True
self.config["general"]["buffer_order"] = buffers_list
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
self.needs_restart = True
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
rt = self.dialog.get_value("general", "retweet_mode")
if rt == _(u"Ask"):
self.config["general"]["retweet_mode"] = "ask"
elif rt == _(u"Retweet without comments"):
self.config["general"]["retweet_mode"] = "direct"
else:
self.config["general"]["retweet_mode"] = "comment"
buffers_list = self.dialog.buffers.get_list()
if buffers_list != self.config["general"]["buffer_order"]:
self.needs_restart = True
self.config["general"]["buffer_order"] = buffers_list
self.config["reporting"]["speech_reporting"] = self.dialog.get_value("reporting", "speech_reporting")
self.config["reporting"]["braille_reporting"] = self.dialog.get_value("reporting", "braille_reporting")
self.config["mysc"]["ocr_language"] = OCRSpace.OcrLangs[self.dialog.extras.ocr_lang.GetSelection()]
# if self.config["other_buffers"]["show_followers"] != self.dialog.get_value("buffers", "followers"):
# self.config["other_buffers"]["show_followers"] = self.dialog.get_value("buffers", "followers")
# pub.sendMessage("create-new-buffer", buffer="followers", account=self.user, create=self.config["other_buffers"]["show_followers"])
@@ -235,75 +239,75 @@ class accountSettingsController(globalSettingsController):
# if self.config["other_buffers"]["show_events"] != self.dialog.get_value("buffers", "events"):
# self.config["other_buffers"]["show_events"] = self.dialog.get_value("buffers", "events")
# pub.sendMessage("create-new-buffer", buffer="events", account=self.user, create=self.config["other_buffers"]["show_events"])
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
try:
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
except:
self.config["sound"]["input_device"] = "default"
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
try:
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
except:
self.config["sound"]["output_device"] = "default"
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
self.config["sound"]["indicate_audio"] = self.dialog.get_value("sound", "indicate_audio")
self.config["sound"]["indicate_geo"] = self.dialog.get_value("sound", "indicate_geo")
self.config["sound"]["indicate_img"] = self.dialog.get_value("sound", "indicate_img")
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("extras", "sndup_apiKey")
self.buffer.session.sound.config = self.config["sound"]
self.buffer.session.sound.check_soundpack()
self.config.write()
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
try:
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
except:
self.config["sound"]["input_device"] = "default"
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
try:
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
except:
self.config["sound"]["output_device"] = "default"
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
self.config["sound"]["indicate_audio"] = self.dialog.get_value("sound", "indicate_audio")
self.config["sound"]["indicate_geo"] = self.dialog.get_value("sound", "indicate_geo")
self.config["sound"]["indicate_img"] = self.dialog.get_value("sound", "indicate_img")
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("extras", "sndup_apiKey")
self.buffer.session.sound.config = self.config["sound"]
self.buffer.session.sound.check_soundpack()
self.config.write()
def toggle_state(self,*args,**kwargs):
return self.dialog.buffers.change_selected_item()
def toggle_state(self,*args,**kwargs):
return self.dialog.buffers.change_selected_item()
def manage_autocomplete(self, *args, **kwargs):
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
def manage_autocomplete(self, *args, **kwargs):
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
def add_ignored_client(self, *args, **kwargs):
client = commonMessageDialogs.get_ignored_client()
if client == None: return
if client not in self.config["twitter"]["ignored_clients"]:
self.config["twitter"]["ignored_clients"].append(client)
self.dialog.ignored_clients.append(client)
def add_ignored_client(self, *args, **kwargs):
client = commonMessageDialogs.get_ignored_client()
if client == None: return
if client not in self.config["twitter"]["ignored_clients"]:
self.config["twitter"]["ignored_clients"].append(client)
self.dialog.ignored_clients.append(client)
def remove_ignored_client(self, *args, **kwargs):
if self.dialog.ignored_clients.get_clients() == 0: return
id = self.dialog.ignored_clients.get_client_id()
self.config["twitter"]["ignored_clients"].pop(id)
self.dialog.ignored_clients.remove_(id)
def remove_ignored_client(self, *args, **kwargs):
if self.dialog.ignored_clients.get_clients() == 0: return
id = self.dialog.ignored_clients.get_client_id()
self.config["twitter"]["ignored_clients"].pop(id)
self.dialog.ignored_clients.remove_(id)
def get_buffers_list(self):
all_buffers=OrderedDict()
all_buffers['home']=_(u"Home")
all_buffers['mentions']=_(u"Mentions")
all_buffers['dm']=_(u"Direct Messages")
all_buffers['sent_dm']=_(u"Sent direct messages")
all_buffers['sent_tweets']=_(u"Sent tweets")
all_buffers['favorites']=_(u"Likes")
all_buffers['followers']=_(u"Followers")
all_buffers['friends']=_(u"Friends")
all_buffers['blocks']=_(u"Blocked users")
all_buffers['muted']=_(u"Muted users")
list_buffers = []
hidden_buffers=[]
all_buffers_keys = all_buffers.keys()
# Check buffers shown first.
for i in self.config["general"]["buffer_order"]:
if i in all_buffers_keys:
list_buffers.append((i, all_buffers[i], True))
# This second pass will retrieve all hidden buffers.
for i in all_buffers_keys:
if i not in self.config["general"]["buffer_order"]:
hidden_buffers.append((i, all_buffers[i], False))
list_buffers.extend(hidden_buffers)
return list_buffers
def get_buffers_list(self):
all_buffers=OrderedDict()
all_buffers['home']=_(u"Home")
all_buffers['mentions']=_(u"Mentions")
all_buffers['dm']=_(u"Direct Messages")
all_buffers['sent_dm']=_(u"Sent direct messages")
all_buffers['sent_tweets']=_(u"Sent tweets")
all_buffers['favorites']=_(u"Likes")
all_buffers['followers']=_(u"Followers")
all_buffers['friends']=_(u"Friends")
all_buffers['blocks']=_(u"Blocked users")
all_buffers['muted']=_(u"Muted users")
list_buffers = []
hidden_buffers=[]
all_buffers_keys = list(all_buffers.keys())
# Check buffers shown first.
for i in self.config["general"]["buffer_order"]:
if i in all_buffers_keys:
list_buffers.append((i, all_buffers[i], True))
# This second pass will retrieve all hidden buffers.
for i in all_buffers_keys:
if i not in self.config["general"]["buffer_order"]:
hidden_buffers.append((i, all_buffers[i], False))
list_buffers.extend(hidden_buffers)
return list_buffers
def toggle_buffer_active(self, ev):
change = self.dialog.buffers.get_event(ev)
if change == True:
self.dialog.buffers.change_selected_item()
def toggle_buffer_active(self, ev):
change = self.dialog.buffers.get_event(ev)
if change == True:
self.dialog.buffers.change_selected_item()

View File

@@ -3,43 +3,43 @@ from wxUI.dialogs import trends
import widgetUtils
class trendingTopicsController(object):
def __init__(self, session):
super(trendingTopicsController, self).__init__()
self.countries = {}
self.cities = {}
self.dialog = trends.trendingTopicsDialog()
self.information = session.twitter.get_available_trends()
self.split_information()
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
self.get_places()
def __init__(self, session):
super(trendingTopicsController, self).__init__()
self.countries = {}
self.cities = {}
self.dialog = trends.trendingTopicsDialog()
self.information = session.twitter.trends_available()
self.split_information()
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
self.get_places()
def split_information(self):
for i in self.information:
if i["placeType"]["name"] == "Country":
self.countries[i["name"]] = i["woeid"]
else:
self.cities[i["name"]] = i["woeid"]
def split_information(self):
for i in self.information:
if i["placeType"]["name"] == "Country":
self.countries[i["name"]] = i["woeid"]
else:
self.cities[i["name"]] = i["woeid"]
def get_places(self, event=None):
values = []
if self.dialog.get_active() == "country":
for i in self.information:
if i["placeType"]["name"] == "Country":
values.append(i["name"])
elif self.dialog.get_active() == "city":
for i in self.information:
if i["placeType"]["name"] != "Country":
values.append(i["name"])
self.dialog.set(values)
def get_places(self, event=None):
values = []
if self.dialog.get_active() == "country":
for i in self.information:
if i["placeType"]["name"] == "Country":
values.append(i["name"])
elif self.dialog.get_active() == "city":
for i in self.information:
if i["placeType"]["name"] != "Country":
values.append(i["name"])
self.dialog.set(values)
def get_woeid(self):
selected = self.dialog.get_item()
if self.dialog.get_active() == "country":
woeid = self.countries[selected]
else:
woeid = self.cities[selected]
return woeid
def get_woeid(self):
selected = self.dialog.get_item()
if self.dialog.get_active() == "country":
woeid = self.countries[selected]
else:
woeid = self.cities[selected]
return woeid
def get_string(self):
return self.dialog.get_item()
def get_string(self):
return self.dialog.get_item()

View File

@@ -6,121 +6,123 @@ import output
from wxUI.dialogs import update_profile, show_user
import logging
log = logging.getLogger("controller.user")
from twython import TwythonError
from tweepy.error import TweepError
from sessions.twitter import utils
class profileController(object):
def __init__(self, session, user=None):
super(profileController, self).__init__()
self.file = None
self.session = session
self.user = user
if user == None:
self.get_data(screen_name=self.session.db["user_name"])
self.dialog = update_profile.updateProfileDialog()
self.fill_profile_fields()
self.uploaded = False
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
else:
try:
self.get_data(screen_name=self.user)
except TwythonError as err:
if err.error_code == 404:
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
if err.error_code == 403:
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
log.error("error %d: %s" % (err.error_code, err.msg))
return
self.dialog = show_user.showUserProfile()
string = self.get_user_info()
self.dialog.set("text", string)
self.dialog.set_title(_(u"Information for %s") % (self.data["screen_name"]))
if self.data["url"] != None:
self.dialog.enable_url()
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
self.do_update()
def __init__(self, session, user=None):
super(profileController, self).__init__()
self.file = None
self.session = session
self.user = user
if user == None:
self.get_data(screen_name=self.session.db["user_name"])
self.dialog = update_profile.updateProfileDialog()
self.fill_profile_fields()
self.uploaded = False
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
else:
try:
self.get_data(screen_name=self.user)
except TweepError as err:
if err.api_code == 50:
wx.MessageDialog(None, _(u"That user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
if err.api_code == 403:
wx.MessageDialog(None, _(u"User has been suspended"), _(u"Error"), wx.ICON_ERROR).ShowModal()
log.error("error %d: %s" % (err.api_code, err.reason))
return
self.dialog = show_user.showUserProfile()
string = self.get_user_info()
self.dialog.set("text", string)
self.dialog.set_title(_(u"Information for %s") % (self.data.screen_name))
if self.data.url != None:
self.dialog.enable_url()
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
self.do_update()
def get_data(self, screen_name):
self.data = self.session.twitter.show_user(screen_name=screen_name)
if screen_name != self.session.db["user_name"]:
self.friendship_status = self.session.twitter.show_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
def get_data(self, screen_name):
self.data = self.session.twitter.get_user(screen_name=screen_name)
if screen_name != self.session.db["user_name"]:
self.friendship_status = self.session.twitter.show_friendship(source_screen_name=self.session.db["user_name"], target_screen_name=screen_name)
def fill_profile_fields(self):
self.dialog.set_name(self.data["name"])
if self.data["url"] != None:
self.dialog.set_url(self.data["url"])
if len(self.data["location"]) > 0:
self.dialog.set_location(self.data["location"])
if len(self.data["description"]) > 0:
self.dialog.set_description(self.data["description"])
def fill_profile_fields(self):
self.dialog.set_name(self.data.name)
if self.data.url != None:
self.dialog.set_url(self.data.url)
if len(self.data.location) > 0:
self.dialog.set_location(self.data.location)
if len(self.data.description) > 0:
self.dialog.set_description(self.data.description)
def get_image(self):
file = self.dialog.upload_picture()
if file != None:
self.file = open(file, "rb")
self.uploaded = True
self.dialog.change_upload_button(self.uploaded)
def get_image(self):
file = self.dialog.upload_picture()
if file != None:
self.file = open(file, "rb")
self.uploaded = True
self.dialog.change_upload_button(self.uploaded)
def discard_image(self):
self.file = None
output.speak(_(u"Discarded"))
self.uploaded = False
self.dialog.change_upload_button(self.uploaded)
def discard_image(self):
self.file = None
output.speak(_(u"Discarded"))
self.uploaded = False
self.dialog.change_upload_button(self.uploaded)
def upload_image(self, *args, **kwargs):
if self.uploaded == False:
self.get_image()
elif self.uploaded == True:
self.discard_image()
def upload_image(self, *args, **kwargs):
if self.uploaded == False:
self.get_image()
elif self.uploaded == True:
self.discard_image()
def do_update(self):
if self.user != None: return
name = self.dialog.get("name")
description = self.dialog.get("description")
location = self.dialog.get("location")
url = self.dialog.get("url")
if self.file != None:
try:
self.session.twitter.update_profile_image(image=self.file)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
try:
self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
def do_update(self):
if self.user != None: return
name = self.dialog.get("name")
description = self.dialog.get("description")
location = self.dialog.get("location")
url = self.dialog.get("url")
if self.file != None:
try:
self.session.twitter.update_profile_image(image=self.file)
except TweepError as e:
output.speak(u"Error %s. %s" % (e.api_code, e.reason))
try:
self.session.twitter.update_profile(name=name, description=description, location=location, url=url)
except TweepError as e:
output.speak(u"Error %s. %s" % (e.api_code, e.reason))
def get_user_info(self):
string = u""
string = string + _(u"Username: @%s\n") % (self.data["screen_name"])
string = string + _(u"Name: %s\n") % (self.data["name"])
if self.data["location"] != "":
string = string + _(u"Location: %s\n") % (self.data["location"])
if self.data["url"] != None:
string = string+ _(u"URL: %s\n") % (self.data["url"])
if self.data["description"] != "":
string = string+ _(u"Bio: %s\n") % (self.data["description"])
if self.data["protected"] == True: protected = _(u"Yes")
else: protected = _(u"No")
string = string+ _(u"Protected: %s\n") % (protected)
if hasattr(self, "friendship_status"):
relation = False
friendship = "Relationship: "
if self.friendship_status["relationship"]["target"]["followed_by"]:
friendship += _(u"You follow {0}. ").format(self.data["name"],)
relation = True
if self.friendship_status["relationship"]["target"]["following"]:
friendship += _(u"{0} is following you.").format(self.data["name"],)
relation = True
if relation == True:
string = string+friendship+"\n"
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data["followers_count"], self.data["friends_count"])
if self.data["verified"] == True: verified = _(u"Yes")
else: verified = _(u"No")
string = string+ _(u"Verified: %s\n") % (verified)
string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"])
string = string+ _(u"Likes: %s") % (self.data["favourites_count"])
return string
def get_user_info(self):
string = u""
string = string + _(u"Username: @%s\n") % (self.data.screen_name)
string = string + _(u"Name: %s\n") % (self.data.name)
if self.data.location != "":
string = string + _(u"Location: %s\n") % (self.data.location)
if self.data.url != None:
string = string+ _(u"URL: %s\n") % (self.data.entities["url"]["urls"][0]["expanded_url"])
if self.data.description != "":
if self.data.entities.get("description") != None and self.data.entities["description"].get("urls"):
self.data.description = utils.expand_urls(self.data.description, self.data.entities["description"])
string = string+ _(u"Bio: %s\n") % (self.data.description)
if self.data.protected == True: protected = _(u"Yes")
else: protected = _(u"No")
string = string+ _(u"Protected: %s\n") % (protected)
if hasattr(self, "friendship_status"):
relation = False
friendship = "Relationship: "
if self.friendship_status[0].following:
friendship += _(u"You follow {0}. ").format(self.data.name,)
relation = True
if self.friendship_status[1].following:
friendship += _(u"{0} is following you.").format(self.data.name,)
relation = True
if relation == True:
string = string+friendship+"\n"
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data.followers_count, self.data.friends_count)
if self.data.verified == True: verified = _(u"Yes")
else: verified = _(u"No")
string = string+ _(u"Verified: %s\n") % (verified)
string = string+ _(u"Tweets: %s\n") % (self.data.statuses_count)
string = string+ _(u"Likes: %s") % (self.data.favourites_count)
return string
def visit_url(self, *args, **kwargs):
webbrowser.open_new_tab(self.data["url"])
def visit_url(self, *args, **kwargs):
webbrowser.open_new_tab(self.data.url)

View File

@@ -1,80 +1,79 @@
# -*- coding: utf-8 -*-
import re
import widgetUtils
import output
from wxUI.dialogs import userActions
from pubsub import pub
from twython import TwythonError
from tweepy.error import TweepError
from extra import autocompletionUsers
class userActionsController(object):
def __init__(self, buffer, users=[], default="follow"):
super(userActionsController, self).__init__()
self.buffer = buffer
self.session = buffer.session
self.dialog = userActions.UserActionsDialog(users, default)
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
if self.dialog.get_response() == widgetUtils.OK:
self.process_action()
def __init__(self, buffer, users=[], default="follow"):
super(userActionsController, self).__init__()
self.buffer = buffer
self.session = buffer.session
self.dialog = userActions.UserActionsDialog(users, default)
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
if self.dialog.get_response() == widgetUtils.OK:
self.process_action()
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
c.show_menu("dm")
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
c.show_menu("dm")
def process_action(self):
action = self.dialog.get_action()
user = self.dialog.get_user()
if user == "": return
getattr(self, action)(user)
def process_action(self):
action = self.dialog.get_action()
user = self.dialog.get_user()
if user == "": return
getattr(self, action)(user)
def follow(self, user):
try:
self.session.twitter.create_friendship(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def follow(self, user):
try:
self.session.twitter.create_friendship(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def unfollow(self, user):
try:
id = self.session.twitter.destroy_friendship(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def unfollow(self, user):
try:
id = self.session.twitter.destroy_friendship(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def mute(self, user):
try:
id = self.session.twitter.create_mute(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def mute(self, user):
try:
id = self.session.twitter.create_mute(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def unmute(self, user):
try:
id = self.session.twitter.destroy_mute(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def unmute(self, user):
try:
id = self.session.twitter.destroy_mute(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def report(self, user):
try:
id = self.session.twitter.report_spam(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def report(self, user):
try:
id = self.session.twitter.report_spam(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def block(self, user):
try:
id = self.session.twitter.create_block(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def block(self, user):
try:
id = self.session.twitter.create_block(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def unblock(self, user):
try:
id = self.session.twitter.destroy_block(screen_name=user )
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def unblock(self, user):
try:
id = self.session.twitter.destroy_block(screen_name=user )
except TweepError as err:
output.speak("Error %s: %s" % (err.api_code, err.reason), True)
def ignore_client(self, user):
tweet = self.buffer.get_right_tweet()
if "sender" in tweet:
output.speak(_(u"You can't ignore direct messages"))
return
client = re.sub(r"(?s)<.*?>", "", tweet["source"])
if client not in self.session.settings["twitter"]["ignored_clients"]:
self.session.settings["twitter"]["ignored_clients"].append(client)
self.session.settings.write()
def ignore_client(self, user):
tweet = self.buffer.get_right_tweet()
if hasattr(tweet, "sender"):
output.speak(_(u"You can't ignore direct messages"))
return
client = tweet.source
if client not in self.session.settings["twitter"]["ignored_clients"]:
self.session.settings["twitter"]["ignored_clients"].append(client)
self.session.settings.write()

View File

@@ -17,6 +17,9 @@
#
############################################################
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import str
from builtins import object
import widgetUtils
from . import wx_ui
from . import wx_transfer_dialogs
@@ -34,146 +37,146 @@ import logging
log = logging.getLogger("extra.AudioUploader.audioUploader")
class audioUploader(object):
def __init__(self, configFile, completed_callback):
self.config = configFile
super(audioUploader, self).__init__()
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
self.file = None
self.recorded = False
self.recording = None
self.playing = None
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
if self.dialog.get_response() == widgetUtils.OK:
self.postprocess()
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
output.speak(_(u"Attaching..."))
if self.dialog.get("services") == "SNDUp":
base_url = "https://sndup.net/post.php"
if len(self.config["sound"]["sndup_api_key"]) > 0:
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
else:
url = base_url
self.uploaderFunction = transfer.Upload(obj=self, field='file', url=url, filename=self.file, completed_callback=completed_callback)
pub.subscribe(self.uploaderDialog.update, "uploading")
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
def __init__(self, configFile, completed_callback):
self.config = configFile
super(audioUploader, self).__init__()
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
self.file = None
self.recorded = False
self.recording = None
self.playing = None
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
if self.dialog.get_response() == widgetUtils.OK:
self.postprocess()
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
output.speak(_(u"Attaching..."))
if self.dialog.get("services") == "SNDUp":
base_url = "https://sndup.net/post.php"
if len(self.config["sound"]["sndup_api_key"]) > 0:
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
else:
url = base_url
self.uploaderFunction = transfer.Upload(obj=self, field='file', url=url, filename=self.file, completed_callback=completed_callback)
pub.subscribe(self.uploaderDialog.update, "uploading")
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
def get_available_services(self):
services = []
services.append("SNDUp")
return services
def get_available_services(self):
services = []
services.append("SNDUp")
return services
def on_pause(self, *args, **kwargs):
if self.dialog.get("pause") == _(u"Pause"):
self.recording.pause()
self.dialog.set("pause", _(u"&Resume"))
elif self.dialog.get("pause") == _(u"Resume"):
self.recording.play()
self.dialog.set("pause", _(U"&Pause"))
def on_pause(self, *args, **kwargs):
if self.dialog.get("pause") == _(u"Pause"):
self.recording.pause()
self.dialog.set("pause", _(u"&Resume"))
elif self.dialog.get("pause") == _(u"Resume"):
self.recording.play()
self.dialog.set("pause", _(U"&Pause"))
def on_record(self, *args, **kwargs):
if self.recording != None:
self.stop_recording()
self.dialog.disable_control("pause")
else:
self.start_recording()
self.dialog.enable_control("pause")
def on_record(self, *args, **kwargs):
if self.recording != None:
self.stop_recording()
self.dialog.disable_control("pause")
else:
self.start_recording()
self.dialog.enable_control("pause")
def start_recording(self):
self.dialog.disable_control("attach_exists")
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.dialog.set("record", _(u"&Stop"))
output.speak(_(u"Recording"))
def start_recording(self):
self.dialog.disable_control("attach_exists")
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.dialog.set("record", _(u"&Stop"))
output.speak(_(u"Recording"))
def stop_recording(self):
self.recording.stop()
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.dialog.set("record", _(u"&Record"))
self.file_attached()
def stop_recording(self):
self.recording.stop()
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.dialog.set("record", _(u"&Record"))
self.file_attached()
def file_attached(self):
self.dialog.set("pause", _(u"&Pause"))
self.dialog.disable_control("record")
self.dialog.enable_control("play")
self.dialog.enable_control("discard")
self.dialog.disable_control("attach_exists")
self.dialog.enable_control("attach")
self.dialog.play.SetFocus()
def file_attached(self):
self.dialog.set("pause", _(u"&Pause"))
self.dialog.disable_control("record")
self.dialog.enable_control("play")
self.dialog.enable_control("discard")
self.dialog.disable_control("attach_exists")
self.dialog.enable_control("attach")
self.dialog.play.SetFocus()
def on_discard(self, *args, **kwargs):
if self.playing:
self._stop()
if self.recording != None:
self.cleanup()
self.dialog.disable_control("attach")
self.dialog.disable_control("play")
self.file = None
self.dialog.enable_control("record")
self.dialog.enable_control("attach_exists")
self.dialog.record.SetFocus()
self.dialog.disable_control("discard")
self.recording = None
output.speak(_(u"Discarded"))
def on_discard(self, *args, **kwargs):
if self.playing:
self._stop()
if self.recording != None:
self.cleanup()
self.dialog.disable_control("attach")
self.dialog.disable_control("play")
self.file = None
self.dialog.enable_control("record")
self.dialog.enable_control("attach_exists")
self.dialog.record.SetFocus()
self.dialog.disable_control("discard")
self.recording = None
output.speak(_(u"Discarded"))
def on_play(self, *args, **kwargs):
if not self.playing:
call_threaded(self._play)
else:
self._stop()
def on_play(self, *args, **kwargs):
if not self.playing:
call_threaded(self._play)
else:
self._stop()
def _play(self):
output.speak(_(u"Playing..."))
def _play(self):
output.speak(_(u"Playing..."))
# try:
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play()
self.dialog.set("play", _(u"&Stop"))
try:
while self.playing.is_playing:
pass
self.dialog.set("play", _(u"&Play"))
self.playing.free()
self.playing = None
except:
pass
self.playing = sound_lib.stream.FileStream(file=str(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play()
self.dialog.set("play", _(u"&Stop"))
try:
while self.playing.is_playing:
pass
self.dialog.set("play", _(u"&Play"))
self.playing.free()
self.playing = None
except:
pass
def _stop(self):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.dialog.set("play", _(u"&Play"))
self.playing = None
def _stop(self):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.dialog.set("play", _(u"&Play"))
self.playing = None
def postprocess(self):
if self.file.lower().endswith('.wav'):
output.speak(_(u"Recoding audio..."))
sound.recode_audio(self.file)
self.wav_file = self.file
self.file = '%s.ogg' % self.file[:-4]
def postprocess(self):
if self.file.lower().endswith('.wav'):
output.speak(_(u"Recoding audio..."))
sound.recode_audio(self.file)
self.wav_file = self.file
self.file = '%s.ogg' % self.file[:-4]
def cleanup(self):
if self.playing and self.playing.is_playing:
self.playing.stop()
if self.recording != None:
if self.recording.is_playing:
self.recording.stop()
try:
self.recording.free()
except:
pass
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
def cleanup(self):
if self.playing and self.playing.is_playing:
self.playing.stop()
if self.recording != None:
if self.recording.is_playing:
self.recording.stop()
try:
self.recording.free()
except:
pass
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
def on_attach_exists(self, *args, **kwargs):
self.file = self.dialog.get_file()
if self.file != False:
self.file_attached()
def on_attach_exists(self, *args, **kwargs):
self.file = self.dialog.get_file()
if self.file != False:
self.file_attached()

View File

@@ -1,5 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from builtins import object
from past.utils import old_div
import sys
import threading
import time
@@ -11,74 +15,74 @@ from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncod
import requests
import os
class Upload(object):
def __init__(self, field=None, obj=None, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
super(Upload, self).__init__(*args, **kwargs)
self.url=url
self.filename=filename
log.debug("Uploading audio to %s, filename %s" % (url, filename))
self.start_time = None
self.completed_callback = completed_callback
self.background_thread = None
self.transfer_rate = 0
self.local_filename=os.path.basename(self.filename)
if isinstance(self.local_filename, unicode):
self.local_filename=self.local_filename.encode(sys.getfilesystemencoding())
self.fin=open(self.filename, 'rb')
self.m = MultipartEncoder(fields={field:(self.local_filename, self.fin, "application/octet-stream")})
self.monitor = MultipartEncoderMonitor(self.m, self.progress_callback)
self.response=None
self.obj=obj
self.follow_location=follow_location
#the verbose parameter is deprecated and will be removed soon
def __init__(self, field=None, obj=None, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
super(Upload, self).__init__(*args, **kwargs)
self.url=url
self.filename=filename
log.debug("Uploading audio to %s, filename %s" % (url, filename))
self.start_time = None
self.completed_callback = completed_callback
self.background_thread = None
self.transfer_rate = 0
self.local_filename=os.path.basename(self.filename)
if isinstance(self.local_filename, str):
self.local_filename=self.local_filename.encode(sys.getfilesystemencoding())
self.fin=open(self.filename, 'rb')
self.m = MultipartEncoder(fields={field:(self.local_filename, self.fin, "application/octet-stream")})
self.monitor = MultipartEncoderMonitor(self.m, self.progress_callback)
self.response=None
self.obj=obj
self.follow_location=follow_location
#the verbose parameter is deprecated and will be removed soon
def elapsed_time(self):
if not self.start_time:
return 0
return time.time() - self.start_time
def elapsed_time(self):
if not self.start_time:
return 0
return time.time() - self.start_time
def progress_callback(self, monitor):
progress = {}
progress["total"] = monitor.len
progress["current"] = monitor.bytes_read
if progress["current"] == 0:
progress["percent"] = 0
self.transfer_rate = 0
else:
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
self.transfer_rate = progress["current"] / self.elapsed_time()
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
if self.transfer_rate:
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
else:
progress["eta"] = 0
pub.sendMessage("uploading", data=progress)
def progress_callback(self, monitor):
progress = {}
progress["total"] = monitor.len
progress["current"] = monitor.bytes_read
if progress["current"] == 0:
progress["percent"] = 0
self.transfer_rate = 0
else:
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
self.transfer_rate = old_div(progress["current"], self.elapsed_time())
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
if self.transfer_rate:
progress["eta"] = old_div((progress["total"] - progress["current"]), self.transfer_rate)
else:
progress["eta"] = 0
pub.sendMessage("uploading", data=progress)
def perform_transfer(self):
log.debug("starting upload...")
self.start_time = time.time()
self.response=requests.post(url=self.url, data=self.monitor, headers={"Content-Type":self.m.content_type}, allow_redirects=self.follow_location, stream=True)
log.debug("Upload finished.")
self.complete_transfer()
def perform_transfer(self):
log.debug("starting upload...")
self.start_time = time.time()
self.response=requests.post(url=self.url, data=self.monitor, headers={"Content-Type":self.m.content_type}, allow_redirects=self.follow_location, stream=True)
log.debug("Upload finished.")
self.complete_transfer()
def perform_threaded(self, *args, **kwargs):
self.background_thread = threading.Thread(target=self.perform_transfer)
self.background_thread.daemon = True
self.background_thread.start()
def perform_threaded(self, *args, **kwargs):
self.background_thread = threading.Thread(target=self.perform_transfer)
self.background_thread.daemon = True
self.background_thread.start()
def complete_transfer(self):
if callable(self.completed_callback):
self.completed_callback(self.obj)
if hasattr(self,'fin') and callable(self.fin.close):
self.fin.close()
def complete_transfer(self):
if callable(self.completed_callback):
self.completed_callback(self.obj)
if hasattr(self,'fin') and callable(self.fin.close):
self.fin.close()
def get_url(self):
try:
data = self.response.json()
except:
return _("Error in file upload: {0}").format(self.data.content,)
if "url" in data and data["url"] != "0":
return data["url"]
elif "error" in data and data["error"] != "0":
return data["error"]
else:
return _("Error in file upload: {0}").format(self.data.content,)
def get_url(self):
try:
data = self.response.json()
except:
return _("Error in file upload: {0}").format(self.data.content,)
if "url" in data and data["url"] != "0":
return data["url"]
elif "error" in data and data["error"] != "0":
return data["error"]
else:
return _("Error in file upload: {0}").format(self.data.content,)

View File

@@ -1,42 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from builtins import str
def convert_bytes(n):
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
if n >= P:
return '%.2fPb' % (float(n) / T)
elif n >= T:
return '%.2fTb' % (float(n) / T)
elif n >= G:
return '%.2fGb' % (float(n) / G)
elif n >= M:
return '%.2fMb' % (float(n) / M)
elif n >= K:
return '%.2fKb' % (float(n) / K)
else:
return '%d' % n
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
if n >= P:
return '%.2fPb' % (float(n) / T)
elif n >= T:
return '%.2fTb' % (float(n) / T)
elif n >= G:
return '%.2fGb' % (float(n) / G)
elif n >= M:
return '%.2fMb' % (float(n) / M)
elif n >= K:
return '%.2fKb' % (float(n) / K)
else:
return '%d' % n
def seconds_to_string(seconds, precision=0):
day = seconds // 86400
hour = seconds // 3600
min = (seconds // 60) % 60
sec = seconds - (hour * 3600) - (min * 60)
sec_spec = "." + str(precision) + "f"
sec_string = sec.__format__(sec_spec)
string = ""
if day == 1:
string += _(u"%d day, ") % day
elif day >= 2:
string += _(u"%d days, ") % day
if (hour == 1):
string += _(u"%d hour, ") % hour
elif (hour >= 2):
string += _("%d hours, ") % hour
if (min == 1):
string += _(u"%d minute, ") % min
elif (min >= 2):
string += _(u"%d minutes, ") % min
if sec >= 0 and sec <= 2:
string += _(u"%s second") % sec_string
else:
string += _(u"%s seconds") % sec_string
return string
day = seconds // 86400
hour = seconds // 3600
min = (seconds // 60) % 60
sec = seconds - (hour * 3600) - (min * 60)
sec_spec = "." + str(precision) + "f"
sec_string = sec.__format__(sec_spec)
string = ""
if day == 1:
string += _(u"%d day, ") % day
elif day >= 2:
string += _(u"%d days, ") % day
if (hour == 1):
string += _(u"%d hour, ") % hour
elif (hour >= 2):
string += _("%d hours, ") % hour
if (min == 1):
string += _(u"%d minute, ") % min
elif (min >= 2):
string += _(u"%d minutes, ") % min
if sec >= 0 and sec <= 2:
string += _(u"%s second") % sec_string
else:
string += _(u"%s seconds") % sec_string
return string

View File

@@ -1,62 +1,63 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
import wx
from .utils import *
import widgetUtils
class UploadDialog(widgetUtils.BaseDialog):
def __init__(self, filename, *args, **kwargs):
super(UploadDialog, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwargs)
self.pane = wx.Panel(self)
self.progress_bar = wx.Gauge(parent=self.pane)
fileBox = wx.BoxSizer(wx.HORIZONTAL)
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
self.file.SetFocus()
fileBox.Add(fileLabel)
fileBox.Add(self.file)
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
currentAmountBox.Add(current_amount_label)
currentAmountBox.Add(self.current_amount)
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
totalSizeBox.Add(total_size_label)
totalSizeBox.Add(self.total_size)
speedBox = wx.BoxSizer(wx.HORIZONTAL)
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
speedBox.Add(speedLabel)
speedBox.Add(self.speed)
etaBox = wx.BoxSizer(wx.HORIZONTAL)
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
etaBox.Add(etaLabel)
etaBox.Add(self.eta)
self.create_buttons()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(fileBox)
sizer.Add(currentAmountBox)
sizer.Add(totalSizeBox)
sizer.Add(speedBox)
sizer.Add(etaBox)
sizer.Add(self.progress_bar)
self.pane.SetSizerAndFit(sizer)
def __init__(self, filename, *args, **kwargs):
super(UploadDialog, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwargs)
self.pane = wx.Panel(self)
self.progress_bar = wx.Gauge(parent=self.pane)
fileBox = wx.BoxSizer(wx.HORIZONTAL)
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
self.file.SetFocus()
fileBox.Add(fileLabel)
fileBox.Add(self.file)
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
currentAmountBox.Add(current_amount_label)
currentAmountBox.Add(self.current_amount)
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
totalSizeBox.Add(total_size_label)
totalSizeBox.Add(self.total_size)
speedBox = wx.BoxSizer(wx.HORIZONTAL)
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
speedBox.Add(speedLabel)
speedBox.Add(self.speed)
etaBox = wx.BoxSizer(wx.HORIZONTAL)
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
etaBox.Add(etaLabel)
etaBox.Add(self.eta)
self.create_buttons()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(fileBox)
sizer.Add(currentAmountBox)
sizer.Add(totalSizeBox)
sizer.Add(speedBox)
sizer.Add(etaBox)
sizer.Add(self.progress_bar)
self.pane.SetSizerAndFit(sizer)
def update(self, data):
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
wx.CallAfter(self.speed.SetValue, data["speed"])
if data["eta"]:
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
def update(self, data):
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
wx.CallAfter(self.speed.SetValue, data["speed"])
if data["eta"]:
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
def create_buttons(self):
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
def create_buttons(self):
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
def get_response(self, fn):
wx.CallAfter(fn, 0.01)
self.ShowModal()
def get_response(self, fn):
wx.CallAfter(fn, 0.01)
self.ShowModal()

View File

@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
from __future__ import unicode_literals
import wx
import widgetUtils
import output
@@ -23,56 +24,56 @@ import logging
log = logging.getLogger("extra.AudioUploader.wx_UI")
class audioDialog(widgetUtils.BaseDialog):
def __init__(self, services):
log.debug("creating audio dialog.")
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
def __init__(self, services):
log.debug("creating audio dialog.")
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.play = wx.Button(panel, -1, _(u"&Play"))
self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"&Pause"))
self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"&Record"))
self.record.SetFocus()
self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file"))
self.discard = wx.Button(panel, -1, _(u"&Discard"))
self.discard.Disable()
label = wx.StaticText(panel, -1, _(u"Upload to"))
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
servicesBox.Add(label, 0, wx.ALL, 5)
servicesBox.Add(self.services, 0, wx.ALL, 5)
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
self.attach.Disable()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel"))
btnSizer.Add(self.play, 0, wx.ALL, 5)
btnSizer.Add(self.pause, 0, wx.ALL, 5)
btnSizer.Add(self.record, 0, wx.ALL, 5)
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
btnSizer2.Add(cancel, 0, wx.ALL, 5)
sizer.Add(servicesBox, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
sizer.Add(btnSizer2, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
self.play = wx.Button(panel, -1, _(u"&Play"))
self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"&Pause"))
self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"&Record"))
self.record.SetFocus()
self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file"))
self.discard = wx.Button(panel, -1, _(u"&Discard"))
self.discard.Disable()
label = wx.StaticText(panel, -1, _(u"Upload to"))
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
servicesBox.Add(label, 0, wx.ALL, 5)
servicesBox.Add(self.services, 0, wx.ALL, 5)
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
self.attach.Disable()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel"))
btnSizer.Add(self.play, 0, wx.ALL, 5)
btnSizer.Add(self.pause, 0, wx.ALL, 5)
btnSizer.Add(self.record, 0, wx.ALL, 5)
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
btnSizer2.Add(cancel, 0, wx.ALL, 5)
sizer.Add(servicesBox, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
sizer.Add(btnSizer2, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def enable_control(self, control):
log.debug("Enabling control %s" % (control,))
if hasattr(self, control):
getattr(self, control).Enable()
def enable_control(self, control):
log.debug("Enabling control %s" % (control,))
if hasattr(self, control):
getattr(self, control).Enable()
def disable_control(self, control):
log.debug("Disabling control %s" % (control,))
if hasattr(self, control):
getattr(self, control).Disable()
def disable_control(self, control):
log.debug("Disabling control %s" % (control,))
if hasattr(self, control):
getattr(self, control).Disable()
def get_file(self):
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return False
return openFileDialog.GetPath()
def get_file(self):
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return False
return openFileDialog.GetPath()

View File

@@ -1,2 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from .soundsTutorial import soundsTutorial

View File

@@ -1,25 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from gi.repository import Gtk
import widgetUtils
class soundsTutorialDialog(Gtk.Dialog):
def __init__(self, actions):
super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
box = self.get_content_area()
label = Gtk.Label("Press enter for listen the sound")
self.list = widgetUtils.list("Action")
self.populate_actions(actions)
lBox = Gtk.Box(spacing=6)
lBox.add(label)
lBox.add(self.list.list)
box.add(lBox)
self.play = Gtk.Button("Play")
box.add(self.play)
self.show_all()
def __init__(self, actions):
super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
box = self.get_content_area()
label = Gtk.Label("Press enter for listen the sound")
self.list = widgetUtils.list("Action")
self.populate_actions(actions)
lBox = Gtk.Box(spacing=6)
lBox.add(label)
lBox.add(self.list.list)
box.add(lBox)
self.play = Gtk.Button("Play")
box.add(self.play)
self.show_all()
def populate_actions(self, actions):
for i in actions:
self.list.insert_item(i)
def populate_actions(self, actions):
for i in actions:
self.list.insert_item(i)
def get_selected(self):
return self.list.get_selected()
def get_selected(self):
return self.list.get_selected()

View File

@@ -1,3 +1,4 @@
from __future__ import unicode_literals
#Reverse sort, by Bill Dengler <codeofdusk@gmail.com> for use in TWBlue http://twblue.es
def invert_tuples(t):
"Invert a list of tuples, so that the 0th element becomes the -1th, and the -1th becomes the 0th."
@@ -8,4 +9,4 @@ def invert_tuples(t):
def reverse_sort(t):
"Sorts a list of tuples/lists by their last elements, not their first."
return invert_tuples(sorted(invert_tuples(t)))
return invert_tuples(sorted(invert_tuples(t)))

View File

@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import object
import platform
import widgetUtils
import os
@@ -8,28 +10,28 @@ import logging
log = logging.getLogger("extra.SoundsTutorial.soundsTutorial")
from . import soundsTutorial_constants
if platform.system() == "Windows":
from . import wx_ui as UI
from . import wx_ui as UI
elif platform.system() == "Linux":
from . import gtk_ui as UI
from . import gtk_ui as UI
class soundsTutorial(object):
def __init__(self, sessionObject):
log.debug("Creating sounds tutorial object...")
super(soundsTutorial, self).__init__()
self.session = sessionObject
self.actions = []
log.debug("Loading actions for sounds tutorial...")
[self.actions.append(i[1]) for i in soundsTutorial_constants.actions]
self.files = []
log.debug("Searching sound files...")
[self.files.append(i[0]) for i in soundsTutorial_constants.actions]
log.debug("Creating dialog...")
self.dialog = UI.soundsTutorialDialog(self.actions)
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
self.dialog.get_response()
def __init__(self, sessionObject):
log.debug("Creating sounds tutorial object...")
super(soundsTutorial, self).__init__()
self.session = sessionObject
self.actions = []
log.debug("Loading actions for sounds tutorial...")
[self.actions.append(i[1]) for i in soundsTutorial_constants.actions]
self.files = []
log.debug("Searching sound files...")
[self.files.append(i[0]) for i in soundsTutorial_constants.actions]
log.debug("Creating dialog...")
self.dialog = UI.soundsTutorialDialog(self.actions)
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
self.dialog.get_response()
def on_play(self, *args, **kwargs):
try:
self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg")
except:
log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],))
def on_play(self, *args, **kwargs):
try:
self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg")
except:
log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],))

View File

@@ -1,30 +1,31 @@
#-*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
#-*- coding: utf-8 -*-
from . import reverse_sort
import application
actions = reverse_sort.reverse_sort([ ("audio", _(u"Audio tweet.")),
("create_timeline", _(u"User timeline buffer created.")),
("delete_timeline", _(u"Buffer destroied.")),
("dm_received", _(u"Direct message received.")),
("dm_sent", _(u"Direct message sent.")),
("error", _(u"Error.")),
("favourite", _(u"Tweet liked.")),
("favourites_timeline_updated", _(u"Likes buffer updated.")),
("geo", _(u"Geotweet.")),
("image", _("Tweet contains one or more images")),
("limit", _(u"Boundary reached.")),
("list_tweet", _(u"List updated.")),
("max_length", _(u"Too many characters.")),
("mention_received", _(u"Mention received.")),
("new_event", _(u"New event.")),
("ready", _(u"{0} is ready.").format(application.name,)),
("reply_send", _(u"Mention sent.")),
("retweet_send", _(u"Tweet retweeted.")),
("search_updated", _(u"Search buffer updated.")),
("tweet_received", _(u"Tweet received.")),
("tweet_send", _(u"Tweet sent.")),
("trends_updated", _(u"Trending topics buffer updated.")),
("tweet_timeline", _(u"New tweet in user timeline buffer.")),
("update_followers", _(u"New follower.")),
("volume_changed", _(u"Volume changed."))])
("create_timeline", _(u"User timeline buffer created.")),
("delete_timeline", _(u"Buffer destroied.")),
("dm_received", _(u"Direct message received.")),
("dm_sent", _(u"Direct message sent.")),
("error", _(u"Error.")),
("favourite", _(u"Tweet liked.")),
("favourites_timeline_updated", _(u"Likes buffer updated.")),
("geo", _(u"Geotweet.")),
("image", _("Tweet contains one or more images")),
("limit", _(u"Boundary reached.")),
("list_tweet", _(u"List updated.")),
("max_length", _(u"Too many characters.")),
("mention_received", _(u"Mention received.")),
("new_event", _(u"New event.")),
("ready", _(u"{0} is ready.").format(application.name,)),
("reply_send", _(u"Mention sent.")),
("retweet_send", _(u"Tweet retweeted.")),
("search_updated", _(u"Search buffer updated.")),
("tweet_received", _(u"Tweet received.")),
("tweet_send", _(u"Tweet sent.")),
("trends_updated", _(u"Trending topics buffer updated.")),
("tweet_timeline", _(u"New tweet in user timeline buffer.")),
("update_followers", _(u"New follower.")),
("volume_changed", _(u"Volume changed."))])

View File

@@ -1,29 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import wx
import widgetUtils
class soundsTutorialDialog(widgetUtils.BaseDialog):
def __init__(self, actions):
super(soundsTutorialDialog, self).__init__(None, -1)
self.SetTitle(_(u"Sounds tutorial"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE)
self.items.SetSelection(0)
listBox = wx.BoxSizer(wx.HORIZONTAL)
listBox.Add(label)
listBox.Add(self.items)
self.play = wx.Button(panel, 1, (u"Play"))
self.play.SetDefault()
close = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(self.play)
btnBox.Add(close)
sizer.Add(listBox)
sizer.Add(btnBox)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def __init__(self, actions):
super(soundsTutorialDialog, self).__init__(None, -1)
self.SetTitle(_(u"Sounds tutorial"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE)
self.items.SetSelection(0)
listBox = wx.BoxSizer(wx.HORIZONTAL)
listBox.Add(label)
listBox.Add(self.items)
self.play = wx.Button(panel, 1, (u"Play"))
self.play.SetDefault()
close = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(self.play)
btnBox.Add(close)
sizer.Add(listBox)
sizer.Add(btnBox)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def get_selection(self):
return self.items.GetSelection()
def get_selection(self):
return self.items.GetSelection()

Some files were not shown because too many files have changed in this diff Show More