1import math 2import os 3import re 4import sys 5 6from pysollib.gamedb import GI 7from pysollib.hint import PySolHintLayoutImportError 8from pysollib.mfxutil import Image, USE_PIL 9from pysollib.mfxutil import Struct, kwdefault 10from pysollib.mygettext import _, n_ 11from pysollib.settings import SELECT_GAME_MENU 12from pysollib.settings import TITLE, WIN_SYSTEM 13from pysollib.settings import USE_FREECELL_SOLVER 14from pysollib.ui.tktile.tkconst import COMPOUNDS, CURSOR_WATCH, EVENT_HANDLED 15from pysollib.ui.tktile.tkconst import EVENT_PROPAGATE 16from pysollib.ui.tktile.tkconst import TOOLBAR_BUTTONS 17from pysollib.ui.tktile.tkutil import after_idle, bind 18 19from six.moves import tkinter 20from six.moves import tkinter_tkfiledialog 21 22 23def createToolbarMenu(menubar, menu): 24 tearoff = menu.cget('tearoff') 25# data_dir = os.path.join(menubar.app.dataloader.dir, 'images', 'toolbar') 26# submenu = MfxMenu(menu, label=n_('Style'), tearoff=tearoff) 27# for f in os.listdir(data_dir): 28# d = os.path.join(data_dir, f) 29# if os.path.isdir(d) and os.path.exists(os.path.join(d, 'small')): 30# name = f.replace('_', ' ').capitalize() 31# submenu.add_radiobutton( 32# label=name, 33# variable=menubar.tkopt.toolbar_style, 34# value=f, command=menubar.mOptToolbarStyle) 35 submenu = MfxMenu(menu, label=n_('Compound'), tearoff=tearoff) 36 for comp, label in COMPOUNDS: 37 submenu.add_radiobutton( 38 label=label, variable=menubar.tkopt.toolbar_compound, 39 value=comp, command=menubar.mOptToolbarCompound) 40 menu.add_separator() 41 menu.add_radiobutton(label=n_("Hide"), 42 variable=menubar.tkopt.toolbar, value=0, 43 command=menubar.mOptToolbar) 44 menu.add_radiobutton(label=n_("Top"), 45 variable=menubar.tkopt.toolbar, value=1, 46 command=menubar.mOptToolbar) 47 menu.add_radiobutton(label=n_("Bottom"), 48 variable=menubar.tkopt.toolbar, value=2, 49 command=menubar.mOptToolbar) 50 menu.add_radiobutton(label=n_("Left"), 51 variable=menubar.tkopt.toolbar, value=3, 52 command=menubar.mOptToolbar) 53 menu.add_radiobutton(label=n_("Right"), 54 variable=menubar.tkopt.toolbar, value=4, 55 command=menubar.mOptToolbar) 56 # menu.add_separator() 57 # menu.add_radiobutton(label=n_("Small icons"), 58 # variable=menubar.tkopt.toolbar_size, value=0, 59 # command=menubar.mOptToolbarSize) 60 # menu.add_radiobutton(label=n_("Large icons"), 61 # variable=menubar.tkopt.toolbar_size, value=1, 62 # command=menubar.mOptToolbarSize) 63 menu.add_separator() 64 submenu = MfxMenu(menu, label=n_('Visible buttons'), tearoff=tearoff) 65 for w in TOOLBAR_BUTTONS: 66 submenu.add_checkbutton( 67 label=_(w.capitalize()), 68 variable=menubar.tkopt.toolbar_vars[w], 69 command=lambda m=menubar, w=w: m.mOptToolbarConfig(w)) 70 71 72# ************************************************************************ 73# * 74# ************************************************************************ 75 76class MfxMenubar(tkinter.Menu): 77 addPath = None 78 79 def __init__(self, master, **kw): 80 self.name = kw["name"] 81 tearoff = 0 82 self.n = kw["tearoff"] = int(kw.get("tearoff", tearoff)) 83 tkinter.Menu.__init__(self, master, **kw) 84 85 def labeltoname(self, label): 86 # print label, type(label) 87 name = re.sub(r"[^0-9a-zA-Z]", "", label).lower() 88 label = _(label) 89 underline = label.find('&') 90 if underline >= 0: 91 label = label.replace('&', '') 92 return name, label, underline 93 94 def add(self, itemType, cnf={}): 95 label = cnf.get("label") 96 if label: 97 name = cnf.get('name') 98 if name: 99 del cnf['name'] # TclError: unknown option "-name" 100 else: 101 name, label, underline = self.labeltoname(label) 102 cnf["underline"] = cnf.get("underline", underline) 103 cnf["label"] = label 104 if name and self.addPath: 105 path = str(self._w) + "." + name 106 self.addPath(path, self, self.n, cnf.get("menu")) 107 tkinter.Menu.add(self, itemType, cnf) 108 self.n = self.n + 1 109 110 111class MfxMenu(MfxMenubar): 112 def __init__(self, master, label, underline=None, **kw): 113 if 'name' in kw: 114 name, label_underline = kw['name'], -1 115 else: 116 name, label, label_underline = self.labeltoname(label) 117 kwdefault(kw, name=name) 118 MfxMenubar.__init__(self, master, **kw) 119 if underline is None: 120 underline = label_underline 121 if master: 122 master.add_cascade( 123 menu=self, name=name, label=label, underline=underline) 124 125 126class PysolMenubarTkCommon: 127 def __init__(self, app, top, progress=None): 128 self._createTkOpt() 129 self._setOptions() 130 # init columnbreak 131 self.cb_max = int(self.top.winfo_screenheight()//23) 132 # sh = self.top.winfo_screenheight() 133 # self.cb_max = 22 134 # if sh >= 600: self.cb_max = 27 135 # if sh >= 768: self.cb_max = 32 136 # if sh >= 1024: self.cb_max = 40 137 self.progress = progress 138 # create menus 139 self.menubar = None 140 self.menupath = {} 141 self.keybindings = {} 142 self._createMenubar() 143 self.top = top 144 145 if self.progress: 146 self.progress.update(step=1) 147 148 # set the menubar 149 self.updateBackgroundImagesMenu() 150 self.top.config(menu=self.menubar) 151 152 def _createTkOpt(self): 153 # structure to convert menu-options to Toolkit variables 154 self.tkopt = Struct( 155 gameid=tkinter.IntVar(), 156 gameid_popular=tkinter.IntVar(), 157 comment=tkinter.BooleanVar(), 158 autofaceup=tkinter.BooleanVar(), 159 autodrop=tkinter.BooleanVar(), 160 autodeal=tkinter.BooleanVar(), 161 quickplay=tkinter.BooleanVar(), 162 undo=tkinter.BooleanVar(), 163 bookmarks=tkinter.BooleanVar(), 164 hint=tkinter.BooleanVar(), 165 shuffle=tkinter.BooleanVar(), 166 highlight_piles=tkinter.BooleanVar(), 167 highlight_cards=tkinter.BooleanVar(), 168 highlight_samerank=tkinter.BooleanVar(), 169 highlight_not_matching=tkinter.BooleanVar(), 170 mahjongg_show_removed=tkinter.BooleanVar(), 171 shisen_show_hint=tkinter.BooleanVar(), 172 accordion_deal_all=tkinter.BooleanVar(), 173 sound=tkinter.BooleanVar(), 174 auto_scale=tkinter.BooleanVar(), 175 preserve_aspect_ratio=tkinter.BooleanVar(), 176 spread_stacks=tkinter.BooleanVar(), 177 center_layout=tkinter.BooleanVar(), 178 cardback=tkinter.IntVar(), 179 tabletile=tkinter.IntVar(), 180 animations=tkinter.IntVar(), 181 redeal_animation=tkinter.BooleanVar(), 182 win_animation=tkinter.BooleanVar(), 183 shadow=tkinter.BooleanVar(), 184 shade=tkinter.BooleanVar(), 185 shade_filled_stacks=tkinter.BooleanVar(), 186 shrink_face_down=tkinter.BooleanVar(), 187 toolbar=tkinter.IntVar(), 188 toolbar_style=tkinter.StringVar(), 189 toolbar_relief=tkinter.StringVar(), 190 toolbar_compound=tkinter.StringVar(), 191 toolbar_size=tkinter.IntVar(), 192 statusbar=tkinter.BooleanVar(), 193 num_cards=tkinter.BooleanVar(), 194 helpbar=tkinter.BooleanVar(), 195 save_games_geometry=tkinter.BooleanVar(), 196 splashscreen=tkinter.BooleanVar(), 197 demo_logo=tkinter.BooleanVar(), 198 mouse_type=tkinter.StringVar(), 199 mouse_undo=tkinter.BooleanVar(), 200 negative_bottom=tkinter.BooleanVar(), 201 pause=tkinter.BooleanVar(), 202 theme=tkinter.StringVar(), 203 toolbar_vars={}, 204 ) 205 for w in TOOLBAR_BUTTONS: 206 self.tkopt.toolbar_vars[w] = tkinter.BooleanVar() 207 208 def _setOptions(self): 209 tkopt, opt = self.tkopt, self.app.opt 210 # set state of the menu items 211 tkopt.autofaceup.set(opt.autofaceup) 212 tkopt.autodrop.set(opt.autodrop) 213 tkopt.autodeal.set(opt.autodeal) 214 tkopt.quickplay.set(opt.quickplay) 215 tkopt.undo.set(opt.undo) 216 tkopt.hint.set(opt.hint) 217 tkopt.shuffle.set(opt.shuffle) 218 tkopt.bookmarks.set(opt.bookmarks) 219 tkopt.highlight_piles.set(opt.highlight_piles) 220 tkopt.highlight_cards.set(opt.highlight_cards) 221 tkopt.highlight_samerank.set(opt.highlight_samerank) 222 tkopt.highlight_not_matching.set(opt.highlight_not_matching) 223 tkopt.shrink_face_down.set(opt.shrink_face_down) 224 tkopt.shade_filled_stacks.set(opt.shade_filled_stacks) 225 tkopt.mahjongg_show_removed.set(opt.mahjongg_show_removed) 226 tkopt.shisen_show_hint.set(opt.shisen_show_hint) 227 tkopt.accordion_deal_all.set(opt.accordion_deal_all) 228 tkopt.sound.set(opt.sound) 229 tkopt.auto_scale.set(opt.auto_scale) 230 tkopt.preserve_aspect_ratio.set(opt.preserve_aspect_ratio) 231 tkopt.spread_stacks.set(opt.spread_stacks) 232 tkopt.center_layout.set(opt.center_layout) 233 tkopt.cardback.set(self.app.cardset.backindex) 234 tkopt.tabletile.set(self.app.tabletile_index) 235 tkopt.animations.set(opt.animations) 236 tkopt.redeal_animation.set(opt.redeal_animation) 237 tkopt.win_animation.set(opt.win_animation) 238 tkopt.shadow.set(opt.shadow) 239 tkopt.shade.set(opt.shade) 240 tkopt.toolbar.set(opt.toolbar) 241 tkopt.toolbar_style.set(opt.toolbar_style) 242 tkopt.toolbar_relief.set(opt.toolbar_relief) 243 tkopt.toolbar_compound.set(opt.toolbar_compound) 244 tkopt.toolbar_size.set(opt.toolbar_size) 245 tkopt.toolbar_relief.set(opt.toolbar_relief) 246 tkopt.statusbar.set(opt.statusbar) 247 tkopt.num_cards.set(opt.num_cards) 248 tkopt.helpbar.set(opt.helpbar) 249 tkopt.save_games_geometry.set(opt.save_games_geometry) 250 tkopt.demo_logo.set(opt.demo_logo) 251 tkopt.splashscreen.set(opt.splashscreen) 252 tkopt.mouse_type.set(opt.mouse_type) 253 tkopt.mouse_undo.set(opt.mouse_undo) 254 tkopt.negative_bottom.set(opt.negative_bottom) 255 for w in TOOLBAR_BUTTONS: 256 tkopt.toolbar_vars[w].set(opt.toolbar_vars.get(w, False)) 257 258 def connectGame(self, game): 259 self.game = game 260 if game is None: 261 return 262 assert self.app is game.app 263 tkopt = self.tkopt 264 tkopt.gameid.set(game.id) 265 tkopt.gameid_popular.set(game.id) 266 tkopt.comment.set(bool(game.gsaveinfo.comment)) 267 tkopt.pause.set(self.game.pause) 268 if game.canFindCard(): 269 self._connect_game_find_card_dialog(game) 270 else: 271 self._destroy_find_card_dialog() 272 self._connect_game_solver_dialog(game) 273 274 # create a GTK-like path 275 def _addPath(self, path, menu, index, submenu): 276 if path not in self.menupath: 277 # print path, menu, index, submenu 278 self.menupath[path] = (menu, index, submenu) 279 280 def _getEnabledState(self, enabled): 281 if enabled: 282 return "normal" 283 return "disabled" 284 285 def updateProgress(self): 286 if self.progress: 287 self.progress.update(step=1) 288 289 def _createMenubar(self): 290 MfxMenubar.addPath = self._addPath 291 kw = {"name": "menubar"} 292 self.menubar = MfxMenubar(self.top, **kw) 293 294 # init keybindings 295 bind(self.top, "<KeyPress>", self._keyPressHandler) 296 297 m = "Ctrl-" 298 if sys.platform == "darwin": 299 m = "Cmd-" 300 301 if WIN_SYSTEM == "aqua": 302 applemenu = MfxMenu(self.menubar, "apple") 303 applemenu.add_command( 304 label=_("&About %s") % TITLE, command=self.mHelpAbout) 305 306 menu = MfxMenu(self.menubar, n_("&File")) 307 menu.add_command( 308 label=n_("&New game"), command=self.mNewGame, accelerator="N") 309 submenu = MfxMenu(menu, label=n_("R&ecent games")) 310 # menu.add_command(label=n_("Select &random game"), 311 # command=self.mSelectRandomGame, accelerator=m+"R") 312 submenu = MfxMenu(menu, label=n_("Select &random game")) 313 submenu.add_command( 314 label=n_("&All games"), command=lambda: 315 self.mSelectRandomGame('all'), accelerator=m+"R") 316 submenu.add_command( 317 label=n_("Games played and &won"), 318 command=lambda: self.mSelectRandomGame('won')) 319 submenu.add_command( 320 label=n_("Games played and ¬ won"), 321 command=lambda: self.mSelectRandomGame('not won')) 322 submenu.add_command( 323 label=n_("Games not &played"), 324 command=lambda: self.mSelectRandomGame('not played')) 325 menu.add_command( 326 label=n_("Select game by nu&mber..."), 327 command=self.mSelectGameById, accelerator=m+"M") 328 menu.add_separator() 329 submenu = MfxMenu(menu, label=n_("Fa&vorite games")) 330 menu.add_command(label=n_("A&dd to favorites"), command=self.mAddFavor) 331 menu.add_command( 332 label=n_("Remove &from favorites"), 333 command=self.mDelFavor) 334 menu.add_separator() 335 menu.add_command( 336 label=n_("&Open..."), 337 command=self.mOpen, accelerator=m+"O") 338 menu.add_command( 339 label=n_("&Save"), 340 command=self.mSave, accelerator=m+"S") 341 menu.add_command(label=n_("Save &as..."), command=self.mSaveAs) 342 menu.add_command( 343 label=n_("E&xport current layout..."), 344 command=self.mExportCurrentLayout) 345 menu.add_command( 346 label=n_("&Import starting layout..."), 347 command=self.mImportStartingLayout) 348 menu.add_separator() 349 menu.add_command( 350 label=n_("&Hold and quit"), 351 command=self.mHoldAndQuit, accelerator=m+"X") 352 if WIN_SYSTEM != "aqua": 353 menu.add_command( 354 label=n_("&Quit"), 355 command=self.mQuit, accelerator=m+"Q") 356 357 if self.progress: 358 self.progress.update(step=1) 359 360 menu = MfxMenu(self.menubar, label=n_("&Select")) 361 self._addSelectGameMenu(menu) 362 363 if self.progress: 364 self.progress.update(step=1) 365 366 menu = MfxMenu(self.menubar, label=n_("&Edit")) 367 menu.add_command( 368 label=n_("&Undo"), 369 command=self.mUndo, accelerator="Z") 370 menu.add_command( 371 label=n_("&Redo"), 372 command=self.mRedo, accelerator="R") 373 menu.add_command(label=n_("Redo &all"), command=self.mRedoAll) 374 375 menu.add_separator() 376 submenu = MfxMenu(menu, label=n_("&Set bookmark")) 377 for i in range(9): 378 label = _("Bookmark %d") % (i + 1) 379 submenu.add_command( 380 label=label, 381 command=lambda i=i: self.mSetBookmark(i)) 382 submenu = MfxMenu(menu, label=n_("Go&to bookmark")) 383 for i in range(9): 384 label = _("Bookmark %d") % (i + 1) 385 acc = m + "%d" % (i + 1) 386 submenu.add_command( 387 label=label, 388 command=lambda i=i: self.mGotoBookmark(i), accelerator=acc) 389 menu.add_command( 390 label=n_("&Clear bookmarks"), 391 command=self.mClearBookmarks) 392 menu.add_separator() 393 394 menu.add_command( 395 label=n_("Restart"), 396 command=self.mRestart, accelerator=m+"G") 397 398 menu.add_separator() 399 menu.add_command(label=n_("Solitaire &Wizard"), command=self.mWizard) 400 menu.add_command( 401 label=n_("&Edit current game"), 402 command=self.mWizardEdit) 403 menu.add_command( 404 label=n_("&Delete current game"), 405 command=self.mWizardDelete) 406 407 menu = MfxMenu(self.menubar, label=n_("&Game")) 408 menu.add_command( 409 label=n_("&Deal cards"), 410 command=self.mDeal, accelerator="D") 411 menu.add_command( 412 label=n_("&Auto drop"), 413 command=self.mDrop, accelerator="A") 414 menu.add_command( 415 label=n_("Shu&ffle tiles"), 416 command=self.mShuffle, accelerator="F") 417 menu.add_checkbutton( 418 label=n_("&Pause"), variable=self.tkopt.pause, 419 command=self.mPause, accelerator="P") 420 # menu.add_command( 421 # label=n_("&Pause"), command=self.mPause, accelerator="P") 422 menu.add_separator() 423 menu.add_command( 424 label=n_("S&tatus..."), 425 command=lambda: self.mPlayerStats(mode=100), accelerator=m+"Y") 426 menu.add_checkbutton( 427 label=n_("&Comments..."), variable=self.tkopt.comment, 428 command=self.mEditGameComment) 429 menu.add_separator() 430 menu.add_command( 431 label=n_("&Statistics..."), 432 command=self.mPlayerStats, accelerator=m+"T") 433 menu.add_command( 434 label=n_("Log..."), 435 command=lambda: self.mPlayerStats(mode=103)) 436 menu.add_separator() 437 menu.add_command( 438 label=n_("D&emo statistics"), 439 command=lambda: self.mPlayerStats(mode=1101)) 440 441 menu = MfxMenu(self.menubar, label=n_("&Assist")) 442 menu.add_command( 443 label=n_("&Hint"), 444 command=self.mHint, accelerator="H") 445 menu.add_command( 446 label=n_("Highlight p&iles"), 447 command=self.mHighlightPiles, accelerator="I") 448 menu.add_command( 449 label=n_("&Find card"), 450 command=self.mFindCard, accelerator="F3") 451 menu.add_separator() 452 menu.add_command( 453 label=n_("&Demo"), 454 command=self.mDemo, accelerator=m+"D") 455 menu.add_command( 456 label=n_("Demo (&all games)"), 457 command=self.mMixedDemo) 458 if USE_FREECELL_SOLVER: 459 menu.add_command(label=n_("&Solver"), command=self.mSolver) 460 else: 461 menu.add_command(label=n_("&Solver"), state='disabled') 462 menu.add_separator() 463 menu.add_command( 464 label=n_("&Piles description"), 465 command=self.mStackDesk, accelerator="F2") 466 467 if self.progress: 468 self.progress.update(step=1) 469 470 menu = MfxMenu(self.menubar, label=n_("&Options")) 471 menu.add_command( 472 label=n_("&Player options..."), 473 command=self.mOptPlayerOptions) 474 submenu = MfxMenu(menu, label=n_("&Automatic play")) 475 submenu.add_checkbutton( 476 label=n_("Auto &face up"), variable=self.tkopt.autofaceup, 477 command=self.mOptAutoFaceUp) 478 submenu.add_checkbutton( 479 label=n_("A&uto drop"), variable=self.tkopt.autodrop, 480 command=self.mOptAutoDrop) 481 submenu.add_checkbutton( 482 label=n_("Auto &deal"), variable=self.tkopt.autodeal, 483 command=self.mOptAutoDeal) 484 submenu.add_separator() 485 submenu.add_checkbutton( 486 label=n_("&Quick play"), variable=self.tkopt.quickplay, 487 command=self.mOptQuickPlay) 488 submenu = MfxMenu(menu, label=n_("Assist &level")) 489 submenu.add_checkbutton( 490 label=n_("Enable &undo"), variable=self.tkopt.undo, 491 command=self.mOptEnableUndo) 492 submenu.add_checkbutton( 493 label=n_("Enable &bookmarks"), variable=self.tkopt.bookmarks, 494 command=self.mOptEnableBookmarks) 495 submenu.add_checkbutton( 496 label=n_("Enable &hint"), variable=self.tkopt.hint, 497 command=self.mOptEnableHint) 498 submenu.add_checkbutton( 499 label=n_("Enable shu&ffle"), variable=self.tkopt.shuffle, 500 command=self.mOptEnableShuffle) 501 submenu.add_checkbutton( 502 label=n_("Enable highlight p&iles"), 503 variable=self.tkopt.highlight_piles, 504 command=self.mOptEnableHighlightPiles) 505 submenu.add_checkbutton( 506 label=n_("Enable highlight &cards"), 507 variable=self.tkopt.highlight_cards, 508 command=self.mOptEnableHighlightCards) 509 submenu.add_checkbutton( 510 label=n_("Enable highlight same &rank"), 511 variable=self.tkopt.highlight_samerank, 512 command=self.mOptEnableHighlightSameRank) 513 submenu.add_checkbutton( 514 label=n_("Highlight &no matching"), 515 variable=self.tkopt.highlight_not_matching, 516 command=self.mOptEnableHighlightNotMatching) 517 submenu.add_separator() 518 submenu.add_checkbutton( 519 label=n_("&Show removed tiles (in Mahjongg games)"), 520 variable=self.tkopt.mahjongg_show_removed, 521 command=self.mOptMahjonggShowRemoved) 522 submenu.add_checkbutton( 523 label=n_("Show hint &arrow (in Shisen-Sho games)"), 524 variable=self.tkopt.shisen_show_hint, 525 command=self.mOptShisenShowHint) 526 submenu.add_checkbutton( 527 label=n_("&Deal all cards (in Accordion type games)"), 528 variable=self.tkopt.accordion_deal_all, 529 command=self.mOptAccordionDealAll) 530 menu.add_separator() 531 label = n_("&Sound...") 532 menu.add_command( 533 label=label, command=self.mOptSoundDialog) 534 # cardsets 535 if USE_PIL: 536 submenu = MfxMenu(menu, label=n_("Card si&ze")) 537 submenu.add_command( 538 label=n_("&Increase the card size"), 539 command=self.mIncreaseCardset, accelerator=m+"+") 540 submenu.add_command( 541 label=n_("&Decrease the card size"), 542 command=self.mDecreaseCardset, accelerator=m+"-") 543 submenu.add_command( 544 label=n_("&Reset the card size"), 545 command=self.mResetCardset) 546 submenu.add_separator() 547 submenu.add_checkbutton( 548 label=n_("&Auto scaling"), variable=self.tkopt.auto_scale, 549 command=self.mOptAutoScale, accelerator=m+'0') 550 submenu.add_checkbutton( 551 label=n_("&Preserve aspect ratio"), 552 variable=self.tkopt.preserve_aspect_ratio, 553 command=self.mOptPreserveAspectRatio) 554 submenu = MfxMenu(menu, label=n_("Card la&yout")) 555 submenu.add_checkbutton( 556 label=n_("&Spread stacks"), variable=self.tkopt.spread_stacks, 557 command=self.mOptSpreadStacks) 558 submenu.add_checkbutton( 559 label=n_("&Center layout"), variable=self.tkopt.center_layout, 560 command=self.mOptCenterLayout) 561 # manager = self.app.cardset_manager 562 # n = manager.len() 563 menu.add_command( 564 label=n_("Cards&et..."), 565 command=self.mSelectCardsetDialog, accelerator=m+"E") 566 menu.add_command( 567 label=n_("Table t&ile..."), 568 command=self.mSelectTileDialog) 569 # this submenu will get set by updateBackgroundImagesMenu() 570 submenu = MfxMenu(menu, label=n_("Card &background")) 571 submenu = MfxMenu(menu, label=n_("Card &view")) 572 submenu.add_checkbutton( 573 label=n_("Card shado&w"), variable=self.tkopt.shadow, 574 command=self.mOptShadow) 575 submenu.add_checkbutton( 576 label=n_("Shade &legal moves"), variable=self.tkopt.shade, 577 command=self.mOptShade) 578 submenu.add_checkbutton( 579 label=n_("&Negative cards bottom"), 580 variable=self.tkopt.negative_bottom, 581 command=self.mOptNegativeBottom) 582 submenu.add_checkbutton( 583 label=n_("Shrink face-down cards"), 584 variable=self.tkopt.shrink_face_down, 585 command=self.mOptShrinkFaceDown) 586 submenu.add_checkbutton( 587 label=n_("Shade &filled stacks"), 588 variable=self.tkopt.shade_filled_stacks, 589 command=self.mOptShadeFilledStacks) 590 submenu = MfxMenu(menu, label=n_("A&nimations")) 591 submenu.add_radiobutton( 592 label=n_("&None"), variable=self.tkopt.animations, value=0, 593 command=self.mOptAnimations) 594 submenu.add_radiobutton( 595 label=n_("&Very fast"), variable=self.tkopt.animations, value=1, 596 command=self.mOptAnimations) 597 submenu.add_radiobutton( 598 label=n_("&Fast"), variable=self.tkopt.animations, value=2, 599 command=self.mOptAnimations) 600 submenu.add_radiobutton( 601 label=n_("&Medium"), variable=self.tkopt.animations, value=3, 602 command=self.mOptAnimations) 603 submenu.add_radiobutton( 604 label=n_("&Slow"), variable=self.tkopt.animations, value=4, 605 command=self.mOptAnimations) 606 submenu.add_radiobutton( 607 label=n_("V&ery slow"), variable=self.tkopt.animations, value=5, 608 command=self.mOptAnimations) 609 submenu.add_separator() 610 submenu.add_checkbutton( 611 label=n_("&Redeal animation"), 612 variable=self.tkopt.redeal_animation, 613 command=self.mRedealAnimation) 614 if Image: 615 submenu.add_checkbutton( 616 label=n_("&Winning animation"), 617 variable=self.tkopt.win_animation, 618 command=self.mWinAnimation) 619 submenu = MfxMenu(menu, label=n_("&Mouse")) 620 submenu.add_radiobutton( 621 label=n_("&Drag-and-Drop"), variable=self.tkopt.mouse_type, 622 value='drag-n-drop', 623 command=self.mOptMouseType) 624 submenu.add_radiobutton( 625 label=n_("&Point-and-Click"), variable=self.tkopt.mouse_type, 626 value='point-n-click', 627 command=self.mOptMouseType) 628 submenu.add_radiobutton( 629 label=n_("&Sticky mouse"), variable=self.tkopt.mouse_type, 630 value='sticky-mouse', 631 command=self.mOptMouseType) 632 submenu.add_separator() 633 submenu.add_checkbutton( 634 label=n_("Use mouse for undo/redo"), 635 variable=self.tkopt.mouse_undo, 636 command=self.mOptMouseUndo) 637 menu.add_separator() 638 menu.add_command(label=n_("&Fonts..."), command=self.mOptFonts) 639 menu.add_command(label=n_("&Colors..."), command=self.mOptColors) 640 menu.add_command(label=n_("Time&outs..."), command=self.mOptTimeouts) 641 menu.add_separator() 642 self.createThemesMenu(menu) 643 submenu = MfxMenu(menu, label=n_("&Toolbar")) 644 createToolbarMenu(self, submenu) 645 submenu = MfxMenu(menu, label=n_("Stat&usbar")) 646 submenu.add_checkbutton( 647 label=n_("Show &statusbar"), variable=self.tkopt.statusbar, 648 command=self.mOptStatusbar) 649 submenu.add_checkbutton( 650 label=n_("Show &number of cards"), variable=self.tkopt.num_cards, 651 command=self.mOptNumCards) 652 submenu.add_checkbutton( 653 label=n_("Show &help bar"), variable=self.tkopt.helpbar, 654 command=self.mOptHelpbar) 655 # if not USE_PIL: 656 menu.add_checkbutton( 657 label=n_("Save games &geometry"), 658 variable=self.tkopt.save_games_geometry, 659 command=self.mOptSaveGamesGeometry) 660 menu.add_checkbutton( 661 label=n_("&Demo logo"), variable=self.tkopt.demo_logo, 662 command=self.mOptDemoLogo) 663 menu.add_checkbutton( 664 label=n_("Startup splash sc&reen"), 665 variable=self.tkopt.splashscreen, 666 command=self.mOptSplashscreen) 667 # menu.add_separator() 668 # menu.add_command(label="Save options", command=self.mOptSave) 669 670 if self.progress: 671 self.progress.update(step=1) 672 673 # macOS: tk creates the menu item "Help->PySolFC Help", therefore 674 # we will not create a duplicate "Help->Contents" item. 675 # The tk-provided menu item expects this callback. 676 self.top.createcommand('tk::mac::ShowHelp', self.mHelp) 677 678 menu = MfxMenu(self.menubar, label=n_("&Help")) 679 if WIN_SYSTEM != "aqua": 680 menu.add_command( 681 label=n_("&Contents"), 682 command=self.mHelp, accelerator=m+"F1") 683 menu.add_command( 684 label=n_("&How to use PySol"), 685 command=self.mHelpHowToPlay) 686 menu.add_command( 687 label=n_("&Rules for this game"), 688 command=self.mHelpRules, accelerator="F1") 689 menu.add_command( 690 label=n_("&License terms"), 691 command=self.mHelpLicense) 692 # menu.add_command(label=n_("What's &new ?"), command=self.mHelpNews) 693 if WIN_SYSTEM != "aqua": 694 menu.add_separator() 695 menu.add_command( 696 label=_("&About %s...") % TITLE, 697 command=self.mHelpAbout) 698 699 MfxMenubar.addPath = None 700 701 # FIXME: all key bindings should be *added* to keyPressHandler 702 ctrl = "Control-" 703 if sys.platform == "darwin": 704 ctrl = "Command-" 705 self._bindKey("", "n", self.mNewGame) 706 self._bindKey(ctrl, "w", self.mSelectGameDialog) 707 self._bindKey(ctrl, "v", self.mSelectGameDialogWithPreview) 708 self._bindKey(ctrl, "r", lambda e: self.mSelectRandomGame()) 709 self._bindKey(ctrl, "m", self.mSelectGameById) 710 self._bindKey(ctrl, "n", self.mNewGameWithNextId) 711 self._bindKey(ctrl, "o", self.mOpen) 712 self._bindKey(ctrl, "s", self.mSave) 713 self._bindKey(ctrl, "x", self.mHoldAndQuit) 714 self._bindKey(ctrl, "q", self.mQuit) 715 self._bindKey(ctrl, "z", self.mUndo) 716 self._bindKey("", "z", self.mUndo) 717 self._bindKey("", "BackSpace", self.mUndo) # undocumented 718 self._bindKey("", "KP_Enter", self.mUndo) # undocumented 719 self._bindKey("", "r", self.mRedo) 720 self._bindKey(ctrl, "g", self.mRestart) 721 self._bindKey("", "space", self.mDeal) # undocumented 722 self._bindKey(ctrl, "y", lambda e: self.mPlayerStats(mode=100)) 723 self._bindKey(ctrl, "t", lambda e: self.mPlayerStats(mode=105)) 724 self._bindKey("", "h", self.mHint) 725 self._bindKey(ctrl, "h", self.mHint1) # undocumented 726 # self._bindKey("", "Shift_L", self.mHighlightPiles) 727 # self._bindKey("", "Shift_R", self.mHighlightPiles) 728 self._bindKey("", "i", self.mHighlightPiles) 729 self._bindKey("", "F3", self.mFindCard) 730 self._bindKey(ctrl, "d", self.mDemo) 731 self._bindKey(ctrl, "e", self.mSelectCardsetDialog) 732 if USE_PIL: 733 self._bindKey(ctrl, "plus", self.mIncreaseCardset) 734 self._bindKey(ctrl, "equal", self.mIncreaseCardset) 735 self._bindKey(ctrl, "minus", self.mDecreaseCardset) 736 self._bindKey(ctrl, "0", self.mOptAutoScale) 737 self._bindKey(ctrl, "b", self.mOptChangeCardback) # undocumented 738 self._bindKey(ctrl, "i", self.mOptChangeTableTile) # undocumented 739 self._bindKey(ctrl, "p", self.mOptPlayerOptions) # undocumented 740 self._bindKey(ctrl, "F1", self.mHelp) 741 self._bindKey("", "F1", self.mHelpRules) 742 self._bindKey("", "Print", self.mScreenshot) 743 self._bindKey(ctrl, "u", self.mPlayNextMusic) # undocumented 744 self._bindKey("", "p", self.mPause) 745 self._bindKey("", "Pause", self.mPause) # undocumented 746 self._bindKey("", "Escape", self.mIconify) # undocumented 747 # ASD and LKJ 748 self._bindKey("", "a", self.mDrop) 749 self._bindKey(ctrl, "a", self.mDrop1) 750 self._bindKey("", "s", self.mUndo) 751 self._bindKey("", "d", self.mDeal) 752 self._bindKey("", "l", self.mDrop) 753 self._bindKey(ctrl, "l", self.mDrop1) 754 self._bindKey("", "k", self.mUndo) 755 self._bindKey("", "j", self.mDeal) 756 757 self._bindKey("", "F2", self.mStackDesk) 758 # 759 # undocumented, devel 760 self._bindKey("", "slash", lambda e: self.mPlayerStats(mode=106)) 761 # 762 self._bindKey("", "f", self.mShuffle) 763 764 for i in range(9): 765 self._bindKey( 766 ctrl, str(i+1), 767 lambda e, i=i: self.mGotoBookmark(i, confirm=0)) 768 769 # undocumented, devel 770 self._bindKey(ctrl, "End", self.mPlayNextMusic) 771 self._bindKey(ctrl, "Prior", self.mSelectPrevGameByName) 772 self._bindKey(ctrl, "Next", self.mSelectNextGameByName) 773 self._bindKey(ctrl, "Up", self.mSelectPrevGameById) 774 self._bindKey(ctrl, "Down", self.mSelectNextGameById) 775 776 # 777 # key binding utility 778 # 779 780 def _bindKey(self, modifier, key, func): 781 sequence = "<" + modifier + "KeyPress-" + key + ">" 782 bind(self.top, sequence, func) 783 if len(key) == 1 and key != key.upper(): 784 key = key.upper() 785 sequence = "<" + modifier + "KeyPress-" + key + ">" 786 bind(self.top, sequence, func) 787 788 def _keyPressHandler(self, event): 789 r = EVENT_PROPAGATE 790 if event and self.game: 791 # print event.__dict__ 792 if self.game.demo: 793 # stop the demo by setting self.game.demo.keypress 794 if event.char: # ignore Ctrl/Shift/etc. 795 self.game.demo.keypress = event.char 796 r = EVENT_HANDLED 797 # func = self.keybindings.get(event.char) 798 # if func and (event.state & ~2) == 0: 799 # func(event) 800 # r = EVENT_HANDLED 801 return r 802 803 # 804 # Select Game menu creation 805 # 806 807 def _addSelectGameMenu(self, menu): 808 # games = map(self.app.gdb.get, 809 # self.app.gdb.getGamesIdSortedByShortName()) 810 games = list(map( 811 self.app.gdb.get, self.app.gdb.getGamesIdSortedByName())) 812 # games = tuple(games) 813 # menu = MfxMenu(menu, label="Select &game") 814 m = "Ctrl-" 815 if sys.platform == "darwin": 816 m = "Cmd-" 817 menu.add_command(label=n_("All &games..."), accelerator=m+"V", 818 command=self.mSelectGameDialogWithPreview) 819 if not SELECT_GAME_MENU: 820 return 821 menu.add_separator() 822 self._addSelectPopularGameSubMenu(games, menu, self.mSelectGame, 823 self.tkopt.gameid) 824 self._addSelectFrenchGameSubMenu(games, menu, self.mSelectGame, 825 self.tkopt.gameid) 826 if self.progress: 827 self.progress.update(step=1) 828 self._addSelectMahjonggGameSubMenu(games, menu, self.mSelectGame, 829 self.tkopt.gameid) 830 self._addSelectOrientalGameSubMenu(games, menu, self.mSelectGame, 831 self.tkopt.gameid) 832 self._addSelectSpecialGameSubMenu(games, menu, self.mSelectGame, 833 self.tkopt.gameid) 834 self._addSelectCustomGameSubMenu(games, menu, self.mSelectGame, 835 self.tkopt.gameid) 836 menu.add_separator() 837 if self.progress: 838 self.progress.update(step=1) 839 self._addSelectAllGameSubMenu(games, menu, self.mSelectGame, 840 self.tkopt.gameid) 841 842 def _addSelectGameSubMenu(self, games, menu, select_data, 843 command, variable): 844 # print select_data 845 need_sep = 0 846 for label, select_func in select_data: 847 if label is None: 848 need_sep = 1 849 continue 850 g = list(filter(select_func, games)) 851 if not g: 852 continue 853 if need_sep: 854 menu.add_separator() 855 need_sep = 0 856 submenu = MfxMenu(menu, label=label) 857 self._addSelectGameSubSubMenu(g, submenu, command, variable) 858 859 def _getNumGames(self, games, select_data): 860 ngames = 0 861 for label, select_func in select_data: 862 ngames += len(list(filter(select_func, games))) 863 return ngames 864 865 def _addSelectMahjonggGameSubMenu(self, games, menu, command, variable): 866 def select_func(gi): 867 return gi.si.game_type == GI.GT_MAHJONGG 868 mahjongg_games = list(filter(select_func, games)) 869 if len(mahjongg_games) == 0: 870 return 871 # 872 menu = MfxMenu(menu, label=n_("&Mahjongg games")) 873 874 def add_menu(games, c0, c1, menu=menu, 875 variable=variable, command=command): 876 if not games: 877 return 878 label = c0 + ' - ' + c1 879 if c0 == c1: 880 label = c0 881 submenu = MfxMenu(menu, label=label, name=None) 882 self._addSelectGameSubSubMenu(games, submenu, command, 883 variable, short_name=True) 884 885 games = {} 886 for gi in mahjongg_games: 887 c = gi.short_name.strip()[0] 888 if c in games: 889 games[c].append(gi) 890 else: 891 games[c] = [gi] 892 games = list(games.items()) 893 games.sort() 894 g0 = [] 895 c0 = c1 = games[0][0] 896 for c, g1 in games: 897 if len(g0)+len(g1) >= self.cb_max: 898 add_menu(g0, c0, c1) 899 g0 = g1 900 c0 = c1 = c 901 else: 902 g0 += g1 903 c1 = c 904 add_menu(g0, c0, c1) 905 906 def _addSelectPopularGameSubMenu(self, games, menu, command, variable): 907 def select_func(gi): 908 return gi.si.game_flags & GI.GT_POPULAR 909 if len(list(filter(select_func, games))) == 0: 910 return 911 data = (n_("&Popular games"), select_func) 912 self._addSelectGameSubMenu(games, menu, (data, ), 913 self.mSelectGamePopular, 914 self.tkopt.gameid_popular) 915 916 def _addSelectFrenchGameSubMenu(self, games, menu, command, variable): 917 if self._getNumGames(games, GI.SELECT_GAME_BY_TYPE) == 0: 918 return 919 submenu = MfxMenu(menu, label=n_("&French games")) 920 self._addSelectGameSubMenu(games, submenu, GI.SELECT_GAME_BY_TYPE, 921 self.mSelectGame, self.tkopt.gameid) 922 923 def _addSelectOrientalGameSubMenu(self, games, menu, command, variable): 924 if self._getNumGames(games, GI.SELECT_ORIENTAL_GAME_BY_TYPE) == 0: 925 return 926 submenu = MfxMenu(menu, label=n_("&Oriental games")) 927 self._addSelectGameSubMenu(games, submenu, 928 GI.SELECT_ORIENTAL_GAME_BY_TYPE, 929 self.mSelectGame, self.tkopt.gameid) 930 931 def _addSelectSpecialGameSubMenu(self, games, menu, command, variable): 932 if self._getNumGames(games, GI.SELECT_ORIENTAL_GAME_BY_TYPE) == 0: 933 return 934 submenu = MfxMenu(menu, label=n_("&Special games")) 935 self._addSelectGameSubMenu(games, submenu, 936 GI.SELECT_SPECIAL_GAME_BY_TYPE, 937 self.mSelectGame, self.tkopt.gameid) 938 939 def _addSelectCustomGameSubMenu(self, games, menu, command, variable): 940 submenu = MfxMenu(menu, label=n_("&Custom games")) 941 942 def select_func(gi): 943 return gi.si.game_type == GI.GT_CUSTOM 944 games = list(filter(select_func, games)) 945 self.updateGamesMenu(submenu, games) 946 947 def _addSelectAllGameSubMenu(self, games, menu, command, variable): 948 if menu.name != "allgamesbyname": 949 menu = MfxMenu(menu, label=n_("&All games by name")) 950 n, d = 0, self.cb_max 951 i = 0 952 while True: 953 if self.progress: 954 self.progress.update(step=1) 955 columnbreak = i > 0 and (i % d) == 0 956 i += 1 957 if not games[n:n+d]: 958 break 959 m = min(n+d-1, len(games)-1) 960 label = games[n].name[:3] + ' - ' + games[m].name[:3] 961 submenu = MfxMenu(menu, label=label, name=None) 962 self._addSelectGameSubSubMenu(games[n:n+d], submenu, 963 command, variable) 964 n += d 965 if columnbreak: 966 menu.entryconfigure(i, columnbreak=columnbreak) 967 968 def _addSelectGameSubSubMenu(self, games, menu, command, variable, 969 short_name=False): 970 # cb = (25, self.cb_max) [ len(g) > 4 * 25 ] 971 # cb = min(cb, self.cb_max) 972 cb = self.cb_max 973 for i in range(len(games)): 974 gi = games[i] 975 columnbreak = i > 0 and (i % cb) == 0 976 if short_name: 977 label = gi.short_name 978 else: 979 label = gi.name 980 # optimized by inlining 981 menu.tk.call((menu._w, 'add', 'radiobutton') + 982 menu._options({'command': command, 983 'variable': variable, 984 'columnbreak': columnbreak, 985 'value': gi.id, 986 'label': label})) 987 988 def updateGamesMenu(self, menu, games): 989 menu.delete(0, 'last') 990 if len(games) == 0: 991 menu.add_radiobutton(label=_('<none>'), name=None, 992 state='disabled') 993 elif len(games) > self.cb_max*4: 994 games.sort(key=lambda x: x.name) 995 self._addSelectAllGameSubMenu(games, menu, 996 command=self.mSelectGame, 997 variable=self.tkopt.gameid) 998 else: 999 self._addSelectGameSubSubMenu(games, menu, 1000 command=self.mSelectGame, 1001 variable=self.tkopt.gameid) 1002 1003 # 1004 # Select Game menu actions 1005 # 1006 1007 def mSelectGame(self, *args): 1008 self._mSelectGame(self.tkopt.gameid.get()) 1009 1010 def mSelectGamePopular(self, *args): 1011 self._mSelectGame(self.tkopt.gameid_popular.get()) 1012 1013 def _mSelectGameDialog(self, d): 1014 if d.status == 0 and d.button == 0 and d.gameid != self.game.id: 1015 self.tkopt.gameid.set(d.gameid) 1016 self.tkopt.gameid_popular.set(d.gameid) 1017 if 0: 1018 self._mSelectGame(d.gameid, random=d.random) 1019 else: 1020 # don't ask areYouSure() 1021 self._cancelDrag() 1022 self.game.endGame() 1023 self.game.quitGame(d.gameid, random=d.random) 1024 return EVENT_HANDLED 1025 1026 def __restoreCursor(self, *event): 1027 self.game.setCursor(cursor=self.app.top_cursor) 1028 1029 def mSelectGameDialog(self, *event): 1030 if self._cancelDrag(break_pause=False): 1031 return 1032 self.game.setCursor(cursor=CURSOR_WATCH) 1033 after_idle(self.top, self.__restoreCursor) 1034 d = self._calcSelectGameDialog()( 1035 self.top, title=_("Select game"), 1036 app=self.app, gameid=self.game.id) 1037 return self._mSelectGameDialog(d) 1038 1039 def mSelectGameDialogWithPreview(self, *event): 1040 if self._cancelDrag(break_pause=False): 1041 return 1042 self.game.setCursor(cursor=CURSOR_WATCH) 1043 bookmark = None 1044 if 0: 1045 # use a bookmark for our preview game 1046 if self.game.setBookmark(-2, confirm=0): 1047 bookmark = self.game.gsaveinfo.bookmarks[-2][0] 1048 del self.game.gsaveinfo.bookmarks[-2] 1049 after_idle(self.top, self.__restoreCursor) 1050 d = self._calcSelectGameDialogWithPreview()( 1051 self.top, title=_("Select game"), 1052 app=self.app, gameid=self.game.id, 1053 bookmark=bookmark) 1054 return self._mSelectGameDialog(d) 1055 1056 # 1057 # menubar overrides 1058 # 1059 1060 def updateFavoriteGamesMenu(self): 1061 gameids = self.app.opt.favorite_gameid 1062 submenu = self.menupath[".menubar.file.favoritegames"][2] 1063 games = [] 1064 for id in gameids: 1065 gi = self.app.getGameInfo(id) 1066 if gi: 1067 games.append(gi) 1068 self.updateGamesMenu(submenu, games) 1069 state = self._getEnabledState 1070 in_favor = self.app.game.id in gameids 1071 menu, index, submenu = self.menupath[".menubar.file.addtofavorites"] 1072 menu.entryconfig(index, state=state(not in_favor)) 1073 menu, index, submenu = \ 1074 self.menupath[".menubar.file.removefromfavorites"] 1075 menu.entryconfig(index, state=state(in_favor)) 1076 1077 def updateRecentGamesMenu(self, gameids): 1078 submenu = self.menupath[".menubar.file.recentgames"][2] 1079 games = [] 1080 for id in gameids: 1081 gi = self.app.getGameInfo(id) 1082 if gi: 1083 games.append(gi) 1084 self.updateGamesMenu(submenu, games) 1085 1086 def updateCustomGamesMenu(self): 1087 menu = self.menupath[".menubar.select.customgames"][2] 1088 menu2 = self.menupath[".menubar.select.allgamesbyname"][2] 1089 1090 def select_func_visible(gi): 1091 return gi.si.game_type != GI.GT_HIDDEN 1092 1093 def select_func_custom(gi): 1094 return gi.si.game_type == GI.GT_CUSTOM 1095 1096 games = list(map(self.app.gdb.get, 1097 self.app.gdb.getGamesIdSortedByName())) 1098 games = list(filter(select_func_visible, games)) 1099 self.progress = False 1100 self.updateGamesMenu(menu2, games) 1101 games = list(filter(select_func_custom, games)) 1102 self.updateGamesMenu(menu, games) 1103 1104 def updateBookmarkMenuState(self): 1105 state = self._getEnabledState 1106 mp1 = self.menupath.get(".menubar.edit.setbookmark") 1107 mp2 = self.menupath.get(".menubar.edit.gotobookmark") 1108 mp3 = self.menupath.get(".menubar.edit.clearbookmarks") 1109 if mp1 is None or mp2 is None or mp3 is None: 1110 return 1111 x = self.app.opt.bookmarks and self.game.canSetBookmark() 1112 # 1113 menu, index, submenu = mp1 1114 for i in range(9): 1115 submenu.entryconfig(i, state=state(x)) 1116 menu.entryconfig(index, state=state(x)) 1117 # 1118 menu, index, submenu = mp2 1119 ms = 0 1120 for i in range(9): 1121 s = self.game.gsaveinfo.bookmarks.get(i) is not None 1122 submenu.entryconfig(i, state=state(s and x)) 1123 ms = ms or s 1124 menu.entryconfig(index, state=state(ms and x)) 1125 # 1126 menu, index, submenu = mp3 1127 menu.entryconfig(index, state=state(ms and x)) 1128 1129 def updateBackgroundImagesMenu(self): 1130 mp = self.menupath.get(".menubar.options.cardbackground") 1131 # delete all entries 1132 submenu = mp[2] 1133 submenu.delete(0, "last") 1134 # insert new cardbacks 1135 mbacks = self.app.images.getCardbacks() 1136 cb = int(math.ceil(math.sqrt(len(mbacks)))) 1137 for i in range(len(mbacks)): 1138 columnbreak = i > 0 and (i % cb) == 0 1139 submenu.add_radiobutton( 1140 label=mbacks[i].name, image=mbacks[i].menu_image, 1141 variable=self.tkopt.cardback, value=i, 1142 command=self.mOptCardback, columnbreak=columnbreak, 1143 indicatoron=0, hidemargin=0) 1144 1145 # 1146 # menu updates 1147 # 1148 1149 def setMenuState(self, state, path): 1150 # print state, path 1151 path = ".menubar." + path 1152 mp = self.menupath.get(path) 1153 menu, index, submenu = mp 1154 s = self._getEnabledState(state) 1155 menu.entryconfig(index, state=s) 1156 1157 def setToolbarState(self, state, path): 1158 # print state, path 1159 s = self._getEnabledState(state) 1160 w = getattr(self.app.toolbar, path + "_button") 1161 w["state"] = s 1162 1163 def _setCommentMenu(self, v): 1164 self.tkopt.comment.set(v) 1165 1166 def _setPauseMenu(self, v): 1167 self.tkopt.pause.set(v) 1168 1169 # 1170 # menu actions 1171 # 1172 1173 DEFAULTEXTENSION = ".pso" 1174 FILETYPES = ((_("%s files") % TITLE, "*" + DEFAULTEXTENSION), 1175 (_("All files"), "*")) 1176 1177 def mAddFavor(self, *event): 1178 gameid = self.app.game.id 1179 if gameid not in self.app.opt.favorite_gameid: 1180 self.app.opt.favorite_gameid.append(gameid) 1181 self.updateFavoriteGamesMenu() 1182 1183 def mDelFavor(self, *event): 1184 gameid = self.app.game.id 1185 if gameid in self.app.opt.favorite_gameid: 1186 self.app.opt.favorite_gameid.remove(gameid) 1187 self.updateFavoriteGamesMenu() 1188 1189 def mOpen(self, *event): 1190 if self._cancelDrag(break_pause=False): 1191 return 1192 filename = self.game.filename 1193 if filename: 1194 idir, ifile = os.path.split(os.path.normpath(filename)) 1195 else: 1196 idir, ifile = "", "" 1197 if not idir: 1198 idir = self.app.dn.savegames 1199 d = tkinter_tkfiledialog.Open() 1200 filename = d.show(filetypes=self.FILETYPES, 1201 defaultextension=self.DEFAULTEXTENSION, 1202 initialdir=idir, initialfile=ifile) 1203 if filename: 1204 filename = os.path.normpath(filename) 1205 # filename = os.path.normcase(filename) 1206 if os.path.isfile(filename): 1207 self.game.loadGame(filename) 1208 1209 def mExportCurrentLayout(self, *event): 1210 if self._cancelDrag(break_pause=False): 1211 return 1212 game = self.game 1213 if not self.menustate.save_as: 1214 return 1215 if not game.Solver_Class: 1216 d = self._calc_MfxMessageDialog()( 1217 self.top, title=_('Export game error'), 1218 text=_(''' 1219Unsupported game for export. 1220'''), 1221 bitmap='error') 1222 return 1223 1224 filename = game.filename 1225 if not filename: 1226 filename = self.app.getGameSaveName(self.game.id) 1227 if os.name == "posix" or os.path.supports_unicode_filenames: 1228 filename += "-" + self.game.getGameNumber(format=0) 1229 else: 1230 filename += "-01" 1231 filename += ".board" 1232 idir, ifile = os.path.split(os.path.normpath(filename)) 1233 if not idir: 1234 idir = self.app.dn.savegames 1235 # print self.game.filename, ifile 1236 d = tkinter_tkfiledialog.SaveAs() 1237 filename = d.show(filetypes=self.FILETYPES, 1238 defaultextension=self.DEFAULTEXTENSION, 1239 initialdir=idir, initialfile=ifile) 1240 if filename: 1241 filename = os.path.normpath(filename) 1242 # filename = os.path.normcase(filename) 1243 with open(filename, 'w') as fh: 1244 game = self.game 1245 fh.write(game.Solver_Class(game, self).calcBoardString()) 1246 self.updateMenus() 1247 1248 def mImportStartingLayout(self, *event): 1249 if self._cancelDrag(break_pause=False): 1250 return 1251 game = self.game 1252 if not game.Solver_Class: 1253 d = self._calc_MfxMessageDialog()( 1254 self.top, title=_('Import game error'), 1255 text=_(''' 1256Unsupported game for import. 1257'''), 1258 bitmap='error') 1259 return 1260 1261 filename = self.game.filename 1262 if filename: 1263 idir, ifile = os.path.split(os.path.normpath(filename)) 1264 else: 1265 idir, ifile = "", "" 1266 if not idir: 1267 idir = self.app.dn.savegames 1268 d = tkinter_tkfiledialog.Open() 1269 key = 'PYSOL_DEBUG_IMPORT' 1270 if key not in os.environ: 1271 filename = d.show(filetypes=self.FILETYPES, 1272 defaultextension=self.DEFAULTEXTENSION, 1273 initialdir=idir, initialfile=ifile) 1274 else: 1275 filename = os.environ[key] 1276 if filename: 1277 filename = os.path.normpath(filename) 1278 # filename = os.path.normcase(filename) 1279 if os.path.isfile(filename): 1280 with open(filename, 'r+b') as fh: 1281 game = self.game 1282 try: 1283 game.Solver_Class(game, self).importFile( 1284 fh, game, self) 1285 except PySolHintLayoutImportError as err: 1286 self._calc_MfxMessageDialog()( 1287 self.top, 1288 title=_('Import game error'), 1289 text=err.format(), 1290 bitmap='error' 1291 ) 1292 game.busy = False 1293 game.endGame() 1294 game.newGame() 1295 1296 def mSaveAs(self, *event): 1297 if self._cancelDrag(break_pause=False): 1298 return 1299 if not self.menustate.save_as: 1300 return 1301 filename = self.game.filename 1302 if not filename: 1303 filename = self.app.getGameSaveName(self.game.id) 1304 if os.name == "posix": 1305 filename = filename + "-" + self.game.getGameNumber(format=0) 1306 elif os.path.supports_unicode_filenames: # new in python 2.3 1307 filename = filename + "-" + self.game.getGameNumber(format=0) 1308 else: 1309 filename = filename + "-01" 1310 filename = filename + self.DEFAULTEXTENSION 1311 idir, ifile = os.path.split(os.path.normpath(filename)) 1312 if not idir: 1313 idir = self.app.dn.savegames 1314 # print self.game.filename, ifile 1315 d = tkinter_tkfiledialog.SaveAs() 1316 filename = d.show(filetypes=self.FILETYPES, 1317 defaultextension=self.DEFAULTEXTENSION, 1318 initialdir=idir, initialfile=ifile) 1319 if filename: 1320 filename = os.path.normpath(filename) 1321 # filename = os.path.normcase(filename) 1322 self.game.saveGame(filename) 1323 self.updateMenus() 1324 1325 def mPause(self, *args): 1326 if not self.game: 1327 return 1328 if not self.game.pause: 1329 if self._cancelDrag(): 1330 return 1331 self.game.doPause() 1332 self.tkopt.pause.set(self.game.pause) 1333 1334 def mOptSoundDialog(self, *args): 1335 if self._cancelDrag(break_pause=False): 1336 return 1337 self._calcSoundOptionsDialog()( 1338 self.top, _("Sound settings"), self.app) 1339 self.tkopt.sound.set(self.app.opt.sound) 1340 1341 def mOptAutoFaceUp(self, *args): 1342 if self._cancelDrag(): 1343 return 1344 self.app.opt.autofaceup = self.tkopt.autofaceup.get() 1345 if self.app.opt.autofaceup: 1346 self.game.autoPlay() 1347 1348 def mOptAutoDrop(self, *args): 1349 if self._cancelDrag(): 1350 return 1351 self.app.opt.autodrop = self.tkopt.autodrop.get() 1352 if self.app.opt.autodrop: 1353 self.game.autoPlay() 1354 1355 def mOptAutoDeal(self, *args): 1356 if self._cancelDrag(): 1357 return 1358 self.app.opt.autodeal = self.tkopt.autodeal.get() 1359 if self.app.opt.autodeal: 1360 self.game.autoPlay() 1361 1362 def mOptQuickPlay(self, *args): 1363 if self._cancelDrag(break_pause=False): 1364 return 1365 self.app.opt.quickplay = self.tkopt.quickplay.get() 1366 1367 def mOptEnableUndo(self, *args): 1368 if self._cancelDrag(break_pause=False): 1369 return 1370 self.app.opt.undo = self.tkopt.undo.get() 1371 self.game.updateMenus() 1372 1373 def mOptEnableBookmarks(self, *args): 1374 if self._cancelDrag(break_pause=False): 1375 return 1376 self.app.opt.bookmarks = self.tkopt.bookmarks.get() 1377 self.game.updateMenus() 1378 1379 def mOptEnableHint(self, *args): 1380 if self._cancelDrag(break_pause=False): 1381 return 1382 self.app.opt.hint = self.tkopt.hint.get() 1383 self.game.updateMenus() 1384 1385 def mOptEnableShuffle(self, *args): 1386 if self._cancelDrag(break_pause=False): 1387 return 1388 self.app.opt.shuffle = self.tkopt.shuffle.get() 1389 self.game.updateMenus() 1390 1391 def mOptEnableHighlightPiles(self, *args): 1392 if self._cancelDrag(break_pause=False): 1393 return 1394 self.app.opt.highlight_piles = self.tkopt.highlight_piles.get() 1395 self.game.updateMenus() 1396 1397 def mOptEnableHighlightCards(self, *args): 1398 if self._cancelDrag(break_pause=False): 1399 return 1400 self.app.opt.highlight_cards = self.tkopt.highlight_cards.get() 1401 self.game.updateMenus() 1402 1403 def mOptEnableHighlightSameRank(self, *args): 1404 if self._cancelDrag(break_pause=False): 1405 return 1406 self.app.opt.highlight_samerank = self.tkopt.highlight_samerank.get() 1407 # self.game.updateMenus() 1408 1409 def mOptEnableHighlightNotMatching(self, *args): 1410 if self._cancelDrag(break_pause=False): 1411 return 1412 self.app.opt.highlight_not_matching = \ 1413 self.tkopt.highlight_not_matching.get() 1414 # self.game.updateMenus() 1415 1416 def mOptAnimations(self, *args): 1417 if self._cancelDrag(break_pause=False): 1418 return 1419 self.app.opt.animations = self.tkopt.animations.get() 1420 1421 def mRedealAnimation(self, *args): 1422 if self._cancelDrag(break_pause=False): 1423 return 1424 self.app.opt.redeal_animation = self.tkopt.redeal_animation.get() 1425 1426 def mWinAnimation(self, *args): 1427 if self._cancelDrag(break_pause=False): 1428 return 1429 self.app.opt.win_animation = self.tkopt.win_animation.get() 1430 1431 def mOptShadow(self, *args): 1432 if self._cancelDrag(break_pause=False): 1433 return 1434 self.app.opt.shadow = self.tkopt.shadow.get() 1435 1436 def mOptShade(self, *args): 1437 if self._cancelDrag(break_pause=False): 1438 return 1439 self.app.opt.shade = self.tkopt.shade.get() 1440 1441 def mOptShrinkFaceDown(self, *args): 1442 if self._cancelDrag(break_pause=False): 1443 return 1444 self.app.opt.shrink_face_down = self.tkopt.shrink_face_down.get() 1445 self.game.endGame(bookmark=1) 1446 self.game.quitGame(bookmark=1) 1447 1448 def mOptShadeFilledStacks(self, *args): 1449 if self._cancelDrag(break_pause=False): 1450 return 1451 self.app.opt.shade_filled_stacks = self.tkopt.shade_filled_stacks.get() 1452 self.game.endGame(bookmark=1) 1453 self.game.quitGame(bookmark=1) 1454 1455 def mOptMahjonggShowRemoved(self, *args): 1456 if self._cancelDrag(): 1457 return 1458 self.app.opt.mahjongg_show_removed = \ 1459 self.tkopt.mahjongg_show_removed.get() 1460 # self.game.updateMenus() 1461 self.game.endGame(bookmark=1) 1462 self.game.quitGame(bookmark=1) 1463 1464 def mOptShisenShowHint(self, *args): 1465 if self._cancelDrag(break_pause=False): 1466 return 1467 self.app.opt.shisen_show_hint = self.tkopt.shisen_show_hint.get() 1468 # self.game.updateMenus() 1469 1470 def mOptAccordionDealAll(self, *args): 1471 if self._cancelDrag(break_pause=False): 1472 return 1473 self.app.opt.accordion_deal_all = self.tkopt.accordion_deal_all.get() 1474 # self.game.updateMenus() 1475 1476 def _updateCardSize(self): 1477 geom = (self.app.canvas.winfo_width(), 1478 self.app.canvas.winfo_height()) 1479 self.app.opt.game_geometry = geom 1480 self.app.game.resizeGame(card_size_manually=True) 1481 if self.app.opt.auto_scale or self.app.opt.spread_stacks: 1482 w, h = self.app.opt.game_geometry 1483 self.app.canvas.setInitialSize(w, h, scrollregion=False) 1484 # Resize a second time to auto scale 1485 self.app.game.resizeGame(card_size_manually=False) 1486 else: 1487 w = int(round(self.app.game.width * self.app.opt.scale_x)) 1488 h = int(round(self.app.game.height * self.app.opt.scale_y)) 1489 self.app.canvas.setInitialSize(w, h) 1490 self.app.top.wm_geometry("") # cancel user-specified geometry 1491 # self.app.top.update_idletasks() 1492 self.app.setTile(self.app.tabletile_index) 1493 self.tkopt.tabletile.set(self.app.tabletile_index) 1494 1495 self.updateMenus() 1496 1497 def mIncreaseCardset(self, *event): 1498 if self._cancelDrag(break_pause=True): 1499 return 1500 if self.app.opt.scale_x < 4: 1501 self.app.opt.scale_x += 0.1 1502 else: 1503 return 1504 if self.app.opt.scale_y < 4: 1505 self.app.opt.scale_y += 0.1 1506 else: 1507 return 1508 self.app.opt.auto_scale = False 1509 self.tkopt.auto_scale.set(False) 1510 self._updateCardSize() 1511 1512 def mDecreaseCardset(self, *event): 1513 if self._cancelDrag(break_pause=True): 1514 return 1515 if self.app.opt.scale_x > 0.5: 1516 self.app.opt.scale_x -= 0.1 1517 else: 1518 return 1519 if self.app.opt.scale_y > 0.5: 1520 self.app.opt.scale_y -= 0.1 1521 else: 1522 return 1523 self.app.opt.auto_scale = False 1524 self.tkopt.auto_scale.set(False) 1525 self._updateCardSize() 1526 1527 def mResetCardset(self, *event): 1528 if self._cancelDrag(break_pause=True): 1529 return 1530 self.app.opt.scale_x = 1 1531 self.app.opt.scale_y = 1 1532 1533 self.app.opt.auto_scale = False 1534 self.tkopt.auto_scale.set(False) 1535 self._updateCardSize() 1536 1537 def mOptAutoScale(self, *event): 1538 if self._cancelDrag(break_pause=True): 1539 return 1540 auto_scale = not self.app.opt.auto_scale 1541 1542 # In the future, it should be possible to use both options together, 1543 # but the current logic conflicts, so not allowed for now. 1544 self.app.opt.spread_stacks = False 1545 self.tkopt.spread_stacks.set(False) 1546 1547 self.app.opt.auto_scale = auto_scale 1548 self.tkopt.auto_scale.set(auto_scale) 1549 self._updateCardSize() 1550 1551 def mOptPreserveAspectRatio(self, *event): 1552 if self._cancelDrag(break_pause=True): 1553 return 1554 preserve_aspect_ratio = not self.app.opt.preserve_aspect_ratio 1555 1556 self.app.opt.preserve_aspect_ratio = preserve_aspect_ratio 1557 self.tkopt.preserve_aspect_ratio.set(preserve_aspect_ratio) 1558 self._updateCardSize() 1559 1560 def mOptSpreadStacks(self, *event): 1561 if self._cancelDrag(break_pause=True): 1562 return 1563 spread_stacks = not self.app.opt.spread_stacks 1564 1565 # In the future, it should be possible to use both options together, 1566 # but the current logic conflicts, so not allowed for now. 1567 self.app.opt.auto_scale = False 1568 self.tkopt.auto_scale.set(False) 1569 1570 self.app.opt.spread_stacks = spread_stacks 1571 self.tkopt.spread_stacks.set(spread_stacks) 1572 self._updateCardSize() 1573 1574 def mOptCenterLayout(self, *event): 1575 if self._cancelDrag(break_pause=True): 1576 return 1577 self.app.opt.center_layout = not self.app.opt.center_layout 1578 self._updateCardSize() 1579 1580 def _mOptCardback(self, index): 1581 if self._cancelDrag(break_pause=False): 1582 return 1583 cs = self.app.cardset 1584 old_index = cs.backindex 1585 cs.updateCardback(backindex=index) 1586 if cs.backindex == old_index: 1587 return 1588 self.app.updateCardset(self.game.id) 1589 image = self.app.images.getBack(update=True) 1590 for card in self.game.cards: 1591 card.updateCardBackground(image=image) 1592 self.app.canvas.update_idletasks() 1593 self.tkopt.cardback.set(cs.backindex) 1594 1595 def mOptCardback(self, *event): 1596 self._mOptCardback(self.tkopt.cardback.get()) 1597 1598 def mOptChangeCardback(self, *event): 1599 self._mOptCardback(self.app.cardset.backindex + 1) 1600 1601 def mOptChangeTableTile(self, *event): 1602 if self._cancelDrag(break_pause=False): 1603 return 1604 n = self.app.tabletile_manager.len() 1605 if n >= 2: 1606 i = (self.tkopt.tabletile.get() + 1) % n 1607 if self.app.setTile(i): 1608 self.tkopt.tabletile.set(i) 1609 1610 def mSelectTileDialog(self, *event): 1611 if self._cancelDrag(break_pause=False): 1612 return 1613 key = self.app.tabletile_index 1614 if key <= 0: 1615 key = self.app.opt.colors['table'] # .lower() 1616 d = self._calcSelectTileDialogWithPreview()( 1617 self.top, app=self.app, 1618 title=_("Select table background"), 1619 manager=self.app.tabletile_manager, 1620 key=key) 1621 if d.status == 0 and d.button == 0: 1622 if isinstance(d.key, str): 1623 tile = self.app.tabletile_manager.get(0) 1624 tile.color = d.key 1625 if self.app.setTile(0): 1626 self.tkopt.tabletile.set(0) 1627 elif d.key > 0 and d.key != self.app.tabletile_index: 1628 if self.app.setTile(d.key): 1629 self.tkopt.tabletile.set(d.key) 1630 1631 def mOptToolbar(self, *event): 1632 # if self._cancelDrag(break_pause=False): return 1633 self.setToolbarSide(self.tkopt.toolbar.get()) 1634 1635 def mOptToolbarStyle(self, *event): 1636 # if self._cancelDrag(break_pause=False): return 1637 self.setToolbarStyle(self.tkopt.toolbar_style.get()) 1638 1639 def mOptToolbarCompound(self, *event): 1640 # if self._cancelDrag(break_pause=False): return 1641 self.setToolbarCompound(self.tkopt.toolbar_compound.get()) 1642 1643 def mOptToolbarSize(self, *event): 1644 # if self._cancelDrag(break_pause=False): return 1645 self.setToolbarSize(self.tkopt.toolbar_size.get()) 1646 1647 def mOptToolbarConfig(self, w): 1648 self.toolbarConfig(w, self.tkopt.toolbar_vars[w].get()) 1649 1650 def mOptStatusbar(self, *event): 1651 if self._cancelDrag(break_pause=False): 1652 return 1653 if not self.app.statusbar: 1654 return 1655 side = self.tkopt.statusbar.get() 1656 self.app.opt.statusbar = side 1657 resize = not self.app.opt.save_games_geometry 1658 if self.app.statusbar.show(side, resize=resize): 1659 self.top.update_idletasks() 1660 1661 def mOptNumCards(self, *event): 1662 if self._cancelDrag(break_pause=False): 1663 return 1664 self.app.opt.num_cards = self.tkopt.num_cards.get() 1665 1666 def mOptHelpbar(self, *event): 1667 if self._cancelDrag(break_pause=False): 1668 return 1669 if not self.app.helpbar: 1670 return 1671 show = self.tkopt.helpbar.get() 1672 self.app.opt.helpbar = show 1673 resize = not self.app.opt.save_games_geometry 1674 if self.app.helpbar.show(show, resize=resize): 1675 self.top.update_idletasks() 1676 1677 def mOptSaveGamesGeometry(self, *event): 1678 if self._cancelDrag(break_pause=False): 1679 return 1680 self.app.opt.save_games_geometry = self.tkopt.save_games_geometry.get() 1681 1682 def mOptDemoLogo(self, *event): 1683 if self._cancelDrag(break_pause=False): 1684 return 1685 self.app.opt.demo_logo = self.tkopt.demo_logo.get() 1686 1687 def mOptSplashscreen(self, *event): 1688 if self._cancelDrag(break_pause=False): 1689 return 1690 self.app.opt.splashscreen = self.tkopt.splashscreen.get() 1691 1692 def mOptMouseType(self, *event): 1693 if self._cancelDrag(break_pause=False): 1694 return 1695 self.app.opt.mouse_type = self.tkopt.mouse_type.get() 1696 1697 def mOptMouseUndo(self, *event): 1698 if self._cancelDrag(break_pause=False): 1699 return 1700 self.app.opt.mouse_undo = self.tkopt.mouse_undo.get() 1701 1702 def mOptNegativeBottom(self, *event): 1703 if self._cancelDrag(): 1704 return 1705 self.app.opt.negative_bottom = self.tkopt.negative_bottom.get() 1706 self.app.updateCardset() 1707 self.game.endGame(bookmark=1) 1708 self.game.quitGame(bookmark=1) 1709 1710 # 1711 # toolbar support 1712 # 1713 1714 def setToolbarSide(self, side): 1715 if self._cancelDrag(break_pause=False): 1716 return 1717 self.app.opt.toolbar = side 1718 self.tkopt.toolbar.set(side) # update radiobutton 1719 resize = not self.app.opt.save_games_geometry 1720 if self.app.toolbar.show(side, resize=resize): 1721 self.top.update_idletasks() 1722 1723 def setToolbarSize(self, size): 1724 if self._cancelDrag(break_pause=False): 1725 return 1726 self.app.opt.toolbar_size = size 1727 self.tkopt.toolbar_size.set(size) # update radiobutton 1728 dir = self.app.getToolbarImagesDir() 1729 if self.app.toolbar.updateImages(dir, size): 1730 self.game.updateStatus(player=self.app.opt.player) 1731 self.top.update_idletasks() 1732 1733 def setToolbarStyle(self, style): 1734 if self._cancelDrag(break_pause=False): 1735 return 1736 self.app.opt.toolbar_style = style 1737 self.tkopt.toolbar_style.set(style) # update radiobutton 1738 dir = self.app.getToolbarImagesDir() 1739 size = self.app.opt.toolbar_size 1740 if self.app.toolbar.updateImages(dir, size): 1741 # self.game.updateStatus(player=self.app.opt.player) 1742 self.top.update_idletasks() 1743 1744 def setToolbarCompound(self, compound): 1745 if self._cancelDrag(break_pause=False): 1746 return 1747 self.app.opt.toolbar_compound = compound 1748 self.tkopt.toolbar_compound.set(compound) # update radiobutton 1749 if self.app.toolbar.setCompound(compound): 1750 self.game.updateStatus(player=self.app.opt.player) 1751 self.top.update_idletasks() 1752 1753 def wizardDialog(self, edit=False): 1754 from pysollib.wizardutil import write_game, reset_wizard 1755 WizardDialog = self._calcWizardDialog() 1756 1757 if edit: 1758 reset_wizard(self.game) 1759 else: 1760 reset_wizard(None) 1761 d = WizardDialog(self.top, _('Solitaire Wizard'), self.app) 1762 if d.status == 0 and d.button == 0: 1763 try: 1764 if edit: 1765 gameid = write_game(self.app, game=self.game) 1766 else: 1767 gameid = write_game(self.app) 1768 except Exception as err: 1769 # if False: 1770 # traceback.print_exc() 1771 self._calc_MfxMessageDialog()( 1772 self.top, title=_('Save game error'), 1773 text=_(''' 1774Error while saving game. 1775 1776%s 1777''') % str(err), 1778 bitmap='error') 1779 return 1780 1781 if SELECT_GAME_MENU: 1782 self.updateCustomGamesMenu() 1783 1784 self.tkopt.gameid.set(gameid) 1785 self._mSelectGame(gameid, force=True) 1786 1787 def mWizard(self, *event): 1788 if self._cancelDrag(break_pause=False): 1789 return 1790 self.wizardDialog() 1791 1792 def mWizardEdit(self, *event): 1793 if self._cancelDrag(break_pause=False): 1794 return 1795 self.wizardDialog(edit=True) 1796 1797 def mWizardDelete(self, *event): 1798 if self._cancelDrag(break_pause=False): 1799 return 1800 if not self.game.areYouSure(_("Delete game"), 1801 _("Delete the game %s?") 1802 % self.game.gameinfo.name): 1803 return 1804 from pysollib.wizardutil import delete_game 1805 delete_game(self.game) 1806 self.game.endGame() 1807 self.game.quitGame(2) 1808 1809 if SELECT_GAME_MENU: 1810 self.updateCustomGamesMenu() 1811 1812 def toolbarConfig(self, w, v): 1813 if self._cancelDrag(break_pause=False): 1814 return 1815 self.app.opt.toolbar_vars[w] = v 1816 self.app.toolbar.config(w, v) 1817 self.top.update_idletasks() 1818 1819 # 1820 # stacks descriptions 1821 # 1822 1823 def mStackDesk(self, *event): 1824 if self.game.stackdesc_list: 1825 self.game.deleteStackDesc() 1826 else: 1827 if self._cancelDrag(break_pause=True): 1828 return 1829 self.game.showStackDesc() 1830