0N/A/*
2362N/A * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.X11;
0N/A
0N/Aimport java.awt.datatransfer.Transferable;
0N/Aimport java.util.SortedMap;
0N/Aimport java.io.IOException;
0N/Aimport java.security.AccessController;
94N/Aimport java.util.HashMap;
94N/Aimport java.util.Map;
94N/Aimport sun.awt.UNIXToolkit;
0N/Aimport sun.awt.datatransfer.DataTransferer;
0N/Aimport sun.awt.datatransfer.SunClipboard;
0N/Aimport sun.awt.datatransfer.ClipboardTransferable;
0N/Aimport sun.security.action.GetIntegerAction;
0N/A
0N/A/**
0N/A * A class which interfaces with the X11 selection service in order to support
0N/A * data transfer via Clipboard operations.
0N/A */
94N/Apublic final class XClipboard extends SunClipboard implements OwnershipListener
94N/A{
0N/A private final XSelection selection;
94N/A // Time of calling XConvertSelection().
94N/A private long convertSelectionTime;
94N/A // The flag used not to call XConvertSelection() if the previous SelectionNotify
94N/A // has not been processed by checkChange().
94N/A private volatile boolean isSelectionNotifyProcessed;
94N/A // The property in which the owner should place requested targets
94N/A // when tracking changes of available data flavors (practically targets).
94N/A private volatile XAtom targetsPropertyAtom;
0N/A
0N/A private static final Object classLock = new Object();
0N/A
0N/A private static final int defaultPollInterval = 200;
0N/A
0N/A private static int pollInterval;
0N/A
94N/A private static Map<Long, XClipboard> targetsAtom2Clipboard;
0N/A
0N/A /**
0N/A * Creates a system clipboard object.
0N/A */
0N/A public XClipboard(String name, String selectionName) {
0N/A super(name);
94N/A selection = new XSelection(XAtom.get(selectionName));
94N/A selection.registerOwershipListener(this);
0N/A }
0N/A
94N/A /*
0N/A * NOTE: This method may be called by privileged threads.
0N/A * DO NOT INVOKE CLIENT CODE ON THIS THREAD!
0N/A */
94N/A public void ownershipChanged(final boolean isOwner) {
94N/A if (isOwner) {
94N/A checkChangeHere(contents);
94N/A } else {
94N/A lostOwnershipImpl();
94N/A }
0N/A }
0N/A
0N/A protected synchronized void setContentsNative(Transferable contents) {
0N/A SortedMap formatMap = DataTransferer.getInstance().getFormatsForTransferable
0N/A (contents, DataTransferer.adaptFlavorMap(flavorMap));
94N/A long[] formats = DataTransferer.keysToLongArray(formatMap);
0N/A
0N/A if (!selection.setOwner(contents, formatMap, formats,
0N/A XToolkit.getCurrentServerTime())) {
0N/A this.owner = null;
0N/A this.contents = null;
0N/A }
0N/A }
0N/A
0N/A public long getID() {
0N/A return selection.getSelectionAtom().getAtom();
0N/A }
0N/A
94N/A @Override
0N/A public synchronized Transferable getContents(Object requestor) {
0N/A if (contents != null) {
0N/A return contents;
0N/A }
0N/A return new ClipboardTransferable(this);
0N/A }
0N/A
0N/A /* Caller is synchronized on this. */
0N/A protected void clearNativeContext() {
0N/A selection.reset();
0N/A }
0N/A
0N/A
0N/A protected long[] getClipboardFormats() {
0N/A return selection.getTargets(XToolkit.getCurrentServerTime());
0N/A }
0N/A
0N/A protected byte[] getClipboardData(long format) throws IOException {
0N/A return selection.getData(format, XToolkit.getCurrentServerTime());
0N/A }
0N/A
94N/A private void checkChangeHere(Transferable contents) {
0N/A if (areFlavorListenersRegistered()) {
94N/A checkChange(DataTransferer.getInstance().
0N/A getFormatsForTransferableAsArray(contents, flavorMap));
0N/A }
0N/A }
0N/A
94N/A private static int getPollInterval() {
94N/A synchronized (XClipboard.classLock) {
94N/A if (pollInterval <= 0) {
94N/A pollInterval = AccessController.doPrivileged(
94N/A new GetIntegerAction("awt.datatransfer.clipboard.poll.interval",
94N/A defaultPollInterval));
94N/A if (pollInterval <= 0) {
94N/A pollInterval = defaultPollInterval;
94N/A }
94N/A }
94N/A return pollInterval;
94N/A }
94N/A }
94N/A
94N/A private XAtom getTargetsPropertyAtom() {
94N/A if (null == targetsPropertyAtom) {
94N/A targetsPropertyAtom =
94N/A XAtom.get("XAWT_TARGETS_OF_SELECTION:" + selection.getSelectionAtom().getName());
94N/A }
94N/A return targetsPropertyAtom;
94N/A }
94N/A
0N/A protected void registerClipboardViewerChecked() {
94N/A // for XConvertSelection() to be called for the first time in getTargetsDelayed()
94N/A isSelectionNotifyProcessed = true;
94N/A
94N/A boolean mustSchedule = false;
94N/A synchronized (XClipboard.classLock) {
94N/A if (targetsAtom2Clipboard == null) {
94N/A targetsAtom2Clipboard = new HashMap<Long, XClipboard>(2);
94N/A }
94N/A mustSchedule = targetsAtom2Clipboard.isEmpty();
94N/A targetsAtom2Clipboard.put(getTargetsPropertyAtom().getAtom(), this);
94N/A if (mustSchedule) {
94N/A XToolkit.addEventDispatcher(XWindow.getXAWTRootWindow().getWindow(),
94N/A new SelectionNotifyHandler());
0N/A }
0N/A }
0N/A if (mustSchedule) {
94N/A XToolkit.schedule(new CheckChangeTimerTask(), XClipboard.getPollInterval());
0N/A }
0N/A }
0N/A
0N/A private static class CheckChangeTimerTask implements Runnable {
0N/A public void run() {
94N/A for (XClipboard clpbrd : targetsAtom2Clipboard.values()) {
94N/A clpbrd.getTargetsDelayed();
0N/A }
0N/A synchronized (XClipboard.classLock) {
94N/A if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) {
94N/A XToolkit.schedule(this, XClipboard.getPollInterval());
94N/A }
94N/A }
94N/A }
94N/A }
94N/A
94N/A private static class SelectionNotifyHandler implements XEventDispatcher {
94N/A public void dispatchEvent(XEvent ev) {
216N/A if (ev.get_type() == XConstants.SelectionNotify) {
94N/A final XSelectionEvent xse = ev.get_xselection();
94N/A XClipboard clipboard = null;
94N/A synchronized (XClipboard.classLock) {
94N/A if (targetsAtom2Clipboard != null && !targetsAtom2Clipboard.isEmpty()) {
94N/A XToolkit.removeEventDispatcher(XWindow.getXAWTRootWindow().getWindow(), this);
94N/A return;
94N/A }
94N/A final long propertyAtom = xse.get_property();
94N/A clipboard = targetsAtom2Clipboard.get(propertyAtom);
94N/A }
94N/A if (null != clipboard) {
94N/A clipboard.checkChange(xse);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A protected void unregisterClipboardViewerChecked() {
94N/A isSelectionNotifyProcessed = false;
0N/A synchronized (XClipboard.classLock) {
94N/A targetsAtom2Clipboard.remove(getTargetsPropertyAtom().getAtom());
94N/A }
94N/A }
94N/A
94N/A // checkChange() will be called on SelectionNotify
94N/A private void getTargetsDelayed() {
94N/A XToolkit.awtLock();
94N/A try {
94N/A long curTime = System.currentTimeMillis();
94N/A if (isSelectionNotifyProcessed || curTime >= (convertSelectionTime + UNIXToolkit.getDatatransferTimeout()))
94N/A {
94N/A convertSelectionTime = curTime;
94N/A XlibWrapper.XConvertSelection(XToolkit.getDisplay(),
94N/A selection.getSelectionAtom().getAtom(),
94N/A XDataTransferer.TARGETS_ATOM.getAtom(),
94N/A getTargetsPropertyAtom().getAtom(),
94N/A XWindow.getXAWTRootWindow().getWindow(),
216N/A XConstants.CurrentTime);
94N/A isSelectionNotifyProcessed = false;
94N/A }
94N/A } finally {
94N/A XToolkit.awtUnlock();
0N/A }
0N/A }
0N/A
94N/A /*
94N/A * Tracks changes of available formats.
94N/A * NOTE: This method may be called by privileged threads.
94N/A * DO NOT INVOKE CLIENT CODE ON THIS THREAD!
94N/A */
94N/A private void checkChange(XSelectionEvent xse) {
94N/A final long propertyAtom = xse.get_property();
94N/A if (propertyAtom != getTargetsPropertyAtom().getAtom()) {
94N/A // wrong atom
94N/A return;
94N/A }
94N/A
94N/A final XAtom selectionAtom = XAtom.get(xse.get_selection());
94N/A final XSelection changedSelection = XSelection.getSelection(selectionAtom);
94N/A
94N/A if (null == changedSelection || changedSelection != selection) {
94N/A // unknown selection - do nothing
94N/A return;
94N/A }
94N/A
94N/A isSelectionNotifyProcessed = true;
94N/A
94N/A if (selection.isOwner()) {
94N/A // selection is owner - do not need formats
94N/A return;
94N/A }
94N/A
94N/A long[] formats = null;
94N/A
216N/A if (propertyAtom == XConstants.None) {
94N/A // We treat None property atom as "empty selection".
94N/A formats = new long[0];
94N/A } else {
94N/A WindowPropertyGetter targetsGetter =
94N/A new WindowPropertyGetter(XWindow.getXAWTRootWindow().getWindow(),
94N/A XAtom.get(propertyAtom), 0,
94N/A XSelection.MAX_LENGTH, true,
216N/A XConstants.AnyPropertyType);
94N/A try {
94N/A targetsGetter.execute();
94N/A formats = XSelection.getFormats(targetsGetter);
94N/A } finally {
94N/A targetsGetter.dispose();
94N/A }
94N/A }
94N/A
94N/A checkChange(formats);
94N/A }
0N/A}