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# 9# This program is free software: you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation, either version 3 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program. If not, see <http://www.gnu.org/licenses/>. 21# 22# ---------------------------------------------------------------------------## 23 24import os 25import sys 26import traceback 27 28import configobj 29 30import pysollib.settings 31from pysollib.mfxutil import print_err 32from pysollib.mygettext import _ 33from pysollib.mygettext import myGettext 34from pysollib.pysoltk import TOOLBAR_BUTTONS, TOOLKIT 35from pysollib.resource import CSI 36 37 38import six 39 40import validate 41 42# ************************************************************************ 43# * Options 44# ************************************************************************ 45 46_global_settings = { 47 'mouse_button1': 1, 48 'mouse_button2': 2, 49 'mouse_button3': 3, 50} 51 52 53def calcCustomMouseButtonsBinding(binding_format): 54 assert _global_settings['mouse_button1'] 55 return binding_format.format( 56 mouse_button1=_global_settings['mouse_button1'], 57 mouse_button2=_global_settings['mouse_button2'], 58 mouse_button3=_global_settings['mouse_button3'], 59 ) 60 61 62configspec = ''' 63[general] 64player = string 65confirm = boolean 66update_player_stats = boolean 67autofaceup = boolean 68autodrop = boolean 69autodeal = boolean 70quickplay = boolean 71shuffle = boolean 72undo = boolean 73bookmarks = boolean 74hint = boolean 75highlight_piles = boolean 76highlight_cards = boolean 77highlight_samerank = boolean 78highlight_not_matching = boolean 79mahjongg_show_removed = boolean 80mahjongg_create_solvable = integer(0, 2) 81shisen_show_hint = boolean 82shisen_show_matching = boolean 83animations = integer(0, 5) 84redeal_animation = boolean 85win_animation = boolean 86flip_animation = boolean 87compact_stacks = boolean 88shadow = boolean 89shade = boolean 90shrink_face_down = boolean 91shade_filled_stacks = boolean 92demo_logo = boolean 93tile_theme = string 94default_tile_theme = string 95toolbar = integer(0, 4) 96toolbar_style = string 97toolbar_relief = string 98toolbar_compound = string 99toolbar_size = integer(0, 1) 100statusbar = boolean 101statusbar_game_number = boolean 102statusbar_stuck = boolean 103num_cards = boolean 104helpbar = boolean 105num_recent_games = integer(10, 100) 106last_gameid = integer 107game_holded = integer 108wm_maximized = boolean 109splashscreen = boolean 110mouse_type = string 111mouse_undo = boolean 112negative_bottom = boolean 113randomize_place = boolean 114dragcursor = boolean 115save_games_geometry = boolean 116game_geometry = int_list(min=2, max=2) 117sound = boolean 118sound_mode = integer(0, 1) 119sound_sample_volume = integer(0, 128) 120sound_sample_buffer_size = integer(1, 4) 121music = boolean 122tabletile_name = string 123center_layout = boolean 124recent_gameid = int_list 125favorite_gameid = int_list 126visible_buttons = string_list 127translate_game_names = boolean 128solver_presets = string_list 129solver_show_progress = boolean 130solver_max_iterations = integer 131solver_iterations_output_step = integer 132solver_preset = string 133display_win_message = boolean 134language = string 135 136[sound_samples] 137move = boolean 138autodrop = boolean 139drop = boolean 140nomove = boolean 141gameperfect = boolean 142deal = boolean 143gamelost = boolean 144autopilotwon = boolean 145flip = boolean 146undo = boolean 147gamefinished = boolean 148areyousure = boolean 149startdrag = boolean 150autoflip = boolean 151autopilotlost = boolean 152turnwaste = boolean 153gamewon = boolean 154droppair = boolean 155redo = boolean 156dealwaste = boolean 157extra = boolean 158 159[fonts] 160sans = list 161small = list 162fixed = list 163canvas_default = list 164canvas_small = list 165canvas_fixed = list 166canvas_large = list 167 168[colors] 169piles = string 170text = string 171table = string 172hintarrow = string 173cards_1 = string 174cards_2 = string 175samerank_1 = string 176samerank_2 = string 177not_matching = string 178 179[timeouts] 180highlight_samerank = float(0.2, 9.9) 181raise_card = float(0.2, 9.9) 182demo = float(0.2, 9.9) 183highlight_cards = float(0.2, 9.9) 184hint = float(0.2, 9.9) 185highlight_piles = float(0.2, 9.9) 186 187[cardsets] 1880 = string_list(min=2, max=2) 1891 = string_list(min=2, max=2) 1902 = string_list(min=2, max=2) 1913 = string_list(min=2, max=2) 1924 = string_list(min=2, max=2) 1935 = string_list(min=2, max=2) 1946 = string_list(min=2, max=2) 1957 = string_list(min=2, max=2) 1968 = string_list(min=2, max=2) 1979 = string_list(min=2, max=2) 198scale_cards = boolean 199scale_x = float 200scale_y = float 201auto_scale = boolean 202spread_stacks = boolean 203preserve_aspect_ratio = boolean 204'''.splitlines() 205 206 207class Options: 208 GENERAL_OPTIONS = [ 209 ('player', 'str'), 210 ('confirm', 'bool'), 211 ('update_player_stats', 'bool'), 212 ('autofaceup', 'bool'), 213 ('autodrop', 'bool'), 214 ('autodeal', 'bool'), 215 ('quickplay', 'bool'), 216 ('shuffle', 'bool'), 217 ('undo', 'bool'), 218 ('bookmarks', 'bool'), 219 ('hint', 'bool'), 220 ('highlight_piles', 'bool'), 221 ('highlight_cards', 'bool'), 222 ('highlight_samerank', 'bool'), 223 ('highlight_not_matching', 'bool'), 224 ('mahjongg_show_removed', 'bool'), 225 ('mahjongg_create_solvable', 'int'), 226 ('shisen_show_hint', 'bool'), 227 ('shisen_show_matching', 'bool'), 228 ('accordion_deal_all', 'bool'), 229 ('animations', 'int'), 230 ('redeal_animation', 'bool'), 231 ('win_animation', 'bool'), 232 ('flip_animation', 'bool'), 233 ('compact_stacks', 'bool'), 234 ('shadow', 'bool'), 235 ('shade', 'bool'), 236 ('shrink_face_down', 'bool'), 237 ('shade_filled_stacks', 'bool'), 238 ('demo_logo', 'bool'), 239 ('tile_theme', 'str'), 240 ('default_tile_theme', 'str'), 241 ('toolbar', 'int'), 242 ('toolbar_style', 'str'), 243 ('toolbar_relief', 'str'), 244 ('toolbar_compound', 'str'), 245 ('toolbar_size', 'int'), 246 ('statusbar', 'bool'), 247 ('statusbar_game_number', 'bool'), 248 ('statusbar_stuck', 'bool'), 249 ('num_cards', 'bool'), 250 ('helpbar', 'bool'), 251 ('num_recent_games', 'int'), 252 ('last_gameid', 'int'), 253 ('game_holded', 'int'), 254 ('wm_maximized', 'bool'), 255 ('splashscreen', 'bool'), 256 ('mouse_type', 'str'), 257 ('mouse_undo', 'bool'), 258 ('negative_bottom', 'bool'), 259 ('randomize_place', 'bool'), 260 # ('save_cardsets', 'bool'), 261 ('dragcursor', 'bool'), 262 ('save_games_geometry', 'bool'), 263 ('sound', 'bool'), 264 ('sound_mode', 'int'), 265 ('sound_sample_volume', 'int'), 266 ('sound_music_volume', 'int'), 267 ('sound_sample_buffer_size', 'int'), 268 ('music', 'bool'), 269 ('tabletile_name', 'str'), 270 ('center_layout', 'bool'), 271 ('translate_game_names', 'bool'), 272 ('solver_presets', 'list'), 273 ('solver_show_progress', 'bool'), 274 ('solver_max_iterations', 'int'), 275 ('solver_iterations_output_step', 'int'), 276 ('solver_preset', 'string'), 277 ('mouse_button1', 'int'), 278 ('mouse_button2', 'int'), 279 ('mouse_button3', 'int'), 280 # ('toolbar_vars', 'list'), 281 # ('recent_gameid', 'list'), 282 # ('favorite_gameid', 'list'), 283 ('display_win_message', 'bool'), 284 ('language', 'str'), 285 ] 286 287 def __init__(self): 288 self._config = None # configobj.ConfigObj instance 289 self._config_encoding = 'utf-8' 290 291 self.version_tuple = pysollib.settings.VERSION_TUPLE # XXX 292 self.saved = 0 # XXX 293 # options menu: 294 self.player = _("Unknown") 295 self.confirm = True 296 self.update_player_stats = True 297 self.autofaceup = True 298 self.autodrop = False 299 self.autodeal = True 300 self.quickplay = True 301 self.shuffle = True 302 self.undo = True 303 self.bookmarks = True 304 self.hint = True 305 self.highlight_piles = True 306 self.highlight_cards = True 307 self.highlight_samerank = True 308 self.highlight_not_matching = True 309 self.mahjongg_show_removed = False 310 self.mahjongg_create_solvable = 2 # 0 - none, 1 - easy, 2 - hard 311 self.accordion_deal_all = True 312 if TOOLKIT == 'kivy': 313 self.mahjongg_create_solvable = 1 # 0 - none, 1 - easy, 2 - hard 314 self.shisen_show_hint = True 315 self.shisen_show_matching = False 316 self.animations = 3 # default to Medium 317 self.redeal_animation = True 318 self.win_animation = True 319 if TOOLKIT == 'kivy': 320 self.redeal_animation = False 321 self.win_animation = False 322 self.flip_animation = True 323 self.compact_stacks = True 324 self.shadow = True 325 self.shade = True 326 self.shrink_face_down = True 327 self.shade_filled_stacks = True 328 self.demo_logo = True 329 self.tile_theme = 'default' 330 self.default_tile_theme = 'default' 331 self.toolbar = 1 # 0 == hide, 1,2,3,4 == top, bottom, lef, right 332 # self.toolbar_style = 'default' 333 if TOOLKIT == 'kivy': 334 self.toolbar = 4 # 0 == hide, 1,2,3,4 == top, bottom, lef, right 335 self.toolbar_style = 'bluecurve' 336 self.toolbar_relief = 'flat' 337 self.toolbar_compound = 'none' # icons only 338 self.toolbar_size = 0 339 self.toolbar_vars = {} 340 for w in TOOLBAR_BUTTONS: 341 self.toolbar_vars[w] = True # show all buttons 342 self.statusbar = True 343 self.statusbar_game_number = False # show game number in statusbar 344 self.statusbar_stuck = False # show stuck indicator 345 self.num_cards = False 346 self.helpbar = False 347 self.splashscreen = True 348 self.mouse_button1 = 1 349 self.mouse_button2 = 2 350 self.mouse_button3 = 3 351 self.mouse_type = 'drag-n-drop' # or 'sticky-mouse' or 'point-n-click' 352 self.mouse_undo = False # use mouse for undo/redo 353 self.negative_bottom = True 354 self.translate_game_names = True 355 self.display_win_message = True 356 self.language = '' 357 # sound 358 self.sound = True 359 self.sound_mode = 1 360 self.sound_sample_volume = 75 361 self.sound_music_volume = 100 362 self.sound_sample_buffer_size = 1 # 1 - 4 (1024 - 4096 bytes) 363 self.music = True 364 self.sound_samples = { 365 'areyousure': True, 366 'autodrop': True, 367 'autoflip': True, 368 'autopilotlost': True, 369 'autopilotwon': True, 370 'deal': True, 371 'dealwaste': True, 372 'droppair': True, 373 'drop': True, 374 'extra': True, 375 'flip': True, 376 'move': True, 377 'nomove': True, 378 'redo': True, 379 'startdrag': True, 380 'turnwaste': True, 381 'undo': True, 382 'gamefinished': False, 383 'gamelost': False, 384 'gameperfect': False, 385 'gamewon': False, 386 } 387 # fonts 388 self.fonts = { 389 "default": None, 390 # "default": ("helvetica", 12), 391 "sans": ("times", 12), # for html 392 "fixed": ("courier", 12), # for html & log 393 "small": ("helvetica", 12), 394 "canvas_default": ("helvetica", 12), 395 # "canvas_card": ("helvetica", 12), 396 "canvas_fixed": ("courier", 12), 397 "canvas_large": ("helvetica", 16), 398 "canvas_small": ("helvetica", 10), 399 } 400 # colors 401 self.colors = { 402 'table': '#008200', 403 'text': '#ffffff', 404 'piles': '#ffc000', 405 'cards_1': '#ffc000', 406 'cards_2': '#0000ff', 407 'samerank_1': '#ffc000', 408 'samerank_2': '#0000ff', 409 'hintarrow': '#303030', 410 'not_matching': '#ff0000', 411 } 412 # delays 413 self.timeouts = { 414 'hint': 1.0, 415 'demo': 1.0, 416 'raise_card': 1.0, 417 'highlight_piles': 1.0, 418 'highlight_cards': 1.0, 419 'highlight_samerank': 1.0, 420 } 421 # additional startup information 422 self.num_recent_games = 15 423 self.recent_gameid = [] 424 self.favorite_gameid = [] 425 if TOOLKIT == 'kivy': 426 self.favorite_gameid = [2, 7, 8, 19, 140, 116, 152, 176, 181, 427 194, 207, 706, 721, 756, 903, 5034, 428 11004, 14405, 14410, 15411, 22225] 429 self.last_gameid = 0 # last game played 430 self.game_holded = 0 # gameid or 0 431 self.wm_maximized = 0 432 self.save_games_geometry = False 433 # saved games geometry (gameid: (width, height)) 434 self.games_geometry = {} 435 self.game_geometry = (0, 0) # game geometry before exit 436 self.offsets = {} # cards offsets 437 # 438 self.randomize_place = False 439 # self.save_cardsets = True 440 self.dragcursor = True 441 # 442 self.scale_cards = False 443 self.scale_x = 1.0 444 self.scale_y = 1.0 445 self.auto_scale = False 446 self.spread_stacks = False 447 self.center_layout = True 448 self.preserve_aspect_ratio = True 449 # solver 450 self.solver_presets = [ 451 'none', 452 'abra-kadabra', 453 'blue-yonder', 454 'conspiracy-theory', 455 'cookie-monster', 456 'cool-jives', 457 'crooked-nose', 458 'fools-gold', 459 'good-intentions', 460 'hello-world', 461 'john-galt-line', 462 'looking-glass', 463 'one-big-family', 464 'rin-tin-tin', 465 'slick-rock', 466 'the-last-mohican', 467 'video-editing', 468 'yellow-brick-road', 469 ] 470 self.solver_show_progress = True 471 self.solver_max_iterations = 100000 472 self.solver_iterations_output_step = 100 473 self.solver_preset = 'video-editing' 474 475 def setDefaults(self, top=None): 476 WIN_SYSTEM = pysollib.settings.WIN_SYSTEM 477 # toolbar 478 # if WIN_SYSTEM == 'win32': 479 # self.toolbar_style = 'crystal' 480 # fonts 481 if WIN_SYSTEM == 'win32': 482 self.fonts["sans"] = ("times new roman", 12) 483 self.fonts["fixed"] = ("courier new", 10) 484 elif WIN_SYSTEM == 'x11': 485 self.fonts["sans"] = ("helvetica", -12) 486 # tile theme 487 if WIN_SYSTEM == 'win32': 488 self.tile_theme = self.default_tile_theme = 'winnative' 489 if sys.getwindowsversion() >= (5, 1): # xp 490 self.tile_theme = 'xpnative' 491 elif WIN_SYSTEM == 'x11': 492 self.tile_theme = 'clam' 493 self.default_tile_theme = 'default' 494 elif WIN_SYSTEM == 'aqua': 495 self.tile_theme = self.default_tile_theme = 'aqua' 496 # 497 sw, sh, sd = 0, 0, 8 498 if top: 499 sw, sh, sd = (top.winfo_screenwidth(), 500 top.winfo_screenheight(), 501 top.winfo_screendepth()) 502 # bg 503 if sd > 8: 504 self.tabletile_name = "Nostalgy.gif" # basename 505 else: 506 self.tabletile_name = None 507 # cardsets 508 c = "Standard" 509 if sw < 800 or sh < 600: 510 c = "2000" 511 if TOOLKIT == 'kivy': 512 c = "Standard" 513 514 # if sw > 1024 and sh > 768: 515 # c = 'Dondorf' 516 self.cardset = { 517 # game_type: (cardset_name, back_file) 518 0: (c, ""), 519 CSI.TYPE_FRENCH: (c, ""), 520 CSI.TYPE_HANAFUDA: ("Kintengu", ""), 521 CSI.TYPE_MAHJONGG: ("Crystal Mahjongg", ""), 522 CSI.TYPE_TAROCK: ("Vienna 2K", ""), 523 CSI.TYPE_HEXADECK: ("Hex A Deck", ""), 524 CSI.TYPE_MUGHAL_GANJIFA: ("Mughal Ganjifa", ""), 525 # CSI.TYPE_NAVAGRAHA_GANJIFA: ("Navagraha Ganjifa", ""), 526 CSI.TYPE_NAVAGRAHA_GANJIFA: ("Dashavatara Ganjifa", ""), 527 CSI.TYPE_DASHAVATARA_GANJIFA: ("Dashavatara Ganjifa", ""), 528 CSI.TYPE_TRUMP_ONLY: ("Matrix", ""), 529 } 530 531 # not changeable options 532 def setConstants(self): 533 if 'shuffle' not in self.toolbar_vars: 534 # new in v.1.1 535 self.toolbar_vars['shuffle'] = True 536 if isinstance(self.mahjongg_create_solvable, bool): 537 # changed in v.1.1 538 self.mahjongg_create_solvable = 2 539 pass 540 541 def copy(self): 542 opt = Options() 543 opt.__dict__.update(self.__dict__) 544 opt.setConstants() 545 return opt 546 547 def save(self, filename): 548 config = self._config 549 550 # general 551 for key, t in self.GENERAL_OPTIONS: 552 val = getattr(self, key) 553 if isinstance(val, str): 554 if sys.version_info < (3,): 555 val = six.text_type(val, 'utf-8') 556 config['general'][key] = val 557 558 config['general']['recent_gameid'] = self.recent_gameid 559 config['general']['favorite_gameid'] = self.favorite_gameid 560 visible_buttons = [b for b in self.toolbar_vars 561 if self.toolbar_vars[b]] 562 config['general']['visible_buttons'] = visible_buttons 563 if 'none' in config['general']['solver_presets']: 564 config['general']['solver_presets'].remove('none') 565 566 # sound_samples 567 config['sound_samples'] = self.sound_samples 568 569 # fonts 570 for key, val in self.fonts.items(): 571 if key == 'default': 572 continue 573 if val is None: 574 continue 575 config['fonts'][key] = val 576 577 # colors 578 config['colors'] = self.colors 579 580 # timeouts 581 config['timeouts'] = self.timeouts 582 583 # cardsets 584 for key, val in self.cardset.items(): 585 config['cardsets'][str(key)] = val 586 for key in ('scale_cards', 'scale_x', 'scale_y', 587 'auto_scale', 'spread_stacks', 588 'preserve_aspect_ratio'): 589 config['cardsets'][key] = getattr(self, key) 590 591 # games_geometry 592 config['games_geometry'].clear() 593 for key, val in self.games_geometry.items(): 594 config['games_geometry'][str(key)] = val 595 config['general']['game_geometry'] = self.game_geometry 596 597 # offsets 598 for key, val in self.offsets.items(): 599 config['offsets'][key] = val 600 601 config.write() 602 # config.write(sys.stdout); print 603 604 def _getOption(self, section, key, t): 605 config = self._config 606 try: 607 if config[section][key] is None: 608 # invalid value 609 return None 610 if t == 'bool': 611 val = config[section].as_bool(key) 612 elif t == 'int': 613 val = config[section].as_int(key) 614 elif t == 'float': 615 val = config[section].as_float(key) 616 elif t == 'list': 617 val = config[section][key] 618 assert isinstance(val, (list, tuple)) 619 else: # str 620 val = config[section][key] 621 except KeyError: 622 val = None 623 except Exception: 624 print_err('load option error: %s: %s' % (section, key)) 625 traceback.print_exc() 626 val = None 627 return val 628 629 def load(self, filename): 630 631 # create ConfigObj instance 632 try: 633 config = configobj.ConfigObj(filename, 634 configspec=configspec, 635 encoding=self._config_encoding) 636 except configobj.ParseError: 637 traceback.print_exc() 638 config = configobj.ConfigObj(configspec=configspec, 639 encoding=self._config_encoding) 640 self._config = config 641 642 # create sections 643 for section in ( 644 'general', 645 'sound_samples', 646 'fonts', 647 'colors', 648 'timeouts', 649 'cardsets', 650 'games_geometry', 651 'offsets', 652 ): 653 if section not in config: 654 config[section] = {} 655 656 # add initial comment 657 if not os.path.exists(filename): 658 config.initial_comment = ['-*- coding: %s -*-' % 659 self._config_encoding] 660 return 661 662 # validation 663 vdt = validate.Validator() 664 res = config.validate(vdt) 665 # from pprint import pprint; pprint(res) 666 if isinstance(res, dict): 667 for section, data in res.items(): 668 if data is True: 669 continue 670 for key, value in data.items(): 671 if value is False: 672 print_err('config file: validation error: ' 673 'section: "%s", key: "%s"' % (section, key)) 674 config[section][key] = None 675 676 # general 677 for key, t in self.GENERAL_OPTIONS: 678 val = self._getOption('general', key, t) 679 if val == 'None': 680 setattr(self, key, None) 681 elif val is not None: 682 setattr(self, key, val) 683 684 pysollib.settings.TRANSLATE_GAME_NAMES = self.translate_game_names 685 686 recent_gameid = self._getOption('general', 'recent_gameid', 'list') 687 if recent_gameid is not None: 688 try: 689 self.recent_gameid = [int(i) for i in recent_gameid] 690 except Exception: 691 traceback.print_exc() 692 693 favorite_gameid = self._getOption('general', 'favorite_gameid', 'list') 694 if favorite_gameid is not None: 695 try: 696 self.favorite_gameid = [int(i) for i in favorite_gameid] 697 except Exception: 698 traceback.print_exc() 699 700 visible_buttons = self._getOption('general', 'visible_buttons', 'list') 701 if visible_buttons is not None: 702 for key in TOOLBAR_BUTTONS: 703 self.toolbar_vars[key] = (key in visible_buttons) 704 705 myGettext.language = self.language 706 707 # solver 708 solver_presets = self._getOption('general', 'solver_presets', 'list') 709 if solver_presets is not None: 710 if 'none' not in solver_presets: 711 solver_presets.insert(0, 'none') 712 self.solver_presets = solver_presets 713 714 # sound_samples 715 for key in self.sound_samples: 716 val = self._getOption('sound_samples', key, 'bool') 717 if val is not None: 718 self.sound_samples[key] = val 719 720 # fonts 721 for key in self.fonts: 722 if key == 'default': 723 continue 724 val = self._getOption('fonts', key, 'str') 725 if val is not None: 726 try: 727 val[1] = int(val[1]) 728 except Exception: 729 traceback.print_exc() 730 else: 731 val = tuple(val) 732 self.fonts[key] = val 733 734 # colors 735 for key in self.colors: 736 val = self._getOption('colors', key, 'str') 737 if val is not None: 738 self.colors[key] = val 739 740 # timeouts 741 for key in self.timeouts: 742 val = self._getOption('timeouts', key, 'float') 743 if val is not None: 744 self.timeouts[key] = val 745 746 # cardsets 747 for key in self.cardset: 748 val = self._getOption('cardsets', str(key), 'list') 749 if val is not None: 750 try: 751 self.cardset[int(key)] = val 752 except Exception: 753 traceback.print_exc() 754 for key, t in (('scale_cards', 'bool'), 755 ('scale_x', 'float'), 756 ('scale_y', 'float'), 757 ('auto_scale', 'bool'), 758 ('spread_stacks', 'bool'), 759 ('preserve_aspect_ratio', 'bool')): 760 val = self._getOption('cardsets', key, t) 761 if val is not None: 762 setattr(self, key, val) 763 764 # games_geometry 765 for key, val in config['games_geometry'].items(): 766 try: 767 val = [int(i) for i in val] 768 assert len(val) == 2 769 self.games_geometry[int(key)] = val 770 except Exception: 771 traceback.print_exc() 772 game_geometry = self._getOption('general', 'game_geometry', 'list') 773 if game_geometry is not None: 774 try: 775 self.game_geometry = tuple(int(i) for i in game_geometry) 776 except Exception: 777 traceback.print_exc() 778 779 # cards offsets 780 for key, val in config['offsets'].items(): 781 try: 782 val = [int(i) for i in val] 783 assert len(val) == 2 784 self.offsets[key] = val 785 except Exception: 786 traceback.print_exc() 787 788 # mouse buttons swap 789 def _positive(button): 790 return max([button, 1]) 791 _global_settings['mouse_button1'] = _positive(self.mouse_button1) 792 _global_settings['mouse_button2'] = _positive(self.mouse_button2) 793 _global_settings['mouse_button3'] = _positive(self.mouse_button3) 794 795 def calcCustomMouseButtonsBinding(self, binding_format): 796 """docstring for calcCustomMouseButtonsBinding""" 797 def _positive(button): 798 return max([button, 1]) 799 return binding_format.format( 800 mouse_button1=_positive(self.mouse_button1), 801 mouse_button2=_positive(self.mouse_button2), 802 mouse_button3=_positive(self.mouse_button3), 803 ) 804