mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
1014 lines
35 KiB
Python
1014 lines
35 KiB
Python
#!/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
|
|
#
|
|
# 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/>.
|
|
#
|
|
# ---------------------------------------------------------------------------##
|
|
|
|
import os
|
|
|
|
from pysollib.gamedb import GI
|
|
from pysollib.help import help_about, help_html
|
|
from pysollib.mfxutil import Struct, openURL
|
|
from pysollib.mfxutil import USE_PIL, print_err
|
|
from pysollib.mygettext import _
|
|
from pysollib.pysolrandom import construct_random
|
|
from pysollib.pysoltk import AllGames_StatsDialog, SingleGame_StatsDialog
|
|
from pysollib.pysoltk import ColorsDialog
|
|
from pysollib.pysoltk import EditTextDialog
|
|
from pysollib.pysoltk import FontsDialog
|
|
from pysollib.pysoltk import FullLog_StatsDialog, SessionLog_StatsDialog
|
|
from pysollib.pysoltk import GameInfoDialog
|
|
from pysollib.pysoltk import MfxExceptionDialog
|
|
from pysollib.pysoltk import MfxMessageDialog, MfxSimpleEntry
|
|
from pysollib.pysoltk import PlayerOptionsDialog
|
|
from pysollib.pysoltk import ProgressionDialog
|
|
from pysollib.pysoltk import PysolMenubarTk, PysolToolbarTk
|
|
from pysollib.pysoltk import Status_StatsDialog, Top_StatsDialog
|
|
from pysollib.pysoltk import TimeoutsDialog
|
|
from pysollib.pysoltk import create_find_card_dialog
|
|
from pysollib.pysoltk import create_full_picture_dialog
|
|
from pysollib.pysoltk import create_solver_dialog
|
|
from pysollib.pysoltk import raise_find_card_dialog
|
|
from pysollib.pysoltk import raise_full_picture_dialog
|
|
from pysollib.settings import DEBUG
|
|
from pysollib.settings import PACKAGE_URL, TITLE
|
|
from pysollib.settings import TOP_SIZE
|
|
from pysollib.stats import FileStatsFormatter
|
|
|
|
|
|
# ************************************************************************
|
|
# * menubar
|
|
# ************************************************************************
|
|
|
|
class PysolMenubar(PysolMenubarTk):
|
|
def __init__(self, app, top, progress=None):
|
|
self.app = app
|
|
self.top = top
|
|
self.game = None
|
|
# enabled/disabled - this is set by updateMenuState()
|
|
self.menustate = Struct(
|
|
save=0,
|
|
save_as=0,
|
|
hold_and_quit=0,
|
|
rndplayed=0,
|
|
rndwon=0,
|
|
rndnotwon=0,
|
|
rndnotplayed=0,
|
|
undo=0,
|
|
redo=0,
|
|
restart=0,
|
|
deal=0,
|
|
hint=0,
|
|
autofaceup=0,
|
|
autodrop=0,
|
|
shuffle=0,
|
|
autodeal=0,
|
|
quickplay=0,
|
|
demo=0,
|
|
highlight_piles=0,
|
|
autoscale=0,
|
|
find_card=0,
|
|
full_picture=0,
|
|
rules=0,
|
|
pause=0,
|
|
custom_game=0,
|
|
)
|
|
PysolMenubarTk.__init__(self, app, top, progress)
|
|
|
|
#
|
|
# delegation to Game
|
|
#
|
|
|
|
def _finishDrag(self):
|
|
return self.game is None or self.game._finishDrag()
|
|
|
|
def _cancelDrag(self, break_pause=True):
|
|
if self.game is None:
|
|
return True
|
|
ret = self.game._cancelDrag(break_pause=break_pause)
|
|
self._setPauseMenu(self.game.pause)
|
|
return ret
|
|
|
|
def changed(self, *args, **kw):
|
|
assert self.game is not None
|
|
return self.game.changed(*args, **kw)
|
|
|
|
#
|
|
# menu updates
|
|
#
|
|
|
|
def _clearMenuState(self):
|
|
ms = self.menustate
|
|
for k, v in ms.__dict__.items():
|
|
if isinstance(v, list):
|
|
ms.__dict__[k] = [0] * len(v)
|
|
else:
|
|
ms.__dict__[k] = 0
|
|
|
|
# update self.menustate for menu items and toolbar
|
|
def _updateMenuState(self):
|
|
self._clearMenuState()
|
|
game = self.game
|
|
assert game is not None
|
|
opt = self.app.opt
|
|
ms = self.menustate
|
|
# 0 = DISABLED, 1 = ENABLED
|
|
ms.rndplayed = len(self._mGetPossibleRandomGames("played")) > 0
|
|
ms.rndwon = len(self._mGetPossibleRandomGames("won")) > 0
|
|
ms.rndnotwon = len(self._mGetPossibleRandomGames("not won")) > 0
|
|
ms.rndnotplayed = len(self._mGetPossibleRandomGames("not played")) > 0
|
|
ms.save_as = game.canSaveGame()
|
|
ms.hold_and_quit = ms.save_as
|
|
if game.filename and ms.save_as:
|
|
ms.save = 1
|
|
if opt.undo:
|
|
if game.canUndo() and game.moves.index > 0:
|
|
ms.undo = 1
|
|
if game.canRedo() and game.moves.index < len(game.moves.history):
|
|
ms.redo = 1
|
|
if game.moves.index > 0:
|
|
ms.restart = 1
|
|
if game.canDealCards():
|
|
ms.deal = 1
|
|
if game.getHintClass() is not None:
|
|
if opt.hint:
|
|
ms.hint = 1
|
|
# if not game.demo: # if not already running
|
|
ms.demo = 1
|
|
autostacks = game.getAutoStacks()
|
|
if autostacks[0]:
|
|
ms.autofaceup = 1
|
|
if autostacks[1] and game.s.foundations:
|
|
ms.autodrop = 1
|
|
if game.s.waste:
|
|
ms.autodeal = 1
|
|
if autostacks[2]:
|
|
ms.quickplay = 1
|
|
if opt.highlight_piles and game.getHighlightPilesStacks():
|
|
ms.highlight_piles = 1
|
|
if opt.auto_scale:
|
|
ms.autoscale = 1
|
|
if game.canFindCard():
|
|
ms.find_card = 1
|
|
if game.canShowFullPicture():
|
|
ms.full_picture = 1
|
|
if game.app.getGameRulesFilename(game.id): # note: this may return ""
|
|
ms.rules = 1
|
|
if not game.finished:
|
|
ms.pause = 1
|
|
if game.gameinfo.si.game_type == GI.GT_CUSTOM:
|
|
ms.custom_game = 1
|
|
if game.canShuffle():
|
|
if opt.shuffle:
|
|
ms.shuffle = 1
|
|
|
|
# update menu items and toolbar
|
|
def _updateMenus(self):
|
|
if self.game is None:
|
|
return
|
|
ms = self.menustate
|
|
# File menu
|
|
self.setMenuState(ms.save, "file.save")
|
|
self.setMenuState(ms.save_as, "file.saveas")
|
|
self.setMenuState(ms.rndplayed, "file.selectrandomgame.gamesplayed")
|
|
self.setMenuState(ms.rndwon, "file.selectrandomgame.gamesplayedandwon")
|
|
self.setMenuState(ms.rndnotwon,
|
|
"file.selectrandomgame.gamesplayedandnotwon")
|
|
self.setMenuState(ms.rndnotplayed,
|
|
"file.selectrandomgame.gamesnotplayed")
|
|
self.setMenuState(ms.hold_and_quit, "file.holdandquit")
|
|
# Edit menu
|
|
self.setMenuState(ms.undo, "edit.undo")
|
|
self.setMenuState(ms.redo, "edit.redo")
|
|
self.setMenuState(ms.redo, "edit.redoall")
|
|
self.updateBookmarkMenuState()
|
|
self.setMenuState(ms.restart, "edit.restart")
|
|
self.setMenuState(ms.custom_game, "edit.editcurrentgame")
|
|
self.setMenuState(ms.custom_game, "edit.deletecurrentgame")
|
|
# Game menu
|
|
self.setMenuState(ms.deal, "game.dealcards")
|
|
self.setMenuState(ms.autodrop, "game.autodrop")
|
|
self.setMenuState(ms.shuffle, "game.shuffletiles")
|
|
self.setMenuState(ms.pause, "game.pause")
|
|
# Assist menu
|
|
self.setMenuState(ms.hint, "assist.hint")
|
|
self.setMenuState(ms.highlight_piles, "assist.highlightpiles")
|
|
self.setMenuState(ms.find_card, "assist.findcard")
|
|
self.setMenuState(ms.full_picture, "assist.showfullpicture")
|
|
self.setMenuState(ms.demo, "assist.demo")
|
|
self.setMenuState(ms.demo, "assist.demoallgames")
|
|
# Options menu
|
|
self.setMenuState(ms.autofaceup, "options.automaticplay.autofaceup")
|
|
self.setMenuState(ms.autodrop, "options.automaticplay.autodrop")
|
|
self.setMenuState(ms.autodeal, "options.automaticplay.autodeal")
|
|
self.setMenuState(ms.quickplay, "options.automaticplay.quickplay")
|
|
if USE_PIL:
|
|
self.setMenuState(ms.autoscale,
|
|
"options.cardsize.preserveaspectratio")
|
|
self.setMenuState(not ms.autoscale,
|
|
"options.cardsize.increasethecardsize")
|
|
self.setMenuState(not ms.autoscale,
|
|
"options.cardsize.decreasethecardsize")
|
|
self.setMenuState(not ms.autoscale,
|
|
"options.cardsize.resetthecardsize")
|
|
# Help menu
|
|
self.setMenuState(ms.rules, "help.rulesforthisgame")
|
|
# Toolbar
|
|
self.setToolbarState(ms.restart, "restart")
|
|
self.setToolbarState(ms.save_as, "save")
|
|
self.setToolbarState(ms.undo, "undo")
|
|
self.setToolbarState(ms.redo, "redo")
|
|
self.setToolbarState(ms.autodrop, "autodrop")
|
|
self.setToolbarState(ms.hint, "hint")
|
|
self.setToolbarState(ms.shuffle, "shuffle")
|
|
self.setToolbarState(ms.pause, "pause")
|
|
self.setToolbarState(ms.rules, "rules")
|
|
|
|
# update menu items and toolbar
|
|
def updateMenus(self):
|
|
if self.game is None:
|
|
return
|
|
self._updateMenuState()
|
|
self._updateMenus()
|
|
|
|
# disable menu items and toolbar
|
|
def disableMenus(self):
|
|
if self.game is None:
|
|
return
|
|
self._clearMenuState()
|
|
self._updateMenus()
|
|
|
|
#
|
|
# File menu
|
|
#
|
|
|
|
def mNewGame(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("New game")):
|
|
return
|
|
if self.game.nextGameFlags(self.game.id) == 0:
|
|
self.game.endGame()
|
|
self.game.newGame()
|
|
else:
|
|
self.game.endGame()
|
|
self.game.quitGame(self.game.id)
|
|
|
|
def _mSelectGame(self, id, random=None, force=False):
|
|
if self._cancelDrag():
|
|
return
|
|
if not force and self.game.id == id:
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("Select game")):
|
|
return
|
|
self.game.endGame()
|
|
self.game.quitGame(id, random=random)
|
|
|
|
def _mNewGameBySeed(self, seed, origin):
|
|
try:
|
|
random = construct_random(seed)
|
|
if random is None:
|
|
return
|
|
id = self.game.id
|
|
if not self.app.getGameInfo(id):
|
|
raise ValueError
|
|
except (ValueError, TypeError):
|
|
MfxMessageDialog(self.top, title=_("Invalid game number"),
|
|
text=_("Invalid game number\n") + str(seed),
|
|
bitmap="error")
|
|
return
|
|
f = self.game.nextGameFlags(id, random)
|
|
if f & 17 == 0:
|
|
return
|
|
random.origin = origin
|
|
if f & 15 == 0:
|
|
self.game.endGame()
|
|
self.game.newGame(random=random)
|
|
else:
|
|
self.game.endGame()
|
|
self.game.quitGame(id, random=random)
|
|
|
|
def mNewGameWithNextId(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("Select next game number")):
|
|
return
|
|
r = self.game.random
|
|
seed = r.increaseSeed(r.initial_seed)
|
|
seed = r.str(seed)
|
|
self._mNewGameBySeed(seed, self.game.random.ORIGIN_NEXT_GAME)
|
|
|
|
def mSelectGameById(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
f = self.game.getGameNumber(format=0)
|
|
d = MfxSimpleEntry(self.top, _("Select new game number"),
|
|
_("\n\nEnter new game number"), f,
|
|
strings=(_("&OK"), _("&Next number"), _("&Cancel")),
|
|
default=0, e_width=25)
|
|
if d.status != 0:
|
|
return
|
|
if d.button == 2:
|
|
return
|
|
if d.button == 1:
|
|
self.mNewGameWithNextId()
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("Select new game number")):
|
|
return
|
|
self._mNewGameBySeed(d.value, self.game.random.ORIGIN_SELECTED)
|
|
|
|
def mSelectRandomGame(self, type='all'):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("Select random game")):
|
|
return
|
|
game_id = None
|
|
games = self._mGetPossibleRandomGames(type)
|
|
if games:
|
|
game_id = self.app.chooseRandomOutOfGames(games)
|
|
if game_id and game_id != self.game.id:
|
|
self.game.endGame()
|
|
self.game.quitGame(game_id)
|
|
|
|
def _mGetPossibleRandomGames(self, type='all'):
|
|
games = []
|
|
for g in self.app.gdb.getGamesIdSortedById():
|
|
gi = self.app.getGameInfo(g)
|
|
if 1 and gi.id == self.game.id:
|
|
# force change of game
|
|
continue
|
|
# Not sure why this check to not change game category existed,
|
|
# but commented, as it doesn't make sense.
|
|
# if 1 and gi.category != self.game.gameinfo.category:
|
|
# don't change game category
|
|
# continue
|
|
won, lost = self.app.stats.getStats(self.app.opt.player, gi.id)
|
|
if type == 'all':
|
|
games.append(gi.id)
|
|
elif type == 'played' and won + lost > 0:
|
|
games.append(gi.id)
|
|
elif type == 'won' and won > 0:
|
|
games.append(gi.id)
|
|
elif type == 'not won' and won == 0 and lost > 0:
|
|
games.append(gi.id)
|
|
elif type == 'not played' and won + lost == 0:
|
|
games.append(gi.id)
|
|
return games
|
|
|
|
def _mSelectNextGameFromList(self, gl, step):
|
|
if self._cancelDrag():
|
|
return
|
|
id = self.game.id
|
|
gl = list(gl)
|
|
if len(gl) < 2 or (id not in gl):
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("Select next game")):
|
|
return
|
|
index = (gl.index(id) + step) % len(gl)
|
|
self.game.endGame()
|
|
self.game.quitGame(gl[index])
|
|
|
|
def mSelectNextGameById(self, *args):
|
|
self._mSelectNextGameFromList(self.app.gdb.getGamesIdSortedById(), 1)
|
|
|
|
def mSelectPrevGameById(self, *args):
|
|
self._mSelectNextGameFromList(self.app.gdb.getGamesIdSortedById(), -1)
|
|
|
|
def mSelectNextGameByName(self, *args):
|
|
self._mSelectNextGameFromList(self.app.gdb.getGamesIdSortedByName(), 1)
|
|
|
|
def mSelectPrevGameByName(self, *args):
|
|
self._mSelectNextGameFromList(
|
|
self.app.gdb.getGamesIdSortedByName(), -1)
|
|
|
|
def mSave(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
if self.menustate.save_as:
|
|
if self.game.filename:
|
|
self.game.saveGame(self.game.filename)
|
|
else:
|
|
self.mSaveAs()
|
|
|
|
def mHoldAndQuit(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
self.game.endGame(holdgame=1)
|
|
self.game.quitGame(holdgame=1)
|
|
|
|
def mQuit(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.changed():
|
|
if not self.game.areYouSure(_("Quit %s") % TITLE):
|
|
return
|
|
self.game.endGame()
|
|
self.game.quitGame()
|
|
|
|
#
|
|
# Edit menu
|
|
#
|
|
|
|
def mUndo(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.menustate.undo:
|
|
self.game.playSample("undo")
|
|
self.game.undo()
|
|
|
|
def mRedo(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.menustate.redo:
|
|
self.game.playSample("redo")
|
|
self.game.redo()
|
|
self.game.checkForWin()
|
|
|
|
def mRedoAll(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.menustate.redo:
|
|
self.app.top.busyUpdate()
|
|
self.game.playSample("redo", loop=1)
|
|
while self.game.moves.index < len(self.game.moves.history):
|
|
self.game.redo()
|
|
if self.game.checkForWin():
|
|
break
|
|
self.game.stopSamples()
|
|
|
|
def mSetBookmark(self, n, confirm=1):
|
|
if self._cancelDrag():
|
|
return
|
|
if not self.app.opt.bookmarks:
|
|
return
|
|
if not (0 <= n <= 8):
|
|
return
|
|
self.game.setBookmark(n, confirm=confirm)
|
|
self.game.updateMenus()
|
|
|
|
def mGotoBookmark(self, n, confirm=-1):
|
|
if self._cancelDrag():
|
|
return
|
|
if not self.app.opt.bookmarks:
|
|
return
|
|
if not (0 <= n <= 8):
|
|
return
|
|
self.game.gotoBookmark(n, confirm=confirm)
|
|
self.game.updateMenus()
|
|
|
|
def mClearBookmarks(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if not self.app.opt.bookmarks:
|
|
return
|
|
if not self.game.gsaveinfo.bookmarks:
|
|
return
|
|
if not self.game.areYouSure(_("Clear bookmarks"),
|
|
_("Clear all bookmarks?")):
|
|
return
|
|
self.game.gsaveinfo.bookmarks = {}
|
|
self.game.updateMenus()
|
|
|
|
def mRestart(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.game.moves.index == 0:
|
|
return
|
|
if self.changed(restart=1):
|
|
if not self.game.areYouSure(_("Restart game"),
|
|
_("Restart this game?")):
|
|
return
|
|
self.game.restartGame()
|
|
|
|
#
|
|
# Game menu
|
|
#
|
|
|
|
def mDeal(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
self.game.dealCards()
|
|
|
|
def mDrop(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
# self.game.autoPlay(autofaceup=-1, autodrop=1)
|
|
self.game.autoDrop(autofaceup=-1)
|
|
|
|
def mDrop1(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
# self.game.autoPlay(autofaceup=1, autodrop=1)
|
|
self.game.autoDrop(autofaceup=1)
|
|
|
|
def mShuffle(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.menustate.shuffle:
|
|
if self.game.canShuffle():
|
|
self.game._mahjonggShuffle()
|
|
|
|
def mFindCard(self, *args):
|
|
if self.game.canFindCard():
|
|
create_find_card_dialog(self.game.top, self.game,
|
|
self.app.getFindCardImagesDir())
|
|
raise_find_card_dialog()
|
|
|
|
def mFullPicture(self, *args):
|
|
if self.game.canShowFullPicture():
|
|
create_full_picture_dialog(self.game.top, self.game)
|
|
raise_full_picture_dialog()
|
|
|
|
def mSolver(self, *args):
|
|
create_solver_dialog(self.game.top, self.app)
|
|
|
|
def mEditGameComment(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
game, gi = self.game, self.game.gameinfo
|
|
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 %(id)s") % kw, text=c)
|
|
if d.status == 0 and d.button == 0:
|
|
text = d.text
|
|
if text.strip() == cc.strip():
|
|
game.gsaveinfo.comment = ""
|
|
else:
|
|
game.gsaveinfo.comment = d.text
|
|
# save to file
|
|
fn = os.path.join(self.app.dn.config, "comments.txt")
|
|
fn = os.path.normpath(fn)
|
|
if not text.endswith(os.linesep):
|
|
text += os.linesep
|
|
try:
|
|
with open(fn, 'at') as fh:
|
|
fh.write(d.text)
|
|
except Exception as err:
|
|
MfxExceptionDialog(
|
|
self.top, err, text=_("Error while writing to file"))
|
|
else:
|
|
MfxMessageDialog(
|
|
self.top, title=_("%s Info") % TITLE, bitmap="info",
|
|
text=_("Comments were appended to\n\n%(filename)s")
|
|
% {'filename': fn})
|
|
|
|
#
|
|
# Game menu - statistics
|
|
#
|
|
|
|
def _mStatsSave(self, player, filename, write_method):
|
|
if player is None:
|
|
text = _("Demo statistics were appended to\n\n%(filename)s")
|
|
filename = filename + "_demo"
|
|
else:
|
|
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:
|
|
a = FileStatsFormatter(self.app, open(filename, "a"))
|
|
write_method(a, player)
|
|
except EnvironmentError as ex:
|
|
MfxExceptionDialog(self.top, ex,
|
|
text=_("Error while writing to file"))
|
|
else:
|
|
MfxMessageDialog(
|
|
self.top, title=_("%s Info") % TITLE, bitmap="info",
|
|
text=text % {'filename': filename})
|
|
|
|
def mPlayerStats(self, *args, **kw):
|
|
wasPaused = False
|
|
if not self.game.pause:
|
|
self.game.doPause()
|
|
wasPaused = True
|
|
mode = kw.get("mode", 101)
|
|
demo = 0
|
|
gameid = None
|
|
gamenum = None
|
|
while mode > 0:
|
|
if mode > 1000:
|
|
demo = not demo
|
|
mode = mode % 1000
|
|
#
|
|
d = Struct(status=-1, button=-1)
|
|
if demo:
|
|
player = None
|
|
else:
|
|
player = self.app.opt.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 = (_("%(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 = (_("%(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 = (_("%(app)s Demo Full log") if demo
|
|
else _("Full log for %(player)s")) % transkw
|
|
d = FullLog_StatsDialog(self.top, header, self.app, player)
|
|
gameid = d.selected_game
|
|
gamenum = d.selected_game_num
|
|
elif mode == 104:
|
|
header = (_("%(app)s Demo Session log") if demo
|
|
else _("Session log for %(player)s")) % transkw
|
|
d = SessionLog_StatsDialog(self.top, header, self.app, player)
|
|
gameid = d.selected_game
|
|
gamenum = d.selected_game_num
|
|
elif mode == 105:
|
|
# 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:
|
|
header = _("Game Info")
|
|
d = GameInfoDialog(self.top, header, self.app)
|
|
elif mode == 107:
|
|
header = _("Statistics progression")
|
|
d = ProgressionDialog(
|
|
self.top, header, self.app, player, gameid=self.game.id)
|
|
elif mode == 202:
|
|
# print stats to file
|
|
write_method = FileStatsFormatter.writeStats
|
|
self._mStatsSave(player, "stats", write_method)
|
|
elif mode == 203:
|
|
# print full log to file
|
|
write_method = FileStatsFormatter.writeFullLog
|
|
self._mStatsSave(player, "log", write_method)
|
|
elif mode == 204:
|
|
# print session log to file
|
|
write_method = FileStatsFormatter.writeSessionLog
|
|
self._mStatsSave(player, "log", write_method)
|
|
elif mode == 301:
|
|
# reset all player stats
|
|
if self.game.areYouSure(
|
|
_("Reset all statistics"),
|
|
_("Reset ALL statistics and logs for player\n" +
|
|
"%(player)s?") % transkw,
|
|
confirm=1, default=1
|
|
):
|
|
self.app.stats.resetStats(player, 0)
|
|
self.game.updateStatus(stats=self.app.stats.getStats(
|
|
self.app.opt.player, self.game.id))
|
|
elif mode == 302:
|
|
# reset player stats for current game
|
|
if self.game.areYouSure(
|
|
_("Reset game statistics"),
|
|
_('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)
|
|
self.game.updateStatus(stats=self.app.stats.getStats(
|
|
self.app.opt.player, self.game.id))
|
|
elif mode == 401:
|
|
# start a new game with a gameid
|
|
if gameid and gameid != self.game.id:
|
|
self.game.endGame()
|
|
self.game.quitGame(gameid)
|
|
elif mode == 402:
|
|
# start a new game with a gameid / gamenumber
|
|
if (gameid and gamenum and
|
|
(gameid != self.game.id or
|
|
gamenum != self.game.getGameNumber(format=0))):
|
|
self.game.endGame()
|
|
self.game.quitGame(gameid,
|
|
random=construct_random(gamenum))
|
|
else:
|
|
print_err("stats problem: %s %s %s" % (mode, demo, player))
|
|
pass
|
|
if d.status != 0:
|
|
break
|
|
mode = d.button
|
|
if self.game.pause:
|
|
if wasPaused:
|
|
self.game.doPause()
|
|
|
|
#
|
|
# Assist menu
|
|
#
|
|
|
|
def mHint(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.app.opt.hint:
|
|
if self.game.showHint(0, self.app.opt.timeouts['hint']):
|
|
self.game.stats.hints += 1
|
|
|
|
def mHint1(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.app.opt.hint:
|
|
if self.game.showHint(1, self.app.opt.timeouts['hint']):
|
|
self.game.stats.hints += 1
|
|
|
|
def mHighlightPiles(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.app.opt.highlight_piles:
|
|
if self.game.highlightPiles(
|
|
self.app.opt.timeouts['highlight_piles']
|
|
):
|
|
self.game.stats.highlight_piles += 1
|
|
|
|
def mDemo(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
if self.game.getHintClass() is not None:
|
|
self._mDemo(mixed=0)
|
|
|
|
def mMixedDemo(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
self._mDemo(mixed=1)
|
|
|
|
def _mDemo(self, mixed):
|
|
if self.changed():
|
|
# only ask if there have been no demo moves or hints yet
|
|
if self.game.stats.demo_moves == 0 and self.game.stats.hints == 0:
|
|
if not self.game.areYouSure(_("Play demo")):
|
|
return
|
|
# self.app.demo_counter = 0
|
|
self.game.startDemo(mixed=mixed)
|
|
|
|
#
|
|
# Options menu
|
|
#
|
|
|
|
def mOptPlayerOptions(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
d = PlayerOptionsDialog(self.top, _("Set player options"), self.app)
|
|
if d.status == 0 and d.button == 0:
|
|
self.app.opt.confirm = bool(d.confirm)
|
|
self.app.opt.update_player_stats = bool(d.update_stats)
|
|
self.app.opt.win_animation = bool(d.win_animation)
|
|
# n = string.strip(d.player)
|
|
n = d.player[:30].strip()
|
|
if 0 < len(n) <= 30:
|
|
self.app.opt.player = n
|
|
self.game.updateStatus(player=self.app.opt.player)
|
|
self.game.updateStatus(stats=self.app.stats.getStats(
|
|
self.app.opt.player, self.game.id))
|
|
self.updateMenus()
|
|
|
|
def mOptColors(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
d = ColorsDialog(self.top, _("Set colors"), self.app)
|
|
text_color = self.app.opt.colors['text']
|
|
if d.status == 0 and d.button == 0:
|
|
self.app.opt.colors['text'] = d.text_color
|
|
self.app.opt.colors['piles'] = d.piles_color
|
|
self.app.opt.colors['cards_1'] = d.cards_1_color
|
|
self.app.opt.colors['cards_2'] = d.cards_2_color
|
|
self.app.opt.colors['samerank_1'] = d.samerank_1_color
|
|
self.app.opt.colors['samerank_2'] = d.samerank_2_color
|
|
self.app.opt.colors['hintarrow'] = d.hintarrow_color
|
|
self.app.opt.colors['not_matching'] = d.not_matching_color
|
|
#
|
|
if text_color != self.app.opt.colors['text']:
|
|
self.app.setTile(self.app.tabletile_index, force=True)
|
|
|
|
def mOptFonts(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
d = FontsDialog(self.top, _("Set fonts"), self.app)
|
|
if d.status == 0 and d.button == 0:
|
|
self.app.opt.fonts.update(d.fonts)
|
|
self._cancelDrag()
|
|
self.game.endGame(bookmark=1)
|
|
self.game.quitGame(bookmark=1)
|
|
|
|
def mOptTimeouts(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
d = TimeoutsDialog(self.top, _("Set timeouts"), self.app)
|
|
if d.status == 0 and d.button == 0:
|
|
self.app.opt.timeouts['demo'] = d.demo_timeout
|
|
self.app.opt.timeouts['hint'] = d.hint_timeout
|
|
self.app.opt.timeouts['raise_card'] = d.raise_card_timeout
|
|
self.app.opt.timeouts['highlight_piles'] = \
|
|
d.highlight_piles_timeout
|
|
self.app.opt.timeouts['highlight_cards'] = \
|
|
d.highlight_cards_timeout
|
|
self.app.opt.timeouts['highlight_samerank'] = \
|
|
d.highlight_samerank_timeout
|
|
|
|
#
|
|
# Help menu
|
|
#
|
|
|
|
def mHelpHtml(self, *args):
|
|
print('mHelpHtml: %s' % str(args))
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_html(self.app, args[0], "html")
|
|
|
|
def mHelp(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_html(self.app, "index.html", "html")
|
|
|
|
def mHelpHowToPlay(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_html(self.app, "howtoplay.html", "html")
|
|
|
|
def mHelpRules(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
if not self.menustate.rules:
|
|
return
|
|
dir = os.path.join("html", "rules")
|
|
# FIXME: plugins
|
|
help_html(self.app, self.app.getGameRulesFilename(self.game.id), dir)
|
|
|
|
def mHelpReportBug(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_html(self.app, "report_bug.html", "html")
|
|
|
|
def mHelpLicense(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_html(self.app, "license.html", "html")
|
|
|
|
def mHelpNews(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_html(self.app, "news.html", "html")
|
|
|
|
def mHelpWebSite(self, *args):
|
|
openURL(PACKAGE_URL)
|
|
|
|
def mHelpAbout(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
help_about(self.app)
|
|
|
|
#
|
|
# misc
|
|
#
|
|
|
|
def mScreenshot(self, *args):
|
|
if self._cancelDrag():
|
|
return
|
|
f = os.path.join(self.app.dn.config, "screenshots")
|
|
if not os.path.isdir(f):
|
|
return
|
|
f = os.path.join(f, self.app.getGameSaveName(self.game.id))
|
|
i = 1
|
|
while 1:
|
|
fn = "%s-%d.ppm" % (f, i)
|
|
if not os.path.exists(fn):
|
|
break
|
|
i = i + 1
|
|
if i >= 10000: # give up
|
|
return
|
|
self.top.screenshot(fn)
|
|
|
|
def mPlayNextMusic(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
if (self.app.audio and self.app.opt.music and
|
|
self.app.opt.sound_music_volume > 0):
|
|
self.app.audio.playNextMusic()
|
|
if 1 and DEBUG:
|
|
index = self.app.audio.getMusicInfo()
|
|
music = self.app.music_manager.get(index)
|
|
if music:
|
|
print("playing music:", music.filename)
|
|
|
|
def mIconify(self, *args):
|
|
if self._cancelDrag(break_pause=False):
|
|
return
|
|
self.top.wm_iconify()
|
|
|
|
|
|
# ************************************************************************
|
|
# * toolbar
|
|
# ************************************************************************
|
|
|
|
class PysolToolbar(PysolToolbarTk):
|
|
def __init__(self, *args, **kwargs):
|
|
self.game = None
|
|
PysolToolbarTk.__init__(self, *args, **kwargs)
|
|
|
|
#
|
|
# public methods
|
|
#
|
|
|
|
def connectGame(self, game):
|
|
self.game = game
|
|
|
|
#
|
|
# button event handlers - delegate to menubar
|
|
#
|
|
|
|
def mNewGame(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mNewGame()
|
|
return 1
|
|
|
|
def mOpen(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mOpen()
|
|
return 1
|
|
|
|
def mRestart(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mRestart()
|
|
return 1
|
|
|
|
def mSave(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mSaveAs()
|
|
return 1
|
|
|
|
def mUndo(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mUndo()
|
|
return 1
|
|
|
|
def mRedo(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mRedo()
|
|
return 1
|
|
|
|
def mDrop(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mDrop()
|
|
return 1
|
|
|
|
def mShuffle(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mShuffle()
|
|
return 1
|
|
|
|
def mHint(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mHint()
|
|
return 1
|
|
|
|
def mPause(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mPause()
|
|
return 1
|
|
|
|
def mPlayerStats(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mPlayerStats()
|
|
return 1
|
|
|
|
def mHelpRules(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mHelpRules()
|
|
return 1
|
|
|
|
def mQuit(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mQuit()
|
|
return 1
|
|
|
|
def mOptPlayerOptions(self, *args):
|
|
if not self._busy():
|
|
self.menubar.mOptPlayerOptions()
|
|
return 1
|