diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp index d426d34c..a20c7bb8 100644 --- a/include/nana/gui/dragdrop.hpp +++ b/include/nana/gui/dragdrop.hpp @@ -22,6 +22,14 @@ 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 { struct implementation; @@ -67,7 +75,12 @@ namespace nana data(const data&) = delete; data& operator=(const data&) = delete; 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(); @@ -81,9 +94,29 @@ namespace nana dragdrop(window source); ~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 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 generator); - void drop_finished(std::function 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 finish_fn); private: implementation* const impl_; }; diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index f03f0b09..f607e9a3 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -513,6 +513,8 @@ namespace detail atombase_.xdnd_position = ::XInternAtom(display_, "XdndPosition", False); atombase_.xdnd_status = ::XInternAtom(display_, "XdndStatus", 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_selection = ::XInternAtom(display_, "XdndSelection", False); atombase_.xdnd_typelist = ::XInternAtom(display_, "XdndTypeList", False); diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index 0cf4b086..84c1c24e 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -159,6 +159,8 @@ namespace detail Atom xdnd_position; Atom xdnd_status; Atom xdnd_action_copy; + Atom xdnd_action_move; + Atom xdnd_action_link; Atom xdnd_drop; Atom xdnd_selection; Atom xdnd_typelist; diff --git a/source/detail/posix/xdnd_protocol.hpp b/source/detail/posix/xdnd_protocol.hpp index ad101c56..1883633e 100644 --- a/source/detail/posix/xdnd_protocol.hpp +++ b/source/detail/posix/xdnd_protocol.hpp @@ -16,69 +16,26 @@ #include "platform_spec.hpp" #include -#include +#include "theme.hpp" #include +#include + +#define DEBUG_XDND_PROTOCOL + +#ifdef DEBUG_XDND_PROTOCOL #include //debug +#endif namespace nana{ 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 { + Atom requested_action; std::vector files; }; @@ -100,31 +57,40 @@ namespace nana{ auto disp = spec_.open_display(); detail::platform_scope_guard lock; ::XSetSelectionOwner(disp, spec_.atombase().xdnd_selection, source, CurrentTime); - std::cout<<"XSetSelectionOwner "<(xclient.data.l[0]); bool is_accepted_by_target = (xclient.data.l[1] & 1); - std::cout<<"XdndStatus: Accepted="< mvout_table_; struct cursor_rep { + Cursor dnd_copy{ 0 }; Cursor dnd_move{ 0 }; Cursor dnd_none{ 0 }; }cursor_; diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index d6266e69..2c2d394a 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -39,17 +39,31 @@ namespace nana { struct dragdrop_data { + dnd_action requested_action; std::vector files; - }; #ifdef NANA_X11 - xdnd_data to_xdnd_data(const dragdrop_data& data) - { - xdnd_data xdata; - xdata.files = data.files; - return xdata; - } + xdnd_data to_xdnd_data() const noexcept + { + auto & atombase = nana::detail::platform_spec::instance().atombase(); + xdnd_data 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 + }; } @@ -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)); if ((!dropdata) && table_.end() == i) @@ -764,6 +778,19 @@ namespace nana 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); #elif defined(NANA_X11) auto& atombase = _m_spec().atombase(); @@ -780,12 +807,14 @@ namespace nana hovered_.window_handle = nullptr; hovered_.native_wd = 0; - window target_wd = 0; + + if(executed_action) + *executed_action = dropdata->data()->requested_action; 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) { auto const disp = _m_spec().open_display(); @@ -793,35 +822,7 @@ namespace nana { auto pos = API::cursor_position(); auto native_cur_wd = reinterpret_cast(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; if(hovered_.native_wd != native_cur_wd) { @@ -835,23 +836,19 @@ namespace nana hovered_.native_wd = native_cur_wd; } - if(ddrop->simple_mode()) + + auto cur_wd = API::find_window(API::cursor_position()); + + std::cout<<" Hovered="<data()); + auto data = dropdata->data()->to_xdnd_data(); API::set_capture(drag_wd, true); nana::detail::xdnd_protocol xdnd_proto{native_source}; //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) { auto const disp = _m_spec().open_display(); @@ -891,7 +889,7 @@ namespace nana auto pos = API::cursor_position(); auto native_cur_wd = reinterpret_cast(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) { @@ -911,10 +909,12 @@ namespace nana std::cout<<"ButtonRelease"< 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; impl_->dragging = false; @@ -1125,7 +1143,7 @@ namespace nana dragdrop_session * ddrop{nullptr}; std::function predicate; std::function generator; - std::function drop_finished; + std::function drop_finished; struct event_handlers { @@ -1143,28 +1161,18 @@ namespace nana void make_drop() { + if (!generator) + return; + auto transf_data = generator(); dragdrop_service::dropdata_type dropdata; 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) - { - } - - if (drop_finished) - drop_finished(DROPEFFECT_NONE != result_effect); -#else -#endif -*/ - auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata); + dnd_action executed_action; + auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata, &executed_action); if(drop_finished) - drop_finished(has_dropped); + drop_finished(has_dropped, executed_action, transf_data); } }; @@ -1240,15 +1248,16 @@ namespace nana impl_->generator = generator; } - void dragdrop::drop_finished(std::function finish_fn) + void dragdrop::drop_finished(std::function 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_->requested_action = requested_action; } dragdrop::data::~data()