def show(self, text_or_file='', font=None, timeout=None): '''Show alert message iff not suppressed. @param text_or_file: The contents (C{str} or C{file}). @keyword font: Optional font (L{Font}), default C{Fonts.MonoSpace}. @keyword timeout: Optional time limit (C{float}). @return: The button clicked (C{PanelButton.Close}) or C{PanelButton.TimedOut} if the I{timeout} expired. @raise ValueError: No I{text_or_file} given. ''' ns = NSAlert.alloc().init() ns.setAlertStyle_(AlertStyle.Info) ns.addButtonWithTitle_(release(NSStr('Close'))) if not text_or_file: raise ValueError('no %s: %r' % ('text_or_file', text_or_file)) text, t = _text_title2(text_or_file, self.title) if t: ns.setMessageText_(release(NSStr(t))) t = nsTextView( text, NSFont.userFixedPitchFontOfSize_(0) if font is None else font.NS) ns.setAccessoryView_(t) r = _runModal(ns, timeout) ns.release() return r
def nsTextView(text, ns_font): '''Return an C{NSTextView} for the given text string. ''' # <http://Developer.Apple.com/documentation/appkit/ # nsalert/1530575-accessoryview> w, h, n = nsTextSize3(text, ns_font=ns_font) if n > 50: r = NSRect4_t(0, 0, max(300, w), min(800, h)) else: # make sure the frame is tall enough to avoid overwritten text r = NSRect4_t(0, 0, 300, max(20, min(800, h))) # XXX key NSFontAttributeName has a NSString value, no longer a Font? # d = NSDictionary.dictionaryWithObject_forKey_(ns_font, NSStr('NSFontAttributeName')) # t = NSAttributedString.alloc().initWithString_attributes_(NSStr(text), d) ns = NSTextView.alloc().initWithFrame_(r) ns.setFont_(ns_font) # XXX set font BEFORE text ns.insertText_(release(NSStr(text))) ns.setEditable_(NO) ns.setDrawsBackground_(NO) if n > 50: # force scroll view ns.setVerticallyResizable_(YES) ns.setHorizontallyResizable_(YES) r.size.width = min(600, r.size.width) sv = NSScrollView.alloc().initWithFrame_(r) sv.setHasVerticalScroller_(YES) sv.setHasHorizontalScroller_(YES) sv.setAutohidesScrollers_(YES) sv.setBorderType_(2) # Border.Bezel or NSBezelBorder sv.setDocumentView_(ns) ns = sv else: ns.sizeToFit() return ns
def app_title(title): '''Get/set the app title. @param title: New title (C{str}). @return: Previous title (C{str}). ''' return nsBundleRename(release(NSStr(title)))
def title(self, title): '''Set the title. ''' if isinstance(title, NSStr): try: self.NS.setTitle_(title) except AttributeError: # no NSApplication.setTitle_ pass title = nsString2str(title) else: try: t = NSStr(title) self.NS.setTitle_(t) release(t) except AttributeError: # no NSApplication.setTitle_ t.release() self._title = bytes2str(title)
def size2(self, bstr): '''Get the size of a string. @param bstr: The string (C{str}, C{bytes} or L{Str}). @return: 2-Tuple (width, height) in (C{float} or C{int}). ''' if isinstance(bstr, Str): ns = bstr.NS elif isinstance(bstr, _ByteStrs): ns = release(NSStr(bstr)) elif isinstanceOf(bstr, NSStr, name='bstr'): ns = bstr return flint(self.NS.widthOfString_(ns)), self.height
def nsTextSize3(text, ns_font=None): '''Return the size of a multi-line text. @param text: The text (C{str}), including C{linesep}arators. @keyword ns_font: The text font (C{NSFont}) or C{None}. @return: 3-Tuple (width, height, lines) in (pixels, pixels) or in (characters, lines, lines) if I{ns_font} is C{None}. ''' w = '' for t in text.split(linesep): if len(t) > len(w): w = t h = n = text.count(linesep) + 1 if ns_font: h *= NSMain.LayoutManager.defaultLineHeightForFont_(ns_font) w = ns_font.widthOfString_(release(NSStr(w))) else: w = len(w) return w, h, n
def url2NS(py, url2=None): '''Create an C{NSURL} instance from a Python string. @param py: The URL (C{str} or C{unicode}). @keyword url2: Optionally, relative to this URL (C{str} or C{unicode}). @return: The ObjC instance (C{NSURL}). @see: U{URL<http://Developer.Apple.com/documentation/foundation/url>} for parsing an C{NSURL}. ''' ns = release(NSStr(py)) if ':' in bytes2str(py): if url2: return NSURL.alloc().initWithString_relativeToURL_(ns, url2NS(url2)) else: return NSURL.alloc().initWithString_(ns) elif url2: return NSURL.alloc().initFileURLWithPath_relativeToURL_(ns, url2NS(url2)) else: return NSURL.alloc().initFileURLWithPath_(ns)
def isAlias(path): '''Resolve a macOS file or folder alias. @param path: The alias name (C{str} or L{NSStr}). @return: The alias' target (C{str}) or C{None} if I{path} isn't a macOS alias. @see: U{mac-alias<http://GitHub.com/al45tair/mac_alias>} and U{here<http://StackOverflow.com/questions/21150169>}. ''' if isinstance(path, _ByteStrs): path = release(NSStr(path)) elif isinstanceOf(path, NSStr, name='path'): pass u = NSURL.alloc().initFileURLWithPath_(path) r = cfURLResolveAlias(u) # URL_t u.release() if r: u = ObjCInstance(r) # URL_t to NSURL r = cfString2str(u.path()) u.release() return r
def label(self, label): '''Set the badge text of the app's dock tile (C{str}). ''' self._label = bytes2str(label) self.NS.setBadgeLabel_(release(NSStr(label))) self.NS.display()
def __init__(self, text_or_file, font=None, title='Text', fraction=0.5, **kwds): '''Create a L{TextWindow}. @param text_or_file: The contents (C{str} or C{file}). @keyword font: Optional font (L{Font}), default C{Fonts.MonoSpace}. @keyword title: Window name or title (C{str}). @keyword fraction: Window size as fraction of the screen (C{float}). @keyword kwds: Optional, additional keyword arguments, see L{Window}. ''' text, t = _text_title2(text_or_file, title) super(TextWindow, self).__init__(title=t, frame=Screen(fraction), **kwds) if font is None: f = NSFont.userFixedPitchFontOfSize_(12) else: f = font.NS w, _, _ = nsTextSize3(text, f) # <http://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/TextUILayer/Tasks/CreateTextViewProg.html> # <http://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html> ns = self.NS cr = ns.contentView().frame() hs = w > cr.size.width sv = NSScrollView.alloc().initWithFrame_(cr) sv.setBorderType_(Border.No) if hs: sv.setHasHorizontalScroller_(YES) sv.setAutoresizingMask_(AutoResize.Sizable) else: sv.setHasHorizontalScroller_(NO) sv.setAutoresizingMask_(AutoResize.WidthSizable) sv.setHasVerticalScroller_(YES) tv = NSTextView.alloc().initWithFrame_(cr) tv.setMaxSize_(NSSize_t(NSIntegerMax, NSIntegerMax)) tv.setMinSize_(NSSize_t(16, cr.size.height)) tc = tv.textContainer() if hs: tv.setHorizontallyResizable_(YES) tv.setAutoresizingMask_(AutoResize.Sizable) tc.setContainerSize_(NSSize_t(NSIntegerMax, NSIntegerMax)) # FLT_MAX tc.setWidthTracksTextView_(NO) # YES? else: tv.setHorizontallyResizable_(NO) tv.setAutoresizingMask_(AutoResize.WidthSizable) tc.setContainerSize_(NSSize_t(cr.size.width, NSIntegerMax)) # FLT_MAX tc.setWidthTracksTextView_(YES) # NO? tv.setVerticallyResizable_(YES) tv.setFont_(f) # XXX set font BEFORE text tv.insertText_(release(NSStr(text))) tv.setEditable_(NO) tv.setDrawsBackground_(NO) self.NSView = sv # == ns.setContentView_(sv) self.PMview = tv # XXX or sv? ns.makeKeyAndOrderFront_(None) ns.makeFirstResponder_(tv)
def save_as( self, name='', filetype='', # PYCHOK expected dir='', hidden=False, hidexts=False, label='', packages=False, prompt='', tags=(), dflt=None): '''Specify a file name in the panel. @keyword name: A suggested file name (C{str}), default "Untitled". @keyword filetype: The file type (C{str}). @keyword dir: The directory (C{str}). @keyword hidden: Show hidden files (C{bool}). @keyword hidexts: Hide file extensions (C{bool}). @keyword label: The name label (C{str}), default "Save As:". @keyword packages: Treat file packages as directories (C{bool}). @keyword prompt: The button label (C{str}), default "Save". @keyword tags: Suggested tag names (C{tuple} of C{str}-s). @keyword dflt: Return value, cancelled (C{None}). @return: The specified file name path (C{str}) or I{dflt}. ''' ns = NSSavePanel.savePanel() # ns.setTitleHidden_(bool(False)) # "does nothing now" if name: ns.setNameFieldStringValue_(release(NSStr(name))) if dir: if dir.lower().startswith('file:///'): ns.setDirectoryURL_(release(NSStr(dir))) else: ns.setDirectory_(release(NSStr(dir))) if filetype: ns.setRequiredFileType_(release(NSStr(filetype.lstrip('.')))) hidexts = False ns.setShowsHiddenFiles_(YES if hidden else NO) # ns.setCanSelectHiddenExtension_(bool(hidden)) ns.setExtensionHidden_(YES if hidexts else NO) if label: ns.setNameFieldLabel_(release(NSStr(label))) ns.setTreatsFilePackagesAsDirectories_(YES if packages else NO) if prompt: ns.setPrompt_(release(NSStr(prompt))) if tags: ns.setTagNames_(py2NS(tags)) ns.setShowsTagField_(True) else: ns.setShowsTagField_(False) while True: r = _runModal(ns) # == runModalForDirectory_file_(None, None) if r == NSOKButton: r = nsString2str(ns.filename()) # == ns.URL().path() break elif r == NSCancelButton: r = dflt break # ns.release() # XXX may crash on Cancel return r
def pick(self, filetypes, aliases=False, dirs=False, files=True, hidden=False, hidexts=False, multiple=False, packages=False, prompt='', otherOK=False, dflt=None): '''Select a file from the panel. @param filetypes: The selectable file types (tuple of str-s). @keyword aliases: Allow selection of aliases (C{bool}). @keyword dirs: Allow selection of directories (C{bool}). @keyword hidden: Allow selection of hidden files (C{bool}). @keyword hidexts: Hide file extensions (C{bool}). @keyword multiple: Allow selection of multiple files (C{bool}). @keyword packages: Treat file packages as directories (C{bool}). @keyword prompt: The button label (C{str}), default "Open". @keyword otherOK: Allow selection of other file types (C{bool}). @keyword dflt: Return value, if cancelled, nothing selected (C{None}). @return: The selected file name path (C{str}) or I{dflt}. ''' if multiple: # setAllowsMultipleSelection_ raise NotImplementedError('multiple %s' % (multiple, )) ns = NSOpenPanel.openPanel() # ns.setTitleHidden_(NO) # "does nothing now" ns.setResolvesAliases_(YES if aliases else NO) ns.setCanChooseDirectories_(YES if dirs else NO) ns.setCanChooseFiles_(YES if files else NO) ns.setShowsHiddenFiles_(YES if hidden else NO) # ns.setCanSelectHiddenExtension_(YES if hidden else NO) ns.setExtensionHidden_(YES if hidexts else NO) # ns.setRequiredFileType_(NSStr) if filetypes: # an NSArray of file extension NSStr[ing]s without the '.' ns.setAllowedFileTypes_(py2NS(t.lstrip('.') for t in filetypes)) ns.setAllowsOtherFileTypes_(YES if otherOK else NO) ns.setTreatsFilePackagesAsDirectories_(YES if packages else NO) if prompt: ns.setPrompt_(release(NSStr(prompt))) while True: # ns.orderFrontRegardless() # only flashes # <http://Developer.Apple.com/documentation/ # appkit/nssavepanel/1525357-runmodal> if ns.runModal() == NSCancelButton: # runModalForTypes_ path = dflt # nothing selected break # paths = ns.filenames() # returns an NSArray # urls = ns.URLs() # returns an NSArray path = nsString2str(ns.filename()) # == ns.URL().path() # mimick NSOpenPanel.setAllowedFileTypes_ if path.lower().endswith(filetypes): break # ns.release() # XXX crashes Cancel pick return path
def show(self, text='', font=None, timeout=None): '''Show alert message iff not suppressed. @keyword text: Optional, accessory text (C{str}). @keyword font: Optional font (L{Font}), default C{Fonts.System}. @keyword timeout: Optional time limit (C{float}). @return: The button clicked (C{PanelButton}). If C{PanelButton.Suppressed} is returned, the alert panel was not shown since it was suppressed due to a previous selection of the corresponding check box. C{PanelButton.TimedOut} is returned if no button was clicked before the I{timeout} expired. ''' # <http://Developer.Apple.com/documentation/appkit/nsalert> ns = NSAlert.alloc().init() ns.setAlertStyle_(self._style) ns.setMessageText_(release(NSStr(self.title))) if self._info: # <http://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/Strings/Articles/stringsParagraphBreaks.html> ns.setInformativeText_(NSStr(self._info)) ns.addButtonWithTitle_(release(NSStr(self._ok))) if self._cancel: ns.addButtonWithTitle_(release(NSStr(self._cancel))) if self._other: ns.addButtonWithTitle_(release(NSStr(self._other))) if self._suppress in (False, YES): self._suppress = False ns.setShowsSuppressionButton_(YES) s = _AlertStyleStr.get(self._style, '') s = 'Do not show this %sAlert again' % (s, ) ns.suppressionButton().setTitle_(release(NSStr(s))) # <http://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/Dialog/Tasks/DisplayAlertHelp.html> # ns.showsHelp_(YES) # ns.helpAnchor_(HTML?) if text: t = nsTextView( text, NSFont.systemFontOfSize_(0) if font is None else font.NS) ns.setAccessoryView_(t) # <http://Developer.Apple.com/documentation/appkit/ # nsalert/1535196-showssuppressionbutton> if self._suppress is None: r = _runModal(ns, timeout) elif self._suppress is False: s = ns.suppressionButton().state() r = _runModal(ns, timeout) # XXX value of NSOnState? if ns.suppressionButton().state() != s: self._suppress = True else: r = PanelButton.Suppressed # ns.release() # XXX may crash return r
def __init__(self, family_or_font, size=0, traits=0, weight=5): '''New L{Font}. @param family_or_font: Generic font name (C{str}, L{Str}, L{NSStr}) like "Times" or "Helvetica" or a L{Font}, C{NSFont} or C{NSFontDescriptor} instance. @keyword size: Desired point size (C{int}), zero for any. @keyword traits: Desired font traits (C{str} or C{FontTrait}C{s mask}). @keyword weigth: Desired book weight (C{int}) in range 0..15, where 0=light, 5=regular, 9=bold and 15=heavy. @raise FontError: No such I{family_or_font}. @raise FontTraitError: Mutually exclusive I{traits}. @raise TypeError: Invalid I{family_or_font}. @raise ValueError: Invalid I{weight}. @note: The new L{Font} may not exhibit the desired I{traits} and I{weight}. The I{weight} is ignored if I{traits} include C{FontTrait.Bold}, both I{traits} and I{weight} are ignored if I{family_or_font} is C{NSFontDescriptor}. @see: Function L{fontsof} to obtain all available fonts of a particular font family. ''' if isinstance(family_or_font, Str): ns, py = family_or_font.NS, str(family_or_font) elif isinstance(family_or_font, _ByteStrs): ns, py = release(NSStr(family_or_font)), bytes2str(family_or_font) elif isinstance(family_or_font, NSStr): ns, py = family_or_font, nsString2str(family_or_font) # elif isObjCInstanceOf(family_or_font, NSFontDescriptor): # <http://Developer.Apple.com/documentation/appkit/nsfont/1525386-init> # ignore traits and weight # ns, py = NSFont.alloc().init_(family_or_font, size), None elif isObjCInstanceOf(family_or_font, NSFont, name='family_or_font'): ns, py = family_or_font, None if size == 0: size = ns.pointSize() if traits == 0: traits = NSMain.FontManager.traitsOfFont_(ns) if not (size == ns.pointSize() and traits == NSMain.FontManager.traitsOfFont_(ns)): ns = ns.familyName() py = nsString2str(ns) if py is not None: # <http://Developer.Apple.com/documentation/appkit/ # nsfontmanager/1462332-fontwithfamily> self._traits = _traitsin(traits) self._weight = _weightin(weight) ns = NSMain.FontManager.fontWithFamily_traits_weight_size_( ns, self._traits, self._weight, size) if isNone(ns): self._family = py self._size = flint(size) raise FontError('no such %s: %s' % ('font', self._argstr())) self._NS = ns # _RO # <http://Developer.Apple.com/library/content/documentation/ # TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html> self._family = nsString2str(ns.familyName()) self._height = flint(NSMain.LayoutManager.defaultLineHeightForFont_(ns) + 1) self._name = nsString2str(ns.fontName()) self._size = flint(ns.pointSize()) # traits not always reflect actual traits self._traits = NSMain.FontManager.traitsOfFont_(ns) or 0 # update with the family traits, if any self._traits |= _traitsin(self._family, raiser=False) if ns.isFixedPitch() and not self.isMonoSpace: self._traits |= FontTrait.MonoSpace self._weight = NSMain.FontManager.weightOfFont_(ns)