From 9e2fedb9151f16147dbdf7b1c77f5780d6e84875 Mon Sep 17 00:00:00 2001 From: skomoroh Date: Wed, 14 Jun 2006 21:19:47 +0000 Subject: [PATCH] - new option: `save games geometry' - 5 new games: `double russian solitaire', `zodiac', `spike', `phantom blockade', `moving left' - tkhtmlviewer: highlight visited urls git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@8 efabe8c0-fbe8-4139-b769-b5e6d273206e --- po/games.pot | 2 +- po/pysol.pot | 4 +- po/ru_games.po | 2 +- po/ru_pysol.po | 24 +++--- pysollib/actions.py | 8 +- pysollib/app.py | 50 ++++++------ pysollib/game.py | 13 ++- pysollib/games/__init__.py | 1 + pysollib/games/bristol.py | 55 ++++++++++--- pysollib/games/gypsy.py | 23 ++++++ pysollib/games/klondike.py | 23 ++++++ pysollib/games/spider.py | 6 ++ pysollib/games/yukon.py | 18 ++++- pysollib/games/zodiac.py | 126 ++++++++++++++++++++++++++++++ pysollib/tk/menubar.py | 17 +++- pysollib/tk/soundoptionsdialog.py | 52 ++++++------ pysollib/tk/tkhtml.py | 53 ++++++++----- pysollib/tk/tktree.py | 3 +- pysollib/tk/tkwidget.py | 45 +++++------ 19 files changed, 386 insertions(+), 139 deletions(-) create mode 100644 pysollib/games/zodiac.py diff --git a/po/games.pot b/po/games.pot index b7c609f4..a91b4ca4 100644 --- a/po/games.pot +++ b/po/games.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PySol 0.0.1\n" -"POT-Creation-Date: Sun Jun 11 00:30:03 2006\n" +"POT-Creation-Date: Sun Jun 11 10:16:06 2006\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/po/pysol.pot b/po/pysol.pot index 62f17f65..46e8d15f 100644 --- a/po/pysol.pot +++ b/po/pysol.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: Sun Jun 11 00:29:57 2006\n" +"POT-Creation-Date: Sun Jun 11 10:16:01 2006\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2142,7 +2142,7 @@ msgid "Show hint arrow (in Shisen-Sho games)" msgstr "" #: pysollib/tk/menubar.py:341 -msgid "&Sound" +msgid "&Sound..." msgstr "" #: pysollib/tk/menubar.py:349 diff --git a/po/ru_games.po b/po/ru_games.po index 8c5b3024..84aee5c1 100644 --- a/po/ru_games.po +++ b/po/ru_games.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PySol 0.0.1\n" -"POT-Creation-Date: Sun Jun 11 00:30:03 2006\n" +"POT-Creation-Date: Sun Jun 11 10:16:06 2006\n" "PO-Revision-Date: 2006-06-10 11:07+0400\n" "Last-Translator: Скоморох \n" "Language-Team: Russian \n" diff --git a/po/ru_pysol.po b/po/ru_pysol.po index cdafff22..cb0eef34 100644 --- a/po/ru_pysol.po +++ b/po/ru_pysol.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: PySol 0.0.1\n" -"POT-Creation-Date: Sun Jun 11 00:29:57 2006\n" -"PO-Revision-Date: 2006-06-11 00:32+0400\n" +"POT-Creation-Date: Sun Jun 11 10:16:01 2006\n" +"PO-Revision-Date: 2006-06-12 15:31+0400\n" "Last-Translator: Скоморох \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" @@ -2280,8 +2280,8 @@ msgid "Show hint arrow (in Shisen-Sho games)" msgstr "Показывать стрелку (в Шисен-Сё)" #: pysollib/tk/menubar.py:341 -msgid "&Sound" -msgstr "&Звук" +msgid "&Sound..." +msgstr "&Звук..." #: pysollib/tk/menubar.py:349 msgid "Cards&et..." @@ -2289,7 +2289,7 @@ msgstr "Коло&да..." #: pysollib/tk/menubar.py:350 msgid "Table t&ile..." -msgstr "&Игровой стол..." +msgstr "Игровой &стол..." #: pysollib/tk/menubar.py:352 msgid "Card &background" @@ -2313,7 +2313,7 @@ msgstr "&Негативные контуры карты" #: pysollib/tk/menubar.py:357 msgid "A&nimations" -msgstr "&Анимация" +msgstr "Анимаци&я" #: pysollib/tk/menubar.py:358 msgid "&None" @@ -2353,11 +2353,11 @@ msgstr "Тайма&уты..." #: pysollib/tk/menubar.py:369 msgid "&Toolbar" -msgstr "Панель &инструментов" +msgstr "Панель и&нструментов" #: pysollib/tk/menubar.py:371 msgid "Stat&usbar" -msgstr "Панель состояния" +msgstr "Панель с&остояния" #: pysollib/tk/menubar.py:372 msgid "Show &statusbar" @@ -2373,11 +2373,11 @@ msgstr "Показывать панель помощи" #: pysollib/tk/menubar.py:375 msgid "&Demo logo" -msgstr "&Демо лого" +msgstr "Д&емо лого" #: pysollib/tk/menubar.py:376 msgid "Startup splash sc&reen" -msgstr "Окно &запуска" +msgstr "О&кно запуска" #: pysollib/tk/menubar.py:380 msgid "&Help" @@ -2809,7 +2809,7 @@ msgstr "Все фоновые изображения" #: pysollib/tk/selecttile.py:158 msgid "&Solid color..." -msgstr "&Монотонный цвет..." +msgstr "М&онотонный цвет..." #: pysollib/tk/soundoptionsdialog.py:111 msgid "Sound enabled" @@ -2837,7 +2837,7 @@ msgstr "&Применить" #: pysollib/tk/soundoptionsdialog.py:169 pysollib/tk/soundoptionsdialog.py:171 msgid "&Mixer..." -msgstr "&Миксер..." +msgstr "Ми&ксер..." #: pysollib/tk/soundoptionsdialog.py:220 msgid "Sound preferences info" diff --git a/pysollib/actions.py b/pysollib/actions.py index f3df4f29..59ba57d1 100644 --- a/pysollib/actions.py +++ b/pysollib/actions.py @@ -130,6 +130,7 @@ class PysolMenubarActions: statusbar = BooleanVar(), num_cards = BooleanVar(), helpbar = BooleanVar(), + save_games_geometry = BooleanVar(), splashscreen = BooleanVar(), demo_logo = BooleanVar(), sticky_mouse = BooleanVar(), @@ -180,6 +181,7 @@ class PysolMenubarActions: tkopt.statusbar.set(opt.statusbar) tkopt.num_cards.set(opt.num_cards) tkopt.helpbar.set(opt.helpbar) + tkopt.save_games_geometry.set(opt.save_games_geometry) tkopt.demo_logo.set(opt.demo_logo) tkopt.splashscreen.set(opt.splashscreen) tkopt.sticky_mouse.set(opt.sticky_mouse) @@ -743,19 +745,19 @@ class PysolMenubarActions: if self._cancelDrag(): return if self.app.opt.hint: if self.game.showHint(0, self.app.opt.hint_sleep): - self.game.stats.hints = self.game.stats.hints + 1 + self.game.stats.hints += 1 def mHint1(self, *args): if self._cancelDrag(): return if self.app.opt.hint: if self.game.showHint(1, self.app.opt.hint_sleep): - self.game.stats.hints = self.game.stats.hints + 1 + self.game.stats.hints += 1 def mHighlightPiles(self, *args): if self._cancelDrag(): return if self.app.opt.highlight_piles: if self.game.highlightPiles(self.app.opt.highlight_piles_sleep): - self.game.stats.highlight_piles = self.game.stats.highlight_piles + 1 + self.game.stats.highlight_piles += 1 def mDemo(self, *args): if self._cancelDrag(): return diff --git a/pysollib/app.py b/pysollib/app.py index bd41e1eb..ea1c99b9 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -129,14 +129,14 @@ class Options: 'autopilotlost' : True, 'autopilotwon' : True, 'deal' : True, - 'deal01' : True, - 'deal02' : True, - 'deal04' : True, - 'deal08' : True, + #'deal01' : True, + #'deal02' : True, + #'deal04' : True, + #'deal08' : True, 'dealwaste' : True, 'droppair' : True, 'drop' : True, - 'extra' : True, + #'extra' : True, 'flip' : True, 'move' : True, 'nomove' : True, @@ -184,6 +184,7 @@ class Options: self.highlight_cards_sleep = 1.0 self.highlight_samerank_sleep = 1.0 # additional startup information + self.num_recent_games = 15 self.recent_gameid = [] self.favorite_gameid = [] self.last_gameid = 0 # last game played @@ -191,6 +192,8 @@ class Options: #self.last_save_dir = None # last directory for load/save self.game_holded = 0 self.wm_maximized = 0 + self.save_games_geometry = False + self.games_geometry = {} # saved games geometry (gameid: (width, height)) # self.splashscreen = True self.sticky_mouse = False @@ -668,7 +671,13 @@ class Application: self.game._saveGame(self.fn.holdgame) self.opt.game_holded = self.game.id except: + traceback.print_exc() pass + # save game geometry + self.wm_save_state() + if self.opt.save_games_geometry and not self.opt.wm_maximized: + geom = (self.canvas.winfo_width(), self.canvas.winfo_height()) + self.opt.games_geometry[self.game.id] = geom self.freeGame() # if self.nextgame.id <= 0: @@ -681,35 +690,25 @@ class Application: finally: # update options self.opt.last_gameid = id -## if self.debug: -## self.wm_save_state() -## # save options -## self.saveOptions() -## # save statistics -## self.saveStatistics() -## # save comments -## self.saveComments() -## # shut down audio -## self.audio.destroy() -## else: - try: self.wm_save_state() - except: - pass # save options try: self.saveOptions() except: + traceback.print_exc() pass # save statistics try: self.saveStatistics() except: + traceback.print_exc() pass # save comments try: self.saveComments() except: + traceback.print_exc() pass # shut down audio try: self.audio.destroy() except: + traceback.print_exc() pass @@ -741,7 +740,7 @@ class Application: if id in self.opt.recent_gameid: self.opt.recent_gameid.remove(id) self.opt.recent_gameid.insert(0, id) - del self.opt.recent_gameid[15:] + del self.opt.recent_gameid[self.opt.num_recent_games:] self.menubar.updateRecentGamesMenu(self.opt.recent_gameid) self.menubar.updateFavoriteGamesMenu() # delete intro progress bar @@ -789,7 +788,6 @@ class Application: self.toolbar.connectGame(None, None) self.menubar.connectGame(None) # clean up the canvas - unbind_destroy(self.canvas) self.canvas.deleteAllItems() self.canvas.update_idletasks() # destruct the game @@ -808,7 +806,7 @@ class Application: if self.top: s = self.top.wm_state() ##print "wm_save_state", s - if s == "zoomed": + if s == "zoomed": # Windows only self.opt.wm_maximized = 1 elif s == "normal": self.opt.wm_maximized = 0 @@ -842,7 +840,7 @@ class Application: for f in ("demo01", "demo02", "demo03", "demo04", "demo05",): self.gimages.demo.append(self.dataloader.findImage(f, dir)) dir = os.path.join("images", "pause") - for f in ("pause01", "pause02",): + for f in ("pause01", "pause02", "pause03",): self.gimages.pause.append(self.dataloader.findImage(f, dir)) ##dir = os.path.join("images", "stats") ##for f in ("barchart",): @@ -950,8 +948,7 @@ class Application: self.opt.cardset[gi.category] = (cs.name, cs.backname) if update & 8: self.opt.cardset[(1, gi.id)] = (cs.name, cs.backname) - #from pprint import pprint - #pprint(self.opt.cardset) + #from pprint import pprint; pprint(self.opt.cardset) def loadCardset(self, cs, id=0, update=7, progress=None): #print 'loadCardset', cs.ident @@ -1146,8 +1143,7 @@ Please select a %s type %s. return opt = unpickle(self.fn.opt) if opt: - ##import pprint - ##pprint.pprint(opt.__dict__) + ##import pprint; pprint.pprint(opt.__dict__) #cardset = self.opt.cardset #cardset.update(opt.cardset) self.opt.__dict__.update(opt.__dict__) diff --git a/pysollib/game.py b/pysollib/game.py index 28bd6deb..c33f3812 100644 --- a/pysollib/game.py +++ b/pysollib/game.py @@ -184,12 +184,18 @@ class Game: if not self.cards: self.cards = self.createCards(progress=self.app.intro.progress) self.initBindings() - self.top.bind('', self.top._sleepEvent) - self.top.bind('<3>', self.top._sleepEvent) + ##self.top.bind('', self.top._sleepEvent) + ##self.top.bind('<3>', self.top._sleepEvent) ##print timer # update display properties self.top.wm_geometry("") # cancel user-specified geometry self.canvas.setInitialSize(self.width, self.height) + # restore game geometry + if self.app.opt.save_games_geometry: + w, h = self.app.opt.games_geometry.get(self.id, (0, 0)) + w, h = max(w, self.width), max(h, self.height) + self.canvas.config(width=w, height=h) + # self.stats.update_time = time.time() self.busy = old_busy ##print timer @@ -784,7 +790,8 @@ class Game: # def playSample(self, name, priority=0, loop=0): - if not self.app.opt.sound_samples[name]: + if self.app.opt.sound_samples.has_key(name) and \ + not self.app.opt.sound_samples[name]: return 0 ##print "playSample:", name, priority, loop if self.app.audio: diff --git a/pysollib/games/__init__.py b/pysollib/games/__init__.py index e1de4c40..d5b56af9 100644 --- a/pysollib/games/__init__.py +++ b/pysollib/games/__init__.py @@ -57,3 +57,4 @@ import unionsquare import wavemotion import windmill import yukon +import zodiac diff --git a/pysollib/games/bristol.py b/pysollib/games/bristol.py index e98f6377..528c4bdf 100644 --- a/pysollib/games/bristol.py +++ b/pysollib/games/bristol.py @@ -187,22 +187,25 @@ class Dover_RowStack(RK_RowStack): class Dover(Bristol): Talon_Class = Bristol_Talon - Foundation_Class = SS_FoundationStack - RowStack_Class = Dover_RowStack + Foundation_Class = StackWrapper(SS_FoundationStack, max_move=0) + RowStack_Class = StackWrapper(Dover_RowStack, base_rank=NO_RANK, max_move=1) ReserveStack_Class = StackWrapper(ReserveStack, max_accept=0, max_cards=UNLIMITED_CARDS) - def createGame(self, text=False): + def createGame(self, rows=8, text=False): # create layout l, s = Layout(self), self.s # set window - self.setSize(2*l.XM+9*l.XS, l.YM+20+5*l.YS) + max_rows = max(rows, self.gameinfo.decks*4) + w, h = 2*l.XM+l.XS+max_rows*l.XS, l.YM+20+5*l.YS + self.setSize(w, h) # create stacks - x, y, = l.XM+l.XM+l.XS, l.YM - for i in range(8): - s.foundations.append(self.Foundation_Class(x, y, self, suit=i/2, max_move=0)) - x += l.XS + x, y, = 2*l.XM+l.XS+l.XS*(max_rows-self.gameinfo.decks*4), l.YM + for j in range(self.gameinfo.decks): + for i in range(4): + s.foundations.append(self.Foundation_Class(x, y, self, suit=i)) + x += l.XS if text: x, y = l.XM+8*l.XS, l.YM tx, ty, ta, tf = l.getTextAttr(None, "s") @@ -210,12 +213,12 @@ class Dover(Bristol): font = self.app.getFont("canvas_default") self.texts.info = MfxCanvasText(self.canvas, tx, ty, anchor=ta, font=font) - x, y = l.XM+l.XM, l.YM+l.YS + x, y = 2*l.XM+(max_rows-rows)*l.XS, l.YM+l.YS if text: y += 20 - for i in range(8): + for i in range(rows): x += l.XS - stack = self.RowStack_Class(x, y, self, base_rank=NO_RANK, max_move=1) + stack = self.RowStack_Class(x, y, self) stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, l.YOFFSET s.rows.append(stack) x, y, = l.XM, l.YM @@ -262,9 +265,9 @@ class NewYork_RowStack(AC_RowStack): class NewYork(Dover): - Foundation_Class = StackWrapper(SS_FoundationStack, mod=13) + Foundation_Class = StackWrapper(SS_FoundationStack, mod=13, max_move=0) Talon_Class = NewYork_Talon - RowStack_Class = StackWrapper(NewYork_RowStack, base_rank=ANY_RANK, mod=13) + RowStack_Class = StackWrapper(NewYork_RowStack, base_rank=ANY_RANK, mod=13, max_move=1) ReserveStack_Class = StackWrapper(NewYork_ReserveStack, max_accept=1, max_cards=UNLIMITED_CARDS, mod=13) def createGame(self): @@ -315,6 +318,30 @@ class NewYork(Dover): p.dump(self.base_card.id) +# /*********************************************************************** +# // Spike +# ************************************************************************/ + +class Spike(Dover): + + Foundation_Class = SS_FoundationStack + RowStack_Class = KingAC_RowStack + + def createGame(self): + Dover.createGame(self, rows=7) + + def startGame(self): + for i in range(1, 7): + self.s.talon.dealRow(rows=self.s.rows[i:], flip=0, frames=0) + self.startDealSample() + self.s.talon.dealRow() + self.s.talon.dealCards() + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return (card1.color != card2.color and + abs(card1.rank-card2.rank) == 1) + + # register the game registerGame(GameInfo(42, Bristol, "Bristol", GI.GT_FAN_TYPE, 1, 0)) @@ -324,4 +351,6 @@ registerGame(GameInfo(266, Dover, "Dover", GI.GT_FAN_TYPE, 2, 0)) registerGame(GameInfo(425, NewYork, "New York", GI.GT_FAN_TYPE, 2, 0)) +registerGame(GameInfo(468, Spike, "Spike", + GI.GT_KLONDIKE, 1, 0)) diff --git a/pysollib/games/gypsy.py b/pysollib/games/gypsy.py index cf6ff610..0d382683 100644 --- a/pysollib/games/gypsy.py +++ b/pysollib/games/gypsy.py @@ -325,6 +325,8 @@ class LexingtonHarp(MilliganHarp): GAME_VERSION = 2 RowStack_Class = Yukon_AC_RowStack Hint_Class = YukonType_Hint + def getHighlightPilesStacks(self): + return () class Brunswick(LexingtonHarp): @@ -344,6 +346,7 @@ class Griffon(Mississippi): # /*********************************************************************** # // Blockade +# // Phantom Blockade # ************************************************************************/ class Blockade(Gypsy): @@ -364,6 +367,24 @@ class Blockade(Gypsy): self.s.talon.moveMove(1, stack) self.leaveState(old_state) + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return (card1.suit == card2.suit and + abs(card1.rank-card2.rank) == 1) + + +class PhantomBlockade(Gypsy): + Layout_Method = Layout.klondikeLayout + RowStack_Class = KingAC_RowStack + + def createGame(self): + Gypsy.createGame(self, rows=13, playcards=24) + + def startGame(self): + self.s.talon.dealRow(frames=0) + self.s.talon.dealRow(frames=0) + self.startDealSample() + self.s.talon.dealRow() + # /*********************************************************************** # // Cone @@ -507,4 +528,6 @@ registerGame(GameInfo(412, Cone, "Cone", GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(463, Surprise, "Surprise", GI.GT_GYPSY, 2, 0)) +registerGame(GameInfo(469, PhantomBlockade, "Phantom Blockade", + GI.GT_GYPSY, 2, 0)) diff --git a/pysollib/games/klondike.py b/pysollib/games/klondike.py index feac717c..cc42ebd1 100644 --- a/pysollib/games/klondike.py +++ b/pysollib/games/klondike.py @@ -950,6 +950,27 @@ class SevenDevils(Klondike): self.s.talon.dealRow(rows=self.s.reserves) +# /*********************************************************************** +# // Moving Left +# ************************************************************************/ + +class MovingLeft(Klondike): + + def createGame(self): + Klondike.createGame(self, max_rounds=1, rows=10, playcards=24) + + def fillStack(self, stack): + if not stack.cards: + old_state = self.enterState(self.S_FILL) + if stack in self.s.rows: + i = list(self.s.rows).index(stack) + if i < 9: + from_stack = self.s.rows[i+1] + pile = from_stack.getPile() + if pile: + from_stack.moveMove(len(pile), stack) + self.leaveState(old_state) + # register the game registerGame(GameInfo(2, Klondike, "Klondike", @@ -1042,4 +1063,6 @@ registerGame(GameInfo(452, DoubleEasthaven, "Double Easthaven", GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(453, TripleEasthaven, "Triple Easthaven", GI.GT_GYPSY, 3, 0)) +registerGame(GameInfo(470, MovingLeft, "Moving Left", + GI.GT_KLONDIKE, 2, 0)) diff --git a/pysollib/games/spider.py b/pysollib/games/spider.py index d3f6a541..6e083183 100644 --- a/pysollib/games/spider.py +++ b/pysollib/games/spider.py @@ -640,6 +640,9 @@ class Chelicera(Game): for i in range(3): self.s.talon.dealRow(rows=self.s.rows[4:]) + def getHighlightPilesStacks(self): + return () + def shallHighlightMatch(self, stack1, card1, stack2, card2): return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1 @@ -793,6 +796,9 @@ class Applegate(Game): return False return True + def getHighlightPilesStacks(self): + return () + def shallHighlightMatch(self, stack1, card1, stack2, card2): return (card1.suit == card2.suit and ((card1.rank + 1) % stack1.cap.mod == card2.rank or diff --git a/pysollib/games/yukon.py b/pysollib/games/yukon.py index ac365573..949425cd 100644 --- a/pysollib/games/yukon.py +++ b/pysollib/games/yukon.py @@ -347,21 +347,31 @@ Diamond: 4 8 Q 3 7 J 2 6 T A 5 9 K''')) # /*********************************************************************** # // Double Yukon +# // Double Russian Solitaire # ************************************************************************/ class DoubleYukon(Yukon): def createGame(self): Yukon.createGame(self, rows=10) def startGame(self): - for i in range(1, len(self.s.rows)): + for i in range(1, len(self.s.rows)-1): self.s.talon.dealRow(rows=self.s.rows[i:], flip=0, frames=0) + #self.s.talon.dealRow(rows=self.s.rows, flip=0, frames=0) for i in range(5): - self.s.talon.dealRow(rows=self.s.rows, flip=1, frames=0) + self.s.talon.dealRow(flip=1, frames=0) self.startDealSample() - self.s.talon.dealRow(rows=self.s.rows[:-1]) + self.s.talon.dealRow() assert len(self.s.talon.cards) == 0 +class DoubleRussianSolitaire(DoubleYukon): + RowStack_Class = StackWrapper(Yukon_SS_RowStack, base_rank=KING) + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return (card1.suit == card2.suit and + (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank)) + + # /*********************************************************************** # // Triple Yukon # ************************************************************************/ @@ -543,3 +553,5 @@ registerGame(GameInfo(450, RawPrawn, "Raw Prawn", GI.GT_YUKON, 1, 0)) registerGame(GameInfo(456, BimBom, "Bim Bom", GI.GT_YUKON | GI.GT_ORIGINAL, 2, 0)) +registerGame(GameInfo(466, DoubleRussianSolitaire, "Double Russian Solitaire", + GI.GT_YUKON | GI.GT_ORIGINAL, 2, 0)) diff --git a/pysollib/games/zodiac.py b/pysollib/games/zodiac.py new file mode 100644 index 00000000..aed43fbb --- /dev/null +++ b/pysollib/games/zodiac.py @@ -0,0 +1,126 @@ +##---------------------------------------------------------------------------## +## +## PySol -- a Python Solitaire game +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; see the file COPYING. +## If not, write to the Free Software Foundation, Inc., +## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +## +##---------------------------------------------------------------------------## + +__all__ = [] + +# imports +import sys + +# PySol imports +from pysollib.gamedb import registerGame, GameInfo, GI +from pysollib.util import * +from pysollib.mfxutil import kwdefault +from pysollib.stack import * +from pysollib.game import Game +from pysollib.layout import Layout +from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint + +# /*********************************************************************** +# // Zodiac +# ************************************************************************/ + +class Zodiac_Foundation(SS_FoundationStack): + def acceptsCards(self, from_stack, cards): + if not SS_FoundationStack.acceptsCards(self, from_stack, cards): + return False + if self.game.s.waste.cards or self.game.s.talon.cards: + return False + return True + + +class Zodiac_RowStack(UD_SS_RowStack): + def acceptsCards(self, from_stack, cards): + if not UD_SS_RowStack.acceptsCards(self, from_stack, cards): + return False + if from_stack in self.game.s.rows: + return False + return True + +class Zodiac_ReserveStack(ReserveStack): + def acceptsCards(self, from_stack, cards): + if not ReserveStack.acceptsCards(self, from_stack, cards): + return False + if from_stack in self.game.s.rows: + return False + return True + + + +class Zodiac(Game): + + def createGame(self): + + # create layout + l, s = Layout(self), self.s + + # set window + w, h = l.XM+12*l.XS, l.YM+5*l.YS + self.setSize(w, h) + + # create stacks + x = l.XM + for i in range(12): + for y in (l.YM, l.YM+4*l.YS): + stack = Zodiac_RowStack(x, y, self, base_rank=NO_RANK) + s.rows.append(stack) + stack.CARD_XOFFSET, stack.CARD_YOFFSET = 0, 0 + x += l.XS + + x = l.XM+4*l.XS + for i in range(4): + y = l.YM+l.YS + s.foundations.append(Zodiac_Foundation(x, y, self, suit=i)) + y += 2*l.YS + s.foundations.append(Zodiac_Foundation(x, y, self, suit=i, + base_rank=KING, dir=-1)) + x += l.XS + + x, y = l.XM+2*l.XS, l.YM+2*l.YS + for i in range(8): + s.reserves.append(Zodiac_ReserveStack(x, y, self)) + x += l.XS + + x, y = l.XM+l.XS, l.YM+l.YS + s.talon = WasteTalonStack(x, y, self, + max_rounds=UNLIMITED_REDEALS) + l.createText(s.talon, 'sw') + x += l.XS + s.waste = WasteStack(x, y, self) + l.createText(s.waste, 'se') + + # define stack-groups + l.defaultStackGroups() + + + def startGame(self): + self.startDealSample() + self.s.talon.dealRow(rows=self.s.reserves) + self.s.talon.dealRow() + self.s.talon.dealCards() + + + def shallHighlightMatch(self, stack1, card1, stack2, card2): + return card1.suit == card2.suit and abs(card1.rank-card2.rank) == 1 + + +# register the game +registerGame(GameInfo(467, Zodiac, "Zodiac", + GI.GT_2DECK_TYPE, 2, -1)) diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index a38b69a3..cd7b277a 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -338,7 +338,7 @@ class PysolMenubar(PysolMenubarActions): submenu.add_checkbutton(label=n_("Show removed tiles (in Mahjongg games)"), variable=self.tkopt.mahjongg_show_removed, command=self.mOptMahjonggShowRemoved) submenu.add_checkbutton(label=n_("Show hint arrow (in Shisen-Sho games)"), variable=self.tkopt.shisen_show_hint, command=self.mOptShisenShowHint) menu.add_separator() - label = n_("&Sound") + label = n_("&Sound...") if self.app.audio.audiodev is None: menu.add_checkbutton(label=label, variable=self.tkopt.sound, command=self.mOptSoundDialog, state=Tkinter.DISABLED) else: @@ -372,6 +372,7 @@ class PysolMenubar(PysolMenubarActions): submenu.add_checkbutton(label=n_("Show &statusbar"), variable=self.tkopt.statusbar, command=self.mOptStatusbar) submenu.add_checkbutton(label=n_("Show &number of cards"), variable=self.tkopt.num_cards, command=self.mOptNumCards) submenu.add_checkbutton(label=n_("Show &help bar"), variable=self.tkopt.helpbar, command=self.mOptHelpbar) + menu.add_checkbutton(label=n_("Save games &geometry"), variable=self.tkopt.save_games_geometry, command=self.mOptSaveGamesGeometry) menu.add_checkbutton(label=n_("&Demo logo"), variable=self.tkopt.demo_logo, command=self.mOptDemoLogo) menu.add_checkbutton(label=n_("Startup splash sc&reen"), variable=self.tkopt.splashscreen, command=self.mOptSplashscreen) ### menu.add_separator() @@ -866,6 +867,7 @@ class PysolMenubar(PysolMenubarActions): self._cancelDrag() self.game.endGame(bookmark=1) self.game.quitGame(bookmark=1) + self.app.opt.games_geometry = {} # clear saved games geometry def _mOptCardback(self, index): if self._cancelDrag(break_pause=False): return @@ -958,7 +960,8 @@ class PysolMenubar(PysolMenubarActions): if not self.app.statusbar: return side = self.tkopt.statusbar.get() self.app.opt.statusbar = side - if self.app.statusbar.show(side): + resize = not self.app.opt.save_games_geometry + if self.app.statusbar.show(side, resize=resize): self.top.update_idletasks() def mOptNumCards(self, *event): @@ -970,9 +973,14 @@ class PysolMenubar(PysolMenubarActions): if not self.app.helpbar: return show = self.tkopt.helpbar.get() self.app.opt.helpbar = show - if self.app.helpbar.show(show): + resize = not self.app.opt.save_games_geometry + if self.app.helpbar.show(show, resize=resize): self.top.update_idletasks() + def mOptSaveGamesGeometry(self, *event): + if self._cancelDrag(break_pause=False): return + self.app.opt.save_games_geometry = self.tkopt.save_games_geometry.get() + def mOptDemoLogo(self, *event): if self._cancelDrag(break_pause=False): return self.app.opt.demo_logo = self.tkopt.demo_logo.get() @@ -1002,7 +1010,8 @@ class PysolMenubar(PysolMenubarActions): if self._cancelDrag(break_pause=False): return self.app.opt.toolbar = side self.tkopt.toolbar.set(side) # update radiobutton - if self.app.toolbar.show(side): + resize = not self.app.opt.save_games_geometry + if self.app.toolbar.show(side, resize=resize): self.top.update_idletasks() def setToolbarSize(self, size): diff --git a/pysollib/tk/soundoptionsdialog.py b/pysollib/tk/soundoptionsdialog.py index f6adaaf8..b4cff8cf 100644 --- a/pysollib/tk/soundoptionsdialog.py +++ b/pysollib/tk/soundoptionsdialog.py @@ -74,31 +74,33 @@ class SoundOptionsDialog(MfxDialog): self.music_volume = IntVar() self.music_volume.set(app.opt.sound_music_volume) self.samples = [ - ('areyousure', 'AreYouSure', BooleanVar()), - ('autodrop', 'AutoDrop', BooleanVar()), - ('autoflip', 'AutoFlip', BooleanVar()), - ('autopilotlost', 'AutopilotLost', BooleanVar()), - ('autopilotwon', 'AutopilotWon', BooleanVar()), - ('deal', 'Deal', BooleanVar()), - #('deal01', 'Deal01', BooleanVar()), - #('deal02', 'Deal02', BooleanVar()), - #('deal04', 'Deal04', BooleanVar()), - #('deal08', 'Deal08', BooleanVar()), - ('dealwaste', 'DealWaste', BooleanVar()), - ('droppair', 'DropPair', BooleanVar()), - ('drop', 'Drop', BooleanVar()), - #('extra', 'Extra', BooleanVar()), - ('flip', 'Flip', BooleanVar()), - ('move', 'Move', BooleanVar()), - ('nomove', 'NoMove', BooleanVar()), - ('redo', 'Redo', BooleanVar()), - ('startdrag', 'StartDrag', BooleanVar()), - ('turnwaste', 'TurnWaste', BooleanVar()), - ('undo', 'Undo', BooleanVar()), - ('gamefinished', 'GameFinished', BooleanVar()), - ('gamelost', 'GameLost', BooleanVar()), - ('gameperfect', 'GamePerfect', BooleanVar()), - ('gamewon', 'GameWon', BooleanVar()), + ('areyousure', _('Are You Sure'), BooleanVar()), + + ('deal', _('Deal'), BooleanVar()), + ('dealwaste', _('Deal waste'), BooleanVar()), + + ('turnwaste', _('Turn waste'), BooleanVar()), + ('startdrag', _('Start drag'), BooleanVar()), + + ('drop', _('Drop'), BooleanVar()), + ('droppair', _('Drop pair'), BooleanVar()), + ('autodrop', _('Auto drop'), BooleanVar()), + + ('flip', _('Flip'), BooleanVar()), + ('autoflip', _('Auto flip'), BooleanVar()), + ('move', _('Move'), BooleanVar()), + ('nomove', _('No move'), BooleanVar()), + + ('undo', _('Undo'), BooleanVar()), + ('redo', _('Redo'), BooleanVar()), + + ('autopilotlost', _('Autopilot lost'), BooleanVar()), + ('autopilotwon', _('Autopilot won'), BooleanVar()), + + ('gamefinished', _('Game finished'), BooleanVar()), + ('gamelost', _('Game lost'), BooleanVar()), + ('gamewon', _('Game won'), BooleanVar()), + ('gameperfect', _('Perfect game'), BooleanVar()), ] # diff --git a/pysollib/tk/tkhtml.py b/pysollib/tk/tkhtml.py index 9e630012..b7466b2e 100644 --- a/pysollib/tk/tkhtml.py +++ b/pysollib/tk/tkhtml.py @@ -125,15 +125,15 @@ class tkHTMLWriter(formatter.DumbWriter): self.text.tag_bind(tag, "<1>", self.createCallback(url)) self.text.tag_bind(tag, "", lambda e: self.anchor_enter(url)) self.text.tag_bind(tag, "", self.anchor_leave) - self.text.tag_config(tag, foreground="blue", underline=1) + fg = 'blue' + u = self.viewer.normurl(url, with_protocol=False) + if u in self.viewer.visited_urls: + fg = '#303080' + self.text.tag_config(tag, foreground=fg, underline=1) self.anchor = None def anchor_enter(self, url): - for p in REMOTE_PROTOCOLS: - if url.startswith(p): - break - else: - url = 'file://'+self.viewer.basejoin(url) + url = self.viewer.normurl(url) self.viewer.statusbar.updateText(url=url) self.text.config(cursor=self.viewer.handcursor) @@ -219,6 +219,7 @@ class tkHTMLViewer: list = [], index = 0, ) + self.visited_urls = [] self.images = {} # need to keep a reference because of garbage collection self.defcursor = parent["cursor"] ##self.defcursor = 'xterm' @@ -247,7 +248,8 @@ class tkHTMLViewer: text_frame = Tkinter.Frame(parent) text_frame.grid(row=1, column=0, columnspan=4, sticky='nsew') self.text = Tkinter.Text(text_frame, - fg='black', bg='white', bd=0, + fg='black', bg='white', + bd=1, relief='sunken', cursor=self.defcursor, wrap='word', padx=20, pady=20) self.text.pack(side=Tkinter.LEFT, fill=Tkinter.BOTH, expand=1) @@ -287,24 +289,22 @@ class tkHTMLViewer: except: pass self.parent = None + def _yview(self, *args): + apply(self.text.yview, args, {}) + return 'break' + def page_up(self, *event): - self.text.yview_scroll(-1, "page") - return "break" + return self._yview('scroll', -1, 'page') def page_down(self, *event): - self.text.yview_scroll(1, "page") - return "break" + return self._yview('scroll', 1, 'page') def unit_up(self, *event): - self.text.yview_scroll(-1, "unit") - return "break" + return self._yview('scroll', -1, 'unit') def unit_down(self, *event): - self.text.yview_scroll(1, "unit") - return "break" + return self._yview('scroll', 1, 'unit') def scroll_top(self, *event): - self.text.yview_moveto(0) - return "break" + return self._yview('moveto', 0) def scroll_bottom(self, *event): - self.text.yview_moveto(1) - return "break" + return self._yview('moveto', 1) # locate a file relative to the current self.url def basejoin(self, url, baseurl=None, relpath=1): @@ -325,6 +325,18 @@ class tkHTMLViewer: url = os.path.normpath(url) return url + def normurl(self, url, with_protocol=True): + for p in REMOTE_PROTOCOLS: + if url.startswith(p): + break + else: + url = self.basejoin(url) + if with_protocol: + if os.name == 'nt': + url = url.replace('\\', '/') + url = 'file://'+url + return url + def openfile(self, url): if url[-1:] == "/" or os.path.isdir(url): url = os.path.join(url, "index.html") @@ -338,6 +350,7 @@ class tkHTMLViewer: if self.app and self.app.game: self.app.game.stopDemo() ##self.app.game._cancelDrag() + ##pass # ftp: and http: would work if we use urllib, but this widget is # far too limited to display anything but our documentation... @@ -418,6 +431,8 @@ to open the following URL: ##self.frame.config(cursor=self.defcursor) def addHistory(self, url, xview=0, yview=0): + if not url in self.visited_urls: + self.visited_urls.append(url) if self.history.index > 0: u, xv, yv = self.history.list[self.history.index-1] if cmp(u, url) == 0: diff --git a/pysollib/tk/tktree.py b/pysollib/tk/tktree.py index dd047d30..fe339fad 100644 --- a/pysollib/tk/tktree.py +++ b/pysollib/tk/tktree.py @@ -38,7 +38,7 @@ import os, string, types import Tkinter # Toolkit imports -from tkutil import bind, unbind_destroy +from tkutil import bind from tkwidget import MfxScrolledCanvas @@ -244,6 +244,7 @@ class MfxTreeInCanvas(MfxScrolledCanvas): def __init__(self, parent, rootnodes, **kw): bg = kw["bg"] = kw.get("bg") or parent.cget("bg") + kw['bd'] = 0 apply(MfxScrolledCanvas.__init__, (self, parent,), kw) # self.rootnodes = rootnodes diff --git a/pysollib/tk/tkwidget.py b/pysollib/tk/tkwidget.py index 85e7ed88..5d7400dd 100644 --- a/pysollib/tk/tkwidget.py +++ b/pysollib/tk/tkwidget.py @@ -420,7 +420,7 @@ class MfxTooltip: class MfxScrolledCanvas: def __init__(self, parent, hbar=2, vbar=2, **kw): bg = kw.get("bg", parent.cget("bg")) - kwdefault(kw, bg=bg, highlightthickness=0) + kwdefault(kw, bg=bg, highlightthickness=0, bd=1, relief='sunken') self.parent = parent self.createFrame(kw) self.canvas = None @@ -629,41 +629,36 @@ class MfxScrolledCanvas: self.vbar_show = show return 1 + def _xview(self, *args): + if self.hbar_show: apply(self.canvas.xview, args, {}) + return 'break' + def _yview(self, *args): + if self.vbar_show: apply(self.canvas.yview, args, {}) + return 'break' + def page_up(self, *event): - self.canvas.yview_scroll(-1, "page") - return "break" + return self._yview('scroll', -1, 'page') def page_down(self, *event): - self.canvas.yview_scroll(1, "page") - return "break" + return self._yview('scroll', 1, 'page') def unit_up(self, *event): - self.canvas.yview_scroll(-1, "unit") - return "break" + return self._yview('scroll', -1, 'unit') def unit_down(self, *event): - self.canvas.yview_scroll(1, "unit") - return "break" + return self._yview('scroll', 1, 'unit') def mouse_wheel_up(self, *event): - self.canvas.yview_scroll(-5, "unit") - return "break" + return self._yview('scroll', -5, 'unit') def mouse_wheel_down(self, *event): - self.canvas.yview_scroll(5, "unit") - return "break" + return self._yview('scroll', 5, 'unit') def page_left(self, *event): - self.canvas.xview_scroll(-1, "page") - return "break" + return self._xview('scroll', -1, 'page') def page_right(self, *event): - self.canvas.xview_scroll(1, "page") - return "break" + return self._xview('scroll', 1, 'page') def unit_left(self, *event): - self.canvas.xview_scroll(-1, "unit") - return "break" + return self._xview('scroll', -1, 'unit') def unit_right(self, *event): - self.canvas.xview_scroll(1, "unit") - return "break" + return self._xview('scroll', 1, 'unit') def scroll_top(self, *event): - self.canvas.yview_moveto(0) - return "break" + return self._yview('moveto', 0) def scroll_bottom(self, *event): - self.canvas.yview_moveto(1) - return "break" + return self._yview('moveto', 1)