diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index 61ebdfa2..b9c5bc84 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -16,7 +16,6 @@ #define NANA_GUI_FILEBOX_HPP #include #include -#include #include #include @@ -30,6 +29,7 @@ namespace nana filebox& operator=(filebox&&) = delete; public: using filters = std::vector>; + using path_type = std::filesystem::path; explicit filebox(bool is_open_mode); filebox(window owner, bool is_open_mode); @@ -69,16 +69,14 @@ namespace nana const ::std::string& path() const; - ::std::string file() const; - - const ::std::vector<::std::string>& files() const; + void allow_multi_select(bool allow); /// Display the filebox dialog - bool show() const; - + std::vector show() const; + /// a function object method alternative to show() to display the filebox dialog, - bool operator()() const + std::vector operator()() const { return show(); } @@ -100,9 +98,12 @@ namespace nana explicit folderbox(window owner = nullptr, const path_type& init_path = {}, std::string title={}); ~folderbox(); - std::optional show() const; + /// Enables/disables multi select + void allow_multi_select(bool allow); - std::optional operator()() const + std::vector show() const; + + std::vector operator()() const { return show(); } diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index b830b8e5..239f9971 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -184,7 +184,7 @@ namespace nana throw std::runtime_error("Nana.GUI.Filebox: Wrong categorize path"); if(path.size() == 0) path = "/"; /// \todo : use nana::filesystem_ext::def_rootstr? - _m_load_cat_path(path); + _m_enter_folder(path); }); @@ -260,7 +260,7 @@ namespace nana selection_.is_deselect_delayed = false; } else if(event_code::dbl_click == arg.evt_code) - _m_click_select_file(arg); + _m_list_dbl_clicked(); }; ls_file_.events().dbl_click.connect_unignorable(fn_list_handler); @@ -354,6 +354,7 @@ namespace nana tb_file_.events().key_char.connect_unignorable([this](const arg_keyboard& arg) { + allow_fall_back_ = false; if(arg.key == nana::keyboard::enter) _m_try_select(tb_file_.caption()); }); @@ -444,7 +445,7 @@ namespace nana else dir = saved_selected_path; - _m_load_cat_path(dir.size() ? dir : fs_ext::path_user().native()); + _m_enter_folder(dir.size() ? dir : fs_ext::path_user().native()); tb_file_.caption(filename); } @@ -570,7 +571,7 @@ namespace nana { auto path = tree_.make_key_path(arg.item, "/") + "/"; _m_resolute_path(path); - _m_load_cat_path(path); + _m_enter_folder(path); } }); } @@ -630,7 +631,7 @@ namespace nana std::sort(file_container_.begin(), file_container_.end(), pred_sort_fs()); } - void _m_load_cat_path(std::string path) + void _m_enter_folder(std::string path) { if((path.size() == 0) || (path[path.size() - 1] != '/')) path += '/'; @@ -763,7 +764,7 @@ namespace nana { auto m = cat.append(fs); m.value(fs); - + if(fs.directory) m.icon(images_.folder); else @@ -791,7 +792,7 @@ namespace nana { auto ext4 = filename.substr(size - 4); - if( (".exe" == ext4) || + if( (".exe" == ext4) || (".dll" == ext4)) use_image = images_.exec; else if((".zip" == ext4) || (".rar" == ext4) || @@ -818,7 +819,7 @@ namespace nana else if(".html" == ext5) use_image = images_.xml; } - + if(use_image.empty()) m.icon(images_.file); else @@ -875,7 +876,7 @@ namespace nana return; } - fb_._m_load_cat_path(fb_.addr_.filesystem); + fb_._m_enter_folder(fb_.addr_.filesystem); fm_.close(); } @@ -968,22 +969,28 @@ namespace nana return false; } - void _m_click_select_file(const arg_mouse& arg) + //pos must be valid + item_fs _m_item_fs(const listbox::index_pair& pos) const + { + item_fs m; + ls_file_.at(pos).resolve_to(m); + return m; + } + + void _m_list_dbl_clicked() { auto selected_item = ls_file_.hovered(false); if(selected_item.empty()) return; - item_fs m; - ls_file_.at(selected_item).resolve_to(m); - - if(event_code::dbl_click == arg.evt_code) + item_fs m = _m_item_fs(selected_item); + if(m.directory) { - if(!m.directory) - _m_try_select(m.name); - else - _m_load_cat_path(addr_.filesystem + m.name + "/"); + _m_enter_folder(addr_.filesystem + m.name + "/"); + allow_fall_back_ = true; } + else + _m_try_select(m.name); } void _m_select_file(const listbox::item_proxy& item) @@ -1076,19 +1083,27 @@ namespace nana } return files; - } + void _m_msgbox(const char* i18n_idstr, const std::string& arg) const + { + internationalization i18n; + + msgbox mb(*this, caption()); + mb.icon(msgbox::icon_warning); + mb< files; + std::string init_file; std::string title; std::string path; @@ -1367,7 +1411,7 @@ namespace nana filebox& filebox::init_file(const std::string& ifstr) { - impl_->files = {ifstr}; + impl_->init_file = ifstr; return *this; } @@ -1383,23 +1427,12 @@ namespace nana return impl_->path; } - std::string filebox::file() const + std::vector filebox::show() const { - if(impl_->files.empty()) - return {}; + std::vector targets; - return impl_->files.front(); - } - - const std::vector& filebox::files() const - { - return impl_->files; - } - - bool filebox::show() const - { #if defined(NANA_WINDOWS) - std::wstring wfile(impl_->files.empty() ? L"" : to_wstring(impl_->files.front())); + std::wstring wfile = to_wstring(impl_->init_file); wfile.resize(impl_->allow_multi_select ? (520 + 32*256) : 520); OPENFILENAME ofn; @@ -1473,34 +1506,31 @@ namespace nana { internal_revert_guard revert; if (FALSE == (impl_->open_or_save ? ::GetOpenFileName(&ofn) : ::GetSaveFileName(&ofn))) - return false; + return targets; } if(impl_->allow_multi_select) { - wchar_t* str = ofn.lpstrFile; - std::wstring dir = str; - str += (dir.length() + 1); - impl_->files.clear(); + const wchar_t* str = ofn.lpstrFile; + auto len = ::wcslen(str); + + path_type parent_path{ str }; + str += (len + 1); + while(*str) { - std::wstring filename = str; - std::wstring file_path = dir + L"\\" + filename; - impl_->files.emplace_back(to_utf8(file_path)); - str += (filename.length() + 1); + len = ::wcslen(str); + targets.emplace_back(parent_path / path_type{str}); + str += (len + 1); } - impl_->path = to_utf8(dir); + impl_->path = parent_path.u8string(); } else { wfile.resize(std::wcslen(wfile.data())); - auto file = to_utf8(wfile); - impl_->files = {file}; - auto tpos = file.find_last_of("\\/"); - if(tpos != file.npos) - impl_->path = file.substr(0, tpos); - else - impl_->path.clear(); + + targets.emplace_back(wfile); + impl_->path = targets.front().parent_path().u8string(); } #elif defined(NANA_POSIX) @@ -1526,23 +1556,23 @@ namespace nana else fb.add_filter("All Files", "*.*"); - fb.load_fs(impl_->path, this->file()); + fb.load_fs(impl_->path, impl_->init_file); API::modal_window(fb); - fb.files().swap(impl_->files); - if(impl_->files.empty()) - return false; - auto tpos = impl_->files.front().find_last_of("\\/"); - if(tpos != impl_->files.front().npos) - impl_->path = impl_->files.front().substr(0, tpos); + for(auto & f : fb.files()) + targets.emplace_back(f); + + + if(!targets.empty()) + impl_->path = targets.front().parent_path().u8string(); else impl_->path.clear(); #endif - return true; + return targets; } - + void filebox::allow_multi_select(bool allow) { @@ -1556,11 +1586,11 @@ namespace nana window owner; path_type init_path; std::string title; - + bool allow_multi_select; }; folderbox::folderbox(window owner, const path_type& init_path, std::string title) - : impl_(new implement{ owner, fs::canonical(init_path).make_preferred(), title }) + : impl_(new implement{ owner, fs::canonical(init_path).make_preferred(), title, false}) {} @@ -1596,14 +1626,19 @@ namespace nana } #endif - std::optional folderbox::show() const + void folderbox::allow_multi_select(bool allow) { + impl_->allow_multi_select = allow; + } + + std::vector folderbox::show() const + { + std::vector targets; #ifdef NANA_WINDOWS - std::optional target; nana::detail::platform_spec::co_initializer co_init; #ifndef NANA_MINGW - IFileDialog *fd(nullptr); + IFileOpenDialog *fd(nullptr); HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd)); if (SUCCEEDED(hr)) { @@ -1612,21 +1647,32 @@ namespace nana if (SUCCEEDED(hr)) fd->SetFolder(init_path); - fd->SetOptions(FOS_PICKFOLDERS); + fd->SetOptions(FOS_PICKFOLDERS | (impl_->allow_multi_select ? FOS_ALLOWMULTISELECT : 0)); fd->Show(reinterpret_cast(API::root(impl_->owner))); // the native handle of the parent nana form goes here - IShellItem *si; - hr = fd->GetResult(&si); // fails if user cancelled - if (SUCCEEDED(hr)) + + ::IShellItemArray *sia; + if (SUCCEEDED(fd->GetResults(&sia))) // fails if user cancelled { - PWSTR pwstr(nullptr); - hr = si->GetDisplayName(SIGDN_FILESYSPATH, &pwstr); - if (SUCCEEDED(hr)) + DWORD num_items; + if (SUCCEEDED(sia->GetCount(&num_items))) { - target = path_type{ pwstr }; - // use the c-string pointed to by pwstr here - CoTaskMemFree(pwstr); + for (DWORD i = 0; i < num_items; ++i) + { + ::IShellItem* si; + if (SUCCEEDED(sia->GetItemAt(i, &si))) + { + PWSTR pwstr(nullptr); + if (SUCCEEDED(si->GetDisplayName(SIGDN_FILESYSPATH, &pwstr))) + { + targets.emplace_back(pwstr); + // use the c-string pointed to by pwstr here + ::CoTaskMemFree(pwstr); + } + si->Release(); + } + } } - si->Release(); + sia->Release(); } fd->Release(); } @@ -1649,27 +1695,25 @@ namespace nana { wchar_t folder_path[MAX_PATH]; if (FALSE != SHGetPathFromIDList(pidl, folder_path)) - target = folder_path; + targets.emplace_back(folder_path); co_init.task_mem_free(pidl); } #endif - return target; #elif defined(NANA_POSIX) using mode = filebox_implement::mode; - filebox_implement fb(impl_->owner, mode::open_directory, {}, true, false/*single select*/); + filebox_implement fb(impl_->owner, mode::open_directory, {}, true, impl_->allow_multi_select); fb.load_fs(impl_->init_path, ""); API::modal_window(fb); - std::string path_directory; - auto path_dir = fb.files(); - if(path_dir.empty()) - return {}; + auto path_dirs = fb.files(); - return path_type{path_dir.front()}; + for(auto & p: path_dirs) + targets.push_back(p); #endif + return targets; } }//end namespace nana diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index caf6380b..7263aa84 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -1228,9 +1228,10 @@ namespace nana impl->browse.events().click.connect_unignorable([wd, impl](const arg_click&) { impl->fbox.owner(wd); - if (impl->fbox.show()) + auto files = impl->fbox.show(); + if(!files.empty()) { - impl->value = impl->fbox.file(); + impl->value = files.front().u8string(); impl->path_edit.caption(impl->value); } }); diff --git a/source/internationalization.cpp b/source/internationalization.cpp index d41b775c..ba3ffcd6 100644 --- a/source/internationalization.cpp +++ b/source/internationalization.cpp @@ -193,9 +193,10 @@ namespace nana table["NANA_FILEBOX_ERROR_INVALID_FOLDER_NAME"] = "Please input a valid name for the new folder."; table["NANA_FILEBOX_ERROR_RENAME_FOLDER_BECAUSE_OF_EXISTING"] = "The folder is existing, please rename it."; table["NANA_FILEBOX_ERROR_RENAME_FOLDER_BECAUSE_OF_FAILED_CREATION"] = "Failed to create the folder, please rename it."; - table["NANA_FILEBOX_ERROR_INVALID_FILENAME"] = "The filename is invalid."; + table["NANA_FILEBOX_ERROR_INVALID_FILENAME"] = "\"%arg0\"\nThe filename is invalid."; table["NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY"] = "The file \"%arg0\"\n is not existing. Please check and retry."; table["NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY"] = "The directory \"%arg0\"\n is not existing. Please check and retry."; + table["NANA_FILEBOX_ERROR_DIRECTORY_INVALID"] = "The directory \"%arg0\"\n is invalid. Please check and retry."; table["NANA_FILEBOX_ERROR_QUERY_REWRITE_BECAUSE_OF_EXISTING"] = "The input file is existing, do you want to overwrite it?"; } };