Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
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_
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
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)))
Ejemplo n.º 7
0
    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()))
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
 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)))
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
 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()
Ejemplo n.º 15
0
def _nsFontsOf(family):
    t = NSStr(family)
    r = NSMain.FontManager.availableMembersOfFontFamily_(t)
    t.release()  # PYCHOK expected
    return r
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
class _NS(object):
    '''(INTERNAL) Singletons.
    '''
    BlankCell = retain(NSStr(''))
    EmptyCell = retain(NSStr('-'))
Ejemplo n.º 18
0
    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
Ejemplo n.º 19
0
 def _nstr(col):
     return retain(NSStr(str(col))) if col else _NS.BlankCell
Ejemplo n.º 20
0
    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
Ejemplo n.º 21
0
    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
Ejemplo n.º 22
0
    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