1from Foundation import *
2from ToDoCell import *
3from ToDoItem import *
4from SelectionNotifyMatrix import *
5
6ToDoItemChangedNotification = "ToDoItemChangedNotification"
7
8class  ToDoDocument (NSDocument):
9    calendar = objc.IBOutlet()
10    dayLabel = objc.IBOutlet()
11    itemList = objc.IBOutlet()
12    statusList = objc.IBOutlet()
13
14
15    __slots__ = ('_dataFromFile', '_activeDays', '_currentItems', '_selectedItem', '_selectedItemEdited')
16
17    def rowSelected_(self, notification):
18        row = notification.object().selectedRow()
19
20        if row == -1:
21            #print 'No rowSelected?'
22            return
23
24        self._selectedItem = self._currentItems.objectAtIndex_(row)
25
26        if not isinstance(self._selectedItem, ToDoItem):
27            self._selectedItem = None
28
29        NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(ToDoItemChangedNotification, self._selectedItem, None)
30
31    def init(self):
32        NSDocument.init(self)
33        self._activeDays = None
34        self._currentItems = None
35        self._selectedItem = None
36        self._selectedItemEdited = 0
37        self._dataFromFile = None
38
39        return self
40
41    def __del__(self): # dealloc in Objective-C code
42
43        NSNotificationCenter.defaultCenter().removeObserver_(self)
44
45    def selectedItem(self):
46        return self._selectedItem
47
48    def windowNibName(self):
49        return "ToDoDocument"
50
51    def windowControllerDidLoadNib_(self, aController):
52        # NSDocument.windowControllerDidLoadNib_(self, aController)
53
54        self.setHasUndoManager_(0)
55        self.itemList.setDelegate_(self)
56
57        index = self.statusList.cells().count()
58        while index:
59            index -= 1
60
61            aCell = ToDoCell.alloc().init()
62            aCell.setTarget_(self)
63            aCell.setAction_('itemStatusClicked:')
64            self.statusList.putCell_atRow_column_(aCell, index, 0)
65
66        if self._dataFromFile:
67            self.loadDocWithData_(self._dataFromFile)
68            self._dataFromFile = None
69        else:
70            self.loadDocWithData_(None)
71
72        NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, 'rowSelected:', RowSelectedNotification, self.itemList)
73        NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, 'rowSelected:', RowSelectedNotification, self.statusList)
74
75    def loadDocWithData_(self, data):
76        if data:
77            dct = NSUnarchiver.unarchiveObjectWithData_(data)
78            self.initDataModelWithDictinary_(dct)
79            dayEnum = self._activeDays.keyEnumerator()
80            now = NSDate.date()
81
82            itemDate = dayEnum.nextObject()
83            while itemDate:
84                itemArray = self._activeDays.objectForKey_(itemDate)
85                itemEnum = itemArray.objectEnumerator()
86
87                anItem = itemEnum.nextObject()
88                while anItem:
89                    if (isinstance(anItem, ToDoItem)
90                            and anItem.secsUntilNotify()
91                            and anItem.status() == INCOMPLETE):
92                        due = anItem.day().addTimeInterfval_(anItem.secondsUntilDue())
93                        elapsed = due.timeIntervalSinceDate_(now)
94                        if elapsed > 0:
95                            self.setTimerForItem_(anItem)
96                        else:
97                            #print "Past due"
98                            NSBeep()
99                            NSRunAlertPanel("To Do", "%s on %s is past due!"%(
100                                    anItem.itemName(),
101                                    due.descriptionWithCalendarFormat_timeZone_locale_(
102                                        "%b %d, %Y at %I:%M %p",
103                                        NSTimeZone.localTimeZone(),
104                                        None
105                                    )
106                                ), None, None, None)
107                            anItem.setSecsUntilNotify_(0)
108                    anItem = itemEnum.nextObject()
109
110                itemDate = dayEnum.nextObject()
111        else:
112            self.initDataModelWithDictionary_(None)
113
114        self.selectItemAtRow_(0)
115        self.updateLists()
116
117        self.dayLabel.setStringValue_(
118            self.calendar.selectedDay().descriptionWithCalendarFormat_timeZone_locale_(
119                "To Do on %a %B %d %Y",
120                NSTimeZone.defaultTimeZone(),
121                None))
122
123    def initDataModelWithDictionary_(self, aDict):
124        if aDict:
125            self._activeDays = aDict
126        else:
127            self._activeDays = NSMutableDictionary.alloc().init()
128
129        date = self.calendar.selectedDay()
130        self.setCurrentItems_(self._activeDays.objectForKey_(date))
131
132    def setCurrentItems_(self, newItems):
133        if newItems:
134            self._currentItems = newItems.mutableCopy()
135        else:
136            numRows, numCols = self.itemList.getNumberOfRows_columns_(None, None)
137            self._currentItems = NSMutableArray.alloc().initWithCapacity_(numRows)
138
139            for d in range(numRows):
140                self._currentItems.addObject_("")
141
142    def updateLists(self):
143        numRows = self.itemList.cells().count()
144
145        for i in range(numRows):
146            if self._currentItems:
147                thisItem = self._currentItems.objectAtIndex_(i)
148            else:
149                thisItem = None
150            #print ">>> object %d is %s %s"%(i, thisItem, isinstance(thisItem, ToDoItem))
151
152            if isinstance(thisItem, ToDoItem):
153                if thisItem.secsUntilDue():
154                    due = thisItem.day().addTimeInterval_(thisItem.secsUntilDue())
155                else:
156                    due = None
157
158                self.itemList.cellAtRow_column_(i, 0).setStringValue_(thisItem.itemName())
159                self.statusList.cellAtRow_column_(i, 0).setTimeDue_(due)
160                self.statusList.cellAtRow_column_(i, 0).setTriState_(thisItem.status())
161            else:
162                self.itemList.cellAtRow_column_(i, 0).setStringValue_("")
163                self.statusList.cellAtRow_column_(i, 0).setTitle_("")
164                self.statusList.cellAtRow_column_(i, 0).setImage_(None)
165
166    def saveDocItems(self):
167        if self._currentItems:
168            cnt = self._currentItems.count()
169
170            for i in range(cnt):
171                anItem = self._currentItems.objectAtIndex_(i)
172                if isinstance(anItem, ToDoItem):
173                    self._activeDays.setObject_forKey_(self._currentItems, anItem.day())
174                    break
175
176    def controlTextDidEndEditing_(self, notif):
177        if not self._selectedItemEdited:
178            return
179
180        row = self.itemList.selectedRow()
181        newName = self.itemList.selectedCell().stringValue()
182
183        if isinstance(self._currentItems.objectAtIndex_(row), ToDoItem):
184            prevNameAtIndex = self._currentItems.objectAtIndex_(row).itemName()
185            if newName == "":
186                self._currentItems.replaceObjectAtRow_withObject_(row, "")
187            elif prevNameAtIndex != newName:
188                self._currentItems.objectAtRow_(row).setItemName_(newName)
189        elif newName != "":
190            newItem = ToDoItem.alloc().initWithName_andDate_(newName, self.calendar.selectedDay())
191            self._currentItems.replaceObjectAtIndex_withObject_(row, newItem)
192
193        self._selectedItem = self._currentItems.objectAtIndex_(row)
194
195        if not isinstance(self._selectedItem, ToDoItem):
196            self._selectedItem = None
197
198        self.updateLists()
199        self._selectedItemEdited = 0
200        self.updateChangeCount_(NSChangeDone)
201
202        NSNotificationCenter.defaultCenter(
203            ).postNotificationName_object_userInfo_(
204            ToDoItemChangedNotification, self._selectedItem, None)
205
206    def selectedItemModified(self):
207        if self._selectedItem:
208            self.setTimerForItem_(self._selectedItem)
209
210        self.updateLists()
211        self.updateChangeCount_(NSChangeDone)
212
213    def calendarMatrix_didChangeToDate_(self, matrix, date):
214        self.saveDocItems()
215
216        if self._activeDays:
217            self.setCurrentItems_(self._activeDays.objectForKey_(date))
218        else:
219            #print "calenderMatrix:didChangeToDate: -> no _activeDays"
220            pass
221
222        self.dayLabel.setStringValue_(
223            date.descriptionWithCalendarFormat_timeZone_locale_(
224            "To Do on %a %B %d %Y", NSTimeZone.defaultTimeZone(),
225            None))
226        self.updateLists()
227        self.selectedItemAtRow_(0)
228
229    def selectedItemAtRow_(self, row):
230        self.itemList.selectCellAtRow_column_(row, 0)
231
232    def controlTextDidBeginEditing_(self, notif):
233        self._selectedItemEdited = 1
234
235    def dataRepresentationOfType_(self, aType):
236        self.saveDocItems()
237
238        return NSArchiver.archivedDataWithRootObject_(self._activeDays)
239
240    def loadRepresentation_ofType_(self, data, aType):
241        if selfcalendar:
242            self.loadDocWithData_(data)
243        else:
244            self._dataFromFile = data
245
246        return 1
247
248    @objc.IBAction
249    def itemStatusClicked_(self, sender):
250        row  = sender.selectedRow()
251        cell = sender.cellAtRow_column_(row, 0)
252        item = self._currentItems.objectAtIndex_(row)
253
254        if isinstance(item, ToDoItem):
255            # print "changing status to", cell.triState()
256            item.setStatus_(cell.triState())
257            self.setTimerForItem_(item)
258
259            self.updateLists()
260            self.updateChangeCount_(NSChangeDone)
261
262            NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
263                ToDoItemChangedNotification, item, None)
264
265    def setTimerForItem_(self, anItem):
266        if anItem.secsUntilNotify() and anItem.status() == INCOMPLETE:
267            notifyDate = anItem.day().addTimeInterval_(anItem.secsUntilDue() - anItem.secsUntilNotify())
268
269            aTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
270                    notifyDate.timeIntervalSinceNow(),
271                    self,
272                    'itemTimerFired:',
273                    anItem,
274                    False)
275            anItem.setTimer_(aTimer)
276        else:
277            anItem.setTimer_(None)
278
279    def itemTimerFired_(self, timer):
280        #print "Timer fired for ", timer
281        anItem = timer.userInfo()
282        dueDate = anItem.day().addTimeInterval_(anItem.secsUntilDue())
283
284        NSBeep()
285
286        NSRunAlertPanel("To Do", "%s on %s"%(
287            anItem.itemName(), dueDate.descriptionWithCalendarFormat_timeZone_locale_(
288            "%b %d, %Y at %I:%M: %p", NSTimeZone.defaultTimeZone(), None),
289                 ), None, None, None)
290        anItem.setSecsUntilNotify_(0)
291        self.setTimerForItem_(anItem)
292        self.updateLists()
293
294        NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
295            ToDoItemChangedNotification,
296            anItem,
297            None)
298
299    def selectItemAtRow_(self, row):
300        self.itemList.selectCellAtRow_column_(row, 0)
301
302if __name__ == "__main__":
303    x = ToDoDocument.alloc()
304    print x
305    print x.init()
306