diff options
author | Rasmus Andersson <rasmus@notion.se> | 2017-09-04 06:03:17 +0300 |
---|---|---|
committer | Rasmus Andersson <rasmus@notion.se> | 2017-09-04 18:12:34 +0300 |
commit | 8234b62ab762637ef24c3398b4204a8ce8db31a7 (patch) | |
tree | 1c8df547021cdb58951630a015e4101ede46dbf1 /misc/pylib/robofab/interface | |
parent | 31ae014e0c827dd76696fdab7e4ca3fed9f6402b (diff) | |
download | inter-8234b62ab762637ef24c3398b4204a8ce8db31a7.tar.xz |
Speeds up font compilation by around 200%
Cython is used to compile some hot paths into native Python extensions.
These hot paths were identified through running ufocompile with the hotshot
profiler and then converting file by file to Cython, starting with the "hottest"
paths and continuing until returns were deminishing. This means that only a few
Python files were converted to Cython.
Closes #23
Closes #20 (really this time)
Diffstat (limited to 'misc/pylib/robofab/interface')
-rwxr-xr-x | misc/pylib/robofab/interface/__init__.py | 14 | ||||
-rwxr-xr-x | misc/pylib/robofab/interface/all/__init__.py | 14 | ||||
-rwxr-xr-x | misc/pylib/robofab/interface/all/dialogs.py | 278 | ||||
-rw-r--r-- | misc/pylib/robofab/interface/all/dialogs_default.py | 76 | ||||
-rw-r--r-- | misc/pylib/robofab/interface/all/dialogs_fontlab_legacy1.py | 73 | ||||
-rw-r--r-- | misc/pylib/robofab/interface/all/dialogs_fontlab_legacy2.py | 373 | ||||
-rwxr-xr-x | misc/pylib/robofab/interface/all/dialogs_legacy.py | 737 | ||||
-rw-r--r-- | misc/pylib/robofab/interface/all/dialogs_mac_vanilla.py | 267 | ||||
-rwxr-xr-x | misc/pylib/robofab/interface/mac/__init__.py | 10 | ||||
-rwxr-xr-x | misc/pylib/robofab/interface/mac/getFileOrFolder.py | 80 | ||||
-rwxr-xr-x | misc/pylib/robofab/interface/win/__init__.py | 10 |
11 files changed, 1932 insertions, 0 deletions
diff --git a/misc/pylib/robofab/interface/__init__.py b/misc/pylib/robofab/interface/__init__.py new file mode 100755 index 000000000..154c42b70 --- /dev/null +++ b/misc/pylib/robofab/interface/__init__.py @@ -0,0 +1,14 @@ +""" + +Directory for interface related modules. Stuff like widgets, +dialog modules. Please keep them sorted by platform. + +interfaces/all : modules that are platform independent +interfaces/mac : modules that are mac specific +interfaces/win : modules that are windows specific + +""" + + + + diff --git a/misc/pylib/robofab/interface/all/__init__.py b/misc/pylib/robofab/interface/all/__init__.py new file mode 100755 index 000000000..154c42b70 --- /dev/null +++ b/misc/pylib/robofab/interface/all/__init__.py @@ -0,0 +1,14 @@ +""" + +Directory for interface related modules. Stuff like widgets, +dialog modules. Please keep them sorted by platform. + +interfaces/all : modules that are platform independent +interfaces/mac : modules that are mac specific +interfaces/win : modules that are windows specific + +""" + + + + diff --git a/misc/pylib/robofab/interface/all/dialogs.py b/misc/pylib/robofab/interface/all/dialogs.py new file mode 100755 index 000000000..8fdfe847b --- /dev/null +++ b/misc/pylib/robofab/interface/all/dialogs.py @@ -0,0 +1,278 @@ +""" + + + Restructured dialogs for Robofab + + dialog file dialogs + +* FontLab 5.04 10.6 dialogKit fl internal * theoretically that should work under windows as well, untested +* FontLab 5.1 10.6 dialogKit fl internal +* FontLab 5.1 10.7 raw cocao fl internal + Glyphs any vanilla vanilla + RoboFont any vanilla vanilla + + This module does a fair amount of sniffing in order to guess + which dialogs to load. Linux and Windows environments are + underrepresented at the moment. Following the prototypes in dialogs_default.py + it is possible (with knowledge of the platform) to extend support. + + The platformApplicationSupport table contains very specific + versions with which certain apps need to work. Moving forward, + it is likely that these versions will change and need to be updated. + + # this calls the new dialogs infrastructure: + from robofab.interface.all.dialogs import Message + + # this calls the old original legacy dialogs infrastructure: + from robofab.interface.all.dialogs_legacy import Message + +""" + + + +# determine platform and application +import sys, os +import platform as _platform + +__verbose__ = False + +platform = None +platformVersion = None +application = None +applicationVersion = None + +if sys.platform in ( + 'mac', + 'darwin', + ): + platform = "mac" + v = _platform.mac_ver()[0] + platformVersion = float('.'.join(v.split('.')[:2])) +elif sys.platform in ( + 'linux1', + 'linux2', # Ubuntu = others? + ): + platform = "linux" +elif os.name == 'nt': + platform = "win" + +# determine application + +try: + # FontLab + # alternative syntax to cheat on the FL import filtering in RF + __import__("FL") + application = "fontlab" + #applicationVersion = fl.version +except ImportError: + pass + +if application is None: + try: + # RoboFont + import mojo + application = 'robofont' + try: + from AppKit import NSBundle + b = NSBundle.mainBundle() + applicationVersion = b.infoDictionary()["CFBundleVersion"] + except ImportError: + pass + except ImportError: + pass + +if application is None: + try: + # Glyphs + import GlyphsApp + application = "glyphs" + except ImportError: + pass + +if application is None: + try: + # fontforge + # note that in some configurations, fontforge can be imported in other pythons as well + # so the availability of the fontforge module is no garuantee that we are in fontforge. + import fontforge + application = "fontforge" + except ImportError: + pass + +pyVersion = sys.version_info[:3] + +# with that out of the way, perhaps we can have a look at where we are +# and which modules we have available. This maps any number of platform / application +# combinations so an independent list of module names. That should make it +# possible to map multiple things to one module. + +platformApplicationSupport = [ + # + # switchboard for platform, application, python version -> dialog implementations + # platform applicatiom python sub module + # | | | | + ('mac', 'fontlab', (2,3,5), "dialogs_fontlab_legacy1"), + # because FontLab 5.01 and earlier on 2.3.5 can run EasyDialogs + # | | | | + # because FontLab 5.1 on mac 10.6 should theoretically be able to run cocoa dialogs, + # but they are very unreliable. So until we know what's going on, FL5.1 on 10.6 + # is going to have to live with DialogKit dialogs. + # | | | | + ('mac', 'fontlab', None, "dialogs_fontlab_legacy2"), + # because FontLab 5.1 on mac, 10.7+ should run cocoa / vanilla + # | | | | + ('mac', None, None, "dialogs_mac_vanilla"), + # perhaps nonelab scripts can run vanilla as well? + # | | | | + ('win', None, None, "dialogs_legacy"), + # older windows stuff might be able to use the legacy dialogs +] + +platformModule = None +foundPlatformModule = False +dialogs = {} + +if __verbose__: + print "robofab.interface.all __init__ - finding out where we were." + +# do we have a support module? +for pl, app, py, platformApplicationModuleName in platformApplicationSupport: + if __verbose__: + print "looking at", pl, app, py, platformApplicationModuleName + if pl is None or pl == platform: + if app is None or app == application: + if py is None or py == pyVersion: + break + if __verbose__: + print "nope" + +if __verbose__: + print "searched for", pl, app, py, platformApplicationModuleName + +# preload the namespace with default functions that do nothing but raise NotImplementedError +from robofab.interface.all.dialogs_default import * + +# now import the module we selected. +if platformApplicationModuleName == "dialogs_fontlab_legacy1": + try: + from robofab.interface.all.dialogs_fontlab_legacy1 import * + foundPlatformModule = True + if __verbose__: + print "loaded robofab.interface.all.dialogs_fontlab_legacy1" + if platform == "mac": + from robofab.interface.mac.getFileOrFolder import GetFile, GetFileOrFolder + except ImportError: + print "can't import", platformApplicationModuleName + +elif platformApplicationModuleName == "dialogs_fontlab_legacy2": + try: + from robofab.interface.all.dialogs_fontlab_legacy2 import * + foundPlatformModule = True + if __verbose__: + print "loaded robofab.interface.all.dialogs_fontlab_legacy2" + if platform == "mac": + # + # + # + # + # + from robofab.interface.all.dialogs_legacy import AskString, TwoChecks, TwoFields, SelectGlyph, FindGlyph, OneList, SearchList, SelectFont, SelectGlyph + except ImportError: + print "can't import", platformApplicationModuleName + +elif platformApplicationModuleName == "dialogs_mac_vanilla": + try: + from robofab.interface.all.dialogs_mac_vanilla import * + foundPlatformModule = True + if __verbose__: + print "loaded robofab.interface.all.dialogs_mac_vanilla" + except ImportError: + print "can't import", platformApplicationModuleName + +elif platformApplicationModuleName == "dialogs_legacy": + try: + from robofab.interface.all.dialogs_legacy import * + foundPlatformModule = True + if __verbose__: + print "loaded robofab.interface.all.dialogs_legacy" + except ImportError: + print "can't import", platformApplicationModuleName + + +__all__ = [ + "AskString", + "AskYesNoCancel", + "FindGlyph", + "GetFile", + "GetFolder", + "GetFileOrFolder", + "Message", + "OneList", + "PutFile", + "SearchList", + "SelectFont", + "SelectGlyph", + "TwoChecks", + "TwoFields", + "ProgressBar", +] + + +def test(): + """ This is a test that prints the available functions and where they're imported from. + The report can be useful for debugging. + + For instance: + + from robofab.interface.all.dialogs import test + test() + + testing RoboFab Dialogs: + python version: (2, 7, 1) + platform: mac + application: None + applicationVersion: None + platformVersion: 10.7 + looking for module: dialogs_mac_vanilla + did we find it? True + + Available dialogs and source: + AskString robofab.interface.all.dialogs_mac_vanilla + AskYesNoCancel robofab.interface.all.dialogs_mac_vanilla + FindGlyph robofab.interface.all.dialogs_mac_vanilla + GetFile robofab.interface.all.dialogs_mac_vanilla + GetFolder robofab.interface.all.dialogs_mac_vanilla + GetFileOrFolder robofab.interface.all.dialogs_mac_vanilla + Message robofab.interface.all.dialogs_mac_vanilla + OneList robofab.interface.all.dialogs_mac_vanilla + PutFile robofab.interface.all.dialogs_mac_vanilla + SearchList robofab.interface.all.dialogs_mac_vanilla + SelectFont robofab.interface.all.dialogs_mac_vanilla + SelectGlyph robofab.interface.all.dialogs_mac_vanilla + TwoChecks robofab.interface.all.dialogs_default + TwoFields robofab.interface.all.dialogs_default + ProgressBar robofab.interface.all.dialogs_mac_vanilla + + """ + + print + print "testing RoboFab Dialogs:" + print "\tpython version:", pyVersion + print "\tplatform:", platform + print "\tapplication:", application + print "\tapplicationVersion:", applicationVersion + print "\tplatformVersion:", platformVersion + print "\tlooking for module:", platformApplicationModuleName + print "\t\tdid we find it?", foundPlatformModule + + print + print "Available dialogs and source:" + for name in __all__: + if name in globals().keys(): + print "\t", name, "\t", globals()[name].__module__ + else: + print "\t", name, "\t not loaded." + +if __name__ == "__main__": + test() + diff --git a/misc/pylib/robofab/interface/all/dialogs_default.py b/misc/pylib/robofab/interface/all/dialogs_default.py new file mode 100644 index 000000000..c513aa9b7 --- /dev/null +++ b/misc/pylib/robofab/interface/all/dialogs_default.py @@ -0,0 +1,76 @@ +""" + + Dialog prototypes. + + These are loaded before any others. So if a specific platform implementation doesn't + have all functions, these will make sure a NotImplemtedError is raised. + + http://www.robofab.org/tools/dialogs.html + +""" + +__all__ = [ + "AskString", + "AskYesNoCancel", + "FindGlyph", + "GetFile", + "GetFolder", + "GetFileOrFolder", + "Message", + "OneList", + "PutFile", + "SearchList", + "SelectFont", + "SelectGlyph", + "TwoChecks", + "TwoFields", + "ProgressBar", +] + +# start with all the defaults. + +def AskString(message, value='', title='RoboFab'): + raise NotImplementedError + +def AskYesNoCancel(message, title='RoboFab', default=0): + raise NotImplementedError + +def FindGlyph(font, message="Search for a glyph:", title='RoboFab'): + raise NotImplementedError + +def GetFile(message=None): + raise NotImplementedError + +def GetFolder(message=None): + raise NotImplementedError + +def GetFileOrFolder(message=None): + raise NotImplementedError + +def Message(message, title='RoboFab'): + raise NotImplementedError + +def OneList(list, message="Select an item:", title='RoboFab'): + raise PendingDeprecationWarning + +def PutFile(message=None, fileName=None): + raise NotImplementedError + +def SearchList(list, message="Select an item:", title='RoboFab'): + raise NotImplementedError + +def SelectFont(message="Select a font:", title='RoboFab'): + raise NotImplementedError + +def SelectGlyph(font, message="Select a glyph:", title='RoboFab'): + raise NotImplementedError + +def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'): + raise PendingDeprecationWarning + +def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'): + raise PendingDeprecationWarning + +class ProgressBar(object): + pass + diff --git a/misc/pylib/robofab/interface/all/dialogs_fontlab_legacy1.py b/misc/pylib/robofab/interface/all/dialogs_fontlab_legacy1.py new file mode 100644 index 000000000..73c3d7a15 --- /dev/null +++ b/misc/pylib/robofab/interface/all/dialogs_fontlab_legacy1.py @@ -0,0 +1,73 @@ +""" + + Dialogs for FontLab < 5.1. + + This one should be loaded for various platforms, using dialogKit + http://www.robofab.org/tools/dialogs.html + +""" + +from FL import * +from dialogKit import ModalDialog, Button, TextBox, EditText + +__all__ = [ + #"AskString", + #"AskYesNoCancel", + #"FindGlyph", + "GetFile", + "GetFolder", + #"Message", + #"OneList", + #"PutFile", + #"SearchList", + #"SelectFont", + #"SelectGlyph", + #"TwoChecks", + #"TwoFields", + "ProgressBar", +] + + +def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None): + strFilter = "All Files (*.*)|*.*|" + defaultExt = "" + # using fontlab's internal file dialogs + return fl.GetFileName(1, defaultExt, message, strFilter) + +def GetFolder(message=None, title=None, directory=None, allowsMultipleSelection=False): + # using fontlab's internal file dialogs + if message is None: + message = "" + return fl.GetPathName(message) + +def PutFile(message=None, fileName=None): + # using fontlab's internal file dialogs + # message is not used + if message is None: + message = "" + if fileName is None: + fileName = "" + defaultExt = "" + return fl.GetFileName(0, defaultExt, fileName, '') + +class ProgressBar(object): + + def __init__(self, title="RoboFab...", ticks=0, label=""): + self._tickValue = 1 + fl.BeginProgress(title, ticks) + + def getCurrentTick(self): + return self._tickValue + + def tick(self, tickValue=None): + if not tickValue: + tickValue = self._tickValue + fl.TickProgress(tickValue) + self._tickValue = tickValue + 1 + + def label(self, label): + pass + + def close(self): + fl.EndProgress() + diff --git a/misc/pylib/robofab/interface/all/dialogs_fontlab_legacy2.py b/misc/pylib/robofab/interface/all/dialogs_fontlab_legacy2.py new file mode 100644 index 000000000..460b73f1e --- /dev/null +++ b/misc/pylib/robofab/interface/all/dialogs_fontlab_legacy2.py @@ -0,0 +1,373 @@ +""" + + Dialogs for FontLab 5.1. + This might work in future versions of FontLab as well. + This is basically a butchered version of vanilla.dialogs. + No direct import of, or dependency on Vanilla + + March 7 2012 + It seems only the dialogs that deal with the file system + need to be replaced, the other dialogs still work. + As we're not entirely sure whether it is worth to maintain + these dialogs, let's fix the imports in dialogs.py. + + This is the phenolic aldehyde version of dialogs. + +""" + +#__import__("FL") +from FL import * + +from Foundation import NSObject +from AppKit import NSApplication, NSInformationalAlertStyle, objc, NSAlert, NSAlertFirstButtonReturn, NSAlertSecondButtonReturn, NSAlertThirdButtonReturn, NSSavePanel, NSOKButton, NSOpenPanel + +NSApplication.sharedApplication() + +__all__ = [ +# "AskString", + "AskYesNoCancel", +# "FindGlyph", + "GetFile", + "GetFolder", + "GetFileOrFolder", + "Message", +# "OneList", + "PutFile", +# "SearchList", +# "SelectFont", +# "SelectGlyph", +# "TwoChecks", +# "TwoFields", + "ProgressBar", +] + + +class BaseMessageDialog(NSObject): + + def initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_(self, + messageText="", + informativeText="", + alertStyle=NSInformationalAlertStyle, + buttonTitlesValues=None, + parentWindow=None, + resultCallback=None): + if buttonTitlesValues is None: + buttonTitlesValues = [] + self = super(BaseMessageDialog, self).init() + self.retain() + self._resultCallback = resultCallback + self._buttonTitlesValues = buttonTitlesValues + # + alert = NSAlert.alloc().init() + alert.setMessageText_(messageText) + alert.setInformativeText_(informativeText) + alert.setAlertStyle_(alertStyle) + for buttonTitle, value in buttonTitlesValues: + alert.addButtonWithTitle_(buttonTitle) + self._value = None + code = alert.runModal() + self._translateValue(code) + return self + + def _translateValue(self, code): + if code == NSAlertFirstButtonReturn: + value = 1 + elif code == NSAlertSecondButtonReturn: + value = 2 + elif code == NSAlertThirdButtonReturn: + value = 3 + else: + value = code - NSAlertThirdButtonReturn + 3 + self._value = self._buttonTitlesValues[value-1][1] + + def windowWillClose_(self, notification): + self.autorelease() + + +class BasePutGetPanel(NSObject): + + def initWithWindow_resultCallback_(self, parentWindow=None, resultCallback=None): + self = super(BasePutGetPanel, self).init() + self.retain() + self._parentWindow = parentWindow + self._resultCallback = resultCallback + return self + + def windowWillClose_(self, notification): + self.autorelease() + + +class PutFilePanel(BasePutGetPanel): + + def initWithWindow_resultCallback_(self, parentWindow=None, resultCallback=None): + self = super(PutFilePanel, self).initWithWindow_resultCallback_(parentWindow, resultCallback) + self.messageText = None + self.title = None + self.fileTypes = None + self.directory = None + self.fileName = None + self.canCreateDirectories = True + self.accessoryView = None + self._result = None + return self + + def run(self): + panel = NSSavePanel.alloc().init() + if self.messageText: + panel.setMessage_(self.messageText) + if self.title: + panel.setTitle_(self.title) + if self.directory: + panel.setDirectory_(self.directory) + if self.fileTypes: + panel.setAllowedFileTypes_(self.fileTypes) + panel.setCanCreateDirectories_(self.canCreateDirectories) + panel.setCanSelectHiddenExtension_(True) + panel.setAccessoryView_(self.accessoryView) + if self._parentWindow is not None: + panel.beginSheetForDirectory_file_modalForWindow_modalDelegate_didEndSelector_contextInfo_( + self.directory, self.fileName, self._parentWindow, self, "savePanelDidEnd:returnCode:contextInfo:", 0) + else: + isOK = panel.runModalForDirectory_file_(self.directory, self.fileName) + if isOK == NSOKButton: + self._result = panel.filename() + + def savePanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context): + panel.close() + if returnCode: + self._result = panel.filename() + if self._resultCallback is not None: + self._resultCallback(self._result) + + savePanelDidEnd_returnCode_contextInfo_ = objc.selector(savePanelDidEnd_returnCode_contextInfo_, signature="v@:@ii") + + +class GetFileOrFolderPanel(BasePutGetPanel): + + def initWithWindow_resultCallback_(self, parentWindow=None, resultCallback=None): + self = super(GetFileOrFolderPanel, self).initWithWindow_resultCallback_(parentWindow, resultCallback) + self.messageText = None + self.title = None + self.directory = None + self.fileName = None + self.fileTypes = None + self.allowsMultipleSelection = False + self.canChooseDirectories = True + self.canChooseFiles = True + self.resolvesAliases = True + self._result = None + return self + + def run(self): + panel = NSOpenPanel.alloc().init() + if self.messageText: + panel.setMessage_(self.messageText) + if self.title: + panel.setTitle_(self.title) + if self.directory: + panel.setDirectory_(self.directory) + if self.fileTypes: + panel.setAllowedFileTypes_(self.fileTypes) + panel.setCanChooseDirectories_(self.canChooseDirectories) + panel.setCanChooseFiles_(self.canChooseFiles) + panel.setAllowsMultipleSelection_(self.allowsMultipleSelection) + panel.setResolvesAliases_(self.resolvesAliases) + if self._parentWindow is not None: + panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_( + self.directory, self.fileName, self.fileTypes, self._parentWindow, self, "openPanelDidEnd:returnCode:contextInfo:", 0) + else: + isOK = panel.runModalForDirectory_file_types_(self.directory, self.fileName, self.fileTypes) + if isOK == NSOKButton: + self._result = panel.filenames() + + def openPanelDidEnd_returnCode_contextInfo_(self, panel, returnCode, context): + panel.close() + if returnCode: + self._result = panel.filenames() + if self._resultCallback is not None: + self._resultCallback(self._result) + + openPanelDidEnd_returnCode_contextInfo_ = objc.selector(openPanelDidEnd_returnCode_contextInfo_, signature="v@:@ii") + + +def Message(message="", title='noLongerUsed', informativeText=""): + """Legacy robofab dialog compatible wrapper.""" + #def _message(messageText="", informativeText="", alertStyle=NSInformationalAlertStyle, parentWindow=None, resultCallback=None): + resultCallback = None + alert = BaseMessageDialog.alloc().initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_( + messageText=message, + informativeText=informativeText, + alertStyle=NSInformationalAlertStyle, + buttonTitlesValues=[("OK", 1)], + parentWindow=None, + resultCallback=None) + if resultCallback is None: + return 1 + + +def AskYesNoCancel(message, title='noLongerUsed', default=None, informativeText=""): + """ + AskYesNoCancel Dialog + + message the string + title* a title of the window + (may not be supported everywhere) + default* index number of which button should be default + (i.e. respond to return) + informativeText* A string with secundary information + + * may not be supported everywhere + """ + parentWindow = None + alert = BaseMessageDialog.alloc().initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_( + messageText=message, + informativeText=informativeText, + alertStyle=NSInformationalAlertStyle, + buttonTitlesValues=[("Cancel", -1), ("Yes", 1), ("No", 0)], + parentWindow=None, + resultCallback=None) + return alert._value + +def _askYesNo(messageText="", informativeText="", alertStyle=NSInformationalAlertStyle, parentWindow=None, resultCallback=None): + parentWindow = None + alert = BaseMessageDialog.alloc().initWithMessageText_informativeText_alertStyle_buttonTitlesValues_window_resultCallback_( + messageText=messageText, informativeText=informativeText, alertStyle=alertStyle, buttonTitlesValues=[("Yes", 1), ("No", 0)], parentWindow=parentWindow, resultCallback=resultCallback) + if resultCallback is None: + return alert._value + +def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None): + """ Legacy robofab dialog compatible wrapper. + This will select UFO on OSX 10.7, FL5.1 + """ + parentWindow = None + resultCallback=None + basePanel = GetFileOrFolderPanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback) + basePanel.messageText = message + basePanel.title = title + basePanel.directory = directory + basePanel.fileName = fileName + basePanel.fileTypes = fileTypes + basePanel.allowsMultipleSelection = allowsMultipleSelection + basePanel.canChooseDirectories = False + basePanel.canChooseFiles = True + basePanel.run() + if basePanel._result is None: + return None + if not allowsMultipleSelection: + # compatibly return only one as we expect + return str(list(basePanel._result)[0]) + else: + # return more if we explicitly expect + return [str(n) for n in list(basePanel._result)] + +def GetFolder(message=None, title=None, directory=None, allowsMultipleSelection=False): + parentWindow = None + resultCallback = None + basePanel = GetFileOrFolderPanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback) + basePanel.messageText = message + basePanel.title = title + basePanel.directory = directory + basePanel.allowsMultipleSelection = allowsMultipleSelection + basePanel.canChooseDirectories = True + basePanel.canChooseFiles = False + basePanel.run() + if basePanel._result is None: + return None + if not allowsMultipleSelection: + # compatibly return only one as we expect + return str(list(basePanel._result)[0]) + else: + # return more if we explicitly expect + return [str(n) for n in list(basePanel._result)] + +def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None, parentWindow=None, resultCallback=None): + parentWindow = None + basePanel = GetFileOrFolderPanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback) + basePanel.messageText = message + basePanel.title = title + basePanel.directory = directory + basePanel.fileName = fileName + basePanel.fileTypes = fileTypes + basePanel.allowsMultipleSelection = allowsMultipleSelection + basePanel.canChooseDirectories = True + basePanel.canChooseFiles = True + basePanel.run() + if basePanel._result is None: + return None + if not allowsMultipleSelection: + # compatibly return only one as we expect + return str(list(basePanel._result)[0]) + else: + # return more if we explicitly expect + return [str(n) for n in list(basePanel._result)] + +def PutFile(message=None, title=None, directory=None, fileName=None, canCreateDirectories=True, fileTypes=None): + parentWindow = None + resultCallback=None + accessoryView=None + basePanel = PutFilePanel.alloc().initWithWindow_resultCallback_(parentWindow, resultCallback) + basePanel.messageText = message + basePanel.title = title + basePanel.directory = directory + basePanel.fileName = fileName + basePanel.fileTypes = fileTypes + basePanel.canCreateDirectories = canCreateDirectories + basePanel.accessoryView = accessoryView + basePanel.run() + return str(basePanel._result) + + +class ProgressBar(object): + + def __init__(self, title="RoboFab...", ticks=0, label=""): + self._tickValue = 1 + fl.BeginProgress(title, ticks) + + def getCurrentTick(self): + return self._tickValue + + def tick(self, tickValue=None): + if not tickValue: + tickValue = self._tickValue + fl.TickProgress(tickValue) + self._tickValue = tickValue + 1 + + def label(self, label): + pass + + def close(self): + fl.EndProgress() + + +# we seem to have problems importing from here. +# so let's see what happens if we make the robofab compatible wrappers here as well. + +# start with all the defaults. + +#def AskString(message, value='', title='RoboFab'): +# raise NotImplementedError + +#def FindGlyph(aFont, message="Search for a glyph:", title='RoboFab'): +# raise NotImplementedError + +#def OneList(list, message="Select an item:", title='RoboFab'): +# raise NotImplementedError + +#def PutFile(message=None, fileName=None): +# raise NotImplementedError + +#def SearchList(list, message="Select an item:", title='RoboFab'): +# raise NotImplementedError + +#def SelectFont(message="Select a font:", title='RoboFab'): +# raise NotImplementedError + +#def SelectGlyph(font, message="Select a glyph:", title='RoboFab'): +# raise NotImplementedError + +#def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'): +# raise NotImplementedError + +#def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'): +# raise NotImplementedError + diff --git a/misc/pylib/robofab/interface/all/dialogs_legacy.py b/misc/pylib/robofab/interface/all/dialogs_legacy.py new file mode 100755 index 000000000..8223a7d64 --- /dev/null +++ b/misc/pylib/robofab/interface/all/dialogs_legacy.py @@ -0,0 +1,737 @@ + +""" + +Dialogs. +Cross-platform and cross-application compatible. Some of them anyway. +(Not all dialogs work on PCs outside of FontLab. Some dialogs are for FontLab only. Sorry.) + +Mac and FontLab implementation written by the RoboFab development team. +PC implementation by Eigi Eigendorf and is (C)2002 Eigi Eigendorf. + +""" + +import os +import sys +from robofab import RoboFabError +from warnings import warn + +MAC = False +PC = False +haveMacfs = False + +if sys.platform in ('mac', 'darwin'): + MAC = True +elif os.name == 'nt': + PC = True +else: + warn("dialogs.py only supports Mac and PC platforms.") +pyVersion = sys.version_info[:3] + +inFontLab = False +try: + from FL import * + inFontLab = True +except ImportError: pass + + +try: + import W + hasW = True +except ImportError: + hasW = False + +try: + import dialogKit + hasDialogKit = True +except ImportError: + hasDialogKit = False + +try: + import EasyDialogs + hasEasyDialogs = True +except: + hasEasyDialogs = False + +if MAC: + if pyVersion < (2, 3, 0): + import macfs + haveMacfs = True +elif PC and not inFontLab: + from win32com.shell import shell + import win32ui + import win32con + + +def _raisePlatformError(dialog): + """error raiser""" + if MAC: + p = 'Macintosh' + elif PC: + p = 'PC' + else: + p = sys.platform + raise RoboFabError("%s is not currently available on the %s platform"%(dialog, p)) + + +class _FontLabDialogOneList: + """A one list dialog for FontLab. This class should not be called directly. Use the OneList function.""" + + def __init__(self, list, message, title='RoboFab'): + self.message = message + self.selected = None + self.list = list + self.d = Dialog(self) + self.d.size = Point(250, 250) + self.d.title = title + self.d.Center() + self.d.AddControl(LISTCONTROL, Rect(12, 30, 238, 190), "list", STYLE_LIST, self.message) + self.list_index = 0 + + def Run(self): + return self.d.Run() + + def on_cancel(self, code): + self.selected = None + + def on_ok(self, code): + self.d.GetValue('list') + # Since FLS v5.2, the GetValue() method of the Dialog() class returns + # a 'wrong' index value from the specified LISTCONTROL. + # If the selected index is n, it will return n-1. For example, when + # the index is 1, it returns 0; when it's 2, it returns 1, and so on. + # If the selection is empty, FLS v5.2 returns -2, while the old v5.0 + # returned None. + # See also: + # - http://forum.fontlab.com/index.php?topic=8807.0 + # - http://forum.fontlab.com/index.php?topic=9003.0 + # + # Edited based on feedback from Adam Twardoch + if fl.buildnumber > 4600 and sys.platform == 'win32': + if self.list_index == -2: + self.selected = None + else: + self.selected = self.list_index + 1 + else: + self.selected = self.list_index + + +class _FontLabDialogSearchList: + """A dialog for searching through a list. It contains a text field and a results list FontLab. This class should not be called directly. Use the SearchList function.""" + + def __init__(self, aList, message, title="RoboFab"): + self.d = Dialog(self) + self.d.size = Point(250, 290) + self.d.title = title + self.d.Center() + + self.message = message + self._fullContent = aList + self.possibleHits = list(aList) + self.possibleHits.sort() + self.possibleHits_index = 0 + self.entryField = "" + self.selected = None + + self.d.AddControl(STATICCONTROL, Rect(10, 10, 240, 30), "message", STYLE_LABEL, message) + self.d.AddControl(EDITCONTROL, Rect(10, 30, 240, aAUTO), "entryField", STYLE_EDIT, "") + self.d.AddControl(LISTCONTROL, Rect(12, 60, 238, 230), "possibleHits", STYLE_LIST, "") + + + def run(self): + self.d.Run() + + def on_entryField(self, code): + self.d.GetValue("entryField") + entry = self.entryField + count = len(entry) + possibleHits = [ + i for i in self._fullContent + if len(i) >= count + and i[:count] == entry + ] + possibleHits.sort() + self.possibleHits = possibleHits + self.possibleHits_index = 0 + self.d.PutValue("possibleHits") + + def on_ok(self, code): + self.d.GetValue("possibleHits") + sel = self.possibleHits_index + if sel == -1: + self.selected = None + else: + self.selected = self.possibleHits[sel] + + def on_cancel(self, code): + self.selected = None + + +class _FontLabDialogTwoFields: + """A two field dialog for FontLab. This class should not be called directly. Use the TwoFields function.""" + + def __init__(self, title_1, value_1, title_2, value_2, title='RoboFab'): + self.d = Dialog(self) + self.d.size = Point(200, 125) + self.d.title = title + self.d.Center() + self.d.AddControl(EDITCONTROL, Rect(120, 10, aIDENT2, aAUTO), "v1edit", STYLE_EDIT, title_1) + self.d.AddControl(EDITCONTROL, Rect(120, 40, aIDENT2, aAUTO), "v2edit", STYLE_EDIT, title_2) + self.v1edit = value_1 + self.v2edit = value_2 + + def Run(self): + return self.d.Run() + + def on_cancel(self, code): + self.v1edit = None + self.v2edit = None + + def on_ok(self, code): + self.d.GetValue("v1edit") + self.d.GetValue("v2edit") + self.v1 = self.v1edit + self.v2 = self.v2edit + +class _FontLabDialogTwoChecks: + """A two check box dialog for FontLab. This class should not be called directly. Use the TwoChecks function.""" + + def __init__(self, title_1, title_2, value1=1, value2=1, title='RoboFab'): + self.d = Dialog(self) + self.d.size = Point(200, 105) + self.d.title = title + self.d.Center() + self.d.AddControl(CHECKBOXCONTROL, Rect(10, 10, aIDENT2, aAUTO), "check1", STYLE_CHECKBOX, title_1) + self.d.AddControl(CHECKBOXCONTROL, Rect(10, 30, aIDENT2, aAUTO), "check2", STYLE_CHECKBOX, title_2) + self.check1 = value1 + self.check2 = value2 + + def Run(self): + return self.d.Run() + + def on_cancel(self, code): + self.check1 = None + self.check2 = None + + def on_ok(self, code): + self.d.GetValue("check1") + self.d.GetValue("check2") + + +class _FontLabDialogAskString: + """A one simple string prompt dialog for FontLab. This class should not be called directly. Use the GetString function.""" + + def __init__(self, message, value, title='RoboFab'): + self.d = Dialog(self) + self.d.size = Point(350, 130) + self.d.title = title + self.d.Center() + self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, aAUTO), "label", STYLE_LABEL, message) + self.d.AddControl(EDITCONTROL, Rect(aIDENT, 40, aIDENT, aAUTO), "value", STYLE_EDIT, '') + self.value=value + + def Run(self): + return self.d.Run() + + def on_cancel(self, code): + self.value = None + + def on_ok(self, code): + self.d.GetValue("value") + +class _FontLabDialogMessage: + """A simple message dialog for FontLab. This class should not be called directly. Use the SimpleMessage function.""" + + def __init__(self, message, title='RoboFab'): + self.d = Dialog(self) + self.d.size = Point(350, 130) + self.d.title = title + self.d.Center() + self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, 80), "label", STYLE_LABEL, message) + + def Run(self): + return self.d.Run() + +class _FontLabDialogGetYesNoCancel: + """A yes no cancel message dialog for FontLab. This class should not be called directly. Use the YesNoCancel function.""" + + def __init__(self, message, title='RoboFab'): + self.d = Dialog(self) + self.d.size = Point(350, 130) + self.d.title = title + self.d.Center() + self.d.ok = 'Yes' + self.d.AddControl(STATICCONTROL, Rect(aIDENT, aIDENT, aIDENT, 80), "label", STYLE_LABEL, message) + self.d.AddControl(BUTTONCONTROL, Rect(100, 95, 172, 115), "button", STYLE_BUTTON, "No") + self.value = 0 + + def Run(self): + return self.d.Run() + + def on_ok(self, code): + self.value = 1 + + def on_cancel(self, code): + self.value = -1 + + def on_button(self, code): + self.value = 0 + self.d.End() + + +class _MacOneListW: + """A one list dialog for Macintosh. This class should not be called directly. Use the OneList function.""" + + def __init__(self, list, message='Make a selection'): + import W + self.list = list + self.selected = None + self.w = W.ModalDialog((200, 240)) + self.w.message = W.TextBox((10, 10, -10, 30), message) + self.w.list = W.List((10, 35, -10, -50), list) + self.w.l = W.HorizontalLine((10, -40, -10, 1), 1) + self.w.cancel = W.Button((10, -30, 87, -10), 'Cancel', self.cancel) + self.w.ok = W.Button((102, -30, 88, -10), 'OK', self.ok) + self.w.setdefaultbutton(self.w.ok) + self.w.bind('cmd.', self.w.cancel.push) + self.w.open() + + def ok(self): + if len(self.w.list.getselection()) == 1: + self.selected = self.w.list.getselection()[0] + self.w.close() + + def cancel(self): + self.selected = None + self.w.close() + +class _MacTwoChecksW: + """ Version using W """ + + def __init__(self, title_1, title_2, value1=1, value2=1, title='RoboFab'): + import W + self.check1 = value1 + self.check2 = value2 + self.w = W.ModalDialog((200, 100)) + self.w.check1 = W.CheckBox((10, 10, -10, 16), title_1, value=value1) + self.w.check2 = W.CheckBox((10, 35, -10, 16), title_2, value=value2) + self.w.l = W.HorizontalLine((10, 60, -10, 1), 1) + self.w.cancel = W.Button((10, 70, 85, 20), 'Cancel', self.cancel) + self.w.ok = W.Button((105, 70, 85, 20), 'OK', self.ok) + self.w.setdefaultbutton(self.w.ok) + self.w.bind('cmd.', self.w.cancel.push) + self.w.open() + + def ok(self): + self.check1 = self.w.check1.get() + self.check2 = self.w.check2.get() + self.w.close() + + def cancel(self): + self.check1 = None + self.check2 = None + self.w.close() + + +class ProgressBar: + def __init__(self, title='RoboFab...', ticks=0, label=''): + """ + A progress bar. + Availability: FontLab, Mac + """ + self._tickValue = 1 + + if inFontLab: + fl.BeginProgress(title, ticks) + elif MAC and hasEasyDialogs: + import EasyDialogs + self._bar = EasyDialogs.ProgressBar(title, maxval=ticks, label=label) + else: + _raisePlatformError('Progress') + + def getCurrentTick(self): + return self._tickValue + + + def tick(self, tickValue=None): + """ + Tick the progress bar. + Availability: FontLab, Mac + """ + if not tickValue: + tickValue = self._tickValue + + if inFontLab: + fl.TickProgress(tickValue) + elif MAC: + self._bar.set(tickValue) + else: + pass + + self._tickValue = tickValue + 1 + + def label(self, label): + """ + Set the label on the progress bar. + Availability: Mac + """ + if inFontLab: + pass + elif MAC: + self._bar.label(label) + else: + pass + + + def close(self): + """ + Close the progressbar. + Availability: FontLab, Mac + """ + if inFontLab: + fl.EndProgress() + elif MAC: + del self._bar + else: + pass + + +def SelectFont(message="Select a font:", title='RoboFab'): + """ + Returns font instance if there is one, otherwise it returns None. + Availability: FontLab + """ + from robofab.world import RFont + if inFontLab: + list = [] + for i in range(fl.count): + list.append(fl[i].full_name) + name = OneList(list, message, title) + if name is None: + return None + else: + return RFont(fl[list.index(name)]) + else: + _raisePlatformError('SelectFont') + +def SelectGlyph(font, message="Select a glyph:", title='RoboFab'): + """ + Returns glyph instance if there is one, otherwise it returns None. + Availability: FontLab + """ + from fontTools.misc.textTools import caselessSort + + if inFontLab: + tl = font.keys() + list = caselessSort(tl) + glyphname = OneList(list, message, title) + if glyphname is None: + return None + else: + return font[glyphname] + else: + _raisePlatformError('SelectGlyph') + +def FindGlyph(font, message="Search for a glyph:", title='RoboFab'): + """ + Returns glyph instance if there is one, otherwise it returns None. + Availability: FontLab + """ + + if inFontLab: + glyphname = SearchList(font.keys(), message, title) + if glyphname is None: + return None + else: + return font[glyphname] + else: + _raisePlatformError('SelectGlyph') + +def OneList(list, message="Select an item:", title='RoboFab'): + """ + Returns selected item, otherwise it returns None. + Availability: FontLab, Macintosh + """ + if inFontLab: + ol = _FontLabDialogOneList(list, message) + ol.Run() + selected = ol.selected + if selected is None: + return None + else: + try: + return list[selected] + except: + return None + elif MAC: + if hasW: + d = _MacOneListW(list, message) + sel = d.selected + if sel is None: + return None + else: + return list[sel] + else: + _raisePlatformError('OneList') + elif PC: + _raisePlatformError('OneList') + +def SearchList(list, message="Select an item:", title='RoboFab'): + """ + Returns selected item, otherwise it returns None. + Availability: FontLab + """ + if inFontLab: + sl = _FontLabDialogSearchList(list, message, title) + sl.run() + selected = sl.selected + if selected is None: + return None + else: + return selected + else: + _raisePlatformError('SearchList') + +def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'): + """ + Returns (value 1, value 2). + Availability: FontLab + """ + if inFontLab: + tf = _FontLabDialogTwoFields(title_1, value_1, title_2, value_2, title) + tf.Run() + try: + v1 = tf.v1 + v2 = tf.v2 + return (v1, v2) + except: + return None + else: + _raisePlatformError('TwoFields') + +def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'): + """ + Returns check value: + 1 if check box 1 is checked + 2 if check box 2 is checked + 3 if both are checked + 0 if neither are checked + None if cancel is clicked. + + Availability: FontLab, Macintosh + """ + tc = None + if inFontLab: + tc = _FontLabDialogTwoChecks(title_1, title_2, value1, value2, title) + tc.Run() + elif MAC: + if hasW: + tc = _MacTwoChecksW(title_1, title_2, value1, value2, title) + else: + _raisePlatformError('TwoChecks') + else: + _raisePlatformError('TwoChecks') + c1 = tc.check1 + c2 = tc.check2 + if c1 == 1 and c2 == 0: + return 1 + elif c1 == 0 and c2 == 1: + return 2 + elif c1 == 1 and c2 == 1: + return 3 + elif c1 == 0 and c2 == 0: + return 0 + else: + return None + +def Message(message, title='RoboFab'): + """ + A simple message dialog. + Availability: FontLab, Macintosh + """ + if inFontLab: + _FontLabDialogMessage(message, title).Run() + elif MAC: + import EasyDialogs + EasyDialogs.Message(message) + else: + _raisePlatformError('Message') + +def AskString(message, value='', title='RoboFab'): + """ + Returns entered string. + Availability: FontLab, Macintosh + """ + if inFontLab: + askString = _FontLabDialogAskString(message, value, title) + askString.Run() + v = askString.value + if v is None: + return None + else: + return v + elif MAC: + import EasyDialogs + askString = EasyDialogs.AskString(message) + if askString is None: + return None + if len(askString) == 0: + return None + else: + return askString + else: + _raisePlatformError('GetString') + +def AskYesNoCancel(message, title='RoboFab', default=0): + """ + Returns 1 for 'Yes', 0 for 'No' and -1 for 'Cancel'. + Availability: FontLab, Macintosh + ("default" argument only available on Macintosh) + """ + if inFontLab: + gync = _FontLabDialogGetYesNoCancel(message, title) + gync.Run() + v = gync.value + return v + elif MAC: + import EasyDialogs + gync = EasyDialogs.AskYesNoCancel(message, default=default) + return gync + else: + _raisePlatformError('GetYesNoCancel') + +def GetFile(message=None): + """ + Select file dialog. Returns path if one is selected. Otherwise it returns None. + Availability: FontLab, Macintosh, PC + """ + path = None + if MAC: + if haveMacfs: + fss, ok = macfs.PromptGetFile(message) + if ok: + path = fss.as_pathname() + else: + from robofab.interface.mac.getFileOrFolder import GetFile + path = GetFile(message) + elif PC: + if inFontLab: + if not message: + message = '' + path = fl.GetFileName(1, message, '', '') + else: + openFlags = win32con.OFN_FILEMUSTEXIST|win32con.OFN_EXPLORER + mode_open = 1 + myDialog = win32ui.CreateFileDialog(mode_open,None,None,openFlags) + myDialog.SetOFNTitle(message) + is_OK = myDialog.DoModal() + if is_OK == 1: + path = myDialog.GetPathName() + else: + _raisePlatformError('GetFile') + return path + +def GetFolder(message=None): + """ + Select folder dialog. Returns path if one is selected. Otherwise it returns None. + Availability: FontLab, Macintosh, PC + """ + path = None + if MAC: + if haveMacfs: + fss, ok = macfs.GetDirectory(message) + if ok: + path = fss.as_pathname() + else: + from robofab.interface.mac.getFileOrFolder import GetFileOrFolder + # This _also_ allows the user to select _files_, but given the + # package/folder dichotomy, I think we have no other choice. + path = GetFileOrFolder(message) + elif PC: + if inFontLab: + if not message: + message = '' + path = fl.GetPathName('', message) + else: + myTuple = shell.SHBrowseForFolder(0, None, message, 64) + try: + path = shell.SHGetPathFromIDList(myTuple[0]) + except: + pass + else: + _raisePlatformError('GetFile') + return path + +GetDirectory = GetFolder + +def PutFile(message=None, fileName=None): + """ + Save file dialog. Returns path if one is entered. Otherwise it returns None. + Availability: FontLab, Macintosh, PC + """ + path = None + if MAC: + if haveMacfs: + fss, ok = macfs.StandardPutFile(message, fileName) + if ok: + path = fss.as_pathname() + else: + import EasyDialogs + path = EasyDialogs.AskFileForSave(message, savedFileName=fileName) + elif PC: + if inFontLab: + if not message: + message = '' + if not fileName: + fileName = '' + path = fl.GetFileName(0, message, fileName, '') + else: + openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_EXPLORER + mode_save = 0 + myDialog = win32ui.CreateFileDialog(mode_save, None, fileName, openFlags) + myDialog.SetOFNTitle(message) + is_OK = myDialog.DoModal() + if is_OK == 1: + path = myDialog.GetPathName() + else: + _raisePlatformError('GetFile') + return path + + +if __name__=='__main__': + import traceback + + print "dialogs hasW", hasW + print "dialogs hasDialogKit", hasDialogKit + print "dialogs MAC", MAC + print "dialogs PC", PC + print "dialogs inFontLab", inFontLab + print "dialogs hasEasyDialogs", hasEasyDialogs + + def tryDialog(dialogClass, args=None): + print + print "tryDialog:", dialogClass, "with args:", args + try: + if args is not None: + apply(dialogClass, args) + else: + apply(dialogClass) + except: + traceback.print_exc(limit=0) + + tryDialog(TwoChecks, ('hello', 'world', 1, 0, 'ugh')) + tryDialog(TwoFields) + tryDialog(TwoChecks, ('hello', 'world', 1, 0, 'ugh')) + tryDialog(OneList, (['a', 'b', 'c'], 'hello world')) + tryDialog(Message, ('hello world',)) + tryDialog(AskString, ('hello world',)) + tryDialog(AskYesNoCancel, ('hello world',)) + + try: + b = ProgressBar('hello', 50, 'world') + for i in range(50): + if i == 25: + b.label('ugh.') + b.tick(i) + b.close() + except: + traceback.print_exc(limit=0) diff --git a/misc/pylib/robofab/interface/all/dialogs_mac_vanilla.py b/misc/pylib/robofab/interface/all/dialogs_mac_vanilla.py new file mode 100644 index 000000000..d4b76f9c7 --- /dev/null +++ b/misc/pylib/robofab/interface/all/dialogs_mac_vanilla.py @@ -0,0 +1,267 @@ +""" + + Dialogs for environments that support cocao / vanilla. + +""" + +import vanilla.dialogs +from AppKit import NSApp, NSModalPanelWindowLevel, NSWindowCloseButton, NSWindowZoomButton, NSWindowMiniaturizeButton + +__all__ = [ + "AskString", + "AskYesNoCancel", + "FindGlyph", + "GetFile", + "GetFileOrFolder", + "GetFolder", + "Message", + "OneList", + "PutFile", + "SearchList", + "SelectFont", + "SelectGlyph", +# "TwoChecks", +# "TwoFields", + "ProgressBar", +] + +class _ModalWindow(vanilla.Window): + + nsWindowLevel = NSModalPanelWindowLevel + + def __init__(self, *args, **kwargs): + super(_ModalWindow, self).__init__(*args, **kwargs) + self._window.standardWindowButton_(NSWindowCloseButton).setHidden_(True) + self._window.standardWindowButton_(NSWindowZoomButton).setHidden_(True) + self._window.standardWindowButton_(NSWindowMiniaturizeButton).setHidden_(True) + + def open(self): + super(_ModalWindow, self).open() + self.center() + NSApp().runModalForWindow_(self._window) + + def windowWillClose_(self, notification): + super(_ModalWindow, self).windowWillClose_(notification) + NSApp().stopModal() + + +class _baseWindowController(object): + + def setUpBaseWindowBehavior(self): + self._getValue = None + + self.w.okButton = vanilla.Button((-70, -30, -15, 20), "OK", callback=self.okCallback, sizeStyle="small") + self.w.setDefaultButton(self.w.okButton) + + self.w.closeButton = vanilla.Button((-150, -30, -80, 20), "Cancel", callback=self.closeCallback, sizeStyle="small") + self.w.closeButton.bind(".", ["command"]) + self.w.closeButton.bind(unichr(27), []) + + self.cancelled = False + + def okCallback(self, sender): + self.w.close() + + def closeCallback(self, sender): + self.cancelled = True + self.w.close() + + def get(self): + raise NotImplementedError + + +class _AskStringController(_baseWindowController): + + def __init__(self, message, value, title): + self.w = _ModalWindow((370, 110), title) + + self.w.infoText = vanilla.TextBox((15, 10, -15, 22), message) + self.w.input = vanilla.EditText((15, 40, -15, 22)) + self.w.input.set(value) + + self.setUpBaseWindowBehavior() + self.w.open() + + def get(self): + if self.cancelled: + return None + return self.w.input.get() + + +class _listController(_baseWindowController): + + def __init__(self, items, message, title, showSearch=False): + + self.items = items + + self.w = _ModalWindow((350, 300), title) + y = 10 + self.w.infoText = vanilla.TextBox((15, y, -15, 22), message) + y += 25 + if showSearch: + self.w.search = vanilla.SearchBox((15, y, -15, 22), callback=self.searchCallback) + y += 25 + self.w.itemList = vanilla.List((15, y, -15, -40), self.items, allowsMultipleSelection=False) + + self.setUpBaseWindowBehavior() + self.w.open() + + def searchCallback(self, sender): + search = sender.get() + + newItems = [item for item in self.items if repr(item).startswith(search)] + self.w.itemList.set(newItems) + if newItems: + self.w.itemList.setSelection([0]) + + def get(self): + index = self.w.itemList.getSelection() + if index: + index = index[0] + return self.w.itemList[index] + return None + + +def AskString(message, value='', title='RoboFab'): + """ + AskString Dialog + + message the string + value a default value + title a title of the window (may not be supported everywhere) + """ + w = _AskStringController(message, value, title) + return w.get() + +def AskYesNoCancel(message, title='RoboFab', default=0, informativeText=""): + """ + AskYesNoCancel Dialog + + message the string + title* a title of the window + (may not be supported everywhere) + default* index number of which button should be default + (i.e. respond to return) + informativeText* A string with secundary information + + * may not be supported everywhere + """ + return vanilla.dialogs.askYesNoCancel(messageText=message, informativeText=informativeText) + +def FindGlyph(aFont, message="Search for a glyph:", title='RoboFab'): + items = aFont.keys() + items.sort() + w = _listController(items, message, title, showSearch=True) + glyphName = w.get() + if glyphName is not None: + return aFont[glyphName] + return None + +def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None): + result = vanilla.dialogs.getFile(messageText=message, title=title, directory=directory, fileName=fileName, allowsMultipleSelection=allowsMultipleSelection, fileTypes=fileTypes) + if result is None: + return None + if not allowsMultipleSelection: + return str(list(result)[0]) + else: + return [str(n) for n in list(result)] + +def GetFolder(message=None, title=None, directory=None, allowsMultipleSelection=False): + result = vanilla.dialogs.getFolder(messageText=message, title=title, directory=directory, allowsMultipleSelection=allowsMultipleSelection) + if result is None: + return None + if not allowsMultipleSelection: + return str(list(result)[0]) + else: + return [str(n) for n in list(result)] + +def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None): + result = vanilla.dialogs.getFileOrFolder(messageText=message, title=title, directory=directory, fileName=fileName, allowsMultipleSelection=allowsMultipleSelection, fileTypes=fileTypes) + if result is None: + return None + if not allowsMultipleSelection: + return str(list(result)[0]) + else: + return [str(n) for n in list(result)] + +def Message(message, title='RoboFab', informativeText=""): + vanilla.dialogs.message(messageText=message, informativeText=informativeText) + +def OneList(items, message="Select an item:", title='RoboFab'): + w = _listController(items, message, title, showSearch=False) + return w.get() + +def PutFile(message=None, fileName=None): + return vanilla.dialogs.putFile(messageText=message, fileName=fileName) + +def SearchList(list, message="Select an item:", title='RoboFab'): + w = _listController(list, message, title, showSearch=True) + return w.get() + +def SelectFont(message="Select a font:", title='RoboFab', allFonts=None): + if allFonts is None: + from robofab.world import AllFonts + fonts = AllFonts() + else: + fonts = allFonts + + data = dict() + for font in fonts: + data["%s" %font] = font + + items = data.keys() + items.sort() + w = _listController(items, message, title, showSearch=False) + value = w.get() + return data.get(value, None) + +def SelectGlyph(aFont, message="Select a glyph:", title='RoboFab'): + items = aFont.keys() + items.sort() + w = _listController(items, message, title, showSearch=False) + glyphName = w.get() + if glyphName is not None: + return aFont[glyphName] + return None + +def TwoChecks(title_1="One", title_2="Two", value1=1, value2=1, title='RoboFab'): + raise NotImplementedError + +def TwoFields(title_1="One:", value_1="0", title_2="Two:", value_2="0", title='RoboFab'): + raise NotImplementedError + + +class ProgressBar(object): + def __init__(self, title="RoboFab...", ticks=None, label=""): + self.w = vanilla.Window((250, 60), title) + if ticks is None: + isIndeterminate = True + ticks = 0 + else: + isIndeterminate = False + self.w.progress = vanilla.ProgressBar((15, 15, -15, 12), maxValue=ticks, isIndeterminate=isIndeterminate, sizeStyle="small") + self.w.text = vanilla.TextBox((15, 32, -15, 14), label, sizeStyle="small") + self.w.progress.start() + self.w.center() + self.w.open() + + def close(self): + self.w.progress.stop() + self.w.close() + + def getCurrentTick(self): + return self.w.progress.get() + + def label(self, label): + self.w.text.set(label) + self.w.text._nsObject.display() + + def tick(self, tickValue=None): + if tickValue is None: + self.w.progress.increment() + else: + self.w.progress.set(tickValue) + + +if __name__ == "__main__": + pass
\ No newline at end of file diff --git a/misc/pylib/robofab/interface/mac/__init__.py b/misc/pylib/robofab/interface/mac/__init__.py new file mode 100755 index 000000000..15f7d59c5 --- /dev/null +++ b/misc/pylib/robofab/interface/mac/__init__.py @@ -0,0 +1,10 @@ +""" + +Directory for interface related modules. +Stuff for MacOSX, widgets, quartz + +""" + + + + diff --git a/misc/pylib/robofab/interface/mac/getFileOrFolder.py b/misc/pylib/robofab/interface/mac/getFileOrFolder.py new file mode 100755 index 000000000..da7edff61 --- /dev/null +++ b/misc/pylib/robofab/interface/mac/getFileOrFolder.py @@ -0,0 +1,80 @@ +"""This module provides two functions, very similar to +EasyDialogs.AskFileForOpen() and EasyDialogs.AskFolder(): GetFile() and +GetFileOrFolder(). The main difference is that the functions here fully +support "packages" or "bundles", ie. folders that appear to be files in +the finder and open/save dialogs. The second difference is that +GetFileOrFolder() allows the user to select a file _or_ a folder. +""" + + +__all__ = ["GetFile", "GetFileOrFolder"] + + +from EasyDialogs import _process_Nav_args, _interact +import Nav +import Carbon.File + + +# Lots of copy/paste from EasyDialogs.py, for one because althought the +# EasyDialogs counterparts take a million options, they don't take the +# one option I need: the flag to support packages... + +kNavSupportPackages = 0x00001000 + + +def GetFile(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None): + """Ask the user to select a file. + + Some of these arguments are not supported: + title, directory, fileName, allowsMultipleSelection and fileTypes are here for compatibility reasons. + """ + default_flags = 0x56 | kNavSupportPackages + args, tpwanted = _process_Nav_args(default_flags, message=message) + _interact() + try: + rr = Nav.NavChooseFile(args) + good = 1 + except Nav.error, arg: + if arg[0] != -128: # userCancelledErr + raise Nav.error, arg + return None + if not rr.validRecord or not rr.selection: + return None + if issubclass(tpwanted, Carbon.File.FSRef): + return tpwanted(rr.selection_fsr[0]) + if issubclass(tpwanted, Carbon.File.FSSpec): + return tpwanted(rr.selection[0]) + if issubclass(tpwanted, str): + return tpwanted(rr.selection_fsr[0].as_pathname()) + if issubclass(tpwanted, unicode): + return tpwanted(rr.selection_fsr[0].as_pathname(), 'utf8') + raise TypeError, "Unknown value for argument 'wanted': %s" % repr(tpwanted) + + +def GetFileOrFolder(message=None, title=None, directory=None, fileName=None, allowsMultipleSelection=False, fileTypes=None): + """Ask the user to select a file or a folder. + + Some of these arguments are not supported: + title, directory, fileName, allowsMultipleSelection and fileTypes are here for compatibility reasons. + """ + default_flags = 0x17 | kNavSupportPackages + args, tpwanted = _process_Nav_args(default_flags, message=message) + _interact() + try: + rr = Nav.NavChooseObject(args) + good = 1 + except Nav.error, arg: + if arg[0] != -128: # userCancelledErr + raise Nav.error, arg + return None + if not rr.validRecord or not rr.selection: + return None + if issubclass(tpwanted, Carbon.File.FSRef): + return tpwanted(rr.selection_fsr[0]) + if issubclass(tpwanted, Carbon.File.FSSpec): + return tpwanted(rr.selection[0]) + if issubclass(tpwanted, str): + return tpwanted(rr.selection_fsr[0].as_pathname()) + if issubclass(tpwanted, unicode): + return tpwanted(rr.selection_fsr[0].as_pathname(), 'utf8') + raise TypeError, "Unknown value for argument 'wanted': %s" % repr(tpwanted) diff --git a/misc/pylib/robofab/interface/win/__init__.py b/misc/pylib/robofab/interface/win/__init__.py new file mode 100755 index 000000000..fe360c4ea --- /dev/null +++ b/misc/pylib/robofab/interface/win/__init__.py @@ -0,0 +1,10 @@ +""" + +Directory for interface related modules. +Stuff for Windows + +""" + + + + |