1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-05 00:02:29 -04:00

Compare commits

...

99 commits

Author SHA1 Message Date
Shlomi Fish
2c16d5773d fix a crash on "open game" 2020-02-09 11:42:44 +02:00
Shlomi Fish
eb5e97acc7 Refactoring / code cleanup.
Use a frozenset()-like list.

See:

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
8078db9abc fix load/save 2020-02-09 11:39:40 +02:00
Juhani Numminen
74016ff224 README: Change venv to reference PKGTREE and list more pre-requisites (#149) 2020-02-09 11:39:40 +02:00
Juhani Numminen
f96ceb57a0 install-pysolfc.sh: Use bash array for holding a program name and arguments
The previous scalar variable with quoted "$PIP" caused a "command not found"
error because that erroneously put prog name and arguments in a single token.
2020-02-09 11:39:40 +02:00
Shlomi Fish
d90bbdab2e Add 13 column Bisley - thanks to qunka.
See:

* https://github.com/shlomif/PySolFC/issues/148

* https://gist.github.com/qunka/3adac1878b481eb419ff6f3e1726add5

Thanks to https://github.com/qunka .
2020-02-09 11:39:40 +02:00
Shlomi Fish
70012407b0 Extract a function or class to step away from God Object.
See:

* https://en.wikipedia.org/wiki/God_object

* https://www.c-sharpcorner.com/article/god-object-a-code-smell/ .

This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
93d8c2a564 Extract a function or class to step away from God Object.
See:

* https://en.wikipedia.org/wiki/God_object

* https://www.c-sharpcorner.com/article/god-object-a-code-smell/ .

This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
85545dd67c avoid leftover py2 test files. 2020-02-09 11:39:40 +02:00
Shlomi Fish
4e7a01517a Extract a function or class to step away from God Object.
See:

* https://en.wikipedia.org/wiki/God_object

* https://www.c-sharpcorner.com/article/god-object-a-code-smell/ .

This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
b4947cccce appveyor fix #1 - temply invalidate caches 2020-02-09 11:39:40 +02:00
Topi Miettinen
879709d682 Skiz help: fix typo
decending -> descending
2020-02-09 11:39:40 +02:00
Shlomi Fish
2bf9b249cc move functionality to pysol-cards PyPI module 2020-02-09 11:39:40 +02:00
Shlomi Fish
1feb099be0 fix tests 2020-02-09 11:39:40 +02:00
Topi Miettinen
4c8c511626 Describe order of Ganjifa suits
Help for Dashavatara Ganjifa games refer to general Ganjifa help for
order of suits, but it was actually missing.
2020-02-09 11:39:40 +02:00
lufebe16
8f618a6a29 Android build script adaptations. 2020-02-09 11:39:40 +02:00
lufebe16
5119b5a161 Added android permission request dialog 2020-02-09 11:39:40 +02:00
lufebe16
de2d9d5eae Double tap added to cardselection (kivy toolkit)
- fixes issue #117
2020-02-09 11:39:40 +02:00
lufebe16
ad0512d333 fixed2: allow game restore to accept older storage format. 2020-02-09 11:39:40 +02:00
lb@lb7520
62a754a0ee fixed: restore game 2020-02-09 11:39:40 +02:00
lb@lb7520
1bc1dd25c5 language selection
- option 'language' added to option section 'general'
- language selection code in kivy menubar
- mygettext refactored
- translations (german) updated
2020-02-09 11:39:40 +02:00
lb@lb7520
58bf92b87a german translations (revised) 2020-02-09 11:39:40 +02:00
lb@lb7520
d0f409db80 german translations (mainly kivy/android related)
- updated kivy/android files
- executed 'make pot' to update message-ids
- german translations added
2020-02-09 11:39:40 +02:00
Juhani Numminen
f50604354c Update translations after string changes 2020-02-09 11:39:40 +02:00
Juhani Numminen
2f93667fc6 Improve internationalization
- More strings marked for translation
- Used format strings instead of concatinating fragments
- Removed space before '?' or '!' from sentences in English

Following the GNU gettext guidelines at
https://www.gnu.org/software/gettext/manual/gettext.html#Preparing-Strings
2020-02-09 11:39:40 +02:00
Shlomi Fish
18e6ab96be Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
4c331d32bd Add more items to "how you can contribute". 2020-02-09 11:39:40 +02:00
Shlomi Fish
d8367a140a Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Juhani Numminen
7a7874fc47 Refactor and comment locale_dir selection 2020-02-09 11:39:40 +02:00
Juhani Numminen
6ea2bba758 Detect LANG for gettext on android 2020-02-09 11:39:40 +02:00
Juhani Numminen
ceb6df5548 setup*.py: Install locale/ also on macOS and Windows 2020-02-09 11:39:40 +02:00
lb@lb7520
e9ee79bb46 Changes to apk build procedures used with fdroid build.
- mkp4a.init: 1 optional parameter
- mkp4a.unsigned: 2 paramters (sdk and ndk paths)
- mkp4a.preload: new helper script
2020-02-09 11:39:40 +02:00
Shlomi Fish
0c686267b3 valid xhtml5 2020-02-09 11:39:40 +02:00
Shlomi Fish
195890894a Fix the website build.
See https://github.com/shlomif/pysolfc-website/ - do not remove.
2020-02-09 11:39:40 +02:00
Shlomi Fish
6a5287c0b8 gnumakefile cleanup. 2020-02-09 11:39:40 +02:00
Shlomi Fish
05cb36a41f Update .gitignore: add generated files or remove old. 2020-02-09 11:39:40 +02:00
Shlomi Fish
39a4978357 Extract a method or a function.
This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
6ddacc7d27 Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
08a1a9b872 Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:40 +02:00
Shlomi Fish
493e044122 Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
lb@lb7520
efff5f56ff cardconv updated.
png images converted from gifs by IM resulted in an indexed png format. This
lead to Problems with tranparency, especially with ganijfa and hanafuda
cardsets. To circumvent these problems IM had to be advised to produce
32 bit mode rgba format.
affects android apk build process only.
2020-02-09 11:39:39 +02:00
lb@lb520
f73c032b41 Further corrections to python3 / android. Card images format has been changed to png. And a bugfix. 2020-02-09 11:39:39 +02:00
lb@lb520
3396bcd0ef additional deletes in mkp4a.init to allow apk builds out of the repo
fix for a gradle bug (in mkp4a.common)
2020-02-09 11:39:39 +02:00
Juhani Numminen
50a4c06e1c android: adapt to python3.
android: Install p4a using pip
Add 'set -e' to sh scripts, they must stop upon any error
README.md: kivy does not specifically require python2
android: Overhaul initialization scripts
android: Simplify apk build scripts by using the 'common' file
Merge the scripts cardconv and cardsetsgiftobmp
android: Fix startup script shebang to python3
android: Simplify mkkeystore and ignore the keystore file
android: Fix up debian prerequisites

The package cython (as opposed to cython3) even caused me to produce
an apk that did not start (TypeError: must be str, not bytes).

Android SDK requires java8; install and set as the default.

The build processes complained about lld and libtinfo5; added in.

mercurial (or hg) is seemingly unused.

The pip pcakges were needed by android/initsdk.py which is gone.

android: Ensure that the html files are built
2020-02-09 11:39:39 +02:00
Shlomi Fish
575b4b1923 moved functionality to pysol-cards.py 2020-02-09 11:39:39 +02:00
Shlomi Fish
72c2276f94 Fix the 'picturegallery' game.
See https://github.com/shlomif/PySolFC/issues/140 . Thanks to
@scottfurry for testing.
2020-02-09 11:39:39 +02:00
Juhani Numminen
989b566090 Add an error message to pysol_cards version check 2020-02-09 11:39:39 +02:00
Shlomi Fish
abdac68586 moved functionality to pysol-cards.py 2020-02-09 11:39:39 +02:00
Shlomi Fish
35d6c0e9b4 Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Shlomi Fish
3ff955be53 consolidate diffs 2020-02-09 11:39:39 +02:00
Shlomi Fish
0e692eb511 update to latest pypi ver 2020-02-09 11:39:39 +02:00
Juhani Numminen
8922e6f0ac travis: Simplify pip install step 2020-02-09 11:39:39 +02:00
Juhani Numminen
2b3f211641 travis: Change language from python to generic
Travis's python virtualenv setup does not help too much in our case
because we use both python2 and python3 while travis will setup the
virtualenv for only one at a time.
2020-02-09 11:39:39 +02:00
Juhani Numminen
07f2a6409a travis: linux: Use alternative way to install deps, apt addon is not working anymore?!
Strip down some dependencies:
 - libperl-dev is not used.
 - perl will come with cpanminus.
 - make is installed in Travis' ubuntu image anyway.
 - python3 will follow from the python3-* modules.
 - Explicitly mention python-pip.
2020-02-09 11:39:39 +02:00
Juhani Numminen
364e0325db travis: osx: Do not set CPPFLAGS etc, nothing will be compiled 2020-02-09 11:39:39 +02:00
Juhani Numminen
8983de5e9a travis: Change Ubuntu 14.04 to 18.04 2020-02-09 11:39:39 +02:00
Juhani Numminen
5243810dba repack-min-cardsets: Remove temporary directory before copying (github #135)
If the directory is not removed, on the second run of the script,
duplicated subdirectories will be created, e.g.
    $dest_vbase/cardset-standard/cardset-standard/
that will essentially double the unpacked size.
2020-02-09 11:39:39 +02:00
Shlomi Fish
4726d91f66 fix flake8 tests 2020-02-09 11:39:39 +02:00
Frédéric Brière
618195bb8e Define canMoveCards() for Hanafuda sequence stacks
Any stack of Hanafuda cards is always deemed movable, even if it is out
of sequence.  The effect can easily be seen a game such as Firecracker,
where:

 - Any stack can be dragged as a whole (but not released)
 - "Highlight piles" will highlight everything
 - Asking for a hint will ignore most valid moves

This is due to Hanafuda_SequenceStack lacking a canMoveCards() method.
2020-02-09 11:39:39 +02:00
Juhani Numminen
09a5df8439 Remove checks for individual cardsets' COPYRIGHT files
It is not strictly necessary to check the presence of the COPYRIGHT file
since pysollib/{tile,tk}/selectcardset.py will work even if it's missing.

In the Debian package, individual COPYRIGHT files are missing as all license
information should be in one file anyway, /usr/share/doc/pysolfc/copyright.

Thanks to Bernhard Reiter who originally wrote this patch:
https://salsa.debian.org/games-team/pysolfc/blob/87493fb/debian/patches/remove_checks_for_copyright_files
2020-02-09 11:39:39 +02:00
Juhani Numminen
e1a320c837 Improve Filesystem Hierarchy Standard compliance
These are actually for binaries, not data:
/usr/games       "Games and educational binaries"
/usr/local/games "Local game binaries"

The directories for the data:
/usr/share/games "Static data files for /usr/games"

"The requirements for the contents of [/usr/local/share] are the same as for
/usr/share."

Quoting https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html
2020-02-09 11:39:39 +02:00
Shlomi Fish
c524900e69 consolidate diffs 2020-02-09 11:39:39 +02:00
Shlomi Fish
b3f6232dd7 Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Shlomi Fish
f3d3517160 add missing attribute.
See https://github.com/shlomif/PySolFC/issues/129 .
2020-02-09 11:39:39 +02:00
Shlomi Fish
ad1a358672 Add missing package - 'game'.
See https://github.com/shlomif/PySolFC/issues/130 .

Thanks to @scottfurry .
2020-02-09 11:39:39 +02:00
Shlomi Fish
a7b8921695 minimal ver of pysol_cards.
See https://github.com/shlomif/PySolFC/issues/128 .
2020-02-09 11:39:39 +02:00
Shlomi Fish
b76e395892 fix travis failures. 2020-02-09 11:39:39 +02:00
Shlomi Fish
b2a9fde2f5 moved functionality to pysol-cards.py 2020-02-09 11:39:39 +02:00
Shlomi Fish
9ef244d185 Extract a function or class to step away from God Object.
See:

* https://en.wikipedia.org/wiki/God_object

* https://www.c-sharpcorner.com/article/god-object-a-code-smell/ .

This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Shlomi Fish
4c77060c40 moved functionality to pysol-cards.py 2020-02-09 11:39:39 +02:00
Juhani Numminen
a147346b99 Makefile: On Windows, use ; as path separator instead of : 2020-02-09 11:39:39 +02:00
Juhani Numminen
4d7b636174 Remove extraneous plurals from po/de*, add msgfmt checks 2020-02-09 11:39:39 +02:00
Juhani Numminen
8658e6c618 Simplify po/pysol.pot generation 2020-02-09 11:39:39 +02:00
Juhani Numminen
1c848c8122 Split de.po into de_pysol.po and de_games.po
All the other languages are that way.
2020-02-09 11:39:39 +02:00
Juhani Numminen
a85f537305 Improve Makefile
The PYTHONPATH is now set centrally, except for the tests.
Environment for the tests is prepared using target-specific variables.
https://www.gnu.org/software/make/manual/make.html#Target_002dspecific

To ensure exiting on command failure, "set -e" is added before loops,
and otherwise ";" is replaced with "&&" between commands.
https://www.debian.org/doc/debian-policy/ch-source.html#error-trapping-in-makefiles

"mkdir -p" does not need an existence test beforehand.

Changed all python invocations to python3.

LANG and PYSOL_DEBUG were never exported so they probably had no effect.
Removed for simplicity.
2020-02-09 11:39:39 +02:00
Juhani Numminen
ab63876f31 Generate all_games.html in the data/html directory 2020-02-09 11:39:39 +02:00
Shlomi Fish
2594ebaa12 moved functionality to pysol-cards.py 2020-02-09 11:39:39 +02:00
Shlomi Fish
bc63eac8cd Refactoring / code cleanup.
See:

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Juhani Numminen
36b568543e Install icons to standard locations for linux desktop
Also reorganize icons in subdirectories indicating size.

See the Icon Theme Specification:
https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#install_icons
2020-02-09 11:39:39 +02:00
Shlomi Fish
539cb641aa list prereqs.
See https://caremad.io/posts/2013/07/setup-vs-requirement/ .
2020-02-09 11:39:39 +02:00
Juhani Numminen
4768e0f3aa Delete local copy of configobj in favor of the pypi package
The "validate" module does come in the same pypi package.
2020-02-09 11:39:39 +02:00
Shlomi Fish
cf84de4070 Extract a method or a function.
This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Shlomi Fish
f69d5cb9b4 Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Shlomi Fish
6d3a0c0d4f install the attrs.py deps. 2020-02-09 11:39:39 +02:00
Shlomi Fish
dde8e2b3df Extract a common class/struct.
This is Refactoring / code cleanup.

See:

* https://en.wikipedia.org/wiki/God_object

* https://en.wikipedia.org/wiki/Extract_class

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:39 +02:00
Juhani Numminen
3a85dfa712 Don't duplicate locales, e.g. de -> de_DE
The duplication seems to be unnecessary. Setting environment variable
LANG=it_CH gives Italian as PySolFC language, even if we didn't explicitly
create 'it_CH'.
2020-02-09 11:39:38 +02:00
Juhani Numminen
6dc39569be Install all translations, not only Russian 2020-02-09 11:39:38 +02:00
Shlomi Fish
6bc3220045 appveyor fix #1 - os.sep 2020-02-09 11:39:38 +02:00
Juhani Numminen
1c91b58342 Update pysol.desktop for unix desktop integration
Encoding has been deprecated - see https://specifications.freedesktop.org/desktop-entry-spec/1.0/apc.html

StartupWMClass adds a startup animation. xprop was used to find out the value for WM_CLASS; it was tested on KDE Plasma 5.
2020-02-09 11:39:38 +02:00
Shlomi Fish
a7713dba3c fix travis / dist tests. 2020-02-09 11:39:38 +02:00
Shlomi Fish
9dd1eeb15e remove pointless comments 2020-02-09 11:39:38 +02:00
Shlomi Fish
b306d78c47 extract a method. 2020-02-09 11:39:38 +02:00
Shlomi Fish
f64875c418 fix pysol --deal 1/24/1000.
Low ms deals without the ms prefix caused an error.
2020-02-09 11:39:38 +02:00
Shlomi Fish
ae16134dce Extract ms_rearrange . 2020-02-09 11:39:38 +02:00
Shlomi Fish
dc1d061103 speedup.
See https://github.com/shlomif/PySolFC/issues/119 .
2020-02-09 11:39:38 +02:00
Shlomi Fish
5a6dcba2fd Extract a function or class to step away from God Object.
See:

* https://en.wikipedia.org/wiki/God_object

* https://www.c-sharpcorner.com/article/god-object-a-code-smell/ .

This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:38 +02:00
Shlomi Fish
b03e877b3b refactoring 2020-02-09 11:39:38 +02:00
Shlomi Fish
648f3d6bb9 refactoring 2020-02-09 11:39:38 +02:00
Shlomi Fish
bcaa95660b Extract a function or class to step away from God Object.
See:

* https://en.wikipedia.org/wiki/God_object

* https://www.c-sharpcorner.com/article/god-object-a-code-smell/ .

This is Refactoring / code cleanup.

See:

* https://refactoring.com/catalog/extractMethod.html

* https://en.wikipedia.org/wiki/Code_refactoring

* https://www.refactoring.com/

* https://www.joelonsoftware.com/2002/01/23/rub-a-dub-dub/

Some small optimisations may have slipped in as well.
2020-02-09 11:39:38 +02:00
105 changed files with 23349 additions and 21828 deletions

View file

@ -8,15 +8,7 @@ install:
- choco install strawberryperl
- copy %PYTHON%\python.exe %PYTHON%\python3.exe
- SET PATH=%PYTHON%;C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
- python3 -mpip install Pillow
- python3 -mpip install flake8
- python3 -mpip install flake8-import-order
- python3 -mpip install py2exe
- python3 -mpip install pycotap
- python3 -mpip install pygame
- python3 -mpip install pysol-cards
- python3 -mpip install random2
- python3 -mpip install six
- python3 -mpip install Pillow attrs configobj flake8 flake8-import-order py2exe pycotap pygame pysol-cards random2 setuptools six
- perl -v
- copy C:\msys64\mingw64\bin\mingw32-make.exe C:\msys64\mingw64\bin\make.exe
- SET PATH=C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
@ -64,6 +56,6 @@ artifacts:
- path: dist\PySolFC_*_setup.exe
name: pysol_win_installer
cache:
- C:\_P5 -> .appveyor.yml
- C:\strawberry -> .appveyor.yml
# - C:\_P5 -> .appveyor.yml
# - C:\strawberry -> .appveyor.yml
shallow_clone: true

12
.gitignore vendored
View file

@ -1,6 +1,7 @@
*.pyc
/.tidyall.d/
/MANIFEST
/_Inline/
/build/*
/data/cardsets/*
/data/html/*
@ -10,15 +11,16 @@
/html-src/html/*
/images
/locale/*
/po/de.po
/po/it.po
/po/pl.po
/po/ru.po
/tests/individually-importing/*.py
/tests/unit-generated/*.py
# output of min card actions
# mask out folder/archives
PySolFC-Cardsets--Minimal-2.0/
PySolFC-Cardsets--Minimal-2.0.*
PySolFC-Cardsets-2.0/
PySolFC-Cardsets--Minimal-2.0/
PySolFC-Cardsets-2.0.*
PySolFC-Cardsets-2.0/
android/*.apk
android/*.zip
android/bin/keystore

View file

@ -1,20 +1,4 @@
addons:
apt:
packages:
- cpanminus
- libperl-dev
- make
- perl
- python-glade2
- python-gnome2
- python-gnome2-dev
- python-gtk2
- python-setuptools
- python-tk
- python3
- python3-pip
- python3-setuptools
- python3-tk
homebrew:
update: true
brewfile: true
@ -32,32 +16,34 @@ deploy:
repo: shlomif/PySolFC
tags: true
skip_cleanup: true
dist: trusty
dist: bionic
before_install:
- if test "$TRAVIS_OS_NAME" = "osx" ; then export CPPFLAGS="$CPPFLAGS -I$(brew --prefix openssl)/include" PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig:$PKG_CONFIG_PATH" LDFLAGS="$LDFLAGS -L$(brew --prefix openssl)/lib" ;fi
- if test "$TRAVIS_OS_NAME" = "osx" ; then wget --content-disposition https://sourceforge.net/projects/pysolfc/files/PySolFC-Cardsets/minimal/PySolFC-Cardsets--Minimal-2.0.tar.xz/download && tar xJf PySolFC-Cardsets--Minimal-2.0.tar.xz && mv PySolFC-Cardsets--Minimal-2.0/cardset-* data ; fi
install:
- sudo cpanm --notest Capture::Tiny
# Tests are failing for them sometimes
- sudo cpanm --notest Capture::Tiny IPC::System::Simple
- sudo cpanm Code::TidyAll::Plugin::Flake8 Perl::Tidy Test::Code::TidyAll Test::Differences Test::TrailingSpace
- export PY_MODS='pycotap pysol-cards random2 six'
- "`which python3` -m pip install --upgrade flake8 flake8-import-order $PY_MODS"
- "sudo /usr/bin/python3 -m pip install --upgrade $PY_MODS || true"
- "sudo `which python2` -m pip install --upgrade $PY_MODS"
- which python
- which python2
language: python
python: "3.6"
- export PY_MODS='attrs configobj pycotap pysol-cards random2 setuptools six'
- sudo pip3 install --upgrade $PY_MODS flake8 flake8-import-order
- sudo pip2 install --upgrade $PY_MODS
language: generic
matrix:
include:
- os: linux
before_install:
- sudo apt-get install -y
cpanminus
python-glade2
python-gnome2
python-gnome2-dev
python-gtk2
python-pip
python-setuptools
python-tk
python3-pip
python3-setuptools
python3-tk
- os: osx
language: generic
env:
- TOXENV=py35
- PATH="/usr/local/opt/openssl/bin:$PATH:/usr/local/bin"
- DYLD_LIBRARY_PATH="/usr/local/opt/curl/lib:/usr/local/opt/openssl/lib:${DYLD_LIBRARY_PATH}"
- LDFLAGS="$LDFLAGS -L/usr/local/opt/openssl/lib"
- CPPFLAGS="$CPPFLAGS -I/usr/local/opt/openssl/include"
script:
- export TIDYALL_DATA_DIR="$HOME/tidyall_d"
- bash -x scripts/travis-ci-build

View file

@ -12,6 +12,7 @@ for general guidelines for contributing to open source.
# How you can contribute
- Translate PySol to a human language you know.
- Test the "master" branch version of the version control repository or other prereleases.
- Try to reproduce [open issues](https://github.com/shlomif/PySolFC/issues)
- Try to fix bugs.
- Add new games.
@ -20,6 +21,7 @@ for general guidelines for contributing to open source.
- Add new features.
- Contribute graphics
- Improve the site
- Package PySol for a new package repository or OS, or update existing packages.
- Make a monetary donation.
- [Star](https://help.github.com/articles/about-stars/) or [Watch](https://help.github.com/articles/watching-and-unwatching-repositories/) the repository on GitHub

View file

@ -7,9 +7,10 @@ include pysol.py setup.py setup_osx.py setup.cfg MANIFEST.in Makefile
include COPYING README.md AUTHORS.md README.android README.kivy
include NEWS.asciidoc
#recursive-include pysollib *.py
include pysollib/*.py pysollib/macosx/*.py pysollib/configobj/*.py
include pysollib/*.py pysollib/macosx/*.py
include pysollib/winsystems/*.py
include pysollib/tk/*.py pysollib/tile/*.py pysollib/pysolgtk/*.py
include pysollib/game/*.py
include pysollib/games/*.py pysollib/games/special/*.py
include pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py
include data/tcl/*.tcl
@ -18,8 +19,8 @@ include data/pysolfc.glade
graft data/themes
recursive-exclude data/themes *.py
include scripts/build.bat scripts/create_iss.py scripts/mahjongg_utils.py
include scripts/pygettext.py scripts/all_games.py scripts/cardset_viewer.py
include scripts/cardconv scripts/cardsetsgiftobmp
include scripts/all_games.py scripts/cardset_viewer.py
include scripts/cardconv
include scripts/gen_individual_importing_tests.py
include tests/individually-importing/PLACEHOLDER
recursive-include tests/lib *.pm *.py
@ -29,6 +30,8 @@ include tests/unit-generated/PLACEHOLDER
include .tidyallrc
include android/*.py
include android/mk*
include android/initsdk
include android/initsupport
include android/debian/*
#graft data/plugins
##

View file

@ -1,82 +1,73 @@
# Makefile for PySolFC
override LANG=C
override PYSOL_DEBUG=1
ifeq ($(OS),Windows_NT)
path_sep = ;
else
path_sep = :
endif
export PYTHONPATH := $(PYTHONPATH)$(path_sep)$(CURDIR)
PYSOLLIB_FILES=pysollib/tk/*.py pysollib/tile/*.py pysollib/*.py \
pysollib/games/*.py pysollib/games/special/*.py \
pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py \
pysollib/kivy/*.py
.PHONY : all install dist all_games_html rules pot mo
.PHONY: all install dist rpm all_games_html rules pot mo pretest test runtest
all:
@echo "No default target"
install:
python setup.py install
python3 setup.py install
dist: all_games_html rules mo
python3 setup.py sdist
rpm: all_games_html rules mo
python setup.py bdist_rpm
python3 setup.py bdist_rpm
all_games_html:
PYTHONPATH=`pwd` ./scripts/all_games.py html id doc/rules bare > docs/all_games.html
DOCS_DIR = docs
HTML_DIR = data/html
ALL_GAMES_HTML_BASE = all_games.html
ALL_GAMES_HTML = $(HTML_DIR)/$(ALL_GAMES_HTML_BASE)
ALL_GAMES_HTML__FOR_WEBSITE = $(DOCS_DIR)/$(ALL_GAMES_HTML_BASE)
all_games_html: $(ALL_GAMES_HTML)
$(ALL_GAMES_HTML) $(ALL_GAMES_HTML__FOR_WEBSITE): rules
cd $(HTML_DIR) && $(CURDIR)/scripts/all_games.py html id rules > $(ALL_GAMES_HTML_BASE)
./scripts/all_games.py html id doc/rules bare > $(ALL_GAMES_HTML__FOR_WEBSITE)
rules:
export PYTHONPATH=`pwd`; (cd html-src && ./gen-html.py)
cd html-src && ./gen-html.py
cp -r html-src/images html-src/html
rm -rf data/html
mv html-src/html data
pot:
PYTHONPATH=`pwd` ./scripts/all_games.py gettext > po/games.pot
PYTHONPATH=`pwd` ./scripts/pygettext.py -k n_ --ngettext-keyword ungettext -o po/pysol-1.pot $(PYSOLLIB_FILES)
xgettext -L C --keyword=N_ -o po/pysol-2.pot data/glade-translations
msgcat po/pysol-1.pot po/pysol-2.pot > po/pysol.pot
rm -f po/pysol-1.pot po/pysol-2.pot
for lng in ru pl; do \
mv -f po/$${lng}_pysol.po po/$${lng}_pysol.old.po; \
msgmerge po/$${lng}_pysol.old.po po/pysol.pot > po/$${lng}_pysol.po; \
rm -f po/$${lng}_pysol.old.po; \
mv -f po/$${lng}_games.po po/$${lng}_games.old.po; \
msgmerge po/$${lng}_games.old.po po/games.pot > po/$${lng}_games.po; \
rm -f po/$${lng}_games.old.po; \
./scripts/all_games.py gettext > po/games.pot
xgettext --keyword=n_ --add-comments=TRANSLATORS: -o po/pysol.pot \
pysollib/*.py pysollib/*/*.py pysollib/*/*/*.py data/pysolfc.glade
set -e; \
for lng in ru de pl it; do \
msgmerge --update --quiet --backup=none po/$${lng}_pysol.po po/pysol.pot; \
msgmerge --update --quiet --backup=none po/$${lng}_games.po po/games.pot; \
done
mo:
for loc in ru ru_RU de de_AT de_BE de_DE de_LU de_CH pl pl_PL it it_IT; do \
test -d locale/$${loc}/LC_MESSAGES || mkdir -p locale/$${loc}/LC_MESSAGES; \
done
for lang in ru pl it; do \
msgcat --use-first po/$${lang}_games.po po/$${lang}_pysol.po > po/$${lang}.po 2>/dev/null; \
done
set -e; \
for lang in ru de pl it; do \
msgfmt -o locale/$${lang}/LC_MESSAGES/pysol.mo po/$${lang}.po; \
mkdir -p locale/$${lang}/LC_MESSAGES; \
msgcat --use-first po/$${lang}_games.po po/$${lang}_pysol.po > po/$${lang}.po; \
msgfmt --check -o locale/$${lang}/LC_MESSAGES/pysol.mo po/$${lang}.po; \
done
cp -f locale/ru/LC_MESSAGES/pysol.mo locale/ru_RU/LC_MESSAGES/pysol.mo
for dir in de_AT de_BE de_DE de_LU de_CH; do \
cp -f locale/de/LC_MESSAGES/pysol.mo locale/$${dir}/LC_MESSAGES/pysol.mo; \
done
cp -f locale/pl/LC_MESSAGES/pysol.mo locale/pl_PL/LC_MESSAGES/pysol.mo
cp -f locale/it/LC_MESSAGES/pysol.mo locale/it_IT/LC_MESSAGES/pysol.mo
pretest:
@rm -f tests/individually-importing/*.py # To avoid stray files
rm -f tests/individually-importing/*.py tests/unit-generated/*.py # To avoid stray files
python3 scripts/gen_individual_importing_tests.py
TEST_ENV_PATH = "`pwd`:`pwd`/tests/lib"
TEST_ENV = PYTHONPATH="$$PYTHONPATH:"$(TEST_ENV_PATH) PERL5LIB="$$PERL5LIB:"$(TEST_ENV_PATH)
TEST_ENV_PATH = $(CURDIR)$(path_sep)$(CURDIR)/tests/lib
TEST_FILES = tests/style/*.t tests/unit-generated/*.py tests/individually-importing/*.py
define RUN_TESTS
$(TEST_ENV) $1 $(TEST_FILES)
endef
test runtest: export PYTHONPATH := $(PYTHONPATH)$(path_sep)$(TEST_ENV_PATH)
test runtest: export PERL5LIB := $(PERL5LIB)$(path_sep)$(TEST_ENV_PATH)
test: pretest
$(call RUN_TESTS,prove)
prove $(TEST_FILES)
runtest: pretest
$(call RUN_TESTS,runprove)
runprove $(TEST_FILES)

View file

@ -45,14 +45,15 @@ Prerequisites (needs root):
Build with 'python-for-android' (as user):
Use the cloned repo or an unpacked distribution tarball.
go to the android directory, then
$ ./mkp4a.init [<sdkdir>] [<ndkdir>] # prepare sdk and p4a installation
$ ./mkp4a.init # prepare sdk and p4a installation
$ ./mkkeystore # if you want to build a release version.
$ ./mkp4a.debug # build debug apk
$ ./mkp4a.release <passwd1> [<passwd2>] # build release apk
$ ./mkp4a.unsigned # build an unsigned release apk
The build system will download all required additional
packages (such as the android sdk and more). Do the first build will
@ -68,11 +69,22 @@ Build with 'python-for-android' (as user):
Cardsets:
Cardsets should be installed in ${HOME}/.PySolFC/cardsets/. On an
android device this is equivalent to /sdcard/.PySolFC/cardsets/.
Cardsets must use the bmp image format. Use scripts/cardsetsgiftobmp
The Apk includes a minimal set of cards for playing.
Additional cardsets can be installed in ${HOME}/.PySolFC/cardsets/.
On an android device this is equivalent to /sdcard/.PySolFC/cardsets/.
Cardsets must use the bmp image format. Use scripts/cardconv
(on a linux system) to convert them, before copying to the device.
Important Notice on python3/kivy 1.11.x (pysol verson 2.6.4 ff):
In kivy, using python3, the support for bmp images depends on OpenGL.
On fairly up to date Android OpenGL ES 3.x is available. ES (Embedded
System) release is a subset of the full OpenGL. Due to this, bmp image
support will be broken on android, but not on up to date workstations.
As gif images are still slow and error prone, the best we can do is to
use png images instead of bmp (as noted above).
Possible known build issues:
2) for android ndk: needs a Version <=13 (because needs ant support).

View file

@ -39,7 +39,7 @@ kivy version.
Additional cardsets are available from the SourceForge
project. To use them with kivy, they need to be converted to
the BMP format. A shell script, 'cardsetsgiftobmp' , has been added
the BMP format. A shell script, 'cardconv' , has been added
to the scripts directory (it requires Bash and ImageMagick).
For all GIF images in the directories data/images and data/tiles ,

View file

@ -96,13 +96,14 @@ per the instructions above.
At the moment, this only works on POSIX (Linux, FreeBSD and similar) systems.
Windows and Mac users - you'll need to chip in with a script for your system.
#### 1 - Install build prerequisites: six and random2
#### 1 - Install build prerequisites: six, random2 and pysol-cards
This is kind of stupid and maybe it can be fixed in the future, but for now:
```
pip install six
pip install random2
pip install pysol-cards
```
You may want to use your OS distribution package system instead, for example:
@ -112,6 +113,12 @@ sudo apt-get install python-six
sudo apt-get install python-random2
```
For Pillow compilation, libjpeg headers and libraries need to be available:
```
sudo apt-get install libjpeg-dev
```
#### 2 - Clone the source from version control
```
@ -122,10 +129,10 @@ cd PySolFC
#### 3 - Create your virtual environment.
```
PKGDIR=/usr/local/packages/PySolFC # or whatever
export PKGDIR
mkdir -p "$PKGDIR"
( cd "$PKGDIR" && python -m venv ./env )
PKGTREE=/usr/local/packages/PySolFC # or whatever
export PKGTREE
mkdir -p "$PKGTREE"
( cd "$PKGTREE" && python -m venv ./env )
```
#### 4 - Run the install script
@ -139,12 +146,11 @@ mkdir -p "$PKGDIR"
#### 6 - Enjoy playing
```
"$PKGDIR"/env/bin/pysol.py
"$PKGTREE"/env/bin/pysol.py
```
## Alternate toolkit.
- Python2 (2.7 or later)
- Kivy (10.0 or later)
- Features:
@ -154,7 +160,7 @@ mkdir -p "$PKGDIR"
- Running from source without installation:
```
python2 pysol.py --kivy
python pysol.py --kivy
```
### Configuring Freecell Solver

View file

@ -10,9 +10,6 @@ root$ ./apt-install.sh
root$ exit
user$ ./pip-install.sh
- .....some output.
Now all required packages are installed to proceed with the
android build.

View file

@ -1,12 +1,20 @@
#!/bin/bash
# als root ausführen!
#!/bin/sh
set -e
apt-get install -y mercurial git default-jdk
apt-get install -y cython cython3
apt-get install -y python3-pip
apt-get install -y python3-yaml
apt-get install -y virtualenv
apt-get install -y pkg-config
apt-get install -y automake autoconf libtool
apt-get install -y zlib1g-dev
apt-get install -y libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
# install as root
apt-get install -y \
git \
openjdk-8-jdk \
cython3 \
python3-pip \
python3-yaml \
virtualenv \
pkg-config \
automake autoconf libtool \
zlib1g-dev \
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
libtinfo5 \
lld
update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java

View file

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

View file

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

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python3
# ---------------------------------------------------------------------------
#
# PySol -- a Python Solitaire game

View file

@ -1,46 +1,27 @@
#!/bin/bash
set -eux
. mkp4a.common
echo '### prepare cardsets'
if [ ! -f ./PySolFC-Cardsets-2.0.tar.bz2 ]
then
echo '### downloading cardets'
# wget http://downloads.sourceforge.net/pysolfc/PySolFC-Cardsets-2.0.tar.bz2
wget https://netix.dl.sourceforge.net/project/pysolfc/PySolFC-Cardsets/PySolFC-Cardsets-2.0/PySolFC-Cardsets-2.0.tar.bz2
if [ ! -f ${cardsets_file} ]; then
echo '### downloading cardsets'
wget https://netix.dl.sourceforge.net/project/pysolfc/PySolFC-Cardsets/minimal/${cardsets_file}
fi
if [ -f ./PySolFC-Cardsets-2.0.tar.bz2 ]
then
if [ ! -d ./PySolFC-Cardsets-2.0 ]
then
echo '### extracting selected cardets'
tar -xjvf PySolFC-Cardsets-2.0.tar.bz2 \
PySolFC-Cardsets-2.0/cardset-crystal-mahjongg \
PySolFC-Cardsets-2.0/cardset-dashavatara-ganjifa \
PySolFC-Cardsets-2.0/cardset-dondorf \
PySolFC-Cardsets-2.0/cardset-hexadeck \
PySolFC-Cardsets-2.0/cardset-kintengu \
PySolFC-Cardsets-2.0/cardset-matrix \
PySolFC-Cardsets-2.0/cardset-mughal-ganjifa \
PySolFC-Cardsets-2.0/cardset-oxymoron \
PySolFC-Cardsets-2.0/cardset-standard \
PySolFC-Cardsets-2.0/cardset-vienna-2k \
PySolFC-Cardsets-2.0/cardset-greywyvern
fi
if [ -d ./PySolFC-Cardsets-2.0 ]
then
echo '### processing cardets'
cd PySolFC-Cardsets-2.0
../../scripts/cardsetsgiftobmp
for i in cardset-*-bmp
do
rm -rf `basename $i -bmp`
done
cd ..
fi
else
echo '### error downloading cardsets'
if [ ! -d ${cardsets_dir} ]; then
echo '### extracting cardsets'
tar -xf ${cardsets_file}
fi
echo '### processing cardsets'
(
cd ${cardsets_dir}
../../scripts/cardconv gif png
for i in cardset-*-png; do
rm -rf `basename $i -png`
done
)
echo '### end cardsets'

View file

@ -1,16 +1,12 @@
#!/bin/bash
set -eux
# memo:
# keytool -genkey -v -keystore my-release-keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 12000
if [ ! -d bin ]
then
echo "mkdir bin"
mkdir bin
fi
mkdir -p bin
if [ -f ./bin/keystore ]
then
if [ -f ./bin/keystore ]; then
echo "keystore is already defined"
else
keytool -genkey -v -keystore ./bin/keystore -alias python -keyalg RSA -keysize 2048 -validity 12000

View file

@ -1,8 +1,8 @@
#!/bin/bash
set -eux
rm -rf tmp
python3 -m pythonforandroid.toolchain clean_dists
python3 -m pythonforandroid.toolchain clean_builds
rm -f *.apk
rm -rf PySolFC-Cardsets-2.0
rm -rf PySolFC-Cardsets--Minimal-2.0.1

View file

@ -1,4 +1,5 @@
#!/bin/bash
set -e
rm -rf tmp
rm -f *.apk

33
android/mkp4a.common Normal file
View 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"

View file

@ -1,32 +1,11 @@
#!/bin/bash
set -eux
package='org.lufebe16.pysolfc'
version=`./version.py`
name='PySolFC'
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
. mkp4a.common
if [ "$1" ]
then
package=${package}.dbg
name=${name}dbg
fi
new_options=${p4a_options}
new_options=${new_options/PySolFC/PySolFCdbg}
new_options=${new_options/org.lufebe16.pysolfc/org.lufebe16.pysolfc.dbg}
python3 -m pythonforandroid.toolchain apk \
--sdk-dir ${HOME}/.cache/sdk-for-p4a \
--ndk-dir ${HOME}/.cache/sdk-for-p4a/ndk-bundle \
--android-api 19 \
--ndk-version r12b \
--arch armeabi-v7a \
--dist-name pysolfc \
--name ${name} \
--bootstrap=sdl2 \
--requirements kivy,hostpython2,random2 \
--minsdk 14 \
--private ${tmpdir} \
--package ${package} \
--version ${version} \
--orientation sensor \
--color=always \
--icon ${tmpdir}/data/images/misc/pysol01.png \
--presplash ${tmpdir}/data/images/misc/pysol06.png \
--copy-libs
${new_options}

View file

@ -1,56 +1,62 @@
#!/bin/bash
set -eux
. mkp4a.common
# NOTE: $1 will be set with fdroid builds only.
echo '### prepare sdk'
./initsdk.py $1 $2 $3
if [[ $# == 0 ]]
then
./initsupport
./initsdk
fi
echo '### install p4a'
p4adir=${HOME}/.cache/tmp-for-p4a
mkdir -p ${p4adir}
p4aversion='0.5.3'
if [ ! -f ${p4adir}/${p4aversion}.zip ]
if [[ $# == 0 ]]
then
wget "https://github.com/kivy/python-for-android/archive/${p4aversion}.zip"
cp -a ./${p4aversion}.zip ${p4adir}/${p4aversion}.zip
rm -f ./${p4aversion}.zip
fi
if [ -f ${p4adir}/${p4aversion}.zip ]
then
python3 -m pip install -q --user "${p4adir}/${p4aversion}.zip"
else
echo "### download of ${p4aversion}.zip failed"
python3 -m pip install -q --user python-for-android
fi
echo '### prepare source'
srcdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
(cd .. && make rules && make all_games_html && make mo)
mkdir -p ${srcdir}
rm -rf ${srcdir}
cp -a .. ${srcdir}
rm -rf ${srcdir}/android
rm -rf ${srcdir}/src
cp -a main.py ${srcdir}/main.py
mkdir -p ${srcdir}/data/images/cards/bottoms/trumps-only
echo "" > ${srcdir}/data/images/cards/bottoms/trumps-only/.keep
mkdir -p ${tmpdir}
rm -rf ${tmpdir}
cp -a .. ${tmpdir}
rm -rf ${tmpdir}/android
rm -rf ${tmpdir}/src
# remove useless load from the app
rm -rf ${tmpdir}/.git
rm -rf ${tmpdir}/.gitignore
rm -rf ${tmpdir}/Screenshots
rm -rf ${tmpdir}/build
rm -rf ${tmpdir}/contrib
rm -rf ${tmpdir}/dist
rm -rf ${tmpdir}/runtests.pl
rm -rf ${tmpdir}/tests
rm -rf ${tmpdir}/Brewfile
rm -rf ${tmpdir}/.appveyor.yml
rm -rf ${tmpdir}/.perltidyrc
rm -rf ${tmpdir}/.travis.yml
rm -rf ${tmpdir}/setup.py
rm -rf ${tmpdir}/setup_osx.py
rm -rf ${tmpdir}/setup.cfg
cp -a ${pkgdir}/supportlib/support-v4-24.1.1.aar ${tmpdir}
cp -a main.py ${tmpdir}
mkdir -p ${tmpdir}/data/images/cards/bottoms/trumps-only
echo "" > ${tmpdir}/data/images/cards/bottoms/trumps-only/.keep
echo '### prepare cardsets'
cardsdir=${HOME}/.cache/tmp-for-p4a/pysolfc
./mkcards
if [ ! -d ${cardsdir}/PySolFC-Cardsets-2.0 ]
then
./mkcards
mv PySolFC-Cardsets-2.0 ${cardsdir}/
rm -f PySolFC-Cardsets-2.0.tar.bz2
fi
if [ -d ${cardsdir}/PySolFC-Cardsets-2.0 ]
then
echo '### copying cardsets'
cp -a ${cardsdir}/PySolFC-Cardsets-2.0/* ${srcdir}/data
fi
cp -a ${cardsets_dir}/* ${tmpdir}/data
echo '### end init'

30
android/mkp4a.preload Executable file
View 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

View file

@ -1,11 +1,13 @@
#!/bin/bash
set -e
. mkp4a.common
pass1=""
pass2=""
keyalias="python"
keystore="${PWD}/bin/keystore"
if [ $1 ]
then
if [ $1 ]; then
pass1=$1
pass2=$1
else
@ -13,16 +15,13 @@ else
echo " (use ./mkkeystore to create one in default location)"
exit
fi
if [ $2 ]
then
if [ $2 ]; then
pass2=$2
fi
if [ $3 ]
then
if [ $3 ]; then
keyalias=$3
fi
if [ $4 ]
then
if [ $4 ]; then
keystore=$4
fi
@ -31,30 +30,10 @@ export P4A_RELEASE_KEYSTORE_PASSWD="$pass1"
export P4A_RELEASE_KEYALIAS_PASSWD="$pass2"
export P4A_RELEASE_KEYALIAS="$keyalias"
version=`./version.py`
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
python3 -m pythonforandroid.toolchain apk \
--sdk-dir ${HOME}/.cache/sdk-for-p4a \
--ndk-dir ${HOME}/.cache/sdk-for-p4a/ndk-bundle \
--android-api 19 \
--ndk-version r12b \
--arch armeabi-v7a \
--dist-name pysolfc \
--name PySolFC \
--bootstrap=sdl2 \
--requirements kivy,hostpython2,random2 \
${p4a_options} \
--release \
--sign \
--minsdk 14 \
--private ${tmpdir} \
--package org.lufebe16.pysolfc \
--version ${version} \
--orientation sensor \
--color=always \
--icon ${tmpdir}/data/images/misc/pysol01.png \
--presplash ${tmpdir}/data/images/misc/pysol06.png \
--copy-libs
--sign
# keystore options (instead environment vars):
#

View file

@ -1,47 +1,21 @@
#!/bin/bash
set -eux
echo '### p4a started'
. mkp4a.common
# sdk-dir and nkd-dir from command line (fdroid mode)
# NOTE: $1 and $2 (sdk and ndk) used with fdroid build only.
sdkdir="${HOME}/.cache/sdk-for-p4a"
ndkdir="${HOME}/.cache/sdk-for-p4a/ndk-bundle"
if [ $1 ]
if [[ $# == 2 ]]
then
echo "set sdk to: $1"
sdkdir="$1"
sdkdir=$1
ndkdir=$2
fi
if [ $2 ]
then
echo "set ndk to: $2"
ndkdir="$2"
fi
echo '### run toolchain'
version=`./version.py`
tmpdir=${HOME}/.cache/tmp-for-p4a/pysolfc/src
python3 -m pythonforandroid.toolchain apk \
${p4a_options} \
--sdk-dir ${sdkdir} \
--ndk-dir ${ndkdir} \
--android-api 19 \
--ndk-version r12b \
--arch armeabi-v7a \
--dist-name pysolfc \
--name PySolFC \
--bootstrap=sdl2 \
--requirements kivy,hostpython2,random2 \
--release \
--minsdk 14 \
--private ${tmpdir} \
--package org.lufebe16.pysolfc \
--version ${version} \
--orientation sensor \
--color=always \
--icon ${tmpdir}/data/images/misc/pysol01.png \
--presplash ${tmpdir}/data/images/misc/pysol06.png \
--copy-libs
--release
# python3 -m pythonforandroid.toolchain apk
# ...
@ -53,5 +27,3 @@ python3 -m pythonforandroid.toolchain apk \
# ohne: -> debug version
# 1: -> release unsigned
# 1 und 2: -> release version.
echo '### p4a finished'

View file

@ -1,7 +1,8 @@
#! /bin/sh -Cefu
#!/bin/bash
set -Cefu
: ${PKGTREE:=/usr/local/packages/PySolFC}
PIP="$(printf '%s/env/bin/pip install --no-binary :all: ' "$PKGTREE")"
PIP=("${PKGTREE}/env/bin/pip" install --no-binary :all:)
PYPROG="$(printf '%s/env/bin/python' "$PKGTREE")"
VERSION="$(env PYTHONPATH=`pwd` "$PYPROG" -c 'from pysollib.settings import VERSION ; print(VERSION)')"
XZBALL="$(printf 'dist/PySolFC-%s.tar.xz' "$VERSION")"
@ -10,6 +11,6 @@ reqs='six random2 pillow'
make dist
for req in $reqs
do
"$PIP" "$req"
"${PIP[@]}" "$req"
done
"$PIP" --upgrade "$XZBALL"
"${PIP[@]}" --upgrade "$XZBALL"

View file

@ -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");

View file

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View file

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -1,4 +1,6 @@
#!/bin/bash
set -e
# convert gif to png - dir recursive
# scharf !!!!

View file

@ -1,8 +1,10 @@
[Desktop Entry]
Encoding=UTF-8
Name=PySol Fan Club Edition
Comment=More than 1000 solitaire card games
Exec=pysol.py
StartupWMClass=Pysol
Terminal=false
Type=Application
Categories=Game;CardGame;
Icon=/usr/share/icons/pysol01.png
Keywords=solitaire;patience;cards;pysolfc;klondike;
Icon=pysol

View file

@ -1,4 +1,5 @@
#!/bin/sh
set -e
from_dir=images
to_dir=clearlooks

View file

@ -69,9 +69,9 @@ the current directory
.br
/usr/local/share/PySolFC
.br
/usr/games/PySolFC
/usr/share/games/PySolFC
.br
/usr/local/games/PySolFC
/usr/local/share/games/PySolFC
.PP
Options are saved in \fB~/.PySolFC/options.cfg\fR
.PP

View file

@ -6,8 +6,11 @@ traditionally been individually hand painted. There are any where from eight
to twelve or more suits per deck, each suit having usually twelve ranks. The
two most common Ganjifa decks are the Mughal which has eight suits and the
Dashavatara which has ten. The suits have pip cards numbered from Ace through
ten and two court cards, the Wazir and the Mir. Ganjifa solitaire games play
the same as games that use the standard deck but the larger number of different
cards in a deck (96 or 120) adds an element of complexity. The fact that each
suit has it's own color makes things quite interesting in games that use
"Alternate Color" row stacks.
ten and two court cards, the Wazir and the Mir. The order of the suits
is: Matsya, Kuchha, Varaha, Narsingha, Vamana, Parashurama, Rama,
Krishna, Buddha, and Kalanki. Ganjifa solitaire games play the same
as games that use the standard deck but the larger number of different
cards in a deck (96 or 120) adds an element of complexity. The fact
that each suit has it's own color makes things quite interesting in
games that use "Alternate Color" row stacks.
<img alt="" src="images/ganjifa.gif">

BIN
html-src/images/ganjifa.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -10,7 +10,7 @@ Move all cards to the foundations.
<p>
This is a Freecell type of game. Cards on the tableau build down in rank
by suit. Cards build up in rank on the foundations.
A stack can be moved if the cards are in decending rank order
A stack can be moved if the cards are in descending rank order
regardless of the suit. Only a King or the highest trump can be played
on an empty row.

File diff suppressed because it is too large Load diff

4989
po/de_pysol.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
"POT-Creation-Date: Mon Mar 7 21:38:07 2011\n"
"POT-Creation-Date: Wed Sep 25 11:43:34 2019\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -702,6 +702,9 @@ msgstr ""
msgid "Czarina"
msgstr ""
msgid "Daddy Longlegs"
msgstr ""
msgid "Danda"
msgstr ""
@ -1272,6 +1275,12 @@ msgstr ""
msgid "FreeCell"
msgstr ""
msgid "FreeCell with Two Reserves"
msgstr ""
msgid "FreeCell with Zero Reserves"
msgstr ""
msgid "Frog"
msgstr ""

View file

@ -5,14 +5,16 @@
msgid ""
msgstr ""
"Project-Id-Version: it_games\n"
"POT-Creation-Date: Thu Sep 6 15:06:46 2007\n"
"POT-Creation-Date: Thu Jul 18 18:43:59 2019\n"
"PO-Revision-Date: 2011-05-12 18:46+0200\n"
"Last-Translator: Giuliano Colla <giuliano.colla@gmail.com>\n"
"Language-Team: Italiano <it@li.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: ./scripts/all_games.py 0.1\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.11.4\n"
msgid " 3x3 Matrix"
@ -75,6 +77,9 @@ msgstr "Conoscenti"
msgid "Adela"
msgstr "Adelina"
msgid "Aglet"
msgstr ""
msgid "Agnes Bernauer"
msgstr "Agnese Bernauer"
@ -243,6 +248,12 @@ msgstr ""
msgid "Bavarian Patience"
msgstr ""
msgid "Bayan"
msgstr ""
msgid "Beacon"
msgstr ""
msgid "Beak and Flipper"
msgstr ""
@ -522,6 +533,9 @@ msgstr ""
msgid "Cat's Tail"
msgstr ""
msgid "Catherine the Great"
msgstr ""
msgid "Cavalier"
msgstr ""
@ -690,6 +704,9 @@ msgstr ""
msgid "Czarina"
msgstr ""
msgid "Daddy Longlegs"
msgstr ""
msgid "Danda"
msgstr ""
@ -804,6 +821,9 @@ msgstr ""
msgid "Double Bisley"
msgstr ""
msgid "Double Blue Moon"
msgstr ""
msgid "Double Canfield"
msgstr ""
@ -876,12 +896,18 @@ msgstr ""
msgid "Double Measure"
msgstr ""
msgid "Double Montana"
msgstr ""
msgid "Double Pyramid"
msgstr ""
msgid "Double Rail"
msgstr ""
msgid "Double Red Moon"
msgstr ""
msgid "Double Russian Solitaire"
msgstr ""
@ -942,6 +968,9 @@ msgstr ""
msgid "Dutch Solitaire"
msgstr ""
msgid "Dutchess"
msgstr ""
msgid "Eagle Wing"
msgstr ""
@ -972,6 +1001,9 @@ msgstr ""
msgid "Eight Off"
msgstr ""
msgid "Eight Packs"
msgstr ""
msgid "Eight Sages"
msgstr ""
@ -1158,6 +1190,9 @@ msgstr ""
msgid "Flying Dragon"
msgstr ""
msgid "Foothold"
msgstr ""
msgid "Footling"
msgstr ""
@ -1242,6 +1277,12 @@ msgstr ""
msgid "FreeCell"
msgstr ""
msgid "FreeCell with Two Reserves"
msgstr ""
msgid "FreeCell with Zero Reserves"
msgstr ""
msgid "Frog"
msgstr ""
@ -1425,6 +1466,9 @@ msgstr ""
msgid "Hanoi Puzzle 6"
msgstr ""
msgid "Hanoi Sequence"
msgstr ""
msgid "Happy New Year"
msgstr ""
@ -1638,6 +1682,9 @@ msgstr ""
msgid "Katrina's Game Relaxed"
msgstr ""
msgid "Kentish"
msgstr ""
msgid "Khadga"
msgstr ""
@ -2310,7 +2357,7 @@ msgstr ""
msgid "Mahjongg Stargate"
msgstr ""
msgid "Mahjongg Step Pyramid"
msgid "Mahjongg Steps Pyramid"
msgstr ""
msgid "Mahjongg Stonehenge"
@ -2652,6 +2699,15 @@ msgstr ""
msgid "Northwest Territory"
msgstr ""
msgid "Not Shisen-Sho 14x6"
msgstr ""
msgid "Not Shisen-Sho 18x8"
msgstr ""
msgid "Not Shisen-Sho 24x12"
msgstr ""
msgid "Number Ten"
msgstr ""
@ -2904,6 +2960,9 @@ msgstr ""
msgid "Puss in the Corner"
msgstr ""
msgid "Putt Putt"
msgstr ""
msgid "Pyramid"
msgstr ""
@ -2925,12 +2984,18 @@ msgstr ""
msgid "Quadrangle"
msgstr ""
msgid "Quadrille"
msgstr ""
msgid "Quadruple Alliance"
msgstr ""
msgid "Quads"
msgstr ""
msgid "Quads +"
msgstr ""
msgid "Quartets"
msgstr ""
@ -3000,6 +3065,10 @@ msgstr ""
msgid "Relax"
msgstr ""
#, fuzzy
msgid "Relaxed Accordion"
msgstr "Fisarmonica"
msgid "Relaxed FreeCell"
msgstr ""
@ -3015,6 +3084,9 @@ msgstr ""
msgid "Relaxed Spider"
msgstr ""
msgid "Relaxed Three Fir-trees"
msgstr ""
msgid "Repair"
msgstr ""
@ -3228,13 +3300,13 @@ msgstr ""
msgid "Shifting"
msgstr ""
msgid "Shisen-Sho (No Gra) 14x6"
msgid "Shisen-Sho (No Gravity) 14x6"
msgstr ""
msgid "Shisen-Sho (No Gra) 18x8"
msgid "Shisen-Sho (No Gravity) 18x8"
msgstr ""
msgid "Shisen-Sho (No Gra) 24x12"
msgid "Shisen-Sho (No Gravity) 24x12"
msgstr ""
msgid "Shisen-Sho 14x6"
@ -3423,15 +3495,15 @@ msgstr ""
msgid "Stargate"
msgstr ""
msgid "Step Pyramid"
msgstr ""
msgid "Step-Up"
msgstr ""
msgid "Steps"
msgstr ""
msgid "Steps Pyramid"
msgstr ""
msgid "Steve"
msgstr ""
@ -3597,6 +3669,9 @@ msgstr ""
msgid "Thieves of Egypt"
msgstr ""
msgid "Thirteen Packs"
msgstr ""
msgid "Thirteen Up"
msgstr ""
@ -3864,6 +3939,9 @@ msgstr ""
msgid "Waning Moon"
msgstr ""
msgid "Wasatch"
msgstr ""
msgid "Washington's Favorite"
msgstr ""
@ -3968,4 +4046,3 @@ msgstr ""
msgid "Zodiac"
msgstr ""

File diff suppressed because it is too large Load diff

View file

@ -4,10 +4,11 @@
msgid ""
msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
"POT-Creation-Date: Mon Mar 7 21:38:07 2011\n"
"POT-Creation-Date: Thu Jul 18 18:43:59 2019\n"
"PO-Revision-Date: 2010-12-16 23:56+0100\n"
"Last-Translator: Jerzy Trzeciak <artusek@wp.pl>\n"
"Language-Team: Polish <pl@li.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -705,6 +706,9 @@ msgstr ""
msgid "Czarina"
msgstr "Czarina"
msgid "Daddy Longlegs"
msgstr ""
msgid "Danda"
msgstr "Danda"
@ -1277,6 +1281,12 @@ msgstr "Free Napoleon"
msgid "FreeCell"
msgstr "FreeCell"
msgid "FreeCell with Two Reserves"
msgstr ""
msgid "FreeCell with Zero Reserves"
msgstr ""
msgid "Frog"
msgstr "Żaba"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,14 +5,17 @@
msgid ""
msgstr ""
"Project-Id-Version: PySol 0.0.1\n"
"POT-Creation-Date: Mon Mar 7 21:38:07 2011\n"
"POT-Creation-Date: Thu Jul 18 18:43:59 2019\n"
"PO-Revision-Date: 2007-09-05 17:43+0400\n"
"Last-Translator: Скоморох <skomoroh@gmail.com>\n"
"Language-Team: Russian <ru@li.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: utf-8\n"
"Generated-By: ./scripts/all_games.py 0.1\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid " 3x3 Matrix"
msgstr "Матрица 3x3"
@ -707,6 +710,10 @@ msgstr "Творог и сыворотка"
msgid "Czarina"
msgstr "Царевна"
#, fuzzy
msgid "Daddy Longlegs"
msgstr "Тенистые аллеи"
#, fuzzy
msgid "Danda"
msgstr "Алмаз"
@ -1289,6 +1296,12 @@ msgstr "Свободный Наполеон"
msgid "FreeCell"
msgstr "Свободная ячейка"
msgid "FreeCell with Two Reserves"
msgstr ""
msgid "FreeCell with Zero Reserves"
msgstr ""
msgid "Frog"
msgstr "Лягушка"

File diff suppressed because it is too large Load diff

View file

@ -47,7 +47,7 @@ from pysollib.pysoltk import create_find_card_dialog
from pysollib.pysoltk import create_solver_dialog
from pysollib.settings import DEBUG
from pysollib.settings import PACKAGE_URL, TITLE
from pysollib.settings import TOP_TITLE
from pysollib.settings import TOP_SIZE
from pysollib.stats import FileStatsFormatter
@ -381,7 +381,7 @@ class PysolMenubar(PysolMenubarTk):
if self._cancelDrag():
return
if self.changed():
if not self.game.areYouSure(_("Quit ") + TITLE):
if not self.game.areYouSure(_("Quit %s") % TITLE):
return
self.game.endGame()
self.game.quitGame()
@ -445,7 +445,7 @@ class PysolMenubar(PysolMenubarTk):
if not self.game.gsaveinfo.bookmarks:
return
if not self.game.areYouSure(_("Clear bookmarks"),
_("Clear all bookmarks ?")):
_("Clear all bookmarks?")):
return
self.game.gsaveinfo.bookmarks = {}
self.game.updateMenus()
@ -457,7 +457,7 @@ class PysolMenubar(PysolMenubarTk):
return
if self.changed(restart=1):
if not self.game.areYouSure(_("Restart game"),
_("Restart this game ?")):
_("Restart this game?")):
return
self.game.restartGame()
@ -501,10 +501,11 @@ class PysolMenubar(PysolMenubarTk):
if self._cancelDrag(break_pause=False):
return
game, gi = self.game, self.game.gameinfo
t = " " + game.getGameNumber(format=1)
cc = _("Comments for %s:\n\n") % (gi.name + t)
kw = {'game': gi.name,
'id': game.getGameNumber(format=1)}
cc = _("Comments for %(game)s %(id)s:\n\n") % kw
c = game.gsaveinfo.comment or cc
d = EditTextDialog(game.top, _("Comments for ")+t, text=c)
d = EditTextDialog(game.top, _("Comments for %(id)s") % kw, text=c)
if d.status == 0 and d.button == 0:
text = d.text
if text.strip() == cc.strip():
@ -525,8 +526,9 @@ class PysolMenubar(PysolMenubarTk):
text=_("Error while writing to file"))
else:
d = MfxMessageDialog(
self.top, title=TITLE+_(" Info"), bitmap="info",
text=_("Comments were appended to\n\n") + fn)
self.top, title=_("%s Info") % TITLE, bitmap="info",
text=_("Comments were appended to\n\n%(filename)s")
% {'filename': fn})
self._setCommentMenu(bool(game.gsaveinfo.comment))
#
@ -535,10 +537,10 @@ class PysolMenubar(PysolMenubarTk):
def _mStatsSave(self, player, filename, write_method):
if player is None:
text = _("Demo statistics")
text = _("Demo statistics were appended to\n\n%(filename)s")
filename = filename + "_demo"
else:
text = _("Your statistics")
text = _("Your statistics were appended to\n\n%(filename)s")
filename = os.path.join(self.app.dn.config, filename + ".txt")
filename = os.path.normpath(filename)
try:
@ -549,8 +551,8 @@ class PysolMenubar(PysolMenubarTk):
text=_("Error while writing to file"))
else:
MfxMessageDialog(
self.top, title=TITLE+_(" Info"), bitmap="info",
text=text + _(" were appended to\n\n") + filename)
self.top, title=_("%s Info") % TITLE, bitmap="info",
text=text % {'filename': filename})
def mPlayerStats(self, *args, **kw):
mode = kw.get("mode", 101)
@ -564,31 +566,40 @@ class PysolMenubar(PysolMenubarTk):
d = Struct(status=-1, button=-1)
if demo:
player = None
p0, p1, p2 = TITLE+_(" Demo"), TITLE+_(" Demo "), ""
else:
player = self.app.opt.player
p0, p1, p2 = player, "", _(" for ") + player
n = self.game.gameinfo.name
# translation keywords
transkw = {'app': TITLE,
'player': player,
'game': n,
'tops': TOP_SIZE}
#
if mode == 100:
d = Status_StatsDialog(self.top, game=self.game)
elif mode == 101:
header = p1 + _("Statistics for ") + n
header = (_("%(app)s Demo Statistics for %(game)s") if demo
else _("Statistics for %(game)s")) % transkw
d = SingleGame_StatsDialog(
self.top, header, self.app, player, gameid=self.game.id)
gameid = d.selected_game
elif mode == 102:
header = p1 + _("Statistics") + p2
header = (_("%(app)s Demo Statistics") if demo
else _("Statistics for %(player)s")) % transkw
d = AllGames_StatsDialog(self.top, header, self.app, player)
gameid = d.selected_game
elif mode == 103:
header = p1 + _("Full log") + p2
header = (_("%(app)s Demo Full log") if demo
else _("Full log for %(player)s")) % transkw
d = FullLog_StatsDialog(self.top, header, self.app, player)
elif mode == 104:
header = p1 + _("Session log") + p2
header = (_("%(app)s Demo Session log") if demo
else _("Session log for %(player)s")) % transkw
d = SessionLog_StatsDialog(self.top, header, self.app, player)
elif mode == 105:
header = p1 + TOP_TITLE + _(" for ") + n
# TRANSLATORS: eg. top 10 or top 5 results for a certain game
header = (_("%(app)s Demo Top %(tops)d for %(game)s") if demo
else _("Top %(tops)d for %(game)s")) % transkw
d = Top_StatsDialog(
self.top, header, self.app, player, gameid=self.game.id)
elif mode == 106:
@ -614,7 +625,8 @@ class PysolMenubar(PysolMenubarTk):
# reset all player stats
if self.game.areYouSure(
_("Reset all statistics"),
_("Reset ALL statistics and logs for player\n%s ?") % p0,
_("Reset ALL statistics and logs for player\n" +
"%(player)s?") % transkw,
confirm=1, default=1
):
self.app.stats.resetStats(player, 0)
@ -624,8 +636,8 @@ class PysolMenubar(PysolMenubarTk):
# reset player stats for current game
if self.game.areYouSure(
_("Reset game statistics"),
_('Reset statistics and logs ' +
'for player\n%s\nand game\n%s ?') % (p0, n),
_('Reset statistics and logs for player\n%(player)s\n'
'and game\n%(game)s?') % transkw,
confirm=1, default=1
):
self.app.stats.resetStats(player, self.game.id)

View file

@ -60,7 +60,7 @@ from pysollib.resource import Tile, TileManager
from pysollib.settings import DEBUG
from pysollib.settings import PACKAGE, VERSION_TUPLE, WIN_SYSTEM
from pysollib.settings import TOOLKIT
from pysollib.util import CARDSET, IMAGE_EXTENSIONS
from pysollib.util import IMAGE_EXTENSIONS
from pysollib.winsystems import TkSettings
if TOOLKIT == 'tk':
from pysollib.ui.tktile.solverdialog import destroy_solver_dialog
@ -329,7 +329,8 @@ class Application:
elif self.commandline.game is not None:
gameid = self.gdb.getGameByName(self.commandline.game)
if gameid is None:
print_err(_("can't find game: ") + self.commandline.game)
print_err(_("can't find game: %(game)s") % {
'game': self.commandline.game})
sys.exit(-1)
else:
self.nextgame.id = gameid
@ -668,7 +669,7 @@ class Application:
if progress is None:
self.wm_save_state()
self.wm_withdraw()
title = _("Loading %s %s...") % (CARDSET, cs.name)
title = _("Loading cardset %s...") % cs.name
color = self.opt.colors['table']
if self.tabletile_index > 0:
color = "#008200"
@ -678,7 +679,7 @@ class Application:
images = Images(self.dataloader, cs)
try:
if not images.load(app=self, progress=progress):
raise Exception("Invalid or damaged "+CARDSET)
raise Exception("Invalid or damaged cardset")
simages = SubsampledImages(images)
if self.opt.save_cardsets:
c = self.cardsets_cache.get(cs.type)
@ -710,8 +711,8 @@ class Application:
# images.destruct()
destruct(images)
MfxExceptionDialog(
self.top, ex, title=CARDSET+_(" load error"),
text=_("Error while loading ")+CARDSET)
self.top, ex, title=_("Cardset load error"),
text=_("Error while loading cardset"))
self.intro.progress = progress
if r and self.menubar is not None:
self.menubar.updateBackgroundImagesMenu()
@ -806,14 +807,14 @@ class Application:
#
t = self.checkCompatibleCardsetType(gi, self.cardset)
MfxMessageDialog(
self.top, title=_("Incompatible ")+CARDSET,
self.top, title=_("Incompatible cardset"),
bitmap="warning",
text=_('''The currently selected %s %s
text=_('''The currently selected cardset %(cardset)s
is not compatible with the game
%s
%(game)s
Please select a %s type %s.
''') % (CARDSET, self.cardset.name, gi.name, t[0], CARDSET),
Please select a %(correct_type)s type cardset.
''') % {'cardset': self.cardset.name, 'game': gi.name, 'correct_type': t[0]},
strings=(_("&OK"),), default=0)
cs = self.__selectCardsetDialog(t)
if cs is None:
@ -852,7 +853,7 @@ Please select a %s type %s.
def __selectCardsetDialog(self, t):
cs = self.selectCardset(
_("Please select a %s type %s") % (t[0], CARDSET),
_("Please select a %s type cardset") % t[0],
self.cardset.index)
return cs
@ -1060,7 +1061,8 @@ Please select a %s type %s.
except Exception as ex:
if DEBUG:
traceback.print_exc()
print_err(_("error loading plugin %s: %s") % (n, ex))
print_err(_("error loading plugin %(file)s: %(err)s") %
{'file': n, 'err': ex})
#
# init cardsets
@ -1211,11 +1213,10 @@ Please select a %s type %s.
d = os.path.join(dirname, name)
if not os.path.isdir(d):
continue
f1 = os.path.join(d, "config.txt")
f2 = os.path.join(d, "COPYRIGHT")
if os.path.isfile(f1) and os.path.isfile(f2):
f = os.path.join(d, "config.txt")
if os.path.isfile(f):
try:
cs = self._readCardsetConfig(d, f1)
cs = self._readCardsetConfig(d, f)
if cs:
# from pprint import pprint
# print cs.name
@ -1232,7 +1233,7 @@ Please select a %s type %s.
fnames[cs.name] = 1
else:
print_err('fail _readCardsetConfig: %s %s'
% (d, f1))
% (d, f))
pass
except Exception:
# traceback.print_exc()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

55
pysollib/game/dump.py Normal file
View 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")

View file

@ -27,6 +27,7 @@ from . import bakersdozen # noqa: F401
from . import bakersgame # noqa: F401
from . import beleagueredcastle # noqa: F401
from . import bisley # noqa: F401
from . import bisley13 # noqa: F401
from . import braid # noqa: F401
from . import bristol # noqa: F401
from . import buffalobill # noqa: F401

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

View file

@ -120,7 +120,8 @@ class Matriarchy_Talon(WasteTalonStack):
return
WasteTalonStack.updateText(self, update_rounds=0)
# t = "Round %d" % self.round
t = _("Round %d/%d") % (self.round, self.max_rounds)
t = _("Round %(round)d/%(max_rounds)d") % {
'round': self.round, 'max_rounds': self.max_rounds}
self.texts.rounds.config(text=t)
t = _("Deal %d") % self.DEAL[self.round-1]
self.texts.misc.config(text=t)

View file

@ -286,6 +286,9 @@ class Hanafuda_SequenceStack(Flower_OpenStack):
return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
return self.isHanafudaSequence([stackcards[-1], cards[0]])
def canMoveCards(self, cards):
return self.basicCanMoveCards(cards) and self.isHanafudaSequence(cards)
class Oonsoo_SequenceStack(Flower_OpenStack):
@ -298,6 +301,10 @@ class Oonsoo_SequenceStack(Flower_OpenStack):
return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
return self.isHanafudaSequence([stackcards[-1], cards[0]], 0)
def canMoveCards(self, cards):
return (self.basicCanMoveCards(cards) and
self.isHanafudaSequence(cards, 0))
class FlowerClock_RowStack(Flower_OpenStack):

View file

@ -40,17 +40,18 @@ from pysollib.settings import PACKAGE_URL, TITLE, TOOLKIT, VERSION
def help_about(app, timeout=0, sound=True):
if sound:
app.audio.playSample("about")
t = _("A Python Solitaire Game Collection\n")
t = _("A Python Solitaire Game Collection")
if app.miscrandom.random() < 0.8:
t = _("A World Domination Project\n")
t = _("A World Domination Project")
strings = (_("&Nice"), _("&Credits..."))
if timeout:
strings = (_("&Enjoy"),)
version = _("Version %s") % VERSION
d = PysolAboutDialog(app, app.top, title=_("About ") + TITLE,
d = PysolAboutDialog(app, app.top, title=_("About %s") % TITLE,
timeout=timeout,
text=_('''PySol Fan Club edition
%s%s
%(description)s
%(versioninfo)s
Copyright (C) 1998 - 2003 Markus F.X.J. Oberhumer.
Copyright (C) 2003 Mt. Hood Playing Card Co.
@ -60,7 +61,8 @@ All Rights Reserved.
PySol is free software distributed under the terms
of the GNU General Public License.
For more information about this application visit''') % (t, version),
For more information about this application visit''') %
{'description': t, 'versioninfo': version},
url=PACKAGE_URL,
image=app.gimages.logos[2],
strings=strings, default=0,
@ -86,7 +88,7 @@ def help_credits(app, timeout=0, sound=True):
t = "kivy"
d = MfxMessageDialog(
app.top, title=_("Credits"), timeout=timeout,
text=TITLE+_(''' credits go to:
text=_('''%(app)s credits go to:
Volker Weidner for getting me into Solitaire
Guido van Rossum for the initial example program
@ -95,8 +97,8 @@ Carl Larsson for the background music
The Gnome AisleRiot team for parts of the documentation
Natascha
The Python, %s, SDL & Linux crews
for making this program possible''') % t,
The Python, %(gui_library)s, SDL & Linux crews
for making this program possible''') % {'app': TITLE, 'gui_library': t},
image=app.gimages.logos[3], image_side="right",
separator=True)
return d.status
@ -122,8 +124,8 @@ def help_html(app, document, dir_, top=None):
document, dir_ = "index.html", "html"
help_html_index = app.dataloader.findFile(document, dir_)
except EnvironmentError:
MfxMessageDialog(app.top, title=TITLE + _(" HTML Problem"),
text=_("Cannot find help document\n") + document,
MfxMessageDialog(app.top, title=_("%s HTML Problem") % TITLE,
text=_("Cannot find help document\n%s") % document,
bitmap="warning")
return None
# print doc, help_html_index
@ -136,7 +138,7 @@ def help_html(app, document, dir_, top=None):
viewer.display(doc, relpath=0)
except Exception:
# traceback.print_exc()
top = make_help_toplevel(app, title=TITLE+_(" Help"))
top = make_help_toplevel(app, title=_("%s Help") % TITLE)
if top.winfo_screenwidth() < 800 or top.winfo_screenheight() < 600:
# maximized = 1
top.wm_minsize(300, 150)

View file

@ -27,6 +27,11 @@ import os
import subprocess
import sys
try:
import jnius
except ImportError:
jnius = None
import pysollib.settings
# ************************************************************************
@ -36,27 +41,31 @@ import pysollib.settings
def init():
if os.name == 'nt' and 'LANG' not in os.environ:
try:
loc = locale.getdefaultlocale()
os.environ['LANG'] = loc[0]
except Exception:
pass
# locale.setlocale(locale.LC_ALL, '')
if 'LANG' not in os.environ:
if os.name == 'nt':
lang, enc = locale.getdefaultlocale()
os.environ['LANG'] = lang
elif jnius: # android
Locale = jnius.autoclass('java.util.Locale')
os.environ['LANG'] = Locale.getDefault().getLanguage()
locale.setlocale(locale.LC_ALL, '')
# install gettext
# locale_dir = 'locale'
locale_dir = None
if os.path.isdir(sys.path[0]):
d = os.path.join(sys.path[0], 'locale')
else:
# i.e. library.zip
d = os.path.join(os.path.dirname(sys.path[0]), 'locale')
if os.path.exists(d) and os.path.isdir(d):
locale_dir = d
# if locale_dir: locale_dir = os.path.normpath(locale_dir)
# gettext.install('pysol', locale_dir, unicode=True) # ngettext don't work
gettext.bindtextdomain('pysol', locale_dir)
locale_locations = (
# locale/ next to the pysol.py script
sys.path[0],
# locale/ next to library.zip (py2exe)
os.path.dirname(sys.path[0]),
# locale/ in curdir (works for e.g. py2app)
os.curdir)
# leaving the domain unbound means sys.prefix+'/share/locale'
for par in locale_locations:
locale_dir = os.path.join(par, 'locale')
if os.path.isdir(locale_dir):
gettext.bindtextdomain('pysol', locale_dir)
break
gettext.textdomain('pysol')
# debug

View file

@ -52,6 +52,8 @@ from kivy.uix.treeview import TreeViewLabel
from kivy.uix.widget import Widget
from kivy.utils import platform
from pysollib.kivy.androidperms import requestStoragePerm
# =============================================================================
@ -671,7 +673,10 @@ class LRectangle(Widget, LBase):
event = LEvent()
event.x = ppos[0]
event.y = ppos[1]
self.group.bindings['<1>'](event)
if touch.is_double_tap:
self.group.bindings['<Double-1>'](event)
else:
self.group.bindings['<1>'](event)
return True
return False
@ -712,6 +717,10 @@ class LImageItem(BoxLayout, LBase):
if self.group and '<1>' in self.group.bindings:
self.group.bindings['<1>'](event)
def send_event_pressed_double_1(self, event):
if self.group and '<Double-1>' in self.group.bindings:
self.group.bindings['<Double-1>'](event)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
@ -734,7 +743,10 @@ class LImageItem(BoxLayout, LBase):
event.y = ppos[1]
self.dragstart = touch.pos
event.cardid = i
self.send_event_pressed_1(event)
if touch.is_double_tap:
self.send_event_pressed_double_1(event)
else:
self.send_event_pressed_1(event)
return True
if self.group is not None:
@ -1168,7 +1180,7 @@ class LMenu(ActionView, LBase):
class MyActionPrev(ActionPrevious, LBase):
pass
kw['app_icon'] = 'data/images/misc/pysol01.png'
kw['app_icon'] = 'data/images/icons/48x48/pysol.png'
kw['with_previous'] = prev
kw['size_hint'] = (.01, 1)
self.ap = MyActionPrev(**kw)
@ -1541,6 +1553,12 @@ class LMainWindow(BoxLayout, LTkBase):
self.workStack = LStack()
self.app = None
'''
from kivy.graphics import opengl_utils
print('OPENGL support:')
print(opengl_utils.gl_get_extensions())
'''
# self.touches = []
# beispiel zu canvas (hintergrund)
@ -1779,6 +1797,8 @@ class LApp(App):
self.mainloop = self.app.mainproc() # Einrichten
self.mainloop.send(None) # Spielprozess starten
logging.info("LApp: on_start processed")
# Android: Request missing android permissions.
requestStoragePerm()
def on_stop(self):
# Achtung wird u.U. 2 mal aufgerufen !!!
@ -1832,12 +1852,6 @@ class LApp(App):
except Exception:
traceback.print_exc()
pass
# save comments
try:
app.saveComments()
except Exception:
traceback.print_exc()
pass
logging.info("LApp: on_pause - gamesaved")
logging.info("LApp: on_pause, Window.size=%s" % str(Window.size))

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

View file

@ -48,10 +48,10 @@ from pysollib.kivy.tkutil import after_idle
from pysollib.kivy.tkutil import bind
from pysollib.mfxutil import Struct
from pysollib.mygettext import _
from pysollib.pysoltk import MfxMessageDialog
from pysollib.pysoltk import connect_game_find_card_dialog
from pysollib.settings import SELECT_GAME_MENU
from pysollib.settings import TITLE
from pysollib.util import CARDSET
# ************************************************************************
@ -177,34 +177,34 @@ class MainMenuDialog(LMenuDialog):
def buildTree(self, tv, node):
rg = tv.add_node(
LTreeNode(
text="File",
text=_("File"),
command=self.make_game_command(self.menubar.mFileMenuDialog)))
rg = tv.add_node(
LTreeNode(
text="Games",
text=_("Games"),
command=self.make_game_command(
self.menubar.mSelectGameDialog)))
rg = tv.add_node(
LTreeNode(
text="Tools",
text=_("Tools"),
command=self.make_game_command(self.menubar.mEditMenuDialog)))
rg = tv.add_node(
LTreeNode(
text="Statistics",
text=_("Statistics"),
command=self.make_game_command(self.menubar.mGameMenuDialog)))
rg = tv.add_node(
LTreeNode(
text="Assist",
text=_("Assist"),
command=self.make_game_command(
self.menubar.mAssistMenuDialog)))
rg = tv.add_node(
LTreeNode(
text="Options",
text=_("Options"),
command=self.make_game_command(
self.menubar.mOptionsMenuDialog)))
rg = tv.add_node(
LTreeNode(
text="Help",
text=_("Help"),
command=self.make_game_command(self.menubar.mHelpMenuDialog)))
del rg
@ -225,7 +225,7 @@ class FileMenuDialog(LMenuDialog):
def buildTree(self, tv, node):
rg = tv.add_node(
LTreeNode(text='Recent games'))
LTreeNode(text=_('Recent games')))
# Recent Liste
recids = self.app.opt.recent_gameid
# recgames = []
@ -238,12 +238,12 @@ class FileMenuDialog(LMenuDialog):
LTreeNode(text=gi.name, command=command), rg)
rg = tv.add_node(
LTreeNode(text='Favorite games'))
LTreeNode(text=_('Favorite games')))
if rg:
tv.add_node(LTreeNode(
text='<Add>', command=self.menubar.mAddFavor), rg)
text=_('<Add>'), command=self.menubar.mAddFavor), rg)
tv.add_node(LTreeNode(
text='<Remove>', command=self.menubar.mDelFavor), rg)
text=_('<Remove>'), command=self.menubar.mDelFavor), rg)
# Recent Liste
favids = self.app.opt.favorite_gameid
@ -257,12 +257,12 @@ class FileMenuDialog(LMenuDialog):
LTreeNode(text=gi.name, command=command), rg)
tv.add_node(LTreeNode(
text='Load', command=self.menubar.mOpen))
text=_('Load'), command=self.menubar.mOpen))
tv.add_node(LTreeNode(
text='Save', command=self.menubar.mSaveAs))
text=_('Save'), command=self.menubar.mSaveAs))
tv.add_node(LTreeNode(
text='Quit', command=self.menubar.mHoldAndQuit))
text=_('Quit'), command=self.menubar.mHoldAndQuit))
# ************************************************************************
@ -289,36 +289,36 @@ class EditMenuDialog(LMenuDialog): # Tools
def buildTree(self, tv, node):
tv.add_node(LTreeNode(
text='New game', command=self.menubar.mNewGame))
text=_('New game'), command=self.menubar.mNewGame))
tv.add_node(LTreeNode(
text='Restart game', command=self.menubar.mRestart))
text=_('Restart game'), command=self.menubar.mRestart))
tv.add_node(LTreeNode(
text='Undo', command=self.menubar.mUndo))
text=_('Undo'), command=self.menubar.mUndo))
tv.add_node(LTreeNode(
text='Redo', command=self.menubar.mRedo))
text=_('Redo'), command=self.menubar.mRedo))
tv.add_node(LTreeNode(
text='Redo all', command=self.menubar.mRedoAll))
text=_('Redo all'), command=self.menubar.mRedoAll))
tv.add_node(LTreeNode(
text='Auto drop', command=self.menubar.mDrop))
text=_('Auto drop'), command=self.menubar.mDrop))
tv.add_node(LTreeNode(
text='Shuffle tiles', command=self.menubar.mShuffle))
text=_('Shuffle tiles'), command=self.menubar.mShuffle))
tv.add_node(LTreeNode(
text='Deal cards', command=self.menubar.mDeal))
text=_('Deal cards'), command=self.menubar.mDeal))
self.addCheckNode(tv, None,
'Pause',
_('Pause'),
self.menubar.tkopt.pause,
self.menubar.mPause)
tv.add_node(LTreeNode(
text='Load game', command=self.menubar.mOpen))
text=_('Load game'), command=self.menubar.mOpen))
tv.add_node(LTreeNode(
text='Save game', command=self.menubar.mSaveAs))
text=_('Save game'), command=self.menubar.mSaveAs))
tv.add_node(LTreeNode(
text='Help', command=self.menubar.mHelpRules))
text=_('Help'), command=self.menubar.mHelpRules))
# -------------------------------------------
# TBD ?
@ -369,7 +369,7 @@ class GameMenuDialog(LMenuDialog):
def buildTree(self, tv, node):
tv.add_node(LTreeNode(
text='Current game ...',
text=_('Current game...'),
command=self.make_command(101, self.menubar.mPlayerStats)), None)
# tv.add_node(LTreeNode(
@ -432,16 +432,16 @@ class AssistMenuDialog(LMenuDialog):
def buildTree(self, tv, node):
tv.add_node(LTreeNode(
text='Hint', command=self.menubar.mHint))
text=_('Hint'), command=self.menubar.mHint))
tv.add_node(LTreeNode(
text='Highlight piles', command=self.menubar.mHighlightPiles))
text=_('Highlight piles'), command=self.menubar.mHighlightPiles))
# tv.add_node(LTreeNode(
# text='Find Card', command=self.menubar.mFindCard))
tv.add_node(LTreeNode(
text='Demo', command=self.menubar.mDemo))
text=_('Demo'), command=self.menubar.mDemo))
# -------------------------------------------
# TBD. How ?
@ -507,27 +507,27 @@ class OptionsMenuDialog(LMenuDialog):
# Automatic play settings
rg = tv.add_node(
LTreeNode(text='Automatic play'))
LTreeNode(text=_('Automatic play')))
if rg:
self.addCheckNode(tv, rg,
'Auto face up',
_('Auto face up'),
self.menubar.tkopt.autofaceup,
self.menubar.mOptAutoFaceUp)
self.addCheckNode(tv, rg,
'Auto drop',
_('Auto drop'),
self.menubar.tkopt.autodrop,
self.menubar.mOptAutoDrop)
self.addCheckNode(tv, rg,
'Auto deal',
_('Auto deal'),
self.menubar.tkopt.autodeal,
self.menubar.mOptAutoDeal)
# submenu.add_separator()
self.addCheckNode(tv, rg,
'Quick play',
_('Quick play'),
self.menubar.tkopt.quickplay,
self.menubar.mOptQuickPlay)
@ -535,214 +535,245 @@ class OptionsMenuDialog(LMenuDialog):
# Player assistance
rg = tv.add_node(
LTreeNode(text='Assist level'))
LTreeNode(text=_('Assist level')))
if rg:
self.addCheckNode(tv, rg,
'Enable undo',
_('Enable undo'),
self.menubar.tkopt.undo,
self.menubar.mOptEnableUndo)
self.addCheckNode(tv, rg,
'Enable bookmarks',
_('Enable bookmarks'),
self.menubar.tkopt.bookmarks,
self.menubar.mOptEnableBookmarks)
self.addCheckNode(tv, rg,
'Enable hint',
_('Enable hint'),
self.menubar.tkopt.hint,
self.menubar.mOptEnableHint)
self.addCheckNode(tv, rg,
'Enable shuffle',
_('Enable shuffle'),
self.menubar.tkopt.shuffle,
self.menubar.mOptEnableShuffle)
self.addCheckNode(tv, rg,
'Enable highlight piles',
_('Enable highlight piles'),
self.menubar.tkopt.highlight_piles,
self.menubar.mOptEnableHighlightPiles)
self.addCheckNode(tv, rg,
'Enable highlight cards',
_('Enable highlight cards'),
self.menubar.tkopt.highlight_cards,
self.menubar.mOptEnableHighlightCards)
self.addCheckNode(tv, rg,
'Enable highlight same rank',
_('Enable highlight same rank'),
self.menubar.tkopt.highlight_samerank,
self.menubar.mOptEnableHighlightSameRank)
self.addCheckNode(tv, rg,
'Highlight no matching',
_('Highlight no matching'),
self.menubar.tkopt.highlight_not_matching,
self.menubar.mOptEnableHighlightNotMatching)
# submenu.add_separator()
self.addCheckNode(tv, rg,
'Show removed tiles (in Mahjongg games)',
_('Show removed tiles (in Mahjongg games)'),
self.menubar.tkopt.mahjongg_show_removed,
self.menubar.mOptMahjonggShowRemoved)
self.addCheckNode(tv, rg,
'Show hint arrow (in Shisen-Sho games)',
_('Show hint arrow (in Shisen-Sho games)'),
self.menubar.tkopt.shisen_show_hint,
self.menubar.mOptShisenShowHint)
# submenu.add_separator()
# -------------------------------------------
# Language options
rg = tv.add_node(
LTreeNode(text=_('Language')))
if rg:
self.addRadioNode(tv, rg,
_('Default'),
self.menubar.tkopt.language, '',
self.menubar.mOptLanguage)
self.addRadioNode(tv, rg,
_('English'),
self.menubar.tkopt.language, 'en',
self.menubar.mOptLanguage)
self.addRadioNode(tv, rg,
_('German'),
self.menubar.tkopt.language, 'de',
self.menubar.mOptLanguage)
self.addRadioNode(tv, rg,
_('Italian'),
self.menubar.tkopt.language, 'it',
self.menubar.mOptLanguage)
self.addRadioNode(tv, rg,
_('Polish'),
self.menubar.tkopt.language, 'pl',
self.menubar.mOptLanguage)
self.addRadioNode(tv, rg,
_('Russian'),
self.menubar.tkopt.language, 'ru',
self.menubar.mOptLanguage)
# -------------------------------------------
# Sound options
rg = tv.add_node(
LTreeNode(text='Sound'))
LTreeNode(text=_('Sound')))
if rg:
self.addCheckNode(tv, rg,
'Enable',
_('Enable'),
self.menubar.tkopt.sound,
self.menubar.mOptSoundDialog)
rg1 = tv.add_node(
LTreeNode(text='Volume'), rg)
LTreeNode(text=_('Volume')), rg)
if rg1:
self.addRadioNode(tv, rg1,
'100%',
_('100%'),
self.menubar.tkopt.sound_sample_volume, 100,
self.menubar.mOptSoundSampleVol)
self.addRadioNode(tv, rg1,
'75%',
_('75%'),
self.menubar.tkopt.sound_sample_volume, 75,
self.menubar.mOptSoundSampleVol)
self.addRadioNode(tv, rg1,
'50%',
_('50%'),
self.menubar.tkopt.sound_sample_volume, 50,
self.menubar.mOptSoundSampleVol)
self.addRadioNode(tv, rg1,
'25%',
_('25%'),
self.menubar.tkopt.sound_sample_volume, 25,
self.menubar.mOptSoundSampleVol)
rg1 = tv.add_node(
LTreeNode(text='Samples'), rg)
LTreeNode(text=_('Samples')), rg)
if rg1:
key = 'areyousure'
self.addCheckNode(
tv, rg1,
'are you sure',
_('are you sure'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'autodrop'
self.addCheckNode(
tv, rg1,
'auto drop',
_('auto drop'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'autoflip'
self.addCheckNode(
tv, rg1,
'auto flip',
_('auto flip'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'autopilotlost'
self.addCheckNode(
tv, rg1,
'auto pilot lost',
_('auto pilot lost'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'autopilotwon'
self.addCheckNode(
tv, rg1,
'auto pilot won',
_('auto pilot won'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'deal'
self.addCheckNode(
tv, rg1,
'deal',
_('deal'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'dealwaste'
self.addCheckNode(
tv, rg1,
'deal waste',
_('deal waste'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'droppair'
self.addCheckNode(
tv, rg1,
'drop pair',
_('drop pair'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'drop'
self.addCheckNode(
tv, rg1,
'drop',
_('drop'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'flip'
self.addCheckNode(
tv, rg1,
'flip',
_('flip'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'move'
self.addCheckNode(
tv, rg1,
'move',
_('move'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'nomove'
self.addCheckNode(
tv, rg1,
'no move',
_('no move'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'redo'
self.addCheckNode(
tv, rg1,
'redo',
_('redo'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'startdrag'
self.addCheckNode(
tv, rg1,
'start drag',
_('start drag'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'turnwaste'
self.addCheckNode(
tv, rg1,
'turn waste',
_('turn waste'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'undo'
self.addCheckNode(
tv, rg1,
'undo',
_('undo'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'gamefinished'
self.addCheckNode(
tv, rg1,
'game finished',
_('game finished'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'gamelost'
self.addCheckNode(
tv, rg1,
'game lost',
_('game lost'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'gameperfect'
self.addCheckNode(
tv, rg1,
'game perfect',
_('game perfect'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
key = 'gamewon'
self.addCheckNode(
tv, rg1,
'game won',
_('game won'),
self.menubar.tkopt.sound_sample_vars[key],
self.make_vars_command(self.menubar.mOptSoundSample, key))
@ -750,7 +781,7 @@ class OptionsMenuDialog(LMenuDialog):
# Cardsets and card backside options
rg = tv.add_node(
LTreeNode(text='Cardsets'))
LTreeNode(text=_('Cardsets')))
if rg:
self.menubar.tkopt.cardset.set(self.app.cardset.index)
@ -790,45 +821,45 @@ class OptionsMenuDialog(LMenuDialog):
# Table background settings
rg = tv.add_node(
LTreeNode(text='Table'))
LTreeNode(text=_('Table')))
if rg:
rg1 = tv.add_node(
LTreeNode(text='Solid colors'), rg)
LTreeNode(text=_('Solid colors')), rg)
if rg1:
key = 'table'
self.addRadioNode(
tv, rg1,
'Blue',
_('Blue'),
self.menubar.tkopt.color_vars[key], '#0082df',
self.menubar.mOptTableColor)
self.addRadioNode(
tv, rg1,
'Green',
_('Green'),
self.menubar.tkopt.color_vars[key], '#008200',
self.menubar.mOptTableColor)
self.addRadioNode(
tv, rg1,
'Navy',
_('Navy'),
self.menubar.tkopt.color_vars[key], '#000086',
self.menubar.mOptTableColor)
self.addRadioNode(
tv, rg1,
'Olive',
_('Olive'),
self.menubar.tkopt.color_vars[key], '#868200',
self.menubar.mOptTableColor)
self.addRadioNode(
tv, rg1,
'Orange',
_('Orange'),
self.menubar.tkopt.color_vars[key], '#f79600',
self.menubar.mOptTableColor)
self.addRadioNode(
tv, rg1,
'Teal',
_('Teal'),
self.menubar.tkopt.color_vars[key], '#008286',
self.menubar.mOptTableColor)
rg1 = tv.add_node(
LTreeNode(text='Tiles and Images'), rg)
LTreeNode(text=_('Tiles and Images')), rg)
if rg1:
tm = self.app.tabletile_manager
@ -848,30 +879,30 @@ class OptionsMenuDialog(LMenuDialog):
# Card view options
rg = tv.add_node(
LTreeNode(text='Card view'))
LTreeNode(text=_('Card view')))
if rg:
self.addCheckNode(tv, rg,
'Card shadow',
_('Card shadow'),
self.menubar.tkopt.shadow,
self.menubar.mOptShadow)
self.addCheckNode(tv, rg,
'Shade legal moves',
_('Shade legal moves'),
self.menubar.tkopt.shade,
self.menubar.mOptShade)
self.addCheckNode(tv, rg,
'Negative cards bottom',
_('Negative cards bottom'),
self.menubar.tkopt.negative_bottom,
self.menubar.mOptNegativeBottom)
self.addCheckNode(tv, rg,
'Shrink face-down cards',
_('Shrink face-down cards'),
self.menubar.tkopt.shrink_face_down,
self.menubar.mOptShrinkFaceDown)
self.addCheckNode(tv, rg,
'Shade filled stacks',
_('Shade filled stacks'),
self.menubar.tkopt.shade_filled_stacks,
self.menubar.mOptShadeFilledStacks)
@ -879,47 +910,47 @@ class OptionsMenuDialog(LMenuDialog):
# Animation settins
rg = tv.add_node(
LTreeNode(text='Animations'))
LTreeNode(text=_('Animations')))
if rg:
self.addRadioNode(tv, rg,
'None',
_('None'),
self.menubar.tkopt.animations, 0,
self.menubar.mOptAnimations)
self.addRadioNode(tv, rg,
'Very fast',
_('Very fast'),
self.menubar.tkopt.animations, 1,
self.menubar.mOptAnimations)
self.addRadioNode(tv, rg,
'Fast',
_('Fast'),
self.menubar.tkopt.animations, 2,
self.menubar.mOptAnimations)
self.addRadioNode(tv, rg,
'Medium',
_('Medium'),
self.menubar.tkopt.animations, 3,
self.menubar.mOptAnimations)
self.addRadioNode(tv, rg,
'Slow',
_('Slow'),
self.menubar.tkopt.animations, 4,
self.menubar.mOptAnimations)
self.addRadioNode(tv, rg,
'Very slow',
_('Very slow'),
self.menubar.tkopt.animations, 5,
self.menubar.mOptAnimations)
# submenu.add_separator()
self.addCheckNode(tv, rg,
'Redeal animation',
_('Redeal animation'),
self.menubar.tkopt.redeal_animation,
self.menubar.mRedealAnimation)
self.addCheckNode(tv, rg,
'Winning animation',
_('Winning animation'),
self.menubar.tkopt.win_animation,
self.menubar.mWinAnimation)
@ -927,15 +958,15 @@ class OptionsMenuDialog(LMenuDialog):
# Touch mode settings
rg = tv.add_node(
LTreeNode(text='Touch mode'))
LTreeNode(text=_('Touch mode')))
if rg:
self.addRadioNode(tv, rg,
'Drag-and-Drop',
_('Drag-and-Drop'),
self.menubar.tkopt.mouse_type, 'drag-n-drop',
self.menubar.mOptMouseType)
self.addRadioNode(tv, rg,
'Point-and-Click',
_('Point-and-Click'),
self.menubar.tkopt.mouse_type, 'point-n-click',
self.menubar.mOptMouseType)
@ -969,10 +1000,10 @@ class OptionsMenuDialog(LMenuDialog):
# Toolbar options
rg = tv.add_node(
LTreeNode(text='Toolbar'))
LTreeNode(text=_('Toolbar')))
if rg:
self.addRadioNode(tv, rg,
'Hide',
_('Hide'),
self.menubar.tkopt.toolbar, 0,
self.menubar.mOptToolbar)
@ -987,11 +1018,11 @@ class OptionsMenuDialog(LMenuDialog):
# self.menubar.mOptToolbar)
self.addRadioNode(tv, rg,
'Left',
_('Left'),
self.menubar.tkopt.toolbar, 3,
self.menubar.mOptToolbar)
self.addRadioNode(tv, rg,
'Right',
_('Right'),
self.menubar.tkopt.toolbar, 4,
self.menubar.mOptToolbar)
@ -1028,12 +1059,12 @@ class OptionsMenuDialog(LMenuDialog):
# self.menubar.mOptDemoLogo)
self.addCheckNode(tv, None,
'Startup splash screen',
_('Startup splash screen'),
self.menubar.tkopt.splashscreen,
self.menubar.mOptSplashscreen)
self.addCheckNode(tv, None,
'Winning splash',
_('Winning splash'),
self.menubar.tkopt.display_win_message,
self.menubar.mWinDialog)
@ -1056,23 +1087,23 @@ class HelpMenuDialog(LMenuDialog):
def buildTree(self, tv, node):
tv.add_node(
LTreeNode(
text='Contents',
text=_('Contents'),
command=self.make_help_command(self.menubar.mHelp)))
tv.add_node(
LTreeNode(
text='How to play',
text=_('How to play'),
command=self.make_help_command(self.menubar.mHelpHowToPlay)))
tv.add_node(
LTreeNode(
text='Rules for this game',
text=_('Rules for this game'),
command=self.make_help_command(self.menubar.mHelpRules)))
tv.add_node(
LTreeNode(
text='License terms',
text=_('License terms'),
command=self.make_help_command(self.menubar.mHelpLicense)))
tv.add_node(
LTreeNode(
text='About' + TITLE + '...',
text=_('About %s...') % TITLE,
command=self.make_help_command(self.menubar.mHelpAbout)))
# tv.add_node(LTreeNode(
@ -1234,6 +1265,7 @@ class PysolMenubarTk:
toolbar_vars={},
sound_sample_vars={},
color_vars={},
language=StringVar(),
)
for w in TOOLBAR_BUTTONS:
self.tkopt.toolbar_vars[w] = BooleanVar()
@ -1288,6 +1320,7 @@ class PysolMenubarTk:
tkopt.negative_bottom.set(opt.negative_bottom)
tkopt.display_win_message.set(opt.display_win_message)
tkopt.cardset.set(self.app.cardset_manager.getSelected())
tkopt.language.set(opt.language)
for w in TOOLBAR_BUTTONS:
tkopt.toolbar_vars[w].set(opt.toolbar_vars.get(w, False))
@ -1346,7 +1379,7 @@ class PysolMenubarTk:
# LMainMenuDialog()
LMenuItem(self.__menubar.menu,
text="Menu", command=self.mMainMenuDialog)
text=_("Menu"), command=self.mMainMenuDialog)
MfxMenubar.addPath = None
@ -1574,7 +1607,8 @@ class PysolMenubarTk:
menu.delete(0, 'last')
if len(games) == 0:
menu.add_radiobutton(label='<none>', name=None, state='disabled')
menu.add_radiobutton(label=_('<none>'), name=None,
state='disabled')
elif len(games) > self.__cb_max * 4:
games.sort(lambda a, b: cmp2(a.name, b.name))
self._addSelectAllGameSubMenu(games, menu,
@ -1791,8 +1825,9 @@ class PysolMenubarTk:
#
DEFAULTEXTENSION = ".pso"
FILETYPES = ((TITLE + " files", "*" + DEFAULTEXTENSION),
("All files", "*"))
# TRANSLATORS: Usually, 'PySol files'
FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
(_("All files"), "*"))
def mAddFavor(self, *event):
gameid = self.app.game.id
@ -1870,6 +1905,16 @@ class PysolMenubarTk:
self.game.doPause()
self.tkopt.pause.set(self.game.pause)
def mOptLanguage(self, *args):
if self._cancelDrag(break_pause=False):
return
self.app.opt.language = self.tkopt.language.get()
MfxMessageDialog(
self.app.top, title=_("Note"),
text=_("""\
These settings will take effect
the next time you restart the %(app)s""") % {'app': TITLE})
def mOptSoundDialog(self, *args):
if self._cancelDrag(break_pause=False):
return
@ -2066,10 +2111,9 @@ class PysolMenubarTk:
# if os.name == "posix":
strings, default = (None, _("&Load"), _(
"&Cancel"), _("&Info..."), ), 1
t = CARDSET
key = self.app.nextgame.cardset.index
d = SelectCardsetDialogWithPreview(
self.top, title=_("Select ") + t,
self.top, title=_("Select cardset"),
app=self.app, manager=self.app.cardset_manager, key=key,
strings=strings, default=default)

View file

@ -35,17 +35,13 @@ class SelectCardsetDialogWithPreview(MfxDialog):
_cardset_store = None
def __init__(self, parent, title, app, manager, key=None, **kw):
kw = self.initKw(kw)
MfxDialog.__init__(self, parent, title, **kw)
#
if key is None:
key = manager.getSelected()
key = 1
self.status = -1
self.key = key
self.app = app
self.manager = manager
self.key = key
self.preview_key = -1
self.all_keys = []
self.status = -1
return
def getSelected(self):
return None

View file

@ -165,7 +165,7 @@ class SelectGameData(SelectDialogTreeData):
if name is None or not list(filter(
select_func, self.all_games_gi)):
continue
name = _("New games in v. ") + name
name = _("New games in v. %(version)s") % {'version': name}
gg.append(SelectGameNode(None, name, select_func))
if 1 and gg:
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),

View file

@ -406,7 +406,7 @@ class HTMLViewer:
# self.defcursor = 'xterm'
self.handcursor = "hand2"
self.title = "Browser"
self.title = _("Browser")
self.window = None
self.running = False
@ -431,11 +431,11 @@ class HTMLViewer:
# BoxLayout(orientation='horizontal', size_hint=(1.0, 0.1))
# create buttons
self.homeButton = HTMLButton(text="Index", on_release=self.goHome)
self.backButton = HTMLButton(text="Back", on_release=self.goBack)
self.homeButton = HTMLButton(text=_("Index"), on_release=self.goHome)
self.backButton = HTMLButton(text=_("Back"), on_release=self.goBack)
self.forwardButton = HTMLButton(
text="Forward", on_release=self.goForward)
self.closeButton = HTMLButton(text="Close", on_release=self.goHome)
text=_("Forward"), on_release=self.goForward)
self.closeButton = HTMLButton(text=_("Close"), on_release=self.goHome)
'''
buttonline.add_widget(self.homeButton)
@ -683,7 +683,8 @@ class HTMLViewer:
self.display(self.home, relpath=0)
def errorDialog(self, msg):
MfxMessageDialog(self.parent, title=TITLE + " HTML Problem",
MfxMessageDialog(self.parent,
title=_("%s HTML Problem") % TITLE,
text=msg,
# bitmap="warning"
# FIXME: this interp don't have images

View file

@ -172,8 +172,11 @@ class SingleGame_StatsDialog(MfxDialog):
print('Stats(p): won=%s, lost=%s' % (won, lost))
text1 = 'Total:\n won: %s ... %s%%\n lost: %s ... %s%%\n\n' % (
won, int(round(100.0 * pwon)), lost, int(round(100.0 * plost)))
text1 = _('Total:\n' +
' won: %(won)s ... %(percentwon)s%%\n' +
' lost: %(lost)s ... %(percentlost)s%%\n\n') % dict(
won=won, percentwon=int(round(100.0 * pwon)),
lost=lost, percentlost=int(round(100.0 * plost)))
# createChart(app, won, lost, _("Total"))
won, lost = app.stats.getSessionStats(player, gameid)
@ -181,9 +184,11 @@ class SingleGame_StatsDialog(MfxDialog):
print('Stats(s): won=%s, lost=%s' % (won, lost))
text2 = \
'Current Session:\n won: %s ... %s%%\n lost: %s ... %s%%\n' % \
(won, int(round(100.0 * pwon)), lost, int(round(100.0 * plost)))
text2 = _('Current Session:\n' +
' won: %(won)s ... %(percentwon)s%%\n' +
' lost: %(lost)s ... %(percentlost)s%%\n') % dict(
won=won, percentwon=(round(100.0 * pwon)),
lost=lost, percentlost=int(round(100.0 * plost)))
# text2 = 'Current Session:\n won=%s, lost=%s\n' % (won, lost)
# createChart(app, won, lost, _("Current session"))

View file

@ -167,10 +167,10 @@ class MfxMessageDialog(MfxDialog):
# LB
# nicht automatisch ein neues spiel laden.
if (title == "Game won"):
if (title == _("Game won")):
self.status = 1
# self.button = 0
if (title == "Game finished"):
if (title == _("Game finished")):
self.status = 1
# self.button =
@ -180,7 +180,7 @@ class MfxMessageDialog(MfxDialog):
class MfxExceptionDialog(MfxMessageDialog):
def __init__(self, parent, ex, title="Error", **kw):
def __init__(self, parent, ex, title=_("Error"), **kw):
kw = KwStruct(kw, bitmap="error")
text = kw.get("text", "")
if not text.endswith("\n"):
@ -221,7 +221,7 @@ class PysolAboutDialog(object):
logging.info('PysolAboutDialog: txt=%s' % text)
text = text + '\n\n' + 'Adaptation to Kivy/Android\n' + \
' Copyright (C) (2016-17) LB'
' Copyright (C) (2016-19) LB'
self.parent = parent
self.app = app
@ -261,13 +261,6 @@ class PysolAboutDialog(object):
label = FText(text=text, halign='center', size_hint=(1, 1))
window.content.add_widget(label)
'''
label = LLabel(text=text)
label.texture_update()
label.size = label.texture_size
image = LImage(texture=label.texture)
window.content.add_widget(image)
'''
# ************************************************************************
# * a simple tooltip
@ -637,53 +630,8 @@ class MfxScrolledCanvas(object):
def scroll_bottom(self, *event):
return self._yview('moveto', 1)
# ************************************************************************
# *
# ************************************************************************
# not used witch kivy. would not nun as it refers TkInter.
'''
class StackDesc:
def __init__(self, game, stack):
self.game = game
self.stack = stack
self.canvas = game.canvas
self.bindings = []
font = game.app.getFont('canvas_small')
# print self.app.cardset.CARDW, self.app.images.CARDW
cardw = game.app.images.CARDW
x, y = stack.x + cardw / 2, stack.y
text = stack.getHelp() + '\n' + stack.getBaseCard()
text = text.strip()
if text:
frame = Tkinter.Frame(self.canvas)
self.frame = frame
label = Tkinter.Message(frame, font=font, text=text,
width=cardw - 8, relief='solid',
fg='#000000', bg='#ffffe0', bd=1)
label.pack()
self.label = label
self.id = self.canvas.create_window(
x, y, window=frame, anchor='n')
self.bindings.append(label.bind(
'<ButtonPress>', self._buttonPressEvent))
# self.bindings.append(label.bind('<Enter>', self._enterEvent))
else:
self.id = None
def _buttonPressEvent(self, *event):
# self.game.deleteStackDesc()
self.frame.tkraise()
def _enterEvent(self, *event):
self.frame.tkraise()
def delete(self):
if self.id:
self.canvas.delete(self.id)
for b in self.bindings:
self.label.unbind('<ButtonPress>', b)
'''

View file

@ -203,7 +203,7 @@ class PysolToolbarTk(BoxLayout):
# (n_("Statistics"), self.mPlayerStats, _("View statistics")),
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
(None, None, None),
(n_("Quit"), self.mHoldAndQuit, _("Quit ") + TITLE),
(n_("Quit"), self.mHoldAndQuit, _("Quit %s") % TITLE),
):
if label is None:
# sep = self._createSeparator()

View file

@ -56,25 +56,25 @@ if TOOLKIT == 'kivy':
def fatal_no_cardsets(app):
app.wm_withdraw()
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
text=_('''No cardsets were found !!!
text=_('''No cardsets were found!!!
Cardsets should be installed into:
%s/cardsets/
%(dir)s
Please check your %s installation.
''') % (getprefdir(PACKAGE), TITLE),
Please check your %(app)s installation.
''') % {'dir': getprefdir(PACKAGE) + '/cardsets/', 'app': TITLE},
bitmap="error", strings=(_("&Quit"),))
else:
def fatal_no_cardsets(app):
app.wm_withdraw()
MfxMessageDialog(app.top, title=_("%s installation error") % TITLE,
text=_('''No cardsets were found !!!
text=_('''No cardsets were found!!!
Main data directory is:
%s
%(dir)s
Please check your %s installation.
''') % (app.dataloader.dir, TITLE),
Please check your %(app)s installation.
''') % {'dir': app.dataloader.dir, 'app': TITLE},
bitmap="error", strings=(_("&Quit"),))
@ -93,8 +93,8 @@ def parse_option(argv):
"sound-mod=",
"help"])
except getopt.GetoptError as err:
print_err(_("%s\ntry %s --help for more information") %
(err, prog_name), 0)
print_err(err + "\n" + _("try %s --help for more information") %
prog_name, 0)
return None
opts = {"help": False,
"deal": None,
@ -303,13 +303,14 @@ def pysol_init(app, args):
app.intro.progress.destroy()
d = MfxMessageDialog(top, title=_("%s installation error") % TITLE,
text=_('''
No games were found !!!
No games were found!!!
Main data directory is:
%s
%(dir)s
Please check your %s installation.
''') % (app.dataloader.dir, TITLE), bitmap="error", strings=(_("&Quit"),))
Please check your %(app)s installation.
''') % {'dir': app.dataloader.dir, 'app': TITLE},
bitmap="error", strings=(_("&Quit"),))
return 1
# init cardsets

View file

@ -143,7 +143,11 @@ def getprefdir(package):
# high resolution clock() and sleep()
uclock = time.clock
try:
uclock = time.perf_counter
except Exception:
uclock = time.clock
usleep = time.sleep
if os.name == "posix":
uclock = time.time

View file

@ -4,40 +4,46 @@ import sys
import six
def n_(x):
return x
class myLocalGettext(object):
def __init__(self, lang):
self.language = lang
def ungettext(msgid1, msgid2, n):
# unicoded ngettext
if not isinstance(msgid1, six.text_type):
msgid1 = six.text_type(msgid1, 'utf-8')
if not isinstance(msgid2, six.text_type):
msgid2 = six.text_type(msgid2, 'utf-8')
domain = gettext._current_domain
try:
t = gettext.translation(domain,
gettext._localedirs.get(domain, None))
except IOError:
if n == 1:
return msgid1
else:
return msgid2
if sys.version_info >= (3, 0):
return t.ngettext(msgid1, msgid2, n)
else:
return t.ungettext(msgid1, msgid2, n)
def fix_gettext():
def ugettext(message):
# unicoded gettext
if not isinstance(message, six.text_type):
message = six.text_type(message, 'utf-8')
def translation(self):
domain = gettext._current_domain
localedir = gettext._localedirs.get(domain, None)
if self.language == "":
t = gettext.translation(domain, localedir)
else:
t = gettext.translation(
domain, localedir, languages=[self.language])
return t
def maketext(self, msg):
if not isinstance(msg, six.text_type):
return six.text_type(msg, 'utf-8')
return msg
def ungettext(self, msgid1, msgid2, n):
# unicoded ngettext
msgid1 = self.maketext(msgid1)
msgid2 = self.maketext(msgid2)
try:
t = gettext.translation(domain,
gettext._localedirs.get(domain, None))
t = self.translation()
except IOError:
if n == 1:
return msgid1
else:
return msgid2
if sys.version_info >= (3, 0):
return t.ngettext(msgid1, msgid2, n)
else:
return t.ungettext(msgid1, msgid2, n)
def ugettext(self, message):
# unicoded gettext
message = self.maketext(message)
try:
t = self.translation()
except IOError:
return message
if sys.version_info >= (3, 0):
@ -45,10 +51,20 @@ def fix_gettext():
else:
return t.ugettext(message)
gettext.ugettext = ugettext
gettext.ungettext = ungettext
myGettext = myLocalGettext('')
def n_(x):
return x
def fix_gettext():
gettext.ugettext = myGettext.ugettext
gettext.ungettext = myGettext.ungettext
fix_gettext()
_ = gettext.ugettext
ungettext = gettext.ungettext

View file

@ -25,16 +25,20 @@ import os
import sys
import traceback
import configobj
import pysollib.settings
from pysollib.configobj import configobj, validate
from pysollib.mfxutil import print_err
from pysollib.mygettext import _
from pysollib.mygettext import myGettext
from pysollib.pysoltk import TOOLBAR_BUTTONS, TOOLKIT
from pysollib.resource import CSI
import six
import validate
# ************************************************************************
# * Options
# ************************************************************************
@ -111,6 +115,7 @@ solver_max_iterations = integer
solver_iterations_output_step = integer
solver_preset = string
display_win_message = boolean
language = string
[sound_samples]
move = boolean
@ -252,6 +257,7 @@ class Options:
# ('recent_gameid', 'list'),
# ('favorite_gameid', 'list'),
('display_win_message', 'bool'),
('language', 'str'),
]
def __init__(self):
@ -319,6 +325,7 @@ class Options:
self.negative_bottom = True
self.translate_game_names = True
self.display_win_message = True
self.language = ''
# sound
self.sound = True
self.sound_mode = 1
@ -663,6 +670,8 @@ class Options:
for key in TOOLBAR_BUTTONS:
self.toolbar_vars[key] = (key in visible_buttons)
myGettext.language = self.language
# solver
solver_presets = self._getOption('general', 'solver_presets', 'list')
if solver_presets is not None:

View file

@ -290,7 +290,7 @@ class SelectGameDialogWithPreview(MfxDialog):
for version, vg in GI.GAMES_BY_PYSOL_VERSION:
def selecter(gi, vg=vg):
return gi.id in vg
label = _("New games in v. ") + version
label = _("New games in v. %(version)s") % {'version': version}
data.append((label, selecter))
self._addGamesFromData(data, store, None,
_("by PySol version"), all_games)
@ -423,7 +423,7 @@ class SelectGameDialogWithPreview(MfxDialog):
# self.top.wm_title(
# "Select Game - " + self.app.getGameTitleName(gameid))
title = self.app.getGameTitleName(gameid)
self.set_title(_("Playable Preview - ") + title)
self.set_title(_("Playable Preview - %(game)s") % {'game': title})
#
self.preview_game = gi.gameclass(gi)
self.preview_game.createPreview(self.preview_app)

View file

@ -434,13 +434,13 @@ class HTMLViewer:
for p in REMOTE_PROTOCOLS:
if url.startswith(p):
if not openURL(url):
self.errorDialog(TITLE + _('''HTML limitation:
The %s protocol is not supported yet.
self.errorDialog(_('''%(app)s HTML limitation:
The %(protocol)s protocol is not supported yet.
Please use your standard web browser
to open the following URL:
%s
''') % (p, url))
%(url)s
''') % {'app': TITLE, 'protocol': p, 'url': url})
return
# locate the file relative to the current url

View file

@ -67,7 +67,8 @@ class StatsFormatter(PysolStatsFormatter):
6, result[6],
7, result[7])
total, played, won, lost, time, moves, perc = self.getStatSummary()
text = _("Total (%d out of %d games)") % (played, total)
text = _("Total (%(played)d out of %(total)d games)") % {
'played': played, 'total': total}
iter = self.store.append(None)
self.store.set(iter,
0, text,

View file

@ -24,95 +24,12 @@
# imports
import re
import time
from pysollib.mfxutil import SubclassResponsibility
try:
import random2
except ImportError:
raise ImportError(
"You need to install " +
"https://pypi.python.org/pypi/random2 using pip or similar.")
from pysol_cards.random import RandomBase # noqa: I100
# ************************************************************************
# * Abstract class for PySol Random number generator.
# *
# * We use a seed of type int in the range [0, MAX_SEED].
# ************************************************************************
class BasicRandom(RandomBase):
# MAX_SEED = 0L
# MAX_SEED = 0xffffffffffffffffL # 64 bits
MAX_SEED = int('100000000000000000000') # 20 digits
ORIGIN_UNKNOWN = 0
ORIGIN_RANDOM = 1
ORIGIN_PREVIEW = 2 # random from preview
ORIGIN_SELECTED = 3 # manually entered
ORIGIN_NEXT_GAME = 4 # "Next game number"
def __init__(self):
self.seed_as_string = None
def getSeedStr(self):
return str(self.initial_seed)
def __str__(self):
return self.str(self.initial_seed)
def str(self, seed):
return '%020d' % seed
def reset(self):
raise SubclassResponsibility
def copy(self):
random = self.__class__(0)
random.__dict__.update(self.__dict__)
return random
def increaseSeed(self, seed):
if seed < self.MAX_SEED:
return seed + 1
return 0
def _getRandomSeed(self):
t = int(time.time() * 256.0)
t = (t ^ (t >> 24)) % (self.MAX_SEED + 1)
return t
def setSeedAsStr(self, new_s):
self.seed_as_string = new_s
def getSeedAsStr(self):
if self.seed_as_string:
return self.seed_as_string
else:
return str(self)
# ************************************************************************
# * Mersenne Twister random number generator
# * uses the standard python module `random'
# ************************************************************************
class MTRandom(BasicRandom, random2.Random):
def __init__(self, seed=None):
if seed is None:
seed = self._getRandomSeed()
BasicRandom.__init__(self)
random2.Random.__init__(self, seed)
self.initial_seed = seed
self.initial_state = self.getstate()
self.origin = self.ORIGIN_UNKNOWN
def reset(self):
self.setstate(self.initial_state)
import pysol_cards
assert getattr(pysol_cards, 'VERSION', (0, 0, 0)) >= (0, 8, 7), (
"Newer version of https://pypi.org/project/pysol-cards is required.")
from pysol_cards.random_base import RandomBase # noqa: I100
from pysol_cards.random import MTRandom, match_ms_deal_prefix # noqa: I100
# ************************************************************************
@ -120,12 +37,12 @@ class MTRandom(BasicRandom, random2.Random):
# * uses the standard python module `random'
# ************************************************************************
# class WHRandom(BasicRandom, random.WichmannHill):
# class WHRandom(RandomBase, random.WichmannHill):
#
# def __init__(self, seed=None):
# if seed is None:
# seed = self._getRandomSeed()
# BasicRandom.__init__(self)
# RandomBase.__init__(self)
# random.WichmannHill.__init__(self, seed)
# self.initial_seed = seed
# self.initial_state = self.getstate()
@ -139,10 +56,10 @@ class MTRandom(BasicRandom, random2.Random):
# ************************************************************************
class MFXRandom(BasicRandom):
class MFXRandom(RandomBase):
def __init__(self, seed=None):
BasicRandom.__init__(self)
RandomBase.__init__(self)
if seed is None:
seed = self._getRandomSeed()
self.initial_seed = self.setSeed(seed)
@ -197,7 +114,7 @@ MS_LONG_BIT = (1 << 1000)
CUSTOM_BIT = (1 << 999)
class CustomRandom(BasicRandom):
class CustomRandom(RandomBase):
def __init__(self):
self.initial_seed = self.seed = MS_LONG_BIT | CUSTOM_BIT
self.origin = self.ORIGIN_UNKNOWN
@ -233,7 +150,9 @@ class LCRandom31(MFXRandom):
return "ms" + str(self.initial_seed)
def str(self, seed):
return "%05d" % int(seed) if not _match_ms(seed) else seed
if match_ms_deal_prefix("{}".format(seed)) is None:
return "%05d" % int(seed)
return seed
def setSeed(self, seed):
seed = int(seed)
@ -275,18 +194,14 @@ PysolRandom = MTRandom
# * PySol support code
# ************************************************************************
def _match_ms(s):
"""match an ms based seed string."""
return re.match(r"ms([0-9]+)\n?\Z", s)
# construct Random from seed string
def constructRandom(s):
if s == 'Custom':
return CustomRandom()
m = _match_ms(s)
if m:
seed = int(m.group(1))
m = match_ms_deal_prefix(s)
if m is not None:
seed = m
if 0 <= seed <= LCRandom31.MAX_SEED:
ret = LCRandom31(seed)
ret.setSeedAsStr(s)
@ -307,9 +222,9 @@ def constructRandom(s):
def random__str2long(s):
if s == 'Custom':
return CUSTOM_BIT | MS_LONG_BIT
m = _match_ms(s)
if m:
return (int(m.group(1)) | MS_LONG_BIT)
m = match_ms_deal_prefix(s)
if m is not None:
return (m | MS_LONG_BIT)
else:
return int(s)

View file

@ -63,8 +63,8 @@ if os.name == 'posix':
DATA_DIRS = [
'/usr/share/PySolFC',
'/usr/local/share/PySolFC',
'/usr/games/PySolFC',
'/usr/local/games/PySolFC',
'/usr/share/games/PySolFC',
'/usr/local/share/games/PySolFC',
]
if os.name == 'nt':
pass

View file

@ -1945,7 +1945,7 @@ class TalonStack(Stack,
nredeals = _('Unlimited redeals.')
else:
n = self.max_rounds-1
nredeals = ungettext('%d readeal', '%d redeals', n) % n
nredeals = ungettext('%d redeal', '%d redeals', n) % n
# round = _('Round #%d.') % self.round
return _('Talon.')+' '+nredeals # +' '+round

View file

@ -199,7 +199,7 @@ class FileStatsFormatter(PysolStatsFormatter):
def writeStats(self, player, sort_by='name'):
if player is None:
player = _('Demo')
header = _("Statistics for ") + player
header = _("Statistics for %(player)s") % {'player': player}
self.writeHeader(header, 62)
header = self.getStatHeader()
self.pstats(*header)
@ -209,7 +209,8 @@ class FileStatsFormatter(PysolStatsFormatter):
self.pstats(gameid=gameid, *result)
self.nl()
total, played, won, lost, time, moves, perc = self.getStatSummary()
self.pstats(_("Total (%d out of %d games)") % (played, total),
self.pstats(_("Total (%(played)d out of %(total)d games)") %
{'played': played, 'total': total},
won+lost, won, lost, time, moves, perc)
self.nl(2)
return played
@ -231,14 +232,14 @@ class FileStatsFormatter(PysolStatsFormatter):
def writeFullLog(self, player):
if player is None:
player = _('Demo')
header = _("Full log for ") + player
header = _("Full log for %(player)s") % {'player': player}
prev_games = self.app.stats.prev_games.get(player)
return self.writeLog(player, header, prev_games)
def writeSessionLog(self, player):
if player is None:
player = _('Demo')
header = _("Session log for ") + player
header = _("Session log for %(player)s") % {'player': player}
prev_games = self.app.stats.session_games.get(player)
return self.writeLog(player, header, prev_games)

25
pysollib/struct_new.py Normal file
View 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)

View file

@ -105,8 +105,8 @@ class PysolMenubarTk(PysolMenubarTkCommon):
self._calc_MfxMessageDialog()(
self.top, title=_("Change theme"),
text=_("""\
This settings will take effect
the next time you restart """)+TITLE,
These settings will take effect
the next time you restart %(app)s""") % {'app': TITLE},
bitmap="warning",
default=0, strings=(_("&OK"),))

View file

@ -21,7 +21,6 @@
#
# ---------------------------------------------------------------------------
import os
from pysollib.gamedb import GI
@ -80,7 +79,8 @@ class SelectGameData(SelectDialogTreeData):
def __init__(self, app):
SelectDialogTreeData.__init__(self)
self.all_games_gi = list(map(
app.gdb.get, app.gdb.getGamesIdSortedByName()))
app.gdb.get,
app.gdb.getGamesIdSortedByName()))
self.no_games = [SelectGameLeaf(None, None, _("(no games)"), None), ]
#
s_by_type = s_oriental = s_special = s_original = s_contrib = \
@ -144,7 +144,7 @@ class SelectGameData(SelectDialogTreeData):
if name is None or not list(filter(
select_func, self.all_games_gi)):
continue
name = _("New games in v. ") + name
name = _("New games in v. %(version)s") % {'version': name}
gg.append(SelectGameNode(None, name, select_func))
if 1 and gg:
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
@ -230,8 +230,6 @@ class SelectGameData(SelectDialogTreeData):
lambda gi: gi.si.redeals == 3),
SelectGameNode(None, _("Unlimited redeals"),
lambda gi: gi.si.redeals == -1),
# SelectGameNode(None, "Variable redeals",
# lambda gi: gi.si.redeals == -2),
SelectGameNode(
None, _("Other number of redeals"),
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
@ -321,7 +319,7 @@ class SelectGameDialog(MfxDialog):
if button == 0: # Ok or double click
self.gameid = self.tree.selection_key
self.tree.n_expansions = 1 # save xyview in any case
if button == 10: # Rules
if button == 1: # Rules
doc = self.app.getGameRulesFilename(self.tree.selection_key)
if not doc:
return
@ -515,7 +513,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
# self.top.wm_title("Select Game - " +
# self.app.getGameTitleName(gameid))
title = self.app.getGameTitleName(gameid)
self.top.wm_title(_("Playable Preview - ") + title)
self.top.wm_title(_("Playable Preview - %(game)s") % {'game': title})
#
self.preview_game = gi.gameclass(gi)
self.preview_game.createPreview(self.preview_app)

View file

@ -368,7 +368,8 @@ class TreeFormatter(PysolStatsFormatter):
self.parent_window.games[id] = t8
total, played, won, lost, time_, moves, perc = self.getStatSummary()
text = _("Total (%d out of %d games)") % (played, total)
text = _("Total (%(played)d out of %(total)d games)") % {
'played': played, 'total': total}
id = self.tree.insert("", "end", text=text,
values=(won+lost, won, lost, time_, moves, perc))
self.parent_window.tree_items.append(id)

View file

@ -188,7 +188,7 @@ class PysolToolbarTk:
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
(None, None, None),
(n_("Quit"), self.mQuit, _("Quit ")+TITLE),
(n_("Quit"), self.mQuit, _("Quit %s") % TITLE),
):
if label is None:
sep = self._createSeparator()

View file

@ -24,7 +24,6 @@
import os
from pysollib.gamedb import GI
from pysollib.help import help_html
from pysollib.mfxutil import KwStruct, Struct, destruct
from pysollib.mfxutil import format_time
from pysollib.mygettext import _
@ -39,11 +38,11 @@ from .selecttree import SelectDialogTreeCanvas
from .selecttree import SelectDialogTreeLeaf, SelectDialogTreeNode
from .tkwidget import MfxDialog, MfxScrolledCanvas
# ************************************************************************
# * Nodes
# ************************************************************************
class SelectGameLeaf(SelectDialogTreeLeaf):
pass
@ -137,7 +136,6 @@ class SelectGameData(SelectDialogTreeData):
if 1 and gg:
s_by_compatibility = SelectGameNode(None, _("by Compatibility"),
tuple(gg))
pass
#
s_by_pysol_version, gg = None, []
for name, games in GI.GAMES_BY_PYSOL_VERSION:
@ -146,7 +144,7 @@ class SelectGameData(SelectDialogTreeData):
if name is None or not list(filter(
select_func, self.all_games_gi)):
continue
name = _("New games in v. ") + name
name = _("New games in v. %(version)s") % {'version': name}
gg.append(SelectGameNode(None, name, select_func))
if 1 and gg:
s_by_pysol_version = SelectGameNode(None, _("by PySol version"),
@ -167,16 +165,16 @@ class SelectGameData(SelectDialogTreeData):
list(app.gdb.getGamesTuplesSortedByAlternateName()))
#
self.rootnodes = [_f for _f in (
SelectGameNode(None, _("All Games"), None),
SelectGameNode(None, _("All Games"), None, expanded=0),
SelectGameNode(None, _("Alternate Names"), ul_alternate_names),
SelectGameNode(None, _("Popular Games"),
lambda gi: gi.si.game_flags & GI.GT_POPULAR),
s_by_type,
s_mahjongg,
s_oriental,
s_special,
SelectGameNode(None, _("Custom Games"),
lambda gi: gi.si.game_type == GI.GT_CUSTOM),
s_by_type,
SelectGameNode(None, _('by Skill Level'), (
SelectGameNode(None, _('Luck only'),
lambda gi: gi.skill_level == GI.SL_LUCK),
@ -232,8 +230,6 @@ class SelectGameData(SelectDialogTreeData):
lambda gi: gi.si.redeals == 3),
SelectGameNode(None, _("Unlimited redeals"),
lambda gi: gi.si.redeals == -1),
SelectGameNode(None, "Variable redeals",
lambda gi: gi.si.redeals == -2),
SelectGameNode(
None, _("Other number of redeals"),
lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3)),
@ -308,8 +304,8 @@ class SelectGameDialog(MfxDialog):
def initKw(self, kw):
kw = KwStruct(kw,
strings=(None, None, _("&Cancel"),), default=0,
separator=True,
resizable=True,
separator=True,
padx=10, pady=10,
buttonpadx=10, buttonpady=5,
)
@ -322,14 +318,15 @@ class SelectGameDialog(MfxDialog):
MfxDialog.destroy(self)
def mDone(self, button):
if button == 0: # Ok or double click
if button == 0: # Ok or double click
self.gameid = self.tree.selection_key
self.tree.n_expansions = 1 # save xyview in any case
if button == 1: # Rules
if button == 1: # Rules
doc = self.app.getGameRulesFilename(self.tree.selection_key)
if not doc:
return
dir = os.path.join("html", "rules")
from pysollib.help import help_html
help_html(self.app, doc, dir, self.top)
return
MfxDialog.mDone(self, button)
@ -436,7 +433,8 @@ class SelectGameDialogWithPreview(SelectGameDialog):
def initKw(self, kw):
kw = KwStruct(kw,
strings=(_("&Select"), _("&Rules"), _("&Cancel"),),
strings=((_("&Rules"), 10), 'sep',
_("&Select"), _("&Cancel"),),
default=0,
)
return SelectGameDialog.initKw(self, kw)
@ -516,7 +514,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
# self.top.wm_title("Select Game - " +
# self.app.getGameTitleName(gameid))
title = self.app.getGameTitleName(gameid)
self.top.wm_title(_("Playable Preview - ") + title)
self.top.wm_title(_("Playable Preview - %(game)s") % {'game': title})
#
self.preview_game = gi.gameclass(gi)
self.preview_game.createPreview(self.preview_app)
@ -545,7 +543,7 @@ class SelectGameDialogWithPreview(SelectGameDialog):
#
self.updateInfo(gameid)
#
rules_button = self.buttons[1]
rules_button = self.buttons[0]
if self.app.getGameRulesFilename(gameid):
rules_button.config(state="normal")
else:

View file

@ -410,7 +410,8 @@ class CanvasFormatter(PysolStatsFormatter):
#
y += self.h
total, played, won, lost, time_, moves, perc = self.getStatSummary()
s = _("Total (%d out of %d games)") % (played, total)
s = _("Total (%(played)d out of %(total)d games)") % {
'played': played, 'total': total}
self.pstats(y, (s, won+lost, won, lost, time_, moves, perc))
def writeLog(self, player, prev_games):

View file

@ -188,7 +188,7 @@ class PysolToolbarTk:
(n_("Statistics"), self.mPlayerStats, _("View statistics")),
(n_("Rules"), self.mHelpRules, _("Rules for this game")),
(None, None, None),
(n_("Quit"), self.mQuit, _("Quit ")+TITLE),
(n_("Quit"), self.mQuit, _("Quit %s") % TITLE),
):
if label is None:
sep = self._createSeparator()

View file

@ -293,7 +293,7 @@ class PysolMenubarTkCommon:
if WIN_SYSTEM == "aqua":
applemenu = MfxMenu(self.menubar, "apple")
applemenu.add_command(
label=_("&About ")+TITLE, command=self.mHelpAbout)
label=_("&About %s") % TITLE, command=self.mHelpAbout)
menu = MfxMenu(self.menubar, n_("&File"))
menu.add_command(
@ -663,7 +663,7 @@ class PysolMenubarTkCommon:
if WIN_SYSTEM != "aqua":
menu.add_separator()
menu.add_command(
label=n_("&About ")+TITLE+"...",
label=_("&About %s...") % TITLE,
command=self.mHelpAbout)
MfxMenubar.addPath = None
@ -968,7 +968,8 @@ class PysolMenubarTkCommon:
def updateGamesMenu(self, menu, games):
menu.delete(0, 'last')
if len(games) == 0:
menu.add_radiobutton(label='<none>', name=None, state='disabled')
menu.add_radiobutton(label=_('<none>'), name=None,
state='disabled')
elif len(games) > self.cb_max*4:
games.sort(key=lambda x: x.name)
self._addSelectAllGameSubMenu(games, menu,
@ -1132,7 +1133,8 @@ class PysolMenubarTkCommon:
#
DEFAULTEXTENSION = ".pso"
FILETYPES = ((TITLE+" files", "*"+DEFAULTEXTENSION), ("All files", "*"))
FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
(_("All files"), "*"))
def mAddFavor(self, *event):
gameid = self.app.game.id

View file

@ -25,7 +25,7 @@ class BaseSolverDialog:
def __init__(self, parent, app, **kw):
self.parent = parent
self.app = app
title = TITLE+' - FreeCell Solver'
title = _('%(app)s - FreeCell Solver') % {'app': TITLE}
kw = self.initKw(kw)
self._calc_MfxDialog().__init__(
self, parent, title, kw.resizable, kw.default)

View file

@ -311,13 +311,13 @@ class Base_HTMLViewer:
for p in REMOTE_PROTOCOLS:
if url.startswith(p):
if not openURL(url):
self.errorDialog(TITLE + _('''HTML limitation:
The %s protocol is not supported yet.
self.errorDialog(_('''%(app)s HTML limitation:
The %(protocol)s protocol is not supported yet.
Please use your standard web browser
to open the following URL:
%s
''') % (p, url))
%(url)s
''') % {'app': TITLE, 'protocol': p, 'url': url})
return
# locate the file relative to the current url
@ -429,7 +429,7 @@ to open the following URL:
def errorDialog(self, msg):
self._calc_MfxMessageDialog()(
self.parent, title=TITLE+" HTML Problem",
self.parent, title="%(app)s HTML Problem" % {'app': TITLE},
text=msg,
# bitmap="warning", # FIXME: this interp don't have images
strings=(_("&OK"),), default=0)

View file

@ -150,7 +150,7 @@ def all_games(sort_by='id'):
if gt == 'French':
gt = 'French (%s)' % GAME_BY_TYPE[gi.si.game_type]
name = gi.name
altnames = '<br>'.join(gi.altnames)
altnames = '<br/>'.join(gi.altnames)
fn = os.path.join(rules_dir, rules_fn)
if 1 and os.path.exists(fn):
print('''<tr>

View file

@ -1,39 +1,61 @@
#!/bin/bash
set -eu
# converts cardset images and config files in current
# directory from input-format to output-format.
# Converts cardset images and config files in subdirs of the current
# directory from input-format to output-format (default: gif to bmp).
#
# example to convert from gif format to png:
# Example to convert from gif format to png:
#
# $> cardconv gif png
#
# needs package 'ImageMagick' beeing installed.
# Needs package 'ImageMagick' being installed.
ifo=''
if [ $1 ]
then
ifo='gif'
ofo='bmp'
if [ $# -eq 2 ]; then
ifo=$1
else
echo 'use: cardconv <input-format> <output-format>'
exit
fi
ofo=''
if [ $2 ]
then
ofo=$2
else
echo 'use: cardconv <input-format> <output-format>'
exit
elif [ $# -ne 0 ]; then
echo "Usage: cardconv [INPUTFORMAT OUTPUTFORMAT]"
echo "If formats are not specified, converts $ifo to $ofo."
exit 1
fi
# alle images.
for i in *.${ifo}; do convert $i `basename $i .${ifo}`.${ofo}; rm -f $i; done
# Returns true if $1/config.txt exists and contains a mention of the input format
# file extension.
chkdir() {
grep -qi "\.${ifo}" "${1}/config".txt
return $?
}
# config.txt
if [ -f config.txt ]
then
cp -a config.txt tmp.txt
cat tmp.txt | sed "s/.${ifo}/.${ofo}/g" >config.txt
rm -f tmp.txt
fi
# Usage: convdir in_dir out_dir
convdir() {
mkdir -p "$2"
# Convert all images
if [ $ofo == 'png' ]; then
for i in $1/*.$ifo; do
convert "$i" "-matte" "-quality" "95" "png32:$2/$(basename $i .$ifo).$ofo"
done
else
for i in $1/*.$ifo; do
convert "$i" "$2/$(basename $i .$ifo).$ofo"
done
fi
# Convert config.txt
if [ -f $1/config.txt ]; then
sed "s/\.${ifo}/.${ofo}/g" $1/config.txt > $2/config.txt
fi
}
# Check all cardsets.
for in_dir in cardset-*; do
out_dir=${in_dir}-${ofo}
if [[ ! $in_dir =~ -$ofo$ ]] && [ ! -d $out_dir ] && chkdir $in_dir; then
convdir $in_dir $out_dir
echo "$out_dir created"
fi
done

View file

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

View file

@ -7,14 +7,22 @@ import re
from sys import platform
IS_MAC = (platform == "darwin")
PY_VERS = ([] if re.search("\\bSKIP_PY2\\b",
os.getenv('TEST_TAGS', '')) else [2])+[3]
TEST_TAGS = os.getenv('TEST_TAGS', '')
def _has_tag(tag):
return re.search("\\b{}\\b".format(tag), TEST_TAGS)
PY_VERS = ([] if _has_tag('SKIP_PY2') else [2])+[3]
SKIP_GTK = _has_tag('SKIP_GTK')
module_names = []
for d, _, files in os.walk("pysollib"):
for f in files:
if re.search("\\.py$", f):
module_names.append(
(d + "/" + re.sub("\\.py$", "", f)).replace("/", "."))
(d + "/" + re.sub("\\.py$", "", f))
.replace("/", ".").replace(os.sep, "."))
module_names.sort()
for module_name in module_names:
@ -22,7 +30,7 @@ for module_name in module_names:
continue
is_gtk = ("gtk" in module_name)
for ver in PY_VERS:
if ((not is_gtk) or (ver == 2 and (not IS_MAC))):
if ((not is_gtk) or (ver == 2 and (not IS_MAC) and (not SKIP_GTK))):
def fmt(s):
return s % {'module_name': module_name, 'ver': ver}
open(os.path.join(".", "tests", "individually-importing", fmt("import_v%(ver)d_%(module_name)s.py")), 'w').write(fmt('''#!/usr/bin/env python%(ver)d
@ -36,6 +44,7 @@ print('ok 1 - imported')
for ver in PY_VERS:
for mod in [
'pysol_tests.acard_unit',
'pysol_tests.game_drag',
'pysol_tests.hint',
'pysol_tests.import_file1',
'pysol_tests.kpat_load_save',

View file

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

@ -10,9 +10,9 @@ set -e -x
src_base="PySolFC-Cardsets"
dest_base="$src_base--Minimal"
ver="2.0"
ver="2.0.1"
src_vbase="$src_base-2.0"
dest_vbase="$dest_base-2.0"
dest_vbase="$dest_base-2.0.1"
src_arc="$src_vbase.tar.bz2"
if ! test -f "$src_arc"
@ -21,6 +21,7 @@ then
fi
tar -xvf "$src_arc"
rm -rf "$dest_vbase"
mkdir -p "$dest_vbase"
cat scripts/cardsets_to_bundle | (while read b
do

View file

@ -1,4 +1,6 @@
#! /bin/sh
set -e
#
# y.sh
# Copyright (C) 2018 shlomif <shlomif@cpan.org>

Some files were not shown because too many files have changed in this diff Show more