// This file Copyright © 2009-2022 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 #include #include #include #include #include #include "Prefs.h" /* gtr_pref_string_get */ #include "RelocateDialog.h" #include "Session.h" #include "Utils.h" namespace { std::string targetLocation; } class RelocateDialog::Impl { public: Impl( RelocateDialog& dialog, Glib::RefPtr const& builder, Glib::RefPtr const& core, std::vector const& torrent_ids); ~Impl(); TR_DISABLE_COPY_MOVE(Impl) private: void onResponse(int response); bool onTimer(); void startMovingNextTorrent(); private: RelocateDialog& dialog_; Glib::RefPtr const core_; std::vector torrent_ids_; int done_ = 0; bool do_move_ = false; sigc::connection timer_; std::unique_ptr message_dialog_; Gtk::FileChooserButton* chooser_ = nullptr; Gtk::RadioButton* move_tb_ = nullptr; }; RelocateDialog::Impl::~Impl() { timer_.disconnect(); } /*** **** ***/ void RelocateDialog::Impl::startMovingNextTorrent() { auto* const tor = core_->find_torrent(torrent_ids_.back()); if (tor != nullptr) { tr_torrentSetLocation(tor, targetLocation.c_str(), do_move_, nullptr, &done_); } torrent_ids_.pop_back(); message_dialog_->set_message( fmt::format(_("Moving '{torrent_name}'"), fmt::arg("torrent_name", tr_torrentName(tor))), true); } /* every once in awhile, check to see if the move is done. * if so, delete the dialog */ bool RelocateDialog::Impl::onTimer() { if (done_ == TR_LOC_ERROR) { Gtk::MessageDialog(*message_dialog_, _("Couldn't move torrent"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true) .run(); message_dialog_.reset(); } else if (done_ == TR_LOC_DONE) { if (!torrent_ids_.empty()) { startMovingNextTorrent(); } else { dialog_.hide(); } } return G_SOURCE_CONTINUE; } void RelocateDialog::Impl::onResponse(int response) { if (response == Gtk::RESPONSE_APPLY) { auto const location = chooser_->get_filename(); do_move_ = move_tb_->get_active(); /* pop up a dialog saying that the work is in progress */ message_dialog_ = std::make_unique( dialog_, Glib::ustring(), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true); message_dialog_->set_secondary_text(_("This may take a moment…")); message_dialog_->set_response_sensitive(Gtk::RESPONSE_CLOSE, false); message_dialog_->show(); /* remember this location for the next torrent */ targetLocation = location; /* remember this location so that it can be the default next time */ gtr_save_recent_dir("relocate", core_, location); /* start the move and periodically check its status */ done_ = TR_LOC_DONE; timer_ = Glib::signal_timeout().connect_seconds(sigc::mem_fun(*this, &Impl::onTimer), 1); onTimer(); } else { dialog_.hide(); } } RelocateDialog::RelocateDialog( BaseObjectType* cast_item, Glib::RefPtr const& builder, Gtk::Window& parent, Glib::RefPtr const& core, std::vector const& torrent_ids) : Gtk::Dialog(cast_item) , impl_(std::make_unique(*this, builder, core, torrent_ids)) { set_transient_for(parent); } RelocateDialog::~RelocateDialog() = default; std::unique_ptr RelocateDialog::create( Gtk::Window& parent, Glib::RefPtr const& core, std::vector const& torrent_ids) { auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("RelocateDialog.ui")); return std::unique_ptr( gtr_get_widget_derived(builder, "RelocateDialog", parent, core, torrent_ids)); } RelocateDialog::Impl::Impl( RelocateDialog& dialog, Glib::RefPtr const& builder, Glib::RefPtr const& core, std::vector const& torrent_ids) : dialog_(dialog) , core_(core) , torrent_ids_(torrent_ids) , chooser_(gtr_get_widget(builder, "new_location_button")) , move_tb_(gtr_get_widget(builder, "move_data_radio")) { dialog_.set_default_response(Gtk::RESPONSE_CANCEL); dialog_.signal_response().connect(sigc::mem_fun(*this, &Impl::onResponse)); auto recent_dirs = gtr_get_recent_dirs("relocate"); if (recent_dirs.empty()) { /* default to download dir */ chooser_->set_current_folder(gtr_pref_string_get(TR_KEY_download_dir)); } else { /* set last used as target */ chooser_->set_current_folder(recent_dirs.front()); recent_dirs.pop_front(); /* add remaining as shortcut */ for (auto const& folder : recent_dirs) { chooser_->remove_shortcut_folder(folder); chooser_->add_shortcut_folder(folder); } } }