forked from bartvdbraak/blender
Drag-n-drop support on Linux
This commit implements drag-n-drop support from external applications into Blender. Used xdnd implementation from Paul Sheer.
This commit is contained in:
parent
7c15fb4f2c
commit
e8a1daaf9b
4
extern/CMakeLists.txt
vendored
4
extern/CMakeLists.txt
vendored
@ -71,3 +71,7 @@ endif()
|
||||
if(WITH_CARVE)
|
||||
add_subdirectory(carve)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_subdirectory(xdnd)
|
||||
endif()
|
||||
|
3
extern/SConscript
vendored
3
extern/SConscript
vendored
@ -34,3 +34,6 @@ if env['WITH_BF_LIBMV']:
|
||||
|
||||
if env['WITH_BF_CARVE']:
|
||||
SConscript(['carve/SConscript'])
|
||||
|
||||
if env['OURPLATFORM'] in ('linux', 'openbsd3', 'sunos5', 'freebsd7', 'freebsd8', 'freebsd9', 'aix4', 'aix5'):
|
||||
SConscript(['xdnd/SConscript'])
|
||||
|
43
extern/xdnd/CMakeLists.txt
vendored
Normal file
43
extern/xdnd/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# ***** 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, Blender Foundation
|
||||
# All rights reserved.
|
||||
#
|
||||
# The Original Code is: all of this file.
|
||||
#
|
||||
# Contributor(s): Sergey Sharybin.
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
set(INC
|
||||
.
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC
|
||||
xdnd.c
|
||||
xdnd.h
|
||||
)
|
||||
|
||||
add_definitions(
|
||||
-DHAVE_SYS_TIME_H
|
||||
)
|
||||
|
||||
blender_add_lib(extern_xdnd "${SRC}" "${INC}" "${INC_SYS}")
|
10
extern/xdnd/SConscript
vendored
Normal file
10
extern/xdnd/SConscript
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
Import('env')
|
||||
|
||||
defs = ['HAVE_SYS_TIME_H']
|
||||
sources = env.Glob('*.c')
|
||||
|
||||
incs = '.'
|
||||
|
||||
env.BlenderLib ( 'extern_xdnd', sources, Split(incs), defs, libtype=['extern','player'], priority=[10, 185])
|
1599
extern/xdnd/xdnd.c
vendored
Normal file
1599
extern/xdnd/xdnd.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
221
extern/xdnd/xdnd.h
vendored
Normal file
221
extern/xdnd/xdnd.h
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
/* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
|
||||
Copyright (C) 1996-2000 Paul Sheer
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _X_DND_H
|
||||
#define _X_DND_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* you can set this to either 2 (which support 0 and 1 as well) or 3 */
|
||||
/* #define XDND_VERSION 2 */
|
||||
#define XDND_VERSION 3
|
||||
|
||||
|
||||
/* XdndEnter */
|
||||
#define XDND_THREE 3
|
||||
#define XDND_ENTER_SOURCE_WIN(e) ((e)->xclient.data.l[0])
|
||||
#define XDND_ENTER_THREE_TYPES(e) (((e)->xclient.data.l[1] & 0x1UL) == 0)
|
||||
#define XDND_ENTER_THREE_TYPES_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL)
|
||||
#define XDND_ENTER_VERSION(e) ((e)->xclient.data.l[1] >> 24)
|
||||
#define XDND_ENTER_VERSION_SET(e,v) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
|
||||
#define XDND_ENTER_TYPE(e,i) ((e)->xclient.data.l[2 + i]) /* i => (0, 1, 2) */
|
||||
|
||||
/* XdndPosition */
|
||||
#define XDND_POSITION_SOURCE_WIN(e) ((e)->xclient.data.l[0])
|
||||
#define XDND_POSITION_ROOT_X(e) ((e)->xclient.data.l[2] >> 16)
|
||||
#define XDND_POSITION_ROOT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFUL)
|
||||
#define XDND_POSITION_ROOT_SET(e,x,y) (e)->xclient.data.l[2] = ((x) << 16) | ((y) & 0xFFFFUL)
|
||||
#define XDND_POSITION_TIME(e) ((e)->xclient.data.l[3])
|
||||
#define XDND_POSITION_ACTION(e) ((e)->xclient.data.l[4])
|
||||
|
||||
/* XdndStatus */
|
||||
#define XDND_STATUS_TARGET_WIN(e) ((e)->xclient.data.l[0])
|
||||
#define XDND_STATUS_WILL_ACCEPT(e) ((e)->xclient.data.l[1] & 0x1L)
|
||||
#define XDND_STATUS_WILL_ACCEPT_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL)
|
||||
#define XDND_STATUS_WANT_POSITION(e) ((e)->xclient.data.l[1] & 0x2UL)
|
||||
#define XDND_STATUS_WANT_POSITION_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x2UL) | (((b) == 0) ? 0 : 0x2UL)
|
||||
#define XDND_STATUS_RECT_X(e) ((e)->xclient.data.l[2] >> 16)
|
||||
#define XDND_STATUS_RECT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFL)
|
||||
#define XDND_STATUS_RECT_WIDTH(e) ((e)->xclient.data.l[3] >> 16)
|
||||
#define XDND_STATUS_RECT_HEIGHT(e) ((e)->xclient.data.l[3] & 0xFFFFL)
|
||||
#define XDND_STATUS_RECT_SET(e,x,y,w,h) {(e)->xclient.data.l[2] = ((x) << 16) | ((y) & 0xFFFFUL); (e)->xclient.data.l[3] = ((w) << 16) | ((h) & 0xFFFFUL); }
|
||||
#define XDND_STATUS_ACTION(e) ((e)->xclient.data.l[4])
|
||||
|
||||
/* XdndLeave */
|
||||
#define XDND_LEAVE_SOURCE_WIN(e) ((e)->xclient.data.l[0])
|
||||
|
||||
/* XdndDrop */
|
||||
#define XDND_DROP_SOURCE_WIN(e) ((e)->xclient.data.l[0])
|
||||
#define XDND_DROP_TIME(e) ((e)->xclient.data.l[2])
|
||||
|
||||
/* XdndFinished */
|
||||
#define XDND_FINISHED_TARGET_WIN(e) ((e)->xclient.data.l[0])
|
||||
|
||||
struct _DndCursor {
|
||||
int width, height;
|
||||
int x, y;
|
||||
unsigned char *image_data, *mask_data;
|
||||
char *_action;
|
||||
Pixmap image_pixmap, mask_pixmap;
|
||||
Cursor cursor;
|
||||
Atom action;
|
||||
};
|
||||
|
||||
typedef struct _DndCursor DndCursor;
|
||||
typedef struct _DndClass DndClass;
|
||||
|
||||
struct _DndClass {
|
||||
/* insert chars sequentionally into the target widget, type will be the same as `desired_type'
|
||||
returned from widget_apply_position. This may be called several times in succession
|
||||
with sequention blocks of data. Must return non-zero on failure */
|
||||
int (*widget_insert_drop) (DndClass * dnd, unsigned char *data, int length, int remaining, Window into, Window from, Atom type);
|
||||
|
||||
/* In response to DELETE requests : FIXME - not yet used */
|
||||
int (*widget_delete_selection) (DndClass * dnd, Window window, Window from);
|
||||
|
||||
/* returns 1 if widget exists, zero otherwise. If this method is not
|
||||
set then the code assumes that no widgets have support for recieving drops.
|
||||
In this case none of the widget methods need be set. */
|
||||
int (*widget_exists) (DndClass * dnd, Window window);
|
||||
|
||||
/* must update the widgets border to its default appearance */
|
||||
void (*widget_apply_leave) (DndClass * dnd, Window widgets_window);
|
||||
|
||||
/* must update the widgets border to give the appearance of being able to recieve a drop,
|
||||
plus return all data to pointers. As per the protocol, if the widget cannot
|
||||
perform the action specified by `action' then it should return either XdndActionPrivate
|
||||
or XdndActionCopy into supported_action (leaving 0 supported_action unchanged is equivalent
|
||||
to XdndActionCopy). Returns 1 if ready to ok drop */
|
||||
int (*widget_apply_position) (DndClass * dnd, Window widgets_window, Window from,
|
||||
Atom action, int x, int y, Time t, Atom * typelist,
|
||||
int *want_position, Atom * supported_action, Atom * desired_type,
|
||||
XRectangle * rectangle);
|
||||
|
||||
/* returns drag data of the specified type. This will be one of `typelist' given to xdnd_drag */
|
||||
void (*widget_get_data) (DndClass * dnd, Window window, unsigned char **data, int *length, Atom type);
|
||||
|
||||
/* this is called from with the main event loop if an expose event is recieved and is optional */
|
||||
void (*handle_expose_events) (DndClass * dnd, XEvent * xevent);
|
||||
|
||||
/* creates a chooser dialog if the action is XdndActionAsk. Returns non-zero on cancel */
|
||||
int (*action_choose_dialog) (DndClass * dnd, char **descriptions, Atom * actions, Atom * result);
|
||||
|
||||
#if 0 /* implemented internally */
|
||||
/* returns a widget that is dnd aware within a parent widget that lies under the point x, y */
|
||||
Window (*widget_get_child_widget) (DndClass * dnd, Window parent, int x, int y);
|
||||
#endif
|
||||
|
||||
void *pad1[8];
|
||||
|
||||
DndCursor *cursors;
|
||||
|
||||
Display *display;
|
||||
|
||||
Atom XdndAware;
|
||||
Atom XdndSelection;
|
||||
Atom XdndEnter;
|
||||
Atom XdndLeave;
|
||||
Atom XdndPosition;
|
||||
Atom XdndDrop;
|
||||
Atom XdndFinished;
|
||||
Atom XdndStatus;
|
||||
Atom XdndActionCopy;
|
||||
Atom XdndActionMove;
|
||||
Atom XdndActionLink;
|
||||
Atom XdndActionAsk;
|
||||
Atom XdndActionPrivate;
|
||||
Atom XdndTypeList;
|
||||
Atom XdndActionList;
|
||||
Atom XdndActionDescription;
|
||||
|
||||
Atom Xdnd_NON_PROTOCOL_ATOM;
|
||||
Atom version;
|
||||
|
||||
Atom pad2[16];
|
||||
|
||||
Window root_window;
|
||||
|
||||
#define XDND_DROP_STAGE_IDLE 0
|
||||
#define XDND_DRAG_STAGE_DRAGGING 1
|
||||
#define XDND_DRAG_STAGE_ENTERED 2
|
||||
#define XDND_DROP_STAGE_CONVERTING 3
|
||||
#define XDND_DROP_STAGE_ENTERED 4
|
||||
int stage;
|
||||
int dragging_version;
|
||||
int internal_drag;
|
||||
int want_position;
|
||||
int ready_to_drop;
|
||||
int will_accept;
|
||||
XRectangle rectangle;
|
||||
Window dropper_window, dragger_window;
|
||||
Atom *dragger_typelist;
|
||||
Atom desired_type;
|
||||
Atom supported_action;
|
||||
Time time;
|
||||
/* drop position from last XdndPosition */
|
||||
int x, y;
|
||||
int pad3[16];
|
||||
|
||||
/* move euclidian pixels before considering this to be an actual drag */
|
||||
float drag_threshold;
|
||||
|
||||
/* block for only this many seconds on not receiving a XdndFinished from target, default : 10 */
|
||||
int time_out;
|
||||
|
||||
#define XDND_OPTION_NO_HYSTERESIS (1<<0)
|
||||
int options;
|
||||
|
||||
/* user hooks */
|
||||
void *user_hook1;
|
||||
void *user_hook2;
|
||||
void *user_hook3;
|
||||
Window dropper_toplevel;
|
||||
void *pad4[15];
|
||||
};
|
||||
|
||||
|
||||
void xdnd_init (DndClass * dnd, Display * display);
|
||||
void xdnd_shut (DndClass * dnd);
|
||||
/* for nested widgets where parent and child receive drops of different
|
||||
types; then always pass typelist as null */
|
||||
void xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist);
|
||||
int xdnd_is_dnd_aware (DndClass * dnd, Window window, int *version, Atom * typelist);
|
||||
void xdnd_set_type_list (DndClass * dnd, Window window, Atom * typelist);
|
||||
void xdnd_set_actions (DndClass * dnd, Window window, Atom * actions, char **descriptions);
|
||||
int xdnd_get_actions (DndClass * dnd, Window window, Atom ** actions, char ***descriptions);
|
||||
int xdnd_choose_action_dialog (DndClass * dnd, Atom * actions, char **descriptions, Atom * result);
|
||||
Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist);
|
||||
|
||||
/* Returns 1 if event is handled, This must be placed in the widget
|
||||
libraries main event loop and be called if the event type is
|
||||
ClientMessage or SelectionNotify */
|
||||
int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent);
|
||||
Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
|
||||
unsigned char **data, int *length, Atom * type, int *x, int *y);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_X_DND_H */
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
310
intern/ghost/intern/GHOST_DropTargetX11.cpp
Normal file
310
intern/ghost/intern/GHOST_DropTargetX11.cpp
Normal file
@ -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;
|
||||
}
|
135
intern/ghost/intern/GHOST_DropTargetX11.h
Normal file
135
intern/ghost/intern/GHOST_DropTargetX11.h
Normal file
@ -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_
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
@ -235,6 +235,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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -828,6 +828,7 @@ endif()
|
||||
extern_minilzo
|
||||
extern_lzma
|
||||
extern_colamd
|
||||
extern_xdnd
|
||||
ge_logic_ketsji
|
||||
extern_recastnavigation
|
||||
ge_phys_common
|
||||
|
Loading…
Reference in New Issue
Block a user