/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include #include #include #include #include #include "defines.h" #include "DragSource.h" #include "Application.h" #include "WarningDialogManager.h" #include "Dictionary.h" #include "DictionaryIterator.h" #ifndef DXD_DO_NOT_REQ_UNISTD_H #include #endif #ifndef DXD_DO_NOT_REQ_SYS_PARAM_H #include #endif #if defined(solaris) #include #endif #if defined(NEEDS_GETHOSTNAME_DECL) extern "C" int gethostname(char *address, int address_len); #endif #if defined(DXD_WIN) #include #endif // // DragSource handles (or cooperates with DropSite on) the following // 1) the Motif end the dnd protocol // 2) free-ing (or not) the memory used for the transfer // 3) setting ICCCM atoms commonly used by dnd participants // 4) the drag cursor // // 2) Deleting data depends on whether the transfer is within an executable // or between executables. We check on that here. // 3) We're setting USER, HOST_NAME, PROCESS. The book lists many other // things we might set. These atoms are used on the drop end. Certain // drops happen only within an executable. // 4) The drag cursor is currently a monochrome bitmap. It could be anything // drawable in X. Using Xpm would be a great way to make a prettier cursor. // boolean DragSource::DragSourceClassInitialized = FALSE; XtTranslations DragSource::drag_tranlations = NULL; void* DragSource::TransferData = 0; static char dragTranslations[] = "#override : DragSource_StartDrag()"; static XtActionsRec dragActions[] = { {"DragSource_StartDrag", (XtActionProc)DragSource_StartDrag}, }; static XContext dragcontext = NULL; DragSource::DragSource() { this->drag_pixmap = 0; this->drag_context = NUL(Widget); } DragSource::~DragSource() { } void DragSource::addSupportedType (int tag, const char *typeName, boolean preferred) { ASSERT(typeName); ASSERT(typeName[0]); ASSERT(this->getDragDictionary()); TransferStyle *ts = new TransferStyle(tag, typeName, preferred); ASSERT (ts); this->getDragDictionary()->addDefinition (typeName, (const void *)ts); } void DragSource::setDragWidget(Widget source_w) { // // One time class initialization stuff // if(!DragSource::DragSourceClassInitialized) { // // So we can get "this" back from the widget // dragcontext = XUniqueContext(); // // Parse the Button 2 translations // DragSource::drag_tranlations = XtParseTranslationTable(dragTranslations); // // Add the action routine // XtAppAddActions(XtWidgetToApplicationContext(source_w), dragActions, XtNumber(dragActions)); DragSource::DragSourceClassInitialized = True; } // // So we can get "this" back from the widget // XSaveContext(XtDisplay(source_w), Window(source_w), dragcontext, (const char *)this); XtOverrideTranslations(source_w, DragSource::drag_tranlations); } void DragSource::setDragIcon(Widget drag_icon) { this->drag_pixmap = 1; this->drag_icon = drag_icon; } Widget DragSource::createDragIcon(int width, int height, char *bits, char *maskbits) { Arg wargs[10]; int n = 0; this->drag_pixmap = XCreateBitmapFromData( theApplication->getDisplay(), XtWindow(theApplication->getRootWidget()), bits, width, height); this->drag_mask = XCreateBitmapFromData( theApplication->getDisplay(), XtWindow(theApplication->getRootWidget()), maskbits, width, height); XtSetArg(wargs[n], XmNpixmap, this->drag_pixmap); n++; XtSetArg(wargs[n], XmNmask, this->drag_mask); n++; XtSetArg(wargs[n], XmNwidth, width); n++; XtSetArg(wargs[n], XmNheight, height); n++; #ifndef DXD_WIN this->drag_icon = XmCreateDragIcon(theApplication->getRootWidget(), "drag_icon", wargs, n); #else /* AJ This is a bug in XmStatic.lib. Exceed is still working on it. */ this->drag_icon = NULL; #endif return this->drag_icon; } extern "C" void DragSource_StartDrag(Widget w, XEvent* event, String* , Cardinal* ) { XPointer ptr; DragSource *ds; ASSERT(dragcontext); if (XFindContext(XtDisplay(w), Window(w), dragcontext, &ptr)) return ; ds = (DragSource *) ptr; ds->startDrag(w, event); } void DragSource::startDrag(Widget w, XEvent* event) { Arg wargs[20]; int n; int num_exports; #define MAX_EXPORTS 20 Atom exports[MAX_EXPORTS]; int response = this->decideToDrag(event); if(response != DragSource::Proceed) { if (response == DragSource::Abort) XBell(XtDisplay(w), 100); return; } // Create an array of Atoms. Each entry identifies a data type we understand. // The array is passed to Motif - lets the world know who we are. Loop over // the dictionary entries to create this array. DictionaryIterator di(*this->getDragDictionary()); num_exports = 0; TransferStyle *ts; while (ts = (TransferStyle*)di.getNextDefinition()) { exports[num_exports] = ts->getAtom(); if ((++num_exports) == MAX_EXPORTS) break; } n = 0; XtSetArg(wargs[n], XmNexportTargets, exports); n++; XtSetArg(wargs[n], XmNnumExportTargets, num_exports); n++; if(event->xkey.state & ShiftMask) { XtSetArg(wargs[n], XmNdragOperations, XmDROP_MOVE); n++; this->operation = XmDROP_MOVE; } else { XtSetArg(wargs[n], XmNdragOperations, XmDROP_COPY); n++; this->operation = XmDROP_COPY; } XtSetArg(wargs[n], XmNconvertProc, DragSource_ConvertProc); n++; XtSetArg(wargs[n], XmNincremental, False); n++; XtSetArg(wargs[n], XmNblendModel, XmBLEND_JUST_SOURCE); n++; // Store "this" in clientData now. It also goes into callData in the // the callback to DragSource_DropFinishCB. It's replaced in clientData // before DragSource_DropFinishCB, inside DragSource_ConvertProc to hold // a TransferStyle*. XtSetArg(wargs[n], XmNclientData, this); n++; if(this->drag_pixmap != 0) { Pixel bg = WhitePixelOfScreen(XtScreen(w)); Pixel fg = BlackPixelOfScreen(XtScreen(w)); XtSetArg(wargs[n], XmNcursorBackground, bg); n++; XtSetArg(wargs[n], XmNcursorForeground, fg); n++; XtSetArg(wargs[n], XmNsourceCursorIcon, this->drag_icon); n++; } this->drag_context = XmDragStart(w, event, wargs, n); if (!this->drag_context) return ; XtAddCallback(this->drag_context, XmNdropFinishCallback, DragSource_DropFinishCB, this); if (this->intra_toplevel) { this->inside_own_shell = TRUE; this->top_level_window = 0; XtAddCallback(this->drag_context, XmNtopLevelLeaveCallback, DragSource_TopLevelLeaveCB, this); XtAddCallback(this->drag_context, XmNtopLevelEnterCallback, DragSource_TopLevelEnterCB, this); } // // Set special ICCCM Atom values so that reciever knows who // we are and can help us to know what to do with the data // DELETE == do we delete this the memory? // PROCESS == our pid // HOST_NAME == hostname of machine we're running on // USER == current user name // Atom DELETE, PROCESS, HOST_NAME, USER; Display *d = XtDisplay(this->drag_context); char hostname[MAXHOSTNAMELEN]; // This is a strang way to get memory for an int, but if I do just declare // pid as an int, then texture complains about an unaligned access. I guess // something inside XChangeProperty expects more alignment than an int provides. double pid_space; int *pid = (int*)&pid_space; *pid = (int)getpid(); DELETE = XmInternAtom(d, "DELETE", False); PROCESS = XmInternAtom(d, "PROCESS", False); HOST_NAME = XmInternAtom(d, "HOST_NAME", False); USER = XmInternAtom(d, "USER", False); Screen *screen = XtScreen(w); Window root = RootWindowOfScreen(screen); XChangeProperty (d, root, PROCESS, XA_INTEGER, 32, PropModeReplace, (unsigned char *)pid, 1); gethostname(hostname, sizeof(hostname)); XChangeProperty (d, root, HOST_NAME, XA_STRING, 8, PropModeReplace, (unsigned char *)hostname, strlen(hostname)); #ifdef DXD_OS_NON_UNIX char *login = NULL; #else char *login = getlogin(); #endif if ((login)&&(login[0])) XChangeProperty (d, root, USER, XA_STRING, 8, PropModeReplace, (unsigned char*)login, strlen(login)); } extern "C" Boolean DragSource_ConvertProc(Widget w, Atom *, Atom *target, Atom *type_return, XtPointer *value_return, unsigned long *length_return, int *format_return) { DragSource *ds; Boolean ret; ret = True; XtVaGetValues(w, XmNclientData, &ds, NULL); ASSERT(ds); // // Call the convert routine, passing in the target, and getting back // the value and length // char *cp = XGetAtomName(theApplication->getDisplay(), *target); // Look up the data type for the transfer in the dictionary based on // the transfer atom. DictionaryIterator di(*ds->getDragDictionary()); TransferStyle *ts; while (ts = (TransferStyle*)di.getNextDefinition()) { if (ts->getAtom() == *target) break; } // // It's not possible for ts==NULL at this point because this callback can // be invoked only if there is some atom which DragSource and DropSite have // in common. However it did occur on ship day, that the irix desktop will // call this callback when there is no shared atom. so... // if (!ts) { if (cp) XFree(cp); return False; } XtVaSetValues (w, XmNclientData, ts, NULL); if (!ds->decodeDragType(ts->getTag(),cp,value_return,length_return,ds->operation)) { *value_return = NULL; DragSource::TransferData = 0; *length_return = 0; ret = False; } else { *type_return = *target; DragSource::TransferData = (void *)*value_return; *format_return = 8; } if (cp) XFree(cp); // // FIXME!!! // if (*length_return > 63000) { *value_return = NULL; *length_return = 0; WarningMessage("Too much info to drag and drop. Try transferring in pieces"); ret = False; } return ret; } extern "C" void DragSource_DropFinishCB(Widget w, XtPointer cdata, XtPointer call_data) { DragSource *ds = (DragSource *)cdata; XmDropFinishCallbackStruct *dfcbs = (XmDropFinishCallbackStruct *)call_data; TransferStyle *ts; XtVaGetValues(w, XmNclientData, &ts, NULL); ASSERT(ts); ds->drag_context = NUL(Widget); ds->dropFinish(ds->operation, ts->getTag(), dfcbs->completionStatus); // // The ds object might not exist at this point so don't reference it anymore. // Atom DELETE; Display *d = XtDisplay(w); Window root = DefaultRootWindow(d); Atom actual_type; int actual_format; unsigned char *data; unsigned long remain, nitems; DELETE = XmInternAtom(d, "DELETE", False); XGetWindowProperty (d, root, DELETE, 0, 4, False, XA_STRING, &actual_type, &actual_format, &nitems, &remain, &data); if ((data) && (!strcmp((const char *)data, "TRUE"))) { if (DragSource::TransferData) #if !defined(sun4) free (DragSource::TransferData); #else free ((char *)DragSource::TransferData); #endif DragSource::TransferData = 0; XFree(data); } } void DragSource::dropFinish(long, int, unsigned char) { // Do nothing, by default } extern "C" void DragSource_TopLevelLeaveCB(Widget w, XtPointer cdata, XtPointer call) { DragSource* ds = (DragSource*)cdata; XmTopLevelLeaveCallbackStruct* tllcbs = (XmTopLevelLeaveCallbackStruct*)call; ASSERT(ds); if (ds->top_level_window == 0) ds->top_level_window = tllcbs->window; } extern "C" void DragSource_TopLevelEnterCB(Widget w, XtPointer cdata, XtPointer call) { DragSource* ds = (DragSource*)cdata; XmTopLevelEnterCallbackStruct* tlecbs = (XmTopLevelEnterCallbackStruct*)call; ASSERT(ds); if (ds->top_level_window) ds->inside_own_shell = (ds->top_level_window == tlecbs->window); } boolean DragSource::isIntraShell() { ASSERT(this->intra_toplevel); ASSERT(this->drag_context); return this->inside_own_shell; } #ifdef Comment extern "C" Boolean IncConvertProc(Widget w, Atom *selection, Atom *target, Atom *type_return, XtPointer *value_return, unsigned long *length_return, int *format_return, unsigned long *max_len, XtPointer client_data, XtRequestId *request_id) { Widget ww; Atom DXMODULE; DXMODULE = XmInternAtom(XtDisplay(w), "DXMODULE", False); XtVaGetValues(w, XmNclientData, &ww, NULL); call_data.amount = *max_len; XtCallCallbacks((Widget) ww, XmNdragConvertCallback, &call_data); *target = DXMODULE; *type_return = XmInternAtom(XtDisplay(w), "XA_STRING", False); *value_return = call_data.data; *length_return = call_data.amount; *format_return = 8; return call_data.done; } #endif