277 lines
6.7 KiB
C++
277 lines
6.7 KiB
C++
// This file Copyright © 2022-2023 Mnemosyne LLC.
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
// License text can be found in the licenses/ folder.
|
|
|
|
#include "PathButton.h"
|
|
|
|
#include "Utils.h"
|
|
|
|
#include <giomm/file.h>
|
|
#include <glibmm/error.h>
|
|
#include <glibmm/i18n.h>
|
|
#include <glibmm/property.h>
|
|
#include <gtkmm/box.h>
|
|
#include <gtkmm/filechooserdialog.h>
|
|
#include <gtkmm/image.h>
|
|
#include <gtkmm/label.h>
|
|
#include <gtkmm/separator.h>
|
|
|
|
#include <vector>
|
|
|
|
class PathButton::Impl
|
|
{
|
|
public:
|
|
explicit Impl(PathButton& widget);
|
|
~Impl() = default;
|
|
|
|
TR_DISABLE_COPY_MOVE(Impl)
|
|
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
std::string const& get_filename() const;
|
|
void set_filename(std::string const& value);
|
|
|
|
void set_shortcut_folders(std::list<std::string> const& value);
|
|
|
|
void add_filter(Glib::RefPtr<Gtk::FileFilter> const& value);
|
|
|
|
Glib::Property<Gtk::FileChooser::Action>& property_action();
|
|
Glib::Property<Glib::ustring>& property_title();
|
|
|
|
sigc::signal<void()>& signal_selection_changed();
|
|
#endif
|
|
|
|
private:
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
void show_dialog();
|
|
|
|
void update();
|
|
void update_mode();
|
|
#endif
|
|
|
|
private:
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
PathButton& widget_;
|
|
|
|
Glib::Property<Gtk::FileChooser::Action> action_;
|
|
Glib::Property<Glib::ustring> title_;
|
|
|
|
sigc::signal<void()> selection_changed_;
|
|
|
|
Gtk::Image* const image_ = nullptr;
|
|
Gtk::Label* const label_ = nullptr;
|
|
Gtk::Image* const mode_ = nullptr;
|
|
|
|
std::string current_file_;
|
|
std::list<std::string> shortcut_folders_;
|
|
std::vector<Glib::RefPtr<Gtk::FileFilter>> filters_;
|
|
#endif
|
|
};
|
|
|
|
PathButton::Impl::Impl([[maybe_unused]] PathButton& widget)
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
: widget_(widget)
|
|
, action_(widget, "action", Gtk::FileChooser::Action::OPEN)
|
|
, title_(widget, "title", {})
|
|
, image_(Gtk::make_managed<Gtk::Image>())
|
|
, label_(Gtk::make_managed<Gtk::Label>())
|
|
, mode_(Gtk::make_managed<Gtk::Image>())
|
|
#endif
|
|
{
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
action_.get_proxy().signal_changed().connect([this]() { update_mode(); });
|
|
|
|
label_->set_ellipsize(Pango::EllipsizeMode::END);
|
|
label_->set_hexpand(true);
|
|
label_->set_halign(Gtk::Align::START);
|
|
|
|
auto* const layout = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 5);
|
|
layout->append(*image_);
|
|
layout->append(*label_);
|
|
layout->append(*Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::VERTICAL));
|
|
layout->append(*mode_);
|
|
widget_.set_child(*layout);
|
|
|
|
widget_.signal_clicked().connect(sigc::mem_fun(*this, &Impl::show_dialog));
|
|
|
|
update();
|
|
update_mode();
|
|
#endif
|
|
}
|
|
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
|
|
std::string const& PathButton::Impl::get_filename() const
|
|
{
|
|
return current_file_;
|
|
}
|
|
|
|
void PathButton::Impl::set_filename(std::string const& value)
|
|
{
|
|
current_file_ = value;
|
|
update();
|
|
selection_changed_.emit();
|
|
}
|
|
|
|
void PathButton::Impl::set_shortcut_folders(std::list<std::string> const& value)
|
|
{
|
|
shortcut_folders_ = value;
|
|
}
|
|
|
|
void PathButton::Impl::add_filter(Glib::RefPtr<Gtk::FileFilter> const& value)
|
|
{
|
|
filters_.push_back(value);
|
|
}
|
|
|
|
Glib::Property<Gtk::FileChooser::Action>& PathButton::Impl::property_action()
|
|
{
|
|
return action_;
|
|
}
|
|
|
|
Glib::Property<Glib::ustring>& PathButton::Impl::property_title()
|
|
{
|
|
return title_;
|
|
}
|
|
|
|
sigc::signal<void()>& PathButton::Impl::signal_selection_changed()
|
|
{
|
|
return selection_changed_;
|
|
}
|
|
|
|
void PathButton::Impl::show_dialog()
|
|
{
|
|
auto const title = title_.get_value();
|
|
|
|
auto dialog = std::make_shared<Gtk::FileChooserDialog>(!title.empty() ? title : _("Select a File"), action_.get_value());
|
|
dialog->set_transient_for(gtr_widget_get_window(widget_));
|
|
dialog->add_button(_("_Cancel"), Gtk::ResponseType::CANCEL);
|
|
dialog->add_button(_("_Open"), Gtk::ResponseType::ACCEPT);
|
|
dialog->set_modal(true);
|
|
|
|
if (!current_file_.empty())
|
|
{
|
|
dialog->set_file(Gio::File::create_for_path(current_file_));
|
|
}
|
|
|
|
for (auto const& folder : shortcut_folders_)
|
|
{
|
|
dialog->remove_shortcut_folder(Gio::File::create_for_path(folder));
|
|
dialog->add_shortcut_folder(Gio::File::create_for_path(folder));
|
|
}
|
|
|
|
for (auto const& filter : filters_)
|
|
{
|
|
dialog->add_filter(filter);
|
|
}
|
|
|
|
dialog->signal_response().connect(
|
|
[this, dialog](int response) mutable
|
|
{
|
|
if (response == Gtk::ResponseType::ACCEPT)
|
|
{
|
|
set_filename(dialog->get_file()->get_path());
|
|
selection_changed_.emit();
|
|
}
|
|
|
|
dialog.reset();
|
|
});
|
|
|
|
dialog->show();
|
|
}
|
|
|
|
void PathButton::Impl::update()
|
|
{
|
|
if (!current_file_.empty())
|
|
{
|
|
auto const file = Gio::File::create_for_path(current_file_);
|
|
|
|
try
|
|
{
|
|
image_->set(file->query_info()->get_icon());
|
|
}
|
|
catch (Glib::Error const&)
|
|
{
|
|
image_->set_from_icon_name("image-missing");
|
|
}
|
|
|
|
label_->set_text(file->get_basename());
|
|
}
|
|
else
|
|
{
|
|
image_->set_from_icon_name("image-missing");
|
|
label_->set_text(_("(None)"));
|
|
}
|
|
|
|
widget_.set_tooltip_text(current_file_);
|
|
}
|
|
|
|
void PathButton::Impl::update_mode()
|
|
{
|
|
mode_->set_from_icon_name(
|
|
action_.get_value() == Gtk::FileChooser::Action::SELECT_FOLDER ? "folder-open-symbolic" : "document-open-symbolic");
|
|
}
|
|
|
|
#endif
|
|
|
|
PathButton::PathButton()
|
|
: Glib::ObjectBase(typeid(PathButton))
|
|
, impl_(std::make_unique<Impl>(*this))
|
|
{
|
|
}
|
|
|
|
PathButton::PathButton(BaseObjectType* cast_item, Glib::RefPtr<Gtk::Builder> const& /*builder*/)
|
|
: Glib::ObjectBase(typeid(PathButton))
|
|
, BaseWidgetType(cast_item)
|
|
, impl_(std::make_unique<Impl>(*this))
|
|
{
|
|
}
|
|
|
|
PathButton::~PathButton() = default;
|
|
|
|
void PathButton::set_shortcut_folders(std::list<std::string> const& value)
|
|
{
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
impl_->set_shortcut_folders(value);
|
|
#else
|
|
for (auto const& folder : value)
|
|
{
|
|
remove_shortcut_folder(folder);
|
|
add_shortcut_folder(folder);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
|
|
|
std::string PathButton::get_filename() const
|
|
{
|
|
return impl_->get_filename();
|
|
}
|
|
|
|
void PathButton::set_filename(std::string const& value)
|
|
{
|
|
impl_->set_filename(value);
|
|
}
|
|
|
|
void PathButton::add_filter(Glib::RefPtr<Gtk::FileFilter> const& value)
|
|
{
|
|
impl_->add_filter(value);
|
|
}
|
|
|
|
Glib::PropertyProxy<Gtk::FileChooser::Action> PathButton::property_action()
|
|
{
|
|
return impl_->property_action().get_proxy();
|
|
}
|
|
|
|
Glib::PropertyProxy<Glib::ustring> PathButton::property_title()
|
|
{
|
|
return impl_->property_title().get_proxy();
|
|
}
|
|
|
|
sigc::signal<void()>& PathButton::signal_selection_changed()
|
|
{
|
|
return impl_->signal_selection_changed();
|
|
}
|
|
|
|
#endif
|