// -*-c++-*- // pkgview.h // // Copyright 1999-2009 Daniel Burrows // Copyright 2008 Obey Arthur Liu // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, write to // the Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. #ifndef PKGVIEW_H_ #define PKGVIEW_H_ #undef OK #include #include #include #include #include #include #include "gui.h" // For entity_state_info. namespace cwidget { namespace threads { class thread; } } class refcounted_progress; namespace gui { class PkgEntity : public Entity { private: entity_state_info current_state_columns(); entity_state_info selected_package_state_columns(); string selected_package_state_color(); pkgCache::PkgIterator pkg; public: PkgEntity(const pkgCache::PkgIterator &_pkg) : pkg(_pkg) { } void activated(const Gtk::TreeModel::Path &path, const Gtk::TreeViewColumn *column, const EntityView *view); /** \brief Fill in the contents of a tree-model row for the given * package/version pair. * * \param row The row to fill in; any existing values * will be overwritten. * \param pkg The package to display in this row. * \param ver The version to display in this row. * \param version_specific The row is version specific (influences * coloring and selected status display) */ void fill_row(const EntityColumns *columns, Gtk::TreeModel::Row &row); void add_packages(std::set &packages); void add_actions(std::set &actions); void dispatch_action(PackagesAction action, bool first_pass); pkgCache::PkgIterator get_pkg() { return pkg; } /** \brief Get the version, if any, that should be displayed. */ pkgCache::VerIterator get_ver() const; /** \brief Get the version, if any, that would be displayed for the given package. */ static pkgCache::VerIterator get_ver(const pkgCache::PkgIterator &pkg); }; /** \brief Interface for generating tree-views. * * A tree-view generator takes each package that appears in the * current package view and places it into an encapsulated * Gtk::TreeModel. */ class PkgTreeModelGenerator { public: // FIXME: Hack while finding a nonblocking thread join. bool finished; PkgTreeModelGenerator() { finished = false; } virtual ~PkgTreeModelGenerator(); /** \brief Add the given package and version to this tree view. * * \param pkg The package to add. */ virtual void add(const pkgCache::PkgIterator &pkg) = 0; /** \brief Perform actions that need to be taken after adding all * the packages. * * For instance, this typically sorts the entire view. */ virtual void finish() = 0; /** \brief Retrieve the model associated with this generator. * * The model will be filled in as add_package() is invoked. * Normally you should only use the model once it is entirely * filled in (to avoid unnecessary screen updates). * * \return The model built by this generator. */ virtual Glib::RefPtr get_model() = 0; }; /** \brief Base class for views that display a subset of the packages * found in the cache. * * \todo The name is lame. */ class PkgViewBase : public EntityView { cwidget::util::ref_ptr limit; sigc::slot1 generatorK; /** \brief Discards the contents of the list and stops building * it. */ void do_cache_closed(); class background_build_store : public sigc::trackable { /** \brief Used to cancel the background build thread on a * best-effort basis. * * Canceling the thread isn't always possible, and it isn't * strictly necessary; it just saves some CPU time to do so. */ class cancel_flag : public aptitude::util::refcounted_base_threadsafe { cancel_flag() : canceled(false) { } // TODO: is it any better to have a mutex around this? Seems // like that might just introduce tons of locks for no good // reason, esp. since this is just a best-effort cancel. (of // course, we could check every 1000 iterations or so...) bool canceled; public: static cwidget::util::ref_ptr create() { return new cancel_flag; } bool is_canceled() const { return canceled; } void cancel() { canceled = true; } }; class build_thread; /** \brief The callback for the current build thread. * * When the build is canceled we disconnect this slot, so that it * has no effect if it's posted to the mean thread. */ safe_slot1 > builder_callback; /** \brief Like builder_callback, but for the current progress. */ safe_slot2 builder_progress_callback; /** \brief The cancel flag for the current build thread. */ cwidget::util::ref_ptr builder_cancel; /** \brief The set of build threads that are active at this * moment. * * These are used when cancel_now() is invoked; they are * maintained in the main thread. */ std::set active_threads; /** \brief The current build thread, if any. * * This is set by the main thread when the builder is started, * and nulled when builder_complete() or builder_cancel() is * invoked. (note, however, that it is not deleted until it's * removed from active_threads, which happens either when it * exits or when it is stopped by cancel_now(). */ cwidget::threads::thread *builder; /** \brief If the given thread exists in active_threads, delete * it and remove it from that set. * * Invoked when a thread exits normally, and when it's stopped * by cancel_now(). */ void thread_stopped(cwidget::threads::thread *t); /** \brief Tells us how to create a new progres bar for * displaying the build progress. */ sigc::slot > builder_progress_k; /** \brief Holds a reference to the progress bar of the current * builder, if any. * * This is set and cleared in the same cases as builder. */ cwidget::util::ref_ptr builder_progress; /** \brief The connection that pulses the main progress bar, if * any. */ sigc::connection pulse_connection; bool pulse_progress(); void progress(int current, int total); /** \brief Invoked in the main thread when the store is done being * built in the background. * * Not invoked if the rebuild is canceled. */ void rebuild_store_finished(Glib::RefPtr model); public: background_build_store(const sigc::slot > &_builder_progress_k); ~background_build_store(); /** \brief Start rebuilding the store. * * Cancels any existing rebuild as a side effect. */ void start(const sigc::slot1 &generatorK, const EntityColumns *columns, const cwidget::util::ref_ptr &limit); /** \brief Stop the background list builder. * * This actually just disconnects it (so that invoking its slot * has no effect) and then asks it to cancel; it doesn't wait for * the thread to actually stop. */ void cancel(); /** \brief Stop all outstanding build threads, and wait for them * to finish. * * This should be invoked, for instance, when the cache is * being closed. */ void cancel_now(); /** \brief Signal indicating that the store has been rebuilt. */ sigc::signal > store_rebuilt; }; /** \brief Invoked when the background builder is finished * rebuilding the store. * * This attaches the model to the tree-view and signals to * clients that it's ready. */ void store_rebuilt(const Glib::RefPtr &model); background_build_store background_builder; public: sigc::signal store_reloading; sigc::signal store_reloaded; /** \brief Create a new PkgViewBase. * * \param _generatorK A function that takes the columns of this PkgView and * returns a PkgTreeModelGenerator object, which is used * to build the contents of the list displayed in this view. * \param refGlade The Glade XML tree from which to load this PkgView's contents. * \param gladename The name of the package view's TreeView in the Glade XML tree. * \param parent_title A string used to identify this view when the "edit columns" * dialog box is popped up. * \param limit The initial limit (or the empty string to ask the user to search). */ PkgViewBase(const sigc::slot1 _generatorK, const Glib::RefPtr &refGlade, const Glib::ustring &gladename, const Glib::ustring &parent_title, const Glib::ustring &limit, const sigc::slot > &build_progress_k); virtual ~PkgViewBase(); /** \brief Rebuild the store using the currently set limit. */ virtual void rebuild_store(); /** \brief Change this view's limit to the given pattern. */ void set_limit(const cwidget::util::ref_ptr &limit); }; /** \brief A view that displays an unorganized list of packages. */ class PkgView : public PkgViewBase { public: class Generator : public PkgTreeModelGenerator { Glib::RefPtr store; // We need this to set up the sorting at the end. const EntityColumns *columns; public: Generator(const EntityColumns *columns); static Generator *create(const EntityColumns *columns); void add(const pkgCache::PkgIterator &pkg); void finish(); Glib::RefPtr get_model(); }; /** \brief Create a new PkgView. * * \param refGlade The Glade XML tree from which to load this PkgView's contents. * \param gladename The name of the package view's TreeView in the Glade XML tree. * \param parent_title A string used to identify this view when the "edit columns" * dialog box is popped up. * \param limit The initial limit (or the empty string to ask the user to search). */ PkgView(const Glib::RefPtr &refGlade, const Glib::ustring &gladename, const Glib::ustring &parent_title, const Glib::ustring &limit, const sigc::slot > &build_progress_k); }; } #endif /* PKGVIEW_H_ */