Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2012-02-17 20:58:09 +0400
committerSergey Sharybin <sergey.vfx@gmail.com>2012-02-17 20:58:09 +0400
commite8a1daaf9bda8f03723879d76557278b9a025c0a (patch)
treecc67b46ef3a957c893df7e288354f126823d4d5e /intern/ghost
parent7c15fb4f2c710a15e0ae7bd758f1cf74a4e97e36 (diff)
Drag-n-drop support on Linux
This commit implements drag-n-drop support from external applications into Blender. Used xdnd implementation from Paul Sheer.
Diffstat (limited to 'intern/ghost')
-rw-r--r--intern/ghost/CMakeLists.txt6
-rw-r--r--intern/ghost/SConscript2
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.cpp310
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.h135
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp22
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h12
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp6
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.h6
8 files changed, 498 insertions, 1 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 35b617e5452..3d65f4972c4 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -220,11 +220,17 @@ elseif(UNIX)
intern/GHOST_SystemX11.cpp
intern/GHOST_SystemPathsX11.cpp
intern/GHOST_WindowX11.cpp
+ intern/GHOST_DropTargetX11.cpp
intern/GHOST_DisplayManagerX11.h
intern/GHOST_SystemX11.h
intern/GHOST_SystemPathsX11.h
intern/GHOST_WindowX11.h
+ intern/GHOST_DropTargetX11.h
+ )
+
+ list(APPEND INC
+ ../../extern/xdnd
)
if(X11_XF86keysym_INCLUDE_PATH)
diff --git a/intern/ghost/SConscript b/intern/ghost/SConscript
index 68a0cbf7bdd..d83107717fc 100644
--- a/intern/ghost/SConscript
+++ b/intern/ghost/SConscript
@@ -42,6 +42,8 @@ elif window_system in ('linux', 'openbsd3', 'sunos5', 'freebsd7', 'freebsd8', 'f
# defs += ['PREFIX=\\"/usr/local/\\"'] # XXX, make an option
defs += ['WITH_X11_XINPUT'] # XXX, make an option
+ incs += ' #/extern/xdnd'
+
elif window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
for f in pf:
try:
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp
new file mode 100644
index 00000000000..2239ac63d33
--- /dev/null
+++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp
@@ -0,0 +1,310 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 by the Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ghost/intern/GHOST_DropTargetX11.cpp
+ * \ingroup GHOST
+ */
+
+#include "GHOST_DropTargetX11.h"
+#include "GHOST_Debug.h"
+
+#include <ctype.h>
+#include <assert.h>
+
+bool GHOST_DropTargetX11::m_xdndInitialized = false;
+DndClass GHOST_DropTargetX11::m_dndClass;
+Atom * GHOST_DropTargetX11::m_dndTypes = NULL;
+Atom * GHOST_DropTargetX11::m_dndActions = NULL;
+const char *GHOST_DropTargetX11::m_dndMimeTypes[] = {"url/url", "text/uri-list", "text/plain", "application/octet-stream"};
+int GHOST_DropTargetX11::m_refCounter = 0;
+
+#define dndTypeURLID 0
+#define dndTypeURIListID 1
+#define dndTypePlainTextID 2
+#define dndTypeOctetStreamID 3
+
+#define dndTypeURL m_dndTypes[dndTypeURLID]
+#define dndTypeURIList m_dndTypes[dndTypeURIListID]
+#define dndTypePlainText m_dndTypes[dndTypePlainTextID]
+#define dndTypeOctetStream m_dndTypes[dndTypeOctetStreamID]
+
+void GHOST_DropTargetX11::Initialize(void)
+{
+ Display *display = m_system->getXDisplay();
+ int dndTypesCount = sizeof(m_dndMimeTypes) / sizeof(char*);
+ int counter;
+
+ xdnd_init(&m_dndClass, display);
+
+ m_dndTypes = new Atom[dndTypesCount + 1];
+ XInternAtoms(display, (char**)m_dndMimeTypes, dndTypesCount, 0, m_dndTypes);
+ m_dndTypes[dndTypesCount] = 0;
+
+ m_dndActions = new Atom[8];
+ counter = 0;
+
+ m_dndActions[counter++] = m_dndClass.XdndActionCopy;
+ m_dndActions[counter++] = m_dndClass.XdndActionMove;
+
+#if 0 /* Not supported yet */
+ dndActions[counter++] = dnd->XdndActionLink;
+ dndActions[counter++] = dnd->XdndActionAsk;
+ dndActions[counter++] = dnd->XdndActionPrivate;
+ dndActions[counter++] = dnd->XdndActionList;
+ dndActions[counter++] = dnd->XdndActionDescription;
+#endif
+
+ m_dndActions[counter++] = 0;
+}
+
+void GHOST_DropTargetX11::Uninitialize(void)
+{
+ xdnd_shut(&m_dndClass);
+}
+
+GHOST_DropTargetX11::GHOST_DropTargetX11(GHOST_WindowX11 * window, GHOST_SystemX11 * system)
+:
+m_window(window),
+m_system(system)
+{
+ if (!m_xdndInitialized) {
+ Initialize();
+ m_xdndInitialized = true;
+ GHOST_PRINT("XDND initialized\n");
+ }
+
+ Window wnd = window->getXWindow();
+
+ xdnd_set_dnd_aware(&m_dndClass, wnd, 0);
+ xdnd_set_type_list(&m_dndClass, wnd, m_dndTypes);
+
+ m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
+ m_refCounter++;
+}
+
+GHOST_DropTargetX11::~GHOST_DropTargetX11()
+{
+ m_refCounter--;
+ if (m_refCounter == 0) {
+ Uninitialize();
+ m_xdndInitialized = false;
+ GHOST_PRINT("XDND uninitialized\n");
+ }
+}
+
+/* based on a code from Saul Rennison
+ * http://stackoverflow.com/questions/2673207/c-c-url-decode-library */
+
+typedef enum DecodeState_e {
+ STATE_SEARCH = 0, ///< searching for an ampersand to convert
+ STATE_CONVERTING ///< convert the two proceeding characters from hex
+} DecodeState_e;
+
+void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn)
+{
+ unsigned int i;
+ unsigned int len = strlen(encodedIn);
+ DecodeState_e state = STATE_SEARCH;
+ int j, asciiCharacter;
+ char tempNumBuf[3] = {0};
+ bool bothDigits = true;
+
+ memset(decodedOut, 0, bufferSize);
+
+ for (i = 0; i < len; ++i) {
+ switch (state) {
+ case STATE_SEARCH:
+ if (encodedIn[i] != '%') {
+ strncat(decodedOut, &encodedIn[i], 1);
+ assert(strlen(decodedOut) < bufferSize);
+ break;
+ }
+
+ // We are now converting
+ state = STATE_CONVERTING;
+ break;
+
+ case STATE_CONVERTING:
+ bothDigits = true;
+
+ // Create a buffer to hold the hex. For example, if %20, this
+ // buffer would hold 20 (in ASCII)
+ memset(tempNumBuf, 0, sizeof(tempNumBuf));
+
+ // Conversion complete (i.e. don't convert again next iter)
+ state = STATE_SEARCH;
+
+ strncpy(tempNumBuf, &encodedIn[i], 2);
+
+ // Ensure both characters are hexadecimal
+
+ for (j = 0; j < 2; ++j) {
+ if (!isxdigit(tempNumBuf[j]))
+ bothDigits = false;
+ }
+
+ if (!bothDigits)
+ break;
+
+ // Convert two hexadecimal characters into one character
+ sscanf(tempNumBuf, "%x", &asciiCharacter);
+
+ // Ensure we aren't going to overflow
+ assert(strlen(decodedOut) < bufferSize);
+
+ // Concatenate this character onto the output
+ strncat(decodedOut, (char*)&asciiCharacter, 1);
+
+ // Skip the next character
+ i++;
+ break;
+ }
+ }
+}
+
+char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl)
+{
+ if(!strncpy(fileUrl, "file://", 7) == 0) {
+ /* assume one character of encoded URL can be expanded to 4 chars max */
+ int decodedSize = 4 * strlen(fileUrl) + 1;
+ char *decodedPath = (char *)malloc(decodedSize);
+
+ UrlDecode(decodedPath, decodedSize, fileUrl + 7);
+
+ return decodedPath;
+ }
+
+ return NULL;
+}
+
+void *GHOST_DropTargetX11::getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize)
+{
+ GHOST_TStringArray *strArray = NULL;
+ int totPaths = 0, curLength = 0;
+
+ /* count total number of file pathes in buffer */
+ for (int i = 0; i <= dropBufferSize; i++) {
+ if (dropBuffer[i] == 0 || dropBuffer[i] == '\n' || dropBuffer[i] == '\r') {
+ if (curLength) {
+ totPaths++;
+ curLength = 0;
+ }
+ }
+ else curLength++;
+ }
+
+ strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
+ strArray->count = 0;
+ strArray->strings = (GHOST_TUns8**)malloc(totPaths*sizeof(GHOST_TUns8*));
+
+ curLength = 0;
+ for (int i = 0; i <= dropBufferSize; i++) {
+ if (dropBuffer[i] == 0 || dropBuffer[i] == '\n' || dropBuffer[i] == '\r') {
+ if (curLength) {
+ char *curPath = (char *)malloc(curLength + 1);
+ char *decodedPath;
+
+ strncpy(curPath, (char*)dropBuffer + i - curLength, curLength);
+ curPath[curLength] = 0;
+
+ decodedPath = FileUrlDecode(curPath);
+ if(decodedPath) {
+ strArray->strings[strArray->count] = (GHOST_TUns8*)decodedPath;
+ strArray->count++;
+ }
+
+ free(curPath);
+ curLength = 0;
+ }
+ }
+ else curLength++;
+ }
+
+ return strArray;
+}
+
+void *GHOST_DropTargetX11::getGhostData(Atom dropType, unsigned char *dropBuffer, int dropBufferSize)
+{
+ void *data = NULL;
+ unsigned char *tmpBuffer = (unsigned char *)malloc(dropBufferSize + 1);
+ bool needsFree = true;
+
+ /* ensure NULL-terminator */
+ memcpy(tmpBuffer, dropBuffer, dropBufferSize);
+ tmpBuffer[dropBufferSize] = 0;
+
+ if (dropType == dndTypeURIList) {
+ m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
+ data = getURIListGhostData(tmpBuffer, dropBufferSize);
+ }
+ else if (dropType == dndTypeURL) {
+ /* need to be tested */
+ char *decodedPath = FileUrlDecode((char *)tmpBuffer);
+
+ if (decodedPath) {
+ m_draggedObjectType = GHOST_kDragnDropTypeString;
+ data = decodedPath;
+ }
+ }
+ else if (dropType == dndTypePlainText || dropType == dndTypeOctetStream) {
+ m_draggedObjectType = GHOST_kDragnDropTypeString;
+ data = tmpBuffer;
+ needsFree = false;
+ }
+ else {
+ m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
+ }
+
+ if (needsFree)
+ free(tmpBuffer);
+
+ return data;
+}
+
+bool GHOST_DropTargetX11::GHOST_HandleClientMessage(XEvent *event)
+{
+ Atom dropType;
+ unsigned char *dropBuffer;
+ int dropBufferSize, dropX, dropY;
+
+ if (xdnd_get_drop(m_system->getXDisplay(), event, m_dndTypes, m_dndActions,
+ &dropBuffer, &dropBufferSize, &dropType, &dropX, &dropY))
+ {
+ void *data = getGhostData(dropType, dropBuffer, dropBufferSize);
+
+ if (data)
+ m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, dropX, dropY, data);
+
+ free(dropBuffer);
+
+ m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
+
+ return true;
+ }
+
+ return false;
+}
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h
new file mode 100644
index 00000000000..2b08b7ef59c
--- /dev/null
+++ b/intern/ghost/intern/GHOST_DropTargetX11.h
@@ -0,0 +1,135 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 by the Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ghost/intern/GHOST_DropTargetWin32.h
+ * \ingroup GHOST
+ */
+
+#ifndef _GHOST_DROP_TARGET_X11_H_
+#define _GHOST_DROP_TARGET_X11_H_
+
+#include <GHOST_Types.h>
+#include "GHOST_WindowX11.h"
+#include "GHOST_SystemX11.h"
+
+#include "xdnd.h"
+
+class GHOST_DropTargetX11
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param window The window to register as drop target.
+ * @param system The associated system.
+ */
+ GHOST_DropTargetX11(GHOST_WindowX11 * window, GHOST_SystemX11 * system);
+
+ /**
+ * Destructor
+ */
+ ~GHOST_DropTargetX11();
+
+ /**
+ * Handler of ClientMessage X11 event
+ */
+ bool GHOST_HandleClientMessage(XEvent *event);
+
+ /**
+ * Get data to pass in event.
+ * It checks the type and calls specific functions for each type.
+ * @param dropType - type of dropped entity.
+ * @param dropBuffer - buffer returned from source application
+ * @param dropBufferSize - size of returned buffer
+ * @return Pointer to data.
+ */
+ void *getGhostData(Atom dropType, unsigned char *dropBuffer, int dropBufferSize);
+
+private:
+ /* Internal helper functions */
+
+ /**
+ * Initiailize XDND and all related X atoms
+ */
+ void Initialize(void);
+
+ /**
+ * Uninitiailize XDND and all related X atoms
+ */
+ void Uninitialize(void);
+
+ /**
+ * Get data to be passed to event from text/uri-list mime type
+ * @param dropBuffer - buffer returned from source application
+ * @param dropBufferSize - size of dropped buffer
+ * @return pointer to newly created GHOST data
+ */
+ void * getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize);
+
+ /**
+ * Decode URL (i.e. converts "file:///a%20b/test" to "file:///a b/test")
+ * @param decodedOut - buffer for decoded URL
+ * @param bufferSize - size of output buffer
+ * @param encodedIn - input encoded buffer to be decoded
+ */
+ void UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn);
+
+ /**
+ * Fully decode file URL (i.e. converts "file:///a%20b/test" to "/a b/test")
+ * @param fileUrl - file path URL to be fully decoded
+ * @return decoded file path (resutl shold be free-d)
+ */
+ char *FileUrlDecode(char *fileUrl);
+
+ /* The associated GHOST_WindowWin32. */
+ GHOST_WindowX11 * m_window;
+ /* The System. */
+ GHOST_SystemX11 * m_system;
+
+ /* Data type of the dragged object */
+ GHOST_TDragnDropTypes m_draggedObjectType;
+
+ /* is dnd stuff initialzied */
+ static bool m_xdndInitialized;
+
+ /* class holding internal stiff of xdnd library */
+ static DndClass m_dndClass;
+
+ /* list of supported types to eb draggeg into */
+ static Atom * m_dndTypes;
+
+ /* list of supported dran'n'drop actions */
+ static Atom * m_dndActions;
+
+ /* List of supported MIME types to be dragged into */
+ static const char *m_dndMimeTypes[];
+
+ /* counter of references to global XDND structures */
+ static int m_refCounter;
+};
+
+#endif // _GHOST_DROP_TARGET_X11_H_
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 22c16009591..7261770771a 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -42,6 +42,8 @@
#include "GHOST_EventButton.h"
#include "GHOST_EventWheel.h"
#include "GHOST_DisplayManagerX11.h"
+#include "GHOST_DropTargetX11.h"
+#include "GHOST_EventDragnDrop.h"
#ifdef WITH_INPUT_NDOF
#include "GHOST_NDOFManagerX11.h"
#endif
@@ -709,8 +711,12 @@ GHOST_SystemX11::processEvent(XEvent *xe)
}
}
} else {
- /* Unknown client message, ignore */
+ /* try to handle drag event (if there's no such events, GHOST_HandleClientMessage will return zero) */
+ if (window->getDropTarget()->GHOST_HandleClientMessage(xe) == false) {
+ /* Unknown client message, ignore */
+ }
}
+
break;
}
@@ -1478,3 +1484,17 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
fprintf(stderr, "failed to own primary\n");
}
}
+
+GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType,
+ GHOST_TDragnDropTypes draggedObjectType,
+ GHOST_IWindow* window,
+ int mouseX, int mouseY,
+ void* data)
+{
+ GHOST_SystemX11* system = ((GHOST_SystemX11*)getSystem());
+ return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
+ eventType,
+ draggedObjectType,
+ window,mouseX,mouseY,data)
+ );
+}
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index 73d9c95e273..8e9131877d3 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -236,6 +236,18 @@ public:
void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
/**
+ * Creates a drag'n'drop event and pushes it immediately onto the event queue.
+ * Called by GHOST_DropTargetX11 class.
+ * @param eventType The type of drag'n'drop event
+ * @param draggedObjectType The type object concerned (currently array of file names, string, ?bitmap)
+ * @param mouseX x mouse coordinate (in window coordinates)
+ * @param mouseY y mouse coordinate
+ * @param window The window on which the event occurred
+ * @return Indication whether the event was handled.
+ */
+ static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,GHOST_IWindow* window, int mouseX, int mouseY, void* data);
+
+ /**
* @see GHOST_ISystem
*/
int toggleConsole(int action) { return 0; }
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index 71e3f7b3340..ea99a9ea7b4 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -32,6 +32,7 @@
#include "GHOST_WindowX11.h"
#include "GHOST_SystemX11.h"
+#include "GHOST_DropTargetX11.h"
#include "STR_String.h"
#include "GHOST_Debug.h"
@@ -326,6 +327,10 @@ GHOST_WindowX11(
}
+ /* initialize drop target for newly created window */
+ m_dropTarget = new GHOST_DropTargetX11(this, m_system);
+ GHOST_PRINT("Set drop target\n");
+
/*
* One of the problem with WM-spec is that can't set a property
* to a window that isn't mapped. That is why we can't "just
@@ -1318,6 +1323,7 @@ GHOST_WindowX11::
}
#endif
+ delete m_dropTarget;
XDestroyWindow(m_display, m_window);
XFree(m_visual);
diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h
index f1146db50f8..f5cbceff7a4 100644
--- a/intern/ghost/intern/GHOST_WindowX11.h
+++ b/intern/ghost/intern/GHOST_WindowX11.h
@@ -45,6 +45,7 @@
class STR_String;
class GHOST_SystemX11;
+class GHOST_DropTargetX11;
/**
* X11 implementation of GHOST_IWindow.
@@ -224,6 +225,9 @@ public:
XIC getX11_XIC() { return m_xic; }
#endif
+ GHOST_DropTargetX11* getDropTarget()
+ { return m_dropTarget; }
+
/*
* Need this in case that we want start the window
* in FullScree or Maximized state.
@@ -361,6 +365,8 @@ private :
/** Cache of XC_* ID's to XCursor structures */
std::map<unsigned int, Cursor> m_standard_cursors;
+ GHOST_DropTargetX11 * m_dropTarget;
+
#ifdef WITH_X11_XINPUT
/* Tablet devices */
XTablet m_xtablet;