Compare commits
346 Commits
v0.50
...
snapshot9.
Author | SHA1 | Date | |
---|---|---|---|
f287308986 | |||
939523c4f9 | |||
aa5977f324 | |||
4b882d8586 | |||
b0bbe5719f | |||
0e3a4372b8 | |||
83575b14b9 | |||
f09ff43672 | |||
0045bd223c | |||
f6bf1dcfdc | |||
91c5165d12 | |||
5976caf736 | |||
831bbc2f2f | |||
![]() |
590e4b0e6b | ||
![]() |
6c690d7a3d | ||
![]() |
b03d832376 | ||
![]() |
d521146562 | ||
![]() |
66686c786d | ||
![]() |
bb27c08424 | ||
![]() |
ae9bd087a8 | ||
![]() |
4bee7991d1 | ||
![]() |
c67e8d041b | ||
![]() |
1d7b2c5712 | ||
![]() |
ebe3ab2c14 | ||
![]() |
4b2c7a9655 | ||
![]() |
24fa7a1815 | ||
![]() |
a60d00d0dc | ||
![]() |
b5f1294b82 | ||
![]() |
671e0e0bff | ||
![]() |
44605f1209 | ||
![]() |
cf139292a6 | ||
![]() |
f3e1dc40ea | ||
![]() |
6ba57e83cf | ||
![]() |
f68388f700 | ||
![]() |
dc5d11c1c8 | ||
![]() |
40adacb32e | ||
![]() |
3747f34b69 | ||
![]() |
938be159a4 | ||
![]() |
7f869f7de1 | ||
07bb6930d0 | |||
![]() |
48238239c3 | ||
![]() |
d1fe610d6e | ||
![]() |
3028b7d8ca | ||
![]() |
4a025caa69 | ||
![]() |
aa7145f5b3 | ||
![]() |
7fb71e2ec5 | ||
![]() |
d02bd91f41 | ||
![]() |
432e40bdfa | ||
![]() |
f0e1f74d03 | ||
![]() |
6250e16cfe | ||
![]() |
0b0da44020 | ||
![]() |
73a069dbe2 | ||
![]() |
81a9a36519 | ||
![]() |
1b4ebebf25 | ||
![]() |
1d1c07caca | ||
![]() |
5f8760a0a9 | ||
![]() |
feda70aea8 | ||
![]() |
e92565013b | ||
![]() |
9ddc74d640 | ||
![]() |
71a061d13c | ||
![]() |
7f6cf8d30a | ||
![]() |
58a8e45d56 | ||
![]() |
bb5f79e07b | ||
![]() |
2a8868f096 | ||
![]() |
74f0e8ba50 | ||
![]() |
1d344275bb | ||
![]() |
ea7f5bacf9 | ||
4c125e4a7a | |||
![]() |
0845553047 | ||
![]() |
6f88e08811 | ||
![]() |
2fa17ee51f | ||
![]() |
7b2cf28f68 | ||
![]() |
e67446b9bf | ||
![]() |
c7d157145b | ||
![]() |
ab08d21d95 | ||
![]() |
ee1f95ea3b | ||
![]() |
8ce5c258d3 | ||
![]() |
9b4fdc8966 | ||
![]() |
6cb5c4bd5c | ||
![]() |
1b2b5913d1 | ||
![]() |
92cd16bec9 | ||
![]() |
ff9e16285e | ||
![]() |
68499ca9e5 | ||
![]() |
6fdc5c67e2 | ||
![]() |
52bfa82ec5 | ||
![]() |
1e6a784192 | ||
![]() |
8eb6d0c4cb | ||
![]() |
e2771023da | ||
![]() |
d19baf411a | ||
![]() |
0ddc20ea7c | ||
![]() |
2aceab527e | ||
![]() |
be787dc728 | ||
![]() |
0aaec95ac4 | ||
![]() |
c1ce8cb8e5 | ||
![]() |
3f63046cbb | ||
![]() |
f241a1fc31 | ||
![]() |
8d873674ee | ||
![]() |
6f4501940e | ||
![]() |
69e3205a68 | ||
![]() |
5001559c7c | ||
![]() |
e52385317e | ||
![]() |
7003bab110 | ||
![]() |
f8f9a4ecf2 | ||
![]() |
edae9fb664 | ||
![]() |
a5198b881e | ||
![]() |
f394701789 | ||
![]() |
4d4816a61b | ||
![]() |
cb94d7f71a | ||
![]() |
c833854097 | ||
![]() |
278c1d5024 | ||
55d21840ef | |||
8e27e9720c | |||
![]() |
e1479b5c10 | ||
![]() |
e6ce553216 | ||
![]() |
eb9128cb20 | ||
![]() |
55683aee96 | ||
e90c70bf02 | |||
71eca7baf2 | |||
![]() |
d777b3a3cb | ||
![]() |
02486ccae6 | ||
![]() |
3633513e85 | ||
![]() |
d4932f3d2f | ||
971306cd73 | |||
4b1a4db3db | |||
472099614a | |||
bd6861d179 | |||
![]() |
6c5e529129 | ||
![]() |
9fbbd29c3b | ||
![]() |
7206debb30 | ||
![]() |
be4ba1608b | ||
![]() |
e7628e1f65 | ||
![]() |
cdb0a5b5a6 | ||
![]() |
1794f497a5 | ||
![]() |
08ce78f97c | ||
![]() |
797e0d7773 | ||
![]() |
ab5f6fd12d | ||
![]() |
2a3937bf55 | ||
ec326ed1c1 | |||
501a5cd57e | |||
![]() |
780adeb001 | ||
![]() |
1adcd86a39 | ||
1c4d0cc530 | |||
![]() |
45d1380f95 | ||
![]() |
78355efc45 | ||
![]() |
0adf06f2a6 | ||
![]() |
3dc4c4c685 | ||
![]() |
7b9247eefe | ||
![]() |
e62039b727 | ||
380e05b079 | |||
5b6cae0a61 | |||
592952c066 | |||
![]() |
42052f5ee7 | ||
![]() |
0162ae5739 | ||
33175cbccd | |||
ce5498ff94 | |||
c50c42b895 | |||
![]() |
0f6b3491f8 | ||
![]() |
036edb6c28 | ||
![]() |
3e8e2312f2 | ||
![]() |
83f9e32fd6 | ||
![]() |
6e294ca0da | ||
![]() |
3a5eeed372 | ||
![]() |
9d083fb550 | ||
![]() |
aca2292637 | ||
![]() |
c7719545ac | ||
![]() |
f39adb658b | ||
![]() |
7174984973 | ||
![]() |
ffaca56ec5 | ||
![]() |
8207bda74b | ||
![]() |
3c5529a761 | ||
![]() |
ebcd5720b0 | ||
![]() |
12541f3de3 | ||
![]() |
594985dec4 | ||
e7943cba50 | |||
f51c873324 | |||
96671645bb | |||
2880638241 | |||
![]() |
3f02a0f422 | ||
![]() |
7dd383b15e | ||
![]() |
ddf9d29b7d | ||
![]() |
109635b8f4 | ||
![]() |
9820d369c3 | ||
![]() |
263ce33c41 | ||
![]() |
0913eac63d | ||
![]() |
ce0aa2d6a3 | ||
![]() |
00d5b88b8d | ||
be0253815f | |||
![]() |
a9405e32e9 | ||
![]() |
d0924a6e15 | ||
![]() |
3c46201af9 | ||
![]() |
2d7c7e8e37 | ||
![]() |
d87929c4c3 | ||
![]() |
d4a4decaed | ||
![]() |
61863c38e3 | ||
![]() |
2d1f7e5ada | ||
![]() |
916ca0127d | ||
![]() |
6f4e9e920f | ||
c2521f28fc | |||
7565d2ea57 | |||
![]() |
23ba46823c | ||
7facb9b020 | |||
5490a59eb5 | |||
d90df91aef | |||
7cdda6806b | |||
97cff4b598 | |||
c45a208cd6 | |||
![]() |
8d6d452dfb | ||
![]() |
21c6a999b4 | ||
![]() |
141d8fa105 | ||
![]() |
dec2456655 | ||
![]() |
9263aa8507 | ||
![]() |
df3a32572b | ||
![]() |
8b9209b62a | ||
eef78cb8dc | |||
7c65a96a2b | |||
5d87ce06b7 | |||
43b797ca6b | |||
fa591c200d | |||
1f5b2aaf7c | |||
77d04e0297 | |||
![]() |
88ffdb4c29 | ||
324b18e8a4 | |||
e2352ea3f2 | |||
![]() |
b272a08ab9 | ||
48e918adb0 | |||
d8581f47d2 | |||
![]() |
17601393de | ||
2e32fa7ef2 | |||
9356a0544f | |||
c085729096 | |||
d2f7228653 | |||
a513303a9a | |||
ffeccbb3b6 | |||
2a83440c44 | |||
5a1fd9bb75 | |||
f8c2ac7468 | |||
ae6d5b38dd | |||
1e9d4432f3 | |||
9eec1c7c44 | |||
7ad1d55241 | |||
c88c37d65b | |||
15eb0f62ea | |||
2e70cbc298 | |||
f2bb173ddb | |||
e046fdd198 | |||
a63d6eceb1 | |||
ee006eeb66 | |||
ac45430839 | |||
a592f747f8 | |||
a0f5a3c134 | |||
977ffacd27 | |||
9256a5f981 | |||
75dfaec727 | |||
89e541be95 | |||
c7d007636e | |||
8c85cc25a0 | |||
47d0a3db67 | |||
e15cd86644 | |||
85a7ffc8eb | |||
ca9f5b0c30 | |||
69aa27c33e | |||
26644da7b4 | |||
4465f6e8aa | |||
4f4b8c6447 | |||
5926732c26 | |||
05f75bf5d0 | |||
e63db7b150 | |||
d4fc809169 | |||
4613b7ada1 | |||
eb4301aeb5 | |||
4c56850da6 | |||
9d4063b662 | |||
1a81d1a536 | |||
e1b3a94b13 | |||
2cc865b62c | |||
007da773c9 | |||
462f6fe395 | |||
e65c8ec4a6 | |||
373ca24a96 | |||
3f1b00af8f | |||
4d1e03fb64 | |||
632e54a727 | |||
188d95ad6c | |||
0ba120c3dd | |||
0b12924874 | |||
d6163d6294 | |||
1b0e8e47fa | |||
ae1df13f4a | |||
![]() |
1093bea416 | ||
1f64b80f9d | |||
c571a913c5 | |||
76d8c292c9 | |||
458d607b9a | |||
3a9a49d64c | |||
b7193d16ee | |||
e7852e5e91 | |||
35f70e050a | |||
![]() |
db8da293d3 | ||
88d7898dba | |||
e906d04f18 | |||
d9d0efab2d | |||
10d0b8ba6b | |||
f13d9113a4 | |||
066814724b | |||
a3e7effb83 | |||
be9ede43a3 | |||
d4ea961dae | |||
de7118caf4 | |||
5eb4f74242 | |||
d2cffe65d8 | |||
42aea45a41 | |||
2dd0cc8fbb | |||
0ede5bfcb0 | |||
62b0bc76a8 | |||
2a7e09b166 | |||
9ad2d6cc69 | |||
1fbee07de2 | |||
3b1d8eb48a | |||
103b62719e | |||
e93f0f4980 | |||
ff65b73232 | |||
51957125c1 | |||
af087508b0 | |||
399de75d64 | |||
c25412bd32 | |||
7b1f2f9482 | |||
fd70bedc05 | |||
bd1b0b5e32 | |||
02bdeae4e6 | |||
4979b2aa66 | |||
a84d35c6af | |||
32884d3bf2 | |||
a599b37e6d | |||
5cb4e0fef7 | |||
7d6bf53019 | |||
29d434bcd4 | |||
186f70afc3 | |||
a6dd5d602f | |||
2895d02cdc | |||
ca288de515 | |||
567e612c72 | |||
1b3b684cbc | |||
cd0aab90ef | |||
05dc8e5a31 | |||
606ab6d6fc | |||
f54d9394b7 |
6
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
*.pyc
|
||||
*~
|
||||
windows-dependencies/*
|
||||
src/build/
|
||||
src/dist/
|
||||
src/config/
|
||||
@@ -10,8 +9,11 @@ src/config3/
|
||||
src/dropbox/
|
||||
src/logs/
|
||||
src/documentation/
|
||||
src/sounds/iOs
|
||||
src/oggenc2.exe
|
||||
src/bootstrap.exe
|
||||
src/Microsoft.VC90.CRT
|
||||
src/Microsoft.VC90.MFC
|
||||
src/launcher.bat
|
||||
src/launcher.bat
|
||||
src/sounds/iOs
|
||||
release-snapshot/
|
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "windows-dependencies"]
|
||||
path = windows-dependencies
|
||||
url = https://github.com/jmdaweb/TWBlue_deps_windows.git
|
103
README.md
@@ -1,9 +1,10 @@
|
||||
TWBlue
|
||||
TWBlue -
|
||||
======
|
||||
|
||||
TWBlue, an accessible, open source and multiplatform twitter application.
|
||||
Copyright (C) 2015. [Technow S.L.](https://www.technow.es)
|
||||
|
||||
TW Blue is an app designed to use Twitter in a simple and fast way and avoiding, as far as possible, the consumtion of excessive resources of the machine where it’s running. With this app you’ll have access to twitter features such as:
|
||||
TW Blue is an app designed to use Twitter simply and efficiently while using minimal system resources.
|
||||
With this app you’ll have access to twitter features such as:
|
||||
|
||||
* Create, reply to, retweet and delete tweets,
|
||||
* Add and remove tweets from favourites,
|
||||
@@ -11,8 +12,98 @@ TW Blue is an app designed to use Twitter in a simple and fast way and avoiding,
|
||||
* See your friends and followers,
|
||||
* Follow, unfollow, block and report users as spam,
|
||||
* Open a user’s timeline, which will allow you to get that user’s tweets separately,
|
||||
* Open URL addresses when attached to a tweet or direct message,
|
||||
* Play various file and URL types which contain audio
|
||||
* Open URLs when attached to a tweet or direct message,
|
||||
* Play audio tweets
|
||||
* and more!
|
||||
|
||||
See the [TWBlue's webpage](http://twblue.com.mx) for more details.
|
||||
See [TWBlue's webpage](http://twblue.es) for more details.
|
||||
|
||||
## Using TWBlue from sources
|
||||
|
||||
This document describes how to run tw blue from source, and, after that, how to build a binary version, which doesn't need Python and the other dependencies to run.
|
||||
|
||||
### Required dependencies.
|
||||
|
||||
Although most dependencies can be found in the windows-dependencies directory, we provide links to their official websites. If you are cloning with git, don't forget to initialize and update the submodules to get the windows-dependencies folder. You can use these two commands to perform this task from git bash:
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
All the dependencies provided in this folder are prebuilt. If you want to build them from source, you will need Microsoft visual Studio 2008.
|
||||
|
||||
#### Dependencies packaged in windows installers
|
||||
|
||||
* [Python,](http://python.org) version 2.7.9
|
||||
If you want to build both x86 and x64 binaries, you can install python x86 to C:\python27 and python x64 to C:\python27x64, for example.
|
||||
* [wxPython](http://www.wxpython.org) for Python 2.7, version 3.0.2
|
||||
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 219
|
||||
* [Pycurl](http://pycurl.sourceforge.net) 7.19.5 for Python 2.7: [32-bit downloads,](https://pypi.python.org/pypi/pycurl/7.19.3.1) [64-bit downloads](http://www.lfd.uci.edu/~gohlke/pythonlibs/)
|
||||
* [PyEnchant,](http://pythonhosted.org/pyenchant/) version 1.6.6.
|
||||
x64 version has been built by TW Blue developers, so you only will find it in windows-dependencies folder
|
||||
|
||||
The windows installers are available only in the windows-dependencies folder
|
||||
|
||||
To build a binary version:
|
||||
|
||||
* [Py2exe](http://www.sourceforge.net/projects/py2exe/) for Python 2.7, version 0.6.9
|
||||
|
||||
#### Dependencies that must be installed using easy_install
|
||||
|
||||
setuptools install a script, called easy_install. You can find it in the python scripts directory. To install packages using easy_install, you have to navigate to the scripts directory using a command prompt, for example:
|
||||
|
||||
cd C:\python27x64\scripts
|
||||
|
||||
You can also add the scripts folder to your path environment variable.
|
||||
|
||||
After that, run the following command to install a package, replacing packagename with the names listed below:
|
||||
|
||||
easy_install -Z package
|
||||
|
||||
The -z switch unzips the package, instead of installing it compressed. If you add the --upgrade switch, you can upgrade a package to its latest version. The following packages need to be installed:
|
||||
|
||||
* pypubsub
|
||||
* dropbox
|
||||
* configobj
|
||||
* requests-oauthlib
|
||||
* future
|
||||
* pygeocoder
|
||||
* suds
|
||||
* arrow
|
||||
* goslate
|
||||
* markdown
|
||||
|
||||
easy_install will automatically get the additional libraries that these packages need to work properly.
|
||||
|
||||
#### Other dependencies
|
||||
|
||||
These dependencies are located in the windows-dependencies directory. You don't need to install or modify them.
|
||||
|
||||
* Bootstrap 1.2.1: included in dependencies directory.
|
||||
This dependency has been built using pure basic 4.61. Its source can be found at http://hg.q-continuum.net/updater
|
||||
* [oggenc2.exe,](http://www.rarewares.org/ogg-oggenc.php) version 2.87
|
||||
* Microsoft Visual c++ 2008 redistributable dlls.
|
||||
|
||||
#### Dependencies required to build the installer
|
||||
|
||||
* [NSIS unicode,](http://www.scratchpaper.com/) version 2.46.5
|
||||
|
||||
### Running TW Blue from source
|
||||
|
||||
Now that you have installed all these packages, you can run TW Blue from source using a command prompt. Navigate to the repo's src directory, and type the following command:
|
||||
|
||||
python main.py
|
||||
|
||||
If necessary, change the first part of the command to reflect the location of your python executable. You can run TW Blue using python x86 and x64
|
||||
|
||||
### Building a binary version
|
||||
|
||||
A binary version doesn't need python and the other dependencies to run, it's the same version that you will find on the TW Blue website if you download the zip files.
|
||||
|
||||
To build it, run the following command from the src folder:
|
||||
|
||||
python setup.py py2exe
|
||||
|
||||
You will find the binaries in the dist directory.
|
||||
|
||||
### How to generate a translation template
|
||||
|
||||
Run the gen_pot.bat file, located in the tools directory. Your python installation must be in your path environment variable. The pot file will appear in the tools directory.
|
@@ -1,4 +1,5 @@
|
||||
Manuel E. Cortéz
|
||||
Bill Dengler
|
||||
Johana Hidrobo
|
||||
Marcelo Sosa
|
||||
Isabel del Castillo
|
||||
|
7
doc/application.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
name = 'TWBlue'
|
||||
snapshot = False
|
||||
if snapshot == False:
|
||||
version = "0.80"
|
||||
else:
|
||||
version = "7"
|
244
doc/strings.py
@@ -1,18 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import application
|
||||
documentation = []
|
||||
documentation.append(_(u"""Documentation for TW Blue 0.46"""))
|
||||
documentation.append(_(u"""Documentation for {0} - {1}""").format(application.name, application.version))
|
||||
# Translators: This is the new line character, don't change it in the translations.
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Table of contents"""))
|
||||
# Table of contents for the python markdown extension
|
||||
documentation.append("""[TOC]""")
|
||||
#documentation.append(_(u"""# Version 0.46 (alpha)"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Warning!"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""You're reading documentation produced for a program still in development. The object of this manual is explaining some details of the operation of the program. Bear in mind that as the software is in the process of active development, parts of this document may change in the near future, so it is advisable to keep an eye on it from time to time to avoid missing too much out."""))
|
||||
documentation.append(_(u"""You're reading documentation produced for a program still in development. The object of this manual is to explain some details of the operation of the program. Bear in mind that as the software is in the process of active development, parts of this document may change in the near future, so it is advisable to keep an eye on it from time to time to avoid missing too much out."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""If you want to see what has changed from the previous version, [read the list of updates here.](changes.html)"""))
|
||||
@@ -41,40 +42,40 @@ documentation.append(_(u"""
|
||||
documentation.append(_(u"""In order to use an application like TW Blue which allows you to manage your Twitter account, you must first be registered on it. It's beyond the scope of this document to explain how to do so. We'll start from the premise that you have an account with its corresponding user name and password."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Authorising the application"""))
|
||||
documentation.append(_(u"""### Authorising the application"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""First off, it's necessary to authorise the program so it can access your Twitter account and act on your behalf. The authorisation process is quite simple, and the program never gets data such as your username and password. In order to authorise the application, you just need to run the main executable file, called TWBlue.exe (on some computers it may appear simply as TWBlue)."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""When executed, if you have not previously configured the program, it will show a dialogue box where it tells you'll be taken to Twitter in order to authorise the application as soon as you press OK. To begin the authorisation process, press the only available button on the box."""))
|
||||
documentation.append(_(u"""Whether this is the first time you open TWBlue or you don't have any session, you will see the session manager. This dialog allows you to authorise as many accounts as you wish. If you press the "new account" button a dialog will tell you that your default browser will be opened in order to authorise the application. Press "yes" so the process may start."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Your default browser will open on the Twitter page to request authorisation. Enter your user name and password if you're not already logged in, look for the authorise button, and press it."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Read the instructions you will get if the process is successful. In summary, you will be given a numeric code with several digits you must paste on an edit field open by the application on another window."""))
|
||||
documentation.append(_(u"""Once you've authorised your twitter account, Twitter will redirect you to a web page which will notify you that TWBlue has been authorised successfully. Now you are able to close that window and come back to the session manager. You will see on the session list a new item temporally called "Authorised account x" -where x is a number. The session name will change once you open that session."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Paste the verification code, and press the enter key. """))
|
||||
documentation.append(_(u"""To start running TWBlue, press the Ok button in the session manager dialog. By default, TWBlue starts all the configured sessions, however, you can change this behavior."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""If all went well, the application will start playing sounds, indicating your data are being updated."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""When the process is finished,the program will play another sound, and the screen reader will say "ready"."""))
|
||||
documentation.append(_(u"""When the process is finished, the program will play another sound, and the screen reader will say "ready"."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## The program's interface {#interface}"""))
|
||||
documentation.append(_(u"""## The program's interface"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""The easiest way to describe the graphical interface of the application is a window with a menu bar with four menus (application, tweet, user and help), a list with several elements, and, in most cases, three buttons: tweet, retweet and reply. The actions available for each element are described below."""))
|
||||
documentation.append(_(u"""The easiest way to describe the graphical user interface of TWBlue is saying that the application has a window which contains a menu bar with five menus (application, tweet, user, buffer and help); one tree view, one list of items and, mostly in every case, three buttons: Tweet, retweet and reply. The actions that are available for every item will be described later."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Elements on the lists may be tweets, direct messages or users. TW Blue creates different tabs for each list, which can be sent tweets, main timeline tweets, favourites, or direct messages, and each tab contains a single type of tweet. These tabs are called lists or buffers."""))
|
||||
documentation.append(_(u"""In the tree view are inserted buffers which are lists to manage the processed data. When you configure a new session on TWBlue and start it, your account is the root of the tree view. Inside of it many buffers are created. Each one of them may contain some of the items which TWBlue works with: Tweets, direct messages, users, trends or events. According to the buffer you were, you will be able to make different actions with these items."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""To switch from list to list press control-tab to go forward, and control-shift-tab to go back. Screen readers will announce the list that gains the focus at all times. These are the basic lists of TW Blue, which are configured by default."""))
|
||||
documentation.append(_(u"""The following is a description for every kind of TWBlue's buffer and the kind of items they work with."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Home: it shows all the tweets on the main timeline. These are the tweets by users you follow."""))
|
||||
@@ -86,39 +87,37 @@ documentation.append(_(u"""* Followers: when users follow you, you'll be able to
|
||||
documentation.append(_(u"""* Friends: the same as the previous list, but these are the users you follow."""))
|
||||
documentation.append(_(u"""* User timelines: these are lists you may create. They contain only the tweets by a specific user. They're used so you can see the tweets by a single person and you don't want to look all over your timeline. You may create as many as you like."""))
|
||||
documentation.append(_(u"""* Events: An event is anything that happens on Twitter, such as when someone follows you, when someone adds or removes one of your tweets from their favorites list, or when you subscribe to a list. There are many more but TW Blue shows the most common ones in the events buffer so that you can easily keep track of what is happening on your account."""))
|
||||
documentation.append(_(u"""* Lists: A list is similar to a temporary timeline, except that you can configure it to contain tweets from multiple users. This is currently an experimental feature. If you decide to use it, please report any problems you encounter."""))
|
||||
documentation.append(_(u"""* Lists: A list is similar to a temporary timeline, except that you can configure it to contain tweets from multiple users."""))
|
||||
documentation.append(_(u"""* Search: A search buffer contains the results of a search operation."""))
|
||||
documentation.append(_(u"""* User favorites: You can have TW Blue create a buffer containing tweets favorited by a particular user."""))
|
||||
documentation.append(_(u"""* Trending Topics: a trend buffer shows the top ten most used terms in a geographical region. This region may be a country or a city. Trends are updated every five minutes."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Note: In this version of TW Blue, you will be able to see up to (or around) 400 friends and followers in their respective buffers. In the next version, we will provide a solution for those who have more to be able to see them."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Bear in mind the default configuration only allows getting the last 200 tweets for the home,, mentions, direct messages, and user timeline lists. You can change this on the setup dialogue. For the sent list, the last 200 tweets and the last 200 sent direct messages will be retrieved. Future versions will allow changing this parameter."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""If there's a URL on a tweet TW Blue will try to open it when you press enter on it. If there are several, it will show you a list with all of them so you choose the one you want. If you're on the followers or friends dialogue, the enter key will show you additional information on them."""))
|
||||
documentation.append(_(u"""If there's a URL on a tweet, TW Blue will try to open it when you press enter on it. If there are several, it will show you a list with all of them so you choose the one you want. If you're on the followers or friends buffer, the enter key will show you additional information about them."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""If you press control-enter, TW Blue will try to play the audio from the focused tweet, as long as it has a URL. If it has the #audio hashtag, you will hear a sound when it is selected, letting you know you can try to play it. However, a tweet can be missing the hashtag and TW Blue will still be able to play it so long as it contains a URL with audio."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Controls {#controls}"""))
|
||||
documentation.append(_(u"""You will also hear a sound when you see any tweet containing geographical information. You can see someone's location by selecting the option "view address" from the tweet menu on the menu bar."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Beginning with the latest version, there's support for an interface which does not require a visible window. It can be activated by pressing control-m, or choosing hide window from the application menu. This interface is entirely driven through shortcut keys. These shortcuts are different from those used to drive the graphical interface. Each interface can use only its own shortcuts, so you may not use the invisible shortcuts if you have the graphical interface opened. This section describes both the graphical and the invisible interface."""))
|
||||
documentation.append(_(u"""## Controls"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""### The graphical user interface (GUI) {#gui}"""))
|
||||
documentation.append(_(u"""Beginning with the 0.36 version, there's support for an interface which does not require a visible window. It can be activated by pressing control-m, or choosing hide window from the application menu. This interface is entirely driven through shortcut keys. These shortcuts are different from those used to drive the graphical interface. By default, you can't use the invisible interface shortcuts on the GUI. It has been made this way to keep compatibility with applications like TheQube and Chicken nugget which may use the same shortcuts. If you wish to have available the invisible interface shortcuts even if you are using the GUI, activate this option on the General tab of the preferences dialogue. This section describes both the graphical and the invisible interface."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""### The graphical user interface (GUI)"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Here you have a list divided into two parts. On the one hand, the buttons you will find while tabbing around on the program's interface, and on the other, the different elements present on the menu bar."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""#### Buttons on the application {#buttons}"""))
|
||||
documentation.append(_(u"""#### Buttons on the application"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Tweet: this button opens up a dialogue box to write your tweet. The message must not exceed 140 characters. If you write past this limit, a sound will play to warn you. You may use the shorten and expand URL buttons to comply with the character limit. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will say an error message in English describing the problem."""))
|
||||
documentation.append(_(u"""* Tweet: this button opens up a dialogue box to write your tweet. The message must not exceed 140 characters. If you write past this limit, a sound will play to warn you. You may use the shorten and expand URL buttons to comply with the character limit. You can translate your message, upload a picture, check spelling or attach audio by selecting one of the available buttons in the dialogue. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will say an error message in English describing the problem."""))
|
||||
documentation.append(_(u"""* Retweet: this button retweets the message you're reading. After you press it, you'll be asked if you want to add a comment or simply send it as written."""))
|
||||
documentation.append(_(u"""* Reply: when you're viewing a tweet, you can reply to the user who sent it by pressing this button. A dialogue will open up like the one for tweeting, but with the name of the user already filled in (for example @user) so you only need to write your message. If there are more users mentioned on the tweet, you can press shift-tab and press the mention all users button. When you're on the friends or followers lists, the button will be called mention instead."""))
|
||||
documentation.append(_(u"""* Direct message: exactly like sending a tweet, but it's a private message which can only be read by the user you send it to. Press shift-tab to see the recipient. If there were other users mentioned on the tweet you were reading, you can arrow up or down to choose which one to send it to, or write the username yourself without the at sign."""))
|
||||
@@ -127,215 +126,86 @@ documentation.append(_(u"""
|
||||
documentation.append(_(u"""Bear in mind that buttons will appear according to which actions are possible on the list you are browsing. For example, on the home timeline, mentions, sent, favourites and user timelines you will see the four buttons, while on the direct messages list you'll only get the direct message and tweet buttons, and on friends and followers lists you will get the direct message, tweet, and mention buttons."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""#### Menus {#menus}"""))
|
||||
documentation.append(_(u"""#### Menus"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""On top of the program window there's a menu bar which has the same functions, and some more. To access the menu bar, press alt. You will find four menus: application, tweet, user and help. This section describes the items on each one of them."""))
|
||||
documentation.append(_(u"""On top of the program window there's a menu bar which has the same functions, and some more. To access the menu bar, press alt. You will find five: application, tweet, user, buffer and help. This section describes the items on each one of them."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""##### Application menu {#app}"""))
|
||||
documentation.append(_(u"""##### Application menu"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Session manager: Opens a window with all the sessions configured in TWBlue, where you can add new sessions or delete the ones you've already created."""))
|
||||
documentation.append(_(u"""* Update profile: opens a dialogue box where you can update your information on Twitter: name, location, URL and bio. If you have already set this up the fields will be prefilled with the existing information. Also, you can upload a photo to your profile."""))
|
||||
documentation.append(_(u"""* Hide window: turns off the Graphical User Interface. Read the section on the invisible interface for further details."""))
|
||||
documentation.append(_(u"""* Search: shows a dialog where you can search for tweets or users on Twitter."""))
|
||||
documentation.append(_(u"""* View trending topics: It opens a buffer to get the trending topics of a country or a city. You'll be able to select from a dialog if you wish to get countries' trends or cities' trends and choose one from the selected list. The trending topics buffer will be created once pressing "ok" on this dialog. Remember this kind of buffer will be updated every five minutes."""))
|
||||
documentation.append(_(u"""* Lists Manager: This dialog allows you to manage your Twitter lists. In order to use them, you must first create them. Here, you can view, edit, create, delete or, optionally, open them in buffers similar to temporary timelines."""))
|
||||
documentation.append(_(u"""* Sound tutorial: Opens a dialog where you can familiarize yourself with the different sounds of the program."""))
|
||||
documentation.append(_(u"""* Preferences: opens up a preference dialogue box from which you can control some of the program settings. The options need no explanation."""))
|
||||
documentation.append(_(u"""* Quit: asks whether you want to exit the program. If the answer is yes, it shuts the application down."""))
|
||||
documentation.append(_(u"""* Sounds tutorial: Opens a dialog where you can familiarize yourself with the different sounds of the program."""))
|
||||
documentation.append(_(u"""* Edit keystrokes: It opens a dialog where you can see and re edit the invisible interface shortcuts."""))
|
||||
### Add description for the global settings and accounts settings options.
|
||||
documentation.append(_(u"""* Quit: asks whether you want to exit the program. If the answer is yes, it shuts the application down. If you wish TWBlue not to ask you for confirmation before exiting, uncheck the checkbox from the preferences dialogue."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""##### Tweet menu {#tweet}"""))
|
||||
documentation.append(_(u"""##### Tweet menu"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* You will first find the items to tweet, reply and retweet, which are equivalent to the buttons with the same name."""))
|
||||
documentation.append(_(u"""* Mark as favourite: marks the tweet you're viewing as a favourite."""))
|
||||
documentation.append(_(u"""* Remove tweet from favourites: removes the tweet from your favourites, but not from Twitter."""))
|
||||
documentation.append(_(u"""* Show tweet: opens up a dialogue box where you can read the tweet, direct message, friend or follower under focus. You can read the text with the cursors. It's the same dialogue box used to write tweets on."""))
|
||||
documentation.append(_(u"""* Show tweet: opens up a dialogue box where you can read the tweet, direct message, friend or follower under focus. You can read the text with the arrow keys. It's the same dialogue box used to write tweets on."""))
|
||||
documentation.append(_(u"""* View address: If the selected tweet has geographical information, TWBlue may display a dialog where you can read the tweet address. This address is got by sending the geographical coordinates of the tweet to Google maps."""))
|
||||
### Add description for view conversation feature
|
||||
documentation.append(_(u"""* Delete: permanently removes the tweet or direct message you're on from Twitter and from your lists. Bear in mind that Twitter only allows you to delete tweets you have posted yourself."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""##### User menu {#user}"""))
|
||||
documentation.append(_(u"""##### User menu"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Bear in mind the four topmost items on this menu open up the same dialogue box. This box has an edit field where you can choose the user you want to act on, by using up and down arrows or by writing the text in yourself. Afterwards you will find a group with four radio buttons to follow, unfollow, report as spam and block. If you choose the follow menu item, the radio button on the dialogue box will be set to follow, and the same applies to unfollow, report as spam and block. Press OK to try to carry out the action. If it doesn't succeed, you'll hear the error message in English."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""The remaining items on the menu are described below:"""))
|
||||
documentation.append(_(u"""The available actions you can choose are described below:"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Follow: Follows a user. This means you'll see his/her tweets on your main timeline, and if he/she also follows you, you'll be able to interchange direct messages."""))
|
||||
documentation.append(_(u"""* Unfollow: Stops following a user, which causes you not being able to see his/her tweets on your main timeline neither interchanging direct messages."""))
|
||||
documentation.append(_(u"""* Mute: While muting someone, TWBlue won't show you nor his/her tweets on your main timeline; neither you'll see that person's mentions. But you both will be able to interchange direct messages. The muted user is not informed of this action."""))
|
||||
documentation.append(_(u"""* Unmute: It turns the way TWBlue treats this user to its normal way. You will see his/her tweets and mentions again."""))
|
||||
documentation.append(_(u"""* Report as spam: It suggests twitter this user is performing prohibited practices on the social network."""))
|
||||
documentation.append(_(u"""* Block: Blocks a user. This forces the user to unfollow you ."""))
|
||||
documentation.append(_(u"""* Unblock: Stops blocking a user.""")
|
||||
documentation.append(_(u"""* Direct message: same action as the button."""))
|
||||
documentation.append(_(u"""* Add to List: In order to see someone's tweets in one or more of your lists, you must add them first. This option will open a dialog where you can select the user you wish to add. Next, you will be asked to select the list you wish to add them to. Afterwards, the list will contain a new member and their tweets will show up there."""))
|
||||
### add description for remove from list and view lists.
|
||||
documentation.append(_(u"""* View user profile: opens up a dialogue box to choose the user whose profile you want to browse."""))
|
||||
documentation.append(_(u"""* Timeline: opens up a dialogue box to choose whose user you want a timeline for. Create it by pressing enter. If you try it with a user that has no tweets, the program will fail. If you try creating an already existing timeline the program will warn you and will not create it again."""))
|
||||
documentation.append(_(u"""* Timeline: Lets you open a user's timeline by choosing the user in a dialog box. It is created when you press enter. If you try it with a user that has no tweets, the program will fail. If you try creating an already existing timeline the program will warn you and will not create it again."""))
|
||||
documentation.append(_(u"""* View favourites: Opens a buffer where you can see what tweets have been favorited by a particular user."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""##### Buffer menu{#buffer}"""))
|
||||
documentation.append(_(u"""##### Buffer menu"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
### add description for load previous items.
|
||||
documentation.append(_(u"""* Mute buffer: Mutes notifications of a particular buffer so you will not hear when new tweets arrive."""))
|
||||
documentation.append(_(u"""* autoread tweets for this buffer: When enabled, the screen reader or SAPI 5 (if enabled) will read the text of incoming tweets. Please note that this could get rather chatty if there are a lot of incoming tweets."""))
|
||||
documentation.append(_(u"""* Clear buffer: Deletes all items from the buffer."""))
|
||||
documentation.append(_(u"""* Remove buffer: dismiss the list you're on."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""##### Help menu {#help}"""))
|
||||
documentation.append(_(u"""##### Help menu"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Documentation: opens up this file, where you can read some useful program concepts."""))
|
||||
documentation.append(_(u"""* What's new in this version?: opens up a document with the list of changes from the current version down to the first."""))
|
||||
documentation.append(_(u"""* Check for updates: every time you open the program it automatically checks for new versions. If there are, it will ask you if you want to download it. If you accept, it will do so, after which it will install it and ask you to let it restart itself, which it does automatically. This item checks for new updates without having to restart the application."""))
|
||||
documentation.append(_(u"""* TW Blue's website: visit our [home page](http://twblue.com.mx) where you can find all relevant information and downloads for TW Blue and become a part of the community."""))
|
||||
documentation.append(_(u"""* Report a bug: opens up a dialogue box to report a bug by filling a couple of fields: the title and a short description of what happened. Pressing enter will send the report. If the operation doesn't succeed the program will show a warning."""))
|
||||
documentation.append(_(u"""* Check for updates: every time you open the program it automatically checks for new versions. If there is any, it will ask you if you want to download it. If you accept, it will do so, after which it will install it and ask you to let it restart itself, which it does automatically. This item checks for new updates without having to restart the application."""))
|
||||
documentation.append(_(u"""* Report a bug: opens up a dialogue box to report a bug by filling a couple of fields. Pressing enter will send the report. If the operation doesn't succeed the program will show a warning."""))
|
||||
documentation.append(_(u"""* TW Blue's website: visit our [home page](http://twblue.es) where you can find all relevant information and downloads for TW Blue and become a part of the community."""))
|
||||
documentation.append(_(u"""* About TW Blue: shows the credits of the program."""))
|
||||
documentation.append(_(u"""
|
||||
documentation.append(_(u"""...
|
||||
"""))
|
||||
documentation.append(_(u"""### Invisible interface {#invisible_interface}"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""If you press control-m, or if you choose hide window from the application menu, you will activate an interface that cannot be used in the usual way, because it is invisible."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Every action on the invisible interface is done through keyboard shortcuts, even browsing lists. Eventually you may open dialogue boxes and these will be visible, but not the application's main window. Read the section on invisible interface shortcuts to know which ones you can use for the time being."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""### Keyboard shortcuts for the graphical interface {#shortcuts}"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Instead of using the buttons and menus, most actions can be carried out by pressing a key combination. The ones available at present are described below:"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Enter: open a URL. If there are more than one you will get a list that will allow you to choose the one you want. On the friends or followers lists it will show details on the selected item."""))
|
||||
documentation.append(_(u"""* Control-enter: attempt to play audio from URL."""))
|
||||
documentation.append(_(u"""* F5: decrease sounds volume. It affects the program sounds as well as audio played from the program."""))
|
||||
documentation.append(_(u"""* F6: increase application sounds volume."""))
|
||||
documentation.append(_(u"""* Control-n: open new tweet dialogue."""))
|
||||
documentation.append(_(u"""* Control-m: hide window."""))
|
||||
documentation.append(_(u"""* Control-q: quit."""))
|
||||
documentation.append(_(u"""* Control-r: open reply tweet dialogue."""))
|
||||
documentation.append(_(u"""* Control-shift-r: Retweet."""))
|
||||
documentation.append(_(u"""* Control-d: send direct message."""))
|
||||
documentation.append(_(u"""* Control-f: mark as favourite."""))
|
||||
documentation.append(_(u"""* Control-shift-f: remove from favourites."""))
|
||||
documentation.append(_(u"""* Control-shift-v: view tweet."""))
|
||||
documentation.append(_(u"""* Control-s: follow a user."""))
|
||||
documentation.append(_(u"""* Control-shift-s: unfollow a user."""))
|
||||
documentation.append(_(u"""* Control-k: block a user."""))
|
||||
documentation.append(_(u"""* Control-shift-k: report as spam."""))
|
||||
documentation.append(_(u"""* Control-i: open user's timeline."""))
|
||||
documentation.append(_(u"""* Control-shift-i: remove timeline."""))
|
||||
documentation.append(_(u"""* Control-p: edit profile."""))
|
||||
documentation.append(_(u"""* Delete: remove tweet or direct message."""))
|
||||
documentation.append(_(u"""* Shift-delete: empty the buffer removing all the elements. This doesn't remove them from Twitter itself."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""### Keyboard shortcuts for the invisible interface {#invisible_shortcuts}"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""These are the shortcuts you may use from the invisible interface. Bear in mind that when the graphical user interface is shown you may not use these. By "win" the left windows key is intended."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Control+win+up arrow: go up on the current list."""))
|
||||
documentation.append(_(u"""* Control+win+down arrow: go down on the current list."""))
|
||||
documentation.append(_(u"""* Control+win+left arrow: go to the previous tab."""))
|
||||
documentation.append(_(u"""* Control+win+right arrow: go to the next tab."""))
|
||||
documentation.append(_(u"""* Control+win+home: go to the first element on the list."""))
|
||||
documentation.append(_(u"""* Control+win+end: go to the last element on the list."""))
|
||||
documentation.append(_(u"""* Control+win+page down: move 20 elements down on the current list."""))
|
||||
documentation.append(_(u"""* Control+win+page up: move 20 elements up on the current list."""))
|
||||
documentation.append(_(u"""* Control+win+alt+up arrow: increase volume by 5%."""))
|
||||
documentation.append(_(u"""* control+win+alt+down arrow: decrease volume by 5%."""))
|
||||
documentation.append(_(u"""* Control+win+enter: open URL on the current tweet, or further information for a friend or follower."""))
|
||||
documentation.append(_(u"""* control+win+alt+enter: attempt to play audio."""))
|
||||
documentation.append(_(u"""* control+win+m: show the graphical interface, turning the invisible one off."""))
|
||||
documentation.append(_(u"""* Control+win+n: new tweet."""))
|
||||
documentation.append(_(u"""* Control+win+r: reply to tweet."""))
|
||||
documentation.append(_(u"""* Control+win+shift-r: retweet."""))
|
||||
documentation.append(_(u"""* Control+win+d: send direct message."""))
|
||||
documentation.append(_(u"""* Control+win+delete: remove a tweet or direct message."""))
|
||||
documentation.append(_(u"""* Control+win+shift+delete: empty the buffer removing all the elements. This doesn't remove them from Twitter itself."""))
|
||||
documentation.append(_(u"""* Win+alt+f: mark as favourite."""))
|
||||
documentation.append(_(u"""* Win+alt+shift+f: remove from favourites."""))
|
||||
documentation.append(_(u"""* Control+win+s: follow a user."""))
|
||||
documentation.append(_(u"""* Control+win+shift+s: unfollow a user."""))
|
||||
documentation.append(_(u"""* Control+win+alt+n: see user details."""))
|
||||
documentation.append(_(u"""* Control+win+v: show tweet on an edit box."""))
|
||||
documentation.append(_(u"""* Control+win+i: open user timeline."""))
|
||||
documentation.append(_(u"""* Control+win+shift+i: remove user timeline."""))
|
||||
documentation.append(_(u"""* Alt+win+p: edit profile."""))
|
||||
documentation.append(_(u"""* Control+win+c: Copy to clipboard."""))
|
||||
documentation.append(_(u"""* Control+win+space: Listen the current message."""))
|
||||
documentation.append(_(u"""* Control+win+a: Add to list."""))
|
||||
documentation.append(_(u"""* Control+win+shift+a: Remove from list."""))
|
||||
documentation.append(_(u"""* Control+Win+Shift+M: Mutes/unmutes the active buffer."""))
|
||||
documentation.append(_(u"""* Control+Win+E: toggles the automatic reading of incoming tweets in the active buffer."""))
|
||||
documentation.append(_(u"""* Control+Win+Shift+Up arrow: move up one tweet in the conversation."""))
|
||||
documentation.append(_(u"""* Control+Win+Shift+Down arrow: move down one tweet in the conversation."""))
|
||||
documentation.append(_(u"""* Win+Alt+M: Globally mute/unmute TW Blue."""))
|
||||
documentation.append(_(u"""* control+win+minus: Search on twitter."""))
|
||||
documentation.append(_(u"""* Control+win+f4: quit."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Lists {#lists}"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""One of the most useful features of Twitter is the ability to create lists. Lists allow you to group users whose tweets you wish to see together instead of viewing their individual buffers. A common example of this would be if you follow multiple tech news accounts; it would be more convenient to have, for example, a "Tech News" list in which you can see tweets from these similar accounts together. A temporary buffer, such as what is created when you are viewing an individual person's timeline, is created and you can add/remove people from the list."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""In TW Blue, we have begun working on this feature little by little. It is still experimental but is in working condition. Below, we will explain how to configure lists."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* First, you will need to open the lists manager which can be found under the application menu."""))
|
||||
documentation.append(_(u"""* In the lists manager, you will first see the lists you have created followed by those which you are a member. If you see no lists, it means that you have not created any and that you are not a part of any list."""))
|
||||
documentation.append(_(u"""* You will then see a group of buttons: Create a New List, Edit, Remove and Open in Buffer. Perhaps the last one is a bit less self-explanatory: it will open the list in a buffer similar to when opening someone's timeline. """))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Once you have created a new list, the next step will be to add users to it. If you were to open it in a buffer right now, it would be empty and no tweets would show up in it. To add users, follow these steps:"""))
|
||||
documentation.append(_(u"""* While browsing your tweets, find a tweet from the user which you wish to add to a list. Next, press ctrl+win+A or select "Add to List" from the User menu."""))
|
||||
documentation.append(_(u"""* A dialog will then appear asking for the user which you wish to add. The name of the user whose tweet you just selected should already be in the box. Simply confirm that it is correct and press the "OK" button."""))
|
||||
documentation.append(_(u"""* Another dialog will appear showing all of your lists. Arrow to the one you want and press the "Add" button."""))
|
||||
documentation.append(_(u"""* To remove a user from a list, repeat the same process but press ctrl+win+shift+A and, from the dialog that appears, choose the list from which you wish to remove the selected user."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Reporting bugs from the web {#reporting}"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Note: if you're running the program you can also report a bug from it, by using the help menu item. This process only allows for two edit fields and takes care of the rest. These steps are described for those who can't run the program, don't have it opened at a given moment, or simply want to report from the web instead of using the integrated bug reporting system."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""All things under the sun (yes, this includes computer programs) are very far from being perfect, so often you may find unforeseen bugs in the application. But as our intent is to always improve you're free (what's more, it would be great if you did) to report the bugs you find on the program so they can be reviewed and eventually fixed"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""In order to open the bug tracker's web, [follow this link](http://twblue.com.mx/errores/bug_report_page.php) It's a website with a form where you must fill several fields. Only three of them are really required (those marked with a star), but the more you can fill the better."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Here are the different form fields and what you must enter on each. Remember only fields mark witha star are required:"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""* Category: this field allows to choose what category to assign the bug to. You can choose general, if it's a program error, or documentation, if you have found a mistake in this manual or the changes list. This is a required field."""))
|
||||
documentation.append(_(u"""* Reproducibility: here you must indicate how easy or hard it is to reproduce the bug. Available options are unknown, not reproducible, not attempted (by default), random, sometimes, or always. Depending on whether you can reproduce the error or not, you should choose the one closest to your situation. If you're making a feature request, this field is irrelevant."""))
|
||||
documentation.append(_(u"""* Severity: here you choose how much it affects the program. Available options are functionality (choose this for a feature request), trivial, text, setting, minor, major, failure, or crash. Note the options go in increasing order. Choose the one which fits the situation best. If you're not sure which to choose you can leave it as it is."""))
|
||||
documentation.append(_(u"""* Priority: choose according to the importance of the bug or feature requested. Available options are none, low, normal, high, urgent, and immediate."""))
|
||||
documentation.append(_(u"""* Choose profile: here you can choose between 32 or 64 bit architecture and OS (Windows 7 for now). If they don't fit, you can fill the edit fields below with your specific information."""))
|
||||
documentation.append(_(u"""* Product version: choose the version of the program you're running in order to find out when the error was introduced. This field will contain a sorted list of the available versions. Although it's not required, it would help a lot in quickly finding the bug."""))
|
||||
documentation.append(_(u"""* Summary: a title for the bug, explaining in a few words what the problem is. It's a required text field."""))
|
||||
documentation.append(_(u"""* Description: this required field asks you to describe in more detail what happened to the program."""))
|
||||
documentation.append(_(u"""* Steps to reproduce: this field is used if you know how to cause the error. It's not required, but it would help a lot knowing how the program gets to the error in order to track it down."""))
|
||||
documentation.append(_(u"""* Additional information: if you have a comment or note to add, it can go here. It's not required."""))
|
||||
documentation.append(_(u"""* File attachment: you can attach the TW Blue.exe.log generated due to the bug. It is not required."""))
|
||||
documentation.append(_(u"""* Visibility: choose if you want the bug to be publically visible or private. By default it's public, and it's recommended to keep it that way."""))
|
||||
documentation.append(_(u"""* Send report: press the button to send the report and have it looked into."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""Many thanks for your participation in reporting bugs and trying out new functionality."""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""## Contact {#contact}"""))
|
||||
documentation.append(_(u"""## Contact"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""If what's explained in this document is not enough, if you want to collaborate in some other way, or if you simply want to get in touch with the application developer, follow the Twitter account [@tw_blue2](https://twitter.com/tw_blue2) or [@manuelcortez00.](https://twitter.com/manuelcortez00) You can also visit [our website](http://twblue.com.mx)"""))
|
||||
documentation.append(_(u"""
|
||||
"""))
|
||||
documentation.append(_(u"""---"""))
|
||||
documentation.append(_(u"""Copyright © 2013-2014. Manuel Cortéz"""))
|
||||
documentation.append(_(u"""Copyright © 2013-2015. Manuel Cortéz"""))
|
||||
|
23
mysc/keys/api_keys.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "api_keys.h"
|
||||
char *get_api_key(){
|
||||
return "key\0";
|
||||
}
|
||||
char *get_api_secret(){
|
||||
return "secret_key\0";
|
||||
}
|
||||
char *get_dropbox_api_key(){
|
||||
return "key\0";
|
||||
}
|
||||
char *get_dropbox_api_secret(){
|
||||
return "secret_key\0";
|
||||
}
|
||||
char *get_twishort_api_key(){
|
||||
return "key\0";
|
||||
}
|
||||
char *get_bts_user(){
|
||||
return "user\0";
|
||||
}
|
||||
char *get_bts_password(){
|
||||
return "pass\0";
|
||||
}
|
||||
|
12
mysc/keys/api_keys.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _API_KEYS_H
|
||||
#define API_KEYS_H
|
||||
|
||||
char *get_api_key();
|
||||
char *get_api_secret();
|
||||
char *get_dropbox_api_key();
|
||||
char *get_dropbox_api_secret();
|
||||
char *get_twishort_api_key();
|
||||
char *get_bts_user();
|
||||
char *get_bts_password();
|
||||
|
||||
#endif
|
BIN
mysc/pa.c format/App/AppInfo/Launcher/Splash.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
8
mysc/pa.c format/App/AppInfo/Launcher/TWBluePortable.ini
Normal file
@@ -0,0 +1,8 @@
|
||||
[Launch]
|
||||
ProgramExecutable=TWBlue\TWBlue.exe
|
||||
ProgramExecutable64=TWBlue64\TWBlue.exe
|
||||
CommandLineArguments=-p -d "%PAL:DataDir%"
|
||||
SinglePortableAppInstance=true
|
||||
MinOS=XP
|
||||
SingleAppInstance=false
|
||||
DirectoryMoveOK=yes
|
BIN
mysc/pa.c format/App/AppInfo/appicon.ico
Normal file
After Width: | Height: | Size: 182 KiB |
BIN
mysc/pa.c format/App/AppInfo/appicon_128.png
Normal file
After Width: | Height: | Size: 379 B |
BIN
mysc/pa.c format/App/AppInfo/appicon_16.png
Normal file
After Width: | Height: | Size: 103 B |
BIN
mysc/pa.c format/App/AppInfo/appicon_32.png
Normal file
After Width: | Height: | Size: 119 B |
28
mysc/pa.c format/App/AppInfo/appinfo.ini
Normal file
@@ -0,0 +1,28 @@
|
||||
[Format]
|
||||
Type=PortableApps.comFormat
|
||||
Version=3.0
|
||||
|
||||
[Details]
|
||||
Name=TWBlue portable
|
||||
AppID=TWBluePortable
|
||||
Publisher=jmdaweb & TWBlue & PortableApps.com
|
||||
Homepage=PortableApps.com/TWBluePortable
|
||||
Category=Internet
|
||||
Description=A portable, fast and accessible Twitter client with many options.
|
||||
Language=Multilingual
|
||||
InstallType=Multilingual
|
||||
|
||||
[License]
|
||||
Shareable=true
|
||||
OpenSource=true
|
||||
Freeware=true
|
||||
CommercialUse=true
|
||||
EULAVersion=2
|
||||
|
||||
[Version]
|
||||
PackageVersion=0.80.0.0
|
||||
DisplayVersion=0.80
|
||||
|
||||
[Control]
|
||||
Icons=1
|
||||
Start=TWBluePortable.exe
|
339
mysc/pa.c format/App/AppInfo/eula.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
17
mysc/pa.c format/App/AppInfo/installer.ini
Normal file
@@ -0,0 +1,17 @@
|
||||
[Languages]
|
||||
ENGLISH=true
|
||||
ENGLISHGB=true
|
||||
ARABIC=true
|
||||
BASQUE=true
|
||||
CATALAN=true
|
||||
FINNISH=true
|
||||
FRENCH=true
|
||||
GALICIAN=true
|
||||
GERMAN=true
|
||||
HUNGARIAN=true
|
||||
ITALIAN=true
|
||||
POLISH=true
|
||||
PORTUGUESEBR=true
|
||||
RUSSIAN=true
|
||||
SPANISHINTERNATIONAL=true
|
||||
TURKISH=true
|
3
mysc/pa.c format/App/Readme.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The files in this directory are necessary for the portable application to
|
||||
function. There is normally no need to directly access or alter any of the
|
||||
files within these directories.
|
339
mysc/pa.c format/App/license.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
BIN
mysc/pa.c format/Other/Help/Images/Donation_Button.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
mysc/pa.c format/Other/Help/Images/Favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
mysc/pa.c format/Other/Help/Images/Help_Background_Footer.png
Normal file
After Width: | Height: | Size: 168 B |
BIN
mysc/pa.c format/Other/Help/Images/Help_Background_Header.png
Normal file
After Width: | Height: | Size: 269 B |
BIN
mysc/pa.c format/Other/Help/Images/Help_Logo_Top.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
339
mysc/pa.c format/Other/Source/LauncherLicense.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
41
mysc/pa.c format/Other/Source/Readme.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
You can get tw blue's source code at https://github.com/manuelcortez/TWBlue
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
|
||||
This package's installer and launcher are released under the GPL. The launcher
|
||||
is the PortableApps.com Launcher, available with full source and documentation
|
||||
from http://portableapps.com/development. We request that developers using the
|
||||
PortableApps.com Launcher please leave this directory intact and unchanged.
|
||||
|
||||
USER CONFIGURATION
|
||||
==================
|
||||
|
||||
Some configuration in the PortableApps.com Launcher can be overridden by the
|
||||
user in an INI file next to TWBluePortable.exe called TWBluePortable.ini.
|
||||
If you are happy with the default options, it is not necessary, though. There
|
||||
is an example INI included with this package to get you started. To use it,
|
||||
copy AppNamePortable.ini from this directory to TWBluePortable.ini next to
|
||||
TWBluePortable.exe. The options in the INI file are as follows:
|
||||
|
||||
AdditionalParameters=
|
||||
DisableSplashScreen=false
|
||||
RunLocally=false
|
||||
|
||||
(There is no need for an INI header in this file; if you have one, though, it
|
||||
won't damage anything.)
|
||||
|
||||
The AdditionalParameters entry allows you to pass additional command-line
|
||||
parameters to the application.
|
||||
|
||||
The DisableSplashScreen entry allows you to run the launcher without the splash
|
||||
screen showing up. The default is false.
|
||||
|
||||
The RunLocally entry allows you to run the portable application from a read-
|
||||
only medium. This is known as Live mode. It copies what it needs to to a
|
||||
temporary directory on the host computer, runs the application, and then
|
||||
deletes it afterwards, leaving nothing behind. This can be useful for running
|
||||
the application from a CD or if you work on a computer that may have spyware or
|
||||
viruses and you'd like to keep your device set to read-only. As a consequence
|
||||
of this technique, any changes you make during the Live mode session aren't
|
||||
saved back to your device. The default is false.
|
6
mysc/pa.c format/Other/Source/TWBluePortable.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
AdditionalParameters=
|
||||
DisableSplashScreen=false
|
||||
RunLocally=false
|
||||
|
||||
# The above options are explained in the included readme.txt
|
||||
# This INI file is an example only and is not used unless it is placed as described in the included readme.txt
|
150
mysc/pa.c format/help.html
Normal file
@@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>TWBlue Portable Help</title>
|
||||
<link rel="alternate" href="http://portableapps.com/feeds/general" type="application/rss+xml" title="PortableApps.com">
|
||||
<link rel="shortcut icon" href="Other/Help/Images/Favicon.ico">
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family : Verdana,Arial,Helvetica,sans-serif;
|
||||
font-size : 76%;
|
||||
color : black;
|
||||
margin : 20px;
|
||||
background : #e6e8ea;
|
||||
text-align : center;
|
||||
}
|
||||
a {
|
||||
color : #b31616;
|
||||
font-weight : bold;
|
||||
}
|
||||
a:link, a:visited, a:active {}
|
||||
a:hover {
|
||||
color : red;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family : Arial, sans-serif;
|
||||
font-weight : normal;
|
||||
}
|
||||
h1 {
|
||||
color : #b31616;
|
||||
font-weight : bold;
|
||||
letter-spacing : -2px;
|
||||
font-size : 2.2em;
|
||||
border-bottom : 1px solid silver;
|
||||
padding-bottom : 5px;
|
||||
}
|
||||
h2 {
|
||||
font-size : 1.5em;
|
||||
border-bottom : 1px solid silver;
|
||||
padding-bottom : 3px;
|
||||
clear : both;
|
||||
}
|
||||
h3 { font-size : 1.2em; }
|
||||
h4 { font-size : 1.1em; }
|
||||
h5 { font-size : 1.0em; }
|
||||
h6 { font-size : 0.8em; }
|
||||
img { border : 0; }
|
||||
ol, ul, li, p, pre, table, tr, td, th { font-size : 1.0em; }
|
||||
pre { font-family : monospace; }
|
||||
strong, b { font-weight : bold; }
|
||||
td, th {
|
||||
border : 1px solid #aaaaaa;
|
||||
border-collapse : collapse;
|
||||
padding : 3px;
|
||||
}
|
||||
th {
|
||||
background : #3667a8;
|
||||
color : white;
|
||||
}
|
||||
ol ol { list-style-type : lower-alpha; }
|
||||
.content {
|
||||
text-align : left;
|
||||
margin-left : auto;
|
||||
margin-right : auto;
|
||||
width : 780px;
|
||||
background-color : white;
|
||||
border-left : 1px solid black;
|
||||
border-right : 1px solid black;
|
||||
padding : 12px 30px;
|
||||
line-height : 150%;
|
||||
}
|
||||
.logo {
|
||||
background : white url("Other/Help/Images/Help_Background_Header.png") repeat-x;
|
||||
width : 840px;
|
||||
margin-top : 20px;
|
||||
margin-left : auto;
|
||||
margin-right : auto;
|
||||
text-align : left;
|
||||
border-right : 1px solid black;
|
||||
border-left : 1px solid black;
|
||||
}
|
||||
.footer {
|
||||
background : white url("Other/Help/Images/Help_Background_Footer.png") repeat-x;
|
||||
width : 840px;
|
||||
height : 16px;
|
||||
margin-left : auto;
|
||||
margin-right : auto;
|
||||
text-align : left;
|
||||
border-right : 1px solid black;
|
||||
border-left : 1px solid black;
|
||||
}
|
||||
.logo img {
|
||||
padding-left : 0px;
|
||||
border : none;
|
||||
position : relative;
|
||||
top : -4px;
|
||||
}
|
||||
* html .content { width : 760px; }
|
||||
* html .logo, * html .footer { width : 820px; }
|
||||
.content h1 { margin : 0px; }
|
||||
h1.hastagline { border : 0; }
|
||||
h2.tagline {
|
||||
color : #747673;
|
||||
clear : none;
|
||||
margin-top : 0em;
|
||||
}
|
||||
|
||||
/* printer styles */
|
||||
@media print {
|
||||
body, .content {
|
||||
margin : 0;
|
||||
padding : 0;
|
||||
}
|
||||
.navigation, .locator, .footer a, .message, .footer-links { display : none; }
|
||||
.footer, .content, .header { border : none; }
|
||||
a {
|
||||
text-decoration : none;
|
||||
font-weight : normal;
|
||||
color : black;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="logo"><a href="http://portableapps.com/"><img src="Other/Help/Images/Help_Logo_Top.png" alt="PortableApps.com - Your Digital Life, Anywhere"></a></div>
|
||||
<div class="content">
|
||||
<h1 class="hastagline">TWBlue Portable Help</h1>
|
||||
<h2 class="tagline">A powerful and accessible Twitter client</h2>
|
||||
<p>TWBlue Portable is the TWBlue whatever it is packaged with a PortableApps.com launcher as a <a href="http://portableapps.com/about/what_is_a_portable_app">portable app</a>, so you can view and send tweets on your iPod, USB flash drive, portable hard drive, etc. It has all the same features as TWBlue, plus, it leaves no personal information behind on the machine you run it on, so you can take it with you wherever you go. <a href="http://twblue.es">Learn more about TWBlue...</a></p>
|
||||
|
||||
<p><a href="http://portableapps.com/donate"><img src="Other/Help/Images/Donation_Button.png" style="vertical-align:middle" alt="Make a Donation"></a> - Support PortableApps.com's Hosting and Development</p>
|
||||
|
||||
<p><a href="http://portableapps.com/node/*Node ID*">Go to the TWBlue Portable Homepage >></a></p>
|
||||
<p><a href="http://portableapps.com/">Get more portable apps at PortableApps.com</a></p>
|
||||
|
||||
<p>This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative.</p>
|
||||
|
||||
<h2>Portable App Issues</h2>
|
||||
<ul>
|
||||
<li><a href="http://portableapps.com/support/portable_app#downloading">Downloading a Portable App</a></li>
|
||||
<li><a href="http://portableapps.com/support/portable_app#installing">Installing a Portable App</a></li>
|
||||
<li><a href="http://portableapps.com/support/portable_app#using">Using a Portable App</a></li>
|
||||
<li><a href="http://portableapps.com/support/portable_app#upgrading">Upgrading a Portable App</a></li>
|
||||
</ul>
|
||||
<p>You can read about advanced configuration options for the PortableApps.com Launcher in its <a href="Other/Source/Readme.txt">readme file</a>.</p>
|
||||
</div>
|
||||
<div class="footer"></div>
|
||||
</body>
|
||||
</html>
|
@@ -1,60 +0,0 @@
|
||||
tw blue build instructions for Windows
|
||||
|
||||
Introduction.
|
||||
This document describes how to run tw blue from source, and, after that, how to build a binary version, which doesn't need Python and the other dependencies to run.
|
||||
|
||||
Required dependencies.
|
||||
The following dependencies need to be installed in your system. If you want to build tw blue for 32-bit versions of Windows, you will find the required software in the x86 folder, inside dependencies directory. If you want to build tw blue for 64-bit windows versions, use the x64 folder.
|
||||
In this document you will also find links to each dependency website.
|
||||
If you want to build manually some of the following libraries, you need Microsoft Visual studio 2008 professional.
|
||||
|
||||
Dependencies list:
|
||||
- Python, version 2.7.6: http://www.python.org
|
||||
- wxPython for Python 2.7, version 3.0.0 (2.9.5 to avoid problems in windows xp): http://www.wxpython.org
|
||||
-Python windows extensions (pywin32) for python 2.7, build 218: http://www.sourceforge.net/projects/pywin32/
|
||||
- ConfigObj, version 4.7.2: http://www.voidspace.org.uk/python/configobj.html
|
||||
- oauthlib 0.6.1: https://pypi.python.org/pypi/oauthlib/0.6.1
|
||||
- Pycurl 7.19.3.1 for Python 2.7:
|
||||
Official website: http://pycurl.sourceforge.net/
|
||||
32-bit downloads: https://pypi.python.org/pypi/pycurl/7.19.3.1
|
||||
64-bit downloads: http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
- Requests 2.2.1:
|
||||
Official website: http://www.python-requests.org/en/latest/
|
||||
Recommended download site: https://pypi.python.org/pypi/requests/2.2.1
|
||||
- Requests-oauthlib 0.4.0: https://github.com/requests/requests-oauthlib
|
||||
- Suds 0.4:
|
||||
Official website: https://fedorahosted.org/suds
|
||||
Recommended download site: https://pypi.python.org/pypi/suds/0.4
|
||||
- Twython 3.1.2: https://pypi.python.org/pypi/twython/3.1.2
|
||||
- Bootstrap 1.2.1: included in dependencies directory.
|
||||
Copy the bootstrap.exe file corresponding to the desired platform in the windows folder, inside this repository.
|
||||
This dependency has been built using pure basic 4.61. Its source can be found at http://hg.q-continuum.net/updater
|
||||
- oggenc2.exe, version 2.87: http://www.rarewares.org/ogg-oggenc.php
|
||||
Copy the oggenc2.exe file corresponding to the desired platform in the windows folder, inside this repository.
|
||||
-Visual C++ 2008 dlls, included in vcredist-x86.7z and vcredist-x64.7z:
|
||||
Extract the file corresponding to your platform to the windows folder.
|
||||
|
||||
To build the binary version:
|
||||
- Py2exe for Python 2.7, version 0.6.9: http://www.sourceforge.net/projects/py2exe/
|
||||
- Setuptools 2.1: https://pypi.python.org/pypi/setuptools
|
||||
- 7-zip: http://7-zip.org
|
||||
|
||||
To generate the documentation:
|
||||
- Pandoc, version 1.12.3:
|
||||
Official website: http://johnmacfarlane.net/pandoc/
|
||||
Downloads site: http://code.google.com/p/pandoc/downloads/list
|
||||
|
||||
How to run tw blue from source
|
||||
Run the file main.py located in windows folder. If you have a x64 system, you can install both 32-bit and 64-bit python versions, and test tw blue in these platforms.
|
||||
|
||||
How to generate the documentation
|
||||
To generate quickly the documentation, a bash console is required. You can find bash in git for windows, cygwin, or MSYS, for example.
|
||||
You must navigate to the tools directory and run the script gen_doc.sh. It will generate and place all html documentation in windows\documentation directory.
|
||||
|
||||
How to build a binary version
|
||||
You must type the following command. In the following example, we will assume that python is the path to the python executable (x86 or x64) and that you have navigated to the windows directory:
|
||||
python setup.py py2exe
|
||||
You will find the binary version in the dist directory. You can compress this folder using 7-zip and you will get the zip version.
|
||||
|
||||
How to generate a translation template
|
||||
You must run the gen_pot.bat file, located in the tools directory. Your python installation should be in your path environment variable. The pot file will appear in the tools directory too.
|
@@ -3,7 +3,7 @@
|
||||
!include "x64.nsh"
|
||||
CRCCheck on
|
||||
XPStyle on
|
||||
Name "TW Blue"
|
||||
Name "TWBlue"
|
||||
OutFile "TWBlue_setup.exe"
|
||||
InstallDir "$PROGRAMFILES\twblue"
|
||||
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
|
||||
@@ -11,17 +11,22 @@ RequestExecutionLevel admin
|
||||
SetCompress auto
|
||||
SetCompressor /solid lzma
|
||||
SetDatablockOptimize on
|
||||
VIAddVersionKey ProductName "TW Blue"
|
||||
VIAddVersionKey LegalCopyright "Copyright 2014 Manuel Cortez."
|
||||
VIAddVersionKey ProductVersion "0.48"
|
||||
VIAddVersionKey FileVersion "0.48"
|
||||
VIProductVersion "0.48.0.0"
|
||||
VIAddVersionKey ProductName "TWBlue"
|
||||
VIAddVersionKey LegalCopyright "Copyright 2015 Manuel Cortéz."
|
||||
VIAddVersionKey ProductVersion "0.80"
|
||||
VIAddVersionKey FileVersion "0.80"
|
||||
VIProductVersion "0.80.0.0"
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!define MUI_LICENSEPAGE_RADIOBUTTONS
|
||||
!insertmacro MUI_PAGE_LICENSE "license.txt"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
var StartMenuFolder
|
||||
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
|
||||
!define MUI_FINISHPAGE_LINK_LOCATION "http://twblue.es"
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
|
||||
!define MUI_FINISHPAGE_RUN_PARAMETERS "-i"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
@@ -33,6 +38,7 @@ var StartMenuFolder
|
||||
!insertmacro MUI_LANGUAGE "Russian"
|
||||
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
||||
!insertmacro MUI_LANGUAGE "Polish"
|
||||
!insertmacro MUI_LANGUAGE "German"
|
||||
!insertmacro MUI_LANGUAGE "Hungarian"
|
||||
!insertmacro MUI_LANGUAGE "Turkish"
|
||||
!insertmacro MUI_LANGUAGE "Arabic"
|
||||
@@ -48,22 +54,22 @@ File /r TWBlue64\*
|
||||
${Else}
|
||||
File /r TWBlue\*
|
||||
${EndIf}
|
||||
CreateShortCut "$DESKTOP\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
||||
CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
|
||||
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue on the web.lnk" "http://twblue.com.mx"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TW Blue"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TWBlue"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortez"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.47"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.com.mx"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.80"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.es"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 47
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 80
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
|
||||
SectionEnd
|
||||
@@ -71,7 +77,7 @@ Section "Uninstall"
|
||||
SetShellVarContext All
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
|
||||
RMDir /r /REBOOTOK $INSTDIR
|
||||
Delete "$DESKTOP\TW Blue.lnk"
|
||||
Delete "$DESKTOP\TWBlue.lnk"
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
|
||||
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
|
||||
SectionEnd
|
||||
|
@@ -5,84 +5,38 @@ user_name = string(default="")
|
||||
ignored_clients = list(default=list())
|
||||
|
||||
[general]
|
||||
language = string(default="system")
|
||||
relative_times = boolean(default=True)
|
||||
hide_gui = boolean(default=False)
|
||||
voice_enabled = boolean(default=False)
|
||||
max_api_calls = integer(default=1)
|
||||
max_tweets_per_call = integer(default=100)
|
||||
reverse_timelines = boolean(default=False)
|
||||
time_to_check_streams = integer(default=30)
|
||||
announce_stream_status = boolean(default=True)
|
||||
ask_at_exit = boolean(default=True)
|
||||
retweet_mode = string(default="ask")
|
||||
persist_size = integer(default=0)
|
||||
buffer_order = list(default=list('home','mentions','dm','sent_dm','sent_tweets','favorites','followers','friends','blocks','muted','events'))
|
||||
|
||||
[sound]
|
||||
volume = float(default=1.0)
|
||||
input_device = string(default="Default")
|
||||
output_device = string(default="Default")
|
||||
session_mute = boolean(default=False)
|
||||
current_soundpack = string(default="default")
|
||||
global_mute = boolean(default=False)
|
||||
sndup_api_key = string(default="")
|
||||
|
||||
[other_buffers]
|
||||
show_friends = boolean(default=True)
|
||||
show_followers = boolean(default=True)
|
||||
show_favourites = boolean(default=True)
|
||||
show_events = boolean(default=True)
|
||||
show_blocks = boolean(default=False)
|
||||
show_muted_users = boolean(default=False)
|
||||
timelines = list(default=list())
|
||||
tweet_searches = list(default=list())
|
||||
lists = list(default=list())
|
||||
favourites_timelines = list(default=list())
|
||||
trending_topic_buffers = list(default=list())
|
||||
muted_buffers = list(default=list())
|
||||
autoread_buffers = list(default=list())
|
||||
autoread_buffers = list(default=list(mentions, direct_messages, events))
|
||||
|
||||
[mysc]
|
||||
spelling_language = string(default="")
|
||||
save_followers_in_autocompletion_db = boolean(default=False)
|
||||
save_friends_in_autocompletion_db = boolean(default=False)
|
||||
|
||||
[services]
|
||||
dropbox_token=string(default="")
|
||||
|
||||
[keymap]
|
||||
up = string(default="control+win+up")
|
||||
down = string(default="control+win+down")
|
||||
left = string(default="control+win+left")
|
||||
right = string(default="control+win+right")
|
||||
conversation_up = string(default="control+win+shift+up")
|
||||
conversation_down = string(default="control+win+shift+down")
|
||||
show_hide = string(default="control+win+m")
|
||||
compose = string(default="control+win+n")
|
||||
reply = string(default="control+win+r")
|
||||
retweet = string(default="control+win+shift+r")
|
||||
dm = string(default="control+win+d")
|
||||
fav = string(default="alt+win+f")
|
||||
unfav = string(default="alt+shift+win+f")
|
||||
action = string(default="control+win+s")
|
||||
details = string(default="control+win+alt+n")
|
||||
view = string(default="control+win+v")
|
||||
close = string(default="control+win+f4")
|
||||
open_timeline = string(default="control+win+i")
|
||||
delete_buffer = string(default="control+win+shift+i")
|
||||
url = string(default="control+win+return")
|
||||
audio = string(default="control+win+alt+return")
|
||||
volume_up = string(default="control+win+alt+up")
|
||||
volume_down = string(default="control+win+alt+down")
|
||||
go_home = string(default="control+win+home")
|
||||
go_end = string(default="control+win+end")
|
||||
go_page_up = string(default="control+win+pageup")
|
||||
go_page_down = string(default="control+win+pagedown")
|
||||
update_profile = string(default="alt+win+p")
|
||||
delete = string(default="control+win+delete")
|
||||
clear_list = string(default="control+win+shift+delete")
|
||||
repeat_item = string(default="control+win+space")
|
||||
copy_to_clipboard = string(default="control+win+c")
|
||||
add_to_list = string(default="control+win+a")
|
||||
remove_from_list = string(default="control+win+shift+a")
|
||||
toggle_mute = string(default="control+win+shift+m")
|
||||
toggle_global_mute = string(default="alt+win+m")
|
||||
toggle_autoread = string(default="control+win+e")
|
||||
search = string(default="control+win+-")
|
||||
edit_keystrokes = string(default="control+win+k")
|
||||
view_user_lists = string(default="control+win+l")
|
||||
get_more_items = string(default="alt+win+pageup")
|
||||
|
@@ -21,7 +21,7 @@ class SAPI5(Output):
|
||||
priority = 101
|
||||
|
||||
def __init__(self):
|
||||
if config.main["general"]["voice_enabled"] == False: raise OutputError
|
||||
if config.app["app-settings"]["voice_enabled"] == False: raise OutputError
|
||||
try:
|
||||
self.object = load_com("SAPI.SPVoice")
|
||||
self._voices = self._available_voices()
|
||||
|
@@ -1,9 +1,9 @@
|
||||
from base import Output, OutputError
|
||||
import atexit
|
||||
|
||||
import application
|
||||
class SpeechDispatcher(Output):
|
||||
"""Supports speech dispatcher on Linux.
|
||||
Note that it will take the configuration from the speech dispatcher, the user will need configure voice, language, punctuation and rate before use this module.
|
||||
Note that this module will use the configuration of speech dispatcher, the user will need to configure the voice, language, punctuation and rate before using this module.
|
||||
"""
|
||||
name = 'SpeechDispatcher'
|
||||
|
||||
@@ -11,7 +11,7 @@ class SpeechDispatcher(Output):
|
||||
super(SpeechDispatcher, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
import speechd
|
||||
self.spd = speechd.SSIPClient("TWBlue")
|
||||
self.spd = speechd.SSIPClient(application.name)
|
||||
except ImportError:
|
||||
raise OutputError
|
||||
atexit.register(self.on_exit_event)
|
||||
|
21
src/app-configuration.defaults
Normal file
@@ -0,0 +1,21 @@
|
||||
[sessions]
|
||||
current_session = string(default="")
|
||||
sessions = list(default=list())
|
||||
ignored_sessions = list(default=list())
|
||||
|
||||
[app-settings]
|
||||
language = string(default="system")
|
||||
hide_gui = boolean(default=False)
|
||||
voice_enabled = boolean(default=False)
|
||||
ask_at_exit = boolean(default=True)
|
||||
use_invisible_keyboard_shorcuts = boolean(default=True)
|
||||
play_ready_sound = boolean(default=True)
|
||||
speak_ready_msg = boolean(default=True)
|
||||
log_level = string(default="error")
|
||||
load_keymap = string(default="default.keymap")
|
||||
|
||||
[proxy]
|
||||
server = string(default="")
|
||||
port = string(default="")
|
||||
user = string(default="")
|
||||
password = string(default="")
|
@@ -1,20 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
name = 'TW Blue'
|
||||
snapshot = False
|
||||
name = 'TWBlue'
|
||||
snapshot = True
|
||||
if snapshot == False:
|
||||
version = "0.48"
|
||||
update_url = 'http://twblue.com.mx/updates/tw_blue.json'
|
||||
version = "0.80"
|
||||
update_url = 'http://twblue.es/updates/twblue_ngen.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 it’s running. With this app you’ll 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'
|
||||
version = "9.4"
|
||||
update_url = 'http://twblue.es/updates/snapshots_ngen.json'
|
||||
author = u"Manuel Cortéz, Bill Dengler"
|
||||
authorEmail = "manuel@manuelcortez.net"
|
||||
copyright = u"Copyright (C) 2015, Technow S.L. \nCopyright (C) 2015, Bill Dengler\nCopyright (C) 2013-2015, Manuel cortéz."
|
||||
description = unicode(name+" is an app designed to use Twitter simply and efficiently while using minimal system resources. This app provides access to most Twitter features.")
|
||||
translators = [u"Bryner Villalobos, Bill Dengler (English)", u"Mohammed Al Shara (Arabic)", u"Joan Rabat, Juan Carlos Rivilla (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Alba Quinteiro (Galician)", u"Steffen Schultz (German)", u"Robert Osztolykan (Hungarian)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Alexander Jaszyn (Russian)", u"Burak (Turkish)"]
|
||||
url = u"http://twblue.es"
|
||||
report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl"
|
@@ -10,14 +10,6 @@ def convert_audioboom(url):
|
||||
audio_id = url.split('.com/')[-1]
|
||||
return 'https://audioboom.com/%s.mp3' % audio_id
|
||||
|
||||
@matches_url('http://q-audio.net')
|
||||
def convert_q_audio(url):
|
||||
result = re.match("^https?://q-audio.net/(i|d|download)/(?P<audio_id>[a-z0-9]+/?)$", url, re.I)
|
||||
if not result or result.group("audio_id") is None:
|
||||
raise TypeError('%r is not a valid URL' % url)
|
||||
audio_id = result.group("audio_id")
|
||||
return 'http://q-audio.net/download/%s' % audio_id
|
||||
|
||||
@matches_url ('http://soundcloud.com/')
|
||||
def convert_soundcloud (url):
|
||||
client_id = "df8113ca95c157b6c9731f54b105b473"
|
||||
|
@@ -1,13 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import argparse
|
||||
import paths
|
||||
import logging
|
||||
import application
|
||||
log = logging.getLogger("commandlineLauncher")
|
||||
|
||||
parser = argparse.ArgumentParser(description="TW Blue command line launcher")
|
||||
parser = argparse.ArgumentParser(description=application.name+" command line launcher")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("-p", "--portable", help="Use TW Blue as a portable aplication", action="store_true", default=True)
|
||||
group.add_argument("-i", "--installed", help="Use TW Blue as an installed application. Config files will be saved on the user data directory", action="store_true")
|
||||
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where TW Blue saves the data files")
|
||||
group.add_argument("-p", "--portable", help="Use " + application.name + " as a portable application.", action="store_true", default=True)
|
||||
group.add_argument("-i", "--installed", help="Use " + application.name + " as an installed application. Config files will be saved in the user data directory", action="store_true")
|
||||
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where " + application.name + " saves userdata.")
|
||||
args = parser.parse_args()
|
||||
log.debug("Starting " + application.name + " with the following arguments: installed = %s, portable = %s and directory = %s" % (args.installed, args.portable, args.directory))
|
||||
if args.installed == True: paths.mode = "installed"
|
||||
elif args.portable == True:
|
||||
paths.mode = "portable"
|
||||
|
@@ -1,16 +1,19 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
from config_utils import Configuration, ConfigurationResetException
|
||||
import config_utils
|
||||
import paths
|
||||
import logging
|
||||
|
||||
MAINFILE = "session.conf"
|
||||
MAINSPEC = "Conf.defaults"
|
||||
log = logging.getLogger("config")
|
||||
|
||||
main = None
|
||||
MAINFILE = "twblue.conf"
|
||||
MAINSPEC = "app-configuration.defaults"
|
||||
|
||||
app = None
|
||||
keymap=None
|
||||
def setup ():
|
||||
global main
|
||||
try:
|
||||
main = Configuration(paths.config_path(MAINFILE), paths.app_path(MAINSPEC))
|
||||
except ConfigurationResetException:
|
||||
pass
|
||||
# return main
|
||||
global app
|
||||
log.debug("Loading global app settings...")
|
||||
app = config_utils.load_config(paths.config_path(MAINFILE), paths.app_path(MAINSPEC))
|
||||
log.debug("Loading keymap...")
|
||||
global keymap
|
||||
keymap = config_utils.load_config(paths.app_path("keymaps/"+app['app-settings']['load_keymap']), paths.app_path('keymaps/base.template'))
|
||||
|
@@ -1,50 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from UserDict import UserDict
|
||||
from configobj import ConfigObj, ParseError
|
||||
from validate import Validator, VdtValueError
|
||||
from validate import Validator, ValidateError
|
||||
import os
|
||||
import string
|
||||
class ConfigLoadError(Exception): pass
|
||||
|
||||
"""We're using the configobj python package
|
||||
from http://www.voidspace.org.uk/python/configobj.html """
|
||||
def load_config(config_path, configspec_path=None, *args, **kwargs):
|
||||
if os.path.exists(config_path):
|
||||
clean_config(config_path)
|
||||
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
|
||||
try:
|
||||
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
|
||||
except ParseError:
|
||||
raise ConfigLoadError("Unable to load %r" % config_path)
|
||||
validator = Validator()
|
||||
validated = config.validate(validator, copy=True)
|
||||
if validated == True:
|
||||
config.write()
|
||||
return config
|
||||
|
||||
class ConfigurationResetException(Exception):
|
||||
pass
|
||||
def is_blank(arg):
|
||||
"Check if a line is blank."
|
||||
for c in arg:
|
||||
if c not in string.whitespace:
|
||||
return False
|
||||
return True
|
||||
def get_keys(path):
|
||||
"Gets the keys of a configobj config file."
|
||||
res=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
if not is_blank(line):
|
||||
res.append(line[0:line.find('=')].strip())
|
||||
fin.close()
|
||||
return res
|
||||
|
||||
def hist(keys):
|
||||
"Generates a histogram of an iterable."
|
||||
res={}
|
||||
for k in keys:
|
||||
res[k]=res.setdefault(k,0)+1
|
||||
return res
|
||||
|
||||
class Configuration (UserDict):
|
||||
def find_problems(hist):
|
||||
"Takes a histogram and returns a list of items occurring more than once."
|
||||
res=[]
|
||||
for k,v in hist.items():
|
||||
if v>1:
|
||||
res.append(k)
|
||||
return res
|
||||
|
||||
def __init__ (self, file=None, spec=None, *args, **kwargs):
|
||||
self.file = file
|
||||
self.spec = spec
|
||||
self.validator = Validator()
|
||||
self.setup_config(file=file, spec=spec)
|
||||
self.validated = self.config.validate(self.validator, copy=True)
|
||||
if self.validated:
|
||||
self.write()
|
||||
UserDict.__init__(self, self.config)
|
||||
|
||||
def setup_config (self, file, spec):
|
||||
#The default way -- load from a file
|
||||
spec = ConfigObj(spec, list_values=False, encoding="utf-8")
|
||||
try:
|
||||
self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True, encoding="utf-8")
|
||||
except ParseError:
|
||||
os.remove(file)
|
||||
self.config = ConfigObj(infile=file, configspec=spec, create_empty=True, stringify=True)
|
||||
raise ConfigurationResetException
|
||||
def __getitem__ (self, *args, **kwargs):
|
||||
return dict(self.config).__getitem__(*args, **kwargs)
|
||||
|
||||
def __setitem__ (self, *args, **kwargs):
|
||||
self.config.__setitem__(*args, **kwargs)
|
||||
UserDict.__setitem__(self, *args, **kwargs)
|
||||
|
||||
def write (self):
|
||||
if hasattr(self.config, 'write'):
|
||||
self.config.write()
|
||||
|
||||
class SessionConfiguration (Configuration):
|
||||
def setup_config (self, file, spec):
|
||||
#No infile required.
|
||||
spec = ConfigObj(spec, list_values=False)
|
||||
self.config = ConfigObj(configspec=spec, stringify=True)
|
||||
def clean_config(path):
|
||||
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
|
||||
orig=[]
|
||||
cleaned=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
orig.append(line)
|
||||
fin.close()
|
||||
for p in find_problems(hist(get_keys(path))):
|
||||
for o in orig:
|
||||
o.strip()
|
||||
if p not in o:
|
||||
cleaned.append(o)
|
||||
if len(cleaned) != 0:
|
||||
cam=open(path,'w')
|
||||
for c in cleaned:
|
||||
cam.write(c)
|
||||
cam.close()
|
||||
return True
|
||||
else:
|
||||
return False
|
@@ -1,3 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import main, dialogs
|
||||
|
863
src/controller/buffersController.py
Normal file
@@ -0,0 +1,863 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
import wx
|
||||
from wxUI import buffers, dialogs, commonMessageDialogs
|
||||
import user
|
||||
elif platform.system() == "Linux":
|
||||
from gi.repository import Gtk
|
||||
from gtkUI import buffers, dialogs, commonMessageDialogs
|
||||
import messages
|
||||
import widgetUtils
|
||||
import arrow
|
||||
import webbrowser
|
||||
import output
|
||||
import config
|
||||
import sound
|
||||
import languageHandler
|
||||
import logging
|
||||
from twitter import compose, utils
|
||||
from mysc.thread_utils import call_threaded
|
||||
from twython import TwythonError
|
||||
from pubsub import pub
|
||||
from long_tweets import twishort, tweets
|
||||
|
||||
log = logging.getLogger("controller.buffers")
|
||||
|
||||
def _tweets_exist(function):
|
||||
""" A decorator to execute a function only if the selected buffer contains at least one item."""
|
||||
def function_(self, *args, **kwargs):
|
||||
if self.buffer.list.get_count() > 0:
|
||||
function(self, *args, **kwargs)
|
||||
return function_
|
||||
|
||||
class bufferController(object):
|
||||
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
|
||||
super(bufferController, self).__init__()
|
||||
self.function = function
|
||||
self.compose_function = None
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.buffer = None
|
||||
self.account = ""
|
||||
self.needs_init = True
|
||||
self.invisible = False # False if the buffer will be ignored on the invisible interface.
|
||||
|
||||
def clear_list(self): pass
|
||||
|
||||
|
||||
def get_event(self, ev):
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "url"
|
||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "interact"
|
||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
||||
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
||||
else:
|
||||
event = None
|
||||
ev.Skip()
|
||||
if event != None:
|
||||
try:
|
||||
getattr(self, event)()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def volume_down(self):
|
||||
if self.session.settings["sound"]["volume"] > 0.0:
|
||||
if self.session.settings["sound"]["volume"] <= 0.05:
|
||||
self.session.settings["sound"]["volume"] = 0.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] -=0.05
|
||||
if hasattr(sound.URLPlayer, "stream"):
|
||||
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
|
||||
def volume_up(self):
|
||||
if self.session.settings["sound"]["volume"] < 1.0:
|
||||
if self.session.settings["sound"]["volume"] >= 0.95:
|
||||
self.session.settings["sound"]["volume"] = 1.0
|
||||
else:
|
||||
self.session.settings["sound"]["volume"] +=0.05
|
||||
if hasattr(sound.URLPlayer, "stream"):
|
||||
sound.URLPlayer.stream.volume = self.session.settings["sound"]["volume"]
|
||||
self.session.sound.play("volume_changed.ogg")
|
||||
|
||||
def interact(self):
|
||||
if hasattr(sound.URLPlayer,'stream'):
|
||||
return sound.URLPlayer.stop_audio(delete=True)
|
||||
tweet = self.get_tweet()
|
||||
url=None
|
||||
urls = utils.find_urls(tweet)
|
||||
if len(urls) == 1:
|
||||
url=urls[0]
|
||||
elif len(urls) > 1:
|
||||
urls_list = dialogs.urlList.urlList()
|
||||
urls_list.populate_list(urls)
|
||||
if urls_list.get_response() == widgetUtils.OK:
|
||||
url=urls_list.get_string()
|
||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
||||
if url != None:
|
||||
output.speak(_(u"Opening media..."), True)
|
||||
if sound.URLPlayer.is_playable(url=url, play=True, volume=self.session.settings["sound"]["volume"]) == False:
|
||||
return webbrowser.open_new_tab(url)
|
||||
# else:
|
||||
# output.speak(_(u"Not actionable."), True)
|
||||
# self.session.sound.play("error.ogg")
|
||||
|
||||
def start_stream(self):
|
||||
pass
|
||||
|
||||
def get_more_items(self):
|
||||
output.speak(_(u"This action is not supported for this buffer"), True)
|
||||
|
||||
def put_items_on_list(self, items):
|
||||
pass
|
||||
|
||||
def remove_buffer(self):
|
||||
return False
|
||||
|
||||
def remove_item(self, item):
|
||||
self.buffer.list.remove_item(item)
|
||||
|
||||
def bind_events(self):
|
||||
pass
|
||||
|
||||
def get_object(self):
|
||||
return self.buffer
|
||||
|
||||
def get_message(self):
|
||||
pass
|
||||
|
||||
def set_list_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.buffer.list.select_item(-1)
|
||||
else:
|
||||
self.buffer.list.select_item(0)
|
||||
|
||||
def reply(self):
|
||||
pass
|
||||
|
||||
def direct_message(self):
|
||||
pass
|
||||
|
||||
def retweet(self):
|
||||
pass
|
||||
|
||||
def destroy_status(self):
|
||||
pass
|
||||
|
||||
def post_tweet(self, *args, **kwargs):
|
||||
title = _(u"Tweet")
|
||||
caption = _(u"Write the tweet here")
|
||||
tweet = messages.tweet(self.session, title, caption, "")
|
||||
if tweet.message.get_response() == widgetUtils.OK:
|
||||
text = tweet.message.get_text()
|
||||
if tweet.image == None:
|
||||
call_threaded(self.session.api_call, call_name="update_status", status=text)
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image)
|
||||
if hasattr(tweet.message, "destroy"): tweet.message.destroy()
|
||||
|
||||
class accountPanel(bufferController):
|
||||
def __init__(self, parent, name, account, account_id):
|
||||
super(accountPanel, self).__init__(parent, None, name)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.buffer = buffers.accountPanel(parent, name)
|
||||
self.type = self.buffer.type
|
||||
self.compose_function = None
|
||||
self.session = None
|
||||
self.needs_init = False
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.name = name
|
||||
self.account_id = account_id
|
||||
|
||||
def setup_account(self):
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.CHECKBOX, self.autostart, menuitem=self.buffer.autostart_account)
|
||||
if self.account_id in config.app["sessions"]["ignored_sessions"]:
|
||||
self.buffer.change_autostart(False)
|
||||
else:
|
||||
self.buffer.change_autostart(True)
|
||||
if not hasattr(self, "logged"):
|
||||
self.buffer.change_login(login=False)
|
||||
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.logout)
|
||||
else:
|
||||
self.buffer.change_login(login=True)
|
||||
widgetUtils.connect_event(self.buffer.login, widgetUtils.BUTTON_PRESSED, self.login)
|
||||
|
||||
def login(self, *args, **kwargs):
|
||||
del self.logged
|
||||
self.setup_account()
|
||||
pub.sendMessage("login", session_id=self.account_id)
|
||||
|
||||
def logout(self, *args, **kwargs):
|
||||
self.logged = False
|
||||
self.setup_account()
|
||||
pub.sendMessage("logout", session_id=self.account_id)
|
||||
|
||||
def autostart(self, *args, **kwargs):
|
||||
if self.account_id in config.app["sessions"]["ignored_sessions"]:
|
||||
self.buffer.change_autostart(True)
|
||||
config.app["sessions"]["ignored_sessions"].remove(self.account_id)
|
||||
else:
|
||||
self.buffer.change_autostart(False)
|
||||
config.app["sessions"]["ignored_sessions"].append(self.account_id)
|
||||
|
||||
class emptyPanel(bufferController):
|
||||
def __init__(self, parent, name, account):
|
||||
super(emptyPanel, self).__init__(parent, None, name)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.buffer = buffers.emptyPanel(parent, name)
|
||||
self.type = self.buffer.type
|
||||
self.compose_function = None
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.name = name
|
||||
self.session = None
|
||||
self.needs_init = True
|
||||
|
||||
class baseBufferController(bufferController):
|
||||
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs):
|
||||
super(baseBufferController, self).__init__(parent, function, *args, **kwargs)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
if bufferType != None:
|
||||
self.buffer = getattr(buffers, bufferType)(parent, name)
|
||||
else:
|
||||
self.buffer = buffers.basePanel(parent, name)
|
||||
self.invisible = True
|
||||
self.name = name
|
||||
self.type = self.buffer.type
|
||||
self.session = sessionObject
|
||||
self.compose_function = compose.compose_tweet
|
||||
log.debug("Compose_function: %s" % (self.compose_function,))
|
||||
self.account = account
|
||||
self.buffer.account = account
|
||||
self.bind_events()
|
||||
self.sound = sound
|
||||
|
||||
def get_formatted_message(self):
|
||||
if self.type == "dm" or self.name == "sent_tweets" or self.name == "sent_direct_messages": return self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"])[1]
|
||||
return self.get_message()
|
||||
|
||||
def get_message(self):
|
||||
return " ".join(self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"]))
|
||||
|
||||
def get_full_tweet(self):
|
||||
tweet = self.get_right_tweet()
|
||||
tweetsList = []
|
||||
tweet_id = tweet["id"]
|
||||
uri = None
|
||||
if tweet.has_key("long_uri"):
|
||||
uri = tweet["long_uri"]
|
||||
tweet = self.session.twitter.twitter.show_status(id=tweet_id)
|
||||
if uri != None:
|
||||
tweet["text"] = twishort.get_full_text(uri)
|
||||
l = tweets.is_long(tweet)
|
||||
while l != False:
|
||||
tweetsList.append(tweet)
|
||||
id = tweets.get_id(l)
|
||||
tweet = self.session.twitter.twitter.show_status(id=id)
|
||||
l = tweets.is_long(tweet)
|
||||
if l == False:
|
||||
tweetsList.append(tweet)
|
||||
return (tweet, tweetsList)
|
||||
|
||||
def start_stream(self):
|
||||
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
|
||||
number_of_items = self.session.order_buffer(self.name, val)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if self.sound == None: return
|
||||
if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages":
|
||||
self.session.sound.play(self.sound)
|
||||
|
||||
def get_more_items(self):
|
||||
elements = []
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
last_id = self.session.db[self.name][0]["id"]
|
||||
else:
|
||||
last_id = self.session.db[self.name][-1]["id"]
|
||||
try:
|
||||
items = self.session.get_more_items(self.function, count=self.session.settings["general"]["max_tweets_per_call"], max_id=last_id, *self.args, **self.kwargs)
|
||||
except TwythonError as e:
|
||||
output.speak(e.message, True)
|
||||
for i in items:
|
||||
if utils.is_allowed(i, self.session.settings["twitter"]["ignored_clients"]) == True:
|
||||
elements.append(i)
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.session.db[self.name].insert(0, i)
|
||||
else:
|
||||
self.session.db[self.name].append(i)
|
||||
selection = self.buffer.list.get_selected()
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
for i in elements:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
else:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
# self.buffer.list.select_item(selection+elements)
|
||||
# else:
|
||||
self.buffer.list.select_item(selection)
|
||||
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
||||
|
||||
def remove_buffer(self):
|
||||
if "-timeline" in self.name:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-9] in self.session.settings["other_buffers"]["timelines"]:
|
||||
self.session.settings["other_buffers"]["timelines"].remove(self.name[:-9])
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
elif "favorite" in self.name:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-9] in self.session.settings["other_buffers"]["favourites_timelines"]:
|
||||
self.session.settings["other_buffers"]["favourites_timelines"].remove(self.name[:-9])
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
else:
|
||||
output.speak(_(u"This buffer is not a timeline; it can't be deleted."), True)
|
||||
return False
|
||||
|
||||
def put_items_on_list(self, number_of_items):
|
||||
log.debug("The list contains %d items " % (self.buffer.list.get_count(),))
|
||||
log.debug("Putting %d items on the list" % (number_of_items,))
|
||||
if self.buffer.list.get_count() == 0:
|
||||
for i in self.session.db[self.name]:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
||||
elif self.buffer.list.get_count() > 0:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
for i in self.session.db[self.name][:number_of_items]:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
for i in self.session.db[self.name][0:number_of_items]:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
log.debug("Now the list contains %d items " % (self.buffer.list.get_count(),))
|
||||
|
||||
def add_new_item(self, item):
|
||||
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(" ".join(tweet[:2]))
|
||||
#Improve performance on Windows
|
||||
if platform.system() == "Windows":
|
||||
call_threaded(utils.is_audio,item)
|
||||
|
||||
def bind_events(self):
|
||||
log.debug("Binding events...")
|
||||
self.buffer.set_focus_function(self.onFocus)
|
||||
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_tweet, self.buffer.tweet)
|
||||
# if self.type == "baseBuffer":
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.retweet, self.buffer.retweet)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.direct_message, self.buffer.dm)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
|
||||
|
||||
def get_tweet(self):
|
||||
if self.session.db[self.name][self.buffer.list.get_selected()].has_key("retweeted_status"):
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]["retweeted_status"]
|
||||
else:
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
||||
return tweet
|
||||
|
||||
def get_right_tweet(self):
|
||||
tweet = self.session.db[self.name][self.buffer.list.get_selected()]
|
||||
return tweet
|
||||
|
||||
@_tweets_exist
|
||||
def reply(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
screen_name = tweet["user"]["screen_name"]
|
||||
id = tweet["id"]
|
||||
users = utils.get_all_mentioned(tweet, self.session.db)
|
||||
message = messages.reply(self.session, _(u"Reply"), _(u"Reply to %s") % (screen_name,), "@%s " % (screen_name,), users)
|
||||
if message.message.get_response() == widgetUtils.OK:
|
||||
if message.image == None:
|
||||
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", in_reply_to_status_id=id, status=message.message.get_text())
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", in_reply_to_status_id=id, status=message.message.get_text(), media=message.file)
|
||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
||||
|
||||
@_tweets_exist
|
||||
def direct_message(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
if self.type == "dm":
|
||||
screen_name = tweet["sender"]["screen_name"]
|
||||
users = utils.get_all_users(tweet, self.session.db)
|
||||
elif self.type == "people":
|
||||
screen_name = tweet["screen_name"]
|
||||
users = [screen_name]
|
||||
else:
|
||||
screen_name = tweet["user"]["screen_name"]
|
||||
users = utils.get_all_users(tweet, self.session.db)
|
||||
dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users)
|
||||
if dm.message.get_response() == widgetUtils.OK:
|
||||
call_threaded(self.session.api_call, call_name="send_direct_message", text=dm.message.get_text(), screen_name=dm.message.get("cb"))
|
||||
if hasattr(dm.message, "destroy"): dm.message.destroy()
|
||||
|
||||
@_tweets_exist
|
||||
def retweet(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
id = tweet["id"]
|
||||
if self.session.settings["general"]["retweet_mode"] == "ask":
|
||||
answer = commonMessageDialogs.retweet_question(self.buffer)
|
||||
if answer == widgetUtils.YES:
|
||||
self._retweet_with_comment(tweet, id)
|
||||
elif answer == widgetUtils.NO:
|
||||
self._direct_retweet(id)
|
||||
elif self.session.settings["general"]["retweet_mode"] == "direct":
|
||||
self._direct_retweet(id)
|
||||
else:
|
||||
self._retweet_with_comment(tweet, id)
|
||||
|
||||
def _retweet_with_comment(self, tweet, id, comment=''):
|
||||
retweet = messages.tweet(self.session, _(u"Retweet"), _(u"Add your comment to the tweet"), u"“@%s: %s ”" % (tweet["user"]["screen_name"], tweet["text"]), max=116-len("@%s " % (tweet["user"]["screen_name"],)), messageType="retweet")
|
||||
if comment != '':
|
||||
retweet.message.set_text(comment)
|
||||
if retweet.message.get_response() == widgetUtils.OK:
|
||||
text = retweet.message.get_text()
|
||||
comments=text
|
||||
if len(text+ u"“@%s: %s ”" % (tweet["user"]["screen_name"], tweet["text"])) < 140:
|
||||
text = text+u"“@%s: %s ”" % (tweet["user"]["screen_name"], tweet["text"])
|
||||
else:
|
||||
answer = commonMessageDialogs.retweet_as_link(self.buffer)
|
||||
if answer == widgetUtils.YES:
|
||||
text = text+" @{2} https://twitter.com/{0}/status/{1}".format(tweet["user"]["screen_name"], id, tweet["user"]["screen_name"])
|
||||
else:
|
||||
return self._retweet_with_comment(tweet, id, comment=comments)
|
||||
if retweet.image == None:
|
||||
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id)
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, media=retweet.image)
|
||||
if hasattr(retweet.message, "destroy"): retweet.message.destroy()
|
||||
|
||||
def _direct_retweet(self, id):
|
||||
call_threaded(self.session.api_call, call_name="retweet", _sound="retweet_send.ogg", id=id)
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
tweet = self.get_tweet()
|
||||
if platform.system() == "Windows" and self.session.settings["general"]["relative_times"] == True:
|
||||
# fix this:
|
||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en")
|
||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||
self.buffer.list.list.SetStringItem(self.buffer.list.get_selected(), 2, ts)
|
||||
if utils.is_audio(tweet):
|
||||
self.session.sound.play("audio.ogg")
|
||||
if utils.is_geocoded(tweet):
|
||||
self.session.sound.play("geo.ogg")
|
||||
self.session.db[str(self.name+"_pos")]=self.buffer.list.get_selected()
|
||||
|
||||
@_tweets_exist
|
||||
def audio(self,url=''):
|
||||
if hasattr(sound.URLPlayer,'stream'):
|
||||
return sound.URLPlayer.stop_audio(delete=True)
|
||||
tweet = self.get_tweet()
|
||||
if tweet == None: return
|
||||
urls = utils.find_urls(tweet)
|
||||
if len(urls) == 1:
|
||||
url=urls[0]
|
||||
elif len(urls) > 1:
|
||||
urls_list = dialogs.urlList.urlList()
|
||||
urls_list.populate_list(urls)
|
||||
if urls_list.get_response() == widgetUtils.OK:
|
||||
url=urls_list.get_string()
|
||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
||||
if url != '':
|
||||
try:
|
||||
sound.URLPlayer.play(url, self.session.settings["sound"]["volume"])
|
||||
except:
|
||||
log.error("Exception while executing audio method.")
|
||||
|
||||
@_tweets_exist
|
||||
def url(self,url='',announce=True):
|
||||
if url == '':
|
||||
tweet = self.get_tweet()
|
||||
urls = utils.find_urls(tweet)
|
||||
if len(urls) == 1:
|
||||
url=urls[0]
|
||||
elif len(urls) > 1:
|
||||
urls_list = dialogs.urlList.urlList()
|
||||
urls_list.populate_list(urls)
|
||||
if urls_list.get_response() == widgetUtils.OK:
|
||||
url=urls_list.get_string()
|
||||
if hasattr(urls_list, "destroy"): urls_list.destroy()
|
||||
if url != '':
|
||||
if announce:
|
||||
output.speak(_(u"Opening URL..."), True)
|
||||
webbrowser.open_new_tab(url)
|
||||
|
||||
def clear_list(self):
|
||||
dlg = commonMessageDialogs.clear_list()
|
||||
if dlg == widgetUtils.YES:
|
||||
self.session.db[self.name] = []
|
||||
self.buffer.list.clear()
|
||||
|
||||
@_tweets_exist
|
||||
def destroy_status(self, *args, **kwargs):
|
||||
index = self.buffer.list.get_selected()
|
||||
if self.type == "events" or self.type == "people" or self.type == "empty" or self.type == "account": return
|
||||
answer = commonMessageDialogs.delete_tweet_dialog(None)
|
||||
if answer == widgetUtils.YES:
|
||||
try:
|
||||
if self.name == "direct_messages" or self.name == "sent_direct_messages":
|
||||
self.session.twitter.twitter.destroy_direct_message(id=self.get_right_tweet()["id"])
|
||||
else:
|
||||
self.session.twitter.twitter.destroy_status(id=self.get_right_tweet()["id"])
|
||||
self.session.db[self.name].pop(index)
|
||||
self.buffer.list.remove_item(index)
|
||||
# if index > 0:
|
||||
except TwythonError:
|
||||
self.session.sound.play("error")
|
||||
|
||||
@_tweets_exist
|
||||
def user_details(self):
|
||||
tweet = self.get_right_tweet()
|
||||
if self.type == "dm":
|
||||
users = utils.get_all_users(tweet, self.session.db)
|
||||
elif self.type == "people":
|
||||
users = [tweet["screen_name"]]
|
||||
else:
|
||||
users = utils.get_all_users(tweet, self.session.db)
|
||||
dlg = dialogs.utils.selectUserDialog(title=_(u"User details"), users=users)
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
user.profileController(session=self.session, user=dlg.get_user())
|
||||
if hasattr(dlg, "destroy"): dlg.destroy()
|
||||
|
||||
class listBufferController(baseBufferController):
|
||||
def __init__(self, parent, function, name, sessionObject, account, sound=None, bufferType=None, list_id=None, *args, **kwargs):
|
||||
super(listBufferController, self).__init__(parent, function, name, sessionObject, account, sound=None, bufferType=None, *args, **kwargs)
|
||||
self.users = []
|
||||
self.list_id = list_id
|
||||
self.kwargs["list_id"] = list_id
|
||||
|
||||
def start_stream(self):
|
||||
self.get_user_ids()
|
||||
super(listBufferController, self).start_stream()
|
||||
|
||||
def get_user_ids(self):
|
||||
self.users = []
|
||||
next_cursor = -1
|
||||
while(next_cursor):
|
||||
users = self.session.twitter.twitter.get_list_members(list_id=self.list_id, cursor=next_cursor, include_entities=False, skip_status=True)
|
||||
for i in users['users']:
|
||||
self.users.append(i["id"])
|
||||
next_cursor = users["next_cursor"]
|
||||
|
||||
def remove_buffer(self):
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-5] in self.session.settings["other_buffers"]["lists"]:
|
||||
self.session.settings["other_buffers"]["lists"].remove(self.name[:-5])
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
class eventsBufferController(bufferController):
|
||||
def __init__(self, parent, name, session, account, *args, **kwargs):
|
||||
super(eventsBufferController, self).__init__(parent, *args, **kwargs)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.invisible = True
|
||||
self.buffer = buffers.eventsPanel(parent, name)
|
||||
self.name = name
|
||||
self.account = account
|
||||
self.buffer.account = self.account
|
||||
self.compose_function = compose.compose_event
|
||||
self.session = session
|
||||
self.type = self.buffer.type
|
||||
self.get_formatted_message = self.get_message
|
||||
|
||||
def get_message(self):
|
||||
if self.buffer.list.get_count() == 0: return _(u"Empty")
|
||||
# fix this:
|
||||
return "%s. %s" % (self.buffer.list.list.GetItemText(self.buffer.list.get_selected()), self.buffer.list.list.GetItemText(self.buffer.list.get_selected(), 1))
|
||||
|
||||
def add_new_item(self, item):
|
||||
tweet = self.compose_function(item, self.session.db["user_name"])
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(" ".join(tweet))
|
||||
if self.buffer.list.get_count() == 1:
|
||||
self.buffer.list.select_item(0)
|
||||
|
||||
def clear_list(self):
|
||||
dlg = commonMessageDialogs.clear_list()
|
||||
if dlg == widgetUtils.YES:
|
||||
self.buffer.list.clear()
|
||||
|
||||
class peopleBufferController(baseBufferController):
|
||||
def __init__(self, parent, function, name, sessionObject, account, bufferType=None, *args, **kwargs):
|
||||
super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel")
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
self.compose_function = compose.compose_followers_list
|
||||
log.debug("Compose_function: %s" % (self.compose_function,))
|
||||
self.get_tweet = self.get_right_tweet
|
||||
self.url = self.interact
|
||||
|
||||
def remove_buffer(self):
|
||||
return False
|
||||
|
||||
def onFocus(self, ev):
|
||||
pass
|
||||
|
||||
def get_message(self):
|
||||
return " ".join(self.compose_function(self.get_tweet(), self.session.db, self.session.settings["general"]["relative_times"]))
|
||||
|
||||
def delete_item(self): pass
|
||||
|
||||
@_tweets_exist
|
||||
def reply(self, *args, **kwargs):
|
||||
tweet = self.get_right_tweet()
|
||||
screen_name = tweet["screen_name"]
|
||||
message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,])
|
||||
if message.message.get_response() == widgetUtils.OK:
|
||||
if message.image == None:
|
||||
call_threaded(self.session.api_call, call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text())
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file)
|
||||
if hasattr(message.message, "destroy"): message.message.destroy()
|
||||
|
||||
def start_stream(self):
|
||||
log.debug("Starting stream for %s buffer, %s account" % (self.name, self.account,))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
val = self.session.get_cursored_stream(self.name, self.function, *self.args, **self.kwargs)
|
||||
# self.session.order_cursored_buffer(self.name, self.session.db[self.name])
|
||||
# log.debug("Number of items retrieved: %d" % (val,))
|
||||
self.put_items_on_list(val)
|
||||
|
||||
def get_more_items(self):
|
||||
try:
|
||||
items = self.session.get_more_items(self.function, users=True, name=self.name, count=self.session.settings["general"]["max_tweets_per_call"], cursor=self.session.db[self.name]["cursor"])
|
||||
except TwythonError as e:
|
||||
output.speak(e.message, True)
|
||||
return
|
||||
for i in items:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.session.db[self.name]["items"].insert(0, i)
|
||||
else:
|
||||
self.session.db[self.name]["items"].append(i)
|
||||
selected = self.buffer.list.get_selected()
|
||||
# self.put_items_on_list(len(items))
|
||||
if self.session.settings["general"]["reverse_timelines"] == True:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
self.buffer.list.select_item(selected)
|
||||
else:
|
||||
for i in items:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
# self.buffer.list.select_item(selection)
|
||||
# else:
|
||||
# self.buffer.list.select_item(selection-elements)
|
||||
output.speak(_(u"%s items retrieved") % (len(items)), True)
|
||||
|
||||
def put_items_on_list(self, number_of_items):
|
||||
log.debug("The list contains %d items" % (self.buffer.list.get_count(),))
|
||||
# log.debug("Putting %d items on the list..." % (number_of_items,))
|
||||
if self.buffer.list.get_count() == 0:
|
||||
for i in self.session.db[self.name]["items"]:
|
||||
tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
||||
# self.buffer.set_list_position()
|
||||
elif self.buffer.list.get_count() > 0:
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
for i in self.session.db[self.name]["items"][:number_of_items]:
|
||||
tweet = self.compose_function(i, self.session.db)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
for i in self.session.db[self.name]["items"][0:number_of_items]:
|
||||
tweet = self.compose_function(i, self.session.db)
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
log.debug("now the list contains %d items" % (self.buffer.list.get_count(),))
|
||||
|
||||
def get_right_tweet(self):
|
||||
tweet = self.session.db[self.name]["items"][self.buffer.list.get_selected()]
|
||||
return tweet
|
||||
|
||||
def add_new_item(self, item):
|
||||
tweet = self.compose_function(item, self.session.db, self.session.settings["general"]["relative_times"])
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
else:
|
||||
self.buffer.list.insert_item(True, *tweet)
|
||||
if self.name in self.session.settings["other_buffers"]["autoread_buffers"] and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and self.session.settings["sound"]["session_mute"] == False:
|
||||
output.speak(" ".join(tweet))
|
||||
|
||||
def clear_list(self):
|
||||
dlg = commonMessageDialogs.clear_list()
|
||||
if dlg == widgetUtils.YES:
|
||||
self.session.db[self.name]["items"] = []
|
||||
self.session.db[self.name]["cursor"] = -1
|
||||
self.buffer.list.clear()
|
||||
|
||||
def interact(self):
|
||||
user.profileController(self.session, user=self.get_right_tweet()["screen_name"])
|
||||
|
||||
class searchBufferController(baseBufferController):
|
||||
def start_stream(self):
|
||||
log.debug("Starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
log.debug("Function: %s" % (self.function,))
|
||||
# try:
|
||||
val = self.session.search(self.name, *self.args, **self.kwargs)
|
||||
# except:
|
||||
# return None
|
||||
num = self.session.order_buffer(self.name, val)
|
||||
self.put_items_on_list(num)
|
||||
if num > 0:
|
||||
self.session.sound.play("search_updated.ogg")
|
||||
|
||||
def remove_buffer(self):
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
|
||||
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
|
||||
self.timer.cancel()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
class searchPeopleBufferController(peopleBufferController):
|
||||
|
||||
def __init__(self, parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs):
|
||||
super(searchPeopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel", *args, **kwargs)
|
||||
log.debug("Initializing buffer %s, account %s" % (name, account,))
|
||||
# self.compose_function = compose.compose_followers_list
|
||||
log.debug("Compose_function: %s" % (self.compose_function,))
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.function = function
|
||||
|
||||
def start_stream(self):
|
||||
log.debug("starting stream for %s buffer, %s account and %s type" % (self.name, self.account, self.type))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
log.debug("Function: %s" % (self.function,))
|
||||
# try:
|
||||
val = self.session.call_paged(self.function, *self.args, **self.kwargs)
|
||||
# except:
|
||||
# return
|
||||
number_of_items = self.session.order_cursored_buffer(self.name, val)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if number_of_items > 0:
|
||||
self.session.sound.play("search_updated.ogg")
|
||||
|
||||
def remove_buffer(self):
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-11] in self.session.settings["other_buffers"]["tweet_searches"]:
|
||||
self.session.settings["other_buffers"]["tweet_searches"].remove(self.name[:-11])
|
||||
self.timer.cancel()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
class trendsBufferController(bufferController):
|
||||
def __init__(self, parent, name, session, account, trendsFor, *args, **kwargs):
|
||||
super(trendsBufferController, self).__init__(parent=parent, session=session)
|
||||
self.trendsFor = trendsFor
|
||||
self.session = session
|
||||
self.account = account
|
||||
self.invisible = True
|
||||
self.buffer = buffers.trendsPanel(parent, name)
|
||||
self.buffer.account = account
|
||||
self.type = self.buffer.type
|
||||
self.bind_events()
|
||||
self.sound = "trends_updated.ogg"
|
||||
self.trends = []
|
||||
self.name = name
|
||||
self.buffer.name = name
|
||||
self.compose_function = self.compose_function_
|
||||
self.get_formatted_message = self.get_message
|
||||
|
||||
def start_stream(self):
|
||||
try:
|
||||
data = self.session.call_paged("get_place_trends", id=self.trendsFor)
|
||||
except:
|
||||
return
|
||||
if not hasattr(self, "name_"):
|
||||
self.name_ = data[0]["locations"][0]["name"]
|
||||
self.trends = data[0]["trends"]
|
||||
self.put_items_on_the_list()
|
||||
self.session.sound.play(self.sound)
|
||||
|
||||
def put_items_on_the_list(self):
|
||||
selected_item = self.buffer.list.get_selected()
|
||||
self.buffer.list.clear()
|
||||
for i in self.trends:
|
||||
tweet = self.compose_function(i)
|
||||
self.buffer.list.insert_item(False, *tweet)
|
||||
self.buffer.set_position(self.session.settings["general"]["reverse_timelines"])
|
||||
|
||||
def compose_function_(self, trend):
|
||||
return [trend["name"]]
|
||||
|
||||
def bind_events(self):
|
||||
log.debug("Binding events...")
|
||||
self.buffer.list.list.Bind(wx.EVT_CHAR_HOOK, self.get_event)
|
||||
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_tweet, self.buffer.tweet)
|
||||
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.retweet, self.buffer.retweet)
|
||||
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.direct_message, self.buffer.dm)
|
||||
# widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.reply, self.buffer.reply)
|
||||
|
||||
def get_message(self):
|
||||
return self.compose_function(self.trends[self.buffer.list.get_selected()])[0]
|
||||
|
||||
def remove_buffer(self):
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
if dlg == widgetUtils.YES:
|
||||
if self.name[:-3] in self.session.settings["other_buffers"]["trending_topic_buffers"]:
|
||||
self.session.settings["other_buffers"]["trending_topic_buffers"].remove(self.name[:-3])
|
||||
self.timer.cancel()
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
def interact(self, *args, **kwargs):
|
||||
self.searchfunction(value=self.get_message())
|
||||
|
||||
class conversationBufferController(searchBufferController):
|
||||
|
||||
def start_stream(self, start=False):
|
||||
if start == True:
|
||||
self.statuses = []
|
||||
self.ids = []
|
||||
self.statuses.append(self.tweet)
|
||||
self.ids.append(self.tweet["id"])
|
||||
tweet = self.tweet
|
||||
while tweet["in_reply_to_status_id"] != None:
|
||||
tweet = self.session.twitter.twitter.show_status(id=tweet["in_reply_to_status_id"])
|
||||
self.statuses.insert(0, tweet)
|
||||
self.ids.append(tweet["id"])
|
||||
if tweet["in_reply_to_status_id"] == None:
|
||||
self.kwargs["since_id"] = tweet["id"]
|
||||
self.ids.append(tweet["id"])
|
||||
val2 = self.session.search(self.name, *self.args, **self.kwargs)
|
||||
for i in val2:
|
||||
if i["in_reply_to_status_id"] in self.ids:
|
||||
self.statuses.append(i)
|
||||
self.ids.append(i["id"])
|
||||
tweet = i
|
||||
number_of_items = self.session.order_buffer(self.name, self.statuses)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if number_of_items > 0:
|
||||
self.session.sound.play("search_updated.ogg")
|
||||
|
76
src/controller/listsController.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import widgetUtils
|
||||
import output
|
||||
from wxUI.dialogs import lists
|
||||
from twython import TwythonError
|
||||
from twitter import compose, utils
|
||||
from pubsub import pub
|
||||
|
||||
class listsController(object):
|
||||
def __init__(self, session, user=None):
|
||||
super(listsController, self).__init__()
|
||||
self.session = session
|
||||
if user == None:
|
||||
self.dialog = lists.listViewer()
|
||||
self.dialog.populate_list(self.get_all_lists())
|
||||
widgetUtils.connect_event(self.dialog.createBtn, widgetUtils.BUTTON_PRESSED, self.create_list)
|
||||
widgetUtils.connect_event(self.dialog.editBtn, widgetUtils.BUTTON_PRESSED, self.edit_list)
|
||||
widgetUtils.connect_event(self.dialog.view, widgetUtils.BUTTON_PRESSED, self.open_list_as_buffer)
|
||||
self.dialog.get_response()
|
||||
|
||||
def get_all_lists(self):
|
||||
return [compose.compose_list(item) for item in self.session.db["lists"]]
|
||||
|
||||
def create_list(self, *args, **kwargs):
|
||||
dialog = lists.createListDialog()
|
||||
if dialog.get_response() == widgetUtils.OK:
|
||||
name = dialog.get("name")
|
||||
description = dialog.get("description")
|
||||
p = dialog.get("public")
|
||||
if public == True:
|
||||
mode = "public"
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
new_list = self.session.twitter.twitter.create_list(name=name, description=description, mode=mode)
|
||||
self.session.db["lists"].append(new_list)
|
||||
self.dialog.lista.insert_item(False, *compose.compose_list(new_list))
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||
dialog.destroy()
|
||||
|
||||
def edit_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]
|
||||
dialog = lists.editListDialog(list)
|
||||
if dialog.get_response() == widgetUtils.OK:
|
||||
name = dialog.get("name")
|
||||
description = dialog.get("description")
|
||||
p = dialog.get("public")
|
||||
if p == True:
|
||||
mode = "public"
|
||||
else:
|
||||
mode = "private"
|
||||
try:
|
||||
self.session.twitter.twitter.update_list(list_id=list["id"], name=name, description=description, mode=mode)
|
||||
self.session.get_lists()
|
||||
self.dialog.populate_list(self.get_all_lists(), True)
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
dialog.destroy()
|
||||
|
||||
def remove_list(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]["id"]
|
||||
if lists.remove_list() == widgetUtils.YES:
|
||||
try:
|
||||
self.session.twitter.twitter.delete_list(list_id=list)
|
||||
self.session.db["lists"].pop(self.dialog.get_item())
|
||||
self.dialog.lista.remove_item(self.dialog.get_item())
|
||||
except TwythonError as e:
|
||||
output.speak("error %s: %s" % (e.error_code, e.msg))
|
||||
|
||||
def open_list_as_buffer(self, *args, **kwargs):
|
||||
if self.dialog.lista.get_count() == 0: return
|
||||
list = self.session.db["lists"][self.dialog.get_item()]
|
||||
pub.sendMessage("create-new-buffer", buffer="list", account=self.session.db["user_name"], create=list["slug"])
|
1286
src/controller/mainController.py
Normal file
186
src/controller/messages.py
Normal file
@@ -0,0 +1,186 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
system = platform.system()
|
||||
import widgetUtils
|
||||
import output
|
||||
import url_shortener
|
||||
import sound
|
||||
from pubsub import pub
|
||||
if system == "Windows":
|
||||
from wxUI.dialogs import message, urlList
|
||||
from extra import translator, SpellChecker, autocompletionUsers
|
||||
from extra.AudioUploader import audioUploader
|
||||
elif system == "Linux":
|
||||
from gtkUI.dialogs import message
|
||||
from twitter import utils
|
||||
|
||||
class basicTweet(object):
|
||||
""" This class handles the tweet main features. Other classes should derive from this class."""
|
||||
def __init__(self, session, title, caption, text, messageType="tweet", max=140):
|
||||
super(basicTweet, self).__init__()
|
||||
self.max = max
|
||||
self.title = title
|
||||
self.session = session
|
||||
self.message = getattr(message, messageType)(title, caption, text)
|
||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||
widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach)
|
||||
# if system == "Windows":
|
||||
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||
self.text_processor()
|
||||
widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten)
|
||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||
|
||||
def translate(self, event=None):
|
||||
dlg = translator.gui.translateDialog()
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
text_to_translate = self.message.get_text().encode("utf-8")
|
||||
source = [x[0] for x in translator.translator.available_languages()][dlg.get("source_lang")]
|
||||
dest = [x[0] for x in translator.translator.available_languages()][dlg.get("dest_lang")]
|
||||
msg = translator.translator.translate(text_to_translate, source, dest)
|
||||
self.message.set_text(msg)
|
||||
self.message.text_focus()
|
||||
output.speak(_(u"Translated"))
|
||||
else:
|
||||
return
|
||||
|
||||
def shorten(self, event=None):
|
||||
urls = utils.find_urls_in_text(self.message.get_text())
|
||||
if len(urls) == 0:
|
||||
output.speak(_(u"There's no URL to be shortened"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) == 1:
|
||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0])))
|
||||
output.speak(_(u"URL shortened"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) > 1:
|
||||
list_urls = urlList.urlList()
|
||||
list_urls.populate_list(urls)
|
||||
if list_urls.get_response() == widgetUtils.OK:
|
||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string())))
|
||||
output.speak(_(u"URL shortened"))
|
||||
self.message.text_focus()
|
||||
|
||||
def unshorten(self, event=None):
|
||||
urls = utils.find_urls_in_text(self.message.get_text())
|
||||
if len(urls) == 0:
|
||||
output.speak(_(u"There's no URL to be expanded"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) == 1:
|
||||
self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0])))
|
||||
output.speak(_(u"URL expanded"))
|
||||
self.message.text_focus()
|
||||
elif len(urls) > 1:
|
||||
list_urls = urlList.urlList()
|
||||
list_urls.populate_list(urls)
|
||||
if list_urls.get_response() == widgetUtils.OK:
|
||||
self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string())))
|
||||
output.speak(_(u"URL expanded"))
|
||||
self.message.text_focus()
|
||||
|
||||
def text_processor(self, *args, **kwargs):
|
||||
self.message.set_title(_(u"%s - %s of %d characters") % (self.title, len(self.message.get_text()), self.max))
|
||||
if len(self.message.get_text()) > 1:
|
||||
self.message.enable_button("shortenButton")
|
||||
self.message.enable_button("unshortenButton")
|
||||
else:
|
||||
self.message.disable_button("shortenButton")
|
||||
self.message.disable_button("unshortenButton")
|
||||
if len(self.message.get_text()) > self.max:
|
||||
self.session.sound.play("max_length.ogg")
|
||||
|
||||
def spellcheck(self, event=None):
|
||||
text = self.message.get_text()
|
||||
checker = SpellChecker.spellchecker.spellChecker(text, "")
|
||||
if hasattr(checker, "fixed_text"):
|
||||
self.message.set_text(checker.fixed_text)
|
||||
|
||||
def attach(self, *args, **kwargs):
|
||||
def completed_callback():
|
||||
url = dlg.uploaderFunction.get_url()
|
||||
pub.unsubscribe(dlg.uploaderDialog.update, "uploading")
|
||||
dlg.uploaderDialog.destroy()
|
||||
if url != 0:
|
||||
self.message.set_text(self.message.get_text()+url+" #audio")
|
||||
else:
|
||||
output.speak(_(u"Unable to upload the audio"))
|
||||
dlg.cleanup()
|
||||
dlg = audioUploader.audioUploader(self.session.settings, completed_callback)
|
||||
|
||||
class tweet(basicTweet):
|
||||
def __init__(self, session, title, caption, text, messageType="tweet", max=140):
|
||||
super(tweet, self).__init__(session, title, caption, text, messageType, max)
|
||||
self.image = None
|
||||
widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
|
||||
def upload_image(self, *args, **kwargs):
|
||||
if self.message.get("upload_image") == _(u"Discard image"):
|
||||
del self.image
|
||||
self.image = None
|
||||
output.speak(_(u"Discarded"))
|
||||
self.message.set("upload_image", _(u"Upload a picture"))
|
||||
else:
|
||||
self.image = self.message.get_image()
|
||||
if self.image != None:
|
||||
self.message.set("upload_image", _(u"Discard image"))
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu()
|
||||
|
||||
class reply(tweet):
|
||||
def __init__(self, session, title, caption, text, users=None):
|
||||
super(reply, self).__init__(session, title, caption, text, messageType="reply")
|
||||
self.users = users
|
||||
if self.users != None and len(self.users) > 1:
|
||||
widgetUtils.connect_event(self.message.mentionAll, widgetUtils.BUTTON_PRESSED, self.mention_all)
|
||||
self.message.enable_button("mentionAll")
|
||||
self.message.set_cursor_at_end()
|
||||
|
||||
def mention_all(self, *args, **kwargs):
|
||||
self.message.set_text(self.message.get_text()+self.users)
|
||||
self.message.set_cursor_at_end()
|
||||
self.message.text_focus()
|
||||
|
||||
class dm(basicTweet):
|
||||
def __init__(self, session, title, caption, text):
|
||||
super(dm, self).__init__(session, title, caption, text, messageType="dm")
|
||||
widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users)
|
||||
|
||||
def autocomplete_users(self, *args, **kwargs):
|
||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
||||
c.show_menu("dm")
|
||||
|
||||
class viewTweet(basicTweet):
|
||||
def __init__(self, tweet, tweetList, is_tweet=True):
|
||||
if is_tweet == True:
|
||||
text = ""
|
||||
for i in xrange(0, len(tweetList)):
|
||||
if tweetList[i].has_key("retweeted_status"):
|
||||
text = text + "rt @%s: %s\n\n" % (tweetList[i]["retweeted_status"]["user"]["screen_name"], tweetList[i]["retweeted_status"]["text"])
|
||||
else:
|
||||
text = text + "@%s: %s\n\n" % (tweetList[i]["user"]["screen_name"], tweetList[i]["text"])
|
||||
rt_count = str(tweet["retweet_count"])
|
||||
favs_count = str(tweet["favorite_count"])
|
||||
if text == "":
|
||||
if tweet.has_key("retweeted_status"):
|
||||
text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], tweet["retweeted_status"]["text"])
|
||||
else:
|
||||
text = tweet["text"]
|
||||
self.message = message.viewTweet(text, rt_count, favs_count)
|
||||
self.message.set_title(len(text))
|
||||
else:
|
||||
text = tweet
|
||||
self.message = message.viewNonTweet(text)
|
||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||
if self.contain_urls() == True:
|
||||
self.message.enable_button("unshortenButton")
|
||||
widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten)
|
||||
self.message.get_response()
|
||||
|
||||
def contain_urls(self):
|
||||
if len(utils.find_urls_in_text(self.message.get_text())) > 0:
|
||||
return True
|
||||
return False
|
281
src/controller/settings.py
Normal file
@@ -0,0 +1,281 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import webbrowser
|
||||
import sound_lib
|
||||
import paths
|
||||
import widgetUtils
|
||||
import config
|
||||
import languageHandler
|
||||
import output
|
||||
import application
|
||||
from wxUI.dialogs import configuration
|
||||
from wxUI import commonMessageDialogs
|
||||
from extra.autocompletionUsers import settings
|
||||
from extra.AudioUploader import dropbox_transfer
|
||||
from pubsub import pub
|
||||
import logging
|
||||
import config_utils
|
||||
log = logging.getLogger("Settings")
|
||||
|
||||
class globalSettingsController(object):
|
||||
def __init__(self):
|
||||
super(globalSettingsController, self).__init__()
|
||||
self.dialog = configuration.configurationDialog()
|
||||
self.create_config()
|
||||
self.needs_restart = False
|
||||
self.is_started = True
|
||||
|
||||
def make_kmmap(self):
|
||||
res={}
|
||||
for i in os.listdir(paths.app_path('keymaps')):
|
||||
if ".keymap" not in i:
|
||||
continue
|
||||
try:
|
||||
res[config_utils.load_config(paths.app_path('keymaps/'+i))['info']['name']]=i
|
||||
except:
|
||||
log.exception("Exception while loading keymap " + i)
|
||||
return res
|
||||
|
||||
def create_config(self):
|
||||
self.kmmap=self.make_kmmap()
|
||||
self.langs = languageHandler.getAvailableLanguages()
|
||||
langs = []
|
||||
[langs.append(i[1]) for i in self.langs]
|
||||
self.codes = []
|
||||
[self.codes.append(i[0]) for i in self.langs]
|
||||
id = self.codes.index(config.app["app-settings"]["language"])
|
||||
self.kmfriendlies=[]
|
||||
self.kmnames=[]
|
||||
for k,v in self.kmmap.items():
|
||||
self.kmfriendlies.append(k)
|
||||
self.kmnames.append(v)
|
||||
self.kmid=self.kmnames.index(config.app['app-settings']['load_keymap'])
|
||||
self.dialog.create_general(langs,self.kmfriendlies)
|
||||
self.dialog.general.language.SetSelection(id)
|
||||
self.dialog.general.km.SetSelection(self.kmid)
|
||||
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
|
||||
|
||||
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
|
||||
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
|
||||
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
|
||||
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
|
||||
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
|
||||
self.dialog.create_proxy()
|
||||
self.dialog.set_value("proxy", "server", config.app["proxy"]["server"])
|
||||
self.dialog.set_value("proxy", "port", config.app["proxy"]["port"])
|
||||
self.dialog.set_value("proxy", "user", config.app["proxy"]["user"])
|
||||
self.dialog.set_value("proxy", "password", config.app["proxy"]["password"])
|
||||
self.dialog.realize()
|
||||
self.response = self.dialog.get_response()
|
||||
|
||||
def save_configuration(self):
|
||||
if self.codes[self.dialog.general.language.GetSelection()] != config.app["app-settings"]["language"]:
|
||||
config.app["app-settings"]["language"] = self.codes[self.dialog.general.language.GetSelection()]
|
||||
languageHandler.setLanguage(config.app["app-settings"]["language"])
|
||||
self.needs_restart = True
|
||||
if self.kmnames[self.dialog.general.km.GetSelection()] != config.app["app-settings"]["load_keymap"]:
|
||||
config.app["app-settings"]["load_keymap"] =self.kmnames[self.dialog.general.km.GetSelection()]
|
||||
self.needs_restart = True
|
||||
|
||||
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
|
||||
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
|
||||
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
|
||||
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
||||
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
||||
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
||||
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
|
||||
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
|
||||
if config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
||||
if self.is_started == True:
|
||||
self.needs_restart = True
|
||||
config.app["proxy"]["server"] = self.dialog.get_value("proxy", "server")
|
||||
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
|
||||
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
|
||||
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
|
||||
config.app.write()
|
||||
|
||||
class accountSettingsController(globalSettingsController):
|
||||
def __init__(self, buffer, window):
|
||||
self.user = buffer.session.db["user_name"]
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.config = buffer.session.settings
|
||||
super(accountSettingsController, self).__init__()
|
||||
|
||||
def create_config(self):
|
||||
self.dialog.create_general_account()
|
||||
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
|
||||
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
|
||||
self.dialog.set_value("general", "apiCalls", self.config["general"]["max_api_calls"])
|
||||
self.dialog.set_value("general", "itemsPerApiCall", self.config["general"]["max_tweets_per_call"])
|
||||
self.dialog.set_value("general", "reverse_timelines", self.config["general"]["reverse_timelines"])
|
||||
rt = self.config["general"]["retweet_mode"]
|
||||
if rt == "ask":
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Ask"))
|
||||
elif rt == "direct":
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet without comments"))
|
||||
else:
|
||||
self.dialog.set_value("general", "retweet_mode", _(u"Retweet with comments"))
|
||||
self.dialog.set_value("general", "persist_size", str(self.config["general"]["persist_size"]))
|
||||
self.dialog.create_other_buffers()
|
||||
buffer_values = self.get_buffers_list()
|
||||
self.dialog.buffers.insert_buffers(buffer_values)
|
||||
self.dialog.buffers.connect_hook_func(self.toggle_buffer_active)
|
||||
widgetUtils.connect_event(self.dialog.buffers.toggle_state, widgetUtils.BUTTON_PRESSED, self.toggle_state)
|
||||
widgetUtils.connect_event(self.dialog.buffers.up, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_up)
|
||||
widgetUtils.connect_event(self.dialog.buffers.down, widgetUtils.BUTTON_PRESSED, self.dialog.buffers.move_down)
|
||||
|
||||
|
||||
self.dialog.create_ignored_clients(self.config["twitter"]["ignored_clients"])
|
||||
widgetUtils.connect_event(self.dialog.ignored_clients.add, widgetUtils.BUTTON_PRESSED, self.add_ignored_client)
|
||||
widgetUtils.connect_event(self.dialog.ignored_clients.remove, widgetUtils.BUTTON_PRESSED, self.remove_ignored_client)
|
||||
self.input_devices = sound_lib.input.Input.get_device_names()
|
||||
self.output_devices = sound_lib.output.Output.get_device_names()
|
||||
self.soundpacks = []
|
||||
[self.soundpacks.append(i) for i in os.listdir(paths.sound_path()) if os.path.isdir(paths.sound_path(i)) == True ]
|
||||
self.dialog.create_sound(self.input_devices, self.output_devices, self.soundpacks)
|
||||
self.dialog.set_value("sound", "volumeCtrl", self.config["sound"]["volume"]*100)
|
||||
self.dialog.set_value("sound", "input", self.config["sound"]["input_device"])
|
||||
self.dialog.set_value("sound", "output", self.config["sound"]["output_device"])
|
||||
self.dialog.set_value("sound", "session_mute", self.config["sound"]["session_mute"])
|
||||
self.dialog.set_value("sound", "soundpack", self.config["sound"]["current_soundpack"])
|
||||
self.dialog.create_audio_services()
|
||||
if self.config["services"]["dropbox_token"] == "":
|
||||
self.dialog.services.set_dropbox(False)
|
||||
else:
|
||||
self.dialog.services.set_dropbox(True)
|
||||
widgetUtils.connect_event(self.dialog.services.dropbox, widgetUtils.BUTTON_PRESSED, self.manage_dropbox)
|
||||
self.dialog.set_value("services", "apiKey", self.config["sound"]["sndup_api_key"])
|
||||
self.dialog.realize()
|
||||
self.dialog.set_title(_(u"Account settings for %s") % (self.user,))
|
||||
self.response = self.dialog.get_response()
|
||||
|
||||
def save_configuration(self):
|
||||
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
||||
self.needs_restart = True
|
||||
self.config["general"]["relative_times"] = self.dialog.get_value("general", "relative_time")
|
||||
self.config["general"]["max_api_calls"] = self.dialog.get_value("general", "apiCalls")
|
||||
self.config["general"]["max_tweets_per_call"] = self.dialog.get_value("general", "itemsPerApiCall")
|
||||
if self.config["general"]["persist_size"] != self.dialog.get_value("general", "persist_size"):
|
||||
if self.dialog.get_value("general", "persist_size") == '':
|
||||
self.config["general"]["persist_size"] =-1
|
||||
else:
|
||||
try:
|
||||
self.config["general"]["persist_size"] = int(self.dialog.get_value("general", "persist_size"))
|
||||
except ValueError:
|
||||
output.speak("Invalid cache size, setting to default.",True)
|
||||
self.config["general"]["persist_size"] =1764
|
||||
|
||||
if self.config["general"]["reverse_timelines"] != self.dialog.get_value("general", "reverse_timelines"):
|
||||
self.needs_restart = True
|
||||
self.config["general"]["reverse_timelines"] = self.dialog.get_value("general", "reverse_timelines")
|
||||
rt = self.dialog.get_value("general", "retweet_mode")
|
||||
if rt == _(u"Ask"):
|
||||
self.config["general"]["retweet_mode"] = "ask"
|
||||
elif rt == _(u"Retweet without comments"):
|
||||
self.config["general"]["retweet_mode"] = "direct"
|
||||
else:
|
||||
self.config["general"]["retweet_mode"] = "comment"
|
||||
buffers_list = self.dialog.buffers.get_list()
|
||||
if set(self.config["general"]["buffer_order"]) != set(buffers_list) or buffers_list != self.config["general"]["buffer_order"]:
|
||||
self.needs_restart = True
|
||||
self.config["general"]["buffer_order"] = buffers_list
|
||||
|
||||
# if self.config["other_buffers"]["show_followers"] != self.dialog.get_value("buffers", "followers"):
|
||||
# self.config["other_buffers"]["show_followers"] = self.dialog.get_value("buffers", "followers")
|
||||
# pub.sendMessage("create-new-buffer", buffer="followers", account=self.user, create=self.config["other_buffers"]["show_followers"])
|
||||
# if self.config["other_buffers"]["show_friends"] != self.dialog.get_value("buffers", "friends"):
|
||||
# self.config["other_buffers"]["show_friends"] = self.dialog.get_value("buffers", "friends")
|
||||
# pub.sendMessage("create-new-buffer", buffer="friends", account=self.user, create=self.config["other_buffers"]["show_friends"])
|
||||
# if self.config["other_buffers"]["show_favourites"] != self.dialog.get_value("buffers", "favs"):
|
||||
# self.config["other_buffers"]["show_favourites"] = self.dialog.get_value("buffers", "favs")
|
||||
# pub.sendMessage("create-new-buffer", buffer="favourites", account=self.user, create=self.config["other_buffers"]["show_favourites"])
|
||||
# if self.config["other_buffers"]["show_blocks"] != self.dialog.get_value("buffers", "blocks"):
|
||||
# self.config["other_buffers"]["show_blocks"] = self.dialog.get_value("buffers", "blocks")
|
||||
# pub.sendMessage("create-new-buffer", buffer="blocked", account=self.user, create=self.config["other_buffers"]["show_blocks"])
|
||||
# if self.config["other_buffers"]["show_muted_users"] != self.dialog.get_value("buffers", "mutes"):
|
||||
# self.config["other_buffers"]["show_muted_users"] = self.dialog.get_value("buffers", "mutes")
|
||||
# pub.sendMessage("create-new-buffer", buffer="muted", account=self.user, create=self.config["other_buffers"]["show_muted_users"])
|
||||
# if self.config["other_buffers"]["show_events"] != self.dialog.get_value("buffers", "events"):
|
||||
# self.config["other_buffers"]["show_events"] = self.dialog.get_value("buffers", "events")
|
||||
# pub.sendMessage("create-new-buffer", buffer="events", account=self.user, create=self.config["other_buffers"]["show_events"])
|
||||
if self.config["sound"]["input_device"] != self.dialog.sound.get("input"):
|
||||
self.config["sound"]["input_device"] = self.dialog.sound.get("input")
|
||||
try:
|
||||
self.buffer.session.sound.input.set_device(self.buffer.session.sound.input.find_device_by_name(self.config["sound"]["input_device"]))
|
||||
except:
|
||||
self.config["sound"]["input_device"] = "default"
|
||||
if self.config["sound"]["output_device"] != self.dialog.sound.get("output"):
|
||||
self.config["sound"]["output_device"] = self.dialog.sound.get("output")
|
||||
try:
|
||||
self.buffer.session.sound.output.set_device(self.buffer.session.sound.output.find_device_by_name(self.config["sound"]["output_device"]))
|
||||
except:
|
||||
self.config["sound"]["output_device"] = "default"
|
||||
self.config["sound"]["volume"] = self.dialog.get_value("sound", "volumeCtrl")/100.0
|
||||
self.config["sound"]["session_mute"] = self.dialog.get_value("sound", "session_mute")
|
||||
self.config["sound"]["current_soundpack"] = self.dialog.sound.get("soundpack")
|
||||
self.buffer.session.sound.config = self.config["sound"]
|
||||
self.buffer.session.sound.check_soundpack()
|
||||
self.config["sound"]["sndup_api_key"] = self.dialog.get_value("services", "apiKey")
|
||||
|
||||
def toggle_state(self,*args,**kwargs):
|
||||
return self.dialog.buffers.change_selected_item()
|
||||
|
||||
def manage_autocomplete(self, *args, **kwargs):
|
||||
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
|
||||
|
||||
def add_ignored_client(self, *args, **kwargs):
|
||||
client = commonMessageDialogs.get_ignored_client()
|
||||
if client == None: return
|
||||
if client not in self.config["twitter"]["ignored_clients"]:
|
||||
self.config["twitter"]["ignored_clients"].append(client)
|
||||
self.dialog.ignored_clients.append(client)
|
||||
|
||||
def remove_ignored_client(self, *args, **kwargs):
|
||||
if self.dialog.ignored_clients.get_clients() == 0: return
|
||||
id = self.dialog.ignored_clients.get_client_id()
|
||||
self.config["twitter"]["ignored_clients"].pop(id)
|
||||
self.dialog.ignored_clients.remove_(id)
|
||||
|
||||
def manage_dropbox(self, *args, **kwargs):
|
||||
if self.dialog.services.get_dropbox() == _(u"Link your Dropbox account"):
|
||||
self.connect_dropbox()
|
||||
else:
|
||||
self.disconnect_dropbox()
|
||||
|
||||
def connect_dropbox(self):
|
||||
auth = dropbox_transfer.dropboxLogin(self.config)
|
||||
url = auth.get_url()
|
||||
self.dialog.services.show_dialog()
|
||||
webbrowser.open(url)
|
||||
resp = self.dialog.services.get_response()
|
||||
if resp == "":
|
||||
self.dialog.services.set_dropbox(False)
|
||||
else:
|
||||
try:
|
||||
auth.authorise(resp)
|
||||
self.dialog.services.set_dropbox()
|
||||
except:
|
||||
self.dialog.services.show_error()
|
||||
self.dialog.services.set_dropbox(False)
|
||||
|
||||
def disconnect_dropbox(self):
|
||||
self.config["services"]["dropbox_token"] = ""
|
||||
self.dialog.services.set_dropbox(False)
|
||||
|
||||
def get_buffers_list(self):
|
||||
all_buffers = ['home','mentions','dm','sent_dm','sent_tweets','favorites','followers','friends','blocks','muted','events']
|
||||
list_buffers = []
|
||||
hidden_buffers=[]
|
||||
for i in all_buffers:
|
||||
if i in self.config["general"]["buffer_order"]:
|
||||
list_buffers.append((i, True))
|
||||
else:
|
||||
hidden_buffers.append((i, False))
|
||||
list_buffers.extend(hidden_buffers)
|
||||
return list_buffers
|
||||
|
||||
def toggle_buffer_active(self, ev):
|
||||
change = self.dialog.buffers.get_event(ev)
|
||||
if change == True:
|
||||
self.dialog.buffers.change_selected_item()
|
45
src/controller/trendingTopics.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from wxUI.dialogs import trends
|
||||
import widgetUtils
|
||||
|
||||
class trendingTopicsController(object):
|
||||
def __init__(self, session):
|
||||
super(trendingTopicsController, self).__init__()
|
||||
self.countries = {}
|
||||
self.cities = {}
|
||||
self.dialog = trends.trendingTopicsDialog()
|
||||
self.information = session.twitter.twitter.get_available_trends()
|
||||
self.split_information()
|
||||
widgetUtils.connect_event(self.dialog.country, widgetUtils.RADIOBUTTON, self.get_places)
|
||||
widgetUtils.connect_event(self.dialog.city, widgetUtils.RADIOBUTTON, self.get_places)
|
||||
self.get_places()
|
||||
|
||||
def split_information(self):
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] == "Country":
|
||||
self.countries[i["name"]] = i["woeid"]
|
||||
else:
|
||||
self.cities[i["name"]] = i["woeid"]
|
||||
|
||||
def get_places(self, event=None):
|
||||
values = []
|
||||
if self.dialog.get_active() == "country":
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] == "Country":
|
||||
values.append(i["name"])
|
||||
elif self.dialog.get_active() == "city":
|
||||
for i in self.information:
|
||||
if i["placeType"]["name"] != "Country":
|
||||
values.append(i["name"])
|
||||
self.dialog.set(values)
|
||||
|
||||
def get_woeid(self):
|
||||
selected = self.dialog.get_item()
|
||||
if self.dialog.get_active() == "country":
|
||||
woeid = self.countries[selected]
|
||||
else:
|
||||
woeid = self.cities[selected]
|
||||
return woeid
|
||||
|
||||
def get_string(self):
|
||||
return self.dialog.get_item()
|
102
src/controller/user.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import webbrowser
|
||||
import widgetUtils
|
||||
import output
|
||||
from wxUI.dialogs import update_profile, show_user
|
||||
from twython import TwythonError
|
||||
|
||||
class profileController(object):
|
||||
def __init__(self, session, user=None):
|
||||
super(profileController, self).__init__()
|
||||
self.file = None
|
||||
self.session = session
|
||||
self.user = user
|
||||
if user == None:
|
||||
self.dialog = update_profile.updateProfileDialog()
|
||||
self.get_data(screen_name=self.session.db["user_name"])
|
||||
self.fill_profile_fields()
|
||||
self.uploaded = False
|
||||
widgetUtils.connect_event(self.dialog.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image)
|
||||
else:
|
||||
self.dialog = show_user.showUserProfile()
|
||||
self.get_data(screen_name=self.user)
|
||||
string = self.get_user_info()
|
||||
self.dialog.set("text", string)
|
||||
self.dialog.set_title(_(u"Information for %s") % (self.data["screen_name"]))
|
||||
if self.data["url"] != None:
|
||||
self.dialog.enable_url()
|
||||
widgetUtils.connect_event(self.dialog.url, widgetUtils.BUTTON_PRESSED, self.visit_url)
|
||||
if self.dialog.get_response() == widgetUtils.OK and self.user == None:
|
||||
self.do_update()
|
||||
|
||||
def get_data(self, screen_name):
|
||||
self.data = self.session.twitter.twitter.show_user(screen_name=screen_name)
|
||||
|
||||
def fill_profile_fields(self):
|
||||
self.dialog.set_name(self.data["name"])
|
||||
if self.data["url"] != None:
|
||||
self.dialog.set_url(self.data["url"])
|
||||
if len(self.data["location"]) > 0:
|
||||
self.dialog.set_location(self.data["location"])
|
||||
if len(self.data["description"]) > 0:
|
||||
self.dialog.set_description(self.data["description"])
|
||||
|
||||
def get_image(self):
|
||||
file = self.dialog.upload_picture()
|
||||
if file != None:
|
||||
self.file = open(file, "rb")
|
||||
self.uploaded = True
|
||||
self.dialog.change_upload_button(self.uploaded)
|
||||
|
||||
def discard_image(self):
|
||||
self.file = None
|
||||
output.speak(_(u"Discarded"))
|
||||
self.uploaded = False
|
||||
self.dialog.change_upload_button(self.uploaded)
|
||||
|
||||
def upload_image(self, *args, **kwargs):
|
||||
if self.uploaded == False:
|
||||
self.get_image()
|
||||
elif self.uploaded == True:
|
||||
self.discard_image()
|
||||
|
||||
def do_update(self):
|
||||
if self.user != None: return
|
||||
name = self.dialog.get("name")
|
||||
description = self.dialog.get("description")
|
||||
location = self.dialog.get("location")
|
||||
url = self.dialog.get("url")
|
||||
if self.file != None:
|
||||
try:
|
||||
self.session.twitter.twitter.update_profile_image(image=self.file)
|
||||
except TwythonError as e:
|
||||
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
|
||||
try:
|
||||
self.session.twitter.twitter.update_profile(name=name, description=description, location=location, url=url)
|
||||
except TwythonError as e:
|
||||
output.speak(u"Error %s. %s" % (e.error_code, e.msg))
|
||||
|
||||
def get_user_info(self):
|
||||
|
||||
string = u""
|
||||
string = string + _(u"Username: @%s\n") % (self.data["screen_name"])
|
||||
string = string + _(u"Name: %s\n") % (self.data["name"])
|
||||
if self.data["location"] != "":
|
||||
string = string + _(u"Location: %s\n") % (self.data["location"])
|
||||
if self.data["url"] != None:
|
||||
string = string+ _(u"URL: %s\n") % (self.data["url"])
|
||||
if self.data["description"] != "":
|
||||
string = string+ _(u"Bio: %s\n") % (self.data["description"])
|
||||
if self.data["protected"] == True: protected = _(u"Yes")
|
||||
else: protected = _(u"No")
|
||||
string = string+ _(u"Protected: %s\n") % (protected)
|
||||
string = string+_(u"Followers: %s\n Friends: %s\n") % (self.data["followers_count"], self.data["friends_count"])
|
||||
if self.data["verified"] == True: verified = _(u"Yes")
|
||||
else: verified = _(u"No")
|
||||
string = string+ _(u"Verified: %s\n") % (verified)
|
||||
string = string+ _(u"Tweets: %s\n") % (self.data["statuses_count"])
|
||||
string = string+ _(u"Favourites: %s") % (self.data["favourites_count"])
|
||||
return string
|
||||
|
||||
def visit_url(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(self.data["url"])
|
73
src/controller/userActionsController.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from wxUI.dialogs import userActions
|
||||
from pubsub import pub
|
||||
import re
|
||||
import widgetUtils
|
||||
import output
|
||||
from twython import TwythonError
|
||||
|
||||
class userActionsController(object):
|
||||
def __init__(self, buffer, users=[], default="follow"):
|
||||
super(userActionsController, self).__init__()
|
||||
self.buffer = buffer
|
||||
self.session = buffer.session
|
||||
self.dialog = userActions.UserActionsDialog(users, default)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
self.process_action()
|
||||
|
||||
def process_action(self):
|
||||
action = self.dialog.get_action()
|
||||
user = self.dialog.get_user()
|
||||
if user == "": return
|
||||
getattr(self, action)(user)
|
||||
|
||||
def follow(self, user):
|
||||
try:
|
||||
self.session.twitter.twitter.create_friendship(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def unfollow(self, user):
|
||||
try:
|
||||
id = self.session.twitter.twitter.destroy_friendship(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def mute(self, user):
|
||||
try:
|
||||
id = self.session.twitter.twitter.create_mute(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def unmute(self, user):
|
||||
try:
|
||||
id = self.session.twitter.twitter.destroy_mute(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def report(self, user):
|
||||
try:
|
||||
id = self.session.twitter.twitter.report_spam(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def block(self, user):
|
||||
try:
|
||||
id = self.session.twitter.twitter.create_block(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def unblock(self, user):
|
||||
try:
|
||||
id = self.session.twitter.twitter.destroy_block(screen_name=user )
|
||||
except TwythonError as err:
|
||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||
|
||||
def ignore_client(self, user):
|
||||
tweet = self.buffer.get_right_tweet()
|
||||
if tweet.has_key("sender"):
|
||||
output.speak(_(u"You can't ignore direct messages"))
|
||||
return
|
||||
client = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||
if client not in self.session.settings["twitter"]["ignored_clients"]:
|
||||
self.session.settings["twitter"]["ignored_clients"].append(client)
|
@@ -1,5 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Handles storage from a durus database """
|
||||
class db(object):
|
||||
def __init__(self):
|
||||
self.settings = {}
|
@@ -1,3 +0,0 @@
|
||||
import gui, transfer, transfer_dialogs, platform
|
||||
if platform.system() != "Darwin":
|
||||
import dropbox
|
189
src/extra/AudioUploader/audioUploader.py
Normal file
@@ -0,0 +1,189 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import widgetUtils
|
||||
import wx_ui
|
||||
import wx_transfer_dialogs
|
||||
import dropbox_transfer, transfer
|
||||
import output
|
||||
import tempfile
|
||||
import sound
|
||||
import os
|
||||
import config
|
||||
from pubsub import pub
|
||||
from mysc.thread_utils import call_threaded
|
||||
import sound_lib
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("extra.AudioUploader.audioUploader")
|
||||
class audioUploader(object):
|
||||
def __init__(self, configFile, completed_callback):
|
||||
self.config = configFile
|
||||
super(audioUploader, self).__init__()
|
||||
self.dialog = wx_ui.audioDialog(services=self.get_available_services())
|
||||
self.file = None
|
||||
self.recorded = False
|
||||
self.recording = None
|
||||
self.playing = None
|
||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
||||
widgetUtils.connect_event(self.dialog.pause, widgetUtils.BUTTON_PRESSED, self.on_pause)
|
||||
widgetUtils.connect_event(self.dialog.record, widgetUtils.BUTTON_PRESSED, self.on_record)
|
||||
widgetUtils.connect_event(self.dialog.attach_exists, widgetUtils.BUTTON_PRESSED, self.on_attach_exists)
|
||||
widgetUtils.connect_event(self.dialog.discard, widgetUtils.BUTTON_PRESSED, self.on_discard)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
self.postprocess()
|
||||
log.debug("Uploading file %s to %s..." % (self.file, self.dialog.get("services")))
|
||||
self.uploaderDialog = wx_transfer_dialogs.UploadDialog(self.file)
|
||||
output.speak(_(u"Attaching..."))
|
||||
if self.dialog.get("services") == "Dropbox":
|
||||
self.uploaderFunction = dropbox_transfer.dropboxUploader(filename=self.file, completed_callback=completed_callback, config=self.config)
|
||||
elif self.dialog.get("services") == "SNDUp":
|
||||
base_url = "http://sndup.net/post.php"
|
||||
if len(self.config["sound"]["sndup_api_key"]) > 0:
|
||||
url = base_url + '?apikey=' + self.config['sound']['sndup_api_key']
|
||||
else:
|
||||
url = base_url
|
||||
self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback)
|
||||
elif self.dialog.get("services") == "TwUp":
|
||||
url = "http://api.twup.me/post.json"
|
||||
self.uploaderFunction = transfer.Upload(field='file', url=url, filename=self.file, completed_callback=completed_callback)
|
||||
pub.subscribe(self.uploaderDialog.update, "uploading")
|
||||
self.uploaderDialog.get_response()
|
||||
self.uploaderFunction.perform_threaded()
|
||||
|
||||
def get_available_services(self):
|
||||
services = []
|
||||
services.append("TwUp")
|
||||
if self.config["services"]["dropbox_token"] != "":
|
||||
services.append("Dropbox")
|
||||
services.append("SNDUp")
|
||||
return services
|
||||
|
||||
def on_pause(self, *args, **kwargs):
|
||||
if self.dialog.get("pause") == _(u"Pause"):
|
||||
self.recording.pause()
|
||||
self.dialog.set("pause", _(u"Resume"))
|
||||
elif self.dialog.get("pause") == _(u"Resume"):
|
||||
self.recording.play()
|
||||
self.dialog.set("pause", _(U"Pause"))
|
||||
|
||||
def on_record(self, *args, **kwargs):
|
||||
if self.recording != None:
|
||||
self.stop_recording()
|
||||
self.dialog.disable_control("pause")
|
||||
else:
|
||||
self.start_recording()
|
||||
self.dialog.enable_control("pause")
|
||||
|
||||
def start_recording(self):
|
||||
self.dialog.disable_control("attach_exists")
|
||||
self.file = tempfile.mktemp(suffix='.wav')
|
||||
self.recording = sound.recording(self.file)
|
||||
self.recording.play()
|
||||
self.dialog.set("record", _(u"Stop"))
|
||||
output.speak(_(u"Recording"))
|
||||
|
||||
def stop_recording(self):
|
||||
self.recording.stop()
|
||||
self.recording.free()
|
||||
output.speak(_(u"Stopped"))
|
||||
self.recorded = True
|
||||
self.dialog.set("record", _(u"Record"))
|
||||
self.file_attached()
|
||||
|
||||
def file_attached(self):
|
||||
self.dialog.set("pause", _(u"Pause"))
|
||||
self.dialog.disable_control("record")
|
||||
self.dialog.enable_control("play")
|
||||
self.dialog.enable_control("discard")
|
||||
self.dialog.disable_control("attach_exists")
|
||||
self.dialog.enable_control("attach")
|
||||
self.dialog.play.SetFocus()
|
||||
|
||||
def on_discard(self, *args, **kwargs):
|
||||
if self.playing:
|
||||
self._stop()
|
||||
if self.recording != None:
|
||||
self.dialog.disable_control("attach")
|
||||
self.dialog.disable_control("play")
|
||||
self.file = None
|
||||
self.dialog.enable_control("record")
|
||||
self.dialog.enable_control("attach_exists")
|
||||
self.dialog.record.SetFocus()
|
||||
self.dialog.disable_control("discard")
|
||||
self.recording = None
|
||||
output.speak(_(u"Discarded"))
|
||||
|
||||
def on_play(self, *args, **kwargs):
|
||||
if not self.playing:
|
||||
call_threaded(self._play)
|
||||
else:
|
||||
self._stop()
|
||||
|
||||
def _play(self):
|
||||
output.speak(_(u"Playing..."))
|
||||
# try:
|
||||
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
|
||||
self.playing.play()
|
||||
self.dialog.set("play", _(u"Stop"))
|
||||
try:
|
||||
while self.playing.is_playing:
|
||||
pass
|
||||
self.dialog.set("play", _(u"Play"))
|
||||
self.playing.free()
|
||||
self.playing = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def _stop(self):
|
||||
output.speak(_(u"Stopped"))
|
||||
self.playing.stop()
|
||||
self.playing.free()
|
||||
self.dialog.set("play", _(u"Play"))
|
||||
self.playing = None
|
||||
|
||||
def postprocess(self):
|
||||
if self.file.lower().endswith('.wav'):
|
||||
output.speak(_(u"Recoding audio..."))
|
||||
sound.recode_audio(self.file)
|
||||
self.wav_file = self.file
|
||||
self.file = '%s.ogg' % self.file[:-4]
|
||||
|
||||
def cleanup(self):
|
||||
if self.playing and self.playing.is_playing:
|
||||
self.playing.stop()
|
||||
if self.recording != None:
|
||||
if self.recording.is_playing:
|
||||
self.recording.stop()
|
||||
try:
|
||||
self.recording.free()
|
||||
except:
|
||||
pass
|
||||
os.remove(self.file)
|
||||
if hasattr(self, 'wav_file'):
|
||||
os.remove(self.wav_file)
|
||||
del(self.wav_file)
|
||||
if hasattr(self, 'wav_file') and os.path.exists(self.file):
|
||||
os.remove(self.file)
|
||||
|
||||
|
||||
def on_attach_exists(self, *args, **kwargs):
|
||||
self.file = self.dialog.get_file()
|
||||
if self.file != False:
|
||||
self.file_attached()
|
||||
|
@@ -3,14 +3,16 @@ import threading
|
||||
import time
|
||||
import os
|
||||
import exceptions
|
||||
import wx
|
||||
import dropbox
|
||||
import config
|
||||
from mysc import event
|
||||
import logging
|
||||
import application
|
||||
from keys import keyring
|
||||
from utils import *
|
||||
from dropbox.rest import ErrorResponse
|
||||
from StringIO import StringIO
|
||||
from pubsub import pub
|
||||
|
||||
log = logging.getLogger("extra.AudioUploader.dropbox_transfer")
|
||||
class UnauthorisedError(exceptions.Exception):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnauthorisedError, self).__init__(*args, **kwargs)
|
||||
@@ -39,29 +41,33 @@ class newChunkedUploader(dropbox.client.ChunkedUploader):
|
||||
self.offset = reply['offset']
|
||||
|
||||
class dropboxLogin(object):
|
||||
def __init__(self):
|
||||
def __init__(self, config):
|
||||
log.debug("Trying to login in Dropbox...")
|
||||
self.logged = False
|
||||
self.app_key = "c8ikm0gexqvovol"
|
||||
self.app_secret = "gvvi6fzfecooast"
|
||||
self.config = config
|
||||
|
||||
def get_url(self):
|
||||
self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect(self.app_key, self.app_secret)
|
||||
log.debug("Getting autorisation URL...")
|
||||
self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect(keyring.get("dropbox_api_key"), keyring.get("dropbox_api_secret"))
|
||||
return self.flow.start()
|
||||
|
||||
def authorise(self, code):
|
||||
log.debug("Authorising " + application.name + " to Dropbox...")
|
||||
access_token, user_id = self.flow.finish(code)
|
||||
config.main["services"]["dropbox_token"] = access_token
|
||||
log.debug("Saving tokens...")
|
||||
self.config["services"]["dropbox_token"] = access_token
|
||||
self.logged = True
|
||||
|
||||
class dropboxUploader(object):
|
||||
def __init__(self, filename, completed_callback, wxDialog, short_url=False):
|
||||
if config.main["services"]["dropbox_token"] != "":
|
||||
self.client = dropbox.client.DropboxClient(config.main["services"]["dropbox_token"])
|
||||
def __init__(self, config, filename, completed_callback, short_url=False):
|
||||
if config["services"]["dropbox_token"] != "":
|
||||
log.debug("logging in Dropbox...")
|
||||
self.client = dropbox.client.DropboxClient(config["services"]["dropbox_token"])
|
||||
else:
|
||||
raise UnauthorisedError("You need authorise TWBlue")
|
||||
log.error("Dropbox is not authorised for this session.")
|
||||
raise UnauthorisedError("You need to authorise " + application.name)
|
||||
self.filename = filename
|
||||
self.short_url = short_url
|
||||
self.wxDialog = wxDialog
|
||||
self.file = open(self.filename, "rb")
|
||||
self.file_size = os.path.getsize(self.filename)
|
||||
self.uploader = newChunkedUploader(client=self.client, file_obj=self.file, length=self.file_size, callback=self.process)
|
||||
@@ -70,6 +76,7 @@ class dropboxUploader(object):
|
||||
self.background_thread = None
|
||||
self.current = 0
|
||||
self.transfer_rate = 0
|
||||
log.debug("File Size: %d " % (self.file_size,))
|
||||
|
||||
def elapsed_time(self):
|
||||
if not self.start_time:
|
||||
@@ -77,6 +84,7 @@ class dropboxUploader(object):
|
||||
return time.time() - self.start_time
|
||||
|
||||
def perform_transfer(self):
|
||||
log.debug("Starting transfer...")
|
||||
self.start_time = time.time()
|
||||
while self.uploader.offset < self.file_size:
|
||||
self.uploader.upload_chunked(self.file_size/100)
|
||||
@@ -94,9 +102,7 @@ class dropboxUploader(object):
|
||||
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
|
||||
else:
|
||||
progress["eta"] = 0
|
||||
info = event.event(event.EVT_OBJECT, 1)
|
||||
info.SetItem(progress)
|
||||
wx.PostEvent(self.wxDialog, info)
|
||||
pub.sendMessage("uploading", data=progress)
|
||||
|
||||
def perform_threaded(self):
|
||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
||||
@@ -104,10 +110,12 @@ class dropboxUploader(object):
|
||||
self.background_thread.start()
|
||||
|
||||
def transfer_completed(self):
|
||||
log.debug("Transfer completed")
|
||||
self.uploader.finish(os.path.basename(self.filename))
|
||||
if callable(self.completed_callback):
|
||||
self.completed_callback()
|
||||
|
||||
def get_url(self):
|
||||
original = "%s" % (self.client.share(os.path.basename(self.filename), False)["url"])
|
||||
return original.replace("dl=0", "dl=1")
|
||||
original = "%s" % (self.client.media(os.path.basename(self.filename))["url"])
|
||||
return original
|
||||
# .replace("dl=0", "dl=1")
|
@@ -1,191 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import output
|
||||
import tempfile
|
||||
import sound
|
||||
import os
|
||||
import config
|
||||
from mysc.thread_utils import call_threaded
|
||||
import sound_lib
|
||||
|
||||
class audioDialog(wx.Dialog):
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
wx.Dialog.__init__(self, None, -1, _(u"Attach audio"))
|
||||
self.file = None
|
||||
self.recorded = False
|
||||
self.recording = None
|
||||
self.playing = None
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.play = wx.Button(panel, -1, _(u"Play"))
|
||||
self.play.Bind(wx.EVT_BUTTON, self.onPlay)
|
||||
self.play.Disable()
|
||||
self.pause = wx.Button(panel, -1, _(u"Pause"))
|
||||
self.pause.Bind(wx.EVT_BUTTON, self.onPause)
|
||||
self.pause.Disable()
|
||||
self.record = wx.Button(panel, -1, _(u"Record"))
|
||||
self.record.Bind(wx.EVT_BUTTON, self.onRecord)
|
||||
self.record.SetFocus()
|
||||
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file"))
|
||||
self.attach_exists.Bind(wx.EVT_BUTTON, self.onAttach)
|
||||
self.discard = wx.Button(panel, -1, _(u"Discard"))
|
||||
self.discard.Bind(wx.EVT_BUTTON, self.onDiscard)
|
||||
self.discard.Disable()
|
||||
label = wx.StaticText(panel, -1, _(u"Upload to"))
|
||||
self.services = wx.ComboBox(panel, -1, choices=self.get_available_services(), value=self.get_available_services()[0], style=wx.CB_READONLY)
|
||||
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
servicesBox.Add(label)
|
||||
servicesBox.Add(self.services)
|
||||
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
|
||||
self.attach.Disable()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
sizer.Add(self.play)
|
||||
sizer.Add(self.pause)
|
||||
sizer.Add(self.record)
|
||||
sizer.Add(self.attach_exists)
|
||||
sizer.Add(self.discard)
|
||||
sizer.Add(self.attach)
|
||||
|
||||
def get_available_services(self):
|
||||
services = []
|
||||
if config.main["services"]["dropbox_token"] != "":
|
||||
services.append("Dropbox")
|
||||
services.append("TwUp")
|
||||
services.append("SNDUp")
|
||||
return services
|
||||
|
||||
def onPause(self, ev):
|
||||
if self.pause.GetLabel() == _(u"Pause"):
|
||||
self.recording.pause()
|
||||
self.pause.SetLabel(_(u"Resume"))
|
||||
elif self.pause.GetLabel() == _(u"Resume"):
|
||||
self.recording.play()
|
||||
self.pause.SetLabel(_(U"Pause"))
|
||||
|
||||
def onRecord(self, ev):
|
||||
if self.recording != None:
|
||||
self.stop_recording()
|
||||
self.pause.Disable()
|
||||
else:
|
||||
self.start_recording()
|
||||
self.pause.Enable()
|
||||
|
||||
def start_recording(self):
|
||||
self.attach_exists.Disable()
|
||||
self.file = tempfile.mktemp(suffix='.wav')
|
||||
self.recording = sound.recording(self.file)
|
||||
self.recording.play()
|
||||
self.record.SetLabel(_(u"Stop recording"))
|
||||
output.speak(_(u"Recording"))
|
||||
|
||||
def stop_recording(self):
|
||||
self.recording.stop()
|
||||
self.recording.free()
|
||||
output.speak(_(u"Stopped"))
|
||||
self.recorded = True
|
||||
self.record.SetLabel(_(u"Record"))
|
||||
self.file_attached()
|
||||
|
||||
def file_attached(self):
|
||||
self.pause.SetLabel(_(u"Pause"))
|
||||
self.record.Disable()
|
||||
self.play.Enable()
|
||||
self.discard.Enable()
|
||||
self.attach_exists.Disable()
|
||||
self.attach.Enable()
|
||||
self.play.SetFocus()
|
||||
|
||||
def onDiscard(self, evt):
|
||||
evt.Skip()
|
||||
if self.playing:
|
||||
self._stop()
|
||||
if self.recording != None:
|
||||
self.attach.Disable()
|
||||
self.play.Disable()
|
||||
self.file = None
|
||||
self.record.Enable()
|
||||
self.attach_exists.Enable()
|
||||
self.record.SetFocus()
|
||||
self.discard.Disable()
|
||||
self.recording = None
|
||||
output.speak(_(u"Discarded"))
|
||||
|
||||
def onPlay(self, evt):
|
||||
evt.Skip()
|
||||
if not self.playing:
|
||||
call_threaded(self._play)
|
||||
else:
|
||||
self._stop()
|
||||
|
||||
def _play(self):
|
||||
output.speak(_(u"Playing..."))
|
||||
# try:
|
||||
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
|
||||
self.playing.play()
|
||||
self.play.SetLabel(_(u"Stop"))
|
||||
try:
|
||||
while self.playing.is_playing:
|
||||
pass
|
||||
self.play.SetLabel(_(u"Play"))
|
||||
self.playing.free()
|
||||
self.playing = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def _stop(self):
|
||||
output.speak(_(u"Stopped"))
|
||||
self.playing.stop()
|
||||
self.playing.free()
|
||||
self.play.SetLabel(_(u"Play"))
|
||||
self.playing = None
|
||||
|
||||
def postprocess(self):
|
||||
if self.file.lower().endswith('.wav'):
|
||||
output.speak(_(u"Recoding audio..."))
|
||||
sound.recode_audio(self.file)
|
||||
self.wav_file = self.file
|
||||
self.file = '%s.ogg' % self.file[:-4]
|
||||
|
||||
def cleanup(self):
|
||||
if self.playing and self.playing.is_playing:
|
||||
self.playing.stop()
|
||||
if self.recording != None:
|
||||
if self.recording.is_playing:
|
||||
self.recording.stop()
|
||||
try:
|
||||
self.recording.free()
|
||||
except:
|
||||
pass
|
||||
os.remove(self.file)
|
||||
if hasattr(self, 'wav_file'):
|
||||
os.remove(self.wav_file)
|
||||
del(self.wav_file)
|
||||
if hasattr(self, 'wav_file') and os.path.exists(self.file):
|
||||
os.remove(self.file)
|
||||
|
||||
|
||||
def onAttach(self, ev):
|
||||
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||
return
|
||||
self.file = openFileDialog.GetPath()
|
||||
self.file_attached()
|
||||
ev.Skip()
|
@@ -4,17 +4,17 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
import json
|
||||
import wx
|
||||
from mysc import event
|
||||
import logging
|
||||
from utils import *
|
||||
from pubsub import pub
|
||||
|
||||
#__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
|
||||
|
||||
log = logging.getLogger("extra.AudioUploader.transfer")
|
||||
class Transfer(object):
|
||||
|
||||
def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, wxDialog=None, *args, **kwargs):
|
||||
def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, *args, **kwargs):
|
||||
self.url = url
|
||||
self.filename = filename
|
||||
log.debug("Uploading audio to %s, filename %s" % (url, filename))
|
||||
self.curl = pycurl.Curl()
|
||||
self.start_time = None
|
||||
self.completed_callback = completed_callback
|
||||
@@ -26,7 +26,6 @@ class Transfer(object):
|
||||
self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0)
|
||||
self.curl.setopt(self.curl.FOLLOWLOCATION, int(follow_location))
|
||||
self.curl.setopt(self.curl.VERBOSE, int(verbose))
|
||||
self.wxDialog = wxDialog
|
||||
super(Transfer, self).__init__(*args, **kwargs)
|
||||
|
||||
def elapsed_time(self):
|
||||
@@ -52,15 +51,15 @@ class Transfer(object):
|
||||
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
|
||||
else:
|
||||
progress["eta"] = 0
|
||||
info = event.event(event.EVT_OBJECT, 1)
|
||||
info.SetItem(progress)
|
||||
wx.PostEvent(self.wxDialog, info)
|
||||
pub.sendMessage("uploading", data=progress)
|
||||
|
||||
def perform_transfer(self):
|
||||
log.debug("starting upload...")
|
||||
self.start_time = time.time()
|
||||
self.curl.perform()
|
||||
self.curl.close()
|
||||
wx.CallAfter(self.complete_transfer)
|
||||
log.debug("Upload finished.")
|
||||
self.complete_transfer()
|
||||
|
||||
def perform_threaded(self):
|
||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
||||
|
@@ -1,14 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
from mysc import event
|
||||
from utils import *
|
||||
import widgetUtils
|
||||
|
||||
__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
|
||||
|
||||
class TransferDialog(wx.Dialog):
|
||||
class TransferDialog(widgetUtils.BaseDialog):
|
||||
|
||||
def __init__(self, filename, *args, **kwargs):
|
||||
super(TransferDialog, self).__init__(*args, **kwargs)
|
||||
super(TransferDialog, self).__init__(parent=None, id=wx.NewId(), *args, **kwargs)
|
||||
self.pane = wx.Panel(self)
|
||||
self.progress_bar = wx.Gauge(parent=self.pane)
|
||||
fileBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
@@ -37,7 +35,7 @@ class TransferDialog(wx.Dialog):
|
||||
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
|
||||
etaBox.Add(etaLabel)
|
||||
etaBox.Add(self.eta)
|
||||
# self.create_buttons()
|
||||
self.create_buttons()
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(fileBox)
|
||||
sizer.Add(currentAmountBox)
|
||||
@@ -46,10 +44,8 @@ class TransferDialog(wx.Dialog):
|
||||
sizer.Add(etaBox)
|
||||
sizer.Add(self.progress_bar)
|
||||
self.pane.SetSizerAndFit(sizer)
|
||||
self.Bind(event.MyEVT_OBJECT, self.update)
|
||||
|
||||
def update(self, ev):
|
||||
data = ev.GetItem()
|
||||
def update(self, data):
|
||||
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
|
||||
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
|
||||
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
|
||||
@@ -59,7 +55,12 @@ class TransferDialog(wx.Dialog):
|
||||
|
||||
def create_buttons(self):
|
||||
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
|
||||
self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel)
|
||||
|
||||
def get_response(self):
|
||||
self.Show()
|
||||
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
|
||||
class UploadDialog(TransferDialog):
|
||||
|
78
src/extra/AudioUploader/wx_ui.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import widgetUtils
|
||||
import output
|
||||
import logging
|
||||
log = logging.getLogger("extra.AudioUploader.wx_UI")
|
||||
|
||||
class audioDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, services):
|
||||
log.debug("creating audio dialog.")
|
||||
super(audioDialog, self).__init__(None, -1, _(u"Attach audio"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnSizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.play = wx.Button(panel, -1, _(u"Play"))
|
||||
self.play.Disable()
|
||||
self.pause = wx.Button(panel, -1, _(u"Pause"))
|
||||
self.pause.Disable()
|
||||
self.record = wx.Button(panel, -1, _(u"Record"))
|
||||
self.record.SetFocus()
|
||||
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file"))
|
||||
self.discard = wx.Button(panel, -1, _(u"Discard"))
|
||||
self.discard.Disable()
|
||||
label = wx.StaticText(panel, -1, _(u"Upload to"))
|
||||
self.services = wx.ComboBox(panel, -1, choices=services, value=services[0], style=wx.CB_READONLY)
|
||||
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
servicesBox.Add(label, 0, wx.ALL, 5)
|
||||
servicesBox.Add(self.services, 0, wx.ALL, 5)
|
||||
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
|
||||
self.attach.Disable()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
btnSizer.Add(self.play, 0, wx.ALL, 5)
|
||||
btnSizer.Add(self.pause, 0, wx.ALL, 5)
|
||||
btnSizer.Add(self.record, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.attach_exists, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.discard, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(self.attach, 0, wx.ALL, 5)
|
||||
btnSizer2.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(servicesBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnSizer, 0, wx.ALL, 5)
|
||||
sizer.Add(btnSizer2, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def enable_control(self, control):
|
||||
log.debug("Enabling control %s" % (control,))
|
||||
if hasattr(self, control):
|
||||
getattr(self, control).Enable()
|
||||
|
||||
def disable_control(self, control):
|
||||
log.debug("Disabling control %s" % (control,))
|
||||
if hasattr(self, control):
|
||||
getattr(self, control).Disable()
|
||||
|
||||
def get_file(self):
|
||||
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
return openFileDialog.GetPath()
|
@@ -1 +1 @@
|
||||
import gui
|
||||
from soundsTutorial import soundsTutorial
|
||||
|
25
src/extra/SoundsTutorial/gtk_ui.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
|
||||
class soundsTutorialDialog(Gtk.Dialog):
|
||||
def __init__(self, actions):
|
||||
super(soundsTutorialDialog, self).__init__("Sounds tutorial", None, 0, (Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
|
||||
box = self.get_content_area()
|
||||
label = Gtk.Label("Press enter for listen the sound")
|
||||
self.list = widgetUtils.list("Action")
|
||||
self.populate_actions(actions)
|
||||
lBox = Gtk.Box(spacing=6)
|
||||
lBox.add(label)
|
||||
lBox.add(self.list.list)
|
||||
box.add(lBox)
|
||||
self.play = Gtk.Button("Play")
|
||||
box.add(self.play)
|
||||
self.show_all()
|
||||
|
||||
def populate_actions(self, actions):
|
||||
for i in actions:
|
||||
self.list.insert_item(i)
|
||||
|
||||
def get_selected(self):
|
||||
return self.list.get_selected()
|
@@ -1,59 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import config
|
||||
import os
|
||||
import paths
|
||||
import sound
|
||||
|
||||
class soundsTutorial(wx.Dialog):
|
||||
def __init__(self):
|
||||
self.actions = [
|
||||
_(u"The tweet may contain a playable audio"),
|
||||
_(u"A timeline has been created"),
|
||||
_(u"A timeline has been deleted"),
|
||||
_(u"You've received a direct message"),
|
||||
_(u"You've sent a direct message"),
|
||||
_(u"A bug has happened"),
|
||||
_(u"You've added a tweet to your favourites"),
|
||||
_(u"Someone's favourites have been updated"),
|
||||
_(u"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()])
|
11
src/extra/SoundsTutorial/reverse_sort.py
Normal file
@@ -0,0 +1,11 @@
|
||||
#Reverse sort, by Bill Dengler <codeofdusk@gmail.com> for use in TWBlue http://twblue.es
|
||||
def invert_tuples(t):
|
||||
"Invert a list of tuples, so that the 0th element becomes the -1th, and the -1th becomes the 0th."
|
||||
res=[]
|
||||
for i in t:
|
||||
res.append(i[::-1])
|
||||
return res
|
||||
|
||||
def reverse_sort(t):
|
||||
"Sorts a list of tuples/lists by their last elements, not their first."
|
||||
return invert_tuples(sorted(invert_tuples(t)))
|
34
src/extra/SoundsTutorial/soundsTutorial.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
import widgetUtils
|
||||
import os
|
||||
import paths
|
||||
import logging
|
||||
log = logging.getLogger("extra.SoundsTutorial.soundsTutorial")
|
||||
import soundsTutorial_constants
|
||||
if platform.system() == "Windows":
|
||||
import wx_ui as UI
|
||||
elif platform.system() == "Linux":
|
||||
import gtk_ui as UI
|
||||
|
||||
class soundsTutorial(object):
|
||||
def __init__(self, sessionObject):
|
||||
log.debug("Creating sounds tutorial object...")
|
||||
super(soundsTutorial, self).__init__()
|
||||
self.session = sessionObject
|
||||
self.actions = []
|
||||
log.debug("Loading actions for sounds tutorial...")
|
||||
[self.actions.append(i[1]) for i in soundsTutorial_constants.actions]
|
||||
self.files = []
|
||||
log.debug("Searching sound files...")
|
||||
[self.files.append(i[0]) for i in soundsTutorial_constants.actions]
|
||||
log.debug("Creating dialog...")
|
||||
self.dialog = UI.soundsTutorialDialog(self.actions)
|
||||
widgetUtils.connect_event(self.dialog.play, widgetUtils.BUTTON_PRESSED, self.on_play)
|
||||
self.dialog.get_response()
|
||||
|
||||
def on_play(self, *args, **kwargs):
|
||||
try:
|
||||
self.session.sound.play(self.files[self.dialog.get_selection()]+".ogg")
|
||||
except:
|
||||
log.exception("Error playing the %s sound" % (self.files[self.dialog.items.GetSelection()],))
|
27
src/extra/SoundsTutorial/soundsTutorial_constants.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#-*- coding: utf-8 -*-
|
||||
import reverse_sort
|
||||
import application
|
||||
actions = reverse_sort.reverse_sort([ ("audio", _(u"Audio tweet.")),
|
||||
("create_timeline", _(u"User timeline buffer created.")),
|
||||
("delete_timeline", _(u"Buffer destroied.")),
|
||||
("dm_received", _(u"Direct message received.")),
|
||||
("dm_sent", _(u"Direct message sent.")),
|
||||
("error", _(u"Error.")),
|
||||
("favourite", _(u"Tweet favorited.")),
|
||||
("favourites_timeline_updated", _(u"Favourites buffer updated.")),
|
||||
("geo", _(u"Geotweet.")),
|
||||
("limit", _(u"Boundary reached.")),
|
||||
("list_tweet", _(u"List updated.")),
|
||||
("max_length", _(u"Too many characters.")),
|
||||
("mention_received", _(u"Mention received.")),
|
||||
("new_event", _(u"New event.")),
|
||||
("ready", _(unicode(application.name+" is ready."))),
|
||||
("reply_send", _(u"Mention sent.")),
|
||||
("retweet_send", _(u"Tweet retweeted.")),
|
||||
("search_updated", _(u"Search buffer updated.")),
|
||||
("tweet_received", _(u"Tweet received.")),
|
||||
("tweet_send", _(u"Tweet sent.")),
|
||||
("trends_updated", _(u"Trending topics buffer updated.")),
|
||||
("tweet_timeline", _(u"New tweet in user timeline buffer.")),
|
||||
("update_followers", _(u"New follower.")),
|
||||
("volume_changed", _(u"Volume changed."))])
|
29
src/extra/SoundsTutorial/wx_ui.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import widgetUtils
|
||||
|
||||
class soundsTutorialDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self, actions):
|
||||
super(soundsTutorialDialog, self).__init__(None, -1)
|
||||
self.SetTitle(_(u"Sounds tutorial"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
|
||||
self.items = wx.ListBox(panel, 1, choices=actions, style=wx.LB_SINGLE)
|
||||
self.items.SetSelection(0)
|
||||
listBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listBox.Add(label)
|
||||
listBox.Add(self.items)
|
||||
self.play = wx.Button(panel, 1, (u"Play"))
|
||||
self.play.SetDefault()
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.play)
|
||||
btnBox.Add(close)
|
||||
sizer.Add(listBox)
|
||||
sizer.Add(btnBox)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def get_selection(self):
|
||||
return self.items.GetSelection()
|
@@ -0,0 +1,4 @@
|
||||
import spellchecker
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
from wx_ui import *
|
@@ -1,105 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import output
|
||||
import config
|
||||
import languageHandler
|
||||
from enchant.checker import SpellChecker
|
||||
from enchant.errors import DictNotFoundError
|
||||
|
||||
class spellCheckerDialog(wx.Dialog):
|
||||
def __init__(self, text, dictionary):
|
||||
super(spellCheckerDialog, self).__init__(None, 1)
|
||||
try:
|
||||
if config.main["general"]["language"] == "system": self.checker = SpellChecker()
|
||||
else: self.checker = SpellChecker(languageHandler.getLanguage())
|
||||
self.checker.set_text(text)
|
||||
except DictNotFoundError:
|
||||
wx.MessageDialog(None, _(u"A bug has happened. There are no dictionaries available for the selected language in TW Blue"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
self.Destroy()
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
word = wx.StaticText(panel, -1, _(u"Mis-spelled word"))
|
||||
self.word = wx.TextCtrl(panel, -1)
|
||||
wordBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
wordBox.Add(word)
|
||||
wordBox.Add(self.word)
|
||||
context = wx.StaticText(panel, -1, _(u"Context"))
|
||||
self.context = wx.TextCtrl(panel, -1)
|
||||
contextBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
contextBox.Add(context)
|
||||
contextBox.Add(self.context)
|
||||
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
|
||||
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
|
||||
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
suggestionsBox.Add(suggest)
|
||||
suggestionsBox.Add(self.suggestions)
|
||||
ignore = wx.Button(panel, -1, _(u"Ignore"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onIgnore, ignore)
|
||||
ignoreAll = wx.Button(panel, -1, _(u"Ignore all"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onIgnoreAll, ignoreAll)
|
||||
replace = wx.Button(panel, -1, _(u"Replace"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onReplace, replace)
|
||||
replaceAll = wx.Button(panel, -1, _(u"Replace all"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onReplaceAll, replaceAll)
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(ignore)
|
||||
btnBox.Add(ignoreAll)
|
||||
btnBox.Add(replace)
|
||||
btnBox.Add(replaceAll)
|
||||
btnBox.Add(close)
|
||||
sizer.Add(wordBox)
|
||||
sizer.Add(contextBox)
|
||||
sizer.Add(suggestionsBox)
|
||||
sizer.Add(btnBox)
|
||||
panel.SetSizerAndFit(sizer)
|
||||
self.check()
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
self.checker.next()
|
||||
textToSay = _(u"Mis-spelled word: %s") % (self.checker.word,)
|
||||
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
|
||||
self.SetTitle(textToSay)
|
||||
output.speak(textToSay)
|
||||
self.word.SetValue(self.checker.word)
|
||||
self.context.ChangeValue(context)
|
||||
self.suggestions.Set(self.checker.suggest())
|
||||
self.suggestions.SetFocus()
|
||||
except StopIteration:
|
||||
wx.MessageDialog(self, _(u"The spelling review has finished."), _("Finished"), style=wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_OK)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def onIgnore(self, ev):
|
||||
self.check()
|
||||
|
||||
def onIgnoreAll(self, ev):
|
||||
self.checker.ignore_always(word=self.checker.word)
|
||||
self.check()
|
||||
|
||||
def onReplace(self, ev):
|
||||
self.checker.replace(self.suggestions.GetStringSelection())
|
||||
self.check()
|
||||
|
||||
def onReplaceAll(self, ev):
|
||||
self.checker.replace_always(self.suggestions.GetStringSelection())
|
||||
self.check()
|
70
src/extra/SpellChecker/spellchecker.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
log = logging.getLogger("extra.SpellChecker.spellChecker")
|
||||
import wx_ui
|
||||
import widgetUtils
|
||||
import output
|
||||
import config
|
||||
import languageHandler
|
||||
from enchant.checker import SpellChecker
|
||||
from enchant.errors import DictNotFoundError
|
||||
from enchant import tokenize
|
||||
import twitterFilter
|
||||
|
||||
class spellChecker(object):
|
||||
def __init__(self, text, dictionary):
|
||||
super(spellChecker, self).__init__()
|
||||
log.debug("Creating the SpellChecker object. Dictionary: %s" % (dictionary,))
|
||||
self.active = True
|
||||
try:
|
||||
if config.app["app-settings"]["language"] == "system":
|
||||
log.debug("Using the system language")
|
||||
self.checker = SpellChecker(filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter])
|
||||
else:
|
||||
log.debug("Using language: %s" % (languageHandler.getLanguage(),))
|
||||
self.checker = SpellChecker(languageHandler.getLanguage(), filters=[twitterFilter.TwitterFilter, tokenize.EmailFilter, tokenize.URLFilter])
|
||||
self.checker.set_text(text)
|
||||
except DictNotFoundError:
|
||||
log.exception("Dictionary for language %s not found." % (dictionary,))
|
||||
wx_ui.dict_not_found_error()
|
||||
self.active = False
|
||||
if self.active == True:
|
||||
log.debug("Creating dialog...")
|
||||
self.dialog = wx_ui.spellCheckerDialog()
|
||||
widgetUtils.connect_event(self.dialog.ignore, widgetUtils.BUTTON_PRESSED, self.ignore)
|
||||
widgetUtils.connect_event(self.dialog.ignoreAll, widgetUtils.BUTTON_PRESSED, self.ignoreAll)
|
||||
widgetUtils.connect_event(self.dialog.replace, widgetUtils.BUTTON_PRESSED, self.replace)
|
||||
widgetUtils.connect_event(self.dialog.replaceAll, widgetUtils.BUTTON_PRESSED, self.replaceAll)
|
||||
self.check()
|
||||
self.dialog.get_response()
|
||||
self.fixed_text = self.checker.get_text()
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
self.checker.next()
|
||||
textToSay = _(u"Misspelled word: %s") % (self.checker.word,)
|
||||
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
|
||||
self.dialog.set_title(textToSay)
|
||||
output.speak(textToSay)
|
||||
self.dialog.set_word_and_suggestions(word=self.checker.word, context=context, suggestions=self.checker.suggest())
|
||||
except StopIteration:
|
||||
log.debug("Process finished.")
|
||||
wx_ui.finished()
|
||||
self.dialog.Destroy()
|
||||
# except AttributeError:
|
||||
# pass
|
||||
|
||||
def ignore(self, ev):
|
||||
self.check()
|
||||
|
||||
def ignoreAll(self, ev):
|
||||
self.checker.ignore_always(word=self.checker.word)
|
||||
self.check()
|
||||
|
||||
def replace(self, ev):
|
||||
self.checker.replace(self.dialog.get_selected_suggestion())
|
||||
self.check()
|
||||
|
||||
def replaceAll(self, ev):
|
||||
self.checker.replace_always(self.dialog.get_selected_suggestion())
|
||||
self.check()
|
15
src/extra/SpellChecker/twitterFilter.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from enchant.tokenize import Filter
|
||||
|
||||
class TwitterFilter(Filter):
|
||||
"""Filter skipping over twitter usernames and hashtags.
|
||||
This filter skips any words matching the following regular expression:
|
||||
^[#@](\S){1, }$
|
||||
That is, any words that resemble users and hashtags.
|
||||
"""
|
||||
_pattern = re.compile(r"^[#@](\S){1,}$")
|
||||
def _skip(self,word):
|
||||
if self._pattern.match(word):
|
||||
return True
|
||||
return False
|
79
src/extra/SpellChecker/wx_ui.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import application
|
||||
class spellCheckerDialog(wx.Dialog):
|
||||
def __init__(self):
|
||||
super(spellCheckerDialog, self).__init__(None, 1)
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
word = wx.StaticText(panel, -1, _(u"Misspelled word"))
|
||||
self.word = wx.TextCtrl(panel, -1)
|
||||
wordBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
wordBox.Add(word, 0, wx.ALL, 5)
|
||||
wordBox.Add(self.word, 0, wx.ALL, 5)
|
||||
context = wx.StaticText(panel, -1, _(u"Context"))
|
||||
self.context = wx.TextCtrl(panel, -1)
|
||||
contextBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
contextBox.Add(context, 0, wx.ALL, 5)
|
||||
contextBox.Add(self.context, 0, wx.ALL, 5)
|
||||
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
|
||||
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
|
||||
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
suggestionsBox.Add(suggest, 0, wx.ALL, 5)
|
||||
suggestionsBox.Add(self.suggestions, 0, wx.ALL, 5)
|
||||
self.ignore = wx.Button(panel, -1, _(u"Ignore"))
|
||||
self.ignoreAll = wx.Button(panel, -1, _(u"Ignore all"))
|
||||
self.replace = wx.Button(panel, -1, _(u"Replace"))
|
||||
self.replaceAll = wx.Button(panel, -1, _(u"Replace all"))
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ignore, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.ignoreAll, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.replace, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.replaceAll, 0, wx.ALL, 5)
|
||||
btnBox.Add(close, 0, wx.ALL, 5)
|
||||
sizer.Add(wordBox, 0, wx.ALL, 5)
|
||||
sizer.Add(contextBox, 0, wx.ALL, 5)
|
||||
sizer.Add(suggestionsBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
|
||||
def get_response(self):
|
||||
return self.ShowModal()
|
||||
|
||||
def set_title(self, title):
|
||||
return self.SetTitle(title)
|
||||
|
||||
def set_word_and_suggestions(self, word, context, suggestions):
|
||||
self.word.SetValue(word)
|
||||
self.context.ChangeValue(context)
|
||||
self.suggestions.Set(suggestions)
|
||||
self.suggestions.SetFocus()
|
||||
|
||||
def get_selected_suggestion(self):
|
||||
return self.suggestions.GetStringSelection()
|
||||
|
||||
def dict_not_found_error():
|
||||
wx.MessageDialog(None, _(u"An error has occurred. There are no dictionaries available for the selected language in " + application.name), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def finished():
|
||||
wx.MessageDialog(None, _(u"Spell check complete."), _(application.name), style=wx.OK).ShowModal()
|
2
src/extra/autocompletionUsers/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import completion, settings
|
47
src/extra/autocompletionUsers/completion.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import output
|
||||
import storage
|
||||
import wx_menu
|
||||
|
||||
class autocompletionUsers(object):
|
||||
def __init__(self, window, session_id):
|
||||
super(autocompletionUsers, self).__init__()
|
||||
self.window = window
|
||||
self.db = storage.storage(session_id)
|
||||
|
||||
def show_menu(self, mode="tweet"):
|
||||
position = self.window.get_position()
|
||||
if mode == "tweet":
|
||||
text = self.window.get_text()
|
||||
text = text[:position]
|
||||
try:
|
||||
pattern = text.split()[-1]
|
||||
except IndexError:
|
||||
output.speak(_(u"You have to start writing"))
|
||||
return
|
||||
if pattern.startswith("@") == True:
|
||||
menu = wx_menu.menu(self.window.text, pattern[1:], mode=mode)
|
||||
users = self.db.get_users(pattern[1:])
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.popup_menu(menu)
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
||||
else:
|
||||
output.speak(_(u"Autocompletion only works for users."))
|
||||
elif mode == "dm":
|
||||
text = self.window.get_user()
|
||||
try:
|
||||
pattern = text.split()[-1]
|
||||
except IndexError:
|
||||
output.speak(_(u"You have to start writing"))
|
||||
return
|
||||
menu = wx_menu.menu(self.window.cb, pattern, mode=mode)
|
||||
users = self.db.get_users(pattern)
|
||||
if len(users) > 0:
|
||||
menu.append_options(users)
|
||||
self.window.popup_menu(menu)
|
||||
menu.destroy()
|
||||
else:
|
||||
output.speak(_(u"There are no results in your users database"))
|
43
src/extra/autocompletionUsers/manage.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import storage
|
||||
import widgetUtils
|
||||
import wx_manage
|
||||
from wxUI import commonMessageDialogs
|
||||
|
||||
class autocompletionManage(object):
|
||||
def __init__(self, session):
|
||||
super(autocompletionManage, self).__init__()
|
||||
self.session = session
|
||||
self.dialog = wx_manage.autocompletionManageDialog()
|
||||
self.database = storage.storage(self.session.session_id)
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
||||
widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_user)
|
||||
self.dialog.get_response()
|
||||
|
||||
def update_list(self):
|
||||
item = self.dialog.users.get_selected()
|
||||
self.dialog.users.clear()
|
||||
self.users = self.database.get_all_users()
|
||||
self.dialog.put_users(self.users)
|
||||
self.dialog.users.select_item(item)
|
||||
|
||||
def add_user(self, *args, **kwargs):
|
||||
usr = self.dialog.get_user()
|
||||
if usr == False:
|
||||
return
|
||||
try:
|
||||
data = self.session.twitter.twitter.show_user(screen_name=usr)
|
||||
except:
|
||||
self.dialog.show_invalid_user_error()
|
||||
return
|
||||
self.database.set_user(data["screen_name"], data["name"], 0)
|
||||
self.update_list()
|
||||
|
||||
def remove_user(self, ev):
|
||||
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
||||
item = self.dialog.users.get_selected()
|
||||
user = self.users[item]
|
||||
self.database.remove_user(user[0])
|
||||
self.update_list()
|
59
src/extra/autocompletionUsers/settings.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import storage
|
||||
import widgetUtils
|
||||
import wx_settings
|
||||
import manage
|
||||
import output
|
||||
from mysc.thread_utils import call_threaded
|
||||
|
||||
class autocompletionSettings(object):
|
||||
def __init__(self, config, buffer, window):
|
||||
super(autocompletionSettings, self).__init__()
|
||||
self.config = config
|
||||
self.buffer = buffer
|
||||
self.window = window
|
||||
self.dialog = wx_settings.autocompletionSettingsDialog()
|
||||
self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
||||
self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
||||
widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list)
|
||||
if self.dialog.get_response() == widgetUtils.OK:
|
||||
call_threaded(self.add_users_to_database)
|
||||
|
||||
def add_users_to_database(self):
|
||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer")
|
||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer")
|
||||
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||
database = storage.storage(self.buffer.session.session_id)
|
||||
if self.dialog.get("followers_buffer") == True:
|
||||
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]["items"]:
|
||||
database.set_user(i["screen_name"], i["name"], 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if self.dialog.get("friends_buffer") == True:
|
||||
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]["items"]:
|
||||
database.set_user(i["screen_name"], i["name"], 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
||||
wx_settings.show_success_dialog()
|
||||
self.dialog.destroy()
|
||||
|
||||
def view_list(self, ev):
|
||||
q = manage.autocompletionManage(self.buffer.session)
|
||||
|
||||
|
||||
def execute_at_startup(window, buffer, config):
|
||||
database = storage.storage(buffer.session.session_id)
|
||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
||||
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i["screen_name"], i["name"], 1)
|
||||
else:
|
||||
database.remove_by_buffer(1)
|
||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
||||
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
||||
for i in buffer.session.db[buffer.name]:
|
||||
database.set_user(i["screen_name"], i["name"], 2)
|
||||
else:
|
||||
database.remove_by_buffer(2)
|
52
src/extra/autocompletionUsers/storage.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sqlite3, paths
|
||||
|
||||
class storage(object):
|
||||
def __init__(self, session_id):
|
||||
self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (session_id)))
|
||||
self.cursor = self.connection.cursor()
|
||||
if self.table_exist("users") == False:
|
||||
self.create_table()
|
||||
|
||||
def table_exist(self, table):
|
||||
ask = self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % (table))
|
||||
answer = ask.fetchone()
|
||||
if answer == None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_all_users(self):
|
||||
self.cursor.execute("""select * from users""")
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def get_users(self, term):
|
||||
self.cursor.execute("""SELECT * FROM users WHERE user LIKE ?""", ('{}%'.format(term),))
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def set_user(self, screen_name, user_name, from_a_buffer):
|
||||
self.cursor.execute("""insert or ignore into users values(?, ?, ?)""", (screen_name, user_name, from_a_buffer))
|
||||
self.connection.commit()
|
||||
|
||||
def remove_user(self, user):
|
||||
self.cursor.execute("""DELETE FROM users WHERE user = ?""", (user,))
|
||||
self.connection.commit()
|
||||
return self.cursor.fetchone()
|
||||
|
||||
def remove_by_buffer(self, bufferType):
|
||||
""" Removes all users saved on a buffer. BufferType is 0 for no buffer, 1 for friends and 2 for followers"""
|
||||
self.cursor.execute("""DELETE FROM users WHERE from_a_buffer = ?""", (bufferType,))
|
||||
self.connection.commit()
|
||||
return self.cursor.fetchone()
|
||||
|
||||
def create_table(self):
|
||||
self.cursor.execute("""
|
||||
create table users(
|
||||
user TEXT UNIQUE,
|
||||
name TEXT,
|
||||
from_a_buffer INTEGER
|
||||
)""")
|
||||
|
||||
def __del__(self):
|
||||
self.cursor.close()
|
||||
self.connection.close()
|
43
src/extra/autocompletionUsers/wx_manage.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import widgetUtils
|
||||
from multiplatform_widgets import widgets
|
||||
import application
|
||||
class autocompletionManageDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocompletion database"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Editing " + application.name + " users database"))
|
||||
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
|
||||
sizer.Add(label, 0, wx.ALL, 5)
|
||||
sizer.Add(self.users.list, 0, wx.ALL, 5)
|
||||
self.add = wx.Button(panel, -1, _(u"Add user"))
|
||||
self.remove = wx.Button(panel, -1, _(u"Remove user"))
|
||||
optionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
optionsBox.Add(self.add, 0, wx.ALL, 5)
|
||||
optionsBox.Add(self.remove, 0, wx.ALL, 5)
|
||||
sizer.Add(optionsBox, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def put_users(self, users):
|
||||
for i in users:
|
||||
j = [i[0], i[1]]
|
||||
self.users.insert_item(False, *j)
|
||||
|
||||
def get_user(self):
|
||||
usr = False
|
||||
userDlg = wx.TextEntryDialog(None, _(u"Twitter username"), _(u"Add user to database"))
|
||||
if userDlg.ShowModal() == wx.ID_OK:
|
||||
usr = userDlg.GetValue()
|
||||
return usr
|
||||
|
||||
def show_invalid_user_error(self):
|
||||
wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
25
src/extra/autocompletionUsers/wx_menu.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
|
||||
class menu(wx.Menu):
|
||||
def __init__(self, window, pattern, mode):
|
||||
super(menu, self).__init__()
|
||||
self.window = window
|
||||
self.pattern = pattern
|
||||
self.mode = mode
|
||||
|
||||
def append_options(self, options):
|
||||
for i in options:
|
||||
item = wx.MenuItem(self, wx.NewId(), "%s (@%s)" % (i[1], i[0]))
|
||||
self.AppendItem(item)
|
||||
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
||||
|
||||
def select_text(self, ev, text):
|
||||
if self.mode == "tweet":
|
||||
self.window.ChangeValue(self.window.GetValue().replace("@"+self.pattern, "@"+text+" "))
|
||||
elif self.mode == "dm":
|
||||
self.window.SetValue(self.window.GetValue().replace(self.pattern, text))
|
||||
self.window.SetInsertionPointEnd()
|
||||
|
||||
def destroy(self):
|
||||
self.Destroy()
|
27
src/extra/autocompletionUsers/wx_settings.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import widgetUtils
|
||||
import application
|
||||
|
||||
class autocompletionSettingsDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users’ settings"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer"))
|
||||
self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer"))
|
||||
sizer.Add(self.followers_buffer, 0, wx.ALL, 5)
|
||||
sizer.Add(self.friends_buffer, 0, wx.ALL, 5)
|
||||
self.viewList = wx.Button(panel, -1, _(u"Manage database..."))
|
||||
sizer.Add(self.viewList, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def show_success_dialog():
|
||||
wx.MessageDialog(None, _(uapplication.name+"'s database of users has been updated."), _(u"Done"), wx.OK).ShowModal()
|
@@ -1,3 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from translator import *
|
||||
import gui
|
||||
import translator
|
||||
import platform
|
||||
if platform.system() == "Windows":
|
||||
import wx_ui as gui
|
||||
|
@@ -1,53 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
try:
|
||||
import urllib2 as request
|
||||
from urllib import quote
|
||||
except:
|
||||
from urllib import request
|
||||
from urllib.parse import quote
|
||||
#!/usr/bin/env python
|
||||
import goslate
|
||||
|
||||
class Translator:
|
||||
string_pattern = r"\"(([^\"\\]|\\.)*)\""
|
||||
match_string =re.compile(
|
||||
r"\,?\["
|
||||
+ string_pattern + r"\,"
|
||||
+ string_pattern + r"\,"
|
||||
+ string_pattern + r"\,"
|
||||
+ string_pattern
|
||||
+r"\]")
|
||||
|
||||
def __init__(self):
|
||||
self.from_lang = ""
|
||||
self.to_lang = ""
|
||||
|
||||
def translate(self, source):
|
||||
json5 = self._get_json5_from_google(source)
|
||||
return self._unescape(self._get_translation_from_json5(json5))
|
||||
|
||||
def _get_translation_from_json5(self, content):
|
||||
result = ""
|
||||
pos = 2
|
||||
while True:
|
||||
m = self.match_string.match(content, pos)
|
||||
if not m:
|
||||
break
|
||||
result += m.group(1)
|
||||
pos = m.end()
|
||||
return result
|
||||
|
||||
def _get_json5_from_google(self, source):
|
||||
escaped_source = quote(source, '')
|
||||
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19'}
|
||||
req = request.Request(
|
||||
url="http://translate.google.com/translate_a/t?client=t&ie=UTF-8&oe=UTF-8"
|
||||
+"&sl=%s&tl=%s&text=%s" % (self.from_lang, self.to_lang, escaped_source)
|
||||
, headers = headers)
|
||||
r = request.urlopen(req)
|
||||
return r.read().decode('utf-8')
|
||||
|
||||
def _unescape(self, text):
|
||||
return re.sub(r"\\.?", lambda x:eval('"%s"'%x.group(0)), text)
|
||||
def translate(text, source_lang, target_lang):
|
||||
gs = goslate.Goslate()
|
||||
return gs.translate(text, target_lang, source_lang)
|
||||
|
||||
languages = {
|
||||
"af": _(u"Afrikaans"),
|
||||
|
@@ -16,12 +16,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import translator
|
||||
import wx
|
||||
from wxUI.dialogs import baseDialog
|
||||
|
||||
class translateDialog(wx.Dialog):
|
||||
class translateDialog(baseDialog.BaseWXDialog):
|
||||
def __init__(self):
|
||||
wx.Dialog.__init__(self, None, -1, title=_(u"Translate message"))
|
||||
super(translateDialog, self).__init__(None, -1, title=_(u"Translate message"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
staticSource = wx.StaticText(panel, -1, _(u"Source language"))
|
||||
@@ -40,5 +41,5 @@ class translateDialog(wx.Dialog):
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
self.SetEscapeId(wx.ID_CANCEL)
|
||||
|
||||
def onOk(self, ev):
|
||||
self.EndModal(wx.ID_OK)
|
||||
def get(self, control):
|
||||
return getattr(self, control).GetSelection()
|
6
src/fixes/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" This module contains some bugfixes for packages used in TWBlue."""
|
||||
import fix_arrow # A few new locales for Three languages in arrow.
|
||||
|
||||
def setup():
|
||||
fix_arrow.fix()
|
91
src/fixes/fix_arrow.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from arrow import locales
|
||||
from arrow.locales import Locale
|
||||
|
||||
def fix():
|
||||
''' This function adds the Catala, Basque and galician locales to the list of locales supported in Arrow.
|
||||
it also fixes capitalizations in names from turkish and arabian locales.
|
||||
see https://github.com/crsmithdev/arrow/pull/207 for following the pull request.'''
|
||||
locales.CatalaLocale = CatalaLocale
|
||||
locales.GalicianLocale = GalicianLocale
|
||||
locales.BasqueLocale = BasqueLocale
|
||||
locales.TurkishLocale.names[-1] = "tr_tr"
|
||||
locales.ArabicLocale.names[-1] = "ar_eg"
|
||||
# We need to reassign the locales list for updating the list with our new contents.
|
||||
locales._locales = locales._map_locales()
|
||||
|
||||
class CatalaLocale(Locale):
|
||||
names = ['ca', 'ca_ca']
|
||||
past = 'Fa {0}'
|
||||
future = '{0}' # I don't know what's the right phrase in catala for the future.
|
||||
|
||||
timeframes = {
|
||||
'now': 'Ara mateix',
|
||||
'seconds': 'segons',
|
||||
'minute': '1 minut',
|
||||
'minutes': '{0} minuts',
|
||||
'hour': 'una hora',
|
||||
'hours': '{0} hores',
|
||||
'day': 'un dia',
|
||||
'days': '{0} dies',
|
||||
'month': 'un mes',
|
||||
'months': '{0} messos',
|
||||
'year': 'un any',
|
||||
'years': '{0} anys',
|
||||
}
|
||||
|
||||
month_names = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
|
||||
month_abbreviations = ['', 'Jener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Decembre']
|
||||
day_names = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
|
||||
day_abbreviations = ['', 'Dilluns', 'Dimars', 'Dimecres', 'Dijous', 'Divendres', 'Disabte', 'Diumenge']
|
||||
|
||||
class GalicianLocale(Locale):
|
||||
names = ['gl', 'gl_gl']
|
||||
past = 'Fai {0}'
|
||||
future = '{0}' # I don't know what's the right phrase in Galician for the future.
|
||||
|
||||
timeframes = {
|
||||
'now': 'Agora mesmo',
|
||||
'seconds': 'segundos',
|
||||
'minute': 'un minuto',
|
||||
'minutes': '{0} minutos',
|
||||
'hour': 'una hora',
|
||||
'hours': '{0} horas',
|
||||
'day': 'un día',
|
||||
'days': '{0} días',
|
||||
'month': 'un mes',
|
||||
'months': '{0} meses',
|
||||
'year': 'un ano',
|
||||
'years': '{0} anos',
|
||||
}
|
||||
|
||||
month_names = ['', 'Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro']
|
||||
month_abbreviations = ['', 'Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xun', 'Xul', 'Ago', 'Set', 'Out', 'Nov', 'Dec']
|
||||
day_names = ['', 'Luns', 'Martes', 'Mércores', 'Xoves', 'Venres', 'Sábado', 'Domingo']
|
||||
day_abbreviations = ['', 'Lun', 'Mar', 'Mer', 'xov', 'Ven' 'Sab', 'Dom']
|
||||
|
||||
class BasqueLocale(Locale):
|
||||
names = ['eu', 'eu_eu']
|
||||
past = 'duela {0}'
|
||||
future = '{0}' # I don't know what's the right phrase in Basque for the future.
|
||||
|
||||
timeframes = {
|
||||
'now': 'Orain',
|
||||
'seconds': 'segundu',
|
||||
'minute': 'minutu bat',
|
||||
'minutes': '{0} minutu',
|
||||
'hour': 'ordu bat',
|
||||
'hours': '{0} ordu',
|
||||
'day': 'egun bat',
|
||||
'days': '{0} egun',
|
||||
'month': 'hilabete bat',
|
||||
'months': '{0} hilabete',
|
||||
'year': 'urte bat',
|
||||
'years': '{0} urte',
|
||||
}
|
||||
|
||||
month_names = ['', 'Urtarrilak', 'Otsailak', 'Martxoak', 'Apirilak', 'Maiatzak', 'Ekainak', 'Uztailak', 'Abuztuak', 'Irailak', 'Urriak', 'Azaroak', 'Abenduak']
|
||||
month_abbreviations = ['', 'urt', 'ots', 'mar', 'api', 'mai', 'eka', 'uzt', 'abu', 'ira', 'urr', 'aza', 'abe']
|
||||
day_names = ['', 'Asteleehna', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata', 'Igandea']
|
||||
day_abbreviations = ['', 'al', 'ar', 'az', 'og', 'ol', 'lr', 'ig']
|
||||
|
@@ -1,105 +0,0 @@
|
||||
# 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
|
11
src/gtkUI/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" This is the GTK view module for TWBlue.
|
||||
As of April 3 2015, there are the things that have been implemented:
|
||||
* the main view (partially implemented)
|
||||
* All buffers.
|
||||
* Three of the most common message dialogs.
|
||||
* Dialogs for tweet, reply, retweet, send a direct message and view a tweet.
|
||||
|
||||
And we need to implement:
|
||||
* All the other dialogs.
|
||||
"""
|
11
src/gtkUI/buffers/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from base import basePanel
|
||||
from dm import dmPanel
|
||||
from events import eventsPanel
|
||||
from favourites import favsPanel
|
||||
from lists import listPanel
|
||||
from panels import accountPanel, emptyPanel
|
||||
from people import peoplePanel
|
||||
from trends import trendsPanel
|
||||
from tweet_searches import searchPanel
|
||||
from user_searches import searchUsersPanel
|
35
src/gtkUI/buffers/base.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import widgetUtils
|
||||
from gi.repository import Gtk
|
||||
|
||||
class basePanel(Gtk.VBox):
|
||||
|
||||
def create_list(self):
|
||||
self.list = widgetUtils.list(_(u"User"), _(u"Text"), _(u"Date"), _(u"Client"))
|
||||
|
||||
def __init__(self, parent, name):
|
||||
super(basePanel, self).__init__(spacing=6)
|
||||
self.name = name
|
||||
self.type = "baseBuffer"
|
||||
self.create_list()
|
||||
self.tweet = Gtk.Button(_(u"Tweet"))
|
||||
self.retweet = Gtk.Button(_(u"Retweet"))
|
||||
self.reply = Gtk.Button(_(u"Reply"))
|
||||
self.dm = Gtk.Button(_(u"Direct message"))
|
||||
btnSizer = Gtk.Box(spacing=6)
|
||||
btnSizer.add(self.tweet)
|
||||
btnSizer.add(self.retweet)
|
||||
btnSizer.add(self.reply)
|
||||
btnSizer.add(self.dm)
|
||||
self.add(self.list.list)
|
||||
self.add(btnSizer)
|
||||
|
||||
def set_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.list.select_item(self.list.get_count()-1)
|
||||
else:
|
||||
self.list.select_item(0)
|
||||
|
||||
def set_focus_function(self, f):
|
||||
tree_selection = self.list.list.get_selection()
|
||||
tree_selection.connect("changed", f)
|
14
src/gtkUI/buffers/dm.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
from base import basePanel
|
||||
|
||||
class dmPanel(basePanel):
|
||||
def __init__(self, parent, name):
|
||||
""" Class to DM'S. Reply and retweet buttons are not showed and they have your delete method for dm's."""
|
||||
super(dmPanel, self).__init__(parent, name)
|
||||
self.retweet.hide()
|
||||
self.retweet.set_no_show_all(True)
|
||||
self.reply.hide()
|
||||
self.reply.set_no_show_all(True)
|
||||
self.type = "dm"
|
25
src/gtkUI/buffers/events.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import widgetUtils
|
||||
from gi.repository import Gtk
|
||||
|
||||
class eventsPanel(Gtk.VBox):
|
||||
""" Buffer to show events. Different than tweets or people."""
|
||||
|
||||
def __init__(self, parent, name):
|
||||
self.type = "event"
|
||||
super(eventsPanel, self).__init__(spacing=6)
|
||||
self.name = name
|
||||
self.list = widgetUtils.list(_(u"Date"), _(u"Event"))
|
||||
self.add(self.list.list)
|
||||
self.tweet = Gtk.Button(_(u"Tweet"))
|
||||
self.delete_event = Gtk.Button(_(u"Remove event"))
|
||||
btnBox = Gtk.Box(spacing=6)
|
||||
btnBox.add(self.tweet)
|
||||
btnBox.add(self.delete_event)
|
||||
self.add(btnBox)
|
||||
|
||||
def set_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.list.select_item(self.list.get_count()-1)
|
||||
else:
|
||||
self.list.select_item(0)
|
8
src/gtkUI/buffers/favourites.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
from base import basePanel
|
||||
|
||||
class favsPanel(basePanel):
|
||||
def __init__(self, parent, name):
|
||||
super(favsPanel, self).__init__(parent, name)
|
||||
self.type = "favourites_timeline"
|
9
src/gtkUI/buffers/lists.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
from base import basePanel
|
||||
|
||||
class listPanel(basePanel):
|
||||
def __init__(self, parent, name):
|
||||
super(listPanel, self).__init__(parent, name)
|
||||
self.type = "list"
|
||||
self.users = []
|
34
src/gtkUI/buffers/panels.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
|
||||
class accountPanel(Gtk.VBox):
|
||||
def __init__(self, parent, name=None):
|
||||
super(accountPanel, self).__init__(spacing=5)
|
||||
self.name = name
|
||||
self.type = "account"
|
||||
self.login = Gtk.Button(_(u"Login"))
|
||||
self.add(self.login)
|
||||
self.autostart_account = Gtk.ToggleButton(_(u"Log in automatically"))
|
||||
self.add(self.autostart_account)
|
||||
|
||||
def change_login(self, login=True):
|
||||
if login == True:
|
||||
self.login.set_label(_(u"Login"))
|
||||
else:
|
||||
self.login.set_label(_(u"Logout"))
|
||||
|
||||
def change_autostart(self, autostart=True):
|
||||
self.autostart_account.set_active(autostart)
|
||||
|
||||
def get_autostart(self):
|
||||
print "actived"
|
||||
print self.autostart_account.get_active()
|
||||
return self.autostart_account.get_active()
|
||||
|
||||
class emptyPanel(Gtk.VBox):
|
||||
def __init__(self, parent, name):
|
||||
super(emptyPanel, self).__init__(spacing=6)
|
||||
self.name = name
|
||||
self.type = "account"
|
||||
|
17
src/gtkUI/buffers/people.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
from base import basePanel
|
||||
|
||||
class peoplePanel(basePanel):
|
||||
""" Buffer used to show people."""
|
||||
|
||||
def create_list(self):
|
||||
self.list = widgetUtils.list(_(u"User"))
|
||||
|
||||
def __init__(self, parent, name):
|
||||
super(peoplePanel, self).__init__(parent, name)
|
||||
self.type = "people"
|
||||
self.reply.set_label(_(u"Mention"))
|
||||
self.retweet.hide()
|
||||
self.retweet.set_no_show_all(True)
|
26
src/gtkUI/buffers/trends.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
|
||||
class trendsPanel(Gtk.VBox):
|
||||
def create_list(self):
|
||||
""" Returns the list for put the tweets here."""
|
||||
self.list = widgetUtils.list(_(u"Trending topic"))
|
||||
|
||||
def __init__(self, parent, name):
|
||||
super(trendsPanel, self).__init__(spacing=6)
|
||||
self.type = "trends"
|
||||
self.create_list()
|
||||
self.tweet = Gtk.Button(_(u"Tweet"))
|
||||
self.tweetTrendBtn = Gtk.Button(_(u"Tweet about this trend"))
|
||||
btnSizer = Gtk.Box(spacing=3)
|
||||
btnSizer.add(self.tweet)
|
||||
btnSizer.add(self.tweetTrendBtn)
|
||||
self.add(btnSizer)
|
||||
self.Add(self.list.list)
|
||||
|
||||
def set_position(self, reversed=False):
|
||||
if reversed == False:
|
||||
self.list.select_item(self.list.get_count()-1)
|
||||
else:
|
||||
self.list.select_item(0)
|
8
src/gtkUI/buffers/tweet_searches.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
from base import basePanel
|
||||
|
||||
class searchPanel(basePanel):
|
||||
def __init__(self, parent, name):
|
||||
super(searchPanel, self).__init__(parent, name)
|
||||
self.type = "search"
|
14
src/gtkUI/buffers/user_searches.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
from tweet_searches import searchPanel
|
||||
import widgetUtils
|
||||
|
||||
class searchUsersPanel(searchPanel):
|
||||
def create_list(self):
|
||||
""" Returns the list for put the tweets here."""
|
||||
self.list = widgetUtils.list(_(u"User"))
|
||||
|
||||
def __init__(self, parent, name):
|
||||
self.create_list()
|
||||
super(searchUsersPanel, self).__init__(parent, name)
|
||||
self.type = "user_searches"
|
64
src/gtkUI/commonMessageDialogs.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import application
|
||||
def retweet_as_link(parent):
|
||||
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, unicode(application.name))
|
||||
dialog.format_secondary_text(_(u"This retweet is over 140 characters. Would you like to post it as a mention to the poster with your comments and a link to the original tweet?"))
|
||||
answer = dialog.run()
|
||||
dialog.destroy()
|
||||
return answer
|
||||
|
||||
def retweet_question(parent):
|
||||
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _(u"Retweet"))
|
||||
dialog.format_secondary_text(_(u"Would you like to add a comment to this tweet?"))
|
||||
answer = dialog.run()
|
||||
dialog.destroy()
|
||||
return answer
|
||||
|
||||
def delete_tweet_dialog(parent):
|
||||
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _(u"Delete"))
|
||||
dialog.format_secondary_text(_(u"Do you really want to delete this message? It will be deleted from Twitter as well."))
|
||||
answer = dialog.run()
|
||||
dialog.destroy()
|
||||
return answer
|
||||
|
||||
def exit_dialog(parent):
|
||||
dialog = Gtk.MessageDialog(parent, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _(u"Exit"))
|
||||
dialog.format_secondary_text(_(u"Do you really want to close " + application.name + "?"))
|
||||
answer = dialog.run()
|
||||
dialog.destroy()
|
||||
return answer
|
||||
|
||||
def needs_restart():
|
||||
wx.MessageDialog(None, _(unicode(application.name+" must be restarted to save these changes. Press OK to restart now.")), _("Restart " + application.name), wx.OK).ShowModal()
|
||||
|
||||
def delete_user_from_db():
|
||||
return wx.MessageDialog(None, _(u"Are you sure you want to delete this user from the database? This user will not appear on the autocomplete results anymore."), _(u"Confirm"), wx.YES_NO|wx.ICON_QUESTION).ShowModal()
|
||||
|
||||
def get_ignored_client():
|
||||
entry = wx.TextEntryDialog(None, _(u"Enter the name of the client here"), _(u"Add a new ignored client"))
|
||||
if entry.ShowModal() == wx.ID_OK:
|
||||
return entry.GetValue()
|
||||
return None
|
||||
|
||||
def clear_list():
|
||||
dlg = wx.MessageDialog(None, _(u"Do you really want to empty this buffer? It's items will be removed from the list but not from Twitter"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
|
||||
return dlg.ShowModal()
|
||||
|
||||
def remove_buffer():
|
||||
return wx.MessageDialog(None, _(u"Do you really want to delete this timeline?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO).ShowModal()
|
||||
|
||||
def user_not_exist():
|
||||
return wx.MessageDialog(None, _(u"The user does not exist"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def timeline_exist():
|
||||
return wx.MessageDialog(None, _(u"There's currently a timeline for this user. You are not able to open another"), _(u"Existing timeline"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def no_tweets():
|
||||
return wx.MessageDialog(None, _(u"This user has no tweets. You can't open a timeline for this user"), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def protected_user():
|
||||
return wx.MessageDialog(None, _(u"This is a protected Twitter user. It means you can not open a timeline using the Streaming API. The user's tweets will not update due to a twitter policy. Do you want to continue?"), _(u"Warning"), wx.ICON_WARNING|wx.YES_NO).ShowModal()
|
||||
|
||||
def no_following():
|
||||
return wx.MessageDialog(None, _(u"This is a protected user account, you need follow to this user for viewing your tweets or favourites."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
1
src/gtkUI/dialogs/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#import trends, configuration, lists, message, search, show_user, update_profile, urlList, userSelection, utils
|
228
src/gtkUI/dialogs/configuration.py
Normal file
@@ -0,0 +1,228 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import baseDialog
|
||||
import wx
|
||||
import logging as original_logger
|
||||
import application
|
||||
class general(wx.Panel, baseDialog.BaseWXDialog):
|
||||
def __init__(self, parent, languages):
|
||||
super(general, self).__init__(parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
language = wx.StaticText(self, -1, _(u"Language"))
|
||||
self.language = wx.ListBox(self, -1, choices=languages)
|
||||
self.language.SetSize(self.language.GetBestSize())
|
||||
langBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
langBox.Add(language, 0, wx.ALL, 5)
|
||||
langBox.Add(self.language, 0, wx.ALL, 5)
|
||||
sizer.Add(langBox, 0, wx.ALL, 5)
|
||||
self.ask_at_exit = wx.CheckBox(self, -1, _(U"ask before exiting " + application.name))
|
||||
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
|
||||
self.use_invisible_shorcuts = wx.CheckBox(self, -1, _(u"Use invisible interface's keyboard shortcuts while GUI is visible"))
|
||||
sizer.Add(self.use_invisible_shorcuts, 0, wx.ALL, 5)
|
||||
self.disable_sapi5 = wx.CheckBox(self, -1, _(u"Activate Sapi5 when any other screen reader is not being run"))
|
||||
sizer.Add(self.disable_sapi5, 0, wx.ALL, 5)
|
||||
self.hide_gui = wx.CheckBox(self, -1, _(u"Hide GUI on launch"))
|
||||
sizer.Add(self.hide_gui, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
|
||||
class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
||||
def __init__(self, parent):
|
||||
super(generalAccount, self).__init__(parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.au = wx.Button(self, wx.NewId(), _(u"Set the autocomplete function"))
|
||||
sizer.Add(self.au, 0, wx.ALL, 5)
|
||||
self.relative_time = wx.CheckBox(self, wx.NewId(), _(U"Relative times"))
|
||||
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
||||
apiCallsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
apiCallsBox.Add(wx.StaticText(self, -1, _(u"API calls when the stream is started (One API call equals to 200 tweetts, two API calls equals 400 tweets, etc):")), 0, wx.ALL, 5)
|
||||
self.apiCalls = wx.SpinCtrl(self, wx.NewId())
|
||||
self.apiCalls.SetRange(1, 10)
|
||||
self.apiCalls.SetSize(self.apiCalls.GetBestSize())
|
||||
apiCallsBox.Add(self.apiCalls, 0, wx.ALL, 5)
|
||||
sizer.Add(apiCallsBox, 0, wx.ALL, 5)
|
||||
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
tweetsPerCallBox.Add(wx.StaticText(self, -1, _(u"Items on each API call")), 0, wx.ALL, 5)
|
||||
self.itemsPerApiCall = wx.SpinCtrl(self, wx.NewId())
|
||||
self.itemsPerApiCall.SetRange(0, 200)
|
||||
self.itemsPerApiCall.SetSize(self.itemsPerApiCall.GetBestSize())
|
||||
tweetsPerCallBox.Add(self.itemsPerApiCall, 0, wx.ALL, 5)
|
||||
sizer.Add(tweetsPerCallBox, 0, wx.ALL, 5)
|
||||
self.reverse_timelines = wx.CheckBox(self, wx.NewId(), _(u"Inverted buffers: The newest tweets will be shown at the beginning of the lists while the oldest at the end"))
|
||||
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
|
||||
class other_buffers(wx.Panel):
|
||||
def __init__(self, parent):
|
||||
super(other_buffers, self).__init__(parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.SetSizer(sizer)
|
||||
|
||||
class ignoredClients(wx.Panel):
|
||||
def __init__(self, parent, choices):
|
||||
super(ignoredClients, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(self, -1, _(u"Ignored clients"))
|
||||
self.clients = wx.ListBox(self, -1, choices=choices)
|
||||
self.clients.SetSize(self.clients.GetBestSize())
|
||||
clientsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
clientsBox.Add(label, 0, wx.ALL, 5)
|
||||
clientsBox.Add(self.clients, 0, wx.ALL, 5)
|
||||
self.add = wx.Button(self, -1, _(u"Add client"))
|
||||
self.remove = wx.Button(self, -1, _(u"Remove client"))
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.add, 0, wx.ALL, 5)
|
||||
btnBox.Add(self.remove, 0, wx.ALL, 5)
|
||||
sizer.Add(clientsBox, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def append(self, client):
|
||||
self.clients.Append(client)
|
||||
|
||||
def get_clients(self):
|
||||
return self.clients.GetCount()
|
||||
|
||||
def get_client_id(self):
|
||||
return self.clients.GetSelection()
|
||||
|
||||
def remove_(self, id):
|
||||
self.clients.Delete(id)
|
||||
|
||||
class sound(wx.Panel):
|
||||
def __init__(self, parent, input_devices, output_devices, soundpacks):
|
||||
wx.Panel.__init__(self, parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
volume = wx.StaticText(self, -1, _(u"Volume"))
|
||||
self.volumeCtrl = wx.Slider(self)
|
||||
self.volumeCtrl.SetRange(0, 100)
|
||||
self.volumeCtrl.SetSize(self.volumeCtrl.GetBestSize())
|
||||
volumeBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
volumeBox.Add(volume, 0, wx.ALL, 5)
|
||||
volumeBox.Add(self.volumeCtrl, 0, wx.ALL, 5)
|
||||
sizer.Add(volumeBox, 0, wx.ALL, 5)
|
||||
self.session_mute = wx.CheckBox(self, -1, _(u"Session mute"))
|
||||
sizer.Add(self.session_mute, 0, wx.ALL, 5)
|
||||
output_label = wx.StaticText(self, -1, _(u"Output device"))
|
||||
self.output = wx.ComboBox(self, -1, choices=output_devices, style=wx.CB_READONLY)
|
||||
self.output.SetSize(self.output.GetBestSize())
|
||||
outputBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
outputBox.Add(output_label, 0, wx.ALL, 5)
|
||||
outputBox.Add(self.output, 0, wx.ALL, 5)
|
||||
sizer.Add(outputBox, 0, wx.ALL, 5)
|
||||
input_label = wx.StaticText(self, -1, _(u"Input device"))
|
||||
self.input = wx.ComboBox(self, -1, choices=input_devices, style=wx.CB_READONLY)
|
||||
self.input.SetSize(self.input.GetBestSize())
|
||||
inputBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
inputBox.Add(input_label, 0, wx.ALL, 5)
|
||||
inputBox.Add(self.input, 0, wx.ALL, 5)
|
||||
sizer.Add(inputBox, 0, wx.ALL, 5)
|
||||
soundBox = wx.BoxSizer(wx.VERTICAL)
|
||||
soundpack_label = wx.StaticText(self, -1, _(u"Sound pack"))
|
||||
self.soundpack = wx.ComboBox(self, -1, choices=soundpacks, style=wx.CB_READONLY)
|
||||
self.soundpack.SetSize(self.soundpack.GetBestSize())
|
||||
soundBox.Add(soundpack_label, 0, wx.ALL, 5)
|
||||
soundBox.Add(self.soundpack, 0, wx.ALL, 5)
|
||||
sizer.Add(soundBox, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def get(self, control):
|
||||
return getattr(self, control).GetStringSelection()
|
||||
|
||||
class audioServicesPanel(wx.Panel):
|
||||
def __init__(self, parent):
|
||||
super(audioServicesPanel, self).__init__(parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
apiKeyLabel = wx.StaticText(self, -1, _(u"If you've got a SndUp account, enter your API Key here. Whether the API Key is wrong, the App will fail to upload anything to the server. Whether there's no API Key here, then the audio files will be uploaded anonimously"))
|
||||
self.apiKey = wx.TextCtrl(self, -1)
|
||||
dc = wx.WindowDC(self.apiKey)
|
||||
dc.SetFont(self.apiKey.GetFont())
|
||||
self.apiKey.SetSize(dc.GetTextExtent("0"*100))
|
||||
apiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
apiKeyBox.Add(apiKeyLabel, 0, wx.ALL, 5)
|
||||
apiKeyBox.Add(self.apiKey, 0, wx.ALL, 5)
|
||||
mainSizer.Add(apiKeyBox, 0, wx.ALL, 5)
|
||||
first_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.dropbox = wx.Button(self, -1)
|
||||
first_sizer.Add(self.dropbox, 0, wx.ALL, 5)
|
||||
mainSizer.Add(first_sizer, 0, wx.ALL, 5)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
def set_dropbox(self, active=True):
|
||||
if active == True:
|
||||
self.dropbox.SetLabel(_(u"Unlink your Dropbox account"))
|
||||
else:
|
||||
self.dropbox.SetLabel(_(u"Link your Dropbox account"))
|
||||
|
||||
def show_dialog(self):
|
||||
wx.MessageDialog(self, _(u"The authorization request will be opened in your browser. Copy the code from Dropbox and paste it into the text box which will appear. You only need to do this once."), _(u"Authorization"), wx.OK).ShowModal()
|
||||
|
||||
def get_response(self):
|
||||
dlg = wx.TextEntryDialog(self, _(u"Enter the code here."), _(u"Verification code"))
|
||||
if dlg.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
return dlg.GetValue()
|
||||
|
||||
def show_error(self):
|
||||
wx.MessageDialog(self, _(u"Error during authorisation. Try again later."), _(u"Error!"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def get_dropbox(self):
|
||||
return self.dropbox.GetLabel()
|
||||
|
||||
class configurationDialog(baseDialog.BaseWXDialog):
|
||||
|
||||
def set_title(self, title):
|
||||
self.SetTitle(title)
|
||||
|
||||
def __init__(self):
|
||||
super(configurationDialog, self).__init__(None, -1)
|
||||
self.panel = wx.Panel(self)
|
||||
self.SetTitle(_(u"TW Blue preferences"))
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.notebook = wx.Notebook(self.panel)
|
||||
|
||||
def create_general(self, languageList):
|
||||
self.general = general(self.notebook, languageList)
|
||||
self.notebook.AddPage(self.general, _(u"General"))
|
||||
self.general.SetFocus()
|
||||
|
||||
def create_general_account(self):
|
||||
self.general = generalAccount(self.notebook)
|
||||
self.notebook.AddPage(self.general, _(u"General"))
|
||||
self.general.SetFocus()
|
||||
|
||||
def create_other_buffers(self):
|
||||
self.buffers = other_buffers(self.notebook)
|
||||
self.notebook.AddPage(self.buffers, _(u"Show other buffers"))
|
||||
|
||||
def create_ignored_clients(self, ignored_clients_list):
|
||||
self.ignored_clients = ignoredClients(self.notebook, ignored_clients_list)
|
||||
self.notebook.AddPage(self.ignored_clients, _(u"Ignored clients"))
|
||||
|
||||
def create_sound(self, output_devices, input_devices, soundpacks):
|
||||
self.sound = sound(self.notebook, output_devices, input_devices, soundpacks)
|
||||
self.notebook.AddPage(self.sound, _(u"Sound"))
|
||||
def create_audio_services(self):
|
||||
self.services = audioServicesPanel(self.notebook)
|
||||
self.notebook.AddPage(self.services, _(u"Audio Services"))
|
||||
|
||||
def realize(self):
|
||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
ok = wx.Button(self.panel, wx.ID_OK, _(u"Save"))
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"))
|
||||
self.SetEscapeId(cancel.GetId())
|
||||
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
||||
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
||||
self.sizer.Add(ok_cancel_box, 0, wx.ALL, 5)
|
||||
self.panel.SetSizer(self.sizer)
|
||||
self.SetClientSize(self.sizer.CalcMin())
|
||||
|
||||
def get_value(self, panel, key):
|
||||
p = getattr(self, panel)
|
||||
return getattr(p, key).GetValue()
|
||||
|
||||
def set_value(self, panel, key, value):
|
||||
p = getattr(self, panel)
|
||||
control = getattr(p, key)
|
||||
getattr(control, "SetValue")(value)
|
||||
|
123
src/gtkUI/dialogs/lists.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
from multiplatform_widgets import widgets
|
||||
|
||||
class listViewer(wx.Dialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(listViewer, self).__init__(parent=None, *args, **kwargs)
|
||||
self.SetTitle(_(u"Lists manager"))
|
||||
panel = wx.Panel(self)
|
||||
label = wx.StaticText(panel, -1, _(u"Lists"))
|
||||
self.lista = widgets.list(panel, _(u"List"), _(u"Description"), _(u"Owner"), _(u"Members"), _(u"mode"), size=(800, 800), style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
|
||||
self.lista.list.SetFocus()
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(label)
|
||||
sizer.Add(self.lista.list)
|
||||
self.createBtn = wx.Button(panel, wx.NewId(), _(u"Create a new list"))
|
||||
self.editBtn = wx.Button(panel, -1, _(u"Edit"))
|
||||
self.deleteBtn = wx.Button(panel, -1, _(u"Remove"))
|
||||
self.view = wx.Button(panel, -1, _(u"Open in buffer"))
|
||||
# self.members = wx.Button(panel, -1, _(u"View members"))
|
||||
# self.members.Disable()
|
||||
# self.subscriptors = wx.Button(panel, -1, _(u"View subscribers"))
|
||||
# self.subscriptors.Disable()
|
||||
# self.get_linkBtn = wx.Button(panel, -1, _(u"Get link for the list"))
|
||||
# self.get_linkBtn.Bind(wx.EVT_BUTTON, self.onGetLink)
|
||||
self.cancelBtn = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnSizer = wx.BoxSizer()
|
||||
btnSizer.Add(self.createBtn)
|
||||
btnSizer.Add(self.editBtn)
|
||||
btnSizer.Add(self.cancelBtn)
|
||||
panel.SetSizer(sizer)
|
||||
|
||||
def populate_list(self, lists):
|
||||
for item in lists:
|
||||
self.lista.insert_item(False, *item)
|
||||
|
||||
def get_item(self):
|
||||
return self.lista.get_selected()
|
||||
|
||||
class userListViewer(listViewer):
|
||||
def __init__(self, username, *args, **kwargs):
|
||||
self.username = username
|
||||
super(userListViewer, self).__init__(*args, **kwargs)
|
||||
self.SetTitle(_(u"Viewing lists for %s") % (self.username))
|
||||
self.createBtn.SetLabel(_(u"Subscribe"))
|
||||
self.deleteBtn.SetLabel(_(u"Unsubscribe"))
|
||||
self.editBtn.Disable()
|
||||
self.view.Disable()
|
||||
|
||||
class createListDialog(wx.Dialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(createListDialog, self).__init__(*args, **kwargs)
|
||||
self.SetTitle(_(u"Create a new list"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
name = wx.StaticText(panel, -1, _(u"Name (20 characters maximun)"))
|
||||
self.name = wx.TextCtrl(panel, -1)
|
||||
nameSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
nameSizer.Add(name)
|
||||
nameSizer.Add(self.name)
|
||||
description = wx.StaticText(panel, -1, _(u"Description"))
|
||||
self.description = wx.TextCtrl(panel, -1)
|
||||
descriptionSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
descriptionSizer.Add(description)
|
||||
descriptionSizer.Add(self.description)
|
||||
mode = wx.StaticText(panel, -1, _(u"Mode"))
|
||||
self.public = wx.RadioButton(panel, -1, _(u"Public"), style=wx.RB_GROUP)
|
||||
self.private = wx.RadioButton(panel, -1, _(u"Private"))
|
||||
modeBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
modeBox.Add(mode)
|
||||
modeBox.Add(self.public)
|
||||
modeBox.Add(self.private)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(ok)
|
||||
btnBox.Add(cancel)
|
||||
sizer.Add(nameSizer)
|
||||
sizer.Add(descriptionSizer)
|
||||
sizer.Add(modeBox)
|
||||
sizer.Add(btnBox)
|
||||
|
||||
def get(self, field):
|
||||
return getattr(self, field).GetValue()
|
||||
|
||||
class editListDialog(createListDialog):
|
||||
|
||||
def __init__(self, list, *args, **kwargs):
|
||||
super(editListDialog, self).__init__(*args, **kwargs)
|
||||
self.SetTitle(_(u"Editing the list %s") % (list["name"]))
|
||||
self.name.ChangeValue(list["name"])
|
||||
self.description.ChangeValue(list["description"])
|
||||
if list["mode"] == "public":
|
||||
self.public.SetValue(True)
|
||||
else:
|
||||
self.private.SetValue(True)
|
||||
|
||||
class addUserListDialog(listViewer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(addUserListDialog, self).__init__(*args, **kwargs)
|
||||
self.SetTitle(_(u"Select a list to add the user"))
|
||||
self.createBtn.SetLabel(_(u"Add"))
|
||||
self.createBtn.SetDefault()
|
||||
self.editBtn.Disable()
|
||||
self.view.Disable()
|
||||
# self.subscriptors.Disable()
|
||||
# self.members.Disable()
|
||||
self.deleteBtn.Disable()
|
||||
|
||||
class removeUserListDialog(listViewer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(removeUserListDialog, self).__init__(*args, **kwargs)
|
||||
self.SetTitle(_(u"Select a list to remove the user"))
|
||||
self.createBtn.SetLabel(_(u"Remove"))
|
||||
self.createBtn.SetDefault()
|
||||
self.editBtn.Disable()
|
||||
self.view.Disable()
|
||||
# self.subscriptors.Disable()
|
||||
# self.members.Disable()
|
||||
self.deleteBtn.Disable()
|
275
src/gtkUI/dialogs/message.py
Normal file
@@ -0,0 +1,275 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from gi.repository import Gtk
|
||||
import widgetUtils
|
||||
|
||||
class textLimited(widgetUtils.baseDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(textLimited, self).__init__(buttons=(Gtk.STOCK_OK, widgetUtils.OK, Gtk.STOCK_CANCEL, widgetUtils.CANCEL), *args, **kwargs)
|
||||
|
||||
def createTextArea(self, message="", text=""):
|
||||
self.label = Gtk.Label(message)
|
||||
self.set_title(message, titleWindow=True)
|
||||
self.text = Gtk.Entry()
|
||||
self.text.set_max_length(140)
|
||||
self.text.set_text(text)
|
||||
self.text.set_placeholder_text(message)
|
||||
self.set_title(str(len(text)))
|
||||
self.textBox = Gtk.Box(spacing=10)
|
||||
self.textBox.add(self.label)
|
||||
self.textBox.add(self.text)
|
||||
|
||||
def get(self, control):
|
||||
if control == "upload_image":
|
||||
return self.upload_image.get_label()
|
||||
elif control == "cb":
|
||||
return self.cb.get_active_text()
|
||||
|
||||
def set(self, control, val):
|
||||
if control == "upload_image":
|
||||
self.upload_image.set_label(val)
|
||||
elif control == "cb":
|
||||
self.cb.set_active_text(val)
|
||||
|
||||
def text_focus(self):
|
||||
self.text.grab_focus()
|
||||
|
||||
def get_text(self):
|
||||
return self.text.get_text()
|
||||
|
||||
def set_text(self, text):
|
||||
self.text.set_text(text)
|
||||
|
||||
def set_title(self, new_title, titleWindow=False):
|
||||
if titleWindow == False:
|
||||
self.text.set_placeholder_text(new_title)
|
||||
else:
|
||||
super(textLimited, self).set_title(new_title)
|
||||
# self.set_title(new_title)
|
||||
|
||||
def enable_button(self, buttonName):
|
||||
if getattr(self, buttonName):
|
||||
return getattr(self, buttonName).show()
|
||||
|
||||
def disable_button(self, buttonName):
|
||||
if getattr(self, buttonName):
|
||||
return getattr(self, buttonName).hide()
|
||||
|
||||
def set_cursor_at_end(self):
|
||||
self.text.set_position(-1)
|
||||
|
||||
def set_cursor_at_position(self, position):
|
||||
self.text.set_position()
|
||||
|
||||
def get_position(self):
|
||||
return self.text.get_position()
|
||||
|
||||
class tweet(textLimited):
|
||||
def createControls(self, title, message, text):
|
||||
self.createTextArea(message, text)
|
||||
self.box.add(self.textBox)
|
||||
self.upload_image = Gtk.Button(_(u"Upload image..."))
|
||||
self.spellcheck = Gtk.Button(_("Check spelling..."))
|
||||
self.attach = Gtk.Button(_(u"Attach audio..."))
|
||||
self.shortenButton = Gtk.Button(_(u"Shorten URL"))
|
||||
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
|
||||
self.shortenButton.hide()
|
||||
self.shortenButton.set_no_show_all(True)
|
||||
self.unshortenButton.hide()
|
||||
self.unshortenButton.set_no_show_all(True)
|
||||
self.translateButton = Gtk.Button(_(u"Translate..."))
|
||||
self.autocompletionButton = Gtk.Button(_(u"&Autocomplete users"))
|
||||
self.buttonsBox1 = Gtk.Box(spacing=6)
|
||||
self.buttonsBox1.add(self.upload_image)
|
||||
self.buttonsBox1.add(self.spellcheck)
|
||||
self.buttonsBox1.add(self.attach)
|
||||
self.box.add(self.buttonsBox1)
|
||||
self.buttonsBox2 = Gtk.Box(spacing=6)
|
||||
self.buttonsBox2.add(self.shortenButton)
|
||||
self.buttonsBox2.add(self.unshortenButton)
|
||||
self.buttonsBox2.add(self.translateButton)
|
||||
self.box.add(self.buttonsBox2)
|
||||
|
||||
def __init__(self, title, message, text):
|
||||
super(tweet, self).__init__()
|
||||
self.createControls(message, title, text)
|
||||
self.show_all()
|
||||
|
||||
def get_image(self):
|
||||
dialog = Gtk.FileChooserDialog(_(u"Select the picture to be uploaded"), self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
||||
filter_jpg = Gtk.FileFilter()
|
||||
filter_jpg.set_name(_(u"JPG images"))
|
||||
filter_jpg.add_mime_type("image/jpeg")
|
||||
dialog.add_filter(filter_jpg)
|
||||
filter_gif = Gtk.FileFilter()
|
||||
filter_gif.set_name(_(u"GIF images"))
|
||||
filter_gif.add_mime_type("image/gif")
|
||||
dialog.add_filter(filter_gif)
|
||||
filter_png = Gtk.FileFilter()
|
||||
filter_png.set_name(_(u"PNG Images"))
|
||||
filter_png.add_mime_type("image/png")
|
||||
dialog.add_filter(filter_png)
|
||||
answer = dialog.run()
|
||||
if answer == widgetUtils.OK:
|
||||
image = dialog.get_filename()
|
||||
dialog.destroy()
|
||||
return open(image, "rb")
|
||||
else:
|
||||
dialog.destroy()
|
||||
return None
|
||||
|
||||
class dm(textLimited):
|
||||
def createControls(self, title, message, users):
|
||||
label = Gtk.Label(_(u"Recipient"))
|
||||
self.cb = Gtk.ComboBoxText.new_with_entry()
|
||||
self.cb.set_entry_text_column(0)
|
||||
for user in users:
|
||||
self.cb.append_text(user)
|
||||
self.cb.get_child().set_placeholder_text(_(u"Recipient"))
|
||||
self.cb.get_child().set_text(users[0])
|
||||
self.autocompletionButton = Gtk.Button(_(u"&Autocomplete users"))
|
||||
self.createTextArea(message, text="")
|
||||
userBox = Gtk.Box(spacing=8)
|
||||
userBox.add(label)
|
||||
userBox.add(self.cb)
|
||||
userBox.add(self.autocompletionButton)
|
||||
self.box.add(userBox)
|
||||
# self.mainBox.Add(self.cb, 0, wx.ALL, 5)
|
||||
self.box.add(self.textBox)
|
||||
self.spellcheck = Gtk.Button(_("Spelling correction"))
|
||||
self.attach = Gtk.Button(_(u"Attach audio"))
|
||||
self.shortenButton = Gtk.Button(_(u"Shorten URL"))
|
||||
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
|
||||
self.shortenButton.hide()
|
||||
self.shortenButton.set_no_show_all(True)
|
||||
self.unshortenButton.hide()
|
||||
self.unshortenButton.set_no_show_all(True)
|
||||
self.translateButton = Gtk.Button(_(u"Translate message"))
|
||||
self.buttonsBox = Gtk.Box(spacing=6)
|
||||
self.buttonsBox.add(self.spellcheck)
|
||||
self.buttonsBox.add(self.attach)
|
||||
self.box.add(self.buttonsBox)
|
||||
self.buttonsBox1 = Gtk.Box(spacing=6)
|
||||
self.buttonsBox1.add(self.shortenButton)
|
||||
self.buttonsBox1.add(self.unshortenButton)
|
||||
self.buttonsBox1.add(self.translateButton)
|
||||
self.box.add(self.buttonsBox1)
|
||||
self.text.grab_focus()
|
||||
|
||||
def __init__(self, title, message, users):
|
||||
super(dm, self).__init__()
|
||||
self.createControls(message, title, users)
|
||||
# self.onTimer(wx.EVT_CHAR_HOOK)
|
||||
self.show_all()
|
||||
|
||||
def get_user(self):
|
||||
return self.cb.get_text()
|
||||
|
||||
def set_user(self, user):
|
||||
return self.cb.set_value()
|
||||
|
||||
class reply(tweet):
|
||||
def __init__(self, title, message, text):
|
||||
super(reply, self).__init__(message, title, text)
|
||||
self.text.set_position(-1)
|
||||
self.mentionAll = Gtk.Button(_(u"Men&tion to all"))
|
||||
self.mentionAll.set_no_show_all(True)
|
||||
self.mentionAll.hide()
|
||||
self.buttonsBox1.add(self.mentionAll)
|
||||
|
||||
class viewTweet(widgetUtils.baseDialog):
|
||||
def set_title(self, lenght):
|
||||
pass
|
||||
self.set_title(_(u"Tweet - %i characters ") % (lenght,))
|
||||
|
||||
def __init__(self, text, rt_count, favs_count):
|
||||
super(viewTweet, self).__init__(buttons=(Gtk.STOCK_OK, widgetUtils.OK, Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
|
||||
label = Gtk.Label(_(u"Tweet"))
|
||||
self.text = Gtk.TextView()
|
||||
self.textBuffer = self.text.get_buffer()
|
||||
self.textBuffer.set_text(text)
|
||||
self.text.set_editable(False)
|
||||
# self.textBuffer.set_placeholder_text(message)
|
||||
textBox = Gtk.Box(spacing=6)
|
||||
textBox.add(label)
|
||||
textBox.add(self.text)
|
||||
self.box.add(textBox)
|
||||
rtCountLabel = Gtk.Label(_(u"Retweets: "))
|
||||
rtCount = Gtk.Entry()
|
||||
rtCount.set_text(rt_count)
|
||||
rtCount.set_editable(False)
|
||||
rtBox = Gtk.Box(spacing=2)
|
||||
rtBox.add(rtCountLabel)
|
||||
rtBox.add(rtCount)
|
||||
favsCountLabel = Gtk.Label(_(u"Favourites: "))
|
||||
favsCount = Gtk.Entry()
|
||||
favsCount.set_text(favs_count)
|
||||
favsCount.set_editable(False)
|
||||
favsBox = Gtk.Box(spacing=2)
|
||||
favsBox.add(favsCountLabel)
|
||||
favsBox.add(favsCount)
|
||||
infoBox = Gtk.Box(spacing=4)
|
||||
infoBox.add(rtBox)
|
||||
infoBox.add(favsBox)
|
||||
self.box.add(infoBox)
|
||||
self.spellcheck = Gtk.Button(_("Spelling correction"))
|
||||
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
|
||||
self.unshortenButton.hide()
|
||||
self.unshortenButton.set_no_show_all(True)
|
||||
self.translateButton = Gtk.Button(_(u"Translate message"))
|
||||
buttonsBox = Gtk.Box(spacing=6)
|
||||
buttonsBox.add(self.spellcheck)
|
||||
buttonsBox.add(self.unshortenButton)
|
||||
buttonsBox.add(self.translateButton)
|
||||
self.box.add(buttonsBox)
|
||||
self.show_all()
|
||||
|
||||
def set_text(self, text):
|
||||
self.textBuffer.set_text(text)
|
||||
|
||||
def get_text(self):
|
||||
return self.textBuffer.get_text(self.textBuffer.get_start_iter(), self.textBuffer.get_end_iter(), False)
|
||||
|
||||
def text_focus(self):
|
||||
self.text.grab_focus()
|
||||
|
||||
def enable_button(self, buttonName):
|
||||
if getattr(self, buttonName):
|
||||
return getattr(self, buttonName).show()
|
||||
|
||||
class viewNonTweet(widgetUtils.baseDialog):
|
||||
|
||||
def __init__(self, text):
|
||||
super(viewNonTweet, self).__init__(buttons=(Gtk.STOCK_OK, widgetUtils.OK, Gtk.STOCK_CANCEL, widgetUtils.CANCEL))
|
||||
self.set_title(_(u"View"))
|
||||
label = Gtk.Label(_(u"Item"))
|
||||
self.text = Gtk.TextView()
|
||||
self.text.set_editable(False)
|
||||
self.text.get_buffer().set_text(text)
|
||||
textBox = Gtk.Box(spacing=5)
|
||||
textBox.add(label)
|
||||
textBox.add(self.text)
|
||||
self.box.Add(textBox)
|
||||
self.spellcheck = Gtk.Button(_("Spelling correction"))
|
||||
self.unshortenButton = Gtk.Button(_(u"Expand URL"))
|
||||
self.unshortenButton.hide()
|
||||
self.unshortenButton.set_no_show_all(True)
|
||||
self.translateButton = Gtk.Button(_(u"Translate message"))
|
||||
buttonsBox = Gtk.Box(spacing=6)
|
||||
buttonsBox.add(self.spellcheck)
|
||||
buttonsBox.add(self.unshortenButton)
|
||||
buttonsBox.add(self.translateButton)
|
||||
self.box.Add(buttonsBox)
|
||||
self.show_all()
|
||||
|
||||
def set_text(self, text):
|
||||
self.text.get_buffer().set_text()
|
||||
|
||||
def get_text(self):
|
||||
return self.text.get_buffer().get_text()
|
||||
|
||||
def text_focus(self):
|
||||
self.text.grab_focus()
|
||||
|
||||
def enable_button(self, buttonName):
|
||||
if getattr(self, buttonName):
|
||||
return getattr(self, buttonName).show()
|
32
src/gtkUI/dialogs/search.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import baseDialog
|
||||
import wx
|
||||
|
||||
class searchDialog(baseDialog.BaseWXDialog):
|
||||
def __init__(self, value=""):
|
||||
super(searchDialog, self).__init__(None, -1)
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.SetTitle(_(u"Search on Twitter"))
|
||||
label = wx.StaticText(panel, -1, _(u"Search"))
|
||||
self.term = wx.TextCtrl(panel, -1, value)
|
||||
dc = wx.WindowDC(self.term)
|
||||
dc.SetFont(self.term.GetFont())
|
||||
self.term.SetSize(dc.GetTextExtent("0"*40))
|
||||
sizer.Add(label, 0, wx.ALL, 5)
|
||||
sizer.Add(self.term, 0, wx.ALL, 5)
|
||||
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
|
||||
self.users = wx.RadioButton(panel, -1, _(u"Users"))
|
||||
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
radioSizer.Add(self.tweets, 0, wx.ALL, 5)
|
||||
radioSizer.Add(self.users, 0, wx.ALL, 5)
|
||||
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
||||
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
|
||||
btnsizer = wx.BoxSizer()
|
||||
btnsizer.Add(ok, 0, wx.ALL, 5)
|
||||
btnsizer.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|