Allow Lensfun DB dir editing from preferences

Also refactor the file chooser button widget to share code with the file
chooser entry. Use our own folder icon instead of the system one.
This commit is contained in:
Lawrence Lee
2023-04-29 18:43:38 -07:00
parent 3d67ca450f
commit f5dba61243
5 changed files with 314 additions and 219 deletions

View File

@@ -1913,6 +1913,7 @@ PREFERENCES_INTENT_SATURATION;Saturation
PREFERENCES_INTERNALTHUMBIFUNTOUCHED;Show embedded JPEG thumbnail if raw is unedited
PREFERENCES_LANG;Language
PREFERENCES_LANGAUTODETECT;Use system language
PREFERENCES_LENSFUNDBDIR;Lensfun database directory
PREFERENCES_LENSPROFILESDIR;Lens profiles directory
PREFERENCES_MAXRECENTFOLDERS;Maximum number of recent folders
PREFERENCES_MENUGROUPEXTPROGS;Group 'Open with'

View File

@@ -1299,23 +1299,201 @@ bool MyHScale::on_key_press_event (GdkEventKey* event)
}
}
MyFileChooserButton::MyFileChooserButton(const Glib::ustring &title, Gtk::FileChooserAction action):
title_(title),
action_(action),
lbl_("", Gtk::ALIGN_START),
show_hidden_(false)
class MyFileChooserWidget::Impl
{
lbl_.set_ellipsize(Pango::ELLIPSIZE_MIDDLE);
lbl_.set_justify(Gtk::JUSTIFY_LEFT);
set_none();
box_.pack_start(lbl_, true, true);
Gtk::Image *img = Gtk::manage(new Gtk::Image());
img->set_from_icon_name("folder-open", Gtk::ICON_SIZE_BUTTON);
box_.pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), false, false, 5);
box_.pack_start(*img, false, false);
box_.show_all_children();
add(box_);
signal_clicked().connect(sigc::mem_fun(*this, &MyFileChooserButton::show_chooser));
public:
Impl(const Glib::ustring &title, Gtk::FileChooserAction action) :
title_(title),
action_(action)
{
}
Glib::ustring title_;
Gtk::FileChooserAction action_;
std::string filename_;
std::string current_folder_;
std::vector<Glib::RefPtr<Gtk::FileFilter>> file_filters_;
Glib::RefPtr<Gtk::FileFilter> cur_filter_;
std::vector<std::string> shortcut_folders_;
bool show_hidden_{false};
sigc::signal<void> selection_changed_;
};
MyFileChooserWidget::MyFileChooserWidget(const Glib::ustring &title, Gtk::FileChooserAction action) :
pimpl(new Impl(title, action))
{
}
std::unique_ptr<Gtk::Image> MyFileChooserWidget::make_folder_image()
{
return std::unique_ptr<Gtk::Image>(new RTImage("folder-open-small.png"));
}
void MyFileChooserWidget::show_chooser(Gtk::Widget *parent)
{
Gtk::FileChooserDialog dlg(getToplevelWindow(parent), pimpl->title_, pimpl->action_);
dlg.add_button(M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL);
dlg.add_button(M(pimpl->action_ == Gtk::FILE_CHOOSER_ACTION_SAVE ? "GENERAL_SAVE" : "GENERAL_OPEN"), Gtk::RESPONSE_OK);
dlg.set_filename(pimpl->filename_);
for (auto &f : pimpl->file_filters_) {
dlg.add_filter(f);
}
if (pimpl->cur_filter_) {
dlg.set_filter(pimpl->cur_filter_);
}
for (auto &f : pimpl->shortcut_folders_) {
dlg.add_shortcut_folder(f);
}
if (!pimpl->current_folder_.empty()) {
dlg.set_current_folder(pimpl->current_folder_);
}
dlg.set_show_hidden(pimpl->show_hidden_);
int res = dlg.run();
if (res == Gtk::RESPONSE_OK) {
pimpl->filename_ = dlg.get_filename();
pimpl->current_folder_ = dlg.get_current_folder();
on_filename_set();
pimpl->selection_changed_.emit();
}
}
void MyFileChooserWidget::on_filename_set()
{
// Sub-classes decide if anything needs to be done.
}
sigc::signal<void> &MyFileChooserWidget::signal_selection_changed()
{
return pimpl->selection_changed_;
}
sigc::signal<void> &MyFileChooserWidget::signal_file_set()
{
return pimpl->selection_changed_;
}
std::string MyFileChooserWidget::get_filename() const
{
return pimpl->filename_;
}
bool MyFileChooserWidget::set_filename(const std::string &filename)
{
pimpl->filename_ = filename;
on_filename_set();
return true;
}
void MyFileChooserWidget::add_filter(const Glib::RefPtr<Gtk::FileFilter> &filter)
{
pimpl->file_filters_.push_back(filter);
}
void MyFileChooserWidget::remove_filter(const Glib::RefPtr<Gtk::FileFilter> &filter)
{
auto it = std::find(pimpl->file_filters_.begin(), pimpl->file_filters_.end(), filter);
if (it != pimpl->file_filters_.end()) {
pimpl->file_filters_.erase(it);
}
}
void MyFileChooserWidget::set_filter(const Glib::RefPtr<Gtk::FileFilter> &filter)
{
pimpl->cur_filter_ = filter;
}
std::vector<Glib::RefPtr<Gtk::FileFilter>> MyFileChooserWidget::list_filters() const
{
return pimpl->file_filters_;
}
bool MyFileChooserWidget::set_current_folder(const std::string &filename)
{
pimpl->current_folder_ = filename;
if (pimpl->action_ == Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER) {
set_filename(filename);
}
return true;
}
std::string MyFileChooserWidget::get_current_folder() const
{
return pimpl->current_folder_;
}
bool MyFileChooserWidget::add_shortcut_folder(const std::string &folder)
{
pimpl->shortcut_folders_.push_back(folder);
return true;
}
bool MyFileChooserWidget::remove_shortcut_folder(const std::string &folder)
{
auto it = std::find(pimpl->shortcut_folders_.begin(), pimpl->shortcut_folders_.end(), folder);
if (it != pimpl->shortcut_folders_.end()) {
pimpl->shortcut_folders_.erase(it);
}
return true;
}
void MyFileChooserWidget::unselect_all()
{
pimpl->filename_ = "";
on_filename_set();
}
void MyFileChooserWidget::unselect_filename(const std::string &filename)
{
if (pimpl->filename_ == filename) {
unselect_all();
}
}
void MyFileChooserWidget::set_show_hidden(bool yes)
{
pimpl->show_hidden_ = yes;
}
class MyFileChooserButton::Impl
{
public:
Gtk::Box box_;
Gtk::Label lbl_{"", Gtk::ALIGN_START};
};
MyFileChooserButton::MyFileChooserButton(const Glib::ustring &title, Gtk::FileChooserAction action):
MyFileChooserWidget(title, action),
pimpl(new Impl())
{
pimpl->lbl_.set_ellipsize(Pango::ELLIPSIZE_MIDDLE);
pimpl->lbl_.set_justify(Gtk::JUSTIFY_LEFT);
on_filename_set();
pimpl->box_.pack_start(pimpl->lbl_, true, true);
pimpl->box_.pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), false, false, 5);
pimpl->box_.pack_start(*Gtk::manage(make_folder_image().release()), false, false);
pimpl->box_.show_all_children();
add(pimpl->box_);
signal_clicked().connect([this]() {
show_chooser(this);
});
if (GTK_MINOR_VERSION < 20) {
set_border_width(2); // margin doesn't work on GTK < 3.20
@@ -1324,151 +1502,16 @@ MyFileChooserButton::MyFileChooserButton(const Glib::ustring &title, Gtk::FileCh
set_name("MyFileChooserButton");
}
void MyFileChooserButton::show_chooser()
void MyFileChooserButton::on_filename_set()
{
Gtk::FileChooserDialog dlg(getToplevelWindow(this), title_, action_);
dlg.add_button(M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL);
dlg.add_button(M(action_ == Gtk::FILE_CHOOSER_ACTION_SAVE ? "GENERAL_SAVE" : "GENERAL_OPEN"), Gtk::RESPONSE_OK);
dlg.set_filename(filename_);
for (auto &f : file_filters_) {
dlg.add_filter(f);
}
if (cur_filter_) {
dlg.set_filter(cur_filter_);
}
for (auto &f : shortcut_folders_) {
dlg.add_shortcut_folder(f);
}
if (!current_folder_.empty()) {
dlg.set_current_folder(current_folder_);
}
dlg.set_show_hidden(show_hidden_);
int res = dlg.run();
if (res == Gtk::RESPONSE_OK) {
filename_ = dlg.get_filename();
current_folder_ = dlg.get_current_folder();
lbl_.set_label(Glib::path_get_basename(filename_));
selection_changed_.emit();
}
}
sigc::signal<void> &MyFileChooserButton::signal_selection_changed()
{
return selection_changed_;
}
sigc::signal<void> &MyFileChooserButton::signal_file_set()
{
return selection_changed_;
}
std::string MyFileChooserButton::get_filename() const
{
return filename_;
}
bool MyFileChooserButton::set_filename(const std::string &filename)
{
filename_ = filename;
if (Glib::file_test(filename_, Glib::FILE_TEST_EXISTS)) {
lbl_.set_label(Glib::path_get_basename(filename_));
if (Glib::file_test(get_filename(), Glib::FILE_TEST_EXISTS)) {
pimpl->lbl_.set_label(Glib::path_get_basename(get_filename()));
} else {
set_none();
}
return true;
}
void MyFileChooserButton::add_filter(const Glib::RefPtr<Gtk::FileFilter> &filter)
{
file_filters_.push_back(filter);
}
void MyFileChooserButton::remove_filter(const Glib::RefPtr<Gtk::FileFilter> &filter)
{
auto it = std::find(file_filters_.begin(), file_filters_.end(), filter);
if (it != file_filters_.end()) {
file_filters_.erase(it);
pimpl->lbl_.set_label(Glib::ustring("(") + M("GENERAL_NONE") + ")");
}
}
void MyFileChooserButton::set_filter(const Glib::RefPtr<Gtk::FileFilter> &filter)
{
cur_filter_ = filter;
}
std::vector<Glib::RefPtr<Gtk::FileFilter>> MyFileChooserButton::list_filters()
{
return file_filters_;
}
bool MyFileChooserButton::set_current_folder(const std::string &filename)
{
current_folder_ = filename;
if (action_ == Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER) {
set_filename(filename);
}
return true;
}
std::string MyFileChooserButton::get_current_folder() const
{
return current_folder_;
}
bool MyFileChooserButton::add_shortcut_folder(const std::string &folder)
{
shortcut_folders_.push_back(folder);
return true;
}
bool MyFileChooserButton::remove_shortcut_folder(const std::string &folder)
{
auto it = std::find(shortcut_folders_.begin(), shortcut_folders_.end(), folder);
if (it != shortcut_folders_.end()) {
shortcut_folders_.erase(it);
}
return true;
}
void MyFileChooserButton::unselect_all()
{
filename_ = "";
set_none();
}
void MyFileChooserButton::unselect_filename(const std::string &filename)
{
if (filename_ == filename) {
unselect_all();
}
}
void MyFileChooserButton::set_show_hidden(bool yes)
{
show_hidden_ = yes;
}
void MyFileChooserButton::set_none()
{
lbl_.set_label(Glib::ustring("(") + M("GENERAL_NONE") + ")");
}
// For an unknown reason (a bug ?), it doesn't work when action = FILE_CHOOSER_ACTION_SELECT_FOLDER !
bool MyFileChooserButton::on_scroll_event (GdkEventScroll* event)
{
@@ -1493,6 +1536,40 @@ void MyFileChooserButton::get_preferred_width_for_height_vfunc (int height, int
}
class MyFileChooserEntry::Impl
{
public:
Gtk::Entry entry;
Gtk::Button file_chooser_button;
};
MyFileChooserEntry::MyFileChooserEntry(const Glib::ustring &title, Gtk::FileChooserAction action) :
MyFileChooserWidget(title, action),
pimpl(new Impl())
{
pimpl->file_chooser_button.set_image(*Gtk::manage(make_folder_image().release()));
pimpl->file_chooser_button.signal_clicked().connect([this]() {
const auto &filename = pimpl->entry.get_text();
if (Glib::file_test(filename, Glib::FILE_TEST_IS_DIR)) {
set_current_folder(filename);
}
set_filename(filename);
show_chooser(this);
});
pack_start(pimpl->entry, true, true);
pack_start(pimpl->file_chooser_button, false, false);
}
void MyFileChooserEntry::on_filename_set()
{
if (pimpl->entry.get_text() != get_filename()) {
pimpl->entry.set_text(get_filename());
}
}
TextOrIcon::TextOrIcon (const Glib::ustring &fname, const Glib::ustring &labelTx, const Glib::ustring &tooltipTx)
{

View File

@@ -396,34 +396,10 @@ protected:
};
/**
* @brief subclass of Gtk::FileChooserButton in order to handle the scrollwheel
*/
class MyFileChooserButton final : public Gtk::Button {
private:
void show_chooser();
Glib::ustring title_;
Gtk::FileChooserAction action_;
Gtk::Box box_;
Gtk::Label lbl_;
std::string filename_;
std::string current_folder_;
std::vector<Glib::RefPtr<Gtk::FileFilter>> file_filters_;
Glib::RefPtr<Gtk::FileFilter> cur_filter_;
std::vector<std::string> shortcut_folders_;
bool show_hidden_;
sigc::signal<void> selection_changed_;
protected:
bool on_scroll_event (GdkEventScroll* event) override;
void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const override;
void get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const override;
void set_none();
class MyFileChooserWidget
{
public:
explicit MyFileChooserButton(const Glib::ustring &title, Gtk::FileChooserAction action=Gtk::FILE_CHOOSER_ACTION_OPEN);
virtual ~MyFileChooserWidget() = default;
sigc::signal<void> &signal_selection_changed();
sigc::signal<void> &signal_file_set();
@@ -434,7 +410,7 @@ public:
void add_filter(const Glib::RefPtr<Gtk::FileFilter> &filter);
void remove_filter(const Glib::RefPtr<Gtk::FileFilter> &filter);
void set_filter(const Glib::RefPtr<Gtk::FileFilter> &filter);
std::vector<Glib::RefPtr<Gtk::FileFilter>> list_filters();
std::vector<Glib::RefPtr<Gtk::FileFilter>> list_filters() const;
bool set_current_folder(const std::string &filename);
std::string get_current_folder() const;
@@ -446,6 +422,54 @@ public:
void unselect_filename(const std::string &filename);
void set_show_hidden(bool yes);
protected:
explicit MyFileChooserWidget(const Glib::ustring &title, Gtk::FileChooserAction action=Gtk::FILE_CHOOSER_ACTION_OPEN);
static std::unique_ptr<Gtk::Image> make_folder_image();
void show_chooser(Gtk::Widget *parent);
virtual void on_filename_set();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
/**
* @brief subclass of Gtk::FileChooserButton in order to handle the scrollwheel
*/
class MyFileChooserButton final : public Gtk::Button, public MyFileChooserWidget
{
private:
class Impl;
std::unique_ptr<Impl> pimpl;
protected:
bool on_scroll_event (GdkEventScroll* event) override;
void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const override;
void get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const override;
void on_filename_set() override;
public:
explicit MyFileChooserButton(const Glib::ustring &title, Gtk::FileChooserAction action=Gtk::FILE_CHOOSER_ACTION_OPEN);
};
class MyFileChooserEntry : public Gtk::Box, public MyFileChooserWidget
{
public:
explicit MyFileChooserEntry(const Glib::ustring &title, Gtk::FileChooserAction action = Gtk::FILE_CHOOSER_ACTION_OPEN);
protected:
void on_filename_set() override;
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
/**

View File

@@ -667,6 +667,18 @@ Gtk::Widget* Preferences::getImageProcessingPanel ()
dirgrid->attach_next_to(*lensProfilesDirLabel, *cameraProfilesDirLabel, Gtk::POS_BOTTOM, 1, 1);
dirgrid->attach_next_to(*lensProfilesDir, *lensProfilesDirLabel, Gtk::POS_RIGHT, 1, 1);
// Lensfun DB dir
Gtk::Label *lensfunDbDirLabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_LENSFUNDBDIR") + ":"));
setExpandAlignProperties(lensfunDbDirLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
lensfunDbDir = Gtk::manage(new MyFileChooserEntry(M("PREFERENCES_LENSFUNDBDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER));
setExpandAlignProperties(lensfunDbDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
Gtk::Label* lensfunDbDirRestartNeededLabel = Gtk::manage(new Gtk::Label(Glib::ustring(" (") + M("PREFERENCES_APPLNEXTSTARTUP") + ")"));
setExpandAlignProperties(lensfunDbDirRestartNeededLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
dirgrid->attach_next_to(*lensfunDbDirLabel, *lensProfilesDirLabel, Gtk::POS_BOTTOM, 1, 1);
dirgrid->attach_next_to(*lensfunDbDir, *lensfunDbDirLabel, Gtk::POS_RIGHT, 1, 1);
dirgrid->attach_next_to(*lensfunDbDirRestartNeededLabel, *lensfunDbDir, Gtk::POS_RIGHT, 1, 1);
//Pack directories to Image Processing panel
cdf->add(*dirgrid);
vbImageProcessing->pack_start (*cdf, Gtk::PACK_SHRINK, 4 );
@@ -1315,10 +1327,7 @@ Gtk::Widget* Preferences::getFileBrowserPanel()
sdlast = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIRLAST")));
sdhome = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIRHOME")));
sdother = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIROTHER") + ": "));
startupdir = Gtk::manage(new Gtk::Entry());
Gtk::Button* sdselect = Gtk::manage(new Gtk::Button());
sdselect->set_image (*Gtk::manage (new RTImage ("folder-open-small.png")));
startupdir = Gtk::manage(new MyFileChooserEntry(M("PREFERENCES_DIRSELECTDLG"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER));
Gtk::RadioButton::Group opts = sdcurrent->get_group();
sdlast->set_group(opts);
@@ -1332,14 +1341,11 @@ Gtk::Widget* Preferences::getFileBrowserPanel()
Gtk::Box* otherbox = Gtk::manage(new Gtk::Box());
otherbox->pack_start(*sdother, Gtk::PACK_SHRINK);
otherbox->pack_start(*startupdir);
otherbox->pack_end(*sdselect, Gtk::PACK_SHRINK, 4);
vbsd->pack_start(*otherbox, Gtk::PACK_SHRINK, 0);
fsd->add(*vbsd);
vbFileBrowser->pack_start (*fsd, Gtk::PACK_SHRINK, 4);
sdselect->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::selectStartupDir));
//---
@@ -1839,7 +1845,7 @@ void Preferences::storePreferences()
moptions.startupDir = STARTUPDIR_LAST;
} else if (sdother->get_active()) {
moptions.startupDir = STARTUPDIR_CUSTOM;
moptions.startupPath = startupdir->get_text();
moptions.startupPath = startupdir->get_filename();
}
moptions.parseExtensions.clear();
@@ -1870,6 +1876,7 @@ void Preferences::storePreferences()
moptions.clutsDir = clutsDir->get_filename();
moptions.rtSettings.cameraProfilesPath = cameraProfilesDir->get_filename();
moptions.rtSettings.lensProfilesPath = lensProfilesDir->get_filename();
moptions.rtSettings.lensfunDbDirectory = lensfunDbDir->get_filename();
moptions.baBehav.resize(ADDSET_PARAM_NUM);
@@ -2065,7 +2072,7 @@ void Preferences::fillPreferences()
sdhome->set_active();
} else if (moptions.startupDir == STARTUPDIR_CUSTOM) {
sdother->set_active();
startupdir->set_text(moptions.startupPath);
startupdir->set_current_folder(moptions.startupPath);
}
extensionModel->clear();
@@ -2130,6 +2137,8 @@ void Preferences::fillPreferences()
lensProfilesDir->set_current_folder(moptions.rtSettings.lensProfilesPath);
lensfunDbDir->set_current_folder(moptions.rtSettings.lensfunDbDirectory);
addc.block(true);
setc.block(true);
@@ -2257,23 +2266,6 @@ void Preferences::cancelPressed()
hide();
}
void Preferences::selectStartupDir()
{
Gtk::FileChooserDialog dialog(getToplevelWindow(this), M("PREFERENCES_DIRSELECTDLG"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
//dialog.set_transient_for(*this);
//Add response buttons to the dialog:
dialog.add_button(M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL);
dialog.add_button(M("GENERAL_OPEN"), Gtk::RESPONSE_OK);
int result = dialog.run();
if (result == Gtk::RESPONSE_OK) {
startupdir->set_text(dialog.get_filename());
}
}
void Preferences::aboutPressed()
{

View File

@@ -92,7 +92,7 @@ class Preferences final :
Gtk::ComboBoxText* languages;
Gtk::CheckButton* ckbLangAutoDetect;
Gtk::Entry* dateformat;
Gtk::Entry* startupdir;
MyFileChooserEntry* startupdir;
Gtk::RadioButton* sdcurrent;
Gtk::RadioButton* sdlast;
Gtk::RadioButton* sdhome;
@@ -117,6 +117,7 @@ class Preferences final :
MyFileChooserButton* clutsDir;
MyFileChooserButton* cameraProfilesDir;
MyFileChooserButton* lensProfilesDir;
MyFileChooserEntry* lensfunDbDir;
Gtk::Label *dfLabel;
Gtk::Label *ffLabel;