Compare commits

...

100 Commits

Author SHA1 Message Date
93dc3ad646 New snapshot 2015-11-03 10:13:09 -06:00
8e67ed025c Changed favourites for likes 2015-11-03 10:07:06 -06:00
fa1b8bfde3 Added more keystrokes to the invisible interface 2015-11-03 09:02:19 -06:00
7dfe2cbb5c Lists should work 2015-11-03 05:56:57 -06:00
880c421f3e added check for updates to the invisible interface 2015-11-03 05:25:03 -06:00
e1b8d49af5 manage_stream_errors will only delete the stream objects 2015-11-03 04:42:38 -06:00
e1d14b8c27 Now conversation buffers are removed properly 2015-11-03 04:41:01 -06:00
594b0cd546 Translator is working properly again 2015-11-03 04:40:05 -06:00
fe9f724673 Updated accessible_output2. Add mac and a better linux support 2015-11-03 04:38:59 -06:00
6a4a3cc94e Cache com saved in data directory. Translation service restored 2015-10-31 20:57:21 -06:00
Jose Manuel Delicado
af41dcfc4e com.py: use config_path instead of data_path. Paths.py: some strings weren't appended as unicode 2015-10-24 23:01:23 +02:00
31611d8429 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-10-23 11:37:43 -05:00
7ed44c839e New snapshot 2015-10-23 11:36:36 -05:00
503c65692d FFix for creating favourites timelines 2015-10-23 11:33:56 -05:00
f76b86b24d A sound bugfix 2015-10-23 11:29:59 -05:00
1f96e71b63 workaround for duplicated tweets 2015-10-23 11:28:54 -05:00
Jose Manuel Delicado
cc249fe1a7 Added japanese, croatian and serbian (latin) languages to the installer 2015-10-19 12:47:52 +02:00
Jose Manuel Delicado
268bd35f3a Updated windows dependencies. Remember to run git submodule update before submiting new commits 2015-10-19 10:52:05 +02:00
Jose Manuel Delicado
0307c0abe3 Fixed com cache error 2015-10-18 22:18:43 +02:00
Jose Manuel Delicado
adf062f654 Now com cache is stored in the config folder, so program files folder keeps untouched on installed copies. Updated contributors in application.py, fixed spelling in some comments in main.py 2015-10-18 12:19:43 +02:00
70b5f25cf0 New snapshot 2015-10-15 08:55:54 -05:00
2b65b89afb Disabled translation service 2015-10-15 08:54:30 -05:00
44e8ed6456 Removed send errors in some functions 2015-10-14 20:38:42 -05:00
792655e299 Improved stream reconnection 2015-10-14 17:30:41 -05:00
0f56d8cdd4 Quoted tweets again 2015-10-14 17:07:57 -05:00
6c47dd2fa9 Some fixes for URL Player 2015-10-14 17:07:30 -05:00
Jose Manuel Delicado
49073bc151 Added new packages to readme.md. Now setup.py adds wx localization files to the binary version. 2015-10-04 18:15:09 +02:00
Jose Manuel Delicado
336acd9860 updated translations 2015-10-03 13:25:23 +02:00
4daeeb7beb Disable some things before snapshot 2015-10-03 05:12:22 -05:00
408ff50404 New snapshot 2015-10-03 04:55:02 -05:00
148c5176c6 Reverted quoted tweets support 2015-10-03 04:34:43 -05:00
bdb9de863f A fw bugfixes 2015-10-02 15:52:08 -05:00
ce1f8b2cc3 New snapshot 2015-09-29 09:39:47 -05:00
5933323beb Updated translation templates 2015-09-29 09:38:39 -05:00
22b1b0a149 Added posibility for search a term in trending topics 2015-09-29 09:37:04 -05:00
e71afeb10f View other user's lists is implemented 2015-09-29 09:22:19 -05:00
98f026156d Fixed search in application menu 2015-09-29 08:42:04 -05:00
cbee57aa30 Quoted tweets support in buffers 2015-09-29 08:38:05 -05:00
fd9e4dc05d New snapshot 2015-09-23 10:01:43 -05:00
6022cecad1 Added japanese, croatian and serbian 2015-09-23 10:01:22 -05:00
89e39e2168 Double tweets should be ignored in streams 2015-09-23 10:01:00 -05:00
a69bf99c1a If there is no locale for language, use English in arrow 2015-09-23 10:00:14 -05:00
d34ef81324 Updated locales 2015-09-23 09:59:34 -05:00
a3c050195a Twishort retweets are displayed as espected 2015-09-17 09:00:30 -05:00
3623eafacd handle_longtweets is enabled by default 2015-09-17 08:59:59 -05:00
Sukil Etxenike
2bd3f0a1d1 Updated docs with contributors 2015-09-06 23:09:14 +02:00
f1f828522e Translation templates updated 2015-09-05 11:46:34 -05:00
51e4898346 Add shorcuts to the audio upload dialogue 2015-09-05 11:42:47 -05:00
c4478198b6 NEw snapshot 2015-08-31 11:18:10 -05:00
f468924b85 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-08-31 11:03:54 -05:00
158b48e4d5 Add and remove user to list are implemented in GUI 2015-08-31 10:08:34 -05:00
756a58e443 New timeline sound is played again 2015-08-31 08:42:28 -05:00
85ce5b0791 Trending topics now have their names at startup 2015-08-31 08:15:37 -05:00
Jose Manuel Delicado
e534d1cd20 Updated readme. 2015-08-28 11:47:54 +02:00
2d35304ef0 New snapshot 2015-08-27 08:13:01 -05:00
4f0e6d758b Restores buffer positions at startup using cached database 2015-08-25 13:37:31 -05:00
bd4aa89c2b Added text_focus() in some actions to the tweet dialogue 2015-08-24 10:00:34 -05:00
b046360293 Added right click menu in buffers 2015-08-24 09:54:15 -05:00
6971fb3999 Bugfix in retweet with comment 2015-08-21 09:55:05 -05:00
7b840f29c4 Keystroke editor can execute the actions directly 2015-08-21 09:51:01 -05:00
71fed7300b IF there are no tweets or favs, the timeline is not created 2015-08-21 09:12:54 -05:00
57315c3b6e open_timeline() cleaned 2015-08-21 08:57:15 -05:00
3d3abc90e1 Expanded URL are displayed in the view tweet dialogue 2015-08-20 17:57:06 -05:00
de7882e4cf Logout session improvements 2015-08-20 17:32:27 -05:00
jmdaweb
10190d61c0 Fixed windows dependencies submodule. Please, don't forget to run git submodule update before sending a commit or pull request. 2015-08-19 13:38:10 +02:00
bb6fa7cb46 logout() now removes database for the session 2015-08-19 05:28:56 -05:00
80cb70c9a6 Merge branch 'next-gen' of https://github.com/manuelcortez/TWBlue into next-gen 2015-08-18 08:05:21 -05:00
77d51aa51a New snapshot 2015-08-18 08:04:58 -05:00
jmdaweb
ebb4e22d02 Updated Windows dependencies. Now wx buttons and messages should be localized. 2015-08-16 15:42:17 +02:00
4344a0df0c Get full text for dm buffers 2015-08-12 17:21:00 -05:00
34ad0c5e47 Reconnecting streams every 5 mins 2015-08-12 16:43:23 -05:00
704ade560a Increasing character limit for direct messages 2015-08-12 15:57:29 -05:00
a4892cf847 TWBlue does not change the current session in invisible interface 2015-08-11 08:58:11 -05:00
e59661775a Updated translations 2015-08-10 09:49:16 -05:00
53a9f04155 New snapshot 2015-08-10 09:28:47 -05:00
100f483a86 List with special characters are handled properly 2015-08-10 09:26:07 -05:00
bad468560a Saving unicode keys as string objects in cache database 2015-08-10 09:24:46 -05:00
eb0cf62a54 A better way to save keymaps 2015-08-10 09:21:51 -05:00
a016932ac7 Timelines fix 2015-08-08 09:42:55 -05:00
1706414171 Timelines order improvements 2015-08-07 16:44:33 -05:00
5f963aa66b Modified windows 10 keymap 2015-08-07 14:05:22 -05:00
25578e4fc6 Fixed #59 2015-08-07 09:05:59 -05:00
faec92ce6c Stream connection improvements 2015-08-06 13:54:26 -05:00
313e725ff2 Autocompletion now works in the actions dialogue 2015-08-06 13:16:55 -05:00
3f82a51ef9 Added a new contributor 2015-08-06 12:07:30 -05:00
d26ab9b910 Fixed bug when opening multiple timelines 2015-08-06 12:03:40 -05:00
aa42e056c0 Updated translation templates 2015-08-04 13:40:16 -05:00
fa6592ed1a Fixed a string 2015-08-04 13:39:57 -05:00
11b06accbb Merge pull request #61 from codeofdusk/stringcleanup
String Cleanup
2015-08-04 13:27:50 -05:00
Sukil Etxenike
915eb5802f Fixed Basque locale 2015-08-02 00:04:04 +02:00
Bill Dengler
4e943c7bee String cleanup. 2015-07-30 14:15:26 -04:00
Bill Dengler
692d75562e String cleanup. 2015-07-30 14:03:52 -04:00
4cce6d4318 Bugfixes 2015-07-30 11:19:20 -05:00
e55e15466d Fixed some bugs 2015-07-30 09:06:56 -05:00
370340de0b Twishort preferences are saved 2015-07-29 14:47:14 -05:00
10bc483887 Added a donation dialogue in some parts of the program 2015-07-29 14:05:26 -05:00
f66b2ccbad Added repeat item option to invisible interface 2015-07-29 13:45:57 -05:00
6460c20a97 Added copy to clipboard option 2015-07-29 08:40:07 -05:00
9f2c240fc1 Removed saving position in database for now 2015-07-17 17:23:30 -05:00
a4cbbfcb9f TWBlue does not break a word in multiline text 2015-07-13 17:39:02 -05:00
118 changed files with 23053 additions and 22529 deletions

View File

@@ -71,8 +71,11 @@ setuptools install a script, called easy_install. You can find it in the python
* arrow * arrow
* goslate * goslate
* markdown * markdown
* pocket
easy_install will automatically get the additional libraries that these packages need to work properly. easy_install will automatically get the additional libraries that these packages need to work properly.
Run the following command to quickly install and upgrade all packages and their dependencies:
easy_install -Z --upgrade six configobj goslate markdown future pocket suds requests oauthlib requests-oauthlib pypubsub pygeocoder arrow python-dateutil futures
#### Other dependencies #### Other dependencies
@@ -95,9 +98,16 @@ Now that you have installed all these packages, you can run TW Blue from source
If necessary, change the first part of the command to reflect the location of your python executable. You can run TW Blue using python x86 and x64 If necessary, change the first part of the command to reflect the location of your python executable. You can run TW Blue using python x86 and x64
### Generating the documentation
To generate the documentation in html format, navigate to the doc folder inside this repo. After that, run this command:
python generator.py
The documentation will be generated, placing each language in a separate folder in the doc directory. Move these folders (for example de, en, es, fr, it, ...) to src/documentation, creating the directory if necesary.
Also, copy the license.txt located in the root of the repo to the documentation folder.
### Building a binary version ### Building a binary version
A binary version doesn't need python and the other dependencies to run, it's the same version that you will find on the TW Blue website if you download the zip files. A binary version doesn't need python and the other dependencies to run, it's the same version that you will find on the TW Blue website if you download the zip files or the snapshot versions.
To build it, run the following command from the src folder: To build it, run the following command from the src folder:
@@ -105,6 +115,17 @@ To build it, run the following command from the src folder:
You will find the binaries in the dist directory. You will find the binaries in the dist directory.
### Building an installer
If you want to install TWBlue in your computer, you must create the installer first. Follow these steps:
* Navigate to the src directory, and create a binary version for x86: C:\python27\python setup.py py2exe
* Move the dist directory to the scripts folder in this repo, and rename it to twblue
* Repeat these steps with Python for x64: C:\python27x64\python setup.py py2exe
* Move the new dist directory to the scripts folder, and rename it to twblue64
* Go to the scripts folder, right click on the twblue.nsi file, and choose compyle unicode NSIS script
* This may take a while. After the process, you will find the installer in the scripts folder
### How to generate a translation template ### How to generate a translation template
Run the gen_pot.bat file, located in the tools directory. Your python installation must be in your path environment variable. The pot file will appear in the tools directory. 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

@@ -34,3 +34,4 @@ Holly Scott-Gardner
Anibal Hernández Anibal Hernández
Sussan Leiva Sussan Leiva
Brian Hartgen Brian Hartgen
PEDRO REINA COLOBON

View File

@@ -163,7 +163,7 @@ documentation.append(_(u"""* Lists Manager: This dialogue box allows you to mana
documentation.append(_(u"""* Edit keystrokes: this opens a dialogue where you can see and edit the shortcuts used in the invisible interface.""")) documentation.append(_(u"""* Edit keystrokes: this opens a dialogue where you can see and edit the shortcuts used in the invisible interface."""))
documentation.append(_(u"""* Account settings: Opens a dialogue box which lets you customize settings for the current account.""")) documentation.append(_(u"""* Account settings: Opens a dialogue box which lets you customize settings for the current account."""))
documentation.append(_(u"""* Global settings: Opens a dialogue which lets you configure settings for the entire application.""")) documentation.append(_(u"""* Global settings: Opens a dialogue which lets you configure settings for the entire application."""))
documentation.append(_(u"""* Quit: asks whether you want to exit the program. If the answer is yes, it closes the application. If you do not want to be asked for confirmation before exiting, uncheck the checkbox from the global settings dialogue box.""")) documentation.append(_(u"""* Exit: asks whether you want to exit the program. If the answer is yes, it closes the application. If you do not want to be asked for confirmation before exiting, uncheck the checkbox from the global settings dialogue box."""))
documentation.append(_(u""" documentation.append(_(u"""
""")) """))
documentation.append(_(u"""##### Tweet menu""")) documentation.append(_(u"""##### Tweet menu"""))
@@ -405,19 +405,21 @@ documentation.append(_(u"""We would also like to thank the translators of TWBlue
documentation.append(_(u""" documentation.append(_(u"""
""")) """))
documentation.append(_(u"""* English: [Bryner Villalobos](https://twitter.com/Bry_StarkCR) and [Bill Dengler](https://twitter.com/codeofdusk).""")) documentation.append(_(u"""* English: [Bryner Villalobos](https://twitter.com/Bry_StarkCR) and [Bill Dengler](https://twitter.com/codeofdusk)."""))
documentation.append(_(u"""* Arabic: Mohammed Al Shara.""")) documentation.append(_(u"""* Arabic: [Mohammed Al Shara](https://twitter.com/mohammed0204)."""))
documentation.append(_(u"""* Catalan: [Joan Rabat](https://twitter.com/joanrabat) and Juan Carlos Rivilla.""")) documentation.append(_(u"""* Catalan: [Joan Rabat](https://twitter.com/joanrabat) and Juan Carlos Rivilla."""))
documentation.append(_(u"""* Spanish: [Manuel Cortéz](https://twitter.com/manuelcortez00).""")) documentation.append(_(u"""* Spanish: [Manuel Cortéz](https://twitter.com/manuelcortez00)."""))
documentation.append(_(u"""* Basque: [Sukil Etxenike](https://twitter.com/sukil2011).""")) documentation.append(_(u"""* Basque: [Sukil Etxenike](https://twitter.com/sukil2011)."""))
documentation.append(_(u"""* Finnish: Jani Kinnunen.""")) documentation.append(_(u"""* Finnish: [Jani Kinnunen](https://twitter.com/jani_kinnunen)."""))
documentation.append(_(u"""* French: Rémi Ruiz.""")) documentation.append(_(u"""* French: [Rémi Ruiz](https://twitter.com/blindhelp38)."""))
documentation.append(_(u"""* Galician: [Alba Kinteiro](https://twitter.com/albasmileforeve).""")) documentation.append(_(u"""* Galician: [Alba Kinteiro](https://twitter.com/albasmileforeve)."""))
documentation.append(_(u"""* German: Steffen Schultz.""")) documentation.append(_(u"""* German: [Steffen Schultz](https://twitter.com/schulle4u)."""))
documentation.append(_(u"""* Croatian: [Zvonimir Stanečić](https://twitter.com/zvonimirek222)."""))
documentation.append(_(u"""* Hungarian: Robert Osztolykan.""")) documentation.append(_(u"""* Hungarian: Robert Osztolykan."""))
documentation.append(_(u"""* Italian: [Christian Leo Mameli](https://twitter.com/llajta2012)."""))
documentation.append(_(u"""* Polish: Pawel Masarczyk.""")) documentation.append(_(u"""* Polish: Pawel Masarczyk."""))
documentation.append(_(u"""* Portuguese: Odenilton Júnior Santos.""")) documentation.append(_(u"""* Portuguese: Odenilton Júnior Santos."""))
documentation.append(_(u"""* Russian: Alexander Jaszyn.""")) documentation.append(_(u"""* Russian: [Александр Яшин](https://twitter.com/radovest)."""))
documentation.append(_(u"""* Turkish: Burak.""")) documentation.append(_(u"""* Turkish: [Burak Yüksek](https://twitter.com/burakyuksek)."""))
documentation.append(_(u""" documentation.append(_(u"""
""")) """))
documentation.append(_(u"""Many thanks also to the people who worked on the documentation. Initially, [Manuel Cortez](https://twitter.com/manuelcortez00) did the documentation in Spanish, and translated to English by [Bryner Villalobos](https://twitter.com/Bry_StarkCR), [Robert Spangler](https://twitter.com/glasscity1837), [Sussan Rey](https://twitter.com/sussanrey17), [Anibal Hernandez](https://twitter.com/anibalmetal), and [Holly Scott-Gardner](https://twitter.com/holly1994). It was updated by [Sukil Etxenike](https://twitter.com/sukil2011), with some valuable corrections by [Brian Hartgen](https://twitter.com/brianhartgen) and [Bill Dengler](https://twitter.com/codeofdusk).""")) documentation.append(_(u"""Many thanks also to the people who worked on the documentation. Initially, [Manuel Cortez](https://twitter.com/manuelcortez00) did the documentation in Spanish, and translated to English by [Bryner Villalobos](https://twitter.com/Bry_StarkCR), [Robert Spangler](https://twitter.com/glasscity1837), [Sussan Rey](https://twitter.com/sussanrey17), [Anibal Hernandez](https://twitter.com/anibalmetal), and [Holly Scott-Gardner](https://twitter.com/holly1994). It was updated by [Sukil Etxenike](https://twitter.com/sukil2011), with some valuable corrections by [Brian Hartgen](https://twitter.com/brianhartgen) and [Bill Dengler](https://twitter.com/codeofdusk)."""))

View File

@@ -45,6 +45,9 @@ var StartMenuFolder
!insertmacro MUI_LANGUAGE "Galician" !insertmacro MUI_LANGUAGE "Galician"
!insertmacro MUI_LANGUAGE "Catalan" !insertmacro MUI_LANGUAGE "Catalan"
!insertmacro MUI_LANGUAGE "Basque" !insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_RESERVEFILE_LANGDLL !insertmacro MUI_RESERVEFILE_LANGDLL
Section Section
SetShellVarContext All SetShellVarContext All

View File

@@ -36,6 +36,7 @@ autoread_buffers = list(default=list(mentions, direct_messages, events))
spelling_language = string(default="") spelling_language = string(default="")
save_followers_in_autocompletion_db = boolean(default=False) save_followers_in_autocompletion_db = boolean(default=False)
save_friends_in_autocompletion_db = boolean(default=False) save_friends_in_autocompletion_db = boolean(default=False)
twishort_enabled = boolean(default=False)
[services] [services]
pocket_access_token = string(default="") pocket_access_token = string(default="")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,12 +5,12 @@ if snapshot == False:
version = "0.80" version = "0.80"
update_url = 'http://twblue.es/updates/twblue_ngen.json' update_url = 'http://twblue.es/updates/twblue_ngen.json'
else: else:
version = "9.9" version = "10.95"
update_url = 'http://twblue.es/updates/snapshots_ngen.json' update_url = 'http://twblue.es/updates/snapshots_ngen.json'
author = u"Manuel Cortéz, Bill Dengler" author = u"Manuel Cortéz"
authorEmail = "manuel@manuelcortez.net" authorEmail = "manuel@manuelcortez.net"
copyright = u"Copyright (C) 2015, Technow S.L. \nCopyright (C) 2015, Bill Dengler\nCopyright (C) 2013-2015, Manuel cortéz." copyright = u"Copyright (C) 2015, Technow S.L. \nCopyright (C) 2013-2015, Manuel cortéz."
description = unicode(name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features.") description = unicode(name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features.")
translators = [u"Bryner Villalobos, Bill Dengler (English)", u"Mohammed Al Shara (Arabic)", u"Joan Rabat, Juan Carlos Rivilla (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Alba Quinteiro (Galician)", u"Steffen Schultz (German)", u"Robert Osztolykan (Hungarian)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Alexander Jaszyn (Russian)", u"Burak (Turkish)"] translators = [u"Bryner Villalobos, Bill Dengler (English)", u"Mohammed Al Shara (Arabic)", u"Joan Rabat, Juan Carlos Rivilla (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Juan Buño (Galician)", u"Steffen Schultz (German)", u"Robert Osztolykan (Hungarian)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Alexander Jaszyn (Russian)", u"Burak (Turkish)"]
url = u"http://twblue.es" url = u"http://twblue.es"
report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl" report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl"

View File

@@ -16,4 +16,4 @@ def setup ():
app = config_utils.load_config(paths.config_path(MAINFILE), paths.app_path(MAINSPEC)) app = config_utils.load_config(paths.config_path(MAINFILE), paths.app_path(MAINSPEC))
log.debug("Loading keymap...") log.debug("Loading keymap...")
global keymap global keymap
keymap = config_utils.load_config(paths.app_path("keymaps/"+app['app-settings']['load_keymap']), paths.app_path('keymaps/base.template')) keymap = config_utils.load_config(paths.config_path("keymap.keymap"), paths.app_path("keymaps/"+app['app-settings']['load_keymap']))

View File

@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time
import platform import platform
if platform.system() == "Windows": if platform.system() == "Windows":
import wx import wx
from wxUI import buffers, dialogs, commonMessageDialogs from wxUI import buffers, dialogs, commonMessageDialogs, menus
import user import user
elif platform.system() == "Linux": elif platform.system() == "Linux":
from gi.repository import Gtk from gi.repository import Gtk
@@ -42,6 +43,7 @@ class bufferController(object):
self.account = "" self.account = ""
self.needs_init = True self.needs_init = True
self.invisible = False # False if the buffer will be ignored on the invisible interface. self.invisible = False # False if the buffer will be ignored on the invisible interface.
self.execution_time = 0
def clear_list(self): pass def clear_list(self): pass
@@ -127,8 +129,9 @@ class bufferController(object):
def post_tweet(self, *args, **kwargs): def post_tweet(self, *args, **kwargs):
title = _(u"Tweet") title = _(u"Tweet")
caption = _(u"Write the tweet here") caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, "") tweet = messages.tweet(self.session, title, caption, "", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"])
if tweet.message.get_response() == widgetUtils.OK: if tweet.message.get_response() == widgetUtils.OK:
self.session.settings["mysc"]["twishort_enabled"] = tweet.message.long_tweet.GetValue()
text = tweet.message.get_text() text = tweet.message.get_text()
if len(text) > 140 and tweet.message.get("long_tweet") == True: if len(text) > 140 and tweet.message.get("long_tweet") == True:
if tweet.image == None: if tweet.image == None:
@@ -141,6 +144,13 @@ class bufferController(object):
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image) 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() if hasattr(tweet.message, "destroy"): tweet.message.destroy()
def save_positions(self):
try:
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
except AttributeError:
pass
class accountPanel(bufferController): class accountPanel(bufferController):
def __init__(self, parent, name, account, account_id): def __init__(self, parent, name, account, account_id):
super(accountPanel, self).__init__(parent, None, name) super(accountPanel, self).__init__(parent, None, name)
@@ -223,7 +233,8 @@ class baseBufferController(bufferController):
return self.get_message() return self.get_message()
def get_message(self): def get_message(self):
return " ".join(self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"])) tweet = self.get_right_tweet()
return " ".join(self.compose_function(tweet, self.session.db, self.session.settings["general"]["relative_times"]))
def get_full_tweet(self): def get_full_tweet(self):
tweet = self.get_right_tweet() tweet = self.get_right_tweet()
@@ -234,6 +245,10 @@ class baseBufferController(bufferController):
uri = tweet["long_uri"] uri = tweet["long_uri"]
try: try:
tweet = self.session.twitter.twitter.show_status(id=tweet_id) tweet = self.session.twitter.twitter.show_status(id=tweet_id)
urls = utils.find_urls_in_text(tweet["text"])
for url in range(0, len(urls)):
try: tweet["text"] = tweet["text"].replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
except TwythonError as e: except TwythonError as e:
utils.twitter_error(e) utils.twitter_error(e)
return return
@@ -245,6 +260,10 @@ class baseBufferController(bufferController):
id = tweets.get_id(l) id = tweets.get_id(l)
try: try:
tweet = self.session.twitter.twitter.show_status(id=id) tweet = self.session.twitter.twitter.show_status(id=id)
urls = utils.find_urls_in_text(tweet["text"])
for url in range(0, len(urls)):
try: tweet["text"] = tweet["text"].replace(urls[url], tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
except TwythonError as e: except TwythonError as e:
utils.twitter_error(e) utils.twitter_error(e)
return return
@@ -254,6 +273,10 @@ class baseBufferController(bufferController):
return (tweet, tweetsList) return (tweet, tweetsList)
def start_stream(self): def start_stream(self):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
self.execution_time = current_time
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type)) log.debug("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)) log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.call_paged(self.function, *self.args, **self.kwargs) val = self.session.call_paged(self.function, *self.args, **self.kwargs)
@@ -344,8 +367,8 @@ class baseBufferController(bufferController):
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False: 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])) output.speak(" ".join(tweet[:2]))
#Improve performance on Windows #Improve performance on Windows
if platform.system() == "Windows": # if platform.system() == "Windows":
call_threaded(utils.is_audio,item) # call_threaded(utils.is_audio,item)
def bind_events(self): def bind_events(self):
log.debug("Binding events...") log.debug("Binding events...")
@@ -356,6 +379,61 @@ class baseBufferController(bufferController):
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.retweet, self.buffer.retweet) widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.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.direct_message, self.buffer.dm)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply) widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
# Replace for the correct way in other platforms.
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
if self.name == "sent_tweets" or self.name == "sent_direct_messages":
menu = menus.sentPanelMenu()
elif self.name == "direct_messages":
menu = menus.dmPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.direct_message, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
else:
menu = menus.basePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.retweet, menuitem=menu.retweet)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.url_, menuitem=menu.openUrl)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.audio, menuitem=menu.play)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.destroy_status, menuitem=menu.remove)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
def user_actions(self, *args, **kwargs):
pub.sendMessage("execute-action", action="follow")
def fav(self, *args, **kwargs):
pub.sendMessage("execute-action", action="add_to_favourites")
def unfav(self, *args, **kwargs):
pub.sendMessage("execute-action", action="remove_from_favourites")
def delete_item_(self, *args, **kwargs):
pub.sendMessage("execute-action", action="delete_item")
def url_(self, *args, **kwargs):
self.url()
def show_menu_by_key(self, ev):
if self.buffer.list.get_count() == 0:
return
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
def get_tweet(self): def get_tweet(self):
if self.session.db[self.name][self.buffer.list.get_selected()].has_key("retweeted_status"): if self.session.db[self.name][self.buffer.list.get_selected()].has_key("retweeted_status"):
@@ -374,8 +452,9 @@ class baseBufferController(bufferController):
screen_name = tweet["user"]["screen_name"] screen_name = tweet["user"]["screen_name"]
id = tweet["id"] id = tweet["id"]
users = utils.get_all_mentioned(tweet, self.session.db) 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) message = messages.reply(self.session, _(u"Reply"), _(u"Reply to %s") % (screen_name,), "@%s " % (screen_name,), twishort_enabled=self.session.settings["mysc"]["twishort_enabled"], users=users)
if message.message.get_response() == widgetUtils.OK: if message.message.get_response() == widgetUtils.OK:
self.session.settings["mysc"]["twishort_enabled"] = message.message.long_tweet.GetValue()
text = message.message.get_text() text = message.message.get_text()
if len(text) > 140 and message.message.get("long_tweet") == True: if len(text) > 140 and message.message.get("long_tweet") == True:
if message.image == None: if message.image == None:
@@ -421,7 +500,7 @@ class baseBufferController(bufferController):
self._retweet_with_comment(tweet, id) self._retweet_with_comment(tweet, id)
def _retweet_with_comment(self, tweet, id, comment=''): 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") 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, messageType="retweet", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"])
if comment != '': if comment != '':
retweet.message.set_text(comment) retweet.message.set_text(comment)
if retweet.message.get_response() == widgetUtils.OK: if retweet.message.get_response() == widgetUtils.OK:
@@ -455,11 +534,10 @@ class baseBufferController(bufferController):
self.session.sound.play("audio.ogg") self.session.sound.play("audio.ogg")
if utils.is_geocoded(tweet): if utils.is_geocoded(tweet):
self.session.sound.play("geo.ogg") self.session.sound.play("geo.ogg")
self.session.db[str(self.name+"_pos")]=self.buffer.list.get_selected()
@_tweets_exist # @_tweets_exist
def audio(self,url=''): def audio(self, url='', *args, **kwargs):
if hasattr(sound.URLPlayer,'stream'): if hasattr(sound.URLPlayer,'stream') and sound.URLPlayer.stream.is_playing == True:
return sound.URLPlayer.stop_audio(delete=True) return sound.URLPlayer.stop_audio(delete=True)
tweet = self.get_tweet() tweet = self.get_tweet()
if tweet == None: return if tweet == None: return
@@ -478,8 +556,8 @@ class baseBufferController(bufferController):
except: except:
log.error("Exception while executing audio method.") log.error("Exception while executing audio method.")
@_tweets_exist # @_tweets_exist
def url(self,url='',announce=True): def url(self, url='', announce=True, *args, **kwargs):
if url == '': if url == '':
tweet = self.get_tweet() tweet = self.get_tweet()
urls = utils.find_urls(tweet) urls = utils.find_urls(tweet)
@@ -533,6 +611,26 @@ class baseBufferController(bufferController):
user.profileController(session=self.session, user=dlg.get_user()) user.profileController(session=self.session, user=dlg.get_user())
if hasattr(dlg, "destroy"): dlg.destroy() if hasattr(dlg, "destroy"): dlg.destroy()
def get_quoted_tweet(self, tweet):
# try:
quoted_tweet = self.session.twitter.twitter.show_status(id=tweet["id"])
urls = utils.find_urls_in_text(quoted_tweet["text"])
for url in range(0, len(urls)):
try: quoted_tweet["text"] = quoted_tweet["text"].replace(urls[url], quoted_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
# except TwythonError as e:
# utils.twitter_error(e)
# return
l = tweets.is_long(quoted_tweet)
id = tweets.get_id(l)
# try:
original_tweet = self.session.twitter.twitter.show_status(id=id)
urls = utils.find_urls_in_text(original_tweet["text"])
for url in range(0, len(urls)):
try: original_tweet["text"] = original_tweet["text"].replace(urls[url], original_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
return compose.compose_quoted_tweet(quoted_tweet, original_tweet, self.session.db, self.session.settings["general"]["relative_times"])
class listBufferController(baseBufferController): class listBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, list_id=None, *args, **kwargs): 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) super(listBufferController, self).__init__(parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs)
@@ -597,6 +695,23 @@ class eventsBufferController(bufferController):
if dlg == widgetUtils.YES: if dlg == widgetUtils.YES:
self.buffer.list.clear() self.buffer.list.clear()
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
menu = menus.eventsPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.destroy_status, menuitem=menu.remove)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
class peopleBufferController(baseBufferController): class peopleBufferController(baseBufferController):
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs): def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel") super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel")
@@ -630,11 +745,13 @@ class peopleBufferController(baseBufferController):
if hasattr(message.message, "destroy"): message.message.destroy() if hasattr(message.message, "destroy"): message.message.destroy()
def start_stream(self): def start_stream(self):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,)) log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs)) log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
val = self.session.get_cursored_stream(self.name, self.function, *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) self.put_items_on_list(val)
def get_more_items(self): def get_more_items(self):
@@ -707,8 +824,28 @@ class peopleBufferController(baseBufferController):
def interact(self): def interact(self):
user.profileController(self.session, user=self.get_right_tweet()["screen_name"]) user.profileController(self.session, user=self.get_right_tweet()["screen_name"])
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.peoplePanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.direct_message, menuitem=menu.reply)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.details, menuitem=menu.details)
# widgetUtils.connect_event(menu, widgetUtils.MENU, self.lists, menuitem=menu.lists)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def details(self, *args, **kwargs):
pub.sendMessage("execute-action", action="user_details")
class searchBufferController(baseBufferController): class searchBufferController(baseBufferController):
def start_stream(self): def start_stream(self):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
self.execution_time = current_time
log.debug("Starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type)) log.debug("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("args: %s, kwargs: %s" % (self.args, self.kwargs))
log.debug("Function: %s" % (self.function,)) log.debug("Function: %s" % (self.function,))
@@ -743,6 +880,10 @@ class searchPeopleBufferController(peopleBufferController):
self.function = function self.function = function
def start_stream(self): def start_stream(self):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
self.execution_time = current_time
log.debug("starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type)) log.debug("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("args: %s, kwargs: %s" % (self.args, self.kwargs))
log.debug("Function: %s" % (self.function,)) log.debug("Function: %s" % (self.function,))
@@ -783,8 +924,13 @@ class trendsBufferController(bufferController):
self.buffer.name = name self.buffer.name = name
self.compose_function = self.compose_function_ self.compose_function = self.compose_function_
self.get_formatted_message = self.get_message self.get_formatted_message = self.get_message
self.reply = self.search_topic
def start_stream(self): def start_stream(self):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
self.execution_time = current_time
try: try:
data = self.session.call_paged("get_place_trends", id=self.trendsFor) data = self.session.call_paged("get_place_trends", id=self.trendsFor)
except: except:
@@ -809,10 +955,11 @@ class trendsBufferController(bufferController):
def bind_events(self): def bind_events(self):
log.debug("Binding events...") log.debug("Binding events...")
self.buffer.list.list.Bind(wx.EVT_CHAR_HOOK, self.get_event) 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.tweet_about_this_trend, self.buffer.tweetTrendBtn)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.retweet, self.buffer.retweet) widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_tweet, self.buffer.tweet)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.direct_message, self.buffer.dm) widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply) widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.search_topic, self.buffer.search_topic)
def get_message(self): def get_message(self):
return self.compose_function(self.trends[self.buffer.list.get_selected()])[0] return self.compose_function(self.trends[self.buffer.list.get_selected()])[0]
@@ -827,12 +974,60 @@ class trendsBufferController(bufferController):
elif dlg == widgetUtils.NO: elif dlg == widgetUtils.NO:
return False return False
def interact(self, *args, **kwargs): def search_topic(self, *args, **kwargs):
self.searchfunction(value=self.get_message()) topic = self.trends[self.buffer.list.get_selected()]["name"]
pub.sendMessage("search", term=topic)
def show_menu(self, ev, pos=0, *args, **kwargs):
menu = menus.trendsPanelMenu()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.search_topic, menuitem=menu.search_topic)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.tweet_about_this_trend, menuitem=menu.tweetThisTrend)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.copy, menuitem=menu.copy)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, ev.GetPosition())
def view(self, *args, **kwargs):
pub.sendMessage("execute-action", action="view_item")
def copy(self, *args, **kwargs):
pub.sendMessage("execute-action", action="copy_to_clipboard")
def tweet_about_this_trend(self, *args, **kwargs):
if self.buffer.list.get_count() == 0: return
title = _(u"Tweet")
caption = _(u"Write the tweet here")
tweet = messages.tweet(self.session, title, caption, self.get_message()+ " ", twishort_enabled=self.session.settings["mysc"]["twishort_enabled"])
tweet.message.set_cursor_at_end()
if tweet.message.get_response() == widgetUtils.OK:
self.session.settings["mysc"]["twishort_enabled"] = tweet.message.long_tweet.GetValue()
text = tweet.message.get_text()
if len(text) > 140 and tweet.message.get("long_tweet") == True:
if tweet.image == None:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text)
else:
text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1)
if tweet.image == None:
call_threaded(self.session.api_call, call_name="update_status", status=text)
else:
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image)
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
def show_menu_by_key(self, ev):
if self.buffer.list.get_count() == 0:
return
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
class conversationBufferController(searchBufferController): class conversationBufferController(searchBufferController):
def start_stream(self, start=False): def start_stream(self, start=False):
# starts stream every 3 minutes.
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 180:
self.execution_time = current_time
if start == True: if start == True:
self.statuses = [] self.statuses = []
self.ids = [] self.ids = []
@@ -858,6 +1053,14 @@ class conversationBufferController(searchBufferController):
if number_of_items > 0: if number_of_items > 0:
self.session.sound.play("search_updated.ogg") self.session.sound.play("search_updated.ogg")
def remove_buffer(self):
dlg = commonMessageDialogs.remove_buffer()
if dlg == widgetUtils.YES:
self.timer.cancel()
return True
elif dlg == WidgetUtils.NO:
return False
class pocketBufferController(baseBufferController): class pocketBufferController(baseBufferController):
def __init__(self, parent, name, sessionObject, account, sound=None, function=None, bufferType=None, *args, **kwargs): def __init__(self, parent, name, sessionObject, account, sound=None, function=None, bufferType=None, *args, **kwargs):
super(pocketBufferController, self).__init__(parent, name, sessionObject, account, sound, function, bufferType, *args, **kwargs) super(pocketBufferController, self).__init__(parent, name, sessionObject, account, sound, function, bufferType, *args, **kwargs)

View File

@@ -15,19 +15,30 @@ class listsController(object):
self.dialog.populate_list(self.get_all_lists()) 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.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list) widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer) widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.remove_list)
else:
self.dialog = lists.userListViewer(user)
self.dialog.populate_list(self.get_user_lists(user))
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.subscribe)
widgetUtils.connect_event(self.dialog.deleteBtn, widgetUtils.BUTTON_PRESSED, self.unsubscribe)
self.dialog.get_response() self.dialog.get_response()
def get_all_lists(self): def get_all_lists(self):
return [compose.compose_list(item) for item in self.session.db["lists"]] return [compose.compose_list(item) for item in self.session.db["lists"]]
def get_user_lists(self, user):
self.lists = self.session.twitter.twitter.show_lists(reverse=True, screen_name=user)
return [compose.compose_list(item) for item in self.lists]
def create_list(self, *args, **kwargs): def create_list(self, *args, **kwargs):
dialog = lists.createListDialog() dialog = lists.createListDialog()
if dialog.get_response() == widgetUtils.OK: if dialog.get_response() == widgetUtils.OK:
name = dialog.get("name") name = dialog.get("name")
description = dialog.get("description") description = dialog.get("description")
p = dialog.get("public") p = dialog.get("public")
if public == True: if p == True:
mode = "public" mode = "public"
else: else:
mode = "private" mode = "private"
@@ -73,4 +84,23 @@ class listsController(object):
def open_list_as_buffer(self, *args, **kwargs): def open_list_as_buffer(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return if self.dialog.lista.get_count() == 0: return
list = self.session.db["lists"][self.dialog.get_item()] list = self.session.db["lists"][self.dialog.get_item()]
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list["slug"]) pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list["name"])
def subscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()]["id"]
try:
list = self.session.twitter.twitter.subscribe_to_list(list_id=list_id)
item = utils.find_item(list["id"], self.session.db["lists"])
self.session.db["lists"].append(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
def unsubscribe(self, *args, **kwargs):
if self.dialog.lista.get_count() == 0: return
list_id = self.lists[self.dialog.get_item()]["id"]
try:
list = self.session.twitter.twitter.unsubscribe_from_list(list_id=list_id)
self.session.db["lists"].remove(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))

View File

@@ -90,12 +90,14 @@ class Controller(object):
""" Gets the last valid buffer for an account. """ Gets the last valid buffer for an account.
account str: A twitter username. account str: A twitter username.
The last valid buffer is the last buffer that contains a session object assigned.""" The last valid buffer is the last buffer that contains a session object assigned."""
# results = self.get_buffers_for_account(account)
results = self.get_buffers_for_account(account) results = self.get_buffers_for_account(account)
return self.view.search(results[-1].name, results[-1].account) return self.view.search(results[-1].name, results[-1].account)
def get_buffers_for_account(self, account): def get_buffers_for_account(self, account):
results = [] results = []
[results.append(i) for i in self.buffers if i.account == account and i.invisible == True] buffers = self.view.get_buffers()
[results.append(self.search_buffer(i.name, i.account)) for i in buffers if i.account == account and (i.type != "account")]
return results return results
def bind_stream_events(self): def bind_stream_events(self):
@@ -126,7 +128,8 @@ class Controller(object):
pub.subscribe(self.manage_stream_errors, "stream-error") pub.subscribe(self.manage_stream_errors, "stream-error")
pub.subscribe(self.create_new_buffer, "create-new-buffer") pub.subscribe(self.create_new_buffer, "create-new-buffer")
pub.subscribe(self.restart_streams, "restart-streams") pub.subscribe(self.restart_streams, "restart-streams")
pub.subscribe(self.execute_action, "execute-action")
pub.subscribe(self.search_topic, "search")
if system == "Windows": if system == "Windows":
pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed") pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed")
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide)
@@ -154,7 +157,9 @@ class Controller(object):
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.follow, menuitem=self.view.follow)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.get_more_items, menuitem=self.view.load_previous_items)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_user_lists, menuitem=self.view.viewLists)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.clear_buffer, menuitem=self.view.clear)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_buffer, self.view.deleteTl)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.check_for_updates, self.view.check_for_updates)
@@ -172,6 +177,8 @@ class Controller(object):
widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed) widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_documentation, self.view.doc)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.add_to_list, self.view.addToList)
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.remove_from_list, self.view.removeFromList)
def set_systray_icon(self): def set_systray_icon(self):
self.systrayIcon = sysTrayIcon.SysTrayIcon() self.systrayIcon = sysTrayIcon.SysTrayIcon()
@@ -233,7 +240,6 @@ class Controller(object):
self.create_ignored_session_buffer(session_.sessions[i]) self.create_ignored_session_buffer(session_.sessions[i])
continue continue
self.create_buffers(session_.sessions[i]) self.create_buffers(session_.sessions[i])
self.set_buffer_positions(session_.sessions[i])
# Connection checker executed each minute. # Connection checker executed each minute.
self.checker_function = RepeatingTimer(60, self.check_connection) self.checker_function = RepeatingTimer(60, self.check_connection)
@@ -244,6 +250,7 @@ class Controller(object):
for i in session_.sessions: for i in session_.sessions:
if session_.sessions[i].is_logged == False: continue if session_.sessions[i].is_logged == False: continue
self.start_buffers(session_.sessions[i]) self.start_buffers(session_.sessions[i])
self.set_buffer_positions(session_.sessions[i])
if config.app["app-settings"]["play_ready_sound"] == True: if config.app["app-settings"]["play_ready_sound"] == True:
session_.sessions[session_.sessions.keys()[0]].sound.play("ready.ogg") session_.sessions[session_.sessions.keys()[0]].sound.play("ready.ogg")
if config.app["app-settings"]["speak_ready_msg"] == True: if config.app["app-settings"]["speak_ready_msg"] == True:
@@ -261,6 +268,7 @@ class Controller(object):
for i in session_.sessions: for i in session_.sessions:
if session_.sessions[i].session_id == session_id: session = session_.sessions[i] if session_.sessions[i].session_id == session_id: session = session_.sessions[i]
session.login() session.login()
session.db = dict()
self.create_buffers(session, False) self.create_buffers(session, False)
self.start_buffers(session) self.start_buffers(session)
@@ -284,11 +292,11 @@ class Controller(object):
self.buffers.append(mentions) self.buffers.append(mentions)
self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"])) self.view.insert_buffer(mentions.buffer, name=_(u"Mentions"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'dm': elif i == 'dm':
dm = buffersController.baseBufferController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", sound="dm_received.ogg") dm = buffersController.baseBufferController(self.view.nb, "get_direct_messages", "direct_messages", session, session.db["user_name"], bufferType="dmPanel", sound="dm_received.ogg", full_text=True)
self.buffers.append(dm) self.buffers.append(dm)
self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"])) self.view.insert_buffer(dm.buffer, name=_(u"Direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_dm': elif i == 'sent_dm':
sent_dm = buffersController.baseBufferController(self.view.nb, "get_sent_messages", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel") sent_dm = buffersController.baseBufferController(self.view.nb, "get_sent_messages", "sent_direct_messages", session, session.db["user_name"], bufferType="dmPanel", full_text=True)
self.buffers.append(sent_dm) self.buffers.append(sent_dm)
self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"])) self.view.insert_buffer(sent_dm.buffer, name=_(u"Sent direct messages"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'sent_tweets': elif i == 'sent_tweets':
@@ -299,7 +307,7 @@ class Controller(object):
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"]) favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", session, session.db["user_name"])
self.buffers.append(favourites) self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Favourites"), pos=self.view.search(session.db["user_name"], session.db["user_name"])) self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
elif i == 'followers': elif i == 'followers':
followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", session, session.db["user_name"], screen_name=session.db["user_name"]) followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", session, session.db["user_name"], screen_name=session.db["user_name"])
self.buffers.append(followers) self.buffers.append(followers)
@@ -329,11 +337,11 @@ class Controller(object):
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"])) self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(i,), pos=self.view.search("timelines", session.db["user_name"]))
favs_timelines = buffersController.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"]) favs_timelines = buffersController.emptyPanel(self.view.nb, "favs_timelines", session.db["user_name"])
self.buffers.append(favs_timelines) self.buffers.append(favs_timelines)
self.view.insert_buffer(favs_timelines.buffer , name=_(u"Favourites timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"])) self.view.insert_buffer(favs_timelines.buffer , name=_(u"Likes timelines"), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
for i in session.settings["other_buffers"]["favourites_timelines"]: for i in session.settings["other_buffers"]["favourites_timelines"]:
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, screen_name=i) tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (i,), session, session.db["user_name"], bufferType=None, screen_name=i)
self.buffers.append(tl) self.buffers.append(tl)
self.view.insert_buffer(tl.buffer, name=_(u"Favourites timeline for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"])) self.view.insert_buffer(tl.buffer, name=_(u"Likes for {}").format(i,), pos=self.view.search("favs_timelines", session.db["user_name"]))
tl.timer = RepeatingTimer(300, tl.start_stream) tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start() tl.timer.start()
lists = buffersController.emptyPanel(self.view.nb, "lists", session.db["user_name"]) lists = buffersController.emptyPanel(self.view.nb, "lists", session.db["user_name"])
@@ -358,40 +366,27 @@ class Controller(object):
buffer.start_stream() buffer.start_stream()
buffer.searchfunction = self.search buffer.searchfunction = self.search
self.buffers.append(buffer) self.buffers.append(buffer)
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (buffer.name_), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
buffer.timer = RepeatingTimer(300, buffer.start_stream) buffer.timer = RepeatingTimer(300, buffer.start_stream)
buffer.timer.start() buffer.timer.start()
self.view.insert_buffer(buffer.buffer, name=_(u"Trending topics for %s") % (buffer.name_), pos=self.view.search(session.db["user_name"], session.db["user_name"]))
def set_buffer_positions(self,session): def set_buffer_positions(self,session):
"Sets positions for buffers if values exist in the database." "Sets positions for buffers if values exist in the database."
for i in self.buffers: for i in self.buffers:
if str(i.name+"_pos") in session.db and hasattr(i.buffer,'list'): if i.account == session.db["user_name"] and session.db.has_key(i.name+"_pos") and hasattr(i.buffer,'list'):
i.buffer.list.select_item(session.db[str(i.name+"_pos")]) i.buffer.list.select_item(session.db[str(i.name+"_pos")])
def logout_account(self, session_id): def logout_account(self, session_id):
for i in session_.sessions: for i in session_.sessions:
if session_.sessions[i].session_id == session_id: session = session_.sessions[i] if session_.sessions[i].session_id == session_id: session = session_.sessions[i]
user = session.db["user_name"] user = session.db["user_name"]
self.destroy_buffer("home_timeline", user) delete_buffers = []
self.destroy_buffer("mentions", user) for i in self.buffers:
self.destroy_buffer("direct_messages", user) if i.account == user and i.name != user:
self.destroy_buffer("sent_direct_messages", user) delete_buffers.append(i.name)
self.destroy_buffer("sent_tweets", user) for i in delete_buffers:
self.destroy_buffer("favourites", user) self.destroy_buffer(i, user)
self.destroy_buffer("followers", user) session.db = None
self.destroy_buffer("friends", user)
self.destroy_buffer("blocked", user)
self.destroy_buffer("muted", user)
self.destroy_buffer("events", user)
self.destroy_buffer("timelines", user)
for i in session.settings["other_buffers"]["timelines"]:
self.destroy_buffer("%s-timeline" % (i,), user)
self.destroy_buffer("favs_timelines", user)
self.destroy_buffer("searches", user)
for i in session.settings["other_buffers"]["tweet_searches"]:
self.destroy_buffer("%s-searchterm" % (i,), user)
for i in session.settings["other_buffers"]["trending_topic_buffers"]:
self.destroy_buffer("%s_tt" % (i,), user)
def destroy_buffer(self, buffer_name, account): def destroy_buffer(self, buffer_name, account):
buffer = self.search_buffer(buffer_name, account) buffer = self.search_buffer(buffer_name, account)
@@ -402,7 +397,10 @@ class Controller(object):
self.buffers.remove(buffer) self.buffers.remove(buffer)
del buffer del buffer
def search(self, value="", *args, **kwargs): def search_topic(self, term):
self.search(value=term)
def search(self, event=None, value="", *args, **kwargs):
""" Searches words or users in twitter. This creates a new buffer containing the search results.""" """ Searches words or users in twitter. This creates a new buffer containing the search results."""
log.debug("Creating a new search...") log.debug("Creating a new search...")
dlg = dialogs.search.searchDialog(value) dlg = dialogs.search.searchDialog(value)
@@ -476,8 +474,20 @@ class Controller(object):
buffer = self.get_best_buffer() buffer = self.get_best_buffer()
SoundsTutorial.soundsTutorial(buffer.session) SoundsTutorial.soundsTutorial(buffer.session)
def view_user_lists(self, users): def view_user_lists(self, *args, **kwargs):
pass buff = self.get_best_buffer()
if not hasattr(buff, "get_right_tweet"): return
tweet = buff.get_right_tweet()
if buff.type != "people":
users = utils.get_all_users(tweet, buff.session.db)
else:
users = [tweet["screen_name"]]
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
if dlg.get_response() == widgetUtils.OK:
user = dlg.get_user()
else:
return
l = listsController.listsController(buff.session, user=user)
def add_to_list(self, *args, **kwargs): def add_to_list(self, *args, **kwargs):
buff = self.get_best_buffer() buff = self.get_best_buffer()
@@ -506,8 +516,32 @@ class Controller(object):
except TwythonError as e: except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg)) output.speak("error %s: %s" % (e.error_code, e.msg))
def remove_from_list(self, user): def remove_from_list(self, *args, **kwargs):
pass buff = self.get_best_buffer()
if not hasattr(buff, "get_right_tweet"): return
tweet = buff.get_right_tweet()
if buff.type != "people":
users = utils.get_all_users(tweet, buff.session.db)
else:
users = [tweet["screen_name"]]
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
if dlg.get_response() == widgetUtils.OK:
user = dlg.get_user()
else:
return
dlg = dialogs.lists.removeUserListDialog()
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
if dlg.get_response() == widgetUtils.OK:
try:
list = buff.session.twitter.twitter.delete_list_member(list_id=buff.session.db["lists"][dlg.get_item()]["id"], screen_name=user)
older_list = utils.find_item(buff.session.db["lists"][dlg.get_item()]["id"], buff.session.db["lists"])
listBuffer = self.search_buffer("%s-list" % (buff.session.db["lists"][dlg.get_item()]["name"].lower()), buff.session.db["user_name"])
if listBuffer != None: listBuffer.get_user_ids()
buff.session.db["lists"].pop(older_list)
buff.session.db["lists"].append(list)
if listBuffer != None: pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
def list_manager(self, *args, **kwargs): def list_manager(self, *args, **kwargs):
s = self.get_best_buffer().session s = self.get_best_buffer().session
@@ -559,6 +593,7 @@ class Controller(object):
self.exit_() self.exit_()
def exit_(self, *args, **kwargs): def exit_(self, *args, **kwargs):
for i in self.buffers: i.save_positions()
log.debug("Exiting...") log.debug("Exiting...")
log.debug("Saving global configuration...") log.debug("Saving global configuration...")
config.app.write() config.app.write()
@@ -696,6 +731,9 @@ class Controller(object):
msg = messages.viewTweet(tweet, tweetsList) msg = messages.viewTweet(tweet, tweetsList)
elif buffer.type == "account" or buffer.type == "empty": elif buffer.type == "account" or buffer.type == "empty":
return return
elif buffer.name == "sent_tweets":
tweet, tweetsList = buffer.get_full_tweet()
msg = messages.viewTweet(tweet, tweetsList)
else: else:
non_tweet = buffer.get_formatted_message() non_tweet = buffer.get_formatted_message()
msg = messages.viewTweet(non_tweet, [], False) msg = messages.viewTweet(non_tweet, [], False)
@@ -713,8 +751,7 @@ class Controller(object):
users = [tweet["screen_name"]] users = [tweet["screen_name"]]
dlg = dialogs.userSelection.selectUserDialog(users=users, default=default) dlg = dialogs.userSelection.selectUserDialog(users=users, default=default)
if dlg.get_response() == widgetUtils.OK: if dlg.get_response() == widgetUtils.OK:
buffer = self.get_best_buffer() usr = utils.if_user_exists(buff.session.twitter.twitter, dlg.get_user())
usr = utils.if_user_exists(buffer.session.twitter.twitter, dlg.get_user())
if usr != None: if usr != None:
if usr["protected"] == True: if usr["protected"] == True:
if usr["following"] == False: if usr["following"] == False:
@@ -723,32 +760,38 @@ class Controller(object):
answer = commonMessageDialogs.protected_user() answer = commonMessageDialogs.protected_user()
if answer == widgetUtils.NO: return if answer == widgetUtils.NO: return
if dlg.get_action() == "tweets": if dlg.get_action() == "tweets":
if dlg.get_user() in buffer.session.settings["other_buffers"]["timelines"]: if usr["statuses_count"] == 0:
commonMessageDialogs.no_tweets()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["timelines"]:
commonMessageDialogs.timeline_exist() commonMessageDialogs.timeline_exist()
return return
tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (dlg.get_user(),), buffer.session, buffer.session.db["user_name"], bufferType=None, screen_name=dlg.get_user()) tl = buffersController.baseBufferController(self.view.nb, "get_user_timeline", "%s-timeline" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], bufferType=None, screen_name=dlg.get_user())
tl.start_stream() tl.start_stream()
pos=self.view.search("timelines", buffer.session.db["user_name"]) pos=self.view.search("timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos) self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl) # self.buffers.insert(pos+1, tl)
self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(dlg.get_user()), pos=pos) self.view.insert_buffer(tl.buffer, name=_(u"Timeline for {}").format(dlg.get_user()), pos=pos)
buffer.session.settings["other_buffers"]["timelines"].append(dlg.get_user()) buff.session.settings["other_buffers"]["timelines"].append(dlg.get_user())
pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buffer.session) pub.sendMessage("restart-streams", streams=["timelinesStream"], session=buff.session)
buffer.session.sound.play("create_timeline.ogg") buff.session.sound.play("create_timeline.ogg")
else: else:
if dlg.get_user() in buffer.session.settings["other_buffers"]["favourites_timelines"]: if usr["favourites_count"] == 0:
commonMessageDialogs.no_favs()
return
if dlg.get_user() in buff.session.settings["other_buffers"]["favourites_timelines"]:
commonMessageDialogs.timeline_exist() commonMessageDialogs.timeline_exist()
return return
tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (dlg.get_user(),), buffer.session, buffer.session.db["user_name"], bufferType=None, screen_name=dlg.get_user()) tl = buffersController.baseBufferController(self.view.nb, "get_favorites", "%s-favorite" % (dlg.get_user(),), buff.session, buff.session.db["user_name"], bufferType=None, screen_name=dlg.get_user())
pos=self.view.search("favs_timelines", buffer.session.db["user_name"]) pos=self.view.search("favs_timelines", buff.session.db["user_name"])
self.insert_buffer(tl, pos) self.insert_buffer(tl, pos+1)
# self.buffers.insert(pos+1, tl) # self.buffers.insert(pos+1, tl)
self.view.insert_buffer(buffer=tl.buffer, name=_(u"Favourites timeline for {}").format(dlg.get_user()), pos=pos) self.view.insert_buffer(buffer=tl.buffer, name=_(u"Likes for {}").format(dlg.get_user()), pos=pos)
tl.start_stream() tl.start_stream()
tl.timer = RepeatingTimer(300, tl.start_stream) tl.timer = RepeatingTimer(300, tl.start_stream)
tl.timer.start() tl.timer.start()
buffer.session.settings["other_buffers"]["favourites_timelines"].append(dlg.get_user()) buff.session.settings["other_buffers"]["favourites_timelines"].append(dlg.get_user())
buffer.session.sound.play("create_timeline.ogg") buff.session.sound.play("create_timeline.ogg")
else: else:
commonMessageDialogs.user_not_exist() commonMessageDialogs.user_not_exist()
@@ -920,13 +963,11 @@ class Controller(object):
def left(self, *args, **kwargs): def left(self, *args, **kwargs):
buff = self.view.get_current_buffer_pos() buff = self.view.get_current_buffer_pos()
print buff
buffer = self.get_current_buffer() buffer = self.get_current_buffer()
if not hasattr(buffer.buffer, "list"): if not hasattr(buffer.buffer, "list"):
output.speak(_(u"No session is currently in focus. Focus a session with the next or previous session shortcut."), True) output.speak(_(u"No session is currently in focus. Focus a session with the next or previous session shortcut."), True)
return return
if buff == self.get_first_buffer(buffer.account) or buff == 0: if buff == self.get_first_buffer(buffer.account) or buff == 0:
print "This is the last buffer"
self.view.change_buffer(self.get_last_buffer(buffer.account)) self.view.change_buffer(self.get_last_buffer(buffer.account))
else: else:
self.view.change_buffer(buff-1) self.view.change_buffer(buff-1)
@@ -1185,7 +1226,7 @@ class Controller(object):
def manage_item_in_list(self, data, user, where): def manage_item_in_list(self, data, user, where):
buffer = self.search_buffer("%s" % (where,), user) buffer = self.search_buffer("%s" % (where,), user)
if buffer == None: return if buffer == None: return
play_sound = "tweet_timeline.ogg" play_sound = "list_tweet.ogg"
if "%s" % (where,) not in buffer.session.settings["other_buffers"]["muted_buffers"]: if "%s" % (where,) not in buffer.session.settings["other_buffers"]["muted_buffers"]:
self.notify(buffer.session, play_sound=play_sound) self.notify(buffer.session, play_sound=play_sound)
output.speak(_(u"One tweet from %s") % (data["user"]["name"])) output.speak(_(u"One tweet from %s") % (data["user"]["name"]))
@@ -1199,10 +1240,28 @@ class Controller(object):
log.debug("Starting the streaming endpoint") log.debug("Starting the streaming endpoint")
session.start_streaming() session.start_streaming()
def set_positions(self):
for i in session_.sessions:
self.set_buffer_positions(i)
def manage_stream_errors(self, session): def manage_stream_errors(self, session):
log.error("An error ocurred with the stream for the %s session. It will be destroyed" % (session,)) log.error(" Restarting %s session streams. It will be destroyed" % (session,))
s = session_.sessions[session] s = session_.sessions[session]
s.listen_stream_error() try:
if hasattr(s, "main_stream"):
self.main_stream.disconnect()
log.error("main stream disconnected")
del s.main_stream
self.timelinesStream.disconnect()
del s.timelinesStream
s.counter = 0
s.reconnection_function_active = False
except AttributeError:
log.error("Error deleting some thing")
# for i in self.buffers:
# if i.invisible == True and i.session.session_id == s.session_id and i.type != "people":
# i.start_stream()
# s.listen_stream_error()
def check_connection(self): def check_connection(self):
for i in session_.sessions: for i in session_.sessions:
@@ -1215,7 +1274,7 @@ class Controller(object):
if buffer == "favourites": if buffer == "favourites":
favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"]) favourites = buffersController.baseBufferController(self.view.nb, "get_favorites", "favourites", buff.session, buff.session.db["user_name"])
self.buffers.append(favourites) self.buffers.append(favourites)
self.view.insert_buffer(favourites.buffer, name=_(u"Favourites"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"])) self.view.insert_buffer(favourites.buffer, name=_(u"Likes"), pos=self.view.search(buff.session.db["user_name"], buff.session.db["user_name"]))
favourites.start_stream() favourites.start_stream()
if buffer == "followers": if buffer == "followers":
followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"]) followers = buffersController.peopleBufferController(self.view.nb, "get_followers_list", "followers", buff.session, buff.session.db["user_name"], screen_name=buff.session.db["user_name"])
@@ -1249,7 +1308,8 @@ class Controller(object):
return return
tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"])) tl = buffersController.listBufferController(self.view.nb, "get_list_statuses", create+"-list", buff.session, buff.session.db["user_name"], bufferType=None, list_id=utils.find_list(create, buff.session.db["lists"]))
buff.session.lists.append(tl) buff.session.lists.append(tl)
self.buffers.append(tl) pos=self.view.search("lists", buff.session.db["user_name"])
self.insert_buffer(tl, pos)
self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(create), pos=self.view.search("lists", buff.session.db["user_name"])) self.view.insert_buffer(tl.buffer, name=_(u"List for {}").format(create), pos=self.view.search("lists", buff.session.db["user_name"]))
tl.start_stream() tl.start_stream()
buff.session.settings["other_buffers"]["lists"].append(create) buff.session.settings["other_buffers"]["lists"].append(create)
@@ -1334,28 +1394,18 @@ class Controller(object):
os.chdir("../../") os.chdir("../../")
def insert_buffer(self, buffer, position): def insert_buffer(self, buffer, position):
# print ref_buf.name, ref_buf.account self.buffers.insert(position, buffer)
# if ref_buf.account != buffer.account or ref_buf.type == "account" or type(ref_buf) == buffers.emptyPanel:
buffers = self.get_buffers_for_account(buffer.account) def copy_to_clipboard(self, *args, **kwargs):
# ref_buf = self.buffers[position+1] output.copy(self.get_current_buffer().get_message())
empty = True output.speak(_(u"Copied"))
for i in buffers[position+1:]:
if i.type == "account" or i.invisible == False: def repeat_item(self, *args, **kwargs):
empty = True output.speak(self.get_current_buffer().get_message())
else:
empty = False def execute_action(self, action):
if empty == True: if hasattr(self, action):
self.buffers.append(buffer) getattr(self, action)()
print "account"
else:
self.buffers.insert(position+1, buffer)
for i in self.buffers:
print i.name, i.account
def __del__(self): def __del__(self):
config.app.write() config.app.write()
def change_buffer(self, bufferPosition):
buff = self.buffers[bufferPosition]
newPos = self.view.search(buff.name, buff.account)
self.view.change_buffer(newPos)

View File

@@ -25,6 +25,7 @@ class basicTweet(object):
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach) widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
# if system == "Windows": # if system == "Windows":
# if messageType != "dm":
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor) widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
self.text_processor() self.text_processor()
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten) widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
@@ -37,7 +38,7 @@ class basicTweet(object):
text_to_translate = self.message.get_text().encode("utf-8") text_to_translate = self.message.get_text().encode("utf-8")
source = [x[0] for x in translator.translator.available_languages()][dlg.get("source_lang")] 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")] dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
msg = translator.translator.translate(text_to_translate, source, dest) msg = translator.translator.translate(text=text_to_translate, source=source, target=dest)
self.message.set_text(msg) self.message.set_text(msg)
self.message.text_focus() self.message.text_focus()
output.speak(_(u"Translated")) output.speak(_(u"Translated"))
@@ -97,6 +98,7 @@ class basicTweet(object):
checker = SpellChecker.spellchecker.spellChecker(text, "") checker = SpellChecker.spellchecker.spellChecker(text, "")
if hasattr(checker, "fixed_text"): if hasattr(checker, "fixed_text"):
self.message.set_text(checker.fixed_text) self.message.set_text(checker.fixed_text)
self.message.text_focus()
def attach(self, *args, **kwargs): def attach(self, *args, **kwargs):
def completed_callback(): def completed_callback():
@@ -109,13 +111,17 @@ class basicTweet(object):
output.speak(_(u"Unable to upload the audio")) output.speak(_(u"Unable to upload the audio"))
dlg.cleanup() dlg.cleanup()
dlg = audioUploader.audioUploader(self.session.settings, completed_callback) dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
self.message.text_focus()
class tweet(basicTweet): class tweet(basicTweet):
def __init__(self, session, title, caption, text, messageType="tweet", max=140): def __init__(self, session, title, caption, text, twishort_enabled, messageType="tweet", max=140):
super(tweet, self).__init__(session, title, caption, text, messageType, max) super(tweet, self).__init__(session, title, caption, text, messageType, max)
self.image = None self.image = None
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image) 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) widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
if twishort_enabled == False:
try: self.message.long_tweet.SetValue(False)
except AttributeError: pass
def upload_image(self, *args, **kwargs): def upload_image(self, *args, **kwargs):
if self.message.get("upload_image") == _(u"Discard image"): if self.message.get("upload_image") == _(u"Discard image"):
@@ -127,14 +133,15 @@ class tweet(basicTweet):
self.image = self.message.get_image() self.image = self.message.get_image()
if self.image != None: if self.image != None:
self.message.set("upload_image", _(u"Discard image")) self.message.set("upload_image", _(u"Discard image"))
self.message.text_focus()
def autocomplete_users(self, *args, **kwargs): def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id) c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
c.show_menu() c.show_menu()
class reply(tweet): class reply(tweet):
def __init__(self, session, title, caption, text, users=None): def __init__(self, session, title, caption, text, twishort_enabled, users=None):
super(reply, self).__init__(session, title, caption, text, messageType="reply") super(reply, self).__init__(session, title, caption, text, twishort_enabled, messageType="reply")
self.users = users self.users = users
if self.users != None and len(self.users) > 1: if self.users != None and len(self.users) > 1:
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.BUTTON_PRESSED, self.mention_all) widgetUtils.connect_event(self.message.mentionAll, widgetUtils.BUTTON_PRESSED, self.mention_all)
@@ -148,7 +155,7 @@ class reply(tweet):
class dm(basicTweet): class dm(basicTweet):
def __init__(self, session, title, caption, text): def __init__(self, session, title, caption, text):
super(dm, self).__init__(session, title, caption, text, messageType="dm") super(dm, self).__init__(session, title, caption, text, messageType="dm", max=10000)
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users) widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
def autocomplete_users(self, *args, **kwargs): def autocomplete_users(self, *args, **kwargs):
@@ -161,9 +168,9 @@ class viewTweet(basicTweet):
text = "" text = ""
for i in xrange(0, len(tweetList)): for i in xrange(0, len(tweetList)):
if tweetList[i].has_key("retweeted_status"): 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"]) text = text + "rt @%s: %s\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i]["retweeted_status"]["text"])
else: else:
text = text + "@%s: %s\n\n" % (tweetList[i]["user"]["screen_name"], tweetList[i]["text"]) text = text + "@%s: %s\n" % (tweetList[i]["user"]["screen_name"], tweetList[i]["text"])
rt_count = str(tweet["retweet_count"]) rt_count = str(tweet["retweet_count"])
favs_count = str(tweet["favorite_count"]) favs_count = str(tweet["favorite_count"])
if text == "": if text == "":
@@ -171,6 +178,7 @@ class viewTweet(basicTweet):
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["text"]) text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["text"])
else: else:
text = tweet["text"] text = tweet["text"]
text = self.clear_text(text)
self.message = message.viewTweet(text, rt_count, favs_count) self.message = message.viewTweet(text, rt_count, favs_count)
self.message.set_title(len(text)) self.message.set_title(len(text))
else: else:
@@ -187,3 +195,10 @@ class viewTweet(basicTweet):
if len(utils.find_urls_in_text(self.message.get_text())) > 0: if len(utils.find_urls_in_text(self.message.get_text())) > 0:
return True return True
return False return False
def clear_text(self, text):
urls = utils.find_urls_in_text(text)
for i in urls:
if "https://twitter.com/" in i:
text = text.replace(i, "")
return text

View File

@@ -34,7 +34,7 @@ class globalSettingsController(object):
if ".keymap" not in i: if ".keymap" not in i:
continue continue
try: try:
res[config_utils.load_config(paths.app_path('keymaps/'+i))['info']['name']]=i res[i[:-7]] =i
except: except:
log.exception("Exception while loading keymap " + i) log.exception("Exception while loading keymap " + i)
return res return res
@@ -79,6 +79,8 @@ class globalSettingsController(object):
self.needs_restart = True self.needs_restart = True
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]: 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()] config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
kmFile = open(paths.config_path("keymap.keymap"), "w")
kmFile.close()
self.needs_restart = True self.needs_restart = True
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"): if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):

View File

@@ -95,7 +95,7 @@ class profileController(object):
else: verified = _(u"No") else: verified = _(u"No")
string = string+ _(u"Verified: %s\n") % (verified) string = string+ _(u"Verified: %s\n") % (verified)
string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"]) string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"])
string = string+ _(u"Favourites: %s") % (self.data["favourites_count"]) string = string+ _(u"Likes: %s") % (self.data["favourites_count"])
return string return string
def visit_url(self, *args, **kwargs): def visit_url(self, *args, **kwargs):

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from wxUI.dialogs import userActions
from pubsub import pub
import re import re
import widgetUtils import widgetUtils
import output import output
from wxUI.dialogs import userActions
from pubsub import pub
from twython import TwythonError from twython import TwythonError
from extra import autocompletionUsers
class userActionsController(object): class userActionsController(object):
def __init__(self, buffer, users=[], default="follow"): def __init__(self, buffer, users=[], default="follow"):
@@ -12,9 +13,14 @@ class userActionsController(object):
self.buffer = buffer self.buffer = buffer
self.session = buffer.session self.session = buffer.session
self.dialog = userActions.UserActionsDialog(users, default) self.dialog = userActions.UserActionsDialog(users, default)
widgetUtils.connect_event(self.dialog.autocompletion, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
if self.dialog.get_response() == widgetUtils.OK: if self.dialog.get_response() == widgetUtils.OK:
self.process_action() self.process_action()
def autocomplete_users(self, *args, **kwargs):
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
c.show_menu("dm")
def process_action(self): def process_action(self):
action = self.dialog.get_action() action = self.dialog.get_action()
user = self.dialog.get_user() user = self.dialog.get_user()

View File

@@ -74,10 +74,10 @@ class audioUploader(object):
def on_pause(self, *args, **kwargs): def on_pause(self, *args, **kwargs):
if self.dialog.get("pause") == _(u"Pause"): if self.dialog.get("pause") == _(u"Pause"):
self.recording.pause() self.recording.pause()
self.dialog.set("pause", _(u"Resume")) self.dialog.set("pause", _(u"&Resume"))
elif self.dialog.get("pause") == _(u"Resume"): elif self.dialog.get("pause") == _(u"Resume"):
self.recording.play() self.recording.play()
self.dialog.set("pause", _(U"Pause")) self.dialog.set("pause", _(U"&Pause"))
def on_record(self, *args, **kwargs): def on_record(self, *args, **kwargs):
if self.recording != None: if self.recording != None:
@@ -92,7 +92,7 @@ class audioUploader(object):
self.file = tempfile.mktemp(suffix='.wav') self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file) self.recording = sound.recording(self.file)
self.recording.play() self.recording.play()
self.dialog.set("record", _(u"Stop")) self.dialog.set("record", _(u"&Stop"))
output.speak(_(u"Recording")) output.speak(_(u"Recording"))
def stop_recording(self): def stop_recording(self):
@@ -100,11 +100,11 @@ class audioUploader(object):
self.recording.free() self.recording.free()
output.speak(_(u"Stopped")) output.speak(_(u"Stopped"))
self.recorded = True self.recorded = True
self.dialog.set("record", _(u"Record")) self.dialog.set("record", _(u"&Record"))
self.file_attached() self.file_attached()
def file_attached(self): def file_attached(self):
self.dialog.set("pause", _(u"Pause")) self.dialog.set("pause", _(u"&Pause"))
self.dialog.disable_control("record") self.dialog.disable_control("record")
self.dialog.enable_control("play") self.dialog.enable_control("play")
self.dialog.enable_control("discard") self.dialog.enable_control("discard")
@@ -137,11 +137,11 @@ class audioUploader(object):
# try: # try:
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE) self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play() self.playing.play()
self.dialog.set("play", _(u"Stop")) self.dialog.set("play", _(u"&Stop"))
try: try:
while self.playing.is_playing: while self.playing.is_playing:
pass pass
self.dialog.set("play", _(u"Play")) self.dialog.set("play", _(u"&Play"))
self.playing.free() self.playing.free()
self.playing = None self.playing = None
except: except:
@@ -151,7 +151,7 @@ class audioUploader(object):
output.speak(_(u"Stopped")) output.speak(_(u"Stopped"))
self.playing.stop() self.playing.stop()
self.playing.free() self.playing.free()
self.dialog.set("play", _(u"Play")) self.dialog.set("play", _(u"&Play"))
self.playing = None self.playing = None
def postprocess(self): def postprocess(self):

View File

@@ -31,14 +31,14 @@ class audioDialog(widgetUtils.BaseDialog):
btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL) btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.play = wx.Button(panel, -1, _(u"Play")) self.play = wx.Button(panel, -1, _(u"&Play"))
self.play.Disable() self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"Pause")) self.pause = wx.Button(panel, -1, _(u"&Pause"))
self.pause.Disable() self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"Record")) self.record = wx.Button(panel, -1, _(u"&Record"))
self.record.SetFocus() self.record.SetFocus()
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file")) self.attach_exists = wx.Button(panel, -1, _(u"&Add an existing file"))
self.discard = wx.Button(panel, -1, _(u"Discard")) self.discard = wx.Button(panel, -1, _(u"&Discard"))
self.discard.Disable() self.discard.Disable()
label = wx.StaticText(panel, -1, _(u"Upload to")) label = wx.StaticText(panel, -1, _(u"Upload to"))
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY) self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
@@ -47,7 +47,7 @@ class audioDialog(widgetUtils.BaseDialog):
servicesBox.Add(self.services, 0, wx.ALL, 5) servicesBox.Add(self.services, 0, wx.ALL, 5)
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach")) self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
self.attach.Disable() self.attach.Disable()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel")) cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Cancel"))
btnSizer.Add(self.play, 0, wx.ALL, 5) btnSizer.Add(self.play, 0, wx.ALL, 5)
btnSizer.Add(self.pause, 0, wx.ALL, 5) btnSizer.Add(self.pause, 0, wx.ALL, 5)
btnSizer.Add(self.record, 0, wx.ALL, 5) btnSizer.Add(self.record, 0, wx.ALL, 5)

View File

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

View File

@@ -1,9 +1,153 @@
#!/usr/bin/env python # encoding: utf-8
import goslate #
# Copyright (C) 2013 Mesar Hameed <mhameed@src.gnome.org>
# This file is covered by the GNU General Public License.
def translate(text, source_lang, target_lang): import os
gs = goslate.Goslate() import re
return gs.translate(text, target_lang, source_lang) import sys
import threading
from time import sleep
from random import randint
import logging
log = logging.getLogger("translator")
import urllib2
# Each group has to be a class of possible breaking points for the writing script.
# Usually this is the major syntax marks, such as:
# full stop, comma, exclaim, question, etc.
arabicBreaks = u'[،؛؟]'
# Thanks to Talori in the NVDA irc room:
# U+3000 to U+303F, U+FE10 to U+FE1F, U+FE30 to U+FE6F, U+FF01 to U+FF60
chineseBreaks = u'[ -〿︐-︟︰-﹯!-⦆]'
latinBreaks = r'[.,!?;:\n]'
splitReg = re.compile(u"{arabic}|{chinese}|{latin}".format(arabic=arabicBreaks, chinese=chineseBreaks, latin=latinBreaks))
def translate(text, source="auto", target="en"):
if source == "": source = "auto"
t = Translator(lang_from=source, lang_to=target, text=text)
t.start()
while t.isAlive():
sleep(0.1)
t.join()
return t.translation
def splitChunks(text, chunksize):
pos = 0
potentialPos = 0
for splitMark in splitReg.finditer(text):
if (splitMark.start() - pos +1) < chunksize:
potentialPos = splitMark.start()
continue
else:
yield text[pos:potentialPos+1]
pos = potentialPos + 1
potentialPos = splitMark.start()
yield text[pos:]
class Translator(threading.Thread):
def __init__(self, lang_from, lang_to, text, lang_swap=None, chunksize=350, *args, **kwargs):
super(Translator, self).__init__(*args, **kwargs)
self._stop = threading.Event()
self.text = text
self.chunksize = chunksize
self.lang_to = lang_to
self.lang_from = lang_from
self.lang_swap = lang_swap
self.translation = ''
self.lang_translated = ''
self.firstChunk = True
def stop(self):
self._stop.set()
def run(self):
for chunk in splitChunks(self.text, self.chunksize):
# Make sure we don't send requests to google too often.
# Try to simulate a human.
if not self.firstChunk:
sleep(randint(1, 10))
req = self.buildRequest(chunk, self.lang_from, self.lang_to)
try:
response = urllib2.urlopen(req)
translation, lang_translated = self.parseData(response)
if self.firstChunk and self.lang_from == "auto" and lang_translated == self.lang_to and self.lang_swap is not None:
self.lang_to = self.lang_swap
self.firstChunk = False
req = self.buildRequest(chunk.encode('utf-8'), self.lang_from, self.lang_to)
response = urllib2.urlopen(req)
translation, lang_translated = self.parseData(response)
except Exception as e:
log.exception("Can not translate text '%s'" %chunk)
# We have probably been blocked, so stop trying to translate.
raise e
self.translation += translation
# some adjustment, better to do on full text
self.translation = self.fixNewlines(self.translation)
self.lang_translated = lang_translated
def buildRequest(self, text, lang_from, lang_to):
"""Build POST request which will be sent to Google."""
urlTemplate = 'http://translate.google.com/translate_a/single?client=t&sl={lang_from}&tl={lang_to}&ie=utf-8&oe=utf-8&dt=t&dt=bd&tk='
url = urlTemplate.format(lang_from=lang_from, lang_to=lang_to)
header = {'User-agent': 'Mozilla/5.0', 'Content-Type': 'application/x-www-form-urlencoded'}
data = 'text=%s' %urllib2.quote(text)
req = urllib2.Request(url, data, header)
return req
def parseData(self, response):
"""Parse unstructured response."""
data = response.readlines()[0]
# get segments with couples ["translation","original text"]
l1, l2 = data.split(']],', 1)
translation = l1[3:]
if l2.startswith('[[\"'):
# get list of synonyms
syn = l2[l2.find(',[')+1:l2.find(']')].split(',')
temp = ', '.join([x.replace('\"', '') for x in syn])
else:
# get a list with each couple as item
sentences = translation.split('],[')
temp = ''
# get translation, removing first char (quote symbol)
for item in sentences:
item = item.split('\",\"', 1)[0][1:]
# join all translations
temp = ' '.join([temp, item])
translation = temp.decode('string-escape').decode('utf-8')
translation = self.fixPunctuation(translation)
# get the language of original text
tempLang = data.partition(']],,\"')[2]
lang = tempLang[:tempLang.find('\"')]
if lang == '':
lang = _("unavailable")
return translation, lang
def fixPunctuation(self, translation):
"""Clean text from space before punctuation symbol."""
# list of potentially positions of spaces to remove
spacePos = []
for puncMark in splitReg.finditer(translation):
spacePos.append(puncMark.start()-1)
if len(spacePos) == 0:
return translation
fixedTranslation = ''
for n in xrange(0,len(translation)):
temp = translation[n]
if n in spacePos and temp == ' ':
continue
else:
fixedTranslation += temp
return fixedTranslation
def fixNewlines(self, translation):
"""Adjust newlines and (subsequent or double) spaces."""
fixes = [('\r\n ', '\r\n'), ('\n ', '\r\n'), (' ', ' ')]
for fix in fixes:
translation = translation.replace(fix[0], fix[1])
# first char is a space, so...
return translation[1:]
languages = { languages = {
"af": _(u"Afrikaans"), "af": _(u"Afrikaans"),

View File

@@ -11,9 +11,17 @@ def fix():
locales.BasqueLocale = BasqueLocale locales.BasqueLocale = BasqueLocale
locales.TurkishLocale.names[-1] = "tr_tr" locales.TurkishLocale.names[-1] = "tr_tr"
locales.ArabicLocale.names[-1] = "ar_eg" locales.ArabicLocale.names[-1] = "ar_eg"
# insert a modified function so if there is no language available in arrow, returns English locale.
locales.get_locale = get_locale
# We need to reassign the locales list for updating the list with our new contents. # We need to reassign the locales list for updating the list with our new contents.
locales._locales = locales._map_locales() locales._locales = locales._map_locales()
def get_locale(name):
locale_cls = locales._locales.get(name.lower())
if locale_cls is None:
return locales.EnglishLocale()
return locale_cls()
class CatalaLocale(Locale): class CatalaLocale(Locale):
names = ['ca', 'ca_ca'] names = ['ca', 'ca_ca']
past = 'Fa {0}' past = 'Fa {0}'
@@ -67,11 +75,13 @@ class GalicianLocale(Locale):
class BasqueLocale(Locale): class BasqueLocale(Locale):
names = ['eu', 'eu_eu'] names = ['eu', 'eu_eu']
past = 'duela {0}' past = 'duela {0}'
future = '{0}' # I don't know what's the right phrase in Basque for the future. future = '{0} igarota'
timeframes = { timeframes = {
'now': 'Orain', 'now': 'Orain',
'seconds': 'segundu', # 'second': 'segundu bat',
'seconds': 'segundu batzuk', # without specifying a number.
#'seconds': '{0} segundu', # specifying a number
'minute': 'minutu bat', 'minute': 'minutu bat',
'minutes': '{0} minutu', 'minutes': '{0} minutu',
'hour': 'ordu bat', 'hour': 'ordu bat',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ import widgetUtils
import config import config
import wx_ui import wx_ui
import constants import constants
from pubsub import pub
class KeystrokeEditor(object): class KeystrokeEditor(object):
def __init__(self): def __init__(self):
@@ -14,6 +15,7 @@ class KeystrokeEditor(object):
self.hold_map = self.map.copy() self.hold_map = self.map.copy()
self.dialog.put_keystrokes(constants.actions, self.map) self.dialog.put_keystrokes(constants.actions, self.map)
widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_keystroke) widgetUtils.connect_event(self.dialog.edit, widgetUtils.BUTTON_PRESSED, self.edit_keystroke)
widgetUtils.connect_event(self.dialog.execute, widgetUtils.BUTTON_PRESSED, self.execute_action)
self.dialog.get_response() self.dialog.get_response()
def edit_keystroke(self, *args, **kwargs): def edit_keystroke(self, *args, **kwargs):
@@ -52,3 +54,7 @@ class KeystrokeEditor(object):
wx_ui.no_key() wx_ui.no_key()
return return
return "+".join(keys) return "+".join(keys)
def execute_action(self, *args, **kwargs):
action = self.dialog.actions[self.dialog.get_action()]
pub.sendMessage("execute-action", action=action)

View File

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

View File

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

View File

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

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################ ############################################################
from twitter import utils
def get_id(url): def get_id(url):
return url.split("/")[-1] return url.split("/")[-1]
@@ -26,3 +27,9 @@ def is_long(tweet):
if "twitter.com" in tweet["entities"]["urls"][url]["expanded_url"]: if "twitter.com" in tweet["entities"]["urls"][url]["expanded_url"]:
long = get_id(tweet["entities"]["urls"][url]["expanded_url"]) long = get_id(tweet["entities"]["urls"][url]["expanded_url"])
return long return long
def clear_url(tweet):
urls = utils.find_urls_in_text(tweet["text"])
try: tweet["message"] = tweet["message"].replace(urls[-1], "")
except IndexError: pass
return tweet

View File

@@ -23,8 +23,10 @@ import application
import keys import keys
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
import fixes import fixes
#extra variables to control the temporal stdout and stderr, while the final files are opened. We understand that some errors could happen while all outputs are closed, so let's try to avoid it. #extra variables to control the temporary stdout and stderr, while the final files are opened. We understand that some errors could happen while all outputs are closed, so let's try to avoid it.
import widgetUtils import widgetUtils
import webbrowser
from wxUI import commonMessageDialogs
if system == "Windows": if system == "Windows":
from logger import logger from logger import logger
from update import updater from update import updater
@@ -51,18 +53,20 @@ log = logging.getLogger("main")
def setup(): def setup():
log.debug("Starting " + application.name + " %s" % (application.version,)) log.debug("Starting " + application.name + " %s" % (application.version,))
config.setup() config.setup()
fixes.setup()
log.debug("Using %s %s" % (platform.system(), platform.architecture()[0])) log.debug("Using %s %s" % (platform.system(), platform.architecture()[0]))
log.debug("Application path is %s" % (paths.app_path(),)) log.debug("Application path is %s" % (paths.app_path(),))
log.debug("config path is %s" % (paths.config_path(),)) log.debug("config path is %s" % (paths.config_path(),))
sound.setup() sound.setup()
output.setup() output.setup()
languageHandler.setLanguage(config.app["app-settings"]["language"]) languageHandler.setLanguage(config.app["app-settings"]["language"])
fixes.setup()
keys.setup() keys.setup()
from controller import mainController from controller import mainController
from sessionmanager import sessionManager from sessionmanager import sessionManager
app = widgetUtils.mainLoopObject() app = widgetUtils.mainLoopObject()
if system == "Windows": if system == "Windows":
if config.app["app-settings"]["donation_dialog_displayed"] == False:
donation()
updater.do_update() updater.do_update()
sm = sessionManager.sessionManagerController() sm = sessionManager.sessionManagerController()
sm.fill_list() sm.fill_list()
@@ -82,4 +86,10 @@ def setup():
GLib.idle_add(r.start) GLib.idle_add(r.start)
app.run() app.run()
def donation():
dlg = commonMessageDialogs.donation()
if dlg == widgetUtils.YES:
webbrowser.open_new_tab("http://twblue.es/?q=donate")
config.app["app-settings"]["donation_dialog_displayed"] = True
setup() setup()

View File

@@ -27,7 +27,9 @@ def stream_threaded(func, *args, **kwargs):
try: try:
func(**k) func(**k)
except: except:
pub.sendMessage("streamError", session=a[0]) log.error("Error in stream with args: %r" % (a,))
pub.sendMessage("stream-error", session=a[0])
thread = threading.Thread(target=new_func, args=args, kwargs=kwargs) thread = threading.Thread(target=new_func, args=args, kwargs=kwargs)
thread.daemon = True thread.daemon = True
thread.start() thread.start()

View File

@@ -24,7 +24,7 @@ def setup ():
except: except:
return logging.exception("Output: Error during initialization.") return logging.exception("Output: Error during initialization.")
def Copy(text): def copy(text):
import win32clipboard import win32clipboard
#Copies text to the clipboard. #Copies text to the clipboard.
win32clipboard.OpenClipboard() win32clipboard.OpenClipboard()

View File

@@ -29,7 +29,7 @@ def config_path():
if directory != None: path = os.path.join(directory, "config") if directory != None: path = os.path.join(directory, "config")
elif directory == None: path = app_path(u"config") elif directory == None: path = app_path(u"config")
elif mode == "installed": elif mode == "installed":
path = data_path("config") path = data_path(u"config")
if not os.path.exists(path): if not os.path.exists(path):
log.debug("%s path does not exist, creating..." % (path,)) log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path) os.mkdir(path)
@@ -42,7 +42,7 @@ def logs_path():
if directory != None: path = os.path.join(directory, "logs") if directory != None: path = os.path.join(directory, "logs")
elif directory == None: path = app_path(u"logs") elif directory == None: path = app_path(u"logs")
elif mode == "installed": elif mode == "installed":
path = data_path("logs") path = data_path(u"logs")
if not os.path.exists(path): if not os.path.exists(path):
log.debug("%s path does not exist, creating..." % (path,)) log.debug("%s path does not exist, creating..." % (path,))
os.mkdir(path) os.mkdir(path)

View File

@@ -9,7 +9,7 @@ import output
import time import time
import sound import sound
import logging import logging
from twitter import utils from twitter import utils, compose
from twython import TwythonError, TwythonRateLimitError, TwythonAuthError from twython import TwythonError, TwythonRateLimitError, TwythonAuthError
import config_utils import config_utils
import shelve import shelve
@@ -18,6 +18,7 @@ import os
from mysc.thread_utils import stream_threaded from mysc.thread_utils import stream_threaded
from pubsub import pub from pubsub import pub
log = logging.getLogger("sessionmanager.session") log = logging.getLogger("sessionmanager.session")
from long_tweets import tweets
sessions = {} sessions = {}
@@ -60,7 +61,9 @@ class Session(object):
if self.db.has_key(name) == False: if self.db.has_key(name) == False:
self.db[name] = [] self.db[name] = []
for i in data: for i in data:
if utils.find_item(i["id"], self.db[name]) == None: if utils.find_item(i["id"], self.db[name]) == None and utils.is_allowed(i, self.settings["twitter"]["ignored_clients"]) == True:
try: i = self.check_quoted_status(i)
except: pass
if self.settings["general"]["reverse_timelines"] == False: self.db[name].append(i) if self.settings["general"]["reverse_timelines"] == False: self.db[name].append(i)
else: self.db[name].insert(0, i) else: self.db[name].insert(0, i)
num = num+1 num = num+1
@@ -119,7 +122,8 @@ class Session(object):
# self.settings = None # self.settings = None
def init_sound(self): def init_sound(self):
self.sound = sound.soundSystem(self.settings["sound"]) try: self.sound = sound.soundSystem(self.settings["sound"])
except: pass
@_require_configuration @_require_configuration
def login(self, verify_credentials=True): def login(self, verify_credentials=True):
@@ -318,10 +322,12 @@ class Session(object):
for z in i.users: for z in i.users:
ids += str(z) + ", " ids += str(z) + ", "
if ids != "": if ids != "":
# print ids
stream_threaded(self.timelinesStream.statuses.filter, self.session_id, follow=ids) stream_threaded(self.timelinesStream.statuses.filter, self.session_id, follow=ids)
def add_friends(self): def add_friends(self):
try: try:
# print "setting friends"
self.timelinesStream.set_friends(self.main_stream.friends) self.timelinesStream.set_friends(self.main_stream.friends)
except AttributeError: except AttributeError:
pass pass
@@ -344,6 +350,7 @@ class Session(object):
self.logged = False self.logged = False
self.twitter = twitter.twitter.twitter() self.twitter = twitter.twitter.twitter()
self.login(False) self.login(False)
# pub.sendMessage("streamError", session=self.session_id)
if self.reconnection_function_active == True: return if self.reconnection_function_active == True: return
self.reconnection_function_active = True self.reconnection_function_active = True
if not hasattr(self, "main_stream"): if not hasattr(self, "main_stream"):
@@ -381,10 +388,11 @@ class Session(object):
if type(key) != str and type(key) != unicode: if type(key) != str and type(key) != unicode:
output.speak("Uh oh, while shelving the database, a key of type " + str(type(key)) + " has been found. It will be converted to type str, but this will cause all sorts of problems on deshelve. Please bring this to the attention of the " + application.name + " developers immediately. More information about the error will be written to the error log.",True) output.speak("Uh oh, while shelving the database, a key of type " + str(type(key)) + " has been found. It will be converted to type str, but this will cause all sorts of problems on deshelve. Please bring this to the attention of the " + application.name + " developers immediately. More information about the error will be written to the error log.",True)
log.error("Uh oh, " + str(key) + " is of type " + str(type(key)) + "!") log.error("Uh oh, " + str(key) + " is of type " + str(type(key)) + "!")
# Convert unicode objects to UTF-8 strings before shelve these objects.
if type(value) == list and self.settings["general"]["persist_size"] != -1 and len(value) > self.settings["general"]["persist_size"]: if type(value) == list and self.settings["general"]["persist_size"] != -1 and len(value) > self.settings["general"]["persist_size"]:
shelf[str(key)]=value[self.settings["general"]["persist_size"]:] shelf[str(key.encode("utf-8"))]=value[self.settings["general"]["persist_size"]:]
else: else:
shelf[str(key)]=value shelf[str(key.encode("utf-8"))]=value
shelf.close() shelf.close()
except: except:
output.speak("An exception occurred while shelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True) output.speak("An exception occurred while shelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
@@ -401,7 +409,7 @@ class Session(object):
try: try:
shelf=shelve.open(paths.config_path(shelfname),'c') shelf=shelve.open(paths.config_path(shelfname),'c')
for key,value in shelf.items(): for key,value in shelf.items():
self.db[unicode(key)]=value self.db[key]=value
shelf.close() shelf.close()
except: except:
output.speak("An exception occurred while deshelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True) output.speak("An exception occurred while deshelving the " + application.name + " database. It will be deleted and rebuilt automatically. If this error persists, send the error log to the " + application.name + " developers.",True)
@@ -410,3 +418,28 @@ class Session(object):
os.remove(shelfname) os.remove(shelfname)
except: except:
pass pass
def check_quoted_status(self, tweet):
status = tweets.is_long(tweet)
if status != False:
tweet["quoted"] = 1
tweet = self.get_quoted_tweet(tweet)
return tweet
def get_quoted_tweet(self, tweet):
quoted_tweet = self.twitter.twitter.show_status(id=tweet["id"])
urls = utils.find_urls_in_text(quoted_tweet["text"])
for url in range(0, len(urls)):
try: quoted_tweet["text"] = quoted_tweet["text"].replace(urls[url], quoted_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
l = tweets.is_long(quoted_tweet)
id = tweets.get_id(l)
try: original_tweet = self.twitter.twitter.show_status(id=id)
except: return quoted_tweet
urls = utils.find_urls_in_text(original_tweet["text"])
for url in range(0, len(urls)):
try: original_tweet["text"] = original_tweet["text"].replace(urls[url], original_tweet["entities"]["urls"][url]["expanded_url"])
except IndexError: pass
return compose.compose_quoted_tweet(quoted_tweet, original_tweet)

View File

@@ -25,6 +25,7 @@ import gettext
import application import application
import platform import platform
from glob import glob from glob import glob
import wx
def get_architecture_files(): def get_architecture_files():
if platform.architecture()[0][:2] == "32": if platform.architecture()[0][:2] == "32":
@@ -48,7 +49,7 @@ def get_data():
("accessible_output2/lib", glob("accessible_output2/lib/*.dll")), ("accessible_output2/lib", glob("accessible_output2/lib/*.dll")),
("keys/lib", glob("keys/lib/*.dll")), ("keys/lib", glob("keys/lib/*.dll")),
("keymaps", glob("keymaps/*.keymap")), ("keymaps", glob("keymaps/*.keymap")),
]+get_sounds()+get_locales()+get_documentation()+sound_lib.find_datafiles()+accessible_output2.find_datafiles()+enchant.utils.win32_data_files()+get_architecture_files() ]+get_sounds()+get_locales()+get_documentation()+sound_lib.find_datafiles()+accessible_output2.find_datafiles()+enchant.utils.win32_data_files()+get_architecture_files()+wx_files()
def get_documentation (): def get_documentation ():
answer = [] answer = []
@@ -79,6 +80,24 @@ def get_locales():
answer.append(new) answer.append(new)
return answer return answer
def wx_files():
wxDir=wx.__path__[0]
localeMoFiles=set()
for f in glob("locales/*/LC_MESSAGES"):
g=f.replace("locales", "locale")
wxMoFile=os.path.join(wxDir,g,"wxstd.mo")
if os.path.isfile(wxMoFile):
localeMoFiles.add((f,(wxMoFile,)))
lang=os.path.split(os.path.split(f)[0])[1]
if '_' in lang:
lang=lang.split('_')[0]
f=os.path.join('locale',lang,'lc_messages')
g=f.replace("locale", "locales")
wxMoFile=os.path.join(wxDir,f,"wxstd.mo")
if os.path.isfile(wxMoFile):
localeMoFiles.add((g,(wxMoFile,)))
return list(localeMoFiles)
if __name__ == '__main__': if __name__ == '__main__':
setup( setup(
name = application.name, name = application.name,
@@ -92,7 +111,7 @@ options = {
'py2exe': { 'py2exe': {
'optimize':2, 'optimize':2,
'packages': ["pubsub", "pubsub.core", "pubsub.core.kwargs", "dbhash"], 'packages': ["pubsub", "pubsub.core", "pubsub.core.kwargs", "dbhash"],
'dll_excludes': ["MPR.dll", "api-ms-win-core-apiquery-l1-1-0.dll", "api-ms-win-core-console-l1-1-0.dll", "api-ms-win-core-delayload-l1-1-1.dll", "api-ms-win-core-errorhandling-l1-1-1.dll", "api-ms-win-core-file-l1-2-0.dll", "api-ms-win-core-handle-l1-1-0.dll", "api-ms-win-core-heap-obsolete-l1-1-0.dll", "api-ms-win-core-libraryloader-l1-1-1.dll", "api-ms-win-core-localization-l1-2-0.dll", "api-ms-win-core-processenvironment-l1-2-0.dll", "api-ms-win-core-processthreads-l1-1-1.dll", "api-ms-win-core-profile-l1-1-0.dll", "api-ms-win-core-registry-l1-1-0.dll", "api-ms-win-core-synch-l1-2-0.dll", "api-ms-win-core-sysinfo-l1-2-0.dll", "api-ms-win-security-base-l1-2-0.dll", "api-ms-win-core-heap-l1-2-0.dll", "api-ms-win-core-interlocked-l1-2-0.dll", "api-ms-win-core-localization-obsolete-l1-1-0.dll", "api-ms-win-core-string-l1-1-0.dll", "api-ms-win-core-string-obsolete-l1-1-0.dll", "WLDAP32.dll", "MSVCP90.dll"], 'dll_excludes': ["MPR.dll", "api-ms-win-core-apiquery-l1-1-0.dll", "api-ms-win-core-console-l1-1-0.dll", "api-ms-win-core-delayload-l1-1-1.dll", "api-ms-win-core-errorhandling-l1-1-1.dll", "api-ms-win-core-file-l1-2-0.dll", "api-ms-win-core-handle-l1-1-0.dll", "api-ms-win-core-heap-obsolete-l1-1-0.dll", "api-ms-win-core-libraryloader-l1-1-1.dll", "api-ms-win-core-localization-l1-2-0.dll", "api-ms-win-core-processenvironment-l1-2-0.dll", "api-ms-win-core-processthreads-l1-1-1.dll", "api-ms-win-core-profile-l1-1-0.dll", "api-ms-win-core-registry-l1-1-0.dll", "api-ms-win-core-synch-l1-2-0.dll", "api-ms-win-core-sysinfo-l1-2-0.dll", "api-ms-win-security-base-l1-2-0.dll", "api-ms-win-core-heap-l1-2-0.dll", "api-ms-win-core-interlocked-l1-2-0.dll", "api-ms-win-core-localization-obsolete-l1-1-0.dll", "api-ms-win-core-string-l1-1-0.dll", "api-ms-win-core-string-obsolete-l1-1-0.dll", "WLDAP32.dll", "MSVCP90.dll", "CRYPT32.dll", "mfc90.dll"],
'skip_archive': True 'skip_archive': True
}, },
}, },

View File

@@ -129,24 +129,7 @@ class URLStream(object):
self.stream.volume = float(volume) self.stream.volume = float(volume)
self.stream.play() self.stream.play()
log.debug("played") log.debug("played")
call_threaded(self.delete_when_done) # call_threaded(self.delete_when_done)
def is_playable(self, url,play=False,volume=1.0):
try:
log.debug("Checking URL playability...")
self.prepare(url)
if self.prepared == True:
stream=sound_lib.stream.URLStream(url=self.url)
if play:
return self.play(stream=stream,volume=volume,announce=False)
return True
except:
return False
def delete_when_done(self):
while hasattr(self,'stream') and self.stream.is_playing:
pass
del self.stream
def stop_audio(self,delete=False): def stop_audio(self,delete=False):
if hasattr(self, "stream"): if hasattr(self, "stream"):
@@ -156,8 +139,8 @@ class URLStream(object):
log.debug("Stopped audio stream.") log.debug("Stopped audio stream.")
except: except:
log.exception("Exception while stopping stream.") log.exception("Exception while stopping stream.")
# if delete: if delete:
# del self.stream del self.stream
log.debug("Deleted audio stream.") log.debug("Deleted audio stream.")
return True return True
else: else:

View File

@@ -21,15 +21,24 @@ class timelinesStreamer(TwythonStreamer):
self.lists = self.session.lists self.lists = self.session.lists
def on_error(self, status_code, data): def on_error(self, status_code, data):
log.debug("%s: %s" % (status_code, data)) log.error("error in stream: %s: %s" % (status_code, data))
# pub.sendMessage("stream-error", session=self.session.session_id)
def on_timeout(self, *args, **kwargs): def on_timeout(self, *args, **kwargs):
log.debug("Twitter timeout Error") log.error("Twitter timeout Error")
pub.sendMessage("stream-error") # pub.sendMessage("stream-error", session=self.session.session_id)
def check_tls(self, data): def check_tls(self, data):
for i in self.session.settings["other_buffers"]["timelines"]: for i in self.session.settings["other_buffers"]["timelines"]:
if data["user"]["screen_name"] == i: if data["user"]["screen_name"] == i:
if utils.find_item(data["id"], self.session.db["%s-timeline" % (i,)]) != None:
log.error("duplicated tweet. Ignoring it...")
return
try:
data_ = self.session.check_quoted_status(data)
data = data_
except AttributeError:
pass
if self.session.settings["general"]["reverse_timelines"] == False: self.session.db["%s-timeline" % (i,)].append(data) if self.session.settings["general"]["reverse_timelines"] == False: self.session.db["%s-timeline" % (i,)].append(data)
else: self.session.db["%s-timeline" % (i,)].insert(0, data) else: self.session.db["%s-timeline" % (i,)].insert(0, data)
pub.sendMessage("item-in-timeline", data= data, user= self.session.db["user_name"], who= i) pub.sendMessage("item-in-timeline", data= data, user= self.session.db["user_name"], who= i)
@@ -37,13 +46,14 @@ class timelinesStreamer(TwythonStreamer):
try: try:
i.users.index(data["user"]["id"]) i.users.index(data["user"]["id"])
usr = data["in_reply_to_user_id"] usr = data["in_reply_to_user_id"]
if (usr != None and usr in self.friends) or data.has_key("retweeted_status"): if (usr != None or usr in self.friends) or data.has_key("retweeted_status"):
data = self.session.check_quoted_status(data)
if self.session.settings["general"]["reverse_timelines"] == False: self.session.db["%s" % (i.name,)].append(data) if self.session.settings["general"]["reverse_timelines"] == False: self.session.db["%s" % (i.name,)].append(data)
else: self.session.db["%s" % (i,)].insert(0, data) else: self.session.db["%s" % (i.name,)].insert(0, data)
pub.sendMessage("item-in-list", data=data, user=self.session.db["user_name"], where=i.name) pub.sendMessage("item-in-list", data=data, user=self.session.db["user_name"], where=i.name)
elif usr == None: elif usr == None:
if self.session.settings["general"]["reverse_timelines"] == False: self.session.db["%s" % (i.name,)].append(data) if self.session.settings["general"]["reverse_timelines"] == False: self.session.db["%s" % (i.name,)].append(data)
else: self.session.db["%s" % (i,)].insert(0, data) else: self.session.db["%s" % (i.name,)].insert(0, data)
pub.sendMessage("item-in-list", data=data, user=self.session.db["user_name"], where=i.name) pub.sendMessage("item-in-list", data=data, user=self.session.db["user_name"], where=i.name)
except ValueError: except ValueError:
pass pass

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