001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.openide.util.datatransfer;
042:
043: import java.awt.datatransfer.*;
044:
045: import javax.swing.event.EventListenerList;
046:
047: // and holding a list of services built over data flavors. [???]
048:
049: /** Extended clipboard that supports listeners that can be notified about
050: * changes of content. Also contains support for attaching content convertors.
051: *
052: * @author Jaroslav Tulach
053: */
054: public abstract class ExClipboard extends Clipboard {
055: /** listeners */
056: private EventListenerList listeners = new EventListenerList();
057:
058: /** Make a new clipboard.
059: * @param name name of the clipboard
060: */
061: public ExClipboard(String name) {
062: super (name);
063: }
064:
065: /** Add a listener to clipboard operations.
066: * @param list the listener
067: */
068: public final void addClipboardListener(ClipboardListener list) {
069: listeners.add(ClipboardListener.class, list);
070: }
071:
072: /** Remove a listener to clipboard operations.
073: * @param list the listener
074: */
075: public final void removeClipboardListener(ClipboardListener list) {
076: listeners.remove(ClipboardListener.class, list);
077: }
078:
079: /** Fires event about change of content in the clipboard.
080: */
081: protected final void fireClipboardChange() {
082: Object[] l = listeners.getListenerList();
083: ClipboardEvent ev = null;
084:
085: for (int i = l.length - 2; i >= 0; i -= 2) {
086: ClipboardListener list = (ClipboardListener) l[i + 1];
087:
088: if (ev == null) {
089: ev = new ClipboardEvent(this );
090: }
091:
092: list.clipboardChanged(ev);
093: }
094: }
095:
096: /** Obtain a list of convertors assigned to
097: * this clipboard.
098: * @return the convertors
099: */
100: protected abstract Convertor[] getConvertors();
101:
102: /** Method that takes a transferable, applies all convertors,
103: * and creates a new transferable using the abilities of the
104: * convertors.
105: * <P>
106: * This method is used when the contents of the clipboard are changed and
107: * also can be used by Drag & Drop to process transferables between source
108: * and target.
109: * <p>
110: * Note that it is possible for the results to vary according to order
111: * of the convertors as specified by {@link #getConvertors}. For example,
112: * the input transferable may contain flavor A, and there may be a convertor
113: * from A to B, and one from B to C; flavor B will always be available, but
114: * flavor C will only be available if the convertor list is in the order
115: * that these were mentioned. Since the standard clipboard implementation
116: * searches for convertors in lookup as well as manifests, ordering might be
117: * specified between a set of layer-supplied convertors by means of folder
118: * ordering attributes.
119: *
120: * @param t input transferable
121: * @return new transferable
122: */
123: public Transferable convert(Transferable t) {
124: Convertor[] convertors = getConvertors();
125:
126: for (int i = 0; i < convertors.length; i++) {
127: if (t == null) {
128: return null;
129: }
130:
131: t = convertors[i].convert(t);
132: }
133:
134: return t;
135: }
136:
137: /** Notifies the transferable that it has been accepted by a drop.
138: * Works only for ExTransferable, other types of transferables are
139: * not notified.
140: *
141: * @param t transferable to notify its listeners
142: * @param action which action has been performed
143: */
144: public static void transferableAccepted(Transferable t, int action) {
145: if (t instanceof ExTransferable) {
146: ((ExTransferable) t).fireAccepted(action);
147: } else if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
148: try {
149: MultiTransferObject mto = (MultiTransferObject) t
150: .getTransferData(ExTransferable.multiFlavor);
151: int cnt = mto.getCount();
152:
153: for (int i = 0; i < cnt; i++) {
154: transferableAccepted(mto.getTransferableAt(i),
155: action);
156: }
157: } catch (Exception e) {
158: // shouldn't occure
159: }
160: }
161: }
162:
163: /** Notifies the transferable that it has been rejected by a drop.
164: * Works only for ExTransferable, other types of transferables are
165: * not notified.
166: *
167: * @param t transferable to notify its listeners
168: */
169: public static void transferableRejected(Transferable t) {
170: if (t instanceof ExTransferable) {
171: ((ExTransferable) t).fireRejected();
172: } else if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
173: try {
174: MultiTransferObject mto = (MultiTransferObject) t
175: .getTransferData(ExTransferable.multiFlavor);
176: int cnt = mto.getCount();
177:
178: for (int i = 0; i < cnt; i++) {
179: transferableRejected(mto.getTransferableAt(i));
180: }
181: } catch (Exception e) {
182: // shouldn't occure
183: }
184: }
185: }
186:
187: public synchronized void setContents(Transferable contents,
188: ClipboardOwner owner) {
189: if (this .contents != null) {
190: transferableOwnershipLost(this .contents);
191: }
192:
193: super .setContents(contents, owner);
194: }
195:
196: /** Notifies the transferable that it has lost ownership in clipboard.
197: * Works only for ExTransferable, other types of transferables are
198: * not notified.
199: *
200: * @param t transferable to notify its listeners
201: */
202: public static void transferableOwnershipLost(Transferable t) {
203: if (t instanceof ExTransferable) {
204: ((ExTransferable) t).fireOwnershipLost();
205: } else if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
206: try {
207: MultiTransferObject mto = (MultiTransferObject) t
208: .getTransferData(ExTransferable.multiFlavor);
209: int cnt = mto.getCount();
210:
211: for (int i = 0; i < cnt; i++) {
212: transferableOwnershipLost(mto.getTransferableAt(i));
213: }
214: } catch (Exception e) {
215: // shouldn't occure
216: }
217: }
218: }
219:
220: /** Convertor that can convert the {@link Transferable contents} of a clipboard to
221: * additional {@link DataFlavor flavors}.
222: */
223: public interface Convertor {
224: /** Convert a given transferable to a new transferable,
225: * generally one which adds new flavors based on the existing flavors.
226: * The recommended usage is as follows:
227: *
228: * <br><code><pre>
229: * public Transferable convert (final Transferable t) {
230: * if (! t.isDataFlavorSupported (fromFlavor)) return t;
231: * if (t.isDataFlavorSupported (toFlavor)) return t;
232: * ExTransferable et = ExTransferable.create (t);
233: * et.put (new ExTransferable.Single (toFlavor) {
234: * public Object getData () throws IOException, UnsupportedFlavorException {
235: * FromObject from = (FromObject) t.getTransferData (fromFlavor);
236: * ToObject to = translateFormats (from);
237: * return to;
238: * }
239: * });
240: * return et;
241: * }
242: * </pre></code>
243: *
244: * <br>Note that this does not perform the conversion until <code>toFlavor</code> is
245: * actually requested, nor does it advertise <code>toFlavor</code> as being available
246: * unless <code>fromFlavor</code> already was.
247: *
248: * <p>You may also want to convert some flavor to a node selection, in which case you should do:
249: *
250: * <br><code><pre>
251: * public Transferable convert (final Transferable t) {
252: * if (! t.isDataFlavorSupported (DataFlavor.stringFlavor)) return t;
253: * if (NodeTransfer.findPaste (t) != null) return t;
254: * ExTransferable et = ExTransferable.create (t);
255: * et.put (NodeTransfer.createPaste (new NodeTransfer.Paste () {
256: * public PasteType[] types (Node target) {
257: * if (isSuitable (target)) {
258: * return new PasteType[] { new PasteType () {
259: * public Transferable paste () throws IOException {
260: * try {
261: * String s = (String) t.getTransferData (DataFlavor.stringFlavor);
262: * addNewSubnode (target, s);
263: * } catch (UnsupportedFlavorException ufe) {
264: * throw new IOException (ufe.toString ());
265: * }
266: * return t;
267: * }
268: * }};
269: * } else {
270: * return new PasteType[0];
271: * }
272: * }
273: * }));
274: * return et;
275: * }
276: * </pre></code>
277: *
278: * <p>Convertors should generally avoid removing flavors from the transferable,
279: * or changing the data for an existing flavor.
280: *
281: * @param t the incoming basic transferable
282: * @return a possible enhanced transferable
283: */
284: public Transferable convert(Transferable t);
285: }
286: }
|