[rkward-cvs] SF.net SVN: rkward:[3822] trunk/rkward

tfry at users.sourceforge.net tfry at users.sourceforge.net
Fri Sep 23 17:41:56 UTC 2011


Revision: 3822
          http://rkward.svn.sourceforge.net/rkward/?rev=3822&view=rev
Author:   tfry
Date:     2011-09-23 17:41:55 +0000 (Fri, 23 Sep 2011)
Log Message:
-----------
Rework package installation dialog. This is still work in progress / not fully implemented.

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/rkward/dialogs/rkloadlibsdialog.cpp
    trunk/rkward/rkward/dialogs/rkloadlibsdialog.h
    trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2011-09-23 14:00:03 UTC (rev 3821)
+++ trunk/rkward/ChangeLog	2011-09-23 17:41:55 UTC (rev 3822)
@@ -1,3 +1,4 @@
+- Reworked package installation / update dialog			TODO: implement removing, "check all updateable", clean layout, fix size, more tips, fix sorting
 - Fixed: Integrated help browser would not update navigation history when following page internal links
 - Documentation on writing RKWard plugins is now accessible, locally
 - The file filter for R script files is now configurable, and includes *.Rhistory, by default

Modified: trunk/rkward/rkward/dialogs/rkloadlibsdialog.cpp
===================================================================
--- trunk/rkward/rkward/dialogs/rkloadlibsdialog.cpp	2011-09-23 14:00:03 UTC (rev 3821)
+++ trunk/rkward/rkward/dialogs/rkloadlibsdialog.cpp	2011-09-23 17:41:55 UTC (rev 3822)
@@ -27,6 +27,8 @@
 #include <qtimer.h>
 #include <qtextstream.h>
 #include <QCloseEvent>
+#include <QSortFilterProxyModel>
+#include <QApplication>
 
 #include <klocale.h>
 #include <kmessagebox.h>
@@ -63,10 +65,8 @@
 	addPage (luwidget, i18n ("Local packages"));
 	connect (this, SIGNAL (installedPackagesChanged ()), luwidget, SLOT (updateInstalledPackages ()));
 
-	addPage (new UpdatePackagesWidget (this), i18n ("Update"));
-
 	install_packages_widget = new InstallPackagesWidget (this);
-	install_packages_pageitem = addPage (install_packages_widget, i18n ("Install"));
+	install_packages_pageitem = addPage (install_packages_widget, i18n ("Install / Update / Remove"));
 
 	setButtonText (KDialog::User1, i18n ("Configure Repositories"));
 
@@ -172,7 +172,7 @@
 	}
 }
 
-bool RKLoadLibsDialog::installPackages (const QStringList &packages, const QString &to_libloc, bool install_dependencies, bool as_root) {
+bool RKLoadLibsDialog::installPackages (const QStringList &packages, const QString &to_libloc, bool install_dependencies, bool as_root, const QStringList& repos) {
 	RK_TRACE (DIALOGS);
 
 	if (packages.isEmpty ()) return false;
@@ -185,6 +185,13 @@
 	if (install_dependencies) command_string += ", dependencies=TRUE";
 	command_string += ")\n";
 
+	QString repos_string = "options (repos= c(";
+	for (int i = 0; i < repos.count (); ++i) {
+		if (i) repos_string.append (", ");
+		repos_string.append (RObject::rQuote (repos[i]));
+	}
+	repos_string.append ("))\n");
+
 // TODO: won't work with some versions of GCC (which ones exactly)?
 //	QFile file (QDir (RKSettingsModuleGeneral::filesPath ()).filePath ("install_script.R"));
 // WORKAROUND:
@@ -193,7 +200,7 @@
 // WORKADOUND END
 	if (file.open (QIODevice::WriteOnly)) {
 		QTextStream stream (&file);
-		stream << "options (repos=" + repos_string + ")\n" + command_string;
+		stream << repos_string + command_string;
 		if (as_root) {
 #ifdef Q_WS_WIN
 			RK_ASSERT (false);
@@ -510,170 +517,9 @@
 	deleteLater ();
 }
 
-/////////////////////// UpdatePackagesWidget //////////////////////////
 
-#define FIND_OLD_PACKAGES_COMMAND 1
-
-UpdatePackagesWidget::UpdatePackagesWidget (RKLoadLibsDialog *dialog) : QWidget (0) {
-	RK_TRACE (DIALOGS);
-	UpdatePackagesWidget::parent = dialog;
-	
-	QVBoxLayout *mvbox = new QVBoxLayout (this);
-	mvbox->setContentsMargins (0, 0, 0, 0);
-	QLabel *label = new QLabel (i18n ("In order to find out, which of your installed packaged have an update available, click \"Fetch List\". This feature requires a working internet connection."), this);
-	label->setWordWrap (true);
-	mvbox->addWidget (label);
-	
-	QHBoxLayout *hbox = new QHBoxLayout ();
-	mvbox->addLayout (hbox);
-	hbox->setContentsMargins (0, 0, 0, 0);
-	
-	updateable_view = new QTreeWidget (this);
-	updateable_view->setHeaderLabels (QStringList () << i18n ("Name") << i18n ("Location") << i18n ("Local Version") << i18n ("Online Version"));
-	updateable_view->setSelectionMode (QAbstractItemView::ExtendedSelection);
-	hbox->addWidget (updateable_view);
-	setFocusProxy (updateable_view);
-	
-	QVBoxLayout *buttonvbox = new QVBoxLayout ();
-	hbox->addLayout (buttonvbox);
-	buttonvbox->setContentsMargins (0, 0, 0, 0);
-	get_list_button = new QPushButton (i18n ("Fetch list"), this);
-	connect (get_list_button, SIGNAL (clicked ()), this, SLOT (getListButtonClicked ()));
-	update_selected_button = new QPushButton (i18n ("Update Selected"), this);
-	connect (update_selected_button, SIGNAL (clicked ()), this, SLOT (updateSelectedButtonClicked ()));
-	update_all_button = new QPushButton (i18n ("Update All"), this);
-	connect (update_all_button, SIGNAL (clicked ()), this, SLOT (updateAllButtonClicked ()));
-	install_params = new PackageInstallParamsWidget (this, false);
-	connect (parent, SIGNAL (libraryLocationsChanged (const QStringList &)), install_params, SLOT (liblocsChanged (const QStringList &)));
-
-	buttonvbox->addWidget (get_list_button);
-	buttonvbox->addStretch (1);
-	buttonvbox->addWidget (update_selected_button);
-	buttonvbox->addWidget (update_all_button);
-	buttonvbox->addStretch (1);
-	buttonvbox->addWidget (install_params);
-	buttonvbox->addStretch (1);
-	
-	update_selected_button->setEnabled (false);
-	update_all_button->setEnabled (false);
-	updateable_view->setEnabled (false);
-
-	new QTreeWidgetItem (updateable_view, QStringList ("..."));	// i18n ("[Click \"Fetch list\" for updates]")
-
-	connect (dialog, SIGNAL (okClicked ()), this, SLOT (ok ()));
-	connect (dialog, SIGNAL (cancelClicked ()), this, SLOT (cancel ()));
-	connect (this, SIGNAL (destroyed ()), dialog, SLOT (childDeleted ()));
-}
-
-UpdatePackagesWidget::~UpdatePackagesWidget () {
-	RK_TRACE (DIALOGS);
-}
-
-void UpdatePackagesWidget::rCommandDone (RCommand *command) {
-	RK_TRACE (DIALOGS);
-	if (command->getFlags () == FIND_OLD_PACKAGES_COMMAND) {
-		if (!command->failed ()) {
-			updateable_view->clear ();
-
-			RK_ASSERT (command->getDataLength () == 5);
-			RData *package = command->getStructureVector ()[0];
-			RData *libpath = command->getStructureVector ()[1];
-			RData *installed = command->getStructureVector ()[2];
-			RData *reposver = command->getStructureVector ()[3];
-			RData *reposstring = command->getStructureVector ()[4];
-
-			unsigned int count = package->getDataLength ();
-			RK_ASSERT (count == libpath->getDataLength ());
-			RK_ASSERT (count == installed->getDataLength ());
-			RK_ASSERT (count == reposver->getDataLength ());
-			for (unsigned int i=0; i < count; ++i) {
-				QTreeWidgetItem* item = new QTreeWidgetItem (updateable_view);
-				item->setText (0, package->getStringVector ()[i]);
-				item->setText (1, libpath->getStringVector ()[i]);
-				item->setText (2, installed->getStringVector ()[i]);
-				item->setText (3, reposver->getStringVector ()[i]);
-			}
-
-			if (updateable_view->topLevelItemCount ()) {
-				update_selected_button->setEnabled (true);
-				update_all_button->setEnabled (true);
-				updateable_view->setEnabled (true);
-				updateable_view->setFocus ();
-				updateable_view->setSortingEnabled (true);
-				updateable_view->sortItems (0, Qt::AscendingOrder);
-			} else {
-				new QTreeWidgetItem (updateable_view, QStringList (i18n ("[No updates available]")));
-			}
-			updateable_view->resizeColumnToContents (0);
-
-			RK_ASSERT (reposstring->getDataLength () == 1);
-			// this is after the repository was chosen. Update the repository string.
-			parent->repos_string = reposstring->getStringVector ()[0];
-		} else {
-			get_list_button->setEnabled (true);
-		}
-	} else {
-		RK_ASSERT (false);
-	}
-}
-
-void UpdatePackagesWidget::updatePackages (const QStringList &list) {
-	RK_TRACE (DIALOGS);
-	bool as_root = false;
-
-	if (list.isEmpty ()) return;
-	if (!install_params->checkWritable (&as_root)) return;
-
-	parent->installPackages (list, install_params->libraryLocation (), install_params->installDependencies (), as_root);
-}
-
-void UpdatePackagesWidget::updateSelectedButtonClicked () {
-	RK_TRACE (DIALOGS);
-	QStringList list;
-	QList<QTreeWidgetItem*> selected = updateable_view->selectedItems ();
-	for (int i = 0; i < selected.count (); ++i) {
-		list.append (selected[i]->text (0));
-	}
-	updatePackages (list);
-}
-
-void UpdatePackagesWidget::updateAllButtonClicked () {
-	RK_TRACE (DIALOGS);
-	QStringList list;
-	for (int i = 0; i < updateable_view->topLevelItemCount (); ++i) {
-		list.append (updateable_view->topLevelItem (i)->text (0));
-	}
-	updatePackages (list);
-}
-
-void UpdatePackagesWidget::getListButtonClicked () {
-	RK_TRACE (DIALOGS);
-
-	get_list_button->setEnabled (false);
-
-	RCommand *command = new RCommand (".rk.get.old.packages ()", RCommand::App | RCommand::GetStructuredData, QString::null, this, FIND_OLD_PACKAGES_COMMAND);
-
-	RKProgressControl *control = new RKProgressControl (this, i18n ("Please stand by while determining, which packages have an update available online."), i18n ("Fetching list"), RKProgressControl::CancellableProgress | RKProgressControl::AutoCancelCommands);
-	control->addRCommand (command, true);
-	RKGlobals::rInterface ()->issueCommand (command, parent->chain);
-	control->doModal (true);
-}
-
-void UpdatePackagesWidget::ok () {
-	RK_TRACE (DIALOGS);
-	deleteLater ();
-}
-
-void UpdatePackagesWidget::cancel () {
-	RK_TRACE (DIALOGS);
-	deleteLater ();
-}
-
-
 /////////////////////// InstallPackagesWidget //////////////////////////
 
-#define FIND_AVAILABLE_PACKAGES_COMMAND 1
-
 InstallPackagesWidget::InstallPackagesWidget (RKLoadLibsDialog *dialog) : QWidget (0) {
 	RK_TRACE (DIALOGS);
 	InstallPackagesWidget::parent = dialog;
@@ -686,36 +532,33 @@
 	QHBoxLayout *hbox = new QHBoxLayout ();
 	mvbox->addLayout (hbox);
 	hbox->setContentsMargins (0, 0, 0, 0);
-	
-	installable_view = new QTreeWidget (this);
-	installable_view->setHeaderLabels (QStringList () << i18n ("Name") << i18n ("Version"));
-	installable_view->setSelectionMode (QAbstractItemView::ExtendedSelection);
-	hbox->addWidget (installable_view);
-	setFocusProxy (installable_view);
 
+	packages_status = new RKRPackageInstallationStatus (this);
+	packages_view = new QTreeView (this);
+	packages_view->setSortingEnabled (true);
+	model = new QSortFilterProxyModel (this);
+	model->setSourceModel (packages_status);
+	packages_view->setModel (model);
+	hbox->addWidget (packages_view);
+	setFocusProxy (packages_view);
+
 	QVBoxLayout *buttonvbox = new QVBoxLayout ();
 	hbox->addLayout (buttonvbox);
 	buttonvbox->setContentsMargins (0, 0, 0, 0);
 	get_list_button = new QPushButton (i18n ("Fetch list"), this);
 	connect (get_list_button, SIGNAL (clicked ()), this, SLOT (getListButtonClicked ()));
-	install_selected_button = new QPushButton (i18n ("Install Selected"), this);
-	connect (install_selected_button, SIGNAL (clicked ()), this, SLOT (installSelectedButtonClicked ()));
 	install_params = new PackageInstallParamsWidget (this, true);
 	connect (parent, SIGNAL (libraryLocationsChanged (const QStringList &)), install_params, SLOT (liblocsChanged (const QStringList &)));
 
 	buttonvbox->addWidget (get_list_button);
 	buttonvbox->addStretch (1);
-	buttonvbox->addWidget (install_selected_button);
-	buttonvbox->addStretch (1);
 	buttonvbox->addWidget (install_params);
 	buttonvbox->addStretch (1);
 	
-	install_selected_button->setEnabled (false);
-	installable_view->setEnabled (false);
+	packages_view->setEnabled (false);
 
-	new QTreeWidgetItem (installable_view, QStringList ("..."));	// i18n ("[Click \"Fetch list\" to see available packages]")
-
 	connect (dialog, SIGNAL (okClicked ()), this, SLOT (ok ()));
+	connect (dialog, SIGNAL (applyClicked ()), this, SLOT (apply ()));
 	connect (dialog, SIGNAL (cancelClicked ()), this, SLOT (cancel ()));
 	connect (this, SIGNAL (destroyed ()), dialog, SLOT (childDeleted ()));
 }
@@ -724,49 +567,7 @@
 	RK_TRACE (DIALOGS);
 }
 
-void InstallPackagesWidget::rCommandDone (RCommand *command) {
-	RK_TRACE (DIALOGS);
-	if (command->getFlags () == FIND_AVAILABLE_PACKAGES_COMMAND) {
-		if (!command->failed ()) {
-			installable_view->clear ();
 
-			RK_ASSERT (command->getDataLength () == 3);
-
-			RData *names = command->getStructureVector ()[0];
-			RData *versions = command->getStructureVector ()[1];
-			RData *repos = command->getStructureVector ()[2];
-
-			unsigned int count = names->getDataLength ();
-			RK_ASSERT (count == versions->getDataLength ());
-			RK_ASSERT (repos->getDataLength () == 1);
-
-			for (unsigned int i=0; i < count; ++i) {
-				QTreeWidgetItem* item = new QTreeWidgetItem (installable_view);
-				item->setText (0, names->getStringVector ()[i]);
-				item->setText (1, versions->getStringVector ()[i]);
-			}
-
-			if (installable_view->topLevelItemCount ()) {
-				install_selected_button->setEnabled (true);
-				installable_view->setEnabled (true);
-				installable_view->setFocus ();
-				installable_view->setSortingEnabled (true);
-				installable_view->sortItems (0, Qt::AscendingOrder);
-			} else {
-				new QTreeWidgetItem (installable_view, QStringList (i18n ("[No packages available]")));
-			}
-			installable_view->resizeColumnToContents (0);
-
-			// this is after the repository was chosen. Update the repository string.
-			parent->repos_string = repos->getStringVector ()[0];
-		} else {
-			get_list_button->setEnabled (true);
-		}
-	} else {
-		RK_ASSERT (false);
-	}
-}
-
 void InstallPackagesWidget::installPackages (const QStringList &list) {
 	RK_TRACE (DIALOGS);
 	bool as_root = false;
@@ -774,47 +575,40 @@
 	if (list.isEmpty ()) return;
 	if (!install_params->checkWritable (&as_root)) return;
 
-	parent->installPackages (list, install_params->libraryLocation (), install_params->installDependencies (), as_root);
+	parent->installPackages (list, install_params->libraryLocation (), install_params->installDependencies (), as_root, packages_status->currentRepositories ());
 }
 
-void InstallPackagesWidget::installSelectedButtonClicked () {
-	RK_TRACE (DIALOGS);
-	QStringList list;
-	QList<QTreeWidgetItem*> selected = installable_view->selectedItems ();
-	for (int i = 0; i < selected.count (); ++i) {
-		list.append (selected[i]->text (0));
-	}
-	installPackages (list);
-}
-
 void InstallPackagesWidget::getListButtonClicked () {
 	RK_TRACE (DIALOGS);
 
+	packages_status->initialize (parent->chain);
 	get_list_button->setEnabled (false);
-
-	RCommand *command = new RCommand (".rk.get.available.packages ()", RCommand::App | RCommand::GetStructuredData, QString::null, this, FIND_AVAILABLE_PACKAGES_COMMAND);
-	RKProgressControl *control = new RKProgressControl (this, i18n ("Please stand by while downloading the list of available packages."), i18n ("Fetching list"), RKProgressControl::CancellableProgress | RKProgressControl::AutoCancelCommands);
-	control->addRCommand (command, true);
-	RKGlobals::rInterface ()->issueCommand (command, parent->chain);
-	control->doModal (true);
+	packages_view->setEnabled (true);
+	for (int i = 0; i <= RKRPackageInstallationStatus::PackageName; ++i) packages_view->resizeColumnToContents (i);
 }
 
 void InstallPackagesWidget::trySelectPackage (const QString &package_name) {
 	RK_TRACE (DIALOGS);
 
-	QList<QTreeWidgetItem*> found = installable_view->findItems (package_name, Qt::MatchExactly, 0);
-	if (found.isEmpty ()) {
+	QModelIndex index = packages_status->markPackageForInstallation (package_name);
+	if (!index.isValid ()) {
 		KMessageBox::sorry (0, i18n ("The package requested by the backend (\"%1\") was not found in the package repositories. Maybe the package name was mis-spelled. Or maybe you need to add additional repositories via the \"Configure Repositories\"-button.", package_name), i18n ("Package not available"));
 	} else {
-		RK_ASSERT (found.count () == 1);
-		installable_view->setCurrentItem (found[0]);
-		found[0]->setSelected (true);
-		installable_view->scrollToItem (found[0]);
+		packages_view->scrollTo (model->mapFromSource (index));
 	}
 }
 
+void InstallPackagesWidget::apply () {
+	RK_TRACE (DIALOGS);
+
+	installPackages (packages_status->packagesToInstall ());
+	packages_status->clearStatus ();
+}
+
 void InstallPackagesWidget::ok () {
 	RK_TRACE (DIALOGS);
+
+	apply ();
 	deleteLater ();
 }
 
@@ -901,4 +695,299 @@
 	libloc_selector->insertItems (0, newlist);
 }
 
+/////////// RKRPackageInstallationStatus /////////////////
+
+RKRPackageInstallationStatus::RKRPackageInstallationStatus (QObject* parent) : QAbstractItemModel (parent) {
+	RK_TRACE (DIALOGS);
+}
+
+RKRPackageInstallationStatus::~RKRPackageInstallationStatus () {
+	RK_TRACE (DIALOGS);
+}
+
+QModelIndex RKRPackageInstallationStatus::markPackageForInstallation (const QString& package_name) {
+	RK_TRACE (DIALOGS);
+
+	// is the package available at all?
+	QModelIndex pindex;
+	int row = available_packages.indexOf (package_name);
+	if (row < 0) return pindex;
+
+	// find out, whether it is a new or and updateable package
+	QModelIndex parent;
+	int urow = updateable_packages_in_available.indexOf (row);
+	if (urow >= 0) {
+		parent = index (UpdateablePackages, 0);
+		row = urow;
+	} else {
+		row = new_packages_in_available.indexOf (row);
+		parent = index (NewPackages, 0);
+	}
+	if (row < 0) {
+		RK_ASSERT (false);
+		return pindex;
+	}
+
+	// mark for installation
+	pindex = index (row, InstallationStatus, parent);
+	setData (pindex, QVariant (Qt::Checked), Qt::CheckStateRole);
+	return pindex;
+}
+
+void RKRPackageInstallationStatus::initialize (RCommandChain *chain) {
+	RK_TRACE (DIALOGS);
+
+	RCommand *command = new RCommand (".rk.get.package.intallation.state ()", RCommand::App | RCommand::GetStructuredData);
+	connect (command->notifier (), SIGNAL (commandFinished(RCommand*)), this, SLOT (statusCommandFinished(RCommand*)));
+	RKProgressControl *control = new RKProgressControl (this, i18n ("Please stand by while downloading the list of available packages."), i18n ("Fetching list"), RKProgressControl::CancellableProgress | RKProgressControl::AutoCancelCommands);
+	control->addRCommand (command, true);
+	RKGlobals::rInterface ()->issueCommand (command, chain);
+	control->doModal (true);
+}
+
+void RKRPackageInstallationStatus::statusCommandFinished (RCommand *command) {
+	RK_TRACE (DIALOGS);
+
+	if (!command->succeeded ()) {
+		RK_ASSERT (false);
+		return;
+	}
+	RK_ASSERT (command->getDataType () == RCommand::StructureVector);
+	RK_ASSERT (command->getDataLength () == 4);
+
+	RData::RDataStorage top = command->getStructureVector ();
+	RData::RDataStorage available = top[0]->getStructureVector ();
+	available_packages = available[0]->getStringVector ();
+	available_titles = available[1]->getStringVector ();
+	available_versions = available[2]->getStringVector ();
+	available_repos = available[3]->getStringVector ();
+	enhance_rk_in_available = available[4]->getIntVector ();
+
+	RData::RDataStorage installed = top[1]->getStructureVector ();
+	installed_packages = installed[0]->getStringVector ();
+	installed_titles = installed[1]->getStringVector ();
+	installed_versions = installed[2]->getStringVector ();
+	installed_libpaths = installed[3]->getStringVector ();
+	enhance_rk_in_installed = installed[4]->getIntVector ();
+	installed_has_update.fill (false, installed_packages.count ());
+
+	new_packages_in_available = top[2]->getIntVector ();
+	RData::RDataStorage updateable = top[3]->getStructureVector ();
+	updateable_packages_in_installed = updateable[0]->getIntVector ();
+	updateable_packages_in_available = updateable[1]->getIntVector ();
+
+	for (int i = updateable_packages_in_installed.count () - 1; i >= 0; --i) {
+		installed_has_update[updateable_packages_in_installed[i]] = true;
+	}
+
+	current_repos = top[4]->getStringVector ();
+
+	clearStatus ();
+}
+
+void RKRPackageInstallationStatus::clearStatus () {
+	RK_TRACE (DIALOGS);
+
+	available_status.fill (NoAction, available_packages.count ());
+	installed_status.fill (NoAction, installed_packages.count ());
+	reset ();
+}
+
+QVariant RKRPackageInstallationStatus::headerData (int section, Qt::Orientation orientation, int role) const {
+	if (orientation != Qt::Horizontal) return QVariant ();
+
+	if ((role == Qt::DecorationRole) && (section == EnhancesRKWard)) return QApplication::windowIcon ();
+
+	if (role == Qt::DisplayRole) {
+		if (section == InstallationStatus) return QVariant (i18n ("Status"));
+		if (section == PackageName) return QVariant (i18n ("Name"));
+		if (section == PackageTitle) return QVariant (i18n ("Title"));
+		if (section == Version) return QVariant (i18n ("Version"));
+		if (section == Location) return QVariant (i18n ("Location"));
+	}
+	return QVariant ();
+}
+
+int RKRPackageInstallationStatus::rowCount (const QModelIndex &parent) const {
+	if (!parent.isValid ()) return TOPLEVELITEM_COUNT;	// top level
+	if (parent.parent ().isValid ()) return 0;			// model has exactly two levels
+
+	int row = parent.row ();
+	if (row == UpdateablePackages) return updateable_packages_in_available.count ();
+	if (row == NewPackages) return available_packages.count ();
+	if (row == InstalledPackages) return installed_packages.count ();
+
+	RK_ASSERT (false);
+	return 0;
+}
+
+QVariant RKRPackageInstallationStatus::data (const QModelIndex &index, int role) const {
+	if (!index.isValid ()) return QVariant ();
+	if (!index.parent ().isValid ()) {	// top level item
+		int row = index.row ();
+		if (row == UpdateablePackages) {
+			if ((role == Qt::DisplayRole) && (index.column () == PackageName)) return QVariant (i18n ("Updateable Packages"));
+			if (role == Qt::ToolTipRole) return QVariant (i18n ("Packages for which an update is available. This may include packages which were merely built against a newer version of R."));
+		} else if (row == NewPackages) {
+			if ((role == Qt::DisplayRole) && (index.column () == PackageName)) return QVariant (i18n ("New Packages"));
+			if (role == Qt::ToolTipRole) return QVariant (i18n ("Packages for which available for installation, but which are not currently installed."));
+		} else if (row == InstalledPackages) {
+			if ((role == Qt::DisplayRole) && (index.column () == PackageName)) return QVariant (i18n ("Installed Packages"));
+			if (role == Qt::ToolTipRole) return QVariant (i18n ("Packages for which installed locally. Note that updates may be available for these packages."));
+		}
+		if (role == Qt::BackgroundColorRole) return QVariant (QColor (200, 200, 200));
+	} else if (!index.parent ().parent ().isValid ()) {		// model has exactly two levels
+		int col = index.column ();
+		int prow = index.parent ().row ();
+		int arow, irow;		// row numbers in the lists of available_packages / installed_packages
+		if (prow == UpdateablePackages) {
+			arow = updateable_packages_in_available.value (index.row ());
+			irow = updateable_packages_in_installed.value (index.row ());
+		} else if (prow == NewPackages) arow = new_packages_in_available.value (index.row ());
+		else irow = index.row ();
+
+		if (col == InstallationStatus) {
+			PackageStatusChange stat;
+			if (prow == InstalledPackages) stat = installed_status.value (irow, NoAction);
+			else stat = available_status.value (arow, NoAction);
+			if (stat == NoAction) {
+				if (role == Qt::CheckStateRole) {
+					if (prow == InstalledPackages) return Qt::PartiallyChecked;
+					return Qt::Unchecked;
+				}
+			} else if (stat == Install) {
+				if (role == Qt::CheckStateRole) return Qt::Checked;
+				if (role == Qt::DisplayRole) return QVariant (i18n ("Install"));
+			} else {
+				if (role == Qt::CheckStateRole) return Qt::Unchecked;
+				if (role == Qt::DisplayRole) return QVariant (i18n ("Remove"));
+			}
+		} else if (col == EnhancesRKWard) {
+			if (role == Qt::DisplayRole) return QVariant (QString (" "));	// must have a placeholder, here, or Qt will collapse the column
+			if (role == Qt::DecorationRole) {
+				bool enhance_rk;
+				if (prow == InstalledPackages) enhance_rk = enhance_rk_in_installed.value (irow);
+				else enhance_rk = enhance_rk_in_available.value (arow);
+				if (enhance_rk) return QApplication::windowIcon ();
+			}
+		} else if (col == PackageName) {
+			if (role == Qt::DisplayRole) {
+				if (prow == InstalledPackages) return installed_packages.value (irow);
+				else return available_packages.value (arow);
+			}
+		} else if (col == PackageTitle) {
+			if (role == Qt::DisplayRole) {
+				if (prow == InstalledPackages) return installed_titles.value (irow);
+				else return available_titles.value (arow);
+			}
+		} else if (col == Version) {
+			if (role == Qt::DisplayRole) {
+				if (prow == InstalledPackages) return installed_versions.value (irow);
+				else if (prow == NewPackages) return available_versions.value (arow);
+				else return QVariant (installed_versions.value (irow) + " -> " + available_versions.value (arow));
+			}
+		} else if (col == Location) {
+			if (role == Qt::DisplayRole) {
+				if (prow == InstalledPackages) return installed_libpaths.value (irow);
+				else if (prow == NewPackages) return available_repos.value (arow);
+				else return QVariant (installed_libpaths.value (irow) + " -> " + available_repos.value (arow));
+			}
+		}
+	}
+	return QVariant ();
+}
+
+Qt::ItemFlags RKRPackageInstallationStatus::flags (const QModelIndex &index) const {
+	qint64 pos = index.internalId ();
+	Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+	if (pos >= 0) flags |= Qt::ItemIsUserCheckable;
+	if (pos == InstalledPackages) flags |= Qt::ItemIsTristate;
+	return flags;
+}
+
+QModelIndex RKRPackageInstallationStatus::index (int row, int column, const QModelIndex &parent) const {
+	if (!parent.isValid ()) return createIndex (row, column, -1);	// toplevel items
+	return createIndex (row, column, parent.row ());				// parent.row () identifies, which toplevel item is the parent.
+}
+
+QModelIndex RKRPackageInstallationStatus::parent (const QModelIndex& index) const {
+	if (index.internalId () == -1) return QModelIndex ();
+	return (RKRPackageInstallationStatus::index (index.internalId (), 0, QModelIndex ()));
+}
+
+bool RKRPackageInstallationStatus::setData (const QModelIndex &index, const QVariant &value, int role) {
+	RK_TRACE (DIALOGS);
+
+	if (role != Qt::CheckStateRole) return false;
+	if (!index.isValid ()) return false;
+	if (!index.parent ().isValid ()) return false;
+	QModelIndex bindex;
+
+	PackageStatusChange stat = NoAction;
+	int irow = -1;
+	int arow = -1;
+	if (value.toInt () == Qt::Checked) stat = Install;
+	else if (value.toInt () == Qt::Unchecked) stat = Remove;
+
+	if (index.internalId () == InstalledPackages) {
+		irow = index.row ();
+		if ((stat == Install) && (installed_status[irow] == Remove)) stat = NoAction;
+		if (installed_has_update.value (irow, false)) {
+			// NOTE: installed, and updatable packages are coupled
+			int urow = updateable_packages_in_installed.indexOf (irow);
+			RK_ASSERT (urow >= 0);
+			arow = updateable_packages_in_available.value (urow);
+			bindex = RKRPackageInstallationStatus::index (urow, InstallationStatus, RKRPackageInstallationStatus::index (UpdateablePackages, 0));
+		}
+	} else {
+		if (stat == Remove) stat = NoAction;
+		if (index.internalId () == UpdateablePackages) {
+			// NOTE: installed, and updatable packages are coupled
+			irow = updateable_packages_in_installed.value (index.row ());
+			arow = updateable_packages_in_available.value (index.row ());
+			bindex = RKRPackageInstallationStatus::index (irow, InstallationStatus, RKRPackageInstallationStatus::index (InstalledPackages, 0));
+		} else {
+			arow = new_packages_in_available.value (index.row ());
+		}
+	}
+
+	if (irow >= 0) installed_status[irow] = stat;
+	if (arow >= 0) available_status[arow] = stat;
+
+	dataChanged (index, index);
+	if (bindex.isValid ()) dataChanged (bindex, bindex);
+
+	return true;
+}
+
+QStringList RKRPackageInstallationStatus::packagesToInstall () const {
+	RK_TRACE (DIALOGS);
+
+	QStringList ret;
+	for (int i = installed_status.count () - 1; i >= 0; --i) {
+		if (installed_status[i] == Install) ret.append (installed_packages[i]);
+	}
+	for (int i = available_status.count () - 1; i >= 0; --i) {
+		if (available_status[i] == Install) {
+			QString package = available_packages[i];
+			if (!ret.contains (package)) ret.append (package);
+		}
+	}
+	return ret;
+}
+
+bool RKRPackageInstallationStatus::packagesToRemove (QStringList *packages, QStringList *liblocs) {
+	RK_TRACE (DIALOGS);
+
+	bool anyfound = false;
+	for (int i = installed_status.count () - 1; i >= 0; --i) {
+		if (installed_status[i] == Remove) {
+			packages->append (installed_packages[i]);
+			liblocs->append (installed_libpaths[i]);
+			anyfound = true;
+		}
+	}
+	return anyfound;
+}
+
 #include "rkloadlibsdialog.moc"

Modified: trunk/rkward/rkward/dialogs/rkloadlibsdialog.h
===================================================================
--- trunk/rkward/rkward/dialogs/rkloadlibsdialog.h	2011-09-23 14:00:03 UTC (rev 3821)
+++ trunk/rkward/rkward/dialogs/rkloadlibsdialog.h	2011-09-23 17:41:55 UTC (rev 3822)
@@ -25,8 +25,9 @@
 #include "../settings/rksettingsmoduler.h"
 #include "../rbackend/rcommandreceiver.h"
 
+class QTreeView;
 class QTreeWidget;
-class QTreeWidgetItem;
+class QSortFilterProxyModel;
 class QComboBox;
 class QPushButton;
 class RKProgressControl;
@@ -54,7 +55,7 @@
 
 	~RKLoadLibsDialog ();
 
-	bool installPackages (const QStringList &packages, const QString &to_libloc, bool install_dependencies, bool as_root);
+	bool installPackages (const QStringList &packages, const QString &to_libloc, bool install_dependencies, bool as_root, const QStringList& repos);
 
 /** opens a modal RKLoadLibsDialog with the "Install new Packages" tab on front (To be used when a require () fails in the R backend
 @param parent parent QWidget. The dialog will be centered there
@@ -83,7 +84,6 @@
 private:
 	void tryDestruct ();
 friend class LoadUnloadWidget;
-friend class UpdatePackagesWidget;
 friend class InstallPackagesWidget;
 	RCommandChain *chain;
 
@@ -93,7 +93,6 @@
 	QString auto_install_package;
 	int num_child_widgets;
 	bool accepted;
-	QString repos_string;
 
 	QProcess* installation_process;
 };
@@ -137,45 +136,81 @@
 	RKLoadLibsDialog *parent;
 };
 
-/**
-Shows which packages are can be updated from CRAN.
-Ro be used in RKLoadLibsDialog.
+/** Item model and encapsulation for package status (used in InstallPackagesWidget) */
+class RKRPackageInstallationStatus : public QAbstractItemModel {
+	Q_OBJECT
+public:
+	RKRPackageInstallationStatus (QObject* parent);
+	~RKRPackageInstallationStatus ();
 
- at author Thomas Friedrichsmeier
-*/
-class UpdatePackagesWidget : public QWidget, public RCommandReceiver {
-Q_OBJECT
-public:
-	UpdatePackagesWidget (RKLoadLibsDialog *dialog);
-	
-	~UpdatePackagesWidget ();
-public slots:
-	void updateSelectedButtonClicked ();
-	void updateAllButtonClicked ();
-	void getListButtonClicked ();
-	void ok ();
-	void cancel ();
-protected:
-	void rCommandDone (RCommand *command);
+	void initialize (RCommandChain *chain);
+
+	enum Columns {
+		EnhancesRKWard,
+		InstallationStatus,
+		PackageName,
+		PackageTitle,
+		Version,
+		Location,
+		COLUMN_COUNT
+	};
+	enum ToplevelItems {
+		UpdateablePackages,
+		NewPackages,
+		InstalledPackages,
+		TOPLEVELITEM_COUNT
+	};
+	enum PackageStatusChange {
+		Install,
+		Remove,
+		NoAction
+	};
+
+/* Item model implementation */
+	int rowCount (const QModelIndex &parent = QModelIndex()) const;
+	int columnCount (const QModelIndex &) const { return COLUMN_COUNT; };
+	QVariant data (const QModelIndex &index, int role=Qt::DisplayRole) const;
+	QVariant headerData (int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;
+	Qt::ItemFlags flags (const QModelIndex &index) const;
+	QModelIndex index (int row, int column, const QModelIndex &parent=QModelIndex()) const;
+	QModelIndex parent (const QModelIndex &index) const;
+	bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+/** returns a list of packages selected for installation / update */
+	QStringList packagesToInstall () const;
+/** fills a list of packages selected for removal, and a parallel list of the locations, from which to remove.
+ * @return true, if any packages are marked for removal, false otherwise. */
+	bool packagesToRemove (QStringList *packages, QStringList *liblocs);
+/** mark a package for installation.
+ * @returns the index of the package, if the package is available, an invalid index, if it is not available */
+	QModelIndex markPackageForInstallation (const QString& package_name);
+/** reset all installation states to NoAction */
+	void clearStatus ();
+	QStringList currentRepositories () const { return current_repos; };
+private slots:
+	void statusCommandFinished (RCommand *command);
 private:
-	void updatePackages (const QStringList &list);
-	QTreeWidget *updateable_view;
+	QStringList available_packages, available_titles, available_versions, available_repos;
+	QStringList installed_packages, installed_titles, installed_versions, installed_libpaths;
+	RData::IntStorage enhance_rk_in_available;
+	RData::IntStorage enhance_rk_in_installed;
+	RData::IntStorage new_packages_in_available;
+	RData::IntStorage updateable_packages_in_available;
+	RData::IntStorage updateable_packages_in_installed;
+	QVector<PackageStatusChange> installed_status;
+	QVector<PackageStatusChange> available_status;
+	QVector<bool> installed_has_update;
 
-	QPushButton *update_selected_button;
-	QPushButton *update_all_button;
-	QPushButton *get_list_button;
-	PackageInstallParamsWidget *install_params;
-	
-	RKLoadLibsDialog *parent;
+	QStringList current_repos;
 };
 
 /**
-Allows the user to install further R packages. For now only packages from CRAN.
-Ro be used in RKLoadLibsDialog.
+Allows the user to update / install R packages.
+To be used in RKLoadLibsDialog.
 
 @author Thomas Friedrichsmeier
 */
-class InstallPackagesWidget : public QWidget, public RCommandReceiver {
+class InstallPackagesWidget : public QWidget {
 Q_OBJECT
 public:
 	InstallPackagesWidget (RKLoadLibsDialog *dialog);
@@ -183,17 +218,16 @@
 	~InstallPackagesWidget ();
 	void trySelectPackage (const QString &package_name);
 public slots:
-	void installSelectedButtonClicked ();
 	void getListButtonClicked ();
 	void ok ();
+	void apply ();
 	void cancel ();
-protected:
-	void rCommandDone (RCommand *command);
 private:
 	void installPackages (const QStringList &list);
-	QTreeWidget *installable_view;
+	QTreeView *packages_view;
+	RKRPackageInstallationStatus *packages_status;
+	QSortFilterProxyModel *model;
 
-	QPushButton *install_selected_button;
 	QPushButton *get_list_button;
 	PackageInstallParamsWidget *install_params;
 	

Modified: trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R
===================================================================
--- trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R	2011-09-23 14:00:03 UTC (rev 3821)
+++ trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R	2011-09-23 17:41:55 UTC (rev 3822)
@@ -90,6 +90,29 @@
 	return(pluginmaps)
 }
 
+# Gather status information on installed and available packages.
+# Return value is used in class RKRPackageInstallationStatus of the frontend
+".rk.get.package.intallation.state" <- function () {
+	# fetch all status information
+	available <- .rk.cached.available.packages ()
+	inst <- installed.packages (fields="Title")
+	old <- as.data.frame (old.packages (available=available), stringsAsFactors=FALSE)
+	new <- new.packages (instPkgs=inst, available=available)
+
+	# convert info to a more suitable format
+	available <- as.data.frame (available, stringsAsFactors=FALSE)
+	inst <- as.data.frame (inst, stringsAsFactors=FALSE)
+	oldinst <- match (paste (old$Package, old$LibPath), paste (inst$Package, inst$LibPath))	# convert package names to position with in the installed packages info
+	oldavail <- match (old$Package, available$Package)	# convert package names to position with in the available packages info
+	new <- match (new, available$Package)	# same for new packages
+
+	list ("available" = list (available$Package, available$Title, available$Version, available$Repository, grepl ("rkward", available$Enhances)),
+		"installed" = list (inst$Package, inst$Title, inst$Version, inst$LibPath, grepl ("rkward", inst$Enhances)),
+		"new" = as.integer (new - 1),
+		"old" = list (as.integer (oldinst - 1), as.integer (oldavail - 1)),
+		"repos" = as.character (options("repos")$repos))
+}
+
 # package information formats may - according to the help - be subject to change. Hence this function to cope with "missing" values
 # also it concatenates everything to a single vector, so we can easily get the whole structure with a single call
 ".rk.get.installed.packages" <- function () {
@@ -123,16 +146,6 @@
 	return (x)
 }
 
-".rk.get.old.packages" <- function () {
-	x <- old.packages (available=.rk.cached.available.packages ())
-	return (list (as.character (x[,"Package"]), as.character (x[,"LibPath"]), as.character (x[,"Installed"]), as.character (x[,"ReposVer"]), rk.make.repos.string ()))
-}
-
-".rk.get.available.packages" <- function () {
-	x <- .rk.cached.available.packages ()
-	return (list (as.character (x[,1]), as.character (x[,2]), rk.make.repos.string ()))
-}
-
 "require" <- function (package, quietly = FALSE, character.only = FALSE, ...)
 {
 	if (!character.only) {

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.





More information about the rkward-tracker mailing list