Compare commits

..

284 Commits

Author SHA1 Message Date
Jose Manuel Delicado
3c5de10fe6 pa.c/appinfo.ini: updated format version to 3.4 2016-10-02 17:04:37 +02:00
Jose Manuel Delicado
230e0fa951 Removed unnecesary txt file from arabic translation folder 2016-10-02 16:47:54 +02:00
31192a5801 Updated locales 2016-10-02 09:40:45 -05:00
f1c974b4cf Reply to tweets will always be quotes 2016-10-02 09:11:02 -05:00
Jose Manuel Delicado
a9f9cb7969 Updated Windows dependencies. Updated readme 2016-10-02 15:20:14 +02:00
Jose Manuel Delicado
3a3393e03a Increased version number to 0.86 2016-10-02 15:14:32 +02:00
3a7c0c8f12 Added a fix for composing tweets in the new way twitter provides them 2016-10-02 07:54:23 -05:00
bd2460e42b Return false in twishort module if a long tweet has been deleted 2016-10-02 07:52:02 -05:00
Jose Manuel Delicado
2dd262cc83 TWBlue can now send long tweets directly to twishort. TWBlue server is nolonger used for this purpose 2016-10-02 14:35:54 +02:00
Jose Manuel Delicado
a2cf1ac8d0 Merge branch 'next-gen' of github.com:manuelcortez/twblue into next-gen 2016-10-02 10:38:40 +02:00
df345b5eec Initial support for extended tweets 2016-09-28 17:25:03 -05:00
Jose Manuel Delicado
b535c996d6 Merge branch 'next-gen' of github.com:manuelcortez/twblue into next-gen 2016-08-13 12:37:06 +02:00
b131f50d42 Updated windows 10 keymap 2016-08-12 23:40:56 -05:00
d729f86898 Merge pull request #87 from masonasons/next-gen
Adds functions to seek the playing audio forward or back 5 seconds.
2016-08-11 03:00:31 -05:00
Mason Armstrong
51ef047fb6 Adds functions to seek the playing audio forward or back 5 seconds. Adds function in audio_player to play/pause the audio but wouldn't work right when bound to the UI so not implemented. 2016-08-10 21:59:19 -05:00
Jose Manuel Delicado
5565e08610 Updated translations and documentation 2016-08-06 22:40:10 +02:00
ab65a8f6db Fixed order for some timelines at startup 2016-08-06 15:11:10 -05:00
7f3c363da8 Call shelf for sessions every 5 minutes 2016-08-06 14:47:42 -05:00
7bc26a94c7 Fixed a bug with database and put_items_on_list 2016-08-06 14:09:27 -05:00
Jose Manuel Delicado
47b8575f55 Updated translations and documentation 2016-08-06 18:40:20 +02:00
Jose Manuel Delicado
2a27998831 Fixed sintax error in setup.py 2016-08-06 18:30:17 +02:00
Jose Manuel Delicado
a64e89994f Added spelling dictionaries for almost all application languages. Setup.py has been modified to handle them automatically. Remember running git submodule update. Increased version number to 0.85. Updated readme 2016-08-06 18:22:54 +02:00
05a8cb7a28 Added run at startup option for installed version 2016-08-05 15:31:22 -05:00
Jose Manuel Delicado
b2ebbf5b7a Merge branch 'next-gen' of github.com:manuelcortez/twblue into next-gen 2016-08-05 15:50:59 +02:00
Jose Manuel Delicado
a03ef851e3 The buffers dialog in account configuration now includes the buffer translated name 2016-08-05 15:50:46 +02:00
7a8d000635 Updated documentation. Updated translation catalogs 2016-08-05 08:34:28 -05:00
41b10e3a84 Long tweets are displayed properly in lists 2016-08-04 17:40:07 -05:00
4056c73052 Updated documentation and process to generate html files 2016-08-04 09:19:47 -05:00
ef95244f0c Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-08-04 08:07:50 -05:00
143ad70073 Fixed a bug with session reconnection 2016-08-04 08:06:39 -05:00
817d7bd6bb Disabled response for tweet deletions due to a bug 2016-08-04 08:02:46 -05:00
3fca5c20fb Merge pull request #84 from TWBlueQS/next-gen
Fixed a bug with galician, vasque and catala languages and arrow when selecting user default as application's language.
2016-08-03 11:07:13 -05:00
Iván Novegil
a373ef8dff Fixed a bug with galician, vasque and catala languages and arrow when selecting user default as application's language. 2016-08-03 17:55:33 +02:00
696d1764bd Changes default keymap to win10 keymap automatically 2016-08-03 09:45:31 -05:00
14b2189840 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-08-02 05:38:34 -05:00
16a94aded1 fixed a typo in the russian translation 2016-08-01 15:54:20 -05:00
Jose Manuel Delicado
d352aaeaac Fixed bugs in galician translation 2016-08-01 16:45:01 +02:00
Jose Manuel Delicado
9250aad80b Updated version to 0.84 2016-07-31 20:55:22 +02:00
Jose Manuel Delicado
6e9be63029 Updated translations and documentation 2016-07-31 18:24:47 +02:00
92d8c7257e Changes in long tweet checkbox are reflected in the title bar 2016-07-28 12:45:06 -05:00
Jose Manuel Delicado
6a265f0203 When posting a tweet or dm, the dialog title bar will be updated if an URL is shortened, the tweet is translated or we apply spell checking 2016-07-28 12:35:28 +02:00
Jose Manuel Delicado
b30a0ac25b Call urllib.quote() in the patched urllib3 function 2016-07-28 00:13:16 +02:00
Jose Manuel Delicado
212f49df08 Patched urllib3 included in requests to avoid encoding audio filenames 2016-07-27 23:26:51 +02:00
2c58645e02 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-07-27 12:07:54 -05:00
e11a727a77 Updated translations: Russian, finnish, french, german, italian and romanian 2016-07-27 12:07:27 -05:00
Jose Manuel Delicado
d2123fdab7 Try to fix Audio Uploader when the audio file name contains non-ascii characters 2016-07-27 19:05:35 +02:00
45263732b8 Fixed focus movement in tweets. Needs tests 2016-07-27 11:56:38 -05:00
d50e8e2798 Fixed a bug with URLS in complex tweets 2016-07-25 13:58:05 -05:00
Jose Manuel Delicado
990ddf64f5 Updated version to 0.83. Added romanian documentation 2016-07-24 10:59:41 +02:00
ff529eacb4 Updated russian and turkish updates 2016-07-24 02:51:30 -05:00
Jose Manuel Delicado
238607bbe7 Updated pa.c format specification to version 3.3. Now setup.py collects all documentation, including license.txt 2016-07-24 00:31:45 +02:00
0aa51d9529 Updated contributors list 2016-07-23 16:54:53 -05:00
3815ce0a67 Enter will create a new tweet in trends buffers 2016-07-23 16:40:40 -05:00
5e91808b73 Added russian to documentation translations 2016-07-23 16:39:59 -05:00
08259cdf16 Updated program translations 2016-07-23 16:38:53 -05:00
0bbb3afde0 Now is possible to update a conversation 2016-07-23 16:24:06 -05:00
3c0110528f The documentation optino in the sys tray icon is enabled and working 2016-07-23 15:43:38 -05:00
18b52e8909 Fixed load of new items in the visible timeline with inverted buffers 2016-07-20 04:47:39 -05:00
037cfec91a Fixed some shorcuts for dialogs 2016-07-19 09:30:43 -05:00
695b35031e Fixed a bug with long tweets when posting 2016-07-19 09:06:43 -05:00
f9d869e824 Fixed a few issues with long tweets 2016-07-18 09:56:22 -05:00
c1c001ad96 Merge branch 'next-gen' of http://manuelcortez.net:1337/twblue/twblue into next-gen 2016-07-15 12:41:07 -05:00
d768afc329 Image uploader has been rewritten. Images can have description when uploading 2016-07-15 12:29:48 -05:00
Jose Manuel Delicado
4186f1a3e6 Merge branch 'next-gen' of manuelcortez.net:twblue/twblue into next-gen 2016-07-03 20:58:15 +02:00
Jose Manuel Delicado
2eac158a39 Updated Windows dependencies. 2016-07-03 20:57:24 +02:00
1ee629d731 Improving quoted retweets 2016-06-28 10:41:13 -05:00
5590ab47ee Possibly fixes errors with duplicates and focus change in the list 2016-06-28 10:11:56 -05:00
b555ed736c Modified .gitignore 2016-06-27 20:52:51 -05:00
da39f40048 Changed way of detecting quoted statuses. Parsing improvements in tweet displayer 2016-06-27 09:44:36 -05:00
b1cf1c5590 Fixed some things related to twishort's tweets 2016-06-25 19:48:21 -05:00
48232a6cf8 Added set_description to twython API 2016-06-25 17:42:25 -05:00
3bc92af55f Removed sessions when configuration is malformed 2016-05-13 12:09:38 -05:00
Jose Manuel Delicado
5e9218b072 Updated dependencies. Updated documentation about how to build the pa.c version 2016-05-09 10:17:32 +02:00
Jose Manuel Delicado
65f860ceef Installer: upgraded all version strings to 0.82 2016-05-08 12:34:06 +02:00
Jose Manuel Delicado
b9b8145bca Added function com_path() to paths.py. This function is now used in libloader/com.py. Added winpaths to the easy_install example in readme.md. Updated application.py in doc folder. 2016-05-08 11:54:10 +02:00
Jose Manuel Delicado
7fab6bcf54 setup.py: retrieve cacert.pem from requests package. Create application compressed archive. Added a fix for requests to support cacert.pem in different locations. 2016-05-08 00:08:40 +02:00
Jose Manuel Delicado
7ad5e6fa37 Goodbye, Pycurl. Now TWBlue uses requests and requests-based packages for all http connections. Remember to run git submodule update before submiting any commits 2016-05-07 19:26:40 +02:00
Jose Manuel Delicado
f6fec67d52 Small fix in audio uploader 2016-05-06 18:23:26 +02:00
Jose Manuel Delicado
b3cac85c4e AudioUploader: some code cleaning, optimizations and bug fixes 2016-05-06 17:38:17 +02:00
Jose Manuel Delicado
ff49bd2488 extras/audioUploader/transfer.py: use pycurl.XFERINFOFUNCTION; PROGRESSFUNCTION is deprecated since libcurl 7.32. Only convert_bytes is imported from utils module. 2016-05-06 10:24:01 +02:00
Jose Manuel Delicado
45f23a4c8a To protect users privacy, TwUp has been placed after SNDUp in audio services list 2016-05-05 19:15:13 +02:00
Jose Manuel Delicado
a67a5e6264 Updated Windows dependencies. Remember to run git submodule update before submiting new commits. 2016-05-01 10:35:05 +02:00
8988d63f33 Fixed audio upload dialog 2016-04-30 06:16:16 -05:00
2268619101 some improvements in long tweets 2016-04-28 13:54:06 -05:00
a312b7f63c Integrate check for updates at startup in the general tab 2016-04-27 16:33:51 -05:00
92d803717f Added Tweet deletion based in streaming events 2016-04-16 13:31:37 -05:00
2124f6c60b Merge pull request #70 from TWBlueQS/next-gen
Added version indication on the issue-reporting system
2016-04-16 11:31:23 -05:00
29c87dbd3f Merge pull request #79 from codeofdusk/postabandon1
post-abandonment : stage I
2016-04-16 11:29:05 -05:00
1ea3c5d23b Merge pull request #78 from codeofdusk/next-gen
English cleanup
2016-04-16 11:25:02 -05:00
Bill Dengler
547f9393b9 Post-abandonment stage I (add tab to settings, add option to check for updates on app launch). Fixed #76 2016-04-15 18:08:50 -04:00
Bill Dengler
16b34c827b Update readme.
Clean up readme.

change favorite to like in README.
2016-04-15 16:57:50 -04:00
Bill Dengler
56f0f37f39 Fix soundnotes.
Fix soundnotes.

Fix soundnotes.

Add information to soundnotes.

Fixed soundnotes.
2016-04-15 16:57:40 -04:00
3e9143d607 Display direct message dialog properly. Fixed #75 2016-04-11 08:21:29 -05:00
da07859138 When session mute is enabled, you shouldn't hear tl's and lists' updates 2016-04-02 06:57:50 -06:00
dfc2b605f5 Added audio playback from soundcloud 2016-04-02 03:10:29 -06:00
8badd3987a Storage: Removed clean the local database before being deleted 2016-04-02 02:59:59 -06:00
7139a2bcb3 Conversations: Ignores deleted tweets and 404 errors 2016-04-01 08:55:30 -06:00
c4e2c3b57a Fixed photo upload when posting tweets 2016-03-29 16:09:44 -06:00
ed95270d3b Updated twython 2016-03-29 16:09:09 -06:00
edd45a1adf Image description is shown in the view tweets dialogue if is possible 2016-03-29 15:12:16 -06:00
f24d5fec4e Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-03-28 17:35:57 -06:00
a9b47bb1a4 Tweets made from Rossiyskaya Gazeta don't break the tweets' viewer anymore 2016-03-28 17:35:00 -06:00
8849ce9039 Removed pocket from configuration 2016-03-28 17:03:36 -06:00
Jose Manuel Delicado
8b4f16ef84 Updated version to 0.82. Stable.json: updated version to 0.81 2016-03-28 13:16:25 +02:00
dbbe6c0600 TWBlue 0.81 has been officially released 2016-03-25 01:24:30 -06:00
3caef5fc81 Updated translations 2016-03-24 22:33:53 -06:00
7cd58708cc Added FightingGames as an official soundpack. @deng90 2016-03-24 22:32:28 -06:00
0814af3bf4 Added notes to the Qwitter soundpack 2016-03-24 22:30:52 -06:00
72ba5a74f5 Minor changes before the next release 2016-03-24 22:18:51 -06:00
cbc301141e Updated translation templates 2016-03-24 22:17:54 -06:00
f466516289 Merge pull request #72 from Oliver2213/next-gen-fork
Fix an error where the geolocate dialog wasn't loading properly
2016-03-23 23:44:01 -06:00
4eef236b1c Merge pull request #73 from Oliver2213/shortcutfix
Fix duplicate shortcut in the reply to tweet dialog
2016-03-23 23:43:24 -06:00
Blake Oliver
ce00083aa2 Fix duplicate shortcut in the reply to tweet dialog
Mention all is now alt+m, translate stays alt+t
2016-03-22 18:35:14 -04:00
Blake Oliver
f92e05ce72 Fix an error where the geolocate dialog wasn't loading properly
For some reason, a standard tweet dialog was being used to display this information, which comes along with spell check, translate, etc; functions a simple geolocate display box doesn't need.
The error was happening because the tweet show dialog was accepting (tweet, tweetList, is_tweet=True) as args, and the call to display the geodata was only providing geodata as tweet, and false as tweetList.
This method signature was erroring out because is_tweet was set to True by default; the False was being accepted as tweetList, which the method later tries to call __str__ which it doesn't have.
I could have just added the is_tweet keyword, but then I would need to change the signature so tweetList is set to none by default, update the method, bla, bla, bla. Too much work.
Instead I added another function in wxUI.commonMessageDialogs called view_geodata, which just accepts the data in question as an argument and displays that in a message box.
2016-03-22 11:21:27 -04:00
c02a11f269 Merge pull request #71 from Oliver2213/next-gen-fork
Add hotkeys for some dialogs, making navigation much faster
2016-03-20 10:47:22 -06:00
Blake Oliver
c79e659b74 Add hotkeys for some dialogs, making navigation much faster 2016-03-20 10:26:05 -04:00
da8009aea0 If a config file is bad formed, it should print errors in logs 2016-03-19 20:59:04 -06:00
2778d2e85d Fixed the chicken nugget keymap 2016-03-19 20:58:14 -06:00
ce9a50903c Changed tokens to the stable version 2016-03-19 20:08:48 -06:00
9a7d39c125 Don'w show relationship status if there is any relationship between user 2016-03-19 20:08:20 -06:00
89759e7d49 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-02-20 06:53:02 -06:00
7ca9d42f5f Added a new contributor. Welcome and thanks @ButchullaWoman! 2016-02-20 06:52:39 -06:00
Jose Manuel Delicado
6a6bec880c SessionManager.WXUI: now the dialog title is translatable 2016-02-20 13:51:12 +01:00
7b22c7d0f8 Added relationship information in user details dialogue. 2016-02-20 06:43:56 -06:00
058866831b Fixed an unhandled exception in updater 2016-02-20 06:34:26 -06:00
229f698e72 Added a mirror URL for checking updates if the main URL is not working 2016-02-19 09:15:46 -06:00
32067d3171 Added json files for the updater 2016-02-19 09:08:27 -06:00
a8f7477a1f new snapshot 2016-02-02 09:38:43 -06:00
9b6461e34e Updated translations 2016-02-02 09:37:06 -06:00
eed3f81cb8 Added a changelog html file 2016-02-02 09:30:13 -06:00
e605c750b1 Spell checker: Now it should work with the default language setting 2016-02-02 09:29:35 -06:00
f49be6cfed Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2016-02-02 09:09:56 -06:00
696faed007 Fixed a bug in the spell checker 2016-02-02 09:07:40 -06:00
Iván Novegil
70169a2a4a Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2016-01-31 12:18:19 +01:00
Jose Manuel Delicado
46b46c4d6d Some important code fixes 2016-01-30 17:14:41 +01:00
Iván Novegil
0875b7ef92 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2016-01-22 18:08:11 +01:00
Jose Manuel Delicado
9cdccca5ff Updated Windows dependencies with the new pa.c installer 3.1.1 2016-01-21 14:00:21 +01:00
Iván Novegil
e3e4fa42db Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2016-01-20 21:11:33 +01:00
Iván Novegil
6e0c6de0af Now the application indicates the user's version and if it's snapshot or not when reporting an issue trough the issue-reporter 2016-01-20 21:04:33 +01:00
Jose Manuel Delicado
564bee3850 Updated Windows dependencies. Updated readme with information about the new pywin32 and pycurl releases 2016-01-20 20:52:44 +01:00
Jose Manuel Delicado
79e723f740 Updated windows dependencies. Run git submodule update. 2016-01-20 20:28:54 +01:00
Jose Manuel Delicado
08a2eca98d Main.py: check if Uninstall.exe exists before importing commandline and other modules 2016-01-20 09:18:27 +01:00
Jose Manuel Delicado
c830d4b5b4 The -i and -p switches aren't required anymore on installed and portable versions 2016-01-19 19:52:09 +01:00
112391afeb Merge pull request #68 from masonasons/next-gen
Trimmed the sounds in the soundpack.
2016-01-11 13:41:06 -06:00
masonasons
39e0431b97 Trimmed the beginnings of the sounds in the Default soundpack 2016-01-09 01:01:20 -06:00
071d75926b Opus support for TWBlue. Code by @masonasons 2016-01-09 00:46:33 -06:00
56b64e5c6d Added source in view tweet dialogue. @masonasons 2016-01-09 00:45:52 -06:00
b8cfd60c9e bugfix 2016-01-09 00:25:28 -06:00
691a9ae17d Fixed remove_buffer and load_previous_items for friends and users timelines 2015-12-29 08:31:13 -06:00
214b9a8809 Fixed a bug with the restart_stream function 2015-12-28 09:05:09 -06:00
e85f54e15c Merge pull request #67 from Oliver2213/update-readme
Update the "upgrade all" command to include missing dependencies
2015-12-27 11:09:59 -06:00
blake oliver
d2245c33ce Update the "upgrade all" command to include missing dependencies 2015-12-27 11:43:25 -05:00
ff6695bba3 Added ANY in the language list for tweet searches 2015-12-27 00:03:15 -06:00
cdcdc86627 Changed go_page_up and go_page_down in the win10 default keymap 2015-12-26 23:39:31 -06:00
b4addf9329 Add update buffer option to the menu bar 2015-12-26 23:34:19 -06:00
9d0c9cfdb5 Don't get new items for people buffers 2015-12-26 23:28:46 -06:00
0afba81c71 Update buffers by pressing ctrl+win+shift+u; workaround for dm issues 2015-12-26 09:02:08 -06:00
0882e4707d Removed an unneeded import in translator module 2015-12-23 09:52:32 -06:00
78079e142f Changed handlers for sent direct messages 2015-12-23 09:51:09 -06:00
00a4203e1a Switched to the microsoft translator 2015-12-23 09:48:50 -06:00
c4fae3b70b Windows key is no longer required in keymap editor 2015-12-17 08:28:20 -06:00
b8dfa4a5e8 added audio playback to the keymap editor 2015-12-16 20:16:18 -06:00
1ca1862a08 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-12-16 16:40:30 -06:00
ba76c74324 Minor changes in session controller 2015-12-16 16:32:58 -06:00
Jose Manuel Delicado
dda37f0083 Updated windows dependencies. Run git submodule update before submiting new commits. Updated readme.md with information about the portableapps.com format. 2015-12-14 13:46:55 +01:00
65c353450e Improvements in the tweets' searched 2015-12-09 17:25:46 -06:00
2bfb53abe1 Added showing followers and friends timelines 2015-12-09 11:51:37 -06:00
ab08eada81 Fixed TWBlue in non-ascii paths 2015-12-09 10:05:17 -06:00
Jose Manuel Delicado
5fcc1de9a5 Updated windows dependencies, remember to run git submodule update after pulling the repository! Removed wheel information about pycurl in readme and updated download links 2015-11-30 17:38:58 +01:00
Jose Manuel Delicado
baeb0f7ae8 Updated pa.c format to version 0.80, added romanian language to the installer 2015-11-30 11:50:59 +01:00
333158a7be New stable version 2015-11-29 20:32:08 -06:00
f24c0955ce ADded libraries for the new stable version. 2015-11-28 10:24:16 -06:00
67412125df Removed pocket related things 2015-11-28 09:54:58 -06:00
f8e14d7040 Added new languages to the doc generator 2015-11-28 09:47:24 -06:00
4532cf867a Undated translations for documentation 2015-11-28 09:46:09 -06:00
79d2f70be9 Added romanian translation. Updated translations 2015-11-28 09:40:16 -06:00
b4eb11a6de Some list improvements 2015-11-27 09:17:14 -06:00
45f663a462 Updated documentation with translators list 2015-11-27 09:16:48 -06:00
5b8ce7757c Modified translators list 2015-11-27 08:24:28 -06:00
e47e0ba3f5 Added more detailed information about licenses 2015-11-27 08:05:41 -06:00
105debe4da Fixed duplicated streams. Removed unneeded code for connection checkers 2015-11-23 10:30:23 -06:00
a053788cce Added code for the future romanian translation 2015-11-23 09:35:21 -06:00
6d578d5203 Fixed a bug with twishort in the longtweets handler 2015-11-18 09:26:07 -06:00
21aacfc2af NEw version 10.96 2015-11-06 09:00:47 -06:00
93dc3ad646 New snapshot 2015-11-03 10:13:09 -06:00
8e67ed025c Changed favourites for likes 2015-11-03 10:07:06 -06:00
fa1b8bfde3 Added more keystrokes to the invisible interface 2015-11-03 09:02:19 -06:00
7dfe2cbb5c Lists should work 2015-11-03 05:56:57 -06:00
880c421f3e added check for updates to the invisible interface 2015-11-03 05:25:03 -06:00
e1b8d49af5 manage_stream_errors will only delete the stream objects 2015-11-03 04:42:38 -06:00
e1d14b8c27 Now conversation buffers are removed properly 2015-11-03 04:41:01 -06:00
594b0cd546 Translator is working properly again 2015-11-03 04:40:05 -06:00
fe9f724673 Updated accessible_output2. Add mac and a better linux support 2015-11-03 04:38:59 -06:00
6a4a3cc94e Cache com saved in data directory. Translation service restored 2015-10-31 20:57:21 -06:00
Jose Manuel Delicado
af41dcfc4e com.py: use config_path instead of data_path. Paths.py: some strings weren't appended as unicode 2015-10-24 23:01:23 +02:00
31611d8429 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-10-23 11:37:43 -05:00
7ed44c839e New snapshot 2015-10-23 11:36:36 -05:00
503c65692d FFix for creating favourites timelines 2015-10-23 11:33:56 -05:00
f76b86b24d A sound bugfix 2015-10-23 11:29:59 -05:00
1f96e71b63 workaround for duplicated tweets 2015-10-23 11:28:54 -05:00
Jose Manuel Delicado
cc249fe1a7 Added japanese, croatian and serbian (latin) languages to the installer 2015-10-19 12:47:52 +02:00
Jose Manuel Delicado
268bd35f3a Updated windows dependencies. Remember to run git submodule update before submiting new commits 2015-10-19 10:52:05 +02:00
Jose Manuel Delicado
0307c0abe3 Fixed com cache error 2015-10-18 22:18:43 +02:00
Jose Manuel Delicado
adf062f654 Now com cache is stored in the config folder, so program files folder keeps untouched on installed copies. Updated contributors in application.py, fixed spelling in some comments in main.py 2015-10-18 12:19:43 +02:00
70b5f25cf0 New snapshot 2015-10-15 08:55:54 -05:00
2b65b89afb Disabled translation service 2015-10-15 08:54:30 -05:00
44e8ed6456 Removed send errors in some functions 2015-10-14 20:38:42 -05:00
792655e299 Improved stream reconnection 2015-10-14 17:30:41 -05:00
0f56d8cdd4 Quoted tweets again 2015-10-14 17:07:57 -05:00
6c47dd2fa9 Some fixes for URL Player 2015-10-14 17:07:30 -05:00
Jose Manuel Delicado
49073bc151 Added new packages to readme.md. Now setup.py adds wx localization files to the binary version. 2015-10-04 18:15:09 +02:00
Jose Manuel Delicado
336acd9860 updated translations 2015-10-03 13:25:23 +02:00
4daeeb7beb Disable some things before snapshot 2015-10-03 05:12:22 -05:00
408ff50404 New snapshot 2015-10-03 04:55:02 -05:00
148c5176c6 Reverted quoted tweets support 2015-10-03 04:34:43 -05:00
bdb9de863f A fw bugfixes 2015-10-02 15:52:08 -05:00
ce1f8b2cc3 New snapshot 2015-09-29 09:39:47 -05:00
5933323beb Updated translation templates 2015-09-29 09:38:39 -05:00
22b1b0a149 Added posibility for search a term in trending topics 2015-09-29 09:37:04 -05:00
e71afeb10f View other user's lists is implemented 2015-09-29 09:22:19 -05:00
98f026156d Fixed search in application menu 2015-09-29 08:42:04 -05:00
cbee57aa30 Quoted tweets support in buffers 2015-09-29 08:38:05 -05:00
fd9e4dc05d New snapshot 2015-09-23 10:01:43 -05:00
6022cecad1 Added japanese, croatian and serbian 2015-09-23 10:01:22 -05:00
89e39e2168 Double tweets should be ignored in streams 2015-09-23 10:01:00 -05:00
a69bf99c1a If there is no locale for language, use English in arrow 2015-09-23 10:00:14 -05:00
d34ef81324 Updated locales 2015-09-23 09:59:34 -05:00
a3c050195a Twishort retweets are displayed as espected 2015-09-17 09:00:30 -05:00
3623eafacd handle_longtweets is enabled by default 2015-09-17 08:59:59 -05:00
Sukil Etxenike
2bd3f0a1d1 Updated docs with contributors 2015-09-06 23:09:14 +02:00
f1f828522e Translation templates updated 2015-09-05 11:46:34 -05:00
51e4898346 Add shorcuts to the audio upload dialogue 2015-09-05 11:42:47 -05:00
c4478198b6 NEw snapshot 2015-08-31 11:18:10 -05:00
f468924b85 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-08-31 11:03:54 -05:00
158b48e4d5 Add and remove user to list are implemented in GUI 2015-08-31 10:08:34 -05:00
756a58e443 New timeline sound is played again 2015-08-31 08:42:28 -05:00
85ce5b0791 Trending topics now have their names at startup 2015-08-31 08:15:37 -05:00
Jose Manuel Delicado
e534d1cd20 Updated readme. 2015-08-28 11:47:54 +02:00
2d35304ef0 New snapshot 2015-08-27 08:13:01 -05:00
4f0e6d758b Restores buffer positions at startup using cached database 2015-08-25 13:37:31 -05:00
bd4aa89c2b Added text_focus() in some actions to the tweet dialogue 2015-08-24 10:00:34 -05:00
b046360293 Added right click menu in buffers 2015-08-24 09:54:15 -05:00
6971fb3999 Bugfix in retweet with comment 2015-08-21 09:55:05 -05:00
7b840f29c4 Keystroke editor can execute the actions directly 2015-08-21 09:51:01 -05:00
71fed7300b IF there are no tweets or favs, the timeline is not created 2015-08-21 09:12:54 -05:00
57315c3b6e open_timeline() cleaned 2015-08-21 08:57:15 -05:00
3d3abc90e1 Expanded URL are displayed in the view tweet dialogue 2015-08-20 17:57:06 -05:00
de7882e4cf Logout session improvements 2015-08-20 17:32:27 -05:00
jmdaweb
10190d61c0 Fixed windows dependencies submodule. Please, don't forget to run git submodule update before sending a commit or pull request. 2015-08-19 13:38:10 +02:00
bb6fa7cb46 logout() now removes database for the session 2015-08-19 05:28:56 -05:00
80cb70c9a6 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-08-18 08:05:21 -05:00
77d51aa51a New snapshot 2015-08-18 08:04:58 -05:00
jmdaweb
ebb4e22d02 Updated Windows dependencies. Now wx buttons and messages should be localized. 2015-08-16 15:42:17 +02:00
4344a0df0c Get full text for dm buffers 2015-08-12 17:21:00 -05:00
34ad0c5e47 Reconnecting streams every 5 mins 2015-08-12 16:43:23 -05:00
704ade560a Increasing character limit for direct messages 2015-08-12 15:57:29 -05:00
a4892cf847 TWBlue does not change the current session in invisible interface 2015-08-11 08:58:11 -05:00
e59661775a Updated translations 2015-08-10 09:49:16 -05:00
53a9f04155 New snapshot 2015-08-10 09:28:47 -05:00
100f483a86 List with special characters are handled properly 2015-08-10 09:26:07 -05:00
bad468560a Saving unicode keys as string objects in cache database 2015-08-10 09:24:46 -05:00
eb0cf62a54 A better way to save keymaps 2015-08-10 09:21:51 -05:00
a016932ac7 Timelines fix 2015-08-08 09:42:55 -05:00
1706414171 Timelines order improvements 2015-08-07 16:44:33 -05:00
5f963aa66b Modified windows 10 keymap 2015-08-07 14:05:22 -05:00
25578e4fc6 Fixed #59 2015-08-07 09:05:59 -05:00
faec92ce6c Stream connection improvements 2015-08-06 13:54:26 -05:00
313e725ff2 Autocompletion now works in the actions dialogue 2015-08-06 13:16:55 -05:00
3f82a51ef9 Added a new contributor 2015-08-06 12:07:30 -05:00
d26ab9b910 Fixed bug when opening multiple timelines 2015-08-06 12:03:40 -05:00
aa42e056c0 Updated translation templates 2015-08-04 13:40:16 -05:00
fa6592ed1a Fixed a string 2015-08-04 13:39:57 -05:00
11b06accbb Merge pull request #61 from codeofdusk/stringcleanup
String Cleanup
2015-08-04 13:27:50 -05:00
Sukil Etxenike
915eb5802f Fixed Basque locale 2015-08-02 00:04:04 +02:00
Bill Dengler
4e943c7bee String cleanup. 2015-07-30 14:15:26 -04:00
Bill Dengler
692d75562e String cleanup. 2015-07-30 14:03:52 -04:00
4cce6d4318 Bugfixes 2015-07-30 11:19:20 -05:00
e55e15466d Fixed some bugs 2015-07-30 09:06:56 -05:00
370340de0b Twishort preferences are saved 2015-07-29 14:47:14 -05:00
10bc483887 Added a donation dialogue in some parts of the program 2015-07-29 14:05:26 -05:00
f66b2ccbad Added repeat item option to invisible interface 2015-07-29 13:45:57 -05:00
6460c20a97 Added copy to clipboard option 2015-07-29 08:40:07 -05:00
9f2c240fc1 Removed saving position in database for now 2015-07-17 17:23:30 -05:00
a4cbbfcb9f TWBlue does not break a word in multiline text 2015-07-13 17:39:02 -05:00
239 changed files with 55517 additions and 30998 deletions

4
.gitignore vendored
View File

@@ -16,4 +16,6 @@ src/Microsoft.VC90.CRT
src/Microsoft.VC90.MFC
src/launcher.bat
src/sounds/iOs
release-snapshot/
release-snapshot/
src/com_cache/
doc/strings.py

6
COPYING Normal file
View File

@@ -0,0 +1,6 @@
Tw Blue is free software, licensed under the GNU GPL license, either version 2 or, at your option, any later version. You can view the license in the file named license.txt, or online at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
Copyright © 2013-2015. Manuel Cortéz
Documentation is written by Sukil Etxenike, Brian Hartgen and Bill Dengler, covered by the Creative Commons Attribution-NonCommercial-ShareAlike 2.5 license. You can read the license at https://creativecommons.org/licenses/by-nc-sa/2.5/
The accessible_output2, audio_services, keyboard_handler, libloader, platform_utils and sound_lib modules are written by Christofer Toth and modified by the TWBlue Team, released under a MIT license. You can get the original modules at http://hg.q-continuum.net.

View File

@@ -1,13 +1,12 @@
TWBlue -
TWBlue -
======
Copyright (C) 2015. [Technow S.L.](https://www.technow.es)
Copyright (C) 2016. [Technow S.L.](https://www.technow.es)
TW Blue is an app designed to use Twitter simply and efficiently while using minimal system resources.
With this app youll have access to twitter features such as:
* Create, reply to, retweet and delete tweets,
* Add and remove tweets from favourites,
* Create, reply to, like, retweet and delete tweets,
* Send and delete direct messages,
* See your friends and followers,
* Follow, unfollow, block and report users as spam,
@@ -18,9 +17,9 @@ With this app youll have access to twitter features such as:
See [TWBlue's webpage](http://twblue.es) for more details.
## Using TWBlue from sources
## Running TWBlue from source
This document describes how to run tw blue from source, and, after that, how to build a binary version, which doesn't need Python and the other dependencies to run.
This document describes how to run tw blue from source and how to build a binary version which doesn't need Python and the other dependencies to run.
### Required dependencies.
@@ -32,14 +31,12 @@ Although most dependencies can be found in the windows-dependencies directory, w
#### Dependencies packaged in windows installers
* [Python,](http://python.org) version 2.7.10
* [Python,](http://python.org) version 2.7.11
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.
* [wxPython](http://www.wxpython.org) for Python 2.7, version 3.0.2.0
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 219
* [Pycurl](http://pycurl.sourceforge.net) 7.19.5.1 for Python 2.7: [32-bit downloads,](https://pypi.python.org/pypi/pycurl/7.19.5.1) [64-bit downloads](http://www.lfd.uci.edu/~gohlke/pythonlibs/)
Note: the x64 version is in wheel format instead of executable installer, so you have to install it using pip. For example: C:\python27x64\scripts\pip install pycurl-7.19.5.1-cp27-none-win_amd64.whl
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 220
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
x64 version has been built by TW Blue developers, so you only will find it in windows-dependencies folder
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
@@ -49,7 +46,7 @@ To build a binary version:
#### Dependencies that must be installed using easy_install
setuptools install a script, called easy_install. You can find it in the python scripts directory. To install packages using easy_install, you have to navigate to the scripts directory using a command prompt, for example:
setuptools installs a script, called easy_install. You can find it in the python scripts directory. To install packages using easy_install, you have to navigate to the scripts directory using a command prompt, for example:
cd C:\python27x64\scripts
@@ -65,14 +62,18 @@ setuptools install a script, called easy_install. You can find it in the python
* pypubsub
* configobj
* requests-oauthlib
* requests-toolbelt
* future
* pygeocoder
* suds
* arrow
* goslate
* arrow==0.6
* markdown
* winpaths
* microsofttranslator
easy_install will automatically get the additional libraries that these packages need to work properly.
Run the following command to quickly install and upgrade all packages and their dependencies:
easy_install -Z --upgrade six configobj goslate markdown future suds requests oauthlib requests-oauthlib requests-toolbelt pypubsub pygeocoder arrow==0.6 python-dateutil futures microsofttranslator winpaths
#### Other dependencies
@@ -87,6 +88,14 @@ This dependency has been built using pure basic 4.61. Its source can be found at
* [NSIS unicode,](http://www.scratchpaper.com/) version 2.46.5
#### Dependencies required to build the portableApps.com format archive
* [NSIS Unicode Portable,](http://portableapps.com/apps/development/nsis_portable) version 2.46.5 rev 3
* [PortableApps.com Launcher,](http://portableapps.com/apps/development/portableapps.com_launcher) version 2.2
* [PortableApps.com Installer,](http://portableapps.com/apps/development/portableapps.com_installer) version 3.4.2
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, ...
### Running TW Blue from source
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:
@@ -95,9 +104,17 @@ Now that you have installed all these packages, you can run TW Blue from source
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
### Generating the documentation
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
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 necesary.
Also, copy the license.txt located in the root of the repo to the documentation folder.
### Building a binary version
A binary version doesn't need python and the other dependencies to run, it's the same version that you will find on the TW Blue website if you download the zip files.
A binary version doesn't need python and the other dependencies to run, it's the same version that you will find on the TW Blue website if you download the zip files or the snapshot versions.
To build it, run the following command from the src folder:
@@ -105,6 +122,29 @@ To build it, run the following command from the src folder:
You will find the binaries in the dist directory.
### Building an installer
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
* 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
* 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
### How to generate a translation template
Run the gen_pot.bat file, located in the tools directory. Your python installation must be in your path environment variable. The pot file will appear in the tools directory.
### How to build the portableApps.com archive
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
* 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
* 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

@@ -33,4 +33,13 @@ Salvadora Melguizo
Holly Scott-Gardner
Anibal Hernández
Sussan Leiva
Brian Hartgen
Brian Hartgen
PEDRO REINA COLOBON
Moora-Moora Arrilla
Blake Oliver
Steffen Schultz
Riku
Burak Yüksek
florian Ionașcu
Christian Leo Mameli
Natalia Hedlund (Наталья Хедлунд)

View File

@@ -2,6 +2,17 @@
name = 'TWBlue'
snapshot = False
if snapshot == False:
version = "0.80"
version = "0.86"
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 = "7"
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) 2015, Technow S.L. \nCopyright (C) 2013-2015, 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

@@ -10,14 +10,19 @@ def prepare_documentation_in_file(fileSource, fileDest):
f2 = open(fileDest, "w")
lns = f1.readlines()
f2.write("# -*- coding: utf-8 -*-\n")
f2.write("documentation = []\n")
f2.write("documentation = [\n")
for i in lns:
if len(i) > 2:
newvar = "documentation.append(_(u\"\"\"%s\"\"\"))\n" % (i[:-2])
if "\n" == i:
newvar = "\"\","
elif "\n" == i[-1]:
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i[:-1])
else:
newvar = "documentation.append(_(u\"\"\"%s\"\"\"))\n" % (i)
newvar = "_(u\"\"\"%s\"\"\"),\n" % (i)
# print i[-1:]
f2.write(newvar)
f1.close()
f2.write("]")
f2.close()
prepare_documentation_in_file("manual.md", "strings.py")

View File

@@ -7,7 +7,7 @@ languageHandler.setLanguage("en")
import strings
# the list of supported language codes of TW Blue
languages = ["en", "es", "fr", "de", "it"]
languages = ["en", "es", "fr", "de", "it", "gl", "ja", "ru", "ro"]
#"eu", "ar", "ca", "es", "fi", "fr", "gl", "hu", "it", "pl", "pt", "ru", "tr"]
def generate_document(language):

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

Binary file not shown.

File diff suppressed because it is too large Load Diff

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

344
doc/manual.md Normal file
View File

@@ -0,0 +1,344 @@
Documentation for TWBlue - 0.84
## Table of contents
[TOC]
## Warning!
You are reading documentation produced for a program still in development. The object of this manual is to explain some details of the operation of the program. Bear in mind that as the software is in the process of active development, parts of this user guide may change in the near future, so it is advisable to keep checking from time to time to avoid missing important information.
If you want to see what has changed from the previous version, [read the list of updates here.](changes.html)
## Introduction
TWBlue is an application to make Twitter simple and fast, while using as few resources as possible. With TWBlue, you can do things like the following:
* Tweet, reply to, retweet and delete tweets,
* Mark and unmark a tweet as favourite,
* Send and delete direct messages,
* See your friends and followers,
* Follow, unfollow, report and block a user,
* Open a user's timeline to see their tweets separately,
* Open URLs from a tweet or direct message,
* Play several types of audio files from addresses,
* And more.
## Usage
Twitter is a social networking or micro-blogging tool which allows you to compose short status updates of your activities in 140 characters or less. Twitter is a way for friends, family and co-workers to communicate and stay connected through the exchange of quick, frequent messages. You can restrict delivery of updates to those in your circle of friends or, by default, allow anyone to access them.
You can monitor the status of updates from your friends, family or co-workers (known as following), and they in turn can read any updates you create, (known as followers). The updates are referred to as Tweets. The Tweets are posted to your Twitter profile or Blog and are searchable using Twitter Search.
In order to use TWBlue, you must first have created an account on the Twitter website. The process for signing up for a Twitter account is very accessible. During the account registration, you will need to choose a Twitter username. This serves two purposes. This is the method through which people will comunicate with you, but most importantly, your username and password will be required to connect TWBlue to your Twitter account. We suggest you choose a username which is memorable both to you and the people you hope will follow you.
We'll start from the premise that you have a Twitter account with its corresponding username and password.
### Authorising the application
First of all, it's necessary to authorise the program so it can access your Twitter account and act on your behalf. The authorisation process is quite simple, and the program never retains data such as your password. In order to authorise the application, you just need to run the main executable file, called TWBlue.exe (on some computers it may appear simply as TWBlue if Windows Explorer is not set to display file extensions). We suggest you may like to place a Windows shortcut on your Desktop pointing to this executable file for quick and easy location.
You can log into several Twitter accounts simultaneously. The program refers to each Twitter account you have configured as a "Session". If this is the first time you have launched TWBlue, and if no Twitter session exists, you will see the Session Manager. This dialogue box allows you to authorise as many accounts as you wish. If you press the Tab key to reach the "new account" button and activate it by pressing the Space Bar, a dialogue box will advise you that your default internet browser will be opened in order to authorise the application and you will be asked if you would like to continue. Activate the "yes" Button by pressing the letter "Y" so the process may start.
Your default browser will open on the Twitter page to request authorisation. Enter your username and password into the appropriate edit fields if you're not already logged in, select the authorise button, and press it.
Once you've authorised your twitter account, the website will redirect you to a page which will notify you that TWBlue has been authorised successfully. Now you are able to close the page by pressing ALT+F4 which will return you to the Session Manager. On the session list, you will see a new item temporarily called "Authorised account x" -where x is a number. The session name will change once you open that session.
To start running TWBlue, press the Ok button in the Session Manager dialogue. By default, the program starts all the configured sessions automatically, however, you can change this behavior.
If all went well, the application will start playing sounds, indicating your data is being updated.
When the process is finished, by default the program will play another sound, and the screen reader will say "ready" (this behaviour can be configured).
## General concepts
Before starting to describe TWBlue's usage, we'll explain some concepts that will be used extensively throughout this manual.
### Buffer
A buffer is a list of items to manage the data which arrives from Twitter, after being processed by the application. When you configure a new session on TWBlue and start it, many buffers are created. Each of them may contain some of the items which this program works with: Tweets, direct messages, users, trends or events. According to the buffer you are focusing, you will be able to do different actions with these items.
The following is a description for every one of TWBlue's buffers and the kind of items they work with.
* Home: this shows all the tweets on the main timeline. These are the tweets by users you follow.
* Mentions: if a user, whether you follow them or not, mentions you on Twitter, you will find it in this list.
* Direct messages: here you will find the private direct messages you exchange with users who follow you , or with any user, if you allow direct messages from everyone (this setting is configurable from Twitter). This list only shows received messages.
* Sent direct messages: this buffer shows all the direct messages sent from your account.
* Sent tweets: this shows all the tweets sent from your account.
* Favourites: here you will see all the tweets you have favourited.
* Followers: when users follow you, you'll be able to see them on this buffer, with some of their account details.
* Friends: the same as the previous buffer, but these are the users you follow.
* User timelines: these are buffers you may create. They contain only the tweets by a specific user. They're used so you can see the tweets by a single person and you don't want to look all over your timeline. You may create as many as you like.
* Events: An event is anything that happens on Twitter, such as when someone follows you, when someone adds or removes one of your tweets from their favourites list, or when you subscribe to a list. There are many more, but the program shows the most common ones in the events buffer so that you can easily keep track of what is happening on your account.
* Lists: A list is similar to a user timeline, except that you can configure it to contain tweets from multiple users.
* Search: A search buffer contains the results of a search operation.
* User favourites: You can have the program create a buffer containing tweets favourited by a particular user.
* Trending Topics: a trend buffer shows the top ten most used terms in a geographical region. This region may be a country or a city. Trends are updated every five minutes.
If a tweet contains a URL, you can press enter in the GUI or Control + Windows + Enter in the invisible interface to open it. If it contains audio, you can press Control + Enter or Control + Windows + Alt + Enter to play it, respectively. TWBlue will play a sound if the tweet contains the \#audio hashtag, but there may be tweets which contain audio without this. Finally, if a tweet contains geographical information, you can press Control + Windows + G in the invisible interface to retrieve it.
### Username fields
These fields accept a Twitter username (without the at sign) as the input. They are present in the send direct message and the user actions dialogue boxes. Those dialogues will be discussed later. The initial value of these fields depends on where they were opened from. They are prepopulated with the username of the sender of the focused tweet (if they were opened from the home and sent timelines, from users' timelines or from lists), the sender of the focused direct message (if from the received or sent direct message buffers) or in the focused user (if from the followers' or friends' buffer). If one of those dialogue boxes is opened from a tweet, and if there are more users mentioned in it, you can use the arrow keys to switch between them. Alternatively, you can also type a username.
## The program's interfaces
### The graphical user interface (GUI)
The graphical user interface of TWBlue consists of a window containing:
* a menu bar accomodating five menus (application, tweet, user, buffer and help);
* One tree view,
* One list of items
* Four buttons in most dialogs: Tweet, retweet , reply and direct message.
The actions that are available for every item will be described later.
In summary, the GUI contains two core components. These are the controls you will find while pressing the Tab key within the program's interface, and the different elements present on the menu bar.
#### Buttons in the application
* Tweet: this button opens up a dialogue box to write your tweet. The message must not exceed 140 characters. If you write past this limit, a sound will play to warn you. Note that the character count is displayed in the title bar. You may use the shorten and expand URL buttons to comply with the character limit. You can upload a picture, check spelling, attach audio or translate your message by selecting one of the available buttons in the dialogue box. In addition, you can autocomplete the entering of users by pressing Alt + A or the button for that purpose if you have the database of users configured. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will speak an error message in English describing the problem.
* Retweet: this button retweets the message you're reading. After you press it, if you haven't configured the application not to do so, you'll be asked if you want to add a comment or simply send it as written. If you choose to add a comment, and if the original tweet plus the comment exceeds 140 characters, you will be asked if you want to post it as a comment with a mention to the original user and a link to the originating tweet.
* Reply: when you're viewing a tweet, you can reply to the user who sent it by pressing this button. A dialogue will open up similar to the one for tweeting, but with the name of the user already filled in (for example @user) so you only need to write your message. If there are more users referred to in the tweet, you can press shift-tab and activate the mention all users button. When you're on the friends or followers lists, the button will be called mention instead.
* Direct message: exactly like sending a tweet, but it's a private message which can only be read by the user you send it to. Press shift-tab to see the recipient. If there were other users mentioned in the tweet you were reading, you can arrow up or down to choose which one to send it to, or write the username yourself without the at sign.
Bear in mind that buttons will appear according to which actions are possible on the list you are browsing. For example, on the home timeline, mentions, sent, favourites and user timelines you will see the four buttons, while on the direct messages list you'll only get the direct message and tweet buttons, and on friends and followers lists the direct message, tweet, and mention buttons will be available.
#### Menus
Visually, Towards the top of the main application window, can be found a menu bar which contains many of the same functions as listed in the previous section, together with some additional items. To access the menu bar, press the alt key. You will find five menus listed: application, tweet, user, buffer and help. This section describes the items on each one of them.
##### Application menu
* Manage accounts: Opens a window with all the sessions configured in TWBlue, where you can add new sessions or delete the ones you've already created.
* Update profile: opens a dialogue where you can update your information on Twitter: name, location, website and bio. If you have already set this up the fields will be prefilled with the existing information. Also, you can upload a photo to your profile.
* Hide window: turns off the Graphical User Interface. Read the section on the invisible interface for further details.
* Search: shows a dialogue box where you can search for tweets or users on Twitter.
* Lists Manager: This dialogue box allows you to manage your Twitter lists. In order to use them, you must first create them. Here, you can view, edit, create, delete or, optionally, open them in buffers similar to user timelines.
* Edit keystrokes: this opens a dialogue where you can see and edit the shortcuts used in the invisible interface.
* Account settings: Opens a dialogue box which lets you customize settings for the current account.
* Global settings: Opens a dialogue which lets you configure settings for the entire application.
* Exit: asks whether you want to exit the program. If the answer is yes, it closes the application. If you do not want to be asked for confirmation before exiting, uncheck the checkbox from the global settings dialogue box.
##### Tweet menu
* You will first find the items to tweet, reply and retweet, which are equivalent to the buttons with the same name.
* Add to favourites: marks the tweet you're viewing as a favourite.
* Remove from favourites: removes the tweet from your favourites, but not from Twitter.
* Show tweet: opens up a dialogue box where you can read the tweet, direct message, friend or follower which has focus. You can read the text with the arrow keys. It's a similar dialog box as used for composing tweets, without the ability to send the tweet, file attachment and autocompleting capabilities. It does however include a retweets and favourites count. If you are in the followers or the friends list, it will only contain a read-only edit box with the information in the focused item and a close button.
* View address: If the selected tweet has geographical information, TWBlue may display a dialogue box where you can read the tweet address. This address is retrieved by sending the geographical coordinates of the tweet to Google maps.
* View conversation: If you are focusing a tweet with a mention, it opens a buffer where you can view the whole conversation.
* Delete: permanently removes the tweet or direct message which has focus from Twitter and from your lists. Bear in mind that Twitter only allows you to delete tweets you have posted yourself.
##### User menu
* Actions: Opens a dialogue where you can interact with a user. This dialogue box will be populated with the user who sent the tweet or direct message in focus or the selected user in the friends or followers buffer. You can edit it or leave it as is and choose one of the following actions:
* Follow: Follows a user. This means you'll see his/her tweets on your home timeline, and if he/she also follows you, you'll be able to exchange direct messages. You may also send / receive direct messages from each other if you have configured the option to allow direct messages from anyone.
* Unfollow: Stops following a user, which causes you not being able to see his/her tweets on your main timeline neither exchanging direct messages, unless they have enabled receiving direct messages from anyone.
* Mute: While muting someone, TWBlue won't show you nor his/her tweets on your main timeline; neither will you see that person's mentions. But you both will be able to exchange direct messages. The muted user is not informed of this action.
* Unmute: this option allows TWBlue to display the user's tweets and mentions again.
* Block: Blocks a user. This forces the user to unfollow you .
* Unblock: Stops blocking a user.
* Report as spam: this option sends a message to Twitter suggesting the user is performing prohibited practices on the social network.
* Ignore tweets from this client: Adds the client from which the focused tweet was sent to the ignored clients list.
* View timeline: Lets you open a user's timeline by choosing the user in a dialog box. It is created when you press enter. If you invoke this option relative to a user that has no tweets, the operation will fail. If you try creating an existing timeline the program will warn you and will not create it again.
* Direct message: same action as the button.
* Add to List: In order to see someone's tweets in one or more of your lists, you must add them first. In the dialogue box that opens after selecting the user, you will be asked to select the list you wish to add the user to. Thereafter, the list will contain a new member and their tweets will be displayed there.
* Remove from list: lets you remove a user from a list.
* View lists: Shows the lists created by a specified user.
* Show user profile: opens a dialogue with the profile of the specified user.
* View favourites: Opens a buffer where you can see the tweets which have been favourited by a particular user.
##### Buffer menu
* New trending topics buffer: This opens a buffer to get the worlwide trending topics or those of a country or a city. You'll be able to select from a dialogue box if you wish to retrieve countries' trends, cities' trends or worldwide trends (this option is in the cities' list) and choose one from the selected list. The trending topics buffer will be created once the "OK" button has been activated within the dialogue box. Remember this kind of buffer will be updated every five minutes.
* Load previous items: This allows more items to be loaded for the specified buffer.
* Mute: Mutes notifications of a particular buffer so you will not hear when new tweets arrive.
* autoread: When enabled, the screen reader or SAPI 5 Text to Speech voice (if enabled) will read the text of incoming tweets. Please note that this could get rather chatty if there are a lot of incoming tweets.
* Clear buffer: Deletes all items from the buffer.
* Destroy: dismisses the list you're on.
##### Help menu
* Documentation: opens up this file, where you can read some useful program concepts.
* Sounds tutorial: Opens a dialog box where you can familiarize yourself with the different sounds of the program.
* What's new in this version?: opens up a document with the list of changes from the current version to the earliest.
* Check for updates: every time you open the program it automatically checks for new versions. If an update is available, it will ask you if you want to download the update. If you accept, the updating process will commence. When complete, TWBlue will be restarted. This item checks for new updates without having to restart the application.
* Report an error: opens up a dialogue box to report a bug by completing a small number of fields. Pressing enter will send the report. If the operation doesn't succeed the program will display a warning.
* TWBlue's website: visit our [home page](http://twblue.es) where you can find all relevant information and downloads for TWBlue and become a part of the community.
* About TWBlue: shows the credits of the program.
### The invisible user interface
The invisible interface, as its name suggests, has no graphical window and works directly with screen readers such as JAWS for Windows, NVDA and System Access. This interface is disabled by default, but you can enable it by pressing Control + M. It works similarly to TheQube and Chicken Nugget. Its shortcuts are similar to those found in these two clients. In addition, the program has builtin support for the keymaps for these applications, configurable through the global settings dialogue. By default, you cannot use this interface's shortcuts in the GUI, but you can configure this in the global settings dialogue.
The next section contains a list of keyboard shortcuts for both interfaces. Bear in mind that we will only describe the default keymap.
## Keyboard shortcuts
### Shortcuts of the graphical user interface (GUI)
* Enter: Open URL.
* Control + Enter: Play audio.
* Control + M: Hide the GUI.
* Control + N: Compose a new tweet.
* Control + R: Reply / mention.
* Control + Shift + R: Retweet.
* Control + D: Send a direct message.
* control + F: Add tweet to favourites.
* Control + Shift + F: Remove a tweet from favourites.
* Control + S: Open the user actions dialogue.
* Control + Shift + V: Show tweet.
* Control + Q: Quit this program.
* Control + I: Open user timeline.
* Control + Shift + i: Destroy buffer.
* F5: Increase volume by 5%.
* F6: Decrease volume by 5%.
* Control + P: Edit your profile.
* Control + Delete: Delete a tweet or direct message.
* Control + Shift + Delete: Empty the current buffer.
### Shortcuts of the invisible interface (default keymap)
* Control + Windows + Up Arrow: moves to the previous item in the buffer.
* Control + Windows + Down Arrow: moves to the next item in the buffer.
* Control + Windows + Left Arrow: Move to the previous buffer.
* Control + Windows + Right Arrow: Move to the next buffer.
* Control + Windows + Shift + Left: Focus the previous session.
* Control + Windows + Shift + Right: Focus the next session.
* Control + Windows + C: View conversation.
* Control + Windows + Enter: Open URL.
* Control + Windows + ALT + Enter: Play audio.
* Control + Windows + M: Show or hide the GUI.
* Control + Windows + N: New tweet.
* Control + Windows + R: Reply / Mention.
* Control + Windows + Shift + R: Retweet.
* Control + Windows + D: Send direct message.
* Windows+ Alt + F: Like a tweet.
* Alt + Windows + Shift + F: Remove from likes.
* Control + Windows + S: Open the user actions dialogue.
* Control + Windows + Alt + N: See user details.
* Control + Windows + V: Show tweet.
* Control + Windows + F4: Quit TWBlue.
* Control + Windows + I: Open user timeline.
* Control + Windows + Shift + I: Destroy buffer.
* Control + Windows + Alt + Up: Increase volume by 5%.
* Control + Windows + Alt + Down: Decrease volume by 5%.
* Control + Windows + Home: Jump to the first element of the current buffer.
* Control + Windows + End: Jump to the last element of the current buffer.
* Control + Windows + PageUp: Jump 20 elements up in the current buffer.
* Control + Windows + PageDown: Jump 20 elements down in the current buffer.
* Windows + Alt + P: Edit profile.
* Control + Windows + Delete: Delete a tweet or direct message.
* Control + Windows + Shift + Delete: Empty the current buffer.
* Control + Windows + Space: Repeat last item.
* Control + Windows + Shift + C: Copy to clipboard.
* Control + Windows+ A: Add user to list.
* Control + Windows + Shift + A: Remove user from list.
* Control + Windows + Shift + M: Mute / unmute the current buffer.
* Windows + Alt + M: Mute / unmute the current session.
* Control + Windows + E: Toggle the automatic reading of incoming tweets in the current buffer.
* Control + Windows + -: Search on Twitter.
* Control + Windows + K: Show the keystroke editor.
* Control + Windows + L: Show lists for a specified user.
* Windows + Alt + PageUp: Load previous items for the current buffer.
* Control + Windows + G: Get geolocation.
* Control + Windows + Shift + G: Display the tweet's geolocation in a dialogue.
* Control + Windows + T: Create a trending topics' buffer.
* Control + Windows + {: Find a string in the current buffer.
## Configuration
As described above, this application has two configuration dialogues, the global settings dialogue and the account settings dialogue.
### The account settings dialogue
#### General tab
* Autocompletion settings: Allows you to configure the autocompletion database. You can add users manually or let TWBlue add your followers, friends or both.
* Relative timestamps: Allows you to configure whether the application will calculate the time the tweet or direct message was sent or received based on the current time, or simply say the time it was received or sent.
* API calls: Allows you to adjust the number of API calls to be made to Twitter by this program.
* Items on each API call: Allows you to specify how many items should be retrieved from Twitter for each API call (default and maximum is 200).
* Inverted buffers: Allows you to specify whether the buffers should be inverted, which means that the oldest items will show at the end of them and the newest at the beginning.
* Retweet mode: Allows you to specify the behaviour when posting a retweet: you can choose between retweeting with a comment, retweeting without comment or being asked.
* Number of items per buffer to cache in database: This allows you to specify how many items TWBlue should cache in a database. You can type any number, 0 to cache all items, or leave blank to disable caching entirely.
#### buffers tab
This tab displays a list for each buffer you have available in TWBlue, except for searches, timelines, favourites' timelines and lists. You can show, hide and move them.
#### The ignored clients tab
In this tab, you can add and remove clients to be ignored by the program.
#### Sound tab
In this tab, you can adjust the sound volume, select the input and output device and set the soundpack used by the program.
#### Audio service tab
In this tab, you can enter your SndUp API key (if you have one) to upload audio to SndUp with your account. Note that if account credentials are not specified you will upload anonimously.
### Global settings
This dialogue allows you to configure some settings which will affect the entire application.
#### General tab {#general-tab_1}
* Language: This allows you to change the language of this program. Currently supported languages are arabic, Catalan, German, English, Spanish, Basque, Finnish, French, Galician, Croatian, Hungarian, Italian, Polish, Portuguese, Russian and Turkish.
* Ask before exiting TWBlue: This checkbox allows you to control whetherthe program will ask for confirmation before exiting.
* Play a sound when TWBlue launches: This checkbox allows you to configure whether the application will play a sound when it has finished loading the buffers.
* Speak a message when TWBlue launches: This is the same as the previous option, but this checkbox configures whether the screen reader will say "ready".
* Use the invisible interface's shortcuts in the GUI: As the invisible interface and the Graphical User Interface have their own shortcuts, you may want to use the invisible interface's keystrokes all the time. If this option is checked, the invisible interface's shortcuts will be usable in the GUI.
* Activate SAPI5 when any other screen reader is not being run: This checkbox allows to activate SAPI 5 TTS when no other screen reader is being run.
* Hide GUI on launch: This allows you to configure whether TWBlue will start with the GUI or the invisible interface.
* Keymap: This option allows you to change the keymap used by the program in the invisible interface. The shipped keymaps are Default, Qwitter, Windows 10 and Chicken Nugget. The keymaps are in the "keymaps" folder, and you can create new ones. Just create a new ".keymap" file and change the keystrokes associated with the actions, as it is done in the shipped keymaps.
#### Proxi tab
In this tab you can configure TWBlue to use a Proxy server by completing the fields displayed (server, port, user and password).
## License, source code and donations
Tw Blue is free software, licensed under the GNU GPL license, either version 2 or, at your option, any later version. You can view the license in the file named license.txt, or online at <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>.
The source code of the program is available on GitHub at <https://www.github.com/manuelcortez/twblue>.
If you want to donate to the project, you can do so at <http://twblue.es/?q=node/3&language=en>. Thank you for your support!
## Contact
If you still have questions after reading this document, if you wish to collaborate to the project in some other way, or if you simply want to get in touch with the application developer, follow the Twitter account [@tw\_blue2](https://twitter.com/tw_blue2) or [@manuelcortez00.](https://twitter.com/manuelcortez00) You can also visit [our website](http://twblue.es)
## Credits
TWBlue is developed and mantained by [Manuel Cortéz](https://twitter.com/manuelcortez00) and [José Manuel Delicado](https://twitter.com/jmdaweb). It's supported and sponsored by [Technow S. L.](https://twitter.com/technow)
We would also like to thank the translators of TWBlue, who have allowed the spreading of the application.
* English: [Manuel Cortéz](https://twitter.com/manuelcortez00).
* Arabic: [Mohammed Al Shara](https://twitter.com/mohammed0204).
* Catalan: [Francisco Torres](https://twitter.com/ftgalleg)
* Spanish: [Manuel Cortéz](https://twitter.com/manuelcortez00).
* Basque: [Sukil Etxenike](https://twitter.com/sukil2011).
* Finnish: [Jani Kinnunen](https://twitter.com/jani_kinnunen).
* French: [Rémi Ruiz](https://twitter.com/blindhelp38).
* Galician: [Juan Buño](https://twitter.com/Quetzatl_).
* German: [Steffen Schultz](https://twitter.com/schulle4u).
* Croatian: [Zvonimir Stanečić](https://twitter.com/zvonimirek222).
* Hungarian: Robert Osztolykan.
* Italian: [Christian Leo Mameli](https://twitter.com/llajta2012).
* Japanese: [Riku](https://twitter.com/riku_sub001)
* Polish: [Pawel Masarczyk.](https://twitter.com/Piciok)
* Portuguese: Odenilton Júnior Santos.
* Romanian: [Florian Ionașcu](https://twitter.com/7ro) and [Răzvan Ciule](https://twitter.com/pilgrim89)
* Russian: [Наталья Хедлунд](https://twitter.com/Lifestar_n).
* Serbian: [Aleksandar Đurić](https://twitter.com/sokodtreshnje)
* Turkish: [Burak Yüksek](https://twitter.com/burakyuksek).
Many thanks also to the people who worked on the documentation. Initially, [Manuel Cortez](https://twitter.com/manuelcortez00) did the documentation in Spanish, and translated to English by [Bryner Villalobos](https://twitter.com/Bry_StarkCR), [Robert Spangler](https://twitter.com/glasscity1837), [Sussan Rey](https://twitter.com/sussanrey17), [Anibal Hernandez](https://twitter.com/anibalmetal), and [Holly Scott-Gardner](https://twitter.com/holly1994). It was updated by [Sukil Etxenike](https://twitter.com/sukil2011), with some valuable corrections by [Brian Hartgen](https://twitter.com/brianhartgen) and [Bill Dengler](https://twitter.com/codeofdusk).
------------------------------------------------------------------------
Copyright © 2013-2016. Manuel Cortéz

View File

@@ -1,427 +0,0 @@
# -*- coding: utf-8 -*-
import application
documentation = []
documentation.append(_(u"""Documentation for TWBlue - {0}""").format(application.version))
# Translators: This is the new line character, don't change it in the translations.
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Table of contents"""))
# Table of contents for the python markdown extension
documentation.append("""[TOC]""")
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Warning!"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""You are reading documentation produced for a program still in development. The object of this manual is to explain some details of the operation of the program. Bear in mind that as the software is in the process of active development, parts of this user guide may change in the near future, so it is advisable to keep checking from time to time to avoid missing important information."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If you want to see what has changed from the previous version, [read the list of updates here.](changes.html)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Introduction"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""TWBlue is an application to make Twitter simple and fast, while using as few resources as possible. With TWBlue, you can do things like the following:"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Tweet, reply to, retweet and delete tweets,"""))
documentation.append(_(u"""* Mark and unmark a tweet as favourite,"""))
documentation.append(_(u"""* Send and delete direct messages,"""))
documentation.append(_(u"""* See your friends and followers,"""))
documentation.append(_(u"""* Follow, unfollow, report and block a user,"""))
documentation.append(_(u"""* Open a user's timeline to see their tweets separately,"""))
documentation.append(_(u"""* Open URLs from a tweet or direct message,"""))
documentation.append(_(u"""* Play several types of audio files from addresses,"""))
documentation.append(_(u"""* And more."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Usage"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Twitter is a social networking or micro-blogging tool which allows you to compose short status updates of your activities in 140 characters or less. Twitter is a way for friends, family and co-workers to communicate and stay connected through the exchange of quick, frequent messages. You can restrict delivery of updates to those in your circle of friends or, by default, allow anyone to access them."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""You can monitor the status of updates from your friends, family or co-workers (known as following), and they in turn can read any updates you create, (known as followers). The updates are referred to as Tweets. The Tweets are posted to your Twitter profile or Blog and are searchable using Twitter Search."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In order to use TWBlue, you must first have created an account on the Twitter website. The process for signing up for a Twitter account is very accessible. During the account registration, you will need to choose a Twitter username. This serves two purposes. This is the method through which people will comunicate with you, but most importantly, your username and password will be required to connect TWBlue to your Twitter account. We suggest you choose a username which is memorable both to you and the people you hope will follow you."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""We'll start from the premise that you have a Twitter account with its corresponding username and password."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Authorising the application"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""First of all, it's necessary to authorise the program so it can access your Twitter account and act on your behalf. The authorisation process is quite simple, and the program never retains data such as your password. In order to authorise the application, you just need to run the main executable file, called TWBlue.exe (on some computers it may appear simply as TWBlue if Windows Explorer is not set to display file extensions). We suggest you may like to place a Windows shortcut on your Desktop pointing to this executable file for quick and easy location."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""You can log into several Twitter accounts simultaneously. The program refers to each Twitter account you have configured as a "Session". If this is the first time you have launched TWBlue, and if no Twitter session exists, you will see the Session Manager. This dialogue box allows you to authorise as many accounts as you wish. If you press the Tab key to reach the "new account" button and activate it by pressing the Space Bar, a dialogue box will advise you that your default internet browser will be opened in order to authorise the application and you will be asked if you would like to continue. Activate the "yes" Button by pressing the letter "Y" so the process may start."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Your default browser will open on the Twitter page to request authorisation. Enter your username and password into the appropriate edit fields if you're not already logged in, select the authorise button, and press it."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Once you've authorised your twitter account, the website will redirect you to a page which will notify you that TWBlue has been authorised successfully. Now you are able to close the page by pressing ALT+F4 which will return you to the Session Manager. On the session list, you will see a new item temporarily called "Authorised account x" -where x is a number. The session name will change once you open that session."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""To start running TWBlue, press the Ok button in the Session Manager dialogue. By default, the program starts all the configured sessions automatically, however, you can change this behavior."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If all went well, the application will start playing sounds, indicating your data is being updated."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""When the process is finished, by default the program will play another sound, and the screen reader will say "ready" (this behaviour can be configured)."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## General concepts"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Before starting to describe TWBlue's usage, we'll explain some concepts that will be used extensively throughout this manual."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Buffer"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""A buffer is a list of items to manage the data which arrives from Twitter, after being processed by the application. When you configure a new session on TWBlue and start it, many buffers are created. Each of them may contain some of the items which this program works with: Tweets, direct messages, users, trends or events. According to the buffer you are focusing, you will be able to do different actions with these items."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The following is a description for every one of TWBlue's buffers and the kind of items they work with."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Home: this shows all the tweets on the main timeline. These are the tweets by users you follow."""))
documentation.append(_(u"""* Mentions: if a user, whether you follow them or not, mentions you on Twitter, you will find it in this list."""))
documentation.append(_(u"""* Direct messages: here you will find the private direct messages you exchange with users who follow you , or with any user, if you allow direct messages from everyone (this setting is configurable from Twitter). This list only shows received messages."""))
documentation.append(_(u"""* Sent direct messages: this buffer shows all the direct messages sent from your account."""))
documentation.append(_(u"""* Sent tweets: this shows all the tweets sent from your account."""))
documentation.append(_(u"""* Favourites: here you will see all the tweets you have favourited."""))
documentation.append(_(u"""* Followers: when users follow you, you'll be able to see them on this buffer, with some of their account details."""))
documentation.append(_(u"""* Friends: the same as the previous buffer, but these are the users you follow."""))
documentation.append(_(u"""* User timelines: these are buffers you may create. They contain only the tweets by a specific user. They're used so you can see the tweets by a single person and you don't want to look all over your timeline. You may create as many as you like."""))
documentation.append(_(u"""* Events: An event is anything that happens on Twitter, such as when someone follows you, when someone adds or removes one of your tweets from their favourites list, or when you subscribe to a list. There are many more, but the program shows the most common ones in the events buffer so that you can easily keep track of what is happening on your account."""))
documentation.append(_(u"""* Lists: A list is similar to a user timeline, except that you can configure it to contain tweets from multiple users."""))
documentation.append(_(u"""* Search: A search buffer contains the results of a search operation."""))
documentation.append(_(u"""* User favourites: You can have the program create a buffer containing tweets favourited by a particular user."""))
documentation.append(_(u"""* Trending Topics: a trend buffer shows the top ten most used terms in a geographical region. This region may be a country or a city. Trends are updated every five minutes."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If a tweet contains a URL, you can press enter in the GUI or Control + Windows + Enter in the invisible interface to open it. If it contains audio, you can press Control + Enter or Control + Windows + Alt + Enter to play it, respectively. TWBlue will play a sound if the tweet contains the #audio hashtag, but there may be tweets which contain audio without this. Finally, if a tweet contains geographical information, you can press Control + Windows + G in the invisible interface to retrieve it."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Username fields"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""These fields accept a Twitter username (without the at sign) as the input. They are present in the send direct message and the user actions dialogue boxes. Those dialogues will be discussed later. The initial value of these fields depends on where they were opened from. They are prepopulated with the username of the sender of the focused tweet (if they were opened from the home and sent timelines, from users' timelines or from lists), the sender of the focused direct message (if from the received or sent direct message buffers) or in the focused user (if from the followers' or friends' buffer). If one of those dialogue boxes is opened from a tweet, and if there are more users mentioned in it, you can use the arrow keys to switch between them. Alternatively, you can also type a username."""))
documentation.append(_(u"""## The program's interfaces"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### The graphical user interface (GUI)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The graphical user interface of TWBlue consists of a window containing:"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* a menu bar accomodating five menus (application, tweet, user, buffer and help);"""))
documentation.append(_(u"""* One tree view,"""))
documentation.append(_(u"""* One list of items"""))
documentation.append(_(u"""* Four buttons in most dialogs: Tweet, retweet , reply and direct message."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The actions that are available for every item will be described later."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In summary, the GUI contains two core components. These are the controls you will find while pressing the Tab key within the program's interface, and the different elements present on the menu bar."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### Buttons in the application"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Tweet: this button opens up a dialogue box to write your tweet. The message must not exceed 140 characters. If you write past this limit, a sound will play to warn you. Note that the character count is displayed in the title bar. You may use the shorten and expand URL buttons to comply with the character limit. You can upload a picture, check spelling, attach audio or translate your message by selecting one of the available buttons in the dialogue box. In addition, you can autocomplete the entering of users by pressing Alt + A or the button for that purpose if you have the database of users configured. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will speak an error message in English describing the problem."""))
documentation.append(_(u"""* Retweet: this button retweets the message you're reading. After you press it, if you haven't configured the application not to do so, you'll be asked if you want to add a comment or simply send it as written. If you choose to add a comment, and if the original tweet plus the comment exceeds 140 characters, you will be asked if you want to post it as a comment with a mention to the original user and a link to the originating tweet."""))
documentation.append(_(u"""* Reply: when you're viewing a tweet, you can reply to the user who sent it by pressing this button. A dialogue will open up similar to the one for tweeting, but with the name of the user already filled in (for example @user) so you only need to write your message. If there are more users referred to in the tweet, you can press shift-tab and activate the mention all users button. When you're on the friends or followers lists, the button will be called mention instead."""))
documentation.append(_(u"""* Direct message: exactly like sending a tweet, but it's a private message which can only be read by the user you send it to. Press shift-tab to see the recipient. If there were other users mentioned in the tweet you were reading, you can arrow up or down to choose which one to send it to, or write the username yourself without the at sign."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Bear in mind that buttons will appear according to which actions are possible on the list you are browsing. For example, on the home timeline, mentions, sent, favourites and user timelines you will see the four buttons, while on the direct messages list you'll only get the direct message and tweet buttons, and on friends and followers lists the direct message, tweet, and mention buttons will be available."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### Menus"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Visually, Towards the top of the main application window, can be found a menu bar which contains many of the same functions as listed in the previous section, together with some additional items. To access the menu bar, press the alt key. You will find five menus listed: application, tweet, user, buffer and help. This section describes the items on each one of them."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Application menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Manage accounts: Opens a window with all the sessions configured in TWBlue, where you can add new sessions or delete the ones you've already created."""))
documentation.append(_(u"""* Update profile: opens a dialogue where you can update your information on Twitter: name, location, website and bio. If you have already set this up the fields will be prefilled with the existing information. Also, you can upload a photo to your profile."""))
documentation.append(_(u"""* Hide window: turns off the Graphical User Interface. Read the section on the invisible interface for further details."""))
documentation.append(_(u"""* Search: shows a dialogue box where you can search for tweets or users on Twitter."""))
documentation.append(_(u"""* Lists Manager: This dialogue box allows you to manage your Twitter lists. In order to use them, you must first create them. Here, you can view, edit, create, delete or, optionally, open them in buffers similar to user timelines."""))
documentation.append(_(u"""* Edit keystrokes: this opens a dialogue where you can see and edit the shortcuts used in the invisible interface."""))
documentation.append(_(u"""* Account settings: Opens a dialogue box which lets you customize settings for the current account."""))
documentation.append(_(u"""* Global settings: Opens a dialogue which lets you configure settings for the entire application."""))
documentation.append(_(u"""* Quit: asks whether you want to exit the program. If the answer is yes, it closes the application. If you do not want to be asked for confirmation before exiting, uncheck the checkbox from the global settings dialogue box."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Tweet menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* You will first find the items to tweet, reply and retweet, which are equivalent to the buttons with the same name."""))
documentation.append(_(u"""* Add to favourites: marks the tweet you're viewing as a favourite."""))
documentation.append(_(u"""* Remove from favourites: removes the tweet from your favourites, but not from Twitter."""))
documentation.append(_(u"""* Show tweet: opens up a dialogue box where you can read the tweet, direct message, friend or follower which has focus. You can read the text with the arrow keys. It's a similar dialog box as used for composing tweets, without the ability to send the tweet, file attachment and autocompleting capabilities. It does however include a retweets and favourites count. If you are in the followers or the friends list, it will only contain a read-only edit box with the information in the focused item and a close button."""))
documentation.append(_(u"""* View address: If the selected tweet has geographical information, TWBlue may display a dialogue box where you can read the tweet address. This address is retrieved by sending the geographical coordinates of the tweet to Google maps."""))
documentation.append(_(u"""* View conversation: If you are focusing a tweet with a mention, it opens a buffer where you can view the whole conversation."""))
documentation.append(_(u"""* Delete: permanently removes the tweet or direct message which has focus from Twitter and from your lists. Bear in mind that Twitter only allows you to delete tweets you have posted yourself."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### User menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Actions: Opens a dialogue where you can interact with a user. This dialogue box will be populated with the user who sent the tweet or direct message in focus or the selected user in the friends or followers buffer. You can edit it or leave it as is and choose one of the following actions:"""))
documentation.append(_(u""" * Follow: Follows a user. This means you'll see his/her tweets on your home timeline, and if he/she also follows you, you'll be able to exchange direct messages. You may also send / receive direct messages from each other if you have configured the option to allow direct messages from anyone."""))
documentation.append(_(u""" * Unfollow: Stops following a user, which causes you not being able to see his/her tweets on your main timeline neither exchanging direct messages, unless they have enabled receiving direct messages from anyone."""))
documentation.append(_(u""" * Mute: While muting someone, TWBlue won't show you nor his/her tweets on your main timeline; neither will you see that person's mentions. But you both will be able to exchange direct messages. The muted user is not informed of this action."""))
documentation.append(_(u""" * Unmute: this option allows TWBlue to display the user's tweets and mentions again."""))
documentation.append(_(u""" * Block: Blocks a user. This forces the user to unfollow you ."""))
documentation.append(_(u""" * Unblock: Stops blocking a user."""))
documentation.append(_(u""" * Report as spam: this option sends a message to Twitter suggesting the user is performing prohibited practices on the social network."""))
documentation.append(_(u""" * Ignore tweets from this client: Adds the client from which the focused tweet was sent to the ignored clients list."""))
documentation.append(_(u"""* View timeline: Lets you open a user's timeline by choosing the user in a dialog box. It is created when you press enter. If you invoke this option relative to a user that has no tweets, the operation will fail. If you try creating an existing timeline the program will warn you and will not create it again."""))
documentation.append(_(u"""* Direct message: same action as the button."""))
documentation.append(_(u"""* Add to List: In order to see someone's tweets in one or more of your lists, you must add them first. In the dialogue box that opens after selecting the user, you will be asked to select the list you wish to add the user to. Thereafter, the list will contain a new member and their tweets will be displayed there."""))
documentation.append(_(u"""* Remove from list: lets you remove a user from a list."""))
documentation.append(_(u"""* View lists: Shows the lists created by a specified user."""))
documentation.append(_(u"""* Show user profile: opens a dialogue with the profile of the specified user."""))
documentation.append(_(u"""* View favourites: Opens a buffer where you can see the tweets which have been favourited by a particular user."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Buffer menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* New trending topics buffer: This opens a buffer to get the worlwide trending topics or those of a country or a city. You'll be able to select from a dialogue box if you wish to retrieve countries' trends, cities' trends or worldwide trends (this option is in the cities' list) and choose one from the selected list. The trending topics buffer will be created once the "OK" button has been activated within the dialogue box. Remember this kind of buffer will be updated every five minutes."""))
documentation.append(_(u"""* Load previous items: This allows more items to be loaded for the specified buffer."""))
documentation.append(_(u"""* Mute: Mutes notifications of a particular buffer so you will not hear when new tweets arrive."""))
documentation.append(_(u"""* autoread: When enabled, the screen reader or SAPI 5 Text to Speech voice (if enabled) will read the text of incoming tweets. Please note that this could get rather chatty if there are a lot of incoming tweets."""))
documentation.append(_(u"""* Clear buffer: Deletes all items from the buffer."""))
documentation.append(_(u"""* Destroy: dismisses the list you're on."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Help menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Documentation: opens up this file, where you can read some useful program concepts."""))
documentation.append(_(u"""* Sounds tutorial: Opens a dialog box where you can familiarize yourself with the different sounds of the program."""))
documentation.append(_(u"""* What's new in this version?: opens up a document with the list of changes from the current version to the earliest."""))
documentation.append(_(u"""* Check for updates: every time you open the program it automatically checks for new versions. If an update is available, it will ask you if you want to download the update. If you accept, the updating process will commence. When complete, TWBlue will be restarted. This item checks for new updates without having to restart the application."""))
documentation.append(_(u"""* Report an error: opens up a dialogue box to report a bug by completing a small number of fields. Pressing enter will send the report. If the operation doesn't succeed the program will display a warning."""))
documentation.append(_(u"""* TWBlue's website: visit our [home page](http://twblue.es) where you can find all relevant information and downloads for TWBlue and become a part of the community."""))
documentation.append(_(u"""* About TWBlue: shows the credits of the program."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### The invisible user interface"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The invisible interface, as its name suggests, has no graphical window and works directly with screen readers such as JAWS for Windows, NVDA and System Access. This interface is disabled by default, but you can enable it by pressing Control + M. It works similarly to TheQube and Chicken Nugget. Its shortcuts are similar to those found in these two clients. In addition, the program has builtin support for the keymaps for these applications, configurable through the global settings dialogue. By default, you cannot use this interface's shortcuts in the GUI, but you can configure this in the global settings dialogue."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The next section contains a list of keyboard shortcuts for both interfaces. Bear in mind that we will only describe the default keymap."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Keyboard shortcuts"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Shortcuts of the graphical user interface (GUI)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Enter: Open URL."""))
documentation.append(_(u"""* Control + Enter: Play audio."""))
documentation.append(_(u"""* Control + M: Hide the GUI."""))
documentation.append(_(u"""* Control + N: Compose a new tweet."""))
documentation.append(_(u"""* Control + R: Reply / mention."""))
documentation.append(_(u"""* Control + Shift + R: Retweet."""))
documentation.append(_(u"""* Control + D: Send a direct message."""))
documentation.append(_(u"""* control + F: Add tweet to favourites."""))
documentation.append(_(u"""* Control + Shift + F: Remove a tweet from favourites."""))
documentation.append(_(u"""* Control + S: Open the user actions dialogue."""))
documentation.append(_(u"""* Control + Shift + V: Show tweet."""))
documentation.append(_(u"""* Control + Q: Quit this program."""))
documentation.append(_(u"""* Control + I: Open user timeline."""))
documentation.append(_(u"""* Control + Shift + i: Destroy buffer."""))
documentation.append(_(u"""* F5: Increase volume by 5%."""))
documentation.append(_(u"""* F6: Decrease volume by 5%."""))
documentation.append(_(u"""* Control + P: Edit your profile."""))
documentation.append(_(u"""* Control + Delete: Delete a tweet or direct message."""))
documentation.append(_(u"""* Control + Shift + Delete: Empty the current buffer."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Shortcuts of the invisible interface (default keymap)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Control + Windows + Up Arrow: moves to the previous item in the buffer."""))
documentation.append(_(u"""* Control + Windows + Down Arrow: moves to the next item in the buffer."""))
documentation.append(_(u"""* Control + Windows + Left Arrow: Move to the previous buffer."""))
documentation.append(_(u"""* Control + Windows + Right Arrow: Move to the next buffer."""))
documentation.append(_(u"""* Control + Windows + Shift + Left: Focus the previous session."""))
documentation.append(_(u"""* Control + Windows + Shift + Right: Focus the next session."""))
documentation.append(_(u"""* Control + Windows + C: View conversation."""))
documentation.append(_(u"""* Control + Enter: Open URL."""))
documentation.append(_(u"""* Control + Windows + Enter: Play audio."""))
documentation.append(_(u"""* Control + Windows + M: Show or hide the GUI."""))
documentation.append(_(u"""* Control + Windows + N: New tweet."""))
documentation.append(_(u"""* Control + Windows + R: Reply / Mention."""))
documentation.append(_(u"""* Control + Windows + Shift + R: Retweet."""))
documentation.append(_(u"""* Control + Windows + D: Send direct message."""))
documentation.append(_(u"""* Windows+ Alt + F: Mark as favourite."""))
documentation.append(_(u"""* Alt + Windows + Shift + F: Remove from favourites."""))
documentation.append(_(u"""* Control + Windows + S: Open the user actions dialogue."""))
documentation.append(_(u"""* Control + Windows + Alt + N: See user details."""))
documentation.append(_(u"""* Control + Windows + V: Show tweet."""))
documentation.append(_(u"""* Control + Windows + F4: Quit TWBlue."""))
documentation.append(_(u"""* Control + Windows + I: Open user timeline."""))
documentation.append(_(u"""* Control + Windows + Shift + I: Destroy buffer."""))
documentation.append(_(u"""* Control + Windows + Alt + Up: Increase volume by 5%."""))
documentation.append(_(u"""* Control + Windows + Alt + Down: Decrease volume by 5%."""))
documentation.append(_(u"""* Control + Windows + Home: Jump to the first element of the current buffer."""))
documentation.append(_(u"""* Control + Windows + End: Jump to the last element of the current buffer."""))
documentation.append(_(u"""* Control + Windows + PageUp: Jump 20 elements up in the current buffer."""))
documentation.append(_(u"""* Control + Windows + PageDown: Jump 20 elements down in the current buffer."""))
documentation.append(_(u"""* Windows + Alt + P: Edit profile."""))
documentation.append(_(u"""* Control + Windows + Delete: Delete a tweet or direct message."""))
documentation.append(_(u"""* Control + Windows + Shift + Delete: Empty the current buffer."""))
documentation.append(_(u"""* Control + Windows + Space: Repeat last item."""))
documentation.append(_(u"""* Control + Windows + Shift + C: Copy to clipboard."""))
documentation.append(_(u"""* Control + Windows+ A: Add user to list."""))
documentation.append(_(u"""* Control + Windows + Shift + A: Remove user from list."""))
documentation.append(_(u"""* Control + Windows + M: Mute / unmute the current buffer."""))
documentation.append(_(u"""* Windows + Alt + M: Mute / unmute the current session."""))
documentation.append(_(u"""* Control + Windows + E: Toggle the automatic reading of incoming tweets in the current buffer."""))
documentation.append(_(u"""* Control + Windows + -: Search on Twitter."""))
documentation.append(_(u"""* Control + Windows + K: Show the keystroke editor."""))
documentation.append(_(u"""* Control + Windows + L: Show lists for a specified user."""))
documentation.append(_(u"""* Windows + Alt + PageUp: Load previous items for the current buffer."""))
documentation.append(_(u"""* Control + Windows + G: Get geolocation."""))
documentation.append(_(u"""* Control + Windows + Shift + G: Display the tweet's geolocation in a dialogue."""))
documentation.append(_(u"""* Control + Windows + T: Create a trending topics' buffer."""))
documentation.append(_(u"""* Control + Windows + {: Find a string in the current buffer."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Configuration"""))
documentation.append(_(u"""As described above, this application has two configuration dialogues, the global settings dialogue and the account settings dialogue."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### The account settings dialogue"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### General tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Autocompletion settings: Allows you to configure the autocompletion database. You can add users manually or let TWBlue add your followers, friends or both."""))
documentation.append(_(u"""* Relative timestamps: Allows you to configure whether the application will calculate the time the tweet or direct message was sent or received based on the current time, or simply say the time it was received or sent."""))
documentation.append(_(u"""* API calls: Allows you to adjust the number of API calls to be made to Twitter by this program."""))
documentation.append(_(u"""* Items on each API call: Allows you to specify how many items should be retrieved from Twitter for each API call (default and maximum is 200)."""))
documentation.append(_(u"""* Inverted buffers: Allows you to specify whether the buffers should be inverted, which means that the oldest items will show at the end of them and the newest at the beginning."""))
documentation.append(_(u"""* Retweet mode: Allows you to specify the behaviour when posting a retweet: you can choose between retweeting with a comment, retweeting without comment or being asked."""))
documentation.append(_(u"""* Number of items per buffer to cache in database: This allows you to specify how many items TWBlue should cache in a database. You can type any number, 0 to cache all items, or leave blank to disable caching entirely."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### buffers tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""This tab displays a list for each buffer you have available in TWBlue, except for searches, timelines, favourites' timelines and lists. You can show, hide and move them."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### The ignored clients tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In this tab, you can add and remove clients to be ignored by the program."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### Sound tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In this tab, you can adjust the sound volume, select the input and output device and set the soundpack used by the program."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### Audio service tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In this tab, you can enter your SndUp API key (if you have one) to upload audio to SndUp with your account. Note that if account credentials are not specified you will upload anonimously."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Global settings"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""This dialogue allows you to configure some settings which will affect the entire application."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### General tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Language: This allows you to change the language of this program. Currently supported languages are arabic, Catalan, German, English, Spanish, Basque, Finnish, French, Galician, Croatian, Hungarian, Italian, Polish, Portuguese, Russian and Turkish."""))
documentation.append(_(u"""* Ask before exiting TWBlue: This checkbox allows you to control whetherthe program will ask for confirmation before exiting."""))
documentation.append(_(u"""* Play a sound when TWBlue launches: This checkbox allows you to configure whether the application will play a sound when it has finished loading the buffers."""))
documentation.append(_(u"""* Speak a message when TWBlue launches: This is the same as the previous option, but this checkbox configures whether the screen reader will say \"ready\"."""))
documentation.append(_(u"""* Use the invisible interface's shortcuts in the GUI: As the invisible interface and the Graphical User Interface have their own shortcuts, you may want to use the invisible interface's keystrokes all the time. If this option is checked, the invisible interface's shortcuts will be usable in the GUI."""))
documentation.append(_(u"""* Activate SAPI5 when any other screen reader is not being run: This checkbox allows to activate SAPI 5 TTS when no other screen reader is being run."""))
documentation.append(_(u"""* Hide GUI on launch: This allows you to configure whether TWBlue will start with the GUI or the invisible interface."""))
documentation.append(_(u"""* Keymap: This option allows you to change the keymap used by the program in the invisible interface. The shipped keymaps are Default, Qwitter, Windows 10 and Chicken Nugget. The keymaps are in the \"keymaps\" folder, and you can create new ones. Just create a new \".keymap\" file and change the keystrokes associated with the actions, as it is done in the shipped keymaps."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""#### Proxi tab"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In this tab you can configure TWBlue to use a Proxy server by completing the fields displayed (server, port, user and password)."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## License, source code and donations"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Tw Blue is free software, licensed under the GNU GPL license, either version 2 or, at your option, any later version. You can view the license in the file named license.txt, or online at <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The source code of the program is available on GitHub at <https://www.github.com/manuelcortez/twblue>."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If you want to donate to the project, you can do so at <http://twblue.es/?q=node/3&language=en>. Thank you for your support!"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Contact"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If you still have questions after reading this document, if you wish to collaborate to the project in some other way, or if you simply want to get in touch with the application developer, follow the Twitter account [@tw_blue2](https://twitter.com/tw_blue2) or [@manuelcortez00.](https://twitter.com/manuelcortez00) You can also visit [our website](http://twblue.es)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Credits"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""TWBlue is developed and mantained by [Manuel Cortéz](https://twitter.com/manuelcortez00), [José Manuel Delicado](https://twitter.com/jmdaweb), and [Bill Dengler](https://twitter.com/codeofdusk). It's supported and sponsored by [Technow S. L.](https://twitter.com/technow)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""We would also like to thank the translators of TWBlue, who have allowed the spreading of the application."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* English: [Bryner Villalobos](https://twitter.com/Bry_StarkCR) and [Bill Dengler](https://twitter.com/codeofdusk)."""))
documentation.append(_(u"""* Arabic: Mohammed Al Shara."""))
documentation.append(_(u"""* Catalan: [Joan Rabat](https://twitter.com/joanrabat) and Juan Carlos Rivilla."""))
documentation.append(_(u"""* Spanish: [Manuel Cortéz](https://twitter.com/manuelcortez00)."""))
documentation.append(_(u"""* Basque: [Sukil Etxenike](https://twitter.com/sukil2011)."""))
documentation.append(_(u"""* Finnish: Jani Kinnunen."""))
documentation.append(_(u"""* French: Rémi Ruiz."""))
documentation.append(_(u"""* Galician: [Alba Kinteiro](https://twitter.com/albasmileforeve)."""))
documentation.append(_(u"""* German: Steffen Schultz."""))
documentation.append(_(u"""* Hungarian: Robert Osztolykan."""))
documentation.append(_(u"""* Polish: Pawel Masarczyk."""))
documentation.append(_(u"""* Portuguese: Odenilton Júnior Santos."""))
documentation.append(_(u"""* Russian: Alexander Jaszyn."""))
documentation.append(_(u"""* Turkish: Burak."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Many thanks also to the people who worked on the documentation. Initially, [Manuel Cortez](https://twitter.com/manuelcortez00) did the documentation in Spanish, and translated to English by [Bryner Villalobos](https://twitter.com/Bry_StarkCR), [Robert Spangler](https://twitter.com/glasscity1837), [Sussan Rey](https://twitter.com/sussanrey17), [Anibal Hernandez](https://twitter.com/anibalmetal), and [Holly Scott-Gardner](https://twitter.com/holly1994). It was updated by [Sukil Etxenike](https://twitter.com/sukil2011), with some valuable corrections by [Brian Hartgen](https://twitter.com/brianhartgen) and [Bill Dengler](https://twitter.com/codeofdusk)."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""---"""))
documentation.append(_(u"""Copyright © 2013-2015. Manuel Cortéz"""))

View File

@@ -1,7 +1,7 @@
[Launch]
ProgramExecutable=TWBlue\TWBlue.exe
ProgramExecutable64=TWBlue64\TWBlue.exe
CommandLineArguments=-p -d "%PAL:DataDir%"
CommandLineArguments=-d "%PAL:DataDir%"
SinglePortableAppInstance=true
MinOS=XP
SingleAppInstance=false

View File

@@ -1,11 +1,11 @@
[Format]
Type=PortableApps.comFormat
Version=3.0
Version=3.4
[Details]
Name=TWBlue portable
Name=tw blue portable
AppID=TWBluePortable
Publisher=jmdaweb & TWBlue & PortableApps.com
Publisher=jmdaweb & TW blue & PortableApps.com
Homepage=PortableApps.com/TWBluePortable
Category=Internet
Description=A portable, fast and accessible Twitter client with many options.
@@ -20,8 +20,8 @@ CommercialUse=true
EULAVersion=2
[Version]
PackageVersion=0.80.0.0
DisplayVersion=0.80
PackageVersion=0.86.0.0
DisplayVersion=0.86
[Control]
Icons=1

View File

@@ -8,10 +8,14 @@ FINNISH=true
FRENCH=true
GALICIAN=true
GERMAN=true
CROATIAN=true
HUNGARIAN=true
ITALIAN=true
JAPANESE=true
POLISH=true
PORTUGUESEBR=true
ROMANIAN=true
RUSSIAN=true
SERBIAN=true
SPANISHINTERNATIONAL=true
TURKISH=true
TURKISH=true

View File

@@ -2,7 +2,7 @@
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>TWBlue Portable Help</title>
<title>tw blue Portable Help</title>
<link rel="alternate" href="http://portableapps.com/feeds/general" type="application/rss+xml" title="PortableApps.com">
<link rel="shortcut icon" href="Other/Help/Images/Favicon.ico">
<style type="text/css">
@@ -125,13 +125,13 @@
<body>
<div class="logo"><a href="http://portableapps.com/"><img src="Other/Help/Images/Help_Logo_Top.png" alt="PortableApps.com - Your Digital Life, Anywhere"></a></div>
<div class="content">
<h1 class="hastagline">TWBlue Portable Help</h1>
<h1 class="hastagline">tw blue Portable Help</h1>
<h2 class="tagline">A powerful and accessible Twitter client</h2>
<p>TWBlue Portable is the TWBlue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as TWBlue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about TWBlue...</a></p>
<p>tw blue Portable is the tw blue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as tw blue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about tw blue...</a></p>
<p><a href="http://portableapps.com/donate"><img src="Other/Help/Images/Donation_Button.png" style="vertical-align:middle" alt="Make a Donation"></a> - Support PortableApps.com's Hosting and Development</p>
<p><a href="http://portableapps.com/node/*Node ID*">Go to the TWBlue Portable Homepage &gt;&gt;</a></p>
<p><a href="http://portableapps.com/node/*Node ID*">Go to the tw blue Portable Homepage &gt;&gt;</a></p>
<p><a href="http://portableapps.com/">Get more portable apps at PortableApps.com</a></p>
<p>This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative.</p>

View File

@@ -12,10 +12,10 @@ SetCompress auto
SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TWBlue"
VIAddVersionKey LegalCopyright "Copyright 2015 Manuel Cortéz."
VIAddVersionKey ProductVersion "0.80"
VIAddVersionKey FileVersion "0.80"
VIProductVersion "0.80.0.0"
VIAddVersionKey LegalCopyright "Copyright 2016 Manuel Cortéz."
VIAddVersionKey ProductVersion "0.86"
VIAddVersionKey FileVersion "0.86"
VIProductVersion "0.86.0.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
@@ -26,7 +26,6 @@ var StartMenuFolder
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
!define MUI_FINISHPAGE_LINK_LOCATION "http://twblue.es"
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
!define MUI_FINISHPAGE_RUN_PARAMETERS "-i"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
@@ -45,6 +44,10 @@ var StartMenuFolder
!insertmacro MUI_LANGUAGE "Galician"
!insertmacro MUI_LANGUAGE "Catalan"
!insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_RESERVEFILE_LANGDLL
Section
SetShellVarContext All
@@ -54,10 +57,10 @@ File /r TWBlue64\*
${Else}
File /r TWBlue\*
${EndIf}
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
!insertmacro MUI_STARTMENU_WRITE_END
@@ -66,10 +69,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.80"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.86"
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" 80
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 86
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd

View File

@@ -28,6 +28,8 @@ timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
followers_timelines = list(default=list())
friends_timelines = list(default=list())
trending_topic_buffers = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list(mentions, direct_messages, events))
@@ -36,6 +38,4 @@ autoread_buffers = list(default=list(mentions, direct_messages, events))
spelling_language = string(default="")
save_followers_in_autocompletion_db = boolean(default=False)
save_friends_in_autocompletion_db = boolean(default=False)
[services]
pocket_access_token = string(default="")
twishort_enabled = boolean(default=False)

View File

@@ -1,19 +1,23 @@
from __future__ import absolute_import
import ctypes
import os
import types
from platform_utils import paths
def load_library(libname):
def load_library(libname, cdll=False):
if paths.is_frozen():
libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname)
else:
libfile = os.path.join(paths.module_path(), 'lib', libname)
return ctypes.windll[libfile]
if cdll:
return ctypes.cdll[libfile]
else:
return ctypes.windll[libfile]
def get_output_classes():
import outputs
from . import outputs
module_type = types.ModuleType
classes = [m.output_class for m in outputs.__dict__.itervalues() if type(m) == module_type and hasattr(m, 'output_class')]
classes = [m.output_class for m in outputs.__dict__.values() if type(m) == module_type and hasattr(m, 'output_class')]
return sorted(classes, key=lambda c: c.priority)
def find_datafiles():

View File

@@ -1,16 +1,20 @@
from __future__ import absolute_import
import platform
if platform.system() == 'Windows':
import nvda
import jaws
import sapi5
import window_eyes
import system_access
import dolphin
import pc_talker
from . import nvda
from . import jaws
from . import sapi5
from . import window_eyes
from . import system_access
from . import dolphin
from . import pc_talker
#import sapi4
elif platform.system() == "Darwin":
import voiceover
elif platform.system() == "Linux":
import speechDispatcher
import auto
if platform.system() == 'Darwin':
from . import voiceover
from . import say
if platform.system() == 'Linux':
from . import e_speak
from . import auto

View File

@@ -1,24 +1,17 @@
import platform
from __future__ import absolute_import
import accessible_output2
from base import Output, OutputError
from .base import Output, OutputError
class Auto(Output):
def __init__(self):
if platform.system() == "Darwin":
import voiceover
self.outputs = [voiceover.VoiceOver()]
elif platform.system() == "Linux":
import speechDispatcher
self.outputs = [speechDispatcher.SpeechDispatcher()]
elif platform.system() == "Windows":
output_classes = accessible_output2.get_output_classes()
self.outputs = []
for output in output_classes:
try:
self.outputs.append(output())
except OutputError:
pass
output_classes = accessible_output2.get_output_classes()
self.outputs = []
for output in output_classes:
try:
self.outputs.append(output())
except OutputError:
pass
def get_first_available_output(self):
for output in self.outputs:
@@ -40,3 +33,8 @@ class Auto(Output):
output = self.get_first_available_output()
if output:
output.speak(*args, **kwargs)
def is_system_output(self):
output = self.get_first_available_output()
if output:
return output.is_system_output()

View File

@@ -5,27 +5,43 @@ class OutputError(Exception):
pass
class Output(object):
name = "Unnamed Output" #The name of this output
lib32 = None #name of 32-bit lib
lib64 = None #name of 64-bit lib
priority = 100 #Where to sort in the list of available outputs for automaticly speaking
name = "Unnamed Output"
lib32 = None
lib64 = None
argtypes = {}
cdll = False
priority = 100
system_output = False
def __init__(self):
is_32bit = platform.architecture()[0] == "32bit"
if self.lib32 and is_32bit:
self.lib = load_library(self.lib32)
self.is_32bit = platform.architecture()[0] == "32bit"
if self.lib32 and self.is_32bit:
self.lib = load_library(self.lib32, cdll=self.cdll)
elif self.lib64:
self.lib = load_library(self.lib64)
self.lib = load_library(self.lib64, cdll=self.cdll)
else:
self.lib = None
if self.lib is not None:
for func in self.argtypes:
try:
getattr(self.lib, func).argtypes = self.argtypes[func]
except AttributeError:
pass
def output(self, text, **options):
output = False
if hasattr(self, 'speak') and callable(self.speak):
self.speak(text, **options)
if self.speak(text, **options):
output = True
if hasattr(self, 'braille') and callable(self.braille):
self.braille(text, **options)
if self.braille(text, **options):
output = True
if not output:
raise RuntimeError("Output %r does not have any method defined to output" % self)
def is_system_output(self):
return self.system_output
def speak(self, **optiont):
return False
def braille(self, *args, **options):
return False

View File

@@ -1,19 +1,25 @@
from __future__ import absolute_import
import os
import ctypes
from base import Output
from .base import Output
class Dolphin (Output):
"""Supports dolphin products."""
name = 'Dolphin'
lib32 = 'dolapi.dll'
argtypes = {
'DolAccess_Command': (ctypes.c_wchar_p, ctypes.c_int, ctypes.c_int),
'DolAccess_Action': (ctypes.c_int,),
}
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
#If we don't call this, the API won't let us speak.
if self.is_active():
self.lib.DolAccess_Command(unicode(text), (len(text)*2)+2, 1)
self.lib.DolAccess_Command(text, (len(text)*2)+2, 1)
def silence(self):
self.lib.DolAccess_Action(141)

View File

@@ -0,0 +1,31 @@
from __future__ import absolute_import
from .base import Output
try:
import espeak.core
except:
raise RuntimeError("Cannot find espeak.core. Please install python-espeak")
class ESpeak(Output):
"""Speech output supporting ESpeak on Linux
Note this requires python-espeak to be installed
This can be done on Debian distros by using apt-get install python-espeak
Or through this tarball: https://launchpad.net/python-espeak
"""
name = "Linux ESpeak"
def is_active(self):
try:
import espeak.core
except:
return False
return True
def speak(self, text, interrupt = 0):
if interrupt:
self.silence()
espeak.core.synth(text)
def silence(self):
espeak.core.cancel()
output_class = ESpeak

View File

@@ -1,8 +1,9 @@
from __future__ import absolute_import
import win32gui
from libloader.com import load_com
import pywintypes
from base import Output, OutputError
from .base import Output, OutputError
class Jaws (Output):
"""Output supporting the Jaws for Windows screen reader."""

View File

@@ -1,15 +1,21 @@
from __future__ import absolute_import
import os
import platform
import ctypes
from platform_utils import paths
from libloader import load_library
from base import Output
from .base import Output
class NVDA(Output):
"""Supports The NVDA screen reader"""
name = "NVDA"
lib32 = 'nvdaControllerClient32.dll'
lib64 = 'nvdaControllerClient64.dll'
argtypes = {
'nvdaController_brailleMessage': (ctypes.c_wchar_p,),
'nvdaController_speakText': (ctypes.c_wchar_p,),
}
def is_active(self):
try:
@@ -18,12 +24,12 @@ class NVDA(Output):
return False
def braille(self, text, **options):
self.lib.nvdaController_brailleMessage(unicode(text))
self.lib.nvdaController_brailleMessage(text)
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.nvdaController_speakText(unicode(text))
self.lib.nvdaController_speakText(text)
def silence(self):
self.lib.nvdaController_cancelSpeech()

View File

@@ -1,14 +1,19 @@
from __future__ import absolute_import
import ctypes
from base import Output
from .base import Output
class PCTalker(Output):
lib32 = 'pctkusr.dll'
lib64 = 'pctkusr64.dll'
cdll = True
argtypes = {
'PCTKPRead': (ctypes.c_char_p, ctypes.c_int, ctypes.c_int)
}
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.PCTKPRead(text.encode('cp932', 'replace'))
self.lib.PCTKPRead(text.encode('cp932', 'replace'), 0, 1)
def silence(self):
self.lib.PCTKVReset()

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
from builtins import range
from libloader.com import load_com
from base import Output
from .base import Output
import logging
log = logging.getLogger(__name__)

View File

@@ -1,12 +1,19 @@
# -*- coding: utf-8 -*-
import config
from __future__ import absolute_import
from collections import OrderedDict
from libloader.com import load_com
from base import Output, OutputError
from .base import Output, OutputError
import pywintypes
import logging
log = logging.getLogger(__name__)
SVSFDefault = 0
SVSFlagsAsync = 1
SVSFPurgeBeforeSpeak = 2
SVSFIsFilename = 4
SVSFIsXML = 8
SVSFIsNotXML = 16
SVSFPersistXML = 32
class SAPI5(Output):
has_volume = True
has_rate = True
@@ -19,9 +26,9 @@ class SAPI5(Output):
max_volume = 100
name = "sapi5"
priority = 101
system_output = True
def __init__(self):
if config.app["app-settings"]["voice_enabled"] == False: raise OutputError
try:
self.object = load_com("SAPI.SPVoice")
self._voices = self._available_voices()
@@ -36,14 +43,14 @@ class SAPI5(Output):
return _voices
def list_voices(self):
return self.available_voices.keys()
return list(self._voices.keys())
def get_voice(self):
return self.object.Voice.GetDescription()
def set_voice(self, value):
log.debug("Setting SAPI5 voice to \"%s\"" % value)
self.object.Voice = self.available_voices[value]
self.object.Voice = self._voices[value]
# For some reason SAPI5 does not reset audio after changing the voice
# By setting the audio device after changing voices seems to fix this
# This was noted from information at:
@@ -75,10 +82,10 @@ class SAPI5(Output):
self.silence()
# We need to do the pitch in XML here
textOutput = "<pitch absmiddle=\"%d\">%s</pitch>" % (round(self._pitch), text.replace("<", "&lt;"))
self.object.Speak(textOutput, 1|8)
self.object.Speak(textOutput, SVSFlagsAsync | SVSFIsXML)
def silence(self):
self.object.Speak("", 3)
self.object.Speak("", SVSFlagsAsync | SVSFPurgeBeforeSpeak)
def is_active(self):
if self.object:

View File

@@ -0,0 +1,21 @@
from __future__ import absolute_import
import os
from .base import Output
class AppleSay(Output):
"""Speech output supporting the Apple Say subsystem."""
name = 'Apple Say'
def __init__(self, voice = 'Alex', rate = '300'):
self.voice = voice
self.rate = rate
super(AppleSay, self).__init__()
def is_active(self):
return not os.system('which say')
def speak(self, text, interrupt = 0):
if interrupt:
self.silence()
os.system('say -v %s -r %s "%s" &' % (self.voice, self.rate, text))
def silence(self):
os.system('killall say')
output_class = AppleSay

View File

@@ -1,29 +0,0 @@
from base import Output, OutputError
import atexit
import application
class SpeechDispatcher(Output):
"""Supports speech dispatcher on Linux.
Note that this module will use the configuration of speech dispatcher, the user will need to configure the voice, language, punctuation and rate before using this module.
"""
name = 'SpeechDispatcher'
def __init__(self, *args, **kwargs):
super(SpeechDispatcher, self).__init__(*args, **kwargs)
try:
import speechd
self.spd = speechd.SSIPClient(application.name)
except ImportError:
raise OutputError
atexit.register(self.on_exit_event)
def speak(self, text, interupt=False):
if interupt == True:
self.spd.cancel()
self.spd.speak(text)
def is_active(self):
return True
def on_exit_event(self):
self.spd.close()
del self.spd

View File

@@ -1,18 +0,0 @@
# Copyright (C) 2001, 2002 Brailcom, o.p.s.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from .client import *

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
SPD_SPAWN_CMD = "/usr/bin/speech-dispatcher"

View File

@@ -1,18 +1,24 @@
from base import Output
from __future__ import absolute_import
import ctypes
from .base import Output
class SystemAccess (Output):
"""Supports System Access and System Access Mobile"""
name = "System Access"
lib32 = 'saapi32.dll'
argtypes = {
'SA_BrlShowTextW': (ctypes.c_wchar_p,),
'SA_SayW': (ctypes.c_wchar_p,),
}
priority = 99
def braille(self, text, **options):
self.lib.SA_BrlShowTextW(unicode(text))
self.lib.SA_BrlShowTextW(text)
def speak(self, text, interrupt=False):
if self.is_active():
self.dll.SA_SayW(unicode(text))
self.dll.SA_SayW(str(text))
def is_active(self):
try:

View File

@@ -1,23 +1 @@
from base import Output, OutputError
class VoiceOver (Output):
"""Supports the VoiceOver screenreader on the Mac.
Note that this will also output as a message to the braille display if VoiceOver is used with braille.
Calling this module could cause VoiceOver to be started.
"""
name = 'VoiceOver'
def __init__(self, *args, **kwargs):
super(VoiceOver, self).__init__(*args, **kwargs)
try:
from appscript import app
self.app = app('VoiceOver')
except ImportError:
raise OutputError
def speak(self, text, interupt=False):
self.app.output(text)
def is_active(self):
return True
from __future__ import absolute_import

View File

@@ -1,6 +1,7 @@
from __future__ import absolute_import
import win32gui
from libloader.com import load_com
from base import Output, OutputError
from .base import Output, OutputError
import pywintypes
class WindowEyes (Output):

View File

@@ -8,12 +8,15 @@ language = string(default="system")
hide_gui = boolean(default=False)
voice_enabled = boolean(default=False)
ask_at_exit = boolean(default=True)
handle_longtweets = boolean(default=False)
autostart = boolean(default=False)
handle_longtweets = boolean(default=True)
use_invisible_keyboard_shorcuts = boolean(default=True)
play_ready_sound = boolean(default=True)
speak_ready_msg = boolean(default=True)
log_level = string(default="error")
load_keymap = string(default="default.keymap")
donation_dialog_displayed = boolean(default=False)
check_for_updates = boolean(default=True)
[proxy]
server = string(default="")

View File

@@ -1,16 +1,18 @@
# -*- coding: utf-8 -*-
name = 'TWBlue'
snapshot = True
snapshot = False
if snapshot == False:
version = "0.80"
version = "0.86"
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 = "9.9"
version = "10.99"
update_url = 'http://twblue.es/updates/snapshots_ngen.json'
author = u"Manuel Cortéz, Bill Dengler"
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) 2015, Technow S.L. \nCopyright (C) 2015, Bill Dengler\nCopyright (C) 2013-2015, Manuel cortéz."
copyright = u"Copyright (C) 2015, Technow S.L. \nCopyright (C) 2013-2015, 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"Alba Quinteiro (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)"]
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

@@ -10,7 +10,7 @@ def convert_audioboom(url):
audio_id = url.split('.com/')[-1]
return 'https://audioboom.com/%s.mp3' % audio_id
@matches_url ('http://soundcloud.com/')
@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))

File diff suppressed because it is too large Load Diff

View File

@@ -6,13 +6,7 @@ import application
log = logging.getLogger("commandlineLauncher")
parser = argparse.ArgumentParser(description=application.name+" command line launcher")
group = parser.add_mutually_exclusive_group()
group.add_argument("-p", "--portable", help="Use " + application.name + " as a portable application.", action="store_true", default=True)
group.add_argument("-i", "--installed", help="Use " + application.name + " as an installed application. Config files will be saved in the user data directory", action="store_true")
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where " + application.name + " saves userdata.")
args = parser.parse_args()
log.debug("Starting " + application.name + " with the following arguments: installed = %s, portable = %s and directory = %s" % (args.installed, args.portable, args.directory))
if args.installed == True: paths.mode = "installed"
elif args.portable == True:
paths.mode = "portable"
if args.directory != None: paths.directory = args.directory
log.debug("Starting " + application.name + " with the following arguments: directory = %s" % (args.directory))
if args.directory != None: paths.directory = args.directory

View File

@@ -2,6 +2,7 @@
import config_utils
import paths
import logging
import platform
log = logging.getLogger("config")
@@ -10,10 +11,17 @@ MAINSPEC = "app-configuration.defaults"
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
keymap = config_utils.load_config(paths.app_path("keymaps/"+app['app-settings']['load_keymap']), paths.app_path('keymaps/base.template'))
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']))

View File

@@ -3,6 +3,9 @@ from configobj import ConfigObj, ParseError
from validate import Validator, ValidateError
import os
import string
from logging import getLogger
log = getLogger("config_utils")
class ConfigLoadError(Exception): pass
def load_config(config_path, configspec_path=None, *args, **kwargs):
@@ -14,10 +17,12 @@ def load_config(config_path, configspec_path=None, *args, **kwargs):
except ParseError:
raise ConfigLoadError("Unable to load %r" % config_path)
validator = Validator()
validated = config.validate(validator, copy=True)
validated = config.validate(validator, preserve_errors=False, copy=True)
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."
@@ -25,6 +30,7 @@ def is_blank(arg):
if c not in string.whitespace:
return False
return True
def get_keys(path):
"Gets the keys of a configobj config file."
res=[]

38
src/controller/attach.py Normal file
View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
import os
import widgetUtils
import logging
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 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 check_remove_status(self):
if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0:
self.dialog.remove.Enable(False)

View File

@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
import time
import platform
if platform.system() == "Windows":
import wx
from wxUI import buffers, dialogs, commonMessageDialogs
from wxUI import buffers, dialogs, commonMessageDialogs, menus
import user
elif platform.system() == "Linux":
from gi.repository import Gtk
@@ -42,11 +43,13 @@ class bufferController(object):
self.account = ""
self.needs_init = True
self.invisible = False # False if the buffer will be ignored on the invisible interface.
self.execution_time = 0
def clear_list(self): pass
def get_event(self, ev):
""" Catches 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"
@@ -82,7 +85,9 @@ class bufferController(object):
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
self.session.sound.play("volume_changed.ogg")
def start_stream(self):
def start_stream(self, mandatory=False):
if mandatory == True:
output.speak(_(u"Unable to update this buffer."))
pass
def get_more_items(self):
@@ -95,7 +100,9 @@ class bufferController(object):
return False
def remove_item(self, item):
f = self.buffer.list.get_selected()
self.buffer.list.remove_item(item)
self.buffer.list.select_item(f)
def bind_events(self):
pass
@@ -127,20 +134,37 @@ class bufferController(object):
def post_tweet(self, *args, **kwargs):
title = _(u"Tweet")
caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "")
tweet = messages.tweet(self.session, title, caption, "", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"])
if tweet.message.get_response() == widgetUtils.OK:
self.session.settings["mysc"]["twishort_enabled"] = tweet.message.long_tweet.GetValue()
text = tweet.message.get_text()
if len(text) > 140 and tweet.message.get("long_tweet") == True:
if tweet.image == None:
if not hasattr(tweet, "attachments"):
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
else:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
if tweet.image == None:
if not hasattr(tweet, "attachments") or len(tweet.attachments) == 0:
call_threaded(self.session.api_call, call_name="update_status", status=text)
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image)
call_threaded(self.post_with_media, text=text, attachments=tweet.attachments)
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
def post_with_media(self, text, attachments):
media_ids = []
for i in attachments:
photo = open(i["file"], "rb")
img = self.session.twitter.twitter.upload_media(media=photo)
self.session.twitter.twitter.set_description(media_id=img["media_id"], alt_text=dict(text=i["description"]))
media_ids.append(img["media_id"])
self.session.twitter.twitter.update_status(status=text, media_ids=media_ids)
def save_positions(self):
try:
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
except AttributeError:
pass
class accountPanel(bufferController):
def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name)
@@ -188,7 +212,7 @@ class accountPanel(bufferController):
class emptyPanel(bufferController):
def __init__(self, parent, name, account):
super(emptyPanel, self).__init__(parent, None, name)
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
@@ -200,7 +224,7 @@ class emptyPanel(bufferController):
self.needs_init = True
class baseBufferController(bufferController):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, compose_func="compose_tweet", *args, **kwargs):
super(baseBufferController, self).__init__(parent, function, *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
if bufferType != None:
@@ -211,7 +235,7 @@ class baseBufferController(bufferController):
self.name = name
self.type = self.buffer.type
self.session = sessionObject
self.compose_function = compose.compose_tweet
self.compose_function = getattr(compose, compose_func)
log.debug("Compose_function: %s" % (self.compose_function,))
self.account = account
self.buffer.account = account
@@ -223,28 +247,36 @@ class baseBufferController(bufferController):
return self.get_message()
def get_message(self):
return " ".join(self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"]))
tweet = self.get_right_tweet()
return " ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"]))
def get_full_tweet(self):
tweet = self.get_right_tweet()
tweetsList = []
tweet_id = tweet["id"]
uri = None
if tweet.has_key("long_uri"):
uri = tweet["long_uri"]
message = None
if tweet.has_key("message"):
message = tweet["message"]
try:
tweet = self.session.twitter.twitter.show_status(id=tweet_id)
tweet = self.session.twitter.twitter.show_status(id=tweet_id, include_ext_alt_text=True, tweet_mode="extended")
urls = utils.find_urls_in_text(tweet["full_text"])
for url in range(0, len(urls)):
try: tweet["full_text"] = tweet["full_text"].replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
except TwythonError as e:
utils.twitter_error(e)
return
if uri != None:
tweet["text"] = twishort.get_full_text(uri)
if message != None:
tweet["message"] = message
l = tweets.is_long(tweet)
while l != False:
tweetsList.append(tweet)
id = tweets.get_id(l)
try:
tweet = self.session.twitter.twitter.show_status(id=id)
tweet = self.session.twitter.twitter.show_status(id=l, include_ext_alt_text=True, tweet_mode="extended")
urls = utils.find_urls_in_text(tweet["full_text"])
for url in range(0, len(urls)):
try: tweet["full_text"] = tweet["full_text"].replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
except TwythonError as e:
utils.twitter_error(e)
return
@@ -253,16 +285,20 @@ class baseBufferController(bufferController):
tweetsList.append(tweet)
return (tweet, tweetsList)
def start_stream(self):
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
number_of_items = self.session.order_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if self.sound == None: return
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages":
self.session.sound.play(self.sound)
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
number_of_items = self.session.order_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None:
self.session.sound.play(self.sound)
return number_of_items
def get_more_items(self):
elements = []
@@ -275,7 +311,9 @@ class baseBufferController(bufferController):
except TwythonError as e:
output.speak(e.message, True)
for i in items:
if utils.is_allowed(i, self.session.settings["twitter"]["ignored_clients"]) == True:
if utils.is_allowed(i, self.session.settings["twitter"]["ignored_clients"]) == True and utils.find_item(i["id"], self.session.db[self.name]) == None:
i = self.session.check_quoted_status(i)
i = self.session.check_long_tweet(i)
elements.append(i)
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db[self.name].insert(0, i)
@@ -301,6 +339,7 @@ class baseBufferController(bufferController):
if dlg == widgetUtils.YES:
if self.name[:-9] in self.session.settings["other_buffers"]["timelines"]:
self.session.settings["other_buffers"]["timelines"].remove(self.name[:-9])
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
@@ -309,6 +348,7 @@ class baseBufferController(bufferController):
if dlg == widgetUtils.YES:
if self.name[:-9] in self.session.settings["other_buffers"]["favourites_timelines"]:
self.session.settings["other_buffers"]["favourites_timelines"].remove(self.name[:-9])
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
@@ -316,7 +356,15 @@ class baseBufferController(bufferController):
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
return False
def remove_tweet(self, id):
if type(self.session.db[self.name]) == dict: return
for i in xrange(0, len(self.session.db[self.name])):
if self.session.db[self.name][i]["id"] == id:
self.session.db[self.name].pop(i)
self.remove_item(i)
def put_items_on_list(self, number_of_items):
if number_of_items == 0 and self.session.settings["general"]["persist_size"] == 0: return
log.debug("The list contains %d items " % (self.buffer.list.get_count(),))
log.debug("Putting %d items on the list" % (number_of_items,))
if self.buffer.list.get_count() == 0:
@@ -324,13 +372,16 @@ class baseBufferController(bufferController):
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
self.buffer.list.insert_item(False, *tweet)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
elif self.buffer.list.get_count() > 0:
elif self.buffer.list.get_count() > 0 and number_of_items > 0:
if self.session.settings["general"]["reverse_timelines"] == False:
for i in self.session.db[self.name][:number_of_items]:
items = self.session.db[self.name][len(self.session.db[self.name])-number_of_items:]
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
self.buffer.list.insert_item(False, *tweet)
else:
for i in self.session.db[self.name][0:number_of_items]:
items = self.session.db[self.name][0:number_of_items]
items.reverse()
for i in items:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
self.buffer.list.insert_item(True, *tweet)
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
@@ -344,8 +395,8 @@ class baseBufferController(bufferController):
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
output.speak(" ".join(tweet[:2]))
#Improve performance on Windows
if platform.system() == "Windows":
call_threaded(utils.is_audio,item)
# if platform.system() == "Windows":
# call_threaded(utils.is_audio,item)
def bind_events(self):
log.debug("Binding events...")
@@ -356,6 +407,61 @@ class baseBufferController(bufferController):
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.retweet, self.buffer.retweet)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.direct_message, self.buffer.dm)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
# Replace for the correct way in other platforms.
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
if self.name == "sent_tweets" or self.name == "sent_direct_messages":
menu = menus.sentPanelMenu()
elif self.name == "direct_messages":
menu = menus.dmPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.direct_message, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
else:
menu = menus.basePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.retweet, menuitem=menu.retweet)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.url_, menuitem=menu.openUrl)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.audio, menuitem=menu.play)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.destroy_status, menuitem=menu.remove)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
def user_actions(self, *args, **kwargs):
pub.sendMessage("execute-action", action="follow")
def fav(self, *args, **kwargs):
pub.sendMessage("execute-action", action="add_to_favourites")
def unfav(self, *args, **kwargs):
pub.sendMessage("execute-action", action="remove_from_favourites")
def delete_item_(self, *args, **kwargs):
pub.sendMessage("execute-action", action="delete_item")
def url_(self, *args, **kwargs):
self.url()
def show_menu_by_key(self, ev):
if self.buffer.list.get_count() == 0:
return
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
def get_tweet(self):
if self.session.db[self.name][self.buffer.list.get_selected()].has_key("retweeted_status"):
@@ -373,9 +479,10 @@ class baseBufferController(bufferController):
tweet = self.get_right_tweet()
screen_name = tweet["user"]["screen_name"]
id = tweet["id"]
users = utils.get_all_mentioned(tweet, self.session.db)
message = messages.reply(self.session, _(u"Reply"), _(u"Reply to %s") % (screen_name,), "@%s " % (screen_name,), users)
users = len(utils.get_all_mentioned(tweet, self.session.db))
message = messages.reply(self.session, _(u"Reply"), _(u"Reply to %s") % (screen_name,), "", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"], users=users)
if message.message.get_response() == widgetUtils.OK:
self.session.settings["mysc"]["twishort_enabled"] = message.message.long_tweet.GetValue()
text = message.message.get_text()
if len(text) > 140 and message.message.get("long_tweet") == True:
if message.image == None:
@@ -383,9 +490,9 @@ class baseBufferController(bufferController):
else:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
if message.image == None:
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", in_reply_to_status_id=id, status=text)
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", in_reply_to_status_id=id, status=text, auto_populate_reply_metadata=message.message.mentionAll.GetValue())
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=id, status=text, media=message.file)
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=id, status=text, media=message.file, auto_populate_reply_metadata=message.message.mentionAll.GetValue())
if hasattr(message.message, "destroy"): message.message.destroy()
@_tweets_exist
@@ -402,7 +509,13 @@ class baseBufferController(bufferController):
users = utils.get_all_users(tweet, self.session.db)
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
if dm.message.get_response() == widgetUtils.OK:
call_threaded(self.session.api_call, call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
val = self.session.api_call(call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
if val != None:
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db["sent_direct_messages"].append(val)
else:
self.session.db["sent_direct_messages"].insert(0, val)
pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"])
if hasattr(dm.message, "destroy"): dm.message.destroy()
@_tweets_exist
@@ -421,20 +534,16 @@ class baseBufferController(bufferController):
self._retweet_with_comment(tweet, id)
def _retweet_with_comment(self, tweet, id, comment=''):
retweet = messages.tweet(self.session, _(u"Retweet"), _(u"Add your comment to the tweet"), u"“@%s: %s" % (tweet["user"]["screen_name"], tweet["text"]), max=116-len("@%s " % (tweet["user"]["screen_name"],)), messageType="retweet")
if tweet.has_key("full_text"):
comments = tweet["full_text"]
else:
comments = tweet["text"]
retweet = messages.tweet(self.session, _(u"Quote"), _(u"Add your comment to the tweet"), u"“@%s: %s" % (tweet["user"]["screen_name"], comments), max=116, messageType="retweet", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"])
if comment != '':
retweet.message.set_text(comment)
if retweet.message.get_response() == widgetUtils.OK:
text = retweet.message.get_text()
comments=text
if len(text+ u"“@%s: %s" % (tweet["user"]["screen_name"], tweet["text"])) < 140:
text = text+u"“@%s: %s" % (tweet["user"]["screen_name"], tweet["text"])
else:
answer = commonMessageDialogs.retweet_as_link(self.buffer)
if answer == widgetUtils.YES:
text = text+" https://twitter.com/{0}/status/{1}".format(tweet["user"]["screen_name"], id)
else:
return self._retweet_with_comment(tweet, id, comment=comments)
text = text+" https://twitter.com/{0}/status/{1}".format(tweet["user"]["screen_name"], id)
if retweet.image == None:
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id)
else:
@@ -455,11 +564,10 @@ class baseBufferController(bufferController):
self.session.sound.play("audio.ogg")
if utils.is_geocoded(tweet):
self.session.sound.play("geo.ogg")
self.session.db[str(self.name+"_pos")]=self.buffer.list.get_selected()
@_tweets_exist
def audio(self,url=''):
if hasattr(sound.URLPlayer,'stream'):
# @_tweets_exist
def audio(self, url='', *args, **kwargs):
if hasattr(sound.URLPlayer,'stream') and sound.URLPlayer.stream.is_playing == True:
return sound.URLPlayer.stop_audio(delete=True)
tweet = self.get_tweet()
if tweet == None: return
@@ -473,13 +581,13 @@ class baseBufferController(bufferController):
url=urls_list.get_string()
if hasattr(urls_list, "destroy"): urls_list.destroy()
if url != '':
try:
sound.URLPlayer.play(url, self.session.settings["sound"]["volume"])
except:
log.error("Exception while executing audio method.")
# try:
sound.URLPlayer.play(url, self.session.settings["sound"]["volume"])
# except:
# log.error("Exception while executing audio method.")
@_tweets_exist
def url(self,url='',announce=True):
# @_tweets_exist
def url(self, url='', announce=True, *args, **kwargs):
if url == '':
tweet = self.get_tweet()
urls = utils.find_urls(tweet)
@@ -533,6 +641,26 @@ class baseBufferController(bufferController):
user.profileController(session=self.session, user=dlg.get_user())
if hasattr(dlg, "destroy"): dlg.destroy()
def get_quoted_tweet(self, tweet):
# try:
quoted_tweet = self.session.twitter.twitter.show_status(id=tweet["id"])
urls = utils.find_urls_in_text(quoted_tweet["text"])
for url in range(0, len(urls)):
try: quoted_tweet["text"] = quoted_tweet["text"].replace(urls[url], quoted_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
# except TwythonError as e:
# utils.twitter_error(e)
# return
l = tweets.is_long(quoted_tweet)
id = tweets.get_id(l)
# try:
original_tweet = self.session.twitter.twitter.show_status(id=id)
urls = utils.find_urls_in_text(original_tweet["text"])
for url in range(0, len(urls)):
try: original_tweet["text"] = original_tweet["text"].replace(urls[url], original_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
class listBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, list_id=None, *args, **kwargs):
super(listBufferController, self).__init__(parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs)
@@ -540,9 +668,9 @@ class listBufferController(baseBufferController):
self.list_id = list_id
self.kwargs["list_id"] = list_id
def start_stream(self):
def start_stream(self, mandatory=False):
self.get_user_ids()
super(listBufferController, self).start_stream()
super(listBufferController, self).start_stream(mandatory)
def get_user_ids(self):
self.users = []
@@ -558,6 +686,7 @@ class listBufferController(baseBufferController):
if dlg == widgetUtils.YES:
if self.name[:-5] in self.session.settings["other_buffers"]["lists"]:
self.session.settings["other_buffers"]["lists"].remove(self.name[:-5])
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
@@ -597,9 +726,26 @@ class eventsBufferController(bufferController):
if dlg == widgetUtils.YES:
self.buffer.list.clear()
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
menu = menus.eventsPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.destroy_status, menuitem=menu.remove)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
class peopleBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel")
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.compose_function = compose.compose_followers_list
log.debug("Compose_function: %s" % (self.compose_function,))
@@ -607,7 +753,27 @@ class peopleBufferController(baseBufferController):
self.url = self.interact
def remove_buffer(self):
return False
if "-followers" in self.name:
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
if self.name[:-10] in self.session.settings["other_buffers"]["followers_timelines"]:
self.session.settings["other_buffers"]["followers_timelines"].remove(self.name[:-10])
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
elif "-friends" in self.name:
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
if self.name[:-8] in self.session.settings["other_buffers"]["friends_timelines"]:
self.session.settings["other_buffers"]["friends_timelines"].remove(self.name[:-8])
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
else:
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
return False
def onFocus(self, ev):
pass
@@ -629,17 +795,20 @@ class peopleBufferController(baseBufferController):
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
if hasattr(message.message, "destroy"): message.message.destroy()
def start_stream(self):
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.get_cursored_stream(self.name, self.function, *self.args, **self.kwargs)
# self.session.order_cursored_buffer(self.name, self.session.db[self.name])
# log.debug("Number of items retrieved: %d" % (val,))
self.put_items_on_list(val)
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.get_cursored_stream(self.name, self.function, *self.args, **self.kwargs)
self.put_items_on_list(val)
return val
def get_more_items(self):
try:
items = self.session.get_more_items(self.function, users=True, name=self.name, count=self.session.settings["general"]["max_tweets_per_call"], cursor=self.session.db[self.name]["cursor"])
items = self.session.get_more_items(self.function, users=True, name=self.name, count=self.session.settings["general"]["max_tweets_per_call"], cursor=self.session.db[self.name]["cursor"], *self.args, **self.kwargs)
except TwythonError as e:
output.speak(e.message, True)
return
@@ -675,7 +844,7 @@ class peopleBufferController(baseBufferController):
# self.buffer.set_list_position()
elif self.buffer.list.get_count() > 0:
if self.session.settings["general"]["reverse_timelines"] == False:
for i in self.session.db[self.name]["items"][:number_of_items]:
for i in self.session.db[self.name]["items"][len(self.session.db[self.name]["items"])-number_of_items:]:
tweet = self.compose_function(i, self.session.db)
self.buffer.list.insert_item(False, *tweet)
else:
@@ -707,19 +876,40 @@ class peopleBufferController(baseBufferController):
def interact(self):
user.profileController(self.session, user=self.get_right_tweet()["screen_name"])
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.peoplePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.direct_message, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.details, menuitem=menu.details)
# widgetUtils.connect_event(menu, widgetUtils.MENU, self.lists, menuitem=menu.lists)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def details(self, *args, **kwargs):
pub.sendMessage("execute-action", action="user_details")
class searchBufferController(baseBufferController):
def start_stream(self):
log.debug("Starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
log.debug("Function: %s" % (self.function,))
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
log.debug("Function: %s" % (self.function,))
# try:
val = self.session.search(self.name, *self.args, **self.kwargs)
val = self.session.search(self.name, *self.args, **self.kwargs)
# except:
# return None
num = self.session.order_buffer(self.name, val)
self.put_items_on_list(num)
if num > 0:
self.session.sound.play("search_updated.ogg")
num = self.session.order_buffer(self.name, val)
self.put_items_on_list(num)
if num > 0:
self.session.sound.play("search_updated.ogg")
return num
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
@@ -727,6 +917,7 @@ class searchBufferController(baseBufferController):
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
self.timer.cancel()
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
@@ -742,19 +933,24 @@ class searchPeopleBufferController(peopleBufferController):
self.kwargs = kwargs
self.function = function
def start_stream(self):
log.debug("starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
log.debug("Function: %s" % (self.function,))
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
self.execution_time = current_time
log.debug("starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
log.debug("Function: %s" % (self.function,))
# try:
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
# except:
# return
number_of_items = self.session.order_cursored_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
number_of_items = self.session.order_cursored_buffer(self.name, val)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
return number_of_items
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
@@ -762,6 +958,7 @@ class searchPeopleBufferController(peopleBufferController):
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
self.timer.cancel()
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
@@ -783,17 +980,22 @@ class trendsBufferController(bufferController):
self.buffer.name = name
self.compose_function = self.compose_function_
self.get_formatted_message = self.get_message
self.reply = self.search_topic
def start_stream(self):
try:
data = self.session.call_paged("get_place_trends", id=self.trendsFor)
except:
return
if not hasattr(self, "name_"):
self.name_ = data[0]["locations"][0]["name"]
self.trends = data[0]["trends"]
self.put_items_on_the_list()
self.session.sound.play(self.sound)
def start_stream(self, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
self.execution_time = current_time
try:
data = self.session.call_paged("get_place_trends", id=self.trendsFor)
except:
return
if not hasattr(self, "name_"):
self.name_ = data[0]["locations"][0]["name"]
self.trends = data[0]["trends"]
self.put_items_on_the_list()
self.session.sound.play(self.sound)
def put_items_on_the_list(self):
selected_item = self.buffer.list.get_selected()
@@ -809,10 +1011,11 @@ class trendsBufferController(bufferController):
def bind_events(self):
log.debug("Binding events...")
self.buffer.list.list.Bind(wx.EVT_CHAR_HOOK, self.get_event)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_tweet, self.buffer.tweet)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.retweet, self.buffer.retweet)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.direct_message, self.buffer.dm)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.tweet_about_this_trend, self.buffer.tweetTrendBtn)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_tweet, self.buffer.tweet)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.search_topic, self.buffer.search_topic)
def get_message(self):
return self.compose_function(self.trends[self.buffer.list.get_selected()])[0]
@@ -823,46 +1026,101 @@ class trendsBufferController(bufferController):
if self.name[:-3] in self.session.settings["other_buffers"]["trending_topic_buffers"]:
self.session.settings["other_buffers"]["trending_topic_buffers"].remove(self.name[:-3])
self.timer.cancel()
self.session.db.pop(self.name)
return True
elif dlg == widgetUtils.NO:
return False
def interact(self, *args, **kwargs):
self.searchfunction(value=self.get_message())
def url(self, *args, **kwargs):
self.tweet_about_this_trend()
def search_topic(self, *args, **kwargs):
topic = self.trends[self.buffer.list.get_selected()]["name"]
pub.sendMessage("search", term=topic)
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.trendsPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.search_topic, menuitem=menu.search_topic)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.tweet_about_this_trend, menuitem=menu.tweetThisTrend)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
def tweet_about_this_trend(self, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
title = _(u"Tweet")
caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, self.get_message()+ " ", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"])
tweet.message.set_cursor_at_end()
if tweet.message.get_response() == widgetUtils.OK:
self.session.settings["mysc"]["twishort_enabled"] = tweet.message.long_tweet.GetValue()
text = tweet.message.get_text()
if len(text) > 140 and tweet.message.get("long_tweet") == True:
if tweet.image == None:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
else:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
if tweet.image == None:
call_threaded(self.session.api_call, call_name="update_status", status=text)
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image)
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
def show_menu_by_key(self, ev):
if self.buffer.list.get_count() == 0:
return
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
class conversationBufferController(searchBufferController):
def start_stream(self, start=False):
if start == True:
self.statuses = []
self.ids = []
self.statuses.append(self.tweet)
self.ids.append(self.tweet["id"])
tweet = self.tweet
while tweet["in_reply_to_status_id"] != None:
tweet = self.session.twitter.twitter.show_status(id=tweet["in_reply_to_status_id"])
self.statuses.insert(0, tweet)
self.ids.append(tweet["id"])
if tweet["in_reply_to_status_id"] == None:
self.kwargs["since_id"] = tweet["id"]
self.ids.append(tweet["id"])
val2 = self.session.search(self.name, *self.args, **self.kwargs)
for i in val2:
if i["in_reply_to_status_id"] in self.ids:
self.statuses.append(i)
self.ids.append(i["id"])
tweet = i
number_of_items = self.session.order_buffer(self.name, self.statuses)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
def start_stream(self, start=False, mandatory=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory == True:
self.execution_time = current_time
if start == True:
self.statuses = []
self.ids = []
self.statuses.append(self.tweet)
self.ids.append(self.tweet["id"])
tweet = self.tweet
while tweet["in_reply_to_status_id"] != None:
try:
tweet = self.session.twitter.twitter.show_status(id=tweet["in_reply_to_status_id"], tweet_mode="extended")
except TwythonError as err:
break
self.statuses.insert(0, tweet)
self.ids.append(tweet["id"])
if tweet["in_reply_to_status_id"] == None:
self.kwargs["since_id"] = tweet["id"]
self.ids.append(tweet["id"])
val2 = self.session.search(self.name, tweet_mode="extended", *self.args, **self.kwargs)
for i in val2:
if i["in_reply_to_status_id"] in self.ids:
self.statuses.append(i)
self.ids.append(i["id"])
tweet = i
number_of_items = self.session.order_buffer(self.name, self.statuses)
log.debug("Number of items retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0:
self.session.sound.play("search_updated.ogg")
return number_of_items
class pocketBufferController(baseBufferController):
def __init__(self, parent, name, sessionObject, account, sound=None, function=None, bufferType=None, *args, **kwargs):
super(pocketBufferController, self).__init__(parent, name, sessionObject, account, sound, function, bufferType, *args, **kwargs)
self.type = "pocket"
def start_stream(self):
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
self.timer.cancel()
return True
elif dlg == widgetUtils.NO:
return False

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from wxUI.dialogs import filters
class filterController(object):
def __init__(self):
self.dialog = filters.filterDialog()
self.dialog.get_response()

View File

@@ -15,19 +15,30 @@ class listsController(object):
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_user_lists(self, user):
self.lists = self.session.twitter.twitter.show_lists(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 public == True:
if p == True:
mode = "public"
else:
mode = "private"
@@ -73,4 +84,23 @@ class listsController(object):
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["slug"])
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.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 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.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))

View File

@@ -90,12 +90,14 @@ class Controller(object):
""" Gets the last valid buffer for an account.
account str: A twitter username.
The last valid buffer is the last buffer that contains a session object assigned."""
# results = self.get_buffers_for_account(account)
results = self.get_buffers_for_account(account)
return self.view.search(results[-1].name, results[-1].account)
def get_buffers_for_account(self, account):
results = []
[results.append(i) for i in self.buffers if i.account == account and i.invisible == True]
buffers = self.view.get_buffers()
[results.append(self.search_buffer(i.name, i.account)) for i in buffers if i.account == account and (i.type != "account")]
return results
def bind_stream_events(self):
@@ -116,6 +118,8 @@ class Controller(object):
pub.subscribe(self.manage_unblocked_user, "unblocked-user")
pub.subscribe(self.manage_item_in_timeline, "item-in-timeline")
pub.subscribe(self.manage_item_in_list, "item-in-list")
pub.subscribe(self.restart_streams_, "restart_streams")
pub.subscribe(self.on_tweet_deleted, "tweet-deleted")
widgetUtils.connect_event(self.view, widgetUtils.CLOSE_EVENT, self.exit_)
def bind_other_events(self):
@@ -126,7 +130,8 @@ class Controller(object):
pub.subscribe(self.manage_stream_errors, "stream-error")
pub.subscribe(self.create_new_buffer, "create-new-buffer")
pub.subscribe(self.restart_streams, "restart-streams")
pub.subscribe(self.execute_action, "execute-action")
pub.subscribe(self.search_topic, "search")
if system == "Windows":
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
@@ -154,7 +159,9 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates)
@@ -168,10 +175,16 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_timeline, self.view.timeline)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_favs_timeline, self.view.favs)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_conversation, menuitem=self.view.view_conversation)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.seekLeft, menuitem=self.view.seekLeft)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.seekRight, menuitem=self.view.seekRight)
if widgetUtils.toolkit == "wx":
widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_changelog, self.view.changelog)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
def set_systray_icon(self):
self.systrayIcon = sysTrayIcon.SysTrayIcon()
@@ -181,6 +194,7 @@ class Controller(object):
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.update_profile, menuitem=self.systrayIcon.update_profile)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.show_hide, menuitem=self.systrayIcon.show_hide)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.check_for_updates, menuitem=self.systrayIcon.check_for_updates)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.view_documentation, menuitem=self.systrayIcon.doc)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.MENU, self.exit, menuitem=self.systrayIcon.exit)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.TASKBAR_LEFT_CLICK, self.taskbar_left_click)
widgetUtils.connect_event(self.systrayIcon, widgetUtils.TASKBAR_RIGHT_CLICK, self.taskbar_right_click)
@@ -233,17 +247,19 @@ class Controller(object):
self.create_ignored_session_buffer(session_.sessions[i])
continue
self.create_buffers(session_.sessions[i])
self.set_buffer_positions(session_.sessions[i])
# Connection checker executed each minute.
self.checker_function = RepeatingTimer(60, self.check_connection)
self.checker_function.start()
self.save_db = RepeatingTimer(300, self.save_data_in_db)
self.save_db.start()
def start(self):
""" Starts all buffer objects. Loads their items."""
for i in session_.sessions:
if session_.sessions[i].is_logged == False: continue
self.start_buffers(session_.sessions[i])
self.set_buffer_positions(session_.sessions[i])
if config.app["app-settings"]["play_ready_sound"] == True:
session_.sessions[session_.sessions.keys()[0]].sound.play("ready.ogg")
if config.app["app-settings"]["speak_ready_msg"] == True:
@@ -261,6 +277,7 @@ class Controller(object):
for i in session_.sessions:
if session_.sessions[i].session_id == session_id: session = session_.sessions[i]
session.login()
session.db = dict()
self.create_buffers(session, False)
self.start_buffers(session)
@@ -276,30 +293,30 @@ class Controller(object):
self.view.add_buffer(account.buffer , name=session.db["user_name"])
for i in session.settings['general']['buffer_order']:
if i == 'home':
home = buffersController.baseBufferController(self.view.nb, "get_home_timeline", "home_timeline", session, session.db["user_name"])
home = buffersController.baseBufferController(self.view.nb, "get_home_timeline", "home_timeline", session, session.db["user_name"], tweet_mode="extended")
self.buffers.append(home)
self.view.insert_buffer(home.buffer, name=_(u"Home"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'mentions':
mentions = buffersController.baseBufferController(self.view.nb, "get_mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg")
mentions = buffersController.baseBufferController(self.view.nb, "get_mentions_timeline", "mentions", session, session.db["user_name"], sound="mention_received.ogg", tweet_mode="extended")
self.buffers.append(mentions)
self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'dm':
dm = buffersController.baseBufferController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", sound="dm_received.ogg")
dm = buffersController.baseBufferController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_dm", sound="dm_received.ogg", full_text=True)
self.buffers.append(dm)
self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_dm':
sent_dm = buffersController.baseBufferController(self.view.nb, "get_sent_messages", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel")
sent_dm = buffersController.baseBufferController(self.view.nb, "get_sent_messages", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", compose_func="compose_dm", full_text=True)
self.buffers.append(sent_dm)
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_tweets':
sent_tweets = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "sent_tweets", session, session.db["user_name"], bufferType="dmPanel", screen_name=session.db["user_name"])
sent_tweets = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "sent_tweets", session, session.db["user_name"], bufferType="dmPanel", screen_name=session.db["user_name"], tweet_mode="extended")
self.buffers.append(sent_tweets)
self.view.insert_buffer(sent_tweets.buffer, name=_(u"Sent tweets"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'favorites':
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"])
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"], tweet_mode="extended")
self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Favourites"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'followers':
followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", session, session.db["user_name"], screen_name=session.db["user_name"])
self.buffers.append(followers)
@@ -324,31 +341,49 @@ class Controller(object):
self.buffers.append(timelines)
self.view.insert_buffer(timelines.buffer , name=_(u"Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["timelines"]:
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], bufferType=None, screen_name=i)
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (i,), session, session.db["user_name"], bufferType=None, screen_name=i, tweet_mode="extended")
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"]))
favs_timelines = buffersController.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"])
self.buffers.append(favs_timelines)
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Favourites timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Likes timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["favourites_timelines"]:
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, screen_name=i)
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, screen_name=i, tweet_mode="extended")
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Favourites timeline for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
followers_timelines = buffersController.emptyPanel(self.view.nb, "followers_timelines", session.db["user_name"])
self.buffers.append(followers_timelines)
self.view.insert_buffer(followers_timelines.buffer , name=_(u"Followers' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["followers_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (i,), session, session.db["user_name"], screen_name=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Followers for {}").format(i,), pos=self.view.search("followers_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
friends_timelines = buffersController.emptyPanel(self.view.nb, "friends_timelines", session.db["user_name"])
self.buffers.append(friends_timelines)
self.view.insert_buffer(friends_timelines.buffer , name=_(u"Friends' Timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["friends_timelines"]:
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (i,), session, session.db["user_name"], screen_name=i)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Friends for {}").format(i,), pos=self.view.search("friends_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
lists = buffersController.emptyPanel(self.view.nb, "lists", session.db["user_name"])
self.buffers.append(lists)
self.view.insert_buffer(lists.buffer , name=_(u"Lists"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["lists"]:
tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, list_id=utils.find_list(i, session.db["lists"]))
tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", "%s-list" % (i,), session, session.db["user_name"], bufferType=None, list_id=utils.find_list(i, session.db["lists"]), tweet_mode="extended")
session.lists.append(tl)
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(i), pos=self.view.search("timelines", session.db["user_name"]))
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(i), pos=self.view.search("lists", session.db["user_name"]))
searches = buffersController.emptyPanel(self.view.nb, "searches", session.db["user_name"])
self.buffers.append(searches)
self.view.insert_buffer(searches.buffer , name=_(u"Searches"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["tweet_searches"]:
tl = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", q=i, count=session.settings["general"]["max_tweets_per_call"])
tl = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (i,), session, session.db["user_name"], bufferType="searchPanel", q=i, count=session.settings["general"]["max_tweets_per_call"], tweet_mode="extended")
self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Search for {}").format(i), pos=self.view.search("searches", session.db["user_name"]))
tl.timer = RepeatingTimer(180, tl.start_stream)
@@ -358,41 +393,28 @@ class Controller(object):
buffer.start_stream()
buffer.searchfunction = self.search
self.buffers.append(buffer)
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (buffer.name_), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
buffer.timer = RepeatingTimer(300, buffer.start_stream)
buffer.timer.start()
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (buffer.name_), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
def set_buffer_positions(self,session):
"Sets positions for buffers if values exist in the database."
for i in self.buffers:
if str(i.name+"_pos") in session.db and hasattr(i.buffer,'list'):
if i.account == session.db["user_name"] and session.db.has_key(i.name+"_pos") and hasattr(i.buffer,'list'):
i.buffer.list.select_item(session.db[str(i.name+"_pos")])
def logout_account(self, session_id):
for i in session_.sessions:
if session_.sessions[i].session_id == session_id: session = session_.sessions[i]
user = session.db["user_name"]
self.destroy_buffer("home_timeline", user)
self.destroy_buffer("mentions", user)
self.destroy_buffer("direct_messages", user)
self.destroy_buffer("sent_direct_messages", user)
self.destroy_buffer("sent_tweets", user)
self.destroy_buffer("favourites", user)
self.destroy_buffer("followers", user)
self.destroy_buffer("friends", user)
self.destroy_buffer("blocked", user)
self.destroy_buffer("muted", user)
self.destroy_buffer("events", user)
self.destroy_buffer("timelines", user)
for i in session.settings["other_buffers"]["timelines"]:
self.destroy_buffer("%s-timeline" % (i,), user)
self.destroy_buffer("favs_timelines", user)
self.destroy_buffer("searches", user)
for i in session.settings["other_buffers"]["tweet_searches"]:
self.destroy_buffer("%s-searchterm" % (i,), user)
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
self.destroy_buffer("%s_tt" % (i,), user)
delete_buffers = []
for i in self.buffers:
if i.account == user and i.name != user:
delete_buffers.append(i.name)
for i in delete_buffers:
self.destroy_buffer(i, user)
session.db = None
def destroy_buffer(self, buffer_name, account):
buffer = self.search_buffer(buffer_name, account)
if buffer == None: return
@@ -402,7 +424,10 @@ class Controller(object):
self.buffers.remove(buffer)
del buffer
def search(self, value="", *args, **kwargs):
def search_topic(self, term):
self.search(value=term)
def search(self, event=None, value="", *args, **kwargs):
""" Searches words or users in twitter. This creates a new buffer containing the search results."""
log.debug("Creating a new search...")
dlg = dialogs.search.searchDialog(value)
@@ -412,13 +437,14 @@ class Controller(object):
if dlg.get("tweets") == True:
if term not in buffer.session.settings["other_buffers"]["tweet_searches"]:
buffer.session.settings["other_buffers"]["tweet_searches"].append(term)
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", q=term, count=buffer.session.settings["general"]["max_tweets_per_call"])
args = {"lang": dlg.get_language(), "result_type": dlg.get_result_type()}
search = buffersController.searchBufferController(self.view.nb, "search", "%s-searchterm" % (term,), buffer.session, buffer.session.db["user_name"], bufferType="searchPanel", q=term, count=buffer.session.settings["general"]["max_tweets_per_call"], **args)
else:
log.error("A buffer for the %s search term is already created. You can't create a duplicate buffer." % (term,))
return
elif dlg.get("users") == True:
search = buffersController.searchPeopleBufferController(self.view.nb, "search_users", "%s-searchUser" % (term,), buffer.session, buffer.session.db["user_name"], bufferType=None, q=term)
search.start_stream()
search.start_stream(mandatory=True)
pos=self.view.search("searches", buffer.session.db["user_name"])
self.insert_buffer(search, pos)
self.view.insert_buffer(search.buffer, name=_(u"Search for {}").format(term), pos=pos)
@@ -454,6 +480,18 @@ class Controller(object):
output.speak(_(u"{0} not found.").format(string,), True)
page.buffer.list.select_item(start)
def seekLeft(self, *args, **kwargs):
try:
sound.URLPlayer.seek(-5)
except:
output.speak("Unable to seek.",True)
def seekRight(self, *args, **kwargs):
try:
sound.URLPlayer.seek(5)
except:
output.speak("Unable to seek.",True)
def edit_keystrokes(self, *args, **kwargs):
editor = keystrokeEditor.KeystrokeEditor()
if editor.changed == True:
@@ -476,8 +514,20 @@ class Controller(object):
buffer = self.get_best_buffer()
SoundsTutorial.soundsTutorial(buffer.session)
def view_user_lists(self, users):
pass
def view_user_lists(self, *args, **kwargs):
buff = self.get_best_buffer()
if not hasattr(buff, "get_right_tweet"): return
tweet = buff.get_right_tweet()
if buff.type != "people":
users = utils.get_all_users(tweet, buff.session.db)
else:
users = [tweet["screen_name"]]
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
if dlg.get_response() == widgetUtils.OK:
user = dlg.get_user()
else:
return
l = listsController.listsController(buff.session, user=user)
def add_to_list(self, *args, **kwargs):
buff = self.get_best_buffer()
@@ -506,8 +556,32 @@ class Controller(object):
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
def remove_from_list(self, user):
pass
def remove_from_list(self, *args, **kwargs):
buff = self.get_best_buffer()
if not hasattr(buff, "get_right_tweet"): return
tweet = buff.get_right_tweet()
if buff.type != "people":
users = utils.get_all_users(tweet, buff.session.db)
else:
users = [tweet["screen_name"]]
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
if dlg.get_response() == widgetUtils.OK:
user = dlg.get_user()
else:
return
dlg = dialogs.lists.removeUserListDialog()
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
if dlg.get_response() == widgetUtils.OK:
try:
list = buff.session.twitter.twitter.delete_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()]["id"], buff.session.db["lists"])
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()]["name"].lower()), buff.session.db["user_name"])
if listBuffer != None: listBuffer.get_user_ids()
buff.session.db["lists"].pop(older_list)
buff.session.db["lists"].append(list)
if listBuffer != None: pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
def list_manager(self, *args, **kwargs):
s = self.get_best_buffer().session
@@ -559,6 +633,7 @@ class Controller(object):
self.exit_()
def exit_(self, *args, **kwargs):
for i in self.buffers: i.save_positions()
log.debug("Exiting...")
log.debug("Saving global configuration...")
config.app.write()
@@ -696,6 +771,9 @@ class Controller(object):
msg = messages.viewTweet(tweet, tweetsList)
elif buffer.type == "account" or buffer.type == "empty":
return
elif buffer.name == "sent_tweets":
tweet, tweetsList = buffer.get_full_tweet()
msg = messages.viewTweet(tweet, tweetsList)
else:
non_tweet = buffer.get_formatted_message()
msg = messages.viewTweet(non_tweet, [], False)
@@ -713,8 +791,7 @@ class Controller(object):
users = [tweet["screen_name"]]
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
if dlg.get_response() == widgetUtils.OK:
buffer = self.get_best_buffer()
usr = utils.if_user_exists(buffer.session.twitter.twitter, dlg.get_user())
usr = utils.if_user_exists(buff.session.twitter.twitter, dlg.get_user())
if usr != None:
if usr["protected"] == True:
if usr["following"] == False:
@@ -722,33 +799,74 @@ class Controller(object):
return
answer = commonMessageDialogs.protected_user()
if answer == widgetUtils.NO: return
if dlg.get_action() == "tweets":
if dlg.get_user() in buffer.session.settings["other_buffers"]["timelines"]:
tl_type = dlg.get_action()
if tl_type == "tweets":
if usr["statuses_count"] == 0:
commonMessageDialogs.no_tweets()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (dlg.get_user(),), buffer.session, buffer.session.db["user_name"], bufferType=None, screen_name=dlg.get_user())
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], bufferType=None, screen_name=dlg.get_user())
tl.start_stream()
pos=self.view.search("timelines", buffer.session.db["user_name"])
self.insert_buffer(tl, pos)
pos=self.view.search("timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl)
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(dlg.get_user()), pos=pos)
buffer.session.settings["other_buffers"]["timelines"].append(dlg.get_user())
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buffer.session)
buffer.session.sound.play("create_timeline.ogg")
else:
if dlg.get_user() in buffer.session.settings["other_buffers"]["favourites_timelines"]:
buff.session.settings["other_buffers"]["timelines"].append(dlg.get_user())
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "favourites":
if usr["favourites_count"] == 0:
commonMessageDialogs.no_favs()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["favourites_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (dlg.get_user(),), buffer.session, buffer.session.db["user_name"], bufferType=None, screen_name=dlg.get_user())
pos=self.view.search("favs_timelines", buffer.session.db["user_name"])
self.insert_buffer(tl, pos)
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], bufferType=None, screen_name=dlg.get_user())
pos=self.view.search("favs_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Favourites timeline for {}").format(dlg.get_user()), pos=pos)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Likes for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buffer.session.settings["other_buffers"]["favourites_timelines"].append(dlg.get_user())
buffer.session.sound.play("create_timeline.ogg")
buff.session.settings["other_buffers"]["favourites_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "followers":
if usr["followers_count"] == 0:
commonMessageDialogs.no_followers()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["followers_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "%s-followers" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], screen_name=dlg.get_user())
pos=self.view.search("followers_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Followers for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buff.session.settings["other_buffers"]["followers_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
elif tl_type == "friends":
if usr["friends_count"] == 0:
commonMessageDialogs.no_friends()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["friends_timelines"]:
commonMessageDialogs.timeline_exist()
return
tl = buffersController.peopleBufferController(self.view.nb, "get_friends_list", "%s-friends" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], screen_name=dlg.get_user())
pos=self.view.search("friends_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos+1)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Friends for {}").format(dlg.get_user()), pos=pos)
tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start()
buff.session.settings["other_buffers"]["friends_timelines"].append(dlg.get_user())
buff.session.sound.play("create_timeline.ogg")
else:
commonMessageDialogs.user_not_exist()
@@ -823,7 +941,7 @@ class Controller(object):
x = tweet["coordinates"]["coordinates"][0]
y = tweet["coordinates"]["coordinates"][1]
address = geocoder.reverse_geocode(y, x)
dlg = messages.viewTweet(address[0].__str__(), False)
dlg = commonMessageDialogs.view_geodata(address[0].__str__())
else:
output.speak(_(u"There are no coordinates in this tweet"))
except GeocoderError:
@@ -920,13 +1038,11 @@ class Controller(object):
def left(self, *args, **kwargs):
buff = self.view.get_current_buffer_pos()
print buff
buffer = self.get_current_buffer()
if not hasattr(buffer.buffer, "list"):
output.speak(_(u"No session is currently in focus. Focus a session with the next or previous session shortcut."), True)
return
if buff == self.get_first_buffer(buffer.account) or buff == 0:
print "This is the last buffer"
self.view.change_buffer(self.get_last_buffer(buffer.account))
else:
self.view.change_buffer(buff-1)
@@ -1061,6 +1177,8 @@ class Controller(object):
return keymap
def register_invisible_keyboard_shorcuts(self, keymap):
if config.changed_keymap:
commonMessageDialogs.changed_keymap()
self.keyboard_handler = WXKeyboardHandler(self.view)
self.keyboard_handler.register_keys(keymap)
@@ -1177,7 +1295,7 @@ class Controller(object):
buffer = self.search_buffer("%s-timeline" % (who,), user)
if buffer == None: return
play_sound = "tweet_timeline.ogg"
if "%s-timeline" % (who,) not in buffer.session.settings["other_buffers"]["muted_buffers"]:
if "%s-timeline" % (who,) not in buffer.session.settings["other_buffers"]["muted_buffers"] and buffer.session.settings["sound"]["session_mute"] == False:
self.notify(buffer.session, play_sound=play_sound)
output.speak(_(u"One tweet from %s") % (data["user"]["name"]))
buffer.add_new_item(data)
@@ -1185,8 +1303,8 @@ class Controller(object):
def manage_item_in_list(self, data, user, where):
buffer = self.search_buffer("%s" % (where,), user)
if buffer == None: return
play_sound = "tweet_timeline.ogg"
if "%s" % (where,) not in buffer.session.settings["other_buffers"]["muted_buffers"]:
play_sound = "list_tweet.ogg"
if "%s" % (where,) not in buffer.session.settings["other_buffers"]["muted_buffers"] and buffer.session.settings["sound"]["session_mute"] == False:
self.notify(buffer.session, play_sound=play_sound)
output.speak(_(u"One tweet from %s") % (data["user"]["name"]))
buffer.add_new_item(data)
@@ -1199,10 +1317,26 @@ class Controller(object):
log.debug("Starting the streaming endpoint")
session.start_streaming()
def set_positions(self):
for i in session_.sessions:
self.set_buffer_positions(i)
def manage_stream_errors(self, session):
log.error("An error ocurred with the stream for the %s session. It will be destroyed" % (session,))
log.error(" Restarting %s session streams. It will be destroyed" % (session,))
s = session_.sessions[session]
s.listen_stream_error()
if hasattr(s, "main_stream"):
s.main_stream.disconnect()
log.error("main stream disconnected")
del s.main_stream
if hasattr(s, "timelinesStream"):
s.timelinesStream.disconnect()
del s.timelinesStream
s.counter = 0
# s.reconnection_function_active = False
# for i in self.buffers:
# if i.invisible == True and i.session.session_id == s.session_id and i.type != "people":
# i.start_stream()
# s.listen_stream_error()
def check_connection(self):
for i in session_.sessions:
@@ -1215,7 +1349,7 @@ class Controller(object):
if buffer == "favourites":
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"])
self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Favourites"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
favourites.start_stream()
if buffer == "followers":
followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
@@ -1249,7 +1383,8 @@ class Controller(object):
return
tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]))
buff.session.lists.append(tl)
self.buffers.append(tl)
pos=self.view.search("lists", buff.session.db["user_name"])
self.insert_buffer(tl, pos)
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(create), pos=self.view.search("lists", buff.session.db["user_name"]))
tl.start_stream()
buff.session.settings["other_buffers"]["lists"].append(create)
@@ -1333,29 +1468,50 @@ class Controller(object):
webbrowser.open("manual.html")
os.chdir("../../")
def view_changelog(self, *args, **kwargs):
os.chdir("documentation")
webbrowser.open("changelog.html")
os.chdir("../")
def insert_buffer(self, buffer, position):
# print ref_buf.name, ref_buf.account
# if ref_buf.account != buffer.account or ref_buf.type == "account" or type(ref_buf) == buffers.emptyPanel:
buffers = self.get_buffers_for_account(buffer.account)
# ref_buf = self.buffers[position+1]
empty = True
for i in buffers[position+1:]:
if i.type == "account" or i.invisible == False:
empty = True
else:
empty = False
if empty == True:
self.buffers.append(buffer)
print "account"
else:
self.buffers.insert(position+1, buffer)
self.buffers.insert(position, buffer)
def copy_to_clipboard(self, *args, **kwargs):
output.copy(self.get_current_buffer().get_message())
output.speak(_(u"Copied"))
def repeat_item(self, *args, **kwargs):
output.speak(self.get_current_buffer().get_message())
def execute_action(self, action):
if hasattr(self, action):
getattr(self, action)()
def restart_streams_(self, session):
for i in self.buffers:
print i.name, i.account
if i.session != None and i.session.session_id == session:
i.start_stream()
def __del__(self):
config.app.write()
def change_buffer(self, bufferPosition):
buff = self.buffers[bufferPosition]
newPos = self.view.search(buff.name, buff.account)
self.view.change_buffer(newPos)
def update_buffer(self, *args, **kwargs):
bf = self.get_current_buffer()
if not hasattr(bf, "start_stream"):
output.speak(_(u"Unable to update this buffer."))
return
else:
output.speak(_(u"Updating buffer..."))
n = bf.start_stream(mandatory=True)
if n != None:
output.speak(_(u"{0} items retrieved").format(n,))
def on_tweet_deleted(self, data):
id = data["delete"]["status"]["id"]
for i in self.buffers:
if hasattr(i, "remove_tweet") and hasattr(i, "name"):
i.remove_tweet(id)
def save_data_in_db(self):
for i in session_.sessions:
session_.sessions[i].shelve()

View File

@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
import re
import platform
import attach
system = platform.system()
import widgetUtils
import output
@@ -25,20 +27,24 @@ class basicTweet(object):
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
# if system == "Windows":
# if messageType != "dm":
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
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)
self.attachments = []
def translate(self, event=None):
dlg = translator.gui.translateDialog()
if dlg.get_response() == widgetUtils.OK:
text_to_translate = self.message.get_text().encode("utf-8")
text_to_translate = self.message.get_text()
source = [x[0] for x in translator.translator.available_languages()][dlg.get("source_lang")]
dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
msg = translator.translator.translate(text_to_translate, source, dest)
msg = translator.translator.translate(text=text_to_translate, source=source, target=dest)
self.message.set_text(msg)
self.text_processor()
self.message.text_focus()
output.speak(_(u"Translated"))
else:
@@ -52,6 +58,7 @@ class basicTweet(object):
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()
@@ -59,6 +66,7 @@ class basicTweet(object):
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):
@@ -69,6 +77,7 @@ class basicTweet(object):
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()
@@ -76,6 +85,7 @@ class basicTweet(object):
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):
@@ -97,59 +107,65 @@ class basicTweet(object):
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():
def completed_callback(dlg):
url = dlg.uploaderFunction.get_url()
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
dlg.uploaderDialog.destroy()
if url != 0:
self.message.set_text(self.message.get_text()+url+" #audio")
self.text_processor()
else:
output.speak(_(u"Unable to upload the audio"))
dlg.cleanup()
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
self.message.text_focus()
class tweet(basicTweet):
def __init__(self, session, title, caption, text, messageType="tweet", max=140):
def __init__(self, session, title, caption, text, twishort_enabled, messageType="tweet", max=140):
super(tweet, self).__init__(session, title, caption, text, messageType, max)
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)
if twishort_enabled == False:
try: self.message.long_tweet.SetValue(False)
except AttributeError: pass
self.text_processor()
def upload_image(self, *args, **kwargs):
if self.message.get("upload_image") == _(u"Discard image"):
del self.image
self.image = None
output.speak(_(u"Discarded"))
self.message.set("upload_image", _(u"Upload a picture"))
else:
self.image = self.message.get_image()
if self.image != None:
self.message.set("upload_image", _(u"Discard image"))
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()
class reply(tweet):
def __init__(self, session, title, caption, text, users=None):
super(reply, self).__init__(session, title, caption, text, messageType="reply")
self.users = users
if self.users != None and len(self.users) > 1:
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.BUTTON_PRESSED, self.mention_all)
def __init__(self, session, title, caption, text, twishort_enabled, users=None):
super(reply, self).__init__(session, title, caption, text, twishort_enabled, messageType="reply")
self.message.mentionAll.SetValue(True)
if users > 1:
# widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all)
self.message.enable_button("mentionAll")
self.message.mentionAll.SetValue(False)
self.message.set_cursor_at_end()
self.text_processor()
def mention_all(self, *args, **kwargs):
self.message.set_text(self.message.get_text()+self.users)
self.message.set_cursor_at_end()
self.message.text_focus()
self.text_processor()
class dm(basicTweet):
def __init__(self, session, title, caption, text):
super(dm, self).__init__(session, title, caption, text, messageType="dm")
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 autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
@@ -157,22 +173,56 @@ class dm(basicTweet):
class viewTweet(basicTweet):
def __init__(self, tweet, tweetList, is_tweet=True):
""" 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:
image_description = []
text = ""
for i in xrange(0, len(tweetList)):
if tweetList[i].has_key("retweeted_status"):
text = text + "rt @%s: %s\n\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i]["retweeted_status"]["text"])
# tweets with message keys are longer tweets, the message value is the full messaje taken from twishort.
if tweetList[i].has_key("message") and tweetList[i]["is_quote_status"] == False:
value = "message"
else:
text = text + "@%s: %s\n\n" % (tweetList[i]["user"]["screen_name"], tweetList[i]["text"])
value = "full_text"
if tweetList[i].has_key("retweeted_status") and tweetList[i]["is_quote_status"] == False:
if tweetList[i].has_key("message") == 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 tweetList[i].has_key("extended_entities") and tweetList[i]["extended_entities"].has_key("media"):
for z in tweetList[i]["extended_entities"]["media"]:
if z.has_key("ext_alt_text") 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")))
if text == "":
if tweet.has_key("retweeted_status"):
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["text"])
if tweet.has_key("message"):
value = "message"
else:
text = tweet["text"]
self.message = message.viewTweet(text, rt_count, favs_count)
value = "full_text"
if tweet.has_key("retweeted_status"):
if tweet.has_key("message") == 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 tweet.has_key("extended_entities") and tweet["extended_entities"].has_key("media"):
for z in tweet["extended_entities"]["media"]:
if z.has_key("ext_alt_text") 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"))
self.message.set_title(len(text))
[self.message.set_image_description(i) for i in image_description]
else:
text = tweet
self.message = message.viewNonTweet(text)
@@ -187,3 +237,10 @@ class viewTweet(basicTweet):
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

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import os
import webbrowser
import pocket
import sound_lib
import paths
import widgetUtils
@@ -15,10 +14,10 @@ from extra.autocompletionUsers import settings
from pubsub import pub
import logging
import config_utils
from pocket_utils import authorisationHandler
import BaseHTTPServer
log = logging.getLogger("Settings")
import keys
from collections import OrderedDict
from platform_utils.autostart import windows as autostart_windows
class globalSettingsController(object):
def __init__(self):
@@ -34,7 +33,7 @@ class globalSettingsController(object):
if ".keymap" not in i:
continue
try:
res[config_utils.load_config(paths.app_path('keymaps/'+i))['info']['name']]=i
res[i[:-7]] =i
except:
log.exception("Exception while loading keymap " + i)
return res
@@ -56,19 +55,24 @@ class globalSettingsController(object):
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", "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", "check_for_updates", config.app["app-settings"]["check_for_updates"])
self.dialog.create_proxy()
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()
@@ -79,8 +83,13 @@ class globalSettingsController(object):
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"))
@@ -90,6 +99,7 @@ class globalSettingsController(object):
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")
if 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
@@ -145,11 +155,11 @@ class accountSettingsController(globalSettingsController):
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.create_services()
if self.config["services"]["pocket_access_token"] == "":
self.dialog.services.set_pocket(False)
else:
self.dialog.services.set_pocket(True)
widgetUtils.connect_event(self.dialog.services.pocketBtn, widgetUtils.BUTTON_PRESSED, self.manage_pocket)
# if self.config["services"]["pocket_access_token"] == "":
# self.dialog.services.set_pocket(False)
# else:
# self.dialog.services.set_pocket(True)
# widgetUtils.connect_event(self.dialog.services.pocketBtn, widgetUtils.BUTTON_PRESSED, self.manage_pocket)
self.dialog.set_value("services", "apiKey", self.config["sound"]["sndup_api_key"])
self.dialog.realize()
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
@@ -243,14 +253,25 @@ class accountSettingsController(globalSettingsController):
self.dialog.ignored_clients.remove_(id)
def get_buffers_list(self):
all_buffers = ['home','mentions','dm','sent_dm','sent_tweets','favorites','followers','friends','blocks','muted','events']
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")
all_buffers['events']=_(u"Events")
list_buffers = []
hidden_buffers=[]
for i in all_buffers:
for i in all_buffers.keys():
if i in self.config["general"]["buffer_order"]:
list_buffers.append((i, True))
list_buffers.append((i, all_buffers[i], True))
else:
hidden_buffers.append((i, False))
hidden_buffers.append((i, all_buffers[i], False))
list_buffers.extend(hidden_buffers)
return list_buffers
@@ -259,24 +280,24 @@ class accountSettingsController(globalSettingsController):
if change == True:
self.dialog.buffers.change_selected_item()
def manage_pocket(self, *args, **kwargs):
if self.dialog.services.get_pocket_status() == _(u"Connect your Pocket account"):
self.connect_pocket()
else:
self.disconnect_pocket()
# def manage_pocket(self, *args, **kwargs):
# if self.dialog.services.get_pocket_status() == _(u"Connect your Pocket account"):
# self.connect_pocket()
# else:
# self.disconnect_pocket()
def connect_pocket(self):
dlg = self.dialog.services.show_pocket_dialog()
if dlg == widgetUtils.YES:
request_token = pocket.Pocket.get_request_token(consumer_key=keys.keyring.get("pocket_consumer_key"), redirect_uri="http://127.0.0.1:8080")
auth_url = pocket.Pocket.get_auth_url(code=request_token, redirect_uri="http://127.0.0.1:8080")
webbrowser.open_new_tab(auth_url)
httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 8080), authorisationHandler.handler)
while authorisationHandler.logged == False:
httpd.handle_request()
user_credentials = pocket.Pocket.get_credentials(consumer_key=keys.keyring.get("pocket_consumer_key"), code=request_token)
self.dialog.services.set_pocket(True)
self.config["services"]["pocket_access_token"] = user_credentials["access_token"]
# def connect_pocket(self):
# dlg = self.dialog.services.show_pocket_dialog()
# if dlg == widgetUtils.YES:
# request_token = pocket.Pocket.get_request_token(consumer_key=keys.keyring.get("pocket_consumer_key"), redirect_uri="http://127.0.0.1:8080")
# auth_url = pocket.Pocket.get_auth_url(code=request_token, redirect_uri="http://127.0.0.1:8080")
# webbrowser.open_new_tab(auth_url)
# httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 8080), authorisationHandler.handler)
# while authorisationHandler.logged == False:
# httpd.handle_request()
# user_credentials = pocket.Pocket.get_credentials(consumer_key=keys.keyring.get("pocket_consumer_key"), code=request_token)
# self.dialog.services.set_pocket(True)
# self.config["services"]["pocket_access_token"] = user_credentials["access_token"]
def disconnect_dropbox(self):
self.config["services"]["pocket_access_token"] = ""

View File

@@ -31,6 +31,8 @@ class profileController(object):
def get_data(self, screen_name):
self.data = self.session.twitter.twitter.show_user(screen_name=screen_name)
if screen_name != self.session.db["user_name"]:
self.friendship_status = self.session.twitter.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"])
@@ -90,12 +92,23 @@ class profileController(object):
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"Favourites: %s") % (self.data["favourites_count"])
string = string+ _(u"Likes: %s") % (self.data["favourites_count"])
return string
def visit_url(self, *args, **kwargs):

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
from wxUI.dialogs import userActions
from pubsub import pub
import re
import widgetUtils
import output
from wxUI.dialogs import userActions
from pubsub import pub
from twython import TwythonError
from extra import autocompletionUsers
class userActionsController(object):
def __init__(self, buffer, users=[], default="follow"):
@@ -12,9 +13,14 @@ class userActionsController(object):
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 process_action(self):
action = self.dialog.get_action()
user = self.dialog.get_user()

View File

@@ -57,27 +57,25 @@ class audioUploader(object):
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
else:
url = base_url
self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback)
elif self.dialog.get("services") == "TwUp":
url = "http://api.twup.me/post.json"
self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback)
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()
self.uploaderDialog.get_response(self.uploaderFunction.perform_threaded)
def get_available_services(self):
services = []
services.append("TwUp")
services.append("SNDUp")
services.append("TwUp")
return services
def on_pause(self, *args, **kwargs):
if self.dialog.get("pause") == _(u"Pause"):
self.recording.pause()
self.dialog.set("pause", _(u"Resume"))
self.dialog.set("pause", _(u"&Resume"))
elif self.dialog.get("pause") == _(u"Resume"):
self.recording.play()
self.dialog.set("pause", _(U"Pause"))
self.dialog.set("pause", _(U"&Pause"))
def on_record(self, *args, **kwargs):
if self.recording != None:
@@ -92,7 +90,7 @@ class audioUploader(object):
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.dialog.set("record", _(u"Stop"))
self.dialog.set("record", _(u"&Stop"))
output.speak(_(u"Recording"))
def stop_recording(self):
@@ -100,11 +98,11 @@ class audioUploader(object):
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.dialog.set("record", _(u"Record"))
self.dialog.set("record", _(u"&Record"))
self.file_attached()
def file_attached(self):
self.dialog.set("pause", _(u"Pause"))
self.dialog.set("pause", _(u"&Pause"))
self.dialog.disable_control("record")
self.dialog.enable_control("play")
self.dialog.enable_control("discard")
@@ -116,8 +114,9 @@ class audioUploader(object):
if self.playing:
self._stop()
if self.recording != None:
self.dialog.disable_control("attach")
self.dialog.disable_control("play")
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")
@@ -137,11 +136,11 @@ class audioUploader(object):
# 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"))
self.dialog.set("play", _(u"&Stop"))
try:
while self.playing.is_playing:
pass
self.dialog.set("play", _(u"Play"))
self.dialog.set("play", _(u"&Play"))
self.playing.free()
self.playing = None
except:
@@ -151,7 +150,7 @@ class audioUploader(object):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.dialog.set("play", _(u"Play"))
self.dialog.set("play", _(u"&Play"))
self.playing = None
def postprocess(self):
@@ -174,10 +173,6 @@ class audioUploader(object):
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
del(self.wav_file)
if hasattr(self, 'wav_file') and os.path.exists(self.file):
os.remove(self.file)
def on_attach_exists(self, *args, **kwargs):
self.file = self.dialog.get_file()

View File

@@ -1,106 +1,71 @@
# -*- coding: utf-8 -*-
import pycurl
import sys
import threading
import time
import json
import logging
from utils import *
from pubsub import pub
log = logging.getLogger("extra.AudioUploader.transfer")
class Transfer(object):
def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
self.url = url
self.filename = filename
log.debug("Uploading audio to %s, filename %s" % (url, filename))
self.curl = pycurl.Curl()
self.start_time = None
self.completed_callback = completed_callback
self.background_thread = None
self.transfer_rate = 0
self.curl.setopt(self.curl.PROGRESSFUNCTION, self.progress_callback)
self.curl.setopt(self.curl.URL, url)
self.curl.setopt(self.curl.NOPROGRESS, 0)
self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0)
self.curl.setopt(self.curl.FOLLOWLOCATION, int(follow_location))
self.curl.setopt(self.curl.VERBOSE, int(verbose))
super(Transfer, self).__init__(*args, **kwargs)
def elapsed_time(self):
if not self.start_time:
return 0
return time.time() - self.start_time
def progress_callback(self, down_total, down_current, up_total, up_current):
progress = {}
progress["total"] = up_total
progress["current"] = up_current
# else:
# print "Killed function"
# return
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 perform_transfer(self):
log.debug("starting upload...")
self.start_time = time.time()
self.curl.perform()
self.curl.close()
log.debug("Upload finished.")
self.complete_transfer()
def perform_threaded(self):
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.curl.close()
self.completed_callback()
class Upload(Transfer):
def __init__(self, field=None, filename=None, *args, **kwargs):
super(Upload, self).__init__(filename=filename, *args, **kwargs)
self.response = dict()
self.curl.setopt(self.curl.POST, 1)
if isinstance(filename, unicode):
local_filename = filename.encode(sys.getfilesystemencoding())
else:
local_filename = filename
self.curl.setopt(self.curl.HTTPPOST, [(field, (self.curl.FORM_FILE, local_filename, self.curl.FORM_FILENAME, filename.encode("utf-8")))])
self.curl.setopt(self.curl.HEADERFUNCTION, self.header_callback)
self.curl.setopt(self.curl.WRITEFUNCTION, self.body_callback)
def header_callback(self, content):
self.response['header'] = content
def body_callback(self, content):
self.response['body'] = content
def get_url(self):
return json.loads(self.response['body'])['url']
class Download(Transfer):
def __init__(self, follow_location=True, *args, **kwargs):
super(Download, self).__init__(*args, **kwargs)
self.download_file = open(self.filename, 'wb')
self.curl.setopt(self.curl.WRITEFUNCTION, self.download_file.write)
def complete_transfer(self):
self.download_file.close()
super(DownloadDialog, self).complete_transfer()
# -*- coding: utf-8 -*-
import sys
import threading
import time
import logging
from utils import convert_bytes
from pubsub import pub
log = logging.getLogger("extra.AudioUploader.transfer")
from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
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.m = MultipartEncoder(fields={field:(self.local_filename, open(self.filename, 'rb'), "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 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 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 complete_transfer(self):
if callable(self.completed_callback):
self.completed_callback(self.obj)
def get_url(self):
return self.response.json()['url']

View File

@@ -3,10 +3,10 @@ import wx
from utils import *
import widgetUtils
class TransferDialog(widgetUtils.BaseDialog):
class UploadDialog(widgetUtils.BaseDialog):
def __init__(self, filename, *args, **kwargs):
super(TransferDialog, self).__init__(parent=None, id=wx.NewId(), *args, **kwargs)
super(UploadDialog, self).__init__(parent=None, id=wx.NewId(), *args, **kwargs)
self.pane = wx.Panel(self)
self.progress_bar = wx.Gauge(parent=self.pane)
fileBox = wx.BoxSizer(wx.HORIZONTAL)
@@ -56,18 +56,6 @@ class TransferDialog(widgetUtils.BaseDialog):
def create_buttons(self):
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
def get_response(self):
self.Show()
def destroy(self):
self.Destroy()
class UploadDialog(TransferDialog):
def __init__(self, filename=None, *args, **kwargs):
super(UploadDialog, self).__init__(filename=filename, *args, **kwargs)
class DownloadDialog(TransferDialog):
def __init__(self, *args, **kwargs):
super(Download, self).__init__(*args, **kwargs)
def get_response(self, fn):
wx.CallAfter(fn, 0.01)
self.ShowModal()

View File

@@ -31,14 +31,14 @@ class audioDialog(widgetUtils.BaseDialog):
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.play = wx.Button(panel, -1, _(u"Play"))
self.play = wx.Button(panel, -1, _(u"&Play"))
self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"Pause"))
self.pause = wx.Button(panel, -1, _(u"&Pause"))
self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"Record"))
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.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)
@@ -47,7 +47,7 @@ class audioDialog(widgetUtils.BaseDialog):
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"))
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)

View File

@@ -7,8 +7,8 @@ actions = reverse_sort.reverse_sort([ ("audio", _(u"Audio tweet.")),
("dm_received", _(u"Direct message received.")),
("dm_sent", _(u"Direct message sent.")),
("error", _(u"Error.")),
("favourite", _(u"Tweet favourited.")),
("favourites_timeline_updated", _(u"Favourites buffer updated.")),
("favourite", _(u"Tweet liked.")),
("favourites_timeline_updated", _(u"Likes buffer updated.")),
("geo", _(u"Geotweet.")),
("limit", _(u"Boundary reached.")),
("list_tweet", _(u"List updated.")),

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env python
import goslate
# -*- coding: utf-8 -*-
from microsofttranslator import Translator
def translate(text="", source="auto", target="en"):
t = Translator("twblue", "4KZA26GYIfmVAqQA/z16Hlucbg64hVSDTIpRjT2FqIU=")
return t.translate(text, target)
def translate(text, source_lang, target_lang):
gs = goslate.Goslate()
return gs.translate(text, target_lang, source_lang)
languages = {
"af": _(u"Afrikaans"),
@@ -100,8 +101,8 @@ languages = {
}
def available_languages():
l = languages.keys()
d = languages.values()
l.insert(0, '')
d.insert(0, _(u"autodetect"))
return sorted(zip(l, d))
l = languages.keys()
d = languages.values()
l.insert(0, '')
d.insert(0, _(u"autodetect"))
return sorted(zip(l, d))

View File

@@ -4,9 +4,10 @@ import sys
import fix_arrow # A few new locales for Three languages in arrow.
import fix_urllib3_warnings # Avoiding some SSL warnings related to Twython.
import fix_win32com
import fix_requests #fix cacert.pem location for TWBlue binary copies
def setup():
fix_arrow.fix()
if hasattr(sys, "frozen"):
fix_win32com.fix()
fix_requests.fix()
fix_urllib3_warnings.fix()

View File

@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from arrow import locales
from arrow.locales import Locale
@@ -11,11 +11,19 @@ def fix():
locales.BasqueLocale = BasqueLocale
locales.TurkishLocale.names[-1] = "tr_tr"
locales.ArabicLocale.names[-1] = "ar_eg"
# insert a modified function so if there is no language available in arrow, returns English locale.
locales.get_locale = get_locale
# We need to reassign the locales list for updating the list with our new contents.
locales._locales = locales._map_locales()
def get_locale(name):
locale_cls = locales._locales.get(name.lower())
if locale_cls is None:
return locales.EnglishLocale()
return locale_cls()
class CatalaLocale(Locale):
names = ['ca', 'ca_ca']
names = ['ca', 'ca_es', 'ca_ca']
past = 'Fa {0}'
future = '{0}' # I don't know what's the right phrase in catala for the future.
@@ -40,9 +48,9 @@ class CatalaLocale(Locale):
day_abbreviations = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
class GalicianLocale(Locale):
names = ['gl', 'gl_gl']
names = ['gl', 'gl_es', 'gl_gl']
past = 'Fai {0}'
future = '{0}' # I don't know what's the right phrase in Galician for the future.
future = 'En {0}'
timeframes = {
'now': 'Agora mesmo',
@@ -65,13 +73,15 @@ class GalicianLocale(Locale):
day_abbreviations = ['', 'Lun', 'Mar', 'Mer', 'xov', 'Ven' 'Sab', 'Dom']
class BasqueLocale(Locale):
names = ['eu', 'eu_eu']
names = ['eu', 'eu_es', 'eu_eu']
past = 'duela {0}'
future = '{0}' # I don't know what's the right phrase in Basque for the future.
future = '{0} igarota'
timeframes = {
'now': 'Orain',
'seconds': 'segundu',
# 'second': 'segundu bat',
'seconds': 'segundu batzuk', # without specifying a number.
#'seconds': '{0} segundu', # specifying a number
'minute': 'minutu bat',
'minutes': '{0} minutu',
'hour': 'ordu bat',

10
src/fixes/fix_requests.py Normal file
View File

@@ -0,0 +1,10 @@
from requests import certs, utils, adapters
import paths
def patched_where():
return paths.app_path(u"cacert.pem")
def fix():
certs.where=patched_where
utils.DEFAULT_CA_BUNDLE_PATH=patched_where()
adapters.DEFAULT_CA_BUNDLE_PATH=patched_where()

View File

@@ -1,5 +1,24 @@
# -*- coding: utf-8 -*-
from requests.packages import urllib3
from requests.packages.urllib3 import fields
import six
import urllib
def fix():
urllib3.disable_warnings()
urllib3.disable_warnings()
fields.format_header_param=patched_format_header_param
def patched_format_header_param(name, value):
if not any(ch in value for ch in '"\\\r\n'):
result = '%s="%s"' % (name, value)
try:
result.encode('ascii')
except (UnicodeEncodeError, UnicodeDecodeError):
pass
else:
return result
if not six.PY3 and isinstance(value, six.text_type): # Python 2:
value = value.encode('utf-8')
value=urllib.quote(value, safe='')
value = '%s=%s' % (name, value)
return value

View File

@@ -1,6 +1,4 @@
import win32com.client
def fix():
if win32com.client.gencache.is_readonly == True:
win32com.client.gencache.is_readonly = False

View File

@@ -47,7 +47,7 @@ class reportBug(object):
issue.project.name = application.name
issue.project.id = 0
issue.summary = self.dialog.get("summary"),
issue.description = "Reported by @%s\n\n" % (self.user_name) + self.dialog.get("description")
issue.description = "Reported by @%s on version %s (snapshot = %s)\n\n" % (self.user_name, application.version, application.snapshot) + self.dialog.get("description")
# to do: Create getters for category, severity and reproducibility in wx_UI.
issue.category = constants.categories[self.dialog.category.GetSelection()]
issue.reproducibility.name = constants.reproducibilities[self.dialog.reproducibility.GetSelection()]

View File

@@ -0,0 +1,35 @@
[info]
name = string(default="Chicken Nugget")
desc = string(default="Remaps TWBlue shortcuts to their equivalents in Christopher Toth's Chicken Nugget Twitter client.")
author = string(default="Bill Dengler <codeofdusk@gmail.com>")
[keymap]
up = string(default="control+win+up")
down = string(default="control+win+down")
left = string(default="control+win+left")
right = string(default="control+win+right")
open_conversation = string(default="control+win+c")
show_hide = string(default="control+win+w")
post_tweet = string(default="control+win+t")
post_reply = string(default="control+win+r")
post_retweet = string(default="control+win+shift+t")
send_dm = string(default="control+win+d")
user_details = string(default="control+win+shift+u")
exit = string(default="control+win+q")
open_timeline = string(default="control+win+u")
remove_buffer = string(default="control+win+back")
audio = string(default="control+win+return")
url = string(default="control+win+b")
go_home = string(default="control+win+home")
go_end = string(default="control+win+end")
delete = string(default="control+win+delete")
clear_buffer = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")
search = string(default="control+win+/")
find = string(default="control+win+shift+/")
check_for_updates = string(default="alt+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -1,30 +0,0 @@
[info]
name = Chicken Nugget
desc = Remaps TWBlue shortcuts to their equivalents in Christopher Toth's Chicken Nugget Twitter client.
author = Bill Dengler <codeofdusk@gmail.com>
[keymap]
up = control+win+up
down = control+win+down
left = control+win+left
right = control+win+right
open_conversation = control+win+c
show_hide = control+win+w
post_tweet = control+win+t
post_reply = control+win+r
post_retweet = control+win+shift+t
send_dm = control+win+d
user_details = control+win+shift+u
exit = control+win+q
open_timeline = control+win+u
remove_buffer = control+win+backspace
audio = control+win+return
secondary_interact = control+win+b
go_home = control+win+home
go_end = control+win+end
delete = control+win+delete
clear_buffer = control+win+shift+delete
repeat_item = control+win+space
copy_to_clipboard = control+win+shift+c
search = control+win+/
find = control+win+shift+/

View File

@@ -1,48 +1,54 @@
[info]
name = Qwitter
desc = A keymap which emulates Qwitter as closely as possible.
author = Bill Dengler <codeofdusk@gmail.com>
name = string(default="Qwitter")
desc = string(default="A keymap which emulates Qwitter as closely as possible.")
author = string(default="Bill Dengler <codeofdusk@gmail.com>")
[keymap]
up = control+win+up
down = control+win+down
left = control+win+left
right = control+win+right
next_account = control+win+shift+right
previous_account = control+win+shift+left
show_hide = control+win+w
post_tweet = control+win+n
post_reply = control+win+r
post_retweet = control+win+shift+r
send_dm = control+win+d
add_to_favourites = alt+win+f
remove_from_favourites = alt+shift+win+f
follow = control+win+l
user_details = control+win+shift+;
view_item = control+win+v
exit = control+win+f4
open_timeline = control+win+i
remove_buffer = "control+win+'"
audio = control+win+return
volume_up = control+win+alt+up
volume_down = control+win+alt+down
go_home = control+win+home
go_end = control+win+end
go_page_up = control+win+pageup
go_page_down = control+win+pagedown
update_profile = control+win+shift+p
delete = control+win+delete
clear_buffer = control+win+shift+delete
repeat_item = control+win+space
copy_to_clipboard = control+win+shift+c
add_to_list = control+win+alt+l
remove_from_list = control+win+alt+shift+l
toggle_buffer_mute = control+win+alt+m
toggle_session_mute = control+win+m
search = control+win+/
find = control+win+shift+/
edit_keystrokes = control+win+k
view_user_lists = win+alt+shift+l
reverse_geocode = control+win+g
view_reverse_geocode = control+win+shift+g
get_trending_topics = control+win+shift+t
up = string(default="control+win+up")
down = string(default="control+win+down")
left = string(default="control+win+left")
right = string(default="control+win+right")
next_account = string(default="control+win+shift+right")
previous_account = string(default="control+win+shift+left")
show_hide = string(default="control+win+w")
post_tweet = string(default="control+win+n")
post_reply = string(default="control+win+r")
post_retweet = string(default="control+win+shift+r")
send_dm = string(default="control+win+d")
add_to_favourites = string(default="alt+win+f")
remove_from_favourites = string(default="alt+shift+win+f")
follow = string(default="control+win+l")
user_details = string(default="control+win+shift+;")
view_item = string(default="control+win+v")
exit = string(default="control+win+f4")
open_timeline = string(default="control+win+i")
remove_buffer = '''string(default="control+win+'")'''
url = string(default="control+win+return")
audio = string(default="control+win+shift+return")
volume_up = string(default="control+win+alt+up")
volume_down = string(default="control+win+alt+down")
go_home = string(default="control+win+home")
go_end = string(default="control+win+end")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="control+win+shift+p")
delete = string(default="control+win+delete")
clear_buffer = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")
add_to_list = string(default="control+win+alt+l")
remove_from_list = string(default="control+win+alt+shift+l")
toggle_buffer_mute = string(default="control+win+alt+m")
toggle_session_mute = string(default="control+win+m")
search = string(default="control+win+/")
find = string(default="control+win+shift+/")
edit_keystrokes = string(default="control+win+k")
view_user_lists = string(default="win+alt+shift+l")
reverse_geocode = string(default="control+win+g")
view_reverse_geocode = string(default="control+win+shift+g")
get_trending_topics = string(default="control+win+shift+t")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -1,52 +0,0 @@
[info]
name = Windows 10 (experimental)
desc = A keymap with remapped modifiers for Windows 10 compatibility.
author = Bill Dengler <codeofdusk@gmail.com>
[keymap]
up = alt+win+up
down = alt+win+down
left = alt+win+left
right = alt+win+right
next_account = alt+win+shift+right
previous_account = alt+win+shift+left
open_conversation = alt+win+c
show_hide = alt+win+w
post_tweet = alt+win+n
post_reply = alt+win+r
post_retweet = alt+win+shift+r
send_dm = alt+win+d
add_to_favourites = alt+win+f
remove_from_favourites = alt+shift+win+f
follow = alt+win+s
user_details = alt+win+shift+n
view_item = alt+win+v
exit = alt+win+f4
open_timeline = alt+win+i
remove_buffer = alt+win+shift+i
audio = alt+win+return
secondary_interact = alt+shift+win+return
volume_up = alt+win+shift+up
go_home = alt+win+home
volume_down = alt+win+shift+down
go_end = alt+win+end
go_page_up = alt+win+pageup
go_page_down = alt+win+pagedown
update_profile = alt+win+p
delete = alt+win+delete
clear_buffer = alt+win+shift+delete
repeat_item = alt+win+space
copy_to_clipboard = alt+win+shift+c
add_to_list = alt+win+a
remove_from_list = alt+win+shift+a
toggle_buffer_mute = alt+win+shift+m
toggle_session_mute = alt+win+m
toggle_autoread = alt+win+e
search = alt+win+-
edit_keystrokes = alt+win+k
view_user_lists = alt+win+l
get_more_items = alt+win+pageup
reverse_geocode = alt+win+g
view_reverse_geocode = alt+win+shift+g
get_trending_topics = alt+win+t

View File

@@ -0,0 +1,56 @@
[info]
name = string(default="Windows 10")
desc = string(default="A keymap with remapped modifiers for Windows 10 compatibility.")
author = string(default="Bill Dengler <codeofdusk@gmail.com>")
[keymap]
up = string(default="alt+win+up")
down = string(default="alt+win+down")
left = string(default="alt+win+left")
right = string(default="alt+win+right")
next_account = string(default="alt+win+shift+right")
previous_account = string(default="alt+win+shift+left")
open_conversation = string(default="alt+win+c")
show_hide = string(default="alt+win+w")
post_tweet = string(default="alt+win+n")
post_reply = string(default="control+win+r")
post_retweet = string(default="alt+win+shift+r")
send_dm = string(default="alt+win+shift+d")
add_to_favourites = string(default="alt+win+f")
remove_from_favourites = string(default="alt+shift+win+f")
follow = string(default="alt+win+s")
user_details = string(default="alt+win+shift+n")
view_item = string(default="alt+win+v")
exit = string(default="alt+win+f4")
open_timeline = string(default="alt+win+i")
remove_buffer = string(default="alt+win+shift+i")
url = string(default="alt+win+return")
audio = string(default="alt+shift+win+return")
volume_up = string(default="alt+win+shift+up")
go_home = string(default="alt+win+home")
volume_down = string(default="alt+win+shift+down")
go_end = string(default="alt+win+end")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="alt+win+delete")
clear_buffer = string(default="alt+win+shift+delete")
repeat_item = string(default="alt+win+space")
copy_to_clipboard = string(default="alt+win+shift+c")
add_to_list = string(default="alt+win+a")
remove_from_list = string(default="alt+win+shift+a")
toggle_buffer_mute = string(default="alt+win+shift+m")
toggle_session_mute = string(default="alt+win+m")
toggle_autoread = string(default="alt+win+e")
search = string(default="alt+win+-")
edit_keystrokes = string(default="alt+win+k")
view_user_lists = string(default="alt+win+l")
get_more_items = string(default="alt+win+pageup")
reverse_geocode = string(default="alt+win+g")
view_reverse_geocode = string(default="alt+win+shift+g")
get_trending_topics = string(default="alt+win+t")
check_for_updates = string(default="alt+win+u")
list_manager = string(default="alt+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+alt+shift+u")

View File

@@ -50,3 +50,8 @@ get_more_items = string(default="alt+win+pageup")
reverse_geocode = string(default="control+win+g")
view_reverse_geocode = string(default="control+win+shift+g")
get_trending_topics = string(default="control+win+t")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -1,54 +1,57 @@
[info]
name = Default
desc = TWBlue's default keymap.
author = Bill Dengler <codeofdusk@gmail.com>
name = string(default="Default")
desc = string(default="TWBlue's default keymap.")
author = string(default="Bill Dengler <codeofdusk@gmail.com>")
[keymap]
up = control+win+up
down = control+win+down
left = control+win+left
right = control+win+right
next_account = control+win+shift+right
previous_account = control+win+shift+left
open_conversation = control+win+c
show_hide = control+win+m
post_tweet = control+win+n
post_reply = control+win+r
post_retweet = control+win+shift+r
send_dm = control+win+d
add_to_favourites = alt+win+f
remove_from_favourites = alt+shift+win+f
follow = control+win+s
user_details = control+win+alt+n
view_item = control+win+v
exit = control+win+f4
open_timeline = control+win+i
remove_buffer = control+win+shift+i
audio = control+alt+win+return
url = control+win+return
volume_up = control+win+alt+up
volume_down = control+win+alt+down
go_home = control+win+home
go_end = control+win+end
go_page_up = control+win+pageup
go_page_down = control+win+pagedown
update_profile = alt+win+p
delete = control+win+delete
clear_buffer = control+win+shift+delete
repeat_item = control+win+space
copy_to_clipboard = control+win+c
add_to_list = control+win+a
remove_from_list = control+win+shift+a
toggle_buffer_mute = control+win+shift+m
toggle_session_mute = alt+win+m
toggle_autoread = control+win+e
search = control+win+-
edit_keystrokes = control+win+k
view_user_lists = control+win+l
get_more_items = alt+win+pageup
reverse_geocode = control+win+g
view_reverse_geocode = control+win+shift+g
get_trending_topics = control+win+t
find = control+win+{
secondary_interact = control+win+alt+return
interact = control+win+return
up = string(default="control+win+up")
down = string(default="control+win+down")
left = string(default="control+win+left")
right = string(default="control+win+right")
next_account = string(default="control+win+shift+right")
previous_account = string(default="control+win+shift+left")
open_conversation = string(default="control+win+c")
show_hide = string(default="control+win+m")
post_tweet = string(default="control+win+n")
post_reply = string(default="control+win+r")
post_retweet = string(default="control+win+shift+r")
send_dm = string(default="control+win+d")
add_to_favourites = string(default="alt+win+f")
remove_from_favourites = string(default="alt+shift+win+f")
follow = string(default="control+win+s")
user_details = string(default="control+win+alt+n")
view_item = string(default="control+win+v")
exit = string(default="control+win+f4")
open_timeline = string(default="control+win+i")
remove_buffer = string(default="control+win+shift+i")
audio = string(default="control+alt+win+return")
url = string(default="control+win+return")
volume_up = string(default="control+win+alt+up")
volume_down = string(default="control+win+alt+down")
go_home = string(default="control+win+home")
go_end = string(default="control+win+end")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="control+win+delete")
clear_buffer = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")
add_to_list = string(default="control+win+a")
remove_from_list = string(default="control+win+shift+a")
toggle_buffer_mute = string(default="control+win+shift+m")
toggle_session_mute = string(default="alt+win+m")
toggle_autoread = string(default="control+win+e")
search = string(default="control+win+-")
edit_keystrokes = string(default="control+win+k")
view_user_lists = string(default="control+win+l")
get_more_items = string(default="alt+win+pageup")
reverse_geocode = string(default="control+win+g")
view_reverse_geocode = string(default="control+win+shift+g")
get_trending_topics = string(default="control+win+t")
find = string(default="control+win+{")
check_for_updates = string(default="control+win+u")
list_manager = string(default="control+win+shift+l")
configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")

View File

@@ -5,10 +5,17 @@ import exceptions
from ctypes import c_char_p
from libloader import load_library
import paths
#if application.snapshot == True:
# if platform.architecture()[0][:2] == "32":
# lib = load_library("snapshot_api_keys32", x86_path=paths.app_path("keys/lib"))
# else:
# lib = load_library("snapshot_api_keys64", x64_path=paths.app_path("keys/lib"))
#else:
if platform.architecture()[0][:2] == "32":
lib = load_library("api_keys32", x86_path=paths.app_path("keys/lib"))
lib = load_library("stable_api_keys32", x86_path=paths.app_path("keys/lib"))
else:
lib = load_library("api_keys64", x64_path=paths.app_path("keys/lib"))
lib = load_library("stable_api_keys64", x64_path=paths.app_path("keys/lib"))
# import linuxKeys
# lib = linuxKeys

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -11,8 +11,8 @@ actions = {
"post_reply": _(u"Reply"),
"post_retweet": _(u"Retweet"),
"send_dm": _(u"Send direct message"),
"add_to_favourites": _(u"Mark as favourite"),
"remove_from_favourites": _(u"Remove from favourites"),
"add_to_favourites": _(u"Like a tweet"),
"remove_from_favourites": _(u"Unlike a tweet"),
"follow": _(u"Open the user actions dialogue"),
"user_details": _(u"See user details"),
"view_item": _(u"Show tweet"),
@@ -46,4 +46,10 @@ actions = {
"view_reverse_geocode": _(u"Display the tweet's geolocation in a dialog"),
"get_trending_topics": _(u"Create a trending topics buffer"),
"open_conversation": _(u"View conversation"),
"check_for_updates": _(u"Check and download updates"),
"lists_manager": _(u"Opens the list manager, which allows you to create, edit, delete and open lists in buffers."),
"configuration": _(u"Opens the global settings dialogue"),
"accountConfiguration": _(u"Opens the account settings dialogue"),
"audio": _(u"Try to play an audio file"),
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
}

View File

@@ -3,6 +3,7 @@ import widgetUtils
import config
import wx_ui
import constants
from pubsub import pub
class KeystrokeEditor(object):
def __init__(self):
@@ -14,6 +15,7 @@ class KeystrokeEditor(object):
self.hold_map = self.map.copy()
self.dialog.put_keystrokes(constants.actions, self.map)
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_keystroke)
widgetUtils.connect_event(self.dialog.execute, widgetUtils.BUTTON_PRESSED, self.execute_action)
self.dialog.get_response()
def edit_keystroke(self, *args, **kwargs):
@@ -35,13 +37,10 @@ class KeystrokeEditor(object):
def get_edited_keystroke(self, dialog):
keys = []
if dialog.get("win") == False:
wx_ui.no_win_message()
return
if dialog.get("control") == True:
keys.append("control")
# if dialog.get("win") == True:
keys.append("win")
if dialog.get("win") == True:
keys.append("win")
if dialog.get("alt") == True:
keys.append("alt")
if dialog.get("shift") == True:
@@ -52,3 +51,7 @@ class KeystrokeEditor(object):
wx_ui.no_key()
return
return "+".join(keys)
def execute_action(self, *args, **kwargs):
action = self.dialog.actions[self.dialog.get_action()]
pub.sendMessage("execute-action", action=action)

View File

@@ -13,18 +13,20 @@ class keystrokeEditorDialog(baseDialog.BaseWXDialog):
self.keys = widgets.list(self, _(u"Action"), _(u"Keystroke"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(400, 450))
self.keys.list.SetFocus()
firstSizer = wx.BoxSizer(wx.HORIZONTAL)
firstSizer.Add(keysText)
firstSizer.Add(self.keys.list)
firstSizer.Add(keysText, 0, wx.ALL, 5)
firstSizer.Add(self.keys.list, 0, wx.ALL, 5)
self.edit = wx.Button(panel, -1, _(u"Edit"))
self.edit.SetDefault()
self.execute = wx.Button(panel, -1, _(u"Execute action"))
close = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
secondSizer = wx.BoxSizer(wx.HORIZONTAL)
secondSizer.Add(self.edit)
secondSizer.Add(close)
sizer.Add(firstSizer)
sizer.Add(secondSizer)
panel.SetSizerAndFit(sizer)
secondSizer.Add(self.edit, 0, wx.ALL, 5)
secondSizer.Add(self.execute, 0, wx.ALL, 5)
secondSizer.Add(close, 0, wx.ALL, 5)
sizer.Add(firstSizer, 0, wx.ALL, 5)
sizer.Add(secondSizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def put_keystrokes(self, actions, keystrokes):
for i in keystrokes:

View File

@@ -69,6 +69,8 @@ def getLanguageDescription(language):
"ar":pgettext("languageName","Arabic"),
"ne":pgettext("languageName","Nepali"),
"sr":pgettext("languageName","Serbian (Latin)"),
"ja":pgettext("languageName","Japanese"),
"ro":pgettext("languageName","Romanian"),
}.get(language,None)
return desc
@@ -195,10 +197,14 @@ def langToWindowsLocale(lang):
"gl": "glc",
"eu": "euq",
"hu": "hun",
"hr": "hrv",
"it": "ita",
"ja": "jpn",
"pl": "plk",
"pt": "ptb",
"ro": "rom",
"ru": "rus",
"tr": "trk"
"tr": "trk",
"sr": "eng",
}
return languages[lang]

View File

@@ -1,13 +1,26 @@
from pywintypes import com_error
import win32com
import paths
win32com.__gen_path__=paths.com_path()
import sys
import os
sys.path.append(os.path.join(win32com.__gen_path__, "."))
from win32com.client import gencache
fixed=False
def prepare_gencache():
gencache.is_readonly = False
gencache.GetGeneratePath()
def patched_getmodule(modname):
mod=__import__(modname)
return sys.modules[modname]
def load_com(*names):
global fixed
if fixed==False:
gencache._GetModule=patched_getmodule
fixed=True
result = None
for name in names:
try:

View File

@@ -32,7 +32,7 @@ def load_library(library, x86_path='.', x64_path='.', *args, **kwargs):
loaded = _do_load(lib, *args, **kwargs)
if loaded is not None:
return loaded
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, path))
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, lib))
def _do_load(file, *args, **kwargs):
loader = TYPES[platform.system()]['loader']

Binary file not shown.

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