// 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 "RelocateDialog.h" #include "GtkCompat.h" #include "PathButton.h" #include "Prefs.h" /* gtr_pref_string_get */ #include "Session.h" #include "Utils.h" #include #include #include #include #include #include #include #include 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_; PathButton* chooser_ = nullptr; Gtk::CheckButton* 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) { auto d = std::make_shared( *message_dialog_, _("Couldn't move torrent"), false, TR_GTK_MESSAGE_TYPE(ERROR), TR_GTK_BUTTONS_TYPE(CLOSE), true); timer_.block(); d->signal_response().connect( [this, d](int /*response*/) mutable { timer_.unblock(); d.reset(); }); d->show(); } else if (done_ == TR_LOC_DONE) { if (!torrent_ids_.empty()) { startMovingNextTorrent(); } else { dialog_.close(); } } return G_SOURCE_CONTINUE; } void RelocateDialog::Impl::onResponse(int response) { if (response == TR_GTK_RESPONSE_TYPE(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, TR_GTK_MESSAGE_TYPE(INFO), TR_GTK_BUTTONS_TYPE(CLOSE), true); message_dialog_->set_secondary_text(_("This may take a moment…")); message_dialog_->set_response_sensitive(TR_GTK_RESPONSE_TYPE(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_.close(); } } 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_derived(builder, "new_location_button")) , move_tb_(gtr_get_widget(builder, "move_data_radio")) { dialog_.set_default_response(TR_GTK_RESPONSE_TYPE(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_filename(gtr_pref_string_get(TR_KEY_download_dir)); } else { /* set last used as target */ chooser_->set_filename(recent_dirs.front()); recent_dirs.pop_front(); /* add remaining as shortcut */ chooser_->set_shortcut_folders(recent_dirs); } }