Putting all the code from the current master branch of TWBlue

This commit is contained in:
Manuel Cortez 2014-10-27 16:29:04 -06:00
parent 58c82e5486
commit 1af4a8b291
284 changed files with 58760 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.pyc
*~
src/build/
src/dist/
src/config/
src/config1/
src/config2/
src/config3/
src/dropbox/
src/logs/
src/documentation/
src/oggenc2.exe
src/bootstrap.exe
src/Microsoft.VC90.CRT
src/Microsoft.VC90.MFC
src/launcher.bat

33
contributors.txt Normal file
View File

@ -0,0 +1,33 @@
Manuel E. Cortéz
Johana Hidrobo
Marcelo Sosa
Isabel del Castillo
José Manuel Delicado
David Picón
Vicente Llopis
Javier Dorado
Guillem León
Marco Serrano
Bryner Villalobos
Robert Spangler
Paweł Masarczyk
Odenilton Júnior Santos
Burak
Alexander Jaszyn
Salva Doménech
Juan Carlos Rivilla
Sukil Etxenike
Javier Currás
Alba Quinteiro
Jani Kinnunen
Mohammed Al Shara
Robert Osztolykan
Rémy Ruiz
Jorge Almeida
Jose Maria ortiz silva
Pablo Perello
Michael Lau
Paris-Apps
Salvadora Melguizo
Holly Scott-Gardner
Anibal Hernández

88
src/Conf.defaults Normal file
View File

@ -0,0 +1,88 @@
[twitter]
user_key = string(default="")
user_secret = string(default="")
user_name = string(default="")
ignored_clients = list(default=list())
[general]
language = string(default="system")
relative_times = boolean(default=True)
hide_gui = boolean(default=False)
voice_enabled = boolean(default=False)
max_api_calls = integer(default=1)
max_tweets_per_call = integer(default=100)
reverse_timelines = boolean(default=False)
time_to_check_streams = integer(default=30)
announce_stream_status = boolean(default=True)
[sound]
volume = float(default=1.0)
input_device = string(default="Default")
output_device = string(default="Default")
current_soundpack = string(default="default")
global_mute = boolean(default=False)
sndup_api_key = string(default="")
[other_buffers]
show_friends = boolean(default=True)
show_followers = boolean(default=True)
show_favourites = boolean(default=True)
show_events = boolean(default=True)
show_blocks = boolean(default=False)
show_muted_users = boolean(default=False)
timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list())
[mysc]
spelling_language = string(default="")
[services]
dropbox_token=string(default="")
[keymap]
up = string(default="control+win+up")
down = string(default="control+win+down")
left = string(default="control+win+left")
right = string(default="control+win+right")
conversation_up = string(default="control+win+shift+up")
conversation_down = string(default="control+win+shift+down")
show_hide = string(default="control+win+m")
compose = string(default="control+win+n")
reply = string(default="control+win+r")
retweet = string(default="control+win+shift+r")
dm = string(default="control+win+d")
fav = string(default="alt+win+f")
unfav = string(default="alt+shift+win+f")
action = string(default="control+win+s")
details = string(default="control+win+alt+n")
view = string(default="control+win+v")
close = string(default="control+win+f4")
open_timeline = string(default="control+win+i")
delete_buffer = string(default="control+win+shift+i")
url = string(default="control+win+return")
audio = string(default="control+win+alt+return")
volume_up = string(default="control+win+alt+up")
volume_down = string(default="control+win+alt+down")
go_home = string(default="control+win+home")
go_end = string(default="control+win+end")
go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="control+win+delete")
clear_list = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+c")
add_to_list = string(default="control+win+a")
remove_from_list = string(default="control+win+shift+a")
toggle_mute = string(default="control+win+shift+m")
toggle_global_mute = string(default="alt+win+m")
toggle_autoread = string(default="control+win+e")
search = string(default="control+win+-")
edit_keystrokes = string(default="control+win+k")
view_user_lists = string(default="control+win+l")
get_more_items = string(default="alt+win+pageup")
connect_streams = string(default="win+alt+c")

View File

@ -0,0 +1,29 @@
import ctypes
import os
import types
from platform_utils import paths
def load_library(libname):
if paths.is_frozen():
libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname)
else:
libfile = os.path.join(paths.module_path(), 'lib', libname)
return ctypes.windll[libfile]
def get_output_classes():
import outputs
module_type = types.ModuleType
classes = [m.output_class for m in outputs.__dict__.itervalues() if type(m) == module_type and hasattr(m, 'output_class')]
return sorted(classes, key=lambda c: c.priority)
def find_datafiles():
import os
import platform
from glob import glob
import accessible_output2
if platform.system() != 'Windows':
return []
path = os.path.join(accessible_output2.__path__[0], 'lib', '*.dll')
results = glob(path)
dest_dir = os.path.join('accessible_output2', 'lib')
return [(dest_dir, results)]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -0,0 +1,42 @@
import platform
import accessible_output2
from base import Output, OutputError
class Auto(Output):
def __init__(self):
if platform.system() == "Darwin":
import voiceover
self.outputs = [voiceover.VoiceOver()]
elif platform.system() == "Linux":
import speechDispatcher
self.outputs = [speechDispatcher.SpeechDispatcher()]
elif platform.system() == "Windows":
output_classes = accessible_output2.get_output_classes()
self.outputs = []
for output in output_classes:
try:
self.outputs.append(output())
except OutputError:
pass
def get_first_available_output(self):
for output in self.outputs:
if output.is_active():
return output
return None
def speak(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.speak(*args, **kwargs)
def braille(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.braille(*args, **kwargs)
def output(self, *args, **kwargs):
output = self.get_first_available_output()
if output:
output.speak(*args, **kwargs)

View File

@ -0,0 +1,31 @@
from accessible_output2 import load_library
import platform
class OutputError(Exception):
pass
class Output(object):
name = "Unnamed Output" #The name of this output
lib32 = None #name of 32-bit lib
lib64 = None #name of 64-bit lib
priority = 100 #Where to sort in the list of available outputs for automaticly speaking
def __init__(self):
is_32bit = platform.architecture()[0] == "32bit"
if self.lib32 and is_32bit:
self.lib = load_library(self.lib32)
elif self.lib64:
self.lib = load_library(self.lib64)
def output(self, text, **options):
output = False
if hasattr(self, 'speak') and callable(self.speak):
self.speak(text, **options)
output = True
if hasattr(self, 'braille') and callable(self.braille):
self.braille(text, **options)
output = True
if not output:
raise RuntimeError("Output %r does not have any method defined to output" % self)

View File

@ -0,0 +1,27 @@
import os
from base import Output
class Dolphin (Output):
"""Supports dolphin products."""
name = 'Dolphin'
lib32 = 'dolapi.dll'
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
#If we don't call this, the API won't let us speak.
if self.is_active():
self.lib.DolAccess_Command(unicode(text), (len(text)*2)+2, 1)
def silence(self):
self.lib.DolAccess_Action(141)
def is_active(self):
try:
return self.lib.DolAccess_GetSystem() in (1, 4, 8)
except:
return False
output_class = Dolphin

View File

@ -0,0 +1,33 @@
import win32gui
from libloader.com import load_com
import pywintypes
from base import Output, OutputError
class Jaws (Output):
"""Output supporting the Jaws for Windows screen reader."""
name = 'jaws'
def __init__(self, *args, **kwargs):
super (Jaws, self).__init__(*args, **kwargs)
try:
self.object = load_com("FreedomSci.JawsApi", "jfwapi")
except pywintypes.com_error:
raise OutputError
def braille(self, text, **options):
# HACK: replace " with ', Jaws doesn't seem to understand escaping them with \
text = text.replace('"', "'")
self.object.RunFunction("BrailleString(\"%s\")" % text)
def speak(self, text, interrupt=False):
self.object.SayString(' %s' % text, interrupt)
def is_active(self):
try:
return self.object.SayString('',0) == True or win32gui.FindWindow("JFWUI2", "JAWS") != 0
except:
return False
output_class = Jaws

View File

@ -0,0 +1,31 @@
import os
import platform
from platform_utils import paths
from libloader import load_library
from base import Output
class NVDA(Output):
"""Supports The NVDA screen reader"""
name = "NVDA"
lib32 = 'nvdaControllerClient32.dll'
lib64 = 'nvdaControllerClient64.dll'
def is_active(self):
try:
return self.lib.nvdaController_testIfRunning() == 0
except:
return False
def braille(self, text, **options):
self.lib.nvdaController_brailleMessage(unicode(text))
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.nvdaController_speakText(unicode(text))
def silence(self):
self.lib.nvdaController_cancelSpeech()
output_class = NVDA

View File

@ -0,0 +1,19 @@
import ctypes
from base import Output
class PCTalker(Output):
lib32 = 'pctkusr.dll'
lib64 = 'pctkusr64.dll'
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.lib.PCTKPRead(text.encode('cp932', 'replace'))
def silence(self):
self.lib.PCTKVReset()
def is_active(self):
return self.lib.PCTKStatus() != 0
output_class = PCTalker

View File

@ -0,0 +1,141 @@
from libloader.com import load_com
from base import Output
import logging
log = logging.getLogger(__name__)
class Sapi4(Output):
name = 'sapi4'
priority = 102
def __init__(self):
sapi4 = load_com("{EEE78591-FE22-11D0-8BEF-0060081841DE}")
self._voiceNo = sapi4.Find(0)
sapi4.Select(self._voiceNo)
sapi4.Speak(" ")
self.__object = sapi4
self._voice_list = self._available_voices()
def _set_capabilities(self):
sapi4 = self.__object
try:
sapi4.Pitch = sapi4.Pitch
self._min_pitch = sapi4.MinPitch
self._max_pitch = sapi4.MaxPitch
self._has_pitch = True
except:
self._min_pitch = 0
self._max_pitch = 0
self._has_pitch = False
try:
sapi4.Speed = sapi4.Speed
self._min_rate = sapi4.MinSpeed
self._max_rate = sapi4.MaxSpeed
self._has_rate = True
except:
self._min_rate = 0
self._max_rate = 0
self._has_rate = False
try:
sapi4.VolumeLeft = sapi4.VolumeLeft
self._min_volume = sapi4.MinVolumeLeft
self._max_volume = sapi4.MaxVolumeLeft
self._has_volume = True
except:
self._min_volume = 0
self._max_volume = 0
self._has_volume = False
def _available_voices(self):
voice_list = []
for voice_no in range(1, self.__object.CountEngines):
voice_list.append(self.__object.ModeName(voice_no))
return voice_list
@property
def available_voices(self):
return self._voice_list
def list_voices(self):
return self.available_voices
def get_voice(self):
return self.__object.ModeName(self._voice_no)
def set_voice(self, value):
self._voice_no = self.list_voices().index(value) + 1
self.__object.Select(self._voice_no)
self.silence()
self.__object.Speak(" ")
self._set_capabilities()
def get_pitch(self):
if self.has_pitch:
return self.__object.Pitch
def set_pitch(self, value):
if self.has_pitch:
self.__object.Pitch = value
def get_rate(self):
if self.has_rate:
return self.__object.Speed
def set_rate(self, value):
if self.has_rate:
self.__object.Speed = value
def get_volume(self):
if self.has_volume:
return self.__object.VolumeLeft
def set_volume(self, value):
if self.has_volume:
self.__object.VolumeLeft = value
@property
def has_pitch(self):
return self._has_pitch
@property
def has_rate(self):
return self._has_rate
@property
def has_volume(self):
return self._has_volume
@property
def min_pitch(self):
return self._min_pitch
@property
def max_pitch(self):
return self._max_pitch
@property
def min_rate(self):
return self._min_rate
@property
def max_rate(self):
return self._max_rate
@property
def min_volume(self):
return self._min_volume
@property
def max_volume(self):
return self._max_volume
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
self.__object.Speak(text)
def silence(self):
self.__object.AudioReset()
output_class = Sapi4

View File

@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
import config
from collections import OrderedDict
from libloader.com import load_com
from base import Output, OutputError
import pywintypes
import logging
log = logging.getLogger(__name__)
class SAPI5(Output):
has_volume = True
has_rate = True
has_pitch = True
min_pitch = -10
max_pitch = 10
min_rate = -10
max_rate = 10
min_volume = 0
max_volume = 100
name = "sapi5"
priority = 101
def __init__(self):
if config.main["general"]["voice_enabled"] == False: raise OutputError
try:
self.object = load_com("SAPI.SPVoice")
self._voices = self._available_voices()
except pywintypes.com_error:
raise OutputError
self._pitch = 0
def _available_voices(self):
_voices = OrderedDict()
for v in self.object.GetVoices():
_voices[v.GetDescription()] = v
return _voices
def list_voices(self):
return self.available_voices.keys()
def get_voice(self):
return self.object.Voice.GetDescription()
def set_voice(self, value):
log.debug("Setting SAPI5 voice to \"%s\"" % value)
self.object.Voice = self.available_voices[value]
# For some reason SAPI5 does not reset audio after changing the voice
# By setting the audio device after changing voices seems to fix this
# This was noted from information at:
# http://lists.nvaccess.org/pipermail/nvda-dev/2011-November/022464.html
self.object.AudioOutput = self.object.AudioOutput
def get_pitch(self):
return self._pitch
def set_pitch(self, value):
log.debug("Setting pitch to %d" % value)
self._pitch = value
def get_rate(self):
return self.object.Rate
def set_rate(self, value):
log.debug("Setting rate to %d" % value)
self.object.Rate = value
def get_volume(self):
return self.object.Volume
def set_volume(self, value):
self.object.Volume = value
def speak(self, text, interrupt=False):
if interrupt:
self.silence()
# We need to do the pitch in XML here
textOutput = "<pitch absmiddle=\"%d\">%s</pitch>" % (round(self._pitch), text.replace("<", "&lt;"))
self.object.Speak(textOutput, 1|8)
def silence(self):
self.object.Speak("", 3)
def is_active(self):
if self.object:
return True
return False
output_class = SAPI5

View File

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

@ -0,0 +1,18 @@
# 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

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

View File

@ -0,0 +1,23 @@
from base import Output
class SystemAccess (Output):
"""Supports System Access and System Access Mobile"""
name = "System Access"
lib32 = 'saapi32.dll'
priority = 99
def braille(self, text, **options):
self.lib.SA_BrlShowTextW(unicode(text))
def speak(self, text, interrupt=False):
if self.is_active():
self.dll.SA_SayW(unicode(text))
def is_active(self):
try:
return self.dll.SA_IsRunning()
except:
return False
output_class = SystemAccess

View File

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

View File

@ -0,0 +1,32 @@
import win32gui
from libloader.com import load_com
from base import Output, OutputError
import pywintypes
class WindowEyes (Output):
"""Speech output supporting the WindowEyes screen reader"""
name = 'Window-Eyes'
def __init__(self, *args, **kwargs):
super(WindowEyes, self).__init__(*args, **kwargs)
try:
self.object = load_com("gwspeak.speak")
except pywintypes.com_error:
raise OutputError
def speak(self, text, interrupt=0):
if interrupt:
self.silence()
self.object.SpeakString(text)
def silence (self):
self.object.Silence()
def is_active(self):
try:
return win32gui.FindWindow("GWMExternalControl", "External Control") != 0
except:
return False
output_class = WindowEyes

20
src/application.py Normal file
View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
name = 'TW Blue'
snapshot = False
if snapshot == False:
version = "0.47"
update_url = 'http://twblue.com.mx/updates/tw_blue.json'
else:
version = "4"
update_url = 'http://twblue.com.mx/updates/snapshots.json'
author = u"Manuel Cortéz"
authorEmail = "info@twblue.com.mx"
copyright = u"copyright (C) 2013-2014, Manuel cortéz"
description = u"TW Blue is an app designed to use Twitter in a simple and fast way and avoiding, as far as possible, the consumtion of excessive resources of the machine where its running. With this app youll have access to most twitter features."
translators = [u"Bryner Villalobos (English)", u"Mohammed Al Shara (Arabic)", u"Salva Doménech, Juan Carlos Rivilla(Catalan)", u"Manuel cortéz(Spanish)", u"Sukil Etxenike Arizaleta(Basque)", u"Jani Kinnunen(finnish)", u"Javier Currás, José Manuel Delicado, Alba Quinteiro(Galician)", 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.com.mx"
report_bugs_url = "http://twblue.com.mx/errores/api/soap/mantisconnect.php?wsdl"
# Tokens
app_key = '8pDLbyOW3saYnvSZ4uLFg'
app_secret = 'YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY'

View File

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

View File

@ -0,0 +1,56 @@
from audio_services import matches_url
import json
import re
import urllib
@matches_url('https://audioboom.com')
def convert_audioboom(url):
if "audioboom.com" not in url.lower():
raise TypeError('%r is not a valid URL' % url)
audio_id = url.split('.com/')[-1]
return 'https://audioboom.com/%s.mp3' % audio_id
@matches_url('http://q-audio.net')
def convert_q_audio(url):
result = re.match("^https?://q-audio.net/(i|d|download)/(?P<audio_id>[a-z0-9]+/?)$", url, re.I)
if not result or result.group("audio_id") is None:
raise TypeError('%r is not a valid URL' % url)
audio_id = result.group("audio_id")
return 'http://q-audio.net/download/%s' % audio_id
@matches_url ('http://soundcloud.com/')
def convert_soundcloud (url):
client_id = "df8113ca95c157b6c9731f54b105b473"
permalink = urllib.urlopen ('http://api.soundcloud.com/resolve.json?client_id=%s&url=%s' %(client_id, url))
if permalink.getcode () == 404:
permalink.close ()
raise TypeError('%r is not a valid URL' % url)
else:
resolved_url = permalink.geturl ()
permalink.close ()
track_url = urllib.urlopen (resolved_url)
track_data = json.loads (track_url.read ())
track_url.close ()
if track_data ['streamable']:
return track_data ['stream_url'] + "?client_id=%s" %client_id
else:
raise TypeError('%r is not streamable' % url)
@matches_url('http://twup.me')
def convert_twup(url):
result = re.match("^http://twup.me/(?P<audio_id>[A-Za-z0-9]+/?)$", url, re.I)
if not result or result.group("audio_id") is None:
raise TypeError('%r is not a valid URL' % url)
audio_id = result.group("audio_id")
return 'http://twup.me/%s' % audio_id
@matches_url('http://sndup.net')
def convert_sndup(url):
result = re.match("^http://sndup.net/(?P<audio_id>[a-z0-9]+/?)(|d|l|a)/?$", url, re.I)
if not result or result.group("audio_id") is None:
raise TypeError('%r is not a valid URL' % url)
audio_id = result.group("audio_id")
return 'http://sndup.net/%s/a' % audio_id
def convert_generic_audio(url):
return url

4966
src/cacert.pem Normal file

File diff suppressed because it is too large Load Diff

14
src/commandline.py Normal file
View File

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
import argparse
import paths
parser = argparse.ArgumentParser(description="TW Blue command line launcher")
group = parser.add_mutually_exclusive_group()
group.add_argument("-p", "--portable", help="Use TW Blue as a portable aplication", action="store_true", default=True)
group.add_argument("-i", "--installed", help="Use TW Blue as an installed application. Config files will be saved on the user data directory", action="store_true")
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where TW Blue saves the data files")
args = parser.parse_args()
if args.installed == True: paths.mode = "installed"
elif args.portable == True:
paths.mode = "portable"
if args.directory != None: paths.directory = args.directory

16
src/config.py Normal file
View File

@ -0,0 +1,16 @@
# -*- coding: cp1252 -*-
from config_utils import Configuration, ConfigurationResetException
import paths
MAINFILE = "session.conf"
MAINSPEC = "Conf.defaults"
main = None
def setup ():
global main
try:
main = Configuration(paths.config_path(MAINFILE), paths.app_path(MAINSPEC))
except ConfigurationResetException:
pass
# return main

50
src/config_utils.py Normal file
View File

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from UserDict import UserDict
from configobj import ConfigObj, ParseError
from validate import Validator, VdtValueError
import os
"""We're using the configobj python package
from http://www.voidspace.org.uk/python/configobj.html """
class ConfigurationResetException(Exception):
pass
class Configuration (UserDict):
def __init__ (self, file=None, spec=None, *args, **kwargs):
self.file = file
self.spec = spec
self.validator = Validator()
self.setup_config(file=file, spec=spec)
self.validated = self.config.validate(self.validator, copy=True)
if self.validated:
self.write()
UserDict.__init__(self, self.config)
def setup_config (self, file, spec):
#The default way -- load from a file
spec = ConfigObj(spec, list_values=False, encoding="utf-8")
try:
self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True, encoding="utf-8")
except ParseError:
os.remove(file)
self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True)
raise ConfigurationResetException
def __getitem__ (self, *args, **kwargs):
return dict(self.config).__getitem__(*args, **kwargs)
def __setitem__ (self, *args, **kwargs):
self.config.__setitem__(*args, **kwargs)
UserDict.__setitem__(self, *args, **kwargs)
def write (self):
if hasattr(self.config, 'write'):
self.config.write()
class SessionConfiguration (Configuration):
def setup_config (self, file, spec):
#No infile required.
spec = ConfigObj(spec, list_values=False)
self.config = ConfigObj(configspec=spec, stringify=True)

5
src/db.py Normal file
View File

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

View File

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

View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
import threading
import time
import os
import exceptions
import wx
import dropbox
import config
from mysc import event
from utils import *
from dropbox.rest import ErrorResponse
from StringIO import StringIO
class UnauthorisedError(exceptions.Exception):
def __init__(self, *args, **kwargs):
super(UnauthorisedError, self).__init__(*args, **kwargs)
class newChunkedUploader(dropbox.client.ChunkedUploader):
def __init__(self, client, file_obj, length, callback):
super(newChunkedUploader, self).__init__(client, file_obj, length)
self.progress_callback = callback
def upload_chunked(self, chunk_size = 4 * 1024 * 1024):
while self.offset < self.target_length:
next_chunk_size = min(chunk_size, self.target_length - self.offset)
if self.last_block == None:
self.last_block = self.file_obj.read(next_chunk_size)
try:
(self.offset, self.upload_id) = self.client.upload_chunk(
StringIO(self.last_block), next_chunk_size, self.offset, self.upload_id)
self.last_block = None
if callable(self.progress_callback): self.progress_callback(self.offset)
except ErrorResponse as e:
reply = e.body
if "offset" in reply and reply['offset'] != 0:
if reply['offset'] > self.offset:
self.last_block = None
self.offset = reply['offset']
class dropboxLogin(object):
def __init__(self):
self.logged = False
self.app_key = "c8ikm0gexqvovol"
self.app_secret = "gvvi6fzfecooast"
def get_url(self):
self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect(self.app_key, self.app_secret)
return self.flow.start()
def authorise(self, code):
access_token, user_id = self.flow.finish(code)
config.main["services"]["dropbox_token"] = access_token
self.logged = True
class dropboxUploader(object):
def __init__(self, filename, completed_callback, wxDialog, short_url=False):
if config.main["services"]["dropbox_token"] != "":
self.client = dropbox.client.DropboxClient(config.main["services"]["dropbox_token"])
else:
raise UnauthorisedError("You need authorise TWBlue")
self.filename = filename
self.short_url = short_url
self.wxDialog = wxDialog
self.file = open(self.filename, "rb")
self.file_size = os.path.getsize(self.filename)
self.uploader = newChunkedUploader(client=self.client, file_obj=self.file, length=self.file_size, callback=self.process)
self.start_time = None
self.completed_callback = completed_callback
self.background_thread = None
self.current = 0
self.transfer_rate = 0
def elapsed_time(self):
if not self.start_time:
return 0
return time.time() - self.start_time
def perform_transfer(self):
self.start_time = time.time()
while self.uploader.offset < self.file_size:
self.uploader.upload_chunked(self.file_size/100)
self.transfer_completed()
def process(self, offset):
progress = {}
self.current = offset
progress["total"] = self.file_size
progress["current"] = self.current
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
self.transfer_rate = progress["current"] / self.elapsed_time()
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
if self.transfer_rate:
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
else:
progress["eta"] = 0
info = event.event(event.EVT_OBJECT, 1)
info.SetItem(progress)
wx.PostEvent(self.wxDialog, info)
def perform_threaded(self):
self.background_thread = threading.Thread(target=self.perform_transfer)
self.background_thread.daemon = True
self.background_thread.start()
def transfer_completed(self):
self.uploader.finish(os.path.basename(self.filename))
if callable(self.completed_callback):
self.completed_callback()
def get_url(self):
original = "%s" % (self.client.share(os.path.basename(self.filename), False)["url"])
return original.replace("dl=0", "dl=1")

View File

@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import output
import tempfile
import sound
import os
import config
from mysc.thread_utils import call_threaded
import sound_lib
class audioDialog(wx.Dialog):
def __init__(self, parent):
self.parent = parent
wx.Dialog.__init__(self, None, -1, _(u"Attach audio"))
self.file = None
self.recorded = False
self.recording = None
self.playing = None
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.play = wx.Button(panel, -1, _(u"Play"))
self.play.Bind(wx.EVT_BUTTON, self.onPlay)
self.play.Disable()
self.pause = wx.Button(panel, -1, _(u"Pause"))
self.pause.Bind(wx.EVT_BUTTON, self.onPause)
self.pause.Disable()
self.record = wx.Button(panel, -1, _(u"Record"))
self.record.Bind(wx.EVT_BUTTON, self.onRecord)
self.record.SetFocus()
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file"))
self.attach_exists.Bind(wx.EVT_BUTTON, self.onAttach)
self.discard = wx.Button(panel, -1, _(u"Discard"))
self.discard.Bind(wx.EVT_BUTTON, self.onDiscard)
self.discard.Disable()
label = wx.StaticText(panel, -1, _(u"Upload to"))
self.services = wx.ComboBox(panel, -1, choices=self.get_available_services(), value=self.get_available_services()[0], style=wx.CB_READONLY)
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
servicesBox.Add(label)
servicesBox.Add(self.services)
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
self.attach.Disable()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
sizer.Add(self.play)
sizer.Add(self.pause)
sizer.Add(self.record)
sizer.Add(self.attach_exists)
sizer.Add(self.discard)
sizer.Add(self.attach)
def get_available_services(self):
services = []
if config.main["services"]["dropbox_token"] != "":
services.append("Dropbox")
services.append("SNDUp")
return services
def onPause(self, ev):
if self.pause.GetLabel() == _(u"Pause"):
self.recording.pause()
self.pause.SetLabel(_(u"Resume"))
elif self.pause.GetLabel() == _(u"Resume"):
self.recording.play()
self.pause.SetLabel(_(U"Pause"))
def onRecord(self, ev):
if self.recording != None:
self.stop_recording()
self.pause.Disable()
else:
self.start_recording()
self.pause.Enable()
def start_recording(self):
self.attach_exists.Disable()
self.file = tempfile.mktemp(suffix='.wav')
self.recording = sound.recording(self.file)
self.recording.play()
self.record.SetLabel(_(u"Stop recording"))
output.speak(_(u"Recording"))
def stop_recording(self):
self.recording.stop()
self.recording.free()
output.speak(_(u"Stopped"))
self.recorded = True
self.record.SetLabel(_(u"Record"))
self.file_attached()
def file_attached(self):
self.pause.SetLabel(_(u"Pause"))
self.record.Disable()
self.play.Enable()
self.discard.Enable()
self.attach_exists.Disable()
self.attach.Enable()
self.play.SetFocus()
def onDiscard(self, evt):
evt.Skip()
if self.playing:
self._stop()
if self.recording != None:
self.attach.Disable()
self.play.Disable()
self.file = None
self.record.Enable()
self.attach_exists.Enable()
self.record.SetFocus()
self.discard.Disable()
self.recording = None
output.speak(_(u"Discarded"))
def onPlay(self, evt):
evt.Skip()
if not self.playing:
call_threaded(self._play)
else:
self._stop()
def _play(self):
output.speak(_(u"Playing..."))
# try:
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
self.playing.play()
self.play.SetLabel(_(u"Stop"))
try:
while self.playing.is_playing:
pass
self.play.SetLabel(_(u"Play"))
self.playing.free()
self.playing = None
except:
pass
def _stop(self):
output.speak(_(u"Stopped"))
self.playing.stop()
self.playing.free()
self.play.SetLabel(_(u"Play"))
self.playing = None
def postprocess(self):
if self.file.lower().endswith('.wav'):
output.speak(_(u"Recoding audio..."))
sound.recode_audio(self.file)
self.wav_file = self.file
self.file = '%s.ogg' % self.file[:-4]
def cleanup(self):
if self.playing and self.playing.is_playing:
self.playing.stop()
if self.recording != None:
if self.recording.is_playing:
self.recording.stop()
try:
self.recording.free()
except:
pass
os.remove(self.file)
if hasattr(self, 'wav_file'):
os.remove(self.wav_file)
del(self.wav_file)
if hasattr(self, 'wav_file') and os.path.exists(self.file):
os.remove(self.file)
def onAttach(self, ev):
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return
self.file = openFileDialog.GetPath()
self.file_attached()
ev.Skip()

View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
import pycurl
import sys
import threading
import time
import json
import wx
from mysc import event
from utils import *
#__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
class Transfer(object):
def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, wxDialog=None, *args, **kwargs):
self.url = url
self.filename = filename
self.curl = pycurl.Curl()
self.start_time = None
self.completed_callback = completed_callback
self.background_thread = None
self.transfer_rate = 0
self.curl.setopt(self.curl.PROGRESSFUNCTION, self.progress_callback)
self.curl.setopt(self.curl.URL, url)
self.curl.setopt(self.curl.NOPROGRESS, 0)
self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0)
self.curl.setopt(self.curl.FOLLOWLOCATION, int(follow_location))
self.curl.setopt(self.curl.VERBOSE, int(verbose))
self.wxDialog = wxDialog
super(Transfer, self).__init__(*args, **kwargs)
def elapsed_time(self):
if not self.start_time:
return 0
return time.time() - self.start_time
def progress_callback(self, down_total, down_current, up_total, up_current):
progress = {}
progress["total"] = up_total
progress["current"] = up_current
# else:
# print "Killed function"
# return
if progress["current"] == 0:
progress["percent"] = 0
self.transfer_rate = 0
else:
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
self.transfer_rate = progress["current"] / self.elapsed_time()
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
if self.transfer_rate:
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
else:
progress["eta"] = 0
info = event.event(event.EVT_OBJECT, 1)
info.SetItem(progress)
wx.PostEvent(self.wxDialog, info)
def perform_transfer(self):
self.start_time = time.time()
self.curl.perform()
self.curl.close()
wx.CallAfter(self.complete_transfer)
def perform_threaded(self):
self.background_thread = threading.Thread(target=self.perform_transfer)
self.background_thread.daemon = True
self.background_thread.start()
def complete_transfer(self):
if callable(self.completed_callback):
self.curl.close()
self.completed_callback()
class Upload(Transfer):
def __init__(self, field=None, filename=None, *args, **kwargs):
super(Upload, self).__init__(filename=filename, *args, **kwargs)
self.response = dict()
self.curl.setopt(self.curl.POST, 1)
if isinstance(filename, unicode):
local_filename = filename.encode(sys.getfilesystemencoding())
else:
local_filename = filename
self.curl.setopt(self.curl.HTTPPOST, [(field, (self.curl.FORM_FILE, local_filename, self.curl.FORM_FILENAME, filename.encode("utf-8")))])
self.curl.setopt(self.curl.HEADERFUNCTION, self.header_callback)
self.curl.setopt(self.curl.WRITEFUNCTION, self.body_callback)
def header_callback(self, content):
self.response['header'] = content
def body_callback(self, content):
self.response['body'] = content
def get_url(self):
return json.loads(self.response['body'])['url']
class Download(Transfer):
def __init__(self, follow_location=True, *args, **kwargs):
super(Download, self).__init__(*args, **kwargs)
self.download_file = open(self.filename, 'wb')
self.curl.setopt(self.curl.WRITEFUNCTION, self.download_file.write)
def complete_transfer(self):
self.download_file.close()
super(DownloadDialog, self).complete_transfer()

View File

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
import wx
from mysc import event
from utils import *
__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
class TransferDialog(wx.Dialog):
def __init__(self, filename, *args, **kwargs):
super(TransferDialog, self).__init__(*args, **kwargs)
self.pane = wx.Panel(self)
self.progress_bar = wx.Gauge(parent=self.pane)
fileBox = wx.BoxSizer(wx.HORIZONTAL)
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
self.file.SetFocus()
fileBox.Add(fileLabel)
fileBox.Add(self.file)
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
currentAmountBox.Add(current_amount_label)
currentAmountBox.Add(self.current_amount)
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
totalSizeBox.Add(total_size_label)
totalSizeBox.Add(self.total_size)
speedBox = wx.BoxSizer(wx.HORIZONTAL)
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
speedBox.Add(speedLabel)
speedBox.Add(self.speed)
etaBox = wx.BoxSizer(wx.HORIZONTAL)
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
etaBox.Add(etaLabel)
etaBox.Add(self.eta)
# self.create_buttons()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(fileBox)
sizer.Add(currentAmountBox)
sizer.Add(totalSizeBox)
sizer.Add(speedBox)
sizer.Add(etaBox)
sizer.Add(self.progress_bar)
self.pane.SetSizerAndFit(sizer)
self.Bind(event.MyEVT_OBJECT, self.update)
def update(self, ev):
data = ev.GetItem()
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
wx.CallAfter(self.speed.SetValue, data["speed"])
if data["eta"]:
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
def create_buttons(self):
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel)
class UploadDialog(TransferDialog):
def __init__(self, filename=None, *args, **kwargs):
super(UploadDialog, self).__init__(filename=filename, *args, **kwargs)
class DownloadDialog(TransferDialog):
def __init__(self, *args, **kwargs):
super(Download, self).__init__(*args, **kwargs)

View File

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

View File

@ -0,0 +1 @@
import gui

View File

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

View File

View File

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import output
import config
import languageHandler
from enchant.checker import SpellChecker
from enchant.errors import DictNotFoundError
class spellCheckerDialog(wx.Dialog):
def __init__(self, text, dictionary):
super(spellCheckerDialog, self).__init__(None, 1)
try:
if config.main["general"]["language"] == "system": self.checker = SpellChecker()
else: self.checker = SpellChecker(languageHandler.getLanguage())
self.checker.set_text(text)
except DictNotFoundError:
wx.MessageDialog(None, _(u"A bug has happened. There are no dictionaries available for the selected language in TW Blue"), _(u"Error"), wx.ICON_ERROR).ShowModal()
self.Destroy()
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
word = wx.StaticText(panel, -1, _(u"Mis-spelled word"))
self.word = wx.TextCtrl(panel, -1)
wordBox = wx.BoxSizer(wx.HORIZONTAL)
wordBox.Add(word)
wordBox.Add(self.word)
context = wx.StaticText(panel, -1, _(u"Context"))
self.context = wx.TextCtrl(panel, -1)
contextBox = wx.BoxSizer(wx.HORIZONTAL)
contextBox.Add(context)
contextBox.Add(self.context)
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
suggestionsBox.Add(suggest)
suggestionsBox.Add(self.suggestions)
ignore = wx.Button(panel, -1, _(u"Ignore"))
self.Bind(wx.EVT_BUTTON, self.onIgnore, ignore)
ignoreAll = wx.Button(panel, -1, _(u"Ignore all"))
self.Bind(wx.EVT_BUTTON, self.onIgnoreAll, ignoreAll)
replace = wx.Button(panel, -1, _(u"Replace"))
self.Bind(wx.EVT_BUTTON, self.onReplace, replace)
replaceAll = wx.Button(panel, -1, _(u"Replace all"))
self.Bind(wx.EVT_BUTTON, self.onReplaceAll, replaceAll)
close = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(ignore)
btnBox.Add(ignoreAll)
btnBox.Add(replace)
btnBox.Add(replaceAll)
btnBox.Add(close)
sizer.Add(wordBox)
sizer.Add(contextBox)
sizer.Add(suggestionsBox)
sizer.Add(btnBox)
panel.SetSizerAndFit(sizer)
self.check()
def check(self):
try:
self.checker.next()
textToSay = _(u"Mis-spelled word: %s") % (self.checker.word,)
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
self.SetTitle(textToSay)
output.speak(textToSay)
self.word.SetValue(self.checker.word)
self.context.ChangeValue(context)
self.suggestions.Set(self.checker.suggest())
self.suggestions.SetFocus()
except StopIteration:
wx.MessageDialog(self, _(u"The spelling review has finished."), _("Finished"), style=wx.OK).ShowModal()
self.EndModal(wx.ID_OK)
except AttributeError:
pass
def onIgnore(self, ev):
self.check()
def onIgnoreAll(self, ev):
self.checker.ignore_always(word=self.checker.word)
self.check()
def onReplace(self, ev):
self.checker.replace(self.suggestions.GetStringSelection())
self.check()
def onReplaceAll(self, ev):
self.checker.replace_always(self.suggestions.GetStringSelection())
self.check()

0
src/extra/__init__.py Normal file
View File

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from translator import *
import gui

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import translator
class translateDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, title=_(u"Translate message"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
staticSource = wx.StaticText(panel, -1, _(u"Source language"))
self.source_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
self.source_lang.SetFocus()
staticDest = wx.StaticText(panel, -1, _(u"Target language"))
self.source_lang.SetSelection(0)
self.dest_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
listSizer = wx.BoxSizer(wx.HORIZONTAL)
listSizer.Add(staticSource)
listSizer.Add(self.source_lang)
listSizer.Add(staticDest)
listSizer.Add(self.dest_lang)
ok = wx.Button(panel, wx.ID_OK)
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL)
self.SetEscapeId(wx.ID_CANCEL)
def onOk(self, ev):
self.EndModal(wx.ID_OK)

View File

@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
import re
try:
import urllib2 as request
from urllib import quote
except:
from urllib import request
from urllib.parse import quote
class Translator:
string_pattern = r"\"(([^\"\\]|\\.)*)\""
match_string =re.compile(
r"\,?\["
+ string_pattern + r"\,"
+ string_pattern + r"\,"
+ string_pattern + r"\,"
+ string_pattern
+r"\]")
def __init__(self):
self.from_lang = ""
self.to_lang = ""
def translate(self, source):
json5 = self._get_json5_from_google(source)
return self._unescape(self._get_translation_from_json5(json5))
def _get_translation_from_json5(self, content):
result = ""
pos = 2
while True:
m = self.match_string.match(content, pos)
if not m:
break
result += m.group(1)
pos = m.end()
return result
def _get_json5_from_google(self, source):
escaped_source = quote(source, '')
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19'}
req = request.Request(
url="http://translate.google.com/translate_a/t?client=t&ie=UTF-8&oe=UTF-8"
+"&sl=%s&tl=%s&text=%s" % (self.from_lang, self.to_lang, escaped_source)
, headers = headers)
r = request.urlopen(req)
return r.read().decode('utf-8')
def _unescape(self, text):
return re.sub(r"\\.?", lambda x:eval('"%s"'%x.group(0)), text)
languages = {
"af": _(u"Afrikaans"),
"sq": _(u"Albanian"),
"am": _(u"Amharic"),
"ar": _(u"Arabic"),
"hy": _(u"Armenian"),
"az": _(u"Azerbaijani"),
"eu": _(u"Basque"),
"be": _(u"Belarusian"),
"bn": _(u"Bengali"),
"bh": _(u"Bihari"),
"bg": _(u"Bulgarian"),
"my": _(u"Burmese"),
"ca": _(u"Catalan"),
"chr": _(u"Cherokee"),
"zh": _(u"Chinese"),
"zh-CN": _(u"Chinese_simplified"),
"zh-TW": _(u"Chinese_traditional"),
"hr": _(u"Croatian"),
"cs": _(u"Czech"),
"da": _(u"Danish"),
"dv": _(u"Dhivehi"),
"nl": _(u"Dutch"),
"en": _(u"English"),
"eo": _(u"Esperanto"),
"et": _(u"Estonian"),
"tl": _(u"Filipino"),
"fi": _(u"Finnish"),
"fr": _(u"French"),
"gl": _(u"Galician"),
"ka": _(u"Georgian"),
"de": _(u"German"),
"el": _(u"Greek"),
"gn": _(u"Guarani"),
"gu": _(u"Gujarati"),
"iw": _(u"Hebrew"),
"hi": _(u"Hindi"),
"hu": _(u"Hungarian"),
"is": _(u"Icelandic"),
"id": _(u"Indonesian"),
"iu": _(u"Inuktitut"),
"ga": _(u"Irish"),
"it": _(u"Italian"),
"ja": _(u"Japanese"),
"kn": _(u"Kannada"),
"kk": _(u"Kazakh"),
"km": _(u"Khmer"),
"ko": _(u"Korean"),
"ku": _(u"Kurdish"),
"ky": _(u"Kyrgyz"),
"lo": _(u"Laothian"),
"lv": _(u"Latvian"),
"lt": _(u"Lithuanian"),
"mk": _(u"Macedonian"),
"ms": _(u"Malay"),
"ml": _(u"Malayalam"),
"mt": _(u"Maltese"),
"mr": _(u"Marathi"),
"mn": _(u"Mongolian"),
"ne": _(u"Nepali"),
"no": _(u"Norwegian"),
"or": _(u"Oriya"),
"ps": _(u"Pashto"),
"fa": _(u"Persian"),
"pl": _(u"Polish"),
"pt-PT": _(u"Portuguese"),
"pa": _(u"Punjabi"),
"ro": _(u"Romanian"),
"ru": _(u"Russian"),
"sa": _(u"Sanskrit"),
"sr": _(u"Serbian"),
"sd": _(u"Sindhi"),
"si": _(u"Sinhalese"),
"sk": _(u"Slovak"),
"sl": _(u"Slovenian"),
"es": _(u"Spanish"),
"sw": _(u"Swahili"),
"sv": _(u"Swedish"),
"tg": _(u"Tajik"),
"ta": _(u"Tamil"),
"tl": _(u"Tagalog"),
"te": _(u"Telugu"),
"th": _(u"Thai"),
"bo": _(u"Tibetan"),
"tr": _(u"Turkish"),
"uk": _(u"Ukrainian"),
"ur": _(u"Urdu"),
"uz": _(u"Uzbek"),
"ug": _(u"Uighur"),
"vi": _(u"Vietnamese"),
"cy": _(u"Welsh"),
"yi": _(u"Yiddish")
}
def available_languages():
l = languages.keys()
d = languages.values()
l.insert(0, '')
d.insert(0, _(u"autodetect"))
return sorted(zip(l, d))

105
src/gettext_windows.py Normal file
View File

@ -0,0 +1,105 @@
# Copyright (c) 2006, 2007, 2010 Alexander Belchenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Helper for standard gettext.py on Windows.
Module obtains user language code on Windows to use with standard
Python gettext.py library.
The module provides 2 functions: setup_env and get_language.
You may use setup_env before initializing gettext functions.
Or you can use get_language to get the list of language codes suitable
to pass them to gettext.find or gettext.translation function.
Usage example #1:
import gettext, gettext_windows
gettext_windows.setup_env()
gettext.install('myapp')
Usage example #2:
import gettext, gettext_windows
lang = gettext_windows.get_language()
translation = gettext.translation('myapp', languages=lang)
_ = translation.gettext
"""
import locale
import os
import sys
OS_WINDOWS = (sys.platform == 'win32')
def setup_env_windows(system_lang=True):
"""Check environment variables used by gettext
and setup LANG if there is none.
"""
if _get_lang_env_var() is not None:
return
lang = get_language_windows(system_lang)
if lang:
os.environ['LANGUAGE'] = ':'.join(lang)
def get_language_windows(system_lang=True):
"""Get language code based on current Windows settings.
@return: list of languages.
"""
try:
import ctypes
except ImportError:
return [locale.getdefaultlocale()[0]]
# get all locales using windows API
lcid_user = ctypes.windll.kernel32.GetUserDefaultLCID()
lcid_system = ctypes.windll.kernel32.GetSystemDefaultLCID()
if system_lang and lcid_user != lcid_system:
lcids = [lcid_user, lcid_system]
else:
lcids = [lcid_user]
return filter(None, [locale.windows_locale.get(i) for i in lcids]) or None
def setup_env_other(system_lang=True):
pass
def get_language_other(system_lang=True):
lang = _get_lang_env_var()
if lang is not None:
return lang.split(':')
return None
def _get_lang_env_var():
for i in ('LANGUAGE','LC_ALL','LC_MESSAGES','LANG'):
lang = os.environ.get(i)
if lang:
return lang
return None
if OS_WINDOWS:
setup_env = setup_env_windows
get_language = get_language_windows
else:
setup_env = setup_env_other
get_language = get_language_other

3
src/gui/__init__.py Normal file
View File

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

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from panels import accountPanel, emptyPanel
from base import *
from dm import *
from events import *
from favourites import *
from lists import *
from people import *
from tweet_searches import *
from user_searches import *

364
src/gui/buffers/base.py Normal file
View File

@ -0,0 +1,364 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import gui.dialogs
import twitter
import webbrowser
import config
import sound
import url_shortener
import logging as original_logger
import output
import platform
import datetime
from twitter import prettydate
from multiplatform_widgets import widgets
from mysc import event
from mysc.thread_utils import call_threaded
from twython import TwythonError
log = original_logger.getLogger("buffers.base")
class basePanel(wx.Panel):
def bind_events(self):
self.Bind(event.MyEVT_OBJECT, self.update)
self.Bind(event.MyEVT_DELETED, self.Remove)
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
if self.system == "Windows":
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.onFocus)
else:
self.list.list.Bind(wx.EVT_LISTBOX, self.onFocus)
def get_message(self, dialog=False):
if dialog == False: return " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db))
else:
list = self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)
return " ".join(list[1:-2])
def create_list(self):
self.list = widgets.list(self, _(u"User"), _(u"Text"), _(u"Date"), _(u"Client"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
if self.system == "Windows":
self.list.set_windows_size(0, 30)
self.list.set_windows_size(1, 160)
self.list.set_windows_size(2, 55)
self.list.set_windows_size(3, 42)
self.list.set_size()
def __init__(self, parent, window, name_buffer, function=None, argumento=None, sound="", timeline=False):
if timeline == False:
self.type = "buffer"
elif timeline == True:
self.type = "timeline"
self.db = window.db
self.twitter = window.twitter
self.name_buffer = name_buffer
self.function = function
self.argumento = argumento
self.sound = sound
self.parent = window
self.compose_function = twitter.compose.compose_tweet
self.system = platform.system()
wx.Panel.__init__(self, parent)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.create_list()
self.btn = wx.Button(self, -1, _(u"Tweet"))
self.btn.Bind(wx.EVT_BUTTON, self.post_status)
self.retweetBtn = wx.Button(self, -1, _(u"Retweet"))
self.retweetBtn.Bind(wx.EVT_BUTTON, self.onRetweet)
self.responseBtn = wx.Button(self, -1, _(u"Reply"))
self.responseBtn.Bind(wx.EVT_BUTTON, self.onResponse)
self.dmBtn = wx.Button(self, -1, _(u"Direct message"))
self.dmBtn.Bind(wx.EVT_BUTTON, self.onDm)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.Add(self.btn, 0, wx.ALL, 5)
btnSizer.Add(self.retweetBtn, 0, wx.ALL, 5)
btnSizer.Add(self.responseBtn, 0, wx.ALL, 5)
btnSizer.Add(self.dmBtn, 0, wx.ALL, 5)
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
self.sizer.Add(self.list.list, 0, wx.ALL, 5)
self.bind_events()
self.SetSizer(self.sizer)
def remove_buffer(self):
if self.type == "timeline":
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this timeline?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
names = config.main["other_buffers"]["timelines"]
user = self.name_buffer
log.info(u"Deleting %s's timeline" % user)
if user in names:
names.remove(user)
self.db.settings.pop(user)
pos = self.db.settings["buffers"].index(user)
self.db.settings["buffers"].remove(user)
return pos
elif self.type == "buffer":
output.speak(_(u"This buffer is not a timeline; it can't be deleted."))
return None
def remove_invalid_buffer(self):
if self.type == "timeline":
names = config.main["other_buffers"]["timelines"]
user = self.name_buffer
log.info(u"Deleting %s's timeline" % user)
if user in names:
names.remove(user)
self.db.settings.pop(user)
pos = self.db.settings["buffers"].index(user)
self.db.settings["buffers"].remove(user)
return pos
def Remove(self, ev):
# try:
self.list.remove_item(ev.GetItem())
# except:
# log.error(u"Cannot delete the %s item from list " % str(ev.GetItem()))
def destroy_status(self, ev):
index = self.list.get_selected()
try:
self.twitter.twitter.destroy_status(id=self.db.settings[self.name_buffer][index]["id"])
self.db.settings[self.name_buffer].pop(index)
self.list.remove_item(index)
if index > 0:
self.list.select_item(index-1)
except:
sound.player.play("error.ogg")
def onFocus(self, ev):
if self.db.settings[self.name_buffer][self.list.get_selected()].has_key("retweeted_status"): tweet = self.db.settings[self.name_buffer][self.list.get_selected()]["retweeted_status"]
else: tweet = self.db.settings[self.name_buffer][self.list.get_selected()]
if config.main["general"]["relative_times"] == True:
# On windows we need only put the new date on the column, but under linux and mac it isn't possible.
if self.system == "Windows":
original_date = datetime.datetime.strptime(tweet["created_at"], "%a %b %d %H:%M:%S +0000 %Y")
date = original_date-datetime.timedelta(seconds=-self.db.settings["utc_offset"])
ts = prettydate(original_date)
self.list.list.SetStringItem(self.list.get_selected(), 2, ts)
else:
self.list.list.SetString(self.list.get_selected(), " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)))
if twitter.utils.is_audio(tweet):
sound.player.play("audio.ogg", False)
def start_streams(self):
if self.name_buffer == "sent":
num = twitter.starting.start_sent(self.db, self.twitter, self.name_buffer, self.function, param=self.argumento)
else:
# try:
if self.argumento != None:
num = twitter.starting.start_stream(self.db, self.twitter, self.name_buffer, self.function, param=self.argumento)
else:
num = twitter.starting.start_stream(self.db, self.twitter, self.name_buffer, self.function)
# except TwythonError:
# raise TwythonError
# self.parent.delete_invalid_timeline()
if self.sound != "" and num > 0 and self.name_buffer != "home_timeline" and self.name_buffer != "sent": sound.player.play(self.sound)
return num
def get_more_items(self):
if config.main["general"]["reverse_timelines"] == False:
last_id = self.db.settings[self.name_buffer][0]["id"]
else:
last_id = self.db.settings[self.name_buffer][-1]["id"]
try:
items = twitter.starting.get_more_items(self.function, self.twitter, count=config.main["general"]["max_tweets_per_call"], max_id=last_id)
except TwythonError as e:
output.speak(e.message)
for i in items:
if config.main["general"]["reverse_timelines"] == False:
self.db.settings[self.name_buffer].insert(0, i)
else:
self.db.settings[self.name_buffer].append(i)
if config.main["general"]["reverse_timelines"] == False:
for i in items:
tweet = self.compose_function(i, self.db)
self.list.insert_item(True, *tweet)
else:
for i in items:
tweet = self.compose_function(i, self.db)
self.list.insert_item(False, *tweet)
output.speak(_(u"%s items retrieved") % (len(items)))
def put_items(self, num):
if self.list.get_count() == 0:
for i in self.db.settings[self.name_buffer]:
tweet = self.compose_function(i, self.db)
self.list.insert_item(False, *tweet)
self.set_list_position()
elif self.list.get_count() > 0:
if config.main["general"]["reverse_timelines"] == False:
for i in self.db.settings[self.name_buffer][:num]:
tweet = self.compose_function(i, self.db)
self.list.insert_item(False, *tweet)
else:
for i in self.db.settings[self.name_buffer][0:num]:
tweet = self.compose_function(i, self.db)
self.list.insert_item(True, *tweet)
def onDm(self, ev):
if self.name_buffer == "sent": return
if self.name_buffer == "direct_messages":
self.onResponse(ev)
else:
user = self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"]
dlg = gui.dialogs.message.dm(_("Direct message to %s") % (user,), "", "", self)
if dlg.ShowModal() == wx.ID_OK:
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
# dlg.Destroy()
if ev != None:
self.list.list.SetFocus()
def post_status(self, ev=None):
text = gui.dialogs.message.tweet(_(u"Write the tweet here"), _(u"Tweet"), "", self)
if text.ShowModal() == wx.ID_OK:
if text.image == None:
call_threaded(self.twitter.api_call, call_name="update_status", _sound="tweet_send.ogg", status=text.text.GetValue())
else:
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="tweet_send.ogg", status=text.text.GetValue(), media=text.file)
# text.Destroy()
if ev != None: self.list.list.SetFocus()
def onRetweet(self, ev):
if self.name_buffer != "direct_messages":
id=self.db.settings[self.name_buffer][self.list.get_selected()]["id"]
ask = wx.MessageDialog(self.parent, _(u"Would you like to add a comment to this tweet?"), _("Retweet"), wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
response = ask.ShowModal()
if response == wx.ID_YES:
dlg = gui.dialogs.message.retweet(_(u"Add your comment to the tweet"), "", u"“@%s: %s" % (self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"], self.db.settings[self.name_buffer][self.list.get_selected()]["text"]), self)
if dlg.ShowModal() == wx.ID_OK:
if dlg.image == None:
call_threaded(self.twitter.api_call, call_name="update_status", _sound="retweet_send.ogg", status=dlg.text.GetValue(), in_reply_to_status_id=dlg.in_reply_to)
else:
call_threaded(self.twitter.call_api, call_name="update_status_with_media", _sound="retweet_send.ogg", status=dlg.text.GetValue(), in_reply_to_status_id=text.in_reply_to, media=dlg.file)
# dlg.Destroy()
if ev != None:
self.list.list.SetFocus()
elif response == wx.ID_NO:
call_threaded(self.twitter.api_call, call_name="retweet", _sound="retweet_send.ogg", id=id)
if ev != None: self.list.list.SetFocus()
ask.Destroy()
def onResponse(self, ev):
if self.name_buffer == "sent": return
dlg = gui.dialogs.message.reply(_(u"Reply to %s") % (self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"]), "", u"@%s " % (self.db.settings[self.name_buffer][self.list.get_selected()]["user"]["screen_name"]), self)
if dlg.ShowModal() == wx.ID_OK:
if dlg.image == None:
call_threaded(self.twitter.api_call, call_name="update_status", _sound="reply_send.ogg", in_reply_to_status_id=dlg.in_reply_to, status=dlg.text.GetValue())
else:
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=dlg.in_reply_to, status=dlg.text.GetValue(), media=dlg.file)
# dlg.Destroy()
if ev != None: self.list.list.SetFocus()
def update(self, ev):
data = ev.GetItem()
announce = ev.GetAnnounce()
if config.main["general"]["reverse_timelines"] == False: self.db.settings[self.name_buffer].append(data)
else: self.db.settings[self.name_buffer].insert(0, data)
tweet = self.compose_function(data, self.db)
self.list.insert_item(config.main["general"]["reverse_timelines"], *tweet)
if self.name_buffer not in config.main["other_buffers"]["muted_buffers"]:
if self.sound != "": sound.player.play(self.sound)
if announce != "": output.speak(announce)
if self.name_buffer in config.main["other_buffers"]["autoread_buffers"]:
output.speak(" ".join(tweet[:2]))
def interact(self, ev):
try:
if self.db.settings[self.name_buffer][self.list.get_selected()].has_key("retweeted_status"): tweet = self.db.settings[self.name_buffer][self.list.get_selected()]["retweeted_status"]
else: tweet = self.db.settings[self.name_buffer][self.list.get_selected()]
urls = twitter.utils.find_urls_in_text(tweet["text"])
except:
urls = []
if type(ev) is str: event = ev
else:
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "delete_item"
else:
ev.Skip()
return
if event == "audio" and len(urls) > 0:
self.streamer(urls[0])
elif event == "url":
if len(urls) == 0: return
elif len(urls) == 1:
output.speak(_(u"Opening URL..."), True)
webbrowser.open(urls[0])
elif len(urls) > 1:
gui.dialogs.urlList.urlList(urls).ShowModal()
elif event == "volume_down":
if config.main["sound"]["volume"] > 0.05:
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
sound.player.play("volume_changed.ogg", False)
if hasattr(self.parent, "audioStream"):
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
elif event == "volume_up":
if config.main["sound"]["volume"] < 0.95:
config.main["sound"]["volume"] = config.main["sound"]["volume"]+0.05
sound.player.play("volume_changed.ogg", False)
if hasattr(self.parent, "audioStream"):
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
elif event == "clear_list" and self.list.get_count() > 0:
dlg = wx.MessageDialog(self, _(u"Do you really want to empty this buffer? It's tweets will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
self.db.settings[self.name_buffer] = []
self.list.clear()
elif event == "delete_item":
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this message?"), _(u"Delete"), wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
self.destroy_status(wx.EVT_MENU)
else:
return
try:
ev.Skip()
except:
pass
def streamer(self, url):
if hasattr(self.parent, "audioStream"):
if self.parent.audioStream.stream.is_active() == 0:
output.speak(_(u"Playing..."))
self.parent.audioStream = sound.urlStream(url)
try:
self.parent.audioStream.prepare()
self.parent.audioStream.play()
except:
del self.parent.audioStream
output.speak(_(u"Unable to play audio."))
else:
output.speak(_(u"Audio stopped."))
self.parent.audioStream.stream.stop()
else:
output.speak(_(u"Playing..."))
self.parent.audioStream = sound.urlStream(url)
try:
self.parent.audioStream.prepare()
self.parent.audioStream.play()
except:
output.speak(_(u"Unable to play audio."))
del self.parent.audioStream
def set_list_position(self):
if config.main["general"]["reverse_timelines"] == False:
self.list.select_item(len(self.db.settings[self.name_buffer])-1)
else:
self.list.select_item(0)

48
src/gui/buffers/dm.py Normal file
View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import sound
import gui.dialogs
import logging as original_logger
from base import basePanel
from mysc.thread_utils import call_threaded
log = original_logger.getLogger("buffers.base")
class dmPanel(basePanel):
def __init__(self, parent, window, name_buffer, function, argumento=None, sound=""):
""" Class to DM'S. Reply and retweet buttons are not showed and they have your delete method for dm's."""
super(dmPanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
self.retweetBtn.Disable()
self.responseBtn.Disable()
def destroy_status(self, ev):
index = self.list.get_selected()
try:
self.twitter.twitter.destroy_direct_message(id=self.db.settings[self.name_buffer][index]["id"])
self.db.settings[self.name_buffer].pop(index)
self.remove_item(index)
except:
sound.player.play("error.ogg")
def onResponse(self, ev):
dlg = gui.dialogs.message.dm(_("Direct message to %s") % (self.db.settings[self.name_buffer][self.list.get_selected()]["sender"]["screen_name"]), "", "", self)
if dlg.ShowModal() == wx.ID_OK:
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
if ev != None:
self.list.list.SetFocus()

134
src/gui/buffers/events.py Normal file
View File

@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import sound
import config
import platform
import gui.dialogs
import output
import logging as original_logger
from multiplatform_widgets import widgets
from mysc import event
from mysc.thread_utils import call_threaded
log = original_logger.getLogger("buffers.base")
class eventsPanel(wx.Panel):
""" Buffer to show events. Different than tweets or people."""
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"))
def bind_events(self):
self.Bind(event.MyEVT_OBJECT, self.update)
def put_items(self, items):
pass
def get_selected_text(self):
if self.list.get_count() == 0: return _(u"Empty")
if self.system == "Windows":
return "%s. %s" % (self.list.list.GetItemText(self.list.get_selected()), self.list.list.GetItemText(self.list.get_selected(), 1))
else:
return self.list.list.GetStringSelection()
def get_message(self, dialog=False):
return self.get_selected_text()
def __init__(self, parent, window, sound=""):
self.type = "event"
self.system = platform.system()
self.name_buffer = "events"
self.parent = window
self.sound = "new_event.ogg"
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer()
self.list = widgets.list(self, _(u"Date"), _(u"Event"), size=(600,600), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
self.tweet = wx.Button(self, -1, _(u"Tweet"))
self.tweet.Bind(wx.EVT_BUTTON, self.post_status)
self.delete_event = wx.Button(self, -1, _(u"Remove event"))
self.delete_event.Bind(wx.EVT_BUTTON, self.on_delete_event)
self.bind_events()
def on_delete_event(self, ev):
self.list.remove_item(self.get_selected())
def remove_buffer(self):
return None
def start_streams(self):
return 0
def post_status(self, ev=None):
text = gui.dialogs.message.tweet(_(u"Write the tweet here"), _(u"Tweet"), "", self.parent)
if text.ShowModal() == wx.ID_OK:
if text.image == None:
call_threaded(self.parent.twitter.api_call, call_name="update_status", _sound="tweet_send.ogg", status=text.text.GetValue())
else:
call_threaded(self.parent.twitter.api_call, call_name="update_status_with_media", _sound="tweet_send.ogg", status=text.text.GetValue(), media=text.file)
# text.Destroy()
if ev != None: self.list.list.SetFocus()
def update(self, ev):
tweet = ev.GetItem()
announce = ev.GetAnnounce()
self.list.insert_item(config.main["general"]["reverse_timelines"], *tweet)
if self.list.get_count() == 1:
self.list.select_item(0)
if self.name_buffer not in config.main["other_buffers"]["muted_buffers"]:
if self.sound != "": sound.player.play(self.sound)
# if announce != "": output.speak(announce)
if self.name_buffer in config.main["other_buffers"]["autoread_buffers"]:
output.speak(" ".join(tweet))
def interact(self, ev):
if type(ev) is str: event = ev
else:
if ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "delete_item"
else:
ev.Skip()
return
if event == "volume_down":
if config.main["sound"]["volume"] > 0.05:
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
sound.player.play("volume_changed.ogg", False)
if hasattr(self.parent, "audioStream"):
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
elif event == "volume_up":
if config.main["sound"]["volume"] < 0.95:
config.main["sound"]["volume"] = config.main["sound"]["volume"]+0.05
sound.player.play("volume_changed.ogg", False)
if hasattr(self.parent, "audioStream"):
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
elif event == "clear_list" and self.get_count() > 0:
dlg = wx.MessageDialog(self, _(u"Do you really want to empty this buffer? It's tweets will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
self.list.clear()
elif event == "delete_item":
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this message?"), _(u"Delete"), wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
self.list.remove_item(self.list.get_selected())
else:
return
try:
ev.Skip()
except:
pass

View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import twitter
import config
import sound
import logging as original_logger
from base import basePanel
log = original_logger.getLogger("buffers.base")
class favsPanel(basePanel):
def __init__(self, parent, window, name_buffer, argumento=None, sound=""):
super(favsPanel, self).__init__(parent, window, name_buffer, function="", argumento=argumento, sound=sound)
self.type = "favourites_timeline"
def start_streams(self):
num = twitter.starting.get_favourites_timeline(self.db, self.twitter, self.name_buffer, param=self.argumento, count=200)
if self.sound != "" and num > 0:
sound.player.play(self.sound)
if self.list.get_count() > 0: self.put_items(num)
return num
return num
def remove_buffer(self):
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this favourites timeline?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
names = config.main["other_buffers"]["favourites_timelines"]
user = self.argumento
log.info(u"Deleting %s's timeline" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos
def remove_invalid_buffer(self):
names = config.main["other_buffers"]["favourites_timelines"]
user = self.name_buffer[:-5]
log.info(u"Deleting %s's timeline" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos

68
src/gui/buffers/lists.py Normal file
View File

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import sound
import config
import twitter
import gui.dialogs
import logging as original_logger
from base import basePanel
from mysc.thread_utils import call_threaded
log = original_logger.getLogger("buffers.base")
class listPanel(basePanel):
def __init__(self, parent, window, name_buffer, argumento="", sound=""):
super(listPanel, self).__init__(parent, window, name_buffer, argumento=argumento, sound=sound)
self.type = "list"
self.users = []
self.sound = "list_tweet.ogg"
def start_streams(self):
self.retrieve_ids()
num = twitter.starting.start_list(self.db, self.twitter, self.name_buffer, list_id=self.argumento)
return num
def retrieve_ids(self):
self.users = twitter.starting.get_users_list(self.twitter, self.argumento)
def remove_buffer(self):
if self.type == "list":
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this list?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
names = config.main["other_buffers"]["lists"]
user = self.name_buffer[:-5]
log.info(u"Deleting %s's list" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos
def remove_invalid_buffer(self):
if self.type == "list":
names = config.main["other_buffers"]["lists"]
user = self.name_buffer[:-5]
log.info(u"Deleting %s's list" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos

37
src/gui/buffers/panels.py Normal file
View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
from multiplatform_widgets import widgets
class accountPanel(wx.Panel):
def __init__(self, parent):
super(accountPanel, self).__init__(parent=parent)
self.type = "account"
sizer = wx.BoxSizer(wx.VERTICAL)
self.list = widgets.list(self, _(u"Announce"))
sizer.Add(self.list.list, 0, wx.ALL, 5)
self.SetSizer(sizer)
class emptyPanel(accountPanel):
def __init__(self, parent):
super(emptyPanel, self).__init__(parent=parent)
self.type = "empty"
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"))

142
src/gui/buffers/people.py Normal file
View File

@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import sound
import config
import twitter
import gui.dialogs
import logging as original_logger
import output
from multiplatform_widgets import widgets
from mysc import event
from base import basePanel
from mysc.thread_utils import call_threaded
from twython import TwythonError
log = original_logger.getLogger("buffers.base")
class peoplePanel(basePanel):
""" Buffer used to show people."""
def bind_events(self):
self.Bind(event.MyEVT_OBJECT, self.update)
self.Bind(event.MyEVT_DELETED, self.Remove)
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
def create_list(self):
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
def __init__(self, parent, window, name_buffer, function, argumento=None, sound="", timeline=False):
self.type = "people"
super(peoplePanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
self.responseBtn.SetLabel(_(u"Mention"))
self.retweetBtn.Disable()
self.compose_function = twitter.compose.compose_followers_list
def onDm(self, ev):
if self.name_buffer == "sent": return
if self.name_buffer == "direct_messages":
self.onResponse(ev)
else:
user = self.db.settings[self.name_buffer][self.list.get_selected()]["screen_name"]
dlg = gui.dialogs.message.dm(_("Direct message to %s") % (user,), "", "", self)
if dlg.ShowModal() == wx.ID_OK:
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
# dlg.Destroy()
if ev != None:
self.list.list.SetFocus()
def onResponse(self, ev):
dlg = gui.dialogs.message.reply(_(u"Reply to %s") % (self.db.settings[self.name_buffer][self.list.get_selected()]["screen_name"]), "", u"@%s " % (self.db.settings[self.name_buffer][self.list.get_selected()]["screen_name"]), self)
if dlg.ShowModal() == wx.ID_OK:
if dlg.image == None:
call_threaded(self.twitter.api_call, call_name="update_status", _sound="reply_send.ogg", in_reply_to_status_id=dlg.in_reply_to, status=dlg.text.GetValue())
else:
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=dlg.in_reply_to, status=dlg.text.GetValue(), media=dlg.file)
# dlg.Destroy()
if ev != None: self.list.list.SetFocus()
def Remove(self, ev):
try:
index = self.list.get_selected()
self.list.remove_item(ev.GetItem())
except:
log.error("Unable to delete element %s from the list %s" % (str(ev.GetItem(), self.name_buffer)))
def start_streams(self):
num = twitter.starting.start_followers(self.db, self.twitter, self.name_buffer, self.function, param=self.argumento)
# sound.player.play(self.sound)
return num
def put_items(self, num):
if self.list.get_count() > 0:
self.list.clear()
for i in self.db.settings[self.name_buffer]:
f = self.compose_function(i, self.db)
self.list.insert_item(False, *f)
self.set_list_position()
def get_more_items(self):
if self.name_buffer == "followers": cursor = twitter.starting.followers_cursor
elif self.name_buffer == "friends": cursor = twitter.starting.friends_cursor
try:
items = twitter.starting.get_more_items(self.function, self.twitter, users=True, name=self.name_buffer, count=config.main["general"]["max_tweets_per_call"], cursor=cursor)
except TwythonError as e:
output.speak(e.message)
return
for i in items:
if config.main["general"]["reverse_timelines"] == False:
self.db.settings[self.name_buffer].insert(0, i)
else:
self.db.settings[self.name_buffer].append(i)
if config.main["general"]["reverse_timelines"] == False:
for i in items:
tweet = self.compose_function(i, self.db)
self.list.insert_item(True, *tweet)
else:
for i in items:
tweet = self.compose_function(i, self.db)
self.list.insert_item(False, *tweet)
output.speak(_(u"%s items retrieved") % (len(items)))
def interact(self, ev):
if type(ev) is str: event = ev
else:
if ev.GetKeyCode() == wx.WXK_RETURN:
event = "url"
elif ev.GetKeyCode() == wx.WXK_F5:
event = "volume_down"
elif ev.GetKeyCode() == wx.WXK_F6:
event = "volume_down"
else:
ev.Skip()
return
if event == "url":
gui.dialogs.show_user.showUserProfile(self.parent.twitter, self.db.settings[self.name_buffer][self.list.get_selected()]["screen_name"]).ShowModal()
elif event == "volume_down":
if config.main["sound"]["volume"] > 0.05:
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
sound.player.play("volume_changed.ogg")
elif event == "volume_up":
if config.main["sound"]["volume"] < 0.95:
config.main["sound"]["volume"] = config.main["sound"]["volume"]+0.05
sound.player.play("volume_changed.ogg")
if type(ev) is not str: ev.Skip()
def remove_buffer(self):
pos = None
return pos

51
src/gui/buffers/trends.py Normal file
View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import sound
import config
import twitter
import gui.dialogs
import logging as original_logger
from base import basePanel
log = original_logger.getLogger("buffers.base")
class trendPanel(basePanel):
def __init__(self, parent, window, name_buffer, *args, **kwargs):
super(searchPanel, self).__init__(parent, window, name_buffer, sound)
self.type = "trend"
self.args = kwargs
def start_streams(self):
num = twitter.starting.search(self.db, self.twitter, self.name_buffer, **self.args)
if num > 0: sound.player.play("search_updated.ogg")
self.put_items(num)
return num
def remove_buffer(self):
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this search term?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
names = config.main["other_buffers"]["tweet_searches"]
user = self.name_buffer[:-7]
log.info(u"Deleting %s's search term" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import sound
import config
import twitter
import gui.dialogs
import output
import logging as original_logger
from base import basePanel
log = original_logger.getLogger("buffers.base")
class searchPanel(basePanel):
def __init__(self, parent, window, name_buffer, *args, **kwargs):
super(searchPanel, self).__init__(parent, window, name_buffer, sound)
self.type = "search"
self.args = kwargs
def load_search(self):
num = self.start_streams()
self.put_items(num)
def start_streams(self):
num = twitter.starting.search(self.db, self.twitter, self.name_buffer, **self.args)
if num > 0: sound.player.play("search_updated.ogg")
return num
def remove_buffer(self):
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this search term?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
names = config.main["other_buffers"]["tweet_searches"]
user = self.name_buffer[:-7]
log.info(u"Deleting %s's search term" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"))

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import twitter
import logging as original_logger
import sound
from people import peoplePanel
from mysc import event
from multiplatform_widgets import widgets
log = original_logger.getLogger("buffers.base")
class searchUsersPanel(peoplePanel):
def create_list(self):
""" Returns the list for put the tweets here."""
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
# def bind_events(self):
# self.Bind(event.MyEVT_OBJECT, self.update)
# self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
def __init__(self, parent, window, name_buffer, *args, **kwargs):
super(searchUsersPanel, self).__init__(parent, window, name_buffer, function=None)
self.compose_function = twitter.compose.compose_followers_list
self.create_list()
self.args = args
self.kwargs = kwargs
self.type = "timeline"
def start_streams(self):
num = twitter.starting.search_users(self.db, self.twitter, self.name_buffer, **self.kwargs)
if num > 0: sound.player.play("search_updated.ogg")
# self.put_items(num)
return num
def load_search(self):
num = self.start_streams()
self.put_items(num)
def remove_buffer(self):
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this search term?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos

View File

@ -0,0 +1 @@
import message, urlList, follow, utils, show_user, update_profile, configuration, lists, search

View File

@ -0,0 +1,420 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import config
#import gui
from gui import buffers
import sound as snd
import sound_lib
import languageHandler
import logging as original_logger
import os
import webbrowser
import paths
import platform
from mysc import restart
log = original_logger.getLogger("configuration")
system = platform.system()
class general(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer(wx.VERTICAL)
language = wx.StaticText(self, -1, _(u"Language"))
self.langs = languageHandler.getAvailableLanguages()
langs = []
[langs.append(i[1]) for i in self.langs]
self.codes = []
[self.codes.append(i[0]) for i in self.langs]
self.language = wx.ListBox(self, -1, choices=langs)
id = self.codes.index(config.main["general"]["language"])
self.language.SetSelection(id)
self.language.SetSize(self.language.GetBestSize())
langBox = wx.BoxSizer(wx.HORIZONTAL)
langBox.Add(language, 0, wx.ALL, 5)
langBox.Add(self.language, 0, wx.ALL, 5)
sizer.Add(langBox, 0, wx.ALL, 5)
self.relative_time = wx.CheckBox(self, -1, _(U"Relative times"))
self.relative_time.SetValue(config.main["general"]["relative_times"])
sizer.Add(self.relative_time, 0, wx.ALL, 5)
if platform.system() == "Windows":
self.disable_sapi5 = wx.CheckBox(self, -1, _(u"Activate Sapi5 when any other screen reader is not being run"))
self.disable_sapi5.SetValue(config.main["general"]["voice_enabled"])
sizer.Add(self.disable_sapi5, 0, wx.ALL, 5)
self.show_gui = wx.CheckBox(self, -1, _(u"Activate the auto-start of the invisible interface"))
self.show_gui.SetValue(config.main["general"]["hide_gui"])
sizer.Add(self.show_gui, 0, wx.ALL, 5)
apiCallsBox = wx.BoxSizer(wx.HORIZONTAL)
apiCallsBox.Add(wx.StaticText(self, -1, _(u"API calls when the stream is started (One API call equals to 200 tweetts, two API calls equals 400 tweets, etc):")), 0, wx.ALL, 5)
self.apiCalls = wx.SpinCtrl(self, -1)
self.apiCalls.SetRange(1, 10)
self.apiCalls.SetValue(config.main["general"]["max_api_calls"])
self.apiCalls.SetSize(self.apiCalls.GetBestSize())
apiCallsBox.Add(self.apiCalls, 0, wx.ALL, 5)
sizer.Add(apiCallsBox, 0, wx.ALL, 5)
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
tweetsPerCallBox.Add(wx.StaticText(self, -1, _(u"Items on each API call")), 0, wx.ALL, 5)
self.itemsPerApiCall = wx.SpinCtrl(self, -1)
self.itemsPerApiCall.SetRange(0, 200)
self.itemsPerApiCall.SetValue(config.main["general"]["max_tweets_per_call"])
self.itemsPerApiCall.SetSize(self.itemsPerApiCall.GetBestSize())
tweetsPerCallBox.Add(self.itemsPerApiCall, 0, wx.ALL, 5)
sizer.Add(tweetsPerCallBox, 0, wx.ALL, 5)
self.reverse_timelines = wx.CheckBox(self, -1, _(u"Inverted buffers: The newest tweets will be shown at the beginning of the lists while the oldest at the end"))
self.reverse_timelines.SetValue(config.main["general"]["reverse_timelines"])
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
self.SetSizer(sizer)
class other_buffers(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.followers_value = config.main["other_buffers"]["show_followers"]
self.friends_value = config.main["other_buffers"]["show_friends"]
self.favs_value = config.main["other_buffers"]["show_favourites"]
self.events_value = config.main["other_buffers"]["show_events"]
self.blocks_value = config.main["other_buffers"]["show_blocks"]
self.mutes_value = config.main["other_buffers"]["show_muted_users"]
self.followers = wx.CheckBox(self, -1, _(u"Show followers"))
self.followers.SetValue(config.main["other_buffers"]["show_followers"])
sizer.Add(self.followers, 0, wx.ALL, 5)
self.friends = wx.CheckBox(self, -1, _(u"Show friends"))
self.friends.SetValue(config.main["other_buffers"]["show_friends"])
sizer.Add(self.friends, 0, wx.ALL, 5)
self.favs = wx.CheckBox(self, -1, _(u"Show favourites"))
self.favs.SetValue(config.main["other_buffers"]["show_favourites"])
sizer.Add(self.favs, 0, wx.ALL, 5)
self.blocks = wx.CheckBox(self, -1, _(u"Show blocked users"))
self.blocks.SetValue(config.main["other_buffers"]["show_blocks"])
sizer.Add(self.blocks, 0, wx.ALL, 5)
self.mutes = wx.CheckBox(self, -1, _(u"Show muted users"))
self.mutes.SetValue(config.main["other_buffers"]["show_muted_users"])
sizer.Add(self.mutes, 0, wx.ALL, 5)
self.events = wx.CheckBox(self, -1, _(u"Show events"))
self.events.SetValue(config.main["other_buffers"]["show_events"])
sizer.Add(self.events, 0, wx.ALL, 5)
self.SetSizer(sizer)
class ignoredClients(wx.Panel):
def __init__(self, parent):
super(ignoredClients, self).__init__(parent=parent)
sizer = wx.BoxSizer(wx.VERTICAL)
choices = config.main["twitter"]["ignored_clients"]
label = wx.StaticText(self, -1, _(u"Ignored clients"))
self.clients = wx.ListBox(self, -1, choices=choices)
self.clients.SetSize(self.clients.GetBestSize())
clientsBox = wx.BoxSizer(wx.HORIZONTAL)
clientsBox.Add(label, 0, wx.ALL, 5)
clientsBox.Add(self.clients, 0, wx.ALL, 5)
add = wx.Button(self, -1, _(u"Add client"))
remove = wx.Button(self, -1, _(u"Remove client"))
self.Bind(wx.EVT_BUTTON, self.add, add)
self.Bind(wx.EVT_BUTTON, self.remove, remove)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(add, 0, wx.ALL, 5)
btnBox.Add(remove, 0, wx.ALL, 5)
sizer.Add(clientsBox, 0, wx.ALL, 5)
sizer.Add(btnBox, 0, wx.ALL, 5)
self.SetSizer(sizer)
def add(self, ev):
entry = wx.TextEntryDialog(self, _(u"Enter the name of the client here"), _(u"Add a new ignored client"))
if entry.ShowModal() == wx.ID_OK:
client = entry.GetValue()
if client not in config.main["twitter"]["ignored_clients"]:
config.main["twitter"]["ignored_clients"].append(client)
self.clients.Append(client)
def remove(self, ev):
if self.clients.GetCount() == 0: return
id = self.clients.GetSelection()
config.main["twitter"]["ignored_clients"].pop(id)
self.clients.Delete(id)
class sound(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
sizer = wx.BoxSizer(wx.VERTICAL)
volume = wx.StaticText(self, -1, _(u"Volume"))
self.volumeCtrl = wx.Slider(self)
self.volumeCtrl.SetRange(0, 100)
self.volumeCtrl.SetValue(config.main["sound"]["volume"]*100)
self.volumeCtrl.SetSize(self.volumeCtrl.GetBestSize())
volumeBox = wx.BoxSizer(wx.HORIZONTAL)
volumeBox.Add(volume, 0, wx.ALL, 5)
volumeBox.Add(self.volumeCtrl, 0, wx.ALL, 5)
sizer.Add(volumeBox, 0, wx.ALL, 5)
self.global_mute = wx.CheckBox(self, -1, _(u"Global mute"))
self.global_mute.SetValue(config.main["sound"]["global_mute"])
sizer.Add(self.global_mute, 0, wx.ALL, 5)
self.output_devices = sound_lib.output.Output.get_device_names()
output_label = wx.StaticText(self, -1, _(u"Output device"))
self.output = wx.ComboBox(self, -1, choices=self.output_devices, style=wx.CB_READONLY)
self.output.SetValue(config.main["sound"]["output_device"])
self.output.SetSize(self.output.GetBestSize())
outputBox = wx.BoxSizer(wx.HORIZONTAL)
outputBox.Add(output_label, 0, wx.ALL, 5)
outputBox.Add(self.output, 0, wx.ALL, 5)
sizer.Add(outputBox, 0, wx.ALL, 5)
self.input_devices = sound_lib.input.Input.get_device_names()
input_label = wx.StaticText(self, -1, _(u"Input device"))
self.input = wx.ComboBox(self, -1, choices=self.input_devices, style=wx.CB_READONLY)
self.input.SetValue(config.main["sound"]["input_device"])
self.input.SetSize(self.input.GetBestSize())
inputBox = wx.BoxSizer(wx.HORIZONTAL)
inputBox.Add(input_label, 0, wx.ALL, 5)
inputBox.Add(self.input, 0, wx.ALL, 5)
sizer.Add(inputBox, 0, wx.ALL, 5)
soundBox = wx.BoxSizer(wx.VERTICAL)
self.soundpacks = []
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(paths.sound_path(i)) == True ]
soundpack_label = wx.StaticText(self, -1, _(u"Sound pack"))
self.soundpack = wx.ComboBox(self, -1, choices=self.soundpacks, style=wx.CB_READONLY)
self.soundpack.SetValue(config.main["sound"]["current_soundpack"])
self.soundpack.SetSize(self.soundpack.GetBestSize())
soundBox.Add(soundpack_label, 0, wx.ALL, 5)
soundBox.Add(self.soundpack, 0, wx.ALL, 5)
sizer.Add(soundBox, 0, wx.ALL, 5)
self.SetSizer(sizer)
class audioServicesPanel(wx.Panel):
def __init__(self, parent):
super(audioServicesPanel, self).__init__(parent)
mainSizer = wx.BoxSizer(wx.VERTICAL)
apiKeyLabel = wx.StaticText(self, -1, _(u"If you've got a SndUp account, enter your API Key here. Whether the API Key is wrong, the App will fail to upload anything to the server. Whether there's no API Key here, then the audio files will be uploaded anonimously"))
self.apiKey = wx.TextCtrl(self, -1)
self.apiKey.SetValue(config.main["sound"]["sndup_api_key"])
dc = wx.WindowDC(self.apiKey)
dc.SetFont(self.apiKey.GetFont())
self.apiKey.SetSize(dc.GetTextExtent("0"*100))
apiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
apiKeyBox.Add(apiKeyLabel, 0, wx.ALL, 5)
apiKeyBox.Add(self.apiKey, 0, wx.ALL, 5)
mainSizer.Add(apiKeyBox, 0, wx.ALL, 5)
first_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.dropbox = wx.Button(self, -1)
if len(config.main["services"]["dropbox_token"]) > 0:
self.dropbox.SetLabel(_(u"Unlink your Dropbox account"))
else:
self.dropbox.SetLabel(_(u"Link your Dropbox account"))
self.dropbox.Bind(wx.EVT_BUTTON, self.onLink_unlink)
first_sizer.Add(self.dropbox, 0, wx.ALL, 5)
mainSizer.Add(first_sizer, 0, wx.ALL, 5)
self.SetSizer(mainSizer)
def setup_dropbox(self):
from extra.AudioUploader import dropbox_transfer
auth = dropbox_transfer.dropboxLogin()
url = auth.get_url()
wx.MessageDialog(self, _(u"The authorisation request will be shown on your browser. Copy the code tat Dropbox will provide and, in the text box that will appear on TW Blue, paste it. This code is necessary to continue. You only need to do it once."), _(u"Authorisation"), wx.OK).ShowModal()
webbrowser.open(url)
dlg = wx.TextEntryDialog(self, _(u"Enter the code here."), _(u"Verification code"))
if dlg.ShowModal() == wx.ID_CANCEL:
return False
resp = dlg.GetValue()
if resp == "":
self.dropbox.SetLabel(_(u"Link your Dropbox account"))
return False
else:
try:
auth.authorise(resp)
self.dropbox.SetLabel(_(u"Unlink your Dropbox account"))
except:
wx.MessageDialog(self, _(u"Error during authorisation. Try again later."), _(u"Error!"), wx.ICON_ERROR).ShowModal()
self.dropbox.SetLabel(_(u"Link your Dropbox account"))
return False
def onLink_unlink(self, ev):
if self.dropbox.GetLabel() == _(u"Link your Dropbox account"):
self.setup_dropbox()
else:
self.disconnect_dropbox()
def disconnect_dropbox(self):
config.main["services"]["dropbox_token"] = ""
self.dropbox.SetLabel(_(u"Link your Dropbox account"))
class configurationDialog(wx.Dialog):
def __init__(self, parent):
self.parent = parent
wx.Dialog.__init__(self, None, -1)
panel = wx.Panel(self)
self.SetTitle(_(u"TW Blue preferences"))
sizer = wx.BoxSizer(wx.VERTICAL)
notebook = wx.Notebook(panel)
self.general = general(notebook)
notebook.AddPage(self.general, _(u"General"))
self.general.SetFocus()
self.buffers = other_buffers(notebook)
notebook.AddPage(self.buffers, _(u"Show other buffers"))
self.ignored_clients = ignoredClients(notebook)
notebook.AddPage(self.ignored_clients, _(u"Ignored clients"))
self.sound = sound(notebook)
notebook.AddPage(self.sound, _(u"Sound"))
self.services = audioServicesPanel(notebook)
notebook.AddPage(self.services, _(u"Audio Services"))
sizer.Add(notebook, 0, wx.ALL, 5)
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
ok = wx.Button(panel, wx.ID_OK, _(u"Save"))
ok.Bind(wx.EVT_BUTTON, self.onSave)
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
self.SetEscapeId(cancel.GetId())
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
sizer.Add(ok_cancel_box, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def check_followers_change(self):
if self.buffers.followers.GetValue() != self.buffers.followers_value:
if self.buffers.followers.GetValue() == True:
log.debug("Creating followers list...")
followers = buffers.peoplePanel(self.parent.nb, self.parent, "followers", self.parent.twitter.twitter.get_followers_list, argumento=self.parent.db.settings["user_name"])
self.parent.nb.InsertSubPage(self.parent.db.settings["buffers"].index(self.parent.db.settings["user_name"]), followers, _(u"Followers"))
num = followers.start_streams()
followers.put_items(num)
self.parent.db.settings["buffers"].append("followers")
elif self.buffers.followers.GetValue() == False:
self.parent.nb.DeletePage(self.parent.db.settings["buffers"].index("followers"))
self.parent.db.settings.pop("followers")
self.parent.db.settings["buffers"].remove("followers")
def check_friends_change(self):
if self.buffers.friends.GetValue() != self.buffers.friends_value:
if self.buffers.friends.GetValue() == True:
log.debug("Creating friends list...")
friends = buffers.peoplePanel(self.parent.nb, self.parent, "friends", self.parent.twitter.twitter.get_friends_list, argumento=self.parent.db.settings["user_name"])
self.parent.nb.InsertSubPage(self.parent.db.settings["buffers"].index(self.parent.db.settings["user_name"]), friends, _(u"friends"))
num = friends.start_streams()
friends.put_items(num)
self.parent.db.settings["buffers"].append("friends")
elif self.buffers.friends.GetValue() == False:
self.parent.nb.DeletePage(self.parent.db.settings["buffers"].index("friends"))
self.parent.db.settings.pop("friends")
self.parent.db.settings["buffers"].remove("friends")
def check_favs_change(self):
if self.buffers.favs.GetValue() != self.buffers.favs_value:
if self.buffers.favs.GetValue() == True:
log.debug("Creating favorites...")
favs = buffers.basePanel(self.parent.nb, self.parent, "favs", self.parent.twitter.twitter.get_favorites)
self.parent.nb.InsertSubPage(self.parent.db.settings["buffers"].index(self.parent.db.settings["user_name"]), favs, _(u"Favorites"))
num = favs.start_streams()
favs.put_items(num)
self.parent.db.settings["buffers"].append("favs")
elif self.buffers.favs.GetValue() == False:
self.parent.nb.DeletePage(self.parent.db.settings["buffers"].index("favs"))
self.parent.db.settings.pop("favs")
self.parent.db.settings["buffers"].remove("favs")
def check_events_change(self):
if self.buffers.events.GetValue() != self.buffers.events_value:
if self.buffers.events.GetValue() == True:
log.debug("Creating events...")
events = buffers.eventsPanel(self.parent.nb, self.parent)
self.parent.nb.InsertSubPage(self.parent.db.settings["buffers"].index(self.parent.db.settings["user_name"]), events, _(u"Events"))
self.parent.db.settings["buffers"].append("events")
elif self.buffers.events.GetValue() == False:
self.parent.nb.DeletePage(self.parent.db.settings["buffers"].index("events"))
self.parent.db.settings["buffers"].remove("events")
def check_blocks_change(self):
if self.buffers.blocks.GetValue() != self.buffers.blocks_value:
if self.buffers.blocks.GetValue() == True:
log.debug("Creating blocked users list...")
blocks = buffers.peoplePanel(self.parent.nb, self.parent, "blocks", self.parent.twitter.twitter.list_blocks)
self.parent.nb.InsertSubPage(self.parent.db.settings["buffers"].index(self.parent.db.settings["user_name"]), blocks, _(u"Blocked users"))
num = blocks.start_streams()
blocks.put_items(num)
self.parent.db.settings["buffers"].append("blocks")
elif self.buffers.blocks.GetValue() == False:
self.parent.nb.DeletePage(self.parent.db.settings["buffers"].index("blocks"))
self.parent.db.settings.pop("blocks")
self.parent.db.settings["buffers"].remove("blocks")
def check_mutes_change(self):
if self.buffers.mutes.GetValue() != self.buffers.mutes_value:
if self.buffers.mutes.GetValue() == True:
log.debug("Creating muted users list...")
mutes = buffers.peoplePanel(self.parent.nb, self.parent, "muteds", self.parent.twitter.twitter.get_muted_users_list)
self.parent.nb.InsertSubPage(self.parent.db.settings["buffers"].index(self.parent.db.settings["user_name"]), mutes, _(u"Muted users"))
num = mutes.start_streams()
mutes.put_items(num)
self.parent.db.settings["buffers"].append("muteds")
elif self.buffers.mutes.GetValue() == False:
self.parent.nb.DeletePage(self.parent.db.settings["buffers"].index("muteds"))
self.parent.db.settings.pop("muteds")
self.parent.db.settings["buffers"].remove("muteds")
def onSave(self, ev):
need_restart = False
# Check general settings
if config.main["general"]["language"] != self.general.langs[self.general.language.GetSelection()][0]:
if self.general.langs[self.general.language.GetSelection()][0] in self.general.codes: config.main["general"]["language"] = self.general.langs[self.general.language.GetSelection()][0]
languageHandler.setLanguage(config.main["general"]["language"])
need_restart = True
if platform.system() == "Windows":
config.main["general"]["voice_enabled"] = self.general.disable_sapi5.GetValue()
config.main["general"]["hide_gui"] = self.general.show_gui.GetValue()
config.main["general"]["max_api_calls"] = self.general.apiCalls.GetValue()
config.main["general"]["max_tweets_per_call"] = self.general.itemsPerApiCall.GetValue()
if config.main["general"]["relative_times"] != self.general.relative_time.GetValue():
config.main["general"]["relative_times"] = self.general.relative_time.GetValue()
need_restart = True
if config.main["general"]["reverse_timelines"] != self.general.reverse_timelines.GetValue():
config.main["general"]["reverse_timelines"] = self.general.reverse_timelines.GetValue()
need_restart = True
## Check buffers settings
config.main["other_buffers"]["show_followers"] = self.buffers.followers.GetValue()
self.check_followers_change()
config.main["other_buffers"]["show_friends"] = self.buffers.friends.GetValue()
self.check_friends_change()
config.main["other_buffers"]["show_favourites"] = self.buffers.favs.GetValue()
self.check_favs_change()
config.main["other_buffers"]["show_events"] = self.buffers.events.GetValue()
self.check_events_change()
config.main["other_buffers"]["show_blocks"] = self.buffers.blocks.GetValue()
self.check_blocks_change()
config.main["other_buffers"]["show_muted_users"] = self.buffers.mutes.GetValue()
self.check_mutes_change()
## Check sound settings
config.main["sound"]["volume"] = self.sound.volumeCtrl.GetValue()/100.0
config.main["sound"]["global_mute"] = self.sound.global_mute.GetValue()
if system == "Windows":
config.main["sound"]["output_device"] = self.sound.output.GetStringSelection()
config.main["sound"]["input_device"] = self.sound.input.GetValue()
try:
snd.player.input.set_device(snd.player.input.find_device_by_name(config.main["sound"]["input_device"]))
snd.player.output.set_device(snd.player.output.find_device_by_name(config.main["sound"]["output_device"]))
except:
config.main["sound"]["output_device"] = "Default"
config.main["sound"]["input_device"] = "Default"
config.main["sound"]["sndup_api_key"] = self.services.apiKey.GetValue()
config.main["sound"]["current_soundpack"] = self.sound.soundpack.GetStringSelection()
snd.player.check_soundpack()
if need_restart == True:
config.main.write()
wx.MessageDialog(None, _(u"The application requires to be restarted to save these changes. Press OK to do it now."), _("Restart TW Blue"), wx.OK).ShowModal()
restart.restart_program()
config.main.write()
self.EndModal(wx.ID_OK)

151
src/gui/dialogs/follow.py Normal file
View File

@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import config
from mysc import event
import twitter
from twitter import utils
from twython import TwythonError
import output
class follow(wx.Dialog):
def __init__(self, parent, default="follow"):
self.parent = parent
wx.Dialog.__init__(self, None, -1)
panel = wx.Panel(self)
userSizer = wx.BoxSizer()
self.SetTitle(_(u"Action"))
if self.parent.name_buffer == "followers" or self.parent.name_buffer == "friends":
list = [self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()]["screen_name"]]
else:
try: list =twitter.utils.get_all_users(self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()], self.parent.db)
except KeyError: list = [self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()]["screen_name"]]
self.cb = wx.ComboBox(panel, -1, choices=list, value=list[0])
self.cb.SetFocus()
userSizer.Add(self.cb)
actionSizer = wx.BoxSizer(wx.VERTICAL)
label2 = wx.StaticText(panel, -1, _(u"Action"))
self.follow = wx.RadioButton(panel, -1, _(u"Follow"), style=wx.RB_GROUP)
self.unfollow = wx.RadioButton(panel, -1, _(u"Unfollow"))
self.mute = wx.RadioButton(panel, -1, _(u"Mute"))
self.unmute = wx.RadioButton(panel, -1, _(u"Unmute"))
self.block = wx.RadioButton(panel, -1, _(u"Block"))
self.unblock = wx.RadioButton(panel, -1, _(u"Unblock"))
self.reportSpam = wx.RadioButton(panel, -1, _(u"Report as spam"))
self.setup_default(default)
actionSizer.Add(label2)
actionSizer.Add(self.follow)
actionSizer.Add(self.unfollow)
actionSizer.Add(self.mute)
actionSizer.Add(self.unmute)
actionSizer.Add(self.block)
actionSizer.Add(self.unblock)
actionSizer.Add(self.reportSpam)
sizer = wx.BoxSizer(wx.VERTICAL)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.Bind(wx.EVT_BUTTON, self.onok)
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok)
btnsizer.Add(cancel)
sizer.Add(userSizer)
sizer.Add(actionSizer)
sizer.Add(btnsizer)
panel.SetSizer(sizer)
self.Bind(wx.EVT_CHAR_HOOK, self.onEscape, self.cb)
def onEscape(self, ev):
if ev.GetKeyCode() == wx.WXK_RETURN:
self.onok(wx.EVT_BUTTON)
ev.Skip()
def onok(self, ev):
if self.follow.GetValue() == True:
try:
self.parent.twitter.twitter.create_friendship(screen_name=self.cb.GetValue())
self.Destroy()
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
elif self.unfollow.GetValue() == True:
try:
id = self.parent.twitter.twitter.destroy_friendship(screen_name=self.cb.GetValue())
self.Destroy()
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
elif self.mute.GetValue() == True:
try:
id = self.parent.twitter.twitter.create_mute(screen_name=self.cb.GetValue())
if config.main["other_buffers"]["show_muted_users"] == True:
tweet_event = event.event(event.EVT_OBJECT, 1)
tweet_event.SetItem(id)
wx.PostEvent(self.parent.parent.nb.GetPage(self.parent.db.settings["buffers"].index("muteds")), tweet_event)
self.parent.db.settings["muted_users"].append(id["id"])
self.Destroy()
output.speak(_(u"You've muted to %s") % (id["screen_name"]))
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
elif self.unmute.GetValue() == True:
try:
id = self.parent.twitter.twitter.destroy_mute(screen_name=self.cb.GetValue())
if config.main["other_buffers"]["show_muted_users"] == True:
item = utils.find_item(id, self.parent.db.settings["muteds"])
if item > 0:
deleted_event = event.event(event.EVT_DELETED, 1)
deleted_event.SetItem(item)
wx.PostEvent(self.parent.parent.nb.GetPage(self.parent.db.settings["buffers"].index("muteds")), deleted_event)
if id["id"] in self.parent.db.settings["muted_users"]: self.parent.db.settings["muted_users"].remove(id["id"])
self.Destroy()
output.speak(_(u"You've unmuted to %s") % (id["screen_name"]))
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
elif self.reportSpam.GetValue() == True:
try:
self.parent.twitter.twitter.report_spam(screen_name=self.cb.GetValue())
self.Destroy()
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
elif self.block.GetValue() == True:
try:
self.parent.twitter.twitter.create_block(screen_name=self.cb.GetValue())
self.Destroy()
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
elif self.unblock.GetValue() == True:
try:
self.parent.twitter.twitter.destroy_block(screen_name=self.cb.GetValue())
self.Destroy()
except TwythonError as err:
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
def setup_default(self, default):
if default == "follow":
self.follow.SetValue(True)
elif default == "unfollow":
self.unfollow.SetValue(True)
elif default == "mute":
self.mute.SetValue(True)
elif default == "unmute":
self.unmute.SetValue(True)
elif default == "report":
self.reportSpam.SetValue(True)
elif default == "block":
self.block.SetValue(True)
elif default == "unblock":
self.unblock.SetValue(True)

244
src/gui/dialogs/lists.py Normal file
View File

@ -0,0 +1,244 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import platform
import output
import config
import gui
from multiplatform_widgets import widgets
from twython import TwythonError
from twitter import compose, utils
class listViewer(wx.Dialog):
def __init__(self, parent):
self.twitter = parent.twitter
self.db = parent.db
self.nb = parent.nb
self.parent = parent
wx.Dialog.__init__(self, None)
self.SetTitle(_(u"Lists manager"))
panel = wx.Panel(self)
label = wx.StaticText(panel, -1, _(u"Lists"))
self.lista = widgets.list(panel, _(u"List"), _(u"Description"), _(u"Owner"), _(u"Members"), _(u"mode"), size=(800, 800), style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
self.lista.list.SetFocus()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(label)
sizer.Add(self.lista.list)
self.createBtn = wx.Button(panel, wx.NewId(), _(u"Create a new list"))
self.createBtn.Bind(wx.EVT_BUTTON, self.onGo)
self.editBtn = wx.Button(panel, -1, _(u"Edit"))
self.Bind(wx.EVT_BUTTON, self.onEdit, self.editBtn)
self.deleteBtn = wx.Button(panel, -1, _(u"Remove"))
self.Bind(wx.EVT_BUTTON, self.onDelete, self.deleteBtn)
self.view = wx.Button(panel, -1, _(u"Open in buffer"))
self.Bind(wx.EVT_BUTTON, self.onView, self.view)
# self.members = wx.Button(panel, -1, _(u"View members"))
# self.members.Disable()
# self.subscriptors = wx.Button(panel, -1, _(u"View subscribers"))
# self.subscriptors.Disable()
# self.get_linkBtn = wx.Button(panel, -1, _(u"Get link for the list"))
# self.get_linkBtn.Bind(wx.EVT_BUTTON, self.onGetLink)
self.cancelBtn = wx.Button(panel, wx.ID_CANCEL)
btnSizer = wx.BoxSizer()
btnSizer.Add(self.createBtn)
btnSizer.Add(self.editBtn)
btnSizer.Add(self.cancelBtn)
panel.SetSizer(sizer)
self.populate_list()
self.lista.select_item(0)
def onGo(self, ev):
ev.Skip()
dlg = createListDialog()
if dlg.ShowModal() == wx.ID_OK:
name = dlg.name.GetValue()
description = dlg.description.GetValue()
if dlg.public.GetValue() == True: mode = "public"
else: mode = "private"
try:
new_list = self.twitter.twitter.create_list(name=name, description=description, mode=mode)
self.db.settings["lists"].append(new_list)
self.lista.insert_item(False, *compose.compose_list(new_list))
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
else:
return
dlg.Destroy()
def onEdit(self, ev):
ev.Skip()
if self.lista.get_count() == 0: return
list = self.db.settings["lists"][self.lista.get_selected()]
dlg = editListDialog(list)
if dlg.ShowModal() == wx.ID_OK:
name = dlg.name.GetValue()
description = dlg.description.GetValue()
if dlg.public.GetValue() == True: mode = "public"
else: mode = "private"
try:
self.twitter.twitter.update_list(list_id=self.lists[self.get_selected()]["id"], name=name, description=description, mode=mode)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
else:
return
dlg.Destroy()
def onDelete(self, ev):
ev.Skip()
if self.lista.get_count() == 0: return
list = self.db.settings["lists"][self.lista.get_selected()]["id"]
dlg = wx.MessageDialog(self, _("Do you really want to delete this list?"), _("Delete"), wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
try:
self.twitter.twitter.delete_list(list_id=list)
self.db.settings["lists"].pop(self.lista.get_selected())
self.remove_item(self.lista.get_selected())
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
dlg.Destroy()
def onView(self, ev):
ev.Skip()
if self.lista.get_count() == 0: return
list_id = self.db.settings["lists"][self.lista.get_selected()]["id"]
list_updated = self.twitter.twitter.get_specific_list(list_id=list_id)
self.db.settings["lists"][self.lista.get_selected()] = list_updated
if list_updated["slug"] not in config.main["other_buffers"]["lists"]:
config.main["other_buffers"]["lists"].append(list_updated["slug"])
output.speak(_(u"List opened"))
else:
output.speak(_(u"This list is arready opened."))
return
listUI = gui.buffers.lists.listPanel(self.nb, self.parent, list_updated["slug"]+"-list", argumento=utils.find_list(list_updated["slug"], self.db.settings["lists"]))
self.nb.AddPage(listUI, _(u"List for %s") % (list_updated["slug"],))
self.db.settings["buffers"].append(list_updated["slug"]+"-list")
num = listUI.start_streams()
listUI.put_items(num)
listUI.sound = "tweet_timeline.wav"
self.parent.stream2.disconnect()
del self.parent.stream2
self.parent.get_tls()
def populate_list(self):
for i in self.db.settings["lists"]:
item = compose.compose_list(i)
self.lista.insert_item(False, *item)
class userListViewer(listViewer):
def __init__(self, parent, username):
self.username = username
super(userListViewer, self).__init__(parent)
self.SetTitle(_(u"Viewing lists for %s") % (self.username))
self.createBtn.SetLabel(_(u"Subscribe"))
self.deleteBtn.SetLabel(_(u"Unsubscribe"))
self.editBtn.Disable()
self.view.Disable()
def populate_list(self):
self.lists = self.twitter.twitter.show_owned_lists(screen_name=self.username, count=200)["lists"]
for i in self.lists:
item = compose.compose_list(i)
self.lista.insert_item(False, *item)
def onGo(self, ev):
list_id = self.lists[self.lista.get_selected()]["id"]
try:
list = self.twitter.twitter.subscribe_to_list(list_id=list_id)
item = utils.find_item(list["id"], self.db.settings["lists"])
self.db.settings["lists"].append(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.status_code, e.msg))
class createListDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, size=(450, 400))
self.SetTitle(_(u"Create a new list"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
name = wx.StaticText(panel, -1, _(u"Name (20 characters maximun)"))
self.name = wx.TextCtrl(panel, -1)
nameSizer = wx.BoxSizer(wx.HORIZONTAL)
nameSizer.Add(name)
nameSizer.Add(self.name)
description = wx.StaticText(panel, -1, _(u"Description"))
self.description = wx.TextCtrl(panel, -1)
descriptionSizer = wx.BoxSizer(wx.HORIZONTAL)
descriptionSizer.Add(description)
descriptionSizer.Add(self.description)
mode = wx.StaticText(panel, -1, _(u"Mode"))
self.public = wx.RadioButton(panel, -1, _(u"Public"), style=wx.RB_GROUP)
self.private = wx.RadioButton(panel, -1, _(u"Private"))
modeBox = wx.BoxSizer(wx.HORIZONTAL)
modeBox.Add(mode)
modeBox.Add(self.public)
modeBox.Add(self.private)
ok = wx.Button(panel, wx.ID_OK)
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(ok)
btnBox.Add(cancel)
sizer.Add(nameSizer)
sizer.Add(descriptionSizer)
sizer.Add(modeBox)
sizer.Add(btnBox)
class editListDialog(createListDialog):
def __init__(self, list):
createListDialog.__init__(self)
self.SetTitle(_(u"Editing the list %s") % (list["name"]))
self.name.ChangeValue(list["name"])
self.description.ChangeValue(list["description"])
if list["mode"] == "public":
self.public.SetValue(True)
else:
self.private.SetValue(True)
class addUserListDialog(listViewer):
def __init__(self, parent):
listViewer.__init__(self, parent)
self.SetTitle(_(u"Select a list to add the user"))
self.createBtn.SetLabel(_(u"Add"))
self.createBtn.SetDefault()
self.editBtn.Disable()
self.view.Disable()
# self.subscriptors.Disable()
# self.members.Disable()
self.deleteBtn.Disable()
def onGo(self, ev):
self.EndModal(wx.ID_OK)
class removeUserListDialog(listViewer):
def __init__(self, parent):
listViewer.__init__(self, parent)
self.SetTitle(_(u"Select a list to remove the user"))
self.createBtn.SetLabel(_(u"Remove"))
self.createBtn.SetDefault()
self.editBtn.Disable()
self.view.Disable()
# self.subscriptors.Disable()
# self.members.Disable()
self.deleteBtn.Disable()
def onGo(self, ev):
self.EndModal(wx.ID_OK)

415
src/gui/dialogs/message.py Normal file
View File

@ -0,0 +1,415 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import twitter
import config
import output
import sound
import urlList
import url_shortener
import json
from mysc.thread_utils import call_threaded
from mysc.repeating_timer import RepeatingTimer
from twython import TwythonError
from extra import translator, AudioUploader
import platform
from extra.AudioUploader import transfer
if platform.system() != "Darwin":
from extra.AudioUploader import dropbox_transfer
from extra.SpellChecker import gui as spellCheckerGUI
class textLimited(wx.Dialog):
def __init__(self, message, title, text, parent):
wx.Dialog.__init__(self, parent)
self.twitter = parent.twitter
self.parent = parent
self.SetTitle(_(u"New tweet"))
self.panel = wx.Panel(self)
def createTextArea(self, message, text):
self.label = wx.StaticText(self.panel, -1, str(len(text)))
self.text = wx.TextCtrl(self.panel, -1, text)
font = self.text.GetFont()
dc = wx.WindowDC(self.text)
dc.SetFont(font)
x, y = dc.GetTextExtent("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
self.text.SetSize((x, y))
self.text.SetFocus()
if platform.system() != "Darwin":
self.text.Bind(wx.EVT_TEXT, self.onTimer)
self.textBox = wx.BoxSizer(wx.HORIZONTAL)
self.textBox.Add(self.label, 0, wx.ALL, 5)
self.textBox.Add(self.text, 0, wx.ALL, 5)
def onCheck(self, ev):
if platform.system() == "Darwin":
return
text = self.text.GetValue()
dlg = spellCheckerGUI.spellCheckerDialog(text, "")
if dlg.ShowModal() == wx.ID_OK:
self.text.ChangeValue(dlg.checker.get_text())
dlg.Destroy()
def onAttach(self, ev):
ev.Skip()
self.recording = AudioUploader.gui.audioDialog(self.parent)
if self.recording.ShowModal() != wx.ID_OK:
self.recording.cleanup()
return
self.recording.postprocess()
output.speak(_(u"Attaching..."))
self.uploaderDialog = AudioUploader.transfer_dialogs.UploadDialog(parent=self.parent, title=_(u"Uploading..."), filename=self.recording.file)
if self.recording.services.GetValue() == "Dropbox" and platform.system() != "Darwin":
self.uploader = dropbox_transfer.dropboxUploader(filename=self.recording.file, completed_callback=self.upload_completed, wxDialog=self.uploaderDialog)
elif self.recording.services.GetValue() == "SNDUp":
base_url = 'http://sndup.net/post.php'
if len(config.main["sound"]["sndup_api_key"]) > 0:
url = base_url + '?apikey=' + config.main['sound']['sndup_api_key']
else:
url = base_url
self.uploader = transfer.Upload(field='file', url=url, filename=self.recording.file, completed_callback=self.upload_completed, wxDialog=self.uploaderDialog)
self.uploaderDialog.Show()
self.uploader.perform_threaded()
def upload_completed(self):
url = self.uploader.get_url()
self.uploaderDialog.Destroy()
if url != 0:
self.text.SetValue(self.text.GetValue()+url+" #audio")
else:
output.speak(_(u"Unable to upload the audio"))
def onTranslate(self, ev):
dlg = translator.gui.translateDialog()
selection = dlg.ShowModal()
if selection != wx.ID_CANCEL:
text_to_translate = self.text.GetValue().encode("utf-8")
source = [x[0] for x in translator.available_languages()][dlg.source_lang.GetSelection()]
dest = [x[0] for x in translator.available_languages()][dlg.dest_lang.GetSelection()]
t = translator.translator.Translator()
t.from_lang = source
t.to_lang = dest
msg = t.translate(text_to_translate)
self.text.ChangeValue(msg)
output.speak(_(u"Translated"))
self.text.SetFocus()
else:
return
dlg.Destroy()
def onSelect(self, ev):
self.text.SelectAll()
def onShorten(self, ev):
urls = twitter.utils.find_urls_in_text(self.text.GetValue())
if len(urls) == 0:
output.speak(_(u"There's no URL to be shortened"))
elif len(urls) == 1:
self.text.SetValue(self.text.GetValue().replace(urls[0], url_shortener.shorten(urls[0])))
output.speak(_(u"URL shortened"))
elif len(urls) > 1:
urlList.shorten(urls, self).ShowModal()
self.text.SetFocus()
def onUnshorten(self, ev):
urls = twitter.utils.find_urls_in_text(self.text.GetValue())
if len(urls) == 0:
output.speak(_(u"There's no URL to be expanded"))
elif len(urls) == 1:
self.text.SetValue(self.text.GetValue().replace(urls[0], url_shortener.unshorten(urls[0])))
output.speak(_(u"URL expanded"))
elif len(urls) > 1:
urlList.unshorten(urls, self).ShowModal()
self.text.SetFocus()
def onTimer(self, ev):
self.label.SetLabel(str(len(self.text.GetValue())))
if len(self.text.GetValue()) > 1:
self.shortenButton.Enable()
self.unshortenButton.Enable()
else:
self.shortenButton.Disable()
self.unshortenButton.Disable()
if len(self.text.GetValue()) > 140:
sound.player.play("max_length.ogg")
self.okButton.Disable()
elif len(self.text.GetValue()) <= 140:
self.okButton.Enable()
def onCancel(self, ev):
self.Destroy()
class tweet(textLimited):
def createControls(self, message, title, text):
self.mainBox = wx.BoxSizer(wx.VERTICAL)
self.createTextArea(message, text)
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
self.upload_image = wx.Button(self.panel, -1, _(u"Upload a picture"), size=wx.DefaultSize)
self.upload_image.Bind(wx.EVT_BUTTON, self.onUpload_image)
if platform.system() != "Darwin":
self.spellcheck = wx.Button(self.panel, -1, _("Spelling correction"), size=wx.DefaultSize)
self.spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
self.attach = wx.Button(self.panel, -1, _(u"Attach audio"), size=wx.DefaultSize)
self.attach.Bind(wx.EVT_BUTTON, self.onAttach)
self.shortenButton = wx.Button(self.panel, -1, _(u"Shorten URL"), size=wx.DefaultSize)
self.shortenButton.Bind(wx.EVT_BUTTON, self.onShorten)
self.unshortenButton = wx.Button(self.panel, -1, _(u"Expand URL"), size=wx.DefaultSize)
self.unshortenButton.Bind(wx.EVT_BUTTON, self.onUnshorten)
self.shortenButton.Disable()
self.unshortenButton.Disable()
self.translateButton = wx.Button(self.panel, -1, _(u"Translate message"), size=wx.DefaultSize)
self.translateButton.Bind(wx.EVT_BUTTON, self.onTranslate)
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Send"), size=wx.DefaultSize)
self.okButton.Bind(wx.EVT_BUTTON, self.onSend)
self.okButton.SetDefault()
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"), size=wx.DefaultSize)
cancelButton.Bind(wx.EVT_BUTTON, self.onCancel)
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 5)
if platform.system() != "Darwin":
self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 5)
self.buttonsBox1.Add(self.attach, 0, wx.ALL, 5)
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 5)
self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 5)
self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 5)
self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 5)
self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 5)
self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL)
self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 5)
self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 5)
self.mainBox.Add(self.ok_cancelSizer)
selectId = wx.NewId()
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
self.accel_tbl = wx.AcceleratorTable([
(wx.ACCEL_CTRL, ord('A'), selectId),
])
self.SetAcceleratorTable(self.accel_tbl)
self.panel.SetSizer(self.mainBox)
def __init__(self, message, title, text, parent):
super(tweet, self).__init__(message, title, text, parent)
self.image = None
self.createControls(message, title, text)
self.onTimer(wx.EVT_CHAR_HOOK)
self.SetClientSize(self.mainBox.CalcMin())
def onUpload_image(self, ev):
if self.upload_image.GetLabel() == _(u"Discard image"):
self.image = None
del self.file
output.speak(_(u"Discarded"))
self.upload_image.SetLabel(_(u"Upload a picture"))
else:
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return
self.file = open(openFileDialog.GetPath(), "rb")
self.image = True
self.upload_image.SetLabel(_(u"Discard image"))
ev.Skip()
def onSend(self, ev):
self.EndModal(wx.ID_OK)
class retweet(tweet):
def __init__(self, message, title, text, parent):
super(retweet, self).__init__(message, title, text, parent)
# self.createControls(message, title, text)
self.in_reply_to = parent.db.settings[parent.name_buffer][parent.list.get_selected()]["id"]
self.text.SetInsertionPoint(0)
def onSend(self, ev):
self.EndModal(wx.ID_OK)
class dm(textLimited):
def createControls(self, message, title, text):
self.mainBox = wx.BoxSizer(wx.VERTICAL)
if self.parent.name_buffer == "followers" or self.parent.name_buffer == "friends":
list = [self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()]["screen_name"]]
else:
list =twitter.utils.get_all_users(self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()], self.parent.db)
label = wx.StaticText(self.panel, -1, _(u"Recipient"))
self.cb = wx.ComboBox(self.panel, -1, choices=list, value=list[0], size=wx.DefaultSize)
self.createTextArea(message, text)
self.mainBox.Add(self.cb, 0, wx.ALL, 5)
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
if platform.system() != "Darwin":
self.spellcheck = wx.Button(self.panel, -1, _("Spelling correction"), size=wx.DefaultSize)
self.spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
self.attach = wx.Button(self.panel, -1, _(u"Attach audio"), size=wx.DefaultSize)
self.attach.Bind(wx.EVT_BUTTON, self.onAttach)
self.shortenButton = wx.Button(self.panel, -1, _(u"Shorten URL"), size=wx.DefaultSize)
self.shortenButton.Bind(wx.EVT_BUTTON, self.onShorten)
self.unshortenButton = wx.Button(self.panel, -1, _(u"Expand URL"), size=wx.DefaultSize)
self.unshortenButton.Bind(wx.EVT_BUTTON, self.onUnshorten)
self.shortenButton.Disable()
self.unshortenButton.Disable()
self.translateButton = wx.Button(self.panel, -1, _(u"Translate message"), size=wx.DefaultSize)
self.translateButton.Bind(wx.EVT_BUTTON, self.onTranslate)
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Send"), size=wx.DefaultSize)
self.okButton.Bind(wx.EVT_BUTTON, self.onSend)
self.okButton.SetDefault()
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"), size=wx.DefaultSize)
cancelButton.Bind(wx.EVT_BUTTON, self.onCancel)
self.buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
if platform.system() != "Darwin":
self.buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
self.buttonsBox.Add(self.attach, 0, wx.ALL, 5)
self.mainBox.Add(self.buttonsBox, 0, wx.ALL, 5)
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsBox1.Add(self.shortenButton, 0, wx.ALL, 5)
self.buttonsBox1.Add(self.unshortenButton, 0, wx.ALL, 5)
self.buttonsBox1.Add(self.translateButton, 0, wx.ALL, 5)
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 5)
self.buttonsBox3 = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsBox3.Add(self.okButton, 0, wx.ALL, 5)
self.buttonsBox3.Add(cancelButton, 0, wx.ALL, 5)
self.mainBox.Add(self.buttonsBox3, 0, wx.ALL, 5)
self.panel.SetSizer(self.mainBox)
def __init__(self, message, title, text, parent):
super(dm, self).__init__(message, title, text, parent)
self.parent = parent
self.image = None
self.createControls(message, title, text)
self.onTimer(wx.EVT_CHAR_HOOK)
self.SetClientSize(self.mainBox.CalcMin())
def onSend(self, ev):
self.EndModal(wx.ID_OK)
class reply(tweet):
def __init__(self, message, title, text, parent):
super(reply, self).__init__(message, title, text, parent)
self.in_reply_to = parent.db.settings[parent.name_buffer][parent.list.get_selected()]["id"]
self.text.SetInsertionPoint(len(self.text.GetValue()))
self.mentionAll = wx.Button(self, -1, _(u"Mention to all"), size=wx.DefaultSize)
self.mentionAll.Disable()
self.mentionAll.Bind(wx.EVT_BUTTON, self.mentionAllUsers)
self.buttonsBox1.Add(self.mentionAll, 0, wx.ALL, 5)
self.buttonsBox1.Layout()
self.mainBox.Layout()
self.check_if_users()
self.SetClientSize(self.mainBox.CalcMin())
def check_if_users(self):
try:
if len(self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()]["entities"]["user_mentions"]) > 0:
self.mentionAll.Enable()
except KeyError:
pass
def mentionAllUsers(self, ev):
self.text.SetValue(self.text.GetValue()+twitter.utils.get_all_mentioned(self.parent.db.settings[self.parent.name_buffer][self.parent.list.get_selected()], self.parent.db))
self.text.SetInsertionPoint(len(self.text.GetValue()))
self.text.SetFocus()
def onSend(self, ev):
self.EndModal(wx.ID_OK)
class viewTweet(wx.Dialog):
def __init__(self, tweet):
super(viewTweet, self).__init__(None, size=(850,850))
self.SetTitle(_(u"Tweet - %i characters ") % (len(tweet)))
panel = wx.Panel(self)
label = wx.StaticText(panel, -1, _(u"Tweet"))
self.text = wx.TextCtrl(panel, -1, tweet, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
dc = wx.WindowDC(self.text)
dc.SetFont(self.text.GetFont())
(x, y, z) = dc.GetMultiLineTextExtent("0"*140)
self.text.SetSize((x, y))
self.text.SetFocus()
textBox = wx.BoxSizer(wx.HORIZONTAL)
textBox.Add(label, 0, wx.ALL, 5)
textBox.Add(self.text, 1, wx.EXPAND, 5)
mainBox = wx.BoxSizer(wx.VERTICAL)
mainBox.Add(textBox, 0, wx.ALL, 5)
if platform.system() != "Darwin":
spellcheck = wx.Button(panel, -1, _("Spelling correction"), size=wx.DefaultSize)
spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
self.unshortenButton = wx.Button(panel, -1, _(u"Expand URL"), size=wx.DefaultSize)
self.unshortenButton.Bind(wx.EVT_BUTTON, self.onUnshorten)
self.unshortenButton.Disable()
translateButton = wx.Button(panel, -1, _(u"Translate message"), size=wx.DefaultSize)
translateButton.Bind(wx.EVT_BUTTON, self.onTranslate)
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"Close"), size=wx.DefaultSize)
cancelButton.SetDefault()
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
if platform.system() != "Darwin":
buttonsBox.Add(spellcheck, 0, wx.ALL, 5)
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
buttonsBox.Add(translateButton, 0, wx.ALL, 5)
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
selectId = wx.NewId()
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
self.accel_tbl = wx.AcceleratorTable([
(wx.ACCEL_CTRL, ord('A'), selectId),
])
self.SetAcceleratorTable(self.accel_tbl)
panel.SetSizer(mainBox)
self.check_urls()
self.SetClientSize(mainBox.CalcMin())
def check_urls(self):
if len(twitter.utils.find_urls_in_text(self.text.GetValue())) > 0:
self.unshortenButton.Enable()
def onCheck(self, ev):
if platform.system() != "Darwin": return
text = self.text.GetValue()
dlg = spellCheckerGUI.spellCheckerDialog(text, "")
if dlg.ShowModal() == wx.ID_OK:
self.text.ChangeValue(dlg.checker.get_text())
dlg.Destroy()
def onTranslate(self, ev):
dlg = translator.gui.translateDialog()
selection = dlg.ShowModal()
if selection != wx.ID_CANCEL:
text_to_translate = self.text.GetValue().encode("utf-8")
source = [x[0] for x in translator.available_languages()][dlg.source_lang.GetSelection()]
dest = [x[0] for x in translator.available_languages()][dlg.dest_lang.GetSelection()]
t = translator.translator.Translator()
t.from_lang = source
t.to_lang = dest
msg = t.translate(text_to_translate)
self.text.ChangeValue(msg)
output.speak(_(u"Translated"))
self.text.SetFocus()
else:
return
dlg.Destroy()
def onSelect(self, ev):
self.text.SelectAll()
def onUnshorten(self, ev):
urls = twitter.utils.find_urls_in_text(self.text.GetValue())
if len(urls) == 0:
output.speak(_(u"There's no URL to be expanded"))
elif len(urls) == 1:
self.text.SetValue(self.text.GetValue().replace(urls[0], url_shortener.unshorten(urls[0])))
output.speak(_(u"URL expanded"))
elif len(urls) > 1:
urlList.unshorten(urls, self).ShowModal()
self.text.SetFocus()

48
src/gui/dialogs/search.py Normal file
View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
class searchDialog(wx.Dialog):
def __init__(self):
super(searchDialog, self).__init__(None, -1)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetTitle(_(u"Search on Twitter"))
label = wx.StaticText(panel, -1, _(u"Search"))
self.term = wx.TextCtrl(panel, -1,)
dc = wx.WindowDC(self.term)
dc.SetFont(self.term.GetFont())
self.term.SetSize(dc.GetTextExtent("0"*40))
sizer.Add(label, 0, wx.ALL, 5)
sizer.Add(self.term, 0, wx.ALL, 5)
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
self.users = wx.RadioButton(panel, -1, _(u"Users"))
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
radioSizer.Add(self.tweets, 0, wx.ALL, 5)
radioSizer.Add(self.users, 0, wx.ALL, 5)
sizer.Add(radioSizer, 0, wx.ALL, 5)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok, 0, wx.ALL, 5)
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())

View File

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx, twitter, config, gui.dialogs, sound, webbrowser
class showUserProfile(wx.Dialog):
def __init__(self, twitter, screen_name):
self.twitter = twitter
self.screen_name = screen_name
wx.Dialog.__init__(self, None, -1)
self.SetTitle(_(u"Information for %s") % (screen_name))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.get_data()
static = wx.StaticText(panel, -1, _(u"Details"))
sizer.Add(static, 0, wx.ALL, 5)
text = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE|wx.TE_READONLY)
# dc = wx.WindowDC(text)
# dc.SetFont(text.GetFont())
# (x, y, z) = dc.GetMultiLineTextExtent("0"*10000)
# text.SetSize((x, y))
text.SetFocus()
sizer.Add(text, 0, wx.ALL|wx.EXPAND, 5)
self.url = wx.Button(panel, -1, _(u"Go to URL"), size=wx.DefaultSize)
self.url.Bind(wx.EVT_BUTTON, self.onUrl)
self.url.Disable()
close = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
close.Bind(wx.EVT_BUTTON, self.onClose)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.Add(self.url, 0, wx.ALL, 5)
btnSizer.Add(close, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
text.ChangeValue(self.compose_string())
text.SetSize(text.GetBestSize())
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def onUrl(self, ev):
webbrowser.open(self.data["url"])
def onClose(self, ev):
self.Destroy()
def get_data(self):
try:
self.data = self.twitter.twitter.show_user(screen_name=self.screen_name)
except:
wx.MessageDialog(self, _(u"This user does not exist on Twitter"), _(u"Error"), wx.ICON_ERROR).ShowModal()
self.EndModal()
def compose_string(self):
string = u""
string = string + _(u"Username: @%s\n") % (self.data["screen_name"])
string = string + _(u"Name: %s\n") % (self.data["name"])
if self.data["location"] != "":
string = string + _(u"Location: %s\n") % (self.data["location"])
if self.data["url"] != None:
string = string+ _(u"URL: %s\n") % (self.data["url"])
self.url.Enable()
if self.data["description"] != "":
string = string+ _(u"Bio: %s\n") % (self.data["description"])
if self.data["protected"] == True: protected = _(u"Yes")
else: protected = _(u"No")
string = string+ _(u"Protected: %s\n") % (protected)
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data["followers_count"], self.data["friends_count"])
string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"])
string = string+ _(u"Favourites: %s") % (self.data["favourites_count"])
return string

View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
from twython import TwythonError
class updateProfile(wx.Dialog):
def __init__(self, parent):
self.twitter = parent.twitter
self.parent = parent
super(updateProfile, self).__init__(parent=None, id=-1)
self.SetTitle(_(u"Update your profile"))
panel = wx.Panel(self)
labelName = wx.StaticText(panel, -1, _(u"Name (20 characters maximum)"))
self.name = wx.TextCtrl(panel, -1)
self.name.SetFocus()
dc = wx.WindowDC(self.name)
dc.SetFont(self.name.GetFont())
self.name.SetSize(dc.GetTextExtent("0"*20))
labelLocation = wx.StaticText(panel, -1, _(u"Location"))
self.location = wx.TextCtrl(panel, -1)
dc = wx.WindowDC(self.location)
dc.SetFont(self.location.GetFont())
self.location.SetSize(dc.GetTextExtent("0"*35))
labelUrl = wx.StaticText(panel, -1, _(u"Website"))
self.url = wx.TextCtrl(panel, -1)
dc = wx.WindowDC(self.url)
dc.SetFont(self.url.GetFont())
self.url.SetSize(dc.GetTextExtent("0"*22))
labelDescription = wx.StaticText(panel, -1, _(u"Bio (160 characters maximum)"))
self.description = wx.TextCtrl(panel, -1, size=(400, 400))
dc = wx.WindowDC(self.description)
dc.SetFont(self.description.GetFont())
self.description.SetSize(dc.GetTextExtent("0"*160))
self.image = None
self.upload_image = wx.Button(panel, -1, _(u"Upload a picture"))
self.upload_image.Bind(wx.EVT_BUTTON, self.onUpload_picture)
ok = wx.Button(panel, wx.ID_OK, _(u"Update profile"))
ok.Bind(wx.EVT_BUTTON, self.onUpdateProfile)
ok.SetDefault()
close = wx.Button(panel, wx.ID_CANCEL, _("Close"))
sizer = wx.BoxSizer(wx.VERTICAL)
nameBox = wx.BoxSizer(wx.HORIZONTAL)
nameBox.Add(labelName, 0, wx.ALL, 5)
nameBox.Add(self.name, 0, wx.ALL, 5)
sizer.Add(nameBox, 0, wx.ALL, 5)
locationBox = wx.BoxSizer(wx.HORIZONTAL)
locationBox.Add(labelLocation, 0, wx.ALL, 5)
locationBox.Add(self.location, 0, wx.ALL, 5)
sizer.Add(locationBox, 0, wx.ALL, 5)
urlBox = wx.BoxSizer(wx.HORIZONTAL)
urlBox.Add(labelUrl, 0, wx.ALL, 5)
urlBox.Add(self.url, 0, wx.ALL, 5)
sizer.Add(urlBox, 0, wx.ALL, 5)
descriptionBox = wx.BoxSizer(wx.HORIZONTAL)
descriptionBox.Add(labelDescription, 0, wx.ALL, 5)
descriptionBox.Add(self.description, 0, wx.ALL, 5)
sizer.Add(descriptionBox, 0, wx.ALL, 5)
sizer.Add(self.upload_image, 5, wx.CENTER, 5)
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(ok, 0, wx.ALL, 5)
btnBox.Add(close, 0, wx.ALL, 5)
sizer.Add(btnBox, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
self.get_data()
def onUpload_picture(self, ev):
if self.upload_image.GetLabel() == _(u"Discard image"):
self.image = None
del self.file
output.speak(_(u"Discarded"))
self.upload_image.SetLabel(_(u"Upload a picture"))
else:
openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if openFileDialog.ShowModal() == wx.ID_CANCEL:
return
self.file = open(openFileDialog.GetPath(), "rb")
self.image = True
self.upload_image.SetLabel(_(u"Discard image"))
ev.Skip()
def onUpdateProfile(self, ev):
try:
if self.image != None:
self.twitter.twitter.update_profile_image(image=self.file)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
try:
f = self.twitter.twitter.update_profile(name=self.name.GetValue(), location=self.location.GetValue(), url=self.url.GetValue(), description=self.description.GetValue())
self.EndModal(wx.ID_OK)
except TwythonError as e:
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
return
def get_data(self):
data = self.twitter.twitter.show_user(screen_name=self.parent.db.settings["user_name"])
self.name.ChangeValue(data["name"])
if data["url"] != None:
self.url.ChangeValue(data["url"])
if len(data["location"]) > 0:
self.location.ChangeValue(data["location"])
if len(data["description"]) > 0:
self.description.ChangeValue(data["description"])

View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import webbrowser
import url_shortener
class urlList(wx.Dialog):
def __init__(self, urls):
self.urls = urls
super(urlList, self).__init__(parent=None, title=_(u"Select an URL"))
panel = wx.Panel(self)
# label = wx.StaticText(panel, -1, _(u"Select a URL"))
self.lista = wx.ListBox(panel, -1)
self.lista.SetFocus()
self.populate_list()
self.lista.SetSelection(0)
self.lista.SetSize(self.lista.GetBestSize())
sizer = wx.BoxSizer(wx.VERTICAL)
# sizer.Add(label, 0, wx.ALL, 5)
sizer.Add(self.lista, 0, wx.ALL, 5)
goBtn = wx.Button(panel, wx.ID_OK)
goBtn.SetDefault()
goBtn.Bind(wx.EVT_BUTTON, self.onGo)
cancelBtn = wx.Button(panel, wx.ID_CANCEL)
btnSizer = wx.BoxSizer()
btnSizer.Add(goBtn, 0, wx.ALL, 5)
btnSizer.Add(cancelBtn, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def onGo(self, ev):
webbrowser.open(self.lista.GetStringSelection())
self.Destroy()
def populate_list(self):
for i in self.urls:
self.lista.Append(i)
class shorten(urlList):
def __init__(self, urls, parent):
urlList.__init__(self, urls)
self.parent = parent
def onGo(self, ev):
self.parent.text.SetValue(self.parent.text.GetValue().replace(self.lista.GetStringSelection(), url_shortener.shorten(self.lista.GetStringSelection())))
self.Destroy()
class unshorten(shorten):
def __init__(self, urls, parent):
shorten.__init__(self, urls, parent)
def onGo(self, ev):
self.parent.text.SetValue(self.parent.text.GetValue().replace(self.lista.GetStringSelection(), url_shortener.unshorten(self.lista.GetStringSelection())))
self.Destroy()

49
src/gui/dialogs/utils.py Normal file
View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx, twitter, gui.dialogs, sound, config
from mysc.repeating_timer import RepeatingTimer
class selectUserDialog(wx.Dialog):
def __init__(self, parent, title):
self.parent = parent
wx.Dialog.__init__(self, None, -1)
panel = wx.Panel(self)
userSizer = wx.BoxSizer()
self.SetTitle(title)
if self.parent.nb.GetCurrentPage().name_buffer == "followers" or self.parent.nb.GetCurrentPage().name_buffer == "friends":
list = [self.parent.db.settings[self.parent.nb.GetCurrentPage().name_buffer][self.parent.nb.GetCurrentPage().list.get_selected()]["screen_name"]]
else:
try: list =twitter.utils.get_all_users(self.parent.db.settings[self.parent.nb.GetCurrentPage().name_buffer][self.parent.nb.GetCurrentPage().list.get_selected()], self.parent.db)
except KeyError: list = [self.parent.db.settings[self.parent.nb.GetCurrentPage().name_buffer][self.parent.nb.GetCurrentPage().list.get_selected()]["screen_name"]]
self.cb = wx.ComboBox(panel, -1, choices=list, value=list[0], size=wx.DefaultSize)
self.cb.SetFocus()
userSizer.Add(wx.StaticText(panel, -1, _(u"User")), 0, wx.ALL, 5)
userSizer.Add(self.cb)
sizer = wx.BoxSizer(wx.VERTICAL)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
# ok.Bind(wx.EVT_BUTTON, self.onok)
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
btnsizer = wx.BoxSizer()
btnsizer.Add(ok, 0, wx.ALL, 5)
btnsizer.Add(cancel, 0, wx.ALL, 5)
sizer.Add(userSizer, 0, wx.ALL, 5)
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())

969
src/gui/main.py Normal file
View File

@ -0,0 +1,969 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import dialogs
import buffers
import config
import twitter
import db
import webbrowser
import sound
import updater
import application
import os
import logging as original_logger
import output
import platform
import urllib2
import sysTrayIcon
import languageHandler
from issueReporter import gui as issueReporterGUI
from sessionmanager import manager
from mysc import event
from mysc.thread_utils import call_threaded
from twython import TwythonError
from urllib2 import URLError
from mysc.repeating_timer import RepeatingTimer
from mysc import localization
if platform.system() == "Windows" or platform.system() == "Linux":
from keyboard_handler.wx_handler import WXKeyboardHandler
from extra import SoundsTutorial
from keystrokeEditor import gui as keystrokeEditorGUI
log = original_logger.getLogger("gui.main")
class mainFrame(wx.Frame):
""" Main class of the Frame. This is the Main Window."""
### MENU
def makeMenus(self):
""" Creates, bind and returns the menu bar for the application. Also in this function, the accel table is created."""
menuBar = wx.MenuBar()
# Application menu
app = wx.Menu()
updateProfile = app.Append(wx.NewId(), _(u"&Update profile"))
self.Bind(wx.EVT_MENU, self.update_profile, updateProfile)
show_hide = app.Append(wx.NewId(), _(u"&Hide window"))
self.Bind(wx.EVT_MENU, self.show_hide, show_hide)
search = app.Append(wx.NewId(), _(u"&Search"))
self.Bind(wx.EVT_MENU, self.search, search)
lists = app.Append(wx.NewId(), _(u"&Lists manager"))
self.Bind(wx.EVT_MENU, self.list_manager, lists)
sounds_tutorial = app.Append(wx.NewId(), _(u"Sounds &tutorial"))
self.Bind(wx.EVT_MENU, self.learn_sounds, sounds_tutorial)
keystroke_editor = app.Append(wx.NewId(), _(u"&Edit keystrokes"))
self.Bind(wx.EVT_MENU, self.edit_keystrokes, keystroke_editor)
prefs = app.Append(wx.ID_PREFERENCES, _(u"&Preferences"))
self.Bind(wx.EVT_MENU, self.preferences, prefs)
close = app.Append(wx.ID_EXIT, _(u"E&xit"))
self.Bind(wx.EVT_MENU, self.close, close)
# Tweet menu
tweet = wx.Menu()
compose = tweet.Append(wx.NewId(), _(u"&Tweet"))
self.Bind(wx.EVT_MENU, self.compose, compose)
response = tweet.Append(wx.NewId(), _(u"Re&ply"))
self.Bind(wx.EVT_MENU, self.reply, response)
retweet = tweet.Append(wx.NewId(), _(u"&Retweet"))
self.Bind(wx.EVT_MENU, self.retweet, retweet)
fav = tweet.Append(wx.NewId(), _(u"Add to &favourites"))
self.Bind(wx.EVT_MENU, self.fav, fav)
unfav = tweet.Append(wx.NewId(), _(u"Remove from favo&urites"))
self.Bind(wx.EVT_MENU, self.unfav, unfav)
view = tweet.Append(wx.NewId(), _(u"&Show tweet"))
self.Bind(wx.EVT_MENU, self.view, view)
delete = tweet.Append(wx.NewId(), _(u"&Delete"))
self.Bind(wx.EVT_MENU, self.delete, delete)
# User menu
user = wx.Menu()
follow = user.Append(wx.NewId(), _(u"&Follow"))
self.Bind(wx.EVT_MENU, self.onFollow, follow)
unfollow = user.Append(wx.NewId(), _(u"&Unfollow"))
self.Bind(wx.EVT_MENU, self.onUnfollow, unfollow)
mute = user.Append(wx.NewId(), _(u"&Mute"))
self.Bind(wx.EVT_MENU, self.onMute, mute)
unmute = user.Append(wx.NewId(), _(u"U&nmute"))
self.Bind(wx.EVT_MENU, self.onUnmute, unmute)
report = user.Append(wx.NewId(), _(u"&Report as spam"))
self.Bind(wx.EVT_MENU, self.onReport, report)
block = user.Append(wx.NewId(), _(u"&Block"))
self.Bind(wx.EVT_MENU, self.onBlock, block)
unblock = user.Append(wx.NewId(), _(u"Unb&lock"))
self.Bind(wx.EVT_MENU, self.onUnblock, unblock)
dm = user.Append(wx.NewId(), _(u"Direct me&ssage"))
self.Bind(wx.EVT_MENU, self.dm, dm)
addToList = user.Append(wx.NewId(), _(u"&Add to list"))
self.Bind(wx.EVT_MENU, self.add_to_list, addToList)
removeFromList = user.Append(wx.NewId(), _(u"R&emove from list"))
self.Bind(wx.EVT_MENU, self.remove_from_list, removeFromList)
viewLists = user.Append(wx.NewId(), _(u"&View lists"))
self.Bind(wx.EVT_MENU, self.view_user_lists, viewLists)
details = user.Append(wx.NewId(), _(u"Show user &profile"))
self.Bind(wx.EVT_MENU, self.details, details)
timeline = user.Append(wx.NewId(), _(u"&Timeline"))
self.Bind(wx.EVT_MENU, self.open_timeline, timeline)
favs = user.Append(wx.NewId(), _(u"V&iew favourites"))
self.Bind(wx.EVT_MENU, self.favs_timeline, favs)
# buffer menu
buffer = wx.Menu()
mute = buffer.Append(wx.NewId(), _(u"&Mute"))
self.Bind(wx.EVT_MENU, self.toggle_mute, mute)
autoread = buffer.Append(wx.NewId(), _(u"&Autoread tweets for this buffer"))
self.Bind(wx.EVT_MENU, self.toggle_autoread, autoread)
clear = buffer.Append(wx.NewId(), _(u"&Clear buffer"))
self.Bind(wx.EVT_MENU, self.clear_list, clear)
deleteTl = buffer.Append(wx.NewId(), _(u"&Remove buffer"))
self.Bind(wx.EVT_MENU, self.delete_buffer, deleteTl)
# Help Menu
help = wx.Menu()
doc = help.Append(-1, _(u"&Documentation"))
self.Bind(wx.EVT_MENU, self.onManual, doc)
changelog = help.Append(wx.NewId(), _(u"&What's new in this version?"))
self.Bind(wx.EVT_MENU, self.onChangelog, changelog)
check_for_updates = help.Append(wx.NewId(), _(u"&Check for updates"))
self.Bind(wx.EVT_MENU, self.onCheckForUpdates, check_for_updates)
reportError = help.Append(wx.NewId(), _(u"&Report an error"))
self.Bind(wx.EVT_MENU, self.onReportBug, reportError)
visit_website = help.Append(-1, _(u"TW Blue &website"))
self.Bind(wx.EVT_MENU, self.onVisit_website, visit_website)
about = help.Append(-1, _(u"About &TW Blue"))
self.Bind(wx.EVT_MENU, self.onAbout, about)
# Add all to the menu Bar
menuBar.Append(app, _(u"&Application"))
menuBar.Append(tweet, _(u"&Tweet"))
menuBar.Append(user, _(u"&User"))
menuBar.Append(buffer, _(u"&Buffer"))
menuBar.Append(help, _(u"&Help"))
downID = wx.NewId()
upID = wx.NewId()
leftID = wx.NewId()
rightID = wx.NewId()
if platform.system() == "Darwin":
self.Bind(wx.EVT_MENU, self.down, id=downID)
self.Bind(wx.EVT_MENU, self.up, id=upID)
self.Bind(wx.EVT_MENU, self.left, id=leftID)
self.Bind(wx.EVT_MENU, self.right, id=rightID)
# Creates the acceleration table.
self.accel_tbl = wx.AcceleratorTable([
(wx.ACCEL_CTRL, ord('N'), compose.GetId()),
(wx.ACCEL_CTRL, ord('R'), response.GetId()),
(wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('R'), retweet.GetId()),
(wx.ACCEL_CTRL, ord('F'), fav.GetId()),
(wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('F'), unfav.GetId()),
(wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('V'), view.GetId()),
(wx.ACCEL_CTRL, ord('D'), dm.GetId()),
(wx.ACCEL_CTRL, ord('Q'), close.GetId()),
(wx.ACCEL_CTRL, ord('S'), follow.GetId()),
(wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('S'), unfollow.GetId()),
(wx.ACCEL_CTRL, ord('K'), block.GetId()),
(wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('K'), report.GetId()),
(wx.ACCEL_CTRL, ord('I'), timeline.GetId()),
(wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord('I'), deleteTl.GetId()),
(wx.ACCEL_CTRL, ord('M'), show_hide.GetId()),
(wx.ACCEL_CTRL, ord('P'), updateProfile.GetId()),
(wx.ACCEL_CTRL, wx.WXK_DOWN, downID),
(wx.ACCEL_CTRL, wx.WXK_UP, upID),
(wx.ACCEL_CTRL, wx.WXK_LEFT, leftID),
(wx.ACCEL_CTRL, wx.WXK_RIGHT, rightID),
])
self.SetAcceleratorTable(self.accel_tbl)
return menuBar
### MAIN
def __init__(self, authorised=True, user_key=None, user_secret=None):
""" Main function of this class."""
if authorised == False:
self.user_key = user_key
self.user_secret = user_secret
else:
self.user_key = self.user_secret = None
log.debug("Loading temporal database...")
self.db = db.db()
# Gets the twitter object for future calls to the twitter Rest API.
log.debug("Getting Twitter's Rest API...")
self.twitter = twitter.twitter.twitter()
super(mainFrame, self).__init__(None, -1, "TW Blue", size=(1600, 1600))
self.Bind(wx.EVT_QUERY_END_SESSION, self.exit)
self.Bind(wx.EVT_END_SESSION, self.exit)
log.debug(u"Creating the system tray icon... ")
sysTray=sysTrayIcon.SysTrayIcon(self)
panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetTitle("TW Blue")
try:
updater.update_manager.check_for_update()
except:
pass
self.SetMenuBar(self.makeMenus())
self.setup_twitter(panel)
def logging_in_twblue(self, panel):
log.debug("Retrieving username...")
twitter.starting.start_user_info(config=self.db, twitter=self.twitter)
config.main["twitter"]["user_name"] = self.db.settings["user_name"]
self.SetTitle(u"@%s. - TW Blue" % (self.db.settings["user_name"]))
self.nb = wx.Treebook(panel, -1)
self.Bind(wx.EVT_CLOSE, self.close)
self.nb.Bind(wx.EVT_TREEBOOK_PAGE_CHANGED, self.onPageChanged)
# Gets the tabs for home, mentions, send and direct messages.
log.debug("Creating buffers...")
self.db.settings["buffers"] = []
account = buffers.accountPanel(self.nb)
self.nb.AddPage(account, self.db.settings["user_name"])
self.db.settings["buffers"].append(self.db.settings["user_name"])
account_index = self.db.settings["buffers"].index(self.db.settings["user_name"])
home = buffers.basePanel(self.nb, self, "home_timeline", self.twitter.twitter.get_home_timeline, sound="tweet_received.ogg")
self.nb.InsertSubPage(account_index, home, _(u"Home"))
self.db.settings["buffers"].append("home_timeline")
self.nb.SetSelection(1)
self.nb.GetPage(1).list.list.SetFocus()
mentionsP = buffers.basePanel(self.nb, self, "mentions", self.twitter.twitter.get_mentions_timeline, sound="mention_received.ogg")
self.nb.InsertSubPage(account_index, mentionsP, _("Mentions"))
self.db.settings["buffers"].append("mentions")
dms = buffers.dmPanel(self.nb, self, "direct_messages", self.twitter.twitter.get_direct_messages, sound="dm_received.ogg")
self.nb.InsertSubPage(account_index, dms, _(u"Direct messages"))
self.db.settings["buffers"].append("direct_messages")
sent = buffers.basePanel(self.nb, self, "sent", self.twitter.twitter.get_user_timeline, argumento=self.db.settings["user_name"])
self.nb.InsertSubPage(account_index, sent, _(u"Sent"))
self.db.settings["buffers"].append("sent")
# If the user has enabled favs from config.
if config.main["other_buffers"]["show_favourites"] == True:
log.debug("Getting Favorited tweets...")
favs = buffers.basePanel(self.nb, self, "favs", self.twitter.twitter.get_favorites)
self.nb.InsertSubPage(account_index, favs, _(u"Favourites"))
self.db.settings["buffers"].append("favs")
# If followers are enabled from config.
if config.main["other_buffers"]["show_followers"] == True:
log.debug("Getting followers...")
followers = buffers.peoplePanel(self.nb, self, "followers", self.twitter.twitter.get_followers_list, argumento=self.db.settings["user_name"], sound="update_followers.ogg")
self.nb.InsertSubPage(account_index, followers, _(u"Followers"))
self.db.settings["buffers"].append("followers")
# Same here but for friends.
if config.main["other_buffers"]["show_friends"] == True:
log.debug("Getting friends...")
friends = buffers.peoplePanel(self.nb, self, "friends", self.twitter.twitter.get_friends_list, argumento=self.db.settings["user_name"])
self.nb.InsertSubPage(account_index, friends, _(u"Friends"))
self.db.settings["buffers"].append("friends")
if config.main["other_buffers"]["show_blocks"] == True:
blocked = buffers.peoplePanel(self.nb, self, "blocks", self.twitter.twitter.list_blocks)
self.nb.InsertSubPage(account_index, blocked, _(u"Blocked users"))
self.db.settings["buffers"].append("blocks")
if config.main["other_buffers"]["show_muted_users"] == True:
muteds = buffers.peoplePanel(self.nb, self, "muteds", self.twitter.twitter.get_muted_users_list)
self.nb.InsertSubPage(account_index, muteds, _(u"Muted users"))
self.db.settings["buffers"].append("muteds")
if config.main["other_buffers"]["show_events"] == True:
evt = buffers.eventsPanel(self.nb, self, sound="new_event.ogg")
self.nb.InsertSubPage(account_index, evt, _(u"Events"))
self.db.settings["buffers"].append("events")
searches = buffers.emptyPanel(self.nb)
self.nb.InsertSubPage(account_index, searches, _(u"Searches"))
self.db.settings["buffers"].append("searches")
for i in config.main["other_buffers"]["tweet_searches"]:
self.nb.InsertSubPage(self.db.settings["buffers"].index("searches"), buffers.searchPanel(self.nb, self, "%s-search" % (i,), q=i, count=100), _(u"Search for %s" % (i,)))
self.db.settings["buffers"].append("%s-search" % (i,))
timelines = buffers.emptyPanel(self.nb)
self.nb.InsertSubPage(account_index, timelines, _(u"Timelines"))
self.db.settings["buffers"].append("timelines")
for i in config.main["other_buffers"]["timelines"]:
self.nb.InsertSubPage(self.db.settings["buffers"].index("timelines"), buffers.basePanel(self.nb, self, i, self.twitter.twitter.get_user_timeline, argumento=i, timeline=True, sound="tweet_timeline.ogg"), _(u"Timeline for %s") % i)
self.db.settings["buffers"].append(i)
lists = buffers.emptyPanel(self.nb)
self.nb.InsertSubPage(account_index, lists, _(u"Lists"))
self.db.settings["buffers"].append("lists")
for i in config.main["other_buffers"]["lists"]:
self.nb.InsertSubPage(self.db.settings["buffers"].index("lists"), buffers.listPanel(self.nb, self, i+"-list", argumento=twitter.utils.find_list(i, self.db.settings["lists"])), _(u"List for %s") % i)
self.db.settings["buffers"].append(i+"-list")
## favourites timelines
favs_timelines = buffers.emptyPanel(self.nb)
self.nb.InsertSubPage(account_index, favs_timelines, _(U"Favourites timelines"))
self.db.settings["buffers"].append("favourites_timelines")
for i in config.main["other_buffers"]["favourites_timelines"]:
self.nb.InsertSubPage(self.db.settings["buffers"].index("favourites_timelines"), buffers.favsPanel(self.nb, self, i+"favs", argumento=i, sound="favourites_timeline_updated.ogg"), _(u"Favourites for %s") % i,)
self.db.settings["buffers"].append(i+"favs")
self.fav_stream = RepeatingTimer(180, self.get_fav_buffers)
self.fav_stream.start()
self.sizer.Add(self.nb, 0, wx.ALL, 5)
panel.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())
self.Bind(event.MyEVT_STARTED, self.onInit)
self.Bind(event.EVT_RESULT, self.onMemberAdded)
call_threaded(self.init, run_streams=True)
def init(self, run_streams=False):
""" Calls the start_stream function for each stream tab."""
deleted = 0
for i in range(0, self.nb.GetPageCount()):
if self.nb.GetPage(i).type == "account" or self.nb.GetPage(i).type == "empty": continue
if i == self.nb.GetPageCount() and deleted > 0:
i = i-1
deleted = deleted-1
log.debug("Starting stream for %s..." % self.nb.GetPage(i).name_buffer)
info_event = event.infoEvent(event.EVT_STARTED, 1)
try:
if self.nb.GetPage(i).type == "search":
self.nb.GetPage(i).timer = RepeatingTimer(180, self.nb.GetPage(i).load_search)
self.nb.GetPage(i).timer.start()
num = self.nb.GetPage(i).start_streams()
info_event.SetItem(i, num)
wx.PostEvent(self, info_event)
except TwythonError as e:
continue
except UnicodeDecodeError: # This happens when there is a bad internet connection
continue
output.speak(_(u"Ready"))
if run_streams == True:
self.get_home()
self.get_tls()
self.check_streams = RepeatingTimer(config.main["general"]["time_to_check_streams"], self.check_stream_up)
self.check_streams.start()
# If all it's done, then play a nice sound saying that all it's OK.
sound.player.play("ready.ogg")
def remove_list(self, id):
for i in range(0, self.nb.GetPageCount()):
if self.nb.GetPage(i).type == "list":
if self.nb.GetPage(i).argumento == id:
pos = self.nb.GetCurrentPage().remove_invalid_buffer()
if pos != None:
self.nb.DeletePage(pos)
self.onMemberAdded()
def onMemberAdded(self, ev):
self.stream2.disconnect()
del self.stream2
self.get_tls()
def get_fav_buffers(self):
for i in config.main["other_buffers"]["favourites_timelines"]:
num = self.nb.GetPage(self.db.settings["buffers"].index(i+"favs")).start_streams()
if num > 0: output.speak(_(u"%s favourites from %s") % (nun, i))
def setup_twitter(self, panel):
""" Setting up the connection for twitter, or authenticate if the config file has valid credentials."""
# try:
self.twitter.login(self.user_key, self.user_secret)
self.logging_in_twblue(panel)
log.info("Authorized in Twitter.")
del self.user_key; del self.user_secret
# except:
# dlg1 = wx.MessageDialog(panel, _(u"Connection error. Try again later."), _(u"Error!"), wx.ICON_ERROR)
# dlg1.ShowModal()
# self.Close(True)
def get_home(self):
""" Gets the home stream, that manages home timeline, mentions, direct messages and sent."""
try:
self.stream = twitter.buffers.stream.streamer(application.app_key, application.app_secret, config.main["twitter"]["user_key"], config.main["twitter"]["user_secret"], parent=self)
call_threaded(self.stream.user)
except:
self.stream.disconnect()
def start_lists(self):
for i in range(0, self.nb.GetPageCount()):
if self.nb.GetPage(i).type == "list": self.nb.GetPage(i).retrieve_ids()
def get_tls(self):
""" Setting the stream for individual user timelines."""
try:
self.stream2 = twitter.buffers.indibidual.streamer(application.app_key, application.app_secret, config.main["twitter"]["user_key"], config.main["twitter"]["user_secret"], parent=self)
# The self.ids contains all IDS for the follow argument of the stream.
ids = ""
# If we have more than 0 items on a list, then.
for i in config.main["other_buffers"]["timelines"]:
ids = ids+self.db.settings[i][0]["user"]["id_str"]+", "
for i in range(0, self.nb.GetPageCount()):
if self.nb.GetPage(i).type == "list":
for z in self.nb.GetPage(i).users:
ids+= str(z)+", "
if ids != "":
# try:
call_threaded(self.stream2.statuses.filter, follow=ids)
# except:
# pass
except:
self.stream2.disconnect()
def check_stream_up(self):
try:
urllib2.urlopen("http://74.125.228.231", timeout=5)
except urllib2.URLError:
if self.stream.connected == True: self.stream.disconnect()
if hasattr(self, "stream2") and self.stream2.connected: self.stream2.disconnect()
if config.main["general"]["announce_stream_status"] == True: output.speak(_(u"Streams disconnected. TW Blue will try to reconnect in a minute."))
return
if self.stream.connected == False:
del self.stream
if config.main["general"]["announce_stream_status"] == True: output.speak(_(u"Reconnecting streams..."))
call_threaded(self.init)
self.get_home()
if hasattr(self, "stream2") and self.stream2.connected == False:
log.debug("Trying reconnects the timelines stream...")
del self.stream2
self.get_tls()
### Events
def edit_keystrokes(self, ev=None):
if hasattr(self, "keyboard_handler"):
dlg = keystrokeEditorGUI.keystrokeEditor(parent=self, keyboard_handler=self.keyboard_handler)
else:
dlg = keystrokeEditorGUI.keystrokeEditor(parent=self)
dlg.ShowModal()
dlg.Destroy()
def search(self, ev=None):
dlg = dialogs.search.searchDialog()
if dlg.ShowModal() == wx.ID_OK:
term = dlg.term.GetValue()
if dlg.tweets.GetValue() == True:
search =buffers.searchPanel(self.nb, self, "%s-search" % (term,), q=term, count=100)
self.nb.InsertSubPage(self.db.settings["buffers"].index("searches"), search, _(u"search for %s") % (term,))
self.db.settings["buffers"].append("%s-search" % (term,))
config.main["other_buffers"]["tweet_searches"].append(term)
elif dlg.users.GetValue() == True:
search =buffers.searchUsersPanel(self.nb, self, "%s_search" % (term,), q=term, count=20)
self.nb.InsertSubPage(self.db.settings["buffers"].index("searches"), search, _(u"search users for %s") % (term,))
self.db.settings["buffers"].append("%s_search" % (term,))
timer = RepeatingTimer(180, search.load_search)
timer.start()
num = search.start_streams()
search.put_items(num)
dlg.Destroy()
def learn_sounds(self, ev):
SoundsTutorial.gui.soundsTutorial().ShowModal()
def view_user_lists(self, ev=None):
userDlg = dialogs.utils.selectUserDialog(parent=self, title=_(u"Select the user"))
if userDlg.ShowModal() == wx.ID_OK:
user = userDlg.cb.GetValue()
else:
return
dlg = dialogs.lists.userListViewer(self, user)
dlg.ShowModal()
userDlg.Destroy()
dlg.Destroy()
def add_to_list(self, ev=None):
userDlg = dialogs.utils.selectUserDialog(parent=self, title=_(u"Select the user"))
if userDlg.ShowModal() == wx.ID_OK:
user = userDlg.cb.GetValue()
else:
return
dlg = dialogs.lists.addUserListDialog(self)
if dlg.ShowModal() == wx.ID_OK:
try:
list = self.twitter.twitter.add_list_member(list_id=self.db.settings["lists"][dlg.lista.get_selected()]["id"], screen_name=user)
older_list = twitter.utils.find_item(self.db.settings["lists"][dlg.lista.get_selected()]["id"], self.db.settings["lists"])
if list["mode"] == "private":
self.db.settings["lists"].pop(older_list)
self.db.settings["lists"].append(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
userDlg.Destroy()
dlg.Destroy()
def remove_from_list(self, ev=None):
userDlg = dialogs.utils.selectUserDialog(parent=self, title=_(u"Select the user"))
if userDlg.ShowModal() == wx.ID_OK:
user = userDlg.cb.GetValue()
else:
return
dlg = dialogs.lists.removeUserListDialog(self)
if dlg.ShowModal() == wx.ID_OK:
try:
list = self.twitter.twitter.delete_list_member(list_id=self.db.settings["lists"][dlg.get_selected()]["id"], screen_name=user)
older_list = twitter.utils.find_item(self.db.settings["lists"][dlg.get_selected()]["id"], self.db.settings["lists"])
if list["mode"] == "private":
self.db.settings["lists"].pop(older_list)
self.db.settings["lists"].append(list)
except TwythonError as e:
output.speak("error %s: %s" % (e.error_code, e.msg))
userDlg.Destroy()
dlg.Destroy()
def list_manager(self, ev):
dlg = dialogs.lists.listViewer(self)
dlg.ShowModal()
self.stream2.disconnect()
del self.stream2
self.get_tls()
dlg.Destroy()
def onInit(self, ev):
if self.nb.GetPage(ev.GetItem()[0]).type != "search" or self.nb.GetPage(ev.GetItem()[0]).type != "favourites_timeline": self.nb.GetPage(ev.GetItem()[0]).put_items(ev.GetItem()[1])
def preferences(self, ev=None):
dlg = dialogs.configuration.configurationDialog(self)
dlg.ShowModal()
dlg.Destroy()
def update_profile(self, ev=None):
dialogs.update_profile.updateProfile(self).ShowModal()
def onManual(self, ev):
lang = localization.get("documentation")
os.chdir("documentation/%s" % (lang,))
webbrowser.open("manual.html")
os.chdir("../../")
def onChangelog(self, ev):
lang = localization.get("documentation")
os.chdir("documentation/%s" % (lang,))
webbrowser.open("changes.html")
os.chdir("../../")
def onVisit_website(self, ev):
webbrowser.open("http://twblue.com.mx")
def onReportBug(self, ev):
issueReporterGUI.reportBug(self.db.settings["user_name"]).ShowModal()
def onCheckForUpdates(self, ev):
updater.update_manager.check_for_update(msg=True)
def details(self, ev=None):
""" This function shows details for the selected user."""
dlg = dialogs.utils.selectUserDialog(parent=self, title=_(u"User details"))
if dlg.ShowModal() == wx.ID_OK:
dialogs.show_user.showUserProfile(self.twitter, dlg.cb.GetValue()).ShowModal()
dlg.Destroy()
def delete(self, ev=None):
""" Deleting a tweet or direct message."""
if self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage() != "friends" and self.nb.GetCurrentPage().name_buffer != "events":
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this message? It will be eliminated from Twitter as well."), _(u"Delete"), wx.ICON_QUESTION|wx.YES_NO)
if dlg.ShowModal() == wx.ID_YES:
self.nb.GetCurrentPage().destroy_status(wx.EVT_MENU)
else:
return
def onPageChanged(self, ev):
""" Announces the new title for the tab."""
if platform.system() == "Darwin":
output.speak(self.nb.GetPageText(self.nb.GetSelection())+",", True)
def skip_blank_pages(self, forward=True):
if self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty" and (self.showing == False or platform.system() == "Darwin"):
self.nb.AdvanceSelection(forward)
def close(self, ev=None):
# if ev == None or hasattr(ev, "GetLoggingOff") == False:
dlg = wx.MessageDialog(self, _(u"Do you really want to close TW Blue?"), _(u"Exit"), wx.YES_NO|wx.ICON_QUESTION)
if dlg.ShowModal() == wx.ID_YES:
self.exit()
dlg.Destroy()
# elif hasattr(ev, "GetLoggingOff") == True:
# self.exit()
def exit(self, event=None):
config.main.write()
log.debug("Exiting...")
try:
self.check_streams.cancel()
except AttributeError:
pass
sound.player.cleaner.cancel()
try:
self.stream.disconnect()
log.debug("Stream disconnected.")
except:
pass
try:
self.stream2.disconnect()
log.debug(u"Timelines stream disconnected.")
except:
pass
wx.GetApp().ExitMainLoop()
def onFollow(self, ev=None):
""" Opens the follow dialog."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "follow").ShowModal()
def onUnfollow(self, ev=None):
""" Opens the unfollow dialog."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "unfollow").ShowModal()
def onMute(self, ev=None):
""" Opens the unfollow dialog."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "mute").ShowModal()
def onUnmute(self, ev=None):
""" Opens the unfollow dialog."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "unmute").ShowModal()
def onReport(self, ev=None):
""" Opens the report dialog, to report as spam to the specified user."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "report").ShowModal()
def onBlock(self, ev=None):
""" Opens the "block" dialog, to block the user that you want."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "block").ShowModal()
def onUnblock(self, ev=None):
""" Opens the "block" dialog, to block the user that you want."""
dialogs.follow.follow(self.nb.GetCurrentPage(), "unblock").ShowModal()
def action(self, ev=None):
dialogs.follow.follow(self.nb.GetCurrentPage()).ShowModal()
def compose(self, ev=None):
""" Opens the new tweet dialog."""
self.nb.GetCurrentPage().post_status(ev)
def reply(self, ev=None):
""" Opens the response dialog."""
self.nb.GetCurrentPage().onResponse(ev)
def dm(self, ev=None):
""" Opens the DM Dialog."""
# The direct_messages buffer has a method to post a diret messages while the other tabs does has not it.
if self.nb.GetCurrentPage().name_buffer == "direct_messages":
self.nb.GetCurrentPage().onResponse(ev)
elif self.nb.GetCurrentPage().name_buffer == "events": return
else:
# dialogs.message.dm(_(u"Direct message to %s ") % (self.db.settings[self.nb.GetCurrentPage().name_buffer][self.nb.GetCurrentPage().get_selected()]["user"]["screen_name"]), "", "", self.nb.GetCurrentPage()).ShowModal()
self.nb.GetCurrentPage().onDm(ev)
def retweet(self, ev=None):
if self.nb.GetCurrentPage().name_buffer != "direct_messages" and self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage().name_buffer != "friends" and self.nb.GetCurrentPage().name_buffer != "events":
self.nb.GetCurrentPage().onRetweet(ev)
def view(self, ev=None):
tweet = self.nb.GetCurrentPage().get_message(dialog=True)
dialogs.message.viewTweet(tweet).ShowModal()
def fav(self, ev=None):
if self.nb.GetCurrentPage().name_buffer != "direct_messages" and self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage().name_buffer != "friends":
try:
self.twitter.twitter.create_favorite(id=self.db.settings[self.nb.GetCurrentPage().name_buffer][self.nb.GetCurrentPage().list.get_selected()]["id"])
sound.player.play("favourite.ogg")
except TwythonError as e:
output.speak(_(u"Error while adding to favourites."), True)
sound.player.play("error.ogg")
def unfav(self, ev=None):
if self.nb.GetCurrentPage().name_buffer != "direct_messages" and self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage().name_buffer != "friends":
try:
self.twitter.twitter.destroy_favorite(id=self.db.settings[self.nb.GetCurrentPage().name_buffer][self.nb.GetCurrentPage().list.get_selected()]["id"])
except TwythonError as e:
output.speak(_(u"Error while removing from favourites."), True)
sound.player.play("error.ogg")
def open_timeline(self, ev=None):
dlg = dialogs.utils.selectUserDialog(self, _(u"Individual timeline"))
if dlg.ShowModal() == wx.ID_OK:
user = twitter.utils.if_user_exists(self.twitter.twitter, dlg.cb.GetValue())
if user == None:
wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
dlg.Destroy()
return
if user not in config.main["other_buffers"]["timelines"]:
config.main["other_buffers"]["timelines"].append(user)
else:
wx.MessageDialog(None, _(u"There's currently a timeline for this user. You are not able to open another"), _(u"Existing timeline"), wx.ICON_ERROR).ShowModal()
dlg.Destroy()
return
sound.player.play("create_timeline.ogg")
st = buffers.basePanel(self.nb, self, user, self.twitter.twitter.get_user_timeline, argumento=user, sound="ready.ogg", timeline=True)
num = st.start_streams()
self.db.settings["buffers"].append(user)
if num == 0:
wx.MessageDialog(None, _(u"This user has no tweets. You can't open a timeline for this user"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
self.db.settings.pop(user)
# self.nb.DeletePage(self.db.settings["buffers"].index(user))
self.db.settings["buffers"].remove(user)
else:
self.nb.InsertSubPage(self.db.settings["buffers"].index("timelines"), st, _(u"Timeline for %s") % (user))
st.put_items(num)
st.sound = "tweet_timeline.ogg"
self.stream2.disconnect()
del self.stream2
self.get_tls()
dlg.Destroy()
def favs_timeline(self, ev=None):
dlg = dialogs.utils.selectUserDialog(self, _(u"List of favourites"))
if dlg.ShowModal() == wx.ID_OK:
user = twitter.utils.if_user_exists(self.twitter.twitter, dlg.cb.GetValue())
if user == None:
wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
dlg.Destroy()
return
if user not in config.main["other_buffers"]["favourites_timelines"]:
config.main["other_buffers"]["favourites_timelines"].append(user)
else:
wx.MessageDialog(None, _(u"There's already a list of favourites for this user. You can't create another."), _(u"Existing list"), wx.ICON_ERROR).ShowModal()
dlg.Destroy()
return
sound.player.play("create_timeline.ogg")
st = buffers.favsPanel(self.nb, self, user+"-favs", argumento=user, sound="favourites_timeline_updated.ogg")
self.nb.InsertSubPage(self.db.settings["buffers"].index("favourites_timelines"), st, _(u"Favourites for %s") % (user))
num = st.start_streams()
self.db.settings["buffers"].append(user+"-favs")
if num == 0:
wx.MessageDialog(None, _(u"This user has no favourites. You can't create a list of favourites for this user."), _(u"Error!"), wx.ICON_ERROR).ShowModal()
self.db.settings.pop(user+"-favs")
self.nb.DeletePage(self.db.settings["buffers"].index(user+"-favs"))
self.db.settings["buffers"].remove(user+"-favs")
st.put_items(num)
dlg.Destroy()
def onAbout(self, ev=None):
info = wx.AboutDialogInfo()
info.SetName(application.name)
info.SetVersion(application.version)
info.SetDescription(application.description)
info.SetCopyright(application.copyright)
info.SetTranslators(application.translators)
# info.SetLicence(application.licence)
info.AddDeveloper(application.author)
wx.AboutBox(info)
def delete_buffer(self, ev=None):
pos = self.nb.GetCurrentPage().remove_buffer()
if pos != None:
self.stream2.disconnect()
del self.stream2
self.nb.DeletePage(self.nb.GetSelection())
sound.player.play("delete_timeline.ogg")
self.get_tls()
def delete_invalid_timeline(self):
pos = self.nb.GetCurrentPage().remove_invalid_buffer()
if pos != None:
self.nb.DeletePage(self.nb.GetSelection())
### Hidden Window
def left(self, event=None):
num = self.nb.GetSelection()
if num == 0:
self.nb.ChangeSelection(self.nb.GetPageCount()-1)
else:
self.nb.SetSelection(num-1)
while self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty": self.skip_blank_pages(False)
try:
msg = _(u"%s, %s of %s") % (self.nb.GetPageText(self.nb.GetSelection()), self.nb.GetCurrentPage().list.get_selected()+1, self.nb.GetCurrentPage().list.get_count())
except:
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
output.speak(msg, 1)
def right(self, event=None):
num = self.nb.GetSelection()
if num+1 == self.nb.GetPageCount():
self.nb.SetSelection(0)
else:
self.nb.SetSelection(num+1)
while self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty": self.skip_blank_pages(True)
try:
msg = _(u"%s, %s of %s") % (self.nb.GetPageText(self.nb.GetSelection()), self.nb.GetCurrentPage().list.get_selected()+1, self.nb.GetCurrentPage().list.get_count())
except:
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
output.speak(msg, 1)
def show_hide(self, ev=None):
# if platform.system() == "Linux" or platform.system() == "Darwin": return
keymap = {}
for i in config.main["keymap"]:
if hasattr(self, i):
keymap[config.main["keymap"][i]] = getattr(self, i)
if self.showing == True:
self.keyboard_handler = WXKeyboardHandler(self)
self.keyboard_handler.register_keys(keymap)
self.Hide()
self.showing = False
else:
self.keyboard_handler.unregister_keys(keymap)
del self.keyboard_handler
self.Show()
self.showing = True
def toggle_global_mute(self, event=None):
if config.main["sound"]["global_mute"] == False:
config.main["sound"]["global_mute"] = True
output.speak(_(u"Global mute on"))
elif config.main["sound"]["global_mute"] == True:
config.main["sound"]["global_mute"] = False
output.speak(_(u"Global mute off"))
def toggle_mute(self, event=None):
buffer = self.nb.GetCurrentPage().name_buffer
if buffer not in config.main["other_buffers"]["muted_buffers"]:
config.main["other_buffers"]["muted_buffers"].append(buffer)
output.speak(_(u"Buffer mute on"))
elif buffer in config.main["other_buffers"]["muted_buffers"]:
config.main["other_buffers"]["muted_buffers"].remove(buffer)
output.speak(_(u"Buffer mute off"))
def toggle_autoread(self, event=None):
buffer = self.nb.GetCurrentPage().name_buffer
if buffer not in config.main["other_buffers"]["autoread_buffers"]:
config.main["other_buffers"]["autoread_buffers"].append(buffer)
output.speak(_(u"The auto-reading of new tweets is enabled for this buffer"))
elif buffer in config.main["other_buffers"]["autoread_buffers"]:
config.main["other_buffers"]["autoread_buffers"].remove(buffer)
output.speak(_(u"The auto-reading of new tweets is disabled for this buffer"))
def repeat_item(self):
output.speak(self.nb.GetCurrentPage().get_message(), 1)
def copy_to_clipboard(self, event=None):
output.Copy(self.nb.GetCurrentPage().get_message())
output.speak(_(u"Copied"))
def clear_list(self, event=None):
self.nb.GetCurrentPage().interact("clear_list")
def conversation_up(self, evt=None):
if config.main["general"]["reverse_timelines"] == True and evt == None:
self.conversation_down("down")
return
id = self.db.settings[self.nb.GetCurrentPage().name_buffer][self.nb.GetCurrentPage().list.get_selected()]["in_reply_to_status_id_str"]
pos = twitter.utils.find_previous_reply(id, self.db.settings["home_timeline"])
if pos != None:
self.nb.ChangeSelection(1)
self.nb.GetCurrentPage().list.select_item(pos)
msg = _(u"%s") % (self.nb.GetCurrentPage().get_message())
output.speak(msg)
def conversation_down(self, evt=None):
if config.main["general"]["reverse_timelines"] == True and evt == None:
self.conversation_up("up")
return
id = self.db.settings[self.nb.GetCurrentPage().name_buffer][self.nb.GetCurrentPage().list.get_selected()]["id_str"]
# pos = twitter.utils.find_next_reply(id, self.db.settings["home_timeline"])
pos = twitter.utils.find_next_reply(id, self.db.settings["home_timeline"])
if pos != None:
self.nb.ChangeSelection(1)
self.nb.GetCurrentPage().list.select_item(pos)
msg = _(u"%s") % (self.nb.GetCurrentPage().get_message())
output.speak(msg)
def go_home(self):
self.nb.GetCurrentPage().list.select_item(0)
try:
output.speak(self.nb.GetCurrentPage().get_message(), 1)
except:
pass
def go_end(self):
self.nb.GetCurrentPage().list.select_item(self.nb.GetCurrentPage().list.get_count()-1)
try:
output.speak(self.nb.GetCurrentPage().get_message(), 1)
except:
pass
def go_page_up(self):
if self.nb.GetCurrentPage().list.get_selected <= 20:
index = 0
else:
index = self.nb.GetCurrentPage().list.get_selected() - 20
self.nb.GetCurrentPage().list.select_item(index)
try:
output.speak(self.nb.GetCurrentPage().get_message(), 1)
except:
pass
def go_page_down(self):
if self.nb.GetCurrentPage().list.get_selected() >= self.nb.GetCurrentPage().list.get_count() - 20:
index = self.nb.GetCurrentPage().list.get_count()-1
else:
index = self.nb.GetCurrentPage().list.get_selected() + 20
self.nb.GetCurrentPage().list.select_item(index)
try:
output.speak(self.nb.GetCurrentPage().get_message(), 1)
except:
pass
def volume_up(self):
self.nb.GetCurrentPage().interact("volume_up")
def volume_down(self):
self.nb.GetCurrentPage().interact("volume_down")
def url(self):
self.nb.GetCurrentPage().interact("url")
def audio(self):
self.nb.GetCurrentPage().interact("audio")
def up(self, event=None):
pos = self.nb.GetCurrentPage().list.get_selected()
index = self.nb.GetCurrentPage().list.get_selected()-1
try:
self.nb.GetCurrentPage().list.select_item(index)
except:
pass
if pos == self.nb.GetCurrentPage().list.get_selected():
sound.player.play("limit.ogg", False)
try:
output.speak(self.nb.GetCurrentPage().get_message(), 1)
except:
pass
def down(self, event=None):
index = self.nb.GetCurrentPage().list.get_selected()+1
pos = self.nb.GetCurrentPage().list.get_selected()
try:
self.nb.GetCurrentPage().list.select_item(index)
except:
pass
if pos == self.nb.GetCurrentPage().list.get_selected():
sound.player.play("limit.ogg", False)
try:
output.speak(self.nb.GetCurrentPage().get_message(), 1)
except:
pass
def get_more_items(self):
self.nb.GetCurrentPage().get_more_items()
def connect_streams(self):
disconnect = False
if self.stream.connected == False:
output.speak(_(u"Trying to reconnect the main stream"))
disconnect = True
del self.stream
call_threaded(self.init)
self.get_home()
if hasattr(self, "stream2") and self.stream2.connected == False:
output.speak(_(u"Trying to reconnect the buffers stream"))
disconnect = True
log.debug("Trying reconnects the timelines stream...")
del self.stream2
self.get_tls()
if disconnect == False:
output.speak(_(u"All streams are working properly"))
### Close App
def Destroy(self):
self.sysTray.Destroy()
super(mainFrame, self).Destroy()

62
src/gui/sysTrayIcon.py Normal file
View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
""" A systray for TW Blue """
############################################################
# Copyright (c) 2014 José Manuel Delicado Alcolea <jmdaweb@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import application
import paths
import os
class SysTrayIcon(wx.TaskBarIcon):
def __init__(self, frame):
super(SysTrayIcon, self).__init__()
self.frame=frame
icon=wx.Icon(os.path.join(paths.app_path(), "icon.ico"), wx.BITMAP_TYPE_ICO)
self.SetIcon(icon, application.name)
self.menu=wx.Menu()
item=self.menu.Append(wx.ID_ANY, _(u"Tweet"))
self.Bind(wx.EVT_MENU, frame.compose, item)
item=self.menu.Append(wx.ID_ANY, _(u"Preferences"))
self.Bind(wx.EVT_MENU, frame.preferences, item)
item=self.menu.Append(wx.ID_ANY, _(u"Update profile"))
self.Bind(wx.EVT_MENU, frame.update_profile, item)
item=self.menu.Append(wx.ID_ANY, _(u"Show / hide"))
self.Bind(wx.EVT_MENU, frame.show_hide, item)
item=self.menu.Append(wx.ID_ANY, _(u"Documentation"))
self.Bind(wx.EVT_MENU, frame.onManual, item)
item=self.menu.Append(wx.ID_ANY, _(u"Check for updates"))
self.Bind(wx.EVT_MENU, frame.onCheckForUpdates, item)
item=self.menu.Append(wx.ID_ANY, _(u"Exit"))
self.Bind(wx.EVT_MENU, frame.close, item)
self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.onRightClick)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.onLeftClick)
def onRightClick(self, evt):
self.PopupMenu(self.menu)
def onLeftClick(self, evt):
if (self.frame.showing):
self.frame.SetFocus()
else:
self.frame.onShow_hide()
def Destroy(self):
self.menu.Destroy()
super(SysTrayIcon, self).Destroy()

BIN
src/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
categories = ["General"]
reproducibilities = ["always", "sometimes", "random", "have not tried", "unable to duplicate"]
severities = ["block", "crash", "major", "minor", "tweak", "text", "trivial", "feature"]

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import paths
import os
def get_logs_files():
files = {}
for i in os.listdir(paths.logs_path()):
if i == "debug.log": continue
f = open(paths.logs_path(i), "r")
files[i] = f.readlines()
f.close()
try: os.remove(paths.logs_path("tracebacks.log"))
except: pass
return files

124
src/issueReporter/gui.py Normal file
View File

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import application
from suds.client import Client
import constants
class reportBug(wx.Dialog):
def __init__(self, user_name):
self.user = "informador"
self.user_name = user_name
self.password = "contrasena"
self.url = application.report_bugs_url
self.categories = [_(u"General")]
self.reproducibilities = [_(u"always"), _(u"sometimes"), _(u"random"), _(u"have not tried"), _(u"unable to duplicate")]
self.severities = [_(u"block"), _(u"crash"), _(u"major"), _(u"minor"), _(u"tweak"), _(u"text"), _(u"trivial"), _(u"feature")]
wx.Dialog.__init__(self, None, -1)
self.SetTitle(_(u"Report an error"))
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
categoryLabel = wx.StaticText(panel, -1, _(u"Select a category"), size=wx.DefaultSize)
self.category = wx.ComboBox(panel, -1, choices=self.categories, style=wx.CB_READONLY)
self.category.SetSize(self.category.GetBestSize())
self.category.SetSelection(0)
categoryB = wx.BoxSizer(wx.HORIZONTAL)
categoryB.Add(categoryLabel, 0, wx.ALL, 5)
categoryB.Add(self.category, 0, wx.ALL, 5)
self.category.SetFocus()
sizer.Add(categoryB, 0, wx.ALL, 5)
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
self.summary = wx.TextCtrl(panel, -1)
dc = wx.WindowDC(self.summary)
dc.SetFont(self.summary.GetFont())
self.summary.SetSize(dc.GetTextExtent("a"*80))
# self.summary.SetFocus()
summaryB = wx.BoxSizer(wx.HORIZONTAL)
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
summaryB.Add(self.summary, 0, wx.ALL, 5)
sizer.Add(summaryB, 0, wx.ALL, 5)
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
dc = wx.WindowDC(self.description)
dc.SetFont(self.description.GetFont())
(x, y, z) = dc.GetMultiLineTextExtent("0"*2000)
self.description.SetSize((x, y))
descBox = wx.BoxSizer(wx.HORIZONTAL)
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
descBox.Add(self.description, 0, wx.ALL, 5)
sizer.Add(descBox, 0, wx.ALL, 5)
reproducibilityLabel = wx.StaticText(panel, -1, _(u"how often does this bug happen?"), size=wx.DefaultSize)
self.reproducibility = wx.ComboBox(panel, -1, choices=self.reproducibilities, style=wx.CB_READONLY)
self.reproducibility.SetSelection(3)
self.reproducibility.SetSize(self.reproducibility.GetBestSize())
reprB = wx.BoxSizer(wx.HORIZONTAL)
reprB.Add(reproducibilityLabel, 0, wx.ALL, 5)
reprB.Add(self.reproducibility, 0, wx.ALL, 5)
sizer.Add(reprB, 0, wx.ALL, 5)
severityLabel = wx.StaticText(panel, -1, _(u"Select the importance that you think this bug has"))
self.severity = wx.ComboBox(panel, -1, choices=self.severities, style=wx.CB_READONLY)
self.severity.SetSize(self.severity.GetBestSize())
self.severity.SetSelection(3)
severityB = wx.BoxSizer(wx.HORIZONTAL)
severityB.Add(severityLabel, 0, wx.ALL, 5)
severityB.Add(self.severity, 0, wx.ALL, 5)
sizer.Add(severityB, 0, wx.ALL, 5)
self.agree = wx.CheckBox(panel, -1, _(u"I know that the TW Blue bug system will get my Twitter username to contact me and fix the bug quickly"))
self.agree.SetValue(False)
sizer.Add(self.agree, 0, wx.ALL, 5)
ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
ok.Bind(wx.EVT_BUTTON, self.onSend)
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
btnBox = wx.BoxSizer(wx.HORIZONTAL)
btnBox.Add(ok, 0, wx.ALL, 5)
btnBox.Add(cancel, 0, wx.ALL, 5)
sizer.Add(btnBox, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def onSend(self, ev):
if self.summary.GetValue() == "" or self.description.GetValue() == "":
wx.MessageDialog(self, _(u"You must fill out both fields"), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
return
if self.agree.GetValue() == False:
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your twitter username to contact to you if is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
return
try:
client = Client(self.url)
issue = client.factory.create('IssueData')
issue.project.name = "TW Blue"
issue.project.id = 0
issue.summary = self.summary.GetValue(),
issue.description = "Reported by @%s\n\n" % (self.user_name) + self.description.GetValue()
issue.category = constants.categories[self.category.GetSelection()]
issue.reproducibility.name = constants.reproducibilities[self.reproducibility.GetSelection()]
issue.severity.name = constants.severities[self.severity.GetSelection()]
issue.priority.name = "normal"
issue.view_state.name = "public"
issue.resolution.name = "open"
issue.projection.name = "none"
issue.eta.name = "eta"
issue.status.name = "new"
id = client.service.mc_issue_add(self.user, self.password, issue)
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
self.EndModal(wx.ID_OK)
except:
wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
self.EndModal(wx.ID_CANCEL)

View File

@ -0,0 +1,3 @@
from main import KeyboardHandler, KeyboardHandlerError
#from wx_handler import WXKeyboardHandler
__all__ = ["KeyboardHandler", "KeyboardHandlerError", "WXKeyboardHandler", "WXPanelKeyboardHandler"]

View File

@ -0,0 +1,7 @@
import platform
if platform.system() == 'Linux':
from linux import LinuxKeyboardHandler as GlobalKeyboardHandler
elif platform.system() == 'Windows':
from wx_handler import WXKeyboardHandler as GlobalKeyboardHandler
elif platform.system() == 'Darwin':
from osx import OSXKeyboardHandler as GlobalKeyboardHandler

View File

@ -0,0 +1,58 @@
from main import KeyboardHandler
import threading
import thread
import pyatspi
def parse(s):
"""parse a string like control+f into (modifier, key).
Unknown modifiers will return ValueError."""
m = 0
lst = s.split('+')
if not len(lst): return (0, s)
#Are these right?
d = {
"shift": 1<<pyatspi.MODIFIER_SHIFT,
"control": 1<<pyatspi.MODIFIER_CONTROL,
"alt": 1<<pyatspi.MODIFIER_ALT,
"win":1<<pyatspi.MODIFIER_META3,
}
for item in lst:
if item in d:
m|=d[item]
lst.remove(item)
#end if
if len(lst) > 1: #more than one key, parse error
raise ValueError, 'unknown modifier %s' % lst[0]
return (m, lst[0].lower())
class AtspiThread(threading.Thread):
def run(self):
pyatspi.Registry.registerKeystrokeListener(handler, kind=(pyatspi.KEY_PRESSED_EVENT,),
mask=pyatspi.allModifiers())
pyatspi.Registry.start()
#the keys we registered
keys = {}
def handler(e):
m,k = e.modifiers,e.event_string.lower()
#not sure why we can't catch control+f. Try to fix it.
if (not e.is_text) and e.id >= 97 <= 126:
k = chr(e.id)
if (m,k) not in keys: return False
thread.start_new(keys[(m,k)], ())
return True #don't pass it on
class LinuxKeyboardHandler(KeyboardHandler):
def __init__(self, *args, **kwargs):
KeyboardHandler.__init__(self, *args, **kwargs)
t = AtspiThread()
t.start()
def register_key(self, key, function):
"""key will be a string, such as control+shift+f.
We need to convert that, using parse_key,
into modifier and key to put into our dictionary."""
#register key so we know if we have it on event receive.
t = parse(key)
keys[t] = function
#if we got this far, the key is valid.
KeyboardHandler.register_key(self, key, function)
def unregister_key (self, key, function):
KeyboardHandler.unregister_key(self, key, function)
del keys[parse(key)]

View File

@ -0,0 +1,88 @@
import platform
import time
class KeyboardHandlerError (Exception): pass
class KeyboardHandler(object):
def __init__(self, repeat_rate=0.0, *args, **kwargs):
self.repeat_rate = repeat_rate #How long between accepting the same keystroke?
self._last_key = None
self._last_keypress_time = 0
super(KeyboardHandler, self).__init__(*args, **kwargs)
self.active_keys = {}
if not hasattr(self, 'replacement_mods'):
self.replacement_mods = {}
if not hasattr(self, 'replacement_keys'):
self.replacement_keys = {}
def register_key (self, key, function):
if key in self.active_keys:
raise KeyboardHandlerError, "Key %s is already registered to a function" % key
if not callable(function):
raise TypeError, "Must provide a callable to be invoked upon keypress"
self.active_keys[key] = function
def unregister_key (self, key, function):
try:
if self.active_keys[key] != function:
raise KeyboardHandlerError, "key %s is not registered to that function" % key
except KeyError:
raise KeyboardHandlerError, "Key %s not currently registered"
del(self.active_keys[key])
def unregister_all_keys(self):
for key in list(self.active_keys):
self.unregister_key(key, self.active_keys[key])
def handle_key (self, key):
if self.repeat_rate and key == self._last_key and time.time() - self._last_keypress_time < self.repeat_rate:
return
try:
function = self.active_keys[key]
except KeyError:
return
self._last_key = key
self._last_keypress_time = time.time()
return function()
def register_keys(self, keys):
"""Given a mapping of keystrokes to functions, registers all keystrokes"""
for k in keys:
self.register_key(k, keys[k])
def unregister_keys(self, keys):
"""Given a mapping of keys to their functions, unregisters all provided keys."""
for k in keys:
self.unregister_key(k, keys[k])
def standardize_key(self, key):
"""Takes a keystroke and places it in a standard case and order in a list."""
working = key.split('+')
working = [i.lower() for i in working]
answer = []
if "control" in working:
answer.append("control")
if "win" in working:
answer.append("win")
if "alt" in working:
answer.append("alt")
if "shift" in working:
answer.append("shift")
if working[-1] not in answer:
answer.append(working[-1])
return answer
def standardize_keymap(self, keymap):
"""Given a keymap, returns the keymap standardized."""
full = {}
for i in keymap:
answer = ""
new = self.standardize_key(keymap[i])
for (c, j) in enumerate(new):
if c < len(new)-1:
answer = "%s%s+" % (answer, j)
else:
answer = "%s%s" % (answer, j)
full[i] = answer
return full

View File

@ -0,0 +1,56 @@
from AppKit import *
from PyObjCTools import AppHelper
from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
from Carbon.Events import cmdKey, controlKey
import struct
from threading import Thread
from main import KeyboardHandler
kEventHotKeyPressedSubtype = 6
kEventHotKeyReleasedSubtype = 9
class OSXKeyboardHandler(KeyboardHandler):
def __init__(self):
super(OSXKeyboardHandler, self).__init__()
self.replacement_keys = dict()
self.app = KeyboardCapturingNSApplication.alloc().init()
self._event_thread = Thread(target=AppHelper.runEventLoop)
self._event_thread.start()
def register_key (self, key, function):
super(OSXKeyboardHandler, self).register_key(key, function)
k, m = self.parse_key(key)
key_id = RegisterEventHotKey(k, m, (0, 0), GetApplicationEventTarget(), 0)
self.key_ids[key] = key_id
def unregister_key (self, key, function):
super(OSXKeyboardHandler, self).unregister_key(key, function)
key_id = self.key_ids[key]
raise NotImplementedError
def parse_key (self, key):
key=key.split("+")
#replacements
#Modifier keys:
for index, item in enumerate(key[0:-1]):
if self.replacement_mods.has_key(item):
key[index] = self.replacement_mods[item]
if self.replacement_keys.has_key(key[-1]):
key[-1] = self.replacement_keys[key[-1]]
elif len(key[-1])==1:
key[-1] = ord(str(key[-1]))-36
mods = 0
for i in key[:-1]:
mods = mods|i
return [key[-1], mods]
class KeyboardCapturingNSApplication(NSApplication):
def sendEvent_(self, theEvent):
if theEvent.type() == NSSystemDefined and theEvent.subtype() == kEventHotKeyPressedSubtype:
self.activateIgnoringOtherApps_(True)
NSRunAlertPanel(u'Hot Key Pressed', u'Hot Key Pressed', None, None, None)
super(NSApplication, self).sendEvent_(theEvent)

View File

@ -0,0 +1,40 @@
import win32api
import win32con
from main import KeyboardHandler
class WindowsKeyboardHandler(KeyboardHandler):
def __init__ (self, *args, **kwargs):
super(WindowsKeyboardHandler, self).__init__(*args, **kwargs)
#Setup the replacement dictionaries.
for i in dir(win32con):
if i.startswith("VK_"):
key = i[3:].lower()
self.replacement_keys[key] = getattr(win32con, i)
elif i.startswith("MOD_"):
key = i[4:].lower()
self.replacement_mods[key] = getattr(win32con, i)
self.replacement_keys .update(dict(pageup=win32con.VK_PRIOR, pagedown=win32con.VK_NEXT))
def parse_key (self, keystroke, separator="+"):
keystroke = str(keystroke) #We don't want unicode
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
mods = 0
for i in keystroke[:-1]:
mods = mods | i #or everything together
return (mods, keystroke[-1])
def keycode_from_key(self, key):
if key in self.replacement_mods:
return self.replacement_mods[key]
if key in self.replacement_keys:
return self.replacement_keys[key]
if len(key) == 1:
return win32api.VkKeyScanEx(key, win32api.GetKeyboardLayout())
def is_key_pressed(self, key):
"""Returns if the given key was pressed. Requires an active message loop or will simply give if the key was pressed recently."""
key = self.keycode_from_key(key)
return win32api.GetAsyncKeyState(key)

View File

@ -0,0 +1,119 @@
import functools
import wx
import platform
from main import KeyboardHandler
__all__ = ['WXKeyboardHandler', 'WXControlKeyboardHandler']
def call_after(func):
def wrapper(*args, **kwargs):
wx.CallAfter(func, *args, **kwargs)
functools.update_wrapper(wrapper, func)
return wrapper
class BaseWXKeyboardHandler(KeyboardHandler):
def __init__(self, *args, **kwargs):
super(BaseWXKeyboardHandler, self).__init__(*args, **kwargs)
#Setup the replacement dictionaries.
for i in dir(wx):
if i.startswith('WXK_'):
key = i[4:].lower()
self.replacement_keys[key] = getattr(wx, i)
elif i.startswith('MOD_'):
key = i[4:].lower()
self.replacement_mods[key] = getattr(wx, i)
def parse_key (self, keystroke, separator="+"):
keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)]
mods = 0
for i in keystroke[:-1]:
mods = mods | i #or everything together
return (mods, keystroke[-1])
def keycode_from_key(self, key):
if key in self.replacement_mods:
result = self.replacement_mods[key]
elif key in self.replacement_keys:
result = self.replacement_keys[key]
if result >= 277:
result -= 277
elif len(key) == 1:
result = ord(key.upper())
print "result: ", result
return result
#try:
if platform.system() == "Windows":
from windows import WindowsKeyboardHandler as keyboard_handler
elif platform.system() == "Linux":
from linux import LinuxKeyboardHandler as keyboard_handler
elif platform.system() == "Darwin":
from osx import OSXKeyboardHandler as keyboard_handler
class WXKeyboardHandler(keyboard_handler):
def __init__ (self, parent, *args, **kwargs):
super(WXKeyboardHandler, self).__init__(*args, **kwargs)
self.parent = parent
self.key_ids = {}
@call_after
def register_key(self, key, function):
super(WXKeyboardHandler, self).register_key(key, function)
key_id = wx.NewId()
parsed = self.parse_key(key)
self.parent.RegisterHotKey(key_id, *parsed)
self.parent.Bind(wx.EVT_HOTKEY, lambda evt: self.process_key(evt, key_id), id=key_id)
self.key_ids[key] = key_id
@call_after
def unregister_key (self, key, function):
super(WXKeyboardHandler, self).unregister_key(key, function)
if key not in self.key_ids:
return #there's nothing we can do.
key_id = self.key_ids[key]
self.parent.UnregisterHotKey(key_id)
self.parent.Unbind( wx.EVT_HOTKEY, id=key_id)
self.key_ids.pop(key)
def process_key (self, evt, id):
evt.Skip()
key_ids = self.key_ids.keys()
for i in key_ids:
if self.key_ids.get(i) == id:
self.handle_key(i)
class WXControlKeyboardHandler(wx.StaticText, KeyboardHandler):
def __init__(self, parent=None, *a, **k):
wx.StaticText.__init__(self, parent=parent)
KeyboardHandler.__init__(self, *a, **k)
self.wx_replacements = {}
for i in [d for d in dir(wx) if d.startswith('WXK_')]:
self.wx_replacements[getattr(wx, i)] = i[4:].lower()
self.Bind(wx.EVT_KEY_DOWN, self.process_key, self)
self.SetFocus()
def process_key(self, evt):
keycode = evt.GetKeyCode()
keyname = self.wx_replacements.get(keycode, None)
modifiers = ""
replacements = ( (evt.ControlDown(), 'control+'),
(evt.AltDown(), 'alt+'),
(evt.ShiftDown(), 'shift+'),
(evt.MetaDown(), 'win+')
)
for mod, ch in (replacements):
if mod:
modifiers += ch
if keyname is None:
if 27 < keycode < 256:
keyname = chr(keycode).lower()
else:
keyname = "(%s)unknown" % keycode
key = modifiers + keyname
self.handle_key(key)

View File

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
actions = {
"up": _(u"Go up up on the current list"),
"down": _(u"Go down up on the current list"),
"left": _(u"Go to the previous tab"),
"right": _(u"Go to the next tab"),
"conversation_up": _(u"Move up one tweet in the conversation"),
"conversation_down": _(u"Move down one tweet in the conversation"),
"show_hide": _(u"Show the graphical interface"),
"compose": _(u"New tweet"),
"reply": _(u"Reply to a tweet"),
"retweet": _(u"Retweet"),
"dm": _(u"Send direct message"),
"fav": _(u"Mark as favourite"),
"unfav": _(u"Remove from favourites"),
"action": _(u"Open the actions dialogue"),
"details": _(u"See user details"),
"view": _(u"Show tweet"),
"close": _(u"Quit"),
"open_timeline": _(u"Open user timeline"),
"delete_buffer": _(u"Remove buffer"),
"url": _(u"Open URL on the current tweet, or further information for a friend or follower"),
"audio": _(u"Attempt to play audio"),
"volume_up": _(u"Increase volume by 5%"),
"volume_down": _(u"Decrease volume by 5%"),
"go_home": _(u"Go to the first element on the list"),
"go_end": _(u"Go to the last element on the list"),
"go_page_up": _(u"Move 20 elements up on the current list"),
"go_page_down": _(u"Move 20 elements down on the current list"),
"update_profile": _(u"Edit profile"),
"delete": _(u"Remove a tweet or direct message"),
"clear_list": _(u"Empty the buffer removing all the elements"),
"repeat_item": _(u"Listen the current message"),
"copy_to_clipboard": _(u"Copy to clipboard"),
"add_to_list": _(u"Add to list"),
"remove_from_list": _(u"Remove from list"),
"toggle_mute": _(u"Mutes/unmutes the active buffer"),
"toggle_global_mute": _(u"Globally mute/unmute TW Blue"),
"toggle_autoread": _(u"toggles the automatic reading of incoming tweets in the active buffer"),
"search": _(u"Search on twitter"),
"edit_keystrokes": _(u"Shows the keystroke editor"),
"view_user_lists": _(u"Show lists for a specified user"),
}

119
src/keystrokeEditor/gui.py Normal file
View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
import config
import wx
import constants
from multiplatform_widgets import widgets
from constants import actions
class keystrokeEditor(wx.Dialog):
def __init__(self, parent=None, keyboard_handler=None):
super(keystrokeEditor, self).__init__(parent=parent, id=-1, title=_(u"Keystroke editor"))
panel = wx.Panel(self)
self.parent = parent
self.keyboard_handler = keyboard_handler or None
self.actions = []
sizer = wx.BoxSizer(wx.VERTICAL)
keysText = wx.StaticText(panel, -1, _(u"Select a keystroke to edit"))
self.keys = widgets.list(self, _(u"Action"), _(u"Keystroke"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(400, 450))
self.keys.list.SetFocus()
firstSizer = wx.BoxSizer(wx.HORIZONTAL)
firstSizer.Add(keysText)
firstSizer.Add(self.keys.list)
edit = wx.Button(panel, -1, _(u"Edit"))
self.Bind(wx.EVT_BUTTON, self.edit, edit)
edit.SetDefault()
close = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
secondSizer = wx.BoxSizer(wx.HORIZONTAL)
secondSizer.Add(edit)
secondSizer.Add(close)
sizer.Add(firstSizer)
sizer.Add(secondSizer)
panel.SetSizerAndFit(sizer)
self.put_keystrokes()
def put_keystrokes(self):
for i in config.main["keymap"]:
action = actions[i]
self.actions.append(i)
keystroke = config.main["keymap"][i]
self.keys.insert_item(False, *[action, keystroke])
def edit(self, ev):
action = self.actions[self.keys.get_selected()]
dlg = editKeystroke(self.parent, action, config.main["keymap"][action], self.keyboard_handler)
if dlg.ShowModal() == wx.ID_OK:
pos = self.keys.get_selected()
self.keys.clear()
self.put_keystrokes()
self.keys.select_item(pos)
# dlg.Destroy()
class editKeystroke(wx.Dialog):
def __init__(self, parent, action, keystroke, keyboard_handler):
super(editKeystroke, self).__init__(parent=None, id=-1, title=_(u"Editing keystroke"))
self.parent = parent
self.keyboard_handler = keyboard_handler
self.action = action
self.keystroke = keystroke
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.control = wx.CheckBox(panel, -1, _(u"Control"))
self.alt = wx.CheckBox(panel, -1, _(u"Alt"))
self.shift = wx.CheckBox(panel, -1, _(u"Shift"))
self.win = wx.CheckBox(panel, -1, _(u"Windows"))
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
sizer1.Add(self.control)
sizer1.Add(self.alt)
sizer1.Add(self.shift)
sizer1.Add(self.win)
charLabel = wx.StaticText(panel, -1, _(u"Key"))
self.key = wx.TextCtrl(panel, -1)
# self.key.SetMaxLength(1)
sizer2 = wx.BoxSizer(wx.HORIZONTAL)
sizer2.Add(charLabel)
sizer2.Add(self.key)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.SetDefault()
self.Bind(wx.EVT_BUTTON, self.ok, ok)
cancel = wx.Button(panel, wx.ID_CANCEL)
sizer3 = wx.BoxSizer(wx.HORIZONTAL)
sizer3.Add(ok)
sizer3.Add(cancel)
sizer.Add(sizer1)
sizer.Add(sizer2)
sizer.Add(sizer3)
panel.SetSizerAndFit(sizer)
self.set_default()
def set_default(self):
for i in self.keystroke.split("+"):
if hasattr(self, i):
key = getattr(self, i)
key.SetValue(True)
self.key.SetValue(self.keystroke.split("+")[-1])
def ok(self, ev):
keys = []
if self.win.GetValue() == False:
wx.MessageDialog(self, _(u"You need to use the Windows key"), _(u"Invalid keystroke"), wx.OK|wx.ICON_ERROR).ShowModal()
return
if self.control.GetValue() == True:
keys.append("control")
if self.win.GetValue() == True:
keys.append("win")
if self.alt.GetValue() == True:
keys.append("alt")
if self.shift.GetValue() == True:
keys.append("shift")
if self.key.GetValue() != "":
keys.append(self.key.GetValue())
else:
wx.MessageDialog(self, _(u"You must provide a character for the keystroke"), _(u"Invalid keystroke"), wx.ICON_ERROR).ShowModal()
return
config.main["keymap"][self.action] = "+".join(keys)
if self.keyboard_handler != None:
self.keyboard_handler.unregister_key(self.keystroke, getattr(self.parent, self.action))
self.keyboard_handler.register_key(config.main["keymap"][self.action], getattr(self.parent, self.action))
self.EndModal(wx.ID_OK)

180
src/languageHandler.py Normal file
View File

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

View File

@ -0,0 +1,7 @@
from .libloader import *
__version__ = 0.1
__author__ = 'Christopher Toth <q@q-continuum.net>'
__doc__ = """
Quickly and easily load shared libraries from various platforms. Also includes a libloader.com module for loading com modules on Windows.
"""

21
src/libloader/com.py Normal file
View File

@ -0,0 +1,21 @@
from pywintypes import com_error
from win32com.client import gencache
def prepare_gencache():
gencache.is_readonly = False
gencache.GetGeneratePath()
def load_com(*names):
result = None
for name in names:
try:
result = gencache.EnsureDispatch(name)
break
except com_error:
continue
if result is None:
raise com_error("Unable to load any of the provided com objects.")
return result

View File

@ -0,0 +1,56 @@
import ctypes
import collections
import platform
import os
TYPES = {
'Linux': {
'loader': ctypes.CDLL,
'functype': ctypes.CFUNCTYPE,
'prefix': 'lib',
'extension': '.so'
},
'Darwin': {
'loader': ctypes.CDLL,
'functype': ctypes.CFUNCTYPE,
'prefix': 'lib',
'extension': '.dylib'
},
}
if platform.system() == 'Windows':
TYPES['Windows'] = {
'loader': ctypes.WinDLL,
'functype': ctypes.WINFUNCTYPE,
'prefix': "",
'extension': '.dll'
}
class LibraryLoadError(OSError): pass
def load_library(library, x86_path='.', x64_path='.', *args, **kwargs):
lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path)
loaded = _do_load(lib, *args, **kwargs)
if loaded is not None:
return loaded
raise LibraryLoadError('unable to load %r. Provided library path: %r' % (library, path))
def _do_load(file, *args, **kwargs):
loader = TYPES[platform.system()]['loader']
return loader(file, *args, **kwargs)
def find_library_path(libname, x86_path='.', x64_path='.'):
libname = '%s%s' % (TYPES[platform.system()]['prefix'], libname)
if platform.architecture()[0] == '64bit':
path = os.path.join(x64_path, libname)
else:
path = os.path.join(x86_path, libname)
ext = get_library_extension()
path = '%s%s' % (path, ext)
return os.path.abspath(path)
def get_functype():
return TYPES[platform.system()]['functype']
def get_library_extension():
return TYPES[platform.system()]['extension']

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.

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