1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is Mozilla Calendar code.
15 *
16 * The Initial Developer of the Original Code is
17 * ArentJan Banck <ajbanck@planet.nl>.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s): ArentJan Banck <ajbanck@planet.nl>
22 * Joey Minta <jminta@gmail.com>
23 * Philipp Kewisch <mozilla@kewis.ch>
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39 /***** calendarClipboard
40 *
41 * NOTES
42 * TODO items
43 * - Add a clipboard listener, to enable/disable menu-items depending if
44 * valid clipboard data is available.
45 *
46 ******/
47
48
getClipboard
49 function getClipboard()
50 {
51 const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
52 const kClipboardIID = Components.interfaces.nsIClipboard;
53 return Components.classes[kClipboardContractID].getService(kClipboardIID);
54 }
55
56 var Transferable = Components.Constructor("@mozilla.org/widget/transferable;1", Components.interfaces.nsITransferable);
57 var SupportsArray = Components.Constructor("@mozilla.org/supports-array;1", Components.interfaces.nsISupportsArray);
58 var SupportsCString = (("nsISupportsCString" in Components.interfaces)
59 ? Components.Constructor("@mozilla.org/supports-cstring;1", Components.interfaces.nsISupportsCString)
60 : Components.Constructor("@mozilla.org/supports-string;1", Components.interfaces.nsISupportsString)
61 );
62 var SupportsString = (("nsISupportsWString" in Components.interfaces)
63 ? Components.Constructor("@mozilla.org/supports-wstring;1", Components.interfaces.nsISupportsWString)
64 : Components.Constructor("@mozilla.org/supports-string;1", Components.interfaces.nsISupportsString)
65 );
66
67 /**
68 * Test if the clipboard has items that can be pasted into Calendar.
69 * This must be of type "text/calendar" or "text/unicode"
70 */
71
canPaste
72 function canPaste()
73 {
74 const kClipboardIID = Components.interfaces.nsIClipboard;
75
76 var clipboard = getClipboard();
77 var flavourArray = new SupportsArray;
78 var flavours = ["text/calendar", "text/unicode"];
79
80 if (kClipboardIID.number == "{8b5314ba-db01-11d2-96ce-0060b0fb9956}") { // on branch
81 for (var i = 0; i < flavours.length; ++i) {
82 var kSuppString = new SupportsCString;
83 kSuppString.data = flavours[i];
84 flavourArray.AppendElement(kSuppString);
85 }
86 return clipboard.hasDataMatchingFlavors(flavourArray,
87 kClipboardIID.kGlobalClipboard);
88 } else {
89 return clipboard.hasDataMatchingFlavors(flavours, flavours.length,
90 kClipboardIID.kGlobalClipboard);
91 }
92 }
93
94 /**
95 * Copy iCalendar data to the Clipboard, and delete the selected events.
96 * Does not use eventarray parameter, because DeletCcommand delete selected events.
97 */
98
cutToClipboard
99 function cutToClipboard( /* calendarEventArray */)
100 {
101 var calendarEventArray = currentView().getSelectedItems({});
102
103 if( copyToClipboard( calendarEventArray ) )
104 {
105 deleteSelectedEvents();
106 }
107 }
108
109
110 /**
111 * Copy iCalendar data to the Clipboard. The data is copied to both
112 * text/calendar and text/unicode.
113 **/
114
copyToClipboard
115 function copyToClipboard( calendarItemArray )
116 {
117 if (!calendarItemArray) {
118 calendarItemArray = currentView().getSelectedItems({});
119 }
120
121 if (!calendarItemArray.length) {
122 dump("Tried to cut/copy 0 events");
123 return false;
124 }
125
126 var calComp = getIcsService().createIcalComponent("VCALENDAR");
127 calSetProdidVersion(calComp);
128
129 for each (item in calendarItemArray) {
130 // If we copy an item and paste it again, it will have the same ID as
131 // the original. Therefore, give every item a new ID.
132 var dummyItem = Components.classes["@mozilla.org/calendar/event;1"]
133 .createInstance(Components.interfaces.calIEvent);
134 var newItem = item.clone();
135 newItem.id = dummyItem.id;
136 calComp.addSubcomponent(newItem.icalComponent);
137 }
138
139 // XXX This might not be enough to be Outlook compatible
140 var sTextiCalendar = calComp.serializeToICS();
141
142 // 1. get the clipboard service
143 var clipboard = getClipboard();
144
145 // 2. create the transferable
146 var trans = new Transferable;
147
148 if ( trans && clipboard) {
149 // 3. register the data flavors
150 trans.addDataFlavor("text/calendar");
151 trans.addDataFlavor("text/unicode");
152
153 // 4. create the data objects
154 var icalWrapper = new SupportsString;
155
156 // get the data
157 icalWrapper.data = sTextiCalendar;
158
159 // 5. add data objects to transferable
160 // Both Outlook 2000 client and Lotus Organizer use text/unicode
161 // when pasting iCalendar data
162 trans.setTransferData("text/calendar", icalWrapper,
163 icalWrapper.data.length*2 ); // double byte data
164 trans.setTransferData("text/unicode", icalWrapper,
165 icalWrapper.data.length*2 );
166
167 clipboard.setData(trans, null,
168 Components.interfaces.nsIClipboard.kGlobalClipboard );
169
170 return true;
171 }
172 return true;
173 }
174
175
176 /**
177 * Paste iCalendar events from the clipboard,
178 * or paste clipboard text into description of new event
179 */
180
pasteFromClipboard
181 function pasteFromClipboard()
182 {
183 if (!canPaste()) {
184 dump("Attempting to paste with no useful data on the clipboard");
185 return;
186 }
187
188 // 1. get the clipboard service
189 var clipboard = getClipboard();
190
191 // 2. create the transferable
192 var trans = new Transferable;
193
194 if (!trans || !clipboard) {
195 dump("Failed to get either a transferable or a clipboard");
196 return;
197 }
198 // 3. register the data flavors you want, highest fidelity first!
199 trans.addDataFlavor("text/calendar");
200 trans.addDataFlavor("text/unicode");
201
202 // 4. get transferable from clipboard
203 clipboard.getData ( trans, Components.interfaces.nsIClipboard.kGlobalClipboard);
204
205 // 5. ask transferable for the best flavor. Need to create new JS
206 // objects for the out params.
207 var flavour = { };
208 var data = { };
209 trans.getAnyTransferData(flavour, data, {});
210 data = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
211 var items = new Array();
212 switch (flavour.value) {
213 case "text/calendar":
214 case "text/unicode":
215 // Moving this test up before processing
216 var destCal = getSelectedCalendar();
217 if (!destCal) {
218 return;
219 }
220 var calComp = getIcsService().parseICS(data, null);
221 var subComp = calComp.getFirstSubcomponent("ANY");
222 while (subComp) {
223 switch (subComp.componentType) {
224 case "VEVENT":
225 var event = Components.classes["@mozilla.org/calendar/event;1"]
226 .createInstance
227 (Components.interfaces.calIEvent);
228 event.icalComponent = subComp;
229 items.push(event);
230 break;
231 case "VTODO":
232 var todo = Components.classes["@mozilla.org/calendar/todo;1"]
233 .createInstance
234 (Components.interfaces.calITodo);
235 todo.icalComponent = subComp;
236 items.push(todo);
237 break;
238 default: break;
239 }
240 subComp = calComp.getNextSubcomponent("ANY");
241 }
242 // If there are multiple items on the clipboard, the earliest
243 // should be set to the selected day and the rest adjusted.
244 var earliestDate = null;
245 for each(item in items) {
246 var date = null;
247 if (item.startDate)
248 date = item.startDate.clone();
249 else if (item.entryDate)
250 date = item.entryDate.clone();
251 else if (item.dueDate)
252 date = item.dueDate.clone();
253
254 if (!date)
255 continue;
256 if (!earliestDate || date.compare(earliestDate) < 0)
257 earliestDate = date;
258 }
259 var firstDate = currentView().selectedDay;
260
261 // Timezones and DT/DST time may differ between the earliest item
262 // and the selected day. Determine the offset between the
263 // earliestDate in local time and the selected day in whole days.
264 earliestDate = earliestDate.getInTimezone(calendarDefaultTimezone());
265 earliestDate.isDate = true;
266 var offset = firstDate.subtractDate(earliestDate);
267 var deltaDST = firstDate.timezoneOffset - earliestDate.timezoneOffset;
268 offset.inSeconds += deltaDST;
269
270 startBatchTransaction();
271 for each(item in items) {
272 var newItem = item.clone();
273 if (item.startDate) {
274 newItem.startDate.addDuration(offset);
275 newItem.endDate.addDuration(offset);
276 } else {
277 if (item.entryDate) {
278 newItem.entryDate.addDuration(offset);
279 }
280 if (item.dueDate) {
281 newItem.dueDate.addDuration(offset);
282 }
283 }
284 doTransaction('add', newItem, destCal, null, null);
285 }
286 endBatchTransaction();
287 break;
288 default:
289 dump("Unknown clipboard type: " + flavour.value);
290 }
291 }