b=293183, implement exception support for recurrence, r=shaver

This commit is contained in:
vladimir%pobox.com 2005-06-10 01:31:48 +00:00
parent 003486ce09
commit 1afea82710
43 changed files with 2003 additions and 1295 deletions

View File

@ -40,7 +40,6 @@
#include "calDateTime.h"
#include "calICSService.h"
#include "calRecurrenceInfo.h"
#include "calRecurrenceRule.h"
#include "calRecurrenceDate.h"
#include "calRecurrenceDateSet.h"
@ -49,37 +48,73 @@
NS_GENERIC_FACTORY_CONSTRUCTOR(calDateTime)
NS_GENERIC_FACTORY_CONSTRUCTOR(calICSService)
NS_GENERIC_FACTORY_CONSTRUCTOR(calRecurrenceInfo)
NS_GENERIC_FACTORY_CONSTRUCTOR(calRecurrenceRule)
NS_GENERIC_FACTORY_CONSTRUCTOR(calRecurrenceDate)
NS_GENERIC_FACTORY_CONSTRUCTOR(calRecurrenceDateSet)
NS_DECL_CLASSINFO(calDateTime)
NS_DECL_CLASSINFO(calICSService)
NS_DECL_CLASSINFO(calRecurrenceRule)
NS_DECL_CLASSINFO(calRecurrenceDate)
NS_DECL_CLASSINFO(calRecurrenceDateSet)
static const nsModuleComponentInfo components[] =
{
{ "Calendar DateTime Object",
CAL_DATETIME_CID,
CAL_DATETIME_CONTRACTID,
calDateTimeConstructor },
calDateTimeConstructor,
NULL,
NULL,
NULL,
NS_CI_INTERFACE_GETTER_NAME(calDateTime),
NULL,
&NS_CLASSINFO_NAME(calDateTime)
},
{ "ICS parser/serializer",
CAL_ICSSERVICE_CID,
CAL_ICSSERVICE_CONTRACTID,
calICSServiceConstructor },
{ "Calendar Recurrence Object",
CAL_RECURRENCEINFO_CID,
CAL_RECURRENCEINFO_CONTRACTID,
calRecurrenceInfoConstructor },
calICSServiceConstructor,
NULL,
NULL,
NULL,
NS_CI_INTERFACE_GETTER_NAME(calICSService),
NULL,
&NS_CLASSINFO_NAME(calICSService)
},
{ "Calendar Recurrence Rule",
CAL_RECURRENCERULE_CID,
CAL_RECURRENCERULE_CONTRACTID,
calRecurrenceRuleConstructor },
calRecurrenceRuleConstructor,
NULL,
NULL,
NULL,
NS_CI_INTERFACE_GETTER_NAME(calRecurrenceRule),
NULL,
&NS_CLASSINFO_NAME(calRecurrenceRule)
},
{ "Calendar Recurrence Date",
CAL_RECURRENCEDATE_CID,
CAL_RECURRENCEDATE_CONTRACTID,
calRecurrenceDateConstructor },
calRecurrenceDateConstructor,
NULL,
NULL,
NULL,
NS_CI_INTERFACE_GETTER_NAME(calRecurrenceDate),
NULL,
&NS_CLASSINFO_NAME(calRecurrenceDate)
},
{ "Calendar Recurrence Date Set",
CAL_RECURRENCEDATESET_CID,
CAL_RECURRENCEDATESET_CONTRACTID,
calRecurrenceDateSetConstructor }
calRecurrenceDateSetConstructor,
NULL,
NULL,
NULL,
NS_CI_INTERFACE_GETTER_NAME(calRecurrenceDateSet),
NULL,
&NS_CLASSINFO_NAME(calRecurrenceDateSet)
}
};
NS_IMPL_NSGETMODULE(calBaseModule, components)

View File

@ -76,9 +76,13 @@ function onAccept()
{
// if this event isn't mutable, we need to clone it like a sheep
var originalEvent = window.calendarEvent;
var event = null;
var event = originalEvent;
event = (originalEvent.isMutable) ? originalEvent : originalEvent.clone();
if (!event.isMutable) {
event = event.clone();
} else {
dump ("#### modifyEvent is mutable already?\n");
}
saveDialog(event);
@ -137,12 +141,20 @@ function loadDialog()
}
/* recurrence */
if (event.recurrenceInfo) {
/* if the item is a proxy occurrence/instance, a few things aren't valid:
* - Setting recurrence on the item
* - changing the calendar
*/
if (event.parentItem != event) {
setElementValue("event-recurrence", "true", "disabled");
setElementValue("set-recurrence", "true", "disabled");
setElementValue("event-calendar", "true", "disabled");
} else if (event.recurrenceInfo) {
setElementValue("event-recurrence", "true", "checked");
}
/* alarms */
if (event.hasAlarm) {
if (event.alarmTime) {
var alarmLength = event.getProperty("alarmLength");
if (alarmLength != null) {
setElementValue("alarm-length-field", alarmLength);
@ -193,8 +205,8 @@ function saveDialog(event)
}
/* alarms */
event.hasAlarm = (getElementValue("event-alarm") != "none");
if (!event.hasAlarm) {
var hasAlarm = (getElementValue("event-alarm") != "none");
if (!hasAlarm) {
event.deleteProperty("alarmLength");
event.deleteProperty("alarmUnits");
event.deleteProperty("alarmRelated");

View File

@ -43,10 +43,16 @@ calendar-event-column {
.fgdragcontainer[dragging="true"] {
display: -moz-box;
/* This is a workaround for a stack bug and display: hidden in underlying
* elements -- the display: hidden bits get misrendered as being on top.
* Setting an opacity here forces a view to be created for this element, too.
*/
opacity: 0.9999;
}
.fgdragbox-label {
font-weight: bold;
overflow: hidden;
}
/*== calendar-event-box ==*/
@ -58,6 +64,7 @@ calendar-event-box {
.calendar-event-box-container {
background: #4e84c2;
padding: 2px;
overflow: hidden;
}
.calendar-event-box-container[parentorient="vertical"] {

View File

@ -110,7 +110,7 @@
if (!orient) orient = "horizontal";
if (orient == "vertical") otherorient = "horizontal";
dump ("calendar-time-bar: orient: " + orient + " other: " + otherorient + "\n");
//dump ("calendar-time-bar: orient: " + orient + " other: " + otherorient + "\n");
function makeTimeBox(timestr, size) {
var box = createXULElement("box");
@ -242,7 +242,7 @@
<xul:box anonid="bgbox" flex="1"/>
<xul:box anonid="topbox" flex="1" equalsize="always"/>
<xul:box anonid="fgbox" flex="1" class="fgdragcontainer">
<xul:box anonid="fgdragspacer" style="display: inherit;">
<xul:box anonid="fgdragspacer" style="display: inherit; overflow: hidden;">
<xul:spacer flex="1"/>
<xul:label anonid="fgdragbox-startlabel" class="fgdragbox-label"/>
</xul:box>
@ -408,7 +408,9 @@
<parameter name="aOccurrence"/>
<body><![CDATA[
for each (var chunk in this.mEvents) {
if (chunk.event.equals(aOccurrence)) {
if (chunk.event.id == aOccurrence.id &&
chunk.event.startDate.compare(aOccurrence.startDate) == 0)
{
return chunk;
}
}
@ -445,7 +447,9 @@
var i;
for (i = 0; i < this.mEvents.length; i++) {
occ = this.mEvents[i].event;
if (occ.equals(aOccurrence)) {
if (occ.id == aOccurrence.id &&
occ.startDate.compare(aOccurrence.startDate) == 0)
{
itemIndex = i;
break;
}
@ -478,8 +482,8 @@
<method name="getStartEndMinutesForOccurrence">
<parameter name="aOccurrence"/>
<body><![CDATA[
var stdate = aOccurrence.occurrenceStartDate;
var enddate = aOccurrence.occurrenceEndDate;
var stdate = aOccurrence.startDate;
var enddate = aOccurrence.endDate;
if (stdate.timezone != this.mTimezone)
stdate = stdate.getInTimezone (this.mTimezone);
@ -650,7 +654,7 @@
<method name="computeEventMap">
<body><![CDATA[
//dump ("computeEventMap\n");
//dump ("computeEventMap\n");
// we need to build a layout data structure
// that looks like this:
// [
@ -679,7 +683,7 @@
eventMap.push(new Array());
for each (var event in this.mEvents) {
//if (event.occurrenceStartDate.isDate)
//if (event.startDate.isDate)
// continue;
if (event.startMinute == null || event.endMinute == null || event.event == null)
@ -972,7 +976,7 @@
<parameter name="aMouseX"/>
<parameter name="aMouseY"/>
<body><![CDATA[
dump ("startSweepingToModify\n");
//dump ("startSweepingToModify\n");
this.mDragState = {
origColumn: this,
dragOccurrence: aOccurrence,
@ -982,7 +986,7 @@
var interval = this.mPixPerMin * 15;
var sizeattr;
dump ("AMY: " + aMouseY + " boY: " + this.parentNode.boxObject.screenY + "\n");
//dump ("AMY: " + aMouseY + " boY: " + this.parentNode.boxObject.screenY + "\n");
var frameloc;
if (this.getAttribute("orient") == "vertical") {
this.mDragState.origLoc = aMouseY;
@ -1050,7 +1054,7 @@
document.calendarEventColumnDragging = this;
//this.onEventSweepMouseMove(event);
dump (">>> drag is: " + this.mDragState.dragType + "\n");
//dump (">>> drag is: " + this.mDragState.dragType + "\n");
window.addEventListener("mousemove", this.onEventSweepMouseMove, false);
window.addEventListener("mouseup", this.onEventSweepMouseUp, false);
@ -1160,7 +1164,7 @@
<xul:box anonid="eventbox" xbl:inherits="orient,width,height" flex="1">
<xul:calendar-event-gripbar anonid="gripbar1" whichside="start" xbl:inherits="parentorient=orient"/>
<xul:vbox class="calendar-event-box-container" xbl:inherits="parentorient=orient" flex="1" align="left">
<xul:label anonid="event-name" flex="1" crop="right"/>
<xul:label anonid="event-name" flex="1" crop="right"/>
<!-- for some reason, textboxes suck ass for reflow. -->
<!-- <xul:textbox class="plain" style="background: transparent !important" anonid="event-name" flex="1" crop="right"/> -->
</xul:vbox>
@ -1224,10 +1228,15 @@
]]></getter>
<setter><![CDATA[
this.mOccurrence = val;
if (val && val.item)
this.eventNameLabel.setAttribute('value', val.item.title);
else
this.eventNameLabel.setAttribute('value', 'Untitled Event');
var evl = this.eventNameLabel;
while (evl.firstChild)
evl.removeChild(evl.firstChild);
if (val) {
evl.appendChild(document.createTextNode(val.title));
} else {
evl.appendChild(document.createTextNode("Untitled Event"));
}
return val;
]]></setter>
</property>
@ -1237,10 +1246,10 @@
onset="return (this.mParentColumn = val);"/>
<property name="startMinute" readonly="true"
onget="if (!this.mOccurrence) return 0; return this.mOccurrence.occurrenceStartDate.hour * 60 + this.mOccurrence.occurrenceStartDate.minute"/>
onget="if (!this.mOccurrence) return 0; return this.mOccurrence.startDate.hour * 60 + this.mOccurrence.startDate.minute"/>
<property name="endMinute" readonly="true"
onget="if (!this.mOccurrence) return 0; return this.mOccurrence.occurrenceEndDate.hour * 60 + this.mOccurrence.occurrenceEndDate.minute"/>
onget="if (!this.mOccurrence) return 0; return this.mOccurrence.endDate.hour * 60 + this.mOccurrence.endDate.minute"/>
</implementation>
<handlers>
@ -1248,11 +1257,6 @@
event.preventBubble();
this.calendarView.selectedOccurrence = this.mOccurrence;
if (this.mOccurrence.item.recurrenceInfo) {
// XXXvv can't drag recurring events yet, FIXME
return;
}
this.mInMouseDown = true;
this.mMouseX = event.screenX;
this.mMouseY = event.screenY;
@ -1357,7 +1361,7 @@
this.calView.refresh();
},
onAddItem: function (aItem) {
//dump ("++ AddItem\n");
//dump ("++ AddItem " + aItem + "\n");
if (!(aItem instanceof Components.interfaces.calIEvent))
return;
aItem = aItem.QueryInterface(Components.interfaces.calIEvent);
@ -1365,6 +1369,7 @@
var occs = aItem.getOccurrencesBetween(this.calView.startDate,
this.calView.endDate,
{});
//dump ("occs: " + occs.length + "\n");
for each (var occ in occs) {
this.calView.doAddEvent(occ);
}
@ -1591,7 +1596,11 @@
if (this.mSelectedOccurrence != val) {
if (this.mSelectedOccurrence) {
var col = this.findColumnForEvent(this.mSelectedOccurrence);
col.column.selectOccurrence(null);
if (col) {
col.column.selectOccurrence(null);
} else {
dump ("Thought I had a selected occurrence (id: " + this.mSelectedOccurrence.id + "), but couldn't find a column for it!\n");
}
}
if (val) {
@ -1675,7 +1684,7 @@
labelbox.setAttribute("height", 30);
labelbox.removeAttribute("width");
var timebarWidth = 50;
var timebarWidth = 100;
timebar.setAttribute("width", timebarWidth);
timebar.removeAttribute("height");
headertimespacer.setAttribute("width", timebarWidth);
@ -1689,7 +1698,7 @@
labelbox.setAttribute("width", 30);
labelbox.removeAttribute("height");
var timebarHeight = 30;
var timebarHeight = 40;
timebar.setAttribute("height", timebarHeight);
timebar.removeAttribute("width");
headertimespacer.setAttribute("height", timebarHeight);
@ -1833,8 +1842,8 @@
<method name="findColumnForEvent">
<parameter name="aEvent"/>
<body><![CDATA[
var estart = aEvent.occurrenceStartDate;
var eend = aEvent.occurrenceEndDate;
var estart = aEvent.startDate;
var eend = aEvent.endDate;
var eday = estart.clone();
eday.isDate = true;
@ -1884,7 +1893,7 @@
var column = col.column;
var header = col.header;
var estart = aEvent.occurrenceStartDate;
var estart = aEvent.startDate;
if (estart.isDate) {
// add it to header
} else {
@ -1903,7 +1912,7 @@
var column = col.column;
var header = col.header;
var estart = aEvent.occurrenceStartDate;
var estart = aEvent.startDate;
if (estart.isDate) {
// remove from header
} else {

View File

@ -143,7 +143,7 @@ function saveDialog()
var deckNumber = Number(getElementValue("period-list"));
var recurrenceInfo = createRecurrenceInfo();
recurrenceInfo.initialize(window.calendarEvent);
recurrenceInfo.item = window.calendarEvent;
var recRule = new calRecurrenceRule();

View File

@ -55,6 +55,7 @@ XPIDLSRCS = calIAlarmService.idl \
calICalendarView.idl \
calICalendarViewController.idl \
calIDateTime.idl \
calIErrors.idl \
calIEvent.idl \
calIICSService.idl \
calIItemBase.idl \

View File

@ -51,11 +51,6 @@
#define CAL_ICSSERVICE_CONTRACTID \
"@mozilla.org/calendar/ics-service;1"
#define CAL_RECURRENCEINFO_CID \
{ 0x04027036, 0x5884, 0x4a30, { 0xb4, 0xaf, 0xf2, 0xca, 0xd7, 0x9f, 0x6e, 0xdf } }
#define CAL_RECURRENCEINFO_CONTRACTID \
"@mozilla.org/calendar/recurrence-info;1"
#define CAL_RECURRENCERULE_CID \
{ 0xd9560bf9, 0x3065, 0x404a, { 0x90, 0x4c, 0xc8, 0x82, 0xfc, 0x9c, 0x9b, 0x74 } }
#define CAL_RECURRENCERULE_CONTRACTID \
@ -72,11 +67,6 @@
"@mozilla.org/calendar/recurrence-date;1"
/* JS -- Update these from calItemModule.js */
#define CAL_ITEM_OCCURRENCE_CID \
{ 0xbad672b3, 0x30b8, 0x4ecd, { 0x80, 0x75, 0x71, 0x53, 0x31, 0x3d, 0x1f, 0x2c } }
#define CAL_ITEM_OCCURRENCE_CONTRACTID \
"@mozilla.org/calendar/item-occurrence;1"
#define CAL_EVENT_CID \
{ 0x974339d5, 0xab86, 0x4491, { 0xaa, 0xaf, 0x2b, 0x2c, 0xa1, 0x77, 0xc1, 0x2b } }
#define CAL_EVENT_CONTRACTID \
@ -92,6 +82,11 @@
#define CAL_ATTENDEE_CONTRACTID \
"@mozilla.org/calendar/attendee;1"
#define CAL_RECURRENCEINFO_CID \
{ 0x04027036, 0x5884, 0x4a30, { 0xb4, 0xaf, 0xf2, 0xca, 0xd7, 0x9f, 0x6e, 0xdf } }
#define CAL_RECURRENCEINFO_CONTRACTID \
"@mozilla.org/calendar/recurrence-info;1"
#define NS_ERROR_CALENDAR_WRONG_COMPONENT_TYPE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_CALENDAR, 1)
// Until extensible xpconnect error mapping works
// #define NS_ERROR_CALENDAR_IMMUTABLE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_CALENDAR, 2)

View File

@ -42,9 +42,9 @@
interface calICalendar;
interface calIDateTime;
interface calICalendarViewController;
interface calIItemOccurrence;
interface calIItemBase;
[scriptable, uuid(be9b4f26-2475-4aeb-9b46-e165fc0809fb)]
[scriptable, uuid(448a7f1a-384c-4e47-b5b5-1c372b4ad3d1)]
interface calICalendarView : nsISupports
{
/**
@ -110,7 +110,7 @@ interface calICalendarView : nsISupports
void getDateList(out unsigned long aCount, [array,size_is(aCount),retval] out calIDateTime aDates);
/**
* Get or set the selected occurrence. Only one occurrence may be selected.
* Get or set the selected item. Only one item may be selected.
*/
attribute calIItemOccurrence selectedOccurrence;
attribute calIItemBase selectedItem;
};

View File

@ -47,7 +47,6 @@ interface nsIVariant;
interface nsIPropertyBag;
interface calICalendar;
interface calIItemOccurrence;
interface calIDateTime;
@ -63,7 +62,7 @@ interface calIIcalComponent;
// Base for Events, Todos, Journals, etc.
//
[scriptable, uuid(096b8cc5-113c-40fb-bc52-d64e3992980c)]
[scriptable, uuid(18521d75-346d-4616-8778-de2c4a9a676e)]
interface calIItemBase : nsISupports
{
// returns true if this thing is able to be modified;
@ -121,9 +120,9 @@ interface calIItemBase : nsISupports
attribute calIIcalComponent icalComponent;
//
// alarms
// alarms; alarmTime is null if there is no
// alarm set.
//
attribute boolean hasAlarm;
attribute calIDateTime alarmTime;
//
@ -137,10 +136,22 @@ interface calIItemBase : nsISupports
// if this item is mutable, the returned array will be a nsIMutableArray
readonly attribute nsIArray attachments;
//
// All event properties are stored in a property bag;
// some number of these are "promoted" to top-level
// accessor attributes. For example, "SUMMARY" is
// promoted to the top-level "title" attribute.
//
// other properties come in through a property bag
// The isPropertyPromoted() attribute can will indicate
// if a particular property is promoted or not, for
// serialization purposes.
//
// Note that if this item is a proxy, then any requests for
// non-existant properties will be forward to the parent item.
// some other properties that may exist:
//
// 'description' - description (string)
// 'location' - location (string)
// 'categories' - categories (string)
@ -149,21 +160,19 @@ interface calIItemBase : nsISupports
// alarmLength/alarmUnits/alarmEmailAddress/lastAlarmAck
// recurInterval/recurCount/recurWeekdays/recurWeeknumber
// Ideally, /all/ of the properties on the event should
// be available via the property bag. (And maybe the
// nsIItemBase impl should be QI'able to
// nsIWritablePropertyBag also)
// if this item is mutable, the returned bag will be a nsIWritablePropertyBag
//readonly attribute nsIPropertyBag properties;
// these forward to an internal property bag; implemented here, so we can
// do access control on set/delete.
// do access control on set/delete to have control over mutability.
readonly attribute nsISimpleEnumerator propertyEnumerator;
boolean hasProperty(in AString name);
nsIVariant getProperty(in AString name);
void setProperty(in AString name, in nsIVariant value);
// will not throw an error if you delete a property that doesn't exist
void deleteProperty(in AString name);
// returns true if the given property is promoted to some
// top-level attribute (e.g. id or title)
boolean isPropertyPromoted(in AString name);
/**
* The organizer (originator) of the item. We will likely not
* honour or preserve all fields in the calIAttendee passed around here.
@ -172,6 +181,10 @@ interface calIItemBase : nsISupports
*/
attribute calIAttendee organizer;
//
// Attendees
//
// The array returned here is not live; it will not reflect calls to
// removeAttendee/addAttendee that follow the call to getAttendees.
void getAttendees(out PRUint32 count,
@ -181,43 +194,66 @@ interface calIItemBase : nsISupports
void addAttendee(in calIAttendee attendee);
void removeAllAttendees();
// return a list of occurrences of this item between the given dates
//
// Occurrence querying
//
/**
* Return a list of occurrences of this item between the given dates. The items
* returned are the same type as this one, as proxies.
*/
void getOccurrencesBetween (in calIDateTime aStartDate, in calIDateTime aEndDate,
out PRUint32 aCount,
[array,size_is(aCount),retval] out calIItemOccurrence aOccurrences);
[array,size_is(aCount),retval] out calIItemBase aOccurrences);
/**
* The next occurrence after this one, if any.
*/
readonly attribute calIItemBase nextOccurrence;
/**
* The previous occurrence before this one, if any.
*/
readonly attribute calIItemBase previousOccurrence;
//
// proxy support
//
/**
* create a proxy for this item; the returned item
* proxy will have parentItem set to this instance.
*/
calIItemBase createProxy();
/**
* If this item is a proxy, parentItem will point upwards
* to our parent. Otherwise, it will point to this.
* parentItem can thus always be used for modifyItem() calls
* to providers.
*/
attribute calIItemBase parentItem;
/**
* The recurrence ID, a.k.a. DTSTART-of-calculated-occurrence,
* or null if this isn't an occurrence.
*/
attribute calIDateTime recurrenceId;
/**
* Returns the given property value if it is natively set on the item,
* otherwise returns null.
*/
nsIVariant getUnproxiedProperty(in AString name);
/**
* An enumerator that lets us walk down the list of properties
* that are actually changed by this proxy from the base. If this
* item is not a proxy, then this list will be equivalent to the one
* returned from propertyEnumerator.
*
* The elements returned by the enumerator will be of type nsIProperty,
* as with a normal nsIPropertyBag enumerator.
*/
readonly attribute nsISimpleEnumerator unproxiedPropertyEnumerator;
};
//
// calIItemOccurrence
//
// An item representing a specific instance of a possibly recurring item.
// XXX unclear how recurrence (& this) works for todo events with due dates.
//
[scriptable, uuid(b19f3d7e-e848-4139-af3e-505a8023568d)]
interface calIItemOccurrence : nsISupports
{
// Initialize this Occurrence
void initialize (in calIItemBase aItem,
in calIDateTime aStartDate,
in calIDateTime aEndDate);
//
// The parent item for which this is the occurrence item for
//
readonly attribute calIItemBase item;
//
// The start and end times to display for this event instance
//
readonly attribute calIDateTime occurrenceStartDate;
readonly attribute calIDateTime occurrenceEndDate;
// same as item.getNextOccurrence(occurrenceEndDate)
readonly attribute calIItemOccurrence next;
// same as item.getPreviousOccurrence(occurrenceStartDate)
readonly attribute calIItemOccurrence previous;
// is this occurrence equal to the other
boolean equals (in calIItemOccurrence aOther);
};

View File

@ -42,7 +42,6 @@
interface calIItemBase;
interface calIDateTime;
interface calIItemOccurrence;
interface calIIcalProperty;

View File

@ -42,7 +42,6 @@
interface calIItemBase;
interface calIDateTime;
interface calIItemOccurrence;
interface calIIcalProperty;

View File

@ -40,13 +40,12 @@
interface calIItemBase;
interface calIDateTime;
interface calIItemOccurrence;
interface calIRecurrenceItem;
interface calIIcalProperty;
[scriptable, uuid(a6a458cf-052c-45d1-bee7-b700ad21109a)]
[scriptable, uuid(8ca5db89-2583-4f0c-b845-4a6d2f229efd)]
interface calIRecurrenceInfo : nsISupports
{
// returns true if this thing is able to be modified;
@ -62,12 +61,11 @@ interface calIRecurrenceInfo : nsISupports
// initialize this with the item for which this recurrence
// applies, so that the start date can be tracked
void initialize (in calIItemBase aItem);
readonly attribute calIItemBase item;
attribute calIItemBase item;
//
// set of recurrence items; the order of these matters.
//
/*
* Set of recurrence items; the order of these matters.
*/
void getRecurrenceItems (out unsigned long aCount, [array,size_is(aCount),retval] out calIRecurrenceItem aItems);
void setRecurrenceItems (in unsigned long aCount, [array,size_is(aCount)] in calIRecurrenceItem aItems);
@ -81,18 +79,90 @@ interface calIRecurrenceInfo : nsISupports
// inserts the item at the given index, pushing the item that was previously there forward
void insertRecurrenceItemAt (in calIRecurrenceItem aItem, in unsigned long aIndex);
//
// recurrence calculation
//
/**
* isFinite is true if the recurrence items specify a finite number
* of occurrences. This is useful for UI and for possibly other users.
*/
readonly attribute boolean isFinite;
// return the next display item for the event,
// where the start time is > aStartTime
calIItemOccurrence getNextOccurrence (in calIDateTime aOccurrenceTime);
/**
* This is a shortcut to appending or removing a single negative
* date assertion. This shortcut may or may not cause problems
* later on, but hey, that's fixable later!
*/
void removeOccurrenceAt (in calIDateTime aRecurrenceId);
void restoreOccurrenceAt (in calIDateTime aRecurrenceId);
// return array of calIItemOccurrence representing all
// occurrences of this event between start (inclusive) and end (non-inclusive).
/*
* exceptions
*/
/**
* Modify an a particular occurrence with the given exception proxy
* item. If the recurrenceId isn't an already existing exception item,
* a new exception is added. Otherwise, the existing exception
* is modified.
*
* The item's parentItem must be equal to this RecurrenceInfo's
* item. <-- XXX check this, compare by calendar/id only
*/
void modifyException (in calIItemBase anItem);
/**
* Return an existing exception item for the given recurrence ID.
* If an exception does not exist, and aCreate is set, a new one
* is created and returned. Otherwise, null is returned.
*/
calIItemBase getExceptionFor (in calIDateTime aRecurrenceId, in boolean aCreate);
/**
* Removes an exception item for the given recurrence ID, if
* any exist.
*/
void removeExceptionFor (in calIDateTime aRecurrenceId);
/**
* Returns a list of all recurrence ids that have exceptions.
*/
void getExceptionIds (out unsigned long aCount, [array,size_is(aCount),retval] out calIDateTime aIds);
/*
* recurrence calculation
*/
/*
* Get the occurrence at the given recurrence ID; if there is no
* exception, then create a new proxy object with the normal occurrence.
* Otherwise, return the exception.
*/
calIItemBase getOccurrenceFor (in calIDateTime aRecurrenceId);
/**
* Return the next start calIDateTime of the recurrence specified by
* this RecurrenceInfo, after aOccurrenceTime.
*/
calIDateTime getNextOccurrenceDate (in calIDateTime aOccurrenceTime);
/**
* Return the next item specified by this RecurrenceInfo, after aOccurrenceTime.
*/
calIItemBase getNextOccurrence (in calIDateTime aOccurrenceTime);
/**
* Return an array of calIDateTime representing all start times of this event
* between start (inclusive) and end (non-inclusive).
*/
void getOccurrenceDates (in calIDateTime aRangeStart,
in calIDateTime aRangeEnd,
in unsigned long aMaxCount,
out unsigned long aCount, [array,size_is(aCount),retval] out calIDateTime aDates);
/**
* Return an array of calIItemOccurrence representing all
* occurrences of this event between start (inclusive) and end (non-inclusive).
*/
void getOccurrences (in calIDateTime aRangeStart,
in calIDateTime aRangeEnd,
in unsigned long aMaxCount,
out unsigned long aCount, [array,size_is(aCount),retval] out calIItemOccurrence aItems);
out unsigned long aCount, [array,size_is(aCount),retval] out calIItemBase aItems);
};

View File

@ -44,7 +44,7 @@ interface calIItemOccurrence;
interface calIIcalProperty;
[scriptable, uuid(d438b44a-9d6e-4ebb-bb03-321c9b81b216)]
[scriptable, uuid(943be334-4995-477e-b325-f0c2319183e8)]
interface calIRecurrenceItem : nsISupports
{
// returns true if this thing is able to be modified;
@ -62,6 +62,10 @@ interface calIRecurrenceItem : nsISupports
// as a negative rule (e.g. exceptions instead of rdates)
attribute boolean isNegative;
// returns whether this item has a finite number of dates
// or not (e.g. a rule with no end date)
readonly attribute boolean isFinite;
// return the next start time after aOccurrencetime for this
// recurrence, starting at aStartTime.
calIDateTime getNextOccurrence (in calIDateTime aStartTime,

View File

@ -42,7 +42,6 @@
interface calIItemBase;
interface calIDateTime;
interface calIItemOccurrence;
// an interface implementing a RRULE

View File

@ -61,9 +61,13 @@ REQUIRES = xpcom \
# sqlite3 \
# $(NULL)
XPIDL_MODULE = calbaseinternal
XPIDLSRCS = \
calInternalInterfaces.idl \
$(NULL)
CPPSRCS = calDateTime.cpp \
calICSService.cpp \
calRecurrenceInfo.cpp \
calRecurrenceRule.cpp \
calRecurrenceDate.cpp \
calRecurrenceDateSet.cpp \
@ -75,6 +79,7 @@ EXTRA_COMPONENTS = \
calAttachment.js \
calAttendee.js \
calCalendarManager.js \
calRecurrenceInfo.js \
calEvent.js \
calItemBase.js \
calItemModule.js \

View File

@ -80,13 +80,13 @@ function calAlarmService() {
onEndBatch: function() { },
onLoad: function() { },
onAddItem: function(aItem) {
if (aItem.hasAlarm)
if (aItem.alarmTime)
this.alarmService.addAlarm(aItem, false);
},
onModifyItem: function(aNewItem, aOldItem) {
this.alarmService.removeAlarm(aOldItem);
if (aNewItem.hasAlarm)
if (aNewItem.alarmTime)
this.alarmService.addAlarm(aNewItem, false);
},
onDeleteItem: function(aDeletedItem) {
@ -112,12 +112,13 @@ function calAlarmService() {
};
}
calAlarmServiceClassInfo = {
var calAlarmServiceClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIAlarmService,
Components.interfaces.nsIObserver
Components.interfaces.nsIObserver,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;
@ -334,7 +335,7 @@ calAlarmService.prototype = {
onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
for (var i = 0; i < aCount; ++i) {
var item = aItems[i];
if (item.hasAlarm) {
if (item.alarmTime) {
this.alarmService.addAlarm(item, false);
}
}

View File

@ -40,11 +40,12 @@
//
function calAttachment() { }
calAttachmentClassInfo = {
var calAttachmentClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIAttachment
Components.interfaces.calIAttachment,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;

View File

@ -42,11 +42,12 @@ function calAttendee() {
createInstance(Components.interfaces.nsIWritablePropertyBag);
}
calAttendeeClassInfo = {
var calAttendeeClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIAttendee
Components.interfaces.calIAttendee,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;

View File

@ -66,11 +66,12 @@ function makeURI(uriString)
return ioservice.newURI(uriString, null, null);
}
calCalendarManagerClassInfo = {
var calCalendarManagerClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calICalendarManager
Components.interfaces.calICalendarManager,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;

View File

@ -54,7 +54,7 @@ extern "C" {
static NS_DEFINE_CID(kCalICSService, CAL_ICSSERVICE_CID);
NS_IMPL_ISUPPORTS2(calDateTime, calIDateTime, nsIXPCScriptable)
NS_IMPL_ISUPPORTS2_CI(calDateTime, calIDateTime, nsIXPCScriptable)
calDateTime::calDateTime()
: mImmutable(PR_FALSE),
@ -107,7 +107,7 @@ NS_IMETHODIMP
calDateTime::MakeImmutable()
{
if (mImmutable)
return NS_ERROR_CALENDAR_IMMUTABLE;
return NS_ERROR_OBJECT_IS_IMMUTABLE;
mImmutable = PR_TRUE;
return NS_OK;
@ -243,7 +243,7 @@ calDateTime::AddDuration(calIDateTime *aDuration)
mLastModified = PR_Now();
return SetTimeInTimezone(mNativeTime + nativeDur, mTimezone);
return SetNativeTime(mNativeTime + nativeDur);
}
NS_IMETHODIMP

View File

@ -48,17 +48,26 @@ function calEvent() {
this.wrappedJSObject = this;
this.initItemBase();
this.initEvent();
this.eventPromotedProps = {
"DTSTART": true,
"DTEND": true,
"DTSTAMP": true,
__proto__: this.itemBasePromotedProps
}
}
// var trickery to suppress lib-as-component errors from loader
var calItemBase;
calEventClassInfo = {
var calEventClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIItemBase,
Components.interfaces.calIEvent
Components.interfaces.calIEvent,
Components.interfaces.calIInternalShallowCopy,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;
@ -79,7 +88,8 @@ calEvent.prototype = {
__proto__: calItemBase ? (new calItemBase()) : {},
QueryInterface: function (aIID) {
if (aIID.equals(Components.interfaces.calIEvent))
if (aIID.equals(Components.interfaces.calIEvent) ||
aIID.equals(Components.interfaces.calIInternalShallowCopy))
return this;
if (aIID.equals(Components.interfaces.nsIClassInfo))
@ -88,36 +98,55 @@ calEvent.prototype = {
return this.__proto__.__proto__.QueryInterface.call(this, aIID);
},
clone: function () {
cloneShallow: function (aNewParent) {
var m = new calEvent();
this.cloneItemBaseInto(m);
m.mStartDate = this.mStartDate.clone();
m.mEndDate = this.mEndDate.clone();
m.isAllDay = this.isAllDay;
this.cloneItemBaseInto(m, aNewParent);
return m;
},
clone: function () {
var m;
if (this.mParentItem) {
var clonedParent = this.mParentItem.clone();
m = clonedParent.recurrenceInfo.getOccurrenceFor (this.recurrenceId);
} else {
m = this.cloneShallow(null);
}
return m;
},
createProxy: function () {
if (this.mIsProxy) {
calDebug("Tried to create a proxy for an existing proxy!\n");
throw Components.results.NS_ERROR_UNEXPECTED;
}
var m = new calEvent();
m.initializeProxy(this);
return m;
},
makeImmutable: function () {
this.mStartDate.makeImmutable();
this.mEndDate.makeImmutable();
this.makeItemBaseImmutable();
},
initEvent: function() {
this.mStartDate = new CalDateTime();
this.mEndDate = new CalDateTime();
this.startDate = new CalDateTime();
this.endDate = new CalDateTime();
},
get duration() {
var dur = new CalDateTime();
dur.setTimeInTimezone (this.mEndDate.nativeTime - this.mStartDate.nativeTime, "floating");
dur.setTimeInTimezone (this.endDate.nativeTime - this.startDate.nativeTime, "floating");
return dur;
},
get recurrenceStartDate() {
return this.mStartDate;
return this.startDate;
},
icsEventPropMap: [
@ -163,6 +192,8 @@ calEvent.prototype = {
return icalcomp;
},
eventPromotedProps: null,
set icalComponent(event) {
this.modify();
if (event.componentType != "VEVENT") {
@ -173,19 +204,17 @@ calEvent.prototype = {
this.setItemBaseFromICS(event);
this.mapPropsFromICS(event, this.icsEventPropMap);
this.mIsAllDay = this.mStartDate && this.mStartDate.isDate;
this.mIsAllDay = this.startDate && this.startDate.isDate;
var promotedProps = {
"DTSTART": true,
"DTEND": true,
"DTSTAMP": true,
__proto__: this.itemBasePromotedProps
};
this.importUnpromotedProperties(event, promotedProps);
this.importUnpromotedProperties(event, eventPromotedProps);
// Importing didn't really change anything
this.mDirty = false;
},
isPropertyPromoted: function (name) {
return (this.eventPromotedProps[name]);
},
getOccurrencesBetween: function(aStartDate, aEndDate, aCount) {
if (this.recurrenceInfo) {
return this.recurrenceInfo.getOccurrences(aStartDate, aEndDate, 0, aCount);
@ -194,10 +223,8 @@ calEvent.prototype = {
if ((this.startDate.compare(aStartDate) >= 0 && this.startDate.compare(aEndDate) <= 0) ||
(this.endDate.compare(aStartDate) >= 0 && this.endDate.compare(aEndDate) <= 0))
{
var occ = Components.classes["@mozilla.org/calendar/item-occurrence;1"].createInstance(Components.interfaces.calIItemOccurrence);
occ.initialize(this, this.startDate, this.endDate);
aCount.value = 1;
return ([ occ ]);
return ([ this ]);
}
aCount.value = 0;
@ -209,7 +236,8 @@ calEvent.prototype = {
var makeMemberAttr;
if (makeMemberAttr) {
makeMemberAttr(calEvent, "mStartDate", null, "startDate");
makeMemberAttr(calEvent, "mEndDate", null, "endDate");
makeMemberAttr(calEvent, "DTSTART", null, "startDate", true);
makeMemberAttr(calEvent, "DTEND", null, "endDate", true);
// XXX get rid of this
makeMemberAttr(calEvent, "mIsAllDay", false, "isAllDay");
}

View File

@ -948,7 +948,7 @@ calIcalComponent::RemoveProperty(calIIcalProperty *prop)
return NS_OK;
}
NS_IMPL_ISUPPORTS1(calICSService, calIICSService)
NS_IMPL_ISUPPORTS1_CI(calICSService, calIICSService)
calICSService::calICSService()
{

View File

@ -42,10 +42,23 @@
//
const ICAL = Components.interfaces.calIIcalComponent;
const kHashPropertyBagContractID = "@mozilla.org/hash-property-bag;1";
const kIWritablePropertyBag = Components.interfaces.nsIWritablePropertyBag;
const HashPropertyBag = new Components.Constructor(kHashPropertyBagContractID, kIWritablePropertyBag);
function calItemBase() { }
function NewCalDateTime(aJSDate) {
var c = new CalDateTime();
if (aJSDate)
c.jsDate = c;
return c;
}
function calItemBase() {
}
calItemBase.prototype = {
mIsProxy: false,
QueryInterface: function (aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIItemBase))
@ -56,21 +69,63 @@ calItemBase.prototype = {
return this;
},
mParentItem: null,
get parentItem() {
if (this.mParentItem)
return this.mParentItem;
else
return this;
},
set parentItem(value) {
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
this.mIsProxy = true;
this.mParentItem = value;
},
initializeProxy: function (aParentItem) {
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
if (this.mParentItem != null)
throw Components.results.NS_ERROR_FAILURE;
this.mParentItem = aParentItem;
this.mCalendar = aParentItem.mCalendar;
this.mIsProxy = true;
},
//
// calIItemBase
//
mImmutable: false,
get isMutable() { return !this.mImmutable; },
mDirty: false,
modify: function() {
if (this.mImmutable)
// Components.results.NS_ERROR_CALENDAR_IMMUTABLE;
throw Components.results.NS_ERROR_FAILURE;
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
this.mDirty = true;
},
makeItemBaseImmutable: function() {
// make all our components immutable
this.mCreationDate.makeImmutable();
ensureNotDirty: function() {
if (!this.mDirty)
return;
if (this.mImmutable) {
dump ("### Something tried to undirty a dirty immutable event!\n");
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
}
this.setProperty("LAST-MODIFIED", NewCalDateTime(new Date()));
this.mDirty = false;
},
makeItemBaseImmutable: function() {
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
// make all our components immutable
if (this.mRecurrenceInfo)
this.mRecurrenceInfo.makeImmutable();
if (this.mAlarmTime)
@ -80,25 +135,31 @@ calItemBase.prototype = {
this.mOrganizer.makeImmutable();
for (var i = 0; i < this.mAttendees.length; i++)
this.mAttendees[i].makeImmutable();
var e = this.mProperties.enumerator;
while (e.hasMoreElements()) {
var prop = e.getNext().QueryInterface(Components.interfaces.nsIProperty);
var val = prop.value;
if (prop.value instanceof Components.interfaces.calIDateTime) {
if (prop.value.isMutable)
prop.value.makeImmutable();
}
}
this.ensureNotDirty();
this.mImmutable = true;
},
// initialize this class's members
initItemBase: function () {
this.mCreationDate = new CalDateTime();
this.mAlarmTime = new CalDateTime();
this.mLastModifiedTime = new CalDateTime();
this.mStampTime = new CalDateTime();
var now = new Date();
this.mCreationDate.jsDate = new Date();
this.mLastModifiedTime.jsDate = new Date();
this.mStampTime.jsDate = new Date();
this.mProperties = Components.classes["@mozilla.org/hash-property-bag;1"].
createInstance(Components.interfaces.nsIWritablePropertyBag);
this.mProperties = new HashPropertyBag();
this.mAttachments = Components.classes["@mozilla.org/array;1"].
createInstance(Components.interfaces.nsIArray);
this.setProperty("CREATED", NewCalDateTime(now));
this.setProperty("LAST-MODIFIED", NewCalDateTime(now));
this.setProperty("DTSTAMP", NewCalDateTime(now));
this.mAttendees = [];
@ -108,82 +169,173 @@ calItemBase.prototype = {
},
// for subclasses to use; copies the ItemBase's values
// into m
cloneItemBaseInto: function (m) {
var suppressDCE = this.lastModifiedTime;
suppressDCE = this.stampTime;
// into m. aNewParent is optional
cloneItemBaseInto: function (m, aNewParent) {
this.updateStampTime();
this.ensureNotDirty();
m.mImmutable = false;
m.mGeneration = this.mGeneration;
m.mLastModifiedTime = this.mLastModifiedTime.clone();
m.mCalendar = this.mCalendar;
m.mId = this.mId;
m.mTitle = this.mTitle;
m.mPriority = this.mPriority;
m.mPrivacy = this.mPrivacy;
m.mStatus = this.mStatus;
m.mHasAlarm = this.mHasAlarm;
m.mIsProxy = this.mIsProxy;
m.mParentItem = aNewParent || this.mParentItem;
m.mCreationDate = this.mCreationDate.clone();
m.mStampTime = this.mStampTime.clone();
m.mCalendar = this.mCalendar;
if (this.mRecurrenceInfo) {
m.mRecurrenceInfo = this.mRecurrenceInfo.clone();
dump ("old recurType: " + this.mRecurrenceInfo.recurType + " new type: " + m.mRecurrenceInfo.recurType + "\n");
m.mRecurrenceInfo.item = m;
}
if (this.mAlarmTime)
m.mAlarmTime = this.mAlarmTime.clone();
m.mAttendees = [];
for (var i = 0; i < this.mAttendees.length; i++)
m.mAttendees[i] = this.mAttendees[i].clone();
// these need fixing
m.mAttachments = this.mAttachments;
m.mProperties = Components.classes["@mozilla.org/hash-property-bag;1"].
createInstance(Components.interfaces.nsIWritablePropertyBag);
var e = this.mProperties.enumerator;
while (e.hasMoreElements()) {
var prop = e.getNext().QueryInterface(Components.interfaces.nsIProperty);
m.mProperties.setProperty (prop.name, prop.value);
var val = prop.value;
if (prop.value instanceof Components.interfaces.calIDateTime)
val = prop.value.clone();
m.mProperties.setProperty (prop.name, val);
}
m.mDirty = this.mDirty;
m.mDirty = false;
// these need fixing
m.mAttachments = this.mAttachments;
return m;
},
get lastModifiedTime() {
if (this.mDirty) {
this.mLastModifiedTime.jsDate = new Date();
this.mDirty = false;
}
return this.mLastModifiedTime;
this.ensureNotDirty();
return this.getProperty("LAST-MODIFIED");
},
mStampTime: null,
get stampTime() {
if (this.mStampTime.isValid)
return this.mStampTime;
return this.mLastModifiedTime;
var prop = this.getProperty("DTSTAMP");
if (prop && prop.isValid)
return prop;
return this.getProperty("LAST-MODIFIED");
},
updateStampTime: function() {
// can't update the stamp time on an immutable event
if (this.mImmutable)
return;
this.modify();
this.mStampTime.jsDate = new Date();
this.setProperty("DTSTAMP", NewCalDateTime(new Date()));
},
get propertyEnumerator() { return this.mProperties.enumerator; },
get unproxiedPropertyEnumerator() {
return this.mProperties.enumerator;
},
get propertyEnumerator() {
if (this.mIsProxy) {
// nsISimpleEnumerator sucks. It really, really sucks.
// The interface is badly defined, it's not clear
// what happens if you just keep calling getNext() without
// calling hasMoreElements in between, which seems like more
// of an informational thing. An interface with
// "advance()" which returns true or false, and with "item()",
// which returns the item the enumerator is pointing to, makes
// far more sense. Right now we have getNext() doing both
// item returning and enumerator advancing, which makes
// no sense.
return {
firstEnumerator: this.mProperties.eumerator,
secondEnumerator: this.mParentItem.propertyEnumerator,
handledProperties: { },
currentItem: null,
QueryInterface: function(aIID) {
if (!aIID.equals(Components.interfaces.nsISimpleEnumerator) ||
!aIID.equals(Components.interfaces.nsISupports))
{
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
},
hasMoreElements: function() {
if (!this.secondEnumerator)
return false;
if (this.firstEnumerator) {
var moreFirst = this.firstEnumerator.hasMoreElements();
if (moreFirst) {
this.currentItem = this.firstEnumerator.getNext();
this.handledProperties[this.currentItem.name] = true;
return true;
}
this.firstEnumerator = null;
}
var moreSecond = this.secondEnumerator.hasMoreElements();
if (moreSecond) {
while (this.currentItem.name in this.handledProperties &&
this.secondEnumerator.hasMoreElements())
do {
this.currentItem = this.secondEnumerator.getNext();
} while (this.currentItem.name in this.handledProperties &&
((this.currentItem = null) == null) && // hack
this.secondEnumerator.hasMoreElements());
if (!this.currentItem)
return false;
return true;
}
this.secondEnumerator = null;
return false;
},
getNext: function() {
if (!currentItem)
throw Components.results.NS_ERROR_UNEXPECTED;
var rval = this.currentItem;
this.currentItem = null;
return rval;
}
};
} else {
return this.mProperties.enumerator;
}
},
getProperty: function (aName) {
try {
return this.mProperties.getProperty(aName);
} catch (e) {
try {
if (this.mIsProxy) {
return this.mParentItem.getProperty(aName);
}
} catch (e) {}
return null;
}
},
getUnproxiedProperty: function (aName) {
try {
return this.mProperties.getProperty(aName);
} catch (e) { }
return null;
},
hasProperty: function (aName) {
return (this.getProperty(aName) != null);
},
setProperty: function (aName, aValue) {
this.modify();
this.mProperties.setProperty(aName, aValue);
@ -193,8 +345,7 @@ calItemBase.prototype = {
this.modify();
try {
this.mProperties.deleteProperty(aName);
} catch (e) {
}
} catch (e) { }
},
getAttendees: function (countObj) {
@ -240,8 +391,7 @@ calItemBase.prototype = {
set calendar (v) {
if (this.mImmutable)
// Components.results.NS_ERROR_CALENDAR_IMMUTABLE;
throw Components.results.NS_ERROR_FAILURE;
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
this.mCalendar = v;
},
@ -280,38 +430,37 @@ calItemBase.prototype = {
"RDATE": true,
"ATTENDEE": true,
"ORGANIZER": true,
"RECURRENCE-ID": true,
},
icsBasePropMap: [
{ cal: "CREATED", ics: "createdTime" },
{ cal: "LAST-MODIFIED", ics: "lastModified" },
{ cal: "DTSTAMP", ics: "stampTime" },
{ cal: "UID", ics: "uid" },
{ cal: "SUMMARY", ics: "summary" },
{ cal: "PRIORITY", ics: "priority" },
{ cal: "STATUS", ics: "status" },
{ cal: "CLASS", ics: "icalClass" } ],
mapPropsFromICS: function(icalcomp, propmap) {
for (var i = 0; i < propmap.length; i++) {
var prop = propmap[i];
var val = icalcomp[prop.ics];
if (val != null && val != ICAL.INVALID_VALUE)
this[prop.cal] = val;
this.setProperty(prop.cal, val);
}
},
mapPropsToICS: function(icalcomp, propmap) {
for (var i = 0; i < propmap.length; i++) {
var prop = propmap[i];
if (!(prop.cal in this))
continue;
var val = this[prop.cal];
var val = this.getProperty(prop.cal);
if (val != null && val != ICAL.INVALID_VALUE)
icalcomp[prop.ics] = val;
}
},
icsBasePropMap: [
{ cal: "mCreationDate", ics: "createdTime" },
{ cal: "mLastModifiedTime", ics: "lastModified" },
{ cal: "mStampTime", ics: "stampTime" },
{ cal: "mId", ics: "uid" },
{ cal: "mTitle", ics: "summary" },
{ cal: "mPriority", ics: "priority" },
{ cal: "mStatus", ics: "status" },
{ cal: "mPrivacy", ics: "icalClass" }],
setItemBaseFromICS: function (icalcomp) {
this.modify();
@ -360,7 +509,7 @@ calItemBase.prototype = {
if (!rec) {
rec = new CalRecurrenceInfo();
rec.initialize(this);
rec.item = this;
}
rec.appendRecurrenceItem(ritem);
@ -380,14 +529,19 @@ calItemBase.prototype = {
}
},
isPropertyPromoted: function (name) {
return (this.itemBasePromotedProps[name]);
},
get icalComponent() {
throw Components.results.NS_NOT_IMPLEMENTED;
},
fillIcalComponentFromBase: function (icalcomp) {
// Make sure that the LMT and ST are updated
var suppressDCE = this.lastModifiedTime;
suppressDCE = this.stampTime;
this.updateStampTime();
this.ensureNotDirty();
this.mapPropsToICS(icalcomp, this.icsBasePropMap);
if (this.mOrganizer)
@ -415,107 +569,35 @@ calItemBase.prototype = {
}
};
function calItemOccurrence () {
this.wrappedJSObject = this;
makeMemberAttr(calItemBase, "X-MOZILLA-GENERATION", 0, "generation", true);
makeMemberAttr(calItemBase, "CREATED", null, "creationDate", true);
makeMemberAttr(calItemBase, "UID", null, "id", true);
makeMemberAttr(calItemBase, "SUMMARY", null, "title", true);
makeMemberAttr(calItemBase, "PRIORITY", 0, "priority", true);
makeMemberAttr(calItemBase, "CLASS", "PUBLIC", "privacy", true);
makeMemberAttr(calItemBase, "STATUS", null, "status", true);
makeMemberAttr(calItemBase, "ALARMTIME", null, "alarmTime", true);
makeMemberAttr(calItemBase, "RECURRENCE-ID", null, "recurrenceId", true);
this.occurrenceStartDate = new CalDateTime();
this.occurrenceEndDate = new CalDateTime();
}
calItemOccurrenceClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIItemOccurrence
];
count.value = ifaces.length;
return ifaces;
},
getHelperForLanguage: function (language) {
return null;
},
contractID: "@mozilla.org/calendar/item-occurrence;1",
classDescription: "Calendar Item Occurrence",
classID: Components.ID("{bad672b3-30b8-4ecd-8075-7153313d1f2c}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0
};
calItemOccurrence.prototype = {
QueryInterface: function (aIID) {
if (aIID.equals(Components.interfaces.nsIClassInfo))
return calItemOccurrenceClassInfo;
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIItemOccurrence))
{
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
},
initialize: function (aItem, aStartDate, aEndDate) {
this.item = aItem;
this.occurrenceStartDate = aStartDate.clone();
this.occurrenceStartDate.makeImmutable();
this.occurrenceEndDate = aEndDate.clone();
this.occurrenceEndDate.makeImmutable();
},
item: null,
occurrenceStartDate: null,
occurrenceEndDate: null,
get next() {
if (this.item.recurrenceInfo)
return this.item.recurrenceInfo.getNextOccurrence(this.item, aEndDate);
return null;
},
get previous() {
if (this.item.recurrenceInfo)
return this.item.recurrenceInfo.getPreviousOccurrence(this.item, aStartDate);
return null;
},
equals: function(aOther) {
if (this.item.id != aOther.item.id)
return false;
if (this.occurrenceStartDate.compare(aOther.occurrenceStartDate) != 0)
return false;
if (this.occurrenceEndDate.compare(aOther.occurrenceEndDate) != 0)
return false;
return true;
}
};
makeMemberAttr(calItemBase, "mGeneration", 0, "generation");
makeMemberAttr(calItemBase, "mCreationDate", null, "creationDate");
makeMemberAttr(calItemBase, "mId", null, "id");
makeMemberAttr(calItemBase, "mTitle", null, "title");
makeMemberAttr(calItemBase, "mPriority", 0, "priority");
makeMemberAttr(calItemBase, "mPrivacy", "PUBLIC", "privacy");
makeMemberAttr(calItemBase, "mStatus", null, "status");
makeMemberAttr(calItemBase, "mHasAlarm", false, "hasAlarm");
makeMemberAttr(calItemBase, "mAlarmTime", null, "alarmTime");
makeMemberAttr(calItemBase, "mRecurrenceInfo", null, "recurrenceInfo");
makeMemberAttr(calItemBase, "mAttachments", null, "attachments");
makeMemberAttr(calItemBase, "mProperties", null, "properties");
function makeMemberAttr(ctor, varname, dflt, attr)
function makeMemberAttr(ctor, varname, dflt, attr, asProperty)
{
ctor.prototype[varname] = dflt;
// XXX handle defaults!
var getter = function () {
return this[varname];
if (asProperty)
return this.getProperty(varname);
else
return this[varname];
};
var setter = function (v) {
this.modify();
this[varname] = v;
if (asProperty)
this.setProperty(varname, v);
else
this[varname] = v;
};
ctor.prototype.__defineGetter__(attr, getter);
ctor.prototype.__defineSetter__(attr, setter);

View File

@ -105,11 +105,6 @@ const componentData =
script: "calTodo.js",
constructor: "calTodo"},
{cid: Components.ID("{bad672b3-30b8-4ecd-8075-7153313d1f2c}"),
contractid: "@mozilla.org/calendar/item-occurrence;1",
script: null,
constructor: "calItemOccurrence"},
{cid: Components.ID("{5c8dcaa3-170c-4a73-8142-d531156f664d}"),
contractid: "@mozilla.org/calendar/attendee;1",
script: "calAttendee.js",
@ -118,7 +113,12 @@ const componentData =
{cid: Components.ID("{5f76b352-ab75-4c2b-82c9-9206dbbf8571}"),
contractid: "@mozilla.org/calendar/attachment;1",
script: "calAttachment.js",
constructor: "calAttachment"}
constructor: "calAttachment"},
{cid: Components.ID("{04027036-5884-4a30-b4af-f2cad79f6edf}"),
contractid: "@mozilla.org/calendar/recurrence-info;1",
script: "calRecurrenceInfo.js",
constructor: "calRecurrenceInfo"}
];
var calItemModule = {
@ -151,8 +151,13 @@ var calItemModule = {
var f = appdir.clone();
f.append(scriptName);
var fileurl = iosvc.newFileURI(f);
loader.loadSubScript(fileurl.spec, null);
try {
var fileurl = iosvc.newFileURI(f);
loader.loadSubScript(fileurl.spec, null);
} catch (e) {
dump("Error while loading " + fileurl.spec + "\n");
throw e;
}
}
this.mScriptsLoaded = true;

View File

@ -48,7 +48,7 @@ extern "C" {
#include "ical.h"
}
NS_IMPL_ISUPPORTS2(calRecurrenceDate, calIRecurrenceItem, calIRecurrenceDate)
NS_IMPL_ISUPPORTS2_CI(calRecurrenceDate, calIRecurrenceItem, calIRecurrenceDate)
calRecurrenceDate::calRecurrenceDate()
: mImmutable(PR_FALSE),
@ -112,6 +112,15 @@ calRecurrenceDate::SetIsNegative(PRBool aIsNegative)
return NS_OK;
}
/* readonly attribute boolean isFinite; */
NS_IMETHODIMP
calRecurrenceDate::GetIsFinite(PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceDate::GetDate(calIDateTime **aDate)
{

View File

@ -48,7 +48,7 @@ extern "C" {
#include "ical.h"
}
NS_IMPL_ISUPPORTS2(calRecurrenceDateSet, calIRecurrenceItem, calIRecurrenceDateSet)
NS_IMPL_ISUPPORTS2_CI(calRecurrenceDateSet, calIRecurrenceItem, calIRecurrenceDateSet)
calRecurrenceDateSet::calRecurrenceDateSet()
: mImmutable(PR_FALSE),
@ -121,6 +121,15 @@ calRecurrenceDateSet::SetIsNegative(PRBool aIsNegative)
return NS_OK;
}
/* readonly attribute boolean isFinite; */
NS_IMETHODIMP
calRecurrenceDateSet::GetIsFinite(PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceDateSet::GetDates(PRUint32 *aCount, calIDateTime ***aDates)
{

View File

@ -1,398 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Oracle Corporation code.
*
* The Initial Developer of the Original Code is
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "calBaseCID.h"
#include "calRecurrenceInfo.h"
#include "calDateTime.h"
#include "calIItemBase.h"
#include "calIEvent.h"
#include "calICSService.h"
#include "nsCOMArray.h"
NS_IMPL_ISUPPORTS1(calRecurrenceInfo, calIRecurrenceInfo)
calRecurrenceInfo::calRecurrenceInfo()
: mImmutable(PR_FALSE)
{
}
NS_IMETHODIMP
calRecurrenceInfo::GetIsMutable(PRBool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = !mImmutable;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::MakeImmutable()
{
if (mImmutable)
return NS_ERROR_FAILURE; // XXX another error code
mImmutable = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::Clone(calIRecurrenceInfo **aResult)
{
nsresult rv;
calRecurrenceInfo *cri = new calRecurrenceInfo;
if (!cri)
return NS_ERROR_OUT_OF_MEMORY;
cri->mBaseItem = mBaseItem;
for (int i = 0; i < mRecurrenceItems.Count(); i++) {
nsCOMPtr<calIRecurrenceItem> item;
rv = mRecurrenceItems[i]->Clone(getter_AddRefs(item));
NS_ENSURE_SUCCESS(rv, rv);
cri->mRecurrenceItems.AppendObject(item);
}
NS_ADDREF(*aResult = cri);
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::Initialize(calIItemBase *aItem)
{
// should it be an error to initialize an already-initialized objet?
mBaseItem = aItem;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::GetItem(calIItemBase **aResult)
{
NS_IF_ADDREF(*aResult = mBaseItem);
return NS_OK;
}
//
// Recurrence Item set munging
//
NS_IMETHODIMP
calRecurrenceInfo::GetRecurrenceItems (PRUint32 *aCount, calIRecurrenceItem ***aItems)
{
if (mRecurrenceItems.Count() == 0) {
*aItems = nsnull;
*aCount = 0;
return NS_OK;
}
calIRecurrenceItem **items = (calIRecurrenceItem**) nsMemory::Alloc (sizeof(calIRecurrenceItem*) * mRecurrenceItems.Count());
if (!items)
return NS_ERROR_OUT_OF_MEMORY;
for (PRInt32 i = 0; i < mRecurrenceItems.Count(); i++) {
NS_IF_ADDREF (items[i] = mRecurrenceItems[i]);
}
*aItems = items;
*aCount = mRecurrenceItems.Count();
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::SetRecurrenceItems (PRUint32 aCount, calIRecurrenceItem **aItems)
{
if (mImmutable) {
return NS_ERROR_FAILURE; // XXX different error
}
mRecurrenceItems.Clear();
for (PRUint32 i = 0; i < aCount; i++)
mRecurrenceItems.AppendObject(aItems[i]);
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::CountRecurrenceItems (PRUint32 *aCount)
{
*aCount = mRecurrenceItems.Count();
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::GetRecurrenceItemAt (PRUint32 aIndex, calIRecurrenceItem **aItem)
{
NS_IF_ADDREF(*aItem = mRecurrenceItems[aIndex]);
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::AppendRecurrenceItem (calIRecurrenceItem *aItem)
{
if (mImmutable) {
return NS_ERROR_FAILURE; // XXX different error
}
mRecurrenceItems.AppendObject(aItem);
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::DeleteRecurrenceItemAt (PRUint32 aIndex)
{
if (!mRecurrenceItems.RemoveObjectAt(aIndex))
return NS_ERROR_INVALID_ARG;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::InsertRecurrenceItemAt (calIRecurrenceItem *aItem, PRUint32 aIndex)
{
if (!mRecurrenceItems.InsertObjectAt(aItem, aIndex))
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceInfo::ClearRecurrenceItems ()
{
mRecurrenceItems.Clear();
return NS_OK;
}
//
// recurrence calculation
//
NS_IMETHODIMP
calRecurrenceInfo::GetNextOccurrence (calIDateTime *aOccurrenceTime, calIItemOccurrence **aItem)
{
NS_ENSURE_ARG_POINTER(aOccurrenceTime);
NS_ENSURE_ARG_POINTER(aItem);
NS_ASSERTION (mBaseItem, "RecurrenceInfo not initialized");
PRInt32 i, j;
PRInt32 result;
nsresult rv;
nsCOMArray<calIDateTime> dates;
nsCOMPtr<calIDateTime> date;
nsCOMPtr<calIDateTime> startDate;
rv = mBaseItem->GetRecurrenceStartDate(getter_AddRefs(startDate));
NS_ENSURE_SUCCESS(rv, rv);
for (i = 0; i < mRecurrenceItems.Count(); i++) {
rv = mRecurrenceItems[i]->GetNextOccurrence (startDate, aOccurrenceTime, getter_AddRefs(date));
NS_ENSURE_SUCCESS(rv, rv);
// if there is no next occurrence, continue.
if (!date)
continue;
PRBool isNegative = PR_FALSE;
rv = mRecurrenceItems[i]->GetIsNegative(&isNegative);
NS_ENSURE_SUCCESS(rv, rv);
if (isNegative) {
// if this is negative, we look for this date in the existing set, and remove it if its there
for (j = dates.Count() - 1; j >= 0; j--) {
if (NS_SUCCEEDED(dates[j]->Compare(date, &result)) && result == 0)
dates.RemoveObjectAt(j);
}
} else {
// if positive, we just add the date to the existing set
dates.AppendObject(date);
}
}
// no next date found
if (dates.Count() == 0) {
*aItem = nsnull;
return NS_OK;
}
// find the earliest date in the set
date = dates[0];
for (i = 0; i < dates.Count(); i++) {
if (NS_SUCCEEDED(date->Compare(dates[i], &result)) && result > 0)
date = dates[i];
}
nsCOMPtr<calIDateTime> enddate;
date->Clone(getter_AddRefs(enddate));
nsCOMPtr<calIEvent> event = do_QueryInterface(mBaseItem);
if (event) {
nsCOMPtr<calIDateTime> duration;
rv = event->GetDuration(getter_AddRefs(duration));
NS_ENSURE_SUCCESS(rv, rv);
enddate->AddDuration(duration);
}
nsCOMPtr<calIItemOccurrence> occ = do_CreateInstance(CAL_ITEM_OCCURRENCE_CONTRACTID);
occ->Initialize(mBaseItem, date, enddate);
NS_ADDREF(*aItem = occ);
return NS_OK;
}
static int PR_CALLBACK
calDateTimeComparator (calIDateTime *aElement1,
calIDateTime *aElement2,
void *aData)
{
PRInt32 result;
aElement1->Compare(aElement2, &result);
return result;
}
NS_IMETHODIMP
calRecurrenceInfo::GetOccurrences (calIDateTime *aRangeStart,
calIDateTime *aRangeEnd,
PRUint32 aMaxCount,
PRUint32 *aCount, calIItemOccurrence ***aItems)
{
NS_ENSURE_ARG_POINTER(aRangeStart);
NS_ENSURE_ARG_POINTER(aCount);
NS_ENSURE_ARG_POINTER(aItems);
NS_ASSERTION (mBaseItem, "RecurrenceInfo not initialized");
PRInt32 i, j, k;
PRInt32 result;
nsresult rv;
nsCOMArray<calIDateTime> dates;
nsCOMPtr<calIDateTime> startDate;
rv = mBaseItem->GetRecurrenceStartDate(getter_AddRefs(startDate));
NS_ENSURE_SUCCESS(rv, rv);
for (i = 0; i < mRecurrenceItems.Count(); i++) {
calIDateTime **cur_dates = nsnull;
PRUint32 num_cur_dates = 0;
// if both range start and end are specified, we ask for all of the occurrences,
// to make sure we catch all possible exceptions. If aRangeEnd isn't specified,
// then we have to ask for aMaxCount, and hope for the best.
if (aRangeStart && aRangeEnd)
rv = mRecurrenceItems[i]->GetOccurrences (startDate, aRangeStart, aRangeEnd, 0, &num_cur_dates, &cur_dates);
else
rv = mRecurrenceItems[i]->GetOccurrences (startDate, aRangeStart, aRangeEnd, aMaxCount, &num_cur_dates, &cur_dates);
NS_ENSURE_SUCCESS(rv, rv);
// if there are no occurrences, continue.
if (num_cur_dates == 0)
continue;
PRBool isNegative = PR_FALSE;
rv = mRecurrenceItems[i]->GetIsNegative(&isNegative);
NS_ENSURE_SUCCESS(rv, rv);
if (isNegative) {
// if this is negative, we look for any of the given dates
// in the existing set, and remove them if they're
// present.
for (j = dates.Count() - 1; j >= 0; j--) {
for (k = 0; k < (int) num_cur_dates; k++) {
if (NS_SUCCEEDED(dates[j]->Compare(cur_dates[k], &result)) && result == 0) {
dates.RemoveObjectAt(j);
break;
}
}
}
} else {
// if positive, we just add these date to the existing set,
// but only if they're not already there
for (j = 0; j < (int) num_cur_dates; j++) {
PRBool isFound = PR_FALSE;
for (k = 0; k < dates.Count(); k++) {
if (NS_SUCCEEDED(dates[k]->Compare(cur_dates[j], &result)) && result == 0) {
isFound = PR_TRUE;
break;
}
}
if (!isFound)
dates.AppendObject(cur_dates[j]);
}
}
}
// now sort the resulting list
dates.Sort(calDateTimeComparator, nsnull);
nsCOMPtr<calIDateTime> duration;
nsCOMPtr<calIEvent> event = do_QueryInterface(mBaseItem);
if (event) {
rv = event->GetDuration(getter_AddRefs(duration));
NS_ENSURE_SUCCESS(rv, rv);
}
PRUint32 count = aMaxCount;
if (!count)
count = dates.Count();
if (count) {
calIItemOccurrence **occArray = (calIItemOccurrence **) nsMemory::Alloc(sizeof(calIItemOccurrence*) * count);
for (int i = 0; i < (int) count; i++) {
nsCOMPtr<calIItemOccurrence> occ = do_CreateInstance (CAL_ITEM_OCCURRENCE_CONTRACTID);
nsCOMPtr<calIDateTime> endDate;
dates[i]->Clone(getter_AddRefs(endDate));
if (event) {
endDate->AddDuration(duration);
}
occ->Initialize (mBaseItem, dates[i], endDate);
NS_ADDREF(occArray[i] = occ);
}
*aItems = occArray;
} else {
*aItems = nsnull;
}
*aCount = count;
return NS_OK;
}

View File

@ -1,69 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Oracle Corporation code.
*
* The Initial Developer of the Original Code is
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef CALRECURRENCEINFO_H_
#define CALRECURRENCEINFO_H_
#include "calIRecurrenceInfo.h"
#include "calIItemBase.h"
#include "calIRecurrenceItem.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
class calRecurrenceInfo : public calIRecurrenceInfo
{
public:
calRecurrenceInfo();
// nsISupports interface
NS_DECL_ISUPPORTS
// calIRecurrenceInfo interface
NS_DECL_CALIRECURRENCEINFO
protected:
PRBool mImmutable;
nsCOMPtr<calIItemBase> mBaseItem;
nsCOMArray<calIRecurrenceItem> mRecurrenceItems;
};
#endif /* CALRECURRENCEINFO_H_ */

View File

@ -0,0 +1,646 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is lightning code.
*
* The Initial Developer of the Original Code is
* Oracle Corporation
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
function calRecurrenceInfo() {
this.mRecurrenceItems = new Array();
this.mExceptions = new Array();
}
function calDebug() {
dump.apply(null, arguments);
}
var calRecurrenceInfoClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIRecurrenceInfo,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;
},
getHelperForLanguage: function (language) {
return null;
},
contractID: "@mozilla.org/calendar/recurrence-info;1",
classDescription: "Calendar Recurrence Info",
classID: Components.ID("{04027036-5884-4a30-b4af-f2cad79f6edf}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0
};
calRecurrenceInfo.prototype = {
// QI with CI
QueryInterface: function(aIID) {
if (aIID.equals(Components.interfaces.nsISupports) ||
aIID.equals(Components.interfaces.calIRecurrenceInfo))
return this;
if (aIID.equals(Components.interfaces.nsIClassInfo))
return calRecurrenceInfoClassInfo;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
//
// Mutability bits
//
mImmutable: false,
get isMutable() { return !this.mImmutable; },
makeImmutable: function() {
if (this.mImmutable)
return;
for each (ritem in this.mRecurrenceItems) {
if (ritem.isMutable)
ritem.makeImmutable();
}
for each (ex in this.mExceptions) {
if (ex.item.isMutable)
ex.item.makeImmutable();
}
this.mImmutable = true;
},
clone: function() {
var cloned = new calRecurrenceInfo();
cloned.mBaseItem = this.mBaseItem;
var clonedItems = [];
for each (ritem in this.mRecurrenceItems)
clonedItems.push(ritem.clone());
cloned.mRecurrenceItems = clonedItems;
var clonedExceptions = [];
for each (exitem in this.mExceptions) {
var c = exitem.item.cloneShallow(this.mBaseItem);
clonedExceptions.push( { id: exitem.id, item: c } );
}
cloned.mExceptions = clonedExceptions;
return cloned;
},
//
// calIRecurrenceInfo impl
//
mBaseItem: null,
get item() {
return this.mBaseItem;
},
set item(value) {
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
this.mBaseItem = value;
},
mRecurrenceItems: null,
get isFinite() {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
for each (ritem in this.mRecurrenceItems) {
if (!ritem.isFinite)
return false;
}
return true;
},
getRecurrenceItems: function(aCount) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
aCount.value = this.mRecurrenceItems.length;
return this.mRecurrenceItems;
},
setRecurrenceItems: function(aCount, aItems) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
// should we clone these?
this.mRecurrenceItems = aItems;
},
countRecurrenceItems: function() {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
return this.mRecurrenceItems.length;
},
getRecurrenceItemAt: function(aIndex) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (aIndex < 0 || aIndex >= mRecurrenceItems.length)
throw Components.results.NS_ERROR_INVALID_ARG;
return this.mRecurrenceItems[aIndex];
},
appendRecurrenceItem: function(aItem) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
this.mRecurrenceItems.push(aItem);
},
deleteRecurrenceItemAt: function(aIndex) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
if (aIndex < 0 || aIndex >= this.mRecurrenceItems.length)
throw Components.results.NS_ERROR_INVALID_ARG;
this.mRecurrenceItems.splice(aIndex, 1);
},
insertRecurrenceItemAt: function(aItem, aIndex) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
if (aIndex < 0 || aIndex >= this.mRecurrenceItems.length)
throw Components.results.NS_ERROR_INVALID_ARG;
this.mRecurrenceItems.splice(aIndex, 0, aItem);
},
clearRecurrenceItems: function() {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
this.mRecurrenceItems = new Array();
},
//
// calculations
//
getNextOccurrenceDate: function (aTime) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
var startDate = this.mBaseItem.getRecurrenceStartDate();
var dates = [];
for each (ritem in this.mRecurrenceItems) {
var date = ritem.getNextOccurrence(startDate, aTime);
if (!date)
continue;
if (ritem.isNegative)
dates = dates.filter(function (d) { return (d.compare(date) != 0); });
else
dates = dates.push(date);
}
// if no dates, there's no next
if (dates.length == 0)
return null;
// find the earliest date
var earliestDate = dates[0];
dates.forEach(function (d) { if (d.compare(earliestDate) < 0) earliestDate = d; });
return earliestDate;
},
getNextOccurrence: function (aTime) {
var earliestDate = this.getNextOccurrenceDate (aTime);
if (!earliestDate)
return null;
if (this.mExceptions) {
// scan exceptions for any dates earlier than
// earliestDate (but still after aTime)
this.mExceptions.forEach (function (ex) {
var dtstart = ex.item.getProperty("DTSTART");
if (aTime.compare(dtstart) <= 0 &&
earliestDate.compare(dtstart) > 0)
{
earliestDate = dtstart;
}
});
}
var startDate = earliestDate.clone();
var endDate = null;
if (this.mBaseItem.hasProperty("DTEND")) {
endDate = earliestDate.clone();
endDate.addDuration(this.mBaseItem.duration);
}
var proxy = this.mBaseItem.createProxy();
proxy.setRecurrenceId(earliestDate);
proxy.setProperty("DTSTART", startDate);
if (endDate)
proxy.setProperty("DTEND", endDate);
return proxy;
},
// internal helper function;
calculateDates: function (aRangeStart, aRangeEnd,
aMaxCount, aIncludeExceptions, aReturnRIDs)
{
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
var startDate = this.mBaseItem.recurrenceStartDate;
var dates = [];
for each (ritem in this.mRecurrenceItems) {
var cur_dates;
// if both range start and end are specified, we ask for all of the occurrences,
// to make sure we catch all possible exceptions. If aRangeEnd isn't specified,
// then we have to ask for aMaxCount, and hope for the best.
if (aRangeStart && aRangeEnd)
cur_dates = ritem.getOccurrences(startDate, aRangeStart, aRangeEnd, 0, {});
else
cur_dates = ritem.getOccurrences(startDate, aRangeStart, aRangeEnd, aMaxCount, {});
if (cur_dates.length == 0)
continue;
if (ritem.isNegative) {
// if this is negative, we look for any of the given dates
// in the existing set, and remove them if they're
// present.
// XXX: i'm pretty sure negative dates can't really have exceptions
// (like, you can't make a date "real" by defining an RECURRENCE-ID which
// is an EXDATE, and then giving it a real DTSTART) -- so we don't
// check exceptions here
cur_dates.forEach (function (dateToRemove) {
dates = dates.filter(function (d) { return d.compare(dateToRemove) != 0; });
});
} else {
// if positive, we just add these date to the existing set,
// but only if they're not already there
var datesToAdd = [];
var rinfo = this;
cur_dates.forEach (function (dateToAdd) {
if (!dates.some(function (d) { return d.compare(dateToAdd) == 0; })) {
if (aIncludeExceptions && rinfo.mExceptions)
{
// only add if there's no exception for this;
// we'll test all exception dates later on
if (!rinfo.getExceptionFor(dateToAdd, false))
dates.push(dateToAdd);
} else {
dates.push(dateToAdd);
}
}
});
}
}
// Now toss in exceptions into this list; we just scan them all.
if (aIncludeExceptions && this.mExceptions) {
this.mExceptions.forEach(function(ex) {
var dtstart = ex.item.getProperty("DTSTART");
var dateToReturn;
if (aReturnRIDs)
dateToReturn = ex.id;
else
dateToReturn = dtstart;
// is our startdate within the range?
if ((!aRangeStart || aRangeStart.compare(dtstart) <= 0) &&
(!aRangeEnd || aRangeEnd.compare(dtstart) > 0))
{
dates.push(dateToReturn);
return;
}
// is our end date within the range?
var dtend = ex.item.getProperty("DTEND");
if ((!aRangeStart || aRangeStart.compare(dtend) <= 0) &&
(!aRangeEnd || aRangeEnd.compare(dtend) > 0))
{
dates.push(dateToReturn);
return;
}
// is the range in the middle of a long event?
if (aRangeStart && aRangeEnd &&
aRangeStart.compare(dtstart) >= 0 &&
aRangeEnd.compare(dtend) <= 0)
{
dates.push(dateToReturn);
return;
}
});
}
// now sort the list
dates.sort(function (a,b) { return a.compare(b); });
// chop anything over aMaxCount, if specified
if (aMaxCount && dates.length > aMaxCount)
dates = dates.splice(aMaxCount, dates.length - aMaxCount);
return dates;
},
getOccurrenceDates: function (aRangeStart, aRangeEnd,
aMaxCount, aCount)
{
var dates = this.calculateDates(aRangeStart, aRangeEnd, aMaxCount, true, false);
aCount.value = dates.length;
return dates;
},
getOccurrences: function (aRangeStart, aRangeEnd,
aMaxCount,
aCount)
{
var dates = this.calculateDates(aRangeStart, aRangeEnd, aMaxCount, true, true);
if (dates.length == 0) {
aCount.value = 0;
return [];
}
var count = aMaxCount;
if (!count)
count = dates.length;
var results = [];
for (var i = 0; i < count; i++) {
var proxy = this.getOccurrenceFor(dates[i]);
results.push(proxy);
}
aCount.value = results.length;
return results;
},
getOccurrenceFor: function (aRecurrenceId) {
var proxy = this.getExceptionFor(aRecurrenceId, false);
if (!proxy) {
var duration = null;
if (this.mBaseItem.hasProperty("DTEND"))
duration = this.mBaseItem.duration;
proxy = this.mBaseItem.createProxy();
proxy.recurrenceId = aRecurrenceId;
proxy.setProperty("DTSTART", aRecurrenceId.clone());
if (duration) {
var enddate = aRecurrenceId.clone();
enddate.addDuration(duration);
proxy.setProperty("DTEND", enddate);
}
if (!this.mBaseItem.isMutable)
proxy.makeImmutable();
}
return proxy;
},
removeOccurrenceAt: function (aRecurrenceId) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
var d = Components.classes["@mozilla.org/calendar/recurrence-date;1"].createInstance(Components.interfaces.calIRecurrenceDate);
d.isNegative = true;
d.date = aRecurrenceId.clone();
return this.appendRecurrenceItem(d);
},
restoreOccurrenceAt: function (aRecurrenceId) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
if (this.mImmutable)
throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
for (var i = 0; i < this.mRecurrenceItems.length; i++) {
if (this.mRecurrenceItems[i] instanceof Components.interfaces.calIRecurrenceDate) {
var rd = this.mRecurrenceItems[i].QueryInterface(Components.interfaces.calIRecurrenceDate);
if (rd.isNegative && rd.date.compare(aRecurrenceId) == 0) {
return this.deleteRecurrenceItemAt(i);
}
}
}
throw Components.results.NS_ERROR_INVALID_ARG;
},
//
// exceptions
//
//
// Some notes:
//
// The way I read ICAL, RECURRENCE-ID is used to specify a
// particular instance of a recurring event, according to the
// RRULEs/RDATEs/etc. specified in the base event. If one of
// these is to be changed ("an exception"), then it can be
// referenced via the UID of the original event, and a
// RECURRENCE-ID of the start time of the instance to change.
// This, to me, means that an event where one of the instances has
// changed to a different time has a RECURRENCE-ID of the original
// start time, and a DTSTART/DTEND representing the new time.
//
// ITIP, however, seems to want something different -- you're
// supposed to use UID/RECURRENCE-ID to select from the current
// set of occurrences of an event. If you change the DTSTART for
// an instance, you're supposed to use the old (original) DTSTART
// as the RECURRENCE-ID, and put the new time as the DTSTART.
// However, after that change, to refer to that instance in the
// future, you have to use the modified DTSTART as the
// RECURRENCE-ID. This madness is described in ITIP end of
// section 3.7.1.
//
// This implementation does the first approach (RECURRENCE-ID will
// never change even if DTSTART for that instance changes), which
// I think is the right thing to do for CalDAV; I don't know what
// we'll do for incoming ITIP events though.
//
mExceptions: null,
modifyException: function (anItem) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
// the item must be an occurrence
if (anItem.parentItem == anItem)
throw Components.results.NS_ERROR_UNEXPECTED;
if (anItem.parentItem.calendar != this.mBaseItem.calendar &&
anItem.parentItem.id != this.mBaseItem.id)
{
calDebug ("recurrenceInfo::addException: item parentItem != this.mBaseItem (calendar/id)!\n");
throw Components.results.NS_ERROR_INVALID_ARG;
}
if (anItem.recurrenceId == null) {
calDebug ("recurrenceInfo::addException: item with null recurrenceId!\n");
throw Components.results.NS_ERROR_INVALID_ARG;
}
var itemtoadd;
if (anItem.isMutable) {
itemtoadd = anItem.cloneShallow(this.mBaseItem);
itemtoadd.makeImmutable();
} else {
itemtoadd = anItem;
}
// we're going to assume that the recurrenceId is valid here,
// because presumably the item came from one of our functions
// remove any old one, if present
this.removeExceptionFor(anItem.recurrenceId);
this.mExceptions.push( { id: itemtoadd.recurrenceId, item: itemtoadd } );
},
createExceptionFor: function (aRecurrenceId) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
// XX should it be an error to createExceptionFor
// an already-existing recurrenceId?
var existing = this.getExceptionFor(aRecurrenceId, false);
if (existing)
return existing;
// check if aRecurrenceId is valid.
// this is a bit of a hack; we know that ranges are defined as [start, end),
// so we do a search on aRecurrenceId and aRecurrenceId.seconds + 1.
var rangeStart = aRecurrenceId;
var rangeEnd = aRecurrenceId.clone();
rangeEnd.second += 1;
rangeEnd.normalize();
var dates = this.getOccurrenceDates (rangeStart, rangeEnd, 1, {});
var found = false;
for each (d in dates) {
if (d.compare(aRecurrenceId) == 0) {
found = true;
break;
}
}
// not found; the recurrence id is invalid
if (!found)
throw Components.results.NS_ERROR_INVALID_ARG;
var rid = aRecurrenceId.clone();
rid.makeImmutable();
var newex = this.mBaseItem.createProxy();
newex.recurrenceId = rid;
this.mExceptions.push({id: rid, item: newex});
return newex;
},
getExceptionFor: function (aRecurrenceId, aCreate) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
for each (ex in this.mExceptions) {
if (ex.id.compare(aRecurrenceId) == 0)
return ex.item;
}
if (aCreate) {
return this.createExceptionFor(aRecurrenceId);
}
return null;
},
removeExceptionFor: function (aRecurrenceId) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
this.mExceptions = this.mExceptions.filter (function(ex) {
return (ex.id.compare(aRecurrenceId) != 0);
});
},
getExceptionIds: function (aCount) {
if (!this.mBaseItem)
throw Components.results.NS_ERROR_NOT_INITIALIZED;
var ids = this.mExceptions.map (function(ex) {
return ex.id;
});
aCount.value = ids.length;
return ids;
},
};

View File

@ -50,7 +50,7 @@ extern "C" {
#include "ical.h"
}
NS_IMPL_ISUPPORTS2(calRecurrenceRule, calIRecurrenceItem, calIRecurrenceRule)
NS_IMPL_ISUPPORTS2_CI(calRecurrenceRule, calIRecurrenceItem, calIRecurrenceRule)
calRecurrenceRule::calRecurrenceRule()
: mImmutable(PR_FALSE),
@ -85,6 +85,21 @@ calRecurrenceRule::MakeImmutable()
return NS_OK;
}
NS_IMETHODIMP
calRecurrenceRule::Clone(calIRecurrenceItem **aResult)
{
calRecurrenceRule *crc = new calRecurrenceRule;
if (!crc)
return NS_ERROR_OUT_OF_MEMORY;
crc->mIsNegative = mIsNegative;
crc->mIsByCount = mIsByCount;
*(crc->mIcalRecur) = *mIcalRecur;
NS_ADDREF(*aResult = crc);
return NS_OK;
}
/* attribute boolean isNegative; */
NS_IMETHODIMP
calRecurrenceRule::GetIsNegative(PRBool *_retval)
@ -105,18 +120,19 @@ calRecurrenceRule::SetIsNegative(PRBool aIsNegative)
return NS_OK;
}
/* readonly attribute boolean isFinite; */
NS_IMETHODIMP
calRecurrenceRule::Clone(calIRecurrenceItem **aResult)
calRecurrenceRule::GetIsFinite(PRBool *_retval)
{
calRecurrenceRule *crc = new calRecurrenceRule;
if (!crc)
return NS_ERROR_OUT_OF_MEMORY;
NS_ENSURE_ARG_POINTER(_retval);
crc->mIsNegative = mIsNegative;
crc->mIsByCount = mIsByCount;
*(crc->mIcalRecur) = *mIcalRecur;
NS_ADDREF(*aResult = crc);
if ((mIsByCount && mIcalRecur->count == 0) ||
(!mIsByCount && icaltime_is_null_time(mIcalRecur->until)))
{
*_retval = PR_FALSE;
} else {
*_retval = PR_TRUE;
}
return NS_OK;
}
@ -169,6 +185,9 @@ calRecurrenceRule::GetCount(PRInt32 *aRecurCount)
{
NS_ENSURE_ARG_POINTER(aRecurCount);
if (!mIsByCount)
return NS_ERROR_FAILURE;
if (mIcalRecur->count == 0 && icaltime_is_null_time(mIcalRecur->until)) {
*aRecurCount = -1;
} else if (mIcalRecur->count) {
@ -203,6 +222,9 @@ calRecurrenceRule::GetEndDate(calIDateTime * *aRecurEnd)
{
NS_ENSURE_ARG_POINTER(aRecurEnd);
if (mIsByCount)
return NS_ERROR_FAILURE;
if (!icaltime_is_null_time(mIcalRecur->until)) {
calDateTime *cdt = new calDateTime(&mIcalRecur->until);
if (!cdt)
@ -389,7 +411,6 @@ calDateTimeComparator (calIDateTime *aElement1,
return result;
}
/* void getOccurrences(in calIDateTime aStartTime, in calIDateTime aEndTime, out unsigned long aCount, [array, size_is (aCount), retval] out calIItemOccurrence aItems); */
NS_IMETHODIMP
calRecurrenceRule::GetOccurrences(calIDateTime *aStartTime,
calIDateTime *aRangeStart,

View File

@ -47,17 +47,29 @@ function calTodo() {
this.wrappedJSObject = this;
this.initItemBase();
this.initTodo();
this.todoPromotedProps = {
"DTSTART": true,
"DTEND": true,
"DTSTAMP": true,
"DUE": true,
"COMPLETED": true,
"PERCENT-COMPLETE": true,
__proto__: this.itemBasePromotedProps
};
}
// var trickery to suppress lib-as-component errors from loader
var calItemBase;
calTodoClassInfo = {
var calTodoClassInfo = {
getInterfaces: function (count) {
var ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.calIItemBase,
Components.interfaces.calITodo
Components.interfaces.calITodo,
Components.interfaces.calIInternalShallowCopy,
Components.interfaces.nsIClassInfo
];
count.value = ifaces.length;
return ifaces;
@ -78,7 +90,8 @@ calTodo.prototype = {
__proto__: calItemBase ? (new calItemBase()) : {},
QueryInterface: function (aIID) {
if (aIID.equals(Components.interfaces.calITodo))
if (aIID.equals(Components.interfaces.calITodo) ||
aIID.equals(Components.interfaces.calIInternalShallowCopy))
return this;
if (aIID.equals(Components.interfaces.nsIClassInfo))
@ -94,13 +107,32 @@ calTodo.prototype = {
this.mPercentComplete = 0;
},
clone: function () {
cloneShallow: function (aNewParent) {
var m = new calTodo();
this.cloneItemBaseInto(m);
this.cloneItemBaseInto(m, aNewParent);
m.mEntryDate = this.mEntryDate.clone();
m.mDueDate = this.mDueDate.clone();
m.mCompletedDate = this.mCompletedDate.clone();
m.mPercentComplete = this.mPercentComplete;
return m;
},
clone: function () {
var m;
if (this.parentItem != this) {
var clonedParent = this.mParentItem.clone();
m = clonedParent.recurrenceInfo.getExceptionFor (this.recurrenceId, true);
} else {
m = this.cloneShallow(null);
}
return m;
},
createProxy: function () {
var m = new calTodo();
m.initializeProxy(this);
return m;
},
@ -165,6 +197,8 @@ calTodo.prototype = {
return icalcomp;
},
todoPromotedProps: null,
set icalComponent(todo) {
this.modify();
if (todo.componentType != "VTODO") {
@ -177,19 +211,14 @@ calTodo.prototype = {
this.mapPropsFromICS(todo, this.icsEventPropMap);
this.mIsAllDay = this.mStartDate && this.mStartDate.isDate;
var promotedProps = {
"DTSTART": true,
"DTEND": true,
"DTSTAMP": true,
"DUE": true,
"COMPLETED": true,
"PERCENT-COMPLETE": true,
__proto__: this.itemBasePromotedProps
};
this.importUnpromotedProperties(todo, promotedProps);
this.importUnpromotedProperties(todo, todoPromotedProps);
// Importing didn't really change anything
this.mDirty = false;
},
isPropertyPromoted: function (name) {
return (this.todoPromotedProps[name]);
},
};
// var decl to prevent spurious error messages when loaded as component

View File

@ -94,7 +94,7 @@ function rebuildAgendaView(invalidate)
if (e instanceof Synthetic)
dump(" " + e.title + "\n");
else
dump(" " + e.item.title + " @ " + e.occurrenceStartDate + "\n");
dump(" " + e.title + " @ " + e.occurrenceStartDate + "\n");
});
*/
this.forceTreeRebuild();
@ -143,12 +143,13 @@ function getCellText(row, column)
if (column.id == "col-agenda-item") {
if (event instanceof Synthetic)
return event.title;
return event.item.title;
return event.title;
}
if (event instanceof Synthetic)
return "";
return event.occurrenceStartDate.toString();
var start = event.startDate || event.entryDate;
return start.toString();
};
agendaTreeView.getLevel =
@ -223,10 +224,7 @@ function hasNextSibling(row, afterIndex)
agendaTreeView.findPeriodForItem =
function findPeriodForItem(item)
{
var start = item.occurrenceStartDate;
if (start.compare(this.today.start) < 0 || start.compare(this.soon.end) > 0)
return null;
var start = item.startDate || item.entryDate;
if (start.compare(this.today.end) <= 0)
return this.today;
@ -236,7 +234,7 @@ function findPeriodForItem(item)
if (start.compare(this.soon.end) <= 0)
return this.soon;
void(item.item.title + " @ " + start + " not in range " +
void(item.title + " @ " + start + " not in range " +
"(" + this.today.start + " - " + this.soon.end + ")\n");
return null;
@ -248,7 +246,7 @@ function addItem(item)
var when = this.findPeriodForItem(item);
if (!when)
return;
void(item.item.title + " @ " + item.occurrenceStartDate + " -> " + when.title + "\n");
void(item.title + " @ " + item.occurrenceStartDate + " -> " + when.title + "\n");
when.events.push(item);
this.calendarUpdateComplete();
};
@ -263,11 +261,16 @@ function deleteItem(item)
}
void("deleting item " + item + " from " + when.title + "\n");
void("before: " + when.events.map(function (e) { return e.item.title; }).join(" ") + "\n");
void("before: " + when.events.map(function (e) { return e.title; }).join(" ") + "\n");
when.events = when.events.filter(function (e) {
return !e.equals(item);
});
void("after: " + when.events.map(function (e) { return e.item.title; }).join(" ") + "\n");
if (e.id != item.id)
return true;
if (e.recurrenceId && item.recurrenceId &&
e.recurrenceId.compare(item.recurrenceId) != 0)
return true;
return false;
});
void("after: " + when.events.map(function (e) { return e.title; }).join(" ") + "\n");
this.rebuildAgendaView(true);
};
@ -276,7 +279,9 @@ function calendarUpdateComplete()
{
[this.today, this.tomorrow, this.soon].forEach(function(when) {
function compare(a, b) {
return a.occurrenceStartDate.compare(b.occurrenceStartDate);
var ad = a.startDate || a.entryDate;
var bd = b.startDate || b.entryDate;
return ad.compare(bd);
}
when.events.sort(compare);
});

View File

@ -113,29 +113,28 @@ var ltnCalendarViewController = {
},
modifyOccurrence: function (aOccurrence, aNewStartTime, aNewEndTime) {
if (aOccurrence.recurrenceInfo) {
dump ("*** Don't know what to do in modifyOccurrence for a recurring event!\n");
return;
}
// if we can modify this thing directly (e.g. just the time changed),
// then do so; otherwise pop up the dialog
if (aNewStartTime && aNewEndTime && !aNewStartTime.isDate && !aNewEndTime.isDate) {
var newEvent = aOccurrence.item.clone();
newEvent.startDate = aNewStartTime;
newEvent.endDate = aNewEndTime;
newEvent.calendar.modifyItem(newEvent, aOccurrence.item, null);
var instance = aOccurrence.clone();
instance.startDate = aNewStartTime;
instance.endDate = aNewEndTime;
instance.calendar.modifyItem(instance, aOccurrence, null);
} else {
modifyEventWithDialog(aOccurrence.item);
modifyEventWithDialog(aOccurrence);
}
},
deleteOccurrence: function (aOccurrence) {
dump ("+++ deleteOccurrence\n");
if (aOccurrence.recurrenceInfo) {
dump ("*** Don't know what do in deleteOccurrence for a recurring event!\n");
return;
if (aOccurrence.parentItem) {
var event = aOccurrence.parentItem.clone();
event.recurrenceInfo.removeOccurrenceAt(aOccurrence.recurrenceId);
event.calendar.modifyItem(event, aOccurrence, null);
} else {
aOccurrence.calendar.deleteItem(aOccurrence, null);
}
aOccurrence.item.calendar.deleteItem(aOccurrence.item, null);
}
};

View File

@ -2,13 +2,33 @@
function ltnCreateInstance(cid, iface) {
if (!iface)
iface = "nsISupports";
return Components.classes[cid].createInstance(Components.interfaces[iface]);
try {
return Components.classes[cid].createInstance(Components.interfaces[iface]);
} catch(e) {
dump("#### ltnCreateInstance failed for: " + cid + "\n");
var frame = Components.stack;
for (var i = 0; frame && (i < 4); i++) {
dump(i + ": " + frame + "\n");
frame = frame.caller;
}
throw e;
}
}
function ltnGetService(cid, iface) {
if (!iface)
iface = "nsISupports";
return Components.classes[cid].getService(Components.interfaces[iface]);
try {
return Components.classes[cid].getService(Components.interfaces[iface]);
} catch(e) {
dump("#### ltnGetService failed for: " + cid + "\n");
var frame = Components.stack;
for (var i = 0; frame && (i < 4); i++) {
dump(i + ": " + frame + "\n");
frame = frame.caller;
}
throw e;
}
}
var atomSvc;

View File

@ -86,7 +86,6 @@ function showCalendar(jumpToToday)
var d = Components.classes['@mozilla.org/calendar/datetime;1'].createInstance(Components.interfaces.calIDateTime);
d.jsDate = new Date();
d = d.getInTimezone(calendarDefaultTimezone());
view.showDate(d);
}

View File

@ -31,8 +31,8 @@
<vbox flex="1">
<hbox>
<minimonth id="ltnMinimonthLeft" onchange="ltnMinimonthPick('left', this);"/>
<minimonth id="ltnMinimonthRight" onchange="ltnMinimonthPick('right', this);"/>
<minimonth id="ltnMinimonthLeft" onchange="ltnMinimonthPick('left', this);" flex="1"/>
<minimonth id="ltnMinimonthRight" onchange="ltnMinimonthPick('right', this);" flex="1"/>
</hbox>
<tabbox flex="1">

View File

@ -61,12 +61,10 @@ function calMemoryCalendar() {
function makeOccurrence(item, start, end)
{
var occ = Components.classes["@mozilla.org/calendar/item-occurrence;1"].
createInstance(Components.interfaces.calIItemOccurrence);
// XXX poor form
occ.wrappedJSObject.item = item;
occ.wrappedJSObject.occurrenceStartDate = start;
occ.wrappedJSObject.occurrenceEndDate = end;
var occ = item.createProxy();
occ.recurrenceId = start;
occ.startDate = start;
occ.endDate = end;
return occ;
}
@ -122,21 +120,15 @@ calMemoryCalendar.prototype = {
// void addObserver( in calIObserver observer );
addObserver: function (aObserver, aItemFilter) {
for each (obs in this.mObservers) {
if (obs == aObserver)
return;
}
if (this.mObservers.any(function(o) { return (o == aObserver); }))
return;
this.mObservers.push(aObserver);
},
// void removeObserver( in calIObserver observer );
removeObserver: function (aObserver) {
var newObservers = Array();
for each (obs in this.mObservers) {
if (obs != aObserver)
newObservers.push(obs);
}
var newObservers = this.mObservers.filter(function(o) { return (o != aObserver); });
this.mObservers = newObservers;
},
@ -243,7 +235,7 @@ calMemoryCalendar.prototype = {
modifiedItem.id,
modifiedItem);
// notify observers
this.observeModifyItem(aOldItem, modifiedItem);
this.observeModifyItem(modifiedItem, aOldItem);
},
// void deleteItem( in calIItemBase aItem, in calIOperationListener aListener );
@ -376,7 +368,7 @@ calMemoryCalendar.prototype = {
// figure out the return interface type
var typeIID = null;
if (itemReturnOccurrences) {
typeIID = calIItemOccurrence;
typeIID = calIItemBase;
} else {
if (wantEvents && wantTodos) {
typeIID = calIItemBase;
@ -425,9 +417,7 @@ calMemoryCalendar.prototype = {
{
// there might be some recurrences here that we need to handle
var recs = item.recurrenceInfo.getOccurrences (aRangeStart, aRangeEnd, 0, {});
for (var i = 0; i < recs.length; i++) {
itemsFound.push(recs[i]);
}
itemsFound = concat(itemsFound, recs);
} else if (itemEndTime >= startTime) {
// no occurrences
if (itemReturnOccurrences)

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +0,0 @@
CREATE TABLE cal_calendar_schema_version (
version INTEGER
);
CREATE TABLE cal_items (
cal_id INTEGER, -- REFERENCES cal_calendars.id,
-- 0: event, 1: todo
item_type INTEGER,
-- ItemBase bits
id STRING,
time_created INTEGER,
last_modified INTEGER,
title STRING,
priority INTEGER,
privacy STRING,
ical_status STRING,
-- CAL_ITEM_FLAG_PRIVATE = 1
-- CAL_ITEM_FLAG_HAS_ATTENDEES = 2
-- CAL_ITEM_FLAG_HAS_PROPERTIES = 4
-- CAL_ITEM_FLAG_EVENT_ALLDAY = 8
-- CAL_ITEM_FLAG_HAS_RECURRENCE = 16
flags INTEGER,
-- Event bits
event_start INTEGER,
event_end INTEGER,
event_stamp INTEGER,
-- Todo bits
todo_entry INTEGER,
todo_due INTEGER,
todo_completed INTEGER,
todo_complete INTEGER,
-- internal bits
alarm_id INTEGER -- REFERENCES cal_alarms.id ON DELETE CASCADE
);
CREATE TABLE cal_attendees (
item_id STRING,
attendee_id STRING,
common_name STRING,
rsvp INTEGER,
role STRING,
status STRING,
type STRING
);
CREATE TABLE cal_alarms (
id INTEGER PRIMARY KEY,
alarm_data BLOB
);
CREATE TABLE cal_recurrence (
item_id STRING,
recur_index INTEGER, -- the index in the recurrence array of this thing
recur_type STRING, -- values from calIRecurrenceInfo; if null, date-based.
is_negative BOOLEAN,
--
-- these are for date-based recurrence
--
-- comma-separated list of dates
dates STRING,
--
-- these are for rule-based recurrence
--
count INTEGER,
end_date INTEGER,
interval INTEGER,
-- components, comma-separated list or null
second STRING,
minute STRING,
hour STRING,
day STRING,
monthday STRING,
yearday STRING,
weekno STRING,
month STRING,
setpos STRING
);
CREATE TABLE cal_properties (
item_id STRING,
key STRING,
value BLOB
);

View File

@ -263,7 +263,7 @@
this.mEditorDate = aDate;
if (this.mSelected) {
this.mSelected.setAttribute("selected", "");
this.mSelected.removeAttribute("selected");
this.mSelected = null;
}
@ -308,7 +308,7 @@
if (aDate.getMonth() != date.getMonth()) {
day.setAttribute("othermonth", "true");
} else {
day.setAttribute("othermonth", "");
day.removeAttribute("othermonth");
}
// highlight the current date
@ -438,7 +438,7 @@
<body>
<![CDATA[
if (this.mSelected) {
this.mSelected.setAttribute("selected", "");
this.mSelected.removeAttribute("selected");
}
this.mSelected = aDay;
aDay.setAttribute("selected", "true");

View File

@ -227,7 +227,7 @@ function loadCalendarEventDialog()
// ALARMS ------------------------------------------------------------
if (!event.hasAlarm) {
if (event.alarmTime == null) {
menuListSelectItem("alarm-type", "none");
} else {
setElementValue("alarm-length-field", event.getProperty("alarmLength"));
@ -558,8 +558,6 @@ function onOKCommand()
// ALARMS ------------------------------------------------------------
var alarmType = getElementValue("alarm-type");
if (alarmType != "" && alarmType != "none") {
event.hasAlarm = true;
var alarmLength = getElementValue("alarm-length-field");
var alarmUnits = getElementValue("alarm-length-units");
var alarmRelated = getElementValue("alarm-trigger-relation");

View File

@ -263,6 +263,8 @@ mozStorageStatement::Reset()
PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Resetting statement: '%s'", nsPromiseFlatCString(mStatementString).get()));
sqlite3_reset(mDBStatement);
sqlite3_clear_bindings(mDBStatement);
mExecuting = PR_FALSE;
return NS_OK;

View File

@ -315,7 +315,7 @@ mozStorageStatementWrapper::Call(nsIXPConnectWrappedNative *wrapper, JSContext *
for (int i = 0; i < argc; i++) {
if (!JSValStorageStatementBinder(cx, mStatement, &i, 1, argv[i])) {
*_retval = PR_FALSE;
return NS_ERROR_FAILURE;
return NS_ERROR_INVALID_ARG;
}
}
@ -522,7 +522,7 @@ mozStorageStatementRow::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContex
double dval = sqlite3_column_double(NativeStatement(), i);
if (!JS_NewNumberValue(cx, dval, vp)) {
*_retval = PR_FALSE;
return NS_ERROR_FAILURE;
return NS_ERROR_OUT_OF_MEMORY;
}
} else if (ctype == SQLITE_TEXT) {
JSString *str = JS_NewUCStringCopyN(cx,
@ -530,7 +530,7 @@ mozStorageStatementRow::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContex
sqlite3_column_bytes16(NativeStatement(), i)/2);
if (!str) {
*_retval = PR_FALSE;
return NS_ERROR_FAILURE;
return NS_ERROR_OUT_OF_MEMORY;
}
*vp = STRING_TO_JSVAL(str);
} else if (ctype == SQLITE_BLOB) {
@ -539,7 +539,7 @@ mozStorageStatementRow::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContex
sqlite3_column_bytes(NativeStatement(), i));
if (!str) {
*_retval = PR_FALSE;
return NS_OK;
return NS_ERROR_OUT_OF_MEMORY;
}
} else if (ctype == SQLITE_NULL) {
*vp = JSVAL_NULL;
@ -788,10 +788,12 @@ mozStorageStatementParams::SetProperty(nsIXPConnectWrappedNative *wrapper, JSCon
}
} else {
*_retval = PR_FALSE;
return NS_ERROR_FAILURE;
}
return NS_OK;
if (*_retval)
return NS_OK;
else
return NS_ERROR_INVALID_ARG;
}
/* void preCreate (in nsISupports nativeObj, in JSContextPtr cx, in JSObjectPtr globalObj, out JSObjectPtr parentObj); */