From afdef8c90ef6cea8d4ec10dc36b701db407625fc Mon Sep 17 00:00:00 2001 From: Alexander Iljin Date: Thu, 24 Aug 2017 04:14:56 +0300 Subject: [PATCH] windows.dropfiles[-docs]: implement the file-drop gesture --- basis/ui/gestures/gestures.factor | 2 + basis/windows/dropfiles/dropfiles-docs.factor | 49 ++++++++++++++--- basis/windows/dropfiles/dropfiles.factor | 52 ++++++++++++++++++- 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/basis/ui/gestures/gestures.factor b/basis/ui/gestures/gestures.factor index edabf341f6..96b69477f4 100644 --- a/basis/ui/gestures/gestures.factor +++ b/basis/ui/gestures/gestures.factor @@ -360,6 +360,8 @@ M: button-down gesture>string #>> [ " " % # ] when* ] "" make ; +M: file-drop gesture>string drop "Drop files" ; + M: left-action gesture>string drop "Swipe left" ; M: right-action gesture>string drop "Swipe right" ; diff --git a/basis/windows/dropfiles/dropfiles-docs.factor b/basis/windows/dropfiles/dropfiles-docs.factor index 80292b3951..661f8215dd 100644 --- a/basis/windows/dropfiles/dropfiles-docs.factor +++ b/basis/windows/dropfiles/dropfiles-docs.factor @@ -1,18 +1,53 @@ ! Copyright (C) 2017 Alexander Ilin. ! See http://factorcode.org/license.txt for BSD license. -USING: help.markup help.syntax kernel windows.ole32 ; +USING: arrays help.markup help.syntax kernel math +ui.backend.windows ui.gestures windows.types ; IN: windows.dropfiles +ABOUT: "windows-dropfiles" + +ARTICLE: "windows-dropfiles" "File drop gesture for Windows" +"A window has to declare whether it wants to accept dropped files. By default files are rejected:" +{ $subsections + accept-files + reject-files + world-accept-files + world-reject-files +} +"When user drops files onto a window, the target gadget may handle the corresponding gesture:" +{ $subsections file-drop } +"Implementation details:" +{ $subsections + "about-dragdrop" + init-message-filter +} ; + +ARTICLE: "about-dragdrop" "File drag-and-drop in Windows" +"There are two mechanisms in Windows that can be used to drag-and-drop files across applications:" +{ $list + { { $snippet "WM_DROPFILES" } " - was introduced back in the early days, it is a message that's posted to a window's message queue after the user has dropped some files on it. While handling the message, the application can fetch the list of the dropped files and the mouse position of the drop." } + { { $snippet "IDropTarget" } " - an OLE reinvention of the same. It provides more fine-grained capabilities of dynamically accepting or rejecting the drop based on the mouse location and the contents of the drop, while the user still drags the files over the window." } +} +"Windows Vista has introduced some security features that made it impossible for the OLE to work between two applications with different security tokens. E.g. if one of the applications is ran with administrative privileges, and the other is without, the OLE drag-and-drop will not work between them." +$nl +"By default, WM_DROPFILES doesn't work either, because the necessary window messages are filtered out from the queue, but it is possible to configure the filters and make it work, see " { $link init-message-filter } "." ; + +HELP: init-message-filter +{ $description "Call " { $snippet "ChangeWindowMessageFilter" } " to allow the window messages necessary for file dropping pass through the filters. This will have a process-wide effect, and will only be called once (when " { $link init-message-filter-done? } " is " { $link POSTPONE: f } ")." +$nl +"The API function is only available since Windows Vista, and is not needed in earlier versions. On Windows XP the missing function will cause an exception on the first call, which will be suppressed, and no more calls will be made." } +{ $notes "It is generally preferrable to use " { $snippet "ChangeWindowMessageFilterEx" } ", because it has a per-window-handle effect, thus gives a more fine-grained security control. Unfortunately, the " { $snippet "Ex" } "-version is only available since Windows 7, and in any case the " { $link add-wm-handler } " has global effect for all Factor native windows, so it's not like we are exposing any additional code to potential exploitation." } ; + HELP: filecount-from-hdrop { $values - { "hdrop" null } - { "n" null } + { "hdrop" HDROP } + { "n" number } } -{ $description "" } ; +{ $description "Return the number of files in the drop." } ; HELP: filenames-from-hdrop { $values - { "hdrop" null } - { "filenames" null } + { "hdrop" HDROP } + { "filenames" array } } -{ $description "" } ; +{ $description "Return an array of file names in the drop. Each file name is a string with a full path to a file or a folder." } ; diff --git a/basis/windows/dropfiles/dropfiles.factor b/basis/windows/dropfiles/dropfiles.factor index ab4ec14c37..0cf62c0298 100644 --- a/basis/windows/dropfiles/dropfiles.factor +++ b/basis/windows/dropfiles/dropfiles.factor @@ -1,7 +1,9 @@ ! Copyright (C) 2017 Alexander Ilin. ! See http://factorcode.org/license.txt for BSD license. -USING: alien.data alien.strings io.encodings.utf16n kernel math -sequences windows.messages windows.shell32 windows.types ; +USING: accessors alien alien.data alien.strings continuations +fry io.encodings.utf16n kernel literals math namespaces +sequences ui.backend.windows ui.gadgets.worlds ui.gestures +windows.messages windows.shell32 windows.types windows.user32 ; IN: windows.dropfiles : filecount-from-hdrop ( hdrop -- n ) @@ -15,3 +17,49 @@ IN: windows.dropfiles [ swap DragQueryFile drop ] keep utf16n alien>string ] with map ; + +! : point-from-hdrop ( hdrop -- loc ) +! POINT [ DragQueryPoint drop ] keep [ x>> ] [ y>> ] bi 2array ; + +: handle-wm-dropfiles ( hdrop -- ) + [ filenames-from-hdrop dropped-files set-global ] [ DragFinish ] bi + key-modifiers hand-gadget get-global propagate-gesture ; + +! The ChangeWindowMessageFilter has a global per-process effect, and so is the +! list of wm-handlers. Therefore, there is no benefit in using the stricter +! ChangeWindowMessageFilterEx approach. Plus, the latter is not in Vista. +: (init-message-filter) ( -- ) + ${ WM_DROPFILES WM_COPYDATA WM_COPYGLOBALDATA } + [ MSGFLT_ADD ChangeWindowMessageFilter win32-error=0/f ] each ; + +: do-once ( guard-variable quot -- ) + dupd '[ t _ set-global @ ] [ get-global ] dip unless ; inline + +SYMBOL: init-message-filter-done? + +! Ignore the errors: on WinXP the function is missing, and is not needed. +: init-message-filter ( -- ) + init-message-filter-done? [ + [ (init-message-filter) ] [ drop ] recover + ] do-once ; + +: install-wm-handler ( -- ) + [ drop 2nip handle-wm-dropfiles 0 ] WM_DROPFILES add-wm-handler ; + +: hwnd-accept-files ( hwnd -- ) + TRUE DragAcceptFiles init-message-filter install-wm-handler ; + +: hwnd-reject-files ( hwnd -- ) + f DragAcceptFiles ; + +: world-accept-files ( world -- ) + handle>> hWnd>> hwnd-accept-files ; + +: world-reject-files ( world -- ) + handle>> hWnd>> hwnd-accept-files ; + +: accept-files ( -- ) + world get world-accept-files ; + +: reject-files ( -- ) + world get world-reject-files ; -- 2.34.1