/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1990-95 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.1 $
|
| Classes : SoXtClipboard
|
| Author(s) : David Mott
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <Inventor/SbDict.h>
#include <Inventor/misc/SoByteStream.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/SoPath.h>
#include <Inventor/Xt/SoXtClipboard.h>
#include <Inventor/errors/SoDebugError.h>
#include "SoXtImportInterest.h"
#include <Xm/AtomMgr.h>
#include <X11/StringDefs.h>
// This list will contain SoXtClipboard pointers.
// The key into this dictionary is an X selection (e.g. XA_PRIMARY),
// and the return value is which SoXtClipboard owns that selection, if any.
// We need this because our import/export functions are static, and
// thus are not tied to any one SoXtClipboard class instance.
SbDict *SoXtClipboard::selOwnerList = NULL;
#define _ATOM_(widget,name) (XmInternAtom(XtDisplay(widget),name,False))
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor
//
SoXtClipboard::SoXtClipboard(
Widget w, // widget for copy/paste data transfers
Atom selectionAtom) // the x selection: XA_SECONDARY, etc.
//
////////////////////////////////////////////////////////////////////////
{
if (selOwnerList == NULL)
selOwnerList = new SbDict;
// _XA_CLIPBOARD_ lets us know to use the X internal atom "CLIPBOARD".
if (selectionAtom == _XA_CLIPBOARD_) {
selectionAtom = XmInternAtom(XtDisplay(w), "CLIPBOARD", False);
}
widget = w;
clipboardAtom = selectionAtom;
eventTime = CurrentTime;
// copyBuffer is used to transfer copy/paste data in binary format.
copyBuffer = NULL;
// Default paste interest.
pasteInterest = new SoXtImportInterestList;
pasteInterest->append(_ATOM_(widget, "INVENTOR_2_1"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "INVENTOR_2_1_FILE"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "VRML_1_0"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "VRML_1_0_FILE"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "INVENTOR"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "INVENTOR_FILE"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "INVENTOR_2_0"), pasteImportCB, this);
pasteInterest->append(_ATOM_(widget, "INVENTOR_2_0_FILE"), pasteImportCB, this);
pasteInterest->append(XA_STRING, pasteImportCB, this);
// Default copy interest
copyInterest = new SbPList();
copyInterest->append((void *)_ATOM_(widget, "INVENTOR_2_1"));
copyInterest->append((void *)_ATOM_(widget, "INVENTOR_2_1_FILE"));
copyInterest->append((void *)_ATOM_(widget, "VRML_1_0"));
copyInterest->append((void *)_ATOM_(widget, "VRML_1_0_FILE"));
copyInterest->append((void *)_ATOM_(widget, "INVENTOR"));
copyInterest->append((void *)_ATOM_(widget, "INVENTOR_FILE"));
copyInterest->append((void *)_ATOM_(widget, "INVENTOR_2_0"));
copyInterest->append((void *)_ATOM_(widget, "INVENTOR_2_0_FILE"));
copyInterest->append((void *) XA_STRING);
// By default, don't pass empty path lists to callbacks
emptyListOK = FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor - if we own the X selection, lose it.
//
SoXtClipboard::~SoXtClipboard()
//
////////////////////////////////////////////////////////////////////////
{
// see if we own the selection
void *data = NULL;
selOwnerList->find(clipboardAtom, data);
if ((SoXtClipboard *)data == this) {
// we own the selection. we should support persistence (i.e. the
// data should be around for someone to paste even after we are
// destroyed), but we don't, so lose the selection.
XtDisownSelection(widget, clipboardAtom, CurrentTime);
selOwnerList->remove(clipboardAtom);
}
delete pasteInterest;
delete copyBuffer;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Copy the byte stream into our copyBuffer, and tell the X server
// that we now own the X selection identified by clipboardAtom (clipboardAtom was
// passed during construction).
//
// Use: protected
//
void
SoXtClipboard::copy(SoByteStream *byteStream, Time t)
//
////////////////////////////////////////////////////////////////////////
{
eventTime = t;
delete copyBuffer;
copyBuffer = byteStream;
copyDataType = _ATOM_(widget, "INVENTOR_2_1");
// SoWWWInline won't write it's data out unless the alternateRep field is set.
// Run through each node in each path, and do this so that the resulting file
// has all the data it can have.
//???
// We copy Inventor 2.1 data.
// We will convert to other formats on request.
if (copyBuffer != NULL) {
// tell the X server that we own the selection
XtOwnSelection(widget,
clipboardAtom,
eventTime,
(XtConvertSelectionProc) SoXtClipboard::exportSelection,
(XtLoseSelectionProc) SoXtClipboard::loseSelection,
(XtSelectionDoneProc) NULL);
// make sure we own the selection
if (XGetSelectionOwner(XtDisplay(widget), clipboardAtom) == XtWindow(widget)) {
// we were granted ownership - associate 'this' with the
// selection atom in our dictionary of X selection owners
selOwnerList->enter(clipboardAtom, this);
}
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Copy the users data into our copyBuffer, and tell the X server
// that we now own the X selection identified by clipboardAtom (clipboardAtom was
// passed during construction). The data is not necessarily Inventor.
//
// Use: protected
//
void
SoXtClipboard::copy(Atom dataType, void *data, uint32_t numBytes, Time t)
//
////////////////////////////////////////////////////////////////////////
{
eventTime = t;
delete copyBuffer;
copyBuffer = new SoByteStream();
copyBuffer->copy(data, numBytes);
copyDataType = dataType;
// This is unknown data to us...
if (data != NULL && numBytes > 0) {
// tell the X server that we own the selection
XtOwnSelection(widget,
clipboardAtom,
eventTime,
(XtConvertSelectionProc) SoXtClipboard::exportSelection,
(XtLoseSelectionProc) SoXtClipboard::loseSelection,
(XtSelectionDoneProc) NULL);
// make sure we own the selection
if (XGetSelectionOwner(XtDisplay(widget), clipboardAtom) == XtWindow(widget)) {
// we were granted ownership - associate 'this' with the
// selection atom in our dictionary of X selection owners
selOwnerList->enter(clipboardAtom, this);
}
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Copy the path list onto this clipboard.
//
// Use: public
//
void
SoXtClipboard::copy(SoPathList *pathList, Time t)
//
////////////////////////////////////////////////////////////////////////
{
SoByteStream *bs = new SoByteStream;
bs->convert(pathList);
copy(bs, t);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Copy the path onto this clipboard.
//
// Use: public
//
void
SoXtClipboard::copy(SoPath *path, Time t)
//
////////////////////////////////////////////////////////////////////////
{
SoByteStream *bs = new SoByteStream;
bs->convert(path);
copy(bs, t);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Copy the node onto this clipboard.
//
// Use: public
//
void
SoXtClipboard::copy(SoNode *node, Time t)
//
////////////////////////////////////////////////////////////////////////
{
SoByteStream *bs = new SoByteStream;
bs->convert(node);
copy(bs, t);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Paste whatever is in the X selection identified by clipboardAtom.
// We do this by requesting the selection data from the X server,
// then return to the main event loop. We will be notified via an event
// when the data is ready.
//
// Use: public
//
void
SoXtClipboard::paste(
Time t,
SoXtClipboardPasteCB *pasteDoneFunc,
void *pasteUserData)
//
////////////////////////////////////////////////////////////////////////
{
// Save values for later
eventTime = t;
callbackFunc = pasteDoneFunc;
userData = pasteUserData;
// if we own the selection, we already have the data,
// so we can bypass the server - simply paste what is in our cutbuffer
SoXtClipboard *clipboard;
void *data;
selOwnerList->find(clipboardAtom, data);
clipboard = (SoXtClipboard *)data;
if (clipboard == this) {
// We own the selection! Is it INventor data?
if (copyBuffer->isRawData()) {
//??? see if the app is interested in this data...
}
else {
SoPathList *pathList;
pathList = SoByteStream::unconvert(copyBuffer);
// let the app know the paste is done
if (callbackFunc != NULL)
(*callbackFunc)(userData, pathList);
}
}
else {
// Find out what data targets the selection owner supports.
// (Of course, we have the server ask the selection owner for us.)
XtGetSelectionValue(widget,
clipboardAtom,
_ATOM_(widget, "TARGETS"),
(XtSelectionCallbackProc) SoXtClipboard::importSelectionTargets,
(XtPointer) this,
eventTime);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Get the TARGETS we support into a table.
//
void
SoXtClipboard::getExportTargets(
char **value,
uint32_t *length)
//
////////////////////////////////////////////////////////////////////////
{
// Return the selection targets which we support
// If the user supplied the copy data type, we serve that up.
// Otherwise, we publicize all the Inventor types we support.
if (copyBuffer->isRawData()) {
// User data!
// (malloc the table since the requestor will own the data)
Atom *table = (Atom *) malloc(sizeof(Atom));
table[0] = copyDataType;
*value = (char *) table;
*length = 1; // number of items in our TARGETS table
}
else {
// Inventor data!
// (malloc the table since the requestor will own the data)
Atom *table = (Atom *) malloc(copyInterest->getLength() * sizeof(Atom));
for (int i = 0; i < copyInterest->getLength(); i++)
table[i] = (Atom)(*copyInterest)[i];
*value = (char *) table;
*length = copyInterest->getLength();
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Export the copied data to the X server so another application may
// paste. This callback is registered by copy().
//
// NOTE: we do not own the data we return (i.e. *value), so we have to
// copy our data buffer here.
//
// Use: static, private
//
Boolean
SoXtClipboard::exportSelection(
Widget widget,
Atom *selectionAtom,
Atom *target,
Atom *returnTarget,
char **value,
uint32_t *length,
int *format)
//
////////////////////////////////////////////////////////////////////////
{
Boolean ok = False;
#ifdef DEBUG
if (selectionAtom == NULL) {
SoDebugError::post("SoXtClipboard::exportSelection",
"- selectionAtom is NULL");
return FALSE;
}
if (target == NULL) {
SoDebugError::post("SoXtClipboard::exportSelection",
"- target is NULL");
return FALSE;
}
if (value == NULL) {
SoDebugError::post("SoXtClipboard::exportSelection",
"- value is NULL");
return FALSE;
}
if (length == NULL) {
SoDebugError::post("SoXtClipboard::exportSelection",
"- length is NULL");
return FALSE;
}
if (format == NULL) {
SoDebugError::post("SoXtClipboard::exportSelection",
"- format is NULL");
return FALSE;
}
if (returnTarget == NULL) {
SoDebugError::post("SoXtClipboard::exportSelection",
"- returnTarget is NULL");
return FALSE;
}
#endif
// get the clipboard pointer for this object
SoXtClipboard *clipboard;
void *data;
if (! selOwnerList->find(*selectionAtom, data)) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::exportSelection",
"- cannot find selection owner");
#endif
return FALSE;
}
clipboard = (SoXtClipboard *)data;
// Do we have any data?
if (clipboard->copyBuffer == NULL)
return False;
if (clipboard->copyBuffer->getData() == NULL || clipboard->copyBuffer->getNumBytes() <= 0)
return False;
// The most likely case - the pasting app wants to know what data types we have.
if (*target == _ATOM_(widget, "TARGETS")) {
clipboard->getExportTargets(value, length);
*format = 32;
*returnTarget = *target;
ok = True;
}
// The xclipboard case - pasting app just wants text. Give them the
// ascii scene graph!
else if (*target == XA_STRING) {
// convert the copyBuffer byte stream into a path list,
// then convert the path list to a byte stream, but in ascii format.
SoPathList *pathList;
pathList = SoByteStream::unconvert(clipboard->copyBuffer);
if (pathList != NULL) {
SoByteStream *asciiBuffer = new SoByteStream;
asciiBuffer->convert(pathList, FALSE); // FALSE == ascii format
// Copy the data buffer since the requestor will own the data
uint32_t numBytes = asciiBuffer->getNumBytes();
if (numBytes > 0) {
char *exportData = (char *) malloc((size_t) numBytes);
if (exportData != NULL) {
bcopy(asciiBuffer->getData(), exportData, (int) numBytes);
*value = exportData;
*length = numBytes;
*format = 8;
*returnTarget = XA_STRING;
ok = True;
}
}
delete asciiBuffer;
delete pathList;
}
}
// If our copyBuffer is a user defined type, then we do no conversion.
// Just serve it up.
else if (clipboard->copyBuffer->isRawData()) {
// Do we have the data that the pasting app wants?
if (clipboard->copyDataType == *target) {
// Copy the data buffer since the requestor will own the data
char *exportData = (char *) malloc((size_t) clipboard->copyBuffer->getNumBytes());
if (exportData != NULL) {
bcopy(clipboard->copyBuffer->getData(),
exportData,
(int)clipboard->copyBuffer->getNumBytes());
*value = exportData;
*length = clipboard->copyBuffer->getNumBytes();
*format = 8;
*returnTarget = clipboard->copyDataType;
ok = True;
}
}
}
else {
// If the data is one of the Inventor types, we need to convert to
// the requested type.
if (convertData(
widget,
clipboard->copyBuffer->getData(),
clipboard->copyBuffer->getNumBytes(),
*target, value, length))
{
*format = 8;
*returnTarget = *target;
ok = True;
}
}
return ok;
}
////////////////////////////////////////////////////////////////////////
//
// Convert our Inventor 2.1 data to the specified target.
//
// static,public
//
SbBool
SoXtClipboard::convertData(
Widget widget,
void *srcData,
uint32_t srcNumBytes,
Atom desiredType,
char **returnData,
uint32_t *returnNumBytes)
//
////////////////////////////////////////////////////////////////////////
{
// Do we know how to convert to the desired target?
if (! (desiredType == _ATOM_(widget, "INVENTOR_2_1") ||
desiredType == _ATOM_(widget, "INVENTOR_2_1_FILE") ||
desiredType == _ATOM_(widget, "VRML_1_0") ||
desiredType == _ATOM_(widget, "VRML_1_0_FILE") ||
desiredType == _ATOM_(widget, "INVENTOR") ||
desiredType == _ATOM_(widget, "INVENTOR_FILE") ||
desiredType == _ATOM_(widget, "INVENTOR_2_0") ||
desiredType == _ATOM_(widget, "INVENTOR_2_0_FILE"))) {
// It's some type we don't know
*returnData = NULL;
*returnNumBytes = 0;
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::convertData",
"desired conversion type not supported");
#endif
return FALSE;
}
// OK, we think we can support the destination target.
char *exportData = NULL;
uint32_t numBytes = 0;
// Run through our supported targets and see if we can convert.
//??? It would be nice if we put conversion callbacks in the copyInterest list,
//??? then we could just run through the list and blindly convert.
//??? should we cache our results?
////////////////////////////////////////////////////////////////////////
//
// Inventor 2.1 - no conversion needed
// (For VRML 1.0 destination - we should probably convert ivToVRML???)
//
////////////////////////////////////////////////////////////////////////
if (desiredType == _ATOM_(widget, "INVENTOR_2_1") ||
desiredType == _ATOM_(widget, "VRML_1_0")) {
exportData = (char *) malloc((size_t) srcNumBytes);
if (exportData != NULL) {
bcopy(srcData, exportData, (int) srcNumBytes);
*returnData = exportData;
*returnNumBytes = srcNumBytes;
return True;
}
else {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::convertData",
"could not allocate %d bytes for return buffer", srcNumBytes);
#endif
return False;
}
}
// Put our 2.1 data into a file
// Write the bytes to a tmp file which will be the input file to ivdowngrade
static char *tmpdir = NULL;
if (tmpdir == NULL) {
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
}
// Create temp filename
char *filename = tempnam(tmpdir, "IV21");
SbString tmpfile(filename);
free(filename);
// Write 2.1 data to the file
if (! writeToFile(tmpfile, srcData, srcNumBytes))
return False;
////////////////////////////////////////////////////////////////////////
//
// If the destination target is a 2.1 file, we're done
// (For VRML 1.0 destination - we should probably convert ivToVRML???)
//
////////////////////////////////////////////////////////////////////////
if (desiredType == _ATOM_(widget, "INVENTOR_2_1_FILE") ||
desiredType == _ATOM_(widget, "VRML_1_0_FILE")) {
exportData = strdup(tmpfile.getString());
numBytes = tmpfile.getLength() + 1; // +1 to grab the \0
*returnData = exportData;
*returnNumBytes = numBytes;
return True;
}
// Make sure ivdowngrade exists
//???
// Create temp output filename
filename = tempnam(tmpdir, "IV20");
SbString tmpfile2(filename);
free(filename);
// Convert to the 2.0 format
#define BUFSIZE 512
char conversionCmd[BUFSIZE];
const char *infile = tmpfile.getString();
const char *outfile = tmpfile2.getString();
sprintf(conversionCmd, "/usr/sbin/ivdowngrade -v 2.0 %s %s", infile, outfile);
if (system(conversionCmd) == -1) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::convertData",
"command <%s> failed", conversionCmd);
#endif
unlink(infile);
unlink(outfile);
return False;
}
// Converson succeeded! Done with the input file
//??? NOTE: we could cache this for future conversions...
unlink(infile); // remove the file
////////////////////////////////////////////////////////////////////////
//
// If the destination target is a 2.0 file, we're done
//
////////////////////////////////////////////////////////////////////////
if (desiredType == _ATOM_(widget, "INVENTOR_FILE") ||
desiredType == _ATOM_(widget, "INVENTOR_2_0_FILE") ) {
exportData = strdup(tmpfile2.getString());
numBytes = tmpfile2.getLength() + 1; // +1 to grab the \0
*returnData = exportData;
*returnNumBytes = numBytes;
return True; // success!
}
// Read the output file into the buffer
// First, open the file.
int fd = open(outfile, O_RDONLY);
if (fd < 0) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::convertData",
"could not open output file %s for reading", outfile);
#endif
unlink(outfile); // remove the file
return False;
}
// Get file size
uint32_t len = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET); // seek back to beginning
// Get buffer to hold data
void *buf = malloc(len);
if (buf == NULL) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::convertData",
"could not malloc %d bytes", len);
#endif
unlink(outfile); // remove the file
return False;
}
// Read that file
if (read(fd, buf, len) == -1) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::convertData",
"could not read %d bytes from output file %s", len, outfile);
#endif
unlink(outfile); // remove the file
return False;
}
// Done with the output file
unlink(outfile); // remove the file
////////////////////////////////////////////////////////////////////////
//
// If the destination target is a 2.0 object, we are done!
//
////////////////////////////////////////////////////////////////////////
if (desiredType == _ATOM_(widget, "INVENTOR") ||
desiredType == _ATOM_(widget, "INVENTOR_2_0") ) {
*returnData = (char *)buf;
*returnNumBytes = len;
return True; // success!
}
// If we get to here, we did not succeed. Bummer.
return False;
}
////////////////////////////////////////////////////////////////////////
//
// Write the copyBuffer to the tmp file
//
// static protected
//
SbBool
SoXtClipboard::writeToFile(
SbString &tmpfile,
void *srcData,
uint32_t srcNumBytes)
//
////////////////////////////////////////////////////////////////////////
{
const char *filename = tmpfile.getString();
int fd = creat(filename, S_IRWXU); // create for reading/writing
if (fd <= 0) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::writeToFile",
"could not open tmp input file %s", filename);
#endif
return False;
}
// Write to temp file
if (write(fd, srcData, srcNumBytes) == -1) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::writeToFile",
"could not write %d bytes to tmp input file %s",
srcNumBytes, filename);
#endif
close(fd);
unlink(filename); // remove the file
return False;
}
close(fd);
return True;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Import the targets supported by the selection owner. If we see
// a target we recognize, then we ask for the selection data in that
// format target. This callback is registered by pasteSelection().
//
// Use: static, private
//
void
SoXtClipboard::importSelectionTargets(
Widget widget,
SoXtClipboard *clipboard,
Atom *selectionAtom,
Atom *target,
char *value,
uint32_t *length,
int *format)
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
if (clipboard == NULL) {
SoDebugError::post("SoXtClipboard::importSelectionTargets",
"- clipboard is NULL");
return;
}
if (target == NULL) {
SoDebugError::post("SoXtClipboard::importSelectionTargets",
"- target is NULL");
return;
}
if (! clipboard->emptyListOK) {
if (value == NULL) {
SoDebugError::post("SoXtClipboard::importSelectionTargets",
"- value is NULL");
return;
}
if (length == NULL) {
SoDebugError::post("SoXtClipboard::importSelectionTargets",
"- length is NULL");
return;
}
if (format == NULL) {
SoDebugError::post("SoXtClipboard::importSelectionTargets",
"- format is NULL");
return;
}
}
#endif
// If no targets are supported
if (*length == 0) {
// Invoke the callback if an empty list is ok
if (clipboard->emptyListOK) {
SoPathList emptyList;
(*clipboard->callbackFunc)(clipboard->userData, &emptyList);
}
return;
}
Atom supportedAtom = 0;
if (*target == _ATOM_(widget, "TARGETS") || *target == XA_ATOM) {
if (*format == 32) {
Atom *lval = (Atom *) value;
supportedAtom = clipboard->chooseFromImportTargets(lval, *length);
}
#ifdef DEBUG
else {
SoDebugError::post("SoXtClipboard::importSelectionTargets",
"- received unknown format %d from clipboard owner", *format );
}
#endif
}
else {
// The selection owner does not support the TARGETS inquiry.
// Let's see if they at least support INVENTOR.
//??? what should we do here?
supportedAtom = _ATOM_(widget, "INVENTOR");
}
// if the selection owner supports a target we like, ask for the data
if (supportedAtom != 0) {
XtGetSelectionValue(clipboard->widget,
*selectionAtom,
supportedAtom,
(XtSelectionCallbackProc) SoXtClipboard::importSelection,
(XtPointer) clipboard,
clipboard->eventTime);
}
// According to Xt doc, the requestor (that's us) owns the data
// and should free it when done.
//??? Put in this change as a workaround to bug #306340
//??? to prevent core dumps. It seems that this sometimes frees
//??? memory here that it should not.
//??? XtFree(value);
}
////////////////////////////////////////////////////////////////////////
//
// See if any of the copy apps supported targets are something we are
// interested in pasting. Algorithm is: see if the first copy target
// matches any of our paste targets. If not, check the second copy
// target, etc.
//
Atom
SoXtClipboard::chooseFromImportTargets(
Atom *supportedTargets,
int length)
//
////////////////////////////////////////////////////////////////////////
{
Atom chosenType = 0;
int i;
// See if anything in the supported targets matches our paste interest
for (i = 0; i < length; i++) {
for (int j = 0; j < pasteInterest->getLength(); j++) {
if (supportedTargets[i] == pasteInterest->getDataType(j)) {
chosenType = supportedTargets[i];
break;
}
}
if (chosenType != 0) break;
}
return chosenType;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Import the current selection from the X server, and paste it
// into the scene graph. This callback is registered by importSelectionTargets().
//
// Use: static, private
//
void
SoXtClipboard::importSelection(
Widget /*w*/,
SoXtClipboard *clipboard,
Atom * /*selectionAtom*/,
Atom *target,
char *value,
uint32_t *length,
int * /*format*/)
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
if (clipboard == NULL) {
SoDebugError::post("SoXtClipboard::importSelection",
"- clipboard is NULL");
return;
}
if (target == NULL) {
SoDebugError::post("SoXtClipboard::importSelection",
"- target is NULL");
return;
}
if (! clipboard->emptyListOK) {
if (value == NULL) {
SoDebugError::post("SoXtClipboard::importSelection",
"- value is NULL");
return;
}
if (length == NULL) {
SoDebugError::post("SoXtClipboard::importSelection",
"- length is NULL");
return;
}
}
#endif
if (*length == 0 && ! clipboard->emptyListOK)
return; // nothing to paste!
if (clipboard->callbackFunc == NULL) return; // no callback to invoke!
// See if we are interested in the paste target
const SoXtImportInterest *interest =
clipboard->pasteInterest->get(clipboard->pasteInterest->find(*target));
// If there is interest, invoke the paste interest callback to read the data
if (interest != NULL) {
(*interest->getCallBack())(
interest->getUserData(),
*target,
(void *) value,
*length);
}
// If there is no interest, but an empty list is ok, invoke the
// callback with an empty list
else if (clipboard->emptyListOK) {
// Invoke the callback if an empty list is ok
SoPathList emptyList;
(*clipboard->callbackFunc)(clipboard->userData, &emptyList);
}
// According to Xt doc, the requestor (that's us) owns the data
// and should free it when done.
//??? Put in this change as a workaround to bug #306340
//??? to prevent core dumps. It seems that this sometimes frees
//??? memory here that it should not.
//??? XtFree(value);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This is called when the user drops data on this widget (drag and drop).
//
// Use: protected
//
void
SoXtClipboard::addPasteInterest(
Atom dataType,
SoXtClipboardImportCB *pasteImportFunc,
void *userData)
//
////////////////////////////////////////////////////////////////////////
{
pasteInterest->append(dataType, pasteImportFunc, userData);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This is called when the user pastes data in this widget.
//
// Use: protected
//
void
SoXtClipboard::pasteImport(Atom dataType, void *data, uint32_t numBytes)
//
////////////////////////////////////////////////////////////////////////
{
// Did we get any data?
SoPathList *pathList = readData(widget, dataType, data, numBytes);
// Did we get Inventor data?
if (pathList == NULL) {
if (emptyListOK) {
SoPathList emptyList;
(*callbackFunc)(userData, &emptyList);
}
#ifdef DEBUG
else
SoDebugError::post("SoXtClipboard::paste",
"could not get Inventor data");
#endif
}
// send the data to the app
else (*callbackFunc)(userData, pathList);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Convert target data we just imported via paste or drop to Inventor.
// This will free data when done.
//
// Use: static
//
SoPathList *
SoXtClipboard::readData(
Widget w,
Atom target,
void *data,
uint32_t numBytes)
//
////////////////////////////////////////////////////////////////////////
{
SoPathList *pathList = NULL;
if (data != NULL) {
if (target == _ATOM_(w, "INVENTOR_2_1") ||
target == _ATOM_(w, "VRML_1_0") ||
target == _ATOM_(w, "INVENTOR") ||
target == _ATOM_(w, "INVENTOR_2_0")) {
pathList = SoByteStream::unconvert(data, numBytes);
}
else if (target == _ATOM_(w, "INVENTOR_2_1_FILE") ||
target == _ATOM_(w, "VRML_1_0_FILE") ||
target == _ATOM_(w, "INVENTOR_FILE") ||
target == _ATOM_(w, "INVENTOR_2_0_FILE")) {
readFile(pathList, (const char *) data);
}
else if (target == XA_STRING) {
// Try to read Inventor data out of this
// if the string is in Inventor ascii interchange format
if (SoDB::isValidHeader((const char *)data)) {
// yes, the string we are importing is actually Inventor data!
pathList = SoByteStream::unconvert(data, numBytes);
}
}
XtFree((char *)data);
}
return pathList;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Read the file and add to the pathList.
//
// Use: static protected
//
void
SoXtClipboard::readFile(SoPathList *&pathList, const char *filename)
//
////////////////////////////////////////////////////////////////////////
{
//??? file could be DXF or some other format. May need to run a converter.
//??? do the routeprint stuff to figure out what to do here!
SoInput in;
SoNode *node;
SoPath *path;
// Open the file.
if (! in.openFile(filename)) {
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::paste", "could not open file %s", filename);
#endif
return;
}
// Make sure there's a pathList
if (pathList == NULL)
pathList = new SoPathList;
// Try reading nodes.
do {
if (SoDB::read(&in, node)) {
if (node != NULL) {
// Read a node! Add to the pathList
path = new SoPath(node);
pathList->append(path);
}
}
} while (node != NULL);
in.closeFile();
// If we succeeded in reading stuff, return.
if (pathList->getLength() > 0)
return;
// Open the file again.
if (! in.openFile(filename))
return;
// If couldn't read nodes, try reading paths.
do {
if (SoDB::read(&in, path)) {
if (path != NULL) {
// Read a path! Add to the pathList
pathList->append(path);
}
}
} while (path != NULL);
in.closeFile();
// If we never added to the pathList, get rid of it
if (pathList->getLength() == 0) {
delete pathList;
pathList = NULL;
#ifdef DEBUG
SoDebugError::post("SoXtClipboard::paste", "could not read Inventor data from file %s", filename);
#endif
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called by the X server when we are no longer the owner of the
// selection. We take this opportunity to nuke our local copyBuffer.
// This callback is registered by copySelection().
//
// Use: static, private
//
void
SoXtClipboard::loseSelection(
Widget /*w*/,
Atom *selectionAtom)
//
////////////////////////////////////////////////////////////////////////
{
// get the clipboard pointer for this object
SoXtClipboard *clipboard;
void *data;
selOwnerList->find(*selectionAtom, data);
clipboard = (SoXtClipboard *)data;
// get the clipboard pointer for this object
if (clipboard != NULL) {
if (clipboard->copyBuffer != NULL) {
delete clipboard->copyBuffer;
clipboard->copyBuffer = NULL;
}
selOwnerList->remove(*selectionAtom);
}
}