mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-25 17:39:23 +00:00
Compare commits
178 Commits
v2024.01.0
...
v2024.05.2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d5ad0fede5 | ||
52ee056759 | |||
fe43ce562c | |||
503bf72b11 | |||
14a956d207 | |||
68651ff736 | |||
49c32ad4b8 | |||
bd69745cef | |||
![]() |
404e545a6d | ||
23468c7c63 | |||
fb08352a91 | |||
00b33550f4 | |||
![]() |
1b61545375 | ||
![]() |
31beabe86e | ||
![]() |
bb531a41d0 | ||
6db1e0b79c | |||
94ad7ce180 | |||
![]() |
7112d0e159 | ||
5620635a36 | |||
24767437f5 | |||
0858b17f17 | |||
e91391a3fa | |||
3320aa5509 | |||
4bb21e15f8 | |||
41d585226a | |||
9c2511561a | |||
cd7279e83b | |||
533f15de55 | |||
10d2c47f9a | |||
b39ccb9f2c | |||
2a1d86f917 | |||
aee2a3b8b2 | |||
fd1a64c7b8 | |||
a5cd118b99 | |||
ee4f254825 | |||
![]() |
a1eb546f23 | ||
74360ac50f | |||
c05dc4b211 | |||
5ad29130b9 | |||
a322507f8b | |||
![]() |
ea60f308c6 | ||
![]() |
c784ca0cd5 | ||
![]() |
04aa4b72f8 | ||
![]() |
d4a6b95ea4 | ||
![]() |
ce9c2b4456 | ||
![]() |
7752fa6f58 | ||
![]() |
cfbdab9b9b | ||
![]() |
8c43525d6a | ||
![]() |
6321ae68b5 | ||
![]() |
1552c412bb | ||
![]() |
1b46c21aa0 | ||
![]() |
47f02ce0df | ||
![]() |
72f10f3654 | ||
![]() |
d2dbdb9a71 | ||
![]() |
ad2a800dad | ||
![]() |
174a5fad61 | ||
![]() |
fa17af13af | ||
![]() |
d2322afeba | ||
![]() |
1c7ffb1f97 | ||
![]() |
f1f83e875b | ||
![]() |
ed3b25ec85 | ||
![]() |
3ccd9018ed | ||
![]() |
5da35a3039 | ||
![]() |
11d9414670 | ||
![]() |
6169bcfbc7 | ||
![]() |
82791fa648 | ||
![]() |
ada246863c | ||
![]() |
1ac04422f9 | ||
![]() |
184b000f78 | ||
![]() |
1aa2ded0bb | ||
![]() |
eba2d34223 | ||
![]() |
75e6027381 | ||
![]() |
698b1d8ae8 | ||
![]() |
86dc45bc2b | ||
![]() |
74f403b750 | ||
![]() |
9422fd6fad | ||
![]() |
0546679452 | ||
![]() |
4adf8e4a47 | ||
![]() |
69c6fb1ee6 | ||
![]() |
e59d3da0fb | ||
![]() |
a3163f4a08 | ||
![]() |
b8bcff3157 | ||
![]() |
72df3fa115 | ||
![]() |
afb9d0414e | ||
![]() |
327bc99924 | ||
![]() |
b8b93a900a | ||
![]() |
185d4c295b | ||
![]() |
4c3ef2dda5 | ||
![]() |
34ca6ca7d1 | ||
![]() |
37728ce9ba | ||
![]() |
479b4f8a76 | ||
![]() |
0a853d9dfd | ||
![]() |
9f1d3b1fb2 | ||
![]() |
ba0fe23080 | ||
![]() |
f06208106d | ||
![]() |
5e074d40cf | ||
![]() |
1eddebb775 | ||
![]() |
9906047f16 | ||
![]() |
6bccfe08b7 | ||
![]() |
ff25f7cbd2 | ||
![]() |
98aeafa0cc | ||
![]() |
32fbf0417c | ||
![]() |
3ed52ae1a8 | ||
![]() |
9b8f989c35 | ||
![]() |
0b6d93b376 | ||
![]() |
b2282e47eb | ||
![]() |
0e5fd4d2a1 | ||
![]() |
d10fa02d75 | ||
![]() |
ce9f3c0be4 | ||
![]() |
c404beda68 | ||
![]() |
e503766eee | ||
![]() |
29188ead9c | ||
![]() |
39525f8cb3 | ||
![]() |
d7ca5c3820 | ||
![]() |
cb1d06f05d | ||
![]() |
177cf1a896 | ||
![]() |
d93620a74f | ||
![]() |
a571cef885 | ||
![]() |
37f2870950 | ||
![]() |
7a31f8e474 | ||
![]() |
f5a9578331 | ||
![]() |
c9e41a534e | ||
![]() |
90df276479 | ||
![]() |
b6a3324dc9 | ||
![]() |
ed83d6b839 | ||
d6d3f0810c | |||
9f80891e1d | |||
![]() |
04853165b3 | ||
![]() |
8777fde660 | ||
![]() |
8a6d505205 | ||
![]() |
e53848fd33 | ||
![]() |
a533dccb38 | ||
![]() |
7fb5ff3927 | ||
![]() |
a8301c0df3 | ||
![]() |
227b1934a8 | ||
![]() |
4161392e12 | ||
![]() |
9294310d65 | ||
![]() |
1a1248e445 | ||
![]() |
ad0a100838 | ||
![]() |
30b87d447c | ||
![]() |
085f63bcb5 | ||
![]() |
5ac71413ce | ||
![]() |
9765a1d9f4 | ||
![]() |
bcf05d344a | ||
![]() |
cfdc3abf08 | ||
![]() |
7c93366b37 | ||
![]() |
614f2b3aaa | ||
![]() |
65d7ff8f8b | ||
![]() |
70a2635dc2 | ||
![]() |
eef6637f85 | ||
![]() |
cd5fb8747b | ||
![]() |
cedbb77eb5 | ||
![]() |
4f6f98f56f | ||
![]() |
18a89df400 | ||
![]() |
935c7d7fa2 | ||
![]() |
dab7b42bee | ||
![]() |
58749b83af | ||
![]() |
7249fbfc01 | ||
![]() |
e926d4f817 | ||
![]() |
870d6ed147 | ||
![]() |
6e10f8e31c | ||
434328056e | |||
36420d6a5b | |||
![]() |
ccd3edd2da | ||
![]() |
c3744fb680 | ||
![]() |
4d89e8c437 | ||
![]() |
d3cc46b723 | ||
![]() |
dc7d9b6923 | ||
![]() |
22c5147c92 | ||
![]() |
dda3d21008 | ||
![]() |
871562fa4c | ||
88ddace811 | |||
8d1b0c73df | |||
3e3396687d | |||
c9c78105a5 | |||
![]() |
49898c4407 | ||
![]() |
46c897221f | ||
![]() |
7a83e399fc |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -46,4 +46,4 @@ jobs:
|
||||
mkdir .release-assets
|
||||
mv scripts\TWBlue_setup.exe .release-assets\TWBlue_setup_${{github.ref_name}}.exe
|
||||
mv scripts\TWBlue64\TWBlue_portable.zip .release-assets\TWBlue_portable_${{github.ref_name}}.zip
|
||||
gh release create release --draft -F "release-notes.md" -t "${{github.ref_name}}" .release-assets\TWBlue_setup_${{github.ref_name}}.exe .release-assets\TWBlue_portable_${{github.ref_name}}.zip
|
||||
gh release create "${{github.ref_name}}" -F "release-notes.md" -t "${{github.ref_name}}" .release-assets\TWBlue_setup_${{github.ref_name}}.exe .release-assets\TWBlue_portable_${{github.ref_name}}.zip
|
@@ -75,7 +75,7 @@ Now that you have installed all these packages, you can run TW Blue from source
|
||||
|
||||
To generate the documentation in html format, navigate to the doc folder inside this repo. After that, run these commands:
|
||||
|
||||
`python document_importer.py`
|
||||
`python documentation_importer.py`
|
||||
`python generator.py`
|
||||
|
||||
The documentation will be generated, placing each language in a separate folder in the doc directory. Move these folders (for example `de`, `en`, `es`, `fr`, `it`, ...) to `src/documentation`, creating the directory if necessary.
|
||||
@@ -108,4 +108,4 @@ To manage translations in TWBlue, you can install the [Babel package.](https://p
|
||||
pybabel extract -o twblue.pot --msgid-bugs-address "manuel@manuelcortez.net" --copyright-holder "MCV software" --input-dirs ..\src
|
||||
```
|
||||
|
||||
Take into account, though, that we use [weblate](https://weblate.mcvsoftware.com) to track translation work for TWBlue. If you wish to be part of our translation team, please open an issue so we can create an account for you in Weblate.
|
||||
Take into account, though, that we use [weblate](https://weblate.mcvsoftware.com) to track translation work for TWBlue. If you wish to be part of our translation team, please open an issue so we can create an account for you in Weblate.
|
||||
|
@@ -2,6 +2,32 @@ TWBlue Changelog
|
||||
|
||||
## changes in this version
|
||||
|
||||
* Core:
|
||||
* The way sessions are named has been changed. Now the account is indicated first, followed by the social network it belongs to.
|
||||
* An option has been added to the global options dialog that allows for the reading of long posts in the graphical interface. This is especially useful since, by default, the graphical interface can only display a limited number of characters in the post.
|
||||
* Some options that are no longer necessary in the application have been removed from the global settings dialog.
|
||||
* Mastodon:
|
||||
* fixed an error that caused TWBlue to not display some posts correctly.
|
||||
* Fixed name for community timelines when created during startup. Now it should be clear if it's a federated or local timeline.
|
||||
* Defined shortcuts to fields on the update profile dialog so it will be easier to navigate.
|
||||
* Now it is possible to load conversations directly from community timelines.
|
||||
|
||||
## Changes in version 2024.5.19
|
||||
|
||||
In this version of TWBlue, which is being released several months after the previous one, we focused on adding initial support for GoToSocial-type networks. GoToSocial is a server for creating decentralized networks similar to Mastodon. Its API is very similar but retains some differences. In this version, TWBlue can be used to log into GoToSocial accounts, although there will be some features, such as the Streaming API and Markdown support, that are not yet functional. Another significant addition is support for creating community timelines, which will allow you to load the local and public timeline of remote instances. This is useful if your instance does not federate directly with them, as it will allow you to see posts from other communities and interact directly with them. Finally, the translation module has been rewritten; it now supports using LibreTranslate by default and DeepL, for which an API key is required. Below is the detailed list of changes:
|
||||
|
||||
* Core:
|
||||
* Added Initial Support to GoToSocial. Some features are not fully implemented yet, although GoToXocial instances should be able to be used as normal sessions in TWBlue. Streaming, poll options and markdown are not supported but planned for the near future.
|
||||
* The translation module has been rewritten. Now, instead of offering translations with Google Translator, the user can choose between [LibreTranslate,](https://github.com/LibreTranslate/LibreTranslate) which requires no configuration thanks to the [instance of the NVDA Spanish community;](https://translate.nvda.es) or translate using [DeepL,](https://deepl.com) for which it is necessary to create an account on DeepL and [subscribe to a DeepL API Free plan](https://support.deepl.com/hc/en-us/articles/360021200939-DeepL-API-Free) to obtain the API key which can be used to translate up to 500000 characters every month. The API key can be entered in the global options dialog, under a new tab called translation services. When translating a text, the translation engine can be changed. When changing the translation engine, the target language must be selected again before translation takes place.
|
||||
* TWBlue should be able to switch to Windows 11 Keymap when running under Windows 11. ([#494](https://github.com/mcv-software/twblue/issues/494))
|
||||
* Mastodon:
|
||||
* Added support for viewing communities: A community timeline is the local or public timeline of another instance. This is especially useful when the instance one is part of does not federate with other remote instances. The posts displayed are only those that are shared publicly. It is possible to interact with the posts from community timelines, but it should be noted that TWBlue will take some time to retrieve the post one wishes to interact with.
|
||||
* When viewing a post, a button displays the number of boosts and times it has been added to favorites. Clicking on that button will open a list of users who have interacted with the post. From that list, it is possible to view profiles and perform common user actions.
|
||||
* Now it is possible to mute conversations in Mastodon sessions. To do this, there is a button that can be called "Mute" or "Unmute Conversation" in the dialog to display the post. Conversations that have been muted will not generate notifications or mentions when they receive new replies. Only conversations that you are a part of can be muted.
|
||||
* Fixed an error that caused TWBlue to be unable to properly display the user action dialog from the followers or following buffer. ([#575](https://github.com/mcv-software/twblue/issues/575))
|
||||
|
||||
## changes in version 2024.01.05
|
||||
|
||||
* Core:
|
||||
* The TWBlue website will no longer be available on the twblue.es domain. Beginning in January 2024, TWBlue will live at https://twblue.mcvsoftware.com. Also, we will start releasing versions on [gitHub releases](https://github.com/mcv-software/twblue/releases) so it will be easier to track specific versions.
|
||||
* As of the first release of TWBlue in 2024, we will officially stop generating 32-bit (X86) compatible binaries due to the increasing difficulty of generating versions compatible with this architecture in modern Python.
|
||||
|
@@ -1,17 +1,13 @@
|
||||
## Changelog
|
||||
|
||||
In this version of TWBlue, which is being released several months after the previous one, we focused on adding initial support for GoToSocial-type networks. GoToSocial is a server for creating decentralized networks similar to Mastodon. Its API is very similar but retains some differences. In this version, TWBlue can be used to log into GoToSocial accounts, although there will be some features, such as the Streaming API and Markdown support, that are not yet functional. Another significant addition is support for creating community timelines, which will allow you to load the local and public timeline of remote instances. This is useful if your instance does not federate directly with them, as it will allow you to see posts from other communities and interact directly with them. Finally, the translation module has been rewritten; it now supports using LibreTranslate by default and DeepL, for which an API key is required. Below is the detailed list of changes:
|
||||
|
||||
* Core:
|
||||
* The TWBlue website will no longer be available on the twblue.es domain. Beginning in January 2024, TWBlue will live at https://twblue.mcvsoftware.com. Also, we will start releasing versions on [gitHub releases](https://github.com/mcv-software/twblue/releases) so it will be easier to track specific versions.
|
||||
* As of the first release of TWBlue in 2024, we will officially stop generating 32-bit (X86) compatible binaries due to the increasing difficulty of generating versions compatible with this architecture in modern Python.
|
||||
* TWBlue should be more reliable when checking for updates.
|
||||
* If running from source, automatic updates will not be checked as this works only for distribution. ([#540](https://github.com/MCV-Software/TWBlue/pull/540))
|
||||
* Fixed the 'report an error' item in the help menu. Now this item redirects to our gitHub issue tracker. ([#524](https://github.com/MCV-Software/TWBlue/pull/524))
|
||||
* Added Initial Support to GoToSocial. Some features are not fully implemented yet, although GoToXocial instances should be able to be used as normal sessions in TWBlue. Streaming, poll options and markdown are not supported but planned for the near future.
|
||||
* The translation module has been rewritten. Now, instead of offering translations with Google Translator, the user can choose between [LibreTranslate,](https://github.com/LibreTranslate/LibreTranslate) which requires no configuration thanks to the [instance of the NVDA Spanish community;](https://translate.nvda.es) or translate using [DeepL,](https://deepl.com) for which it is necessary to create an account on DeepL and [subscribe to a DeepL API Free plan](https://support.deepl.com/hc/en-us/articles/360021200939-DeepL-API-Free) to obtain the API key which can be used to translate up to 500000 characters every month. The API key can be entered in the global options dialog, under a new tab called translation services. When translating a text, the translation engine can be changed. When changing the translation engine, the target language must be selected again before translation takes place.
|
||||
* TWBlue should be able to switch to Windows 11 Keymap when running under Windows 11. ([#494](https://github.com/mcv-software/twblue/issues/494))
|
||||
* Mastodon:
|
||||
* Implemented actions for the notifications buffer on a mastodon instance. Actions can be performed from the contextual menu on every notification, or by using invisible keystrokes. ([#517](https://github.com/mcv-software/twblue/issues/517))
|
||||
* Implemented update profile dialog. ([#547](https://github.com/MCV-Software/TWBlue/pull/547))
|
||||
* It is possible to display an user profile from the user menu within the menu bar, or by using the invisible keystroke for user details. ([#555](https://github.com/MCV-Software/TWBlue/pull/555))
|
||||
* Added possibility to vote in polls. This is mapped to Alt+Win+Shift+V in the invisible keymaps for windows 10/11.
|
||||
* Added posts search. Take into account that Mastodon instances should be configured with full text search enabled. Search for posts only include posts the logged-in user has interacted with. ([#541](https://github.com/MCV-Software/TWBlue/pull/541))
|
||||
* Added user autocompletion settings in account settings dialog, so it is possible to ask TWBlue to scan mastodon accounts and add people from followers and following buffers. For now, user autocompletion can be used only when composing new posts or replies.
|
||||
* TWBlue should be able to ignore deleted direct messages or messages from deleted accounts. Previously, a direct message that no longer existed in the instance caused errors when loading the direct messages buffer and could potentially affect notifications as well.
|
||||
* TWBlue should be able to ignore notifications from deleted accounts or posts.
|
||||
* Added support for viewing communities: A community timeline is the local or public timeline of another instance. This is especially useful when the instance one is part of does not federate with other remote instances. The posts displayed are only those that are shared publicly. It is possible to interact with the posts from community timelines, but it should be noted that TWBlue will take some time to retrieve the post one wishes to interact with.
|
||||
* When viewing a post, a button displays the number of boosts and times it has been added to favorites. Clicking on that button will open a list of users who have interacted with the post. From that list, it is possible to view profiles and perform common user actions.
|
||||
* Now it is possible to mute conversations in Mastodon sessions. To do this, there is a button that can be called "Mute" or "Unmute Conversation" in the dialog to display the post. Conversations that have been muted will not generate notifications or mentions when they receive new replies. Only conversations that you are a part of can be muted.
|
||||
* Fixed an error that caused TWBlue to be unable to properly display the user action dialog from the followers or following buffer. ([#575](https://github.com/mcv-software/twblue/issues/575))
|
112
requirements.txt
112
requirements.txt
@@ -1,55 +1,57 @@
|
||||
wxpython
|
||||
pytest
|
||||
coverage
|
||||
wheel
|
||||
future
|
||||
configobj
|
||||
markdown
|
||||
requests
|
||||
oauthlib
|
||||
requests-oauthlib
|
||||
requests-toolbelt
|
||||
pypubsub
|
||||
arrow
|
||||
python-dateutil
|
||||
winpaths
|
||||
PySocks
|
||||
win_inet_pton
|
||||
# Install the latest RC of this lib
|
||||
# see https://github.com/ssut/py-googletrans/issues/234
|
||||
googletrans==4.0.0-rc1
|
||||
idna<3,>=2.5
|
||||
chardet
|
||||
urllib3
|
||||
youtube-dl
|
||||
python-vlc
|
||||
pypiwin32
|
||||
pywin32
|
||||
certifi
|
||||
backports.functools_lru_cache
|
||||
cx_freeze
|
||||
twitter-text-parser
|
||||
mastodon.py
|
||||
pyenchant
|
||||
sqlitedict
|
||||
cx-Logging
|
||||
h11
|
||||
h2
|
||||
hpack
|
||||
hstspreload
|
||||
httpcore
|
||||
httpx
|
||||
hyperframe
|
||||
rfc3986
|
||||
sniffio
|
||||
attrs
|
||||
importlib-metadata
|
||||
numpy
|
||||
pillow
|
||||
charset-normalizer
|
||||
demoji
|
||||
psutil
|
||||
git+https://github.com/accessibleapps/libloader
|
||||
git+https://github.com/accessibleapps/platform_utils
|
||||
git+https://github.com/accessibleapps/accessible_output2
|
||||
git+https://github.com/accessibleapps/sound_lib
|
||||
accessible_output2 @ git+https://github.com/accessibleapps/accessible_output2@57bda997d98e87dd78aa049e7021cf777871619b
|
||||
arrow==1.3.0
|
||||
attrs==23.2.0
|
||||
backports.functools-lru-cache==2.0.0
|
||||
blurhash==1.1.4
|
||||
certifi==2024.2.2
|
||||
chardet==5.2.0
|
||||
charset-normalizer==3.3.2
|
||||
colorama==0.4.6
|
||||
configobj==5.0.8
|
||||
coverage==7.5.1
|
||||
cx-Freeze==7.0.0
|
||||
cx-Logging==3.2.0
|
||||
decorator==5.1.1
|
||||
demoji==1.1.0
|
||||
deepl==1.18.0
|
||||
future==1.0.0
|
||||
idna==3.7
|
||||
importlib-metadata==7.1.0
|
||||
iniconfig==2.0.0
|
||||
libloader @ git+https://github.com/accessibleapps/libloader@bc94811c095b2e57a036acd88660be9a33260267
|
||||
libretranslatepy==2.1.4
|
||||
lief==0.14.1
|
||||
Markdown==3.6
|
||||
Mastodon.py==1.8.1
|
||||
numpy==1.26.4
|
||||
oauthlib==3.2.2
|
||||
packaging==24.0
|
||||
pillow==10.3.0
|
||||
platform_utils @ git+https://github.com/accessibleapps/platform_utils@e0d79f7b399c4ea677a633d2dde9202350d62c38
|
||||
pluggy==1.5.0
|
||||
psutil==5.9.8
|
||||
pyenchant==3.2.2
|
||||
pypiwin32==223
|
||||
Pypubsub==4.0.3
|
||||
PySocks==1.7.1
|
||||
pytest==8.2.1
|
||||
python-dateutil==2.9.0.post0
|
||||
python-magic-bin==0.4.14
|
||||
python-vlc==3.0.20123
|
||||
pywin32==306
|
||||
requests==2.32.1
|
||||
requests-oauthlib==2.0.0
|
||||
requests-toolbelt==1.0.0
|
||||
rfc3986==2.0.0
|
||||
six==1.16.0
|
||||
sniffio==1.3.1
|
||||
sound_lib @ git+https://github.com/accessibleapps/sound_lib@a439f0943fb95ee7b6ba24f51a686f47c4ad66b2
|
||||
sqlitedict==2.1.0
|
||||
twitter-text-parser==3.0.0
|
||||
types-python-dateutil==2.9.0.20240316
|
||||
urllib3==2.2.1
|
||||
win-inet-pton==1.1.0
|
||||
winpaths==0.2
|
||||
wxPython==4.2.1
|
||||
youtube-dl==2021.12.17
|
||||
zipp==3.18.2
|
@@ -27,7 +27,7 @@ var StartMenuFolder
|
||||
!insertmacro MUI_PAGE_STARTMENU startmenu $StartMenuFolder
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!define MUI_FINISHPAGE_LINK "Visit TWBlue website"
|
||||
!define MUI_FINISHPAGE_LINK_LOCATION "https://twblue.es"
|
||||
!define MUI_FINISHPAGE_LINK_LOCATION "https://twblue.mcvsoftware.com"
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\TWBlue.exe"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
@@ -65,7 +65,7 @@ CreateShortCut "$DESKTOP\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN startmenu
|
||||
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue.lnk" "$INSTDIR\TWBlue.exe"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "http://twblue.es"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TWBlue on the web.lnk" "https://twblue.mcvsoftware.com"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
@@ -74,7 +74,7 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "U
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "InstallLocation" $INSTDIR
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" "Publisher" "Manuel Cortéz"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "DisplayVersion" "0.95"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "https://twblue.es"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "URLInfoAbout" "https://twblue.mcvsoftware.com"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMajor" 0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "VersionMinor" 0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\twblue" "NoModify" 1
|
||||
|
@@ -9,8 +9,7 @@ update_period = integer(default=2)
|
||||
hide_gui = boolean(default=False)
|
||||
voice_enabled = boolean(default=False)
|
||||
ask_at_exit = boolean(default=True)
|
||||
autostart = boolean(default=False)
|
||||
handle_longtweets = boolean(default=True)
|
||||
read_long_posts_in_gui = boolean(default=True)
|
||||
use_invisible_keyboard_shorcuts = boolean(default=True)
|
||||
play_ready_sound = boolean(default=True)
|
||||
speak_ready_msg = boolean(default=True)
|
||||
@@ -18,9 +17,6 @@ log_level = string(default="error")
|
||||
load_keymap = string(default="default.keymap")
|
||||
donation_dialog_displayed = boolean(default=False)
|
||||
check_for_updates = boolean(default=True)
|
||||
remember_mention_and_longtweet = boolean(default=False)
|
||||
longtweet = boolean(default=false)
|
||||
mention_all = boolean(default=False)
|
||||
no_streaming = boolean(default=False)
|
||||
|
||||
[proxy]
|
||||
@@ -28,4 +24,10 @@ type = integer(default=0)
|
||||
server = string(default="")
|
||||
port = integer(default=8080)
|
||||
user = string(default="")
|
||||
password = string(default="")
|
||||
password = string(default="")
|
||||
|
||||
[translator]
|
||||
engine=string(default="LibreTranslate")
|
||||
lt_api_url=string(default="https://translate.nvda.es")
|
||||
lt_api_key=string(default="")
|
||||
deepl_api_key = string(default="")
|
@@ -1,5 +1,6 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
import os
|
||||
import sys
|
||||
import config_utils
|
||||
import paths
|
||||
import logging
|
||||
@@ -21,7 +22,10 @@ def setup ():
|
||||
log.debug("Loading keymap...")
|
||||
global keymap
|
||||
if float(platform.version()[:2]) >= 10 and app["app-settings"]["load_keymap"] == "default.keymap":
|
||||
app["app-settings"]["load_keymap"] = "Windows 10.keymap"
|
||||
if sys.getwindowsversion().build > 22000:
|
||||
app["app-settings"]["load_keymap"] = "Windows11.keymap"
|
||||
else:
|
||||
app["app-settings"]["load_keymap"] = "Windows 10.keymap"
|
||||
app.write()
|
||||
global changed_keymap
|
||||
changed_keymap = True
|
||||
|
@@ -5,3 +5,4 @@ from .conversations import ConversationBuffer, ConversationListBuffer
|
||||
from .users import UserBuffer
|
||||
from .notifications import NotificationsBuffer
|
||||
from .search import SearchBuffer
|
||||
from .community import CommunityBuffer
|
||||
|
@@ -320,7 +320,7 @@ class BaseBuffer(base.Buffer):
|
||||
return False
|
||||
return True
|
||||
|
||||
def reply(self, item=None, *args, **kwargs):
|
||||
def reply(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
visibility = item.visibility
|
||||
@@ -357,7 +357,7 @@ class BaseBuffer(base.Buffer):
|
||||
if hasattr(post.message, "destroy"):
|
||||
post.message.destroy()
|
||||
|
||||
def send_message(self, item=None, *args, **kwargs):
|
||||
def send_message(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
title = _("Conversation with {}").format(item.account.username)
|
||||
@@ -384,7 +384,7 @@ class BaseBuffer(base.Buffer):
|
||||
if hasattr(post.message, "destroy"):
|
||||
post.message.destroy()
|
||||
|
||||
def share_item(self, item=None, *args, **kwargs):
|
||||
def share_item(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
if self.can_share(item=item) == False:
|
||||
@@ -406,6 +406,8 @@ class BaseBuffer(base.Buffer):
|
||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
|
||||
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||
output.speak(self.get_message(), interrupt=True)
|
||||
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
||||
self.session.sound.play("audio.ogg")
|
||||
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
||||
@@ -414,7 +416,7 @@ class BaseBuffer(base.Buffer):
|
||||
pub.sendMessage("toggleShare", shareable=can_share)
|
||||
self.buffer.boost.Enable(can_share)
|
||||
|
||||
def audio(self, item=None, *args, **kwargs):
|
||||
def audio(self, event=None, item=None, *args, **kwargs):
|
||||
if sound.URLPlayer.player.is_playing():
|
||||
return sound.URLPlayer.stop_audio()
|
||||
if item == None:
|
||||
@@ -489,7 +491,7 @@ class BaseBuffer(base.Buffer):
|
||||
return item.reblog.url
|
||||
return item.url
|
||||
|
||||
def open_in_browser(self, item=None, *args, **kwargs):
|
||||
def open_in_browser(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
url = self.get_item_url(item=item)
|
||||
@@ -510,7 +512,7 @@ class BaseBuffer(base.Buffer):
|
||||
item = item.reblog
|
||||
call_threaded(self.session.api_call, call_name="status_unfavourite", preexec_message=_("Removing from favorites..."), _sound="favourite.ogg", id=item.id)
|
||||
|
||||
def toggle_favorite(self, item=None, *args, **kwargs):
|
||||
def toggle_favorite(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
if item.reblog != None:
|
||||
@@ -525,7 +527,7 @@ class BaseBuffer(base.Buffer):
|
||||
else:
|
||||
call_threaded(self.session.api_call, call_name="status_unfavourite", preexec_message=_("Removing from favorites..."), _sound="favourite.ogg", id=item.id)
|
||||
|
||||
def toggle_bookmark(self, item=None, *args, **kwargs):
|
||||
def toggle_bookmark(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
if item.reblog != None:
|
||||
@@ -550,7 +552,7 @@ class BaseBuffer(base.Buffer):
|
||||
output.speak(_("No status found with that ID"))
|
||||
return
|
||||
# print(post)
|
||||
msg = messages.viewPost(item, offset_hours=self.session.db["utc_offset"], item_url=self.get_item_url(item=item))
|
||||
msg = messages.viewPost(self.session, item, offset_hours=self.session.db["utc_offset"], item_url=self.get_item_url(item=item))
|
||||
|
||||
def ocr_image(self):
|
||||
post = self.get_item()
|
||||
@@ -591,8 +593,11 @@ class BaseBuffer(base.Buffer):
|
||||
response = viewer.message.ShowModal()
|
||||
viewer.message.Destroy()
|
||||
|
||||
def vote(self):
|
||||
post = self.get_item()
|
||||
def vote(self, item=None):
|
||||
if item == None:
|
||||
post = self.get_item()
|
||||
else:
|
||||
post = item
|
||||
if not hasattr(post, "poll") or post.poll == None:
|
||||
return
|
||||
poll = post.poll
|
||||
|
160
src/controller/buffers/mastodon/community.py
Normal file
160
src/controller/buffers/mastodon/community.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import logging
|
||||
import mastodon
|
||||
import widgetUtils
|
||||
import output
|
||||
from wxUI import commonMessageDialogs
|
||||
from sessions.mastodon import utils
|
||||
from . import base
|
||||
|
||||
log = logging.getLogger("controller.buffers.mastodon.community")
|
||||
|
||||
class CommunityBuffer(base.BaseBuffer):
|
||||
def __init__(self, community_url, *args, **kwargs):
|
||||
super(CommunityBuffer, self).__init__(*args, **kwargs)
|
||||
self.community_url = community_url
|
||||
self.community_api = mastodon.Mastodon(api_base_url=self.community_url)
|
||||
self.timeline = kwargs.get("timeline", "local")
|
||||
self.kwargs.pop("timeline")
|
||||
|
||||
def get_buffer_name(self):
|
||||
type = _("Local") if self.timeline == "local" else _("Federated")
|
||||
instance = self.community_url.replace("https://", "")
|
||||
return _(f"{type} timeline for {instance}")
|
||||
|
||||
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||
current_time = time.time()
|
||||
if self.execution_time == 0 or current_time-self.execution_time >= 180 or mandatory==True:
|
||||
self.execution_time = current_time
|
||||
log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type))
|
||||
log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs))
|
||||
count = self.session.settings["general"]["max_posts_per_call"]
|
||||
min_id = None
|
||||
# toDo: Implement reverse timelines properly here.
|
||||
if self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||
if self.session.settings["general"]["reverse_timelines"]:
|
||||
min_id = self.session.db[self.name][0].id
|
||||
else:
|
||||
min_id = self.session.db[self.name][-1].id
|
||||
try:
|
||||
results = self.community_api.timeline(timeline=self.timeline, min_id=min_id, limit=count, *self.args, **self.kwargs)
|
||||
results.reverse()
|
||||
except Exception as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
number_of_items = self.session.order_buffer(self.name, results)
|
||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||
self.put_items_on_list(number_of_items)
|
||||
if number_of_items > 0 and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True:
|
||||
self.session.sound.play(self.sound)
|
||||
# Autoread settings
|
||||
if avoid_autoreading == False and mandatory == True and number_of_items > 0 and self.name in self.session.settings["other_buffers"]["autoread_buffers"]:
|
||||
self.auto_read(number_of_items)
|
||||
return number_of_items
|
||||
|
||||
def get_more_items(self):
|
||||
elements = []
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
max_id = self.session.db[self.name][0].id
|
||||
else:
|
||||
max_id = self.session.db[self.name][-1].id
|
||||
try:
|
||||
items = self.community_api.timeline(timeline=self.timeline, max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
||||
except Exception as e:
|
||||
log.exception("Error %s" % (str(e)))
|
||||
return
|
||||
items_db = self.session.db[self.name]
|
||||
for i in items:
|
||||
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||
elements.append(i)
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
items_db.insert(0, i)
|
||||
else:
|
||||
items_db.append(i)
|
||||
self.session.db[self.name] = items_db
|
||||
selection = self.buffer.list.get_selected()
|
||||
log.debug("Retrieved %d items from cursored search in function %s." % (len(elements), self.function))
|
||||
safe = True
|
||||
if self.session.settings["general"]["read_preferences_from_instance"]:
|
||||
safe = self.session.expand_spoilers == False
|
||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||
for i in elements:
|
||||
post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
|
||||
self.buffer.list.insert_item(True, *post)
|
||||
else:
|
||||
for i in elements:
|
||||
post = self.compose_function(i, self.session.db, self.session.settings, self.session.settings["general"]["relative_times"], self.session.settings["general"]["show_screen_names"], safe=safe)
|
||||
self.buffer.list.insert_item(False, *post)
|
||||
self.buffer.list.select_item(selection)
|
||||
output.speak(_(u"%s items retrieved") % (str(len(elements))), True)
|
||||
|
||||
def remove_buffer(self, force=False):
|
||||
if force == False:
|
||||
dlg = commonMessageDialogs.remove_buffer()
|
||||
else:
|
||||
dlg = widgetUtils.YES
|
||||
if dlg == widgetUtils.YES:
|
||||
tl_info = f"{self.timeline}@{self.community_url}"
|
||||
self.session.settings["other_buffers"]["communities"].remove(tl_info)
|
||||
self.session.settings.write()
|
||||
if self.name in self.session.db:
|
||||
self.session.db.pop(self.name)
|
||||
return True
|
||||
elif dlg == widgetUtils.NO:
|
||||
return False
|
||||
|
||||
def get_item_from_instance(self, *args, **kwargs):
|
||||
item = self.get_item()
|
||||
try:
|
||||
results = self.session.api.search(q=item.url, resolve=True, result_type="statuses")
|
||||
except Exception as e:
|
||||
log.exception("Error when searching for remote post.")
|
||||
return None
|
||||
item = results["statuses"][0]
|
||||
return item
|
||||
|
||||
def reply(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).reply(item=item)
|
||||
|
||||
def send_message(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).send_message(item=item)
|
||||
|
||||
def share_item(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).share_item(item=item)
|
||||
|
||||
def add_to_favorites(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).add_to_favorite(item=item)
|
||||
|
||||
def remove_from_favorites(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).remove_from_favorites(item=item)
|
||||
|
||||
def toggle_favorite(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).toggle_favorite(item=item)
|
||||
|
||||
def toggle_bookmark(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).toggle_bookmark(item=item)
|
||||
|
||||
def vote(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).vote(item=item)
|
||||
|
||||
def view_item(self, *args, **kwargs):
|
||||
item = self.get_item_from_instance()
|
||||
if item != None:
|
||||
super(CommunityBuffer, self).view_item(item=item)
|
@@ -4,6 +4,7 @@ import logging
|
||||
import wx
|
||||
import widgetUtils
|
||||
import output
|
||||
import config
|
||||
from mastodon import MastodonNotFoundError
|
||||
from controller.mastodon import messages
|
||||
from controller.buffers.mastodon.base import BaseBuffer
|
||||
@@ -162,6 +163,8 @@ class ConversationListBuffer(BaseBuffer):
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
post = self.get_item()
|
||||
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||
output.speak(self.get_message(), interrupt=True)
|
||||
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
||||
self.session.sound.play("audio.ogg")
|
||||
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
||||
|
@@ -1,8 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import logging
|
||||
import arrow
|
||||
import widgetUtils
|
||||
import output
|
||||
import languageHandler
|
||||
import config
|
||||
from pubsub import pub
|
||||
from controller.buffers.mastodon.base import BaseBuffer
|
||||
from controller.mastodon import messages
|
||||
@@ -38,8 +41,11 @@ class NotificationsBuffer(BaseBuffer):
|
||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 1, ts)
|
||||
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||
output.speak(self.get_message(), interrupt=True)
|
||||
|
||||
def bind_events(self):
|
||||
self.buffer.set_focus_function(self.onFocus)
|
||||
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.post)
|
||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.destroy_status, self.buffer.dismiss)
|
||||
|
@@ -153,6 +153,7 @@ class Controller(object):
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.toggle_buffer_mute, self.view.mute_buffer)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_timeline, self.view.timeline)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_favs_timeline, self.view.favs)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.community_timeline, self.view.community_timeline)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.open_conversation, menuitem=self.view.view_conversation)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.seekLeft, menuitem=self.view.seekLeft)
|
||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.seekRight, menuitem=self.view.seekRight)
|
||||
@@ -846,7 +847,14 @@ class Controller(object):
|
||||
|
||||
def register_invisible_keyboard_shorcuts(self, keymap):
|
||||
if config.changed_keymap:
|
||||
commonMessageDialogs.changed_keymap()
|
||||
build_number = sys.getwindowsversion().build
|
||||
if build_number > 22000:
|
||||
system = "Windows 11"
|
||||
keystroke_editor_shortcut = "Control+Win+Alt+K"
|
||||
else:
|
||||
system = "Windows 10"
|
||||
keystroke_editor_shortcut = "Win+Alt+K"
|
||||
commonMessageDialogs.changed_keymap(system, keystroke_editor_shortcut)
|
||||
# Make sure we pass a keymap without undefined keystrokes.
|
||||
new_keymap = {key: keymap[key] for key in keymap.keys() if keymap[key] != ""}
|
||||
self.keyboard_handler = WXKeyboardHandler(self.view)
|
||||
@@ -1141,3 +1149,9 @@ class Controller(object):
|
||||
handler = self.get_handler(type=buffer.session.type)
|
||||
if handler and hasattr(handler, 'openFollowingTimeline'):
|
||||
handler.openFollowingTimeline(self, buffer, user)
|
||||
|
||||
def community_timeline(self, *args, user=None):
|
||||
buffer = self.get_best_buffer()
|
||||
handler = self.get_handler(type=buffer.session.type)
|
||||
if handler and hasattr(handler, 'community_timeline'):
|
||||
handler.community_timeline(self, buffer)
|
||||
|
@@ -1,7 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import logging
|
||||
import mastodon
|
||||
import output
|
||||
from mastodon import MastodonError
|
||||
from pubsub import pub
|
||||
from mysc import restart
|
||||
from mysc.thread_utils import call_threaded
|
||||
@@ -10,7 +12,7 @@ from wxUI.dialogs.mastodon import dialogs
|
||||
from wxUI.dialogs import userAliasDialogs
|
||||
from wxUI import commonMessageDialogs
|
||||
from wxUI.dialogs.mastodon import updateProfile as update_profile_dialogs
|
||||
from wxUI.dialogs.mastodon import showUserProfile
|
||||
from wxUI.dialogs.mastodon import showUserProfile, communityTimeline
|
||||
from sessions.mastodon.utils import html_filter
|
||||
from . import userActions, settings
|
||||
|
||||
@@ -48,7 +50,7 @@ class Handler(object):
|
||||
details=_("Show user profile"),
|
||||
favs=None,
|
||||
# In buffer Menu.
|
||||
trends=None,
|
||||
community_timeline =_("Create community timeline"),
|
||||
filter=None,
|
||||
manage_filters=None
|
||||
)
|
||||
@@ -102,9 +104,16 @@ class Handler(object):
|
||||
# for i in session.settings["other_buffers"]["lists"]:
|
||||
# pub.sendMessage("createBuffer", buffer_type="ListBuffer", session_type=session.type, buffer_title=_(u"List for {}").format(i), parent_tab=lists_position, start=False, kwargs=dict(parent=controller.view.nb, function="list_timeline", name="%s-list" % (i,), sessionObject=session, name, bufferType=None, sound="list_tweet.ogg", list_id=utils.find_list(i, session.db["lists"]), include_ext_alt_text=True, tweet_mode="extended"))
|
||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Searches"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="searches", account=name))
|
||||
searches_position =controller.view.search("searches", session.db["user_name"])
|
||||
searches_position =controller.view.search("searches", name)
|
||||
for term in session.settings["other_buffers"]["post_searches"]:
|
||||
pub.sendMessage("createBuffer", buffer_type="SearchBuffer", session_type=session.type, buffer_title=_("Search for {}").format(term), parent_tab=searches_position, start=True, kwargs=dict(parent=controller.view.nb, compose_func="compose_post", function="search", name="%s-searchterm" % (term,), sessionObject=session, account=session.get_name(), sound="search_updated.ogg", q=term, result_type="statuses"))
|
||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Communities"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="communities", account=name))
|
||||
communities_position =controller.view.search("communities", name)
|
||||
for community in session.settings["other_buffers"]["communities"]:
|
||||
bufftype = _("Local") if community.split("@")[0] == "local" else _("federated")
|
||||
community_name = community.split("@")[1].replace("https://", "")
|
||||
title = _(f"{bufftype} timeline for {community_name}")
|
||||
pub.sendMessage("createBuffer", buffer_type="CommunityBuffer", session_type=session.type, buffer_title=title, parent_tab=communities_position, start=True, kwargs=dict(parent=controller.view.nb, function="timeline", compose_func="compose_post", name=community, sessionObject=session, community_url=community.split("@")[1], account=session.get_name(), sound="search_updated.ogg", timeline=community.split("@")[0]))
|
||||
# for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
||||
# pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (i,), sessionObject=session, name, trendsFor=i, sound="trends_updated.ogg"))
|
||||
|
||||
@@ -121,7 +130,12 @@ class Handler(object):
|
||||
pub.sendMessage("buffer-title-changed", buffer=buffer)
|
||||
|
||||
def open_conversation(self, controller, buffer):
|
||||
post = buffer.get_item()
|
||||
# detect if we are in a community buffer.
|
||||
# Community buffers are special because we'll need to retrieve the object locally at first.
|
||||
if hasattr(buffer, "community_url"):
|
||||
post = buffer.get_item_from_instance()
|
||||
else:
|
||||
post = buffer.get_item()
|
||||
if post.reblog != None:
|
||||
post = post.reblog
|
||||
conversations_position =controller.view.search("direct_messages", buffer.session.get_name())
|
||||
@@ -151,7 +165,11 @@ class Handler(object):
|
||||
users.insert(0, status.reblog.account.acct)
|
||||
else:
|
||||
users = [user.acct for user in status.mentions if user.id != buffer.session.db["user_id"]]
|
||||
if item.account.acct not in users:
|
||||
if hasattr(item, "account"):
|
||||
acct = item.account.acct
|
||||
else:
|
||||
acct = item.acct
|
||||
if acct not in users:
|
||||
users.insert(0, item.account.acct)
|
||||
u = userActions.userActions(buffer.session, users)
|
||||
|
||||
@@ -355,3 +373,29 @@ class Handler(object):
|
||||
user = buffer.session.api.account(selectedUser[-1])
|
||||
dlg = showUserProfile.ShowUserProfile(user)
|
||||
dlg.ShowModal()
|
||||
|
||||
def community_timeline(self, controller, buffer):
|
||||
dlg = communityTimeline.CommunityTimeline()
|
||||
if dlg.ShowModal() != wx.ID_OK:
|
||||
return
|
||||
url = dlg.url.GetValue()
|
||||
bufftype = dlg.get_action()
|
||||
local_api = mastodon.Mastodon(api_base_url=url)
|
||||
try:
|
||||
instance = local_api.instance()
|
||||
except MastodonError:
|
||||
commonMessageDialogs.invalid_instance()
|
||||
return
|
||||
if bufftype == "local":
|
||||
title = _(f"Local timeline for {url.replace('https://', '')}")
|
||||
else:
|
||||
title = _(f"Federated timeline for {url}")
|
||||
bufftype = "public"
|
||||
dlg.Destroy()
|
||||
tl_info = f"{bufftype}@{url}"
|
||||
if tl_info in buffer.session.settings["other_buffers"]["communities"]:
|
||||
return # buffer already exists.
|
||||
buffer.session.settings["other_buffers"]["communities"].append(tl_info)
|
||||
buffer.session.settings.write()
|
||||
communities_position =controller.view.search("communities", buffer.session.get_name())
|
||||
pub.sendMessage("createBuffer", buffer_type="CommunityBuffer", session_type=buffer.session.type, buffer_title=title, parent_tab=communities_position, start=True, kwargs=dict(parent=controller.view.nb, function="timeline", name=tl_info, sessionObject=buffer.session, account=buffer.session.get_name(), sound="tweet_timeline.ogg", community_url=url, timeline=bufftype))
|
||||
|
@@ -6,10 +6,12 @@ import widgetUtils
|
||||
import config
|
||||
import output
|
||||
from twitter_text import parse_tweet, config
|
||||
from mastodon import MastodonError
|
||||
from controller import messages
|
||||
from sessions.mastodon import templates
|
||||
from wxUI.dialogs.mastodon import postDialogs
|
||||
from extra.autocompletionUsers import completion
|
||||
from . import userList
|
||||
|
||||
def character_count(post_text, post_cw, character_limit=500):
|
||||
# We will use text for counting character limit only.
|
||||
@@ -235,9 +237,11 @@ class post(messages.basicMessage):
|
||||
self.message.visibility.SetSelection(setting)
|
||||
|
||||
class viewPost(post):
|
||||
def __init__(self, post, offset_hours=0, date="", item_url=""):
|
||||
def __init__(self, session, post, offset_hours=0, date="", item_url=""):
|
||||
self.session = session
|
||||
if post.reblog != None:
|
||||
post = post.reblog
|
||||
self.post_id = post.id
|
||||
author = post.account.display_name if post.account.display_name != "" else post.account.username
|
||||
title = _(u"Post from {}").format(author)
|
||||
image_description = templates.process_image_descriptions(post.media_attachments)
|
||||
@@ -254,6 +258,13 @@ class viewPost(post):
|
||||
else:
|
||||
source = source_obj.get("name")
|
||||
self.message = postDialogs.viewPost(text=text, boosts_count=boost_count, favs_count=favs_count, source=source, date=date, privacy=privacy)
|
||||
participants = [post.account.id] + [account.id for account in post.mentions]
|
||||
print(post, participants)
|
||||
if self.session.db["user_id"] in participants:
|
||||
self.message.mute.Enable(True)
|
||||
if post.muted:
|
||||
self.message.mute.SetLabel(_("Unmute conversation"))
|
||||
widgetUtils.connect_event(self.message.mute, widgetUtils.BUTTON_PRESSED, self.mute_unmute)
|
||||
self.message.SetTitle(title)
|
||||
if image_description != "":
|
||||
self.message.image_description.Enable(True)
|
||||
@@ -264,12 +275,42 @@ class viewPost(post):
|
||||
widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share)
|
||||
self.item_url = item_url
|
||||
widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate)
|
||||
widgetUtils.connect_event(self.message.boosts_button, widgetUtils.BUTTON_PRESSED, self.on_boosts)
|
||||
widgetUtils.connect_event(self.message.favorites_button, widgetUtils.BUTTON_PRESSED, self.on_favorites)
|
||||
self.message.ShowModal()
|
||||
|
||||
# We won't need text_processor in this dialog, so let's avoid it.
|
||||
def text_processor(self):
|
||||
pass
|
||||
|
||||
def mute_unmute(self, *args, **kwargs):
|
||||
post = self.session.api.status(self.post_id)
|
||||
if post.muted == True:
|
||||
action = "status_unmute"
|
||||
new_label = _("Mute conversation")
|
||||
msg = _("Conversation unmuted.")
|
||||
else:
|
||||
action = "status_mute"
|
||||
new_label = _("Unmute conversation")
|
||||
msg = _("Conversation muted.")
|
||||
try:
|
||||
getattr(self.session.api, action)(self.post_id)
|
||||
self.message.mute.SetLabel(new_label)
|
||||
output.speak(msg)
|
||||
except MastodonError:
|
||||
return
|
||||
|
||||
|
||||
def on_boosts(self, *args, **kwargs):
|
||||
users = self.session.api.status_reblogged_by(self.post_id)
|
||||
title = _("people who boosted this post")
|
||||
user_list = userList.MastodonUserList(session=self.session, users=users, title=title)
|
||||
|
||||
def on_favorites(self, *args, **kwargs):
|
||||
users = self.session.api.status_favourited_by(self.post_id)
|
||||
title = _("people who favorited this post")
|
||||
user_list = userList.MastodonUserList(session=self.session, users=users, title=title)
|
||||
|
||||
def share(self, *args, **kwargs):
|
||||
if hasattr(self, "item_url"):
|
||||
output.copy(self.item_url)
|
||||
|
25
src/controller/mastodon/userList.py
Normal file
25
src/controller/mastodon/userList.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from mastodon import MastodonError
|
||||
from wxUI.dialogs.mastodon import showUserProfile
|
||||
from controller.userList import UserListController
|
||||
from . import userActions
|
||||
|
||||
class MastodonUserList(UserListController):
|
||||
|
||||
def process_users(self, users):
|
||||
return [dict(id=user.id, display_name=f"{user.display_name} (@{user.acct})", acct=user.acct) for user in users]
|
||||
|
||||
def on_actions(self, *args, **kwargs):
|
||||
user = self.dialog.user_list.GetSelection()
|
||||
user_account = self.users[user]
|
||||
u = userActions.userActions(self.session, [user_account.get("acct")])
|
||||
|
||||
def on_details(self, *args, **kwargs):
|
||||
user = self.dialog.user_list.GetSelection()
|
||||
user_id = self.users[user].get("id")
|
||||
try:
|
||||
user_object = self.session.api.account(user_id)
|
||||
except MastodonError:
|
||||
return
|
||||
dlg = showUserProfile.ShowUserProfile(user_object)
|
||||
dlg.ShowModal()
|
@@ -1,25 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import widgetUtils
|
||||
import output
|
||||
from extra import translator, SpellChecker
|
||||
import config
|
||||
from extra import SpellChecker
|
||||
from extra.translator import TranslatorController
|
||||
|
||||
class basicMessage(object):
|
||||
def translate(self, event=None):
|
||||
dlg = translator.gui.translateDialog()
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
text_to_translate = self.message.text.GetValue()
|
||||
language_dict = translator.translator.available_languages()
|
||||
for k in language_dict:
|
||||
if language_dict[k] == dlg.dest_lang.GetStringSelection():
|
||||
dst = k
|
||||
msg = translator.translator.translate(text=text_to_translate, target=dst)
|
||||
self.message.text.ChangeValue(msg)
|
||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||
self.text_processor()
|
||||
self.message.text.SetFocus()
|
||||
output.speak(_(u"Translated"))
|
||||
else:
|
||||
t = TranslatorController(self.message.text.GetValue())
|
||||
if t.response == False:
|
||||
return
|
||||
msg = t.translate()
|
||||
self.message.text.ChangeValue(msg)
|
||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||
self.text_processor()
|
||||
self.message.text.SetFocus()
|
||||
output.speak(_(u"Translated"))
|
||||
|
||||
def text_processor(self, *args, **kwargs):
|
||||
pass
|
||||
|
@@ -6,7 +6,6 @@ import config
|
||||
import languageHandler
|
||||
import application
|
||||
from pubsub import pub
|
||||
from mysc import autostart as autostart_windows
|
||||
from wxUI.dialogs import configuration
|
||||
from wxUI import commonMessageDialogs
|
||||
|
||||
@@ -48,21 +47,16 @@ class globalSettingsController(object):
|
||||
self.dialog.create_general(langs,self.kmfriendlies)
|
||||
self.dialog.general.language.SetSelection(id)
|
||||
self.dialog.general.km.SetSelection(self.kmid)
|
||||
if paths.mode == "installed":
|
||||
self.dialog.set_value("general", "autostart", config.app["app-settings"]["autostart"])
|
||||
else:
|
||||
self.dialog.general.autostart.Enable(False)
|
||||
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
|
||||
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
|
||||
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
|
||||
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
|
||||
self.dialog.set_value("general", "handle_longtweets", config.app["app-settings"]["handle_longtweets"])
|
||||
self.dialog.set_value("general", "read_long_posts_in_gui", config.app["app-settings"]["read_long_posts_in_gui"])
|
||||
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
|
||||
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
|
||||
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
|
||||
self.dialog.set_value("general", "update_period", config.app["app-settings"]["update_period"])
|
||||
self.dialog.set_value("general", "check_for_updates", config.app["app-settings"]["check_for_updates"])
|
||||
self.dialog.set_value("general", "remember_mention_and_longtweet", config.app["app-settings"]["remember_mention_and_longtweet"])
|
||||
proxyTypes = [_("System default"), _("HTTP"), _("SOCKS v4"), _("SOCKS v4 with DNS support"), _("SOCKS v5"), _("SOCKS v5 with DNS support")]
|
||||
self.dialog.create_proxy(proxyTypes)
|
||||
try:
|
||||
@@ -73,7 +67,10 @@ class globalSettingsController(object):
|
||||
self.dialog.set_value("proxy", "port", config.app["proxy"]["port"])
|
||||
self.dialog.set_value("proxy", "user", config.app["proxy"]["user"])
|
||||
self.dialog.set_value("proxy", "password", config.app["proxy"]["password"])
|
||||
|
||||
self.dialog.create_translator_panel()
|
||||
self.dialog.set_value("translator_panel", "libre_api_url", config.app["translator"]["lt_api_url"])
|
||||
self.dialog.set_value("translator_panel", "libre_api_key", config.app["translator"]["lt_api_key"])
|
||||
self.dialog.set_value("translator_panel", "deepL_api_key", config.app["translator"]["deepl_api_key"])
|
||||
self.dialog.realize()
|
||||
self.response = self.dialog.get_response()
|
||||
|
||||
@@ -89,9 +86,6 @@ class globalSettingsController(object):
|
||||
kmFile.close()
|
||||
log.debug("Triggered app restart due to a keymap change.")
|
||||
self.needs_restart = True
|
||||
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
|
||||
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
|
||||
autostart_windows.setAutoStart(application.name, enable=self.dialog.get_value("general", "autostart"))
|
||||
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
|
||||
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
|
||||
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
|
||||
@@ -106,11 +100,10 @@ class globalSettingsController(object):
|
||||
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
||||
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
||||
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
||||
config.app["app-settings"]["handle_longtweets"] = self.dialog.get_value("general", "handle_longtweets")
|
||||
config.app["app-settings"]["read_long_posts_in_gui"] = self.dialog.get_value("general", "read_long_posts_in_gui")
|
||||
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
|
||||
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
|
||||
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
|
||||
config.app["app-settings"]["remember_mention_and_longtweet"] = self.dialog.get_value("general", "remember_mention_and_longtweet")
|
||||
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
||||
if self.is_started == True:
|
||||
self.needs_restart = True
|
||||
@@ -120,4 +113,7 @@ class globalSettingsController(object):
|
||||
config.app["proxy"]["port"] = self.dialog.get_value("proxy", "port")
|
||||
config.app["proxy"]["user"] = self.dialog.get_value("proxy", "user")
|
||||
config.app["proxy"]["password"] = self.dialog.get_value("proxy", "password")
|
||||
config.app["translator"]["lt_api_url"] = self.dialog.get_value("translator_panel", "libre_api_url")
|
||||
config.app["translator"]["lt_api_key"] = self.dialog.get_value("translator_panel", "libre_api_key")
|
||||
config.app["translator"]["deepl_api_key"] = self.dialog.get_value("translator_panel", "deepL_api_key")
|
||||
config.app.write()
|
||||
|
23
src/controller/userList.py
Normal file
23
src/controller/userList.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import widgetUtils
|
||||
from pubsub import pub
|
||||
from wxUI.dialogs import userList
|
||||
|
||||
class UserListController(object):
|
||||
def __init__(self, users, session, title):
|
||||
super(UserListController, self).__init__()
|
||||
self.session = session
|
||||
self.users = self.process_users(users)
|
||||
self.dialog = userList.UserListDialog(title=title, users=[user.get("display_name", user.get("acct")) for user in self.users])
|
||||
widgetUtils.connect_event(self.dialog.actions_button, widgetUtils.BUTTON_PRESSED, self.on_actions)
|
||||
widgetUtils.connect_event(self.dialog.details_button, widgetUtils.BUTTON_PRESSED, self.on_details)
|
||||
self.dialog.ShowModal()
|
||||
|
||||
def process_users(self, users):
|
||||
return {}
|
||||
|
||||
def on_actions(self):
|
||||
pass
|
||||
|
||||
def on_details(self, *args, **kwargs):
|
||||
pass
|
@@ -1,5 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from . import translator
|
||||
from . import wx_ui as gui
|
||||
from .translator import TranslatorController
|
||||
|
1
src/extra/translator/engines/__init__.py
Normal file
1
src/extra/translator/engines/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
14
src/extra/translator/engines/deep_l.py
Normal file
14
src/extra/translator/engines/deep_l.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import config
|
||||
from deepl import Translator
|
||||
|
||||
def translate(text: str, target_language: str) -> str:
|
||||
key = config.app["translator"]["deepl_api_key"]
|
||||
t = Translator(key)
|
||||
return t.translate_text(text, target_lang=target_language).text
|
||||
|
||||
def languages():
|
||||
key = config.app["translator"]["deepl_api_key"]
|
||||
t = Translator(key)
|
||||
langs = t.get_target_languages()
|
||||
return langs
|
45
src/extra/translator/engines/libre_translate.py
Normal file
45
src/extra/translator/engines/libre_translate.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Modified Libretranslatepy module which adds an user agent for making requests against more instances. """
|
||||
import json
|
||||
from typing import Any, Dict
|
||||
from urllib import request, parse
|
||||
from libretranslatepy import LibreTranslateAPI
|
||||
|
||||
class CustomLibreTranslateAPI(LibreTranslateAPI):
|
||||
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
|
||||
def _create_request(self, url: str, method: str, data: Dict[str, str]) -> request.Request:
|
||||
url_params = parse.urlencode(data)
|
||||
req = request.Request(url, method=method, data=url_params.encode())
|
||||
req.add_header("User-Agent", self.USER_AGENT)
|
||||
return req
|
||||
|
||||
def translate(self, q: str, source: str = "en", target: str = "es", timeout: int | None = None) -> Any:
|
||||
url = self.url + "translate"
|
||||
params: Dict[str, str] = {"q": q, "source": source, "target": target}
|
||||
if self.api_key is not None:
|
||||
params["api_key"] = self.api_key
|
||||
req = self._create_request(url=url, method="POST", data=params)
|
||||
response = request.urlopen(req, timeout=timeout)
|
||||
response_str = response.read().decode()
|
||||
return json.loads(response_str)["translatedText"]
|
||||
|
||||
def detect(self, q: str, timeout: int | None = None) -> Any:
|
||||
url = self.url + "detect"
|
||||
params: Dict[str, str] = {"q": q}
|
||||
if self.api_key is not None:
|
||||
params["api_key"] = self.api_key
|
||||
req = self._create_request(url=url, method="POST", data=params)
|
||||
response = request.urlopen(req, timeout=timeout)
|
||||
response_str = response.read().decode()
|
||||
return json.loads(response_str)
|
||||
|
||||
def languages(self, timeout: int | None = None) -> Any:
|
||||
url = self.url + "languages"
|
||||
params: Dict[str, str] = dict()
|
||||
if self.api_key is not None:
|
||||
params["api_key"] = self.api_key
|
||||
req = self._create_request(url=url, method="GET", data=params)
|
||||
response = request.urlopen(req, timeout=timeout)
|
||||
response_str = response.read().decode()
|
||||
return json.loads(response_str)
|
@@ -1,116 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
from googletrans import Translator, LANGUAGES
|
||||
import threading
|
||||
import wx
|
||||
import config
|
||||
from pubsub import pub
|
||||
from . engines import libre_translate, deep_l
|
||||
from .wx_ui import translateDialog
|
||||
|
||||
log = logging.getLogger("extras.translator")
|
||||
|
||||
# create a single translator instance
|
||||
# see https://github.com/ssut/py-googletrans/issues/234
|
||||
t = None
|
||||
class TranslatorController(object):
|
||||
def __init__(self, text):
|
||||
super(TranslatorController, self).__init__()
|
||||
self.text = text
|
||||
self.languages = []
|
||||
self.response = False
|
||||
self.dialog = translateDialog()
|
||||
pub.subscribe(self.on_engine_changed, "translator.engine_changed")
|
||||
if config.app["translator"]["engine"] == "LibreTranslate":
|
||||
self.dialog.engine_select.SetSelection(0)
|
||||
elif config.app["translator"]["engine"] == "DeepL":
|
||||
self.dialog.engine_select.SetSelection(1)
|
||||
threading.Thread(target=self.load_languages).start()
|
||||
if self.dialog.ShowModal() == wx.ID_OK:
|
||||
self.response = True
|
||||
for k in self.language_dict:
|
||||
if self.language_dict[k] == self.dialog.dest_lang.GetStringSelection():
|
||||
self.target_language= k
|
||||
pub.unsubscribe(self.on_engine_changed, "translator.engine_changed")
|
||||
|
||||
def translate(text="", target="en"):
|
||||
global t
|
||||
log.debug("Received translation request for language %s, text=%s" % (target, text))
|
||||
if t == None:
|
||||
t = Translator()
|
||||
vars = dict(text=text, dest=target)
|
||||
return t.translate(**vars).text
|
||||
def load_languages(self):
|
||||
self.language_dict = self.get_languages()
|
||||
self.languages = [self.language_dict[k] for k in self.language_dict]
|
||||
self.dialog.set_languages(self.languages)
|
||||
|
||||
supported_langs = None
|
||||
def on_engine_changed(self, engine):
|
||||
config.app["translator"]["engine"] = engine
|
||||
config.app.write()
|
||||
threading.Thread(target=self.load_languages).start()
|
||||
|
||||
languages = {
|
||||
"af": _(u"Afrikaans"),
|
||||
"sq": _(u"Albanian"),
|
||||
"am": _(u"Amharic"),
|
||||
"ar": _(u"Arabic"),
|
||||
"hy": _(u"Armenian"),
|
||||
"az": _(u"Azerbaijani"),
|
||||
"eu": _(u"Basque"),
|
||||
"be": _(u"Belarusian"),
|
||||
"bn": _(u"Bengali"),
|
||||
"bh": _(u"Bihari"),
|
||||
"bg": _(u"Bulgarian"),
|
||||
"my": _(u"Burmese"),
|
||||
"ca": _(u"Catalan"),
|
||||
"chr": _(u"Cherokee"),
|
||||
"zh": _(u"Chinese"),
|
||||
"zh-CN": _(u"Chinese_simplified"),
|
||||
"zh-TW": _(u"Chinese_traditional"),
|
||||
"hr": _(u"Croatian"),
|
||||
"cs": _(u"Czech"),
|
||||
"da": _(u"Danish"),
|
||||
"dv": _(u"Dhivehi"),
|
||||
"nl": _(u"Dutch"),
|
||||
"en": _(u"English"),
|
||||
"eo": _(u"Esperanto"),
|
||||
"et": _(u"Estonian"),
|
||||
"tl": _(u"Filipino"),
|
||||
"fi": _(u"Finnish"),
|
||||
"fr": _(u"French"),
|
||||
"gl": _(u"Galician"),
|
||||
"ka": _(u"Georgian"),
|
||||
"de": _(u"German"),
|
||||
"el": _(u"Greek"),
|
||||
"gn": _(u"Guarani"),
|
||||
"gu": _(u"Gujarati"),
|
||||
"iw": _(u"Hebrew"),
|
||||
"hi": _(u"Hindi"),
|
||||
"hu": _(u"Hungarian"),
|
||||
"is": _(u"Icelandic"),
|
||||
"id": _(u"Indonesian"),
|
||||
"iu": _(u"Inuktitut"),
|
||||
"ga": _(u"Irish"),
|
||||
"it": _(u"Italian"),
|
||||
"ja": _(u"Japanese"),
|
||||
"kn": _(u"Kannada"),
|
||||
"kk": _(u"Kazakh"),
|
||||
"km": _(u"Khmer"),
|
||||
"ko": _(u"Korean"),
|
||||
"ku": _(u"Kurdish"),
|
||||
"ky": _(u"Kyrgyz"),
|
||||
"lo": _(u"Laothian"),
|
||||
"lv": _(u"Latvian"),
|
||||
"lt": _(u"Lithuanian"),
|
||||
"mk": _(u"Macedonian"),
|
||||
"ms": _(u"Malay"),
|
||||
"ml": _(u"Malayalam"),
|
||||
"mt": _(u"Maltese"),
|
||||
"mr": _(u"Marathi"),
|
||||
"mn": _(u"Mongolian"),
|
||||
"ne": _(u"Nepali"),
|
||||
"no": _(u"Norwegian"),
|
||||
"or": _(u"Oriya"),
|
||||
"ps": _(u"Pashto"),
|
||||
"fa": _(u"Persian"),
|
||||
"pl": _(u"Polish"),
|
||||
"pt": _(u"Portuguese"),
|
||||
"pa": _(u"Punjabi"),
|
||||
"ro": _(u"Romanian"),
|
||||
"ru": _(u"Russian"),
|
||||
"sa": _(u"Sanskrit"),
|
||||
"sr": _(u"Serbian"),
|
||||
"sd": _(u"Sindhi"),
|
||||
"si": _(u"Sinhalese"),
|
||||
"sk": _(u"Slovak"),
|
||||
"sl": _(u"Slovenian"),
|
||||
"es": _(u"Spanish"),
|
||||
"sw": _(u"Swahili"),
|
||||
"sv": _(u"Swedish"),
|
||||
"tg": _(u"Tajik"),
|
||||
"ta": _(u"Tamil"),
|
||||
"tl": _(u"Tagalog"),
|
||||
"te": _(u"Telugu"),
|
||||
"th": _(u"Thai"),
|
||||
"bo": _(u"Tibetan"),
|
||||
"tr": _(u"Turkish"),
|
||||
"uk": _(u"Ukrainian"),
|
||||
"ur": _(u"Urdu"),
|
||||
"uz": _(u"Uzbek"),
|
||||
"ug": _(u"Uighur"),
|
||||
"vi": _(u"Vietnamese"),
|
||||
"cy": _(u"Welsh"),
|
||||
"yi": _(u"Yiddish")
|
||||
}
|
||||
def translate(self):
|
||||
log.debug("Received translation request for language %s, text=%s" % (self.target_language, self.text))
|
||||
if config.app["translator"].get("engine") == "LibreTranslate":
|
||||
translator = libre_translate.CustomLibreTranslateAPI(config.app["translator"]["lt_api_url"], config.app["translator"]["lt_api_key"])
|
||||
vars = dict(q=self.text, target=self.target_language)
|
||||
return translator.translate(**vars)
|
||||
elif config.app["translator"]["engine"] == "DeepL" and config.app["translator"]["deepl_api_key"] != "":
|
||||
return deep_l.translate(text=self.text, target_language=self.target_language)
|
||||
|
||||
def available_languages():
|
||||
return dict(sorted(languages.items(), key=lambda x: x[1]))
|
||||
def get_languages(self):
|
||||
languages = {}
|
||||
if config.app["translator"].get("engine") == "LibreTranslate":
|
||||
translator = libre_translate.CustomLibreTranslateAPI(config.app["translator"]["lt_api_url"], config.app["translator"]["lt_api_key"])
|
||||
languages = {l.get("code"): l.get("name") for l in translator.languages()}
|
||||
elif config.app["translator"]["engine"] == "DeepL" and config.app["translator"]["deepl_api_key"] != "":
|
||||
languages = {language.code: language.name for language in deep_l.languages()}
|
||||
return dict(sorted(languages.items(), key=lambda x: x[1]))
|
||||
|
@@ -16,23 +16,26 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
from . import translator
|
||||
import wx
|
||||
from pubsub import pub
|
||||
from wxUI.dialogs import baseDialog
|
||||
|
||||
class translateDialog(baseDialog.BaseWXDialog):
|
||||
def __init__(self):
|
||||
languages = []
|
||||
language_dict = translator.available_languages()
|
||||
for k in language_dict:
|
||||
languages.append(language_dict[k])
|
||||
super(translateDialog, self).__init__(None, -1, title=_(u"Translate message"))
|
||||
self.engines = ["LibreTranslate", "DeepL"]
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
staticEngine = wx.StaticText(panel, -1, _(u"Translation engine"))
|
||||
self.engine_select = wx.ComboBox(panel, -1, choices=self.engines, style=wx.CB_READONLY)
|
||||
self.engine_select.Bind(wx.EVT_COMBOBOX, lambda event: pub.sendMessage("translator.engine_changed", engine=self.engine_select.GetValue()))
|
||||
staticDest = wx.StaticText(panel, -1, _(u"Target language"))
|
||||
self.dest_lang = wx.ComboBox(panel, -1, choices=languages, style = wx.CB_READONLY)
|
||||
self.dest_lang = wx.ComboBox(panel, -1, style = wx.CB_READONLY)
|
||||
self.dest_lang.SetFocus()
|
||||
self.dest_lang.SetSelection(0)
|
||||
engineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
engineSizer.Add(staticEngine)
|
||||
engineSizer.Add(self.engine_select)
|
||||
listSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listSizer.Add(staticDest)
|
||||
listSizer.Add(self.dest_lang)
|
||||
@@ -40,6 +43,14 @@ class translateDialog(baseDialog.BaseWXDialog):
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
self.SetEscapeId(wx.ID_CANCEL)
|
||||
sizer.Add(engineSizer, 0, wx.EXPAND | wx.ALL, 5)
|
||||
sizer.Add(listSizer, 0, wx.EXPAND | wx.ALL, 5)
|
||||
sizer.Add(ok, 0, wx.ALIGN_CENTER | wx.ALL, 5)
|
||||
sizer.Add(cancel, 0, wx.ALIGN_CENTER | wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
|
||||
def set_languages(self, languages):
|
||||
wx.CallAfter(self.dest_lang.SetItems, languages)
|
||||
|
||||
def get(self, control):
|
||||
return getattr(self, control).GetSelection()
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import httpcore
|
||||
httpcore.SyncHTTPTransport = httpcore.AsyncHTTPProxy
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
|
@@ -3,6 +3,7 @@ access_token = string(default="")
|
||||
instance = string(default="")
|
||||
user_name = string(default="")
|
||||
ignored_clients = list(default=list())
|
||||
type = string(default="mastodon")
|
||||
|
||||
[general]
|
||||
relative_times = boolean(default=True)
|
||||
@@ -29,6 +30,7 @@ indicate_img = boolean(default=True)
|
||||
[other_buffers]
|
||||
timelines = list(default=list())
|
||||
searches = list(default=list())
|
||||
communities = list(default=list())
|
||||
lists = list(default=list())
|
||||
followers_timelines = list(default=list())
|
||||
following_timelines = list(default=list())
|
||||
|
@@ -1,44 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from future import standard_library
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
import winreg
|
||||
import os
|
||||
import sys
|
||||
from platform_utils import paths
|
||||
|
||||
RUN_REGKEY = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
|
||||
|
||||
def is_installed(app_subkey):
|
||||
"""Checks if the currently running copy is installed or portable variant. Requires the name of the application subkey found under the uninstall section in Windows registry."""
|
||||
|
||||
try:
|
||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s" % app_subkey)
|
||||
inst_dir = winreg.QueryValueEx(key,"InstallLocation")[0]
|
||||
except WindowsError:
|
||||
return False
|
||||
winreg.CloseKey(key)
|
||||
try:
|
||||
return os.stat(inst_dir) == os.stat(paths.app_path())
|
||||
except WindowsError:
|
||||
return False
|
||||
|
||||
def getAutoStart(app_name):
|
||||
"""Queries if the automatic startup should be set for the application or not, depending on it's current state."""
|
||||
|
||||
try:
|
||||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY)
|
||||
val = winreg.QueryValueEx(key, str(app_name))[0]
|
||||
return os.stat(val) == os.stat(sys.argv[0])
|
||||
except (WindowsError, OSError):
|
||||
return False
|
||||
|
||||
def setAutoStart(app_name, enable=True):
|
||||
"""Configures automatic startup for the application, if the enable argument is set to True. If set to False, deletes the application AutoStart value."""
|
||||
if getAutoStart(app_name) == enable:
|
||||
return
|
||||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, winreg.KEY_WRITE)
|
||||
if enable:
|
||||
winreg.SetValueEx(key, str(app_name), None, winreg.REG_SZ, paths.get_executable())
|
||||
else:
|
||||
winreg.DeleteValue(key, str(app_name))
|
@@ -14,6 +14,7 @@ import application
|
||||
from pubsub import pub
|
||||
from controller import settings
|
||||
from sessions.mastodon import session as MastodonSession
|
||||
from sessions.gotosocial import session as GotosocialSession
|
||||
from . import manager
|
||||
from . import wxUI as view
|
||||
|
||||
@@ -68,7 +69,7 @@ class sessionManagerController(object):
|
||||
name = _("{account_name}@{instance} (Mastodon)").format(account_name=config_test["mastodon"]["user_name"], instance=config_test["mastodon"]["instance"].replace("https://", ""))
|
||||
if config_test["mastodon"]["instance"] != "" and config_test["mastodon"]["access_token"] != "":
|
||||
sessionsList.append(name)
|
||||
self.sessions.append(dict(type="mastodon", id=i))
|
||||
self.sessions.append(dict(type=config_test["mastodon"].get("type", "mastodon"), id=i))
|
||||
else:
|
||||
try:
|
||||
log.debug("Deleting session %s" % (i,))
|
||||
@@ -94,6 +95,8 @@ class sessionManagerController(object):
|
||||
# Create the session object based in session type.
|
||||
if i.get("type") == "mastodon":
|
||||
s = MastodonSession.Session(i.get("id"))
|
||||
elif i.get("type") == "gotosocial":
|
||||
s = GotosocialSession.Session(i.get("id"))
|
||||
s.get_configuration()
|
||||
if i.get("id") not in config.app["sessions"]["ignored_sessions"]:
|
||||
try:
|
||||
@@ -116,7 +119,7 @@ class sessionManagerController(object):
|
||||
s = MastodonSession.Session(location)
|
||||
result = s.authorise()
|
||||
if result == True:
|
||||
self.sessions.append(dict(id=location, type=type))
|
||||
self.sessions.append(dict(id=location, type=s.settings["mastodon"].get("type")))
|
||||
self.view.add_new_session_to_list()
|
||||
|
||||
def remove_account(self, index):
|
||||
|
1
src/sessions/gotosocial/__init__.py
Normal file
1
src/sessions/gotosocial/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
13
src/sessions/gotosocial/session.py
Normal file
13
src/sessions/gotosocial/session.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from sessions.mastodon import session
|
||||
|
||||
class Session(session.Session):
|
||||
# disable version check so Mastodon.py won't throw exceptions.
|
||||
version_check_mode = "none"
|
||||
name = "GoToSocial"
|
||||
|
||||
def get_lists(self):
|
||||
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
|
||||
self.db["lists"] = []
|
||||
|
||||
def get_muted_users(self):
|
||||
self.db["muted_users"] = []
|
@@ -22,6 +22,8 @@ log = logging.getLogger("sessions.mastodonSession")
|
||||
MASTODON_VERSION = "4.0.1"
|
||||
|
||||
class Session(base.baseSession):
|
||||
version_check_mode = "created"
|
||||
name = "Mastodon"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Session, self).__init__(*args, **kwargs)
|
||||
@@ -32,6 +34,7 @@ class Session(base.baseSession):
|
||||
self.char_limit = 500
|
||||
self.post_visibility = "public"
|
||||
self.expand_spoilers = False
|
||||
self.software = "mastodon"
|
||||
pub.subscribe(self.on_status, "mastodon.status_received")
|
||||
pub.subscribe(self.on_status_updated, "mastodon.status_updated")
|
||||
pub.subscribe(self.on_notification, "mastodon.notification_received")
|
||||
@@ -40,7 +43,7 @@ class Session(base.baseSession):
|
||||
if self.settings["mastodon"]["access_token"] != None and self.settings["mastodon"]["instance"] != None:
|
||||
try:
|
||||
log.debug("Logging in to Mastodon instance {}...".format(self.settings["mastodon"]["instance"]))
|
||||
self.api = mastodon.Mastodon(access_token=self.settings["mastodon"]["access_token"], api_base_url=self.settings["mastodon"]["instance"], mastodon_version=MASTODON_VERSION, user_agent="TWBlue/{}".format(application.version))
|
||||
self.api = mastodon.Mastodon(access_token=self.settings["mastodon"]["access_token"], api_base_url=self.settings["mastodon"]["instance"], mastodon_version=MASTODON_VERSION, user_agent="TWBlue/{}".format(application.version), version_check_mode=self.version_check_mode)
|
||||
if verify_credentials == True:
|
||||
credentials = self.api.account_verify_credentials()
|
||||
self.db["user_name"] = credentials["username"]
|
||||
@@ -50,7 +53,7 @@ class Session(base.baseSession):
|
||||
log.debug("Logged.")
|
||||
self.counter = 0
|
||||
except MastodonError:
|
||||
log.exception("The login attempt failed.")
|
||||
log.exception(f"The login attempt failed on instance {self.settings['mastodon']['instance']}.")
|
||||
self.logged = False
|
||||
else:
|
||||
self.logged = False
|
||||
@@ -67,7 +70,7 @@ class Session(base.baseSession):
|
||||
return
|
||||
try:
|
||||
client_id, client_secret = mastodon.Mastodon.create_app("TWBlue", api_base_url=authorisation_dialog.GetValue(), website="https://twblue.es")
|
||||
temporary_api = mastodon.Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=instance, mastodon_version=MASTODON_VERSION, user_agent="TWBlue/{}".format(application.version))
|
||||
temporary_api = mastodon.Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=instance, mastodon_version=MASTODON_VERSION, user_agent="TWBlue/{}".format(application.version), version_check_mode="none") # disable version check so we can handle more platforms than Mastodon.
|
||||
auth_url = temporary_api.auth_request_url()
|
||||
except MastodonError:
|
||||
dlg = wx.MessageDialog(None, _("We could not connect to your mastodon instance. Please verify that the domain exists and the instance is accessible via a web browser."), _("Instance error"), wx.ICON_ERROR)
|
||||
@@ -90,6 +93,13 @@ class Session(base.baseSession):
|
||||
return
|
||||
self.create_session_folder()
|
||||
self.get_configuration()
|
||||
# handle when the instance is GoTosocial.
|
||||
# this might be extended for other activity pub software later on.
|
||||
nodeinfo = temporary_api.instance_nodeinfo()
|
||||
if nodeinfo.software.get("name") == "gotosocial":
|
||||
self.settings["mastodon"]["type"] = nodeinfo.software.get("name")
|
||||
# GoToSocial doesn't support certain buffers so we redefine all of them here.
|
||||
self.settings["general"]["buffer_order"] = ['home', 'local', 'mentions', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'notifications']
|
||||
self.settings["mastodon"]["access_token"] = access_token
|
||||
self.settings["mastodon"]["instance"] = instance
|
||||
self.settings.write()
|
||||
@@ -117,11 +127,20 @@ class Session(base.baseSession):
|
||||
|
||||
def get_lists(self):
|
||||
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
|
||||
if self.software == "gotosocial":
|
||||
self.db["lists"] = []
|
||||
return
|
||||
self.db["lists"] = self.api.lists()
|
||||
|
||||
def get_muted_users(self):
|
||||
### ToDo: Use a function to retrieve all muted users.
|
||||
self.db["muted_users"] = self.api.mutes()
|
||||
if self.software == "gotosocial":
|
||||
self.db["muted_users"] = []
|
||||
return
|
||||
try:
|
||||
self.db["muted_users"] = self.api.mutes()
|
||||
except MastodonNotFoundError:
|
||||
self.db["muted_users"] = []
|
||||
|
||||
def order_buffer(self, name, data, ignore_older=False):
|
||||
num = 0
|
||||
@@ -223,12 +242,14 @@ class Session(base.baseSession):
|
||||
instance = self.settings["mastodon"]["instance"]
|
||||
instance = instance.replace("https://", "")
|
||||
user = self.settings["mastodon"]["user_name"]
|
||||
return "Mastodon: {}@{}".format(user, instance)
|
||||
return "{}@{} ({})".format(user, instance, self.name)
|
||||
|
||||
def start_streaming(self):
|
||||
if self.settings["general"]["disable_streaming"]:
|
||||
log.info("Streaming is disabled for session {}. Skipping...".format(self.get_name()))
|
||||
return
|
||||
if self.software == "gotosocial":
|
||||
return
|
||||
listener = streaming.StreamListener(session_name=self.get_name(), user_id=self.db["user_id"])
|
||||
try:
|
||||
stream_healthy = self.api.stream_healthy()
|
||||
|
@@ -29,8 +29,8 @@ def donation():
|
||||
dlg = wx.MessageDialog(None, _(u"If you like {0} we need your help to keep it going. Help us by donating to the project. This will help us pay for the server, the domain and some other things to ensure that {0} will be actively maintained. Your donation will give us the means to continue the development of {0}, and to keep {0} free. Would you like to donate now?").format(application.name), _(u"We need your help"), wx.ICON_QUESTION|wx.YES_NO)
|
||||
return dlg.ShowModal()
|
||||
|
||||
def changed_keymap():
|
||||
return wx.MessageDialog(None, _(u"TWBlue has detected that you're running windows 10 and has changed the default keymap to the Windows 10 keymap. It means that some keyboard shorcuts could be different. Please check the keystroke editor by pressing Alt+Win+K to see all available keystrokes for this keymap."), _(u"Information"), wx.OK).ShowModal()
|
||||
def changed_keymap(system, keystroke_editor_shortcut):
|
||||
return wx.MessageDialog(None, _(f"TWBlue has detected that you're running {system} and has changed the default keymap to the {system} keymap. It means that some keyboard shorcuts could be different. Please check the keystroke editor by pressing {keystroke_editor_shortcut} to see all available keystrokes for this keymap."), _(u"Information"), wx.OK).ShowModal()
|
||||
|
||||
def invalid_configuration():
|
||||
return wx.MessageDialog(None, _("The configuration file is invalid."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||
@@ -44,3 +44,6 @@ def cant_update_source() -> wx.MessageDialog:
|
||||
"""
|
||||
dlg = wx.MessageDialog(None, _("Sorry, you can't update while running {} from source.").format(application.name), _("Error"), wx.OK)
|
||||
return dlg.ShowModal()
|
||||
|
||||
def invalid_instance():
|
||||
return wx.MessageDialog(None, _("the provided instance is invalid. Please try again."), _("Invalid instance"), wx.ICON_ERROR).ShowModal()
|
@@ -1,4 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
|
||||
class BaseWXDialog(wx.Dialog):
|
||||
|
@@ -19,9 +19,7 @@ class general(wx.Panel, baseDialog.BaseWXDialog):
|
||||
langBox.Add(language, 0, wx.ALL, 5)
|
||||
langBox.Add(self.language, 0, wx.ALL, 5)
|
||||
sizer.Add(langBox, 0, wx.ALL, 5)
|
||||
self.autostart = wx.CheckBox(self, -1, _(u"Run {0} at Windows startup").format(application.name,))
|
||||
self.ask_at_exit = wx.CheckBox(self, -1, _(U"ask before exiting {0}").format(application.name,))
|
||||
sizer.Add(self.autostart, 0, wx.ALL, 5)
|
||||
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
|
||||
self.no_streaming = wx.CheckBox(self, -1, _(U"Disable Streaming functions"))
|
||||
sizer.Add(self.no_streaming, 0, wx.ALL, 5)
|
||||
@@ -42,10 +40,8 @@ class general(wx.Panel, baseDialog.BaseWXDialog):
|
||||
sizer.Add(self.disable_sapi5, 0, wx.ALL, 5)
|
||||
self.hide_gui = wx.CheckBox(self, -1, _(u"Hide GUI on launch"))
|
||||
sizer.Add(self.hide_gui, 0, wx.ALL, 5)
|
||||
self.handle_longtweets = wx.CheckBox(self, wx.ID_ANY, _(u"Use Codeofdusk's longtweet handlers (may decrease client performance)"))
|
||||
sizer.Add(self.handle_longtweets, 0, wx.ALL, 5)
|
||||
self.remember_mention_and_longtweet = wx.CheckBox(self, -1, _(u"Remember state for mention all and long tweet"))
|
||||
sizer.Add(self.remember_mention_and_longtweet, 0, wx.ALL, 5)
|
||||
self.read_long_posts_in_gui = wx.CheckBox(self, wx.ID_ANY, _("Read long posts in GUI"))
|
||||
sizer.Add(self.read_long_posts_in_gui, 0, wx.ALL, 5)
|
||||
kmbox = wx.BoxSizer(wx.VERTICAL)
|
||||
km_label = wx.StaticText(self, -1, _(u"Keymap"))
|
||||
self.km = wx.ComboBox(self, -1, choices=keymaps, style=wx.CB_READONLY)
|
||||
@@ -201,6 +197,30 @@ class other_buffers(wx.Panel):
|
||||
buffers_list.append(self.buffers.get_text_column(i, 0))
|
||||
return buffers_list
|
||||
|
||||
class TranslatorPanel(wx.Panel, baseDialog.BaseWXDialog):
|
||||
def __init__(self, parent):
|
||||
super(TranslatorPanel, self).__init__(parent)
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
lbl_libre_url = wx.StaticText(self, wx.ID_ANY, _(u"LibreTranslate API URL: "))
|
||||
self.libre_api_url = wx.TextCtrl(self, wx.ID_ANY)
|
||||
libreUrlBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
libreUrlBox.Add(lbl_libre_url, 0, wx.ALL, 5)
|
||||
libreUrlBox.Add(self.libre_api_url, 1, wx.ALL | wx.EXPAND, 5)
|
||||
sizer.Add(libreUrlBox, 0, wx.ALL | wx.EXPAND, 5)
|
||||
lbl_libre_api_key = wx.StaticText(self, wx.ID_ANY, _(u"LibreTranslate API Key (optional): "))
|
||||
self.libre_api_key = wx.TextCtrl(self, wx.ID_ANY)
|
||||
libreApiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
libreApiKeyBox.Add(lbl_libre_api_key, 0, wx.ALL, 5)
|
||||
libreApiKeyBox.Add(self.libre_api_key, 1, wx.ALL | wx.EXPAND, 5)
|
||||
sizer.Add(libreApiKeyBox, 0, wx.ALL | wx.EXPAND, 5)
|
||||
lbl_deepL_api_key = wx.StaticText(self, wx.ID_ANY, _(u"DeepL API Key: "))
|
||||
self.deepL_api_key = wx.TextCtrl(self, wx.ID_ANY)
|
||||
deepLApiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
deepLApiKeyBox.Add(lbl_deepL_api_key, 0, wx.ALL, 5)
|
||||
deepLApiKeyBox.Add(self.deepL_api_key, 1, wx.ALL | wx.EXPAND, 5)
|
||||
sizer.Add(deepLApiKeyBox, 0, wx.ALL | wx.EXPAND, 5)
|
||||
self.SetSizer(sizer)
|
||||
class configurationDialog(baseDialog.BaseWXDialog):
|
||||
def set_title(self, title):
|
||||
self.SetTitle(title)
|
||||
@@ -221,6 +241,10 @@ class configurationDialog(baseDialog.BaseWXDialog):
|
||||
self.proxy = proxy(self.notebook, proxyTypes)
|
||||
self.notebook.AddPage(self.proxy, _(u"Proxy"))
|
||||
|
||||
def create_translator_panel(self):
|
||||
self.translator_panel= TranslatorPanel(self.notebook)
|
||||
self.notebook.AddPage(self.translator_panel, _("Translation services"))
|
||||
|
||||
def realize(self):
|
||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
38
src/wxUI/dialogs/mastodon/communityTimeline.py
Normal file
38
src/wxUI/dialogs/mastodon/communityTimeline.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
|
||||
class CommunityTimeline(wx.Dialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CommunityTimeline, self).__init__(parent=None, *args, **kwargs)
|
||||
panel = wx.Panel(self)
|
||||
communitySizer = wx.BoxSizer()
|
||||
self.SetTitle(_("Create community timeline"))
|
||||
communityLabel = wx.StaticText(panel, -1, _("Community URL"))
|
||||
self.url = wx.TextCtrl(panel, -1)
|
||||
self.url.SetFocus()
|
||||
communitySizer.Add(communityLabel, 0, wx.ALL, 5)
|
||||
communitySizer.Add(self.url, 0, wx.ALL, 5)
|
||||
actionSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label2 = wx.StaticText(panel, -1, _(u"Buffer type"))
|
||||
self.local= wx.RadioButton(panel, -1, _("Local timeline"), style=wx.RB_GROUP)
|
||||
self.federated= wx.RadioButton(panel, -1, _("Federated Timeline"))
|
||||
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
hSizer.Add(label2, 0, wx.ALL, 5)
|
||||
actionSizer.Add(self.local, 0, wx.ALL, 5)
|
||||
actionSizer.Add(self.federated, 0, wx.ALL, 5)
|
||||
hSizer.Add(actionSizer, 0, wx.ALL, 5)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
ok = wx.Button(panel, wx.ID_OK, _(u"&OK"))
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"&Close"))
|
||||
btnsizer = wx.BoxSizer()
|
||||
btnsizer.Add(ok)
|
||||
btnsizer.Add(cancel)
|
||||
sizer.Add(communitySizer)
|
||||
sizer.Add(hSizer, 0, wx.ALL, 5)
|
||||
sizer.Add(btnsizer)
|
||||
panel.SetSizer(sizer)
|
||||
|
||||
def get_action(self):
|
||||
if self.local.GetValue() == True: return "local"
|
||||
elif self.federated.GetValue() == True: return "federated"
|
@@ -1,7 +0,0 @@
|
||||
ffmpeg -hwaccel qsv -c:v h264_qsv -i "$1" -map 0 -c copy -c:v hevc_qsv -preset slow -global_quality 22 -look_ahead 1 "final/${1%.*}.mkv"
|
||||
|
||||
#!/bin/bash
|
||||
for i in *.wmv;
|
||||
do
|
||||
ffmpeg -i "$i" -c:v libx264 -c:a aac -vf scale=640:480 -crf 20 "final/$i.mp4"
|
||||
done
|
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
|
||||
class Post(wx.Dialog):
|
||||
@@ -164,80 +165,84 @@ class Post(wx.Dialog):
|
||||
def unable_to_attach_poll(self, *args, **kwargs):
|
||||
return wx.MessageDialog(self, _("You can add a poll or media files. In order to add your poll, please remove other attachments first."), _("Error adding poll"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
import wx
|
||||
|
||||
class viewPost(wx.Dialog):
|
||||
def set_title(self, lenght):
|
||||
self.SetTitle(_("Post - %i characters ") % (lenght,))
|
||||
def set_title(self, length):
|
||||
self.SetTitle(_("Post - %i characters ") % length)
|
||||
|
||||
def __init__(self, text="", boosts_count=0, favs_count=0, source="", date="", privacy="", *args, **kwargs):
|
||||
super(viewPost, self).__init__(parent=None, id=wx.ID_ANY, size=(850,850))
|
||||
super(viewPost, self).__init__(parent=None, id=wx.ID_ANY, size=(850, 850))
|
||||
self.init_ui(text, boosts_count, favs_count, source, date, privacy)
|
||||
|
||||
def init_ui(self, text, boosts_count, favs_count, source, date, privacy):
|
||||
panel = wx.Panel(self)
|
||||
label = wx.StaticText(panel, -1, _("Post"))
|
||||
self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||
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)
|
||||
label2 = wx.StaticText(panel, -1, _("Image description"))
|
||||
self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180))
|
||||
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
main_sizer.Add(self.create_text_section(panel, text), 1, wx.EXPAND | wx.ALL, 5)
|
||||
main_sizer.Add(self.create_image_description_section(panel), 1, wx.EXPAND | wx.ALL, 5)
|
||||
main_sizer.Add(self.create_info_section(panel, privacy, boosts_count, favs_count, source, date), 0, wx.EXPAND | wx.ALL, 5)
|
||||
main_sizer.Add(self.create_buttons_section(panel), 0, wx.ALIGN_RIGHT | wx.ALL, 5)
|
||||
panel.SetSizer(main_sizer)
|
||||
self.SetClientSize(main_sizer.CalcMin())
|
||||
|
||||
def create_text_section(self, panel, text):
|
||||
sizer = wx.StaticBoxSizer(wx.StaticBox(panel, wx.ID_ANY, _("Post")), wx.VERTICAL)
|
||||
self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY | wx.TE_MULTILINE)
|
||||
sizer.Add(self.text, 1, wx.EXPAND | wx.ALL, 5)
|
||||
return sizer
|
||||
|
||||
def create_image_description_section(self, panel):
|
||||
sizer = wx.StaticBoxSizer(wx.StaticBox(panel, wx.ID_ANY, _("Image description")), wx.VERTICAL)
|
||||
self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY | wx.TE_MULTILINE)
|
||||
self.image_description.Enable(False)
|
||||
iBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
iBox.Add(label2, 0, wx.ALL, 5)
|
||||
iBox.Add(self.image_description, 1, wx.EXPAND, 5)
|
||||
mainBox.Add(iBox, 0, wx.ALL, 5)
|
||||
privacyLabel = wx.StaticText(panel, -1, _("Privacy"))
|
||||
privacy = wx.TextCtrl(panel, -1, privacy, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
privacyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
privacyBox.Add(privacyLabel, 0, wx.ALL, 5)
|
||||
privacyBox.Add(privacy, 0, wx.ALL, 5)
|
||||
boostsCountLabel = wx.StaticText(panel, -1, _(u"Boosts: "))
|
||||
boostsCount = wx.TextCtrl(panel, -1, str(boosts_count), size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
boostBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
boostBox.Add(boostsCountLabel, 0, wx.ALL, 5)
|
||||
boostBox.Add(boostsCount, 0, wx.ALL, 5)
|
||||
favsCountLabel = wx.StaticText(panel, -1, _("Favorites: "))
|
||||
favsCount = wx.TextCtrl(panel, -1, str(favs_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)
|
||||
sourceLabel = wx.StaticText(panel, -1, _("Source: "))
|
||||
source = wx.TextCtrl(panel, -1, source, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
sourceBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sourceBox.Add(sourceLabel, 0, wx.ALL, 5)
|
||||
sourceBox.Add(source, 0, wx.ALL, 5)
|
||||
dateLabel = wx.StaticText(panel, -1, _(u"Date: "))
|
||||
date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
dateBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
dateBox.Add(dateLabel, 0, wx.ALL, 5)
|
||||
dateBox.Add(date, 0, wx.ALL, 5)
|
||||
infoBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
infoBox.Add(privacyBox, 0, wx.ALL, 5)
|
||||
infoBox.Add(boostBox, 0, wx.ALL, 5)
|
||||
infoBox.Add(favsBox, 0, wx.ALL, 5)
|
||||
infoBox.Add(sourceBox, 0, wx.ALL, 5)
|
||||
mainBox.Add(infoBox, 0, wx.ALL, 5)
|
||||
mainBox.Add(dateBox, 0, wx.ALL, 5)
|
||||
sizer.Add(self.image_description, 1, wx.EXPAND | wx.ALL, 5)
|
||||
return sizer
|
||||
|
||||
def create_info_section(self, panel, privacy, boosts_count, favs_count, source, date):
|
||||
sizer = wx.StaticBoxSizer(wx.StaticBox(panel, wx.ID_ANY, _("Information")), wx.VERTICAL)
|
||||
flex_sizer = wx.FlexGridSizer(cols=3, hgap=10, vgap=10)
|
||||
flex_sizer.AddGrowableCol(1)
|
||||
flex_sizer.Add(wx.StaticText(panel, -1, _("Privacy")), 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
flex_sizer.Add(wx.TextCtrl(panel, -1, privacy, style=wx.TE_READONLY | wx.TE_MULTILINE), 1, wx.EXPAND)
|
||||
flex_sizer.Add(self.create_boosts_section(panel, boosts_count), 1, wx.EXPAND | wx.ALL, 5)
|
||||
flex_sizer.Add(self.create_favorites_section(panel, favs_count), 1, wx.EXPAND | wx.ALL, 5)
|
||||
flex_sizer.Add(wx.StaticText(panel, -1, _("Source")), 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
flex_sizer.Add(wx.TextCtrl(panel, -1, source, style=wx.TE_READONLY | wx.TE_MULTILINE), 1, wx.EXPAND)
|
||||
flex_sizer.Add(wx.StaticText(panel, -1, _("Date")), 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
flex_sizer.Add(wx.TextCtrl(panel, -1, date, style=wx.TE_READONLY | wx.TE_MULTILINE), 1, wx.EXPAND)
|
||||
sizer.Add(flex_sizer, 1, wx.EXPAND | wx.ALL, 5)
|
||||
return sizer
|
||||
|
||||
def create_boosts_section(self, panel, boosts_count):
|
||||
sizer = wx.StaticBoxSizer(wx.StaticBox(panel, wx.ID_ANY, _("Boosts")), wx.VERTICAL)
|
||||
self.boosts_button = wx.Button(panel, -1, str(boosts_count))
|
||||
self.boosts_button.SetToolTip(_("View users who boosted this post"))
|
||||
sizer.Add(self.boosts_button, 1, wx.EXPAND | wx.ALL, 5)
|
||||
return sizer
|
||||
|
||||
def create_favorites_section(self, panel, favs_count):
|
||||
sizer = wx.StaticBoxSizer(wx.StaticBox(panel, wx.ID_ANY, _("Favorites")), wx.VERTICAL)
|
||||
self.favorites_button = wx.Button(panel, -1, str(favs_count))
|
||||
self.favorites_button.SetToolTip(_("View users who favorited this post"))
|
||||
sizer.Add(self.favorites_button, 1, wx.EXPAND | wx.ALL, 5)
|
||||
return sizer
|
||||
|
||||
def create_buttons_section(self, panel):
|
||||
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.mute = wx.Button(panel, wx.ID_ANY, _("Mute conversation"))
|
||||
self.mute.Enable(False)
|
||||
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
|
||||
self.share.Enable(False)
|
||||
self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize)
|
||||
self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize)
|
||||
cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize)
|
||||
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
|
||||
self.translateButton = wx.Button(panel, wx.ID_ANY, _("&Translate..."))
|
||||
cancelButton = wx.Button(panel, wx.ID_CANCEL, _("C&lose"))
|
||||
cancelButton.SetDefault()
|
||||
buttonsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
buttonsBox.Add(self.share, 0, wx.ALL, 5)
|
||||
buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5)
|
||||
buttonsBox.Add(self.translateButton, 0, wx.ALL, 5)
|
||||
buttonsBox.Add(cancelButton, 0, wx.ALL, 5)
|
||||
mainBox.Add(buttonsBox, 0, wx.ALL, 5)
|
||||
selectId = wx.ID_ANY
|
||||
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())
|
||||
sizer.Add(self.mute, 0, wx.ALL, 5)
|
||||
sizer.Add(self.share, 0, wx.ALL, 5)
|
||||
sizer.Add(self.spellcheck, 0, wx.ALL, 5)
|
||||
sizer.Add(self.translateButton, 0, wx.ALL, 5)
|
||||
sizer.Add(cancelButton, 0, wx.ALL, 5)
|
||||
return sizer
|
||||
|
||||
def set_text(self, text):
|
||||
self.text.ChangeValue(text)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import requests
|
||||
from io import BytesIO
|
||||
@@ -39,14 +40,14 @@ class UpdateProfileDialog(wx.Dialog):
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
# create widgets
|
||||
display_name_label = wx.StaticText(panel, label=_("Display Name"))
|
||||
display_name_label = wx.StaticText(panel, label=_("&Display Name"))
|
||||
self.display_name = wx.TextCtrl(panel, value=display_name, style= wx.TE_PROCESS_ENTER, size=(200, 30))
|
||||
name_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
name_sizer.Add(display_name_label, wx.SizerFlags().Center())
|
||||
name_sizer.Add(self.display_name, wx.SizerFlags().Center())
|
||||
sizer.Add(name_sizer, wx.CENTER)
|
||||
|
||||
bio_label = wx.StaticText(panel, label=_("Bio"))
|
||||
bio_label = wx.StaticText(panel, label=_("&Bio"))
|
||||
self.bio = wx.TextCtrl(panel, value=note, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, size=(400, 60))
|
||||
bio_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
bio_sizer.Add(bio_label, wx.SizerFlags().Center())
|
||||
@@ -67,7 +68,7 @@ class UpdateProfileDialog(wx.Dialog):
|
||||
self.header_image = wx.StaticBitmap(panel, bitmap=image.ConvertToBitmap())
|
||||
|
||||
self.header_image.AcceptsFocusFromKeyboard = return_true
|
||||
self.change_header = wx.Button(panel, label=_("Change header"))
|
||||
self.change_header = wx.Button(panel, label=_("Change &header"))
|
||||
header_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
header_sizer.Add(header_label, wx.SizerFlags().Center())
|
||||
header_sizer.Add(self.header_image, wx.SizerFlags().Center())
|
||||
@@ -88,7 +89,7 @@ class UpdateProfileDialog(wx.Dialog):
|
||||
self.avatar_image = wx.StaticBitmap(panel, bitmap=image.ConvertToBitmap())
|
||||
|
||||
self.avatar_image.AcceptsFocusFromKeyboard = return_true
|
||||
self.change_avatar = wx.Button(panel, label=_("Change avatar"))
|
||||
self.change_avatar = wx.Button(panel, label=_("Change &avatar"))
|
||||
avatar_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
avatar_sizer.Add(avatar_label, wx.SizerFlags().Center())
|
||||
avatar_sizer.Add(self.avatar_image, wx.SizerFlags().Center())
|
||||
@@ -98,7 +99,7 @@ class UpdateProfileDialog(wx.Dialog):
|
||||
self.fields = []
|
||||
for i in range(1, 5):
|
||||
field_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
field_label = wx.StaticText(panel, label=_("Field {}: Label").format(i))
|
||||
field_label = wx.StaticText(panel, label=_("Field &{}: Label").format(i))
|
||||
field_sizer.Add(field_label, wx.SizerFlags().Center().Border(wx.ALL, 5))
|
||||
|
||||
label_textctrl = wx.TextCtrl(panel, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, size=(200, 30))
|
||||
@@ -116,11 +117,11 @@ class UpdateProfileDialog(wx.Dialog):
|
||||
sizer.Add(field_sizer, 0, wx.CENTER)
|
||||
self.fields.append((label_textctrl, content_textctrl))
|
||||
|
||||
self.locked = wx.CheckBox(panel, label=_("Private account"))
|
||||
self.locked = wx.CheckBox(panel, label=_("&Private account"))
|
||||
self.locked.SetValue(locked)
|
||||
self.bot = wx.CheckBox(panel, label=_("Bot account"))
|
||||
self.bot = wx.CheckBox(panel, label=_("&Bot account"))
|
||||
self.bot.SetValue(bot)
|
||||
self.discoverable = wx.CheckBox(panel, label=_("Discoverable account"))
|
||||
self.discoverable = wx.CheckBox(panel, label=_("&Discoverable account"))
|
||||
self.discoverable.SetValue(discoverable)
|
||||
sizer.Add(self.locked, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
||||
sizer.Add(self.bot, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
||||
|
33
src/wxUI/dialogs/userList.py
Normal file
33
src/wxUI/dialogs/userList.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
|
||||
class UserListDialog(wx.Dialog):
|
||||
def __init__(self, parent=None, title="", users=[]):
|
||||
super(UserListDialog, self).__init__(parent=parent, title=title, size=(400, 300))
|
||||
self.users = users
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
panel = wx.Panel(self)
|
||||
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
title_text = wx.StaticText(panel, label=self.GetTitle())
|
||||
title_font = title_text.GetFont()
|
||||
title_font.PointSize += 2
|
||||
title_font = title_font.Bold()
|
||||
title_text.SetFont(title_font)
|
||||
main_sizer.Add(title_text, 0, wx.ALIGN_CENTER | wx.TOP, 10)
|
||||
user_list_box = wx.StaticBox(panel, wx.ID_ANY, "Users")
|
||||
user_list_sizer = wx.StaticBoxSizer(user_list_box, wx.VERTICAL)
|
||||
self.user_list = wx.ListBox(panel, wx.ID_ANY, choices=self.users, style=wx.LB_SINGLE)
|
||||
user_list_sizer.Add(self.user_list, 1, wx.EXPAND | wx.ALL, 10)
|
||||
main_sizer.Add(user_list_sizer, 1, wx.EXPAND | wx.ALL, 15)
|
||||
buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.actions_button = wx.Button(panel, wx.ID_ANY, "Actions")
|
||||
buttons_sizer.Add(self.actions_button, 0, wx.RIGHT, 10)
|
||||
self.details_button = wx.Button(panel, wx.ID_ANY, _("View profile"))
|
||||
buttons_sizer.Add(self.details_button, 0, wx.RIGHT, 10)
|
||||
close_button = wx.Button(panel, wx.ID_CANCEL, "Close")
|
||||
buttons_sizer.Add(close_button, 0)
|
||||
main_sizer.Add(buttons_sizer, 0, wx.ALIGN_CENTER | wx.BOTTOM, 15)
|
||||
panel.SetSizer(main_sizer)
|
||||
# self.SetSizerAndFit(main_sizer)
|
@@ -52,6 +52,7 @@ class mainFrame(wx.Frame):
|
||||
# buffer menu
|
||||
self.menubar_buffer = wx.Menu()
|
||||
self.update_buffer = self.menubar_buffer.Append(wx.ID_ANY, _(u"&Update buffer"))
|
||||
self.community_timeline = self.menubar_buffer.Append(wx.ID_ANY, _("Create community timeline"))
|
||||
self.trends = self.menubar_buffer.Append(wx.ID_ANY, _(u"New &trending topics buffer..."))
|
||||
self.filter = self.menubar_buffer.Append(wx.ID_ANY, _(u"Create a &filter"))
|
||||
self.manage_filters = self.menubar_buffer.Append(wx.ID_ANY, _(u"&Manage filters"))
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{"current_version": "2023.04.13",
|
||||
"description": "Stops support for Twitter session on Feb 9, updates to mastodon sessions. Please avoid using autoupdate as it will not work.",
|
||||
{"current_version": "2024.05.19",
|
||||
"description": "Initial support for GoToSocial instances, mute conversations, local and public timelines from remote instances, translate using LibreTranslate or DeepL and several bugfixes.",
|
||||
"date": "unknown",
|
||||
"downloads":
|
||||
{"Windows32": "https://twblue.es/pubs/twblue_x86.zip",
|
||||
"Windows64": "https://twblue.es/pubs/twblue_x64.zip"}
|
||||
{"Windows32": "https://github.com/MCV-Software/TWBlue/releases/download/v2024.05.19/TWBlue_portable_v2024.05.19.zip",
|
||||
"Windows64": "https://github.com/MCV-Software/TWBlue/releases/download/v2024.05.19/TWBlue_portable_v2024.05.19.zip"}
|
||||
}
|
Reference in New Issue
Block a user