// This file Copyright © 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 "GtkCompat.h" #include "Utils.h" #include #include #include #if GTKMM_CHECK_VERSION(4, 0, 0) #include #include #include #include #include #include #include #endif #include 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 const& value); void add_filter(Glib::RefPtr const& value); Glib::Property& property_action(); Glib::Property& property_title(); sigc::signal& 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 action_; Glib::Property title_; sigc::signal selection_changed_; Gtk::Image* const image_ = nullptr; Gtk::Label* const label_ = nullptr; Gtk::Image* const mode_ = nullptr; std::string current_file_; std::list shortcut_folders_; std::vector> 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()) , label_(Gtk::make_managed()) , mode_(Gtk::make_managed()) #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::Orientation::HORIZONTAL, 5); layout->append(*image_); layout->append(*label_); layout->append(*Gtk::make_managed(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 const& value) { shortcut_folders_ = value; } void PathButton::Impl::add_filter(Glib::RefPtr const& value) { filters_.push_back(value); } Glib::Property& PathButton::Impl::property_action() { return action_; } Glib::Property& PathButton::Impl::property_title() { return title_; } sigc::signal& PathButton::Impl::signal_selection_changed() { return selection_changed_; } void PathButton::Impl::show_dialog() { auto const title = title_.get_value(); auto dialog = Gtk::FileChooserNative::create( !title.empty() ? title : _("Select a File"), action_.get_value(), _("_Open"), _("_Cancel")); dialog->set_transient_for(gtr_widget_get_window(widget_)); 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 == TR_GTK_RESPONSE_TYPE(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(*this)) { } PathButton::PathButton(BaseObjectType* cast_item, Glib::RefPtr const& /*builder*/) : Glib::ObjectBase(typeid(PathButton)) , BaseWidgetType(cast_item) , impl_(std::make_unique(*this)) { } PathButton::~PathButton() = default; void PathButton::set_shortcut_folders(std::list 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 const& value) { impl_->add_filter(value); } Glib::PropertyProxy PathButton::property_action() { return impl_->property_action().get_proxy(); } Glib::PropertyProxy PathButton::property_title() { return impl_->property_title().get_proxy(); } sigc::signal& PathButton::signal_selection_changed() { return impl_->signal_selection_changed(); } #endif