Compare commits

..

346 Commits

Author SHA1 Message Date
f287308986 Cleaned translation files 2015-06-11 16:13:34 -05:00
939523c4f9 Updated translations 2015-06-11 16:11:03 -05:00
aa5977f324 Fixes for the V 9.4 snapshot 2015-06-11 16:09:46 -05:00
4b882d8586 Updated translation templates 2015-06-08 10:04:33 -05:00
b0bbe5719f Fixed a bug when the user unfollows someone 2015-06-08 10:04:16 -05:00
0e3a4372b8 Removed some code 2015-06-08 05:41:48 -05:00
83575b14b9 Opening lists as a buffer is now possible from the list manager 2015-06-05 05:21:49 -05:00
f09ff43672 A couple of bugfixes 2015-06-04 17:15:03 -05:00
0045bd223c Code cleanup and a couple of fixes 2015-06-01 17:52:23 -05:00
f6bf1dcfdc Twishort keys fixed for 64 bits systems 2015-06-01 17:51:49 -05:00
91c5165d12 Interact function refactorized 2015-06-01 11:18:39 -05:00
5976caf736 Code fixes 2015-05-26 20:23:02 -05:00
831bbc2f2f Solved conflicts 2015-05-18 08:42:07 -05:00
Bill Dengler
590e4b0e6b #39: Move new methods. 2015-05-17 13:34:06 -04:00
Bill Dengler
6c690d7a3d Move interact to main controler. 2015-05-17 13:22:11 -04:00
Bill Dengler
b03d832376 String cleanup. 2015-05-17 00:15:10 -04:00
Bill Dengler
d521146562 Fixed #44. 2015-05-16 19:25:02 -04:00
Bill Dengler
66686c786d String cleanup. 2015-05-16 19:09:11 -04:00
Bill Dengler
bb27c08424 #43: Fix keymap conflict. 2015-05-16 19:08:13 -04:00
Bill Dengler
ae9bd087a8 Merge branch 'next-gen' into issue39 2015-05-16 17:43:32 -04:00
Bill Dengler
4bee7991d1 Fix error in base keymap template. 2015-05-16 17:38:24 -04:00
Bill Dengler
c67e8d041b Closed #43. 2015-05-16 17:36:43 -04:00
Bill Dengler
1d7b2c5712 Closed #39. 2015-05-16 14:25:38 -04:00
Bill Dengler
ebe3ab2c14 #39: Update Chicken Nugget keymap. 2015-05-16 13:59:21 -04:00
Bill Dengler
4b2c7a9655 #39: Make keymap changes for secondary_interact. 2015-05-16 13:58:06 -04:00
Bill Dengler
24fa7a1815 #39: Add option to disable new behavior. 2015-05-16 13:56:20 -04:00
Bill Dengler
a60d00d0dc #41: Added Chicken Nugget keymap. 2015-05-16 13:41:17 -04:00
Bill Dengler
b5f1294b82 #41: Implement keymap switching. 2015-05-16 13:28:44 -04:00
Bill Dengler
671e0e0bff String cleanup. 2015-05-16 03:31:53 -04:00
Bill Dengler
44605f1209 String cleanup. 2015-05-16 03:20:58 -04:00
Bill Dengler
cf139292a6 #41: Cleanup. 2015-05-16 03:10:20 -04:00
Bill Dengler
f3e1dc40ea Closed #40. 2015-05-16 03:01:39 -04:00
Bill Dengler
6ba57e83cf #41: Replace references to config.app["keymap"] to config.keymap["keymap"] 2015-05-16 01:23:33 -04:00
Bill Dengler
f68388f700 Clean up default keymap. 2015-05-16 01:15:01 -04:00
Bill Dengler
dc5d11c1c8 #41: Add load_keymap config entry, load a keymap as config.keymap. 2015-05-16 01:13:54 -04:00
Bill Dengler
40adacb32e #41: Added Chicken Nugget keymap. 2015-05-16 00:53:37 -04:00
Bill Dengler
3747f34b69 #40: Created base and default keymaps. 2015-05-16 00:15:10 -04:00
Bill Dengler
938be159a4 Closed #19. 2015-05-15 23:48:12 -04:00
Bill Dengler
7f869f7de1 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2015-05-15 21:44:13 -04:00
07bb6930d0 Fixed #38 2015-05-15 20:42:59 -05:00
Bill Dengler
48238239c3 Initial support for #29 2015-05-15 21:39:01 -04:00
Bill Dengler
d1fe610d6e Cleaned up copyright section. 2015-05-14 22:17:57 -04:00
Bill Dengler
3028b7d8ca You can now override hardcoded keys by adding attributes to the application module. 2015-05-14 22:12:42 -04:00
Bill Dengler
4a025caa69 String cleanup. 2015-05-14 10:14:42 -04:00
Bill Dengler
aa7145f5b3 Fix keycodes. 2015-05-14 10:12:43 -04:00
Bill Dengler
7fb71e2ec5 Fix filenames, play error sound on unactionable. 2015-05-14 10:09:31 -04:00
Bill Dengler
d02bd91f41 Fix error in sound declaration. 2015-05-14 10:03:38 -04:00
Bill Dengler
432e40bdfa Fix audio playback. 2015-05-14 10:01:37 -04:00
Bill Dengler
f0e1f74d03 Delete audio stream after successful playback. 2015-05-13 23:55:23 -04:00
Bill Dengler
6250e16cfe Fixed #37. 2015-05-13 23:21:47 -04:00
Bill Dengler
0b0da44020 Fixed #36. 2015-05-13 23:20:54 -04:00
Bill Dengler
73a069dbe2 Implement fix for url mishandling, remove print statements for testing, fixed #35 2015-05-13 22:10:37 -04:00
Bill Dengler
81a9a36519 Honor session volume during audio playback from interact method. 2015-05-13 21:59:46 -04:00
Bill Dengler
1b4ebebf25 Add delete flag to method call in interact, so we can stop an audio stream and play another. 2015-05-13 21:35:33 -04:00
Bill Dengler
1d1c07caca Add delete flag to stop_audio, clean duplicate code. 2015-05-13 21:34:26 -04:00
Bill Dengler
5f8760a0a9 Fixed error in settings controler. 2015-05-13 20:43:06 -04:00
Bill Dengler
feda70aea8 String, code cleanups. 2015-05-13 20:40:21 -04:00
Bill Dengler
e92565013b Cleanup, disabled accurate audio algo by default, string cleanup. 2015-05-13 20:26:05 -04:00
Bill Dengler
9ddc74d640 Typo fix. 2015-05-13 17:32:59 -04:00
Bill Dengler
71a061d13c Properly implemented announcements. 2015-05-13 17:32:03 -04:00
Bill Dengler
7f6cf8d30a Added announce flags to play and url methods. 2015-05-13 17:29:58 -04:00
Bill Dengler
58a8e45d56 Bugfix. 2015-05-13 17:25:46 -04:00
Bill Dengler
bb5f79e07b Removed output, time tests. 2015-05-13 14:42:42 -04:00
Bill Dengler
2a8868f096 More code changes. This is a big mess right now. Will clean this up. 2015-05-13 13:36:28 -04:00
Bill Dengler
74f0e8ba50 Attempt to fix merge conflict. 2015-05-13 12:53:14 -04:00
Bill Dengler
1d344275bb #34: just playing around a bit. Really broken right now. 2015-05-13 12:49:22 -04:00
Bill Dengler
ea7f5bacf9 #35: Add is_playable 2015-05-13 10:39:21 -04:00
4c125e4a7a Reverted audio algorithm 2015-05-13 08:55:45 -05:00
Bill Dengler
0845553047 Fixed #34 2015-05-12 22:12:24 -04:00
Bill Dengler
6f88e08811 Remove unneeded lines. 2015-05-12 20:53:28 -04:00
Bill Dengler
2fa17ee51f Clean up interact method. 2015-05-12 20:50:17 -04:00
Bill Dengler
7b2cf28f68 Miscelaneous fixes. 2015-05-12 20:38:40 -04:00
Bill Dengler
e67446b9bf Added exception handlers. 2015-05-12 20:19:39 -04:00
Bill Dengler
c7d157145b Fixed indentation error. 2015-05-12 19:55:32 -04:00
Bill Dengler
ab08d21d95 Made algo option application-wide, setting should actually work now. 2015-05-12 19:52:47 -04:00
Bill Dengler
ee1f95ea3b Clean out memmos when switching algos. 2015-05-12 19:30:44 -04:00
Bill Dengler
8ce5c258d3 Added more hooks, we now call the proper method to cache audio test results, added force flag to skip memmo checks and overwrite invalid memmos (useful when switching algos). 2015-05-12 19:11:24 -04:00
Bill Dengler
9b4fdc8966 #33: Added more hooks. 2015-05-12 18:40:28 -04:00
Bill Dengler
6cb5c4bd5c #33: Removed inefficient method, redesigning. 2015-05-12 18:09:15 -04:00
Bill Dengler
1b2b5913d1 #33: Implement setting for use_modern_audio_algo but not it does nothing right now. 2015-05-12 17:18:30 -04:00
Bill Dengler
92cd16bec9 #33: Add is_audio rebuild method, to eliminate slowness as much as possible. 2015-05-12 15:26:06 -04:00
Bill Dengler
ff9e16285e #33: Performance improvements (Windows). 2015-05-12 14:53:23 -04:00
Bill Dengler
68499ca9e5 #33: Generalize. 2015-05-12 14:21:15 -04:00
Bill Dengler
6fdc5c67e2 #33: Memmoize new audio detection logic for performance, follow HTTP redirects when determining filetype. 2015-05-12 14:13:43 -04:00
Bill Dengler
52bfa82ec5 #33: Add better audio detection support (needs testing, could pose a security risk, users should be able to disable this). 2015-05-12 13:31:51 -04:00
Bill Dengler
1e6a784192 #33: geocode checkers, audio handlers (there's probably a much better way to do this). 2015-05-12 12:01:19 -04:00
Bill Dengler
8eb6d0c4cb #33: Implemented exception on audio links that don't play. 2015-05-12 11:46:08 -04:00
Bill Dengler
e2771023da #33: Generalize audio method. 2015-05-12 11:30:56 -04:00
Bill Dengler
d19baf411a #33: generalize url. 2015-05-12 11:20:50 -04:00
Bill Dengler
0ddc20ea7c #33: Miscelaneous cleanup. 2015-05-12 10:47:24 -04:00
Bill Dengler
2aceab527e Keyboard remaps. 2015-05-12 10:14:26 -04:00
Bill Dengler
be787dc728 #33: redefine methods. 2015-05-12 10:12:09 -04:00
Bill Dengler
0aaec95ac4 #33: Initial interact() method. Very rough. 2015-05-12 09:47:41 -04:00
Bill Dengler
c1ce8cb8e5 Closed #32. 2015-05-11 21:21:27 -04:00
Bill Dengler
3f63046cbb #31: Refactored solution. 2015-05-11 20:25:35 -04:00
Bill Dengler
f241a1fc31 #31: Implement support for invisible interface. GUI implementation needs testing and possibly to be implemented. 2015-05-11 19:54:20 -04:00
Bill Dengler
8d873674ee #31: Initial support. 2015-05-11 19:41:19 -04:00
Bill Dengler
6f4501940e #31: Remove old dict that was to be used for this purpose. 2015-05-11 19:00:25 -04:00
Bill Dengler
69e3205a68 Fix the client, #30 really needs to be closed @manuelcortez 2015-05-10 13:37:05 -04:00
Bill Dengler
5001559c7c Fixed syntax error. 2015-05-10 13:24:56 -04:00
Bill Dengler
e52385317e Error corrections. 2015-05-09 23:19:21 -04:00
Bill Dengler
7003bab110 Fixed capitalization error. 2015-05-09 23:16:40 -04:00
Bill Dengler
f8f9a4ecf2 Fixed syntax error. 2015-05-09 23:13:47 -04:00
Bill Dengler
edae9fb664 Fixed syntax error. 2015-05-09 23:13:17 -04:00
Bill Dengler
a5198b881e #15: Allow for caching to be disabled, delete database when cache is disabled, remove unneeded conditional logic from method calls and move into methods themselves. 2015-05-09 23:12:17 -04:00
Bill Dengler
f394701789 #15: Initial work for persistance size limiting. 2015-05-09 23:04:06 -04:00
Bill Dengler
4d4816a61b Close #29 2015-05-09 16:29:20 -04:00
Bill Dengler
cb94d7f71a More cleanup. 2015-05-09 13:08:37 -04:00
Bill Dengler
c833854097 String cleanup in sounds tutorial. 2015-05-09 13:00:21 -04:00
Bill Dengler
278c1d5024 Closed #28 2015-05-09 12:54:02 -04:00
55d21840ef Included technow in the copyright section 2015-05-08 10:40:05 -05:00
8e27e9720c Closed #26 2015-05-08 09:38:00 -05:00
Bill Dengler
e1479b5c10 Fixed #25 2015-05-08 10:27:24 -04:00
Bill Dengler
e6ce553216 Updated application info, set bug reporting URL to new page for #26. 2015-05-08 10:08:00 -04:00
Bill Dengler
eb9128cb20 Split buffer dialog again. This closes #18 we won't implement this. 2015-05-08 09:59:52 -04:00
Bill Dengler
55683aee96 Fixed #23 2015-05-07 16:59:13 -04:00
e90c70bf02 Fix for #22 2015-05-07 09:16:41 -05:00
71eca7baf2 Fixed #21 2015-05-06 21:11:50 -05:00
Bill Dengler
d777b3a3cb Changes for #21 and #16 but issues still there. Needs work. 2015-05-06 19:06:20 -04:00
Bill Dengler
02486ccae6 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2015-05-06 16:42:04 -04:00
Bill Dengler
3633513e85 Fix #16. 2015-05-06 16:41:37 -04:00
Bill Dengler
d4932f3d2f Fix issue 13. 2015-05-06 16:36:01 -04:00
971306cd73 Fixed a bug in the buffer order 2015-05-06 13:46:47 -05:00
4b1a4db3db Now buffer list contains translatable strings 2015-05-06 13:33:53 -05:00
472099614a Fix a git conflict 2015-05-06 13:21:09 -05:00
bd6861d179 Move up and down are fully implemented 2015-05-06 12:56:32 -05:00
Bill Dengler
6c5e529129 More fixes. 2015-05-06 12:47:46 -04:00
Bill Dengler
9fbbd29c3b Fix errors in handlers. 2015-05-06 12:43:22 -04:00
Bill Dengler
7206debb30 Fix error in GUI binding declaration. 2015-05-06 12:38:59 -04:00
Bill Dengler
be4ba1608b More handlers. 2015-05-06 12:36:48 -04:00
Bill Dengler
e7628e1f65 Checks for move_up and move_down methods, untested since I broke my Python install and need to rebuild the repo. 2015-05-06 12:05:33 -04:00
Bill Dengler
cdb0a5b5a6 Enable the persistance (cache) by default. 2015-05-06 10:54:58 -04:00
Bill Dengler
1794f497a5 String cleanup, require restart when persistant_session changed. 2015-05-06 10:54:12 -04:00
Bill Dengler
08ce78f97c Speak state upon toggle. 2015-05-06 10:42:39 -04:00
Bill Dengler
797e0d7773 Fixed traceback. 2015-05-06 10:38:34 -04:00
Bill Dengler
ab5f6fd12d Unhook return in buffers dialog. 2015-05-06 10:27:22 -04:00
Bill Dengler
2a3937bf55 Implement show/hide toggle. 2015-05-06 00:26:01 -04:00
ec326ed1c1 Updater commit fixed 2015-05-05 17:54:38 -05:00
501a5cd57e Buffer configuration rewritten (move up and down is not possible yet) 2015-05-05 17:52:54 -05:00
Bill Dengler
780adeb001 Handle exceptions on update, added this because update server is down. 2015-05-05 11:18:40 -04:00
Bill Dengler
1adcd86a39 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2015-05-05 10:15:01 -04:00
1c4d0cc530 Replaced if with elif for a better performance 2015-05-05 08:39:03 -05:00
Bill Dengler
45d1380f95 Remove unneeded config entries. 2015-05-04 16:40:32 -04:00
Bill Dengler
78355efc45 Removed handling code for old buffer management. 2015-05-04 13:15:44 -04:00
Bill Dengler
0adf06f2a6 Remove options for old buffer management from UI. 2015-05-04 13:10:19 -04:00
Bill Dengler
3dc4c4c685 Buffer order now controlable by editing config file. 2015-05-04 12:51:47 -04:00
Bill Dengler
7b9247eefe Reimplemented long retweet handler. 2015-05-04 11:42:28 -04:00
Bill Dengler
e62039b727 Added message dialogs for handling retweets over 140 characters (I thought I pushed these but perhaps not). 2015-05-04 11:30:21 -04:00
380e05b079 Updated translations, new snapshot 2015-05-04 08:09:34 -05:00
5b6cae0a61 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-05-03 14:17:10 -05:00
592952c066 Added persistant session in the general account settings 2015-05-03 14:16:38 -05:00
Bill Dengler
42052f5ee7 Cleaned up user menu. 2015-05-02 18:42:21 -04:00
Bill Dengler
0162ae5739 Initial framework for unified new buffer dialog. 2015-05-02 18:35:11 -04:00
33175cbccd Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-05-02 17:31:45 -05:00
ce5498ff94 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-05-02 17:22:32 -05:00
c50c42b895 Updating some translation strings 2015-05-02 17:22:28 -05:00
Bill Dengler
0f6b3491f8 Generalization. 2015-05-02 18:22:23 -04:00
Bill Dengler
036edb6c28 Cleanup, generalize spell checker. 2015-05-02 18:17:40 -04:00
Bill Dengler
3e8e2312f2 Improved the tweet dialogs. 2015-05-02 18:09:28 -04:00
Bill Dengler
83f9e32fd6 Corrected word choice. 2015-05-02 04:29:41 -04:00
Bill Dengler
6e294ca0da Fixed a typo. 2015-05-02 04:04:29 -04:00
Bill Dengler
3a5eeed372 Announce to user on database shelve, the process can take a while especially with large dictionaries and slow systems and we don't want users thinking the app hung. 2015-05-02 04:00:29 -04:00
Bill Dengler
9d083fb550 Fixed error handling. 2015-05-02 03:53:20 -04:00
Bill Dengler
aca2292637 Removed print statements, these were for my debugging only. 2015-05-02 03:49:47 -04:00
Bill Dengler
c7719545ac All keys will be converted to bitestrings on deshelve and to unicode on shelve. This will probably break i18n majorly, so a better fix should be implemented at some point. 2015-05-02 03:48:36 -04:00
Bill Dengler
f39adb658b Added exception handlers to deshelve and shelve methods. 2015-05-02 03:41:28 -04:00
Bill Dengler
7174984973 String cleanup, improved user experience. What is a timeline? And it's more logical to have the option to create a new buffer in the buffer menu. 2015-05-02 03:04:46 -04:00
Bill Dengler
ffaca56ec5 Cleanup. 2015-05-01 12:54:27 -04:00
Bill Dengler
8207bda74b Consistancy cleanup. 2015-05-01 11:16:41 -04:00
Bill Dengler
3c5529a761 String cleanup. 2015-05-01 11:05:42 -04:00
Bill Dengler
ebcd5720b0 Various changes related to generalization and persistance. 2015-05-01 02:05:25 -04:00
Bill Dengler
12541f3de3 Implement persistance. 2015-05-01 02:03:42 -04:00
Bill Dengler
594985dec4 Initial shelve support for persistance in sessions. 2015-05-01 01:48:42 -04:00
e7943cba50 Updated spanish translation, basic lists support 2015-04-27 16:08:02 -05:00
f51c873324 New snapshot 2015-04-22 15:34:44 -05:00
96671645bb Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-04-20 17:49:38 -05:00
2880638241 Updated some translations; basic proxy support (needs testing) 2015-04-20 17:49:07 -05:00
Bill Dengler
3f02a0f422 More generalization. 2015-04-19 19:41:24 -04:00
Bill Dengler
7dd383b15e more app name removal. 2015-04-19 19:33:22 -04:00
Bill Dengler
ddf9d29b7d Fixed imports. 2015-04-19 19:28:27 -04:00
Bill Dengler
109635b8f4 More generalization. 2015-04-19 19:23:31 -04:00
Bill Dengler
9820d369c3 Merge branch 'next-gen' into generalize 2015-04-19 19:14:02 -04:00
Bill Dengler
263ce33c41 More string cleanup. 2015-04-19 19:13:57 -04:00
Bill Dengler
0913eac63d Merge branch 'next-gen' into generalize 2015-04-19 19:11:40 -04:00
Bill Dengler
ce0aa2d6a3 String cleanup. 2015-04-19 19:11:23 -04:00
Bill Dengler
00d5b88b8d Remove hardcoded app names. 2015-04-19 19:10:34 -04:00
be0253815f Merge pull request #12 from codeofdusk/next-gen
Ready sound/message toggles, string cleanup, and sounds tutorial menu moving.
2015-04-18 04:35:58 -05:00
Bill Dengler
a9405e32e9 More improvements to the sounds tutorial, reintroduced generalized app name. Why is this implemented as a list of tuples, wouldn't this be better implemented as a dictionary? 2015-04-17 19:32:30 -04:00
Bill Dengler
d0924a6e15 Even more string cleanup. 2015-04-17 19:00:24 -04:00
Bill Dengler
3c46201af9 More string cleanup. 2015-04-17 18:31:27 -04:00
Bill Dengler
2d7c7e8e37 String cleanup. 2015-04-17 18:24:27 -04:00
Bill Dengler
d87929c4c3 Cleaned up strings, added settings for the ready sound and message to the WX UI. 2015-04-17 17:53:24 -04:00
Bill Dengler
d4a4decaed Remove hardcoded app name. 2015-04-17 15:49:35 -04:00
Bill Dengler
61863c38e3 More GUI adjustments for sounds tutorial option move. 2015-04-17 15:39:43 -04:00
Bill Dengler
2d1f7e5ada Moved the option for the sounds tutorial to the "help" menu where a new user of the application is most likely to look. 2015-04-17 15:38:25 -04:00
Bill Dengler
916ca0127d The ready sound and message can now be disabled from the config file. 2015-04-17 15:32:12 -04:00
Bill Dengler
6f4e9e920f Made some app defaults more sane. 2015-04-17 15:07:30 -04:00
c2521f28fc TWBlue allows ask for comment or not in a retweet. Check account settings 2015-04-15 11:09:36 -05:00
7565d2ea57 Merge pull request #11 from codeofdusk/next-gen
More alphabetization.
2015-04-14 11:13:18 -05:00
Bill Dengler
23ba46823c Fixed the soundsTutorial. Really this time. 2015-04-14 11:38:15 -04:00
7facb9b020 Keystroke editor dialog is showed even if config has changed 2015-04-14 09:41:12 -05:00
5490a59eb5 Fixed a typo in the pypubsub package 2015-04-14 09:28:51 -05:00
d90df91aef AttributeError fixed from pull request #10 2015-04-14 09:03:16 -05:00
7cdda6806b Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-04-14 08:51:33 -05:00
97cff4b598 New snapshot 2015-04-14 08:51:27 -05:00
c45a208cd6 Merge pull request #10 from codeofdusk/next-gen
Sounds tutorial refactor and docstring cleanup
2015-04-14 08:49:14 -05:00
Bill Dengler
8d6d452dfb Fixed docstrings. 2015-04-12 19:43:19 -04:00
Bill Dengler
21c6a999b4 The sounds tutorial is now sorted in alphabetical order. 2015-04-12 18:16:19 -04:00
Bill Dengler
141d8fa105 Improved strings for sounds tutorial. 2015-04-12 17:42:02 -04:00
José Manuel
dec2456655 Merge pull request #9 from codeofdusk/next-gen
English translation cleanup
2015-04-12 21:50:21 +02:00
Bill Dengler
9263aa8507 Fixed readme. 2015-04-12 13:52:55 -04:00
Bill Dengler
df3a32572b Made application description more succinct. 2015-04-12 12:35:54 -04:00
Bill Dengler
8b9209b62a Clean up spelling in help texts, improve clarity. 2015-04-12 12:29:24 -04:00
eef78cb8dc Libraries from keys are copied in setup 2015-04-12 00:03:51 -05:00
7c65a96a2b Fixed translator 2015-04-10 09:36:25 -05:00
5d87ce06b7 Jaws should not speak navigation keystrokes in invisible mode 2015-04-10 09:35:09 -05:00
43b797ca6b Get More Items has been fixed for showing items in the correct order 2015-04-10 09:34:24 -05:00
fa591c200d Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-04-10 09:33:44 -05:00
1f5b2aaf7c Merge pull request #8 from sukiletxe/next-gen
Fixed small typo in arrow, in Basque locale.
2015-04-08 17:47:18 -05:00
77d04e0297 Longer retweets now includes the username of its owner 2015-04-08 17:46:14 -05:00
Sukil Etxenike
88ffdb4c29 Fixed small typo in arrow, in Basque locale. 2015-04-08 23:47:50 +02:00
324b18e8a4 Merge pull request #7 from sukiletxe/next-gen
Corrected program strings.
2015-04-08 13:44:20 -05:00
e2352ea3f2 Long retweets support. Read a long retweet by showing the tweet 2015-04-08 13:34:50 -05:00
Sukil Etxenike
b272a08ab9 Corrected program strings. 2015-04-05 23:39:55 +02:00
48e918adb0 Focus is handled in Gtk, accelerators and some menu items work 2015-04-04 21:25:52 -06:00
d8581f47d2 Merge pull request #6 from sukiletxe/next-gen
Corrected documentation. Added information for session manager and block...
2015-04-04 09:33:11 -06:00
Sukil Etxenike
17601393de Corrected documentation. Added information for session manager and block /
unblock.
2015-04-04 16:54:57 +02:00
2e32fa7ef2 First great commit for Gtk. It is partially functional now 2015-04-03 16:57:08 -06:00
9356a0544f A gtk view for session manager. Key libraries for linux need to be compiled 2015-03-31 17:46:15 -06:00
c085729096 Mention to all is bound to alt+t 2015-03-30 13:03:22 -06:00
d2f7228653 Config validation, invisible interface fix 2015-03-30 10:55:56 -06:00
a513303a9a Issue reporter module has been refactored in MVC 2015-03-29 23:27:18 -06:00
ffeccbb3b6 user searches and conversation refactored 2015-03-28 05:58:57 -06:00
2a83440c44 Fixes in inverted buffers, fixes in people buffers too 2015-03-28 05:02:17 -06:00
5a1fd9bb75 TWBlue can authorise a dropbox account again 2015-03-26 17:02:58 -06:00
f8c2ac7468 The spell checker now really works 2015-03-26 16:54:20 -06:00
ae6d5b38dd Jaws interrupt fix 2015-03-26 16:38:06 -06:00
1e9d4432f3 Now is possible to delete direct messages 2015-03-26 16:20:21 -06:00
9eec1c7c44 Don't load empty searches 2015-03-26 15:55:31 -06:00
7ad1d55241 Deleted an incorrect line in the session file 2015-03-26 15:17:41 -06:00
c88c37d65b Doc changes 2015-03-25 04:56:48 -06:00
15eb0f62ea TWBlue does not open timelines for protected users when is not possible 2015-03-24 17:28:01 -06:00
2e70cbc298 Searches improvements in inverted buffers 2015-03-24 17:07:14 -06:00
f2bb173ddb A few bugfixes 2015-03-24 03:47:16 -06:00
e046fdd198 TWBlue is closed even if sessions are not started properly; remove the systray icon 2015-03-20 17:19:40 -06:00
a63d6eceb1 Multiline in tweets, searches improvements and visual changes 2015-03-20 17:04:39 -06:00
ee006eeb66 Now searches use the max_tweets_per_api_call and audioboom playback fixed 2015-03-19 12:03:34 -06:00
ac45430839 You can't delete the home timeline 2015-03-19 05:34:18 -06:00
a592f747f8 Now user action events are announced in the events buffer 2015-03-19 05:31:19 -06:00
a0f5a3c134 Ask at exit now is saved in the config file 2015-03-19 05:23:02 -06:00
977ffacd27 A login fix 2015-03-19 04:56:29 -06:00
9256a5f981 Add the conversations option to the menu bar 2015-03-19 04:50:24 -06:00
75dfaec727 Basic conversation support, a jaws fix 2015-03-18 11:45:39 -06:00
89e541be95 Open a search from the trending topics buffer is now possible 2015-03-17 12:24:29 -06:00
c7d007636e Favourites and tweets timelines are available from the menu bar 2015-03-17 11:20:04 -06:00
8c85cc25a0 Basic support for favourites timelines 2015-03-17 05:12:29 -06:00
47d0a3db67 Now user searches and view user details on retweets works better 2015-03-17 03:50:49 -06:00
e15cd86644 Two new events added in the events API 2015-03-17 03:39:59 -06:00
85a7ffc8eb German and croatian are added to language handler, message logs format has changed to partially fix #5 2015-03-16 19:24:27 -06:00
ca9f5b0c30 Some text fixes, fix for turkey locales in arrow 2015-03-15 18:24:15 -06:00
69aa27c33e A few timeline bugfixes 2015-03-14 17:38:26 -06:00
26644da7b4 Changes the text for the logout button when is needed 2015-03-12 17:55:54 -06:00
4465f6e8aa Autocompletion works for the recipient in a direct message 2015-03-12 12:24:34 -06:00
4f4b8c6447 Expand URL button is shown in the view tweet dialogue 2015-03-12 11:45:53 -06:00
5926732c26 Fix for invisible mode 2015-03-12 09:28:58 -06:00
05f75bf5d0 Autoread and mute buffer now are checkable items in the WX interface 2015-03-12 08:31:39 -06:00
e63db7b150 The systray module has been added 2015-03-12 05:17:59 -06:00
d4fc809169 Session mute and buffer mute has been added to their keyboard shorcuts 2015-03-11 17:17:29 -06:00
4613b7ada1 Autoreading is now supported 2015-03-11 16:36:22 -06:00
eb4301aeb5 Play URL now works with all URL 2015-03-11 06:11:42 -06:00
4c56850da6 Changing soundpacks from settings take effect 2015-03-11 05:54:46 -06:00
9d4063b662 Hide buffers frof the account settings dialogue is possible 2015-03-10 09:44:21 -06:00
1a81d1a536 Hide GUI at startup is fixed 2015-03-09 15:55:34 -06:00
e1b3a94b13 It saves config for the logged sessions in the exit function 2015-03-09 13:53:47 -06:00
2cc865b62c view user info is now posible from the menu bar or control+win+shift+n 2015-03-08 14:18:29 -06:00
007da773c9 Two fixes for the last commit 2015-03-08 00:36:41 -06:00
462f6fe395 a minor session bugfix, improvements in the invisible navigation 2015-03-07 20:23:41 -06:00
e65c8ec4a6 Basic update profile function, some minor bugfixes, sound playback improvements 2015-03-06 21:37:08 -06:00
373ca24a96 Session manager is now fully MVC, it can removes an account too 2015-03-05 11:27:48 -06:00
3f1b00af8f Edit keystrokes works from the menu bar, you can add more sessions from application menu 2015-03-04 17:52:27 -06:00
4d1e03fb64 Now a session is saved properly when session manager creates it 2015-03-03 13:48:57 -06:00
632e54a727 Setting up multiple accounts using the session manager 2015-03-03 13:41:41 -06:00
188d95ad6c Another authorisation bug has been fixed 2015-03-02 15:06:47 -06:00
0ba120c3dd An authorisation bugfix 2015-03-01 20:41:02 -06:00
0b12924874 Action dialogue is attached to the right keyboard shorcuts 2015-03-01 20:11:26 -06:00
d6163d6294 Merge branch 'next-gen' of https://github.com/manuelcortez/twblue into next-gen 2015-03-01 20:02:08 -06:00
1b0e8e47fa A bugfix related to muted buffers 2015-03-01 20:02:00 -06:00
ae1df13f4a Merge pull request #5 from zstanecic/next-gen
croatian localization
2015-03-01 19:59:54 -06:00
jmdaweb
1093bea416 Added windows dependencies submodule, added pa.c format files 2015-03-01 23:06:20 +01:00
1f64b80f9d modified setup for compiling the new generation 2015-02-28 08:31:19 -06:00
c571a913c5 TWBlue doesn't play audioboom boos 2015-02-26 16:10:31 -06:00
76d8c292c9 Visit website & about the program work from the help menu 2015-02-26 16:09:26 -06:00
458d607b9a Start session automatically, login and logout in realtime from the account buffer in the GUI 2015-02-26 15:21:26 -06:00
3a9a49d64c Check for updates now works from the menu bar too 2015-02-26 10:26:46 -06:00
b7193d16ee The keystroke editor now works properly 2015-02-26 10:09:13 -06:00
e7852e5e91 Comments in some functions and submodules 2015-02-25 08:39:06 -06:00
35f70e050a Fixed locales in arrow for Catala and Basque (Galician is pending) 2015-02-22 18:45:26 -06:00
zstanecic
db8da293d3 croatian localization 2015-02-22 20:29:45 +01:00
88d7898dba The updater system is executed at the startup 2015-02-22 11:38:22 -06:00
e906d04f18 Now jaws doesn't say the keys pressed in invisible mode 2015-02-22 10:07:56 -06:00
d9d0efab2d Updater has been refactored 2015-02-16 17:21:27 -06:00
10d0b8ba6b Delete search buffers 2015-02-16 16:37:56 -06:00
f13d9113a4 Installer scripts 2015-02-13 10:01:09 -06:00
066814724b A bugfix on buffers deletion 2015-02-12 13:18:24 -06:00
a3e7effb83 Improvements in the tweet and dm dialogue 2015-02-12 13:10:42 -06:00
be9ede43a3 Mention and direct message for people buffers 2015-02-12 12:59:56 -06:00
d4ea961dae A bugfix in deleting a timeline 2015-02-12 10:35:13 -06:00
de7118caf4 Tweet's timelines are possible, a way to restart stream endpoint has been created 2015-02-12 10:29:51 -06:00
5eb4f74242 Delete timelines is now possible 2015-02-11 16:14:25 -06:00
d2cffe65d8 Don't load unallowed tweets 2015-02-08 05:48:40 -06:00
42aea45a41 A bug exiting TWBlue on GUI mode 2015-02-07 13:55:52 -06:00
2dd0cc8fbb Fixes for actions in empty buffers 2015-02-07 10:43:37 -06:00
0ede5bfcb0 Clear buffers is now possible, keys for 64 bits has been added 2015-02-07 10:23:23 -06:00
62b0bc76a8 A new library for storing app keys is added 2015-02-04 17:00:03 -06:00
2a7e09b166 We have a logo 2015-02-03 10:23:43 -06:00
9ad2d6cc69 Get_more_items has been implemented 2015-02-03 09:59:18 -06:00
1fbee07de2 Geo support has been implemented 2015-02-03 05:10:01 -06:00
3b1d8eb48a Trending topic buffers are loaded during init 2015-02-03 04:39:46 -06:00
103b62719e Trending topics support has been added 2015-02-01 21:13:18 -06:00
e93f0f4980 User actions dialog now works, the spell checker module ignores twitter usernames, hashtags, URL and email addresses 2015-02-01 00:49:03 -06:00
ff65b73232 Account settings works properly; auto completion has been implemented and improved, TWBlue loads in a threaded mode 2015-01-27 17:09:28 -06:00
51957125c1 Global settings works, basic support for account settings 2015-01-25 02:38:17 -06:00
af087508b0 Timelines fix and some minor bugfixes too 2015-01-22 08:54:41 -06:00
399de75d64 Invisible navigation fixes, URLPlayer volume fixes, shutdown fix 2015-01-20 22:07:13 -06:00
c25412bd32 Sound object refactored, more invisible shorcuts, global configuration and fixed buffer navigation in invisible mode 2015-01-20 15:40:33 -06:00
7b1f2f9482 Basic invisible interface support 2015-01-19 12:15:57 -06:00
fd70bedc05 Adding log information to events 2015-01-18 17:19:39 -06:00
bd1b0b5e32 Some stream improvements 2015-01-13 12:31:37 -06:00
02bdeae4e6 Changed the default soundpack to adium_glset 2015-01-08 08:32:53 -06:00
4979b2aa66 some useful functions 2015-01-08 08:01:45 -06:00
a84d35c6af Now the buttons in the buffers are connected to their functions; datetime and languageHandler improvements. 2015-01-05 06:05:31 -06:00
32884d3bf2 Delete/view tweets and direct messages has been implemented; some buffer fixes. 2015-01-05 05:33:09 -06:00
a599b37e6d Retweets, add and remove from favourites are supported 2015-01-02 10:53:33 -06:00
5cb4e0fef7 trends_updated and geo are in the sounds tutorial 2015-01-02 10:06:45 -06:00
7d6bf53019 Now it's possible ignore session at strtup 2015-01-02 09:55:27 -06:00
29d434bcd4 Reply and direct message are supported, updated locales and code improvements 2015-01-02 09:38:44 -06:00
186f70afc3 Some code changes to having a more consistent MVC application. 2015-01-01 21:40:57 -06:00
a6dd5d602f Uploading photos is now supported for tweets 2014-12-31 00:07:27 -06:00
2895d02cdc Next-gen: Audio uploader is now MVC, so tweets can include audios. Sounds tutorial is MVC and has been improved. 2014-12-29 20:58:30 -06:00
ca288de515 next-gen: updated widgetUtils 2014-12-29 14:35:08 -06:00
567e612c72 Spell checker is refactored in an MVC pattern and now works for tweets 2014-11-16 22:30:58 -06:00
1b3b684cbc Some documentation added on the wx part on widgetUtils 2014-11-16 21:37:29 -06:00
cd0aab90ef Support for shorten and unshorten URL's on tweets has been added 2014-11-15 19:40:19 -06:00
05dc8e5a31 TWBlue should use direct messages on all accounts #fix #next-gen 2014-11-14 16:02:15 -06:00
606ab6d6fc Post tweets and translate is possible in the next-gen 2014-11-12 22:37:52 -06:00
f54d9394b7 The next generation branch has been added 2014-11-12 20:41:29 -06:00
333 changed files with 52815 additions and 37548 deletions

8
.gitignore vendored
View File

@@ -3,11 +3,17 @@
src/build/
src/dist/
src/config/
src/config1/
src/config2/
src/config3/
src/dropbox/
src/logs/
src/documentation/
src/sounds/iOs
src/oggenc2.exe
src/bootstrap.exe
src/Microsoft.VC90.CRT
src/Microsoft.VC90.MFC
src/launcher.bat
src/launcher.bat
src/sounds/iOs
release-snapshot/

View File

@@ -1,9 +1,10 @@
TWBlue
TWBlue -
======
TWBlue, an accessible, open source and multiplatform twitter application.
Copyright (C) 2015. [Technow S.L.](https://www.technow.es)
TW Blue is an app designed to use Twitter in a simple and fast way and avoiding, as far as possible, the consumtion of excessive resources of the machine where its running. With this app youll have access to twitter features such as:
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,
@@ -11,11 +12,11 @@ TW Blue is an app designed to use Twitter in a simple and fast way and avoiding,
* See your friends and followers,
* Follow, unfollow, block and report users as spam,
* Open a users timeline, which will allow you to get that users tweets separately,
* Open URL addresses when attached to a tweet or direct message,
* Play various file and URL types which contain audio
* Open URLs when attached to a tweet or direct message,
* Play audio tweets
* and more!
See the [TWBlue's webpage](http://twblue.com.mx) for more details.
See [TWBlue's webpage](http://twblue.es) for more details.
## Using TWBlue from sources
@@ -59,6 +60,7 @@ setuptools install a script, called easy_install. You can find it in the python
The -z switch unzips the package, instead of installing it compressed. If you add the --upgrade switch, you can upgrade a package to its latest version. The following packages need to be installed:
* pypubsub
* dropbox
* configobj
* requests-oauthlib
@@ -66,6 +68,7 @@ setuptools install a script, called easy_install. You can find it in the python
* pygeocoder
* suds
* arrow
* goslate
* markdown
easy_install will automatically get the additional libraries that these packages need to work properly.
@@ -85,15 +88,15 @@ This dependency has been built using pure basic 4.61. Its source can be found at
### 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 src directory into the repo, and type the following command:
Now that you have installed all these packages, you can run TW Blue from source using a command prompt. Navigate to the repo's src directory, and type the following command:
python main.py
If necesary, change the first part of the command to reflect where is your python executable. You can run TW Blue using python x86 and x64
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
### 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 in 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.
To build it, run the following command from the src folder:
@@ -103,4 +106,4 @@ To build it, run the following command from the src folder:
### How to generate a translation template
You must run the gen_pot.bat file, located in the tools directory. Your python installation should be in your path environment variable. The pot file will appear in the tools directory too.
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.

View File

@@ -1,4 +1,5 @@
Manuel E. Cortéz
Bill Dengler
Johana Hidrobo
Marcelo Sosa
Isabel del Castillo

7
doc/application.py Normal file
View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
name = 'TWBlue'
snapshot = False
if snapshot == False:
version = "0.80"
else:
version = "7"

View File

@@ -13,7 +13,7 @@ documentation.append(_(u"""
documentation.append(_(u"""## Warning!"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""You're reading documentation produced for a program still in development. The object of this manual is explaining 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 document may change in the near future, so it is advisable to keep an eye on it from time to time to avoid missing too much out."""))
documentation.append(_(u"""You're 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 document may change in the near future, so it is advisable to keep an eye on it from time to time to avoid missing too much out."""))
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)"""))
@@ -48,38 +48,36 @@ documentation.append(_(u"""
documentation.append(_(u"""First off, 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 gets data such as your username and 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)."""))
documentation.append(_(u"""
"""))
#$documentation.append(_(u"""When executed, if you have not previously configured the program, it will show a dialogue box where it tells you'll be taken to Twitter in order to authorise the application as soon as you press OK. To begin the authorisation process, press the only available button on the box."""))
documentation.append(_(u"""Whether this is the first time you open TWBlue or you don't have any session, you will see the session manager. This dialog allows you to authorise as many accounts as you wish. If you press the "new account" button a dialog will tell you that your default browser will be opened in order to authorise the application. Press "yes" 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 user name and password if you're not already logged in, look for the authorise button, and press it."""))
documentation.append(_(u"""
"""))
#$documentation.append(_(u"""Read the instructions you will get if the process is successful. In summary, you will be given a numeric code with several digits you must paste on an edit field open by the application on another window."""))
#$documentation.append(_(u"""
#$"""))
#$documentation.append(_(u"""Paste the verification code, and press the enter key. """))
#$documentation.append(_(u"""
#$"""))
### Add here the instructions on how to deal with the session manager.
documentation.append(_(u"""Once you've authorised your twitter account, Twitter will redirect you to a web page which will notify you that TWBlue has been authorised successfully. Now you are able to close that window and come back to the session manager. You will see on the session list a new item temporally 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 dialog. By default, TWBlue starts all the configured sessions, 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 are being updated."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""When the process is finished,the program will play another sound, and the screen reader will say "ready"."""))
documentation.append(_(u"""When the process is finished, the program will play another sound, and the screen reader will say "ready"."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## The program's interface"""))
documentation.append(_(u"""
"""))
### Add the new GUI description here
#$documentation.append(_(u"""The easiest way to describe the graphical interface of the application is a window with a menu bar with five menus (application, tweet, user, buffer and help), a list with several elements, and, in most cases, three buttons: tweet, retweet and reply. The actions available for each element are described below."""))
#$documentation.append(_(u"""
#$"""))
#$documentation.append(_(u"""Elements on the lists may be tweets, direct messages or users. TW Blue creates different tabs for each list, which can be sent tweets, main timeline tweets, favourites, or direct messages, and each tab contains a single type of tweet. These tabs are called lists or buffers."""))
#$documentation.append(_(u"""
#$"""))
#$documentation.append(_(u"""To switch from list to list press control-tab to go forward, and control-shift-tab to go back. Screen readers will announce the list that gains the focus at all times. These are the basic lists of TW Blue, which are configured by default."""))
#$documentation.append(_(u"""
#$"""))
documentation.append(_(u"""The easiest way to describe the graphical user interface of TWBlue is saying that the application has a window which contains a menu bar with five menus (application, tweet, user, buffer and help); one tree view, one list of items and, mostly in every case, three buttons: Tweet, retweet and reply. The actions that are available for every item will be described later."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In the tree view are inserted buffers which are lists to manage the processed data. When you configure a new session on TWBlue and start it, your account is the root of the tree view. Inside of it many buffers are created. Each one of them may contain some of the items which TWBlue works with: Tweets, direct messages, users, trends or events. According to the buffer you were, you will be able to make different actions with these items."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The following is a description for every kind of TWBlue's buffer and the kind of items they work with."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Home: it 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 on this list."""))
documentation.append(_(u"""* Direct messages: here go the private direct messages you exchange with users you follow and who follow you back. This list only shows received messages."""))
@@ -92,27 +90,22 @@ documentation.append(_(u"""* Events: An event is anything that happens on Twitte
documentation.append(_(u"""* Lists: A list is similar to a temporary 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 favorites: You can have TW Blue create a buffer containing tweets favorited by a particular user."""))
### add here the trending buffers description.
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"""Note: In this version of TW Blue, you will be able to see up to (or around) 400 friends and followers in their respective buffers. In the next version, we will provide a solution for those who have more to be able to see them."""))
#$documentation.append(_(u"""
#$"""))
#$documentation.append(_(u"""Bear in mind the default configuration only allows getting the last 200 tweets for the home,, mentions, direct messages, and user timeline lists. You can change this on the setup dialogue. For the sent list, the last 200 tweets and the last 200 sent direct messages will be retrieved. Future versions will allow changing this parameter."""))
#$documentation.append(_(u"""
#$"""))
documentation.append(_(u"""If there's a URL on a tweet TW Blue will try to open it when you press enter on it. If there are several, it will show you a list with all of them so you choose the one you want. If you're on the followers or friends buffer, the enter key will show you additional information on them."""))
documentation.append(_(u"""If there's a URL on a tweet, TW Blue will try to open it when you press enter on it. If there are several, it will show you a list with all of them so you choose the one you want. If you're on the followers or friends buffer, the enter key will show you additional information about them."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If you press control-enter, TW Blue will try to play the audio from the focused tweet, as long as it has a URL. If it has the #audio hashtag, you will hear a sound when it is selected, letting you know you can try to play it. However, a tweet can be missing the hashtag and TW Blue will still be able to play it so long as it contains a URL with audio."""))
documentation.append(_(u"""
"""))
### Add information about the GEO location in tweets.
documentation.append(_(u"""You will also hear a sound when you see any tweet containing geographical information. You can see someone's location by selecting the option "view address" from the tweet menu on the menu bar."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Controls"""))
documentation.append(_(u"""
"""))
### add more information about using invisible shorcuts in the GUI mode in the next variable.
documentation.append(_(u"""Beginning with the 0.36 version, there's support for an interface which does not require a visible window. It can be activated by pressing control-m, or choosing hide window from the application menu. This interface is entirely driven through shortcut keys. These shortcuts are different from those used to drive the graphical interface. This section describes both the graphical and the invisible interface."""))
documentation.append(_(u"""Beginning with the 0.36 version, there's support for an interface which does not require a visible window. It can be activated by pressing control-m, or choosing hide window from the application menu. This interface is entirely driven through shortcut keys. These shortcuts are different from those used to drive the graphical interface. By default, you can't use the invisible interface shortcuts on the GUI. It has been made this way to keep compatibility with applications like TheQube and Chicken nugget which may use the same shortcuts. If you wish to have available the invisible interface shortcuts even if you are using the GUI, activate this option on the General tab of the preferences dialogue. This section describes both the graphical and the invisible interface."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### The graphical user interface (GUI)"""))
@@ -124,8 +117,7 @@ documentation.append(_(u"""
documentation.append(_(u"""#### Buttons on the application"""))
documentation.append(_(u"""
"""))
### Add information on spell correction, translate, attach images and audio.
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. You may use the shorten and expand URL buttons to comply with the character limit. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will say an error message in English describing the problem."""))
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. You may use the shorten and expand URL buttons to comply with the character limit. You can translate your message, upload a picture, check spelling or attach audio by selecting one of the available buttons in the dialogue. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will say an error message in English describing the problem."""))
documentation.append(_(u"""* Retweet: this button retweets the message you're reading. After you press it, you'll be asked if you want to add a comment or simply send it as written."""))
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 like 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 mentioned on the tweet, you can press shift-tab and press 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 on 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."""))
@@ -143,206 +135,77 @@ documentation.append(_(u"""
documentation.append(_(u"""##### Application menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Session manager: 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 box where you can update your information on Twitter: name, location, URL 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 dialog where you can search for tweets or users on Twitter."""))
documentation.append(_(u"""* View trending topics: It opens a buffer to get the trending topics of a country or a city. You'll be able to select from a dialog if you wish to get countries' trends or cities' trends and choose one from the selected list. The trending topics buffer will be created once pressing "ok" on this dialog. Remember this kind of buffer will be updated every five minutes."""))
documentation.append(_(u"""* Lists Manager: This dialog 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 temporary timelines."""))
documentation.append(_(u"""* Sound tutorial: Opens a dialog where you can familiarize yourself with the different sounds of the program."""))
documentation.append(_(u"""* Preferences: opens up a preference dialogue box from which you can control some of the program settings. The options need no explanation."""))
documentation.append(_(u"""* Quit: asks whether you want to exit the program. If the answer is yes, it shuts the application down."""))
documentation.append(_(u"""* Sounds tutorial: Opens a dialog where you can familiarize yourself with the different sounds of the program."""))
documentation.append(_(u"""* Edit keystrokes: It opens a dialog where you can see and re edit the invisible interface shortcuts."""))
### Add description for the global settings and accounts settings options.
documentation.append(_(u"""* Quit: asks whether you want to exit the program. If the answer is yes, it shuts the application down. If you wish TWBlue not to ask you for confirmation before exiting, uncheck the checkbox from the preferences dialogue."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Tweet menu {#tweet}"""))
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"""* Mark as favourite: marks the tweet you're viewing as a favourite."""))
documentation.append(_(u"""* Remove tweet 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 under focus. You can read the text with the cursors. It's the same dialogue box used to write tweets on."""))
documentation.append(_(u"""* Show tweet: opens up a dialogue box where you can read the tweet, direct message, friend or follower under focus. You can read the text with the arrow keys. It's the same dialogue box used to write tweets on."""))
documentation.append(_(u"""* View address: If the selected tweet has geographical information, TWBlue may display a dialog where you can read the tweet address. This address is got by sending the geographical coordinates of the tweet to Google maps."""))
### Add description for view conversation feature
documentation.append(_(u"""* Delete: permanently removes the tweet or direct message you're on 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 {#user}"""))
documentation.append(_(u"""##### User menu"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Bear in mind the four topmost items on this menu open up the same dialogue box. This box has an edit field where you can choose the user you want to act on, by using up and down arrows or by writing the text in yourself. Afterwards you will find a group with four radio buttons to follow, unfollow, report as spam and block. If you choose the follow menu item, the radio button on the dialogue box will be set to follow, and the same applies to unfollow, report as spam and block. Press OK to try to carry out the action. If it doesn't succeed, you'll hear the error message in English."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The remaining items on the menu are described below:"""))
documentation.append(_(u"""The available actions you can choose are described below:"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Follow: Follows a user. This means you'll see his/her tweets on your main timeline, and if he/she also follows you, you'll be able to interchange direct messages."""))
documentation.append(_(u"""* Unfollow: Stops following a user, which causes you not being able to see his/her tweets on your main timeline neither interchanging direct messages."""))
documentation.append(_(u"""* Mute: While muting someone, TWBlue won't show you nor his/her tweets on your main timeline; neither you'll see that person's mentions. But you both will be able to interchange direct messages. The muted user is not informed of this action."""))
documentation.append(_(u"""* Unmute: It turns the way TWBlue treats this user to its normal way. You will see his/her tweets and mentions again."""))
documentation.append(_(u"""* Report as spam: It suggests twitter this user is performing prohibited practices on the social network."""))
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"""* 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. This option will open a dialog where you can select the user you wish to add. Next, you will be asked to select the list you wish to add them to. Afterwards, the list will contain a new member and their tweets will show up there."""))
### add description for remove from list and view lists.
documentation.append(_(u"""* View user profile: opens up a dialogue box to choose the user whose profile you want to browse."""))
documentation.append(_(u"""* Timeline: opens up a dialogue box to choose whose user you want a timeline for. Create it by pressing enter. If you try it with a user that has no tweets, the program will fail. If you try creating an already existing timeline the program will warn you and will not create it again."""))
documentation.append(_(u"""* 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 try it with a user that has no tweets, the program will fail. If you try creating an already existing timeline the program will warn you and will not create it again."""))
documentation.append(_(u"""* View favourites: Opens a buffer where you can see what tweets have been favorited by a particular user."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Buffer menu{#buffer}"""))
documentation.append(_(u"""##### Buffer menu"""))
documentation.append(_(u"""
"""))
### add description for load previous items.
documentation.append(_(u"""* Mute buffer: Mutes notifications of a particular buffer so you will not hear when new tweets arrive."""))
documentation.append(_(u"""* autoread tweets for this buffer: When enabled, the screen reader or SAPI 5 (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"""* Remove buffer: dismiss the list you're on."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""##### Help menu {#help}"""))
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"""* What's new in this version?: opens up a document with the list of changes from the current version down to the first."""))
documentation.append(_(u"""* Check for updates: every time you open the program it automatically checks for new versions. If there are, it will ask you if you want to download it. If you accept, it will do so, after which it will install it and ask you to let it restart itself, which it does automatically. This item checks for new updates without having to restart the application."""))
documentation.append(_(u"""* TW Blue's website: visit our [home page](http://twblue.com.mx) where you can find all relevant information and downloads for TW Blue and become a part of the community."""))
documentation.append(_(u"""* Report a bug: opens up a dialogue box to report a bug by filling a couple of fields: the title and a short description of what happened. Pressing enter will send the report. If the operation doesn't succeed the program will show a warning."""))
documentation.append(_(u"""* Check for updates: every time you open the program it automatically checks for new versions. If there is any, it will ask you if you want to download it. If you accept, it will do so, after which it will install it and ask you to let it restart itself, which it does automatically. This item checks for new updates without having to restart the application."""))
documentation.append(_(u"""* Report a bug: opens up a dialogue box to report a bug by filling a couple of fields. Pressing enter will send the report. If the operation doesn't succeed the program will show a warning."""))
documentation.append(_(u"""* TW Blue's website: visit our [home page](http://twblue.es) where you can find all relevant information and downloads for TW Blue and become a part of the community."""))
documentation.append(_(u"""* About TW Blue: shows the credits of the program."""))
documentation.append(_(u"""
documentation.append(_(u"""...
"""))
documentation.append(_(u"""### Invisible interface {#invisible_interface}"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If you press control-m, or if you choose hide window from the application menu, you will activate an interface that cannot be used in the usual way, because it is invisible."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Every action on the invisible interface is done through keyboard shortcuts, even browsing lists. Eventually you may open dialogue boxes and these will be visible, but not the application's main window. Read the section on invisible interface shortcuts to know which ones you can use for the time being."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Keyboard shortcuts for the graphical interface {#shortcuts}"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Instead of using the buttons and menus, most actions can be carried out by pressing a key combination. The ones available at present are described below:"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Enter: open a URL. If there are more than one you will get a list that will allow you to choose the one you want. On the friends or followers lists it will show details on the selected item."""))
documentation.append(_(u"""* Control-enter: attempt to play audio from URL."""))
documentation.append(_(u"""* F5: decrease sounds volume. It affects the program sounds as well as audio played from the program."""))
documentation.append(_(u"""* F6: increase application sounds volume."""))
documentation.append(_(u"""* Control-n: open new tweet dialogue."""))
documentation.append(_(u"""* Control-m: hide window."""))
documentation.append(_(u"""* Control-q: quit."""))
documentation.append(_(u"""* Control-r: open reply tweet dialogue."""))
documentation.append(_(u"""* Control-shift-r: Retweet."""))
documentation.append(_(u"""* Control-d: send direct message."""))
documentation.append(_(u"""* Control-f: mark as favourite."""))
documentation.append(_(u"""* Control-shift-f: remove from favourites."""))
documentation.append(_(u"""* Control-shift-v: view tweet."""))
documentation.append(_(u"""* Control-s: follow a user."""))
documentation.append(_(u"""* Control-shift-s: unfollow a user."""))
documentation.append(_(u"""* Control-k: block a user."""))
documentation.append(_(u"""* Control-shift-k: report as spam."""))
documentation.append(_(u"""* Control-i: open user's timeline."""))
documentation.append(_(u"""* Control-shift-i: remove timeline."""))
documentation.append(_(u"""* Control-p: edit profile."""))
documentation.append(_(u"""* Delete: remove tweet or direct message."""))
documentation.append(_(u"""* Shift-delete: empty the buffer removing all the elements. This doesn't remove them from Twitter itself."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""### Keyboard shortcuts for the invisible interface {#invisible_shortcuts}"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""These are the shortcuts you may use from the invisible interface. Bear in mind that when the graphical user interface is shown you may not use these. By "win" the left windows key is intended."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Control+win+up arrow: go up on the current list."""))
documentation.append(_(u"""* Control+win+down arrow: go down on the current list."""))
documentation.append(_(u"""* Control+win+left arrow: go to the previous tab."""))
documentation.append(_(u"""* Control+win+right arrow: go to the next tab."""))
documentation.append(_(u"""* Control+win+home: go to the first element on the list."""))
documentation.append(_(u"""* Control+win+end: go to the last element on the list."""))
documentation.append(_(u"""* Control+win+page down: move 20 elements down on the current list."""))
documentation.append(_(u"""* Control+win+page up: move 20 elements up on the current list."""))
documentation.append(_(u"""* Control+win+alt+up arrow: increase volume by 5%."""))
documentation.append(_(u"""* control+win+alt+down arrow: decrease volume by 5%."""))
documentation.append(_(u"""* Control+win+enter: open URL on the current tweet, or further information for a friend or follower."""))
documentation.append(_(u"""* control+win+alt+enter: attempt to play audio."""))
documentation.append(_(u"""* control+win+m: show the graphical interface, turning the invisible one off."""))
documentation.append(_(u"""* Control+win+n: new tweet."""))
documentation.append(_(u"""* Control+win+r: reply to tweet."""))
documentation.append(_(u"""* Control+win+shift-r: retweet."""))
documentation.append(_(u"""* Control+win+d: send direct message."""))
documentation.append(_(u"""* Control+win+delete: remove a tweet or direct message."""))
documentation.append(_(u"""* Control+win+shift+delete: empty the buffer removing all the elements. This doesn't remove them from Twitter itself."""))
documentation.append(_(u"""* Win+alt+f: mark as favourite."""))
documentation.append(_(u"""* Win+alt+shift+f: remove from favourites."""))
documentation.append(_(u"""* Control+win+s: follow a user."""))
documentation.append(_(u"""* Control+win+shift+s: unfollow a user."""))
documentation.append(_(u"""* Control+win+alt+n: see user details."""))
documentation.append(_(u"""* Control+win+v: show tweet on an edit box."""))
documentation.append(_(u"""* Control+win+i: open user timeline."""))
documentation.append(_(u"""* Control+win+shift+i: remove user timeline."""))
documentation.append(_(u"""* Alt+win+p: edit profile."""))
documentation.append(_(u"""* Control+win+c: Copy to clipboard."""))
documentation.append(_(u"""* Control+win+space: Listen the current message."""))
documentation.append(_(u"""* Control+win+a: Add to list."""))
documentation.append(_(u"""* Control+win+shift+a: Remove from list."""))
documentation.append(_(u"""* Control+Win+Shift+M: Mutes/unmutes the active buffer."""))
documentation.append(_(u"""* Control+Win+E: toggles the automatic reading of incoming tweets in the active buffer."""))
documentation.append(_(u"""* Control+Win+Shift+Up arrow: move up one tweet in the conversation."""))
documentation.append(_(u"""* Control+Win+Shift+Down arrow: move down one tweet in the conversation."""))
documentation.append(_(u"""* Win+Alt+M: Globally mute/unmute TW Blue."""))
documentation.append(_(u"""* control+win+minus: Search on twitter."""))
documentation.append(_(u"""* Control+win+f4: quit."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Lists {#lists}"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""One of the most useful features of Twitter is the ability to create lists. Lists allow you to group users whose tweets you wish to see together instead of viewing their individual buffers. A common example of this would be if you follow multiple tech news accounts; it would be more convenient to have, for example, a "Tech News" list in which you can see tweets from these similar accounts together. A temporary buffer, such as what is created when you are viewing an individual person's timeline, is created and you can add/remove people from the list."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In TW Blue, we have begun working on this feature little by little. It is still experimental but is in working condition. Below, we will explain how to configure lists."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* First, you will need to open the lists manager which can be found under the application menu."""))
documentation.append(_(u"""* In the lists manager, you will first see the lists you have created followed by those which you are a member. If you see no lists, it means that you have not created any and that you are not a part of any list."""))
documentation.append(_(u"""* You will then see a group of buttons: Create a New List, Edit, Remove and Open in Buffer. Perhaps the last one is a bit less self-explanatory: it will open the list in a buffer similar to when opening someone's timeline. """))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Once you have created a new list, the next step will be to add users to it. If you were to open it in a buffer right now, it would be empty and no tweets would show up in it. To add users, follow these steps:"""))
documentation.append(_(u"""* While browsing your tweets, find a tweet from the user which you wish to add to a list. Next, press ctrl+win+A or select "Add to List" from the User menu."""))
documentation.append(_(u"""* A dialog will then appear asking for the user which you wish to add. The name of the user whose tweet you just selected should already be in the box. Simply confirm that it is correct and press the "OK" button."""))
documentation.append(_(u"""* Another dialog will appear showing all of your lists. Arrow to the one you want and press the "Add" button."""))
documentation.append(_(u"""* To remove a user from a list, repeat the same process but press ctrl+win+shift+A and, from the dialog that appears, choose the list from which you wish to remove the selected user."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Reporting bugs from the web {#reporting}"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Note: if you're running the program you can also report a bug from it, by using the help menu item. This process only allows for two edit fields and takes care of the rest. These steps are described for those who can't run the program, don't have it opened at a given moment, or simply want to report from the web instead of using the integrated bug reporting system."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""All things under the sun (yes, this includes computer programs) are very far from being perfect, so often you may find unforeseen bugs in the application. But as our intent is to always improve you're free (what's more, it would be great if you did) to report the bugs you find on the program so they can be reviewed and eventually fixed"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""In order to open the bug tracker's web, [follow this link](http://twblue.com.mx/errores/bug_report_page.php) It's a website with a form where you must fill several fields. Only three of them are really required (those marked with a star), but the more you can fill the better."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Here are the different form fields and what you must enter on each. Remember only fields mark witha star are required:"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""* Category: this field allows to choose what category to assign the bug to. You can choose general, if it's a program error, or documentation, if you have found a mistake in this manual or the changes list. This is a required field."""))
documentation.append(_(u"""* Reproducibility: here you must indicate how easy or hard it is to reproduce the bug. Available options are unknown, not reproducible, not attempted (by default), random, sometimes, or always. Depending on whether you can reproduce the error or not, you should choose the one closest to your situation. If you're making a feature request, this field is irrelevant."""))
documentation.append(_(u"""* Severity: here you choose how much it affects the program. Available options are functionality (choose this for a feature request), trivial, text, setting, minor, major, failure, or crash. Note the options go in increasing order. Choose the one which fits the situation best. If you're not sure which to choose you can leave it as it is."""))
documentation.append(_(u"""* Priority: choose according to the importance of the bug or feature requested. Available options are none, low, normal, high, urgent, and immediate."""))
documentation.append(_(u"""* Choose profile: here you can choose between 32 or 64 bit architecture and OS (Windows 7 for now). If they don't fit, you can fill the edit fields below with your specific information."""))
documentation.append(_(u"""* Product version: choose the version of the program you're running in order to find out when the error was introduced. This field will contain a sorted list of the available versions. Although it's not required, it would help a lot in quickly finding the bug."""))
documentation.append(_(u"""* Summary: a title for the bug, explaining in a few words what the problem is. It's a required text field."""))
documentation.append(_(u"""* Description: this required field asks you to describe in more detail what happened to the program."""))
documentation.append(_(u"""* Steps to reproduce: this field is used if you know how to cause the error. It's not required, but it would help a lot knowing how the program gets to the error in order to track it down."""))
documentation.append(_(u"""* Additional information: if you have a comment or note to add, it can go here. It's not required."""))
documentation.append(_(u"""* File attachment: you can attach the TW Blue.exe.log generated due to the bug. It is not required."""))
documentation.append(_(u"""* Visibility: choose if you want the bug to be publically visible or private. By default it's public, and it's recommended to keep it that way."""))
documentation.append(_(u"""* Send report: press the button to send the report and have it looked into."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Many thanks for your participation in reporting bugs and trying out new functionality."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""## Contact {#contact}"""))
documentation.append(_(u"""## Contact"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If what's explained in this document is not enough, if you want to collaborate 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.com.mx)"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""---"""))
documentation.append(_(u"""Copyright © 2013-2014. Manuel Cortéz"""))
documentation.append(_(u"""Copyright © 2013-2015. Manuel Cortéz"""))

23
mysc/keys/api_keys.c Normal file
View File

@@ -0,0 +1,23 @@
#include "api_keys.h"
char *get_api_key(){
return "key\0";
}
char *get_api_secret(){
return "secret_key\0";
}
char *get_dropbox_api_key(){
return "key\0";
}
char *get_dropbox_api_secret(){
return "secret_key\0";
}
char *get_twishort_api_key(){
return "key\0";
}
char *get_bts_user(){
return "user\0";
}
char *get_bts_password(){
return "pass\0";
}

12
mysc/keys/api_keys.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef _API_KEYS_H
#define API_KEYS_H
char *get_api_key();
char *get_api_secret();
char *get_dropbox_api_key();
char *get_dropbox_api_secret();
char *get_twishort_api_key();
char *get_bts_user();
char *get_bts_password();
#endif

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 182 KiB

View File

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 379 B

View File

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 103 B

View File

Before

Width:  |  Height:  |  Size: 119 B

After

Width:  |  Height:  |  Size: 119 B

View File

@@ -20,8 +20,8 @@ CommercialUse=true
EULAVersion=2
[Version]
PackageVersion=0.51.0.0
DisplayVersion=0.51
PackageVersion=0.80.0.0
DisplayVersion=0.80
[Control]
Icons=1

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 168 B

View File

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,89 +1,89 @@
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "x64.nsh"
CRCCheck on
XPStyle on
Name "TW Blue"
OutFile "TWBlue_setup.exe"
InstallDir "$PROGRAMFILES\twblue"
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
RequestExecutionLevel admin
SetCompress auto
SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TW Blue"
VIAddVersionKey LegalCopyright "Copyright 2014 Manuel Cortez."
VIAddVersionKey ProductVersion "0.51"
VIAddVersionKey FileVersion "0.51"
VIProductVersion "0.51.0.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
!insertmacro MUI_PAGE_DIRECTORY
var StartMenuFolder
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_LINK "Visit TW Blue 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
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Italian"
!insertmacro MUI_LANGUAGE "Finnish"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "PortugueseBR"
!insertmacro MUI_LANGUAGE "Polish"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Hungarian"
!insertmacro MUI_LANGUAGE "Turkish"
!insertmacro MUI_LANGUAGE "Arabic"
!insertmacro MUI_LANGUAGE "Galician"
!insertmacro MUI_LANGUAGE "Catalan"
!insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_RESERVEFILE_LANGDLL
Section
SetShellVarContext All
SetOutPath "$INSTDIR"
${If} ${RunningX64}
File /r TWBlue64\*
${Else}
File /r TWBlue\*
${EndIf}
CreateShortCut "$DESKTOP\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue on the web.lnk" "http://twblue.es"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
!insertmacro MUI_STARTMENU_WRITE_END
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TW Blue"
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 Cortez"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.51"
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" 51
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd
Section "Uninstall"
SetShellVarContext All
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
RMDir /r /REBOOTOK $INSTDIR
Delete "$DESKTOP\TW Blue.lnk"
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
SectionEnd
Function .onInit
${If} ${RunningX64}
StrCpy $instdir "$programfiles64\twblue"
${EndIf}
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "x64.nsh"
CRCCheck on
XPStyle on
Name "TWBlue"
OutFile "TWBlue_setup.exe"
InstallDir "$PROGRAMFILES\twblue"
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
RequestExecutionLevel admin
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"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
!insertmacro MUI_PAGE_DIRECTORY
var StartMenuFolder
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!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
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Italian"
!insertmacro MUI_LANGUAGE "Finnish"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "PortugueseBR"
!insertmacro MUI_LANGUAGE "Polish"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Hungarian"
!insertmacro MUI_LANGUAGE "Turkish"
!insertmacro MUI_LANGUAGE "Arabic"
!insertmacro MUI_LANGUAGE "Galician"
!insertmacro MUI_LANGUAGE "Catalan"
!insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_RESERVEFILE_LANGDLL
Section
SetShellVarContext All
SetOutPath "$INSTDIR"
${If} ${RunningX64}
File /r TWBlue64\*
${Else}
File /r TWBlue\*
${EndIf}
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
!insertmacro MUI_STARTMENU_WRITE_END
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TWBlue"
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" "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" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd
Section "Uninstall"
SetShellVarContext All
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
RMDir /r /REBOOTOK $INSTDIR
Delete "$DESKTOP\TWBlue.lnk"
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
SectionEnd
Function .onInit
${If} ${RunningX64}
StrCpy $instdir "$programfiles64\twblue"
${EndIf}
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd

View File

@@ -5,40 +5,32 @@ user_name = string(default="")
ignored_clients = list(default=list())
[general]
language = string(default="system")
relative_times = boolean(default=True)
hide_gui = boolean(default=False)
voice_enabled = boolean(default=False)
max_api_calls = integer(default=1)
max_tweets_per_call = integer(default=100)
reverse_timelines = boolean(default=False)
time_to_check_streams = integer(default=30)
announce_stream_status = boolean(default=True)
ask_at_exit = boolean(default=True)
use_invisible_keyboard_shorcuts = boolean(default=False)
retweet_mode = string(default="ask")
persist_size = integer(default=0)
buffer_order = list(default=list('home','mentions','dm','sent_dm','sent_tweets','favorites','followers','friends','blocks','muted','events'))
[sound]
volume = float(default=1.0)
input_device = string(default="Default")
output_device = string(default="Default")
session_mute = boolean(default=False)
current_soundpack = string(default="default")
global_mute = boolean(default=False)
sndup_api_key = string(default="")
[other_buffers]
show_friends = boolean(default=True)
show_followers = boolean(default=True)
show_favourites = boolean(default=True)
show_events = boolean(default=True)
show_blocks = boolean(default=False)
show_muted_users = boolean(default=False)
timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
trending_topic_buffers = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list())
autoread_buffers = list(default=list(mentions, direct_messages, events))
[mysc]
spelling_language = string(default="")
@@ -48,48 +40,3 @@ save_friends_in_autocompletion_db = boolean(default=False)
[services]
dropbox_token=string(default="")
[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")
conversation_up = string(default="control+win+shift+up")
conversation_down = string(default="control+win+shift+down")
show_hide = string(default="control+win+m")
compose = string(default="control+win+n")
reply = string(default="control+win+r")
retweet = string(default="control+win+shift+r")
dm = string(default="control+win+d")
fav = string(default="alt+win+f")
unfav = string(default="alt+shift+win+f")
action = string(default="control+win+s")
details = string(default="control+win+alt+n")
view = string(default="control+win+v")
close = string(default="control+win+f4")
open_timeline = string(default="control+win+i")
delete_buffer = string(default="control+win+shift+i")
url = string(default="control+win+return")
audio = string(default="control+win+alt+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_list = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+c")
add_to_list = string(default="control+win+a")
remove_from_list = string(default="control+win+shift+a")
toggle_mute = string(default="control+win+shift+m")
toggle_global_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")

View File

@@ -21,7 +21,7 @@ class SAPI5(Output):
priority = 101
def __init__(self):
if config.main["general"]["voice_enabled"] == False: raise OutputError
if config.app["app-settings"]["voice_enabled"] == False: raise OutputError
try:
self.object = load_com("SAPI.SPVoice")
self._voices = self._available_voices()

View File

@@ -1,9 +1,9 @@
from base import Output, OutputError
import atexit
import application
class SpeechDispatcher(Output):
"""Supports speech dispatcher on Linux.
Note that it will take the configuration from the speech dispatcher, the user will need configure voice, language, punctuation and rate before use this module.
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'
@@ -11,7 +11,7 @@ class SpeechDispatcher(Output):
super(SpeechDispatcher, self).__init__(*args, **kwargs)
try:
import speechd
self.spd = speechd.SSIPClient("TWBlue")
self.spd = speechd.SSIPClient(application.name)
except ImportError:
raise OutputError
atexit.register(self.on_exit_event)

View File

@@ -0,0 +1,21 @@
[sessions]
current_session = string(default="")
sessions = list(default=list())
ignored_sessions = list(default=list())
[app-settings]
language = string(default="system")
hide_gui = boolean(default=False)
voice_enabled = boolean(default=False)
ask_at_exit = 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")
[proxy]
server = string(default="")
port = string(default="")
user = string(default="")
password = string(default="")

View File

@@ -1,20 +1,16 @@
# -*- coding: utf-8 -*-
name = 'TWBlue'
snapshot = False
snapshot = True
if snapshot == False:
version = "0.51"
update_url = 'http://twblue.es/updates/tw_blue.json'
version = "0.80"
update_url = 'http://twblue.es/updates/twblue_ngen.json'
else:
version = "7"
update_url = 'http://twblue.es/updates/snapshots.json'
author = u"Manuel Cortéz"
version = "9.4"
update_url = 'http://twblue.es/updates/snapshots_ngen.json'
author = u"Manuel Cortéz, Bill Dengler"
authorEmail = "manuel@manuelcortez.net"
copyright = u"copyright (C) 2013-2015, Manuel cortéz"
description = u"TW Blue is an app designed to use Twitter in a simple and fast way and avoiding, as far as possible, the consumtion of excessive resources of the machine where its running. With this app youll have access to most twitter features."
translators = [u"Bryner Villalobos (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)"]
copyright = u"Copyright (C) 2015, Technow S.L. \nCopyright (C) 2015, Bill Dengler\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)"]
url = u"http://twblue.es"
report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl"
# Tokens
app_key = '8pDLbyOW3saYnvSZ4uLFg'
app_secret = 'YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY'
report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl"

View File

@@ -10,14 +10,6 @@ def convert_audioboom(url):
audio_id = url.split('.com/')[-1]
return 'https://audioboom.com/%s.mp3' % audio_id
@matches_url('http://q-audio.net')
def convert_q_audio(url):
result = re.match("^https?://q-audio.net/(i|d|download)/(?P<audio_id>[a-z0-9]+/?)$", url, re.I)
if not result or result.group("audio_id") is None:
raise TypeError('%r is not a valid URL' % url)
audio_id = result.group("audio_id")
return 'http://q-audio.net/download/%s' % audio_id
@matches_url ('http://soundcloud.com/')
def convert_soundcloud (url):
client_id = "df8113ca95c157b6c9731f54b105b473"

View File

@@ -1,16 +1,18 @@
# -*- coding: utf-8 -*-
import argparse
import paths
import logging
import application
log = logging.getLogger("commandlineLauncher")
parser = argparse.ArgumentParser(description="TW Blue command line launcher")
parser = argparse.ArgumentParser(description=application.name+" command line launcher")
group = parser.add_mutually_exclusive_group()
group.add_argument("-p", "--portable", help="Use TW Blue as a portable aplication", action="store_true", default=True)
group.add_argument("-i", "--installed", help="Use TW Blue as an installed application. Config files will be saved on the user data directory", action="store_true")
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where TW Blue saves the data files")
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()
if args.installed == True:
paths.mode = "installed"
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

View File

@@ -1,16 +1,19 @@
# -*- coding: cp1252 -*-
from config_utils import Configuration, ConfigurationResetException
import config_utils
import paths
import logging
MAINFILE = "session.conf"
MAINSPEC = "Conf.defaults"
log = logging.getLogger("config")
main = None
MAINFILE = "twblue.conf"
MAINSPEC = "app-configuration.defaults"
app = None
keymap=None
def setup ():
global main
try:
main = Configuration(paths.config_path(MAINFILE), paths.app_path(MAINSPEC))
except ConfigurationResetException:
pass
# return main
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'))

View File

@@ -1,50 +1,73 @@
# -*- coding: utf-8 -*-
from UserDict import UserDict
from configobj import ConfigObj, ParseError
from validate import Validator, VdtValueError
from validate import Validator, ValidateError
import os
import string
class ConfigLoadError(Exception): pass
"""We're using the configobj python package
from http://www.voidspace.org.uk/python/configobj.html """
def load_config(config_path, configspec_path=None, *args, **kwargs):
if os.path.exists(config_path):
clean_config(config_path)
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
try:
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
except ParseError:
raise ConfigLoadError("Unable to load %r" % config_path)
validator = Validator()
validated = config.validate(validator, copy=True)
if validated == True:
config.write()
return config
class ConfigurationResetException(Exception):
pass
def is_blank(arg):
"Check if a line is blank."
for c in arg:
if c not in string.whitespace:
return False
return True
def get_keys(path):
"Gets the keys of a configobj config file."
res=[]
fin=open(path)
for line in fin:
if not is_blank(line):
res.append(line[0:line.find('=')].strip())
fin.close()
return res
def hist(keys):
"Generates a histogram of an iterable."
res={}
for k in keys:
res[k]=res.setdefault(k,0)+1
return res
class Configuration (UserDict):
def find_problems(hist):
"Takes a histogram and returns a list of items occurring more than once."
res=[]
for k,v in hist.items():
if v>1:
res.append(k)
return res
def __init__ (self, file=None, spec=None, *args, **kwargs):
self.file = file
self.spec = spec
self.validator = Validator()
self.setup_config(file=file, spec=spec)
self.validated = self.config.validate(self.validator, copy=True)
if self.validated:
self.write()
UserDict.__init__(self, self.config)
def setup_config (self, file, spec):
#The default way -- load from a file
spec = ConfigObj(spec, list_values=False, encoding="utf-8")
try:
self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True, encoding="utf-8")
except ParseError:
os.remove(file)
self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True)
raise ConfigurationResetException
def __getitem__ (self, *args, **kwargs):
return dict(self.config).__getitem__(*args, **kwargs)
def __setitem__ (self, *args, **kwargs):
self.config.__setitem__(*args, **kwargs)
UserDict.__setitem__(self, *args, **kwargs)
def write (self):
if hasattr(self.config, 'write'):
self.config.write()
class SessionConfiguration (Configuration):
def setup_config (self, file, spec):
#No infile required.
spec = ConfigObj(spec, list_values=False)
self.config = ConfigObj(configspec=spec, stringify=True)
def clean_config(path):
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
orig=[]
cleaned=[]
fin=open(path)
for line in fin:
orig.append(line)
fin.close()
for p in find_problems(hist(get_keys(path))):
for o in orig:
o.strip()
if p not in o:
cleaned.append(o)
if len(cleaned) != 0:
cam=open(path,'w')
for c in cleaned:
cam.write(c)
cam.close()
return True
else:
return False

View File

@@ -1,3 +1 @@
# -*- coding: utf-8 -*-
import main, dialogs

View File

@@ -0,0 +1,863 @@
# -*- coding: utf-8 -*-
import platform
if platform.system() == "Windows":
import wx
from wxUI import buffers, dialogs, commonMessageDialogs
import user
elif platform.system() == "Linux":
from gi.repository import Gtk
from gtkUI import buffers, dialogs, commonMessageDialogs
import messages
import widgetUtils
import arrow
import webbrowser
import output
import config
import sound
import languageHandler
import logging
from twitter import compose, utils
from mysc.thread_utils import call_threaded
from twython import TwythonError
from pubsub import pub
from long_tweets import twishort, tweets
log = logging.getLogger("controller.buffers")
def _tweets_exist(function):
""" A decorator to execute a function only if the selected buffer contains at least one item."""
def function_(self, *args, **kwargs):
if self.buffer.list.get_count() > 0:
function(self, *args, **kwargs)
return function_
class bufferController(object):
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
super(bufferController, self).__init__()
self.function = function
self.compose_function = None
self.args = args
self.kwargs = kwargs
self.buffer = None
self.account = ""
self.needs_init = True
self.invisible = False # False if the buffer will be ignored on the invisible interface.
def clear_list(self): pass
def get_event(self, ev):
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "url"
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "interact"
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
else:
event = None
ev.Skip()
if event != None:
try:
getattr(self, event)()
except AttributeError:
pass
def volume_down(self):
if self.session.settings["sound"]["volume"] > 0.0:
if self.session.settings["sound"]["volume"] <= 0.05:
self.session.settings["sound"]["volume"] = 0.0
else:
self.session.settings["sound"]["volume"] -=0.05
if hasattr(sound.URLPlayer, "stream"):
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
self.session.sound.play("volume_changed.ogg")
def volume_up(self):
if self.session.settings["sound"]["volume"] < 1.0:
if self.session.settings["sound"]["volume"] >= 0.95:
self.session.settings["sound"]["volume"] = 1.0
else:
self.session.settings["sound"]["volume"] +=0.05
if hasattr(sound.URLPlayer, "stream"):
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
self.session.sound.play("volume_changed.ogg")
def interact(self):
if hasattr(sound.URLPlayer,'stream'):
return sound.URLPlayer.stop_audio(delete=True)
tweet = self.get_tweet()
url=None
urls = utils.find_urls(tweet)
if len(urls) == 1:
url=urls[0]
elif len(urls) > 1:
urls_list = dialogs.urlList.urlList()
urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK:
url=urls_list.get_string()
if hasattr(urls_list, "destroy"): urls_list.destroy()
if url != None:
output.speak(_(u"Opening media..."), True)
if sound.URLPlayer.is_playable(url=url, play=True, volume=self.session.settings["sound"]["volume"]) == False:
return webbrowser.open_new_tab(url)
# else:
# output.speak(_(u"Not actionable."), True)
# self.session.sound.play("error.ogg")
def start_stream(self):
pass
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"), True)
def put_items_on_list(self, items):
pass
def remove_buffer(self):
return False
def remove_item(self, item):
self.buffer.list.remove_item(item)
def bind_events(self):
pass
def get_object(self):
return self.buffer
def get_message(self):
pass
def set_list_position(self, reversed=False):
if reversed == False:
self.buffer.list.select_item(-1)
else:
self.buffer.list.select_item(0)
def reply(self):
pass
def direct_message(self):
pass
def retweet(self):
pass
def destroy_status(self):
pass
def post_tweet(self, *args, **kwargs):
title = _(u"Tweet")
caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "")
if tweet.message.get_response() == widgetUtils.OK:
text = tweet.message.get_text()
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()
class accountPanel(bufferController):
def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.accountPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.session = None
self.needs_init = False
self.account = account
self.buffer.account = account
self.name = name
self.account_id = account_id
def setup_account(self):
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(False)
else:
self.buffer.change_autostart(True)
if not hasattr(self, "logged"):
self.buffer.change_login(login=False)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
else:
self.buffer.change_login(login=True)
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
def login(self, *args, **kwargs):
del self.logged
self.setup_account()
pub.sendMessage("login", session_id=self.account_id)
def logout(self, *args, **kwargs):
self.logged = False
self.setup_account()
pub.sendMessage("logout", session_id=self.account_id)
def autostart(self, *args, **kwargs):
if self.account_id in config.app["sessions"]["ignored_sessions"]:
self.buffer.change_autostart(True)
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
else:
self.buffer.change_autostart(False)
config.app["sessions"]["ignored_sessions"].append(self.account_id)
class emptyPanel(bufferController):
def __init__(self, parent, name, account):
super(emptyPanel, self).__init__(parent, None, name)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.buffer = buffers.emptyPanel(parent, name)
self.type = self.buffer.type
self.compose_function = None
self.account = account
self.buffer.account = account
self.name = name
self.session = None
self.needs_init = True
class baseBufferController(bufferController):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs):
super(baseBufferController, self).__init__(parent, function, *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
if bufferType != None:
self.buffer = getattr(buffers, bufferType)(parent, name)
else:
self.buffer = buffers.basePanel(parent, name)
self.invisible = True
self.name = name
self.type = self.buffer.type
self.session = sessionObject
self.compose_function = compose.compose_tweet
log.debug("Compose_function: %s" % (self.compose_function,))
self.account = account
self.buffer.account = account
self.bind_events()
self.sound = sound
def get_formatted_message(self):
if self.type == "dm" or self.name == "sent_tweets" or self.name == "sent_direct_messages": return self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"])[1]
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"]))
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"]
tweet = self.session.twitter.twitter.show_status(id=tweet_id)
if uri != None:
tweet["text"] = twishort.get_full_text(uri)
l = tweets.is_long(tweet)
while l != False:
tweetsList.append(tweet)
id = tweets.get_id(l)
tweet = self.session.twitter.twitter.show_status(id=id)
l = tweets.is_long(tweet)
if l == False:
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 get_more_items(self):
elements = []
if self.session.settings["general"]["reverse_timelines"] == False:
last_id = self.session.db[self.name][0]["id"]
else:
last_id = self.session.db[self.name][-1]["id"]
try:
items = self.session.get_more_items(self.function, count=self.session.settings["general"]["max_tweets_per_call"], max_id=last_id, *self.args, **self.kwargs)
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:
elements.append(i)
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db[self.name].insert(0, i)
else:
self.session.db[self.name].append(i)
selection = self.buffer.list.get_selected()
if self.session.settings["general"]["reverse_timelines"] == False:
for i in elements:
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
self.buffer.list.insert_item(True, *tweet)
else:
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)
# self.buffer.list.select_item(selection+elements)
# else:
self.buffer.list.select_item(selection)
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
def remove_buffer(self):
if "-timeline" in self.name:
dlg = commonMessageDialogs.remove_buffer()
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])
return True
elif dlg == widgetUtils.NO:
return False
elif "favorite" in self.name:
dlg = commonMessageDialogs.remove_buffer()
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])
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 put_items_on_list(self, number_of_items):
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:
for i in self.session.db[self.name]:
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:
if self.session.settings["general"]["reverse_timelines"] == False:
for i in self.session.db[self.name][:number_of_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]:
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(),))
def add_new_item(self, item):
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"])
if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *tweet)
else:
self.buffer.list.insert_item(True, *tweet)
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)
def bind_events(self):
log.debug("Binding events...")
self.buffer.set_focus_function(self.onFocus)
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_tweet, self.buffer.tweet)
# if self.type == "baseBuffer":
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)
def get_tweet(self):
if self.session.db[self.name][self.buffer.list.get_selected()].has_key("retweeted_status"):
tweet = self.session.db[self.name][self.buffer.list.get_selected()]["retweeted_status"]
else:
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
return tweet
def get_right_tweet(self):
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
return tweet
@_tweets_exist
def reply(self, *args, **kwargs):
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)
if message.message.get_response() == widgetUtils.OK:
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=message.message.get_text())
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=id, status=message.message.get_text(), media=message.file)
if hasattr(message.message, "destroy"): message.message.destroy()
@_tweets_exist
def direct_message(self, *args, **kwargs):
tweet = self.get_right_tweet()
if self.type == "dm":
screen_name = tweet["sender"]["screen_name"]
users = utils.get_all_users(tweet, self.session.db)
elif self.type == "people":
screen_name = tweet["screen_name"]
users = [screen_name]
else:
screen_name = tweet["user"]["screen_name"]
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"))
if hasattr(dm.message, "destroy"): dm.message.destroy()
@_tweets_exist
def retweet(self, *args, **kwargs):
tweet = self.get_right_tweet()
id = tweet["id"]
if self.session.settings["general"]["retweet_mode"] == "ask":
answer = commonMessageDialogs.retweet_question(self.buffer)
if answer == widgetUtils.YES:
self._retweet_with_comment(tweet, id)
elif answer == widgetUtils.NO:
self._direct_retweet(id)
elif self.session.settings["general"]["retweet_mode"] == "direct":
self._direct_retweet(id)
else:
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 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+" @{2} https://twitter.com/{0}/status/{1}".format(tweet["user"]["screen_name"], id, tweet["user"]["screen_name"])
else:
return self._retweet_with_comment(tweet, id, comment=comments)
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:
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, media=retweet.image)
if hasattr(retweet.message, "destroy"): retweet.message.destroy()
def _direct_retweet(self, id):
call_threaded(self.session.api_call, call_name="retweet", _sound="retweet_send.ogg", id=id)
def onFocus(self, *args, **kwargs):
tweet = self.get_tweet()
if platform.system() == "Windows" and self.session.settings["general"]["relative_times"] == True:
# fix this:
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en")
ts = original_date.humanize(locale=languageHandler.getLanguage())
self.buffer.list.list.SetStringItem(self.buffer.list.get_selected(), 2, ts)
if utils.is_audio(tweet):
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'):
return sound.URLPlayer.stop_audio(delete=True)
tweet = self.get_tweet()
if tweet == None: return
urls = utils.find_urls(tweet)
if len(urls) == 1:
url=urls[0]
elif len(urls) > 1:
urls_list = dialogs.urlList.urlList()
urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK:
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.")
@_tweets_exist
def url(self,url='',announce=True):
if url == '':
tweet = self.get_tweet()
urls = utils.find_urls(tweet)
if len(urls) == 1:
url=urls[0]
elif len(urls) > 1:
urls_list = dialogs.urlList.urlList()
urls_list.populate_list(urls)
if urls_list.get_response() == widgetUtils.OK:
url=urls_list.get_string()
if hasattr(urls_list, "destroy"): urls_list.destroy()
if url != '':
if announce:
output.speak(_(u"Opening URL..."), True)
webbrowser.open_new_tab(url)
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.session.db[self.name] = []
self.buffer.list.clear()
@_tweets_exist
def destroy_status(self, *args, **kwargs):
index = self.buffer.list.get_selected()
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
answer = commonMessageDialogs.delete_tweet_dialog(None)
if answer == widgetUtils.YES:
try:
if self.name == "direct_messages" or self.name == "sent_direct_messages":
self.session.twitter.twitter.destroy_direct_message(id=self.get_right_tweet()["id"])
else:
self.session.twitter.twitter.destroy_status(id=self.get_right_tweet()["id"])
self.session.db[self.name].pop(index)
self.buffer.list.remove_item(index)
# if index > 0:
except TwythonError:
self.session.sound.play("error")
@_tweets_exist
def user_details(self):
tweet = self.get_right_tweet()
if self.type == "dm":
users = utils.get_all_users(tweet, self.session.db)
elif self.type == "people":
users = [tweet["screen_name"]]
else:
users = utils.get_all_users(tweet, self.session.db)
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
if dlg.get_response() == widgetUtils.OK:
user.profileController(session=self.session, user=dlg.get_user())
if hasattr(dlg, "destroy"): dlg.destroy()
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)
self.users = []
self.list_id = list_id
self.kwargs["list_id"] = list_id
def start_stream(self):
self.get_user_ids()
super(listBufferController, self).start_stream()
def get_user_ids(self):
self.users = []
next_cursor = -1
while(next_cursor):
users = self.session.twitter.twitter.get_list_members(list_id=self.list_id, cursor=next_cursor, include_entities=False, skip_status=True)
for i in users['users']:
self.users.append(i["id"])
next_cursor = users["next_cursor"]
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
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])
return True
elif dlg == widgetUtils.NO:
return False
class eventsBufferController(bufferController):
def __init__(self, parent, name, session, account, *args, **kwargs):
super(eventsBufferController, self).__init__(parent, *args, **kwargs)
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.invisible = True
self.buffer = buffers.eventsPanel(parent, name)
self.name = name
self.account = account
self.buffer.account = self.account
self.compose_function = compose.compose_event
self.session = session
self.type = self.buffer.type
self.get_formatted_message = self.get_message
def get_message(self):
if self.buffer.list.get_count() == 0: return _(u"Empty")
# fix this:
return "%s. %s" % (self.buffer.list.list.GetItemText(self.buffer.list.get_selected()), self.buffer.list.list.GetItemText(self.buffer.list.get_selected(), 1))
def add_new_item(self, item):
tweet = self.compose_function(item, self.session.db["user_name"])
if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *tweet)
else:
self.buffer.list.insert_item(True, *tweet)
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))
if self.buffer.list.get_count() == 1:
self.buffer.list.select_item(0)
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.buffer.list.clear()
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")
log.debug("Initializing buffer %s, account %s" % (name, account,))
self.compose_function = compose.compose_followers_list
log.debug("Compose_function: %s" % (self.compose_function,))
self.get_tweet = self.get_right_tweet
self.url = self.interact
def remove_buffer(self):
return False
def onFocus(self, ev):
pass
def get_message(self):
return " ".join(self.compose_function(self.get_tweet(), self.session.db, self.session.settings["general"]["relative_times"]))
def delete_item(self): pass
@_tweets_exist
def reply(self, *args, **kwargs):
tweet = self.get_right_tweet()
screen_name = tweet["screen_name"]
message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,])
if message.message.get_response() == widgetUtils.OK:
if message.image == None:
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text())
else:
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 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"])
except TwythonError as e:
output.speak(e.message, True)
return
for i in items:
if self.session.settings["general"]["reverse_timelines"] == False:
self.session.db[self.name]["items"].insert(0, i)
else:
self.session.db[self.name]["items"].append(i)
selected = self.buffer.list.get_selected()
# self.put_items_on_list(len(items))
if self.session.settings["general"]["reverse_timelines"] == True:
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)
self.buffer.list.select_item(selected)
else:
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)
# self.buffer.list.select_item(selection)
# else:
# self.buffer.list.select_item(selection-elements)
output.speak(_(u"%s items retrieved") % (len(items)), True)
def put_items_on_list(self, number_of_items):
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:
for i in self.session.db[self.name]["items"]:
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"])
# 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]:
tweet = self.compose_function(i, self.session.db)
self.buffer.list.insert_item(False, *tweet)
else:
for i in self.session.db[self.name]["items"][0:number_of_items]:
tweet = self.compose_function(i, self.session.db)
self.buffer.list.insert_item(True, *tweet)
log.debug("now the list contains %d items" % (self.buffer.list.get_count(),))
def get_right_tweet(self):
tweet = self.session.db[self.name]["items"][self.buffer.list.get_selected()]
return tweet
def add_new_item(self, item):
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"])
if self.session.settings["general"]["reverse_timelines"] == False:
self.buffer.list.insert_item(False, *tweet)
else:
self.buffer.list.insert_item(True, *tweet)
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))
def clear_list(self):
dlg = commonMessageDialogs.clear_list()
if dlg == widgetUtils.YES:
self.session.db[self.name]["items"] = []
self.session.db[self.name]["cursor"] = -1
self.buffer.list.clear()
def interact(self):
user.profileController(self.session, user=self.get_right_tweet()["screen_name"])
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,))
# try:
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")
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
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()
return True
elif dlg == widgetUtils.NO:
return False
class searchPeopleBufferController(peopleBufferController):
def __init__(self, parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs):
super(searchPeopleBufferController, 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,))
self.args = args
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,))
# try:
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")
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
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()
return True
elif dlg == widgetUtils.NO:
return False
class trendsBufferController(bufferController):
def __init__(self, parent, name, session, account, trendsFor, *args, **kwargs):
super(trendsBufferController, self).__init__(parent=parent, session=session)
self.trendsFor = trendsFor
self.session = session
self.account = account
self.invisible = True
self.buffer = buffers.trendsPanel(parent, name)
self.buffer.account = account
self.type = self.buffer.type
self.bind_events()
self.sound = "trends_updated.ogg"
self.trends = []
self.name = name
self.buffer.name = name
self.compose_function = self.compose_function_
self.get_formatted_message = self.get_message
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 put_items_on_the_list(self):
selected_item = self.buffer.list.get_selected()
self.buffer.list.clear()
for i in self.trends:
tweet = self.compose_function(i)
self.buffer.list.insert_item(False, *tweet)
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
def compose_function_(self, trend):
return [trend["name"]]
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)
def get_message(self):
return self.compose_function(self.trends[self.buffer.list.get_selected()])[0]
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
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()
return True
elif dlg == widgetUtils.NO:
return False
def interact(self, *args, **kwargs):
self.searchfunction(value=self.get_message())
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")

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
import widgetUtils
import output
from wxUI.dialogs import lists
from twython import TwythonError
from twitter import compose, utils
from pubsub import pub
class listsController(object):
def __init__(self, session, user=None):
super(listsController, self).__init__()
self.session = session
if user == None:
self.dialog = lists.listViewer()
self.dialog.populate_list(self.get_all_lists())
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
self.dialog.get_response()
def get_all_lists(self):
return [compose.compose_list(item) for item in self.session.db["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:
mode = "public"
else:
mode = "private"
try:
new_list = self.session.twitter.twitter.create_list(name=name, description=description, mode=mode)
self.session.db["lists"].append(new_list)
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
dialog.destroy()
def edit_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]
dialog = lists.editListDialog(list)
if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name")
description = dialog.get("description")
p = dialog.get("public")
if p == True:
mode = "public"
else:
mode = "private"
try:
self.session.twitter.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
self.session.get_lists()
self.dialog.populate_list(self.get_all_lists(), True)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
dialog.destroy()
def remove_list(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()]["id"]
if lists.remove_list() == widgetUtils.YES:
try:
self.session.twitter.twitter.delete_list(list_id=list)
self.session.db["lists"].pop(self.dialog.get_item())
self.dialog.lista.remove_item(self.dialog.get_item())
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
def 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"])

File diff suppressed because it is too large Load Diff

186
src/controller/messages.py Normal file
View File

@@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
import platform
system = platform.system()
import widgetUtils
import output
import url_shortener
import sound
from pubsub import pub
if system == "Windows":
from wxUI.dialogs import message, urlList
from extra import translator, SpellChecker, autocompletionUsers
from extra.AudioUploader import audioUploader
elif system == "Linux":
from gtkUI.dialogs import message
from twitter import utils
class basicTweet(object):
""" This class handles the tweet main features. Other classes should derive from this class."""
def __init__(self, session, title, caption, text, messageType="tweet", max=140):
super(basicTweet, self).__init__()
self.max = max
self.title = title
self.session = session
self.message = getattr(message, messageType)(title, caption, text)
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":
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)
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")
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)
self.message.set_text(msg)
self.message.text_focus()
output.speak(_(u"Translated"))
else:
return
def shorten(self, event=None):
urls = utils.find_urls_in_text(self.message.get_text())
if len(urls) == 0:
output.speak(_(u"There's no URL to be shortened"))
self.message.text_focus()
elif len(urls) == 1:
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
output.speak(_(u"URL shortened"))
self.message.text_focus()
elif len(urls) > 1:
list_urls = urlList.urlList()
list_urls.populate_list(urls)
if list_urls.get_response() == widgetUtils.OK:
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
output.speak(_(u"URL shortened"))
self.message.text_focus()
def unshorten(self, event=None):
urls = utils.find_urls_in_text(self.message.get_text())
if len(urls) == 0:
output.speak(_(u"There's no URL to be expanded"))
self.message.text_focus()
elif len(urls) == 1:
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
output.speak(_(u"URL expanded"))
self.message.text_focus()
elif len(urls) > 1:
list_urls = urlList.urlList()
list_urls.populate_list(urls)
if list_urls.get_response() == widgetUtils.OK:
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
output.speak(_(u"URL expanded"))
self.message.text_focus()
def text_processor(self, *args, **kwargs):
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, len(self.message.get_text()), self.max))
if len(self.message.get_text()) > 1:
self.message.enable_button("shortenButton")
self.message.enable_button("unshortenButton")
else:
self.message.disable_button("shortenButton")
self.message.disable_button("unshortenButton")
if len(self.message.get_text()) > self.max:
self.session.sound.play("max_length.ogg")
def spellcheck(self, event=None):
text = self.message.get_text()
checker = SpellChecker.spellchecker.spellChecker(text, "")
if hasattr(checker, "fixed_text"):
self.message.set_text(checker.fixed_text)
def attach(self, *args, **kwargs):
def completed_callback():
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")
else:
output.speak(_(u"Unable to upload the audio"))
dlg.cleanup()
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
class tweet(basicTweet):
def __init__(self, session, title, caption, text, 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)
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"))
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)
self.message.enable_button("mentionAll")
self.message.set_cursor_at_end()
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()
class dm(basicTweet):
def __init__(self, session, title, caption, text):
super(dm, self).__init__(session, title, caption, text, messageType="dm")
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu("dm")
class viewTweet(basicTweet):
def __init__(self, tweet, tweetList, is_tweet=True):
if is_tweet == True:
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"])
else:
text = text + "@%s: %s\n\n" % (tweetList[i]["user"]["screen_name"], tweetList[i]["text"])
rt_count = str(tweet["retweet_count"])
favs_count = str(tweet["favorite_count"])
if text == "":
if tweet.has_key("retweeted_status"):
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["text"])
else:
text = tweet["text"]
self.message = message.viewTweet(text, rt_count, favs_count)
self.message.set_title(len(text))
else:
text = tweet
self.message = message.viewNonTweet(text)
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
if self.contain_urls() == True:
self.message.enable_button("unshortenButton")
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
self.message.get_response()
def contain_urls(self):
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
return True
return False

281
src/controller/settings.py Normal file
View File

@@ -0,0 +1,281 @@
# -*- coding: utf-8 -*-
import os
import webbrowser
import sound_lib
import paths
import widgetUtils
import config
import languageHandler
import output
import application
from wxUI.dialogs import configuration
from wxUI import commonMessageDialogs
from extra.autocompletionUsers import settings
from extra.AudioUploader import dropbox_transfer
from pubsub import pub
import logging
import config_utils
log = logging.getLogger("Settings")
class globalSettingsController(object):
def __init__(self):
super(globalSettingsController, self).__init__()
self.dialog = configuration.configurationDialog()
self.create_config()
self.needs_restart = False
self.is_started = True
def make_kmmap(self):
res={}
for i in os.listdir(paths.app_path('keymaps')):
if ".keymap" not in i:
continue
try:
res[config_utils.load_config(paths.app_path('keymaps/'+i))['info']['name']]=i
except:
log.exception("Exception while loading keymap " + i)
return res
def create_config(self):
self.kmmap=self.make_kmmap()
self.langs = languageHandler.getAvailableLanguages()
langs = []
[langs.append(i[1]) for i in self.langs]
self.codes = []
[self.codes.append(i[0]) for i in self.langs]
id = self.codes.index(config.app["app-settings"]["language"])
self.kmfriendlies=[]
self.kmnames=[]
for k,v in self.kmmap.items():
self.kmfriendlies.append(k)
self.kmnames.append(v)
self.kmid=self.kmnames.index(config.app['app-settings']['load_keymap'])
self.dialog.create_general(langs,self.kmfriendlies)
self.dialog.general.language.SetSelection(id)
self.dialog.general.km.SetSelection(self.kmid)
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", "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.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()
def save_configuration(self):
if self.codes[self.dialog.general.language.GetSelection()] != config.app["app-settings"]["language"]:
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
languageHandler.setLanguage(config.app["app-settings"]["language"])
self.needs_restart = True
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
self.needs_restart = True
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"))
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
config.app["app-settings"]["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")
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
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
config.app.write()
class accountSettingsController(globalSettingsController):
def __init__(self, buffer, window):
self.user = buffer.session.db["user_name"]
self.buffer = buffer
self.window = window
self.config = buffer.session.settings
super(accountSettingsController, self).__init__()
def create_config(self):
self.dialog.create_general_account()
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
self.dialog.set_value("general", "apiCalls", self.config["general"]["max_api_calls"])
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
rt = self.config["general"]["retweet_mode"]
if rt == "ask":
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
elif rt == "direct":
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
else:
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
self.dialog.create_other_buffers()
buffer_values = self.get_buffers_list()
self.dialog.buffers.insert_buffers(buffer_values)
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
self.input_devices = sound_lib.input.Input.get_device_names()
self.output_devices = sound_lib.output.Output.get_device_names()
self.soundpacks = []
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(paths.sound_path(i)) == True ]
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
self.dialog.set_value("sound", "volumeCtrl", self.config["sound"]["volume"]*100)
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
self.dialog.create_audio_services()
if self.config["services"]["dropbox_token"] == "":
self.dialog.services.set_dropbox(False)
else:
self.dialog.services.set_dropbox(True)
widgetUtils.connect_event(self.dialog.services.dropbox, widgetUtils.BUTTON_PRESSED, self.manage_dropbox)
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,))
self.response = self.dialog.get_response()
def save_configuration(self):
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
self.needs_restart = True
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
self.config["general"]["max_api_calls"] = self.dialog.get_value("general", "apiCalls")
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
if self.dialog.get_value("general", "persist_size") == '':
self.config["general"]["persist_size"] =-1
else:
try:
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
except ValueError:
output.speak("Invalid cache size, setting to default.",True)
self.config["general"]["persist_size"] =1764
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
self.needs_restart = True
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
rt = self.dialog.get_value("general", "retweet_mode")
if rt == _(u"Ask"):
self.config["general"]["retweet_mode"] = "ask"
elif rt == _(u"Retweet without comments"):
self.config["general"]["retweet_mode"] = "direct"
else:
self.config["general"]["retweet_mode"] = "comment"
buffers_list = self.dialog.buffers.get_list()
if set(self.config["general"]["buffer_order"]) != set(buffers_list) or buffers_list != self.config["general"]["buffer_order"]:
self.needs_restart = True
self.config["general"]["buffer_order"] = buffers_list
# if self.config["other_buffers"]["show_followers"] != self.dialog.get_value("buffers", "followers"):
# self.config["other_buffers"]["show_followers"] = self.dialog.get_value("buffers", "followers")
# pub.sendMessage("create-new-buffer", buffer="followers", account=self.user, create=self.config["other_buffers"]["show_followers"])
# if self.config["other_buffers"]["show_friends"] != self.dialog.get_value("buffers", "friends"):
# self.config["other_buffers"]["show_friends"] = self.dialog.get_value("buffers", "friends")
# pub.sendMessage("create-new-buffer", buffer="friends", account=self.user, create=self.config["other_buffers"]["show_friends"])
# if self.config["other_buffers"]["show_favourites"] != self.dialog.get_value("buffers", "favs"):
# self.config["other_buffers"]["show_favourites"] = self.dialog.get_value("buffers", "favs")
# pub.sendMessage("create-new-buffer", buffer="favourites", account=self.user, create=self.config["other_buffers"]["show_favourites"])
# if self.config["other_buffers"]["show_blocks"] != self.dialog.get_value("buffers", "blocks"):
# self.config["other_buffers"]["show_blocks"] = self.dialog.get_value("buffers", "blocks")
# pub.sendMessage("create-new-buffer", buffer="blocked", account=self.user, create=self.config["other_buffers"]["show_blocks"])
# if self.config["other_buffers"]["show_muted_users"] != self.dialog.get_value("buffers", "mutes"):
# self.config["other_buffers"]["show_muted_users"] = self.dialog.get_value("buffers", "mutes")
# pub.sendMessage("create-new-buffer", buffer="muted", account=self.user, create=self.config["other_buffers"]["show_muted_users"])
# if self.config["other_buffers"]["show_events"] != self.dialog.get_value("buffers", "events"):
# self.config["other_buffers"]["show_events"] = self.dialog.get_value("buffers", "events")
# pub.sendMessage("create-new-buffer", buffer="events", account=self.user, create=self.config["other_buffers"]["show_events"])
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
try:
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
except:
self.config["sound"]["input_device"] = "default"
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
try:
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
except:
self.config["sound"]["output_device"] = "default"
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
self.buffer.session.sound.config = self.config["sound"]
self.buffer.session.sound.check_soundpack()
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("services", "apiKey")
def toggle_state(self,*args,**kwargs):
return self.dialog.buffers.change_selected_item()
def manage_autocomplete(self, *args, **kwargs):
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
def add_ignored_client(self, *args, **kwargs):
client = commonMessageDialogs.get_ignored_client()
if client == None: return
if client not in self.config["twitter"]["ignored_clients"]:
self.config["twitter"]["ignored_clients"].append(client)
self.dialog.ignored_clients.append(client)
def remove_ignored_client(self, *args, **kwargs):
if self.dialog.ignored_clients.get_clients() == 0: return
id = self.dialog.ignored_clients.get_client_id()
self.config["twitter"]["ignored_clients"].pop(id)
self.dialog.ignored_clients.remove_(id)
def manage_dropbox(self, *args, **kwargs):
if self.dialog.services.get_dropbox() == _(u"Link your Dropbox account"):
self.connect_dropbox()
else:
self.disconnect_dropbox()
def connect_dropbox(self):
auth = dropbox_transfer.dropboxLogin(self.config)
url = auth.get_url()
self.dialog.services.show_dialog()
webbrowser.open(url)
resp = self.dialog.services.get_response()
if resp == "":
self.dialog.services.set_dropbox(False)
else:
try:
auth.authorise(resp)
self.dialog.services.set_dropbox()
except:
self.dialog.services.show_error()
self.dialog.services.set_dropbox(False)
def disconnect_dropbox(self):
self.config["services"]["dropbox_token"] = ""
self.dialog.services.set_dropbox(False)
def get_buffers_list(self):
all_buffers = ['home','mentions','dm','sent_dm','sent_tweets','favorites','followers','friends','blocks','muted','events']
list_buffers = []
hidden_buffers=[]
for i in all_buffers:
if i in self.config["general"]["buffer_order"]:
list_buffers.append((i, True))
else:
hidden_buffers.append((i, False))
list_buffers.extend(hidden_buffers)
return list_buffers
def toggle_buffer_active(self, ev):
change = self.dialog.buffers.get_event(ev)
if change == True:
self.dialog.buffers.change_selected_item()

View File

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

102
src/controller/user.py Normal file
View File

@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
import webbrowser
import widgetUtils
import output
from wxUI.dialogs import update_profile, show_user
from twython import TwythonError
class profileController(object):
def __init__(self, session, user=None):
super(profileController, self).__init__()
self.file = None
self.session = session
self.user = user
if user == None:
self.dialog = update_profile.updateProfileDialog()
self.get_data(screen_name=self.session.db["user_name"])
self.fill_profile_fields()
self.uploaded = False
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
else:
self.dialog = show_user.showUserProfile()
self.get_data(screen_name=self.user)
string = self.get_user_info()
self.dialog.set("text", string)
self.dialog.set_title(_(u"Information for %s") % (self.data["screen_name"]))
if self.data["url"] != None:
self.dialog.enable_url()
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
self.do_update()
def get_data(self, screen_name):
self.data = self.session.twitter.twitter.show_user(screen_name=screen_name)
def fill_profile_fields(self):
self.dialog.set_name(self.data["name"])
if self.data["url"] != None:
self.dialog.set_url(self.data["url"])
if len(self.data["location"]) > 0:
self.dialog.set_location(self.data["location"])
if len(self.data["description"]) > 0:
self.dialog.set_description(self.data["description"])
def get_image(self):
file = self.dialog.upload_picture()
if file != None:
self.file = open(file, "rb")
self.uploaded = True
self.dialog.change_upload_button(self.uploaded)
def discard_image(self):
self.file = None
output.speak(_(u"Discarded"))
self.uploaded = False
self.dialog.change_upload_button(self.uploaded)
def upload_image(self, *args, **kwargs):
if self.uploaded == False:
self.get_image()
elif self.uploaded == True:
self.discard_image()
def do_update(self):
if self.user != None: return
name = self.dialog.get("name")
description = self.dialog.get("description")
location = self.dialog.get("location")
url = self.dialog.get("url")
if self.file != None:
try:
self.session.twitter.twitter.update_profile_image(image=self.file)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
try:
self.session.twitter.twitter.update_profile(name=name, description=description, location=location, url=url)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
def get_user_info(self):
string = u""
string = string + _(u"Username: @%s\n") % (self.data["screen_name"])
string = string + _(u"Name: %s\n") % (self.data["name"])
if self.data["location"] != "":
string = string + _(u"Location: %s\n") % (self.data["location"])
if self.data["url"] != None:
string = string+ _(u"URL: %s\n") % (self.data["url"])
if self.data["description"] != "":
string = string+ _(u"Bio: %s\n") % (self.data["description"])
if self.data["protected"] == True: protected = _(u"Yes")
else: protected = _(u"No")
string = string+ _(u"Protected: %s\n") % (protected)
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"])
return string
def visit_url(self, *args, **kwargs):
webbrowser.open_new_tab(self.data["url"])

View File

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

View File

@@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
""" Handles storage from a durus database """
class db(object):
def __init__(self):
self.settings = {}

View File

@@ -1,3 +0,0 @@
import gui, transfer, transfer_dialogs, platform
if platform.system() != "Darwin":
import dropbox

View File

@@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# 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, see <http://www.gnu.org/licenses/>.
#
############################################################
import widgetUtils
import wx_ui
import wx_transfer_dialogs
import dropbox_transfer, transfer
import output
import tempfile
import sound
import os
import config
from pubsub import pub
from mysc.thread_utils import call_threaded
import sound_lib
import logging
log = logging.getLogger("extra.AudioUploader.audioUploader")
class audioUploader(object):
def __init__(self, configFile, completed_callback):
self.config = configFile
super(audioUploader, self).__init__()
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
self.file = None
self.recorded = False
self.recording = None
self.playing = None
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
if self.dialog.get_response() == widgetUtils.OK:
self.postprocess()
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
output.speak(_(u"Attaching..."))
if self.dialog.get("services") == "Dropbox":
self.uploaderFunction = dropbox_transfer.dropboxUploader(filename=self.file, completed_callback=completed_callback, config=self.config)
elif self.dialog.get("services") == "SNDUp":
base_url = "http://sndup.net/post.php"
if len(self.config["sound"]["sndup_api_key"]) > 0:
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
else:
url = base_url
self.uploaderFunction = transfer.Upload(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)
pub.subscribe(self.uploaderDialog.update, "uploading")
self.uploaderDialog.get_response()
self.uploaderFunction.perform_threaded()
def get_available_services(self):
services = []
services.append("TwUp")
if self.config["services"]["dropbox_token"] != "":
services.append("Dropbox")
services.append("SNDUp")
return services
def on_pause(self, *args, **kwargs):
if self.dialog.get("pause") == _(u"Pause"):
self.recording.pause()
self.dialog.set("pause", _(u"Resume"))
elif self.dialog.get("pause") == _(u"Resume"):
self.recording.play()
self.dialog.set("pause", _(U"Pause"))
def on_record(self, *args, **kwargs):
if self.recording != None:
self.stop_recording()
self.dialog.disable_control("pause")
else:
self.start_recording()
self.dialog.enable_control("pause")
def start_recording(self):
self.dialog.disable_control("attach_exists")
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.dialog.set("record", _(u"Stop"))
output.speak(_(u"Recording"))
def stop_recording(self):
self.recording.stop()
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.dialog.set("record", _(u"Record"))
self.file_attached()
def file_attached(self):
self.dialog.set("pause", _(u"Pause"))
self.dialog.disable_control("record")
self.dialog.enable_control("play")
self.dialog.enable_control("discard")
self.dialog.disable_control("attach_exists")
self.dialog.enable_control("attach")
self.dialog.play.SetFocus()
def on_discard(self, *args, **kwargs):
if self.playing:
self._stop()
if self.recording != None:
self.dialog.disable_control("attach")
self.dialog.disable_control("play")
self.file = None
self.dialog.enable_control("record")
self.dialog.enable_control("attach_exists")
self.dialog.record.SetFocus()
self.dialog.disable_control("discard")
self.recording = None
output.speak(_(u"Discarded"))
def on_play(self, *args, **kwargs):
if not self.playing:
call_threaded(self._play)
else:
self._stop()
def _play(self):
output.speak(_(u"Playing..."))
# try:
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play()
self.dialog.set("play", _(u"Stop"))
try:
while self.playing.is_playing:
pass
self.dialog.set("play", _(u"Play"))
self.playing.free()
self.playing = None
except:
pass
def _stop(self):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.dialog.set("play", _(u"Play"))
self.playing = None
def postprocess(self):
if self.file.lower().endswith('.wav'):
output.speak(_(u"Recoding audio..."))
sound.recode_audio(self.file)
self.wav_file = self.file
self.file = '%s.ogg' % self.file[:-4]
def cleanup(self):
if self.playing and self.playing.is_playing:
self.playing.stop()
if self.recording != None:
if self.recording.is_playing:
self.recording.stop()
try:
self.recording.free()
except:
pass
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
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()
if self.file != False:
self.file_attached()

View File

@@ -3,14 +3,16 @@ import threading
import time
import os
import exceptions
import wx
import dropbox
import config
from mysc import event
import logging
import application
from keys import keyring
from utils import *
from dropbox.rest import ErrorResponse
from StringIO import StringIO
from pubsub import pub
log = logging.getLogger("extra.AudioUploader.dropbox_transfer")
class UnauthorisedError(exceptions.Exception):
def __init__(self, *args, **kwargs):
super(UnauthorisedError, self).__init__(*args, **kwargs)
@@ -39,29 +41,33 @@ class newChunkedUploader(dropbox.client.ChunkedUploader):
self.offset = reply['offset']
class dropboxLogin(object):
def __init__(self):
def __init__(self, config):
log.debug("Trying to login in Dropbox...")
self.logged = False
self.app_key = "c8ikm0gexqvovol"
self.app_secret = "gvvi6fzfecooast"
self.config = config
def get_url(self):
self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect(self.app_key, self.app_secret)
log.debug("Getting autorisation URL...")
self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect(keyring.get("dropbox_api_key"), keyring.get("dropbox_api_secret"))
return self.flow.start()
def authorise(self, code):
log.debug("Authorising " + application.name + " to Dropbox...")
access_token, user_id = self.flow.finish(code)
config.main["services"]["dropbox_token"] = access_token
log.debug("Saving tokens...")
self.config["services"]["dropbox_token"] = access_token
self.logged = True
class dropboxUploader(object):
def __init__(self, filename, completed_callback, wxDialog, short_url=False):
if config.main["services"]["dropbox_token"] != "":
self.client = dropbox.client.DropboxClient(config.main["services"]["dropbox_token"])
def __init__(self, config, filename, completed_callback, short_url=False):
if config["services"]["dropbox_token"] != "":
log.debug("logging in Dropbox...")
self.client = dropbox.client.DropboxClient(config["services"]["dropbox_token"])
else:
raise UnauthorisedError("You need authorise TWBlue")
log.error("Dropbox is not authorised for this session.")
raise UnauthorisedError("You need to authorise " + application.name)
self.filename = filename
self.short_url = short_url
self.wxDialog = wxDialog
self.file = open(self.filename, "rb")
self.file_size = os.path.getsize(self.filename)
self.uploader = newChunkedUploader(client=self.client, file_obj=self.file, length=self.file_size, callback=self.process)
@@ -70,6 +76,7 @@ class dropboxUploader(object):
self.background_thread = None
self.current = 0
self.transfer_rate = 0
log.debug("File Size: %d " % (self.file_size,))
def elapsed_time(self):
if not self.start_time:
@@ -77,6 +84,7 @@ class dropboxUploader(object):
return time.time() - self.start_time
def perform_transfer(self):
log.debug("Starting transfer...")
self.start_time = time.time()
while self.uploader.offset < self.file_size:
self.uploader.upload_chunked(self.file_size/100)
@@ -94,9 +102,7 @@ class dropboxUploader(object):
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
else:
progress["eta"] = 0
info = event.event(event.EVT_OBJECT, 1)
info.SetItem(progress)
wx.PostEvent(self.wxDialog, info)
pub.sendMessage("uploading", data=progress)
def perform_threaded(self):
self.background_thread = threading.Thread(target=self.perform_transfer)
@@ -104,10 +110,12 @@ class dropboxUploader(object):
self.background_thread.start()
def transfer_completed(self):
log.debug("Transfer completed")
self.uploader.finish(os.path.basename(self.filename))
if callable(self.completed_callback):
self.completed_callback()
def get_url(self):
original = "%s" % (self.client.share(os.path.basename(self.filename), False)["url"])
return original.replace("dl=0", "dl=1")
original = "%s" % (self.client.media(os.path.basename(self.filename))["url"])
return original
# .replace("dl=0", "dl=1")

View File

@@ -1,191 +0,0 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# 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, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import output
import tempfile
import sound
import os
import config
from mysc.thread_utils import call_threaded
import sound_lib
class audioDialog(wx.Dialog):
def __init__(self, parent):
self.parent = parent
wx.Dialog.__init__(self, None, -1, _(u"Attach audio"))
self.file = None
self.recorded = False
self.recording = None
self.playing = None
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.play = wx.Button(panel, -1, _(u"Play"))
self.play.Bind(wx.EVT_BUTTON, self.onPlay)
self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"Pause"))
self.pause.Bind(wx.EVT_BUTTON, self.onPause)
self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"Record"))
self.record.Bind(wx.EVT_BUTTON, self.onRecord)
self.record.SetFocus()
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file"))
self.attach_exists.Bind(wx.EVT_BUTTON, self.onAttach)
self.discard = wx.Button(panel, -1, _(u"Discard"))
self.discard.Bind(wx.EVT_BUTTON, self.onDiscard)
self.discard.Disable()
label = wx.StaticText(panel, -1, _(u"Upload to"))
self.services = wx.ComboBox(panel, -1, choices=self.get_available_services(), value=self.get_available_services()[0], style=wx.CB_READONLY)
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
servicesBox.Add(label)
servicesBox.Add(self.services)
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
self.attach.Disable()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
sizer.Add(self.play)
sizer.Add(self.pause)
sizer.Add(self.record)
sizer.Add(self.attach_exists)
sizer.Add(self.discard)
sizer.Add(self.attach)
def get_available_services(self):
services = []
if config.main["services"]["dropbox_token"] != "":
services.append("Dropbox")
services.append("TwUp")
services.append("SNDUp")
return services
def onPause(self, ev):
if self.pause.GetLabel() == _(u"Pause"):
self.recording.pause()
self.pause.SetLabel(_(u"Resume"))
elif self.pause.GetLabel() == _(u"Resume"):
self.recording.play()
self.pause.SetLabel(_(U"Pause"))
def onRecord(self, ev):
if self.recording != None:
self.stop_recording()
self.pause.Disable()
else:
self.start_recording()
self.pause.Enable()
def start_recording(self):
self.attach_exists.Disable()
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.record.SetLabel(_(u"Stop recording"))
output.speak(_(u"Recording"))
def stop_recording(self):
self.recording.stop()
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.record.SetLabel(_(u"Record"))
self.file_attached()
def file_attached(self):
self.pause.SetLabel(_(u"Pause"))
self.record.Disable()
self.play.Enable()
self.discard.Enable()
self.attach_exists.Disable()
self.attach.Enable()
self.play.SetFocus()
def onDiscard(self, evt):
evt.Skip()
if self.playing:
self._stop()
if self.recording != None:
self.attach.Disable()
self.play.Disable()
self.file = None
self.record.Enable()
self.attach_exists.Enable()
self.record.SetFocus()
self.discard.Disable()
self.recording = None
output.speak(_(u"Discarded"))
def onPlay(self, evt):
evt.Skip()
if not self.playing:
call_threaded(self._play)
else:
self._stop()
def _play(self):
output.speak(_(u"Playing..."))
# try:
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play()
self.play.SetLabel(_(u"Stop"))
try:
while self.playing.is_playing:
pass
self.play.SetLabel(_(u"Play"))
self.playing.free()
self.playing = None
except:
pass
def _stop(self):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.play.SetLabel(_(u"Play"))
self.playing = None
def postprocess(self):
if self.file.lower().endswith('.wav'):
output.speak(_(u"Recoding audio..."))
sound.recode_audio(self.file)
self.wav_file = self.file
self.file = '%s.ogg' % self.file[:-4]
def cleanup(self):
if self.playing and self.playing.is_playing:
self.playing.stop()
if self.recording != None:
if self.recording.is_playing:
self.recording.stop()
try:
self.recording.free()
except:
pass
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
del(self.wav_file)
if hasattr(self, 'wav_file') and os.path.exists(self.file):
os.remove(self.file)
def onAttach(self, ev):
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return
self.file = openFileDialog.GetPath()
self.file_attached()
ev.Skip()

View File

@@ -4,17 +4,17 @@ import sys
import threading
import time
import json
import wx
from mysc import event
import logging
from utils import *
from pubsub import pub
#__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
log = logging.getLogger("extra.AudioUploader.transfer")
class Transfer(object):
def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, wxDialog=None, *args, **kwargs):
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
@@ -26,7 +26,6 @@ class Transfer(object):
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))
self.wxDialog = wxDialog
super(Transfer, self).__init__(*args, **kwargs)
def elapsed_time(self):
@@ -52,15 +51,15 @@ class Transfer(object):
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
else:
progress["eta"] = 0
info = event.event(event.EVT_OBJECT, 1)
info.SetItem(progress)
wx.PostEvent(self.wxDialog, info)
pub.sendMessage("uploading", data=progress)
def perform_transfer(self):
log.debug("starting upload...")
self.start_time = time.time()
self.curl.perform()
self.curl.close()
wx.CallAfter(self.complete_transfer)
log.debug("Upload finished.")
self.complete_transfer()
def perform_threaded(self):
self.background_thread = threading.Thread(target=self.perform_transfer)

View File

@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*-
import wx
from mysc import event
from utils import *
import widgetUtils
__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
class TransferDialog(wx.Dialog):
class TransferDialog(widgetUtils.BaseDialog):
def __init__(self, filename, *args, **kwargs):
super(TransferDialog, self).__init__(*args, **kwargs)
super(TransferDialog, 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)
@@ -37,7 +35,7 @@ class TransferDialog(wx.Dialog):
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
etaBox.Add(etaLabel)
etaBox.Add(self.eta)
# self.create_buttons()
self.create_buttons()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(fileBox)
sizer.Add(currentAmountBox)
@@ -46,10 +44,8 @@ class TransferDialog(wx.Dialog):
sizer.Add(etaBox)
sizer.Add(self.progress_bar)
self.pane.SetSizerAndFit(sizer)
self.Bind(event.MyEVT_OBJECT, self.update)
def update(self, ev):
data = ev.GetItem()
def update(self, data):
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
@@ -59,7 +55,12 @@ class TransferDialog(wx.Dialog):
def create_buttons(self):
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel)
def get_response(self):
self.Show()
def destroy(self):
self.Destroy()
class UploadDialog(TransferDialog):

View File

@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# 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, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import widgetUtils
import output
import logging
log = logging.getLogger("extra.AudioUploader.wx_UI")
class audioDialog(widgetUtils.BaseDialog):
def __init__(self, services):
log.debug("creating audio dialog.")
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.play = wx.Button(panel, -1, _(u"Play"))
self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"Pause"))
self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"Record"))
self.record.SetFocus()
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file"))
self.discard = wx.Button(panel, -1, _(u"Discard"))
self.discard.Disable()
label = wx.StaticText(panel, -1, _(u"Upload to"))
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
servicesBox.Add(label, 0, wx.ALL, 5)
servicesBox.Add(self.services, 0, wx.ALL, 5)
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
self.attach.Disable()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
btnSizer.Add(self.play, 0, wx.ALL, 5)
btnSizer.Add(self.pause, 0, wx.ALL, 5)
btnSizer.Add(self.record, 0, wx.ALL, 5)
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
btnSizer2.Add(cancel, 0, wx.ALL, 5)
sizer.Add(servicesBox, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
sizer.Add(btnSizer2, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def enable_control(self, control):
log.debug("Enabling control %s" % (control,))
if hasattr(self, control):
getattr(self, control).Enable()
def disable_control(self, control):
log.debug("Disabling control %s" % (control,))
if hasattr(self, control):
getattr(self, control).Disable()
def get_file(self):
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return False
return openFileDialog.GetPath()

View File

@@ -1 +1 @@
import gui
from soundsTutorial import soundsTutorial

View File

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

View File

@@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
import wx
import config
import os
import paths
import sound
class soundsTutorial(wx.Dialog):
def __init__(self):
self.actions = [
_(u"The tweet may contain a playable audio"),
_(u"A timeline has been created"),
_(u"A timeline has been deleted"),
_(u"You've received a direct message"),
_(u"You've sent a direct message"),
_(u"A bug has happened"),
_(u"You've added a tweet to your favourites"),
_(u"Someone's favourites have been updated"),
_(u"The tweet has coordinates to determine its location"),
_(u"There are no more tweets to read"),
_(u"A list has a new tweet"),
_(u"You can't add any more characters on the tweet"),
_(u"You've been mentioned "),
_(u"A new event has happened"),
_(u"TW Blue is ready "),
_(u"You've replied"),
_(u"You've retweeted"),
_(u"A search has been updated"),
_(u"the buffer for trending topics has been updated"),
_(u"There's a new tweet in the main buffer"),
_(u"You've sent a tweet"),
_(u"There's a new tweet in a timeline"),
_(u"You have a new follower"),
_(u"You've turned the volume up or down")]
self.files = os.listdir(paths.sound_path("default"))
super(soundsTutorial, self).__init__(None, -1)
if len(self.actions) > len(self.files):
wx.MessageDialog(None, _(u"It seems as though the currently used sound pack needs an update. %i fails are still be required to use this function. Make sure to obtain the needed lacking sounds or to contact with the sound pack developer.") % (len(self.actions) - len(self.files)), _(u"Error"), wx.ICON_ERROR).ShowModal()
self.Destroy()
self.SetTitle(_(u"Sounds tutorial"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
self.items = wx.ListBox(panel, 1, choices=self.actions, style=wx.LB_SINGLE)
self.items.SetSelection(0)
listBox = wx.BoxSizer(wx.HORIZONTAL)
listBox.Add(label)
listBox.Add(self.items)
play = wx.Button(panel, 1, (u"Play"))
play.SetDefault()
self.Bind(wx.EVT_BUTTON, self.onPlay, play)
close = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(play)
btnBox.Add(close)
sizer.Add(listBox)
sizer.Add(btnBox)
panel.SetSizer(sizer)
def onPlay(self, ev):
sound.player.play(self.files[self.items.GetSelection()])

View File

@@ -0,0 +1,11 @@
#Reverse sort, by Bill Dengler <codeofdusk@gmail.com> for use in TWBlue http://twblue.es
def invert_tuples(t):
"Invert a list of tuples, so that the 0th element becomes the -1th, and the -1th becomes the 0th."
res=[]
for i in t:
res.append(i[::-1])
return res
def reverse_sort(t):
"Sorts a list of tuples/lists by their last elements, not their first."
return invert_tuples(sorted(invert_tuples(t)))

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
import spellchecker
import platform
if platform.system() == "Windows":
from wx_ui import *

View File

@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# 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, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import output
import config
import languageHandler
from enchant.checker import SpellChecker
from enchant.errors import DictNotFoundError
class spellCheckerDialog(wx.Dialog):
def __init__(self, text, dictionary):
super(spellCheckerDialog, self).__init__(None, 1)
try:
if config.main["general"]["language"] == "system": self.checker = SpellChecker()
else: self.checker = SpellChecker(languageHandler.getLanguage())
self.checker.set_text(text)
except DictNotFoundError:
wx.MessageDialog(None, _(u"A bug has happened. There are no dictionaries available for the selected language in TW Blue"), _(u"Error"), wx.ICON_ERROR).ShowModal()
self.Destroy()
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
word = wx.StaticText(panel, -1, _(u"Mis-spelled word"))
self.word = wx.TextCtrl(panel, -1)
wordBox = wx.BoxSizer(wx.HORIZONTAL)
wordBox.Add(word)
wordBox.Add(self.word)
context = wx.StaticText(panel, -1, _(u"Context"))
self.context = wx.TextCtrl(panel, -1)
contextBox = wx.BoxSizer(wx.HORIZONTAL)
contextBox.Add(context)
contextBox.Add(self.context)
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
suggestionsBox.Add(suggest)
suggestionsBox.Add(self.suggestions)
ignore = wx.Button(panel, -1, _(u"Ignore"))
self.Bind(wx.EVT_BUTTON, self.onIgnore, ignore)
ignoreAll = wx.Button(panel, -1, _(u"Ignore all"))
self.Bind(wx.EVT_BUTTON, self.onIgnoreAll, ignoreAll)
replace = wx.Button(panel, -1, _(u"Replace"))
self.Bind(wx.EVT_BUTTON, self.onReplace, replace)
replaceAll = wx.Button(panel, -1, _(u"Replace all"))
self.Bind(wx.EVT_BUTTON, self.onReplaceAll, replaceAll)
close = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(ignore)
btnBox.Add(ignoreAll)
btnBox.Add(replace)
btnBox.Add(replaceAll)
btnBox.Add(close)
sizer.Add(wordBox)
sizer.Add(contextBox)
sizer.Add(suggestionsBox)
sizer.Add(btnBox)
panel.SetSizerAndFit(sizer)
self.check()
def check(self):
try:
self.checker.next()
textToSay = _(u"Mis-spelled word: %s") % (self.checker.word,)
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
self.SetTitle(textToSay)
output.speak(textToSay)
self.word.SetValue(self.checker.word)
self.context.ChangeValue(context)
self.suggestions.Set(self.checker.suggest())
self.suggestions.SetFocus()
except StopIteration:
wx.MessageDialog(self, _(u"The spelling review has finished."), _("Finished"), style=wx.OK).ShowModal()
self.EndModal(wx.ID_OK)
except AttributeError:
pass
def onIgnore(self, ev):
self.check()
def onIgnoreAll(self, ev):
self.checker.ignore_always(word=self.checker.word)
self.check()
def onReplace(self, ev):
self.checker.replace(self.suggestions.GetStringSelection())
self.check()
def onReplaceAll(self, ev):
self.checker.replace_always(self.suggestions.GetStringSelection())
self.check()

View File

@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
import logging
log = logging.getLogger("extra.SpellChecker.spellChecker")
import wx_ui
import widgetUtils
import output
import config
import languageHandler
from enchant.checker import SpellChecker
from enchant.errors import DictNotFoundError
from enchant import tokenize
import twitterFilter
class spellChecker(object):
def __init__(self, text, dictionary):
super(spellChecker, self).__init__()
log.debug("Creating the SpellChecker object. Dictionary: %s" % (dictionary,))
self.active = True
try:
if config.app["app-settings"]["language"] == "system":
log.debug("Using the system language")
self.checker = SpellChecker(filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter])
else:
log.debug("Using language: %s" % (languageHandler.getLanguage(),))
self.checker = SpellChecker(languageHandler.getLanguage(), filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter])
self.checker.set_text(text)
except DictNotFoundError:
log.exception("Dictionary for language %s not found." % (dictionary,))
wx_ui.dict_not_found_error()
self.active = False
if self.active == True:
log.debug("Creating dialog...")
self.dialog = wx_ui.spellCheckerDialog()
widgetUtils.connect_event(self.dialog.ignore, widgetUtils.BUTTON_PRESSED, self.ignore)
widgetUtils.connect_event(self.dialog.ignoreAll, widgetUtils.BUTTON_PRESSED, self.ignoreAll)
widgetUtils.connect_event(self.dialog.replace, widgetUtils.BUTTON_PRESSED, self.replace)
widgetUtils.connect_event(self.dialog.replaceAll, widgetUtils.BUTTON_PRESSED, self.replaceAll)
self.check()
self.dialog.get_response()
self.fixed_text = self.checker.get_text()
def check(self):
try:
self.checker.next()
textToSay = _(u"Misspelled word: %s") % (self.checker.word,)
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
self.dialog.set_title(textToSay)
output.speak(textToSay)
self.dialog.set_word_and_suggestions(word=self.checker.word, context=context, suggestions=self.checker.suggest())
except StopIteration:
log.debug("Process finished.")
wx_ui.finished()
self.dialog.Destroy()
# except AttributeError:
# pass
def ignore(self, ev):
self.check()
def ignoreAll(self, ev):
self.checker.ignore_always(word=self.checker.word)
self.check()
def replace(self, ev):
self.checker.replace(self.dialog.get_selected_suggestion())
self.check()
def replaceAll(self, ev):
self.checker.replace_always(self.dialog.get_selected_suggestion())
self.check()

View File

@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
import re
from enchant.tokenize import Filter
class TwitterFilter(Filter):
"""Filter skipping over twitter usernames and hashtags.
This filter skips any words matching the following regular expression:
^[#@](\S){1, }$
That is, any words that resemble users and hashtags.
"""
_pattern = re.compile(r"^[#@](\S){1,}$")
def _skip(self,word):
if self._pattern.match(word):
return True
return False

View File

@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# 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, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import application
class spellCheckerDialog(wx.Dialog):
def __init__(self):
super(spellCheckerDialog, self).__init__(None, 1)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
word = wx.StaticText(panel, -1, _(u"Misspelled word"))
self.word = wx.TextCtrl(panel, -1)
wordBox = wx.BoxSizer(wx.HORIZONTAL)
wordBox.Add(word, 0, wx.ALL, 5)
wordBox.Add(self.word, 0, wx.ALL, 5)
context = wx.StaticText(panel, -1, _(u"Context"))
self.context = wx.TextCtrl(panel, -1)
contextBox = wx.BoxSizer(wx.HORIZONTAL)
contextBox.Add(context, 0, wx.ALL, 5)
contextBox.Add(self.context, 0, wx.ALL, 5)
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
suggestionsBox.Add(suggest, 0, wx.ALL, 5)
suggestionsBox.Add(self.suggestions, 0, wx.ALL, 5)
self.ignore = wx.Button(panel, -1, _(u"Ignore"))
self.ignoreAll = wx.Button(panel, -1, _(u"Ignore all"))
self.replace = wx.Button(panel, -1, _(u"Replace"))
self.replaceAll = wx.Button(panel, -1, _(u"Replace all"))
close = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(self.ignore, 0, wx.ALL, 5)
btnBox.Add(self.ignoreAll, 0, wx.ALL, 5)
btnBox.Add(self.replace, 0, wx.ALL, 5)
btnBox.Add(self.replaceAll, 0, wx.ALL, 5)
btnBox.Add(close, 0, wx.ALL, 5)
sizer.Add(wordBox, 0, wx.ALL, 5)
sizer.Add(contextBox, 0, wx.ALL, 5)
sizer.Add(suggestionsBox, 0, wx.ALL, 5)
sizer.Add(btnBox, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def get_response(self):
return self.ShowModal()
def set_title(self, title):
return self.SetTitle(title)
def set_word_and_suggestions(self, word, context, suggestions):
self.word.SetValue(word)
self.context.ChangeValue(context)
self.suggestions.Set(suggestions)
self.suggestions.SetFocus()
def get_selected_suggestion(self):
return self.suggestions.GetStringSelection()
def dict_not_found_error():
wx.MessageDialog(None, _(u"An error has occurred. There are no dictionaries available for the selected language in " + application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
def finished():
wx.MessageDialog(None, _(u"Spell check complete."), _(application.name), style=wx.OK).ShowModal()

View File

@@ -1 +1,2 @@
# -*- coding: utf-8 -*-
import completion, settings

View File

@@ -1,31 +1,47 @@
# -*- coding: utf-8 -*-
import storage
import output
import storage
import wx_menu
class autocompletionUsers(object):
def __init__(self, window):
def __init__(self, window, session_id):
super(autocompletionUsers, self).__init__()
self.window = window
self.db = storage.storage(session_id)
def show_menu(self):
position = self.window.text.GetInsertionPoint()
text = self.window.text.GetValue()
text = text[:position]
try:
pattern = text.split()[-1]
except IndexError:
output.speak(_(u"You have to start writing"))
return
if pattern.startswith("@") == True:
db = storage.storage()
menu = wx_menu.menu(self.window.text, pattern[1:])
users = db.get_users(pattern[1:])
def show_menu(self, mode="tweet"):
position = self.window.get_position()
if mode == "tweet":
text = self.window.get_text()
text = text[:position]
try:
pattern = text.split()[-1]
except IndexError:
output.speak(_(u"You have to start writing"))
return
if pattern.startswith("@") == True:
menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode)
users = self.db.get_users(pattern[1:])
if len(users) > 0:
menu.append_options(users)
self.window.popup_menu(menu)
menu.destroy()
else:
output.speak(_(u"There are no results in your users database"))
else:
output.speak(_(u"Autocompletion only works for users."))
elif mode == "dm":
text = self.window.get_user()
try:
pattern = text.split()[-1]
except IndexError:
output.speak(_(u"You have to start writing"))
return
menu = wx_menu.menu(self.window.cb, pattern, mode=mode)
users = self.db.get_users(pattern)
if len(users) > 0:
menu.append_options(users)
self.window.PopupMenu(menu, self.window.text.GetPosition())
menu.Destroy()
self.window.popup_menu(menu)
menu.destroy()
else:
output.speak(_(u"There are not results in your users database"))
else:
output.speak(_(u"Autocompletion only works for users."))
output.speak(_(u"There are no results in your users database"))

View File

@@ -1,19 +1,20 @@
# -*- coding: utf-8 -*-
import storage
import wx
import widgetUtils
import wx_manage
from wxUI import commonMessageDialogs
class autocompletionManage(object):
def __init__(self, window):
def __init__(self, session):
super(autocompletionManage, self).__init__()
self.window = window
self.session = session
self.dialog = wx_manage.autocompletionManageDialog()
self.database = storage.storage()
self.database = storage.storage(self.session.session_id)
self.users = self.database.get_all_users()
self.dialog.put_users(self.users)
self.dialog.add.Bind(wx.EVT_BUTTON, self.add_user)
self.dialog.remove.Bind(wx.EVT_BUTTON, self.remove_user)
self.dialog.ShowModal()
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user)
self.dialog.get_response()
def update_list(self):
item = self.dialog.users.get_selected()
@@ -22,12 +23,12 @@ class autocompletionManage(object):
self.dialog.put_users(self.users)
self.dialog.users.select_item(item)
def add_user(self, event=None):
def add_user(self, *args, **kwargs):
usr = self.dialog.get_user()
if usr == False:
return
try:
data = self.window.twitter.twitter.show_user(screen_name=usr)
data = self.session.twitter.twitter.show_user(screen_name=usr)
except:
self.dialog.show_invalid_user_error()
return
@@ -35,8 +36,7 @@ class autocompletionManage(object):
self.update_list()
def remove_user(self, ev):
ask = wx.MessageDialog(None, _(u"Are you sure you want to delete this user from the database? This user will not appear on the autocomplete results anymore."), _(u"Confirm"), wx.YES_NO|wx.ICON_QUESTION)
if ask.ShowModal() == wx.ID_YES:
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
item = self.dialog.users.get_selected()
user = self.users[item]
self.database.remove_user(user[0])

View File

@@ -1,58 +1,59 @@
# -*- coding: utf-8 -*-
import storage
import wx
import config
import widgetUtils
import wx_settings
import manage
import output
from mysc.thread_utils import call_threaded
class autocompletionSettings(object):
def __init__(self, window):
def __init__(self, config, buffer, window):
super(autocompletionSettings, self).__init__()
self.config = config
self.buffer = buffer
self.window = window
self.dialog = wx_settings.autocompletionSettingsDialog()
self.dialog.friends_buffer.SetValue(config.main["mysc"]["save_friends_in_autocompletion_db"])
self.dialog.followers_buffer.SetValue(config.main["mysc"]["save_followers_in_autocompletion_db"])
self.dialog.viewList.Bind(wx.EVT_BUTTON, self.view_list)
if self.dialog.ShowModal() == wx.ID_OK:
self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"])
self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"])
widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list)
if self.dialog.get_response() == widgetUtils.OK:
call_threaded(self.add_users_to_database)
def add_users_to_database(self):
config.main["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.friends_buffer.GetValue()
config.main["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.friends_buffer.GetValue()
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer")
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer")
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
database = storage.storage()
if self.dialog.followers_buffer.GetValue() == True:
buffer = self.window.search_buffer("people", "followers")
for i in buffer.db.settings[buffer.name_buffer]:
database = storage.storage(self.buffer.session.session_id)
if self.dialog.get("followers_buffer") == True:
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
for i in buffer.session.db[buffer.name]["items"]:
database.set_user(i["screen_name"], i["name"], 1)
else:
database.remove_by_buffer(1)
if self.dialog.friends_buffer.GetValue() == True:
buffer = self.window.search_buffer("people", "friends")
for i in buffer.db.settings[buffer.name_buffer]:
if self.dialog.get("friends_buffer") == True:
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
for i in buffer.session.db[buffer.name]["items"]:
database.set_user(i["screen_name"], i["name"], 2)
else:
database.remove_by_buffer(2)
wx_settings.show_success_dialog()
self.dialog.Destroy()
self.dialog.destroy()
def view_list(self, ev):
q = manage.autocompletionManage(self.window)
q = manage.autocompletionManage(self.buffer.session)
def execute_at_startup(window):
database = storage.storage()
if config.main["mysc"]["save_followers_in_autocompletion_db"] == True and config.main["other_buffers"]["show_followers"] == True:
buffer = window.search_buffer("people", "followers")
for i in buffer.db.settings[buffer.name_buffer]:
def execute_at_startup(window, buffer, config):
database = storage.storage(buffer.session.session_id)
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
for i in buffer.session.db[buffer.name]:
database.set_user(i["screen_name"], i["name"], 1)
else:
database.remove_by_buffer(1)
if config.main["mysc"]["save_friends_in_autocompletion_db"] == True and config.main["other_buffers"]["show_friends"] == True:
buffer = window.search_buffer("people", "friends")
for i in buffer.db.settings[buffer.name_buffer]:
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
for i in buffer.session.db[buffer.name]:
database.set_user(i["screen_name"], i["name"], 2)
else:
database.remove_by_buffer(2)

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
import sqlite3, paths
from sessionmanager import manager
class storage(object):
def __init__(self):
self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (manager.manager.get_current_session())))
def __init__(self, session_id):
self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (session_id)))
self.cursor = self.connection.cursor()
if self.table_exist("users") == False:
self.create_table()

View File

@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
import wx
import widgetUtils
from multiplatform_widgets import widgets
class autocompletionManageDialog(wx.Dialog):
import application
class autocompletionManageDialog(widgetUtils.BaseDialog):
def __init__(self):
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocomplete users database"))
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(panel, -1, _(u"Editing TWBlue users database"))
label = wx.StaticText(panel, -1, _(u"Editing " + application.name + " users database"))
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
sizer.Add(label, 0, wx.ALL, 5)
sizer.Add(self.users.list, 0, wx.ALL, 5)

View File

@@ -2,10 +2,11 @@
import wx
class menu(wx.Menu):
def __init__(self, window, pattern):
def __init__(self, window, pattern, mode):
super(menu, self).__init__()
self.window = window
self.pattern = pattern
self.mode = mode
def append_options(self, options):
for i in options:
@@ -14,5 +15,11 @@ class menu(wx.Menu):
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
def select_text(self, ev, text):
self.window.ChangeValue(self.window.GetValue().replace(self.pattern, text+" "))
self.window.SetInsertionPointEnd()
if self.mode == "tweet":
self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" "))
elif self.mode == "dm":
self.window.SetValue(self.window.GetValue().replace(self.pattern, text))
self.window.SetInsertionPointEnd()
def destroy(self):
self.Destroy()

View File

@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
import wx
import widgetUtils
import application
class autocompletionSettingsDialog(wx.Dialog):
class autocompletionSettingsDialog(widgetUtils.BaseDialog):
def __init__(self):
super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users settings"))
panel = wx.Panel(self)
@@ -10,7 +12,7 @@ class autocompletionSettingsDialog(wx.Dialog):
self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer"))
sizer.Add(self.followers_buffer, 0, wx.ALL, 5)
sizer.Add(self.friends_buffer, 0, wx.ALL, 5)
self.viewList = wx.Button(panel, -1, _(u"See the users list"))
self.viewList = wx.Button(panel, -1, _(u"Manage database..."))
sizer.Add(self.viewList, 0, wx.ALL, 5)
ok = wx.Button(panel, wx.ID_OK)
cancel = wx.Button(panel, wx.ID_CANCEL)
@@ -22,4 +24,4 @@ class autocompletionSettingsDialog(wx.Dialog):
self.SetClientSize(sizer.CalcMin())
def show_success_dialog():
wx.MessageDialog(None, _(u"TWBlue's database of users has been updated."), _(u"Done"), wx.OK).ShowModal()
wx.MessageDialog(None, _(uapplication.name+"'s database of users has been updated."), _(u"Done"), wx.OK).ShowModal()

View File

@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from translator import *
import gui
import translator
import platform
if platform.system() == "Windows":
import wx_ui as gui

View File

@@ -1,53 +1,9 @@
# -*- coding: utf-8 -*-
import re
try:
import urllib2 as request
from urllib import quote
except:
from urllib import request
from urllib.parse import quote
#!/usr/bin/env python
import goslate
class Translator:
string_pattern = r"\"(([^\"\\]|\\.)*)\""
match_string =re.compile(
r"\,?\["
+ string_pattern + r"\,"
+ string_pattern + r"\,"
+ string_pattern + r"\,"
+ string_pattern
+r"\]")
def __init__(self):
self.from_lang = ""
self.to_lang = ""
def translate(self, source):
json5 = self._get_json5_from_google(source)
return self._unescape(self._get_translation_from_json5(json5))
def _get_translation_from_json5(self, content):
result = ""
pos = 2
while True:
m = self.match_string.match(content, pos)
if not m:
break
result += m.group(1)
pos = m.end()
return result
def _get_json5_from_google(self, source):
escaped_source = quote(source, '')
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19'}
req = request.Request(
url="http://translate.google.com/translate_a/t?client=t&ie=UTF-8&oe=UTF-8"
+"&sl=%s&tl=%s&text=%s" % (self.from_lang, self.to_lang, escaped_source)
, headers = headers)
r = request.urlopen(req)
return r.read().decode('utf-8')
def _unescape(self, text):
return re.sub(r"\\.?", lambda x:eval('"%s"'%x.group(0)), text)
def translate(text, source_lang, target_lang):
gs = goslate.Goslate()
return gs.translate(text, target_lang, source_lang)
languages = {
"af": _(u"Afrikaans"),

View File

@@ -16,12 +16,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import translator
import wx
from wxUI.dialogs import baseDialog
class translateDialog(wx.Dialog):
class translateDialog(baseDialog.BaseWXDialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, title=_(u"Translate message"))
super(translateDialog, self).__init__(None, -1, title=_(u"Translate message"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
staticSource = wx.StaticText(panel, -1, _(u"Source language"))
@@ -40,5 +41,5 @@ class translateDialog(wx.Dialog):
cancel = wx.Button(panel, wx.ID_CANCEL)
self.SetEscapeId(wx.ID_CANCEL)
def onOk(self, ev):
self.EndModal(wx.ID_OK)
def get(self, control):
return getattr(self, control).GetSelection()

6
src/fixes/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
""" This module contains some bugfixes for packages used in TWBlue."""
import fix_arrow # A few new locales for Three languages in arrow.
def setup():
fix_arrow.fix()

91
src/fixes/fix_arrow.py Normal file
View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
from arrow import locales
from arrow.locales import Locale
def fix():
''' This function adds the Catala, Basque and galician locales to the list of locales supported in Arrow.
it also fixes capitalizations in names from turkish and arabian locales.
see https://github.com/crsmithdev/arrow/pull/207 for following the pull request.'''
locales.CatalaLocale = CatalaLocale
locales.GalicianLocale = GalicianLocale
locales.BasqueLocale = BasqueLocale
locales.TurkishLocale.names[-1] = "tr_tr"
locales.ArabicLocale.names[-1] = "ar_eg"
# We need to reassign the locales list for updating the list with our new contents.
locales._locales = locales._map_locales()
class CatalaLocale(Locale):
names = ['ca', 'ca_ca']
past = 'Fa {0}'
future = '{0}' # I don't know what's the right phrase in catala for the future.
timeframes = {
'now': 'Ara mateix',
'seconds': 'segons',
'minute': '1 minut',
'minutes': '{0} minuts',
'hour': 'una hora',
'hours': '{0} hores',
'day': 'un dia',
'days': '{0} dies',
'month': 'un mes',
'months': '{0} messos',
'year': 'un any',
'years': '{0} anys',
}
month_names = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
month_abbreviations = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
day_names = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
day_abbreviations = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
class GalicianLocale(Locale):
names = ['gl', 'gl_gl']
past = 'Fai {0}'
future = '{0}' # I don't know what's the right phrase in Galician for the future.
timeframes = {
'now': 'Agora mesmo',
'seconds': 'segundos',
'minute': 'un minuto',
'minutes': '{0} minutos',
'hour': 'una hora',
'hours': '{0} horas',
'day': 'un día',
'days': '{0} días',
'month': 'un mes',
'months': '{0} meses',
'year': 'un ano',
'years': '{0} anos',
}
month_names = ['', 'Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro']
month_abbreviations = ['', 'Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xun', 'Xul', 'Ago', 'Set', 'Out', 'Nov', 'Dec']
day_names = ['', 'Luns', 'Martes', 'Mércores', 'Xoves', 'Venres', 'Sábado', 'Domingo']
day_abbreviations = ['', 'Lun', 'Mar', 'Mer', 'xov', 'Ven' 'Sab', 'Dom']
class BasqueLocale(Locale):
names = ['eu', 'eu_eu']
past = 'duela {0}'
future = '{0}' # I don't know what's the right phrase in Basque for the future.
timeframes = {
'now': 'Orain',
'seconds': 'segundu',
'minute': 'minutu bat',
'minutes': '{0} minutu',
'hour': 'ordu bat',
'hours': '{0} ordu',
'day': 'egun bat',
'days': '{0} egun',
'month': 'hilabete bat',
'months': '{0} hilabete',
'year': 'urte bat',
'years': '{0} urte',
}
month_names = ['', 'Urtarrilak', 'Otsailak', 'Martxoak', 'Apirilak', 'Maiatzak', 'Ekainak', 'Uztailak', 'Abuztuak', 'Irailak', 'Urriak', 'Azaroak', 'Abenduak']
month_abbreviations = ['', 'urt', 'ots', 'mar', 'api', 'mai', 'eka', 'uzt', 'abu', 'ira', 'urr', 'aza', 'abe']
day_names = ['', 'Asteleehna', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata', 'Igandea']
day_abbreviations = ['', 'al', 'ar', 'az', 'og', 'ol', 'lr', 'ig']

11
src/gtkUI/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
""" This is the GTK view module for TWBlue.
As of April 3 2015, there are the things that have been implemented:
* the main view (partially implemented)
* All buffers.
* Three of the most common message dialogs.
* Dialogs for tweet, reply, retweet, send a direct message and view a tweet.
And we need to implement:
* All the other dialogs.
"""

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from base import basePanel
from dm import dmPanel
from events import eventsPanel
from favourites import favsPanel
from lists import listPanel
from panels import accountPanel, emptyPanel
from people import peoplePanel
from trends import trendsPanel
from tweet_searches import searchPanel
from user_searches import searchUsersPanel

35
src/gtkUI/buffers/base.py Normal file
View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
import widgetUtils
from gi.repository import Gtk
class basePanel(Gtk.VBox):
def create_list(self):
self.list = widgetUtils.list(_(u"User"), _(u"Text"), _(u"Date"), _(u"Client"))
def __init__(self, parent, name):
super(basePanel, self).__init__(spacing=6)
self.name = name
self.type = "baseBuffer"
self.create_list()
self.tweet = Gtk.Button(_(u"Tweet"))
self.retweet = Gtk.Button(_(u"Retweet"))
self.reply = Gtk.Button(_(u"Reply"))
self.dm = Gtk.Button(_(u"Direct message"))
btnSizer = Gtk.Box(spacing=6)
btnSizer.add(self.tweet)
btnSizer.add(self.retweet)
btnSizer.add(self.reply)
btnSizer.add(self.dm)
self.add(self.list.list)
self.add(btnSizer)
def set_position(self, reversed=False):
if reversed == False:
self.list.select_item(self.list.get_count()-1)
else:
self.list.select_item(0)
def set_focus_function(self, f):
tree_selection = self.list.list.get_selection()
tree_selection.connect("changed", f)

14
src/gtkUI/buffers/dm.py Normal file
View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import widgetUtils
from base import basePanel
class dmPanel(basePanel):
def __init__(self, parent, name):
""" Class to DM'S. Reply and retweet buttons are not showed and they have your delete method for dm's."""
super(dmPanel, self).__init__(parent, name)
self.retweet.hide()
self.retweet.set_no_show_all(True)
self.reply.hide()
self.reply.set_no_show_all(True)
self.type = "dm"

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
import widgetUtils
from gi.repository import Gtk
class eventsPanel(Gtk.VBox):
""" Buffer to show events. Different than tweets or people."""
def __init__(self, parent, name):
self.type = "event"
super(eventsPanel, self).__init__(spacing=6)
self.name = name
self.list = widgetUtils.list(_(u"Date"), _(u"Event"))
self.add(self.list.list)
self.tweet = Gtk.Button(_(u"Tweet"))
self.delete_event = Gtk.Button(_(u"Remove event"))
btnBox = Gtk.Box(spacing=6)
btnBox.add(self.tweet)
btnBox.add(self.delete_event)
self.add(btnBox)
def set_position(self, reversed=False):
if reversed == False:
self.list.select_item(self.list.get_count()-1)
else:
self.list.select_item(0)

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
from base import basePanel
class favsPanel(basePanel):
def __init__(self, parent, name):
super(favsPanel, self).__init__(parent, name)
self.type = "favourites_timeline"

View File

@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
from base import basePanel
class listPanel(basePanel):
def __init__(self, parent, name):
super(listPanel, self).__init__(parent, name)
self.type = "list"
self.users = []

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import widgetUtils
class accountPanel(Gtk.VBox):
def __init__(self, parent, name=None):
super(accountPanel, self).__init__(spacing=5)
self.name = name
self.type = "account"
self.login = Gtk.Button(_(u"Login"))
self.add(self.login)
self.autostart_account = Gtk.ToggleButton(_(u"Log in automatically"))
self.add(self.autostart_account)
def change_login(self, login=True):
if login == True:
self.login.set_label(_(u"Login"))
else:
self.login.set_label(_(u"Logout"))
def change_autostart(self, autostart=True):
self.autostart_account.set_active(autostart)
def get_autostart(self):
print "actived"
print self.autostart_account.get_active()
return self.autostart_account.get_active()
class emptyPanel(Gtk.VBox):
def __init__(self, parent, name):
super(emptyPanel, self).__init__(spacing=6)
self.name = name
self.type = "account"

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import widgetUtils
from base import basePanel
class peoplePanel(basePanel):
""" Buffer used to show people."""
def create_list(self):
self.list = widgetUtils.list(_(u"User"))
def __init__(self, parent, name):
super(peoplePanel, self).__init__(parent, name)
self.type = "people"
self.reply.set_label(_(u"Mention"))
self.retweet.hide()
self.retweet.set_no_show_all(True)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import widgetUtils
class trendsPanel(Gtk.VBox):
def create_list(self):
""" Returns the list for put the tweets here."""
self.list = widgetUtils.list(_(u"Trending topic"))
def __init__(self, parent, name):
super(trendsPanel, self).__init__(spacing=6)
self.type = "trends"
self.create_list()
self.tweet = Gtk.Button(_(u"Tweet"))
self.tweetTrendBtn = Gtk.Button(_(u"Tweet about this trend"))
btnSizer = Gtk.Box(spacing=3)
btnSizer.add(self.tweet)
btnSizer.add(self.tweetTrendBtn)
self.add(btnSizer)
self.Add(self.list.list)
def set_position(self, reversed=False):
if reversed == False:
self.list.select_item(self.list.get_count()-1)
else:
self.list.select_item(0)

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
from base import basePanel
class searchPanel(basePanel):
def __init__(self, parent, name):
super(searchPanel, self).__init__(parent, name)
self.type = "search"

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
from tweet_searches import searchPanel
import widgetUtils
class searchUsersPanel(searchPanel):
def create_list(self):
""" Returns the list for put the tweets here."""
self.list = widgetUtils.list(_(u"User"))
def __init__(self, parent, name):
self.create_list()
super(searchUsersPanel, self).__init__(parent, name)
self.type = "user_searches"

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import application
def retweet_as_link(parent):
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, unicode(application.name))
dialog.format_secondary_text(_(u"This retweet is over 140 characters. Would you like to post it as a mention to the poster with your comments and a link to the original tweet?"))
answer = dialog.run()
dialog.destroy()
return answer
def retweet_question(parent):
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _(u"Retweet"))
dialog.format_secondary_text(_(u"Would you like to add a comment to this tweet?"))
answer = dialog.run()
dialog.destroy()
return answer
def delete_tweet_dialog(parent):
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _(u"Delete"))
dialog.format_secondary_text(_(u"Do you really want to delete this message? It will be deleted from Twitter as well."))
answer = dialog.run()
dialog.destroy()
return answer
def exit_dialog(parent):
dialog = Gtk.MessageDialog(parent, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _(u"Exit"))
dialog.format_secondary_text(_(u"Do you really want to close " + application.name + "?"))
answer = dialog.run()
dialog.destroy()
return answer
def needs_restart():
wx.MessageDialog(None, _(unicode(application.name+" must be restarted to save these changes. Press OK to restart now.")), _("Restart " + application.name), wx.OK).ShowModal()
def delete_user_from_db():
return wx.MessageDialog(None, _(u"Are you sure you want to delete this user from the database? This user will not appear on the autocomplete results anymore."), _(u"Confirm"), wx.YES_NO|wx.ICON_QUESTION).ShowModal()
def get_ignored_client():
entry = wx.TextEntryDialog(None, _(u"Enter the name of the client here"), _(u"Add a new ignored client"))
if entry.ShowModal() == wx.ID_OK:
return entry.GetValue()
return None
def clear_list():
dlg = wx.MessageDialog(None, _(u"Do you really want to empty this buffer? It's items will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
return dlg.ShowModal()
def remove_buffer():
return wx.MessageDialog(None, _(u"Do you really want to delete this timeline?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO).ShowModal()
def user_not_exist():
return wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
def timeline_exist():
return wx.MessageDialog(None, _(u"There's currently a timeline for this user. You are not able to open another"), _(u"Existing timeline"), wx.ICON_ERROR).ShowModal()
def no_tweets():
return wx.MessageDialog(None, _(u"This user has no tweets. You can't open a timeline for this user"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
def protected_user():
return wx.MessageDialog(None, _(u"This is a protected Twitter user. It means you can not open a timeline using the Streaming API. The user's tweets will not update due to a twitter policy. Do you want to continue?"), _(u"Warning"), wx.ICON_WARNING|wx.YES_NO).ShowModal()
def no_following():
return wx.MessageDialog(None, _(u"This is a protected user account, you need follow to this user for viewing your tweets or favourites."), _(u"Error"), wx.ICON_ERROR).ShowModal()

View File

@@ -0,0 +1 @@
#import trends, configuration, lists, message, search, show_user, update_profile, urlList, userSelection, utils

View File

@@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
import baseDialog
import wx
import logging as original_logger
import application
class general(wx.Panel, baseDialog.BaseWXDialog):
def __init__(self, parent, languages):
super(general, self).__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
language = wx.StaticText(self, -1, _(u"Language"))
self.language = wx.ListBox(self, -1, choices=languages)
self.language.SetSize(self.language.GetBestSize())
langBox = wx.BoxSizer(wx.HORIZONTAL)
langBox.Add(language, 0, wx.ALL, 5)
langBox.Add(self.language, 0, wx.ALL, 5)
sizer.Add(langBox, 0, wx.ALL, 5)
self.ask_at_exit = wx.CheckBox(self, -1, _(U"ask before exiting " + application.name))
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
self.use_invisible_shorcuts = wx.CheckBox(self, -1, _(u"Use invisible interface's keyboard shortcuts while GUI is visible"))
sizer.Add(self.use_invisible_shorcuts, 0, wx.ALL, 5)
self.disable_sapi5 = wx.CheckBox(self, -1, _(u"Activate Sapi5 when any other screen reader is not being run"))
sizer.Add(self.disable_sapi5, 0, wx.ALL, 5)
self.hide_gui = wx.CheckBox(self, -1, _(u"Hide GUI on launch"))
sizer.Add(self.hide_gui, 0, wx.ALL, 5)
self.SetSizer(sizer)
class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
def __init__(self, parent):
super(generalAccount, self).__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.au = wx.Button(self, wx.NewId(), _(u"Set the autocomplete function"))
sizer.Add(self.au, 0, wx.ALL, 5)
self.relative_time = wx.CheckBox(self, wx.NewId(), _(U"Relative times"))
sizer.Add(self.relative_time, 0, wx.ALL, 5)
apiCallsBox = wx.BoxSizer(wx.HORIZONTAL)
apiCallsBox.Add(wx.StaticText(self, -1, _(u"API calls when the stream is started (One API call equals to 200 tweetts, two API calls equals 400 tweets, etc):")), 0, wx.ALL, 5)
self.apiCalls = wx.SpinCtrl(self, wx.NewId())
self.apiCalls.SetRange(1, 10)
self.apiCalls.SetSize(self.apiCalls.GetBestSize())
apiCallsBox.Add(self.apiCalls, 0, wx.ALL, 5)
sizer.Add(apiCallsBox, 0, wx.ALL, 5)
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
tweetsPerCallBox.Add(wx.StaticText(self, -1, _(u"Items on each API call")), 0, wx.ALL, 5)
self.itemsPerApiCall = wx.SpinCtrl(self, wx.NewId())
self.itemsPerApiCall.SetRange(0, 200)
self.itemsPerApiCall.SetSize(self.itemsPerApiCall.GetBestSize())
tweetsPerCallBox.Add(self.itemsPerApiCall, 0, wx.ALL, 5)
sizer.Add(tweetsPerCallBox, 0, wx.ALL, 5)
self.reverse_timelines = wx.CheckBox(self, wx.NewId(), _(u"Inverted buffers: The newest tweets will be shown at the beginning of the lists while the oldest at the end"))
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
self.SetSizer(sizer)
class other_buffers(wx.Panel):
def __init__(self, parent):
super(other_buffers, self).__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
class ignoredClients(wx.Panel):
def __init__(self, parent, choices):
super(ignoredClients, self).__init__(parent=parent)
sizer = wx.BoxSizer(wx.VERTICAL)
label = wx.StaticText(self, -1, _(u"Ignored clients"))
self.clients = wx.ListBox(self, -1, choices=choices)
self.clients.SetSize(self.clients.GetBestSize())
clientsBox = wx.BoxSizer(wx.HORIZONTAL)
clientsBox.Add(label, 0, wx.ALL, 5)
clientsBox.Add(self.clients, 0, wx.ALL, 5)
self.add = wx.Button(self, -1, _(u"Add client"))
self.remove = wx.Button(self, -1, _(u"Remove client"))
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(self.add, 0, wx.ALL, 5)
btnBox.Add(self.remove, 0, wx.ALL, 5)
sizer.Add(clientsBox, 0, wx.ALL, 5)
sizer.Add(btnBox, 0, wx.ALL, 5)
self.SetSizer(sizer)
def append(self, client):
self.clients.Append(client)
def get_clients(self):
return self.clients.GetCount()
def get_client_id(self):
return self.clients.GetSelection()
def remove_(self, id):
self.clients.Delete(id)
class sound(wx.Panel):
def __init__(self, parent, input_devices, output_devices, soundpacks):
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer(wx.VERTICAL)
volume = wx.StaticText(self, -1, _(u"Volume"))
self.volumeCtrl = wx.Slider(self)
self.volumeCtrl.SetRange(0, 100)
self.volumeCtrl.SetSize(self.volumeCtrl.GetBestSize())
volumeBox = wx.BoxSizer(wx.HORIZONTAL)
volumeBox.Add(volume, 0, wx.ALL, 5)
volumeBox.Add(self.volumeCtrl, 0, wx.ALL, 5)
sizer.Add(volumeBox, 0, wx.ALL, 5)
self.session_mute = wx.CheckBox(self, -1, _(u"Session mute"))
sizer.Add(self.session_mute, 0, wx.ALL, 5)
output_label = wx.StaticText(self, -1, _(u"Output device"))
self.output = wx.ComboBox(self, -1, choices=output_devices, style=wx.CB_READONLY)
self.output.SetSize(self.output.GetBestSize())
outputBox = wx.BoxSizer(wx.HORIZONTAL)
outputBox.Add(output_label, 0, wx.ALL, 5)
outputBox.Add(self.output, 0, wx.ALL, 5)
sizer.Add(outputBox, 0, wx.ALL, 5)
input_label = wx.StaticText(self, -1, _(u"Input device"))
self.input = wx.ComboBox(self, -1, choices=input_devices, style=wx.CB_READONLY)
self.input.SetSize(self.input.GetBestSize())
inputBox = wx.BoxSizer(wx.HORIZONTAL)
inputBox.Add(input_label, 0, wx.ALL, 5)
inputBox.Add(self.input, 0, wx.ALL, 5)
sizer.Add(inputBox, 0, wx.ALL, 5)
soundBox = wx.BoxSizer(wx.VERTICAL)
soundpack_label = wx.StaticText(self, -1, _(u"Sound pack"))
self.soundpack = wx.ComboBox(self, -1, choices=soundpacks, style=wx.CB_READONLY)
self.soundpack.SetSize(self.soundpack.GetBestSize())
soundBox.Add(soundpack_label, 0, wx.ALL, 5)
soundBox.Add(self.soundpack, 0, wx.ALL, 5)
sizer.Add(soundBox, 0, wx.ALL, 5)
self.SetSizer(sizer)
def get(self, control):
return getattr(self, control).GetStringSelection()
class audioServicesPanel(wx.Panel):
def __init__(self, parent):
super(audioServicesPanel, self).__init__(parent)
mainSizer = wx.BoxSizer(wx.VERTICAL)
apiKeyLabel = wx.StaticText(self, -1, _(u"If you've got a SndUp account, enter your API Key here. Whether the API Key is wrong, the App will fail to upload anything to the server. Whether there's no API Key here, then the audio files will be uploaded anonimously"))
self.apiKey = wx.TextCtrl(self, -1)
dc = wx.WindowDC(self.apiKey)
dc.SetFont(self.apiKey.GetFont())
self.apiKey.SetSize(dc.GetTextExtent("0"*100))
apiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
apiKeyBox.Add(apiKeyLabel, 0, wx.ALL, 5)
apiKeyBox.Add(self.apiKey, 0, wx.ALL, 5)
mainSizer.Add(apiKeyBox, 0, wx.ALL, 5)
first_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.dropbox = wx.Button(self, -1)
first_sizer.Add(self.dropbox, 0, wx.ALL, 5)
mainSizer.Add(first_sizer, 0, wx.ALL, 5)
self.SetSizer(mainSizer)
def set_dropbox(self, active=True):
if active == True:
self.dropbox.SetLabel(_(u"Unlink your Dropbox account"))
else:
self.dropbox.SetLabel(_(u"Link your Dropbox account"))
def show_dialog(self):
wx.MessageDialog(self, _(u"The authorization request will be opened in your browser. Copy the code from Dropbox and paste it into the text box which will appear. You only need to do this once."), _(u"Authorization"), wx.OK).ShowModal()
def get_response(self):
dlg = wx.TextEntryDialog(self, _(u"Enter the code here."), _(u"Verification code"))
if dlg.ShowModal() == wx.ID_CANCEL:
return False
return dlg.GetValue()
def show_error(self):
wx.MessageDialog(self, _(u"Error during authorisation. Try again later."), _(u"Error!"), wx.ICON_ERROR).ShowModal()
def get_dropbox(self):
return self.dropbox.GetLabel()
class configurationDialog(baseDialog.BaseWXDialog):
def set_title(self, title):
self.SetTitle(title)
def __init__(self):
super(configurationDialog, self).__init__(None, -1)
self.panel = wx.Panel(self)
self.SetTitle(_(u"TW Blue preferences"))
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.notebook = wx.Notebook(self.panel)
def create_general(self, languageList):
self.general = general(self.notebook, languageList)
self.notebook.AddPage(self.general, _(u"General"))
self.general.SetFocus()
def create_general_account(self):
self.general = generalAccount(self.notebook)
self.notebook.AddPage(self.general, _(u"General"))
self.general.SetFocus()
def create_other_buffers(self):
self.buffers = other_buffers(self.notebook)
self.notebook.AddPage(self.buffers, _(u"Show other buffers"))
def create_ignored_clients(self, ignored_clients_list):
self.ignored_clients = ignoredClients(self.notebook, ignored_clients_list)
self.notebook.AddPage(self.ignored_clients, _(u"Ignored clients"))
def create_sound(self, output_devices, input_devices, soundpacks):
self.sound = sound(self.notebook, output_devices, input_devices, soundpacks)
self.notebook.AddPage(self.sound, _(u"Sound"))
def create_audio_services(self):
self.services = audioServicesPanel(self.notebook)
self.notebook.AddPage(self.services, _(u"Audio Services"))
def realize(self):
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
ok = wx.Button(self.panel, wx.ID_OK, _(u"Save"))
ok.SetDefault()
cancel = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"))
self.SetEscapeId(cancel.GetId())
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
self.sizer.Add(ok_cancel_box, 0, wx.ALL, 5)
self.panel.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())
def get_value(self, panel, key):
p = getattr(self, panel)
return getattr(p, key).GetValue()
def set_value(self, panel, key, value):
p = getattr(self, panel)
control = getattr(p, key)
getattr(control, "SetValue")(value)

123
src/gtkUI/dialogs/lists.py Normal file
View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
import wx
from multiplatform_widgets import widgets
class listViewer(wx.Dialog):
def __init__(self, *args, **kwargs):
super(listViewer, self).__init__(parent=None, *args, **kwargs)
self.SetTitle(_(u"Lists manager"))
panel = wx.Panel(self)
label = wx.StaticText(panel, -1, _(u"Lists"))
self.lista = widgets.list(panel, _(u"List"), _(u"Description"), _(u"Owner"), _(u"Members"), _(u"mode"), size=(800, 800), style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
self.lista.list.SetFocus()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(label)
sizer.Add(self.lista.list)
self.createBtn = wx.Button(panel, wx.NewId(), _(u"Create a new list"))
self.editBtn = wx.Button(panel, -1, _(u"Edit"))
self.deleteBtn = wx.Button(panel, -1, _(u"Remove"))
self.view = wx.Button(panel, -1, _(u"Open in buffer"))
# self.members = wx.Button(panel, -1, _(u"View members"))
# self.members.Disable()
# self.subscriptors = wx.Button(panel, -1, _(u"View subscribers"))
# self.subscriptors.Disable()
# self.get_linkBtn = wx.Button(panel, -1, _(u"Get link for the list"))
# self.get_linkBtn.Bind(wx.EVT_BUTTON, self.onGetLink)
self.cancelBtn = wx.Button(panel, wx.ID_CANCEL)
btnSizer = wx.BoxSizer()
btnSizer.Add(self.createBtn)
btnSizer.Add(self.editBtn)
btnSizer.Add(self.cancelBtn)
panel.SetSizer(sizer)
def populate_list(self, lists):
for item in lists:
self.lista.insert_item(False, *item)
def get_item(self):
return self.lista.get_selected()
class userListViewer(listViewer):
def __init__(self, username, *args, **kwargs):
self.username = username
super(userListViewer, self).__init__(*args, **kwargs)
self.SetTitle(_(u"Viewing lists for %s") % (self.username))
self.createBtn.SetLabel(_(u"Subscribe"))
self.deleteBtn.SetLabel(_(u"Unsubscribe"))
self.editBtn.Disable()
self.view.Disable()
class createListDialog(wx.Dialog):
def __init__(self, *args, **kwargs):
super(createListDialog, self).__init__(*args, **kwargs)
self.SetTitle(_(u"Create a new list"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
name = wx.StaticText(panel, -1, _(u"Name (20 characters maximun)"))
self.name = wx.TextCtrl(panel, -1)
nameSizer = wx.BoxSizer(wx.HORIZONTAL)
nameSizer.Add(name)
nameSizer.Add(self.name)
description = wx.StaticText(panel, -1, _(u"Description"))
self.description = wx.TextCtrl(panel, -1)
descriptionSizer = wx.BoxSizer(wx.HORIZONTAL)
descriptionSizer.Add(description)
descriptionSizer.Add(self.description)
mode = wx.StaticText(panel, -1, _(u"Mode"))
self.public = wx.RadioButton(panel, -1, _(u"Public"), style=wx.RB_GROUP)
self.private = wx.RadioButton(panel, -1, _(u"Private"))
modeBox = wx.BoxSizer(wx.HORIZONTAL)
modeBox.Add(mode)
modeBox.Add(self.public)
modeBox.Add(self.private)
ok = wx.Button(panel, wx.ID_OK)
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(ok)
btnBox.Add(cancel)
sizer.Add(nameSizer)
sizer.Add(descriptionSizer)
sizer.Add(modeBox)
sizer.Add(btnBox)
def get(self, field):
return getattr(self, field).GetValue()
class editListDialog(createListDialog):
def __init__(self, list, *args, **kwargs):
super(editListDialog, self).__init__(*args, **kwargs)
self.SetTitle(_(u"Editing the list %s") % (list["name"]))
self.name.ChangeValue(list["name"])
self.description.ChangeValue(list["description"])
if list["mode"] == "public":
self.public.SetValue(True)
else:
self.private.SetValue(True)
class addUserListDialog(listViewer):
def __init__(self, *args, **kwargs):
super(addUserListDialog, self).__init__(*args, **kwargs)
self.SetTitle(_(u"Select a list to add the user"))
self.createBtn.SetLabel(_(u"Add"))
self.createBtn.SetDefault()
self.editBtn.Disable()
self.view.Disable()
# self.subscriptors.Disable()
# self.members.Disable()
self.deleteBtn.Disable()
class removeUserListDialog(listViewer):
def __init__(self, *args, **kwargs):
super(removeUserListDialog, self).__init__(*args, **kwargs)
self.SetTitle(_(u"Select a list to remove the user"))
self.createBtn.SetLabel(_(u"Remove"))
self.createBtn.SetDefault()
self.editBtn.Disable()
self.view.Disable()
# self.subscriptors.Disable()
# self.members.Disable()
self.deleteBtn.Disable()

View File

@@ -0,0 +1,275 @@
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import widgetUtils
class textLimited(widgetUtils.baseDialog):
def __init__(self, *args, **kwargs):
super(textLimited, self).__init__(buttons=(Gtk.STOCK_OK, widgetUtils.OK, Gtk.STOCK_CANCEL, widgetUtils.CANCEL), *args, **kwargs)
def createTextArea(self, message="", text=""):
self.label = Gtk.Label(message)
self.set_title(message, titleWindow=True)
self.text = Gtk.Entry()
self.text.set_max_length(140)
self.text.set_text(text)
self.text.set_placeholder_text(message)
self.set_title(str(len(text)))
self.textBox = Gtk.Box(spacing=10)
self.textBox.add(self.label)
self.textBox.add(self.text)
def get(self, control):
if control == "upload_image":
return self.upload_image.get_label()
elif control == "cb":
return self.cb.get_active_text()
def set(self, control, val):
if control == "upload_image":
self.upload_image.set_label(val)
elif control == "cb":
self.cb.set_active_text(val)
def text_focus(self):
self.text.grab_focus()
def get_text(self):
return self.text.get_text()
def set_text(self, text):
self.text.set_text(text)
def set_title(self, new_title, titleWindow=False):
if titleWindow == False:
self.text.set_placeholder_text(new_title)
else:
super(textLimited, self).set_title(new_title)
# self.set_title(new_title)
def enable_button(self, buttonName):
if getattr(self, buttonName):
return getattr(self, buttonName).show()
def disable_button(self, buttonName):
if getattr(self, buttonName):
return getattr(self, buttonName).hide()
def set_cursor_at_end(self):
self.text.set_position(-1)
def set_cursor_at_position(self, position):
self.text.set_position()
def get_position(self):
return self.text.get_position()
class tweet(textLimited):
def createControls(self, title, message, text):
self.createTextArea(message, text)
self.box.add(self.textBox)
self.upload_image = Gtk.Button(_(u"Upload image..."))
self.spellcheck = Gtk.Button(_("Check spelling..."))
self.attach = Gtk.Button(_(u"Attach audio..."))
self.shortenButton = Gtk.Button(_(u"Shorten URL"))
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
self.shortenButton.hide()
self.shortenButton.set_no_show_all(True)
self.unshortenButton.hide()
self.unshortenButton.set_no_show_all(True)
self.translateButton = Gtk.Button(_(u"Translate..."))
self.autocompletionButton = Gtk.Button(_(u"&Autocomplete users"))
self.buttonsBox1 = Gtk.Box(spacing=6)
self.buttonsBox1.add(self.upload_image)
self.buttonsBox1.add(self.spellcheck)
self.buttonsBox1.add(self.attach)
self.box.add(self.buttonsBox1)
self.buttonsBox2 = Gtk.Box(spacing=6)
self.buttonsBox2.add(self.shortenButton)
self.buttonsBox2.add(self.unshortenButton)
self.buttonsBox2.add(self.translateButton)
self.box.add(self.buttonsBox2)
def __init__(self, title, message, text):
super(tweet, self).__init__()
self.createControls(message, title, text)
self.show_all()
def get_image(self):
dialog = Gtk.FileChooserDialog(_(u"Select the picture to be uploaded"), self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filter_jpg = Gtk.FileFilter()
filter_jpg.set_name(_(u"JPG images"))
filter_jpg.add_mime_type("image/jpeg")
dialog.add_filter(filter_jpg)
filter_gif = Gtk.FileFilter()
filter_gif.set_name(_(u"GIF images"))
filter_gif.add_mime_type("image/gif")
dialog.add_filter(filter_gif)
filter_png = Gtk.FileFilter()
filter_png.set_name(_(u"PNG Images"))
filter_png.add_mime_type("image/png")
dialog.add_filter(filter_png)
answer = dialog.run()
if answer == widgetUtils.OK:
image = dialog.get_filename()
dialog.destroy()
return open(image, "rb")
else:
dialog.destroy()
return None
class dm(textLimited):
def createControls(self, title, message, users):
label = Gtk.Label(_(u"Recipient"))
self.cb = Gtk.ComboBoxText.new_with_entry()
self.cb.set_entry_text_column(0)
for user in users:
self.cb.append_text(user)
self.cb.get_child().set_placeholder_text(_(u"Recipient"))
self.cb.get_child().set_text(users[0])
self.autocompletionButton = Gtk.Button(_(u"&Autocomplete users"))
self.createTextArea(message, text="")
userBox = Gtk.Box(spacing=8)
userBox.add(label)
userBox.add(self.cb)
userBox.add(self.autocompletionButton)
self.box.add(userBox)
# self.mainBox.Add(self.cb, 0, wx.ALL, 5)
self.box.add(self.textBox)
self.spellcheck = Gtk.Button(_("Spelling correction"))
self.attach = Gtk.Button(_(u"Attach audio"))
self.shortenButton = Gtk.Button(_(u"Shorten URL"))
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
self.shortenButton.hide()
self.shortenButton.set_no_show_all(True)
self.unshortenButton.hide()
self.unshortenButton.set_no_show_all(True)
self.translateButton = Gtk.Button(_(u"Translate message"))
self.buttonsBox = Gtk.Box(spacing=6)
self.buttonsBox.add(self.spellcheck)
self.buttonsBox.add(self.attach)
self.box.add(self.buttonsBox)
self.buttonsBox1 = Gtk.Box(spacing=6)
self.buttonsBox1.add(self.shortenButton)
self.buttonsBox1.add(self.unshortenButton)
self.buttonsBox1.add(self.translateButton)
self.box.add(self.buttonsBox1)
self.text.grab_focus()
def __init__(self, title, message, users):
super(dm, self).__init__()
self.createControls(message, title, users)
# self.onTimer(wx.EVT_CHAR_HOOK)
self.show_all()
def get_user(self):
return self.cb.get_text()
def set_user(self, user):
return self.cb.set_value()
class reply(tweet):
def __init__(self, title, message, text):
super(reply, self).__init__(message, title, text)
self.text.set_position(-1)
self.mentionAll = Gtk.Button(_(u"Men&tion to all"))
self.mentionAll.set_no_show_all(True)
self.mentionAll.hide()
self.buttonsBox1.add(self.mentionAll)
class viewTweet(widgetUtils.baseDialog):
def set_title(self, lenght):
pass
self.set_title(_(u"Tweet - %i characters ") % (lenght,))
def __init__(self, text, rt_count, favs_count):
super(viewTweet, self).__init__(buttons=(Gtk.STOCK_OK, widgetUtils.OK, Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
label = Gtk.Label(_(u"Tweet"))
self.text = Gtk.TextView()
self.textBuffer = self.text.get_buffer()
self.textBuffer.set_text(text)
self.text.set_editable(False)
# self.textBuffer.set_placeholder_text(message)
textBox = Gtk.Box(spacing=6)
textBox.add(label)
textBox.add(self.text)
self.box.add(textBox)
rtCountLabel = Gtk.Label(_(u"Retweets: "))
rtCount = Gtk.Entry()
rtCount.set_text(rt_count)
rtCount.set_editable(False)
rtBox = Gtk.Box(spacing=2)
rtBox.add(rtCountLabel)
rtBox.add(rtCount)
favsCountLabel = Gtk.Label(_(u"Favourites: "))
favsCount = Gtk.Entry()
favsCount.set_text(favs_count)
favsCount.set_editable(False)
favsBox = Gtk.Box(spacing=2)
favsBox.add(favsCountLabel)
favsBox.add(favsCount)
infoBox = Gtk.Box(spacing=4)
infoBox.add(rtBox)
infoBox.add(favsBox)
self.box.add(infoBox)
self.spellcheck = Gtk.Button(_("Spelling correction"))
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
self.unshortenButton.hide()
self.unshortenButton.set_no_show_all(True)
self.translateButton = Gtk.Button(_(u"Translate message"))
buttonsBox = Gtk.Box(spacing=6)
buttonsBox.add(self.spellcheck)
buttonsBox.add(self.unshortenButton)
buttonsBox.add(self.translateButton)
self.box.add(buttonsBox)
self.show_all()
def set_text(self, text):
self.textBuffer.set_text(text)
def get_text(self):
return self.textBuffer.get_text(self.textBuffer.get_start_iter(), self.textBuffer.get_end_iter(), False)
def text_focus(self):
self.text.grab_focus()
def enable_button(self, buttonName):
if getattr(self, buttonName):
return getattr(self, buttonName).show()
class viewNonTweet(widgetUtils.baseDialog):
def __init__(self, text):
super(viewNonTweet, self).__init__(buttons=(Gtk.STOCK_OK, widgetUtils.OK, Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
self.set_title(_(u"View"))
label = Gtk.Label(_(u"Item"))
self.text = Gtk.TextView()
self.text.set_editable(False)
self.text.get_buffer().set_text(text)
textBox = Gtk.Box(spacing=5)
textBox.add(label)
textBox.add(self.text)
self.box.Add(textBox)
self.spellcheck = Gtk.Button(_("Spelling correction"))
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
self.unshortenButton.hide()
self.unshortenButton.set_no_show_all(True)
self.translateButton = Gtk.Button(_(u"Translate message"))
buttonsBox = Gtk.Box(spacing=6)
buttonsBox.add(self.spellcheck)
buttonsBox.add(self.unshortenButton)
buttonsBox.add(self.translateButton)
self.box.Add(buttonsBox)
self.show_all()
def set_text(self, text):
self.text.get_buffer().set_text()
def get_text(self):
return self.text.get_buffer().get_text()
def text_focus(self):
self.text.grab_focus()
def enable_button(self, buttonName):
if getattr(self, buttonName):
return getattr(self, buttonName).show()

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import baseDialog
import wx
class searchDialog(baseDialog.BaseWXDialog):
def __init__(self, value=""):
super(searchDialog, self).__init__(None, -1)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetTitle(_(u"Search on Twitter"))
label = wx.StaticText(panel, -1, _(u"Search"))
self.term = wx.TextCtrl(panel, -1, value)
dc = wx.WindowDC(self.term)
dc.SetFont(self.term.GetFont())
self.term.SetSize(dc.GetTextExtent("0"*40))
sizer.Add(label, 0, wx.ALL, 5)
sizer.Add(self.term, 0, wx.ALL, 5)
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
self.users = wx.RadioButton(panel, -1, _(u"Users"))
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
radioSizer.Add(self.tweets, 0, wx.ALL, 5)
radioSizer.Add(self.users, 0, wx.ALL, 5)
sizer.Add(radioSizer, 0, wx.ALL, 5)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok, 0, wx.ALL, 5)
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
import wx
import baseDialog
class showUserProfile(baseDialog.BaseWXDialog):
def __init__(self):
super(showUserProfile, self).__init__(parent=None, id=wx.NewId())
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
static = wx.StaticText(panel, -1, _(u"Details"))
sizer.Add(static, 0, wx.ALL, 5)
self.text = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE|wx.TE_READONLY, size=(350, 250))
self.text.SetFocus()
sizer.Add(self.text, 0, wx.ALL|wx.EXPAND, 5)
self.url = wx.Button(panel, -1, _(u"Go to URL"), size=wx.DefaultSize)
self.url.Disable()
close = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.Add(self.url, 0, wx.ALL, 5)
btnSizer.Add(close, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def enable_url(self, enabled=True):
self.url.Enable(enabled)

View File

@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
import baseDialog
import wx
class trendingTopicsDialog(baseDialog.BaseWXDialog):
def __init__(self):
super(trendingTopicsDialog, self).__init__(None, -1)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetTitle(_(u"View trending topics"))
label = wx.StaticText(panel, -1, _(u"Trending topics by"))
sizer.Add(label, 0, wx.ALL, 5)
self.country = wx.RadioButton(panel, -1, _(u"Country"), style=wx.RB_GROUP)
self.city = wx.RadioButton(panel, -1, _(u"City"))
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
radioSizer.Add(label, 0, wx.ALL, 5)
radioSizer.Add(self.country, 0, wx.ALL, 5)
radioSizer.Add(self.city, 0, wx.ALL, 5)
sizer.Add(radioSizer, 0, wx.ALL, 5)
label = wx.StaticText(panel, -1, _(u"Location"))
self.location = wx.ListBox(panel, -1, choices=[], style=wx.CB_READONLY)
locationBox = wx.BoxSizer(wx.HORIZONTAL)
locationBox.Add(label, 0, wx.ALL, 5)
locationBox.Add(self.location, 0, wx.ALL, 5)
sizer.Add(locationBox, 0, wx.ALL, 5)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok, 0, wx.ALL, 5)
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def get_active(self):
if self.country.GetValue() == True:
return "country"
else:
return "city"
def get_item(self):
return self.location.GetStringSelection()
def set(self, values):
self.location.Set(values)

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
import wx
import baseDialog
class updateProfileDialog(baseDialog.BaseWXDialog):
def __init__(self):
super(updateProfileDialog, self).__init__(parent=None, id=-1)
self.SetTitle(_(u"Update your profile"))
panel = wx.Panel(self)
labelName = wx.StaticText(panel, -1, _(u"Name (20 characters maximum)"))
self.name = wx.TextCtrl(panel, -1)
self.name.SetFocus()
dc = wx.WindowDC(self.name)
dc.SetFont(self.name.GetFont())
self.name.SetSize(dc.GetTextExtent("0"*20))
labelLocation = wx.StaticText(panel, -1, _(u"Location"))
self.location = wx.TextCtrl(panel, -1)
dc = wx.WindowDC(self.location)
dc.SetFont(self.location.GetFont())
self.location.SetSize(dc.GetTextExtent("0"*35))
labelUrl = wx.StaticText(panel, -1, _(u"Website"))
self.url = wx.TextCtrl(panel, -1)
dc = wx.WindowDC(self.url)
dc.SetFont(self.url.GetFont())
self.url.SetSize(dc.GetTextExtent("0"*22))
labelDescription = wx.StaticText(panel, -1, _(u"Bio (160 characters maximum)"))
self.description = wx.TextCtrl(panel, -1, size=(400, 400))
dc = wx.WindowDC(self.description)
dc.SetFont(self.description.GetFont())
self.description.SetSize(dc.GetTextExtent("0"*160))
self.image = None
self.upload_image = wx.Button(panel, -1, _(u"Upload a picture"))
self.ok = wx.Button(panel, wx.ID_OK, _(u"Update profile"))
self.ok.SetDefault()
close = wx.Button(panel, wx.ID_CANCEL, _("Close"))
sizer = wx.BoxSizer(wx.VERTICAL)
nameBox = wx.BoxSizer(wx.HORIZONTAL)
nameBox.Add(labelName, 0, wx.ALL, 5)
nameBox.Add(self.name, 0, wx.ALL, 5)
sizer.Add(nameBox, 0, wx.ALL, 5)
locationBox = wx.BoxSizer(wx.HORIZONTAL)
locationBox.Add(labelLocation, 0, wx.ALL, 5)
locationBox.Add(self.location, 0, wx.ALL, 5)
sizer.Add(locationBox, 0, wx.ALL, 5)
urlBox = wx.BoxSizer(wx.HORIZONTAL)
urlBox.Add(labelUrl, 0, wx.ALL, 5)
urlBox.Add(self.url, 0, wx.ALL, 5)
sizer.Add(urlBox, 0, wx.ALL, 5)
descriptionBox = wx.BoxSizer(wx.HORIZONTAL)
descriptionBox.Add(labelDescription, 0, wx.ALL, 5)
descriptionBox.Add(self.description, 0, wx.ALL, 5)
sizer.Add(descriptionBox, 0, wx.ALL, 5)
sizer.Add(self.upload_image, 5, wx.CENTER, 5)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(self.ok, 0, wx.ALL, 5)
btnBox.Add(close, 0, wx.ALL, 5)
sizer.Add(btnBox, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def set_name(self, name):
self.set("name", name)
def set_description(self, description):
self.set("description", description)
def set_location(self, location):
self.set("location", location)
def set_url(self, url):
self.set("url", url)
def change_upload_button(self, uploaded=False):
if uploaded == False:
self.upload_image.SetLabel(_(u"Upload a picture"))
else:
self.upload_image.SetLabel(_(u"Discard image"))
def upload_picture(self):
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return None
return openFileDialog.GetPath()
def hide_upload_button(self, hide):
self.upload_image.Enable(hide)
def set_readonly(self):
self.name.style = wx.TE_READONLY
self.name.Refresh()
self.description.style = wx.TE_READONLY
self.description.Refresh()
self.location.style = wx.TE_READONLY
self.location.Refresh()
self.url.style = wx.TE_READONLY
self.url.Refresh()
self.hide_upload_button(False)
self.ok.Enable(False)

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