[Checkins] SVN: Zem/trunk/ Initial checkin of Zem 0.9.8

Zachery Bir zbir at urbanape.com
Wed Dec 20 09:23:20 EST 2006


Log message for revision 71616:
  Initial checkin of Zem 0.9.8
  

Changed:
  A   Zem/trunk/
  A   Zem/trunk/LICENSE.txt
  A   Zem/trunk/Nibs/
  A   Zem/trunk/Nibs/InfoPlist.strings
  A   Zem/trunk/Nibs/MainMenu.nib/
  A   Zem/trunk/Nibs/MainMenu.nib/classes.nib
  A   Zem/trunk/Nibs/MainMenu.nib/info.nib
  A   Zem/trunk/Nibs/MainMenu.nib/keyedobjects.nib
  A   Zem/trunk/Nibs/MainMenu.nib/objects.nib
  A   Zem/trunk/Nibs/Preferences.nib/
  A   Zem/trunk/Nibs/Preferences.nib/classes.nib
  A   Zem/trunk/Nibs/Preferences.nib/info.nib
  A   Zem/trunk/Nibs/Preferences.nib/keyedobjects.nib
  A   Zem/trunk/PreferenceController.py
  A   Zem/trunk/Readme.html
  A   Zem/trunk/Readme.txt
  A   Zem/trunk/Resources/
  A   Zem/trunk/Resources/Files.png
  A   Zem/trunk/Resources/HelperApps.png
  A   Zem/trunk/Resources/WebDAV.png
  A   Zem/trunk/Resources/ZEM.icns
  A   Zem/trunk/Resources/ZEMDocument.icns
  A   Zem/trunk/Resources/add_disabled.png
  A   Zem/trunk/Resources/add_idle.png
  A   Zem/trunk/Resources/add_pressed.png
  A   Zem/trunk/Resources/com.urbanape.zopeeditmanager.plist
  A   Zem/trunk/Resources/remove_disabled.png
  A   Zem/trunk/Resources/remove_idle.png
  A   Zem/trunk/Resources/remove_pressed.png
  A   Zem/trunk/ZemAppDelegate.py
  A   Zem/trunk/ZopeDocument.py
  A   Zem/trunk/images/
  A   Zem/trunk/images/mozilla_config.png
  A   Zem/trunk/images/zem_files_prefs.png
  A   Zem/trunk/images/zem_helper_apps_prefs.png
  A   Zem/trunk/images/zem_screenshot_1.png
  A   Zem/trunk/images/zem_screenshot_2.png
  A   Zem/trunk/images/zem_webdav_prefs.png
  A   Zem/trunk/setup.py

-=-
Added: Zem/trunk/LICENSE.txt
===================================================================
--- Zem/trunk/LICENSE.txt	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/LICENSE.txt	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,53 @@
+Zope Public License (ZPL) Version 2.0
+-----------------------------------------------
+
+This software is Copyright (c) Zope Corporation (tm) and
+Contributors. All rights reserved.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the above
+   copyright notice, this list of conditions, and the following
+   disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials
+   provided with the distribution.
+
+3. The name Zope Corporation (tm) must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from Zope Corporation.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use Servicemarks
+   (sm) or Trademarks (tm) of Zope Corporation. Use of them is
+   covered in a separate agreement (see
+   http://www.zope.com/Marks).
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.


Property changes on: Zem/trunk/LICENSE.txt
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zem/trunk/Nibs/InfoPlist.strings
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Nibs/InfoPlist.strings
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zem/trunk/Nibs/MainMenu.nib/classes.nib
===================================================================
--- Zem/trunk/Nibs/MainMenu.nib/classes.nib	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Nibs/MainMenu.nib/classes.nib	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,19 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {finishEdits = id; performClose = id; showPreferencePanel = id; }; 
+            CLASS = ZemAppDelegate; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {
+                "current_edits" = NSTableView; 
+                finish = id; 
+                "sync_message" = id; 
+                "sync_spinner" = id; 
+                window = id; 
+            }; 
+            SUPERCLASS = NSObject; 
+        }
+    ); 
+    IBVersion = 1; 
+}
\ No newline at end of file


Property changes on: Zem/trunk/Nibs/MainMenu.nib/classes.nib
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zem/trunk/Nibs/MainMenu.nib/info.nib
===================================================================
--- Zem/trunk/Nibs/MainMenu.nib/info.nib	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Nibs/MainMenu.nib/info.nib	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,22 @@
+<?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>IBDocumentLocation</key>
+	<string>11 30 356 240 0 0 1440 878 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>18 522 312 44 0 0 1440 878 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>446.1</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>202</integer>
+		<integer>29</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>8L2127</string>
+</dict>
+</plist>


Property changes on: Zem/trunk/Nibs/MainMenu.nib/info.nib
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zem/trunk/Nibs/MainMenu.nib/keyedobjects.nib
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Nibs/MainMenu.nib/keyedobjects.nib
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zem/trunk/Nibs/MainMenu.nib/objects.nib
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Nibs/MainMenu.nib/objects.nib
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zem/trunk/Nibs/Preferences.nib/classes.nib
===================================================================
--- Zem/trunk/Nibs/Preferences.nib/classes.nib	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Nibs/Preferences.nib/classes.nib	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,36 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {
+                addHelper = id; 
+                chooseTempDir = id; 
+                removeHelper = id; 
+                savePreferences = id; 
+                setAlwaysBorrowWebDAVLocks = id; 
+                setCleanupFiles = id; 
+                setConfirmOnFinish = id; 
+                setSaveInterval = id; 
+                setUseWebDAVLocks = id; 
+            }; 
+            CLASS = PreferenceController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {
+                "add_helper" = id; 
+                "always_borrow_webdav_locks" = id; 
+                "cleanup_files" = id; 
+                "confirm_on_finish" = id; 
+                "files_pane" = id; 
+                "helper_apps" = id; 
+                "helper_apps_pane" = id; 
+                "remove_helper" = id; 
+                "save_interval" = id; 
+                "temp_dir" = id; 
+                "use_webdav_locks" = id; 
+                "webdav_pane" = id; 
+            }; 
+            SUPERCLASS = NSWindowController; 
+        }
+    ); 
+    IBVersion = 1; 
+}
\ No newline at end of file


Property changes on: Zem/trunk/Nibs/Preferences.nib/classes.nib
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zem/trunk/Nibs/Preferences.nib/info.nib
===================================================================
--- Zem/trunk/Nibs/Preferences.nib/info.nib	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Nibs/Preferences.nib/info.nib	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,28 @@
+<?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>IBDocumentLocation</key>
+	<string>376 29 356 240 0 0 1440 878 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>15</key>
+		<string>510 525 420 122 0 0 1440 878 </string>
+		<key>16</key>
+		<string>132 347 420 342 0 0 1440 878 </string>
+		<key>7</key>
+		<string>341 643 420 195 0 0 1440 878 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>446.1</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>15</integer>
+		<integer>6</integer>
+		<integer>7</integer>
+		<integer>16</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>8L2127</string>
+</dict>
+</plist>


Property changes on: Zem/trunk/Nibs/Preferences.nib/info.nib
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zem/trunk/Nibs/Preferences.nib/keyedobjects.nib
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Nibs/Preferences.nib/keyedobjects.nib
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zem/trunk/PreferenceController.py
===================================================================
--- Zem/trunk/PreferenceController.py	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/PreferenceController.py	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,357 @@
+#
+#  PreferenceController.py
+#  ZopeEditManager
+#
+#  Created by Zachery Bir on 2003-10-29.
+#  Copyright (c) 2004 Zope Corporation. All rights reserved.
+#
+
+# library imports
+import os, re
+import objc
+from objc import YES, NO
+from PyObjCTools import NibClassBuilder
+
+# import needed classes/functions from Foundation
+from Foundation import *
+
+# import Nib loading functionality from AppKit
+from AppKit import *
+
+# local product imports
+
+MACOSX_VERSION = os.uname()[2] >= u'7.0.0' and u'panther' or u'jaguar'
+
+def addToolbarItem(aController, anIdentifier, aLabel, aPaletteLabel,
+                   aToolTip, aTarget, anAction, anItemContent, aMenu):
+    """
+    Adds an freshly created item to the toolbar defined by
+    aController.  Makes a number of assumptions about the
+    implementation of aController.  It should be refactored into a
+    generically useful toolbar management untility.
+    """
+    toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
+
+    toolbarItem.setLabel_(aLabel)
+    toolbarItem.setPaletteLabel_(aPaletteLabel)
+    toolbarItem.setToolTip_(aToolTip)
+    toolbarItem.setTarget_(aTarget)
+    if anAction:
+        toolbarItem.setAction_(anAction)
+
+    if type(anItemContent) == NSImage:
+        toolbarItem.setImage_(anItemContent)
+    else:
+        toolbarItem.setView_(anItemContent)
+        bounds = anItemContent.bounds()
+        minSize = (100, bounds[1][1])
+        maxSize = (1000, bounds[1][1])
+        toolbarItem.setMinSize_( minSize )
+        toolbarItem.setMaxSize_( maxSize )
+
+    if aMenu:
+        menuItem = NSMenuItem.alloc().init()
+        menuItem.setSubmenu_(aMenu)
+        menuItem.setTitle_( aMenu.title() )
+        toolbarItem.setMenuFormRepresentation_(menuItem)
+
+    aController._toolbarItems[anIdentifier] = toolbarItem
+
+# create ObjC classes as defined in Preferences.nib
+NibClassBuilder.extractClasses("Preferences")
+class PreferenceController(NibClassBuilder.AutoBaseClass):
+    """
+    The Controller object for managing various preference variables.
+    """
+    def addHelper_(self, sender):
+        lastIndex = self.numberOfRowsInTableView_(self.helper_apps)
+        self._edited_fields.append({
+            u'type' : u'Type %d' % lastIndex,
+            u'extension' : u'Ext %d' % lastIndex,
+            u'editor' : u'Editor %d' % lastIndex})
+        self.helper_apps.reloadData()
+
+    def removeHelper_(self, sender):
+        row = self.helper_apps.selectedRow()
+        if row != -1:
+            del self._edited_fields[row]
+            self.helper_apps.reloadData()
+
+    def savePreferences_(self, sender):
+        self.sud.setBool_forKey_(bool(self._cleanup_files), u'cleanup_files')
+        self.sud.setBool_forKey_(bool(self._confirm_on_finish),
+                                 u'confirm_on_finish')
+        self.sud.setBool_forKey_(bool(self._use_webdav_locks), u'use_locks')
+        self.sud.setBool_forKey_(bool(self._always_borrow_webdav_locks),
+                                 u'always_borrow_locks')
+        self.sud.setFloat_forKey_(self._save_interval, u'save_interval')
+        self.sud.setObject_forKey_(self._temp_dir, u'temp_dir')
+
+        for field in self._edited_fields:
+            self._helper_apps[field[u'type']] = {
+                u'editor' : field[u'editor'],
+                u'extension' : field[u'extension']}
+        self.sud.setObject_forKey_(self._helper_apps, u'helper_apps')
+
+        self.sud.synchronize()
+
+    def chooseTempDir_(self, sender):
+        panel = NSOpenPanel.openPanel()
+        panel.setCanChooseDirectories_(True)
+        panel.setCanChooseFiles_(False)
+        panel.setAllowsMultipleSelection_(False)
+        panel.setResolvesAliases_(True)
+        bSFD = panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo_
+        bSFD(self._temp_dir, # directory
+             None, # file
+             None, # types
+             self.window(), # modalForWindow
+             self, # modalDelegate
+             self.chooseTempDirDidEnd_returnCode_contextInfo_, # didEndSelector
+             0 # contextInfo
+             )
+
+    def chooseTempDirDidEnd_returnCode_contextInfo_(self, sheet, returnCode,
+                                                    contextInfo):
+        if returnCode:
+            self._temp_dir = sheet.filenames()[0]
+            self.temp_dir.setStringValue_(self._temp_dir)
+
+    chooseTempDirDidEnd_returnCode_contextInfo_ = objc.selector(
+        chooseTempDirDidEnd_returnCode_contextInfo_,
+        selector='chooseTempDirDidEnd:returnCode:contextInfo:',
+        signature='@@:@i@'
+        )
+
+    def setCleanupFiles_(self, sender):
+        self._cleanup_files = self.cleanup_files.state()
+
+    def setConfirmOnFinish_(self, sender):
+        self._confirm_on_finish = self.confirm_on_finish.state()
+
+    def setSaveInterval_(self, sender):
+        self._save_interval = self.save_interval.floatValue()
+
+    def setUseWebDAVLocks_(self, sender):
+        self._use_webdav_locks = self.use_webdav_locks.state()
+        if self._use_webdav_locks:
+            self.always_borrow_webdav_locks.setEnabled_(YES)
+        else:
+            self.always_borrow_webdav_locks.setState_(0)
+            self.always_borrow_webdav_locks.setEnabled_(NO)
+
+    def setAlwaysBorrowWebDAVLocks_(self, sender):
+        self._always_borrow_webdav_locks = self.always_borrow_webdav_locks.state()
+
+    def windowWillClose_(self, sender):
+        self.savePreferences_(self)
+
+    def numberOfRowsInTableView_(self, tableView):
+        return len(self._edited_fields)
+
+    def tableView_objectValueForTableColumn_row_(self, tableView,
+                                                 aColumn, aRow):
+        ident = aColumn.identifier()
+        return self._edited_fields[aRow].get(ident, u'')
+
+    def tableView_setObjectValue_forTableColumn_row_(self, aTableView,
+                                                     anObject, aTableColumn,
+                                                     rowIndex):
+        ident = aTableColumn.identifier()
+        self._edited_fields[rowIndex][ident] = anObject
+
+    def tableView_shouldEditTableColumn_row_(self, aTableView, aTableColumn,
+                                             rowIndex):
+        return YES
+
+    if MACOSX_VERSION == u'panther':
+        def tableView_sortDescriptorsDidChange_(self, aTableView,
+                                                oldDescriptors):
+
+            array = NSArray.arrayWithArray_(self._edited_fields)
+            self._edited_fields = array.sortedArrayUsingDescriptors_(
+                aTableView.sortDescriptors())
+            self.helper_apps.reloadData()
+
+    def tableViewSelectionDidChange_(self, aNotification):
+        if self.helper_apps.selectedRow() == -1:
+            self.remove_helper.setEnabled_(NO)
+            self.remove_helper.setImage_(self.remove_disabled)
+        else:
+            self.remove_helper.setEnabled_(YES)
+            self.remove_helper.setImage_(self.remove_idle)
+
+    def awakeFromNib(self):
+        self.createToolbar()
+        self.cleanup_files.setState_(self._cleanup_files and 1 or 0)
+        self.confirm_on_finish.setState_(self._confirm_on_finish and 1 or 0)
+        self.save_interval.setFloatValue_(
+            self._save_interval and self._save_interval or 0.0)
+        self.use_webdav_locks.setState_(self._use_webdav_locks and 1 or 0)
+        self.always_borrow_webdav_locks.setState_(
+            self._always_borrow_webdav_locks and 1 or 0)
+        self.always_borrow_webdav_locks.setEnabled_(
+            self._use_webdav_locks and 1 or 0)
+        self.temp_dir.setStringValue_(self._temp_dir)
+
+        if MACOSX_VERSION == u'panther':
+            self.helper_apps.tableColumnWithIdentifier_(
+        u"type").setSortDescriptorPrototype_(self.typeDescriptor)
+            self.helper_apps.tableColumnWithIdentifier_(
+        u"editor").setSortDescriptorPrototype_(self.editorDescriptor)
+            self.helper_apps.tableColumnWithIdentifier_(
+        u"extension").setSortDescriptorPrototype_(self.extensionDescriptor)
+            self.helper_apps.setSortDescriptors_([self.typeDescriptor,])
+
+        self.cached_window_frame = self.window().frame()
+        self.cached_files_pane_frame = self.files_pane.frame()
+        self.cached_webdav_pane_frame = self.webdav_pane.frame()
+        self.cached_helper_apps_pane_frame = self.helper_apps_pane.frame()
+
+        self.remove_helper.setEnabled_(NO)
+        self.remove_helper.setImage_(self.remove_disabled)
+
+        self.window().toolbar().setSelectedItemIdentifier_(u"Files Pane")
+        self.showFilesPane_(self)
+
+    def createToolbar(self):
+        toolbar = NSToolbar.alloc().initWithIdentifier_(u"Preferences Window")
+        toolbar.setDelegate_(self)
+        toolbar.setAllowsUserCustomization_(NO)
+        toolbar.setAutosavesConfiguration_(YES)
+
+        self.createToolbarItems()
+
+        self.window().setToolbar_(toolbar)
+
+    def createToolbarItems(self):
+        addToolbarItem(self, u'Files Pane', u'Files', u'Files',
+            u'Set File Preferences', None, 'showFilesPane:',
+            NSImage.imageNamed_(u"Files"), None)
+
+        addToolbarItem(self, u'WebDAV Pane', u'WebDAV', u'WebDAV',
+            u'Set WebDAV Preferences', None, 'showWebDAVPane:',
+            NSImage.imageNamed_(u"WebDAV"), None)
+
+        addToolbarItem(self, u'Helper Apps Pane', u'Helper Apps',
+            u'Helper Apps', u'Set Helper Apps Preferences', None,
+            'showHelperAppsPane:', NSImage.imageNamed_(u"HelperApps"), None)
+
+        self._toolbarDefaultItemIdentifiers = [
+            u'Files Pane',
+            u'WebDAV Pane',
+            u'Helper Apps Pane',
+            ]
+
+    def toolbarDefaultItemIdentifiers_(self, anIdentifier):
+        return self._toolbarDefaultItemIdentifiers
+
+    def toolbarAllowedItemIdentifiers_(self, anIdentifier):
+        return self._toolbarDefaultItemIdentifiers
+
+    def toolbarSelectableItemIdentifiers_(self, anIdentifier):
+        return self._toolbarDefaultItemIdentifiers
+
+    def toolbarWillAddItem_(self, notification):
+        pass
+
+    def toolbarDidRemoveItem_(self, notification):
+        pass
+
+    def toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar_(self,
+        toolbar, itemIdentifier, flag):
+        newItem = NSToolbarItem.alloc().initWithItemIdentifier_(itemIdentifier)
+        item = self._toolbarItems[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
+
+    def showFilesPane_(self, sender):
+        self.resizeViewFor_(self.cached_files_pane_frame)
+        self.window().setContentView_(self.files_pane)
+
+    def showWebDAVPane_(self, sender):
+        self.resizeViewFor_(self.cached_webdav_pane_frame)
+        self.window().setContentView_(self.webdav_pane)
+
+    def showHelperAppsPane_(self, sender):
+        self.resizeViewFor_(self.cached_helper_apps_pane_frame)
+        self.window().setContentView_(self.helper_apps_pane)
+
+    def resizeViewFor_(self, view_frame):
+        windowFrame = self.window().contentRectForFrameRect_styleMask_(
+            self.window().frame(), self.window().styleMask())
+        newWindowHeight = NSHeight(view_frame)
+        toolbarHeight = 0.0
+        if self.window().toolbar().isVisible():
+            toolbarHeight = NSHeight(
+                self.window().toolbar()._toolbarView().frame())
+        newWindowFrame = self.window().frameRectForContentRect_styleMask_(
+            ((NSMinX(windowFrame), NSMaxY(windowFrame) - newWindowHeight),
+             (NSWidth(windowFrame), newWindowHeight)),
+            self.window().styleMask())
+        self.window().setFrame_display_animate_(newWindowFrame,
+                                                YES,
+                                                self.window().isVisible())
+
+    def init(self):
+
+        self = self.initWithWindowNibName_(u"Preferences")
+
+        self._edited_fields = []
+        self._toolbarItems = {}
+        self._toolbarDefaultItemIdentifiers = []
+
+        self.sud = NSUserDefaults.standardUserDefaults()
+        self._cleanup_files = self.sud.boolForKey_(u'cleanup_files')
+        self._confirm_on_finish = self.sud.boolForKey_(u'confirm_on_finish')
+        self._save_interval = self.sud.floatForKey_(u'save_interval')
+        self._use_webdav_locks = self.sud.boolForKey_(u'use_locks')
+        self._always_borrow_webdav_locks = self.sud.boolForKey_(
+            u'always_borrow_locks')
+        self._helper_apps = NSMutableDictionary.dictionaryWithDictionary_(
+            self.sud.dictionaryForKey_(u'helper_apps'))
+        self._temp_dir = self.sud.stringForKey_(u'temp_dir') or u'/tmp'
+
+        # This button will change, so we should pre-load them
+        self.remove_idle = NSImage.imageNamed_(u"remove_idle")
+        self.remove_disabled = NSImage.imageNamed_(u"remove_disabled")
+
+        for x in range(len(self._helper_apps.keys())):
+            type = self._helper_apps.keys()[x]
+            extension = self._helper_apps[type].get(u'extension', u'')
+            editor = self._helper_apps[type].get(u'editor', u'')
+            self._edited_fields.append({u'type': type,
+                                        u'extension': extension,
+                                        u'editor': editor})
+
+        if MACOSX_VERSION == u'panther':
+            self.typeDescriptor = (
+                NSSortDescriptor.alloc().initWithKey_ascending_selector_(
+                    u"type", YES, 'caseInsensitiveCompare:'))
+
+            self.editorDescriptor = (
+                NSSortDescriptor.alloc().initWithKey_ascending_selector_(
+                    u"editor", YES, 'caseInsensitiveCompare:'))
+
+            self.extensionDescriptor = (
+                NSSortDescriptor.alloc().initWithKey_ascending_selector_(
+                    u"extension", YES, 'caseInsensitiveCompare:'))
+
+        return self
+


Property changes on: Zem/trunk/PreferenceController.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zem/trunk/Readme.html
===================================================================
--- Zem/trunk/Readme.html	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Readme.html	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,178 @@
+<html>
+  <head>
+    <title>Zem</title>
+  </head>
+  <body>
+    <h1>Zem</h1>
+
+    <p>Zem is an implementation of Casey Duncan's excellent External
+      Editor for Mac OS X, providing Mac users with flexibility in
+      their choice of content-specific editors, and as many concurrent
+      editors as they need running at the same time.</p>
+
+    <p>Taking Casey's work, and adapting it for Mac OS X users using
+      the PyObjC bridge, I provide the same functionality, but in a
+      native Mac OS X application with an intuitive graphical user
+      interface. This means that you can specify any Mac OS X
+      application (Carbon or Cocoa) to act as the editor for a MIME
+      type, type group, or Zope meta_type. And you can have as many
+      concurrent edits as you like, since all edits are handled
+      through Zem.</p>
+
+    <h2>Installing It</h2>
+
+    <p>Drag the ZopeEditManger application to the Applications folder
+      in your hard drive or home directory. You can use the built-in
+      preferences, one of the supplied preference files, or edit your
+      own.</p>
+
+    <h2>Using It</h2>
+
+    <p><img src="images/zem_screenshot_1.png" alt=""></p>
+
+    <p>As you download links from the Zope Management Interface (ZMI)
+      or the Content Management Framework (CMF) or Plone, new
+      documents will accumulate in the main table.</p>
+
+    <p><img src="images/zem_screenshot_2.png" alt=""></p>
+
+    <p>As saves are made in the editor, Zem will synch those changes
+      back to the server, and display the time of the last synch. To
+      remove a document from Zem, simply select it from the table, and
+      click the 'Finish' button, or press the 'delete' key.</p>
+
+    <h2>Configuring your Browser</h2>
+
+    <p>Currently, the only browser fully set to work with Zem is
+      Mozilla. Internet Explorer supports the configuration of File
+      Helpers, but I've been unable to get it working quite right. In
+      any case, other browsers will download a particular file. That
+      file can be dragged onto the Zem icon and it will work fine. To
+      enable Zem in Mozilla, select the Helper Applications pane from
+      the Navigator group, and create a New Type called
+      'application/x-zope-edit', and choose Zem with the Application
+      picker.</p>
+
+    <p><img src="images/mozilla_config.png" alt=""></p>
+
+    <h2>Configuration</h2>
+
+    <p>Zem provides a GUI Preferences panel. Just choose
+      "Preferences..." from the Zem menu, or press Command-, to open
+      the window.</p>
+
+    <h2>Options</h2>
+
+    <p>The available options for Zem are (names in parentheses are the
+      corresponding key names in the Preferences plist):</p>
+
+    <h3>Files Prefs</h3>
+
+    <p><img src="images/zem_files_prefs.png" alt=""></p>
+
+    <dl>
+      <dt>Cleanup Files (<code>cleanup_files</code>)</dt>
+      <dd>Whether to delete the temp files created.<br/><br/>WARNING
+        the temp file coming from the browser contains authentication
+        information and therefore setting this to &lt;false/&gt; is a
+        security risk, especially on shared machines. If set to
+        &lt;true/&gt;, that file is deleted at the earliest
+        opportunity, before the editor is even spawned. Set to
+        &lt;false/&gt; for debugging only.</dd>
+
+      <dt>Confirm on Finish (<code>confirm_on_finish</code>)</dt>
+      <dd>When you are finished locally editing a file,
+        Zem will ask you to confirm this. You can disable
+        this behavior by unchecking this button.</dd>
+
+      <dt>Save Interval (<code>save_interval</code>)</dt>
+      <dd>The interval in seconds that the helper application checks
+        the edited file for changes.</dd>
+
+      <dt>Temporary Files (<code>temp_dir</code>)</dt>
+      <dd>Path to store local copies of object data being
+        edited. Defaults to /tmp (/private/tmp).</dd>
+
+    </dl>
+
+    <h3>WebDAV Prefs</h3>
+
+    <p><img src="images/zem_webdav_prefs.png" alt=""></p>
+
+    <dl>
+
+      <dt>Use WebDAV Locks (<code>use_locks</code>)</dt>
+      <dd>Whether to use WebDAV locking. The user editing must have
+        the proper WebDAV related permissions for this to work.</dd>
+
+      <dt>Always borrow WebDAV Locks
+        (<code>always_borrow_locks</code>)</dt>
+      <dd>When use_locks is enabled this features suppresses warnings
+        when trying to edit an object you have already locked.  When
+        enabled, external editor will always "borrow" the existing
+        lock token instead of doing the locking itself. This is useful
+        when using CMFStaging for instance. If omitted, this option
+        defaults to &lt;false/&gt;.</dd>
+
+    </dl>
+
+    <h3>Helper Apps Prefs</h3>
+
+    <p><img src="images/zem_helper_apps_prefs.png" alt=""></p>
+
+    <p>To edit an entry, simply double click on the cell, and edit. To
+      add a new Helper App, click the '+' button. To remove an entry,
+      select a row, and click '-'. You can sort the table by any of
+      the columns, ascending or descending.</p>
+
+    <dl>
+
+      <dt>Type</dt>
+      <dd>Either the meta_type of the Zope object, or the MIME type of
+        the file it would represent.</dd>
+
+      <dt>Extension (<code>extension</code>)</dt>
+      <dd>The file extension to add to the content file. Allows better
+        handling of images and can improve syntax highlighting.</dd>
+
+      <dt>Editor (<code>editor</code>)</dt>
+      <dd>Application name used to invoke the editor application.</dd>
+
+    </dl>
+
+    <h2>Credits</h2>
+
+    <p>I would like to thank the following people for their help in
+      this endeavor:</p>
+
+    <ul>
+
+      <li>Casey Duncan, for the excellent ExternalEditor product, and
+        the initial zopeedit.py</li>
+      <li>Bill Bumgarner and Bob Ippolito, for their hard work on
+        PyObjC, and their endless help on #macpython on
+        irc.freenode.net.</li>
+      <li>Jes&uacute;s D&iacute;az Blanco, for his help refining the
+        UI</li>
+      <li>Sascha Gresk, Eric W. Brown, Brian Morton, Jeff Putsch,
+        Aurelius Prochazka, J&uuml;rgen Valldorf, Michael Bond,
+        S&eacute;bastien Verbois, Jonah Crawford, Jean-Philippe Rey,
+        Jim Allman, and Alan Runyan for using it and supplying
+        valuable feedback</li>
+
+    </ul>
+
+    <h2>Conclusion</h2>
+
+    <p>I hope you enjoy using this software. If you have any comments,
+      suggestions or would like to report a bug, send an email with
+      'Zem' in the Subject line to the author:</p>
+
+    <p>Zachery Bir <a href="mailto:zbir at urbanape.com?subject=Zem%20Feedback">&lt;zbir at urbanape.com&gt;</a></p>
+
+    <hr/>
+
+    <p>&copy; 2003-2005, Zachery Bir and Zope Corporation. All rights
+      reserved.</p>
+  </body>
+</html>


Property changes on: Zem/trunk/Readme.html
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zem/trunk/Readme.txt
===================================================================
--- Zem/trunk/Readme.txt	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Readme.txt	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,146 @@
+Zem
+
+Zem is an implementation of Casey Duncan's excellent External Editor
+for Mac OS X, providing Mac users with flexibility in their choice of
+content-specific editors, and as many concurrent editors as they need
+running at the same time.
+
+Taking Casey's work, and adapting it for Mac OS X users using the
+PyObjC bridge, I provide the same functionality, but in a native Mac
+OS X application with an intuitive graphical user interface. This
+means that you can specify any Mac OS X application (Carbon or Cocoa)
+to act as the editor for a MIME type, type group, or Zope
+meta_type. And you can have as many concurrent edits as you like,
+since all edits are handled through Zem.
+
+Installing It
+
+Drag the ZopeEditManger application to the Applications folder in your
+hard drive or home directory. You can use the built-in preferences,
+one of the supplied preference files, or edit your own.
+
+Using It
+
+As you download links from the Zope Management Interface (ZMI) or the
+Content Management Framework (CMF) or Plone, new documents will
+accumulate in the main table.
+
+As saves are made in the editor, Zem will synch those changes back to
+the server, and display the time of the last synch. To remove a
+document from Zem, simply select it from the table, and click the
+'Finish' button, or press the 'delete' key.
+
+Configuring your Browser
+
+Currently, the only browser fully set to work with Zem is
+Mozilla. Internet Explorer supports the configuration of File Helpers,
+but I've been unable to get it working quite right. In any case, other
+browsers will download a particular file. That file can be dragged
+onto the Zem icon and it will work fine. To enable Zem in Mozilla,
+select the Helper Applications pane from the Navigator group, and
+create a New Type called 'application/x-zope-edit', and choose Zem
+with the Application picker.
+
+Configuration
+
+Zem provides a GUI Preferences panel. Just choose "Preferences..."
+from the Zem menu, or press Command-, to open the window.
+
+Options
+
+The available options for Zem are (names in parentheses are the
+corresponding key names in the Preferences plist):
+
+Files Prefs
+
+Cleanup Files (cleanup_files)
+
+    Whether to delete the temp files created.
+
+    WARNING the temp file coming from the browser contains
+    authentication information and therefore setting this to <false/>
+    is a security risk, especially on shared machines. If set to
+    <true/>, that file is deleted at the earliest opportunity, before
+    the editor is even spawned. Set to <false /> for debugging only.
+
+Confirm on Finish (confirm_on_finish)
+
+    When you are finished locally editing a file, Zem will ask you to
+    confirm this. You can disable this behavior by unchecking this
+    button.
+
+Save Interval (save_interval)
+
+    The interval in seconds that the helper application checks the
+    edited file for changes.
+
+Temporary Files (temp_dir)
+
+    Path to store local copies of object data being edited. Defaults
+    to /tmp (/ private/tmp).
+
+WebDAV Prefs
+
+Use WebDAV Locks (use_locks)
+
+    Whether to use WebDAV locking. The user editing must have the
+    proper WebDAV related permissions for this to work.
+
+Always borrow WebDAV Locks (always_borrow_locks)
+
+    When use_locks is enabled this features suppresses warnings when
+    trying to edit an object you have already locked. When enabled,
+    external editor will always "borrow" the existing lock token
+    instead of doing the locking itself. This is useful when using
+    CMFStaging for instance. If omitted, this option defaults to
+    <false/>.
+
+Helper Apps Prefs
+
+To edit an entry, simply double click on the cell, and edit. To add a
+new Helper App, click the '+' button. To remove an entry, select a
+row, and click '-'. You can sort the table by any of the columns,
+ascending or descending.
+
+Type
+
+    Either the meta_type of the Zope object, or the MIME type of the
+    file it would represent.
+
+Extension (extension)
+
+    The file extension to add to the content file. Allows better
+    handling of images and can improve syntax highlighting.
+
+Editor (editor)
+
+    Application name used to invoke the editor application.
+
+Credits
+
+I would like to thank the following people for their help in this
+endeavor:
+
+  - Casey Duncan, for the excellent ExternalEditor product, and the
+    initial zopeedit.py
+
+  - Bill Bumgarner and Bob Ippolito, for their hard work on PyObjC,
+    and their endless help on #macpython on irc.freenode.net.
+
+  - Jesus Diaz Blanco, for his help refining the UI
+
+  - Sascha Gresk, Eric W. Brown, Brian Morton, Jeff Putsch, Aurelius
+    Prochazka, Jurgen Valldorf, Michael Bond, Sebastien Verbois, Jonah
+    Crawford, Jean-Philippe Rey, Jim Allman, and Alan Runyan for using
+    it and supplying valuable feedback
+
+Conclusion
+
+I hope you enjoy using this software. If you have any comments,
+suggestions or would like to report a bug, send an email with 'Zem' in
+the Subject line to the author:
+
+Zachery Bir <zbir at urbanape.com>
+
+(C) 2003-2005, Zachery Bir and Zope Corporation. All rights reserved.
+


Property changes on: Zem/trunk/Readme.txt
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zem/trunk/Resources/Files.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/Files.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/HelperApps.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/HelperApps.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/WebDAV.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/WebDAV.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/ZEM.icns
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/ZEM.icns
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zem/trunk/Resources/ZEMDocument.icns
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/ZEMDocument.icns
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zem/trunk/Resources/add_disabled.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/add_disabled.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/add_idle.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/add_idle.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/add_pressed.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/add_pressed.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/com.urbanape.zopeeditmanager.plist
===================================================================
--- Zem/trunk/Resources/com.urbanape.zopeeditmanager.plist	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/Resources/com.urbanape.zopeeditmanager.plist	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,103 @@
+<?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>helper_apps</key>
+    <dict>
+        <key>DTML Document</key>
+        <dict>
+            <key>extension</key>
+            <string>.dtml</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>DTML Method</key>
+        <dict>
+            <key>extension</key>
+            <string>.dtml</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>Script (Python)</key>
+        <dict>
+            <key>extension</key>
+            <string>.py</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>Page Template</key>
+        <dict>
+            <key>extension</key>
+            <string>.html</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>Z SQL Method</key>
+        <dict>
+            <key>extension</key>
+            <string>.sql</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>image/gif</key>
+        <dict>
+            <key>extension</key>
+            <string>.gif</string>
+            <key>editor</key>
+            <string>Preview</string>
+        </dict>
+        <key>image/jpeg</key>
+        <dict>
+            <key>extension</key>
+            <string>.jpg</string>
+            <key>editor</key>
+            <string>Preview</string>
+        </dict>
+        <key>image/png</key>
+        <dict>
+            <key>extension</key>
+            <string>.png</string>
+            <key>editor</key>
+            <string>Preview</string>
+        </dict>
+        <key>image/*</key>
+        <dict>
+            <key>editor</key>
+            <string>Preview</string>
+        </dict>
+        <key>text/html</key>
+        <dict>
+            <key>extension</key>
+            <string>.html</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>text/xml</key>
+        <dict>
+            <key>extension</key>
+            <string>.xml</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+        <key>text/*</key>
+        <dict>
+            <key>extension</key>
+            <string>.txt</string>
+            <key>editor</key>
+            <string>TextEdit</string>
+        </dict>
+    </dict>
+    <key>save_interval</key>
+    <real>10.0</real>
+    <key>confirm_on_finish</key>
+    <true/>
+    <key>cleanup_files</key>
+    <true/>
+    <key>use_locks</key>
+    <true/>
+    <key>always_borrow_locks</key>
+    <false/>
+    <key>version_check</key>
+    <string>0.9</string>
+</dict>
+</plist>


Property changes on: Zem/trunk/Resources/com.urbanape.zopeeditmanager.plist
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zem/trunk/Resources/remove_disabled.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/remove_disabled.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/remove_idle.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/remove_idle.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/Resources/remove_pressed.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/Resources/remove_pressed.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/ZemAppDelegate.py
===================================================================
--- Zem/trunk/ZemAppDelegate.py	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/ZemAppDelegate.py	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,232 @@
+#
+#  ZemAppDelegate.py
+#  Zem
+#
+#  Created by Zachery Bir on Thu Jun 10 2004.
+#  Copyright (c) 2004 Zope Corporation. All rights reserved.
+#
+
+# library imports
+import os, re
+from objc import YES, NO
+from PyObjCTools import NibClassBuilder, AppHelper
+
+# import needed classes/functions from Foundation
+from Foundation import *
+
+# import Nib loading functionality from AppKit
+from AppKit import *
+
+# local product imports
+from ZopeDocument import ZopeDocument, __version__
+from PreferenceController import PreferenceController
+
+def iterNSIndexSet(s):
+    val = s.lastIndex()
+    while val != NSNotFound:
+        yield val
+        val = s.indexLessThanIndex_(val)
+
+NibClassBuilder.extractClasses("MainMenu")
+class ZemAppDelegate(NibClassBuilder.AutoBaseClass):
+    """
+    The Controller object for managing various processes editing Zope
+    objects externally.
+    """
+    def updateIfModified(self, timer):
+        timerUser = timer.userInfo()
+        for doc in timerUser.current_edits_data:
+            mtime = os.path.getmtime(doc.getContentFile())
+
+            if mtime != doc.last_mtime:
+                # File was modified
+                self.sync_spinner.startAnimation_(self)
+                doc.putChanges()
+                self.sync_spinner.stopAnimation_(self)
+                self.sync_message.setStringValue_(u'Last synched: %s' % NSDate.date().descriptionWithCalendarFormat_timeZone_locale_(u"%a, %d %b %Y at %H:%M:%S %p", None, None))
+                doc.last_mtime = mtime
+
+        return 1
+
+    def performClose_(self, sender):
+        keyWindow = NSApplication.sharedApplication().keyWindow()
+        if keyWindow is self.window:
+            return NO
+        keyWindow.performClose_(sender)
+
+    def showPreferencePanel_(self, sender):
+        if self.preferenceController is None:
+            self.preferenceController = PreferenceController.alloc().init()
+        self.preferenceController.showWindow_(self)
+
+    def application_openFile_(self, app, filename):
+        """
+        Invoked by NSApplication when the app attempts to open a file
+        """
+        NSLog(u'Opening Application')
+
+        zopeDoc = ZopeDocument(filename)
+        zopeDoc.removeFileIfNecessary(filename)
+        self.current_edits_data.append(zopeDoc)
+        self.current_edits.reloadData()
+        return self.openDocument_(zopeDoc)
+
+    def gotoEdit_(self):
+        row = self.current_edits.selectedRow()
+        if row != -1:
+            zopeDoc = self.current_edits_data[row]
+            return self.openDocument_(zopeDoc)
+
+    def openDocument_(self, zopeDoc):
+        if self.sud.boolForKey_(u'use_locks'):
+            if zopeDoc.metadata.get(u'lock-token'):
+                if (self.sud.boolForKey_(u'always_borrow_locks')
+                    or zopeDoc.metadata.get(u'borrow_lock')
+                    or NSRunAlertPanel(u"Borrow Lock?",
+                                       u"This object is already locked by"
+                                       " you in another session. Do you want"
+                                       " to borrow this lock and continue?",
+                                       u"Yes", u"Cancel", None)):
+                    zopeDoc.lock_token = (u'opaquelocktoken:%s'
+                                          % zopeDoc.metadata['lock-token'])
+            else:
+                self.finishEdits_()
+
+        if self.sud.boolForKey_(u'use_locks'):
+            zopeDoc.lock()
+
+        editor = zopeDoc.getEditor()
+        content_file = zopeDoc.getContentFile()
+        return self.ws.openFile_withApplication_andDeactivate_(content_file,
+                                                               editor, YES)
+
+    def finishEdits_(self, sender=None):
+        """
+        We want to get rid of the lock on one or more Zope objects and
+        delete its local copy.
+        """
+        selected = self.current_edits.numberOfSelectedRows()
+        if selected:
+            perform = True
+            if self.sud.boolForKey_(u'confirm_on_finish'):
+                if selected == 1:
+                    msg = u"Are you sure you're finished with this object?"
+                else:
+                    msg = (u"Are you sure you're finished with these %d "
+                           "objects?" % (selected))
+                perform = NSRunAlertPanel(u"Deleting Entry", msg,
+                                          u"Yes", u"Cancel", None)
+            for row in iterNSIndexSet(self.current_edits.selectedRowIndexes()):
+                del self.current_edits_data[row]
+                self.current_edits.reloadData()
+
+    def numberOfRowsInTableView_(self, tableView):
+        return len(self.current_edits_data)
+
+    def tableView_objectValueForTableColumn_row_(self, tableView,
+                                                 aColumn, aRow):
+        ident = aColumn.identifier()
+        return getattr(self.current_edits_data[aRow], 'get' + ident)()
+
+    def tableViewSelectionDidChange_(self, aNotification):
+        if self.current_edits.selectedRow() == -1:
+            self.finish.setEnabled_(NO)
+        else:
+            self.finish.setEnabled_(YES)
+
+    def upgradePrefs(self):
+        new_prefs = {}
+        keep_keys = [u'always_borrow_locks', u'cleanup_files',
+                     u'confirm_on_finish', u'helper_apps', u'save_interval',
+                     u'use_locks', u'version_check']
+        for key in keep_keys:
+            new_prefs[key] = self.sud.objectForKey_(key)
+        edit_keys = self.sud.persistentDomainForName_(self.bundleIdent).keys()
+        helper_apps = self.sud.dictionaryForKey_(u'helper_apps')
+        if helper_apps is None:
+            helper_apps = {}
+        for key in edit_keys:
+            if key not in keep_keys:
+                if self.sud.objectForKey_(key).has_key(u'editor'):
+                    helper_apps[key] = self.sud.objectForKey_(key)
+                else:
+                    sub_keys = self.sud.objectForKey_(key).keys()
+                    for sub_key in sub_keys:
+                        helper_apps[u"%s/%s" % (key, sub_key)] = self.sud.objectForKey_(key)[sub_key]
+        new_prefs[u'helper_apps'] = helper_apps
+
+        # Possibly a new pref this version
+        if not new_prefs.has_key(u'confirm_on_finish'):
+            new_prefs[u'confirm_on_finish'] = YES
+
+        # Update this pref always
+        new_prefs[u'version_check'] = __version__
+
+        self.sud.removePersistentDomainForName_(self.bundleIdent)
+        self.sud.setPersistentDomain_forName_(new_prefs, self.bundleIdent)
+        self.sud.synchronize()
+        NSRunAlertPanel(u"Preferences Updated",
+                        u"Your Preferences have been upgraded.",
+                        u"Close", None, None)
+
+    def resetPrefs(self):
+        self.sud.removePersistentDomainForName_(self.bundleIdent)
+        self.sud.setPersistentDomain_forName_(
+            NSDictionary.dictionaryWithContentsOfFile_(
+                NSBundle.mainBundle().pathForResource_ofType_(
+                    self.bundleIdent, "plist")),
+            self.bundleIdent)
+        self.sud.synchronize()
+        if NSRunAlertPanel(u"Preferences Reset",
+                           u"Your Preferences have been reset.",
+                           u"Edit Prefs", u"Close", None):
+            self.showPreferencePanel_(self)
+
+    def awakeFromNib(self):
+        self.current_edits.setDoubleAction_("gotoEdit:")
+        self.sync_message.setStringValue_(u'')
+        self.finish.setEnabled_(NO)
+
+    def init(self):
+        self.bundleIdent = NSBundle.mainBundle().bundleIdentifier()
+        self.preferenceController = None
+        self.current_edits_data = []
+        self.ws = NSWorkspace.sharedWorkspace()
+        NSUserDefaults.resetStandardUserDefaults()
+        self.sud = NSUserDefaults.standardUserDefaults()
+        pdomain = self.sud.persistentDomainForName_(self.bundleIdent)
+        if pdomain is None:
+            self.sud.setPersistentDomain_forName_(
+                NSDictionary.dictionaryWithContentsOfFile_(
+                    NSBundle.mainBundle().pathForResource_ofType_(
+                        self.bundleIdent, "plist")),
+                self.bundleIdent)
+            self.sud.synchronize()
+
+        version_check = self.sud.stringForKey_(u'version_check')
+        if version_check != __version__:
+            response =  NSRunAlertPanel(u"Preferences Differ",
+                                        u"Your Preferences were set with "
+                                        "an older version of "
+                                        "Zem. Would you "
+                                        "like to upgrade your Preferences?",
+                                        u"Upgrade", u"Start Fresh", None)
+            if response == 1:
+                self.upgradePrefs()
+            if response == 0:
+                self.resetPrefs()
+
+        interval = (self.sud.floatForKey_(u'save_interval')) or 20.0
+        timer = (
+    NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
+                interval, self, 'updateIfModified', self, YES))
+        NSRunLoop.currentRunLoop().addTimer_forMode_(timer,
+                                                     NSDefaultRunLoopMode)
+
+        return self
+
+    def applicationWillTerminate_(self, notification):
+        for x in range(len(self.current_edits_data)):
+            del self.current_edits_data[x]
+
+AppHelper.runEventLoop(argv=[])


Property changes on: Zem/trunk/ZemAppDelegate.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zem/trunk/ZopeDocument.py
===================================================================
--- Zem/trunk/ZopeDocument.py	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/ZopeDocument.py	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,299 @@
+#
+#  ZopeEditController.py
+#  ZopeEditManager
+#
+#  Created by Zachery Bir on 2003-08-20.
+#  Copyright (c) 2004 Zope Corporation. All rights reserved.
+#
+
+# library imports
+import sys, os, re
+import urllib
+from urlparse import urlparse
+from httplib import HTTPConnection, HTTPSConnection
+from tempfile import mktemp
+
+# import needed classes/functions from Foundation
+from Foundation import *
+
+# import Nib loading functionality from AppKit
+from AppKit import *
+
+__version__ = u"0.9"
+
+def fatalError(message):
+    """Show error message and exit"""
+    if NSRunAlertPanel('Sorry',
+                       'Fatal Error: %s' % message,
+                       'Quit', None, None):
+        # Write out debug info to a temp file
+        debug_f = open(mktemp('-zopeedit-traceback.txt'), 'w')
+        try:
+            traceback.print_exc(file=debug_f)
+        finally:
+            debug_f.close()
+        sys.exit(0)
+
+class ZopeDocument:
+
+    def __init__(self, filename):
+        self.filename = filename
+        (self.metadata, self.contents) = self.getMetadataAndContents(filename)
+        meta_type = self.metadata.get('meta_type', None)
+        content_type = self.metadata.get('content_type', 'text/plain')
+
+        self.sud = NSUserDefaults.standardUserDefaults()
+
+        options_group = self.sud.dictionaryForKey_('helper_apps')
+
+        self.options = options_group.get(meta_type,
+                                    options_group.get(content_type, None))
+        if not self.options:
+            content_type, content_subtype = re.split(r'[/_]', content_type, 1)
+            self.options = options_group.get("%s/*" % content_type)
+
+        scheme, self.host, self.path = urlparse(self.metadata['url'])[:3]
+        self.ssl = scheme == 'https'
+
+        self.content_file = self.generateContentFile()
+
+        self.last_mtime = os.path.getmtime(self.content_file)
+
+        if self.ssl:
+            # See if ssl is available
+            try:
+                from socket import ssl
+            except ImportError:
+                fatalError('SSL support is not available on this system. '
+                           'Make sure openssl is installed '
+                           'and reinstall Python.')
+
+        self.saved = 1
+        self.lock_token = None
+        self.did_lock = 0
+
+    def getEditor(self):
+        return self.options['editor']
+
+    def getContentFile(self):
+        return self.content_file
+
+    def getContentFileName(self):
+        return os.path.split(self.content_file)[-1].split(',')[-1]
+
+    def getFilename(self):
+        return self.filename
+
+    def generateContentFile(self):
+        content_file = urllib.unquote('-%s%s' % (self.host, self.path))
+        content_file = content_file.replace('/',
+                                            ',').replace(':',
+                                                         ',').replace(' ',
+                                                                      '_')
+
+        extension = self.options.get('extension', None)
+
+        if extension and not content_file.endswith(extension):
+            content_file = content_file + extension
+
+        if self.sud.stringForKey_('temp_dir'):
+            while 1:
+                temp = os.path.expanduser(self.sud.stringForKey_('temp_dir'))
+                temp = os.tempnam(temp)
+                content_file = '%s%s' % (temp, content_file)
+                if not os.path.exists(content_file):
+                    break
+        else:
+            content_file = mktemp(content_file)
+
+        out_f = open(content_file, 'wb')
+        out_f.write(self.contents)
+        out_f.close()
+
+        return content_file
+
+    def getMetadataAndContents(self, filename):
+        metadata = {}
+
+        in_f = open(filename, 'rb')
+
+        # grab the metadata and build a dictionary
+        while 1:
+            line = in_f.readline()[:-1]
+            if not line: break
+            key, val = line.split(':', 1)
+            metadata[key] = val
+
+        # grab the rest and stick it in contents
+        contents = in_f.read()
+
+        in_f.close()
+
+        return (metadata, contents)
+
+    def removeFileIfNecessary(self, filename):
+        if self.sud.boolForKey_('cleanup_files'):
+            try:
+                os.remove(filename)
+            except OSError:
+                pass # Sometimes we aren't allowed to delete it
+
+    def putChanges(self):
+        """Save changes to the file back to Zope"""
+        if self.sud.boolForKey_('use_locks') and self.lock_token is None:
+            # We failed to get a lock initially, so try again before saving
+            if not self.lock():
+                # Confirm save without lock
+                if not NSRunAlertPanel('Lock Error',
+                                       'Could not acquire lock. '
+                                       'Attempt to save to Zope anyway?',
+                                       'Yes', 'No', None):
+                    return 0
+
+        f = open(self.content_file, 'rb')
+        body = f.read()
+        f.close()
+        headers = {'Content-Type':
+                   self.metadata.get('content_type', 'text/plain')}
+
+        if self.lock_token is not None:
+            headers['If'] = '<%s> (<%s>)' % (self.path, self.lock_token)
+
+        response = self.zopeRequest('PUT', headers, body)
+        del body # Don't keep the body around longer then we need to
+
+        if response.status / 100 != 2:
+            # Something went wrong
+            message = response.read()
+            NSLog(message)
+            if NSRunAlertPanel(message,
+                               'Could not save to Zope.\n'
+                               'Error occurred during HTTP put',
+                               'Retry', 'Cancel', None):
+                return self.putChanges()
+            else:
+                return 0
+        return 1
+
+    def lock(self):
+        """Apply a webdav lock to the object in Zope"""
+        if self.lock_token is not None:
+            return 0 # Already have a lock token
+
+        headers = {'Content-Type':'text/xml; charset="utf-8"',
+                   'Timeout':'infinite',
+                   'Depth':'infinity',
+                  }
+        body = ('<?xml version="1.0" encoding="utf-8"?>\n'
+                '<d:lockinfo xmlns:d="DAV:">\n'
+                '  <d:lockscope><d:exclusive/></d:lockscope>\n'
+                '  <d:locktype><d:write/></d:locktype>\n'
+                '  <d:depth>infinity</d:depth>\n'
+                '  <d:owner>\n'
+                '  <d:href>Zope External Editor</d:href>\n'
+                '  </d:owner>\n'
+                '</d:lockinfo>'
+                )
+
+        response = self.zopeRequest('LOCK', headers, body)
+
+        if response.status / 100 == 2:
+            # We got our lock, extract the lock token and return it
+            reply = response.read()
+            token_start = reply.find('>opaquelocktoken:')
+            token_end = reply.find('<', token_start)
+            if token_start > 0 and token_end > 0:
+                self.lock_token = reply[token_start+1:token_end]
+                self.did_lock = 1
+        else:
+            # We can't lock her sir!
+            if response.status == 423:
+                message = '(object already locked)'
+            else:
+                message = ''
+
+            if NSRunAlertPanel(response.read(),
+                               'Lock request failed %s' % message,
+                               'Retry', 'Cancel', None):
+                self.lock()
+            else:
+                self.did_lock = 0
+        return self.did_lock
+
+    def unlock(self, interactive=1):
+        """Remove webdav lock from edited zope object"""
+        if not self.did_lock or self.lock_token is None:
+            return 0 # nothing to do
+
+        headers = {'Lock-Token':self.lock_token}
+        response = self.zopeRequest('UNLOCK', headers)
+
+        if interactive and response.status / 100 != 2:
+            # Captain, she's still locked!
+            if NSRunAlertPanel(response.read(),
+                               'Unlock request failed',
+                               'Retry', 'Cancel', None):
+                self.unlock()
+            else:
+                self.did_lock = 0
+        else:
+            self.did_lock = 1
+            self.lock_token = None
+        return self.did_lock
+
+    def zopeRequest(self, method, headers={}, body=''):
+        """Send a request back to Zope"""
+        try:
+            if self.ssl:
+                h = HTTPSConnection(self.host)
+            else:
+                h = HTTPConnection(self.host)
+
+            h.putrequest(method, self.path)
+            h.putheader('User-Agent', 'Zope External Editor/%s' % __version__)
+            h.putheader('Connection', 'close')
+
+            for header, value in headers.items():
+                h.putheader(header, value)
+
+            h.putheader("Content-Length", str(len(body)))
+
+            if self.metadata.get('auth','').startswith('Basic'):
+                h.putheader("Authorization", self.metadata['auth'])
+
+            if self.metadata.get('cookie'):
+                h.putheader("Cookie", self.metadata['cookie'])
+
+            h.endheaders()
+            h.send(body)
+            return h.getresponse()
+        except:
+            # On error return a null response with error info
+            class NullResponse:
+                def getheader(self, n, d=None):
+                    return d
+
+                def read(self):
+                    return '(No Response From Server)'
+
+            response = NullResponse()
+            response.reason = sys.exc_info()[1]
+
+            try:
+                response.status, response.reason = response.reason
+            except ValueError:
+                response.status = 0
+
+            if response.reason == 'EOF occurred in violation of protocol':
+                # Ignore this protocol error as a workaround for
+                # broken ssl server implementations
+                response.status = 200
+
+            return response
+
+    def __del__(self):
+        """Let's clean up after ourselves, shall we?"""
+        if self.lock_token:
+            self.unlock(interactive=0)
+
+        os.remove(self.getContentFile())


Property changes on: Zem/trunk/ZopeDocument.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zem/trunk/images/mozilla_config.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/images/mozilla_config.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/images/zem_files_prefs.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/images/zem_files_prefs.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/images/zem_helper_apps_prefs.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/images/zem_helper_apps_prefs.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/images/zem_screenshot_1.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/images/zem_screenshot_1.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/images/zem_screenshot_2.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/images/zem_screenshot_2.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/images/zem_webdav_prefs.png
===================================================================
(Binary files differ)


Property changes on: Zem/trunk/images/zem_webdav_prefs.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: Zem/trunk/setup.py
===================================================================
--- Zem/trunk/setup.py	2006-12-20 14:18:52 UTC (rev 71615)
+++ Zem/trunk/setup.py	2006-12-20 14:23:16 UTC (rev 71616)
@@ -0,0 +1,45 @@
+from distutils.core import setup
+import py2app
+
+plist = dict(
+    CFBundleName = "Zem",
+    CFBundleIdentifier = "com.urbanape.zopeeditmanager",
+    CFBundleShortVersionString = "Version 0.9.8",
+    CFBundleGetInfoString = ("Zem version 0.9.8, "
+                             "Copyright 2003-2006 Zope Corporation"),
+    NSHumanReadableCopyright = "Copyright 2003-2006 Zope Corporation",
+    CFBundleIconFile = "ZEM.icns",
+    CFBundleInfoDictionaryVersion = "6.0",
+    CFBundlePackageType = "APPL",
+    CFBundleSignature = "ZEDM",
+    CFBundleVersion = "0.9.8",
+    NSMainNibFile = "MainMenu",
+    NSPrincipalClass = "NSApplication",
+    CFBundleDevelopmentRegion = "English",
+    CFBundleDocumentTypes = [
+        dict(CFBundleTypeExtensions = ["zem"],
+             CFBundleTypeOSTypes = ["ZEMD"],
+             CFBundleTypeMIMETypes = ["application/x-zope-edit"],
+             CFBundleTypeRole = "Viewer",
+             CFBundleTypeIconFile = "ZEMDocument.icns")])
+
+setup(
+    data_files = ["Nibs/MainMenu.nib",
+                  "Nibs/Preferences.nib",
+                  "Resources/Files.png",
+                  "Resources/HelperApps.png",
+                  "Resources/WebDAV.png",
+                  "Resources/ZEM.icns",
+                  "Resources/ZEMDocument.icns",
+                  "Resources/add_disabled.png",
+                  "Resources/add_idle.png",
+                  "Resources/add_pressed.png",
+                  "Resources/remove_disabled.png",
+                  "Resources/remove_idle.png",
+                  "Resources/remove_pressed.png",
+                  "Resources/com.urbanape.zopeeditmanager.plist"
+                  ],
+    app = [dict(
+        script="ZemAppDelegate.py", plist=plist),
+           ],
+    )


Property changes on: Zem/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Checkins mailing list