commit
						66461d6304
					
				| @ -1,7 +1,7 @@ | ||||
| /**
 | ||||
|  *	Filebox | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0. | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -69,11 +69,10 @@ namespace nana | ||||
| 
 | ||||
| 
 | ||||
| 		const ::std::string& path() const; | ||||
| 		const ::std::string& file() const; | ||||
| #if defined(NANA_WINDOWS) | ||||
| 		::std::string file() const; | ||||
| 
 | ||||
| 		const ::std::vector<::std::string>& files() const; | ||||
| 		void allow_multi_select(bool allow); | ||||
| #endif | ||||
| 
 | ||||
| 		/// Display the filebox dialog
 | ||||
| 		bool show() const; | ||||
|  | ||||
| @ -104,6 +104,7 @@ namespace drawerbase | ||||
| 		struct element_tag | ||||
| 		{ | ||||
| 			checkbox * uiobj; | ||||
| 			event_handle eh_clicked; | ||||
| 			event_handle eh_checked; | ||||
| 			event_handle eh_destroy; | ||||
| 			event_handle eh_keyboard; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| /*
 | ||||
|  *	Concept of Component Set | ||||
|  *	Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0.  | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at  | ||||
| @ -60,17 +60,19 @@ namespace nana{	namespace widgets{ namespace detail | ||||
| 		/// Widget scheme.
 | ||||
| 		typedef WidgetScheme widget_scheme_t; | ||||
| 
 | ||||
| 		widget_scheme_t * wdg_scheme_ptr_{ nullptr }; | ||||
| 		//widget_scheme_t * wdg_scheme_ptr_{ nullptr };	//deprecated
 | ||||
| 
 | ||||
| 	public: | ||||
| 		/// The destructor.
 | ||||
| 		virtual ~compset_placer(){} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		/// Init the scheme pointer
 | ||||
| 		void init_scheme(widget_scheme_t* wdg_scheme_ptr) | ||||
| 		void init_scheme(widget_scheme_t* wdg_scheme_ptr)	//deprecated
 | ||||
| 		{ | ||||
| 			wdg_scheme_ptr_ = wdg_scheme_ptr; | ||||
| 		} | ||||
| 		*/ | ||||
| 
 | ||||
| 		/// Enable/Disable the specified component.
 | ||||
| 		virtual void enable(component_t, bool) = 0; | ||||
|  | ||||
| @ -92,6 +92,9 @@ namespace nana{ | ||||
| 		/// Determines whether a specified option is checked, it throws an out_of_range if !(pos < number of options)
 | ||||
| 		bool option_checked(std::size_t pos) const; | ||||
| 
 | ||||
| 		/// Change typeface of caption label ( does not effect child widgets )
 | ||||
| 		void typeface( const nana::paint::font& font ); | ||||
| 
 | ||||
| 		group& enable_format_caption(bool format); | ||||
| 
 | ||||
| 		group& collocate() noexcept; | ||||
| @ -101,7 +104,7 @@ namespace nana{ | ||||
| 		void field_display(const char* field_name, bool display); ///<Displays/Discards an existing field.
 | ||||
| 		bool field_display(const char* field_name) const;	///<Determines whether the specified field is displayed.
 | ||||
| 		void erase(window handle);				///< Erases a window from field.
 | ||||
| 		 | ||||
| 
 | ||||
| 		template<typename Widget, typename ...Args> | ||||
| 		Widget* create_child(const char* field, Args && ... args) | ||||
| 		{ | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /**
 | ||||
|  *	A List Box Implementation | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0.  | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at  | ||||
| @ -1461,7 +1461,19 @@ the nana::detail::basic_window member pointer scheme | ||||
| 		/// Returns the number of columns
 | ||||
| 		size_type column_size() const; | ||||
| 
 | ||||
| 		void column_resizable(bool resizable); | ||||
| 		/// Move column to view_position
 | ||||
| 		void move_column(size_type abs_pos, size_type view_pos); | ||||
| 
 | ||||
|         /// Sort columns in range first_col to last_col inclusive using the values from a row
 | ||||
|         void reorder_columns(size_type first_col, | ||||
| 							 size_type last_col, | ||||
| 							 index_pair row, bool reverse, | ||||
| 							 std::function<bool(const std::string &cell1, size_type col1, | ||||
| 												const std::string &cell2, size_type col2, | ||||
| 												const nana::any *rowval, | ||||
| 												bool reverse)> comp); | ||||
| 
 | ||||
|         void column_resizable(bool resizable); | ||||
| 		bool column_resizable() const; | ||||
| 		void column_movable(bool); | ||||
| 		bool column_movable() const; | ||||
| @ -1517,7 +1529,8 @@ the nana::detail::basic_window member pointer scheme | ||||
| 		 | ||||
| 		///Sets a strict weak ordering comparer for a column
 | ||||
| 		void set_sort_compare(	size_type col, | ||||
| 								std::function<bool(const std::string&, nana::any*, const std::string&, nana::any*, bool reverse)> strick_ordering); | ||||
| 								std::function<bool(const std::string&, nana::any*, | ||||
| 								                   const std::string&, nana::any*, bool reverse)> strick_ordering); | ||||
| 
 | ||||
| 		/// sort() and ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items
 | ||||
| 		void sort_col(size_type col, bool reverse = false); | ||||
| @ -1538,6 +1551,7 @@ the nana::detail::basic_window member pointer scheme | ||||
| 
 | ||||
| 		void enable_single(bool for_selection, bool category_limited); | ||||
| 		void disable_single(bool for_selection); | ||||
| 		bool is_single_enabled(bool for_selection) const noexcept;	///< Determines whether the single selection/check is enabled.
 | ||||
| 		export_options& def_export_options(); | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -151,7 +151,13 @@ namespace nana | ||||
| 		bool vertical() const; | ||||
| 		void maximum(unsigned); | ||||
| 		unsigned maximum() const; | ||||
| 		void value(unsigned); | ||||
| 
 | ||||
| 		/** Set slider value
 | ||||
|             @param[in] v new value for slider. | ||||
|             v will be clipped to the range 0 to maximum | ||||
|         */ | ||||
| 		void value(int ); | ||||
| 
 | ||||
| 		unsigned value() const; | ||||
| 		unsigned move_step(bool forward);                         ///< Increase or decrease the value of slider.
 | ||||
| 		unsigned adorn() const; | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /**
 | ||||
|  *	A Tree Box Implementation | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0.  | ||||
|  *	(See accompanying file LICENSE or copy at  | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  *	Platform Implementation | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0. | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -29,6 +29,7 @@ | ||||
| 
 | ||||
| #include "../../paint/image_accessor.hpp" | ||||
| 
 | ||||
| 
 | ||||
| namespace nana{ | ||||
| 	namespace detail{ | ||||
| 
 | ||||
| @ -199,7 +200,7 @@ namespace nana{ | ||||
| 		namespace x11_wait | ||||
| 		{ | ||||
| 			static Bool configure(Display *disp, XEvent *evt, char *arg) | ||||
| 			{     | ||||
| 			{ | ||||
| 			    return disp && evt && arg && (evt->type == ConfigureNotify) && (evt->xconfigure.window == *reinterpret_cast<Window*>(arg)); | ||||
| 			} | ||||
| 
 | ||||
| @ -1019,15 +1020,11 @@ namespace nana{ | ||||
| 			auto const owner = restrict::spec.get_owner(wd); | ||||
| 			if(owner && (owner != reinterpret_cast<native_window_type>(restrict::spec.root_window()))) | ||||
| 			{ | ||||
| 				auto origin = window_position(owner); | ||||
| #if 0 | ||||
| 				x += origin.x; | ||||
| 				y += origin.y; | ||||
| #else | ||||
| 				auto owner_extents = window_frame_extents(owner); | ||||
| 				x += origin.x + owner_extents.left; | ||||
| 				y += origin.y + owner_extents.top; | ||||
| #endif | ||||
| 				int origin_x, origin_y; | ||||
| 				Window child_useless_for_API; | ||||
| 				::XTranslateCoordinates(disp, reinterpret_cast<Window>(owner), restrict::spec.root_window(), 0, 0, &origin_x, &origin_y, &child_useless_for_API); | ||||
| 				x += origin_x; | ||||
| 				y += origin_y; | ||||
| 			} | ||||
| 
 | ||||
| 			::XMoveWindow(disp, reinterpret_cast<Window>(wd), x, y); | ||||
| @ -1074,7 +1071,6 @@ namespace nana{ | ||||
| 			XSizeHints hints; | ||||
| 			nana::detail::platform_scope_guard psg; | ||||
| 
 | ||||
| 
 | ||||
| 			//Returns if the requested rectangle is same with the current rectangle.
 | ||||
| 			//In some X-Server versions/implementations, XMapWindow() doesn't generate
 | ||||
| 			//a ConfigureNotify if the requested rectangle is same with the current rectangle.
 | ||||
| @ -1114,11 +1110,11 @@ namespace nana{ | ||||
| 			auto const owner = restrict::spec.get_owner(wd); | ||||
| 			if(owner && (owner != reinterpret_cast<native_window_type>(restrict::spec.root_window()))) | ||||
| 			{ | ||||
| 				auto origin = window_position(owner); | ||||
| 
 | ||||
| 				auto owner_extents = window_frame_extents(owner); | ||||
| 				x += origin.x + owner_extents.left; | ||||
| 				y += origin.y + owner_extents.top; | ||||
| 				int origin_x, origin_y; | ||||
| 				Window child_useless_for_API; | ||||
| 				::XTranslateCoordinates(disp, reinterpret_cast<Window>(owner), restrict::spec.root_window(), 0, 0, &origin_x, &origin_y, &child_useless_for_API); | ||||
| 				x += origin_x; | ||||
| 				y += origin_y; | ||||
| 			} | ||||
| 
 | ||||
| 			::XMoveResizeWindow(disp, reinterpret_cast<Window>(wd), x, y, r.width, r.height); | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
| *	Filebox | ||||
| *	Nana C++ Library(http://www.nanapro.org)
 | ||||
| *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
| *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
| * | ||||
| *	Distributed under the Boost Software License, Version 1.0. | ||||
| *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -38,6 +38,8 @@ | ||||
| #	include "../detail/posix/theme.hpp" | ||||
| #endif | ||||
| 
 | ||||
| #include <iostream> //debug
 | ||||
| 
 | ||||
| namespace fs = std::filesystem; | ||||
| namespace fs_ext = nana::filesystem_ext; | ||||
| 
 | ||||
| @ -153,7 +155,7 @@ namespace nana | ||||
| 		typedef treebox::item_proxy item_proxy; | ||||
| 	public: | ||||
| 
 | ||||
| 		filebox_implement(window owner, mode dialog_mode, const std::string& title, bool pick_directory = false): | ||||
| 		filebox_implement(window owner, mode dialog_mode, const std::string& title, bool pick_directory, bool allow_multi_select): | ||||
| 			form(owner, API::make_center(owner, 630, 440)), | ||||
| 			pick_directory_(pick_directory), | ||||
| 			mode_(dialog_mode) | ||||
| @ -239,11 +241,42 @@ namespace nana | ||||
| 			ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_TYPE"), 80); | ||||
| 			ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_SIZE"), 70); | ||||
| 
 | ||||
| 			auto fn_sel_file = [this](const arg_mouse& arg){ | ||||
| 				_m_select_file(arg); | ||||
| 
 | ||||
| 			auto fn_list_handler = [this](const arg_mouse& arg){ | ||||
| 				if(event_code::mouse_down == arg.evt_code) | ||||
| 				{ | ||||
| 					selection_.is_deselect_delayed = true; | ||||
| 				} | ||||
| 				else if(event_code::mouse_up == arg.evt_code) | ||||
| 				{ | ||||
| 					selection_.is_deselect_delayed = false; | ||||
| #if 0 | ||||
| 					this->_m_delay_deselect(true); | ||||
| #endif | ||||
| 					if(_m_sync_with_selection()) | ||||
| 						_m_display_target_filenames(); | ||||
| 				} | ||||
| 				else if(event_code::mouse_move == arg.evt_code) | ||||
| 				{ | ||||
| 					if(arg.left_button) | ||||
| 					{ | ||||
| 						selection_.is_deselect_delayed = false; | ||||
| 						std::cout<<"MouseMove set is_deselect_delayed = true"<<std::endl; | ||||
| 					} | ||||
| 				} | ||||
| 				else if(event_code::dbl_click == arg.evt_code) | ||||
| 					_m_click_select_file(arg); | ||||
| 			}; | ||||
| 			ls_file_.events().dbl_click.connect_unignorable(fn_sel_file); | ||||
| 			ls_file_.events().mouse_down.connect_unignorable(fn_sel_file); | ||||
| 
 | ||||
| 			ls_file_.events().dbl_click.connect_unignorable(fn_list_handler); | ||||
| 			ls_file_.events().mouse_down.connect_unignorable(fn_list_handler); | ||||
| 			ls_file_.events().mouse_up.connect_unignorable(fn_list_handler); | ||||
| 			ls_file_.events().mouse_move.connect_unignorable(fn_list_handler); | ||||
| 
 | ||||
| 			ls_file_.events().selected.connect_unignorable([this](const arg_listbox& arg){ | ||||
| 				_m_select_file(arg.item); | ||||
| 			}); | ||||
| 
 | ||||
| 			ls_file_.set_sort_compare(0, [](const std::string& a, nana::any* fs_a, const std::string& b, nana::any* fs_b, bool reverse) -> bool | ||||
| 				{ | ||||
| 					int dira = any_cast<item_fs>(fs_a)->directory ? 1 : 0; | ||||
| @ -327,7 +360,7 @@ namespace nana | ||||
| 			tb_file_.events().key_char.connect_unignorable([this](const arg_keyboard& arg) | ||||
| 			{ | ||||
| 				if(arg.key == nana::keyboard::enter) | ||||
| 					_m_ok(); | ||||
| 					_m_try_select(tb_file_.caption()); | ||||
| 			}); | ||||
| 
 | ||||
| 			//Don't create the combox for choose a file extension if the dialog is used for picking a directory.
 | ||||
| @ -341,11 +374,11 @@ namespace nana | ||||
| 			btn_ok_.create(*this); | ||||
| 			btn_ok_.i18n(i18n_eval("NANA_BUTTON_OK_SHORTKEY")); | ||||
| 
 | ||||
| 
 | ||||
| 			btn_ok_.events().click.connect_unignorable([this](const arg_click&) | ||||
| 			{ | ||||
| 				_m_ok(); | ||||
| 				_m_try_select(tb_file_.caption()); | ||||
| 			}); | ||||
| 
 | ||||
| 			btn_cancel_.create(*this); | ||||
| 			btn_cancel_.i18n(i18n_eval("NANA_BUTTON_CANCEL_SHORTKEY")); | ||||
| 
 | ||||
| @ -377,6 +410,10 @@ namespace nana | ||||
| 			} | ||||
| 			else | ||||
| 				caption(title); | ||||
| 
 | ||||
| 
 | ||||
| 			if(!allow_multi_select) | ||||
| 				ls_file_.enable_single(true, true); | ||||
| 		} | ||||
| 
 | ||||
| 		void def_extension(const std::string& ext) | ||||
| @ -392,7 +429,7 @@ namespace nana | ||||
| 			std::string dir; | ||||
| 
 | ||||
| 			auto pos = init_file.find_last_of("\\/"); | ||||
| 			auto file_with_path_removed = (pos != init_file.npos ? init_file.substr(pos + 1) : init_file); | ||||
| 			auto filename = (pos != init_file.npos ? init_file.substr(pos + 1) : init_file); | ||||
| 
 | ||||
| 			if(saved_init_path != init_path) | ||||
| 			{ | ||||
| @ -400,7 +437,7 @@ namespace nana | ||||
| 					saved_init_path = init_path; | ||||
| 
 | ||||
| 				//Phase 2: Check whether init_file contains a path
 | ||||
| 				if(file_with_path_removed == init_file) | ||||
| 				if(filename == init_file) | ||||
| 				{ | ||||
| 					//Phase 3: Check whether init_path is empty
 | ||||
| 					if(init_path.size()) | ||||
| @ -414,7 +451,7 @@ namespace nana | ||||
| 
 | ||||
| 			_m_load_cat_path(dir.size() ? dir : fs_ext::path_user().native()); | ||||
| 
 | ||||
| 			tb_file_.caption(file_with_path_removed); | ||||
| 			tb_file_.caption(filename); | ||||
| 		} | ||||
| 
 | ||||
| 		void add_filter(const std::string& desc, const std::string& type) | ||||
| @ -450,6 +487,7 @@ namespace nana | ||||
| 				cb_types_.anyobj(i, v); | ||||
| 		} | ||||
| 
 | ||||
| #if 0 | ||||
| 		bool file(std::string& fs) const | ||||
| 		{ | ||||
| 			if(selection_.type == kind::none) | ||||
| @ -464,6 +502,20 @@ namespace nana | ||||
| 			fs = selection_.target; | ||||
| 			return true; | ||||
| 		} | ||||
| #endif | ||||
| 		std::vector<std::string> files() const | ||||
| 		{ | ||||
| 			if(kind::none == selection_.type) | ||||
| 				return {}; | ||||
| 
 | ||||
| 			auto pos = selection_.targets.front().find_last_of("\\/"); | ||||
| 			if(pos != selection_.targets.front().npos) | ||||
| 				saved_selected_path = selection_.targets.front().substr(0, pos); | ||||
| 			else | ||||
| 				saved_selected_path.clear(); | ||||
| 
 | ||||
| 			return selection_.targets; | ||||
| 		} | ||||
| 	private: | ||||
| 		void _m_layout() | ||||
| 		{ | ||||
| @ -704,7 +756,7 @@ namespace nana | ||||
| 			if(pick_directory_) | ||||
| 				return is_dir; | ||||
| 
 | ||||
| 			if((is_dir || 0 == extension) || (0 == extension->size())) return true; | ||||
| 			if(is_dir || (nullptr == extension) || extension->empty()) return true; | ||||
| 
 | ||||
| 			for(auto & extstr : *extension) | ||||
| 			{ | ||||
| @ -799,9 +851,8 @@ namespace nana | ||||
| 			ls_file_.auto_draw(true); | ||||
| 		} | ||||
| 
 | ||||
| 		void _m_finish(kind::t type, const std::string& tar) | ||||
| 		void _m_finish(kind::t type) | ||||
| 		{ | ||||
| 			selection_.target = tar; | ||||
| 			selection_.type = type; | ||||
| 			close(); | ||||
| 		} | ||||
| @ -881,40 +932,346 @@ namespace nana | ||||
| 			return true; | ||||
| 		} | ||||
| 	private: | ||||
| 		void _m_select_file(const arg_mouse& arg) | ||||
| 		void _m_insert_filename(const std::string& name) | ||||
| 		{ | ||||
| 			auto sel = ls_file_.selected(); | ||||
| 			if(sel.empty()) | ||||
| 			if(selection_.targets.cend() == std::find(selection_.targets.cbegin(), selection_.targets.cend(), name)) | ||||
| 				selection_.targets.push_back(name); | ||||
| 		} | ||||
| 
 | ||||
| 		bool _m_hovered_good() const | ||||
| 		{ | ||||
| 			auto pos = ls_file_.hovered(false); | ||||
| 			if(!pos.empty()) | ||||
| 			{ | ||||
| 				item_fs mfs; | ||||
| 				ls_file_.at(pos).resolve_to(mfs); | ||||
| 				return !mfs.directory; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		bool _m_has_good_select() const | ||||
| 		{ | ||||
| 			auto selected_items = ls_file_.selected(); | ||||
| 			if(selected_items.size()) | ||||
| 			{ | ||||
| 				for(auto & pos : selected_items) | ||||
| 				{ | ||||
| 					item_fs mfs; | ||||
| 					ls_file_.at(pos).resolve_to(mfs); | ||||
| 
 | ||||
| 					if((mode::open_directory == mode_) || (false == mfs.directory)) | ||||
| 						return true; | ||||
| 				} | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		bool _m_sync_with_selection() | ||||
| 		{ | ||||
| 			if(_m_has_good_select()) | ||||
| 			{ | ||||
| 				auto selected_items = ls_file_.selected(); | ||||
| 				if(selected_items.size() && (selected_items.size() < selection_.targets.size())) | ||||
| 				{ | ||||
| 					selection_.targets.clear(); | ||||
| 					for(auto & pos : selected_items) | ||||
| 					{ | ||||
| 						item_fs mfs; | ||||
| 						ls_file_.at(pos).resolve_to(mfs); | ||||
| 
 | ||||
| 						if((mode::open_directory == mode_) || (false == mfs.directory)) | ||||
| 							selection_.targets.push_back(mfs.name); | ||||
| 					} | ||||
| 					return true; | ||||
| 				} | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		void _m_click_select_file(const arg_mouse& arg) | ||||
| 		{ | ||||
| 			auto selected_item = ls_file_.hovered(false); | ||||
| 			if(selected_item.empty()) | ||||
| 				return; | ||||
| 
 | ||||
| 			auto index = sel[0]; | ||||
| 			item_fs m; | ||||
| 			ls_file_.at(index).resolve_to(m); | ||||
| 			ls_file_.at(selected_item).resolve_to(m); | ||||
| 
 | ||||
| 			if(event_code::dbl_click == arg.evt_code) | ||||
| 			{ | ||||
| 				if(m.directory) | ||||
| 					_m_load_cat_path(addr_.filesystem + m.name + "/"); | ||||
| 				if(!m.directory) | ||||
| 					_m_try_select(m.name); | ||||
| 				else | ||||
| 					_m_finish(kind::filesystem, addr_.filesystem + m.name); | ||||
| 					_m_load_cat_path(addr_.filesystem + m.name + "/"); | ||||
| 			} | ||||
| #if 0 | ||||
| 			else | ||||
| 			{ | ||||
| 				if((mode::open_directory == mode_) || (false == m.directory)) | ||||
| 				{ | ||||
| 					selection_.target = addr_.filesystem + m.name; | ||||
| 					tb_file_.caption(m.name); | ||||
| 					if(ls_file_.is_single_enabled(true) || !arg.ctrl) | ||||
| 					{ | ||||
| 						std::cout<<"assign by click select "<<m.name<<std::endl; | ||||
| 						selection_.targets = {m.name}; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						std::cout<<"insert by click select "<<m.name<<std::endl; | ||||
| 						_m_insert_filename(m.name); | ||||
| 					} | ||||
| 					_m_display_target_filenames(); | ||||
| 				} | ||||
| 			} | ||||
| #endif | ||||
| 		} | ||||
| 
 | ||||
| #if 0 | ||||
| 		void _m_delay_deselect(bool only_clear_register) | ||||
| 		{ | ||||
| 			if(!only_clear_register) | ||||
| 			{ | ||||
| 				std::string filename_string; | ||||
| 				for(auto i = selection_.targets.cbegin(); i != selection_.targets.cend();) | ||||
| 				{ | ||||
| 					std::filesystem::path p{*i}; | ||||
| 
 | ||||
| 					auto u = std::find(selection_.delay_deselect.cbegin(), selection_.delay_deselect.cend(), p.filename().u8string()); | ||||
| 					if(u == selection_.delay_deselect.cend()) | ||||
| 					{ | ||||
| 						++i; | ||||
| 						continue; | ||||
| 					} | ||||
| 					i = selection_.targets.erase(i);				 | ||||
| 				} | ||||
| 				 | ||||
| 				_m_display_target_filenames(); | ||||
| 			} | ||||
| 
 | ||||
| 			selection_.delay_deselect.clear();		 | ||||
| 		} | ||||
| #endif | ||||
| 
 | ||||
| 		void _m_select_file(const listbox::item_proxy& item) | ||||
| 		{ | ||||
| 			item_fs mfs; | ||||
| 			ls_file_.at(item.pos()).resolve_to(mfs); | ||||
| 
 | ||||
| 			if(item.selected()) | ||||
| 			{ | ||||
| 				if((mode::open_directory == mode_) || (false == mfs.directory)) | ||||
| 				{ | ||||
| 					if(ls_file_.selected().size() < 2) | ||||
| 						selection_.targets.clear(); | ||||
| 
 | ||||
| 					std::cout<<"insert by select file:"<<mfs.name<<std::endl; | ||||
| 					_m_insert_filename(mfs.name); | ||||
| 				} | ||||
| 			} | ||||
| 			else if(_m_hovered_good() || _m_has_good_select()) | ||||
| 			{ | ||||
| 				for(auto i = selection_.targets.cbegin(); i != selection_.targets.cend();) | ||||
| 				{ | ||||
| 					std::filesystem::path p{*i}; | ||||
| 					if(p.filename().u8string() == mfs.name) | ||||
| 					{ | ||||
| 						std::cout<<"_m_select_file delay deselect = "<<selection_.is_deselect_delayed<<std::endl; | ||||
| 						if(!selection_.is_deselect_delayed) | ||||
| 						{ | ||||
| 							i = selection_.targets.erase(i); | ||||
| 							continue; | ||||
| 						} | ||||
| 						 | ||||
| 		#if 0 | ||||
| 						selection_.delay_deselect.push_back(mfs.name); | ||||
| 		#endif | ||||
| 					} | ||||
| 					++i; | ||||
| 				} | ||||
| 			} | ||||
| 			_m_display_target_filenames(); | ||||
| 		} | ||||
| 
 | ||||
| 		void _m_display_target_filenames() | ||||
| 		{ | ||||
| 			std::string filename_string; | ||||
| 			if(selection_.targets.size() == 1) | ||||
| 			{ | ||||
| 				filename_string = selection_.targets.front(); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				for(auto i = selection_.targets.crbegin(); i != selection_.targets.crend(); ++i) | ||||
| 				{ | ||||
| 					std::filesystem::path p{*i}; | ||||
| 					if(!filename_string.empty()) | ||||
| 						filename_string += ' '; | ||||
| 
 | ||||
| 					filename_string += "\"" + p.filename().u8string() + "\""; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			tb_file_.caption(filename_string); | ||||
| 		} | ||||
| 
 | ||||
| 		std::vector<std::string> _m_strip_files(const std::string& text) | ||||
| 		{ | ||||
| 			std::vector<std::string> files; | ||||
| 			std::size_t start_pos = 0; | ||||
| 			while(true) | ||||
| 			{ | ||||
| 				while(true) | ||||
| 				{ | ||||
| 					auto pos = text.find_first_of(" \"", start_pos); | ||||
| 					if(text.npos == pos) | ||||
| 					{ | ||||
| 						if(text.length() == start_pos) | ||||
| 							return files; | ||||
| 
 | ||||
| 						return {}; | ||||
| 					} | ||||
| 
 | ||||
| 					start_pos = pos + 1; | ||||
| 
 | ||||
| 					if(text[pos] == '"') | ||||
| 						break; | ||||
| 				} | ||||
| 
 | ||||
| 				auto pos = text.find('"', start_pos); | ||||
| 				if(text.npos == pos) | ||||
| 					return {}; | ||||
| 
 | ||||
| 				if(pos - start_pos > 0) | ||||
| 					files.push_back(text.substr(start_pos, pos - start_pos)); | ||||
| 
 | ||||
| 				start_pos = pos + 1; | ||||
| 			} | ||||
| 
 | ||||
| 			return files; | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 		void _m_try_select(const std::string& files_text) | ||||
| 		{ | ||||
| 			selection_.targets.clear(); | ||||
| 			//auto file = tb_file_.caption();
 | ||||
| 			auto targets = _m_strip_files(files_text); | ||||
| #if 1	//debug
 | ||||
| 			std::cout<<"filebox OK: file="<<files_text<<std::endl; | ||||
| 			for(auto t : targets) | ||||
| 				std::cout<<"filebox OK stripped:"<<t<<std::endl; | ||||
| #endif | ||||
| 			if(targets.empty()) | ||||
| 			{ | ||||
| 				//No file is selected
 | ||||
| 				if(files_text.empty()) | ||||
| 					return; | ||||
| 
 | ||||
| 				targets.push_back(files_text); | ||||
| 			} | ||||
| 
 | ||||
| 			internationalization i18n; | ||||
| 
 | ||||
| 			//the position of tar in targets
 | ||||
| 			int tar_idx = -1; | ||||
| 
 | ||||
| 			//Check whether the selected files are valid.
 | ||||
| 			for(auto tar : targets) | ||||
| 			{ | ||||
| 				++tar_idx; | ||||
| 				if(tar[0] == '.') | ||||
| 				{ | ||||
| 					msgbox mb(*this, caption()); | ||||
| 					mb.icon(msgbox::icon_warning); | ||||
| 					mb<<tar<<std::endl<<i18n("NANA_FILEBOX_ERROR_INVALID_FILENAME"); | ||||
| 					mb(); | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
| 				if(tar[0] != '/') | ||||
| 					tar = addr_.filesystem + tar; | ||||
| 
 | ||||
| 				auto fattr = fs::status(tar); | ||||
| 				auto ftype = static_cast<fs::file_type>(fattr.type()); | ||||
| 
 | ||||
| 				//Check if the selected name is a directory
 | ||||
| 				auto is_dir = fs::is_directory(fattr); | ||||
| 
 | ||||
| 				if(!is_dir && _m_append_def_extension(tar)) | ||||
| 				{ | ||||
| 					//Add the extension, then check if it is a directory again.
 | ||||
| 					fattr = fs::status(tar); | ||||
| 					ftype = static_cast<fs::file_type>(fattr.type()); | ||||
| 					is_dir = fs::is_directory(fattr); | ||||
| 				} | ||||
| 
 | ||||
| 				if(is_dir) | ||||
| 				{ | ||||
| 					//Enter the directory if this is the first tar.
 | ||||
| 					if(0 == tar_idx) | ||||
| 					{ | ||||
| 						_m_load_cat_path(tar); | ||||
| 						tb_file_.caption(std::string{}); | ||||
| 						return; | ||||
| 					} | ||||
| 
 | ||||
| 					//Other folders are ignored
 | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
| 				if(mode::write_file != mode_) | ||||
| 				{ | ||||
| 					if(fs::file_type::not_found == ftype) | ||||
| 					{ | ||||
| 						msgbox mb(*this, caption()); | ||||
| 						mb.icon(msgbox::icon_information); | ||||
| 						if(mode::open_file == mode_) | ||||
| 							mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); | ||||
| 						else | ||||
| 							mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); | ||||
| 						mb(); | ||||
| 
 | ||||
| 						return; | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					if(fs::file_type::not_found != ftype) | ||||
| 					{ | ||||
| 						msgbox mb(*this, caption(), msgbox::yes_no); | ||||
| 						mb.icon(msgbox::icon_question); | ||||
| 						mb<<i18n("NANA_FILEBOX_ERROR_QUERY_REWRITE_BECAUSE_OF_EXISTING"); | ||||
| 						if(msgbox::pick_no == mb()) | ||||
| 							return; | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				selection_.targets.push_back(tar); | ||||
| 			} | ||||
| 
 | ||||
| 			_m_finish(kind::filesystem);			 | ||||
| 		} | ||||
| 
 | ||||
| #if 0 | ||||
| 		void _m_ok() | ||||
| 		{ | ||||
| 			std::string tar = selection_.target; | ||||
| 			selection_.targets.clear(); | ||||
| 			auto file = tb_file_.caption(); | ||||
| 			auto targets = _m_strip_files(file); | ||||
| #if 1	//debug
 | ||||
| 			std::cout<<"filebox OK: file="<<file<<std::endl; | ||||
| 			for(auto t : targets) | ||||
| 				std::cout<<"filebox OK stripped:"<<t<<std::endl; | ||||
| #endif | ||||
| 
 | ||||
| 			if(selection_.target.empty()) | ||||
| 			//No file is selected
 | ||||
| 			if(targets.empty()) | ||||
| 				return; | ||||
| 
 | ||||
| #if 0 | ||||
| 			std::string tar; | ||||
| 			if(selection_.targets.empty()) | ||||
| 			{ | ||||
| 				auto file = tb_file_.caption(); | ||||
| 				if(file.size()) | ||||
| 				{ | ||||
| 					internationalization i18n; | ||||
| @ -959,7 +1316,7 @@ namespace nana | ||||
| 						{ | ||||
| 							msgbox mb(*this, caption()); | ||||
| 							mb.icon(msgbox::icon_information); | ||||
| 							if(mode::open_file != mode_) | ||||
| 							if(mode::open_file == mode_) | ||||
| 								mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); | ||||
| 							else | ||||
| 								mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); | ||||
| @ -979,11 +1336,94 @@ namespace nana | ||||
| 								return; | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					selection_.targets = {tar}; | ||||
| 				} | ||||
| 			} | ||||
| #else | ||||
| 			internationalization i18n; | ||||
| 
 | ||||
| 			_m_finish(kind::filesystem, tar); | ||||
| 			//the position of tar in targets
 | ||||
| 			int tar_idx = -1; | ||||
| 
 | ||||
| 			//Check whether the selected files are valid.
 | ||||
| 			for(auto tar : targets) | ||||
| 			{ | ||||
| 				++tar_idx; | ||||
| 				if(tar[0] == '.') | ||||
| 				{ | ||||
| 					msgbox mb(*this, caption()); | ||||
| 					mb.icon(msgbox::icon_warning); | ||||
| 					mb<<file<<std::endl<<i18n("NANA_FILEBOX_ERROR_INVALID_FILENAME"); | ||||
| 					mb(); | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
| 				if(tar[0] != '/') | ||||
| 					tar = addr_.filesystem + tar; | ||||
| 
 | ||||
| 				auto fattr = fs::status(tar); | ||||
| 				auto ftype = static_cast<fs::file_type>(fattr.type()); | ||||
| 
 | ||||
| 				//Check if the selected name is a directory
 | ||||
| 				auto is_dir = fs::is_directory(fattr); | ||||
| 
 | ||||
| 				if(!is_dir && _m_append_def_extension(tar)) | ||||
| 				{ | ||||
| 					//Add the extension, then check if it is a directory again.
 | ||||
| 					fattr = fs::status(tar); | ||||
| 					ftype = static_cast<fs::file_type>(fattr.type()); | ||||
| 					is_dir = fs::is_directory(fattr); | ||||
| 				} | ||||
| 
 | ||||
| 				if(is_dir) | ||||
| 				{ | ||||
| 					//Enter the directory if this is the first tar.
 | ||||
| 					if(0 == tar_idx) | ||||
| 					{ | ||||
| 						_m_load_cat_path(tar); | ||||
| 						tb_file_.caption(std::string{}); | ||||
| 						return; | ||||
| 					} | ||||
| 
 | ||||
| 					//Other folders are ignored
 | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
| 				if(mode::write_file != mode_) | ||||
| 				{ | ||||
| 					if(fs::file_type::not_found == ftype) | ||||
| 					{ | ||||
| 						msgbox mb(*this, caption()); | ||||
| 						mb.icon(msgbox::icon_information); | ||||
| 						if(mode::open_file == mode_) | ||||
| 							mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); | ||||
| 						else | ||||
| 							mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); | ||||
| 						mb(); | ||||
| 
 | ||||
| 						return; | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					if(fs::file_type::not_found != ftype) | ||||
| 					{ | ||||
| 						msgbox mb(*this, caption(), msgbox::yes_no); | ||||
| 						mb.icon(msgbox::icon_question); | ||||
| 						mb<<i18n("NANA_FILEBOX_ERROR_QUERY_REWRITE_BECAUSE_OF_EXISTING"); | ||||
| 						if(msgbox::pick_no == mb()) | ||||
| 							return; | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				selection_.targets.push_back(tar); | ||||
| 			} | ||||
| #endif | ||||
| 
 | ||||
| 			_m_finish(kind::filesystem); | ||||
| 		} | ||||
| #endif | ||||
| 
 | ||||
| 		void _m_tr_expand(item_proxy node, bool exp) | ||||
| 		{ | ||||
| @ -1064,7 +1504,11 @@ namespace nana | ||||
| 		struct selection_rep | ||||
| 		{ | ||||
| 			kind::t type; | ||||
| 			std::string target; | ||||
| 			std::vector<std::string> targets; | ||||
| #if 0 | ||||
| 			std::vector<std::string> delay_deselect; | ||||
| #endif | ||||
| 			bool is_deselect_delayed{ false }; | ||||
| 		}selection_; | ||||
| 
 | ||||
| 		static std::string saved_init_path; | ||||
| @ -1099,12 +1543,9 @@ namespace nana | ||||
| 		window owner; | ||||
| 		bool open_or_save; | ||||
| 
 | ||||
| #if defined(NANA_WINDOWS) | ||||
| 		bool allow_multi_select; | ||||
| 		std::vector<std::string> files; | ||||
| #else | ||||
| 		std::string file; | ||||
| #endif | ||||
| 
 | ||||
| 		std::string title; | ||||
| 		std::string path; | ||||
| 		std::vector<filter> filters; | ||||
| @ -1120,8 +1561,8 @@ namespace nana | ||||
| 	{ | ||||
| 		impl_->owner = owner; | ||||
| 		impl_->open_or_save = open; | ||||
| #if defined(NANA_WINDOWS) | ||||
| 		impl_->allow_multi_select = false; | ||||
| #if defined(NANA_WINDOWS) | ||||
| 		auto len = ::GetCurrentDirectory(0, nullptr); | ||||
| 		if(len) | ||||
| 		{ | ||||
| @ -1178,11 +1619,7 @@ namespace nana | ||||
| 
 | ||||
| 	filebox& filebox::init_file(const std::string& ifstr) | ||||
| 	{ | ||||
| #if defined(NANA_WINDOWS) | ||||
| 		impl_->files = {ifstr}; | ||||
| #else | ||||
| 		impl_->file = ifstr; | ||||
| #endif | ||||
| 		return *this; | ||||
| 	} | ||||
| 
 | ||||
| @ -1198,30 +1635,18 @@ namespace nana | ||||
| 		return impl_->path; | ||||
| 	} | ||||
| 
 | ||||
| #if defined(NANA_WINDOWS) | ||||
| 	const std::string& filebox::file() const | ||||
| 	std::string filebox::file() const | ||||
| 	{ | ||||
| 		if(impl_->files.empty()) | ||||
| 		{ | ||||
| 			static const std::string empty = ""; | ||||
| 			return empty; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return impl_->files.front(); | ||||
| 		} | ||||
| 			return {}; | ||||
| 
 | ||||
| 		return impl_->files.front(); | ||||
| 	} | ||||
| 
 | ||||
| 	const std::vector<std::string>& filebox::files() const | ||||
| 	{ | ||||
| 		return impl_->files; | ||||
| 	} | ||||
| #else | ||||
| 	const std::string& filebox::file() const | ||||
| 	{ | ||||
| 		return impl_->file; | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	bool filebox::show() const | ||||
| 	{ | ||||
| @ -1332,7 +1757,7 @@ namespace nana | ||||
| 
 | ||||
| #elif defined(NANA_POSIX) | ||||
| 		using mode = filebox_implement::mode; | ||||
| 		filebox_implement fb(impl_->owner, (impl_->open_or_save ? mode::open_file : mode::write_file), impl_->title); | ||||
| 		filebox_implement fb(impl_->owner, (impl_->open_or_save ? mode::open_file : mode::write_file), impl_->title, false, impl_->allow_multi_select); | ||||
| 
 | ||||
| 		if(impl_->filters.size()) | ||||
| 		{ | ||||
| @ -1353,27 +1778,28 @@ namespace nana | ||||
| 		else | ||||
| 			fb.add_filter("All Files", "*.*"); | ||||
| 
 | ||||
| 		fb.load_fs(impl_->path, impl_->file); | ||||
| 		fb.load_fs(impl_->path, this->file()); | ||||
| 
 | ||||
| 		API::modal_window(fb); | ||||
| 		if(false == fb.file(impl_->file)) | ||||
| 
 | ||||
| 		fb.files().swap(impl_->files); | ||||
| 		if(impl_->files.empty()) | ||||
| 			return false; | ||||
| 		auto tpos = impl_->file.find_last_of("\\/"); | ||||
| 		if(tpos != impl_->file.npos) | ||||
| 			impl_->path = impl_->file.substr(0, tpos); | ||||
| 
 | ||||
| 		auto tpos = impl_->files.front().find_last_of("\\/"); | ||||
| 		if(tpos != impl_->files.front().npos) | ||||
| 			impl_->path = impl_->files.front().substr(0, tpos); | ||||
| 		else | ||||
| 			impl_->path.clear(); | ||||
| #endif | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 	 | ||||
| #if defined(NANA_WINDOWS) | ||||
| 
 | ||||
| 	void filebox::allow_multi_select(bool allow) | ||||
| 	{ | ||||
| 		impl_->allow_multi_select = allow; | ||||
| 	} | ||||
| #endif | ||||
| 	//end class filebox
 | ||||
| 
 | ||||
| 	//class directory picker
 | ||||
| @ -1484,17 +1910,18 @@ namespace nana | ||||
| 
 | ||||
| #elif defined(NANA_POSIX) | ||||
| 		using mode = filebox_implement::mode; | ||||
| 		filebox_implement fb(impl_->owner, mode::open_directory, {}, true); | ||||
| 		filebox_implement fb(impl_->owner, mode::open_directory, {}, true, false/*single select*/); | ||||
| 
 | ||||
| 		fb.load_fs(impl_->init_path, ""); | ||||
| 
 | ||||
| 		API::modal_window(fb); | ||||
| 
 | ||||
| 		std::string path_directory; | ||||
| 		if(false == fb.file(path_directory)) | ||||
| 		auto path_dir = fb.files(); | ||||
| 		if(path_dir.empty()) | ||||
| 			return {}; | ||||
| 
 | ||||
| 		return path_type{path_directory}; | ||||
| 		return path_type{path_dir.front()}; | ||||
| #endif | ||||
| 	} | ||||
| }//end namespace nana
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  *	A CheckBox Implementation | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0. | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -216,6 +216,7 @@ namespace nana{ namespace drawerbase | ||||
| 			{ | ||||
| 				e.uiobj->radio(false); | ||||
| 				e.uiobj->react(true); | ||||
| 				API::umake_event(e.eh_clicked); | ||||
| 				API::umake_event(e.eh_checked); | ||||
| 				API::umake_event(e.eh_destroy); | ||||
| 				API::umake_event(e.eh_keyboard); | ||||
| @ -232,7 +233,7 @@ namespace nana{ namespace drawerbase | ||||
| 
 | ||||
| 			el.uiobj = &uiobj; | ||||
| 
 | ||||
| 			uiobj.events().checked.connect_unignorable([this](const arg_checkbox& arg) | ||||
| 			el.eh_checked = uiobj.events().checked.connect_unignorable([this](const arg_checkbox& arg) | ||||
| 			{ | ||||
| 				if (arg.widget->checked()) | ||||
| 				{ | ||||
| @ -244,7 +245,7 @@ namespace nana{ namespace drawerbase | ||||
| 				} | ||||
| 			}, true); | ||||
| 
 | ||||
| 			el.eh_checked = uiobj.events().click.connect_unignorable([this](const arg_click& arg) | ||||
| 			el.eh_clicked = uiobj.events().click.connect_unignorable([this](const arg_click& arg) | ||||
| 			{ | ||||
| 				for (auto & i : ui_container_) | ||||
| 					i.uiobj->check(arg.window_handle == i.uiobj->handle()); | ||||
|  | ||||
| @ -27,10 +27,11 @@ | ||||
| 	if(empty())	\ | ||||
| 		throw std::logic_error("the group is invalid"); | ||||
| 
 | ||||
| namespace nana{ | ||||
| namespace nana | ||||
| { | ||||
| 
 | ||||
| 	static const char* field_title = "__nana_group_title__"; | ||||
| 	static const char* field_options = "__nana_group_options__"; | ||||
| static const char* field_title = "__nana_group_title__"; | ||||
| static const char* field_options = "__nana_group_options__"; | ||||
| 
 | ||||
| 	struct group::implement | ||||
| 	{ | ||||
| @ -41,108 +42,109 @@ namespace nana{ | ||||
| 		unsigned gap{2}; | ||||
| 		std::string usr_div_str; | ||||
| 
 | ||||
| 		nana::size caption_dimension; | ||||
|     nana::size caption_dimension; | ||||
| 
 | ||||
| 		std::vector<std::unique_ptr<checkbox>> options; | ||||
| 		radio_group * radio_logic{nullptr}; | ||||
|     std::vector<std::unique_ptr<checkbox>> options; | ||||
|     radio_group * radio_logic{nullptr}; | ||||
| 
 | ||||
| 		implement() = default; | ||||
|     implement() = default; | ||||
| 
 | ||||
| 		implement(window grp_panel, ::std::string titel, bool vsb, unsigned gap=2) | ||||
| 			: caption (grp_panel, std::move(titel), vsb), | ||||
| 			  place_content{grp_panel}, | ||||
| 			  gap{gap} | ||||
| 		{ | ||||
| 		} | ||||
|     implement(window grp_panel, ::std::string titel, bool vsb, unsigned gap=2) | ||||
|         : caption (grp_panel, std::move(titel), vsb), | ||||
|           place_content{grp_panel}, | ||||
|           gap{gap} | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
| 		void create(window pnl) | ||||
| 		{ | ||||
| 			caption.create(pnl); | ||||
| 			caption.caption(""); | ||||
| 			place_content.bind(pnl); | ||||
|     void create(window pnl) | ||||
|     { | ||||
|         caption.create(pnl); | ||||
|         caption.caption(""); | ||||
|         place_content.bind(pnl); | ||||
| 
 | ||||
| 			if (!radio_logic) | ||||
| 				radio_logic = new radio_group; | ||||
| 		} | ||||
|         if (!radio_logic) | ||||
|             radio_logic = new radio_group; | ||||
|     } | ||||
| 
 | ||||
| 		void update_div() | ||||
| 		{ | ||||
| 			const std::size_t padding = 10; | ||||
| 			caption_dimension = caption.measure(1000); | ||||
| 			caption_dimension.width += 1; | ||||
|     void update_div() | ||||
|     { | ||||
|         const std::size_t padding = 10; | ||||
|         caption_dimension = caption.measure(1000); | ||||
|         caption_dimension.width += 1; | ||||
| 
 | ||||
| 			std::string div = "vert margin=[0," + std::to_string(gap) + "," + std::to_string(gap + 5) + "," + std::to_string(gap) + "]"; | ||||
|         std::string div = "vert margin=[0," + std::to_string(gap) + "," + std::to_string(gap + 5) + "," + std::to_string(gap) + "]"; | ||||
| 
 | ||||
| 			div += "<weight=" + std::to_string(caption_dimension.height) + " "; | ||||
|         div += "<weight=" + std::to_string(caption_dimension.height) + " "; | ||||
| 
 | ||||
| 			if (align::left == caption_align) | ||||
| 				div += "<weight=" + std::to_string(padding) + ">"; | ||||
| 			else | ||||
| 				div += "<>";	//right or center
 | ||||
|         if (align::left == caption_align) | ||||
|             div += "<weight=" + std::to_string(padding) + ">"; | ||||
|         else | ||||
|             div += "<>";	//right or center
 | ||||
| 
 | ||||
| 			div += "<" + std::string{ field_title } + " weight=" + std::to_string(caption_dimension.width) + ">"; | ||||
|         div += "<" + std::string{ field_title } + " weight=" + std::to_string(caption_dimension.width) + ">"; | ||||
| 
 | ||||
| 			if (align::right == caption_align) | ||||
| 				div += "<weight=" + std::to_string(padding) + ">"; | ||||
| 			else if (align::center == caption_align) | ||||
| 				div += "<>"; | ||||
|         if (align::right == caption_align) | ||||
|             div += "<weight=" + std::to_string(padding) + ">"; | ||||
|         else if (align::center == caption_align) | ||||
|             div += "<>"; | ||||
| 
 | ||||
| 			div += "><<vert margin=5 " + std::string(field_options) + ">"; | ||||
|         div += "><<vert margin=5 " + std::string(field_options) + ">"; | ||||
| 
 | ||||
| 			if (!usr_div_str.empty()) | ||||
| 				div += "<" + usr_div_str + ">>"; | ||||
| 			else | ||||
| 				div += ">"; | ||||
|         if (!usr_div_str.empty()) | ||||
|             div += "<" + usr_div_str + ">>"; | ||||
|         else | ||||
|             div += ">"; | ||||
| 
 | ||||
| 			place_content.div(div.c_str()); | ||||
|         place_content.div(div.c_str()); | ||||
| 
 | ||||
| 			if (options.empty()) | ||||
| 				place_content.field_display(field_options, false); | ||||
|         if (options.empty()) | ||||
|             place_content.field_display(field_options, false); | ||||
| 
 | ||||
| 			if (caption.caption().empty()) | ||||
| 				place_content.field_display(field_title, false); | ||||
| 		} | ||||
| 	}; | ||||
|         if (caption.caption().empty()) | ||||
|             place_content.field_display(field_title, false); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 	group::group() | ||||
| 		: impl_(new implement) | ||||
| 	{ | ||||
| 	} | ||||
| group::group() | ||||
|     : impl_(new implement) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| 	group::group(window parent, const rectangle& r, bool vsb) | ||||
| 		: group() | ||||
| 	{ | ||||
| 		create(parent, r, vsb); | ||||
| 	} | ||||
| group::group(window parent, const rectangle& r, bool vsb) | ||||
|     : group() | ||||
| { | ||||
|     create(parent, r, vsb); | ||||
| } | ||||
| 
 | ||||
| 	using groupbase_type = widget_object<category::widget_tag, drawerbase::panel::drawer, general_events, drawerbase::group::scheme>; | ||||
| using groupbase_type = widget_object<category::widget_tag, drawerbase::panel::drawer, general_events, drawerbase::group::scheme>; | ||||
| 
 | ||||
| 	group::group(window parent, ::std::string titel, bool formatted, unsigned  gap, const rectangle& r, bool vsb) | ||||
| 		: group(parent, r, vsb) | ||||
| 	{ | ||||
| 		this->bgcolor(API::bgcolor(parent)); | ||||
| group::group(window parent, ::std::string titel, bool formatted, unsigned  gap, const rectangle& r, bool vsb) | ||||
|     : group(parent, r, vsb) | ||||
| { | ||||
|     this->bgcolor(API::bgcolor(parent)); | ||||
| 
 | ||||
| 		impl_.reset(new implement(*this, std::move(titel), vsb, gap)); | ||||
|     impl_.reset(new implement(*this, std::move(titel), vsb, gap)); | ||||
| 
 | ||||
| 		impl_->caption.format(formatted); | ||||
| 		_m_init(); | ||||
| 	} | ||||
|     impl_->caption.format(formatted); | ||||
|     _m_init(); | ||||
| } | ||||
| 
 | ||||
| 	group::~group() | ||||
| 	{ | ||||
| 		delete impl_->radio_logic; | ||||
| 	} | ||||
| group::~group() | ||||
| { | ||||
|     delete impl_->radio_logic; | ||||
| } | ||||
| 
 | ||||
| 	checkbox& group::add_option(std::string text) | ||||
| 	{ | ||||
| 		_THROW_IF_EMPTY() | ||||
| checkbox& group::add_option(std::string text) | ||||
| { | ||||
|     _THROW_IF_EMPTY() | ||||
| 
 | ||||
| #ifdef _nana_std_has_emplace_return_type | ||||
| 		auto & opt = impl_->options.emplace_back(new checkbox{ handle() }); | ||||
|     auto & opt = impl_->options.emplace_back(new checkbox { handle() }); | ||||
| #else | ||||
| 		impl_->options.emplace_back(new checkbox(handle())); | ||||
| 		auto & opt = impl_->options.back(); | ||||
|     impl_->options.emplace_back(new checkbox(handle())); | ||||
|     auto & opt = impl_->options.back(); | ||||
| #endif | ||||
| 
 | ||||
| 		opt->transparent(true); | ||||
| 		opt->caption(std::move(text)); | ||||
| 		impl_->place_content[field_options] << *opt; | ||||
| @ -358,5 +360,6 @@ namespace nana{ | ||||
| 		impl_->update_div(); | ||||
| 		impl_->place_content.collocate(); | ||||
| 	} | ||||
| 
 | ||||
| }//end namespace nana
 | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  *	A List Box Implementation | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0. | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -17,6 +17,12 @@ | ||||
|  *		dankan1890(pr#158) | ||||
|  * | ||||
|  */ | ||||
| #include <algorithm> | ||||
| #include <list> | ||||
| #include <deque> | ||||
| #include <stdexcept> | ||||
| #include <map> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include <nana/gui/widgets/listbox.hpp> | ||||
| #include <nana/gui/widgets/panel.hpp>	//for inline widget
 | ||||
| @ -28,12 +34,6 @@ | ||||
| #include <nana/system/platform.hpp> | ||||
| #include "skeletons/content_view.hpp" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <list> | ||||
| #include <deque> | ||||
| #include <stdexcept> | ||||
| #include <map> | ||||
| 
 | ||||
| namespace nana | ||||
| { | ||||
| 	static void check_range(std::size_t pos, std::size_t size) | ||||
| @ -130,17 +130,18 @@ namespace nana | ||||
| 				struct column | ||||
| 					: public column_interface | ||||
| 				{ | ||||
| 					native_string_type caption; | ||||
| 					unsigned width_px; | ||||
| 					std::pair<unsigned, unsigned> range_width_px; | ||||
| 					native_string_type caption;                     //< header title
 | ||||
| 					unsigned width_px;                              //< column width in pixels
 | ||||
| 					std::pair<unsigned, unsigned> range_width_px;   //< allowed width
 | ||||
| 					bool visible_state{ true }; | ||||
| 
 | ||||
| 					/// Absolute position of column when it was creating
 | ||||
| 					size_type index; | ||||
| 
 | ||||
| 					size_type index;                          //< Absolute position of column when it was created
 | ||||
| 
 | ||||
| 					nana::align alignment{ nana::align::left }; | ||||
| 
 | ||||
| 					std::function<bool(const std::string&, nana::any*, const std::string&, nana::any*, bool reverse)> weak_ordering; | ||||
| 					std::function<bool(const std::string&,  nana::any*, | ||||
| 							           const std::string&,  nana::any*,        bool reverse)> weak_ordering; | ||||
| 
 | ||||
| 					std::shared_ptr<paint::font> font;	///< The exclusive column font
 | ||||
| 
 | ||||
| @ -186,18 +187,18 @@ namespace nana | ||||
| 					{ | ||||
| 					} | ||||
| 				private: | ||||
| 					//The definition is provided after essence
 | ||||
| 					/// The definition is provided after essence
 | ||||
| 					void _m_refresh() noexcept; | ||||
| 				private: | ||||
| 					essence* const ess_; | ||||
| 				public: | ||||
| 					//Implementation of column_interface
 | ||||
| 					/// Implementation of column_interface
 | ||||
| 					unsigned width() const noexcept override | ||||
| 					{ | ||||
| 						return width_px; | ||||
| 					} | ||||
| 
 | ||||
| 					// Sets the width and overrides the ranged width
 | ||||
| 					/// Sets the width and overrides the ranged width
 | ||||
| 					void width(unsigned pixels) noexcept override | ||||
| 					{ | ||||
| 						width_px = pixels; | ||||
| @ -223,7 +224,7 @@ namespace nana | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					size_type position(bool disp_order) const noexcept override;	//The definition is provided after essence
 | ||||
| 					size_type position(bool disp_order) const noexcept override;	//< The definition is provided after essence
 | ||||
| 
 | ||||
| 					std::string text() const noexcept override | ||||
| 					{ | ||||
| @ -440,7 +441,7 @@ namespace nana | ||||
| 					throw std::invalid_argument("listbox: invalid header index"); | ||||
| 				} | ||||
| 
 | ||||
|                 /// find and return a ref to the column that originaly was at position "pos" previous to any list reorganization.
 | ||||
|                 /// find and return a ref to the column that originally was at position "pos" previous to any list reorganization.
 | ||||
| 				column& at(size_type pos, bool disp_order = false) | ||||
| 				{ | ||||
| 					check_range(pos, cont_.size()); | ||||
| @ -507,7 +508,7 @@ namespace nana | ||||
| 					return{ left, 0 }; | ||||
| 				} | ||||
| 
 | ||||
| 				/// return the original index of the visible col currently before(in front of) or after the col originaly at index "index"
 | ||||
| 				/// return the original index of the visible col currently before(in front of) or after the col originally at index "index"
 | ||||
| 				size_type next(size_type index) const noexcept | ||||
| 				{ | ||||
| 					bool found_me = false; | ||||
| @ -542,7 +543,25 @@ namespace nana | ||||
| 					return pos; | ||||
| 				} | ||||
|                  | ||||
| 				/// move the col originaly at "from" to the position currently in front (or after) the col originaly at index "to" invalidating some current index
 | ||||
| 
 | ||||
| 				/// move col to view pos
 | ||||
| 				void move_to_view_pos (size_type col, size_type view, bool front) noexcept | ||||
| 				{ | ||||
| 					if (!front) view++; | ||||
| 					if (view >= cont_.size() )		return; | ||||
| 
 | ||||
| 					auto i = std::find_if( cont_.begin(), | ||||
| 							               cont_.end(), | ||||
| 							               [&](const column& c){return col==c.index;}); | ||||
| 
 | ||||
| 					if (i==cont_.end()) return; | ||||
| 
 | ||||
| 					auto col_from = *i; | ||||
| 					cont_.erase(i); | ||||
| 					cont_.insert(cont_.begin()+ view, col_from); | ||||
| 
 | ||||
| 				} | ||||
| 				/// move the col originally at "from" to the position currently in front (or after) the col originally at index "to" invalidating some current index
 | ||||
| 				void move(size_type from, size_type to, bool front) noexcept | ||||
| 				{ | ||||
| 					if ((from == to) || (from >= cont_.size()) || (to >= cont_.size())) | ||||
| @ -1850,6 +1869,11 @@ namespace nana | ||||
| 					(for_selection ? single_selection_ : single_check_) = false; | ||||
| 				} | ||||
| 
 | ||||
| 				bool is_single_enabled(bool for_selection) const noexcept | ||||
| 				{ | ||||
| 					return (for_selection ? single_selection_ : single_check_); | ||||
| 				} | ||||
| 
 | ||||
| 				size_type size_item(size_type cat) const | ||||
| 				{ | ||||
| 					return get(cat)->items.size(); | ||||
| @ -2006,6 +2030,8 @@ namespace nana | ||||
| 				{ | ||||
| 					bool	started{ false }; | ||||
| 					bool	reverse_selection{ false }; | ||||
| 					bool	scroll_direction; | ||||
| 					bool	deselect_when_start_to_move; | ||||
| 
 | ||||
| 					point	screen_pos; | ||||
| 					point	begin_position;	///< Logical position to the 
 | ||||
| @ -2013,9 +2039,18 @@ namespace nana | ||||
| 					index_pairs already_selected; | ||||
| 					index_pairs selections; | ||||
| 
 | ||||
| 					bool scroll_direction; | ||||
| 					unsigned scroll_step{ 1 }; | ||||
| 					unsigned mouse_move_timestamp{ 0 }; | ||||
| 
 | ||||
| 					bool is_already_selected(const index_pair& abs_pos) const noexcept | ||||
| 					{ | ||||
| 						return (already_selected.cend() != std::find(already_selected.cbegin(), already_selected.cend(), abs_pos)); | ||||
| 					} | ||||
| 
 | ||||
| 					bool is_selected(const index_pair& abs_pos) const noexcept | ||||
| 					{ | ||||
| 						return (selections.cend() != std::find(selections.cbegin(), selections.cend(), abs_pos)); | ||||
| 					} | ||||
| 				}mouse_selection; | ||||
| 
 | ||||
| 
 | ||||
| @ -2113,6 +2148,7 @@ namespace nana | ||||
| 					mouse_selection.started = true; | ||||
| 					mouse_selection.begin_position = logic_pos; | ||||
| 					mouse_selection.end_position = logic_pos; | ||||
| 					mouse_selection.deselect_when_start_to_move = true; | ||||
| 
 | ||||
| 					if (arg.ctrl || arg.shift) | ||||
| 					{ | ||||
| @ -2128,6 +2164,17 @@ namespace nana | ||||
| 					if (!mouse_selection.started) | ||||
| 						return; | ||||
| 
 | ||||
| 					// When the button is pressed and start to move the mouse, the listbox should deselect all items.
 | ||||
| 					// But when ctrl is clicked
 | ||||
| 					if (mouse_selection.deselect_when_start_to_move) | ||||
| 					{ | ||||
| 						mouse_selection.deselect_when_start_to_move = false; | ||||
| 						if (mouse_selection.already_selected.empty()) | ||||
| 							lister.select_for_all(false); | ||||
| 
 | ||||
| 						mouse_selection.selections.clear(); | ||||
| 					} | ||||
| 
 | ||||
| 					mouse_selection.screen_pos = screen_pos; | ||||
| 
 | ||||
| 					auto logic_pos = coordinate_cast(screen_pos, true); | ||||
| @ -2143,7 +2190,7 @@ namespace nana | ||||
| 
 | ||||
| 					mouse_selection.end_position = logic_pos; | ||||
| 
 | ||||
| 					bool cancel_selections = true; | ||||
| 					std::vector<std::pair<index_pair, bool>> selections; | ||||
| 
 | ||||
| 					auto content_x = coordinate_cast({ columns_range().first, 0 }, true).x; | ||||
| 					if ((std::max)(mouse_selection.end_position.x, mouse_selection.begin_position.x) >= content_x && | ||||
| @ -2154,18 +2201,18 @@ namespace nana | ||||
| 						auto begin = lister.advance(lister.first(), begin_off); | ||||
| 						if (!begin.empty()) | ||||
| 						{ | ||||
| 							std::vector<std::pair<index_pair, bool>> selections; | ||||
| 
 | ||||
| 							if ((mouse_selection.end_position.y < 0) || (lister.distance(lister.first(), begin) == begin_off)) | ||||
| 							{ | ||||
| 								//The range [begin_off, last_off] is a range of box selection
 | ||||
| 								auto last_off = (std::max)(mouse_selection.begin_position.y, mouse_selection.end_position.y) / item_height(); | ||||
| 								auto last = lister.advance(lister.first(), last_off); | ||||
| 
 | ||||
| 								//Tries to select the items in the box, then returns the items with their previous selected states
 | ||||
| 								selections = lister.select_display_range_if(begin, last, false, [this](const index_pair& abs_pos) { | ||||
| 									if (this->mouse_selection.reverse_selection) | ||||
| 									if (mouse_selection.reverse_selection) | ||||
| 									{ | ||||
| 										if (mouse_selection.already_selected.cend() != std::find(mouse_selection.already_selected.cbegin(), mouse_selection.already_selected.cend(), abs_pos)) | ||||
| 										//Deselects the items in the box which has been already selected
 | ||||
| 										if(mouse_selection.is_already_selected(abs_pos)) | ||||
| 										{ | ||||
| 											item_proxy{ this, abs_pos }.select(false); | ||||
| 											return false; | ||||
| @ -2176,21 +2223,21 @@ namespace nana | ||||
| 
 | ||||
| 								for (auto & pair : selections) | ||||
| 								{ | ||||
| 									//Continue if the previous state is selected. It indicates the item now is not selected.
 | ||||
| 									if (pair.second) | ||||
| 										continue; | ||||
| 
 | ||||
| 									if (mouse_selection.selections.cend() == | ||||
| 										std::find(mouse_selection.selections.cbegin(), mouse_selection.selections.cend(), pair.first)) | ||||
| 									{ | ||||
| 									//Add the item to selections container.
 | ||||
| 									if(!mouse_selection.is_selected(pair.first)) | ||||
| 										mouse_selection.selections.push_back(pair.first); | ||||
| 									} | ||||
| 								} | ||||
| 
 | ||||
| 								//Deselects the items which are in mouse_selection.selections but not in selections.
 | ||||
| 								//Eq to mouse_selection.selections = selections
 | ||||
| #ifdef _MSC_VER | ||||
| 								for (auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();) | ||||
| #else | ||||
| 								for(auto i = mouse_selection.selections.begin(); i != mouse_selection.selections.end();) | ||||
| 
 | ||||
| #endif | ||||
| 								{ | ||||
| 									auto & selpos = *i; | ||||
| @ -2204,30 +2251,45 @@ namespace nana | ||||
| 									else | ||||
| 										++i; | ||||
| 								} | ||||
| 
 | ||||
| 								cancel_selections = false; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					if (cancel_selections) | ||||
| 					//Restores an already selected item if it is not in selections.
 | ||||
| 					for (auto& abs_pos : mouse_selection.already_selected) | ||||
| 					{ | ||||
| 						if (!mouse_selection.already_selected.empty()) | ||||
| 						if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), [abs_pos](const std::pair<index_pair, bool>& rhs) { | ||||
| 							return (abs_pos == rhs.first); | ||||
| 							})) | ||||
| 						{ | ||||
| 							for (auto & pos : mouse_selection.selections) | ||||
| 								item_proxy(this, pos).select(false); | ||||
| 
 | ||||
| 							//Don't restore the already selections if it is reverse selection(pressing shift). Behaves like Windows Explorer.
 | ||||
| 							if (!mouse_selection.reverse_selection) | ||||
| 							item_proxy m{ this, abs_pos }; | ||||
| 							if (!m.selected()) | ||||
| 							{ | ||||
| 								for (auto & abs_pos : mouse_selection.already_selected) | ||||
| 									item_proxy(this, abs_pos).select(true); | ||||
| 								m.select(true); | ||||
| 								//Add the item to selections container.
 | ||||
| 								if(!mouse_selection.is_selected(abs_pos)) | ||||
| 									mouse_selection.selections.push_back(abs_pos); | ||||
| 							} | ||||
| 						} | ||||
| 						else | ||||
| 							lister.select_for_all(false); | ||||
| 					} | ||||
| 
 | ||||
| 						mouse_selection.selections.clear(); | ||||
| 					//Deselects the item which is not in already_selected and selections but in mouse_selection.selections
 | ||||
| 					for(auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();) | ||||
| 					{ | ||||
| 						auto abs_pos = *i; | ||||
| 
 | ||||
| 						bool is_box_selected = (selections.cend() != std::find_if(selections.cbegin(), selections.cend(), [abs_pos](const std::pair<index_pair, bool>& rhs) { | ||||
| 							return (abs_pos == rhs.first); | ||||
| 							})); | ||||
| 
 | ||||
| 						if (is_box_selected || mouse_selection.is_already_selected(abs_pos)) | ||||
| 						{ | ||||
| 							++i; | ||||
| 							continue; | ||||
| 						} | ||||
| 
 | ||||
| 						item_proxy{ this, abs_pos }.select(false); | ||||
| 						i = mouse_selection.selections.erase(i); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| @ -4110,11 +4172,13 @@ namespace nana | ||||
| 					essence_->draw_peripheral(); | ||||
| 				} | ||||
| 
 | ||||
| 				// In mouse move event, it cancels the msup_deselect if the listbox is draggable or it started the mouse selection.
 | ||||
| 				void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) | ||||
| 				{ | ||||
| 					using item_state = essence::item_state; | ||||
| 					using parts = essence::parts; | ||||
| 
 | ||||
| 					//Don't deselect the items if the listbox is draggable
 | ||||
| 					if ((operation_states::msup_deselect == essence_->operation.state) && API::dev::window_draggable(arg.window_handle)) | ||||
| 						essence_->operation.state = operation_states::none; | ||||
| 
 | ||||
| @ -4168,6 +4232,9 @@ namespace nana | ||||
| 					if (essence_->mouse_selection.started) | ||||
| 					{ | ||||
| 						essence_->update_mouse_selection(arg.pos); | ||||
| 
 | ||||
| 						//Don't deselect items if the mouse selection is started
 | ||||
| 						essence_->operation.state = operation_states::none; | ||||
| 						need_refresh = true; | ||||
| 					} | ||||
| 
 | ||||
| @ -4364,7 +4431,7 @@ namespace nana | ||||
| 						if (arg.is_left_button() && (!lister.single_status(true))) | ||||
| 							essence_->start_mouse_selection(arg); | ||||
| 
 | ||||
| 						//Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed
 | ||||
| 						//Deselecting 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.
 | ||||
| 						if (!(arg.ctrl || arg.shift)) | ||||
| 						{ | ||||
| @ -4411,7 +4478,7 @@ namespace nana | ||||
| 						essence_->stop_mouse_selection(); | ||||
| 						need_refresh = true; | ||||
| 					} | ||||
| 
 | ||||
| 					 | ||||
| 					if (operation_states::msup_deselect == essence_->operation.state) | ||||
| 					{ | ||||
| 						essence_->operation.state = operation_states::none; | ||||
| @ -5954,9 +6021,16 @@ namespace nana | ||||
| 
 | ||||
| 		void listbox::disable_single(bool for_selection) | ||||
| 		{ | ||||
| 			internal_scope_guard lock; | ||||
| 			_m_ess().lister.disable_single(for_selection); | ||||
| 		} | ||||
| 
 | ||||
| 		bool listbox::is_single_enabled(bool for_selection) const noexcept | ||||
| 		{ | ||||
| 			internal_scope_guard lock; | ||||
| 			return _m_ess().lister.is_single_enabled(for_selection); | ||||
| 		} | ||||
| 
 | ||||
|         listbox::export_options& listbox::def_export_options() | ||||
|         { | ||||
| 			return _m_ess().def_exp_options; | ||||
| @ -6097,5 +6171,49 @@ namespace nana | ||||
| 			internal_scope_guard lock; | ||||
| 			return _m_ess().content_view->scroll_operation(); | ||||
| 		} | ||||
| 
 | ||||
| 		/// Move column to view_position
 | ||||
| 		void listbox::move_column(size_type abs_pos, size_type view_pos) | ||||
| 		{ | ||||
| 			internal_scope_guard lock; | ||||
| 			return _m_ess().header.move_to_view_pos(abs_pos, view_pos, true); | ||||
| 			_m_ess().update(); | ||||
| 		} | ||||
| 
 | ||||
| 		/// Sort columns in range first_col to last_col inclusive using a row
 | ||||
| 		void listbox::reorder_columns(size_type first_col, | ||||
| 									  size_type last_col, | ||||
| 									  index_pair row, bool reverse, | ||||
| 									  std::function<bool(const std::string &cell1, size_type col1, | ||||
| 														 const std::string &cell2, size_type col2, | ||||
| 														 const nana::any *rowval, | ||||
| 														 bool reverse)> comp) | ||||
| 		{ | ||||
| 			if (first_col<0 || last_col<=first_col) | ||||
| 				return; | ||||
| 			if (last_col >= column_size()) | ||||
| 				return; | ||||
| 			std::vector<size_type> new_idx; | ||||
| 			for(size_type i=first_col; i<=last_col; ++i) new_idx.push_back(i); | ||||
| 			 | ||||
| 			internal_scope_guard lock; | ||||
| 			auto ip_row = this->at(row); | ||||
| 			auto pnany=_m_ess().lister.anyobj(row,false); | ||||
| 			std::sort(new_idx.begin(), new_idx.end(), [&](size_type col1, | ||||
| 														  size_type col2) | ||||
| 			{ | ||||
| 				return comp(ip_row.text(col1), col1, | ||||
| 						    ip_row.text(col2), col2, | ||||
| 						    pnany, reverse); | ||||
| 			}); | ||||
| 
 | ||||
| 			//Only change the view position of columns
 | ||||
| 			for(size_t i=0; i<new_idx.size(); ++i) | ||||
| 			{ | ||||
| 				move_column(new_idx[i],i+first_col); | ||||
| 			} | ||||
| 			_m_ess().update(); | ||||
| 		} | ||||
| 
 | ||||
| 	//end class listbox
 | ||||
| }//end namespace nana
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
| *	A text editor implementation | ||||
| *	Nana C++ Library(http://www.nanapro.org)
 | ||||
| *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
| *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
| * | ||||
| *	Distributed under the Boost Software License, Version 1.0. | ||||
| *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -1728,7 +1728,7 @@ namespace nana { | ||||
| 					str = impl_->textbase.getline(0); | ||||
| 					for (std::size_t i = 1; i < lines; ++i) | ||||
| 					{ | ||||
| 						str += L"\n\r"; | ||||
| 						str += L"\r\n"; | ||||
| 						str += impl_->textbase.getline(i); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| @ -245,7 +245,7 @@ namespace nana | ||||
| 				}; | ||||
| 			public: | ||||
| 				enum class parts{none, bar, slider}; | ||||
| 				 | ||||
| 
 | ||||
| 				using graph_reference = drawer_trigger::graph_reference; | ||||
| 
 | ||||
| 				model() | ||||
| @ -256,7 +256,7 @@ namespace nana | ||||
| 					proto_.renderer = pat::cloneable<renderer_interface>{interior_renderer{}}; | ||||
| 
 | ||||
| 					attr_.seek_dir = seekdir::bilateral; | ||||
| 					 | ||||
| 
 | ||||
| 					attr_.is_draw_adorn = false; | ||||
| 					attr_.vcur = 0; | ||||
| 					attr_.vmax = 10; | ||||
| @ -361,7 +361,7 @@ namespace nana | ||||
| 				parts seek_where(::nana::point pos) const | ||||
| 				{ | ||||
| 					nana::rectangle r = _m_bar_area(); | ||||
| 					 | ||||
| 
 | ||||
| 					if (attr_.slider.vert) | ||||
| 					{ | ||||
| 						std::swap(pos.x, pos.y); | ||||
| @ -373,7 +373,7 @@ namespace nana | ||||
| 						return parts::slider; | ||||
| 
 | ||||
| 					sdpos = static_cast<int>(attr_.slider.weight) / 2; | ||||
| 					 | ||||
| 
 | ||||
| 					if (sdpos <= pos.x && pos.x < sdpos + static_cast<int>(r.width)) | ||||
| 					{ | ||||
| 						if(pos.y < r.bottom()) | ||||
| @ -446,7 +446,7 @@ namespace nana | ||||
| 				bool move_slider(const ::nana::point& pos) | ||||
| 				{ | ||||
| 					int adorn_pos = slider_state_.snap_pos + (attr_.slider.vert ? pos.y : pos.x) - slider_state_.refpos.x; | ||||
| 					 | ||||
| 
 | ||||
| 					if (adorn_pos > 0) | ||||
| 					{ | ||||
| 						int range = static_cast<int>(_m_range()); | ||||
| @ -691,7 +691,7 @@ namespace nana | ||||
| 					window wd; | ||||
| 					nana::slider * widget; | ||||
| 				}other_; | ||||
| 				 | ||||
| 
 | ||||
| 				struct prototype_tag | ||||
| 				{ | ||||
| 					pat::cloneable<slider::renderer_interface> renderer; | ||||
| @ -759,9 +759,9 @@ namespace nana | ||||
| 				void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) | ||||
| 				{ | ||||
| 					// check if slider is disabled
 | ||||
| 					if(!API::get_widget(arg.window_handle)->enabled())  | ||||
| 					if(!API::get_widget(arg.window_handle)->enabled()) | ||||
| 						return;		// do nothing
 | ||||
| 					 | ||||
| 
 | ||||
| 					bool updated = false; | ||||
| 					if (model_ptr_->if_trace_slider()) | ||||
| 					{ | ||||
| @ -804,7 +804,7 @@ namespace nana | ||||
| 
 | ||||
| 	//class slider
 | ||||
| 		slider::slider(){} | ||||
| 		 | ||||
| 
 | ||||
| 		slider::slider(window wd, bool visible) | ||||
| 		{ | ||||
| 			create(wd, rectangle(), visible); | ||||
| @ -844,10 +844,14 @@ namespace nana | ||||
| 			return get_drawer_trigger().get_model()->attribute().vmax; | ||||
| 		} | ||||
| 
 | ||||
| 		void slider::value(unsigned v) | ||||
| 		void slider::value(int v) | ||||
| 		{ | ||||
| 			if(handle()) | ||||
| 			{ | ||||
| 			    // limit to positive values, vcur expects unsigned
 | ||||
| 			    if( v < 0 ) | ||||
|                     v = 0; | ||||
| 
 | ||||
| 				if(get_drawer_trigger().get_model()->vcur(v)) | ||||
| 					API::refresh_window(handle()); | ||||
| 			} | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  *	A Spin box widget | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0. | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -467,7 +467,7 @@ namespace nana | ||||
| 
 | ||||
| 					std::wstring text; | ||||
| 
 | ||||
| 					if (API::is_focus_ready(editor_->window_handle())) | ||||
| 					if (API::is_focus_ready(editor_->window_handle()) && editor_->attr().editable) | ||||
| 						text = to_wstring(range_->value()); | ||||
| 					else | ||||
| 						text = to_wstring(modifier_.prefix + range_->value() + modifier_.suffix); | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| /*
 | ||||
|  *	A Treebox Implementation | ||||
|  *	Nana C++ Library(http://www.nanapro.org)
 | ||||
|  *	Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) | ||||
|  *	Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) | ||||
|  * | ||||
|  *	Distributed under the Boost Software License, Version 1.0. | ||||
|  *	(See accompanying file LICENSE_1_0.txt or copy at | ||||
| @ -1330,6 +1330,10 @@ namespace nana | ||||
| 			class internal_placer | ||||
| 				: public compset_placer_interface | ||||
| 			{ | ||||
| 			public: | ||||
| 				internal_placer(const scheme& schm): | ||||
| 					scheme_(schm) | ||||
| 				{} | ||||
| 			private: | ||||
| 				//Implement the compset_locator_interface
 | ||||
| 
 | ||||
| @ -1364,17 +1368,24 @@ namespace nana | ||||
| 
 | ||||
| 				virtual unsigned item_height(graph_reference graph) const override | ||||
| 				{ | ||||
| 					auto m = std::max((enable_crook_ ? wdg_scheme_ptr_->crook_size : 0), (enable_icon_ ? wdg_scheme_ptr_->icon_size : 0)); | ||||
| 					auto m = std::max((enable_crook_ ? scheme_.crook_size : 0), (enable_icon_ ? scheme_.icon_size : 0)); | ||||
| 
 | ||||
| #if 1 | ||||
| 					unsigned as = 0, ds = 0, il; | ||||
| 					graph.text_metrics(as, ds, il); | ||||
| 					return std::max(as + ds + 8, m); | ||||
| #else | ||||
| #ifdef _nana_std_has_string_view | ||||
| 					return std::max(m, graph.text_extent_size(std::wstring_view{ L"jH{", 3 }).height + 8); | ||||
| #else | ||||
| 					return std::max(m, graph.text_extent_size(L"jH{", 3).height + 8); | ||||
| #endif | ||||
| #endif | ||||
| 				} | ||||
| 
 | ||||
| 				virtual unsigned item_width(graph_reference graph, const item_attribute_t& attr) const override | ||||
| 				{ | ||||
| 					return graph.text_extent_size(attr.text).width + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0) + (enable_icon_ ? wdg_scheme_ptr_ ->icon_size: 0) + (wdg_scheme_ptr_->text_offset << 1) + wdg_scheme_ptr_->item_offset; | ||||
| 					return graph.text_extent_size(attr.text).width + (enable_crook_ ? scheme_.crook_size : 0) + (enable_icon_ ? scheme_.icon_size : 0) + (scheme_.text_offset << 1) + scheme_.item_offset; | ||||
| 				} | ||||
| 
 | ||||
| 				// Locate a component through the specified coordinate.
 | ||||
| @ -1389,7 +1400,7 @@ namespace nana | ||||
| 					case component_t::expander: | ||||
| 						if(attr.has_children) | ||||
| 						{ | ||||
| 							r->width = wdg_scheme_ptr_->item_offset; | ||||
| 							r->width = scheme_.item_offset; | ||||
| 							return true; | ||||
| 						} | ||||
| 						return false; | ||||
| @ -1398,26 +1409,26 @@ namespace nana | ||||
| 					case component_t::crook: | ||||
| 						if(enable_crook_) | ||||
| 						{ | ||||
| 							r->x += wdg_scheme_ptr_->item_offset; | ||||
| 							r->width = wdg_scheme_ptr_->crook_size; | ||||
| 							r->x += scheme_.item_offset; | ||||
| 							r->width = scheme_.crook_size; | ||||
| 							return true; | ||||
| 						} | ||||
| 						return false; | ||||
| 					case component_t::icon: | ||||
| 						if(enable_icon_) | ||||
| 						{ | ||||
| 							r->x += wdg_scheme_ptr_->item_offset + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0); | ||||
| 							r->x += scheme_.item_offset + (enable_crook_ ? scheme_.crook_size : 0); | ||||
| 							r->y = 2; | ||||
| 							r->width = wdg_scheme_ptr_->icon_size; | ||||
| 							r->width = scheme_.icon_size; | ||||
| 							r->height -= 2; | ||||
| 							return true; | ||||
| 						} | ||||
| 						return false; | ||||
| 					case component_t::text: | ||||
| 						{ | ||||
| 							auto text_pos = wdg_scheme_ptr_->item_offset + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0) + (enable_icon_ ? wdg_scheme_ptr_->icon_size : 0) + wdg_scheme_ptr_->text_offset; | ||||
| 							auto text_pos = scheme_.item_offset + (enable_crook_ ? scheme_.crook_size : 0) + (enable_icon_ ? scheme_.icon_size : 0) + scheme_.text_offset; | ||||
| 							r->x += text_pos; | ||||
| 							r->width -= (text_pos + wdg_scheme_ptr_->text_offset); | ||||
| 							r->width -= (text_pos + scheme_.text_offset); | ||||
| 						}; | ||||
| 						return true; | ||||
| 					default: | ||||
| @ -1426,6 +1437,7 @@ namespace nana | ||||
| 					return false; | ||||
| 				} | ||||
| 			private: | ||||
| 				const scheme& scheme_; | ||||
| 				bool enable_crook_{ false }; | ||||
| 				bool enable_icon_{ false }; | ||||
| 			}; | ||||
| @ -1680,7 +1692,7 @@ namespace nana | ||||
| 				{ | ||||
| 					impl_->data.trigger_ptr = this; | ||||
| 					impl_->data.renderer = nana::pat::cloneable<renderer_interface>(internal_renderer()); | ||||
| 					impl_->data.comp_placer = nana::pat::cloneable<compset_placer_interface>(internal_placer()); | ||||
| 					//impl_->data.comp_placer = nana::pat::cloneable<compset_placer_interface>(internal_placer());	//deprecated
 | ||||
| 
 | ||||
| 					impl_->adjust.timer.elapse([this] | ||||
| 					{ | ||||
| @ -1946,13 +1958,16 @@ namespace nana | ||||
| 					widget.bgcolor(colors::white); | ||||
| 					impl_->data.widget_ptr = static_cast<::nana::treebox*>(&widget); | ||||
| 					impl_->data.scheme_ptr = static_cast<::nana::treebox::scheme_type*>(API::dev::get_scheme(widget)); | ||||
| 					impl_->data.comp_placer->init_scheme(impl_->data.scheme_ptr); | ||||
| 					//impl_->data.comp_placer->init_scheme(impl_->data.scheme_ptr);	//deprecated
 | ||||
| 					impl_->data.comp_placer = nana::pat::cloneable<compset_placer_interface>(internal_placer{ *impl_->data.scheme_ptr }); | ||||
| 
 | ||||
| 					widget.caption("nana treebox"); | ||||
| 				} | ||||
| 
 | ||||
| 				void trigger::detached() | ||||
| 				{ | ||||
| 					//Reset the comp_placer, because after deteching, the scheme refered by comp_placer will be released
 | ||||
| 					impl_->data.comp_placer.reset(); | ||||
| 					impl_->data.graph = nullptr; | ||||
| 				} | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 besh81
						besh81