Compare commits

...

88 Commits

Author SHA1 Message Date
375cdd2528 0.50 Release changes. Last commit for this year. 2014-12-18 00:59:18 -06:00
jmdaweb
836675bfa1 New sound for trending topics, updated contributors in application.py 2014-12-16 22:12:29 +01:00
jmdaweb
2b3c7601f2 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-15 18:10:55 +01:00
eb2c315abb Bugfixes for lists buffers and autocomplete 2014-12-15 09:53:13 -06:00
e12f440cf2 Build_installer fixes 2014-12-14 19:07:18 -06:00
jmdaweb
57dc271fb0 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-12 19:33:37 +01:00
193cafcc9d A bash script to create TWBlue installer versions has been added 2014-12-12 12:21:59 -06:00
jmdaweb
e7b6c39f76 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-12 11:26:19 +01:00
7df2441442 Fixes for the Snapshot6 compilation process 2014-12-11 15:37:53 -06:00
jmdaweb
aa687450f6 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-11 21:52:01 +01:00
6e0ae38d4e Code changes for Snapshot 6 2014-12-11 12:21:37 -06:00
jmdaweb
fa8c9f639c Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-11 17:26:02 +01:00
9502cef251 Final additions for TWBlue 0.50. TWBlue's source code has been frozen 2014-12-11 10:03:02 -06:00
jmdaweb
982dd78f81 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-11 16:26:30 +01:00
jmdaweb
4b98f27968 Now TWBlue.exe.log shouldn't be created, so admin rights aren't required the first time in the installed version. 2014-12-11 16:25:22 +01:00
jmdaweb
ecb0fa8f2b Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-10 20:04:17 +01:00
ef2ff9dbfc This tries to fix the bug related to stdout and stderr for TWBlue installed 2014-12-10 12:44:11 -06:00
jmdaweb
1fe94642a2 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-10 18:33:18 +01:00
b232a0b0ad Ignoring clients improvements: adding clients from the action users dialog does always work, no matters the buffer the client is added 2014-12-10 11:19:38 -06:00
2ce59cf208 Autocompletion improvements. Adding/removing users manually is now possible. 2014-12-09 17:56:43 -06:00
jmdaweb
ceb8266828 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-09 17:10:44 +01:00
jmdaweb
21970f2c94 installer: now two radio buttons are displayed in the license page. 2014-12-09 17:10:03 +01:00
14965f73d3 Invisible shorcuts no blocks itself when passes through the events buffer 2014-12-09 08:26:33 -06:00
jmdaweb
267b454fc6 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-09 10:36:30 +01:00
c3dc00c241 Mantis support is back on TWBlue 2014-12-08 22:06:44 -06:00
jmdaweb
9fb67f01ae Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-08 20:49:05 +01:00
6d1cd0b1fc The mention all button works as espected when someone replies to a retweet 2014-12-08 13:38:27 -06:00
a7ef572d05 Now, TWBlue does not block the system when any session is closed or turned off 2014-12-08 12:46:29 -06:00
403784b77a The switcher module does not close the app when it is cancelled. 2014-12-08 10:17:07 -06:00
d032a6d8f7 Use invisible interface's keyboard shorcuts even if the window is displayed 2014-12-08 09:44:02 -06:00
jmdaweb
231d43ab22 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-08 00:32:46 +01:00
5e3bcfc82e Mentions are detected even if username's capitalization is incorrect 2014-12-07 13:24:56 -06:00
8e92d97260 Trending topic buffers are removed from configuration 2014-12-07 12:22:19 -06:00
bb1ffa9ff1 Merge branch 'master' of https://github.com/manuelcortez/twblue 2014-12-07 09:31:46 -06:00
104bc4ec8c Changes for Snapshot 5 release 2014-12-07 09:31:39 -06:00
jmdaweb
6ae555fe72 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-06 20:18:10 +01:00
jmdaweb
dd51516c30 Improved installer script. In the finish page, it shows a checkbox to run TW Blue, and a button to go to the website. Modified installer version to 0.50 2014-12-06 20:17:21 +01:00
jmdaweb
4e18bb0721 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-06 00:09:11 +01:00
jmdaweb
0492e65aa0 Merge branch 'master' of github.com:manuelcortez/TWBlue 2014-12-06 00:08:43 +01:00
jmdaweb
a5fb59d7a7 fixed submodule 2014-12-06 00:08:09 +01:00
f2fab29cb7 Trending topics improvements 2014-12-05 17:05:29 -06:00
jmdaweb
f36accf89b Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-06 00:01:21 +01:00
a13536233e Improvements on autocomplete, now it updates its database at startup 2014-12-05 16:23:54 -06:00
bc2d448464 Updated translations 2014-12-05 12:21:40 -06:00
60ec9ab215 Date is displayed properly in the GUI 2014-12-05 11:31:58 -06:00
20b5fc079b onFocus will play the ogg file instead the mp3 2014-12-05 11:25:29 -06:00
80e231b689 Merge branch 'master' of https://github.com/manuelcortez/twblue 2014-12-05 11:21:27 -06:00
7137d437bd Basic tt support, fixed some English mistakes reported by @sukiletxe 2014-12-05 11:20:55 -06:00
jmdaweb
a07b3c1829 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-02 23:36:47 +01:00
jmdaweb
c062c10542 Converted the geo sound to ogg format 2014-12-02 23:35:37 +01:00
jmdaweb
0e7c070bd4 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-02 22:18:45 +01:00
jmdaweb
9f35d079b1 Updated windows-dependencies submodule with the new pycurl 2014-12-02 22:18:11 +01:00
jmdaweb
a3fca4d2a0 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-02 21:27:00 +01:00
ada6f1fb0d Fixing some issues with tweets and retweets 2014-12-02 11:40:39 -06:00
79124810b0 Reverse geocode is now in TWBlue. Ctrl+win+g and CTRL+Shift+Win+G and in the tweet menu on the menu bar 2014-12-01 17:19:03 -06:00
fee7254d55 Configuration for autocompletion users 2014-12-01 15:21:25 -06:00
jmdaweb
c12902d011 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-12-01 13:14:36 +01:00
2f7eb12104 There is a context menu on buffers. When the applications key or the right mouse button is pressed that menu is displayed. It only works for the GUI 2014-12-01 05:55:25 -06:00
855cefeb8d Now the session manager does not appear ramdonly 2014-11-30 22:35:48 -06:00
e649b883c6 gendoc script has been deletedo so it isn't necessary 2014-11-30 17:43:00 -06:00
jmdaweb
1a61f2d790 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-11-30 22:26:33 +01:00
jmdaweb
6abfba317a updated windows dependencies submodule 2014-11-30 22:25:50 +01:00
jmdaweb
18ce92daab Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-11-30 11:19:03 +01:00
jmdaweb
84f729f42b Now setup.py shouldn't include an unnecesary mfc90.dll in x64 builds 2014-11-30 11:17:26 +01:00
jmdaweb
4e7666cf52 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-11-30 00:42:56 +01:00
88e0fbb531 Updated readmes 2014-11-29 11:29:22 -06:00
af32b3ceb6 Merge branch 'master' of github.com:manuelcortez/TWBlue 2014-11-29 11:09:39 -06:00
94dc083650 Windows-dependencies fix 2014-11-29 11:09:33 -06:00
acb3cce1d6 Changes on snapshot 4; audio uploader fixes 2014-11-29 11:07:06 -06:00
jmdaweb
2b3e1099f6 Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-11-27 22:05:21 +01:00
jmdaweb
60507e8f6b Pressing enter, spacebar or mouse left click should show window if it's not visible 2014-11-27 17:31:10 +01:00
jmdaweb
75bfc17bb1 Merge branch 'master' of github.com:manuelcortez/TWBlue 2014-11-27 17:19:08 +01:00
acb8c5acd3 TWBlue starts automatically if there is only an account 2014-11-27 07:18:05 -06:00
jmdaweb
5bcf04fc94 Merge branch 'master' of github.com:manuelcortez/TWBlue 2014-11-27 10:12:54 +01:00
0f2fbc775a Switch account is now possible from the application menu 2014-11-26 17:26:53 -06:00
1b684cd12c The session manager has a remove account button 2014-11-26 16:38:46 -06:00
9d64901791 Session manager deletes invalid sessions; plays audio from any URL when the tweet has more than one 2014-11-26 13:45:24 -06:00
18f7cb6c96 Replies will not show on list buffers 2014-11-26 11:52:36 -06:00
7f673fb9de Show more information about tweets on the ViewTweets dialog 2014-11-26 05:43:13 -06:00
jmdaweb
f6f542fbec Merge branch 'master' of https://github.com/manuelcortez/TWBlue 2014-11-25 23:09:33 +01:00
jmdaweb
a211e2e5e4 Merge branch 'master' of github.com:manuelcortez/TWBlue 2014-11-25 22:49:12 +01:00
3ec8ac31a5 Autocompletion for users has been implemented 2014-11-25 12:30:00 -06:00
jmdaweb
dacbb19586 added the windows dependencies as a git submodule, so the users can get them only if they plan to build a binary version 2014-11-25 14:44:59 +01:00
jmdaweb
55691cca20 Fixed setup.py, now it takes the correct bootstrap.exe in x64 builds 2014-11-24 17:26:35 +01:00
0104b97df0 Merge branch 'master' of github.com:manuelcortez/TWBlue 2014-11-21 11:39:35 -06:00
aa58e61e5e TWBlue shows followers and friends in realtime again 2014-11-21 11:37:41 -06:00
jmdaweb
fae204f3c2 updated windows build instructions 2014-11-19 16:07:55 +01:00
d8149a4c96 TWBlue has access to the direct messages on all accounts 2014-11-14 16:05:34 -06:00
90 changed files with 12299 additions and 6306 deletions

4
.gitignore vendored
View File

@@ -1,12 +1,8 @@
*.pyc
*~
windows-dependencies/*
src/build/
src/dist/
src/config/
src/config1/
src/config2/
src/config3/
src/dropbox/
src/logs/
src/documentation/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "windows-dependencies"]
path = windows-dependencies
url = https://github.com/jmdaweb/TWBlue_deps_windows.git

View File

@@ -16,3 +16,57 @@ TW Blue is an app designed to use Twitter in a simple and fast way and avoiding,
* and more!
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.
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 windows-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,](http://python.org) version 2.7.8
* [wxPython](http://www.wxpython.org) for Python 2.7, version 3.0.0 (2.9.5 to avoid problems in windows xp)
* [Python windows extensions (pywin32)](http://www.sourceforge.net/projects/pywin32/) for python 2.7, build 218
* [ConfigObj,](http://www.voidspace.org.uk/python/configobj.html) version 4.7.2
* [oauthlib](https://pypi.python.org/pypi/oauthlib/0.6.1) 0.6.1
* [Pycurl](http://pycurl.sourceforge.net) 7.19.3.1 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/)
* [Requests](http://www.python-requests.org/en/latest/) 2.2.1: [Recommended download site](https://pypi.python.org/pypi/requests/2.2.1)
* [Requests-oauthlib](https://github.com/requests/requests-oauthlib) 0.4.0
* [Suds](https://fedorahosted.org/suds) 0.4: [Recommended download site](https://pypi.python.org/pypi/suds/0.4)
* [Pygeocoder: ](http://code.xster.net/pygeocoder/wiki/Home) You can install this dependency by using pip or easy_install.
* 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,](http://www.rarewares.org/ogg-oggenc.php) version 2.87
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 src folder.
If you want to build the binary version:
* [Py2exe](http://www.sourceforge.net/projects/py2exe/) for Python 2.7, version 0.6.9
* [Setuptools](https://pypi.python.org/pypi/setuptools) 2.1
- [7-zip](http://7-zip.org)
### How to run tw blue from source
Run the file main.py located in the src 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 build a binary version
You must type the following command. In this example, we will assume that python is the path to the python executable (x86 or x64) and that you have navigated to the src 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.

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
documentation = []
documentation.append(_(u"""Documentation for TW Blue 0.46"""))
documentation.append(_(u"""Documentation for TW Blue 0.50"""))
# Translators: This is the new line character, don't change it in the translations.
documentation.append(_(u"""
"""))
@@ -47,16 +47,16 @@ documentation.append(_(u"""
documentation.append(_(u"""First off, it's necessary to authorise the program so it can access your Twitter account and act on your behalf. The authorisation process is quite simple, and the program never gets data such as your username and password. In order to authorise the application, you just need to run the main executable file, called TWBlue.exe (on some computers it may appear simply as TWBlue)."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""When executed, if you have not previously configured the program, it will show a dialogue box where it tells you'll be taken to Twitter in order to authorise the application as soon as you press OK. To begin the authorisation process, press the only available button on the box."""))
#$documentation.append(_(u"""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"""Your default browser will open on the Twitter page to request authorisation. Enter your user name and password if you're not already logged in, look for the authorise button, and press it."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Read the instructions you will get if the process is successful. In summary, you will be given a numeric code with several digits you must paste on an edit field open by the application on another window."""))
#$documentation.append(_(u"""Read the instructions you will get if the process is successful. In summary, you will be given a numeric code with several digits you must paste on an edit field open by the application on another window."""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""Paste the verification code, and press the enter key. """))
#$documentation.append(_(u"""Paste the verification code, and press the enter key. """))
documentation.append(_(u"""
"""))
documentation.append(_(u"""If all went well, the application will start playing sounds, indicating your data are being updated."""))
@@ -65,10 +65,10 @@ 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"""
"""))
documentation.append(_(u"""## The program's interface {#interface}"""))
documentation.append(_(u"""## The program's interface"""))
documentation.append(_(u"""
"""))
documentation.append(_(u"""The easiest way to describe the graphical interface of the application is a window with a menu bar with four menus (application, tweet, user and help), a list with several elements, and, in most cases, three buttons: tweet, retweet and reply. The actions available for each element are described below."""))
documentation.append(_(u"""The easiest way to describe the graphical 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."""))

View File

@@ -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.

View File

@@ -13,15 +13,20 @@ SetCompressor /solid lzma
SetDatablockOptimize on
VIAddVersionKey ProductName "TW Blue"
VIAddVersionKey LegalCopyright "Copyright 2014 Manuel Cortez."
VIAddVersionKey ProductVersion "0.48"
VIAddVersionKey FileVersion "0.48"
VIProductVersion "0.48.0.0"
VIAddVersionKey ProductVersion "0.50"
VIAddVersionKey FileVersion "0.50"
VIProductVersion "0.50.0.0"
!insertmacro MUI_PAGE_WELCOME
!define MUI_LICENSEPAGE_RADIOBUTTONS
!insertmacro MUI_PAGE_LICENSE "license.txt"
!insertmacro MUI_PAGE_DIRECTORY
var StartMenuFolder
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_LINK "Visit TW Blue website"
!define MUI_FINISHPAGE_LINK_LOCATION "http://twblue.com.mx"
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
!define MUI_FINISHPAGE_RUN_PARAMETERS "-i"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
@@ -60,10 +65,10 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "D
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortez"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.47"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.50"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "http://twblue.com.mx"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 47
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 50
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoRepair" 1
SectionEnd

View File

@@ -15,6 +15,7 @@ reverse_timelines = boolean(default=False)
time_to_check_streams = integer(default=30)
announce_stream_status = boolean(default=True)
ask_at_exit = boolean(default=True)
use_invisible_keyboard_shorcuts = boolean(default=False)
[sound]
volume = float(default=1.0)
@@ -35,11 +36,14 @@ timelines = list(default=list())
tweet_searches = list(default=list())
lists = list(default=list())
favourites_timelines = list(default=list())
trending_topic_buffers = list(default=list())
muted_buffers = list(default=list())
autoread_buffers = list(default=list())
[mysc]
spelling_language = string(default="")
save_followers_in_autocompletion_db = boolean(default=False)
save_friends_in_autocompletion_db = boolean(default=False)
[services]
dropbox_token=string(default="")
@@ -86,3 +90,6 @@ search = string(default="control+win+-")
edit_keystrokes = string(default="control+win+k")
view_user_lists = string(default="control+win+l")
get_more_items = string(default="alt+win+pageup")
reverse_geocode = string(default="control+win+g")
view_reverse_geocode = string(default="control+win+shift+g")
get_trending_topics = string(default="control+win+t")

View File

@@ -2,18 +2,18 @@
name = 'TW Blue'
snapshot = False
if snapshot == False:
version = "0.48"
version = "0.50"
update_url = 'http://twblue.com.mx/updates/tw_blue.json'
else:
version = "4"
version = "6"
update_url = 'http://twblue.com.mx/updates/snapshots.json'
author = u"Manuel Cortéz"
authorEmail = "info@twblue.com.mx"
authorEmail = "manuel@manuelcortez.net"
copyright = u"copyright (C) 2013-2014, Manuel cortéz"
description = u"TW Blue is an app designed to use Twitter in a simple and fast way and avoiding, as far as possible, the consumtion of excessive resources of the machine where its running. With this app youll have access to most twitter features."
translators = [u"Bryner Villalobos (English)", u"Mohammed Al Shara (Arabic)", u"Salva Doménech, Juan Carlos Rivilla(Catalan)", u"Manuel cortéz(Spanish)", u"Sukil Etxenike Arizaleta(Basque)", u"Jani Kinnunen(finnish)", u"Javier Currás, José Manuel Delicado, Alba Quinteiro(Galician)", u"Robert Osztolykan(Hungarian)", u"Paweł Masarczyk(Polish)", u"Odenilton Júnior Santos(Portuguese)", u"Alexander Jaszyn(Russian)", u"Burak (Turkish)"]
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"Alba Quinteiro(Galician)", u"Robert Osztolykan(Hungarian)", u"Paweł Masarczyk(Polish)", u"Odenilton Júnior Santos(Portuguese)", u"Alexander Jaszyn(Russian)", u"Burak (Turkish)"]
url = u"http://twblue.com.mx"
#report_bugs_url = "http://twblue.com.mx/errores/api/soap/mantisconnect.php?wsdl"
report_bugs_url = "http://twblue.com.mx/bugs/api/soap/mantisconnect.php?wsdl"
# Tokens
app_key = '8pDLbyOW3saYnvSZ4uLFg'

View File

@@ -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")
parser.add_argument("-d", "--data-directory", action="store", dest="directory", help="Specifies the directory where TW Blue saves the data files")
args = parser.parse_args()
if args.installed == True: paths.mode = "installed"
if args.installed == True:
paths.mode = "installed"
elif args.portable == True:
paths.mode = "portable"
if args.directory != None: paths.directory = args.directory

View File

@@ -16,6 +16,7 @@ class soundsTutorial(wx.Dialog):
_(u"A bug has happened"),
_(u"You've added a tweet to your favourites"),
_(u"Someone's favourites have been updated"),
_(u"The tweet has coordinates to determine its location"),
_(u"There are no more tweets to read"),
_(u"A list has a new tweet"),
_(u"You can't add any more characters on the tweet"),

View File

@@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View 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."))

View 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()

View 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)

View 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()

View 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()

View 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()

View 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()

View File

@@ -8,3 +8,4 @@ from lists import *
from people import *
from tweet_searches import *
from user_searches import *
from trends import *

View File

@@ -27,6 +27,7 @@ import logging as original_logger
import output
import platform
import datetime
import menus
from twitter import prettydate
from multiplatform_widgets import widgets
from mysc import event
@@ -39,6 +40,8 @@ class basePanel(wx.Panel):
def bind_events(self):
self.Bind(event.MyEVT_OBJECT, self.update)
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)
if self.system == "Windows":
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.onFocus)
@@ -147,7 +150,7 @@ class basePanel(wx.Panel):
if config.main["general"]["relative_times"] == True:
# On windows we need only put the new date on the column, but under linux and mac it isn't possible.
if self.system == "Windows":
original_date = datetime.datetime.strptime(tweet["created_at"], "%a %b %d %H:%M:%S +0000 %Y")
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 = prettydate(original_date)
self.list.list.SetStringItem(self.list.get_selected(), 2, ts)
@@ -155,6 +158,8 @@ class basePanel(wx.Panel):
self.list.list.SetString(self.list.get_selected(), " ".join(self.compose_function(self.db.settings[self.name_buffer][self.list.get_selected()], self.db)))
if twitter.utils.is_audio(tweet):
sound.player.play("audio.ogg", False)
if twitter.utils.is_geocoded(tweet):
sound.player.play("geo.ogg", False)
def start_streams(self):
if self.name_buffer == "sent":
@@ -181,16 +186,19 @@ class basePanel(wx.Panel):
except TwythonError as e:
output.speak(e.message)
for i in items:
if twitter.utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False:
self.db.settings[self.name_buffer].insert(0, i)
else:
self.db.settings[self.name_buffer].append(i)
if config.main["general"]["reverse_timelines"] == False:
for i in items:
if twitter.utils.is_allowed(i) == True:
tweet = self.compose_function(i, self.db)
self.list.insert_item(True, *tweet)
else:
for i in items:
if twitter.utils.is_allowed(i) == True:
tweet = self.compose_function(i, self.db)
self.list.insert_item(False, *tweet)
output.speak(_(u"%s items retrieved") % (len(items)))
@@ -278,10 +286,15 @@ class basePanel(wx.Panel):
if self.name_buffer in config.main["other_buffers"]["autoread_buffers"]:
output.speak(" ".join(tweet[:2]))
def interact(self, ev):
try:
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):
try:
tweet = self.get_tweet()
urls = twitter.utils.find_urls_in_text(tweet["text"])
except:
urls = []
@@ -297,14 +310,21 @@ class basePanel(wx.Panel):
ev.Skip()
return
if event == "audio" and len(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":
if len(urls) == 0: return
elif len(urls) == 1:
output.speak(_(u"Opening URL..."), True)
webbrowser.open(urls[0])
elif len(urls) > 1:
gui.dialogs.urlList.urlList(urls).ShowModal()
urlList = gui.dialogs.urlList.urlList(urls)
if urlList.ShowModal() == wx.ID_OK:
webbrowser.open_new_tab(urls[urlList.lista.GetSelection()])
elif event == "volume_down":
if config.main["sound"]["volume"] > 0.05:
config.main["sound"]["volume"] = config.main["sound"]["volume"]-0.05
@@ -362,3 +382,18 @@ class basePanel(wx.Panel):
self.list.select_item(len(self.db.settings[self.name_buffer])-1)
else:
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())

View File

@@ -19,6 +19,7 @@
import wx
import sound
import gui.dialogs
import menus
import logging as original_logger
from base import basePanel
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)
self.retweetBtn.Disable()
self.responseBtn.Disable()
self.type = "direct_message"
def destroy_status(self, ev):
index = self.list.get_selected()
@@ -46,3 +48,12 @@ class dmPanel(basePanel):
call_threaded(self.twitter.api_call, call_name="send_direct_message", _sound="dm_sent.ogg", text=dlg.text.GetValue(), screen_name=dlg.cb.GetValue())
if ev != None:
self.list.list.SetFocus()
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())

View File

@@ -21,6 +21,7 @@ import sound
import config
import platform
import gui.dialogs
import menus
import output
import logging as original_logger
from multiplatform_widgets import widgets
@@ -36,6 +37,8 @@ class eventsPanel(wx.Panel):
def bind_events(self):
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):
pass
@@ -132,3 +135,12 @@ class eventsPanel(wx.Panel):
ev.Skip()
except:
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())

View File

@@ -30,7 +30,7 @@ class favsPanel(basePanel):
self.type = "favourites_timeline"
def start_streams(self):
num = twitter.starting.get_favourites_timeline(self.db, self.twitter, self.name_buffer, param=self.argumento, count=200)
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:
sound.player.play(self.sound)
if self.list.get_count() > 0: self.put_items(num)

View File

@@ -35,7 +35,7 @@ class listPanel(basePanel):
def start_streams(self):
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
def retrieve_ids(self):

134
src/gui/buffers/menus.py Normal file
View 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)

View File

@@ -20,18 +20,20 @@ import wx
from multiplatform_widgets import widgets
class accountPanel(wx.Panel):
def __init__(self, parent):
def __init__(self, parent, name_buffer):
super(accountPanel, self).__init__(parent=parent)
self.type = "account"
self.name_buffer = name_buffer
sizer = wx.BoxSizer(wx.VERTICAL)
self.list = widgets.list(self, _(u"Announce"))
sizer.Add(self.list.list, 0, wx.ALL, 5)
self.SetSizer(sizer)
class emptyPanel(accountPanel):
def __init__(self, parent):
super(emptyPanel, self).__init__(parent=parent)
self.type = "empty"
def get_more_items(self):
output.speak(_(u"This action is not supported for this buffer"))
class emptyPanel(accountPanel):
def __init__(self, parent):
super(emptyPanel, self).__init__(parent=parent, name_buffer="")
self.type = "empty"

View File

@@ -21,6 +21,7 @@ import sound
import config
import twitter
import gui.dialogs
import menus
import logging as original_logger
import output
from multiplatform_widgets import widgets
@@ -36,13 +37,15 @@ class peoplePanel(basePanel):
self.Bind(event.MyEVT_OBJECT, self.update)
self.Bind(event.MyEVT_DELETED, self.Remove)
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):
self.list = widgets.list(self, _(u"User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, size=(800, 800))
def __init__(self, parent, window, name_buffer, function, argumento=None, sound="", timeline=False):
self.type = "people"
super(peoplePanel, self).__init__(parent, window, name_buffer, function, argumento=argumento, sound=sound)
self.type = "people"
self.responseBtn.SetLabel(_(u"Mention"))
self.retweetBtn.Disable()
self.compose_function = twitter.compose.compose_followers_list
@@ -140,3 +143,18 @@ class peoplePanel(basePanel):
def remove_buffer(self):
pos = None
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())

View File

@@ -17,35 +17,155 @@
#
############################################################
import wx
import sound
import config
import twitter
import gui.dialogs
import twitter
import config
import sound
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")
class trendPanel(basePanel):
def __init__(self, parent, window, name_buffer, *args, **kwargs):
super(searchPanel, self).__init__(parent, window, name_buffer, sound)
self.type = "trend"
self.args = kwargs
class trendsPanel(wx.Panel):
def start_streams(self):
num = twitter.starting.search(self.db, self.twitter, self.name_buffer, **self.args)
if num > 0: sound.player.play("search_updated.ogg")
self.put_items(num)
return num
def compose_function(self, trend):
return [trend["name"]]
def bind_events(self):
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)
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):
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:
names = config.main["other_buffers"]["tweet_searches"]
user = self.name_buffer[:-7]
log.info(u"Deleting %s's search term" % user)
if user in names:
names.remove(user)
self.db.settings.pop(self.name_buffer)
pos = self.db.settings["buffers"].index(self.name_buffer)
self.db.settings["buffers"].remove(self.name_buffer)
return pos
topics = config.main["other_buffers"]["trending_topic_buffers"]
topic = self.name_buffer[:-3]
log.info(u"Deleting %s's trending topics buffer" % topic)
if topic in topics:
topics.remove(topic)
return 0
def start_streams(self):
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())

View File

@@ -40,7 +40,7 @@ class searchUsersPanel(peoplePanel):
self.create_list()
self.args = args
self.kwargs = kwargs
self.type = "timeline"
self.type = "user_search"
def start_streams(self):
num = twitter.starting.search_users(self.db, self.twitter, self.name_buffer, **self.kwargs)

View File

@@ -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

View File

@@ -29,6 +29,7 @@ import webbrowser
import paths
import platform
from mysc import restart
from extra.autocompletionUsers import settings
log = original_logger.getLogger("configuration")
system = platform.system()
@@ -50,9 +51,13 @@ class general(wx.Panel):
langBox.Add(language, 0, wx.ALL, 5)
langBox.Add(self.language, 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.SetValue(config.main["general"]["ask_at_exit"])
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.SetValue(config.main["general"]["relative_times"])
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)
self.SetSizer(sizer)
class other_buffers(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
@@ -264,6 +270,7 @@ class configurationDialog(wx.Dialog):
self.general = general(notebook)
notebook.AddPage(self.general, _(u"General"))
self.general.SetFocus()
self.Bind(wx.EVT_BUTTON, self.autocompletion, self.general.au)
self.buffers = other_buffers(notebook)
notebook.AddPage(self.buffers, _(u"Show other buffers"))
self.ignored_clients = ignoredClients(notebook)
@@ -285,6 +292,9 @@ class configurationDialog(wx.Dialog):
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def autocompletion(self, ev):
configuration = settings.autocompletionSettings(self.parent)
def check_followers_change(self):
if self.buffers.followers.GetValue() != self.buffers.followers_value:
if self.buffers.followers.GetValue() == True:
@@ -376,6 +386,13 @@ class configurationDialog(wx.Dialog):
if platform.system() == "Windows":
config.main["general"]["voice_enabled"] = self.general.disable_sapi5.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"]["max_api_calls"] = self.general.apiCalls.GetValue()
config.main["general"]["max_tweets_per_call"] = self.general.itemsPerApiCall.GetValue()

View File

@@ -23,6 +23,7 @@ import twitter
from twitter import utils
from twython import TwythonError
import output
import re
class follow(wx.Dialog):
def __init__(self, parent, default="follow"):
@@ -48,6 +49,7 @@ class follow(wx.Dialog):
self.block = wx.RadioButton(panel, -1, _(u"Block"))
self.unblock = wx.RadioButton(panel, -1, _(u"Unblock"))
self.reportSpam = wx.RadioButton(panel, -1, _(u"Report as spam"))
self.ignore_client = wx.RadioButton(panel, -1, _(u"Ignore tweets from this client"))
self.setup_default(default)
actionSizer.Add(label2)
actionSizer.Add(self.follow)
@@ -57,6 +59,7 @@ class follow(wx.Dialog):
actionSizer.Add(self.block)
actionSizer.Add(self.unblock)
actionSizer.Add(self.reportSpam)
actionSizer.Add(self.ignore_client)
sizer = wx.BoxSizer(wx.VERTICAL)
ok = wx.Button(panel, wx.ID_OK, _(u"OK"))
ok.Bind(wx.EVT_BUTTON, self.onok)
@@ -133,6 +136,16 @@ class follow(wx.Dialog):
self.Destroy()
except TwythonError as err:
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):
if default == "follow":

View File

@@ -127,7 +127,7 @@ class listViewer(wx.Dialog):
output.speak(_(u"This list is arready opened."))
return
listUI = gui.buffers.lists.listPanel(self.nb, self.parent, list_updated["slug"]+"-list", argumento=utils.find_list(list_updated["slug"], self.db.settings["lists"]))
self.nb.AddPage(listUI, _(u"List for %s") % (list_updated["slug"],))
self.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")
num = listUI.start_streams()
listUI.put_items(num)
@@ -165,6 +165,14 @@ class userListViewer(listViewer):
self.db.settings["lists"].append(list)
except TwythonError as e:
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):

View File

@@ -30,6 +30,7 @@ from twython import TwythonError
from extra import translator, AudioUploader
import platform
from extra.AudioUploader import transfer
from extra.autocompletionUsers import completion
if platform.system() != "Darwin":
from extra.AudioUploader import dropbox_transfer
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.Bind(wx.EVT_BUTTON, self.onSend)
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.Bind(wx.EVT_BUTTON, self.onCancel)
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
@@ -202,8 +205,11 @@ class tweet(textLimited):
self.mainBox.Add(self.ok_cancelSizer)
selectId = wx.NewId()
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([
(wx.ACCEL_CTRL, ord('A'), selectId),
#(wx.ACCEL_ALT, ord('A'), autocompletionId),
])
self.SetAcceleratorTable(self.accel_tbl)
self.panel.SetSizer(self.mainBox)
@@ -215,6 +221,10 @@ class tweet(textLimited):
self.onTimer(wx.EVT_CHAR_HOOK)
self.SetClientSize(self.mainBox.CalcMin())
def autocompletion(self, event=None):
c = completion.autocompletionUsers(self)
c.show_menu()
def onUpload_image(self, ev):
if self.upload_image.GetLabel() == _(u"Discard image"):
self.image = None
@@ -305,7 +315,7 @@ class reply(tweet):
super(reply, self).__init__(message, title, text, parent)
self.in_reply_to = parent.db.settings[parent.name_buffer][parent.list.get_selected()]["id"]
self.text.SetInsertionPoint(len(self.text.GetValue()))
self.mentionAll = wx.Button(self, -1, _(u"Mention to all"), size=wx.DefaultSize)
self.mentionAll = wx.Button(self, -1, _(u"Men&tion all"), size=wx.DefaultSize)
self.mentionAll.Disable()
self.mentionAll.Bind(wx.EVT_BUTTON, self.mentionAllUsers)
self.buttonsBox1.Add(self.mentionAll, 0, wx.ALL, 5)
@@ -332,10 +342,14 @@ class reply(tweet):
class viewTweet(wx.Dialog):
def __init__(self, tweet):
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)
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.SetFont(self.text.GetFont())
(x, y, z) = dc.GetMultiLineTextExtent("0"*140)
@@ -346,6 +360,20 @@ class viewTweet(wx.Dialog):
textBox.Add(self.text, 1, wx.EXPAND, 5)
mainBox = wx.BoxSizer(wx.VERTICAL)
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":
spellcheck = wx.Button(panel, -1, _("Spelling correction"), size=wx.DefaultSize)
spellcheck.Bind(wx.EVT_BUTTON, self.onCheck)
@@ -417,3 +445,87 @@ class viewTweet(wx.Dialog):
urlList.unshorten(urls, self).ShowModal()
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()

View File

@@ -19,24 +19,34 @@
import wx
class trendingTopicsDialog(wx.Dialog):
def __init__(self):
super(searchDialog, self).__init__(None, -1)
def __init__(self, information):
super(trendingTopicsDialog, self).__init__(None, -1)
self.countries = {}
self.cities = {}
self.information = information
self.split_information()
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetTitle(_(u"Search on Twitter"))
label = wx.StaticText(panel, -1, _(u"Search"))
self.term = wx.TextCtrl(panel, -1,)
dc = wx.WindowDC(self.term)
dc.SetFont(self.term.GetFont())
self.term.SetSize(dc.GetTextExtent("0"*40))
self.SetTitle(_(u"View trending topics"))
label = wx.StaticText(panel, -1, _(u"Trending topics by"))
sizer.Add(label, 0, wx.ALL, 5)
sizer.Add(self.term, 0, wx.ALL, 5)
self.tweets = wx.RadioButton(panel, -1, _(u"Tweets"), style=wx.RB_GROUP)
self.users = wx.RadioButton(panel, -1, _(u"Users"))
self.country = wx.RadioButton(panel, -1, _(u"Country"), style=wx.RB_GROUP)
self.city = wx.RadioButton(panel, -1, _(u"City"))
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.Add(self.tweets, 0, wx.ALL, 5)
radioSizer.Add(self.users, 0, wx.ALL, 5)
radioSizer.Add(label, 0, wx.ALL, 5)
radioSizer.Add(self.country, 0, wx.ALL, 5)
radioSizer.Add(self.city, 0, wx.ALL, 5)
sizer.Add(radioSizer, 0, wx.ALL, 5)
label = wx.StaticText(panel, -1, _(u"Location"))
self.location = wx.ListBox(panel, -1, choices=[], style=wx.CB_READONLY)
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.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Close"))
@@ -46,3 +56,25 @@ class trendingTopicsDialog(wx.Dialog):
sizer.Add(btnsizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
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)

View File

@@ -46,8 +46,7 @@ class urlList(wx.Dialog):
self.SetClientSize(sizer.CalcMin())
def onGo(self, ev):
webbrowser.open(self.lista.GetStringSelection())
self.Destroy()
self.EndModal(wx.ID_OK)
def populate_list(self):
for i in self.urls:

View File

@@ -32,10 +32,16 @@ import output
import platform
import urllib2
import sysTrayIcon
import switchModule
from wx.lib.pubsub import pub
import languageHandler
from extra.autocompletionUsers import settings as autocompletionUsersSettings
import pygeocoder
from pygeolib import GeocoderError
from sessionmanager import manager
from issueReporter import gui as issueReporterGUI
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 urllib2 import URLError
from mysc.repeating_timer import RepeatingTimer
@@ -46,6 +52,7 @@ from extra import SoundsTutorial
from keystrokeEditor import gui as keystrokeEditorGUI
log = original_logger.getLogger("gui.main")
geocoder = pygeocoder.Geocoder()
class mainFrame(wx.Frame):
""" Main class of the Frame. This is the Main Window."""
@@ -56,12 +63,16 @@ class mainFrame(wx.Frame):
# Application 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"))
self.Bind(wx.EVT_MENU, self.update_profile, updateProfile)
show_hide = app.Append(wx.NewId(), _(u"&Hide window"))
self.Bind(wx.EVT_MENU, self.show_hide, show_hide)
search = app.Append(wx.NewId(), _(u"&Search"))
self.Bind(wx.EVT_MENU, self.search, search)
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"))
self.Bind(wx.EVT_MENU, self.list_manager, lists)
sounds_tutorial = app.Append(wx.NewId(), _(u"Sounds &tutorial"))
@@ -87,6 +98,8 @@ class mainFrame(wx.Frame):
self.Bind(wx.EVT_MENU, self.unfav, unfav)
view = tweet.Append(wx.NewId(), _(u"&Show tweet"))
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"))
self.Bind(wx.EVT_MENU, self.delete, delete)
@@ -208,17 +221,17 @@ class mainFrame(wx.Frame):
log.debug("Getting Twitter's Rest API...")
self.twitter = twitter.twitter.twitter()
super(mainFrame, self).__init__(None, -1, "TW Blue", size=(1600, 1600))
self.Bind(wx.EVT_QUERY_END_SESSION, self.exit)
self.Bind(wx.EVT_END_SESSION, self.exit)
wx.GetApp().Bind(wx.EVT_QUERY_END_SESSION, self.exit)
wx.GetApp().Bind(wx.EVT_END_SESSION, self.exit)
log.debug(u"Creating the system tray icon... ")
sysTray=sysTrayIcon.SysTrayIcon(self)
self.sysTray=sysTrayIcon.SysTrayIcon(self)
panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetTitle("TW Blue")
try:
updater.update_manager.check_for_update()
except:
pass
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.setup_twitter(panel)
@@ -233,7 +246,7 @@ class mainFrame(wx.Frame):
# Gets the tabs for home, mentions, send and direct messages.
log.debug("Creating 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.db.settings["buffers"].append(self.db.settings["user_name"])
account_index = self.db.settings["buffers"].index(self.db.settings["user_name"])
@@ -310,11 +323,23 @@ class mainFrame(wx.Frame):
self.db.settings["buffers"].append(i+"favs")
self.fav_stream = RepeatingTimer(180, self.get_fav_buffers)
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)
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)
self.SetClientSize(self.sizer.CalcMin())
self.Bind(event.MyEVT_STARTED, self.onInit)
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)
def init(self, run_streams=False):
@@ -347,6 +372,7 @@ class mainFrame(wx.Frame):
self.check_streams.start()
# If all it's done, then play a nice sound saying that all it's OK.
sound.player.play("ready.ogg")
autocompletionUsersSettings.execute_at_startup(window=self)
def remove_list(self, id):
for i in range(0, self.nb.GetPageCount()):
@@ -369,21 +395,21 @@ class mainFrame(wx.Frame):
def setup_twitter(self, panel):
""" 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.logging_in_twblue(panel)
log.info("Authorized in Twitter.")
del self.user_key; del self.user_secret
except:
dlg1 = wx.MessageDialog(panel, _(u"Connection error. Try again later."), _(u"Error!"), wx.ICON_ERROR)
dlg1.ShowModal()
self.Close(True)
# except:
# dlg1 = wx.MessageDialog(panel, _(u"Connection error. Try again later."), _(u"Error!"), wx.ICON_ERROR)
# dlg1.ShowModal()
# self.Close(True)
def get_home(self):
""" Gets the home stream, that manages home timeline, mentions, direct messages and sent."""
try:
self.stream = twitter.buffers.stream.streamer(application.app_key, application.app_secret, config.main["twitter"]["user_key"], config.main["twitter"]["user_secret"], parent=self)
call_threaded(self.stream.user)
stream_threaded(self.stream.user)
except:
self.stream.disconnect()
@@ -406,13 +432,16 @@ class mainFrame(wx.Frame):
ids+= str(z)+", "
if ids != "":
# try:
call_threaded(self.stream2.statuses.filter, follow=ids)
stream_threaded(self.stream2.statuses.filter, follow=ids)
# except:
# pass
# except:
# self.stream2.disconnect()
def check_stream_up(self):
if not hasattr(self, "stream") and not hasattr(self, "stream2"):
self.init(run_streams=True)
return
try:
urllib2.urlopen("http://74.125.228.231", timeout=5)
except urllib2.URLError:
@@ -501,7 +530,7 @@ class mainFrame(wx.Frame):
dlg = dialogs.lists.removeUserListDialog(self)
if dlg.ShowModal() == wx.ID_OK:
try:
list = self.twitter.twitter.delete_list_member(list_id=self.db.settings["lists"][dlg.get_selected()]["id"], screen_name=user)
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"])
if list["mode"] == "private":
self.db.settings["lists"].pop(older_list)
@@ -546,8 +575,7 @@ class mainFrame(wx.Frame):
webbrowser.open("http://twblue.com.mx")
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):
updater.update_manager.check_for_update(msg=True)
@@ -574,7 +602,7 @@ class mainFrame(wx.Frame):
output.speak(self.nb.GetPageText(self.nb.GetSelection())+",", True)
def skip_blank_pages(self, forward=True):
if self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty" and (self.showing == False or platform.system() == "Darwin"):
if self.nb.GetCurrentPage().type == "account" or self.nb.GetCurrentPage().type == "empty":
self.nb.AdvanceSelection(forward)
def close(self, ev=None):
@@ -590,6 +618,8 @@ class mainFrame(wx.Frame):
def exit(self, event=None):
config.main.write()
log.debug("Exiting...")
self.sysTray.RemoveIcon()
self.sysTray.Destroy()
try:
self.check_streams.cancel()
except AttributeError:
@@ -661,8 +691,18 @@ class mainFrame(wx.Frame):
self.nb.GetCurrentPage().onRetweet(ev)
def view(self, ev=None):
tweet = self.nb.GetCurrentPage().get_message(dialog=True)
tp = self.nb.GetCurrentPage().type
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):
if self.nb.GetCurrentPage().name_buffer != "direct_messages" and self.nb.GetCurrentPage().name_buffer != "followers" and self.nb.GetCurrentPage().name_buffer != "friends":
@@ -778,6 +818,8 @@ class mainFrame(wx.Frame):
except:
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
output.speak(msg, 1)
if self.showing == True:
self.nb.GetCurrentPage().list.list.SetFocus()
def right(self, event=None):
num = self.nb.GetSelection()
@@ -791,21 +833,37 @@ class mainFrame(wx.Frame):
except:
msg = _(u"%s. Empty") % (self.nb.GetPageText(self.nb.GetSelection()))
output.speak(msg, 1)
if self.showing == True:
self.nb.GetCurrentPage().list.list.SetFocus()
def show_hide(self, ev=None):
# if platform.system() == "Linux" or platform.system() == "Darwin": return
def create_invisible_keyboard_shorcuts(self):
keymap = {}
for i in config.main["keymap"]:
if hasattr(self, i):
keymap[config.main["keymap"][i]] = getattr(self, i)
if self.showing == True:
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 config.main["general"]["use_invisible_keyboard_shorcuts"] == False:
self.register_invisible_keyboard_shorcuts(km)
self.Hide()
self.showing = False
else:
self.keyboard_handler.unregister_keys(keymap)
del self.keyboard_handler
if config.main["general"]["use_invisible_keyboard_shorcuts"] == False:
self.unregister_invisible_keyboard_shorcuts(km)
self.Show()
self.showing = True
@@ -959,6 +1017,72 @@ class mainFrame(wx.Frame):
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
def Destroy(self):
self.sysTray.Destroy()

23
src/gui/switchModule.py Normal file
View 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()

View File

@@ -55,7 +55,7 @@ class SysTrayIcon(wx.TaskBarIcon):
if (self.frame.showing):
self.frame.SetFocus()
else:
self.frame.onShow_hide()
self.frame.show_hide()
def Destroy(self):
self.menu.Destroy()

View File

View File

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

View File

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

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

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
############################################################
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import wx
import application
from suds.client import Client
import constants
class reportBug(wx.Dialog):
def __init__(self, user_name):
self.user = "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)

View File

@@ -42,4 +42,7 @@ actions = {
"edit_keystrokes": _(u"Shows the keystroke editor"),
"view_user_lists": _(u"Show lists for a specified user"),
"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"),
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -19,57 +19,68 @@ A twitter accessible, easy of use and cross platform application."""
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
import sys
from StringIO import StringIO
#redirect the original stdout and stderr
stdout=sys.stdout
stderr=sys.stderr
# Set a StringIO object as stdout and stderr to avoid problems using the installed version.
sys.stdout = StringIO()
sys.stderr = StringIO()
import wx
import os
ssmg = None
import gui
import paths
import config
import commandline
import platform
if platform.system() == "Windows":
from logger import logger as logging
if platform.system() == "Darwin":
import osx_prepare
osx_prepare.setup()
from logger import logger as logging
from sessionmanager import manager
from sessionmanager import gui as smGUI
manager.setup()
import sys
import config
import output
import sound
import languageHandler
#close the memory buffers for stdout and stderr
sys.stdout.close()
sys.stderr.close()
#if it's a binary version
if hasattr(sys, 'frozen'):
sys.stderr = open(paths.logs_path("stderr.log"), 'w')
sys.stdout = open(paths.logs_path("stdout.log"), 'w')
else:
sys.stdout=stdout
sys.stderr=stderr
class app(wx.App):
def __init__(self, *args, **kwargs):
super(app, self).__init__(*args, **kwargs)
if platform.system() != "Darwin":
self.start()
else:
self.mac()
def mac(self):
self.hold_frame = wx.Frame(title="None", parent=None)
self.hold_frame.Show()
wx.CallLater(10, self.start)
def start(self):
app = wx.App()
#app = wx.App(redirect=True, useBestVisual=True, filename=paths.logs_path('tracebacks.log'))
configured = False
configs = []
for i in os.listdir(paths.config_path()):
if os.path.isdir(paths.config_path(i)): configs.append(i)
if len(configs) == 1:
manager.manager.set_current_session(configs[0])
config.MAINFILE = "%s/session.conf" % (manager.manager.get_current_session())
config.setup()
lang=config.main['general']['language']
languageHandler.setLanguage(lang)
sound.setup()
output.setup()
configured = True
else:
ssmg = smGUI.sessionManagerWindow()
if ssmg.ShowModal() == wx.ID_OK:
if configured == True or ssmg.ShowModal() == wx.ID_OK:
frame = gui.main.mainFrame()
frame.Show()
frame.showing = True
if config.main != None and config.main["general"]["hide_gui"] == True and platform.system() == "Windows":
frame.show_hide()
frame.Hide()
self.SetTopWindow(frame)
if hasattr(self, "frame"): self.hold_frame.Hide()
# If the user press on cancel.
else:
self.Exit()
ap = app()
app.SetTopWindow(frame)
else:
app.Exit()
### I should uncomment this
#if platform.system() != "Windows":
# local = wx.Locale(wx.LANGUAGE_DEFAULT)
@@ -77,5 +88,5 @@ ap = app()
# local.AddCatalog("twblue")
#ap = app(redirect=True, useBestVisual=True, filename=paths.logs_path('tracebacks.log'))
#wx.CallLater(10, start)
ap.MainLoop()
app.MainLoop()

View File

@@ -3,6 +3,7 @@ import threading
import wx
from twython import TwythonRateLimitError
import time
from wx.lib.pubsub import pub
def call_threaded(func, *args, **kwargs):
#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.start()
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

View File

@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import shutil
import time
import wx
import manager
@@ -23,28 +24,26 @@ class sessionManagerWindow(wx.Dialog):
label = wx.StaticText(panel, -1, _(u"Select a twitter account to start TW Blue"), size=wx.DefaultSize)
listSizer = wx.BoxSizer(wx.HORIZONTAL)
self.list = widgets.list(panel, _(u"Account"), style=wx.LC_SINGLE_SEL|wx.LC_REPORT)
self.fill_list()
listSizer.Add(label, 0, wx.ALL, 5)
listSizer.Add(self.list.list, 0, wx.ALL, 5)
sizer.Add(listSizer, 0, wx.ALL, 5)
new = wx.Button(panel, -1, _(u"New account"), size=wx.DefaultSize)
new.Bind(wx.EVT_BUTTON, self.new_account)
self.removeSession = wx.Button(panel, -1, _(u"Remove session"))
self.removeSession.Disable()
self.removeSession.Bind(wx.EVT_BUTTON, self.remove)
ok = wx.Button(panel, wx.ID_OK, size=wx.DefaultSize)
ok.SetDefault()
ok.Bind(wx.EVT_BUTTON, self.ok)
cancel = wx.Button(panel, wx.ID_CANCEL, size=wx.DefaultSize)
buttons = wx.BoxSizer(wx.HORIZONTAL)
buttons.Add(new, 0, wx.ALL, 5)
buttons.Add(self.removeSession, 0, wx.ALL, 5)
buttons.Add(ok, 0, wx.ALL, 5)
buttons.Add(cancel, 0, wx.ALL, 5)
sizer.Add(buttons, 0, wx.ALL, 5)
panel.SetSizer(sizer)
# sizer.Layout()
# self.Fit()
# self.SetSize(panel.GetBestSize())
# panelSizer.Add(panel)
# self.SetSizerAndFit(sizer)
# sizer.Layout()
self.fill_list()
min = sizer.CalcMin()
self.SetClientSize(min)
@@ -55,12 +54,16 @@ class sessionManagerWindow(wx.Dialog):
strconfig = "%s/session.conf" % (paths.config_path(i))
config_test = Configuration(strconfig)
name = config_test["twitter"]["user_name"]
if name != "" and config_test["twitter"]["user_key"] != "" and config_test["twitter"]["user_secret"] != "":
if name != "" or (config_test["twitter"]["user_key"] != "" and config_test["twitter"]["user_secret"] != ""):
self.list.insert_item(False, name)
self.sessions.append(i)
else:
del config_test
shutil.rmtree(path=paths.config_path(i), ignore_errors=True)
if self.list.get_count() > 0:
self.list.select_item(0)
self.list.list.SetSize(self.list.list.GetBestSize())
self.removeSession.Enable()
def ok(self, ev):
if self.list.get_count() == 0:
@@ -74,8 +77,6 @@ class sessionManagerWindow(wx.Dialog):
languageHandler.setLanguage(lang)
sound.setup()
output.setup()
# else:
# self.name = current_session
self.EndModal(wx.ID_OK)
def new_account(self, ev):
@@ -99,3 +100,12 @@ class sessionManagerWindow(wx.Dialog):
if self.list.get_count() == 1:
self.list.select_item(0)
self.sessions.append(location)
def remove(self, ev):
selected_item = self.list.get_selected()
selected_session = self.sessions[selected_item]
ask = wx.MessageDialog(self, _(u"Do you really want delete this account?"), _(u"Remove account"), wx.YES_NO)
if ask.ShowModal() == wx.ID_YES:
self.sessions.remove(selected_session)
shutil.rmtree(path=paths.config_path(selected_session), ignore_errors=True)
self.list.remove_item(selected_item)

View File

@@ -34,7 +34,7 @@ def get_architecture_files():
("Microsoft.VC90.MFC", glob("../windows-dependencies/x86/Microsoft.VC90.MFC/*")),]
elif platform.architecture()[0][:2] == "64":
return [
("", ["../windows-dependencies/x64/oggenc2.exe", "../windows-dependencies/x86/bootstrap.exe"]),
("", ["../windows-dependencies/x64/oggenc2.exe", "../windows-dependencies/x64/bootstrap.exe"]),
("Microsoft.VC90.CRT", glob("../windows-dependencies/x64/Microsoft.VC90.CRT/*")),
("Microsoft.VC90.MFC", glob("../windows-dependencies/x64/Microsoft.VC90.MFC/*")),]
@@ -90,7 +90,8 @@ data_files = get_data(),
options = {
'py2exe': {
'optimize':2,
'dll_excludes': ["MPR.dll", "api-ms-win-core-apiquery-l1-1-0.dll", "api-ms-win-core-console-l1-1-0.dll", "api-ms-win-core-delayload-l1-1-1.dll", "api-ms-win-core-errorhandling-l1-1-1.dll", "api-ms-win-core-file-l1-2-0.dll", "api-ms-win-core-handle-l1-1-0.dll", "api-ms-win-core-heap-obsolete-l1-1-0.dll", "api-ms-win-core-libraryloader-l1-1-1.dll", "api-ms-win-core-localization-l1-2-0.dll", "api-ms-win-core-processenvironment-l1-2-0.dll", "api-ms-win-core-processthreads-l1-1-1.dll", "api-ms-win-core-profile-l1-1-0.dll", "api-ms-win-core-registry-l1-1-0.dll", "api-ms-win-core-synch-l1-2-0.dll", "api-ms-win-core-sysinfo-l1-2-0.dll", "api-ms-win-security-base-l1-2-0.dll", "api-ms-win-core-heap-l1-2-0.dll", "api-ms-win-core-interlocked-l1-2-0.dll", "api-ms-win-core-localization-obsolete-l1-1-0.dll", "api-ms-win-core-string-l1-1-0.dll", "api-ms-win-core-string-obsolete-l1-1-0.dll", "WLDAP32.dll", "MSVCP90.dll"],
'packages': ["wx.lib.pubsub", "wx.lib.pubsub.core", "wx.lib.pubsub.core.kwargs"],
'dll_excludes': ["MPR.dll", "api-ms-win-core-apiquery-l1-1-0.dll", "api-ms-win-core-console-l1-1-0.dll", "api-ms-win-core-delayload-l1-1-1.dll", "api-ms-win-core-errorhandling-l1-1-1.dll", "api-ms-win-core-file-l1-2-0.dll", "api-ms-win-core-handle-l1-1-0.dll", "api-ms-win-core-heap-obsolete-l1-1-0.dll", "api-ms-win-core-libraryloader-l1-1-1.dll", "api-ms-win-core-localization-l1-2-0.dll", "api-ms-win-core-processenvironment-l1-2-0.dll", "api-ms-win-core-processthreads-l1-1-1.dll", "api-ms-win-core-profile-l1-1-0.dll", "api-ms-win-core-registry-l1-1-0.dll", "api-ms-win-core-synch-l1-2-0.dll", "api-ms-win-core-sysinfo-l1-2-0.dll", "api-ms-win-security-base-l1-2-0.dll", "api-ms-win-core-heap-l1-2-0.dll", "api-ms-win-core-interlocked-l1-2-0.dll", "api-ms-win-core-localization-obsolete-l1-1-0.dll", "api-ms-win-core-string-l1-1-0.dll", "api-ms-win-core-string-obsolete-l1-1-0.dll", "WLDAP32.dll", "MSVCP90.dll", "MFC90.dll"],
'skip_archive': True
},
},

BIN
src/sounds/default/geo.ogg Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from twitter import compose
from twitter import compose, utils
from twython import TwythonStreamer
import sound
from mysc import event
@@ -34,13 +34,20 @@ class streamer(TwythonStreamer):
tweet_event.SetItem(data)
announce = _(u"One tweet from %s in the list %s") % (data["user"]["name"], self.parent.nb.GetPage(i).name_buffer[:-5])
tweet_event.SetAnnounce(announce)
usr = data["in_reply_to_user_id"]
if (usr != None and usr in self.friends) or data.has_key("retweeted_status"):
wx.PostEvent(self.parent.nb.GetPage(i), tweet_event)
elif usr == None:
wx.PostEvent(self.parent.nb.GetPage(i), tweet_event)
except ValueError:
pass
def set_friends(self, friends):
self.friends = friends
def on_success(self, data):
try:
if data.has_key("text"):
if data.has_key("text") and utils.is_allowed(data):
self.check_tls(data)
except:
pass

View File

@@ -8,6 +8,7 @@ import config
import logging as original_logger
log = original_logger.getLogger("MainStream")
import output
from wx.lib.pubsub import pub
class streamer(TwythonStreamer):
def __init__(self, app_key, app_secret, oauth_token, oauth_token_secret, timeout=300, retry_count=None, retry_in=10, client_args=None, handlers=None, chunk_size=1, parent=None):
@@ -51,7 +52,7 @@ class streamer(TwythonStreamer):
wx.PostEvent(self.parent.search_buffer("buffer", "favs"), tweet_event)
def check_mentions(self, data):
if "@%s" % (self.db.settings["user_name"]) in data["text"]:
if "@%s" % (self.db.settings["user_name"].lower()) in data["text"].lower():
tweet_event = event.event(event.EVT_OBJECT, 1)
tweet_event.SetItem(data)
text = _(u"One mention from %s ") % (data["user"]["name"])
@@ -68,15 +69,14 @@ class streamer(TwythonStreamer):
tweet_event.SetItem(data["direct_message"])
text = _(u"One direct message")
tweet_event.SetAnnounce(text)
wx.PostEvent(self.parent.search_buffer("buffer", "direct_messages"), tweet_event)
wx.PostEvent(self.parent.search_buffer("direct_message", "direct_messages"), tweet_event)
def check_follower(self, data):
if data["target"]["screen_name"] == self.db.settings["user_name"]:
if config.main["other_buffers"]["show_followers"] == True:
if data["target"]["screen_name"] == self.db.settings["user_name"] and config.main["other_buffers"]["show_followers"] == True:
tweet_event = event.event(event.EVT_OBJECT, 1)
tweet_event.SetItem(data["source"])
wx.PostEvent(self.parent.search_buffer("people", "followers"), tweet_event)
elif data["source"]["screen_name"] == self.db.settings["user_name"] and config.main["other_buffers"]["show_friends"] == True:
elif data["source"]["screen_name"] == self.db.settings["user_name"]:
tweet_event = event.event(event.EVT_OBJECT, 1)
tweet_event.SetItem(data["target"])
wx.PostEvent(self.parent.search_buffer("people", "friends"), tweet_event)
@@ -106,6 +106,7 @@ class streamer(TwythonStreamer):
self.process_dm(data)
elif "friends" in data:
self.friends = data["friends"]
pub.sendMessage("friendsReceived")
elif "text" in data and utils.is_allowed(data) == True:
if data["user"]["id"] in self.muted_users: return
self.check_mentions(data)
@@ -119,7 +120,7 @@ class streamer(TwythonStreamer):
self.check_favs(data)
elif "unfavorite" == data["event"] and config.main["other_buffers"]["show_favourites"] == True:
self.remove_fav(data)
elif "follow" == data["event"] and config.main["other_buffers"]["show_followers"] == True:
elif "follow" == data["event"]:
self.check_follower(data)
elif "unfollow" == data["event"] and config.main["other_buffers"]["show_followers"] == True:
self.remove_friend(data)

View File

@@ -13,6 +13,8 @@ else:
languageHandler.setLanguage("system")
import platform
system = platform.system()
import logging as original_logger
log = original_logger.getLogger("events")
def prettydate(d):
""" Converts a string to the relative time."""
@@ -153,7 +155,7 @@ def compose_tweet(tweet, db):
else: user = tweet["sender"]["name"]
elif tweet.has_key("user"):
user = tweet["user"]["name"]
source = re.sub(r"(?s)<.*?>", " ", tweet["source"])
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
try: text = "rt @%s: %s" % (tweet["retweeted_status"]["user"]["screen_name"], StripChars(tweet["retweeted_status"]["text"]))
except KeyError: text = "%s" % (StripChars(tweet["text"]))
if text[-1] in chars: text=text+"."
@@ -224,7 +226,9 @@ def compose_event(data, username):
elif data["event"] == "list_user_unsubscribed":
if data["source"]["screen_name"] == username: event = _(u"You've unsubscribed from the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["target"]["name"], data["target"]["screen_name"])
else: event = _("You've been unsubscribed from the list %s, which is owned by %s(@%s)") % (data["target_object"]["name"], data["source"]["name"], data["source"]["screen_name"])
else: event = _("Unknown")
else:
log.error("event: %s\n target: %s\n source: %s\n" % (data["event"], data["target"], data["source"]))
event = _("Unknown")
# output.speak(event)
return [time.strftime("%I:%M %p"), event]

View File

@@ -75,12 +75,13 @@ def start_stream(db, twitter, name, function, param=None):
last_id = 0
if len(db.settings[name]) > 0:
for i in tl:
if int(i["id"]) > int(last_id):
if int(i["id"]) > int(last_id) and utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
elif len(db.settings[name]) == 0:
for i in tl:
if utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
@@ -121,7 +122,7 @@ def update_stream(config, twitter, name, function, param=None, sndFile=""):
tl = function(sinze_id=config.settings[name][-1]["id"], screen_name=param, count=config.main["general"]["max_tweets_per_call"])
tl.reverse()
for i in tl:
if i["id"] > config.settings[name][-1]["id"]:
if i["id"] > config.settings[name][-1]["id"] and utils.is_allowed(i) == True:
config.settings[name].append(i)
sounded = True
num = num+1
@@ -171,7 +172,7 @@ def start_sent(db, twitter, name, function, param=None):
num = num+1
return num
def start_list(db, twitter, name, list_id):
def start_list(db, twitter, name, list_id, *args, **kwargs):
num = 0
if db.settings.has_key(name):
try:
@@ -181,20 +182,21 @@ def start_list(db, twitter, name, list_id):
last_id = db.settings[name][-1]["id"]
except IndexError:
pass
tl = twitter.twitter.get_list_statuses(list_id=list_id, count=200)
tl = twitter.twitter.get_list_statuses(list_id=list_id, *args, **kwargs)
else:
tl = twitter.twitter.get_list_statuses(list_id=list_id, count=200)
tl = twitter.twitter.get_list_statuses(list_id=list_id, *args, **kwargs)
tl.reverse()
db.settings[name] = []
last_id = 0
if len(db.settings[name]) > 0:
for i in tl:
if int(i["id"]) > int(last_id):
if int(i["id"]) > int(last_id) and utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
elif len(db.settings[name]) == 0:
for i in tl:
if utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
@@ -209,12 +211,13 @@ def search(db, twitter, name, *args, **kwargs):
tl["statuses"].reverse()
if len(db.settings[name]) > 0:
for i in tl["statuses"]:
if utils.find_item(i["id"], db.settings[name]) == None:
if utils.find_item(i["id"], db.settings[name]) == None and utils.is_allowed(i):
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
elif len(db.settings[name]) == 0:
for i in tl["statuses"]:
if utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
@@ -247,12 +250,13 @@ def get_favourites_timeline(db, twitter, name, param, *args, **kwargs):
tl.reverse()
if len(db.settings[name]) > 0:
for i in tl:
if utils.find_item(i["id"], db.settings[name]) == None:
if utils.find_item(i["id"], db.settings[name]) == None and utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1
elif len(db.settings[name]) == 0:
for i in tl:
if utils.is_allowed(i) == True:
if config.main["general"]["reverse_timelines"] == False: db.settings[name].append(i)
else: db.settings[name].insert(0, i)
num = num+1

View File

@@ -25,6 +25,7 @@ class handler(BaseHTTPServer.BaseHTTPRequestHandler):
verifier = params.get('oauth_verifier', [None])[0]
self.wfile.write("You have successfully logged in to Twitter with TW Blue. "
"You can close this window now.")
self.wfile.close()
class twitter(object):
@@ -39,7 +40,7 @@ class twitter(object):
def authorise(self):
httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 8080), handler)
twitter = Twython(application.app_key, application.app_secret)
twitter = Twython(application.app_key, application.app_secret, auth_endpoint='authorize')
auth = twitter.get_authentication_tokens("http://127.0.0.1:8080")
webbrowser.open_new_tab(auth['auth_url'])
global logged, verifier
@@ -48,6 +49,8 @@ class twitter(object):
self.twitter = Twython(application.app_key, application.app_secret, auth['oauth_token'], auth['oauth_token_secret'])
final = self.twitter.get_authorized_tokens(verifier)
self.save_configuration(final["oauth_token"], final["oauth_token_secret"])
httpd.server_close()
def save_configuration(self, user_key=None, user_secret=None):
if user_key != None and user_secret != None:

View File

@@ -58,9 +58,12 @@ def is_audio(tweet):
return True
return False
def is_geocoded(tweet):
if tweet.has_key("coordinates") and tweet["coordinates"] != None:
return True
def get_all_mentioned(tweet, config):
""" Gets all users that has been mentioned."""
if tweet.has_key("retweeted_status"): tweet = tweet["retweeted_status"]
string = []
for i in tweet["entities"]["user_mentions"]:
if i["screen_name"] != config.settings["user_name"] and i["screen_name"] != tweet["user"]["screen_name"]:
@@ -106,9 +109,12 @@ def api_call(parent=None, call_name=None, preexec_message="", success="", succes
return val
def is_allowed(tweet):
try:
allowed = True
if tweet.has_key("retweeted_status"): tweet = tweet["retweeted_status"]
source = re.sub(r"(?s)<.*?>", "", tweet["source"])
for i in config.main["twitter"]["ignored_clients"]:
if i.lower() == source.lower(): allowed = False
return allowed
except KeyError:
return True

View File

@@ -26,7 +26,8 @@ def check_for_update(msg=False):
else:
progress.Update(percent, _(u"Update"))
def update_complete():
wx.MessageDialog(None, _(u"The new TW Blue version has been downloaded and installed. Press OK to start the application."), _(u"Done!")).ShowModal()
ms = wx.MessageDialog(None, _(u"The new TW Blue version has been downloaded and installed. Press OK to start the application."), _(u"Done!"))
if ms.ShowModal() == wx.ID_OK:
sys.exit()
app_updater = updater.AutoUpdater(url, new_path, 'bootstrap.exe', app_path=paths.app_path(), postexecute=paths.app_path("TWBlue.exe"), finish_callback=update_complete, percentage_callback=update)
app_updater.start_update()

View File

@@ -0,0 +1,3 @@
import platform
if platform.system() == "Windows":
from wxUtils import *

View File

@@ -0,0 +1,32 @@
import wx
# Code responses for WX dialogs.
# this is when an user presses OK on a dialogue.
OK = wx.ID_OK
# This is when an user presses cancel on a dialogue.
CANCEL = wx.ID_CANCEL
# This is when an user closes the dialogue or an id to create the close button.
CLOSE = wx.ID_CLOSE
# The response for a "yes" Button pressed on a dialogue.
YES = wx.ID_YES
# This is when the user presses No on a default dialogue.
NO = wx.ID_NO
#events
# This is raised when the application must be closed.
CLOSE_EVENT = wx.EVT_CLOSE
# This is activated when a button is pressed.
BUTTON_PRESSED = wx.EVT_BUTTON
# This is activated when an user enter text on an edit box.
ENTERED_TEXT = wx.EVT_TEXT
def exit_application():
""" Closes the current window cleanly. """
wx.GetApp().ExitMainLoop()
def connect_event(parent, event, func):
""" Connects an event to a function.
parent wx.window: The widget that will listen for the event.
event widgetUtils.event: The event that will be listened for the parent. The event should be one of the widgetUtils events.
function func: The function that will be connected to the event."""
return getattr(parent, "Bind")(event, func)

57
tools/build_twblue.sh Normal file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
# Define paths for a regular use, if there are not paths for python32 or 64, these commands will be taken.
pythonpath32="/C/python27x86"
pythonpath64="/C/python27"
nsyspath=$PROGRAMFILES/NSIS
help () {
echo -e "$0 | usage:"
echo -e "$0 | \t./generate_installer.sh [-py32path <path to python for 32 bits> | -py64path <path for python on 64 bits> | -nsyspath <path to nsys> | -h]"
}
# parsing options from the command line
while [[ $# > 1 ]]
do
key="$1"
shift
case $key in
-py32path)
pythonpath32="$1"
shift
;;
-py64path)
pythonpath64="$1"
shift
;;
-nsispath)
nsispath="$1"
shift
;;
-help)
help
;;
*)
help
esac
done
cd ../src
if [ -d build/ ];
then
rm -rf build
fi
if [ -d dist/ ];
then
rm -rf dist
fi
$pythonpath32/python.exe "setup.py" "py2exe" "--quiet"
mv -f dist ../scripts/TWBlue
rm -rf build
$pythonpath64/python.exe "setup.py" "py2exe" "--quiet"
mv -f dist ../scripts/TWBlue64
rm -rf build
cd ../scripts
$nsispath/Unicode/makensis.exe "twblue.nsi"
rm -rf TWBlue
rm -rf TWBlue64

View File

@@ -1,13 +0,0 @@
#!/bin/bash
mkdir ../src/documentation
for i in `ls ../documentation`
do
if test -d ../documentation/$i
then
mkdir ../src/documentation/$i
pandoc -s ../documentation/$i/changes.md -o ../src/documentation/$i/changes.html
pandoc -s ../documentation/$i/manual.md -o ../src/documentation/$i/manual.html
cp ../documentation/license.txt ../src/documentation/license.txt
fi
done
exit

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1
windows-dependencies Submodule

Submodule windows-dependencies added at 739034e207