1#!/usr/bin/env python
2# -*- mode: python; coding: utf-8; -*-
3# ---------------------------------------------------------------------------#
4#
5# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
6# Copyright (C) 2003 Mt. Hood Playing Card Co.
7# Copyright (C) 2005-2009 Skomoroh
8# Copyright (C) 2017 LB
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22#
23# ---------------------------------------------------------------------------#
24
25import math
26import os
27import re
28
29from kivy.event import EventDispatcher
30from kivy.properties import BooleanProperty
31from kivy.properties import NumericProperty
32from kivy.properties import StringProperty
33
34from pysollib.gamedb import GI
35from pysollib.kivy.LApp import LMenu
36from pysollib.kivy.LApp import LMenuItem
37from pysollib.kivy.LApp import LScrollView
38from pysollib.kivy.LApp import LTopLevel
39from pysollib.kivy.LApp import LTreeNode
40from pysollib.kivy.LApp import LTreeRoot
41from pysollib.kivy.findcarddialog import destroy_find_card_dialog
42from pysollib.kivy.selectcardset import SelectCardsetDialogWithPreview
43from pysollib.kivy.selectgame import SelectGameDialog
44from pysollib.kivy.solverdialog import connect_game_solver_dialog
45from pysollib.kivy.tkconst import CURSOR_WATCH, EVENT_HANDLED, EVENT_PROPAGATE
46from pysollib.kivy.tkconst import TOOLBAR_BUTTONS
47from pysollib.kivy.tkutil import after_idle
48from pysollib.kivy.tkutil import bind
49from pysollib.mfxutil import Struct
50from pysollib.mygettext import _
51from pysollib.pysoltk import MfxMessageDialog
52from pysollib.pysoltk import connect_game_find_card_dialog
53from pysollib.settings import SELECT_GAME_MENU
54from pysollib.settings import TITLE
55
56
57# ************************************************************************
58# * tk emuls:
59# ************************************************************************
60
61
62class TkVarObj(EventDispatcher):
63    def __init(self):
64        self.value = None
65
66    def set(self, v):
67        if v is None:
68            if type(self.value) is str:
69                v = ''
70        self.value = v
71
72    def get(self):
73        return self.value
74
75
76class BooleanVar(TkVarObj):
77    value = BooleanProperty(False)
78
79
80class IntVar(TkVarObj):
81    value = NumericProperty(0)
82
83
84class StringVar(TkVarObj):
85    value = StringProperty('')
86
87# ************************************************************************
88# * Menu Dialogs
89# ************************************************************************
90
91
92class LMenuDialog(object):
93
94    dialogCache = {}
95
96    def make_pop_command(self, parent, title):
97        def pop_command(event):
98            print('event = %s' % event)
99            parent.popWork(title)
100        return pop_command
101
102    def __init__(self, menubar, parent, title, app, **kw):
103        super(LMenuDialog, self).__init__()
104
105        self.menubar = menubar
106        self.parent = parent
107        self.app = app
108        self.title = title
109        self.window = None
110        self.running = False
111        self.persist = False
112        if 'persist' in kw:
113            self.persist = kw['persist']
114
115        # prüfen ob noch aktiv - toggle.
116
117        if parent.workStack.peek(title) is not None:
118            parent.popWork(title)
119            return
120
121        if self.persist and title in self.dialogCache:
122            parent.pushWork(title, self.dialogCache[title])
123            return
124
125        pc = self.closeWindow = self.make_pop_command(parent, title)
126
127        # neuen Dialog aufbauen.
128
129        window = LTopLevel(parent, title, **kw)
130        window.titleline.bind(on_press=pc)
131        self.parent.pushWork(title, window)
132        self.window = window
133        self.running = True
134
135        if self.persist:
136            self.dialogCache[title] = window
137
138        # Tree skelett.
139
140        tv = self.tvroot = LTreeRoot(root_options=dict(text='EditTree'))
141        tv.hide_root = True
142        tv.size_hint = 1, None
143        tv.bind(minimum_height=tv.setter('height'))
144
145        # menupunkte aufbauen.
146
147        self.buildTree(tv, None)
148
149        # tree in einem Scrollwindow präsentieren.
150
151        root = LScrollView(pos=(0, 0))
152        root.add_widget(tv)
153        self.window.content.add_widget(root)
154
155    def buildTree(self, tree, node):
156        print('buildTree base')
157        # to implement in dervied class
158        pass
159
160# ************************************************************************
161
162
163class MainMenuDialog(LMenuDialog):
164
165    def __init__(self, menubar, parent, title, app, **kw):
166        kw['size_hint'] = (0.2, 1)
167        kw['persist'] = True
168        super(MainMenuDialog, self).__init__(
169            menubar, parent, title, app, **kw)
170
171    def make_game_command(self, command):
172        def game_command():
173            command()
174            self.closeWindow(0)
175        return game_command
176
177    def buildTree(self, tv, node):
178        rg = tv.add_node(
179            LTreeNode(
180                text=_("File"),
181                command=self.make_game_command(self.menubar.mFileMenuDialog)))
182        rg = tv.add_node(
183            LTreeNode(
184                text=_("Games"),
185                command=self.make_game_command(
186                    self.menubar.mSelectGameDialog)))
187        rg = tv.add_node(
188            LTreeNode(
189                text=_("Tools"),
190                command=self.make_game_command(self.menubar.mEditMenuDialog)))
191        rg = tv.add_node(
192            LTreeNode(
193                text=_("Statistics"),
194                command=self.make_game_command(self.menubar.mGameMenuDialog)))
195        rg = tv.add_node(
196            LTreeNode(
197                text=_("Assist"),
198                command=self.make_game_command(
199                    self.menubar.mAssistMenuDialog)))
200        rg = tv.add_node(
201            LTreeNode(
202                text=_("Options"),
203                command=self.make_game_command(
204                    self.menubar.mOptionsMenuDialog)))
205        rg = tv.add_node(
206            LTreeNode(
207                text=_("Help"),
208                command=self.make_game_command(self.menubar.mHelpMenuDialog)))
209        del rg
210
211# ************************************************************************
212
213
214class FileMenuDialog(LMenuDialog):
215
216    def __init__(self, menubar, parent, title, app, **kw):
217        kw['size_hint'] = (0.3, 1)
218        super(FileMenuDialog, self).__init__(
219            menubar, parent, title, app, **kw)
220
221    def make_game_command(self, key, command):
222        def game_command():
223            command(key)
224        return game_command
225
226    def buildTree(self, tv, node):
227        rg = tv.add_node(
228            LTreeNode(text=_('Recent games')))
229        # Recent Liste
230        recids = self.app.opt.recent_gameid
231        # recgames = []
232        for rid in recids:
233            gi = self.app.getGameInfo(rid)
234            if gi:
235                command = self.make_game_command(
236                    rid, self.menubar._mSelectGame)
237                tv.add_node(
238                    LTreeNode(text=gi.name, command=command), rg)
239
240        rg = tv.add_node(
241            LTreeNode(text=_('Favorite games')))
242        if rg:
243            tv.add_node(LTreeNode(
244                text=_('<Add>'), command=self.menubar.mAddFavor), rg)
245            tv.add_node(LTreeNode(
246                text=_('<Remove>'), command=self.menubar.mDelFavor), rg)
247
248            # Recent Liste
249            favids = self.app.opt.favorite_gameid
250            # favgames = []
251            for fid in favids:
252                gi = self.app.getGameInfo(fid)
253                if gi:
254                    command = self.make_game_command(
255                        fid, self.menubar._mSelectGame)
256                    tv.add_node(
257                        LTreeNode(text=gi.name, command=command), rg)
258
259        tv.add_node(LTreeNode(
260            text=_('Load'), command=self.menubar.mOpen))
261        tv.add_node(LTreeNode(
262            text=_('Save'), command=self.menubar.mSaveAs))
263
264        tv.add_node(LTreeNode(
265            text=_('Quit'), command=self.menubar.mHoldAndQuit))
266
267# ************************************************************************
268
269
270class EditMenuDialog(LMenuDialog):  # Tools
271
272    def __init__(self, menubar, parent, title, app, **kw):
273        kw['size_hint'] = (0.2, 1)
274        kw['persist'] = True
275        super(EditMenuDialog, self).__init__(
276            menubar, parent, title, app, **kw)
277
278    def make_auto_command(self, variable, command):
279        def auto_command():
280            variable.set(not variable.get())
281            command()
282        return auto_command
283
284    def addCheckNode(self, tv, rg, title, auto_var, auto_com):
285        command = self.make_auto_command(auto_var, auto_com)
286        rg1 = tv.add_node(
287            LTreeNode(text=title, command=command, variable=auto_var), rg)
288        return rg1
289
290    def buildTree(self, tv, node):
291        tv.add_node(LTreeNode(
292            text=_('New game'), command=self.menubar.mNewGame))
293        tv.add_node(LTreeNode(
294            text=_('Restart game'), command=self.menubar.mRestart))
295
296        tv.add_node(LTreeNode(
297            text=_('Undo'), command=self.menubar.mUndo))
298        tv.add_node(LTreeNode(
299            text=_('Redo'), command=self.menubar.mRedo))
300        tv.add_node(LTreeNode(
301            text=_('Redo all'), command=self.menubar.mRedoAll))
302
303        tv.add_node(LTreeNode(
304            text=_('Auto drop'), command=self.menubar.mDrop))
305        tv.add_node(LTreeNode(
306            text=_('Shuffle tiles'), command=self.menubar.mShuffle))
307        tv.add_node(LTreeNode(
308            text=_('Deal cards'), command=self.menubar.mDeal))
309
310        self.addCheckNode(tv, None,
311                          _('Pause'),
312                          self.menubar.tkopt.pause,
313                          self.menubar.mPause)
314
315        tv.add_node(LTreeNode(
316            text=_('Load game'), command=self.menubar.mOpen))
317        tv.add_node(LTreeNode(
318            text=_('Save game'), command=self.menubar.mSaveAs))
319
320        tv.add_node(LTreeNode(
321            text=_('Help'), command=self.menubar.mHelpRules))
322
323        # -------------------------------------------
324        # TBD ?
325        '''
326        menu.add_separator()
327        submenu = MfxMenu(menu, label=n_("&Set bookmark"))
328        for i in range(9):
329            label = _("Bookmark %d") % (i + 1)
330            submenu.add_command(
331                label=label, command=lambda i=i: self.mSetBookmark(i))
332        submenu = MfxMenu(menu, label=n_("Go&to bookmark"))
333        for i in range(9):
334            label = _("Bookmark %d") % (i + 1)
335            acc = m + "%d" % (i + 1)
336            submenu.add_command(
337                label=label,
338                command=lambda i=i: self.mGotoBookmark(i),
339                accelerator=acc)
340        menu.add_command(
341            label=n_("&Clear bookmarks"), command=self.mClearBookmarks)
342        menu.add_separator()
343        '''
344        # und solitär wizard (-> custom games).
345        '''
346        tv.add_node(LTreeNode(
347            text='Solitaire &Wizard', command=self.menubar.mWizard))
348        tv.add_node(LTreeNode(
349                text='Edit current game', command=self.menubar.mWizardEdit))
350        '''
351
352# ************************************************************************
353
354
355class GameMenuDialog(LMenuDialog):
356
357    def __init__(self, menubar, parent, title, app, **kw):
358        kw['size_hint'] = (0.2, 1)
359        kw['persist'] = True
360        super(GameMenuDialog, self).__init__(
361            menubar, parent, title, app, **kw)
362
363    def make_command(self, key, command):
364        def stats_command():
365            kw = {}
366            kw['mode'] = key
367            command(**kw)
368        return stats_command
369
370    def buildTree(self, tv, node):
371        tv.add_node(LTreeNode(
372            text=_('Current game...'),
373            command=self.make_command(101, self.menubar.mPlayerStats)), None)
374
375        # tv.add_node(LTreeNode(
376        #   text='All games ...',
377        #   command=self.make_command(102, self.menubar.mPlayerStats)), None)
378
379    # -------------------------------------------
380    # TBD ? - just to remember original tk code.
381    '''
382        menu.add_command(
383            label=n_("S&tatus..."),
384            command=lambda x: self.mPlayerStats(mode=100), accelerator=m+"Y")
385        menu.add_checkbutton(
386            label=n_("&Comments..."), variable=self.tkopt.comment,
387            command=self.mEditGameComment)
388    '''
389    '''
390        menu.add_separator()
391        submenu = MfxMenu(menu, label=n_("&Statistics"))
392        submenu.add_command(
393            label=n_("Current game..."),
394            command=lambda x: self.mPlayerStats(mode=101))
395        submenu.add_command(
396            label=n_("All games..."),
397            command=lambda x: self.mPlayerStats(mode=102))
398        submenu.add_separator()
399        submenu.add_command(
400            label=n_("Session log..."),
401            command=lambda x: self.mPlayerStats(mode=104))
402        submenu.add_command(
403            label=n_("Full log..."),
404            command=lambda x: self.mPlayerStats(mode=103))
405        submenu.add_separator()
406        submenu.add_command(
407            label=TOP_TITLE+"...",
408            command=lambda x: self.mPlayerStats(mode=105),
409            accelerator=m+"T")
410        submenu.add_command(
411            label=n_("Progression..."),
412            command=lambda x: self.mPlayerStats(mode=107))
413        submenu = MfxMenu(menu, label=n_("D&emo statistics"))
414        submenu.add_command(
415            label=n_("Current game..."),
416            command=lambda x: self.mPlayerStats(mode=1101))
417        submenu.add_command(
418            label=n_("All games..."),
419            command=lambda x: self.mPlayerStats(mode=1102))
420    '''
421
422# ************************************************************************
423
424
425class AssistMenuDialog(LMenuDialog):
426
427    def __init__(self, menubar, parent, title, app, **kw):
428        kw['size_hint'] = (0.2, 1)
429        kw['persist'] = True
430        super(AssistMenuDialog, self).__init__(
431            menubar, parent, title, app, **kw)
432
433    def buildTree(self, tv, node):
434        tv.add_node(LTreeNode(
435            text=_('Hint'), command=self.menubar.mHint))
436
437        tv.add_node(LTreeNode(
438            text=_('Highlight piles'), command=self.menubar.mHighlightPiles))
439
440        # tv.add_node(LTreeNode(
441        #   text='Find Card', command=self.menubar.mFindCard))
442
443        tv.add_node(LTreeNode(
444            text=_('Demo'), command=self.menubar.mDemo))
445
446        # -------------------------------------------
447        # TBD. How ?
448
449        '''
450        menu.add_command(
451            label=n_("Demo (&all games)"), command=self.mMixedDemo)
452        if USE_FREECELL_SOLVER:
453            menu.add_command(label=n_("&Solver"), command=self.mSolver)
454        else:
455            menu.add_command(label=n_("&Solver"), state='disabled')
456        menu.add_separator()
457        menu.add_command(
458            label=n_("&Piles description"),
459            command=self.mStackDesk, accelerator="F2")
460        '''
461
462# ************************************************************************
463
464
465class OptionsMenuDialog(LMenuDialog):
466
467    def __init__(self, menubar, parent, title, app, **kw):
468        kw['size_hint'] = (0.5, 1)
469        kw['persist'] = True
470        super(OptionsMenuDialog, self).__init__(
471            menubar, parent, title, app, **kw)
472
473    def make_auto_command(self, variable, command):
474        def auto_command():
475            variable.set(not variable.get())
476            command()
477        return auto_command
478
479    def addCheckNode(self, tv, rg, title, auto_var, auto_com):
480        command = self.make_auto_command(auto_var, auto_com)
481        rg1 = tv.add_node(
482            LTreeNode(text=title, command=command, variable=auto_var), rg)
483        return rg1
484
485    def make_val_command(self, variable, value, command):
486        def val_command():
487            variable.set(value)
488            command()
489        return val_command
490
491    def make_vars_command(self, command, key):
492        def vars_command():
493            command(key)
494        return vars_command
495
496    def addRadioNode(self, tv, rg, title, auto_var, auto_val, auto_com):
497        command = self.make_val_command(auto_var, auto_val, auto_com)
498        rg1 = tv.add_node(
499            LTreeNode(text=title,
500                      command=command,
501                      variable=auto_var, value=auto_val), rg)
502        return rg1
503
504    def buildTree(self, tv, node):
505
506        # -------------------------------------------
507        # Automatic play settings
508
509        rg = tv.add_node(
510            LTreeNode(text=_('Automatic play')))
511        if rg:
512            self.addCheckNode(tv, rg,
513                              _('Auto face up'),
514                              self.menubar.tkopt.autofaceup,
515                              self.menubar.mOptAutoFaceUp)
516
517            self.addCheckNode(tv, rg,
518                              _('Auto drop'),
519                              self.menubar.tkopt.autodrop,
520                              self.menubar.mOptAutoDrop)
521
522            self.addCheckNode(tv, rg,
523                              _('Auto deal'),
524                              self.menubar.tkopt.autodeal,
525                              self.menubar.mOptAutoDeal)
526
527            # submenu.add_separator()
528
529            self.addCheckNode(tv, rg,
530                              _('Quick play'),
531                              self.menubar.tkopt.quickplay,
532                              self.menubar.mOptQuickPlay)
533
534        # -------------------------------------------
535        # Player assistance
536
537        rg = tv.add_node(
538            LTreeNode(text=_('Assist level')))
539        if rg:
540            self.addCheckNode(tv, rg,
541                              _('Enable undo'),
542                              self.menubar.tkopt.undo,
543                              self.menubar.mOptEnableUndo)
544
545            self.addCheckNode(tv, rg,
546                              _('Enable bookmarks'),
547                              self.menubar.tkopt.bookmarks,
548                              self.menubar.mOptEnableBookmarks)
549
550            self.addCheckNode(tv, rg,
551                              _('Enable hint'),
552                              self.menubar.tkopt.hint,
553                              self.menubar.mOptEnableHint)
554
555            self.addCheckNode(tv, rg,
556                              _('Enable shuffle'),
557                              self.menubar.tkopt.shuffle,
558                              self.menubar.mOptEnableShuffle)
559
560            self.addCheckNode(tv, rg,
561                              _('Enable highlight piles'),
562                              self.menubar.tkopt.highlight_piles,
563                              self.menubar.mOptEnableHighlightPiles)
564
565            self.addCheckNode(tv, rg,
566                              _('Enable highlight cards'),
567                              self.menubar.tkopt.highlight_cards,
568                              self.menubar.mOptEnableHighlightCards)
569
570            self.addCheckNode(tv, rg,
571                              _('Enable highlight same rank'),
572                              self.menubar.tkopt.highlight_samerank,
573                              self.menubar.mOptEnableHighlightSameRank)
574
575            self.addCheckNode(tv, rg,
576                              _('Highlight no matching'),
577                              self.menubar.tkopt.highlight_not_matching,
578                              self.menubar.mOptEnableHighlightNotMatching)
579
580            # submenu.add_separator()
581
582            self.addCheckNode(tv, rg,
583                              _('Show removed tiles (in Mahjongg games)'),
584                              self.menubar.tkopt.mahjongg_show_removed,
585                              self.menubar.mOptMahjonggShowRemoved)
586
587            self.addCheckNode(tv, rg,
588                              _('Show hint arrow (in Shisen-Sho games)'),
589                              self.menubar.tkopt.shisen_show_hint,
590                              self.menubar.mOptShisenShowHint)
591
592            self.addCheckNode(tv, rg,
593                              _('Deal all cards (in Accordion type games)'),
594                              self.menubar.tkopt.accordion_deal_all,
595                              self.menubar.mOptAccordionDealAll)
596
597            # submenu.add_separator()
598
599        # -------------------------------------------
600        # Language options
601
602        rg = tv.add_node(
603            LTreeNode(text=_('Language')))
604        if rg:
605            self.addRadioNode(tv, rg,
606                              _('Default'),
607                              self.menubar.tkopt.language, '',
608                              self.menubar.mOptLanguage)
609            self.addRadioNode(tv, rg,
610                              _('English'),
611                              self.menubar.tkopt.language, 'en',
612                              self.menubar.mOptLanguage)
613            self.addRadioNode(tv, rg,
614                              _('German'),
615                              self.menubar.tkopt.language, 'de',
616                              self.menubar.mOptLanguage)
617            self.addRadioNode(tv, rg,
618                              _('Italian'),
619                              self.menubar.tkopt.language, 'it',
620                              self.menubar.mOptLanguage)
621            self.addRadioNode(tv, rg,
622                              _('Polish'),
623                              self.menubar.tkopt.language, 'pl',
624                              self.menubar.mOptLanguage)
625            self.addRadioNode(tv, rg,
626                              _('Russian'),
627                              self.menubar.tkopt.language, 'ru',
628                              self.menubar.mOptLanguage)
629
630        # -------------------------------------------
631        # Sound options
632
633        rg = tv.add_node(
634            LTreeNode(text=_('Sound')))
635        if rg:
636            self.addCheckNode(tv, rg,
637                              _('Enable'),
638                              self.menubar.tkopt.sound,
639                              self.menubar.mOptSoundDialog)
640
641            rg1 = tv.add_node(
642                LTreeNode(text=_('Volume')), rg)
643            if rg1:
644                self.addRadioNode(tv, rg1,
645                                  _('100%'),
646                                  self.menubar.tkopt.sound_sample_volume, 100,
647                                  self.menubar.mOptSoundSampleVol)
648                self.addRadioNode(tv, rg1,
649                                  _('75%'),
650                                  self.menubar.tkopt.sound_sample_volume, 75,
651                                  self.menubar.mOptSoundSampleVol)
652                self.addRadioNode(tv, rg1,
653                                  _('50%'),
654                                  self.menubar.tkopt.sound_sample_volume, 50,
655                                  self.menubar.mOptSoundSampleVol)
656                self.addRadioNode(tv, rg1,
657                                  _('25%'),
658                                  self.menubar.tkopt.sound_sample_volume, 25,
659                                  self.menubar.mOptSoundSampleVol)
660
661            rg1 = tv.add_node(
662                LTreeNode(text=_('Samples')), rg)
663            if rg1:
664                key = 'areyousure'
665                self.addCheckNode(
666                    tv, rg1,
667                    _('are you sure'),
668                    self.menubar.tkopt.sound_sample_vars[key],
669                    self.make_vars_command(self.menubar.mOptSoundSample, key))
670                key = 'autodrop'
671                self.addCheckNode(
672                    tv, rg1,
673                    _('auto drop'),
674                    self.menubar.tkopt.sound_sample_vars[key],
675                    self.make_vars_command(self.menubar.mOptSoundSample, key))
676                key = 'autoflip'
677                self.addCheckNode(
678                    tv, rg1,
679                    _('auto flip'),
680                    self.menubar.tkopt.sound_sample_vars[key],
681                    self.make_vars_command(self.menubar.mOptSoundSample, key))
682                key = 'autopilotlost'
683                self.addCheckNode(
684                    tv, rg1,
685                    _('auto pilot lost'),
686                    self.menubar.tkopt.sound_sample_vars[key],
687                    self.make_vars_command(self.menubar.mOptSoundSample, key))
688                key = 'autopilotwon'
689                self.addCheckNode(
690                    tv, rg1,
691                    _('auto pilot won'),
692                    self.menubar.tkopt.sound_sample_vars[key],
693                    self.make_vars_command(self.menubar.mOptSoundSample, key))
694                key = 'deal'
695                self.addCheckNode(
696                    tv, rg1,
697                    _('deal'),
698                    self.menubar.tkopt.sound_sample_vars[key],
699                    self.make_vars_command(self.menubar.mOptSoundSample, key))
700                key = 'dealwaste'
701                self.addCheckNode(
702                    tv, rg1,
703                    _('deal waste'),
704                    self.menubar.tkopt.sound_sample_vars[key],
705                    self.make_vars_command(self.menubar.mOptSoundSample, key))
706                key = 'droppair'
707                self.addCheckNode(
708                    tv, rg1,
709                    _('drop pair'),
710                    self.menubar.tkopt.sound_sample_vars[key],
711                    self.make_vars_command(self.menubar.mOptSoundSample, key))
712                key = 'drop'
713                self.addCheckNode(
714                    tv, rg1,
715                    _('drop'),
716                    self.menubar.tkopt.sound_sample_vars[key],
717                    self.make_vars_command(self.menubar.mOptSoundSample, key))
718                key = 'flip'
719                self.addCheckNode(
720                    tv, rg1,
721                    _('flip'),
722                    self.menubar.tkopt.sound_sample_vars[key],
723                    self.make_vars_command(self.menubar.mOptSoundSample, key))
724                key = 'move'
725                self.addCheckNode(
726                    tv, rg1,
727                    _('move'),
728                    self.menubar.tkopt.sound_sample_vars[key],
729                    self.make_vars_command(self.menubar.mOptSoundSample, key))
730                key = 'nomove'
731                self.addCheckNode(
732                    tv, rg1,
733                    _('no move'),
734                    self.menubar.tkopt.sound_sample_vars[key],
735                    self.make_vars_command(self.menubar.mOptSoundSample, key))
736                key = 'redo'
737                self.addCheckNode(
738                    tv, rg1,
739                    _('redo'),
740                    self.menubar.tkopt.sound_sample_vars[key],
741                    self.make_vars_command(self.menubar.mOptSoundSample, key))
742                key = 'startdrag'
743                self.addCheckNode(
744                    tv, rg1,
745                    _('start drag'),
746                    self.menubar.tkopt.sound_sample_vars[key],
747                    self.make_vars_command(self.menubar.mOptSoundSample, key))
748                key = 'turnwaste'
749                self.addCheckNode(
750                    tv, rg1,
751                    _('turn waste'),
752                    self.menubar.tkopt.sound_sample_vars[key],
753                    self.make_vars_command(self.menubar.mOptSoundSample, key))
754                key = 'undo'
755                self.addCheckNode(
756                    tv, rg1,
757                    _('undo'),
758                    self.menubar.tkopt.sound_sample_vars[key],
759                    self.make_vars_command(self.menubar.mOptSoundSample, key))
760                key = 'gamefinished'
761                self.addCheckNode(
762                    tv, rg1,
763                    _('game finished'),
764                    self.menubar.tkopt.sound_sample_vars[key],
765                    self.make_vars_command(self.menubar.mOptSoundSample, key))
766                key = 'gamelost'
767                self.addCheckNode(
768                    tv, rg1,
769                    _('game lost'),
770                    self.menubar.tkopt.sound_sample_vars[key],
771                    self.make_vars_command(self.menubar.mOptSoundSample, key))
772                key = 'gameperfect'
773                self.addCheckNode(
774                    tv, rg1,
775                    _('game perfect'),
776                    self.menubar.tkopt.sound_sample_vars[key],
777                    self.make_vars_command(self.menubar.mOptSoundSample, key))
778                key = 'gamewon'
779                self.addCheckNode(
780                    tv, rg1,
781                    _('game won'),
782                    self.menubar.tkopt.sound_sample_vars[key],
783                    self.make_vars_command(self.menubar.mOptSoundSample, key))
784                key = 'extra'
785                self.addCheckNode(
786                    tv, rg1,
787                    _('Other'),
788                    self.menubar.tkopt.sound_sample_vars[key],
789                    self.make_vars_command(self.menubar.mOptSoundSample, key))
790
791        # -------------------------------------------
792        # Cardsets and card backside options
793
794        rg = tv.add_node(
795            LTreeNode(text=_('Cardsets')))
796        if rg:
797            self.menubar.tkopt.cardset.set(self.app.cardset.index)
798
799            csm = self.app.cardset_manager
800            # cnt = csm.len()
801            i = 0
802            while 1:
803                cs = csm.get(i)
804                if cs is None:
805                    break
806                rg1 = self.addRadioNode(tv, rg,
807                                        cs.name,
808                                        self.menubar.tkopt.cardset, i,
809                                        self.menubar.mOptCardset)
810                if rg1:
811                    cbs = cs.backnames
812                    self.menubar.tkopt.cardbacks[i] = IntVar()
813                    self.menubar.tkopt.cardbacks[i].set(cs.backindex)
814
815                    bcnt = len(cbs)
816                    bi = 0
817                    while 1:
818                        if bi == bcnt:
819                            break
820                        cb = cbs[bi]
821                        self.addRadioNode(
822                            tv, rg1,
823                            cb,
824                            self.menubar.tkopt.cardbacks[i], bi,
825                            self.make_vars_command(
826                                self.menubar.mOptSetCardback, i))
827                        bi += 1
828
829                i += 1
830
831        # -------------------------------------------
832        # Table background settings
833
834        rg = tv.add_node(
835            LTreeNode(text=_('Table')))
836        if rg:
837            rg1 = tv.add_node(
838                LTreeNode(text=_('Solid colors')), rg)
839            if rg1:
840                key = 'table'
841                self.addRadioNode(
842                    tv, rg1,
843                    _('Blue'),
844                    self.menubar.tkopt.color_vars[key], '#0082df',
845                    self.menubar.mOptTableColor)
846                self.addRadioNode(
847                    tv, rg1,
848                    _('Green'),
849                    self.menubar.tkopt.color_vars[key], '#008200',
850                    self.menubar.mOptTableColor)
851                self.addRadioNode(
852                    tv, rg1,
853                    _('Navy'),
854                    self.menubar.tkopt.color_vars[key], '#000086',
855                    self.menubar.mOptTableColor)
856                self.addRadioNode(
857                    tv, rg1,
858                    _('Olive'),
859                    self.menubar.tkopt.color_vars[key], '#868200',
860                    self.menubar.mOptTableColor)
861                self.addRadioNode(
862                    tv, rg1,
863                    _('Orange'),
864                    self.menubar.tkopt.color_vars[key], '#f79600',
865                    self.menubar.mOptTableColor)
866                self.addRadioNode(
867                    tv, rg1,
868                    _('Teal'),
869                    self.menubar.tkopt.color_vars[key], '#008286',
870                    self.menubar.mOptTableColor)
871
872            rg1 = tv.add_node(
873                LTreeNode(text=_('Textures')), rg)
874            rg2 = tv.add_node(
875                LTreeNode(text=_('Images')), rg)
876
877            if rg1 or rg2:
878                tm = self.app.tabletile_manager
879                # cnt = tm.len()
880                i = 1
881                while True:
882                    ti = tm.get(i)
883
884                    if ti is None:
885                        break
886                    if ti.save_aspect == 0 and ti.stretch == 0 and rg1:
887                        self.addRadioNode(tv, rg1,
888                                          ti.name,
889                                          self.menubar.tkopt.tabletile, i,
890                                          self.menubar.mOptTileSet)
891                    if (ti.save_aspect == 1 or ti.stretch == 1) and rg2:
892                        self.addRadioNode(tv, rg2,
893                                          ti.name,
894                                          self.menubar.tkopt.tabletile, i,
895                                          self.menubar.mOptTileSet)
896                    i += 1
897
898        # -------------------------------------------
899        # Card view options
900
901        rg = tv.add_node(
902            LTreeNode(text=_('Card view')))
903        if rg:
904            self.addCheckNode(tv, rg,
905                              _('Card shadow'),
906                              self.menubar.tkopt.shadow,
907                              self.menubar.mOptShadow)
908
909            self.addCheckNode(tv, rg,
910                              _('Shade legal moves'),
911                              self.menubar.tkopt.shade,
912                              self.menubar.mOptShade)
913
914            self.addCheckNode(tv, rg,
915                              _('Negative cards bottom'),
916                              self.menubar.tkopt.negative_bottom,
917                              self.menubar.mOptNegativeBottom)
918
919            self.addCheckNode(tv, rg,
920                              _('Shrink face-down cards'),
921                              self.menubar.tkopt.shrink_face_down,
922                              self.menubar.mOptShrinkFaceDown)
923
924            self.addCheckNode(tv, rg,
925                              _('Shade filled stacks'),
926                              self.menubar.tkopt.shade_filled_stacks,
927                              self.menubar.mOptShadeFilledStacks)
928
929        # -------------------------------------------
930        # Animation settins
931
932        rg = tv.add_node(
933            LTreeNode(text=_('Animations')))
934        if rg:
935            self.addRadioNode(tv, rg,
936                              _('None'),
937                              self.menubar.tkopt.animations, 0,
938                              self.menubar.mOptAnimations)
939
940            self.addRadioNode(tv, rg,
941                              _('Very fast'),
942                              self.menubar.tkopt.animations, 1,
943                              self.menubar.mOptAnimations)
944
945            self.addRadioNode(tv, rg,
946                              _('Fast'),
947                              self.menubar.tkopt.animations, 2,
948                              self.menubar.mOptAnimations)
949
950            self.addRadioNode(tv, rg,
951                              _('Medium'),
952                              self.menubar.tkopt.animations, 3,
953                              self.menubar.mOptAnimations)
954
955            self.addRadioNode(tv, rg,
956                              _('Slow'),
957                              self.menubar.tkopt.animations, 4,
958                              self.menubar.mOptAnimations)
959
960            self.addRadioNode(tv, rg,
961                              _('Very slow'),
962                              self.menubar.tkopt.animations, 5,
963                              self.menubar.mOptAnimations)
964
965            # submenu.add_separator()
966
967            self.addCheckNode(tv, rg,
968                              _('Redeal animation'),
969                              self.menubar.tkopt.redeal_animation,
970                              self.menubar.mRedealAnimation)
971
972            self.addCheckNode(tv, rg,
973                              _('Winning animation'),
974                              self.menubar.tkopt.win_animation,
975                              self.menubar.mWinAnimation)
976
977        # -------------------------------------------
978        # Touch mode settings
979
980        rg = tv.add_node(
981            LTreeNode(text=_('Touch mode')))
982        if rg:
983            self.addRadioNode(tv, rg,
984                              _('Drag-and-Drop'),
985                              self.menubar.tkopt.mouse_type, 'drag-n-drop',
986                              self.menubar.mOptMouseType)
987
988            self.addRadioNode(tv, rg,
989                              _('Point-and-Click'),
990                              self.menubar.tkopt.mouse_type, 'point-n-click',
991                              self.menubar.mOptMouseType)
992
993            # sinnlos mit touch-device:
994            # self.addRadioNode(tv, rg,
995            #   'Sticky mouse',
996            #   self.menubar.tkopt.mouse_type, u'sticky-mouse',
997            #   self.menubar.mOptMouseType)
998
999            # submenu.add_separator()
1000
1001            # sinnlos mit touch-device:
1002            # self.addCheckNode(tv, rg,
1003            #   'Use mouse for undo/redo',
1004            #   self.menubar.tkopt.mouse_undo,
1005            #   self.menubar.mOptMouseUndo)
1006
1007        # submenu.add_separator()
1008
1009        # -------------------------------------------
1010        # TBD ?
1011
1012        '''
1013        menu.add_command(label=n_("&Fonts..."), command=self.mOptFonts)
1014        menu.add_command(label=n_("&Colors..."), command=self.mOptColors)
1015        menu.add_command(label=n_("Time&outs..."), command=self.mOptTimeouts)
1016        menu.add_separator()
1017        '''
1018
1019        # -------------------------------------------
1020        # Toolbar options
1021
1022        rg = tv.add_node(
1023            LTreeNode(text=_('Toolbar')))
1024        if rg:
1025            self.addRadioNode(tv, rg,
1026                              _('Hide'),
1027                              self.menubar.tkopt.toolbar, 0,
1028                              self.menubar.mOptToolbar)
1029
1030            # not supported: Top, Bottom
1031            # self.addRadioNode(tv, rg,
1032            #   'Top',
1033            #   self.menubar.tkopt.toolbar, 1,
1034            #   self.menubar.mOptToolbar)
1035            # self.addRadioNode(tv, rg,
1036            #   'Bottom',
1037            #   self.menubar.tkopt.toolbar, 2,
1038            #   self.menubar.mOptToolbar)
1039
1040            self.addRadioNode(tv, rg,
1041                              _('Left'),
1042                              self.menubar.tkopt.toolbar, 3,
1043                              self.menubar.mOptToolbar)
1044            self.addRadioNode(tv, rg,
1045                              _('Right'),
1046                              self.menubar.tkopt.toolbar, 4,
1047                              self.menubar.mOptToolbar)
1048
1049        # -------------------------------------------
1050        # Statusbar - not implemented
1051
1052        '''
1053        submenu = MfxMenu(menu, label=n_("Stat&usbar"))
1054        submenu.add_checkbutton(
1055             label=n_("Show &statusbar"),
1056             variable=self.tkopt.statusbar,
1057             command=self.mOptStatusbar)
1058        submenu.add_checkbutton(
1059            label=n_("Show &number of cards"),
1060            variable=self.tkopt.num_cards,
1061            command=self.mOptNumCards)
1062        submenu.add_checkbutton(
1063            label=n_("Show &help bar"),
1064            variable=self.tkopt.helpbar,
1065            command=self.mOptHelpbar)
1066        '''
1067
1068        # -------------------------------------------
1069        # general options
1070
1071        # self.addCheckNode(tv, None,
1072        #   'Save games geometry',
1073        #   self.menubar.tkopt.save_games_geometry,
1074        #   self.menubar.mOptSaveGamesGeometry)
1075
1076        # self.addCheckNode(tv, None,
1077        #   'Demo logo',
1078        #   self.menubar.tkopt.demo_logo,
1079        #   self.menubar.mOptDemoLogo)
1080
1081        self.addCheckNode(tv, None,
1082                          _('Startup splash screen'),
1083                          self.menubar.tkopt.splashscreen,
1084                          self.menubar.mOptSplashscreen)
1085
1086        self.addCheckNode(tv, None,
1087                          _('Winning splash'),
1088                          self.menubar.tkopt.display_win_message,
1089                          self.menubar.mWinDialog)
1090
1091
1092# ************************************************************************
1093
1094
1095class HelpMenuDialog(LMenuDialog):
1096    def __init__(self, menubar, parent, title, app, **kw):
1097        kw['size_hint'] = (0.3, 1)
1098        kw['persist'] = True
1099        super(HelpMenuDialog, self).__init__(menubar, parent, title, app, **kw)
1100
1101    def make_help_command(self, command):
1102        def help_command():
1103            command()
1104            self.closeWindow(0)
1105        return help_command
1106
1107    def buildTree(self, tv, node):
1108        tv.add_node(
1109            LTreeNode(
1110                text=_('Contents'),
1111                command=self.make_help_command(self.menubar.mHelp)))
1112        tv.add_node(
1113            LTreeNode(
1114                text=_('How to use PySol'),
1115                command=self.make_help_command(self.menubar.mHelpHowToPlay)))
1116        tv.add_node(
1117            LTreeNode(
1118                text=_('Rules for this game'),
1119                command=self.make_help_command(self.menubar.mHelpRules)))
1120        tv.add_node(
1121            LTreeNode(
1122                text=_('License terms'),
1123                command=self.make_help_command(self.menubar.mHelpLicense)))
1124        tv.add_node(
1125            LTreeNode(
1126                text=_('About %s...') % TITLE,
1127                command=self.make_help_command(self.menubar.mHelpAbout)))
1128
1129        # tv.add_node(LTreeNode(
1130        #   text='AboutKivy ...',
1131        #   command=self.makeHtmlCommand(self.menubar, "kivy.html")))
1132
1133    def makeHtmlCommand(self, bar, htmlfile):
1134        def htmlCommand():
1135            bar.mHelpHtml(htmlfile)
1136
1137        return htmlCommand
1138
1139
1140# ************************************************************************
1141# *
1142# ************************************************************************
1143
1144
1145class EmulTkMenu(object):
1146
1147    def __init__(self, master, **kw):
1148
1149        self.name = kw["name"]
1150        self.n = 0
1151        self._w = None
1152        if (self.name):
1153            if master._w == '.':
1154                self._w = '.' + self.name
1155            else:
1156                self._w = master._w + '.' + self.name
1157        else:
1158            self.name = "<>"
1159
1160    def labeltoname(self, label):
1161        name = re.sub(r"[^0-9a-zA-Z]", "", label).lower()
1162        label = _(label)
1163        underline = label.find('&')
1164        if underline >= 0:
1165            label = label.replace('&', '')
1166        return name, label, underline
1167
1168    def add_cascade(self, cnf={}, **kw):
1169        self.add('cascade', cnf or kw)
1170        pass
1171
1172    def add(self, itemType, cnf={}):
1173        label = cnf.get("label")
1174        if label:
1175            name = cnf.get('name')
1176            if name:
1177                name, label, underline = self.labeltoname(label)
1178                cnf["underline"] = cnf.get("underline", underline)
1179                cnf["label"] = label
1180                if name and self.addPath:
1181                    path = str(self._w) + "." + name
1182                    self.addPath(path, self, self.n, cnf.get("menu"))
1183
1184    def cget(self, key):
1185        return key
1186
1187# ************************************************************************
1188
1189
1190class MfxMenubar(EmulTkMenu):
1191    addPath = None
1192
1193    def __init__(self, master, **kw):
1194        super(MfxMenubar, self).__init__(master, **kw)
1195        topmenu = self.name == 'menubar'
1196
1197        self.menu = LMenu(not topmenu, text=self.name)
1198        if topmenu:
1199            master.setMenu(self.menu)
1200
1201# ************************************************************************
1202# * - create menubar
1203# * - update menubar
1204# * - menu actions
1205# ************************************************************************
1206
1207
1208class PysolMenubarTk:
1209    def __init__(self, app, top, progress=None):
1210        self._createTkOpt()
1211        self._setOptions()
1212        # init columnbreak
1213#        self.__cb_max = int(self.top.winfo_screenheight()/23)
1214        self.__cb_max = 8
1215#         sh = self.top.winfo_screenheight()
1216#         self.__cb_max = 22
1217#         if sh >= 600: self.__cb_max = 27
1218#         if sh >= 768: self.__cb_max = 32
1219#         if sh >= 1024: self.__cb_max = 40
1220        self.progress = progress
1221        # create menus
1222        self.__menubar = None
1223        self.__menupath = {}
1224        self.__keybindings = {}
1225        self._createMenubar()
1226        self.top = top
1227
1228        if self.progress:
1229            self.progress.update(step=1)
1230
1231        # set the menubar
1232        # self.updateBackgroundImagesMenu()
1233        # self.top.config(menu=self.__menubar)
1234
1235    def _createTkOpt(self):
1236        # structure to convert menu-options to Toolkit variables
1237        self.tkopt = Struct(
1238            gameid=IntVar(),
1239            gameid_popular=IntVar(),
1240            comment=BooleanVar(),
1241            autofaceup=BooleanVar(),
1242            autodrop=BooleanVar(),
1243            autodeal=BooleanVar(),
1244            quickplay=BooleanVar(),
1245            undo=BooleanVar(),
1246            bookmarks=BooleanVar(),
1247            hint=BooleanVar(),
1248            shuffle=BooleanVar(),
1249            highlight_piles=BooleanVar(),
1250            highlight_cards=BooleanVar(),
1251            highlight_samerank=BooleanVar(),
1252            highlight_not_matching=BooleanVar(),
1253            mahjongg_show_removed=BooleanVar(),
1254            shisen_show_hint=BooleanVar(),
1255            accordion_deal_all=BooleanVar(),
1256            sound=BooleanVar(),
1257            sound_sample_volume=IntVar(),
1258            sound_music_volume=IntVar(),
1259            cardback=IntVar(),
1260            tabletile=IntVar(),
1261            animations=IntVar(),
1262            redeal_animation=BooleanVar(),
1263            win_animation=BooleanVar(),
1264            shadow=BooleanVar(),
1265            shade=BooleanVar(),
1266            shade_filled_stacks=BooleanVar(),
1267            shrink_face_down=BooleanVar(),
1268            toolbar=IntVar(),
1269            toolbar_style=StringVar(),
1270            toolbar_relief=StringVar(),
1271            toolbar_compound=StringVar(),
1272            toolbar_size=IntVar(),
1273            statusbar=BooleanVar(),
1274            num_cards=BooleanVar(),
1275            helpbar=BooleanVar(),
1276            save_games_geometry=BooleanVar(),
1277            splashscreen=BooleanVar(),
1278            demo_logo=BooleanVar(),
1279            mouse_type=StringVar(),
1280            mouse_undo=BooleanVar(),
1281            negative_bottom=BooleanVar(),
1282            display_win_message=BooleanVar(),
1283            pause=BooleanVar(),
1284            cardset=IntVar(),
1285            cardbacks={},
1286            toolbar_vars={},
1287            sound_sample_vars={},
1288            color_vars={},
1289            language=StringVar(),
1290        )
1291        for w in TOOLBAR_BUTTONS:
1292            self.tkopt.toolbar_vars[w] = BooleanVar()
1293        for k in self.app.opt.sound_samples:
1294            self.tkopt.sound_sample_vars[k] = BooleanVar()
1295        for k in self.app.opt.colors:
1296            self.tkopt.color_vars[k] = StringVar()
1297
1298    def _setOptions(self):
1299        tkopt, opt = self.tkopt, self.app.opt
1300        # set state of the menu items
1301        tkopt.autofaceup.set(opt.autofaceup)
1302        tkopt.autodrop.set(opt.autodrop)
1303        tkopt.autodeal.set(opt.autodeal)
1304        tkopt.quickplay.set(opt.quickplay)
1305        tkopt.undo.set(opt.undo)
1306        tkopt.hint.set(opt.hint)
1307        tkopt.shuffle.set(opt.shuffle)
1308        tkopt.bookmarks.set(opt.bookmarks)
1309        tkopt.highlight_piles.set(opt.highlight_piles)
1310        tkopt.highlight_cards.set(opt.highlight_cards)
1311        tkopt.highlight_samerank.set(opt.highlight_samerank)
1312        tkopt.highlight_not_matching.set(opt.highlight_not_matching)
1313        tkopt.shrink_face_down.set(opt.shrink_face_down)
1314        tkopt.shade_filled_stacks.set(opt.shade_filled_stacks)
1315        tkopt.mahjongg_show_removed.set(opt.mahjongg_show_removed)
1316        tkopt.shisen_show_hint.set(opt.shisen_show_hint)
1317        tkopt.accordion_deal_all.set(opt.accordion_deal_all)
1318        tkopt.sound.set(opt.sound)
1319        tkopt.sound_sample_volume.set(opt.sound_sample_volume)
1320        tkopt.sound_music_volume.set(opt.sound_music_volume)
1321        tkopt.cardback.set(self.app.cardset.backindex)
1322        tkopt.tabletile.set(self.app.tabletile_index)
1323        tkopt.animations.set(opt.animations)
1324        tkopt.redeal_animation.set(opt.redeal_animation)
1325        tkopt.win_animation.set(opt.win_animation)
1326        tkopt.shadow.set(opt.shadow)
1327        tkopt.shade.set(opt.shade)
1328        tkopt.toolbar.set(opt.toolbar)
1329        tkopt.toolbar_style.set(opt.toolbar_style)
1330        tkopt.toolbar_relief.set(opt.toolbar_relief)
1331        tkopt.toolbar_compound.set(opt.toolbar_compound)
1332        tkopt.toolbar_size.set(opt.toolbar_size)
1333        tkopt.toolbar_relief.set(opt.toolbar_relief)
1334        tkopt.statusbar.set(opt.statusbar)
1335        tkopt.num_cards.set(opt.num_cards)
1336        tkopt.helpbar.set(opt.helpbar)
1337        tkopt.save_games_geometry.set(opt.save_games_geometry)
1338        tkopt.demo_logo.set(opt.demo_logo)
1339        tkopt.splashscreen.set(opt.splashscreen)
1340        tkopt.mouse_type.set(opt.mouse_type)
1341        tkopt.mouse_undo.set(opt.mouse_undo)
1342        tkopt.negative_bottom.set(opt.negative_bottom)
1343        tkopt.display_win_message.set(opt.display_win_message)
1344        tkopt.cardset.set(self.app.cardset_manager.getSelected())
1345        tkopt.language.set(opt.language)
1346
1347        for w in TOOLBAR_BUTTONS:
1348            tkopt.toolbar_vars[w].set(opt.toolbar_vars.get(w, False))
1349        for k in self.app.opt.sound_samples:
1350            self.tkopt.sound_sample_vars[k].set(
1351                opt.sound_samples.get(k, False))
1352        for k in self.app.opt.colors:
1353            self.tkopt.color_vars[k].set(opt.colors.get(k, '#000000'))
1354
1355    def connectGame(self, game):
1356        self.game = game
1357        if game is None:
1358            return
1359        assert self.app is game.app
1360        tkopt = self.tkopt
1361        # opt = self.app.opt
1362        tkopt.gameid.set(game.id)
1363        tkopt.gameid_popular.set(game.id)
1364        tkopt.comment.set(bool(game.gsaveinfo.comment))
1365        tkopt.pause.set(self.game.pause)
1366        if game.canFindCard():
1367            connect_game_find_card_dialog(game)
1368        else:
1369            destroy_find_card_dialog()
1370        connect_game_solver_dialog(game)
1371
1372    # create a GTK-like path
1373    def _addPath(self, path, menu, index, submenu):
1374        # print ('MfxMenubar: _addPath %s, %s' % (path, menu))
1375        # y = self.yy
1376        if path not in self.__menupath:
1377            # print path, menu, index, submenu
1378            self.__menupath[path] = (menu, index, submenu)
1379
1380    def _getEnabledState(self, enabled):
1381        print('_getEnabledState: %s' % enabled)
1382        if enabled:
1383            return "normal"
1384        return "disabled"
1385
1386    def updateProgress(self):
1387        if self.progress:
1388            self.progress.update(step=1)
1389
1390    #
1391    # create the menubar
1392    #
1393
1394    def _createMenubar(self):
1395        MfxMenubar.addPath = self._addPath
1396        kw = {"name": "menubar"}
1397        self.__menubar = MfxMenubar(self.top, **kw)
1398
1399        # init keybindings
1400        bind(self.top, "<KeyPress>", self._keyPressHandler)
1401
1402        # LMainMenuDialog()
1403        LMenuItem(self.__menubar.menu,
1404                  text=_("Menu"), command=self.mMainMenuDialog)
1405
1406        MfxMenubar.addPath = None
1407
1408    #
1409    # key binding utility
1410    #
1411
1412    def _bindKey(self, modifier, key, func):
1413        #         if 0 and not modifier and len(key) == 1:
1414        #             self.__keybindings[key.lower()] = func
1415        #             self.__keybindings[key.upper()] = func
1416        #             return
1417        if not modifier and len(key) == 1:
1418            # ignore Ctrl/Shift/Alt
1419            # but don't ignore NumLock (state == 16)
1420            def lfunc(e, func=func):
1421                return e.state in (0, 16) and func(e)
1422            func = lfunc
1423            # func = lambda e, func=func: e.state in (0, 16) and func(e)
1424        sequence = "<" + modifier + "KeyPress-" + key + ">"
1425        bind(self.top, sequence, func)
1426        if len(key) == 1 and key != key.upper():
1427            key = key.upper()
1428            sequence = "<" + modifier + "KeyPress-" + key + ">"
1429            bind(self.top, sequence, func)
1430
1431    def _keyPressHandler(self, event):
1432        r = EVENT_PROPAGATE
1433        if event and self.game:
1434            # print event.__dict__
1435            if self.game.demo:
1436                # stop the demo by setting self.game.demo.keypress
1437                if event.char:    # ignore Ctrl/Shift/etc.
1438                    self.game.demo.keypress = event.char
1439                    r = EVENT_HANDLED
1440#             func = self.__keybindings.get(event.char)
1441#             if func and (event.state & ~2) == 0:
1442#                 func(event)
1443#                 r = EVENT_HANDLED
1444        return r
1445
1446    #
1447    # Select Game menu creation
1448    #
1449    '''
1450    def _addSelectGameMenu(self, menu):
1451        games = map(self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())
1452        m = "Ctrl-"
1453        if sys.platform == "darwin":
1454            m = "Cmd-"
1455        menu.add_command(label=n_("All &games..."), accelerator=m + "W",
1456                         command=self.mSelectGameDialog)
1457
1458    def _addSelectGameSubMenu(self, games, menu, select_data,
1459                              command, variable):
1460        # print select_data
1461        need_sep = 0
1462        for label, select_func in select_data:
1463            if label is None:
1464                need_sep = 1
1465                continue
1466            g = filter(select_func, games)
1467            if not g:
1468                continue
1469            if need_sep:
1470                menu.add_separator()
1471                need_sep = 0
1472            submenu = MfxMenu(menu, label=label)
1473            self._addSelectGameSubSubMenu(g, submenu, command, variable)
1474
1475    def _getNumGames(self, games, select_data):
1476        ngames = 0
1477        for label, select_func in select_data:
1478            ngames += len(filter(select_func, games))
1479        return ngames
1480
1481    def _addSelectMahjonggGameSubMenu(self, games, menu, command, variable):
1482        def select_func(gi): return gi.si.game_type == GI.GT_MAHJONGG
1483        mahjongg_games = filter(select_func, games)
1484        if len(mahjongg_games) == 0:
1485            return
1486        #
1487        menu = MfxMenu(menu, label=n_("&Mahjongg games"))
1488
1489        def add_menu(games, c0, c1, menu=menu,
1490                     variable=variable, command=command):
1491            if not games:
1492                return
1493            label = c0 + ' - ' + c1
1494            if c0 == c1:
1495                label = c0
1496            submenu = MfxMenu(menu, label=label, name=None)
1497            self._addSelectGameSubSubMenu(games, submenu, command,
1498                                          variable, short_name=True)
1499
1500        games = {}
1501        for gi in mahjongg_games:
1502            c = gi.short_name.strip()[0]
1503            if c in games:
1504                games[c].append(gi)
1505            else:
1506                games[c] = [gi]
1507        games = games.items()
1508        games.sort()
1509        g0 = []
1510        c0 = c1 = games[0][0]
1511        for c, g1 in games:
1512            if len(g0) + len(g1) >= self.__cb_max:
1513                add_menu(g0, c0, c1)
1514                g0 = g1
1515                c0 = c1 = c
1516            else:
1517                g0 += g1
1518                c1 = c
1519        add_menu(g0, c0, c1)
1520
1521    def _addSelectPopularGameSubMenu(self, games, menu, command, variable):
1522        def select_func(gi): return gi.si.game_flags & GI.GT_POPULAR
1523        if len(filter(select_func, games)) == 0:
1524            return
1525        data = (n_("&Popular games"), select_func)
1526        self._addSelectGameSubMenu(games, menu, (data, ),
1527                                   self.mSelectGamePopular,
1528                                   self.tkopt.gameid_popular)
1529
1530    def _addSelectFrenchGameSubMenu(self, games, menu, command, variable):
1531        if self._getNumGames(games, GI.SELECT_GAME_BY_TYPE) == 0:
1532            return
1533        submenu = MfxMenu(menu, label=n_("&French games"))
1534        self._addSelectGameSubMenu(games, submenu, GI.SELECT_GAME_BY_TYPE,
1535                                   self.mSelectGame, self.tkopt.gameid)
1536
1537    def _addSelectOrientalGameSubMenu(self, games, menu, command, variable):
1538        if self._getNumGames(games, GI.SELECT_ORIENTAL_GAME_BY_TYPE) == 0:
1539            return
1540        submenu = MfxMenu(menu, label=n_("&Oriental games"))
1541        self._addSelectGameSubMenu(games, submenu,
1542                                   GI.SELECT_ORIENTAL_GAME_BY_TYPE,
1543                                   self.mSelectGame, self.tkopt.gameid)
1544
1545    def _addSelectSpecialGameSubMenu(self, games, menu, command, variable):
1546        if self._getNumGames(games, GI.SELECT_ORIENTAL_GAME_BY_TYPE) == 0:
1547            return
1548        submenu = MfxMenu(menu, label=n_("&Special games"))
1549        self._addSelectGameSubMenu(games, submenu,
1550                                   GI.SELECT_SPECIAL_GAME_BY_TYPE,
1551                                   self.mSelectGame, self.tkopt.gameid)
1552
1553    def _addSelectCustomGameSubMenu(self, games, menu, command, variable):
1554        submenu = MfxMenu(menu, label=n_("&Custom games"))
1555
1556        def select_func(gi): return gi.si.game_type == GI.GT_CUSTOM
1557        games = filter(select_func, games)
1558        self.updateGamesMenu(submenu, games)
1559    '''
1560
1561    def _addSelectAllGameSubMenu(self, games, menu, command, variable):
1562        # LB
1563        # herausgenommen: zu aufwendig !
1564        return
1565        '''
1566        menu = MfxMenu(menu, label=n_("&All games by name"))
1567        n, d = 0, self.__cb_max
1568        i = 0
1569        while True:
1570            if self.progress:
1571                self.progress.update(step=1)
1572            columnbreak = i > 0 and (i % d) == 0
1573            i += 1
1574            if not games[n:n + d]:
1575                break
1576            m = min(n + d - 1, len(games) - 1)
1577            label = games[n].name[:3] + ' - ' + games[m].name[:3]
1578
1579            submenu = MfxMenu(menu, label=label, name=None)
1580            self._addSelectGameSubSubMenu(games[n:n + d], submenu,
1581                                          command, variable)
1582            n += d
1583            # if columnbreak:
1584            #    menu.entryconfigure(i, columnbreak=columnbreak)
1585        '''
1586
1587    # Eine 'closure' in Python? - voila!
1588    def make_gamesetter(self, n, variable, command):
1589        def gamesetter(x):
1590            variable.set(n)
1591            command()
1592        return gamesetter
1593
1594    def _addSelectGameSubSubMenu(self, games, menu, command, variable,
1595                                 short_name=False):
1596
1597        # cb = self.__cb_max
1598        for i in range(len(games)):
1599            gi = games[i]
1600            # columnbreak = i > 0 and (i % cb) == 0
1601            if short_name:
1602                label = gi.short_name
1603            else:
1604                label = gi.name
1605
1606            # optimized by inlining
1607
1608            # geht nicht mehr 'optimiert' mit kivy
1609            # die Funktionalität des tk.calls kann mit hilfe
1610            # einer 'closure' rekonstruiert werden (s.o).
1611            # LB
1612
1613            gsetter = self.make_gamesetter(gi.id, variable, command)
1614            menu.add_command(label=label, command=gsetter)
1615
1616            # menu.tk.call((menu._w, 'add', 'radiobutton') +
1617            #             menu._options({'command': command,
1618            #                            'variable': variable,
1619            #                            'columnbreak': columnbreak,
1620            #                            'value': gi.id,
1621            #                            'label': label}))
1622
1623    def updateGamesMenu(self, menu, games):
1624
1625        def cmp2(a, b):
1626            """python 3 replacement for python 2 cmp function"""
1627            return (a > b) - (a < b)
1628
1629        menu.delete(0, 'last')
1630
1631        if len(games) == 0:
1632            menu.add_radiobutton(label=_('<none>'), name=None,
1633                                 state='disabled')
1634        elif len(games) > self.__cb_max * 4:
1635            games.sort(lambda a, b: cmp2(a.name, b.name))
1636            self._addSelectAllGameSubMenu(games, menu,
1637                                          command=self.mSelectGame,
1638                                          variable=self.tkopt.gameid)
1639        else:
1640            self._addSelectGameSubSubMenu(games, menu,
1641                                          command=self.mSelectGame,
1642                                          variable=self.tkopt.gameid)
1643
1644    def mMainMenuDialog(self, *event):
1645        MainMenuDialog(self, self.top, title=_("Main Menu"), app=self.app)
1646        return EVENT_HANDLED
1647
1648    def mFileMenuDialog(self, *event):
1649        if self._cancelDrag(break_pause=False):
1650            return
1651        self.game.setCursor(cursor=CURSOR_WATCH)
1652        after_idle(self.top, self.__restoreCursor)
1653        FileMenuDialog(self, self.top, title=_("File Menu"), app=self.app)
1654        return EVENT_HANDLED
1655
1656    def mEditMenuDialog(self, *event):
1657        if self._cancelDrag(break_pause=False):
1658            return
1659        self.game.setCursor(cursor=CURSOR_WATCH)
1660        after_idle(self.top, self.__restoreCursor)
1661        EditMenuDialog(self, self.top, title=_("Tools"), app=self.app)
1662        return EVENT_HANDLED
1663
1664    def mGameMenuDialog(self, *event):
1665        if self._cancelDrag(break_pause=False):
1666            return
1667        self.game.setCursor(cursor=CURSOR_WATCH)
1668        after_idle(self.top, self.__restoreCursor)
1669        GameMenuDialog(self, self.top, title=_("Statistics"), app=self.app)
1670        return EVENT_HANDLED
1671
1672    def mAssistMenuDialog(self, *event):
1673        if self._cancelDrag(break_pause=False):
1674            return
1675        self.game.setCursor(cursor=CURSOR_WATCH)
1676        after_idle(self.top, self.__restoreCursor)
1677        AssistMenuDialog(self, self.top, title=_("Assists"), app=self.app)
1678        return EVENT_HANDLED
1679
1680    def mOptionsMenuDialog(self, *event):
1681        if self._cancelDrag(break_pause=False):
1682            return
1683        self.game.setCursor(cursor=CURSOR_WATCH)
1684        after_idle(self.top, self.__restoreCursor)
1685        OptionsMenuDialog(self, self.top, title=_("Options"), app=self.app)
1686        return EVENT_HANDLED
1687
1688    def mHelpMenuDialog(self, *event):
1689        if self._cancelDrag(break_pause=False):
1690            return
1691        self.game.setCursor(cursor=CURSOR_WATCH)
1692        after_idle(self.top, self.__restoreCursor)
1693        HelpMenuDialog(self, self.top, title=_("Help"), app=self.app)
1694        return EVENT_HANDLED
1695    #
1696    # Select Game menu actions
1697    #
1698
1699    def mSelectGame(self, *args):
1700        print('mSelectGame %s' % self)
1701        self._mSelectGame(self.tkopt.gameid.get())
1702
1703    def mSelectGamePopular(self, *args):
1704        self._mSelectGame(self.tkopt.gameid_popular.get())
1705
1706    def _mSelectGameDialog(self, d):
1707        if d.gameid != self.game.id:
1708            self.tkopt.gameid.set(d.gameid)
1709            self.tkopt.gameid_popular.set(d.gameid)
1710            self._cancelDrag()
1711            self.game.endGame()
1712            self.game.quitGame(d.gameid, random=d.random)
1713        return EVENT_HANDLED
1714
1715    def __restoreCursor(self, *event):
1716        self.game.setCursor(cursor=self.app.top_cursor)
1717
1718    def mSelectGameDialog(self, *event):
1719        if self._cancelDrag(break_pause=False):
1720            return
1721        self.game.setCursor(cursor=CURSOR_WATCH)
1722        after_idle(self.top, self.__restoreCursor)
1723        d = SelectGameDialog(self.top, title=_("Select game"),
1724                             app=self.app, gameid=self.game.id)
1725        return self._mSelectGameDialog(d)
1726
1727    #
1728    # menubar overrides
1729    #
1730
1731    def updateFavoriteGamesMenu(self):
1732        return
1733
1734        # TBD ?
1735        '''
1736        gameids = self.app.opt.favorite_gameid
1737
1738        print('favorite_gameids = %s' % gameids)
1739
1740        submenu = self.__menupath[".menubar.file.favoritegames"][2]
1741        games = []
1742        for id in gameids:
1743            gi = self.app.getGameInfo(id)
1744            if gi:
1745                games.append(gi)
1746        self.updateGamesMenu(submenu, games)
1747
1748        # das folgende ist nur das enable/disable des add/remove buttons.
1749        # geht mit kivy nicht so.
1750
1751#        state = self._getEnabledState
1752#        in_favor = self.app.game.id in gameids
1753
1754# menu, index, submenu = self.__menupath[".menubar.file.addtofavorites"]
1755# menu.entryconfig(index, state=state(not in_favor))
1756
1757# menu, index, submenu = self.__menupath[".menubar.file.removefromfavorites"]
1758# menu.entryconfig(index, state=state(in_favor))
1759        '''
1760
1761    def updateRecentGamesMenu(self, gameids):
1762        return
1763
1764        # TBD ?
1765        '''
1766        submenu = self.__menupath[".menubar.file.recentgames"][2]
1767        games = []
1768        for id in gameids:
1769            gi = self.app.getGameInfo(id)
1770            if gi:
1771                games.append(gi)
1772        self.updateGamesMenu(submenu, games)
1773        '''
1774
1775    def updateBookmarkMenuState(self):
1776        # LB:
1777        print('updateBookmarkMenuState - fake')
1778        return
1779
1780        state = self._getEnabledState
1781        mp1 = self.__menupath.get(".menubar.edit.setbookmark")
1782        mp2 = self.__menupath.get(".menubar.edit.gotobookmark")
1783        mp3 = self.__menupath.get(".menubar.edit.clearbookmarks")
1784        if mp1 is None or mp2 is None or mp3 is None:
1785            return
1786        x = self.app.opt.bookmarks and self.game.canSetBookmark()
1787        #
1788        menu, index, submenu = mp1
1789        for i in range(9):
1790            submenu.entryconfig(i, state=state(x))
1791        menu.entryconfig(index, state=state(x))
1792        #
1793        menu, index, submenu = mp2
1794        ms = 0
1795        for i in range(9):
1796            s = self.game.gsaveinfo.bookmarks.get(i) is not None
1797            submenu.entryconfig(i, state=state(s and x))
1798            ms = ms or s
1799        menu.entryconfig(index, state=state(ms and x))
1800        #
1801        menu, index, submenu = mp3
1802        menu.entryconfig(index, state=state(ms and x))
1803
1804    def updateBackgroundImagesMenu(self):
1805        # LB:
1806        print('updateBackgroundImagesMenu - fake')
1807        return
1808
1809        mp = self.__menupath.get(".menubar.options.cardbackground")
1810        # delete all entries
1811        submenu = mp[2]
1812        submenu.delete(0, "last")
1813        # insert new cardbacks
1814        mbacks = self.app.images.getCardbacks()
1815        cb = int(math.ceil(math.sqrt(len(mbacks))))
1816        for i in range(len(mbacks)):
1817            columnbreak = i > 0 and (i % cb) == 0
1818            submenu.add_radiobutton(
1819                    label=mbacks[i].name,
1820                    image=mbacks[i].menu_image,
1821                    variable=self.tkopt.cardback,
1822                    value=i,
1823                    command=self.mOptCardback,
1824                    columnbreak=columnbreak,
1825                    indicatoron=0,
1826                    hidemargin=0)
1827    #
1828    # menu updates
1829    #
1830
1831    def setMenuState(self, state, path):
1832        # LB: not used
1833        return
1834
1835    def setToolbarState(self, state, path):
1836        # LB: not used
1837        return
1838
1839    def _setCommentMenu(self, v):
1840        self.tkopt.comment.set(v)
1841
1842    def _setPauseMenu(self, v):
1843        self.tkopt.pause.set(v)
1844
1845    #
1846    # menu actions
1847    #
1848
1849    DEFAULTEXTENSION = ".pso"
1850    # TRANSLATORS: Usually, 'PySol files'
1851    FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION),
1852                 (_("All files"), "*"))
1853
1854    def mAddFavor(self, *event):
1855        gameid = self.app.game.id
1856        if gameid not in self.app.opt.favorite_gameid:
1857            self.app.opt.favorite_gameid.append(gameid)
1858            self.updateFavoriteGamesMenu()
1859
1860    def mDelFavor(self, *event):
1861        gameid = self.app.game.id
1862        if gameid in self.app.opt.favorite_gameid:
1863            self.app.opt.favorite_gameid.remove(gameid)
1864            self.updateFavoriteGamesMenu()
1865
1866    def mOpen(self, *event):
1867        if self._cancelDrag(break_pause=False):
1868            return
1869        # filename = self.game.filename
1870        filename = "lastgame.pso"
1871        if filename:
1872            idir, ifile = os.path.split(os.path.normpath(filename))
1873        else:
1874            idir, ifile = "", ""
1875        if not idir:
1876            idir = self.app.dn.savegames
1877#        d = tkFileDialog.Open()
1878#        filename = d.show(filetypes=self.FILETYPES,
1879#                          defaultextension=self.DEFAULTEXTENSION,
1880#                          initialdir=idir, initialfile=ifile)
1881        filename = idir + "/" + ifile
1882
1883        print('filename = %s' % filename)
1884        if filename:
1885            filename = os.path.normpath(filename)
1886            # filename = os.path.normcase(filename)
1887            if os.path.isfile(filename):
1888                self.game.loadGame(filename)
1889
1890    def mSaveAs(self, *event):
1891        if self._cancelDrag(break_pause=False):
1892            return
1893        if not self.menustate.save_as:
1894            return
1895        # filename = self.game.filename
1896        filename = "lastgame.pso"
1897        if not filename:
1898            filename = self.app.getGameSaveName(self.game.id)
1899            if os.name == "posix":
1900                filename = filename + "-" + self.game.getGameNumber(format=0)
1901            elif os.path.supports_unicode_filenames:  # new in python 2.3
1902                filename = filename + "-" + self.game.getGameNumber(format=0)
1903            else:
1904                filename = filename + "-01"
1905            filename = filename + self.DEFAULTEXTENSION
1906        idir, ifile = os.path.split(os.path.normpath(filename))
1907        if not idir:
1908            idir = self.app.dn.savegames
1909        # print self.game.filename, ifile
1910        # d = tkFileDialog.SaveAs()
1911        # filename = d.show(filetypes=self.FILETYPES,
1912        #                  defaultextension=self.DEFAULTEXTENSION,
1913        #                  initialdir=idir, initialfile=ifile)
1914        filename = idir + "/" + ifile
1915        if filename:
1916            filename = os.path.normpath(filename)
1917            # filename = os.path.normcase(filename)
1918            self.game.saveGame(filename)
1919            self.updateMenus()
1920
1921    def mPause(self, *args):
1922        if not self.game:
1923            return
1924        if not self.game.pause:
1925            if self._cancelDrag():
1926                return
1927        self.game.doPause()
1928        self.tkopt.pause.set(self.game.pause)
1929
1930    def mOptLanguage(self, *args):
1931        if self._cancelDrag(break_pause=False):
1932            return
1933        self.app.opt.language = self.tkopt.language.get()
1934        MfxMessageDialog(
1935           self.app.top, title=_("Note"),
1936           text=_("""\
1937These settings will take effect
1938the next time you restart the %(app)s""") % {'app': TITLE})
1939
1940    def mOptSoundDialog(self, *args):
1941        if self._cancelDrag(break_pause=False):
1942            return
1943        self.app.opt.sound = self.tkopt.sound.get()
1944
1945    def mOptSoundSampleVol(self, *args):
1946        if self._cancelDrag(break_pause=False):
1947            return
1948        self.app.opt.sound_sample_volume = self.tkopt.sound_sample_volume.get()
1949
1950    def mOptSoundMusicVol(self, *args):
1951        if self._cancelDrag(break_pause=False):
1952            return
1953        self.app.opt.sound_music_volume = self.tkopt.sound_music_volume.get()
1954
1955    def mOptSoundSample(self, key, *args):
1956        if self._cancelDrag(break_pause=False):
1957            return
1958        self.app.opt.sound_samples[key] = \
1959            self.tkopt.sound_sample_vars[key].get()
1960
1961    def mOptTableColor(self, *args):
1962        if self._cancelDrag(break_pause=False):
1963            return
1964        nv = self.tkopt.color_vars['table'].get()
1965        ov = self.app.opt.colors['table']
1966        self.app.opt.colors['table'] = nv
1967        if ov != nv:
1968            self.app.top_bg = nv
1969            self.app.tabletile_index = 0
1970            self.app.setTile(0, force=True)
1971            self.tkopt.tabletile.set(0)
1972
1973    def mOptTileSet(self, *args):
1974        if self._cancelDrag(break_pause=False):
1975            return
1976        idx = self.tkopt.tabletile.get()
1977        if idx > 0 and idx != self.app.tabletile_index:
1978            self.app.setTile(idx)
1979            self.tkopt.color_vars['table'].set('#008285')
1980
1981    def mOptAutoFaceUp(self, *args):
1982        if self._cancelDrag():
1983            return
1984        self.app.opt.autofaceup = self.tkopt.autofaceup.get()
1985        if self.app.opt.autofaceup:
1986            self.game.autoPlay()
1987
1988    def mOptAutoDrop(self, *args):
1989        if self._cancelDrag():
1990            return
1991        self.app.opt.autodrop = self.tkopt.autodrop.get()
1992        if self.app.opt.autodrop:
1993            self.game.autoPlay()
1994
1995    def mOptAutoDeal(self, *args):
1996        if self._cancelDrag():
1997            return
1998        self.app.opt.autodeal = self.tkopt.autodeal.get()
1999        if self.app.opt.autodeal:
2000            self.game.autoPlay()
2001
2002    def mOptQuickPlay(self, *args):
2003        if self._cancelDrag(break_pause=False):
2004            return
2005        self.app.opt.quickplay = self.tkopt.quickplay.get()
2006
2007    def mOptEnableUndo(self, *args):
2008        if self._cancelDrag(break_pause=False):
2009            return
2010        self.app.opt.undo = self.tkopt.undo.get()
2011        self.game.updateMenus()
2012
2013    def mOptEnableBookmarks(self, *args):
2014        if self._cancelDrag(break_pause=False):
2015            return
2016        self.app.opt.bookmarks = self.tkopt.bookmarks.get()
2017        self.game.updateMenus()
2018
2019    def mOptEnableHint(self, *args):
2020        if self._cancelDrag(break_pause=False):
2021            return
2022        self.app.opt.hint = self.tkopt.hint.get()
2023        self.game.updateMenus()
2024
2025    def mOptEnableShuffle(self, *args):
2026        if self._cancelDrag(break_pause=False):
2027            return
2028        self.app.opt.shuffle = self.tkopt.shuffle.get()
2029        self.game.updateMenus()
2030
2031    def mOptEnableHighlightPiles(self, *args):
2032        if self._cancelDrag(break_pause=False):
2033            return
2034        self.app.opt.highlight_piles = self.tkopt.highlight_piles.get()
2035        self.game.updateMenus()
2036
2037    def mOptEnableHighlightCards(self, *args):
2038        if self._cancelDrag(break_pause=False):
2039            return
2040        self.app.opt.highlight_cards = self.tkopt.highlight_cards.get()
2041        self.game.updateMenus()
2042
2043    def mOptEnableHighlightSameRank(self, *args):
2044        if self._cancelDrag(break_pause=False):
2045            return
2046        self.app.opt.highlight_samerank = self.tkopt.highlight_samerank.get()
2047        # self.game.updateMenus()
2048
2049    def mOptEnableHighlightNotMatching(self, *args):
2050        if self._cancelDrag(break_pause=False):
2051            return
2052        self.app.opt.highlight_not_matching = \
2053            self.tkopt.highlight_not_matching.get()
2054        # self.game.updateMenus()
2055
2056    def mOptAnimations(self, *args):
2057        if self._cancelDrag(break_pause=False):
2058            return
2059        self.app.opt.animations = self.tkopt.animations.get()
2060
2061    def mRedealAnimation(self, *args):
2062        if self._cancelDrag(break_pause=False):
2063            return
2064        self.app.opt.redeal_animation = self.tkopt.redeal_animation.get()
2065
2066    def mWinAnimation(self, *args):
2067        if self._cancelDrag(break_pause=False):
2068            return
2069        self.app.opt.win_animation = self.tkopt.win_animation.get()
2070
2071    def mWinDialog(self, *args):
2072        if self._cancelDrag(break_pause=False):
2073            return
2074        self.app.opt.display_win_message = self.tkopt.display_win_message.get()
2075
2076    def mOptShadow(self, *args):
2077        if self._cancelDrag(break_pause=False):
2078            return
2079        self.app.opt.shadow = self.tkopt.shadow.get()
2080
2081    def mOptShade(self, *args):
2082        if self._cancelDrag(break_pause=False):
2083            return
2084        self.app.opt.shade = self.tkopt.shade.get()
2085
2086    def mOptShrinkFaceDown(self, *args):
2087        if self._cancelDrag(break_pause=False):
2088            return
2089        self.app.opt.shrink_face_down = self.tkopt.shrink_face_down.get()
2090        self.game.endGame(bookmark=1)
2091        self.game.quitGame(bookmark=1)
2092
2093    def mOptShadeFilledStacks(self, *args):
2094        if self._cancelDrag(break_pause=False):
2095            return
2096        self.app.opt.shade_filled_stacks = self.tkopt.shade_filled_stacks.get()
2097        self.game.endGame(bookmark=1)
2098        self.game.quitGame(bookmark=1)
2099
2100    def mOptMahjonggShowRemoved(self, *args):
2101        if self._cancelDrag():
2102            return
2103        self.app.opt.mahjongg_show_removed = \
2104            self.tkopt.mahjongg_show_removed.get()
2105        # self.game.updateMenus()
2106        self.game.endGame(bookmark=1)
2107        self.game.quitGame(bookmark=1)
2108
2109    def mOptShisenShowHint(self, *args):
2110        if self._cancelDrag(break_pause=False):
2111            return
2112        self.app.opt.shisen_show_hint = self.tkopt.shisen_show_hint.get()
2113        # self.game.updateMenus()
2114
2115    def mOptAccordionDealAll(self, *args):
2116        if self._cancelDrag(break_pause=False):
2117            return
2118        self.app.opt.accordion_deal_all = self.tkopt.accordion_deal_all.get()
2119        # self.game.updateMenus()
2120
2121    def mOptCardset(self, *event):
2122        if self._cancelDrag(break_pause=False):
2123            return
2124        idx = self.tkopt.cardset.get()
2125        cs = self.app.cardset_manager.get(idx)
2126        if cs is None or idx == self.app.cardset.index:
2127            return
2128        if idx >= 0:
2129            self.app.nextgame.cardset = cs
2130            self._cancelDrag()
2131            self.game.endGame(bookmark=1)
2132            self.game.quitGame(bookmark=1)
2133
2134    def mSelectCardsetDialog(self, *event):
2135        if self._cancelDrag(break_pause=False):
2136            return
2137        # strings, default = ("&OK", "&Load", "&Cancel"), 0
2138        strings, default = (None, _("&Load"), _("&Cancel"), ), 1
2139        # if os.name == "posix":
2140        strings, default = (None, _("&Load"), _(
2141            "&Cancel"), _("&Info..."), ), 1
2142        key = self.app.nextgame.cardset.index
2143        d = SelectCardsetDialogWithPreview(
2144                self.top, title=_("Select cardset"),
2145                app=self.app, manager=self.app.cardset_manager, key=key,
2146                strings=strings, default=default)
2147
2148        cs = self.app.cardset_manager.get(d.key)
2149        if cs is None or d.key == self.app.cardset.index:
2150            return
2151        if d.status == 0 and d.button in (0, 1) and d.key >= 0:
2152            self.app.nextgame.cardset = cs
2153            if d.button == 1:
2154                self._cancelDrag()
2155                self.game.endGame(bookmark=1)
2156                self.game.quitGame(bookmark=1)
2157
2158    def mOptSetCardback(self, key, *event):
2159        val = self.tkopt.cardbacks[key].get()
2160        cs = self.app.cardset_manager.get(key)
2161        cs.updateCardback(backindex=val)
2162        # ANM: wir können den Background nur für das aktuell
2163        # selektierte Cardset wirklich ändern. Nur dieses wird
2164        # wird in den Optionen gespeichert.
2165        if (cs == self.app.cardset):
2166            self.app.updateCardset(self.game.id)
2167            self.app.cardset.backindex = val
2168            image = self.app.images.getBack(update=True)
2169            for card in self.game.cards:
2170                card.updateCardBackground(image=image)
2171            self.app.canvas.update_idletasks()
2172
2173    def _mOptCardback(self, index):
2174        if self._cancelDrag(break_pause=False):
2175            return
2176        cs = self.app.cardset
2177        old_index = cs.backindex
2178        cs.updateCardback(backindex=index)
2179        if cs.backindex == old_index:
2180            return
2181        self.app.updateCardset(self.game.id)
2182        image = self.app.images.getBack(update=True)
2183        for card in self.game.cards:
2184            card.updateCardBackground(image=image)
2185        self.app.canvas.update_idletasks()
2186        self.tkopt.cardback.set(cs.backindex)
2187
2188    def mOptCardback(self, *event):
2189        self._mOptCardback(self.tkopt.cardback.get())
2190
2191    def mOptChangeCardback(self, *event):
2192        self._mOptCardback(self.app.cardset.backindex + 1)
2193
2194    def mOptToolbar(self, *event):
2195        # if self._cancelDrag(break_pause=False): return
2196        self.setToolbarSide(self.tkopt.toolbar.get())
2197
2198    def mOptToolbarStyle(self, *event):
2199        # if self._cancelDrag(break_pause=False): return
2200        self.setToolbarStyle(self.tkopt.toolbar_style.get())
2201
2202    def mOptToolbarCompound(self, *event):
2203        # if self._cancelDrag(break_pause=False): return
2204        self.setToolbarCompound(self.tkopt.toolbar_compound.get())
2205
2206    def mOptToolbarSize(self, *event):
2207        # if self._cancelDrag(break_pause=False): return
2208        self.setToolbarSize(self.tkopt.toolbar_size.get())
2209
2210    def mOptToolbarRelief(self, *event):
2211        # if self._cancelDrag(break_pause=False): return
2212        self.setToolbarRelief(self.tkopt.toolbar_relief.get())
2213
2214    def mOptToolbarConfig(self, w):
2215        self.toolbarConfig(w, self.tkopt.toolbar_vars[w].get())
2216
2217    def mOptStatusbar(self, *event):
2218        if self._cancelDrag(break_pause=False):
2219            return
2220        if not self.app.statusbar:
2221            return
2222        side = self.tkopt.statusbar.get()
2223        self.app.opt.statusbar = side
2224        resize = not self.app.opt.save_games_geometry
2225        if self.app.statusbar.show(side, resize=resize):
2226            self.top.update_idletasks()
2227
2228    def mOptNumCards(self, *event):
2229        if self._cancelDrag(break_pause=False):
2230            return
2231        self.app.opt.num_cards = self.tkopt.num_cards.get()
2232
2233    def mOptHelpbar(self, *event):
2234        if self._cancelDrag(break_pause=False):
2235            return
2236        if not self.app.helpbar:
2237            return
2238        show = self.tkopt.helpbar.get()
2239        self.app.opt.helpbar = show
2240        resize = not self.app.opt.save_games_geometry
2241        if self.app.helpbar.show(show, resize=resize):
2242            self.top.update_idletasks()
2243
2244    def mOptSaveGamesGeometry(self, *event):
2245        if self._cancelDrag(break_pause=False):
2246            return
2247        self.app.opt.save_games_geometry = self.tkopt.save_games_geometry.get()
2248
2249    def mOptDemoLogo(self, *event):
2250        if self._cancelDrag(break_pause=False):
2251            return
2252        self.app.opt.demo_logo = self.tkopt.demo_logo.get()
2253
2254    def mOptSplashscreen(self, *event):
2255        if self._cancelDrag(break_pause=False):
2256            return
2257        self.app.opt.splashscreen = self.tkopt.splashscreen.get()
2258
2259    def mOptMouseType(self, *event):
2260        if self._cancelDrag(break_pause=False):
2261            return
2262        self.app.opt.mouse_type = self.tkopt.mouse_type.get()
2263
2264    def mOptMouseUndo(self, *event):
2265        if self._cancelDrag(break_pause=False):
2266            return
2267        self.app.opt.mouse_undo = self.tkopt.mouse_undo.get()
2268
2269    def mOptNegativeBottom(self, *event):
2270        if self._cancelDrag():
2271            return
2272        self.app.opt.negative_bottom = self.tkopt.negative_bottom.get()
2273        self.app.updateCardset()
2274        self.game.endGame(bookmark=1)
2275        self.game.quitGame(bookmark=1)
2276
2277    #
2278    # toolbar support
2279    #
2280
2281    def setToolbarSide(self, side):
2282        if self._cancelDrag(break_pause=False):
2283            return
2284        self.app.opt.toolbar = side
2285        self.tkopt.toolbar.set(side)                    # update radiobutton
2286        resize = not self.app.opt.save_games_geometry
2287        if self.app.toolbar.show(side, resize=resize):
2288            self.top.update_idletasks()
2289
2290    def setToolbarSize(self, size):
2291        if self._cancelDrag(break_pause=False):
2292            return
2293        self.app.opt.toolbar_size = size
2294        self.tkopt.toolbar_size.set(size)                # update radiobutton
2295        dir = self.app.getToolbarImagesDir()
2296        if self.app.toolbar.updateImages(dir, size):
2297            self.game.updateStatus(player=self.app.opt.player)
2298            self.top.update_idletasks()
2299
2300    def setToolbarStyle(self, style):
2301        if self._cancelDrag(break_pause=False):
2302            return
2303        self.app.opt.toolbar_style = style
2304        # update radiobutton
2305        self.tkopt.toolbar_style.set(style)
2306        dir = self.app.getToolbarImagesDir()
2307        size = self.app.opt.toolbar_size
2308        if self.app.toolbar.updateImages(dir, size):
2309            # self.game.updateStatus(player=self.app.opt.player)
2310            self.top.update_idletasks()
2311
2312    def setToolbarCompound(self, compound):
2313        if self._cancelDrag(break_pause=False):
2314            return
2315        self.app.opt.toolbar_compound = compound
2316        self.tkopt.toolbar_compound.set(
2317            compound)          # update radiobutton
2318        if self.app.toolbar.setCompound(compound):
2319            self.game.updateStatus(player=self.app.opt.player)
2320            self.top.update_idletasks()
2321
2322    def setToolbarRelief(self, relief):
2323        if self._cancelDrag(break_pause=False):
2324            return
2325        self.app.opt.toolbar_relief = relief
2326        self.tkopt.toolbar_relief.set(relief)           # update radiobutton
2327        self.app.toolbar.setRelief(relief)
2328        self.top.update_idletasks()
2329
2330    def toolbarConfig(self, w, v):
2331        if self._cancelDrag(break_pause=False):
2332            return
2333        self.app.opt.toolbar_vars[w] = v
2334        self.app.toolbar.config(w, v)
2335        self.top.update_idletasks()
2336
2337    #
2338    # stacks descriptions
2339    #
2340
2341    def mStackDesk(self, *event):
2342        if self.game.stackdesc_list:
2343            self.game.deleteStackDesc()
2344        else:
2345            if self._cancelDrag(break_pause=True):
2346                return
2347            self.game.showStackDesc()
2348
2349    def wizardDialog(self, edit=False):
2350        from pysollib.wizardutil import write_game, reset_wizard
2351        from wizarddialog import WizardDialog
2352
2353        if edit:
2354            reset_wizard(self.game)
2355        else:
2356            reset_wizard(None)
2357        d = WizardDialog(self.top, _('Solitaire Wizard'), self.app)
2358        if d.status == 0 and d.button == 0:
2359            try:
2360                if edit:
2361                    gameid = write_game(self.app, game=self.game)
2362                else:
2363                    gameid = write_game(self.app)
2364            except Exception:
2365                return
2366            if SELECT_GAME_MENU:
2367                menu = self.__menupath[".menubar.select.customgames"][2]
2368
2369                def select_func(gi): return gi.si.game_type == GI.GT_CUSTOM
2370                games = map(self.app.gdb.get,
2371                            self.app.gdb.getGamesIdSortedByName())
2372                games = filter(select_func, games)
2373                self.updateGamesMenu(menu, games)
2374
2375            self.tkopt.gameid.set(gameid)
2376            self._mSelectGame(gameid, force=True)
2377
2378    def mWizard(self, *event):
2379        if self._cancelDrag(break_pause=False):
2380            return
2381        self.wizardDialog()
2382
2383    def mWizardEdit(self, *event):
2384        if self._cancelDrag(break_pause=False):
2385            return
2386        self.wizardDialog(edit=True)
2387
2388
2389'''
2390'''
2391