Title: [commits] (stearns) [11422] First step of several reminder changes: nothing user-visible yet, mostly reorganizing for what's to come: reminders are now attached to ContentItems instead of CalendarEvents, plus a little renaming.

Diff

Modified: trunk/chandler/application/Utility.py (11421 => 11422)

--- trunk/chandler/application/Utility.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/application/Utility.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -33,7 +33,7 @@
 # with your name (and some helpful text). The comment's really there just to
 # cause Subversion to warn you of a conflict when you update, in case someone 
 # else changes it at the same time you do (that's why it's on the same line).
-SCHEMA_VERSION = "232" # stearns: Moved detail parcel to osaf/views
+SCHEMA_VERSION = "233" # stearns: ContentItems own reminders now
 
 logger = None # initialized in initLogging()
 

Modified: trunk/chandler/parcels/osaf/framework/blocks/ControlBlocks.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/framework/blocks/ControlBlocks.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/framework/blocks/ControlBlocks.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -1064,13 +1064,13 @@
                 return 0
             return -1
 
-        events = schema.ns('osaf.pim', view).eventsWithReminders
-        lastPastKey = events.findInIndex('reminderTime', 'last', matches)
+        itemsWithReminders = schema.ns('osaf.pim', view).itemsWithReminders
+        lastPastKey = itemsWithReminders.findInIndex('reminderTime', 'last', matches)
 
         if lastPastKey is not None:
-            return [(ev.reminderFireTime, ev ,ev.reminders.first()) 
-                    for ev in (view[key] for key in 
-                     events.iterindexkeys('reminderTime', None, lastPastKey))]
+            return [(item.reminderFireTime, item, item.reminders.first())
+                    for item in (view[key] for key in
+                     itemsWithReminders.iterindexkeys('reminderTime', None, lastPastKey))]
 
         return []
     
@@ -1107,8 +1107,8 @@
         if nextReminderTime is None:
             # The dialog didn't give us a time to fire; we'll fire at the
             # next non-pending reminder's time.
-            events = schema.ns('osaf.pim', self.itsView).eventsWithReminders
-            firstReminder = events.firstInIndex('reminderTime')
+            itemsWithReminders = schema.ns('osaf.pim', self.itsView).itemsWithReminders
+            firstReminder = itemsWithReminders.firstInIndex('reminderTime')
             if firstReminder is not None:
                 nextReminderTime = firstReminder.reminderFireTime
 

Modified: trunk/chandler/parcels/osaf/pim/__init__.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/pim/__init__.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/pim/__init__.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -16,8 +16,9 @@
 # Import classes whose schemas are part of this parcel
 # (this should include all ContentItem subclasses in this package)
 #
+from calculated import Calculated
 from items import (
-    Calculated, ContentKind, ContentItem, ImportanceEnum, Group, Principal, 
+    ContentKind, ContentItem, ImportanceEnum, Group, Principal, 
     Project, Tag, TriageEnum, getTriageStatusName, getTriageStatusOrder, 
     getNextTriageStatus, UserNotification
 )
@@ -30,7 +31,7 @@
 from calendar.DateTimeUtil import (ampmNames, durationFormat, mediumDateFormat, 
      monthNames, sampleDate, sampleTime, shortDateFormat, shortTimeFormat, 
      weekdayNames, weekdayName)
-from calendar.Reminders import Reminder, RemindableMixin
+from reminders import Remindable, Reminder
 from tasks import Task, TaskMixin
 from mail import EmailAddress
 from application.Parcel import Reference
@@ -121,6 +122,31 @@
         filterAttributes=['isGenerated', 'modificationFor']
     )
 
+    itemKindCollection = KindCollection.update(
+        parcel, 'items',
+        kind = ContentItem.getKind(view),
+       recursive=True)
+
+    itemsWithRemindersIncludingTrash = FilteredCollection.update(
+        parcel, 'itemsWithRemindersIncludingTrash',
+        source=itemKindCollection,
+        filterExpression="view.hasTrueValue(uuid, 'reminders')",
+        filterAttributes=['reminders'])
+
+    itemsWithReminders = AppCollection.update(
+        parcel, 'itemsWithReminders',
+        source=itemsWithRemindersIncludingTrash,
+        exclusions=trashCollection,
+        trash=None,
+    )
+
+    # the monitor list assumes all reminders will be relativeTo
+    # effectiveStartTime, which is true in 0.6, but may not be in the future
+    itemsWithReminders.addIndex('reminderTime', 'compare',
+                                compare='cmpReminderTime',
+                                monitor=('startTime', 'allDay', 'anyTime'
+                                         'reminders'))
+
     # the "All" / "My" collection
     allCollection = SmartCollection.update(parcel, 'allCollection',
         displayName=_(u"Dashboard"),
@@ -146,19 +172,6 @@
     
     events.addIndex('icalUID', 'value', attribute='icalUID')
 
-    eventsWithRemindersIncludingTrash = FilteredCollection.update(
-        parcel, 'eventsWithRemindersIncludingTrash',
-        source=events,
-        filterExpression="view.hasTrueValue(uuid, 'reminders')",
-        filterAttributes=['reminders'])
-
-    eventsWithReminders = AppCollection.update(
-        parcel, 'eventsWithReminders',
-        source=eventsWithRemindersIncludingTrash,
-        exclusions=trashCollection,
-        trash=None,
-    )
-
     longEvents = FilteredCollection.update(parcel, 'longEvents',
         source = events,
         filterMethod= (LongEventFilter(None, parcel), 'isLongEvent'),
@@ -176,13 +189,6 @@
                         superindex=(events, events.__collection__,
                                     'effectiveEndNoTZ'))
     
-    # the monitor list assumes all reminders will be relativeTo
-    # effectiveStartTime, which is true in 0.6, but may not be in the future
-    eventsWithReminders.addIndex('reminderTime', 'compare',
-                                 compare='cmpReminderTime',
-                                 monitor=('startTime', 'allDay', 'anyTime'
-                                          'reminders'))
-
     masterFilter = "view.hasTrueValues(uuid, 'occurrences', 'rruleset')"
     masterEvents = FilteredCollection.update(parcel, 'masterEvents',
         source = events,

Added: trunk/chandler/parcels/osaf/pim/calculated.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/pim/calculated.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/pim/calculated.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -0,0 +1,36 @@
+#   Copyright (c) 2003-2006 Open Source Applications Foundation
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+
+__parcel__ = "osaf.pim"
+
+#import logging
+#logger = logging.getLogger(__name__)
+
+class Calculated(property):
+    """
+    A property with type information, in the style of our schema.* objects.
+    - This could become a schema class when it grows up :-)
+    - I'm open to a different name: I think it oughta be schema.Property, but pje
+    thought Calculated was better...
+    """
+    def __new__(cls, schema_type, basedOn, fget, fset=None, fdel=None,
+                doc=None):
+        return property.__new__(cls, fget, fset, fdel, doc)
+
+    def __init__(self, schema_type, basedOn, fget, fset=None, fdel=None,
+                 doc=None):
+        property.__init__(self, fget, fset, fdel, doc)
+        self.type = schema_type
+        self.basedOn = basedOn
Property changes on: trunk/chandler/parcels/osaf/pim/calculated.py
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native

Modified: trunk/chandler/parcels/osaf/pim/calendar/Calendar.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/pim/calendar/Calendar.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/pim/calendar/Calendar.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -25,16 +25,17 @@
 
 from application import schema
 from osaf.pim.contacts import Contact
-from osaf.pim.items import Calculated, ContentItem
+from osaf.pim.calculated import Calculated
+from osaf.pim.items import ContentItem
 from osaf.pim.notes import Note
 from osaf.pim.calendar import Recurrence
 from application.dialogs import RecurrenceDialog
 import wx
 
 from TimeZone import formatTime
-from Reminders import RemindableMixin, Reminder
 from osaf.pim.calendar.TimeZone import coerceTimeZone, TimeZoneInfo
 from osaf.pim.calendar import DateTimeUtil
+from osaf.pim.reminders import Remindable, Reminder
 from PyICU import DateFormat, DateFormatSymbols, ICUtzinfo
 from datetime import datetime, time, timedelta
 import itertools
@@ -269,7 +270,7 @@
 
 
 
-class CalendarEventMixin(RemindableMixin):
+class CalendarEventMixin(ContentItem):
     """
     This is the set of CalendarEvent-specific attributes. This Kind is 'mixed
     in' to others kinds to create Kinds that can be instantiated.

Deleted: trunk/chandler/parcels/osaf/pim/calendar/Reminders.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/pim/calendar/Reminders.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/pim/calendar/Reminders.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -1,209 +0,0 @@
-#   Copyright (c) 2003-2006 Open Source Applications Foundation
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-
-from application import schema
-from osaf.pim.items import Calculated, ContentItem
-from datetime import datetime, time, timedelta
-from PyICU import ICUtzinfo
-
-
-class Reminder(schema.Item):
-    delta = schema.One(
-        schema.TimeDelta,
-        doc="The amount in advance this reminder should occur (usually negative!)",
-    )
-
-    relativeTo = schema.One(
-        schema.Text,
-        initialValue='effectiveStartTime',
-    )
-
-    reminderItems = schema.Sequence(
-        "RemindableMixin",
-        inverse="reminders",
-        initialValue=[]
-    )
-
-    expiredReminderItems = schema.Sequence(
-        "RemindableMixin",
-        inverse="expiredReminders",
-        initialValue=[]
-    )
-
-    snoozedUntil = schema.One(
-        schema.DateTimeTZ,
-        defaultValue=None
-    )
-
-    schema.addClouds(
-        sharing = schema.Cloud(delta, relativeTo, snoozedUntil)
-    )
-
-    def getBaseTimeFor(self, remindable):
-        """
-        Get the relative-to time for this remindable's next reminder
-        so that the UI can generate a message relative to it.
-        """
-        return getattr(remindable, self.relativeTo, datetime.max)
-
-    def getNextReminderTimeFor(self, remindable):
-        """ Get the time for this remindable's next reminder """
-        result = self.snoozedUntil or \
-               (self.getBaseTimeFor(remindable) + self.delta)
-        if result.tzinfo is None:
-            result = result.replace(tzinfo=ICUtzinfo.default)
-        return result
-
-class RemindableMixin(ContentItem):
-    reminders = schema.Sequence(
-        Reminder,
-        inverse=Reminder.reminderItems,
-        initialValue=[]
-    )
-
-    expiredReminders = schema.Sequence(
-        Reminder,
-        inverse=Reminder.expiredReminderItems,
-        initialValue=[]
-    )
-
-    schema.addClouds(
-        copying = schema.Cloud(reminders,expiredReminders),
-        sharing = schema.Cloud(
-            byCloud = [reminders, expiredReminders]
-        )
-    )
-
-    def getReminderInterval(self):
-        for attr in ("reminders", "expiredReminders"):
-            if (self.hasLocalAttributeValue(attr)):
-                #@@@ This assumes we've only got 0 or 1 reminders.
-                first = getattr(self, attr).first()
-                try:
-                    return first.delta
-                except AttributeError:
-                    pass
-
-        return None
-
-    def setReminderInterval(self, delta):
-        reminderCollection = self.reminders
-        firstReminder = reminderCollection.first()
-
-        if firstReminder is None:
-            reminderCollection = self.expiredReminders
-            firstReminder = reminderCollection.first()
-
-        if firstReminder is not None:
-            reminderCollection.remove(firstReminder)
-            if not (len(firstReminder.reminderItems) or \
-                    len(firstReminder.expiredReminderItems)):
-                firstReminder.delete()
-
-        if delta is not None:
-            self.makeReminder(delta, checkExpired=True)
-
-    reminderInterval = Calculated(
-        schema.TimeDelta,
-        basedOn=('reminders',),
-        fget=getReminderInterval,
-        fset=setReminderInterval,
-        doc="Reminder interval, computed from the first unexpired reminder."
-    )
-
-    def getReminderFireTime(self):
-        """
-        A simplification of the possible complexity of reminder, assumes one
-        or zero reminders.  Returns a datetime or None.
-        """
-        reminder = self.reminders.first()
-        if reminder is None:
-            return None
-        else:
-            return reminder.getNextReminderTimeFor(self)
-
-    reminderFireTime = Calculated(
-        schema.DateTimeTZ,
-        basedOn=('startTime', 'allDay', 'anyTime', 'reminders'),
-        fget=getReminderFireTime,
-        doc="Reminder fire time, or None for no unexpired reminders")
-
-    def makeReminder(self, delta, checkExpired=False):
-        # @@@ I think the proxy code should override calls to this method
-        # add a separate reference to this reminder to each generated event,
-        # (or something like that). (Remindable.snoozeReminder will call this
-        # method; that operation should only affect the actual event, not the
-        # series)
-        newReminder = Reminder(None, delta=delta, itsView=self.itsView)
-
-        addThisTo = self.reminders
-
-        if checkExpired:
-            nextTime = newReminder.getNextReminderTimeFor(self)
-
-            if (nextTime is not None and
-                nextTime < datetime.now(ICUtzinfo.default)):
-                addThisTo = self.expiredReminders
-
-        addThisTo.add(newReminder)
-        return newReminder
-
-    def dismissReminder(self, reminder):
-        """ Dismiss this reminder. """
-
-        # Make sure the next one's around, so we'll prime the reminder-
-        # watching mechanism to alert us about it. We also check that
-        # reminders for past events don't trigger this one.
-        now = datetime.now(ICUtzinfo.default)
-
-        try:
-            getNextOccurrenceMethod = self.getNextOccurrence
-        except AttributeError:
-            pass
-        else:
-            # Get the next occurrence of this event. We
-            # don't need to do anything with it; we just
-            # want to make sure it's been instantiated
-            # so that the next reminder will fire.
-            getNextOccurrenceMethod(after=now)
-
-        # In the case of generated occurrences, the reminder
-        # may already have fired (cf fixReminders() in
-        # CalendarEventMixin.getNextOccurrence
-        if reminder in self.reminders:
-            self.reminders.remove(reminder)
-        if getattr(reminder, 'snoozedUntil', None) is not None:
-            # This is a "snooze" reminder, just toss it.
-            assert len(reminder.reminderItems) == 0
-            assert len(reminder.expiredReminderItems) == 0
-            reminder.delete()
-        else:
-            if not reminder in self.expiredReminders:
-                self.expiredReminders.add(reminder)
-
-
-
-    def snoozeReminder(self, reminder, delay):
-        """ Snooze this reminder for this long. """
-        # Dismiss the original reminder
-        originalTime = reminder.getNextReminderTimeFor(self)
-        self.dismissReminder(reminder)
-
-        # Make a new reminder for this event
-        newReminder = Reminder(None, itsView=self.itsView,
-                               snoozedUntil=(datetime.now(ICUtzinfo.default) +
-                                             delay))
-        self.reminders.add(newReminder)
-        return newReminder

Modified: trunk/chandler/parcels/osaf/pim/items.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/pim/items.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/pim/items.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -31,6 +31,7 @@
 from i18n import ChandlerMessageFactory as _
 from osaf import messages
 from PyICU import ICUtzinfo
+from reminders import Remindable
 
 logger = logging.getLogger(__name__)
 
@@ -67,26 +68,8 @@
 def getNextTriageStatus(value):
     return TriageEnum.values[(triageStatusOrder[value]+1) % len(TriageEnum.values)]
     
-class Calculated(property):
+class ContentItem(Remindable):
     """
-    A property with type information, in the style of our schema.* objects.
-    - This could become a schema class when it grows up :-)
-    - I'm open to a different name: I think it oughta be schema.Property, but pje
-    thought Calculated was better...
-    """
-    def __new__(cls, schema_type, basedOn, fget, fset=None, fdel=None,
-                doc=None):
-        return property.__new__(cls, fget, fset, fdel, doc)
-
-    def __init__(self, schema_type, basedOn, fget, fset=None, fdel=None,
-                 doc=None):
-        property.__init__(self, fget, fset, fdel, doc)
-        self.type = schema_type
-        self.basedOn = basedOn
-
-
-class ContentItem(schema.Item):
-    """
     Content Item
 
     Content Item is the abstract super-kind for things like Contacts, Calendar

Added: trunk/chandler/parcels/osaf/pim/reminders.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/pim/reminders.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/pim/reminders.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -0,0 +1,209 @@
+#   Copyright (c) 2003-2006 Open Source Applications Foundation
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+
+from application import schema
+from calculated import Calculated
+from datetime import datetime, time, timedelta
+from PyICU import ICUtzinfo
+
+
+class Reminder(schema.Item):
+    delta = schema.One(
+        schema.TimeDelta,
+        doc="The amount in advance this reminder should occur (usually negative!)",
+    )
+
+    relativeTo = schema.One(
+        schema.Text,
+        initialValue='effectiveStartTime',
+    )
+
+    reminderItems = schema.Sequence(
+        "Remindable",
+        inverse="reminders",
+        initialValue=[]
+    )
+
+    expiredReminderItems = schema.Sequence(
+        "Remindable",
+        inverse="expiredReminders",
+        initialValue=[]
+    )
+
+    snoozedUntil = schema.One(
+        schema.DateTimeTZ,
+        defaultValue=None
+    )
+
+    schema.addClouds(
+        sharing = schema.Cloud(delta, relativeTo, snoozedUntil)
+    )
+
+    def getBaseTimeFor(self, remindable):
+        """
+        Get the relative-to time for this remindable's next reminder
+        so that the UI can generate a message relative to it.
+        """
+        return getattr(remindable, self.relativeTo, datetime.max)
+
+    def getNextReminderTimeFor(self, remindable):
+        """ Get the time for this remindable's next reminder """
+        result = self.snoozedUntil or \
+               (self.getBaseTimeFor(remindable) + self.delta)
+        if result.tzinfo is None:
+            result = result.replace(tzinfo=ICUtzinfo.default)
+        return result
+
+class Remindable(schema.Item):
+    reminders = schema.Sequence(
+        Reminder,
+        inverse=Reminder.reminderItems,
+        initialValue=[]
+    )
+
+    expiredReminders = schema.Sequence(
+        Reminder,
+        inverse=Reminder.expiredReminderItems,
+        initialValue=[]
+    )
+
+    schema.addClouds(
+        copying = schema.Cloud(reminders,expiredReminders),
+        sharing = schema.Cloud(
+            byCloud = [reminders, expiredReminders]
+        )
+    )
+
+    def getReminderInterval(self):
+        for attr in ("reminders", "expiredReminders"):
+            if (self.hasLocalAttributeValue(attr)):
+                #@@@ This assumes we've only got 0 or 1 reminders.
+                first = getattr(self, attr).first()
+                try:
+                    return first.delta
+                except AttributeError:
+                    pass
+
+        return None
+
+    def setReminderInterval(self, delta):
+        reminderCollection = self.reminders
+        firstReminder = reminderCollection.first()
+
+        if firstReminder is None:
+            reminderCollection = self.expiredReminders
+            firstReminder = reminderCollection.first()
+
+        if firstReminder is not None:
+            reminderCollection.remove(firstReminder)
+            if not (len(firstReminder.reminderItems) or \
+                    len(firstReminder.expiredReminderItems)):
+                firstReminder.delete()
+
+        if delta is not None:
+            self.makeReminder(delta, checkExpired=True)
+
+    reminderInterval = Calculated(
+        schema.TimeDelta,
+        basedOn=('reminders',),
+        fget=getReminderInterval,
+        fset=setReminderInterval,
+        doc="Reminder interval, computed from the first unexpired reminder."
+    )
+
+    def getReminderFireTime(self):
+        """
+        A simplification of the possible complexity of reminder, assumes one
+        or zero reminders.  Returns a datetime or None.
+        """
+        reminder = self.reminders.first()
+        if reminder is None:
+            return None
+        else:
+            return reminder.getNextReminderTimeFor(self)
+
+    reminderFireTime = Calculated(
+        schema.DateTimeTZ,
+        basedOn=('startTime', 'allDay', 'anyTime', 'reminders'),
+        fget=getReminderFireTime,
+        doc="Reminder fire time, or None for no unexpired reminders")
+
+    def makeReminder(self, delta, checkExpired=False):
+        # @@@ I think the proxy code should override calls to this method
+        # add a separate reference to this reminder to each generated event,
+        # (or something like that). (Remindable.snoozeReminder will call this
+        # method; that operation should only affect the actual event, not the
+        # series)
+        newReminder = Reminder(None, delta=delta, itsView=self.itsView)
+
+        addThisTo = self.reminders
+
+        if checkExpired:
+            nextTime = newReminder.getNextReminderTimeFor(self)
+
+            if (nextTime is not None and
+                nextTime < datetime.now(ICUtzinfo.default)):
+                addThisTo = self.expiredReminders
+
+        addThisTo.add(newReminder)
+        return newReminder
+
+    def dismissReminder(self, reminder):
+        """ Dismiss this reminder. """
+
+        # Make sure the next one's around, so we'll prime the reminder-
+        # watching mechanism to alert us about it. We also check that
+        # reminders for past events don't trigger this one.
+        now = datetime.now(ICUtzinfo.default)
+
+        try:
+            getNextOccurrenceMethod = self.getNextOccurrence
+        except AttributeError:
+            pass
+        else:
+            # Get the next occurrence of this event. We
+            # don't need to do anything with it; we just
+            # want to make sure it's been instantiated
+            # so that the next reminder will fire.
+            getNextOccurrenceMethod(after=now)
+
+        # In the case of generated occurrences, the reminder
+        # may already have fired (cf fixReminders() in
+        # CalendarEventMixin.getNextOccurrence
+        if reminder in self.reminders:
+            self.reminders.remove(reminder)
+        if getattr(reminder, 'snoozedUntil', None) is not None:
+            # This is a "snooze" reminder, just toss it.
+            assert len(reminder.reminderItems) == 0
+            assert len(reminder.expiredReminderItems) == 0
+            reminder.delete()
+        else:
+            if not reminder in self.expiredReminders:
+                self.expiredReminders.add(reminder)
+
+
+
+    def snoozeReminder(self, reminder, delay):
+        """ Snooze this reminder for this long. """
+        # Dismiss the original reminder
+        originalTime = reminder.getNextReminderTimeFor(self)
+        self.dismissReminder(reminder)
+
+        # Make a new reminder for this event
+        newReminder = Reminder(None, itsView=self.itsView,
+                               snoozedUntil=(datetime.now(ICUtzinfo.default) +
+                                             delay))
+        self.reminders.add(newReminder)
+        return newReminder
Property changes on: trunk/chandler/parcels/osaf/pim/reminders.py
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native

Modified: trunk/chandler/parcels/osaf/views/detail/__init__.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/views/detail/__init__.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/views/detail/__init__.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -30,7 +30,6 @@
     CalendarRecurrenceEndAreaBlock, 
     CalendarRecurrencePopupAreaBlock, 
     CalendarRecurrenceSpacer2Area, 
-    CalendarReminderAreaBlock, 
     CalendarStampBlock,
     CalendarTimeAEBlock,
     CalendarTimeZoneAreaBlock,
@@ -45,6 +44,7 @@
     MailMessageButtonBlock,
     OutboundOnlyAreaBlock,
     PrivateSwitchButtonBlock, 
+    ReminderAreaBlock, 
     StaticRedirectAttribute,
     StaticRedirectAttributeLabel, 
     StaticTextLabel, 

Modified: trunk/chandler/parcels/osaf/views/detail/detail.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/views/detail/detail.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/views/detail/detail.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -684,22 +684,79 @@
 
 class ReminderConditionalBlock(Item):
     def shouldShow (self, item):
-        return item.isAttributeModifiable('reminders') \
-               or len(item.reminders) > 0
+        return isinstance(item, pim.CalendarEventMixin) and \
+               (item.isAttributeModifiable('reminders') \
+               or len(item.reminders) > 0)
 
     def getWatchList(self):
         watchList = super(ReminderConditionalBlock, self).getWatchList()
         watchList.append((self.item, 'reminders'))
         return watchList
     
-class CalendarReminderSpacerBlock(ReminderConditionalBlock,
-                                  SynchronizedSpacerBlock):
+class ReminderSpacerBlock(ReminderConditionalBlock,
+                          SynchronizedSpacerBlock):
     pass
 
-class CalendarReminderAreaBlock (ReminderConditionalBlock,
-                                 DetailSynchronizedContentItemDetail):
+class ReminderAreaBlock(ReminderConditionalBlock,
+                        DetailSynchronizedContentItemDetail):
     pass
 
+class ReminderAttributeEditor(ChoiceAttributeEditor):
+    def GetControlValue (self, control):
+        """
+        Get the reminder delta value for the current selection.
+        """
+        # @@@ i18n For now, assumes that the menu will be a number of minutes, 
+        # followed by a space (eg, "1 minute", "15 minutes", etc), or something
+        # that doesn't match this (eg, "None") for no-alarm.
+        menuChoice = control.GetStringSelection()
+        try:
+            minuteCount = int(menuChoice.split(u" ")[0])
+        except ValueError:
+            # "None"
+            value = None
+        else:
+            value = timedelta(minutes=-minuteCount)
+        return value
+
+    def SetControlValue (self, control, value):
+        """
+        Select the choice that matches this delta value.
+        """
+        # We also take this opportunity to populate the menu
+        existingValue = self.GetControlValue(control)
+        if existingValue != value or control.GetCount() == 0:            
+            # rebuild the list of choices
+            choices = self.GetChoices()
+            control.Clear()
+            control.AppendItems(choices)
+
+            if value is None:
+                choiceIndex = 0 # the "None" choice
+            else:
+                minutes = ((value.days * 1440) + (value.seconds / 60))
+                reminderChoice = (minutes == -1) and _(u"1 minute") or (_(u"%(numberOf)i minutes") % {'numberOf': -minutes})
+                choiceIndex = control.FindString(reminderChoice)
+                # If we can't find the choice, just show "None" - this'll happen if this event's reminder has been "snoozed"
+                if choiceIndex == -1:
+                    choiceIndex = 0 # the "None" choice
+            control.Select(choiceIndex)
+
+    def GetAttributeValue (self, item, attributeName):
+        """
+        Get the value from the specified attribute of the item.
+        """
+        return item.reminderInterval
+
+    def SetAttributeValue (self, item, attributeName, value):
+        """Set the value of the attribute given by the value.
+        """
+        if not self.ReadOnly((item, attributeName)) and \
+           value != self.GetAttributeValue(item, attributeName):
+
+            setattr(item, attributeName, value)
+            self.AttributeChanged()
+
 class TransparencyConditionalBlock(Item):
     def shouldShow (self, item):
         # don't show for anyTime or @time events (but do show for allDay
@@ -1035,62 +1092,6 @@
             self.SetControlValue(self.control, 
                              self.GetAttributeValue(item, attributeName))
 
-class ReminderAttributeEditor(ChoiceAttributeEditor):
-    def GetControlValue (self, control):
-        """
-        Get the reminder delta value for the current selection.
-        """
-        # @@@ i18n For now, assumes that the menu will be a number of minutes, 
-        # followed by a space (eg, "1 minute", "15 minutes", etc), or something
-        # that doesn't match this (eg, "None") for no-alarm.
-        menuChoice = control.GetStringSelection()
-        try:
-            minuteCount = int(menuChoice.split(u" ")[0])
-        except ValueError:
-            # "None"
-            value = None
-        else:
-            value = timedelta(minutes=-minuteCount)
-        return value
-
-    def SetControlValue (self, control, value):
-        """
-        Select the choice that matches this delta value.
-        """
-        # We also take this opportunity to populate the menu
-        existingValue = self.GetControlValue(control)
-        if existingValue != value or control.GetCount() == 0:            
-            # rebuild the list of choices
-            choices = self.GetChoices()
-            control.Clear()
-            control.AppendItems(choices)
-
-            if value is None:
-                choiceIndex = 0 # the "None" choice
-            else:
-                minutes = ((value.days * 1440) + (value.seconds / 60))
-                reminderChoice = (minutes == -1) and _(u"1 minute") or (_(u"%(numberOf)i minutes") % {'numberOf': -minutes})
-                choiceIndex = control.FindString(reminderChoice)
-                # If we can't find the choice, just show "None" - this'll happen if this event's reminder has been "snoozed"
-                if choiceIndex == -1:
-                    choiceIndex = 0 # the "None" choice
-            control.Select(choiceIndex)
-
-    def GetAttributeValue (self, item, attributeName):
-        """
-        Get the value from the specified attribute of the item.
-        """
-        return item.reminderInterval
-
-    def SetAttributeValue (self, item, attributeName, value):
-        """Set the value of the attribute given by the value.
-        """
-        if not self.ReadOnly((item, attributeName)) and \
-           value != self.GetAttributeValue(item, attributeName):
-
-            setattr(item, attributeName, value)
-            self.AttributeChanged()
-
 class RecurrenceAttributeEditor(ChoiceAttributeEditor):
     # These are the values we pass around; they're the same as the menu indices.
     # This is a list of the frequency enumeration names (defined in 

Modified: trunk/chandler/parcels/osaf/views/detail/detailblocks.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/views/detail/detailblocks.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/views/detail/detailblocks.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -398,6 +398,27 @@
             position=0.5,
             border=RectType(0,6,0,6)).install(parcel)
 
+    reminderArea = \
+        makeArea(parcel, 'ReminderArea',
+            baseClass=ReminderAreaBlock,
+            childrenBlocks=[
+                makeLabel(parcel, _(u'alarm'), borderTop=5),
+                makeSpacer(parcel, width=8),
+                makeEditor(parcel, 'EditReminder',
+                    viewAttribute=u'reminderInterval',
+                    presentationStyle={
+                        # @@@ XXX i18n: the code assumes that if the value
+                        # starts with a digit, it's a number of minutes; if not,
+                        # it's None.
+                        'choices': [_(u'None'), _(u'1 minute'), _(u'5 minutes'), 
+                                    _(u'10 minutes'), _(u'30 minutes'), 
+                                    _(u'60 minutes'), _(u'90 minutes')],
+                        'format' : 'reminder'
+                    },
+                    stretchFactor=0.0,
+                    minimumSize=SizeType(100, -1))],
+            position=0.81).install(parcel)
+
     # The Note AEBlock
     notesBlock = makeEditor(parcel, 'NotesBlock',
                             viewAttribute=u'body',
@@ -425,6 +446,9 @@
         makeSpacer(parcel, height=6, position=0.01).install(parcel),
         parcel['MarkupBar'],
         headlineArea, 
+        makeSpacer(parcel, height=7, baseClass=ReminderSpacerBlock,
+                   position=0.809999).install(parcel),
+        reminderArea,
         makeSpacer(parcel, height=7, position=0.8999).install(parcel),
         notesBlock,
         appearsInArea,
@@ -578,26 +602,6 @@
                     presentationStyle={'format': 'ends'},
                     stretchFactor=0.0,
                     size=SizeType(75, -1))])
-
-    reminderArea = \
-        makeArea(parcel, 'CalendarReminderArea',
-            baseClass=CalendarReminderAreaBlock,
-            childrenBlocks=[
-                makeLabel(parcel, _(u'alarm'), borderTop=5),
-                makeSpacer(parcel, width=8),
-                makeEditor(parcel, 'EditReminder',
-                    viewAttribute=u'reminderInterval',
-                    presentationStyle={
-                        # @@@ XXX i18n: the code assumes that if the value
-                        # starts with a digit, it's a number of minutes; if not,
-                        # it's None.
-                        'choices': [_(u'None'), _(u'1 minute'), _(u'5 minutes'), 
-                                    _(u'10 minutes'), _(u'30 minutes'), 
-                                    _(u'60 minutes'), _(u'90 minutes')],
-                        'format' : 'reminder'
-                    },
-                    stretchFactor=0.0,
-                    minimumSize=SizeType(100, -1))])
  
     timeDescriptionArea = \
         makeArea(parcel, 'CalendarTimeDescriptionArea',
@@ -641,10 +645,7 @@
                 locationArea,
                 makeSpacer(parcel, height=4),
                 #timeDescriptionArea,
-                timeEditArea,
-                makeSpacer(parcel, height=7,
-                           baseClass=CalendarReminderSpacerBlock),
-                reminderArea]).install(parcel)
+                timeEditArea]).install(parcel)
 
     makeSubtree(parcel, osaf.pim.CalendarEventMixin, [ calendarDetails ])
  

Modified: trunk/chandler/parcels/osaf/views/main/mainblocks.py (11421 => 11422)

--- trunk/chandler/parcels/osaf/views/main/mainblocks.py	2006-08-15 19:56:04 UTC (rev 11421)
+++ trunk/chandler/parcels/osaf/views/main/mainblocks.py	2006-08-15 20:04:20 UTC (rev 11422)
@@ -39,7 +39,7 @@
     ReminderTimer.update(
         parcel, 'ReminderTimer',
         event = main.ReminderTime,
-        contents = pim_ns.eventsWithReminders)
+        contents = pim_ns.itemsWithReminders)
 
     SidebarBranchPointDelegateInstance = SidebarBranchPointDelegate.update(
         parcel, 'SidebarBranchPointDelegateInstance',




_______________________________________________
Commits mailing list
[email protected]
http://lists.osafoundation.org/mailman/listinfo/commits

Reply via email to