TableModelWithSearch
====================

* :download:`Download example <PyObjCExample-TableModelWithSearch.zip>`

A more advanced example of Key-Value Coding. This uses a custom
``NSArrayController``.


.. rst-class:: tabber

Sources
-------

.. rst-class:: tabbertab

FilteringArrayController.py
...........................

.. sourcecode:: python

    #
    #  FilteringArrayController.py
    #  TableModelWithSearch
    #
    #  Created by Bill Bumgarner on Sun Apr 04 2004.
    #  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
    #
    
    import re
    
    import objc
    from Cocoa import NSArrayController
    from objc import super  # noqa: A004
    
    kLiteralSearch = "Literal Search"
    kRegularExpressionSearch = "Regular Expression Search"
    
    
    def regexForSearchString(searchString, searchType):
        if not searchString:
            return None
    
        searchString = searchString.strip()
        if searchType == kLiteralSearch:
            searchString = re.escape(searchString.strip()) + r"(?i)"
        return re.compile(searchString)
    
    
    def dictValueFilter(dicts, regex):
        for dct in dicts:
            for value in dct.values():
                if regex.search(value):
                    yield dct
                    break
    
    
    class FilteringArrayController(NSArrayController):
        searchString = None
        lastRegex = None
        searchType = kLiteralSearch
    
        def arrangeObjects_(self, objects):
            supermethod = super().arrangeObjects_
            try:
                regex = regexForSearchString(self.searchString, self.searchType)
            except:  # noqa: E722, B001
                regex = self.lastRegex
            self.lastRegex = regex
            if regex is None:
                return supermethod(objects)
            return supermethod(list(dictValueFilter(objects, regex)))
    
        @objc.IBAction
        def performSearch_(self, sender):
            self.searchString = sender.stringValue()
            self.rearrangeObjects()
    
        @objc.IBAction
        def changeSearchType_(self, searchType):
            self.lastRegex = None
            self.searchString = None
            self.searchType = searchType
            self.rearrangeObjects()

.. rst-class:: tabbertab

TableModelWithSearchAppDelegate.py
..................................

.. sourcecode:: python

    import pwd
    
    from Foundation import NSMutableArray, NSObject
    
    
    def getPasswords():
        a = NSMutableArray()
        for pw in pwd.getpwall():
            a.append(
                {
                    "name": pw.pw_name,
                    "password": pw.pw_passwd,
                    "uid": str(pw.pw_uid),
                    "gid": str(pw.pw_gid),
                    "gecos": pw.pw_gecos,
                    "home_dir": pw.pw_dir,
                    "shell": pw.pw_shell,
                }
            )
    
        return a
    
    
    class TableModelWithSearchAppDelegate(NSObject):
        def passwords(self):
            if not hasattr(self, "_cachedpasswords"):
                self._cachedpasswords = getPasswords()
            return self._cachedpasswords

.. rst-class:: tabbertab

ToolbarCreator.py
.................

.. sourcecode:: python

    #
    #  ToolbarCreator.py
    #  TableModelWithSearch
    #
    #  Created by Bill Bumgarner on Sun Apr 04 2004.
    #  Copyright (c) 2004 __MyCompanyName__. All rights reserved.
    #
    
    import objc
    from Cocoa import (
        NSMenu,
        NSMenuItem,
        NSObject,
        NSToolbar,
        NSToolbarCustomizeToolbarItemIdentifier,
        NSToolbarFlexibleSpaceItemIdentifier,
        NSToolbarItem,
        NSToolbarPrintItemIdentifier,
        NSToolbarSeparatorItemIdentifier,
        NSToolbarSpaceItemIdentifier,
    )
    from FilteringArrayController import kLiteralSearch, kRegularExpressionSearch
    
    kToolbarIdentifier = "TableModel Toolbar Identifier"
    kSearchFieldItemIdentifier = "TableModel Search Field Identifier"
    
    
    class ToolbarCreator(NSObject):
        filteringArrayController = objc.IBOutlet()
        searchField = objc.IBOutlet()
        window = objc.IBOutlet()
    
        def awakeFromNib(self):
            self.toolbarItemCache = {}
    
            # create toolbar containing search field
            toolbar = NSToolbar.alloc().initWithIdentifier_(kToolbarIdentifier)
            toolbar.setDelegate_(self)
            toolbar.setAllowsUserCustomization_(True)
            toolbar.setAutosavesConfiguration_(True)
    
            searchFieldItem = NSToolbarItem.alloc().initWithItemIdentifier_(
                kSearchFieldItemIdentifier
            )
            self.searchFieldItem = searchFieldItem
            searchFieldItem.setLabel_("Search")
            searchFieldItem.setPaletteLabel_("Search Field")
            searchFieldItem.setToolTip_("Search")
            searchFieldItem.setView_(self.searchField)
            searchFieldItem.setMinSize_(self.searchField.bounds().size)
            maxSize = self.searchField.bounds().size
            maxSize.width = maxSize.width + 150
            searchFieldItem.setMaxSize_(maxSize)
    
            self.toolbarItemCache[kSearchFieldItemIdentifier] = searchFieldItem
    
            self.window.setToolbar_(toolbar)
    
            cellMenu = NSMenu.alloc().initWithTitle_("Search Menu")
            # note, bottom up!
            for v in [kRegularExpressionSearch, kLiteralSearch]:
                item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
                    v, "changeSearchType:", ""
                )
                item.setRepresentedObject_(v)
                item.setTarget_(self)
                cellMenu.insertItem_atIndex_(item, 0)
            self.searchField.cell().setSearchMenuTemplate_(cellMenu)
            # this better be the kLiteralSearch menu item
            self.changeSearchType_(item)
    
        @objc.IBAction
        def changeSearchType_(self, sender):
            obj = sender.representedObject()
            self.searchField.cell().setPlaceholderString_(obj)
            self.searchField.setStringValue_("")
            self.filteringArrayController.changeSearchType_(obj)
    
        def toolbarDefaultItemIdentifiers_(self, aToolbar):
            return [
                kSearchFieldItemIdentifier,
                NSToolbarFlexibleSpaceItemIdentifier,
                NSToolbarSeparatorItemIdentifier,
                NSToolbarCustomizeToolbarItemIdentifier,
            ]
    
        def toolbarAllowedItemIdentifiers_(self, aToolbar):
            return [
                kSearchFieldItemIdentifier,
                NSToolbarFlexibleSpaceItemIdentifier,
                NSToolbarSpaceItemIdentifier,
                NSToolbarSeparatorItemIdentifier,
                NSToolbarPrintItemIdentifier,
                NSToolbarCustomizeToolbarItemIdentifier,
            ]
    
        def toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar_(
            self, toolbar, itemIdentifier, flag
        ):
            newItem = NSToolbarItem.alloc().initWithItemIdentifier_(itemIdentifier)
            item = self.toolbarItemCache[itemIdentifier]
    
            newItem.setLabel_(item.label())
            newItem.setPaletteLabel_(item.paletteLabel())
            if item.view():
                newItem.setView_(item.view())
            else:
                newItem.setImage_(item.image())
    
            newItem.setToolTip_(item.toolTip())
            newItem.setTarget_(item.target())
            newItem.setAction_(item.action())
            newItem.setMenuFormRepresentation_(item.menuFormRepresentation())
    
            if newItem.view():
                newItem.setMinSize_(item.minSize())
                newItem.setMaxSize_(item.maxSize())
    
            return newItem

.. rst-class:: tabbertab

main.py
.......

.. sourcecode:: python

    #
    #  __main__.py
    #  TableModelWithSearch
    #
    #  Created by Bob Ippolito on Sun Apr 04 2004.
    #  Copyright (c) 2004 Bob Ippolito. All rights reserved.
    #
    
    # import classes required to start application
    import FilteringArrayController  # noqa: F401
    import TableModelWithSearchAppDelegate  # noqa: F401
    import ToolbarCreator  # noqa: F401
    from PyObjCTools import AppHelper
    
    # start the event loop
    AppHelper.runEventLoop(argv=[])

.. rst-class:: tabbertab

setup.py
........

.. sourcecode:: python

    """
    Script for building the example, alternative to the Xcode project
    
    Usage:
        python3 setup.py py2app
    """
    
    from setuptools import setup
    
    setup(
        name="TableModelWithSearch",
        app=["main.py"],
        data_files=["English.lproj"],
        setup_requires=["py2app", "pyobjc-framework-Cocoa"],
    )

