diff --git a/data/PySol.icns b/data/PySol.icns new file mode 100644 index 00000000..d80f947c Binary files /dev/null and b/data/PySol.icns differ diff --git a/pysollib/app.py b/pysollib/app.py index a0c303dc..579860fe 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -1687,7 +1687,7 @@ Please select a %s type %s. # find all available music songs dirs = manager.getSearchDirs(self, "music-*", "PYSOL_MUSIC") ##print dirs - ext_re = re.compile(self.audio.EXTENTIONS) + ext_re = re.compile(self.audio.EXTENSIONS) self.initResource(manager, dirs, ext_re, Music) diff --git a/pysollib/games/__init__.py b/pysollib/games/__init__.py index 5bb03ec7..ffaa96c9 100644 --- a/pysollib/games/__init__.py +++ b/pysollib/games/__init__.py @@ -61,3 +61,5 @@ import wavemotion import windmill import yukon import zodiac +from ultra import * +from special import * \ No newline at end of file diff --git a/pysollib/init.py b/pysollib/init.py index 004cccbd..1ab8e7be 100644 --- a/pysollib/init.py +++ b/pysollib/init.py @@ -77,12 +77,19 @@ def init(): elif settings.TOOLKIT == 'tk' and settings.USE_TILE == 'auto': # check tile import Tkinter + from Tkinter import TclError root = Tkinter.Tk() + # + # TkAqua displays the console automatically in application + # bundles, so we hide it here. + from macosx.appSupport import hideTkConsole + # + hideTkConsole(root) root.withdraw() settings.USE_TILE = False try: root.tk.call('package', 'require', 'tile', '0.7.8') - except: + except TclError: pass else: settings.USE_TILE = True diff --git a/pysollib/macosx/__init__.py b/pysollib/macosx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pysollib/macosx/appSupport.py b/pysollib/macosx/appSupport.py new file mode 100644 index 00000000..b152f578 --- /dev/null +++ b/pysollib/macosx/appSupport.py @@ -0,0 +1,25 @@ +""" +A number of function that enhance PySol on MacOSX when it used as a normal +GUI application (as opposed to an X11 application). +""" +import sys +from Tkinter import Menu, Text, TclError + +def runningAsOSXApp(): + """ Returns True iff running from the PySol.app bundle on OSX """ + return (sys.platform == 'darwin' and 'PySol.app' in sys.argv[0]) + +def hideTkConsole(root): + try: + root.tk.call('console', 'hide') + except TclError: + pass + +def setupApp(app): + """ + Perform setup for the OSX application bundle. + """ + if not runningAsOSXApp(): return + + hideTkConsole(app.top) + #overrideRootMenu(root, flist) diff --git a/pysollib/pysolaudio.py b/pysollib/pysolaudio.py index 06f0f361..db5ab5ee 100644 --- a/pysollib/pysolaudio.py +++ b/pysollib/pysolaudio.py @@ -56,7 +56,7 @@ except ImportError: class AbstractAudioClient: - EXTENTIONS = r"\.((wav)|(it)|(mod)|(mp3)|(pym)|(s3m)|(xm))$" + EXTENSIONS = r"\.((wav)|(it)|(mod)|(mp3)|(pym)|(s3m)|(xm))$" CAN_PLAY_SOUND = False CAN_PLAY_MUSIC = False @@ -418,7 +418,7 @@ class OSSAudioClient(AbstractAudioClient): class PyGameAudioClient(AbstractAudioClient): - EXTENTIONS = r'\.((ogg)|(mp3)|(wav)|(it)|(mod)|(s3m)|(xm)|(mid))$' + EXTENSIONS = r'\.((ogg)|(mp3)|(wav)|(it)|(mod)|(s3m)|(xm)|(mid))$' CAN_PLAY_SOUND = True CAN_PLAY_MUSIC = True diff --git a/pysollib/resource.py b/pysollib/resource.py index 4a3b8866..2b7b8d2e 100644 --- a/pysollib/resource.py +++ b/pysollib/resource.py @@ -43,6 +43,7 @@ from mfxutil import Struct, KwStruct, EnvError, latin1_to_ascii from settings import PACKAGE, VERSION from settings import DEBUG +gettext = _ # /*********************************************************************** # // Abstract diff --git a/pysollib/tile/Tile.py b/pysollib/tile/Tile.py index 22d529b2..f2bca5d1 100644 --- a/pysollib/tile/Tile.py +++ b/pysollib/tile/Tile.py @@ -1,5 +1,5 @@ -import os +import sys import Tkinter from Tkconstants import * @@ -325,7 +325,7 @@ class Scrollbar(Widget, Tkinter.Scrollbar): # from http://tkinter.unpythonic.net/wiki/PyLocateTile : # standard Tk scrollbars work on OS X, but Tile ones look weird -if os.name == "mac": +if sys.platform == "darwin": Scrollbar = Tkinter.Scrollbar diff --git a/pysollib/tile/menubar.py b/pysollib/tile/menubar.py index 8f07105c..042c3cff 100644 --- a/pysollib/tile/menubar.py +++ b/pysollib/tile/menubar.py @@ -37,7 +37,7 @@ __all__ = ['PysolMenubar'] # imports -import math, os, re +import math, os, sys, re import Tile as Tkinter import tkFileDialog @@ -204,6 +204,7 @@ class PysolMenubar(PysolMenubarActions): self.__menupath = {} self.__keybindings = {} self._createMenubar() + self.top = top if self.progress: self.progress.update(step=1) @@ -350,7 +351,11 @@ class PysolMenubar(PysolMenubarActions): bind(self.top, "", self._keyPressHandler) m = "Ctrl-" - if os.name == "mac": m = "Cmd-" + if sys.platform == "darwin": m = "Cmd-" + + if self.top.tk.call("tk", "windowingsystem") == "aqua": + applemenu=MfxMenu(self.__menubar, n_("apple")) + applemenu.add_command(label=_("&About ")+PACKAGE, command=self.mHelpAbout) menu = MfxMenu(self.__menubar, n_("&File")) menu.add_command(label=n_("&New game"), command=self.mNewGame, accelerator="N") @@ -372,7 +377,8 @@ class PysolMenubar(PysolMenubarActions): menu.add_command(label=n_("Save &as..."), command=self.mSaveAs) menu.add_separator() menu.add_command(label=n_("&Hold and quit"), command=self.mHoldAndQuit) - menu.add_command(label=n_("&Quit"), command=self.mQuit, accelerator=m+"Q") + if not self.top.tk.call("tk", "windowingsystem") == "aqua": + menu.add_command(label=n_("&Quit"), command=self.mQuit, accelerator=m+"Q") if self.progress: self.progress.update(step=1) @@ -511,13 +517,15 @@ class PysolMenubar(PysolMenubarActions): menu.add_command(label=n_("&Rules for this game"), command=self.mHelpRules, accelerator="F1") menu.add_command(label=n_("&License terms"), command=self.mHelpLicense) ##menu.add_command(label=n_("What's &new ?"), command=self.mHelpNews) - menu.add_separator() - menu.add_command(label=n_("&About ")+PACKAGE+"...", command=self.mHelpAbout) + if not self.top.tk.call("tk", "windowingsystem") == "aqua": + menu.add_separator() + menu.add_command(label=n_("&About ")+PACKAGE+"...", command=self.mHelpAbout) MfxMenubar.addPath = None ### FIXME: all key bindings should be *added* to keyPressHandler ctrl = "Control-" + if sys.platform == "darwin": ctrl = "Command-" self._bindKey("", "n", self.mNewGame) self._bindKey("", "g", self.mSelectGameDialog) self._bindKey("", "v", self.mSelectGameDialogWithPreview) diff --git a/pysollib/tile/soundoptionsdialog.py b/pysollib/tile/soundoptionsdialog.py index 2d91ad46..1e6be712 100644 --- a/pysollib/tile/soundoptionsdialog.py +++ b/pysollib/tile/soundoptionsdialog.py @@ -142,7 +142,7 @@ class SoundOptionsDialog(MfxDialog): # remove "Apply" button kw.strings[1] = None # - frame = Tkinter.LabelFrame(top_frame, text=_('Enable samles')) + frame = Tkinter.LabelFrame(top_frame, text=_('Enable samples')) frame.pack(expand=1, fill='both', padx=5, pady=5) frame.columnconfigure(0, weight=1) frame.columnconfigure(1, weight=1) diff --git a/pysollib/tile/tkwrap.py b/pysollib/tile/tkwrap.py index 07216901..91de970b 100644 --- a/pysollib/tile/tkwrap.py +++ b/pysollib/tile/tkwrap.py @@ -48,6 +48,7 @@ from tkFont import Font # PySol imports from pysollib.mfxutil import destruct, Struct from pysollib.settings import PACKAGE, VERSION +from pysollib.macosx.appSupport import setupApp from tkutil import after_idle, init_tile, wm_set_icon from tkconst import EVENT_HANDLED, EVENT_PROPAGATE @@ -99,7 +100,7 @@ class MfxRoot(Tkinter.Tk): self.app = app def initToolkit(self, app, fg=None, bg=None, font=None): - theme = app.opt.tile_theme + setupApp(app) sw, sh, sd = self.winfo_screenwidth(), self.winfo_screenheight(), self.winfo_screendepth() self.wm_group(self) self.wm_title(PACKAGE + ' ' + VERSION) @@ -147,7 +148,16 @@ class MfxRoot(Tkinter.Tk): # theme try: - init_tile(app, self, theme) + windowingsystem = app.top.tk.call("tk", "windowingsystem") + if windowingsystem == "x11": + app.opt.tile_theme = "clam" + elif windowingsystem == "aqua": + app.opt.tile_theme = "aqua" + elif windowingsystem == "win32": + app.opt.tile_theme = "xpnative" + init_tile(app, self, app.opt.tile_theme) + except TclError: + raise except Exception, err: print >> sys.stderr, 'ERROR: set theme:', err ##self.option_add('*Toolbar.relief', 'groove') diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index 6cf09b58..1db3c075 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -37,7 +37,7 @@ __all__ = ['PysolMenubar'] # imports -import math, os, re +import math, os, sys, re import Tkinter, tkColorChooser, tkFileDialog # PySol imports @@ -213,6 +213,7 @@ class PysolMenubar(PysolMenubarActions): self.__menupath = {} self.__keybindings = {} self._createMenubar() + self.top = top if self.progress: self.progress.update(step=1) @@ -357,7 +358,11 @@ class PysolMenubar(PysolMenubarActions): bind(self.top, "", self._keyPressHandler) m = "Ctrl-" - if os.name == "mac": m = "Cmd-" + if sys.platform == "darwin": m = "Cmd-" + + if self.top.tk.call("tk", "windowingsystem") == "aqua": + applemenu=MfxMenu(self.__menubar, n_("apple")) + applemenu.add_command(label=_("&About ")+PACKAGE, command=self.mHelpAbout) menu = MfxMenu(self.__menubar, n_("&File")) menu.add_command(label=n_("&New game"), command=self.mNewGame, accelerator="N") @@ -379,7 +384,8 @@ class PysolMenubar(PysolMenubarActions): menu.add_command(label=n_("Save &as..."), command=self.mSaveAs) menu.add_separator() menu.add_command(label=n_("&Hold and quit"), command=self.mHoldAndQuit) - menu.add_command(label=n_("&Quit"), command=self.mQuit, accelerator=m+"Q") + if not self.top.tk.call("tk", "windowingsystem") == "aqua": + menu.add_command(label=n_("&Quit"), command=self.mQuit, accelerator=m+"Q") if self.progress: self.progress.update(step=1) @@ -516,13 +522,15 @@ class PysolMenubar(PysolMenubarActions): menu.add_command(label=n_("&Rules for this game"), command=self.mHelpRules, accelerator="F1") menu.add_command(label=n_("&License terms"), command=self.mHelpLicense) ##menu.add_command(label=n_("What's &new ?"), command=self.mHelpNews) - menu.add_separator() - menu.add_command(label=n_("&About ")+PACKAGE+"...", command=self.mHelpAbout) + if not self.top.tk.call("tk", "windowingsystem") == "aqua": + menu.add_separator() + menu.add_command(label=n_("&About ")+PACKAGE+"...", command=self.mHelpAbout) MfxMenubar.addPath = None ### FIXME: all key bindings should be *added* to keyPressHandler ctrl = "Control-" + if sys.platform == "darwin": ctrl = "Command-" self._bindKey("", "n", self.mNewGame) self._bindKey("", "g", self.mSelectGameDialog) self._bindKey("", "v", self.mSelectGameDialogWithPreview) diff --git a/pysollib/tk/tkwrap.py b/pysollib/tk/tkwrap.py index 10b7c4ef..86e1a609 100644 --- a/pysollib/tk/tkwrap.py +++ b/pysollib/tk/tkwrap.py @@ -48,6 +48,7 @@ from tkFont import Font # PySol imports from pysollib.mfxutil import destruct, Struct from pysollib.settings import PACKAGE, VERSION +from pysollib.macosx.appSupport import setupApp from tkutil import after_idle, wm_set_icon from tkconst import EVENT_HANDLED, EVENT_PROPAGATE @@ -99,6 +100,7 @@ class MfxRoot(Tkinter.Tk): self.app = app def initToolkit(self, app, fg=None, bg=None, font=None, theme=None): + setupApp(app) sw, sh, sd = self.winfo_screenwidth(), self.winfo_screenheight(), self.winfo_screendepth() self.wm_group(self) self.wm_title(PACKAGE + ' ' + VERSION) diff --git a/setup_osx.py b/setup_osx.py new file mode 100644 index 00000000..145a2852 --- /dev/null +++ b/setup_osx.py @@ -0,0 +1,86 @@ +""" +Usage: + python setup.py py2app +""" + +import os, sys +from subprocess import call +from setuptools import setup +from pysollib.settings import PACKAGE, VERSION, FC_VERSION + +# Use Tile widgets, if they are installed. +# http://tktable.sourceforge.net/tile/ +import Tkinter +root = Tkinter.Tk() +root.withdraw() +try: + root.tk.call('package', 'require', 'tile', '0.7.8') +except: + TILE = None +else: + TILE = "tile0.7.8" + TCL_EXTENSION_PATH = "/Library/Tcl" +finally: + root.destroy() + del root, Tkinter + +# Use Freecell Solver, if it is installed. +# http://fc-solve.berlios.de/ +SOLVER_LIB_PATH = "/usr/local/lib/libfreecell-solver.0.dylib" +SOLVER = "/usr/local/bin/fc-solve" +if not os.path.exists(SOLVER_LIB_PATH): + SOLVER_LIB_PATH = SOLVER = "" + +GETINFO_STRING = "PySol Fan Club Edition \ + %s %s, (C) 1998-2003 Markus F.X.J Oberhumer \ + %s, (C) 2006 Skomoroh" % (PACKAGE, VERSION, FC_VERSION) +PLIST = dict( + CFBundleDevelopmentRegion = 'en_US', + CFBundleExecutable = PACKAGE, + CFBundleDisplayName = PACKAGE, + CFBundleGetInfoString = GETINFO_STRING, + CFBundleIdentifier = 'org.pysol.PySol', + CFBundleName = PACKAGE, + CFBundleVersion = '%s (%s)' % (VERSION, FC_VERSION), + CFBundleShortVersionString = '%s' % VERSION, + NSHumanReadableCopyright = "Copyright (C) 1998-2003 Markus F.X.J. Oberhumer", + ) +APP = ['pysol.py'] +ICON_FILE = 'data/PySol.icns' +DATA_FILES = ['docs', 'data', 'scripts','COPYING', 'README', SOLVER] +RESOURCES = [os.path.join(TCL_EXTENSION_PATH, TILE)] if TILE else [] +FRAMEWORKS = [SOLVER_LIB_PATH] +OPTIONS = dict(argv_emulation=True, + plist=PLIST, + iconfile=ICON_FILE, + resources=RESOURCES, + frameworks=FRAMEWORKS, + excludes=['pysollib.pysolgtk'] + ) + +setup( + app=APP, + data_files=DATA_FILES, + options={'py2app': OPTIONS}, + setup_requires=['py2app'], + ) + +## +top = os.getcwd() +# FIXME: a hack to get Tcl extensions working +# from inside the app bundle +if TILE and "py2app" in sys.argv: + os.chdir('dist/%s.app/Contents/Frameworks' % PACKAGE) + try: + os.symlink('../Resources/%s' % TILE, TILE) + except OSError: + pass + os.chdir(top) +# Modify the fc-solve binary with install_name_tool to use the dependent +# libfreecell-solver dynamic library in the app bundle. +if SOLVER and "py2app" in sys.argv: + os.chdir('dist/%s.app/Contents/Resources' % PACKAGE) + call("install_name_tool -change \ + /usr/local/lib/libfreecell-solver.0.dylib \ + @executable_path/../Frameworks/libfreecell-solver.0.dylib fc-solve", + shell=True) \ No newline at end of file