releases-comm-central/calendar/base/content/calendar-base-view.xml

779 строки
30 KiB
XML

<?xml version="1.0"?>
<!--
- ***** 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 Sun Microsystems code.
-
- The Initial Developer of the Original Code is Sun Microsystems.
- Portions created by the Initial Developer are Copyright (C) 2008
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Berend Cornelius <berend.cornelius@sun.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 *****
-->
<bindings id="calendar-multiday-view-bindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="calendar-base-view">
<resources>
<stylesheet src="chrome://calendar/skin/calendar-alarms.css"/>
</resources>
<implementation>
<field name="mWeekStartOffset">0</field>
<field name="mRangeStartDate">null</field>;
<field name="mRangeEndDate">null</field>;
<field name="mWorkdaysOnly">false</field>
<field name="mRefreshQueue">[]</field>
<field name="mRefreshPending">null</field>
<field name="mCalendar">null</field>
<field name="mController">null</field>
<field name="mStartDate">null</field>
<field name="mEndDate">null</field>
<field name="mTasksInView">false</field>
<field name="mShowCompleted">false</field>
<field name="mDisplayDaysOff">true</field>
<field name="mDaysOffArray">[0,6]</field>
<field name="mTimezone">null</field>
<field name="mFlashingEvents">new Object()</field>
<field name="mSelectedItems">[]</field>
<field name="mBatchCount">0</field>
<field name="mLongWeekdayTotalPixels">-1</field>
<field name="mResizeHandler">null</field>
<field name="mPrefObserver"><![CDATA[
({ calView: this,
observe: function calViewPrefChange(subj, topic, pref) {
this.calView.handlePreference(subj, topic, pref);
return;
}
})
]]></field>
<field name="mOperationListener"><![CDATA[
({
calView: this,
onOperationComplete:
function onOperationComplete(aCalendar, aStatus, aOperationType,
aId, aDetail) {
// Fire event
this.calView.fireEvent('viewloaded', aOperationType);
// signal that the current operation finished.
this.calView.mRefreshPending = null;
// immediately start the next job on the queue.
this.calView.popRefreshQueue();
},
onGetResult:
function onGetResult(aCalendar, aStatus, aItemType, aDetail,
aCount, aItems) {
if (!Components.isSuccessCode(aStatus)) {
return;
}
let items = aItems.filter(function(x) !isToDo(x) ||
(x.entryDate && x.dueDate));
for each (let item in items) {
this.calView.doAddItem(item);
}
}
})
]]></field>
<field name="mObserver"><![CDATA[
// the calIObserver, calICompositeObserver, and calIAlarmServiceObserver
({
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, null, aIID,
[Components.interfaces.calIObserver,
Components.interfaces.calIAlarmServiceObserver,
Components.interfaces.calICompositeObserver,
Components.interfaces.nsISupports]);
},
calView: this,
onStartBatch: function onStartBatch() {
this.calView.mBatchCount++;
},
onEndBatch: function onEndBatch() {
this.calView.mBatchCount--;
if (this.calView.mBatchCount == 0) {
this.calView.refresh();
}
},
onLoad: function onLoad() {
this.calView.refresh();
},
onAddItem: function onAddItem(aItem) {
if (this.calView.mBatchCount) {
return;
}
if (isToDo(aItem)) {
if (!aItem.entryDate || !aItem.dueDate) {
return;
}
if(!this.calView.mTasksInView){
return;
}
if (aItem.isCompleted && !this.calView.mShowCompleted) {
return;
}
}
let occs = aItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (let occ in occs) {
this.calView.doAddItem(occ);
}
return;
},
onModifyItem: function onModifyItem(aNewItem, aOldItem) {
if (this.calView.mBatchCount) {
return;
}
if (isToDo(aNewItem) && isToDo(aOldItem) &&
!this.calView.mTasksInView) {
return;
}
let occs;
if (!isToDo(aOldItem) ||
(aOldItem.entryDate && aOldItem.dueDate)) {
occs = aOldItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (let occ in occs) {
this.calView.doDeleteItem(occ);
}
}
if (isToDo(aNewItem)) {
if (!aNewItem.entryDate || !aNewItem.dueDate || !this.calView.mTasksInView) {
return;
}
if (aNewItem.isCompleted && !this.calView.mShowCompleted) {
return;
}
}
occs = aNewItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (let occ in occs) {
this.calView.doAddItem(occ);
}
},
onDeleteItem: function onDeleteItem(aItem) {
if (this.calView.mBatchCount) {
return;
}
if (isToDo(aItem)) {
if (!this.calView.mTasksInView) {
return;
}
if (!aItem.entryDate || !aItem.dueDate) {
return;
}
if (aItem.isCompleted && !this.calView.mShowCompleted) {
return;
}
}
let occs = aItem.getOccurrencesBetween(this.calView.startDate,
this.calView.queryEndDate,
{});
for each (let occ in occs) {
this.calView.doDeleteItem(occ);
}
},
onError: function onError(aCalendar, aErrNo, aMessage) { },
onPropertyChanged: function(aCalendar, aName, aValue, aOldValue) {
switch (aName) {
case "suppressAlarms":
if (!getPrefSafe("calendar.alarms.indicator.show", true) ||
aCalendar.getProperty("capabilities.alarms.popup.supported") === false ) {
break;
}
// else fall through
case "readOnly":
case "disabled":
// XXXvv we can be smarter about how we handle this stuff
this.calView.refresh();
break;
}
},
onPropertyDeleting: function(aCalendar, aName) {
// Values are not important here yet.
this.onPropertyChanged(aCalendar, aName, null, null);
},
//
// calIAlarmServiceObserver stuff
//
onAlarm: function onAlarm(aAlarmItem) {
this.calView.flashAlarm(aAlarmItem, false);
},
onRemoveAlarmsByItem: function onRemoveAlarmsByItem(aItem) {
// Stop the flashing for the item.
this.calView.flashAlarm(aItem, true);
},
onRemoveAlarmsByCalendar: function onRemoveAlarmsByCalendar(aCalendar) {
// Stop the flashing for all items of this calendar
for each (let item in this.calView.mFlashingEvents) {
if (item.calendar.id == aCalendar.id) {
this.calView.flashAlarm(item, true);
}
}
},
//
// calICompositeObserver stuff
// XXXvv we can be smarter about how we handle this stuff
//
onCalendarAdded: function onCalendarAdded(aCalendar) {
if (!aCalendar.getProperty("disabled")) {
this.calView.refresh();
}
},
onCalendarRemoved: function onCalendarRemoved(aCalendar) {
if (!aCalendar.getProperty("disabled")) {
this.calView.refresh();
}
},
onDefaultCalendarChanged:
function onDefaultCalendarChanged(aNewDefaultCalendar) {
// don't care, for now
}
})
]]></field>
<constructor><![CDATA[
const kWorkdaysCommand = "calendar_toggle_workdays_only_command";
const kTasksInViewCommand = "calendar_toggle_tasks_in_view_command";
const kShowCompleted = "calendar_toggle_show_completed_in_view_command";
const kOrientation = "calendar_toggle_orientation_command";
this.workdaysOnly = (document.getElementById(kWorkdaysCommand)
.getAttribute("checked") == "true");
this.tasksInView = (document.getElementById(kTasksInViewCommand)
.getAttribute("checked") == "true");
this.rotated = (document.getElementById(kOrientation)
.getAttribute("checked") == "true");
this.showCompleted = (document.getElementById(kShowCompleted)
.getAttribute("checked") == "true");
this.mTimezone = calendarDefaultTimezone();
let alarmService = Components.classes['@mozilla.org/calendar/alarm-service;1']
.getService(Components.interfaces.calIAlarmService);
alarmService.addObserver(this.mObserver);
let self = this;
this.setAttribute("type", this.type);
this.mResizeHandler = function resizeHandler() {
self.onResize(self); };
this.viewBroadcaster.addEventListener(this.getAttribute("type") + "viewresized", this.mResizeHandler, true);
// add a preference observer to monitor changes
let pb2 = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch2);
pb2.addObserver("calendar.", this.mPrefObserver, false);
this.weekStartOffset = getPrefSafe("calendar.week.start", 0);
this.updateDaysOffPrefs();
]]></constructor>
<destructor><![CDATA[
if (this.mCalendar) {
this.mCalendar.removeObserver(this.mObserver);
}
let alarmService = Components.classes['@mozilla.org/calendar/alarm-service;1']
.getService(Components.interfaces.calIAlarmService);
alarmService.removeObserver(this.mObserver);
this.viewBroadcaster.removeEventListener(this.getAttribute("type") + "viewresized", this.mResizeHandler, true);
let pb2 = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch2);
pb2.removeObserver("calendar.", this.mPrefObserver);
]]></destructor>
<property name="type" readonly="true">
<getter><![CDATA[
let typelist = this.id.split("-");
return typelist[0];
]]></getter>
</property>
<property name="viewBroadcaster" readonly="true"
onget="return document.getElementById('calendarviewBroadcaster')"/>
<property name="labeldaybox" readonly="true"
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'labeldaybox')"/>
<property name="rotated"
onget="return (this.orient == 'horizontal')"
onset="return (this.orient = (val ? 'horizontal' : 'vertical'))"/>
<property name="displayDaysOff"
onget="return this.mDisplayDaysOff;"
onset="return (this.mDisplayDaysOff = val);"/>
<property name="controller"
onget="return this.mController;"
onset="return (this.mController = val);"/>
<property name="daysOffArray"
onget="return this.mDaysOffArray;"
onset="return (this.mDaysOffArray = val);"/>
<property name="tasksInView"
onget="return this.mTasksInView;"
onset="return (this.mTasksInView = val);"/>
<property name="showCompleted"
onget="return this.mShowCompleted;"
onset="return (this.mShowCompleted = val);"/>
<property name="timezone"
onget="return this.mTimezone;"
onset="return (this.mTimezone = val);"/>
<property name="workdaysOnly"
onget="return this.mWorkdaysOnly;"
onset="return (this.mWorkdaysOnly = val);"/>
<property name="selectionObserver" readonly="true"
onget="return this.mSelectionObserver;"/>
<property name="startDay" readonly="true"
onget="return this.startDate;"/>
<property name="endDay" readonly="true"
onget="return this.endDate;"/>
<property name="rangeStartDate"
onget="return this.mRangeStartDate;"
onset="return (this.mRangeStartDate = val);"/>
<property name="rangeEndDate"
onget="return this.mRangeEndDate;"
onset="return (this.mRangeEndDate = val);"/>
<property name="observerID" readonly="true"
onget="return 'base-view-observer';"/>
<property name="weekStartOffset">
<getter><![CDATA[
return this.mWeekStartOffset;
]]></getter>
<setter><![CDATA[
this.mWeekStartOffset = val;
return val;
]]></setter>
</property>
<property name="displayCalendar">
<getter><![CDATA[
return this.mCalendar;
]]></getter>
<setter><![CDATA[
if (this.mCalendar)
this.mCalendar.removeObserver(this.mObserver);
this.mCalendar = val;
this.mCalendar.addObserver(this.mObserver);
this.refresh();
return val;
]]></setter>
</property>
<property name="initialized">
<getter><![CDATA[
let retval;
// Some views throw when accessing an uninitialized startDay
try {
retval = this.displayCalendar && this.startDay
&& this.endDay;
} catch(ex) {
return false;
}
return retval;
]]></getter>
</property>
<method name="goToDay">
<parameter name="aDate"/>
<body><![CDATA[
this.showDate(aDate);
]]></body>
</method>
<method name="getRangeDescription">
<body><![CDATA[
return getDateFormatter().formatInterval(this.rangeStartDate, this.rangeEndDate);
]]></body>
</method>
<method name="popRefreshQueue">
<body><![CDATA[
let pendingRefresh = this.mRefreshPending;
if (pendingRefresh) {
if (calInstanceOf(pendingRefresh, Components.interfaces.calIOperation)) {
this.mRefreshPending = null;
pendingRefresh.cancel(null);
} else {
if(this.mRefreshQueue.length > 0) {
this.relayout();
}
return;
}
}
let refreshJob = this.mRefreshQueue.pop();
if (!refreshJob) {
return;
}
if (!this.startDate || !this.endDate) {
return;
}
// recreate our columns
this.relayout();
if (!this.mCalendar) {
return;
}
// start our items query; for a disjoint date range
// we get all the items, and just filter out the ones we don't
// care about in addItem
let filter = this.mCalendar.ITEM_FILTER_CLASS_OCCURRENCES;
if (this.mShowCompleted) {
filter |= this.mCalendar.ITEM_FILTER_COMPLETED_ALL;
} else {
filter |= this.mCalendar.ITEM_FILTER_COMPLETED_NO;
}
if(this.mTasksInView) {
filter |= this.mCalendar.ITEM_FILTER_TYPE_ALL;
} else {
filter |= this.mCalendar.ITEM_FILTER_TYPE_EVENT;
}
this.mRefreshPending = true;
pendingRefresh = this.mCalendar.getItems(filter,
0,
this.startDate,
this.queryEndDate,
this.mOperationListener);
if (pendingRefresh && pendingRefresh.isPending) { // support for calIOperation
this.mRefreshPending = pendingRefresh;
}
]]></body>
</method>
<!-- This function guarantees that the
labels are clipped in the instance that the overflow occurrs,
avoiding horizontal scrollbars from showing up for a short period. -->
<method name="adjustWeekdayLength">
<parameter name="forceShortName"/>
<body><![CDATA[
let useShortNames = false;
let labeldayboxkids = this.labeldaybox.childNodes;
if (!labeldayboxkids || !labeldayboxkids[0]) {
useShortNames = true;
} else if (forceShortName && forceShortName === true) {
useShortNames = true;
} else {
let clientWidth = document.getAnonymousElementByAttribute(this, "anonid", "mainbox").clientWidth;
let timespacer = document.getAnonymousElementByAttribute(this, "anonid", "headertimespacer");
if (timespacer) {
clientWidth -= timespacer.clientWidth;
}
if (this.getLongWeekdayTotalPixels() > 0.95 * clientWidth) {
useShortNames = true;
}
}
for (let i = 0; i < labeldayboxkids.length; i++) {
labeldayboxkids[i].shortWeekNames = useShortNames;
}
]]></body>
</method>
<method name="today">
<body><![CDATA[
let date = createDateTime();
date.jsDate = new Date();
date = date.getInTimezone(this.mTimezone);
date.isDate = true;
return date;
]]></body>
</method>
<method name="isVisible">
<body><![CDATA[
return (this.nodeName == currentView().nodeName);
]]></body>
</method>
<method name="refresh">
<body><![CDATA[
if (this.mBatchCount == 0 && this.isVisible()) {
var refreshJob = {};
this.mRefreshQueue.push(refreshJob);
this.popRefreshQueue();
}
]]></body>
</method>
<!-- the end date that should be used for getItems and similar queries -->
<property name="queryEndDate" readonly="true">
<getter><![CDATA[
let end = this.endDate;
if (!end) {
return null;
}
end = end.clone();
end.day += 1;
end.isDate = true;
return end;
]]></getter>
</property>
<method name="fireEvent">
<parameter name="aEventName"/>
<parameter name="aEventDetail"/>
<body><![CDATA[
let event = document.createEvent('Events');
event.initEvent(aEventName, true, false);
event.detail = aEventDetail;
this.dispatchEvent(event);
]]></body>
</method>
<method name="removeDropShadows">
<body><![CDATA[
let dropbox = document.getAnonymousElementByAttribute(this, "dropbox", "true");
if (dropbox && dropbox !== undefined) {
dropbox.setAttribute("dropbox", "false");
}
]]></body>
</method>
<method name="getLongWeekdayTotalPixels">
<body><![CDATA[
if (this.mLongWeekdayTotalPixels <= 0) {
let maxDayWidth = 0;
for each(label in this.labeldaybox.childNodes) {
if (label.localName == "calendar-day-label") {
label.shortWeekNames = false;
let curPixelLength = label.getLongWeekdayPixels();
maxDayWidth = Math.max(maxDayWidth, curPixelLength);
}
}
this.mLongWeekdayTotalPixels = (maxDayWidth * this.labeldaybox.childNodes.length) + 10;
}
return this.mLongWeekdayTotalPixels;
]]></body>
</method>
<!-- A preference handler which is called by the preference observer.
Can be overwritten in derived bindings. -->
<method name="handleCommonPreference">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aPreference"/>
<body><![CDATA[
// refresh view if categories seem to have changed
if (aPreference.indexOf("calendar.category.color") == 0) {
this.refreshView();
return;
}
switch (aPreference) {
case "calendar.week.d0sundaysoff":
case "calendar.week.d1mondaysoff":
case "calendar.week.d2tuesdaysoff":
case "calendar.week.d3wednesdaysoff":
case "calendar.week.d4thursdaysoff":
case "calendar.week.d5fridaysoff":
case "calendar.week.d6saturdaysoff":
this.updateDaysOffPrefs();
break;
case "calendar.timezone.local":
this.timezone = calendarDefaultTimezone();
this.refreshView();
break;
case "calendar.alarms.indicator.show":
//Break here to ensure the view is refreshed
break;
case "calendar.week.start":
this.weekStartOffset = aSubject.getIntPref(aPreference);
break;
case "calendar.date.format":
this.refreshView();
break;
default:
return;
}
this.refreshView();
]]></body>
</method>
<method name="updateDaysOffPrefs">
<body><![CDATA[
const weekPrefix = "calendar.week.";
const prefNames = ["d0sundaysoff", "d1mondaysoff", "d2tuesdaysoff",
"d3wednesdaysoff", "d4thursdaysoff",
"d5fridaysoff", "d6saturdaysoff"];
const defaults = ["true", "false", "false", "false",
"false", "false", "true"];
let daysOff = new Array();
for (let i in prefNames) {
if (getPrefSafe(weekPrefix+prefNames[i])) {
daysOff.push(Number(i));
}
}
this.daysOffArray = daysOff;
]]></body>
</method>
<method name="refreshView">
<body><![CDATA[
if (!this.startDay || !this.endDay) {
// don't refresh if we're not initialized
return;
}
// Just refresh, the goToDay function will notice
this.goToDay(this.selectedDay);
]]></body>
</method>
</implementation>
<handlers>
<handler event="move">
<![CDATA[
this.moveView(event.detail);
]]>
</handler>
<handler event="keypress"><![CDATA[
const kKE = Components.interfaces.nsIDOMKeyEvent;
switch (event.keyCode) {
case kKE.DOM_VK_PAGE_UP:
this.moveView(-1);
break;
case kKE.DOM_VK_PAGE_DOWN:
this.moveView(1);
break;
}
]]></handler>
<handler event="DOMMouseScroll"><![CDATA[
if (event.shiftKey) {
this.moveView(event.detail < 0 ? -1 : 1);
}
// Prevent default scroll handling
event.preventDefault();
]]></handler>
</handlers>
</binding>
<binding id="calendar-day-label" extends="xul:box">
<content flex="1" pack="center">
<xul:label anonid="longWeekdayName" class="calendar-day-label-name" xbl:inherits="selected"/>
<xul:label anonid="shortWeekdayName" class="calendar-day-label-name" hidden="true" xbl:inherits="selected"/>
</content>
<implementation>
<field name="mWeekday">-1</field>
<field name="longWeekdayPixels">0</field>
<field name="mDate">null</field>
<property name="longName" readonly="true"
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'longWeekdayName');"/>
<property name="shortName" readonly="true"
onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'shortWeekdayName');"/>
<property name="weekDay">
<getter>return this.mWeekday;</getter>
<setter><![CDATA[
this.mWeekday = val % 7;
this.longName.value = getDateFormatter().dayName(val);
this.shortName.value = getDateFormatter().shortDayName(val);
return this.mWeekday;
]]></setter>
</property>
<property name="date">
<getter><![CDATA[
return this.mDate;
]]></getter>
<setter><![CDATA[
this.mDate = val;
this.shortName.setAttribute("value", getDateFormatter().shortDayName(val.weekday) + " " +
getDateFormatter().formatDateWithoutYear(val));
this.longName.setAttribute("value", getDateFormatter().dayName(val.weekday) + " " +
getDateFormatter().formatDateWithoutYear(val));
return val;
]]></setter>
</property>
<property name="shortWeekNames">
<getter><![CDATA[
]]></getter>
<setter><![CDATA[
// cache before change, in case we are switching to short
this.getLongWeekdayPixels();
setBooleanAttribute(this.longName, "hidden", val);
setBooleanAttribute(this.shortName, "hidden", !val);
return val;
]]></setter>
</property>
<method name="getLongWeekdayPixels">
<body><![CDATA[
// Only do this if the long weekdays are visible and we haven't already cached.
let longNameWidth = this.longName.boxObject.width;
if (longNameWidth > 0) {
this.longWeekdayPixels = longNameWidth +
getSummarizedStyleValues(this.longName, ["margin-left", "margin-right"]);
this.longWeekdayPixels += getSummarizedStyleValues(this, ["border-left-width",
"padding-left", "padding-right"]);
return this.longWeekdayPixels;
} else {
// in this case the weekdaypixels have not yet been layouted;
// by definition 0 is returned
return 0;
}
]]></body>
</method>
</implementation>
</binding>
</bindings>