new folderbox class

This commit is contained in:
Jinhao 2018-06-20 01:09:56 +08:00
parent 69e6f4c4a4
commit c0836fbbec
2 changed files with 359 additions and 214 deletions

View File

@ -1,7 +1,7 @@
/** /**
* Filebox * Filebox
* 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
@ -15,6 +15,7 @@
#ifndef NANA_GUI_FILEBOX_HPP #ifndef NANA_GUI_FILEBOX_HPP
#define NANA_GUI_FILEBOX_HPP #define NANA_GUI_FILEBOX_HPP
#include <nana/gui/basis.hpp> #include <nana/gui/basis.hpp>
#include <nana/filesystem/filesystem.hpp>
#include <vector> #include <vector>
#include <utility> #include <utility>
@ -80,5 +81,29 @@ namespace nana
private: private:
implement * impl_; implement * impl_;
}; };
class folderbox
{
struct implement;
folderbox(const folderbox&) = delete;
folderbox& operator=(const folderbox&) = delete;
folderbox(folderbox&&) = delete;
folderbox& operator=(folderbox&&) = delete;
public:
using path_type = std::experimental::filesystem::path;
folderbox(window owner = nullptr, const path_type& init_path = {});
~folderbox();
std::optional<path_type> show() const;
std::optional<path_type> operator()() const
{
return show();
}
private:
implement* impl_;
};
}//end namespace nana }//end namespace nana
#endif #endif

View File

@ -1,7 +1,7 @@
/* /*
* Filebox * Filebox
* 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
@ -15,18 +15,19 @@
#include <nana/filesystem/filesystem_ext.hpp> #include <nana/filesystem/filesystem_ext.hpp>
#if defined(NANA_WINDOWS) #if defined(NANA_WINDOWS)
#include <windows.h> # include <windows.h>
# include <Shobjidl.h>
#elif defined(NANA_POSIX) #elif defined(NANA_POSIX)
#include <nana/gui/widgets/label.hpp> # include <nana/gui/widgets/label.hpp>
#include <nana/gui/widgets/button.hpp> # include <nana/gui/widgets/button.hpp>
#include <nana/gui/widgets/listbox.hpp> # include <nana/gui/widgets/listbox.hpp>
#include <nana/gui/widgets/categorize.hpp> # include <nana/gui/widgets/categorize.hpp>
#include <nana/gui/widgets/textbox.hpp> # include <nana/gui/widgets/textbox.hpp>
#include <nana/gui/widgets/treebox.hpp> # include <nana/gui/widgets/treebox.hpp>
#include <nana/gui/widgets/combox.hpp> # include <nana/gui/widgets/combox.hpp>
#include <nana/gui/place.hpp> # include <nana/gui/place.hpp>
#include <stdexcept> # include <stdexcept>
#include <algorithm> # include <algorithm>
#endif #endif
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
@ -130,6 +131,13 @@ namespace nana
} }
}; };
public: public:
enum class mode
{
open_file, //choose an existing file.
write_file, //choose a filename, it can be specified a new filename which doesn't exist.
open_directory, //choose an existing directory.
};
struct kind struct kind
{ {
enum t{none, filesystem}; enum t{none, filesystem};
@ -138,8 +146,10 @@ namespace nana
typedef treebox::item_proxy item_proxy; typedef treebox::item_proxy item_proxy;
public: public:
filebox_implement(window owner, bool io_read, const std::string& title) filebox_implement(window owner, mode dialog_mode, const std::string& title, bool pick_directory = false):
: form(owner, API::make_center(owner, 630, 440)), io_read_(io_read) form(owner, API::make_center(owner, 630, 440)),
pick_directory_(pick_directory),
mode_(dialog_mode)
{ {
internationalization i18n; internationalization i18n;
path_.create(*this); path_.create(*this);
@ -159,6 +169,7 @@ namespace nana
_m_load_cat_path(path); _m_load_cat_path(path);
}); });
filter_.create(*this); filter_.create(*this);
filter_.multi_lines(false); filter_.multi_lines(false);
filter_.tip_string(i18n("NANA_FILEBOX_FILTER")); filter_.tip_string(i18n("NANA_FILEBOX_FILTER"));
@ -203,7 +214,7 @@ namespace nana
ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_SIZE"), 70); ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_SIZE"), 70);
auto fn_sel_file = [this](const arg_mouse& arg){ auto fn_sel_file = [this](const arg_mouse& arg){
_m_sel_file(arg); _m_select_file(arg);
}; };
ls_file_.events().dbl_click.connect_unignorable(fn_sel_file); ls_file_.events().dbl_click.connect_unignorable(fn_sel_file);
ls_file_.events().mouse_down.connect_unignorable(fn_sel_file); ls_file_.events().mouse_down.connect_unignorable(fn_sel_file);
@ -278,7 +289,11 @@ namespace nana
}); });
lb_file_.create(*this); lb_file_.create(*this);
lb_file_.i18n(i18n_eval("NANA_FILEBOX_FILE_COLON"));
const char* idstr = (mode::open_directory == dialog_mode? "NANA_FILEBOX_DIRECTORY_COLON" : "NANA_FILEBOX_FILE_COLON");
lb_file_.i18n(i18n_eval(idstr));
lb_file_.text_align(align::right, align_v::center);
tb_file_.create(*this); tb_file_.create(*this);
tb_file_.multi_lines(false); tb_file_.multi_lines(false);
@ -289,9 +304,13 @@ namespace nana
_m_ok(); _m_ok();
}); });
cb_types_.create(*this); //Don't create the combox for choose a file extension if the dialog is used for picking a directory.
cb_types_.editable(false); if(!pick_directory)
cb_types_.events().selected.connect_unignorable([this](const arg_combox&){ _m_list_fs(); }); {
cb_types_.create(*this);
cb_types_.editable(false);
cb_types_.events().selected.connect_unignorable([this](const arg_combox&){ _m_list_fs(); });
}
btn_ok_.create(*this); btn_ok_.create(*this);
btn_ok_.i18n(i18n_eval("NANA_BUTTON_OK_SHORTKEY")); btn_ok_.i18n(i18n_eval("NANA_BUTTON_OK_SHORTKEY"));
@ -313,8 +332,23 @@ namespace nana
_m_layout(); _m_layout();
_m_init_tree(); _m_init_tree();
if(0 == title.size()) if(title.empty())
this->i18n(i18n_eval(io_read ? "NANA_FILEBOX_OPEN" : "NANA_FILEBOX_SAVE_AS")); {
const char* idstr{ nullptr };
switch(dialog_mode)
{
case mode::open_file:
idstr = "NANA_FILEBOX_OPEN";
break;
case mode::write_file:
idstr = "NANA_FILEBOX_SAVE_AS";
break;
case mode::open_directory:
idstr = "NANA_FILEBOX_OPEN_DIRECTORY";
break;
}
this->i18n(i18n_eval(idstr));
}
else else
caption(title); caption(title);
} }
@ -412,16 +446,21 @@ namespace nana
"<weight=34 margin=5 path arrange=[variable,200] gap=5>" "<weight=34 margin=5 path arrange=[variable,200] gap=5>"
"<weight=30 margin=[0,0,5,10] new_folder arrange=[100]>" "<weight=30 margin=[0,0,5,10] new_folder arrange=[100]>"
"<content arrange=[180] gap=[5]><weight=8>" "<content arrange=[180] gap=[5]><weight=8>"
"<weight=26<weight=100><vert weight=60 label margin=[0,0,0,5]>" "<weight=26<weight=100><vert weight=80 label margin=[0,5,0,0]>"
"<file margin=[0,18,0,5] arrange=[variable,variable,190] gap=[10]>>" "<file margin=[0,18,0,5] arrange=[variable,variable,190] gap=[10]>>"
"<weight=48 margin=[8,0,14]<>" "<weight=48 margin=[8,0,14]<>"
"<buttons weight=208 margin=[0,18,0] gap=[14]>>"); "<buttons weight=208 margin=[0,18,0] gap=[14]>>");
place_.field("path")<<path_<<filter_; place_.field("path")<<path_<<filter_;
place_.field("new_folder")<<btn_folder_; place_.field("new_folder")<<btn_folder_;
place_.field("content")<<tree_<<ls_file_; place_.field("content")<<tree_<<ls_file_;
place_.field("label")<<lb_file_; place_.field("label")<<lb_file_;
place_.field("file")<<tb_file_<<cb_types_; place_.field("file")<<tb_file_;
if(!pick_directory_)
place_.field("file")<<cb_types_;
place_.field("buttons")<<btn_ok_<<btn_cancel_; place_.field("buttons")<<btn_ok_<<btn_cancel_;
place_.collocate(); place_.collocate();
} }
@ -630,6 +669,9 @@ namespace nana
if(filter.size() && (name.find(filter) == filter.npos)) if(filter.size() && (name.find(filter) == filter.npos))
return false; return false;
if(pick_directory_)
return is_dir;
if((is_dir || 0 == extension) || (0 == extension->size())) return true; if((is_dir || 0 == extension) || (0 == extension->size())) return true;
for(auto & extstr : *extension) for(auto & extstr : *extension)
@ -744,7 +786,7 @@ namespace nana
return true; return true;
} }
private: private:
void _m_sel_file(const arg_mouse& arg) void _m_select_file(const arg_mouse& arg)
{ {
auto sel = ls_file_.selected(); auto sel = ls_file_.selected();
if(sel.empty()) if(sel.empty())
@ -763,7 +805,7 @@ namespace nana
} }
else else
{ {
if(false == m.directory) if((mode::open_directory == mode_) || (false == m.directory))
{ {
selection_.target = addr_.filesystem + m.name; selection_.target = addr_.filesystem + m.name;
tb_file_.caption(m.name); tb_file_.caption(m.name);
@ -816,13 +858,16 @@ namespace nana
return; return;
} }
if(io_read_) if(mode::write_file != mode_)
{ {
if(fs::file_type::not_found == ftype) if(fs::file_type::not_found == ftype)
{ {
msgbox mb(*this, caption()); msgbox mb(*this, caption());
mb.icon(msgbox::icon_information); mb.icon(msgbox::icon_information);
mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); 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(); mb();
return; return;
@ -892,7 +937,8 @@ namespace nana
} }
} }
private: private:
bool io_read_; bool const pick_directory_;
mode mode_;
std::string def_ext_; std::string def_ext_;
place place_; place place_;
@ -933,224 +979,298 @@ namespace nana
#endif #endif
//class filebox //class filebox
struct filebox::implement struct filebox::implement
{
struct filter
{ {
struct filter std::string des;
{ std::string type;
std::string des;
std::string type;
};
window owner;
bool open_or_save;
std::string file;
std::string title;
std::string path;
std::vector<filter> filters;
}; };
filebox::filebox(bool is_openmode) window owner;
: filebox(nullptr, is_openmode) bool open_or_save;
{
}
filebox::filebox(window owner, bool open) std::string file;
: impl_(new implement) std::string title;
{ std::string path;
impl_->owner = owner; std::vector<filter> filters;
impl_->open_or_save = open; };
filebox::filebox(bool is_openmode)
: filebox(nullptr, is_openmode)
{
}
filebox::filebox(window owner, bool open)
: impl_(new implement)
{
impl_->owner = owner;
impl_->open_or_save = open;
#if defined(NANA_WINDOWS) #if defined(NANA_WINDOWS)
auto len = ::GetCurrentDirectory(0, nullptr); auto len = ::GetCurrentDirectory(0, nullptr);
if(len) if(len)
{ {
std::wstring path; std::wstring path;
path.resize(len + 1); path.resize(len + 1);
::GetCurrentDirectory(len, &(path[0])); ::GetCurrentDirectory(len, &(path[0]));
path.resize(len); path.resize(len);
impl_->path = to_utf8(path); impl_->path = to_utf8(path);
} }
#endif #endif
} }
filebox::filebox(const filebox& other) filebox::filebox(const filebox& other)
: impl_(new implement(*other.impl_)) : impl_(new implement(*other.impl_))
{} {}
filebox::~filebox() filebox::~filebox()
{
delete impl_;
}
filebox& filebox::operator=(const filebox& other)
{
if (this != &other)
*impl_ = *other.impl_;
return *this;
}
void filebox::owner(window wd)
{
impl_->owner = wd;
}
std::string filebox::title(std::string s)
{
impl_->title.swap(s);
return s;
}
filebox& filebox::init_path(const std::string& ipstr)
{
if(ipstr.empty())
{ {
delete impl_; impl_->path.clear();
} }
else
filebox& filebox::operator=(const filebox& other)
{ {
if (this != &other) if (fs::is_directory(ipstr))
*impl_ = *other.impl_; impl_->path = ipstr;
return *this;
} }
return *this;
}
void filebox::owner(window wd) filebox& filebox::init_file(const std::string& ifstr)
{ {
impl_->owner = wd; impl_->file = ifstr;
} return *this;
}
std::string filebox::title(std::string s) filebox& filebox::add_filter(const std::string& description, const std::string& filetype)
{ {
impl_->title.swap(s); implement::filter flt = {description, filetype};
return s; impl_->filters.push_back(flt);
} return *this;
}
filebox& filebox::init_path(const std::string& ipstr) std::string filebox::path() const
{ {
if(ipstr.empty()) return impl_->path;
{ }
impl_->path.clear();
}
else
{
if (fs::is_directory(ipstr))
impl_->path = ipstr;
}
return *this;
}
filebox& filebox::init_file(const std::string& ifstr) std::string filebox::file() const
{ {
impl_->file = ifstr; return impl_->file;
return *this; }
}
filebox& filebox::add_filter(const std::string& description, const std::string& filetype) bool filebox::show() const
{ {
implement::filter flt = {description, filetype};
impl_->filters.push_back(flt);
return *this;
}
std::string filebox::path() const
{
return impl_->path;
}
std::string filebox::file() const
{
return impl_->file;
}
bool filebox::show() const
{
#if defined(NANA_WINDOWS) #if defined(NANA_WINDOWS)
auto winitfile = to_wstring(impl_->file); auto winitfile = to_wstring(impl_->file);
std::wstring wfile(winitfile); std::wstring wfile(winitfile);
wfile.resize(520); wfile.resize(520);
OPENFILENAME ofn; OPENFILENAME ofn;
memset(&ofn, 0, sizeof ofn); memset(&ofn, 0, sizeof ofn);
internal_scope_guard lock; internal_scope_guard lock;
ofn.lStructSize = sizeof(ofn); ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = reinterpret_cast<HWND>(API::root(impl_->owner)); ofn.hwndOwner = reinterpret_cast<HWND>(API::root(impl_->owner));
ofn.lpstrFile = &(wfile[0]); ofn.lpstrFile = &(wfile[0]);
ofn.nMaxFile = static_cast<DWORD>(wfile.size() - 1); ofn.nMaxFile = static_cast<DWORD>(wfile.size() - 1);
const wchar_t * filter; const wchar_t * filter;
std::wstring filter_holder; std::wstring filter_holder;
std::wstring default_extension; std::wstring default_extension;
if(impl_->filters.size()) if(impl_->filters.size())
{
for(auto & f : impl_->filters)
{ {
for(auto & f : impl_->filters) filter_holder += to_wstring(f.des);
filter_holder += static_cast<std::wstring::value_type>('\0');
std::wstring fs = to_wstring(f.type);
std::size_t pos = 0;
while(true)
{ {
filter_holder += to_wstring(f.des); pos = fs.find(L" ", pos);
filter_holder += static_cast<std::wstring::value_type>('\0'); if(pos == fs.npos)
std::wstring fs = to_wstring(f.type); break;
std::size_t pos = 0; fs.erase(pos);
while(true) }
{ filter_holder += fs;
pos = fs.find(L" ", pos); filter_holder += static_cast<std::wstring::value_type>('\0');
if(pos == fs.npos)
break;
fs.erase(pos);
}
filter_holder += fs;
filter_holder += static_cast<std::wstring::value_type>('\0');
//Get the default file extentsion //Get the default file extentsion
if (default_extension.empty()) if (default_extension.empty())
{
pos = fs.find_last_of('.');
if (pos != fs.npos)
{ {
pos = fs.find_last_of('.'); fs = fs.substr(pos + 1);
if (pos != fs.npos) if (fs != L"*")
{ {
fs = fs.substr(pos + 1); default_extension = fs;
if (fs != L"*") ofn.lpstrDefExt = default_extension.data();
{
default_extension = fs;
ofn.lpstrDefExt = default_extension.data();
}
} }
} }
} }
filter = filter_holder.data();
} }
else filter = filter_holder.data();
filter = L"All Files\0*.*\0"; }
else
filter = L"All Files\0*.*\0";
auto wtitle = to_wstring(impl_->title); auto wtitle = to_wstring(impl_->title);
auto wpath = to_wstring(impl_->path); auto wpath = to_wstring(impl_->path);
ofn.lpstrFilter = filter; ofn.lpstrFilter = filter;
ofn.lpstrTitle = (wtitle.empty() ? nullptr : wtitle.c_str()); ofn.lpstrTitle = (wtitle.empty() ? nullptr : wtitle.c_str());
ofn.nFilterIndex = 0; ofn.nFilterIndex = 0;
ofn.lpstrFileTitle = nullptr; ofn.lpstrFileTitle = nullptr;
ofn.nMaxFileTitle = 0; ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = (wpath.empty() ? nullptr : wpath.c_str()); ofn.lpstrInitialDir = (wpath.empty() ? nullptr : wpath.c_str());
if (!impl_->open_or_save) if (!impl_->open_or_save)
ofn.Flags = OFN_OVERWRITEPROMPT; //Overwrite prompt if it is save mode ofn.Flags = OFN_OVERWRITEPROMPT; //Overwrite prompt if it is save mode
ofn.Flags |= OFN_NOCHANGEDIR; ofn.Flags |= OFN_NOCHANGEDIR;
{ {
internal_revert_guard revert; internal_revert_guard revert;
if (FALSE == (impl_->open_or_save ? ::GetOpenFileName(&ofn) : ::GetSaveFileName(&ofn))) if (FALSE == (impl_->open_or_save ? ::GetOpenFileName(&ofn) : ::GetSaveFileName(&ofn)))
return false;
}
wfile.resize(std::wcslen(wfile.data()));
impl_->file = to_utf8(wfile);
#elif defined(NANA_POSIX)
filebox_implement fb(impl_->owner, impl_->open_or_save, impl_->title);
if(impl_->filters.size())
{
for(auto & f: impl_->filters)
{
std::string fs = f.type;
std::size_t pos = 0;
while(true)
{
pos = fs.find(" ", pos);
if(pos == fs.npos)
break;
fs.erase(pos);
}
fb.add_filter(f.des, fs);
}
}
else
fb.add_filter("All Files", "*.*");
fb.load_fs(impl_->path, impl_->file);
API::modal_window(fb);
if(false == fb.file(impl_->file))
return false; return false;
#endif }
auto tpos = impl_->file.find_last_of("\\/");
if(tpos != impl_->file.npos)
impl_->path = impl_->file.substr(0, tpos);
else
impl_->path.clear();
return true; wfile.resize(std::wcslen(wfile.data()));
}//end class filebox impl_->file = to_utf8(wfile);
#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);
if(impl_->filters.size())
{
for(auto & f: impl_->filters)
{
std::string fs = f.type;
std::size_t pos = 0;
while(true)
{
pos = fs.find(" ", pos);
if(pos == fs.npos)
break;
fs.erase(pos);
}
fb.add_filter(f.des, fs);
}
}
else
fb.add_filter("All Files", "*.*");
fb.load_fs(impl_->path, impl_->file);
API::modal_window(fb);
if(false == fb.file(impl_->file))
return false;
#endif
auto tpos = impl_->file.find_last_of("\\/");
if(tpos != impl_->file.npos)
impl_->path = impl_->file.substr(0, tpos);
else
impl_->path.clear();
return true;
}//end class filebox
//class directory_picker
struct folderbox::implement
{
window owner;
path_type init_path;
};
folderbox::folderbox(window owner, const path_type& init_path)
: impl_(new implement)
{
impl_->owner = owner;
impl_->init_path = init_path;
}
folderbox::~folderbox()
{
delete impl_;
}
std::optional<folderbox::path_type> folderbox::show() const
{
#ifdef NANA_WINDOWS
path_type target;
CoInitialize(NULL);
IFileDialog *fd(nullptr);
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd));
if (SUCCEEDED(hr))
{
IShellItem *init_path{ nullptr };
hr = SHCreateItemFromParsingName(impl_->init_path.wstring().c_str(), nullptr, IID_PPV_ARGS(&init_path));
if (SUCCEEDED(hr))
fd->SetDefaultFolder(init_path);
fd->SetOptions(FOS_PICKFOLDERS);
fd->Show(reinterpret_cast<HWND>(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))
{
PWSTR pwstr(nullptr);
hr = si->GetDisplayName(SIGDN_FILESYSPATH, &pwstr);
if (SUCCEEDED(hr))
{
target = pwstr;
// use the c-string pointed to by pwstr here
CoTaskMemFree(pwstr);
}
si->Release();
}
fd->Release();
}
CoUninitialize();
return target;
#elif defined(NANA_POSIX)
using mode = filebox_implement::mode;
filebox_implement fb(impl_->owner, mode::open_directory, {}, true);
fb.load_fs(impl_->init_path, "");
API::modal_window(fb);
std::string path_directory;
if(false == fb.file(path_directory))
return {};
return path_type{path_directory};
#endif
}
}//end namespace nana }//end namespace nana