Compare commits
99 commits
78c7e333c4
...
2c16d5773d
Author | SHA1 | Date | |
---|---|---|---|
|
2c16d5773d | ||
|
eb5e97acc7 | ||
|
8078db9abc | ||
|
74016ff224 | ||
|
f96ceb57a0 | ||
|
d90bbdab2e | ||
|
70012407b0 | ||
|
93d8c2a564 | ||
|
85545dd67c | ||
|
4e7a01517a | ||
|
b4947cccce | ||
|
879709d682 | ||
|
2bf9b249cc | ||
|
1feb099be0 | ||
|
4c8c511626 | ||
|
8f618a6a29 | ||
|
5119b5a161 | ||
|
de2d9d5eae | ||
|
ad0512d333 | ||
|
62a754a0ee | ||
|
1bc1dd25c5 | ||
|
58bf92b87a | ||
|
d0f409db80 | ||
|
f50604354c | ||
|
2f93667fc6 | ||
|
18e6ab96be | ||
|
4c331d32bd | ||
|
d8367a140a | ||
|
7a7874fc47 | ||
|
6ea2bba758 | ||
|
ceb6df5548 | ||
|
e9ee79bb46 | ||
|
0c686267b3 | ||
|
195890894a | ||
|
6a5287c0b8 | ||
|
05cb36a41f | ||
|
39a4978357 | ||
|
6ddacc7d27 | ||
|
08a1a9b872 | ||
|
493e044122 | ||
|
efff5f56ff | ||
|
f73c032b41 | ||
|
3396bcd0ef | ||
|
50a4c06e1c | ||
|
575b4b1923 | ||
|
72c2276f94 | ||
|
989b566090 | ||
|
abdac68586 | ||
|
35d6c0e9b4 | ||
|
3ff955be53 | ||
|
0e692eb511 | ||
|
8922e6f0ac | ||
|
2b3f211641 | ||
|
07f2a6409a | ||
|
364e0325db | ||
|
8983de5e9a | ||
|
5243810dba | ||
|
4726d91f66 | ||
|
618195bb8e | ||
|
09a5df8439 | ||
|
e1a320c837 | ||
|
c524900e69 | ||
|
b3f6232dd7 | ||
|
f3d3517160 | ||
|
ad1a358672 | ||
|
a7b8921695 | ||
|
b76e395892 | ||
|
b2a9fde2f5 | ||
|
9ef244d185 | ||
|
4c77060c40 | ||
|
a147346b99 | ||
|
4d7b636174 | ||
|
8658e6c618 | ||
|
1c848c8122 | ||
|
a85f537305 | ||
|
ab63876f31 | ||
|
2594ebaa12 | ||
|
bc63eac8cd | ||
|
36b568543e | ||
|
539cb641aa | ||
|
4768e0f3aa | ||
|
cf84de4070 | ||
|
f69d5cb9b4 | ||
|
6d3a0c0d4f | ||
|
dde8e2b3df | ||
|
3a85dfa712 | ||
|
6dc39569be | ||
|
6bc3220045 | ||
|
1c91b58342 | ||
|
a7713dba3c | ||
|
9dd1eeb15e | ||
|
b306d78c47 | ||
|
f64875c418 | ||
|
ae16134dce | ||
|
dc1d061103 | ||
|
5a6dcba2fd | ||
|
b03e877b3b | ||
|
648f3d6bb9 | ||
|
bcaa95660b |
|
@ -8,15 +8,7 @@ install:
|
|||
- choco install strawberryperl
|
||||
- copy %PYTHON%\python.exe %PYTHON%\python3.exe
|
||||
- SET PATH=%PYTHON%;C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
|
||||
- python3 -mpip install Pillow
|
||||
- python3 -mpip install flake8
|
||||
- python3 -mpip install flake8-import-order
|
||||
- python3 -mpip install py2exe
|
||||
- python3 -mpip install pycotap
|
||||
- python3 -mpip install pygame
|
||||
- python3 -mpip install pysol-cards
|
||||
- python3 -mpip install random2
|
||||
- python3 -mpip install six
|
||||
- python3 -mpip install Pillow attrs configobj flake8 flake8-import-order py2exe pycotap pygame pysol-cards random2 setuptools six
|
||||
- perl -v
|
||||
- copy C:\msys64\mingw64\bin\mingw32-make.exe C:\msys64\mingw64\bin\make.exe
|
||||
- SET PATH=C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
|
||||
|
@ -64,6 +56,6 @@ artifacts:
|
|||
- path: dist\PySolFC_*_setup.exe
|
||||
name: pysol_win_installer
|
||||
cache:
|
||||
- C:\_P5 -> .appveyor.yml
|
||||
- C:\strawberry -> .appveyor.yml
|
||||
# - C:\_P5 -> .appveyor.yml
|
||||
# - C:\strawberry -> .appveyor.yml
|
||||
shallow_clone: true
|
||||
|
|
12
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
*.pyc
|
||||
/.tidyall.d/
|
||||
/MANIFEST
|
||||
/_Inline/
|
||||
/build/*
|
||||
/data/cardsets/*
|
||||
/data/html/*
|
||||
|
@ -10,15 +11,16 @@
|
|||
/html-src/html/*
|
||||
/images
|
||||
/locale/*
|
||||
/po/de.po
|
||||
/po/it.po
|
||||
/po/pl.po
|
||||
/po/ru.po
|
||||
/tests/individually-importing/*.py
|
||||
/tests/unit-generated/*.py
|
||||
|
||||
# output of min card actions
|
||||
# mask out folder/archives
|
||||
PySolFC-Cardsets--Minimal-2.0/
|
||||
PySolFC-Cardsets--Minimal-2.0.*
|
||||
PySolFC-Cardsets-2.0/
|
||||
PySolFC-Cardsets--Minimal-2.0/
|
||||
PySolFC-Cardsets-2.0.*
|
||||
PySolFC-Cardsets-2.0/
|
||||
android/*.apk
|
||||
android/*.zip
|
||||
android/bin/keystore
|
||||
|
|
54
.travis.yml
|
@ -1,20 +1,4 @@
|
|||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- cpanminus
|
||||
- libperl-dev
|
||||
- make
|
||||
- perl
|
||||
- python-glade2
|
||||
- python-gnome2
|
||||
- python-gnome2-dev
|
||||
- python-gtk2
|
||||
- python-setuptools
|
||||
- python-tk
|
||||
- python3
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- python3-tk
|
||||
homebrew:
|
||||
update: true
|
||||
brewfile: true
|
||||
|
@ -32,32 +16,34 @@ deploy:
|
|||
repo: shlomif/PySolFC
|
||||
tags: true
|
||||
skip_cleanup: true
|
||||
dist: trusty
|
||||
dist: bionic
|
||||
before_install:
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then export CPPFLAGS="$CPPFLAGS -I$(brew --prefix openssl)/include" PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig:$PKG_CONFIG_PATH" LDFLAGS="$LDFLAGS -L$(brew --prefix openssl)/lib" ;fi
|
||||
- if test "$TRAVIS_OS_NAME" = "osx" ; then wget --content-disposition https://sourceforge.net/projects/pysolfc/files/PySolFC-Cardsets/minimal/PySolFC-Cardsets--Minimal-2.0.tar.xz/download && tar xJf PySolFC-Cardsets--Minimal-2.0.tar.xz && mv PySolFC-Cardsets--Minimal-2.0/cardset-* data ; fi
|
||||
install:
|
||||
- sudo cpanm --notest Capture::Tiny
|
||||
# Tests are failing for them sometimes
|
||||
- sudo cpanm --notest Capture::Tiny IPC::System::Simple
|
||||
- sudo cpanm Code::TidyAll::Plugin::Flake8 Perl::Tidy Test::Code::TidyAll Test::Differences Test::TrailingSpace
|
||||
- export PY_MODS='pycotap pysol-cards random2 six'
|
||||
- "`which python3` -m pip install --upgrade flake8 flake8-import-order $PY_MODS"
|
||||
- "sudo /usr/bin/python3 -m pip install --upgrade $PY_MODS || true"
|
||||
- "sudo `which python2` -m pip install --upgrade $PY_MODS"
|
||||
- which python
|
||||
- which python2
|
||||
language: python
|
||||
python: "3.6"
|
||||
- export PY_MODS='attrs configobj pycotap pysol-cards random2 setuptools six'
|
||||
- sudo pip3 install --upgrade $PY_MODS flake8 flake8-import-order
|
||||
- sudo pip2 install --upgrade $PY_MODS
|
||||
language: generic
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
before_install:
|
||||
- sudo apt-get install -y
|
||||
cpanminus
|
||||
python-glade2
|
||||
python-gnome2
|
||||
python-gnome2-dev
|
||||
python-gtk2
|
||||
python-pip
|
||||
python-setuptools
|
||||
python-tk
|
||||
python3-pip
|
||||
python3-setuptools
|
||||
python3-tk
|
||||
- os: osx
|
||||
language: generic
|
||||
env:
|
||||
- TOXENV=py35
|
||||
- PATH="/usr/local/opt/openssl/bin:$PATH:/usr/local/bin"
|
||||
- DYLD_LIBRARY_PATH="/usr/local/opt/curl/lib:/usr/local/opt/openssl/lib:${DYLD_LIBRARY_PATH}"
|
||||
- LDFLAGS="$LDFLAGS -L/usr/local/opt/openssl/lib"
|
||||
- CPPFLAGS="$CPPFLAGS -I/usr/local/opt/openssl/include"
|
||||
script:
|
||||
- export TIDYALL_DATA_DIR="$HOME/tidyall_d"
|
||||
- bash -x scripts/travis-ci-build
|
||||
|
|
|
@ -12,6 +12,7 @@ for general guidelines for contributing to open source.
|
|||
# How you can contribute
|
||||
|
||||
- Translate PySol to a human language you know.
|
||||
- Test the "master" branch version of the version control repository or other prereleases.
|
||||
- Try to reproduce [open issues](https://github.com/shlomif/PySolFC/issues)
|
||||
- Try to fix bugs.
|
||||
- Add new games.
|
||||
|
@ -20,6 +21,7 @@ for general guidelines for contributing to open source.
|
|||
- Add new features.
|
||||
- Contribute graphics
|
||||
- Improve the site
|
||||
- Package PySol for a new package repository or OS, or update existing packages.
|
||||
- Make a monetary donation.
|
||||
- [Star](https://help.github.com/articles/about-stars/) or [Watch](https://help.github.com/articles/watching-and-unwatching-repositories/) the repository on GitHub
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@ include pysol.py setup.py setup_osx.py setup.cfg MANIFEST.in Makefile
|
|||
include COPYING README.md AUTHORS.md README.android README.kivy
|
||||
include NEWS.asciidoc
|
||||
#recursive-include pysollib *.py
|
||||
include pysollib/*.py pysollib/macosx/*.py pysollib/configobj/*.py
|
||||
include pysollib/*.py pysollib/macosx/*.py
|
||||
include pysollib/winsystems/*.py
|
||||
include pysollib/tk/*.py pysollib/tile/*.py pysollib/pysolgtk/*.py
|
||||
include pysollib/game/*.py
|
||||
include pysollib/games/*.py pysollib/games/special/*.py
|
||||
include pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py
|
||||
include data/tcl/*.tcl
|
||||
|
@ -18,8 +19,8 @@ include data/pysolfc.glade
|
|||
graft data/themes
|
||||
recursive-exclude data/themes *.py
|
||||
include scripts/build.bat scripts/create_iss.py scripts/mahjongg_utils.py
|
||||
include scripts/pygettext.py scripts/all_games.py scripts/cardset_viewer.py
|
||||
include scripts/cardconv scripts/cardsetsgiftobmp
|
||||
include scripts/all_games.py scripts/cardset_viewer.py
|
||||
include scripts/cardconv
|
||||
include scripts/gen_individual_importing_tests.py
|
||||
include tests/individually-importing/PLACEHOLDER
|
||||
recursive-include tests/lib *.pm *.py
|
||||
|
@ -29,6 +30,8 @@ include tests/unit-generated/PLACEHOLDER
|
|||
include .tidyallrc
|
||||
include android/*.py
|
||||
include android/mk*
|
||||
include android/initsdk
|
||||
include android/initsupport
|
||||
include android/debian/*
|
||||
#graft data/plugins
|
||||
##
|
||||
|
|
83
Makefile
|
@ -1,82 +1,73 @@
|
|||
# Makefile for PySolFC
|
||||
|
||||
override LANG=C
|
||||
override PYSOL_DEBUG=1
|
||||
ifeq ($(OS),Windows_NT)
|
||||
path_sep = ;
|
||||
else
|
||||
path_sep = :
|
||||
endif
|
||||
export PYTHONPATH := $(PYTHONPATH)$(path_sep)$(CURDIR)
|
||||
|
||||
PYSOLLIB_FILES=pysollib/tk/*.py pysollib/tile/*.py pysollib/*.py \
|
||||
pysollib/games/*.py pysollib/games/special/*.py \
|
||||
pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py \
|
||||
pysollib/kivy/*.py
|
||||
|
||||
.PHONY : all install dist all_games_html rules pot mo
|
||||
.PHONY: all install dist rpm all_games_html rules pot mo pretest test runtest
|
||||
|
||||
all:
|
||||
@echo "No default target"
|
||||
|
||||
install:
|
||||
python setup.py install
|
||||
python3 setup.py install
|
||||
|
||||
dist: all_games_html rules mo
|
||||
python3 setup.py sdist
|
||||
|
||||
rpm: all_games_html rules mo
|
||||
python setup.py bdist_rpm
|
||||
python3 setup.py bdist_rpm
|
||||
|
||||
all_games_html:
|
||||
PYTHONPATH=`pwd` ./scripts/all_games.py html id doc/rules bare > docs/all_games.html
|
||||
DOCS_DIR = docs
|
||||
HTML_DIR = data/html
|
||||
ALL_GAMES_HTML_BASE = all_games.html
|
||||
ALL_GAMES_HTML = $(HTML_DIR)/$(ALL_GAMES_HTML_BASE)
|
||||
ALL_GAMES_HTML__FOR_WEBSITE = $(DOCS_DIR)/$(ALL_GAMES_HTML_BASE)
|
||||
all_games_html: $(ALL_GAMES_HTML)
|
||||
|
||||
$(ALL_GAMES_HTML) $(ALL_GAMES_HTML__FOR_WEBSITE): rules
|
||||
cd $(HTML_DIR) && $(CURDIR)/scripts/all_games.py html id rules > $(ALL_GAMES_HTML_BASE)
|
||||
./scripts/all_games.py html id doc/rules bare > $(ALL_GAMES_HTML__FOR_WEBSITE)
|
||||
|
||||
rules:
|
||||
export PYTHONPATH=`pwd`; (cd html-src && ./gen-html.py)
|
||||
cd html-src && ./gen-html.py
|
||||
cp -r html-src/images html-src/html
|
||||
rm -rf data/html
|
||||
mv html-src/html data
|
||||
|
||||
pot:
|
||||
PYTHONPATH=`pwd` ./scripts/all_games.py gettext > po/games.pot
|
||||
PYTHONPATH=`pwd` ./scripts/pygettext.py -k n_ --ngettext-keyword ungettext -o po/pysol-1.pot $(PYSOLLIB_FILES)
|
||||
xgettext -L C --keyword=N_ -o po/pysol-2.pot data/glade-translations
|
||||
msgcat po/pysol-1.pot po/pysol-2.pot > po/pysol.pot
|
||||
rm -f po/pysol-1.pot po/pysol-2.pot
|
||||
for lng in ru pl; do \
|
||||
mv -f po/$${lng}_pysol.po po/$${lng}_pysol.old.po; \
|
||||
msgmerge po/$${lng}_pysol.old.po po/pysol.pot > po/$${lng}_pysol.po; \
|
||||
rm -f po/$${lng}_pysol.old.po; \
|
||||
mv -f po/$${lng}_games.po po/$${lng}_games.old.po; \
|
||||
msgmerge po/$${lng}_games.old.po po/games.pot > po/$${lng}_games.po; \
|
||||
rm -f po/$${lng}_games.old.po; \
|
||||
./scripts/all_games.py gettext > po/games.pot
|
||||
xgettext --keyword=n_ --add-comments=TRANSLATORS: -o po/pysol.pot \
|
||||
pysollib/*.py pysollib/*/*.py pysollib/*/*/*.py data/pysolfc.glade
|
||||
set -e; \
|
||||
for lng in ru de pl it; do \
|
||||
msgmerge --update --quiet --backup=none po/$${lng}_pysol.po po/pysol.pot; \
|
||||
msgmerge --update --quiet --backup=none po/$${lng}_games.po po/games.pot; \
|
||||
done
|
||||
|
||||
mo:
|
||||
for loc in ru ru_RU de de_AT de_BE de_DE de_LU de_CH pl pl_PL it it_IT; do \
|
||||
test -d locale/$${loc}/LC_MESSAGES || mkdir -p locale/$${loc}/LC_MESSAGES; \
|
||||
done
|
||||
for lang in ru pl it; do \
|
||||
msgcat --use-first po/$${lang}_games.po po/$${lang}_pysol.po > po/$${lang}.po 2>/dev/null; \
|
||||
done
|
||||
set -e; \
|
||||
for lang in ru de pl it; do \
|
||||
msgfmt -o locale/$${lang}/LC_MESSAGES/pysol.mo po/$${lang}.po; \
|
||||
mkdir -p locale/$${lang}/LC_MESSAGES; \
|
||||
msgcat --use-first po/$${lang}_games.po po/$${lang}_pysol.po > po/$${lang}.po; \
|
||||
msgfmt --check -o locale/$${lang}/LC_MESSAGES/pysol.mo po/$${lang}.po; \
|
||||
done
|
||||
cp -f locale/ru/LC_MESSAGES/pysol.mo locale/ru_RU/LC_MESSAGES/pysol.mo
|
||||
for dir in de_AT de_BE de_DE de_LU de_CH; do \
|
||||
cp -f locale/de/LC_MESSAGES/pysol.mo locale/$${dir}/LC_MESSAGES/pysol.mo; \
|
||||
done
|
||||
cp -f locale/pl/LC_MESSAGES/pysol.mo locale/pl_PL/LC_MESSAGES/pysol.mo
|
||||
cp -f locale/it/LC_MESSAGES/pysol.mo locale/it_IT/LC_MESSAGES/pysol.mo
|
||||
|
||||
pretest:
|
||||
@rm -f tests/individually-importing/*.py # To avoid stray files
|
||||
rm -f tests/individually-importing/*.py tests/unit-generated/*.py # To avoid stray files
|
||||
python3 scripts/gen_individual_importing_tests.py
|
||||
|
||||
TEST_ENV_PATH = "`pwd`:`pwd`/tests/lib"
|
||||
TEST_ENV = PYTHONPATH="$$PYTHONPATH:"$(TEST_ENV_PATH) PERL5LIB="$$PERL5LIB:"$(TEST_ENV_PATH)
|
||||
TEST_ENV_PATH = $(CURDIR)$(path_sep)$(CURDIR)/tests/lib
|
||||
TEST_FILES = tests/style/*.t tests/unit-generated/*.py tests/individually-importing/*.py
|
||||
|
||||
define RUN_TESTS
|
||||
$(TEST_ENV) $1 $(TEST_FILES)
|
||||
endef
|
||||
test runtest: export PYTHONPATH := $(PYTHONPATH)$(path_sep)$(TEST_ENV_PATH)
|
||||
test runtest: export PERL5LIB := $(PERL5LIB)$(path_sep)$(TEST_ENV_PATH)
|
||||
|
||||
test: pretest
|
||||
$(call RUN_TESTS,prove)
|
||||
prove $(TEST_FILES)
|
||||
|
||||
runtest: pretest
|
||||
$(call RUN_TESTS,runprove)
|
||||
runprove $(TEST_FILES)
|
||||
|
|
|
@ -45,14 +45,15 @@ Prerequisites (needs root):
|
|||
|
||||
Build with 'python-for-android' (as user):
|
||||
|
||||
Use the cloned repo or an unpacked distribution tarball.
|
||||
|
||||
go to the android directory, then
|
||||
|
||||
$ ./mkp4a.init [<sdkdir>] [<ndkdir>] # prepare sdk and p4a installation
|
||||
$ ./mkp4a.init # prepare sdk and p4a installation
|
||||
$ ./mkkeystore # if you want to build a release version.
|
||||
|
||||
$ ./mkp4a.debug # build debug apk
|
||||
$ ./mkp4a.release <passwd1> [<passwd2>] # build release apk
|
||||
$ ./mkp4a.unsigned # build an unsigned release apk
|
||||
|
||||
The build system will download all required additional
|
||||
packages (such as the android sdk and more). Do the first build will
|
||||
|
@ -68,11 +69,22 @@ Build with 'python-for-android' (as user):
|
|||
|
||||
Cardsets:
|
||||
|
||||
Cardsets should be installed in ${HOME}/.PySolFC/cardsets/. On an
|
||||
android device this is equivalent to /sdcard/.PySolFC/cardsets/.
|
||||
Cardsets must use the bmp image format. Use scripts/cardsetsgiftobmp
|
||||
The Apk includes a minimal set of cards for playing.
|
||||
|
||||
Additional cardsets can be installed in ${HOME}/.PySolFC/cardsets/.
|
||||
On an android device this is equivalent to /sdcard/.PySolFC/cardsets/.
|
||||
Cardsets must use the bmp image format. Use scripts/cardconv
|
||||
(on a linux system) to convert them, before copying to the device.
|
||||
|
||||
Important Notice on python3/kivy 1.11.x (pysol verson 2.6.4 ff):
|
||||
In kivy, using python3, the support for bmp images depends on OpenGL.
|
||||
On fairly up to date Android OpenGL ES 3.x is available. ES (Embedded
|
||||
System) release is a subset of the full OpenGL. Due to this, bmp image
|
||||
support will be broken on android, but not on up to date workstations.
|
||||
|
||||
As gif images are still slow and error prone, the best we can do is to
|
||||
use png images instead of bmp (as noted above).
|
||||
|
||||
Possible known build issues:
|
||||
|
||||
2) for android ndk: needs a Version <=13 (because needs ant support).
|
||||
|
|
|
@ -39,7 +39,7 @@ kivy version.
|
|||
|
||||
Additional cardsets are available from the SourceForge
|
||||
project. To use them with kivy, they need to be converted to
|
||||
the BMP format. A shell script, 'cardsetsgiftobmp' , has been added
|
||||
the BMP format. A shell script, 'cardconv' , has been added
|
||||
to the scripts directory (it requires Bash and ImageMagick).
|
||||
|
||||
For all GIF images in the directories data/images and data/tiles ,
|
||||
|
|
22
README.md
|
@ -96,13 +96,14 @@ per the instructions above.
|
|||
At the moment, this only works on POSIX (Linux, FreeBSD and similar) systems.
|
||||
Windows and Mac users - you'll need to chip in with a script for your system.
|
||||
|
||||
#### 1 - Install build prerequisites: six and random2
|
||||
#### 1 - Install build prerequisites: six, random2 and pysol-cards
|
||||
|
||||
This is kind of stupid and maybe it can be fixed in the future, but for now:
|
||||
|
||||
```
|
||||
pip install six
|
||||
pip install random2
|
||||
pip install pysol-cards
|
||||
```
|
||||
|
||||
You may want to use your OS distribution package system instead, for example:
|
||||
|
@ -112,6 +113,12 @@ sudo apt-get install python-six
|
|||
sudo apt-get install python-random2
|
||||
```
|
||||
|
||||
For Pillow compilation, libjpeg headers and libraries need to be available:
|
||||
|
||||
```
|
||||
sudo apt-get install libjpeg-dev
|
||||
```
|
||||
|
||||
#### 2 - Clone the source from version control
|
||||
|
||||
```
|
||||
|
@ -122,10 +129,10 @@ cd PySolFC
|
|||
#### 3 - Create your virtual environment.
|
||||
|
||||
```
|
||||
PKGDIR=/usr/local/packages/PySolFC # or whatever
|
||||
export PKGDIR
|
||||
mkdir -p "$PKGDIR"
|
||||
( cd "$PKGDIR" && python -m venv ./env )
|
||||
PKGTREE=/usr/local/packages/PySolFC # or whatever
|
||||
export PKGTREE
|
||||
mkdir -p "$PKGTREE"
|
||||
( cd "$PKGTREE" && python -m venv ./env )
|
||||
```
|
||||
|
||||
#### 4 - Run the install script
|
||||
|
@ -139,12 +146,11 @@ mkdir -p "$PKGDIR"
|
|||
#### 6 - Enjoy playing
|
||||
|
||||
```
|
||||
"$PKGDIR"/env/bin/pysol.py
|
||||
"$PKGTREE"/env/bin/pysol.py
|
||||
```
|
||||
|
||||
## Alternate toolkit.
|
||||
|
||||
- Python2 (2.7 or later)
|
||||
- Kivy (10.0 or later)
|
||||
|
||||
- Features:
|
||||
|
@ -154,7 +160,7 @@ mkdir -p "$PKGDIR"
|
|||
- Running from source without installation:
|
||||
|
||||
```
|
||||
python2 pysol.py --kivy
|
||||
python pysol.py --kivy
|
||||
```
|
||||
|
||||
### Configuring Freecell Solver
|
||||
|
|
|
@ -10,9 +10,6 @@ root$ ./apt-install.sh
|
|||
|
||||
root$ exit
|
||||
|
||||
user$ ./pip-install.sh
|
||||
- .....some output.
|
||||
|
||||
Now all required packages are installed to proceed with the
|
||||
android build.
|
||||
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
#!/bin/bash
|
||||
# als root ausführen!
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
apt-get install -y mercurial git default-jdk
|
||||
apt-get install -y cython cython3
|
||||
apt-get install -y python3-pip
|
||||
apt-get install -y python3-yaml
|
||||
apt-get install -y virtualenv
|
||||
apt-get install -y pkg-config
|
||||
apt-get install -y automake autoconf libtool
|
||||
apt-get install -y zlib1g-dev
|
||||
apt-get install -y libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
||||
# install as root
|
||||
|
||||
apt-get install -y \
|
||||
git \
|
||||
openjdk-8-jdk \
|
||||
cython3 \
|
||||
python3-pip \
|
||||
python3-yaml \
|
||||
virtualenv \
|
||||
pkg-config \
|
||||
automake autoconf libtool \
|
||||
zlib1g-dev \
|
||||
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||
libtinfo5 \
|
||||
lld
|
||||
|
||||
update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# als user installieren !
|
||||
|
||||
python3 -m pip install --user pyasn1
|
||||
python3 -m pip install --user pyasn1_modules
|
||||
python3 -m pip install --user requests
|
||||
python3 -m pip install --user clint
|
26
android/initsdk
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
# This script mimics the instructions laid out in the p4a documentation:
|
||||
# https://python-for-android.readthedocs.io/en/latest/quickstart/
|
||||
|
||||
. mkp4a.common
|
||||
|
||||
if [[ -d $sdkdir && -d $ndkdir ]]; then
|
||||
echo "Skipping SDK and NDK installation: SDK and NDK directories already exist."
|
||||
exit
|
||||
fi
|
||||
|
||||
urlbase=https://dl.google.com/android/repository/
|
||||
tools_zip=sdk-tools-linux-4333796.zip
|
||||
ndk_zip=android-ndk-r17c-linux-x86_64.zip
|
||||
|
||||
mkdir -p $sdkdir $ndkdir
|
||||
|
||||
[ -a $ndk_zip ] || wget $urlbase/$ndk_zip
|
||||
unzip -d $(dirname $ndkdir) $ndk_zip
|
||||
|
||||
[ -a $tools_zip ] || wget $urlbase/$tools_zip
|
||||
unzip -d $sdkdir $tools_zip
|
||||
|
||||
$sdkdir/tools/bin/sdkmanager 'platforms;android-27'
|
||||
$sdkdir/tools/bin/sdkmanager 'build-tools;29.0.1'
|
|
@ -1,243 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
|
||||
import glob
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from zipfile import ZipFile, ZipInfo
|
||||
|
||||
from clint.textui import progress
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
cachefiles = [
|
||||
('https://dl.google.com/android/repository/platform-tools-latest-linux.zip',
|
||||
'',
|
||||
'platform-tools'),
|
||||
('https://dl.google.com/android/repository/tools_r25.2.5-linux.zip',
|
||||
'577516819c8b5fae680f049d39014ff1ba4af870b687cab10595783e6f22d33e',
|
||||
'tools'),
|
||||
('https://dl.google.com/android/repository/android-19_r04.zip',
|
||||
'5efc3a3a682c1d49128daddb6716c433edf16e63349f32959b6207524ac04039',
|
||||
'platform'),
|
||||
('https://dl.google.com/android/repository/build-tools_r26-linux.zip',
|
||||
'7422682f92fb471d4aad4c053c9982a9a623377f9d5e4de7a73cd44ebf2f3c61',
|
||||
'build-tools'),
|
||||
('https://dl.google.com/'
|
||||
'android/repository/android-ndk-r12b-linux-x86_64.zip',
|
||||
'eafae2d614e5475a3bcfd7c5f201db5b963cc1290ee3e8ae791ff0c66757781e',
|
||||
'ndk'),
|
||||
]
|
||||
|
||||
# https://stackoverflow.com/questions/39296101:
|
||||
|
||||
|
||||
class MyZipFile(ZipFile):
|
||||
|
||||
def extract(self, member, path=None, pwd=None):
|
||||
if not isinstance(member, ZipInfo):
|
||||
member = self.getinfo(member)
|
||||
|
||||
if path is None:
|
||||
path = os.getcwd()
|
||||
|
||||
ret_val = self._extract_member(member, path, pwd)
|
||||
attr = member.external_attr >> 16
|
||||
os.chmod(ret_val, attr)
|
||||
return ret_val
|
||||
|
||||
# Reused from fdroidserver:
|
||||
|
||||
|
||||
def sha256_for_file(path):
|
||||
with open(path, 'rb') as f:
|
||||
s = hashlib.sha256()
|
||||
while True:
|
||||
data = f.read(4096)
|
||||
if not data:
|
||||
break
|
||||
s.update(data)
|
||||
return s.hexdigest()
|
||||
|
||||
# Adapted from fdroidserver:
|
||||
|
||||
|
||||
def update_cache(cachedir, cachefiles):
|
||||
for srcurl, shasum, typ in cachefiles:
|
||||
filename = os.path.basename(srcurl)
|
||||
local_filename = os.path.join(cachedir, filename)
|
||||
|
||||
if os.path.exists(local_filename):
|
||||
local_length = os.path.getsize(local_filename)
|
||||
else:
|
||||
|
||||
local_length = -1
|
||||
|
||||
if (typ == 'ndk') and (ndkloc is not None):
|
||||
continue
|
||||
elif (typ != 'tools') and (sdkloc is not None):
|
||||
continue
|
||||
|
||||
resume_header = {}
|
||||
download = True
|
||||
|
||||
try:
|
||||
r = requests.head(srcurl, allow_redirects=True, timeout=60)
|
||||
if r.status_code == 200:
|
||||
content_length = int(r.headers.get('content-length'))
|
||||
else:
|
||||
content_length = local_length # skip the download
|
||||
except requests.exceptions.RequestException as e:
|
||||
content_length = local_length # skip the download
|
||||
logger.warn('%s', e)
|
||||
|
||||
if local_length == content_length:
|
||||
download = False
|
||||
elif local_length > content_length:
|
||||
logger.info('deleting corrupt file from cache: %s',
|
||||
local_filename)
|
||||
os.remove(local_filename)
|
||||
logger.info("Downloading %s to cache", filename)
|
||||
elif local_length > -1 and local_length < content_length:
|
||||
logger.info("Resuming download of %s", local_filename)
|
||||
resume_header = {
|
||||
'Range': 'bytes=%d-%d' % (local_length, content_length)}
|
||||
else:
|
||||
logger.info("Downloading %s to cache", filename)
|
||||
|
||||
if download:
|
||||
r = requests.get(srcurl, headers=resume_header,
|
||||
stream=True, verify=False, allow_redirects=True)
|
||||
content_length = int(r.headers.get('content-length'))
|
||||
with open(local_filename, 'ab') as f:
|
||||
for chunk in progress.bar(
|
||||
r.iter_content(chunk_size=65536),
|
||||
expected_size=(content_length / 65536) + 1):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
f.write(chunk)
|
||||
if not shasum == '':
|
||||
v = sha256_for_file(local_filename)
|
||||
if v == shasum:
|
||||
logger.info("Shasum verified for %s", local_filename)
|
||||
else:
|
||||
logger.critical(
|
||||
"Invalid shasum of '%s' detected for %s", v, local_filename)
|
||||
os.remove(local_filename)
|
||||
sys.exit(1)
|
||||
|
||||
# Build the sdk from zips.
|
||||
|
||||
|
||||
def build_sdk(sdkdir, cachedir, cachfiles):
|
||||
for srcurl, shasum, typ in cachefiles:
|
||||
filename = os.path.basename(srcurl)
|
||||
local_filename = os.path.join(cachedir, filename)
|
||||
|
||||
if typ == 'tools':
|
||||
if os.path.exists(local_filename):
|
||||
print('Extract: %s' % local_filename)
|
||||
zf = MyZipFile(local_filename)
|
||||
zf.extractall(sdkdir)
|
||||
elif typ == 'platform-tools':
|
||||
if (sdkloc is None) and (os.path.exists(local_filename)):
|
||||
print('Extract: %s' % local_filename)
|
||||
zf = MyZipFile(local_filename)
|
||||
zf.extractall(sdkdir)
|
||||
else:
|
||||
print('Link to: %s' % sdkloc)
|
||||
os.symlink(sdkloc + '/platform-tools',
|
||||
sdkdir + '/platform-tools')
|
||||
elif typ == 'platform':
|
||||
if (sdkloc is None) and (os.path.exists(local_filename)):
|
||||
print('Extract: %s' % local_filename)
|
||||
zf = MyZipFile(local_filename)
|
||||
zf.extractall(sdkdir + '/platforms')
|
||||
else:
|
||||
print('Link to: %s' % sdkloc)
|
||||
os.symlink(sdkloc + '/platforms', sdkdir + '/platforms')
|
||||
elif typ == 'build-tools':
|
||||
if (sdkloc is None) and (os.path.exists(local_filename)):
|
||||
print('Extract: %s' % local_filename)
|
||||
zf = MyZipFile(local_filename)
|
||||
zf.extractall(sdkdir + '/build-tools')
|
||||
else:
|
||||
print('Link to: %s' % sdkloc)
|
||||
os.symlink(sdkloc + '/build-tools', sdkdir + '/build-tools')
|
||||
elif typ == 'ndk':
|
||||
if ndkloc is None:
|
||||
print('Extract: %s' % local_filename)
|
||||
zf = MyZipFile(local_filename)
|
||||
zf.extractall(sdkdir)
|
||||
lst = glob.glob(sdkdir + '/*-ndk-*')
|
||||
print(lst)
|
||||
os.rename(lst[0], sdkdir + '/ndk-bundle')
|
||||
else:
|
||||
print('Link to: %s' % ndkloc)
|
||||
os.symlink(ndkloc, sdkdir + '/ndk-bundle')
|
||||
|
||||
|
||||
logger = logging.getLogger('prepare-fdroid-build')
|
||||
logging.basicConfig(format='%(message)s', level=logging.INFO)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# command line arguments
|
||||
|
||||
sdkloc = None
|
||||
ndkloc = None
|
||||
if len(sys.argv) > 1:
|
||||
sdkloc = sys.argv[1]
|
||||
if (len(sdkloc) > 0) and (sdkloc[-1] == '/'):
|
||||
sdkloc = sdkloc[:-1]
|
||||
if not os.path.isdir(sdkloc):
|
||||
sdkloc = None
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
ndkloc = sys.argv[2]
|
||||
if (len(ndkloc) > 0) and (ndkloc[-1] == '/'):
|
||||
ndkloc = ndkloc[:-1]
|
||||
if not os.path.isdir(ndkloc):
|
||||
ndkloc = None
|
||||
|
||||
fdroidmode = None
|
||||
if len(sys.argv) > 3:
|
||||
fdroidmode = sys.argv[3]
|
||||
if (len(fdroidmode) > 0):
|
||||
fdroidmode = '1'
|
||||
|
||||
if sdkloc == "":
|
||||
sdkloc = None
|
||||
if ndkloc == "":
|
||||
ndkloc = None
|
||||
|
||||
logger.info('sdkloc = %s' % sdkloc)
|
||||
logger.info('ndkloc = %s' % ndkloc)
|
||||
|
||||
# sdkloc and ndkloc already set by the user and fdroidmode:
|
||||
# nothing to do.
|
||||
|
||||
if (sdkloc is not None) and (ndkloc is not None) and (fdroidmode is not None):
|
||||
sys.exit(0)
|
||||
|
||||
# cache dir (using the same as in fdroidserver/buildserver)
|
||||
cachedir = os.path.join(os.getenv('HOME'), '.cache', 'fdroidserver')
|
||||
logger.info('cachedir name is: %s', cachedir)
|
||||
|
||||
if not os.path.exists(cachedir):
|
||||
os.makedirs(cachedir, 0o755)
|
||||
logger.info('created cachedir %s', cachedir)
|
||||
|
||||
# sdkdir location
|
||||
sdkdir = os.path.join(os.getenv('HOME'), '.cache', 'sdk-for-p4a')
|
||||
logger.info('sdkdir name is: %s', sdkdir)
|
||||
|
||||
if not os.path.exists(sdkdir):
|
||||
os.makedirs(sdkdir, 0o755)
|
||||
logger.debug('created sdkdir %s', sdkdir)
|
||||
|
||||
update_cache(cachedir, cachefiles)
|
||||
build_sdk(sdkdir, cachedir, cachefiles)
|
||||
else:
|
||||
logger.info('sdkdir %s already exists', sdkdir)
|
10
android/initsupport
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
# Download android support library from maven
|
||||
|
||||
. mkp4a.common
|
||||
|
||||
liburl="https://maven.google.com/com/android/support/support-v4/24.1.1"
|
||||
libname="support-v4-24.1.1.aar"
|
||||
|
||||
. mkp4a.preload supportlib ${liburl} ${libname}
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2.7
|
||||
#!/usr/bin/env python3
|
||||
# ---------------------------------------------------------------------------
|
||||
#
|
||||
# PySol -- a Python Solitaire game
|
||||
|
|
|
@ -1,46 +1,27 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
|
||||
. mkp4a.common
|
||||
|
||||
echo '### prepare cardsets'
|
||||
|
||||
if [ ! -f ./PySolFC-Cardsets-2.0.tar.bz2 ]
|
||||
then
|
||||
echo '### downloading cardets'
|
||||
# wget http://downloads.sourceforge.net/pysolfc/PySolFC-Cardsets-2.0.tar.bz2
|
||||
wget https://netix.dl.sourceforge.net/project/pysolfc/PySolFC-Cardsets/PySolFC-Cardsets-2.0/PySolFC-Cardsets-2.0.tar.bz2
|
||||
if [ ! -f ${cardsets_file} ]; then
|
||||
echo '### downloading cardsets'
|
||||
wget https://netix.dl.sourceforge.net/project/pysolfc/PySolFC-Cardsets/minimal/${cardsets_file}
|
||||
fi
|
||||
|
||||
if [ -f ./PySolFC-Cardsets-2.0.tar.bz2 ]
|
||||
then
|
||||
if [ ! -d ./PySolFC-Cardsets-2.0 ]
|
||||
then
|
||||
echo '### extracting selected cardets'
|
||||
tar -xjvf PySolFC-Cardsets-2.0.tar.bz2 \
|
||||
PySolFC-Cardsets-2.0/cardset-crystal-mahjongg \
|
||||
PySolFC-Cardsets-2.0/cardset-dashavatara-ganjifa \
|
||||
PySolFC-Cardsets-2.0/cardset-dondorf \
|
||||
PySolFC-Cardsets-2.0/cardset-hexadeck \
|
||||
PySolFC-Cardsets-2.0/cardset-kintengu \
|
||||
PySolFC-Cardsets-2.0/cardset-matrix \
|
||||
PySolFC-Cardsets-2.0/cardset-mughal-ganjifa \
|
||||
PySolFC-Cardsets-2.0/cardset-oxymoron \
|
||||
PySolFC-Cardsets-2.0/cardset-standard \
|
||||
PySolFC-Cardsets-2.0/cardset-vienna-2k \
|
||||
PySolFC-Cardsets-2.0/cardset-greywyvern
|
||||
fi
|
||||
|
||||
if [ -d ./PySolFC-Cardsets-2.0 ]
|
||||
then
|
||||
echo '### processing cardets'
|
||||
cd PySolFC-Cardsets-2.0
|
||||
../../scripts/cardsetsgiftobmp
|
||||
for i in cardset-*-bmp
|
||||
do
|
||||
rm -rf `basename $i -bmp`
|
||||
done
|
||||
cd ..
|
||||
fi
|
||||
else
|
||||
echo '### error downloading cardsets'
|
||||
if [ ! -d ${cardsets_dir} ]; then
|
||||
echo '### extracting cardsets'
|
||||
tar -xf ${cardsets_file}
|
||||
fi
|
||||
|
||||
echo '### processing cardsets'
|
||||
(
|
||||
cd ${cardsets_dir}
|
||||
../../scripts/cardconv gif png
|
||||
for i in cardset-*-png; do
|
||||
rm -rf `basename $i -png`
|
||||
done
|
||||
)
|
||||
|
||||
echo '### end cardsets'
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
|
||||
# memo:
|
||||
# keytool -genkey -v -keystore my-release-keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 12000
|
||||
|
||||
if [ ! -d bin ]
|
||||
then
|
||||
echo "mkdir bin"
|
||||
mkdir bin
|
||||
fi
|
||||
mkdir -p bin
|
||||
|
||||
if [ -f ./bin/keystore ]
|
||||
then
|
||||
if [ -f ./bin/keystore ]; then
|
||||
echo "keystore is already defined"
|
||||
else
|
||||
keytool -genkey -v -keystore ./bin/keystore -alias python -keyalg RSA -keysize 2048 -validity 12000
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
|
||||
rm -rf tmp
|
||||
python3 -m pythonforandroid.toolchain clean_dists
|
||||
python3 -m pythonforandroid.toolchain clean_builds
|
||||
rm -f *.apk
|
||||
rm -rf PySolFC-Cardsets-2.0
|
||||
|
||||
rm -rf PySolFC-Cardsets--Minimal-2.0.1
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
rm -rf tmp
|
||||
rm -f *.apk
|
||||
|
|
33
android/mkp4a.common
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Common constants for various scripts in this directory.
|
||||
|
||||
version=$(PYTHONPATH=.. python3 -c \
|
||||
'from pysollib.settings import VERSION; print(VERSION)')
|
||||
|
||||
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
||||
|
||||
cardsets_dir='PySolFC-Cardsets--Minimal-2.0.1'
|
||||
cardsets_file="${cardsets_dir}.tar.xz"
|
||||
|
||||
sdkdir="${HOME}/.cache/sdk-for-p4a/sdk"
|
||||
ndkdir="${HOME}/.cache/sdk-for-p4a/android-ndk-r17c"
|
||||
pkgdir="${HOME}/.local/share/python-for-android/packages"
|
||||
|
||||
# gradle may need this.
|
||||
export TERM="xterm"
|
||||
|
||||
p4a_options="\
|
||||
--sdk-dir ${sdkdir} \
|
||||
--ndk-dir ${ndkdir} \
|
||||
--dist-name pysolfc \
|
||||
--name PySolFC \
|
||||
--package org.lufebe16.pysolfc \
|
||||
--version ${version} \
|
||||
--bootstrap sdl2 \
|
||||
--requirements python3,attrs,configobj,kivy,pysol-cards,random2,six \
|
||||
--private ${tmpdir} \
|
||||
--orientation sensor \
|
||||
--icon ${tmpdir}/data/images/icons/48x48/pysol.png \
|
||||
--presplash ${tmpdir}/data/images/icons/1024x1024/pysol.png \
|
||||
--copy-libs \
|
||||
--add-jar ${tmpdir}/support-v4-24.1.1.aar \
|
||||
--color always"
|
|
@ -1,32 +1,11 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
|
||||
package='org.lufebe16.pysolfc'
|
||||
version=`./version.py`
|
||||
name='PySolFC'
|
||||
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
||||
. mkp4a.common
|
||||
|
||||
if [ "$1" ]
|
||||
then
|
||||
package=${package}.dbg
|
||||
name=${name}dbg
|
||||
fi
|
||||
new_options=${p4a_options}
|
||||
new_options=${new_options/PySolFC/PySolFCdbg}
|
||||
new_options=${new_options/org.lufebe16.pysolfc/org.lufebe16.pysolfc.dbg}
|
||||
|
||||
python3 -m pythonforandroid.toolchain apk \
|
||||
--sdk-dir ${HOME}/.cache/sdk-for-p4a \
|
||||
--ndk-dir ${HOME}/.cache/sdk-for-p4a/ndk-bundle \
|
||||
--android-api 19 \
|
||||
--ndk-version r12b \
|
||||
--arch armeabi-v7a \
|
||||
--dist-name pysolfc \
|
||||
--name ${name} \
|
||||
--bootstrap=sdl2 \
|
||||
--requirements kivy,hostpython2,random2 \
|
||||
--minsdk 14 \
|
||||
--private ${tmpdir} \
|
||||
--package ${package} \
|
||||
--version ${version} \
|
||||
--orientation sensor \
|
||||
--color=always \
|
||||
--icon ${tmpdir}/data/images/misc/pysol01.png \
|
||||
--presplash ${tmpdir}/data/images/misc/pysol06.png \
|
||||
--copy-libs
|
||||
${new_options}
|
||||
|
|
|
@ -1,56 +1,62 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
|
||||
. mkp4a.common
|
||||
|
||||
# NOTE: $1 will be set with fdroid builds only.
|
||||
|
||||
echo '### prepare sdk'
|
||||
|
||||
./initsdk.py $1 $2 $3
|
||||
if [[ $# == 0 ]]
|
||||
then
|
||||
./initsupport
|
||||
./initsdk
|
||||
fi
|
||||
|
||||
echo '### install p4a'
|
||||
|
||||
p4adir=${HOME}/.cache/tmp-for-p4a
|
||||
mkdir -p ${p4adir}
|
||||
|
||||
p4aversion='0.5.3'
|
||||
if [ ! -f ${p4adir}/${p4aversion}.zip ]
|
||||
if [[ $# == 0 ]]
|
||||
then
|
||||
wget "https://github.com/kivy/python-for-android/archive/${p4aversion}.zip"
|
||||
cp -a ./${p4aversion}.zip ${p4adir}/${p4aversion}.zip
|
||||
rm -f ./${p4aversion}.zip
|
||||
fi
|
||||
if [ -f ${p4adir}/${p4aversion}.zip ]
|
||||
then
|
||||
python3 -m pip install -q --user "${p4adir}/${p4aversion}.zip"
|
||||
else
|
||||
echo "### download of ${p4aversion}.zip failed"
|
||||
python3 -m pip install -q --user python-for-android
|
||||
fi
|
||||
|
||||
echo '### prepare source'
|
||||
|
||||
srcdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
||||
(cd .. && make rules && make all_games_html && make mo)
|
||||
|
||||
mkdir -p ${srcdir}
|
||||
rm -rf ${srcdir}
|
||||
cp -a .. ${srcdir}
|
||||
rm -rf ${srcdir}/android
|
||||
rm -rf ${srcdir}/src
|
||||
cp -a main.py ${srcdir}/main.py
|
||||
mkdir -p ${srcdir}/data/images/cards/bottoms/trumps-only
|
||||
echo "" > ${srcdir}/data/images/cards/bottoms/trumps-only/.keep
|
||||
mkdir -p ${tmpdir}
|
||||
rm -rf ${tmpdir}
|
||||
cp -a .. ${tmpdir}
|
||||
rm -rf ${tmpdir}/android
|
||||
rm -rf ${tmpdir}/src
|
||||
|
||||
# remove useless load from the app
|
||||
rm -rf ${tmpdir}/.git
|
||||
rm -rf ${tmpdir}/.gitignore
|
||||
rm -rf ${tmpdir}/Screenshots
|
||||
rm -rf ${tmpdir}/build
|
||||
rm -rf ${tmpdir}/contrib
|
||||
rm -rf ${tmpdir}/dist
|
||||
rm -rf ${tmpdir}/runtests.pl
|
||||
rm -rf ${tmpdir}/tests
|
||||
rm -rf ${tmpdir}/Brewfile
|
||||
rm -rf ${tmpdir}/.appveyor.yml
|
||||
rm -rf ${tmpdir}/.perltidyrc
|
||||
rm -rf ${tmpdir}/.travis.yml
|
||||
|
||||
rm -rf ${tmpdir}/setup.py
|
||||
rm -rf ${tmpdir}/setup_osx.py
|
||||
rm -rf ${tmpdir}/setup.cfg
|
||||
|
||||
cp -a ${pkgdir}/supportlib/support-v4-24.1.1.aar ${tmpdir}
|
||||
cp -a main.py ${tmpdir}
|
||||
mkdir -p ${tmpdir}/data/images/cards/bottoms/trumps-only
|
||||
echo "" > ${tmpdir}/data/images/cards/bottoms/trumps-only/.keep
|
||||
|
||||
echo '### prepare cardsets'
|
||||
|
||||
cardsdir=${HOME}/.cache/tmp-for-p4a/pysolfc
|
||||
./mkcards
|
||||
|
||||
if [ ! -d ${cardsdir}/PySolFC-Cardsets-2.0 ]
|
||||
then
|
||||
./mkcards
|
||||
mv PySolFC-Cardsets-2.0 ${cardsdir}/
|
||||
rm -f PySolFC-Cardsets-2.0.tar.bz2
|
||||
fi
|
||||
|
||||
if [ -d ${cardsdir}/PySolFC-Cardsets-2.0 ]
|
||||
then
|
||||
echo '### copying cardsets'
|
||||
cp -a ${cardsdir}/PySolFC-Cardsets-2.0/* ${srcdir}/data
|
||||
fi
|
||||
cp -a ${cardsets_dir}/* ${tmpdir}/data
|
||||
|
||||
echo '### end init'
|
||||
|
|
30
android/mkp4a.preload
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
|
||||
# package preload helper for fdroid build.
|
||||
|
||||
set -eux
|
||||
|
||||
if [[ $# < 3 ]]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
packagebase=${HOME}'/.local/share/python-for-android/packages'
|
||||
|
||||
packagedir=${packagebase}/$1
|
||||
packageurl=$2
|
||||
packagename=$3
|
||||
packagemark='.mark-'${packagename}
|
||||
|
||||
if [[ $# == 4 ]]
|
||||
then
|
||||
packagemark='.mark'$4
|
||||
fi
|
||||
|
||||
mkdir -p ${packagedir}
|
||||
cd ${packagedir}
|
||||
if [ ! -f ${packagemark} ]
|
||||
then
|
||||
wget -nv ${packageurl}/${packagename}
|
||||
touch ${packagemark}
|
||||
fi
|
|
@ -1,11 +1,13 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
. mkp4a.common
|
||||
|
||||
pass1=""
|
||||
pass2=""
|
||||
keyalias="python"
|
||||
keystore="${PWD}/bin/keystore"
|
||||
if [ $1 ]
|
||||
then
|
||||
if [ $1 ]; then
|
||||
pass1=$1
|
||||
pass2=$1
|
||||
else
|
||||
|
@ -13,16 +15,13 @@ else
|
|||
echo " (use ./mkkeystore to create one in default location)"
|
||||
exit
|
||||
fi
|
||||
if [ $2 ]
|
||||
then
|
||||
if [ $2 ]; then
|
||||
pass2=$2
|
||||
fi
|
||||
if [ $3 ]
|
||||
then
|
||||
if [ $3 ]; then
|
||||
keyalias=$3
|
||||
fi
|
||||
if [ $4 ]
|
||||
then
|
||||
if [ $4 ]; then
|
||||
keystore=$4
|
||||
fi
|
||||
|
||||
|
@ -31,30 +30,10 @@ export P4A_RELEASE_KEYSTORE_PASSWD="$pass1"
|
|||
export P4A_RELEASE_KEYALIAS_PASSWD="$pass2"
|
||||
export P4A_RELEASE_KEYALIAS="$keyalias"
|
||||
|
||||
version=`./version.py`
|
||||
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
||||
|
||||
python3 -m pythonforandroid.toolchain apk \
|
||||
--sdk-dir ${HOME}/.cache/sdk-for-p4a \
|
||||
--ndk-dir ${HOME}/.cache/sdk-for-p4a/ndk-bundle \
|
||||
--android-api 19 \
|
||||
--ndk-version r12b \
|
||||
--arch armeabi-v7a \
|
||||
--dist-name pysolfc \
|
||||
--name PySolFC \
|
||||
--bootstrap=sdl2 \
|
||||
--requirements kivy,hostpython2,random2 \
|
||||
${p4a_options} \
|
||||
--release \
|
||||
--sign \
|
||||
--minsdk 14 \
|
||||
--private ${tmpdir} \
|
||||
--package org.lufebe16.pysolfc \
|
||||
--version ${version} \
|
||||
--orientation sensor \
|
||||
--color=always \
|
||||
--icon ${tmpdir}/data/images/misc/pysol01.png \
|
||||
--presplash ${tmpdir}/data/images/misc/pysol06.png \
|
||||
--copy-libs
|
||||
--sign
|
||||
|
||||
# keystore options (instead environment vars):
|
||||
#
|
||||
|
|
|
@ -1,47 +1,21 @@
|
|||
#!/bin/bash
|
||||
set -eux
|
||||
|
||||
echo '### p4a started'
|
||||
. mkp4a.common
|
||||
|
||||
# sdk-dir and nkd-dir from command line (fdroid mode)
|
||||
# NOTE: $1 and $2 (sdk and ndk) used with fdroid build only.
|
||||
|
||||
sdkdir="${HOME}/.cache/sdk-for-p4a"
|
||||
ndkdir="${HOME}/.cache/sdk-for-p4a/ndk-bundle"
|
||||
if [ $1 ]
|
||||
if [[ $# == 2 ]]
|
||||
then
|
||||
echo "set sdk to: $1"
|
||||
sdkdir="$1"
|
||||
sdkdir=$1
|
||||
ndkdir=$2
|
||||
fi
|
||||
if [ $2 ]
|
||||
then
|
||||
echo "set ndk to: $2"
|
||||
ndkdir="$2"
|
||||
fi
|
||||
|
||||
echo '### run toolchain'
|
||||
|
||||
version=`./version.py`
|
||||
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
||||
|
||||
python3 -m pythonforandroid.toolchain apk \
|
||||
${p4a_options} \
|
||||
--sdk-dir ${sdkdir} \
|
||||
--ndk-dir ${ndkdir} \
|
||||
--android-api 19 \
|
||||
--ndk-version r12b \
|
||||
--arch armeabi-v7a \
|
||||
--dist-name pysolfc \
|
||||
--name PySolFC \
|
||||
--bootstrap=sdl2 \
|
||||
--requirements kivy,hostpython2,random2 \
|
||||
--release \
|
||||
--minsdk 14 \
|
||||
--private ${tmpdir} \
|
||||
--package org.lufebe16.pysolfc \
|
||||
--version ${version} \
|
||||
--orientation sensor \
|
||||
--color=always \
|
||||
--icon ${tmpdir}/data/images/misc/pysol01.png \
|
||||
--presplash ${tmpdir}/data/images/misc/pysol06.png \
|
||||
--copy-libs
|
||||
--release
|
||||
|
||||
# python3 -m pythonforandroid.toolchain apk
|
||||
# ...
|
||||
|
@ -53,5 +27,3 @@ python3 -m pythonforandroid.toolchain apk \
|
|||
# ohne: -> debug version
|
||||
# 1: -> release unsigned
|
||||
# 1 und 2: -> release version.
|
||||
|
||||
echo '### p4a finished'
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#! /bin/sh -Cefu
|
||||
#!/bin/bash
|
||||
set -Cefu
|
||||
|
||||
: ${PKGTREE:=/usr/local/packages/PySolFC}
|
||||
PIP="$(printf '%s/env/bin/pip install --no-binary :all: ' "$PKGTREE")"
|
||||
PIP=("${PKGTREE}/env/bin/pip" install --no-binary :all:)
|
||||
PYPROG="$(printf '%s/env/bin/python' "$PKGTREE")"
|
||||
VERSION="$(env PYTHONPATH=`pwd` "$PYPROG" -c 'from pysollib.settings import VERSION ; print(VERSION)')"
|
||||
XZBALL="$(printf 'dist/PySolFC-%s.tar.xz' "$VERSION")"
|
||||
|
@ -10,6 +11,6 @@ reqs='six random2 pillow'
|
|||
make dist
|
||||
for req in $reqs
|
||||
do
|
||||
"$PIP" "$req"
|
||||
"${PIP[@]}" "$req"
|
||||
done
|
||||
"$PIP" --upgrade "$XZBALL"
|
||||
"${PIP[@]}" --upgrade "$XZBALL"
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Translatable strings file generated by Glade.
|
||||
* Add this file to your project's POTFILES.in.
|
||||
* DO NOT compile it as part of your application.
|
||||
*/
|
||||
|
||||
gchar *s = N_("Game Statistics");
|
||||
gchar *s = N_("Game:");
|
||||
gchar *s = N_("Won:");
|
||||
gchar *s = N_("Total:");
|
||||
gchar *s = N_("Lost:");
|
||||
gchar *s = N_("Current session");
|
||||
gchar *s = N_("Won:");
|
||||
gchar *s = N_("Lost:");
|
||||
gchar *s = N_("Total:");
|
||||
gchar *s = N_("Total");
|
||||
gchar *s = N_("Current game");
|
||||
gchar *s = N_("Playing time:");
|
||||
gchar *s = N_("Moves:");
|
||||
gchar *s = N_("Total moves:");
|
||||
gchar *s = N_("Minimum");
|
||||
gchar *s = N_("Maximum");
|
||||
gchar *s = N_("Average");
|
||||
gchar *s = N_("Summary");
|
||||
gchar *s = N_("Playing time");
|
||||
gchar *s = N_("Moves");
|
||||
gchar *s = N_("Total moves");
|
||||
gchar *s = N_("Game:");
|
||||
gchar *s = N_("Top 10");
|
||||
gchar *s = N_("All games");
|
||||
gchar *s = N_("Full log");
|
||||
gchar *s = N_("Session log");
|
||||
gchar *s = N_("Set timeouts");
|
||||
gchar *s = N_("Demo:");
|
||||
gchar *s = N_("Hint:");
|
||||
gchar *s = N_("Raise card:");
|
||||
gchar *s = N_("Highlight piles:");
|
||||
gchar *s = N_("Highlight cards:");
|
||||
gchar *s = N_("Highlight same rank:");
|
||||
gchar *s = N_("Set colors");
|
||||
gchar *s = N_("Highlight piles:");
|
||||
gchar *s = N_("Highlight cards 1:");
|
||||
gchar *s = N_("Highlight cards 2:");
|
||||
gchar *s = N_("Highlight same rank 1:");
|
||||
gchar *s = N_("Highlight same rank 2:");
|
||||
gchar *s = N_("Hint arrow:");
|
||||
gchar *s = N_("Highlight not matching:");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Text foreground:");
|
||||
gchar *s = N_("Set font");
|
||||
gchar *s = N_("HTML: ");
|
||||
gchar *s = N_("Small: ");
|
||||
gchar *s = N_("Fixed: ");
|
||||
gchar *s = N_("Tableau default: ");
|
||||
gchar *s = N_("Tableau fixed: ");
|
||||
gchar *s = N_("Tableau small: ");
|
||||
gchar *s = N_("Tableau large: ");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Change...");
|
||||
gchar *s = N_("Sound settings");
|
||||
gchar *s = N_("Sound enabled");
|
||||
gchar *s = N_("Sample volume:");
|
||||
gchar *s = N_("Music volume:");
|
||||
gchar *s = N_("Enable samles");
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
@ -1,4 +1,6 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# convert gif to png - dir recursive
|
||||
|
||||
# scharf !!!!
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=PySol Fan Club Edition
|
||||
Comment=More than 1000 solitaire card games
|
||||
Exec=pysol.py
|
||||
StartupWMClass=Pysol
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Game;CardGame;
|
||||
Icon=/usr/share/icons/pysol01.png
|
||||
Keywords=solitaire;patience;cards;pysolfc;klondike;
|
||||
Icon=pysol
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
from_dir=images
|
||||
to_dir=clearlooks
|
||||
|
|
|
@ -69,9 +69,9 @@ the current directory
|
|||
.br
|
||||
/usr/local/share/PySolFC
|
||||
.br
|
||||
/usr/games/PySolFC
|
||||
/usr/share/games/PySolFC
|
||||
.br
|
||||
/usr/local/games/PySolFC
|
||||
/usr/local/share/games/PySolFC
|
||||
.PP
|
||||
Options are saved in \fB~/.PySolFC/options.cfg\fR
|
||||
.PP
|
||||
|
|
|
@ -6,8 +6,11 @@ traditionally been individually hand painted. There are any where from eight
|
|||
to twelve or more suits per deck, each suit having usually twelve ranks. The
|
||||
two most common Ganjifa decks are the Mughal which has eight suits and the
|
||||
Dashavatara which has ten. The suits have pip cards numbered from Ace through
|
||||
ten and two court cards, the Wazir and the Mir. Ganjifa solitaire games play
|
||||
the same as games that use the standard deck but the larger number of different
|
||||
cards in a deck (96 or 120) adds an element of complexity. The fact that each
|
||||
suit has it's own color makes things quite interesting in games that use
|
||||
"Alternate Color" row stacks.
|
||||
ten and two court cards, the Wazir and the Mir. The order of the suits
|
||||
is: Matsya, Kuchha, Varaha, Narsingha, Vamana, Parashurama, Rama,
|
||||
Krishna, Buddha, and Kalanki. Ganjifa solitaire games play the same
|
||||
as games that use the standard deck but the larger number of different
|
||||
cards in a deck (96 or 120) adds an element of complexity. The fact
|
||||
that each suit has it's own color makes things quite interesting in
|
||||
games that use "Alternate Color" row stacks.
|
||||
<img alt="" src="images/ganjifa.gif">
|
||||
|
|
BIN
html-src/images/ganjifa.gif
Normal file
After Width: | Height: | Size: 40 KiB |
|
@ -10,7 +10,7 @@ Move all cards to the foundations.
|
|||
<p>
|
||||
This is a Freecell type of game. Cards on the tableau build down in rank
|
||||
by suit. Cards build up in rank on the foundations.
|
||||
A stack can be moved if the cards are in decending rank order
|
||||
A stack can be moved if the cards are in descending rank order
|
||||
regardless of the suit. Only a King or the highest trump can be played
|
||||
on an empty row.
|
||||
|
||||
|
|
4989
po/de_pysol.po
Normal file
11
po/games.pot
|
@ -5,7 +5,7 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PySol 0.0.1\n"
|
||||
"POT-Creation-Date: Mon Mar 7 21:38:07 2011\n"
|
||||
"POT-Creation-Date: Wed Sep 25 11:43:34 2019\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -702,6 +702,9 @@ msgstr ""
|
|||
msgid "Czarina"
|
||||
msgstr ""
|
||||
|
||||
msgid "Daddy Longlegs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Danda"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1272,6 +1275,12 @@ msgstr ""
|
|||
msgid "FreeCell"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeCell with Two Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeCell with Zero Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "Frog"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -5,14 +5,16 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: it_games\n"
|
||||
"POT-Creation-Date: Thu Sep 6 15:06:46 2007\n"
|
||||
"POT-Creation-Date: Thu Jul 18 18:43:59 2019\n"
|
||||
"PO-Revision-Date: 2011-05-12 18:46+0200\n"
|
||||
"Last-Translator: Giuliano Colla <giuliano.colla@gmail.com>\n"
|
||||
"Language-Team: Italiano <it@li.org>\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: ./scripts/all_games.py 0.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: KBabel 1.11.4\n"
|
||||
|
||||
msgid " 3x3 Matrix"
|
||||
|
@ -75,6 +77,9 @@ msgstr "Conoscenti"
|
|||
msgid "Adela"
|
||||
msgstr "Adelina"
|
||||
|
||||
msgid "Aglet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Agnes Bernauer"
|
||||
msgstr "Agnese Bernauer"
|
||||
|
||||
|
@ -243,6 +248,12 @@ msgstr ""
|
|||
msgid "Bavarian Patience"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bayan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Beacon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Beak and Flipper"
|
||||
msgstr ""
|
||||
|
||||
|
@ -522,6 +533,9 @@ msgstr ""
|
|||
msgid "Cat's Tail"
|
||||
msgstr ""
|
||||
|
||||
msgid "Catherine the Great"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cavalier"
|
||||
msgstr ""
|
||||
|
||||
|
@ -690,6 +704,9 @@ msgstr ""
|
|||
msgid "Czarina"
|
||||
msgstr ""
|
||||
|
||||
msgid "Daddy Longlegs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Danda"
|
||||
msgstr ""
|
||||
|
||||
|
@ -804,6 +821,9 @@ msgstr ""
|
|||
msgid "Double Bisley"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Blue Moon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Canfield"
|
||||
msgstr ""
|
||||
|
||||
|
@ -876,12 +896,18 @@ msgstr ""
|
|||
msgid "Double Measure"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Montana"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Pyramid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Rail"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Red Moon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Double Russian Solitaire"
|
||||
msgstr ""
|
||||
|
||||
|
@ -942,6 +968,9 @@ msgstr ""
|
|||
msgid "Dutch Solitaire"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dutchess"
|
||||
msgstr ""
|
||||
|
||||
msgid "Eagle Wing"
|
||||
msgstr ""
|
||||
|
||||
|
@ -972,6 +1001,9 @@ msgstr ""
|
|||
msgid "Eight Off"
|
||||
msgstr ""
|
||||
|
||||
msgid "Eight Packs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Eight Sages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1158,6 +1190,9 @@ msgstr ""
|
|||
msgid "Flying Dragon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Foothold"
|
||||
msgstr ""
|
||||
|
||||
msgid "Footling"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1242,6 +1277,12 @@ msgstr ""
|
|||
msgid "FreeCell"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeCell with Two Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeCell with Zero Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "Frog"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1425,6 +1466,9 @@ msgstr ""
|
|||
msgid "Hanoi Puzzle 6"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hanoi Sequence"
|
||||
msgstr ""
|
||||
|
||||
msgid "Happy New Year"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1638,6 +1682,9 @@ msgstr ""
|
|||
msgid "Katrina's Game Relaxed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Kentish"
|
||||
msgstr ""
|
||||
|
||||
msgid "Khadga"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2310,7 +2357,7 @@ msgstr ""
|
|||
msgid "Mahjongg Stargate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Mahjongg Step Pyramid"
|
||||
msgid "Mahjongg Steps Pyramid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Mahjongg Stonehenge"
|
||||
|
@ -2652,6 +2699,15 @@ msgstr ""
|
|||
msgid "Northwest Territory"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not Shisen-Sho 14x6"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not Shisen-Sho 18x8"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not Shisen-Sho 24x12"
|
||||
msgstr ""
|
||||
|
||||
msgid "Number Ten"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2904,6 +2960,9 @@ msgstr ""
|
|||
msgid "Puss in the Corner"
|
||||
msgstr ""
|
||||
|
||||
msgid "Putt Putt"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pyramid"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2925,12 +2984,18 @@ msgstr ""
|
|||
msgid "Quadrangle"
|
||||
msgstr ""
|
||||
|
||||
msgid "Quadrille"
|
||||
msgstr ""
|
||||
|
||||
msgid "Quadruple Alliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "Quads"
|
||||
msgstr ""
|
||||
|
||||
msgid "Quads +"
|
||||
msgstr ""
|
||||
|
||||
msgid "Quartets"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3000,6 +3065,10 @@ msgstr ""
|
|||
msgid "Relax"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Relaxed Accordion"
|
||||
msgstr "Fisarmonica"
|
||||
|
||||
msgid "Relaxed FreeCell"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3015,6 +3084,9 @@ msgstr ""
|
|||
msgid "Relaxed Spider"
|
||||
msgstr ""
|
||||
|
||||
msgid "Relaxed Three Fir-trees"
|
||||
msgstr ""
|
||||
|
||||
msgid "Repair"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3228,13 +3300,13 @@ msgstr ""
|
|||
msgid "Shifting"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shisen-Sho (No Gra) 14x6"
|
||||
msgid "Shisen-Sho (No Gravity) 14x6"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shisen-Sho (No Gra) 18x8"
|
||||
msgid "Shisen-Sho (No Gravity) 18x8"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shisen-Sho (No Gra) 24x12"
|
||||
msgid "Shisen-Sho (No Gravity) 24x12"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shisen-Sho 14x6"
|
||||
|
@ -3423,15 +3495,15 @@ msgstr ""
|
|||
msgid "Stargate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Step Pyramid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Step-Up"
|
||||
msgstr ""
|
||||
|
||||
msgid "Steps"
|
||||
msgstr ""
|
||||
|
||||
msgid "Steps Pyramid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Steve"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3597,6 +3669,9 @@ msgstr ""
|
|||
msgid "Thieves of Egypt"
|
||||
msgstr ""
|
||||
|
||||
msgid "Thirteen Packs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Thirteen Up"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3864,6 +3939,9 @@ msgstr ""
|
|||
msgid "Waning Moon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wasatch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Washington's Favorite"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3968,4 +4046,3 @@ msgstr ""
|
|||
|
||||
msgid "Zodiac"
|
||||
msgstr ""
|
||||
|
||||
|
|
7415
po/it_pysol.po
|
@ -4,10 +4,11 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PySol 0.0.1\n"
|
||||
"POT-Creation-Date: Mon Mar 7 21:38:07 2011\n"
|
||||
"POT-Creation-Date: Thu Jul 18 18:43:59 2019\n"
|
||||
"PO-Revision-Date: 2010-12-16 23:56+0100\n"
|
||||
"Last-Translator: Jerzy Trzeciak <artusek@wp.pl>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"Language: pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
@ -705,6 +706,9 @@ msgstr ""
|
|||
msgid "Czarina"
|
||||
msgstr "Czarina"
|
||||
|
||||
msgid "Daddy Longlegs"
|
||||
msgstr ""
|
||||
|
||||
msgid "Danda"
|
||||
msgstr "Danda"
|
||||
|
||||
|
@ -1277,6 +1281,12 @@ msgstr "Free Napoleon"
|
|||
msgid "FreeCell"
|
||||
msgstr "FreeCell"
|
||||
|
||||
msgid "FreeCell with Two Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeCell with Zero Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "Frog"
|
||||
msgstr "Żaba"
|
||||
|
||||
|
|
7343
po/pl_pysol.po
6730
po/pysol.pot
|
@ -5,14 +5,17 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PySol 0.0.1\n"
|
||||
"POT-Creation-Date: Mon Mar 7 21:38:07 2011\n"
|
||||
"POT-Creation-Date: Thu Jul 18 18:43:59 2019\n"
|
||||
"PO-Revision-Date: 2007-09-05 17:43+0400\n"
|
||||
"Last-Translator: Скоморох <skomoroh@gmail.com>\n"
|
||||
"Language-Team: Russian <ru@li.org>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: utf-8\n"
|
||||
"Generated-By: ./scripts/all_games.py 0.1\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
msgid " 3x3 Matrix"
|
||||
msgstr "Матрица 3x3"
|
||||
|
@ -707,6 +710,10 @@ msgstr "Творог и сыворотка"
|
|||
msgid "Czarina"
|
||||
msgstr "Царевна"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Daddy Longlegs"
|
||||
msgstr "Тенистые аллеи"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Danda"
|
||||
msgstr "Алмаз"
|
||||
|
@ -1289,6 +1296,12 @@ msgstr "Свободный Наполеон"
|
|||
msgid "FreeCell"
|
||||
msgstr "Свободная ячейка"
|
||||
|
||||
msgid "FreeCell with Two Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "FreeCell with Zero Reserves"
|
||||
msgstr ""
|
||||
|
||||
msgid "Frog"
|
||||
msgstr "Лягушка"
|
||||
|
||||
|
|
7333
po/ru_pysol.po
|
@ -47,7 +47,7 @@ from pysollib.pysoltk import create_find_card_dialog
|
|||
from pysollib.pysoltk import create_solver_dialog
|
||||
from pysollib.settings import DEBUG
|
||||
from pysollib.settings import PACKAGE_URL, TITLE
|
||||
from pysollib.settings import TOP_TITLE
|
||||
from pysollib.settings import TOP_SIZE
|
||||
from pysollib.stats import FileStatsFormatter
|
||||
|
||||
|
||||
|
@ -381,7 +381,7 @@ class PysolMenubar(PysolMenubarTk):
|
|||
if self._cancelDrag():
|
||||
return
|
||||
if self.changed():
|
||||
if not self.game.areYouSure(_("Quit ") + TITLE):
|
||||
if not self.game.areYouSure(_("Quit %s") % TITLE):
|
||||
return
|
||||
self.game.endGame()
|
||||
self.game.quitGame()
|
||||
|
@ -445,7 +445,7 @@ class PysolMenubar(PysolMenubarTk):
|
|||
if not self.game.gsaveinfo.bookmarks:
|
||||
return
|
||||
if not self.game.areYouSure(_("Clear bookmarks"),
|
||||
_("Clear all bookmarks ?")):
|
||||
_("Clear all bookmarks?")):
|
||||
return
|
||||
self.game.gsaveinfo.bookmarks = {}
|
||||
self.game.updateMenus()
|
||||
|
@ -457,7 +457,7 @@ class PysolMenubar(PysolMenubarTk):
|
|||
return
|
||||
if self.changed(restart=1):
|
||||
if not self.game.areYouSure(_("Restart game"),
|
||||
_("Restart this game ?")):
|
||||
_("Restart this game?")):
|
||||
return
|
||||
self.game.restartGame()
|
||||
|
||||
|
@ -501,10 +501,11 @@ class PysolMenubar(PysolMenubarTk):
|
|||
if self._cancelDrag(break_pause=False):
|
||||
return
|
||||
game, gi = self.game, self.game.gameinfo
|
||||
t = " " + game.getGameNumber(format=1)
|
||||
cc = _("Comments for %s:\n\n") % (gi.name + t)
|
||||
kw = {'game': gi.name,
|
||||
'id': game.getGameNumber(format=1)}
|
||||
cc = _("Comments for %(game)s %(id)s:\n\n") % kw
|
||||
c = game.gsaveinfo.comment or cc
|
||||
d = EditTextDialog(game.top, _("Comments for ")+t, text=c)
|
||||
d = EditTextDialog(game.top, _("Comments for %(id)s") % kw, text=c)
|
||||
if d.status == 0 and d.button == 0:
|
||||
text = d.text
|
||||
if text.strip() == cc.strip():
|
||||
|
@ -525,8 +526,9 @@ class PysolMenubar(PysolMenubarTk):
|
|||
text=_("Error while writing to file"))
|
||||
else:
|
||||
d = MfxMessageDialog(
|
||||
self.top, title=TITLE+_(" Info"), bitmap="info",
|
||||
text=_("Comments were appended to\n\n") + fn)
|
||||
self.top, title=_("%s Info") % TITLE, bitmap="info",
|
||||
text=_("Comments were appended to\n\n%(filename)s")
|
||||
% {'filename': fn})
|
||||
self._setCommentMenu(bool(game.gsaveinfo.comment))
|
||||
|
||||
#
|
||||
|
@ -535,10 +537,10 @@ class PysolMenubar(PysolMenubarTk):
|
|||
|
||||
def _mStatsSave(self, player, filename, write_method):
|
||||
if player is None:
|
||||
text = _("Demo statistics")
|
||||
text = _("Demo statistics were appended to\n\n%(filename)s")
|
||||
filename = filename + "_demo"
|
||||
else:
|
||||
text = _("Your statistics")
|
||||
text = _("Your statistics were appended to\n\n%(filename)s")
|
||||
filename = os.path.join(self.app.dn.config, filename + ".txt")
|
||||
filename = os.path.normpath(filename)
|
||||
try:
|
||||
|
@ -549,8 +551,8 @@ class PysolMenubar(PysolMenubarTk):
|
|||
text=_("Error while writing to file"))
|
||||
else:
|
||||
MfxMessageDialog(
|
||||
self.top, title=TITLE+_(" Info"), bitmap="info",
|
||||
text=text + _(" were appended to\n\n") + filename)
|
||||
self.top, title=_("%s Info") % TITLE, bitmap="info",
|
||||
text=text % {'filename': filename})
|
||||
|
||||
def mPlayerStats(self, *args, **kw):
|
||||
mode = kw.get("mode", 101)
|
||||
|
@ -564,31 +566,40 @@ class PysolMenubar(PysolMenubarTk):
|
|||
d = Struct(status=-1, button=-1)
|
||||
if demo:
|
||||
player = None
|
||||
p0, p1, p2 = TITLE+_(" Demo"), TITLE+_(" Demo "), ""
|
||||
else:
|
||||
player = self.app.opt.player
|
||||
p0, p1, p2 = player, "", _(" for ") + player
|
||||
n = self.game.gameinfo.name
|
||||
# translation keywords
|
||||
transkw = {'app': TITLE,
|
||||
'player': player,
|
||||
'game': n,
|
||||
'tops': TOP_SIZE}
|
||||
#
|
||||
if mode == 100:
|
||||
d = Status_StatsDialog(self.top, game=self.game)
|
||||
elif mode == 101:
|
||||
header = p1 + _("Statistics for ") + n
|
||||
header = (_("%(app)s Demo Statistics for %(game)s") if demo
|
||||
else _("Statistics for %(game)s")) % transkw
|
||||
d = SingleGame_StatsDialog(
|
||||
self.top, header, self.app, player, gameid=self.game.id)
|
||||
gameid = d.selected_game
|
||||
elif mode == 102:
|
||||
header = p1 + _("Statistics") + p2
|
||||
header = (_("%(app)s Demo Statistics") if demo
|
||||
else _("Statistics for %(player)s")) % transkw
|
||||
d = AllGames_StatsDialog(self.top, header, self.app, player)
|
||||
gameid = d.selected_game
|
||||
elif mode == 103:
|
||||
header = p1 + _("Full log") + p2
|
||||
header = (_("%(app)s Demo Full log") if demo
|
||||
else _("Full log for %(player)s")) % transkw
|
||||
d = FullLog_StatsDialog(self.top, header, self.app, player)
|
||||
elif mode == 104:
|
||||
header = p1 + _("Session log") + p2
|
||||
header = (_("%(app)s Demo Session log") if demo
|
||||
else _("Session log for %(player)s")) % transkw
|
||||
d = SessionLog_StatsDialog(self.top, header, self.app, player)
|
||||
elif mode == 105:
|
||||
header = p1 + TOP_TITLE + _(" for ") + n
|
||||
# TRANSLATORS: eg. top 10 or top 5 results for a certain game
|
||||
header = (_("%(app)s Demo Top %(tops)d for %(game)s") if demo
|
||||
else _("Top %(tops)d for %(game)s")) % transkw
|
||||
d = Top_StatsDialog(
|
||||
self.top, header, self.app, player, gameid=self.game.id)
|
||||
elif mode == 106:
|
||||
|
@ -614,7 +625,8 @@ class PysolMenubar(PysolMenubarTk):
|
|||
# reset all player stats
|
||||
if self.game.areYouSure(
|
||||
_("Reset all statistics"),
|
||||
_("Reset ALL statistics and logs for player\n%s ?") % p0,
|
||||
_("Reset ALL statistics and logs for player\n" +
|
||||
"%(player)s?") % transkw,
|
||||
confirm=1, default=1
|
||||
):
|
||||
self.app.stats.resetStats(player, 0)
|
||||
|
@ -624,8 +636,8 @@ class PysolMenubar(PysolMenubarTk):
|
|||
# reset player stats for current game
|
||||
if self.game.areYouSure(
|
||||
_("Reset game statistics"),
|
||||
_('Reset statistics and logs ' +
|
||||
'for player\n%s\nand game\n%s ?') % (p0, n),
|
||||
_('Reset statistics and logs for player\n%(player)s\n'
|
||||
'and game\n%(game)s?') % transkw,
|
||||
confirm=1, default=1
|
||||
):
|
||||
self.app.stats.resetStats(player, self.game.id)
|
||||
|
|
|
@ -60,7 +60,7 @@ from pysollib.resource import Tile, TileManager
|
|||
from pysollib.settings import DEBUG
|
||||
from pysollib.settings import PACKAGE, VERSION_TUPLE, WIN_SYSTEM
|
||||
from pysollib.settings import TOOLKIT
|
||||
from pysollib.util import CARDSET, IMAGE_EXTENSIONS
|
||||
from pysollib.util import IMAGE_EXTENSIONS
|
||||
from pysollib.winsystems import TkSettings
|
||||
if TOOLKIT == 'tk':
|
||||
from pysollib.ui.tktile.solverdialog import destroy_solver_dialog
|
||||
|
@ -329,7 +329,8 @@ class Application:
|
|||
elif self.commandline.game is not None:
|
||||
gameid = self.gdb.getGameByName(self.commandline.game)
|
||||
if gameid is None:
|
||||
print_err(_("can't find game: ") + self.commandline.game)
|
||||
print_err(_("can't find game: %(game)s") % {
|
||||
'game': self.commandline.game})
|
||||
sys.exit(-1)
|
||||
else:
|
||||
self.nextgame.id = gameid
|
||||
|
@ -668,7 +669,7 @@ class Application:
|
|||
if progress is None:
|
||||
self.wm_save_state()
|
||||
self.wm_withdraw()
|
||||
title = _("Loading %s %s...") % (CARDSET, cs.name)
|
||||
title = _("Loading cardset %s...") % cs.name
|
||||
color = self.opt.colors['table']
|
||||
if self.tabletile_index > 0:
|
||||
color = "#008200"
|
||||
|
@ -678,7 +679,7 @@ class Application:
|
|||
images = Images(self.dataloader, cs)
|
||||
try:
|
||||
if not images.load(app=self, progress=progress):
|
||||
raise Exception("Invalid or damaged "+CARDSET)
|
||||
raise Exception("Invalid or damaged cardset")
|
||||
simages = SubsampledImages(images)
|
||||
if self.opt.save_cardsets:
|
||||
c = self.cardsets_cache.get(cs.type)
|
||||
|
@ -710,8 +711,8 @@ class Application:
|
|||
# images.destruct()
|
||||
destruct(images)
|
||||
MfxExceptionDialog(
|
||||
self.top, ex, title=CARDSET+_(" load error"),
|
||||
text=_("Error while loading ")+CARDSET)
|
||||
self.top, ex, title=_("Cardset load error"),
|
||||
text=_("Error while loading cardset"))
|
||||
self.intro.progress = progress
|
||||
if r and self.menubar is not None:
|
||||
self.menubar.updateBackgroundImagesMenu()
|
||||
|
@ -806,14 +807,14 @@ class Application:
|
|||
#
|
||||
t = self.checkCompatibleCardsetType(gi, self.cardset)
|
||||
MfxMessageDialog(
|
||||
self.top, title=_("Incompatible ")+CARDSET,
|
||||
self.top, title=_("Incompatible cardset"),
|
||||
bitmap="warning",
|
||||
text=_('''The currently selected %s %s
|
||||
text=_('''The currently selected cardset %(cardset)s
|
||||
is not compatible with the game
|
||||
%s
|
||||
%(game)s
|
||||
|
||||
Please select a %s type %s.
|
||||
''') % (CARDSET, self.cardset.name, gi.name, t[0], CARDSET),
|
||||
Please select a %(correct_type)s type cardset.
|
||||
''') % {'cardset': self.cardset.name, 'game': gi.name, 'correct_type': t[0]},
|
||||
strings=(_("&OK"),), default=0)
|
||||
cs = self.__selectCardsetDialog(t)
|
||||
if cs is None:
|
||||
|
@ -852,7 +853,7 @@ Please select a %s type %s.
|
|||
|
||||
def __selectCardsetDialog(self, t):
|
||||
cs = self.selectCardset(
|
||||
_("Please select a %s type %s") % (t[0], CARDSET),
|
||||
_("Please select a %s type cardset") % t[0],
|
||||
self.cardset.index)
|
||||
return cs
|
||||
|
||||
|
@ -1060,7 +1061,8 @@ Please select a %s type %s.
|
|||
except Exception as ex:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
print_err(_("error loading plugin %s: %s") % (n, ex))
|
||||
print_err(_("error loading plugin %(file)s: %(err)s") %
|
||||
{'file': n, 'err': ex})
|
||||
|
||||
#
|
||||
# init cardsets
|
||||
|
@ -1211,11 +1213,10 @@ Please select a %s type %s.
|
|||
d = os.path.join(dirname, name)
|
||||
if not os.path.isdir(d):
|
||||
continue
|
||||
f1 = os.path.join(d, "config.txt")
|
||||
f2 = os.path.join(d, "COPYRIGHT")
|
||||
if os.path.isfile(f1) and os.path.isfile(f2):
|
||||
f = os.path.join(d, "config.txt")
|
||||
if os.path.isfile(f):
|
||||
try:
|
||||
cs = self._readCardsetConfig(d, f1)
|
||||
cs = self._readCardsetConfig(d, f)
|
||||
if cs:
|
||||
# from pprint import pprint
|
||||
# print cs.name
|
||||
|
@ -1232,7 +1233,7 @@ Please select a %s type %s.
|
|||
fnames[cs.name] = 1
|
||||
else:
|
||||
print_err('fail _readCardsetConfig: %s %s'
|
||||
% (d, f1))
|
||||
% (d, f))
|
||||
pass
|
||||
except Exception:
|
||||
# traceback.print_exc()
|
||||
|
|
55
pysollib/game/dump.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
|
||||
# Copyright (C) 2003 Mt. Hood Playing Card Co.
|
||||
# Copyright (C) 2005-2009 Skomoroh
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pysollib.pysolrandom import random__str2long
|
||||
from pysollib.settings import PACKAGE
|
||||
from pysollib.settings import VERSION, VERSION_TUPLE
|
||||
|
||||
|
||||
def pysolDumpGame(game_, p, bookmark=0):
|
||||
game_.updateTime()
|
||||
assert 0 <= bookmark <= 2
|
||||
p.dump(PACKAGE)
|
||||
p.dump(VERSION)
|
||||
p.dump(VERSION_TUPLE)
|
||||
p.dump(bookmark)
|
||||
p.dump(game_.GAME_VERSION)
|
||||
p.dump(game_.id)
|
||||
#
|
||||
p.dump(random__str2long(game_.random.getSeedStr()))
|
||||
p.dump(game_.random.getstate())
|
||||
#
|
||||
p.dump(len(game_.allstacks))
|
||||
for stack in game_.allstacks:
|
||||
p.dump(len(stack.cards))
|
||||
for card in stack.cards:
|
||||
p.dump(card.id)
|
||||
p.dump(card.face_up)
|
||||
p.dump(game_.s.talon.round)
|
||||
p.dump(game_.finished)
|
||||
if 0 <= bookmark <= 1:
|
||||
p.dump(game_.saveinfo)
|
||||
p.dump(game_.gsaveinfo)
|
||||
p.dump(game_.moves)
|
||||
p.dump(game_.snapshots)
|
||||
if 0 <= bookmark <= 1:
|
||||
if bookmark == 0:
|
||||
game_.gstats.saved += 1
|
||||
p.dump(game_.gstats)
|
||||
p.dump(game_.stats)
|
||||
game_._saveGameHook(p)
|
||||
p.dump("EOF")
|
|
@ -27,6 +27,7 @@ from . import bakersdozen # noqa: F401
|
|||
from . import bakersgame # noqa: F401
|
||||
from . import beleagueredcastle # noqa: F401
|
||||
from . import bisley # noqa: F401
|
||||
from . import bisley13 # noqa: F401
|
||||
from . import braid # noqa: F401
|
||||
from . import bristol # noqa: F401
|
||||
from . import buffalobill # noqa: F401
|
||||
|
|
89
pysollib/games/bisley13.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- mode: python; coding: utf-8; -*-
|
||||
# ---------------------------------------------------------------------------##
|
||||
#
|
||||
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
|
||||
# Copyright (C) 2003 Mt. Hood Playing Card Co.
|
||||
# Copyright (C) 2005-2009 Skomoroh
|
||||
# Copyright (C) 2020 qunka
|
||||
# Modified by Shlomi Fish, 2020, while disclaiming all copyrights.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ---------------------------------------------------------------------------##
|
||||
|
||||
from pysollib.gamedb import GI, GameInfo, registerGame
|
||||
from pysollib.games.bisley import Bisley
|
||||
from pysollib.layout import Layout
|
||||
from pysollib.stack import \
|
||||
InitialDealTalonStack, \
|
||||
SS_FoundationStack, \
|
||||
UD_SS_RowStack
|
||||
from pysollib.util import KING, NO_RANK
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * Bisley 13
|
||||
# ************************************************************************
|
||||
|
||||
class Bisley13(Bisley):
|
||||
|
||||
#
|
||||
# game layout
|
||||
#
|
||||
|
||||
def createGame(self):
|
||||
# create layout
|
||||
l, s = Layout(self), self.s
|
||||
|
||||
# set window
|
||||
w, h = 2*l.XM+9*l.XS, max(2*(l.YM+l.YS+8*l.YOFFSET), l.YM+5*l.YS)
|
||||
self.setSize(w, h)
|
||||
|
||||
# create stacks
|
||||
x, y = l.XM, l.YM
|
||||
for i in range(7):
|
||||
s.rows.append(UD_SS_RowStack(x, y, self, base_rank=NO_RANK))
|
||||
x += l.XS
|
||||
x, y = l.XM, l.YM+l.YS+8*l.YOFFSET
|
||||
for i in range(6):
|
||||
s.rows.append(UD_SS_RowStack(x, y, self, base_rank=NO_RANK))
|
||||
x += l.XS
|
||||
y = l.YM
|
||||
for i in range(4):
|
||||
x = l.XM+7*l.XS+l.XM
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, i, max_move=0))
|
||||
x += l.XS
|
||||
s.foundations.append(SS_FoundationStack(x, y, self, i,
|
||||
base_rank=KING, max_move=0, dir=-1))
|
||||
y += l.YS
|
||||
|
||||
s.talon = InitialDealTalonStack(w-l.XS, h-l.YS, self)
|
||||
|
||||
# default
|
||||
l.defaultAll()
|
||||
|
||||
#
|
||||
# game overrides
|
||||
#
|
||||
|
||||
def startGame(self):
|
||||
self._startDealNumRows(3)
|
||||
self.s.talon.dealRow(rows=self.s.rows[4:13])
|
||||
self.s.talon.dealRow(rows=self.s.foundations[::2])
|
||||
|
||||
|
||||
# register the game
|
||||
registerGame(GameInfo(343001, Bisley13, "Bisley 13",
|
||||
GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0, GI.SL_MOSTLY_SKILL))
|
|
@ -120,7 +120,8 @@ class Matriarchy_Talon(WasteTalonStack):
|
|||
return
|
||||
WasteTalonStack.updateText(self, update_rounds=0)
|
||||
# t = "Round %d" % self.round
|
||||
t = _("Round %d/%d") % (self.round, self.max_rounds)
|
||||
t = _("Round %(round)d/%(max_rounds)d") % {
|
||||
'round': self.round, 'max_rounds': self.max_rounds}
|
||||
self.texts.rounds.config(text=t)
|
||||
t = _("Deal %d") % self.DEAL[self.round-1]
|
||||
self.texts.misc.config(text=t)
|
||||
|
|
|
@ -286,6 +286,9 @@ class Hanafuda_SequenceStack(Flower_OpenStack):
|
|||
return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
|
||||
return self.isHanafudaSequence([stackcards[-1], cards[0]])
|
||||
|
||||
def canMoveCards(self, cards):
|
||||
return self.basicCanMoveCards(cards) and self.isHanafudaSequence(cards)
|
||||
|
||||
|
||||
class Oonsoo_SequenceStack(Flower_OpenStack):
|
||||
|
||||
|
@ -298,6 +301,10 @@ class Oonsoo_SequenceStack(Flower_OpenStack):
|
|||
return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
|
||||
return self.isHanafudaSequence([stackcards[-1], cards[0]], 0)
|
||||
|
||||
def canMoveCards(self, cards):
|
||||
return (self.basicCanMoveCards(cards) and
|
||||
self.isHanafudaSequence(cards, 0))
|
||||
|
||||
|
||||
class FlowerClock_RowStack(Flower_OpenStack):
|
||||
|
||||
|
|
|
@ -40,17 +40,18 @@ from pysollib.settings import PACKAGE_URL, TITLE, TOOLKIT, VERSION
|
|||
def help_about(app, timeout=0, sound=True):
|
||||
if sound:
|
||||
app.audio.playSample("about")
|
||||
t = _("A Python Solitaire Game Collection\n")
|
||||
t = _("A Python Solitaire Game Collection")
|
||||
if app.miscrandom.random() < 0.8:
|
||||
t = _("A World Domination Project\n")
|
||||
t = _("A World Domination Project")
|
||||
strings = (_("&Nice"), _("&Credits..."))
|
||||
if timeout:
|
||||
strings = (_("&Enjoy"),)
|
||||
version = _("Version %s") % VERSION
|
||||
d = PysolAboutDialog(app, app.top, title=_("About ") + TITLE,
|
||||
d = PysolAboutDialog(app, app.top, title=_("About %s") % TITLE,
|
||||
timeout=timeout,
|
||||
text=_('''PySol Fan Club edition
|
||||
%s%s
|
||||
%(description)s
|
||||
%(versioninfo)s
|
||||
|
||||
Copyright (C) 1998 - 2003 Markus F.X.J. Oberhumer.
|
||||
Copyright (C) 2003 Mt. Hood Playing Card Co.
|
||||
|
@ -60,7 +61,8 @@ All Rights Reserved.
|
|||
PySol is free software distributed under the terms
|
||||
of the GNU General Public License.
|
||||
|
||||
For more information about this application visit''') % (t, version),
|
||||
For more information about this application visit''') %
|
||||
{'description': t, 'versioninfo': version},
|
||||
url=PACKAGE_URL,
|
||||
image=app.gimages.logos[2],
|
||||
strings=strings, default=0,
|
||||
|
@ -86,7 +88,7 @@ def help_credits(app, timeout=0, sound=True):
|
|||
t = "kivy"
|
||||
d = MfxMessageDialog(
|
||||
app.top, title=_("Credits"), timeout=timeout,
|
||||
text=TITLE+_(''' credits go to:
|
||||
text=_('''%(app)s credits go to:
|
||||
|
||||
Volker Weidner for getting me into Solitaire
|
||||
Guido van Rossum for the initial example program
|
||||
|
@ -95,8 +97,8 @@ Carl Larsson for the background music
|
|||
The Gnome AisleRiot team for parts of the documentation
|
||||
Natascha
|
||||
|
||||
The Python, %s, SDL & Linux crews
|
||||
for making this program possible''') % t,
|
||||
The Python, %(gui_library)s, SDL & Linux crews
|
||||
for making this program possible''') % {'app': TITLE, 'gui_library': t},
|
||||
image=app.gimages.logos[3], image_side="right",
|
||||
separator=True)
|
||||
return d.status
|
||||
|
@ -122,8 +124,8 @@ def help_html(app, document, dir_, top=None):
|
|||
document, dir_ = "index.html", "html"
|
||||
help_html_index = app.dataloader.findFile(document, dir_)
|
||||
except EnvironmentError:
|
||||
MfxMessageDialog(app.top, title=TITLE + _(" HTML Problem"),
|
||||
text=_("Cannot find help document\n") + document,
|
||||
MfxMessageDialog(app.top, title=_("%s HTML Problem") % TITLE,
|
||||
text=_("Cannot find help document\n%s") % document,
|
||||
bitmap="warning")
|
||||
return None
|
||||
# print doc, help_html_index
|
||||
|
@ -136,7 +138,7 @@ def help_html(app, document, dir_, top=None):
|
|||
viewer.display(doc, relpath=0)
|
||||
except Exception:
|
||||
# traceback.print_exc()
|
||||
top = make_help_toplevel(app, title=TITLE+_(" Help"))
|
||||
top = make_help_toplevel(app, title=_("%s Help") % TITLE)
|
||||
if top.winfo_screenwidth() < 800 or top.winfo_screenheight() < 600:
|
||||
# maximized = 1
|
||||
top.wm_minsize(300, 150)
|
||||
|
|
|
@ -27,6 +27,11 @@ import os
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
import jnius
|
||||
except ImportError:
|
||||
jnius = None
|
||||
|
||||
import pysollib.settings
|
||||
|
||||
# ************************************************************************
|
||||
|
@ -36,27 +41,31 @@ import pysollib.settings
|
|||
|
||||
def init():
|
||||
|
||||
if os.name == 'nt' and 'LANG' not in os.environ:
|
||||
try:
|
||||
loc = locale.getdefaultlocale()
|
||||
os.environ['LANG'] = loc[0]
|
||||
except Exception:
|
||||
pass
|
||||
# locale.setlocale(locale.LC_ALL, '')
|
||||
if 'LANG' not in os.environ:
|
||||
if os.name == 'nt':
|
||||
lang, enc = locale.getdefaultlocale()
|
||||
os.environ['LANG'] = lang
|
||||
elif jnius: # android
|
||||
Locale = jnius.autoclass('java.util.Locale')
|
||||
os.environ['LANG'] = Locale.getDefault().getLanguage()
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
# install gettext
|
||||
# locale_dir = 'locale'
|
||||
locale_dir = None
|
||||
if os.path.isdir(sys.path[0]):
|
||||
d = os.path.join(sys.path[0], 'locale')
|
||||
else:
|
||||
# i.e. library.zip
|
||||
d = os.path.join(os.path.dirname(sys.path[0]), 'locale')
|
||||
if os.path.exists(d) and os.path.isdir(d):
|
||||
locale_dir = d
|
||||
# if locale_dir: locale_dir = os.path.normpath(locale_dir)
|
||||
# gettext.install('pysol', locale_dir, unicode=True) # ngettext don't work
|
||||
gettext.bindtextdomain('pysol', locale_dir)
|
||||
locale_locations = (
|
||||
# locale/ next to the pysol.py script
|
||||
sys.path[0],
|
||||
# locale/ next to library.zip (py2exe)
|
||||
os.path.dirname(sys.path[0]),
|
||||
# locale/ in curdir (works for e.g. py2app)
|
||||
os.curdir)
|
||||
# leaving the domain unbound means sys.prefix+'/share/locale'
|
||||
|
||||
for par in locale_locations:
|
||||
locale_dir = os.path.join(par, 'locale')
|
||||
if os.path.isdir(locale_dir):
|
||||
gettext.bindtextdomain('pysol', locale_dir)
|
||||
break
|
||||
|
||||
gettext.textdomain('pysol')
|
||||
|
||||
# debug
|
||||
|
|
|
@ -52,6 +52,8 @@ from kivy.uix.treeview import TreeViewLabel
|
|||
from kivy.uix.widget import Widget
|
||||
from kivy.utils import platform
|
||||
|
||||
from pysollib.kivy.androidperms import requestStoragePerm
|
||||
|
||||
# =============================================================================
|
||||
|
||||
|
||||
|
@ -671,7 +673,10 @@ class LRectangle(Widget, LBase):
|
|||
event = LEvent()
|
||||
event.x = ppos[0]
|
||||
event.y = ppos[1]
|
||||
self.group.bindings['<1>'](event)
|
||||
if touch.is_double_tap:
|
||||
self.group.bindings['<Double-1>'](event)
|
||||
else:
|
||||
self.group.bindings['<1>'](event)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -712,6 +717,10 @@ class LImageItem(BoxLayout, LBase):
|
|||
if self.group and '<1>' in self.group.bindings:
|
||||
self.group.bindings['<1>'](event)
|
||||
|
||||
def send_event_pressed_double_1(self, event):
|
||||
if self.group and '<Double-1>' in self.group.bindings:
|
||||
self.group.bindings['<Double-1>'](event)
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
|
||||
if self.collide_point(*touch.pos):
|
||||
|
@ -734,7 +743,10 @@ class LImageItem(BoxLayout, LBase):
|
|||
event.y = ppos[1]
|
||||
self.dragstart = touch.pos
|
||||
event.cardid = i
|
||||
self.send_event_pressed_1(event)
|
||||
if touch.is_double_tap:
|
||||
self.send_event_pressed_double_1(event)
|
||||
else:
|
||||
self.send_event_pressed_1(event)
|
||||
return True
|
||||
|
||||
if self.group is not None:
|
||||
|
@ -1168,7 +1180,7 @@ class LMenu(ActionView, LBase):
|
|||
class MyActionPrev(ActionPrevious, LBase):
|
||||
pass
|
||||
|
||||
kw['app_icon'] = 'data/images/misc/pysol01.png'
|
||||
kw['app_icon'] = 'data/images/icons/48x48/pysol.png'
|
||||
kw['with_previous'] = prev
|
||||
kw['size_hint'] = (.01, 1)
|
||||
self.ap = MyActionPrev(**kw)
|
||||
|
@ -1541,6 +1553,12 @@ class LMainWindow(BoxLayout, LTkBase):
|
|||
self.workStack = LStack()
|
||||
self.app = None
|
||||
|
||||
'''
|
||||
from kivy.graphics import opengl_utils
|
||||
print('OPENGL support:')
|
||||
print(opengl_utils.gl_get_extensions())
|
||||
'''
|
||||
|
||||
# self.touches = []
|
||||
|
||||
# beispiel zu canvas (hintergrund)
|
||||
|
@ -1779,6 +1797,8 @@ class LApp(App):
|
|||
self.mainloop = self.app.mainproc() # Einrichten
|
||||
self.mainloop.send(None) # Spielprozess starten
|
||||
logging.info("LApp: on_start processed")
|
||||
# Android: Request missing android permissions.
|
||||
requestStoragePerm()
|
||||
|
||||
def on_stop(self):
|
||||
# Achtung wird u.U. 2 mal aufgerufen !!!
|
||||
|
@ -1832,12 +1852,6 @@ class LApp(App):
|
|||
except Exception:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
# save comments
|
||||
try:
|
||||
app.saveComments()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
logging.info("LApp: on_pause - gamesaved")
|
||||
|
||||
logging.info("LApp: on_pause, Window.size=%s" % str(Window.size))
|
||||
|
|
64
pysollib/kivy/androidperms.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
import logging
|
||||
try:
|
||||
import jnius
|
||||
except ImportError:
|
||||
jnius = None
|
||||
|
||||
# link address of related support library:
|
||||
# https://maven.google.com/com/android/support/support-v4/24.1.1/support-v4-24.1.1.aar
|
||||
|
||||
# inspired by stackoverflow.com/questions/47510030/
|
||||
# as functools (reduce,partial,map) do not seem to work in python3 on android,
|
||||
# implemented in a classic functional way.
|
||||
# LB190927.
|
||||
# wait loop removed. (Implement it in external code if needed.)
|
||||
# LB191011.
|
||||
|
||||
|
||||
class AndroidPerms(object):
|
||||
def __init__(self):
|
||||
if jnius is None:
|
||||
return
|
||||
self.PythonActivity = jnius.autoclass(
|
||||
'org.kivy.android.PythonActivity')
|
||||
self.Compat = jnius.autoclass(
|
||||
'android.support.v4.content.ContextCompat')
|
||||
self.currentActivity = jnius.cast(
|
||||
'android.app.Activity', self.PythonActivity.mActivity)
|
||||
|
||||
def getPerm(self, permission):
|
||||
if jnius is None:
|
||||
return True
|
||||
p = self.Compat.checkSelfPermission(self.currentActivity, permission)
|
||||
return p == 0
|
||||
|
||||
# check actual permissions
|
||||
def getPerms(self, permissions):
|
||||
if jnius is None:
|
||||
return True
|
||||
haveperms = True
|
||||
for perm in permissions:
|
||||
haveperms = haveperms and self.getPerm(perm)
|
||||
return haveperms
|
||||
|
||||
# invoke the permissions dialog
|
||||
def requestPerms(self, permissions):
|
||||
if jnius is None:
|
||||
return True
|
||||
logging.info("androidperms: invoke permission dialog")
|
||||
self.currentActivity.requestPermissions(permissions, 0)
|
||||
return
|
||||
|
||||
|
||||
def getStoragePerm():
|
||||
ap = AndroidPerms()
|
||||
return ap.getPerms(
|
||||
["android.permission.WRITE_EXTERNAL_STORAGE"])
|
||||
|
||||
|
||||
def requestStoragePerm():
|
||||
ap = AndroidPerms()
|
||||
# ap.requestPerms(
|
||||
# ["android.permission.READ_EXTERNAL_STORAGE","android.permission.WRITE_EXTERNAL_STORAGE"])
|
||||
ap.requestPerms(
|
||||
["android.permission.WRITE_EXTERNAL_STORAGE"])
|
|
@ -48,10 +48,10 @@ from pysollib.kivy.tkutil import after_idle
|
|||
from pysollib.kivy.tkutil import bind
|
||||
from pysollib.mfxutil import Struct
|
||||
from pysollib.mygettext import _
|
||||
from pysollib.pysoltk import MfxMessageDialog
|
||||
from pysollib.pysoltk import connect_game_find_card_dialog
|
||||
from pysollib.settings import SELECT_GAME_MENU
|
||||
from pysollib.settings import TITLE
|
||||
from pysollib.util import CARDSET
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
|
@ -177,34 +177,34 @@ class MainMenuDialog(LMenuDialog):
|
|||
def buildTree(self, tv, node):
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="File",
|
||||
text=_("File"),
|
||||
command=self.make_game_command(self.menubar.mFileMenuDialog)))
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="Games",
|
||||
text=_("Games"),
|
||||
command=self.make_game_command(
|
||||
self.menubar.mSelectGameDialog)))
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="Tools",
|
||||
text=_("Tools"),
|
||||
command=self.make_game_command(self.menubar.mEditMenuDialog)))
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="Statistics",
|
||||
text=_("Statistics"),
|
||||
command=self.make_game_command(self.menubar.mGameMenuDialog)))
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="Assist",
|
||||
text=_("Assist"),
|
||||
command=self.make_game_command(
|
||||
self.menubar.mAssistMenuDialog)))
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="Options",
|
||||
text=_("Options"),
|
||||
command=self.make_game_command(
|
||||
self.menubar.mOptionsMenuDialog)))
|
||||
rg = tv.add_node(
|
||||
LTreeNode(
|
||||
text="Help",
|
||||
text=_("Help"),
|
||||
command=self.make_game_command(self.menubar.mHelpMenuDialog)))
|
||||
del rg
|
||||
|
||||
|
@ -225,7 +225,7 @@ class FileMenuDialog(LMenuDialog):
|
|||
|
||||
def buildTree(self, tv, node):
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Recent games'))
|
||||
LTreeNode(text=_('Recent games')))
|
||||
# Recent Liste
|
||||
recids = self.app.opt.recent_gameid
|
||||
# recgames = []
|
||||
|
@ -238,12 +238,12 @@ class FileMenuDialog(LMenuDialog):
|
|||
LTreeNode(text=gi.name, command=command), rg)
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Favorite games'))
|
||||
LTreeNode(text=_('Favorite games')))
|
||||
if rg:
|
||||
tv.add_node(LTreeNode(
|
||||
text='<Add>', command=self.menubar.mAddFavor), rg)
|
||||
text=_('<Add>'), command=self.menubar.mAddFavor), rg)
|
||||
tv.add_node(LTreeNode(
|
||||
text='<Remove>', command=self.menubar.mDelFavor), rg)
|
||||
text=_('<Remove>'), command=self.menubar.mDelFavor), rg)
|
||||
|
||||
# Recent Liste
|
||||
favids = self.app.opt.favorite_gameid
|
||||
|
@ -257,12 +257,12 @@ class FileMenuDialog(LMenuDialog):
|
|||
LTreeNode(text=gi.name, command=command), rg)
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Load', command=self.menubar.mOpen))
|
||||
text=_('Load'), command=self.menubar.mOpen))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Save', command=self.menubar.mSaveAs))
|
||||
text=_('Save'), command=self.menubar.mSaveAs))
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Quit', command=self.menubar.mHoldAndQuit))
|
||||
text=_('Quit'), command=self.menubar.mHoldAndQuit))
|
||||
|
||||
# ************************************************************************
|
||||
|
||||
|
@ -289,36 +289,36 @@ class EditMenuDialog(LMenuDialog): # Tools
|
|||
|
||||
def buildTree(self, tv, node):
|
||||
tv.add_node(LTreeNode(
|
||||
text='New game', command=self.menubar.mNewGame))
|
||||
text=_('New game'), command=self.menubar.mNewGame))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Restart game', command=self.menubar.mRestart))
|
||||
text=_('Restart game'), command=self.menubar.mRestart))
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Undo', command=self.menubar.mUndo))
|
||||
text=_('Undo'), command=self.menubar.mUndo))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Redo', command=self.menubar.mRedo))
|
||||
text=_('Redo'), command=self.menubar.mRedo))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Redo all', command=self.menubar.mRedoAll))
|
||||
text=_('Redo all'), command=self.menubar.mRedoAll))
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Auto drop', command=self.menubar.mDrop))
|
||||
text=_('Auto drop'), command=self.menubar.mDrop))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Shuffle tiles', command=self.menubar.mShuffle))
|
||||
text=_('Shuffle tiles'), command=self.menubar.mShuffle))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Deal cards', command=self.menubar.mDeal))
|
||||
text=_('Deal cards'), command=self.menubar.mDeal))
|
||||
|
||||
self.addCheckNode(tv, None,
|
||||
'Pause',
|
||||
_('Pause'),
|
||||
self.menubar.tkopt.pause,
|
||||
self.menubar.mPause)
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Load game', command=self.menubar.mOpen))
|
||||
text=_('Load game'), command=self.menubar.mOpen))
|
||||
tv.add_node(LTreeNode(
|
||||
text='Save game', command=self.menubar.mSaveAs))
|
||||
text=_('Save game'), command=self.menubar.mSaveAs))
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Help', command=self.menubar.mHelpRules))
|
||||
text=_('Help'), command=self.menubar.mHelpRules))
|
||||
|
||||
# -------------------------------------------
|
||||
# TBD ?
|
||||
|
@ -369,7 +369,7 @@ class GameMenuDialog(LMenuDialog):
|
|||
|
||||
def buildTree(self, tv, node):
|
||||
tv.add_node(LTreeNode(
|
||||
text='Current game ...',
|
||||
text=_('Current game...'),
|
||||
command=self.make_command(101, self.menubar.mPlayerStats)), None)
|
||||
|
||||
# tv.add_node(LTreeNode(
|
||||
|
@ -432,16 +432,16 @@ class AssistMenuDialog(LMenuDialog):
|
|||
|
||||
def buildTree(self, tv, node):
|
||||
tv.add_node(LTreeNode(
|
||||
text='Hint', command=self.menubar.mHint))
|
||||
text=_('Hint'), command=self.menubar.mHint))
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Highlight piles', command=self.menubar.mHighlightPiles))
|
||||
text=_('Highlight piles'), command=self.menubar.mHighlightPiles))
|
||||
|
||||
# tv.add_node(LTreeNode(
|
||||
# text='Find Card', command=self.menubar.mFindCard))
|
||||
|
||||
tv.add_node(LTreeNode(
|
||||
text='Demo', command=self.menubar.mDemo))
|
||||
text=_('Demo'), command=self.menubar.mDemo))
|
||||
|
||||
# -------------------------------------------
|
||||
# TBD. How ?
|
||||
|
@ -507,27 +507,27 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Automatic play settings
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Automatic play'))
|
||||
LTreeNode(text=_('Automatic play')))
|
||||
if rg:
|
||||
self.addCheckNode(tv, rg,
|
||||
'Auto face up',
|
||||
_('Auto face up'),
|
||||
self.menubar.tkopt.autofaceup,
|
||||
self.menubar.mOptAutoFaceUp)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Auto drop',
|
||||
_('Auto drop'),
|
||||
self.menubar.tkopt.autodrop,
|
||||
self.menubar.mOptAutoDrop)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Auto deal',
|
||||
_('Auto deal'),
|
||||
self.menubar.tkopt.autodeal,
|
||||
self.menubar.mOptAutoDeal)
|
||||
|
||||
# submenu.add_separator()
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Quick play',
|
||||
_('Quick play'),
|
||||
self.menubar.tkopt.quickplay,
|
||||
self.menubar.mOptQuickPlay)
|
||||
|
||||
|
@ -535,214 +535,245 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Player assistance
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Assist level'))
|
||||
LTreeNode(text=_('Assist level')))
|
||||
if rg:
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable undo',
|
||||
_('Enable undo'),
|
||||
self.menubar.tkopt.undo,
|
||||
self.menubar.mOptEnableUndo)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable bookmarks',
|
||||
_('Enable bookmarks'),
|
||||
self.menubar.tkopt.bookmarks,
|
||||
self.menubar.mOptEnableBookmarks)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable hint',
|
||||
_('Enable hint'),
|
||||
self.menubar.tkopt.hint,
|
||||
self.menubar.mOptEnableHint)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable shuffle',
|
||||
_('Enable shuffle'),
|
||||
self.menubar.tkopt.shuffle,
|
||||
self.menubar.mOptEnableShuffle)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable highlight piles',
|
||||
_('Enable highlight piles'),
|
||||
self.menubar.tkopt.highlight_piles,
|
||||
self.menubar.mOptEnableHighlightPiles)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable highlight cards',
|
||||
_('Enable highlight cards'),
|
||||
self.menubar.tkopt.highlight_cards,
|
||||
self.menubar.mOptEnableHighlightCards)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable highlight same rank',
|
||||
_('Enable highlight same rank'),
|
||||
self.menubar.tkopt.highlight_samerank,
|
||||
self.menubar.mOptEnableHighlightSameRank)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Highlight no matching',
|
||||
_('Highlight no matching'),
|
||||
self.menubar.tkopt.highlight_not_matching,
|
||||
self.menubar.mOptEnableHighlightNotMatching)
|
||||
|
||||
# submenu.add_separator()
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Show removed tiles (in Mahjongg games)',
|
||||
_('Show removed tiles (in Mahjongg games)'),
|
||||
self.menubar.tkopt.mahjongg_show_removed,
|
||||
self.menubar.mOptMahjonggShowRemoved)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Show hint arrow (in Shisen-Sho games)',
|
||||
_('Show hint arrow (in Shisen-Sho games)'),
|
||||
self.menubar.tkopt.shisen_show_hint,
|
||||
self.menubar.mOptShisenShowHint)
|
||||
|
||||
# submenu.add_separator()
|
||||
|
||||
# -------------------------------------------
|
||||
# Language options
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text=_('Language')))
|
||||
if rg:
|
||||
self.addRadioNode(tv, rg,
|
||||
_('Default'),
|
||||
self.menubar.tkopt.language, '',
|
||||
self.menubar.mOptLanguage)
|
||||
self.addRadioNode(tv, rg,
|
||||
_('English'),
|
||||
self.menubar.tkopt.language, 'en',
|
||||
self.menubar.mOptLanguage)
|
||||
self.addRadioNode(tv, rg,
|
||||
_('German'),
|
||||
self.menubar.tkopt.language, 'de',
|
||||
self.menubar.mOptLanguage)
|
||||
self.addRadioNode(tv, rg,
|
||||
_('Italian'),
|
||||
self.menubar.tkopt.language, 'it',
|
||||
self.menubar.mOptLanguage)
|
||||
self.addRadioNode(tv, rg,
|
||||
_('Polish'),
|
||||
self.menubar.tkopt.language, 'pl',
|
||||
self.menubar.mOptLanguage)
|
||||
self.addRadioNode(tv, rg,
|
||||
_('Russian'),
|
||||
self.menubar.tkopt.language, 'ru',
|
||||
self.menubar.mOptLanguage)
|
||||
|
||||
# -------------------------------------------
|
||||
# Sound options
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Sound'))
|
||||
LTreeNode(text=_('Sound')))
|
||||
if rg:
|
||||
self.addCheckNode(tv, rg,
|
||||
'Enable',
|
||||
_('Enable'),
|
||||
self.menubar.tkopt.sound,
|
||||
self.menubar.mOptSoundDialog)
|
||||
|
||||
rg1 = tv.add_node(
|
||||
LTreeNode(text='Volume'), rg)
|
||||
LTreeNode(text=_('Volume')), rg)
|
||||
if rg1:
|
||||
self.addRadioNode(tv, rg1,
|
||||
'100%',
|
||||
_('100%'),
|
||||
self.menubar.tkopt.sound_sample_volume, 100,
|
||||
self.menubar.mOptSoundSampleVol)
|
||||
self.addRadioNode(tv, rg1,
|
||||
'75%',
|
||||
_('75%'),
|
||||
self.menubar.tkopt.sound_sample_volume, 75,
|
||||
self.menubar.mOptSoundSampleVol)
|
||||
self.addRadioNode(tv, rg1,
|
||||
'50%',
|
||||
_('50%'),
|
||||
self.menubar.tkopt.sound_sample_volume, 50,
|
||||
self.menubar.mOptSoundSampleVol)
|
||||
self.addRadioNode(tv, rg1,
|
||||
'25%',
|
||||
_('25%'),
|
||||
self.menubar.tkopt.sound_sample_volume, 25,
|
||||
self.menubar.mOptSoundSampleVol)
|
||||
|
||||
rg1 = tv.add_node(
|
||||
LTreeNode(text='Samples'), rg)
|
||||
LTreeNode(text=_('Samples')), rg)
|
||||
if rg1:
|
||||
key = 'areyousure'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'are you sure',
|
||||
_('are you sure'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'autodrop'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'auto drop',
|
||||
_('auto drop'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'autoflip'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'auto flip',
|
||||
_('auto flip'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'autopilotlost'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'auto pilot lost',
|
||||
_('auto pilot lost'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'autopilotwon'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'auto pilot won',
|
||||
_('auto pilot won'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'deal'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'deal',
|
||||
_('deal'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'dealwaste'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'deal waste',
|
||||
_('deal waste'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'droppair'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'drop pair',
|
||||
_('drop pair'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'drop'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'drop',
|
||||
_('drop'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'flip'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'flip',
|
||||
_('flip'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'move'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'move',
|
||||
_('move'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'nomove'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'no move',
|
||||
_('no move'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'redo'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'redo',
|
||||
_('redo'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'startdrag'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'start drag',
|
||||
_('start drag'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'turnwaste'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'turn waste',
|
||||
_('turn waste'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'undo'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'undo',
|
||||
_('undo'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'gamefinished'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'game finished',
|
||||
_('game finished'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'gamelost'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'game lost',
|
||||
_('game lost'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'gameperfect'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'game perfect',
|
||||
_('game perfect'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
key = 'gamewon'
|
||||
self.addCheckNode(
|
||||
tv, rg1,
|
||||
'game won',
|
||||
_('game won'),
|
||||
self.menubar.tkopt.sound_sample_vars[key],
|
||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||
|
||||
|
@ -750,7 +781,7 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Cardsets and card backside options
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Cardsets'))
|
||||
LTreeNode(text=_('Cardsets')))
|
||||
if rg:
|
||||
self.menubar.tkopt.cardset.set(self.app.cardset.index)
|
||||
|
||||
|
@ -790,45 +821,45 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Table background settings
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Table'))
|
||||
LTreeNode(text=_('Table')))
|
||||
if rg:
|
||||
rg1 = tv.add_node(
|
||||
LTreeNode(text='Solid colors'), rg)
|
||||
LTreeNode(text=_('Solid colors')), rg)
|
||||
if rg1:
|
||||
key = 'table'
|
||||
self.addRadioNode(
|
||||
tv, rg1,
|
||||
'Blue',
|
||||
_('Blue'),
|
||||
self.menubar.tkopt.color_vars[key], '#0082df',
|
||||
self.menubar.mOptTableColor)
|
||||
self.addRadioNode(
|
||||
tv, rg1,
|
||||
'Green',
|
||||
_('Green'),
|
||||
self.menubar.tkopt.color_vars[key], '#008200',
|
||||
self.menubar.mOptTableColor)
|
||||
self.addRadioNode(
|
||||
tv, rg1,
|
||||
'Navy',
|
||||
_('Navy'),
|
||||
self.menubar.tkopt.color_vars[key], '#000086',
|
||||
self.menubar.mOptTableColor)
|
||||
self.addRadioNode(
|
||||
tv, rg1,
|
||||
'Olive',
|
||||
_('Olive'),
|
||||
self.menubar.tkopt.color_vars[key], '#868200',
|
||||
self.menubar.mOptTableColor)
|
||||
self.addRadioNode(
|
||||
tv, rg1,
|
||||
'Orange',
|
||||
_('Orange'),
|
||||
self.menubar.tkopt.color_vars[key], '#f79600',
|
||||
self.menubar.mOptTableColor)
|
||||
self.addRadioNode(
|
||||
tv, rg1,
|
||||
'Teal',
|
||||
_('Teal'),
|
||||
self.menubar.tkopt.color_vars[key], '#008286',
|
||||
self.menubar.mOptTableColor)
|
||||
|
||||
rg1 = tv.add_node(
|
||||
LTreeNode(text='Tiles and Images'), rg)
|
||||
LTreeNode(text=_('Tiles and Images')), rg)
|
||||
|
||||
if rg1:
|
||||
tm = self.app.tabletile_manager
|
||||
|
@ -848,30 +879,30 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Card view options
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Card view'))
|
||||
LTreeNode(text=_('Card view')))
|
||||
if rg:
|
||||
self.addCheckNode(tv, rg,
|
||||
'Card shadow',
|
||||
_('Card shadow'),
|
||||
self.menubar.tkopt.shadow,
|
||||
self.menubar.mOptShadow)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Shade legal moves',
|
||||
_('Shade legal moves'),
|
||||
self.menubar.tkopt.shade,
|
||||
self.menubar.mOptShade)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Negative cards bottom',
|
||||
_('Negative cards bottom'),
|
||||
self.menubar.tkopt.negative_bottom,
|
||||
self.menubar.mOptNegativeBottom)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Shrink face-down cards',
|
||||
_('Shrink face-down cards'),
|
||||
self.menubar.tkopt.shrink_face_down,
|
||||
self.menubar.mOptShrinkFaceDown)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Shade filled stacks',
|
||||
_('Shade filled stacks'),
|
||||
self.menubar.tkopt.shade_filled_stacks,
|
||||
self.menubar.mOptShadeFilledStacks)
|
||||
|
||||
|
@ -879,47 +910,47 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Animation settins
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Animations'))
|
||||
LTreeNode(text=_('Animations')))
|
||||
if rg:
|
||||
self.addRadioNode(tv, rg,
|
||||
'None',
|
||||
_('None'),
|
||||
self.menubar.tkopt.animations, 0,
|
||||
self.menubar.mOptAnimations)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Very fast',
|
||||
_('Very fast'),
|
||||
self.menubar.tkopt.animations, 1,
|
||||
self.menubar.mOptAnimations)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Fast',
|
||||
_('Fast'),
|
||||
self.menubar.tkopt.animations, 2,
|
||||
self.menubar.mOptAnimations)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Medium',
|
||||
_('Medium'),
|
||||
self.menubar.tkopt.animations, 3,
|
||||
self.menubar.mOptAnimations)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Slow',
|
||||
_('Slow'),
|
||||
self.menubar.tkopt.animations, 4,
|
||||
self.menubar.mOptAnimations)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Very slow',
|
||||
_('Very slow'),
|
||||
self.menubar.tkopt.animations, 5,
|
||||
self.menubar.mOptAnimations)
|
||||
|
||||
# submenu.add_separator()
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Redeal animation',
|
||||
_('Redeal animation'),
|
||||
self.menubar.tkopt.redeal_animation,
|
||||
self.menubar.mRedealAnimation)
|
||||
|
||||
self.addCheckNode(tv, rg,
|
||||
'Winning animation',
|
||||
_('Winning animation'),
|
||||
self.menubar.tkopt.win_animation,
|
||||
self.menubar.mWinAnimation)
|
||||
|
||||
|
@ -927,15 +958,15 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Touch mode settings
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Touch mode'))
|
||||
LTreeNode(text=_('Touch mode')))
|
||||
if rg:
|
||||
self.addRadioNode(tv, rg,
|
||||
'Drag-and-Drop',
|
||||
_('Drag-and-Drop'),
|
||||
self.menubar.tkopt.mouse_type, 'drag-n-drop',
|
||||
self.menubar.mOptMouseType)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Point-and-Click',
|
||||
_('Point-and-Click'),
|
||||
self.menubar.tkopt.mouse_type, 'point-n-click',
|
||||
self.menubar.mOptMouseType)
|
||||
|
||||
|
@ -969,10 +1000,10 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# Toolbar options
|
||||
|
||||
rg = tv.add_node(
|
||||
LTreeNode(text='Toolbar'))
|
||||
LTreeNode(text=_('Toolbar')))
|
||||
if rg:
|
||||
self.addRadioNode(tv, rg,
|
||||
'Hide',
|
||||
_('Hide'),
|
||||
self.menubar.tkopt.toolbar, 0,
|
||||
self.menubar.mOptToolbar)
|
||||
|
||||
|
@ -987,11 +1018,11 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# self.menubar.mOptToolbar)
|
||||
|
||||
self.addRadioNode(tv, rg,
|
||||
'Left',
|
||||
_('Left'),
|
||||
self.menubar.tkopt.toolbar, 3,
|
||||
self.menubar.mOptToolbar)
|
||||
self.addRadioNode(tv, rg,
|
||||
'Right',
|
||||
_('Right'),
|
||||
self.menubar.tkopt.toolbar, 4,
|
||||
self.menubar.mOptToolbar)
|
||||
|
||||
|
@ -1028,12 +1059,12 @@ class OptionsMenuDialog(LMenuDialog):
|
|||
# self.menubar.mOptDemoLogo)
|
||||
|
||||
self.addCheckNode(tv, None,
|
||||
'Startup splash screen',
|
||||
_('Startup splash screen'),
|
||||
self.menubar.tkopt.splashscreen,
|
||||
self.menubar.mOptSplashscreen)
|
||||
|
||||
self.addCheckNode(tv, None,
|
||||
'Winning splash',
|
||||
_('Winning splash'),
|
||||
self.menubar.tkopt.display_win_message,
|
||||
self.menubar.mWinDialog)
|
||||
|
||||
|
@ -1056,23 +1087,23 @@ class HelpMenuDialog(LMenuDialog):
|
|||
def buildTree(self, tv, node):
|
||||
tv.add_node(
|
||||
LTreeNode(
|
||||
text='Contents',
|
||||
text=_('Contents'),
|
||||
command=self.make_help_command(self.menubar.mHelp)))
|
||||
tv.add_node(
|
||||
LTreeNode(
|
||||
text='How to play',
|
||||
text=_('How to play'),
|
||||
command=self.make_help_command(self.menubar.mHelpHowToPlay)))
|
||||
tv.add_node(
|
||||
LTreeNode(
|
||||
text='Rules for this game',
|
||||
text=_('Rules for this game'),
|
||||
command=self.make_help_command(self.menubar.mHelpRules)))
|
||||
tv.add_node(
|
||||
LTreeNode(
|
||||
text='License terms',
|
||||
text=_('License terms'),
|
||||
command=self.make_help_command(self.menubar.mHelpLicense)))
|
||||
tv.add_node(
|
||||
LTreeNode(
|
||||
text='About' + TITLE + '...',
|
||||
text=_('About %s...') % TITLE,
|
||||
command=self.make_help_command(self.menubar.mHelpAbout)))
|
||||
|
||||
# tv.add_node(LTreeNode(
|
||||
|
@ -1234,6 +1265,7 @@ class PysolMenubarTk:
|
|||
toolbar_vars={},
|
||||
sound_sample_vars={},
|
||||
color_vars={},
|
||||
language=StringVar(),
|
||||
)
|
||||
for w in TOOLBAR_BUTTONS:
|
||||
self.tkopt.toolbar_vars[w] = BooleanVar()
|
||||
|
@ -1288,6 +1320,7 @@ class PysolMenubarTk:
|
|||
tkopt.negative_bottom.set(opt.negative_bottom)
|
||||
tkopt.display_win_message.set(opt.display_win_message)
|
||||
tkopt.cardset.set(self.app.cardset_manager.getSelected())
|
||||
tkopt.language.set(opt.language)
|
||||
|
||||
for w in TOOLBAR_BUTTONS:
|
||||
tkopt.toolbar_vars[w].set(opt.toolbar_vars.get(w, False))
|
||||
|
@ -1346,7 +1379,7 @@ class PysolMenubarTk:
|
|||
|
||||
# LMainMenuDialog()
|
||||
LMenuItem(self.__menubar.menu,
|
||||
text="Menu", command=self.mMainMenuDialog)
|
||||
text=_("Menu"), command=self.mMainMenuDialog)
|
||||
|
||||
MfxMenubar.addPath = None
|
||||
|
||||
|
@ -1574,7 +1607,8 @@ class PysolMenubarTk:
|
|||
menu.delete(0, 'last')
|
||||
|
||||
if len(games) == 0:
|
||||
menu.add_radiobutton(label='<none>', name=None, state='disabled')
|
||||
menu.add_radiobutton(label=_('<none>'), name=None,
|
||||
state='disabled')
|
||||
elif len(games) > self.__cb_max * 4:
|
||||
games.sort(lambda a, b: cmp2(a.name, b.name))
|
||||
self._addSelectAllGameSubMenu(games, menu,
|
||||
|
@ -1791,8 +1825,9 @@ class PysolMenubarTk:
|
|||
#
|
||||
|
||||
DEFAULTEXTENSION = ".pso"
|
||||
FILETYPES = ((TITLE + " files", "*" + DEFAULTEXTENSION),
|
||||
("All files", "*"))
|
||||
# TRANSLATORS: Usually, 'PySol files'
|
||||
FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
|
||||
(_("All files"), "*"))
|
||||
|
||||
def mAddFavor(self, *event):
|
||||
gameid = self.app.game.id
|
||||
|
@ -1870,6 +1905,16 @@ class PysolMenubarTk:
|
|||
self.game.doPause()
|
||||
self.tkopt.pause.set(self.game.pause)
|
||||
|
||||
def mOptLanguage(self, *args):
|
||||
if self._cancelDrag(break_pause=False):
|
||||
return
|
||||
self.app.opt.language = self.tkopt.language.get()
|
||||
MfxMessageDialog(
|
||||
self.app.top, title=_("Note"),
|
||||
text=_("""\
|
||||
These settings will take effect
|
||||
the next time you restart the %(app)s""") % {'app': TITLE})
|
||||
|
||||
def mOptSoundDialog(self, *args):
|
||||
if self._cancelDrag(break_pause=False):
|
||||
return
|
||||
|
@ -2066,10 +2111,9 @@ class PysolMenubarTk:
|
|||
# if os.name == "posix":
|
||||
strings, default = (None, _("&Load"), _(
|
||||
"&Cancel"), _("&Info..."), ), 1
|
||||
t = CARDSET
|
||||
key = self.app.nextgame.cardset.index
|
||||
d = SelectCardsetDialogWithPreview(
|
||||
self.top, title=_("Select ") + t,
|
||||
self.top, title=_("Select cardset"),
|
||||
app=self.app, manager=self.app.cardset_manager, key=key,
|
||||
strings=strings, default=default)
|
||||
|
||||
|
|
|
@ -35,17 +35,13 @@ class SelectCardsetDialogWithPreview(MfxDialog):
|
|||
_cardset_store = None
|
||||
|
||||
def __init__(self, parent, title, app, manager, key=None, **kw):
|
||||
kw = self.initKw(kw)
|
||||
MfxDialog.__init__(self, parent, title, **kw)
|
||||
#
|
||||
if key is None:
|
||||
key = manager.getSelected()
|
||||
key = 1
|
||||
self.status = -1
|
||||
self.key = key
|
||||
self.app = app
|
||||
self.manager = manager
|
||||
self.key = key
|
||||
self.preview_key = -1
|
||||
self.all_keys = []
|
||||
self.status = -1
|
||||
return
|
||||
|
||||
def getSelected(self):
|
||||
return None
|
||||
|
|
|
@ -165,7 +165,7 @@ class SelectGameData(SelectDialogTreeData):
|
|||
if name is None or not list(filter(
|
||||
select_func, self.all_games_gi)):
|
||||
continue
|
||||
name = _("New games in v. ") + name
|
||||
name = _("New games in v. %(version)s") % {'version': name}
|
||||
gg.append(SelectGameNode(None, name, select_func))
|
||||
if 1 and gg:
|
||||
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
||||
|
|
|
@ -406,7 +406,7 @@ class HTMLViewer:
|
|||
# self.defcursor = 'xterm'
|
||||
self.handcursor = "hand2"
|
||||
|
||||
self.title = "Browser"
|
||||
self.title = _("Browser")
|
||||
self.window = None
|
||||
self.running = False
|
||||
|
||||
|
@ -431,11 +431,11 @@ class HTMLViewer:
|
|||
# BoxLayout(orientation='horizontal', size_hint=(1.0, 0.1))
|
||||
|
||||
# create buttons
|
||||
self.homeButton = HTMLButton(text="Index", on_release=self.goHome)
|
||||
self.backButton = HTMLButton(text="Back", on_release=self.goBack)
|
||||
self.homeButton = HTMLButton(text=_("Index"), on_release=self.goHome)
|
||||
self.backButton = HTMLButton(text=_("Back"), on_release=self.goBack)
|
||||
self.forwardButton = HTMLButton(
|
||||
text="Forward", on_release=self.goForward)
|
||||
self.closeButton = HTMLButton(text="Close", on_release=self.goHome)
|
||||
text=_("Forward"), on_release=self.goForward)
|
||||
self.closeButton = HTMLButton(text=_("Close"), on_release=self.goHome)
|
||||
|
||||
'''
|
||||
buttonline.add_widget(self.homeButton)
|
||||
|
@ -683,7 +683,8 @@ class HTMLViewer:
|
|||
self.display(self.home, relpath=0)
|
||||
|
||||
def errorDialog(self, msg):
|
||||
MfxMessageDialog(self.parent, title=TITLE + " HTML Problem",
|
||||
MfxMessageDialog(self.parent,
|
||||
title=_("%s HTML Problem") % TITLE,
|
||||
text=msg,
|
||||
# bitmap="warning"
|
||||
# FIXME: this interp don't have images
|
||||
|
|
|
@ -172,8 +172,11 @@ class SingleGame_StatsDialog(MfxDialog):
|
|||
|
||||
print('Stats(p): won=%s, lost=%s' % (won, lost))
|
||||
|
||||
text1 = 'Total:\n won: %s ... %s%%\n lost: %s ... %s%%\n\n' % (
|
||||
won, int(round(100.0 * pwon)), lost, int(round(100.0 * plost)))
|
||||
text1 = _('Total:\n' +
|
||||
' won: %(won)s ... %(percentwon)s%%\n' +
|
||||
' lost: %(lost)s ... %(percentlost)s%%\n\n') % dict(
|
||||
won=won, percentwon=int(round(100.0 * pwon)),
|
||||
lost=lost, percentlost=int(round(100.0 * plost)))
|
||||
|
||||
# createChart(app, won, lost, _("Total"))
|
||||
won, lost = app.stats.getSessionStats(player, gameid)
|
||||
|
@ -181,9 +184,11 @@ class SingleGame_StatsDialog(MfxDialog):
|
|||
|
||||
print('Stats(s): won=%s, lost=%s' % (won, lost))
|
||||
|
||||
text2 = \
|
||||
'Current Session:\n won: %s ... %s%%\n lost: %s ... %s%%\n' % \
|
||||
(won, int(round(100.0 * pwon)), lost, int(round(100.0 * plost)))
|
||||
text2 = _('Current Session:\n' +
|
||||
' won: %(won)s ... %(percentwon)s%%\n' +
|
||||
' lost: %(lost)s ... %(percentlost)s%%\n') % dict(
|
||||
won=won, percentwon=(round(100.0 * pwon)),
|
||||
lost=lost, percentlost=int(round(100.0 * plost)))
|
||||
# text2 = 'Current Session:\n won=%s, lost=%s\n' % (won, lost)
|
||||
|
||||
# createChart(app, won, lost, _("Current session"))
|
||||
|
|
|
@ -167,10 +167,10 @@ class MfxMessageDialog(MfxDialog):
|
|||
|
||||
# LB
|
||||
# nicht automatisch ein neues spiel laden.
|
||||
if (title == "Game won"):
|
||||
if (title == _("Game won")):
|
||||
self.status = 1
|
||||
# self.button = 0
|
||||
if (title == "Game finished"):
|
||||
if (title == _("Game finished")):
|
||||
self.status = 1
|
||||
# self.button =
|
||||
|
||||
|
@ -180,7 +180,7 @@ class MfxMessageDialog(MfxDialog):
|
|||
|
||||
|
||||
class MfxExceptionDialog(MfxMessageDialog):
|
||||
def __init__(self, parent, ex, title="Error", **kw):
|
||||
def __init__(self, parent, ex, title=_("Error"), **kw):
|
||||
kw = KwStruct(kw, bitmap="error")
|
||||
text = kw.get("text", "")
|
||||
if not text.endswith("\n"):
|
||||
|
@ -221,7 +221,7 @@ class PysolAboutDialog(object):
|
|||
logging.info('PysolAboutDialog: txt=%s' % text)
|
||||
|
||||
text = text + '\n\n' + 'Adaptation to Kivy/Android\n' + \
|
||||
' Copyright (C) (2016-17) LB'
|
||||
' Copyright (C) (2016-19) LB'
|
||||
|
||||
self.parent = parent
|
||||
self.app = app
|
||||
|
@ -261,13 +261,6 @@ class PysolAboutDialog(object):
|
|||
label = FText(text=text, halign='center', size_hint=(1, 1))
|
||||
window.content.add_widget(label)
|
||||
|
||||
'''
|
||||
label = LLabel(text=text)
|
||||
label.texture_update()
|
||||
label.size = label.texture_size
|
||||
image = LImage(texture=label.texture)
|
||||
window.content.add_widget(image)
|
||||
'''
|
||||
|
||||
# ************************************************************************
|
||||
# * a simple tooltip
|
||||
|
@ -637,53 +630,8 @@ class MfxScrolledCanvas(object):
|
|||
def scroll_bottom(self, *event):
|
||||
return self._yview('moveto', 1)
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# *
|
||||
# ************************************************************************
|
||||
# not used witch kivy. would not nun as it refers TkInter.
|
||||
|
||||
|
||||
'''
|
||||
class StackDesc:
|
||||
|
||||
def __init__(self, game, stack):
|
||||
self.game = game
|
||||
self.stack = stack
|
||||
self.canvas = game.canvas
|
||||
self.bindings = []
|
||||
|
||||
font = game.app.getFont('canvas_small')
|
||||
# print self.app.cardset.CARDW, self.app.images.CARDW
|
||||
cardw = game.app.images.CARDW
|
||||
x, y = stack.x + cardw / 2, stack.y
|
||||
text = stack.getHelp() + '\n' + stack.getBaseCard()
|
||||
text = text.strip()
|
||||
if text:
|
||||
frame = Tkinter.Frame(self.canvas)
|
||||
self.frame = frame
|
||||
label = Tkinter.Message(frame, font=font, text=text,
|
||||
width=cardw - 8, relief='solid',
|
||||
fg='#000000', bg='#ffffe0', bd=1)
|
||||
label.pack()
|
||||
self.label = label
|
||||
self.id = self.canvas.create_window(
|
||||
x, y, window=frame, anchor='n')
|
||||
self.bindings.append(label.bind(
|
||||
'<ButtonPress>', self._buttonPressEvent))
|
||||
# self.bindings.append(label.bind('<Enter>', self._enterEvent))
|
||||
else:
|
||||
self.id = None
|
||||
|
||||
def _buttonPressEvent(self, *event):
|
||||
# self.game.deleteStackDesc()
|
||||
self.frame.tkraise()
|
||||
|
||||
def _enterEvent(self, *event):
|
||||
self.frame.tkraise()
|
||||
|
||||
def delete(self):
|
||||
if self.id:
|
||||
self.canvas.delete(self.id)
|
||||
for b in self.bindings:
|
||||
self.label.unbind('<ButtonPress>', b)
|
||||
'''
|
||||
|
|
|
@ -203,7 +203,7 @@ class PysolToolbarTk(BoxLayout):
|
|||
# (n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
||||
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
||||
(None, None, None),
|
||||
(n_("Quit"), self.mHoldAndQuit, _("Quit ") + TITLE),
|
||||
(n_("Quit"), self.mHoldAndQuit, _("Quit %s") % TITLE),
|
||||
):
|
||||
if label is None:
|
||||
# sep = self._createSeparator()
|
||||
|
|
|
@ -56,25 +56,25 @@ if TOOLKIT == 'kivy':
|
|||
def fatal_no_cardsets(app):
|
||||
app.wm_withdraw()
|
||||
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
|
||||
text=_('''No cardsets were found !!!
|
||||
text=_('''No cardsets were found!!!
|
||||
|
||||
Cardsets should be installed into:
|
||||
%s/cardsets/
|
||||
%(dir)s
|
||||
|
||||
Please check your %s installation.
|
||||
''') % (getprefdir(PACKAGE), TITLE),
|
||||
Please check your %(app)s installation.
|
||||
''') % {'dir': getprefdir(PACKAGE) + '/cardsets/', 'app': TITLE},
|
||||
bitmap="error", strings=(_("&Quit"),))
|
||||
else:
|
||||
def fatal_no_cardsets(app):
|
||||
app.wm_withdraw()
|
||||
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
|
||||
text=_('''No cardsets were found !!!
|
||||
text=_('''No cardsets were found!!!
|
||||
|
||||
Main data directory is:
|
||||
%s
|
||||
%(dir)s
|
||||
|
||||
Please check your %s installation.
|
||||
''') % (app.dataloader.dir, TITLE),
|
||||
Please check your %(app)s installation.
|
||||
''') % {'dir': app.dataloader.dir, 'app': TITLE},
|
||||
bitmap="error", strings=(_("&Quit"),))
|
||||
|
||||
|
||||
|
@ -93,8 +93,8 @@ def parse_option(argv):
|
|||
"sound-mod=",
|
||||
"help"])
|
||||
except getopt.GetoptError as err:
|
||||
print_err(_("%s\ntry %s --help for more information") %
|
||||
(err, prog_name), 0)
|
||||
print_err(err + "\n" + _("try %s --help for more information") %
|
||||
prog_name, 0)
|
||||
return None
|
||||
opts = {"help": False,
|
||||
"deal": None,
|
||||
|
@ -303,13 +303,14 @@ def pysol_init(app, args):
|
|||
app.intro.progress.destroy()
|
||||
d = MfxMessageDialog(top, title=_("%s installation error") % TITLE,
|
||||
text=_('''
|
||||
No games were found !!!
|
||||
No games were found!!!
|
||||
|
||||
Main data directory is:
|
||||
%s
|
||||
%(dir)s
|
||||
|
||||
Please check your %s installation.
|
||||
''') % (app.dataloader.dir, TITLE), bitmap="error", strings=(_("&Quit"),))
|
||||
Please check your %(app)s installation.
|
||||
''') % {'dir': app.dataloader.dir, 'app': TITLE},
|
||||
bitmap="error", strings=(_("&Quit"),))
|
||||
return 1
|
||||
|
||||
# init cardsets
|
||||
|
|
|
@ -143,7 +143,11 @@ def getprefdir(package):
|
|||
|
||||
|
||||
# high resolution clock() and sleep()
|
||||
uclock = time.clock
|
||||
try:
|
||||
uclock = time.perf_counter
|
||||
except Exception:
|
||||
uclock = time.clock
|
||||
|
||||
usleep = time.sleep
|
||||
if os.name == "posix":
|
||||
uclock = time.time
|
||||
|
|
|
@ -4,40 +4,46 @@ import sys
|
|||
import six
|
||||
|
||||
|
||||
def n_(x):
|
||||
return x
|
||||
class myLocalGettext(object):
|
||||
def __init__(self, lang):
|
||||
self.language = lang
|
||||
|
||||
|
||||
def ungettext(msgid1, msgid2, n):
|
||||
# unicoded ngettext
|
||||
if not isinstance(msgid1, six.text_type):
|
||||
msgid1 = six.text_type(msgid1, 'utf-8')
|
||||
if not isinstance(msgid2, six.text_type):
|
||||
msgid2 = six.text_type(msgid2, 'utf-8')
|
||||
domain = gettext._current_domain
|
||||
try:
|
||||
t = gettext.translation(domain,
|
||||
gettext._localedirs.get(domain, None))
|
||||
except IOError:
|
||||
if n == 1:
|
||||
return msgid1
|
||||
else:
|
||||
return msgid2
|
||||
if sys.version_info >= (3, 0):
|
||||
return t.ngettext(msgid1, msgid2, n)
|
||||
else:
|
||||
return t.ungettext(msgid1, msgid2, n)
|
||||
|
||||
|
||||
def fix_gettext():
|
||||
def ugettext(message):
|
||||
# unicoded gettext
|
||||
if not isinstance(message, six.text_type):
|
||||
message = six.text_type(message, 'utf-8')
|
||||
def translation(self):
|
||||
domain = gettext._current_domain
|
||||
localedir = gettext._localedirs.get(domain, None)
|
||||
if self.language == "":
|
||||
t = gettext.translation(domain, localedir)
|
||||
else:
|
||||
t = gettext.translation(
|
||||
domain, localedir, languages=[self.language])
|
||||
return t
|
||||
|
||||
def maketext(self, msg):
|
||||
if not isinstance(msg, six.text_type):
|
||||
return six.text_type(msg, 'utf-8')
|
||||
return msg
|
||||
|
||||
def ungettext(self, msgid1, msgid2, n):
|
||||
# unicoded ngettext
|
||||
msgid1 = self.maketext(msgid1)
|
||||
msgid2 = self.maketext(msgid2)
|
||||
try:
|
||||
t = gettext.translation(domain,
|
||||
gettext._localedirs.get(domain, None))
|
||||
t = self.translation()
|
||||
except IOError:
|
||||
if n == 1:
|
||||
return msgid1
|
||||
else:
|
||||
return msgid2
|
||||
if sys.version_info >= (3, 0):
|
||||
return t.ngettext(msgid1, msgid2, n)
|
||||
else:
|
||||
return t.ungettext(msgid1, msgid2, n)
|
||||
|
||||
def ugettext(self, message):
|
||||
# unicoded gettext
|
||||
message = self.maketext(message)
|
||||
try:
|
||||
t = self.translation()
|
||||
except IOError:
|
||||
return message
|
||||
if sys.version_info >= (3, 0):
|
||||
|
@ -45,10 +51,20 @@ def fix_gettext():
|
|||
else:
|
||||
return t.ugettext(message)
|
||||
|
||||
gettext.ugettext = ugettext
|
||||
|
||||
gettext.ungettext = ungettext
|
||||
myGettext = myLocalGettext('')
|
||||
|
||||
|
||||
def n_(x):
|
||||
return x
|
||||
|
||||
|
||||
def fix_gettext():
|
||||
gettext.ugettext = myGettext.ugettext
|
||||
gettext.ungettext = myGettext.ungettext
|
||||
|
||||
|
||||
fix_gettext()
|
||||
|
||||
_ = gettext.ugettext
|
||||
ungettext = gettext.ungettext
|
||||
|
|
|
@ -25,16 +25,20 @@ import os
|
|||
import sys
|
||||
import traceback
|
||||
|
||||
import configobj
|
||||
|
||||
import pysollib.settings
|
||||
from pysollib.configobj import configobj, validate
|
||||
from pysollib.mfxutil import print_err
|
||||
from pysollib.mygettext import _
|
||||
from pysollib.mygettext import myGettext
|
||||
from pysollib.pysoltk import TOOLBAR_BUTTONS, TOOLKIT
|
||||
from pysollib.resource import CSI
|
||||
|
||||
|
||||
import six
|
||||
|
||||
import validate
|
||||
|
||||
# ************************************************************************
|
||||
# * Options
|
||||
# ************************************************************************
|
||||
|
@ -111,6 +115,7 @@ solver_max_iterations = integer
|
|||
solver_iterations_output_step = integer
|
||||
solver_preset = string
|
||||
display_win_message = boolean
|
||||
language = string
|
||||
|
||||
[sound_samples]
|
||||
move = boolean
|
||||
|
@ -252,6 +257,7 @@ class Options:
|
|||
# ('recent_gameid', 'list'),
|
||||
# ('favorite_gameid', 'list'),
|
||||
('display_win_message', 'bool'),
|
||||
('language', 'str'),
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -319,6 +325,7 @@ class Options:
|
|||
self.negative_bottom = True
|
||||
self.translate_game_names = True
|
||||
self.display_win_message = True
|
||||
self.language = ''
|
||||
# sound
|
||||
self.sound = True
|
||||
self.sound_mode = 1
|
||||
|
@ -663,6 +670,8 @@ class Options:
|
|||
for key in TOOLBAR_BUTTONS:
|
||||
self.toolbar_vars[key] = (key in visible_buttons)
|
||||
|
||||
myGettext.language = self.language
|
||||
|
||||
# solver
|
||||
solver_presets = self._getOption('general', 'solver_presets', 'list')
|
||||
if solver_presets is not None:
|
||||
|
|
|
@ -290,7 +290,7 @@ class SelectGameDialogWithPreview(MfxDialog):
|
|||
for version, vg in GI.GAMES_BY_PYSOL_VERSION:
|
||||
def selecter(gi, vg=vg):
|
||||
return gi.id in vg
|
||||
label = _("New games in v. ") + version
|
||||
label = _("New games in v. %(version)s") % {'version': version}
|
||||
data.append((label, selecter))
|
||||
self._addGamesFromData(data, store, None,
|
||||
_("by PySol version"), all_games)
|
||||
|
@ -423,7 +423,7 @@ class SelectGameDialogWithPreview(MfxDialog):
|
|||
# self.top.wm_title(
|
||||
# "Select Game - " + self.app.getGameTitleName(gameid))
|
||||
title = self.app.getGameTitleName(gameid)
|
||||
self.set_title(_("Playable Preview - ") + title)
|
||||
self.set_title(_("Playable Preview - %(game)s") % {'game': title})
|
||||
#
|
||||
self.preview_game = gi.gameclass(gi)
|
||||
self.preview_game.createPreview(self.preview_app)
|
||||
|
|
|
@ -434,13 +434,13 @@ class HTMLViewer:
|
|||
for p in REMOTE_PROTOCOLS:
|
||||
if url.startswith(p):
|
||||
if not openURL(url):
|
||||
self.errorDialog(TITLE + _('''HTML limitation:
|
||||
The %s protocol is not supported yet.
|
||||
self.errorDialog(_('''%(app)s HTML limitation:
|
||||
The %(protocol)s protocol is not supported yet.
|
||||
|
||||
Please use your standard web browser
|
||||
to open the following URL:
|
||||
%s
|
||||
''') % (p, url))
|
||||
%(url)s
|
||||
''') % {'app': TITLE, 'protocol': p, 'url': url})
|
||||
return
|
||||
|
||||
# locate the file relative to the current url
|
||||
|
|
|
@ -67,7 +67,8 @@ class StatsFormatter(PysolStatsFormatter):
|
|||
6, result[6],
|
||||
7, result[7])
|
||||
total, played, won, lost, time, moves, perc = self.getStatSummary()
|
||||
text = _("Total (%d out of %d games)") % (played, total)
|
||||
text = _("Total (%(played)d out of %(total)d games)") % {
|
||||
'played': played, 'total': total}
|
||||
iter = self.store.append(None)
|
||||
self.store.set(iter,
|
||||
0, text,
|
||||
|
|
|
@ -24,95 +24,12 @@
|
|||
|
||||
# imports
|
||||
import re
|
||||
import time
|
||||
|
||||
from pysollib.mfxutil import SubclassResponsibility
|
||||
try:
|
||||
import random2
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"You need to install " +
|
||||
"https://pypi.python.org/pypi/random2 using pip or similar.")
|
||||
|
||||
from pysol_cards.random import RandomBase # noqa: I100
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * Abstract class for PySol Random number generator.
|
||||
# *
|
||||
# * We use a seed of type int in the range [0, MAX_SEED].
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
class BasicRandom(RandomBase):
|
||||
# MAX_SEED = 0L
|
||||
# MAX_SEED = 0xffffffffffffffffL # 64 bits
|
||||
MAX_SEED = int('100000000000000000000') # 20 digits
|
||||
|
||||
ORIGIN_UNKNOWN = 0
|
||||
ORIGIN_RANDOM = 1
|
||||
ORIGIN_PREVIEW = 2 # random from preview
|
||||
ORIGIN_SELECTED = 3 # manually entered
|
||||
ORIGIN_NEXT_GAME = 4 # "Next game number"
|
||||
|
||||
def __init__(self):
|
||||
self.seed_as_string = None
|
||||
|
||||
def getSeedStr(self):
|
||||
return str(self.initial_seed)
|
||||
|
||||
def __str__(self):
|
||||
return self.str(self.initial_seed)
|
||||
|
||||
def str(self, seed):
|
||||
return '%020d' % seed
|
||||
|
||||
def reset(self):
|
||||
raise SubclassResponsibility
|
||||
|
||||
def copy(self):
|
||||
random = self.__class__(0)
|
||||
random.__dict__.update(self.__dict__)
|
||||
return random
|
||||
|
||||
def increaseSeed(self, seed):
|
||||
if seed < self.MAX_SEED:
|
||||
return seed + 1
|
||||
return 0
|
||||
|
||||
def _getRandomSeed(self):
|
||||
t = int(time.time() * 256.0)
|
||||
t = (t ^ (t >> 24)) % (self.MAX_SEED + 1)
|
||||
return t
|
||||
|
||||
def setSeedAsStr(self, new_s):
|
||||
self.seed_as_string = new_s
|
||||
|
||||
def getSeedAsStr(self):
|
||||
if self.seed_as_string:
|
||||
return self.seed_as_string
|
||||
else:
|
||||
return str(self)
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * Mersenne Twister random number generator
|
||||
# * uses the standard python module `random'
|
||||
# ************************************************************************
|
||||
|
||||
class MTRandom(BasicRandom, random2.Random):
|
||||
|
||||
def __init__(self, seed=None):
|
||||
if seed is None:
|
||||
seed = self._getRandomSeed()
|
||||
BasicRandom.__init__(self)
|
||||
random2.Random.__init__(self, seed)
|
||||
self.initial_seed = seed
|
||||
self.initial_state = self.getstate()
|
||||
self.origin = self.ORIGIN_UNKNOWN
|
||||
|
||||
def reset(self):
|
||||
self.setstate(self.initial_state)
|
||||
import pysol_cards
|
||||
assert getattr(pysol_cards, 'VERSION', (0, 0, 0)) >= (0, 8, 7), (
|
||||
"Newer version of https://pypi.org/project/pysol-cards is required.")
|
||||
from pysol_cards.random_base import RandomBase # noqa: I100
|
||||
from pysol_cards.random import MTRandom, match_ms_deal_prefix # noqa: I100
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
|
@ -120,12 +37,12 @@ class MTRandom(BasicRandom, random2.Random):
|
|||
# * uses the standard python module `random'
|
||||
# ************************************************************************
|
||||
|
||||
# class WHRandom(BasicRandom, random.WichmannHill):
|
||||
# class WHRandom(RandomBase, random.WichmannHill):
|
||||
#
|
||||
# def __init__(self, seed=None):
|
||||
# if seed is None:
|
||||
# seed = self._getRandomSeed()
|
||||
# BasicRandom.__init__(self)
|
||||
# RandomBase.__init__(self)
|
||||
# random.WichmannHill.__init__(self, seed)
|
||||
# self.initial_seed = seed
|
||||
# self.initial_state = self.getstate()
|
||||
|
@ -139,10 +56,10 @@ class MTRandom(BasicRandom, random2.Random):
|
|||
# ************************************************************************
|
||||
|
||||
|
||||
class MFXRandom(BasicRandom):
|
||||
class MFXRandom(RandomBase):
|
||||
|
||||
def __init__(self, seed=None):
|
||||
BasicRandom.__init__(self)
|
||||
RandomBase.__init__(self)
|
||||
if seed is None:
|
||||
seed = self._getRandomSeed()
|
||||
self.initial_seed = self.setSeed(seed)
|
||||
|
@ -197,7 +114,7 @@ MS_LONG_BIT = (1 << 1000)
|
|||
CUSTOM_BIT = (1 << 999)
|
||||
|
||||
|
||||
class CustomRandom(BasicRandom):
|
||||
class CustomRandom(RandomBase):
|
||||
def __init__(self):
|
||||
self.initial_seed = self.seed = MS_LONG_BIT | CUSTOM_BIT
|
||||
self.origin = self.ORIGIN_UNKNOWN
|
||||
|
@ -233,7 +150,9 @@ class LCRandom31(MFXRandom):
|
|||
return "ms" + str(self.initial_seed)
|
||||
|
||||
def str(self, seed):
|
||||
return "%05d" % int(seed) if not _match_ms(seed) else seed
|
||||
if match_ms_deal_prefix("{}".format(seed)) is None:
|
||||
return "%05d" % int(seed)
|
||||
return seed
|
||||
|
||||
def setSeed(self, seed):
|
||||
seed = int(seed)
|
||||
|
@ -275,18 +194,14 @@ PysolRandom = MTRandom
|
|||
# * PySol support code
|
||||
# ************************************************************************
|
||||
|
||||
def _match_ms(s):
|
||||
"""match an ms based seed string."""
|
||||
return re.match(r"ms([0-9]+)\n?\Z", s)
|
||||
|
||||
|
||||
# construct Random from seed string
|
||||
def constructRandom(s):
|
||||
if s == 'Custom':
|
||||
return CustomRandom()
|
||||
m = _match_ms(s)
|
||||
if m:
|
||||
seed = int(m.group(1))
|
||||
m = match_ms_deal_prefix(s)
|
||||
if m is not None:
|
||||
seed = m
|
||||
if 0 <= seed <= LCRandom31.MAX_SEED:
|
||||
ret = LCRandom31(seed)
|
||||
ret.setSeedAsStr(s)
|
||||
|
@ -307,9 +222,9 @@ def constructRandom(s):
|
|||
def random__str2long(s):
|
||||
if s == 'Custom':
|
||||
return CUSTOM_BIT | MS_LONG_BIT
|
||||
m = _match_ms(s)
|
||||
if m:
|
||||
return (int(m.group(1)) | MS_LONG_BIT)
|
||||
m = match_ms_deal_prefix(s)
|
||||
if m is not None:
|
||||
return (m | MS_LONG_BIT)
|
||||
else:
|
||||
return int(s)
|
||||
|
||||
|
|
|
@ -63,8 +63,8 @@ if os.name == 'posix':
|
|||
DATA_DIRS = [
|
||||
'/usr/share/PySolFC',
|
||||
'/usr/local/share/PySolFC',
|
||||
'/usr/games/PySolFC',
|
||||
'/usr/local/games/PySolFC',
|
||||
'/usr/share/games/PySolFC',
|
||||
'/usr/local/share/games/PySolFC',
|
||||
]
|
||||
if os.name == 'nt':
|
||||
pass
|
||||
|
|
|
@ -1945,7 +1945,7 @@ class TalonStack(Stack,
|
|||
nredeals = _('Unlimited redeals.')
|
||||
else:
|
||||
n = self.max_rounds-1
|
||||
nredeals = ungettext('%d readeal', '%d redeals', n) % n
|
||||
nredeals = ungettext('%d redeal', '%d redeals', n) % n
|
||||
# round = _('Round #%d.') % self.round
|
||||
return _('Talon.')+' '+nredeals # +' '+round
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ class FileStatsFormatter(PysolStatsFormatter):
|
|||
def writeStats(self, player, sort_by='name'):
|
||||
if player is None:
|
||||
player = _('Demo')
|
||||
header = _("Statistics for ") + player
|
||||
header = _("Statistics for %(player)s") % {'player': player}
|
||||
self.writeHeader(header, 62)
|
||||
header = self.getStatHeader()
|
||||
self.pstats(*header)
|
||||
|
@ -209,7 +209,8 @@ class FileStatsFormatter(PysolStatsFormatter):
|
|||
self.pstats(gameid=gameid, *result)
|
||||
self.nl()
|
||||
total, played, won, lost, time, moves, perc = self.getStatSummary()
|
||||
self.pstats(_("Total (%d out of %d games)") % (played, total),
|
||||
self.pstats(_("Total (%(played)d out of %(total)d games)") %
|
||||
{'played': played, 'total': total},
|
||||
won+lost, won, lost, time, moves, perc)
|
||||
self.nl(2)
|
||||
return played
|
||||
|
@ -231,14 +232,14 @@ class FileStatsFormatter(PysolStatsFormatter):
|
|||
def writeFullLog(self, player):
|
||||
if player is None:
|
||||
player = _('Demo')
|
||||
header = _("Full log for ") + player
|
||||
header = _("Full log for %(player)s") % {'player': player}
|
||||
prev_games = self.app.stats.prev_games.get(player)
|
||||
return self.writeLog(player, header, prev_games)
|
||||
|
||||
def writeSessionLog(self, player):
|
||||
if player is None:
|
||||
player = _('Demo')
|
||||
header = _("Session log for ") + player
|
||||
header = _("Session log for %(player)s") % {'player': player}
|
||||
prev_games = self.app.stats.session_games.get(player)
|
||||
return self.writeLog(player, header, prev_games)
|
||||
|
||||
|
|
25
pysollib/struct_new.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fenc=utf-8
|
||||
#
|
||||
# Copyright © 2019 Shlomi Fish <shlomif@cpan.org>
|
||||
#
|
||||
# Distributed under terms of the MIT license.
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class NewStruct(object):
|
||||
"""docstring for NewStruct"""
|
||||
def copy(self):
|
||||
ret = self.__class__()
|
||||
ret.__dict__.update(self.__dict__)
|
||||
return ret
|
||||
|
||||
def addattr(self, **kw):
|
||||
for k in kw.keys():
|
||||
if k in self.__dict__:
|
||||
raise AttributeError(k)
|
||||
self.__dict__.update(kw)
|
|
@ -105,8 +105,8 @@ class PysolMenubarTk(PysolMenubarTkCommon):
|
|||
self._calc_MfxMessageDialog()(
|
||||
self.top, title=_("Change theme"),
|
||||
text=_("""\
|
||||
This settings will take effect
|
||||
the next time you restart """)+TITLE,
|
||||
These settings will take effect
|
||||
the next time you restart %(app)s""") % {'app': TITLE},
|
||||
bitmap="warning",
|
||||
default=0, strings=(_("&OK"),))
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from pysollib.gamedb import GI
|
||||
|
@ -80,7 +79,8 @@ class SelectGameData(SelectDialogTreeData):
|
|||
def __init__(self, app):
|
||||
SelectDialogTreeData.__init__(self)
|
||||
self.all_games_gi = list(map(
|
||||
app.gdb.get, app.gdb.getGamesIdSortedByName()))
|
||||
app.gdb.get,
|
||||
app.gdb.getGamesIdSortedByName()))
|
||||
self.no_games = [SelectGameLeaf(None, None, _("(no games)"), None), ]
|
||||
#
|
||||
s_by_type = s_oriental = s_special = s_original = s_contrib = \
|
||||
|
@ -144,7 +144,7 @@ class SelectGameData(SelectDialogTreeData):
|
|||
if name is None or not list(filter(
|
||||
select_func, self.all_games_gi)):
|
||||
continue
|
||||
name = _("New games in v. ") + name
|
||||
name = _("New games in v. %(version)s") % {'version': name}
|
||||
gg.append(SelectGameNode(None, name, select_func))
|
||||
if 1 and gg:
|
||||
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
||||
|
@ -230,8 +230,6 @@ class SelectGameData(SelectDialogTreeData):
|
|||
lambda gi: gi.si.redeals == 3),
|
||||
SelectGameNode(None, _("Unlimited redeals"),
|
||||
lambda gi: gi.si.redeals == -1),
|
||||
# SelectGameNode(None, "Variable redeals",
|
||||
# lambda gi: gi.si.redeals == -2),
|
||||
SelectGameNode(
|
||||
None, _("Other number of redeals"),
|
||||
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
|
||||
|
@ -321,7 +319,7 @@ class SelectGameDialog(MfxDialog):
|
|||
if button == 0: # Ok or double click
|
||||
self.gameid = self.tree.selection_key
|
||||
self.tree.n_expansions = 1 # save xyview in any case
|
||||
if button == 10: # Rules
|
||||
if button == 1: # Rules
|
||||
doc = self.app.getGameRulesFilename(self.tree.selection_key)
|
||||
if not doc:
|
||||
return
|
||||
|
@ -515,7 +513,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
# self.top.wm_title("Select Game - " +
|
||||
# self.app.getGameTitleName(gameid))
|
||||
title = self.app.getGameTitleName(gameid)
|
||||
self.top.wm_title(_("Playable Preview - ") + title)
|
||||
self.top.wm_title(_("Playable Preview - %(game)s") % {'game': title})
|
||||
#
|
||||
self.preview_game = gi.gameclass(gi)
|
||||
self.preview_game.createPreview(self.preview_app)
|
||||
|
|
|
@ -368,7 +368,8 @@ class TreeFormatter(PysolStatsFormatter):
|
|||
self.parent_window.games[id] = t8
|
||||
|
||||
total, played, won, lost, time_, moves, perc = self.getStatSummary()
|
||||
text = _("Total (%d out of %d games)") % (played, total)
|
||||
text = _("Total (%(played)d out of %(total)d games)") % {
|
||||
'played': played, 'total': total}
|
||||
id = self.tree.insert("", "end", text=text,
|
||||
values=(won+lost, won, lost, time_, moves, perc))
|
||||
self.parent_window.tree_items.append(id)
|
||||
|
|
|
@ -188,7 +188,7 @@ class PysolToolbarTk:
|
|||
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
||||
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
||||
(None, None, None),
|
||||
(n_("Quit"), self.mQuit, _("Quit ")+TITLE),
|
||||
(n_("Quit"), self.mQuit, _("Quit %s") % TITLE),
|
||||
):
|
||||
if label is None:
|
||||
sep = self._createSeparator()
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
import os
|
||||
|
||||
from pysollib.gamedb import GI
|
||||
from pysollib.help import help_html
|
||||
from pysollib.mfxutil import KwStruct, Struct, destruct
|
||||
from pysollib.mfxutil import format_time
|
||||
from pysollib.mygettext import _
|
||||
|
@ -39,11 +38,11 @@ from .selecttree import SelectDialogTreeCanvas
|
|||
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
|
||||
from .tkwidget import MfxDialog, MfxScrolledCanvas
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# * Nodes
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
class SelectGameLeaf(SelectDialogTreeLeaf):
|
||||
pass
|
||||
|
||||
|
@ -137,7 +136,6 @@ class SelectGameData(SelectDialogTreeData):
|
|||
if 1 and gg:
|
||||
s_by_compatibility = SelectGameNode(None, _("by Compatibility"),
|
||||
tuple(gg))
|
||||
pass
|
||||
#
|
||||
s_by_pysol_version, gg = None, []
|
||||
for name, games in GI.GAMES_BY_PYSOL_VERSION:
|
||||
|
@ -146,7 +144,7 @@ class SelectGameData(SelectDialogTreeData):
|
|||
if name is None or not list(filter(
|
||||
select_func, self.all_games_gi)):
|
||||
continue
|
||||
name = _("New games in v. ") + name
|
||||
name = _("New games in v. %(version)s") % {'version': name}
|
||||
gg.append(SelectGameNode(None, name, select_func))
|
||||
if 1 and gg:
|
||||
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
||||
|
@ -167,16 +165,16 @@ class SelectGameData(SelectDialogTreeData):
|
|||
list(app.gdb.getGamesTuplesSortedByAlternateName()))
|
||||
#
|
||||
self.rootnodes = [_f for _f in (
|
||||
SelectGameNode(None, _("All Games"), None),
|
||||
SelectGameNode(None, _("All Games"), None, expanded=0),
|
||||
SelectGameNode(None, _("Alternate Names"), ul_alternate_names),
|
||||
SelectGameNode(None, _("Popular Games"),
|
||||
lambda gi: gi.si.game_flags & GI.GT_POPULAR),
|
||||
s_by_type,
|
||||
s_mahjongg,
|
||||
s_oriental,
|
||||
s_special,
|
||||
SelectGameNode(None, _("Custom Games"),
|
||||
lambda gi: gi.si.game_type == GI.GT_CUSTOM),
|
||||
s_by_type,
|
||||
SelectGameNode(None, _('by Skill Level'), (
|
||||
SelectGameNode(None, _('Luck only'),
|
||||
lambda gi: gi.skill_level == GI.SL_LUCK),
|
||||
|
@ -232,8 +230,6 @@ class SelectGameData(SelectDialogTreeData):
|
|||
lambda gi: gi.si.redeals == 3),
|
||||
SelectGameNode(None, _("Unlimited redeals"),
|
||||
lambda gi: gi.si.redeals == -1),
|
||||
SelectGameNode(None, "Variable redeals",
|
||||
lambda gi: gi.si.redeals == -2),
|
||||
SelectGameNode(
|
||||
None, _("Other number of redeals"),
|
||||
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
|
||||
|
@ -308,8 +304,8 @@ class SelectGameDialog(MfxDialog):
|
|||
def initKw(self, kw):
|
||||
kw = KwStruct(kw,
|
||||
strings=(None, None, _("&Cancel"),), default=0,
|
||||
separator=True,
|
||||
resizable=True,
|
||||
separator=True,
|
||||
padx=10, pady=10,
|
||||
buttonpadx=10, buttonpady=5,
|
||||
)
|
||||
|
@ -322,14 +318,15 @@ class SelectGameDialog(MfxDialog):
|
|||
MfxDialog.destroy(self)
|
||||
|
||||
def mDone(self, button):
|
||||
if button == 0: # Ok or double click
|
||||
if button == 0: # Ok or double click
|
||||
self.gameid = self.tree.selection_key
|
||||
self.tree.n_expansions = 1 # save xyview in any case
|
||||
if button == 1: # Rules
|
||||
if button == 1: # Rules
|
||||
doc = self.app.getGameRulesFilename(self.tree.selection_key)
|
||||
if not doc:
|
||||
return
|
||||
dir = os.path.join("html", "rules")
|
||||
from pysollib.help import help_html
|
||||
help_html(self.app, doc, dir, self.top)
|
||||
return
|
||||
MfxDialog.mDone(self, button)
|
||||
|
@ -436,7 +433,8 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
|
||||
def initKw(self, kw):
|
||||
kw = KwStruct(kw,
|
||||
strings=(_("&Select"), _("&Rules"), _("&Cancel"),),
|
||||
strings=((_("&Rules"), 10), 'sep',
|
||||
_("&Select"), _("&Cancel"),),
|
||||
default=0,
|
||||
)
|
||||
return SelectGameDialog.initKw(self, kw)
|
||||
|
@ -516,7 +514,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
# self.top.wm_title("Select Game - " +
|
||||
# self.app.getGameTitleName(gameid))
|
||||
title = self.app.getGameTitleName(gameid)
|
||||
self.top.wm_title(_("Playable Preview - ") + title)
|
||||
self.top.wm_title(_("Playable Preview - %(game)s") % {'game': title})
|
||||
#
|
||||
self.preview_game = gi.gameclass(gi)
|
||||
self.preview_game.createPreview(self.preview_app)
|
||||
|
@ -545,7 +543,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
|||
#
|
||||
self.updateInfo(gameid)
|
||||
#
|
||||
rules_button = self.buttons[1]
|
||||
rules_button = self.buttons[0]
|
||||
if self.app.getGameRulesFilename(gameid):
|
||||
rules_button.config(state="normal")
|
||||
else:
|
||||
|
|
|
@ -410,7 +410,8 @@ class CanvasFormatter(PysolStatsFormatter):
|
|||
#
|
||||
y += self.h
|
||||
total, played, won, lost, time_, moves, perc = self.getStatSummary()
|
||||
s = _("Total (%d out of %d games)") % (played, total)
|
||||
s = _("Total (%(played)d out of %(total)d games)") % {
|
||||
'played': played, 'total': total}
|
||||
self.pstats(y, (s, won+lost, won, lost, time_, moves, perc))
|
||||
|
||||
def writeLog(self, player, prev_games):
|
||||
|
|
|
@ -188,7 +188,7 @@ class PysolToolbarTk:
|
|||
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
||||
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
||||
(None, None, None),
|
||||
(n_("Quit"), self.mQuit, _("Quit ")+TITLE),
|
||||
(n_("Quit"), self.mQuit, _("Quit %s") % TITLE),
|
||||
):
|
||||
if label is None:
|
||||
sep = self._createSeparator()
|
||||
|
|
|
@ -293,7 +293,7 @@ class PysolMenubarTkCommon:
|
|||
if WIN_SYSTEM == "aqua":
|
||||
applemenu = MfxMenu(self.menubar, "apple")
|
||||
applemenu.add_command(
|
||||
label=_("&About ")+TITLE, command=self.mHelpAbout)
|
||||
label=_("&About %s") % TITLE, command=self.mHelpAbout)
|
||||
|
||||
menu = MfxMenu(self.menubar, n_("&File"))
|
||||
menu.add_command(
|
||||
|
@ -663,7 +663,7 @@ class PysolMenubarTkCommon:
|
|||
if WIN_SYSTEM != "aqua":
|
||||
menu.add_separator()
|
||||
menu.add_command(
|
||||
label=n_("&About ")+TITLE+"...",
|
||||
label=_("&About %s...") % TITLE,
|
||||
command=self.mHelpAbout)
|
||||
|
||||
MfxMenubar.addPath = None
|
||||
|
@ -968,7 +968,8 @@ class PysolMenubarTkCommon:
|
|||
def updateGamesMenu(self, menu, games):
|
||||
menu.delete(0, 'last')
|
||||
if len(games) == 0:
|
||||
menu.add_radiobutton(label='<none>', name=None, state='disabled')
|
||||
menu.add_radiobutton(label=_('<none>'), name=None,
|
||||
state='disabled')
|
||||
elif len(games) > self.cb_max*4:
|
||||
games.sort(key=lambda x: x.name)
|
||||
self._addSelectAllGameSubMenu(games, menu,
|
||||
|
@ -1132,7 +1133,8 @@ class PysolMenubarTkCommon:
|
|||
#
|
||||
|
||||
DEFAULTEXTENSION = ".pso"
|
||||
FILETYPES = ((TITLE+" files", "*"+DEFAULTEXTENSION), ("All files", "*"))
|
||||
FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
|
||||
(_("All files"), "*"))
|
||||
|
||||
def mAddFavor(self, *event):
|
||||
gameid = self.app.game.id
|
||||
|
|
|
@ -25,7 +25,7 @@ class BaseSolverDialog:
|
|||
def __init__(self, parent, app, **kw):
|
||||
self.parent = parent
|
||||
self.app = app
|
||||
title = TITLE+' - FreeCell Solver'
|
||||
title = _('%(app)s - FreeCell Solver') % {'app': TITLE}
|
||||
kw = self.initKw(kw)
|
||||
self._calc_MfxDialog().__init__(
|
||||
self, parent, title, kw.resizable, kw.default)
|
||||
|
|
|
@ -311,13 +311,13 @@ class Base_HTMLViewer:
|
|||
for p in REMOTE_PROTOCOLS:
|
||||
if url.startswith(p):
|
||||
if not openURL(url):
|
||||
self.errorDialog(TITLE + _('''HTML limitation:
|
||||
The %s protocol is not supported yet.
|
||||
self.errorDialog(_('''%(app)s HTML limitation:
|
||||
The %(protocol)s protocol is not supported yet.
|
||||
|
||||
Please use your standard web browser
|
||||
to open the following URL:
|
||||
%s
|
||||
''') % (p, url))
|
||||
%(url)s
|
||||
''') % {'app': TITLE, 'protocol': p, 'url': url})
|
||||
return
|
||||
|
||||
# locate the file relative to the current url
|
||||
|
@ -429,7 +429,7 @@ to open the following URL:
|
|||
|
||||
def errorDialog(self, msg):
|
||||
self._calc_MfxMessageDialog()(
|
||||
self.parent, title=TITLE+" HTML Problem",
|
||||
self.parent, title="%(app)s HTML Problem" % {'app': TITLE},
|
||||
text=msg,
|
||||
# bitmap="warning", # FIXME: this interp don't have images
|
||||
strings=(_("&OK"),), default=0)
|
||||
|
|
|
@ -150,7 +150,7 @@ def all_games(sort_by='id'):
|
|||
if gt == 'French':
|
||||
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
|
||||
name = gi.name
|
||||
altnames = '<br>'.join(gi.altnames)
|
||||
altnames = '<br/>'.join(gi.altnames)
|
||||
fn = os.path.join(rules_dir, rules_fn)
|
||||
if 1 and os.path.exists(fn):
|
||||
print('''<tr>
|
||||
|
|
|
@ -1,39 +1,61 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
# converts cardset images and config files in current
|
||||
# directory from input-format to output-format.
|
||||
# Converts cardset images and config files in subdirs of the current
|
||||
# directory from input-format to output-format (default: gif to bmp).
|
||||
#
|
||||
# example to convert from gif format to png:
|
||||
# Example to convert from gif format to png:
|
||||
#
|
||||
# $> cardconv gif png
|
||||
#
|
||||
# needs package 'ImageMagick' beeing installed.
|
||||
# Needs package 'ImageMagick' being installed.
|
||||
|
||||
ifo=''
|
||||
if [ $1 ]
|
||||
then
|
||||
ifo='gif'
|
||||
ofo='bmp'
|
||||
|
||||
if [ $# -eq 2 ]; then
|
||||
ifo=$1
|
||||
else
|
||||
echo 'use: cardconv <input-format> <output-format>'
|
||||
exit
|
||||
fi
|
||||
|
||||
ofo=''
|
||||
if [ $2 ]
|
||||
then
|
||||
ofo=$2
|
||||
else
|
||||
echo 'use: cardconv <input-format> <output-format>'
|
||||
exit
|
||||
elif [ $# -ne 0 ]; then
|
||||
echo "Usage: cardconv [INPUTFORMAT OUTPUTFORMAT]"
|
||||
echo "If formats are not specified, converts $ifo to $ofo."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# alle images.
|
||||
for i in *.${ifo}; do convert $i `basename $i .${ifo}`.${ofo}; rm -f $i; done
|
||||
# Returns true if $1/config.txt exists and contains a mention of the input format
|
||||
# file extension.
|
||||
chkdir() {
|
||||
grep -qi "\.${ifo}" "${1}/config".txt
|
||||
return $?
|
||||
}
|
||||
|
||||
# config.txt
|
||||
if [ -f config.txt ]
|
||||
then
|
||||
cp -a config.txt tmp.txt
|
||||
cat tmp.txt | sed "s/.${ifo}/.${ofo}/g" >config.txt
|
||||
rm -f tmp.txt
|
||||
fi
|
||||
# Usage: convdir in_dir out_dir
|
||||
convdir() {
|
||||
mkdir -p "$2"
|
||||
|
||||
# Convert all images
|
||||
if [ $ofo == 'png' ]; then
|
||||
for i in $1/*.$ifo; do
|
||||
convert "$i" "-matte" "-quality" "95" "png32:$2/$(basename $i .$ifo).$ofo"
|
||||
done
|
||||
else
|
||||
for i in $1/*.$ifo; do
|
||||
convert "$i" "$2/$(basename $i .$ifo).$ofo"
|
||||
done
|
||||
fi
|
||||
|
||||
# Convert config.txt
|
||||
if [ -f $1/config.txt ]; then
|
||||
sed "s/\.${ifo}/.${ofo}/g" $1/config.txt > $2/config.txt
|
||||
fi
|
||||
}
|
||||
|
||||
# Check all cardsets.
|
||||
for in_dir in cardset-*; do
|
||||
out_dir=${in_dir}-${ofo}
|
||||
|
||||
if [[ ! $in_dir =~ -$ofo$ ]] && [ ! -d $out_dir ] && chkdir $in_dir; then
|
||||
convdir $in_dir $out_dir
|
||||
echo "$out_dir created"
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# creates a bmp copy off all gif cardsets in the current dir.
|
||||
# uses package 'ImageMagick'
|
||||
|
||||
ifo='gif'
|
||||
if [ $1 ]
|
||||
then
|
||||
ifo=$1
|
||||
fi
|
||||
|
||||
ofo='bmp'
|
||||
if [ $2 ]
|
||||
then
|
||||
ofo=$2
|
||||
fi
|
||||
|
||||
function chkdir()
|
||||
{
|
||||
# convert all images
|
||||
cd $1
|
||||
if [ -f config.txt ]
|
||||
then
|
||||
g=`grep -i ".${ifo}" config.txt`
|
||||
#echo $g
|
||||
if [[ $g == "" ]]
|
||||
then
|
||||
cd ..
|
||||
return 1
|
||||
else
|
||||
cd ..
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
cd ..
|
||||
return 0
|
||||
}
|
||||
|
||||
function convdir()
|
||||
{
|
||||
# convert all images
|
||||
for i in *.${ifo}; do convert $i `basename $i .${ifo}`.${ofo}; rm -f $i; done
|
||||
|
||||
# convert config.txt
|
||||
if [ -f config.txt ]
|
||||
then
|
||||
cp -a config.txt tmp.txt
|
||||
cat tmp.txt | sed "s/.${ifo}/.${ofo}/g" >config.txt
|
||||
rm -f tmp.txt
|
||||
fi
|
||||
}
|
||||
|
||||
# check all cardsets.
|
||||
|
||||
for k in cardset-*
|
||||
do
|
||||
x=${k%-bmp}
|
||||
y=$x-${ofo}
|
||||
|
||||
if chkdir $k
|
||||
then
|
||||
if [ $k != $y ]
|
||||
then
|
||||
if [ -a $y ]
|
||||
then
|
||||
echo "$y exists already - skipped"
|
||||
else
|
||||
echo "$y processing ..."
|
||||
cp -a $k $y
|
||||
cd $y
|
||||
convdir
|
||||
cd ..
|
||||
echo "$y created"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
|
@ -7,14 +7,22 @@ import re
|
|||
from sys import platform
|
||||
|
||||
IS_MAC = (platform == "darwin")
|
||||
PY_VERS = ([] if re.search("\\bSKIP_PY2\\b",
|
||||
os.getenv('TEST_TAGS', '')) else [2])+[3]
|
||||
TEST_TAGS = os.getenv('TEST_TAGS', '')
|
||||
|
||||
|
||||
def _has_tag(tag):
|
||||
return re.search("\\b{}\\b".format(tag), TEST_TAGS)
|
||||
|
||||
|
||||
PY_VERS = ([] if _has_tag('SKIP_PY2') else [2])+[3]
|
||||
SKIP_GTK = _has_tag('SKIP_GTK')
|
||||
module_names = []
|
||||
for d, _, files in os.walk("pysollib"):
|
||||
for f in files:
|
||||
if re.search("\\.py$", f):
|
||||
module_names.append(
|
||||
(d + "/" + re.sub("\\.py$", "", f)).replace("/", "."))
|
||||
(d + "/" + re.sub("\\.py$", "", f))
|
||||
.replace("/", ".").replace(os.sep, "."))
|
||||
|
||||
module_names.sort()
|
||||
for module_name in module_names:
|
||||
|
@ -22,7 +30,7 @@ for module_name in module_names:
|
|||
continue
|
||||
is_gtk = ("gtk" in module_name)
|
||||
for ver in PY_VERS:
|
||||
if ((not is_gtk) or (ver == 2 and (not IS_MAC))):
|
||||
if ((not is_gtk) or (ver == 2 and (not IS_MAC) and (not SKIP_GTK))):
|
||||
def fmt(s):
|
||||
return s % {'module_name': module_name, 'ver': ver}
|
||||
open(os.path.join(".", "tests", "individually-importing", fmt("import_v%(ver)d_%(module_name)s.py")), 'w').write(fmt('''#!/usr/bin/env python%(ver)d
|
||||
|
@ -36,6 +44,7 @@ print('ok 1 - imported')
|
|||
for ver in PY_VERS:
|
||||
for mod in [
|
||||
'pysol_tests.acard_unit',
|
||||
'pysol_tests.game_drag',
|
||||
'pysol_tests.hint',
|
||||
'pysol_tests.import_file1',
|
||||
'pysol_tests.kpat_load_save',
|
||||
|
|
|
@ -1,725 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
# Originally written by Barry Warsaw <barry@zope.com>
|
||||
#
|
||||
# Minimally patched to make it even more xgettext compatible
|
||||
# by Peter Funk <pf@artcom-gmbh.de>
|
||||
#
|
||||
# 2002-11-22 Jürgen Hermann <jh@web.de>
|
||||
# Added checks that _() only contains string literals, and
|
||||
# command line args are resolved to module lists, i.e. you
|
||||
# can now pass a filename, a module or package name, or a
|
||||
# directory (including globbing chars, important for Win32).
|
||||
# Made docstring fit in 80 chars wide displays using pydoc.
|
||||
#
|
||||
# 2007-05-11 Scomoroh <scomoroh@gmail.com>
|
||||
# Added very simple support for ngettext
|
||||
#
|
||||
|
||||
import functools
|
||||
import getopt
|
||||
import glob
|
||||
import imp
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import token
|
||||
import tokenize
|
||||
|
||||
from six import PY2, print_
|
||||
# for selftesting
|
||||
try:
|
||||
import fintl
|
||||
_ = fintl.gettext
|
||||
except ImportError:
|
||||
def _(s):
|
||||
return s
|
||||
|
||||
__doc__ = _("""pygettext -- Python equivalent of xgettext(1)
|
||||
|
||||
Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the
|
||||
internationalization of C programs. Most of these tools are independent of
|
||||
the programming language and can be used from within Python programs.
|
||||
Martin von Loewis' work[1] helps considerably in this regard.
|
||||
|
||||
There's one problem though; xgettext is the program that scans source code
|
||||
looking for message strings, but it groks only C (or C++). Python
|
||||
introduces a few wrinkles, such as dual quoting characters, triple quoted
|
||||
strings, and raw strings. xgettext understands none of this.
|
||||
|
||||
Enter pygettext, which uses Python's standard tokenize module to scan
|
||||
Python source code, generating .pot files identical to what GNU xgettext[2]
|
||||
generates for C and C++ code. From there, the standard GNU tools can be
|
||||
used.
|
||||
|
||||
A word about marking Python strings as candidates for translation. GNU
|
||||
xgettext recognizes the following keywords: gettext, dgettext, dcgettext,
|
||||
and gettext_noop. But those can be a lot of text to include all over your
|
||||
code. C and C++ have a trick: they use the C preprocessor. Most
|
||||
internationalized C source includes a #define for gettext() to _() so that
|
||||
what has to be written in the source is much less. Thus these are both
|
||||
translatable strings:
|
||||
|
||||
gettext("Translatable String")
|
||||
_("Translatable String")
|
||||
|
||||
Python of course has no preprocessor so this doesn't work so well. Thus,
|
||||
pygettext searches only for _() by default, but see the -k/--keyword flag
|
||||
below for how to augment this.
|
||||
|
||||
[1] http://www.python.org/workshops/1997-10/proceedings/loewis.html
|
||||
[2] http://www.gnu.org/software/gettext/gettext.html
|
||||
|
||||
NOTE: pygettext attempts to be option and feature compatible with GNU
|
||||
xgettext where ever possible. However some options are still missing or are
|
||||
not fully implemented. Also, xgettext's use of command line switches with
|
||||
option arguments is broken, and in these cases, pygettext just defines
|
||||
additional switches.
|
||||
|
||||
Usage: pygettext [options] inputfile ...
|
||||
|
||||
Options:
|
||||
|
||||
-a
|
||||
--extract-all
|
||||
Extract all strings.
|
||||
|
||||
-d name
|
||||
--default-domain=name
|
||||
Rename the default output file from messages.pot to name.pot.
|
||||
|
||||
-E
|
||||
--escape
|
||||
Replace non-ASCII characters with octal escape sequences.
|
||||
|
||||
-D
|
||||
--docstrings
|
||||
Extract module, class, method, and function docstrings. These do
|
||||
not need to be wrapped in _() markers, and in fact cannot be for
|
||||
Python to consider them docstrings. (See also the -X option).
|
||||
|
||||
-h
|
||||
--help
|
||||
Print this help message and exit.
|
||||
|
||||
-k word
|
||||
--keyword=word
|
||||
Keywords to look for in addition to the default set, which are:
|
||||
%(DEFAULTKEYWORDS)s
|
||||
|
||||
You can have multiple -k flags on the command line.
|
||||
|
||||
-K
|
||||
--no-default-keywords
|
||||
Disable the default set of keywords (see above). Any keywords
|
||||
explicitly added with the -k/--keyword option are still recognized.
|
||||
|
||||
--no-location
|
||||
Do not write filename/lineno location comments.
|
||||
|
||||
-n
|
||||
--add-location
|
||||
Write filename/lineno location comments indicating where each
|
||||
extracted string is found in the source. These lines appear before
|
||||
each msgid. The style of comments is controlled by the -S/--style
|
||||
option. This is the default.
|
||||
|
||||
-o filename
|
||||
--output=filename
|
||||
Rename the default output file from messages.pot to filename. If
|
||||
filename is `-' then the output is sent to standard out.
|
||||
|
||||
-p dir
|
||||
--output-dir=dir
|
||||
Output files will be placed in directory dir.
|
||||
|
||||
-S stylename
|
||||
--style stylename
|
||||
Specify which style to use for location comments. Two styles are
|
||||
supported:
|
||||
|
||||
Solaris # File: filename, line: line-number
|
||||
GNU #: filename:line
|
||||
|
||||
The style name is case insensitive. GNU style is the default.
|
||||
|
||||
-v
|
||||
--verbose
|
||||
Print the names of the files being processed.
|
||||
|
||||
-V
|
||||
--version
|
||||
Print the version of pygettext and exit.
|
||||
|
||||
-w columns
|
||||
--width=columns
|
||||
Set width of output to columns.
|
||||
|
||||
-x filename
|
||||
--exclude-file=filename
|
||||
Specify a file that contains a list of strings that are not be
|
||||
extracted from the input files. Each string to be excluded must
|
||||
appear on a line by itself in the file.
|
||||
|
||||
-X filename
|
||||
--no-docstrings=filename
|
||||
Specify a file that contains a list of files (one per line) that
|
||||
should not have their docstrings extracted. This is only useful in
|
||||
conjunction with the -D option above.
|
||||
|
||||
If `inputfile' is -, standard input is read.
|
||||
""")
|
||||
|
||||
__version__ = '1.6con'
|
||||
|
||||
default_keywords = ['_']
|
||||
DEFAULTKEYWORDS = ', '.join(default_keywords)
|
||||
default_ngettext_keywords = ['ngettext']
|
||||
|
||||
EMPTYSTRING = ''
|
||||
|
||||
# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's
|
||||
# there.
|
||||
pot_header = _('''\
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\\n"
|
||||
"POT-Creation-Date: %(time)s\\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\\n"
|
||||
"Content-Transfer-Encoding: ENCODING\\n"
|
||||
"Generated-By: pygettext.py %(version)s\\n"
|
||||
|
||||
''')
|
||||
|
||||
|
||||
def usage(code, msg=''):
|
||||
print_(__doc__ % globals(), file=sys.stderr)
|
||||
if msg:
|
||||
print_(msg, file=sys.stderr)
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
escapes = []
|
||||
|
||||
|
||||
def make_escapes(pass_iso8859):
|
||||
global escapes
|
||||
if pass_iso8859:
|
||||
# Allow iso-8859 characters to pass through so that e.g. 'msgid
|
||||
# "Höhe"' would result not result in 'msgid "H\366he"'. Otherwise we
|
||||
# escape any character outside the 32..126 range.
|
||||
mod = 128
|
||||
else:
|
||||
mod = 256
|
||||
for i in range(256):
|
||||
if 32 <= (i % mod) <= 126:
|
||||
escapes.append(chr(i))
|
||||
else:
|
||||
escapes.append("\\%03o" % i)
|
||||
escapes[ord('\\')] = '\\\\'
|
||||
escapes[ord('\t')] = '\\t'
|
||||
escapes[ord('\r')] = '\\r'
|
||||
escapes[ord('\n')] = '\\n'
|
||||
escapes[ord('\"')] = '\\"'
|
||||
|
||||
|
||||
def escape(s):
|
||||
global escapes
|
||||
s = list(s)
|
||||
for i in range(len(s)):
|
||||
s[i] = escapes[ord(s[i])]
|
||||
return EMPTYSTRING.join(s)
|
||||
|
||||
|
||||
def safe_eval(s):
|
||||
# unwrap quotes, safely
|
||||
return eval(s, {'__builtins__': {}}, {})
|
||||
|
||||
|
||||
def normalize(s):
|
||||
# This converts the various Python string types into a format that is
|
||||
# appropriate for .po files, namely much closer to C style.
|
||||
lines = s.split('\n')
|
||||
if len(lines) == 1:
|
||||
s = '"' + escape(s) + '"'
|
||||
else:
|
||||
if not lines[-1]:
|
||||
del lines[-1]
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
for i in range(len(lines)):
|
||||
lines[i] = escape(lines[i])
|
||||
lineterm = '\\n"\n"'
|
||||
s = '""\n"' + lineterm.join(lines) + '"'
|
||||
return s
|
||||
|
||||
|
||||
def containsAny(str, set):
|
||||
"""Check whether 'str' contains ANY of the chars in 'set'"""
|
||||
return 1 in [c in str for c in set]
|
||||
|
||||
|
||||
def _visit_pyfiles(list, dirname, names):
|
||||
"""Helper for getFilesForName()."""
|
||||
# get extension for python source files
|
||||
if '_py_ext' not in globals():
|
||||
global _py_ext
|
||||
_py_ext = [triple[0] for triple in imp.get_suffixes()
|
||||
if triple[2] == imp.PY_SOURCE][0]
|
||||
|
||||
# don't recurse into CVS directories
|
||||
if 'CVS' in names:
|
||||
names.remove('CVS')
|
||||
|
||||
# add all *.py files to list
|
||||
list.extend(
|
||||
[os.path.join(dirname, file) for file in names
|
||||
if os.path.splitext(file)[1] == _py_ext]
|
||||
)
|
||||
|
||||
|
||||
def _get_modpkg_path(dotted_name, pathlist=None):
|
||||
"""Get the filesystem path for a module or a package.
|
||||
|
||||
Return the file system path to a file for a module, and to a directory for
|
||||
a package. Return None if the name is not found, or is a builtin or
|
||||
extension module.
|
||||
"""
|
||||
# split off top-most name
|
||||
parts = dotted_name.split('.', 1)
|
||||
|
||||
if len(parts) > 1:
|
||||
# we have a dotted path, import top-level package
|
||||
try:
|
||||
file, pathname, description = imp.find_module(parts[0], pathlist)
|
||||
if file:
|
||||
file.close()
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
# check if it's indeed a package
|
||||
if description[2] == imp.PKG_DIRECTORY:
|
||||
# recursively handle the remaining name parts
|
||||
pathname = _get_modpkg_path(parts[1], [pathname])
|
||||
else:
|
||||
pathname = None
|
||||
else:
|
||||
# plain name
|
||||
try:
|
||||
file, pathname, description = imp.find_module(
|
||||
dotted_name, pathlist)
|
||||
if file:
|
||||
file.close()
|
||||
if description[2] not in [imp.PY_SOURCE, imp.PKG_DIRECTORY]:
|
||||
pathname = None
|
||||
except ImportError:
|
||||
pathname = None
|
||||
|
||||
return pathname
|
||||
|
||||
|
||||
def getFilesForName(name):
|
||||
"""Get a list of module files for a filename, a module or package name,
|
||||
or a directory.
|
||||
"""
|
||||
if not os.path.exists(name):
|
||||
# check for glob chars
|
||||
if containsAny(name, "*?[]"):
|
||||
files = glob.glob(name)
|
||||
list = []
|
||||
for file in files:
|
||||
list.extend(getFilesForName(file))
|
||||
return list
|
||||
|
||||
# try to find module or package
|
||||
name = _get_modpkg_path(name)
|
||||
if not name:
|
||||
return []
|
||||
|
||||
if os.path.isdir(name):
|
||||
# find all python files in directory
|
||||
list = []
|
||||
os.path.walk(name, _visit_pyfiles, list)
|
||||
return list
|
||||
elif os.path.exists(name):
|
||||
# a single file
|
||||
return [name]
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class TokenEater:
|
||||
def __init__(self, options):
|
||||
self.__options = options
|
||||
self.__messages = {}
|
||||
self.__state = self.__waiting
|
||||
self.__data = []
|
||||
self.__lineno = -1
|
||||
self.__freshmodule = 1
|
||||
self.__curfile = None
|
||||
self.__ngettext = False
|
||||
|
||||
def __call__(self, ttype, tstring, stup, etup, line):
|
||||
# dispatch
|
||||
# import token
|
||||
# print >> sys.stderr, 'ttype:', token.tok_name[ttype], \
|
||||
# 'tstring:', tstring
|
||||
self.__state(ttype, tstring, stup[0])
|
||||
|
||||
def __waiting(self, ttype, tstring, lineno):
|
||||
opts = self.__options
|
||||
# Do docstring extractions, if enabled
|
||||
if opts.docstrings and not opts.nodocstrings.get(self.__curfile):
|
||||
# module docstring?
|
||||
if self.__freshmodule:
|
||||
if ttype == tokenize.STRING:
|
||||
self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
|
||||
self.__freshmodule = 0
|
||||
elif ttype not in (tokenize.COMMENT, tokenize.NL):
|
||||
self.__freshmodule = 0
|
||||
return
|
||||
# class docstring?
|
||||
if ttype == tokenize.NAME and tstring in ('class', 'def'):
|
||||
self.__state = self.__suiteseen
|
||||
return
|
||||
if ttype == tokenize.NAME and tstring in opts.keywords:
|
||||
self.__state = self.__keywordseen
|
||||
self.__ngettext = tstring in opts.ngettext_keywords
|
||||
|
||||
def __suiteseen(self, ttype, tstring, lineno):
|
||||
# ignore anything until we see the colon
|
||||
if ttype == tokenize.OP and tstring == ':':
|
||||
self.__state = self.__suitedocstring
|
||||
|
||||
def __suitedocstring(self, ttype, tstring, lineno):
|
||||
# ignore any intervening noise
|
||||
if ttype == tokenize.STRING:
|
||||
self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
|
||||
self.__state = self.__waiting
|
||||
elif ttype not in (tokenize.NEWLINE, tokenize.INDENT,
|
||||
tokenize.COMMENT):
|
||||
# there was no class docstring
|
||||
self.__state = self.__waiting
|
||||
|
||||
def __keywordseen(self, ttype, tstring, lineno):
|
||||
if ttype == tokenize.OP and tstring == '(':
|
||||
self.__data = []
|
||||
self.__lineno = lineno
|
||||
self.__state = self.__openseen
|
||||
else:
|
||||
self.__state = self.__waiting
|
||||
|
||||
def __openseen(self, ttype, tstring, lineno):
|
||||
if ttype == tokenize.OP and tstring == ')':
|
||||
# We've seen the last of the translatable strings. Record the
|
||||
# line number of the first line of the strings and update the list
|
||||
# of messages seen. Reset state for the next batch. If there
|
||||
# were no strings inside _(), then just ignore this entry.
|
||||
if self.__data:
|
||||
if self.__ngettext:
|
||||
data = []
|
||||
msg = []
|
||||
for s in self.__data:
|
||||
if s is not None:
|
||||
msg.append(s)
|
||||
else:
|
||||
data.append(EMPTYSTRING.join(msg))
|
||||
msg = []
|
||||
if len(data) == 2 and data[0] and data[1]:
|
||||
self.__addentry(tuple(data))
|
||||
elif self.__options.verbose:
|
||||
print_(_(
|
||||
'*** %(file)s:%(lineno)s: incorrect '
|
||||
'ngettext format'
|
||||
) % {
|
||||
'file': self.__curfile,
|
||||
'lineno': self.__lineno}, file=sys.stderr)
|
||||
else:
|
||||
self.__addentry(EMPTYSTRING.join(self.__data))
|
||||
self.__state = self.__waiting
|
||||
elif ttype == tokenize.STRING:
|
||||
self.__data.append(safe_eval(tstring))
|
||||
elif ttype not in [tokenize.COMMENT, token.INDENT, token.DEDENT,
|
||||
token.NEWLINE, tokenize.NL]:
|
||||
if self.__ngettext and ttype == tokenize.OP and tstring == ',':
|
||||
self.__data.append(None)
|
||||
elif self.__ngettext: # and ttype == tokenize.NUMBER:
|
||||
pass
|
||||
else:
|
||||
# warn if we see anything else than STRING or whitespace
|
||||
if self.__options.verbose:
|
||||
print_(_(
|
||||
'*** %(file)s:%(lineno)s: Seen unexpected '
|
||||
'token "%(token)s"'
|
||||
) % {
|
||||
'token': tstring,
|
||||
'file': self.__curfile,
|
||||
'lineno': self.__lineno
|
||||
}, file=sys.stderr)
|
||||
self.__state = self.__waiting
|
||||
|
||||
def __addentry(self, msg, lineno=None, isdocstring=0):
|
||||
if lineno is None:
|
||||
lineno = self.__lineno
|
||||
if msg not in self.__options.toexclude:
|
||||
entry = (self.__curfile, lineno)
|
||||
self.__messages.setdefault(msg, {})[entry] = isdocstring
|
||||
|
||||
def set_filename(self, filename):
|
||||
self.__curfile = filename
|
||||
self.__freshmodule = 1
|
||||
|
||||
def write(self, fp):
|
||||
options = self.__options
|
||||
timestamp = time.ctime(time.time())
|
||||
# The time stamp in the header doesn't have the same format as that
|
||||
# generated by xgettext...
|
||||
print_(pot_header % {'time': timestamp, 'version': __version__},
|
||||
file=fp)
|
||||
# Sort the entries. First sort each particular entry's keys, then
|
||||
# sort all the entries by their first item.
|
||||
reverse = {}
|
||||
for k, v in self.__messages.items():
|
||||
keys = list(v.keys())
|
||||
keys.sort()
|
||||
reverse.setdefault(tuple(keys), []).append((k, v))
|
||||
rkeys = list(reverse.keys())
|
||||
rkeys.sort()
|
||||
for rkey in rkeys:
|
||||
rentries = reverse[rkey]
|
||||
rentries.sort()
|
||||
for k, v in rentries:
|
||||
isdocstring = 0
|
||||
# If the entry was gleaned out of a docstring, then add a
|
||||
# comment stating so. This is to aid translators who may wish
|
||||
# to skip translating some unimportant docstrings.
|
||||
if functools.reduce(operator.__add__, v.values()):
|
||||
isdocstring = 1
|
||||
# k is the message string, v is a dictionary-set of (filename,
|
||||
# lineno) tuples. We want to sort the entries in v first by
|
||||
# file name and then by line number.
|
||||
v = list(v.keys())
|
||||
v.sort()
|
||||
if not options.writelocations:
|
||||
pass
|
||||
# location comments are different b/w Solaris and GNU:
|
||||
elif options.locationstyle == options.SOLARIS:
|
||||
for filename, lineno in v:
|
||||
d = {'filename': filename, 'lineno': lineno}
|
||||
print_(_('# File: %(filename)s, line: %(lineno)d') % d,
|
||||
file=fp)
|
||||
elif options.locationstyle == options.GNU:
|
||||
# fit as many locations on one line, as long as the
|
||||
# resulting line length doesn't exceeds 'options.width'
|
||||
locline = '#:'
|
||||
for filename, lineno in v:
|
||||
d = {'filename': filename, 'lineno': lineno}
|
||||
s = _(' %(filename)s:%(lineno)d') % d
|
||||
if len(locline) + len(s) <= options.width:
|
||||
locline = locline + s
|
||||
else:
|
||||
print_(locline, file=fp)
|
||||
locline = "#:" + s
|
||||
if len(locline) > 2:
|
||||
print_(locline, file=fp)
|
||||
if isdocstring:
|
||||
print_('#, docstring', file=fp)
|
||||
if isinstance(k, str):
|
||||
print_('msgid', normalize(k), file=fp)
|
||||
print_('msgstr ""\n', file=fp)
|
||||
else:
|
||||
# ngettext
|
||||
assert isinstance(k, tuple)
|
||||
assert len(k) == 2
|
||||
print_('msgid', normalize(k[0]), file=fp)
|
||||
print_('msgid_plural', normalize(k[1]), file=fp)
|
||||
print_('msgstr[0] ""', file=fp)
|
||||
print_('msgstr[1] ""\n', file=fp)
|
||||
|
||||
|
||||
def main():
|
||||
global default_keywords
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
'ad:DEhk:Kno:p:S:Vvw:x:X:',
|
||||
['extract-all', 'default-domain=', 'escape', 'help',
|
||||
'keyword=', 'no-default-keywords', 'ngettext-keyword=',
|
||||
'add-location', 'no-location', 'output=', 'output-dir=',
|
||||
'style=', 'verbose', 'version', 'width=', 'exclude-file=',
|
||||
'docstrings', 'no-docstrings',
|
||||
])
|
||||
except getopt.error as msg:
|
||||
usage(1, msg)
|
||||
|
||||
# for holding option values
|
||||
class Options:
|
||||
# constants
|
||||
GNU = 1
|
||||
SOLARIS = 2
|
||||
# defaults
|
||||
extractall = 0 # FIXME: currently this option has no effect at all.
|
||||
escape = 0
|
||||
keywords = []
|
||||
ngettext_keywords = []
|
||||
outpath = ''
|
||||
outfile = 'messages.pot'
|
||||
writelocations = 1
|
||||
locationstyle = GNU
|
||||
verbose = 0
|
||||
width = 78
|
||||
excludefilename = ''
|
||||
docstrings = 0
|
||||
nodocstrings = {}
|
||||
|
||||
options = Options()
|
||||
locations = {'gnu': options.GNU,
|
||||
'solaris': options.SOLARIS,
|
||||
}
|
||||
|
||||
# parse options
|
||||
for opt, arg in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
usage(0)
|
||||
elif opt in ('-a', '--extract-all'):
|
||||
options.extractall = 1
|
||||
elif opt in ('-d', '--default-domain'):
|
||||
options.outfile = arg + '.pot'
|
||||
elif opt in ('-E', '--escape'):
|
||||
options.escape = 1
|
||||
elif opt in ('-D', '--docstrings'):
|
||||
options.docstrings = 1
|
||||
elif opt in ('-k', '--keyword'):
|
||||
options.keywords.append(arg)
|
||||
elif opt in ('--ngettext-keyword'):
|
||||
options.ngettext_keywords.append(arg)
|
||||
elif opt in ('-K', '--no-default-keywords'):
|
||||
default_keywords = []
|
||||
elif opt in ('-n', '--add-location'):
|
||||
options.writelocations = 1
|
||||
elif opt in ('--no-location',):
|
||||
options.writelocations = 0
|
||||
elif opt in ('-S', '--style'):
|
||||
options.locationstyle = locations.get(arg.lower())
|
||||
if options.locationstyle is None:
|
||||
usage(1, _('Invalid value for --style: %s') % arg)
|
||||
elif opt in ('-o', '--output'):
|
||||
options.outfile = arg
|
||||
elif opt in ('-p', '--output-dir'):
|
||||
options.outpath = arg
|
||||
elif opt in ('-v', '--verbose'):
|
||||
options.verbose = 1
|
||||
elif opt in ('-V', '--version'):
|
||||
print(_('pygettext.py (xgettext for Python) %s') % __version__)
|
||||
sys.exit(0)
|
||||
elif opt in ('-w', '--width'):
|
||||
try:
|
||||
options.width = int(arg)
|
||||
except ValueError:
|
||||
usage(1, _('--width argument must be an integer: %s') % arg)
|
||||
elif opt in ('-x', '--exclude-file'):
|
||||
options.excludefilename = arg
|
||||
elif opt in ('-X', '--no-docstrings'):
|
||||
fp = open(arg)
|
||||
try:
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
options.nodocstrings[line[:-1]] = 1
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
# calculate escapes
|
||||
make_escapes(options.escape)
|
||||
|
||||
# calculate all keywords
|
||||
options.keywords.extend(default_keywords)
|
||||
|
||||
options.ngettext_keywords.extend(default_ngettext_keywords)
|
||||
options.keywords.extend(options.ngettext_keywords)
|
||||
|
||||
# initialize list of strings to exclude
|
||||
if options.excludefilename:
|
||||
try:
|
||||
fp = open(options.excludefilename)
|
||||
options.toexclude = fp.readlines()
|
||||
fp.close()
|
||||
except IOError:
|
||||
print_(_("Can't read --exclude-file: %s") %
|
||||
options.excludefilename, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
options.toexclude = []
|
||||
|
||||
# resolve args to module lists
|
||||
expanded = []
|
||||
for arg in args:
|
||||
if arg == '-':
|
||||
expanded.append(arg)
|
||||
else:
|
||||
expanded.extend(getFilesForName(arg))
|
||||
args = expanded
|
||||
|
||||
# slurp through all the files
|
||||
eater = TokenEater(options)
|
||||
for filename in args:
|
||||
if filename == '-':
|
||||
if options.verbose:
|
||||
print(_('Reading standard input'))
|
||||
fp = sys.stdin
|
||||
closep = 0
|
||||
else:
|
||||
if options.verbose:
|
||||
print(_('Working on %s') % filename)
|
||||
fp = open(filename, 'rb')
|
||||
closep = 1
|
||||
try:
|
||||
eater.set_filename(filename)
|
||||
try:
|
||||
if PY2:
|
||||
for token_info in tokenize.generate_tokens(fp.readline):
|
||||
eater(*token_info)
|
||||
else:
|
||||
for token_info in tokenize.tokenize(fp.readline):
|
||||
eater(*token_info)
|
||||
except tokenize.TokenError as e:
|
||||
print_('%s: %s, line %d, column %d' % (
|
||||
e[0], filename, e[1][0], e[1][1]), file=sys.stderr)
|
||||
except tokenize.StopTokenizing:
|
||||
pass
|
||||
finally:
|
||||
if closep:
|
||||
fp.close()
|
||||
|
||||
# write the output
|
||||
if options.outfile == '-':
|
||||
fp = sys.stdout
|
||||
closep = 0
|
||||
else:
|
||||
if options.outpath:
|
||||
options.outfile = os.path.join(options.outpath, options.outfile)
|
||||
fp = open(options.outfile, 'w')
|
||||
closep = 1
|
||||
try:
|
||||
eater.write(fp)
|
||||
finally:
|
||||
if closep:
|
||||
fp.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
# some more test strings
|
||||
_('a unicode string')
|
||||
# this one creates a warning
|
||||
_('*** Seen unexpected token "%(token)s"') % {'token': 'test'}
|
||||
_('more' 'than' 'one' 'string')
|
5
scripts/repack-min-cardsets.bash
Normal file → Executable file
|
@ -10,9 +10,9 @@ set -e -x
|
|||
|
||||
src_base="PySolFC-Cardsets"
|
||||
dest_base="$src_base--Minimal"
|
||||
ver="2.0"
|
||||
ver="2.0.1"
|
||||
src_vbase="$src_base-2.0"
|
||||
dest_vbase="$dest_base-2.0"
|
||||
dest_vbase="$dest_base-2.0.1"
|
||||
src_arc="$src_vbase.tar.bz2"
|
||||
|
||||
if ! test -f "$src_arc"
|
||||
|
@ -21,6 +21,7 @@ then
|
|||
fi
|
||||
|
||||
tar -xvf "$src_arc"
|
||||
rm -rf "$dest_vbase"
|
||||
mkdir -p "$dest_vbase"
|
||||
cat scripts/cardsets_to_bundle | (while read b
|
||||
do
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#! /bin/sh
|
||||
set -e
|
||||
|
||||
#
|
||||
# y.sh
|
||||
# Copyright (C) 2018 shlomif <shlomif@cpan.org>
|
||||
|
|