Merge remote-tracking branch 'cnjinhao/develop-1.7' into cmake-dev
This commit is contained in:
		
						commit
						ed183c2bff
					
				| @ -20,6 +20,7 @@ matrix: | |||||||
|             - alsa-oss |             - alsa-oss | ||||||
|             - libx11-dev |             - libx11-dev | ||||||
|             - libxft-dev |             - libxft-dev | ||||||
|  |             - libxcursor-dev | ||||||
|           sources:  |           sources:  | ||||||
|             - ubuntu-toolchain-r-test |             - ubuntu-toolchain-r-test | ||||||
|              |              | ||||||
| @ -36,6 +37,7 @@ matrix: | |||||||
|             - alsa-oss |             - alsa-oss | ||||||
|             - libx11-dev |             - libx11-dev | ||||||
|             - libxft-dev |             - libxft-dev | ||||||
|  |             - libxcursor-dev | ||||||
|           sources:  |           sources:  | ||||||
|             - ubuntu-toolchain-r-test |             - ubuntu-toolchain-r-test | ||||||
|             - llvm-toolchain-precise  |             - llvm-toolchain-precise  | ||||||
|  | |||||||
| @ -66,6 +66,7 @@ | |||||||
| 		<Unit filename="../../source/gui/detail/native_window_interface.cpp" /> | 		<Unit filename="../../source/gui/detail/native_window_interface.cpp" /> | ||||||
| 		<Unit filename="../../source/gui/detail/window_layout.cpp" /> | 		<Unit filename="../../source/gui/detail/window_layout.cpp" /> | ||||||
| 		<Unit filename="../../source/gui/detail/window_manager.cpp" /> | 		<Unit filename="../../source/gui/detail/window_manager.cpp" /> | ||||||
|  | 		<Unit filename="../../source/gui/dragdrop.cpp" /> | ||||||
| 		<Unit filename="../../source/gui/dragger.cpp" /> | 		<Unit filename="../../source/gui/dragger.cpp" /> | ||||||
| 		<Unit filename="../../source/gui/drawing.cpp" /> | 		<Unit filename="../../source/gui/drawing.cpp" /> | ||||||
| 		<Unit filename="../../source/gui/effects.cpp" /> | 		<Unit filename="../../source/gui/effects.cpp" /> | ||||||
|  | |||||||
| @ -202,6 +202,7 @@ | |||||||
|     <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> | ||||||
|  |     <ClCompile Include="..\..\source\gui\dragdrop.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\dragger.cpp" /> |     <ClCompile Include="..\..\source\gui\dragger.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\drawing.cpp" /> |     <ClCompile Include="..\..\source\gui\drawing.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\effects.cpp" /> |     <ClCompile Include="..\..\source\gui\effects.cpp" /> | ||||||
|  | |||||||
| @ -333,6 +333,9 @@ | |||||||
|     <ClCompile Include="..\..\source\detail\platform_abstraction.cpp"> |     <ClCompile Include="..\..\source\detail\platform_abstraction.cpp"> | ||||||
|       <Filter>Source Files\nana\detail</Filter> |       <Filter>Source Files\nana\detail</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="..\..\source\gui\dragdrop.cpp"> | ||||||
|  |       <Filter>Source Files\nana\gui</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="..\..\source\gui\widgets\group.cpp"> |     <ClCompile Include="..\..\source\gui\widgets\group.cpp"> | ||||||
|  | |||||||
| @ -196,6 +196,7 @@ | |||||||
|     <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> | ||||||
|  |     <ClCompile Include="..\..\source\gui\dragdrop.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\dragger.cpp" /> |     <ClCompile Include="..\..\source\gui\dragger.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\drawing.cpp" /> |     <ClCompile Include="..\..\source\gui\drawing.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\effects.cpp" /> |     <ClCompile Include="..\..\source\gui\effects.cpp" /> | ||||||
|  | |||||||
| @ -291,6 +291,9 @@ | |||||||
|     <ClCompile Include="..\..\source\detail\platform_abstraction.cpp"> |     <ClCompile Include="..\..\source\detail\platform_abstraction.cpp"> | ||||||
|       <Filter>Source Files\detail</Filter> |       <Filter>Source Files\detail</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="..\..\source\gui\dragdrop.cpp"> | ||||||
|  |       <Filter>Source Files\gui</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="..\..\include\nana\any.hpp"> |     <ClInclude Include="..\..\include\nana\any.hpp"> | ||||||
|  | |||||||
| @ -179,6 +179,7 @@ | |||||||
|     <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> |     <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> | ||||||
|  |     <ClCompile Include="..\..\source\gui\dragdrop.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\dragger.cpp" /> |     <ClCompile Include="..\..\source\gui\dragger.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\drawing.cpp" /> |     <ClCompile Include="..\..\source\gui\drawing.cpp" /> | ||||||
|     <ClCompile Include="..\..\source\gui\effects.cpp" /> |     <ClCompile Include="..\..\source\gui\effects.cpp" /> | ||||||
|  | |||||||
| @ -292,6 +292,9 @@ | |||||||
|     <ClCompile Include="..\..\source\gui\wvl.cpp"> |     <ClCompile Include="..\..\source\gui\wvl.cpp"> | ||||||
|       <Filter>Sources\gui</Filter> |       <Filter>Sources\gui</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="..\..\source\gui\dragdrop.cpp"> | ||||||
|  |       <Filter>Sources\gui</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="..\..\include\nana\gui\widgets\spinbox.hpp"> |     <ClInclude Include="..\..\include\nana\gui\widgets\spinbox.hpp"> | ||||||
|  | |||||||
| @ -65,6 +65,13 @@ namespace nana | |||||||
| 		blend | 		blend | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	enum class dragdrop_status | ||||||
|  | 	{ | ||||||
|  | 		not_ready, | ||||||
|  | 		ready, | ||||||
|  | 		in_progress | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	namespace category | 	namespace category | ||||||
| 	{ | 	{ | ||||||
| 		enum class flags | 		enum class flags | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| /**
 | /**
 | ||||||
|  *	A Basic Window Widget Definition |  *	A Basic Window Widget Definition | ||||||
|  *	Nana C++ Library(http://www.nanapro.org)
 |  *	Nana C++ Library(http://www.nanapro.org)
 | ||||||
|  *	Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) |  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||||
|  * |  * | ||||||
|  *	Distributed under the Boost Software License, Version 1.0. |  *	Distributed under the Boost Software License, Version 1.0. | ||||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at |  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||||
| @ -179,7 +179,8 @@ namespace detail | |||||||
| 			bool ignore_menubar_focus	: 1;	///< A flag indicates whether the menubar sets the focus.
 | 			bool ignore_menubar_focus	: 1;	///< A flag indicates whether the menubar sets the focus.
 | ||||||
| 			bool ignore_mouse_focus		: 1;	///< A flag indicates whether the widget accepts focus when clicking on it
 | 			bool ignore_mouse_focus		: 1;	///< A flag indicates whether the widget accepts focus when clicking on it
 | ||||||
| 			bool space_click_enabled : 1;		///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key.
 | 			bool space_click_enabled : 1;		///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key.
 | ||||||
| 			unsigned Reserved	:18; | 			bool draggable : 1; | ||||||
|  | 			unsigned Reserved	:17; | ||||||
| 			unsigned char tab;		///< indicate a window that can receive the keyboard TAB
 | 			unsigned char tab;		///< indicate a window that can receive the keyboard TAB
 | ||||||
| 			mouse_action	action; | 			mouse_action	action; | ||||||
| 			mouse_action	action_before; | 			mouse_action	action_before; | ||||||
| @ -234,6 +235,7 @@ namespace detail | |||||||
| 											///< if the active_window is null, the parent of this window keeps focus.
 | 											///< if the active_window is null, the parent of this window keeps focus.
 | ||||||
| 			paint::graphics glass_buffer;	///< if effect.bground is avaiable. Refer to window_layout::make_bground.
 | 			paint::graphics glass_buffer;	///< if effect.bground is avaiable. Refer to window_layout::make_bground.
 | ||||||
| 			update_state	upd_state; | 			update_state	upd_state; | ||||||
|  | 			dragdrop_status	dnd_state{ dragdrop_status::not_ready }; | ||||||
| 
 | 
 | ||||||
| 			union | 			union | ||||||
| 			{ | 			{ | ||||||
|  | |||||||
							
								
								
									
										45
									
								
								include/nana/gui/dragdrop.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								include/nana/gui/dragdrop.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | /**
 | ||||||
|  | *	Drag and Drop Implementation | ||||||
|  | *	Nana C++ Library(http://www.nanapro.org)
 | ||||||
|  | *	Copyright(C) 2018 Jinhao(cnjinhao@hotmail.com) | ||||||
|  | * | ||||||
|  | *	Distributed under the Boost Software License, Version 1.0. | ||||||
|  | *	(See accompanying file LICENSE_1_0.txt or copy at | ||||||
|  | *	http://www.boost.org/LICENSE_1_0.txt)
 | ||||||
|  | * | ||||||
|  | *	@file: nana/gui/dragdrop.hpp | ||||||
|  | *	@author: Jinhao(cnjinhao@hotmail.com) | ||||||
|  | */ | ||||||
|  | #ifndef NANA_GUI_DRAGDROP_INCLUDED | ||||||
|  | #define NANA_GUI_DRAGDROP_INCLUDED | ||||||
|  | 
 | ||||||
|  | #include <nana/push_ignore_diagnostic> | ||||||
|  | #include <functional> | ||||||
|  | #include "basis.hpp" | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
|  | namespace nana | ||||||
|  | { | ||||||
|  | 	class simple_dragdrop | ||||||
|  | 	{ | ||||||
|  | 		struct implementation; | ||||||
|  | 
 | ||||||
|  | 		simple_dragdrop(const simple_dragdrop&) = delete; | ||||||
|  | 		simple_dragdrop& operator=(const simple_dragdrop&) = delete; | ||||||
|  | 
 | ||||||
|  | 		simple_dragdrop(simple_dragdrop&&) = delete; | ||||||
|  | 		simple_dragdrop& operator=(simple_dragdrop&&) = delete; | ||||||
|  | 	public: | ||||||
|  | 		simple_dragdrop(window drag_wd); | ||||||
|  | 		~simple_dragdrop(); | ||||||
|  | 
 | ||||||
|  | 		/// Sets a condition that determines whether the drag&drop can start
 | ||||||
|  | 		void condition(std::function<bool()> predicate_fn); | ||||||
|  | 		void make_drop(window target, std::function<void()> drop_fn); | ||||||
|  | 	private: | ||||||
|  | 		implementation* const impl_; | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -1,7 +1,7 @@ | |||||||
| /*
 | /*
 | ||||||
|  *	Nana GUI Programming Interface Implementation |  *	Nana GUI Programming Interface Implementation | ||||||
|  *	Nana C++ Library(http://www.nanapro.org)
 |  *	Nana C++ Library(http://www.nanapro.org)
 | ||||||
|  *	Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) |  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||||
|  * |  * | ||||||
|  *	Distributed under the Boost Software License, Version 1.0. |  *	Distributed under the Boost Software License, Version 1.0. | ||||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at |  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||||
| @ -117,6 +117,9 @@ namespace API | |||||||
| 		void lazy_refresh(); | 		void lazy_refresh(); | ||||||
| 
 | 
 | ||||||
| 		void draw_shortkey_underline(paint::graphics&, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color&); | 		void draw_shortkey_underline(paint::graphics&, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color&); | ||||||
|  | 
 | ||||||
|  | 		void window_draggable(window, bool enabled); | ||||||
|  | 		bool window_draggable(window); | ||||||
| 	}//end namespace dev
 | 	}//end namespace dev
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -476,6 +479,8 @@ namespace API | |||||||
| 	::std::optional<std::pair<::nana::size, ::nana::size>> content_extent(window wd, unsigned limited_px, bool limit_width); | 	::std::optional<std::pair<::nana::size, ::nana::size>> content_extent(window wd, unsigned limited_px, bool limit_width); | ||||||
| 
 | 
 | ||||||
| 	unsigned screen_dpi(bool x_requested); | 	unsigned screen_dpi(bool x_requested); | ||||||
|  | 
 | ||||||
|  | 	dragdrop_status window_dragdrop_status(::nana::window); | ||||||
| }//end namespace API
 | }//end namespace API
 | ||||||
| 
 | 
 | ||||||
| }//end namespace nana
 | }//end namespace nana
 | ||||||
|  | |||||||
| @ -1497,6 +1497,9 @@ the nana::detail::basic_window member pointer scheme | |||||||
| 		void erase(index_pairs indexes);	///<Erases specified items.
 | 		void erase(index_pairs indexes);	///<Erases specified items.
 | ||||||
| 		item_proxy erase(item_proxy); | 		item_proxy erase(item_proxy); | ||||||
| 
 | 
 | ||||||
|  | 		/// Returns the item which is hovered
 | ||||||
|  | 		index_pair hovered() const; | ||||||
|  | 
 | ||||||
| 		bool sortable() const; | 		bool sortable() const; | ||||||
| 		void sortable(bool enable); | 		void sortable(bool enable); | ||||||
| 		 | 		 | ||||||
|  | |||||||
| @ -1041,6 +1041,12 @@ namespace detail | |||||||
| 		msg_dispatcher_->dispatch(reinterpret_cast<Window>(modal)); | 		msg_dispatcher_->dispatch(reinterpret_cast<Window>(modal)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	void platform_spec::msg_dispatch(std::function<bool(const msg_packet_tag&)> msg_filter_fn) | ||||||
|  | 	{ | ||||||
|  | 		msg_dispatcher_->dispatch(msg_filter_fn); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	void* platform_spec::request_selection(native_window_type requestor, Atom type, size_t& size) | 	void* platform_spec::request_selection(native_window_type requestor, Atom type, size_t& size) | ||||||
| 	{ | 	{ | ||||||
| 		if(requestor) | 		if(requestor) | ||||||
| @ -1104,6 +1110,40 @@ namespace detail | |||||||
| 		return graph; | 		return graph; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 	bool platform_spec::register_dragdrop(native_window_type wd, dragdrop_interface* ddrop) | ||||||
|  | 	{ | ||||||
|  | 		platform_scope_guard lock; | ||||||
|  | 		if(0 != xdnd_.dragdrop.count(wd)) | ||||||
|  | 			return false; | ||||||
|  | 
 | ||||||
|  | 		int dndver = 4; | ||||||
|  | 		::XChangeProperty(display_, reinterpret_cast<Window>(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, | ||||||
|  | 			PropModeReplace, reinterpret_cast<unsigned char*>(&dndver), 1); | ||||||
|  | 
 | ||||||
|  | 		auto & ref_drop = xdnd_.dragdrop[wd]; | ||||||
|  | 		ref_drop.dragdrop = ddrop; | ||||||
|  | 		ref_drop.ref_count = 1; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) | ||||||
|  | 	{ | ||||||
|  | 		platform_scope_guard lock; | ||||||
|  | 		auto i = xdnd_.dragdrop.find(wd); | ||||||
|  | 		if(i == xdnd_.dragdrop.end()) | ||||||
|  | 			return nullptr; | ||||||
|  | 
 | ||||||
|  | 		auto ddrop = i->second; | ||||||
|  | 		if(ddrop.ref_count <= 1) | ||||||
|  | 		{ | ||||||
|  | 			xdnd_.dragdrop.erase(i); | ||||||
|  | 			return ddrop.dragdrop; | ||||||
|  | 		} | ||||||
|  | 		--ddrop.ref_count; | ||||||
|  | 		return nullptr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	//_m_msg_filter
 | 	//_m_msg_filter
 | ||||||
| 	//@return:	_m_msg_filter returns three states
 | 	//@return:	_m_msg_filter returns three states
 | ||||||
| 	//		0 = msg_dispatcher dispatches the XEvent
 | 	//		0 = msg_dispatcher dispatches the XEvent
 | ||||||
| @ -1163,7 +1203,7 @@ namespace detail | |||||||
| 				else if(evt.xselection.property == self.atombase_.xdnd_selection) | 				else if(evt.xselection.property == self.atombase_.xdnd_selection) | ||||||
| 				{ | 				{ | ||||||
| 					bool accepted = false; | 					bool accepted = false; | ||||||
| 					msg.kind = msg.kind_mouse_drop; | 					msg.kind = msg_packet_tag::pkt_family::mouse_drop; | ||||||
| 					msg.u.mouse_drop.window = 0; | 					msg.u.mouse_drop.window = 0; | ||||||
| 					if(bytes_left > 0 && type == self.xdnd_.good_type) | 					if(bytes_left > 0 && type == self.xdnd_.good_type) | ||||||
| 					{ | 					{ | ||||||
|  | |||||||
| @ -143,7 +143,7 @@ namespace detail | |||||||
| 				{ | 				{ | ||||||
| 					//Make a cleanup msg packet to infor the dispatcher the window is closed.
 | 					//Make a cleanup msg packet to infor the dispatcher the window is closed.
 | ||||||
| 					msg_packet_tag msg; | 					msg_packet_tag msg; | ||||||
| 					msg.kind = msg.kind_cleanup; | 					msg.kind = msg_packet_tag::pkt_family::cleanup; | ||||||
| 					msg.u.packet_window = wd; | 					msg.u.packet_window = wd; | ||||||
| 					thr->msg_queue.push_back(msg); | 					thr->msg_queue.push_back(msg); | ||||||
| 				} | 				} | ||||||
| @ -171,6 +171,30 @@ namespace detail | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		template<typename MsgFilter> | ||||||
|  | 		void dispatch(MsgFilter msg_filter_fn) | ||||||
|  | 		{ | ||||||
|  | 			auto tid = nana::system::this_thread_id(); | ||||||
|  | 			msg_packet_tag msg; | ||||||
|  | 			int qstate; | ||||||
|  | 
 | ||||||
|  | 			//Test whether the thread is registered for window, and retrieve the queue state for event
 | ||||||
|  | 			while((qstate = _m_read_queue(tid, msg, 0))) | ||||||
|  | 			{ | ||||||
|  | 				//the queue is empty
 | ||||||
|  | 				if(-1 == qstate) | ||||||
|  | 				{ | ||||||
|  | 					if(false == _m_wait_for_queue(tid)) | ||||||
|  | 						proc_.timer_proc(tid); | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					if(msg_filter_fn(msg)) | ||||||
|  | 						return; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	private: | 	private: | ||||||
| 		void _m_msg_driver() | 		void _m_msg_driver() | ||||||
| 		{ | 		{ | ||||||
| @ -220,7 +244,7 @@ namespace detail | |||||||
| 					switch(proc_.filter_proc(event, msg_pack)) | 					switch(proc_.filter_proc(event, msg_pack)) | ||||||
| 					{ | 					{ | ||||||
| 					case 0: | 					case 0: | ||||||
| 						msg_pack.kind = msg_pack.kind_xevent; | 						msg_pack.kind = msg_packet_tag::pkt_family::xevent; | ||||||
| 						msg_pack.u.xevent = event; | 						msg_pack.u.xevent = event; | ||||||
| 						_m_msg_dispatch(msg_pack); | 						_m_msg_dispatch(msg_pack); | ||||||
| 						break; | 						break; | ||||||
| @ -246,9 +270,9 @@ namespace detail | |||||||
| 		{ | 		{ | ||||||
| 			switch(pack.kind) | 			switch(pack.kind) | ||||||
| 			{ | 			{ | ||||||
| 			case msg_packet_tag::kind_xevent: | 			case msg_packet_tag::pkt_family::xevent: | ||||||
| 				return _m_event_window(pack.u.xevent); | 				return _m_event_window(pack.u.xevent); | ||||||
| 			case msg_packet_tag::kind_mouse_drop: | 			case msg_packet_tag::pkt_family::mouse_drop: | ||||||
| 				return pack.u.mouse_drop.window; | 				return pack.u.mouse_drop.window; | ||||||
| 			default: | 			default: | ||||||
| 				break; | 				break; | ||||||
| @ -294,7 +318,7 @@ namespace detail | |||||||
| 							//Check whether the event dispatcher is used for the modal window
 | 							//Check whether the event dispatcher is used for the modal window
 | ||||||
| 							//and when the modal window is closing, the event dispatcher would
 | 							//and when the modal window is closing, the event dispatcher would
 | ||||||
| 							//stop event pumping.
 | 							//stop event pumping.
 | ||||||
| 							if((modal == msg.u.packet_window) && (msg.kind == msg.kind_cleanup)) | 							if((modal == msg.u.packet_window) && (msg.kind == msg_packet_tag::pkt_family::cleanup)) | ||||||
| 								return 0; | 								return 0; | ||||||
| 
 | 
 | ||||||
| 							return 1; | 							return 1; | ||||||
|  | |||||||
| @ -10,8 +10,8 @@ namespace detail | |||||||
| { | { | ||||||
| 	struct msg_packet_tag | 	struct msg_packet_tag | ||||||
| 	{ | 	{ | ||||||
| 		enum kind_t{kind_xevent, kind_mouse_drop, kind_cleanup}; | 		enum class pkt_family{xevent, mouse_drop, cleanup}; | ||||||
| 		kind_t kind; | 		pkt_family kind; | ||||||
| 		union | 		union | ||||||
| 		{ | 		{ | ||||||
| 			XEvent xevent; | 			XEvent xevent; | ||||||
|  | |||||||
| @ -36,6 +36,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <map> | #include <map> | ||||||
|  | #include <functional> | ||||||
| #include "msg_packet.hpp" | #include "msg_packet.hpp" | ||||||
| #include "../platform_abstraction_types.hpp" | #include "../platform_abstraction_types.hpp" | ||||||
| 
 | 
 | ||||||
| @ -176,6 +177,12 @@ namespace detail | |||||||
| 		~platform_scope_guard(); | 		~platform_scope_guard(); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	class dragdrop_interface | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		virtual ~dragdrop_interface() = default; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	class platform_spec | 	class platform_spec | ||||||
| 	{ | 	{ | ||||||
| 		typedef platform_spec self_type; | 		typedef platform_spec self_type; | ||||||
| @ -246,6 +253,7 @@ namespace detail | |||||||
| 		void msg_insert(native_window_type); | 		void msg_insert(native_window_type); | ||||||
| 		void msg_set(timer_proc_type, event_proc_type); | 		void msg_set(timer_proc_type, event_proc_type); | ||||||
| 		void msg_dispatch(native_window_type modal); | 		void msg_dispatch(native_window_type modal); | ||||||
|  | 		void msg_dispatch(std::function<bool(const msg_packet_tag&)>); | ||||||
| 
 | 
 | ||||||
| 		//X Selections
 | 		//X Selections
 | ||||||
| 		void* request_selection(native_window_type requester, Atom type, size_t & bufsize); | 		void* request_selection(native_window_type requester, Atom type, size_t & bufsize); | ||||||
| @ -255,6 +263,9 @@ namespace detail | |||||||
| 		//@biref: The image object should be kept for a long time till the window is closed,
 | 		//@biref: The image object should be kept for a long time till the window is closed,
 | ||||||
| 		//			the image object is release in remove() method.
 | 		//			the image object is release in remove() method.
 | ||||||
| 		const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); | 		const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); | ||||||
|  | 
 | ||||||
|  | 		bool register_dragdrop(native_window_type, dragdrop_interface*); | ||||||
|  | 		dragdrop_interface* remove_dragdrop(native_window_type); | ||||||
| 	private: | 	private: | ||||||
| 		static int _m_msg_filter(XEvent&, msg_packet_tag&); | 		static int _m_msg_filter(XEvent&, msg_packet_tag&); | ||||||
| 		void _m_caret_routine(); | 		void _m_caret_routine(); | ||||||
| @ -311,6 +322,14 @@ namespace detail | |||||||
| 			int timestamp; | 			int timestamp; | ||||||
| 			Window wd_src; | 			Window wd_src; | ||||||
| 			nana::point pos; | 			nana::point pos; | ||||||
|  | 
 | ||||||
|  | 			struct refcount_dragdrop | ||||||
|  | 			{ | ||||||
|  | 				dragdrop_interface* dragdrop{nullptr}; | ||||||
|  | 				std::size_t ref_count{0}; | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			std::map<native_window_type, refcount_dragdrop> dragdrop; | ||||||
| 		}xdnd_; | 		}xdnd_; | ||||||
| 
 | 
 | ||||||
| 		msg_dispatcher * msg_dispatcher_; | 		msg_dispatcher * msg_dispatcher_; | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| /*
 | /*
 | ||||||
| *	A Basic Window Widget Definition | *	A Basic Window Widget Definition | ||||||
| *	Nana C++ Library(http://www.nanapro.org)
 | *	Nana C++ Library(http://www.nanapro.org)
 | ||||||
| *	Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) | *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||||
| * | * | ||||||
| *	Distributed under the Boost Software License, Version 1.0. | *	Distributed under the Boost Software License, Version 1.0. | ||||||
| *	(See accompanying file LICENSE_1_0.txt or copy at | *	(See accompanying file LICENSE_1_0.txt or copy at | ||||||
| @ -410,6 +410,7 @@ namespace nana | |||||||
| 				flags.enabled = true; | 				flags.enabled = true; | ||||||
| 				flags.modal = false; | 				flags.modal = false; | ||||||
| 				flags.take_active = true; | 				flags.take_active = true; | ||||||
|  | 				flags.draggable = false; | ||||||
| 				flags.dropable = false; | 				flags.dropable = false; | ||||||
| 				flags.fullscreen = false; | 				flags.fullscreen = false; | ||||||
| 				flags.tab = nana::detail::tab_type::none; | 				flags.tab = nana::detail::tab_type::none; | ||||||
|  | |||||||
| @ -418,10 +418,10 @@ namespace detail | |||||||
| 	{ | 	{ | ||||||
| 		switch(msg.kind) | 		switch(msg.kind) | ||||||
| 		{ | 		{ | ||||||
| 		case nana::detail::msg_packet_tag::kind_xevent: | 		case nana::detail::msg_packet_tag::pkt_family::xevent: | ||||||
| 			window_proc_for_xevent(display, msg.u.xevent); | 			window_proc_for_xevent(display, msg.u.xevent); | ||||||
| 			break; | 			break; | ||||||
| 		case nana::detail::msg_packet_tag::kind_mouse_drop: | 		case nana::detail::msg_packet_tag::pkt_family::mouse_drop: | ||||||
| 			window_proc_for_packet(display, msg); | 			window_proc_for_packet(display, msg); | ||||||
| 			break; | 			break; | ||||||
| 		default: break; | 		default: break; | ||||||
| @ -441,7 +441,7 @@ namespace detail | |||||||
| 
 | 
 | ||||||
| 			switch(msg.kind) | 			switch(msg.kind) | ||||||
| 			{ | 			{ | ||||||
| 			case nana::detail::msg_packet_tag::kind_mouse_drop: | 			case nana::detail::msg_packet_tag::pkt_family::mouse_drop: | ||||||
| 				msgwd = brock.wd_manager().find_window(native_window, {msg.u.mouse_drop.x, msg.u.mouse_drop.y}); | 				msgwd = brock.wd_manager().find_window(native_window, {msg.u.mouse_drop.x, msg.u.mouse_drop.y}); | ||||||
| 				if(msgwd) | 				if(msgwd) | ||||||
| 				{ | 				{ | ||||||
|  | |||||||
							
								
								
									
										824
									
								
								source/gui/dragdrop.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										824
									
								
								source/gui/dragdrop.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,824 @@ | |||||||
|  | /**
 | ||||||
|  | *	Drag and Drop Implementation | ||||||
|  | *	Nana C++ Library(http://www.nanapro.org)
 | ||||||
|  | *	Copyright(C) 2018 Jinhao(cnjinhao@hotmail.com) | ||||||
|  | * | ||||||
|  | *	Distributed under the Boost Software License, Version 1.0. | ||||||
|  | *	(See accompanying file LICENSE_1_0.txt or copy at | ||||||
|  | *	http://www.boost.org/LICENSE_1_0.txt)
 | ||||||
|  | * | ||||||
|  | *	@file: nana/gui/dragdrop.cpp | ||||||
|  | *	@author: Jinhao(cnjinhao@hotmail.com) | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <nana/gui/dragdrop.hpp> | ||||||
|  | #include <nana/gui/programming_interface.hpp> | ||||||
|  | 
 | ||||||
|  | #include <nana/gui/detail/bedrock.hpp> | ||||||
|  | #include <nana/gui/detail/basic_window.hpp> | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | #include <set> | ||||||
|  | 
 | ||||||
|  | #ifdef NANA_WINDOWS | ||||||
|  | #	include <windows.h> | ||||||
|  | #	include <oleidl.h> | ||||||
|  | #	include <comdef.h> | ||||||
|  | #	include <Shlobj.h> | ||||||
|  | #elif defined(NANA_X11) | ||||||
|  | #	include "../detail/posix/platform_spec.hpp" | ||||||
|  | #	include <nana/gui/detail/native_window_interface.hpp> | ||||||
|  | #	include <X11/Xcursor/Xcursor.h> | ||||||
|  | #	include <fstream> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace nana | ||||||
|  | { | ||||||
|  | 	/// drop_association
 | ||||||
|  | 	/**
 | ||||||
|  | 	 * This class is used for querying whether tow windows have a connection of drag and drop | ||||||
|  | 	 */ | ||||||
|  | 	class drop_association | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		void add(window source, window target) | ||||||
|  | 		{ | ||||||
|  | 			assoc_[source].insert(target); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void erase(window wd) | ||||||
|  | 		{ | ||||||
|  | 			assoc_.erase(wd); | ||||||
|  | 
 | ||||||
|  | 			for (auto & assoc : assoc_) | ||||||
|  | 				assoc.second.erase(wd); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bool has(window source, window target) const | ||||||
|  | 		{ | ||||||
|  | 			auto i = assoc_.find(source); | ||||||
|  | 			if (i != assoc_.end()) | ||||||
|  | 				return (0 != i->second.count(target)); | ||||||
|  | 			 | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		std::map<window, std::set<window>> assoc_; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | #ifdef NANA_WINDOWS | ||||||
|  | 	template<typename Interface, const IID& iid> | ||||||
|  | 	class win32com_iunknown : public Interface | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		//Implements IUnknown
 | ||||||
|  | 		STDMETHODIMP QueryInterface(REFIID riid, void **ppv) | ||||||
|  | 		{ | ||||||
|  | 			if (riid == IID_IUnknown || riid == iid) { | ||||||
|  | 				*ppv = static_cast<IUnknown*>(this); | ||||||
|  | 				AddRef(); | ||||||
|  | 				return S_OK; | ||||||
|  | 			} | ||||||
|  | 			*ppv = NULL; | ||||||
|  | 			return E_NOINTERFACE; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP_(ULONG) AddRef() | ||||||
|  | 		{ | ||||||
|  | 			return InterlockedIncrement(&ref_count_); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP_(ULONG) Release() | ||||||
|  | 		{ | ||||||
|  | 			LONG cRef = InterlockedDecrement(&ref_count_); | ||||||
|  | 			if (cRef == 0) delete this; | ||||||
|  | 			return cRef; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		LONG ref_count_{ 1 }; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	class win32com_drop_target : public IDropTarget | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		win32com_drop_target(const drop_association& drop_assoc) : | ||||||
|  | 			drop_assoc_(drop_assoc) | ||||||
|  | 		{} | ||||||
|  | 
 | ||||||
|  | 		void set_source(window wd) | ||||||
|  | 		{ | ||||||
|  | 			source_window_ = wd; | ||||||
|  | 		} | ||||||
|  | 	public: | ||||||
|  | 		//Implements IUnknown
 | ||||||
|  | 		STDMETHODIMP QueryInterface(REFIID riid, void **ppv) | ||||||
|  | 		{ | ||||||
|  | 			if (riid == IID_IUnknown || riid == IID_IDropTarget) { | ||||||
|  | 				*ppv = static_cast<IUnknown*>(this); | ||||||
|  | 				AddRef(); | ||||||
|  | 				return S_OK; | ||||||
|  | 			} | ||||||
|  | 			*ppv = NULL; | ||||||
|  | 			return E_NOINTERFACE; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP_(ULONG) AddRef() | ||||||
|  | 		{ | ||||||
|  | 			return InterlockedIncrement(&ref_count_); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP_(ULONG) Release() | ||||||
|  | 		{ | ||||||
|  | 			LONG cRef = InterlockedDecrement(&ref_count_); | ||||||
|  | 			if (cRef == 0) delete this; | ||||||
|  | 			return cRef; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		// IDropTarget
 | ||||||
|  | 		STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) | ||||||
|  | 		{ | ||||||
|  | 			*pdwEffect &= DROPEFFECT_COPY; | ||||||
|  | 			return S_OK; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) | ||||||
|  | 		{ | ||||||
|  | 			auto hovered_wd = API::find_window(point(pt.x, pt.y)); | ||||||
|  | 			if ((hovered_wd && (hovered_wd == source_window_)) || drop_assoc_.has(source_window_, hovered_wd)) | ||||||
|  | 				*pdwEffect &= DROPEFFECT_COPY; | ||||||
|  | 			else | ||||||
|  | 				*pdwEffect = DROPEFFECT_NONE; | ||||||
|  | 
 | ||||||
|  | 			return S_OK; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP DragLeave() | ||||||
|  | 		{ | ||||||
|  | 			return E_NOTIMPL; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) | ||||||
|  | 		{ | ||||||
|  | 			return E_NOTIMPL; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		LONG ref_count_{ 1 }; | ||||||
|  | 
 | ||||||
|  | 		window source_window_{ nullptr }; | ||||||
|  | 		const drop_association& drop_assoc_; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class drop_source : public win32com_iunknown<IDropSource, IID_IDropSource> | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		drop_source(window wd) : | ||||||
|  | 			window_handle_(wd) | ||||||
|  | 		{} | ||||||
|  | 
 | ||||||
|  | 		window source() const | ||||||
|  | 		{ | ||||||
|  | 			return window_handle_; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		// IDropSource
 | ||||||
|  | 		STDMETHODIMP QueryContinueDrag(BOOL esc_pressed, DWORD key_state) override | ||||||
|  | 		{ | ||||||
|  | 			if (esc_pressed) | ||||||
|  | 				return DRAGDROP_S_CANCEL; | ||||||
|  | 
 | ||||||
|  | 			//Drop the object if left button is released.
 | ||||||
|  | 			if (0 == (key_state & (MK_LBUTTON))) | ||||||
|  | 				return DRAGDROP_S_DROP; | ||||||
|  | 			 | ||||||
|  | 			return S_OK; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP GiveFeedback(DWORD effect) override | ||||||
|  | 		{ | ||||||
|  | 			return DRAGDROP_S_USEDEFAULTCURSORS; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		window const window_handle_; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class drop_data : public win32com_iunknown<IDataObject, IID_IDataObject> | ||||||
|  | 	{ | ||||||
|  | 		struct medium | ||||||
|  | 		{ | ||||||
|  | 			STGMEDIUM * stgmedium; | ||||||
|  | 			FORMATETC * format; | ||||||
|  | 		}; | ||||||
|  | 	public: | ||||||
|  | 		STDMETHODIMP GetData(FORMATETC *request_format, STGMEDIUM *pmedium) override | ||||||
|  | 		{ | ||||||
|  | 			if (!(request_format && pmedium)) | ||||||
|  | 				return E_INVALIDARG; | ||||||
|  | 
 | ||||||
|  | 			pmedium->hGlobal = nullptr; | ||||||
|  | 
 | ||||||
|  | 			for (auto & med : mediums_) | ||||||
|  | 			{ | ||||||
|  | 				if ((request_format->tymed & med.format->tymed) && | ||||||
|  | 					(request_format->dwAspect == med.format->dwAspect) && | ||||||
|  | 					(request_format->cfFormat == med.format->cfFormat)) | ||||||
|  | 				{ | ||||||
|  | 					return _m_copy_medium(pmedium, med.stgmedium, med.format); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			return DV_E_FORMATETC; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) override | ||||||
|  | 		{ | ||||||
|  | 			return E_NOTIMPL; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP QueryGetData(FORMATETC *pformatetc) override | ||||||
|  | 		{ | ||||||
|  | 			if (NULL == pformatetc) | ||||||
|  | 				return E_INVALIDARG; | ||||||
|  | 			 | ||||||
|  | 			if (!(DVASPECT_CONTENT & pformatetc->dwAspect)) | ||||||
|  | 				return DV_E_DVASPECT; | ||||||
|  | 			 | ||||||
|  | 			HRESULT result = DV_E_TYMED; | ||||||
|  | 
 | ||||||
|  | 			for(auto & med : mediums_) | ||||||
|  | 			{ | ||||||
|  | 				if (med.format->tymed & pformatetc->tymed) | ||||||
|  | 				{ | ||||||
|  | 					if (med.format->cfFormat == pformatetc->cfFormat) | ||||||
|  | 						return S_OK; | ||||||
|  | 					 | ||||||
|  | 					result = DV_E_FORMATETC; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return result; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut) override | ||||||
|  | 		{ | ||||||
|  | 			return E_NOTIMPL; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override | ||||||
|  | 		{ | ||||||
|  | 			if (!(pformatetc && pmedium)) | ||||||
|  | 				return E_INVALIDARG; | ||||||
|  | 
 | ||||||
|  | 			if (pformatetc->tymed != pmedium->tymed) | ||||||
|  | 				return E_FAIL; | ||||||
|  | 
 | ||||||
|  | 			medium retain; | ||||||
|  | 			retain.format = new FORMATETC; | ||||||
|  | 			retain.stgmedium = new (std::nothrow) STGMEDIUM; | ||||||
|  | 			if (nullptr == retain.stgmedium) | ||||||
|  | 			{ | ||||||
|  | 				delete retain.format; | ||||||
|  | 				return E_FAIL; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			std::memset(retain.format, 0, sizeof(FORMATETC)); | ||||||
|  | 			std::memset(retain.stgmedium, 0, sizeof(STGMEDIUM)); | ||||||
|  | 
 | ||||||
|  | 			*retain.format = *pformatetc; | ||||||
|  | 
 | ||||||
|  | 			_m_copy_medium(retain.stgmedium, pmedium, pformatetc); | ||||||
|  | 
 | ||||||
|  | 			if (TRUE == fRelease) | ||||||
|  | 				::ReleaseStgMedium(pmedium); | ||||||
|  | 
 | ||||||
|  | 			mediums_.emplace_back(retain); | ||||||
|  | 			return S_OK; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override | ||||||
|  | 		{ | ||||||
|  | 			if (NULL == ppenumFormatEtc) | ||||||
|  | 				return E_INVALIDARG; | ||||||
|  | 
 | ||||||
|  | 			if (DATADIR_GET != dwDirection) | ||||||
|  | 				return E_NOTIMPL; | ||||||
|  | 
 | ||||||
|  | 			*ppenumFormatEtc = NULL; | ||||||
|  | 
 | ||||||
|  | 			FORMATETC rgfmtetc[] = | ||||||
|  | 			{ | ||||||
|  | 				{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL }, | ||||||
|  | 			}; | ||||||
|  | 			return ::SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override | ||||||
|  | 		{ | ||||||
|  | 			return OLE_E_ADVISENOTSUPPORTED; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP DUnadvise(DWORD dwConnection) override | ||||||
|  | 		{ | ||||||
|  | 			return OLE_E_ADVISENOTSUPPORTED; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise) override | ||||||
|  | 		{ | ||||||
|  | 			return OLE_E_ADVISENOTSUPPORTED; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		static HRESULT _m_copy_medium(STGMEDIUM* stgmed_dst, STGMEDIUM* stgmed_src, FORMATETC* fmt_src) | ||||||
|  | 		{ | ||||||
|  | 			if (!(stgmed_dst && stgmed_src && fmt_src)) | ||||||
|  | 				return E_INVALIDARG; | ||||||
|  | 			 | ||||||
|  | 			switch (stgmed_src->tymed) | ||||||
|  | 			{ | ||||||
|  | 			case TYMED_HGLOBAL: | ||||||
|  | 				stgmed_dst->hGlobal = (HGLOBAL)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_GDI: | ||||||
|  | 				stgmed_dst->hBitmap = (HBITMAP)OleDuplicateData(stgmed_src->hBitmap, fmt_src->cfFormat, 0); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_MFPICT: | ||||||
|  | 				stgmed_dst->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(stgmed_src->hMetaFilePict, fmt_src->cfFormat, 0); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_ENHMF: | ||||||
|  | 				stgmed_dst->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(stgmed_src->hEnhMetaFile, fmt_src->cfFormat, 0); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_FILE: | ||||||
|  | 				stgmed_dst->lpszFileName = (LPOLESTR)OleDuplicateData(stgmed_src->lpszFileName, fmt_src->cfFormat, 0); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_ISTREAM: | ||||||
|  | 				stgmed_dst->pstm = stgmed_src->pstm; | ||||||
|  | 				stgmed_src->pstm->AddRef(); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_ISTORAGE: | ||||||
|  | 				stgmed_dst->pstg = stgmed_src->pstg; | ||||||
|  | 				stgmed_src->pstg->AddRef(); | ||||||
|  | 				break; | ||||||
|  | 			case TYMED_NULL: | ||||||
|  | 			default: | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			stgmed_dst->tymed = stgmed_src->tymed; | ||||||
|  | 			stgmed_dst->pUnkForRelease = nullptr; | ||||||
|  | 			if (stgmed_src->pUnkForRelease) | ||||||
|  | 			{ | ||||||
|  | 				stgmed_dst->pUnkForRelease = stgmed_src->pUnkForRelease; | ||||||
|  | 				stgmed_src->pUnkForRelease->AddRef(); | ||||||
|  | 			} | ||||||
|  | 			return S_OK; | ||||||
|  | 		} | ||||||
|  | 	private: | ||||||
|  | 		std::vector<medium> mediums_; | ||||||
|  | 	}; | ||||||
|  | #elif defined(NANA_X11) | ||||||
|  | 	class x11_dragdrop: public detail::dragdrop_interface | ||||||
|  | 	{ | ||||||
|  | 
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	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_; | ||||||
|  | 	}; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	class dragdrop_service | ||||||
|  | 	{ | ||||||
|  | 		dragdrop_service() = default; | ||||||
|  | 	public: | ||||||
|  | 		static dragdrop_service& instance() | ||||||
|  | 		{ | ||||||
|  | 			static dragdrop_service serv; | ||||||
|  | 			return serv; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void create_dragdrop(window wd) | ||||||
|  | 		{ | ||||||
|  | 			auto native_wd = API::root(wd); | ||||||
|  | 			if (nullptr == native_wd) | ||||||
|  | 				return; | ||||||
|  | 
 | ||||||
|  | #ifdef NANA_WINDOWS | ||||||
|  | 			if(table_.empty()) | ||||||
|  | 				::OleInitialize(nullptr); | ||||||
|  | 
 | ||||||
|  | 			win32com_drop_target* drop_target = nullptr; | ||||||
|  | 
 | ||||||
|  | 			auto i = table_.find(native_wd); | ||||||
|  | 			if (i == table_.end()) | ||||||
|  | 			{ | ||||||
|  | 				drop_target = new win32com_drop_target{drop_assoc_}; | ||||||
|  | 				::RegisterDragDrop(reinterpret_cast<HWND>(native_wd), drop_target); | ||||||
|  | 
 | ||||||
|  | 				table_[native_wd] = drop_target; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				drop_target = i->second; | ||||||
|  | 				drop_target->AddRef(); | ||||||
|  | 			} | ||||||
|  | #elif defined(NANA_X11) | ||||||
|  | 			auto ddrop = new x11_dragdrop; | ||||||
|  | 			if(!_m_spec().register_dragdrop(native_wd, ddrop)) | ||||||
|  | 				delete ddrop; | ||||||
|  | #endif | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void remove(window wd) | ||||||
|  | 		{ | ||||||
|  | #ifdef NANA_WINDOWS | ||||||
|  | 			auto i = table_.find(API::root(wd)); | ||||||
|  | 			if (i != table_.end()) | ||||||
|  | 			{ | ||||||
|  | 				if (0 == i->second->Release()) | ||||||
|  | 					table_.erase(i); | ||||||
|  | 			} | ||||||
|  | #elif defined(NANA_X11) | ||||||
|  | 			auto ddrop = _m_spec().remove_dragdrop(API::root(wd)); | ||||||
|  | 			delete ddrop; | ||||||
|  | #endif | ||||||
|  | 			drop_assoc_.erase(wd); | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bool dragdrop(window drag_wd) | ||||||
|  | 		{ | ||||||
|  | #ifdef NANA_WINDOWS | ||||||
|  | 			auto i = table_.find(API::root(drag_wd)); | ||||||
|  | 			if (table_.end() == i) | ||||||
|  | 				return false; | ||||||
|  | 
 | ||||||
|  | 			auto drop_src = new drop_source{ drag_wd }; | ||||||
|  | 			auto drop_dat = new (std::nothrow) drop_data; | ||||||
|  | 			if (!drop_dat) | ||||||
|  | 			{ | ||||||
|  | 				delete drop_src; | ||||||
|  | 				return false; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			i->second->set_source(drag_wd); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 			DWORD eff; | ||||||
|  | 			auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); | ||||||
|  | 
 | ||||||
|  | 			i->second->set_source(nullptr); | ||||||
|  | 
 | ||||||
|  | 			return true; | ||||||
|  | #elif defined(NANA_X11) | ||||||
|  | 			auto const native_wd = reinterpret_cast<Window>(API::root(drag_wd)); | ||||||
|  | 
 | ||||||
|  | 			{ | ||||||
|  | 				detail::platform_scope_guard lock; | ||||||
|  | 				::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_wd, CurrentTime); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 			hovered_.window_handle = nullptr; | ||||||
|  | 			hovered_.native_wd = 0; | ||||||
|  | 			window target_wd = 0; | ||||||
|  | 			auto& atombase = _m_spec().atombase(); | ||||||
|  | 			//while(true)
 | ||||||
|  | 			{ | ||||||
|  | 
 | ||||||
|  | 				_m_spec().msg_dispatch([this, drag_wd, native_wd, &target_wd, &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(); | ||||||
|  | 						if (MotionNotify == msg_pkt.u.xevent.type) | ||||||
|  | 						{ | ||||||
|  | 							auto pos = API::cursor_position(); | ||||||
|  | 							auto native_cur_wd = reinterpret_cast<Window>(detail::native_interface::find_window(pos.x, pos.y)); | ||||||
|  | 
 | ||||||
|  | 							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_wd, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); | ||||||
|  | 								hovered_.native_wd = native_cur_wd; | ||||||
|  | 							} | ||||||
|  | 
 | ||||||
|  | 							auto cur_wd = API::find_window(API::cursor_position()); | ||||||
|  | 
 | ||||||
|  | 							if(hovered_.window_handle != cur_wd) | ||||||
|  | 							{ | ||||||
|  | 								_m_free_cursor(); | ||||||
|  | 
 | ||||||
|  | 								hovered_.window_handle = cur_wd; | ||||||
|  | 
 | ||||||
|  | 								if((drag_wd == cur_wd) || drop_assoc_.has(drag_wd, cur_wd)) | ||||||
|  | 									hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-move").c_str()); | ||||||
|  | 								else | ||||||
|  | 									hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-none").c_str()); | ||||||
|  | 								::XDefineCursor(disp, native_cur_wd, hovered_.cursor);								 | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 						else if(msg_pkt.u.xevent.type == ButtonRelease) | ||||||
|  | 						{ | ||||||
|  | 							target_wd = API::find_window(API::cursor_position()); | ||||||
|  | 							::XUndefineCursor(disp, hovered_.native_wd); | ||||||
|  | 							_m_free_cursor(); | ||||||
|  | 							return true; | ||||||
|  | 						} | ||||||
|  | 						 | ||||||
|  | 					} | ||||||
|  | 					return false; | ||||||
|  | 				}); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return (nullptr != target_wd); | ||||||
|  | #endif | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		drop_association& drop_assoc() | ||||||
|  | 		{ | ||||||
|  | 			return drop_assoc_; | ||||||
|  | 		} | ||||||
|  | #ifdef NANA_X11 | ||||||
|  | 	private: | ||||||
|  | 		static nana::detail::platform_spec & _m_spec() | ||||||
|  | 		{ | ||||||
|  | 			return nana::detail::platform_spec::instance(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		//dndversion<<24, fl_XdndURIList, XA_STRING, 0
 | ||||||
|  | 		static void _m_client_msg(Window wd_target, Window wd_src, Atom xdnd_atom, Atom data, Atom data_type) | ||||||
|  | 		{ | ||||||
|  | 			auto const display = _m_spec().open_display(); | ||||||
|  | 			XEvent evt; | ||||||
|  | 			::memset(&evt, 0, sizeof evt); | ||||||
|  | 			evt.xany.type = ClientMessage; | ||||||
|  | 			evt.xany.display = display; | ||||||
|  | 			evt.xclient.window = wd_target; | ||||||
|  | 			evt.xclient.message_type = xdnd_atom; | ||||||
|  | 			evt.xclient.format = 32; | ||||||
|  | 
 | ||||||
|  | 			//Target window
 | ||||||
|  | 			evt.xclient.data.l[0] = wd_src; | ||||||
|  | 			//Accept set
 | ||||||
|  | 			evt.xclient.data.l[1] = 1; | ||||||
|  | 			evt.xclient.data.l[2] = data; | ||||||
|  | 			evt.xclient.data.l[3] = data_type; | ||||||
|  | 			evt.xclient.data.l[4] = 0; | ||||||
|  | 
 | ||||||
|  | 			::XSendEvent(display, wd_target, True, NoEventMask, &evt); | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		static int _m_xdnd_aware(Window wd) | ||||||
|  | 		{ | ||||||
|  | 			Atom actual; int format; unsigned long count, remaining; | ||||||
|  | 			unsigned char *data = 0; | ||||||
|  | 			XGetWindowProperty(_m_spec().open_display(), wd, _m_spec().atombase().xdnd_aware,  | ||||||
|  | 				0, 4, False, XA_ATOM, &actual, &format, &count, &remaining, &data); | ||||||
|  | 
 | ||||||
|  | 			int version = 0; | ||||||
|  | 			if ((actual == XA_ATOM) && (format==32) && count && data) | ||||||
|  | 				version = int(*(Atom*)data); | ||||||
|  | 
 | ||||||
|  | 			if (data) | ||||||
|  | 				::XFree(data); | ||||||
|  | 			return version; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void _m_free_cursor() | ||||||
|  | 		{ | ||||||
|  | 			if(hovered_.cursor) | ||||||
|  | 			{ | ||||||
|  | 				::XFreeCursor(_m_spec().open_display(), hovered_.cursor); | ||||||
|  | 				hovered_.cursor = 0; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 	private: | ||||||
|  | 		drop_association drop_assoc_; | ||||||
|  | #ifdef NANA_WINDOWS | ||||||
|  | 		std::map<native_window_type, win32com_drop_target*> table_; | ||||||
|  | #elif defined (NANA_X11) | ||||||
|  | 		shared_icons icons_; | ||||||
|  | 		struct hovered_status | ||||||
|  | 		{ | ||||||
|  | 			Window native_wd{0}; | ||||||
|  | 			window window_handle{nullptr}; | ||||||
|  | 
 | ||||||
|  | 			unsigned shape{0}; | ||||||
|  | 			Cursor cursor{0}; | ||||||
|  | 		}hovered_; | ||||||
|  | #endif | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	struct simple_dragdrop::implementation | ||||||
|  | 	{ | ||||||
|  | 		window window_handle; | ||||||
|  | 		std::function<bool()> predicate; | ||||||
|  | 		std::map<window, std::function<void()>> targets; | ||||||
|  | 
 | ||||||
|  | 		bool dragging{ false }; | ||||||
|  | 
 | ||||||
|  | #ifdef NANA_X11 | ||||||
|  | 		bool cancel() | ||||||
|  | 		{ | ||||||
|  | 			if (!dragging) | ||||||
|  | 				return false; | ||||||
|  | 
 | ||||||
|  | 			if (API::is_window(window_handle)) | ||||||
|  | 			{ | ||||||
|  | 				dragging = true; | ||||||
|  | 				using basic_window = ::nana::detail::basic_window; | ||||||
|  | 				auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(window_handle); | ||||||
|  | 				real_wd->other.dnd_state = dragdrop_status::not_ready; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			API::release_capture(window_handle); | ||||||
|  | 			dragging = false; | ||||||
|  | 
 | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	simple_dragdrop::simple_dragdrop(window drag_wd) : | ||||||
|  | 		impl_(new implementation) | ||||||
|  | 	{ | ||||||
|  | 		dragdrop_service::instance().create_dragdrop(drag_wd); | ||||||
|  | 
 | ||||||
|  | 		if (!API::is_window(drag_wd)) | ||||||
|  | 		{ | ||||||
|  | 			delete impl_; | ||||||
|  | 			throw std::invalid_argument("simple_dragdrop: invalid window handle"); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		impl_->window_handle = drag_wd; | ||||||
|  | 		API::dev::window_draggable(drag_wd, true); | ||||||
|  | 
 | ||||||
|  | 		auto & events = API::events<>(drag_wd); | ||||||
|  | 
 | ||||||
|  | #if 1 //#ifdef NANA_WINDOWS
 | ||||||
|  | 		events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ | ||||||
|  | 			if (arg.is_left_button() && API::is_window(impl_->window_handle)) | ||||||
|  | 			{ | ||||||
|  | 				impl_->dragging = ((!impl_->predicate) || impl_->predicate()); | ||||||
|  | 
 | ||||||
|  | 				using basic_window = ::nana::detail::basic_window; | ||||||
|  | 				auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(impl_->window_handle); | ||||||
|  | 				real_wd->other.dnd_state = dragdrop_status::ready; | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		events.mouse_move.connect_unignorable([this](const arg_mouse& arg) { | ||||||
|  | 			if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) | ||||||
|  | 				return; | ||||||
|  | 
 | ||||||
|  | 			using basic_window = ::nana::detail::basic_window; | ||||||
|  | 			auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); | ||||||
|  | 			real_wd->other.dnd_state = dragdrop_status::in_progress; | ||||||
|  | 
 | ||||||
|  | 			auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); | ||||||
|  | 
 | ||||||
|  | 			real_wd->other.dnd_state = dragdrop_status::not_ready; | ||||||
|  | 			impl_->dragging = false; | ||||||
|  | 
 | ||||||
|  | 			if (has_dropped) | ||||||
|  | 			{ | ||||||
|  | 				auto drop_wd = API::find_window(API::cursor_position()); | ||||||
|  | 				auto i = impl_->targets.find(drop_wd); | ||||||
|  | 				if ((impl_->targets.end() != i) && i->second) | ||||||
|  | 					i->second(); | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | #elif 1 | ||||||
|  | 		events.mouse_down.connect_unignorable([drag_wd](const arg_mouse& arg){ | ||||||
|  | 			if (arg.is_left_button() && API::is_window(drag_wd)) | ||||||
|  | 			{ | ||||||
|  | 				//API::set_capture(drag_wd, true);
 | ||||||
|  | 
 | ||||||
|  | 				using basic_window = ::nana::detail::basic_window; | ||||||
|  | 				auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(drag_wd); | ||||||
|  | 				real_wd->other.dnd_state = dragdrop_status::ready; | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		events.mouse_move.connect_unignorable([this](const arg_mouse& arg){ | ||||||
|  | 			if (!arg.is_left_button()) | ||||||
|  | 				return; | ||||||
|  | 
 | ||||||
|  | 			if (impl_->dragging) | ||||||
|  | 			{ | ||||||
|  | 				auto drop_wd = API::find_window(API::cursor_position()); | ||||||
|  | 				auto i = impl_->targets.find(drop_wd); | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				if ((!impl_->predicate) || impl_->predicate()) | ||||||
|  | 				{ | ||||||
|  | 					if (API::is_window(arg.window_handle)) | ||||||
|  | 					{ | ||||||
|  | 						impl_->dragging = true; | ||||||
|  | 						using basic_window = ::nana::detail::basic_window; | ||||||
|  | 						auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); | ||||||
|  | 						real_wd->other.dnd_state = dragdrop_status::in_progress; | ||||||
|  | 
 | ||||||
|  | 						dragdrop_service::instance().dragdrop(arg.window_handle); | ||||||
|  | 						return; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				//API::release_capture(impl_->window_handle);
 | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		events.mouse_up.connect_unignorable([this]{ | ||||||
|  | 			if (impl_->cancel()) | ||||||
|  | 			{ | ||||||
|  | 				auto drop_wd = API::find_window(API::cursor_position()); | ||||||
|  | 				auto i = impl_->targets.find(drop_wd); | ||||||
|  | 				if (impl_->targets.end() == i || !i->second) | ||||||
|  | 					return; | ||||||
|  | 
 | ||||||
|  | 				i->second(); | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		events.key_press.connect_unignorable([this]{ | ||||||
|  | 			impl_->cancel(); | ||||||
|  | 		}); | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	simple_dragdrop::~simple_dragdrop() | ||||||
|  | 	{ | ||||||
|  | 		dragdrop_service::instance().remove(impl_->window_handle); | ||||||
|  | 		API::dev::window_draggable(impl_->window_handle, false); | ||||||
|  | 		delete impl_; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void simple_dragdrop::condition(std::function<bool()> predicate_fn) | ||||||
|  | 	{ | ||||||
|  | 		impl_->predicate.swap(predicate_fn); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void simple_dragdrop::make_drop(window target, std::function<void()> drop_fn) | ||||||
|  | 	{ | ||||||
|  | 		dragdrop_service::instance().drop_assoc().add(impl_->window_handle, target); | ||||||
|  | 		impl_->targets[target].swap(drop_fn); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -411,6 +411,26 @@ namespace API | |||||||
| 
 | 
 | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		void window_draggable(window wd, bool enabled) | ||||||
|  | 		{ | ||||||
|  | 			auto real_wd = reinterpret_cast<basic_window*>(wd); | ||||||
|  | 			internal_scope_guard lock; | ||||||
|  | 			if (restrict::wd_manager().available(real_wd)) | ||||||
|  | 				real_wd->flags.draggable = enabled; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bool window_draggable(window wd) | ||||||
|  | 		{ | ||||||
|  | 			auto real_wd = reinterpret_cast<basic_window*>(wd); | ||||||
|  | 			internal_scope_guard lock; | ||||||
|  | 			if (restrict::wd_manager().available(real_wd)) | ||||||
|  | 				return real_wd->flags.draggable; | ||||||
|  | 
 | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 	}//end namespace dev
 | 	}//end namespace dev
 | ||||||
| 
 | 
 | ||||||
| 	widget* get_widget(window wd) | 	widget* get_widget(window wd) | ||||||
| @ -1518,5 +1538,16 @@ namespace API | |||||||
| 	{ | 	{ | ||||||
| 		return ::nana::platform_abstraction::screen_dpi(x_requested); | 		return ::nana::platform_abstraction::screen_dpi(x_requested); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	dragdrop_status window_dragdrop_status(::nana::window wd) | ||||||
|  | 	{ | ||||||
|  | 		auto real_wd = reinterpret_cast<basic_window*>(wd); | ||||||
|  | 		internal_scope_guard lock; | ||||||
|  | 
 | ||||||
|  | 		if (restrict::wd_manager().available(real_wd)) | ||||||
|  | 			return real_wd->other.dnd_state; | ||||||
|  | 
 | ||||||
|  | 		return dragdrop_status::not_ready; | ||||||
|  | 	} | ||||||
| }//end namespace API
 | }//end namespace API
 | ||||||
| }//end namespace nana
 | }//end namespace nana
 | ||||||
|  | |||||||
| @ -1968,6 +1968,12 @@ namespace nana | |||||||
| 				std::vector<inline_pane*> active_panes_; | 				std::vector<inline_pane*> active_panes_; | ||||||
| 			};//end class es_lister
 | 			};//end class es_lister
 | ||||||
| 
 | 
 | ||||||
|  | 			enum class operation_states | ||||||
|  | 			{ | ||||||
|  | 				none, | ||||||
|  | 				msup_deselect | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 			/// created and live by the trigger, holds data for listbox: the state of the struct does not effect on member funcions, therefore all data members are public.
 | 			/// created and live by the trigger, holds data for listbox: the state of the struct does not effect on member funcions, therefore all data members are public.
 | ||||||
| 			struct essence | 			struct essence | ||||||
| @ -1981,7 +1987,9 @@ namespace nana | |||||||
| 				bool auto_draw{true}; | 				bool auto_draw{true}; | ||||||
| 				bool checkable{false}; | 				bool checkable{false}; | ||||||
| 				bool if_image{false}; | 				bool if_image{false}; | ||||||
|  | #if 0 //deprecated
 | ||||||
| 				bool deselect_deferred{ false };	//deselects items when mouse button is released.
 | 				bool deselect_deferred{ false };	//deselects items when mouse button is released.
 | ||||||
|  | #endif | ||||||
| 				unsigned text_height; | 				unsigned text_height; | ||||||
| 
 | 
 | ||||||
|                 ::nana::listbox::export_options def_exp_options; |                 ::nana::listbox::export_options def_exp_options; | ||||||
| @ -1999,6 +2007,12 @@ namespace nana | |||||||
| 
 | 
 | ||||||
| 				std::function<void(paint::graphics&, const rectangle&, bool)> ctg_icon_renderer;	///< Renderer for the category icon
 | 				std::function<void(paint::graphics&, const rectangle&, bool)> ctg_icon_renderer;	///< Renderer for the category icon
 | ||||||
| 
 | 
 | ||||||
|  | 				struct operation_rep | ||||||
|  | 				{ | ||||||
|  | 					operation_states state{operation_states::none}; | ||||||
|  | 					index_pair item; | ||||||
|  | 				}operation; | ||||||
|  | 
 | ||||||
| 				struct mouse_selection_part | 				struct mouse_selection_part | ||||||
| 				{ | 				{ | ||||||
| 					bool	started{ false }; | 					bool	started{ false }; | ||||||
| @ -2121,6 +2135,10 @@ namespace nana | |||||||
| 
 | 
 | ||||||
| 				void update_mouse_selection(const point& screen_pos) | 				void update_mouse_selection(const point& screen_pos) | ||||||
| 				{ | 				{ | ||||||
|  | 					//Don't update if it is not started
 | ||||||
|  | 					if (!mouse_selection.started) | ||||||
|  | 						return; | ||||||
|  | 
 | ||||||
| 					mouse_selection.screen_pos = screen_pos; | 					mouse_selection.screen_pos = screen_pos; | ||||||
| 
 | 
 | ||||||
| 					auto logic_pos = coordinate_cast(screen_pos, true); | 					auto logic_pos = coordinate_cast(screen_pos, true); | ||||||
| @ -4132,8 +4150,13 @@ namespace nana | |||||||
| 					using item_state = essence::item_state; | 					using item_state = essence::item_state; | ||||||
| 					using parts = essence::parts; | 					using parts = essence::parts; | ||||||
| 
 | 
 | ||||||
|  | #if 0	//deprecated
 | ||||||
| 					//Cancel deferred deselection operation when mouse moves.
 | 					//Cancel deferred deselection operation when mouse moves.
 | ||||||
| 					essence_->deselect_deferred = false; | 					essence_->deselect_deferred = false; | ||||||
|  | #else | ||||||
|  | 					if (operation_states::msup_deselect == essence_->operation.state) | ||||||
|  | 						essence_->operation.state = operation_states::none; | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 					bool need_refresh = false; | 					bool need_refresh = false; | ||||||
| 
 | 
 | ||||||
| @ -4293,21 +4316,44 @@ namespace nana | |||||||
| 										essence_->mouse_selection.reverse_selection = true; | 										essence_->mouse_selection.reverse_selection = true; | ||||||
| 										new_selected_status = !essence_->cs_status(abs_item_pos, true); | 										new_selected_status = !essence_->cs_status(abs_item_pos, true); | ||||||
| 									} | 									} | ||||||
|  | #if 0 //deprecated
 | ||||||
|  | 									else if (nana::mouse::right_button == arg.button) | ||||||
|  | 									{ | ||||||
|  | 										//Unselects all selected items if the current item is not selected before selecting.
 | ||||||
|  | 										auto selected = lister.pick_items(true); | ||||||
|  | 										if (selected.cend() == std::find(selected.cbegin(), selected.cend(), item_pos)) | ||||||
|  | 											lister.select_for_all(false, abs_item_pos); | ||||||
|  | 									} | ||||||
| 									else | 									else | ||||||
| 									{ | 									{ | ||||||
| 										if (nana::mouse::right_button == arg.button) | 										//Unselects all selected items except current item if right button clicked.
 | ||||||
|  | 										lister.select_for_all(false, abs_item_pos);	//cancel all selections
 | ||||||
|  | 									} | ||||||
|  | #else | ||||||
|  | 									else | ||||||
|  | 									{ | ||||||
|  | 										auto selected = lister.pick_items(true); | ||||||
|  | 										if (selected.cend() != std::find(selected.cbegin(), selected.cend(), item_pos)) | ||||||
| 										{ | 										{ | ||||||
| 											//Unselects all selected items if the current item is not selected before selecting.
 | 											//If the current selected one has been selected before selecting, remains the selection states for all
 | ||||||
| 											auto selected = lister.pick_items(true); | 											//selected items. But these items will be unselected when the mouse is released.
 | ||||||
| 											if (selected.cend() == std::find(selected.cbegin(), selected.cend(), item_pos)) | 
 | ||||||
| 												lister.select_for_all(false, abs_item_pos); | 											//Other items will be unselected if multiple items are selected.
 | ||||||
|  | 											if (selected.size() > 1) | ||||||
|  | 											{ | ||||||
|  | 												essence_->operation.item = abs_item_pos; | ||||||
|  | 
 | ||||||
|  | 												//Don't deselect the selections if the listbox is draggable.
 | ||||||
|  | 												//It should remain the selections for drag-and-drop
 | ||||||
|  | 
 | ||||||
|  | 												if (!API::dev::window_draggable(arg.window_handle)) | ||||||
|  | 													essence_->operation.state = operation_states::msup_deselect; | ||||||
|  | 											} | ||||||
| 										} | 										} | ||||||
| 										else | 										else | ||||||
| 										{ | 											lister.select_for_all(false, abs_item_pos); | ||||||
| 											//Unselects all selected items except current item if right button clicked.
 |  | ||||||
| 											lister.select_for_all(false, abs_item_pos);	//cancel all selections
 |  | ||||||
| 										} |  | ||||||
| 									} | 									} | ||||||
|  | #endif | ||||||
| 								} | 								} | ||||||
| 								else | 								else | ||||||
| 								{ | 								{ | ||||||
| @ -4377,7 +4423,15 @@ namespace nana | |||||||
| 
 | 
 | ||||||
| 						//Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed
 | 						//Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed
 | ||||||
| 						//Pressing ctrl or shift is to selects other items without deselecting current selections.
 | 						//Pressing ctrl or shift is to selects other items without deselecting current selections.
 | ||||||
|  | #if 0 //deprecated
 | ||||||
| 						essence_->deselect_deferred = !(arg.ctrl || arg.shift); | 						essence_->deselect_deferred = !(arg.ctrl || arg.shift); | ||||||
|  | #else | ||||||
|  | 						if (!(arg.ctrl || arg.shift)) | ||||||
|  | 						{ | ||||||
|  | 							essence_->operation.state = operation_states::msup_deselect; | ||||||
|  | 							essence_->operation.item = index_pair{nana::npos, nana::npos}; | ||||||
|  | 						} | ||||||
|  | #endif | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					if(update) | 					if(update) | ||||||
| @ -4419,11 +4473,19 @@ namespace nana | |||||||
| 						need_refresh = true; | 						need_refresh = true; | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
|  | #if 0	//deprecated
 | ||||||
| 					if (essence_->deselect_deferred) | 					if (essence_->deselect_deferred) | ||||||
| 					{ | 					{ | ||||||
| 						essence_->deselect_deferred = false; | 						essence_->deselect_deferred = false; | ||||||
| 						need_refresh |= (essence_->lister.select_for_all(false)); | 						need_refresh |= (essence_->lister.select_for_all(false)); | ||||||
| 					} | 					} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 					if (operation_states::msup_deselect == essence_->operation.state) | ||||||
|  | 					{ | ||||||
|  | 						essence_->operation.state = operation_states::none; | ||||||
|  | 						need_refresh |= essence_->lister.select_for_all(false, essence_->operation.item); | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					if (need_refresh) | 					if (need_refresh) | ||||||
| 					{ | 					{ | ||||||
| @ -5790,6 +5852,18 @@ namespace nana | |||||||
| 			return item_proxy(ess); | 			return item_proxy(ess); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		listbox::index_pair listbox::hovered() const | ||||||
|  | 		{ | ||||||
|  | 			internal_scope_guard lock; | ||||||
|  | 			using parts = drawerbase::listbox::essence::parts; | ||||||
|  | 
 | ||||||
|  | 			auto & ptr_where = _m_ess().pointer_where; | ||||||
|  | 			if ((ptr_where.first == parts::list || ptr_where.first == parts::checker) && ptr_where.second != npos) | ||||||
|  | 				return _m_ess().lister.advance(_m_ess().first_display(), static_cast<int>(ptr_where.second)); | ||||||
|  | 			 | ||||||
|  | 			return index_pair{ npos, npos }; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		bool listbox::sortable() const | 		bool listbox::sortable() const | ||||||
| 		{ | 		{ | ||||||
| 			internal_scope_guard lock; | 			internal_scope_guard lock; | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ namespace nana { | |||||||
| 
 | 
 | ||||||
| 				bool	passive{ true }; //The passive mode determines whether to update if scrollbar changes. It updates the client window if passive is true.
 | 				bool	passive{ true }; //The passive mode determines whether to update if scrollbar changes. It updates the client window if passive is true.
 | ||||||
| 
 | 
 | ||||||
| 				bool	drag_started{ false }; | 				bool	drag_view_move{ false };	//indicates the status of the content-view if it moves origin by dragging
 | ||||||
| 				point origin; | 				point origin; | ||||||
| 
 | 
 | ||||||
| 				std::shared_ptr<cv_scroll_rep> cv_scroll; | 				std::shared_ptr<cv_scroll_rep> cv_scroll; | ||||||
| @ -120,19 +120,22 @@ namespace nana { | |||||||
| 							if (!arg.is_left_button()) | 							if (!arg.is_left_button()) | ||||||
| 								return; | 								return; | ||||||
| 
 | 
 | ||||||
| 							this->drag_started = this->view.view_area().is_hit(arg.pos); | 							this->drag_view_move = this->view.view_area().is_hit(arg.pos); | ||||||
| 						} | 						} | ||||||
| 						else if (event_code::mouse_move == arg.evt_code) | 						else if (event_code::mouse_move == arg.evt_code) | ||||||
| 						{ | 						{ | ||||||
| 							if (this->drag_started && this->drive(arg.pos)) | 							if (this->drag_view_move && (dragdrop_status::not_ready == API::window_dragdrop_status(this->window_handle))) | ||||||
| 							{ | 							{ | ||||||
| 								tmr.interval(16); | 								if (this->drive(arg.pos)) | ||||||
| 								tmr.start(); | 								{ | ||||||
|  | 									tmr.interval(16); | ||||||
|  | 									tmr.start(); | ||||||
|  | 								} | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 						else if (event_code::mouse_up == arg.evt_code) | 						else if (event_code::mouse_up == arg.evt_code) | ||||||
| 						{ | 						{ | ||||||
| 							this->drag_started = false; | 							this->drag_view_move = false; | ||||||
| 							tmr.stop(); | 							tmr.stop(); | ||||||
| 						} | 						} | ||||||
| 					}; | 					}; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 qPCR4vir
						qPCR4vir