ToDos
=====

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

Shows two array controllers, one to manage the contents of a table view, the
other to manage a pop-up menu in a table column. Also shows two value
transformers to alter the color of text.

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

Category.py
...........

.. sourcecode:: python

    #
    #  Category.py
    #  ToDos
    #
    #  Converted by u.fiedler on 09.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 Foundation import NSObject
    
    
    class Category(NSObject):
        title = objc.ivar("title")
        priority = objc.ivar("priority", objc._C_INT)
    
        @classmethod
        def allCategories(cls):
            """Predefined global list of categories"""
            return categories
    
        @classmethod
        def categoryForPriority_(cls, thePriority):
            for category in categories:
                if thePriority >= category.priority:
                    return category
            return None
    
        @classmethod
        def categoryWithTitle_andPriority_(cls, aTitle, aValue):
            """Convenience constructor"""
            newCategory = Category.alloc().init()
            newCategory.title = aTitle
            newCategory.priority = aValue
            return newCategory
    
        # NSCoding methods
        # To encode, simply save 'priority'; on decode, replace self with
        # the existing instance from 'allCategories' with the same priority
    
        def encodeWithCoder_(self, encoder):
            if encoder.allowsKeyedCoding():
                encoder.encodeInt_forKey_(self.priority, "priority")
            else:
                encoder.encodeObject_(self.priority)
    
        def initWithCoder_(self, decoder):
            if decoder.allowsKeyedCoding():
                thePriority = decoder.decodeIntForKey_("priority")
            else:
                thePriority = decoder.decodeObject()
            return Category.categoryForPriority_(thePriority)
    
    
    categories = [
        Category.categoryWithTitle_andPriority_("Vital", 11),
        Category.categoryWithTitle_andPriority_("Very Important", 4),
        Category.categoryWithTitle_andPriority_("Important", 3),
        Category.categoryWithTitle_andPriority_("Not Important", 2),
        Category.categoryWithTitle_andPriority_("Whenever", 0),
    ]

.. rst-class:: tabbertab

OverdueTransformer.py
.....................

.. sourcecode:: python

    #
    #  OverdueTransformer.py
    #  ToDos
    #
    #  Converted by u.fiedler on 09.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 NSColor, NSValueTransformer
    
    
    class OverdueTransformer(NSValueTransformer):
        @classmethod
        def transformedValueClass(cls):
            return NSColor
    
        @classmethod
        def allowsReverseTransformation(cls):
            return False
    
        def transformedValue_(self, aDate):
            if aDate is None:
                return None
            if aDate.timeIntervalSinceNow() < 0:
                return NSColor.redColor()
            return NSColor.blackColor()

.. rst-class:: tabbertab

PriorityToColourTransformer.py
..............................

.. sourcecode:: python

    #
    #  PriorityToColourTransformer.py
    #  ToDos
    #
    #  Converted by u.fiedler on 09.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 NSColor, NSValueTransformer
    
    
    class PriorityToColourTransformer(NSValueTransformer):
        @classmethod
        def transformedValueClass(cls):
            return NSColor
    
        @classmethod
        def allowsReverseTransformation(cls):
            return False
    
        def transformedValue_(self, priority):
            if priority is None:
                return NSColor.blackColor()
            elif priority > 4:
                return NSColor.redColor()
            elif priority > 3:
                return NSColor.orangeColor()
            elif priority > 2:
                return NSColor.blueColor()
            elif priority > 1:
                return NSColor.greenColor()
            elif priority > 0:
                return NSColor.brownColor()
            else:
                return NSColor.blackColor()

.. rst-class:: tabbertab

ToDos.py
........

.. sourcecode:: python

    import ToDosDocument  # noqa: F401
    
    if __name__ == "__main__":
        from PyObjCTools import AppHelper
    
        AppHelper.runEventLoop()

.. rst-class:: tabbertab

ToDosDocument.py
................

.. sourcecode:: python

    #
    #  ToDosDocument.py
    #  ToDos
    #
    #  Converted by u.fiedler on 09.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 Category import Category
    from Cocoa import (
        NSDocument,
        NSKeyedArchiver,
        NSKeyedUnarchiver,
        NSMutableArray,
        NSValueTransformer,
    )
    from objc import super  # noqa: A004
    from OverdueTransformer import OverdueTransformer
    from PriorityToColourTransformer import PriorityToColourTransformer
    
    
    class ToDosDocument(NSDocument):
        nix = objc.IBOutlet()
        toDos = objc.ivar()
    
        def init(self):
            self = super().init()
            if self is None:
                return None
            self.toDos = NSMutableArray()
            return self  # if this line is missing you will get the
            # simple message: "Can't create new document"
    
        def categories(self):
            return Category.allCategories()
    
        def windowNibName(self):
            return "ToDosDocument"
    
        def dataRepresentationOfType_(self, aType):
            return NSKeyedArchiver.archivedDataWithRootObject_(self.toDos)
    
        def loadDataRepresentation_ofType_(self, data, aType):
            self.toDos = NSKeyedUnarchiver.unarchiveObjectWithData_(data)
            return True
    
    
    priorityTransformer = PriorityToColourTransformer.alloc().init()
    NSValueTransformer.setValueTransformer_forName_(
        priorityTransformer, "PriorityToColourTransformer"
    )
    
    overdueTransformer = OverdueTransformer.alloc().init()
    NSValueTransformer.setValueTransformer_forName_(
        overdueTransformer, "OverdueTransformer"
    )

.. rst-class:: tabbertab

setup.py
........

.. sourcecode:: python

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

