# RubyFrictionlessAppDelegate.rb # RubyFrictionless # # Created by Pierce T. Wetter III on 4/3/07. # Copyright __MyCompanyName__ 2007 . All rights reserved. require 'osx/cocoa' OSX.require_framework 'CoreData' OSX.require_framework('CoreGraphics') OSX.require_framework('SyncServices') require 'ToodleDoSyncer.rb' class AppDelegate < OSX::NSObject attr_accessor :contextWindow,:browserWindows, :preferenceWindow, :contextWindowC, :templatesWindow def applicationDidFinishLaunching(notification) @@appDelegate=self objc_send :performSelector,'openAllWindows', :withObject, nil, :afterDelay, 0.0 @doSyncing = NSUserDefaults.standardUserDefaults.boolForKey('dotMacSyncing') syncClient.setSyncAlertHandler_selector(self,'client:mightWantToSyncEntityNames:') if @doSyncing # And check if there were any errors the last time. SACrashReporter.submit #RuNSNotificationCenter.defaultCenter.addObserver_selector_name_object(self,'managedObjectContextDidChange:',OSX::NSManagedObjectContextObjectsDidChangeNotification,nil) end def openAllWindows actions=managedObjectContext.objectsForEntityNamed('Action') actions.each do |a| a.appLaunch end unless actions && actions.count > 0 %w{Work Home Errands Online Email Phone Waiting Weekend}.each do |cname| context=NSEntityDescription.insertNewObjectForEntityForName_inManagedObjectContext("Context",managedObjectContext) managedObjectContext.processPendingChanges context.name=cname end [ "Professional @Work :area :note This is an area/role to hold your job related actions so you can easily focus on just those.", "Personal @Home :area :note This is an area/role to hold your home related actions so you can easily focus on just those.", "Spouse > Personal @Home :area :note This is possibly one of the roles you play in life. ", "Parent > Personal @Home :area :note This is possibly one of the roles you play in life.", "Child > Personal @Home :area :note This is possibly one of the roles you play in life.", "Homeowner > Personal @Home :area :note This is a good place to hold home maintenance tasks.", "Employee > Professional @Work :area :note To Quote Bob Dylan, you gotta serve somebody.", "Manager > Professional @Work :area :note Have people you have to manage?", "Coworker > Professional @Work :area :note I'm sure you work with people.", "Sample Project > Professional :note A project is anything with child actions or child projects", "Sample Action > Sample Project :note A Tasks is anything without any children", "Another Action > Sample Project :note A Tasks is anything without any children", ].each do |aname| newAction= NSEntityDescription.insertNewObjectForEntityForName_inManagedObjectContext("Action",managedObjectContext) newAction.smartName=aname managedObjectContext.processPendingChanges end end actions=managedObjectContext.objectWithValue_forKey_entityNamed("YES","noteOpen",'Action') if actions && actions.count > 0 actions.each {|a| a.openJustNotes} end newBrowserWindow(self) end def newQuickEntryWindow(sender) end def buildPreferenceWindow @preferenceWindow=AMPreferenceWindowController.alloc.initWithAutosaveName("Preferences") defaults={} gp=GeneralPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self) sp=SparklePreferences.alloc.initWithController_appDelegate(@preferenceWindow,self) tp=TwitterPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self) tdp=ToodleDoPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self) mp=MailPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self) syp=SyncPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self) end def preferenceWindow buildPreferenceWindow unless @preferenceWindow @preferenceWindow end def openPreferences(sender) preferenceWindow.showWindow(self) preferenceWindow.window.makeKeyAndOrderFront(self) end def managedObjectModel return @managedObjectModel if @managedObjectModel allBundles = OSX::NSMutableSet.alloc.init url = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.pathForResource_ofType("RubyFrictionless_DataModel","momd"),false) @managedObjectModel = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(url) return @managedObjectModel end # Change this path/code to point to your App's data store. def applicationSupportFolder OSX.NSHomeDirectory.stringByAppendingPathComponent(File.join('Library', 'Application Support', 'Frictionless')) end def managedObjectContext return @managedObjectContext if @managedObjectContext error = OSX::OCObject.new fileManager = OSX::NSFileManager.defaultManager storeFolder = applicationSupportFolder unless fileManager.fileExistsAtPath_isDirectory?(storeFolder, nil) fileManager.createDirectoryAtPath_attributes(storeFolder, nil) end @shouldRefreshSync=false oldurl = OSX::NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("Frictionless.xml")) url = OSX::NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3")) options = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true) } if (fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.xml")) && !fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3"))) then print "migrating from XML store to SQL store\n" mappingModel=NSMappingModel.mappingModelFromBundles_forSourceModel_destinationModel(NSArray.arrayWithObject(NSBundle.mainBundle()),managedObjectModel,managedObjectModel) if (!mappingModel) then print "auto mapping didn't work, trying manual\n" nopurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.pathForResource_ofType("XMLupdate","cdm"),false) print NSBundle.mainBundle.resourcePath() oldmomurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.resourcePath()+"/RubyFrictionless_DataModel.momd/RubyFrictionless_DataModel.mom",false) newmomurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.resourcePath()+"/RubyFrictionless_DataModel.momd/RubyFrictionless_DataModel_scheduling_ext.mom",false) oldMom = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(oldmomurl) newMom = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(newmomurl) mappingModel=NSMappingModel.mappingModelFromBundles_forSourceModel_destinationModel([NSBundle.mainBundle()],oldMom,newMom); if (mappingModel) then #print "mapping model: #{mappingModel}\n" migrateManager= NSMigrationManager.alloc.initWithSourceModel_destinationModel(oldMom,newMom) #print "migrateManager: #{migrateManager}\n" migrateoptions = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true), :NSIgnorePersistentStoreVersioningOption =>NSNumber.numberWithBool(true) } result,error=migrateManager.migrateStoreFromURL_type_options_withMappingModel_toDestinationURL_destinationType_destinationOptions_error( oldurl,OSX::NSXMLStoreType,migrateoptions,mappingModel,url,OSX::NSSQLiteStoreType,migrateoptions) print "migration result, error: #{result},#{error}\n" end end if (!mappingModel) then print "auto mapping2 didn't work\n" nopurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.pathForResource_ofType("XMLupdate","cdm"),false) mappingModel=NSMappingModel.alloc.initWithContentsOfURL(nopurl) if (mappingModel) then #print "mapping model: #{mappingModel}\n" migrateManager= NSMigrationManager.alloc.initWithSourceModel_destinationModel(managedObjectModel,managedObjectModel) #print "migrateManager: #{migrateManager}\n" migrateoptions = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true), :NSIgnorePersistentStoreVersioningOption =>NSNumber.numberWithBool(true) } result,error=migrateManager.migrateStoreFromURL_type_options_withMappingModel_toDestinationURL_destinationType_destinationOptions_error( oldurl,OSX::NSXMLStoreType,migrateoptions,mappingModel,url,OSX::NSSQLiteStoreType,migrateoptions) print "migration result, error: #{result},#{error}\n" end end if (!mappingModel) then print "no mapping model!\n" exit end end unless fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3")) @shouldRefreshSync=true @storeMetaDeta=nil end coordinator = OSX::NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(managedObjectModel) # FIXME: cannot get errors options = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true) } persistentStore=coordinator.addPersistentStoreWithType_configuration_URL_options_error(OSX::NSSQLiteStoreType, nil, url, options, error) if persistentStore then @managedObjectContext = OSX::NSManagedObjectContext.alloc.init @managedObjectContext.setPersistentStoreCoordinator(coordinator) fastsyncurl= OSX::NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("FrictionlessFastSync.fastSync")) coordinator.setStoresFastSyncDetailsAtURL_forPersistentStore(fastsyncurl,persistentStore) else OSX::NSApplication.sharedApplication.presentError(error) end $managedObjectContext=@managedObjectContext; return @managedObjectContext end def syncClient clientIdentifier=NSBundle.mainBundle.bundleIdentifier client=OSX::ISyncManager.sharedManager.clientWithIdentifier(clientIdentifier) if (client == nil) if (ISyncManager.sharedManager.registerSchemaWithBundlePath( NSBundle.mainBundle.pathForResource_ofType("FrictionlessSyncSchema","syncschema"))) path=NSBundle.mainBundle.pathForResource_ofType("RubySelfClient","plist") client=ISyncManager.sharedManager.registerClientWithIdentifier_descriptionFilePath(clientIdentifier,path) client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypeApplication) client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypeDevice) client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypeServer) client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypePeer) end end client end def client_mightWantToSyncEntityNames(client,names) print "client syncing #{names}\n" saveAction(self) end def windowWillReturnUndoManager(window) return managedObjectContext.undoManager end def showContextWindow(sender) app=OSX::NSApplication.sharedApplication @contextWindowC=NSWindowController.alloc.initWithWindowNibName_owner("ContextsWindow",self) if not @contextWindow @contextWindowC.showWindow(self) @contextWindow.makeKeyAndOrderFront(nil) end def showTemplatesWindow(sender) app=OSX::NSApplication.sharedApplication @templatesWindowC=NSWindowController.alloc.initWithWindowNibName_owner("Templates",self) if not @templatesWindow @templatesWindowC.showWindow(self) #@templatesWindowC.makeKeyAndOrderFront(nil) end def newBrowserWindow(sender) @browserWindows ||=[] ab=ActionBrowser.alloc.init ab.setup(self) @browserWindows << ab ab end def newActionFocusWindow(action) return newFocusWindow([action]) end def newFocusWindow(focus) @browserWindows ||=[] ab=ActionBrowser.alloc.init ab.setup(self,focus) @browserWindows << ab ab end def AppDelegate.newFocusWindow(focus) @@appDelegate.newFocusWindow(focus) end def AppDelegate.newActionFocusWindow(focus) @@appDelegate.newActionFocusWindow(focus) end def newHelpWindow(sender) hw=HelpWindow.alloc.init hw.setup(self,nil) hw end def syncAction(sender) return unless @doSyncing #managedObjectContext.persistentStoreCoordinator.syncWithClient_inBackground_handler_error(syncClient,true,self,error) if syncClient print "starting sync\n" begin Action.startSync error=managedObjectContext.persistentStoreCoordinator.syncWithClient_inBackground_handler_error(syncClient,false,self) if syncClient Action.endSync rescue print $! end print "sync finished!\n" end def toodleSync(sender) saveAction(sender) @toodle=ToodleDoSyncer.alloc.init unless @toodle @toodle.syncAll(managedObjectContext) saveAction(sender) end def mailSync(sender) saveAction(sender) @mailSyncer=MailSyncer.alloc.init unless @mailSyncer @mailSyncer.syncMail(managedObjectContext) saveAction(sender) end def toodleClean(sender) saveAction(sender) @toodle=ToodleDoSyncer.alloc.init unless @toodle @toodle.clean(managedObjectContext) saveAction(sender) end def saveAction(sender) error = nil unless managedObjectContext.save?(error) OSX::NSApplication.sharedApplication.presentError(error) return end syncAction(sender) if @doSyncing end def saveInMainThread(sender) self.performSelector_withObject_afterDelay('saveAction:',sender,0.0) end def managedObjectContextsToMonitorWhenSyncingPersistentStoreCoordinator(psc) print "moctoMonitor\n" return [managedObjectContext] end def managedObjectContextsToReloadAfterSyncingPersistentStoreCoordinator(psc) print "moctoReload\n" return [managedObjectContext] end def applicationShouldTerminate(sender) reply = OSX::NSTerminateNow context = managedObjectContext if context if context.commitEditing? # FIXME: cannot get errors error = OSX::OCObject.new unless context.save?(error) error.userInfo.valueForKey("NSDetailedErrors").each { |e| print e.userInfo} errorResult = OSX::NSApplication.sharedApplication.presentError?(error) if errorResult reply = OSX::NSTerminateCancel else alertReturn = OSX.NSRunAlertPanel(nil, "Could not save changes while quitting. Quit anyway?", "Quit anyway", "Cancel", nil) if alertReturn == OSX::NSAlertAlternateReturn reply = OSX::NSTerminateCancel end end end else reply = OSX::NSTerminateCancel end end return reply end def statusesReceived_forRequest(statuses,identifier) print statuses end def directMessagesReceived_messages_forRequest(messages,identifier) print messages end def userInfoReceived_forRequest(userInfo,identifier) print userInfo end def requestSucceeded(requestIdentifier) print requestIdentifier end def requestFailed_withError(requestIdentifier,error) print "Twitter request failed! (#{requestIdentifier}) Error: #{error.localizedDescription} (#{error.userInfo})" end def persistentStoreCoordinatorShouldStartSyncing?(psc) print "persistentStoreCoordinatorShouldStartSyncing?\n" @syncChanges=false return true end # def persistentStoreCoordinatorShouldStartSyncing(psc) # print "persistentStoreCoordinatorShouldStartSyncing\n" # return true # end def persistentStoreCoordinator_willPushChangesInSyncSession(psc,session) print "persistentStoreCoordinator_willPushChangesInSyncSession\n" end def persistentStoreCoordinator_didPushChangesInSyncSession(psc,session) print "persistentStoreCoordinator_didPushChangesInSyncSession\n" @syncChanges=true end def persistentStoreCoordinator_willPullChangesInSyncSession(psc,session) print "persistentStoreCoordinator_willPullChangesInSyncSession\n" end def persistentStoreCoordinator_didPullChangesInSyncSession(psc,session) print "persistentStoreCoordinator_didPullChangesInSyncSession\n" end def persistentStoreCoordinator_didFinishSyncSession(psc,session) print "persistentStoreCoordinator_didFinishSyncSession\n" if @syncChanges then print "resetting action caches\n" actions=managedObjectContext.objectsForEntityNamed('Action') actions.each do |a| #fix any bad parents a.reset_caches end actions.each do |a| #fix any bad parents a.setParent(nil) if a.parent? and a.parent.hasAncestor?(a) end end end def persistentStoreCoordinator_didCancelSyncSession_error(psc,session,error) print "persistentStoreCoordinator_didCancelSyncSession_error\n" end def persistentStoreCoordinator_willPushRecord_forManagedObject_inSyncSession(psc,record,object,session) #print "pushing #{record} for object #{object}\n" @syncChanges=true record end def persistentStoreCoordinator_willDeleteRecordRecordWithIdentifier_inSyncSession(psc,record,object,session) print "persistentStoreCoordinator_willDeleteRecordRecordWithIdentifier_inSyncSession\n" @syncChanges=true return true end def persistentStoreCoordinator_willApplyChange_toManagedObject_inSyncSession(psc,change,object,session) print "changes #{change} for object #{object}\n" object.syncON if object @syncChanges=true change end def persistentStoreCoordinator_didApplyChange_toManagedObject_inSyncSession(psc,change,object,session) print "persistentStoreCoordinator_didApplyChange_toManagedObject_inSyncSession\n" object.syncOFF if object if (object && object.entity.name=="Action") object.setParent(nil) if object.parent? and object.hasAncestor?(object) end end def persistentStoreCoordinator_didCommitChanges_toManagedObject_inSyncSession(psc,change,object,session) print "persistentStoreCoordinator_didCommitChanges_toManagedObject_inSyncSession\n" @syncChanges=true end end