Commits

Ronald Oussoren  committed 35b2052

Merge 'subclass-branch'

  • Participants
  • Parent commits da3c59d

Comments (0)

Files changed (180)

 sizeof (void *).  (This is also hard to fix;  to do so requires either
 creating a bunch of methods with the various possible return types or
 doing some very serious hardcore pointer magic. [bbum])
+  Ronald: Seems to be solved, NSInvocation happily works with large
+  return values.
 
 (-) It's impossible to send messages containing '$' in the name: ObjC
 allows it, Python does not.
 
 (-) Fix issues with API that requires pointers;  NSScanner, for
 example.  See Mssh.
+   Ronald: Infrastructure for this is present, but not optimal.
 
 Known portability problems
 --------------------------
 
 (-) Sending ObjC messages from multiple threads does not currently work.
 The ``argument arena'' in objc_support.c should be rewritten thread-safe.
+  Ronald: The ``argument arena'' should be removed completely, we're mostly 
+  there.
 
 (-) The ``forward'' mechanism should be revised: the current
 implementation assumes the arguments passed are ALL Objective-C
 objects, since no check is possible...  (Not sure this can be fixed,
 but it should be revisited given that the Mac OS X/XS APIs for
 forwarding are a bit different than the old forward:: APIs. [bbum])
+  Ronald: Fixed. It is now possible to specify the signature of a 
+  python method.
 
 (-) [OC_MemoryStream close] should deallocate the stream's buffer with
 vm_deallocate(). See NXCloseMemory() documentation. (NXStream APIs are
 
 (*) With Foundation, pythonify_c_value and its counterpart should convert
 to/from NSString and PyStringObject. (What about Unicode???? [bbum])
+  Ronald: Should we? It might be more usefull to provide a function/method
+  to convert NSString values to Python string/unicode objects: NSString
+  seems to have a number of methods without a Python equivalent. I've no
+  problems with automaticly translating Python strings to NSString objects.

File pyobjc/ChangeLog

+2002-09-28 Ronald Oussoren <oussoren@cistron.nl>
+	* objc-class.m: Actually implement the selector->python cache,
+	  this significantly speeds up method dispatch (iClass is now
+	  pretty snappy)
+
+2002-09-23 Ronald Oussoren <oussoren@cistron.nl>
+	* Changelog: fix my e-mail address.
+
+	* setup.py: Check for a framework install of python on MacOS X
+
+	* setup.py: Check for Python 2.3 or later
+
+	* OC_PythonObject.m: Remove debug printf statements
+
+	* class-builder.m: 
+	  - Use alloca instead of malloc for small buffers
+	  - Use signature string from the Python wrapper of a class, instead
+	    of reading it directly from the Objective-C class: The user may
+	    have specified a better signature.
+
+ 	* objc-class.m:
+	  - Add 'tp_compare' and 'tp_hash' special methods.
+	  - Fix memory leak in ObjCClass_FindSelector
+
+	* objc_support.m:
+	  - Add support for unicode strings
+	  - In depythonify_c_value: do nothing if output buffer is NULL
+	  - arguments arena support is completely disabled. As a side-effect,
+	    _C_PTR values inside structures/arrays are no longer translated
+	    to/from python.
+
+	* objc_util.m: Translate some Cocoa exceptions into Python 
+
+	* selector.m:
+	  - Remove debug printfs
+	  - Actually add implementations for ObjCSelector_Signature
+	    and ObjCSelector_Selector
+	
+	* super-call.m:
+	  - The python wrapper for an Objective-C method might know a better
+	    signature than the method itself.
+	
+	* Modules/Cocoa:
+	  - Copied later version, including some more constants.
+
+	* Lib/objc/__init__.py: 
+	  - Re-enable __setitem__ special method for objects with
+	    setObject:forKey:
+	  - IBAction is now a function
+	  - If we are a '.app' add the Resources directory to the python
+	    search-path (and at the front), and remove the '.' that is 
+	    normally at the front. 
+
+	* Lib/objc/_FoundationSignatures.py
+	  - Add better signatures for a number of methods
+
+	* Lib/objc/builder.py: Remove invalid comment
+
+	* Examples/iClass: New example (Cocoa class browser)
+	  
+
+2002-09-15 Ronald Oussoren <oussoren@cistron.nl>
+	* Fix a number of memory leaks. There are still some leaks left.
+
+	* Some minor cleanups
+
+	* Documentation about reference counts.
+
+2002-09-08 Ronald Oussoren <oussoren@cistron.nl>
+	* Redid the merge, the directory structure has been reorganised to
+	  better reflect the importance of the modules for Cocoa programming.
+
+2002-08-09 Ronald Oussoren <oussoren@cistron.nl>
+	* this is the start of the merge of an experimental and completely
+	  seperate branch. 
+
+
 2002-01-30  Steve Majewski <sdm7g@Virginia.EDU>
 	
 	* will now build for Python 2.2

File pyobjc/Doc/TODO

+Important:
+- reference counting cleanup (add Py_INC/DECREF and retain/release at 
+  correct places)
+- [EASY]implement missing functions needed to instantiate python-objc hybrids
+  (from objcective-C) [+alloc, -retain, ...]
+- [EASY]implement script to generate 'proxy-methods' needed to override 
+  objective-C methods in python.
+- [?] error-checking
+- [?] forwarding of exceptions (both from objc->python and python->objc)
+- Methods & multiple inheritance: selectors inherited from non-primary
+  superclasses will not be registered with runtime. (Is that necessary?)
+
+Less important
+- [HARD] implement calling of superclass implementation of methods
+- [?] make objc_support thread-safe
+- [MEDIUM] design and implement mechanism to use custom mapping for
+   selectors (needed to properly support NSScanner)
+- [EASY] use above to map troublesome methods in AppKit and Foundation
+- [EASY] implement python module that maps static functions, types and
+   constants in AppKit and Foundation
+- Write example application (adapt CurrencyConverter and implement a
+  document-based application)
+- Add tools/skeletons/... needed to implement specific Cocoa projects:
+  * Application
+  * Configuration panel
+  * (Standalone) service
+  * Screensaver
+  * ...
+
+Housekeeping
+- code cleanup
+- documentation
+
+===============
+
+- Merge code to call to objective-C methods from pyobjc
+  But: we probably need to use low-level code from runtime instead of 
+  NSInvocation, for now that seems the best way to transparently support
+  super(...) calls in the python code.
+- Merge/implement code to proxy objective-C objects
+- implement subclassing
+- test-code
+- documentation
+- Foundation and AppKit helper modules
+
+The structure for the 'new' pyobjc:
+
+package pyobjc
+  module _pyobjc              # The 'core' native code
+  module _FoundationMapping   # Special mapping code for Foundation classes
+  module __init__	      # Package init
+     # Basically does:
+     from _pyobjc import *
+     import _FoundationMapping
+     _pyobjc.init()
+     del init
+     del _pyobjc
+
+
+package Cocoa:
+  package Foundation:
+    module _Foundation
+      # Global functions and C-types in Foundation kit
+    module __init__
+      # Does:
+      import pyobjc
+      from _Foundation import *
+      clslist = (
+      	'NSObject', ....
+      )
+      for cls in clslist:
+      	tmp = pyobjc.lookup_class(cls)
+	set_global(cls, tmp)
+      # NSBundle.bundleForClass: may be usefull here!
+
+  package AppKit:
+    module _AppKitMapping
+      # Helper code to fully support mapping AppKit classes
+    module _AppKit
+      # Mapping of global function and types in AppKit
+    module __init__
+      # Like Foundation.__init__
+

File pyobjc/Doc/architecture.txt

+TODO
+- Expand
+- Convert to StructuredText
+
+Introduction
+------------
+
+This document gives a (brief) description of how the PyObjc package is 
+structured.
+
+Objective-C classes and objects
+-------------------------------
+
+Objective-C classes are represented directly as python classes. This allows
+us to implement subclassing of Objective-C classes with the full power that
+new-style classes provide.
+
+There is one problem with this though, PyTypeObject does not have space to
+store additional information, and is a variable-sized object. This means that
+subclasses of PyType_Type cannot add instance variables. We solve this by
+storing the additional information in a dictionary indexed by the PyTypeObjects
+that represent Objective-C classes.
+
+Objective-C objects are represented by proxy objects that are instances of
+the classes descriped above.
+
+TODO: work out how we'll implement subclasses objects and describe here.
+
+
+Methods and instance variables
+------------------------------
+
+Methods and instance variables are represented as 'descriptor' objects that
+are attributes of the PyTypeObject describing a class. This way it is possible
+to use the normal python introspection mechanisms to explore an objective-C
+object/class.
+
+There is also a mechanism to call methods that are not part of the advertised
+interface of a class. This is needed to support classes like NSProxy that 
+forward method invocations to other objects.

File pyobjc/Doc/structure.txt

+Structure of the PyObjC package
+===============================
+
+.. This document is in structured text markup to enable easy translation to 
+   HTML.
+
+Introduction
+------------
+
+This document gives an overview of the PyObjC for developers (of the package).
+
+One of the sections describes how all of it works, and some of the limitation.
+
+
+Methods
+-------
+
+Classes are scanned for methods when the python wrapper for a class is created.
+We then create python wrappers for those methods. This way users can use the
+normal python introspection methods to check which methods are available.
+
+Sadly enough some classes in the Cocoa frameworks on Mac OSX grow new methods
+when the first instance of those classes is created. We therefore have added 
+some additional code that rescans the method tables on several occasions.
+
+Subclassing
+-----------
+
+It is possible to subclass objective-C classes in python and this results in a hybrid Python/Objective-C class. Instances of these classes consist of a cluster of 2 objects, a Python object and an Objective-C object.
+
+The reference count (or retainCount in objective-C speak) is stored in the 
+Python object, mostly because that is the only way to maintain a single 
+reference count for the cluster. The pointers from the python half of the 
+cluster to the objective-C half, and the pointers the other way around, are
+not counted in the reference count. If those would be counted we would
+introduce cycles that are not detectable by the cycle-breaking garbage 
+collector in python and all python/objective-C hybrids would be immortal.
+
+The first python subclass of an objective-C class introduces a new instance
+variable in the objective-C object to store the pointer to the python half of
+the cluster. This variable is always referenced by name.  The python half is 
+a subclass of objc_object that already contains a pointer to an objective-C 
+object. 
+
+The first python subclass of an objective-C class also introduces a number of
+methods (both class methods and instance methods) that allow us to maintain the
+illusion of a single object. Check class-builder.m for details.
+
+
+Directory structure
+-------------------
+
+Doc/
+  Documentation
+
+Examples/
+  Example scripts and applets.
+
+Lib/
+  Python modules that will be installed in the library. Currently contains
+  the packages 'objc', 'Cocoa' and 'AddressBook'
+
+Modules/
+  Extension modules related to the packages in 'Lib'. This directory contains
+  both the core module 'objc._objc' and a number of extension modules that
+  help in wrapping all of Cocoa.
+
+Scripts/
+  Scripts used during building and/or development of PyObjC.
+
+Tools/
+  Scripts that are usefull for users of PyObjC
+
+Reference counts
+----------------
+
+The Objective-C rules for reference counts are pretty easy: A small number
+of class methods (alloc, allocWithZone:, copy, ...) transfer object ownership
+to the caller. For all other objects you have to call 'retain' if you want
+to keep a reference. This includes all factory methods (e.g. 
+[String stringWithCString:"bla"])!
+
+The objc module tries to make the management of reference counts completely
+transparent for the user of the module. This is mostly solved in 
+[de]pythonify_c_value. Additonal code is needed when calling methods that
+transfer ownership of their return value (as described above) and when updating
+a instance variable in an Objective-C object (retain new and release old, 
+in that order). Both are implemented.
+
+Strings
+-------
+
+We currently automaticly convert from Python strings to NSStrings (and back). 
+This may not be the smartest thing to do because NSString has a number of 
+methods that don't have an equivalent in Python. 
+
+Converting a Python string to objective-C and back currently converts the 
+string to Unicode. If may be usefull to try to convert to a normal string
+(using [NSString dataUsingEncoding:allowLossyConversion:]) and only return
+a Unicode object if that fails.
+
+When translating from NSString to a Python unicode object (and back) we first 
+translate to a UTF8 encoding. This way we don't have to worry about any
+differences in the representation of Unicode strings in Python and objective-C
+(Python has two different represenations, selection is at compile-time)

File pyobjc/Doc/users.txt

+Userguide for PyObjC
+====================
+
+.. This file is formatted using the rules for StructuredText
+
+Introduction
+------------
+
+This is the user guide for PyObjC. It describes how to use this package to
+implement Python scripts that use Objective-C classes and objects. It also
+describes the limitations of this Package. 
+
+The last section describes the C API of this package and why you may want to
+use it.
+
+Overview
+--------
+
+TODO
+
+
+Variables and Constants
+-----------------------
+
+TODO
+
+
+Functions
+---------
+
+TODO
+
+
+
+Limitations
+-----------
+
+The objc module automaticly wraps classes and objects. This works correctly in
+the easy case where there are no methods with variable arguments or 
+pass-by-reference arguments. If there are, the core module needs some help.
+
+For this reason it is best to not use just the core API when your working with
+this module, but to use packages like 'Cocoa' that provide the help needed and
+might also wrap other functionality (constants, functions) in a framework.
+
+
+C API
+-----
+
+Introduction
+~~~~~~~~~~~~
+
+The PyObjC package provides a C API that can be used when you're wrapping 
+functions that deal with Objective-C objects or classes. It can also be used
+to provide functions that help to wrap problematic objective-C methods (like 
+those that take a variable number of arguments).
+
+This API is used by the 'Cocoa' package (part of the PyObjC distribution) to
+wrap the entire Cocoa API.
+
+How to use it
+~~~~~~~~~~~~~
+
+You ``#include`` "pyobjc-api.h" in your module implementation. In the module
+initialisation function you then call ``ObjC_ImportModule(mymodule)``. After
+this you can use the functions and constants defined in the API.
+
+Constants
+~~~~~~~~~
+
+TODO
+
+Functions
+~~~~~~~~~
+
+TODO
+
+
+Limitations
+~~~~~~~~~~~
+
+We currently assume that extension module contain at most 1 file that uses
+the PyObjC API.

File pyobjc/Doc/warts.txt

+The mapping from Objective-C onto Python is almost complete, but the following
+things should be kept in mind:
+
+- You must manually take care of reference-counts when updating IBOutlet fields.
+  This means calling 'retain' when setting a value and 'release' when you unset
+  it (or replace it by another one).
+
+- Objective-C only has single inheritance. You can use multiple inheritance in
+  the python code, but only to mix in 'fully python' classes.
+
+- In objective-C it is valid to call methods on nil, these calls will be
+  ignored. The nil 'object' is mapped to None and None will not ignore method
+  calls, but will most likely raise an AttributeError. You'll have to remember
+  this when translating from Objective-C to python.

File pyobjc/Examples/00ReadMe.txt

+This directory contains a number of examples for using the pyobjc module.
+
+A number of simple scripts that demo the core module.
+- subclassing-objective-c.py
+  Create a subclass of an objective-C class
+
+- super-call.py
+  Likewise, but call super-class implementation of a method
+
+- dictionary.py
+  Use a NS*Dictionary object.
+
+And a number of Cocoa applications. These use the 'Cocoa' package.
+- CurrencyConverter
+  A simple NIB based application
+
+- TableModel
+  Shows how to use a NSTableView
+
+- Todo
+  A more complex NIB based applications. This is a document-based application.
+  The code is a translation into pyton of an example project in 
+  'Learning Cocoa' from O'Reilly

File pyobjc/Examples/Cocoa/AppKit.py

-import pyobjc
-from Cocoa import Foundation
-
-
-def LoadBundle( path ):
-        bundle = Foundation.NSBundle.bundleWithPath_( path )
-        bundle.load()
-        return bundle
-
-
-# load AppKit so those classes are available for inspection
-AppKitBundle = LoadBundle( '/System/Library/Frameworks/AppKit.framework')
-
-__AppKit__ = globals()
-for _name in dir(pyobjc.runtime):
-    if _name[0] <> '_' and _name not in dir(Foundation)+Foundation.__skip__:
-		__AppKit__[_name] = pyobjc.lookup_class(_name)
-
-del _name
-
-
-

File pyobjc/Examples/Cocoa/Foundation.py

-
-import pyobjc
-
-__Foundation__ = globals()
-__skip__ = [ 'NSInvocationBuilder', 'sel_is_mapped' ]
-
-for _name in dir(pyobjc.runtime):
-    if _name[0] <> '_' and _name not in __skip__:
-        __Foundation__[_name] =  pyobjc.lookup_class(_name) 
-
-sel_is_mapped = pyobjc.runtime.sel_is_mapped 
-del _name
-
-
-
-

File pyobjc/Examples/Cocoa/__init__.py

-
-import pyobjc
-
-_Pool = pyobjc.runtime.NSAutoreleasePool()
-
-__all__ = [ 'Foundation', 'AppKit' ]
-
-
-

File pyobjc/Examples/CurrencyConverter/CurrencyConverter.py

+import sys
+from Foundation import NSObject
+from AppKit import NSApplicationMain
+from objc import *
+
+class Converter (NSObject):
+	def convertAmount(self, amt, rate):
+		return amt*rate
+		
+class ConverterController (NSObject):
+
+	# First define the IB Outlets, the 'ivar' calls below define new
+	# instance variables in the objective-C class (e.g. visible
+	# for introspection in objective-C)
+	converter   = IBOutlet('converter')
+	dollarField = IBOutlet('dollarField')
+	rateField   = IBOutlet('rateField')
+	totalField  = IBOutlet('totalField')
+	extraField  = IBOutlet('extraField')
+
+	def dummy(self):
+		return "dummy"
+
+	def awakeFromNib(self):
+		print "awakeFromNib"
+
+		# Provide some defaults for the user...
+		self.dollarField.setFloatValue_(2.0)
+		self.rateField.setFloatValue_(3.0)
+
+	def convert_(self, sender):
+		print 'DBG PYTHON: ConverterController.convert_(%s, %s)' % (self, sender)
+		rate = self.rateField.floatValue()
+		print '  rate=', rate
+		amt = self.dollarField.floatValue()
+		print '  amt=', amt
+
+		total = self.converter.convertAmount(rate, amt)
+		print '  total=', total
+		self.totalField.setFloatValue_(total)
+		self.rateField.selectText_(self)
+		print "Done"
+
+
+sys.exit(NSApplicationMain(sys.argv))

File pyobjc/Examples/CurrencyConverter/English.lproj/InfoPlist.strings

Binary file added.

File pyobjc/Examples/CurrencyConverter/English.lproj/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = Converter; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {convert = id; }; 
+            CLASS = ConverterController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {converter = id; dollarField = id; rateField = id; totalField = id; }; 
+            SUPERCLASS = NSObject; 
+        }, 
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+    ); 
+    IBVersion = 1; 
+}

File pyobjc/Examples/CurrencyConverter/English.lproj/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>34 73 356 240 0 0 800 578 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>26 119 318 44 0 0 800 578 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>21</integer>
+		<integer>29</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5Q125</string>
+</dict>
+</plist>

File pyobjc/Examples/CurrencyConverter/English.lproj/MainMenu.nib/objects.nib

Binary file added.

File pyobjc/Examples/CurrencyConverter/setup-app.py

+#!/usr/bin/env pythonw
+"""
+Python script for building the applet.
+"""
+
+import objc.builder
+
+objc.builder.build_applet(
+	app_name= 'CurrencyConverterPY',
+	main_py = 'CurrencyConverter.py',
+	extra_files = ['English.lproj/MainMenu.nib'])
+# NOTE: We'd like to use just 'English.lproj', but buildtools.py gets 
+# confused by that....

File pyobjc/Examples/HelloWorld.py

 #    [ obj method: arg1 withOtherArgs: arg2 ] 
 #				obj.method_withOtherArgs_( arg1, arg2 )
 
-import pyobjc
-rt = pyobjc.runtime	# shorthand -- runtime gets used a lot!
+import objc
 
-class AppDelegate:
+# We should import AppKit and Foundation, but don't do that
+# here to show we're using just the basic objective-C bindings.
+NSBundle = objc.lookup_class('NSBundle')
+NSAutoreleasePool = objc.lookup_class('NSAutoreleasePool')
+NSApplication = objc.lookup_class('NSApplication')
+NSWindow = objc.lookup_class('NSWindow')
+NSButton = objc.lookup_class('NSButton')
+NSSound = objc.lookup_class('NSSound')
+NSObject = objc.lookup_class('NSObject')
+
+class AppDelegate (NSObject):
     def applicationDidFinishLaunching_(self, aNotification):
         print "Hello, World!"
 
 def main():
 
-    pool = rt.NSAutoreleasePool()
-
     # Load Application Framework:
-    rt.NSBundle.bundleWithPath_(
+    NSBundle.bundleWithPath_(
 	'/System/Library/Frameworks/AppKit.framework').load()
 
-    NSApp = rt.NSApplication.sharedApplication()
+    NSApp = NSApplication.sharedApplication()
 
-    ##! NSApp.setDelegate_( AppDelegate() )
+    NSApp.setDelegate_( AppDelegate.alloc().init() )
 
-    win = rt.NSWindow.alloc()
+    win = NSWindow.alloc()
     frame = ((200.0, 300.0), (250.0, 100.0))
     win.initWithContentRect_styleMask_backing_defer_ (frame, 15, 2, 0)
     win.setTitle_ ('HelloWorld')
     win.setLevel_ (3)			# floating window
 
-    hel = rt.NSButton.alloc().initWithFrame_ (((10.0, 10.0), (80.0, 80.0)))
+    hel = NSButton.alloc().initWithFrame_ (((10.0, 10.0), (80.0, 80.0)))
     win.contentView().addSubview_ (hel)
     hel.setBezelStyle_( 4 )
     hel.setTitle_( 'Hello!' )
 
-    beep = rt.NSSound.alloc()
+    beep = NSSound.alloc()
     beep.initWithContentsOfFile_byReference_( 
 	'/System/Library/Sounds/tink.aiff', 1 )
     hel.setSound_( beep )
 
-    bye = rt.NSButton.alloc().initWithFrame_ (((100.0, 10.0), (80.0, 80.0)))
+    bye = NSButton.alloc().initWithFrame_ (((100.0, 10.0), (80.0, 80.0)))
     win.contentView().addSubview_ (bye)
     bye.setBezelStyle_( 4 )
     bye.setTarget_ (NSApp)
     bye.setEnabled_ ( 1 )
     bye.setTitle_( 'Goodbye!' )
 
-    adios = rt.NSSound.alloc()
+    adios = NSSound.alloc()
     adios.initWithContentsOfFile_byReference_( 
 	'/System/Library/Sounds/Basso.aiff', 1 )
     bye.setSound_( adios )

File pyobjc/Examples/TableModel/00ReadMe.txt

+Example of how to provide a table-model from python.
+
+Note that the abstract protocol contains a number of methods that take
+plain C arguments (e.g. not 'id' values). 
+
+Because these methods are not present in NSObject, you must explicitly 
+specify the Objective-C signature of the method, hence the calls to 'selector'

File pyobjc/Examples/TableModel/English.lproj/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {CLASS = PyModel; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+    ); 
+    IBVersion = 1; 
+}

File pyobjc/Examples/TableModel/English.lproj/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>58 119 356 240 0 0 1152 746 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>69 252 318 44 0 0 1152 746 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>21</integer>
+		<integer>29</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5S66</string>
+</dict>
+</plist>

File pyobjc/Examples/TableModel/English.lproj/MainMenu.nib/objects.nib

Binary file added.

File pyobjc/Examples/TableModel/TableModel.py

+from Foundation import NSObject
+from AppKit import NSApplicationMain
+from objc import selector
+import sys
+
+class PyModel (NSObject):
+	__slots__  = ('rowcount')
+
+	def awakeFromNib(self):
+		self.rowcount = 10
+		return self
+
+	def init(self):
+		self.rowcount = 10
+		return self
+
+	def numberOfRowsInTableView_(self, aTableView):
+		print "numerOfRowsInTableView: called"
+		return self.rowcount
+
+	numberOfRowsInTableView_ = selector(numberOfRowsInTableView_,
+		signature="i@:@")
+
+
+	def tableView_objectValueForTableColumn_row_(self, 
+			aTableView, aTableColumn, rowIndex):
+		print "tableView:objectValueForTableColumn:row: called"
+		return "{%s, %d}"%(aTableColumn.identifier(), rowIndex)
+
+	tableView_objectValueForTableColumn_row_ = selector(
+		tableView_objectValueForTableColumn_row_,
+		signature='@@:@@i')
+
+
+sys.exit(NSApplicationMain(sys.argv))

File pyobjc/Examples/TableModel/setup-app.py

+#!/usr/bin/env pythonw
+"""
+Python script for building the applet.
+"""
+
+import objc.builder
+
+objc.builder.build_applet(
+	app_name= 'TableModelPY',
+	main_py = 'TableModel.py',
+	extra_files = ['English.lproj'])

File pyobjc/Examples/TableModel2/English.lproj/InfoPlist.strings

Binary file added.

File pyobjc/Examples/TableModel2/English.lproj/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {CLASS = PyModel; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+    ); 
+    IBVersion = 1; 
+}

File pyobjc/Examples/TableModel2/English.lproj/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>58 119 356 240 0 0 1152 746 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>69 252 318 44 0 0 1152 746 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>21</integer>
+		<integer>29</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5S66</string>
+</dict>
+</plist>

File pyobjc/Examples/TableModel2/English.lproj/MainMenu.nib/objects.nib

Binary file added.

File pyobjc/Examples/TableModel2/README.txt

+TableModel2
+bbum@codefab.com
+
+This is a conversion of Ronald's TableModel example into a standalone Cocoa project.   This project also provides a basic recipe for creating any standalone Cocoa applications (or multiple document applications) that are implemented in Python using the Obj-C bridge.
+
+Projects of this style allow for freely mixing python and objective-c source.  Simply add the python source to the project as you would any other source file.  Project Builder will automatically copy all python source files into the Resources/ directory of the resulting application product.
+
+Because the PyObjC module-- the Cocoa support piece, specifically-- automatically adds the 'Contents/Resources/' directory of the application wrapper to Python's sys.path attribute, any python scripts added to the project will both be automatically copied into the Resources/ directory and automatically found by Python.
+
+Of course, if you localize the python scripts, they won't be automatically found.   However, this would be easy to fix by calling NSBundle's -preferredLocalizations method and adding the associated localization directories to sys.path.
+
+To create a new PyCocoa project in Project Builder, do the following.  Of course, this assumes that pyobjc is correctly installed.   This has been tested with python built via Fink -- it should work fine with any build of python.  Unfortunately, it will not work with the Python shipped with OS X as that distribution is incomplete -- it lacks a library to link against (either dynamic or static).
+
+1. Create a new Cocoa Application or Cocoa Document-based Application
+
+2. Add libpython2.2.a to project (or Python.framework, if using the framework build).  For a standard Fink installation, the library will be in /sw/lib/python2.2/config/.
+
+3. If not using the framework build, add the python include path to the project's search path (/sw/include/python2.2/ for Fink).
+
+4. Replace the main.m in the project with the main.m from this example.
+
+5. Add the key 'PrincipalPythonFile' to the Info.plist.  The value is the name of the Python file that you want to be loaded first as the application starts up.  Whatever Python file is chosen, it should take care of loading any other Python files necessary to define the various classes necessary for application startup.   If this key is not defined, the main() function will try to load the file 'Main.py'.   If that file is not found, the application will raise an exception and exit.
+
+6. Add '-undefined suppress -force_flat_namespace' to the linker flags for the project.  You can also turn off the prebinding feature as it doesn't work anyway.
+
+NOTE:  See the Web Services Tool example as it implements an application that is intended to actually do something useful (beyond the very useful task of testing the PyObjC module).  Also -- step 5 should likely be skipped.  Instead, add a Main.py file to your project that imports all of the python files that define classes necessary for App startup -- see the Web Services Example...
+

File pyobjc/Examples/TableModel2/TableModel.py

+from Foundation import NSObject
+from Foundation import NSProcessInfo
+from AppKit import NSApplicationMain
+from objc import selector
+import sys
+
+class PyModel (NSObject):
+	__slots__  = ('rowcount')
+
+	def awakeFromNib(self):
+		self.rowcount = 10
+		return self
+
+	def init(self):
+		self.rowcount = 10
+		return self
+
+	def numberOfRowsInTableView_(self, aTableView):
+		print "numerOfRowsInTableView: called"
+		return self.rowcount
+
+	numberOfRowsInTableView_ = selector(numberOfRowsInTableView_, signature="i@:@")
+
+	def tableView_objectValueForTableColumn_row_(self, 
+			aTableView, aTableColumn, rowIndex):
+		print "tableView:objectValueForTableColumn:row: called"
+		return "{%s, %d}"%(aTableColumn.identifier(), rowIndex)
+
+	tableView_objectValueForTableColumn_row_ = selector(tableView_objectValueForTableColumn_row_, signature='@@:@@i')

File pyobjc/Examples/TableModel2/TableModel2.pbproj/project.pbxproj

+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 38;
+	objects = {
+		080E96DDFE201D6D7F000001 = {
+			children = (
+				F67591E9034123C701336B42,
+			);
+			isa = PBXGroup;
+			name = Classes;
+			refType = 4;
+		};
+		089C165CFE840E0CC02AAC07 = {
+			children = (
+				089C165DFE840E0CC02AAC07,
+			);
+			isa = PBXVariantGroup;
+			name = InfoPlist.strings;
+			refType = 4;
+		};
+		089C165DFE840E0CC02AAC07 = {
+			fileEncoding = 10;
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/InfoPlist.strings;
+			refType = 4;
+		};
+		089C165EFE840E0CC02AAC07 = {
+			fileRef = 089C165CFE840E0CC02AAC07;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+//080
+//081
+//082
+//083
+//084
+//100
+//101
+//102
+//103
+//104
+		1058C7A0FEA54F0111CA2CBB = {
+			children = (
+				F67592030341FC8E01336B42,
+				F67592040341FC8E01336B42,
+				29B97325FDCFA39411CA2CEA,
+				1058C7A1FEA54F0111CA2CBB,
+			);
+			isa = PBXGroup;
+			name = "Linked Frameworks";
+			refType = 4;
+		};
+		1058C7A1FEA54F0111CA2CBB = {
+			isa = PBXFrameworkReference;
+			name = Cocoa.framework;
+			path = /System/Library/Frameworks/Cocoa.framework;
+			refType = 0;
+		};
+		1058C7A2FEA54F0111CA2CBB = {
+			children = (
+				29B97324FDCFA39411CA2CEA,
+			);
+			isa = PBXGroup;
+			name = "Other Frameworks";
+			refType = 4;
+		};
+		1058C7A3FEA54F0111CA2CBB = {
+			fileRef = 1058C7A1FEA54F0111CA2CBB;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+//100
+//101
+//102
+//103
+//104
+//170
+//171
+//172
+//173
+//174
+		17587328FF379C6511CA2CBB = {
+			isa = PBXApplicationReference;
+			path = TableModel2.app;
+			refType = 3;
+		};
+//170
+//171
+//172
+//173
+//174
+//190
+//191
+//192
+//193
+//194
+		19C28FACFE9D520D11CA2CBB = {
+			children = (
+				17587328FF379C6511CA2CBB,
+			);
+			isa = PBXGroup;
+			name = Products;
+			refType = 4;
+		};
+//190
+//191
+//192
+//193
+//194
+//290
+//291
+//292
+//293
+//294
+		29B97313FDCFA39411CA2CEA = {
+			buildStyles = (
+				4A9504CCFFE6A4B311CA0CBA,
+				4A9504CDFFE6A4B311CA0CBA,
+			);
+			isa = PBXProject;
+			mainGroup = 29B97314FDCFA39411CA2CEA;
+			projectDirPath = "";
+			targets = (
+				29B97326FDCFA39411CA2CEA,
+			);
+		};
+		29B97314FDCFA39411CA2CEA = {
+			children = (
+				080E96DDFE201D6D7F000001,
+				29B97315FDCFA39411CA2CEA,
+				29B97317FDCFA39411CA2CEA,
+				29B97323FDCFA39411CA2CEA,
+				19C28FACFE9D520D11CA2CBB,
+				F67591ED0341252E01336B42,
+			);
+			isa = PBXGroup;
+			name = TableModel2;
+			path = "";
+			refType = 4;
+		};
+		29B97315FDCFA39411CA2CEA = {
+			children = (
+				29B97316FDCFA39411CA2CEA,
+			);
+			isa = PBXGroup;
+			name = "Other Sources";
+			path = "";
+			refType = 4;
+		};
+		29B97316FDCFA39411CA2CEA = {
+			fileEncoding = 30;
+			isa = PBXFileReference;
+			path = main.m;
+			refType = 4;
+		};
+		29B97317FDCFA39411CA2CEA = {
+			children = (
+				089C165CFE840E0CC02AAC07,
+				F67591E6034123A101336B42,
+			);
+			isa = PBXGroup;
+			name = Resources;
+			path = "";
+			refType = 4;
+		};
+		29B97323FDCFA39411CA2CEA = {
+			children = (
+				1058C7A0FEA54F0111CA2CBB,
+				1058C7A2FEA54F0111CA2CBB,
+				F67591EB0341244601336B42,
+			);
+			isa = PBXGroup;
+			name = Frameworks;
+			path = "";
+			refType = 4;
+		};
+		29B97324FDCFA39411CA2CEA = {
+			isa = PBXFrameworkReference;
+			name = AppKit.framework;
+			path = /System/Library/Frameworks/AppKit.framework;
+			refType = 0;
+		};
+		29B97325FDCFA39411CA2CEA = {
+			isa = PBXFrameworkReference;
+			name = Foundation.framework;
+			path = /System/Library/Frameworks/Foundation.framework;
+			refType = 0;
+		};
+		29B97326FDCFA39411CA2CEA = {
+			buildPhases = (
+				29B97327FDCFA39411CA2CEA,
+				29B97328FDCFA39411CA2CEA,
+				29B9732BFDCFA39411CA2CEA,
+				29B9732DFDCFA39411CA2CEA,
+			);
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = "";
+				HEADER_SEARCH_PATHS = /sw/include/python2.2;
+				INSTALL_PATH = "$(HOME)/Applications";
+				LIBRARY_SEARCH_PATHS = "/sw/lib/python2.2/config/ /sw/lib/python2.2/config";
+				OTHER_CFLAGS = "";
+				OTHER_LDFLAGS = "-force_flat_namespace -undefined suppress";
+				PREBINDING = NO;
+				PRODUCT_NAME = TableModel2;
+				SECTORDER_FLAGS = "";
+				WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
+				WRAPPER_EXTENSION = app;
+			};
+			dependencies = (
+			);
+			isa = PBXApplicationTarget;
+			name = TableModel2;
+			productInstallPath = "$(HOME)/Applications";
+			productName = TableModel2;
+			productReference = 17587328FF379C6511CA2CBB;
+			productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>TableModel2</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>0.1</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+	<key>PrincipalPythonFile</key>
+	<string>TableModel.py</string>
+</dict>
+</plist>
+";
+			shouldUseHeadermap = 1;
+		};
+		29B97327FDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+			);
+			isa = PBXHeadersBuildPhase;
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		29B97328FDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				089C165EFE840E0CC02AAC07,
+				F67591E8034123A101336B42,
+				F67591EA034123C701336B42,
+				F67591EE0341252E01336B42,
+			);
+			isa = PBXResourcesBuildPhase;
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		29B9732BFDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				29B9732CFDCFA39411CA2CEA,
+			);
+			isa = PBXSourcesBuildPhase;
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		29B9732CFDCFA39411CA2CEA = {
+			fileRef = 29B97316FDCFA39411CA2CEA;
+			isa = PBXBuildFile;
+			settings = {
+				ATTRIBUTES = (
+				);
+			};
+		};
+		29B9732DFDCFA39411CA2CEA = {
+			buildActionMask = 2147483647;
+			files = (
+				1058C7A3FEA54F0111CA2CBB,
+				F67591FA0341FC6601336B42,
+				F67595680341FC9001336B42,
+				F67595690341FC9001336B42,
+				F69197E60342BCD401B4D85B,
+			);
+			isa = PBXFrameworksBuildPhase;
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+//290
+//291
+//292
+//293
+//294
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+		4A9504CCFFE6A4B311CA0CBA = {
+			buildRules = (
+			);
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				OPTIMIZATION_CFLAGS = "-O0";
+			};
+			isa = PBXBuildStyle;
+			name = Development;
+		};
+		4A9504CDFFE6A4B311CA0CBA = {
+			buildRules = (
+			);
+			buildSettings = {
+				COPY_PHASE_STRIP = YES;
+			};
+			isa = PBXBuildStyle;
+			name = Deployment;
+		};
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+//F60
+//F61
+//F62
+//F63
+//F64
+		F67591E6034123A101336B42 = {
+			children = (
+				F67591E7034123A101336B42,
+			);
+			isa = PBXVariantGroup;
+			name = MainMenu.nib;
+			path = "";
+			refType = 4;
+		};
+		F67591E7034123A101336B42 = {
+			isa = PBXFileReference;
+			name = English;
+			path = English.lproj/MainMenu.nib;
+			refType = 4;
+		};
+		F67591E8034123A101336B42 = {
+			fileRef = F67591E6034123A101336B42;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F67591E9034123C701336B42 = {
+			isa = PBXFileReference;
+			path = TableModel.py;
+			refType = 4;
+		};
+		F67591EA034123C701336B42 = {
+			fileRef = F67591E9034123C701336B42;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F67591EB0341244601336B42 = {
+			isa = PBXFileReference;
+			name = libpython2.2.a;
+			path = /sw/lib/python2.2/config/libpython2.2.a;
+			refType = 0;
+		};
+		F67591ED0341252E01336B42 = {
+			isa = PBXFileReference;
+			path = README.txt;
+			refType = 4;
+		};
+		F67591EE0341252E01336B42 = {
+			fileRef = F67591ED0341252E01336B42;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F67591FA0341FC6601336B42 = {
+			fileRef = 29B97325FDCFA39411CA2CEA;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F67592030341FC8E01336B42 = {
+			isa = PBXFrameworkReference;
+			name = CoreFoundation.framework;
+			path = /System/Library/Frameworks/CoreFoundation.framework;
+			refType = 0;
+		};
+		F67592040341FC8E01336B42 = {
+			isa = PBXFrameworkReference;
+			name = CoreServices.framework;
+			path = /System/Library/Frameworks/CoreServices.framework;
+			refType = 0;
+		};
+		F67595680341FC9001336B42 = {
+			fileRef = F67592030341FC8E01336B42;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F67595690341FC9001336B42 = {
+			fileRef = F67592040341FC8E01336B42;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		F69197E60342BCD401B4D85B = {
+			fileRef = F67591EB0341244601336B42;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+	};
+	rootObject = 29B97313FDCFA39411CA2CEA;
+}

File pyobjc/Examples/TableModel2/main.m

+#import <Python.h>
+#import <Foundation/Foundation.h>
+#import <sys/param.h>
+
+int pyobjc_main(int argc, const char *argv[])
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSString *pythonBinPath = [[NSUserDefaults standardUserDefaults] stringForKey: @"PythonBinPath"];
+
+    pythonBinPath = pythonBinPath ? pythonBinPath : @"/usr/bin/python";
+    [pythonBinPath retain];
+    Py_SetProgramName((char *)[pythonBinPath cString]); 
+    Py_Initialize();
+
+    NSString *mainPyFile = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"PrincipalPythonFile"];
+    NSString *mainPyPath = nil;
+
+    if (mainPyFile)
+        mainPyPath = [[NSBundle mainBundle] pathForResource: mainPyFile ofType: nil];
+
+    if ( !mainPyPath ) {
+        NSLog(@"*** WARNING *** PrincipalPythonFile key either did not exist in Info.plist or the file it referred to did not exist.  Trying 'Main.py'.");
+        mainPyPath = [[NSBundle mainBundle] pathForResource: @"Main.py" ofType: nil];
+    }
+
+    if ( !mainPyPath )
+        [NSException raise: NSInternalInconsistencyException
+                    format: @"%s:%d pyobjc_main() Failed to find main python entry point for application.  Exiting.", __FILE__, __LINE__];
+    [mainPyPath retain];
+
+    FILE *mainPy = fopen([mainPyPath cString], "r");
+    int result = PyRun_SimpleFile(mainPy, (char *)[[mainPyPath lastPathComponent] cString]);
+
+    if ( result != 0 )
+        [NSException raise: NSInternalInconsistencyException
+                    format: @"%s:%d pyobjc_main() Failed to run the python file at '%@'.", __FILE__, __LINE__, mainPyPath];
+    
+    [pool release];
+
+    return NSApplicationMain(argc, argv);
+}
+
+int main(int argc, const char *argv[])
+{
+    return pyobjc_main(argc, argv);
+}

File pyobjc/Examples/Todo/00ReadMe.txt

+Final version of the Todo application from 'Learning Cocoa' (O'reilly),
+translated to python.
+
+The nib-file is unchanged from the version in the tar-archive downloaded
+from the o'reilly website.
+
+Notes:
+- The code doesn't work correctly at the moment. 
+  * Loading does not work. The code uses NSCoder and the methods for
+    decoding C-types have not been wrapped yet. Adding this is not very 
+    hard.
+  * The first line in the list ToDo items doesn't show unless the field
+    is selected. The objective-C version has the same problem. 
+
+- This is a minimal translation, the application logic has not been
+  'pythonified'. 
+
+- There are two blocks of code that are not filled in in the objective-C 
+  version (left as an extercise for the reader). I've not yet written those
+  pieces.

File pyobjc/Examples/Todo/CalendarMatrix.py

+from Foundation import *
+from AppKit import *
+from objc import IBOutlet
+
+gNumDaysInMonth = ( 0, 31, 28, 31, 30, 21, 30, 31, 31, 30, 31, 30, 31 )
+
+def isLeap(year):
+	return (((year % 4) == 0 and ((year % 100) != 0)) or (year % 400) == 0)
+
+class CalendarMatrix (NSMatrix):
+	_lastMonthButton = IBOutlet('lastMonthButton')
+	_monthName       = IBOutlet('monthName')
+	_nextMonthButton = IBOutlet('nextMonthButton')
+
+	__slots__ = ('_selectedDay', '_startOffset')
+
+	def initWithFrame_(self, frameRect):
+		self._selectedDay = None
+		self._startOffset = 0
+
+		cell = NSButtonCell.alloc().initTextCell_("")
+		now  = NSCalendarDate.date()
+
+		cell.setShowsStateBy_(NSOnOffButton)
+		self.initWithFrame_mode_prototype_numberOfRows_numberOfColumns_(
+			frameRect, NSRadioModeMatrix, cell, 5, 7)
+
+		count = 0
+		for i in range(6):
+			for j in range(7):
+				val = self.cellAtRow_column_(i, j)
+				if val:
+					val.setTag_(count)
+				count += 1
+		cell.release() # is this needed?
+
+		self._selectedDay = NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
+				now.yearOfCommonEra(), 
+				now.monthOfYear(), 
+				now.dayOfMonth(), 
+				0, 
+				0, 
+				0, 
+				NSTimeZone.localTimeZone()).retain()
+		return self
+
+
+	def choseDay_(self, sender):
+		prevSelDate = self.selectedDay()
+		selDay = self.selectedCell().tag() - self._startOffset + 1
+
+		selDate = NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
+				prevSelDate.yearOfCommonEra(),
+				prevSelDate.monthOfYear(),
+				selDay,
+				0,
+				0,
+				0,
+				NSTimeZone.localTimeZone())
+		self.setSelectedDay_(selDate)
+		self.highlightTodayIfVisible()
+
+		if self.delegate().respondsToSelector_('calendarMatrix:didChangeToDate:'):
+			self.delegate().calendarMatrix_didChangeToDate_(
+				self, selDate)
+
+
+	def monthChanged_(self, sender):
+		thisDate = self.selectedDay()
+		currentYear = thisDate.yearOfCommonEra()
+		currentMonth = thisDate.monthOfYear()
+
+		print sender, self._nextMonthButton, self._lastMonthButton
+
+		if sender is self._nextMonthButton:
+			print "NEXT MONTH", sender, self._nextMonthButton
+			if currentMonth == 12:
+				currentMonth = 1
+				currentYear += 1
+			else:
+				currentMonth += 1
+		else:
+			print "PREVIOUS MONTH"
+			if currentMonth == 1:
+				currentMonth = 12
+				currentYear -= 1
+			else:
+				currentMonth -= 1
+
+		self.setSelectedDay_(NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(currentYear, currentMonth, 1, 0, 0, 0, NSTimeZone.localTimeZone()))
+		self.refreshCalendar()
+		self.choseDay_(self)
+	
+	def setSelectedDay_(self, newDay):
+		self._selectedDay = newDay
+
+	def selectedDay(self):
+		return self._selectedDay
+
+	def refreshCalendar(self):
+
+		selDate = self.selectedDay()
+		currentMonth = selDate.monthOfYear()
+		currentYear = selDate.yearOfCommonEra()
+
+		firstOfMonth = NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
+					currentYear,
+					currentMonth,
+					1,
+					0,
+					0,
+					0,
+					NSTimeZone.localTimeZone())
+		self._monthName.setStringValue_(
+			firstOfMonth.descriptionWithCalendarFormat_("%B %Y"))
+		daysInMonth = gNumDaysInMonth[currentMonth]
+
+		if (currentMonth == 2) and isLeap(currentYear):
+			daysInMonth += 1
+
+		self._startOffset = firstOfMonth.dayOfWeek()
+
+		dayLabel = 1
+
+		for i in range(42):
+			cell = self.cellWithTag_(i)
+			if cell is None:
+				continue
+
+			if i < self._startOffset or i >= (daysInMonth + self._startOffset):
+				# blank out unused cells in the matrix
+				cell.setBordered_(False)
+				cell.setEnabled_(False)
+				cell.setTitle_("")
+				cell.setCellAttribute_to_(NSCellHighlighted, False)
+			else:
+				# Fill in valid days in the matrix
+				cell.setBordered_(True)
+				cell.setEnabled_(True)
+				cell.setFont_(NSFont.systemFontOfSize_(12))
+				cell.setTitle_(str(dayLabel))
+				dayLabel += 1
+				cell.setCellAttribute_to_(NSCellHighlighted, False)
+
+		self.selectCellWithTag_(selDate.dayOfMonth() + self._startOffset - 1)
+		self.highlightTodayIfVisible()
+
+	
+	def highlightTodayIfVisible(self):
+		now = NSCalendarDate.date()
+		selDate = self.selectedDay()
+
+		if selDate.yearOfCommonEra() == now.yearOfCommonEra() \
+				and selDate.monthOfYear() == now.monthOfYear() \
+				and selDate.dayOfMonth() == now.dayOfMonth():
+			aCell = self.cellWithTag_(
+				now.dayOfMonth() + self._startOffset - 1)
+			aCell.setHighlightsBy_(NSMomentaryChangeButton)
+			aCell.setCellAttribute_to_(NSCellHighlighted, True)
+
+	def awakeFromNib(self):
+		self.setTarget_(self)
+		self.setAction_('choseDay:')
+		self.setAutosizesCells_(True)
+		self.refreshCalendar()
+		self.choseDay_(self)
+
+

File pyobjc/Examples/Todo/English.lproj/Credits.rtf

+{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\paperw9840\paperh8400
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
+
+\f0\b\fs24 \cf0 Engineering:
+\b0 \
+	Some people\
+\
+
+\b Human Interface Design:
+\b0 \
+	Some other people\
+\
+
+\b Testing:
+\b0 \
+	Hopefully not nobody\
+\
+
+\b Documentation:
+\b0 \
+	Whoever\
+\
+
+\b With special thanks to:
+\b0 \
+	Mom\
+}

File pyobjc/Examples/Todo/English.lproj/InfoPlist.strings

Binary file added.

File pyobjc/Examples/Todo/English.lproj/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {showInfo = id; }; 
+            CLASS = ToDoAppDelegate; 
+            LANGUAGE = ObjC; 
+            SUPERCLASS = NSObject; 
+        }
+    ); 
+    IBVersion = 1; 
+}

File pyobjc/Examples/Todo/English.lproj/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBMainMenuLocation</key>
+	<string>22 299 258 44 0 44 1024 702 </string>
+</dict>
+</plist>

File pyobjc/Examples/Todo/English.lproj/MainMenu.nib/objects.nib

Binary file added.

File pyobjc/Examples/Todo/English.lproj/ToDoDocument.nib/classes.nib

+{
+    IBClasses = (
+        {
+            ACTIONS = {choseDay = id; monthChanged = id; }; 
+            CLASS = CalendarMatrix; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {lastMonthButton = id; monthName = id; nextMonthButton = id; }; 
+            SUPERCLASS = NSMatrix; 
+        }, 
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {CLASS = MySound; LANGUAGE = ObjC; SUPERCLASS = Sound; }, 
+        {CLASS = SelectionNotifyMatrix; LANGUAGE = ObjC; SUPERCLASS = NSMatrix; }, 
+        {
+            ACTIONS = {itemStatusClicked = id; }; 
+            CLASS = ToDoDocument; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {calendar = id; dayLabel = id; itemList = id; statusList = id; }; 
+            SUPERCLASS = NSDocument; 
+        }
+    ); 
+    IBVersion = 1; 
+}

File pyobjc/Examples/Todo/English.lproj/ToDoDocument.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>15 -15 364 240 0 44 1024 702 </string>
+	<key>IBFramework Version</key>
+	<string>263.2</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>125</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>5S66</string>
+</dict>
+</plist>

File pyobjc/Examples/Todo/English.lproj/ToDoDocument.nib/objects.nib

Binary file added.

File pyobjc/Examples/Todo/English.lproj/ToDoInfoWindow.nib/classes.nib

+{
+    IBClasses = (
+        {CLASS = First