1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#ifndef FILE_DROP_H_09457802957842560325626
#define FILE_DROP_H_09457802957842560325626
#include <vector>
#include <functional>
#include <zen/zstring.h>
#include <zen/utf.h>
#include <wx/window.h>
#include <wx/event.h>
#include <wx/dnd.h>
#ifdef ZEN_WIN_VISTA_AND_LATER
#include <zen/win.h>
#endif
namespace zen
{
//register simple file drop event (without issue of freezing dialogs and without wxFileDropTarget overdesign)
//CAVEAT: a drop target window must not be directly or indirectly contained within a wxStaticBoxSizer until the following wxGTK bug
//is fixed. According to wxWidgets release cycles this is expected to be: never http://trac.wxwidgets.org/ticket/2763
/*
1. setup a window to emit EVENT_DROP_FILE:
- simple file system paths: setupFileDrop
- any shell paths with validation: setupShellItemDrop
2. register events:
wnd.Connect (EVENT_DROP_FILE, FileDropEventHandler(MyDlg::OnFilesDropped), nullptr, this);
wnd.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(MyDlg::OnFilesDropped), nullptr, this);
3. do something:
void MyDlg::OnFilesDropped(FileDropEvent& event);
*/
namespace impl
{
inline
wxEventType getFileDropEventType()
{
//inline functions have external linkage by default => this static is also extern, i.e. program wide unique! but defined in a header... ;)
static wxEventType dummy = wxNewEventType();
return dummy;
}
}
//define new event type
const wxEventType EVENT_DROP_FILE = impl::getFileDropEventType();
class FileDropEvent : public wxCommandEvent
{
public:
FileDropEvent(const std::vector<Zstring>& droppedPaths) : wxCommandEvent(EVENT_DROP_FILE), droppedPaths_(droppedPaths) {}
const std::vector<Zstring>& getPaths() const { return droppedPaths_; }
private:
wxEvent* Clone() const override { return new FileDropEvent(*this); }
const std::vector<Zstring> droppedPaths_;
};
using FileDropEventFunction = void (wxEvtHandler::*)(FileDropEvent&);
#define FileDropEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FileDropEventFunction, &func)
#ifdef ZEN_WIN_VISTA_AND_LATER
using DragDropValidator = bool (*)(const std::vector<Zstring>& shellItemPaths); //plain static function => no lifetime management needed!
using DragDropConsumer = std::function<void (const std::vector<Zstring>& shellItemPaths)>;
namespace impl
{
void registerDragDrop(HWND hwnd, const DragDropValidator& acceptDrop, const DragDropConsumer& onDrop);
void unregisterDragDrop(HWND hwnd);
class DragDropCleanupWindow : private wxWindow
{
public:
DragDropCleanupWindow(wxWindow& dropWindow) : wxWindow(&dropWindow, wxID_ANY), dropHwnd(dropWindow.GetHWND())
{
Hide(); //this is just a dummy window so that its parent can have ownership
Disable();
}
~DragDropCleanupWindow() { impl::unregisterDragDrop(dropHwnd); }
private:
const HWND dropHwnd;
};
}
inline
void setupShellItemDrop(wxWindow& dropWindow, const DragDropValidator& acceptDrop)
{
auto onDrop = [&dropWindow](const std::vector<Zstring>& shellItemPaths)
{
//create a custom event on drop window: execute event after file dropping is completed! (after mouse is released)
if (wxEvtHandler* handler = dropWindow.GetEventHandler())
handler->AddPendingEvent(FileDropEvent(shellItemPaths));
};
impl::registerDragDrop(static_cast<HWND>(dropWindow.GetHWND()), acceptDrop, onDrop);
//make sure clean-up is tied to dropWindow life-time:
new impl::DragDropCleanupWindow(dropWindow); //ownership passed to "dropWindow"
}
#endif
namespace impl
{
class WindowDropTarget : public wxFileDropTarget
{
public:
WindowDropTarget(wxWindow& dropWindow) : dropWindow_(dropWindow) {}
private:
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& fileArray) override
{
/*Linux, MTP: we get an empty file array
=> switching to wxTextDropTarget won't help (much): we'd get the format
mtp://[usb:001,002]/Telefonspeicher/Folder/file.txt
instead of
/run/user/1000/gvfs/mtp:host=%5Busb%3A001%2C002%5D/Telefonspeicher/Folder/file.txt
*/
//wxPoint clientDropPos(x, y)
std::vector<Zstring> filePaths;
for (const wxString& file : fileArray)
filePaths.push_back(utfCvrtTo<Zstring>(file));
//create a custom event on drop window: execute event after file dropping is completed! (after mouse is released)
if (wxEvtHandler* handler = dropWindow_.GetEventHandler())
handler->AddPendingEvent(FileDropEvent(filePaths));
return true;
}
wxWindow& dropWindow_;
};
}
inline
void setupFileDrop(wxWindow& wnd) { wnd.SetDropTarget(new impl::WindowDropTarget(wnd)); /*takes ownership*/ }
}
#endif //FILE_DROP_H_09457802957842560325626
|