FilteringController
===================

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

Demonstrates how to subclass ``NSArrayController`` to implement filtering of a
``NSTableView``. Also demonstrates the use of indexed accessors.

Originally from "Cocoa Bindings Examples and Hints", converted to
PyObjC by u.fiedler.

.. http://homepage.mac.com/mmalc/CocoaExamples/controllers.html
   (dead link)


.. rst-class:: tabber

Sources
-------

.. rst-class:: tabbertab

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

.. sourcecode:: python

    #
    #  FilteringArrayController.py
    #  FilteringController
    #
    #  Converted by u.fiedler on 05.02.05.
    #
    #  The original version was written in Objective-C by Malcolm Crawford
    #  at http://homepage.mac.com/mmalc/CocoaExamples/controllers.html
    
    from Cocoa import NSArrayController
    from objc import super  # noqa: A004
    
    
    class FilteringArrayController(NSArrayController):
        _k_searchString = ""
    
        def search_(self, sender):
            self.setSearchString_(sender.stringValue())
            self.rearrangeObjects()
    
        def newObject(self):
            """
            Creates and returns a new object of the class specified by objectClass.
            Set default values, and keep reference to new object -- see arrangeObjects_
            """
            self.newObj = super().newObject()
            self.newObj.setValue_forKey_("First", "firstName")
            self.newObj.setValue_forKey_("Last", "lastName")
            return self.newObj
    
        def arrangeObjects_(self, objects):
            if self._k_searchString is None or self._k_searchString == "":
                self.newObj = None
                return super().arrangeObjects_(objects)
    
            # Create array of objects that match search string.
            # Also add any newly-created object unconditionally:
            # (a) You'll get an error if a newly-added object isn't added to
            # arrangedObjects.
            # (b) The user will see newly-added objects even if they don't
            # match the search term.
    
            matchedObjects = []
            lowerSearch = self._k_searchString.lower()
            for item in objects:
                if item == self.newObj:
                    # if the item has just been created, add it unconditionally
                    matchedObjects.append(item)
                    self.newObj = None
                else:
                    lowerName = item.valueForKeyPath_("firstName").lower()
                    if lowerSearch in lowerName:
                        matchedObjects.append(item)
                    else:
                        lowerName = item.valueForKeyPath_("lastName").lower()
                        if lowerSearch in lowerName:
                            matchedObjects.append(item)
            return super().arrangeObjects_(matchedObjects)
    
        def searchString(self):
            return self._k_searchString
    
        def setSearchString_(self, newStr):
            self._k_searchString = newStr

.. rst-class:: tabbertab

FilteringController.py
......................

.. sourcecode:: python

    #
    #  FilteringController
    #
    
    import FilteringArrayController  # noqa: F401
    import FilteringControllerDocument  # noqa: F401
    from PyObjCTools import AppHelper
    
    AppHelper.runEventLoop()

.. rst-class:: tabbertab

FilteringControllerDocument.py
..............................

.. sourcecode:: python

    #
    #  FilteringControllerDocument.py
    #  FilteringController
    #
    #  Converted by u.fiedler on 05.02.05.
    #
    #  The original version was written in Objective-C by Malcolm Crawford
    #  at http://homepage.mac.com/mmalc/CocoaExamples/controllers.html
    
    import objc
    from Cocoa import NSDocument, NSKeyedArchiver, NSKeyedUnarchiver
    from objc import super  # noqa: A004
    
    
    class FilteringControllerDocument(NSDocument):
        peopleController = objc.IBOutlet()
    
        def init(self):
            self = super().init()
            if self is None:
                return None
            self._k_people = []
            return self
    
        def windowNibName(self):
            return "FilteringControllerDocument"
    
        def windowControllerDidLoadNib_(self, controller):
            super().windowControllerDidLoadNib_(controller)
    
        def dataRepresentationOfType_(self, aType):
            return NSKeyedArchiver.archivedDataWithRootObject_(self._k_people)
    
        def loadDataRepresentation_ofType_(self, data, aType):
            self.setPeople_(NSKeyedUnarchiver.unarchiveObjectWithData_(data))
            return True
    
        # indexed accessors
    
        def people(self):
            return self._k_people
    
        def setPeople_(self, people):
            self._k_people[:] = people
    
        @objc.accessor
        def countOfPeople(self):
            return len(self._k_people)
    
        @objc.accessor
        def objectInPeopleAtIndex_(self, idx):
            return self._k_people[idx]
    
        @objc.accessor
        def insertObject_inPeopleAtIndex_(self, obj, idx):
            self._k_people.insert(idx, obj)
    
        @objc.accessor
        def removeObjectFromPeopleAtIndex_(self, idx):
            del self._k_people[idx]
    
        @objc.accessor
        def replaceObjectInPeopleAtIndex_withObject_(self, idx, obj):
            self._k_people[idx] = obj

.. rst-class:: tabbertab

setup.py
........

.. sourcecode:: python

    """
    Script for building the example:
    
    Usage:
        python3 setup.py py2app
    """
    
    from setuptools import setup
    
    plist = {
        "CFBundleDocumentTypes": [
            {
                "CFBundleTypeExtensions": ["FilteringController", "*"],
                "CFBundleTypeName": "FilteringController File",
                "CFBundleTypeRole": "Editor",
                "NSDocumentClass": "FilteringControllerDocument",
            }
        ]
    }
    
    setup(
        name="FilteringController",
        app=["FilteringController.py"],
        data_files=["English.lproj"],
        options={"py2app": {"plist": plist}},
        setup_requires=["py2app", "pyobjc-framework-Cocoa"],
    )

