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
|
- choco install strawberryperl
|
||||||
- copy %PYTHON%\python.exe %PYTHON%\python3.exe
|
- copy %PYTHON%\python.exe %PYTHON%\python3.exe
|
||||||
- SET PATH=%PYTHON%;C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
|
- SET PATH=%PYTHON%;C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
|
||||||
- python3 -mpip install Pillow
|
- python3 -mpip install Pillow attrs configobj flake8 flake8-import-order py2exe pycotap pygame pysol-cards random2 setuptools six
|
||||||
- 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
|
|
||||||
- perl -v
|
- perl -v
|
||||||
- copy C:\msys64\mingw64\bin\mingw32-make.exe C:\msys64\mingw64\bin\make.exe
|
- 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%
|
- 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
|
- path: dist\PySolFC_*_setup.exe
|
||||||
name: pysol_win_installer
|
name: pysol_win_installer
|
||||||
cache:
|
cache:
|
||||||
- C:\_P5 -> .appveyor.yml
|
# - C:\_P5 -> .appveyor.yml
|
||||||
- C:\strawberry -> .appveyor.yml
|
# - C:\strawberry -> .appveyor.yml
|
||||||
shallow_clone: true
|
shallow_clone: true
|
||||||
|
|
12
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
/.tidyall.d/
|
/.tidyall.d/
|
||||||
/MANIFEST
|
/MANIFEST
|
||||||
|
/_Inline/
|
||||||
/build/*
|
/build/*
|
||||||
/data/cardsets/*
|
/data/cardsets/*
|
||||||
/data/html/*
|
/data/html/*
|
||||||
|
@ -10,15 +11,16 @@
|
||||||
/html-src/html/*
|
/html-src/html/*
|
||||||
/images
|
/images
|
||||||
/locale/*
|
/locale/*
|
||||||
|
/po/de.po
|
||||||
/po/it.po
|
/po/it.po
|
||||||
/po/pl.po
|
/po/pl.po
|
||||||
/po/ru.po
|
/po/ru.po
|
||||||
/tests/individually-importing/*.py
|
/tests/individually-importing/*.py
|
||||||
/tests/unit-generated/*.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--Minimal-2.0.*
|
||||||
PySolFC-Cardsets-2.0/
|
PySolFC-Cardsets--Minimal-2.0/
|
||||||
PySolFC-Cardsets-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:
|
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:
|
homebrew:
|
||||||
update: true
|
update: true
|
||||||
brewfile: true
|
brewfile: true
|
||||||
|
@ -32,32 +16,34 @@ deploy:
|
||||||
repo: shlomif/PySolFC
|
repo: shlomif/PySolFC
|
||||||
tags: true
|
tags: true
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
dist: trusty
|
dist: bionic
|
||||||
before_install:
|
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
|
- 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:
|
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
|
- sudo cpanm Code::TidyAll::Plugin::Flake8 Perl::Tidy Test::Code::TidyAll Test::Differences Test::TrailingSpace
|
||||||
- export PY_MODS='pycotap pysol-cards random2 six'
|
- export PY_MODS='attrs configobj pycotap pysol-cards random2 setuptools six'
|
||||||
- "`which python3` -m pip install --upgrade flake8 flake8-import-order $PY_MODS"
|
- sudo pip3 install --upgrade $PY_MODS flake8 flake8-import-order
|
||||||
- "sudo /usr/bin/python3 -m pip install --upgrade $PY_MODS || true"
|
- sudo pip2 install --upgrade $PY_MODS
|
||||||
- "sudo `which python2` -m pip install --upgrade $PY_MODS"
|
language: generic
|
||||||
- which python
|
|
||||||
- which python2
|
|
||||||
language: python
|
|
||||||
python: "3.6"
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
- 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
|
- 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:
|
script:
|
||||||
- export TIDYALL_DATA_DIR="$HOME/tidyall_d"
|
- export TIDYALL_DATA_DIR="$HOME/tidyall_d"
|
||||||
- bash -x scripts/travis-ci-build
|
- bash -x scripts/travis-ci-build
|
||||||
|
|
|
@ -12,6 +12,7 @@ for general guidelines for contributing to open source.
|
||||||
# How you can contribute
|
# How you can contribute
|
||||||
|
|
||||||
- Translate PySol to a human language you know.
|
- 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 reproduce [open issues](https://github.com/shlomif/PySolFC/issues)
|
||||||
- Try to fix bugs.
|
- Try to fix bugs.
|
||||||
- Add new games.
|
- Add new games.
|
||||||
|
@ -20,6 +21,7 @@ for general guidelines for contributing to open source.
|
||||||
- Add new features.
|
- Add new features.
|
||||||
- Contribute graphics
|
- Contribute graphics
|
||||||
- Improve the site
|
- Improve the site
|
||||||
|
- Package PySol for a new package repository or OS, or update existing packages.
|
||||||
- Make a monetary donation.
|
- 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
|
- [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 COPYING README.md AUTHORS.md README.android README.kivy
|
||||||
include NEWS.asciidoc
|
include NEWS.asciidoc
|
||||||
#recursive-include pysollib *.py
|
#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/winsystems/*.py
|
||||||
include pysollib/tk/*.py pysollib/tile/*.py pysollib/pysolgtk/*.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/*.py pysollib/games/special/*.py
|
||||||
include pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py
|
include pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py
|
||||||
include data/tcl/*.tcl
|
include data/tcl/*.tcl
|
||||||
|
@ -18,8 +19,8 @@ include data/pysolfc.glade
|
||||||
graft data/themes
|
graft data/themes
|
||||||
recursive-exclude data/themes *.py
|
recursive-exclude data/themes *.py
|
||||||
include scripts/build.bat scripts/create_iss.py scripts/mahjongg_utils.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/all_games.py scripts/cardset_viewer.py
|
||||||
include scripts/cardconv scripts/cardsetsgiftobmp
|
include scripts/cardconv
|
||||||
include scripts/gen_individual_importing_tests.py
|
include scripts/gen_individual_importing_tests.py
|
||||||
include tests/individually-importing/PLACEHOLDER
|
include tests/individually-importing/PLACEHOLDER
|
||||||
recursive-include tests/lib *.pm *.py
|
recursive-include tests/lib *.pm *.py
|
||||||
|
@ -29,6 +30,8 @@ include tests/unit-generated/PLACEHOLDER
|
||||||
include .tidyallrc
|
include .tidyallrc
|
||||||
include android/*.py
|
include android/*.py
|
||||||
include android/mk*
|
include android/mk*
|
||||||
|
include android/initsdk
|
||||||
|
include android/initsupport
|
||||||
include android/debian/*
|
include android/debian/*
|
||||||
#graft data/plugins
|
#graft data/plugins
|
||||||
##
|
##
|
||||||
|
|
83
Makefile
|
@ -1,82 +1,73 @@
|
||||||
# Makefile for PySolFC
|
# Makefile for PySolFC
|
||||||
|
|
||||||
override LANG=C
|
ifeq ($(OS),Windows_NT)
|
||||||
override PYSOL_DEBUG=1
|
path_sep = ;
|
||||||
|
else
|
||||||
|
path_sep = :
|
||||||
|
endif
|
||||||
|
export PYTHONPATH := $(PYTHONPATH)$(path_sep)$(CURDIR)
|
||||||
|
|
||||||
PYSOLLIB_FILES=pysollib/tk/*.py pysollib/tile/*.py pysollib/*.py \
|
.PHONY: all install dist rpm all_games_html rules pot mo pretest test runtest
|
||||||
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
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@echo "No default target"
|
@echo "No default target"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
python setup.py install
|
python3 setup.py install
|
||||||
|
|
||||||
dist: all_games_html rules mo
|
dist: all_games_html rules mo
|
||||||
python3 setup.py sdist
|
python3 setup.py sdist
|
||||||
|
|
||||||
rpm: all_games_html rules mo
|
rpm: all_games_html rules mo
|
||||||
python setup.py bdist_rpm
|
python3 setup.py bdist_rpm
|
||||||
|
|
||||||
all_games_html:
|
DOCS_DIR = docs
|
||||||
PYTHONPATH=`pwd` ./scripts/all_games.py html id doc/rules bare > docs/all_games.html
|
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:
|
rules:
|
||||||
export PYTHONPATH=`pwd`; (cd html-src && ./gen-html.py)
|
cd html-src && ./gen-html.py
|
||||||
cp -r html-src/images html-src/html
|
cp -r html-src/images html-src/html
|
||||||
rm -rf data/html
|
rm -rf data/html
|
||||||
mv html-src/html data
|
mv html-src/html data
|
||||||
|
|
||||||
pot:
|
pot:
|
||||||
PYTHONPATH=`pwd` ./scripts/all_games.py gettext > po/games.pot
|
./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 --keyword=n_ --add-comments=TRANSLATORS: -o po/pysol.pot \
|
||||||
xgettext -L C --keyword=N_ -o po/pysol-2.pot data/glade-translations
|
pysollib/*.py pysollib/*/*.py pysollib/*/*/*.py data/pysolfc.glade
|
||||||
msgcat po/pysol-1.pot po/pysol-2.pot > po/pysol.pot
|
set -e; \
|
||||||
rm -f po/pysol-1.pot po/pysol-2.pot
|
for lng in ru de pl it; do \
|
||||||
for lng in ru pl; do \
|
msgmerge --update --quiet --backup=none po/$${lng}_pysol.po po/pysol.pot; \
|
||||||
mv -f po/$${lng}_pysol.po po/$${lng}_pysol.old.po; \
|
msgmerge --update --quiet --backup=none po/$${lng}_games.po po/games.pot; \
|
||||||
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; \
|
|
||||||
done
|
done
|
||||||
|
|
||||||
mo:
|
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 \
|
set -e; \
|
||||||
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
|
|
||||||
for lang in ru de pl it; do \
|
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
|
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:
|
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
|
python3 scripts/gen_individual_importing_tests.py
|
||||||
|
|
||||||
TEST_ENV_PATH = "`pwd`:`pwd`/tests/lib"
|
TEST_ENV_PATH = $(CURDIR)$(path_sep)$(CURDIR)/tests/lib
|
||||||
TEST_ENV = PYTHONPATH="$$PYTHONPATH:"$(TEST_ENV_PATH) PERL5LIB="$$PERL5LIB:"$(TEST_ENV_PATH)
|
|
||||||
TEST_FILES = tests/style/*.t tests/unit-generated/*.py tests/individually-importing/*.py
|
TEST_FILES = tests/style/*.t tests/unit-generated/*.py tests/individually-importing/*.py
|
||||||
|
|
||||||
define RUN_TESTS
|
test runtest: export PYTHONPATH := $(PYTHONPATH)$(path_sep)$(TEST_ENV_PATH)
|
||||||
$(TEST_ENV) $1 $(TEST_FILES)
|
test runtest: export PERL5LIB := $(PERL5LIB)$(path_sep)$(TEST_ENV_PATH)
|
||||||
endef
|
|
||||||
|
|
||||||
test: pretest
|
test: pretest
|
||||||
$(call RUN_TESTS,prove)
|
prove $(TEST_FILES)
|
||||||
|
|
||||||
runtest: pretest
|
runtest: pretest
|
||||||
$(call RUN_TESTS,runprove)
|
runprove $(TEST_FILES)
|
||||||
|
|
|
@ -45,14 +45,15 @@ Prerequisites (needs root):
|
||||||
|
|
||||||
Build with 'python-for-android' (as user):
|
Build with 'python-for-android' (as user):
|
||||||
|
|
||||||
|
Use the cloned repo or an unpacked distribution tarball.
|
||||||
|
|
||||||
go to the android directory, then
|
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.
|
$ ./mkkeystore # if you want to build a release version.
|
||||||
|
|
||||||
$ ./mkp4a.debug # build debug apk
|
$ ./mkp4a.debug # build debug apk
|
||||||
$ ./mkp4a.release <passwd1> [<passwd2>] # build release apk
|
$ ./mkp4a.release <passwd1> [<passwd2>] # build release apk
|
||||||
$ ./mkp4a.unsigned # build an unsigned release apk
|
|
||||||
|
|
||||||
The build system will download all required additional
|
The build system will download all required additional
|
||||||
packages (such as the android sdk and more). Do the first build will
|
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:
|
||||||
|
|
||||||
Cardsets should be installed in ${HOME}/.PySolFC/cardsets/. On an
|
The Apk includes a minimal set of cards for playing.
|
||||||
android device this is equivalent to /sdcard/.PySolFC/cardsets/.
|
|
||||||
Cardsets must use the bmp image format. Use scripts/cardsetsgiftobmp
|
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.
|
(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:
|
Possible known build issues:
|
||||||
|
|
||||||
2) for android ndk: needs a Version <=13 (because needs ant support).
|
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
|
Additional cardsets are available from the SourceForge
|
||||||
project. To use them with kivy, they need to be converted to
|
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).
|
to the scripts directory (it requires Bash and ImageMagick).
|
||||||
|
|
||||||
For all GIF images in the directories data/images and data/tiles ,
|
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.
|
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.
|
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:
|
This is kind of stupid and maybe it can be fixed in the future, but for now:
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install six
|
pip install six
|
||||||
pip install random2
|
pip install random2
|
||||||
|
pip install pysol-cards
|
||||||
```
|
```
|
||||||
|
|
||||||
You may want to use your OS distribution package system instead, for example:
|
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
|
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
|
#### 2 - Clone the source from version control
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -122,10 +129,10 @@ cd PySolFC
|
||||||
#### 3 - Create your virtual environment.
|
#### 3 - Create your virtual environment.
|
||||||
|
|
||||||
```
|
```
|
||||||
PKGDIR=/usr/local/packages/PySolFC # or whatever
|
PKGTREE=/usr/local/packages/PySolFC # or whatever
|
||||||
export PKGDIR
|
export PKGTREE
|
||||||
mkdir -p "$PKGDIR"
|
mkdir -p "$PKGTREE"
|
||||||
( cd "$PKGDIR" && python -m venv ./env )
|
( cd "$PKGTREE" && python -m venv ./env )
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4 - Run the install script
|
#### 4 - Run the install script
|
||||||
|
@ -139,12 +146,11 @@ mkdir -p "$PKGDIR"
|
||||||
#### 6 - Enjoy playing
|
#### 6 - Enjoy playing
|
||||||
|
|
||||||
```
|
```
|
||||||
"$PKGDIR"/env/bin/pysol.py
|
"$PKGTREE"/env/bin/pysol.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Alternate toolkit.
|
## Alternate toolkit.
|
||||||
|
|
||||||
- Python2 (2.7 or later)
|
|
||||||
- Kivy (10.0 or later)
|
- Kivy (10.0 or later)
|
||||||
|
|
||||||
- Features:
|
- Features:
|
||||||
|
@ -154,7 +160,7 @@ mkdir -p "$PKGDIR"
|
||||||
- Running from source without installation:
|
- Running from source without installation:
|
||||||
|
|
||||||
```
|
```
|
||||||
python2 pysol.py --kivy
|
python pysol.py --kivy
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuring Freecell Solver
|
### Configuring Freecell Solver
|
||||||
|
|
|
@ -10,9 +10,6 @@ root$ ./apt-install.sh
|
||||||
|
|
||||||
root$ exit
|
root$ exit
|
||||||
|
|
||||||
user$ ./pip-install.sh
|
|
||||||
- .....some output.
|
|
||||||
|
|
||||||
Now all required packages are installed to proceed with the
|
Now all required packages are installed to proceed with the
|
||||||
android build.
|
android build.
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
# als root ausführen!
|
set -e
|
||||||
|
|
||||||
apt-get install -y mercurial git default-jdk
|
# install as root
|
||||||
apt-get install -y cython cython3
|
|
||||||
apt-get install -y python3-pip
|
apt-get install -y \
|
||||||
apt-get install -y python3-yaml
|
git \
|
||||||
apt-get install -y virtualenv
|
openjdk-8-jdk \
|
||||||
apt-get install -y pkg-config
|
cython3 \
|
||||||
apt-get install -y automake autoconf libtool
|
python3-pip \
|
||||||
apt-get install -y zlib1g-dev
|
python3-yaml \
|
||||||
apt-get install -y libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
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
|
# PySol -- a Python Solitaire game
|
||||||
|
|
|
@ -1,46 +1,27 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
. mkp4a.common
|
||||||
|
|
||||||
echo '### prepare cardsets'
|
echo '### prepare cardsets'
|
||||||
|
|
||||||
if [ ! -f ./PySolFC-Cardsets-2.0.tar.bz2 ]
|
if [ ! -f ${cardsets_file} ]; then
|
||||||
then
|
echo '### downloading cardsets'
|
||||||
echo '### downloading cardets'
|
wget https://netix.dl.sourceforge.net/project/pysolfc/PySolFC-Cardsets/minimal/${cardsets_file}
|
||||||
# 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
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f ./PySolFC-Cardsets-2.0.tar.bz2 ]
|
if [ ! -d ${cardsets_dir} ]; then
|
||||||
then
|
echo '### extracting cardsets'
|
||||||
if [ ! -d ./PySolFC-Cardsets-2.0 ]
|
tar -xf ${cardsets_file}
|
||||||
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'
|
|
||||||
fi
|
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'
|
echo '### end cardsets'
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
# memo:
|
# memo:
|
||||||
# keytool -genkey -v -keystore my-release-keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 12000
|
# keytool -genkey -v -keystore my-release-keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 12000
|
||||||
|
|
||||||
if [ ! -d bin ]
|
mkdir -p bin
|
||||||
then
|
|
||||||
echo "mkdir bin"
|
|
||||||
mkdir bin
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f ./bin/keystore ]
|
if [ -f ./bin/keystore ]; then
|
||||||
then
|
|
||||||
echo "keystore is already defined"
|
echo "keystore is already defined"
|
||||||
else
|
else
|
||||||
keytool -genkey -v -keystore ./bin/keystore -alias python -keyalg RSA -keysize 2048 -validity 12000
|
keytool -genkey -v -keystore ./bin/keystore -alias python -keyalg RSA -keysize 2048 -validity 12000
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
rm -rf tmp
|
rm -rf tmp
|
||||||
python3 -m pythonforandroid.toolchain clean_dists
|
python3 -m pythonforandroid.toolchain clean_dists
|
||||||
python3 -m pythonforandroid.toolchain clean_builds
|
python3 -m pythonforandroid.toolchain clean_builds
|
||||||
rm -f *.apk
|
rm -f *.apk
|
||||||
rm -rf PySolFC-Cardsets-2.0
|
rm -rf PySolFC-Cardsets--Minimal-2.0.1
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
rm -rf tmp
|
rm -rf tmp
|
||||||
rm -f *.apk
|
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
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
package='org.lufebe16.pysolfc'
|
. mkp4a.common
|
||||||
version=`./version.py`
|
|
||||||
name='PySolFC'
|
|
||||||
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
|
||||||
|
|
||||||
if [ "$1" ]
|
new_options=${p4a_options}
|
||||||
then
|
new_options=${new_options/PySolFC/PySolFCdbg}
|
||||||
package=${package}.dbg
|
new_options=${new_options/org.lufebe16.pysolfc/org.lufebe16.pysolfc.dbg}
|
||||||
name=${name}dbg
|
|
||||||
fi
|
|
||||||
|
|
||||||
python3 -m pythonforandroid.toolchain apk \
|
python3 -m pythonforandroid.toolchain apk \
|
||||||
--sdk-dir ${HOME}/.cache/sdk-for-p4a \
|
${new_options}
|
||||||
--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
|
|
||||||
|
|
|
@ -1,56 +1,62 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
. mkp4a.common
|
||||||
|
|
||||||
|
# NOTE: $1 will be set with fdroid builds only.
|
||||||
|
|
||||||
echo '### prepare sdk'
|
echo '### prepare sdk'
|
||||||
|
|
||||||
./initsdk.py $1 $2 $3
|
if [[ $# == 0 ]]
|
||||||
|
then
|
||||||
|
./initsupport
|
||||||
|
./initsdk
|
||||||
|
fi
|
||||||
|
|
||||||
echo '### install p4a'
|
echo '### install p4a'
|
||||||
|
|
||||||
p4adir=${HOME}/.cache/tmp-for-p4a
|
if [[ $# == 0 ]]
|
||||||
mkdir -p ${p4adir}
|
|
||||||
|
|
||||||
p4aversion='0.5.3'
|
|
||||||
if [ ! -f ${p4adir}/${p4aversion}.zip ]
|
|
||||||
then
|
then
|
||||||
wget "https://github.com/kivy/python-for-android/archive/${p4aversion}.zip"
|
python3 -m pip install -q --user python-for-android
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo '### prepare source'
|
echo '### prepare source'
|
||||||
|
|
||||||
srcdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
(cd .. && make rules && make all_games_html && make mo)
|
||||||
|
|
||||||
mkdir -p ${srcdir}
|
mkdir -p ${tmpdir}
|
||||||
rm -rf ${srcdir}
|
rm -rf ${tmpdir}
|
||||||
cp -a .. ${srcdir}
|
cp -a .. ${tmpdir}
|
||||||
rm -rf ${srcdir}/android
|
rm -rf ${tmpdir}/android
|
||||||
rm -rf ${srcdir}/src
|
rm -rf ${tmpdir}/src
|
||||||
cp -a main.py ${srcdir}/main.py
|
|
||||||
mkdir -p ${srcdir}/data/images/cards/bottoms/trumps-only
|
# remove useless load from the app
|
||||||
echo "" > ${srcdir}/data/images/cards/bottoms/trumps-only/.keep
|
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'
|
echo '### prepare cardsets'
|
||||||
|
|
||||||
cardsdir=${HOME}/.cache/tmp-for-p4a/pysolfc
|
./mkcards
|
||||||
|
|
||||||
if [ ! -d ${cardsdir}/PySolFC-Cardsets-2.0 ]
|
cp -a ${cardsets_dir}/* ${tmpdir}/data
|
||||||
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
|
|
||||||
|
|
||||||
echo '### end init'
|
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
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. mkp4a.common
|
||||||
|
|
||||||
pass1=""
|
pass1=""
|
||||||
pass2=""
|
pass2=""
|
||||||
keyalias="python"
|
keyalias="python"
|
||||||
keystore="${PWD}/bin/keystore"
|
keystore="${PWD}/bin/keystore"
|
||||||
if [ $1 ]
|
if [ $1 ]; then
|
||||||
then
|
|
||||||
pass1=$1
|
pass1=$1
|
||||||
pass2=$1
|
pass2=$1
|
||||||
else
|
else
|
||||||
|
@ -13,16 +15,13 @@ else
|
||||||
echo " (use ./mkkeystore to create one in default location)"
|
echo " (use ./mkkeystore to create one in default location)"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if [ $2 ]
|
if [ $2 ]; then
|
||||||
then
|
|
||||||
pass2=$2
|
pass2=$2
|
||||||
fi
|
fi
|
||||||
if [ $3 ]
|
if [ $3 ]; then
|
||||||
then
|
|
||||||
keyalias=$3
|
keyalias=$3
|
||||||
fi
|
fi
|
||||||
if [ $4 ]
|
if [ $4 ]; then
|
||||||
then
|
|
||||||
keystore=$4
|
keystore=$4
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -31,30 +30,10 @@ export P4A_RELEASE_KEYSTORE_PASSWD="$pass1"
|
||||||
export P4A_RELEASE_KEYALIAS_PASSWD="$pass2"
|
export P4A_RELEASE_KEYALIAS_PASSWD="$pass2"
|
||||||
export P4A_RELEASE_KEYALIAS="$keyalias"
|
export P4A_RELEASE_KEYALIAS="$keyalias"
|
||||||
|
|
||||||
version=`./version.py`
|
|
||||||
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
|
|
||||||
|
|
||||||
python3 -m pythonforandroid.toolchain apk \
|
python3 -m pythonforandroid.toolchain apk \
|
||||||
--sdk-dir ${HOME}/.cache/sdk-for-p4a \
|
${p4a_options} \
|
||||||
--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 \
|
|
||||||
--release \
|
--release \
|
||||||
--sign \
|
--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
|
|
||||||
|
|
||||||
# keystore options (instead environment vars):
|
# keystore options (instead environment vars):
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,47 +1,21 @@
|
||||||
#!/bin/bash
|
#!/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"
|
if [[ $# == 2 ]]
|
||||||
ndkdir="${HOME}/.cache/sdk-for-p4a/ndk-bundle"
|
|
||||||
if [ $1 ]
|
|
||||||
then
|
then
|
||||||
echo "set sdk to: $1"
|
sdkdir=$1
|
||||||
sdkdir="$1"
|
ndkdir=$2
|
||||||
fi
|
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 \
|
python3 -m pythonforandroid.toolchain apk \
|
||||||
|
${p4a_options} \
|
||||||
--sdk-dir ${sdkdir} \
|
--sdk-dir ${sdkdir} \
|
||||||
--ndk-dir ${ndkdir} \
|
--ndk-dir ${ndkdir} \
|
||||||
--android-api 19 \
|
--release
|
||||||
--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
|
|
||||||
|
|
||||||
# python3 -m pythonforandroid.toolchain apk
|
# python3 -m pythonforandroid.toolchain apk
|
||||||
# ...
|
# ...
|
||||||
|
@ -53,5 +27,3 @@ python3 -m pythonforandroid.toolchain apk \
|
||||||
# ohne: -> debug version
|
# ohne: -> debug version
|
||||||
# 1: -> release unsigned
|
# 1: -> release unsigned
|
||||||
# 1 und 2: -> release version.
|
# 1 und 2: -> release version.
|
||||||
|
|
||||||
echo '### p4a finished'
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#! /bin/sh -Cefu
|
#!/bin/bash
|
||||||
|
set -Cefu
|
||||||
|
|
||||||
: ${PKGTREE:=/usr/local/packages/PySolFC}
|
: ${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")"
|
PYPROG="$(printf '%s/env/bin/python' "$PKGTREE")"
|
||||||
VERSION="$(env PYTHONPATH=`pwd` "$PYPROG" -c 'from pysollib.settings import VERSION ; print(VERSION)')"
|
VERSION="$(env PYTHONPATH=`pwd` "$PYPROG" -c 'from pysollib.settings import VERSION ; print(VERSION)')"
|
||||||
XZBALL="$(printf 'dist/PySolFC-%s.tar.xz' "$VERSION")"
|
XZBALL="$(printf 'dist/PySolFC-%s.tar.xz' "$VERSION")"
|
||||||
|
@ -10,6 +11,6 @@ reqs='six random2 pillow'
|
||||||
make dist
|
make dist
|
||||||
for req in $reqs
|
for req in $reqs
|
||||||
do
|
do
|
||||||
"$PIP" "$req"
|
"${PIP[@]}" "$req"
|
||||||
done
|
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
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
# convert gif to png - dir recursive
|
# convert gif to png - dir recursive
|
||||||
|
|
||||||
# scharf !!!!
|
# scharf !!!!
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Encoding=UTF-8
|
|
||||||
Name=PySol Fan Club Edition
|
Name=PySol Fan Club Edition
|
||||||
|
Comment=More than 1000 solitaire card games
|
||||||
Exec=pysol.py
|
Exec=pysol.py
|
||||||
|
StartupWMClass=Pysol
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Game;CardGame;
|
Categories=Game;CardGame;
|
||||||
Icon=/usr/share/icons/pysol01.png
|
Keywords=solitaire;patience;cards;pysolfc;klondike;
|
||||||
|
Icon=pysol
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
from_dir=images
|
from_dir=images
|
||||||
to_dir=clearlooks
|
to_dir=clearlooks
|
||||||
|
|
|
@ -69,9 +69,9 @@ the current directory
|
||||||
.br
|
.br
|
||||||
/usr/local/share/PySolFC
|
/usr/local/share/PySolFC
|
||||||
.br
|
.br
|
||||||
/usr/games/PySolFC
|
/usr/share/games/PySolFC
|
||||||
.br
|
.br
|
||||||
/usr/local/games/PySolFC
|
/usr/local/share/games/PySolFC
|
||||||
.PP
|
.PP
|
||||||
Options are saved in \fB~/.PySolFC/options.cfg\fR
|
Options are saved in \fB~/.PySolFC/options.cfg\fR
|
||||||
.PP
|
.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
|
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
|
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
|
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
|
ten and two court cards, the Wazir and the Mir. The order of the suits
|
||||||
the same as games that use the standard deck but the larger number of different
|
is: Matsya, Kuchha, Varaha, Narsingha, Vamana, Parashurama, Rama,
|
||||||
cards in a deck (96 or 120) adds an element of complexity. The fact that each
|
Krishna, Buddha, and Kalanki. Ganjifa solitaire games play the same
|
||||||
suit has it's own color makes things quite interesting in games that use
|
as games that use the standard deck but the larger number of different
|
||||||
"Alternate Color" row stacks.
|
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>
|
<p>
|
||||||
This is a Freecell type of game. Cards on the tableau build down in rank
|
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.
|
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
|
regardless of the suit. Only a King or the highest trump can be played
|
||||||
on an empty row.
|
on an empty row.
|
||||||
|
|
||||||
|
|
4989
po/de_pysol.po
Normal file
11
po/games.pot
|
@ -5,7 +5,7 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PySol 0.0.1\n"
|
"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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -702,6 +702,9 @@ msgstr ""
|
||||||
msgid "Czarina"
|
msgid "Czarina"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Daddy Longlegs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Danda"
|
msgid "Danda"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1272,6 +1275,12 @@ msgstr ""
|
||||||
msgid "FreeCell"
|
msgid "FreeCell"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "FreeCell with Two Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "FreeCell with Zero Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Frog"
|
msgid "Frog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,16 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: it_games\n"
|
"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"
|
"PO-Revision-Date: 2011-05-12 18:46+0200\n"
|
||||||
"Last-Translator: Giuliano Colla <giuliano.colla@gmail.com>\n"
|
"Last-Translator: Giuliano Colla <giuliano.colla@gmail.com>\n"
|
||||||
"Language-Team: Italiano <it@li.org>\n"
|
"Language-Team: Italiano <it@li.org>\n"
|
||||||
|
"Language: it\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: ./scripts/all_games.py 0.1\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"
|
"X-Generator: KBabel 1.11.4\n"
|
||||||
|
|
||||||
msgid " 3x3 Matrix"
|
msgid " 3x3 Matrix"
|
||||||
|
@ -75,6 +77,9 @@ msgstr "Conoscenti"
|
||||||
msgid "Adela"
|
msgid "Adela"
|
||||||
msgstr "Adelina"
|
msgstr "Adelina"
|
||||||
|
|
||||||
|
msgid "Aglet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Agnes Bernauer"
|
msgid "Agnes Bernauer"
|
||||||
msgstr "Agnese Bernauer"
|
msgstr "Agnese Bernauer"
|
||||||
|
|
||||||
|
@ -243,6 +248,12 @@ msgstr ""
|
||||||
msgid "Bavarian Patience"
|
msgid "Bavarian Patience"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Bayan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Beacon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Beak and Flipper"
|
msgid "Beak and Flipper"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -522,6 +533,9 @@ msgstr ""
|
||||||
msgid "Cat's Tail"
|
msgid "Cat's Tail"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Catherine the Great"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cavalier"
|
msgid "Cavalier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -690,6 +704,9 @@ msgstr ""
|
||||||
msgid "Czarina"
|
msgid "Czarina"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Daddy Longlegs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Danda"
|
msgid "Danda"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -804,6 +821,9 @@ msgstr ""
|
||||||
msgid "Double Bisley"
|
msgid "Double Bisley"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Double Blue Moon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Double Canfield"
|
msgid "Double Canfield"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -876,12 +896,18 @@ msgstr ""
|
||||||
msgid "Double Measure"
|
msgid "Double Measure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Double Montana"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Double Pyramid"
|
msgid "Double Pyramid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Double Rail"
|
msgid "Double Rail"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Double Red Moon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Double Russian Solitaire"
|
msgid "Double Russian Solitaire"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -942,6 +968,9 @@ msgstr ""
|
||||||
msgid "Dutch Solitaire"
|
msgid "Dutch Solitaire"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Dutchess"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Eagle Wing"
|
msgid "Eagle Wing"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -972,6 +1001,9 @@ msgstr ""
|
||||||
msgid "Eight Off"
|
msgid "Eight Off"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Eight Packs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Eight Sages"
|
msgid "Eight Sages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1158,6 +1190,9 @@ msgstr ""
|
||||||
msgid "Flying Dragon"
|
msgid "Flying Dragon"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Foothold"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Footling"
|
msgid "Footling"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1242,6 +1277,12 @@ msgstr ""
|
||||||
msgid "FreeCell"
|
msgid "FreeCell"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "FreeCell with Two Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "FreeCell with Zero Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Frog"
|
msgid "Frog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1425,6 +1466,9 @@ msgstr ""
|
||||||
msgid "Hanoi Puzzle 6"
|
msgid "Hanoi Puzzle 6"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Hanoi Sequence"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Happy New Year"
|
msgid "Happy New Year"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1638,6 +1682,9 @@ msgstr ""
|
||||||
msgid "Katrina's Game Relaxed"
|
msgid "Katrina's Game Relaxed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Kentish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Khadga"
|
msgid "Khadga"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2310,7 +2357,7 @@ msgstr ""
|
||||||
msgid "Mahjongg Stargate"
|
msgid "Mahjongg Stargate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Mahjongg Step Pyramid"
|
msgid "Mahjongg Steps Pyramid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Mahjongg Stonehenge"
|
msgid "Mahjongg Stonehenge"
|
||||||
|
@ -2652,6 +2699,15 @@ msgstr ""
|
||||||
msgid "Northwest Territory"
|
msgid "Northwest Territory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Not Shisen-Sho 14x6"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Not Shisen-Sho 18x8"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Not Shisen-Sho 24x12"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Number Ten"
|
msgid "Number Ten"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2904,6 +2960,9 @@ msgstr ""
|
||||||
msgid "Puss in the Corner"
|
msgid "Puss in the Corner"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Putt Putt"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Pyramid"
|
msgid "Pyramid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2925,12 +2984,18 @@ msgstr ""
|
||||||
msgid "Quadrangle"
|
msgid "Quadrangle"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Quadrille"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Quadruple Alliance"
|
msgid "Quadruple Alliance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Quads"
|
msgid "Quads"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Quads +"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Quartets"
|
msgid "Quartets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3000,6 +3065,10 @@ msgstr ""
|
||||||
msgid "Relax"
|
msgid "Relax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Relaxed Accordion"
|
||||||
|
msgstr "Fisarmonica"
|
||||||
|
|
||||||
msgid "Relaxed FreeCell"
|
msgid "Relaxed FreeCell"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3015,6 +3084,9 @@ msgstr ""
|
||||||
msgid "Relaxed Spider"
|
msgid "Relaxed Spider"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Relaxed Three Fir-trees"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Repair"
|
msgid "Repair"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3228,13 +3300,13 @@ msgstr ""
|
||||||
msgid "Shifting"
|
msgid "Shifting"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Shisen-Sho (No Gra) 14x6"
|
msgid "Shisen-Sho (No Gravity) 14x6"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Shisen-Sho (No Gra) 18x8"
|
msgid "Shisen-Sho (No Gravity) 18x8"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Shisen-Sho (No Gra) 24x12"
|
msgid "Shisen-Sho (No Gravity) 24x12"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Shisen-Sho 14x6"
|
msgid "Shisen-Sho 14x6"
|
||||||
|
@ -3423,15 +3495,15 @@ msgstr ""
|
||||||
msgid "Stargate"
|
msgid "Stargate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Step Pyramid"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Step-Up"
|
msgid "Step-Up"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Steps"
|
msgid "Steps"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Steps Pyramid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Steve"
|
msgid "Steve"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3597,6 +3669,9 @@ msgstr ""
|
||||||
msgid "Thieves of Egypt"
|
msgid "Thieves of Egypt"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Thirteen Packs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Thirteen Up"
|
msgid "Thirteen Up"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3864,6 +3939,9 @@ msgstr ""
|
||||||
msgid "Waning Moon"
|
msgid "Waning Moon"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Wasatch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Washington's Favorite"
|
msgid "Washington's Favorite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3968,4 +4046,3 @@ msgstr ""
|
||||||
|
|
||||||
msgid "Zodiac"
|
msgid "Zodiac"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
7415
po/it_pysol.po
|
@ -4,10 +4,11 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PySol 0.0.1\n"
|
"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"
|
"PO-Revision-Date: 2010-12-16 23:56+0100\n"
|
||||||
"Last-Translator: Jerzy Trzeciak <artusek@wp.pl>\n"
|
"Last-Translator: Jerzy Trzeciak <artusek@wp.pl>\n"
|
||||||
"Language-Team: Polish <pl@li.org>\n"
|
"Language-Team: Polish <pl@li.org>\n"
|
||||||
|
"Language: pl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
@ -705,6 +706,9 @@ msgstr ""
|
||||||
msgid "Czarina"
|
msgid "Czarina"
|
||||||
msgstr "Czarina"
|
msgstr "Czarina"
|
||||||
|
|
||||||
|
msgid "Daddy Longlegs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Danda"
|
msgid "Danda"
|
||||||
msgstr "Danda"
|
msgstr "Danda"
|
||||||
|
|
||||||
|
@ -1277,6 +1281,12 @@ msgstr "Free Napoleon"
|
||||||
msgid "FreeCell"
|
msgid "FreeCell"
|
||||||
msgstr "FreeCell"
|
msgstr "FreeCell"
|
||||||
|
|
||||||
|
msgid "FreeCell with Two Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "FreeCell with Zero Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Frog"
|
msgid "Frog"
|
||||||
msgstr "Żaba"
|
msgstr "Żaba"
|
||||||
|
|
||||||
|
|
7343
po/pl_pysol.po
6730
po/pysol.pot
|
@ -5,14 +5,17 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PySol 0.0.1\n"
|
"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"
|
"PO-Revision-Date: 2007-09-05 17:43+0400\n"
|
||||||
"Last-Translator: Скоморох <skomoroh@gmail.com>\n"
|
"Last-Translator: Скоморох <skomoroh@gmail.com>\n"
|
||||||
"Language-Team: Russian <ru@li.org>\n"
|
"Language-Team: Russian <ru@li.org>\n"
|
||||||
|
"Language: ru\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: utf-8\n"
|
"Content-Transfer-Encoding: utf-8\n"
|
||||||
"Generated-By: ./scripts/all_games.py 0.1\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"
|
msgid " 3x3 Matrix"
|
||||||
msgstr "Матрица 3x3"
|
msgstr "Матрица 3x3"
|
||||||
|
@ -707,6 +710,10 @@ msgstr "Творог и сыворотка"
|
||||||
msgid "Czarina"
|
msgid "Czarina"
|
||||||
msgstr "Царевна"
|
msgstr "Царевна"
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Daddy Longlegs"
|
||||||
|
msgstr "Тенистые аллеи"
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Danda"
|
msgid "Danda"
|
||||||
msgstr "Алмаз"
|
msgstr "Алмаз"
|
||||||
|
@ -1289,6 +1296,12 @@ msgstr "Свободный Наполеон"
|
||||||
msgid "FreeCell"
|
msgid "FreeCell"
|
||||||
msgstr "Свободная ячейка"
|
msgstr "Свободная ячейка"
|
||||||
|
|
||||||
|
msgid "FreeCell with Two Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "FreeCell with Zero Reserves"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Frog"
|
msgid "Frog"
|
||||||
msgstr "Лягушка"
|
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.pysoltk import create_solver_dialog
|
||||||
from pysollib.settings import DEBUG
|
from pysollib.settings import DEBUG
|
||||||
from pysollib.settings import PACKAGE_URL, TITLE
|
from pysollib.settings import PACKAGE_URL, TITLE
|
||||||
from pysollib.settings import TOP_TITLE
|
from pysollib.settings import TOP_SIZE
|
||||||
from pysollib.stats import FileStatsFormatter
|
from pysollib.stats import FileStatsFormatter
|
||||||
|
|
||||||
|
|
||||||
|
@ -381,7 +381,7 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
if self._cancelDrag():
|
if self._cancelDrag():
|
||||||
return
|
return
|
||||||
if self.changed():
|
if self.changed():
|
||||||
if not self.game.areYouSure(_("Quit ") + TITLE):
|
if not self.game.areYouSure(_("Quit %s") % TITLE):
|
||||||
return
|
return
|
||||||
self.game.endGame()
|
self.game.endGame()
|
||||||
self.game.quitGame()
|
self.game.quitGame()
|
||||||
|
@ -445,7 +445,7 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
if not self.game.gsaveinfo.bookmarks:
|
if not self.game.gsaveinfo.bookmarks:
|
||||||
return
|
return
|
||||||
if not self.game.areYouSure(_("Clear bookmarks"),
|
if not self.game.areYouSure(_("Clear bookmarks"),
|
||||||
_("Clear all bookmarks ?")):
|
_("Clear all bookmarks?")):
|
||||||
return
|
return
|
||||||
self.game.gsaveinfo.bookmarks = {}
|
self.game.gsaveinfo.bookmarks = {}
|
||||||
self.game.updateMenus()
|
self.game.updateMenus()
|
||||||
|
@ -457,7 +457,7 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
return
|
return
|
||||||
if self.changed(restart=1):
|
if self.changed(restart=1):
|
||||||
if not self.game.areYouSure(_("Restart game"),
|
if not self.game.areYouSure(_("Restart game"),
|
||||||
_("Restart this game ?")):
|
_("Restart this game?")):
|
||||||
return
|
return
|
||||||
self.game.restartGame()
|
self.game.restartGame()
|
||||||
|
|
||||||
|
@ -501,10 +501,11 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
if self._cancelDrag(break_pause=False):
|
if self._cancelDrag(break_pause=False):
|
||||||
return
|
return
|
||||||
game, gi = self.game, self.game.gameinfo
|
game, gi = self.game, self.game.gameinfo
|
||||||
t = " " + game.getGameNumber(format=1)
|
kw = {'game': gi.name,
|
||||||
cc = _("Comments for %s:\n\n") % (gi.name + t)
|
'id': game.getGameNumber(format=1)}
|
||||||
|
cc = _("Comments for %(game)s %(id)s:\n\n") % kw
|
||||||
c = game.gsaveinfo.comment or cc
|
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:
|
if d.status == 0 and d.button == 0:
|
||||||
text = d.text
|
text = d.text
|
||||||
if text.strip() == cc.strip():
|
if text.strip() == cc.strip():
|
||||||
|
@ -525,8 +526,9 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
text=_("Error while writing to file"))
|
text=_("Error while writing to file"))
|
||||||
else:
|
else:
|
||||||
d = MfxMessageDialog(
|
d = MfxMessageDialog(
|
||||||
self.top, title=TITLE+_(" Info"), bitmap="info",
|
self.top, title=_("%s Info") % TITLE, bitmap="info",
|
||||||
text=_("Comments were appended to\n\n") + fn)
|
text=_("Comments were appended to\n\n%(filename)s")
|
||||||
|
% {'filename': fn})
|
||||||
self._setCommentMenu(bool(game.gsaveinfo.comment))
|
self._setCommentMenu(bool(game.gsaveinfo.comment))
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -535,10 +537,10 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
|
|
||||||
def _mStatsSave(self, player, filename, write_method):
|
def _mStatsSave(self, player, filename, write_method):
|
||||||
if player is None:
|
if player is None:
|
||||||
text = _("Demo statistics")
|
text = _("Demo statistics were appended to\n\n%(filename)s")
|
||||||
filename = filename + "_demo"
|
filename = filename + "_demo"
|
||||||
else:
|
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.join(self.app.dn.config, filename + ".txt")
|
||||||
filename = os.path.normpath(filename)
|
filename = os.path.normpath(filename)
|
||||||
try:
|
try:
|
||||||
|
@ -549,8 +551,8 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
text=_("Error while writing to file"))
|
text=_("Error while writing to file"))
|
||||||
else:
|
else:
|
||||||
MfxMessageDialog(
|
MfxMessageDialog(
|
||||||
self.top, title=TITLE+_(" Info"), bitmap="info",
|
self.top, title=_("%s Info") % TITLE, bitmap="info",
|
||||||
text=text + _(" were appended to\n\n") + filename)
|
text=text % {'filename': filename})
|
||||||
|
|
||||||
def mPlayerStats(self, *args, **kw):
|
def mPlayerStats(self, *args, **kw):
|
||||||
mode = kw.get("mode", 101)
|
mode = kw.get("mode", 101)
|
||||||
|
@ -564,31 +566,40 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
d = Struct(status=-1, button=-1)
|
d = Struct(status=-1, button=-1)
|
||||||
if demo:
|
if demo:
|
||||||
player = None
|
player = None
|
||||||
p0, p1, p2 = TITLE+_(" Demo"), TITLE+_(" Demo "), ""
|
|
||||||
else:
|
else:
|
||||||
player = self.app.opt.player
|
player = self.app.opt.player
|
||||||
p0, p1, p2 = player, "", _(" for ") + player
|
|
||||||
n = self.game.gameinfo.name
|
n = self.game.gameinfo.name
|
||||||
|
# translation keywords
|
||||||
|
transkw = {'app': TITLE,
|
||||||
|
'player': player,
|
||||||
|
'game': n,
|
||||||
|
'tops': TOP_SIZE}
|
||||||
#
|
#
|
||||||
if mode == 100:
|
if mode == 100:
|
||||||
d = Status_StatsDialog(self.top, game=self.game)
|
d = Status_StatsDialog(self.top, game=self.game)
|
||||||
elif mode == 101:
|
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(
|
d = SingleGame_StatsDialog(
|
||||||
self.top, header, self.app, player, gameid=self.game.id)
|
self.top, header, self.app, player, gameid=self.game.id)
|
||||||
gameid = d.selected_game
|
gameid = d.selected_game
|
||||||
elif mode == 102:
|
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)
|
d = AllGames_StatsDialog(self.top, header, self.app, player)
|
||||||
gameid = d.selected_game
|
gameid = d.selected_game
|
||||||
elif mode == 103:
|
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)
|
d = FullLog_StatsDialog(self.top, header, self.app, player)
|
||||||
elif mode == 104:
|
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)
|
d = SessionLog_StatsDialog(self.top, header, self.app, player)
|
||||||
elif mode == 105:
|
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(
|
d = Top_StatsDialog(
|
||||||
self.top, header, self.app, player, gameid=self.game.id)
|
self.top, header, self.app, player, gameid=self.game.id)
|
||||||
elif mode == 106:
|
elif mode == 106:
|
||||||
|
@ -614,7 +625,8 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
# reset all player stats
|
# reset all player stats
|
||||||
if self.game.areYouSure(
|
if self.game.areYouSure(
|
||||||
_("Reset all statistics"),
|
_("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
|
confirm=1, default=1
|
||||||
):
|
):
|
||||||
self.app.stats.resetStats(player, 0)
|
self.app.stats.resetStats(player, 0)
|
||||||
|
@ -624,8 +636,8 @@ class PysolMenubar(PysolMenubarTk):
|
||||||
# reset player stats for current game
|
# reset player stats for current game
|
||||||
if self.game.areYouSure(
|
if self.game.areYouSure(
|
||||||
_("Reset game statistics"),
|
_("Reset game statistics"),
|
||||||
_('Reset statistics and logs ' +
|
_('Reset statistics and logs for player\n%(player)s\n'
|
||||||
'for player\n%s\nand game\n%s ?') % (p0, n),
|
'and game\n%(game)s?') % transkw,
|
||||||
confirm=1, default=1
|
confirm=1, default=1
|
||||||
):
|
):
|
||||||
self.app.stats.resetStats(player, self.game.id)
|
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 DEBUG
|
||||||
from pysollib.settings import PACKAGE, VERSION_TUPLE, WIN_SYSTEM
|
from pysollib.settings import PACKAGE, VERSION_TUPLE, WIN_SYSTEM
|
||||||
from pysollib.settings import TOOLKIT
|
from pysollib.settings import TOOLKIT
|
||||||
from pysollib.util import CARDSET, IMAGE_EXTENSIONS
|
from pysollib.util import IMAGE_EXTENSIONS
|
||||||
from pysollib.winsystems import TkSettings
|
from pysollib.winsystems import TkSettings
|
||||||
if TOOLKIT == 'tk':
|
if TOOLKIT == 'tk':
|
||||||
from pysollib.ui.tktile.solverdialog import destroy_solver_dialog
|
from pysollib.ui.tktile.solverdialog import destroy_solver_dialog
|
||||||
|
@ -329,7 +329,8 @@ class Application:
|
||||||
elif self.commandline.game is not None:
|
elif self.commandline.game is not None:
|
||||||
gameid = self.gdb.getGameByName(self.commandline.game)
|
gameid = self.gdb.getGameByName(self.commandline.game)
|
||||||
if gameid is None:
|
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)
|
sys.exit(-1)
|
||||||
else:
|
else:
|
||||||
self.nextgame.id = gameid
|
self.nextgame.id = gameid
|
||||||
|
@ -668,7 +669,7 @@ class Application:
|
||||||
if progress is None:
|
if progress is None:
|
||||||
self.wm_save_state()
|
self.wm_save_state()
|
||||||
self.wm_withdraw()
|
self.wm_withdraw()
|
||||||
title = _("Loading %s %s...") % (CARDSET, cs.name)
|
title = _("Loading cardset %s...") % cs.name
|
||||||
color = self.opt.colors['table']
|
color = self.opt.colors['table']
|
||||||
if self.tabletile_index > 0:
|
if self.tabletile_index > 0:
|
||||||
color = "#008200"
|
color = "#008200"
|
||||||
|
@ -678,7 +679,7 @@ class Application:
|
||||||
images = Images(self.dataloader, cs)
|
images = Images(self.dataloader, cs)
|
||||||
try:
|
try:
|
||||||
if not images.load(app=self, progress=progress):
|
if not images.load(app=self, progress=progress):
|
||||||
raise Exception("Invalid or damaged "+CARDSET)
|
raise Exception("Invalid or damaged cardset")
|
||||||
simages = SubsampledImages(images)
|
simages = SubsampledImages(images)
|
||||||
if self.opt.save_cardsets:
|
if self.opt.save_cardsets:
|
||||||
c = self.cardsets_cache.get(cs.type)
|
c = self.cardsets_cache.get(cs.type)
|
||||||
|
@ -710,8 +711,8 @@ class Application:
|
||||||
# images.destruct()
|
# images.destruct()
|
||||||
destruct(images)
|
destruct(images)
|
||||||
MfxExceptionDialog(
|
MfxExceptionDialog(
|
||||||
self.top, ex, title=CARDSET+_(" load error"),
|
self.top, ex, title=_("Cardset load error"),
|
||||||
text=_("Error while loading ")+CARDSET)
|
text=_("Error while loading cardset"))
|
||||||
self.intro.progress = progress
|
self.intro.progress = progress
|
||||||
if r and self.menubar is not None:
|
if r and self.menubar is not None:
|
||||||
self.menubar.updateBackgroundImagesMenu()
|
self.menubar.updateBackgroundImagesMenu()
|
||||||
|
@ -806,14 +807,14 @@ class Application:
|
||||||
#
|
#
|
||||||
t = self.checkCompatibleCardsetType(gi, self.cardset)
|
t = self.checkCompatibleCardsetType(gi, self.cardset)
|
||||||
MfxMessageDialog(
|
MfxMessageDialog(
|
||||||
self.top, title=_("Incompatible ")+CARDSET,
|
self.top, title=_("Incompatible cardset"),
|
||||||
bitmap="warning",
|
bitmap="warning",
|
||||||
text=_('''The currently selected %s %s
|
text=_('''The currently selected cardset %(cardset)s
|
||||||
is not compatible with the game
|
is not compatible with the game
|
||||||
%s
|
%(game)s
|
||||||
|
|
||||||
Please select a %s type %s.
|
Please select a %(correct_type)s type cardset.
|
||||||
''') % (CARDSET, self.cardset.name, gi.name, t[0], CARDSET),
|
''') % {'cardset': self.cardset.name, 'game': gi.name, 'correct_type': t[0]},
|
||||||
strings=(_("&OK"),), default=0)
|
strings=(_("&OK"),), default=0)
|
||||||
cs = self.__selectCardsetDialog(t)
|
cs = self.__selectCardsetDialog(t)
|
||||||
if cs is None:
|
if cs is None:
|
||||||
|
@ -852,7 +853,7 @@ Please select a %s type %s.
|
||||||
|
|
||||||
def __selectCardsetDialog(self, t):
|
def __selectCardsetDialog(self, t):
|
||||||
cs = self.selectCardset(
|
cs = self.selectCardset(
|
||||||
_("Please select a %s type %s") % (t[0], CARDSET),
|
_("Please select a %s type cardset") % t[0],
|
||||||
self.cardset.index)
|
self.cardset.index)
|
||||||
return cs
|
return cs
|
||||||
|
|
||||||
|
@ -1060,7 +1061,8 @@ Please select a %s type %s.
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
traceback.print_exc()
|
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
|
# init cardsets
|
||||||
|
@ -1211,11 +1213,10 @@ Please select a %s type %s.
|
||||||
d = os.path.join(dirname, name)
|
d = os.path.join(dirname, name)
|
||||||
if not os.path.isdir(d):
|
if not os.path.isdir(d):
|
||||||
continue
|
continue
|
||||||
f1 = os.path.join(d, "config.txt")
|
f = os.path.join(d, "config.txt")
|
||||||
f2 = os.path.join(d, "COPYRIGHT")
|
if os.path.isfile(f):
|
||||||
if os.path.isfile(f1) and os.path.isfile(f2):
|
|
||||||
try:
|
try:
|
||||||
cs = self._readCardsetConfig(d, f1)
|
cs = self._readCardsetConfig(d, f)
|
||||||
if cs:
|
if cs:
|
||||||
# from pprint import pprint
|
# from pprint import pprint
|
||||||
# print cs.name
|
# print cs.name
|
||||||
|
@ -1232,7 +1233,7 @@ Please select a %s type %s.
|
||||||
fnames[cs.name] = 1
|
fnames[cs.name] = 1
|
||||||
else:
|
else:
|
||||||
print_err('fail _readCardsetConfig: %s %s'
|
print_err('fail _readCardsetConfig: %s %s'
|
||||||
% (d, f1))
|
% (d, f))
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
# traceback.print_exc()
|
# 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 bakersgame # noqa: F401
|
||||||
from . import beleagueredcastle # noqa: F401
|
from . import beleagueredcastle # noqa: F401
|
||||||
from . import bisley # noqa: F401
|
from . import bisley # noqa: F401
|
||||||
|
from . import bisley13 # noqa: F401
|
||||||
from . import braid # noqa: F401
|
from . import braid # noqa: F401
|
||||||
from . import bristol # noqa: F401
|
from . import bristol # noqa: F401
|
||||||
from . import buffalobill # 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
|
return
|
||||||
WasteTalonStack.updateText(self, update_rounds=0)
|
WasteTalonStack.updateText(self, update_rounds=0)
|
||||||
# t = "Round %d" % self.round
|
# 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)
|
self.texts.rounds.config(text=t)
|
||||||
t = _("Deal %d") % self.DEAL[self.round-1]
|
t = _("Deal %d") % self.DEAL[self.round-1]
|
||||||
self.texts.misc.config(text=t)
|
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 cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
|
||||||
return self.isHanafudaSequence([stackcards[-1], cards[0]])
|
return self.isHanafudaSequence([stackcards[-1], cards[0]])
|
||||||
|
|
||||||
|
def canMoveCards(self, cards):
|
||||||
|
return self.basicCanMoveCards(cards) and self.isHanafudaSequence(cards)
|
||||||
|
|
||||||
|
|
||||||
class Oonsoo_SequenceStack(Flower_OpenStack):
|
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 cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
|
||||||
return self.isHanafudaSequence([stackcards[-1], cards[0]], 0)
|
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):
|
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):
|
def help_about(app, timeout=0, sound=True):
|
||||||
if sound:
|
if sound:
|
||||||
app.audio.playSample("about")
|
app.audio.playSample("about")
|
||||||
t = _("A Python Solitaire Game Collection\n")
|
t = _("A Python Solitaire Game Collection")
|
||||||
if app.miscrandom.random() < 0.8:
|
if app.miscrandom.random() < 0.8:
|
||||||
t = _("A World Domination Project\n")
|
t = _("A World Domination Project")
|
||||||
strings = (_("&Nice"), _("&Credits..."))
|
strings = (_("&Nice"), _("&Credits..."))
|
||||||
if timeout:
|
if timeout:
|
||||||
strings = (_("&Enjoy"),)
|
strings = (_("&Enjoy"),)
|
||||||
version = _("Version %s") % VERSION
|
version = _("Version %s") % VERSION
|
||||||
d = PysolAboutDialog(app, app.top, title=_("About ") + TITLE,
|
d = PysolAboutDialog(app, app.top, title=_("About %s") % TITLE,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
text=_('''PySol Fan Club edition
|
text=_('''PySol Fan Club edition
|
||||||
%s%s
|
%(description)s
|
||||||
|
%(versioninfo)s
|
||||||
|
|
||||||
Copyright (C) 1998 - 2003 Markus F.X.J. Oberhumer.
|
Copyright (C) 1998 - 2003 Markus F.X.J. Oberhumer.
|
||||||
Copyright (C) 2003 Mt. Hood Playing Card Co.
|
Copyright (C) 2003 Mt. Hood Playing Card Co.
|
||||||
|
@ -60,7 +61,8 @@ All Rights Reserved.
|
||||||
PySol is free software distributed under the terms
|
PySol is free software distributed under the terms
|
||||||
of the GNU General Public License.
|
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,
|
url=PACKAGE_URL,
|
||||||
image=app.gimages.logos[2],
|
image=app.gimages.logos[2],
|
||||||
strings=strings, default=0,
|
strings=strings, default=0,
|
||||||
|
@ -86,7 +88,7 @@ def help_credits(app, timeout=0, sound=True):
|
||||||
t = "kivy"
|
t = "kivy"
|
||||||
d = MfxMessageDialog(
|
d = MfxMessageDialog(
|
||||||
app.top, title=_("Credits"), timeout=timeout,
|
app.top, title=_("Credits"), timeout=timeout,
|
||||||
text=TITLE+_(''' credits go to:
|
text=_('''%(app)s credits go to:
|
||||||
|
|
||||||
Volker Weidner for getting me into Solitaire
|
Volker Weidner for getting me into Solitaire
|
||||||
Guido van Rossum for the initial example program
|
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
|
The Gnome AisleRiot team for parts of the documentation
|
||||||
Natascha
|
Natascha
|
||||||
|
|
||||||
The Python, %s, SDL & Linux crews
|
The Python, %(gui_library)s, SDL & Linux crews
|
||||||
for making this program possible''') % t,
|
for making this program possible''') % {'app': TITLE, 'gui_library': t},
|
||||||
image=app.gimages.logos[3], image_side="right",
|
image=app.gimages.logos[3], image_side="right",
|
||||||
separator=True)
|
separator=True)
|
||||||
return d.status
|
return d.status
|
||||||
|
@ -122,8 +124,8 @@ def help_html(app, document, dir_, top=None):
|
||||||
document, dir_ = "index.html", "html"
|
document, dir_ = "index.html", "html"
|
||||||
help_html_index = app.dataloader.findFile(document, dir_)
|
help_html_index = app.dataloader.findFile(document, dir_)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
MfxMessageDialog(app.top, title=TITLE + _(" HTML Problem"),
|
MfxMessageDialog(app.top, title=_("%s HTML Problem") % TITLE,
|
||||||
text=_("Cannot find help document\n") + document,
|
text=_("Cannot find help document\n%s") % document,
|
||||||
bitmap="warning")
|
bitmap="warning")
|
||||||
return None
|
return None
|
||||||
# print doc, help_html_index
|
# print doc, help_html_index
|
||||||
|
@ -136,7 +138,7 @@ def help_html(app, document, dir_, top=None):
|
||||||
viewer.display(doc, relpath=0)
|
viewer.display(doc, relpath=0)
|
||||||
except Exception:
|
except Exception:
|
||||||
# traceback.print_exc()
|
# 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:
|
if top.winfo_screenwidth() < 800 or top.winfo_screenheight() < 600:
|
||||||
# maximized = 1
|
# maximized = 1
|
||||||
top.wm_minsize(300, 150)
|
top.wm_minsize(300, 150)
|
||||||
|
|
|
@ -27,6 +27,11 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import jnius
|
||||||
|
except ImportError:
|
||||||
|
jnius = None
|
||||||
|
|
||||||
import pysollib.settings
|
import pysollib.settings
|
||||||
|
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
|
@ -36,27 +41,31 @@ import pysollib.settings
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
|
|
||||||
if os.name == 'nt' and 'LANG' not in os.environ:
|
if 'LANG' not in os.environ:
|
||||||
try:
|
if os.name == 'nt':
|
||||||
loc = locale.getdefaultlocale()
|
lang, enc = locale.getdefaultlocale()
|
||||||
os.environ['LANG'] = loc[0]
|
os.environ['LANG'] = lang
|
||||||
except Exception:
|
elif jnius: # android
|
||||||
pass
|
Locale = jnius.autoclass('java.util.Locale')
|
||||||
# locale.setlocale(locale.LC_ALL, '')
|
os.environ['LANG'] = Locale.getDefault().getLanguage()
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
|
||||||
# install gettext
|
# install gettext
|
||||||
# locale_dir = 'locale'
|
locale_locations = (
|
||||||
locale_dir = None
|
# locale/ next to the pysol.py script
|
||||||
if os.path.isdir(sys.path[0]):
|
sys.path[0],
|
||||||
d = os.path.join(sys.path[0], 'locale')
|
# locale/ next to library.zip (py2exe)
|
||||||
else:
|
os.path.dirname(sys.path[0]),
|
||||||
# i.e. library.zip
|
# locale/ in curdir (works for e.g. py2app)
|
||||||
d = os.path.join(os.path.dirname(sys.path[0]), 'locale')
|
os.curdir)
|
||||||
if os.path.exists(d) and os.path.isdir(d):
|
# leaving the domain unbound means sys.prefix+'/share/locale'
|
||||||
locale_dir = d
|
|
||||||
# if locale_dir: locale_dir = os.path.normpath(locale_dir)
|
for par in locale_locations:
|
||||||
# gettext.install('pysol', locale_dir, unicode=True) # ngettext don't work
|
locale_dir = os.path.join(par, 'locale')
|
||||||
gettext.bindtextdomain('pysol', locale_dir)
|
if os.path.isdir(locale_dir):
|
||||||
|
gettext.bindtextdomain('pysol', locale_dir)
|
||||||
|
break
|
||||||
|
|
||||||
gettext.textdomain('pysol')
|
gettext.textdomain('pysol')
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
|
|
|
@ -52,6 +52,8 @@ from kivy.uix.treeview import TreeViewLabel
|
||||||
from kivy.uix.widget import Widget
|
from kivy.uix.widget import Widget
|
||||||
from kivy.utils import platform
|
from kivy.utils import platform
|
||||||
|
|
||||||
|
from pysollib.kivy.androidperms import requestStoragePerm
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
@ -671,7 +673,10 @@ class LRectangle(Widget, LBase):
|
||||||
event = LEvent()
|
event = LEvent()
|
||||||
event.x = ppos[0]
|
event.x = ppos[0]
|
||||||
event.y = ppos[1]
|
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 True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -712,6 +717,10 @@ class LImageItem(BoxLayout, LBase):
|
||||||
if self.group and '<1>' in self.group.bindings:
|
if self.group and '<1>' in self.group.bindings:
|
||||||
self.group.bindings['<1>'](event)
|
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):
|
def on_touch_down(self, touch):
|
||||||
|
|
||||||
if self.collide_point(*touch.pos):
|
if self.collide_point(*touch.pos):
|
||||||
|
@ -734,7 +743,10 @@ class LImageItem(BoxLayout, LBase):
|
||||||
event.y = ppos[1]
|
event.y = ppos[1]
|
||||||
self.dragstart = touch.pos
|
self.dragstart = touch.pos
|
||||||
event.cardid = i
|
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
|
return True
|
||||||
|
|
||||||
if self.group is not None:
|
if self.group is not None:
|
||||||
|
@ -1168,7 +1180,7 @@ class LMenu(ActionView, LBase):
|
||||||
class MyActionPrev(ActionPrevious, LBase):
|
class MyActionPrev(ActionPrevious, LBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
kw['app_icon'] = 'data/images/misc/pysol01.png'
|
kw['app_icon'] = 'data/images/icons/48x48/pysol.png'
|
||||||
kw['with_previous'] = prev
|
kw['with_previous'] = prev
|
||||||
kw['size_hint'] = (.01, 1)
|
kw['size_hint'] = (.01, 1)
|
||||||
self.ap = MyActionPrev(**kw)
|
self.ap = MyActionPrev(**kw)
|
||||||
|
@ -1541,6 +1553,12 @@ class LMainWindow(BoxLayout, LTkBase):
|
||||||
self.workStack = LStack()
|
self.workStack = LStack()
|
||||||
self.app = None
|
self.app = None
|
||||||
|
|
||||||
|
'''
|
||||||
|
from kivy.graphics import opengl_utils
|
||||||
|
print('OPENGL support:')
|
||||||
|
print(opengl_utils.gl_get_extensions())
|
||||||
|
'''
|
||||||
|
|
||||||
# self.touches = []
|
# self.touches = []
|
||||||
|
|
||||||
# beispiel zu canvas (hintergrund)
|
# beispiel zu canvas (hintergrund)
|
||||||
|
@ -1779,6 +1797,8 @@ class LApp(App):
|
||||||
self.mainloop = self.app.mainproc() # Einrichten
|
self.mainloop = self.app.mainproc() # Einrichten
|
||||||
self.mainloop.send(None) # Spielprozess starten
|
self.mainloop.send(None) # Spielprozess starten
|
||||||
logging.info("LApp: on_start processed")
|
logging.info("LApp: on_start processed")
|
||||||
|
# Android: Request missing android permissions.
|
||||||
|
requestStoragePerm()
|
||||||
|
|
||||||
def on_stop(self):
|
def on_stop(self):
|
||||||
# Achtung wird u.U. 2 mal aufgerufen !!!
|
# Achtung wird u.U. 2 mal aufgerufen !!!
|
||||||
|
@ -1832,12 +1852,6 @@ class LApp(App):
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
pass
|
pass
|
||||||
# save comments
|
|
||||||
try:
|
|
||||||
app.saveComments()
|
|
||||||
except Exception:
|
|
||||||
traceback.print_exc()
|
|
||||||
pass
|
|
||||||
logging.info("LApp: on_pause - gamesaved")
|
logging.info("LApp: on_pause - gamesaved")
|
||||||
|
|
||||||
logging.info("LApp: on_pause, Window.size=%s" % str(Window.size))
|
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.kivy.tkutil import bind
|
||||||
from pysollib.mfxutil import Struct
|
from pysollib.mfxutil import Struct
|
||||||
from pysollib.mygettext import _
|
from pysollib.mygettext import _
|
||||||
|
from pysollib.pysoltk import MfxMessageDialog
|
||||||
from pysollib.pysoltk import connect_game_find_card_dialog
|
from pysollib.pysoltk import connect_game_find_card_dialog
|
||||||
from pysollib.settings import SELECT_GAME_MENU
|
from pysollib.settings import SELECT_GAME_MENU
|
||||||
from pysollib.settings import TITLE
|
from pysollib.settings import TITLE
|
||||||
from pysollib.util import CARDSET
|
|
||||||
|
|
||||||
|
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
|
@ -177,34 +177,34 @@ class MainMenuDialog(LMenuDialog):
|
||||||
def buildTree(self, tv, node):
|
def buildTree(self, tv, node):
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="File",
|
text=_("File"),
|
||||||
command=self.make_game_command(self.menubar.mFileMenuDialog)))
|
command=self.make_game_command(self.menubar.mFileMenuDialog)))
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="Games",
|
text=_("Games"),
|
||||||
command=self.make_game_command(
|
command=self.make_game_command(
|
||||||
self.menubar.mSelectGameDialog)))
|
self.menubar.mSelectGameDialog)))
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="Tools",
|
text=_("Tools"),
|
||||||
command=self.make_game_command(self.menubar.mEditMenuDialog)))
|
command=self.make_game_command(self.menubar.mEditMenuDialog)))
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="Statistics",
|
text=_("Statistics"),
|
||||||
command=self.make_game_command(self.menubar.mGameMenuDialog)))
|
command=self.make_game_command(self.menubar.mGameMenuDialog)))
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="Assist",
|
text=_("Assist"),
|
||||||
command=self.make_game_command(
|
command=self.make_game_command(
|
||||||
self.menubar.mAssistMenuDialog)))
|
self.menubar.mAssistMenuDialog)))
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="Options",
|
text=_("Options"),
|
||||||
command=self.make_game_command(
|
command=self.make_game_command(
|
||||||
self.menubar.mOptionsMenuDialog)))
|
self.menubar.mOptionsMenuDialog)))
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text="Help",
|
text=_("Help"),
|
||||||
command=self.make_game_command(self.menubar.mHelpMenuDialog)))
|
command=self.make_game_command(self.menubar.mHelpMenuDialog)))
|
||||||
del rg
|
del rg
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ class FileMenuDialog(LMenuDialog):
|
||||||
|
|
||||||
def buildTree(self, tv, node):
|
def buildTree(self, tv, node):
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Recent games'))
|
LTreeNode(text=_('Recent games')))
|
||||||
# Recent Liste
|
# Recent Liste
|
||||||
recids = self.app.opt.recent_gameid
|
recids = self.app.opt.recent_gameid
|
||||||
# recgames = []
|
# recgames = []
|
||||||
|
@ -238,12 +238,12 @@ class FileMenuDialog(LMenuDialog):
|
||||||
LTreeNode(text=gi.name, command=command), rg)
|
LTreeNode(text=gi.name, command=command), rg)
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Favorite games'))
|
LTreeNode(text=_('Favorite games')))
|
||||||
if rg:
|
if rg:
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='<Add>', command=self.menubar.mAddFavor), rg)
|
text=_('<Add>'), command=self.menubar.mAddFavor), rg)
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='<Remove>', command=self.menubar.mDelFavor), rg)
|
text=_('<Remove>'), command=self.menubar.mDelFavor), rg)
|
||||||
|
|
||||||
# Recent Liste
|
# Recent Liste
|
||||||
favids = self.app.opt.favorite_gameid
|
favids = self.app.opt.favorite_gameid
|
||||||
|
@ -257,12 +257,12 @@ class FileMenuDialog(LMenuDialog):
|
||||||
LTreeNode(text=gi.name, command=command), rg)
|
LTreeNode(text=gi.name, command=command), rg)
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Load', command=self.menubar.mOpen))
|
text=_('Load'), command=self.menubar.mOpen))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Save', command=self.menubar.mSaveAs))
|
text=_('Save'), command=self.menubar.mSaveAs))
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
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):
|
def buildTree(self, tv, node):
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='New game', command=self.menubar.mNewGame))
|
text=_('New game'), command=self.menubar.mNewGame))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Restart game', command=self.menubar.mRestart))
|
text=_('Restart game'), command=self.menubar.mRestart))
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Undo', command=self.menubar.mUndo))
|
text=_('Undo'), command=self.menubar.mUndo))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Redo', command=self.menubar.mRedo))
|
text=_('Redo'), command=self.menubar.mRedo))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Redo all', command=self.menubar.mRedoAll))
|
text=_('Redo all'), command=self.menubar.mRedoAll))
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Auto drop', command=self.menubar.mDrop))
|
text=_('Auto drop'), command=self.menubar.mDrop))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Shuffle tiles', command=self.menubar.mShuffle))
|
text=_('Shuffle tiles'), command=self.menubar.mShuffle))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Deal cards', command=self.menubar.mDeal))
|
text=_('Deal cards'), command=self.menubar.mDeal))
|
||||||
|
|
||||||
self.addCheckNode(tv, None,
|
self.addCheckNode(tv, None,
|
||||||
'Pause',
|
_('Pause'),
|
||||||
self.menubar.tkopt.pause,
|
self.menubar.tkopt.pause,
|
||||||
self.menubar.mPause)
|
self.menubar.mPause)
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Load game', command=self.menubar.mOpen))
|
text=_('Load game'), command=self.menubar.mOpen))
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Save game', command=self.menubar.mSaveAs))
|
text=_('Save game'), command=self.menubar.mSaveAs))
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Help', command=self.menubar.mHelpRules))
|
text=_('Help'), command=self.menubar.mHelpRules))
|
||||||
|
|
||||||
# -------------------------------------------
|
# -------------------------------------------
|
||||||
# TBD ?
|
# TBD ?
|
||||||
|
@ -369,7 +369,7 @@ class GameMenuDialog(LMenuDialog):
|
||||||
|
|
||||||
def buildTree(self, tv, node):
|
def buildTree(self, tv, node):
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Current game ...',
|
text=_('Current game...'),
|
||||||
command=self.make_command(101, self.menubar.mPlayerStats)), None)
|
command=self.make_command(101, self.menubar.mPlayerStats)), None)
|
||||||
|
|
||||||
# tv.add_node(LTreeNode(
|
# tv.add_node(LTreeNode(
|
||||||
|
@ -432,16 +432,16 @@ class AssistMenuDialog(LMenuDialog):
|
||||||
|
|
||||||
def buildTree(self, tv, node):
|
def buildTree(self, tv, node):
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Hint', command=self.menubar.mHint))
|
text=_('Hint'), command=self.menubar.mHint))
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Highlight piles', command=self.menubar.mHighlightPiles))
|
text=_('Highlight piles'), command=self.menubar.mHighlightPiles))
|
||||||
|
|
||||||
# tv.add_node(LTreeNode(
|
# tv.add_node(LTreeNode(
|
||||||
# text='Find Card', command=self.menubar.mFindCard))
|
# text='Find Card', command=self.menubar.mFindCard))
|
||||||
|
|
||||||
tv.add_node(LTreeNode(
|
tv.add_node(LTreeNode(
|
||||||
text='Demo', command=self.menubar.mDemo))
|
text=_('Demo'), command=self.menubar.mDemo))
|
||||||
|
|
||||||
# -------------------------------------------
|
# -------------------------------------------
|
||||||
# TBD. How ?
|
# TBD. How ?
|
||||||
|
@ -507,27 +507,27 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Automatic play settings
|
# Automatic play settings
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Automatic play'))
|
LTreeNode(text=_('Automatic play')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Auto face up',
|
_('Auto face up'),
|
||||||
self.menubar.tkopt.autofaceup,
|
self.menubar.tkopt.autofaceup,
|
||||||
self.menubar.mOptAutoFaceUp)
|
self.menubar.mOptAutoFaceUp)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Auto drop',
|
_('Auto drop'),
|
||||||
self.menubar.tkopt.autodrop,
|
self.menubar.tkopt.autodrop,
|
||||||
self.menubar.mOptAutoDrop)
|
self.menubar.mOptAutoDrop)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Auto deal',
|
_('Auto deal'),
|
||||||
self.menubar.tkopt.autodeal,
|
self.menubar.tkopt.autodeal,
|
||||||
self.menubar.mOptAutoDeal)
|
self.menubar.mOptAutoDeal)
|
||||||
|
|
||||||
# submenu.add_separator()
|
# submenu.add_separator()
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Quick play',
|
_('Quick play'),
|
||||||
self.menubar.tkopt.quickplay,
|
self.menubar.tkopt.quickplay,
|
||||||
self.menubar.mOptQuickPlay)
|
self.menubar.mOptQuickPlay)
|
||||||
|
|
||||||
|
@ -535,214 +535,245 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Player assistance
|
# Player assistance
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Assist level'))
|
LTreeNode(text=_('Assist level')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable undo',
|
_('Enable undo'),
|
||||||
self.menubar.tkopt.undo,
|
self.menubar.tkopt.undo,
|
||||||
self.menubar.mOptEnableUndo)
|
self.menubar.mOptEnableUndo)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable bookmarks',
|
_('Enable bookmarks'),
|
||||||
self.menubar.tkopt.bookmarks,
|
self.menubar.tkopt.bookmarks,
|
||||||
self.menubar.mOptEnableBookmarks)
|
self.menubar.mOptEnableBookmarks)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable hint',
|
_('Enable hint'),
|
||||||
self.menubar.tkopt.hint,
|
self.menubar.tkopt.hint,
|
||||||
self.menubar.mOptEnableHint)
|
self.menubar.mOptEnableHint)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable shuffle',
|
_('Enable shuffle'),
|
||||||
self.menubar.tkopt.shuffle,
|
self.menubar.tkopt.shuffle,
|
||||||
self.menubar.mOptEnableShuffle)
|
self.menubar.mOptEnableShuffle)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable highlight piles',
|
_('Enable highlight piles'),
|
||||||
self.menubar.tkopt.highlight_piles,
|
self.menubar.tkopt.highlight_piles,
|
||||||
self.menubar.mOptEnableHighlightPiles)
|
self.menubar.mOptEnableHighlightPiles)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable highlight cards',
|
_('Enable highlight cards'),
|
||||||
self.menubar.tkopt.highlight_cards,
|
self.menubar.tkopt.highlight_cards,
|
||||||
self.menubar.mOptEnableHighlightCards)
|
self.menubar.mOptEnableHighlightCards)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable highlight same rank',
|
_('Enable highlight same rank'),
|
||||||
self.menubar.tkopt.highlight_samerank,
|
self.menubar.tkopt.highlight_samerank,
|
||||||
self.menubar.mOptEnableHighlightSameRank)
|
self.menubar.mOptEnableHighlightSameRank)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Highlight no matching',
|
_('Highlight no matching'),
|
||||||
self.menubar.tkopt.highlight_not_matching,
|
self.menubar.tkopt.highlight_not_matching,
|
||||||
self.menubar.mOptEnableHighlightNotMatching)
|
self.menubar.mOptEnableHighlightNotMatching)
|
||||||
|
|
||||||
# submenu.add_separator()
|
# submenu.add_separator()
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Show removed tiles (in Mahjongg games)',
|
_('Show removed tiles (in Mahjongg games)'),
|
||||||
self.menubar.tkopt.mahjongg_show_removed,
|
self.menubar.tkopt.mahjongg_show_removed,
|
||||||
self.menubar.mOptMahjonggShowRemoved)
|
self.menubar.mOptMahjonggShowRemoved)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
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.tkopt.shisen_show_hint,
|
||||||
self.menubar.mOptShisenShowHint)
|
self.menubar.mOptShisenShowHint)
|
||||||
|
|
||||||
# submenu.add_separator()
|
# 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
|
# Sound options
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Sound'))
|
LTreeNode(text=_('Sound')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Enable',
|
_('Enable'),
|
||||||
self.menubar.tkopt.sound,
|
self.menubar.tkopt.sound,
|
||||||
self.menubar.mOptSoundDialog)
|
self.menubar.mOptSoundDialog)
|
||||||
|
|
||||||
rg1 = tv.add_node(
|
rg1 = tv.add_node(
|
||||||
LTreeNode(text='Volume'), rg)
|
LTreeNode(text=_('Volume')), rg)
|
||||||
if rg1:
|
if rg1:
|
||||||
self.addRadioNode(tv, rg1,
|
self.addRadioNode(tv, rg1,
|
||||||
'100%',
|
_('100%'),
|
||||||
self.menubar.tkopt.sound_sample_volume, 100,
|
self.menubar.tkopt.sound_sample_volume, 100,
|
||||||
self.menubar.mOptSoundSampleVol)
|
self.menubar.mOptSoundSampleVol)
|
||||||
self.addRadioNode(tv, rg1,
|
self.addRadioNode(tv, rg1,
|
||||||
'75%',
|
_('75%'),
|
||||||
self.menubar.tkopt.sound_sample_volume, 75,
|
self.menubar.tkopt.sound_sample_volume, 75,
|
||||||
self.menubar.mOptSoundSampleVol)
|
self.menubar.mOptSoundSampleVol)
|
||||||
self.addRadioNode(tv, rg1,
|
self.addRadioNode(tv, rg1,
|
||||||
'50%',
|
_('50%'),
|
||||||
self.menubar.tkopt.sound_sample_volume, 50,
|
self.menubar.tkopt.sound_sample_volume, 50,
|
||||||
self.menubar.mOptSoundSampleVol)
|
self.menubar.mOptSoundSampleVol)
|
||||||
self.addRadioNode(tv, rg1,
|
self.addRadioNode(tv, rg1,
|
||||||
'25%',
|
_('25%'),
|
||||||
self.menubar.tkopt.sound_sample_volume, 25,
|
self.menubar.tkopt.sound_sample_volume, 25,
|
||||||
self.menubar.mOptSoundSampleVol)
|
self.menubar.mOptSoundSampleVol)
|
||||||
|
|
||||||
rg1 = tv.add_node(
|
rg1 = tv.add_node(
|
||||||
LTreeNode(text='Samples'), rg)
|
LTreeNode(text=_('Samples')), rg)
|
||||||
if rg1:
|
if rg1:
|
||||||
key = 'areyousure'
|
key = 'areyousure'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'are you sure',
|
_('are you sure'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'autodrop'
|
key = 'autodrop'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'auto drop',
|
_('auto drop'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'autoflip'
|
key = 'autoflip'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'auto flip',
|
_('auto flip'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'autopilotlost'
|
key = 'autopilotlost'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'auto pilot lost',
|
_('auto pilot lost'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'autopilotwon'
|
key = 'autopilotwon'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'auto pilot won',
|
_('auto pilot won'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'deal'
|
key = 'deal'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'deal',
|
_('deal'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'dealwaste'
|
key = 'dealwaste'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'deal waste',
|
_('deal waste'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'droppair'
|
key = 'droppair'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'drop pair',
|
_('drop pair'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'drop'
|
key = 'drop'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'drop',
|
_('drop'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'flip'
|
key = 'flip'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'flip',
|
_('flip'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'move'
|
key = 'move'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'move',
|
_('move'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'nomove'
|
key = 'nomove'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'no move',
|
_('no move'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'redo'
|
key = 'redo'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'redo',
|
_('redo'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'startdrag'
|
key = 'startdrag'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'start drag',
|
_('start drag'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'turnwaste'
|
key = 'turnwaste'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'turn waste',
|
_('turn waste'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'undo'
|
key = 'undo'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'undo',
|
_('undo'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'gamefinished'
|
key = 'gamefinished'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'game finished',
|
_('game finished'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'gamelost'
|
key = 'gamelost'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'game lost',
|
_('game lost'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'gameperfect'
|
key = 'gameperfect'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'game perfect',
|
_('game perfect'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
key = 'gamewon'
|
key = 'gamewon'
|
||||||
self.addCheckNode(
|
self.addCheckNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'game won',
|
_('game won'),
|
||||||
self.menubar.tkopt.sound_sample_vars[key],
|
self.menubar.tkopt.sound_sample_vars[key],
|
||||||
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
self.make_vars_command(self.menubar.mOptSoundSample, key))
|
||||||
|
|
||||||
|
@ -750,7 +781,7 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Cardsets and card backside options
|
# Cardsets and card backside options
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Cardsets'))
|
LTreeNode(text=_('Cardsets')))
|
||||||
if rg:
|
if rg:
|
||||||
self.menubar.tkopt.cardset.set(self.app.cardset.index)
|
self.menubar.tkopt.cardset.set(self.app.cardset.index)
|
||||||
|
|
||||||
|
@ -790,45 +821,45 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Table background settings
|
# Table background settings
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Table'))
|
LTreeNode(text=_('Table')))
|
||||||
if rg:
|
if rg:
|
||||||
rg1 = tv.add_node(
|
rg1 = tv.add_node(
|
||||||
LTreeNode(text='Solid colors'), rg)
|
LTreeNode(text=_('Solid colors')), rg)
|
||||||
if rg1:
|
if rg1:
|
||||||
key = 'table'
|
key = 'table'
|
||||||
self.addRadioNode(
|
self.addRadioNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'Blue',
|
_('Blue'),
|
||||||
self.menubar.tkopt.color_vars[key], '#0082df',
|
self.menubar.tkopt.color_vars[key], '#0082df',
|
||||||
self.menubar.mOptTableColor)
|
self.menubar.mOptTableColor)
|
||||||
self.addRadioNode(
|
self.addRadioNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'Green',
|
_('Green'),
|
||||||
self.menubar.tkopt.color_vars[key], '#008200',
|
self.menubar.tkopt.color_vars[key], '#008200',
|
||||||
self.menubar.mOptTableColor)
|
self.menubar.mOptTableColor)
|
||||||
self.addRadioNode(
|
self.addRadioNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'Navy',
|
_('Navy'),
|
||||||
self.menubar.tkopt.color_vars[key], '#000086',
|
self.menubar.tkopt.color_vars[key], '#000086',
|
||||||
self.menubar.mOptTableColor)
|
self.menubar.mOptTableColor)
|
||||||
self.addRadioNode(
|
self.addRadioNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'Olive',
|
_('Olive'),
|
||||||
self.menubar.tkopt.color_vars[key], '#868200',
|
self.menubar.tkopt.color_vars[key], '#868200',
|
||||||
self.menubar.mOptTableColor)
|
self.menubar.mOptTableColor)
|
||||||
self.addRadioNode(
|
self.addRadioNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'Orange',
|
_('Orange'),
|
||||||
self.menubar.tkopt.color_vars[key], '#f79600',
|
self.menubar.tkopt.color_vars[key], '#f79600',
|
||||||
self.menubar.mOptTableColor)
|
self.menubar.mOptTableColor)
|
||||||
self.addRadioNode(
|
self.addRadioNode(
|
||||||
tv, rg1,
|
tv, rg1,
|
||||||
'Teal',
|
_('Teal'),
|
||||||
self.menubar.tkopt.color_vars[key], '#008286',
|
self.menubar.tkopt.color_vars[key], '#008286',
|
||||||
self.menubar.mOptTableColor)
|
self.menubar.mOptTableColor)
|
||||||
|
|
||||||
rg1 = tv.add_node(
|
rg1 = tv.add_node(
|
||||||
LTreeNode(text='Tiles and Images'), rg)
|
LTreeNode(text=_('Tiles and Images')), rg)
|
||||||
|
|
||||||
if rg1:
|
if rg1:
|
||||||
tm = self.app.tabletile_manager
|
tm = self.app.tabletile_manager
|
||||||
|
@ -848,30 +879,30 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Card view options
|
# Card view options
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Card view'))
|
LTreeNode(text=_('Card view')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Card shadow',
|
_('Card shadow'),
|
||||||
self.menubar.tkopt.shadow,
|
self.menubar.tkopt.shadow,
|
||||||
self.menubar.mOptShadow)
|
self.menubar.mOptShadow)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Shade legal moves',
|
_('Shade legal moves'),
|
||||||
self.menubar.tkopt.shade,
|
self.menubar.tkopt.shade,
|
||||||
self.menubar.mOptShade)
|
self.menubar.mOptShade)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Negative cards bottom',
|
_('Negative cards bottom'),
|
||||||
self.menubar.tkopt.negative_bottom,
|
self.menubar.tkopt.negative_bottom,
|
||||||
self.menubar.mOptNegativeBottom)
|
self.menubar.mOptNegativeBottom)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Shrink face-down cards',
|
_('Shrink face-down cards'),
|
||||||
self.menubar.tkopt.shrink_face_down,
|
self.menubar.tkopt.shrink_face_down,
|
||||||
self.menubar.mOptShrinkFaceDown)
|
self.menubar.mOptShrinkFaceDown)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Shade filled stacks',
|
_('Shade filled stacks'),
|
||||||
self.menubar.tkopt.shade_filled_stacks,
|
self.menubar.tkopt.shade_filled_stacks,
|
||||||
self.menubar.mOptShadeFilledStacks)
|
self.menubar.mOptShadeFilledStacks)
|
||||||
|
|
||||||
|
@ -879,47 +910,47 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Animation settins
|
# Animation settins
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Animations'))
|
LTreeNode(text=_('Animations')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'None',
|
_('None'),
|
||||||
self.menubar.tkopt.animations, 0,
|
self.menubar.tkopt.animations, 0,
|
||||||
self.menubar.mOptAnimations)
|
self.menubar.mOptAnimations)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Very fast',
|
_('Very fast'),
|
||||||
self.menubar.tkopt.animations, 1,
|
self.menubar.tkopt.animations, 1,
|
||||||
self.menubar.mOptAnimations)
|
self.menubar.mOptAnimations)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Fast',
|
_('Fast'),
|
||||||
self.menubar.tkopt.animations, 2,
|
self.menubar.tkopt.animations, 2,
|
||||||
self.menubar.mOptAnimations)
|
self.menubar.mOptAnimations)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Medium',
|
_('Medium'),
|
||||||
self.menubar.tkopt.animations, 3,
|
self.menubar.tkopt.animations, 3,
|
||||||
self.menubar.mOptAnimations)
|
self.menubar.mOptAnimations)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Slow',
|
_('Slow'),
|
||||||
self.menubar.tkopt.animations, 4,
|
self.menubar.tkopt.animations, 4,
|
||||||
self.menubar.mOptAnimations)
|
self.menubar.mOptAnimations)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Very slow',
|
_('Very slow'),
|
||||||
self.menubar.tkopt.animations, 5,
|
self.menubar.tkopt.animations, 5,
|
||||||
self.menubar.mOptAnimations)
|
self.menubar.mOptAnimations)
|
||||||
|
|
||||||
# submenu.add_separator()
|
# submenu.add_separator()
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Redeal animation',
|
_('Redeal animation'),
|
||||||
self.menubar.tkopt.redeal_animation,
|
self.menubar.tkopt.redeal_animation,
|
||||||
self.menubar.mRedealAnimation)
|
self.menubar.mRedealAnimation)
|
||||||
|
|
||||||
self.addCheckNode(tv, rg,
|
self.addCheckNode(tv, rg,
|
||||||
'Winning animation',
|
_('Winning animation'),
|
||||||
self.menubar.tkopt.win_animation,
|
self.menubar.tkopt.win_animation,
|
||||||
self.menubar.mWinAnimation)
|
self.menubar.mWinAnimation)
|
||||||
|
|
||||||
|
@ -927,15 +958,15 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Touch mode settings
|
# Touch mode settings
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Touch mode'))
|
LTreeNode(text=_('Touch mode')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Drag-and-Drop',
|
_('Drag-and-Drop'),
|
||||||
self.menubar.tkopt.mouse_type, 'drag-n-drop',
|
self.menubar.tkopt.mouse_type, 'drag-n-drop',
|
||||||
self.menubar.mOptMouseType)
|
self.menubar.mOptMouseType)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Point-and-Click',
|
_('Point-and-Click'),
|
||||||
self.menubar.tkopt.mouse_type, 'point-n-click',
|
self.menubar.tkopt.mouse_type, 'point-n-click',
|
||||||
self.menubar.mOptMouseType)
|
self.menubar.mOptMouseType)
|
||||||
|
|
||||||
|
@ -969,10 +1000,10 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# Toolbar options
|
# Toolbar options
|
||||||
|
|
||||||
rg = tv.add_node(
|
rg = tv.add_node(
|
||||||
LTreeNode(text='Toolbar'))
|
LTreeNode(text=_('Toolbar')))
|
||||||
if rg:
|
if rg:
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Hide',
|
_('Hide'),
|
||||||
self.menubar.tkopt.toolbar, 0,
|
self.menubar.tkopt.toolbar, 0,
|
||||||
self.menubar.mOptToolbar)
|
self.menubar.mOptToolbar)
|
||||||
|
|
||||||
|
@ -987,11 +1018,11 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# self.menubar.mOptToolbar)
|
# self.menubar.mOptToolbar)
|
||||||
|
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Left',
|
_('Left'),
|
||||||
self.menubar.tkopt.toolbar, 3,
|
self.menubar.tkopt.toolbar, 3,
|
||||||
self.menubar.mOptToolbar)
|
self.menubar.mOptToolbar)
|
||||||
self.addRadioNode(tv, rg,
|
self.addRadioNode(tv, rg,
|
||||||
'Right',
|
_('Right'),
|
||||||
self.menubar.tkopt.toolbar, 4,
|
self.menubar.tkopt.toolbar, 4,
|
||||||
self.menubar.mOptToolbar)
|
self.menubar.mOptToolbar)
|
||||||
|
|
||||||
|
@ -1028,12 +1059,12 @@ class OptionsMenuDialog(LMenuDialog):
|
||||||
# self.menubar.mOptDemoLogo)
|
# self.menubar.mOptDemoLogo)
|
||||||
|
|
||||||
self.addCheckNode(tv, None,
|
self.addCheckNode(tv, None,
|
||||||
'Startup splash screen',
|
_('Startup splash screen'),
|
||||||
self.menubar.tkopt.splashscreen,
|
self.menubar.tkopt.splashscreen,
|
||||||
self.menubar.mOptSplashscreen)
|
self.menubar.mOptSplashscreen)
|
||||||
|
|
||||||
self.addCheckNode(tv, None,
|
self.addCheckNode(tv, None,
|
||||||
'Winning splash',
|
_('Winning splash'),
|
||||||
self.menubar.tkopt.display_win_message,
|
self.menubar.tkopt.display_win_message,
|
||||||
self.menubar.mWinDialog)
|
self.menubar.mWinDialog)
|
||||||
|
|
||||||
|
@ -1056,23 +1087,23 @@ class HelpMenuDialog(LMenuDialog):
|
||||||
def buildTree(self, tv, node):
|
def buildTree(self, tv, node):
|
||||||
tv.add_node(
|
tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text='Contents',
|
text=_('Contents'),
|
||||||
command=self.make_help_command(self.menubar.mHelp)))
|
command=self.make_help_command(self.menubar.mHelp)))
|
||||||
tv.add_node(
|
tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text='How to play',
|
text=_('How to play'),
|
||||||
command=self.make_help_command(self.menubar.mHelpHowToPlay)))
|
command=self.make_help_command(self.menubar.mHelpHowToPlay)))
|
||||||
tv.add_node(
|
tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text='Rules for this game',
|
text=_('Rules for this game'),
|
||||||
command=self.make_help_command(self.menubar.mHelpRules)))
|
command=self.make_help_command(self.menubar.mHelpRules)))
|
||||||
tv.add_node(
|
tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text='License terms',
|
text=_('License terms'),
|
||||||
command=self.make_help_command(self.menubar.mHelpLicense)))
|
command=self.make_help_command(self.menubar.mHelpLicense)))
|
||||||
tv.add_node(
|
tv.add_node(
|
||||||
LTreeNode(
|
LTreeNode(
|
||||||
text='About' + TITLE + '...',
|
text=_('About %s...') % TITLE,
|
||||||
command=self.make_help_command(self.menubar.mHelpAbout)))
|
command=self.make_help_command(self.menubar.mHelpAbout)))
|
||||||
|
|
||||||
# tv.add_node(LTreeNode(
|
# tv.add_node(LTreeNode(
|
||||||
|
@ -1234,6 +1265,7 @@ class PysolMenubarTk:
|
||||||
toolbar_vars={},
|
toolbar_vars={},
|
||||||
sound_sample_vars={},
|
sound_sample_vars={},
|
||||||
color_vars={},
|
color_vars={},
|
||||||
|
language=StringVar(),
|
||||||
)
|
)
|
||||||
for w in TOOLBAR_BUTTONS:
|
for w in TOOLBAR_BUTTONS:
|
||||||
self.tkopt.toolbar_vars[w] = BooleanVar()
|
self.tkopt.toolbar_vars[w] = BooleanVar()
|
||||||
|
@ -1288,6 +1320,7 @@ class PysolMenubarTk:
|
||||||
tkopt.negative_bottom.set(opt.negative_bottom)
|
tkopt.negative_bottom.set(opt.negative_bottom)
|
||||||
tkopt.display_win_message.set(opt.display_win_message)
|
tkopt.display_win_message.set(opt.display_win_message)
|
||||||
tkopt.cardset.set(self.app.cardset_manager.getSelected())
|
tkopt.cardset.set(self.app.cardset_manager.getSelected())
|
||||||
|
tkopt.language.set(opt.language)
|
||||||
|
|
||||||
for w in TOOLBAR_BUTTONS:
|
for w in TOOLBAR_BUTTONS:
|
||||||
tkopt.toolbar_vars[w].set(opt.toolbar_vars.get(w, False))
|
tkopt.toolbar_vars[w].set(opt.toolbar_vars.get(w, False))
|
||||||
|
@ -1346,7 +1379,7 @@ class PysolMenubarTk:
|
||||||
|
|
||||||
# LMainMenuDialog()
|
# LMainMenuDialog()
|
||||||
LMenuItem(self.__menubar.menu,
|
LMenuItem(self.__menubar.menu,
|
||||||
text="Menu", command=self.mMainMenuDialog)
|
text=_("Menu"), command=self.mMainMenuDialog)
|
||||||
|
|
||||||
MfxMenubar.addPath = None
|
MfxMenubar.addPath = None
|
||||||
|
|
||||||
|
@ -1574,7 +1607,8 @@ class PysolMenubarTk:
|
||||||
menu.delete(0, 'last')
|
menu.delete(0, 'last')
|
||||||
|
|
||||||
if len(games) == 0:
|
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:
|
elif len(games) > self.__cb_max * 4:
|
||||||
games.sort(lambda a, b: cmp2(a.name, b.name))
|
games.sort(lambda a, b: cmp2(a.name, b.name))
|
||||||
self._addSelectAllGameSubMenu(games, menu,
|
self._addSelectAllGameSubMenu(games, menu,
|
||||||
|
@ -1791,8 +1825,9 @@ class PysolMenubarTk:
|
||||||
#
|
#
|
||||||
|
|
||||||
DEFAULTEXTENSION = ".pso"
|
DEFAULTEXTENSION = ".pso"
|
||||||
FILETYPES = ((TITLE + " files", "*" + DEFAULTEXTENSION),
|
# TRANSLATORS: Usually, 'PySol files'
|
||||||
("All files", "*"))
|
FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
|
||||||
|
(_("All files"), "*"))
|
||||||
|
|
||||||
def mAddFavor(self, *event):
|
def mAddFavor(self, *event):
|
||||||
gameid = self.app.game.id
|
gameid = self.app.game.id
|
||||||
|
@ -1870,6 +1905,16 @@ class PysolMenubarTk:
|
||||||
self.game.doPause()
|
self.game.doPause()
|
||||||
self.tkopt.pause.set(self.game.pause)
|
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):
|
def mOptSoundDialog(self, *args):
|
||||||
if self._cancelDrag(break_pause=False):
|
if self._cancelDrag(break_pause=False):
|
||||||
return
|
return
|
||||||
|
@ -2066,10 +2111,9 @@ class PysolMenubarTk:
|
||||||
# if os.name == "posix":
|
# if os.name == "posix":
|
||||||
strings, default = (None, _("&Load"), _(
|
strings, default = (None, _("&Load"), _(
|
||||||
"&Cancel"), _("&Info..."), ), 1
|
"&Cancel"), _("&Info..."), ), 1
|
||||||
t = CARDSET
|
|
||||||
key = self.app.nextgame.cardset.index
|
key = self.app.nextgame.cardset.index
|
||||||
d = SelectCardsetDialogWithPreview(
|
d = SelectCardsetDialogWithPreview(
|
||||||
self.top, title=_("Select ") + t,
|
self.top, title=_("Select cardset"),
|
||||||
app=self.app, manager=self.app.cardset_manager, key=key,
|
app=self.app, manager=self.app.cardset_manager, key=key,
|
||||||
strings=strings, default=default)
|
strings=strings, default=default)
|
||||||
|
|
||||||
|
|
|
@ -35,17 +35,13 @@ class SelectCardsetDialogWithPreview(MfxDialog):
|
||||||
_cardset_store = None
|
_cardset_store = None
|
||||||
|
|
||||||
def __init__(self, parent, title, app, manager, key=None, **kw):
|
def __init__(self, parent, title, app, manager, key=None, **kw):
|
||||||
kw = self.initKw(kw)
|
|
||||||
MfxDialog.__init__(self, parent, title, **kw)
|
|
||||||
#
|
|
||||||
if key is None:
|
if key is None:
|
||||||
key = manager.getSelected()
|
key = 1
|
||||||
|
self.status = -1
|
||||||
|
self.key = key
|
||||||
self.app = app
|
self.app = app
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.key = key
|
return
|
||||||
self.preview_key = -1
|
|
||||||
self.all_keys = []
|
|
||||||
self.status = -1
|
|
||||||
|
|
||||||
def getSelected(self):
|
def getSelected(self):
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -165,7 +165,7 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
if name is None or not list(filter(
|
if name is None or not list(filter(
|
||||||
select_func, self.all_games_gi)):
|
select_func, self.all_games_gi)):
|
||||||
continue
|
continue
|
||||||
name = _("New games in v. ") + name
|
name = _("New games in v. %(version)s") % {'version': name}
|
||||||
gg.append(SelectGameNode(None, name, select_func))
|
gg.append(SelectGameNode(None, name, select_func))
|
||||||
if 1 and gg:
|
if 1 and gg:
|
||||||
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
||||||
|
|
|
@ -406,7 +406,7 @@ class HTMLViewer:
|
||||||
# self.defcursor = 'xterm'
|
# self.defcursor = 'xterm'
|
||||||
self.handcursor = "hand2"
|
self.handcursor = "hand2"
|
||||||
|
|
||||||
self.title = "Browser"
|
self.title = _("Browser")
|
||||||
self.window = None
|
self.window = None
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
|
@ -431,11 +431,11 @@ class HTMLViewer:
|
||||||
# BoxLayout(orientation='horizontal', size_hint=(1.0, 0.1))
|
# BoxLayout(orientation='horizontal', size_hint=(1.0, 0.1))
|
||||||
|
|
||||||
# create buttons
|
# create buttons
|
||||||
self.homeButton = HTMLButton(text="Index", on_release=self.goHome)
|
self.homeButton = HTMLButton(text=_("Index"), on_release=self.goHome)
|
||||||
self.backButton = HTMLButton(text="Back", on_release=self.goBack)
|
self.backButton = HTMLButton(text=_("Back"), on_release=self.goBack)
|
||||||
self.forwardButton = HTMLButton(
|
self.forwardButton = HTMLButton(
|
||||||
text="Forward", on_release=self.goForward)
|
text=_("Forward"), on_release=self.goForward)
|
||||||
self.closeButton = HTMLButton(text="Close", on_release=self.goHome)
|
self.closeButton = HTMLButton(text=_("Close"), on_release=self.goHome)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
buttonline.add_widget(self.homeButton)
|
buttonline.add_widget(self.homeButton)
|
||||||
|
@ -683,7 +683,8 @@ class HTMLViewer:
|
||||||
self.display(self.home, relpath=0)
|
self.display(self.home, relpath=0)
|
||||||
|
|
||||||
def errorDialog(self, msg):
|
def errorDialog(self, msg):
|
||||||
MfxMessageDialog(self.parent, title=TITLE + " HTML Problem",
|
MfxMessageDialog(self.parent,
|
||||||
|
title=_("%s HTML Problem") % TITLE,
|
||||||
text=msg,
|
text=msg,
|
||||||
# bitmap="warning"
|
# bitmap="warning"
|
||||||
# FIXME: this interp don't have images
|
# FIXME: this interp don't have images
|
||||||
|
|
|
@ -172,8 +172,11 @@ class SingleGame_StatsDialog(MfxDialog):
|
||||||
|
|
||||||
print('Stats(p): won=%s, lost=%s' % (won, lost))
|
print('Stats(p): won=%s, lost=%s' % (won, lost))
|
||||||
|
|
||||||
text1 = 'Total:\n won: %s ... %s%%\n lost: %s ... %s%%\n\n' % (
|
text1 = _('Total:\n' +
|
||||||
won, int(round(100.0 * pwon)), lost, int(round(100.0 * plost)))
|
' 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"))
|
# createChart(app, won, lost, _("Total"))
|
||||||
won, lost = app.stats.getSessionStats(player, gameid)
|
won, lost = app.stats.getSessionStats(player, gameid)
|
||||||
|
@ -181,9 +184,11 @@ class SingleGame_StatsDialog(MfxDialog):
|
||||||
|
|
||||||
print('Stats(s): won=%s, lost=%s' % (won, lost))
|
print('Stats(s): won=%s, lost=%s' % (won, lost))
|
||||||
|
|
||||||
text2 = \
|
text2 = _('Current Session:\n' +
|
||||||
'Current Session:\n won: %s ... %s%%\n lost: %s ... %s%%\n' % \
|
' won: %(won)s ... %(percentwon)s%%\n' +
|
||||||
(won, int(round(100.0 * pwon)), lost, int(round(100.0 * plost)))
|
' 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)
|
# text2 = 'Current Session:\n won=%s, lost=%s\n' % (won, lost)
|
||||||
|
|
||||||
# createChart(app, won, lost, _("Current session"))
|
# createChart(app, won, lost, _("Current session"))
|
||||||
|
|
|
@ -167,10 +167,10 @@ class MfxMessageDialog(MfxDialog):
|
||||||
|
|
||||||
# LB
|
# LB
|
||||||
# nicht automatisch ein neues spiel laden.
|
# nicht automatisch ein neues spiel laden.
|
||||||
if (title == "Game won"):
|
if (title == _("Game won")):
|
||||||
self.status = 1
|
self.status = 1
|
||||||
# self.button = 0
|
# self.button = 0
|
||||||
if (title == "Game finished"):
|
if (title == _("Game finished")):
|
||||||
self.status = 1
|
self.status = 1
|
||||||
# self.button =
|
# self.button =
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ class MfxMessageDialog(MfxDialog):
|
||||||
|
|
||||||
|
|
||||||
class MfxExceptionDialog(MfxMessageDialog):
|
class MfxExceptionDialog(MfxMessageDialog):
|
||||||
def __init__(self, parent, ex, title="Error", **kw):
|
def __init__(self, parent, ex, title=_("Error"), **kw):
|
||||||
kw = KwStruct(kw, bitmap="error")
|
kw = KwStruct(kw, bitmap="error")
|
||||||
text = kw.get("text", "")
|
text = kw.get("text", "")
|
||||||
if not text.endswith("\n"):
|
if not text.endswith("\n"):
|
||||||
|
@ -221,7 +221,7 @@ class PysolAboutDialog(object):
|
||||||
logging.info('PysolAboutDialog: txt=%s' % text)
|
logging.info('PysolAboutDialog: txt=%s' % text)
|
||||||
|
|
||||||
text = text + '\n\n' + 'Adaptation to Kivy/Android\n' + \
|
text = text + '\n\n' + 'Adaptation to Kivy/Android\n' + \
|
||||||
' Copyright (C) (2016-17) LB'
|
' Copyright (C) (2016-19) LB'
|
||||||
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.app = app
|
self.app = app
|
||||||
|
@ -261,13 +261,6 @@ class PysolAboutDialog(object):
|
||||||
label = FText(text=text, halign='center', size_hint=(1, 1))
|
label = FText(text=text, halign='center', size_hint=(1, 1))
|
||||||
window.content.add_widget(label)
|
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
|
# * a simple tooltip
|
||||||
|
@ -637,53 +630,8 @@ class MfxScrolledCanvas(object):
|
||||||
def scroll_bottom(self, *event):
|
def scroll_bottom(self, *event):
|
||||||
return self._yview('moveto', 1)
|
return self._yview('moveto', 1)
|
||||||
|
|
||||||
|
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
# *
|
# *
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
# not used witch kivy. would not nun as it refers TkInter.
|
# 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_("Statistics"), self.mPlayerStats, _("View statistics")),
|
||||||
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
||||||
(None, None, None),
|
(None, None, None),
|
||||||
(n_("Quit"), self.mHoldAndQuit, _("Quit ") + TITLE),
|
(n_("Quit"), self.mHoldAndQuit, _("Quit %s") % TITLE),
|
||||||
):
|
):
|
||||||
if label is None:
|
if label is None:
|
||||||
# sep = self._createSeparator()
|
# sep = self._createSeparator()
|
||||||
|
|
|
@ -56,25 +56,25 @@ if TOOLKIT == 'kivy':
|
||||||
def fatal_no_cardsets(app):
|
def fatal_no_cardsets(app):
|
||||||
app.wm_withdraw()
|
app.wm_withdraw()
|
||||||
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
|
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
|
||||||
text=_('''No cardsets were found !!!
|
text=_('''No cardsets were found!!!
|
||||||
|
|
||||||
Cardsets should be installed into:
|
Cardsets should be installed into:
|
||||||
%s/cardsets/
|
%(dir)s
|
||||||
|
|
||||||
Please check your %s installation.
|
Please check your %(app)s installation.
|
||||||
''') % (getprefdir(PACKAGE), TITLE),
|
''') % {'dir': getprefdir(PACKAGE) + '/cardsets/', 'app': TITLE},
|
||||||
bitmap="error", strings=(_("&Quit"),))
|
bitmap="error", strings=(_("&Quit"),))
|
||||||
else:
|
else:
|
||||||
def fatal_no_cardsets(app):
|
def fatal_no_cardsets(app):
|
||||||
app.wm_withdraw()
|
app.wm_withdraw()
|
||||||
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
|
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
|
||||||
text=_('''No cardsets were found !!!
|
text=_('''No cardsets were found!!!
|
||||||
|
|
||||||
Main data directory is:
|
Main data directory is:
|
||||||
%s
|
%(dir)s
|
||||||
|
|
||||||
Please check your %s installation.
|
Please check your %(app)s installation.
|
||||||
''') % (app.dataloader.dir, TITLE),
|
''') % {'dir': app.dataloader.dir, 'app': TITLE},
|
||||||
bitmap="error", strings=(_("&Quit"),))
|
bitmap="error", strings=(_("&Quit"),))
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,8 +93,8 @@ def parse_option(argv):
|
||||||
"sound-mod=",
|
"sound-mod=",
|
||||||
"help"])
|
"help"])
|
||||||
except getopt.GetoptError as err:
|
except getopt.GetoptError as err:
|
||||||
print_err(_("%s\ntry %s --help for more information") %
|
print_err(err + "\n" + _("try %s --help for more information") %
|
||||||
(err, prog_name), 0)
|
prog_name, 0)
|
||||||
return None
|
return None
|
||||||
opts = {"help": False,
|
opts = {"help": False,
|
||||||
"deal": None,
|
"deal": None,
|
||||||
|
@ -303,13 +303,14 @@ def pysol_init(app, args):
|
||||||
app.intro.progress.destroy()
|
app.intro.progress.destroy()
|
||||||
d = MfxMessageDialog(top, title=_("%s installation error") % TITLE,
|
d = MfxMessageDialog(top, title=_("%s installation error") % TITLE,
|
||||||
text=_('''
|
text=_('''
|
||||||
No games were found !!!
|
No games were found!!!
|
||||||
|
|
||||||
Main data directory is:
|
Main data directory is:
|
||||||
%s
|
%(dir)s
|
||||||
|
|
||||||
Please check your %s installation.
|
Please check your %(app)s installation.
|
||||||
''') % (app.dataloader.dir, TITLE), bitmap="error", strings=(_("&Quit"),))
|
''') % {'dir': app.dataloader.dir, 'app': TITLE},
|
||||||
|
bitmap="error", strings=(_("&Quit"),))
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# init cardsets
|
# init cardsets
|
||||||
|
|
|
@ -143,7 +143,11 @@ def getprefdir(package):
|
||||||
|
|
||||||
|
|
||||||
# high resolution clock() and sleep()
|
# high resolution clock() and sleep()
|
||||||
uclock = time.clock
|
try:
|
||||||
|
uclock = time.perf_counter
|
||||||
|
except Exception:
|
||||||
|
uclock = time.clock
|
||||||
|
|
||||||
usleep = time.sleep
|
usleep = time.sleep
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
uclock = time.time
|
uclock = time.time
|
||||||
|
|
|
@ -4,40 +4,46 @@ import sys
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
def n_(x):
|
class myLocalGettext(object):
|
||||||
return x
|
def __init__(self, lang):
|
||||||
|
self.language = lang
|
||||||
|
|
||||||
|
def translation(self):
|
||||||
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')
|
|
||||||
domain = gettext._current_domain
|
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:
|
try:
|
||||||
t = gettext.translation(domain,
|
t = self.translation()
|
||||||
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 ugettext(self, message):
|
||||||
|
# unicoded gettext
|
||||||
|
message = self.maketext(message)
|
||||||
|
try:
|
||||||
|
t = self.translation()
|
||||||
except IOError:
|
except IOError:
|
||||||
return message
|
return message
|
||||||
if sys.version_info >= (3, 0):
|
if sys.version_info >= (3, 0):
|
||||||
|
@ -45,10 +51,20 @@ def fix_gettext():
|
||||||
else:
|
else:
|
||||||
return t.ugettext(message)
|
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()
|
fix_gettext()
|
||||||
|
|
||||||
_ = gettext.ugettext
|
_ = gettext.ugettext
|
||||||
|
ungettext = gettext.ungettext
|
||||||
|
|
|
@ -25,16 +25,20 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
import configobj
|
||||||
|
|
||||||
import pysollib.settings
|
import pysollib.settings
|
||||||
from pysollib.configobj import configobj, validate
|
|
||||||
from pysollib.mfxutil import print_err
|
from pysollib.mfxutil import print_err
|
||||||
from pysollib.mygettext import _
|
from pysollib.mygettext import _
|
||||||
|
from pysollib.mygettext import myGettext
|
||||||
from pysollib.pysoltk import TOOLBAR_BUTTONS, TOOLKIT
|
from pysollib.pysoltk import TOOLBAR_BUTTONS, TOOLKIT
|
||||||
from pysollib.resource import CSI
|
from pysollib.resource import CSI
|
||||||
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
import validate
|
||||||
|
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
# * Options
|
# * Options
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
|
@ -111,6 +115,7 @@ solver_max_iterations = integer
|
||||||
solver_iterations_output_step = integer
|
solver_iterations_output_step = integer
|
||||||
solver_preset = string
|
solver_preset = string
|
||||||
display_win_message = boolean
|
display_win_message = boolean
|
||||||
|
language = string
|
||||||
|
|
||||||
[sound_samples]
|
[sound_samples]
|
||||||
move = boolean
|
move = boolean
|
||||||
|
@ -252,6 +257,7 @@ class Options:
|
||||||
# ('recent_gameid', 'list'),
|
# ('recent_gameid', 'list'),
|
||||||
# ('favorite_gameid', 'list'),
|
# ('favorite_gameid', 'list'),
|
||||||
('display_win_message', 'bool'),
|
('display_win_message', 'bool'),
|
||||||
|
('language', 'str'),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -319,6 +325,7 @@ class Options:
|
||||||
self.negative_bottom = True
|
self.negative_bottom = True
|
||||||
self.translate_game_names = True
|
self.translate_game_names = True
|
||||||
self.display_win_message = True
|
self.display_win_message = True
|
||||||
|
self.language = ''
|
||||||
# sound
|
# sound
|
||||||
self.sound = True
|
self.sound = True
|
||||||
self.sound_mode = 1
|
self.sound_mode = 1
|
||||||
|
@ -663,6 +670,8 @@ class Options:
|
||||||
for key in TOOLBAR_BUTTONS:
|
for key in TOOLBAR_BUTTONS:
|
||||||
self.toolbar_vars[key] = (key in visible_buttons)
|
self.toolbar_vars[key] = (key in visible_buttons)
|
||||||
|
|
||||||
|
myGettext.language = self.language
|
||||||
|
|
||||||
# solver
|
# solver
|
||||||
solver_presets = self._getOption('general', 'solver_presets', 'list')
|
solver_presets = self._getOption('general', 'solver_presets', 'list')
|
||||||
if solver_presets is not None:
|
if solver_presets is not None:
|
||||||
|
|
|
@ -290,7 +290,7 @@ class SelectGameDialogWithPreview(MfxDialog):
|
||||||
for version, vg in GI.GAMES_BY_PYSOL_VERSION:
|
for version, vg in GI.GAMES_BY_PYSOL_VERSION:
|
||||||
def selecter(gi, vg=vg):
|
def selecter(gi, vg=vg):
|
||||||
return gi.id in 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))
|
data.append((label, selecter))
|
||||||
self._addGamesFromData(data, store, None,
|
self._addGamesFromData(data, store, None,
|
||||||
_("by PySol version"), all_games)
|
_("by PySol version"), all_games)
|
||||||
|
@ -423,7 +423,7 @@ class SelectGameDialogWithPreview(MfxDialog):
|
||||||
# self.top.wm_title(
|
# self.top.wm_title(
|
||||||
# "Select Game - " + self.app.getGameTitleName(gameid))
|
# "Select Game - " + self.app.getGameTitleName(gameid))
|
||||||
title = 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 = gi.gameclass(gi)
|
||||||
self.preview_game.createPreview(self.preview_app)
|
self.preview_game.createPreview(self.preview_app)
|
||||||
|
|
|
@ -434,13 +434,13 @@ class HTMLViewer:
|
||||||
for p in REMOTE_PROTOCOLS:
|
for p in REMOTE_PROTOCOLS:
|
||||||
if url.startswith(p):
|
if url.startswith(p):
|
||||||
if not openURL(url):
|
if not openURL(url):
|
||||||
self.errorDialog(TITLE + _('''HTML limitation:
|
self.errorDialog(_('''%(app)s HTML limitation:
|
||||||
The %s protocol is not supported yet.
|
The %(protocol)s protocol is not supported yet.
|
||||||
|
|
||||||
Please use your standard web browser
|
Please use your standard web browser
|
||||||
to open the following URL:
|
to open the following URL:
|
||||||
%s
|
%(url)s
|
||||||
''') % (p, url))
|
''') % {'app': TITLE, 'protocol': p, 'url': url})
|
||||||
return
|
return
|
||||||
|
|
||||||
# locate the file relative to the current url
|
# locate the file relative to the current url
|
||||||
|
|
|
@ -67,7 +67,8 @@ class StatsFormatter(PysolStatsFormatter):
|
||||||
6, result[6],
|
6, result[6],
|
||||||
7, result[7])
|
7, result[7])
|
||||||
total, played, won, lost, time, moves, perc = self.getStatSummary()
|
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)
|
iter = self.store.append(None)
|
||||||
self.store.set(iter,
|
self.store.set(iter,
|
||||||
0, text,
|
0, text,
|
||||||
|
|
|
@ -24,95 +24,12 @@
|
||||||
|
|
||||||
# imports
|
# imports
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
|
|
||||||
from pysollib.mfxutil import SubclassResponsibility
|
import pysol_cards
|
||||||
try:
|
assert getattr(pysol_cards, 'VERSION', (0, 0, 0)) >= (0, 8, 7), (
|
||||||
import random2
|
"Newer version of https://pypi.org/project/pysol-cards is required.")
|
||||||
except ImportError:
|
from pysol_cards.random_base import RandomBase # noqa: I100
|
||||||
raise ImportError(
|
from pysol_cards.random import MTRandom, match_ms_deal_prefix # noqa: I100
|
||||||
"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)
|
|
||||||
|
|
||||||
|
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
|
@ -120,12 +37,12 @@ class MTRandom(BasicRandom, random2.Random):
|
||||||
# * uses the standard python module `random'
|
# * uses the standard python module `random'
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
|
|
||||||
# class WHRandom(BasicRandom, random.WichmannHill):
|
# class WHRandom(RandomBase, random.WichmannHill):
|
||||||
#
|
#
|
||||||
# def __init__(self, seed=None):
|
# def __init__(self, seed=None):
|
||||||
# if seed is None:
|
# if seed is None:
|
||||||
# seed = self._getRandomSeed()
|
# seed = self._getRandomSeed()
|
||||||
# BasicRandom.__init__(self)
|
# RandomBase.__init__(self)
|
||||||
# random.WichmannHill.__init__(self, seed)
|
# random.WichmannHill.__init__(self, seed)
|
||||||
# self.initial_seed = seed
|
# self.initial_seed = seed
|
||||||
# self.initial_state = self.getstate()
|
# 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):
|
def __init__(self, seed=None):
|
||||||
BasicRandom.__init__(self)
|
RandomBase.__init__(self)
|
||||||
if seed is None:
|
if seed is None:
|
||||||
seed = self._getRandomSeed()
|
seed = self._getRandomSeed()
|
||||||
self.initial_seed = self.setSeed(seed)
|
self.initial_seed = self.setSeed(seed)
|
||||||
|
@ -197,7 +114,7 @@ MS_LONG_BIT = (1 << 1000)
|
||||||
CUSTOM_BIT = (1 << 999)
|
CUSTOM_BIT = (1 << 999)
|
||||||
|
|
||||||
|
|
||||||
class CustomRandom(BasicRandom):
|
class CustomRandom(RandomBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.initial_seed = self.seed = MS_LONG_BIT | CUSTOM_BIT
|
self.initial_seed = self.seed = MS_LONG_BIT | CUSTOM_BIT
|
||||||
self.origin = self.ORIGIN_UNKNOWN
|
self.origin = self.ORIGIN_UNKNOWN
|
||||||
|
@ -233,7 +150,9 @@ class LCRandom31(MFXRandom):
|
||||||
return "ms" + str(self.initial_seed)
|
return "ms" + str(self.initial_seed)
|
||||||
|
|
||||||
def str(self, 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):
|
def setSeed(self, seed):
|
||||||
seed = int(seed)
|
seed = int(seed)
|
||||||
|
@ -275,18 +194,14 @@ PysolRandom = MTRandom
|
||||||
# * PySol support code
|
# * 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
|
# construct Random from seed string
|
||||||
def constructRandom(s):
|
def constructRandom(s):
|
||||||
if s == 'Custom':
|
if s == 'Custom':
|
||||||
return CustomRandom()
|
return CustomRandom()
|
||||||
m = _match_ms(s)
|
m = match_ms_deal_prefix(s)
|
||||||
if m:
|
if m is not None:
|
||||||
seed = int(m.group(1))
|
seed = m
|
||||||
if 0 <= seed <= LCRandom31.MAX_SEED:
|
if 0 <= seed <= LCRandom31.MAX_SEED:
|
||||||
ret = LCRandom31(seed)
|
ret = LCRandom31(seed)
|
||||||
ret.setSeedAsStr(s)
|
ret.setSeedAsStr(s)
|
||||||
|
@ -307,9 +222,9 @@ def constructRandom(s):
|
||||||
def random__str2long(s):
|
def random__str2long(s):
|
||||||
if s == 'Custom':
|
if s == 'Custom':
|
||||||
return CUSTOM_BIT | MS_LONG_BIT
|
return CUSTOM_BIT | MS_LONG_BIT
|
||||||
m = _match_ms(s)
|
m = match_ms_deal_prefix(s)
|
||||||
if m:
|
if m is not None:
|
||||||
return (int(m.group(1)) | MS_LONG_BIT)
|
return (m | MS_LONG_BIT)
|
||||||
else:
|
else:
|
||||||
return int(s)
|
return int(s)
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,8 @@ if os.name == 'posix':
|
||||||
DATA_DIRS = [
|
DATA_DIRS = [
|
||||||
'/usr/share/PySolFC',
|
'/usr/share/PySolFC',
|
||||||
'/usr/local/share/PySolFC',
|
'/usr/local/share/PySolFC',
|
||||||
'/usr/games/PySolFC',
|
'/usr/share/games/PySolFC',
|
||||||
'/usr/local/games/PySolFC',
|
'/usr/local/share/games/PySolFC',
|
||||||
]
|
]
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1945,7 +1945,7 @@ class TalonStack(Stack,
|
||||||
nredeals = _('Unlimited redeals.')
|
nredeals = _('Unlimited redeals.')
|
||||||
else:
|
else:
|
||||||
n = self.max_rounds-1
|
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
|
# round = _('Round #%d.') % self.round
|
||||||
return _('Talon.')+' '+nredeals # +' '+round
|
return _('Talon.')+' '+nredeals # +' '+round
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ class FileStatsFormatter(PysolStatsFormatter):
|
||||||
def writeStats(self, player, sort_by='name'):
|
def writeStats(self, player, sort_by='name'):
|
||||||
if player is None:
|
if player is None:
|
||||||
player = _('Demo')
|
player = _('Demo')
|
||||||
header = _("Statistics for ") + player
|
header = _("Statistics for %(player)s") % {'player': player}
|
||||||
self.writeHeader(header, 62)
|
self.writeHeader(header, 62)
|
||||||
header = self.getStatHeader()
|
header = self.getStatHeader()
|
||||||
self.pstats(*header)
|
self.pstats(*header)
|
||||||
|
@ -209,7 +209,8 @@ class FileStatsFormatter(PysolStatsFormatter):
|
||||||
self.pstats(gameid=gameid, *result)
|
self.pstats(gameid=gameid, *result)
|
||||||
self.nl()
|
self.nl()
|
||||||
total, played, won, lost, time, moves, perc = self.getStatSummary()
|
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)
|
won+lost, won, lost, time, moves, perc)
|
||||||
self.nl(2)
|
self.nl(2)
|
||||||
return played
|
return played
|
||||||
|
@ -231,14 +232,14 @@ class FileStatsFormatter(PysolStatsFormatter):
|
||||||
def writeFullLog(self, player):
|
def writeFullLog(self, player):
|
||||||
if player is None:
|
if player is None:
|
||||||
player = _('Demo')
|
player = _('Demo')
|
||||||
header = _("Full log for ") + player
|
header = _("Full log for %(player)s") % {'player': player}
|
||||||
prev_games = self.app.stats.prev_games.get(player)
|
prev_games = self.app.stats.prev_games.get(player)
|
||||||
return self.writeLog(player, header, prev_games)
|
return self.writeLog(player, header, prev_games)
|
||||||
|
|
||||||
def writeSessionLog(self, player):
|
def writeSessionLog(self, player):
|
||||||
if player is None:
|
if player is None:
|
||||||
player = _('Demo')
|
player = _('Demo')
|
||||||
header = _("Session log for ") + player
|
header = _("Session log for %(player)s") % {'player': player}
|
||||||
prev_games = self.app.stats.session_games.get(player)
|
prev_games = self.app.stats.session_games.get(player)
|
||||||
return self.writeLog(player, header, prev_games)
|
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._calc_MfxMessageDialog()(
|
||||||
self.top, title=_("Change theme"),
|
self.top, title=_("Change theme"),
|
||||||
text=_("""\
|
text=_("""\
|
||||||
This settings will take effect
|
These settings will take effect
|
||||||
the next time you restart """)+TITLE,
|
the next time you restart %(app)s""") % {'app': TITLE},
|
||||||
bitmap="warning",
|
bitmap="warning",
|
||||||
default=0, strings=(_("&OK"),))
|
default=0, strings=(_("&OK"),))
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#
|
#
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from pysollib.gamedb import GI
|
from pysollib.gamedb import GI
|
||||||
|
@ -80,7 +79,8 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
SelectDialogTreeData.__init__(self)
|
SelectDialogTreeData.__init__(self)
|
||||||
self.all_games_gi = list(map(
|
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), ]
|
self.no_games = [SelectGameLeaf(None, None, _("(no games)"), None), ]
|
||||||
#
|
#
|
||||||
s_by_type = s_oriental = s_special = s_original = s_contrib = \
|
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(
|
if name is None or not list(filter(
|
||||||
select_func, self.all_games_gi)):
|
select_func, self.all_games_gi)):
|
||||||
continue
|
continue
|
||||||
name = _("New games in v. ") + name
|
name = _("New games in v. %(version)s") % {'version': name}
|
||||||
gg.append(SelectGameNode(None, name, select_func))
|
gg.append(SelectGameNode(None, name, select_func))
|
||||||
if 1 and gg:
|
if 1 and gg:
|
||||||
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
||||||
|
@ -230,8 +230,6 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
lambda gi: gi.si.redeals == 3),
|
lambda gi: gi.si.redeals == 3),
|
||||||
SelectGameNode(None, _("Unlimited redeals"),
|
SelectGameNode(None, _("Unlimited redeals"),
|
||||||
lambda gi: gi.si.redeals == -1),
|
lambda gi: gi.si.redeals == -1),
|
||||||
# SelectGameNode(None, "Variable redeals",
|
|
||||||
# lambda gi: gi.si.redeals == -2),
|
|
||||||
SelectGameNode(
|
SelectGameNode(
|
||||||
None, _("Other number of redeals"),
|
None, _("Other number of redeals"),
|
||||||
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
|
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
|
if button == 0: # Ok or double click
|
||||||
self.gameid = self.tree.selection_key
|
self.gameid = self.tree.selection_key
|
||||||
self.tree.n_expansions = 1 # save xyview in any case
|
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)
|
doc = self.app.getGameRulesFilename(self.tree.selection_key)
|
||||||
if not doc:
|
if not doc:
|
||||||
return
|
return
|
||||||
|
@ -515,7 +513,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
||||||
# self.top.wm_title("Select Game - " +
|
# self.top.wm_title("Select Game - " +
|
||||||
# self.app.getGameTitleName(gameid))
|
# self.app.getGameTitleName(gameid))
|
||||||
title = 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 = gi.gameclass(gi)
|
||||||
self.preview_game.createPreview(self.preview_app)
|
self.preview_game.createPreview(self.preview_app)
|
||||||
|
|
|
@ -368,7 +368,8 @@ class TreeFormatter(PysolStatsFormatter):
|
||||||
self.parent_window.games[id] = t8
|
self.parent_window.games[id] = t8
|
||||||
|
|
||||||
total, played, won, lost, time_, moves, perc = self.getStatSummary()
|
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,
|
id = self.tree.insert("", "end", text=text,
|
||||||
values=(won+lost, won, lost, time_, moves, perc))
|
values=(won+lost, won, lost, time_, moves, perc))
|
||||||
self.parent_window.tree_items.append(id)
|
self.parent_window.tree_items.append(id)
|
||||||
|
|
|
@ -188,7 +188,7 @@ class PysolToolbarTk:
|
||||||
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
||||||
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
||||||
(None, None, None),
|
(None, None, None),
|
||||||
(n_("Quit"), self.mQuit, _("Quit ")+TITLE),
|
(n_("Quit"), self.mQuit, _("Quit %s") % TITLE),
|
||||||
):
|
):
|
||||||
if label is None:
|
if label is None:
|
||||||
sep = self._createSeparator()
|
sep = self._createSeparator()
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from pysollib.gamedb import GI
|
from pysollib.gamedb import GI
|
||||||
from pysollib.help import help_html
|
|
||||||
from pysollib.mfxutil import KwStruct, Struct, destruct
|
from pysollib.mfxutil import KwStruct, Struct, destruct
|
||||||
from pysollib.mfxutil import format_time
|
from pysollib.mfxutil import format_time
|
||||||
from pysollib.mygettext import _
|
from pysollib.mygettext import _
|
||||||
|
@ -39,11 +38,11 @@ from .selecttree import SelectDialogTreeCanvas
|
||||||
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
|
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
|
||||||
from .tkwidget import MfxDialog, MfxScrolledCanvas
|
from .tkwidget import MfxDialog, MfxScrolledCanvas
|
||||||
|
|
||||||
|
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
# * Nodes
|
# * Nodes
|
||||||
# ************************************************************************
|
# ************************************************************************
|
||||||
|
|
||||||
|
|
||||||
class SelectGameLeaf(SelectDialogTreeLeaf):
|
class SelectGameLeaf(SelectDialogTreeLeaf):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -137,7 +136,6 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
if 1 and gg:
|
if 1 and gg:
|
||||||
s_by_compatibility = SelectGameNode(None, _("by Compatibility"),
|
s_by_compatibility = SelectGameNode(None, _("by Compatibility"),
|
||||||
tuple(gg))
|
tuple(gg))
|
||||||
pass
|
|
||||||
#
|
#
|
||||||
s_by_pysol_version, gg = None, []
|
s_by_pysol_version, gg = None, []
|
||||||
for name, games in GI.GAMES_BY_PYSOL_VERSION:
|
for name, games in GI.GAMES_BY_PYSOL_VERSION:
|
||||||
|
@ -146,7 +144,7 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
if name is None or not list(filter(
|
if name is None or not list(filter(
|
||||||
select_func, self.all_games_gi)):
|
select_func, self.all_games_gi)):
|
||||||
continue
|
continue
|
||||||
name = _("New games in v. ") + name
|
name = _("New games in v. %(version)s") % {'version': name}
|
||||||
gg.append(SelectGameNode(None, name, select_func))
|
gg.append(SelectGameNode(None, name, select_func))
|
||||||
if 1 and gg:
|
if 1 and gg:
|
||||||
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
|
||||||
|
@ -167,16 +165,16 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
list(app.gdb.getGamesTuplesSortedByAlternateName()))
|
list(app.gdb.getGamesTuplesSortedByAlternateName()))
|
||||||
#
|
#
|
||||||
self.rootnodes = [_f for _f in (
|
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, _("Alternate Names"), ul_alternate_names),
|
||||||
SelectGameNode(None, _("Popular Games"),
|
SelectGameNode(None, _("Popular Games"),
|
||||||
lambda gi: gi.si.game_flags & GI.GT_POPULAR),
|
lambda gi: gi.si.game_flags & GI.GT_POPULAR),
|
||||||
|
s_by_type,
|
||||||
s_mahjongg,
|
s_mahjongg,
|
||||||
s_oriental,
|
s_oriental,
|
||||||
s_special,
|
s_special,
|
||||||
SelectGameNode(None, _("Custom Games"),
|
SelectGameNode(None, _("Custom Games"),
|
||||||
lambda gi: gi.si.game_type == GI.GT_CUSTOM),
|
lambda gi: gi.si.game_type == GI.GT_CUSTOM),
|
||||||
s_by_type,
|
|
||||||
SelectGameNode(None, _('by Skill Level'), (
|
SelectGameNode(None, _('by Skill Level'), (
|
||||||
SelectGameNode(None, _('Luck only'),
|
SelectGameNode(None, _('Luck only'),
|
||||||
lambda gi: gi.skill_level == GI.SL_LUCK),
|
lambda gi: gi.skill_level == GI.SL_LUCK),
|
||||||
|
@ -232,8 +230,6 @@ class SelectGameData(SelectDialogTreeData):
|
||||||
lambda gi: gi.si.redeals == 3),
|
lambda gi: gi.si.redeals == 3),
|
||||||
SelectGameNode(None, _("Unlimited redeals"),
|
SelectGameNode(None, _("Unlimited redeals"),
|
||||||
lambda gi: gi.si.redeals == -1),
|
lambda gi: gi.si.redeals == -1),
|
||||||
SelectGameNode(None, "Variable redeals",
|
|
||||||
lambda gi: gi.si.redeals == -2),
|
|
||||||
SelectGameNode(
|
SelectGameNode(
|
||||||
None, _("Other number of redeals"),
|
None, _("Other number of redeals"),
|
||||||
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
|
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
|
||||||
|
@ -308,8 +304,8 @@ class SelectGameDialog(MfxDialog):
|
||||||
def initKw(self, kw):
|
def initKw(self, kw):
|
||||||
kw = KwStruct(kw,
|
kw = KwStruct(kw,
|
||||||
strings=(None, None, _("&Cancel"),), default=0,
|
strings=(None, None, _("&Cancel"),), default=0,
|
||||||
separator=True,
|
|
||||||
resizable=True,
|
resizable=True,
|
||||||
|
separator=True,
|
||||||
padx=10, pady=10,
|
padx=10, pady=10,
|
||||||
buttonpadx=10, buttonpady=5,
|
buttonpadx=10, buttonpady=5,
|
||||||
)
|
)
|
||||||
|
@ -322,14 +318,15 @@ class SelectGameDialog(MfxDialog):
|
||||||
MfxDialog.destroy(self)
|
MfxDialog.destroy(self)
|
||||||
|
|
||||||
def mDone(self, button):
|
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.gameid = self.tree.selection_key
|
||||||
self.tree.n_expansions = 1 # save xyview in any case
|
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)
|
doc = self.app.getGameRulesFilename(self.tree.selection_key)
|
||||||
if not doc:
|
if not doc:
|
||||||
return
|
return
|
||||||
dir = os.path.join("html", "rules")
|
dir = os.path.join("html", "rules")
|
||||||
|
from pysollib.help import help_html
|
||||||
help_html(self.app, doc, dir, self.top)
|
help_html(self.app, doc, dir, self.top)
|
||||||
return
|
return
|
||||||
MfxDialog.mDone(self, button)
|
MfxDialog.mDone(self, button)
|
||||||
|
@ -436,7 +433,8 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
||||||
|
|
||||||
def initKw(self, kw):
|
def initKw(self, kw):
|
||||||
kw = KwStruct(kw,
|
kw = KwStruct(kw,
|
||||||
strings=(_("&Select"), _("&Rules"), _("&Cancel"),),
|
strings=((_("&Rules"), 10), 'sep',
|
||||||
|
_("&Select"), _("&Cancel"),),
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
return SelectGameDialog.initKw(self, kw)
|
return SelectGameDialog.initKw(self, kw)
|
||||||
|
@ -516,7 +514,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
||||||
# self.top.wm_title("Select Game - " +
|
# self.top.wm_title("Select Game - " +
|
||||||
# self.app.getGameTitleName(gameid))
|
# self.app.getGameTitleName(gameid))
|
||||||
title = 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 = gi.gameclass(gi)
|
||||||
self.preview_game.createPreview(self.preview_app)
|
self.preview_game.createPreview(self.preview_app)
|
||||||
|
@ -545,7 +543,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
|
||||||
#
|
#
|
||||||
self.updateInfo(gameid)
|
self.updateInfo(gameid)
|
||||||
#
|
#
|
||||||
rules_button = self.buttons[1]
|
rules_button = self.buttons[0]
|
||||||
if self.app.getGameRulesFilename(gameid):
|
if self.app.getGameRulesFilename(gameid):
|
||||||
rules_button.config(state="normal")
|
rules_button.config(state="normal")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -410,7 +410,8 @@ class CanvasFormatter(PysolStatsFormatter):
|
||||||
#
|
#
|
||||||
y += self.h
|
y += self.h
|
||||||
total, played, won, lost, time_, moves, perc = self.getStatSummary()
|
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))
|
self.pstats(y, (s, won+lost, won, lost, time_, moves, perc))
|
||||||
|
|
||||||
def writeLog(self, player, prev_games):
|
def writeLog(self, player, prev_games):
|
||||||
|
|
|
@ -188,7 +188,7 @@ class PysolToolbarTk:
|
||||||
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
|
||||||
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
|
||||||
(None, None, None),
|
(None, None, None),
|
||||||
(n_("Quit"), self.mQuit, _("Quit ")+TITLE),
|
(n_("Quit"), self.mQuit, _("Quit %s") % TITLE),
|
||||||
):
|
):
|
||||||
if label is None:
|
if label is None:
|
||||||
sep = self._createSeparator()
|
sep = self._createSeparator()
|
||||||
|
|
|
@ -293,7 +293,7 @@ class PysolMenubarTkCommon:
|
||||||
if WIN_SYSTEM == "aqua":
|
if WIN_SYSTEM == "aqua":
|
||||||
applemenu = MfxMenu(self.menubar, "apple")
|
applemenu = MfxMenu(self.menubar, "apple")
|
||||||
applemenu.add_command(
|
applemenu.add_command(
|
||||||
label=_("&About ")+TITLE, command=self.mHelpAbout)
|
label=_("&About %s") % TITLE, command=self.mHelpAbout)
|
||||||
|
|
||||||
menu = MfxMenu(self.menubar, n_("&File"))
|
menu = MfxMenu(self.menubar, n_("&File"))
|
||||||
menu.add_command(
|
menu.add_command(
|
||||||
|
@ -663,7 +663,7 @@ class PysolMenubarTkCommon:
|
||||||
if WIN_SYSTEM != "aqua":
|
if WIN_SYSTEM != "aqua":
|
||||||
menu.add_separator()
|
menu.add_separator()
|
||||||
menu.add_command(
|
menu.add_command(
|
||||||
label=n_("&About ")+TITLE+"...",
|
label=_("&About %s...") % TITLE,
|
||||||
command=self.mHelpAbout)
|
command=self.mHelpAbout)
|
||||||
|
|
||||||
MfxMenubar.addPath = None
|
MfxMenubar.addPath = None
|
||||||
|
@ -968,7 +968,8 @@ class PysolMenubarTkCommon:
|
||||||
def updateGamesMenu(self, menu, games):
|
def updateGamesMenu(self, menu, games):
|
||||||
menu.delete(0, 'last')
|
menu.delete(0, 'last')
|
||||||
if len(games) == 0:
|
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:
|
elif len(games) > self.cb_max*4:
|
||||||
games.sort(key=lambda x: x.name)
|
games.sort(key=lambda x: x.name)
|
||||||
self._addSelectAllGameSubMenu(games, menu,
|
self._addSelectAllGameSubMenu(games, menu,
|
||||||
|
@ -1132,7 +1133,8 @@ class PysolMenubarTkCommon:
|
||||||
#
|
#
|
||||||
|
|
||||||
DEFAULTEXTENSION = ".pso"
|
DEFAULTEXTENSION = ".pso"
|
||||||
FILETYPES = ((TITLE+" files", "*"+DEFAULTEXTENSION), ("All files", "*"))
|
FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
|
||||||
|
(_("All files"), "*"))
|
||||||
|
|
||||||
def mAddFavor(self, *event):
|
def mAddFavor(self, *event):
|
||||||
gameid = self.app.game.id
|
gameid = self.app.game.id
|
||||||
|
|
|
@ -25,7 +25,7 @@ class BaseSolverDialog:
|
||||||
def __init__(self, parent, app, **kw):
|
def __init__(self, parent, app, **kw):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.app = app
|
self.app = app
|
||||||
title = TITLE+' - FreeCell Solver'
|
title = _('%(app)s - FreeCell Solver') % {'app': TITLE}
|
||||||
kw = self.initKw(kw)
|
kw = self.initKw(kw)
|
||||||
self._calc_MfxDialog().__init__(
|
self._calc_MfxDialog().__init__(
|
||||||
self, parent, title, kw.resizable, kw.default)
|
self, parent, title, kw.resizable, kw.default)
|
||||||
|
|
|
@ -311,13 +311,13 @@ class Base_HTMLViewer:
|
||||||
for p in REMOTE_PROTOCOLS:
|
for p in REMOTE_PROTOCOLS:
|
||||||
if url.startswith(p):
|
if url.startswith(p):
|
||||||
if not openURL(url):
|
if not openURL(url):
|
||||||
self.errorDialog(TITLE + _('''HTML limitation:
|
self.errorDialog(_('''%(app)s HTML limitation:
|
||||||
The %s protocol is not supported yet.
|
The %(protocol)s protocol is not supported yet.
|
||||||
|
|
||||||
Please use your standard web browser
|
Please use your standard web browser
|
||||||
to open the following URL:
|
to open the following URL:
|
||||||
%s
|
%(url)s
|
||||||
''') % (p, url))
|
''') % {'app': TITLE, 'protocol': p, 'url': url})
|
||||||
return
|
return
|
||||||
|
|
||||||
# locate the file relative to the current url
|
# locate the file relative to the current url
|
||||||
|
@ -429,7 +429,7 @@ to open the following URL:
|
||||||
|
|
||||||
def errorDialog(self, msg):
|
def errorDialog(self, msg):
|
||||||
self._calc_MfxMessageDialog()(
|
self._calc_MfxMessageDialog()(
|
||||||
self.parent, title=TITLE+" HTML Problem",
|
self.parent, title="%(app)s HTML Problem" % {'app': TITLE},
|
||||||
text=msg,
|
text=msg,
|
||||||
# bitmap="warning", # FIXME: this interp don't have images
|
# bitmap="warning", # FIXME: this interp don't have images
|
||||||
strings=(_("&OK"),), default=0)
|
strings=(_("&OK"),), default=0)
|
||||||
|
|
|
@ -150,7 +150,7 @@ def all_games(sort_by='id'):
|
||||||
if gt == 'French':
|
if gt == 'French':
|
||||||
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
|
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
|
||||||
name = gi.name
|
name = gi.name
|
||||||
altnames = '<br>'.join(gi.altnames)
|
altnames = '<br/>'.join(gi.altnames)
|
||||||
fn = os.path.join(rules_dir, rules_fn)
|
fn = os.path.join(rules_dir, rules_fn)
|
||||||
if 1 and os.path.exists(fn):
|
if 1 and os.path.exists(fn):
|
||||||
print('''<tr>
|
print('''<tr>
|
||||||
|
|
|
@ -1,39 +1,61 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
# converts cardset images and config files in current
|
# Converts cardset images and config files in subdirs of the current
|
||||||
# directory from input-format to output-format.
|
# 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
|
# $> cardconv gif png
|
||||||
#
|
#
|
||||||
# needs package 'ImageMagick' beeing installed.
|
# Needs package 'ImageMagick' being installed.
|
||||||
|
|
||||||
ifo=''
|
ifo='gif'
|
||||||
if [ $1 ]
|
ofo='bmp'
|
||||||
then
|
|
||||||
|
if [ $# -eq 2 ]; then
|
||||||
ifo=$1
|
ifo=$1
|
||||||
else
|
|
||||||
echo 'use: cardconv <input-format> <output-format>'
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
ofo=''
|
|
||||||
if [ $2 ]
|
|
||||||
then
|
|
||||||
ofo=$2
|
ofo=$2
|
||||||
else
|
elif [ $# -ne 0 ]; then
|
||||||
echo 'use: cardconv <input-format> <output-format>'
|
echo "Usage: cardconv [INPUTFORMAT OUTPUTFORMAT]"
|
||||||
exit
|
echo "If formats are not specified, converts $ifo to $ofo."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# alle images.
|
# Returns true if $1/config.txt exists and contains a mention of the input format
|
||||||
for i in *.${ifo}; do convert $i `basename $i .${ifo}`.${ofo}; rm -f $i; done
|
# file extension.
|
||||||
|
chkdir() {
|
||||||
|
grep -qi "\.${ifo}" "${1}/config".txt
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
# config.txt
|
# Usage: convdir in_dir out_dir
|
||||||
if [ -f config.txt ]
|
convdir() {
|
||||||
then
|
mkdir -p "$2"
|
||||||
cp -a config.txt tmp.txt
|
|
||||||
cat tmp.txt | sed "s/.${ifo}/.${ofo}/g" >config.txt
|
# Convert all images
|
||||||
rm -f tmp.txt
|
if [ $ofo == 'png' ]; then
|
||||||
fi
|
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
|
from sys import platform
|
||||||
|
|
||||||
IS_MAC = (platform == "darwin")
|
IS_MAC = (platform == "darwin")
|
||||||
PY_VERS = ([] if re.search("\\bSKIP_PY2\\b",
|
TEST_TAGS = os.getenv('TEST_TAGS', '')
|
||||||
os.getenv('TEST_TAGS', '')) else [2])+[3]
|
|
||||||
|
|
||||||
|
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 = []
|
module_names = []
|
||||||
for d, _, files in os.walk("pysollib"):
|
for d, _, files in os.walk("pysollib"):
|
||||||
for f in files:
|
for f in files:
|
||||||
if re.search("\\.py$", f):
|
if re.search("\\.py$", f):
|
||||||
module_names.append(
|
module_names.append(
|
||||||
(d + "/" + re.sub("\\.py$", "", f)).replace("/", "."))
|
(d + "/" + re.sub("\\.py$", "", f))
|
||||||
|
.replace("/", ".").replace(os.sep, "."))
|
||||||
|
|
||||||
module_names.sort()
|
module_names.sort()
|
||||||
for module_name in module_names:
|
for module_name in module_names:
|
||||||
|
@ -22,7 +30,7 @@ for module_name in module_names:
|
||||||
continue
|
continue
|
||||||
is_gtk = ("gtk" in module_name)
|
is_gtk = ("gtk" in module_name)
|
||||||
for ver in PY_VERS:
|
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):
|
def fmt(s):
|
||||||
return s % {'module_name': module_name, 'ver': ver}
|
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
|
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 ver in PY_VERS:
|
||||||
for mod in [
|
for mod in [
|
||||||
'pysol_tests.acard_unit',
|
'pysol_tests.acard_unit',
|
||||||
|
'pysol_tests.game_drag',
|
||||||
'pysol_tests.hint',
|
'pysol_tests.hint',
|
||||||
'pysol_tests.import_file1',
|
'pysol_tests.import_file1',
|
||||||
'pysol_tests.kpat_load_save',
|
'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"
|
src_base="PySolFC-Cardsets"
|
||||||
dest_base="$src_base--Minimal"
|
dest_base="$src_base--Minimal"
|
||||||
ver="2.0"
|
ver="2.0.1"
|
||||||
src_vbase="$src_base-2.0"
|
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"
|
src_arc="$src_vbase.tar.bz2"
|
||||||
|
|
||||||
if ! test -f "$src_arc"
|
if ! test -f "$src_arc"
|
||||||
|
@ -21,6 +21,7 @@ then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tar -xvf "$src_arc"
|
tar -xvf "$src_arc"
|
||||||
|
rm -rf "$dest_vbase"
|
||||||
mkdir -p "$dest_vbase"
|
mkdir -p "$dest_vbase"
|
||||||
cat scripts/cardsets_to_bundle | (while read b
|
cat scripts/cardsets_to_bundle | (while read b
|
||||||
do
|
do
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
#
|
#
|
||||||
# y.sh
|
# y.sh
|
||||||
# Copyright (C) 2018 shlomif <shlomif@cpan.org>
|
# Copyright (C) 2018 shlomif <shlomif@cpan.org>
|
||||||
|
|