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 __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, fraction=fraction, **kwds) if font is None: f = NSFont.userFixedPitchFontOfSize_(12) else: f = font.NS w, _, _ = nsTextSize3(text, f) # <https://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/TextUILayer/Tasks/CreateTextViewProg.html> # <https://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html> ns = self.NS cf = ns.contentView().frame() hs = w > cf.size.width sv = NSScrollView.alloc().initWithFrame_(cf) 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_(cf) tv.setMaxSize_(NSSize_t(NSIntegerMax, NSIntegerMax)) tv.setMinSize_(NSSize_t(16, cf.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(cf.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 unicode2NS(py): '''Create an C{NSStr} instance from a Python C{unicode} string. @param py: The value (C{unicode}). @return: The ObjC instance (C{NSStr}). ''' return NSStr(py.encode(DEFAULT_UNICODE)) # .stringWithUTF8String_
def str2NS(py): '''Create an C{NSStr} instance from a Python C{str}. @param py: The value (C{str}). @return: The ObjC instance (C{NSStr}). ''' return NSStr(py)
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 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 open(self, url, tab=False): '''Open a new window or tab in the browser. @param url: The URL to open (C{str}). @keyword tab: New tab (C{bool}), new window otherwise. @return: Parsed I{url} as C{ParseResult}. @raise ValueError: Scheme of I{url} not 'http', 'https' or 'file'. ''' ns = url2NS(url) sc = nsString2str(ns.scheme()) if sc.lower() not in ('http', 'https', 'file'): raise ValueError('%s scheme %r invalid: %r' % ('url', sc, url)) if self._browser: self._browser.open(url, new=2 if tab else 1) elif self.NS: d = dict2NS(dict(URL=ns, reveal=True, newTab=bool(tab)), frozen=True) u = NSStr('WebBrowserOpenURLNotification') self.NS.postNotificationName_object_userInfo_(u, None, d) u.release() # PYCHOK expected return _urlparse(nsString2str(ns.absoluteString()))
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 __init__(self, name='Custom', ID=_NN_, width=612, height=792, margins=None, printer=None): '''New L{PaperCustom} from paper attributes. @raise TypeError: Invalid I{margins} or I{printer}. ''' h = float(height) w = float(width) z = 'x'.join(map(zfstr, (w, h))) if name: n = str(name) i = str(ID) or '%s %s' % (name, z) else: n = z i = str(ID) or z if margins is None: m = PaperMargins() elif isinstanceOf(margins, PaperMargins, name='margins'): m = PaperMargins(margins) if printer is None: p = get_printer() elif isinstanceOf(printer, Printer, name='printer'): p = printer pm = PMPaper_t() self._libPCcall(libPC.PMPaperCreateCustom, p, NSStr(n), NSStr(i), w, h, m, byref(pm)) Paper.__init__(self, pm)
def _nsPrinter(name, pm): '''(INTERNAL) New C{NSPrinter} instance. ''' if isinstanceOf(pm, PMPrinter_t, name='pm'): # NSStr(name) ns = send_message('NSPrinter', 'alloc', restype=Id_t) # _initWithName:printer:(Id_t, Id_t, SEL_t, Id_t, LP_Struct_t) # requires special selector handling due to leading underscore ns = send_message(ns, '_initWithName:printer:', NSStr(name), pm, restype=Id_t, argtypes=[Id_t, PMPrinter_t]) else: ns = None return ns
def tableView_objectValueForTableColumn_row_(self, table, col, row): # table is the NSTableView created above, # row is the row number, but col is an # NSTableColumn instance, not an index # (and col.identifier must be an NSStr). try: r = self.rows[row] if r in (None, ()): # XXX reduce the height of row separator? # <https://Developer.Apple.com/library/content/samplecode/ # CocoaTipsAndTricks/Listings/TableViewVariableRowHeights_ # TableViewVariableRowHeightsAppDelegate_m.html> return _NS.BlankCell c = self.id2i[col.identifier()] # **) return an NSStr, always return r[c] if 0 <= c < len(r) else _NS.EmptyCell except (IndexError, KeyError): # TypeError, ValueError c = col.identifier() return release(NSStr('[C%r, R%s]' % (c, row)))
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<https://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 printView(self, PMview, toPDF=_NN_, wait=True): '''Print an ObjC C{NSView} or C{PMview}. @param PMview: The ObjC view to print (C{NSView} or C{PMview}). @keyword toPDF: Save as PDF file name (C{str}). @keyword wait: Wait for print completion (C{bool}). @return: C{True} if printing succeeded, C{False} if printing failed or C{None} if ignored. @raise PrintError: If I{toPDF} file exists. @raise TypeError: Invalid I{PMview}. ''' if PMview and isObjCInstanceOf( PMview, NSImageView, NSTableView, NSTextView, name='PMview'): pi = NSMain.PrintInfo if not self.isDefault: pi = NSPrintInfo.alloc().initWithDictionary_(pi.dictionary()) pi.setPrinter_(self.NS) # NSPrinter if toPDF: if os.access(toPDF, os.F_OK): raise PrintError('%s exists: %r' % ('PDF file', toPDF)) # <https://Developer.Apple.com/documentation/appkit/ # nsprintoperation/1534130-pdfoperationwithview> po = NSPrintOperation.PDFOperationWithView_insideRect_toPath_printInfo_( PMview, PMview.frame(), NSStr(toPDF), pi) else: # <https://StackOverflow.com/questions/6452144/ # how-to-make-a-print-dialog-with-preview-for-printing-an-image-file> po = NSPrintOperation.printOperationWithView_printInfo_( PMview, pi) if not wait: po.setCanSpawnSeparateThread_(YES) return True if po.runOperation() else False else: return None
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 _nsFontsOf(family): t = NSStr(family) r = NSMain.FontManager.availableMembersOfFontFamily_(t) t.release() # PYCHOK expected 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): # <https://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: # <https://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 # <https://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)
class _NS(object): '''(INTERNAL) Singletons. ''' BlankCell = retain(NSStr('')) EmptyCell = retain(NSStr('-'))
def display(self, title, width=400, height=300): '''Show the table in a scrollable window. @param title: Window title (C{str}). @keyword width: Window frame width (C{int} or C{float}). @keyword height: Window frame height (C{int} or C{float}). @return: The window (L{TableWindow}). @raise ValueError: Invalid header column ":width", font ":trait" or text ":alignment". ''' f = Rect4(0, 0, width, height) v = NSTableView.alloc().initWithFrame_(f.NS) cols = [] high = 0 id2i = {} # map col.identifier to col number wide = f.width # == v.frame().size.width # <https://Developer.Apple.com/documentation/appkit/nstablecolumn> for i, h in enumerate(self._headers): # note, the identifier MUST be an NSStr (to avoid warnings) t = retain(NSStr(str(i))) c = NSTableColumn.alloc().initWithIdentifier_(t) # simply map col.identifier to I{int}, instead of frequent, # costly int(nsString2str(col.identifier())) conversions in # _NSTableViewDelegate.tableView_objectValueForTableColumn_row_ id2i[c.identifier()] = i # <https://Developer.Apple.com/documentation/appkit/nscell> h = _format(h, c) cols.append(h) c.setTitle_(release( NSStr(h))) # == c.headerCell().setStringValue_(NSStr(h)) # <https://Developer.Apple.com/documentation/uikit/nstextalignment> v.addTableColumn_(c) # increase row height 1-2 points to show (bold) descenders high = max(high, Font(c.dataCell().font()).height + 2) wide -= c.width() if wide > 0: # stretch last col to frame edge c.setWidth_(float(wide + c.width())) if high > v.rowHeight(): # adjust the row height v.setRowHeight_(high) # <https://Developer.Apple.com/library/content/documentation/ # Cocoa/Conceptual/TableView/VisualAttributes/VisualAttributes.html> v.setGridStyleMask_(NSTableViewSolidHorizontalGridLineMask | NSTableViewSolidVerticalGridLineMask) # v.setDrawsGrid_(YES) # XXX obsolete, not needed d = NSTableViewDelegate.alloc().init(cols, self._rows, id2i) v.setDelegate_(d) v.setDataSource_(d) # v.setEditing_(NSMain.NO_false) # NO v.reloadData() self.NS = retain(v) self.NSdelegate = retain(d) self._window = w = TableWindow(title, self) # v.setDelegate_(w.delegate) return w
def _nstr(col): return retain(NSStr(str(col))) if col else _NS.BlankCell
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 # <https://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. ''' # <https://Developer.Apple.com/documentation/appkit/nsalert> ns = NSAlert.alloc().init() ns.setAlertStyle_(self._style) ns.setMessageText_(release(NSStr(self.title))) if self._info: # <https://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))) # <https://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) # <https://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