improve dnd interfaces
This commit is contained in:
parent
01ed1d13e9
commit
ce1b143b59
@ -22,6 +22,14 @@
|
|||||||
|
|
||||||
namespace nana
|
namespace nana
|
||||||
{
|
{
|
||||||
|
/// Drag and drop actions
|
||||||
|
enum class dnd_action
|
||||||
|
{
|
||||||
|
copy, ///< Copy the data to target.
|
||||||
|
move, ///< Move the data to target.
|
||||||
|
link ///< Create a link from source data to target.
|
||||||
|
};
|
||||||
|
|
||||||
class simple_dragdrop
|
class simple_dragdrop
|
||||||
{
|
{
|
||||||
struct implementation;
|
struct implementation;
|
||||||
@ -67,7 +75,12 @@ namespace nana
|
|||||||
data(const data&) = delete;
|
data(const data&) = delete;
|
||||||
data& operator=(const data&) = delete;
|
data& operator=(const data&) = delete;
|
||||||
public:
|
public:
|
||||||
data();
|
/// Constructor
|
||||||
|
/**
|
||||||
|
* Constructs a data object used for drag and drop
|
||||||
|
* @param requested_action Indicates how the data to be transferred.
|
||||||
|
*/
|
||||||
|
data(dnd_action requested_action = dnd_action::copy);
|
||||||
data(data&&);
|
data(data&&);
|
||||||
~data();
|
~data();
|
||||||
|
|
||||||
@ -81,9 +94,29 @@ namespace nana
|
|||||||
dragdrop(window source);
|
dragdrop(window source);
|
||||||
~dragdrop();
|
~dragdrop();
|
||||||
|
|
||||||
|
/// Condition of dragging
|
||||||
|
/***
|
||||||
|
* The preciate function is called when press mouse button on the source window, it returns true to indicate the start of dragging. If the predicate is not set, it always start to drag.
|
||||||
|
* @param predicate_fn A predicate function to be set.
|
||||||
|
*/
|
||||||
void condition(std::function<bool()> predicate_fn);
|
void condition(std::function<bool()> predicate_fn);
|
||||||
|
|
||||||
|
/// Transferred data
|
||||||
|
/**
|
||||||
|
* Set a data generator. When drag begins, it is called to generate a data object for transferring.
|
||||||
|
* @param generator It returns the data for transferring.
|
||||||
|
*/
|
||||||
void prepare_data(std::function<data()> generator);
|
void prepare_data(std::function<data()> generator);
|
||||||
void drop_finished(std::function<void(bool)> finish_fn);
|
|
||||||
|
/// Drop handler
|
||||||
|
/**
|
||||||
|
* The drop handler is called when the drop operation is completed. There are 3 parameters for the handler
|
||||||
|
* dropped Indicates whether the data is accepted by a target window.
|
||||||
|
* executed_action Indicates the action returned by target window. Ignore if dropped is false.
|
||||||
|
* data_transferred The data object which is generated by the generator.
|
||||||
|
* @param finish_fn The drop handling function.
|
||||||
|
*/
|
||||||
|
void drop_finished(std::function<void(bool dropped, dnd_action executed_action, data& data_transferred)> finish_fn);
|
||||||
private:
|
private:
|
||||||
implementation* const impl_;
|
implementation* const impl_;
|
||||||
};
|
};
|
||||||
|
@ -513,6 +513,8 @@ namespace detail
|
|||||||
atombase_.xdnd_position = ::XInternAtom(display_, "XdndPosition", False);
|
atombase_.xdnd_position = ::XInternAtom(display_, "XdndPosition", False);
|
||||||
atombase_.xdnd_status = ::XInternAtom(display_, "XdndStatus", False);
|
atombase_.xdnd_status = ::XInternAtom(display_, "XdndStatus", False);
|
||||||
atombase_.xdnd_action_copy = ::XInternAtom(display_, "XdndActionCopy", False);
|
atombase_.xdnd_action_copy = ::XInternAtom(display_, "XdndActionCopy", False);
|
||||||
|
atombase_.xdnd_action_move = ::XInternAtom(display_, "XdndActionMove", False);
|
||||||
|
atombase_.xdnd_action_link = ::XInternAtom(display_, "XdndActionLink", False);
|
||||||
atombase_.xdnd_drop = ::XInternAtom(display_, "XdndDrop", False);
|
atombase_.xdnd_drop = ::XInternAtom(display_, "XdndDrop", False);
|
||||||
atombase_.xdnd_selection = ::XInternAtom(display_, "XdndSelection", False);
|
atombase_.xdnd_selection = ::XInternAtom(display_, "XdndSelection", False);
|
||||||
atombase_.xdnd_typelist = ::XInternAtom(display_, "XdndTypeList", False);
|
atombase_.xdnd_typelist = ::XInternAtom(display_, "XdndTypeList", False);
|
||||||
|
@ -159,6 +159,8 @@ namespace detail
|
|||||||
Atom xdnd_position;
|
Atom xdnd_position;
|
||||||
Atom xdnd_status;
|
Atom xdnd_status;
|
||||||
Atom xdnd_action_copy;
|
Atom xdnd_action_copy;
|
||||||
|
Atom xdnd_action_move;
|
||||||
|
Atom xdnd_action_link;
|
||||||
Atom xdnd_drop;
|
Atom xdnd_drop;
|
||||||
Atom xdnd_selection;
|
Atom xdnd_selection;
|
||||||
Atom xdnd_typelist;
|
Atom xdnd_typelist;
|
||||||
|
@ -16,69 +16,26 @@
|
|||||||
|
|
||||||
#include "platform_spec.hpp"
|
#include "platform_spec.hpp"
|
||||||
#include <nana/filesystem/filesystem.hpp>
|
#include <nana/filesystem/filesystem.hpp>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
#include "theme.hpp"
|
||||||
#include <X11/Xcursor/Xcursor.h>
|
#include <X11/Xcursor/Xcursor.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
#define DEBUG_XDND_PROTOCOL
|
||||||
|
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
#include <iostream> //debug
|
#include <iostream> //debug
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nana{
|
namespace nana{
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
class shared_icons
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
shared_icons()
|
|
||||||
{
|
|
||||||
path_ = "/usr/share/icons/";
|
|
||||||
ifs_.open(path_ + "default/index.theme");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cursor(const std::string& name)
|
|
||||||
{
|
|
||||||
auto theme = _m_read("Icon Theme", "Inherits");
|
|
||||||
|
|
||||||
return path_ + theme + "/cursors/" + name;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string _m_read(const std::string& category, const std::string& key)
|
|
||||||
{
|
|
||||||
ifs_.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
bool found_cat = false;
|
|
||||||
while(ifs_.good())
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
std::getline(ifs_, text);
|
|
||||||
|
|
||||||
if(0 == text.find('['))
|
|
||||||
{
|
|
||||||
if(found_cat)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if(text.find(category + "]") != text.npos)
|
|
||||||
{
|
|
||||||
found_cat = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(found_cat && (text.find(key + "=") == 0))
|
|
||||||
{
|
|
||||||
return text.substr(key.size() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string path_;
|
|
||||||
std::ifstream ifs_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct xdnd_data
|
struct xdnd_data
|
||||||
{
|
{
|
||||||
|
Atom requested_action;
|
||||||
std::vector<std::filesystem::path> files;
|
std::vector<std::filesystem::path> files;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,31 +57,40 @@ namespace nana{
|
|||||||
auto disp = spec_.open_display();
|
auto disp = spec_.open_display();
|
||||||
detail::platform_scope_guard lock;
|
detail::platform_scope_guard lock;
|
||||||
::XSetSelectionOwner(disp, spec_.atombase().xdnd_selection, source, CurrentTime);
|
::XSetSelectionOwner(disp, spec_.atombase().xdnd_selection, source, CurrentTime);
|
||||||
std::cout<<"XSetSelectionOwner "<<source<<std::endl;
|
|
||||||
|
|
||||||
shared_icons icons;
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
|
std::cout<<"XSetSelectionOwner "<<source<<std::endl;
|
||||||
|
#endif
|
||||||
|
/* //deprecated
|
||||||
|
theme icons;
|
||||||
|
cursor_.dnd_copy = ::XcursorFilenameLoadCursor(disp, icons.cursor("dnd-copy").c_str());
|
||||||
cursor_.dnd_move = ::XcursorFilenameLoadCursor(disp, icons.cursor("dnd-move").c_str());
|
cursor_.dnd_move = ::XcursorFilenameLoadCursor(disp, icons.cursor("dnd-move").c_str());
|
||||||
cursor_.dnd_none = ::XcursorFilenameLoadCursor(disp, icons.cursor("dnd-none").c_str());
|
cursor_.dnd_none = ::XcursorFilenameLoadCursor(disp, icons.cursor("dnd-none").c_str());
|
||||||
|
*/
|
||||||
|
cursor_.dnd_copy = ::XcursorLibraryLoadCursor(disp, "dnd-copy");
|
||||||
|
cursor_.dnd_move = ::XcursorLibraryLoadCursor(disp, "dnd-move");
|
||||||
|
cursor_.dnd_none = ::XcursorLibraryLoadCursor(disp, "dnd-none");
|
||||||
}
|
}
|
||||||
|
|
||||||
~xdnd_protocol()
|
~xdnd_protocol()
|
||||||
{
|
{
|
||||||
auto disp = spec_.open_display();
|
auto disp = spec_.open_display();
|
||||||
|
::XFreeCursor(disp, cursor_.dnd_copy);
|
||||||
::XFreeCursor(disp, cursor_.dnd_move);
|
::XFreeCursor(disp, cursor_.dnd_move);
|
||||||
::XFreeCursor(disp, cursor_.dnd_none);
|
::XFreeCursor(disp, cursor_.dnd_none);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mouse_move(Window wd, const nana::point& pos)
|
void mouse_move(Window wd, const nana::point& pos, Atom requested_action)
|
||||||
{
|
{
|
||||||
if(wd != target_)
|
if(wd != target_)
|
||||||
{
|
{
|
||||||
_m_xdnd_leave();
|
_m_xdnd_leave();
|
||||||
|
|
||||||
if(_m_xdnd_enter(wd))
|
if(_m_xdnd_enter(wd))
|
||||||
_m_xdnd_position(pos);
|
_m_xdnd_position(pos, requested_action);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_m_xdnd_position(pos);
|
_m_xdnd_position(pos, requested_action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mouse_leave()
|
void mouse_leave()
|
||||||
@ -132,9 +98,14 @@ namespace nana{
|
|||||||
_m_xdnd_leave();
|
_m_xdnd_leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mouse_release()
|
bool mouse_release()
|
||||||
{
|
{
|
||||||
_m_xdnd_drop();
|
return _m_xdnd_drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Atom executed_action() const
|
||||||
|
{
|
||||||
|
return executed_action_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return true to exit xdnd_protocol event handler
|
//Return true to exit xdnd_protocol event handler
|
||||||
@ -144,16 +115,18 @@ namespace nana{
|
|||||||
|
|
||||||
if(atombase.xdnd_status == xclient.message_type)
|
if(atombase.xdnd_status == xclient.message_type)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<"Event: XdndStatus"<<std::endl;
|
std::cout<<"Event: XdndStatus"<<std::endl;
|
||||||
|
#endif
|
||||||
if(xdnd_status_state::position != xstate_ && xdnd_status_state::drop != xstate_)
|
if(xdnd_status_state::position != xstate_ && xdnd_status_state::drop != xstate_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Window target_wd = static_cast<Window>(xclient.data.l[0]);
|
Window target_wd = static_cast<Window>(xclient.data.l[0]);
|
||||||
bool is_accepted_by_target = (xclient.data.l[1] & 1);
|
bool is_accepted_by_target = (xclient.data.l[1] & 1);
|
||||||
|
|
||||||
std::cout<<"XdndStatus: Accepted="<<is_accepted_by_target<<", target="<<is_accepted_by_target<<std::endl;
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
|
std::cout<<"XdndStatus: Accepted="<<is_accepted_by_target<<", target="<<target_wd<<std::endl;
|
||||||
|
#endif
|
||||||
if(xclient.data.l[1] & 0x2)
|
if(xclient.data.l[1] & 0x2)
|
||||||
{
|
{
|
||||||
rectangle rct{
|
rectangle rct{
|
||||||
@ -166,21 +139,28 @@ namespace nana{
|
|||||||
if(!rct.empty())
|
if(!rct.empty())
|
||||||
{
|
{
|
||||||
mvout_table_[target_wd] = rct;
|
mvout_table_[target_wd] = rct;
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<". rct=("<<rct.x<<","<<rct.y<<","<<rct.width<<","<<rct.height<<")";
|
std::cout<<". rct=("<<rct.x<<","<<rct.y<<","<<rct.width<<","<<rct.height<<")";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<std::endl;
|
std::cout<<std::endl;
|
||||||
|
#endif
|
||||||
_m_cursor(is_accepted_by_target);
|
_m_cursor(is_accepted_by_target);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if((!is_accepted_by_target) && (xdnd_status_state::drop == xstate_))
|
if((!is_accepted_by_target) && (xdnd_status_state::drop == xstate_))
|
||||||
{
|
{
|
||||||
_m_xdnd_leave();
|
_m_xdnd_leave();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executed_action_ = xclient.data.l[4];
|
||||||
|
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
|
std::cout<<" Assign ExecutedAction = "<<static_cast<int>(executed_action_)<<std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
xstate_ = xdnd_status_state::normal;
|
xstate_ = xdnd_status_state::normal;
|
||||||
}
|
}
|
||||||
else if(atombase.xdnd_finished == xclient.message_type)
|
else if(atombase.xdnd_finished == xclient.message_type)
|
||||||
@ -193,8 +173,9 @@ namespace nana{
|
|||||||
{
|
{
|
||||||
if(spec_.atombase().xdnd_selection == xselectionrequest.selection)
|
if(spec_.atombase().xdnd_selection == xselectionrequest.selection)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<"Event SelectionRequest: XdndSelection"<<std::endl;
|
std::cout<<"Event SelectionRequest: XdndSelection"<<std::endl;
|
||||||
|
#endif
|
||||||
::XEvent evt;
|
::XEvent evt;
|
||||||
evt.xselection.type = SelectionNotify;
|
evt.xselection.type = SelectionNotify;
|
||||||
evt.xselection.display = xselectionrequest.display;
|
evt.xselection.display = xselectionrequest.display;
|
||||||
@ -204,6 +185,7 @@ namespace nana{
|
|||||||
evt.xselection.property = 0;
|
evt.xselection.property = 0;
|
||||||
evt.xselection.time = xselectionrequest.time;
|
evt.xselection.time = xselectionrequest.time;
|
||||||
|
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
auto property_name = ::XGetAtomName(spec_.open_display(), xselectionrequest.property); //debug
|
auto property_name = ::XGetAtomName(spec_.open_display(), xselectionrequest.property); //debug
|
||||||
|
|
||||||
std::cout<<"SelectionRequest"<<std::endl;
|
std::cout<<"SelectionRequest"<<std::endl;
|
||||||
@ -213,11 +195,13 @@ namespace nana{
|
|||||||
std::cout<<" property ="<<xselectionrequest.property<<", name="<<property_name<<std::endl;
|
std::cout<<" property ="<<xselectionrequest.property<<", name="<<property_name<<std::endl;
|
||||||
std::cout<<" target ="<<xselectionrequest.target<<std::endl;
|
std::cout<<" target ="<<xselectionrequest.target<<std::endl;
|
||||||
::XFree(property_name);
|
::XFree(property_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(xselectionrequest.target == spec_.atombase().text_uri_list)
|
if(xselectionrequest.target == spec_.atombase().text_uri_list)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<"SelectionRequest target = text_uri_list";
|
std::cout<<"SelectionRequest target = text_uri_list";
|
||||||
|
#endif
|
||||||
if(data.files.size())
|
if(data.files.size())
|
||||||
{
|
{
|
||||||
std::string uri_list;
|
std::string uri_list;
|
||||||
@ -227,9 +211,9 @@ namespace nana{
|
|||||||
uri_list += file.u8string();
|
uri_list += file.u8string();
|
||||||
uri_list += "\r\n";
|
uri_list += "\r\n";
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<". URIs="<<uri_list<<std::endl;
|
std::cout<<". URIs="<<uri_list<<std::endl;
|
||||||
|
#endif
|
||||||
::XChangeProperty (spec_.open_display(),
|
::XChangeProperty (spec_.open_display(),
|
||||||
xselectionrequest.requestor,
|
xselectionrequest.requestor,
|
||||||
xselectionrequest.property,
|
xselectionrequest.property,
|
||||||
@ -242,9 +226,10 @@ namespace nana{
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
else
|
else
|
||||||
std::cout<<"SelectionRequest target = "<<xselectionrequest.target<<std::endl;
|
std::cout<<"SelectionRequest target = "<<xselectionrequest.target<<std::endl;
|
||||||
|
#endif
|
||||||
platform_scope_guard lock;
|
platform_scope_guard lock;
|
||||||
::XSendEvent(spec_.open_display(), xselectionrequest.requestor, False, 0, &evt);
|
::XSendEvent(spec_.open_display(), xselectionrequest.requestor, False, 0, &evt);
|
||||||
::XFlush(spec_.open_display());
|
::XFlush(spec_.open_display());
|
||||||
@ -254,7 +239,6 @@ namespace nana{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool _m_xdnd_enter(Window wd)
|
bool _m_xdnd_enter(Window wd)
|
||||||
{
|
{
|
||||||
//xdnd version of the window
|
//xdnd version of the window
|
||||||
@ -263,13 +247,15 @@ namespace nana{
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
target_ = wd;
|
target_ = wd;
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<"Send XdndEnter, text/uri-list="<<spec_.atombase().text_uri_list<<std::endl;
|
std::cout<<"Send XdndEnter, text/uri-list="<<spec_.atombase().text_uri_list<<std::endl;
|
||||||
|
#endif
|
||||||
_m_client_msg(spec_.atombase().xdnd_enter, (xdnd_ver << 24), spec_.atombase().text_uri_list, XA_STRING);
|
_m_client_msg(spec_.atombase().xdnd_enter, (xdnd_ver << 24), spec_.atombase().text_uri_list, XA_STRING);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _m_xdnd_position(const nana::point& pos)
|
void _m_xdnd_position(const nana::point& pos, Atom requested_action)
|
||||||
{
|
{
|
||||||
if(xdnd_status_state::normal != xstate_)
|
if(xdnd_status_state::normal != xstate_)
|
||||||
return;
|
return;
|
||||||
@ -278,12 +264,13 @@ namespace nana{
|
|||||||
if(i != mvout_table_.end() && i->second.is_hit(pos))
|
if(i != mvout_table_.end() && i->second.is_hit(pos))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::cout<<"Send: XdndPosition"<<std::endl;
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
|
std::cout<<"Send: XdndPosition, RequestedAction="<<requested_action<<", XdndActionCopy="<<spec_.atombase().xdnd_action_copy<<std::endl;
|
||||||
|
#endif
|
||||||
xstate_ = xdnd_status_state::position;
|
xstate_ = xdnd_status_state::position;
|
||||||
//Send XdndPosition
|
//Send XdndPosition
|
||||||
long position = (pos.x << 16 | pos.y);
|
long position = (pos.x << 16 | pos.y);
|
||||||
_m_client_msg(spec_.atombase().xdnd_position, 0, position, CurrentTime, spec_.atombase().xdnd_action_copy);
|
_m_client_msg(spec_.atombase().xdnd_position, 0, position, CurrentTime, requested_action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _m_xdnd_leave()
|
void _m_xdnd_leave()
|
||||||
@ -292,19 +279,30 @@ namespace nana{
|
|||||||
|
|
||||||
if(target_)
|
if(target_)
|
||||||
{
|
{
|
||||||
std::cout<<"Send: XdndLeave"<<std::endl;
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
|
std::cout<<"Send: XdndLeave, reset ExecutedAction"<<std::endl;
|
||||||
|
#endif
|
||||||
_m_client_msg(spec_.atombase().xdnd_leave, 0, 0, 0);
|
_m_client_msg(spec_.atombase().xdnd_leave, 0, 0, 0);
|
||||||
target_ = 0;
|
target_ = 0;
|
||||||
|
executed_action_ = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _m_xdnd_drop()
|
bool _m_xdnd_drop()
|
||||||
{
|
{
|
||||||
::XUndefineCursor(spec_.open_display(), source_);
|
::XUndefineCursor(spec_.open_display(), source_);
|
||||||
xstate_ = xdnd_status_state::drop;
|
xstate_ = xdnd_status_state::drop;
|
||||||
std::cout<<"Send: XdndDrop"<<std::endl;
|
|
||||||
_m_client_msg(spec_.atombase().xdnd_drop, 0, CurrentTime, 0);
|
if(executed_action_)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
|
std::cout<<"Send: XdndDrop"<<std::endl;
|
||||||
|
#endif
|
||||||
|
_m_client_msg(spec_.atombase().xdnd_drop, 0, CurrentTime, 0);
|
||||||
|
}
|
||||||
target_ = 0;
|
target_ = 0;
|
||||||
|
|
||||||
|
return (executed_action_ != 0);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
//dndversion<<24, fl_XdndURIList, XA_STRING, 0
|
//dndversion<<24, fl_XdndURIList, XA_STRING, 0
|
||||||
@ -343,7 +341,9 @@ namespace nana{
|
|||||||
if ((actual == XA_ATOM) && (format==32) && count && data)
|
if ((actual == XA_ATOM) && (format==32) && count && data)
|
||||||
{
|
{
|
||||||
version = int(*(Atom*)data);
|
version = int(*(Atom*)data);
|
||||||
|
#ifdef DEBUG_XDND_PROTOCOL
|
||||||
std::cout<<"Get:XdndAware version:"<<version<<std::endl;
|
std::cout<<"Get:XdndAware version:"<<version<<std::endl;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data)
|
if (data)
|
||||||
@ -355,39 +355,17 @@ namespace nana{
|
|||||||
{
|
{
|
||||||
::XDefineCursor(spec_.open_display(), source_, (accepted ? cursor_.dnd_move : cursor_.dnd_none));
|
::XDefineCursor(spec_.open_display(), source_, (accepted ? cursor_.dnd_move : cursor_.dnd_none));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 //deprecated
|
|
||||||
//Check if window has a property
|
|
||||||
static bool _m_has_property(Window wd, Atom atom, unsigned char** data)
|
|
||||||
{
|
|
||||||
Atom type = 0;
|
|
||||||
int f;
|
|
||||||
unsigned long n, a;
|
|
||||||
|
|
||||||
unsigned char * data_back = nullptr;
|
|
||||||
if(nullptr == data)
|
|
||||||
data = &data_back;
|
|
||||||
|
|
||||||
if (::XGetWindowProperty(spec_.open_display(), wd, atom, 0, 0, False, AnyPropertyType, &type, &f,&n,&a,data) == Success)
|
|
||||||
{
|
|
||||||
//release the *data if failed to get the property or unspecified output buffer
|
|
||||||
if((0 == type) || data_back)
|
|
||||||
::XFree(*data);
|
|
||||||
|
|
||||||
return (0 != type);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
private:
|
private:
|
||||||
nana::detail::platform_spec& spec_;
|
nana::detail::platform_spec& spec_;
|
||||||
Window const source_;
|
Window const source_;
|
||||||
Window target_{ 0 };
|
Window target_{ 0 };
|
||||||
|
Atom executed_action_{ 0 };
|
||||||
xdnd_status_state xstate_{xdnd_status_state::normal};
|
xdnd_status_state xstate_{xdnd_status_state::normal};
|
||||||
std::map<Window, nana::rectangle> mvout_table_;
|
std::map<Window, nana::rectangle> mvout_table_;
|
||||||
|
|
||||||
struct cursor_rep
|
struct cursor_rep
|
||||||
{
|
{
|
||||||
|
Cursor dnd_copy{ 0 };
|
||||||
Cursor dnd_move{ 0 };
|
Cursor dnd_move{ 0 };
|
||||||
Cursor dnd_none{ 0 };
|
Cursor dnd_none{ 0 };
|
||||||
}cursor_;
|
}cursor_;
|
||||||
|
@ -39,17 +39,31 @@ namespace nana
|
|||||||
{
|
{
|
||||||
struct dragdrop_data
|
struct dragdrop_data
|
||||||
{
|
{
|
||||||
|
dnd_action requested_action;
|
||||||
std::vector<std::filesystem::path> files;
|
std::vector<std::filesystem::path> files;
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef NANA_X11
|
#ifdef NANA_X11
|
||||||
xdnd_data to_xdnd_data(const dragdrop_data& data)
|
xdnd_data to_xdnd_data() const noexcept
|
||||||
{
|
{
|
||||||
xdnd_data xdata;
|
auto & atombase = nana::detail::platform_spec::instance().atombase();
|
||||||
xdata.files = data.files;
|
xdnd_data xdata;
|
||||||
return xdata;
|
xdata.requested_action = atombase.xdnd_action_copy;
|
||||||
}
|
|
||||||
|
switch(requested_action)
|
||||||
|
{
|
||||||
|
case dnd_action::copy:
|
||||||
|
xdata.requested_action = atombase.xdnd_action_copy; break;
|
||||||
|
case dnd_action::move:
|
||||||
|
xdata.requested_action = atombase.xdnd_action_move; break;
|
||||||
|
case dnd_action::link:
|
||||||
|
xdata.requested_action = atombase.xdnd_action_link; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
xdata.files = files;
|
||||||
|
return xdata;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -744,7 +758,7 @@ namespace nana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dragdrop(window drag_wd, dropdata_type* dropdata)
|
bool dragdrop(window drag_wd, dropdata_type* dropdata, dnd_action* executed_action)
|
||||||
{
|
{
|
||||||
auto i = table_.find(API::root(drag_wd));
|
auto i = table_.find(API::root(drag_wd));
|
||||||
if ((!dropdata) && table_.end() == i)
|
if ((!dropdata) && table_.end() == i)
|
||||||
@ -764,6 +778,19 @@ namespace nana
|
|||||||
|
|
||||||
delete drop_src;
|
delete drop_src;
|
||||||
|
|
||||||
|
if (executed_action)
|
||||||
|
{
|
||||||
|
switch (result_effect)
|
||||||
|
{
|
||||||
|
case DROPEFFECT_COPY:
|
||||||
|
*executed_action = dnd_action::copy; break;
|
||||||
|
case DROPEFFECT_MOVE:
|
||||||
|
*executed_action = dnd_action::move; break;
|
||||||
|
case DROPEFFECT_LINK:
|
||||||
|
*executed_action = dnd_action::link; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (DROPEFFECT_NONE != result_effect);
|
return (DROPEFFECT_NONE != result_effect);
|
||||||
#elif defined(NANA_X11)
|
#elif defined(NANA_X11)
|
||||||
auto& atombase = _m_spec().atombase();
|
auto& atombase = _m_spec().atombase();
|
||||||
@ -780,12 +807,14 @@ namespace nana
|
|||||||
|
|
||||||
hovered_.window_handle = nullptr;
|
hovered_.window_handle = nullptr;
|
||||||
hovered_.native_wd = 0;
|
hovered_.native_wd = 0;
|
||||||
window target_wd = 0;
|
|
||||||
|
if(executed_action)
|
||||||
|
*executed_action = dropdata->data()->requested_action;
|
||||||
|
|
||||||
if(ddrop->simple_mode())
|
if(ddrop->simple_mode())
|
||||||
{
|
{
|
||||||
|
|
||||||
_m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{
|
_m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{
|
||||||
if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind)
|
if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind)
|
||||||
{
|
{
|
||||||
auto const disp = _m_spec().open_display();
|
auto const disp = _m_spec().open_display();
|
||||||
@ -793,35 +822,7 @@ namespace nana
|
|||||||
{
|
{
|
||||||
auto pos = API::cursor_position();
|
auto pos = API::cursor_position();
|
||||||
auto native_cur_wd = reinterpret_cast<Window>(detail::native_interface::find_window(pos.x, pos.y));
|
auto native_cur_wd = reinterpret_cast<Window>(detail::native_interface::find_window(pos.x, pos.y));
|
||||||
#if 0
|
|
||||||
const char* icon = nullptr;
|
|
||||||
if(hovered_.native_wd != native_cur_wd)
|
|
||||||
{
|
|
||||||
if(hovered_.native_wd)
|
|
||||||
{
|
|
||||||
_m_free_cursor();
|
|
||||||
::XUndefineCursor(disp, hovered_.native_wd);
|
|
||||||
}
|
|
||||||
|
|
||||||
_m_client_msg(native_cur_wd, native_source, 1, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING);
|
|
||||||
hovered_.native_wd = native_cur_wd;
|
|
||||||
|
|
||||||
if(!ddrop->simple_mode())
|
|
||||||
icon = "dnd-move";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ddrop->simple_mode())
|
|
||||||
{
|
|
||||||
auto cur_wd = API::find_window(API::cursor_position());
|
|
||||||
if(hovered_.window_handle != cur_wd)
|
|
||||||
{
|
|
||||||
hovered_.window_handle = cur_wd;
|
|
||||||
|
|
||||||
icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
const char* icon = nullptr;
|
const char* icon = nullptr;
|
||||||
if(hovered_.native_wd != native_cur_wd)
|
if(hovered_.native_wd != native_cur_wd)
|
||||||
{
|
{
|
||||||
@ -835,23 +836,19 @@ namespace nana
|
|||||||
hovered_.native_wd = native_cur_wd;
|
hovered_.native_wd = native_cur_wd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ddrop->simple_mode())
|
|
||||||
|
auto cur_wd = API::find_window(API::cursor_position());
|
||||||
|
|
||||||
|
std::cout<<" Hovered="<<cur_wd;
|
||||||
|
if(hovered_.window_handle != cur_wd)
|
||||||
{
|
{
|
||||||
auto cur_wd = API::find_window(API::cursor_position());
|
hovered_.window_handle = cur_wd;
|
||||||
|
|
||||||
std::cout<<" Hovered="<<cur_wd;
|
icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none");
|
||||||
if(hovered_.window_handle != cur_wd)
|
std::cout<<" ICON="<<icon;
|
||||||
{
|
|
||||||
hovered_.window_handle = cur_wd;
|
|
||||||
|
|
||||||
icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none");
|
|
||||||
std::cout<<" ICON="<<icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout<<std::endl;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
icon = "dnd-move";
|
std::cout<<std::endl;
|
||||||
|
|
||||||
if(icon)
|
if(icon)
|
||||||
{
|
{
|
||||||
@ -859,11 +856,9 @@ namespace nana
|
|||||||
hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str());
|
hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str());
|
||||||
::XDefineCursor(disp, native_cur_wd, hovered_.cursor);
|
::XDefineCursor(disp, native_cur_wd, hovered_.cursor);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else if(msg_pkt.u.xevent.type == ButtonRelease)
|
else if(msg_pkt.u.xevent.type == ButtonRelease)
|
||||||
{
|
{
|
||||||
target_wd = API::find_window(API::cursor_position());
|
|
||||||
::XUndefineCursor(disp, hovered_.native_wd);
|
::XUndefineCursor(disp, hovered_.native_wd);
|
||||||
_m_free_cursor();
|
_m_free_cursor();
|
||||||
API::release_capture(drag_wd);
|
API::release_capture(drag_wd);
|
||||||
@ -873,16 +868,19 @@ namespace nana
|
|||||||
}
|
}
|
||||||
return detail::propagation_chain::stop;
|
return detail::propagation_chain::stop;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//In simple mode, it always returns true. The drag and drop is determined by class simple_dragdrop
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto data = detail::to_xdnd_data(*dropdata->data());
|
auto data = dropdata->data()->to_xdnd_data();
|
||||||
|
|
||||||
API::set_capture(drag_wd, true);
|
API::set_capture(drag_wd, true);
|
||||||
nana::detail::xdnd_protocol xdnd_proto{native_source};
|
nana::detail::xdnd_protocol xdnd_proto{native_source};
|
||||||
|
|
||||||
//Not simple mode
|
//Not simple mode
|
||||||
_m_spec().msg_dispatch([this, ddrop, &data, drag_wd, xdnd_proto, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{
|
_m_spec().msg_dispatch([this, ddrop, &data, drag_wd, &xdnd_proto, native_source, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{
|
||||||
if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind)
|
if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind)
|
||||||
{
|
{
|
||||||
auto const disp = _m_spec().open_display();
|
auto const disp = _m_spec().open_display();
|
||||||
@ -891,7 +889,7 @@ namespace nana
|
|||||||
auto pos = API::cursor_position();
|
auto pos = API::cursor_position();
|
||||||
auto native_cur_wd = reinterpret_cast<Window>(detail::native_interface::find_window(pos.x, pos.y));
|
auto native_cur_wd = reinterpret_cast<Window>(detail::native_interface::find_window(pos.x, pos.y));
|
||||||
|
|
||||||
xdnd_proto.mouse_move(native_cur_wd, pos);
|
xdnd_proto.mouse_move(native_cur_wd, pos, data.requested_action);
|
||||||
}
|
}
|
||||||
else if(ClientMessage == msg_pkt.u.xevent.type)
|
else if(ClientMessage == msg_pkt.u.xevent.type)
|
||||||
{
|
{
|
||||||
@ -911,10 +909,12 @@ namespace nana
|
|||||||
std::cout<<"ButtonRelease"<<std::endl;
|
std::cout<<"ButtonRelease"<<std::endl;
|
||||||
API::release_capture(drag_wd);
|
API::release_capture(drag_wd);
|
||||||
|
|
||||||
xdnd_proto.mouse_release();
|
|
||||||
std::cout<<"mouse_release"<<std::endl;
|
std::cout<<"mouse_release"<<std::endl;
|
||||||
target_wd = API::find_window(API::cursor_position());
|
|
||||||
_m_free_cursor();
|
_m_free_cursor();
|
||||||
|
|
||||||
|
//Exits the msg loop if xdnd_proto doesn't send the XdndDrop because of refusal of the DND
|
||||||
|
if(!xdnd_proto.mouse_release())
|
||||||
|
return detail::propagation_chain::exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
return detail::propagation_chain::stop;
|
return detail::propagation_chain::stop;
|
||||||
@ -922,9 +922,14 @@ namespace nana
|
|||||||
return detail::propagation_chain::pass;
|
return detail::propagation_chain::pass;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
if(xdnd_proto.executed_action() != 0)
|
||||||
|
{
|
||||||
|
if(executed_action)
|
||||||
|
*executed_action = _m_from_xdnd_action(xdnd_proto.executed_action());
|
||||||
|
|
||||||
return (nullptr != target_wd);
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -936,6 +941,19 @@ namespace nana
|
|||||||
return nana::detail::platform_spec::instance();
|
return nana::detail::platform_spec::instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dnd_action _m_from_xdnd_action(Atom action) noexcept
|
||||||
|
{
|
||||||
|
auto & atombase = _m_spec().atombase();
|
||||||
|
if(action == atombase.xdnd_action_copy)
|
||||||
|
return dnd_action::copy;
|
||||||
|
else if(action == atombase.xdnd_action_move)
|
||||||
|
return dnd_action::move;
|
||||||
|
else if(action == atombase.xdnd_action_link)
|
||||||
|
return dnd_action::link;
|
||||||
|
|
||||||
|
return dnd_action::copy;
|
||||||
|
}
|
||||||
|
|
||||||
//dndversion<<24, fl_XdndURIList, XA_STRING, 0
|
//dndversion<<24, fl_XdndURIList, XA_STRING, 0
|
||||||
static void _m_client_msg(Window wd_target, Window wd_src, int flag, Atom xdnd_atom, Atom data, Atom data_type)
|
static void _m_client_msg(Window wd_target, Window wd_src, int flag, Atom xdnd_atom, Atom data, Atom data_type)
|
||||||
{
|
{
|
||||||
@ -974,7 +992,7 @@ namespace nana
|
|||||||
|
|
||||||
#ifdef NANA_WINDOWS
|
#ifdef NANA_WINDOWS
|
||||||
#elif defined (NANA_X11)
|
#elif defined (NANA_X11)
|
||||||
nana::detail::shared_icons icons_;
|
nana::detail::theme icons_;
|
||||||
struct hovered_status
|
struct hovered_status
|
||||||
{
|
{
|
||||||
Window native_wd{0};
|
Window native_wd{0};
|
||||||
@ -1070,7 +1088,7 @@ namespace nana
|
|||||||
|
|
||||||
std::unique_ptr<dragdrop_service::dropdata_type> dropdata{new dragdrop_service::dropdata_type};
|
std::unique_ptr<dragdrop_service::dropdata_type> dropdata{new dragdrop_service::dropdata_type};
|
||||||
|
|
||||||
auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle, dropdata.get());
|
auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle, dropdata.get(), nullptr);
|
||||||
|
|
||||||
real_wd->other.dnd_state = dragdrop_status::not_ready;
|
real_wd->other.dnd_state = dragdrop_status::not_ready;
|
||||||
impl_->dragging = false;
|
impl_->dragging = false;
|
||||||
@ -1125,7 +1143,7 @@ namespace nana
|
|||||||
dragdrop_session * ddrop{nullptr};
|
dragdrop_session * ddrop{nullptr};
|
||||||
std::function<bool()> predicate;
|
std::function<bool()> predicate;
|
||||||
std::function<data()> generator;
|
std::function<data()> generator;
|
||||||
std::function<void(bool)> drop_finished;
|
std::function<void(bool, dnd_action, data&)> drop_finished;
|
||||||
|
|
||||||
struct event_handlers
|
struct event_handlers
|
||||||
{
|
{
|
||||||
@ -1143,28 +1161,18 @@ namespace nana
|
|||||||
|
|
||||||
void make_drop()
|
void make_drop()
|
||||||
{
|
{
|
||||||
|
if (!generator)
|
||||||
|
return;
|
||||||
|
|
||||||
auto transf_data = generator();
|
auto transf_data = generator();
|
||||||
dragdrop_service::dropdata_type dropdata;
|
dragdrop_service::dropdata_type dropdata;
|
||||||
dropdata.assign(*transf_data.real_data_);
|
dropdata.assign(*transf_data.real_data_);
|
||||||
/* //deprecated
|
|
||||||
#ifdef NANA_WINDOWS
|
|
||||||
drop_source drop_src{ source_handle };
|
|
||||||
DWORD result_effect = DROPEFFECT_NONE;
|
|
||||||
auto status = ::DoDragDrop(&dropdata, &drop_src, DROPEFFECT_COPY | DROPEFFECT_MOVE, &result_effect);
|
|
||||||
|
|
||||||
if (DROPEFFECT_NONE == result_effect)
|
dnd_action executed_action;
|
||||||
{
|
auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata, &executed_action);
|
||||||
}
|
|
||||||
|
|
||||||
if (drop_finished)
|
|
||||||
drop_finished(DROPEFFECT_NONE != result_effect);
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata);
|
|
||||||
|
|
||||||
if(drop_finished)
|
if(drop_finished)
|
||||||
drop_finished(has_dropped);
|
drop_finished(has_dropped, executed_action, transf_data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1240,15 +1248,16 @@ namespace nana
|
|||||||
impl_->generator = generator;
|
impl_->generator = generator;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dragdrop::drop_finished(std::function<void(bool)> finish_fn)
|
void dragdrop::drop_finished(std::function<void(bool, dnd_action, data&)> finish_fn)
|
||||||
{
|
{
|
||||||
impl_->drop_finished = finish_fn;
|
impl_->drop_finished = finish_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dragdrop::data::data():
|
dragdrop::data::data(dnd_action requested_action):
|
||||||
real_data_(new detail::dragdrop_data)
|
real_data_(new detail::dragdrop_data)
|
||||||
{
|
{
|
||||||
|
real_data_->requested_action = requested_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
dragdrop::data::~data()
|
dragdrop::data::~data()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user