require "Machine_Action.rb" OSX.require_framework 'AddressBook' class Action < Machine_Action_rb attr_accessor :doneCalc, :noteReader #@@dueColor=NSColor.redColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@dueColor=NSColor.redColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@beforeStartColor=NSColor.lightGrayColor @@nextColor=NSColor.purpleColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@doneColor=NSColor.greenColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@todayColor=NSColor.alternateSelectedControlColor @@uiColor=NSColor.orangeColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@iColor=NSColor.blueColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@uColor=NSColor.yellowColor.blendedColorWithFraction(0.5, ofColor:NSColor.blackColor) @@txtIcon=NSImage.imageNamed("txt") @@notxtIcon=NSImage.imageNamed("notxt") Action.setKeys(["dueDate","doDate","done","startDate","parent","sequenceValue","isImportant","isUrgent"],triggerChangeNotificationsForDependentKey:"textColor") Action.setKeys(["dueDate"],triggerChangeNotificationsForDependentKey:"dueDateDate") Action.setKeys(["dueDate"],triggerChangeNotificationsForDependentKey:"dueDateTime") Action.setKeys(["dueDate"],triggerChangeNotificationsForDependentKey:"dueDateDateOrNil") Action.setKeys(["dueDate"],triggerChangeNotificationsForDependentKey:"dueDateTimeOrNil") Action.setKeys(["doDate"],triggerChangeNotificationsForDependentKey:"doDateDate") Action.setKeys(["doDate"],triggerChangeNotificationsForDependentKey:"doDateTime") Action.setKeys(["doDate"],triggerChangeNotificationsForDependentKey:"doDateDateOrNil") Action.setKeys(["doDate"],triggerChangeNotificationsForDependentKey:"doDateTimeOrNil") Action.setKeys(["startDate"],triggerChangeNotificationsForDependentKey:"startDateDate") Action.setKeys(["startDate"],triggerChangeNotificationsForDependentKey:"startDateTime") Action.setKeys(["startDate"],triggerChangeNotificationsForDependentKey:"startDateDateOrNil") Action.setKeys(["startDate"],triggerChangeNotificationsForDependentKey:"startDateTimeOrNil") Action.setKeys(["parent","sequenceValue"],triggerChangeNotificationsForDependentKey:"sequencePath") ["do","due","start"].each do |d| ["#{d}Hour","#{d}Minute","#{d}DateDate"].each do |k| Action.setKeys(["doDate"],triggerChangeNotificationsForDependentKey:k) end end def self.==(obj) self.isEqual(obj) end def reset_caches resetProjectCalc reset_kids reset_actionable reset_actionableKids reset_childDate reset_dateQuestions reset_doneCalc reset_actionable reset_textColor resetTemplateCache end def parentChangeKey(key) reset_caches kids.each{|c| c.parentChangeKey(key)} if children? end def childChangeKey(key) reset_caches parent.childChangeKey(key) if parent? && !syncing? end def siblingChangeKey(key) case key when "done" if isSequential? reset_actionable parent.reset_actionableKids if parent? end reset_textColor end end def didChangeValueForKey(key) if (!syncing?) then reset_caches if parent? parent.childChangeKey(key) parent.kids.each{|c| c.siblingChangeKey(key)} if parent.children? end kids.each{|c| c.parentChangeKey(key)} if children? super_didChangeValueForKey(key) end end def openJustNotes @noteReader ||= NoteReader.alloc.initWithAction(self) @noteReader.show setNoteOpen(true) end def openNoteReader if inMailMessageID? then link = "message://%3c#{inMailMessageID}%3e" print "opening mail link: #{link}\n" link = NSURL.URLWithString(link) NSWorkspace.sharedWorkspace.openURL?(link) if link end @noteReader ||= NoteReader.alloc.initWithAction(self) @noteReader.show setNoteOpen(true) end def closeNoteReader setNoteOpen(false) @noteReader = nil end # find accesor for key-value coding # "key" must be a ruby string # def kvc_getter_method(key) # [key, key + '?'].each do |m| # return m if respond_to? m # end # return nil # accessor not found # end # # def kvc_setter_method(key) # [kvc_internal_setter(key), key + '='].each do |m| # return m if respond_to? m # end # return nil # end # # def kvc_accessor_notfound(key) # fmt = '%s: this class is not key value coding-compliant for the key "%s"' # raise sprintf(fmt, self.class, key.to_s) # end # # def rbSetValue_forKey(value, key) # if m = kvc_setter_method(key.to_s) # send(m, value) # else # kvc_accessor_notfound(key) # end # end # def rbValue_forKey(value, key) # print "rbValueForKey\n" # if m = kvc_getter_method(key.to_s) # send(m, value) # else # kvc_accessor_notfound(key) # end # end # # def valueForKey(key) # print "vfk ",key,"\n" # if m = kvc_getter_method(key.to_s) # return send(m) # else # #super_valueForKey(key) # reurn objc_send(key.to_s) # end # end # # def setValue_forKey(value,key) # if m = kvc_setter_method(key.to_s) # send(m,value) # else # super # end # end # # # def valueForUndefinedKey(key) # if m = kvc_getter_method(key.to_s) # return send(m) # else # super # end # end # # def setValue_forUndefinedKey(value,key) # if m_kvc_setter_method(key.to_s) # send(m,value) # else # super # end # end # def awakeFromInsert #super setCreateDate(NSCalendarDate.calendarDate) end # def context # result=super_context # return parent.context if result==nil && parent? # return result # end def appleScriptName "actions" end def parent? #cache to avoid crossing bridge return @parentQ if @parentQ != nil @parentQ= (self.parent != nil) @parentQ end def children? return @childrenQ if @childrenQ != nil kids= self.children @childrenQ=(kids != nil && kids.count > 0) end def sequencePath if parent? then result="%s.%03d" % [parent.sequencePath,sequenceValue] else result="%03d" % sequenceValue end end def namePath(sep) if parent? then result = "%s%s%s" % [parent.namePath(sep),sep,name] else result = name end result end def sequenceValue result=super_sequenceValue result = 999 if (result==nil || result.to_i==0) result end def reset_kids @kids=nil @actionableKids=nil end def kids return @kids if @kids return nil if !children? @kids=children.allObjects.sort_by {|x| x.sequenceValue.to_i} @kids end def checkSequential? return true if !parent? pkids= parent.kids pkids.each do |a| return false if a != self && !a.done? # sequential items can only start after previous items complete return true if a==self # if we're the first undone item, we can go! end end def reset_actionableKids @actionableKids=nil end def actionableKids return @actionableKids if @actionableKids return nil if !children? @actionableKids= kids.select {|x| x.isActionable?} @actionableKids end def isNext? return false if isArea? || !isActionable? return false if !parent? return parent.actionableKids[0]==self if parent.actionableKids return false end def bumpChildrenFromIndex_throughIndex(fromIndex,toIndex) #print "bumping #{fromIndex} to #{toIndex}\n" bump=toIndex-fromIndex+1 list= kids list.each{|a| a.sequenceValue= a.sequenceValue.to_i+bump if a.sequenceValue.to_i >=fromIndex } if list end def resequence #print "resequencing\n" list= kids list.each_with_index {|a,index| a.sequenceValue=index+1} if list end def resequenceAll self.resequence children.each { |x| resequenceAll} if (children?) end def isSomedayUp? return isSomeday? || (parent? && parent.isSomedayUp?) end def isTemplateUp return isTemplateUp? end def isTemplateUp? return @isTemplateUpCache if @isTemplateUpCache !=nil @isTemplateUpCache=isTemplate? || (parent? && parent.isTemplateUp?) return @isTemplateUpCache end def resetTemplateCache @isTemplateUpCache=nil end def templateParent return parent.templateParent if (parent? && parent.isTemplate?) return self end def isTemplateRoot return isTemplateRoot? end def isTemplateRoot? return isTemplate? && !(parent.isTemplateUp?) end def isSomedayUp=(value) if (!value || value.to_i==0) then if (parent? && parent.isSomedayUp?) parent.isSomedayUp=value #recurse up to disable if parent is set else setIsSomeday(value) end else setIsSomeday(value) end end def setIsSomedayUp=(value) isSomedayUp=(value) end def isTemplateUp=(value) if (!value || value.to_i==0) then if (parent? && parent.isTemplateUp) parent.isTemplateUp=value #recurse up to disable if parent is set else setIsTemplate(value) end else setIsTemplate(value) end end def setIsTemplateUp=(value) isTemplateUp=(value) end def isArea? isArea.boolValue end def isProject? (!isArea?) && children? end def isProject isProject? end def isTask? ! (isArea? || children?) end def reset_actionable @actionable=nil end def isActionable? return @actionable if @actionable !=nil @actionable=actionableCalc end def actionableCalc ! (done? || isArea? || children? || isSomedayUp? || isTemplateUp? || beforeStart? || (isSequential? && !checkSequential? )) end def isTask isTask? end def rootAction return parent.rootAction if parent? self end def resetProjectCalc @projectCalc=nil @parentQ=nil @childrenQ=nil end def setIsArea(value) super_setIsArea(value) end def project return @projectCalc if @projectCalc if parent? && !(parent.isArea?) @projectCalc=parent.project else @projectCalc=self end end def project=(value) setParent(value) end def setProject(value) setParent(value) end def setParentAtIndex(value,index) return if value.hasAncestor?(self) resetProjectCalc value.bumpChildrenFromIndex_throughIndex(index,index+1) if value value.reset_kids if value parentChange(parent,value) super_setParent(value) setSequenceValue(index) end def setParent(value) return if value && value.hasAncestor?(self) oldparent = parent resetProjectCalc sequenceValue= 999 # throw to the bottom of the list parentChange(parent,value) super_setParent(value) parent.resequence if parent? oldparent.resequence if oldparent != nil end def area return if parent? && !isArea? self end def depth depth=1 p=parent while(p) do depth++ p=parent end depth end def done? done.to_i==1 end def finishOrDoDate result=nil if done? then result=finishDate elsif doDate then result=doDate elsif startDate then result=startDate else result=dueDate end result end def isToday? today=NSDate.date.dayNumber return ( (doDate && doDate.dayNumber==today) || (startDate && startDate.dayNumber==today) || (finishDate && finishDate.dayNumber==today)|| (!done? && ((doDate && doDate.dayNumber<=today) || (dueDate && dueDate.dayNumber<=today))) ) end def isDue? return @isDue if @isDue != nil @isDue= (!done? && dueDate && dueDate.dayNumber <= NSDate.date.dayNumber) @isDue end def reset_dateQuestions @beforeStart=nil @isDue=nil end def beforeStart? return @beforeStart if @beforeStart != nil @beforeStart = (startDate && !done? && startDate.dayNumber > NSDate.date.dayNumber) @beforeStart end def hasAncestor?(ancestor) if ancestor==self then return true else return parent !=nil && parent.hasAncestor?(ancestor) end end def hasChild?(child) child.hasAncestor?(self) end def primitiveDone willAccessValueForKey "done" result = primitiveValueForKey "done" didAccessValueForKey "done" result end def reset_nextCalc reset_doneCalc reset_dateQuestions if actionableKids && actionableKids.size > 0 nextAction=actionableKids[0] nextAction.willChangeValueForKey "textColor" nextAction.didChangeValueForKey "textColor" end end def reset_doneCalc @doneCalc=nil reset_actionableKids parent.reset_doneCalc if parent? && parent != nil # recurion might not have reset everything end def doneCalc return @doneCalc if @doneCalc!= nil mx=self.valueForKeyPath("children.@min.done") mn=self.valueForKeyPath("children.@max.done") @doneCalc=mx @doneCalc= -1 if (mn!=mx) @doneCalc end def done result=super_done result=doneCalc if children? #recurring items "reset" when asked if they're done if result.to_i > 0 && isRecurring? && recurReset? && doDate != nil && doDate.dayNumber.to_i <= NSDate.date.dayNumber.to_i setDone(0) setFinishDate(nil) result=0 end result end def instantiateTemplate newAction=cloneOfSelf newAction.setCreateDate(NSCalendarDate.calendarDate) newAction.setIsTemplate(false) newAction.setDone(0) if children? kids.each do |a| newA=a.instantiateTemplate newA.setParent(newAction) end end newAction end def editTemplate AppDelegate.newFocusWindow(self) end def markedDoneTransition setFinishDate(NSCalendarDate.calendarDate) NSNotificationCenter.defaultCenter.postNotificationName_object("markedDoneTransition",self); end def markedUndoneTransition setFinishDate(nil) NSNotificationCenter.defaultCenter.postNotificationName_object("markedUndoneTransition",self); end def setDone(value) value=1 if (-1 == value.to_i) # mixed state checkboxes do 0->-1->1, we only want 0->1 oldvalue=primitiveDone wasNext= (oldvalue.to_i==0 && value.to_i > 0 && isNext?) super_setDone(value) if (oldvalue.to_i != value.to_i && !syncing?) then if (1 == value.to_i) markedDoneTransition if isRecurring? offset=1 deadline=0 offset=recurDays.to_i if recurDays? deadline=recurDeadline.to_i if recurDeadline? if !recurReset clone=cloneOfSelf clone.setDone(0) clone.setFinishDate(0) clone.setStartDate(NSDate.date.dateByAddingDays(offset)) clone.setDoDate(NSDate.date.dateByAddingDays(offset)) clone.setDueDate(NSDate.date.dateByAddingDays(offset+deadline)) if dueDate? setIsRecurring(0) else setStartDate(NSDate.date.dateByAddingDays(offset)) setDoDate(NSDate.date.dateByAddingDays(offset)) setDueDate(NSDate.date.dateByAddingDays(offset+deadline)) if dueDate? end end else markedUndoneTransition end if parent? if (wasNext) parent.reset_nextCalc else parent.reset_doneCalc end parent.willChangeValueForKey "done" parent.didChangeValueForKey "done" end end end def parentChange(oldParent,newParent) if oldParent oldParent.reset_childDate oldParent.resetProjectCalc end if newParent newParent.reset_childDate newParent.resetProjectCalc end @isTemplateUpCache=nil end def dueDate date=super_dueDate if children? then @childDueDate=self.valueForKeyPath("children.@max.dueDate") date=@childDueDate if @childDueDate && (!date || !(@childDueDate.compare(date) < 0)) end date end def reset_childDate @childStartDate=nil @childDueDate=nil reset_dateQuestions end def startDate date=super_startDate if children? @childStartDate=self.valueForKeyPath("children.@min.startDate") if not @childStartDate date=@childStartDate if @childStartDate && (!date || !(@childStartDate.compare(date) < 0)) end date end def priority p = 4 p -= 1 if isUrgent? p -= 2 if isImportant? p end def deepEach yield(self) #yield once for me self.children.allObjects.to_a.each{|c| c.deepEach { |x| yield(x)} } if children? end def eachTask yield(self) if isTask? if children? self.children.allObjects.to_a.each do |c| c.eachTask{ |x| yield(x) } end end end def hasChildren children? end def setSmartName(value) smartName= value end def smartName=(value) value=parseSmartString(value) super_setName(value) end def smartName(value) name end def setName(value) value=parseSmartString(value) super_setName(value) end def findObjectByName(entity,value) moc=self.managedObjectContext ed=NSEntityDescription.entityForName_inManagedObjectContext(entity,moc) request=NSFetchRequest.alloc.init request.setEntity(ed) pred=NSPredicate.predicateWithFormat_argumentArray("name BEGINSWITH[c] %@",[value]) request.setPredicate(pred) sort=NSSortDescriptor.alloc.initWithKey_ascending("name",true) request.setSortDescriptors([sort]) error=0 result=moc.executeFetchRequest_error(request,nil) return result[0] if result && result.count > 0 return nil end def findContext(value) value.strip! result=findObjectByName("Context",value) #print "Found Context #{result.name} for #{value}\n" if result #print "Couldn't find '#{value}'\n" unless result result end def findProject(value) value.strip! result=findObjectByName("Action",value) #print "Found Project #{result.name} for #{value}\n" if result #print "Couldn't find '#{value}'\n" unless result result end def parseDate(value) df=NSDateFormatter.alloc.initWithDateFormat_allowNaturalLanguage("%a %b %e %I%:%M%p",true) result=df.dateFromString(value) print "Turned #{value} into #{result}\n" print "Couldn't parse #{value}\n" unless result result end def parseSmartString(value) value=value.to_s #make sure its a ruby string markdone=0 value.sub!(/^[ \t]*did[ \t]+/) {|s| markdone=1; ''} value.sub!(/^[ \t]*Did[ \t]+/) {|s| markdone=1; ''} # process action name to produce new value # @ context will search for the matching context, or :context, or :c # > Project will search for the matching project, or :project, or :p # :due will try to set the due date, i.e. :due tues # :start will try to set the start date, i.e. :start mon # :on will try to set the do on date, i.e. :on mon or :do # :i will set "is important" # :u will set "is urgent" # :ui, :will set both # :someday # :template # :recur # :reset # :area contextr= %r{((@[ \t]*)|(\:context|:c))[ \t]*([^:@>\n\r]+)}i projectr= %r{((>[ \t]*)|(\:project|:p))[ \t]*([^:@>\n\r]+)}i duer=%r{(:due)[ \t]*(((\d{1,2}:\d\d)|[^:@>\n\r])+)}i startr=%r{(\:start)[ \t]*(((\d{1,2}:\d\d)|[^:@>\n\r])+)}i doner=%r{(\:done)[ \t]*}i onr=%r{(\:on)[ \t]*(((\d{1,2}:\d\d)|[^:@>\n\r])+)}i priorityr=%r{(\:iu|:ui|:i|:u)[ \t]*}i somedayr=%r{(\:someday)[ \t]*}i templater=%r{(\:templ*a*t*e*)[ \t]*}i recurr=%r{(\:recur)[ \t]*}i resetr=%r{(\:reset)[ \t]*}i arear=%r{(\:area)[ \t]*}i noter=%r{(:note|\n|\r)[ \t]*(.*)}i regex=Regexp.union(contextr,projectr,duer,startr,onr,priorityr,somedayr,templater,recurr,resetr,arear,doner,noter) value.gsub!(regex) do #print "match: ",$&,"\n" #print "list: 1#{$1} 2#{$2} 3#{$3} 4#{$4}" match=$~.to_a match.shift match=match.select{|x| x} managedObjectContext.processPendingChanges() # best to process changes as we manipulate relationships case match[0] when /@|:c/i ##print "Context: #{match[1]} #{match[2]}\n" setContext(findContext(match[2])) when />|:p/i ##print "Project: #{match[1]} #{match[2]}\n" setParent(findProject(match[2])) when /:due/i ##print "Due: #{match[1]}\n" ddate= parseDate(match[1]) setDueDate(ddate) when /:start/i ##print "Start: #{match[1]}\n" sdate= parseDate(match[1]) print "got a start date #{sdate}\n" setStartDate(sdate) print "now date #{startDate}\n" when /:done/i markdone=1 when /:on|:do/ ##print "Do: #{match[1]}\n" ddate= parseDate(match[1]) setDoDate(ddate) when /:iu|:ui/i setIsImportant(1) setIsUrgent(1) when /:i/i setIsImportant(1) when /:u/i setIsUrgent(1) when /:someday/i setIsSomeday(1) when /:temp/i setIsTemplate(1) when /:recur/i setIsRecurring(1) when /:reset/i setRecurReset(1) when /:area/i setIsArea(1) when /:note|\n|\r/i setNoteString(match[1]) end #print "Match #{match}\n\n" '' end managedObjectContext.processPendingChanges() # best to process changes as we manipulate relationships super_setName(value) setDone(1) if (markdone==1) value end def isUrgent result = super_isUrgent result = 0 unless result != nil result end def isImportant result=super_isImportant result=0 unless result != nil result end def smartNameTest string = " did stuff @work @ play > Project >project4 :project Project2 :p project3 :c work :due Feb 3 4:00pm :done :i :u :ui :someday :template :recur :reset :area" string.sub!(/^[ \t]*did[ \t]*/) {|s| print "found did\n"; ''} # @ context will search for the matching context, or :context, or :c # > Project will search for the matching project, or :project, or :p # :due will try to set the due date, i.e. :due tues # :start will try to set the start date, i.e. :start mon # :on will try to set the do on date, i.e. :on mon # :i will set "is important" # :u will set "is urgent" # :ui, :will set both # :someday # :temp[late] # :recur # :reset # :area contextr= %r{((@[ \t]*)|(\:context|:c))[ \t]*([^:@>]+)}i projectr= %r{((>[ \t]*)|(\:project|:p))[ \t]*([^:@>]+)}i duer=%r{(:due)[ \t]*(((\d{1,2}:\d\d)|[^:@>])+)}i startr=%r{(\:start)[ \t]*(((\d{1,2}:\d\d)|[^:@>])+)}i doner=%r{(\:done)[ \t]*}i onr=%r{(\:on|:do)[ \t]*(((\d{1,2}:\d\d)|[^:@>])+)}i priorityr=%r{(\:iu|:ui|:i|:u)[ \t]*}i somedayr=%r{(\:someday)[ \t]*}i templater=%r{(\:templ*a*t*e*)[ \t]*}i recurr=%r{(\:recur)[ \t]*}i resetr=%r{(\:reset)[ \t]*}i arear=%r{(\:area)[ \t]*}i regex=Regexp.union(contextr,projectr,duer,startr,onr,priorityr,somedayr,templater,recurr,resetr,arear,doner) string.gsub!(regex) do #print "match: ",$&,"\n" #print "list: 1#{$1} 2#{$2} 3#{$3} 4#{$4}" match=$~.to_a match.shift match=match.select{|x| x} case match[0] when /@|:c/ print "Context: #{match[1]} #{match[2]}\n" when /\>/ print "Project: #{match[1]} #{match[2]}\n" when /:p/i print "Project: #{match[1]} #{match[2]}\n" when /:due/ print "Due: #{match[1]}\n" when /:start/ print "Start: #{match[1]}\n" when /:on|do/ print "Do On: #{match[1]}\n" when /:done/ print "Done\n" when /:iu|:ui/ print "UI\n" when /:i/ print "important\n" when /:u/ print "Urgent\n" when /:someday/ print "someday\n" when /:temp/ print "template\n" when /:recur/ print "recur\n" when /:reset/ print "reset\n" when /:area/ print "area\n" else print "No Match? " match.each_with_index {|i,m| print "#{i}:#{m} "} print "\n" end print "Match #{match}\n\n" '' end print string end def addChildrenObject(value) super_addChildrenObject(value) reset_kids end def removeChildrenObject(value) super_removeChildrenObject(value) reset_kids end def reset_textColor @textColor=nil end def textColorCalc return @@dueColor if isDue? return @@beforeStartColor if beforeStart? return @@nextColor if isNext? return @@doneColor if done? return @@uiColor if isUrgent? && isImportant? return @@iColor if isImportant? return @@uColor if isUrgent? return @@todayColor if isToday? return @@beforeStartColor if isTemplateUp? return NSColor.blackColor end def textColor return @textColor if @textColor @textColor=textColorCalc @textColor end #objc_method :textColor,%w{id} def actionState return "stateDue" if isDue? return "stateBeforeStart" if beforeStart? return "stateNext" if isNext? return "stateDone" if done? return "stateUI" if isUrgent? && isImportant? return "stateI" if isImportant? return "stateU" if isUrgent? return "stateToday" if isToday? return "stateTemplate" if isTemplateUp? return "stateNone" end def selfPlusChildren(array) array << self kids.each {|a| a.selfPlusChildren(array)} if children? array end def Action.loadDefaults defaults=NSUserDefaultsController.sharedUserDefaultsController.values @@dueColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("dueColor")) if defaults.valueForKey("dueColor") @@beforeStartColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("beforeStartColor")) if defaults.valueForKey("beforeStartColor") @@nextColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("nextColor")) if defaults.valueForKey("nextColor") @@doneColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("doneColor")) if defaults.valueForKey("doneColor") @@uiColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("UIColor")) if defaults.valueForKey("UIColor") @@iColor=NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("IColor")) if defaults.valueForKey("IColor") @@uColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("UColor")) if defaults.valueForKey("UColor") @@todayColor= NSUnarchiver.unarchiveObjectWithData(defaults.valueForKey("todayColor")) if defaults.valueForKey("todayColor") end def appLaunch setIsUrgent(0) if self.isUrgent==nil setIsImportant(0) if self.isImportant==nil end def doDelegateTo(abperson,bestEmail,extraText) now = NSCalendarDate.date #print "best email #{bestEmail}\n" defaults=NSUserDefaults.standardUserDefaults delay = defaults.integerForKey("delegateFollowUpDelay") email = defaults.boolForKey("delegateShouldGenerateEmail") delay = 7 if not delay future = now.dateByAddingDays(delay) self.delegatedToID=abperson.uniqueId if (self.dueDate?) then if (self.dueDate.dayNumber > future) self.startDate=future else self.startDate=self.dueDate end else self.startDate = future end n = self.name n = n.sub("Fup: ","") n = "Fup: " << n name=n kids.each {|a| a.doDelegateTo(abperson) } if kids #print "Properties #{ABPerson.properties}\n" #print "Person: #{abperson}\n" #print "Person email: #{abperson.valueForProperty('Email')}\n" if (!bestEmail) then emailAddresses=abperson.valueForProperty("Email") bestEmail = emailAddresses.valueAtIndex(emailAddresses.indexForIdentifier(emailAddresses.primaryIdentifier)) end if email && !done? MailSyncer.singleton.sendDelegateMail(bestEmail,self,extraText,delay) self.delegatedToEmail=bestEmail end end def setDelegatedToID(value) @delegatedTo=nil super_setDelegatedToID(value) end def setDelegatedFromID(value) @delegatedFrom=nil super_setDelegatedFromID(value) end def delegatedTo return nil unless delegatedToID? @delgatedTo=ABAddressBook.sharedAddressBook.recordForUniqueId(delegatedToID) unless @delegatedTo @delegateTo end def delegatedFrom return nil unless delegatedFromID? @delgatedFrom=ABAddressBook.sharedAddressBook.recordForUniqueId(delgatedFromID) unless @delgatedFrom @delegatedFrom end def rootProject project end def rootArea p = project.parent return p if p && p.isArea? return nil end def objectEnumerator return nil end end