Compare commits
109 Commits
Author | SHA1 | Date | |
---|---|---|---|
437f01ac78 | |||
fd6c7fa2d7 | |||
![]() |
b373e716c7 | ||
cc00c513c3 | |||
![]() |
0a0fe58715 | ||
e831098bd2 | |||
![]() |
05d62456ee | ||
d2e6221432 | |||
![]() |
d31a3e5bb7 | ||
e3b0a3d731 | |||
3a6849cf0b | |||
6832a4a93e | |||
![]() |
bda1ddce47 | ||
b106b3a0f2 | |||
813ad2fc1e | |||
4bcd8d8b4e | |||
![]() |
4ef7c6c5f1 | ||
b6c9edafcf | |||
73be8400b0 | |||
![]() |
5fdbb578c4 | ||
![]() |
540bf5d312 | ||
375cdd2528 | |||
![]() |
836675bfa1 | ||
![]() |
2b3c7601f2 | ||
eb2c315abb | |||
e12f440cf2 | |||
![]() |
57dc271fb0 | ||
193cafcc9d | |||
![]() |
e7b6c39f76 | ||
7df2441442 | |||
![]() |
aa687450f6 | ||
6e0ae38d4e | |||
![]() |
fa8c9f639c | ||
9502cef251 | |||
![]() |
982dd78f81 | ||
![]() |
4b98f27968 | ||
![]() |
ecb0fa8f2b | ||
ef2ff9dbfc | |||
![]() |
1fe94642a2 | ||
b232a0b0ad | |||
2ce59cf208 | |||
![]() |
ceb8266828 | ||
![]() |
21970f2c94 | ||
14965f73d3 | |||
![]() |
267b454fc6 | ||
c3dc00c241 | |||
![]() |
9fb67f01ae | ||
6d1cd0b1fc | |||
a7ef572d05 | |||
403784b77a | |||
d032a6d8f7 | |||
![]() |
231d43ab22 | ||
5e3bcfc82e | |||
8e92d97260 | |||
bb1ffa9ff1 | |||
104bc4ec8c | |||
![]() |
6ae555fe72 | ||
![]() |
dd51516c30 | ||
![]() |
4e18bb0721 | ||
![]() |
0492e65aa0 | ||
![]() |
a5fb59d7a7 | ||
f2fab29cb7 | |||
![]() |
f36accf89b | ||
a13536233e | |||
bc2d448464 | |||
60ec9ab215 | |||
20b5fc079b | |||
80e231b689 | |||
7137d437bd | |||
![]() |
a07b3c1829 | ||
![]() |
c062c10542 | ||
![]() |
0e7c070bd4 | ||
![]() |
9f35d079b1 | ||
![]() |
a3fca4d2a0 | ||
ada6f1fb0d | |||
79124810b0 | |||
fee7254d55 | |||
![]() |
c12902d011 | ||
2f7eb12104 | |||
855cefeb8d | |||
e649b883c6 | |||
![]() |
1a61f2d790 | ||
![]() |
6abfba317a | ||
![]() |
18ce92daab | ||
![]() |
84f729f42b | ||
![]() |
4e7666cf52 | ||
88e0fbb531 | |||
af32b3ceb6 | |||
94dc083650 | |||
acb3cce1d6 | |||
![]() |
2b3e1099f6 | ||
![]() |
60507e8f6b | ||
![]() |
75bfc17bb1 | ||
acb8c5acd3 | |||
![]() |
5bcf04fc94 | ||
0f2fbc775a | |||
1b684cd12c | |||
9d64901791 | |||
18f7cb6c96 | |||
7f673fb9de | |||
![]() |
f6f542fbec | ||
![]() |
a211e2e5e4 | ||
3ec8ac31a5 | |||
![]() |
dacbb19586 | ||
![]() |
55691cca20 | ||
0104b97df0 | |||
aa58e61e5e | |||
![]() |
fae204f3c2 | ||
d8149a4c96 |
4
.gitignore
vendored
@@ -1,12 +1,8 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
windows-dependencies/*
|
|
||||||
src/build/
|
src/build/
|
||||||
src/dist/
|
src/dist/
|
||||||
src/config/
|
src/config/
|
||||||
src/config1/
|
|
||||||
src/config2/
|
|
||||||
src/config3/
|
|
||||||
src/dropbox/
|
src/dropbox/
|
||||||
src/logs/
|
src/logs/
|
||||||
src/documentation/
|
src/documentation/
|
||||||
|
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "windows-dependencies"]
|
||||||
|
path = windows-dependencies
|
||||||
|
url = https://github.com/jmdaweb/TWBlue_deps_windows.git
|
90
README.md
@@ -15,4 +15,92 @@ TW Blue is an app designed to use Twitter in a simple and fast way and avoiding,
|
|||||||
* Play various file and URL types which contain audio
|
* Play various file and URL types which contain audio
|
||||||
* and more!
|
* and more!
|
||||||
|
|
||||||
See the [TWBlue's webpage](http://twblue.com.mx) for more details.
|
See the [TWBlue's webpage](http://twblue.com.mx) 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:
|
||||||
|
|
||||||
|
* dropbox
|
||||||
|
* configobj
|
||||||
|
* requests-oauthlib
|
||||||
|
* future
|
||||||
|
* pygeocoder
|
||||||
|
* suds
|
||||||
|
* arrow
|
||||||
|
* 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 src directory into the repo, and type the following command:
|
||||||
|
|
||||||
|
python main.py
|
||||||
|
|
||||||
|
If necesary, change the first part of the command to reflect where is your python executable. You can run TW Blue using python x86 and x64
|
||||||
|
|
||||||
|
### Building a binary version
|
||||||
|
|
||||||
|
A binary version doesn't need python and the other dependencies to run, it's the same version that you will find in TW Blue website if you download the zip files.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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.
|
@@ -1,12 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import application
|
||||||
documentation = []
|
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.
|
# Translators: This is the new line character, don't change it in the translations.
|
||||||
documentation.append(_(u"""
|
documentation.append(_(u"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""## Table of contents"""))
|
documentation.append(_(u"""## Table of contents"""))
|
||||||
|
# Table of contents for the python markdown extension
|
||||||
documentation.append("""[TOC]""")
|
documentation.append("""[TOC]""")
|
||||||
#documentation.append(_(u"""# Version 0.46 (alpha)"""))
|
|
||||||
documentation.append(_(u"""
|
documentation.append(_(u"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""## Warning!"""))
|
documentation.append(_(u"""## Warning!"""))
|
||||||
@@ -41,42 +42,44 @@ 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"""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"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""## Authorising the application"""))
|
documentation.append(_(u"""### Authorising the application"""))
|
||||||
documentation.append(_(u"""
|
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"""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"""
|
||||||
"""))
|
"""))
|
||||||
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"""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"""
|
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"""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"""
|
||||||
"""))
|
"""))
|
||||||
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"""Read the instructions you will get if the process is successful. In summary, you will be given a numeric code with several digits you must paste on an edit field open by the application on another window."""))
|
||||||
documentation.append(_(u"""
|
#$documentation.append(_(u"""
|
||||||
"""))
|
#$"""))
|
||||||
documentation.append(_(u"""Paste the verification code, and press the enter key. """))
|
#$documentation.append(_(u"""Paste the verification code, and press the enter key. """))
|
||||||
documentation.append(_(u"""
|
#$documentation.append(_(u"""
|
||||||
"""))
|
#$"""))
|
||||||
|
### Add here the instructions on how to deal with the session manager.
|
||||||
documentation.append(_(u"""If all went well, the application will start playing sounds, indicating your data are being updated."""))
|
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"""
|
||||||
"""))
|
"""))
|
||||||
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"""
|
||||||
"""))
|
"""))
|
||||||
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"""
|
|
||||||
"""))
|
|
||||||
documentation.append(_(u"""Elements on the lists may be tweets, direct messages or users. TW Blue creates different tabs for each list, which can be sent tweets, main timeline tweets, favourites, or direct messages, and each tab contains a single type of tweet. These tabs are called lists or buffers."""))
|
|
||||||
documentation.append(_(u"""
|
|
||||||
"""))
|
|
||||||
documentation.append(_(u"""To switch from list to list press control-tab to go forward, and control-shift-tab to go back. Screen readers will announce the list that gains the focus at all times. These are the basic lists of TW Blue, which are configured by default."""))
|
|
||||||
documentation.append(_(u"""
|
documentation.append(_(u"""
|
||||||
"""))
|
"""))
|
||||||
|
### Add the new GUI description here
|
||||||
|
#$documentation.append(_(u"""The easiest way to describe the graphical interface of the application is a window with a menu bar with five menus (application, tweet, user, buffer and help), a list with several elements, and, in most cases, three buttons: tweet, retweet and reply. The actions available for each element are described below."""))
|
||||||
|
#$documentation.append(_(u"""
|
||||||
|
#$"""))
|
||||||
|
#$documentation.append(_(u"""Elements on the lists may be tweets, direct messages or users. TW Blue creates different tabs for each list, which can be sent tweets, main timeline tweets, favourites, or direct messages, and each tab contains a single type of tweet. These tabs are called lists or buffers."""))
|
||||||
|
#$documentation.append(_(u"""
|
||||||
|
#$"""))
|
||||||
|
#$documentation.append(_(u"""To switch from list to list press control-tab to go forward, and control-shift-tab to go back. Screen readers will announce the list that gains the focus at all times. These are the basic lists of TW Blue, which are configured by default."""))
|
||||||
|
#$documentation.append(_(u"""
|
||||||
|
#$"""))
|
||||||
documentation.append(_(u"""* Home: it shows all the tweets on the main timeline. These are the tweets by users you follow."""))
|
documentation.append(_(u"""* Home: it shows all the tweets on the main timeline. These are the tweets by users you follow."""))
|
||||||
documentation.append(_(u"""* Mentions: if a user, whether you follow them or not, mentions you on Twitter, you will find it on this list."""))
|
documentation.append(_(u"""* Mentions: if a user, whether you follow them or not, mentions you on Twitter, you will find it on this list."""))
|
||||||
documentation.append(_(u"""* Direct messages: here go the private direct messages you exchange with users you follow and who follow you back. This list only shows received messages."""))
|
documentation.append(_(u"""* Direct messages: here go the private direct messages you exchange with users you follow and who follow you back. This list only shows received messages."""))
|
||||||
@@ -86,38 +89,42 @@ 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"""* 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"""* 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"""* 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"""* 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"""* User favorites: You can have TW Blue create a buffer containing tweets favorited by a particular user."""))
|
||||||
|
### add here the trending buffers description.
|
||||||
documentation.append(_(u"""
|
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"""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"""
|
||||||
"""))
|
#$"""))
|
||||||
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"""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"""
|
||||||
"""))
|
#$"""))
|
||||||
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 on them."""))
|
||||||
documentation.append(_(u"""
|
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"""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"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""## Controls {#controls}"""))
|
### Add information about the GEO location in tweets.
|
||||||
|
documentation.append(_(u"""## Controls"""))
|
||||||
documentation.append(_(u"""
|
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."""))
|
### add more information about using invisible shorcuts in the GUI mode in the next variable.
|
||||||
|
documentation.append(_(u"""Beginning with the 0.36 version, there's support for an interface which does not require a visible window. It can be activated by pressing control-m, or choosing hide window from the application menu. This interface is entirely driven through shortcut keys. These shortcuts are different from those used to drive the graphical interface. This section describes both the graphical and the invisible interface."""))
|
||||||
documentation.append(_(u"""
|
documentation.append(_(u"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""### The graphical user interface (GUI) {#gui}"""))
|
documentation.append(_(u"""### The graphical user interface (GUI)"""))
|
||||||
documentation.append(_(u"""
|
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"""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"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""#### Buttons on the application {#buttons}"""))
|
documentation.append(_(u"""#### Buttons on the application"""))
|
||||||
documentation.append(_(u"""
|
documentation.append(_(u"""
|
||||||
"""))
|
"""))
|
||||||
|
### Add information on spell correction, translate, attach images and audio.
|
||||||
documentation.append(_(u"""* Tweet: this button opens up a dialogue box to write your tweet. The message must not exceed 140 characters. If you write past this limit, a sound will play to warn you. You may use the shorten and expand URL buttons to comply with the character limit. Press enter to send the tweet. If all goes well, you'll hear a sound confirming it. Otherwise, the screen reader will say an error message in English describing the problem."""))
|
documentation.append(_(u"""* Tweet: this button opens up a dialogue box to write your tweet. The message must not exceed 140 characters. If you write past this limit, a sound will play to warn you. You may use the shorten and expand URL buttons to comply with the character limit. 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"""* 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"""* 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."""))
|
||||||
@@ -127,13 +134,13 @@ 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"""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"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""#### Menus {#menus}"""))
|
documentation.append(_(u"""#### Menus"""))
|
||||||
documentation.append(_(u"""
|
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"""
|
||||||
"""))
|
"""))
|
||||||
documentation.append(_(u"""##### Application menu {#app}"""))
|
documentation.append(_(u"""##### Application menu"""))
|
||||||
documentation.append(_(u"""
|
documentation.append(_(u"""
|
||||||
"""))
|
"""))
|
||||||
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"""* 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."""))
|
||||||
|
BIN
pa.c format/App/AppInfo/Launcher/Splash.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
8
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
pa.c format/App/AppInfo/appicon.ico
Normal file
After Width: | Height: | Size: 182 KiB |
BIN
pa.c format/App/AppInfo/appicon_128.png
Normal file
After Width: | Height: | Size: 379 B |
BIN
pa.c format/App/AppInfo/appicon_16.png
Normal file
After Width: | Height: | Size: 103 B |
BIN
pa.c format/App/AppInfo/appicon_32.png
Normal file
After Width: | Height: | Size: 119 B |
28
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.51.0.0
|
||||||
|
DisplayVersion=0.51
|
||||||
|
|
||||||
|
[Control]
|
||||||
|
Icons=1
|
||||||
|
Start=TWBluePortable.exe
|
339
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
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
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
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
pa.c format/Other/Help/Images/Donation_Button.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
pa.c format/Other/Help/Images/Favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
pa.c format/Other/Help/Images/Help_Background_Footer.png
Normal file
After Width: | Height: | Size: 168 B |
BIN
pa.c format/Other/Help/Images/Help_Background_Header.png
Normal file
After Width: | Height: | Size: 269 B |
BIN
pa.c format/Other/Help/Images/Help_Logo_Top.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
339
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
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
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
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.
|
|
@@ -1,83 +1,89 @@
|
|||||||
!include "MUI2.nsh"
|
!include "MUI2.nsh"
|
||||||
!include "LogicLib.nsh"
|
!include "LogicLib.nsh"
|
||||||
!include "x64.nsh"
|
!include "x64.nsh"
|
||||||
CRCCheck on
|
CRCCheck on
|
||||||
XPStyle on
|
XPStyle on
|
||||||
Name "TW Blue"
|
Name "TW Blue"
|
||||||
OutFile "TWBlue_setup.exe"
|
OutFile "TWBlue_setup.exe"
|
||||||
InstallDir "$PROGRAMFILES\twblue"
|
InstallDir "$PROGRAMFILES\twblue"
|
||||||
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
|
InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "InstallLocation"
|
||||||
RequestExecutionLevel admin
|
RequestExecutionLevel admin
|
||||||
SetCompress auto
|
SetCompress auto
|
||||||
SetCompressor /solid lzma
|
SetCompressor /solid lzma
|
||||||
SetDatablockOptimize on
|
SetDatablockOptimize on
|
||||||
VIAddVersionKey ProductName "TW Blue"
|
VIAddVersionKey ProductName "TW Blue"
|
||||||
VIAddVersionKey LegalCopyright "Copyright 2014 Manuel Cortez."
|
VIAddVersionKey LegalCopyright "Copyright 2014 Manuel Cortez."
|
||||||
VIAddVersionKey ProductVersion "0.48"
|
VIAddVersionKey ProductVersion "0.51"
|
||||||
VIAddVersionKey FileVersion "0.48"
|
VIAddVersionKey FileVersion "0.51"
|
||||||
VIProductVersion "0.48.0.0"
|
VIProductVersion "0.51.0.0"
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
!insertmacro MUI_PAGE_LICENSE "license.txt"
|
!define MUI_LICENSEPAGE_RADIOBUTTONS
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
!insertmacro MUI_PAGE_LICENSE "license.txt"
|
||||||
var StartMenuFolder
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
|
var StartMenuFolder
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
|
||||||
!insertmacro MUI_PAGE_FINISH
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
!insertmacro MUI_UNPAGE_CONFIRM
|
!define MUI_FINISHPAGE_LINK "Visit TW Blue website"
|
||||||
!insertmacro MUI_UNPAGE_INSTFILES
|
!define MUI_FINISHPAGE_LINK_LOCATION "http://twblue.es"
|
||||||
!insertmacro MUI_LANGUAGE "English"
|
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
|
||||||
!insertmacro MUI_LANGUAGE "French"
|
!define MUI_FINISHPAGE_RUN_PARAMETERS "-i"
|
||||||
!insertmacro MUI_LANGUAGE "Spanish"
|
!insertmacro MUI_PAGE_FINISH
|
||||||
!insertmacro MUI_LANGUAGE "Italian"
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
!insertmacro MUI_LANGUAGE "Finnish"
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
!insertmacro MUI_LANGUAGE "Russian"
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
!insertmacro MUI_LANGUAGE "French"
|
||||||
!insertmacro MUI_LANGUAGE "Polish"
|
!insertmacro MUI_LANGUAGE "Spanish"
|
||||||
!insertmacro MUI_LANGUAGE "Hungarian"
|
!insertmacro MUI_LANGUAGE "Italian"
|
||||||
!insertmacro MUI_LANGUAGE "Turkish"
|
!insertmacro MUI_LANGUAGE "Finnish"
|
||||||
!insertmacro MUI_LANGUAGE "Arabic"
|
!insertmacro MUI_LANGUAGE "Russian"
|
||||||
!insertmacro MUI_LANGUAGE "Galician"
|
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
||||||
!insertmacro MUI_LANGUAGE "Catalan"
|
!insertmacro MUI_LANGUAGE "Polish"
|
||||||
!insertmacro MUI_LANGUAGE "Basque"
|
!insertmacro MUI_LANGUAGE "German"
|
||||||
!insertmacro MUI_RESERVEFILE_LANGDLL
|
!insertmacro MUI_LANGUAGE "Hungarian"
|
||||||
Section
|
!insertmacro MUI_LANGUAGE "Turkish"
|
||||||
SetShellVarContext All
|
!insertmacro MUI_LANGUAGE "Arabic"
|
||||||
SetOutPath "$INSTDIR"
|
!insertmacro MUI_LANGUAGE "Galician"
|
||||||
${If} ${RunningX64}
|
!insertmacro MUI_LANGUAGE "Catalan"
|
||||||
File /r TWBlue64\*
|
!insertmacro MUI_LANGUAGE "Basque"
|
||||||
${Else}
|
!insertmacro MUI_RESERVEFILE_LANGDLL
|
||||||
File /r TWBlue\*
|
Section
|
||||||
${EndIf}
|
SetShellVarContext All
|
||||||
CreateShortCut "$DESKTOP\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
SetOutPath "$INSTDIR"
|
||||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
|
${If} ${RunningX64}
|
||||||
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
File /r TWBlue64\*
|
||||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
${Else}
|
||||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue on the web.lnk" "http://twblue.com.mx"
|
File /r TWBlue\*
|
||||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
${EndIf}
|
||||||
!insertmacro MUI_STARTMENU_WRITE_END
|
CreateShortCut "$DESKTOP\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TW Blue"
|
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue.lnk" "$INSTDIR\TWBlue.exe" "-i"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
|
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TW Blue on the web.lnk" "http://twblue.es"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortez"
|
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.47"
|
!insertmacro MUI_STARTMENU_WRITE_END
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.com.mx"
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayName" "TW Blue"
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 47
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
|
||||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortez"
|
||||||
SectionEnd
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.51"
|
||||||
Section "Uninstall"
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.es"
|
||||||
SetShellVarContext All
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
|
||||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 51
|
||||||
RMDir /r /REBOOTOK $INSTDIR
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
|
||||||
Delete "$DESKTOP\TW Blue.lnk"
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
|
||||||
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
|
SectionEnd
|
||||||
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
|
Section "Uninstall"
|
||||||
SectionEnd
|
SetShellVarContext All
|
||||||
Function .onInit
|
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue"
|
||||||
${If} ${RunningX64}
|
RMDir /r /REBOOTOK $INSTDIR
|
||||||
StrCpy $instdir "$programfiles64\twblue"
|
Delete "$DESKTOP\TW Blue.lnk"
|
||||||
${EndIf}
|
!insertmacro MUI_STARTMENU_GETFOLDER startmenu $StartMenuFolder
|
||||||
!insertmacro MUI_LANGDLL_DISPLAY
|
RMDir /r "$SMPROGRAMS\$StartMenuFolder"
|
||||||
FunctionEnd
|
SectionEnd
|
||||||
|
Function .onInit
|
||||||
|
${If} ${RunningX64}
|
||||||
|
StrCpy $instdir "$programfiles64\twblue"
|
||||||
|
${EndIf}
|
||||||
|
!insertmacro MUI_LANGDLL_DISPLAY
|
||||||
|
FunctionEnd
|
||||||
|
@@ -15,6 +15,7 @@ reverse_timelines = boolean(default=False)
|
|||||||
time_to_check_streams = integer(default=30)
|
time_to_check_streams = integer(default=30)
|
||||||
announce_stream_status = boolean(default=True)
|
announce_stream_status = boolean(default=True)
|
||||||
ask_at_exit = boolean(default=True)
|
ask_at_exit = boolean(default=True)
|
||||||
|
use_invisible_keyboard_shorcuts = boolean(default=False)
|
||||||
|
|
||||||
[sound]
|
[sound]
|
||||||
volume = float(default=1.0)
|
volume = float(default=1.0)
|
||||||
@@ -35,11 +36,14 @@ timelines = list(default=list())
|
|||||||
tweet_searches = list(default=list())
|
tweet_searches = list(default=list())
|
||||||
lists = list(default=list())
|
lists = list(default=list())
|
||||||
favourites_timelines = list(default=list())
|
favourites_timelines = list(default=list())
|
||||||
|
trending_topic_buffers = list(default=list())
|
||||||
muted_buffers = list(default=list())
|
muted_buffers = list(default=list())
|
||||||
autoread_buffers = list(default=list())
|
autoread_buffers = list(default=list())
|
||||||
|
|
||||||
[mysc]
|
[mysc]
|
||||||
spelling_language = string(default="")
|
spelling_language = string(default="")
|
||||||
|
save_followers_in_autocompletion_db = boolean(default=False)
|
||||||
|
save_friends_in_autocompletion_db = boolean(default=False)
|
||||||
|
|
||||||
[services]
|
[services]
|
||||||
dropbox_token=string(default="")
|
dropbox_token=string(default="")
|
||||||
@@ -86,3 +90,6 @@ search = string(default="control+win+-")
|
|||||||
edit_keystrokes = string(default="control+win+k")
|
edit_keystrokes = string(default="control+win+k")
|
||||||
view_user_lists = string(default="control+win+l")
|
view_user_lists = string(default="control+win+l")
|
||||||
get_more_items = string(default="alt+win+pageup")
|
get_more_items = string(default="alt+win+pageup")
|
||||||
|
reverse_geocode = string(default="control+win+g")
|
||||||
|
view_reverse_geocode = string(default="control+win+shift+g")
|
||||||
|
get_trending_topics = string(default="control+win+t")
|
@@ -1,20 +1,20 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
name = 'TW Blue'
|
name = 'TWBlue'
|
||||||
snapshot = False
|
snapshot = False
|
||||||
if snapshot == False:
|
if snapshot == False:
|
||||||
version = "0.48"
|
version = "0.51"
|
||||||
update_url = 'http://twblue.com.mx/updates/tw_blue.json'
|
update_url = 'http://twblue.es/updates/tw_blue.json'
|
||||||
else:
|
else:
|
||||||
version = "4"
|
version = "7"
|
||||||
update_url = 'http://twblue.com.mx/updates/snapshots.json'
|
update_url = 'http://twblue.es/updates/snapshots.json'
|
||||||
author = u"Manuel Cortéz"
|
author = u"Manuel Cortéz"
|
||||||
authorEmail = "info@twblue.com.mx"
|
authorEmail = "manuel@manuelcortez.net"
|
||||||
copyright = u"copyright (C) 2013-2014, Manuel cortéz"
|
copyright = u"copyright (C) 2013-2015, Manuel cortéz"
|
||||||
description = u"TW Blue is an app designed to use Twitter in a simple and fast way and avoiding, as far as possible, the consumtion of excessive resources of the machine where it’s running. With this app you’ll have access to most twitter features."
|
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)"]
|
translators = [u"Bryner Villalobos (English)", u"Mohammed Al Shara (Arabic)", u"Joan Rabat, Juan Carlos Rivilla (Catalan)", u"Manuel cortéz (Spanish)", u"Sukil Etxenike Arizaleta (Basque)", u"Jani Kinnunen (finnish)", u"Rémy Ruiz (French)", u"Alba Quinteiro (Galician)", u"Steffen Schultz (German)", u"Robert Osztolykan (Hungarian)", u"Paweł Masarczyk (Polish)", u"Odenilton Júnior Santos (Portuguese)", u"Alexander Jaszyn (Russian)", u"Burak (Turkish)"]
|
||||||
url = u"http://twblue.com.mx"
|
url = u"http://twblue.es"
|
||||||
#report_bugs_url = "http://twblue.com.mx/errores/api/soap/mantisconnect.php?wsdl"
|
report_bugs_url = "http://twblue.es/bugs/api/soap/mantisconnect.php?wsdl"
|
||||||
|
|
||||||
# Tokens
|
# Tokens
|
||||||
app_key = '8pDLbyOW3saYnvSZ4uLFg'
|
app_key = '8pDLbyOW3saYnvSZ4uLFg'
|
||||||
app_secret = 'YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY'
|
app_secret = 'YsgdrzY9B4yyYvYsyee78rKI3GshjHpenVS9LnFJXY'
|
@@ -8,7 +8,9 @@ group.add_argument("-p", "--portable", help="Use TW Blue as a portable aplicatio
|
|||||||
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")
|
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")
|
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where TW Blue saves the data files")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.installed == True: paths.mode = "installed"
|
if args.installed == True:
|
||||||
|
paths.mode = "installed"
|
||||||
elif args.portable == True:
|
elif args.portable == True:
|
||||||
paths.mode = "portable"
|
paths.mode = "portable"
|
||||||
if args.directory != None: paths.directory = args.directory
|
if args.directory != None: paths.directory = args.directory
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ class soundsTutorial(wx.Dialog):
|
|||||||
_(u"A bug has happened"),
|
_(u"A bug has happened"),
|
||||||
_(u"You've added a tweet to your favourites"),
|
_(u"You've added a tweet to your favourites"),
|
||||||
_(u"Someone's favourites have been updated"),
|
_(u"Someone's favourites have been updated"),
|
||||||
|
_(u"The tweet has coordinates to determine its location"),
|
||||||
_(u"There are no more tweets to read"),
|
_(u"There are no more tweets to read"),
|
||||||
_(u"A list has a new tweet"),
|
_(u"A list has a new tweet"),
|
||||||
_(u"You can't add any more characters on the tweet"),
|
_(u"You can't add any more characters on the tweet"),
|
||||||
@@ -25,6 +26,7 @@ class soundsTutorial(wx.Dialog):
|
|||||||
_(u"You've replied"),
|
_(u"You've replied"),
|
||||||
_(u"You've retweeted"),
|
_(u"You've retweeted"),
|
||||||
_(u"A search has been updated"),
|
_(u"A search has been updated"),
|
||||||
|
_(u"the buffer for trending topics has been updated"),
|
||||||
_(u"There's a new tweet in the main buffer"),
|
_(u"There's a new tweet in the main buffer"),
|
||||||
_(u"You've sent a tweet"),
|
_(u"You've sent a tweet"),
|
||||||
_(u"There's a new tweet in a timeline"),
|
_(u"There's a new tweet in a timeline"),
|
||||||
|
1
src/extra/autocompletionUsers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
31
src/extra/autocompletionUsers/completion.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import storage
|
||||||
|
import output
|
||||||
|
import wx_menu
|
||||||
|
|
||||||
|
class autocompletionUsers(object):
|
||||||
|
def __init__(self, window):
|
||||||
|
super(autocompletionUsers, self).__init__()
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
def show_menu(self):
|
||||||
|
position = self.window.text.GetInsertionPoint()
|
||||||
|
text = self.window.text.GetValue()
|
||||||
|
text = text[:position]
|
||||||
|
try:
|
||||||
|
pattern = text.split()[-1]
|
||||||
|
except IndexError:
|
||||||
|
output.speak(_(u"You have to start writing"))
|
||||||
|
return
|
||||||
|
if pattern.startswith("@") == True:
|
||||||
|
db = storage.storage()
|
||||||
|
menu = wx_menu.menu(self.window.text, pattern[1:])
|
||||||
|
users = db.get_users(pattern[1:])
|
||||||
|
if len(users) > 0:
|
||||||
|
menu.append_options(users)
|
||||||
|
self.window.PopupMenu(menu, self.window.text.GetPosition())
|
||||||
|
menu.Destroy()
|
||||||
|
else:
|
||||||
|
output.speak(_(u"There are not results in your users database"))
|
||||||
|
else:
|
||||||
|
output.speak(_(u"Autocompletion only works for users."))
|
43
src/extra/autocompletionUsers/manage.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import storage
|
||||||
|
import wx
|
||||||
|
import wx_manage
|
||||||
|
|
||||||
|
class autocompletionManage(object):
|
||||||
|
def __init__(self, window):
|
||||||
|
super(autocompletionManage, self).__init__()
|
||||||
|
self.window = window
|
||||||
|
self.dialog = wx_manage.autocompletionManageDialog()
|
||||||
|
self.database = storage.storage()
|
||||||
|
self.users = self.database.get_all_users()
|
||||||
|
self.dialog.put_users(self.users)
|
||||||
|
self.dialog.add.Bind(wx.EVT_BUTTON, self.add_user)
|
||||||
|
self.dialog.remove.Bind(wx.EVT_BUTTON, self.remove_user)
|
||||||
|
self.dialog.ShowModal()
|
||||||
|
|
||||||
|
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, event=None):
|
||||||
|
usr = self.dialog.get_user()
|
||||||
|
if usr == False:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
data = self.window.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):
|
||||||
|
ask = wx.MessageDialog(None, _(u"Are you sure you want to delete this user from the database? This user will not appear on the autocomplete results anymore."), _(u"Confirm"), wx.YES_NO|wx.ICON_QUESTION)
|
||||||
|
if ask.ShowModal() == wx.ID_YES:
|
||||||
|
item = self.dialog.users.get_selected()
|
||||||
|
user = self.users[item]
|
||||||
|
self.database.remove_user(user[0])
|
||||||
|
self.update_list()
|
58
src/extra/autocompletionUsers/settings.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import storage
|
||||||
|
import wx
|
||||||
|
import config
|
||||||
|
import wx_settings
|
||||||
|
import manage
|
||||||
|
import output
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
|
|
||||||
|
class autocompletionSettings(object):
|
||||||
|
def __init__(self, window):
|
||||||
|
super(autocompletionSettings, self).__init__()
|
||||||
|
self.window = window
|
||||||
|
self.dialog = wx_settings.autocompletionSettingsDialog()
|
||||||
|
self.dialog.friends_buffer.SetValue(config.main["mysc"]["save_friends_in_autocompletion_db"])
|
||||||
|
self.dialog.followers_buffer.SetValue(config.main["mysc"]["save_followers_in_autocompletion_db"])
|
||||||
|
self.dialog.viewList.Bind(wx.EVT_BUTTON, self.view_list)
|
||||||
|
if self.dialog.ShowModal() == wx.ID_OK:
|
||||||
|
call_threaded(self.add_users_to_database)
|
||||||
|
|
||||||
|
def add_users_to_database(self):
|
||||||
|
config.main["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.friends_buffer.GetValue()
|
||||||
|
config.main["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.friends_buffer.GetValue()
|
||||||
|
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||||
|
database = storage.storage()
|
||||||
|
if self.dialog.followers_buffer.GetValue() == True:
|
||||||
|
buffer = self.window.search_buffer("people", "followers")
|
||||||
|
for i in buffer.db.settings[buffer.name_buffer]:
|
||||||
|
database.set_user(i["screen_name"], i["name"], 1)
|
||||||
|
else:
|
||||||
|
database.remove_by_buffer(1)
|
||||||
|
if self.dialog.friends_buffer.GetValue() == True:
|
||||||
|
buffer = self.window.search_buffer("people", "friends")
|
||||||
|
for i in buffer.db.settings[buffer.name_buffer]:
|
||||||
|
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.window)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_at_startup(window):
|
||||||
|
database = storage.storage()
|
||||||
|
if config.main["mysc"]["save_followers_in_autocompletion_db"] == True and config.main["other_buffers"]["show_followers"] == True:
|
||||||
|
buffer = window.search_buffer("people", "followers")
|
||||||
|
for i in buffer.db.settings[buffer.name_buffer]:
|
||||||
|
database.set_user(i["screen_name"], i["name"], 1)
|
||||||
|
else:
|
||||||
|
database.remove_by_buffer(1)
|
||||||
|
if config.main["mysc"]["save_friends_in_autocompletion_db"] == True and config.main["other_buffers"]["show_friends"] == True:
|
||||||
|
buffer = window.search_buffer("people", "friends")
|
||||||
|
for i in buffer.db.settings[buffer.name_buffer]:
|
||||||
|
database.set_user(i["screen_name"], i["name"], 2)
|
||||||
|
else:
|
||||||
|
database.remove_by_buffer(2)
|
53
src/extra/autocompletionUsers/storage.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sqlite3, paths
|
||||||
|
from sessionmanager import manager
|
||||||
|
|
||||||
|
class storage(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.connection = sqlite3.connect(paths.config_path("%s/autocompletionUsers.dat" % (manager.manager.get_current_session())))
|
||||||
|
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()
|
42
src/extra/autocompletionUsers/wx_manage.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
from multiplatform_widgets import widgets
|
||||||
|
|
||||||
|
class autocompletionManageDialog(wx.Dialog):
|
||||||
|
def __init__(self):
|
||||||
|
super(autocompletionManageDialog, self).__init__(parent=None, id=-1, title=_(u"Manage Autocomplete users’ database"))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
label = wx.StaticText(panel, -1, _(u"Editing TWBlue 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()
|
18
src/extra/autocompletionUsers/wx_menu.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class menu(wx.Menu):
|
||||||
|
def __init__(self, window, pattern):
|
||||||
|
super(menu, self).__init__()
|
||||||
|
self.window = window
|
||||||
|
self.pattern = pattern
|
||||||
|
|
||||||
|
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):
|
||||||
|
self.window.ChangeValue(self.window.GetValue().replace(self.pattern, text+" "))
|
||||||
|
self.window.SetInsertionPointEnd()
|
25
src/extra/autocompletionUsers/wx_settings.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class autocompletionSettingsDialog(wx.Dialog):
|
||||||
|
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"See the users list"))
|
||||||
|
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, _(u"TWBlue's database of users has been updated."), _(u"Done"), wx.OK).ShowModal()
|
@@ -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
|
|
@@ -7,4 +7,5 @@ from favourites import *
|
|||||||
from lists import *
|
from lists import *
|
||||||
from people import *
|
from people import *
|
||||||
from tweet_searches import *
|
from tweet_searches import *
|
||||||
from user_searches import *
|
from user_searches import *
|
||||||
|
from trends import *
|
@@ -18,6 +18,8 @@
|
|||||||
############################################################
|
############################################################
|
||||||
import wx
|
import wx
|
||||||
import gui.dialogs
|
import gui.dialogs
|
||||||
|
import arrow
|
||||||
|
import languageHandler
|
||||||
import twitter
|
import twitter
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import config
|
import config
|
||||||
@@ -27,7 +29,7 @@ import logging as original_logger
|
|||||||
import output
|
import output
|
||||||
import platform
|
import platform
|
||||||
import datetime
|
import datetime
|
||||||
from twitter import prettydate
|
import menus
|
||||||
from multiplatform_widgets import widgets
|
from multiplatform_widgets import widgets
|
||||||
from mysc import event
|
from mysc import event
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
@@ -39,6 +41,8 @@ class basePanel(wx.Panel):
|
|||||||
def bind_events(self):
|
def bind_events(self):
|
||||||
self.Bind(event.MyEVT_OBJECT, self.update)
|
self.Bind(event.MyEVT_OBJECT, self.update)
|
||||||
self.Bind(event.MyEVT_DELETED, self.Remove)
|
self.Bind(event.MyEVT_DELETED, self.Remove)
|
||||||
|
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.showMenu, self.list.list)
|
||||||
|
self.Bind(wx.EVT_LIST_KEY_DOWN, self.showMenuByKey, self.list.list)
|
||||||
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
|
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
|
||||||
if self.system == "Windows":
|
if self.system == "Windows":
|
||||||
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.onFocus)
|
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.onFocus)
|
||||||
@@ -147,14 +151,19 @@ class basePanel(wx.Panel):
|
|||||||
if config.main["general"]["relative_times"] == True:
|
if config.main["general"]["relative_times"] == True:
|
||||||
# On windows we need only put the new date on the column, but under linux and mac it isn't possible.
|
# On windows we need only put the new date on the column, but under linux and mac it isn't possible.
|
||||||
if self.system == "Windows":
|
if self.system == "Windows":
|
||||||
original_date = datetime.datetime.strptime(tweet["created_at"], "%a %b %d %H:%M:%S +0000 %Y")
|
# self.db.settings[self.name_buffer][self.list.get_selected()]["created_at"] = tweet["created_at"].replace("+0000 ", "")
|
||||||
date = original_date-datetime.timedelta(seconds=-self.db.settings["utc_offset"])
|
original_date = arrow.get(self.db.settings[self.name_buffer][self.list.get_selected()]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en")
|
||||||
ts = prettydate(original_date)
|
# original_date = datetime.datetime.strptime(self.db.settings[self.name_buffer][self.list.get_selected()]["created_at"], "%a %b %d %H:%M:%S +0000 %Y")
|
||||||
|
# date = original_date-datetime.timedelta(seconds=-self.db.settings["utc_offset"])
|
||||||
|
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||||
|
# prettydate(original_date)
|
||||||
self.list.list.SetStringItem(self.list.get_selected(), 2, ts)
|
self.list.list.SetStringItem(self.list.get_selected(), 2, ts)
|
||||||
else:
|
else:
|
||||||
self.list.list.SetString(self.list.get_selected(), " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)))
|
self.list.list.SetString(self.list.get_selected(), " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)))
|
||||||
if twitter.utils.is_audio(tweet):
|
if twitter.utils.is_audio(tweet):
|
||||||
sound.player.play("audio.ogg", False)
|
sound.player.play("audio.ogg", False)
|
||||||
|
if twitter.utils.is_geocoded(tweet):
|
||||||
|
sound.player.play("geo.ogg", False)
|
||||||
|
|
||||||
def start_streams(self):
|
def start_streams(self):
|
||||||
if self.name_buffer == "sent":
|
if self.name_buffer == "sent":
|
||||||
@@ -181,18 +190,21 @@ class basePanel(wx.Panel):
|
|||||||
except TwythonError as e:
|
except TwythonError as e:
|
||||||
output.speak(e.message)
|
output.speak(e.message)
|
||||||
for i in items:
|
for i in items:
|
||||||
if config.main["general"]["reverse_timelines"] == False:
|
if twitter.utils.is_allowed(i) == True:
|
||||||
self.db.settings[self.name_buffer].insert(0, i)
|
if config.main["general"]["reverse_timelines"] == False:
|
||||||
else:
|
self.db.settings[self.name_buffer].insert(0, i)
|
||||||
self.db.settings[self.name_buffer].append(i)
|
else:
|
||||||
|
self.db.settings[self.name_buffer].append(i)
|
||||||
if config.main["general"]["reverse_timelines"] == False:
|
if config.main["general"]["reverse_timelines"] == False:
|
||||||
for i in items:
|
for i in items:
|
||||||
tweet = self.compose_function(i, self.db)
|
if twitter.utils.is_allowed(i) == True:
|
||||||
self.list.insert_item(True, *tweet)
|
tweet = self.compose_function(i, self.db)
|
||||||
|
self.list.insert_item(True, *tweet)
|
||||||
else:
|
else:
|
||||||
for i in items:
|
for i in items:
|
||||||
tweet = self.compose_function(i, self.db)
|
if twitter.utils.is_allowed(i) == True:
|
||||||
self.list.insert_item(False, *tweet)
|
tweet = self.compose_function(i, self.db)
|
||||||
|
self.list.insert_item(False, *tweet)
|
||||||
output.speak(_(u"%s items retrieved") % (len(items)))
|
output.speak(_(u"%s items retrieved") % (len(items)))
|
||||||
|
|
||||||
def put_items(self, num):
|
def put_items(self, num):
|
||||||
@@ -200,7 +212,7 @@ class basePanel(wx.Panel):
|
|||||||
for i in self.db.settings[self.name_buffer]:
|
for i in self.db.settings[self.name_buffer]:
|
||||||
tweet = self.compose_function(i, self.db)
|
tweet = self.compose_function(i, self.db)
|
||||||
self.list.insert_item(False, *tweet)
|
self.list.insert_item(False, *tweet)
|
||||||
self.set_list_position()
|
self.set_list_position()
|
||||||
elif self.list.get_count() > 0:
|
elif self.list.get_count() > 0:
|
||||||
if config.main["general"]["reverse_timelines"] == False:
|
if config.main["general"]["reverse_timelines"] == False:
|
||||||
for i in self.db.settings[self.name_buffer][:num]:
|
for i in self.db.settings[self.name_buffer][:num]:
|
||||||
@@ -278,10 +290,15 @@ class basePanel(wx.Panel):
|
|||||||
if self.name_buffer in config.main["other_buffers"]["autoread_buffers"]:
|
if self.name_buffer in config.main["other_buffers"]["autoread_buffers"]:
|
||||||
output.speak(" ".join(tweet[:2]))
|
output.speak(" ".join(tweet[:2]))
|
||||||
|
|
||||||
|
def get_tweet(self):
|
||||||
|
""" Gets a tweet or retweet."""
|
||||||
|
if self.db.settings[self.name_buffer][self.list.get_selected()].has_key("retweeted_status"): tweet = self.db.settings[self.name_buffer][self.list.get_selected()]["retweeted_status"]
|
||||||
|
else: tweet = self.db.settings[self.name_buffer][self.list.get_selected()]
|
||||||
|
return tweet
|
||||||
|
|
||||||
def interact(self, ev):
|
def interact(self, ev):
|
||||||
try:
|
try:
|
||||||
if self.db.settings[self.name_buffer][self.list.get_selected()].has_key("retweeted_status"): tweet = self.db.settings[self.name_buffer][self.list.get_selected()]["retweeted_status"]
|
tweet = self.get_tweet()
|
||||||
else: tweet = self.db.settings[self.name_buffer][self.list.get_selected()]
|
|
||||||
urls = twitter.utils.find_urls_in_text(tweet["text"])
|
urls = twitter.utils.find_urls_in_text(tweet["text"])
|
||||||
except:
|
except:
|
||||||
urls = []
|
urls = []
|
||||||
@@ -297,14 +314,21 @@ class basePanel(wx.Panel):
|
|||||||
ev.Skip()
|
ev.Skip()
|
||||||
return
|
return
|
||||||
if event == "audio" and len(urls) > 0:
|
if event == "audio" and len(urls) > 0:
|
||||||
self.streamer(urls[0])
|
if len(urls) == 1:
|
||||||
|
self.streamer(urls[0])
|
||||||
|
elif len(urls) > 1:
|
||||||
|
urlList = gui.dialogs.urlList.urlList(urls)
|
||||||
|
if urlList.ShowModal() == wx.ID_OK:
|
||||||
|
self.streamer(urls[urlList.lista.GetSelection()])
|
||||||
elif event == "url":
|
elif event == "url":
|
||||||
if len(urls) == 0: return
|
if len(urls) == 0: return
|
||||||
elif len(urls) == 1:
|
elif len(urls) == 1:
|
||||||
output.speak(_(u"Opening URL..."), True)
|
output.speak(_(u"Opening URL..."), True)
|
||||||
webbrowser.open(urls[0])
|
webbrowser.open(urls[0])
|
||||||
elif len(urls) > 1:
|
elif len(urls) > 1:
|
||||||
gui.dialogs.urlList.urlList(urls).ShowModal()
|
urlList = gui.dialogs.urlList.urlList(urls)
|
||||||
|
if urlList.ShowModal() == wx.ID_OK:
|
||||||
|
webbrowser.open_new_tab(urls[urlList.lista.GetSelection()])
|
||||||
elif event == "volume_down":
|
elif event == "volume_down":
|
||||||
if config.main["sound"]["volume"] > 0.05:
|
if config.main["sound"]["volume"] > 0.05:
|
||||||
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
|
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
|
||||||
@@ -362,3 +386,18 @@ class basePanel(wx.Panel):
|
|||||||
self.list.select_item(len(self.db.settings[self.name_buffer])-1)
|
self.list.select_item(len(self.db.settings[self.name_buffer])-1)
|
||||||
else:
|
else:
|
||||||
self.list.select_item(0)
|
self.list.select_item(0)
|
||||||
|
|
||||||
|
def showMenu(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
if self.name_buffer == "sent":
|
||||||
|
self.PopupMenu(menus.sentPanelMenu(self), ev.GetPosition())
|
||||||
|
else:
|
||||||
|
self.PopupMenu(menus.basePanelMenu(self), ev.GetPosition())
|
||||||
|
|
||||||
|
def showMenuByKey(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||||
|
if self.name_buffer == "sent":
|
||||||
|
self.PopupMenu(menus.sentPanelMenu(self), self.list.list.GetPosition())
|
||||||
|
else:
|
||||||
|
self.PopupMenu(menus.basePanelMenu(self), self.list.list.GetPosition())
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
import wx
|
import wx
|
||||||
import sound
|
import sound
|
||||||
import gui.dialogs
|
import gui.dialogs
|
||||||
|
import menus
|
||||||
import logging as original_logger
|
import logging as original_logger
|
||||||
from base import basePanel
|
from base import basePanel
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
@@ -30,6 +31,7 @@ class dmPanel(basePanel):
|
|||||||
super(dmPanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
|
super(dmPanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
|
||||||
self.retweetBtn.Disable()
|
self.retweetBtn.Disable()
|
||||||
self.responseBtn.Disable()
|
self.responseBtn.Disable()
|
||||||
|
self.type = "direct_message"
|
||||||
|
|
||||||
def destroy_status(self, ev):
|
def destroy_status(self, ev):
|
||||||
index = self.list.get_selected()
|
index = self.list.get_selected()
|
||||||
@@ -45,4 +47,13 @@ class dmPanel(basePanel):
|
|||||||
if dlg.ShowModal() == wx.ID_OK:
|
if dlg.ShowModal() == wx.ID_OK:
|
||||||
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
|
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
|
||||||
if ev != None:
|
if ev != None:
|
||||||
self.list.list.SetFocus()
|
self.list.list.SetFocus()
|
||||||
|
|
||||||
|
def showMenu(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
self.PopupMenu(menus.dmPanelMenu(self), ev.GetPosition())
|
||||||
|
|
||||||
|
def showMenuByKey(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||||
|
self.PopupMenu(menus.dmPanelMenu(self), self.list.list.GetPosition())
|
||||||
|
@@ -21,6 +21,7 @@ import sound
|
|||||||
import config
|
import config
|
||||||
import platform
|
import platform
|
||||||
import gui.dialogs
|
import gui.dialogs
|
||||||
|
import menus
|
||||||
import output
|
import output
|
||||||
import logging as original_logger
|
import logging as original_logger
|
||||||
from multiplatform_widgets import widgets
|
from multiplatform_widgets import widgets
|
||||||
@@ -36,6 +37,8 @@ class eventsPanel(wx.Panel):
|
|||||||
|
|
||||||
def bind_events(self):
|
def bind_events(self):
|
||||||
self.Bind(event.MyEVT_OBJECT, self.update)
|
self.Bind(event.MyEVT_OBJECT, self.update)
|
||||||
|
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.showMenu, self.list.list)
|
||||||
|
self.Bind(wx.EVT_LIST_KEY_DOWN, self.showMenuByKey, self.list.list)
|
||||||
|
|
||||||
def put_items(self, items):
|
def put_items(self, items):
|
||||||
pass
|
pass
|
||||||
@@ -131,4 +134,13 @@ class eventsPanel(wx.Panel):
|
|||||||
try:
|
try:
|
||||||
ev.Skip()
|
ev.Skip()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def showMenu(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
self.PopupMenu(menus.eventsPanelMenu(self), ev.GetPosition())
|
||||||
|
|
||||||
|
def showMenuByKey(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||||
|
self.PopupMenu(menus.eventsPanelMenu(self), self.list.list.GetPosition())
|
||||||
|
@@ -30,7 +30,7 @@ class favsPanel(basePanel):
|
|||||||
self.type = "favourites_timeline"
|
self.type = "favourites_timeline"
|
||||||
|
|
||||||
def start_streams(self):
|
def start_streams(self):
|
||||||
num = twitter.starting.get_favourites_timeline(self.db, self.twitter, self.name_buffer, param=self.argumento, count=200)
|
num = twitter.starting.get_favourites_timeline(self.db, self.twitter, self.name_buffer, param=self.argumento, count=config.main["general"]["max_tweets_per_call"])
|
||||||
if self.sound != "" and num > 0:
|
if self.sound != "" and num > 0:
|
||||||
sound.player.play(self.sound)
|
sound.player.play(self.sound)
|
||||||
if self.list.get_count() > 0: self.put_items(num)
|
if self.list.get_count() > 0: self.put_items(num)
|
||||||
|
@@ -35,7 +35,7 @@ class listPanel(basePanel):
|
|||||||
|
|
||||||
def start_streams(self):
|
def start_streams(self):
|
||||||
self.retrieve_ids()
|
self.retrieve_ids()
|
||||||
num = twitter.starting.start_list(self.db, self.twitter, self.name_buffer, list_id=self.argumento)
|
num = twitter.starting.start_list(self.db, self.twitter, self.name_buffer, list_id=self.argumento, count=config.main["general"]["max_tweets_per_call"])
|
||||||
return num
|
return num
|
||||||
|
|
||||||
def retrieve_ids(self):
|
def retrieve_ids(self):
|
||||||
|
134
src/gui/buffers/menus.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class basePanelMenu(wx.Menu):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(basePanelMenu, self).__init__()
|
||||||
|
self.window = parent
|
||||||
|
retweet = wx.MenuItem(self, wx.NewId(), _(u"&Retweet"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.onRetweet, retweet)
|
||||||
|
self.AppendItem(retweet)
|
||||||
|
reply = wx.MenuItem(self, wx.NewId(), _(u"Re&ply"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.onResponse, reply)
|
||||||
|
self.AppendItem(reply)
|
||||||
|
fav = wx.MenuItem(self, wx.NewId(), _(u"Add to &favourites"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.fav, fav)
|
||||||
|
self.AppendItem(fav)
|
||||||
|
unfav = wx.MenuItem(self, wx.NewId(), _(u"Remove from favo&urites"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.unfav, unfav)
|
||||||
|
self.AppendItem(unfav)
|
||||||
|
openUrl = wx.MenuItem(self, wx.NewId(), _(u"&Open URL"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.url, openUrl)
|
||||||
|
self.AppendItem(openUrl)
|
||||||
|
play = wx.MenuItem(self, wx.NewId(), _(u"&Play audio"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.audio, play)
|
||||||
|
self.AppendItem(play)
|
||||||
|
view = wx.MenuItem(self, wx.NewId(), _(u"&Show tweet"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view, view)
|
||||||
|
self.AppendItem(view)
|
||||||
|
copy = wx.MenuItem(self, wx.NewId(), _(u"&Copy to clipboard"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.copy_to_clipboard, copy)
|
||||||
|
self.AppendItem(copy)
|
||||||
|
remove = wx.MenuItem(self, wx.NewId(), _(u"&Delete"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.delete, remove)
|
||||||
|
self.AppendItem(remove)
|
||||||
|
userActions = wx.MenuItem(self, wx.NewId(), _(u"&User actions..."))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.onFollow, userActions)
|
||||||
|
self.AppendItem(userActions)
|
||||||
|
|
||||||
|
class dmPanelMenu(wx.Menu):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(dmPanelMenu, self).__init__()
|
||||||
|
self.window = parent
|
||||||
|
reply = wx.MenuItem(self, wx.NewId(), _(u"Re&ply"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.onResponse, reply)
|
||||||
|
self.AppendItem(reply)
|
||||||
|
openUrl = wx.MenuItem(self, wx.NewId(), _(u"&Open URL"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.url, openUrl)
|
||||||
|
self.AppendItem(openUrl)
|
||||||
|
play = wx.MenuItem(self, wx.NewId(), _(u"&Play audio"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.audio, play)
|
||||||
|
self.AppendItem(play)
|
||||||
|
view = wx.MenuItem(self, wx.NewId(), _(u"&Show direct message"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view, view)
|
||||||
|
self.AppendItem(view)
|
||||||
|
copy = wx.MenuItem(self, wx.NewId(), _(u"&Copy to clipboard"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.copy_to_clipboard, copy)
|
||||||
|
self.AppendItem(copy)
|
||||||
|
remove = wx.MenuItem(self, wx.NewId(), _(u"&Delete"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.delete, remove)
|
||||||
|
self.AppendItem(remove)
|
||||||
|
userActions = wx.MenuItem(self, wx.NewId(), _(u"&User actions..."))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.onFollow, userActions)
|
||||||
|
self.AppendItem(userActions)
|
||||||
|
|
||||||
|
class sentPanelMenu(wx.Menu):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(sentPanelMenu, self).__init__()
|
||||||
|
self.window = parent
|
||||||
|
openUrl = wx.MenuItem(self, wx.NewId(), _(u"&Open URL"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.url, openUrl)
|
||||||
|
self.AppendItem(openUrl)
|
||||||
|
play = wx.MenuItem(self, wx.NewId(), _(u"&Play audio"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.audio, play)
|
||||||
|
self.AppendItem(play)
|
||||||
|
view = wx.MenuItem(self, wx.NewId(), _(u"&Show tweet"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view, view)
|
||||||
|
self.AppendItem(view)
|
||||||
|
copy = wx.MenuItem(self, wx.NewId(), _(u"&Copy to clipboard"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.copy_to_clipboard, copy)
|
||||||
|
self.AppendItem(copy)
|
||||||
|
remove = wx.MenuItem(self, wx.NewId(), _(u"&Delete"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.delete, remove)
|
||||||
|
self.AppendItem(remove)
|
||||||
|
|
||||||
|
class eventsPanelMenu(wx.Menu):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(eventsPanelMenu, self).__init__()
|
||||||
|
self.window = parent
|
||||||
|
view = wx.MenuItem(self, wx.NewId(), _(u"&Show event"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view, view)
|
||||||
|
self.AppendItem(view)
|
||||||
|
copy = wx.MenuItem(self, wx.NewId(), _(u"&Copy to clipboard"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.copy_to_clipboard, copy)
|
||||||
|
self.AppendItem(copy)
|
||||||
|
remove = wx.MenuItem(self, wx.NewId(), _(u"&Delete"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.delete, remove)
|
||||||
|
self.AppendItem(remove)
|
||||||
|
|
||||||
|
class peoplePanelMenu(wx.Menu):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(peoplePanelMenu, self).__init__()
|
||||||
|
self.window = parent
|
||||||
|
reply = wx.MenuItem(self, wx.NewId(), _(u"&Mention"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.onResponse, reply)
|
||||||
|
self.AppendItem(reply)
|
||||||
|
lists = wx.MenuItem(self, wx.NewId(), _(u"&View lists"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view_user_lists, lists)
|
||||||
|
self.AppendItem(lists)
|
||||||
|
details = wx.MenuItem(self, wx.NewId(), _(u"Show user &profile"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.details, details)
|
||||||
|
self.AppendItem(details)
|
||||||
|
view = wx.MenuItem(self, wx.NewId(), _(u"&Show user"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view, view)
|
||||||
|
self.AppendItem(view)
|
||||||
|
copy = wx.MenuItem(self, wx.NewId(), _(u"&Copy to clipboard"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.copy_to_clipboard, copy)
|
||||||
|
self.AppendItem(copy)
|
||||||
|
userActions = wx.MenuItem(self, wx.NewId(), _(u"&User actions..."))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.onFollow, userActions)
|
||||||
|
self.AppendItem(userActions)
|
||||||
|
|
||||||
|
class trendsPanelMenu(wx.Menu):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(trendsPanelMenu, self).__init__()
|
||||||
|
self.window = parent
|
||||||
|
tweetThisTrend = wx.MenuItem(self, wx.NewId(), _(u"&Tweet about this trend"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.onResponse, tweetThisTrend)
|
||||||
|
self.AppendItem(tweetThisTrend)
|
||||||
|
view = wx.MenuItem(self, wx.NewId(), _(u"&Show item"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.view, view)
|
||||||
|
self.AppendItem(view)
|
||||||
|
copy = wx.MenuItem(self, wx.NewId(), _(u"&Copy to clipboard"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.window.parent.copy_to_clipboard, copy)
|
||||||
|
self.AppendItem(copy)
|
@@ -20,18 +20,20 @@ import wx
|
|||||||
from multiplatform_widgets import widgets
|
from multiplatform_widgets import widgets
|
||||||
|
|
||||||
class accountPanel(wx.Panel):
|
class accountPanel(wx.Panel):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent, name_buffer):
|
||||||
super(accountPanel, self).__init__(parent=parent)
|
super(accountPanel, self).__init__(parent=parent)
|
||||||
self.type = "account"
|
self.type = "account"
|
||||||
|
self.name_buffer = name_buffer
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.list = widgets.list(self, _(u"Announce"))
|
self.list = widgets.list(self, _(u"Announce"))
|
||||||
sizer.Add(self.list.list, 0, wx.ALL, 5)
|
sizer.Add(self.list.list, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
|
def get_more_items(self):
|
||||||
|
output.speak(_(u"This action is not supported for this buffer"))
|
||||||
|
|
||||||
class emptyPanel(accountPanel):
|
class emptyPanel(accountPanel):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(emptyPanel, self).__init__(parent=parent)
|
super(emptyPanel, self).__init__(parent=parent, name_buffer="")
|
||||||
self.type = "empty"
|
self.type = "empty"
|
||||||
|
|
||||||
def get_more_items(self):
|
|
||||||
output.speak(_(u"This action is not supported for this buffer"))
|
|
@@ -21,6 +21,7 @@ import sound
|
|||||||
import config
|
import config
|
||||||
import twitter
|
import twitter
|
||||||
import gui.dialogs
|
import gui.dialogs
|
||||||
|
import menus
|
||||||
import logging as original_logger
|
import logging as original_logger
|
||||||
import output
|
import output
|
||||||
from multiplatform_widgets import widgets
|
from multiplatform_widgets import widgets
|
||||||
@@ -36,13 +37,15 @@ class peoplePanel(basePanel):
|
|||||||
self.Bind(event.MyEVT_OBJECT, self.update)
|
self.Bind(event.MyEVT_OBJECT, self.update)
|
||||||
self.Bind(event.MyEVT_DELETED, self.Remove)
|
self.Bind(event.MyEVT_DELETED, self.Remove)
|
||||||
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
|
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
|
||||||
|
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.showMenu, self.list.list)
|
||||||
|
self.Bind(wx.EVT_LIST_KEY_DOWN, self.showMenuByKey, self.list.list)
|
||||||
|
|
||||||
def create_list(self):
|
def create_list(self):
|
||||||
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
|
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
|
||||||
|
|
||||||
def __init__(self, parent, window, name_buffer, function, argumento=None, sound="", timeline=False):
|
def __init__(self, parent, window, name_buffer, function, argumento=None, sound="", timeline=False):
|
||||||
self.type = "people"
|
|
||||||
super(peoplePanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
|
super(peoplePanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
|
||||||
|
self.type = "people"
|
||||||
self.responseBtn.SetLabel(_(u"Mention"))
|
self.responseBtn.SetLabel(_(u"Mention"))
|
||||||
self.retweetBtn.Disable()
|
self.retweetBtn.Disable()
|
||||||
self.compose_function = twitter.compose.compose_followers_list
|
self.compose_function = twitter.compose.compose_followers_list
|
||||||
@@ -140,3 +143,18 @@ class peoplePanel(basePanel):
|
|||||||
def remove_buffer(self):
|
def remove_buffer(self):
|
||||||
pos = None
|
pos = None
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
|
def get_message(self, dialog=False):
|
||||||
|
if dialog == False: return " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db))
|
||||||
|
else:
|
||||||
|
list = self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)
|
||||||
|
return " ".join(list)
|
||||||
|
|
||||||
|
def showMenu(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
self.PopupMenu(menus.peoplePanelMenu(self), ev.GetPosition())
|
||||||
|
|
||||||
|
def showMenuByKey(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||||
|
self.PopupMenu(menus.peoplePanelMenu(self), self.list.list.GetPosition())
|
||||||
|
@@ -17,35 +17,155 @@
|
|||||||
#
|
#
|
||||||
############################################################
|
############################################################
|
||||||
import wx
|
import wx
|
||||||
import sound
|
|
||||||
import config
|
|
||||||
import twitter
|
|
||||||
import gui.dialogs
|
import gui.dialogs
|
||||||
|
import twitter
|
||||||
|
import config
|
||||||
|
import sound
|
||||||
import logging as original_logger
|
import logging as original_logger
|
||||||
from base import basePanel
|
import output
|
||||||
|
import platform
|
||||||
|
import menus
|
||||||
|
from multiplatform_widgets import widgets
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
log = original_logger.getLogger("buffers.base")
|
log = original_logger.getLogger("buffers.base")
|
||||||
|
|
||||||
class trendPanel(basePanel):
|
class trendsPanel(wx.Panel):
|
||||||
def __init__(self, parent, window, name_buffer, *args, **kwargs):
|
|
||||||
super(searchPanel, self).__init__(parent, window, name_buffer, sound)
|
def compose_function(self, trend):
|
||||||
self.type = "trend"
|
return [trend["name"]]
|
||||||
self.args = kwargs
|
|
||||||
|
|
||||||
def start_streams(self):
|
def bind_events(self):
|
||||||
num = twitter.starting.search(self.db, self.twitter, self.name_buffer, **self.args)
|
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.showMenu, self.list.list)
|
||||||
if num > 0: sound.player.play("search_updated.ogg")
|
self.Bind(wx.EVT_LIST_KEY_DOWN, self.showMenuByKey, self.list.list)
|
||||||
self.put_items(num)
|
self.list.list.Bind(wx.EVT_CHAR_HOOK, self.interact)
|
||||||
return num
|
|
||||||
|
def get_message(self, dialog=False):
|
||||||
|
return self.compose_function(self.trends[self.list.get_selected()])[0]
|
||||||
|
|
||||||
|
|
||||||
|
def create_list(self):
|
||||||
|
self.list = widgets.list(self, _(u"Trending topic"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
|
||||||
|
if self.system == "Windows":
|
||||||
|
self.list.set_windows_size(0, 30)
|
||||||
|
self.list.set_size()
|
||||||
|
|
||||||
|
def __init__(self, parent, window, name_buffer, argumento=None, sound=""):
|
||||||
|
self.type = "trends"
|
||||||
|
self.twitter = window.twitter
|
||||||
|
self.name_buffer = name_buffer
|
||||||
|
self.argumento = argumento
|
||||||
|
self.sound = sound
|
||||||
|
self.parent = window
|
||||||
|
self.system = platform.system()
|
||||||
|
wx.Panel.__init__(self, parent)
|
||||||
|
self.trends = []
|
||||||
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.create_list()
|
||||||
|
self.btn = wx.Button(self, -1, _(u"Tweet"))
|
||||||
|
self.btn.Bind(wx.EVT_BUTTON, self.post_status)
|
||||||
|
self.tweetTrendBtn = wx.Button(self, -1, _(u"Tweet about this trend"))
|
||||||
|
self.tweetTrendBtn.Bind(wx.EVT_BUTTON, self.onResponse)
|
||||||
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
btnSizer.Add(self.btn, 0, wx.ALL, 5)
|
||||||
|
btnSizer.Add(self.tweetTrendBtn, 0, wx.ALL, 5)
|
||||||
|
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
|
||||||
|
self.sizer.Add(self.list.list, 0, wx.ALL, 5)
|
||||||
|
self.bind_events()
|
||||||
|
self.SetSizer(self.sizer)
|
||||||
|
|
||||||
def remove_buffer(self):
|
def remove_buffer(self):
|
||||||
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this search term?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
|
dlg = wx.MessageDialog(self, _(u"Do you really want to delete this buffer?"), _(u"Attention"), style=wx.ICON_QUESTION|wx.YES_NO)
|
||||||
if dlg.ShowModal() == wx.ID_YES:
|
if dlg.ShowModal() == wx.ID_YES:
|
||||||
names = config.main["other_buffers"]["tweet_searches"]
|
topics = config.main["other_buffers"]["trending_topic_buffers"]
|
||||||
user = self.name_buffer[:-7]
|
topic = self.name_buffer[:-3]
|
||||||
log.info(u"Deleting %s's search term" % user)
|
log.info(u"Deleting %s's trending topics buffer" % topic)
|
||||||
if user in names:
|
if topic in topics:
|
||||||
names.remove(user)
|
topics.remove(topic)
|
||||||
self.db.settings.pop(self.name_buffer)
|
return 0
|
||||||
pos = self.db.settings["buffers"].index(self.name_buffer)
|
|
||||||
self.db.settings["buffers"].remove(self.name_buffer)
|
def start_streams(self):
|
||||||
return pos
|
data = self.twitter.twitter.get_place_trends(id=self.argumento)
|
||||||
|
if not hasattr(self, "name"):
|
||||||
|
self.name = data[0]["locations"][0]["name"]
|
||||||
|
self.trends = data[0]["trends"]
|
||||||
|
sound.player.play(self.sound)
|
||||||
|
return len(self.trends)
|
||||||
|
|
||||||
|
def get_more_items(self):
|
||||||
|
output.speak(_(u"This action is not supported for this buffer"))
|
||||||
|
|
||||||
|
def put_items(self, num):
|
||||||
|
selected_item = self.list.get_selected()
|
||||||
|
self.list.clear()
|
||||||
|
for i in self.trends:
|
||||||
|
tweet = self.compose_function(i)
|
||||||
|
self.list.insert_item(False, *tweet)
|
||||||
|
self.set_list_position()
|
||||||
|
self.list.select_item(selected_item)
|
||||||
|
|
||||||
|
def post_status(self, ev=None):
|
||||||
|
text = gui.dialogs.message.tweet(_(u"Write the tweet here"), _(u"Tweet"), "", self)
|
||||||
|
if text.ShowModal() == wx.ID_OK:
|
||||||
|
if text.image == None:
|
||||||
|
call_threaded(self.twitter.api_call, call_name="update_status", _sound="tweet_send.ogg", status=text.text.GetValue())
|
||||||
|
else:
|
||||||
|
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="tweet_send.ogg", status=text.text.GetValue(), media=text.file)
|
||||||
|
if ev != None: self.list.list.SetFocus()
|
||||||
|
|
||||||
|
def onRetweet(self, event=None): pass
|
||||||
|
|
||||||
|
def onResponse(self, ev):
|
||||||
|
trend = self.trends[self.list.get_selected()]["name"]
|
||||||
|
text = gui.dialogs.message.tweet(_(u"Write the tweet here"), _(u"Tweet"), trend+" ", self)
|
||||||
|
if text.ShowModal() == wx.ID_OK:
|
||||||
|
if text.image == None:
|
||||||
|
call_threaded(self.twitter.api_call, call_name="update_status", _sound="tweet_send.ogg", status=text.text.GetValue())
|
||||||
|
else:
|
||||||
|
call_threaded(self.twitter.api_call, call_name="update_status_with_media", _sound="tweet_send.ogg", status=text.text.GetValue(), media=text.file)
|
||||||
|
if ev != None: self.list.list.SetFocus()
|
||||||
|
|
||||||
|
def interact(self, ev):
|
||||||
|
if type(ev) is str: event = ev
|
||||||
|
else:
|
||||||
|
if ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
||||||
|
else:
|
||||||
|
ev.Skip()
|
||||||
|
return
|
||||||
|
if event == "volume_down":
|
||||||
|
if config.main["sound"]["volume"] > 0.05:
|
||||||
|
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
|
||||||
|
sound.player.play("volume_changed.ogg", False)
|
||||||
|
if hasattr(self.parent, "audioStream"):
|
||||||
|
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
|
||||||
|
elif event == "volume_up":
|
||||||
|
if config.main["sound"]["volume"] < 0.95:
|
||||||
|
config.main["sound"]["volume"] = config.main["sound"]["volume"]+0.05
|
||||||
|
sound.player.play("volume_changed.ogg", False)
|
||||||
|
if hasattr(self.parent, "audioStream"):
|
||||||
|
self.parent.audioStream.stream.volume = config.main["sound"]["volume"]
|
||||||
|
elif event == "clear_list" and self.list.get_count() > 0:
|
||||||
|
dlg = wx.MessageDialog(self, _(u"Do you really want to empty this buffer? It's items will be removed from the list"), _(u"Empty buffer"), wx.ICON_QUESTION|wx.YES_NO)
|
||||||
|
if dlg.ShowModal() == wx.ID_YES:
|
||||||
|
self.trends = []
|
||||||
|
self.list.clear()
|
||||||
|
try:
|
||||||
|
ev.Skip()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_list_position(self):
|
||||||
|
if config.main["general"]["reverse_timelines"] == False:
|
||||||
|
self.list.select_item(len(self.trends)-1)
|
||||||
|
else:
|
||||||
|
self.list.select_item(0)
|
||||||
|
|
||||||
|
def showMenu(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
self.PopupMenu(menus.trendsPanelMenu(self), ev.GetPosition())
|
||||||
|
|
||||||
|
def showMenuByKey(self, ev):
|
||||||
|
if self.list.get_count() == 0: return
|
||||||
|
if ev.GetKeyCode() == wx.WXK_WINDOWS_MENU:
|
||||||
|
self.PopupMenu(menus.trendsPanelMenu(self), self.list.list.GetPosition())
|
||||||
|
@@ -40,7 +40,7 @@ class searchUsersPanel(peoplePanel):
|
|||||||
self.create_list()
|
self.create_list()
|
||||||
self.args = args
|
self.args = args
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
self.type = "timeline"
|
self.type = "user_search"
|
||||||
|
|
||||||
def start_streams(self):
|
def start_streams(self):
|
||||||
num = twitter.starting.search_users(self.db, self.twitter, self.name_buffer, **self.kwargs)
|
num = twitter.starting.search_users(self.db, self.twitter, self.name_buffer, **self.kwargs)
|
||||||
|
@@ -1 +1 @@
|
|||||||
import message, urlList, follow, utils, show_user, update_profile, configuration, lists, search
|
import message, urlList, follow, utils, show_user, update_profile, configuration, lists, search, trending
|
@@ -29,6 +29,7 @@ import webbrowser
|
|||||||
import paths
|
import paths
|
||||||
import platform
|
import platform
|
||||||
from mysc import restart
|
from mysc import restart
|
||||||
|
from extra.autocompletionUsers import settings
|
||||||
log = original_logger.getLogger("configuration")
|
log = original_logger.getLogger("configuration")
|
||||||
|
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
@@ -50,9 +51,13 @@ class general(wx.Panel):
|
|||||||
langBox.Add(language, 0, wx.ALL, 5)
|
langBox.Add(language, 0, wx.ALL, 5)
|
||||||
langBox.Add(self.language, 0, wx.ALL, 5)
|
langBox.Add(self.language, 0, wx.ALL, 5)
|
||||||
sizer.Add(langBox, 0, wx.ALL, 5)
|
sizer.Add(langBox, 0, wx.ALL, 5)
|
||||||
|
self.au = wx.Button(self, -1, _(u"Set the autocomplete function"))
|
||||||
self.ask_at_exit = wx.CheckBox(self, -1, _(U"ask before exiting TwBlue?"))
|
self.ask_at_exit = wx.CheckBox(self, -1, _(U"ask before exiting TwBlue?"))
|
||||||
self.ask_at_exit.SetValue(config.main["general"]["ask_at_exit"])
|
self.ask_at_exit.SetValue(config.main["general"]["ask_at_exit"])
|
||||||
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
|
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
|
||||||
|
self.use_invisible_shorcuts = wx.CheckBox(self, -1, _(u"Use invisible interface's keyboard shorcuts on the GUI"))
|
||||||
|
self.use_invisible_shorcuts.SetValue(config.main["general"]["use_invisible_keyboard_shorcuts"])
|
||||||
|
sizer.Add(self.use_invisible_shorcuts, 0, wx.ALL, 5)
|
||||||
self.relative_time = wx.CheckBox(self, -1, _(U"Relative times"))
|
self.relative_time = wx.CheckBox(self, -1, _(U"Relative times"))
|
||||||
self.relative_time.SetValue(config.main["general"]["relative_times"])
|
self.relative_time.SetValue(config.main["general"]["relative_times"])
|
||||||
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
||||||
@@ -84,6 +89,7 @@ class general(wx.Panel):
|
|||||||
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
|
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
|
|
||||||
class other_buffers(wx.Panel):
|
class other_buffers(wx.Panel):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
wx.Panel.__init__(self, parent)
|
wx.Panel.__init__(self, parent)
|
||||||
@@ -264,6 +270,7 @@ class configurationDialog(wx.Dialog):
|
|||||||
self.general = general(notebook)
|
self.general = general(notebook)
|
||||||
notebook.AddPage(self.general, _(u"General"))
|
notebook.AddPage(self.general, _(u"General"))
|
||||||
self.general.SetFocus()
|
self.general.SetFocus()
|
||||||
|
self.Bind(wx.EVT_BUTTON, self.autocompletion, self.general.au)
|
||||||
self.buffers = other_buffers(notebook)
|
self.buffers = other_buffers(notebook)
|
||||||
notebook.AddPage(self.buffers, _(u"Show other buffers"))
|
notebook.AddPage(self.buffers, _(u"Show other buffers"))
|
||||||
self.ignored_clients = ignoredClients(notebook)
|
self.ignored_clients = ignoredClients(notebook)
|
||||||
@@ -285,6 +292,9 @@ class configurationDialog(wx.Dialog):
|
|||||||
panel.SetSizer(sizer)
|
panel.SetSizer(sizer)
|
||||||
self.SetClientSize(sizer.CalcMin())
|
self.SetClientSize(sizer.CalcMin())
|
||||||
|
|
||||||
|
def autocompletion(self, ev):
|
||||||
|
configuration = settings.autocompletionSettings(self.parent)
|
||||||
|
|
||||||
def check_followers_change(self):
|
def check_followers_change(self):
|
||||||
if self.buffers.followers.GetValue() != self.buffers.followers_value:
|
if self.buffers.followers.GetValue() != self.buffers.followers_value:
|
||||||
if self.buffers.followers.GetValue() == True:
|
if self.buffers.followers.GetValue() == True:
|
||||||
@@ -376,6 +386,13 @@ class configurationDialog(wx.Dialog):
|
|||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
config.main["general"]["voice_enabled"] = self.general.disable_sapi5.GetValue()
|
config.main["general"]["voice_enabled"] = self.general.disable_sapi5.GetValue()
|
||||||
config.main["general"]["ask_at_exit"] = self.general.ask_at_exit.GetValue()
|
config.main["general"]["ask_at_exit"] = self.general.ask_at_exit.GetValue()
|
||||||
|
if (self.general.use_invisible_shorcuts.GetValue() == True and config.main["general"]["use_invisible_keyboard_shorcuts"] != True) and self.parent.showing == True:
|
||||||
|
km = self.parent.create_invisible_keyboard_shorcuts()
|
||||||
|
self.parent.register_invisible_keyboard_shorcuts(km)
|
||||||
|
elif (self.general.use_invisible_shorcuts.GetValue() == False and config.main["general"]["use_invisible_keyboard_shorcuts"] != False) and self.parent.showing == True:
|
||||||
|
km = self.parent.create_invisible_keyboard_shorcuts()
|
||||||
|
self.parent.unregister_invisible_keyboard_shorcuts(km)
|
||||||
|
config.main["general"]["use_invisible_keyboard_shorcuts"] = self.general.use_invisible_shorcuts.GetValue()
|
||||||
config.main["general"]["hide_gui"] = self.general.show_gui.GetValue()
|
config.main["general"]["hide_gui"] = self.general.show_gui.GetValue()
|
||||||
config.main["general"]["max_api_calls"] = self.general.apiCalls.GetValue()
|
config.main["general"]["max_api_calls"] = self.general.apiCalls.GetValue()
|
||||||
config.main["general"]["max_tweets_per_call"] = self.general.itemsPerApiCall.GetValue()
|
config.main["general"]["max_tweets_per_call"] = self.general.itemsPerApiCall.GetValue()
|
||||||
|
@@ -23,6 +23,7 @@ import twitter
|
|||||||
from twitter import utils
|
from twitter import utils
|
||||||
from twython import TwythonError
|
from twython import TwythonError
|
||||||
import output
|
import output
|
||||||
|
import re
|
||||||
|
|
||||||
class follow(wx.Dialog):
|
class follow(wx.Dialog):
|
||||||
def __init__(self, parent, default="follow"):
|
def __init__(self, parent, default="follow"):
|
||||||
@@ -48,6 +49,7 @@ class follow(wx.Dialog):
|
|||||||
self.block = wx.RadioButton(panel, -1, _(u"Block"))
|
self.block = wx.RadioButton(panel, -1, _(u"Block"))
|
||||||
self.unblock = wx.RadioButton(panel, -1, _(u"Unblock"))
|
self.unblock = wx.RadioButton(panel, -1, _(u"Unblock"))
|
||||||
self.reportSpam = wx.RadioButton(panel, -1, _(u"Report as spam"))
|
self.reportSpam = wx.RadioButton(panel, -1, _(u"Report as spam"))
|
||||||
|
self.ignore_client = wx.RadioButton(panel, -1, _(u"Ignore tweets from this client"))
|
||||||
self.setup_default(default)
|
self.setup_default(default)
|
||||||
actionSizer.Add(label2)
|
actionSizer.Add(label2)
|
||||||
actionSizer.Add(self.follow)
|
actionSizer.Add(self.follow)
|
||||||
@@ -57,6 +59,7 @@ class follow(wx.Dialog):
|
|||||||
actionSizer.Add(self.block)
|
actionSizer.Add(self.block)
|
||||||
actionSizer.Add(self.unblock)
|
actionSizer.Add(self.unblock)
|
||||||
actionSizer.Add(self.reportSpam)
|
actionSizer.Add(self.reportSpam)
|
||||||
|
actionSizer.Add(self.ignore_client)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
||||||
ok.Bind(wx.EVT_BUTTON, self.onok)
|
ok.Bind(wx.EVT_BUTTON, self.onok)
|
||||||
@@ -133,6 +136,16 @@ class follow(wx.Dialog):
|
|||||||
self.Destroy()
|
self.Destroy()
|
||||||
except TwythonError as err:
|
except TwythonError as err:
|
||||||
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
output.speak("Error %s: %s" % (err.error_code, err.msg), True)
|
||||||
|
elif self.ignore_client.GetValue() == True:
|
||||||
|
tweet = self.parent.get_tweet()
|
||||||
|
if tweet.has_key("sender"):
|
||||||
|
output.speak(_(u"You can't ignore direct messages"))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
client = re.sub(r"(?s)<.*?>", "", tweet["source"])
|
||||||
|
if client not in config.main["twitter"]["ignored_clients"]:
|
||||||
|
config.main["twitter"]["ignored_clients"].append(client)
|
||||||
|
self.Destroy()
|
||||||
|
|
||||||
def setup_default(self, default):
|
def setup_default(self, default):
|
||||||
if default == "follow":
|
if default == "follow":
|
||||||
|
@@ -127,7 +127,7 @@ class listViewer(wx.Dialog):
|
|||||||
output.speak(_(u"This list is arready opened."))
|
output.speak(_(u"This list is arready opened."))
|
||||||
return
|
return
|
||||||
listUI = gui.buffers.lists.listPanel(self.nb, self.parent, list_updated["slug"]+"-list", argumento=utils.find_list(list_updated["slug"], self.db.settings["lists"]))
|
listUI = gui.buffers.lists.listPanel(self.nb, self.parent, list_updated["slug"]+"-list", argumento=utils.find_list(list_updated["slug"], self.db.settings["lists"]))
|
||||||
self.nb.AddPage(listUI, _(u"List for %s") % (list_updated["slug"],))
|
self.nb.InsertSubPage(self.db.settings["buffers"].index("lists"), listUI, _(u"List for %s") % (list_updated["slug"],))
|
||||||
self.db.settings["buffers"].append(list_updated["slug"]+"-list")
|
self.db.settings["buffers"].append(list_updated["slug"]+"-list")
|
||||||
num = listUI.start_streams()
|
num = listUI.start_streams()
|
||||||
listUI.put_items(num)
|
listUI.put_items(num)
|
||||||
@@ -165,6 +165,14 @@ class userListViewer(listViewer):
|
|||||||
self.db.settings["lists"].append(list)
|
self.db.settings["lists"].append(list)
|
||||||
except TwythonError as e:
|
except TwythonError as e:
|
||||||
output.speak("error %s: %s" % (e.status_code, e.msg))
|
output.speak("error %s: %s" % (e.status_code, e.msg))
|
||||||
|
def onDelete(self, event=None):
|
||||||
|
list_id = self.lists[self.lista.get_selected()]["id"]
|
||||||
|
try:
|
||||||
|
list = self.twitter.twitter.unsubscribe_from_list(list_id=list_id)
|
||||||
|
item = utils.find_item(list["id"], self.db.settings["lists"])
|
||||||
|
self.db.settings["lists"].remove(list)
|
||||||
|
except TwythonError as e:
|
||||||
|
output.speak("error %s: %s" % (e.msg))
|
||||||
|
|
||||||
class createListDialog(wx.Dialog):
|
class createListDialog(wx.Dialog):
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ from twython import TwythonError
|
|||||||
from extra import translator, AudioUploader
|
from extra import translator, AudioUploader
|
||||||
import platform
|
import platform
|
||||||
from extra.AudioUploader import transfer
|
from extra.AudioUploader import transfer
|
||||||
|
from extra.autocompletionUsers import completion
|
||||||
if platform.system() != "Darwin":
|
if platform.system() != "Darwin":
|
||||||
from extra.AudioUploader import dropbox_transfer
|
from extra.AudioUploader import dropbox_transfer
|
||||||
from extra.SpellChecker import gui as spellCheckerGUI
|
from extra.SpellChecker import gui as spellCheckerGUI
|
||||||
@@ -183,6 +184,8 @@ class tweet(textLimited):
|
|||||||
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Send"), size=wx.DefaultSize)
|
self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Send"), size=wx.DefaultSize)
|
||||||
self.okButton.Bind(wx.EVT_BUTTON, self.onSend)
|
self.okButton.Bind(wx.EVT_BUTTON, self.onSend)
|
||||||
self.okButton.SetDefault()
|
self.okButton.SetDefault()
|
||||||
|
autocompletionButton = wx.Button(self.panel, -1, _(u"&Autocomplete users"))
|
||||||
|
self.Bind(wx.EVT_BUTTON, self.autocompletion, autocompletionButton)
|
||||||
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"), size=wx.DefaultSize)
|
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"), size=wx.DefaultSize)
|
||||||
cancelButton.Bind(wx.EVT_BUTTON, self.onCancel)
|
cancelButton.Bind(wx.EVT_BUTTON, self.onCancel)
|
||||||
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
@@ -202,8 +205,11 @@ class tweet(textLimited):
|
|||||||
self.mainBox.Add(self.ok_cancelSizer)
|
self.mainBox.Add(self.ok_cancelSizer)
|
||||||
selectId = wx.NewId()
|
selectId = wx.NewId()
|
||||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
||||||
|
# autocompletionId = wx.NewId()
|
||||||
|
# self.Bind(wx.EVT_MENU, self.autocompletion, id=autocompletionId)
|
||||||
self.accel_tbl = wx.AcceleratorTable([
|
self.accel_tbl = wx.AcceleratorTable([
|
||||||
(wx.ACCEL_CTRL, ord('A'), selectId),
|
(wx.ACCEL_CTRL, ord('A'), selectId),
|
||||||
|
#(wx.ACCEL_ALT, ord('A'), autocompletionId),
|
||||||
])
|
])
|
||||||
self.SetAcceleratorTable(self.accel_tbl)
|
self.SetAcceleratorTable(self.accel_tbl)
|
||||||
self.panel.SetSizer(self.mainBox)
|
self.panel.SetSizer(self.mainBox)
|
||||||
@@ -215,6 +221,10 @@ class tweet(textLimited):
|
|||||||
self.onTimer(wx.EVT_CHAR_HOOK)
|
self.onTimer(wx.EVT_CHAR_HOOK)
|
||||||
self.SetClientSize(self.mainBox.CalcMin())
|
self.SetClientSize(self.mainBox.CalcMin())
|
||||||
|
|
||||||
|
def autocompletion(self, event=None):
|
||||||
|
c = completion.autocompletionUsers(self)
|
||||||
|
c.show_menu()
|
||||||
|
|
||||||
def onUpload_image(self, ev):
|
def onUpload_image(self, ev):
|
||||||
if self.upload_image.GetLabel() == _(u"Discard image"):
|
if self.upload_image.GetLabel() == _(u"Discard image"):
|
||||||
self.image = None
|
self.image = None
|
||||||
@@ -305,7 +315,7 @@ class reply(tweet):
|
|||||||
super(reply, self).__init__(message, title, text, parent)
|
super(reply, self).__init__(message, title, text, parent)
|
||||||
self.in_reply_to = parent.db.settings[parent.name_buffer][parent.list.get_selected()]["id"]
|
self.in_reply_to = parent.db.settings[parent.name_buffer][parent.list.get_selected()]["id"]
|
||||||
self.text.SetInsertionPoint(len(self.text.GetValue()))
|
self.text.SetInsertionPoint(len(self.text.GetValue()))
|
||||||
self.mentionAll = wx.Button(self, -1, _(u"Mention to all"), size=wx.DefaultSize)
|
self.mentionAll = wx.Button(self, -1, _(u"Men&tion all"), size=wx.DefaultSize)
|
||||||
self.mentionAll.Disable()
|
self.mentionAll.Disable()
|
||||||
self.mentionAll.Bind(wx.EVT_BUTTON, self.mentionAllUsers)
|
self.mentionAll.Bind(wx.EVT_BUTTON, self.mentionAllUsers)
|
||||||
self.buttonsBox1.Add(self.mentionAll, 0, wx.ALL, 5)
|
self.buttonsBox1.Add(self.mentionAll, 0, wx.ALL, 5)
|
||||||
@@ -332,10 +342,14 @@ class reply(tweet):
|
|||||||
class viewTweet(wx.Dialog):
|
class viewTweet(wx.Dialog):
|
||||||
def __init__(self, tweet):
|
def __init__(self, tweet):
|
||||||
super(viewTweet, self).__init__(None, size=(850,850))
|
super(viewTweet, self).__init__(None, size=(850,850))
|
||||||
self.SetTitle(_(u"Tweet - %i characters ") % (len(tweet)))
|
self.SetTitle(_(u"Tweet - %i characters ") % (len(tweet["text"])))
|
||||||
panel = wx.Panel(self)
|
panel = wx.Panel(self)
|
||||||
label = wx.StaticText(panel, -1, _(u"Tweet"))
|
label = wx.StaticText(panel, -1, _(u"Tweet"))
|
||||||
self.text = wx.TextCtrl(panel, -1, tweet, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
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.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
dc = wx.WindowDC(self.text)
|
dc = wx.WindowDC(self.text)
|
||||||
dc.SetFont(self.text.GetFont())
|
dc.SetFont(self.text.GetFont())
|
||||||
(x, y, z) = dc.GetMultiLineTextExtent("0"*140)
|
(x, y, z) = dc.GetMultiLineTextExtent("0"*140)
|
||||||
@@ -346,6 +360,20 @@ class viewTweet(wx.Dialog):
|
|||||||
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
||||||
mainBox = wx.BoxSizer(wx.VERTICAL)
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
mainBox.Add(textBox, 0, wx.ALL, 5)
|
mainBox.Add(textBox, 0, wx.ALL, 5)
|
||||||
|
rtCountLabel = wx.StaticText(panel, -1, _(u"Retweets: "))
|
||||||
|
rtCount = wx.TextCtrl(panel, -1, str(tweet["retweet_count"]), size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
rtBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
rtBox.Add(rtCountLabel, 0, wx.ALL, 5)
|
||||||
|
rtBox.Add(rtCount, 0, wx.ALL, 5)
|
||||||
|
favsCountLabel = wx.StaticText(panel, -1, _(u"Favourites: "))
|
||||||
|
favsCount = wx.TextCtrl(panel, -1, str(tweet["favorite_count"]), size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||||
|
favsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
favsBox.Add(favsCountLabel, 0, wx.ALL, 5)
|
||||||
|
favsBox.Add(favsCount, 0, wx.ALL, 5)
|
||||||
|
infoBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
infoBox.Add(rtBox, 0, wx.ALL, 5)
|
||||||
|
infoBox.Add(favsBox, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(infoBox, 0, wx.ALL, 5)
|
||||||
if platform.system() != "Darwin":
|
if platform.system() != "Darwin":
|
||||||
spellcheck = wx.Button(panel, -1, _("Spelling correction"), size=wx.DefaultSize)
|
spellcheck = wx.Button(panel, -1, _("Spelling correction"), size=wx.DefaultSize)
|
||||||
spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
|
spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
|
||||||
@@ -417,3 +445,87 @@ class viewTweet(wx.Dialog):
|
|||||||
urlList.unshorten(urls, self).ShowModal()
|
urlList.unshorten(urls, self).ShowModal()
|
||||||
self.text.SetFocus()
|
self.text.SetFocus()
|
||||||
|
|
||||||
|
class viewNonTweet(wx.Dialog):
|
||||||
|
def __init__(self, tweet):
|
||||||
|
super(viewNonTweet, self).__init__(None, size=(850,850))
|
||||||
|
self.SetTitle(_(u"View"))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
label = wx.StaticText(panel, -1, _(u"Item"))
|
||||||
|
self.text = wx.TextCtrl(parent=panel, id=-1, value=tweet, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||||
|
dc = wx.WindowDC(self.text)
|
||||||
|
dc.SetFont(self.text.GetFont())
|
||||||
|
(x, y, z) = dc.GetMultiLineTextExtent("0"*140)
|
||||||
|
self.text.SetSize((x, y))
|
||||||
|
self.text.SetFocus()
|
||||||
|
textBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
textBox.Add(label, 0, wx.ALL, 5)
|
||||||
|
textBox.Add(self.text, 1, wx.EXPAND, 5)
|
||||||
|
mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
mainBox.Add(textBox, 0, wx.ALL, 5)
|
||||||
|
spellcheck = wx.Button(panel, -1, _("Spelling correction"), size=wx.DefaultSize)
|
||||||
|
spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
|
||||||
|
self.unshortenButton = wx.Button(panel, -1, _(u"Expand URL"), size=wx.DefaultSize)
|
||||||
|
self.unshortenButton.Bind(wx.EVT_BUTTON, self.onUnshorten)
|
||||||
|
self.unshortenButton.Disable()
|
||||||
|
translateButton = wx.Button(panel, -1, _(u"Translate message"), size=wx.DefaultSize)
|
||||||
|
translateButton.Bind(wx.EVT_BUTTON, self.onTranslate)
|
||||||
|
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"Close"), size=wx.DefaultSize)
|
||||||
|
cancelButton.SetDefault()
|
||||||
|
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
buttonsBox.Add(spellcheck, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(translateButton, 0, wx.ALL, 5)
|
||||||
|
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
||||||
|
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
||||||
|
selectId = wx.NewId()
|
||||||
|
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
||||||
|
self.accel_tbl = wx.AcceleratorTable([
|
||||||
|
(wx.ACCEL_CTRL, ord('A'), selectId),
|
||||||
|
])
|
||||||
|
self.SetAcceleratorTable(self.accel_tbl)
|
||||||
|
panel.SetSizer(mainBox)
|
||||||
|
self.SetClientSize(mainBox.CalcMin())
|
||||||
|
self.check_urls()
|
||||||
|
|
||||||
|
def check_urls(self):
|
||||||
|
if len(twitter.utils.find_urls_in_text(self.text.GetValue())) > 0:
|
||||||
|
self.unshortenButton.Enable()
|
||||||
|
|
||||||
|
def onCheck(self, ev):
|
||||||
|
text = self.text.GetValue()
|
||||||
|
dlg = spellCheckerGUI.spellCheckerDialog(text, "")
|
||||||
|
if dlg.ShowModal() == wx.ID_OK:
|
||||||
|
self.text.ChangeValue(dlg.checker.get_text())
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def onTranslate(self, ev):
|
||||||
|
dlg = translator.gui.translateDialog()
|
||||||
|
selection = dlg.ShowModal()
|
||||||
|
if selection != wx.ID_CANCEL:
|
||||||
|
text_to_translate = self.text.GetValue().encode("utf-8")
|
||||||
|
source = [x[0] for x in translator.available_languages()][dlg.source_lang.GetSelection()]
|
||||||
|
dest = [x[0] for x in translator.available_languages()][dlg.dest_lang.GetSelection()]
|
||||||
|
t = translator.translator.Translator()
|
||||||
|
t.from_lang = source
|
||||||
|
t.to_lang = dest
|
||||||
|
msg = t.translate(text_to_translate)
|
||||||
|
self.text.ChangeValue(msg)
|
||||||
|
output.speak(_(u"Translated"))
|
||||||
|
self.text.SetFocus()
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
dlg.Destroy()
|
||||||
|
|
||||||
|
def onSelect(self, ev):
|
||||||
|
self.text.SelectAll()
|
||||||
|
|
||||||
|
def onUnshorten(self, ev):
|
||||||
|
urls = twitter.utils.find_urls_in_text(self.text.GetValue())
|
||||||
|
if len(urls) == 0:
|
||||||
|
output.speak(_(u"There's no URL to be expanded"))
|
||||||
|
elif len(urls) == 1:
|
||||||
|
self.text.SetValue(self.text.GetValue().replace(urls[0], url_shortener.unshorten(urls[0])))
|
||||||
|
output.speak(_(u"URL expanded"))
|
||||||
|
elif len(urls) > 1:
|
||||||
|
urlList.unshorten(urls, self).ShowModal()
|
||||||
|
self.text.SetFocus()
|
||||||
|
@@ -19,24 +19,34 @@
|
|||||||
import wx
|
import wx
|
||||||
|
|
||||||
class trendingTopicsDialog(wx.Dialog):
|
class trendingTopicsDialog(wx.Dialog):
|
||||||
def __init__(self):
|
def __init__(self, information):
|
||||||
super(searchDialog, self).__init__(None, -1)
|
super(trendingTopicsDialog, self).__init__(None, -1)
|
||||||
|
self.countries = {}
|
||||||
|
self.cities = {}
|
||||||
|
self.information = information
|
||||||
|
self.split_information()
|
||||||
panel = wx.Panel(self)
|
panel = wx.Panel(self)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.SetTitle(_(u"Search on Twitter"))
|
self.SetTitle(_(u"View trending topics"))
|
||||||
label = wx.StaticText(panel, -1, _(u"Search"))
|
label = wx.StaticText(panel, -1, _(u"Trending topics by"))
|
||||||
self.term = wx.TextCtrl(panel, -1,)
|
|
||||||
dc = wx.WindowDC(self.term)
|
|
||||||
dc.SetFont(self.term.GetFont())
|
|
||||||
self.term.SetSize(dc.GetTextExtent("0"*40))
|
|
||||||
sizer.Add(label, 0, wx.ALL, 5)
|
sizer.Add(label, 0, wx.ALL, 5)
|
||||||
sizer.Add(self.term, 0, wx.ALL, 5)
|
self.country = wx.RadioButton(panel, -1, _(u"Country"), style=wx.RB_GROUP)
|
||||||
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
|
self.city = wx.RadioButton(panel, -1, _(u"City"))
|
||||||
self.users = wx.RadioButton(panel, -1, _(u"Users"))
|
self.Bind(wx.EVT_RADIOBUTTON, self.get_places, self.country)
|
||||||
|
self.Bind(wx.EVT_RADIOBUTTON, self.get_places, self.city)
|
||||||
|
|
||||||
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
radioSizer.Add(self.tweets, 0, wx.ALL, 5)
|
radioSizer.Add(label, 0, wx.ALL, 5)
|
||||||
radioSizer.Add(self.users, 0, wx.ALL, 5)
|
radioSizer.Add(self.country, 0, wx.ALL, 5)
|
||||||
|
radioSizer.Add(self.city, 0, wx.ALL, 5)
|
||||||
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
sizer.Add(radioSizer, 0, wx.ALL, 5)
|
||||||
|
label = wx.StaticText(panel, -1, _(u"Location"))
|
||||||
|
self.location = wx.ListBox(panel, -1, choices=[], style=wx.CB_READONLY)
|
||||||
|
self.get_places()
|
||||||
|
locationBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
locationBox.Add(label, 0, wx.ALL, 5)
|
||||||
|
locationBox.Add(self.location, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(locationBox, 0, wx.ALL, 5)
|
||||||
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
|
||||||
ok.SetDefault()
|
ok.SetDefault()
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
|
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
|
||||||
@@ -46,3 +56,25 @@ class trendingTopicsDialog(wx.Dialog):
|
|||||||
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
sizer.Add(btnsizer, 0, wx.ALL, 5)
|
||||||
panel.SetSizer(sizer)
|
panel.SetSizer(sizer)
|
||||||
self.SetClientSize(sizer.CalcMin())
|
self.SetClientSize(sizer.CalcMin())
|
||||||
|
|
||||||
|
def split_information(self):
|
||||||
|
# self.countries["World wide"] = 1
|
||||||
|
# self.cities["World wide"] = 1
|
||||||
|
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.country.GetValue() == True:
|
||||||
|
for i in self.information:
|
||||||
|
if i["placeType"]["name"] == "Country":
|
||||||
|
values.append(i["name"])
|
||||||
|
elif self.city.GetValue() == True:
|
||||||
|
for i in self.information:
|
||||||
|
if i["placeType"]["name"] != "Country":
|
||||||
|
values.append(i["name"])
|
||||||
|
self.location.Set(values)
|
||||||
|
|
@@ -46,8 +46,7 @@ class urlList(wx.Dialog):
|
|||||||
self.SetClientSize(sizer.CalcMin())
|
self.SetClientSize(sizer.CalcMin())
|
||||||
|
|
||||||
def onGo(self, ev):
|
def onGo(self, ev):
|
||||||
webbrowser.open(self.lista.GetStringSelection())
|
self.EndModal(wx.ID_OK)
|
||||||
self.Destroy()
|
|
||||||
|
|
||||||
def populate_list(self):
|
def populate_list(self):
|
||||||
for i in self.urls:
|
for i in self.urls:
|
||||||
|
219
src/gui/main.py
@@ -32,10 +32,16 @@ import output
|
|||||||
import platform
|
import platform
|
||||||
import urllib2
|
import urllib2
|
||||||
import sysTrayIcon
|
import sysTrayIcon
|
||||||
|
import switchModule
|
||||||
|
from wx.lib.pubsub import pub
|
||||||
import languageHandler
|
import languageHandler
|
||||||
|
from extra.autocompletionUsers import settings as autocompletionUsersSettings
|
||||||
|
import pygeocoder
|
||||||
|
from pygeolib import GeocoderError
|
||||||
from sessionmanager import manager
|
from sessionmanager import manager
|
||||||
|
from issueReporter import gui as issueReporterGUI
|
||||||
from mysc import event
|
from mysc import event
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded, stream_threaded
|
||||||
from twython import TwythonError
|
from twython import TwythonError
|
||||||
from urllib2 import URLError
|
from urllib2 import URLError
|
||||||
from mysc.repeating_timer import RepeatingTimer
|
from mysc.repeating_timer import RepeatingTimer
|
||||||
@@ -46,6 +52,8 @@ from extra import SoundsTutorial
|
|||||||
from keystrokeEditor import gui as keystrokeEditorGUI
|
from keystrokeEditor import gui as keystrokeEditorGUI
|
||||||
log = original_logger.getLogger("gui.main")
|
log = original_logger.getLogger("gui.main")
|
||||||
|
|
||||||
|
geocoder = pygeocoder.Geocoder()
|
||||||
|
function_reconnect_streams_running = False
|
||||||
class mainFrame(wx.Frame):
|
class mainFrame(wx.Frame):
|
||||||
""" Main class of the Frame. This is the Main Window."""
|
""" Main class of the Frame. This is the Main Window."""
|
||||||
|
|
||||||
@@ -56,12 +64,16 @@ class mainFrame(wx.Frame):
|
|||||||
|
|
||||||
# Application menu
|
# Application menu
|
||||||
app = wx.Menu()
|
app = wx.Menu()
|
||||||
|
switch_account = app.Append(wx.NewId(), _(u"S&witch account"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.switch_account, switch_account)
|
||||||
updateProfile = app.Append(wx.NewId(), _(u"&Update profile"))
|
updateProfile = app.Append(wx.NewId(), _(u"&Update profile"))
|
||||||
self.Bind(wx.EVT_MENU, self.update_profile, updateProfile)
|
self.Bind(wx.EVT_MENU, self.update_profile, updateProfile)
|
||||||
show_hide = app.Append(wx.NewId(), _(u"&Hide window"))
|
show_hide = app.Append(wx.NewId(), _(u"&Hide window"))
|
||||||
self.Bind(wx.EVT_MENU, self.show_hide, show_hide)
|
self.Bind(wx.EVT_MENU, self.show_hide, show_hide)
|
||||||
search = app.Append(wx.NewId(), _(u"&Search"))
|
search = app.Append(wx.NewId(), _(u"&Search"))
|
||||||
self.Bind(wx.EVT_MENU, self.search, search)
|
self.Bind(wx.EVT_MENU, self.search, search)
|
||||||
|
trends = app.Append(wx.NewId(), _(u"View &trending topics"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.get_trending_topics, trends)
|
||||||
lists = app.Append(wx.NewId(), _(u"&Lists manager"))
|
lists = app.Append(wx.NewId(), _(u"&Lists manager"))
|
||||||
self.Bind(wx.EVT_MENU, self.list_manager, lists)
|
self.Bind(wx.EVT_MENU, self.list_manager, lists)
|
||||||
sounds_tutorial = app.Append(wx.NewId(), _(u"Sounds &tutorial"))
|
sounds_tutorial = app.Append(wx.NewId(), _(u"Sounds &tutorial"))
|
||||||
@@ -87,6 +99,8 @@ class mainFrame(wx.Frame):
|
|||||||
self.Bind(wx.EVT_MENU, self.unfav, unfav)
|
self.Bind(wx.EVT_MENU, self.unfav, unfav)
|
||||||
view = tweet.Append(wx.NewId(), _(u"&Show tweet"))
|
view = tweet.Append(wx.NewId(), _(u"&Show tweet"))
|
||||||
self.Bind(wx.EVT_MENU, self.view, view)
|
self.Bind(wx.EVT_MENU, self.view, view)
|
||||||
|
view_coordinates = tweet.Append(wx.NewId(), _(u"View &address"))
|
||||||
|
self.Bind(wx.EVT_MENU, self.reverse_geocode, view_coordinates)
|
||||||
delete = tweet.Append(wx.NewId(), _(u"&Delete"))
|
delete = tweet.Append(wx.NewId(), _(u"&Delete"))
|
||||||
self.Bind(wx.EVT_MENU, self.delete, delete)
|
self.Bind(wx.EVT_MENU, self.delete, delete)
|
||||||
|
|
||||||
@@ -208,17 +222,17 @@ class mainFrame(wx.Frame):
|
|||||||
log.debug("Getting Twitter's Rest API...")
|
log.debug("Getting Twitter's Rest API...")
|
||||||
self.twitter = twitter.twitter.twitter()
|
self.twitter = twitter.twitter.twitter()
|
||||||
super(mainFrame, self).__init__(None, -1, "TW Blue", size=(1600, 1600))
|
super(mainFrame, self).__init__(None, -1, "TW Blue", size=(1600, 1600))
|
||||||
self.Bind(wx.EVT_QUERY_END_SESSION, self.exit)
|
wx.GetApp().Bind(wx.EVT_QUERY_END_SESSION, self.exit)
|
||||||
self.Bind(wx.EVT_END_SESSION, self.exit)
|
wx.GetApp().Bind(wx.EVT_END_SESSION, self.exit)
|
||||||
log.debug(u"Creating the system tray icon... ")
|
log.debug(u"Creating the system tray icon... ")
|
||||||
sysTray=sysTrayIcon.SysTrayIcon(self)
|
self.sysTray=sysTrayIcon.SysTrayIcon(self)
|
||||||
panel = wx.Panel(self)
|
panel = wx.Panel(self)
|
||||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.SetTitle("TW Blue")
|
self.SetTitle("TW Blue")
|
||||||
try:
|
try:
|
||||||
updater.update_manager.check_for_update()
|
updater.update_manager.check_for_update()
|
||||||
except:
|
except:
|
||||||
pass
|
wx.MessageDialog(self, _(u"An error occurred while looking for an update. It may be due to any problem either on our server or on your DNS servers. Please, try again later."), _(u"Error!"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||||
self.SetMenuBar(self.makeMenus())
|
self.SetMenuBar(self.makeMenus())
|
||||||
self.setup_twitter(panel)
|
self.setup_twitter(panel)
|
||||||
|
|
||||||
@@ -233,7 +247,7 @@ class mainFrame(wx.Frame):
|
|||||||
# Gets the tabs for home, mentions, send and direct messages.
|
# Gets the tabs for home, mentions, send and direct messages.
|
||||||
log.debug("Creating buffers...")
|
log.debug("Creating buffers...")
|
||||||
self.db.settings["buffers"] = []
|
self.db.settings["buffers"] = []
|
||||||
account = buffers.accountPanel(self.nb)
|
account = buffers.accountPanel(self.nb, self.db.settings["user_name"])
|
||||||
self.nb.AddPage(account, self.db.settings["user_name"])
|
self.nb.AddPage(account, self.db.settings["user_name"])
|
||||||
self.db.settings["buffers"].append(self.db.settings["user_name"])
|
self.db.settings["buffers"].append(self.db.settings["user_name"])
|
||||||
account_index = self.db.settings["buffers"].index(self.db.settings["user_name"])
|
account_index = self.db.settings["buffers"].index(self.db.settings["user_name"])
|
||||||
@@ -310,11 +324,23 @@ class mainFrame(wx.Frame):
|
|||||||
self.db.settings["buffers"].append(i+"favs")
|
self.db.settings["buffers"].append(i+"favs")
|
||||||
self.fav_stream = RepeatingTimer(180, self.get_fav_buffers)
|
self.fav_stream = RepeatingTimer(180, self.get_fav_buffers)
|
||||||
self.fav_stream.start()
|
self.fav_stream.start()
|
||||||
|
for i in config.main["other_buffers"]["trending_topic_buffers"]:
|
||||||
|
buff = buffers.trendsPanel(self.nb, self, "%s_tt" % (i,), argumento=i, sound="trends_updated.ogg")
|
||||||
|
timer = RepeatingTimer(300, buff.start_streams)
|
||||||
|
timer.start()
|
||||||
|
num = buff.start_streams()
|
||||||
|
buff.put_items(num)
|
||||||
|
self.nb.InsertSubPage(self.db.settings["buffers"].index(self.db.settings["user_name"]), buff, _(u"Trending topics for %s") % (buff.name,))
|
||||||
self.sizer.Add(self.nb, 0, wx.ALL, 5)
|
self.sizer.Add(self.nb, 0, wx.ALL, 5)
|
||||||
|
if config.main["general"]["use_invisible_keyboard_shorcuts"] == True:
|
||||||
|
km = self.create_invisible_keyboard_shorcuts()
|
||||||
|
self.register_invisible_keyboard_shorcuts(km)
|
||||||
panel.SetSizer(self.sizer)
|
panel.SetSizer(self.sizer)
|
||||||
self.SetClientSize(self.sizer.CalcMin())
|
self.SetClientSize(self.sizer.CalcMin())
|
||||||
self.Bind(event.MyEVT_STARTED, self.onInit)
|
self.Bind(event.MyEVT_STARTED, self.onInit)
|
||||||
self.Bind(event.EVT_RESULT, self.onMemberAdded)
|
self.Bind(event.EVT_RESULT, self.onMemberAdded)
|
||||||
|
pub.subscribe(self.listen_streamerror, "streamError")
|
||||||
|
pub.subscribe(self.listen_for_friends, "friendsReceived")
|
||||||
call_threaded(self.init, run_streams=True)
|
call_threaded(self.init, run_streams=True)
|
||||||
|
|
||||||
def init(self, run_streams=False):
|
def init(self, run_streams=False):
|
||||||
@@ -325,7 +351,7 @@ class mainFrame(wx.Frame):
|
|||||||
if i == self.nb.GetPageCount() and deleted > 0:
|
if i == self.nb.GetPageCount() and deleted > 0:
|
||||||
i = i-1
|
i = i-1
|
||||||
deleted = deleted-1
|
deleted = deleted-1
|
||||||
log.debug("Starting stream for %s..." % self.nb.GetPage(i).name_buffer)
|
# log.debug(u"Starting stream for %s..." % self.nb.GetPage(i).name_buffer)
|
||||||
info_event = event.infoEvent(event.EVT_STARTED, 1)
|
info_event = event.infoEvent(event.EVT_STARTED, 1)
|
||||||
try:
|
try:
|
||||||
if self.nb.GetPage(i).type == "search":
|
if self.nb.GetPage(i).type == "search":
|
||||||
@@ -347,6 +373,7 @@ class mainFrame(wx.Frame):
|
|||||||
self.check_streams.start()
|
self.check_streams.start()
|
||||||
# If all it's done, then play a nice sound saying that all it's OK.
|
# If all it's done, then play a nice sound saying that all it's OK.
|
||||||
sound.player.play("ready.ogg")
|
sound.player.play("ready.ogg")
|
||||||
|
autocompletionUsersSettings.execute_at_startup(window=self)
|
||||||
|
|
||||||
def remove_list(self, id):
|
def remove_list(self, id):
|
||||||
for i in range(0, self.nb.GetPageCount()):
|
for i in range(0, self.nb.GetPageCount()):
|
||||||
@@ -369,21 +396,21 @@ class mainFrame(wx.Frame):
|
|||||||
|
|
||||||
def setup_twitter(self, panel):
|
def setup_twitter(self, panel):
|
||||||
""" Setting up the connection for twitter, or authenticate if the config file has valid credentials."""
|
""" Setting up the connection for twitter, or authenticate if the config file has valid credentials."""
|
||||||
try:
|
# try:
|
||||||
self.twitter.login(self.user_key, self.user_secret)
|
self.twitter.login(self.user_key, self.user_secret)
|
||||||
self.logging_in_twblue(panel)
|
self.logging_in_twblue(panel)
|
||||||
log.info("Authorized in Twitter.")
|
log.info("Authorized in Twitter.")
|
||||||
del self.user_key; del self.user_secret
|
del self.user_key; del self.user_secret
|
||||||
except:
|
# except:
|
||||||
dlg1 = wx.MessageDialog(panel, _(u"Connection error. Try again later."), _(u"Error!"), wx.ICON_ERROR)
|
# dlg1 = wx.MessageDialog(panel, _(u"Connection error. Try again later."), _(u"Error!"), wx.ICON_ERROR)
|
||||||
dlg1.ShowModal()
|
# dlg1.ShowModal()
|
||||||
self.Close(True)
|
# self.Close(True)
|
||||||
|
|
||||||
def get_home(self):
|
def get_home(self):
|
||||||
""" Gets the home stream, that manages home timeline, mentions, direct messages and sent."""
|
""" Gets the home stream, that manages home timeline, mentions, direct messages and sent."""
|
||||||
try:
|
try:
|
||||||
self.stream = twitter.buffers.stream.streamer(application.app_key, application.app_secret, config.main["twitter"]["user_key"], config.main["twitter"]["user_secret"], parent=self)
|
self.stream = twitter.buffers.stream.streamer(application.app_key, application.app_secret, config.main["twitter"]["user_key"], config.main["twitter"]["user_secret"], parent=self)
|
||||||
call_threaded(self.stream.user)
|
stream_threaded(self.stream.user)
|
||||||
except:
|
except:
|
||||||
self.stream.disconnect()
|
self.stream.disconnect()
|
||||||
|
|
||||||
@@ -406,29 +433,36 @@ class mainFrame(wx.Frame):
|
|||||||
ids+= str(z)+", "
|
ids+= str(z)+", "
|
||||||
if ids != "":
|
if ids != "":
|
||||||
# try:
|
# try:
|
||||||
call_threaded(self.stream2.statuses.filter, follow=ids)
|
stream_threaded(self.stream2.statuses.filter, follow=ids)
|
||||||
# except:
|
# except:
|
||||||
# pass
|
# pass
|
||||||
# except:
|
# except:
|
||||||
# self.stream2.disconnect()
|
# self.stream2.disconnect()
|
||||||
|
|
||||||
def check_stream_up(self):
|
def check_stream_up(self):
|
||||||
try:
|
global function_reconnect_streams_running
|
||||||
urllib2.urlopen("http://74.125.228.231", timeout=5)
|
if function_reconnect_streams_running == True: return
|
||||||
except urllib2.URLError:
|
if not hasattr(self, "stream") and not hasattr(self, "stream2"):
|
||||||
if self.stream.connected == True: self.stream.disconnect()
|
function_reconnect_streams_running = True
|
||||||
if hasattr(self, "stream2") and self.stream2.connected: self.stream2.disconnect()
|
self.init(run_streams=True)
|
||||||
if config.main["general"]["announce_stream_status"] == True: output.speak(_(u"Streams disconnected. TW Blue will try to reconnect in a minute."))
|
|
||||||
return
|
return
|
||||||
if self.stream.connected == False:
|
# try:
|
||||||
del self.stream
|
# urllib2.urlopen("http://74.125.228.231", timeout=5)
|
||||||
if config.main["general"]["announce_stream_status"] == True: output.speak(_(u"Reconnecting streams..."))
|
# except urllib2.URLError:
|
||||||
call_threaded(self.init)
|
# if self.stream.connected == True: self.stream.disconnect()
|
||||||
self.get_home()
|
# if hasattr(self, "stream2") and self.stream2.connected: self.stream2.disconnect()
|
||||||
if hasattr(self, "stream2") and self.stream2.connected == False:
|
# if config.main["general"]["announce_stream_status"] == True: output.speak(_(u"Streams disconnected. TW Blue will try to reconnect in a minute."))
|
||||||
log.debug("Trying reconnects the timelines stream...")
|
# return
|
||||||
del self.stream2
|
# if self.stream.connected == False:
|
||||||
self.get_tls()
|
# del self.stream
|
||||||
|
# if config.main["general"]["announce_stream_status"] == True: output.speak(_(u"Reconnecting streams..."))
|
||||||
|
# call_threaded(self.init)
|
||||||
|
# self.get_home()
|
||||||
|
# if hasattr(self, "stream2") and self.stream2.connected == False:
|
||||||
|
# log.debug("Trying reconnects the timelines stream...")
|
||||||
|
# del self.stream2
|
||||||
|
# self.get_tls()
|
||||||
|
function_reconnect_streams_running = False
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
@@ -501,7 +535,7 @@ class mainFrame(wx.Frame):
|
|||||||
dlg = dialogs.lists.removeUserListDialog(self)
|
dlg = dialogs.lists.removeUserListDialog(self)
|
||||||
if dlg.ShowModal() == wx.ID_OK:
|
if dlg.ShowModal() == wx.ID_OK:
|
||||||
try:
|
try:
|
||||||
list = self.twitter.twitter.delete_list_member(list_id=self.db.settings["lists"][dlg.get_selected()]["id"], screen_name=user)
|
list = self.twitter.twitter.delete_list_member(list_id=self.db.settings["lists"][dlg.lista.get_selected()]["id"], screen_name=user)
|
||||||
older_list = twitter.utils.find_item(self.db.settings["lists"][dlg.get_selected()]["id"], self.db.settings["lists"])
|
older_list = twitter.utils.find_item(self.db.settings["lists"][dlg.get_selected()]["id"], self.db.settings["lists"])
|
||||||
if list["mode"] == "private":
|
if list["mode"] == "private":
|
||||||
self.db.settings["lists"].pop(older_list)
|
self.db.settings["lists"].pop(older_list)
|
||||||
@@ -546,8 +580,7 @@ class mainFrame(wx.Frame):
|
|||||||
webbrowser.open("http://twblue.com.mx")
|
webbrowser.open("http://twblue.com.mx")
|
||||||
|
|
||||||
def onReportBug(self, ev):
|
def onReportBug(self, ev):
|
||||||
webbrowser.open("https://github.com/manuelcortez/TWBlue/issues")
|
issueReporterGUI.reportBug(self.db.settings["user_name"]).ShowModal()
|
||||||
# issueReporterGUI.reportBug(self.db.settings["user_name"]).ShowModal()
|
|
||||||
|
|
||||||
def onCheckForUpdates(self, ev):
|
def onCheckForUpdates(self, ev):
|
||||||
updater.update_manager.check_for_update(msg=True)
|
updater.update_manager.check_for_update(msg=True)
|
||||||
@@ -574,7 +607,7 @@ class mainFrame(wx.Frame):
|
|||||||
output.speak(self.nb.GetPageText(self.nb.GetSelection())+",", True)
|
output.speak(self.nb.GetPageText(self.nb.GetSelection())+",", True)
|
||||||
|
|
||||||
def skip_blank_pages(self, forward=True):
|
def skip_blank_pages(self, forward=True):
|
||||||
if self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty" and (self.showing == False or platform.system() == "Darwin"):
|
if self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty":
|
||||||
self.nb.AdvanceSelection(forward)
|
self.nb.AdvanceSelection(forward)
|
||||||
|
|
||||||
def close(self, ev=None):
|
def close(self, ev=None):
|
||||||
@@ -590,6 +623,8 @@ class mainFrame(wx.Frame):
|
|||||||
def exit(self, event=None):
|
def exit(self, event=None):
|
||||||
config.main.write()
|
config.main.write()
|
||||||
log.debug("Exiting...")
|
log.debug("Exiting...")
|
||||||
|
self.sysTray.RemoveIcon()
|
||||||
|
self.sysTray.Destroy()
|
||||||
try:
|
try:
|
||||||
self.check_streams.cancel()
|
self.check_streams.cancel()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -661,8 +696,18 @@ class mainFrame(wx.Frame):
|
|||||||
self.nb.GetCurrentPage().onRetweet(ev)
|
self.nb.GetCurrentPage().onRetweet(ev)
|
||||||
|
|
||||||
def view(self, ev=None):
|
def view(self, ev=None):
|
||||||
tweet = self.nb.GetCurrentPage().get_message(dialog=True)
|
tp = self.nb.GetCurrentPage().type
|
||||||
dialogs.message.viewTweet(tweet).ShowModal()
|
if tp == "buffer" or tp == "timeline" or tp == "favourites_timeline" or tp == "list" or tp == "search":
|
||||||
|
try:
|
||||||
|
id = self.db.settings[self.nb.GetCurrentPage().name_buffer][self.nb.GetCurrentPage().list.get_selected()]["id"]
|
||||||
|
tweet = self.twitter.twitter.show_status(id=id)
|
||||||
|
dialogs.message.viewTweet(tweet).ShowModal()
|
||||||
|
except TwythonError as e:
|
||||||
|
non_tweet = self.nb.GetCurrentPage().get_message(dialog=True)
|
||||||
|
dialogs.message.viewNonTweet(non_tweet).ShowModal()
|
||||||
|
else:
|
||||||
|
non_tweet = self.nb.GetCurrentPage().get_message(dialog=True)
|
||||||
|
dialogs.message.viewNonTweet(non_tweet).ShowModal()
|
||||||
|
|
||||||
def fav(self, ev=None):
|
def fav(self, ev=None):
|
||||||
if self.nb.GetCurrentPage().name_buffer != "direct_messages" and self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage().name_buffer != "friends":
|
if self.nb.GetCurrentPage().name_buffer != "direct_messages" and self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage().name_buffer != "friends":
|
||||||
@@ -778,6 +823,8 @@ class mainFrame(wx.Frame):
|
|||||||
except:
|
except:
|
||||||
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
|
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
|
||||||
output.speak(msg, 1)
|
output.speak(msg, 1)
|
||||||
|
if self.showing == True:
|
||||||
|
self.nb.GetCurrentPage().list.list.SetFocus()
|
||||||
|
|
||||||
def right(self, event=None):
|
def right(self, event=None):
|
||||||
num = self.nb.GetSelection()
|
num = self.nb.GetSelection()
|
||||||
@@ -791,21 +838,37 @@ class mainFrame(wx.Frame):
|
|||||||
except:
|
except:
|
||||||
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
|
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
|
||||||
output.speak(msg, 1)
|
output.speak(msg, 1)
|
||||||
|
if self.showing == True:
|
||||||
|
self.nb.GetCurrentPage().list.list.SetFocus()
|
||||||
|
|
||||||
def show_hide(self, ev=None):
|
def create_invisible_keyboard_shorcuts(self):
|
||||||
# if platform.system() == "Linux" or platform.system() == "Darwin": return
|
|
||||||
keymap = {}
|
keymap = {}
|
||||||
for i in config.main["keymap"]:
|
for i in config.main["keymap"]:
|
||||||
if hasattr(self, i):
|
if hasattr(self, i):
|
||||||
keymap[config.main["keymap"][i]] = getattr(self, i)
|
keymap[config.main["keymap"][i]] = getattr(self, i)
|
||||||
|
return keymap
|
||||||
|
|
||||||
|
def register_invisible_keyboard_shorcuts(self, keymap):
|
||||||
|
self.keyboard_handler = WXKeyboardHandler(self)
|
||||||
|
self.keyboard_handler.register_keys(keymap)
|
||||||
|
|
||||||
|
def unregister_invisible_keyboard_shorcuts(self, keymap):
|
||||||
|
try:
|
||||||
|
self.keyboard_handler.unregister_keys(keymap)
|
||||||
|
del self.keyboard_handler
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def show_hide(self, ev=None):
|
||||||
|
km = self.create_invisible_keyboard_shorcuts()
|
||||||
if self.showing == True:
|
if self.showing == True:
|
||||||
self.keyboard_handler = WXKeyboardHandler(self)
|
if config.main["general"]["use_invisible_keyboard_shorcuts"] == False:
|
||||||
self.keyboard_handler.register_keys(keymap)
|
self.register_invisible_keyboard_shorcuts(km)
|
||||||
self.Hide()
|
self.Hide()
|
||||||
self.showing = False
|
self.showing = False
|
||||||
else:
|
else:
|
||||||
self.keyboard_handler.unregister_keys(keymap)
|
if config.main["general"]["use_invisible_keyboard_shorcuts"] == False:
|
||||||
del self.keyboard_handler
|
self.unregister_invisible_keyboard_shorcuts(km)
|
||||||
self.Show()
|
self.Show()
|
||||||
self.showing = True
|
self.showing = True
|
||||||
|
|
||||||
@@ -959,6 +1022,72 @@ class mainFrame(wx.Frame):
|
|||||||
return page
|
return page
|
||||||
return page
|
return page
|
||||||
|
|
||||||
|
def switch_account(self, ev):
|
||||||
|
switchModule.switcher(self)
|
||||||
|
|
||||||
|
def reverse_geocode(self, event=None):
|
||||||
|
try:
|
||||||
|
tweet = self.nb.GetCurrentPage().get_tweet()
|
||||||
|
if tweet["coordinates"] != None:
|
||||||
|
x = tweet["coordinates"]["coordinates"][0]
|
||||||
|
y = tweet["coordinates"]["coordinates"][1]
|
||||||
|
address = geocoder.reverse_geocode(y, x)
|
||||||
|
if event == None: output.speak(address[0].__str__().decode("utf-8"))
|
||||||
|
else: wx.MessageDialog(self, address[0].__str__().decode("utf-8"), _(u"Address"), wx.OK).ShowModal()
|
||||||
|
else:
|
||||||
|
output.speak(_(u"There are no coordinates in this tweet"))
|
||||||
|
except GeocoderError:
|
||||||
|
output.speak(_(u"There are no results for the coordinates in this tweet"))
|
||||||
|
except ValueError:
|
||||||
|
output.speak(_(u"Error decoding coordinates. Try again later."))
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def view_reverse_geocode(self, event=None):
|
||||||
|
try:
|
||||||
|
tweet = self.nb.GetCurrentPage().get_tweet()
|
||||||
|
if tweet["coordinates"] != None:
|
||||||
|
x = tweet["coordinates"]["coordinates"][0]
|
||||||
|
y = tweet["coordinates"]["coordinates"][1]
|
||||||
|
address = geocoder.reverse_geocode(y, x)
|
||||||
|
dialogs.message.viewNonTweet(address[0].__str__().decode("utf-8")).ShowModal()
|
||||||
|
else:
|
||||||
|
output.speak(_(u"There are no coordinates in this tweet"))
|
||||||
|
except GeocoderError:
|
||||||
|
output.speak(_(u"There are no results for the coordinates in this tweet"))
|
||||||
|
except ValueError:
|
||||||
|
output.speak(_(u"Error decoding coordinates. Try again later."))
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_trending_topics(self, event=None):
|
||||||
|
info = self.twitter.twitter.get_available_trends()
|
||||||
|
trendingDialog = dialogs.trending.trendingTopicsDialog(info)
|
||||||
|
if trendingDialog.ShowModal() == wx.ID_OK:
|
||||||
|
if trendingDialog.country.GetValue() == True:
|
||||||
|
woeid = trendingDialog.countries[trendingDialog.location.GetStringSelection()]
|
||||||
|
elif trendingDialog.city.GetValue() == True:
|
||||||
|
woeid = trendingDialog.cities[trendingDialog.location.GetStringSelection()]
|
||||||
|
buff = buffers.trendsPanel(self.nb, self, "%s_tt" % (woeid,), argumento=woeid, sound="trends_updated.ogg")
|
||||||
|
self.nb.InsertSubPage(self.db.settings["buffers"].index(self.db.settings["user_name"]), buff, _(u"Trending topics for %s") % (trendingDialog.location.GetStringSelection(),))
|
||||||
|
timer = RepeatingTimer(300, buff.start_streams)
|
||||||
|
timer.start()
|
||||||
|
num = buff.start_streams()
|
||||||
|
config.main["other_buffers"]["trending_topic_buffers"].append(woeid)
|
||||||
|
buff.put_items(num)
|
||||||
|
|
||||||
|
def listen_streamerror(self):
|
||||||
|
log.error("There is a connection error")
|
||||||
|
if hasattr(self, "stream"):
|
||||||
|
self.stream.disconnect()
|
||||||
|
del self.stream
|
||||||
|
if hasattr(self, "stream2"):
|
||||||
|
self.stream2.disconnect()
|
||||||
|
del self.stream2
|
||||||
|
|
||||||
|
def listen_for_friends(self):
|
||||||
|
self.stream2.set_friends(self.stream.friends)
|
||||||
|
|
||||||
### Close App
|
### Close App
|
||||||
def Destroy(self):
|
def Destroy(self):
|
||||||
self.sysTray.Destroy()
|
self.sysTray.Destroy()
|
||||||
|
23
src/gui/switchModule.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
import gui
|
||||||
|
import config
|
||||||
|
from sessionmanager import gui as sessionManagerUI
|
||||||
|
|
||||||
|
class switcher(object):
|
||||||
|
def __init__(self, window):
|
||||||
|
self.hold_window = window
|
||||||
|
self.hold_window.Hide()
|
||||||
|
sessionManagerWindow = sessionManagerUI.sessionManagerWindow()
|
||||||
|
if sessionManagerWindow.ShowModal() == wx.ID_OK:
|
||||||
|
self.hold_window.Destroy()
|
||||||
|
self.window = gui.main.mainFrame()
|
||||||
|
self.window.Show()
|
||||||
|
self.window.showing = True
|
||||||
|
if config.main != None and config.main["general"]["hide_gui"] == True:
|
||||||
|
self.window.show_hide()
|
||||||
|
self.window.Hide()
|
||||||
|
wx.GetApp().SetTopWindow(self.window)
|
||||||
|
else:
|
||||||
|
self.hold_window.Show()
|
||||||
|
|
@@ -55,7 +55,7 @@ class SysTrayIcon(wx.TaskBarIcon):
|
|||||||
if (self.frame.showing):
|
if (self.frame.showing):
|
||||||
self.frame.SetFocus()
|
self.frame.SetFocus()
|
||||||
else:
|
else:
|
||||||
self.frame.onShow_hide()
|
self.frame.show_hide()
|
||||||
|
|
||||||
def Destroy(self):
|
def Destroy(self):
|
||||||
self.menu.Destroy()
|
self.menu.Destroy()
|
||||||
|
0
src/issueReporter/__init__.py
Normal file
21
src/issueReporter/constants.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
############################################################
|
||||||
|
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
categories = ["General", "documentation", "translation"]
|
||||||
|
reproducibilities = ["always", "sometimes", "random", "have not tried", "unable to duplicate"]
|
||||||
|
severities = ["block", "crash", "major", "minor", "tweak", "text", "trivial", "feature"]
|
32
src/issueReporter/get_logs.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
############################################################
|
||||||
|
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
import paths
|
||||||
|
import os
|
||||||
|
|
||||||
|
def get_logs_files():
|
||||||
|
files = {}
|
||||||
|
for i in os.listdir(paths.logs_path()):
|
||||||
|
if i == "debug.log": continue
|
||||||
|
f = open(paths.logs_path(i), "r")
|
||||||
|
files[i] = f.readlines()
|
||||||
|
f.close()
|
||||||
|
try: os.remove(paths.logs_path("tracebacks.log"))
|
||||||
|
except: pass
|
||||||
|
return files
|
||||||
|
|
124
src/issueReporter/gui.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
############################################################
|
||||||
|
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
import wx
|
||||||
|
import application
|
||||||
|
from suds.client import Client
|
||||||
|
import constants
|
||||||
|
|
||||||
|
class reportBug(wx.Dialog):
|
||||||
|
def __init__(self, user_name):
|
||||||
|
self.user = "reporter1"
|
||||||
|
self.user_name = user_name
|
||||||
|
self.password = "contrasena"
|
||||||
|
self.url = application.report_bugs_url
|
||||||
|
self.categories = [_(u"General"), _(u"Documentation"), _(u"Translation")]
|
||||||
|
self.reproducibilities = [_(u"always"), _(u"sometimes"), _(u"random"), _(u"have not tried"), _(u"unable to duplicate")]
|
||||||
|
self.severities = [_(u"block"), _(u"crash"), _(u"major"), _(u"minor"), _(u"tweak"), _(u"text"), _(u"trivial"), _(u"feature")]
|
||||||
|
wx.Dialog.__init__(self, None, -1)
|
||||||
|
self.SetTitle(_(u"Report an error"))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
categoryLabel = wx.StaticText(panel, -1, _(u"Select a category"), size=wx.DefaultSize)
|
||||||
|
self.category = wx.ComboBox(panel, -1, choices=self.categories, style=wx.CB_READONLY)
|
||||||
|
self.category.SetSize(self.category.GetBestSize())
|
||||||
|
self.category.SetSelection(0)
|
||||||
|
categoryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
categoryB.Add(categoryLabel, 0, wx.ALL, 5)
|
||||||
|
categoryB.Add(self.category, 0, wx.ALL, 5)
|
||||||
|
self.category.SetFocus()
|
||||||
|
sizer.Add(categoryB, 0, wx.ALL, 5)
|
||||||
|
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
|
||||||
|
self.summary = wx.TextCtrl(panel, -1)
|
||||||
|
dc = wx.WindowDC(self.summary)
|
||||||
|
dc.SetFont(self.summary.GetFont())
|
||||||
|
self.summary.SetSize(dc.GetTextExtent("a"*80))
|
||||||
|
# self.summary.SetFocus()
|
||||||
|
summaryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
|
||||||
|
summaryB.Add(self.summary, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(summaryB, 0, wx.ALL, 5)
|
||||||
|
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
|
||||||
|
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
|
||||||
|
dc = wx.WindowDC(self.description)
|
||||||
|
dc.SetFont(self.description.GetFont())
|
||||||
|
(x, y, z) = dc.GetMultiLineTextExtent("0"*2000)
|
||||||
|
self.description.SetSize((x, y))
|
||||||
|
descBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
|
||||||
|
descBox.Add(self.description, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(descBox, 0, wx.ALL, 5)
|
||||||
|
reproducibilityLabel = wx.StaticText(panel, -1, _(u"how often does this bug happen?"), size=wx.DefaultSize)
|
||||||
|
self.reproducibility = wx.ComboBox(panel, -1, choices=self.reproducibilities, style=wx.CB_READONLY)
|
||||||
|
self.reproducibility.SetSelection(3)
|
||||||
|
self.reproducibility.SetSize(self.reproducibility.GetBestSize())
|
||||||
|
reprB = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
reprB.Add(reproducibilityLabel, 0, wx.ALL, 5)
|
||||||
|
reprB.Add(self.reproducibility, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(reprB, 0, wx.ALL, 5)
|
||||||
|
severityLabel = wx.StaticText(panel, -1, _(u"Select the importance that you think this bug has"))
|
||||||
|
self.severity = wx.ComboBox(panel, -1, choices=self.severities, style=wx.CB_READONLY)
|
||||||
|
self.severity.SetSize(self.severity.GetBestSize())
|
||||||
|
self.severity.SetSelection(3)
|
||||||
|
severityB = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
severityB.Add(severityLabel, 0, wx.ALL, 5)
|
||||||
|
severityB.Add(self.severity, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(severityB, 0, wx.ALL, 5)
|
||||||
|
self.agree = wx.CheckBox(panel, -1, _(u"I know that the TW Blue bug system will get my Twitter username to contact me and fix the bug quickly"))
|
||||||
|
self.agree.SetValue(False)
|
||||||
|
sizer.Add(self.agree, 0, wx.ALL, 5)
|
||||||
|
ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
|
||||||
|
ok.Bind(wx.EVT_BUTTON, self.onSend)
|
||||||
|
ok.SetDefault()
|
||||||
|
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||||
|
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
btnBox.Add(ok, 0, wx.ALL, 5)
|
||||||
|
btnBox.Add(cancel, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||||
|
panel.SetSizer(sizer)
|
||||||
|
self.SetClientSize(sizer.CalcMin())
|
||||||
|
|
||||||
|
def onSend(self, ev):
|
||||||
|
if self.summary.GetValue() == "" or self.description.GetValue() == "":
|
||||||
|
wx.MessageDialog(self, _(u"You must fill out both fields"), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||||
|
return
|
||||||
|
if self.agree.GetValue() == False:
|
||||||
|
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your twitter username to contact to you if is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||||
|
return
|
||||||
|
# try:
|
||||||
|
client = Client(self.url)
|
||||||
|
issue = client.factory.create('IssueData')
|
||||||
|
issue.project.name = "TWBlue"
|
||||||
|
issue.project.id = 0
|
||||||
|
issue.summary = self.summary.GetValue(),
|
||||||
|
issue.description = "Reported by @%s\n\n" % (self.user_name) + self.description.GetValue()
|
||||||
|
issue.category = constants.categories[self.category.GetSelection()]
|
||||||
|
issue.reproducibility.name = constants.reproducibilities[self.reproducibility.GetSelection()]
|
||||||
|
issue.severity.name = constants.severities[self.severity.GetSelection()]
|
||||||
|
issue.priority.name = "normal"
|
||||||
|
issue.view_state.name = "public"
|
||||||
|
issue.resolution.name = "open"
|
||||||
|
issue.projection.name = "none"
|
||||||
|
issue.eta.name = "eta"
|
||||||
|
issue.status.name = "new"
|
||||||
|
id = client.service.mc_issue_add(self.user, self.password, issue)
|
||||||
|
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
|
||||||
|
self.EndModal(wx.ID_OK)
|
||||||
|
# except:
|
||||||
|
# wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
|
||||||
|
# self.EndModal(wx.ID_CANCEL)
|
@@ -42,4 +42,7 @@ actions = {
|
|||||||
"edit_keystrokes": _(u"Shows the keystroke editor"),
|
"edit_keystrokes": _(u"Shows the keystroke editor"),
|
||||||
"view_user_lists": _(u"Show lists for a specified user"),
|
"view_user_lists": _(u"Show lists for a specified user"),
|
||||||
"get_more_items": _(u"loads previous items to any buffer"),
|
"get_more_items": _(u"loads previous items to any buffer"),
|
||||||
|
"reverse_geocode": _(u"Get location of any tweet"),
|
||||||
|
"view_reverse_geocode": _(u"Displays the tweet's location in a dialog"),
|
||||||
|
"get_trending_topics": _(u"Creates a buffer for displaying trends for a desired place"),
|
||||||
}
|
}
|
@@ -126,23 +126,27 @@ def setLanguage(lang):
|
|||||||
elif system == "Darwin":
|
elif system == "Darwin":
|
||||||
import Foundation
|
import Foundation
|
||||||
localeName = Foundation.NSLocale.currentLocale().identifier()
|
localeName = Foundation.NSLocale.currentLocale().identifier()
|
||||||
# trans=gettext.translation('twblue', localedir=paths.locale_path(), languages=[localeName])
|
|
||||||
# curLang=localeName
|
|
||||||
else:
|
|
||||||
localeName=locale.getdefaultlocale()[0]
|
|
||||||
trans=gettext.translation('twblue', localedir=paths.locale_path(), languages=[localeName])
|
trans=gettext.translation('twblue', localedir=paths.locale_path(), languages=[localeName])
|
||||||
curLang=localeName
|
curLang=localeName
|
||||||
|
# else:
|
||||||
|
# localeName=locale.getdefaultlocale()[0]
|
||||||
|
# trans=gettext.translation('twblue', localedir=paths.locale_path(), languages=[localeName])
|
||||||
|
# curLang=localeName
|
||||||
|
|
||||||
else:
|
else:
|
||||||
trans=gettext.translation("twblue", localedir=paths.locale_path(), languages=[lang])
|
trans=gettext.translation("twblue", localedir=paths.locale_path(), languages=[lang])
|
||||||
curLang=lang
|
curLang=lang
|
||||||
localeChanged=False
|
localeChanged=False
|
||||||
#Try setting Python's locale to lang
|
#Try setting Python's locale to lang
|
||||||
try:
|
# try:
|
||||||
|
if system == "Windows":
|
||||||
|
locale.setlocale(locale.LC_ALL, langToWindowsLocale(lang))
|
||||||
|
localeChanged=True
|
||||||
|
else:
|
||||||
locale.setlocale(locale.LC_ALL, lang)
|
locale.setlocale(locale.LC_ALL, lang)
|
||||||
localeChanged=True
|
localeChanged=True
|
||||||
except:
|
# except:
|
||||||
pass
|
# pass
|
||||||
if not localeChanged and '_' in lang:
|
if not localeChanged and '_' in lang:
|
||||||
#Python couldn'tsupport the language_country locale, just try language.
|
#Python couldn'tsupport the language_country locale, just try language.
|
||||||
try:
|
try:
|
||||||
@@ -158,7 +162,7 @@ def setLanguage(lang):
|
|||||||
curLang="en"
|
curLang="en"
|
||||||
trans.install(unicode=True)
|
trans.install(unicode=True)
|
||||||
# Install our pgettext function.
|
# Install our pgettext function.
|
||||||
__builtin__.__dict__["pgettext"] = makePgettext(trans)
|
# __builtin__.__dict__["pgettext"] = makePgettext(trans)
|
||||||
|
|
||||||
def getLanguage():
|
def getLanguage():
|
||||||
return curLang
|
return curLang
|
||||||
@@ -178,3 +182,21 @@ def normalizeLanguage(lang):
|
|||||||
ld[1]=ld[1].upper()
|
ld[1]=ld[1].upper()
|
||||||
return "_".join(ld)
|
return "_".join(ld)
|
||||||
|
|
||||||
|
def langToWindowsLocale(lang):
|
||||||
|
languages = {"en": "eng",
|
||||||
|
"ar": "ara",
|
||||||
|
"ca": "cat",
|
||||||
|
"de": "deu",
|
||||||
|
"es": "esp",
|
||||||
|
"fi": "fin",
|
||||||
|
"fr": "fre_FRA",
|
||||||
|
"gl": "glc",
|
||||||
|
"eu": "euq",
|
||||||
|
"hu": "hun",
|
||||||
|
"it": "ita",
|
||||||
|
"pl": "plk",
|
||||||
|
"pt": "ptb",
|
||||||
|
"ru": "rus",
|
||||||
|
"tr": "trk"
|
||||||
|
}
|
||||||
|
return languages[lang]
|
BIN
src/locales/de/LC_MESSAGES/twblue.mo
Normal file
2925
src/locales/de/LC_MESSAGES/twblue.po
Normal file
BIN
src/locales/es/LC_MESSAGES/wxstd.mo
Normal file
@@ -6,8 +6,8 @@ import sys
|
|||||||
|
|
||||||
APP_LOG_FILE = 'debug.log'
|
APP_LOG_FILE = 'debug.log'
|
||||||
ERROR_LOG_FILE = "error.log"
|
ERROR_LOG_FILE = "error.log"
|
||||||
MESSAGE_FORMAT = "%(asctime)s %(name)s %(levelname)s: %(message)s"
|
MESSAGE_FORMAT = u"%(asctime)s %(name)s %(levelname)s: %(message)s"
|
||||||
DATE_FORMAT = "%a %b %d, %Y %H:%M:%S"
|
DATE_FORMAT = u"%b %d, %Y %H:%M:%S"
|
||||||
|
|
||||||
formatter = logging.Formatter(MESSAGE_FORMAT, datefmt=DATE_FORMAT)
|
formatter = logging.Formatter(MESSAGE_FORMAT, datefmt=DATE_FORMAT)
|
||||||
|
|
||||||
|
@@ -17,4 +17,16 @@
|
|||||||
#
|
#
|
||||||
############################################################
|
############################################################
|
||||||
import requests
|
import requests
|
||||||
|
import re
|
||||||
|
|
||||||
|
api_key = "d757b8e7f9221d8b95880a02bab524b7"
|
||||||
|
|
||||||
|
def get_tweet(uri):
|
||||||
|
global api_key
|
||||||
|
data = requests.get("http://api.twishort.com/1.1/get.json", params={"uri": uri, "api_key": api_key})
|
||||||
|
return data.json()["text"]
|
||||||
|
|
||||||
|
def get_uri(url):
|
||||||
|
url_ = re.search("twishort.com/", url)
|
||||||
|
return url[url_.end():]
|
||||||
|
|
||||||
|
105
src/main.py
@@ -19,63 +19,80 @@ A twitter accessible, easy of use and cross platform application."""
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
############################################################
|
############################################################
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
#redirect the original stdout and stderr
|
||||||
|
stdout=sys.stdout
|
||||||
|
stderr=sys.stderr
|
||||||
|
sys.stdout = open(os.path.join(os.getenv("temp"), "stdout.log"), "w")
|
||||||
|
sys.stderr = open(os.path.join(os.getenv("temp"), "stderr.log"), "w")
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
ssmg = None
|
ssmg = None
|
||||||
import gui
|
import gui
|
||||||
|
import wxLangs
|
||||||
import paths
|
import paths
|
||||||
import config
|
import config
|
||||||
import commandline
|
import commandline
|
||||||
import platform
|
import platform
|
||||||
if platform.system() == "Windows":
|
from logger import logger as logging
|
||||||
from logger import logger as logging
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
import osx_prepare
|
|
||||||
osx_prepare.setup()
|
|
||||||
from sessionmanager import manager
|
from sessionmanager import manager
|
||||||
from sessionmanager import gui as smGUI
|
from sessionmanager import gui as smGUI
|
||||||
manager.setup()
|
manager.setup()
|
||||||
import sys
|
import config
|
||||||
|
import output
|
||||||
|
import sound
|
||||||
|
import languageHandler
|
||||||
|
#extra variables to control the temporal stdout and stderr, while the final files are opened. We understand that some errors could happen while all outputs are closed, so let's try to avoid it.
|
||||||
|
stdout_temp=sys.stdout
|
||||||
|
stderr_temp=sys.stderr
|
||||||
|
#if it's a binary version
|
||||||
if hasattr(sys, 'frozen'):
|
if hasattr(sys, 'frozen'):
|
||||||
sys.stderr = open(paths.logs_path("stderr.log"), 'w')
|
sys.stderr = open(paths.logs_path("stderr.log"), 'w')
|
||||||
sys.stdout = open(paths.logs_path("stdout.log"), 'w')
|
sys.stdout = open(paths.logs_path("stdout.log"), 'w')
|
||||||
|
else:
|
||||||
class app(wx.App):
|
sys.stdout=stdout
|
||||||
def __init__(self, *args, **kwargs):
|
sys.stderr=stderr
|
||||||
super(app, self).__init__(*args, **kwargs)
|
#the final log files have been opened succesfully, let's close the temporal files
|
||||||
if platform.system() != "Darwin":
|
stdout_temp.close()
|
||||||
self.start()
|
stderr_temp.close()
|
||||||
else:
|
#finally, remove the temporal files. TW Blue doesn't need them anymore, and we will get more free space on the harddrive
|
||||||
self.mac()
|
os.remove(stdout_temp.name)
|
||||||
|
os.remove(stderr_temp.name)
|
||||||
def mac(self):
|
app = wx.App()
|
||||||
self.hold_frame = wx.Frame(title="None", parent=None)
|
#app = wx.App(redirect=True, useBestVisual=True, filename=paths.logs_path('tracebacks.log'))
|
||||||
self.hold_frame.Show()
|
configured = False
|
||||||
wx.CallLater(10, self.start)
|
configs = []
|
||||||
|
for i in os.listdir(paths.config_path()):
|
||||||
def start(self):
|
if os.path.isdir(paths.config_path(i)): configs.append(i)
|
||||||
ssmg = smGUI.sessionManagerWindow()
|
if len(configs) == 1:
|
||||||
if ssmg.ShowModal() == wx.ID_OK:
|
manager.manager.set_current_session(configs[0])
|
||||||
frame = gui.main.mainFrame()
|
config.MAINFILE = "%s/session.conf" % (manager.manager.get_current_session())
|
||||||
frame.Show()
|
config.setup()
|
||||||
frame.showing = True
|
lang=config.main['general']['language']
|
||||||
if config.main != None and config.main["general"]["hide_gui"] == True and platform.system() == "Windows":
|
languageHandler.setLanguage(lang)
|
||||||
frame.show_hide()
|
sound.setup()
|
||||||
frame.Hide()
|
output.setup()
|
||||||
self.SetTopWindow(frame)
|
configured = True
|
||||||
if hasattr(self, "frame"): self.hold_frame.Hide()
|
else:
|
||||||
# If the user press on cancel.
|
ssmg = smGUI.sessionManagerWindow()
|
||||||
else:
|
if configured == True or ssmg.ShowModal() == wx.ID_OK:
|
||||||
self.Exit()
|
frame = gui.main.mainFrame()
|
||||||
|
frame.Show()
|
||||||
ap = app()
|
frame.showing = True
|
||||||
|
if config.main != None and config.main["general"]["hide_gui"] == True and platform.system() == "Windows":
|
||||||
|
frame.show_hide()
|
||||||
|
frame.Hide()
|
||||||
|
app.SetTopWindow(frame)
|
||||||
|
else:
|
||||||
|
app.Exit()
|
||||||
### I should uncomment this
|
### I should uncomment this
|
||||||
#if platform.system() != "Windows":
|
#if platform.system() != "Windows":
|
||||||
# local = wx.Locale(wx.LANGUAGE_DEFAULT)
|
if languageHandler.getLanguage() != "en":
|
||||||
# local.AddCatalogLookupPathPrefix(paths.locale_path())
|
local = wx.Locale(wxLangs.getLanguage())
|
||||||
# local.AddCatalog("twblue")
|
local.AddCatalogLookupPathPrefix(paths.locale_path())
|
||||||
|
local.AddCatalog("twblue")
|
||||||
|
#languageHandler.setLanguage(lang)
|
||||||
#ap = app(redirect=True, useBestVisual=True, filename=paths.logs_path('tracebacks.log'))
|
#ap = app(redirect=True, useBestVisual=True, filename=paths.logs_path('tracebacks.log'))
|
||||||
#wx.CallLater(10, start)
|
#wx.CallLater(10, start)
|
||||||
ap.MainLoop()
|
app.MainLoop()
|
||||||
|
|
||||||
|
@@ -1,7 +1,12 @@
|
|||||||
import gettext_windows, os
|
import os
|
||||||
|
import languageHandler
|
||||||
|
|
||||||
def get(rootFolder):
|
def get(rootFolder):
|
||||||
defaultLocale = gettext_windows.get_language()[0][:2]
|
# defaultLocale = gettext_windows.get_language()[0][:2]
|
||||||
|
defaultLocale = languageHandler.curLang
|
||||||
|
if len(defaultLocale) > 2:
|
||||||
|
defaultLocale = defaultLocale[:2]
|
||||||
|
print defaultLocale
|
||||||
if os.path.exists(rootFolder+"/"+defaultLocale):
|
if os.path.exists(rootFolder+"/"+defaultLocale):
|
||||||
return defaultLocale
|
return defaultLocale
|
||||||
else:
|
else:
|
||||||
|
@@ -3,6 +3,7 @@ import threading
|
|||||||
import wx
|
import wx
|
||||||
from twython import TwythonRateLimitError
|
from twython import TwythonRateLimitError
|
||||||
import time
|
import time
|
||||||
|
from wx.lib.pubsub import pub
|
||||||
|
|
||||||
def call_threaded(func, *args, **kwargs):
|
def call_threaded(func, *args, **kwargs):
|
||||||
#Call the given function in a daemonized thread and return the thread.
|
#Call the given function in a daemonized thread and return the thread.
|
||||||
@@ -19,3 +20,14 @@ def call_threaded(func, *args, **kwargs):
|
|||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
return thread
|
return thread
|
||||||
|
|
||||||
|
def stream_threaded(func, *args, **kwargs):
|
||||||
|
def new_func(*a, **k):
|
||||||
|
try:
|
||||||
|
func(*a, **k)
|
||||||
|
except:
|
||||||
|
pub.sendMessage("streamError")
|
||||||
|
thread = threading.Thread(target=new_func, args=args, kwargs=kwargs)
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
return thread
|