Initial drag and drop support.
This commit is contained in:
parent
0548c713e8
commit
89d0723ba3
@ -773,6 +773,20 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
|
||||
*/
|
||||
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
||||
|
||||
|
||||
/*! @brief The function signature for drop callbacks.
|
||||
*
|
||||
* This is the function signature for drop callbacks.
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] string The string descriptor for the dropped object.
|
||||
*
|
||||
* @sa glfwSetDropCallback
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*,const char*);
|
||||
|
||||
/*! @brief The function signature for monitor configuration callbacks.
|
||||
*
|
||||
* This is the function signature for monitor configuration callback functions.
|
||||
@ -2014,6 +2028,22 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu
|
||||
*/
|
||||
GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun);
|
||||
|
||||
/*! @brief Sets the drop callback.
|
||||
*
|
||||
* This function sets the drop callback of the specified window, which is
|
||||
* called when an object is dropped over the window.
|
||||
*
|
||||
*
|
||||
* @param[in] window The window whose callback to set.
|
||||
* @param[in] cbfun The new drop callback, or `NULL` to remove the currently
|
||||
* set callback.
|
||||
* @return The previously set callback, or `NULL` if no callback was set or an
|
||||
* error occurred.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun);
|
||||
|
||||
/*! @brief Returns whether the specified joystick is present.
|
||||
*
|
||||
* This function returns whether the specified joystick is present.
|
||||
|
@ -413,6 +413,8 @@ static int translateKey(unsigned int key)
|
||||
@interface GLFWContentView : NSView
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
char * fileNamesForDrag;
|
||||
int fileNamesSize;
|
||||
NSTrackingArea* trackingArea;
|
||||
}
|
||||
|
||||
@ -444,7 +446,14 @@ static int translateKey(unsigned int key)
|
||||
window = initWindow;
|
||||
trackingArea = nil;
|
||||
|
||||
fileNamesForDrag = (char*)malloc(1024);
|
||||
fileNamesSize = 1024;
|
||||
|
||||
[self updateTrackingAreas];
|
||||
|
||||
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObjects:
|
||||
NSFilenamesPboardType, nil]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -453,6 +462,7 @@ static int translateKey(unsigned int key)
|
||||
-(void)dealloc
|
||||
{
|
||||
[trackingArea release];
|
||||
free(fileNamesForDrag);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -657,6 +667,72 @@ static int translateKey(unsigned int key)
|
||||
_glfwInputScroll(window, deltaX, deltaY);
|
||||
}
|
||||
|
||||
|
||||
// arturoc: this makes the cursor dissapear when the window is
|
||||
// resized or received a drag operation
|
||||
/*- (void)resetCursorRects
|
||||
{
|
||||
[self discardCursorRects];
|
||||
[self addCursorRect:[self bounds] cursor:_glfw.ns.cursor];
|
||||
}*/
|
||||
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
|
||||
== NSDragOperationGeneric) {
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return NSDragOperationGeneric;
|
||||
|
||||
}
|
||||
|
||||
return NSDragOperationNone;
|
||||
}
|
||||
|
||||
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
|
||||
[self setNeedsDisplay:YES];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
|
||||
NSPasteboard *zPasteboard = [sender draggingPasteboard];
|
||||
NSArray *files = [zPasteboard propertyListForType:NSFilenamesPboardType];
|
||||
|
||||
// set the first char to 0 so strcat
|
||||
// starts to add from the beginning
|
||||
fileNamesForDrag[0] = 0;
|
||||
|
||||
int dragX = [sender draggingLocation].x;
|
||||
int dragY = [sender draggingLocation].y;
|
||||
|
||||
int dragSize = 1;
|
||||
if ([files count]) {
|
||||
NSEnumerator *filenameEnum = [files objectEnumerator];
|
||||
NSString *name;
|
||||
while (name = [filenameEnum nextObject]) {
|
||||
dragSize += [name length]+1;
|
||||
if (dragSize > fileNamesSize){
|
||||
fileNamesSize *= 2;
|
||||
fileNamesForDrag = realloc(fileNamesForDrag, fileNamesSize);
|
||||
}
|
||||
strcat(fileNamesForDrag, [name UTF8String]);
|
||||
strcat(fileNamesForDrag, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
int height;
|
||||
_glfwPlatformGetWindowSize(window, NULL, &height);
|
||||
_glfwInputCursorMotion(window, dragX, height-dragY);
|
||||
_glfwInputDrop(window, fileNamesForDrag);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender {
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
17
src/input.c
17
src/input.c
@ -211,6 +211,12 @@ void _glfwInputCursorEnter(_GLFWwindow* window, int entered)
|
||||
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
|
||||
}
|
||||
|
||||
void _glfwInputDrop(_GLFWwindow* window, const char* dropString){
|
||||
|
||||
if (window->callbacks.drop)
|
||||
window->callbacks.drop((GLFWwindow*) window, dropString);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW public API //////
|
||||
@ -394,3 +400,14 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle,
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
GLFWdropfun previous;
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
previous = window->callbacks.drop;
|
||||
window->callbacks.drop = cbfun;
|
||||
return previous;
|
||||
}
|
||||
|
@ -239,6 +239,7 @@ struct _GLFWwindow
|
||||
GLFWscrollfun scroll;
|
||||
GLFWkeyfun key;
|
||||
GLFWcharfun character;
|
||||
GLFWdropfun drop;
|
||||
} callbacks;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
@ -678,6 +679,14 @@ void _glfwInputMonitorChange(void);
|
||||
*/
|
||||
void _glfwInputError(int error, const char* format, ...);
|
||||
|
||||
/*! @brief Notifies dropped object over window.
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] dropString The string descriptor of the dropped object
|
||||
* description.
|
||||
* @ingroup event
|
||||
*/
|
||||
void _glfwInputDrop(_GLFWwindow* window, const char* dropString);
|
||||
|
||||
|
||||
//========================================================================
|
||||
// Utility functions
|
||||
|
@ -215,6 +215,9 @@ int _glfwPlatformInit(void)
|
||||
if (!_glfwInitContextAPI())
|
||||
return GL_FALSE;
|
||||
|
||||
_glfw.win32.dropString = (char*)malloc(1000);
|
||||
_glfw.win32.dropStringSize = 1000;
|
||||
|
||||
_glfwInitTimer();
|
||||
_glfwInitJoysticks();
|
||||
|
||||
|
@ -169,6 +169,8 @@ typedef struct _GLFWlibraryWin32
|
||||
ATOM classAtom;
|
||||
DWORD foregroundLockTimeout;
|
||||
char* clipboardString;
|
||||
char* dropString;
|
||||
int dropStringSize;
|
||||
|
||||
// Timer data
|
||||
struct {
|
||||
|
@ -29,7 +29,9 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <Windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <Shellapi.h>
|
||||
|
||||
#define _GLFW_KEY_INVALID -2
|
||||
|
||||
@ -747,6 +749,39 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
// TODO: Restore vsync if compositing was disabled
|
||||
break;
|
||||
}
|
||||
case WM_DROPFILES:
|
||||
{
|
||||
TCHAR szName[MAX_PATH];
|
||||
HDROP hDrop = (HDROP)wParam;
|
||||
POINT pt;
|
||||
int numFiles = DragQueryFile(hDrop, 0xFFFFFFFF, szName, MAX_PATH);
|
||||
int currentSize = 1;
|
||||
int i;
|
||||
char* utf8str;
|
||||
DragQueryPoint(hDrop, &pt);
|
||||
|
||||
// Move the mouse to the position of the drop
|
||||
_glfwInputCursorMotion(window,pt.x,pt.y);
|
||||
|
||||
memset(_glfw.win32.dropString, 0, _glfw.win32.dropStringSize);
|
||||
for(i = 0; i < numFiles; i++)
|
||||
{
|
||||
DragQueryFile(hDrop, i, szName, MAX_PATH);
|
||||
utf8str = _glfwCreateUTF8FromWideString((const wchar_t*)szName);
|
||||
currentSize += strlen(utf8str);
|
||||
if(_glfw.win32.dropStringSize < currentSize){
|
||||
_glfw.win32.dropStringSize *= 2;
|
||||
_glfw.win32.dropString = (char*)realloc(_glfw.win32.dropString,_glfw.win32.dropStringSize);
|
||||
}
|
||||
strcat(_glfw.win32.dropString, utf8str);
|
||||
strcat(_glfw.win32.dropString, "\n");
|
||||
free(utf8str);
|
||||
}
|
||||
|
||||
_glfwInputDrop(window,_glfw.win32.dropString);
|
||||
DragFinish(hDrop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
@ -866,6 +901,8 @@ static int createWindow(_GLFWwindow* window,
|
||||
|
||||
free(wideTitle);
|
||||
|
||||
DragAcceptFiles(window->win32.handle, TRUE);
|
||||
|
||||
if (!window->win32.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window");
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// Translate an X11 key code to a GLFW key code.
|
||||
@ -394,7 +395,6 @@ static void detectEWMH(void)
|
||||
(unsigned char**) &supportedAtoms);
|
||||
|
||||
// See which of the atoms we support that are supported by the WM
|
||||
|
||||
_glfw.x11.NET_WM_STATE =
|
||||
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE");
|
||||
_glfw.x11.NET_WM_STATE_FULLSCREEN =
|
||||
@ -539,6 +539,21 @@ static GLboolean initExtensions(void)
|
||||
_glfw.x11.SAVE_TARGETS =
|
||||
XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
|
||||
|
||||
// Find or create drag and drop atoms
|
||||
|
||||
//Atoms for Xdnd
|
||||
_glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", True);
|
||||
_glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", True);
|
||||
_glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", True);
|
||||
_glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", True);
|
||||
_glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", True);
|
||||
_glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", True);
|
||||
_glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", True);
|
||||
_glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", True);
|
||||
_glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", True);
|
||||
|
||||
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,24 @@ typedef struct _GLFWlibraryX11
|
||||
Atom NET_ACTIVE_WINDOW;
|
||||
Atom MOTIF_WM_HINTS;
|
||||
|
||||
// Atoms for Xdnd
|
||||
Atom XdndAware;
|
||||
Atom XdndEnter;
|
||||
Atom XdndPosition;
|
||||
Atom XdndStatus;
|
||||
Atom XdndActionCopy;
|
||||
Atom XdndDrop;
|
||||
Atom XdndLeave;
|
||||
Atom XdndFinished;
|
||||
Atom XdndSelection;
|
||||
struct{
|
||||
Window sourceWindow;
|
||||
char* string;
|
||||
char* type1;
|
||||
char* type2;
|
||||
char* type3;
|
||||
} xdnd;
|
||||
|
||||
// Selection atoms
|
||||
Atom TARGETS;
|
||||
Atom MULTIPLE;
|
||||
|
130
src/x11_window.c
130
src/x11_window.c
@ -306,6 +306,14 @@ static GLboolean createWindow(_GLFWwindow* window,
|
||||
XISelectEvents(_glfw.x11.display, window->x11.handle, &eventmask, 1);
|
||||
}
|
||||
|
||||
// Enable Xdnd
|
||||
if(_glfw.x11.XdndAware!=None)
|
||||
{
|
||||
//Announce XDND support
|
||||
Atom version=5;
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&version, 1);
|
||||
}
|
||||
|
||||
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
||||
|
||||
XRRSelectInput(_glfw.x11.display, window->x11.handle,
|
||||
@ -494,6 +502,7 @@ static void leaveFullscreenMode(_GLFWwindow* window)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process the specified X event
|
||||
//
|
||||
static void processEvent(XEvent *event)
|
||||
@ -703,10 +712,128 @@ static void processEvent(XEvent *event)
|
||||
False,
|
||||
SubstructureNotifyMask | SubstructureRedirectMask,
|
||||
event);
|
||||
|
||||
}
|
||||
else if(event->xclient.message_type == _glfw.x11.XdndEnter)
|
||||
{
|
||||
// Xdnd Enter: the drag&drop event has started in the window,
|
||||
// we could be getting the type and possible conversions here
|
||||
// but since we use always string conversion we don't need
|
||||
// it
|
||||
}
|
||||
else if(event->xclient.message_type == _glfw.x11.XdndDrop)
|
||||
{
|
||||
// Xdnd Drop: The drag&drop event has finished dropping on
|
||||
// the window, ask to convert the selection
|
||||
_glfw.x11.xdnd.sourceWindow = event->xclient.data.l[0];
|
||||
XConvertSelection(_glfw.x11.display,
|
||||
_glfw.x11.XdndSelection,
|
||||
_glfw.x11.UTF8_STRING,
|
||||
_glfw.x11.XdndSelection,
|
||||
window->x11.handle, CurrentTime);
|
||||
|
||||
}
|
||||
else if(event->xclient.message_type == _glfw.x11.XdndLeave)
|
||||
{
|
||||
|
||||
}
|
||||
else if(event->xclient.message_type == _glfw.x11.XdndPosition)
|
||||
{
|
||||
// Xdnd Position: get coordinates of the mouse inside the window
|
||||
// and update the mouse position
|
||||
int absX = (event->xclient.data.l[2]>>16) & 0xFFFF;
|
||||
int absY = (event->xclient.data.l[2]) & 0xFFFF;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
_glfwPlatformGetWindowPos(window,&x,&y);
|
||||
|
||||
_glfwInputCursorMotion(window,absX-x,absY-y);
|
||||
|
||||
// Xdnd: reply with an XDND status message
|
||||
XClientMessageEvent m;
|
||||
memset(&m, sizeof(m), 0);
|
||||
m.type = ClientMessage;
|
||||
m.display = event->xclient.display;
|
||||
m.window = event->xclient.data.l[0];
|
||||
m.message_type = _glfw.x11.XdndStatus;
|
||||
m.format=32;
|
||||
m.data.l[0] = window->x11.handle;
|
||||
m.data.l[1] = 1; // Always accept the dnd with no rectangle
|
||||
m.data.l[2] = 0; // Specify an empty rectangle
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = _glfw.x11.XdndActionCopy; // We only accept copying
|
||||
|
||||
XSendEvent(_glfw.x11.display, event->xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SelectionNotify:
|
||||
{
|
||||
if(event->xselection.property != None)
|
||||
{
|
||||
// Xdnd: got a selection notification from the conversion
|
||||
// we asked for, get the data and finish the d&d event
|
||||
char* data;
|
||||
free(_glfw.x11.xdnd.string);
|
||||
_glfw.x11.xdnd.string = NULL;
|
||||
int result = _glfwGetWindowProperty(event->xselection.requestor,
|
||||
event->xselection.property,
|
||||
event->xselection.target,
|
||||
(unsigned char**) &data);
|
||||
|
||||
if(result){
|
||||
// nautilus seems to add a \r at the end of the paths
|
||||
// remove it so paths can be directly used
|
||||
_glfw.x11.xdnd.string = malloc(strlen(data));
|
||||
char *to = _glfw.x11.xdnd.string;
|
||||
const char *from = data;
|
||||
const char *current = strchr(from, '\r');
|
||||
while(current)
|
||||
{
|
||||
int charsToCopy = current - from;
|
||||
memcpy(to, from, (size_t)charsToCopy);
|
||||
to += charsToCopy;
|
||||
|
||||
from = current+1;
|
||||
current = strchr(from, '\r');
|
||||
}
|
||||
|
||||
size_t remaining = strlen(from);
|
||||
|
||||
memcpy(to, from, remaining);
|
||||
to += remaining;
|
||||
*to = 0;
|
||||
}
|
||||
|
||||
XClientMessageEvent m;
|
||||
memset(&m, sizeof(m), 0);
|
||||
m.type = ClientMessage;
|
||||
m.display = _glfw.x11.display;
|
||||
m.window = _glfw.x11.xdnd.sourceWindow;
|
||||
m.message_type = _glfw.x11.XdndFinished;
|
||||
m.format=32;
|
||||
m.data.l[0] = window->x11.handle;
|
||||
m.data.l[1] = result;
|
||||
m.data.l[2] = _glfw.x11.XdndActionCopy; //We only ever copy.
|
||||
|
||||
// Reply that all is well.
|
||||
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.sourceWindow, False, NoEventMask, (XEvent*)&m);
|
||||
|
||||
XSync(_glfw.x11.display, False);
|
||||
|
||||
XFree(data);
|
||||
|
||||
if(result)
|
||||
{
|
||||
_glfwInputDrop(window,_glfw.x11.xdnd.string);
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MapNotify:
|
||||
{
|
||||
@ -897,7 +1024,7 @@ unsigned long _glfwGetWindowProperty(Window window,
|
||||
&bytesAfter,
|
||||
value);
|
||||
|
||||
if (actualType != type)
|
||||
if (type != AnyPropertyType && actualType != type)
|
||||
return 0;
|
||||
|
||||
return itemCount;
|
||||
@ -1001,7 +1128,6 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
|
||||
if (child)
|
||||
{
|
||||
int left, top;
|
||||
|
||||
XTranslateCoordinates(_glfw.x11.display, window->x11.handle, child,
|
||||
0, 0, &left, &top, &child);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user