From 33f4d63f357fc815fbb7ba63e7b89fa2c3296351 Mon Sep 17 00:00:00 2001 From: Obey Arthur Liu Date: Tue, 3 Jun 2008 14:06:18 +0200 Subject: add gtkmm to configure.ac --- configure.ac | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 5e32bcb9..4463288d 100644 --- a/configure.ac +++ b/configure.ac @@ -64,8 +64,10 @@ PKG_CHECK_MODULES(SIGC, sigc++-2.0) PKG_CHECK_MODULES(CWIDGET, cwidget) -CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $ept_CFLAGS" -LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $ept_LIBS" +PKG_CHECK_MODULES(GTKMM, gtkmm-2.4) + +CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GTKMM_CFLAGS $ept_CFLAGS" +LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GTKMM_LIBS $ept_LIBS" AC_DEFINE_UNQUOTED(SIGC_VERSION, ["$(pkg-config --modversion sigc++-2.0)"], [The version of libsigc++ with which the program was compiled]) dnl Checks for header files. -- cgit v1.2.3 From a0a27d25c3f313bfa70f90ab3378bd8c5483aec2 Mon Sep 17 00:00:00 2001 From: Obey Arthur Liu Date: Tue, 3 Jun 2008 18:03:14 +0200 Subject: Created gtk/ stub and autotools lines. --- configure.ac | 1 + src/Makefile.am | 4 ++-- src/gtk/Makefile.am | 7 +++++++ src/gtk/gui.cc | 23 +++++++++++++++++++++++ src/gtk/gui.h | 11 +++++++++++ src/main.cc | 2 +- 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/gtk/Makefile.am create mode 100644 src/gtk/gui.cc create mode 100644 src/gtk/gui.h (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 4463288d..8cee4831 100644 --- a/configure.ac +++ b/configure.ac @@ -326,6 +326,7 @@ AC_CONFIG_FILES([ m4/Makefile po/Makefile.in src/Makefile + src/gtk/Makefile src/cmdline/Makefile src/generic/Makefile src/generic/apt/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 9a182483..b5ae3135 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ MAINTAINERCLEANFILES=Makefile.in -SUBDIRS=generic cmdline mine +SUBDIRS=generic gtk cmdline mine localedir = $(datadir)/locale INCLUDES = -I$(top_builddir) -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src @@ -8,7 +8,7 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ bin_PROGRAMS=aptitude -LDADD=@LIBINTL@ cmdline/libcmdline.a mine/libcmine.a \ +LDADD=@LIBINTL@ gtk/libgtk.a cmdline/libcmdline.a mine/libcmine.a \ generic/util/libgeneric-util.a generic/apt/libgeneric-apt.a aptitude_SOURCES= \ diff --git a/src/gtk/Makefile.am b/src/gtk/Makefile.am new file mode 100644 index 00000000..05cc5e5a --- /dev/null +++ b/src/gtk/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES=-I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/intl + +noinst_LIBRARIES=libgtk.a + +libgtk_a_SOURCES=\ + gui.cc \ + gui.h diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc new file mode 100644 index 00000000..bbfaad7b --- /dev/null +++ b/src/gtk/gui.cc @@ -0,0 +1,23 @@ +#include "gui.h" + +#undef OK +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + + +using namespace std; + +void gui_init() +{ + Gtk::Main kit(); + Gtk::Window window; + Gtk::Main::run(window); +} + +void gui_main() +{ + // +} diff --git a/src/gtk/gui.h b/src/gtk/gui.h new file mode 100644 index 00000000..99508a77 --- /dev/null +++ b/src/gtk/gui.h @@ -0,0 +1,11 @@ +#ifndef GUI_H_ +#define GUI_H_ + +#undef OK +#include + +void gui_init(); + +void gui_main(); + +#endif /*GUI_H_*/ diff --git a/src/main.cc b/src/main.cc index b6bd3b6e..735b5fff 100644 --- a/src/main.cc +++ b/src/main.cc @@ -66,7 +66,7 @@ #include #include -#include "ui.h" +#include "gtk/gui.h" #include "progress.h" #include "pkg_columnizer.h" -- cgit v1.2.3 From 4088bbdef2d6a619e9f6a70f97753d18d022d673 Mon Sep 17 00:00:00 2001 From: Obey Arthur Liu Date: Mon, 14 Jul 2008 04:50:12 +0200 Subject: Created stub glade interface and handling. --- configure.ac | 6 +- src/gtk/gui.cc | 45 +++++-- src/gtk/gui.h | 8 +- src/gtk/ui-main.glade | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 370 insertions(+), 19 deletions(-) create mode 100644 src/gtk/ui-main.glade (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 8cee4831..b24ca065 100644 --- a/configure.ac +++ b/configure.ac @@ -66,8 +66,10 @@ PKG_CHECK_MODULES(CWIDGET, cwidget) PKG_CHECK_MODULES(GTKMM, gtkmm-2.4) -CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GTKMM_CFLAGS $ept_CFLAGS" -LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GTKMM_LIBS $ept_LIBS" +PKG_CHECK_MODULES(LIBGLADEMM, libglademm-2.4) + +CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" +LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" AC_DEFINE_UNQUOTED(SIGC_VERSION, ["$(pkg-config --modversion sigc++-2.0)"], [The version of libsigc++ with which the program was compiled]) dnl Checks for header files. diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc index bbfaad7b..fd9d9901 100644 --- a/src/gtk/gui.cc +++ b/src/gtk/gui.cc @@ -1,23 +1,42 @@ #include "gui.h" -#undef OK -#include - #ifdef HAVE_CONFIG_H #include #endif +#undef OK +#include +#include -using namespace std; - -void gui_init() +namespace gui { - Gtk::Main kit(); - Gtk::Window window; - Gtk::Main::run(window); -} + //This is a list of global and unique widgets + Gtk::Window * pMainWindow; -void gui_main() -{ - // + void main(int argc, char *argv[]) + { + Gtk::Main kit(argc, argv); + // Use the basename of argv0 to find the Glade file. + // TODO: note that the .glade file will ultimately + // go to /usr/share/aptitude/glade or something, + // so a more general solution will be needed. + std::string argv0(argv[0]); + std::string argv0_path; + std::string::size_type last_slash = argv0.rfind('/'); + if(last_slash != std::string::npos) + { + while(last_slash > 0 && argv0[last_slash - 1] == '/') + --last_slash; + argv0_path = std::string(argv0, 0, last_slash); + } + else + argv0_path = '.'; + + const std::string glade_main_file = argv0_path + "/gtk/ui-main.glade"; + + //Loading the .glade file and widgets + Glib::RefPtr refXml = Gnome::Glade::Xml::create(glade_main_file); + refXml->get_widget("main_window", pMainWindow); + Gtk::Main::run(*pMainWindow); + } } diff --git a/src/gtk/gui.h b/src/gtk/gui.h index 99508a77..aab4bea8 100644 --- a/src/gtk/gui.h +++ b/src/gtk/gui.h @@ -1,11 +1,11 @@ #ifndef GUI_H_ #define GUI_H_ -#undef OK -#include +namespace gui +{ -void gui_init(); + void main(int argc, char *argv[]); -void gui_main(); +} #endif /*GUI_H_*/ diff --git a/src/gtk/ui-main.glade b/src/gtk/ui-main.glade new file mode 100644 index 00000000..57743a8f --- /dev/null +++ b/src/gtk/ui-main.glade @@ -0,0 +1,330 @@ + + + + + 800 + 600 + + + True + + + True + + + True + _Fichier + True + + + True + + + True + gtk-new + True + True + + + + + True + gtk-open + True + True + + + + + True + gtk-save + True + True + + + + + True + gtk-save-as + True + True + + + + + True + + + + + True + gtk-quit + True + True + + + + + + + + + True + É_dition + True + + + True + + + True + gtk-cut + True + True + + + + + True + gtk-copy + True + True + + + + + True + gtk-paste + True + True + + + + + True + gtk-delete + True + True + + + + + + + + + True + _Affichage + True + + + + + True + Aid_e + True + + + True + + + True + gtk-about + True + True + + + + + + + + + False + + + + + True + + + True + gtk-home + + + False + + + + + False + 1 + + + + + True + True + + + True + This is the dashboard + + + + + True + Dashboard + + + False + tab + + + + + True + This is the download view + + + 1 + + + + + True + Download + + + 1 + False + tab + + + + + True + This is the package tree + + + 2 + + + + + True + Package tree + + + 2 + False + tab + + + + + True + This is the package view + + + 3 + + + + + True + Package view + + + 3 + False + tab + + + + + True + This is the preview view + + + 4 + + + + + True + Preview + + + 4 + False + tab + + + + + True + This is the resolver view + + + 5 + + + + + True + Resolver + + + 5 + False + tab + + + + + 2 + + + + + True + True + + + 3 + + + + + 24 + True + + + 200 + True + + + + False + + + + + True + 2 + + + 1 + + + + + False + 4 + + + + + + -- cgit v1.2.3 From a0533f17c1fd4161bf30455e0aeb68ca9f960577 Mon Sep 17 00:00:00 2001 From: Obey Arthur Liu Date: Thu, 17 Jul 2008 01:16:55 +0200 Subject: Basic list updating working with statusbar updates. Glib autotools fix. --- configure.ac | 6 +- src/gtk/gui.cc | 240 +++++++++++++++++++++++++++++++++++++++++++++----- src/gtk/gui.h | 21 +++++ src/gtk/ui-main.glade | 21 ++++- 4 files changed, 265 insertions(+), 23 deletions(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index b24ca065..6ad3a5cf 100644 --- a/configure.ac +++ b/configure.ac @@ -64,12 +64,14 @@ PKG_CHECK_MODULES(SIGC, sigc++-2.0) PKG_CHECK_MODULES(CWIDGET, cwidget) +PKG_CHECK_MODULES(GLIBMM, glibmm-2.4) + PKG_CHECK_MODULES(GTKMM, gtkmm-2.4) PKG_CHECK_MODULES(LIBGLADEMM, libglademm-2.4) -CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" -LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" +CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GLIBMM_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" +LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GLIBMM_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" AC_DEFINE_UNQUOTED(SIGC_VERSION, ["$(pkg-config --modversion sigc++-2.0)"], [The version of libsigc++ with which the program was compiled]) dnl Checks for header files. diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc index 0c72f5a5..bbb85409 100644 --- a/src/gtk/gui.cc +++ b/src/gtk/gui.cc @@ -2,6 +2,8 @@ #include #endif +#include "gui.h" + #include #include "aptitude.h" @@ -10,10 +12,46 @@ #include #include +#include + +#include <../generic/apt/apt.h> +#include <../generic/apt/apt_undo_group.h> +#include <../generic/apt/matchers.h> +#include <../generic/apt/download_install_manager.h> +#include <../generic/apt/download_update_manager.h> +#include <../generic/apt/aptitude_resolver_universe.h> +#include <../generic/apt/resolver_manager.h> +#include <../generic/problemresolver/exceptions.h> +#include <../generic/problemresolver/solution.h> +//#include <../main.h> +#include <../progress.h> +#include <../generic/util/util.h> + +#include + +#include + +typedef generic_solution aptitude_solution; + namespace gui { //This is a list of global and unique base widgets and other related stuff Glib::RefPtr refXml; + AptitudeWindow * pMainWindow; + std::string glade_main_file; + + // True if a download or package-list update is proceeding. This hopefully will + // avoid the nasty possibility of collisions between them. + // FIXME: uses implicit locking -- if multithreading happens, should use a mutex + // instead. + static bool active_download; + static bool want_to_quit = false; + + void gtk_update() + { + while (Gtk::Main::events_pending()) + Gtk::Main::iteration(); + } /** * This is a list of tab types. @@ -116,34 +154,186 @@ namespace gui TabsManager * pMainNotebook; /** - * This is the main Aptitude custom window widget. + * Adds a dashboard tab to the interface. + * TODO: Get this one out of here! + */ + void tab_add_dashboard() + { + Tab * tab; + Glib::RefPtr refLocalXml = Gnome::Glade::Xml::create(glade_main_file, "label1"); + refLocalXml->get_widget_derived("label1", tab); + tab->set_metadata("truc", DashboardTab); + pMainNotebook->set_current_page(pMainNotebook->append_page(*tab)); + } + + /** + * Adds a download tab to the interface. + * TODO: Get this one out of here! */ - class AptitudeWindow : public Gtk::Window + Tab * tab_add_download() { + Tab * tab; + Glib::RefPtr refLocalXml = Gnome::Glade::Xml::create(glade_main_file, "label2"); + refLocalXml->get_widget_derived("label2", tab); + tab->set_metadata("truc download", DownloadTab); + pMainNotebook->set_current_page(pMainNotebook->append_page(*tab)); + return tab; + } + + class guiOpProgress : public OpProgress + { // must derive to read protected member.. + private: + float sanitizePercentFraction(float percent) + { + float rval = percent / 100; + if (percent < 0) + rval = 0; + if (percent > 1) + rval = 1; + return rval; + } public: - Gtk::ToolButton * pToolButtonDashboard; - /** - * Glade::Xml derived widget constructor. - */ - AptitudeWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade) : Gtk::Window(cobject) + ~guiOpProgress() + { + pMainWindow->pProgressBar->set_text(""); + pMainWindow->pProgressBar->set_fraction(0); + } + void Update() { - refGlade->get_widget("main_toolbutton_dashboard", pToolButtonDashboard); + if (CheckChange(0.25)) + { + pMainWindow->pProgressBar->set_text(Op); + pMainWindow->pProgressBar->set_fraction(sanitizePercentFraction(Percent)); + gtk_update(); + } } }; - AptitudeWindow * pMainWindow; + guiOpProgress * gen_progress_bar() + { + return new guiOpProgress; + } - /** - * Adds a dashboard tab to the interface. - * TODO: Get this one out of here! - */ - void tab_add_dashboard() + void check_apt_errors() + { + string currerr, tag; + while (!_error->empty()) + { + bool iserr = _error->PopMessage(currerr); + if (iserr) + tag = "E:"; + else + tag = "W:"; + + Gtk::MessageDialog dialog(*pMainWindow, "There's a problem with apt...", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, + true); + dialog.set_secondary_text(tag + currerr); + dialog.run(); + } + } + + class guiPkgAcquireStatus : public pkgAcquireStatus + { // must also derive to read protected members.. + public: + bool Pulse(pkgAcquire *Owner) + { + pkgAcquireStatus::Pulse(Owner); + if (TotalItems != 0) + pMainWindow->pProgressBar->set_fraction(((float)CurrentItems)/((float)TotalItems)); + pMainWindow->pProgressBar->set_text(ssprintf("%lu of %lu done", CurrentItems, TotalItems)); + gtk_update(); + return !want_to_quit; + } + bool MediaChange(std::string, std::string) + { + return false; + } + void Fetch(pkgAcquire::ItemDesc &Itm) + { + std::cout << Itm.Description << std::endl; + + pMainWindow->pStatusBar->pop(0); + pMainWindow->pStatusBar->push(Itm.Description, 0); + + /*Gtk::TreeModel::iterator iter = pdownloadstore->append(); + Gtk::TreeModel::Row row = *iter; + row[downloadcolumns.URI] = Itm.URI; + row[downloadcolumns.ShortDesc] = Itm.ShortDesc; + row[downloadcolumns.Description] = Itm.Description;*/ + gtk_update(); + } + }; + + void really_do_update_lists() + { + download_update_manager *m = new download_update_manager; + + // downloading now I suppose ? + guiOpProgress progress; + guiPkgAcquireStatus acqlog; + acqlog.Update = true; + acqlog.MorePulses = true; + if (m->prepare(progress, acqlog, NULL)) + { + std::cout << "m->prepare succeeded" << std::endl; + } + else + { + std::cout << "m->prepare failed" << std::endl; + return; + } + acqlog.Update = true; + acqlog.MorePulses = true; + m->do_download(100); + m->finish(pkgAcquire::Continue, progress); + guiOpProgress * p = gen_progress_bar(); + apt_load_cache(p, true, NULL); + delete p; + } + + void do_update_lists(Tab * tab) + { + if (!active_download) + { + if (getuid()==0) + { + pMainWindow->pProgressBar->set_text("Updating.."); + pMainWindow->pProgressBar->set_fraction(0); + //pdownloadstore->clear(); + really_do_update_lists(); + pMainWindow->pProgressBar->set_fraction(0); + pMainWindow->pStatusBar->pop(0); + } + else + { + Gtk::MessageDialog dialog(*pMainWindow, + "There's a problem with you not being root...", false, + Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); + dialog.set_secondary_text("You're supposed to be a super-user to be allowed to break stuff you know ?"); + + dialog.run(); + } + } + else + std::cout << "A package-list update or install run is already taking place." + << std::endl; + } + + void do_update() { - Tab * dashboard; - refXml->get_widget_derived("label1", dashboard); - dashboard->set_metadata("truc", DashboardTab); - pMainNotebook->set_current_page(pMainNotebook->append_page(*dashboard)); + Tab * tab = tab_add_download(); + do_update_lists(tab); + } + AptitudeWindow::AptitudeWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade) : Gtk::Window(cobject) + { + refGlade->get_widget("main_toolbutton_dashboard", pToolButtonDashboard); + pToolButtonDashboard->signal_clicked().connect(&tab_add_dashboard); + refGlade->get_widget("main_toolbutton_update", pToolButtonUpdate); + pToolButtonUpdate->signal_clicked().connect(&do_update); + refGlade->get_widget("main_progressbar", pProgressBar); + refGlade->get_widget("main_statusbar", pStatusBar); + pStatusBar->push("Aptitude-gtk v2", 0); } void main(int argc, char *argv[]) @@ -165,15 +355,25 @@ namespace gui else argv0_path = '.'; - const std::string glade_main_file = argv0_path + "/gtk/ui-main.glade"; + glade_main_file = argv0_path + "/gtk/ui-main.glade"; //Loading the .glade file and widgets refXml = Gnome::Glade::Xml::create(glade_main_file); + refXml->get_widget_derived("main_window", pMainWindow); - pMainWindow->pToolButtonDashboard->signal_clicked().connect(&tab_add_dashboard); refXml->get_widget_derived("main_notebook", pMainNotebook); + // TODO: this is unnecessary if consume_errors is connected for the GUI. + check_apt_errors(); + + guiOpProgress * p=gui::gen_progress_bar(); + char *status_fname=NULL; + apt_init(p, true, status_fname); + if(status_fname) + free(status_fname); + check_apt_errors(); + delete p; //This is the loop Gtk::Main::run(*pMainWindow); } diff --git a/src/gtk/gui.h b/src/gtk/gui.h index aab4bea8..23c80b4f 100644 --- a/src/gtk/gui.h +++ b/src/gtk/gui.h @@ -1,9 +1,30 @@ #ifndef GUI_H_ #define GUI_H_ +#undef OK +#include +#include + namespace gui { + /** + * This is the main Aptitude custom window widget. + */ + class AptitudeWindow : public Gtk::Window + { + private: + Gtk::ToolButton * pToolButtonDashboard; + Gtk::ToolButton * pToolButtonUpdate; + public: + Gtk::ProgressBar * pProgressBar; + Gtk::Statusbar * pStatusBar; + /** + * Glade::Xml derived widget constructor. + */ + AptitudeWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade)/* : Gtk::Window(cobject)*/; + }; + void main(int argc, char *argv[]); } diff --git a/src/gtk/ui-main.glade b/src/gtk/ui-main.glade index e8f03de2..cbf2e46f 100644 --- a/src/gtk/ui-main.glade +++ b/src/gtk/ui-main.glade @@ -1,6 +1,5 @@ - 800 600 @@ -149,12 +148,32 @@ True + Dashboard gtk-home False + + + True + + + False + False + + + + + True + Update + gtk-refresh + + + False + + False -- cgit v1.2.3 From 9f871d2fccb8a8fd07e7db3bded40f47d47fc3cb Mon Sep 17 00:00:00 2001 From: Obey Arthur Liu Date: Sat, 26 Jul 2008 21:54:22 +0200 Subject: Improved responsivity by threading out packages sorting and dpkg run using Glib::Thread. --- configure.ac | 6 ++-- src/gtk/gui.cc | 109 +++++++++++++++++++++++++++++++++++++++++---------------- src/gtk/gui.h | 4 ++- 3 files changed, 85 insertions(+), 34 deletions(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 6ad3a5cf..514f9dc1 100644 --- a/configure.ac +++ b/configure.ac @@ -66,12 +66,14 @@ PKG_CHECK_MODULES(CWIDGET, cwidget) PKG_CHECK_MODULES(GLIBMM, glibmm-2.4) +PKG_CHECK_MODULES(GTHREAD, gthread-2.0) + PKG_CHECK_MODULES(GTKMM, gtkmm-2.4) PKG_CHECK_MODULES(LIBGLADEMM, libglademm-2.4) -CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GLIBMM_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" -LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GLIBMM_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" +CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GLIBMM_CFLAGS $GTHREAD_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" +LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GLIBMM_LIBS $GTHREAD_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" AC_DEFINE_UNQUOTED(SIGC_VERSION, ["$(pkg-config --modversion sigc++-2.0)"], [The version of libsigc++ with which the program was compiled]) dnl Checks for header files. diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc index f8d542e8..34b64ffe 100644 --- a/src/gtk/gui.cc +++ b/src/gtk/gui.cc @@ -61,12 +61,12 @@ namespace gui Gtk::Main::iteration(); } - float guiOpProgress::sanitizePercentFraction(float percent) + double guiOpProgress::sanitizePercentFraction(float percent) { - float rval = percent / 100; - if (percent < 0) + double rval = (double)percent / 100; + if (rval < 0) rval = 0; - if (percent > 1) + if (rval > 1) rval = 1; return rval; } @@ -79,7 +79,7 @@ namespace gui void guiOpProgress::Update() { - if (CheckChange(0.25)) + if (CheckChange(0.02)) { pMainWindow->get_progress_bar()->set_text(Op); pMainWindow->get_progress_bar()->set_fraction(sanitizePercentFraction(Percent)); @@ -489,11 +489,6 @@ namespace gui p->OverallProgress(num, total, 1, _("Building view")); ++num; - if (num % 1000 == 0) - { - gtk_update(); - pMainWindow->get_progress_bar()->pulse(); - } // Filter useless packages up-front. if(pkg.VersionList().end() && pkg.ProvidesList().end()) @@ -506,11 +501,24 @@ namespace gui } } } - gtk_update(); - generator->finish(); - gtk_update(); - p->OverallProgress(total, total, 1, _("Building view")); + p->OverallProgress(total, total, 1, _("Finalizing view")); + + Glib::Timer * time = new Glib::Timer::Timer(); + Glib::Thread * sort_thread = Glib::Thread::create(sigc::mem_fun(*generator, &PackagesTreeModelGenerator::finish), true); + while(!generator->finished) + { + if(time->elapsed() > 0.05) + { + time->reset(); + pMainWindow->get_progress_bar()->pulse(); + gtk_update(); + } + } + sort_thread->join(); + + //generator->finish(); + delete p; return generator->get_model(); @@ -581,11 +589,7 @@ namespace gui p->OverallProgress(num, total, 1, _("Building view")); ++num; - if (num % 10 == 0) - { - gtk_update(); - pMainWindow->get_progress_bar()->pulse(); - } + std::pair::iterator, std::multimap::iterator> reverse_range = reverse_packages_store->equal_range(*pkg); @@ -623,6 +627,8 @@ namespace gui private: PackagesTabGenerator(PackagesColumns *_packages_columns) { + // FIXME: Hack while finding a nonblocking thread join. + finished = false; packages_columns = _packages_columns; store = Gtk::ListStore::create(*packages_columns); } @@ -641,12 +647,6 @@ namespace gui return new PackagesTabGenerator(packages_columns); } - void init(PackagesColumns *_packages_columns) - { - packages_columns = _packages_columns; - store = Gtk::ListStore::create(*packages_columns); - } - void add(const pkgCache::PkgIterator &pkg, const pkgCache::VerIterator &ver, std::multimap * reverse_packages_store) { @@ -667,6 +667,8 @@ namespace gui void finish() { store->set_sort_column(packages_columns->Name, Gtk::SORT_ASCENDING); + // FIXME: Hack while finding a nonblocking thread join. + finished = true; } Glib::RefPtr get_model() @@ -760,6 +762,8 @@ namespace gui private: PreviewTabGenerator(PackagesColumns *_packages_columns) { + // FIXME: Hack while finding a nonblocking thread join. + finished = false; packages_columns = _packages_columns; store = Gtk::TreeStore::create(*packages_columns); } @@ -816,6 +820,8 @@ namespace gui void finish() { store->set_sort_column(packages_columns->Name, Gtk::SORT_ASCENDING); + // FIXME: Hack while finding a nonblocking thread join. + finished = true; } Glib::RefPtr get_model() @@ -1339,11 +1345,37 @@ namespace gui class InstallRemoveTab : public DownloadTab { + // FIXME: Hack while finding a nonblocking thread join or something else. + bool finished; public: InstallRemoveTab(Glib::ustring &label) : DownloadTab(label) { - ;; + // FIXME: Hack while finding a nonblocking thread join or something else. + finished = false; + } + void handle_result(pkgPackageManager::OrderResult result) + { + Gtk::MessageDialog dialog(*pMainWindow, "Install run finished", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, + true); + switch(result) + { + case pkgPackageManager::Completed: + dialog.set_secondary_text("Successfully completed!"); + break; + case pkgPackageManager::Incomplete: + dialog.set_secondary_text("Partially completed!"); + break; + case pkgPackageManager::Failed: + dialog.set_secondary_text("Failed!"); + break; + } + dialog.run(); + } + void handle_install(download_install_manager *m, OpProgress &progress) + { + m->finish(pkgAcquire::Continue, progress); + finished = true; } void install_or_remove_packages() { @@ -1365,12 +1397,26 @@ namespace gui acqlog.Update = true; acqlog.MorePulses = true; download_store->clear(); + m->do_download(100); - m->finish(pkgAcquire::Continue, progress); - Gtk::MessageDialog dialog(*pMainWindow, "Install run finished", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, - true); - dialog.set_secondary_text("It may or may not have worked. Who knows?"); - dialog.run(); + + // FIXME: Hack while finding a nonblocking thread join or something else. + Glib::Timer * time = new Glib::Timer::Timer(); + Glib::Thread * install_thread = + Glib::Thread::create(sigc::bind(sigc::mem_fun(*this, &InstallRemoveTab::handle_install), m, progress), true); + m->post_install_hook.connect(sigc::mem_fun(*this, &InstallRemoveTab::handle_result)); + while(!finished) + { + if(time->elapsed() > 0.05) + { + time->reset(); + pMainWindow->get_progress_bar()->pulse(); + gtk_update(); + } + } + install_thread->join(); + + //m->finish(pkgAcquire::Continue, progress); } }; @@ -1566,6 +1612,7 @@ namespace gui void main(int argc, char *argv[]) { + Glib::thread_init(); pKit = new Gtk::Main(argc, argv); Gtk::Main::signal_quit().connect(&do_want_quit); // Use the basename of argv0 to find the Glade file. diff --git a/src/gtk/gui.h b/src/gtk/gui.h index 22f62111..a40a0ee0 100644 --- a/src/gtk/gui.h +++ b/src/gtk/gui.h @@ -38,7 +38,7 @@ namespace gui class guiOpProgress : public OpProgress { // must derive to read protected member.. private: - float sanitizePercentFraction(float percent); + double sanitizePercentFraction(float percent); public: ~guiOpProgress(); void Update(); @@ -163,6 +163,8 @@ namespace gui class PackagesTreeModelGenerator { public: + // FIXME: Hack while finding a nonblocking thread join. + bool finished; virtual ~PackagesTreeModelGenerator(); /** \brief Add the given package and version to this tree view. -- cgit v1.2.3 From c3f585578546834e1335f83bb5f7b4973a1578e2 Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Mon, 1 Sep 2008 16:47:52 -0700 Subject: Make the GTK+ frontend an optional part of the build (but enabled by default). Changed the configure and build procedure to make the libraries for the GTK+ frontend optional, only compile the GTK+ frontend if the libraries are present, and allow the GTK+ frontend to be disabled even if they are present via --disable-gtk. This is needed so that we can build a non-GUI version of the program for use in the base system. --- configure.ac | 42 ++++++++++++++++++++++++++++++++++++++---- src/Makefile.am | 9 +++++++-- src/main.cc | 11 +++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 514f9dc1..514325e6 100644 --- a/configure.ac +++ b/configure.ac @@ -64,13 +64,47 @@ PKG_CHECK_MODULES(SIGC, sigc++-2.0) PKG_CHECK_MODULES(CWIDGET, cwidget) -PKG_CHECK_MODULES(GLIBMM, glibmm-2.4) +HAVE_GTK=1 +AC_ARG_ENABLE(gtk, + AS_HELP_STRING(--disable-gtk, [don't compile the GTK+/gtkmm frontend even if the appropriate libraries are available]), + [if test x$withval = xyes + then + HAVE_GTK=1 + else + HAVE_GTK= + fi]) + +WANT_HAVE_GTK=$HAVE_GTK -PKG_CHECK_MODULES(GTHREAD, gthread-2.0) +dnl We always test all the libraries so that the user gets to see +dnl which libraries are needed for GTK+. +if test x$HAVE_GTK = x1 +then + AC_MSG_NOTICE([Testing for the libraries needed to build the GTK+ frontend.]) -PKG_CHECK_MODULES(GTKMM, gtkmm-2.4) + PKG_CHECK_MODULES(GLIBMM, glibmm-2.4,,[HAVE_GTK=]) + PKG_CHECK_MODULES(GTHREAD, gthread-2.0,,[HAVE_GTK=]) + PKG_CHECK_MODULES(GTKMM, gtkmm-2.4,,[HAVE_GTK=]) + PKG_CHECK_MODULES(LIBGLADEMM, libglademm-2.4,,[HAVE_GTK=]) +else + AC_MSG_NOTICE([Disabling the GTK+ frontend at your request (--disable-gtk).]) +fi -PKG_CHECK_MODULES(LIBGLADEMM, libglademm-2.4) +if test x$WANT_HAVE_GTK = x1 && test x$HAVE_GTK != x1 +then + AC_MSG_WARN([Unable to find the necessary GTK+ libraries; disabling GTK+ support.]) +fi + +if test x$HAVE_GTK = x1 +then + AC_MSG_NOTICE([The GTK+ frontend will be built.]) +fi + +AM_CONDITIONAL([BUILD_GTK], [test x$HAVE_GTK = x1]) +if test x$HAVE_GTK = x1 +then + AC_DEFINE([HAVE_GTK], [], [Define if the GTK+ frontend is included in the build.]) +fi CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GLIBMM_CFLAGS $GTHREAD_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GLIBMM_LIBS $GTHREAD_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" diff --git a/src/Makefile.am b/src/Makefile.am index b5ae3135..65f553be 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,11 @@ MAINTAINERCLEANFILES=Makefile.in -SUBDIRS=generic gtk cmdline mine +if BUILD_GTK + MAYBE_GTK = gtk + MAYBE_LIBGTK = gtk/libgtk.a +endif + +SUBDIRS=generic $(MAYBE_GTK) cmdline mine localedir = $(datadir)/locale INCLUDES = -I$(top_builddir) -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src @@ -8,7 +13,7 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ bin_PROGRAMS=aptitude -LDADD=@LIBINTL@ gtk/libgtk.a cmdline/libcmdline.a mine/libcmine.a \ +LDADD=@LIBINTL@ $(MAYBE_LIBGTK) cmdline/libcmdline.a mine/libcmine.a \ generic/util/libgeneric-util.a generic/apt/libgeneric-apt.a aptitude_SOURCES= \ diff --git a/src/main.cc b/src/main.cc index be7d4776..2c700184 100644 --- a/src/main.cc +++ b/src/main.cc @@ -66,7 +66,9 @@ #include #include +#ifdef HAVE_GTK #include "gtk/gui.h" +#endif #include "progress.h" #include "pkg_columnizer.h" @@ -254,8 +256,10 @@ option opts[]={ {"remove-user-tag-from", 1, &getopt_result, OPTION_REMOVE_USER_TAG_FROM}, {"arch-only", 0, &getopt_result, OPTION_ARCH_ONLY}, {"not-arch-only", 0, &getopt_result, OPTION_NOT_ARCH_ONLY}, +#ifdef HAVE_GTK {"gui", 0, &getopt_result, OPTION_GUI}, {"no-gui", 0, &getopt_result, OPTION_NO_GUI}, +#endif {0,0,0,0} }; @@ -317,8 +321,10 @@ int main(int argc, char *argv[]) int quiet = 0; std::vector user_tags; +#ifdef HAVE_GTK // TODO: this should be a configuration option. bool gui = true; +#endif int curopt; // The last option seen @@ -548,12 +554,14 @@ int main(int argc, char *argv[]) case OPTION_DISABLE_COLUMNS: disable_columns = true; break; +#ifdef HAVE_GTK case OPTION_GUI: gui = true; break; case OPTION_NO_GUI: gui = false; break; +#endif default: fprintf(stderr, "%s", _("WEIRDNESS: unknown option code received\n")); @@ -751,12 +759,15 @@ int main(int argc, char *argv[]) } } +#ifdef HAVE_GTK if(gui) { gui::main(argc, argv); return 0; + fprintf(stderr, "Internal error: the non-GTK+ build should never try to activate the GUI."); } else +#endif { ui_init(); -- cgit v1.2.3 From 99ffa86229344212e8b5b801b357c26d42a60788 Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Wed, 3 Sep 2008 10:50:26 -0700 Subject: Start working on a rewrite of the matching code: implemented the core structure to represent search patterns. The matching code has been due for a rewrite for a long time. There are two goals in this rewrite: (1) Make the matchers not a black box, so that the various frontends can provide interactive editing of search patterns, so that it's easier to write new analysis and/or execution functions on search patterns, etc. The specific mechanism I chose is to make the pattern type be a "poor man's algebraic datatype": a single data type that can represent all the legal forms of a search pattern. This flips us to the other side of the expression problem; since the search expressions are actually a programming language, this is a more appropriate place to be. (2) Add support for Xapian. (3) Don't put all the code into one enormous file. (2) isn't necessarily dependent on (1), but if I did (2) first, I would have to do even more work to do (1). This commit is the first step in this direction, implementing the core pattern data type (untested). --- configure.ac | 1 + src/Makefile.am | 3 +- src/generic/apt/Makefile.am | 2 + src/generic/apt/matching/Makefile.am | 12 + src/generic/apt/matching/pattern.h | 1692 ++++++++++++++++++++++++++++++++++ 5 files changed, 1709 insertions(+), 1 deletion(-) create mode 100644 src/generic/apt/matching/Makefile.am create mode 100644 src/generic/apt/matching/pattern.h (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 514325e6..2009ebc5 100644 --- a/configure.ac +++ b/configure.ac @@ -370,6 +370,7 @@ AC_CONFIG_FILES([ src/cmdline/Makefile src/generic/Makefile src/generic/apt/Makefile + src/generic/apt/matching/Makefile src/generic/problemresolver/Makefile src/generic/util/Makefile src/mine/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 65f553be..2c732c61 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,8 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ bin_PROGRAMS=aptitude LDADD=@LIBINTL@ $(MAYBE_LIBGTK) cmdline/libcmdline.a mine/libcmine.a \ - generic/util/libgeneric-util.a generic/apt/libgeneric-apt.a + generic/util/libgeneric-util.a generic/apt/libgeneric-apt.a \ + generic/apt/matching/libgeneric-matching.a aptitude_SOURCES= \ aptitude.h \ diff --git a/src/generic/apt/Makefile.am b/src/generic/apt/Makefile.am index d3eaa71b..f70a6b42 100644 --- a/src/generic/apt/Makefile.am +++ b/src/generic/apt/Makefile.am @@ -1,5 +1,7 @@ MAINTAINERCLEANFILES = Makefile.in +SUBDIRS = matching + localedir = $(datadir)/locale INCLUDES = -I$(top_builddir) -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ diff --git a/src/generic/apt/matching/Makefile.am b/src/generic/apt/matching/Makefile.am new file mode 100644 index 00000000..5421afb4 --- /dev/null +++ b/src/generic/apt/matching/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINERCLEANFILES = Makefile.in + +localedir = $(datadir)/locale +INCLUDES = -I$(top_builddir) -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src +DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ +LDADD = @LIBINTL@ + +noinst_LIBRARIES = libgeneric-matching.a + +libgeneric_matching_a_SOURCES = \ + pattern.cc \ + pattern.h \ No newline at end of file diff --git a/src/generic/apt/matching/pattern.h b/src/generic/apt/matching/pattern.h new file mode 100644 index 00000000..51974da0 --- /dev/null +++ b/src/generic/apt/matching/pattern.h @@ -0,0 +1,1692 @@ +// pattern.h -*-c++-*- +// +// Copyright (C) 2008 Daniel Burrows +// +// 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. + +/// \file pattern.h + +#include + +#include + +#include +#include + +#include +#include + +#include + +namespace aptitude +{ + namespace matching + { + /** \brief An exception related to the matching code. + */ + class MatchingException : public cwidget::util::Exception + { + std::string msg; + + public: + /** \brief Create a matching exception. + * + * \param _msg The error message associated with this exception. + */ + MatchingException(const std::string &_msg); + + /** \brief Retrieve the error message associated with this exception. */ + std::string errmsg() const; + }; + + /** \brief C++ wrapper for regular expression objects. + * + * This class turns compilation errors into exceptions and + * handles safely disposing of the pattern when it's destroyed. + */ + class regex : public util::refcounted_base + { + regex_t r; + + // Forbid copy-construction. + regex(const regex &); + + public: + /** \brief Compile a regular expression. + * + * \param pattern The text of the pattern to compile. + * \param cflags The compilation flags (see regex(3)). + * + * \throw MatchingException if compilation fails. + */ + regex(const std::string &pattern, int cflags); + ~regex(); + + /** \brief Execute the regular expression. + * + * \param s The string to match into. + * \param matches The array in which to store group match + * information, or NULL to not store it. + * \param num_matches The number of entries in matches. + * \param eflags The execution flags (see regex(3)). + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const char *s, regmatch_t *matches, size_t num_matches, + int eflags) const; + + /** \brief Execute the regular expression. + * + * \param s The string to match into. + * \param matches The array in which to store group match + * information, or NULL to not store it. + * \param num_matches The number of entries in matches. + * \param eflags The execution flags (see regex(3)). + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const std::string &s, regmatch_t *matches, size_t num_matches, + int eflags) const + { + return exec(s.c_str(), matches, num_matches, eflags); + } + + /** \brief Execute the regular expression with no execution flags. + * + * \param s The string to match into. + * \param matches The array in which to store group match + * information, or NULL to not store it. + * \param num_matches The number of entries in matches. + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const char *s, regmatch_t *matches, size_t num_matches) const + { + return exec(s, matches, num_matches, 0); + } + + /** \brief Execute the regular expression with no execution flags. + * + * \param s The string to match into. + * \param matches The array in which to store group match + * information, or NULL to not store it. + * \param num_matches The number of entries in matches. + * \param eflags The execution flags (see regex(3)). + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const std::string &s, regmatch_t *matches, size_t num_matches) const + { + return exec(s.c_str(), matches, num_matches); + } + + /** \brief Execute the regular expression without retrieving group information. + * + * \param s The string to match into. + * \param eflags The execution flags (see regex(3)). + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const char *s, int eflags) const + { + return exec(s, NULL, 0, eflags); + } + + /** \brief Execute the regular expression without retrieving group information. + * + * \param s The string to match into. + * \param eflags The execution flags (see regex(3)). + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const std::string &s, int eflags) const + { + return exec(s.c_str(), eflags); + } + + /** \brief Execute the regular expression without retrieving group information + * and with no execution flags. + * + * \param s The string to match into. + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const char *s) const + { + return exec(s, NULL, 0); + } + + /** \brief Execute the regular expression without retrieving group information + * and with no execution flags. + * + * \param s The string to match into. + * + * \return \b true if the expression matched and \b false otherwise. + */ + bool exec(const std::string &s) const + { + return exec(s.c_str()); + } + }; + + /** \brief Represents a single node in a match expression. + * + * This is a "poor man's algebraic datatype". It provides one + * named constructor for each form of a match expression; to use + * a value of this type, switch on the type tag (get_type()) and + * use the accessors corresponding to the appropriate type + * (get_TYPE_FIELD()). + * + * \nosubgrouping + */ + class pattern : public util::refcounted_base + { + public: + /** \name Term types */ + + /** \brief Represents the type of a term. + * + * Normally this is just the undecorated name, but in a few + * cases (when the name conflicts with a C++ reserved word) + * _tp is appended. + */ + enum type + { + /** \brief ?archive(PATTERN) + * + * Matches packages by their archive. + * + * Fields: regex_info. + */ + archive, + /** \brief ?action(ACTION) + * + * Matches packages by their action flag. + * + * Fields: action_info, action_text. + */ + action, + /** \brief ?all-versions(PATTERN) + * + * Matches a package if all its versions match the given PATTERN. + * + * Fields: pattern. + */ + all_versions, + /** \brief ?any-version(PATTERN) + * + * Matches a package if any of its versions matches the given PATTERN. + * + * Fields: pattern. + */ + any_version, + /** \brief ?automatic + * + * Matches packages that were automatically installed. + */ + automatic, + /** \brief ?and(PATTERN, ...) + * + * Matches packages if none of its patterns fail. + * + * Fields: patterns. + */ + and_tp, + /** \brief ?bind(X, PATTERN) + * + * Matches if PATTERN matches the value of X. + * + * Fields: variable_name, pattern. + */ + bind, + /** \brief ?broken + * + * Matches broken packages. + */ + broken, + /** \brief ?broken-TYPE + * + * Matches packages with a particular type of broken + * dependency. + */ + broken_type, + /** \brief ?version(CANDIDATE) + * + * Matches the candidate version of a package. + */ + candidate_version, + /** \brief ?config-files + * + * Matches packages that were removed but that still have + * config files present. + */ + config_files, + /** \brief ?version(CURRENT) + * + * Matches the current version of a package. + */ + current_version, + /** \brief ?DEPENDS-TYPE(PATTERN), ?broken-DEPENDS-TYPE(PATTERN) + * + * Matches packages with a dependency of the type + * DEPENDS-TYPE on a package matching PATTERN. If "broken" + * is true, only broken dependencies are considered. + * + * Fields: depends_type, pattern, broken. + */ + depends, + /** \brief ?description + * + * Matches packages by their Description field. + * + * Fields: regex_info. + */ + description, + /** \brief ?essential + * + * Matches Essential packages. + */ + essential, + /** \brief ?=X + * + * Matches packages/versions that equal the given stack position. + * + * Fields: variable_name, stack_position. + */ + equal, + /** \brief ?false + * + * Matches nothing. + */ + false_tp, + /** \brief ?for X: PATTERN + * + * Matches packages if PATTERN matches with "X" bound to that package. + * + * Fields: variable_name, pattern. + */ + for_tp, + /** \brief ?garbage + * + * Matches packages that are not required by any manually + * installed package. + */ + garbage, + /** \brief ?version(TARGET) + * + * Matches the to-be-installed version of a package. + */ + install_version, + /** \brief ?installed + * + * Matches installed packages/versions. + */ + installed, + /** \brief ?action(keep) + * + * Matches packages that no action is being taken on. + */ + keep, + /** \brief ?maintainer(PATTERN) + * + * Matches packages by their Maintainer field. + * + * Fields: regex_info. + */ + maintainer, + /** \brief ?name(PATTERN) + * + * Matches packages by their name. + * + * Fields: regex_info. + */ + name, + /** \brief ?narrow(FILTER, PATTERN) + * + * Matches packages if a version matching filter matches PATTERN. + * + * Fields: filter, pattern + */ + narrow, + /** \brief ?new + * + * Matches packages that are "new". + */ + new_tp, + /** \brief ?not(PATTERN) + * + * Matches packages if the given PATTERN fails to match. + * + * Fields: pattern. + */ + not_tp, + /** \brief ?obsolete + * + * Matches packages that are installed but that are not + * downloadable. + */ + obsolete, + /** \brief ?or(PATTERN, ...) + * + * Matches if at least one PATTERN matches. + * + * Fields: patterns. + */ + or_tp, + /** \brief ?origin(PATTERN) + * + * Matches packages by their origin. + * + * Fields: regex_info. + */ + origin, + /** \brief ?priority(PRIORITY) + * + * Matches packages by their priority. + * + * Fields: priority, priority_name. + */ + priority, + /** \brief ?provides(PATTERN) + * + * Matches packages that provide a package matching the + * given pattern. + * + * Fields: pattern. + */ + provides, + /** \brief ?reverse-TYPE(PATTERN), ?reverse-broken-TYPE(PATTERN) + * + * Matches packages that have a reverse dependency of the + * given TYPE on a package matching PATTERN. If "broken" + * is set, only dependencies that are broken by the + * currently planned installation will be examined. + * + * Fields: type, pattern, broken. + */ + reverse_depends, + /** \brief ?reverse-provides(PATTERN), ?provided-by(PATTERN) + * + * Matches packages that are provided by a package matching + * PATTERN. + * + * Fields: pattern. + */ + reverse_provides, + /** \brief ?section(PATTERN) + * + * Matches packages by their section. + * + * Fields: regex_info. + */ + section, + /** \brief ?source-package(PATTERN) + * + * Matches packages by their source package. + * + * Fields: regex_info. + */ + source_package, + /** \brief ?source-version(PATTERN) + * + * Matches packages by the version of their source package. + * + * Fields: regex_info. + */ + source_version, + /** \brief ?tag(PATTERN) + * + * Matches packages using debtags. + * + * Fields: regex_info. + */ + tag, + /** \brief ?task(PATTERN) + * + * Matches packages by their task. + * + * Fields: regex_info. + */ + task, + /** \brief ?true + * + * Matches everything. + */ + true_tp, + /** \brief ?upgradable + * + * Matches packages that are upgradable. + */ + upgradable, + /** \brief ?user-tag(PATTERN) + * + * Matches packages by their user tags. + * + * Fields: regex_info. + */ + user_tag, + /** \brief ?version(PATTERN) + * + * Matches packages by their version. + * + * Fields: regex_info. + */ + version, + /** \brief ?virtual + * + * Matches package versions that are not associated with a + * real package, or virtual packages, or package versions + * that correspond to removing a package. + */ + virtual_tp, + /** \brief ?widen(pattern) + * + * Matches packages and package versions if any version of + * the package matches PATTERN. + * + * Fields: pattern. + */ + widen + }; + + /** \brief Represents information about the regular expression + * associated with a pattern. + * + * Patterns that use regular expressions compile the regex + * twice, once with grouping enabled and once without (to allow + * quick matches in the case that we aren't retrieving group + * information). In addition, the text of the regular + * expression that the user entered is preserved. + */ + class regex_info + { + cwidget::util::ref_ptr regex_group; + cwidget::util::ref_ptr regex_nogroup; + + std::string regex_string; + + public: + /** \brief Create an empty regex_info structure. + * + * The regex_group and regex_nogroup fields will be NULL, and + * regex_string will be empty. + */ + regex_info() + { + } + + /** \brief Compile the given regular expression. + * + * \param _regex_string The text of the regular expression. + * + * \throw MatchingException if the regular expression cannot be + * compiled. + */ + regex_info(const std::string &_regex_string) + : regex_group(new regex(_regex_string, REG_ICASE|REG_EXTENDED)), + regex_nogroup(new regex(_regex_string, REG_ICASE|REG_EXTENDED|REG_NOSUB)), + regex_string(_regex_string) + { + } + + /** \brief Retrieve the regular expression, compiled with + * grouping enabled. + */ + const cwidget::util::ref_ptr &get_regex_group() const + { + return regex_group; + } + + /** \brief Retrieve the regular expression, compiled without + * grouping enabled. + */ + const cwidget::util::ref_ptr &get_regex_nogroup() const + { + return regex_nogroup; + } + + /** \brief Retrieve the original text of the regular expression. */ + const std::string &get_regex_string() const + { + return regex_string; + } + }; + + struct action_info + { + pkg_action_state state; + bool require_purge; + }; + private: + + // The type of this node. + type tp; + + regex_info regex_information; + + // The sub-patterns, if any. + std::vector > sub_patterns; + + // The variable name, if any, or the string corresponding + // to the action match information. + std::string string_info; + + // Groups several POD values that aren't used simultaneously. + union + { + struct + { + // The dependency type, if any. + pkgCache::Dep::DepType deptype; + + // Whether to match broken packages (if applicable). + bool broken; + } dep; + + // The stack position, if applicable. + size_t stack_position; + + // The action information, if applicable. + action_info action; + + // The priority, if applicable. + pkgCache::State::VerPriority priority; + } info; + + // Disallow copy-construction. + pattern(const pattern &other); + + // Allocate a pattern with no parameters. + pattern(type _tp) + : tp(_tp) + { + } + + // Allocate a pattern that has a regular expression. + pattern(type _tp, const regex_info &_regex_information) + : tp(_tp), + regex_information(_regex_information) + { + } + + pattern(type _tp, pkgCache::State::VerPriority priority) + : tp(_tp) + { + info.priority = priority; + } + + // Allocate a pattern that just has string information. + pattern(type _tp, + const std::string &_string_info) + : tp(_tp), string_info(_string_info) + { + } + + // Allocate a pattern that has sub-patterns. + template + pattern(type _tp, + Iter sub_patterns_start, Iter sub_patterns_end, + const std::string &_string_info = std::string()) + : tp(_tp), sub_patterns(sub_patterns_start, sub_patterns_end), + string_info(_string_info) + { + } + + // Allocate a pattern that has a single sub-pattern. + pattern(type _tp, + const cwidget::util::ref_ptr &p, + const std::string &_string_info = std::string()) + : tp(_tp), sub_patterns(&p, (&p) + 1), string_info(_string_info) + { + } + + // Allocate a pattern that has two sub-patterns. + pattern(type _tp, + const cwidget::util::ref_ptr &p1, + const cwidget::util::ref_ptr &p2, + const std::string &_string_info = std::string()) + : tp(_tp), string_info(_string_info) + { + sub_patterns.push_back(p1); + sub_patterns.push_back(p2); + } + + // Allocate a pattern that just has dependency info. + pattern(type _tp, + pkgCache::Dep::DepType deptype, bool broken) + : tp(_tp) + { + info.dep.deptype = deptype; + info.dep.broken = broken; + } + + // Allocate a pattern that has sub-patterns and dependency info. + template + pattern(type _tp, + Iter sub_patterns_start, Iter sub_patterns_end, + pkgCache::Dep::DepType deptype, bool broken) + : tp(_tp), sub_patterns(sub_patterns_start, sub_patterns_end) + { + info.dep.deptype = deptype; + info.dep.broken = broken; + } + + // Allocate a pattern that has stack position information. + pattern(type _tp, size_t _stack_position, + const std::string &_string_info) + : tp(_tp), string_info(_string_info) + { + info.stack_position = _stack_position; + } + + // Allocate a pattern that has package action information. + pattern(type _tp, pkg_action_state state, bool require_purge, + const std::string &_string_info) + : tp(_tp), string_info(_string_info) + { + info.action.state = state; + info.action.require_purge = require_purge; + } + + public: + + /** \name archive term constructor and accessors. */ + + // @{ + + /** \brief Create an ?archive term. + * + * \param s The regular expression to match against. + */ + static cwidget::util::ref_ptr + make_archive(const std::string &s) + { + return new pattern(archive, regex_info(s)); + } + + /** \brief Get the regex_info field of an ?archive term. */ + const regex_info &get_archive_regex_info() const + { + eassert(tp == archive); + + return regex_information; + } + + // @} + + /** \name action term constructor and accessors. */ + // @{ + + /** \brief Create an ?action term. + * + * \param s The action type to match. + */ + static cwidget::util::ref_ptr + make_action(const std::string &s); + + /** \brief Retrieve the information associated with an ?action + * term. + */ + const action_info &get_action_action_info() const + { + eassert(tp == action); + + return info.action; + } + + /** \brief Retrieve the name of the action being selected + * by an ?action term. + */ + const std::string &get_action_action_string() const + { + eassert(tp == action); + + return string_info; + } + + // @} + + /** \name all_versions term constructor and accessors. */ + // @{ + + /** \brief Create an ?all-versions term. + * + * \param p The sub-pattern. + */ + static cwidget::util::ref_ptr + make_all_versions(const cwidget::util::ref_ptr &p) + { + return new pattern(all_versions, p); + } + + /** \brief Retrieve the sub-pattern of an ?all-versions term. + */ + const cwidget::util::ref_ptr & + get_all_versions_pattern() const + { + eassert(tp == all_versions && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name any_version term constructor and accessors. */ + // @{ + + /** \brief Create an ?any-versions term. + * + * \param p The sub-pattern. + */ + static cwidget::util::ref_ptr + make_any_version(const cwidget::util::ref_ptr &p) + { + return new pattern(any_version, p); + } + + /** \brief Retrieve the sub-pattern of an ?any-version term. */ + const cwidget::util::ref_ptr & + get_any_version_pattern() const + { + eassert(tp == any_version && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name automatic term constructor. */ + + // @{ + + /** \brief Create an ?automatic term. */ + static cwidget::util::ref_ptr + make_automatic() + { + return new pattern(automatic); + } + + // @} + + /** \name and_tp term constructor and accessors. */ + + // @{ + + /** \brief Create an ?and term. + * + * \param sub_patterns_begin The beginning of the + * range of sub-patterns. + * \param sub_patterns_end The end of the range of + * sub-patterns. + */ + template + static cwidget::util::ref_ptr + make_and(Iter sub_patterns_begin, Iter sub_patterns_end) + { + return new pattern(sub_patterns_begin, sub_patterns_end); + } + + /** \brief Retrieve the sub-patterns of an ?and term. */ + const std::vector > &get_and_patterns() const + { + eassert(tp == and_tp); + + return sub_patterns; + } + + // @} + + /** \name bind term constructor and accessors. */ + + // @{ + + /** \brief Create a ?bind term. + * + * \param variable_name The variable name to bind. + * \param p The pattern in which to + * bind this variable. + */ + static cwidget::util::ref_ptr + make_bind(const std::string &variable_name, + const cwidget::util::ref_ptr &p) + { + return new pattern(bind, p, variable_name); + } + + /** \brief Retrieve the variable name of a ?bind term. */ + const std::string &get_bind_variable_name() const + { + eassert(tp == bind); + + return string_info; + } + + /** \brief Retrieve the sub-pattern of a ?bind term. */ + const cwidget::util::ref_ptr &get_bind_pattern() const + { + eassert(tp == bind && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name broken term constructor */ + + // @{ + + /** \brief Create a ?broken term. */ + static cwidget::util::ref_ptr + make_broken() + { + return new pattern(broken); + } + + // @} + + /** \name broken_type term constructor and accessors */ + + // @{ + + /** \brief Create a ?broken-TYPE term. + * + * \param deptype The type of dependency to match. + */ + static cwidget::util::ref_ptr + make_broken_type(pkgCache::Dep::DepType deptype) + { + return new pattern(broken_type, deptype, true); + } + + // @} + + /** \name candidate_version term constructor. */ + + // @{ + + /** \brief Create a ?version(CANDIDATE) term. */ + static cwidget::util::ref_ptr + make_candidate_version() + { + return new pattern(candidate_version); + } + + // @} + + /** \name config_files term constructor */ + + // @{ + + /** \brief Create a ?config-files term. */ + static cwidget::util::ref_ptr + make_config_files() + { + return new pattern(config_files); + } + + // @} + + /** \name current_version term constructor */ + + // @{ + + /** \brief Create a ?version(CURRENT) term. */ + + static cwidget::util::ref_ptr + make_current_version() + { + return new pattern(current_version); + } + + // @} + + /** \name depends term constructor and accessors */ + + // @{ + + /** \brief Create a ?depends or ?broken-depends term. + * + * \param deptype The type of dependency to match. + * \param broken \b true to only match broken dependencies. + * \param p The sub-pattern of ?depends. + */ + static cwidget::util::ref_ptr + make_depends(pkgCache::Dep::DepType deptype, + bool broken, + const cwidget::util::ref_ptr &p) + { + return new pattern(depends, &p, (&p) + 1, deptype, broken); + } + + /** \brief Retrieve the depends_type field of a ?depends term. */ + pkgCache::Dep::DepType get_depends_depends_type() const + { + eassert(tp == depends); + + return info.dep.deptype; + } + + /** \brief Retrieve the broken field of a ?depends term. */ + bool get_depends_broken() const + { + eassert(tp == depends); + + return info.dep.broken; + } + + /** \brief Retrieve the sub-pattern of a ?depends term. */ + const cwidget::util::ref_ptr &get_depends_pattern() const + { + eassert(tp == depends && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name description term constructor and accessors */ + + // @{ + + /** \brief Create a ?description term. + * + * \param s The regular expression to match against the + * description. + */ + static cwidget::util::ref_ptr make_description(const std::string &s) + { + return new pattern(description, regex_info(s)); + } + + /** \brief Retrieve the regular expression info for a + * description term. + */ + const regex_info &get_description_regex_info() const + { + eassert(tp == description); + + return regex_information; + } + + // @} + + /** \name essential term constructor */ + + // @{ + + /** \brief Create an ?essential term. */ + static cwidget::util::ref_ptr make_essential() + { + return new pattern(essential); + } + + // @} + + /** \name equal term constructor and accessors */ + + // @{ + + /** \brief Create a ?= term. */ + static cwidget::util::ref_ptr make_equal(size_t stack_position, + const std::string &variable_name) + { + return new pattern(equal, stack_position, variable_name); + } + + /** \brief Retrieve the stack position of a ?= term. */ + size_t get_equal_stack_position() const + { + eassert(tp == equal); + + return info.stack_position; + } + + /** \brief Retrieve the variable name of a ?= term. */ + const std::string &get_equal_variable_name() const + { + eassert(tp == equal); + + return string_info; + } + + // @} + + /** \name false term constructor */ + + // @{ + + /** \brief Create a ?false term. */ + static cwidget::util::ref_ptr make_false() + { + return new pattern(false_tp); + } + + // @} + + /** \name for_tp term constructor and accessors. */ + + // @{ + + /** \brief Create a ?for term. + * + * \param variable_name The name of the variable introduced + * by this term. + * \param p The sub-pattern of this term. + */ + static cwidget::util::ref_ptr make_for(const std::string &variable_name, + const cwidget::util::ref_ptr &p) + { + return new pattern(for_tp, p, variable_name); + } + + // @} + + /** \name garbage term constructor. */ + + // @{ + + /** \brief Create a ?garbage term. */ + static cwidget::util::ref_ptr make_garbage() + { + return new pattern(garbage); + } + + // @} + + /** \name install_version term */ + + // @{ + + /** \brief Create a ?version(TARGET) term. */ + static cwidget::util::ref_ptr make_install_version() + { + return new pattern(install_version); + } + + // @} + + /** \name installed term constructor */ + + // @{ + + /** \brief Create a ?installed term. */ + static cwidget::util::ref_ptr make_installed() + { + return new pattern(installed); + } + + // @} + + /** \name keep term constructor */ + + // @{ + + /** \brief Create a ?action(keep) term. */ + static cwidget::util::ref_ptr make_keep() + { + return new pattern(keep); + } + + // @} + + /** \name maintainer term constructor and accessors */ + + // @{ + + /** \brief Create a ?maintainer term. + * + * \param s The regular expression to match against the + * package maintainer. + */ + static cwidget::util::ref_ptr make_maintainer(const std::string &s) + { + return new pattern(maintainer, regex_info(s)); + } + + /** \brief Retrieve the regular expression info for a + * maintainer term. + */ + const regex_info &get_maintainer_regex_info() const + { + eassert(tp == maintainer); + + return regex_information; + } + + // @} + + /** \name name term constructor and accessors */ + + // @{ + + /** \brief Create a ?name term. + * + * \param s The regular expression to match against the + * package name. + */ + static cwidget::util::ref_ptr make_name(const std::string &s) + { + return new pattern(name, regex_info(s)); + } + + /** \brief Retrieve the regular expression info for a + * name term. + */ + const regex_info &get_name_regex_info() const + { + eassert(tp == name); + + return regex_information; + } + + // @} + + /** \name narrow term constructor and accessors */ + + // @{ + + /** \brief Create a ?narrow term. + * + * \param filter The filter for this term. + * \param p The sub-pattern of this term. + */ + static cwidget::util::ref_ptr make_narrow(const cwidget::util::ref_ptr &filter, + const cwidget::util::ref_ptr &p) + { + return new pattern(narrow, filter, p); + } + + /** \brief Retrieve the filter of a ?narrow term. */ + const cwidget::util::ref_ptr &get_narrow_filter() const + { + eassert(tp == narrow && sub_patterns.size() == 2); + + return sub_patterns[0]; + } + + /** \brief Retrieve the pattern of a ?narrow term. */ + const cwidget::util::ref_ptr &get_narrow_pattern() const + { + eassert(tp == narrow && sub_patterns.size() == 2); + + return sub_patterns[1]; + } + + // @} + + /** \name new_tp term constructor */ + + // @{ + + /** \brief Create a ?new term. */ + static cwidget::util::ref_ptr make_new() + { + return new pattern(new_tp); + } + + // @} + + /** \name not_tp term constructor and accessors */ + + // @{ + + /** \brief Create a ?not term. + * + * \param p The sub-pattern of this term. + */ + static cwidget::util::ref_ptr make_not(const cwidget::util::ref_ptr &p) + { + return new pattern(not_tp, p); + } + + /** \brief Retrieve the pattern field of a ?not term. */ + const cwidget::util::ref_ptr &get_not_pattern() const + { + eassert(tp == not_tp && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name obsolete term constructor */ + + // @{ + + /** \brief Create an ?obsolete term. */ + static cwidget::util::ref_ptr make_obsolete() + { + return new pattern(obsolete); + } + + // @} + + /** \name or_tp term constructor and accessors */ + + // @{ + + /** \brief Create an ?or term. + * + * \param begin The left bound of the range with which + * to construct the term. + * \param end The right bound of the range with which + * to construct the term. + */ + template + static cwidget::util::ref_ptr make_or(Iter begin, + Iter end) + { + return new pattern(or_tp, begin, end); + } + + /** \brief Retrieve the sub-patterns of an ?or term. */ + const std::vector > & + get_or_patterns() const + { + eassert(tp == or_tp); + + return sub_patterns; + } + + // @} + + /** \name origin term constructor and accessors */ + + // @{ + + /** \brief Create an ?origin term. + * + * \param s The regular expression to match against + * the package's origin. + */ + static cwidget::util::ref_ptr make_origin(const std::string &s) + { + return new pattern(origin, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of an ?origin term. */ + const regex_info &get_origin_regex_info() const + { + eassert(tp == origin); + + return regex_information; + } + + // @} + + /** \name priority term constructor and accessors */ + + // @{ + + /** \brief Create a ?priority term. + * + * \param s The name of the priority to select. + */ + cwidget::util::ref_ptr make_priority(const std::string &s); + + /** \brief Retrieve the priority field of a ?priority term. */ + pkgCache::State::VerPriority get_priority_priority() const + { + eassert(tp == priority); + + return info.priority; + } + + /** \brief Retrieve the priority_name field of a ?priority term. */ + const std::string &get_priority_priority_name() const + + { + eassert(tp == priority); + + return string_info; + } + + // @} + + /** \name provides term constructor and accessors */ + + // @{ + + /** \brief Create a ?provides term. + * + * \param p The sub-pattern of the term. + */ + static cwidget::util::ref_ptr + make_provides(const cwidget::util::ref_ptr &p) + { + return new pattern(provides, p); + } + + /** \brief Retrieve the pattern field of a ?provides term. */ + const cwidget::util::ref_ptr & + get_provides_pattern() const + { + eassert(tp == provides && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + + /** \name reverse_depends term constructor and accessors */ + + // @{ + + /** \brief Create a ?reverse-depends or ?broken-reverse-depends term. + * + * \param deptype The type of dependency to match. + * \param broken \b true to only match broken dependencies. + * \param p The sub-pattern of ?reverse-depends. + */ + static cwidget::util::ref_ptr + make_reverse_depends(pkgCache::Dep::DepType deptype, + bool broken, + const cwidget::util::ref_ptr &p) + { + return new pattern(reverse_depends, &p, (&p) + 1, deptype, broken); + } + + /** \brief Retrieve the depends_type field of a ?reverse-depends term. */ + pkgCache::Dep::DepType get_reverse_depends_depends_type() const + { + eassert(tp == reverse_depends); + + return info.dep.deptype; + } + + /** \brief Retrieve the broken field of a ?reverse-depends term. */ + bool get_reverse_depends_broken() const + { + eassert(tp == reverse_depends); + + return info.dep.broken; + } + + /** \brief Retrieve the sub-pattern of a ?reverse-depends term. */ + const cwidget::util::ref_ptr &get_reverse_depends_pattern() const + { + eassert(tp == reverse_depends && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name reverse_provides term constructor and accessors */ + + // @{ + + /** \brief Create a ?reverse-provides term. + * + * \param p The sub-pattern of ?reverse-provides. + */ + static cwidget::util::ref_ptr + make_reverse_provides(const cwidget::util::ref_ptr &p) + { + return new pattern(reverse_provides, p); + } + + /** \brief Retrieve the sub-pattern of a ?reverse-provides term. */ + const cwidget::util::ref_ptr & + get_reverse_provides_pattern() const + { + eassert(tp == reverse_provides && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + + /** \name section term constructor and accessors */ + + // @{ + + /** \brief Create a ?section term. + * + * \param s The regular expression to match against the + * package's section. + */ + static cwidget::util::ref_ptr + make_section(const std::string &s) + { + return new pattern(section, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?section term. */ + const regex_info &get_section_regex_info() const + { + eassert(tp == section); + + return regex_information; + } + + // @} + + /** \name source_package term constructor and accessors */ + + // @{ + + /** \brief Create a ?source-package term. + * + * \param s The regular expression to match against the + * source package. + */ + static cwidget::util::ref_ptr + make_source_package(const std::string &s) + { + return new pattern(source_package, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?source-package term. */ + const regex_info &get_source_package_regex_info() const + { + eassert(tp == source_package); + + return regex_information; + } + + // @} + + /** \name source_version term constructor and accessors */ + + // @{ + + /** \brief Create a ?source-version term. + * + * \param s The regular expression to match against the + * source package version. + */ + static cwidget::util::ref_ptr + make_source_version(const std::string &s) + { + return new pattern(source_version, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?source-version term. */ + const regex_info &get_source_version_regex_info() const + { + eassert(tp == source_version); + + return regex_information; + } + + // @} + + /** \name tag term constructor and accessors */ + + // @{ + + /** \brief Create a ?tag term. + * + * \param s The regular expression to match against the + * package's tags. + */ + static cwidget::util::ref_ptr + make_tag(const std::string &s) + { + return new pattern(tag, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?tag term. */ + const regex_info &get_tag_regex_info() const + { + eassert(tp == tag); + + return regex_information; + } + + // @} + + /** \name task term constructor and accessors */ + + // @{ + + /** \brief Create a ?task term. + * + * \param s The regular expression to match against the + * package's tasks. + */ + static cwidget::util::ref_ptr + make_task(const std::string &s) + { + return new pattern(task, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?task term. */ + const regex_info &get_task_regex_info() const + { + eassert(tp == task); + + return regex_information; + } + + // @} + + /** \name true term constructor */ + + // @{ + + /** \brief Create a ?true term. */ + static cwidget::util::ref_ptr make_true() + { + return new pattern(true_tp); + } + + // @} + + /** \name upgradable term constructor */ + + // @{ + + /** \brief Create an ?upgradable term. */ + static cwidget::util::ref_ptr make_upgradable() + { + return new pattern(upgradable); + } + + // @} + + /** \name user_tag term constructor and accessors */ + + // @{ + + /** \brief Create a ?user-tag term. + * + * \param s The regular expression to match against the + * package's user tags. + */ + static cwidget::util::ref_ptr + make_user_tag(const std::string &s) + { + return new pattern(user_tag, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?user-tag term. */ + const regex_info &get_user_tag_regex_info() const + { + eassert(tp == user_tag); + + return regex_information; + } + + // @} + + /** \name version term constructor and accessors */ + + // @{ + + /** \brief Create a ?version term. + * + * \param s The regular expression to match against the + * package's versions. + */ + static cwidget::util::ref_ptr + make_version(const std::string &s) + { + return new pattern(version, regex_info(s)); + } + + /** \brief Retrieve the regex_info field of a ?version term. */ + const regex_info &get_version_regex_info() const + { + eassert(tp == version); + + return regex_information; + } + + // @} + + /** \name virtual_tp term constructor and accessors */ + + // @{ + + /** \brief Create a ?virtual term. */ + + static cwidget::util::ref_ptr make_virtual() + { + return new pattern(virtual_tp); + } + + // @} + + /** \name widen term constructor and accessors */ + + // @{ + + /** \brief Create a ?widen term. + * + * \param p The sub-pattern of the new term. + */ + static cwidget::util::ref_ptr + make_widen(const cwidget::util::ref_ptr &p) + { + return new pattern(widen, p); + } + + /** \brief Retrieve the sub-pattern of a ?widen term. */ + const cwidget::util::ref_ptr &get_widen_pattern() + { + eassert(tp == widen && sub_patterns.size() == 1); + + return sub_patterns.front(); + } + + // @} + }; + } +} + -- cgit v1.2.3 From 2086ad23985f056bf9e1a343d90503cf7104ea68 Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Sun, 19 Oct 2008 12:14:38 -0700 Subject: Make ept non-optional. The new aptitude UI stuff depends more heavily on ept and won't work well if ept is missing. Instead of trying to fiddle with compiling out blocks of code and so on, we now just require ept at all times. --- configure.ac | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 2009ebc5..01711597 100644 --- a/configure.ac +++ b/configure.ac @@ -41,24 +41,8 @@ AC_CHECK_LIB(pthread, main, ALL_LINGUAS="ar bs ca cs da de el es eu fi fr gl hu it ja km ku lt mr nb nl nn pl pt pt_BR ro ru sk sv tl tr vi zh_CN zh_TW" AM_GNU_GETTEXT([external]) -HAVE_EPT= -AC_ARG_WITH(ept, - AS_HELP_STRING(--with-ept, [compile against the ept library (enables debtags and xapian-index support)]), - [if test x$withval = xyes - then - HAVE_EPT=1 - fi]) - -PKG_CHECK_MODULES(ept, libept, [], [HAVE_EPT=]) - -if test x$HAVE_EPT = x1 -then - AC_DEFINE(HAVE_EPT, 1, [Define if the ept package information library is available.]) -else - # Force the program not to link against ept. - ept_CFLAGS= - ept_LIBS= -fi +PKG_CHECK_MODULES(ept, libept, [], [AC_MSG_ERROR([Can't find the ept library -- please install libept-dev])]) +AC_DEFINE(HAVE_EPT) PKG_CHECK_MODULES(SIGC, sigc++-2.0) -- cgit v1.2.3 From e8b1e2860317c3ab3cb186ce406391a148f5ae03 Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Sun, 19 Oct 2008 14:45:15 -0700 Subject: Fix configure.ac. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 01711597..75ecc3f2 100644 --- a/configure.ac +++ b/configure.ac @@ -42,7 +42,7 @@ ALL_LINGUAS="ar bs ca cs da de el es eu fi fr gl hu it ja km ku lt mr nb nl nn p AM_GNU_GETTEXT([external]) PKG_CHECK_MODULES(ept, libept, [], [AC_MSG_ERROR([Can't find the ept library -- please install libept-dev])]) -AC_DEFINE(HAVE_EPT) +AC_DEFINE([HAVE_EPT], [], [Backwards compatibility symbol; must always be defined.]) PKG_CHECK_MODULES(SIGC, sigc++-2.0) -- cgit v1.2.3 From 7c4a3599abf4c93447b66754f1d848439aa91621 Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Mon, 20 Oct 2008 18:23:51 -0700 Subject: Instead of baking the status file descriptor into the download_install_manager object, pass it as an argument to the post-install process. This lets us deal with the fact that due to weirdness in vte, we might not know the status file descriptor until we're about to invoke dpkg. --- configure.ac | 2 ++ src/cmdline/cmdline_do_action.cc | 7 +++---- src/cmdline/cmdline_upgrade.cc | 7 +++---- src/generic/apt/download_install_manager.cc | 6 ++---- src/generic/apt/download_install_manager.h | 18 +++++++----------- src/gtk/gui.cc | 16 +++------------- src/gtk/ui-main.glade | 26 +++++++++++++++++++++++--- src/ui.cc | 6 +++--- 8 files changed, 46 insertions(+), 42 deletions(-) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 75ecc3f2..4f02d715 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,8 @@ PKG_CHECK_MODULES(SIGC, sigc++-2.0) PKG_CHECK_MODULES(CWIDGET, cwidget) +PKG_CHECK_MODULES(VTE, vte) + HAVE_GTK=1 AC_ARG_ENABLE(gtk, AS_HELP_STRING(--disable-gtk, [don't compile the GTK+/gtkmm frontend even if the appropriate libraries are available]), diff --git a/src/cmdline/cmdline_do_action.cc b/src/cmdline/cmdline_do_action.cc index 38865f07..0bf23869 100644 --- a/src/cmdline/cmdline_do_action.cc +++ b/src/cmdline/cmdline_do_action.cc @@ -27,9 +27,9 @@ using namespace std; namespace { - pkgPackageManager::OrderResult run_dpkg_directly(sigc::slot0 f) + pkgPackageManager::OrderResult run_dpkg_directly(sigc::slot1 f) { - return f(); + return f(aptcfg->FindI("APT::Status-Fd", -1)); } } @@ -316,8 +316,7 @@ int cmdline_do_action(int argc, char *argv[], aptitude::cmdline::apply_user_tags(user_tags); download_install_manager m(download_only, - sigc::ptr_fun(&run_dpkg_directly), - aptcfg->FindI("APT::Status-Fd", -1)); + sigc::ptr_fun(&run_dpkg_directly)); int rval = (cmdline_do_download(&m, verbose) == download_manager::success ? 0 : -1); diff --git a/src/cmdline/cmdline_upgrade.cc b/src/cmdline/cmdline_upgrade.cc index 9d492d2c..f523840a 100644 --- a/src/cmdline/cmdline_upgrade.cc +++ b/src/cmdline/cmdline_upgrade.cc @@ -29,9 +29,9 @@ namespace { - pkgPackageManager::OrderResult run_dpkg_directly(sigc::slot0 f) + pkgPackageManager::OrderResult run_dpkg_directly(sigc::slot1 f) { - return f(); + return f(aptcfg->FindI("APT::Status-Fd", -1)); } } @@ -178,8 +178,7 @@ int cmdline_upgrade(int argc, char *argv[], aptitude::cmdline::apply_user_tags(user_tags); download_install_manager m(download_only, - sigc::ptr_fun(&run_dpkg_directly), - aptcfg->FindI("APT::Status-Fd", -1)); + sigc::ptr_fun(&run_dpkg_directly)); int rval = (cmdline_do_download(&m, verbose) == download_manager::success ? 0 : -1); diff --git a/src/generic/apt/download_install_manager.cc b/src/generic/apt/download_install_manager.cc index db4f7460..77722f2d 100644 --- a/src/generic/apt/download_install_manager.cc +++ b/src/generic/apt/download_install_manager.cc @@ -36,10 +36,8 @@ using namespace std; download_install_manager::download_install_manager(bool _download_only, - const run_dpkg_in_terminal_func &_run_dpkg_in_terminal, - int _status_fd) + const run_dpkg_in_terminal_func &_run_dpkg_in_terminal) : log(NULL), download_only(_download_only), pm(new pkgDPkgPM(*apt_cache_file)), - status_fd(_status_fd), run_dpkg_in_terminal(_run_dpkg_in_terminal) { } @@ -104,7 +102,7 @@ bool download_install_manager::prepare(OpProgress &progress, return true; } -pkgPackageManager::OrderResult download_install_manager::run_dpkg() +pkgPackageManager::OrderResult download_install_manager::run_dpkg(int status_fd) { sigset_t allsignals; sigset_t oldsignals; diff --git a/src/generic/apt/download_install_manager.h b/src/generic/apt/download_install_manager.h index 30d12e47..7e279f84 100644 --- a/src/generic/apt/download_install_manager.h +++ b/src/generic/apt/download_install_manager.h @@ -36,11 +36,11 @@ /** \file download_install_manager.h */ -// The type of a function that runs a nullary function in a terminal -// (possibly the current controlling terminal) and returns its result. -// The function may be invoked in a sub-process, but the status file -// descriptor (if one is defined) must be preserved. -typedef sigc::slot1 > run_dpkg_in_terminal_func; +// The type of a function that runs dpkg in a terminal. It's passed a +// function that invokes dpkg as currently appropriate, given the +// status file descriptor on which to invoke it (or -1 to discard +// status messages). This may be invoked in a sub-process. +typedef sigc::slot1 > run_dpkg_in_terminal_func; /** Manages downloading and installing packages. */ class download_install_manager : public download_manager @@ -57,16 +57,13 @@ class download_install_manager : public download_manager /** The list of sources from which to download. */ pkgSourceList src_list; - /** The apt status file descriptor. */ - int status_fd; - /** How to run the actual install process. */ run_dpkg_in_terminal_func run_dpkg_in_terminal; /** Actually run dpkg; this is the part of the installation * that might run in a sub-process. */ - pkgPackageManager::OrderResult run_dpkg(); + pkgPackageManager::OrderResult run_dpkg(int status_fd); /** Actually perform the installation/removal of packages and tell * the caller what happened. @@ -85,8 +82,7 @@ public: * status information. */ download_install_manager(bool _download_only, - const run_dpkg_in_terminal_func &_run_dpkg_in_terminal, - int _status_fd); + const run_dpkg_in_terminal_func &_run_dpkg_in_terminal); ~download_install_manager(); /** Set up an install run. Does not take ownership of any of the diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc index 78ff2879..90db8fab 100644 --- a/src/gtk/gui.cc +++ b/src/gtk/gui.cc @@ -57,15 +57,6 @@ namespace cw = cwidget; -// \todo Run in a vte widget. -namespace -{ - pkgPackageManager::OrderResult run_dpkg_directly(sigc::slot0 f) - { - return f(); - } -} - namespace gui { // \todo Some of these icon choices suck. @@ -291,10 +282,10 @@ namespace gui { // Scary callback functions. This needs to be cleaned up. pkgPackageManager::OrderResult - gui_run_dpkg(sigc::slot0 f, + gui_run_dpkg(sigc::slot1 f, pkgPackageManager::OrderResult *set_loc) { - pkgPackageManager::OrderResult result = f(); + pkgPackageManager::OrderResult result = f(-1); *set_loc = result; return result; } @@ -332,8 +323,7 @@ namespace gui download_install_manager *m = new download_install_manager(false, sigc::bind(sigc::ptr_fun(&gui_run_dpkg), - &result), - -1); + &result)); guiOpProgress progress; cw::util::ref_ptr acqlog(guiPkgAcquireStatus::create()); diff --git a/src/gtk/ui-main.glade b/src/gtk/ui-main.glade index 47db37a1..5c00b0c1 100644 --- a/src/gtk/ui-main.glade +++ b/src/gtk/ui-main.glade @@ -1,6 +1,6 @@ - + 800 @@ -1495,12 +1495,32 @@ - + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + A terminal running dpkg will appear here. + + + + + 8 + - + + True + Applying Changes + tab + 8 + False diff --git a/src/ui.cc b/src/ui.cc index 9032793a..beb6a734 100644 --- a/src/ui.cc +++ b/src/ui.cc @@ -1148,10 +1148,10 @@ static void maybe_show_old_tmpdir_message() namespace { - pkgPackageManager::OrderResult run_dpkg_with_cwidget_suspended(sigc::slot0 f) + pkgPackageManager::OrderResult run_dpkg_with_cwidget_suspended(sigc::slot1 f) { cw::toplevel::suspend(); - pkgPackageManager::OrderResult rval = f(); + pkgPackageManager::OrderResult rval = f(-1); if(rval != pkgPackageManager::Incomplete) { @@ -1174,7 +1174,7 @@ namespace void install_or_remove_packages() { - download_install_manager *m = new download_install_manager(false, sigc::ptr_fun(&run_dpkg_with_cwidget_suspended), -1); + download_install_manager *m = new download_install_manager(false, sigc::ptr_fun(&run_dpkg_with_cwidget_suspended)); m->post_forget_new_hook.connect(package_states_changed.make_slot()); -- cgit v1.2.3 From f9cef2b7ff169a4a0bd3b37156bd72cc3ef93ecd Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Tue, 21 Oct 2008 07:49:24 -0700 Subject: Proof-of-concept code to use a vte terminal with the dpkg install process. Because finish() ends up invoking GTK+ code, this will always crash after dpkg runs (but that was the case previously, anyway). And I want to eventually hide the terminal by default and just show a notification saying that you could see a terminal if you wanted to. But this is a good start. --- configure.ac | 4 +- src/gtk/Makefile.am | 2 + src/gtk/dpkg_terminal.cc | 305 +++++++++++++++++++++++++++++++++++++++++++++++ src/gtk/dpkg_terminal.h | 56 +++++++++ src/gtk/gui.cc | 48 +++++++- src/gtk/tab.h | 7 +- src/gtk/ui-main.glade | 3 +- 7 files changed, 417 insertions(+), 8 deletions(-) create mode 100644 src/gtk/dpkg_terminal.cc create mode 100644 src/gtk/dpkg_terminal.h (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 4f02d715..8f991dcc 100644 --- a/configure.ac +++ b/configure.ac @@ -92,8 +92,8 @@ then AC_DEFINE([HAVE_GTK], [], [Define if the GTK+ frontend is included in the build.]) fi -CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GLIBMM_CFLAGS $GTHREAD_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $ept_CFLAGS" -LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GLIBMM_LIBS $GTHREAD_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $ept_LIBS" +CXXFLAGS="$CXXFLAGS $SIGC_CFLAGS $CWIDGET_CFLAGS $GLIBMM_CFLAGS $GTHREAD_CFLAGS $GTKMM_CFLAGS $LIBGLADEMM_CFLAGS $VTE_CFLAGS $ept_CFLAGS" +LIBS="$LIBS $SIGC_LIBS $CWIDGET_LIBS $GLIBMM_LIBS $GTHREAD_LIBS $GTKMM_LIBS $LIBGLADEMM_LIBS $VTE_LIBS $ept_LIBS" AC_DEFINE_UNQUOTED(SIGC_VERSION, ["$(pkg-config --modversion sigc++-2.0)"], [The version of libsigc++ with which the program was compiled]) dnl Checks for header files. diff --git a/src/gtk/Makefile.am b/src/gtk/Makefile.am index 69e7cdc2..d3619d10 100644 --- a/src/gtk/Makefile.am +++ b/src/gtk/Makefile.am @@ -9,6 +9,8 @@ libgtk_a_SOURCES=\ dependency_chains_tab.h \ download.cc \ download.h \ + dpkg_terminal.cc \ + dpkg_terminal.h \ entityview.cc \ entityview.h \ errortab.cc \ diff --git a/src/gtk/dpkg_terminal.cc b/src/gtk/dpkg_terminal.cc new file mode 100644 index 00000000..5a6e5ac8 --- /dev/null +++ b/src/gtk/dpkg_terminal.cc @@ -0,0 +1,305 @@ +// dpkg_terminal.cc +// +// Copyright 2008 Daniel Burrows +// +// 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. + +#include "dpkg_terminal.h" + +#include +#include +#include + +#include + +#include + +#include +#include +#include // Fox UNIX-domain sockets. + +#include + +#include + +#include "gui.h" + +namespace cw = cwidget; + +namespace gui +{ + namespace + { + // In some theoretical sense, it might be worthwhile to create a + // thread to monitor the dpkg socket. In practical terms, though, + // I expect that it'll be perfectly acceptable to handle dpkg + // status messages from the main loop. + // + // \todo actually parse the messages and emit some sort of signal + // for them. + bool process_data_from_dpkg_socket(Glib::IOCondition condition, + int fd) + { + switch(condition) + { + case Glib::IO_IN: + { + char c; + while(recv(fd, &c, 1, MSG_DONTWAIT) > 0) + ; // Just snarf all the data in a lame way for now. + return true; + } + case Glib::IO_NVAL: + case Glib::IO_ERR: + case Glib::IO_HUP: + return false; + + default: + return true; // ?? + } + } + + int open_unix_socket() + { + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if(sock == -1) + { + int errnum = errno; + std::string err = cw::util::sstrerror(errnum); + std::string msg = cw::util::ssprintf(_("%s: Unable to create a Unix-domain socket: %s"), + "open_unix_socket", + err.c_str()); + _error->Error("%s", msg.c_str()); + } + + return sock; + } + + // Closure structure to connect up the child-exited signal. + struct child_exited_info + { + // The PID of the child to monitor. + pid_t pid; + + // The box to place the child's result in. + cw::threads::box *result_box; + + gulong handler_id; + + child_exited_info(pid_t _pid, + cw::threads::box *_result_box) + : pid(_pid), + result_box(_result_box) + { + } + }; + + void destroy_child_exited_info(gpointer data, + GClosure *closure) + { + delete (child_exited_info *)data; + } + + void handle_dpkg_result(VteReaper *vtereaper, + gint pid, + gint status, + gpointer user_data) + { + child_exited_info *info = (child_exited_info *)user_data; + + if(info->pid != pid) + return; + + pkgPackageManager::OrderResult result = pkgPackageManager::Failed; + if(WIFEXITED(status)) + result = (pkgPackageManager::OrderResult) WEXITSTATUS(status); + + // TODO: we'll leave the background thread running forever if an + // exception gets thrown somehow. But I don't think we can + // avoid this. + info->result_box->put(pkgPackageManager::Failed); + + g_signal_handler_disconnect(vte_reaper_get(), + info->handler_id); + } + + void connect_dpkg_result(pid_t pid, + cw::threads::box *result_box) + { + child_exited_info *info = new child_exited_info(pid, result_box); + // We use implicit locking here to know that the signal won't be + // triggered before handler_id is set. + info->handler_id = + g_signal_connect_data(vte_reaper_get(), + "child-exited", + G_CALLBACK(&handle_dpkg_result), + info, + &destroy_child_exited_info, + (GConnectFlags)0); + } + + // The dpkg stuff is actually run from the main loop. Why? + // Because we want to be able to connect signals safely, and to + // avoid racing with the VTE reaper (which seems to be somewhat + // hard to eliminate) or threads accessing _error. + void do_run_dpkg(const sigc::slot1 f, + const sigc::slot1 register_terminal, + cw::threads::box *result_box) + { + GtkWidget *vte = vte_terminal_new(); + + Gtk::Widget *w(Glib::wrap(vte, false)); + + register_terminal(w); + + // Create a temporary UNIX-domain socket to pass status + // information to the parent. + temp::dir tempdir("aptitude"); + temp::name socketname(tempdir, "commsocket"); + + // To avoid races, we bind the receive end of the socket first and + // start accepting connections. + int listen_sock = open_unix_socket(); + if(listen_sock == -1) + { + result_box->put(pkgPackageManager::Failed); + } + + // Ensure that the socket is always closed when this routine + // exits. + FileFd sock_fd(listen_sock); + + struct sockaddr_un sa; + + const size_t max_socket_name = sizeof(sa.sun_path); + + if(socketname.get_name().size() > max_socket_name) + { + _error->Error("Internal error: the temporary socket name is too long!"); + result_box->put(pkgPackageManager::Failed); + } + + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, socketname.get_name().c_str(), max_socket_name); + if(bind(listen_sock, (struct sockaddr *)&sa, sizeof(sa)) != 0) + { + int errnum = errno; + std::string err = cw::util::sstrerror(errnum); + _error->Error("%s: Unable to bind to the temporary socket: %s", + __PRETTY_FUNCTION__, + err.c_str()); + result_box->put(pkgPackageManager::Failed); + } + + if(listen(listen_sock, 1) != 0) + { + int errnum = errno; + std::string err = cw::util::sstrerror(errnum); + _error->Error("%s: Unable to listen on the temporary socket: %s", + __PRETTY_FUNCTION__, + err.c_str()); + result_box->put(pkgPackageManager::Failed); + } + + pid_t pid = vte_terminal_forkpty(VTE_TERMINAL(vte), + NULL, NULL, + FALSE, FALSE, FALSE); + + if(pid == 0) + { + // The child process. It passes status information to the + // parent process and uses its *return code* to indicate the + // success / failure state. + + // NB: we should close the listen side here, but I don't + // because of my magic knowledge that vte will. + + int write_sock = open_unix_socket(); + if(write_sock == -1) + { + _error->DumpErrors(); + _exit(pkgPackageManager::Failed); + } + + if(connect(write_sock, (struct sockaddr *)&sa, sizeof(sa)) != 0) + { + int errnum = errno; + std::string err = cw::util::sstrerror(errnum); + _error->Error("%s: Unable to bind to the temporary socket: %s", + __PRETTY_FUNCTION__, + err.c_str()); + _error->DumpErrors(); + _exit(pkgPackageManager::Failed); + } + + pkgPackageManager::OrderResult result = f(write_sock); + + // Make sure errors appear somewhere (we really ought to push + // them down the FIFO). + _error->DumpErrors(); + + _exit(result); + } + else + { + int read_sock = accept(listen_sock, NULL, NULL); + if(read_sock == -1) + { + int errnum = errno; + std::string errmsg = cw::util::sstrerror(errnum); + _error->Error(_("%s: Unable to accept a connection from the subprocess: %s"), + __PRETTY_FUNCTION__, + errmsg.c_str()); + } + + // Catch status output from the install process. + Glib::signal_io().connect(sigc::bind(sigc::ptr_fun(&process_data_from_dpkg_socket), + read_sock), + read_sock, + Glib::IO_IN | Glib::IO_ERR | Glib::IO_HUP | Glib::IO_NVAL); + + // The parent process. Here we just wait for the reaper to + // tell us that the child finished, then return the result. + // We use implicit locking here to avoid a race condition that + // could occur: we know that the reaper won't fire before we + // can connect to it because its signal executions go through + // the main loop, and this function call is blocking the main + // loop. + connect_dpkg_result(pid, result_box); + + vte_reaper_add_child(pid); + } + } + } + + pkgPackageManager::OrderResult run_dpkg_in_terminal(const sigc::slot1 &f, + const sigc::slot1 ®ister_terminal) + { + cw::threads::box result_box; + + // Ask the main thread to start the dpkg run and store the result + // in result_box. + post_event(sigc::bind(sigc::ptr_fun(&do_run_dpkg), + f, + register_terminal, + &result_box)); + + // Once the result-box is filled in, we're done. Correctness here + // relies on the fact that no-one accesses the box after put()-ing + // into it. + return result_box.take(); + } +} diff --git a/src/gtk/dpkg_terminal.h b/src/gtk/dpkg_terminal.h new file mode 100644 index 00000000..7a642703 --- /dev/null +++ b/src/gtk/dpkg_terminal.h @@ -0,0 +1,56 @@ +// -*-c++-*- + +// dpkg_terminal.h +// +// Copyright 2008 Daniel Burrows +// +// 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 DPKG_TERMINAL_H +#define DPKG_TERMINAL_H + +#include + +#include + +/** \brief Support for creating a GUI terminal in which dpkg can be + * invoked. + * + * \file dpkg_terminal.h + */ + +namespace gui +{ + /** \brief Run dpkg in a terminal, blocking this thread until it + * completes. + * + * Note that this *must* be invoked in a background thread, or it + * will deadlock itself! + * + * \param f The function to invoke when we want to run dpkg. + * + * \param register_terminal A function to invoke to register the + * terminal (e.g., to store it in a variable or to add it to a new + * tab). This function assumes ownership of the terminal widget. + * + * register_terminal is invoked in the same thread that called + * run_dpkg_in_terminal(). + */ + pkgPackageManager::OrderResult run_dpkg_in_terminal(const sigc::slot1 &f, + const sigc::slot1 ®ister_terminal); +} + +#endif diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc index 90db8fab..1d1b5cc6 100644 --- a/src/gtk/gui.cc +++ b/src/gtk/gui.cc @@ -48,6 +48,7 @@ #include #include +#include #include #include #include @@ -280,24 +281,65 @@ namespace gui // installation. namespace { + class DpkgTerminalTab : public Tab + { + Gtk::ScrolledWindow *terminal_scrolled_window; + + public: + DpkgTerminalTab(Gtk::Widget *term) + : Tab(DpkgTerminal, "Applying changes", + Gnome::Glade::Xml::create(glade_main_file, "main_apply_changes_scrolledwindow"), + "main_apply_changes_scrolledwindow") + { + get_xml()->get_widget("main_apply_changes_scrolledwindow", + terminal_scrolled_window); + terminal_scrolled_window->remove(); + terminal_scrolled_window->add(*Gtk::manage(term)); + + get_widget()->show_all(); + } + }; + // Scary callback functions. This needs to be cleaned up. + + void register_dpkg_terminal(Gtk::Widget *w) + { + tab_add(new DpkgTerminalTab(w)); + } + + // Callback for running dpkg from a background thread. pkgPackageManager::OrderResult gui_run_dpkg(sigc::slot1 f, pkgPackageManager::OrderResult *set_loc) { - pkgPackageManager::OrderResult result = f(-1); + // \todo We should change the download notification to tell the + // user that they can click to obtain a terminal; this is just a + // proof-of-concept. + pkgPackageManager::OrderResult result = run_dpkg_in_terminal(f, sigc::ptr_fun(®ister_dpkg_terminal)); *set_loc = result; return result; } - void handle_install(download_install_manager *m, OpProgress &progress, + void handle_install(download_install_manager *m, bool *in_dpkg, bool *finished) { + OpProgress progress; + download_manager::result result = download_manager::do_again; while (result == download_manager::do_again) { m->do_download(100); *in_dpkg = true; + // \todo Calling finish() from a background thread will + // crash because it invokes callbacks that ultimately end up + // calling GTK+ functions. finish() should be split up into + // a part that runs before invoking dpkg and a part that + // runs afterwards, the same way that it's split around the + // download process. In fact, the whole business of + // downloading could be viewed as being a series of + // background actions whose results are passed to a + // suspended continuation. There may be something to chew + // on there... result = m->finish(pkgAcquire::Continue, progress); *in_dpkg = false; } @@ -343,7 +385,7 @@ namespace gui pMainWindow->get_progress_bar()->set_text(_("Installing / removing packages...")); Glib::Thread * install_thread = - Glib::Thread::create(sigc::bind(sigc::ptr_fun(&handle_install), m, progress, &in_dpkg, &finished), true); + Glib::Thread::create(sigc::bind(sigc::ptr_fun(&handle_install), m, &in_dpkg, &finished), true); while(!finished) { if (in_dpkg) diff --git a/src/gtk/tab.h b/src/gtk/tab.h index 810e1a0c..62b44e75 100644 --- a/src/gtk/tab.h +++ b/src/gtk/tab.h @@ -41,7 +41,12 @@ namespace gui * chains linking two groups of packages. */ DependencyChains, - Download, Packages, Info, Preview, Resolver, InstallRemove, Error + Download, Packages, Info, Preview, Resolver, InstallRemove, + /** \brief A tab that shows the terminal output produced by a dpkg + * invocation. + */ + DpkgTerminal, + Error }; /** diff --git a/src/gtk/ui-main.glade b/src/gtk/ui-main.glade index 5c00b0c1..ce921f70 100644 --- a/src/gtk/ui-main.glade +++ b/src/gtk/ui-main.glade @@ -1,6 +1,6 @@ - + 800 @@ -1496,7 +1496,6 @@ - True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC -- cgit v1.2.3 From 4912141db219729ac4cb0cc64d6d68e21c4f684f Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Mon, 10 Nov 2008 19:41:12 -0800 Subject: Add a configure option to disable locating the .glade file relative to the cwd. Right now this will just make the program crash; next up is to actually find the .glade file in $datadir. The idea is that the packaged version of aptitude shouldn't be scrounging around in $(pwd) looking for its data files. (if people want to load the glade file from an alternate location, we can add a command-line option that does that in a disciplined way) --- configure.ac | 8 ++++++++ src/gtk/gui.cc | 2 ++ 2 files changed, 10 insertions(+) (limited to 'configure.ac') diff --git a/configure.ac b/configure.ac index 8f991dcc..4dea05fd 100644 --- a/configure.ac +++ b/configure.ac @@ -239,6 +239,14 @@ fi WERROR="-Werror" +AC_ARG_ENABLE(private-glade-file, + AS_HELP_STRING([--disable-private-glade-file], [Always read the .glade file from \$datadir instead of reading one in ".". This can be useful when installing aptitude globally, to avoid accidentally picking up the wrong version of the .glade file.]), + [if test x$enableval = xno + then + AC_DEFINE([DISABLE_PRIVATE_GLADE_FILE], [], [Define to disable the use of a .glade file found relative to the current working directory.]) + fi] + ) + AC_ARG_ENABLE(dynamic-backtrace, AS_HELP_STRING([--enable-dynamic-backtrace], [Modify the executable so that it can generate a backtrace for uncaught exceptions. Will double the size of the stripped binary.]), [if test x$enableval = xyes diff --git a/src/gtk/gui.cc b/src/gtk/gui.cc index 4ad311c5..1df5096c 100644 --- a/src/gtk/gui.cc +++ b/src/gtk/gui.cc @@ -1141,6 +1141,7 @@ namespace gui void init_glade(int argc, char *argv[]) { +#ifndef DISABLE_PRIVATE_GLADE_FILE // Use the basename of argv0 to find the Glade file. // TODO: note that the .glade file will ultimately // go to /usr/share/aptitude/glawith referede or something, @@ -1161,6 +1162,7 @@ namespace gui //Loading the .glade file and widgets refXml = Gnome::Glade::Xml::create(glade_main_file); +#endif } namespace -- cgit v1.2.3