Todo¶
A PyObjC Example without documentation
Sources¶
CalendarMatrix.py¶
import Cocoa
import objc
gNumDaysInMonth = (0, 31, 28, 31, 30, 21, 30, 31, 31, 30, 31, 30, 31)
def isLeap(year):
return ((year % 4) == 0 and ((year % 100) != 0)) or (year % 400) == 0
class CalendarMatrix(Cocoa.NSMatrix):
lastMonthButton = objc.IBOutlet()
monthName = objc.IBOutlet()
nextMonthButton = objc.IBOutlet()
__slots__ = ("_selectedDay", "_startOffset")
def initWithFrame_(self, frameRect):
self._selectedDay = None
self._startOffset = 0
cell = Cocoa.NSButtonCell.alloc().initTextCell_("")
now = Cocoa.NSCalendarDate.date()
cell.setShowsStateBy_(Cocoa.NSOnOffButton)
self.initWithFrame_mode_prototype_numberOfRows_numberOfColumns_(
frameRect, Cocoa.NSRadioModeMatrix, cell, 5, 7
)
count = 0
for i in range(6):
for j in range(7):
val = self.cellAtRow_column_(i, j)
if val:
val.setTag_(count)
count += 1
self._selectedDay = Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_( # noqa: B950
now.yearOfCommonEra(),
now.monthOfYear(),
now.dayOfMonth(),
0,
0,
0,
Cocoa.NSTimeZone.localTimeZone(),
)
return self
@objc.IBAction
def choseDay_(self, sender):
prevSelDate = self.selectedDay()
selDay = self.selectedCell().tag() - self._startOffset + 1
selDate = (
Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
prevSelDate.yearOfCommonEra(),
prevSelDate.monthOfYear(),
selDay,
0,
0,
0,
Cocoa.NSTimeZone.localTimeZone(),
)
)
self.setSelectedDay_(selDate)
self.highlightTodayIfVisible()
if self.delegate().respondsToSelector_("calendarMatrix:didChangeToDate:"):
self.delegate().calendarMatrix_didChangeToDate_(self, selDate)
@objc.IBAction
def monthChanged_(self, sender):
thisDate = self.selectedDay()
currentYear = thisDate.yearOfCommonEra()
currentMonth = thisDate.monthOfYear()
if sender is self.nextMonthButton:
if currentMonth == 12:
currentMonth = 1
currentYear += 1
else:
currentMonth += 1
else:
if currentMonth == 1:
currentMonth = 12
currentYear -= 1
else:
currentMonth -= 1
self.setSelectedDay_(
Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
currentYear, currentMonth, 1, 0, 0, 0, Cocoa.NSTimeZone.localTimeZone()
)
)
self.refreshCalendar()
self.choseDay_(self)
def setSelectedDay_(self, newDay):
self._selectedDay = newDay
def selectedDay(self):
return self._selectedDay
def refreshCalendar(self):
selDate = self.selectedDay()
currentMonth = selDate.monthOfYear()
currentYear = selDate.yearOfCommonEra()
firstOfMonth = (
Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
currentYear, currentMonth, 1, 0, 0, 0, Cocoa.NSTimeZone.localTimeZone()
)
)
self.monthName.setStringValue_(
firstOfMonth.descriptionWithCalendarFormat_("%B %Y")
)
daysInMonth = gNumDaysInMonth[currentMonth]
if (currentMonth == 2) and isLeap(currentYear):
daysInMonth += 1
self._startOffset = firstOfMonth.dayOfWeek()
dayLabel = 1
for i in range(42):
cell = self.cellWithTag_(i)
if cell is None:
continue
if i < self._startOffset or i >= (daysInMonth + self._startOffset):
# blank out unused cells in the matrix
cell.setBordered_(False)
cell.setEnabled_(False)
cell.setTitle_("")
cell.setCellAttribute_to_(Cocoa.NSCellHighlighted, False)
else:
# Fill in valid days in the matrix
cell.setBordered_(True)
cell.setEnabled_(True)
cell.setFont_(Cocoa.NSFont.systemFontOfSize_(12))
cell.setTitle_(str(dayLabel))
dayLabel += 1
cell.setCellAttribute_to_(Cocoa.NSCellHighlighted, False)
self.selectCellWithTag_(selDate.dayOfMonth() + self._startOffset - 1)
self.highlightTodayIfVisible()
def highlightTodayIfVisible(self):
now = Cocoa.NSCalendarDate.date()
selDate = self.selectedDay()
if (
selDate.yearOfCommonEra() == now.yearOfCommonEra()
and selDate.monthOfYear() == now.monthOfYear()
and selDate.dayOfMonth() == now.dayOfMonth()
):
aCell = self.cellWithTag_(now.dayOfMonth() + self._startOffset - 1)
aCell.setHighlightsBy_(Cocoa.NSMomentaryChangeButton)
aCell.setCellAttribute_to_(Cocoa.NSCellHighlighted, True)
def awakeFromNib(self):
self.setTarget_(self)
self.setAction_("choseDay:")
self.setAutosizesCells_(True)
self.refreshCalendar()
self.choseDay_(self)
InfoWindowController.py¶
import Cocoa
import objc
from ToDoDocument import ToDoDocument
from ToDoItem import (
COMPLETE,
SECS_IN_HOUR,
SECS_IN_DAY,
ToDoItem,
ConvertSecondsToTime,
ConvertTimeToSeconds,
ToDoItemChangedNotification,
)
NOTIFY_TAG = 0
RESCHEDULE_TAG = 1
NOTES_TAG = 2
NotifyLengthNone = 0
NotifyLengthQuarter = 1
NotifyLengthHour = 2
NotifyLengthDay = 3
NotifyLengthOther = 4
_sharedInfoWindowController = None
class InfoWindowController(Cocoa.NSWindowController):
dummyView = objc.IBOutlet()
infoDate = objc.IBOutlet()
infoItem = objc.IBOutlet()
infoNotes = objc.IBOutlet()
infoNotifyAMPM = objc.IBOutlet()
infoNotifyHour = objc.IBOutlet()
infoNotifyMinute = objc.IBOutlet()
infoNotifyOtherHours = objc.IBOutlet()
infoNotifySwitchMatrix = objc.IBOutlet()
infoPopUp = objc.IBOutlet()
infoSchedComplete = objc.IBOutlet()
infoSchedDate = objc.IBOutlet()
infoSchedMatrix = objc.IBOutlet()
infoWindowViews = objc.IBOutlet()
notesView = objc.IBOutlet()
notifyView = objc.IBOutlet()
reschedView = objc.IBOutlet()
__slots__ = ("_inspectingDocument",)
@objc.IBAction
def switchClicked_(self, sender):
dueSecs = 0
idx = 0
theItem = self._inspectingDocument.selectedItem()
if theItem is None:
return
if sender is self.infoNotifyAMPM:
if self.infoNotifyHour.intValue():
pmFlag = self.infoNotifyAMPM.selectedRow() == 1
dueSecs = ConvertTimeToSeconds(
self.infoNotifyHour.intValue(),
self.infoNotifyMinute.intValue(),
pmFlag,
)
theItem.setSecsUntilDue_(dueSecs)
elif sender is self.infoNotifySwitchMatrix:
idx = self.infoNotifySwitchMatrix.selectedRow()
if not theItem:
pass
elif idx == NotifyLengthNone:
theItem.setSecsUntilNotify_(0)
elif idx == NotifyLengthQuarter:
theItem.setSecsUntilNotify_(SECS_IN_HOUR / 4)
elif idx == NotifyLengthHour:
theItem.setSecsUntilNotify_(SECS_IN_HOUR)
elif idx == NotifyLengthDay:
theItem.setSecsUntilNotify_(SECS_IN_DAY)
elif idx == NotifyLengthOther:
theItem.setSecsUntilNotify_(
self.infoNotifyOtherHours.intValue() * SECS_IN_HOUR
)
else:
Cocoa.NSLog("Error in selectedRow")
elif sender is self.infoSchedComplete:
if theItem:
theItem.setStatus_(COMPLETE)
elif sender is self.infoSchedMatrix:
# left as an exercise in the objective-C code
pass
self.updateInfoWindow()
self._inspectingDocument.selectedItemModified()
def textDidChange_(self, notification):
if notification.object() is self.infoNotes:
self._inspectingDocument.selectedItem().setNotes_(self.infoNotes.string())
self._inspectingDocument.selectItemModified()
def textDidEndEditing_(self, notification):
if notification.object() is self.infoNotes:
self._inspectingDocument.selectedItem().setNotes_(self.infoNotes.string())
self._inspectingDocument.selectedItemModified()
def controlTextDidEndEditing_(self, notification):
dueSecs = 0
theItem = self._inspectingDocument.selectedItem()
if theItem is None:
return
if (notification.object() is self.infoNotifyHour) or (
notification.object() is self.infoNotifyMinute
):
dueSecs = ConvertTimeToSeconds(
self.infoNotifyHour.intValue(),
self.infoNotifyMinute.intValue(),
self.infoNotifyAMPM.cellAtRow_column_(1, 0).state(),
)
theItem.setSecsUntilNotify_(dueSecs)
elif notification.object() is self.infoNotifyOtherHours:
if self.infoNotifySwitchMatrix.selectedRow() == NotifyLengthOther:
theItem.setSecsUntilNotify_(
self.infoNotifyOtherHours.intValue() * SECS_IN_HOUR
)
else:
return
elif notification.object() is self.infoSchedDate:
# Left as an exercise
pass
self._inspectingDocument.selectedItemModified()
@classmethod
def sharedInfoWindowController(self):
global _sharedInfoWindowController
if not _sharedInfoWindowController:
_sharedInfoWindowController = InfoWindowController.alloc().init()
return _sharedInfoWindowController
def init(self):
self = self.initWithWindowNibName_("ToDoInfoWindow")
if self:
self.setWindowFrameAutosaveName_("Info")
return self
def dump_outlets(self):
print("dummyView", self.dummyView)
print("infoDate", self.infoDate)
print("infoItem", self.infoItem)
print("infoNotes", self.infoNotes)
print("infoNotifyAMPM", self.infoNotifyAMPM)
print("infoNotifyHour", self.infoNotifyHour)
print("infoNotifyMinute", self.infoNotifyMinute)
print("infoNotifyOtherHours", self.infoNotifyOtherHours)
print("infoNotifySwitchMatrix", self.infoNotifySwitchMatrix)
print("infoPopUp", self.infoPopUp)
print("infoSchedComplet", self.infoSchedComplete)
print("infoSchedDate", self.infoSchedDate)
print("infoSchedMatrix", self.infoSchedMatrix)
print("infoWindowViews", self.infoWindowViews)
print("notesView", self.notesView)
print("notifyView", self.notifyView)
print("reschedView", self.reschedView)
def windowDidLoad(self):
Cocoa.NSWindowController.windowDidLoad(self)
self.notifyView.retain()
self.notifyView.removeFromSuperview()
self.reschedView.retain()
self.reschedView.removeFromSuperview()
self.notesView.retain()
self.notesView.removeFromSuperview()
self.infoWindowViews = None
self.infoNotes.setDelegate_(self)
self.swapInfoWindowView_(self)
self.setMainWindow_(Cocoa.NSApp().mainWindow())
self.updateInfoWindow()
Cocoa.NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "mainWindowChanged:", Cocoa.NSWindowDidBecomeMainNotification, None
)
Cocoa.NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "mainWindowResigned:", Cocoa.NSWindowDidResignMainNotification, None
)
Cocoa.NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "selectedItemChanged:", ToDoItemChangedNotification, None
)
def __del__(self): # dealloc
Cocoa.NSNotificationCenter.defaultCenter().removeObserver_(self)
# Cannot to this
Cocoa.NSWindowController.dealloc(self)
def updateInfoWindow(self):
minute = 0
hour = 0
selected = self.infoPopUp.selectedItem().tag()
selectedItem = self._inspectingDocument.selectedItem()
if isinstance(selectedItem, ToDoItem):
self.infoItem.setStringValue_(selectedItem.itemName())
self.infoDate.setStringValue_(
selectedItem.day().descriptionWithCalendarFormat_timeZone_locale_(
"%a, %b %d %Y", Cocoa.NSTimeZone.localTimeZone(), None
)
)
if selected == NOTIFY_TAG:
dueSecs = selectedItem.secsUntilDue()
hour, minutes, pmFlag = ConvertSecondsToTime(dueSecs)
self.infoNotifyAMPM.cellAtRow_column_(0, 0).setState_(not pmFlag)
self.infoNotifyAMPM.cellAtRow_column_(1, 0).setState_(pmFlag)
self.infoNotifyHour.setIntValue_(hour)
self.infoNotifyMinute.setIntValue_(minute)
notifySecs = selectedItem.secsUntilNotify()
clearButtonMatrix(self.infoNotifySwitchMatrix)
if notifySecs == 0:
self.infoNotifySwitchMatrix.cellAtRow_column_(
NotifyLengthNone, 0
).setState_(Cocoa.NSOnState)
elif notifySecs == SECS_IN_HOUR / 4:
self.infoNotifySwitchMatrix.cellAtRow_column_(
NotifyLengthQuarter, 0
).setState_(Cocoa.NSOnState)
elif notifySecs == SECS_IN_HOUR:
self.infoNotifySwitchMatrix.cellAtRow_column_(
NotifyLengthHour, 0
).setState_(Cocoa.NSOnState)
elif notifySecs == SECS_IN_DAY:
self.infoNotifySwitchMatrix.cellAtRow_column_(
NotifyLengthDay, 0
).setState_(Cocoa.NSOnState)
else:
self.infoNotifySwitchMatrix.cellAtRow_column_(
NotifyLengthOther, 0
).setState_(Cocoa.NSOnState)
self.infoNotifyOtherHours.setIntValue_(notifySecs / SECS_IN_HOUR)
elif selected == RESCHEDULE_TAG:
# left as an exercise
pass
elif selected == NOTES_TAG:
self.infoNotes.setString_(selectedItem.notes())
else:
self.infoItem.setStringValue_("")
self.infoDate.setStringValue_("")
self.infoNotifyHour.setStringValue_("")
self.infoNotifyMinute.setStringValue_("")
self.infoNotifyAMPM.cellAtRow_column_(0, 0).setState_(Cocoa.NSOnState)
self.infoNotifyAMPM.cellAtRow_column_(1, 0).setState_(Cocoa.NSOffState)
clearButtonMatrix(self.infoNotifySwitchMatrix)
self.infoNotifySwitchMatrix.cellAtRow_column_(
NotifyLengthNone, 0
).setState_(Cocoa.NSOnState)
self.infoNotifyOtherHours.setStringValue_("")
self.infoNotes.setString_("")
def setMainWindow_(self, mainWindow):
if not mainWindow:
return
controller = mainWindow.windowController()
if isinstance(controller.document(), ToDoDocument):
self._inspectingDocument = controller.document()
else:
self._inspectingDocument = None
self.updateInfoWindow()
def mainWindowChanged_(self, notification):
self.setMainWindow_(notification.object())
def mainWindowResigned_(self, notification):
self.setMainWindow_(None)
@objc.IBAction
def swapInfoWindowView_(self, sender):
selected = self.infoPopUp.selectedItem().tag()
if selected == NOTIFY_TAG:
newView = self.notifyView
elif selected == RESCHEDULE_TAG:
newView = self.reschedView
elif selected == NOTES_TAG:
newView = self.notesView
if self.dummyView.contentView() != newView:
self.dummyView.setContentView_(newView)
def selectedItemChanged_(self, notification):
self.updateInfoWindow()
def clearButtonMatrix(matrix):
rows, cols = matrix.getNumberOfRows_columns_()
for i in range(rows):
cell = matrix.cellAtRow_column_(i, 0)
if cell:
cell.setState_(False)
SelectionNotifyMatrix.py¶
import Cocoa
from objc import super # noqa: A004
RowSelectedNotification = "RowSelectedNotification"
class SelectionNotifyMatrix(Cocoa.NSMatrix):
def mouseDown_(self, theEvent):
super().mouseDown_(theEvent)
row = self.selectedRow()
if row != -1:
Cocoa.NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
RowSelectedNotification, self, None
)
def selectCellAtRow_column_(self, row, col):
super().selectCellAtRow_column_(row, col)
Cocoa.NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
RowSelectedNotification, self, None
)
ToDoCell.py¶
import Cocoa
import objc
NOT_DONE = 0
DONE = 1
DEFERRED = 2
class ToDoCell(Cocoa.NSButtonCell):
__slots__ = ("_triState", "_doneImage", "_deferredImage", "_timeDue")
def init(self):
self._triState = NOT_DONE
self._timeDue = None
self._doneImage = None
self._deferredImage = None
Cocoa.NSButtonCell.initTextCell_(self, "")
self.setType_(Cocoa.NSToggleButton)
self.setImagePosition_(Cocoa.NSImageLeft)
self.setBezelStyle_(Cocoa.NSShadowlessSquareBezelStyle)
self.setFont_(Cocoa.NSFont.userFontOfSize_(10))
self.setAlignment_(Cocoa.NSRightTextAlignment)
self._doneImage = Cocoa.NSImage.imageNamed_("DoneMark")
self._deferredImage = Cocoa.NSImage.imageNamed_("DeferredMark")
return self
@objc.typedAccessor(objc._C_INT)
def setTriState_(self, newState):
if newState > DEFERRED:
self._triState = NOT_DONE
else:
self._triState = newState
self.updateImage()
@objc.typedAccessor(objc._C_INT)
def triState(self):
return self._triState
def setState_(self, val):
pass
def state(self):
if self._triState == DEFERRED:
return DONE
else:
return self._triState
def updateImage(self):
if self._triState == NOT_DONE:
self.setImage_(None)
elif self._triState == DONE:
self.setImage_(self._doneImage)
elif self._triState == DEFERRED:
self.setImage_(self._deferredImage)
self.controlView().updateCell_(self)
def startTrackingAt_inView_(self, startPoint, controlView):
return 1
def stopTracking_at_inView_mouseIsUp_(
self, lastPoint, stopPoint, controlView, flag
):
if flag:
self.setTriState_(self.triState() + 1)
def setTimeDue_(self, newTime):
if newTime:
self._timeDue = newTime
self.setTitle_(
self._timeDue.descriptionWithCalendarFormat_timeZone_locale_(
"%I:%M %p", Cocoa.NSTimeZone.localTimeZone(), None
)
)
else:
self._timeDue = None
self.setTitle_("-->")
def timeDue(self):
return self._timeDue
ToDoDocument.py¶
import Cocoa
import objc
from objc import super # noqa: A004
from SelectionNotifyMatrix import RowSelectedNotification
from ToDoCell import ToDoCell
from ToDoItem import ToDoItem, INCOMPLETE
ToDoItemChangedNotification = "ToDoItemChangedNotification"
class ToDoDocument(Cocoa.NSDocument):
calendar = objc.IBOutlet()
dayLabel = objc.IBOutlet()
itemList = objc.IBOutlet()
statusList = objc.IBOutlet()
__slots__ = (
"_dataFromFile",
"_activeDays",
"_currentItems",
"_selectedItem",
"_selectedItemEdited",
)
def rowSelected_(self, notification):
row = notification.object().selectedRow()
if row == -1:
return
self._selectedItem = self._currentItems.objectAtIndex_(row)
if not isinstance(self._selectedItem, ToDoItem):
self._selectedItem = None
Cocoa.NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
ToDoItemChangedNotification, self._selectedItem, None
)
def init(self):
self = super().init()
if self is None:
return self
self._activeDays = None
self._currentItems = None
self._selectedItem = None
self._selectedItemEdited = 0
self._dataFromFile = None
return self
def __del__(self): # dealloc in Objective-C code
Cocoa.NSNotificationCenter.defaultCenter().removeObserver_(self)
def selectedItem(self):
return self._selectedItem
def windowNibName(self):
return "ToDoDocument"
def windowControllerDidLoadNib_(self, aController):
# Cocoa.NSDocument.windowControllerDidLoadNib_(self, aController)
self.setHasUndoManager_(0)
self.itemList.setDelegate_(self)
index = self.statusList.cells().count()
while index:
index -= 1
aCell = ToDoCell.alloc().init()
aCell.setTarget_(self)
aCell.setAction_("itemStatusClicked:")
self.statusList.putCell_atRow_column_(aCell, index, 0)
if self._dataFromFile:
self.loadDocWithData_(self._dataFromFile)
self._dataFromFile = None
else:
self.loadDocWithData_(None)
Cocoa.NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "rowSelected:", RowSelectedNotification, self.itemList
)
Cocoa.NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "rowSelected:", RowSelectedNotification, self.statusList
)
def loadDocWithData_(self, data):
if data:
dct = Cocoa.NSUnarchiver.unarchiveObjectWithData_(data)
self.initDataModelWithDictinary_(dct)
dayEnum = self._activeDays.keyEnumerator()
now = Cocoa.NSDate.date()
itemDate = dayEnum.nextObject()
while itemDate:
itemArray = self._activeDays.objectForKey_(itemDate)
itemEnum = itemArray.objectEnumerator()
anItem = itemEnum.nextObject()
while anItem:
if (
isinstance(anItem, ToDoItem)
and anItem.secsUntilNotify()
and anItem.status() == INCOMPLETE
):
due = anItem.day().addTimeInterfval_(anItem.secondsUntilDue())
elapsed = due.timeIntervalSinceDate_(now)
if elapsed > 0:
self.setTimerForItem_(anItem)
else:
Cocoa.NSBeep()
Cocoa.NSRunAlertPanel(
"To Do",
"%s on %s is past due!"
% (
anItem.itemName(),
due.descriptionWithCalendarFormat_timeZone_locale_(
"%b %d, %Y at %I:%M %p",
Cocoa.NSTimeZone.localTimeZone(),
None,
),
),
None,
None,
None,
)
anItem.setSecsUntilNotify_(0)
anItem = itemEnum.nextObject()
itemDate = dayEnum.nextObject()
else:
self.initDataModelWithDictionary_(None)
self.selectItemAtRow_(0)
self.updateLists()
self.dayLabel.setStringValue_(
self.calendar.selectedDay().descriptionWithCalendarFormat_timeZone_locale_(
"To Do on %a %B %d %Y", Cocoa.NSTimeZone.defaultTimeZone(), None
)
)
def initDataModelWithDictionary_(self, aDict):
if aDict:
self._activeDays = aDict
else:
self._activeDays = Cocoa.NSMutableDictionary.alloc().init()
date = self.calendar.selectedDay()
self.setCurrentItems_(self._activeDays.objectForKey_(date))
def setCurrentItems_(self, newItems):
if newItems:
self._currentItems = newItems.mutableCopy()
else:
numRows, numCols = self.itemList.getNumberOfRows_columns_(None, None)
self._currentItems = Cocoa.NSMutableArray.alloc().initWithCapacity_(numRows)
for _ in range(numRows):
self._currentItems.addObject_("")
def updateLists(self):
numRows = self.itemList.cells().count()
for i in range(numRows):
if self._currentItems:
thisItem = self._currentItems.objectAtIndex_(i)
else:
thisItem = None
if isinstance(thisItem, ToDoItem):
if thisItem.secsUntilDue():
due = thisItem.day().addTimeInterval_(thisItem.secsUntilDue())
else:
due = None
self.itemList.cellAtRow_column_(i, 0).setStringValue_(
thisItem.itemName()
)
self.statusList.cellAtRow_column_(i, 0).setTimeDue_(due)
self.statusList.cellAtRow_column_(i, 0).setTriState_(thisItem.status())
else:
self.itemList.cellAtRow_column_(i, 0).setStringValue_("")
self.statusList.cellAtRow_column_(i, 0).setTitle_("")
self.statusList.cellAtRow_column_(i, 0).setImage_(None)
def saveDocItems(self):
if self._currentItems:
cnt = self._currentItems.count()
for i in range(cnt):
anItem = self._currentItems.objectAtIndex_(i)
if isinstance(anItem, ToDoItem):
self._activeDays.setObject_forKey_(self._currentItems, anItem.day())
break
def controlTextDidEndEditing_(self, notif):
if not self._selectedItemEdited:
return
row = self.itemList.selectedRow()
newName = self.itemList.selectedCell().stringValue()
if isinstance(self._currentItems.objectAtIndex_(row), ToDoItem):
prevNameAtIndex = self._currentItems.objectAtIndex_(row).itemName()
if newName == "":
self._currentItems.replaceObjectAtRow_withObject_(row, "")
elif prevNameAtIndex != newName:
self._currentItems.objectAtRow_(row).setItemName_(newName)
elif newName != "":
newItem = ToDoItem.alloc().initWithName_andDate_(
newName, self.calendar.selectedDay()
)
self._currentItems.replaceObjectAtIndex_withObject_(row, newItem)
self._selectedItem = self._currentItems.objectAtIndex_(row)
if not isinstance(self._selectedItem, ToDoItem):
self._selectedItem = None
self.updateLists()
self._selectedItemEdited = 0
self.updateChangeCount_(Cocoa.NSChangeDone)
Cocoa.NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
ToDoItemChangedNotification, self._selectedItem, None
)
def selectedItemModified(self):
if self._selectedItem:
self.setTimerForItem_(self._selectedItem)
self.updateLists()
self.updateChangeCount_(Cocoa.NSChangeDone)
def calendarMatrix_didChangeToDate_(self, matrix, date):
self.saveDocItems()
if self._activeDays:
self.setCurrentItems_(self._activeDays.objectForKey_(date))
else:
pass
self.dayLabel.setStringValue_(
date.descriptionWithCalendarFormat_timeZone_locale_(
"To Do on %a %B %d %Y", Cocoa.NSTimeZone.defaultTimeZone(), None
)
)
self.updateLists()
self.selectedItemAtRow_(0)
def selectedItemAtRow_(self, row):
self.itemList.selectCellAtRow_column_(row, 0)
def controlTextDidBeginEditing_(self, notif):
self._selectedItemEdited = 1
def dataRepresentationOfType_(self, aType):
self.saveDocItems()
return Cocoa.NSArchiver.archivedDataWithRootObject_(self._activeDays)
def loadRepresentation_ofType_(self, data, aType):
if self.calendar:
self.loadDocWithData_(data)
else:
self._dataFromFile = data
return 1
@objc.IBAction
def itemStatusClicked_(self, sender):
row = sender.selectedRow()
cell = sender.cellAtRow_column_(row, 0)
item = self._currentItems.objectAtIndex_(row)
if isinstance(item, ToDoItem):
item.setStatus_(cell.triState())
self.setTimerForItem_(item)
self.updateLists()
self.updateChangeCount_(Cocoa.NSChangeDone)
Cocoa.NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
ToDoItemChangedNotification, item, None
)
def setTimerForItem_(self, anItem):
if anItem.secsUntilNotify() and anItem.status() == INCOMPLETE:
notifyDate = anItem.day().addTimeInterval_(
anItem.secsUntilDue() - anItem.secsUntilNotify()
)
aTimer = Cocoa.NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( # noqa: B950
notifyDate.timeIntervalSinceNow(),
self,
"itemTimerFired:",
anItem,
False,
)
anItem.setTimer_(aTimer)
else:
anItem.setTimer_(None)
def itemTimerFired_(self, timer):
anItem = timer.userInfo()
dueDate = anItem.day().addTimeInterval_(anItem.secsUntilDue())
Cocoa.NSBeep()
Cocoa.NSRunAlertPanel(
"To Do",
"%s on %s"
% (
anItem.itemName(),
dueDate.descriptionWithCalendarFormat_timeZone_locale_(
"%b %d, %Y at %I:%M: %p", Cocoa.NSTimeZone.defaultTimeZone(), None
),
),
None,
None,
None,
)
anItem.setSecsUntilNotify_(0)
self.setTimerForItem_(anItem)
self.updateLists()
Cocoa.NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_(
ToDoItemChangedNotification, anItem, None
)
def selectItemAtRow_(self, row):
self.itemList.selectCellAtRow_column_(row, 0)
ToDoItem.py¶
import Cocoa
import objc
from objc import super # noqa: A004
# enum ToDoItemStatus
INCOMPLETE = 0
COMPLETE = 1
DEFER_TO_NEXT_DAY = 2
SECS_IN_MINUTE = 60
SECS_IN_HOUR = SECS_IN_MINUTE * 60
SECS_IN_DAY = SECS_IN_HOUR * 24
SECS_IN_WEEK = SECS_IN_DAY * 7
class ToDoItem(Cocoa.NSObject):
__slots__ = (
"_day",
"_itemName",
"_notes",
"_timer",
"_secsUntilDue",
"_secsUntilNotify",
"_status",
)
def init(self):
self = super().init()
if self is None:
return None
self._day = None
self._itemName = None
self._notes = None
self._secsUntilDue = 0
self._secsUntilNotify = 0
self._status = None
self._timer = None
def description(self):
descr = """%s
\tName: %s
\tDay: %s
\tNotes: %s
\tCompleted: %s
\tSecs Until Due: %d
\tSecs Until Notify: %d
""" % (
super.description(),
self.itemName(),
self._day,
self._notes,
["No", "YES"][self.status() == COMPLETE],
self._secsUntilDue,
self._secsUntilNotify,
)
return descr
def initWithName_andDate_(self, aName, aDate):
self = super().init()
if self is None:
return None
self._day = None
self._itemName = None
self._notes = None
self._secsUntilDue = 0
self._secsUntilNotify = 0
self._status = None
self._timer = None
if not aName:
return None
self.setItemName_(aName)
if aDate:
self.setDay_(aDate)
else:
now = Cocoa.NSCalendarDate.date()
self.setDay_(
Cocoa.NSCalendarDate.dateWithYear_month_day_hour_minute_second_timeZone_(
now.yearOfCommonEra(),
now.monthOfYear(),
now.dayOfMonth(),
0,
0,
0,
Cocoa.NSTimeZone.localTimeZone(),
)
)
self.setStatus_(INCOMPLETE)
self.setNotes_("")
return self
def encodeWithCoder_(self, coder):
coder.encodeObject_(self._day)
coder.encodeObject_(self._itemName)
coder.encodeObject_(self._notes)
tempTime = self._secsUntilDue
coder.encodeValueOfObjCType_at_(objc._C_LNG, tempTime)
tempTime = self._secsUntilNotify
coder.encodeValueOfObjCType_at_(objc._C_LNG, tempTime)
tempStatus = self._status
coder.encodeValueOfObjCType_at_(objc._C_INT, tempStatus)
def initWithCoder_(self, coder):
self.setDay_(coder.decodeObject())
self.setItemName_(coder.decodeObject())
self.setNotes_(coder.decodeObject())
tempTime = coder.decodeObjectOfObjCType_at_(objc._C_LNG)
self.setSecsUntilDue_(tempTime)
tempTime = coder.decodeObjectOfObjCType_at_(objc._C_LNG)
self.setSecsUntilNotify_(tempTime)
tempStatus = coder.decodeObjectOfObjCType_at_(objc._C_INT)
self.setSecsUntilNotify_(tempStatus)
return self
def __del__(self): # dealloc
if self._notes:
self._timer.invalidate()
def setDay_(self, newDay):
self._day = newDay
def day(self):
return self._day
def setItemName_(self, newName):
self._itemName = newName
def itemName(self):
return self._itemName
def setNotes_(self, newNotes):
self._notes = newNotes
def notes(self):
return self._notes
def setTimer_(self, newTimer):
if self._timer:
self._timer.invalidate()
if newTimer:
self._timer = newTimer
else:
self._timer = None
def timer(self):
return self._timer
def setStatus_(self, newStatus):
self._status = newStatus
def status(self):
return self._status
def setSecsUntilDue_(self, secs):
self._secsUntilDue = secs
def secsUntilDue(self):
return self._secsUntilDue
def setSecsUntilNotify_(self, secs):
self._secsUntilNotify = secs
def secsUntilNotify(self):
return self._secsUntilNotify
def ConvertTimeToSeconds(hour, minute, pm):
if hour == 12:
hour = 0
if pm:
hour += 12
return (hour * SECS_IN_HOUR) + (minute * SECS_IN_MINUTE)
def ConvertSecondsToTime(secs):
pm = 0
hour = secs / SECS_IN_HOUR
if hour > 11:
hour -= 12
pm = 1
if hour == 0:
hour = 12
minute = (secs % SECS_IN_HOUR) / SECS_IN_MINUTE
return (hour, minute, pm)
TodoAppDelegate.py¶
import objc
from Foundation import NSObject
from InfoWindowController import InfoWindowController
class ToDoAppDelegate(NSObject):
@objc.IBAction
def showInfo_(self, sender):
InfoWindowController.sharedInfoWindowController().showWindow_(sender)
main.py¶
# Import all submodules, to make sure all
# classes are known to the runtime
import CalendarMatrix # noqa: F401
import InfoWindowController # noqa: F401
import SelectionNotifyMatrix # noqa: F401
import TodoAppDelegate # noqa: F401
import ToDoCell # noqa: F401
import ToDoDocument # noqa: F401
import ToDoItem # noqa: F401
from PyObjCTools import AppHelper
AppHelper.runEventLoop()
setup.py¶
"""
Script for building the example.
Usage:
python3 setup.py py2app
"""
import glob
from setuptools import setup
images = glob.glob("Images/*.tiff")
icons = glob.glob("Icons/*.icns")
plist = {
"CFBundleShortVersionString": "To Do v1",
"CFBundleIconFile": "ToDoApp.icns",
"CFBundleGetInfoString": "To Do v1",
"CFBundleIdentifier": "net.sf.pyobjc.ToDo",
"CFBundleDocumentTypes": [
{
"CFBundleTypeName": "To Do list",
"CFBundleTypeRole": "Editor",
"NSDocumentClass": "ToDoDocument",
"CFBundleTypeIconFile": "ToDoDoc.icns",
"CFBundleTypeExtensions": ["ToDo"],
"CFBundleTypeOSTypes": ["ToDo"],
}
],
"CFBundleName": "To Do",
}
setup(
app=["main.py"],
data_files=["English.lproj"] + images + icons,
options={"py2app": {"plist": plist}},
setup_requires=["py2app", "pyobjc-framework-Cocoa"],
)