#!/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) 2017 LB
#
# 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 collections
import math
import os
import re

from kivy.cache import Cache
from kivy.clock import Clock
from kivy.core.window import Window

from pysollib.gamedb import GI
from pysollib.kivy.LApp import LMenu
from pysollib.kivy.LApp import LMenuItem
from pysollib.kivy.LApp import LScrollView
from pysollib.kivy.LApp import LTopLevel
from pysollib.kivy.LApp import LTreeNode
from pysollib.kivy.LApp import LTreeRoot
from pysollib.kivy.LApp import LTreeSliderNode
from pysollib.kivy.LObjWrap import LBoolWrap
from pysollib.kivy.LObjWrap import LListWrap
from pysollib.kivy.LObjWrap import LNumWrap
from pysollib.kivy.LObjWrap import LStringWrap
from pysollib.kivy.androidrot import AndroidScreenRotation
from pysollib.kivy.findcarddialog import destroy_find_card_dialog
from pysollib.kivy.fullpicturedialog import destroy_full_picture_dialog
from pysollib.kivy.selectcardset import SelectCardsetDialogWithPreview
from pysollib.kivy.selectgame import SelectGameDialog
from pysollib.kivy.solverdialog import connect_game_solver_dialog
from pysollib.kivy.tkconst import CURSOR_WATCH, EVENT_HANDLED, EVENT_PROPAGATE
from pysollib.kivy.tkconst import TOOLBAR_BUTTONS
from pysollib.kivy.tkutil import after_idle
from pysollib.kivy.tkutil import bind
from pysollib.kivy.toast import Toast
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.pysoltk import connect_game_full_picture_dialog
from pysollib.settings import SELECT_GAME_MENU
from pysollib.settings import TITLE

# ************************************************************************
# * Common base
# ************************************************************************


class LMenuBase:
    def __init__(self, menubar, parent, title, app):
        self.menubar = menubar
        self.parent = parent
        self.app = app
        self.title = title

    def make_pop_command(self, parent, title):
        def pop_command(event):
            parent.popWork(title)
        return pop_command

    def closeWindow(self, event):
        self.parent.popWork(self.title)

    def auto_close(self, command):
        def auto_close_command():
            if command is not None:
                command()
            self.closeWindow(0)
        return auto_close_command

    def make_toggle_command(self, variable, command):
        def auto_command():
            variable.value = not variable.value
            if command is not None:
                command()
        return auto_command

    def make_val_command(self, variable, value, command):
        def val_command():
            variable.value = value
            if command is not None:
                command()
        return val_command

    def make_vars_command(self, command, key):
        def vars_command():
            command(key)
        return vars_command

    def make_game_command(self, key, command):
        def game_command():
            self.closeWindow(0)
            command(key)
        return game_command

    def make_command(self, command):
        def _command():
            self.closeWindow(0)
            if command is not None:
                command()
        return _command

    def addCheckNode(self, tv, rg, title, auto_var, auto_com):
        command = self.make_toggle_command(auto_var, auto_com)
        rg1 = tv.add_node(
            LTreeNode(text=title, command=command, variable=auto_var), rg)
        return rg1

    def addRadioNode(self, tv, rg, title, auto_var, auto_val, auto_com):
        command = self.make_val_command(auto_var, auto_val, auto_com)
        rg1 = tv.add_node(
            LTreeNode(text=title,
                      command=command,
                      variable=auto_var, value=auto_val), rg)
        return rg1

    def addSliderNode(self, tv, rg, auto_var, auto_setup):
        rg1 = tv.add_node(
            LTreeSliderNode(variable=auto_var, setup=auto_setup), rg)
        return rg1

# ************************************************************************
# * Tree Generators
# ************************************************************************


class LTreeGenerator(LMenuBase):
    def generate(self):
        tv = LTreeRoot(root_options={'text': 'EditTree'})
        tv.hide_root = True
        tv.size_hint = 1, None
        tv.bind(minimum_height=tv.setter('height'))

        gen = self.buildTree(tv, None)

        def process(dt):
            try:
                gen.send(None)
                Clock.schedule_once(process, 0.2)
            except StopIteration:
                print('generator: all jobs done')

        Clock.schedule_once(process, 0.2)
        return tv

    def buildTree(self, tv, node):
        print('buildTree generator function not implemented')
        # needs at least on 'yield' statement
        # not reentrant: do not recurse !
        # implement it in a dervied class
        yield

# ************************************************************************
# * Menu Dialogs
# ************************************************************************


class LMenuDialog(LMenuBase):

    dialogCache = {}

    def __init__(self, menubar, parent, title, app, **kw):
        super().__init__(menubar, parent, title, app)

        self.window = None
        self.running = False
        self.persist = False
        if 'persist' in kw:
            self.persist = kw['persist']
        self.tvroot = None
        if 'tv' in kw:
            self.tvroot = kw['tv']

        # prüfen ob noch aktiv - toggle.

        if parent.workStack.peek(title) is not None:
            parent.popWork(title)
            return

        if self.persist and title in self.dialogCache:
            parent.pushWork(title, self.dialogCache[title])
            return

        pc = self.closeWindow = self.make_pop_command(parent, title)

        # neuen Dialog aufbauen.

        window = LTopLevel(parent, title, **kw)
        window.titleline.bind(on_press=pc)
        self.parent.pushWork(title, window)
        self.window = window
        self.running = True

        from pysollib.kivy.LApp import get_menu_size_hint

        def updrule(obj, val):
            self.window.size_hint = get_menu_size_hint()
        updrule(0, 0)
        self.parent.bind(size=updrule)

        if self.persist:
            self.dialogCache[title] = window

        # Tree construct or assign.

        if self.tvroot is None:
            tv = self.initTree()
            self.buildTree(tv, None)
        else:
            tv = self.tvroot

        # show the tree in a scroll window

        root = LScrollView(pos=(0, 0))
        root.add_widget(tv)
        self.window.content.add_widget(root)

    def initTree(self):
        tv = self.tvroot = LTreeRoot(root_options={'text': 'EditTree'})
        tv.hide_root = True
        tv.size_hint = 1, None
        tv.bind(minimum_height=tv.setter('height'))
        return tv

    def buildTree(self, tree, node):
        # to implement in dervied class if needed
        pass

# ************************************************************************


class MainMenuDialog(LMenuDialog):

    def __init__(self, menubar, parent, title, app, **kw):
        kw['persist'] = True
        super().__init__(
            menubar, parent, title, app, **kw)

        # print('MainMenuDialog starting')

    def buildTree(self, tv, node):
        rg = tv.add_node(
            LTreeNode(
                text=_("File"),
                command=self.auto_close(self.menubar.mFileMenuDialog)))
        rg = tv.add_node(
            LTreeNode(
                text=_("Games"),
                command=self.auto_close(
                    self.menubar.mSelectGameDialog)))
        rg = tv.add_node(
            LTreeNode(
                text=_("Tools"),
                command=self.auto_close(self.menubar.mEditMenuDialog)))
        rg = tv.add_node(
            LTreeNode(
                text=_("Statistics"),
                command=self.auto_close(self.menubar.mGameMenuDialog)))
        rg = tv.add_node(
            LTreeNode(
                text=_("Assist"),
                command=self.auto_close(
                    self.menubar.mAssistMenuDialog)))
        rg = tv.add_node(
            LTreeNode(
                text=_("Options"),
                command=self.auto_close(
                    self.menubar.mOptionsMenuDialog)))
        rg = tv.add_node(
            LTreeNode(
                text=_("Help"),
                command=self.auto_close(self.menubar.mHelpMenuDialog)))
        del rg

# ************************************************************************


class FileMenuDialog(LMenuDialog):
    def make_favid_list(self, tv, rg):
        favids = self.app.opt.favorite_gameid
        for fid in favids:
            gi = self.app.getGameInfo(fid)
            if gi:
                command = self.make_game_command(
                    fid, self.menubar._mSelectGame)
                tv.add_node(
                    LTreeNode(text=gi.name, command=command), rg)

    def remove_favid_list(self, tv, rg):
        delist = []
        for n in rg.nodes:
            if n.text not in [_('<Add>'), _('<Remove>')]:
                delist.append(n)
        for m in delist:
            tv.remove_node(m)

    def change_favid_list(self, command, *args):
        def doit():
            command()
            self.remove_favid_list(args[0], args[1])
            self.make_favid_list(args[0], args[1])
        return doit

    def buildTree(self, tv, node):
        rg = tv.add_node(
            LTreeNode(text=_('Recent games')))
        recids = self.app.opt.recent_gameid
        for rid in recids:
            gi = self.app.getGameInfo(rid)
            if gi:
                command = self.make_game_command(
                    rid, self.menubar._mSelectGame)
                tv.add_node(
                    LTreeNode(text=gi.name, command=command), rg)

        rg = tv.add_node(
            LTreeNode(text=_('Favorite games')))
        if rg:
            tv.add_node(LTreeNode(
                text=_('<Add>'),
                command=self.change_favid_list(
                    self.menubar.mAddFavor, tv, rg)), rg)
            tv.add_node(LTreeNode(
                text=_('<Remove>'),
                command=self.change_favid_list(
                    self.menubar.mDelFavor, tv, rg)), rg)

            self.make_favid_list(tv, rg)

        tv.add_node(LTreeNode(
            text=_('Random game'),
            command=self.make_command(self.menubar.mSelectRandomGame)))
        tv.add_node(LTreeNode(
            text=_('Load'), command=self.make_command(self.menubar.mOpen)))
        tv.add_node(LTreeNode(
            text=_('Save'), command=self.make_command(self.menubar.mSaveAs)))

        tv.add_node(LTreeNode(
            text=_('Quit'), command=self.menubar.mHoldAndQuit))

# ************************************************************************


class EditMenuDialog(LMenuDialog):  # Tools

    def __init__(self, menubar, parent, title, app, **kw):
        kw['persist'] = True
        super().__init__(
            menubar, parent, title, app, **kw)

    def buildTree(self, tv, node):
        tv.add_node(LTreeNode(
            text=_('New game'), command=self.menubar.mNewGame))
        tv.add_node(LTreeNode(
            text=_('Restart game'), command=self.menubar.mRestart))

        tv.add_node(LTreeNode(
            text=_('Undo'), command=self.menubar.mUndo))
        tv.add_node(LTreeNode(
            text=_('Redo'), command=self.menubar.mRedo))
        tv.add_node(LTreeNode(
            text=_('Redo all'), command=self.menubar.mRedoAll))

        tv.add_node(LTreeNode(
            text=_('Auto drop'), command=self.menubar.mDrop))
        tv.add_node(LTreeNode(
            text=_('Shuffle tiles'), command=self.menubar.mShuffle))
        tv.add_node(LTreeNode(
            text=_('Deal cards'), command=self.menubar.mDeal))
        tv.add_node(LTreeNode(
            text=_('Reset zoom'),
            command=self.auto_close(self.menubar.mResetZoom)))

        self.addCheckNode(tv, None,
                          _('Pause'),
                          self.menubar.tkopt.pause,
                          self.menubar.mPause)

        tv.add_node(LTreeNode(
            text=_('Load game'), command=self.menubar.mOpen))
        tv.add_node(LTreeNode(
            text=_('Save game'), command=self.menubar.mSaveAs))

        tv.add_node(LTreeNode(
            text=_('Help'), command=self.menubar.mHelpRules))

        # -------------------------------------------
        # TBD ?
        '''
        menu.add_separator()
        submenu = MfxMenu(menu, label=n_("&Set bookmark"))
        for i in range(9):
            label = _("Bookmark %d") % (i + 1)
            submenu.add_command(
                label=label, command=lambda i=i: self.mSetBookmark(i))
        submenu = MfxMenu(menu, label=n_("Go&to bookmark"))
        for i in range(9):
            label = _("Bookmark %d") % (i + 1)
            acc = m + "%d" % (i + 1)
            submenu.add_command(
                label=label,
                command=lambda i=i: self.mGotoBookmark(i),
                accelerator=acc)
        menu.add_command(
            label=n_("&Clear bookmarks"), command=self.mClearBookmarks)
        menu.add_separator()
        '''
        # und solitär wizard (-> custom games).
        '''
        tv.add_node(LTreeNode(
            text='Solitaire &Wizard...', command=self.menubar.mWizard))
        tv.add_node(LTreeNode(
                text='Edit current game', command=self.menubar.mWizardEdit))
        '''

# ************************************************************************


class GameMenuDialog(LMenuDialog):

    def __init__(self, menubar, parent, title, app, **kw):
        kw['persist'] = True
        super().__init__(
            menubar, parent, title, app, **kw)

    def make_command(self, key, command):
        def stats_command():
            kw = {}
            kw['mode'] = key
            command(**kw)
        return stats_command

    def buildTree(self, tv, node):
        tv.add_node(LTreeNode(
            text=_('Current game...'),
            command=self.auto_close(
                self.make_command(101, self.menubar.mPlayerStats))), None)

        # tv.add_node(LTreeNode(
        #   text='All games ...',
        #   command=self.make_command(102, self.menubar.mPlayerStats)), None)

    # -------------------------------------------
    # TBD ? - just to remember original tk code.
    '''
        menu.add_command(
            label=n_("S&tatus..."),
            command=lambda x: self.mPlayerStats(mode=100), accelerator=m+"Y")
        menu.add_checkbutton(
            label=n_("&Comments..."), variable=self.tkopt.comment,
            command=self.mEditGameComment)
    '''
    '''
        menu.add_separator()
        submenu = MfxMenu(menu, label=n_("&Statistics"))
        submenu.add_command(
            label=n_("Current game..."),
            command=lambda x: self.mPlayerStats(mode=101))
        submenu.add_command(
            label=n_("All games..."),
            command=lambda x: self.mPlayerStats(mode=102))
        submenu.add_separator()
        submenu.add_command(
            label=n_("Session log..."),
            command=lambda x: self.mPlayerStats(mode=104))
        submenu.add_command(
            label=n_("Full log..."),
            command=lambda x: self.mPlayerStats(mode=103))
        submenu.add_separator()
        submenu.add_command(
            label=TOP_TITLE+"...",
            command=lambda x: self.mPlayerStats(mode=105),
            accelerator=m+"T")
        submenu.add_command(
            label=n_("Progression..."),
            command=lambda x: self.mPlayerStats(mode=107))
        submenu = MfxMenu(menu, label=n_("D&emo statistics..."))
        submenu.add_command(
            label=n_("Current game..."),
            command=lambda x: self.mPlayerStats(mode=1101))
        submenu.add_command(
            label=n_("All games..."),
            command=lambda x: self.mPlayerStats(mode=1102))
    '''

# ************************************************************************


class AssistMenuDialog(LMenuDialog):

    def __init__(self, menubar, parent, title, app, **kw):
        kw['persist'] = True
        super().__init__(
            menubar, parent, title, app, **kw)

    def buildTree(self, tv, node):
        tv.add_node(LTreeNode(
            text=_('Hint'), command=self.menubar.mHint))

        tv.add_node(LTreeNode(
            text=_('Highlight piles'), command=self.menubar.mHighlightPiles))

        tv.add_node(LTreeNode(
            text=_('Show full picture...'),
            command=self.auto_close(self.menubar.mFullPicture)))

        # tv.add_node(LTreeNode(
        #   text='Find Card', command=self.menubar.mFindCard))

        tv.add_node(LTreeNode(
            text=_('Demo'), command=self.auto_close(self.menubar.mDemo)))

        # -------------------------------------------
        # TBD. How ?

        '''
        menu.add_command(
            label=n_("Demo (&all games)"), command=self.mMixedDemo)
        if USE_FREECELL_SOLVER:
            menu.add_command(label=n_("&Solver..."), command=self.mSolver)
        else:
            menu.add_command(label=n_("&Solver..."), state='disabled')
        menu.add_separator()
        menu.add_command(
            label=n_("&Piles description"),
            command=self.mStackDesk, accelerator="F2")
        '''

# ************************************************************************


class LOptionsMenuGenerator(LTreeGenerator):
    def buildTree(self, tv, node):
        # -------------------------------------------
        # Automatic play settings

        rg = tv.add_node(
            LTreeNode(text=_('Automatic play')))
        if rg:
            self.addCheckNode(tv, rg,
                              _('Auto face-up'),
                              self.menubar.tkopt.autofaceup,
                              self.menubar.mOptAutoFaceUp)

            self.addCheckNode(tv, rg,
                              _('Auto drop'),
                              self.menubar.tkopt.autodrop,
                              self.menubar.mOptAutoDrop)

            self.addCheckNode(tv, rg,
                              _('Auto deal'),
                              self.menubar.tkopt.autodeal,
                              self.menubar.mOptAutoDeal)

            # submenu.add_separator()

            self.addCheckNode(tv, rg,
                              _('Quick play'),
                              self.menubar.tkopt.quickplay,
                              self.menubar.mOptQuickPlay)

        yield
        # -------------------------------------------
        # Player assistance

        rg = tv.add_node(
            LTreeNode(text=_('Assist level')))
        if rg:
            self.addCheckNode(tv, rg,
                              _('Enable undo'),
                              self.menubar.tkopt.undo,
                              self.menubar.mOptEnableUndo)

            self.addCheckNode(tv, rg,
                              _('Enable bookmarks'),
                              self.menubar.tkopt.bookmarks,
                              self.menubar.mOptEnableBookmarks)

            self.addCheckNode(tv, rg,
                              _('Enable hint'),
                              self.menubar.tkopt.hint,
                              self.menubar.mOptEnableHint)

            self.addCheckNode(tv, rg,
                              _('Enable shuffle'),
                              self.menubar.tkopt.shuffle,
                              self.menubar.mOptEnableShuffle)

            self.addCheckNode(tv, rg,
                              _('Free hints'),
                              self.menubar.tkopt.free_hint,
                              self.menubar.mOptFreeHints)

            self.addCheckNode(tv, rg,
                              _('Enable highlight piles'),
                              self.menubar.tkopt.highlight_piles,
                              self.menubar.mOptEnableHighlightPiles)

            self.addCheckNode(tv, rg,
                              _('Enable highlight cards'),
                              self.menubar.tkopt.highlight_cards,
                              self.menubar.mOptEnableHighlightCards)

            self.addCheckNode(tv, rg,
                              _('Enable highlight same rank'),
                              self.menubar.tkopt.highlight_samerank,
                              self.menubar.mOptEnableHighlightSameRank)

            self.addCheckNode(tv, rg,
                              _('Enable face-down peek'),
                              self.menubar.tkopt.peek_facedown,
                              self.menubar.mOptEnablePeekFacedown)

            self.addCheckNode(tv, rg,
                              _('Highlight no matching'),
                              self.menubar.tkopt.highlight_not_matching,
                              self.menubar.mOptEnableHighlightNotMatching)

            self.addCheckNode(tv, rg,
                              _('Stuck notification'),
                              self.menubar.tkopt.stuck_notification,
                              self.menubar.mOptEnableStuckNotification)

            # submenu.add_separator()

            self.addCheckNode(tv, rg,
                              _('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)'),
                              self.menubar.tkopt.shisen_show_hint,
                              self.menubar.mOptShisenShowHint)

            self.addCheckNode(tv, rg,
                              _('Deal all cards (in Accordion type games)'),
                              self.menubar.tkopt.accordion_deal_all,
                              self.menubar.mOptAccordionDealAll)

            self.addCheckNode(tv, rg,
                              _('Auto-remove first card (in Pegged games)'),
                              self.menubar.tkopt.accordion_deal_all,
                              self.menubar.mOptPeggedAutoRemove)

            # submenu.add_separator()

        yield
        # -------------------------------------------
        # 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)

        yield
        # -------------------------------------------
        # Sound options

        rg = tv.add_node(
            LTreeNode(text=_('Sound')))
        if rg:
            self.addCheckNode(tv, rg,
                              _('Enable'),
                              self.menubar.tkopt.sound, None)

            rg1 = tv.add_node(
                LTreeNode(text=_('Volume')), rg)
            if rg1:
                self.addRadioNode(tv, rg1,
                                  _('100%'),
                                  self.menubar.tkopt.sound_sample_volume, 100,
                                  None)
                self.addRadioNode(tv, rg1,
                                  _('75%'),
                                  self.menubar.tkopt.sound_sample_volume, 75,
                                  None)
                self.addRadioNode(tv, rg1,
                                  _('50%'),
                                  self.menubar.tkopt.sound_sample_volume, 50,
                                  None)
                self.addRadioNode(tv, rg1,
                                  _('25%'),
                                  self.menubar.tkopt.sound_sample_volume, 25,
                                  None)

            rg1 = tv.add_node(
                LTreeNode(text=_('Samples')), rg)
            if rg1:
                key = 'areyousure'
                self.addCheckNode(
                    tv, rg1,
                    _('are you sure'),
                    self.menubar.tkopt.sound_areyousure, None)
                key = 'autodrop'
                self.addCheckNode(
                    tv, rg1,
                    _('auto drop'),
                    self.menubar.tkopt.sound_autodrop, None)
                key = 'autoflip'
                self.addCheckNode(
                    tv, rg1,
                    _('auto flip'),
                    self.menubar.tkopt.sound_autoflip, None)
                key = 'autopilotlost'
                self.addCheckNode(
                    tv, rg1,
                    _('auto pilot lost'),
                    self.menubar.tkopt.sound_autopilotlost, None)
                key = 'autopilotwon'
                self.addCheckNode(
                    tv, rg1,
                    _('auto pilot won'),
                    self.menubar.tkopt.sound_autopilotwon, None)
                key = 'deal'
                self.addCheckNode(
                    tv, rg1,
                    _('deal'),
                    self.menubar.tkopt.sound_deal, None)
                key = 'dealwaste'
                self.addCheckNode(
                    tv, rg1,
                    _('deal waste'),
                    self.menubar.tkopt.sound_dealwaste, None)
                key = 'droppair'
                self.addCheckNode(
                    tv, rg1,
                    _('drop pair'),
                    self.menubar.tkopt.sound_droppair, None)
                key = 'drop'
                self.addCheckNode(
                    tv, rg1,
                    _('drop'),
                    self.menubar.tkopt.sound_drop, None)
                key = 'flip'
                self.addCheckNode(
                    tv, rg1,
                    _('flip'),
                    self.menubar.tkopt.sound_flip, None)
                key = 'move'
                self.addCheckNode(
                    tv, rg1,
                    _('move'),
                    self.menubar.tkopt.sound_move, None)
                key = 'nomove'
                self.addCheckNode(
                    tv, rg1,
                    _('no move'),
                    self.menubar.tkopt.sound_nomove, None)
                key = 'redo'
                self.addCheckNode(
                    tv, rg1,
                    _('redo'),
                    self.menubar.tkopt.sound_redo, None)
                key = 'startdrag'
                self.addCheckNode(
                    tv, rg1,
                    _('start drag'),
                    self.menubar.tkopt.sound_startdrag, None)
                key = 'turnwaste'
                self.addCheckNode(
                    tv, rg1,
                    _('turn waste'),
                    self.menubar.tkopt.sound_turnwaste, None)
                key = 'undo'
                self.addCheckNode(
                    tv, rg1,
                    _('undo'),
                    self.menubar.tkopt.sound_undo, None)
                key = 'gamefinished'
                self.addCheckNode(
                    tv, rg1,
                    _('game finished'),
                    self.menubar.tkopt.sound_gamefinished, None)
                key = 'gamelost'
                self.addCheckNode(
                    tv, rg1,
                    _('game lost'),
                    self.menubar.tkopt.sound_gamelost, None)
                key = 'gameperfect'
                self.addCheckNode(
                    tv, rg1,
                    _('game perfect'),
                    self.menubar.tkopt.sound_gameperfect, None)
                key = 'gamewon'
                self.addCheckNode(
                    tv, rg1,
                    _('game won'),
                    self.menubar.tkopt.sound_gamewon, None)
                key = 'extra'
                self.addCheckNode(
                    tv, rg1,
                    _('Other'),
                    self.menubar.tkopt.sound_extra, None)

        yield
        # -------------------------------------------
        # Cardsets and card backside options

        from pysollib.resource import CSI

        rg = tv.add_node(
            LTreeNode(text=_('Cardsets')))
        if rg:
            self.menubar.tkopt.cardset.value = self.app.cardset.index

            cdict = collections.defaultdict(list)
            for i, cardset in enumerate(self.app.cardset_manager.getAll()):
                cdict[cardset.type].append((i, cardset))

            for k in sorted(cdict.keys()):
                name = CSI.TYPE_NAME[k]
                csl = cdict[k]
                rg1 = tv.add_node(LTreeNode(text=name), rg)

                for cst in csl:
                    i = cst[0]
                    cs = cst[1]
                    rg2 = self.addRadioNode(
                        tv,rg1,cs.name,self.menubar.tkopt.cardset, # noqa
                        i,self.menubar.mOptCardset)                # noqa

                    if rg2:
                        cbs = cs.backnames
                        self.menubar.tkopt.cardbacks[i] = LNumWrap(None)
                        self.menubar.tkopt.cardbacks[i].value = cs.backindex

                        bcnt = len(cbs)
                        bi = 0
                        while 1:
                            if bi == bcnt: break  # noqa
                            cb = cbs[bi]
                            self.addRadioNode(tv,rg2,cb,             # noqa
                                self.menubar.tkopt.cardbacks[i],bi,  # noqa
                                self.make_vars_command(              # noqa
                                    self.menubar.mOptSetCardback, i))
                            bi += 1
        yield
        # -------------------------------------------
        # Table background settings

        rg = tv.add_node(
            LTreeNode(text=_('Table')))
        if rg:
            rg1 = tv.add_node(
                LTreeNode(text=_('Solid colors')), rg)
            if rg1:
                key = 'table'
                self.addRadioNode(
                    tv, rg1,
                    _('Azure'),
                    self.menubar.tkopt.color_vars[key], '#0082df',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Black'),
                    self.menubar.tkopt.color_vars[key], '#000000',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Blue'),
                    self.menubar.tkopt.color_vars[key], '#0000ff',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Bronze'),
                    self.menubar.tkopt.color_vars[key], '#ce8946',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Brown'),
                    self.menubar.tkopt.color_vars[key], '#684700',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Cyan'),
                    self.menubar.tkopt.color_vars[key], '#00ffff',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Gold'),
                    self.menubar.tkopt.color_vars[key], '#ffd700',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Grey'),
                    self.menubar.tkopt.color_vars[key], '#888888',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Green'),
                    self.menubar.tkopt.color_vars[key], '#008200',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Lime'),
                    self.menubar.tkopt.color_vars[key], '#00ff00',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Magenta'),
                    self.menubar.tkopt.color_vars[key], '#ff00ff',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Navy'),
                    self.menubar.tkopt.color_vars[key], '#000086',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Olive'),
                    self.menubar.tkopt.color_vars[key], '#868200',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Orange'),
                    self.menubar.tkopt.color_vars[key], '#f79600',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Pink'),
                    self.menubar.tkopt.color_vars[key], '#ff92fc',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Purple'),
                    self.menubar.tkopt.color_vars[key], '#800080',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Red'),
                    self.menubar.tkopt.color_vars[key], '#ff0000',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Silver'),
                    self.menubar.tkopt.color_vars[key], '#c0c0c0',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Teal'),
                    self.menubar.tkopt.color_vars[key], '#008080',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Violet'),
                    self.menubar.tkopt.color_vars[key], '#8300ff',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('White'),
                    self.menubar.tkopt.color_vars[key], '#ffffff',
                    self.menubar.mOptTableColor)
                self.addRadioNode(
                    tv, rg1,
                    _('Yellow'),
                    self.menubar.tkopt.color_vars[key], '#ffff00',
                    self.menubar.mOptTableColor)

            rg1 = tv.add_node(
                LTreeNode(text=_('Tiles')), rg)
            rg2 = tv.add_node(
                LTreeNode(text=_('Images')), rg)

            if rg1 or rg2:
                tm = self.app.tabletile_manager
                # cnt = tm.len()
                i = 1
                while True:
                    ti = tm.get(i)

                    if ti is None:
                        break
                    if ti.save_aspect == 0 and ti.stretch == 0 and rg1:
                        self.addRadioNode(tv, rg1,
                                          ti.name,
                                          self.menubar.tkopt.tabletile, i,
                                          self.menubar.mOptTileSet)
                    if (ti.save_aspect == 1 or ti.stretch == 1) and rg2:
                        self.addRadioNode(tv, rg2,
                                          ti.name,
                                          self.menubar.tkopt.tabletile, i,
                                          self.menubar.mOptTileSet)
                    i += 1

        yield
        # -------------------------------------------
        # Card view options

        rg = tv.add_node(
            LTreeNode(text=_('Card view')))
        if rg:
            self.addCheckNode(tv, rg,
                              _('Card shadow'),
                              self.menubar.tkopt.shadow,
                              self.menubar.mOptShadow)

            self.addCheckNode(tv, rg,
                              _('Shade legal moves'),
                              self.menubar.tkopt.shade,
                              self.menubar.mOptShade)

            self.addCheckNode(tv, rg,
                              _('Negative cards bottom'),
                              self.menubar.tkopt.negative_bottom,
                              self.menubar.mOptNegativeBottom)

            self.addCheckNode(tv, rg,
                              _('Shrink face-down cards'),
                              self.menubar.tkopt.shrink_face_down,
                              self.menubar.mOptShrinkFaceDown)

            self.addCheckNode(tv, rg,
                              _('Shade filled stacks'),
                              self.menubar.tkopt.shade_filled_stacks,
                              self.menubar.mOptShadeFilledStacks)

        yield
        # -------------------------------------------
        # Animation settins

        rg = tv.add_node(
            LTreeNode(text=_('Animations')))
        if rg:
            self.addRadioNode(tv, rg,
                              _('None'),
                              self.menubar.tkopt.animations, 0,
                              self.menubar.mOptAnimations)

            self.addRadioNode(tv, rg,
                              _('Very fast'),
                              self.menubar.tkopt.animations, 1,
                              self.menubar.mOptAnimations)

            self.addRadioNode(tv, rg,
                              _('Fast'),
                              self.menubar.tkopt.animations, 2,
                              self.menubar.mOptAnimations)

            self.addRadioNode(tv, rg,
                              _('Medium'),
                              self.menubar.tkopt.animations, 3,
                              self.menubar.mOptAnimations)

            self.addRadioNode(tv, rg,
                              _('Slow'),
                              self.menubar.tkopt.animations, 4,
                              self.menubar.mOptAnimations)

            self.addRadioNode(tv, rg,
                              _('Very slow'),
                              self.menubar.tkopt.animations, 5,
                              self.menubar.mOptAnimations)

            # NOTE: All the following animation features only work on the
            # desktop and only if pillow is installed. So its useless to
            # present them here.
            '''
            self.addCheckNode(tv, rg,
                              _('Redeal animation'),
                              self.menubar.tkopt.redeal_animation,
                              self.menubar.mRedealAnimation)
            self.addCheckNode(tv, rg,
                              _('Winning animation'),
                              self.menubar.tkopt.win_animation,
                              self.menubar.mWinAnimation)
            self.addCheckNode(tv, rg,
                              _('Flip animation'),
                              self.menubar.tkopt.flip_animation,
                              None)
            '''

        yield
        # -------------------------------------------
        # Touch mode settings

        rg = tv.add_node(
            LTreeNode(text=_('Touch mode')))
        if rg:
            self.addRadioNode(tv, rg,
                              _('Drag-and-Drop'),
                              self.menubar.tkopt.mouse_type, 'drag-n-drop',
                              None)

            self.addRadioNode(tv, rg,
                              _('Point-and-Click'),
                              self.menubar.tkopt.mouse_type, 'point-n-click',
                              None)

            # sinnlos mit touch-device:
            # self.addRadioNode(tv, rg,
            #   'Sticky mouse',
            #   self.menubar.tkopt.mouse_type, u'sticky-mouse',
            #   self.menubar.mOptMouseType)

            # submenu.add_separator()

            # sinnlos mit touch-device:
            # self.addCheckNode(tv, rg,
            #   'Use mouse for undo/redo',
            #   self.menubar.tkopt.mouse_undo,
            #   self.menubar.mOptMouseUndo)

        # submenu.add_separator()

        # -------------------------------------------
        # TBD ?

        '''
        menu.add_command(label=n_("&Fonts..."), command=self.mOptFonts)
        menu.add_command(label=n_("&Colors..."), command=self.mOptColors)
        menu.add_command(label=n_("Time&outs..."), command=self.mOptTimeouts)
        menu.add_separator()
        '''

        yield
        # -------------------------------------------
        # Toolbar options

        rg = tv.add_node(
            LTreeNode(text=_('Toolbar')))
        if rg:
            self.addRadioNode(tv, rg,
                              _('Hide'),
                              self.menubar.tkopt.toolbar, 0,
                              self.menubar.setToolbarPos)

            self.addRadioNode(tv, rg,
                              _('Top'),
                              self.menubar.tkopt.toolbar, 1,
                              self.menubar.setToolbarPos)
            self.addRadioNode(tv, rg,
                              _('Bottom'),
                              self.menubar.tkopt.toolbar, 2,
                              self.menubar.setToolbarPos)
            self.addRadioNode(tv, rg,
                              _('Left'),
                              self.menubar.tkopt.toolbar, 3,
                              self.menubar.setToolbarPos)
            self.addRadioNode(tv, rg,
                              _('Right'),
                              self.menubar.tkopt.toolbar, 4,
                              self.menubar.setToolbarPos)

            rg1 = tv.add_node(
                LTreeNode(text=_('Visible buttons')), rg)
            if rg1:
                for w in TOOLBAR_BUTTONS:
                    w0 = w[0].upper()
                    ww = w[1:]
                    self.addCheckNode(tv, rg1,
                        _(w0+ww),  # noqa
                        self.menubar.tkopt.toolbar_vars[w],
                        self.make_vars_command(self.menubar.mOptToolbarConfig, w))  # noqa

        # -------------------------------------------
        # Statusbar - not implemented

        '''
        submenu = MfxMenu(menu, label=n_("Stat&usbar"))
        submenu.add_checkbutton(
             label=n_("Show &statusbar"),
             variable=self.tkopt.statusbar,
             command=self.mOptStatusbar)
        submenu.add_checkbutton(
            label=n_("Show &number of cards"),
            variable=self.tkopt.num_cards,
            command=self.mOptNumCards)
        submenu.add_checkbutton(
            label=n_("Show &help bar"),
            variable=self.tkopt.helpbar,
            command=self.mOptHelpbar)
        '''
        # -------------------------------------------
        # Other graphics settings

        rg = tv.add_node(
            LTreeNode(text=_('Other graphics')))
        data_dir = os.path.join(self.app.dataloader.dir, 'images', 'demo')
        dl = tv.add_node(
            LTreeNode(text=_('Demo logo')), rg)
        styledirs = os.listdir(data_dir)
        styledirs.append("none")
        styledirs.sort()
        for f in styledirs:
            d = os.path.join(data_dir, f)
            if (os.path.isdir(d) and os.path.exists(os.path.join(d))) \
                    or f == "none":
                name = f.replace('_', ' ').capitalize()
                self.addRadioNode(tv, dl,
                                  _(name),
                                  self.menubar.tkopt.demo_logo_style, f,
                                  self.menubar.mOptDemoLogoStyle)

        data_dir = os.path.join(self.app.dataloader.dir, 'images', 'pause')
        dl = tv.add_node(
            LTreeNode(text=_('Pause text')), rg)
        for f in sorted(os.listdir(data_dir)):
            d = os.path.join(data_dir, f)
            if os.path.isdir(d) and os.path.exists(os.path.join(d)):
                name = f.replace('_', ' ').capitalize()
                self.addRadioNode(tv, dl,
                                  _(name),
                                  self.menubar.tkopt.pause_text_style, f,
                                  self.menubar.mOptPauseTextStyle)

        data_dir = os.path.join(self.app.dataloader.dir, 'images',
                                'redealicons')
        dl = tv.add_node(
            LTreeNode(text=_('Redeal icons')), rg)
        for f in sorted(os.listdir(data_dir)):
            d = os.path.join(data_dir, f)
            if os.path.isdir(d) and os.path.exists(os.path.join(d)):
                name = f.replace('_', ' ').capitalize()
                self.addRadioNode(tv, dl,
                                  _(name),
                                  self.menubar.tkopt.redeal_icon_style, f,
                                  self.menubar.mOptRedealIconStyle)

        # -------------------------------------------
        # general options

        rg = tv.add_node(
            LTreeNode(text=_('Font size')))
        if rg:
            self.addRadioNode(tv, rg,
                              _('default'),
                              self.menubar.tkopt.fontscale, 'default',
                              None)
            self.addRadioNode(tv, rg,
                              _('tiny'),
                              self.menubar.tkopt.fontscale, 'tiny',
                              None)
            self.addRadioNode(tv, rg,
                              _('small'),
                              self.menubar.tkopt.fontscale, 'small',
                              None)
            self.addRadioNode(tv, rg,
                              _('normal'),
                              self.menubar.tkopt.fontscale, 'normal',
                              None)
            self.addRadioNode(tv, rg,
                              _('large'),
                              self.menubar.tkopt.fontscale, 'large',
                              None)
            self.addRadioNode(tv, rg,
                              _('huge'),
                              self.menubar.tkopt.fontscale, 'huge',
                              None)
            '''
            self.addSliderNode(tv, rg, self.menubar.tkopt.fontsizefactor,
                               (0.7, 2.0, 0.1))
            '''

        # self.addCheckNode(tv, None,
        #   'Save games geometry',
        #   self.menubar.tkopt.save_games_geometry,
        #   self.menubar.mOptSaveGamesGeometry)

        # self.addCheckNode(tv, None,
        #   'Demo logo',
        #   self.menubar.tkopt.demo_logo,
        #   self.menubar.mOptDemoLogo)

        self.addCheckNode(tv, None,
                          _('Startup splash screen'),
                          self.menubar.tkopt.splashscreen,
                          None)

        self.addCheckNode(tv, None,
                          _('Winning splash'),
                          self.menubar.tkopt.display_win_message,
                          None)

# ************************************************************************


class OptionsMenuDialog(LMenuDialog):

    def __init__(self, menubar, parent, title, app, **kw):
        kw['persist'] = True
        super().__init__(
            menubar, parent, title, app, **kw)

    def initTree(self):
        og = LOptionsMenuGenerator(
            self.menubar, self.parent, title=_("Options"), app=self.app)
        tv = og.generate()
        return tv

# ************************************************************************


class HelpMenuDialog(LMenuDialog):
    def __init__(self, menubar, parent, title, app, **kw):
        kw['persist'] = True
        super().__init__(menubar, parent, title, app, **kw)

    def buildTree(self, tv, node):
        tv.add_node(
            LTreeNode(
                text=_('Contents'),
                command=self.auto_close(self.menubar.mHelp)))
        tv.add_node(
            LTreeNode(
                text=_('How to use PySol'),
                command=self.auto_close(self.menubar.mHelpHowToPlay)))
        tv.add_node(
            LTreeNode(
                text=_('Rules for this game'),
                command=self.auto_close(self.menubar.mHelpRules)))
        tv.add_node(
            LTreeNode(
                text=_('License terms'),
                command=self.auto_close(self.menubar.mHelpLicense)))
        tv.add_node(
            LTreeNode(
                text=_('About %s...') % TITLE,
                command=self.auto_close(self.menubar.mHelpAbout)))

        # tv.add_node(LTreeNode(
        #   text='AboutKivy ...',
        #   command=self.makeHtmlCommand(self.menubar, "kivy.html")))
    '''
    def makeHtmlCommand(self, bar, htmlfile):
        def htmlCommand():
            bar.mHelpHtml(htmlfile)
        return htmlCommand
    '''

# ************************************************************************
# *
# ************************************************************************


class EmulTkMenu:

    def __init__(self, master, **kw):

        self.name = kw["name"]
        self.n = 0
        self._w = None
        if (self.name):
            if master._w == '.':
                self._w = '.' + self.name
            else:
                self._w = master._w + '.' + self.name
        else:
            self.name = "<>"

    def labeltoname(self, label):
        name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
        label = _(label)
        underline = label.find('&')
        if underline >= 0:
            label = label.replace('&', '')
        return name, label, underline

    def add_cascade(self, cnf=None, **kw):
        if cnf is None:
            cnf = {}
        self.add('cascade', cnf or kw)

    def add(self, itemType, cnf=None):
        if cnf is None:
            cnf = {}
        label = cnf.get("label")
        if label:
            name = cnf.get('name')
            if name:
                name, label, underline = self.labeltoname(label)
                cnf["underline"] = cnf.get("underline", underline)
                cnf["label"] = label
                if name and self.addPath:
                    path = str(self._w) + "." + name
                    self.addPath(path, self, self.n, cnf.get("menu"))

    def cget(self, key):
        return key

# ************************************************************************


class MfxMenubar(EmulTkMenu):
    addPath = None

    def __init__(self, master, **kw):
        super().__init__(master, **kw)
        topmenu = self.name == 'menubar'

        self.menu = LMenu(not topmenu, text=self.name)
        if topmenu:
            master.setMenu(self.menu)

# ************************************************************************
# * - create menubar
# * - update menubar
# * - menu actions
# ************************************************************************


class DictObjMap:
    def __init__(self, val):
        self.__dict__ = val


class PysolMenubarTk:
    def __init__(self, app, top, progress=None):
        print('PysolMenubarTk: __init__()')
        self.top = top
        self.app = app

        self._createTkOpt()
        self._setOptions()

        self.__cb_max = 8
        self.progress = progress

        # create menus
        self.__menubar = None
        self.__menupath = {}
        self.__keybindings = {}
        self._createMenubar()

        if self.progress:
            self.progress.update(step=1)

    def unlockScreenRotation(self, obj, val):
        AndroidScreenRotation.unlock(toaster=False)
        print('unlock screen rotation')

    def setFontScale(self, obj, val):
        from kivy.metrics import Metrics
        vals = {
            'tiny':   0.833,
            'small':  1.0,
            'normal': 1.2,
            'large':  1.44,
            'huge':   1.728
        }
        if val == 'default':
            Metrics.reset_metrics()
        else:
            Metrics.fontscale = vals[val]
    '''
    def setFontSize(self, obj, val):
        from kivy.metrics import Metrics
        Metrics.fontscale = val
    '''

    def _createTkOpt(self):
        opt = self.app.opt

        # fake options
        self.tabletile_index = self.app.tabletile_index
        self.cardback = self.app.cardset.backindex
        self.cardset = self.app.cardset_manager.getSelected()
        self.pause = False
        if self.game:
            self.pause = self.game.pause
        self.gameid = 0
        self.gameid_popular = 0

        # map dicts to Obj
        self.sound_samples = DictObjMap(opt.sound_samples)
        self.tbv = DictObjMap(opt.toolbar_vars)
        self.cvo = DictObjMap(opt.colors)

        # option mappings.
        self.tkopt = Struct(
            # automation
            autofaceup=LBoolWrap(opt, "autofaceup"),
            autodrop=LBoolWrap(opt, "autodrop"),
            autodeal=LBoolWrap(opt, "autodeal"),
            quickplay=LBoolWrap(opt, "quickplay"),
            # support
            undo=LBoolWrap(opt, "undo"),
            hint=LBoolWrap(opt, "hint"),
            free_hint=LBoolWrap(opt, "free_hint"),
            shuffle=LBoolWrap(opt, "shuffle"),
            bookmarks=LBoolWrap(opt, "bookmarks"),
            highlight_piles=LBoolWrap(opt, "highlight_piles"),
            highlight_cards=LBoolWrap(opt, "highlight_cards"),
            highlight_samerank=LBoolWrap(opt, "highlight_samerank"),
            peek_facedown=LBoolWrap(opt, "peek_facedown"),
            highlight_not_matching=LBoolWrap(opt, "highlight_not_matching"),
            stuck_notification=LBoolWrap(opt, "stuck_notification"),
            mahjongg_show_removed=LBoolWrap(opt, "mahjongg_show_removed"),
            shisen_show_hint=LBoolWrap(opt, "shisen_show_hint"),
            accordion_deal_all=LBoolWrap(opt, "accordion_deal_all"),
            pegged_auto_remove=LBoolWrap(opt, "pegged_auto_remove"),
            # sound
            sound=LBoolWrap(opt, "sound"),
            sound_sample_volume=LNumWrap(opt, "sound_sample_volume"),
            sound_music_volume=LNumWrap(opt, "sound_music_volume"),
            # sound samples
            sound_areyousure=LBoolWrap(self.sound_samples, 'areyousure'),
            sound_autodrop=LBoolWrap(self.sound_samples, 'autodrop'),
            sound_autoflip=LBoolWrap(self.sound_samples, 'autoflip'),
            sound_autopilotlost=LBoolWrap(self.sound_samples, 'autopilotlost'),
            sound_autopilotwon=LBoolWrap(self.sound_samples, 'autopilotwon'),
            sound_deal=LBoolWrap(self.sound_samples, 'deal'),
            sound_dealwaste=LBoolWrap(self.sound_samples, 'dealwaste'),
            sound_droppair=LBoolWrap(self.sound_samples, 'droppair'),
            sound_drop=LBoolWrap(self.sound_samples, 'drop'),
            sound_extra=LBoolWrap(self.sound_samples, 'extra'),
            sound_flip=LBoolWrap(self.sound_samples, 'flip'),
            sound_move=LBoolWrap(self.sound_samples, 'move'),
            sound_nomove=LBoolWrap(self.sound_samples, 'nomove'),
            sound_redo=LBoolWrap(self.sound_samples, 'redo'),
            sound_startdrag=LBoolWrap(self.sound_samples, 'startdrag'),
            sound_turnwaste=LBoolWrap(self.sound_samples, 'turnwaste'),
            sound_undo=LBoolWrap(self.sound_samples, 'undo'),
            sound_gamefinished=LBoolWrap(self.sound_samples, 'gamefinished'),
            sound_gamelost=LBoolWrap(self.sound_samples, 'gamelost'),
            sound_gameperfect=LBoolWrap(self.sound_samples, 'gameperfect'),
            sound_gamewon=LBoolWrap(self.sound_samples, 'gamewon'),
            # animation
            animations=LNumWrap(opt, "animations"),
            redeal_animation=LBoolWrap(opt, "redeal_animation"),
            win_animation=LBoolWrap(opt, "win_animation"),
            flip_animation=LBoolWrap(opt, "flip_animation"),
            # toolbar
            toolbar=LNumWrap(opt, "toolbar"),
            toolbar_land=LNumWrap(opt, "toolbar_land", self.mOptToolbar),
            toolbar_port=LNumWrap(opt, "toolbar_port", self.mOptToolbar),
            toolbar_style=LStringWrap(opt, "toolbar_style"),
            toolbar_relief=LStringWrap(opt, "toolbar_relief"),
            toolbar_compound=LStringWrap(opt, "toolbar_compound"),
            toolbar_size=LNumWrap(opt, "toolbar_size"),
            toolbar_vars={},
            # card dsiplay and text style settings
            demo_logo=LBoolWrap(opt, "demo_logo"),
            demo_logo_style=LStringWrap(opt, "demo_logo_style"),
            pause_text_style=LStringWrap(opt, "pause_text_style"),
            redeal_icon_style=LStringWrap(opt, "redeal_icon_style"),
            mouse_type=LStringWrap(opt, "mouse_type"),
            mouse_undo=LBoolWrap(opt, "mouse_undo"),
            shade_filled_stacks=LBoolWrap(opt, "shade_filled_stacks"),
            shrink_face_down=LBoolWrap(opt, "shrink_face_down"),
            negative_bottom=LBoolWrap(opt, "negative_bottom"),
            shadow=LBoolWrap(opt, "shadow"),
            shade=LBoolWrap(opt, "shade"),
            # colors
            color_vars={},
            tabletile=LNumWrap(self, "tabletile_index"),
            # other
            splashscreen=LBoolWrap(opt, "splashscreen"),
            display_win_message=LBoolWrap(opt, "display_win_message"),
            language=LStringWrap(opt, "language"),
            save_games_geometry=LBoolWrap(opt, "save_games_geometry"),
            pause=LBoolWrap(self, "pause"),
            table_zoom=LListWrap(opt, "table_zoom"),
            fontscale=LStringWrap(opt, "fontscale", self.setFontScale),
            # fontsizefactor=LNumWrap(opt, "fontsizefactor", self.setFontSize),
            # cards
            cardset=LNumWrap(self, "cardset"),
            cardback=LNumWrap(self, "cardback"),
            cardbacks={},
            # statusbar (not implemented)
            statusbar=LBoolWrap(opt, "statusbar"),
            # num_cards=BooleanVar(),
            # helpbar=BooleanVar(),
            # game
            gameid=LNumWrap(self, "gameid", self.unlockScreenRotation),
            gameid_popular=LNumWrap(self, "gameid_popular"),
        )
        for w in TOOLBAR_BUTTONS:
            self.tkopt.toolbar_vars[w] = LBoolWrap(self.tbv, w)
        for k in self.app.opt.colors:
            self.tkopt.color_vars[k] = LStringWrap(self.cvo, k)

    def _setOptions(self):
        self.tkopt.save_games_geometry.value = False
        self.getToolbarPos(None, Window.size)
        self.setFontScale(None, self.tkopt.fontscale.value)
        # self.setFontSize(None, self.tkopt.fontsizefactor.value)
        Window.bind(size=self.getToolbarPos)

    def getToolbarPos(self, obj, size):
        if (size[0] > size[1]):
            self.tkopt.toolbar.value = self.tkopt.toolbar_land.value
        else:
            self.tkopt.toolbar.value = self.tkopt.toolbar_port.value

    def setToolbarPos(self, *args):
        if (Window.size[0] > Window.size[1]):
            self.tkopt.toolbar_land.value = self.tkopt.toolbar.value
        else:
            self.tkopt.toolbar_port.value = self.tkopt.toolbar.value

    def connectGame(self, game):
        self.game = game
        if game is None:
            return
        assert self.app is game.app
        tkopt = self.tkopt
        # opt = self.app.opt
        tkopt.gameid.value = game.id
        tkopt.gameid_popular.value = game.id
        tkopt.pause.value = self.game.pause
        if game.canFindCard():
            connect_game_find_card_dialog(game)
        else:
            destroy_find_card_dialog()
        if game.canShowFullPicture():
            connect_game_full_picture_dialog(game)
        else:
            destroy_full_picture_dialog()
        connect_game_solver_dialog(game)

    # create a GTK-like path
    def _addPath(self, path, menu, index, submenu):
        # print ('MfxMenubar: _addPath %s, %s' % (path, menu))
        # y = self.yy
        if path not in self.__menupath:
            # print path, menu, index, submenu
            self.__menupath[path] = (menu, index, submenu)

    def _getEnabledState(self, enabled):
        print('_getEnabledState: %s' % enabled)
        if enabled:
            return "normal"
        return "disabled"

    def updateProgress(self):
        if self.progress:
            self.progress.update(step=1)

    #
    # create the menubar
    #

    def _createMenubar(self):
        MfxMenubar.addPath = self._addPath
        kw = {"name": "menubar"}
        self.__menubar = MfxMenubar(self.top, **kw)

        # init keybindings
        bind(self.top, "<KeyPress>", self._keyPressHandler)

        # LMainMenuDialog()
        LMenuItem(self.__menubar.menu,
                  text=_("Menu"), command=self.mMainMenuDialog)

        MfxMenubar.addPath = None

    #
    # key binding utility
    #

    def _bindKey(self, modifier, key, func):
        #         if 0 and not modifier and len(key) == 1:
        #             self.__keybindings[key.lower()] = func
        #             self.__keybindings[key.upper()] = func
        #             return
        if not modifier and len(key) == 1:
            # ignore Ctrl/Shift/Alt
            # but don't ignore NumLock (state == 16)
            def lfunc(e, func=func):
                return e.state in (0, 16) and func(e)
            func = lfunc
            # func = lambda e, func=func: e.state in (0, 16) and func(e)
        sequence = "<" + modifier + "KeyPress-" + key + ">"
        bind(self.top, sequence, func)
        if len(key) == 1 and key != key.upper():
            key = key.upper()
            sequence = "<" + modifier + "KeyPress-" + key + ">"
            bind(self.top, sequence, func)

    def _keyPressHandler(self, event):
        r = EVENT_PROPAGATE
        if event and self.game:
            # print event.__dict__
            if self.game.demo:
                # stop the demo by setting self.game.demo.keypress
                if event.char:    # ignore Ctrl/Shift/etc.
                    self.game.demo.keypress = event.char
                    r = EVENT_HANDLED
#             func = self.__keybindings.get(event.char)
#             if func and (event.state & ~2) == 0:
#                 func(event)
#                 r = EVENT_HANDLED
        return r

    #
    # Select Game menu creation
    #
    '''
    def _addSelectGameMenu(self, menu):
        games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
        m = "Ctrl-"
        if sys.platform == "darwin":
            m = "Cmd-"
        menu.add_command(label=n_("All &games..."), accelerator=m + "W",
                         command=self.mSelectGameDialog)

    def _addSelectGameSubMenu(self, games, menu, select_data,
                              command, variable):
        # print select_data
        need_sep = 0
        for label, select_func in select_data:
            if label is None:
                need_sep = 1
                continue
            g = filter(select_func, games)
            if not g:
                continue
            if need_sep:
                menu.add_separator()
                need_sep = 0
            submenu = MfxMenu(menu, label=label)
            self._addSelectGameSubSubMenu(g, submenu, command, variable)

    def _getNumGames(self, games, select_data):
        ngames = 0
        for label, select_func in select_data:
            ngames += len(filter(select_func, games))
        return ngames

    def _addSelectMahjonggGameSubMenu(self, games, menu, command, variable):
        def select_func(gi): return gi.si.game_type == GI.GT_MAHJONGG
        mahjongg_games = filter(select_func, games)
        if len(mahjongg_games) == 0:
            return
        #
        menu = MfxMenu(menu, label=n_("&Mahjongg games"))

        def add_menu(games, c0, c1, menu=menu,
                     variable=variable, command=command):
            if not games:
                return
            label = c0 + ' - ' + c1
            if c0 == c1:
                label = c0
            submenu = MfxMenu(menu, label=label, name=None)
            self._addSelectGameSubSubMenu(games, submenu, command,
                                          variable, short_name=True)

        games = {}
        for gi in mahjongg_games:
            c = gi.short_name.strip()[0]
            if c in games:
                games[c].append(gi)
            else:
                games[c] = [gi]
        games = games.items()
        games.sort()
        g0 = []
        c0 = c1 = games[0][0]
        for c, g1 in games:
            if len(g0) + len(g1) >= self.__cb_max:
                add_menu(g0, c0, c1)
                g0 = g1
                c0 = c1 = c
            else:
                g0 += g1
                c1 = c
        add_menu(g0, c0, c1)

    def _addSelectPopularGameSubMenu(self, games, menu, command, variable):
        def select_func(gi): return gi.si.game_flags & GI.GT_POPULAR
        if len(filter(select_func, games)) == 0:
            return
        data = (n_("&Popular games"), select_func)
        self._addSelectGameSubMenu(games, menu, (data, ),
                                   self.mSelectGamePopular,
                                   self.tkopt.gameid_popular)

    def _addSelectFrenchGameSubMenu(self, games, menu, command, variable):
        if self._getNumGames(games, GI.SELECT_GAME_BY_TYPE) == 0:
            return
        submenu = MfxMenu(menu, label=n_("&French games"))
        self._addSelectGameSubMenu(games, submenu, GI.SELECT_GAME_BY_TYPE,
                                   self.mSelectGame, self.tkopt.gameid)

    def _addSelectOrientalGameSubMenu(self, games, menu, command, variable):
        if self._getNumGames(games, GI.SELECT_ORIENTAL_GAME_BY_TYPE) == 0:
            return
        submenu = MfxMenu(menu, label=n_("&Oriental games"))
        self._addSelectGameSubMenu(games, submenu,
                                   GI.SELECT_ORIENTAL_GAME_BY_TYPE,
                                   self.mSelectGame, self.tkopt.gameid)

    def _addSelectSpecialGameSubMenu(self, games, menu, command, variable):
        if self._getNumGames(games, GI.SELECT_ORIENTAL_GAME_BY_TYPE) == 0:
            return
        submenu = MfxMenu(menu, label=n_("&Special games"))
        self._addSelectGameSubMenu(games, submenu,
                                   GI.SELECT_SPECIAL_GAME_BY_TYPE,
                                   self.mSelectGame, self.tkopt.gameid)

    def _addSelectCustomGameSubMenu(self, games, menu, command, variable):
        submenu = MfxMenu(menu, label=n_("&Custom games"))

        def select_func(gi): return gi.si.game_type == GI.GT_CUSTOM
        games = filter(select_func, games)
        self.updateGamesMenu(submenu, games)
    '''

    def _addSelectAllGameSubMenu(self, games, menu, command, variable):
        # LB
        # herausgenommen: zu aufwendig !
        return
        '''
        menu = MfxMenu(menu, label=n_("&All games by name"))
        n, d = 0, self.__cb_max
        i = 0
        while True:
            if self.progress:
                self.progress.update(step=1)
            columnbreak = i > 0 and (i % d) == 0
            i += 1
            if not games[n:n + d]:
                break
            m = min(n + d - 1, len(games) - 1)
            label = games[n].name[:3] + ' - ' + games[m].name[:3]

            submenu = MfxMenu(menu, label=label, name=None)
            self._addSelectGameSubSubMenu(games[n:n + d], submenu,
                                          command, variable)
            n += d
            # if columnbreak:
            #    menu.entryconfigure(i, columnbreak=columnbreak)
        '''

    # Eine 'closure' in Python? - voila!
    def make_gamesetter(self, n, variable, command):
        def gamesetter(x):
            variable.value = n
            command()
        return gamesetter

    def _addSelectGameSubSubMenu(self, games, menu, command, variable,
                                 short_name=False):

        # cb = self.__cb_max
        for gi in games:
            # columnbreak = i > 0 and (i % cb) == 0
            if short_name:
                label = gi.short_name
            else:
                label = gi.name

            # optimized by inlining

            # geht nicht mehr 'optimiert' mit kivy
            # die Funktionalität des tk.calls kann mit hilfe
            # einer 'closure' rekonstruiert werden (s.o).
            # LB

            gsetter = self.make_gamesetter(gi.id, variable, command)
            menu.add_command(label=label, command=gsetter)

            # menu.tk.call((menu._w, 'add', 'radiobutton') +
            #             menu._options({'command': command,
            #                            'variable': variable,
            #                            'columnbreak': columnbreak,
            #                            'value': gi.id,
            #                            'label': label}))

    def updateGamesMenu(self, menu, games):

        def cmp2(a, b):
            """python 3 replacement for python 2 cmp function"""
            return (a > b) - (a < b)

        menu.delete(0, 'last')

        if len(games) == 0:
            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,
                                          command=self.mSelectGame,
                                          variable=self.tkopt.gameid)
        else:
            self._addSelectGameSubSubMenu(games, menu,
                                          command=self.mSelectGame,
                                          variable=self.tkopt.gameid)

    def mMainMenuDialog(self, *event):
        MainMenuDialog(self, self.top, title=_("Main Menu"), app=self.app)
        return EVENT_HANDLED

    def mFileMenuDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)
        FileMenuDialog(self, self.top, title=_("File Menu"), app=self.app)
        return EVENT_HANDLED

    def mEditMenuDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)
        EditMenuDialog(self, self.top, title=_("Tools"), app=self.app)
        return EVENT_HANDLED

    def mGameMenuDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)
        GameMenuDialog(self, self.top, title=_("Statistics"), app=self.app)
        return EVENT_HANDLED

    def mAssistMenuDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)
        AssistMenuDialog(self, self.top, title=_("Assists"), app=self.app)
        return EVENT_HANDLED

    def mOptionsMenuDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)

        OptionsMenuDialog(self, self.top, title=_("Options"), app=self.app)
        return EVENT_HANDLED

    def mHelpMenuDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)
        HelpMenuDialog(self, self.top, title=_("Help"), app=self.app)
        return EVENT_HANDLED
    #
    # Select Game menu actions
    #

    def mSelectGame(self, *args):
        print('mSelectGame %s' % self)
        self._mSelectGame(self.tkopt.gameid.value)

    def mSelectGamePopular(self, *args):
        self._mSelectGame(self.tkopt.gameid_popular.value)

    def _mSelectGameDialog(self, d):
        if d.gameid != self.game.id:
            self.tkopt.gameid.value = d.gameid
            self.tkopt.gameid_popular.value = d.gameid
            self._cancelDrag()
            self.game.endGame()
            self.game.quitGame(d.gameid, random=d.random)
        return EVENT_HANDLED

    def __restoreCursor(self, *event):
        self.game.setCursor(cursor=self.app.top_cursor)

    def mSelectGameDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.game.setCursor(cursor=CURSOR_WATCH)
        after_idle(self.top, self.__restoreCursor)
        d = SelectGameDialog(self.top, title=_("Select game"),
                             app=self.app, gameid=self.game.id)
        return self._mSelectGameDialog(d)

    #
    # menubar overrides
    #

    def updateFavoriteGamesMenu(self):
        return

        # TBD ?
        '''
        gameids = self.app.opt.favorite_gameid

        print('favorite_gameids = %s' % gameids)

        submenu = self.__menupath[".menubar.file.favoritegames"][2]
        games = []
        for id in gameids:
            gi = self.app.getGameInfo(id)
            if gi:
                games.append(gi)
        self.updateGamesMenu(submenu, games)

        # das folgende ist nur das enable/disable des add/remove buttons.
        # geht mit kivy nicht so.

#        state = self._getEnabledState
#        in_favor = self.app.game.id in gameids

# menu, index, submenu = self.__menupath[".menubar.file.addtofavorites"]
# menu.entryconfig(index, state=state(not in_favor))

# menu, index, submenu = self.__menupath[".menubar.file.removefromfavorites"]
# menu.entryconfig(index, state=state(in_favor))
        '''

    def updateRecentGamesMenu(self, gameids):
        return

        # TBD ?
        '''
        submenu = self.__menupath[".menubar.file.recentgames"][2]
        games = []
        for id in gameids:
            gi = self.app.getGameInfo(id)
            if gi:
                games.append(gi)
        self.updateGamesMenu(submenu, games)
        '''

    def updateBookmarkMenuState(self):
        # LB:
        # print('updateBookmarkMenuState - fake')
        return

        state = self._getEnabledState
        mp1 = self.__menupath.get(".menubar.edit.setbookmark")
        mp2 = self.__menupath.get(".menubar.edit.gotobookmark")
        mp3 = self.__menupath.get(".menubar.edit.clearbookmarks")
        if mp1 is None or mp2 is None or mp3 is None:
            return
        x = self.app.opt.bookmarks and self.game.canSetBookmark()
        #
        menu, index, submenu = mp1
        for i in range(9):
            submenu.entryconfig(i, state=state(x))
        menu.entryconfig(index, state=state(x))
        #
        menu, index, submenu = mp2
        ms = 0
        for i in range(9):
            s = self.game.gsaveinfo.bookmarks.get(i) is not None
            submenu.entryconfig(i, state=state(s and x))
            ms = ms or s
        menu.entryconfig(index, state=state(ms and x))
        #
        menu, index, submenu = mp3
        menu.entryconfig(index, state=state(ms and x))

    def updateBackgroundImagesMenu(self):
        # LB:
        print('updateBackgroundImagesMenu - fake')
        return

        mp = self.__menupath.get(".menubar.options.cardbackground")
        # delete all entries
        submenu = mp[2]
        submenu.delete(0, "last")
        # insert new cardbacks
        mbacks = self.app.images.getCardbacks()
        cb = int(math.ceil(math.sqrt(len(mbacks))))
        for i in range(len(mbacks)):
            columnbreak = i > 0 and (i % cb) == 0
            submenu.add_radiobutton(
                    label=mbacks[i].name,
                    image=mbacks[i].menu_image,
                    variable=self.tkopt.cardback,
                    value=i,
                    command=self.mOptCardback,
                    columnbreak=columnbreak,
                    indicatoron=0,
                    hidemargin=0)
    #
    # menu updates
    #

    def setMenuState(self, state, path):
        # LB: not used
        return

    def setToolbarState(self, state, path):
        # LB: not used
        return

    def _setPauseMenu(self, v):
        self.tkopt.pause.value = v

    #
    # menu actions
    #

    DEFAULTEXTENSION = ".pso"
    # TRANSLATORS: Usually, 'PySol files'
    FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
                 (_("All files"), "*"))

    def mAddFavor(self, *event):
        gameid = self.app.game.id
        if gameid not in self.app.opt.favorite_gameid:
            self.app.opt.favorite_gameid.insert(0, gameid)
            self.updateFavoriteGamesMenu()

    def mDelFavor(self, *event):
        gameid = self.app.game.id
        if gameid in self.app.opt.favorite_gameid:
            self.app.opt.favorite_gameid.remove(gameid)
            self.updateFavoriteGamesMenu()

    def mOpen(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        # filename = self.game.filename
        filename = "lastgame.pso"
        if filename:
            idir, ifile = os.path.split(os.path.normpath(filename))
        else:
            idir, ifile = "", ""
        if not idir:
            idir = self.app.dn.savegames
#        d = tkinter.filedialog.Open()
#        filename = d.show(filetypes=self.FILETYPES,
#                          defaultextension=self.DEFAULTEXTENSION,
#                          initialdir=idir, initialfile=ifile)
        filename = idir + "/" + ifile

        print('filename = %s' % filename)
        if filename:
            filename = os.path.normpath(filename)
            if os.path.isfile(filename):
                baseWindow = Cache.get('LAppCache', 'baseWindow')
                text = _("loading game from:")+filename
                toast = Toast(text=text)
                toast.show(parent=baseWindow, duration=4.0)
                Clock.schedule_once(lambda dt: self.game.loadGame(filename), 1.0) # noqa

    def mSaveAs(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        if not self.menustate.save_as:
            return
        # filename = self.game.filename
        filename = "lastgame.pso"
        if not filename:
            filename = self.app.getGameSaveName(self.game.id)
            if os.name == "posix":
                filename = filename + "-" + self.game.getGameNumber(format=0)
            elif os.path.supports_unicode_filenames:  # new in python 2.3
                filename = filename + "-" + self.game.getGameNumber(format=0)
            else:
                filename = filename + "-01"
            filename = filename + self.DEFAULTEXTENSION
        idir, ifile = os.path.split(os.path.normpath(filename))
        if not idir:
            idir = self.app.dn.savegames

        filename = idir + "/" + ifile
        if filename:
            filename = os.path.normpath(filename)
            # filename = os.path.normcase(filename)
            if self.game.saveGame(filename):
                baseWindow = Cache.get('LAppCache', 'baseWindow')
                text = _("game saved to:")+filename
                toast = Toast(text=text)
                toast.show(parent=baseWindow, duration=5.0)
            self.updateMenus()

    def mResetZoom(self, *args):
        self.tkopt.table_zoom.value = [1.0, 0.0, 0.0]

    def mPause(self, *args):
        if not self.game:
            return
        if not self.game.pause:
            if self._cancelDrag():
                return
        self.game.doPause()
        self.tkopt.pause.value = self.game.pause

    def mOptLanguage(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        MfxMessageDialog(
           self.app.top, title=_("Note"),
           text=_("""\
These settings will take effect
the next time you restart the %(app)s""") % {'app': TITLE})

    def mAppSetTile(self, idx, force=False):
        self.app.setTile(idx, force=force)
        # setTile() may change option values (?):
        # app.opt.tabletile_scale_method:  not manaaged here -> o.k.
        # app.opt.tabletile_name:          not managed here -> o.k.
        # app.tabletile_index:             managed here, is set to idx, no change occurs.  # noqa
        # app.opt.colors['table']:         managed as color_vars, complex, (proved: no change)  # noqa
        # print('**********', self.app.opt.colors['table'])
        # print('**********', self.tkopt.color_vars['table'].value)

    def mOptTableColor(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.tkopt.tabletile.value = 0  # (0 denotes color instead of a tile)
        nv = self.tkopt.color_vars['table'].value
        self.app.top_bg = nv
        self.mAppSetTile(0, force=True)

    def mOptTileSet(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        idx = self.tkopt.tabletile.value
        if idx > 0 and idx != self.app.tabletile_index:
            self.mAppSetTile(idx)
            # set color out of known colors
            # (its to remove the check flag from the menu):
            self.tkopt.color_vars['table'].value = '#008285'

    def mOptAutoFaceUp(self, *args):
        if self._cancelDrag():
            return
        if self.app.opt.autofaceup:
            self.game.autoPlay()

    def mOptAutoDrop(self, *args):
        if self._cancelDrag():
            return
        if self.app.opt.autodrop:
            self.game.autoPlay()

    def mOptAutoDeal(self, *args):
        if self._cancelDrag():
            return
        if self.app.opt.autodeal:
            self.game.autoPlay()

    def mOptQuickPlay(self, *args):
        if self._cancelDrag(break_pause=False):
            return

    def mOptEnableUndo(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptEnableBookmarks(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptEnableHint(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptFreeHints(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptEnableShuffle(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptEnableHighlightPiles(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptEnableHighlightCards(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.updateMenus()

    def mOptEnableHighlightSameRank(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptEnablePeekFacedown(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptEnableHighlightNotMatching(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptEnableStuckNotification(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptAnimations(self, *args):
        if self._cancelDrag(break_pause=False):
            return

    def mRedealAnimation(self, *args):
        if self._cancelDrag(break_pause=False):
            return

    def mWinAnimation(self, *args):
        if self._cancelDrag(break_pause=False):
            return

    def mOptShadow(self, *args):
        if self._cancelDrag(break_pause=False):
            return

    def mOptShade(self, *args):
        if self._cancelDrag(break_pause=False):
            return

    def mOptShrinkFaceDown(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.endGame(bookmark=1)
        self.game.quitGame(bookmark=1)

    def mOptShadeFilledStacks(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        self.game.endGame(bookmark=1)
        self.game.quitGame(bookmark=1)

    def mOptMahjonggShowRemoved(self, *args):
        if self._cancelDrag():
            return
        # self.game.updateMenus()
        self.game.endGame(bookmark=1)
        self.game.quitGame(bookmark=1)

    def mOptShisenShowHint(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptAccordionDealAll(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptPeggedAutoRemove(self, *args):
        if self._cancelDrag(break_pause=False):
            return
        # self.game.updateMenus()

    def mOptCardset(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        idx = self.tkopt.cardset.value
        cs = self.app.cardset_manager.get(idx)
        if cs is None or idx == self.app.cardset.index:
            return
        if idx >= 0:
            self.app.nextgame.cardset = cs
            self._cancelDrag()
            self.game.endGame(bookmark=1)
            self.game.quitGame(bookmark=1)

    def mSelectCardsetDialog(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        # strings, default = ("&OK", "&Load", "&Cancel"), 0
        strings, default = (None, _("&Load"), _("&Cancel"), ), 1
        # if os.name == "posix":
        strings, default = (None, _("&Load"), _(
            "&Cancel"), _("&Info..."), ), 1
        key = self.app.nextgame.cardset.index
        d = SelectCardsetDialogWithPreview(
                self.top, title=_("Select cardset"),
                app=self.app, manager=self.app.cardset_manager, key=key,
                strings=strings, default=default)

        cs = self.app.cardset_manager.get(d.key)
        if cs is None or d.key == self.app.cardset.index:
            return
        if d.status == 0 and d.button in (0, 1) and d.key >= 0:
            self.app.nextgame.cardset = cs
            if d.button == 1:
                self._cancelDrag()
                self.game.endGame(bookmark=1)
                self.game.quitGame(bookmark=1)

    def mOptSetCardback(self, key, *event):
        val = self.tkopt.cardbacks[key].value
        cs = self.app.cardset_manager.get(key)
        cs.updateCardback(backindex=val)
        # ANM: wir können den Background nur für das aktuell
        # selektierte Cardset wirklich ändern. Nur dieses wird
        # wird in den Optionen gespeichert.
        if (cs == self.app.cardset):
            self.app.updateCardset(self.game.id)
            self.app.cardset.backindex = val
            image = self.app.images.getBack(update=True)
            for card in self.game.cards:
                card.updateCardBackground(image=image)

    def _mOptCardback(self, index):
        if self._cancelDrag(break_pause=False):
            return
        cs = self.app.cardset
        old_index = cs.backindex
        cs.updateCardback(backindex=index)
        if cs.backindex == old_index:
            return
        self.app.updateCardset(self.game.id)
        image = self.app.images.getBack(update=True)
        for card in self.game.cards:
            card.updateCardBackground(image=image)
        self.tkopt.cardback.value = cs.backindex

    def mOptCardback(self, *event):
        self._mOptCardback(self.tkopt.cardback.value)

    def mOptChangeCardback(self, *event):
        self._mOptCardback(self.app.cardset.backindex + 1)

    def mOptToolbar(self, *event):
        self.app.toolbar.show()

    def mOptToolbarStyle(self, *event):
        self.setToolbarStyle(self.tkopt.toolbar_style.value)

    def mOptToolbarCompound(self, *event):
        self.setToolbarCompound(self.tkopt.toolbar_compound.value)

    def mOptToolbarSize(self, *event):
        self.setToolbarSize(self.tkopt.toolbar_size.value)

    def mOptToolbarRelief(self, *event):
        self.setToolbarRelief(self.tkopt.toolbar_relief.value)

    def mOptToolbarConfig(self, w):
        self.toolbarConfig(w, self.tkopt.toolbar_vars[w].value)

    def mOptDemoLogoStyle(self, *event):
        self.setDemoLogoStyle()

    def mOptPauseTextStyle(self, *event):
        self.setPauseTextStyle()

    def mOptRedealIconStyle(self, *event):
        self.setRedealIconStyle()

    def mOptStatusbar(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        if not self.app.statusbar:
            return
        side = self.tkopt.statusbar.value  # noqa
        resize = not self.app.opt.save_games_geometry  # noqa

    def mOptSaveGamesGeometry(self, *event):
        if self._cancelDrag(break_pause=False):
            return

    def mOptDemoLogo(self, *event):
        if self._cancelDrag(break_pause=False):
            return

    def mOptNegativeBottom(self, *event):
        if self._cancelDrag():
            return
        self.app.updateCardset()
        self.game.endGame(bookmark=1)
        self.game.quitGame(bookmark=1)

    #
    # toolbar support
    #

    def setToolbarSize(self, size):
        if self._cancelDrag(break_pause=False):
            return
        dir = self.app.getToolbarImagesDir()
        if self.app.toolbar.updateImages(dir, size):
            self.game.updateStatus(player=self.app.opt.player)

    def setToolbarStyle(self, style):
        if self._cancelDrag(break_pause=False):
            return
        dir = self.app.getToolbarImagesDir()
        size = self.app.opt.toolbar_size
        self.app.toolbar.updateImages(dir, size)

    def setToolbarCompound(self, compound):
        if self._cancelDrag(break_pause=False):
            return
        if self.app.toolbar.setCompound(compound):
            self.game.updateStatus(player=self.app.opt.player)

    def setToolbarRelief(self, relief):
        if self._cancelDrag(break_pause=False):
            return
        self.app.toolbar.setRelief(relief)

    def toolbarConfig(self, w, v):
        if self._cancelDrag(break_pause=False):
            return

    #
    # other graphics
    #

    def setDemoLogoStyle(self, style=None):
        if self._cancelDrag(break_pause=False):
            return
        if self.tkopt.demo_logo_style.value == "none":
            self.tkopt.demo_logo.value = False
        else:
            self.tkopt.demo_logo.value = True
            self.app.loadImages2()
            self.app.loadImages4()

    def setDialogIconStyle(self, style):
        if self._cancelDrag(break_pause=False):
            return
        self.app.loadImages1()
        self.app.loadImages4()

    def setPauseTextStyle(self, style=None):
        if self._cancelDrag(break_pause=False):
            return
        self.app.loadImages2()
        self.app.loadImages4()
        if self.tkopt.pause.value:
            self.app.game.displayPauseImage()

    def setRedealIconStyle(self, style=None):
        if self._cancelDrag(break_pause=False):
            return
        self.app.loadImages2()
        self.app.loadImages4()
        try:
            images = self.app.game.canvas.findImagesByType("redeal_image")
            for i in images:
                i.group.stack.updateRedealImage()
        except:  # noqa
            pass

    #
    # stacks descriptions
    #

    def mStackDesk(self, *event):
        if self.game.stackdesc_list:
            self.game.deleteStackDesc()
        else:
            if self._cancelDrag(break_pause=True):
                return
            self.game.showStackDesc()

    def wizardDialog(self, edit=False):
        from pysollib.wizardutil import write_game, reset_wizard
        from wizarddialog import WizardDialog

        if edit:
            reset_wizard(self.game)
        else:
            reset_wizard(None)
        d = WizardDialog(self.top, _('Solitaire Wizard'), self.app)
        if d.status == 0 and d.button == 0:
            try:
                if edit:
                    gameid = write_game(self.app, game=self.game)
                else:
                    gameid = write_game(self.app)
            except Exception:
                return
            if SELECT_GAME_MENU:
                menu = self.__menupath[".menubar.select.customgames"][2]

                def select_func(gi): return gi.si.game_type == GI.GT_CUSTOM
                games = map(self.app.gdb.get,
                            self.app.gdb.getGamesIdSortedByName())
                games = filter(select_func, games)
                self.updateGamesMenu(menu, games)

            self.tkopt.gameid.value = gameid
            self._mSelectGame(gameid, force=True)

    def mWizard(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.wizardDialog()

    def mWizardEdit(self, *event):
        if self._cancelDrag(break_pause=False):
            return
        self.wizardDialog(edit=True)


'''
'''
